1、首先明白什麼叫依賴注入、控制反轉,及其作用
控制反轉(Inversion of Control ,Ioc)
所謂控制反轉就是應用本身不負責依賴對象的創建及維護,依賴對象的創建及維護由外部容器來負責。這樣控制權就由應用轉移到了外部容器,控制權的轉移就是所謂反轉。
依賴注入(Dependency Injection)
所謂依賴注入就是指:在運行期間,有外部容器動態地將依賴對象注入到組件中(構造方法和set方法)
好處:
1.降低組件之間的耦合度,實現軟件各層之間的解耦.
2.可以使容器提供衆多服務如事務管理消息服務處理等等。當我們使用容器管理事務時,開發人員就不需要手工 控制事務,也不需要處理複雜的事務傳播
3.容器提供單例模式支持,開發人員不需要自己編寫實現代碼.
4.容器提供了AOP技術,利用它很容易實現如權限攔截,運行期監控等功能
5.容器提供衆多的輔佐類,使這些類可以加快應用的開發.如jdbcTemplate HibernateTemplate
個人理解依賴注入完全實現了面向接口編程,只需定義方法,無需實現,首先面向接口編程的好處就是統一編碼風格。由容器創建對象,可以說是低內聚,高耦合。增加了代碼的可維護性。
2、spring對於編程人員最重要的是寫xml配置文件和使用註解那麼我這裏給幾個簡單的案例
依賴注入配置xml
- <bean id="person" class="com.itheima12.spring.di.xml.setter.Person">
- <!--
- property就是一個bean的屬性
- name就是用來描述屬性的名稱
- value就是值,如果是一般類型(基本類型和String)
- -->
- <property name="pid" value="1"></property>
- <property name="name" value="狗蛋"></property>
- <!--
- spring容器內部創建的student對象給Person的student對象賦值了
- -->
- <property name="student">
- <ref bean="student"/>
- </property>
- <property name="lists">
- <list>
- <value>list1</value>
- <value>list2</value>
- <ref bean="student"/>
- </list>
- </property>
- <property name="sets">
- <set>
- <value>set1</value>
- <value>set2</value>
- <ref bean="student"/>
- </set>
- </property>
- <property name="map">
- <map>
- <entry key="m1">
- <value>map1</value>
- </entry>
- <entry key="m2">
- <ref bean="student"/>
- </entry>
- </map>
- </property>
- <property name="properties">
- <props>
- <prop key="p1">prop1</prop>
- <prop key="p2">prop2</prop>
- </props>
- </property>
- <property name="objects">
- <list>
- <value>obj1</value>
- <ref bean="student"/>
- </list>
- </property>
- </bean>
- "1.0" encoding="UTF-8" xml version=
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
- <bean id="person" class="com.itheima12.spring.di.xml.constructor.Person">
- <!--
- constructor-arg指的是構造器中的參數
- index 角標 從0開始
- value 如果一般類型,用value賦值
- ref 引用類型賦值
- -->
- <constructor-arg index="0" value="asdfsafd"></constructor-arg>
- <constructor-arg index="1" ref="student"></constructor-arg>
- </bean>
- <bean id="student" class="com.itheima12.spring.di.xml.constructor.Student"></bean>
- </beans>
- "1.0" encoding="UTF-8" xml version=
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:context="http://www.springframework.org/schema/context"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
- http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context-2.5.xsd">
- <!--
- component:把一個類放入到spring容器中,該類就是一個component
- 在base-package指定的包及子包下掃描所有的類
- -->
- <context:component-scan base-package="com.itheima12.spring.scan">
- </context:component-scan>
- </beans>
好了這裏就主要介紹了這些,還有很多遇到時在看文檔吧,百度吧(包括單例,多例,事務 ,aop,繼承,初始化銷燬(
- <bean id="helloWorld" class="com.itheima12.spring.initdestroy.HelloWorld"
- init-method="init"
- destroy-method="destroy"></bean>
),懶加載,別名,工廠方法創建對象等等,都可以看案例。
3、我們來學習aop
切面(Aspect):其實就是共有功能的實現。如日誌切面、權限切面、事務切面等。在實際應用中通常是一個存放共有功能實現的普通Java類,之所以能被AOP容器識別成切面,是在配置中指定的。
通知(Advice):是切面的具體實現。以目標方法爲參照點,根據放置的地方不同,可分爲前置通知(Before)、後置通知(AfterReturning)、異常通知(AfterThrowing)、最終通知(After)與環繞通知(Around)5種。在實際應用中通常是切面類中的一個方法,具體屬於哪類通知,同樣是在配置中指定的。
連接點(Joinpoint):就是程序在運行過程中能夠插入切面的地點。例如,方法調用、異常拋出或字段修改等,但spring只支持方法級的連接點。
切入點(Pointcut):用於定義通知應該切入到哪些連接點上。不同的通知通常需要切入到不同的連接點上,這種精準的匹配是由切入點的正則表達式來定義的。
目標對象(Target):就是那些即將切入切面的對象,也就是那些被通知的對象。這些對象中已經只剩下乾乾淨淨的核心業務邏輯代碼了,所有的共有功能代碼等待AOP容器的切入。
代理對象(Proxy):將通知應用到目標對象之後被動態創建的對象。可以簡單地理解爲,代理對象的功能等於目標對象的核心業務邏輯功能加上共有功能。代理對象對於使用者而言是透明的,是程序運行過程中的產物。
織入(Weaving):將切面應用到目標對象從而創建一個新的代理對象的過程。這個過程可以發生在編譯期、類裝載期及運行期,當然不同的發生點有着不同的前提條件。譬如發生在編譯期的話,就要求有一個支持這種AOP實現的特殊編譯器;發生在類裝載期,就要求有一個支持AOP實現的特殊類裝載器;只有發生在運行期,則可直接通過Java語言的反射機制與動態代理機制來動態實現
意義:在開發的時候,各個切面和目標類是完全鬆耦合的,但是最終生成的代理對象的方法把這幾個代理對象的內容就結合起來了。
通知介紹
攔截環繞通知
- /**
- * 環繞通知
- * joinPoint.proceed();這個代碼如果在環繞通知中不寫,則目標方法不再執行
- * 能控制目標方法的執行
- *前置通知和後置通知能在目標方法的前面和後面加一些代碼,但是不能控制目標方法的執行
- */
- public void aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable{
- System.out.println("begin");
- joinPoint.proceed();//調用目標方法
- System.out.println("end");
- }
在Spring中最基礎的通知類型是攔截環繞通知(interception around advice)。
Spring裏使用方法攔截的環繞通知兼容AOP聯盟接口。
前置通知
個更簡單的通知類型是前置通知(before advice)。 它不需要MethodInvocation對象,因爲它只是在進入方法之前被調用。
前置通知的一個主要優點是它不需要調用proceed()方法,因此就不會發生無意間運行攔截器鏈失敗的情況。
- /*
- 前置通知
- 1、在目標方法執行之前
- 2、獲取不到目標方法的返回
- <aop:before method="beginTransaction" pointcut-ref="perform"/>
- * 參數:連接點
- */
- public void beginTransaction(JoinPoint joinPoint){
- String methodName = joinPoint.getSignature().getName();
- System.out.println("連接點的名稱:"+methodName);
- System.out.println("目標類:"+joinPoint.getTarget().getClass());
- System.out.println("begin transaction");
- }
異常通知
- <!--
- 異常通知
- -->
- <aop:after-throwing method="throwingMethod" throwing="ex" pointcut-ref="perform"/>
- /**
- * 異常通知
- * 接受目標方法拋出的異常
- */
- public void throwingMethod(JoinPoint joinPoint,Throwable ex){
- System.out.println(ex.getMessage());
- }
後置通知
- /**
- * 後置通知
- * 在目標方法執行之後
- 1、後置通知可以獲取到目標方法的返回值
- 2、當目標方法拋出異常,後置通知將不再執行
- <aop:after-returning method="commit" pointcut-ref="perform" returning="val"/>
- */
- public void commit(JoinPoint joinPoint,Object val){
- System.out.println("目標方法的返回值:"+val);
- System.out.println("commit");
- }
後置通知可以訪問返回值(但不能進行修改),被調用方法,方法參數以及目標對象。
最終通知
- /**
- * 最終通知
- 無論目標方法是否拋出異常都將執行
- <aop:after method="finallyMethod" pointcut-ref="perform"/>
- */
- public void finallyMethod(){
- System.out.println("finally method");
- }
引入通知
Spring 把引入通知(introduction advice)作爲一種特殊的攔截通知進行處理。
引入通知需要一個IntroductionAdvisor,和一個IntroductionInterceptor,後者實現下面的接口:
Public interface IntroductionInterceptor extends MethodInterceptor {
boolean implementsInterface(Class intf);
}
繼承自AOP聯盟MethodInterceptor 接口的invoke()方法,必須確保實現引入:也就是說,如果被調用的方法位於一個已經被引入接口裏,這個引入攔截器將負責完成對這個方法的調用--因爲它不能調用proceed()方法。
引入通知不能和任何切入點一起使用,因爲它是應用在類級別而不是方法級別。
xml文件配置
- <aop:config>
- <!-- 面 -->
- <aop:aspect ref="cacheInterceptor">
- <!-- 點 -->
- <!-- 環繞通知 -->
- <aop:around method="doAround" pointcut="execution(* cn.itcast.core.service.*.*.get*(..))"/>
- <!-- 變更 -->
- <!-- 後知通知 -->
- <aop:after method="doAfter" pointcut="execution(* cn.itcast.core.service.*.*.update*(..))"/>
- <aop:after method="doAfter" pointcut="execution(* cn.itcast.core.service.*.*.add*(..))"/>
- <aop:after method="doAfter" pointcut="execution(* cn.itcast.core.service.*.*.delete*(..))"/>
- <!--前置通知-->
- <!-- <aop:before method="" pointcut-ref=""/> -->
- </aop:aspect>
- </aop:config>
切入點表達式一般格式
Spring AOP 用戶可能會經常使用 execution切入點指示符。執行表達式的格式如下:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)
throws-pattern?)
除了返回類型模式(上面代碼片斷中的ret-type-pattern),名字模式和參數模式以外, 所有的部分都是可選的。
在service包或其子包中定義的任意方法的執行:(國家電力)
execution(* com.xyz.service..*.*(..))
在service包中定義的任意方法的執行:
execution(* com.xyz.service.*.*(..))