Spring提供了對物件進行AOP程式設計.Spring本身並不是AOP(簡單來說就是spring能幫我們產生代理物件)
AOP:橫向重複,縱向抽取.(Filter過濾器,Interceptor攔截器,Proxy動態代理)
spring實現aop的原理
動態代理
被代理的物件必須實現介面,才能產生代理物件.如果沒有介面將不能使用動態代理技術
public class UserServiceProxyFactory {
private UserService service;
public UserServiceProxyFactory(UserService service) {
super();
this.service = service;
}
public UserService getUserServiceProxy(){
UserService us = (UserService) Proxy.newProxyInstance(this.getClass().getClassLoader(),UserServiceImpl.class.getInterfaces(),new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("start transaction");
Object invoke = method.invoke(service, args);
System.out.println("commit transaction");
return invoke;
}
});
return us;
}
}
複製程式碼
cglib代理
第三方代理技術,可以對任何類進行代理.代理的原理是對被代理物件進行繼承.如果目標物件被final物件修飾,那麼該類將無法被cglib代理
public class UserProxyFactory {
public UserService getUserServiceProxy() {
Enhancer en = new Enhancer(); // 生成代理物件
en.setSuperclass(UserServiceImpl.class);
en.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] arg2, MethodProxy proxy) throws Throwable {
System.out.println("start transaction");
Object invokeSuper = proxy.invokeSuper(obj, arg2);
System.out.println("commit transaction");
return invokeSuper;
}
});
UserService obj = (UserService) en.create();
return obj;
}
}
複製程式碼
那麼spring是使用了哪一個技術呢?
兩種都使用了,根據情況來決定使用了哪一種技術,如果目標物件有介面就優先使用動態代理技術,如果沒有介面就使用cglib代理
動態代理強調的是介面,cglib代理強調的是對目標物件的繼承.
Spring中的aop名詞
名詞 | 含義 |
---|---|
joinpoint(連線點) | 目標物件中,所有可以增強的方法 |
Pointcut(切入點) | 目標物件,已經被增強的方法 |
Advice(通知/增強) | 增強的程式碼 |
Target(目標) | 被代理的物件 |
Weaving(織入) | 將通知應用到切入點的過程 |
Proxy (代理) | 將通知織入目標物件之後,形成代理物件 |
aspect(切面) | 切入點+通知 |
Spring-Aop的配置(xml配置)
1. 導包&&匯入約束
- 基礎4個元件包+2個日誌包+
spring-aspects-4.2.4.RELEASE.jar
spring-aop-4.2.4.RELEASE.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
- 匯入約束
約束檔案:spring-aop-4.2.xsd
字首為:aop
2. 準備目標物件
填寫要被代理的目標物件.
3. 準備通知(advice)
準備增強的程式碼
4. 配置進行織入,將通知應用到切入點的過程
- 配置目標物件
<bean name="UserService" class="com.demo.service.UserServiceImpl"></bean>
複製程式碼
- 配置通知
<bean name="MyAdvice" class="com.demo.aspect.MyAdvice"></bean>
複製程式碼
- 配置切入點
<aop:config>
<aop:pointcut expression="execution(* com.demo.service.*ServiceImpl.*(..))" id="pc"/>
<aop:aspect ref="MyAdvice">
<aop:after method="after" pointcut-ref="pc"/>
</aop:aspect>
</aop:config>
複製程式碼
- 切入點表示式
execution(表示式) 表示式: [方法訪問修飾符] 方法返回值 包名.類名.方法名(方法的引數)
public * cn.demo.spring.dao.*.*(..)
* cn.demo.spring.dao.*.*(..)
* cn.demo.spring.dao.UserDao+.*(..)
* cn.demo.spring.dao..*.*(..)
複製程式碼
Spring-aop(註解配置)
@EnableAspectJAutoProxy註解:開啟aop支援,相當於xml檔案中的aop:aspectj-autoproxy</aop:aspectj-autoproxy>標籤
配置檔案
<!-- <context:component-scan base-package="com.demo.service"></context:component-scan> -->
<!--1. config the target -->
<bean name="UserService" class="com.demo.service.UserServiceImpl"></bean>
<!--2. config the advice -->
<bean name="myAdvice" class="com.demo.aspect.MyAdvice"></bean>
<!--3. config the Pointcut 開啟spring對aop註解的支援 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
複製程式碼
通知類
@Aspect
public class MyAdvice {
@Pointcut("execution(* com.demo.service.*ServiceImpl.*(..))")
public void pc(){}
@Before("MyAdvice.pc()")
public void before() {
System.out.println("前置通知");
}
@After("MyAdvice.pc()")
public void after() {
System.out.println("後置通知");
}
@Around("MyAdvice.pc()")
public void around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("環繞通知前部分");
Object proceed = pjp.proceed();
System.out.println("環繞通知後部分");
}
public void beforeException() {
System.out.println("前置通知,就算出現異常也會呼叫");
}
public void afterException() {
System.out.println("後置通知,就算出現異常也會呼叫");
}
}
複製程式碼