代理是什么?
代理是一种设计理念,是一种设计思想。
对被代理的目标类进行增强。
比如,我有类A,B,C,现在需要对A、B、C类中的方法进行增强,每次调用三个类中的方法时,都会自动打印日志"target被增强了"。
而代理就可以实现对被代理的目标类的增强。
代理有两种实现,静态代理和动态代理。
静态代理是什么?
在程序编译前,我们就手动地将最终的代理类写好。
假设有接口IAservice,类Aservice实现IAservice。现在要对类Aservice进行增强,则需要写一个代理类AserviceProxy,实现IAservice,实现所有方法,并在方法中进行增强,然后再调用Aservice的原始方法。
代码如下
/**
* 测试类
*/
public class MyStaticProxyTest {
public static void main(String[] args) {
IBservice bservice = new MyStaticProxy(new Bservice());
bservice.say();
/*
打印结果如下:
target被增强了
你好,我是Bservice,实现了IBservice
*/
}
}
/**
* 目标类的接口
*/
interface IBservice {
void say();
}
/**
* 目标类,需要被增强的类。
*/
class Bservice implements IBservice {
@Override
public void say() {
System.out.println("你好,我是Bservice,实现了IBservice");
}
}
/**
* 静态代理类,需要和被代理类{@link Bservice}实现相同的接口{@link IBservice},然后在和目标类的相同方法中,调用目标类的相同方法。
*/
class MyStaticProxy implements IBservice {
/**
* 被代理的目标类,{@link MyStaticProxy}就是对{@link this#target}进行增强
*/
private final IBservice target;
public MyStaticProxy(IBservice target) {
this.target = target;
}
@Override
public void say() {
System.out.println("target被增强了");
// 在代理类的say方法中调用目标类的say方法
target.say();
}
}
动态代理是什么?
在程序编译打包完成后,都没有生成代理类,而是在运行时,动态地生成代理类的字节码文件,所以叫做动态代理。
假设有接口IAservice,类Aservice实现IAservice。现在要对类Aservice进行增强,则AserviceProxy则是在程序运行的时候,动态生成。
我们只需要定义动态代理类,并制定动态代理类对哪些类进行代理即可。
JDK动态代理有哪些应用场景?
- Spring AOP中的事务、过滤器、拦截器等。
如何使用JDK动态代理?
代码如下
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class MyJdkDynamicProxy1 {
public static void main(String[] args) {
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
/*使用自定义的动态代理类,对Aservice对象进行代理。
需要注意的是,必须要使用IAservice接口作为引用,否则会报错,比如,使用Aservice来接口,就会报错。
因为JDK动态代理是通过实现被代理类实现的接口的方式,所以必须要用接口作为引用。
*/
IAservice strings = new MyJdkDynamicProxy(new Aservice()).getProxy();
strings.say();
/*
打印结果如下:
target被增强了
你好,我是Aservice,实现了IAservice
解释:调用了say接口的方法,打印了一次target被增强了。
而该打印语句正是{@link MyJdkDynamicProxy#invoke()}方法中对原方法的增强。
fa
*/
}
}
interface IAservice {
void say();
}
class Aservice implements IAservice {
@Override
public void say() {
System.out.println("你好,我是Aservice,实现了IAservice");
}
public void run() {
System.out.println("我在奔跑");
}
}
/**
* JDK动态代理类,需要实现{@link InvocationHandler}接口,
* 然后实现{@link InvocationHandler#invoke(Object, Method, Object[])}方法,
*/
class MyJdkDynamicProxy implements InvocationHandler {
/**
* 被代理的目标类,{@link MyJdkDynamicProxy}就是对{@link this#target}进行增强
*/
private final Object target;
public MyJdkDynamicProxy(Object target) {
this.target = target;
}
/**
* 获取代理对象。
*
* @param <T> 方法泛型。此处方法泛型代表方法的返回类型是什么,方法泛型就是什么。
* @return 代理之后的对象。
*/
public <T> T getProxy() {
/*创建代理对象,并对被代理对象的所有接口的方法进行代理。target.getClass().getInterfaces()就是目标类的所有接口*/
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
/**
* 此方法中,可以对调用对象的方法进行增强,下面的代码中,System.out.println("target被增强了");就是对原来的方法进行增强。
* method.invoke(target, args);代码就是对原来方法的调用。
*
* @return 返回原方法调用结果
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("target被增强了");
return method.invoke(target, args);
}
}
JDK动态代理是如何实现的?
- 如何创建代理类
在运行时动态生成代理类的字节码文件,并通过反射调用代理类的构造器,动态创建代理类。
查看java.lang.reflect.Proxy#newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)方法。
看该方法是如何创建代理类的。
以下代码中,只挑了关键部分。
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
{
final Class<?>[] intfs = interfaces.clone();
/*
* Look up or generate the designated proxy class.
* 在运行时动态地生成代理类
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
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) {//...省略异常处理代码
} catch (InvocationTargetException e) {//...省略异常处理代码
} catch (NoSuchMethodException e) {//...省略异常处理代码
}
}
查看Class<?> cl = getProxyClass0(loader, intfs)方法
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);
}
查看proxyClassCache.get(loader, interfaces)方法
/**
* Look-up the value through the cache. This always evaluates the
* {@code subKeyFactory} function and optionally evaluates
* {@code valueFactory} function if there is no entry in the cache for given
* pair of (key, subKey) or the entry has already been cleared.
*
* @param key possibly null key
* @param parameter parameter used together with key to create sub-key and
* value (should not be null)
* @return the cached value (never null)
* @throws NullPointerException if {@code parameter} passed in or
* {@code sub-key} calculated by
* {@code subKeyFactory} or {@code value}
* calculated by {@code valueFactory} is null.
*/
public V get(K key, P parameter) {
Objects.requireNonNull(parameter);
expungeStaleEntries();
Object cacheKey = CacheKey.valueOf(key, refQueue);
// lazily install the 2nd level valuesMap for the particular cacheKey
ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
if (valuesMap == null) {
ConcurrentMap<Object, Supplier<V>> oldValuesMap
= map.putIfAbsent(cacheKey,
valuesMap = new ConcurrentHashMap<>());
if (oldValuesMap != null) {
valuesMap = oldValuesMap;
}
}
// create subKey and retrieve the possible Supplier<V> stored by that
// subKey from valuesMap
// subKeyFactory.apply(key, parameter)会生成代理类的字节码
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
// ...省略代码
}
按住键盘Ctrl+Alt+鼠标左键点击,查看ProxyClassFactory中的方法
查看subKeyFactory.apply(key, parameter)
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
/*
* Generate the specified proxy class.
* 这里已经生成了字节码文件
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
/*
*这是一个native方法:private static native Class<?> defineClass0(ClassLoader loader, String name,
byte[] b, int off, int len);
*/
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
}
}
}
查看代码byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
final byte[] var4 = var3.generateClassFile();
/*
* saveGeneratedFiles如果是true,则会将生成的字节码文件写入磁盘。
* 通过查看 Ctrl+鼠标左键查看saveGeneratedFiles
* private static final boolean saveGeneratedFiles = (Boolean)AccessController.doPrivileged(new GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"));
* 得知,可以通过sun.misc.ProxyGenerator.saveGeneratedFiles参数对saveGeneratedFiles进行设置,
* 则可以通过如下代码对该参数进行设置
*/
if (saveGeneratedFiles) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
try {
int var1 = var0.lastIndexOf(46);
Path var2;
if (var1 > 0) {
Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
Files.createDirectories(var3);
var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
} else {
var2 = Paths.get(var0 + ".class");
}
Files.write(var2, var4, new OpenOption[0]);
return null;
} catch (IOException var4x) {
throw new InternalError("I/O exception saving generated file: " + var4x);
}
}
});
}
return var4;
}
- 查看动态生成的代理类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
/**
* 类名为$Proxy0,JDK动态代理动态生成的类,类名一般是$符号开头,后面是Proxy0,我猜如果有多哥代理类,则是$Proxy1、$Proxy2...
* 继承了Proxy类
*/
final class $Proxy0 extends Proxy implements IAservice {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
/**重写了equals方法*/
public final boolean equals(Object var1) throws {
try {
// h是动态代理对象,即Main方法中创建的MyDynamicProxy的对象,
// 而m1是equals方法,这里就是通过反射调用目标类的equals方法,下面的toString、hashCode方法同理。
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void say() throws {
try {
// 通过反射调用h的invoke方法,而invoke方法对m3方法进行调用。
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
/*
* 在静态代码快中,对m0、m1、m2、m3进行复制,
* 而m3正是目标类的say方法。
*/
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("com.lhy.java.base.dynamic_proxy.IAservice").getMethod("say");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}