• 【动态代理】使用动态代理解析注解原数据,获取接口信息


    项目结构

    完整代码:https://github.com/ssslinppp/dynamicproxy

    DynamicproxyApplication

    @ComponentScan("com.ssslinppp")
    @SpringBootApplication
    public class DynamicproxyApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(DynamicproxyApplication.class, args);
        }
    }
    

    自定义注解

    ClassAnnotation

    @Documented
    @Target({ElementType.TYPE})
    @Retention(RUNTIME)
    public @interface ClassAnnotation {
        String classAlias() default "";
    }
    

    MethodAnnotation

    @Documented
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MethodAnnotation {
        String methodAlias() default "";
    }
    

    ParamAnnotation

    @Documented
    @Target({ElementType.PARAMETER})
    @Retention(RUNTIME)
    public @interface ParamAnnotation {
        String value() default "";
    }
    

    自定义接口

    IDemoOne

    @ClassAnnotation(classAlias = "DemoInterfaceOne")
    public interface IDemoOne {
        @MethodAnnotation(methodAlias = "methodOne")
        public String getOne(@ParamAnnotation(value = "map") HashMap map);
    
        @MethodAnnotation(methodAlias = "methodMulti")
        public String getMulti(@ParamAnnotation(value = "name") String name, @ParamAnnotation(value = "age") int age);
    }
    

    使用动态代理实例化接口

    步骤1:获取所有声明了 ClassAnnotation注解的接口

     Set<Class<?>> clszzsWithClassAnnotataion = new Reflections("com.ssslinppp.*").getTypesAnnotatedWith(ClassAnnotation.class);
    

    步骤2:使用动态代理实例化接口

    步骤3:将动态代理实例化类动态注入Spring上下文

    // 获取所有声明了 ClassAnnotation 注解的接口和类
            Set<Class<?>> clszzsWithClassAnnotataion = new Reflections("com.ssslinppp.*").getTypesAnnotatedWith(ClassAnnotation.class);
            for (Class<?> cls : clszzsWithClassAnnotataion) {
                if (cls.isInterface()) { // Proxy动态代理仅适用于Interface,如果不是接口,需要使用CGLib实现动态代理
                    // 创建动态代理类: 步骤2
                    Object proxy = Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class<?>[]{cls}, handler);
    
                    // 将代理类注入到Spring上下文: 步骤3
                    System.out.println("注入spring上下文:" + cls.getName() + ", " + proxy.getClass().getName());
                    registerBean(cls.getName(), proxy);
                }
            }
    

    动态代理中:接口方法的具体实现

    InvocationHandler handler = new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    LinkedHashMap<String, String> params = new LinkedHashMap<>();
    
                    // 解析方法参数
                    Parameter[] parameters = method.getParameters();  // 获取所有方法参数
                    for (int i = 0; i < parameters.length; i++) {
                        // 获取方法参数注解,并取得参数注解中的原数据信息
                        ParamAnnotation param = parameters[i].getAnnotation(ParamAnnotation.class);
                        if (param != null) {
                            params.put(param.value(), String.valueOf(args[i]));
                        }
                    }
    
                    return params.toString();
                }
            };
    

    完整代码如下

    package com.ssslinppp.dynamicproxy.proxy;
    
    import com.ssslinppp.dynamicproxy.annotation.ClassAnnotation;
    import com.ssslinppp.dynamicproxy.annotation.ParamAnnotation;
    import org.reflections.Reflections;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.support.DefaultListableBeanFactory;
    import org.springframework.context.ApplicationContext;
    import org.springframework.stereotype.Component;
    
    import javax.annotation.PostConstruct;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Parameter;
    import java.lang.reflect.Proxy;
    import java.util.LinkedHashMap;
    import java.util.Set;
    
    @Component
    public class DynamicProxyInit {
    
        @Autowired
        private ApplicationContext ctx;
    
        /**
         * 将对象注入到Spring应用上下文
         *
         * @param name
         * @param obj
         */
        public void registerBean(String name, Object obj) {
            // 获取BeanFactory
            DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) ctx
                    .getAutowireCapableBeanFactory();
    
            // 动态注册bean.
            defaultListableBeanFactory.registerSingleton(name, obj);
        }
    
        @PostConstruct
        public void initDynamicProxy() {
            InvocationHandler handler = new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    LinkedHashMap<String, String> params = new LinkedHashMap<>();
    
                    // 解析方法参数
                    Parameter[] parameters = method.getParameters();  // 获取所有方法参数
                    for (int i = 0; i < parameters.length; i++) {
                        // 获取方法参数注解,并取得参数注解中的原数据信息
                        ParamAnnotation param = parameters[i].getAnnotation(ParamAnnotation.class);
                        if (param != null) {
                            params.put(param.value(), String.valueOf(args[i]));
                        }
                    }
    
                    return params.toString();
                }
            };
    
            // 获取所有声明了 ClassAnnotation 注解的接口和类
            Set<Class<?>> clszzsWithClassAnnotataion = new Reflections("com.ssslinppp.*").getTypesAnnotatedWith(ClassAnnotation.class);
            for (Class<?> cls : clszzsWithClassAnnotataion) {
                if (cls.isInterface()) { // Proxy动态代理仅适用于Interface,如果不是接口,需要使用CGLib实现动态代理
                    // 创建动态代理类
                    Object proxy = Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class<?>[]{cls}, handler);
    
                    // 将代理类注入到Spring上下文
                    System.out.println("注入spring上下文:" + cls.getName() + ", " + proxy.getClass().getName());
                    registerBean(cls.getName(), proxy);
                }
            }
        }
    }
    
    

    测试类

    TestController

    @RestController
    @RequestMapping("/proxy")
    public class TestController {
        @Autowired
        @Lazy //重要:使用延时初始化,防止动态代理后执行时,demoOne依赖注入失败
        private IDemoOne demoOne;
    
        @RequestMapping("/one")
        public String getOne() throws Exception {
            HashMap map = Maps.newHashMap();
            map.put("key1", "value1");
            map.put("key2", "value2");
            return demoOne.getOne(map).toString();
        }
    
        @RequestMapping("multi")
        public String getMulti() throws Exception {
            return demoOne.getMulti("zhangSan", 18).toString();
        }
    }
    

    输出

    存在的问题(已解决)

    应该已经注意到了,包名使用zontroller而不是使用controller,目的是为了让TestController类在DynamicProxyInit之后初始化,这样才不会报错,但是这种方式肯定不能应用于真实环境中!
    如果TestController初始化优先于DynamicProxyInit,则会报错:
    提示找不到 IDemoOne.java接口的实例。

    目前还没有找到解决方法

    上述问题解决方案

    延时初始化 @Lazy

    public class TestController {
        @Autowired
        @Lazy
        private IDemoOne demoOne;
    
  • 相关阅读:
    转:ibatis的N+1问题解决方案
    转:ibatis动态sql
    转:Spring源码分析:IOC容器
    web项目中通过spring获得ApplicationContext
    转:import static和import的区别
    python实现linux命令结果输出
    linux获取当前pts
    docker挂载本地目录
    mysql数据导入导出
    python实现linux远程操控windows执行cmd命令
  • 原文地址:https://www.cnblogs.com/ssslinppp/p/7576226.html
Copyright © 2020-2023  润新知