目錄
1、動態代理
1.1、jdk動態代理
1.2、cglib動態代理
1.3、動態代理的好處
2、什么是AOP
2.1、AOP常用術語
2.2、切面的構成
3、使用aspectJ框架實現AOP
3.1、aspectJ簡介
聲明實現類ServiceImpl
聲明切面
3.3、@AfterReturning后置通知
切面類代碼
3.4、@Around環繞通知(功能最強的通知)
1、動態代理
1.1、jdk動態代理
使用jdk中的Proxy,Method,InvocaitonHanderl創建代理對象。 jdk動態代理要求目標類必須實現接口,關于細節本文就不贅述了。
要求:
必須要有接口
目標類必須實現接口(一個或多個)
1.2、cglib動態代理
第三方的工具庫,創建代理對象,原理是繼承。 通過繼承目標類,創建子類。子類就是代理對象。 要求目標類不能是final的,方法也不能是final的
1.3、動態代理的好處
在目標類源代碼不改變的情況下,增加功能。
減少代碼的重復
專注業務邏輯代碼
解耦合,讓你的業務功能和日志,事務非業務功能分離。
2、什么是AOP
面向切面編程, 基于動態代理的,可以使用jdk,cglib兩種代理方式。Aop就是動態代理的規范化, 把動態代理的實現步驟,方式都定義好了, 讓開發人員用一種統一的方式,使用動態代理實現。
2.1、AOP常用術語
Aspect: 切面,給你的目標類增加的功能,就是切面。 像日志,事務都是切面。切面的特點: 一般都是非業務方法,獨立使用的。
JoinPoint:連接點 ,連接業務方法和切面的位置。需要給哪個方法增加切面,這個方法就是連接點。
Pointcut : 切入點 ,指多個連接點方法的集合。
目標對象: 給哪個類的方法增加功能, 這個類就是目標對象。
Advice:通知,通知表示切面功能執行的時間。
2.2、切面的構成
切面就是要給別的方法進行增強的方法,一個切面有以下三個要素。
切面的功能代碼,切面干什么
切面的執行位置,使用Pointcut表示切面執行的位置
切面的執行時間,使用Advice表示時間,在目標方法之前,還是目標方法之后。
3、使用aspectJ框架實現AOP
3.1、aspectJ簡介
aspectJ是一個開源的專門做aop的框架。spring框架中集成了aspectj框架,通過spring就能使用aspectj的功能。aspectJ框架實現aop有兩種方式:
使用xml的配置文件 : 配置全局事務
使用注解,我們在項目中要做aop功能,一般都使用注解, aspectj有5個注解。
再使用aspectJ做aop之前要先加入aspectJ依賴。
<!--aspectJ依賴--><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>5.2.5.RELEASE</version></dependency>
3.2 @Before前置通知
前置通知注解修飾的切面在連接點方法之前執行。下面通過一段代碼體驗一下。
聲明接口IService
public interface IService {void doSome(String name, int age);
}
聲明實現類ServiceImpl
public class ServiceImpl implements IService {@Overridepublic void doSome(String name, int age) {System.out.println("===doSome()===");}
}
聲明切面
@Aspect
public class MyAspectJ {/*** 定義功能增強方法(方法就是切面)* 1、方法的必須為public* 2、方法無返回值* 3、方法名稱自定義* 4、方法可以有參數,也可以沒有參數* 5、方法的定義上方加入注解,表示切入點的執行時機@Before(value = "execution(public void com.mms.ba01.ServiceImpl.doSome(String,int))")public void beforeLog() {System.out.println("前置通知->系統當前時間:" + new Date());}*//*前置通知,帶方法參數的切面切面方法有參數時要求參數是JoinPoint類型,參數名自定義,該參數就代表了連接點方法,即doSome方法使用該參數可以獲取切入點表達式、切入點方法簽名、目標對象等*/@Before(value = "execution(* *..ServiceImpl.doSome(..))")public void beforeLog(JoinPoint jp) {System.out.println("連接點方法的方法簽名="+jp.getSignature());System.out.println("連接點方法的方法名="+jp.getSignature().getName());//獲取連接點方法參數Object[] args = jp.getArgs();for (Object arg : args) {System.out.println("arg="+arg);}}
}
測試
public class MyTest {//aop前置通知@Testpublic void test01() {String config = "ba01/applicationContext.xml";ApplicationContext ac = new ClassPathXmlApplicationContext(config);//獲取目標對象,此時的service就是spring生成的代理對象//注意返回值類型是接口類型,不能是實現類接口,否則報錯IService service = (IService) ac.getBean("service");//使用代理對象執行方法service.doSome("張三",23);}
}
3.3、@AfterReturning后置通知
在IService接口中新增方法:
Student doStudent(Student student);
在ServiceImpl實現doStudent方
@Overridepublic Student doStudent(Student student) {return student;}
切面類代碼
@Aspect
public class MyAspectJ {/*** @AfterReturning: 后置通知,在連接點方法執行之后執行后置通知方法* 方法定義格式:* 1、公共方法* 2、沒有返回值* 3、方法名稱自定義* 4、與前置通知一樣,可以有JoinPoint類型參數,該參數表示連接點方法對象;還可以有一個* Object類型參數,用于接收連接點方法的執行結果,注意該參數的參數名必須與切入點表達式* 的returning屬性的屬性值一致,表示將returning屬性值賦給Object對象*//*@AfterReturning(value = "execution(* *..ServiceImpl.doOther(..))", returning = "obj")public void afterTransaction(JoinPoint jp, Object obj) {System.out.println("doOther方法的返回參數="+obj);System.out.println("事務已提交...");經過驗證:在后置通知切面內不能改變連接點方法的返回值}*/@AfterReturning(value = "execution(* *..ServiceImpl.doStudent(..))", returning = "obj")public void afterTransaction(JoinPoint jp, Object obj) {System.out.println(obj);Student student = new Student();student.setName("李四");student.setAge(24);obj = student;System.out.println("===查看是否改變了連接點方法的返回值==="+obj);/*經過驗證:在后置通知切面內不能改變連接點方法的返回值*/}
}
3.4、@Around環繞通知(功能最強的通知)
環繞通知是功能最強的通知,它的本質就是jdk動態代理,他可以在連接點方法之前和之后都可以執行,最厲害的是他可以改變連接點方法的執行結果(返回結果)。還是拿上面的doStudent(Student student)方法來說明,經過驗證前置通知和后置通知都不能改變doStudent(Student student)方法的返回值。下面看一下環繞通知是如何做的。
切面類
@Aspect
public class MyAspectJ {/*環繞通知:@Around(切入點表達式)1、環繞通知是最重要的一個通知,他表示在連接點方法的前或者后都可以執行,它的本質就是jdk動態代理的invoke方法的method參數2、定義格式a、publicb、必須有返回值,類型為Object*/@Around(value = "pointCut()")/*再次回憶一下jdk動態代理的invoke方法的定義@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {是不是感覺與下面的方法定義眼熟啊,沒錯,環繞通知切面的定義實質上就是jdk動態代理*/public Object around(ProceedingJoinPoint pj) throws Throwable {System.out.println("環繞通知在連接點方法之前執行了...");Object result = null;result = pj.proceed();Student student = new Student();student.setName("李四");student.setAge(24);//改變連接點方法返回值result = student;System.out.println("事務已提交...");return result;}/*使用pointcut管理切面表達式1、在一個切面類中,若多個切面的切面表達式均為同一個,每次都要寫重復的代碼,此時就可以使用pointcut來管理切面表達式了2、定義格式:公共public無返回值無參數*/@Pointcut(value = "execution(* *.doStudent(..))")public void pointCut() {//空方法體}
}