AOP經常使用的實現方式有兩種,一種是採用聲明的方式來實現(基於XML),一種是採用註解的方式來實現(基於AspectJ)。java
首先複習下AOP中一些比較重要的概念:正則表達式
Joinpoint(鏈接點):程序執行時的某個特定的點,在Spring中就是某一個方法的執行 。
Pointcut(切點):說的通俗點,spring中AOP的切點就是指一些方法的集合,而這些方法是須要被加強、被代理的。通常都是按照必定的約定規則來表示的,如正則表達式等。切點是由一類鏈接點組成。
Advice(通知):仍是說的通俗點,就是在指定切點上要幹些什麼。
Advisor(通知器):其實就是切點和通知的結合 。spring
1、基於XML配置的Spring AOPexpress
採用聲明的方式實現(在XML文件中配置),大體步驟爲:配置文件中配置pointcut, 在java中用編寫實際的aspect 類, 針對對切入點進行相關的業務處理。springboot
業務接口:ide
package com.springboottime.time.service; public interface AdviceService { /*查找用戶*/ public String findUser(); /*添加用戶*/ public void addUser(); }
業務實現:測試
package com.springboottime.time.service.serviceImpl; import com.springboottime.time.service.AdviceService; import lombok.Data; @Data public class AdviceServiceImpl implements AdviceService { private String name; @Override public String findUser() { System.out.println("***************執行業務方法findUser,查找的用戶名字爲:"+name+"****************"); return name; } @Override public void addUser() { System.out.println("***************執行業務方法addUser****************"); throw new RuntimeException(); } }
切面類:spa
package com.springboottime.time.aop; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; public class AopAspect { /** * 前置通知:目標方法調用以前執行的代碼 * @param jp */ public void doBefore(JoinPoint jp){ System.out.println("===========執行前置通知============"); } /** * 後置返回通知:目標方法正常結束後執行的代碼 * 返回通知是能夠訪問到目標方法的返回值的 * @param jp * @param result */ public void doAfterReturning(JoinPoint jp,String result){ System.out.println("===========執行後置通知============"); System.out.println("返回值result==================="+result); } /** * 最終通知:目標方法調用以後執行的代碼(不管目標方法是否出現異常均執行) * 由於方法可能會出現異常,因此不能返回方法的返回值 * @param jp */ public void doAfter(JoinPoint jp){ System.out.println("===========執行最終通知============"); } /** * * 異常通知:目標方法拋出異常時執行的代碼 * 在目標方法執行的時候,若是拋出異常,當即進入此方法 * 能夠訪問到異常對象 * @param jp * @param ex */ public void doAfterThrowing(JoinPoint jp,Exception ex){ System.out.println("===========執行異常通知============"); } /** * 環繞通知:目標方法調用先後執行的代碼,能夠在方法調用先後完成自定義的行爲。 * 包圍一個鏈接點(join point)的通知。它會在切入點方法執行前執行同時方法結束也會執行對應的部分。 * 主要是調用proceed()方法來執行切入點方法,來做爲環繞通知先後方法的分水嶺。 * * 環繞通知相似於動態代理的全過程:ProceedingJoinPoint類型的參數能夠決定是否執行目標方法。 * 並且環繞通知必須有返回值,返回值即爲目標方法的返回值 * @param pjp * @return * @throws Throwable */ public Object doAround(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("======執行環繞通知開始========="); // 調用方法的參數 Object[] args = pjp.getArgs(); // 調用的方法名 String method = pjp.getSignature().getName(); // 獲取目標對象 Object target = pjp.getTarget(); // 執行完方法的返回值 // 調用proceed()方法,就會觸發切入點方法執行 Object result=pjp.proceed(); //若是調用pjp.proceed()執行業務方法的時候拋出異常,那麼下面的代碼將不會執行 System.out.println("輸出,方法名:" + method + ";目標對象:" + target + ";返回值:" + result); System.out.println("======執行環繞通知結束========="); return result; } }
Spring的AOP配置:spring-aop.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-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <!-- 聲明一個業務類 --> <bean id="userManager" class="com.springboottime.time.service.serviceImpl.AdviceServiceImpl"> <property name="name" value="lixiaoxi"></property> </bean> <!-- 聲明通知類 --> <bean id="aspectBean" class="com.springboottime.time.aop.AopAspect" /> <aop:config> <aop:aspect ref="aspectBean"> <aop:pointcut id="pointcut" expression="execution(* com.springboottime.time.service.serviceImpl.AdviceServiceImpl..*(..))"/> <aop:before method="doBefore" pointcut-ref="pointcut"/> <aop:after-returning method="doAfterReturning" pointcut-ref="pointcut" returning="result"/> <aop:after method="doAfter" pointcut-ref="pointcut" /> <aop:around method="doAround" pointcut-ref="pointcut"/> <aop:after-throwing method="doAfterThrowing" pointcut-ref="pointcut" throwing="ex"/> </aop:aspect> </aop:config> </beans>
springboot啓動類設置:注意要引入xml文件code
package com.springboottime.time; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.ImportResource; @ImportResource("classpath:spring-aop.xml") //@ImportResource("classpath:spring-aop-aspectJ.xml") @SpringBootApplication public class TimeApplication { public static void main(String[] args) { SpringApplication.run(TimeApplication.class, args); } }
測試類:
package com.springboottime.time; import com.springboottime.time.service.AdviceService; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest public class TimeApplicationTests { @Autowired private AdviceService adviceService; @Test public void contextLoads() { } @Test public void test(){ String user = adviceService.findUser(); System.out.println("<><><><><><><><><><><><><>"); adviceService.addUser(); } }
測試結果:
===========執行前置通知============ ======執行環繞通知開始========= ***************執行業務方法findUser,查找的用戶名字爲:lixiaoxi**************** 輸出,方法名:findUser;目標對象:AdviceServiceImpl(name=lixiaoxi);返回值:lixiaoxi ======執行環繞通知結束========= ===========執行最終通知============ ===========執行後置通知============ 返回值result===================lixiaoxi <><><><><><><><><><><><><> ===========執行前置通知============ ======執行環繞通知開始========= ***************執行業務方法addUser**************** ===========執行異常通知============ ===========執行最終通知============ java.lang.RuntimeException at com.springboottime.time.service.serviceImpl.AdviceServiceImpl.addUser(AdviceServiceImpl.java:20)
2、使用註解配置AOP
採用註解來作aop, 主要是將寫在spring 配置文件中的鏈接點寫到註解裏面。
業務接口和業務實現與上邊同樣,具體切面類以下:
package com.springboottime.time.aop; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; @Aspect public class AopAspectJ { /** * 必須爲final String類型的,註解裏要使用的變量只能是靜態常量類型的 */ public static final String EDP="execution(* com.springboottime.time.service.serviceImpl.AdviceServiceImpl..*(..))"; /** * 切面的前置方法 即方法執行前攔截到的方法 * 在目標方法執行以前的通知 * @param jp */ @Before(EDP) public void doBefore(JoinPoint jp){ System.out.println("=========AopAspectJ執行前置通知=========="); } /** * 在方法正常執行經過以後執行的通知叫作返回通知 * 能夠返回到方法的返回值 在註解後加入returning * @param jp * @param result */ @AfterReturning(value=EDP,returning="result") public void doAfterReturning(JoinPoint jp,String result){ System.out.println("===========AopAspectJ執行後置通知============"); } /** * 最終通知:目標方法調用以後執行的通知(不管目標方法是否出現異常均執行) * @param jp */ @After(value=EDP) public void doAfter(JoinPoint jp){ System.out.println("===========AopAspectJ執行最終通知============"); } /** * 在目標方法非正常執行完成, 拋出異常的時候會走此方法 * @param jp * @param ex */ @AfterThrowing(value=EDP,throwing="ex") public void doAfterThrowing(JoinPoint jp,Exception ex) { System.out.println("===========AopAspectJ執行異常通知============"); } /** * 環繞通知:目標方法調用先後執行的通知,能夠在方法調用先後完成自定義的行爲。 * @param pjp * @return * @throws Throwable */ @Around(EDP) public Object doAround(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("======AopAspectJ執行環繞通知開始========="); // 調用方法的參數 Object[] args = pjp.getArgs(); // 調用的方法名 String method = pjp.getSignature().getName(); // 獲取目標對象 Object target = pjp.getTarget(); // 執行完方法的返回值 // 調用proceed()方法,就會觸發切入點方法執行 Object result=pjp.proceed(); System.out.println("輸出,方法名:" + method + ";目標對象:" + target + ";返回值:" + result); System.out.println("======AopAspectJ執行環繞通知結束========="); return result; } }
spring的配置:spring-aop-aspectJ.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-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <!-- 聲明spring對@AspectJ的支持 --> <aop:aspectj-autoproxy/> <!-- 聲明一個業務類 --> <bean id="userManager" class="com.springboottime.time.service.serviceImpl.AdviceServiceImpl"> <property name="name" value="lixiaoxi"></property> </bean> <!-- 聲明通知類 --> <bean id="aspectBean" class="com.springboottime.time.aop.AopAspectJ" /> </beans>
springboot啓動類配置:
package com.springboottime.time; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.ImportResource; //@ImportResource("classpath:spring-aop.xml") @ImportResource("classpath:spring-aop-aspectJ.xml") @SpringBootApplication public class TimeApplication { public static void main(String[] args) { SpringApplication.run(TimeApplication.class, args); } }
測試結果:
======AopAspectJ執行環繞通知開始========= =========AopAspectJ執行前置通知========== ***************執行業務方法findUser,查找的用戶名字爲:lixiaoxi**************** 輸出,方法名:findUser;目標對象:AdviceServiceImpl(name=lixiaoxi);返回值:lixiaoxi ======AopAspectJ執行環繞通知結束========= ===========AopAspectJ執行最終通知============ ===========AopAspectJ執行後置通知============ <><><><><><><><><><><><><> ======AopAspectJ執行環繞通知開始========= =========AopAspectJ執行前置通知========== ***************執行業務方法addUser**************** ===========AopAspectJ執行最終通知============ ===========AopAspectJ執行異常通知============ java.lang.RuntimeException at com.springboottime.time.service.serviceImpl.AdviceServiceImpl.addUser(AdviceServiceImpl.java:20)