• 19-spring_aop


    本章目录


    代理模式 

    概念

    1. Proxy, 表示代理! 提供了对目标对象另外的访问方式,即通过代理访问目标对象。
    2. 通过代理对象查找目标对象,有以下优点:
      • 目标对象没有暴露给用户
      • 代理,在目标对象的基础上,可以增加额外的验证等功能

    静态代理

    1. 特点:

      • 目标对象必须实现接口
      • 代理对象必须实现和目标对象同样的接口
    2. 简单例子

      需求

      1. IUserDao.java(dao接口)
      2. UserDao.java(dao实现)
      3. UserDaoProxy.java(dao代理类,对UserDao中功能进行扩展)
      4. App(测试类)

      代码

      1. package claire.a_static;
        
        /**
         * Dao接口
         * @author claire
         *
         */
        public interface IUserDao {
            void save();
        }
        IUserDao.java
        package claire.a_static;
        
        /**
         * dao实现类(目标对象)
         * @author claire
         *
         */
        public class UserDao implements IUserDao{
        
            @Override
            public void save() {
                System.out.println("保存用户");
            }
        
        }
        UserDao
        package claire.a_static;
        
        /**
         * dao代理类,对UserDao进行扩展
         * @author claire
         *
         */
        public class UserDaoProxy implements IUserDao{
            private UserDao target = new UserDao();
        
            @Override
            public void save() {
                System.out.println("代理操作:开启事务");
                target.save();
                System.out.println("代理操作:提交事务");
            }
            
        }
        UserDaoProxy
        package claire.a_static;
        
        import org.junit.Test;
        
        /**
         * 测试类
         * @author claire
         *
         */
        public class App {
            @Test
            public void testProxy() throws Exception{
                IUserDao proxy = new UserDaoProxy();
                proxy.save();
            }
        }
        App

         

    3. 缺点

      1. 代理对象,需要依赖目标对象的接口。如果接口功能变化,目标对象变化,会引入代理对象的变化。
      2. 对每一个目标对象,都要分别写一个代理类,麻烦! (代理工厂)

     


    动态代理

    1. 简介

      • 通常说的动态代理,就是指jdk代理。 因为是通过jdk的api在运行时期,动态的生成代理对象的。
      • 目标对象一定要实现接口,代理对象不用实现接口
    2. JDK 生成代理对象的Api

      • Proxy newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)  

        参数loader :        当前目标对象使用的类加载器。

        参数interfaces :  当前目标对象实现的接口

        参数 h:                接口类型,事件处理器.

            当执行目标对象方法的时候,会触发事件; 把当前执行的方法(method对象),传入事件处理器方法参数中,  这样就可以根据业务逻辑,判断是否执行目标对象方法或扩展功能
      • 示例代码
        package claire.b_dynamic;
        
        /**
         * Dao接口
         * @author claire
         *
         */
        public interface IUserDao {
            void save();
            void find();
        }
        IUserDao
        package claire.b_dynamic;
        
        /**
         * dao实现类(目标对象)
         * @author claire
         *
         */
        public class UserDao implements IUserDao{
        
            @Override
            public void save() {
                System.out.println("保存用户");
            }
        
            @Override
            public void find() {
                // TODO Auto-generated method stub
                System.out.println("find");
            }
        
        }
        UserDao
        package claire.b_dynamic;
        
        import java.lang.reflect.InvocationHandler;
        import java.lang.reflect.Method;
        import java.lang.reflect.Proxy;
        
        /**
         * 代理工厂
         * 
         * @author claire
         * 
         */
        public class ProxyFactory{
            // 接收目标对象
            private static Object target;
        
            public ProxyFactory(Object target) {
                super();
                this.target = target;
            }
            
            
        
            // 返回目标对象的代理对象
            /**
             * Proxy.newProxyInstance参数loader : 当前目标对象使用的类加载器! 参数interfaces :
             * 当前目标对象实现的接口 参数 h: 接口类型,事件处理器.
             * 
             * @return
             */
            public static Object getProxyInstance(){
                return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                        target.getClass().getInterfaces(),
                        new InvocationHandler() {
                            @Override
                            public Object invoke(Object proxy, Method method, Object[] args)
                                    throws Throwable {
                                Object result = null;
                                //获取当前执行的方法名
                                if("find".equals(method.getName())){
                                    //直接调用
                                    result = method.invoke(target, args);
                                }else{
                                    System.out.println("开启额外事务");
                                    result = method.invoke(target, args);
                                    System.out.println("提交事务");
                                }
                                //执行目标对象的方法
                                
                                return result;
                            }
                        }
                        );
            
            }
        
        
        }
        ProxyFactory
        package claire.b_dynamic;
        
        import org.junit.Test;
        
        /**
         * 测试类
         * @author claire
         *
         */
        public class App {
            @Test
            public void testProxy() throws Exception{
                //创建代理对象
                UserDao target = new UserDao();
                //代理对象
                IUserDao proxyInstance = (IUserDao)new ProxyFactory(target).getProxyInstance();
                //执行代理对象方法
                proxyInstance.save();
                
            }
            
            @Test
            public void testProxy_find() throws Exception{
                //创建代理对象
                UserDao target = new UserDao();
                //代理对象
                IUserDao proxyInstance = (IUserDao)new ProxyFactory(target).getProxyInstance();
                //执行代理对象方法
                proxyInstance.find();
                
            }
        }
        App

     


    Cglib

    1. 简介

      也叫”子类代理” 当目标对象没有实现接口,就不能使用jdk提供的代理,可以以子类的方式实现。在运行时期动态在内存中构建一个子类对象的方法,从而对目标对象扩展,这种就是cglib代理。Spring也支持cglib代理,核心包中已经包含此功能。cglib是一个强大的高性能的代码生成包,它可以在运行期扩展Java类与实现Java接口。cglib包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。
    1. 代码

      • 示例代码
        package claire.c_cglib;
        
        /**
         * dao实现类(目标对象)
         * @author claire
         *
         */
        public class UserDao{
        
            /**
             * 设为final或static,不会重写。不会执行额外事务
             */
            public final void save() {
                System.out.println("保存用户");
            }
        
            public void find() {
                // TODO Auto-generated method stub
                System.out.println("find");
            }
        
        }
        UserDao
        package claire.c_cglib;
        
        import java.lang.reflect.Method;
        import org.springframework.cglib.proxy.Enhancer;
        import org.springframework.cglib.proxy.MethodInterceptor;
        import org.springframework.cglib.proxy.MethodProxy;
        
        /**
         * 代理工厂
         * 
         * @author claire
         * 
         */
        public class ProxyFactory implements MethodInterceptor {
            // 接收目标对象
            private static Object target;
        
            public ProxyFactory(Object target) {
                super();
                this.target = target;
            }
        
            /**
             * 对目标对象生成子类对象
             * 
             * @return
             */
            public Object getProxyInstance(){
                
                //字节码生成工具
                Enhancer en = new Enhancer();
                //设置父类
                en.setSuperclass(target.getClass());
                //设置回调函数
                en.setCallback(this);
                //创建子类对象
                return en.create();
            }
        
            /**
             * 事件处理器,执行目标方法时候触发
             */
            @Override
            public Object intercept(Object obj, Method method, Object[] args,
                    MethodProxy methodProxy) throws Throwable {
                // TODO Auto-generated method stub
                System.out.println("开启事务");
                Object result = method.invoke(target, args);
                System.out.println("提交事务");
                return result;
            }
        
        }
        ProxyFactory
        package claire.c_cglib;
        
        import org.junit.Test;
        
        /**
         * 测试类
         * @author claire
         *
         */
        public class App {
            @Test
            public void testProxy_gblib() throws Exception{
                //创建代理对象
                UserDao target = new UserDao();
                //代理对象
                UserDao proxyInstance = (UserDao)new ProxyFactory(target).getProxyInstance();
                //执行代理对象方法
                proxyInstance.save();
                System.out.println("********");
                proxyInstance.find();
                
            }
            
        }
        App

    1. 总结

      1. 目标对象可以不实现接口
      2. 目标类不能为final, 如果为final报错
      3. 方法如果为final/static, 不会被代理拦截,会直接执行目标对象方法

     


    代理总结

    在SpringAop编程中

    1. JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口。如果目标对象有实现接口,spring使用jdk提供的代理生成代理对象。
    2. 如果目标对象没有实现接口,使用cglib代理。
    3. 如果目标没有实现接口、且为final,不能进行aop编程,报错。不能生成代理。

    Aop编程

    aop编程简介:
    在软件开发中,有很多功能(如日志,安全,缓存,事务管理等)散布在应用中的多处功能中,被重复执行,这些代码被称为横切关注点,把这些横切关注点代码与业务代码相分离就是面向切面编程(aop)所要解决的任务。 关注点代码只需编写一次,之后在执行业务代码时候动态植入关注点代码。(Aop: Aspect Object Programming 面向切面编程)。Spring对aop的支持局限于方法拦截。
     
      如果要重用通用功能,最常见的面向对象技术是继承或委托,但是,如果整个应用都使用相同的基类,继承往往会导致一个脆弱的对象体系;而委托可能会需要对委托对象进行复杂的调用,切面提供了取代继承和委托的另一种可选方案,而且在很多场景下更加清晰简洁。在使用面向切面编程时,仍在一个地方定义通用功能,但是可以通过声明的方式定义这个功能要在何处应用,而无需修改受影响的类。这些通用功能所在的特殊类,被称为切面。
     
    aop相关术语:
    • 通知:    通知定义了切面是什么以及何时使用。Spring定义了5种通知,分别为:前置通知,后置通知,返回通知,异常通知及环绕通知。
    • 连接点:应用执行过程中能够插入切面的一个点。这个“点”可以是调用方法时,抛出异常时,甚至是修改一个字段时。
    • 切点: 一个切面不需要通知应用的所有连接点,切点有助于缩小切面所通知的连接点的范围。定义“何处”执行通知。
    • 切面:   切面是通知和切点的结合。
    • 引入:   引入允许我们向现有的类添加新方法或属性。
    • 织入:   织入是把切面应用到目标对象并创建新的代理对象的过程。切面在指定的连接点被织入到目标对象中。

     

    实现Aop编程

     示例代码:

    package claire.d_aop2;
    
    public interface IUserDao {
    
        void save();
    
    }
    IUserDao
    package claire.d_aop2;
    
    import org.springframework.stereotype.Repository;
    
    @Repository //把对象加入IOC
    public class UserDao implements IUserDao{
        @Override
        public void save() {
            System.out.println("save...");
        }
    }
    UserDao
    package claire.d_aop2;
    
    import org.springframework.stereotype.Component;
    
    //重复的代码
    
    @Component("aop")
    public class TranscationAop {
        
        public void beginTransaction(){
            System.out.println("开启事务");
        }
        
        public void commit(){
            System.out.println("提交事务");
        }
    }
    TranscationAop关注点代码
    package claire.d_aop2;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    
    public class ProxyFactory {
        /**
         * 生成代理对象
         * @param target 目标对象
         * @param aop 给目标对象动态植入重复代码(关注点代码)
         * @return
         */
        public Object getProxyInstance(final Object target, final TranscationAop aop){
            return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                    target.getClass().getInterfaces(), 
                    new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args)
                                throws Throwable {
                            aop.beginTransaction();
                            Object result = method.invoke(target, args);
                            aop.commit();
                            return result;
                        }
                    });
        }
    }
    ProxyFactory 代理工厂
    <?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"
        xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd"
        >
        <!-- 注解方式实现Spring IOC容器配置 -->
        <!-- 开启注解扫描 -->
        <context:component-scan base-package="claire.d_aop2"></context:component-scan>
        <bean id="proxyFactory" class="claire.d_aop2.ProxyFactory"></bean>
        <bean id="userDaoProxy" factory-bean="proxyFactory" factory-method="getProxyInstance">
            <constructor-arg index="0" ref="userDao"></constructor-arg>
            <constructor-arg index="1" ref="aop"></constructor-arg>
        </bean>
    </beans>
    applicationContext.xml
    package claire.d_aop2;
    
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class App {
        private ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml", getClass());
        
        @Test
        public void test_dop() throws Exception{
            IUserDao user = (IUserDao)ac.getBean("userDaoProxy");
            user.save();
        }
    }
    App


    Aop编程-注解方式

    开发步骤:

    1. 引入jar文件(前三个包不再sping源码中,直接百度下载)
      1. aopalliance.jar               

      2. aspectjrt.jar                    (aspectj 在spring之前,面向切面开发的公用组件)

      3. aspectjweaver.jar           下载地址

      4. spring-aop-4.3.9.RELEASE.jar  

    2. 引入aop名称空间(需要注意schemaLocation的顺序,如果aop在context前面,就会没有出现注解提示)
      <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"
          xmlns:context="http://www.springframework.org/schema/context"
          xmlns:aop="http://www.springframework.org/schema/aop"
          xsi:schemaLocation="http://www.springframework.org/schema/beans
              http://www.springframework.org/schema/beans/spring-beans.xsd
              http://www.springframework.org/schema/context
              http://www.springframework.org/schema/context/spring-context.xsd
              http://www.springframework.org/schema/aop
              http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
              "
          >
    3. 开启aop注解<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    4. 使用aop相关注解实现aop编程(通知类)
      1. @Aspect               指定一个类为切面类(通知类)

      2. @Before          前置通知  

      3. @After                   后置通知  

      4. @AfterReturning    返回后通知  【出现异常不执行】

      5. @AfterThrowing    异常通知  

      6. @Around                环绕通知  

      7. @Pointcut             定义一个切入点表达式变量  (后面使用这个切入点表达式的时候,直接引用方法名即可)

    5. execution切入点指示符:
      • execution(方法修饰符    方法返回值    方法所属类    匹配方法名    (方法中的形参表)    方法声明抛出的异常):括号中黑体部分不可以省略,
      • 上面各部分均可使用通配符("*", "..")。其中“*”表示匹配任意类型的参数,“..” 代表0个或者多个任意类型的参数。
        • 例如:
          • (): 匹配任何一个无参方法。
          • (..):  匹配一个可以接受任何数量和类型参数的方法。
          • (*):    匹配一个可以接受一个任意类型的参数。
          • (*, Integer):    匹配两个参数,第一个为任意类型,第二个必须为Integer类型。
        • 例子2:
          • execution(public * *(..)):     匹配所有类型为public,第一个*为返回值,第二个为方法名。
          • execution(* save*(..)):     匹配所有方法名前缀为save的方法。其返回值可以为任意类型。
          • execution(* *production(*, String)):  匹配返回值为任意类型的方法名后缀为production的方法,要求其参数有两个参数,且第二个参数为String类型。
          • execution(* com.zhihu.claire..*(..)):    匹配返回值为任意类型的、在com.zhuihu.calire包下的所有方法,包括子包。
          • execution(* com..*.*service.find*(..)):    匹配com包及其子包下所有后缀名为service的类中,所有方法名前缀为find的方法。
          • execution(* foo(String, ..)): 至少有一个String类型的参数。

    示例代码:

      • <?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"
            xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
            xmlns:aop="http://www.springframework.org/schema/aop"
            xsi:schemaLocation="http://www.springframework.org/schema/beans 
                                http://www.springframework.org/schema/beans/spring-beans.xsd
                                http://www.springframework.org/schema/context 
                                http://www.springframework.org/schema/context/spring-context-4.3.xsd
                                http://www.springframework.org/schema/aop
                                http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
        
            <!-- 注解方式实现Spring IOC容器配置 -->
            <!-- 开启注解扫描 -->
            <context:component-scan base-package="claire"></context:component-scan>
            <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
            
            
        </beans>
        application.xml
        package claire.dao;
        
        public interface IUserDao {
        
            void save();
        
        }
        IUserDao.java
        package claire.dao;
        
        import org.springframework.stereotype.Repository;
        
        @Repository //把对象加入IOC
        public class UserDao implements IUserDao{
            @Override
            public void save() {
                System.out.println("save...");
            }
        }
        UserDao
        package claire.aopannotion;
        
        import org.aspectj.lang.ProceedingJoinPoint;
        import org.aspectj.lang.annotation.After;
        import org.aspectj.lang.annotation.Around;
        import org.aspectj.lang.annotation.Aspect;
        import org.aspectj.lang.annotation.Before;
        import org.springframework.stereotype.Component;
        
        //重复的代码
        
        @Component("aop")
        @Aspect //指定为一个切面类
        public class TranscationAop {
            
            @Before("execution(public * save(..))")
            public void beginTransaction(){
                System.out.println("前置通知");
            }
            
            @After("execution(public * save(..))")
            public void commit(){
                System.out.println("后置通知");
            }
            
            @Around("execution(public * save(..))")
            public void aroundTransaction(ProceedingJoinPoint pjp) throws Throwable{
                System.out.println("环绕开始");
                pjp.proceed();
                System.out.println("环绕结束");
            }
        }
        TranscationAop
        package claire.aopannotion;
        
        import org.junit.Test;
        import org.springframework.context.ApplicationContext;
        import org.springframework.context.support.ClassPathXmlApplicationContext;
        
        import claire.dao.IUserDao;
        
        public class App {
            private ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml", getClass());
            
            @Test
            public void test_dop() throws Exception{
                IUserDao user = (IUserDao)ac.getBean("userDao");
                user.save();
            }
        }
        Test

    处理通知中的参数:

        • 如果切面所通知的方法有参数的话,实现能访问和使用传递给被通知方法的参数
              @Before("execution(* save(int)) and args(count)")
              public void beginTransaction(int count) {
                  System.out.println("前置通知+count:"+count);
              }

          args()标明传递给sava方法的int型参数也会传递到通知中去。

    通过注解引入新功能:

    具体解释见下面的XML配置单元。使用 @DeclareParents(value="", defaultImpl=) 进行配置。

    Spring生内部生成代理对象的过程:

              1. 创建容器对象的时候, 根据“切入点表达式”拦截的类,生成代理对象。

              2. 如果目标对象有实现接口,使用jdk代理。没有实现接口,使用cglib代理。

              4. 从容器获取代理后的对象。

              5. 执行代理对象的方法,运行时动态植入切面类中的通知。


    Aop编程-xml配置方式

     如果需要声明切面,但是又不能为通知类添加注解的时候,就必须转为xml配置。(xml配置更易于维护)

    aop常用配置元素

    AOP配置元素 用途             
    <aop:advisor> 定义aop通知器
    <aop:after> 定义aop后置通知(不管是否执行成功)
    <aop:after-returning> 定义aop返回通知
    <aop:after-throwing> 定义dop异常通知
    <aop:around> 定义aop环绕通知(可以不通过成员变量,使前置通知与后置通知之间能共享消息)
    <aop:aspect> 定义一个切面
    <aop:aspectj-autoproxy> 启用@AspectJ注解驱动的切面
    <aop:before> 定义一个aop前置通知
    <aop:config> 顶层的aop配置元素。大多数的<aop:*>元素必须包含在<aop:config>元素内
    <aop:declare-parents> 以透明的方式为被通知的对象引入额外的接口
    <aop:pointcut> 定义一个切点

    基础示例代码:

    将上面注解方式实现aop中的java类中的注解删除,其他保持不变,只需在application.xml中配置即可,具体配置如下:

    • package claire.aopannotion;
      
      import org.aspectj.lang.ProceedingJoinPoint;
      
      public class TranscationAop {
      
          public void beginTransaction() {
              System.out.println("前置通知");
          }
      
          public void afterTransaction() {
              System.out.println("后置通知");
          }
      
          public void aroundTransaction(ProceedingJoinPoint pjp) throws Throwable {
              System.out.println("环绕开始 + 目标对象" + pjp.getTarget());
              pjp.proceed();
              System.out.println("环绕结束");
          }
      }
      TranscationAop
      <?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"
          xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
          xmlns:aop="http://www.springframework.org/schema/aop"
          xsi:schemaLocation="http://www.springframework.org/schema/beans 
                              http://www.springframework.org/schema/beans/spring-beans.xsd
                              http://www.springframework.org/schema/context 
                              http://www.springframework.org/schema/context/spring-context-4.3.xsd
                              http://www.springframework.org/schema/aop
                              http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
      
          <bean id="userDao" class="claire.dao.UserDao"></bean>
          <bean id="aop" class="claire.aopannotion.TranscationAop"></bean>
          <aop:config>
              <!-- 定义切入点, 在大量通知元素的pointcut属性定义相同时,使用此元素消除重复 -->
              <aop:pointcut expression="execution(public * claire.dao.UserDao.*(..))" id="pt"/>
              <!-- 定义切面 -->
              <aop:aspect ref="aop">
                  <!--aop:pointcut 也可以定义在切面定义里面,被一个切面内的所有通知使用-->
                  <aop:pointcut expression="execution(public * claire.dao.UserDao.*(..))" id="pt2"/>
                  
                  <aop:before method="beginTransaction" pointcut-ref="pt"/>
                  <aop:after method="afterTransaction" pointcut="execution(public * claire.dao.UserDao.*(..))"/>
                  <aop:around method="aroundTransaction" pointcut-ref="pt2"/>
              </aop:aspect>
              <!-- aop通知器 -->
              
          </aop:config>
      
      </beans>
      application.xml

    使用xml配置实现新功能:

      不用直接修改对象或类的定义就能够为对象或者类增加新的方法。使用切面,可以为对象拥有的方法添加新功能。同样的,使用被成为引入的aop概念,可以为Spring Bean添加新方法。在Spring中,切面只是实现了其所包装bean的相同接口代理,如果除了这些接口,代理也能暴露新的接口的话,那么切面所通知的bean看起来就像是实现了新的接口。注意,当引入接口的方法被调用时,代理会把此调用委托给实现了新接口的某个其他对象,实际上一个bean的实现被拆分到多个类中

    具体实现

      (为了方便,例子中所有用到的文件都在同一个包下):

      假设新引入的接口为Encoreable,定义了接口方法newMethod(),它的接口实现类为DefaultEncoreable。

    1. <aop:declare-parents types-matching=""   implement-interface="" delegate-ref="" />

      <aop:declare-parents>声明了此切面所通知的bean要在它的对象层次结构中拥有新的父类型,类型匹配 types-matching接口的那些bean在父结构中会增加 implement-interface所指向的接口, 而具体实现则通过default-impl属性来指定。

    2. 代码:
      package claire.execution;
      
      public interface Encoreable {
          void newMethod();
      }
      新增接口Encoreable
      package claire.execution;
      
      import org.springframework.stereotype.Component;
      
      @Component("defaultEncoreable")
      public class DefaultEncoreable implements Encoreable{
      
          @Override
          public void newMethod() {
              // TODO Auto-generated method stub
              System.out.println("实现了新引入接口方法的类");
          }
      
      }
      新接口实现
      package claire.execution;
      
      public interface IUserDao {
      
          void save(int a);
          int get();
      
      }
      原有接口IUserDao
      package claire.execution;
      
      import org.springframework.stereotype.Repository;
      
      @Repository
      public class UserDao implements IUserDao{
          
          @Override
          public void save(int a) {
              System.out.println("UserDao:save");
          }
      
          @Override
          public int get() {
              // TODO Auto-generated method stub
              System.out.println("get");
              return 0;
          }
      
      }
      UserDao
      <?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"
          xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
          xmlns:aop="http://www.springframework.org/schema/aop"
          xsi:schemaLocation="http://www.springframework.org/schema/beans 
                              http://www.springframework.org/schema/beans/spring-beans.xsd
                              http://www.springframework.org/schema/context 
                              http://www.springframework.org/schema/context/spring-context-4.3.xsd
                              http://www.springframework.org/schema/aop
                              http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
      
          <context:component-scan base-package="claire.execution"></context:component-scan>
      
          <aop:config>
              <aop:aspect>
                  <aop:declare-parents types-matching="claire.execution.IUserDao+"
                      implement-interface="claire.execution.Encoreable" delegate-ref="defaultEncoreable" />
              </aop:aspect>
      
          </aop:config>
      
      </beans>
      xml配置
      package claire.execution;
      
      import org.junit.Test;
      import org.springframework.context.ApplicationContext;
      import org.springframework.context.support.ClassPathXmlApplicationContext;
      public class App {
          private ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml", getClass());
          
          @Test
          public void test_dop() throws Exception{
              
              IUserDao user = (IUserDao)ac.getBean("userDao");
              ((Encoreable)user).newMethod();
              System.out.println("**********");
              user.get();
              System.out.println("************");
              user.save(2);
          }
      }
      test

    切入点表达式

    Spring借助AspectJ的切入点表达式来定义Spring切面。

    (注意只有execution指示器是实际匹配执行的,而其他的指示器都是用来限制匹配的。)

    AspectJ指示器                      描述
    arg() 限制连接点匹配参数为指定类型的执行方法
    @args() 限制连接点匹配参数由指定注解标注的执行方法
    execution() 用于匹配是连接点的执行方法
    this() 限制连接点匹配aop代理的bean引用为指定类型的类                      
    target 限制连接点匹配目标对象为指定类型的类
    @target() 限制连接点匹配特定的执行对象,这些对象对应的类要具有指定类型的注解           
    within() 限制连接点匹配指定的类型
    @within() 限制连接点匹配指定注解所标注的类型
    @annotion 限定匹配带有指定注解的连接点

    格式:

    •  切入点表达式的api文档在spring-framework-4.3.9.RELEASEdocsspring-framework-referencehtml中的第十一章 Aspect Oriented Programming with Spring11.2.3 Declaring a pointcut章节中
      execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)
                  throws-pattern?)

      具体:

      • modifiers-pattern:                    拦截的方法的访问修饰符,可以省略即(“?”表示零次或一次)。
      • ret-type-pattern:                      连接点方法返回值类型,必须指定,通常定义为* ,表示可以匹配任意返回值类型。
      • declaring-type-pattern:            拦截的方法所在的类(?)。
      • name-pattern(param-pattern): 方法名(参数), 方法名必须指定,可定义为*。参数用".."表示匹配任意参数
      • throws-pattern:                        方法声明的异常。

    例子:

    • <aop:config>
              <aop:aspect ref="aop">
                  <!-- 对claire.execution.UserDao内的所有方法加入前置通知 -->
                  <aop:pointcut expression="execution(* claire.execution.UserDao.*(..))" id="pt1"/>
                  <aop:before method="beginTransaction" pointcut-ref="pt1"/>
                  <!-- 对所有返回值为int类型的方法设置后置通知 -->
                  <!-- 定义省略了方法修饰符,方法所在包,方法名任意,参数任意 -->
                  <aop:pointcut expression="execution(int *(..))" id="pt2"/>
                  <aop:after method="afterTransaction" pointcut-ref="pt2"/>    
              </aop:aspect>
          </aop:config>
    • 所有名称中包含“save"的方法: execution(* *save*(..))
    • 指定包(此例中包名为claire)及其子包的所有类的所有方法: execution(* claire..*.*(..)) 这里第二个 * 表示类,第三个 * 表示方法名。
    • save及get方法: execution(* claire..*.save(..)) or execution(* claire..*.get(..)) 或者用 || 
    • 不拦截get: !execution(* claire.execution.UserDao.get(..)) 或者用not(使用not时not前面要加空格)
    • *为通知传递参数:
      execution(* claire..UserDao.save(int)) and args(count)
  • 相关阅读:
    Kafka如何保证读写的跨分区与会话
    Kafka topic中的partition的leader选举
    Kafka为什么这么快
    sqoop导入导出
    为什么要用redis去重
    bypass SortShuffleManager的bypass运行机制
    大数据常用端口号
    vector基础
    【拓扑排序】
    【POJ】Crazy Search(hash)
  • 原文地址:https://www.cnblogs.com/clairexxx/p/10303129.html
Copyright © 2020-2023  润新知