java的动态代理无法获取实现类上的注解(问题解决记录)
问题描述
使用
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
方法,
创建代理对象时,无法获取实现类上的注解,只能获取接口上的注解
原因
使用 newProxyInstance
方法时,该对象是一个实现了 指定接口 的匿名类(我的理解)。
因此,无法去获取到指定要被代理的类的相关注解,即注解只有声明在接口的相关方法上才能被发现并起效果的原因。
这也是为什么返回类型无法强转为代理时声明的实现类的类型的原因,因为这两压根不是继承关系,但是可以使用接口类型来接收返回值。
解决方法(这个看了应该就明白了)
可以通过传入被代理的实现类的 Class,来解决。我们直接通过该 Class 对象,反射获取它的方法和类以及变量,这样就可以直接获取到相应的注解,然后,修改对应的方法即可。
这个问题我是这样想的,首先你肯定是能知道代理对象的类的,不然你如何写代理,所以这个解决方法应该不存在不行的问题。
示例代码
注解类
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Aspect {
Class[] types();
}
接口类
public interface IOrder {
void pay() throws InterruptedException;
void show();
}
要被代理的实现类
@Aspect(types = TimeUsageIAspect.class)
public class Order implements IOrder {
private int status = 0;
@Override
@Aspect(types = {TimeUsageIAspect.class})
public void pay() throws InterruptedException {
// 模拟支付耗时
Thread.sleep(50);
this.status = 1;
}
@Override
public void show() {
System.out.println("status: " + this.status);
}
public static void main(String[] args) throws InterruptedException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
// 实现代理的方法,传入实现类的 Class
IOrder order = ObjectFactory.newInstance2(Order.class);
order.pay();
order.show();
}
}
ObjectFactory类
public class ObjectFactory {
public static <T> T newInstance(Class<T> cls) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
// 存放所有的切面类
LinkedList<IAspect> aspects = new LinkedList<>();
if (cls.isAnnotationPresent(Aspect.class)) {
Class[] types = cls.getAnnotation(Aspect.class).types();
for (Class type : types) {
IAspect aspect = (IAspect) type.getConstructor().newInstance();
aspects.push(aspect);
}
}
T target = cls.getConstructor().newInstance();
return (T) Proxy.newProxyInstance(
cls.getClassLoader(),
cls.getInterfaces(),
(proxy, method, args) -> {
for (IAspect aspect : aspects) {
aspect.before();
}
Object result = method.invoke(target, args);
for (IAspect aspect : aspects) {
aspect.after();
}
return result;
}
);
}
public static <T> T newInstance2(Class<T> cls) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
// 记录方法与相关处理的映射之间的关系
HashMap<String,LinkedList<IAspect> > methodAspectMap = new HashMap<>();
Method[] methods = cls.getMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(Aspect.class)) {
Class[] types = method.getAnnotation(Aspect.class).types();
LinkedList<IAspect> aspects = new LinkedList<>();
for (Class type : types) {
IAspect aspect = (IAspect) type.getConstructor().newInstance();
aspects.push(aspect);
}
methodAspectMap.put(method.getName(), aspects);
}
}
T target = cls.getConstructor().newInstance();
return (T) Proxy.newProxyInstance(
cls.getClassLoader(),
cls.getInterfaces(),
(proxy, method, args) -> {
// 根据方法名,执行对应注解的该干的事
if (methodAspectMap.containsKey(method.getName())) {
LinkedList<IAspect> aspects = methodAspectMap.get(method.getName());
for (IAspect aspect : aspects) {
aspect.before();
}
Object result = method.invoke(target, args);
for (IAspect aspect : aspects) {
aspect.after();
}
return result;
}
return method.invoke(target, args);
}
);
}
}
题外话
这个示例代码,大部分是我学其他教程时写的,然后边学边瞎改的就遇到了这个问题,百度又没百度到方法(这个不用想,肯定是姿势不对),灵机一动就想到了这个办法,记录一下。代码肯定有许多莫名其妙的地方,别去管就完事。比如这方法的注解判断,感觉我整的好烂。