zoukankan      html  css  js  c++  java
  • 使用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     // 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>
  • 相关阅读:
    Linux日志文件utmp、wtmp、lastlog、messages
    Linux日志五大命令详解
    php 函数合并 array_merge 与 + 的区别
    MySQL对数据表进行分组查询(GROUP BY)
    如何在mysql中查询每个分组的前几名
    Autojump:一个可以在 Linux 文件系统快速导航的高级 cd 命令
    linux 查看磁盘空间大小
    js刷新页面方法大全
    [知乎有感] 读研到底为了什么,值不值得?
    [Hadoop] 在Ubuntu系统上一步步搭建Hadoop(单机模式)
  • 原文地址:https://www.cnblogs.com/leo-chen-2014/p/10567243.html
Copyright © 2011-2022 走看看