• jdk动态代理 注解的获取


    一、使用场景

      根据方法或者类上是否有指定的注解,进行业务增强。例如:手写transactional注解,实现声明式事务

      1、自定义transaction注解

    /**
     * 描述 自定义事务注解
     *
     * @author lwb
     * @since 2020/7/8
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE, ElementType.METHOD})
    @Documented
    @Inherited
    public @interface Transactional {
    }
    View Code

      2、业务代码:定义用户服务接口,实现用户服务接口,在实现类的方法上标注@transaction注解

    /**
     * 描述 用户服务接口类
     *
     * @author lwb
     * @since 2020/7/8
     */
    public interface UserService {
    
        void doSomething(String thing);
    }
    
    public class UserServiceImpl implements UserService {
        @Override
        @Transactional
        public void doSomething(String thing) {
            System.out.println("do something : " + thing);
        }
    }
    View Code

      3、动态代理工厂:生成对象的jdk动态代理

    public static Object getJdkProxy(Object object){
            return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(),
                    (proxy, method, args) -> {
                        Object result;
                        if(method.isAnnotationPresent(Transactional.class) || method.getDeclaringClass().isAnnotationPresent(Transactional.class)){
                            try {
                                System.out.println("开启事务");
                                result = method.invoke(object, args);
                                System.out.println("提交事务");
                            }catch (Exception e){
                                System.out.println("回滚事务");
                                throw e;
                            }
                        }else{
                            result = method.invoke(object, args);
                        }
    
                        return result;
                    });
        }
    View Code

      4、测试

    /**
     * 描述
     *
     * @author lwb
     * @since 2020/7/8
     */
    public class ProxyTest {
    
        @Test
        public void testJdkProxy(){
            UserService userService = new UserServiceImpl();
            UserService jdkProxy = (UserService) ProxyFactory.getJdkProxy(userService);
            jdkProxy.doSomething("eat");
        }
    }
    View Code

      5、校验测试结果

      

      发现并未打印事务信息。不符合预期结果。

     二、问题分析

       明明在实现类的方法上标注了Transaction注解,为什么在增强中没有获取到Transaction注解?分析jdk动态代理的原理可知:该method是UserService接口的doSomething方法,method.getDeclaringClass()获取的是UserSevice接口,在doSomething方法与UserService接口上并未使用Transaction注解,所以没打印事务信息。

    三、解决方案

      根据问题分析,可以得出以下两种解决方案:

      1、在UserService接口上或者UserService接口中的doSomething上标注Transaction注解

    /**
     * 描述 用户服务接口类
     *
     * @author lwb
     * @since 2020/7/8
     */
    public interface UserService {
    
        @Transactional
        void doSomething(String thing);
    }
    
    /**
     * 描述 用户服务实现类
     *
     * @author lwb
     * @since 2020/7/8
     */
    public class UserServiceImpl implements UserService {
        @Override
        public void doSomething(String thing) {
            System.out.println("do something : " + thing);
        }
    }
    View Code

         测试结果

         

      测试通过,符合预期。这种将注解标注在接口中,则所有的实现类,都会进行增强

      2、修改代理工厂方法,获取实现类与实现类的方法,判断是否标注注解

       业务代码还原:去除接口方法上的注解,将注解添加到实现类的方法上

    /**
     * 描述 用户服务接口类
     *
     * @author lwb
     * @since 2020/7/8
     */
    public interface UserService {
    
        void doSomething(String thing);
    }
    
    
    
    /**
     * 描述 用户服务实现类
     *
     * @author lwb
     * @since 2020/7/8
     */
    public class UserServiceImpl implements UserService {
        @Override
        @Transactional
        public void doSomething(String thing) {
            System.out.println("do something : " + thing);
        }
    }
    View Code

      修改代理工厂的方法

    public static Object getJdkProxy(Object object){
            return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(),
                    (proxy, method, args) -> {
                        Object result;
                        Method targetMethod = object.getClass().getMethod(method.getName(), method.getParameterTypes());
                        if(method.isAnnotationPresent(Transactional.class) || method.getDeclaringClass().isAnnotationPresent(Transactional.class)
                                || object.getClass().isAnnotationPresent(Transactional.class) || targetMethod.isAnnotationPresent(Transactional.class)){
                            try {
                                System.out.println("开启事务");
                                result = method.invoke(object, args);
                                System.out.println("提交事务");
                            }catch (Exception e){
                                System.out.println("回滚事务");
                                throw e;
                            }
                        }else{
                            result = method.invoke(object, args);
                        }
    
                        return result;
                    });
        }
    View Code

     测试结果

    测试通过,符合预期。标注在实现类上的注解,只在实现类生效

  • 相关阅读:
    FastAPI项目实战: 个人博客项目的API
    Jmeter分布式执行,java.rmi.UnmarshalException: xxxAbstractSimpleThreadGroup错误
    [转]JMeter分布式的坑
    Docker菜鸟教程-硬敲系列
    VMware EXIS 安装
    2020简单总结
    07.1 迭代器、生成器
    locust 的 ‘1’ 版本时代变化
    移动端专项测试-内存泄漏
    乘风破浪的不止姐姐,还有我们的测试工程师!
  • 原文地址:https://www.cnblogs.com/damon-blogs/p/13266132.html
Copyright © 2020-2023  润新知