我們一起了解 Spring 中的 AOP !
本文轉(zhuǎn)載自微信公眾號(hào)「程序員千羽」,作者程序員千羽 。轉(zhuǎn)載本文請(qǐng)聯(lián)系程序員千羽公眾號(hào)。
- 1. Spring AOP簡(jiǎn)介
- 2. 動(dòng)態(tài)代理
- jdk動(dòng)態(tài)代理
- CGLIB代理
- 3. 基于代理類的AOP實(shí)現(xiàn)
- Spring的通知類型
- ProxyFactoryBean
- 4. AspectJ開發(fā)
- 基于XML的聲明式AspectJ
- 基于注解的聲明式AspectJ(常用)
“GitHub:https://github.com/nateshao/ssm/tree/master/103-spring-aop
1. Spring AOP簡(jiǎn)介
什么是AOP?
AOP的全稱是Aspect-Oriented Programming,即面向切面編程(也稱面向方面編程)。它是面向?qū)ο缶幊?OOP)的一種補(bǔ)充,目前已成為一種比較成熟的編程方式。
在傳統(tǒng)的業(yè)務(wù)處理代碼中,通常都會(huì)進(jìn)行事務(wù)處理、日志記錄等操作。雖然使用OOP可以通過組合或者繼承的方式來達(dá)到代碼的重用,但如果要實(shí)現(xiàn)某個(gè)功能(如日志記錄),同樣的代碼仍然會(huì)分散到各個(gè)方法中。這樣,如果想要關(guān)閉某個(gè)功能,或者對(duì)其進(jìn)行修改,就必須要修改所有的相關(guān)方法。這不但增加了開發(fā)人員的工作量,而且提高了代碼的出錯(cuò)率。
為了解決這一問題,AOP思想隨之產(chǎn)生。AOP采取橫向抽取機(jī)制,將分散在各個(gè)方法中的重復(fù)代碼提取出來,然后在程序編譯或運(yùn)行時(shí),再將這些提取出來的代碼應(yīng)用到需要執(zhí)行的地方。這種采用橫向抽取機(jī)制的方式,采用傳統(tǒng)的OOP思想顯然是無(wú)法辦到的,因?yàn)镺OP只能實(shí)現(xiàn)父子關(guān)系的縱向的重用。雖然AOP是一種新的編程思想,但卻不是OOP的替代品,它只是OOP的延伸和補(bǔ)充。
類與切面的關(guān)系
AOP的使用,使開發(fā)人員在編寫業(yè)務(wù)邏輯時(shí)可以專心于核心業(yè)務(wù),而不用過多的關(guān)注于其他業(yè)務(wù)邏輯的實(shí)現(xiàn),這不但提高了開發(fā)效率,而且增強(qiáng)了代碼的可維護(hù)性。
Proxy(代理):將通知應(yīng)用到目標(biāo)對(duì)象之后,被動(dòng)態(tài)創(chuàng)建的對(duì)象。
Weaving(織入):將切面代碼插入到目標(biāo)對(duì)象上,從而生成代理對(duì)象的過程。
2. 動(dòng)態(tài)代理
jdk動(dòng)態(tài)代理
“JDK動(dòng)態(tài)代理是通過java.lang.reflect.Proxy 類來實(shí)現(xiàn)的,我們可以調(diào)用Proxy類的newProxyInstance()方法來創(chuàng)建代理對(duì)象。對(duì)于使用業(yè)務(wù)接口的類,Spring默認(rèn)會(huì)使用JDK動(dòng)態(tài)代理來實(shí)現(xiàn)AOP。
UserDao.java
- public interface UserDao {
- public void addUser();
- public void deleteUser();
- }
UserDaoImpl.java
- package com.nateshao.aop;
- import org.springframework.stereotype.Repository;
- /**
- * @date Created by 邵桐杰 on 2021/10/14 17:59
- * @微信公眾號(hào) 程序員千羽
- * @個(gè)人網(wǎng)站 www.nateshao.cn
- * @博客 https://nateshao.gitee.io
- * @GitHub https://github.com/nateshao
- * @Gitee https://gitee.com/nateshao
- * Description:
- */
- @Repository("userDao")
- public class UserDaoImpl implements UserDao{
- @Override
- public void addUser() {
- System.out.println("添加用戶");
- }
- @Override
- public void deleteUser() {
- System.out.println("刪除用戶");
- }
- }
JdkProxy.java
- package com.nateshao.aop;
- import com.nateshao.aspect.MyAspect;
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
- /**
- * @date Created by 邵桐杰 on 2021/10/14 18:01
- * @微信公眾號(hào) 程序員千羽
- * @個(gè)人網(wǎng)站 www.nateshao.cn
- * @博客 https://nateshao.gitee.io
- * @GitHub https://github.com/nateshao
- * @Gitee https://gitee.com/nateshao
- * Description: JDK代理類
- */
- public class JdkProxy implements InvocationHandler {
- // 聲明目標(biāo)類接口
- private UserDao userDao;
- // 創(chuàng)建代理方法
- public Object createProxy(UserDao userDao) {
- this.userDao = userDao;
- // 1.類加載器
- ClassLoader classLoader = JdkProxy.class.getClassLoader();
- // 2.被代理對(duì)象實(shí)現(xiàn)的所有接口
- Class[] clazz = userDao.getClass().getInterfaces();
- // 3.使用代理類,進(jìn)行增強(qiáng),返回的是代理后的對(duì)象
- return Proxy.newProxyInstance(classLoader,clazz,this);
- }
- /**
- * 所有動(dòng)態(tài)代理類的方法調(diào)用,都會(huì)交由invoke()方法去處理
- * @param proxy 被代理后的對(duì)象
- * @param method 將要被執(zhí)行的方法信息(反射)
- * @param args 執(zhí)行方法時(shí)需要的參數(shù)
- * @return
- * @throws Throwable
- */
- @Override
- public Object invoke(Object proxy, Method method, Object[] args)
- throws Throwable {
- // 聲明切面
- MyAspect myAspect = new MyAspect();
- // 前增強(qiáng)
- myAspect.check_Permissions();
- // 在目標(biāo)類上調(diào)用方法,并傳入?yún)?shù)
- Object obj = method.invoke(userDao, args);
- // 后增強(qiáng)
- myAspect.log();
- return obj;
- }
- }
CGLIB代理
通過前面的學(xué)習(xí)可知,JDK的動(dòng)態(tài)代理用起來非常簡(jiǎn)單,但它是有局限性的,使用動(dòng)態(tài)代理的對(duì)象必須實(shí)現(xiàn)一個(gè)或多個(gè)接口。
如果想代理沒有實(shí)現(xiàn)接口的類,那么可以使用CGLIB代理。
“CGLIB(Code Generation Library)是一個(gè)高性能開源的代碼生成包,它采用非常底層的字節(jié)碼技術(shù),對(duì)指定的目標(biāo)類生成一個(gè)子類,并對(duì)子類進(jìn)行增強(qiáng)。
UserDao.java
- public class UserDao {
- public void addUser(){
- System.out.println("添加用戶");
- }
- public void deleteUser(){
- System.out.println("添加用戶");
- }
- }
CglibProxy.java
- package com.nateshao.cglib;
- import com.nateshao.aspect.MyAspect;
- import org.springframework.cglib.proxy.Enhancer;
- import org.springframework.cglib.proxy.MethodInterceptor;
- import org.springframework.cglib.proxy.MethodProxy;
- import java.lang.reflect.Method;
- /**
- * @date Created by 邵桐杰 on 2021/10/14 18:18
- * @微信公眾號(hào) 程序員千羽
- * @個(gè)人網(wǎng)站 www.nateshao.cn
- * @博客 https://nateshao.gitee.io
- * @GitHub https://github.com/nateshao
- * @Gitee https://gitee.com/nateshao
- * Description:
- */
- // 代理類
- public class CglibProxy implements MethodInterceptor {
- // 代理方法
- public Object createProxy(Object target) {
- // 創(chuàng)建一個(gè)動(dòng)態(tài)類對(duì)象
- Enhancer enhancer = new Enhancer();
- // 確定需要增強(qiáng)的類,設(shè)置其父類
- enhancer.setSuperclass(target.getClass());
- // 添加回調(diào)函數(shù)
- enhancer.setCallback(this);
- // 返回創(chuàng)建的代理類
- return enhancer.create();
- }
- /**
- * @param proxy CGlib根據(jù)指定父類生成的代理對(duì)象
- * @param method 攔截的方法
- * @param args 攔截方法的參數(shù)數(shù)組
- * @param methodProxy 方法的代理對(duì)象,用于執(zhí)行父類的方法
- * @return
- * @throws Throwable
- */
- @Override
- public Object intercept(Object proxy, Method method, Object[] args,
- MethodProxy methodProxy) throws Throwable {
- // 創(chuàng)建切面類對(duì)象
- MyAspect myAspect = new MyAspect();
- // 前增強(qiáng)
- myAspect.check_Permissions();
- // 目標(biāo)方法執(zhí)行
- Object obj = methodProxy.invokeSuper(proxy, args);
- // 后增強(qiáng)
- myAspect.log();
- return obj;
- }
- }
CglibTest.java
- package com.nateshao.cglib;
- /**
- * @date Created by 邵桐杰 on 2021/10/14 18:25
- * @微信公眾號(hào) 程序員千羽
- * @個(gè)人網(wǎng)站 www.nateshao.cn
- * @博客 https://nateshao.gitee.io
- * @GitHub https://github.com/nateshao
- * @Gitee https://gitee.com/nateshao
- * Description:
- */
- public class CglibTest {
- public static void main(String[] args) {
- // 創(chuàng)建代理對(duì)象
- CglibProxy cglibProxy = new CglibProxy();
- // 創(chuàng)建目標(biāo)對(duì)象
- UserDao userDao = new UserDao();
- // 獲取增強(qiáng)后的目標(biāo)對(duì)象
- UserDao userDao1 = (UserDao)cglibProxy.createProxy(userDao);
- // 執(zhí)行方法
- userDao1.addUser();
- userDao1.deleteUser();
- }
- }
3. 基于代理類的AOP實(shí)現(xiàn)
Spring的通知類型
Spring按照通知在目標(biāo)類方法的連接點(diǎn)位置,可以分為5種類型,具體如下:
- org.springframework.aop.MethodBeforeAdvice(前置通知)
在目標(biāo)方法執(zhí)行前實(shí)施增強(qiáng),可以應(yīng)用于權(quán)限管理等功能。
- org.springframework.aop.AfterReturningAdvice(后置通知)
在目標(biāo)方法執(zhí)行后實(shí)施增強(qiáng),可以應(yīng)用于關(guān)閉流、上傳文件、刪除臨時(shí)文件等功能。
- org.aopalliance.intercept.MethodInterceptor(環(huán)繞通知)
在目標(biāo)方法執(zhí)行前后實(shí)施增強(qiáng),可以應(yīng)用于日志、事務(wù)管理等功能。
- org.springframework.aop.ThrowsAdvice(異常拋出通知)
在方法拋出異常后實(shí)施增強(qiáng),可以應(yīng)用于處理異常記錄日志等功能。
- org.springframework.aop.IntroductionInterceptor(引介通知)
在目標(biāo)類中添加一些新的方法和屬性,可以應(yīng)用于修改老版本程序。
ProxyFactoryBean
“ProxyFactoryBean是FactoryBean接口的實(shí)現(xiàn)類,F(xiàn)actoryBean負(fù)責(zé)實(shí)例化一個(gè)Bean,而ProxyFactoryBean負(fù)責(zé)為其他Bean創(chuàng)建代理實(shí)例。在Spring中,使用ProxyFactoryBean是創(chuàng)建AOP代理的基本方式。
ProxyFactoryBean類中的常用可配置屬性如下:
代碼實(shí)現(xiàn)
MyAspect.java
- package com.nateshao.factorybean;
- import org.aopalliance.intercept.MethodInterceptor;
- import org.aopalliance.intercept.MethodInvocation;
- /**
- * @date Created by 邵桐杰 on 2021/10/14 18:36
- * @微信公眾號(hào) 程序員千羽
- * @個(gè)人網(wǎng)站 www.nateshao.cn
- * @博客 https://nateshao.gitee.io
- * @GitHub https://github.com/nateshao
- * @Gitee https://gitee.com/nateshao
- * Description: 切面類
- */
- public class MyAspect implements MethodInterceptor {
- @Override
- public Object invoke(MethodInvocation mi) throws Throwable {
- check_Permissions();
- // 執(zhí)行目標(biāo)方法
- Object obj = mi.proceed();
- log();
- return obj;
- }
- public void check_Permissions(){
- System.out.println("模擬檢查權(quán)限...");
- }
- public void log(){
- System.out.println("模擬記錄日志...");
- }
- }
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"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
- <!-- 1 目標(biāo)類 -->
- <bean id="userDao" class="com.nateshao.jdk.UserDaoImpl" />
- <!-- 2 切面類 -->
- <bean id="myAspect" class="com.nateshao.factorybean.MyAspect" />
- <!-- 3 使用Spring代理工廠定義一個(gè)名稱為userDaoProxy的代理對(duì)象 -->
- <bean id="userDaoProxy"
- class="org.springframework.aop.framework.ProxyFactoryBean">
- <!-- 3.1 指定代理實(shí)現(xiàn)的接口-->
- <property name="proxyInterfaces"
- value="com.nateshao.jdk.UserDao" />
- <!-- 3.2 指定目標(biāo)對(duì)象 -->
- <property name="target" ref="userDao" />
- <!-- 3.3 指定切面,織入環(huán)繞通知 -->
- <property name="interceptorNames" value="myAspect" />
- <!-- 3.4 指定代理方式,true:使用cglib,false(默認(rèn)):使用jdk動(dòng)態(tài)代理 -->
- <property name="proxyTargetClass" value="true" />
- </bean>
- </beans>
ProxyFactoryBeanTest.java
- package com.nateshao.factorybean;
- import com.nateshao.jdk.UserDao;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
- /**
- * @date Created by 邵桐杰 on 2021/10/14 18:41
- * @微信公眾號(hào) 程序員千羽
- * @個(gè)人網(wǎng)站 www.nateshao.cn
- * @博客 https://nateshao.gitee.io
- * @GitHub https://github.com/nateshao
- * @Gitee https://gitee.com/nateshao
- * Description: 測(cè)試類
- */
- public class ProxyFactoryBeanTest {
- public static void main(String args[]) {
- String xmlPath = "applicationContext.xml";
- ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
- // 從Spring容器獲得內(nèi)容
- UserDao userDao = (UserDao) applicationContext.getBean("userDaoProxy");
- // 執(zhí)行方法
- userDao.addUser();
- userDao.deleteUser();
- }
- }
4. AspectJ開發(fā)
“概述:AspectJ是一個(gè)基于Java語(yǔ)言的AOP框架,它提供了強(qiáng)大的AOP功能。Spring 2.0以后,Spring AOP引入了對(duì)AspectJ的支持,并允許直接使用AspectJ進(jìn)行編程,而Spring自身的AOP API也盡量與AspectJ保持一致。新版本的Spring框架,也建議使用AspectJ來開發(fā)AOP。使用AspectJ實(shí)現(xiàn)AOP有兩種方式:一種是基于XML的聲明式AspectJ,另一種是基于注解的聲明式AspectJ。
基于XML的聲明式AspectJ
“基于XML的聲明式AspectJ是指通過XML文件來定義切面、切入點(diǎn)及通知,所有的切面、切入點(diǎn)和通知都必須定義在< aop:config >元素內(nèi)。
< aop:config >元素及其子元素如下:
小提示:圖中灰色部分標(biāo)注的元素即為常用的配置元素
XML文件中常用元素的配置方式如下:
- <bean id="myAspect" class="com.nateshao.aspectj.xml.MyAspect" />
- <aop:config>
- <aop:aspect id="aspect" ref="myAspect">
- <aop:pointcut expression="execution(* com.nateshao.jdk.*.*(..))“ id="myPointCut" />
- <aop:before method="myBefore" pointcut-ref="myPointCut" />
- <aop:after-returning method="myAfterReturning“ pointcut-ref="myPointCut" returning="returnVal" />
- <aop:around method="myAround" pointcut-ref="myPointCut" />
- <aop:after-throwing method="myAfterThrowing“ pointcut-ref="myPointCut" throwing="e" />
- <aop:after method="myAfter" pointcut-ref="myPointCut" />
- </aop:aspect>
- </aop:config>
配置切面
“在Spring的配置文件中,配置切面使用的是< aop:aspect >元素,該元素會(huì)將一個(gè)已定義好的Spring Bean轉(zhuǎn)換成切面Bean,所以要在配置文件中先定義一個(gè)普通的Spring Bean。
配置< aop:aspect >元素時(shí),通常會(huì)指定id和ref兩個(gè)屬性。
id:用于定義該切面的唯一標(biāo)識(shí)名稱。 ref:用于引用普通的Spring Bean
配置切入點(diǎn)
“當(dāng)< aop:pointcut>元素作為< aop:config>元素的子元素定義時(shí),表示該切入點(diǎn)是全局切入點(diǎn),它可被多個(gè)切面所共享;當(dāng)< aop:pointcut>元素作為< aop:aspect>元素的子元素時(shí),表示該切入點(diǎn)只對(duì)當(dāng)前切面有效。
在定義< aop:pointcut>元素時(shí),通常會(huì)指定id和expression兩個(gè)屬性。
id:用于指定切入點(diǎn)的唯-標(biāo)識(shí)名稱。. expressione:用于指定切入點(diǎn)關(guān)聯(lián)的切入點(diǎn)表達(dá)式
切入點(diǎn)表達(dá)式
- execution(* com.nateshao.jdk. * . * (..)) 是定義的切入點(diǎn)表達(dá)式,該切入點(diǎn)表達(dá)式的意思是匹配com.nateshao.jdk包中任意類的任意方法的執(zhí)行。
- execution(* com.nateshao.jdk..(..)) :表達(dá)式的主體
- execution(* :* 表示所有返回類型
- com.nateshao.jdk:需要攔截的包名字
- execution(* com.nateshao.jdk. * :* 代表所有類
- execution(* com.nateshao.jdk. * . * :方法名,使用* 代表所有方法
- execution(* com.nateshao.jdk..(..)) :. . 表示任意參數(shù)
配置通知
“使用< aop:aspect>的子元素可以配置5種常用通知,這5個(gè)子元素不支持使用子元素,但在使用時(shí)可以指定一些屬性,其常用屬性及其描述如下:
MyAspect.java
- package com.nateshao.aspectj.xml;
- import org.aspectj.lang.JoinPoint;
- import org.aspectj.lang.ProceedingJoinPoint;
- /**
- * @date Created by 邵桐杰 on 2021/10/14 19:56
- * @微信公眾號(hào) 程序員千羽
- * @個(gè)人網(wǎng)站 www.nateshao.cn
- * @博客 https://nateshao.gitee.io
- * @GitHub https://github.com/nateshao
- * @Gitee https://gitee.com/nateshao
- * Description: 切面類,在此類中編寫通知
- */
- public class MyAspect {
- // 前置通知
- public void myBefore(JoinPoint joinPoint) {
- System.out.print("前置通知 :模擬執(zhí)行權(quán)限檢查...,");
- System.out.print("目標(biāo)類是:"+joinPoint.getTarget() );
- System.out.println(",被織入增強(qiáng)處理的目標(biāo)方法為:"
- +joinPoint.getSignature().getName());
- }
- // 后置通知
- public void myAfterReturning(JoinPoint joinPoint) {
- System.out.print("后置通知:模擬記錄日志...," );
- System.out.println("被織入增強(qiáng)處理的目標(biāo)方法為:"
- + joinPoint.getSignature().getName());
- }
- /**
- * 環(huán)繞通知
- * ProceedingJoinPoint 是JoinPoint子接口,表示可以執(zhí)行目標(biāo)方法
- * 1.必須是Object類型的返回值
- * 2.必須接收一個(gè)參數(shù),類型為ProceedingJoinPoint
- * 3.必須throws Throwable
- */
- public Object myAround(ProceedingJoinPoint proceedingJoinPoint)
- throws Throwable {
- // 開始
- System.out.println("環(huán)繞開始:執(zhí)行目標(biāo)方法之前,模擬開啟事務(wù)...");
- // 執(zhí)行當(dāng)前目標(biāo)方法
- Object obj = proceedingJoinPoint.proceed();
- // 結(jié)束
- System.out.println("環(huán)繞結(jié)束:執(zhí)行目標(biāo)方法之后,模擬關(guān)閉事務(wù)...");
- return obj;
- }
- // 異常通知
- public void myAfterThrowing(JoinPoint joinPoint, Throwable e) {
- System.out.println("異常通知:" + "出錯(cuò)了" + e.getMessage());
- }
- // 最終通知
- public void myAfter() {
- System.out.println("最終通知:模擬方法結(jié)束后的釋放資源...");
- }
- }
config.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/beans
- http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
- <!-- 1 目標(biāo)類 -->
- <bean id="userDao" class="com.nateshao.jdk.UserDaoImpl" />
- <!-- 2 切面類 -->
- <bean id="myAspect" class="com.nateshao.factorybean.MyAspect" />
- <!-- 3 使用Spring代理工廠定義一個(gè)名稱為userDaoProxy的代理對(duì)象 -->
- <bean id="userDaoProxy"
- class="org.springframework.aop.framework.ProxyFactoryBean">
- <!-- 3.1 指定代理實(shí)現(xiàn)的接口-->
- <property name="proxyInterfaces"
- value="com.nateshao.jdk.UserDao" />
- <!-- 3.2 指定目標(biāo)對(duì)象 -->
- <property name="target" ref="userDao" />
- <!-- 3.3 指定切面,織入環(huán)繞通知 -->
- <property name="interceptorNames" value="myAspect" />
- <!-- 3.4 指定代理方式,true:使用cglib,false(默認(rèn)):使用jdk動(dòng)態(tài)代理 -->
- <property name="proxyTargetClass" value="true" />
- </bean>
- </beans>
TestXmlAspectj.java
- package com.nateshao.aspectj.xml;
- import com.nateshao.jdk.UserDao;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
- /**
- * @date Created by 邵桐杰 on 2021/10/14 19:58
- * @微信公眾號(hào) 程序員千羽
- * @個(gè)人網(wǎng)站 www.nateshao.cn
- * @博客 https://nateshao.gitee.io
- * @GitHub https://github.com/nateshao
- * @Gitee https://gitee.com/nateshao
- * Description:
- */
- public class TestXmlAspectj {
- public static void main(String args[]) {
- String xmlPath =
- "config.xml";
- ApplicationContext applicationContext =
- new ClassPathXmlApplicationContext(xmlPath);
- // 1 從spring容器獲得內(nèi)容
- UserDao userDao = (UserDao) applicationContext.getBean("userDao");
- // 2 執(zhí)行方法
- userDao.addUser();
- }
- }
基于注解的聲明式AspectJ(常用)
AspectJ框架為AOP的實(shí)現(xiàn)提供了一套注解,用以取代Spring配置文件中為實(shí)現(xiàn)AOP功能所配置的臃腫代碼。AspectJ的注解及其描述如下所示:
MyAspect.java
- package com.nateshao.aspectj.annotation;
- import org.aspectj.lang.JoinPoint;
- import org.aspectj.lang.ProceedingJoinPoint;
- import org.aspectj.lang.annotation.*;
- import org.springframework.stereotype.Component;
- /**
- * @date Created by 邵桐杰 on 2021/10/14 20:06
- * @微信公眾號(hào) 程序員千羽
- * @個(gè)人網(wǎng)站 www.nateshao.cn
- * @博客 https://nateshao.gitee.io
- * @GitHub https://github.com/nateshao
- * @Gitee https://gitee.com/nateshao
- * Description: 切面類,在此類中編寫通知
- */
- @Aspect
- @Component
- public class MyAspect {
- // 定義切入點(diǎn)表達(dá)式
- @Pointcut("execution(* com.nateshao.jdk.*.*(..))")
- // 使用一個(gè)返回值為void、方法體為空的方法來命名切入點(diǎn)
- private void myPointCut(){}
- // 前置通知
- @Before("myPointCut()")
- public void myBefore(JoinPoint joinPoint) {
- System.out.print("前置通知 :模擬執(zhí)行權(quán)限檢查...,");
- System.out.print("目標(biāo)類是:"+joinPoint.getTarget() );
- System.out.println(",被織入增強(qiáng)處理的目標(biāo)方法為:"
- +joinPoint.getSignature().getName());
- }
- // 后置通知
- @AfterReturning(value="myPointCut()")
- public void myAfterReturning(JoinPoint joinPoint) {
- System.out.print("后置通知:模擬記錄日志...," );
- System.out.println("被織入增強(qiáng)處理的目標(biāo)方法為:"
- + joinPoint.getSignature().getName());
- }
- // 環(huán)繞通知
- @Around("myPointCut()")
- public Object myAround(ProceedingJoinPoint proceedingJoinPoint)
- throws Throwable {
- // 開始
- System.out.println("環(huán)繞開始:執(zhí)行目標(biāo)方法之前,模擬開啟事務(wù)...");
- // 執(zhí)行當(dāng)前目標(biāo)方法
- Object obj = proceedingJoinPoint.proceed();
- // 結(jié)束
- System.out.println("環(huán)繞結(jié)束:執(zhí)行目標(biāo)方法之后,模擬關(guān)閉事務(wù)...");
- return obj;
- }
- // 異常通知
- @AfterThrowing(value="myPointCut()",throwing="e")
- public void myAfterThrowing(JoinPoint joinPoint, Throwable e) {
- System.out.println("異常通知:" + "出錯(cuò)了" + e.getMessage());
- }
- // 最終通知
- @After("myPointCut()")
- public void myAfter() {
- System.out.println("最終通知:模擬方法結(jié)束后的釋放資源...");
- }
- }
annotation.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"
- xmlns:context="http://www.springframework.org/schema/context"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
- http://www.springframework.org/schema/aop
- http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
- http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context-4.3.xsd">
- <!-- 指定需要掃描的包,使注解生效 -->
- <context:component-scan base-package="com.nateshao" />
- <!-- 啟動(dòng)基于注解的聲明式AspectJ支持 -->
- <aop:aspectj-autoproxy />
- </beans>
TestAnnotationAspectj.java
- package com.nateshao.aspectj.annotation;
- import com.nateshao.jdk.UserDao;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
- /**
- * @date Created by 邵桐杰 on 2021/10/14 20:09
- * @微信公眾號(hào) 程序員千羽
- * @個(gè)人網(wǎng)站 www.nateshao.cn
- * @博客 https://nateshao.gitee.io
- * @GitHub https://github.com/nateshao
- * @Gitee https://gitee.com/nateshao
- * Description:
- */
- public class TestAnnotationAspectj {
- public static void main(String args[]) {
- String xmlPath = "annotation.xml";
- ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
- // 1 從spring容器獲得內(nèi)容
- UserDao userDao = (UserDao) applicationContext.getBean("userDao");
- // 2 執(zhí)行方法
- userDao.addUser();
- }
- }
總結(jié)
這篇文章主要講解了Spring框架中AOP的相關(guān)知識(shí)。
- 首先對(duì)AOP進(jìn)行了簡(jiǎn)單的介紹,
- 然后講解了Spring中的兩種動(dòng)態(tài)代理,
- 接下來講解了Spring中基于代理類的AOP實(shí)現(xiàn),
- 最后講解了如何使用AspectJ框架來進(jìn)行AOP開發(fā)。
通過本章的學(xué)習(xí),我們可以了解AOP的概念和作用,理解AOP中的相關(guān)常用術(shù)語(yǔ),熟悉Spring中兩種動(dòng)態(tài)代理方式的區(qū)別,并能夠掌握基于代理類和AspectJ框架的AOP開發(fā)方式。