spring面向切面
(1)AOP概念:
面向切面程式設計,指擴充套件功能不修改原始碼,將功能程式碼從業務邏輯程式碼中分離出來。 主要功能:日誌記錄,效能統計,安全控制,事務處理,異常處理等等。
主要意圖:將日誌記錄,效能統計,安全控制,事務處理,異常處理等程式碼從業務邏輯程式碼中劃分出來,通過對這些行為的分離,我們希望可以將它們獨立到非指導業務邏輯的方法中,進而改變這些行為的時候不影響業務邏輯的程式碼。
AOP特點:
採用橫向抽取機制,取代了傳統縱向繼承體系重複性程式碼。
AOP底層實現:
AOP底層使用動態代理實現。包括兩種方式:
(1)使用JDK動態代理實現。
(2)使用cglib來實現
JDK動態代理實現: 只能對實現了介面的類生成代理,而不是針對類,該目標型別實現的介面都將被代理。原理是通過在執行期間建立一個介面的實現類來完成對目標物件的代理。 1. 定義一個實現介面InvocationHandler的類 2. 通過建構函式,注入被代理類 3. 實現invoke( Object proxy, Method method, Object[] args)方法 4. 在主函式中獲得被代理類的類載入器 5. 使用Proxy.newProxyInstance( )產生一個代理物件 6. 通過代理物件呼叫各種方法 cglib動態代理實現: 針對類實現代理,對是否實現介面無要求。原理是對指定的類生成一個子類,覆蓋其中的方法,因為是繼承,所以被代理的類或方法最好不要宣告為final型別。 1. 定義一個實現了MethodInterceptor介面的類 2. 實現其intercept()方法,在其中呼叫proxy.invokeSuper( )
代理方式的選擇:
- 如果目標物件實現了介面,預設情況下回採用JDK的動態代理實現AOP,也可以強制使用cglib實現AOP
- 如果目標物件沒有實現介面,必須採用cglib庫,Spring會自動在JDK動態代理和cglib之間轉換
AOP操作術語:
Joinpoint(連線點): 類裡面可以被增強的方法,這些方法稱為連線點
Pointcut(切入點):所謂切入點是指我們要對哪些Joinpoint進行攔截的定義.
Advice(通知/增強):所謂通知是指攔截到Joinpoint之後所要做的事情就是通知.通知分為前置通知,後置通知,異常通知,最終通知,環繞通知(切面要完成的功能)
Aspect(切面): 是切入點和通知(引介)的結合Introduction(引介):引介是一種特殊的通知在不修改類程式碼的前提下, Introduction可以在執行期為類動態地新增一些方法或Field.
Target(目標物件):代理的目標物件(要增強的類)Weaving(織入):是把增強應用到目標的過程,把advice 應用到 target的過程
Proxy(代理):一個類被AOP織入增強後,就產生一個結果代理類
**切入點:**在類裡邊可以有很多方法被增強,比如實際操作中,只是增強了個別方法,則定義實際被增強的某個方法為切入點。
**通知/增強:**增強的邏輯,稱為增強,比如擴充套件日誌功能,這個日誌功能稱為增強。包括:
前置通知:在方法之前執行
後置通知:在方法之後執行
異常通知:方法出現異常執行
最終通知:在後置之後執行
環繞通知:在方法之前和之後執行
切面:把增強應用到具體方法上面的過程稱為切面。
使用表示式配置切入點:常用的表示式execution(<訪問修飾符>?<返回型別><方法名>(<引數>)<異常>)
(1)execution(* cn.itcast.aop.Book.add(…))
(2)execution(* cn.itcast.aop.Book.(…))
(3)execution(
(4) 匹配所有save開頭的方法 execution(* save*(…))
//------------------------------//
1.首先定義一個藉口UserDao
package com.offcn.dao;
public interface UserDao {
public void add();
}
//------------------------------------//
2.然後實現一個類UserDaoImpl ,相當於一個目標類
package com.offcn.dao.impl;
import com.offcn.dao.UserDao;
public class UserDaoImpl implements UserDao {
@Override
public void add() {
System.out.println(“增加成功”);
// throw new RuntimeException(“出錯了!”);
}
}
//---------------------------------------------------------------//
3.
package com.offcn.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import java.util.Arrays;
//定義切面類AopError–相當於一個代理
public class AopError {
/JoinPoint 這個類所有動態代理類的資訊/
public void affterError(JoinPoint joinPoint, RuntimeException e){
/joinPoint.getSignature() 這個方法是拿到你的動態代理的簽名檔案/
System.out.println(joinPoint.getSignature().getName()+“這個方法異常資訊”+e.getMessage());
}
public void after(JoinPoint joinPoint){
System.out.println(“後置通知”);
}
/*環繞增強 傳遞的是其JoinPoint子類*/
public void around(ProceedingJoinPoint proceedingJoinPoint){
System.out.println(proceedingJoinPoint.getTarget()+ Arrays.toString(proceedingJoinPoint.getArgs()));
/*呼叫此方法執行目標物件相應的方法*//*這個方法又相當於一個分割線,也就是前置與後置的分割線*/
try {
proceedingJoinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println(proceedingJoinPoint.getSignature().getName());
}
public void before(){
System.out.println("前置通知");
}
}
//-----------------------------------//核心配置檔案:
<?xml version="1.0" encoding="UTF-8"?><!--設定dao層-->
<bean id="userDao" class="com.offcn.dao.impl.UserDaoImpl"></bean>
<!--設定Service層-->
<bean id="userService" class="com.offcn.service.impl.UserServiceDaoImpl">
<property name="userDao" ref="userDao"></property>
</bean>
<!--設定增強aop-->
<bean id="aopError" class="com.offcn.aop.AopError"></bean>
<aop:config>
<!--對那一方法或者哪一個類進行植入:切入點--><!--expression 每個引數對哪一個類進行注入,這句話execution代表所有方法,*代表全部方法-->
<aop:pointcut id="pointcut" expression="execution(* com.offcn.service.UserService.*(..))"></aop:pointcut>
<!--引入方法發增強bean 切面-->
<aop:aspect ref="aopError">
<!--在這個裡面可以配置--><!--這個方法必須對應引入增強bean的方法,在這裡面要設定切點-->
<!--<aop:after-throwing method="affterError" pointcut-ref="pointcut" throwing="e"></aop:after-throwing>-->
<!--後置增強-->
<aop:after method="after" pointcut-ref="pointcut"></aop:after>
<!--前置增強-->
<aop:before method="before" pointcut-ref="pointcut"></aop:before>
<!--環繞增強,在前後都做相關操作,相當於一個前置和一個後者增強-->
<!--<aop:around method="around" pointcut-ref="pointcut"></aop:around>-->
</aop:aspect>
</aop:config>
測試類如下: package com.offcn.test;
import com.offcn.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class test {
public static void main(String[] args) {
ApplicationContext app = new ClassPathXmlApplicationContext(“applicationContext.xml”);
UserService userServiceDao = (UserService) app.getBean(“userService”);
userServiceDao.add();
}
}
console結果如下:
前置通知
增加成功了
後置通知[email protected]