javascript
Spring系列之AOP实现的两种方式
Spring只支持XML方式而沒有實現注解的方式(也叫AspectJ方式)的AOP,所以要使用@Aspect注解,只能引入AspectJ相關的 jar 包:
aopalliance-1.0.jar 和 aspectjweaver.jar
Spring的配置文件 applicationContext.xml 中引入context、aop對應的命名空間;
配置自動掃描的包,同時使切面類中相關方法中的注解生效,需自動地為匹配到的方法所在的類生成代理對象。
<?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.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"><!-- 配置自動掃描的包 --><context:component-scan base-package="com.qcc.beans.aop"></context:component-scan><!-- 自動為切面方法中匹配的方法所在的類生成代理對象。 --><aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>AOP常用的實現方式有兩種,
1、采用聲明的方式來實現(基于XML),
2、是采用注解的方式來實現(基于AspectJ)。
AOP中一些比較重要的概念
Joinpoint(連接點):程序執行時的某個特定的點,在Spring中就是某一個方法的執行 。
Pointcut(切點):說的通俗點,spring中AOP的切點就是指一些方法的集合,而這些方法是需要被增強、被代理的。一般都是按照一定的約定規則來表示的,如正則表達式等。切點是由一類連接點組成。?
Advice(通知):還是說的通俗點,就是在指定切點上要干些什么。?
Advisor(通知器):其實就是切點和通知的結合 。
注意:
1、環繞方法通知,環繞方法通知要注意必須給出調用之后的返回值,否則被代理的方法會停止調用并返回null,除非你真的打算這么做。 ? ? ? ? ??
2、只有環繞通知才可以使用JoinPoint的子類ProceedingJoinPoint,各連接點類型可以調用代理的方法,并獲取、改變返回值。
1、<aop:pointcut>如果位于<aop:aspect>元素中,則命名切點只能被當前<aop:aspect>內定義的元素訪問到。為了能被整個<aop:config>元素中定義的所有增強訪問,則必須在<aop:config>下定義切點。
2、如果在<aop:config>元素下直接定義<aop:pointcut>,必須保證<aop:pointcut>在<aop:aspect>之前定義。<aop:config>下還可以定義<aop:advisor>,三者在<aop:config>中的配置有先后順序的要求:首先必須是<aop:pointcut>,然后是<aop:advisor>,最后是<aop:aspect>。而在<aop:aspect>中定義的<aop:pointcut>則沒有先后順序的要求,可以在任何位置定義。<aop:pointcut>:用來定義切入點,該切入點可以重用;<aop:advisor>:用來定義只有一個通知和一個切入點的切面;<aop:aspect>:用來定義切面,該切面可以包含多個切入點和通知,而且標簽內部的通知和切入點定義是無序的;和advisor的區別就在此,advisor只包含一個通知和一個切入點。
3、在使用spring框架配置AOP的時候,不管是通過XML配置文件還是注解的方式都需要定義pointcut"切入點"。
例如定義切入點表達式 execution(* com.sample.service.impl..*.*(..))
execution()是最常用的切點函數,其語法如下所示:
整個表達式可以分為五個部分:
(1)、execution(): 表達式主體。
(2)、第一個*號:表示返回類型,*號表示所有的類型。
(3)、包名:表示需要攔截的包名,后面的兩個句點表示當前包和當前包的所有子包,com.sample.service.impl包、子孫包下所有類的方法。
(4)、第二個*號:表示類名,*號表示所有的類。
(5)、*(..):最后這個星號表示方法名,*號表示所有的方法,后面括弧里面表示方法的參數,兩個句點表示任何參數。
一、基于XML配置的Spring AOP
采用聲明的方式實現(在XML文件中配置),大致步驟為:配置文件中配置pointcut, 在java中用編寫實際的aspect 類, 針對對切入點進行相關的業務處理。
業務接口:
package com.spring.service; public interface IUserManagerService {//查找用戶public String findUser();//添加用戶public void addUser(); }業務實現:
package com.spring.service.impl; import com.spring.service.IUserManagerService; public class UserManagerServiceImpl implements IUserManagerService{ private String name;public void setName(String name){this.name=name;}public String getName(){return this.name;}public String findUser(){System.out.println("============執行業務方法findUser,查找的用戶是:"+name+"=============");return name;}public void addUser(){System.out.println("============執行業務方法addUser=============");//throw new RuntimeException();} }切面類:
package com.spring.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();System.out.println("輸出,方法名:" + method + ";目標對象:" + target + ";返回值:" + result);System.out.println("======執行環繞通知結束=========");return result;} }Spring配置:
<?xml version="1.0" encoding="UTF-8"?> <beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:p="http://www.springframework.org/schema/p"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsdhttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"><!-- 聲明一個業務類 --><bean id="userManager" class="com.spring.service.impl.UserManagerServiceImpl"><property name="name" value="lixiaoxi"></property></bean> <!-- 聲明通知類 --><bean id="aspectBean" class="com.spring.aop.AopAspect" /><aop:config><aop:aspect ref="aspectBean"><aop:pointcut id="pointcut" expression="execution(* com.spring.service.impl.UserManagerServiceImpl..*(..))"/><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>測試類:
package com.spring.test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.spring.service.IUserManagerService; public class TestAop {public static void main(String[] args) throws Exception{ApplicationContext act = new ClassPathXmlApplicationContext("applicationContext3.xml");IUserManagerService userManager = (IUserManagerService)act.getBean("userManager");userManager.findUser();System.out.println("\n");userManager.addUser();} }二、使用注解配置AOP
采用注解來做aop, 主要是將寫在spring 配置文件中的連接點寫到注解里面。
業務接口和業務實現與上邊一樣,具體切面類如下:
package com.spring.aop; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before;@Aspect public class AopAspectJ {/** * 必須為final String類型的,注解里要使用的變量只能是靜態常量類型的 */ public static final String EDP="execution(* com.spring.service.impl.UserManagerServiceImpl..*(..))";/*** 切面的前置方法 即方法執行前攔截到的方法* 在目標方法執行之前的通知* @param jp*/@Before(EDP)public void doBefore(JoinPoint jp){System.out.println("=========執行前置通知==========");}/*** 在方法正常執行通過之后執行的通知叫做返回通知* 可以返回到方法的返回值 在注解后加入returning * @param jp* @param result*/@AfterReturning(value=EDP,returning="result")public void doAfterReturning(JoinPoint jp,String result){System.out.println("===========執行后置通知============");}/*** 最終通知:目標方法調用之后執行的通知(無論目標方法是否出現異常均執行)* @param jp*/@After(value=EDP)public void doAfter(JoinPoint jp){System.out.println("===========執行最終通知============");}/*** 環繞通知:目標方法調用前后執行的通知,可以在方法調用前后完成自定義的行為。* @param pjp* @return* @throws Throwable*/@Around(EDP)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();System.out.println("輸出,方法名:" + method + ";目標對象:" + target + ";返回值:" + result);System.out.println("======執行環繞通知結束=========");return result;}/*** 在目標方法非正常執行完成, 拋出異常的時候會走此方法* @param jp* @param ex*/@AfterThrowing(value=EDP,throwing="ex")public void doAfterThrowing(JoinPoint jp,Exception ex) {System.out.println("===========執行異常通知============");} }spring的配置: <?xml version="1.0" encoding="UTF-8"?> <beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-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.spring.service.impl.UserManagerServiceImpl"> <property name="name" value="lixiaoxi"></property> </bean> <!-- 聲明通知類 --> <bean id="aspectBean" class="com.spring.aop.AopAspectJ" /> </beans> 測試類: package com.spring.test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.spring.service.IUserManagerService; public class TestAop1 { public static void main(String[] args) throws Exception{ ApplicationContext act = new ClassPathXmlApplicationContext("applicationContext4.xml"); IUserManagerService userManager = (IUserManagerService)act.getBean("userManager"); userManager.findUser(); System.out.println("\n"); userManager.addUser(); } }
轉載于:https://www.cnblogs.com/xyhero/p/60829d413d54b280ff2f143be19029b4.html
總結
以上是生活随笔為你收集整理的Spring系列之AOP实现的两种方式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 最小生成树学习-Kruskal算法
- 下一篇: 浅谈WebGIS开放数据(矢量数据)