● 什么是代理:
顾名思义,代理就是帮别人做事情,比如代购,可能会收取一定的费用(代码中指对方法的增强)
● 什么是静态代理:
静态代理是静态的,代理类由自己手动创建,在编译期就已经生成,并且代理的目标类是确定的,也就是由代码写死的,一旦运行就不能改变。
● 什么是动态代理
与静态代理不同,动态代理的代理类运行期才生成,其代理的目标类也会根据传入的参数的改变而改变,也就是说,同一份代码,可以代理多个类(当然生成的代理类也不同),减少了代码的冗余
//卖票接口
public interface SellTickets {
void sell();
}
//火车站 火车站具有卖票功能,所以需要实现SellTickets接口
public class TrainStation implements SellTickets {
public void sell() {
System.out.println("火车站卖票");
}
}
//代售点
public class ProxyPoint implements SellTickets {
private TrainStation station = new TrainStation();
public void sell() {
System.out.println("代理点收取一些服务费用");
station.sell();
}
}
上述代码就是一个简单的静态代理,其中ProxyPoint 就是代理类,可见其代理了TrainStation类,并对TrainStation类的sell方法进行增强(收取代理费用)
不难看出,这份代码对于代理已经写死了,ProxyPoint 只能代理TrainStation类,如果要代理其他的类,需要重新建个代理类。可能会造成类爆炸。
基于接口的动态代理
jdk中提供了一个动态代理类,Proxy ,这个类 提供了一个 newProxyInstance 静态方法,可以帮助我们创建代理对象(代理类)。
通过动态代理,因为代理对象是动态生成的,所有并不需要创建代理类,只需要一个代理工厂,通过工厂来动态生产代理类即可。
newProxyInstance()方法参数说明:
ClassLoader loader : 类加载器,用于加载代理类,使用真实对象的类加载器即可
Class>[] interfaces : 真实对象所实现的接口,代理模式真实对象和代理对象实现相同的接口
InvocationHandler h : 代理对象的调用处理程序InvocationHandler中invoke方法参数说明:
proxy : 代理对象
method : 对应于在代理对象上调用的接口方法的 Method 实例
args : 代理对象调用接口方法时传递的实际参数
/**
* 代理工厂,用来创建代理对象
*/
public class ProxyFactory {
private TrainStation train = new TrainStation();
/**
* 获取动态代理对象
* @return
*/
public SellTickets getProxyObject(){
SellTickets proxyObject = (SellTickets) Proxy.newProxyInstance(train.getClass().getClassLoader(), train.getClass().getInterfaces(),
(proxy,method,args) -> {
System.out.println("方法增强,jdk动态代理模式");
Object res = method.invoke(train, args);
return res;
});
return proxyObject;
}
}
动态代理是在运行期生成代理对象,所以在代码层面是看不出代理对象的结构的。
但可以借助java诊断工具 arthas-boot.jar
,获取运行时,内存中存在的代理类
java -jar arthas-boot.jar
获取到 代理对象如下
package com.sun.proxy;
import com.rwto.structure.proxy.OtherTicket;
import com.rwto.structure.proxy.SellTickets;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0
extends Proxy
implements SellTickets,
OtherTicket {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public $Proxy0(InvocationHandler invocationHandler) {
super(invocationHandler);
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("com.rwto.structure.proxy.SellTickets").getMethod("sell", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
}
catch (NoSuchMethodException noSuchMethodException) {
throw new NoSuchMethodError(noSuchMethodException.getMessage());
}
catch (ClassNotFoundException classNotFoundException) {
throw new NoClassDefFoundError(classNotFoundException.getMessage());
}
}
public final boolean equals(Object object) {
try {
return (Boolean)this.h.invoke(this, m1, new Object[]{object});
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final String toString() {
try {
return (String)this.h.invoke(this, m2, null);
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final int hashCode() {
try {
return (Integer)this.h.invoke(this, m0, null);
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final void sell() {
try {
this.h.invoke(this, m3, null);
return;
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
}
从上面的类中,我们可以看到以下几个信息:
● 代理类($Proxy0)实现了SellTickets。也就是说真实类和代理类实现同样的接口。
● 代理类($Proxy0)将我们提供了的匿名内部类对象传递给了父类。
jdk 动态代理的执行流程如下
执行流程如下:
JDK 动态代理虽然方便,但有一个致命的局限性,那就是被代理的方法必须 在接口中有定义,也就是被代理对象必须实现了相应的接口。
对于这个局限性的补充,就出现了CGlib :基于父类的动态代理
CGLIB是第三方提供的包,所以需要引入jar包的坐标:
<dependency>
<groupId>cglibgroupId>
<artifactId>cglibartifactId>
<version>3.1version>
dependency>
代码如下
public class ProxyFactory implements MethodInterceptor {
public TrainStation getProxyObject(){
// 创建Enhancer对象 类似于 Proxy类
Enhancer enhancer = new Enhancer();
// 设置父类的字节码对象 即需要代理的类
enhancer.setSuperclass(TrainStation.class);
//设置回调函数,增强方法
enhancer.setCallback(this);
// 生成代理对象
TrainStation proxy = (TrainStation) enhancer.create();
return proxy;
}
/**
* 方法拦截(增强)
* @param o 代理对象
* @param method 真实对象的method实例
* @param args 实际参数
* @param methodProxy 代理对象中的方法的method实例
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("方法增强,CGlib动态代理模式");
TrainStation result = (TrainStation) methodProxy.invokeSuper(o, args);
return result;
}
}
通过arthas 工具获取 代理类
package com.rwto.structure.proxy.cglib;
import com.rwto.structure.proxy.cglib.TrainStation;
import java.lang.reflect.Method;
import net.sf.cglib.core.ReflectUtils;
import net.sf.cglib.core.Signature;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Factory;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class TrainStation$$EnhancerByCGLIB$$b7647434
extends TrainStation
implements Factory {
private boolean CGLIB$BOUND;
private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
private static final Callback[] CGLIB$STATIC_CALLBACKS;
private MethodInterceptor CGLIB$CALLBACK_0;
private static final Method CGLIB$sell$0$Method;
private static final MethodProxy CGLIB$sell$0$Proxy;
private static final Object[] CGLIB$emptyArgs;
private static final Method CGLIB$finalize$1$Method;
private static final MethodProxy CGLIB$finalize$1$Proxy;
private static final Method CGLIB$equals$2$Method;
private static final MethodProxy CGLIB$equals$2$Proxy;
private static final Method CGLIB$toString$3$Method;
private static final MethodProxy CGLIB$toString$3$Proxy;
private static final Method CGLIB$hashCode$4$Method;
private static final MethodProxy CGLIB$hashCode$4$Proxy;
private static final Method CGLIB$clone$5$Method;
private static final MethodProxy CGLIB$clone$5$Proxy;
static void CGLIB$STATICHOOK1() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
Class<?> clazz = Class.forName("com.rwto.structure.proxy.cglib.TrainStation$$EnhancerByCGLIB$$b7647434");
Class<?> clazz2 = Class.forName("com.rwto.structure.proxy.cglib.TrainStation");
CGLIB$sell$0$Method = ReflectUtils.findMethods(new String[]{"sell", "()V"}, clazz2.getDeclaredMethods())[0];
CGLIB$sell$0$Proxy = MethodProxy.create(clazz2, clazz, "()V", "sell", "CGLIB$sell$0");
clazz2 = Class.forName("java.lang.Object");
Method[] methodArray = ReflectUtils.findMethods(new String[]{"finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, clazz2.getDeclaredMethods());
CGLIB$finalize$1$Method = methodArray[0];
CGLIB$finalize$1$Proxy = MethodProxy.create(clazz2, clazz, "()V", "finalize", "CGLIB$finalize$1");
CGLIB$equals$2$Method = methodArray[1];
CGLIB$equals$2$Proxy = MethodProxy.create(clazz2, clazz, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2");
CGLIB$toString$3$Method = methodArray[2];
CGLIB$toString$3$Proxy = MethodProxy.create(clazz2, clazz, "()Ljava/lang/String;", "toString", "CGLIB$toString$3");
CGLIB$hashCode$4$Method = methodArray[3];
CGLIB$hashCode$4$Proxy = MethodProxy.create(clazz2, clazz, "()I", "hashCode", "CGLIB$hashCode$4");
CGLIB$clone$5$Method = methodArray[4];
CGLIB$clone$5$Proxy = MethodProxy.create(clazz2, clazz, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5");
}
final void CGLIB$sell$0() {
super.sell();
}
public final void sell() {
MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
if (methodInterceptor == null) {
TrainStation$$EnhancerByCGLIB$$b7647434.CGLIB$BIND_CALLBACKS(this);
methodInterceptor = this.CGLIB$CALLBACK_0;
}
if (methodInterceptor != null) {
Object object = methodInterceptor.intercept(this, CGLIB$sell$0$Method, CGLIB$emptyArgs, CGLIB$sell$0$Proxy);
return;
}
super.sell();
}
final void CGLIB$finalize$1() throws Throwable {
super.finalize();
}
protected final void finalize() throws Throwable {
MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
if (methodInterceptor == null) {
TrainStation$$EnhancerByCGLIB$$b7647434.CGLIB$BIND_CALLBACKS(this);
methodInterceptor = this.CGLIB$CALLBACK_0;
}
if (methodInterceptor != null) {
Object object = methodInterceptor.intercept(this, CGLIB$finalize$1$Method, CGLIB$emptyArgs, CGLIB$finalize$1$Proxy);
return;
}
super.finalize();
}
final boolean CGLIB$equals$2(Object object) {
return super.equals(object);
}
public final boolean equals(Object object) {
MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
if (methodInterceptor == null) {
TrainStation$$EnhancerByCGLIB$$b7647434.CGLIB$BIND_CALLBACKS(this);
methodInterceptor = this.CGLIB$CALLBACK_0;
}
if (methodInterceptor != null) {
Object object2 = methodInterceptor.intercept(this, CGLIB$equals$2$Method, new Object[]{object}, CGLIB$equals$2$Proxy);
return object2 == null ? false : (Boolean)object2;
}
return super.equals(object);
}
final String CGLIB$toString$3() {
return super.toString();
}
public final String toString() {
MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
if (methodInterceptor == null) {
TrainStation$$EnhancerByCGLIB$$b7647434.CGLIB$BIND_CALLBACKS(this);
methodInterceptor = this.CGLIB$CALLBACK_0;
}
if (methodInterceptor != null) {
return (String)methodInterceptor.intercept(this, CGLIB$toString$3$Method, CGLIB$emptyArgs, CGLIB$toString$3$Proxy);
}
return super.toString();
}
final int CGLIB$hashCode$4() {
return super.hashCode();
}
public final int hashCode() {
MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
if (methodInterceptor == null) {
TrainStation$$EnhancerByCGLIB$$b7647434.CGLIB$BIND_CALLBACKS(this);
methodInterceptor = this.CGLIB$CALLBACK_0;
}
if (methodInterceptor != null) {
Object object = methodInterceptor.intercept(this, CGLIB$hashCode$4$Method, CGLIB$emptyArgs, CGLIB$hashCode$4$Proxy);
return object == null ? 0 : ((Number)object).intValue();
}
return super.hashCode();
}
final Object CGLIB$clone$5() throws CloneNotSupportedException {
return super.clone();
}
protected final Object clone() throws CloneNotSupportedException {
MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
if (methodInterceptor == null) {
TrainStation$$EnhancerByCGLIB$$b7647434.CGLIB$BIND_CALLBACKS(this);
methodInterceptor = this.CGLIB$CALLBACK_0;
}
if (methodInterceptor != null) {
return methodInterceptor.intercept(this, CGLIB$clone$5$Method, CGLIB$emptyArgs, CGLIB$clone$5$Proxy);
}
return super.clone();
}
public static MethodProxy CGLIB$findMethodProxy(Signature signature) {
String string = ((Object)signature).toString();
switch (string.hashCode()) {
case -1574182249: {
if (!string.equals("finalize()V")) break;
return CGLIB$finalize$1$Proxy;
}
case -508378822: {
if (!string.equals("clone()Ljava/lang/Object;")) break;
return CGLIB$clone$5$Proxy;
}
case 1826985398: {
if (!string.equals("equals(Ljava/lang/Object;)Z")) break;
return CGLIB$equals$2$Proxy;
}
case 1913648695: {
if (!string.equals("toString()Ljava/lang/String;")) break;
return CGLIB$toString$3$Proxy;
}
case 1978249955: {
if (!string.equals("sell()V")) break;
return CGLIB$sell$0$Proxy;
}
case 1984935277: {
if (!string.equals("hashCode()I")) break;
return CGLIB$hashCode$4$Proxy;
}
}
return null;
}
public TrainStation$$EnhancerByCGLIB$$b7647434() {
TrainStation$$EnhancerByCGLIB$$b7647434 trainStation$$EnhancerByCGLIB$$b7647434 = this;
TrainStation$$EnhancerByCGLIB$$b7647434.CGLIB$BIND_CALLBACKS(trainStation$$EnhancerByCGLIB$$b7647434);
}
public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] callbackArray) {
CGLIB$THREAD_CALLBACKS.set(callbackArray);
}
public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] callbackArray) {
CGLIB$STATIC_CALLBACKS = callbackArray;
}
private static final void CGLIB$BIND_CALLBACKS(Object object) {
block2: {
Object object2;
block3: {
TrainStation$$EnhancerByCGLIB$$b7647434 trainStation$$EnhancerByCGLIB$$b7647434 = (TrainStation$$EnhancerByCGLIB$$b7647434)object;
if (trainStation$$EnhancerByCGLIB$$b7647434.CGLIB$BOUND) break block2;
trainStation$$EnhancerByCGLIB$$b7647434.CGLIB$BOUND = true;
object2 = CGLIB$THREAD_CALLBACKS.get();
if (object2 != null) break block3;
object2 = CGLIB$STATIC_CALLBACKS;
if (CGLIB$STATIC_CALLBACKS == null) break block2;
}
trainStation$$EnhancerByCGLIB$$b7647434.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])object2)[0];
}
}
public Object newInstance(Callback[] callbackArray) {
TrainStation$$EnhancerByCGLIB$$b7647434.CGLIB$SET_THREAD_CALLBACKS(callbackArray);
TrainStation$$EnhancerByCGLIB$$b7647434 trainStation$$EnhancerByCGLIB$$b7647434 = new TrainStation$$EnhancerByCGLIB$$b7647434();
TrainStation$$EnhancerByCGLIB$$b7647434.CGLIB$SET_THREAD_CALLBACKS(null);
return trainStation$$EnhancerByCGLIB$$b7647434;
}
public Object newInstance(Callback callback) {
TrainStation$$EnhancerByCGLIB$$b7647434.CGLIB$SET_THREAD_CALLBACKS(new Callback[]{callback});
TrainStation$$EnhancerByCGLIB$$b7647434 trainStation$$EnhancerByCGLIB$$b7647434 = new TrainStation$$EnhancerByCGLIB$$b7647434();
TrainStation$$EnhancerByCGLIB$$b7647434.CGLIB$SET_THREAD_CALLBACKS(null);
return trainStation$$EnhancerByCGLIB$$b7647434;
}
public Object newInstance(Class[] classArray, Object[] objectArray, Callback[] callbackArray) {
TrainStation$$EnhancerByCGLIB$$b7647434 trainStation$$EnhancerByCGLIB$$b7647434;
TrainStation$$EnhancerByCGLIB$$b7647434.CGLIB$SET_THREAD_CALLBACKS(callbackArray);
Class[] classArray2 = classArray;
switch (classArray.length) {
case 0: {
trainStation$$EnhancerByCGLIB$$b7647434 = new TrainStation$$EnhancerByCGLIB$$b7647434();
break;
}
default: {
throw new IllegalArgumentException("Constructor not found");
}
}
TrainStation$$EnhancerByCGLIB$$b7647434.CGLIB$SET_THREAD_CALLBACKS(null);
return trainStation$$EnhancerByCGLIB$$b7647434;
}
public Callback getCallback(int n) {
MethodInterceptor methodInterceptor;
TrainStation$$EnhancerByCGLIB$$b7647434.CGLIB$BIND_CALLBACKS(this);
switch (n) {
case 0: {
methodInterceptor = this.CGLIB$CALLBACK_0;
break;
}
default: {
methodInterceptor = null;
}
}
return methodInterceptor;
}
public void setCallback(int n, Callback callback) {
switch (n) {
case 0: {
this.CGLIB$CALLBACK_0 = (MethodInterceptor)callback;
break;
}
}
}
public Callback[] getCallbacks() {
TrainStation$$EnhancerByCGLIB$$b7647434.CGLIB$BIND_CALLBACKS(this);
TrainStation$$EnhancerByCGLIB$$b7647434 trainStation$$EnhancerByCGLIB$$b7647434 = this;
return new Callback[]{this.CGLIB$CALLBACK_0};
}
public void setCallbacks(Callback[] callbackArray) {
Callback[] callbackArray2 = callbackArray;
TrainStation$$EnhancerByCGLIB$$b7647434 trainStation$$EnhancerByCGLIB$$b7647434 = this;
this.CGLIB$CALLBACK_0 = (MethodInterceptor)callbackArray[0];
}
static {
TrainStation$$EnhancerByCGLIB$$b7647434.CGLIB$STATICHOOK1();
}
}
可以看出
● CGlib是通过生成一个子类 来继承 被代理的类,实现的动态代理,因此cglib不能代理被final修饰的类
● 使用CGLib实现动态代理,CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,在JDK1.6之前比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的类或者方法进行代理,因为CGLib原理是动态生成被代理类的子类。
● 在JDK1.6、JDK1.7、JDK1.8逐步对JDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLib代理效率,只有当进行大量调用的时候,JDK1.6和JDK1.7比CGLib代理效率低一点,但是到JDK1.8的时候,JDK代理效率高于CGLib代理。所以如果有接口使用JDK动态代理,如果没有接口使用CGLIB代理。