1、創(chuàng)建增強(qiáng)類型
spring的增強(qiáng)類型為5個(gè)
1、前置,后置,環(huán)繞,異常,引介,這些增強(qiáng)接口都有一些方法,
在接口方法中定義橫切邏輯,就可以將它們織入到目標(biāo)類方法的相應(yīng)連接點(diǎn)的位置。
我們這里只看前置和環(huán)繞。
我們先來溫故一下,spring如何使用jdk動(dòng)態(tài)代理實(shí)現(xiàn)aop
1、把業(yè)務(wù)邏輯里的橫切邏輯拿出來只留下業(yè)務(wù)邏輯
2、創(chuàng)建橫切邏輯類,實(shí)現(xiàn)InvocationHandler接口,并給構(gòu)造方法傳入一個(gè)目標(biāo)類對(duì)象Object target
3、實(shí)現(xiàn)接口里的方法invoke(Object proxy,Method method,Object[] args),
在invoke方法中調(diào)用編寫橫切邏輯,然后調(diào)用method.invoke(target,args)采用反射機(jī)制調(diào)用業(yè)務(wù)類方法,即目標(biāo)類對(duì)象方法。
4、測(cè)試類,聲明一個(gè)業(yè)務(wù)類接口target,并業(yè)務(wù)類實(shí)現(xiàn)類實(shí)例化它;聲明一個(gè)增強(qiáng)類(織入器)handler并實(shí)例化(織入器就是整合和業(yè)務(wù)邏輯和橫切邏輯);
在聲明一個(gè)業(yè)務(wù)類接口proxy,并調(diào)用Proxy的靜態(tài)方法newProxyInstance實(shí)例化一個(gè)整合后的代理對(duì)象。該方法有三個(gè)參數(shù),第一個(gè)是目標(biāo)類的類加載器,第二個(gè)
是創(chuàng)建代理實(shí)例所實(shí)現(xiàn)的接口,第三個(gè)是織入器。
這里沒有用到spring配置文件,完全硬編碼。
硬編碼就是把代碼寫死,如果要改功能就只能修改代碼,軟編碼就是不需要修改代碼,只需要修改配置就可以修改功能。
spring底層就是采用動(dòng)態(tài)代理實(shí)現(xiàn)aop的
怎么使用增強(qiáng)類型實(shí)現(xiàn)aop
spring使用前置增強(qiáng):
- 創(chuàng)建一個(gè)前置增強(qiáng)類并且實(shí)現(xiàn)MethodBeforeAdvice接口,實(shí)現(xiàn)一個(gè)方法before(Method method,Object[] args,Object target) throws Throwable
method為目標(biāo)類方法,args為目標(biāo)類方法的入?yún)ⅲ瑃arget為目標(biāo)類實(shí)例。然后直接在before()方法里寫橫切邏輯代碼,不再需要調(diào)用method.invoke,因?yàn)檫@里已經(jīng)是前置通知了。
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
/*
* 前置增強(qiáng)類
*/
public class LogBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
System.out.println("我是前置日志.......");
}
}
- 采用硬編碼的方式測(cè)試前置通知,聲明一個(gè)業(yè)務(wù)邏輯接口target,業(yè)務(wù)邏輯實(shí)現(xiàn)類實(shí)例化它;聲明一個(gè)前置增強(qiáng)類advice并實(shí)例化它;聲明一個(gè)
spring提供的代理工廠ProxyFactory,pf并實(shí)例化它;然后設(shè)置代理目標(biāo)pf.setTarget(target),為代理目標(biāo)添加增強(qiáng)pf.addAdvice(advice);然后聲明一個(gè)業(yè)務(wù)服務(wù)接口,并用pf.getProxy()實(shí)例化它。解析PrxoyFactory以后再說。
/*
采用硬編碼方式測(cè)試前置通知
*/
ForumService target = new ForumServiceImpl();
LogBeforeAdvice advice = new LogBeforeAdvice();
ProxyFactory pf = new ProxyFactory();
pf.setTarget(target);
pf.addAdvice(advice);
ForumService proxy = (ForumService) pf.getProxy();
proxy.removeForum(1);
- 采用spring配置方式
1、聲明增強(qiáng)類bean,advice
2、聲明業(yè)務(wù)邏輯實(shí)現(xiàn)類bean,target
3、聲明ProxyFactoryBean,pf
并且傳參,proxyInterfaces指定代理接口一般只有業(yè)務(wù)邏輯接口,如果是多個(gè)接口,使用<list>元素;interceptorNames指定使用的增強(qiáng)bean,target;target-ref指定對(duì)哪個(gè)bean進(jìn)行代理即對(duì)哪個(gè)業(yè)務(wù)實(shí)現(xiàn)類bean進(jìn)行代理,這里是target。
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<bean id="advice" class="com.ljs.springaop.advice.LogBeforeAdvice"/>
<bean id="target" class="com.ljs.springaop.advice.ForumServiceImpl"/>
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="target"/>
<property name="interceptorNames">
<value>advice</value>
</property>
<property name="proxyInterfaces" value="com.ljs.springaop.advice.ForumService"/>
</bean>
</beans>
/*
采用軟編碼方式測(cè)試前置通知
*/
ApplicationContext context = new ClassPathXmlApplicationContext(
"com/ljs/springaop/advice/spring.xml");
ForumService proxy2 = (ForumService) context.getBean("proxy");
proxy2.removeForum(1);
spring使用環(huán)繞增強(qiáng):
- 創(chuàng)建一個(gè)環(huán)繞增強(qiáng)類并且實(shí)現(xiàn)MethodInterceptor,這個(gè)有點(diǎn)像spring使用jdk動(dòng)態(tài)代理實(shí)現(xiàn)aop,環(huán)繞綜合實(shí)現(xiàn)了前置和后置兩者的功能。然后實(shí)現(xiàn)該方法,invoke(MethodInvocation invocation) throws Throwable,注意這個(gè)接口是org.aopalliance.intercept.MethodInterceptor,別搞錯(cuò)了。
- 再該方法中需要先獲取該目標(biāo)方法的參數(shù),用Object[] args=invocation.getArguments()然后再傳值String clientName = (String)args[0]
- 在編寫橫切邏輯
- 通過反射機(jī)制調(diào)用目標(biāo)方法Object obj = invocation.proceed();
- 編寫橫切邏輯
- return obj;
- 采用spring配置方式,增強(qiáng)bean,業(yè)務(wù)邏輯實(shí)現(xiàn)類bean,spring代理工廠bean,然后跟前置一樣傳參。
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class LogInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Object[] args = invocation.getArguments();//目標(biāo)方法入?yún)? int number = (int) args[0];
System.out.println("我是環(huán)繞log1,你想刪除的是"+number);//在目標(biāo)方法前執(zhí)行調(diào)用
Object obj = invocation.proceed();//通過反射機(jī)制調(diào)用目標(biāo)方法
System.out.println("我是環(huán)繞log2");//在目標(biāo)方法后執(zhí)行調(diào)用
return obj;
}
}
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<bean id="advice" class="com.ljs.springaop.advice.interceptor.LogInterceptor"/>
<bean id="target" class="com.ljs.springaop.advice.interceptor.ForumServiceImpl"/>
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="target"/>
<property name="interceptorNames">
<value>advice</value>
</property>
<property name="proxyInterfaces" value="com.ljs.springaop.advice.interceptor.ForumService"/>
</bean>
</beans>
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(
"com/ljs/springaop/advice/interceptor/spring.xml");
ForumService proxy = (ForumService) context.getBean("proxy");
proxy.removeForum(1);
}