Java中的代理 (静态代理+动态代理)
在我们要新增功能或者提高代码扩展性的时候可能需要用到代理。
比如在不改变已有代码的情况下,需要增加统计每个方法耗时的功能,怎么解决?
其中一个办法就是通过代理实现。常用的有静态代理和动态代理。
(1) 静态代理
静态代理其实就是设计模式里的代理模式。
代理模式是一种设计模式,提供了对目标对象额外的访问方式,即通过代理对象访问目标对象,这样可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。
已有类 UserService
、UserServiceImpl
,新增类 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);
}
}
在已有UserService
和UserServiceImpl
的情况下,增加了UserServiceProxy
,在使用时把UserServiceImpl代理到UserServiceProxy,即可达到不改变原有代码逻辑并且增加统计方法耗时的功能。
(1.1) 静态代理的优缺点
优点
只要代理对象对目标对象进行包装,即可实现增强功能;
缺点
1、冗余。静态代理只能为一个目标对象服务,如果目标对象过多,则会产生很多代理类。
2、不易维护。一旦接口增加方法,目标对象与代理对象都要进行修改。
(2) 动态代理
动态代理指动态地在内存中构建代理对象,从而实现对目标对象的代理功能。
常用的动态代理有 JDK Proxy
、 CGLib
和 Javassist
静态代理与动态代理的区别
静态代理在编译时就已经实现,编译完成后代理类是一个实际的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