前言
面試中經常會被問到Spring AOP的原理,相信大家都是條件反射的想到JDK動態代理和CGLib動態代理,本文將介紹這兩個代理的區別及實現一個Spring AOP實例。
1、爲什麼會有AOP
AOP的全稱是Aspect Oriented Programming,翻譯成中文是面向切面編程。當我們在開發日誌、權限驗證、事務等功能時,如果不使用AOP,只能在每個對象裏引用公共行爲,這樣做不便於維護,而且有大量重複代碼,AOP的出現就是解決這些問題。
2、Spring AOP的原理
Spring Aop中使用的代理有兩種方式,一種是Jdk的動態代理,另一種是基於CGLIB實現的代理,當我們的bean對象實現了某一接口後,Spring默認將採用Jdk動態代理,當我們的bean對象沒有實現接口時,默認將採用CGLIB代理,Spring也支持我們在bean對象實現了接口時也強制的使用CGLIB代理。Spring的Bean容器在初始化bean對象的時候就會判斷對應的bean是否需要進行切面編程,即是否需要對其進行代理,如果需要,則初始化的時候就會把它初始化爲一個代理對象。
3、Jdk動態代理
- JDK動態代理基於攔截器和反射來實現。
- JDK動態代理是不需要第三方庫支持的,只需要JDK環境就可以進行代理。
使用的條件:
- 必須實現InvocationHandler接口;
- 使用Proxy.newProxyInstance產生代理對象;
- 被代理的對象必須要實現接口;
4、CGLib動態代理
- CGLib包的底層是通過使用一個小而快的字節碼處理框架ASM,來轉換字節碼並生成新的類。
- CGLib是針對類實現代理,主要是對指定的類生成一個子類,覆蓋其中的方法,並覆蓋其中方法實現增強。
使用條件
- CGLib是針對類實現代理,採用的是繼承,類或方法不要聲明成final;
- 必須實現MethodInterceptor接口;
- 需要使用CGLib包;
5、spring aop 實踐
- 編寫測試類
//user接口
public interface UserDao {
//登錄
void login();
}
//user實現類,測試jdk代理
public class MyUserDao implements UserDao {
@Override
public void login() {
System.out.println("接口用戶正常登錄");
}
}
//無接口實現,測試cglib
public class CglibDao {
public void cglibLogin(){
System.out.println("cglib用戶正常登錄");
}
}
//自定義切面類
public class MyAspect {
public void begin() {
System.out.println("[前置通知] 開啓事務..");
}
public void commit() {
System.out.println("[後置通知] 提交事務..");
}
public void after() {
System.out.println("[返回後通知]");
}
public void afterThrowing() {
System.out.println("[異常通知]");
}
public void arroud(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("[環繞前:]");
pjp.proceed(); // 執行目標方法
System.out.println("[環繞後:]");
}
}
//測試類
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = {"classpath:applicationContext.xml"})
public class AopTest {
@Resource
UserDao userDao;
@Resource
CglibDao cglibDao;
@Test
public void testUser(){
userDao.login();
}
@Test
public void testCglib(){
cglibDao.cglibLogin();
}
}
配置文件applicationContext.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: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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- dao實例加入容器 -->
<bean id="myUserDao" class="aop.MyUserDao"></bean>
<!-- dao實例加入容器 -->
<bean id="cglibDao" class="aop.CglibDao"></bean>
<!-- 實例化切面類 -->
<bean id="myAspect" class="aop.MyAspect"></bean>
<!-- Aop相關配置 -->
<aop:config>
<!-- 切入點表達式定義 -->
<aop:pointcut expression="execution(* aop.*Dao.*(..))" id="transactionPointcut"/>
<!-- 切面配置 -->
<aop:aspect ref="myAspect">
<!-- 【環繞通知】 -->
<aop:around method="arroud" pointcut-ref="transactionPointcut"/>
<!-- 【前置通知】 在目標方法之前執行 -->
<aop:before method="begin" pointcut-ref="transactionPointcut" />
<!-- 【後置通知】 -->
<aop:after method="commit" pointcut-ref="transactionPointcut"/>
<!-- 【返回後通知】 -->
<aop:after-returning method="after" pointcut-ref="transactionPointcut"/>
<!-- 異常通知 -->
<aop:after-throwing method="afterThrowing" pointcut-ref="transactionPointcut"/>
</aop:aspect>
</aop:config>
</beans>
輸出結果:
結束語
本篇詳細的介紹了Jdk動態代理和Cglib代理,以及手寫了一個spring aop的實例,熟悉spring aop的配置。