什麼是AOP
在軟件業,AOP爲Aspect Oriented Programming的縮寫,意爲:面向切面編程,通過預編譯方式和運行期動態代理實現程序功能的統一維護的一種技術。AOP是OOP(面向對象編程)的延續,是軟件開發中的一個熱點,也是Spring框架中的一個重要內容,是函數式編程的一種衍生範型。
AOP採取橫向抽取機制,取代了傳統縱向繼承體系重複性代碼。經典應用:事務管理、性能監視、安全檢查、緩存 、日誌等,Spring AOP使用純Java實現,不需要專門的編譯過程和類加載器,在運行期通過代理方式向目標類織入增強代碼。
利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發的效率。
AspectJ是一個基於Java語言的AOP框架,Spring2.0開始,Spring AOP引入對Aspect的支持,AspectJ擴展了Java語言,提供了一個專門的編譯器,在編譯時提供橫向代碼的織入。
AOP實現原理
aop底層採用代理機制進行實現。
1、接口 + 實現類 :
如果目標對象實現了接口默認情況下會使用 jdk 的動態代理Proxy實現AOP,也可以強制使用cglib。操作如下:
(1)添加cglib庫,SPRING_HOME/cglib/*.jar
(2)在spring配置文件中加入如下配置:
<aop:aspectj-autoproxy proxy-target-class="true"/>
2、只有實現類:
如果目標對象沒有實現接口,就必須使用cglib字節碼增強,Spring 會自動在動態代理和CGLIB之間轉換。
JDK動態代理和CGLIB字節碼生成的區別?
1、原理的區別:
jdk動態代理是利用反射機制生成一個實現代理接口的匿名類,在調用具體方法前調用InvokeHandler來處理。
cglib動態代理是利用asm開源包,對代理對象類的class文件加載進來,通過修改其字節碼生成子類來處理。
2、生成的區別:
jdk動態代理只能對實現了接口的類生成代理,而不能針對類
cglib是針對類實現代理,主要是對指定的類生成一個子類,覆蓋其中的方法,因爲是繼承,所以該類或方法最好不要聲明成final
AOP術語
1、target:目標類,需要被代理的類。例如:UserService
2、Joinpoint(連接點):所謂連接點是指那些可能被攔截到的方法。例如:所有的方法
3、PointCut (切入點):已經被增強的連接點。例如:addUser()
4、advice (通知/增強):增強代碼。例如:after、before
5、 Weaving(織入):是指把增強advice應用到目標對象target來創建新的代理對象proxy的過程.
6、proxy 代理類
7、Aspect(切面): 是切入點pointcut和通知advice的結合
一個線是一個特殊的面。
一個切入點和一個通知,組成成一個特殊的面。
示例:
目標類:
public interface UserService {
public void addUser();
public void updateUser();
public void deleteUser();
}
切面類:
public class MyAspect {
public void before(){
System.out.println("開啓事務");
}
public void after(){
System.out.println("提交事務");
}
}
jdk動態代理工廠:
public class MyBeanFactory {
public static UserService createService() {
// 1 目標類
final UserService userService = new UserServiceImpl();
// 2切面類
final MyAspect myAspect = new MyAspect();
/*
* 3 代理類:將目標類(切入點)和 切面類(通知) 結合 --> 切面 Proxy.newProxyInstance
* 參數1:loader ,類加載器,動態代理類 運行時創建,任何類都需要類加載器將其加載到內存。
* 一般情況:當前類.class.getClassLoader(); 目標類實例.getClass().get...
* 參數2:Class[] interfaces 代理類需要實現的所有接口
* 方式1:目標類實例.getClass().getInterfaces() ;注意:只能獲得自己接口,不能獲得父元素接口
* 方式2:new Class[]{UserService.class} 例如:jdbc 驅動 --> DriverManager
* 獲得接口 Connection 參數3:InvocationHandler 處理類,接口,必須進行實現類,一般採用匿名內部 提供
* invoke 方法,代理類的每一個方法執行時,都將調用一次invoke 參數31:Object proxy :代理對象
* 參數32:Method method : 代理對象當前執行的方法的描述對象(反射) 執行方法名:method.getName()
* 執行方法:method.invoke(對象,實際參數) 參數33:Object[] args :方法實際參數
*
*/
UserService proxService = (UserService) Proxy.newProxyInstance(MyBeanFactory.class.getClassLoader(),
userService.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 前執行
myAspect.before();
// 執行目標類的方法
Object obj = method.invoke(userService, args);
// 後執行
myAspect.after();
return obj;
}
});
return proxService;
}
}
cglib代理工廠:
public class MyBeanFactory {
public static UserServiceImpl createService() {
// 1 目標類
final UserServiceImpl userService = new UserServiceImpl();
// 2切面類
final MyAspect myAspect = new MyAspect();
// 3.代理類 ,採用cglib,底層創建目標類的子類
// 3.1 核心類
Enhancer enhancer = new Enhancer();
// 3.2 確定父類
enhancer.setSuperclass(userService.getClass());
/*
* 3.3 設置回調函數 , MethodInterceptor接口 等效 jdk InvocationHandler接口
* intercept() 等效 jdk invoke() 參數1、參數2、參數3:以invoke一樣 參數4:methodProxy
* 方法的代理
*
*
*/
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)
throws Throwable {
// 前
myAspect.before();
// 執行目標類的方法
Object obj = method.invoke(userService, args);
// * 執行代理類的父類 ,執行目標類 (目標類和代理類 父子關係)
methodProxy.invokeSuper(proxy, args);
// 後
myAspect.after();
return obj;
}
});
// 3.4 創建代理
UserServiceImpl proxService = (UserServiceImpl) enhancer.create();
return proxService;
}
}
測試
@Test
public void demo01(){
UserService userService = MyBeanFactory.createService();
userService.addUser();
userService.updateUser();
userService.deleteUser();
}
AspectJ
簡介
AspectJ是一個基於Java語言的AOP框架
Spring2.0以後新增了對AspectJ切點表達式支持
@AspectJ 是AspectJ1.5新增功能,通過JDK5註解技術,允許直接在Bean類中定義切面,新版本Spring框架,建議使用AspectJ方式來開發AOP
主要用途:自定義開發
切入點表達式
1、execution() 用於描述方法
語法:execution(修飾符 返回值 包.類.方法名(參數) throws異常)
修飾符,一般省略
public 公共方法
* 任意
返回值,不能省略
void 返回沒有值
String 返回值字符串
* 任意
包,[省略]
com.itheima.crm 固定包
com.itheima.crm.*.service crm包下面子包任意 (例如:com.itheima.crm.staff.service)
com.itheima.crm.. crm包下面的所有子包(含自己)
com.itheima.crm.*.service.. crm包下面任意子包,固定目錄service,service目錄任意包
類,[省略]
UserServiceImpl 指定類
*Impl 以Impl結尾
User* 以User開頭
* 任意
方法名,不能省略
addUser 固定方法
add* 以add開頭
*Do 以Do結尾
* 任意
(參數)
() 無參
(int) 一個整型
(int ,int) 兩個
(..) 參數任意
throws ,可省略,一般不寫。
綜合1
execution(* com.itheima.crm.*.service..*.*(..))
綜合2
<aop:pointcut id="myPointCut" expression="execution(* com.itheima.*WithCommit.*(..)) ||
execution(* com.itheima.*Service.*(..))" />
2、within:匹配包或子包中的方法(瞭解)
within(com.itheima.aop..*)
3、this:匹配實現接口的代理對象中的方法(瞭解)
this(com.itheima.aop.user.UserDAO)
4、target:匹配實現接口的目標對象中的方法(瞭解)
target(com.itheima.aop.user.UserDAO)
5、args:匹配參數格式符合標準的方法(瞭解)
args(int,int)
6、bean(id) :對指定的bean所有的方法(瞭解)
bean(‘userServiceId’)