AOP是針對面向對象編程的一種補充,有時使用面向對象不能很好完成一些額外的功能業務時,可以采用AOP來進行補充。
AOP術語:
-
切面(Aspect)
切面是用於編寫切面邏輯的一個類,這個類很類似於JDK動態代理中的回調處理器或者cglib中的方法攔截器,主要就是將需要增強目標對象的功能代碼編寫在這個類中,而這些功能增強的代碼就是切面邏輯。
-
切入點(Pointcut)
通常切入點都是以一種表達式的形式來描述
-
通知/增強(Advice)
通知就是切面中具體的增強邏輯,總共分為五種:
1)前置通知(在目標方法調用之前執行)
2)后置通知(在目標方法正確返回之后執行)
3)環繞通知(在目標方法調用前后執行)
4)異常通知(當目標方法拋出異常時執行,並且不會執行后置通知)
5)最終通知(不管目標方法有無異常都會執行)
-
連接點(Joinpoint)
目標對象的方法就稱之為連接點,一個切入點可以對應目標對象的的多個連接點。
-
代理(Proxy)
在運行時動態創建的對象,稱之為代理對象,負責調用目標對象的方法,並執行增強功能
-
目標(Target)
被代理的對象就是目標對象
-
織入(Weaver)
將切面中的通知應用到目標對象上並且產生代理的過程稱之為織入。
因此通常描述為“將通知織入到具體的目標”。
項目結構:

代碼示例:
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/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"> <!-- 裝備UserServiceImpl --> <bean id="userService" class="edu.nf.ch11.service.impl.UserServiceImpl"/> <!-- 裝配自定義的切面--> <bean id="userServiceAspect" class="edu.nf.ch11.service.aspect.UserServiceAspect"/> <!-- 配置AOP, proxy-target-class用於設置是否強制使用cglib進行動態代理 true表示強制使用,false則表示spring會根據目標對象有無實現接口來決定 使用jdk動態代理還是cglib代理。默認值就是false--> <aop:config> <!-- 配置切入點,id屬性給切入點定義一個唯一標識,expression用於編寫切入點表達式--> <!-- execution表達式的使用:切入范圍是在方法級別--> <!-- 表達式語法[訪問修飾符] 返回值類型 [完整類名].方法名(參數)--> <!-- *號表示通配所有,方法的參數可以使用".."來代表任意個數和類型的參數--> <aop:pointcut id="myCut" expression="execution(* edu.nf.ch11.service.*.*(..))"/> <!-- within表達式的使用:切入范圍是在類級別--> <!--<aop:pointcut id="myCut" expression="within(edu.nf.ch11.service.impl.*)"/>--> <!-- 引用上面裝配的切面類的id --> <aop:aspect ref="userServiceAspect"> <!-- 配置具體的通知方法,method指定通知的方法名 pointcut-ref引用上面定的切入點的id,也可以通過pointcut來編寫相應的切入點表達式 --> <!-- 前置通知 --> <aop:before method="before" pointcut-ref="myCut"/> <!-- 環繞通知--> <aop:around method="around" pointcut-ref="myCut"/> <!-- 后置通知,returning指定后置通知的參數名,用於獲取目標方法的返回值--> <aop:after-returning method="afterReturn" pointcut-ref="myCut" returning="returnVal"/> <!-- 異常通知,如果要獲取目標方法拋出的異常對象,需要指定throwing屬性,value對應異常通知的參數名--> <aop:after-throwing method="throwAdvice" pointcut-ref="myCut" throwing="e"/> <!-- 最終通知--> <aop:after method="after" pointcut-ref="myCut"/> </aop:aspect> </aop:config> </beans>
切面(增強)類:
package edu.nf.ch11.service.aspect; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.reflect.MethodSignature; import javax.xml.crypto.dsig.SignatureMethod; /** * @author wangl * @date 2018/10/23 * 編寫一個UserService的切面 * 在切面中編寫的方法都稱之為通知或者是增強 * 在AOP當中,通知有五種類型 * 1. 前置通知 * 2. 后置通知 * 3. 環繞通知 * 4. 最終通知 * 5. 異常通知 * 在一個切面中,任何一種類型的通知都可以定義多個,當有多個通知存在的時候 * 就會形成一個通知棧 */ public class UserServiceAspect { /** * 前置通知 * 在執行目標方法之前執行,攔截目標方法的參數 * 可以通過JoinPoint獲得目標方法的參數信息 */ public void before(JoinPoint joinPoint){ System.out.println("前置通知..."); //通過連接點獲得目標對象 //joinPoint.getTarget(); //獲取目標方法的參數信息 Object[] params = joinPoint.getArgs(); for (Object param : params) { System.out.println(param); } } /** * 后置通知 * 在目標方法執行完並且return之后執行 */ public void afterReturn(String returnVal){ System.out.println("后置通知..."+returnVal); } /** * 環繞通知 * @param pjp 連接點處理器,由它負責調用目標對象的具體方法 * */ public Object around(ProceedingJoinPoint pjp) throws Throwable { System.out.println("環繞通知前..."); //調用目標對象的方法 Object returnVal = pjp.proceed(); //獲取目標方法的參數 System.out.println(pjp.getArgs()[0]); //通過MethodSignature獲取連接點方法信息 MethodSignature ms = (MethodSignature)pjp.getSignature(); //獲取正在調用的目標方法 System.out.println(ms.getMethod().getName()); //獲取目標方法的返回類型 System.out.println(ms.getReturnType()); System.out.println("環繞通知后..."); return returnVal; } /** * 異常通知 * @param e 獲取目標方法拋出的異常對象 */ public void throwAdvice(Throwable e){ System.out.println("異常通知..."+e.getMessage()); } /** * 最終通知 */ public void after(){ System.out.println("最終通知..."); } }
UserService接口:
package edu.nf.ch11.service; /** * @author wangl * @date 2018/10/23 */ public interface UserService { /** * 添加用戶 */ void addUser(); /** * 刪除用戶 * @param uid */ void deleteUser(String uid); /** * 查詢用戶 * @param uid * @return */ String getUserNameById(String uid); }
UserServiceImpl實現類:
package edu.nf.ch11.service.impl; import edu.nf.ch11.service.UserService; /** * @author wangl * @date 2018/10/23 * 目標對象(被代理的對象) * Spring的AOP中需要代理的所有目標對象都應該歸納在ioc容器中管理 */ public class UserServiceImpl implements UserService { @Override public void addUser() { System.out.println("保存用戶信息"); } @Override public void deleteUser(String uid) { System.out.println("刪除用戶,ID:" + uid); } @Override public String getUserNameById(String uid) { System.out.println("根據ID查詢用戶名"); //System.out.println(10/0); return "user1"; } }
程序測試類:
package edu.nf.ch11.test; import edu.nf.ch11.service.UserService; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * @author wangl * @date 2018/10/23 */ public class UserServiceAspectTest { @Test public void testAddUser(){ ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); //這里從容器中獲取的對象是一個代理對象 UserService service = context.getBean("userService", UserService.class); //service.addUser(); //System.out.println("----------------------"); //service.deleteUser("10001"); //System.out.println("----------------------"); service.getUserNameById("10001"); } }
運行結果

