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

项目结构

完整代码: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;
原文地址:https://www.cnblogs.com/ssslinppp/p/7576226.html