使用Spring AOP实现业务依赖解耦
Posted 一个理想主义者的奋斗
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用Spring AOP实现业务依赖解耦相关的知识,希望对你有一定的参考价值。
Spring IOC用于解决对象依赖之间的解耦,而Spring AOP则用于解决业务依赖之间的解耦;
统一在一个地方定义【通用功能】,通过声明的方式定义这些通用的功能以何种【方式】【织入】到某些【特定应用】里去,并且【不需要修改】特定应用的代码;
-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不需要修改:动态为目标类创建代理对象,不需要修改业务代码本身;
AspectJ的功能比Spring AOP更加全面,如果需要可以在Spring中加载AspectJ的功能模块;使用Spring AOP除了常规Spring的jar包外还需要引入aspectjweaver-[xxx].jar;
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;
-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在切面方法中获取;
通过注解实现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;
一般的Aspect的生命周期都是singleton,当然也可以设置成perflow, perthis, pertypewithin, pertarget的不同周期;实现org.springFramework.core.Ordered接口,重写getOrder()方法可以控制当前advice在目标方法周边的执行有限顺序;
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的权限校验,
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的类并在该类中调用目标类的方法;
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 // [email protected]d5a9d 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>
以上是关于使用Spring AOP实现业务依赖解耦的主要内容,如果未能解决你的问题,请参考以下文章