javascript
Spring【AOP模块】就是这么简单
2019獨角獸企業重金招聘Python工程師標準>>>
前言
到目前為止,已經簡單學習了Spring的Core模塊、....于是我們就開啟了Spring的AOP模塊了...在講解AOP模塊之前,首先我們來講解一下cglib代理、以及怎么手動實現AOP編程
cglib代理
在講解cglib之前,首先我們來回顧一下靜態代理和動態代理....我之前就寫過了靜態代理、動態代理的博文:blog.csdn.net/hon_3y/arti…
由于靜態代理需要實現目標對象的相同接口,那么可能會導致代理類會非常非常多....不好維護---->因此出現了動態代理
動態代理也有個約束:目標對象一定是要有接口的,沒有接口就不能實現動態代理.....----->因此出現了cglib代理
cglib代理也叫子類代理,從內存中構建出一個子類來擴展目標對象的功能!
- CGLIB是一個強大的高性能的代碼生成包,它可以在運行期擴展Java類與實現Java接口。它廣泛的被許多AOP的框架使用,例如Spring AOP和dynaop,為他們提供方法的interception(攔截)。
編寫cglib代理
接下來我們就講講怎么寫cglib代理:
- 需要引入cglib – jar文件, 但是spring的核心包中已經包括了cglib功能,所以直接引入spring-core-3.2.5.jar即可。
- 引入功能包后,就可以在內存中動態構建子類
- 代理的類不能為final,否則報錯【在內存中構建子類來做擴展,當然不能為final,有final就不能繼承了】
- 目標對象的方法如果為final/static, 那么就不會被攔截,即不會執行目標對象額外的業務方法。
- 測試:
?
?
?
?
使用cglib就是為了彌補動態代理的不足【動態代理的目標對象一定要實現接口】
手動實現AOP編程
AOP 面向切面的編程:
- AOP可以實現“業務代碼”與“關注點代碼”分離
下面我們來看一段代碼:
// 保存一個用戶 public void add(User user) { Session session = null; Transaction trans = null; try { session = HibernateSessionFactoryUtils.getSession(); // 【關注點代碼】trans = session.beginTransaction(); // 【關注點代碼】session.save(user); // 核心業務代碼trans.commit(); //…【關注點代碼】} catch (Exception e) { e.printStackTrace(); if(trans != null){ trans.rollback(); //..【關注點代碼】} } finally{ HibernateSessionFactoryUtils.closeSession(session); ..【關注點代碼】} }- 關注點代碼,就是指重復執行的代碼。
- 業務代碼與關注點代碼分離,好處?
- 關注點代碼寫一次即可;
- 開發者只需要關注核心業務;
- 運行時期,執行核心業務代碼時候動態植入關注點代碼; 【代理】
案例分析:
- IUser接口
我們一步一步來分析,首先我們的UserDao有一個save()方法,每次都要開啟事務和關閉事務
//@Component -->任何地方都能用這個 @Repository //-->這個在Dao層中使用public class UserDao {public void save() {System.out.println("開始事務");System.out.println("DB:保存用戶");System.out.println("關閉事務");}}- 在剛學習java基礎的時候,我們知道:如果某些功能經常需要用到就封裝成方法:
- 現在呢,我們可能有多個Dao,都需要有開啟事務和關閉事務的功能,現在只有UserDao中有這兩個方法,重用性還是不夠高。因此我們抽取出一個類出來
- 在UserDao維護這個變量,要用的時候,調用方法就行了。
- 現在的開啟事務、關閉事務還是需要我在userDao中手動調用。還是不夠優雅。。我想要的效果:當我在調用userDao的save()方法時,動態地開啟事務、關閉事務。因此,我們就用到了代理。當然了,真正執行方法的都是userDao、要干事的是AOP,因此在代理中需要維護他們的引用。
工廠靜態方法:
- 把AOP加入IOC容器中
- 把UserDao放入容器中
- 在配置文件中開啟注解掃描,使用工廠靜態方法創建代理對象
- ?
-
測試,得到UserDao對象,調用方法
?
?
工廠非靜態方法
上面使用的是工廠靜態方法來創建代理類對象。我們也使用一下非靜態的工廠方法創建對象。
package aa;import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy;/*** Created by ozc on 2017/5/11.*/public class ProxyFactory {public Object getProxyInstance(final Object target_, final AOP aop_) {//目標對象和關鍵點代碼的類都是通過外界傳遞進來return Proxy.newProxyInstance(target_.getClass().getClassLoader(),target_.getClass().getInterfaces(),new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {aop_.begin();Object returnValue = method.invoke(target_, args);aop_.close();return returnValue;}});} }配置文件:先創建工廠,再創建代理類對象
<?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:p="http://www.springframework.org/schema/p"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!--創建工廠--><bean id="factory" class="aa.ProxyFactory"/><!--通過工廠創建代理--><bean id="IUser" class="aa.IUser" factory-bean="factory" factory-method="getProxyInstance"><constructor-arg index="0" ref="userDao"/><constructor-arg index="1" ref="AOP"/></bean><context:component-scan base-package="aa"/></beans>?
?
AOP的概述
Aop: aspect object programming 面向切面編程
- 功能: 讓關注點代碼與業務代碼分離!
- 面向切面編程就是指: 對很多功能都有的重復的代碼抽取,再在運行的時候往業務方法上動態植入“切面類代碼”。
關注點:
- 重復代碼就叫做關注點。
切面:
- 關注點形成的類,就叫切面(類)!
切入點:
- 執行目標對象方法,動態植入切面代碼。
- 可以通過切入點表達式,指定攔截哪些類的哪些方法; 給指定的類在運行的時候植入切面類代碼。
切入點表達式:
- 指定哪些類的哪些方法被攔截
使用Spring AOP開發步驟
1) 先引入aop相關jar文件 (aspectj aop優秀組件)
- spring-aop-3.2.5.RELEASE.jar 【spring3.2源碼】
- aopalliance.jar 【spring2.5源碼/lib/aopalliance】
- aspectjweaver.jar 【spring2.5源碼/lib/aspectj】或【aspectj-1.8.2\lib】
- aspectjrt.jar 【spring2.5源碼/lib/aspectj】或【aspectj-1.8.2\lib】
注意: 用到spring2.5版本的jar文件,如果用jdk1.7可能會有問題。
- 需要升級aspectj組件,即使用aspectj-1.8.2版本中提供jar文件提供。
2) bean.xml中引入aop名稱空間
- xmlns:context="http://www.springframework.org/schema/context"
- http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context.xsd
引入jar包
引入4個jar包:
?
?
引入名稱空間
<?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:p="http://www.springframework.org/schema/p"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"></beans>注解方式實現AOP編程
我們之前手動的實現AOP編程是需要自己來編寫代理工廠的**,現在有了Spring,就不需要我們自己寫代理工廠了。Spring內部會幫我們創建代理工廠**。
- 也就是說,不用我們自己寫代理對象了。
因此,我們只要關心切面類、切入點、編寫切入表達式指定攔截什么方法就可以了!
還是以上一個例子為案例,使用Spring的注解方式來實現AOP編程
在配置文件中開啟AOP注解方式
<?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:p="http://www.springframework.org/schema/p"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://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="aa"/><!-- 開啟aop注解方式 --><aop:aspectj-autoproxy></aop:aspectj-autoproxy></beans>代碼:
- 切面類
- UserDao實現了IUser接口
- IUser接口
- 測試代碼:
?
?
目標對象沒有接口
上面我們測試的是UserDao有IUser接口,內部使用的是動態代理...那么我們這次測試的是目標對象沒有接口
- OrderDao沒有實現接口
- 測試代碼:
?
?
優化和AOP注解API
API:
-
@Aspect 指定一個類為切面類
-
@Pointcut("execution( cn.itcast.e_aop_anno..(..))") 指定切入點表達式*
-
@Before("pointCut_()") 前置通知: 目標方法之前執行
-
@After("pointCut_()") 后置通知:目標方法之后執行(始終執行)
-
@AfterReturning("pointCut_()") 返回后通知: 執行方法結束前執行(異常不執行)
-
@AfterThrowing("pointCut_()") 異常通知: 出現異常時候執行
-
@Around("pointCut_()") 環繞通知: 環繞目標方法執行
-
測試:
優化
我們的代碼是這樣的:每次寫Before、After等,都要重寫一次切入點表達式,這樣就不優雅了。
@Before("execution(* aa.*.*(..))")public void begin() {System.out.println("開始事務");}@After("execution(* aa.*.*(..))")public void close() {System.out.println("關閉事務");}于是乎,我們要使用@Pointcut這個注解,來指定切入點表達式,在用到的地方中,直接引用就行了!
- 那么我們的代碼就可以改造成這樣了:
XML方式實現AOP編程
首先,我們把所有的注解都去掉...
- XML文件配置
- 測試:
測試OrderDao
?
?
測試UserDao
?
?
切入點表達式
切入點表達式主要就是來配置攔截哪些類的哪些方法
查官方文檔
..我們去文檔中找找它的語法...
?
?
在文檔中搜索:execution(
?
?
語法解析
那么它的語法是這樣子的:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)符號講解:
- ?號代表0或1,可以不寫
- “*”號代表任意類型,0或多
- 方法參數為..表示為可變參數
參數講解:
- modifiers-pattern?【修飾的類型,可以不寫】
- ret-type-pattern【方法返回值類型,必寫】
- declaring-type-pattern?【方法聲明的類型,可以不寫】
- name-pattern(param-pattern)【要匹配的名稱,括號里面是方法的參數】
- throws-pattern?【方法拋出的異常類型,可以不寫】
官方也有給出一些例子給我們理解:
?
?
測試代碼
<!-- 【攔截所有public方法】 --><!--<aop:pointcut expression="execution(public * *(..))" id="pt"/>--><!-- 【攔截所有save開頭的方法 】 --><!--<aop:pointcut expression="execution(* save*(..))" id="pt"/>--><!-- 【攔截指定類的指定方法, 攔截時候一定要定位到方法】 --><!--<aop:pointcut expression="execution(public * cn.itcast.g_pointcut.OrderDao.save(..))" id="pt"/>--><!-- 【攔截指定類的所有方法】 --><!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.*(..))" id="pt"/>--><!-- 【攔截指定包,以及其自包下所有類的所有方法】 --><!--<aop:pointcut expression="execution(* cn..*.*(..))" id="pt"/>--><!-- 【多個表達式】 --><!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) || execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>--><!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) or execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>--><!-- 下面2個且關系的,沒有意義 --><!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) && execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>--><!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) and execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>--><!-- 【取非值】 --><!--<aop:pointcut expression="!execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->如果文章有錯的地方歡迎指正,大家互相交流。習慣在微信看技術文章,想要獲取更多的Java資源的同學,可以關注微信公眾號:Java3y
?
轉載于:https://my.oschina.net/u/3777556/blog/1634746
總結
以上是生活随笔為你收集整理的Spring【AOP模块】就是这么简单的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 命令行下从bak文件恢复sqlserve
- 下一篇: 英佩臻游联手发力《全球使命VR》突显未来