AOP: 面向切面編程,是一種編程思想,是對面向對象的一種補充.java
:面向對象是靜態角度考慮程序結構而面向切面是動態的考慮程序運行過程.spring
:面向切面編程是將交叉業務封裝成切面,利用AOP容器功能將切面織入到主業務邏輯express
Spring AOP是Spring框架中的一部分,但能夠做爲一個獨立的模塊單獨存在。Spring AOP實現AOP技術從本質上來說,是利用了JDK提供的動態代理技術。而從實際的實現方式來看,則是利用了IoC(Inversion of Control,反轉模式)機制,同時採用了AOP聯盟(AOP Alliance)的通用AOP接口。首先,Spring AOP經過xml配置文件配置了pointcut,並利用Interceptor(攔截機)做爲設定的觸發條件。Interceptor是由用戶自定義的,它至關因而AOP中的advice,但該Interceptor須要實現AOP聯盟的通用AOP接口,例如org.aopalliance.intercept.MethodInterceptor。最後定義一個Spring AOP ProxyFactory用於加載執行AOP組件,並利用IoC機制將advice注入到接口以及實現類中。編程
通知(advice): Spring 提供的一種切面,功能簡單,只能講切面織入到目標類的全部目標方法中而沒法織入到指定目標方法中app
/*AOP 小Demo*/ /*業務接口*/ public interface IService { public void doSome(); public void doOther(); } /*業務實現類*/ public class ServiceImpl implements IService{ @Override public void doSome() { System.out.println("doSome 業務方法!"); } @Override public void doOther() { System.out.println("doOther 業務方法"); } } /*通知類: 前置通知*/ public class MyMethodAdvice implements MethodBeforeAdvice{ public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable { System.out.println("before method ......"); } } /*測試類*/ public class MyTest { @Test public void test01(){ String config = "com/xiehe/dao/applicationSpring.xml"; //1.獲取容器 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config); //2.從容器中獲取代理對象 IService service= (IService) context.getBean("myServiceProxy"); service.doSome(); service.doOther(); } /*spring 配置文件: applicationSpring.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:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- bean definitions here --> <!-- 配置目標對象 --> <bean id="myService" class="com.xiehe.dao.ServiceImpl"></bean> <!-- 配置切面:通知 --> <bean id="myMethodAdvice" class="com.xiehe.dao.MyMethodAdvice"></bean> <!-- 配置代理: 此處使用了jdk 動態代理 --> <bean id="myServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- 目標對象 --> <property name="target" ref="myService" /> <!-- 接口 --> <property name="interfaces" value="com.xiehe.dao.IService" /> <!-- 切面 --> <property name="interceptorNames" value="myMethodAdvice" /> </bean> </beans>
顧問(advisor):Spring 提供的另外一種切面,能夠完成複雜的切面織入功能(顧問 + 切入點)框架
在上例的spring 配置文件中配置顧問ide
<?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- bean definitions here --> <!-- 配置目標對象 --> <bean id="myService" class="com.xiehe.dao8.ServiceImpl"></bean> <!-- 配置切面:通知 --> <bean id="myMethodAdvice" class="com.xiehe.dao8.MyMethodAdvice"></bean> <bean id="myMethodAdvice2" class="com.xiehe.dao8.MyMethodAdvice2"></bean> <!-- 配置顧問(通知 + 切入點):名稱匹配 --> <bean id="myAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor"> <!-- 通知--> <property name="advice" ref="myMethodAdvice" /> <!-- 切入點--> <property name="mappedNames" value="doSome,doOther"/> </bean> <!-- 配置代理 : CGLIB --> <bean id="myServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="myService" /> <property name="interfaces" value="com.xiehe.dao8.IService" /> <!-- 配置顧問-- > <property name="interceptorNames" value="myAdvisor"/> <!-- 指定優化的值爲true: 強制使用CGLIB --> <property name="optimize" value="true"/> </bean> </beans>
上例中,代理工廠bean(ProxyFactoryBean)一次只能爲一個目標對象代理,Spring中提供自動代理來爲全部的目標自動生成代理:測試
自動代理生成器均繼承於bean 後處理器,BeanPostProcessor優化
1.DefaultAdvisorAutoProxyCreator (默認advisor自動代理生成器)this
<?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- bean definitions here --> <!-- 配置目標對象 --> <bean id="myService" class="com.xiehe.dao10.ServiceImpl"></bean> <bean id="myService2" class="com.xiehe.dao10.ServiceImpl"></bean> <!-- 配置切面:通知 --> <bean id="myMethodAdvice" class="com.xiehe.dao10.MyMethodAdvice"></bean> <bean id="myMethodAdvice2" class="com.xiehe.dao10.MyMethodAdvice2"></bean> <!-- 配置顧問:名稱匹配 --> <bean id="myAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor"> <property name="advice" ref="myMethodAdvice" /> <property name="mappedNames" value="doOther,doSome"/> </bean> <!-- 配置代理 --> <!-- 默認advisor配置自動代理 --> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/> </beans>
默認advisor自動代理生成器 會給每個目標對象生成代理,靈活性較差
BeanNameAutoProxyCreator(Bean名稱自動代理生成器):能夠指定給某一個或多個目標對象生成代理,靈活性好
<?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- bean definitions here --> <!-- 配置目標對象 --> <bean id="myService" class="com.xiehe.dao10.ServiceImpl"></bean> <bean id="myService2" class="com.xiehe.dao10.ServiceImpl"></bean> <!-- 配置切面:通知 --> <bean id="myMethodAdvice" class="com.xiehe.dao10.MyMethodAdvice"></bean> <bean id="myMethodAdvice2" class="com.xiehe.dao10.MyMethodAdvice2"></bean> <!-- 配置顧問:名稱匹配 --> <bean id="myAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor"> <property name="advice" ref="myMethodAdvice" /> <property name="mappedNames" value="doOther,doSome"/> </bean> <!-- 配置代理 --> <!-- 名稱自動代理生成器 --> <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="beanNames" value="myService2"/> <property name="interceptorNames" value="myMethodAdvice,myMethodAdvice2"/> </bean> </beans>
因爲 AspectJ 是 Java 語言語法和語義的擴展,因此它提供了本身的一套處理方面的關鍵字。除了包含字段和方法以外,AspectJ 的方面聲明還包含pointcut和advice成員。示例中的pointcut使用了修飾符(modifier)和通配符(wildcard)模式來表達「全部公共方法」。對賬戶的訪問,由 pointcut 參數提供。advice使用這個參數,而pointcut則用 this(account) 把它綁定。這樣作的效果,就是捕獲了正在執行的方法所隸屬的Account對象。不然,advice的主體與方法的主體類似。advice能夠包含認證代碼,或者就像在這個示例中同樣,能夠調用其餘方法。
AspectJ 基於註解的AOP 實現
/*上例中接口和實現類不變*/ /*自定義java類,使用@Aspect 註解表示這是一個切面類*/ @Aspect public class MyAspect { /*在切面類中,爲每個方法指定切入點*/ @Before(value="execution(* com.xiehe.dao11.IService.doSome(..))") public void beforeSome(){ System.out.println("前置加強!"); } @AfterReturning("execution(* com.xiehe.dao11.IService.doSome(..))") public void afterReturn(){ System.out.println("後置加強!"); } } /*spring配置文件*/ <?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"> <!-- bean definitions here --> <!-- 配置目標bean --> <bean id="myService" class="com.xiehe.dao11.ServiceImpl"/> <!-- 配置切面 --> <bean id="myAspect" class="com.xiehe.dao11.MyAspect"/> <!-- 配置aspectj自動代理 --> <aop:aspectj-autoproxy/> </beans>
AspectJ 基於xml的AOP 實現
/*在定義類中,不在須要指定切點和切面註解*/ public class MyAspect { public void beforeSome(JoinPoint jp){ System.out.println("前置加強!"); } public void afterReturn(Object result){ System.out.println("後置加強 執行結果:" +result); } public Object around(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("換繞前:"); Object result =pjp.proceed(); System.out.println("環繞後:"); return result; } public void afterThrows(Throwable ex){ System.out.println("異常通知:"+ex.getMessage()); } public void after(){ System.out.println("最終方法!"); } public void myAspect(){} } /*Spring配置文件:applicationSpring.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"> <!-- bean definitions here --> <!-- 配置目標bean --> <bean id="myService" class="com.xiehe.dao13.ServiceImpl"/> <!-- 配置切面 --> <bean id="myAspect" class="com.xiehe.dao13.MyAspect"/> <!-- 配置AOP --> <aop:config> <!-- 定義切入點 --> <aop:pointcut expression="execution(* *..IService.doSome(..))" id="doSomePointcut"/> <aop:pointcut expression="execution(* com.xiehe.dao13.IService.doOther(..))" id="doOtherPointcut"/> <aop:pointcut expression="execution(* com.xiehe.dao13.IService.doThrid(..))" id="doThridPointcut"/> <!-- 定義切面 --> <aop:aspect ref="myAspect"> <aop:before method="beforeSome" pointcut-ref="doSomePointcut"/> <aop:after-returning method="afterReturn" pointcut-ref="doOtherPointcut" returning="result"/> <aop:around method="around(org.aspectj.lang.ProceedingJoinPoint)" pointcut-ref="doThridPointcut"/> </aop:aspect> </aop:config> </beans>