• Spring学习笔记2—AOP


    1.AOP概念

      AOP(Aspect Oriented Programming):面向切面编程,AOP能够将那些与业务无关,却为业务模块所共同调用的应用(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。  

     

    2.AOP术语  

      通知(Advice) 

      在AOP术语中,切面的工作被成为通知。通知定义了切面是什么以及何时使用。 

      Spring切面可以应用5种类型的通知:  

    • Before —— 在方法被调用之前调用通知;
    • After —— 在方法完成之后调用通知,无论方法执行是否成功;
    • After-returning —— 在方法成功执行之后调用通知;
    • After-throwing —— 在方法抛出异常后调用通知;
    • Around —— 通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为; 

      连接点(Joinpoint) 

      连接点是在应用执行过程中能够插入切面的一个点。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。 

      切点(Pointcut) 

      如果通知定义了切面的“什么”和“何时”,那么切点就定义了“何处”,即切点用于准确定位应该在什么地方应用切面的通知

      切面(Aspect) 

      切面是通知和切点的结合。通知和切点共同定义了关于切面的全部内容——它是什么,在何时和何处完成其功能。

      引入(Introduction) 

      引入允许我们向现有的类添加新方法或属性,通过通知类,从而可以在无需修改现有的类的情况下,让它们具有新的行为和状态(通过使用<aop:declare-parents>声明实现)。 

      织入(Weaving) 

      织入是将切面应用到目标对象来创建新的代理对象的过程,切面在指定的连接点被织入到目标对象中(注入前置、后置通知等)。 

     

    3.使用切点选择连接点  

      Spring AOP中,需要使用AspectJ的切点表达式语言来定义切点。

      Spring仅支持AspectJ切点指示器的一个子集:

    Spring AOP支持的AspectJ切点指示符
    AspectJ指示器 描述
    execution 用于匹配方法执行的连接点
    within 用于匹配指定类型内的方法执行
    this 用于匹配当前AOP代理对象类型的执行方法,注意是AOP代理对象的类型匹配,这样就可能包括引入接口也类型匹配
    target 用于匹配当前目标对象类型的执行方法,注意是目标对象的类型匹配,这样就不包括引入接口也类型匹配
    args 用于匹配当前执行的方法传入的参数为指定类型的执行方法
    @within 用于匹配持有指定注解类型内的方法
    @target 用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解
    @args 用于匹配当前执行的方法传入的参数持有指定注解的执行
    @annotation 用于匹配当前执行方法持有指定注解的方法
    bean Spring AOP扩展的,用于匹配特定名称的Bean对象的执行方法
    reference pointcut 表示引用其他命名切入点

      在Spring中尝试使用其他AspectJ指示器时,将会抛出IllegalArgumentException异常。 

      例如,当切点表达式如下时:  

    execution(* com.wdcloud.jyx.bj.service.*Service.*(..))

       我们使用execution指示器选择com.wdcloud.jyx.bj.service包下后缀为Service的所有类的所有方法,表达式以*开始,表示我们不关心方法的返回值,而方法参数为(..)表示匹配任意参数的方法。

     

    4.在Spring中配置AOP  

    这里介绍两种配置方法:使用xml配置AOP和使用@AspcetJ注解配置AOP,并且还介绍使用@AspcetJ注解配置AOP时如何获取目标方法名、方法参数和方法返回值。

    基于xml配置Spring AOP:  

    Spring提供了声明式切面的选择。Spring的AOP配置元素简化了基于POJO切面的声明: 

    AOP配置元素功能
    <aop:advisor> 定义一个AOP通知器
    <aop:after> 定义一个AOP后置通知(不考虑被通知的方法是否执行成功,相当于再finally中执行)
    <aop:after-returning> 定义一个AOP返回后通知
    <aop:after-throwing> 定义一个AOP抛出后通知
    <aop:around> 定义一个AOP环绕通知
    <aop:aspect> 定义一个切面
    <aop:before> 定义一个AOP前置通知
    <aop:config> 顶层的AOP元素。大多数<aop:*>元素必须包含在<aop:config>里
    <aop:pointcut> 定义一个切点
    <aop:declare-parents> 为被通知的对象引入额外的接口,并透明地实现

    4.1 引用 aspectJ 的 jar 包: aspectjweaver.jar aspectjrt.jar  

    4.2 在applicationContext.xml中引入AOP对应的命名空间:

        xmlns:aop="http://www.springframework.org/schema/aop"  
        xsi:schemaLocation="http://www.springframework.org/schema/aop  
                    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

    4.3 创建切面类:

    public class LogInterceptor {
    
        public void before() {
            System.out.println("----------method start----------");
        }
    
        public void after() {
            System.out.println("----------method after----------");
        }
    
        public void AfterReturning() {
            System.out.println("----------method AfterReturning----------");
        }
    
        public void AfterThrowing() {
            System.out.println("----------method AfterThrowing----------");
        }
    }

    4.4 在applicationContext.xml中配置AOP

      <!-- spring AOP配置 -->
        <bean id="logAdvice" class="com.wdcloud.solr.util.LogInterceptor"/>
        
        <aop:config> 
            <aop:aspect ref="logAdvice">        
                <aop:pointcut id="pointcut" expression="execution(* com.wdcloud.solr.action.*Action.*(..))" />
                
                <aop:before pointcut-ref="pointcut" method="before" />
                <aop:after-returning pointcut-ref="pointcut" method="AfterReturning" />
                <aop:after-throwing pointcut-ref="pointcut" method="AfterThrowing" />
             </aop:aspect> 
        </aop:config>

    测试结果:

    基于@AspcetJ注解配置Spring AOP:

    4.1和4.2步骤同上。

    4.3 创建切面类:

    @Component
    @Aspect
    public class LogInterceptor {
    
        // 定义切点
        public static final String actionPointcut = "execution(* com.wdcloud.solr.action.*Action.*(..))";
    
        @Before(actionPointcut)
        public void before() {
            System.out.println("----------method start----------");
        }
    
        @After(actionPointcut)
        public void after() {
            System.out.println("----------method after----------");
        }
    
        @AfterReturning(actionPointcut)
        public void afterReturning() {
            System.out.println("----------method AfterReturning----------");
        }
    
        @AfterThrowing(actionPointcut)
        public void afterThrowing() {
            System.out.println("----------method AfterThrowing----------");
        }
    }

    4.4 在applicationContext.xml中配置AOP

    <!-- 自动为spring容器中那些配置@aspectJ切面的bean创建代理 -->
    <!-- proxy-target-class属性,默认为false,表示使用jdk动态代理织入增强,当poxy-target-class="true"时,表示使用CGLib动态代理技术织入增强 -->
    <aop:aspectj-autoproxy proxy-target-class="true"/>

    测试结果:

    使用@AspcetJ注解配置AOP时如何获取目标方法名、方法参数和方法返回值:

    访问目标方法最简单的做法是定义切面方法时,将第一个参数定义为JoinPoint类型,当该切面方法被调用时,该JoinPoint参数就代表了织入切面的连接点。JoinPoint里包含了如下几个常用的方法:

    • Object[] getArgs:返回目标方法的参数

    • Signature getSignature:返回目标方法的签名

    • Object getTarget:返回被织入增强处理的目标对象

    • Object getThis:返回AOP框架为目标对象生成的代理对象

    将上面的切面类修改如下:

    @Component
    @Aspect
    public class LogInterceptor {
    
        // 定义切点
        public static final String actionPointcut = "execution(* com.wdcloud.solr.action.*Action.*(..))";
        public static final String servicePointcut = "execution(* com.wdcloud.solr.service.*Service.*(..))";
    
        @Before(actionPointcut)
        public void before(JoinPoint point) {
            System.out.println("----------method " + point.getSignature().getDeclaringTypeName() + "."
                    + point.getSignature().getName() + " start----------");
            System.out.println("----------param:" + JSONObject.toJSONString(point.getArgs()) + "----------");
        }
    
        @After(actionPointcut)
        public void after(JoinPoint point) {
            System.out.println("----------method " + point.getSignature().getDeclaringTypeName() + "."
                    + point.getSignature().getName() + " end----------");
        }
    
        @AfterReturning(pointcut = actionPointcut, returning = "returnValue")
        public void afterReturning(JoinPoint point, Object returnValue) {
            System.out.println("----------result:" + JSONObject.toJSONString(returnValue) + "----------");
            System.out.println("----------method " + point.getSignature().getDeclaringTypeName() + "."
                    + point.getSignature().getName() + " end----------");
        }
    
        //当有多个切点的时候,用"||"连接切点即可
        @AfterThrowing(pointcut = actionPointcut + "||" + servicePointcut, throwing = "exception")
        public void afterThrowing(JoinPoint point, Exception exception) {
            System.out.println("----------exception:" + exception.getMessage() + "----------");
        }
    }

    测试结果:

    如果想要在aop中排除某些方法,可以这样配置切点:

    public static final String noGetMethodPointcut = "execution(* witparking.starlightmanagement.service.impl.*Service.*(..)) and " +
            "!execution(* witparking.starlightmanagement.service.impl.*Service.get*(..))";

    参考

    http://blog.csdn.net/robinjwong/article/details/25568481#t6

    http://www.cnblogs.com/oumyye/p/4480196.html

    http://blog.csdn.net/ye_sheng/article/details/48178983

  • 相关阅读:
    Android Service学习之AIDL, Parcelable和远程服务
    数据结构&算法实践—【排序|交换排序】地精排序及改进
    Android: INSTALL_FAILED_UPDATE_INCOMPATIBLE错误解决措施
    重构改善既有代码的设计:对象之间移动特性的八种方法(五)
    重构改善既有代码的设计:简化条件表达式(七)
    [Innost]Android深入浅出之Binder机制
    Android: R cannot be resolved to a variable
    Python xml属性/节点/文本的增删改[xml.etree.ElementTree]
    数据结构&算法实践—【排序|插入排序】插入排序
    重构改善既有代码的设计:简化函数调用 (八)
  • 原文地址:https://www.cnblogs.com/Jason-Xiang/p/5387996.html
Copyright © 2020-2023  润新知