项目结构

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

完整代码:​​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();
}
}

输出

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

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

存在的问题(已解决)

应该已经注意到了,包名使用zontroller而不是使用controller,目的是为了让TestController类在DynamicProxyInit之后初始化,这样才不会报错,但是这种方式肯定不能应用于真实环境中!

如果TestController初始化优先于DynamicProxyInit,则会报错:

提示找不到 IDemoOne.java接口的实例。

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

目前还没有找到解决方法

上述问题解决方案

延时初始化 @Lazy

public class TestController {
@Autowired
@Lazy
private IDemoOne demoOne;