java的动态代理小记

1、静态代理与动态代理区别

java class文件加载

一个正常的java工程,是在编辑器里编辑代码,通过编译生成class文件,存储在磁盘中。这些class文件是二进制文件,JVM虚拟机通过读取这些字节码文件,取出二进制数据,加载到内存中,解析.class文件内的信息,生成对应的class对象。

代理模式

代理模式是一种设计模式,提供了对目标对象另外的访问方式。即通过代理对象访问目标对象,这样,在代理对象添加额外操作,可以扩展目标对象的功能。java中可以分为静态代理和动态代理。

静态代理

需要为每一个目标对象都在代码阶段实现一个代理对象。即在系统运行时,此代理对象已经存在,这样会导致我们的系统的类数量繁多,不易维护。

动态代理

为了避免静态代理的问题,可以动态的创建代理。在运行阶段,需要代理的地方,可以根据目标类和目标类的接口,动态的穿件一个代理,这个动态代理不会存在class文件,在用完之后就会销毁,这样就避免系统中类冗余繁多的问题。
spring的两大核心思想是Ioc(控制反转)和AOP(切面编程)。AOP主要通过java的动态代理和cglib代理实现。动态代理需要所代理的目标类要有接口。而cglib是通过继承所代理的目标类来实现,一般情况下优先使用java的动态代理。

2、 jdk动态代理实例

下面通过一个例子说明动态代理的实现以及需要注意的事项。
UserService对于一个增删改查的接口。
    
    public interface UserService {
    
    void insert();
    
    void update();
    
    void query();
    
    void delete();

}
    

而UserServiceImpl是上述接口的实现

public class UserServiceImpl implements UserService {

    /* (non-Javadoc)
     * @see com.test.aop.dongtaidaili.UserService#insert()
     */
    @Override
    public void insert() {
        // TODO Auto-generated method stub
        
        System.out.println("插入");

    }

    /* (non-Javadoc)
     * @see com.test.aop.dongtaidaili.UserService#update()
     */
    @Override
    public void update() {
        // TODO Auto-generated method stub
        
        System.out.println("更新");

    }

    /* (non-Javadoc)
     * @see com.test.aop.dongtaidaili.UserService#query()
     */
    @Override
    public void query() {
        // TODO Auto-generated method stub
        
        System.out.println("查询");

    }

    /* (non-Javadoc)
     * @see com.test.aop.dongtaidaili.UserService#delete()
     */
    @Override
    public void delete() {
        // TODO Auto-generated method stub
        System.out.println("删除");

    }

}

我们需要对UserServiceImpl进行动态代理,增强其内部方法。

public class UserServiceProxy implements InvocationHandler{
    
    /**
     * @param us
     */
    public UserServiceProxy(Object us) {
        super();
        this.us = us;
    }

    private Object us;
    
    public Object getUserServiceProxy(){
        
            Object newProxyInstance =Proxy.newProxyInstance(UserServiceProxy.class.getClassLoader(),
                    us.getclass().getInterfaces(), this);
        
        
        
        return newProxyInstance;
    }

    /* (non-Javadoc)
     * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // TODO Auto-generated method stub
        System.out.println("事务开始");  //对目标类的方法进行代码增强
          System.out.println("Method:" + method);
          System.out.println("proxy:" + proxy.getClass());
        
        Object invoke = method.invoke(us, args);
        System.out.println("事务提交");  //对目标类的方法进行代码增强
        
        return invoke;
    }

}

动态代理的代理类有两个关键点:
一是要实现InvocationHandler接口。这个接口有一个方法,当我们通过动态代理类调用方法时,就会转而调用这个代理类的invoke方法。
public Object invoke(Object proxy, Method method, Object[] args)
在这个方法里,我们并进行相关代码增强(即实现相关权限检查,事务等逻辑)。三个参数

Object proxy:**是被代理的目标类。比如上述的例子中,就是我们传入的UserServiceImpl的类的实例。而不是代理类本身。  如果我们传入代理类本身,会发现结果是不断递归调用,直到栈溢出。这是需要注意的。**
Method method:代理类的方法
Object[] args:代理类的方法的参数

代理类的另一个关键是Proxy,用于动态生成代理对象的类。其常用方法

 Object java.lang.reflect.Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 
 
 ClassLoader loader:类加载器,可以用代理类或者被代理类的类加载器(其实是同一个)
  Class[] interfaces:被代理类的接口数组
  InvocationHandler h:代理类,指向UserServiceProxy本身
 

最后,看结果验证:

public class Test {
    
    @org.junit.Test
    public void test(){
        
        UserService userService=new UserServiceImpl();
        ServiceProxy proxy=new ServiceProxy(userService);
        Object userServiceProxy = proxy.getUserServiceProxy();
        System.out.println("userServiceProxy is "+userServiceProxy.getClass().getName());
        ((UserService) userServiceProxy).delete();
        
    }

}
结果:
userServiceProxy is com.sun.proxy.$Proxy4
事务开始
Method:public abstract void com.test.aop.dongtaidaili.UserService.delete()
proxy:class com.sun.proxy.$Proxy4
删除
事务提交

结果分析:
1、userServiceProxy is com.sun.proxy.$Proxy4 表明 Proxy.newProxyInstance 创建的代理对象是在jvm运行时动态生成的一个对象。
2、事务开始、事务提交是我们对被代理对象的方法增强内容,其在代理类的invoke()方法中实现。
3、在invoke方法中 System.out.println("Method:" + method); 其结果是Method:public abstract void com.test.aop.dongtaidaili.UserService.delete() 表明invoke的method就是被代理对象的方法
4、System.out.println("userServiceProxy is "+userServiceProxy.getClass().getName());的结果是proxy:class com.sun.proxy.$Proxy4,表明invoke的第一个参数Object proxy是代理对象。

5、Object newProxyInstance = Proxy.newProxyInstance(ServiceProxy.class.getClassLoader(),us.getClass().getInterfaces(), this);这个生成代理对象,传入了类加载器、代理类的接口以及自己。在调用代理对象的方法时,其内部是 this.h.invoke(this, m3, null);其中m3就是被代理对象的方法。所以我们在代理类中的invoke方法中实现代理逻辑,并且最终调用目标对象的方法。代理类中药传入目标对象的实现,也是为了在这里使用。即Object invoke = method.invoke(us, args);这里的us为目标对象,通过反射调用目标对象的方法。
6、由于代理对象是extends了Proxy,因此其只能对接口进行代理

3、jdk动态代理的原理

根据第二节的举例,实现动态代理的实例就是newProxyInstance :

    private Object us;
    
    public Object getUserServiceProxy(){
        
            Object newProxyInstance =Proxy.newProxyInstance(UserServiceProxy.class.getClassLoader(),
                    us.getclass().getInterfaces(), this);
        
        
        
        return newProxyInstance;
    }

通过字面意思,可以理解为通过目标类的接口集合,生成代理类,其中的this是指实现了InvocationHandler接口的实例,通过第二节,知道所有的动态代理类的方法访问,最终都会回到它的 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {}方法。

查看Proxy.newProxyInstance的源码

    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, interfaces);
        }

        /*
         * Look up or generate the designated proxy class.
         * 这里是生成了动态代理的类,并进行了加载,返回的cl应该类似于下面
         * Class clazz = classLoader.defineMyClass(result, 0, read);
         * 即已经被jvm加载了
         */
        Class cl = getProxyClass0(loader, interfaces);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }
      //constructorParams是指包含InvocationHandler.class一个元素的数组
      //这里就是生成了以InvocationHandler为输入参数的构造方法
            final Constructor cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            //返回了以InvocationHandler为入参的构造方法进行了动态代理类的实例化
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

上述的重点都在注释中标注了,就是动态代理类的生成及加载,然后生成一个以InvocationHandler为入参的构造方法的实例。所以,重点在于如何生成这个动态代理类,或者说这个动态代理类到底长什么样?
JDK提供了sun.misc.ProxyGenerator.generateProxyClass(String proxyName,class[] interfaces) 底层方法来产生动态代理类的字节码,因此,可以定义一个工具:

public class ProxyUtils {
    
    /* 
     * 将根据类信息 动态生成的二进制字节码保存到硬盘中, 
     * 默认的是clazz目录下 
         * params :clazz 需要生成动态代理类的类 
         * proxyName : 为动态生成的代理类的名称 
         */  
    public static void generateClassFile(Class clazz,String proxyName)  
    {  
        //根据类信息和提供的代理类名称,生成字节码  
        byte[] classFile = ProxyGenerator.generateProxyClass(proxyName, clazz.getInterfaces());   
        String paths = clazz.getResource(".").getPath();  
        System.out.println(paths);  
        FileOutputStream out = null;    
          
        try {  
            //保留到硬盘中  
            out = new FileOutputStream(paths+proxyName+".class");    
            out.write(classFile);    
            out.flush();    
        } catch (Exception e) {    
            e.printStackTrace();    
        } finally {    
            try {    
                out.close();    
            } catch (IOException e) {    
                e.printStackTrace();    
            }    
        }    
    }  

}

然后,在我们的动态代理类的定义中,加入

    public Object getUserServiceProxy(){
        
        Object newProxyInstance =  Proxy.newProxyInstance(ServiceProxy.class.getClassLoader(),
                    us.getClass().getInterfaces(), this);
        //加入这一句,将动态代理的class文件存入硬盘
        ProxyUtils.generateClassFile(us.getClass(), "xxxService");  
        
        
        
        return newProxyInstance;
    }

。然后,通过反编译工具如jd-gui.exe 打开:

import com.test.aop.proxy.UserService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class xxxService extends Proxy
  implements UserService
{
  private static Method m1;
  private static Method m5;
  private static Method m6;
  private static Method m2;
  private static Method m4;
  private static Method m0;
  private static Method m3;

  public xxxService(InvocationHandler paramInvocationHandler)
    throws 
  {
    super(paramInvocationHandler);
  }

  public final boolean equals(Object paramObject)
    throws 
  {
    try
    {
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final void insert()
    throws 
  {
    try
    {
      this.h.invoke(this, m5, null);
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final void query()
    throws 
  {
    try
    {
      this.h.invoke(this, m6, null);
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final String toString()
    throws 
  {
    try
    {
      return (String)this.h.invoke(this, m2, null);
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final void delete()
    throws 
  {
    try
    {
    //都是转而调用InvocationHandler 的invoke方法实现
      this.h.invoke(this, m4, null);
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final int hashCode()
    throws 
  {
    try
    {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final void update()
    throws 
  {
    try
    {
      this.h.invoke(this, m3, null);
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  static
  {
    try
    {
//为每一个需要方法对象,当调用相应的方法时,分别将方法对象作为参数传递给InvocationHandler处理  
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m5 = Class.forName("com.test.aop.proxy.UserService").getMethod("insert", new Class[0]);
      m6 = Class.forName("com.test.aop.proxy.UserService").getMethod("query", new Class[0]);
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      m4 = Class.forName("com.test.aop.proxy.UserService").getMethod("delete", new Class[0]);
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      m3 = Class.forName("com.test.aop.proxy.UserService").getMethod("update", new Class[0]);
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
  }
}

从反编译的动态代理类可知:
1、xxxService extends Proxy implements UserService,因此,jdk的动态代理类的目标类需要实现接口,因为我们的动态代理类已经继承了Proxy这个类,不能再继承其他类,那么动态代理类和目标类产生关联,只能通过接口了。(类与类之间产生关系的可以通过实现功能接口或者继承实现。)
2、动态代理类对于接口的方法实现,均是调用InvocationHandler的public Object invoke(Object proxy, Method method, Object[] args) 方法实现。

你可能感兴趣的:(java的动态代理小记)