zoukankan      html  css  js  c++  java
  • 基于@AspectJ和Schema的AOP

    Spring对AOP的支持

    Spring在新版本中对AOP功能进行了重要的增强:

    1. 新增了基于Schema的配置支持,为AOP专门提供了aop命名空间。
    2. 新增了对AspectJ切点表达式语言的支持。@AspectJ是AspectJ1.5新增的功能,它通过Java5.0的注解技术,允许开发者在POJO中定义切面。Spring使用和@AspectJ相同风格非注解,并通过AspectJ提供的注解库和解析库处理切点。由于Spring只支持方法级的切点,所以仅对@AspectJ提供了有限的支持。
    3. 可以无缝地集成AspectJ.AspectJ提供了语言级切面的实现,Spring对开源世界里一切优秀的东西向来采取兼容并蓄的态度。
      SpringAOP包括基于XML配置的AOP和AspectJ注解的AOP.
      虽然AspectJ提供对AOP更为细致的实现,但像实例化切面,属性访问切面、条件切面等功能,在实际应用中并不常见。

    Java5.0注解知识

    @param @return 等Javadoc标签就是注解标签,为第三方工具提供了描述程序代码的注释信息。Java5.0注解可以看做javadoc和Xdoclet标签的延伸与发展。在Java5.0中可以自定义这些标签,并通过java语言的反射机制来获取类中标注的注解,完成特定的功能。
    注解是代码的副属信息,遵循一个原则:注解语言不能直接干涉程序代码的运行,无论删除或增加注解,代码都能正常运行。第三方工具可以利用代码中的注解间接控制程序代码的运行,他们通过java反射机制读取注解的信息,并根据这些信息更改目标程序的逻辑,SpringAOP对@AspectJ提供支持,正是采取这种方法。

    使用@AspectJ

    定义切面类

    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    //通过该注解定义一个切面
    @Aspect
    public class PreGreetingAspect {
    
        @Before("execution(* greetTo(..))")//定义切点和增强类型
        public void beforeGreeting(){//增强的横切逻辑
            System.out.println("How are you");
        }
    
    }
    

    测试类

    import org.springframework.aop.aspectj.annotation.AspectJProxyFactory;
    public class AspectJProxyTest {
    
        public static void main(String[] args) {
            Waiter target = new NaiveWaiter();
            //定义一个Aspect类型的代理工厂
            AspectJProxyFactory factory = new AspectJProxyFactory();
            //设置代理目标类
            factory.setTarget(target);
            //添加切面类
            factory.addAspect(PreGreetingAspect.class);
            //生成织入切面的代理对象
            Waiter proxy = factory.getProxy();
            proxy.greetTo("欢欢");
            proxy.serveTo("欢欢");
        }
    
    }
    
    

    此时会发现,在PreGreetingAspect 切面类中定义的前置方法和通过BeforeAdvice接口定义的前置方法增强不一样,一个明显的区别是PreGreetingAspect 类的beforeGreeting()方法没有任何入参,而BeforeAdvice的接口方法before(Method method, Object[] args, Object target)的入参提供了一条访问目标对象方法和入参的途径。@Aspect定义的切面也提供了访问目标对象连接点信息的方法。

    通过配置使用@AspectJ

    xml配置

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <!--目标bean-->
        <bean id="waiter" class="com.ijianghu.advice.impl.NaiveWaiter"/>
        <!--使用定义了@AspectJ的切面-->
        <bean class="com.ijianghu.aspect.PreGreetingAspect"/>
        <!--自动代理创建器,自动将@AspectJ注解切面类织入目标bean中-->
        <!--AnnotationAwareAspectJAutoProxyCreator能够将AspectJ注解切面类自动织入到目标Bean中。-->
        <bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator"
        />
    </beans>
    

    schema-aop配置

    <?xml version="1.0" encoding="UTF-8"?>
    <!--首先引入aop命名空间-->
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/aop">
        <!--aspectj-autoproxy自动为Spring容器中那些匹配@aspectj切面的Bean创建代理,完成切面织入-->
        <!--Spring在内部依旧使用AnnotationAwareAspectJAutoProxyCreator进行自动代理的创建工作,
        具体实现细节被aop:aspectj-autoproxy 隐藏起来-->
        <aop:aspectj-autoproxy/>
        <bean id="waiter" class="com.ijianghu.advice.impl.NaiveWaiter"/>
        <bean class="com.ijianghu.aspect.PreGreetingAspect"/>
    </beans>
    

    @AspectJ语法基础

    切点表达式函数

    切点表达式由关键字和操作参数组成,如execution(* greetTo(..))
    Spring支持9个@AspectJ切点表达式函数,用不同的方式描述目标类的连接点,根据描述对象的不同,大致分为4类:

    1. 方法切点函数:通过描述目标类方法的信息定义连接点
    2. 方法入参切点函数:通过描述目标类方法入参的信息定义连接点
    3. 目标类切点函数: 通过描述目标类类型的信息定义连接点
    4. 代理类切点函数:通过描述目标类的代理类的信息定义连接点
      详情请参考博客

    在函数入参中使用通配符

    @Aspect支持3种通配符
    :匹配任意字符,但它只能匹配上下文中的一个元素
    ..:匹配任意字符,可以匹配上下文中的多个元素,但在表示类时,必须和
    联合使用,而在入参时则单独使用。
    +:表示按类型匹配指定类的所有类,必须跟在类名后面,如com.smart.Car+。继承或扩展指定类的所有类,同时还包括指定类本身。
    [详情参考博客](https://blog.csdn.net/gexiaoyizhimei/article/details/99988144)

    逻辑运算符

    切点表达式由切点函数组成,切点函数之间还可以进行逻辑运算,组成复合切点。Spring支持以下切点运算符

    • &&:与操作符,相当于切点的交集运算
    • ||:或操作符:相当于切点的并集运算
    • !:非操作符:相当于切点的反集元素那,not是等效的操作符
      @Aspect中并不提供and or not,它们是Spring为了在xml配置文件中方便定义切点表达式而特意添加的等价运算符,和一般的语法表达式不一样,在Spring中使用and or not 时,允许不在前后添加空格。

    不同增强类型

    Spring本身使用接口描述各种增强类型,@AspectJ也为各种增强类型提供了不同的注解类,它们位于org.aspectj.lang.annotation.*包中。这些注解拥有若干个成员,可以通过成员完成定义切点信息,绑定连接参数等操作。注解期限都是RetentionPolicy.RUNTIME,标注目标都是ElementType.METHOD。

    1. @Before
      前置增强,相当于BeforeAdvice.Before注解类有两个成员。
    • value:该成员用于定义切点
    • argNames:由于无法通过Java反射机制获取方法入参名,所以如果在java编译时为启用调式信息,或者需要在运行期解析切点,就必须通过这个成员指定注解所标注增强方法的参数名(注意二者名字必须完全相同),多个参数名用逗号隔开。
    1. @AfterReturning
      后置增强, ==AfterReturningAdvice
    • value
    • pointcut
    • returning
    • agrNames
    1. @Around
      环绕增强 == MethodInterceptor
    • value
    • argNames
    1. @AfterThrowing
      抛出增强,相当于ThrowsAdvice
    • value
    • pointcut
    • throwing
    • argNames
    1. @After
      Final增强,不管是抛出异常还是正常退出,该增强都会得到执行。该增强没有对应的增强接口,可以看成ThrowsAdvice和AfterReturningAdvice的混合物,一般用于释放资源,相当于try{}finally{}的控制流。
    • value
    • argNames
    1. DeclareParents
      引介增强, == IntroductionInterceptor
    • value
    • defaultImpl:默认的接口实现类

    引介增强用法

    • 定义增强切面
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.DeclareParents;
     @Aspect
    public class EnableSellerAspect {
    
        @DeclareParents(value = "com.ijianghu.advice.impl.NaiveWaiter",//指定要增强的目标类
                defaultImpl = com.ijianghu.aspect.impl.SmartSeller.class)  //指定目标增强实现类
        public Seller seller;   //要实现的目标接口
    
    }
    
    • 配置Spring
    <?xml version="1.0" encoding="UTF-8"?>
    <!--首先引入aop命名空间-->
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/aop">
        <!--aspectj-autoproxy自动为Spring容器中那些匹配@aspectj切面的Bean创建代理,完成切面织入-->
        <!--Spring在内部依旧使用AnnotationAwareAspectJAutoProxyCreator进行自动代理的创建工作,
        具体实现细节被aop:aspectj-autoproxy 隐藏起来-->
        <aop:aspectj-autoproxy/>
        <bean id="waiter" class="com.ijianghu.advice.impl.NaiveWaiter"/>
        <bean class="com.ijianghu.aspect.declare.EnableSellerAspect"/>
    </beans>
    
    • 测试
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class DeclareAspectJProxyTest {
    
        public static void main(String[] args) {
           String configPath = "com\ijianghu\aspect\config\applicationContext-schema-declare.xml";
           ApplicationContext ctx = new ClassPathXmlApplicationContext(configPath);
           Waiter waiter = (Waiter) ctx.getBean("waiter");
           waiter.greetTo("欢欢");
           Seller seller = (Seller)waiter;
           seller.sell("beer");
        }
    
    }
    

    切点函数详解

    切点函数是AspectJ表达式语言的核心,也是使用@AspectJ进行切面定义的难点。

  • 相关阅读:
    EntityFramework 实践 Overview
    Retrofit 实践
    elasticsearch入门
    正则如何不匹配该内容
    windows下安装了2个python,如何下载模块到不同的python中
    python中 遇到的读取坑2.7和3.6版本的问题
    如何在idea中加载本地中已有的python
    如何在linux上有2个python的情况下安装gensim
    正则表达式,包含此内容(不管是前面还是后面)
    rdd里的foreach无法对外界产生影响
  • 原文地址:https://www.cnblogs.com/nangonghui/p/13170383.html
Copyright © 2011-2022 走看看