Spring 面向切面程式設計
阿新 • • 發佈:2018-11-03
AOP 代理設定模式
概述:
- 所謂代理,就是一個人或某機構代表一個人或某機構採取行動。在一些情況下,客戶不想或不能直接引用一個物件,而代理物件可以在客戶端和目標物件之間起到中間的作用。
- 通俗講:
- 經紀人和藝人的關係,經紀人便是代理,藝人就是目標使用者,藝人不想做的(安裝裝置,舞臺等),由經紀人去聯絡處理,這樣的模式就是代理模式。
JDK: 靜態代理 動態代理 cglib 生成的動態代理
- 靜態代理
- 缺點:擴充套件性差、每次生成新的目標物件類,即使一樣,也要重新生成一個代理物件
- 優點:好理解,程式碼清晰,不復雜
AOP的原始碼中用到了兩種動態代理來實現攔截切入功能
- jdk動態代理和cglib動態代理。
- jdk動態代理是由java內部的反射機制來實現的,cglib動態代理底層則是藉助asm來實現的。
- 總的來說,反射機制在生成類的過程中比較高效,而asm在生成類之後的相關執行過程中比較高效(可以通過將asm生成的類進行快取,這樣解決asm生成類過程低效問題)。
定義一個介面
public interface Singer {
//唱歌
public void singing();
//跳舞
public void dancing();
}
實現介面的類
public class ZhangJie implements Singer{
@Override
public void singing() {
System.out.println("正在唱歌");
}
@Override
public void dancing() {
System.out.println("正在跳舞");
}
}
靜態代理
//首先要例項化的物件是張傑(也就是我們的目標物件)
Singer singer = new ZhangJie();//多型/向上轉型
//例項化代理物件來呼叫其方法(在呼叫目標物件前後做相關操作)
ProxyWang proxyWang = new ProxyWang(singer);
proxyWang.singing();
動態代理
//目標物件
Singer singer = new ZhangJie();
//jdk提供了這樣一個類,代表代理的意思
//第一個引數是類載入器
//第二個引數是目標物件的class
//第三個引數是代理物件
//返回值型別為Object
Singer s = (Singer) Proxy.newProxyInstance(singer.getClass().getClassLoader(),new Class[]{Singer.class},new ProxyAi(singer));
s.singing();
cglib 生成的動態代理
public class CglibProxy implements MethodInterceptor {
//例項化我們的目標物件
private Object obj;
public Object getObj(Object obj) {
this.obj = obj;
//得到建立代理物件的物件
Enhancer enhancer = new Enhancer();
//設定類載入器
enhancer.setClassLoader(this.obj.getClass().getClassLoader());
//設定其父類
enhancer.setSuperclass(this.obj.getClass());
//設定回撥,只要走下面這方法都會走回調
enhancer.setCallback(this);
//建立代理物件
return enhancer.create();
}
//這個方法就是在你待用目標物件的方法都會執行這個方法
//第一個引數是代理物件/第二個引數是目標物件的方法/第三個引數是目標物件的方法引數/第四個引數是代理物件的攔截方法
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object ob = null;
if (method.getName().equals("singing")){
System.out.println("裝置已經安裝成功");
ob = method.invoke(obj, objects);
System.out.println("唱歌結束");
}else if (method.getName().equals("dancing")) {
System.out.println("舞臺已經搭建成功,show time");
ob = method.invoke(obj, objects);
System.out.println("舞會結束");
}
return ob;
}
}
//例項化一個cglibProxy 代理物件類
CglibProxy cglibProxy = new CglibProxy();
Singer singer = new ZhangJie();
//這個引數代表我們的目標物件
Singer singer1 = (Singer) cglibProxy.getObj(singer);
singer1.dancing();
Spring AOP的配置元素
設定的步驟
spring 要配置的bean
<bean id="aopError" class="com.offcn.aop.AopError"></bean>
public class AopError {
public void afterError(JoinPoint joinPoint,RuntimeException e){
System.out.println(joinPoint.getSignature().getName()+"這個方法的異常資訊"+e.getMessage());
}
public void after(JoinPoint joinPoint){
System.out.println(joinPoint.getSignature().getName());
}
//環繞通知 傳遞的是 JoinPoint 的子類 ProceedingJoinPoint
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());
}
}
設定切面、切點、異常,前置,後置,環繞通知
<!-- 設定切面 -->
<aop:config>
<!-- 配置切點 -->
<aop:pointcut id="pointcut" expression="execution(* com.offcn.service.UserService.*(..))"></aop:pointcut>
<!-- 引入增強bean -->
<aop:aspect ref="aopError">
<!-- 配置異常後置增強,這個方法必須引入增強bean的方法, -->
<aop:after-throwing method="afterError" pointcut-ref="pointcut" throwing="e"></aop:after-throwing>
<!-- 後置增強 -->
<aop:after method="after" pointcut-ref="pointcut"></aop:after>
<!-- 前置增強 -->
<aop:before method="after" pointcut-ref="pointcut"></aop:before>
<!-- 環繞增強 -->
<aop:around method="around" pointcut-ref="pointcut"></aop:around>
</aop:aspect>
</aop:config>
測試 Test
public class Test {
@org.junit.Test
public void test(){
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) app.getBean("userService");
userService.add();
}
}
註解前置、後置、環繞、異常通知
@Aspect
public class AopError {
@AfterThrowing(pointcut = "execution(* com.offcn.service.UserService.*(..))",throwing = "e")
public void afterError(JoinPoint joinPoint, RuntimeException e){
System.out.println(joinPoint.getSignature().getName()+"這個方法的異常資訊"+e.getMessage());
}
public void after(JoinPoint joinPoint){
System.out.println(joinPoint.getSignature().getName());
}
@Around("execution(* com.offcn.service.UserService.*(..))")
//環繞增強 傳遞的是 JoinPoint 的子類 ProceedingJoinPoint
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());
}
}