无法从JDK动态代理获取方法注释

时间:2017-04-01 02:42:56

标签: java dynamic-proxy

我在网上搜索了很长时间。但没用。请帮助或尝试提供一些如何实现此目的的想法

在我的客户端,我可以使用我的Real Object获取我的方法的注释,但不能使用代理对象。

我曾经认为这是因为我自己的Annotation上缺少Retention Annotation。但它不起作用。这是代码:

注释:

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {}

接口

public interface UserDao {
    void save();
    void update();
}

实施

public class UserDaoImpl implements UserDao{
    @Override
    @MyAnnotation
    public void save() {
        System.out.println("save");
    }

    @Override
    public void update() {
        System.out.println("update");
    }
}

客户端+驱动程序应用程序:

public class Client {
    public static void main(String[] args) {
        final UserDaoImpl dao = new UserDaoImpl();
        Method[] methods = dao.getClass().getMethods();
        for (Method m : methods){
            if(m.getAnnotation(MyAnnotation.class) != null)
                System.out.println("save method is enhanced");
        }

        UserDao proxy = (UserDao) Proxy.newProxyInstance(
            dao.getClass().getClassLoader(),
            dao.getClass().getInterfaces(),
            new InvocationHandler() {
                    @Override
                    public Object invoke(Object obj, Method method, Object[] args) throws Throwable {
                        obj = dao;
                        if(method.getAnnotation(MyAnnotation.class) != null)
                            System.out.println("save method is enhanced2");
                        Object result = method.invoke(obj, args);
                        return result;
                    }
            }
        );

        proxy.save();
        System.out.println("-----------");
        proxy.update();
    }
}

1 个答案:

答案 0 :(得分:1)

动态代理直接实现给定的接口,它不会像你想象的那样扩展实现类。我修复了你的非编译代码,添加了一些日志输出并将所有类移到我的一个包中,因为Java编译器不喜欢默认包。

驱动程序应用程序(已更新):

package de.scrum_master.so;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class Client {
  public static void main(String[] args) {
    final UserDaoImpl dao = new UserDaoImpl();
    Method[] methods = dao.getClass().getMethods();
    for (Method method : methods) {
      if (method.getAnnotation(MyAnnotation.class) != null) {
        System.out.println(method + " -> " + method.getDeclaringClass());
        System.out.println("impl method is enhanced");
      }
    }
    System.out.println("-----------");

    UserDao proxy = (UserDao) Proxy.newProxyInstance(
      dao.getClass().getClassLoader(),
      dao.getClass().getInterfaces(),
      new InvocationHandler() {
        @Override
        public Object invoke(Object obj, Method method, Object[] args) throws Throwable {
          obj = dao;
          System.out.println(method + " -> " + method.getDeclaringClass());
          if (method.getAnnotation(MyAnnotation.class) != null)
            System.out.println("proxy method is enhanced");
          Object result = method.invoke(obj, args);
          return result;
        }
      });

    proxy.save();
    System.out.println("-----------");
    proxy.update();
  }
}

控制台日志:

public void de.scrum_master.so.UserDaoImpl.save() -> class de.scrum_master.so.UserDaoImpl
impl method is enhanced
-----------
public abstract void de.scrum_master.so.UserDao.save() -> interface de.scrum_master.so.UserDao
save
-----------
public abstract void de.scrum_master.so.UserDao.update() -> interface de.scrum_master.so.UserDao
update

您可以在此处查看UserDaoImpl与动态UserDao代理之间的区别。

我不确定您为什么用springaop标记此问题,因为在您的示例代码中没有AOP而没有Spring,但我想您想要使用Spring AOP,它也基于动态代理。在那里,您可以选择强制执行接口的组件也将创建为CGLIB代理,即代理直接扩展其基类。也许它有效,但我不确定CGLIB代理如何工作。如果它们将覆盖每个方法,则会应用JVM限制:方法注释永远不会被重写方法继承。

非常适合您的技术问题。但你真正想要实现的目标是什么?这是许多人在这里犯的错误:他们没有描述他们想要实现的目标,但已经考虑到了可能不合适的技术解决方案。这样他们就会错过解决问题的其他选择。通常,应用程序设计基于其错误的假设存在缺陷,如果他们不太注重解决方案偏差,则可以轻松修复。也许如果这对您没有帮助,您可以创建一个新问题,将其链接到此问题,其中显示您的Spring AOP问题并描述您想要实现的目标。

更新:如果要从代理的调用处理程序访问原始(“真实”)对象,可以这样做:

改进了驱动程序应用程序:

package de.scrum_master.so;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class Client {
  public static void main(String[] args) {
    final UserDaoImpl dao = new UserDaoImpl();
    Method[] methods = dao.getClass().getMethods();
    for (Method method : methods) {
      if (method.getAnnotation(MyAnnotation.class) != null) {
        System.out.println(method + " -> " + method.getDeclaringClass());
        System.out.println("impl method is enhanced");
      }
    }
    System.out.println("-----------");

    UserDao proxy = (UserDao) Proxy.newProxyInstance(
      dao.getClass().getClassLoader(),
      dao.getClass().getInterfaces(),
      new MyInvocationHandler(dao)
    );

    proxy.save();
    System.out.println("-----------");
    proxy.update();
  }

  private static class MyInvocationHandler implements InvocationHandler {
    private Object realObject;

    public MyInvocationHandler(Object realObject) {
      this.realObject = realObject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      System.out.println(method + " -> " + method.getDeclaringClass());
      if (method.getAnnotation(MyAnnotation.class) != null)
        System.out.println("proxy method is enhanced");
      if (realObject.getClass().getMethod(method.getName(), method.getParameterTypes()).getAnnotation(MyAnnotation.class) != null)
        System.out.println("real object method is enhanced");
      return method.invoke(realObject, args);
    }

  }
}

控制台日志:

public void de.scrum_master.so.UserDaoImpl.save() -> class de.scrum_master.so.UserDaoImpl
impl method is enhanced
-----------
public abstract void de.scrum_master.so.UserDao.save() -> interface de.scrum_master.so.UserDao
real object method is enhanced
save
-----------
public abstract void de.scrum_master.so.UserDao.update() -> interface de.scrum_master.so.UserDao
update

请注意第real object method is enhanced行。