一 學習大綱
? ? ? ?1.? 動態代理設計模式(JDK和cglib)
? ? ? ?2.? AOP詳解
? ? ? ?3.? AOP中幾種通知類型
? ? ? ?4.兩種實現方式(Schema-base和Aspectj)
二 知識點詳解
? ? ? ?1. AOP:中文名稱 面向切面編程
? ? ? ?2 英文全稱:Aspect Object Programming
? ? ? ?3 正常程序執行流程都行縱向執行流程,spring Aop 又叫面向切面編程,在原有的縱向執行流程中添加橫切面
? ? ? ?3.1 不需要修改原有程序代碼,優點:1.高擴展性,2. 原有功能相當于釋放部分邏輯,讓職務更加明確。
4.面向切面編程是什么?
? ? ? 4.1在程序原有的縱向流程執行中,針對某一個或者某一些方法添加通知,形成橫切面向過程就叫做面向切面編程。
5.常用概念
? ? ? 5.1 原有功能:切點,pointcut
? ? ? 5.2 前置通知:在切點之前執行的功能.before advice
? ? ? 5.3 后置通知:在切點之后執行的功能,after advice
? ? ? 5.4 在切點執行過程中出現的異常,會觸發異常通知 throws advice
? ? ? 5.5 所有功能總稱叫做切面
? ? ? 5.6 織入:把切面嵌入到原有功能的過程叫做織入
6 spring提供了2種Aop實現方式
? ? ?6.1 Schema-based
? ? ? ? ? ?6.1.1 每個通知都需要實現接口或類
? ? ? ? ? ?6.1.2 配置spring配置文件時在 <aop:config> 配置
? ? 6.2 AspectJ
? ? ? ? ? 6.2.1 每個通知都不需要實現接口或類
? ? ? ? ?6.2.2 配置 spring 配置文件是在 <aop:config>的子標簽 <aop:aspect> 中配置的
二 Schema-based 實現步驟
1. 導入jar包
2. 新建通知類
2.1 新建前置通知類,三個參數,agr0:切點方法對象Method 對象。
agr1:切點方法參數,arg2:切點在哪個對象中
public class MyBeforeAdvice implements MethodBeforeAdvice{
? ? ?@Overload
? ? public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable{
? ? ? ? System.out.println("執行前置通知");
????}
}
2.2 新建后置通知類,agr0:切點方法返回值, arg1 切點方法對象,arg2 切點方法參數,arg3 切點方法所在類的對象。
public class MyAfterAdvice implements AfterReturningAdvice{
? ? ?@Overload ? ?
????public void afterReturning(Object arg0,Method arg,1, Object[] arg2, Object arg3) throws Throwable{
? ? ? ? System.out.println("執行后置通知"); ???
?????}
}?
3 配置spring 配置文件
引入 aop 命名空間 ,配置通知類的<bean>? 配置切面
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/sc
hema/beans
http://www.springframework.org/schema/beans/spring-be
ans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.
xsd">
<!-- 配置通知類對象,在切面中引入 -->
<bean id="mybefore"? ?class="com.chen.advice.MyBeforeAdvice"></bean>
<bean id="myafter"? ?class="com.chen.advice.MyAfterAdvice"></bean>
<!-- 配置切面 -->
<aop:config>
<!-- 配置切點 -->
<aop:pointcut expression="execution(*? com.chen.test.Demo.demo2())"? id="mypoint"/>
<!-- 通知 -->
<aop:advisor advice-ref="mybefore"? pointcut-ref="mypoint"/>
<aop:advisor advice-ref="myafter"? ?pointcut-ref="mypoint"/>
</aop:config>
<!-- 配置 Demo 類,測試使用 ,ioc? 控制反轉特效,把對bean的引用 奪到spring 中去-->
<bean id="demo" class="com.chen.test.Demo"></bean>
</beans>
測試類
public class Test {
????public static void main(String[] args) {
? ? /**
? ? ? ? *通過ioc特性,自己不用創建demo
????*/
?????// Demo demo = new Demo();
????????// demo.demo1();
????????// demo.demo2();
????????// demo.demo3();
????????ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
????????Demo demo = ac.getBean("demo",Demo.class);
????????demo.demo1();
????????demo.demo2();
????????demo.demo3();
????}
}
執行結果
三? 配置異常通知的步驟(AspectJ方式)
1. 只有切點報異常才能出發異常通知,切點也就是 Demo
2. 在 spring 中 有AspectJ 方式提供了異常通知的方法,如果想用 schema-base 實現實現特點的要求自己編寫方法
3. 實現步驟,新建類,在類寫任意名稱的方法
public class MyThrowAdvice{
????public void myexception(Exception e1){
????????System.out.println("執行異常通知"+e1.getMessage());
????}
}
3.1 在spring 配置文件中配置
? ? <aop:aspect> 的ref屬性表示:方法在哪個類中
? ? <aop:xxxx/> 表示什么通知
? ? method:當觸發這個通知時,調用哪個方法
? ? throwing:異常對象名,必須和通知中方法參數名相同(可以不在通知聲明異常對象)
applicationContext.xml
<bean id="mythrow"? class="com.chen.advice.MyThrowAdvice"></bean>
????<aop:config>
????????<aop:aspect ref="mythrow">
????????<aop:pointcut expression="execution(* com.chen.test.Demo.demo1())" id="mypoint"/>
????????<aop:after-throwing method="myexception"? pointcut-ref="mypoint" throwing="e1"/>
????????</aop:aspect>
????</aop:config>
<bean id="demo" class="com.chen.test.Demo"></bean>
四 異常通知(Schema-based方式)
1. 新建一個類,實現throwsAdvice接口,
必須自己寫方法,切必須叫afterThrowing
有兩個參數方式,必須是一個或者4個
異常類型要與切點報的異常類型一致
public class MyThrow implements ThrowsAdvice{
????public void afterThrowing(Exception ex) throws Throwable {
????????System.out.println("執行異常通過-schema-base 方式");
????}
}
在applicationContext.xml 配置
<bean id="mythrow"? class="com.chen.advice.MyThrow"></bean>
????<aop:config>
????????<aop:pointcut expression="execution(* com.chen.test.Demo.demo1())" id="mypoint"/>
????????<aop:advisor advice-ref="mythrow" pointcut-ref="mypoint" />
????</aop:config>
<bean id="demo" class="com.chen.test.Demo"></bean>
五 環繞通知(Schema-based 方式)
1. 把前置通知和后置通知都寫到了一個通知里,組成那個了環繞通知
2.實現步驟,新建一個類實現 MethodInterceptor
public class MyArround implements MethodInterceptor {
????@Override
????public Object invoke(MethodInvocation arg0) throws Throwable {
????????System.out.println("環繞-前置");
????????Object result = arg0.proceed();//放行,調用切點方式
????????System.out.println("環繞-后置");
????????return result;
????}
}
配置 applicationContext.xml
<bean id="myarround" class="com.chen.advice.MyArround"></bean>
????<aop:config>
????<aop:pointcut expression="execution(* com.chen.test.Demo.demo1())" id="mypoint"/>
????<aop:advisor advice-ref="myarround" pointcut-ref="mypoint" />
????</aop:config>
<bean id="demo" class="com.chen.test.Demo"></bean>
六 使用AspectJ 方式實現
1. 新建類,不用實現
? ? 1.1 類中方法名任意
public class MyAdvice {
????public void mybefore(String name1,int age1){
???????System.out.println("前置"+name1 );
????}
????public void mybefore1(String name1){
????????System.out.println("前置:"+name1);
????}
????public void myaftering(){
????????System.out.println("后置 2");
????}
????public void myafter(){
????????System.out.println("后置 1");
????}
????public void mythrow(){
????????System.out.println("異常");
????}
????public Object myarround(ProceedingJoinPoint p) throws Throwable{
? ? ????System.out.println("執行環繞");
? ? ? ? System.out.println("環繞-前置");
? ? ? ? Object result = p.proceed();
????????System.out.println("環繞后置");
????????return result;
}
1.2 配置spring 配置文件
<aop: after> 后置通知,是否出現異常都執行
<aop:after-returning/> 后置通知,只有切點正確執行時執行,
<aop:after/> 和 <aop:after-returning/> 和 <aop: after-throwing/> 執行順序和配置順序相關
?execution() 括號不能擴上,args
中間使用 and 不能使用 && 由 spring 把 and 解析成 &&
args(名稱) 名稱自定義,順序和demo1(參數,參數)對應
<aop:before/> arg-name="名稱" 名稱來源于expression ="" 中args(),名稱必須一樣
args() 有幾個參數,arg-name 里面必須有幾個參數
arg-name="" 里面名稱必須和通知方法參數名對象
<aop:config>
????<aop:aspect ref="myadvice">
????????<aop:pointcut expression="execution(* com.chen.test.Demo.demo1(String,int)) and args(name1,age1)" id="mypoint"/>
????????<aop:pointcut expression="execution(* com.chen.test.Demo.demo1(String)) and args(name1)" id="mypoint1"/>
????????<aop:before method="mybefore" pointcut-ref="mypoint" arg-names="name1,age1"/>
????????<aop:before method="mybefore1" pointcut-ref="mypoint1" arg-names="name1"/>
????</aop:aspect>
</aop:config>
七 使用注解(基于 Aspect)
1. spring 不會自動去尋找注解,必須告訴 spring 哪些包下的類中可能有注解
1.1 引入xmlns:context
<context:component-scan base-package="com.chen.advice"></context:component-scan>
2. @Component
????相當于<bean/>? ?
????如果沒有參數,把類名首字母變小寫,相當于<bean id=""/>
? ? @Component("自定義名稱")
實現步驟
? ? 在spring配置文件中配置注解在哪些包中1.? 然后多個包? 用? ,? 隔開
? ? 在Demo 類中添加@Componet
? ? 在方法中添加@Pointcut("")定義切點
@Component
public class Demo {
????@Pointcut("execution(*com.chen.test.Demo.demo1())")
????public void demo1() throws Exception{
????????// int i = 5/0;
????????System.out.println("demo1");
????}
}
3.3 在通知類中配置
? ? @Component 類被 Spring管理
? ? @Aspect 相當于<aop:aspect/> 表示通知方法在當前類中
@Component
@Aspect
public class MyAdvice {
????@Before("com.chen.test.Demo.demo1()")
????public void mybefore(){
????????System.out.println("前置");
????}
????@After("com.chen.test.Demo.demo1()")
????public void myafter(){
????????System.out.println("后置通知");
????}
????@AfterThrowing("com.chen.test.Demo.demo1()")
? ? public void mythrow(){
????????System.out.println("異常通知");
????}
????@Around("com.chen.test.Demo.demo1()")
????public Object myarround(ProceedingJoinPoint p) throws Throwable{
????????System.out.println("環繞-前置");
????????Object result = p.proceed();
????????System.out.println("環繞-后置");
????return result;
????}
}
九 靜態代理設計模式
1.? 由代理對象代理所有真實對象的功能
????????自己編寫代理類
????????每個代理的功能需要單獨編寫
2靜態代理設計模式的缺點
????????當代理功能比較多時,代理類中方法需要寫很多
十 動態代理
1. 為了解決靜態代理頻繁編寫代理功能缺點
2 分類
? ? ? ? jdk 提供的
? ? ? ? cglib 動態代理
十一? JDK 動態代理
1. 和cglib 動態代理相比
? ? ? ? 優點:jdk自帶,不需要導入額外jar
? ? ? ? 缺點? : 真實對象必須實現接口,利用反射機制,效率不高
2. 使用JDK動態代理是可能出現下面異常
? ? ? ? ? ? 出現原因:希望把接口對象轉換為具體真實對象
異常: java.lang.ClassCastException
十二 cglib 動態代理
1.cglib優點
? ? ? ? 基于字節碼,生成真實對象的子類
? ? ? ? ????運行效率高于JDK動態代理
? ? ? ? 不需要實現接口
? 2 cgilb 缺點
? ? ? ? 非JDK功能,需要導入額外jar
3. 使用spring aop時,只要出現Proxy 和真實對象轉換異常
? ? ? ? 設置為true 使用cglib
? ? ? ? 設置為 false 使用 jdk(默認值)
<aop: aspectj-autoproxyproxy-target-class="true"></aop:aspectj-autoproxy>
?