zoukankan      html  css  js  c++  java
  • Spring Boot实践——Spring AOP实现之动态代理

    Spring AOP 介绍

      AOP的介绍可以查看 Spring Boot实践——AOP实现

      与AspectJ的静态代理不同,Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。

      Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理。JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK动态代理的核心是InvocationHandler接口和Proxy类。

      如果目标类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,是利用asm开源包,可以在运行时动态的生成某个类的子类。注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。

      这里有注意的几点如下:

    • 从Spring 3.2以后不再将CGLIB放在项目的classpath下,而是将CGLIB类打包放在spring-core下面的org.springframework中。这个就意味着基于CGLIB的动态代理与JDK的动态代理在支持“just works”就一样了。
    • 在Spring 4.0中,因为CGLIB代理实例是通过Objenesis创建的,所以代理对象的构造器不再有两次调用。
    • 在 Spring Boot 2.0 中,Spring Boot现在默认使用CGLIB动态代理(基于类的动态代理), 包括AOP. 如果需要基于接口的动态代理(JDK基于接口的动态代理) , 需要设置spring.aop.proxy-target-class属性为false。

    实现方式

    一、验证Spring AOP动态代理

    博主使用的是Spring Boot 2.0版本

    1、JDK动态代理

    定义接口

    public interface Person {
        String say(String name);
        
        void eat(String food);
    }

    实现类

    @Component
    public class Chinese implements Person {
        private Logger logger = LoggerFactory.getLogger(Person.class);
        
        public Chinese() {
            super();
            logger.info("Chinese ==> Chinese method : 正在生成一个Chinese实例");
        }
    
        @Override
        @PersonAnnotation(name="Chinese")//该注解是用来定义切点
        public String say(String name) {
            logger.info("Chinese ==> say method : say {}", name);
            return name + " hello, JDK implement AOP";
        }
    
        @Override
        public void eat(String food) {
            logger.info("Chinese ==> eat method : eat {}", food);
        }
        
    }

    定义Aspect

    @Aspect
    @Component
    public class PersonAspect {
        private Logger logger = LoggerFactory.getLogger(PersonAspect.class);
        
        @Pointcut("@annotation(com.only.mate.springboot.annotation.PersonAnnotation)")
        public void pointCut(){
            
        }
        
        @Before("pointCut()")
        public void before(JoinPoint joinPoint) throws Throwable {
            logger.info("PersonAspect ==> before method : {}", joinPoint.getClass());
    
        }
        
        @After("pointCut()")
        public void after(JoinPoint joinPoint){
            logger.info("PersonAspect ==> after method : {}", joinPoint.getClass());
        }
    }

    2、CGLIB动态代理

    定义一个类

    @Component
    public class American {
        private Logger logger = LoggerFactory.getLogger(American.class);
        
        public American() {
            super();
            logger.info("American ==> American method : 正在生成一个American实例");
        }
    
        @PersonAnnotation(name="American")//该注解是用来定义切点
        public String say(String name) {
            logger.info("American ==> say method : say {}", name);
            return name + " hello, CGLIB implement AOP";
        }
    
        public void eat(String food) {
            logger.info("American ==> eat method : eat {}", food);
        }
        
    }

    3、配置

    <!-- 自动扫描使用了aspectj注解的类 -->
    <aop:aspectj-autoproxy/>

    @Configuration
    @ComponentScan("com.only.mate.springboot.aop")
    @EnableAspectJAutoProxy//开启AspectJ注解
    public class CustomAopConfigurer {
    }

    4、运行

    @Controller
    @RequestMapping(value="/aop")
    public class AopImplementController {
        private Logger logger = LoggerFactory.getLogger(AopImplementController.class);
        
        @Autowired
        private Person chinese;
        @Autowired
        private American american;
        
        @ResponseBody
        @RequestMapping(value="/talk")
        public String talk() {
            chinese.say("中国人说汉语");
            american.say("American say english");
            
            logger.info("AopImplementController ==> talk method : {}", chinese.getClass());
            logger.info("AopImplementController ==> talk method : {}", american.getClass());
    
            return "success";
        }
    }

    5、效果图

    问题出现了,按照之前的说法,Chinese应该是用JDK动态代理,American使用CGLIB动态代理才对。


      由于博主使用的是Spring Boot 2.0 ,在 Spring Boot 2.0 中,Spring Boot现在默认使用CGLIB动态代理(基于类的动态代理), 包括AOP。 如果需要基于接口的动态代理(JDK基于接口的动态代理) ,需要设置spring.aop.proxy-target-class属性为false。

    因此在application.properties加上配置spring.aop.proxy-target-class=false。重启访问

    如图:

    这样才达到了预期的目的


    通过网上查找:

    • 想要强制使用CGLIB,那么就设置<aop:config>下面的proxy-target-class属性为true
    <aop:config proxy-target-class="true">
            <!-- other beans defined here... -->
    </aop:config>

    此处没有验证,仅供参考。

    • 要是使用@AspectJ强制使用CGLIB的话,可以配置<aop:aspectj-autoproxy>下的proxy-target-class属性为true
    <aop:aspectj-autoproxy proxy-target-class="true"/>

    此处博主用Spring Boot 2.0,加以上配置无效,还是使用默认的CGLIB动态代理。失效的具体原因有待查验!

    • 要是使用@AspectJ强制使用CGLIB的话,向@EnableAspectJAutoProxy注解中添加属性proxyTargetClass = true

    此处博主用Spring Boot 2.0,加以上配置无效,还是使用默认的CGLIB动态代理。失效的具体原因有待查验!

    二、Spring AOP动态代理实现

    1、JDK动态代理

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    /**
     * @Description: Spring AOP之JDK动态代理实现
     * @ClassName: JDKDynamicSubject 
     * @author OnlyMate
     * @Date 2018年9月11日 下午4:47:53  
     *
     */
    public class JDKDynamicObject implements InvocationHandler {
        private Logger logger = LoggerFactory.getLogger(JDKDynamicObject.class);
    
        private Object target;
    
        public JDKDynamicObject() {
        }
    
        /**
         * @Description: 绑定对象,并生成代理对象
         * @Title: bind 
         * @author OnlyMate
         * @Date 2018年9月11日 下午4:48:31 
         * @param target
         * @return
         */
        public Object bind(Object target) {
            this.target = target;
            // 取得代理对象
            return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                    target.getClass().getInterfaces(), this);
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            logger.info("JDKDynamicObject ==> invoke method : {},{},{}", proxy.getClass(), method.getName(),
                    args.toString());
            method.invoke(target, args);
            return null;
        }
    }

    2、CGLIB动态代理

    import java.lang.reflect.Method;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.cglib.proxy.Enhancer;
    import org.springframework.cglib.proxy.InvocationHandler;
    
    /**
     * @Description: Spring AOP之CGLIB动态代理实现
     * @ClassName: CGLIBDynamicObject 
     * @author OnlyMate
     * @Date 2018年9月11日 下午4:57:30  
     *
     */
    public class CGLIBDynamicObject implements InvocationHandler {
        private Logger logger = LoggerFactory.getLogger(CGLIBDynamicObject.class);
    
        private Object target;
    
        public CGLIBDynamicObject() {
        }
    
        /**
         * @Description: 绑定对象,并生成代理对象
         * @Title: bind 
         * @author OnlyMate
         * @Date 2018年9月11日 下午4:48:31 
         * @param target
         * @return
         */
        public Object bind(Object target) {
            this.target = target;
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(this.target.getClass());
            // 回调方法
            enhancer.setCallback(this);
            // 创建代理对象
            return enhancer.create();
        }
    
        
        @Override
        public Object invoke(Object arg0, Method arg1, Object[] arg2) throws Throwable {
            logger.info("CGLIBDynamicObject ==> invoke method : {},{},{}", arg0.getClass(), arg1.getName(),
                    arg2.toString());
            arg1.invoke(target, arg2);
            return null;
        }
    }

    3、运行

    @Controller
    @RequestMapping(value="/aop")
    public class AopImplementController {
        private Logger logger = LoggerFactory.getLogger(AopImplementController.class);
        
        @Autowired
        private Person chinese;
        @Autowired
        private American american;
        
        @ResponseBody
        @RequestMapping(value="/talk")
        public String talk() {
    //        chinese.say("中国人说汉语");
    //        american.say("American say english");
    //        
    //        logger.info("AopImplementController ==> talk method : {}", chinese.getClass());
    //        logger.info("AopImplementController ==> talk method : {}", american.getClass());
            
            //自定义JDK动态代理
    //        Chinese chinese1 = new Chinese();
    //        InvocationHandler dsc = new JDKDynamicObject(chinese1);
    //        Person person = (Person) Proxy.newProxyInstance(chinese1.getClass().getClassLoader(), chinese1.getClass().getInterfaces(), dsc);
    //        person.say("中国人说汉语");
    //        logger.info("AopImplementController ==> talk method : JDKDynamicObject {}", person.getClass());
            
            JDKDynamicObject dsc1 = new JDKDynamicObject();
            Person person1 = (Person)dsc1.bind(new Chinese());
            person1.say("中国人说汉语");
            logger.info("AopImplementController ==> talk method : JDKDynamicObject {}", person1.getClass());
            
            //自定义CGLIB动态代理
            CGLIBDynamicObject dsm = new CGLIBDynamicObject();
            American american1 = (American) dsm.bind(new American()); 
            american1.say("American say english");
            logger.info("AopImplementController ==> talk method : CGLIBDynamicObject {}", american1.getClass());
            return "success";
            
        }
    
    }

    4、效果图

    简单的实现了Spring AOP的JDK动态代理和CGLIB动态代理。

  • 相关阅读:
    怎样整理房间
    拙劣的外部变量
    鸡窝里飞出伪凤凰
    flag标志什么?哦,它标志代码馊了——(三)
    边界测试——让BUG现形
    static的滥用与变态的阉割
    无知乱吃药
    flag标志什么?哦,它标志代码馊了——(一)
    flag标志什么?哦,它标志代码馊了——(二)
    半身不遂和粗中有细
  • 原文地址:https://www.cnblogs.com/onlymate/p/9630788.html
Copyright © 2011-2022 走看看