Java 设计模式系列(十二)代理模式

设计模式之美 - 代理模式

设计模式之美目录:https://www.cnblogs.com/binarylei/p/8999236.html

代理模式:给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。GoF 的《设计模式》一书中把 RPC 称作远程代理。其它应用场景如缓存、监控、统计、鉴权、限流、事务、幂等、日志等。

package com.github.binarylei.design.proxy;

public interface UserService {
public void say();
} public class UserServiceImpl implements UserService {
@Override
public void say() {
System.out.println("Hello World!");
}
}

1. 静态代理

public void test() {
UserServiceImpl obj = new UserServiceImpl();
UserService userService = new UserService() {
@Override
public void say() {
System.out.println("这是静态代理");
obj.say();
}
};
userService.say();
}

很明显静态代理每个被代理的类都要手写一个代理类,当修改被代理的类时也要修改对应的代理类。解决这个问题则是由程序来生成对应的代理类,这就是动态代理。

2. 动态代理

2.1 JDK 动态代理

public void test1() throws Exception {
UserServiceImpl obj = new UserServiceImpl(); UserService userService = (UserService) Proxy.newProxyInstance(
UserServiceImpl.class.getClassLoader(), UserServiceImpl.class.getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getDeclaringClass() == Object.class) {
return method.invoke(obj, args);
} else {
System.out.println(proxy.getClass().getName());
System.out.println(method);
Object ret = method.invoke(obj, args);
return ret;
}
}
});
userService.say();
System.out.println(userService);
}

注意:JDK 中所要进行动态代理的类必须要实现一个接口 ,也就是说只能对该类所实现接口中定义的方法进行代理,这在实际编程中具有一定的局限性,而且使用反射的效率也并不是很高。

2.2 CGLib 动态代理

使用 CGLib 实现动态代理,完全不受代理类必须实现接口的限制,而且 CGLib 底层采用 ASM 字节码生成框架,使用字节码技术生成代理类,比使用 Java 反射效率要高。唯一需要注意的是,CGLib 不能对声明为 final 的方法进行代理,因为 CGLib 原理是动态生成被代理类的子类。

<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.8</version>
</dependency>
public void test2() {
Enhancer enhancer = new Enhancer();
//1. 设置父类
enhancer.setSuperclass(UserServiceImpl.class); //2. 设置回调函数
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println(method + " proxy");
Object ret = proxy.invokeSuper(obj, args);
return null;
}
}); //3. 获取代理对象
UserService userService = (UserService) enhancer.create();
userService.say();
}

参数:Object 为由 CGLib 动态生成的代理类实例,Method 为上文中实体类所调用的被代理的方法引用,Object[] 为参数值列表,MethodProxy 为生成的代理类对方法的代理引用。proxy.invokeSuper(obj, arg) 从代理实例的方法调用返回的值。

3. 动态代理原理

3.1 原理

JDK 的动态代理实际上是在内存中生成了一个字节码类,并进行编译,加载。JVM 生成的类名称都是以 $ 开头,eg: $Proxy0

public void test1() throws Exception {
UserServiceImpl obj = new UserServiceImpl(); UserService userService = (UserService) Proxy.newProxyInstance(
UserServiceImpl.class.getClassLoader(), UserServiceImpl.class.getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(proxy.getClass().getName()); // $Proxy0
Object ret = method.invoke(obj, args);
return ret;
}
});
userService.say();
System.out.println(userService); byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", obj.getClass().getInterfaces());
FileOutputStream out = new FileOutputStream(
this.getClass().getResource("").getPath() + "$Proxy0.class");
out.write(bytes);
}

查看生成的 $Proxy0.class 类如下:

import com.github.binarylei.design.proxy.UserService;
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 UserService {
private static Method m3; public $Proxy0(InvocationHandler var1) throws {
super(var1);
} public final void say() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
} static {
try {
m3 = Class.forName("com.github.binarylei.design.proxy.UserService").getMethod("say", new Class[0]);
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
} // ...
}

3.2 手写动态代理

(1) 定义 MyInvocationHandler 接口

@FunctionalInterface
public interface MyInvocationHandler { Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

(2) 实现自己的 MyProxy 类

package com.github.binarylei.design.proxy.my;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method; /**
* @author: leigang
* @version: 2018-10-02
*/
public class MyProxy { public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, MyInvocationHandler h) {
FileWriter out = null;
try {
// 1. 动态生成源代码 .java 文件
String src = generateSrc(interfaces); // 2. .java 文件生成到磁盘
File file = new File(MyProxy.class.getResource("").getPath() + "$Proxy1.java");
out = new FileWriter(file);
out.write(src);
out.flush();
out.close(); // 3. 把 .java 文件编译成 .class 文件
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager manager = compiler.getStandardFileManager(
null, null, null);
Iterable<? extends JavaFileObject> iterable = manager.getJavaFileObjects(file); JavaCompiler.CompilationTask task = compiler.getTask(
null, manager, null, null, null, iterable);
task.call();
manager.close(); // 4. 编译生成的 .class 类到 JVM 中
Class<?> clazz = Class.forName("com.github.binarylei.design.proxy.my.$Proxy1"); // 5. 返回字节码重组以后新代理对象
Constructor<?> constructor = clazz.getConstructor(MyInvocationHandler.class);
return constructor.newInstance(h);
} catch (Exception e) {
e.printStackTrace();
}
return null;
} private static final String ln = "\r\n"; private static String generateSrc(Class<?>[] interfaces) {
StringBuilder sb = new StringBuilder();
sb.append("package com.github.binarylei.design.proxy.my;").append(ln);
sb.append("import com.github.binarylei.design.proxy.UserService;").append(ln);
sb.append("import java.lang.reflect.Method;").append(ln);
sb.append("public final class $Proxy1 implements ").append(interfaces[0].getSimpleName()).append(" {").append(ln);
sb.append("private static MyInvocationHandler h;").append(ln);
sb.append("public $Proxy1(MyInvocationHandler h) throws Exception {").append(ln);
sb.append("this.h = h;").append(ln);
sb.append("}").append(ln); for (Class<?> clazz : interfaces) {
Method[] methods = clazz.getMethods();
for (Method m : methods) {
sb.append("public final void say() {").append(ln);
sb.append("try {").append(ln);
sb.append("Method m = Class.forName(\"").append(clazz.getName()).append("\").getMethod(\"")
.append(m.getName()).append("\", new Class[0]);").append(ln);
sb.append("h.invoke(this, m, (Object[]) null);").append(ln);
sb.append("} catch (Throwable e) {").append(ln);
sb.append("e.printStackTrace();").append(ln);
sb.append("}").append(ln);
sb.append("}").append(ln);
}
}
sb.append("}").append(ln);
return sb.toString();
} }

(3) 测试

public void test3() {
UserServiceImpl obj = new UserServiceImpl(); UserService userService = (UserService) MyProxy.newProxyInstance(
UserServiceImpl.class.getClassLoader(), UserServiceImpl.class.getInterfaces(),
new MyInvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(proxy.getClass().getName());
System.out.println(method);
Object ret = method.invoke(obj, args);
return ret;
}
});
userService.say();
System.out.println(userService);
}

参考:

  1. 实战CGLib系列文章 MethodInterceptor和Enhancer_CGLib:https://yq.aliyun.com/ziliao/296216

每天用心记录一点点。内容也许不重要,但习惯很重要!

上一篇:设计模式之享元(flyweight)模式


下一篇:JAVA设计模式 5【结构型】代理模式的理解与使用