AOP概述
在軟件業,AOP爲Aspect Oriented Programming的縮寫,意爲:面向切面編程。
- AOP是一種編程範式,隸屬於軟工範疇,指導開發者如何組織程序結構。
- AOP最早由AOP聯盟的組織提出的,制定了一套規範.Spring將AOP思想引入到框架中,必須遵守AOP聯盟的規範。
- 通過預編譯方式和運行期動態代理實現程序功能的統一維護的一種技術。
- AOP是OOP的延續,是軟件開發中的一個熱點,也是Spring框架中的一個重要內容,是函數式編程的一種衍生範型。
- 利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發的效率。
- AOP採取橫向抽取機制,取代了傳統縱向繼承體系重複性代碼(性能監視、事務管理、安全檢查、緩存)
Spring框架的AOP的底層實現
Spring的傳統AOP中根據類是否實現接口,來採用不同的代理方式
- 如果實現類接口,使用JDK動態代理完成AOP
- 如果沒有實現接口,採用CGLIB動態代理完成AOP
1,基於JDK的動態代理
必須是面向接口的,只有實現了具體接口的類才能生成代理對象
/**
* 使用JDK的方式生成代理對象
* @author Administrator
*/
public class MyProxyUtils {
public static UserDao getProxy(final UserDao dao) {
// 使用Proxy類生成代理對象
UserDao proxy = (UserDao) Proxy.newProxyInstance(dao.getClass().getClassLoader(),
dao.getClass().getInterfaces(), new InvocationHandler() {
// 代理對象方法一直線,invoke方法就會執行一次
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if("save".equals(method.getName())){
System.out.println("記錄日誌...");
// 開啓事務
}
// 提交事務
// 讓dao類的save或者update方法正常的執行下去
return method.invoke(dao, args);
}
});
// 返回代理對象
return proxy;
}
}
2,基於CGLIB動態代理
對於沒有實現了接口的類,也可以產生代理,產生這個類的子類的方式
public class MycglibUtils {
public static BookDaoImpl getProxy(){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(BookDaoImpl.class);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args,MethodProxy methodProxy) throws Throwable {
if("save".equals(method.getName())){
// 記錄日誌
System.out.println("記錄日誌了...");
}
return methodProxy.invokeSuper(obj, args);
}
});
BookDaoImpl proxy = (BookDaoImpl) enhancer.create();
return proxy;
}
}
Spring基於AspectJ的AOP的開發
1,術語
名稱 | 意義 | 解釋 |
---|---|---|
Joinpoint | 連接點 | 所謂連接點是指那些被攔截到的點。在spring中,這些點指的是方法,因爲spring只支持方法類型的連接點 |
Pointcut | 切入點 | 所謂切入點是指我們要對哪些Joinpoint進行攔截的定義 |
Advice | 通知/增強 | 所謂通知是指攔截到Joinpoint之後所要做的事情就是通知.通知分爲前置通知,後置通知,異常通知,最終通知,環繞通知(切面要完成的功能) |
Introduction | 引介 | 引介是一種特殊的通知在不修改類代碼的前提下, Introduction可以在運行期爲類動態地添加一些方法或Field |
Target | 目標對象 | 代理的目標對象 |
Weaving | 織入 | 是指把增強應用到目標對象來創建新的代理對象的過程 |
Proxy | 代理 | 一個類被AOP織入增強後,就產生一個結果代理類 |
Aspect | 切面 | 是切入點和通知的結合,以後咱們自己來編寫和配置的 |
2,AspectJ的XML方式完成AOP的開發
2.1,依賴導入與約束
<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">
</beans>
2.2,創建包結構,編寫具體的接口和實現類
- package:com.itheima.demo2
- Interface:CustomerDao
- Impl:CustomerDaoImpl
2.3,目標類配置到Spring中
<bean id="customerDao" class="com.itheima.demo3.CustomerDaoImpl"/>
2.4,定義切面類
public class MyAspectXml {
// 定義通知
public void log(){
System.out.println("記錄日誌...");
}
}
2.5,在配置文件中定義切面類
<bean id="myAspectXml" class="com.itheima.demo3.MyAspectXml"/>
2.6,在配置文件中完成aop的配置
<aop:config>
<!-- 引入切面類 -->
<aop:aspect ref="myAspectXml">
<!-- 定義通知類型:切面類的方法和切入點的表達式 -->
<aop:before method="log" pointcut="execution(public * com.itheima.demo3.CustomerDaoImpl.save(..))"/>
</aop:aspect>
</aop:config>
2.7,完成測試
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml")
public class Demo3 {
@Resource(name="customerDao")
private CustomerDao customerDao;
@Test
public void run1(){
customerDao.save();
customerDao.update();
customerDao.delete();
}
}
3,切入點的表達式
配置切入點的時候,需要定義表達式,重點的格式如下:execution(public * *(..)),具體展開如下:
<aop:before method="log" pointcut="execution(public * com.itheima.demo3.CustomerDaoImpl.save(..))"/>
解釋:execution([修飾符] 返回值類型 包名.類名.方法名(參數))
- 修飾符可以省略不寫,不是必須要出現的
- 返回值類型是不能省略不寫的,根據你的方法來編寫返回值。可以使用 * 代替
- 包名例如:com.itheima.demo3.BookDaoImpl
- 首先com是不能省略不寫的,但是可以使用 * 代替
- 中間的包名可以使用 * 號代替
- 如果想省略中間的包名可以使用 ..
- 類名也可以使用 * 號代替,也有類似的寫法:*DaoImpl
- 方法也可以使用 * 號代替
- 參數如果是一個參數可以使用 * 號代替,如果想代表任意參數使用 ..
4,AOP的通知類型
4.1,前置通知
- 在目標類的方法執行之前執行。
- 配置文件信息
<aop:after method="before" pointcut-ref="myPointcut3"/>
- 應用:可以對方法的參數來做校驗
4.2,最終通知
- 在目標類的方法執行之後執行,如果程序出現了異常,最終通知也會執行。
- 配置文件信息
<aop:after method="after" pointcut-ref="myPointcut3"/>
- 應用:例如像釋放資源
4.3,後置通知
- 方法正常執行後的通知。
- 配置文件信息
<aop:after-returning method="afterReturning" pointcut-ref="myPointcut2"/>
- 應用:可以修改方法的返回值
4.4,異常拋出通知
- 在拋出異常後通知
- 配置文件信息
<aop:after-throwing method="afterThorwing" pointcut-ref="myPointcut3"/>
- 應用:包裝異常的信息
4.5,環繞通知
- 方法的執行前後執行。
- 配置文件信息
在配置文件中編寫具體的配置:<aop:around method="around" pointcut-ref="myPointcut2"/>
- 注意:目標的方法默認不執行,需要使用ProceedingJoinPoint對來讓目標對象的方法執行。
public void around(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("around 11111");
joinPoint.proceed();
System.out.println("around 222222");
}