繼續學習,方便自己復查記錄
①AOP簡介:
? ?面向切面編程(也叫面向方面編程):Aspect Oriented Programming(AOP)。
? ?Spring框架中的一個重要內容。。
? ?通過預編譯方式和運行期間動態代理實現在不修改源代碼的情況下給程序動態統一添加功能的一種技術。AOP是OOP(面向對象編程)的延續。
? ?作用: 可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高? 程序的可重用性,同時提高了開發的效率。
? ?使用場景:日志記錄,性能統計,安全控制,事務處理,異常處理等。
②AOP實例學習:幾種通知類型↓↓↓↓
1,前置通知;
2,后置通知;
3,環繞通知;
4,返回通知;
5,異常通知;
②-1首先看下不用AOP前的代碼寫法,
(作為測試,業務目的很簡單:在程序的開始和結束打印一點日志)
代碼如下:涉及4個文件
代碼1:配置類 bean.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"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="studentService" class="com.java1234.service.impl.StudentServiceImpl"></bean></beans>代碼2:StudentService類
package com.java1234.service;public interface StudentService {public void addStudent(String name);
}代碼3:StudentService實現類
package com.java1234.service.impl;import com.java1234.service.StudentService;public class StudentServiceImpl implements StudentService{@Overridepublic void addStudent(String name) {System.out.println("開始添加學生"+name);System.out.println("添加學生"+name);System.out.println("完成學生"+name+"的添加");}}代碼4:測試類
package com.java1234.test;import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;import com.java1234.service.StudentService;public class T {private ApplicationContext ac;@Beforepublic void setUp() throws Exception {ac=new ClassPathXmlApplicationContext("beans.xml");}@Testpublic void test1() {// 多態特性:父類的引用可以指向具體的實現。// 按照bean.xml的定義,本來 ac.getBean("studentService") 得到的是StudentServiceImpl,被安全的向下轉型為StudentService類型StudentService studentService=(StudentService)ac.getBean("studentService");//studentService.addStudent("張三");}}
②-2使用AOP類的變化:
a.增加切面通知類
b.引入相關jar包:aopalliance.jar,aspectjweaver-1.6.6.jar,spring-aspects-4.0.6.RELEASE.jar(版本號可能有差異)
c.在bean.xml中增加
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation??后面增加http://www.springframework.org/schema/aop
????http://www.springframework.org/schema/aop/spring-aop.xsd
加完上面這個就可以在bean.xml中使用aop功能了??
插入知識點1:bean.xml的命名空間的含義。可以跳過
xsi:schemaLocation 的通用結構為: ?命名空間URI1 XSD路徑1 命名空間URI2 XSD路徑2 ...
例子:(都是兩個兩個一起的)
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
?? ?
在給定的 xsi:schemaLocation 屬性中,每一對字符串分別由命名空間 URI 和 實際 XSD 文件路徑組成。具體解析如下:命名空間 URI
http://www.springframework.org/schema/beans
這是 Spring 框架中 beans 模塊的命名空間 URI,用于標識 XML 中相關元素的所屬規范。
http://www.springframework.org/schema/aop
這是 Spring AOP 模塊的命名空間 URI,用于標識 AOP 相關配置的命名空間。
實際 XSD 文件路徑
http://www.springframework.org/schema/beans/spring-beans.xsd
這是 beans 模塊對應的 XML Schema 定義文件(XSD)的實際網絡路徑。
http://www.springframework.org/schema/aop/spring-aop.xsd
這是 AOP 模塊對應的 XSD 文件路徑,定義了 AOP 配置的校驗規則。
插入知識點2:執行表達式execution
的理解,可以跳過
執行表達式
execution(* com.java1234.service.*.*(..))
的含義該表達式是 Spring AOP 中的切點(Pointcut)表達式,用于匹配特定方法的執行。以下是逐部分解析:
組成部分解析
execution
表示匹配方法執行的連接點(Join Point),是 AOP 中最常用的切點指示符。
*
(第一個星號)
匹配任意返回類型(如void
、String
、int
等)。
com.java1234.service.*
指定包路徑和類名:
com.java1234.service
是目標包名。.*
匹配該包下的任意類(但不包括子包中的類)。
*
(第三個星號)
匹配類中的任意方法名。
(..)
匹配任意參數列表(無論是否有參數,或參數類型是什么)。完整含義
該表達式匹配:
com.java1234.service
包下任意類中,所有返回類型、所有方法名、所有參數列表的方法。常見用法示例
@Aspect @Component public class ServiceAspect {@Before("execution(* com.java1234.service.*.*(..))")public void logBeforeServiceMethod(JoinPoint joinPoint) {System.out.println("Executing: " + joinPoint.getSignature());} }
擴展說明
- 若需匹配子包中的類,可使用
..
:(2個點)
execution(* com.java1234.service..*.*(..))
。- 若需限定方法名,可替換
*
為具體名稱(如get*
匹配以get
開頭的方法)。相關語法對比
// 匹配特定返回類型(String)的方法 execution(String com.java1234.service.*.*(..))// 匹配特定類(UserService)中的方法 execution(* com.java1234.service.UserService.*(..))// 匹配特定參數類型(需一個Long參數)的方法 execution(* com.java1234.service.*.*(Long))
d.在bean.xml中增加aop切面的定義? ,完整的bean.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/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 引入第三行 xmlns:aop="http://www.springframework.org/schema/aop" 和引入第7.8行 http://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd就可以使用AOP功能配置了 --><!-- 切面類是studentServiceAspect(這里有想添加的方法功能) --><bean id="studentServiceAspect" class="com.java1234.advice.StudentServiceAspect"></bean><bean id="studentService" class="com.java1234.service.impl.StudentServiceImpl"></bean><aop:config><!--aop:aspect 配置切面,關聯的切面類是studentServiceAspect(這里有想添加的方法功能) --><aop:aspect id="studentServiceAspectConfig" ref="studentServiceAspect"><!-- aop:pointcut 定義1個切點,spring的切點是定義到方法級的。execution是表達式,用于匹配特定方法的執行--><!-- execution(* com.java1234.service.*.*(..)) 用于匹配特定方法的執行 --><aop:pointcut expression="execution(* com.java1234.service.*.*(..))" id="businessService"/><!-- 開始定義各種通知 aop:before aop:after aop:around aop:after-returning aop:after-returning aop:after-throwing--><!-- 定義1,前置通知;方法執行前觸發. pointcut-ref="businessService" 表示引用的切點是businessService--><aop:before method="doBefore" pointcut-ref="businessService" /><!-- 定義2,后置通知;方法執行后觸發(無論是否拋出異常)。 --><!--aop:after method="doAfter" pointcut-ref="businessService"/--><!-- 定義3,環繞通知;包圍目標方法,需手動調用 proceed()。 --><aop:around method="doAround" pointcut-ref="businessService" /><!-- 定義4,返回通知; 方法正常返回后觸發。--><aop:after-returning method="doAfterReturning" pointcut-ref="businessService"/><!-- 定義5,異常通知 方法拋出異常時觸發。--><aop:after-throwing method="doAfterThrowing" pointcut-ref="businessService" throwing="ex"/></aop:aspect></aop:config>
</beans>
其他的代碼1,業務類:沒有了冗余的日志打印代碼,用aop實現了
package com.java1234.service.impl;import com.java1234.service.StudentService;public class StudentServiceImpl implements StudentService{@Overridepublic void addStudent(String name) {// System.out.println("開始添加學生"+name);System.out.println("業務邏輯開始::::添加學生"+name);// System.out.println(1/0);// System.out.println("完成學生"+name+"的添加");}}
?
?代碼2: 接口類(匹配上expression="execution(* com.java1234.service.*.*(..))"這里的路徑)
package com.java1234.service;public interface StudentService {public void addStudent(String name);
}
?代碼3:切面類(5種通知的具體內容)
package com.java1234.advice;import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;public class StudentServiceAspect {public void doBefore(JoinPoint jp){System.out.println("doBefore:類名:"+jp.getTarget().getClass().getName());System.out.println("doBefore:方法名:"+jp.getSignature().getName());System.out.println("doBefore:開始添加學生:"+jp.getArgs()[0]);}public void doAfter(JoinPoint jp){System.out.println("doAfter:類名:"+jp.getTarget().getClass().getName());System.out.println("doAfter:方法名:"+jp.getSignature().getName());System.out.println("doAfter:學生添加完成:"+jp.getArgs()[0]);}public Object doAround(ProceedingJoinPoint pjp) throws Throwable{ // 參數和其他通知不一樣 ProceedingJoinPointSystem.out.println("doAround:添加學生前");System.out.println("doAround:手動調用 proceed方法");Object retVal=pjp.proceed(); // 需手動調用 proceed()。//System.out.println(retVal);System.out.println("doAround:添加學生后");return retVal;}public void doAfterReturning(JoinPoint jp){System.out.println("doAfterReturning:返回通知");}public void doAfterThrowing(JoinPoint jp,Throwable ex){System.out.println("doAfterThrowing:異常通知");System.out.println("doAfterThrowing:異常信息:"+ex.getMessage());}
}
??代碼4:測試類,調用的main類
package com.java1234.test;import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;import com.java1234.service.StudentService;public class T {public static void main(String[] args) {ApplicationContext ac=new ClassPathXmlApplicationContext("beans.xml");StudentService studentService=(StudentService)ac.getBean("studentService");studentService.addStudent("張三");}}
?最后:關于5種通知:
1,前置通知;
2,后置通知;
3,環繞通知;
4,返回通知;
5,異常通知;
?
ps1:?第4種返回通知;第5種異常通知; 不會同時存在,只能同時返回1種
ps2. 如果同時存在時的執行順序:
在業務處理無異常時的執行順序:
1前置通知--》3環繞通知的前處理--》業務邏輯--》3環繞通知的后處理--》2后置通知--》4返回通知
在業務處理有異常時的執行順序:
1前置通知--》3環繞通知的前處理--》業務邏輯--》5異常通知