Proxy(代理模式)
描述
由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。
根据代理的创建时期,代理模式分为静态代理和动态代理。
- 静态:由程序员创建代理类或特定工具自动生成源代码再对其编译,在程序运行前代理类的 .class 文件就已经存在了。
- 动态:在程序运行时,运用反射机制动态创建而成
场景
使用代理模式主要有两个目的:一是保护目标对象,二是增强目标对象。
- AOP
- RPC
写法
静态代理
抽象主题
interface Subject {
void request();
}
真实主题
class RealSubject implements Subject {
public void request() {
System.out.println("访问真实主题方法...");
}
}
代理
class Proxy implements Subject {
private RealSubject realSubject;
public void request() {
if (realSubject == null) {
realSubject = new RealSubject();
}
preRequest();
realSubject.request();
postRequest();
}
public void preRequest() {
System.out.println("访问真实主题之前的预处理。");
}
public void postRequest() {
System.out.println("访问真实主题之后的后续处理。");
}
}
图解
动态代理
基于JDK(需要传入目标类的实现):调用JDK自带的 Proxy 方法生成代理类,实现方式基于内部聚合,通过反射机制实现。
写法一:直接调用Proxy根据接口生成,传入实现 InvocationHandler 接口的具体业务增强类 BizInvocationHandler 。
Subject proxy2 = (Subject)Proxy.newProxyInstance(Subject.class.getClassLoader(), new Class[]{Subject.class}, new BizInvocationHandler(subject));
实现对应的逻辑增强接口 InvocationHandle 时,应持有代理目标的引用,一般通过构造函数实现。
public class BizInvocationHandler implements InvocationHandler {
// 这个是个动态代理的核心类,它实现了InvocationHandler接口,它的作用是在调用真实对象的方法之前和之后做一些事情。
private Object target;
public BizInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, java.lang.reflect.Method method, Object[] args) throws Throwable {
System.out.println("before2");
Object result = method.invoke(target, args);
System.out.println("after2");
return result;
}
}
写法二:将生成逻辑封装到实现 InvocationHandler 接口的具体业务增强类中,简化调用逻辑。
public class DynamicProxy<T> implements InvocationHandler {
private Object target;
public DynamicProxy(Object target) {
this.target = target;
}
T getInstance(T target) {
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
@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("before");
}
private void after(){
System.out.println("after");
}
}
图解
基于Cglib(无需传入目标类的实现):使用第三方CGlibProxy,通过工具类Enhancer实现。
写法一:直接使用工具类Enhancer生成,传入实现 MethodInterceptor接口的具体业务增强类到Callback中。
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Subject.class);
enhancer.setCallback(new BizMethodInterceptor());
Subject sub = (Subject) enhancer.create();
sub.realSubject();
public class BizMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("before");
Object result = proxy.invokeSuper(obj, args);
System.out.println("after");
return result;
}
}
写法二:将生成逻辑封装到实现 MethodInterceptor 接口的具体业务增强类中,简化调用逻辑。
public class CGlibProxy implements MethodInterceptor {
public Object getInstance(Class<?> clazz) throws Exception{
//相当于Proxy,代理的工具类
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
before();
Object obj = methodProxy.invokeSuper(o,objects);
after();
return obj;
}
private void before(){
System.out.println("before");
}
private void after(){
System.out.println("after");
}
}
手写动态代理
类加载器:重写findClass方法,先读取目标类的字节码文件 .class 调用 defineClass 将二进制文件转化为内存中的 class;
public class GPClassLoader extends ClassLoader {
private File classPathFile;
public GPClassLoader(){
String classPath = GPClassLoader.class.getResource("").getPath();
this.classPathFile = new File(classPath);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
String className = GPClassLoader.class.getPackage().getName() + "." + name;
if(classPathFile != null){
File classFile = new File(classPathFile,name.replaceAll("\.","/") + ".class");
if(classFile.exists()){
FileInputStream in = null;
ByteArrayOutputStream out = null;
try{
in = new FileInputStream(classFile);
out = new ByteArrayOutputStream();
byte [] buff = new byte[1024];
int len;
while ((len = in.read(buff)) != -1){
out.write(buff,0,len);
}
return defineClass(className,out.toByteArray(),0,out.size());
}catch (Exception e){
e.printStackTrace();
}
}
}
return null;
}
}
回调接口:用于完成代理模式下的逻辑增强
public interface GPInvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
动态代理工具类:根据需要代理的接口实现代理类
public class GPProxy {
public static Object newProxyInstance(GPClassLoader classLoader, Class<?> [] interfaces, GPInvocationHandler h){
//1、动态生成源代码.java文件
//2、Java文件输出磁盘
//3、把生成的.java文件编译成.class文件
//4、编译生成的.class文件加载到JVM中来
//5、返回字节码重组以后的新的代理对象
Class proxyClass = classLoader.findClass("$Proxy0");
Constructor c = proxyClass.getConstructor(GPInvocationHandler.class);
/*
将增强逻辑类通过构造函数放入代理类中
返回一个拥有增强逻辑引用,切实现代理目标接口的代理类。
*/
return c.newInstance(h);
}
}
优点
- 客户端与目标类分离解耦,从而易于扩展
- 起到控制保护目标对象的作用
- 增强目标对象
缺点
- 增加类的数目
- 增加了代理对象,影响执行效率
- 增加系统复杂度
理解
静态代理
代理类中包含代理目标Target类,实现Target类中所有接口,并整合代理的逻辑对代理对象功能进行增强。使用方式为 new 代理类传入代理对象。
动态代理
代理类通过JDK或者Cglib提供的方法实现代理,只是意义上的代理,实现机制和基础类图不是一致的。
其他
设计模式
不要纠结于具体实现的代码结构,设计模式更多的是概念上一致而非具体是现实上的一致。
JDK Proxy
- 基于Proxy生成的代理类默认类名规则,num是根据不同代理接口计数的。
String proxyName = proxyPkg + proxyClassNamePrefix + num;
- 生成的代理类继承了 Proxy ,其内部 h 指向了 实际的增强扩展业务逻辑的接口(InvocationHandler)实现类。
protected InvocationHandler h;