何为代理模式
代理模式是指,在调用方与被调用方之间加一层代理,用来控制对被调用方的访问。属于结构型模式。
为何需要代理模式
某些特殊情况,调用方不适合直接访问被调用方,通过加一层代理,可以隔绝被调用方的部分细节,同时还可以对被调用方的部分功能做增强。
静态代理
假设有个人叫张三,他想买一套房子,但是他没有渠道,他的朋友赵六人脉广资源多,于是张三就请求赵六帮忙物色房源,代理张三购房。
/**
* 张三
*/
public class Zhangsan {
public void buyHouse() {
System.out.println("张三购买一套200平的独栋别墅,带地下室和车库!");
}
}
/**
* 赵六
*/
public class Zhaoliu {
private Zhangsan zhangsan;
// 引用被代理对象
public Zhaoliu(Zhangsan zhangsan) {
this.zhangsan = zhangsan;
}
public void buyHouse() {
before();
zhangsan.buyHouse();
after();
}
/**
* 被代理方法执行之前的增强
*/
private void before() {
System.out.println("赵六替张三物色房源!");
}
/**
* 被代理方法执行之后的增强
*/
private void after() {
System.out.println("房屋交易完成!");
}
}
public class Test {
public static void main(String[] args) {
Zhaoliu zhaoliu = new Zhaoliu(new Zhangsan());
zhaoliu.buyHouse();
}
}
执行结果:
赵六替张三物色房源!
张三购买一套200平的独栋别墅,带地下室和车库!
房屋交易完成!
动态代理
通过上面的实例,大家有没有发现静态代理的弊端,假设现在又有一个李四和王五也有购房的需求,但是他们不像张三一样有一个神通广大的朋友赵六,那他们如果想买房该找谁呢?于是乎,现在需要一个更加通用的解决方案,便是房产中介,不管你是谁,只要是一个人,只要有购房的需求都可以找房产中介。
JDK实现动态代理
/**
* 人的抽象
*/
public interface IPerson {
/**
* 购买房子
*/
public void buyHouse();
}
/**
* 李四
*/
public class Lisi implements IPerson {
@Override
public void buyHouse() {
System.out.println("李四想要购买一个500平的大平层!");
}
}
/**
* 房产中介:链家
*/
public class Lianjia implements InvocationHandler {
// 被代理对象
private IPerson target;
/**
* 获取一个代理类的实例(可以理解为链家为您动态分配了一个房产经纪)
* @param target
* @return
*/
public IPerson getInstance(IPerson target) {
this.target = target;
return (IPerson) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
/**
* 代理方法执行时调用的方法
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object result = method.invoke(target, args);
after();
return result;
}
private void before() {
System.out.println("链家为您物色房源信息!");
}
private void after() {
System.out.println("交易结束!");
}
}
public class Test {
public static void main(String[] args) throws Exception {
IPerson person = new Lianjia().getInstance(new Lisi());
person.buyHouse();
}
}
执行结果:
链家为您物色房源信息!
李四想要购买一个500平的大平层!
交易结束!
JDK动态代理实现原理刨析
动态代理之所以是动态的,其主要原因是动态的生成了一个代理类,该代理类一般是$Proxyn($Proxy是固定的,n代表一个数字)这样格式的,我们现在用一段程序将该代理类输出到磁盘并反编译,看一下它的庐山真面目。
public class Test {
public static void main(String[] args) throws Exception {
// 生成代理类
IPerson person = new Lianjia().getInstance(new Lisi());
person.buyHouse();
// 获取代理类$Proxy0的字节文件
byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{IPerson.class});
// 将代理类输出至磁盘D://$Proxy0.class
FileOutputStream fos = new FileOutputStream("D://$Proxy0.class");
fos.write(bytes);
fos.close();
}
}
public final class $Proxy0 extends Proxy implements IPerson {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public $Proxy0(InvocationHandler var1) {
super(var1);
}
public final boolean equals(Object var1) {
try {
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() {
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 void buyHouse() {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
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.test.proxy.IPerson").getMethod("buyHouse");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
通过分析代理类源码,可以得出结论:
1、代理类是继承了JDK的Proxy类,并实现了被代理类的接口;
2、代理类内部持有一个InvocationHandler(触发管理类)对象,方法的调用其实最终会调用InvocationHandler的invoke()方法。
动手实现自己的动态代理
知道了动态代理的实现原理,我们可以手动实现一套属于自己的动态代理API,话不多说,直接上代码
public interface MyInvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
public class MyProxy {
private static final String ln = "\r\n";
protected MyInvocationHandler h;
protected MyProxy(MyInvocationHandler h) {
this.h = h;
}
public static Object newProxyInstance(MyClassLoader loader,
Class<?>[] interfaces,
MyInvocationHandler h) {
try {
//动态生成.java源码文件
String src = generate(interfaces);
//将.java源码文件输出至磁盘的指定目录
String basePath = MyProxy.class.getResource("").getFile();
String filePath = basePath + "/$Proxy0.java";
File file = new File(filePath);
FileOutputStream fos = new FileOutputStream(file);
fos.write(src.getBytes());
fos.flush();
fos.close();
//将.java源码文件编译成.class字节码文件
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager standardFileManager = compiler.getStandardFileManager(null, null, null);
Iterable<? extends JavaFileObject> iterable = standardFileManager.getJavaFileObjects(file);
JavaCompiler.CompilationTask task = compiler.getTask(null, standardFileManager, null, null, null, iterable);
task.call();
standardFileManager.close();
file.delete();
//将.class字节码文件加载至JVM中
Class proxyClass = loader.findClass("$Proxy0");
//返回新生成的代理对象
Constructor constructor = proxyClass.getConstructor(MyInvocationHandler.class);
return constructor.newInstance(h);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static String generate(Class<?>[] interfaces) {
StringBuffer srcBuffer = new StringBuffer();
srcBuffer.append(MyProxy.class.getPackage() + ";" + ln);
//拼接导入包
for (Class<?> interfaceClass : interfaces) {
String interfaceName = interfaceClass.getName();
srcBuffer.append("import " + interfaceName + ";" + ln);
}
srcBuffer.append("import com.test.proxy.mydynamicproxy.MyInvocationHandler;" + ln);
srcBuffer.append("import com.test.proxy.mydynamicproxy.MyProxy;" + ln);
srcBuffer.append("import java.lang.reflect.*;" + ln);
//拼接类的声明
srcBuffer.append("public final class $Proxy0 extends MyProxy ");
for (Class<?> interfaceClass : interfaces) {
String interfaceSimpleName = interfaceClass.getSimpleName();
srcBuffer.append("implements " + interfaceSimpleName);
}
srcBuffer.append(" {" + ln);
//拼接方法声明
Set<Method> methodSet = new LinkedHashSet<Method>();
for (Class<?> interfaceClass : interfaces) {
Method[] methods = interfaceClass.getMethods();
for (Method method : methods) {
methodSet.add(method);
}
}
for (int i = 0;i < methodSet.size();i++) {
srcBuffer.append("private static Method m" + i + ";" + ln);
}
//拼接构造函数
srcBuffer.append("public $Proxy0(MyInvocationHandler h) {" + ln);
srcBuffer.append("super(h);" + ln);
srcBuffer.append("}" + ln);
//拼接方法
int i = 0;
Iterator<Method> iterator = methodSet.iterator();
while (iterator.hasNext()) {
Method method = iterator.next();
srcBuffer.append("public final "+method.getReturnType().getSimpleName()+" "+method.getName()+"(");
Class<?>[] parameterTypes = method.getParameterTypes();
for (int j = 0;j < parameterTypes.length;j++) {
if (j == parameterTypes.length - 1) {
srcBuffer.append(parameterTypes[j].getSimpleName() + "var"+j);
} else {
srcBuffer.append(parameterTypes[j].getSimpleName() + "var"+j+",");
}
}
srcBuffer.append(") {" + ln);
srcBuffer.append("try {" + ln);
if (method.getReturnType() != Void.class && method.getReturnType() != void.class) {
srcBuffer.append("return (" + method.getReturnType().getSimpleName() + ")");
}
srcBuffer.append("super.h.invoke(this, m"+i+", new Object[]{");
for (int j = 0;j < parameterTypes.length;j++) {
if (j == parameterTypes.length - 1) {
srcBuffer.append("var"+j);
} else {
srcBuffer.append("var"+j+",");
}
}
srcBuffer.append("});" + ln);
srcBuffer.append("} catch (RuntimeException | Error e) {" + ln);
srcBuffer.append("throw e;" + ln);
srcBuffer.append("} catch (Throwable t) {" + ln);
srcBuffer.append("throw new UndeclaredThrowableException(t);" + ln);
srcBuffer.append("}" + ln);
srcBuffer.append("}" + ln);
i++;
}
//拼接静态代码块
srcBuffer.append("static {" + ln);
srcBuffer.append("try {" + ln);
i = 0;
iterator = methodSet.iterator();
while (iterator.hasNext()) {
Method method = iterator.next();
srcBuffer.append("m"+i+" = Class.forName(\""+method.getDeclaringClass().getName()+"\").getMethod(\""+method.getName()+"\"");
Parameter[] parameters = method.getParameters();
if (parameters != null && parameters.length > 0) {
for (Parameter parameter : parameters) {
srcBuffer.append(", Class.forName(\""+parameter.getType().getName()+"\")");
}
}
srcBuffer.append("");
srcBuffer.append(");" + ln);
}
srcBuffer.append("} catch (NoSuchMethodException e) {" + ln);
srcBuffer.append("throw new NoSuchMethodError(e.getMessage());" + ln);
srcBuffer.append("} catch (ClassNotFoundException e) {" + ln);
srcBuffer.append("throw new NoClassDefFoundError(e.getMessage());" + ln);
srcBuffer.append("}" + ln);
srcBuffer.append("}" + ln);
srcBuffer.append("}");
return srcBuffer.toString();
}
}
public class MyClassLoader extends ClassLoader{
public Class<?> findClass(String name) {
String className = MyClassLoader.class.getPackage().getName() + "." + name;
String basePath = MyClassLoader.class.getResource("").getFile();
String filePath = basePath + "/" + name + ".class";
File file = new File(filePath);
byte[] buffer = null;
FileInputStream fis = null;
ByteArrayOutputStream baos = null;
try {
fis = new FileInputStream(file);
baos = new ByteArrayOutputStream();
byte[] n = new byte[1024];
int len;
while ((len = fis.read(n)) != -1) {
baos.write(n, 0, len);
}
buffer = baos.toByteArray();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (fis != null) {
fis.close();
}
if (baos != null) {
baos.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
return defineClass(className, buffer, 0, buffer.length);
}
}
public class MyLianjia implements MyInvocationHandler {
private IPerson person;
public MyLianjia(IPerson person) {
this.person = person;
}
public IPerson getInstance() {
return (IPerson) MyProxy.newProxyInstance(new MyClassLoader(), person.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object result = method.invoke(person, args);
after();
return result;
}
private void after() {
System.out.println("交易结束!!!");
}
private void before() {
System.out.println("中介开始物色房源!!!");
}
}
public class Test {
public static void main(String[] args) {
IPerson person = new MyLianjia(new Lisi()).getInstance();
person.buyHouse();
}
}
运行结果:
中介开始物色房源!!!
李四想要购买一个500平的大平层!
交易结束!!!
CGLIB实现动态代理
首先引入Cglib的依赖:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
/**
* Cglib中介类,用于生成代理类对象以及实现增强方法
* @param <T>
*/
public class CglibIntermediary<T> implements MethodInterceptor {
public T getInstance(Class<? extends T> clazz) {
//设置一个增强者对象
Enhancer enhancer = new Enhancer();
//设置被代理对象的类型(创建的代理类实际是被代理类的子类)
enhancer.setSuperclass(clazz);
//设置回调(最终会回调MethodInterceptor类的interceptor方法)
enhancer.setCallback(this);
//生成并返回代理类对象
return (T) enhancer.create();
}
/**
* 回调的方法,完成对原有方法的增强
* @param o
* @param method
* @param objects
* @param methodProxy
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
before();
Object result = methodProxy.invokeSuper(o, objects);
after();
return result;
}
private void after() {
System.out.println("Cglib中介交易结束!!!");
}
private void before() {
System.out.println("Cglib中介物色房源!!!");
}
}
编写测试类:
public class Test {
public static void main(String[] args) {
Zhangsan proxy = new CglibIntermediary<Zhangsan>().getInstance(Zhangsan.class);
proxy.buyHouse();
}
}
测试结果:
Cglib中介物色房源!!!
张三购买一套200平的独栋别墅,带地下室和车库!
Cglib中介交易结束!!!
CGLIB动态代理实现原理分析
测试代码中加入下面一句话,可以看到cglib动态生成的代码
public class Test {
public static void main(String[] args) {
//将cglib动态生成的.class文件输出至D://cglib_proxy_class/目录下
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D://cglib_proxy_class/");
Zhangsan proxy = new CglibIntermediary<Zhangsan>().getInstance(Zhangsan.class);
proxy.buyHouse();
}
}
可以看到其主要生成了以下3个类:
Zhangsan$$EnhancerByCGLIB$$4dde1686:动态代理类,其继承了被代理类
Zhangsan$$FastClassByCGLIB$$3571adf0:被代理类的fastclass类
Zhangsan$$EnhancerByCGLIB$$4dde1686$$FastClassByCGLIB$$92f3b06a:代理类的fastclass类
fastclass类,其内部为每一个方法维护了一个索引(int类型的index),可以快速的找到要执行的方法,不用反射获取,效率更高。
我们从cglib动态代理的执行过程,看一下其执行的原理:
1、创建代理类
代理的入口是我们自己编写的CglibIntermediary类的getInstance()方法,通过调用enhancer.create()生成代理类
Zhangsan$$EnhancerByCGLIB$$4dde1686,其内部会维护一个MethodInterceptor的引用CGLIB$CALLBACK_0,
初始化时会执行静态代码块中的CGLIB$STATICHOOK1()方法
static {
CGLIB$STATICHOOK1();
}
static void CGLIB$STATICHOOK1() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
Class var0 = Class.forName("com.test.proxy.staticproxy.Zhangsan$$EnhancerByCGLIB$$4dde1686");
Class var1;
Method[] var10000 = ReflectUtils.findMethods(new String[]{"finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
CGLIB$finalize$1$Method = var10000[0];
CGLIB$finalize$1$Proxy = MethodProxy.create(var1, var0, "()V", "finalize", "CGLIB$finalize$1");
CGLIB$equals$2$Method = var10000[1];
CGLIB$equals$2$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2");
CGLIB$toString$3$Method = var10000[2];
CGLIB$toString$3$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$3");
CGLIB$hashCode$4$Method = var10000[3];
CGLIB$hashCode$4$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$4");
CGLIB$clone$5$Method = var10000[4];
CGLIB$clone$5$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5");
CGLIB$buyHouse$0$Method = ReflectUtils.findMethods(new String[]{"buyHouse", "()V"}, (var1 = Class.forName("com.test.proxy.staticproxy.Zhangsan")).getDeclaredMethods())[0];
CGLIB$buyHouse$0$Proxy = MethodProxy.create(var1, var0, "()V", "buyHouse", "CGLIB$buyHouse$0");
}
2、创建MethodProxy对象
//我们主要关注CGLIB$STATICHOOK1()方法的最后一步创建buyHouse()方法的代理,var1是被代理类,var0为代理类
CGLIB$buyHouse$0$Method = ReflectUtils.findMethods(new String[]{"buyHouse", "()V"}, (var1 = Class.forName("com.test.proxy.staticproxy.Zhangsan")).getDeclaredMethods())[0];
CGLIB$buyHouse$0$Proxy = MethodProxy.create(var1, var0, "()V", "buyHouse", "CGLIB$buyHouse$0");
/**
* 创建方法的代理
* c1传入的被代理类,c2传入的是代理类
*/
public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
MethodProxy proxy = new MethodProxy();
//被代理方法
proxy.sig1 = new Signature(name1, desc);
//代理方法
proxy.sig2 = new Signature(name2, desc);
//将代理类和被代理类的引用封装到CreateInfo中
proxy.createInfo = new MethodProxy.CreateInfo(c1, c2);
//返回方法的代理
return proxy;
}
方法的调用
我们在测试类中调用buyHouse()方法时,实际是调用的代理类的buyHouse()方法
public final void buyHouse() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
//判断其持有的MethodInterceptor对象是否为空
//若不为空,则执行其interceptor方法
//若为空,则执行原有被代理对象的buyHouse()方法
if (var10000 != null) {
var10000.intercept(this, CGLIB$buyHouse$0$Method, CGLIB$emptyArgs, CGLIB$buyHouse$0$Proxy);
} else {
super.buyHouse();
}
}
来到我们自己编写的实现了MethodInterceptor接口的intercetor()方法的CglibIntermediary类
/**
* 回调的方法,完成对原有方法的增强
* @param o
* @param method
* @param objects
* @param methodProxy
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//方法执行前的操作
before();
//调用代理方法
Object result = methodProxy.invokeSuper(o, objects);
//方法执行后的操作
after();
return result;
}
调用MethodProxy类的invokeSuper()方法
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
//初始化fastClass
this.init();
MethodProxy.FastClassInfo fci = this.fastClassInfo;
//执行方法的调用
return fci.f2.invoke(fci.i2, obj, args);
} catch (InvocationTargetException var4) {
throw var4.getTargetException();
}
}
private void init() {
if (this.fastClassInfo == null) {
synchronized(this.initLock) {
if (this.fastClassInfo == null) {
//获取缓存的代理类和被代理类
MethodProxy.CreateInfo ci = this.createInfo;
MethodProxy.FastClassInfo fci = new MethodProxy.FastClassInfo();
//生成fastClass
fci.f1 = helper(ci, ci.c1);
fci.f2 = helper(ci, ci.c2);
//获取方法
fci.i1 = fci.f1.getIndex(this.sig1);
fci.i2 = fci.f2.getIndex(this.sig2);
this.fastClassInfo = fci;
this.createInfo = null;
}
}
}
}
/**
* 动态生成fastClass类
*/
private static FastClass helper(MethodProxy.CreateInfo ci, Class type) {
Generator g = new Generator();
g.setType(type);
g.setClassLoader(ci.c2.getClassLoader());
g.setNamingPolicy(ci.namingPolicy);
g.setStrategy(ci.strategy);
g.setAttemptLoad(ci.attemptLoad);
return g.create();
}
至此,我们便分析完成了CGLIB动态代理的全过程。
JDK动态代理和CGLIB动态代理的区别
1、JDK动态代理是java源码中自带的方式,不需要引入额外的依赖。CGLIB需要引入第三方的依赖;
2、JDK动态代理直接生成class文件,CGLIB通过ASM框架生成字节码且生成逻辑复杂,生成代理类的效率比JDK要低;
3、JDK动态代理是通过反射调用的,CGLIB通过fastclass机制调用,其方法的调用效率比JDK要高;
4、JDK的动态代理需要被代理类有实现的接口,代理类也是基于其实现的接口来创建的,被代理的方法也仅限于接口声明的方法。CGLIB的动态代理类是通过继承被代理类的方式创建的,对被代理类的要求更低;
5、若被代理类有实现的接口,或者被代理的就是一个接口没有实现类,可以用JDK的动态代理。若被代理类没有实现的接口,可以用CGLIB的动态代理。
动态代理的实际应用
1、SpringAop,若类符合切点表达式,则Spring在动态创建类对象的时候,会为其床建一个代理对象,方法执行的时候,会依据切面类中定义的方法,对原有方法做增强;
2、MyBatis的Mapper类,MyBatis中的Mapper类只有接口的声明,并没有实现类,MyBatis会自动为当前接口创建一个代理类,并实现其增删改查的方法;
3、MyBatis的插件,MyBatis为了实现功能的扩展,预留了插件的机制,若MyBatis的配置文件中配置了插件类,MyBatis在运行时会为插件可以拦截的类(Executor、StatementHandler、ParameterHandler、ResultSetHandler)动态创建代理对象,并将被代理类的方法调用引入实现了Interceptor接口的intercept()方法中,从而实现功能的动态扩展。