使用Spring AOP實現業務依賴解耦

Spring IOC用於解決對象依賴之間的解耦,而Spring AOP則用於解決業務依賴之間的解耦;web

統一在一個地方定義【通用功能】,經過聲明的方式定義這些通用的功能以何種【方式】【織入】到某些【特定應用】裏去,而且【不須要修改】特定應用的代碼;
-1通用功能:<aop:aspect>如日誌、安全或事務,具體的方法動做稱爲Advice;
-2方式:<aop:before|after-returning|around>如方法調用、字段修改和拋出異常,Spring AOP僅支持方法調用(method execution join point);
-3織入:Weaving時期:編譯期,類加載期和運行期,Spring AOP僅支持運行期植入;
-4 特定應用:<aop:pointcut>,匹配鏈接點,也就是指定aspect適配的目標方法;
-5不須要修改:動態爲目標類建立代理對象,不須要修改業務代碼自己;spring

AspectJ的功能比Spring AOP更加全面,若是須要能夠在Spring中加載AspectJ的功能模塊;使用Spring AOP除了常規Spring的jar包外還須要引入aspectjweaver-[xxx].jar;express

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans">
 3   <!-- Config for IOC -->
 4   <context:component-scan base-package="com.active.leo.helloworld" />
 5   <context:property-placeholder location="classpath:service.properties" />
 6   <import resource="webmvc-config.xml" />
 7 
 8   <!-- Config for AOP -->
 9   <aop:aspectj-autoproxy />
10   <bean id="audience" class="com.active.leo.helloworld.Audience" />
11 
12   <aop:config>
13     <aop:aspect ref="audience">
14       <aop:pointcut id="performance" 
15            expression="execution(* com.active.leo.helloorld.Performer.perform(..))" /> 
16         
17       <aop:before pointcut-ref="performance" method="takeSeats" />
18       <aop:before pointcut-ref="performance" method="turnOffCellPhones" />
19       <aop:after-returning pointcut-ref="performance" method="applaud" />
20       <aop:after-throwing  pointcut-ref="performance" method="demandRefund" />
21     </aop:aspect>
22   </aop:config>
23 </bean> 
24 </beans>

-1 <aop:aspct>表示定義一個切面,而且與名爲audience的切面實現類關聯;
-2 <aop:pointcut>表示定義一個名爲performance的切點,並與須要進行AOP的目標業務方法綁定;execution的目標方法匹配的pattern:安全

1 execution([modifiers?] [return-type] [declaring –type?] [func-name]([param-name]) [throw-exp?] )

其餘的還有within(表示被某個註解標註的全部類), this, target和args;mvc

-3 <aop:before>表示調用目標業務perform方法以前觸發AOP,perform方法必定會執行;
-4 <aop:after-returning>表示調用perform方法觸發正常結束以後觸發AOP;
-5 <aop:after-throwing>表示調用perform方法拋出異常後觸發AOP;
-6 <aop:around>合併before和after-returning,PreceedingJoinPoint.proceed()爲目標動做;能夠控制執行流程,可根據狀況決定是否執行perform方法;
若是目標動做有參數,能夠藉助arg/arg-names在切面方法中獲取;app

 

經過註解實現Spring AOP
首先要建立一個用於config的Java Bean,
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {}
而後經過@Aspect,@Pointcut,@Before,@AfterReturning,@Around等實現;全部使用AspectJ標註的前提條件是JavaBean能夠被ClassLoader發現,因此須要額外添加@Component用於被IOC容器發現;被申明爲Aspect的JavaBean不能用於其餘Aspect的auto-proxying;ide

通常的Aspect的生命週期都是singleton,固然也能夠設置成perflow, perthis, pertypewithin, pertarget的不一樣週期;實現org.springFramework.core.Ordered接口,重寫getOrder()方法能夠控制當前advice在目標方法周邊的執行有限順序;ui

 1 public class App {
 2     public void func1() {}
 3     public void func2() {}
 4 }
 5 
 6 @Aspect
 7 public class SpringAopAspectDemo implements Ordered {
 8 
 9     @Pointcut("execution(* com.test.spring.aspectj.App.func1(..))")
10     public void pointcut4Func1() {}
11 
12     @Pointcut("execution(* com.test.spring.aspectj.App.func2(..))")
13     public void pointcut4Func2() {}
14 
15     @Around("pointcut4Func1()")
16     public Object doDevice(ProceedingJoinPoint pjp) throws Throwable {
17         //something needs to be done in aspect.
18     }
19 }

 

使用@Interface實現自定義註解
經過@Interface自定義註解,並結合spring AOP能夠實現針對request的權限校驗,this

 1 @Target(ElementType.METHOD)
 2 @Retention(RetentionPolicy.RUNTIME)
 3 public @interface PermissionRequired {
 4   boolean isReqiureAuth default false;
 5   boolean isRequireAuzh default false;
 6 }
 7 
 8 >>>>>>>>>>>>>
 9 
10 @Pointcut("execution(* com.ychen.application.*.*(..)) && "
11             + "@annotation(requiredPermission)")
12     public void pointController(RequiredPermission requiredPermission) {
13         // nothing
14 }
15 
16 @Around("pointController(requiredPermission)")
17 public Object applySecurityCheck(ProceedingJoinPoint point, 
18     RequiredPermission requiredPermission) throws Throwable {
19   // auth check
20 }

 

Spring AOP使用JDK Dynamic Proxy和CGLIB對目標類進行代理:
#1 JDK Dynamic Proxy方式使用Java Reflection技術,實現InvocationHandler接口,所以要求目標類有一個Interface,而且目標方法須要在此Interface中申明,動態建立一個實現了Interface的類並在該類中調用目標類的方法;spa

 1 public class PerformanceMonitorProxy implements InvocationHandler {
 2 
 3   private Object target;
 4   public ServiceWithPerformanceMonitorProxy(Object target) {
 5     this.target = target;
 6   }
 7   public static Object newProxyInstance(Object target) {
 8     Class clazz = target.getClass();
 9     return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(),
10               new ServiceWithPerformanceMonitorProxy(target));
11   }
12   @Override
13   public Object invoke(Object proxy, Method method, Object[] args) 
14         throws Throwable {
15     //do something before target function invocation
16     PerformanceMonitor.begin(method.getName());
17     Object result = method.invoke(target, args);
18     //do something after target function invocation
19     PerformanceMonitor.end(method.getName());
20     return result;
21   }
22 }

#2 CGLIB使用字節碼技術,實現MethodInterceptor接口,動態生成一個目標類的子類,經過over-write去覆蓋目標方法的執行,並在子類方法中調用目標方法的先後進行AOP;

 1 public class CGlibProxy implements MethodInterceptor {
 2   private Enhancer enhancer = new Enhancer();
 3   public Object getProxy(Class clazz) {
 4     enhancer.setSuperclass(clazz);
 5     // 代理執行時會回調此this持有的intercept方法,以實現代碼織入
 6     enhancer.setCallback(this); 
 7     return enhancer.create();
 8   }
 9 
10   @Override
11   public Object intercept(Object target, Method method, Object[] args, 
12       MethodProxy methodProxy) throws Throwable {
13     PerformanceMonitor.begin(method.getName());
14     Object result = methodProxy.invokeSuper(target, args);
15     // 下面這樣是沒法執行原有方法的,由於這裏的target並非原有類的實例,而是代理類的實例
16     // target :
17     // com.dianping.aop.AdminServiceImpl$$EnhancerByCGLIB$$225da297@16dd5a9d
18     // Object result = method.invoke(target, args);
19     PerformanceMonitor.end(method.getName());
20     return result;
21   }
22 }

Spring通常首選JDK Dynamic Proxy進行代理,若是遇到沒有實現Interface的狀況則使用CGLIB,固然能夠經過下屬設置強制使用CGLIB;

1 <aop:config proxy-target-class="true">
2     <!-- other beans defined here... -->
3 </aop:config>
相關文章
相關標籤/搜索