3-2 Spring Bean裝配之Bean的生命周期
Spring Bean裝配之Bean的生命周期
1、Bean的定義:XML文件中的<bean>標(biāo)簽的配置。
2、Bean的初始化:IOC容器(ApplicationContext)加載配置文件,并生成Bean的實(shí)例。
3、Bean的使用:通過(guò)getBean()方法獲得Bean的實(shí)例,并使用Bean。
4、Bean的銷毀:在IOC容器銷毀前,銷毀所有由該容器創(chuàng)建的Bean的實(shí)例。
Bean的初始化和銷毀(針對(duì)某一個(gè)Bean)
Bean的初始化(兩種方式):
1、實(shí)現(xiàn)org.springframework.beans.factory.InitializingBean接口,覆蓋afterPropertiesSet方法。
2、配置<bean>標(biāo)簽時(shí),添加init-method屬性。
Bean的銷毀(兩種方式)
1、實(shí)現(xiàn)org.springframework.beans.factory.DisposableBean接口,覆蓋destroy方法(比如:釋放一些連接池)。
2、配置<bean>標(biāo)簽的destroy-method屬性。
Bean的初始化和銷毀(針對(duì)所有Bean,在XML配置文件的描述中添加default-init-method="init",default-destroy-method="destroy",注意:默認(rèn)的初始化配置屬性,當(dāng)Bean里沒有該方法時(shí),也不會(huì)報(bào)錯(cuò))
注意:當(dāng)同時(shí)使用Bean的屬性配置、實(shí)現(xiàn)接口的配置,會(huì)先執(zhí)行實(shí)現(xiàn)接口初始化和銷毀方式、再執(zhí)行配置初始化和銷毀方式,默認(rèn)屬性配置不會(huì)生效,也就是說(shuō)一個(gè)Bean沒使用init-method屬性和實(shí)現(xiàn)接口,那么這個(gè)默認(rèn)屬性配置則可以使用。
7-3 Advice擴(kuò)展
Advice擴(kuò)展
一、給advice傳遞參數(shù)
【a】方式一:在通知注解里寫參數(shù),通過(guò)&& args(參數(shù)名,..)傳入?yún)?shù),該參數(shù)可以是自己定義的類型,也可以為String等類型。(通常獲取參數(shù),進(jìn)行一些判斷或者日志的記錄)
【a】方式二:切入點(diǎn)和通知分開寫的方式,在切入點(diǎn)注解里寫參數(shù),在通知注解里引用切入點(diǎn)注解的方法名。
【b】運(yùn)行時(shí),生效的一個(gè)注解,這兩個(gè)注解修飾在方法上,在通知注解里通過(guò)&& @annotation(運(yùn)行時(shí)注解修飾的方法名,首字母小寫),如果某個(gè)方法使用了運(yùn)行時(shí)注解,則一定會(huì)給value()附一個(gè)值,在通知方法里可以通過(guò)方法參數(shù),調(diào)用該方法得到該值(一般記錄該注解用了哪些方法,或者用在判斷該方法上是否加了某注解,后者判斷獲取到的值是哪值)
7-2 Advice定義及實(shí)例
Advice定義及實(shí)例(AspectJ中advice的定義)
一、Before advice:對(duì)于切面類使用@Aspect修飾類,相當(dāng)于<aop:aspect>配置,在方法上添加@Before(“execution(* com.imooc.aop.aspectj.biz.*Biz.*(..))”),相當(dāng)于<aop:before>標(biāo)簽,@Component修飾類,省略了配置<bean>標(biāo)簽。
案例(前置通知):
步驟1:業(yè)務(wù)方法
public class MoocBiz {
public void print(){
System.out.println("業(yè)務(wù)方法執(zhí)行了");
}
}
步驟2:切面方法
@Aspect
public class MoocAspect {
@Before("execution(* *Biz.*(..))")
public void beforeAdvice(){
System.out.println("前置通知方法執(zhí)行了");
}
}
XML配置:
<?xml version="1.0" encoding="UTF-8"?>
<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"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans?
? ? ? ? http://www.springframework.org/schema/beans/spring-beans.xsd ?
? ? ? ? http://www.springframework.org/schema/context
? ? ? ? http://www.springframework.org/schema/context/spring-context.xsd
? ? ? ? http://www.springframework.org/schema/aop?
? ? ? ? http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="src"></context:component-scan>
<bean id="moocBiz" class="MoocBiz"></bean>
<bean id="moocAspect" class="MoocAspect"></bean>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
測(cè)試:
@Test
public void testMooc(){
ApplicationContext ac=new ClassPathXmlApplicationContext("spring-ioc2.xml");
MoocBiz mb=(MoocBiz) ac.getBean("moocBiz");
mb.print();
}
結(jié)果:
前置通知方法執(zhí)行了
業(yè)務(wù)方法執(zhí)行了
可以定義共享的pointcut(一個(gè)方法上使用@Pointcut("execution(* com.imooc.aop.aspectj.biz.*Biz.*(..))"),然后@Before("pointcut()")修飾另一個(gè)方法上
@Aspect
public class MoocAspect {
@Pointcut("execution(* *Biz.*(..))")
public void pointcut(){
}
@Before("pointcut()")
public void beforeAdvice(){
System.out.println("前置通知方法執(zhí)行了");
}
}
二、After returning advice
和前置通知相似。
有時(shí)候需要在通知體內(nèi)得到返回的實(shí)際值,可以使用@AfterReturning綁定返回值的形式,如果知道返回值類型可以寫類型,如果不知道,可以寫Object。
后置通知案例:
public class MoocBiz {
public void print(){
System.out.println("業(yè)務(wù)方法執(zhí)行了");
}
}
@Aspect
public class MoocAspect {
@Pointcut("execution(* *Biz.*(..))")
public void pointcut(){
}
@Before("pointcut()")
public void beforeAdvice(){
System.out.println("前置通知方法執(zhí)行了");
}
@AfterReturning(pointcut="pointcut()",returning="o")
public void afterAdvice(Object o){
System.out.println("AfterReturning:"+o);
}
}
<context:component-scan base-package="src"></context:component-scan>
<bean id="moocBiz" class="MoocBiz"></bean>
<bean id="moocAspect" class="MoocAspect"></bean>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
結(jié)果:
前置通知方法執(zhí)行了
業(yè)務(wù)方法執(zhí)行了
AfterReturning:null
三、After throwing advice
有時(shí)候需要在通知體內(nèi)得到異常的實(shí)際值,可以使用@AfterReturning綁定返回值的形式
案例:
@AfterThrowing(pointcut="pointcut()",throwing="t")
public void afterThrowAdvice(RuntimeException t){
System.out.println("異常:"+t.getMessage());
}
public class MoocBiz {
public void print(){
System.out.println("業(yè)務(wù)方法執(zhí)行了");
throw new RuntimeException("運(yùn)行時(shí)異常");
}
}
結(jié)果:
前置通知方法執(zhí)行了
業(yè)務(wù)方法執(zhí)行了
異常:運(yùn)行時(shí)異常
四、After(finally)advice
最終通知必須準(zhǔn)備處理正常和異常兩種返回情況,它通常用于釋放資源。(類似于try-catch中finally的功能)
@After("pointcut()")
public void afterAdvice(){
System.out.println("后置通知執(zhí)行了");
}
前置通知方法執(zhí)行了
業(yè)務(wù)方法執(zhí)行了
后置通知執(zhí)行了
異常:運(yùn)行時(shí)異常
五、環(huán)繞通知(Around Advice)
1、環(huán)繞通知使用@Around注解來(lái)聲明,通知方法的第一個(gè)參數(shù)必須是ProceedingJoinPoint類型
2、在通知方法內(nèi)部會(huì)調(diào)用ProceedingJoinPoint的proceed()方法會(huì)導(dǎo)致執(zhí)行真正的方法,同時(shí)可以傳入一個(gè)Object[]對(duì)象,數(shù)組中的值將被作為參數(shù)傳遞給方法。
案例:
@Around("pointcut()")
public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("環(huán)繞前置通知方法執(zhí)行了========");
Object o=pjp.proceed();
System.out.println("環(huán)繞后置通知方法執(zhí)行了========");
return o;
}
結(jié)果:
環(huán)繞前置通知方法執(zhí)行了========
前置通知方法執(zhí)行了
業(yè)務(wù)方法執(zhí)行了
環(huán)繞后置通知方法執(zhí)行了========
后置通知執(zhí)行了
AfterReturning:null
7-1 AspectJ介紹及Pointcut注解應(yīng)用
AspectJ介紹及Pointcut注解應(yīng)用
1、@AspectJ的風(fēng)格類似純java注解的普通java類。
2、Spring可以使用AspectJ來(lái)做切入點(diǎn)解析。
3、AOP的運(yùn)行時(shí)仍舊是純的SpringAOP,對(duì)AspectJ的編譯器或者織入無(wú)依賴性。
Spring中配置@AspectJ
1、對(duì)@AspectJ支持可以使用XML或者Java注解類型的配置(@EnableAspectJAutoProxy使用AspectJ進(jìn)行自動(dòng)代理)
2、前提:確保AspectJ的aspectjweaver.jar庫(kù)包含在應(yīng)用程序(版本1.6.8或更高版本)的classpath中,以Spring4.0.5為基準(zhǔn)的,其他版本可能不一樣(可以根據(jù)具體情況查找Spring具體文檔)。
aspect注解的具體使用
1、@AspectJ切面使用@Aspect注解配置,擁有@Aspect的任何bean將被Spring自動(dòng)識(shí)別并應(yīng)用。
2、@Aspect注解的類可以有方法和字段,他們也可能包括切入點(diǎn)(pointcut),通知(Advice)和引入(introduction)聲明。
3、@Aspect注解是不能夠通過(guò)類路徑自動(dòng)檢測(cè)發(fā)現(xiàn)的,所以需要配合使用@Component注釋或者在xml配置bean(@Aspect注解的類配置在xml中)。
4、一個(gè)類中的@Aspect注解標(biāo)識(shí)它為一個(gè)切面,并且將自己從自動(dòng)代理中排出。
如何定義一個(gè)切入點(diǎn)
1、一個(gè)切入點(diǎn)通過(guò)一個(gè)普通的方法定義來(lái)提供,并且切入點(diǎn)表達(dá)式使用@Pointcut注解,方法返回類型必須為void
2、定義一個(gè)名為'anyOldTransfer',這個(gè)切入點(diǎn)將匹配任何名為“transfer”的方法的執(zhí)行。
切入點(diǎn)支持哪些定義方式
組合Pointcut
1、切入點(diǎn)表達(dá)式可以通過(guò)&&、||和!進(jìn)行組合,也可以通過(guò)名字引用切入點(diǎn)表達(dá)式。
2、通過(guò)組合,可以建立更加復(fù)雜的切入點(diǎn)表達(dá)式。
如何定義良好的pointcuts
1、AspectJ是編譯期的AOP
2、檢查代碼并匹配連接點(diǎn)與切入點(diǎn)的代價(jià)是昂貴的。
3、一個(gè)好的切入點(diǎn)應(yīng)該包括以下幾點(diǎn)
【a】選擇特定類型的連接點(diǎn),如:execution,get,set,call,handler。
【b】確定連接點(diǎn)范圍,如:within,withincode。
【c】匹配上下文信息,如:this,target,@annotation。
6-3 ProxyFactoryBean及相關(guān)內(nèi)容(下)
ProxyFactoryBean及相關(guān)內(nèi)容(下)
Proxying classes
1、前面的例子中如果沒有Person(BizLogic)接口,這種情況下Spring會(huì)使用CGLIB代理,而不是JDK動(dòng)態(tài)代理。
2、可以強(qiáng)制使用CGLIB,即使有接口的情況下。
3、CGLIB代理的工作原理是在運(yùn)行時(shí)生成目標(biāo)類的子類,Spring配置這個(gè)生成的子類委托方法調(diào)用到原來(lái)的目標(biāo)
4、子類是用來(lái)實(shí)現(xiàn)Decorator模式,織入通知
5、CGLIB的代理對(duì)用戶是透明的,需要注意:
【1】final方法不能被通知,因?yàn)樗鼈儾荒鼙桓采w。
【2】不用把CGLIB添加到classpath中,在Spring3.2中,CGLIB被重新包裝并包含在Spring核心JAR(即基于CGLIB的AOP就像JDK動(dòng)態(tài)代理一樣“開箱即用”,也就是說(shuō)不用手動(dòng)引入CGLIB這個(gè)包)
?使用global advisors:使*做通配符,匹配所有攔截器加入通知鏈
實(shí)現(xiàn)了MethodInterceptor這個(gè)類似的方式,才可以使用通配符的方式。
簡(jiǎn)化的proxy定義
1、Spring中支持父子bean定義的方式,以及內(nèi)部bean定義,可以帶來(lái)更簡(jiǎn)潔的代理定義(抽象屬性標(biāo)記父bean定義為抽象的這樣它不能被實(shí)例化)
Spring官方文檔給出的例子(TransactionProxyFactoryBean是基于事物的代理Bean)通過(guò)getBean獲得的是TransactionProxyFactoryBean的實(shí)例
使用ProxyFactory的好處:
1、使用SpringAOP而不必依賴于SpringIOC
2、大多數(shù)情況下最佳實(shí)踐是用IoC容器創(chuàng)建AOP代理。
3、雖然可以硬編碼方式實(shí)現(xiàn),但是Spring推薦使用配置或注解方式實(shí)現(xiàn)。
使用auto-proxy
1、Spring也允許使用“自動(dòng)代理”的bean定義,它可以自動(dòng)代理選定的bean,這是建立在Spring的“bean post processor”功能基礎(chǔ)上的(在加載bean的時(shí)候就可以修改)
2、主要通過(guò)BeanNameAutoProxyCreator實(shí)現(xiàn)(自動(dòng)代理所有以jdk開始的Bean,也包括onlyJdk這種Bean)
3、DefaultAdvisorAutoProxyCreator,當(dāng)前IoC容器中自動(dòng)應(yīng)用,來(lái)達(dá)到創(chuàng)建動(dòng)態(tài)代理的效果,不用顯示聲明引用advisor的bean定義
6-3 ProxyFactoryBean及相關(guān)內(nèi)容(下)
ProxyFactoryBean及相關(guān)內(nèi)容(下)
Proxying classes
1、前面的例子中如果沒有Person(BizLogic)接口,這種情況下Spring會(huì)使用CGLIB代理,而不是JDK動(dòng)態(tài)代理。
2、可以強(qiáng)制使用CGLIB,即使有接口的情況下。
3、CGLIB代理的工作原理是在運(yùn)行時(shí)生成目標(biāo)類的子類,Spring配置這個(gè)生成的子類委托方法調(diào)用到原來(lái)的目標(biāo)
4、子類是用來(lái)實(shí)現(xiàn)Decorator模式,織入通知
5、CGLIB的代理對(duì)用戶是透明的,需要注意:
【1】final方法不能被通知,因?yàn)樗鼈儾荒鼙桓采w。
【2】不用把CGLIB添加到classpath中,在Spring3.2中,CGLIB被重新包裝并包含在Spring核心JAR(即基于CGLIB的AOP就像JDK動(dòng)態(tài)代理一樣“開箱即用”,也就是說(shuō)不用手動(dòng)引入CGLIB這個(gè)包)
?使用global advisors:使*做通配符,匹配所有攔截器加入通知鏈
6-2 ProxyFactoryBean及相關(guān)內(nèi)容(上)
ProxyFactoryBean及相關(guān)內(nèi)容(上)
1、通過(guò)Spring API創(chuàng)建Spring AOP代理的基本方法是使用org.springframework.aop.framework.ProxyFactoryBean
2、ProxyFactoryBean可以完全控制切入點(diǎn)和通知(advice)以及他們的順序。(和其它工廠Bean實(shí)現(xiàn)方式是一樣的,都引入了一個(gè)中間層)
假如有這樣一種情況:定義了一個(gè)Bean的Id為foo的ProxyFactoryBean,那么引用foo這個(gè)對(duì)象,看到的將不是ProxyFactoryBean本身,而是ProxyFactoryBean這個(gè)類通過(guò)getObject()方法創(chuàng)建的對(duì)象。
getObject():將創(chuàng)建一個(gè)AOP代理包裝一個(gè)目標(biāo)對(duì)象。(ProxyFactoryBean通過(guò)這種方式達(dá)到代理的目的)
3、使用ProxyFactoryBean或者其它IoC相關(guān)類來(lái)創(chuàng)建AOP代理的最重要好處是因?yàn)橥ㄖ颓腥朦c(diǎn)也可以由IoC來(lái)管理。
4、當(dāng)被代理類沒有實(shí)現(xiàn)任何接口,使用CGLIB代理,否則使用JDK代理。
5、通過(guò)設(shè)置proxyTargetClass為true,可強(qiáng)制使用CGLIB代理(無(wú)論代理類是否實(shí)現(xiàn)接口)
6、如果目標(biāo)類實(shí)現(xiàn)了一個(gè)(或者多個(gè))接口,那么創(chuàng)建代理的類型將依賴ProxyFactoryBean的配置。(因?yàn)镻roxyFactoryBean里有一個(gè)ProxyInterfaces屬性,該屬性可以查看該類實(shí)現(xiàn)了哪些接口)
7、如果ProxyFactoryBean的proxyInterfaces屬性被設(shè)置為一個(gè)或者多個(gè)全限定接口名(包括包名、類名、完整的名稱),基于JDK的代理將被創(chuàng)建。
8、如果ProxyFactoryBean的proxyInterfaces屬性沒有被設(shè)置,但是目標(biāo)類實(shí)現(xiàn)了一個(gè)(或者更多)接口,那么ProxyFactoryBean將自動(dòng)檢測(cè)到這個(gè)目標(biāo)類已經(jīng)實(shí)現(xiàn)了至少一個(gè)接口,創(chuàng)建一個(gè)基于JDK的代理。
例子:創(chuàng)建基于接口的代理
ProxyFactoryBean工作原理:首先,定義了一個(gè)Id為personTarget的Bean,又定義了一個(gè)Id為myAdvisor的Bean,定義了一個(gè)Id為debugInterceptor的Bean,重點(diǎn)在接下來(lái)的定義,定義了一個(gè)Id為person的Bean,但是對(duì)應(yīng)的并不是Person類,而是Spring提供的ProxyFactoryBean這個(gè)類,并且這個(gè)Bean配置里有一個(gè)屬性名為proxyInterfaces的<property>配置,并且它的value值為Person的路徑,另外一個(gè)<property>標(biāo)簽屬性名稱為targer并且指向personTarget(這個(gè)類是Person的具體實(shí)現(xiàn)類),當(dāng)我們get()Id為person這個(gè)Bean時(shí),返回的并不是ProxyFactoryBean的對(duì)象,而是ProxyFactoryBean里的getObject()返回的對(duì)象,它返回的是屬性名為target指向的personTarget的對(duì)象,通過(guò)ProxyFactoryBean創(chuàng)建代理的時(shí)候,可以指定它的interceptor,interceptorNames屬性,是一個(gè)集合,可以通過(guò)List、Value指定它的具體內(nèi)容。
案例:(通過(guò)Spring API實(shí)現(xiàn)通知)
各種通知的類前面已經(jīng)寫好了,所以主要進(jìn)行XML配置。
public interface BizLogic {
String save();
}
public class BizLogicImpl implements BizLogic {
@Override
public String save() {
System.out.println("BizLogicImpl: BizLogicImpl save");
return "BizLogicImpl save";
}
}
XML配置:
<?xml version="1.0" encoding="UTF-8"?>
<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.xsd"?
? ? ? ? default-autowire="constructor">
? ?<bean id="afterReturningAdvice" class="springAPI.MoocAfterReturningAdvice" ></bean>
? ? ? <bean id="beforeAdvice" class="springAPI.MoocBeforeAdvice" ></bean>
? ? ? <bean id="methodInterceptor" class="springAPI.MoocMethodInterceptor" ></bean>
? ? ? <bean id="throwsAdvice" class="springAPI.MoocThrowsAdvice" ></bean>
? ?<bean id="pointcutBean" class="org.springframework.aop.support.NameMatchMethodPointcut">
? ?<property name="mappedNames">
? ?<list>
? ?<value>sa*</value>
? ?</list>
? ?</property>
? ?</bean>
? ?<bean id="defaultAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
? ?<property name="advice" ref="beforeAdvice"></property>
? ?<property name="pointcut" ref="pointcutBean"></property>
? ?</bean>
? ?<bean id="bizLogicImplTarget" class="springAPI.BizLogicImpl"></bean>
? ?<bean id="bizLogicImpl" class="org.springframework.aop.framework.ProxyFactoryBean">
? ?<property name="target"><ref bean="bizLogicImplTarget"/></property>
? ?<property name="interceptorNames">
? ?<list>
? ?<value>afterReturningAdvice</value>
? ? ? <value>beforeAdvice</value>
? ? ? <value>methodInterceptor</value>
<!-- ? ? ? <value>throwsAdvice</value>
?--> ??
? ?</list>
? ?</property>
? ?</bean>?
? ?
?</beans>
測(cè)試:
@Test
public void testApi(){
ApplicationContext ac=new ClassPathXmlApplicationContext("spring-ioc.xml");
BizLogic bl=(BizLogic)ac.getBean("bizLogicImpl");
bl.save();
}
結(jié)果:
前置通知的方法:save ? springAPI.BizLogicImpl
環(huán)繞前通知實(shí)現(xiàn)了
BizLogicImpl: BizLogicImpl save
環(huán)繞后通知實(shí)現(xiàn)了
返回后通知方法執(zhí)行了save ?springAPI.BizLogicImpl ?null
Spring的API實(shí)現(xiàn)AOP功能
1、這是Spring1.2的歷史用法,現(xiàn)在Spring4.0仍然支持。
2、這是SpringAOP的基礎(chǔ)
使用Spring的API實(shí)現(xiàn)AOP的原因:Schema-based(配置文件)和AspectJ實(shí)現(xiàn)AOP完全可以滿足我們使用AOP的功能,使用API是因?yàn)檫@是SpringAOP的基礎(chǔ),可以加深我們應(yīng)用和了解SpringAOP的理解(無(wú)論是注解方式、還是XML配置文件的方式,最終實(shí)現(xiàn)的基礎(chǔ)都是和API密切關(guān)系的)。
3、現(xiàn)在SpringAOP的用法也是基于歷史的,只是更簡(jiǎn)便了(在Spring1.2時(shí)配置文件很多,還需要了解各種配置)。
Spring的API
一、Pointcut(作為一個(gè)接口,有幾個(gè)實(shí)現(xiàn)類)
1、Pointcut接口的實(shí)現(xiàn)類之一:NameMatchMethodPointcut,根據(jù)方法名字進(jìn)行匹配。
2、該類有一個(gè)成員變量:mappedNames(它是一個(gè)集合,存放用于匹配的方法的名稱),匹配方法的集合。
配置文件的例子:(sa開頭的所有方法進(jìn)行切入)
二、Before advice(原理:?jiǎn)为?dú)寫一個(gè)前置通知的類,并實(shí)現(xiàn)MethodBeforeAdvice接口,并實(shí)現(xiàn)該接口的方法,)
1、一個(gè)簡(jiǎn)單的通知類型。
2、執(zhí)行邏輯方法前被調(diào)用,不需要MethodInvocation對(duì)象。
3、前置通知可以在連接點(diǎn)執(zhí)行之前插入自定義行為,但不能改變返回值。
案例:(會(huì)使用到后面的知識(shí),暫時(shí)編寫這些)
public class MoocBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] object, Object target) throws Throwable {
System.out.println("前置通知的方法:" + method.getName() + " ? " + target.getClass().getName());
}
}
三、Throws advice
1、如果連接點(diǎn)(執(zhí)行的業(yè)務(wù)方法)出現(xiàn)異常,throws advice在方法返回后被調(diào)用。
2、如果throws-advice的方法拋出異常,那么它將覆蓋原有異常。
3、接口org.springframework.aop.ThrowAdvice不包含任何方法,僅僅是一個(gè)聲明,其實(shí)現(xiàn)類一定要實(shí)現(xiàn)類似這樣的方法,void afterThrowing([Method, args, target],ThrowableSubclass);
Throws advice發(fā)生異常的說(shuō)明
public void afterThrowing(Exception ex);//只有一個(gè)異常。
public void afterThrowing(RemoteException ex);//另外一種類型的異常。
?public void afterThrowing(Method method,Object[] args(目標(biāo)參數(shù)),Object target(對(duì)象),Exception ex);
?public void afterThrowing(Method method,Object[] args(目標(biāo)參數(shù)),Object target(對(duì)象),ServletException ex(其他類型的異常));
結(jié)論:異常通知(如上)方法參數(shù)里必須有異常,其他參數(shù)可有可無(wú)的。
拋出異常通知案例:
public class MoocThrowsAdvice implements ThrowsAdvice{
public void afterThrows(Exception ex){
System.out.println("拋出異常通知執(zhí)行了");
}
public void afterThrows(Method method,Object[] object,Object target,Exception ex){
System.out.println("拋出異常通知執(zhí)行了");
}
}
四、After Returning advice
1、返回后通知必須實(shí)現(xiàn)org.springframework.aop.AfterReturningAdvice接口(它和基于配置文件的配置方式是一樣的)
2、可以訪問(wèn)返回值(但不能修改返回值)、被調(diào)用的方法、方法的參數(shù)和目標(biāo)。
3、如果拋出異常,將會(huì)拋出攔截器鏈,替代返回值。
返回后通知案例
public class MoocAfterReturningAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("返回后通知方法執(zhí)行了"+method.getName()+" ?"+target.getClass().getName()+" ?"+returnValue);
}
}
五、Interception around advice(環(huán)繞通知)
1、Spring的切入點(diǎn)模型使得切入點(diǎn)可以獨(dú)立與advice重用,以針對(duì)不同的advice可以使用相同的切入點(diǎn)(和之前基于XML配置文件的AOP實(shí)現(xiàn)方式是一樣的,切入點(diǎn)可以放在外面單獨(dú)地去定義,通過(guò)Point reference,在每一個(gè)業(yè)務(wù)邏輯方法中都可以引用相同的切入點(diǎn),當(dāng)然,每個(gè)Advice也可以定義自己的Point cut)。
案例:
public class MoocMethodInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Object obj = null;
System.out.println("環(huán)繞前通知實(shí)現(xiàn)了");
obj = invocation.proceed();
System.out.println("環(huán)繞后通知實(shí)現(xiàn)了");
return null;
}
}
六、Introduction advice(和XML配置中的Introduction advice一樣的功能)
1、Spring把引入通知作為一種特殊的攔截通知。
2、如果使用API實(shí)現(xiàn)AOP,則需要IntroductionAdvisor和IntroductionInterceptor這兩個(gè)接口。
3、Introduction advice僅適用于類,不能和任何其它切入點(diǎn)一起使用。
如下為基于XML配置的Introduction advice內(nèi)容
一個(gè)Spring test suite的例子
1、如果調(diào)用lock()方法,希望所有的setter方法拋出LockedException異常(使用場(chǎng)景:如使物體不可變,AOP典型例子)
2、需要完成繁重任務(wù)的IntroductionInterceptor,不是去實(shí)現(xiàn)該接口,而是使用org.springframework.aop.support.DelegatingIntroductionInterceptor
3-5 Spring Bean裝配之Resource
Resources:針對(duì)資源文件的統(tǒng)一接口,通過(guò)Spring加載一些資源文件的時(shí)候,可以通過(guò)它去控制。
——UrlResource:URL對(duì)應(yīng)的資源,根據(jù)一個(gè)URL地址即可構(gòu)建Resources。
——ClassPathResoure:獲取類路徑下的資源文件(相對(duì)路徑)。
——FileSystemResource:獲取文件系統(tǒng)里面的資源(絕對(duì)路徑)。
——ServletContextResource:ServletContext封裝的資源,用于訪問(wèn)ServletContext環(huán)境下的資源。(和Web相關(guān)的資源文件的入口)
——InputStreamResource:針對(duì)于輸入流封裝的資源。(構(gòu)建它需要InputStream)
——ByteArrayResource:針對(duì)于字節(jié)數(shù)組封裝的資源。(構(gòu)建它需要ByteArray)
ResourceLoader:對(duì)Resource加載的一個(gè)類,在SpringIOC容器中,所有的ApplicationContext都實(shí)現(xiàn)了ResourceLoader接口,所有的ApplicationContext都可以用來(lái)獲取Resource實(shí)例,所以可以通過(guò)getResource(String location)方法獲取資源Resource。
ResourceLoader接口的聲明(有個(gè)方法,輸入為文件的位置,返回的是Resource的實(shí)例)
ResourceLoader注入?yún)?shù)時(shí)前綴的幾種類型
ResourceLoader前綴:classpath:(相對(duì)路徑,加載文件)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?file:(絕對(duì)路徑,加載文件)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?url:??http(web路徑、加載文件)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (none):直接輸入路徑,依賴ApplicationContext
案例:(Bean通過(guò)實(shí)現(xiàn)ApplicationContext接口,間接的實(shí)現(xiàn)了ResourceLoader接口(加載Resource實(shí)例),就可以使用getResource()獲取Resource的實(shí)例,Resource擁有一系列的方法,比如獲取文件名稱(getFilename()和獲取文件長(zhǎng)度contentLength())
步驟1:
public class ResourceDemo implements ApplicationContextAware {
private ApplicationContext ac;
@Override
public void setApplicationContext(ApplicationContext ac) throws BeansException {
this.ac=ac;
}
public void resource() throws IOException{
Resource r=ac.getResource("classpath:resourceDemo.txt");(直接寫文件,而不寫全路徑,是因?yàn)镴ava build path 配置了source,所以這里是相對(duì)路徑)
System.out.println(r.getFilename());
System.out.println(r.contentLength());
}
}
步驟2:
? ?<bean id="resourceDemo" class="ResourceDemo" ></bean>
步驟3:
@Test
public void testBean() throws IOException{
ApplicationContext ac=new ClassPathXmlApplicationContext("spring-ioc.xml");
ResourceDemo rd=(ResourceDemo)ac.getBean("resourceDemo");
rd.resource();
}
測(cè)試:
@Test
public void testBean() throws IOException{
ApplicationContext ac=new ClassPathXmlApplicationContext("spring-ioc.xml");
ResourceDemo rd=(ResourceDemo)ac.getBean("resourceDemo");
rd.resource();
}
結(jié)果:(文件:resourceDemo.txt,在src——>resource文件夾下)
resourceDemo.txt
6
案例:file方式
案例:url方式
3-4 Spring Bean裝配之自動(dòng)裝配
xml方式的Bean自動(dòng)注入(<beans>標(biāo)簽的屬性default-autowire=byName)
No:不做任何操作,默認(rèn)選項(xiàng)。
byname:根據(jù)setXXX()方法的set后的名稱進(jìn)行自動(dòng)注入(首字母不區(qū)分大小寫)。查找xml中有沒有<bean>標(biāo)簽id的名稱與屬性完全一致的,如果有則自動(dòng)注入,并且執(zhí)行set方法,基于set方法,如果沒有不報(bào)錯(cuò),該屬性置為NULL,并且不執(zhí)行set()。
byType:根據(jù)類型自動(dòng)注入,如果容器中存在一個(gè)與指定屬性類型相同的bean,那么該屬性自動(dòng)注入;如果存在多個(gè)該類型bean,那么拋出異常,并指出不能使用byType方式進(jìn)行自動(dòng)裝配,如果沒有找到相匹配的bean,則什么事都不發(fā)生,該屬性置為null基于set方法。
Constructor:與byType方式類似,不同之處在于它應(yīng)用于構(gòu)造器參數(shù)。如果容器中沒有找到與構(gòu)造器參數(shù)類型一個(gè)bean,那么拋出異常。
Spring官方文檔對(duì)自動(dòng)注入屬性的四種類型解釋
案例:(原理:IOC容器加載XML配置文件,由于設(shè)置了自動(dòng)按名稱進(jìn)行注入,所以當(dāng)獲取Bean1的實(shí)例時(shí),查找XML文件中有沒有Id為set方法后的名稱(去掉set,并且不區(qū)分大小寫),如果有則進(jìn)行自動(dòng)注入)
步驟1:
編寫兩個(gè)類,并在其中一個(gè)類中引用其他類作為自己的屬性,而且必須提供set方法。
步驟2:
編寫xml文件
<beans>標(biāo)簽添加?default-autowire="byName"
并且對(duì)著兩個(gè)類進(jìn)行<bean>配置。
代碼:
public class Bean1{
private Bean2 bean2;
public Bean2 getBean3() {
return bean2;
}
public void setBean3(Bean2 bean2) {
this.bean2 = bean2;
}
}
xml:
<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.xsd"?
? ? ? ? default-autowire="byName">
? ?<bean id="bean1" class="main.java.com.imooc.Bean1"></bean>
? ?<bean id="bean3" class="main.java.com.imooc.Bean2"></bean>
?</beans>
結(jié)果:
Bean1 [bean2=main.java.com.imooc.Bean2@2ec5f45d]
當(dāng)default-autowire="byType"時(shí):則會(huì)根據(jù)屬性的類型查找xml中是否有和它相同的類型的<bean>(也就是class),如果有以一個(gè)則進(jìn)行注入,如果一個(gè)以上則報(bào)異常,沒有則為null。
通過(guò)構(gòu)造方法實(shí)現(xiàn)自動(dòng)注入:
步驟1:提供構(gòu)造方法為屬性賦值。
代碼:
public class Bean1{
private Bean2 bean2;
public Bean1(Bean2 bean2) {
super();
this.bean2 = bean2;
}
步驟2:xml中設(shè)置default-autowired="constructor”,并且提供和有參構(gòu)造函數(shù)的參數(shù)相同類型的一個(gè)<bean>標(biāo)簽。
代碼:
<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.xsd"?
? ? ? ? default-autowire="constructor">
? ?<bean id="bean1" class="main.java.com.imooc.Bean1"></bean>
? ?<bean class="main.java.com.imooc.Bean2"></bean>
?</beans>
結(jié)果:
Bean2@5c5766b1

Spring入門篇
- 難度中級(jí)
- 時(shí)長(zhǎng) 7小時(shí) 0分
- 人數(shù)1812
- 評(píng)分8.4
Spring是為解決企業(yè)應(yīng)用程序開發(fā)復(fù)雜性而創(chuàng)建的一個(gè)Java開源框架,應(yīng)用非常廣泛。業(yè)內(nèi)非常流行的SSH架構(gòu)中的其中一個(gè)"S"指的就是Spring。本門課程作為Spring的入門級(jí)課程,將結(jié)合實(shí)例為您帶來(lái)依賴注入、IOC和AOP的基本概念及用法,為后續(xù)高級(jí)課程的學(xué)習(xí)打下基礎(chǔ)。

曾混跡于企業(yè)應(yīng)用領(lǐng)域,后轉(zhuǎn)戰(zhàn)電商,現(xiàn)奮戰(zhàn)于互聯(lián)網(wǎng)教育行業(yè),轉(zhuǎn)眼間已匆匆數(shù)年,喜愛技術(shù)甚于自己,至今不悔。感天地之廣大,歲月之蹉跎,若能重來(lái),仍代碼。
舉報(bào)