Java中的代理 (静态代理+动态代理)

 在我们要新增功能或者提高代码扩展性的时候可能需要用到代理。
 比如在不改变已有代码的情况下,需要增加统计每个方法耗时的功能,怎么解决?

 其中一个办法就是通过代理实现。常用的有静态代理和动态代理。  

(1) 静态代理

 静态代理其实就是设计模式里的代理模式。

 代理模式是一种设计模式,提供了对目标对象额外的访问方式,即通过代理对象访问目标对象,这样可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。
代理模式  

 已有类 UserServiceUserServiceImpl,新增类 UserServiceProxy

public interface UserService {

    String getUserNameById(Long id);

}
public class UserServiceImpl implements UserService {

    @Override
    public String getUserNameById(Long id) {
        System.out.println("--getUserNameById--");
        return "lisi";
    }

}
public class UserServiceProxy implements UserService {

    private UserService target;

    public UserServiceProxy(UserService userService) {
        this.target = userService;
    }

    @Override
    public String getUserNameById(Long id) {
        long startTime = System.currentTimeMillis();
        String userName = target.getUserNameById(id);
        long costTime = System.currentTimeMillis() - startTime;
        System.out.println("cost " + costTime + "ms");
        return userName;
    }

}
public class UserServiceProxyTest {

    public static void main(String[] args) {
        UserService target = new UserServiceImpl();
        UserServiceProxy proxy = new UserServiceProxy(target);
        proxy.getUserNameById(1L);
    }

}

 在已有UserServiceUserServiceImpl的情况下,增加了UserServiceProxy,在使用时把UserServiceImpl代理到UserServiceProxy,即可达到不改变原有代码逻辑并且增加统计方法耗时的功能。  

(1.1) 静态代理的优缺点

优点
 只要代理对象对目标对象进行包装,即可实现增强功能;

缺点

1、冗余。静态代理只能为一个目标对象服务,如果目标对象过多,则会产生很多代理类。
2、不易维护。一旦接口增加方法,目标对象与代理对象都要进行修改。  

(2) 动态代理

 动态代理指动态地在内存中构建代理对象,从而实现对目标对象的代理功能。
 常用的动态代理有 JDK ProxyCGLibJavassist
 
静态代理与动态代理的区别

静态代理在编译时就已经实现,编译完成后代理类是一个实际的class文件。
动态代理是在运行时动态生成的,即编译完成后没有实际的class文件,而是在运行时动态生成类字节码,并加载到JVM中。

(3) 动态代理-JDK Proxy

用法1 代理接口

public static void main(String[] args) {
    // 实际执行类
    UserService target = new UserServiceImpl();
    // 需要增加的功能
    InvocationHandler invocationHandler = new UserServiceInvocationHandler(target);

    ClassLoader loader = target.getClass().getClassLoader();
    Class<?>[] interfaces = target.getClass().getInterfaces();
    // 生成代理类
    UserService userServiceProxy = (UserService) Proxy.newProxyInstance(loader, interfaces, invocationHandler);
    String userName = userServiceProxy.getUserNameById(1L);
    System.out.println(userName);
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class UserServiceInvocationHandler implements InvocationHandler {

    private UserService target;

    public UserServiceInvocationHandler(UserService target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = null;
        if ("getUserNameById".equals(method.getName())) {
            long startTime = System.currentTimeMillis();
            System.out.println("-----before " + method.getName() + "-----");
            // 执行被代理对象的方法
            // 这个例子里指 UserServiceImpl 的 getUserNameById 方法 
            result = method.invoke(target, args);
            System.out.println("-----after " + method.getName() + "-----");
            long costTime = System.currentTimeMillis() - startTime;
            // 这里是为了演示动态代理作用 实际项目里用日志、mq或调对应方法
            System.out.println("cost " + costTime + "ms");
        } else {
            result = method.invoke(target, args);
        }
        return result;
    }

}
-----before getUserNameById-----
--getUserNameById--
-----after getUserNameById-----
cost 1ms
lisi

   

(3) 动态代理-CGLib

 
 用到cglib包和asm包
 cglib依赖的是cglib包下的MethodInterceptor接口,每调用代理类的方法,都会调用intercept方法

<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/asm/asm -->
<dependency>
    <groupId>asm</groupId>
    <artifactId>asm</artifactId>
    <version>3.3.1</version>
</dependency>
public static void main(String[] args) {
    UserServiceMethodInterceptor methodInterceptor = new UserServiceMethodInterceptor();
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(UserServiceImpl.class);
    enhancer.setCallback(methodInterceptor);

    // 这里生成的是 xxx.UserServiceImpl$$EnhancerByCGLIB$$bf6964ea@78e67e0a
    UserServiceImpl cglibProxy = (UserServiceImpl) enhancer.create();
    String userName = cglibProxy.getUserNameById(1L);
    System.out.println(userName);
}

 

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class UserServiceMethodInterceptor implements MethodInterceptor {

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("------before " + methodProxy.getSuperName() + "------");
        long startTime = System.currentTimeMillis();
        // 被代理的
        Object resultValue = methodProxy.invokeSuper(o, objects);
        System.out.println("------after " + methodProxy.getSuperName() + "------");

        long costTime = System.currentTimeMillis() - startTime;
        //
        System.out.println("cost " + costTime + "ms");
        return resultValue;
    }

}
------before CGLIB$getUserNameById$0------
--getUserNameById--
------after CGLIB$getUserNameById$0------
cost 12ms
lisi

动态代理-Javassist

Javassist 是一个开源的生成 Java 字节码的类库,其主要优点在于简单、快速,直接使用Javassist 提供的 Java API 就能动态修改类的结构,或是动态生成类。

Javassist 也可以实现动态代理功能,底层的原理也是通过创建目标类的子类的方式实现的。

//

() JDK Proxy 和 CGlib的异同

JDK Proxy
 1、JDK Proxy:利用反射机制生成一个实现代理接口的类,在调用具体方法前调用InvokeHandler来处理。比较消耗系统性能。
 2、JDK Proxy 有一个限制,就是使用动态代理的对象必须实现一个或多个接口。

CGlib
 1、CGlib 动态代理:利用ASM(开源的Java字节码编辑库,操作字节码)开源包,将代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
 2、CGlib代理无需实现接口,通过生成类字节码实现代理,比反射稍快,不存在性能问题,但cglib会继承目标对象,需要重写方法,所以目标对象不能为final类。

() JDP Proxy源码学习

 可以看到,代理实现时用到了反射加载对应的类及生成对应的实例。

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

     final Class<?>[] intfs = interfaces.clone();
     final SecurityManager sm = System.getSecurityManager();
     if (sm != null) {
          checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
     }

     /*
     * Look up or generate the designated proxy class.
     */
     // 获取类
     Class<?> cl = getProxyClass0(loader, intfs);

     /*
     * Invoke its constructor with the designated invocation handler.
     */
     try {
          if (sm != null) {
               checkNewProxyPermission(Reflection.getCallerClass(), cl);
          }

          // 通过反射获取构造方法
          final Constructor<?> cons = cl.getConstructor(constructorParams);
          final InvocationHandler ih = h;
          // 检查类是否是public权限
          if (!Modifier.isPublic(cl.getModifiers())) {
               AccessController.doPrivileged(new PrivilegedAction<Void>() {
               public Void run() {
                    cons.setAccessible(true);
                    return null;
               }
               });
          }
          // 通过构造方法创建实例
          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);
     }
}

 

/**
* a cache of proxy classes
*/
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
     proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

/**
* Generate a proxy class.  Must call the checkProxyAccess method
* to perform permission checks before calling this.
*/
private static Class<?> getProxyClass0(ClassLoader loader,
                                        Class<?>... interfaces) {
     if (interfaces.length > 65535) {
          throw new IllegalArgumentException("interface limit exceeded");
     }

     // If the proxy class defined by the given loader implementing
     // the given interfaces exists, this will simply return the cached copy;
     // otherwise, it will create the proxy class via the ProxyClassFactory
     return proxyClassCache.get(loader, interfaces);
}

 可以看到代理类的接口不能超过65535个
 而且会缓存生成的代理类

() CGlib源码学习

源码位置:net.sf.cglib.proxy.Enhancer

/**
 * Generate a new class if necessary and uses the specified
 * callbacks (if any) to create a new object instance.
 * Uses the no-arg constructor of the superclass.
 * @return a new instance
 */
public Object create() {
    classOnly = false;
    argumentTypes = null;
    return createHelper();
}
private Object createHelper() {
    preValidate();
    Object key = KEY_FACTORY.newInstance((superclass != null) ? superclass.getName() : null,
            ReflectUtils.getNames(interfaces),
            filter == ALL_ZERO ? null : new WeakCacheKey<CallbackFilter>(filter),
            callbackTypes,
            useFactory,
            interceptDuringConstruction,
            serialVersionUID);
    this.currentKey = key;
    // 创建代理类
    Object result = super.create(key);
    return result;
}

 
源码位置: net.sf.cglib.core.AbstractClassGenerator

protected Object create(Object key) {
    try {
        ClassLoader loader = getClassLoader();
        Map<ClassLoader, ClassLoaderData> cache = CACHE;
        ClassLoaderData data = cache.get(loader);
        if (data == null) {
            synchronized (AbstractClassGenerator.class) {
                cache = CACHE;
                data = cache.get(loader);
                if (data == null) {
                    Map<ClassLoader, ClassLoaderData> newCache = new WeakHashMap<ClassLoader, ClassLoaderData>(cache);
                    data = new ClassLoaderData(loader);
                    newCache.put(loader, data);
                    CACHE = newCache;
                }
            }
        }
        this.key = key;
        Object obj = data.get(this, getUseCache());
        if (obj instanceof Class) {
            return firstInstance((Class) obj);
        }
        return nextInstance(obj);
    } catch (RuntimeException e) {
        throw e;
    } catch (Error e) {
        throw e;
    } catch (Exception e) {
        throw new CodeGenerationException(e);
    }
}

 
源码位置:net.sf.cglib.proxy.Enhancer

protected Object nextInstance(Object instance) {
    EnhancerFactoryData data = (EnhancerFactoryData) instance;

    if (classOnly) {
        return data.generatedClass;
    }

    Class[] argumentTypes = this.argumentTypes;
    Object[] arguments = this.arguments;
    if (argumentTypes == null) {
        argumentTypes = Constants.EMPTY_CLASS_ARRAY;
        arguments = null;
    }
    return data.newInstance(argumentTypes, arguments, callbacks);
}

参考资料

[1] Java三种代理模式:静态代理、动态代理和cglib代理
[2] Java实现动态代理
[3] cglib-github