代理模式的三种实现

Proxy
本文对《onJava8基础卷》第十九章反射中提到的静态代理和基于反射技术的动态代理,也就是通常说的JDK代理的方式进行分享。同时摘录了CGLib实现动态代理的方式。并对代码实现中的一些细节进行进行了注释。
首先是一个简单的例子,展示了代理模式的基本结构:
- 通用接口,或者说需要被代理的一组方法。
- 被代理对象。
- 代理对象。
被代理对象和代理对象均实现了通用接口。从代理模式的原理触出发,通用接口起到了一个统一的效果,利用了多态这个面向对象的特性。让调用方不需要关注实现,只需要关注功能。体现了封装和扩展。实现接口相较于继承父类我认为是更加灵活的方式。从语义上来讲,继承表示,is-a,接口表示can。同时考虑JDK实现动态代理的方式,我认为使用接口是更合适的。
但是如果初始类没有提供接口,那么可以考虑重构,提供一组接口或者使用继承的方式完成代理。或者在没有接口,无法继承的情况下也可以使用CGLIB,通过字节码增强完成代理。
package com.caldarius.reflection;
/**
* @Date: 2024/10/10
* @Author: Caldarius
* @Description:
**/
//被代理类和代理类均实现的通用接口,调用方实际上依赖于这个接口,这里体现了依赖倒置的思想。
//上层不直接依赖于下层,双方都依赖于抽象的接口/抽象类
interface Interface {
void doSomething();
void somethingElse(String arg);
}
//被代理的对象
class RealObject implements Interface {
@Override public void doSomething() {
System.out.println("doSomething");
}
@Override public void somethingElse(String arg) {
System.out.println("somethingElse " + arg);
}
}
//代理对象
class SimpleProxy implements Interface {
//代理对象持有被代理对象
private Interface proxied;
SimpleProxy(Interface proxied) {
this.proxied = proxied;
}
@Override public void doSomething() {
//代理对象在调用被代理对象对应方法之前的操作
System.out.println("SimpleProxy doSomething before proxied doSomething");
//被代理对象调用本身的方法
proxied.doSomething();
//代理对象在调用被代理对象对应方法之后的操作
System.out.println("SimpleProxy doSomething after proxied doSomething");
}
@Override public void somethingElse(String arg) {
System.out.println(
"SimpleProxy somethingElse " + arg+" before proxied somethingElse");
proxied.somethingElse(arg);
System.out.println(
"SimpleProxy somethingElse " + arg+" after proxied somethingElse");
}
}
class SimpleProxyDemo {
public static void consumer(Interface iface) {
iface.doSomething();
iface.somethingElse("bonobo");
}
public static void main(String[] args) {
System.out.println("real done:");
consumer(new RealObject());
System.out.println("proxy done:");
consumer(new SimpleProxy(new RealObject()));
}
}
/*
Output:
real done:
doSomething
somethingElse bonobo
proxy done:
SimpleProxy doSomething before proxied doSomething
doSomething
SimpleProxy doSomething after proxied doSomething
SimpleProxy somethingElse bonobo before proxied somethingElse
somethingElse bonobo
SimpleProxy somethingElse bonobo after proxied somethingElse
*/
反射实现动态代理
动态代理是一种更加灵活的方式,省去了手动创建代理类的过程,在运行中创建代理类。缺点是需要在invoke()
方法中根据传入参数锁定需要代理的方法,操作起来比较复杂。
package com.caldarius.reflection;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @Date: 2024/10/10
* @Author: Caldarius
* @Description:
**/
//调用处理器接口的实现类,判断是什么方法调用,然后决定如何处理
class DynamicProxyHandler implements InvocationHandler {
private Object proxied;
DynamicProxyHandler(Object proxied) {
this.proxied = proxied;
}
//三个参数分别是:被代理的对象,当前方法,方法的参数
@Override public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println(
"**** proxy: " + proxy.getClass() +
", method: " + method + ", args: " + args);
if(args != null)
for(Object arg : args)
System.out.println(" " + arg);
return method.invoke(proxied, args);
}
}
class SimpleDynamicProxy {
public static void consumer(Interface iface) {
iface.doSomething();
iface.somethingElse("bonobo");
}
public static void main(String[] args) {
//使用普通对象调用consumer
RealObject real = new RealObject();
consumer(real);
//构建代理对象,再次调用consumer
//三个参数分别是:
//1.一个类加载器
//2.希望代理对象实现的接口列表
//3.InvocationHandler对象的一个实现类 -> 这个类将负责处理代理的主要逻辑
Interface proxy = (Interface) Proxy.newProxyInstance(
Interface.class.getClassLoader(),
new Class[]{ Interface.class },
new DynamicProxyHandler(real));
consumer(proxy);
}
}
/*
Output:
doSomething
somethingElse bonobo
**** proxy: class com.caldarius.reflection.$Proxy0, method: public abstract void com.caldarius.reflection.Interface.doSomething(), args: null
doSomething
**** proxy: class com.caldarius.reflection.$Proxy0, method: public abstract void com.caldarius.reflection.Interface.somethingElse(java.lang.String), args: [Ljava.lang.Object;@1e80bfe8
bonobo
somethingElse bonobo
*/
在invoke中筛选方法的示例
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(method.getName().equals("interesting"))
System.out.println(
"Proxy detected the interesting method");
return method.invoke(proxied, args);
}
}
CGLIB
使用CGLib完成动态代理
package com.caldarius.reflection;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibProxy implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println(
"**** proxy: " +
", method: " + method + ", args: " + objects);
if(objects != null)
for(Object arg : objects)
System.out.println(" " + arg);
Object res = methodProxy.invokeSuper(o, objects);
System.out.println("END");
return res;
}
}
class EnhancerHandlerFactory{
public Object getProxy() {
Enhancer enhancer = new Enhancer();
//设置被代理的对象
enhancer.setSuperclass(RealObject.class);
//设置动态代理处理器
enhancer.setCallback(new CglibProxy());
return enhancer.create();
}
}
class TestProxy{
public static void main(String[] args) {
RealObject realObject = (RealObject) new EnhancerHandlerFactory().getProxy();
realObject.doSomething();
realObject.somethingElse("CGLIB Proxy");
}
}
/*
output:
**** proxy: , method: public void com.caldarius.reflection.RealObject.doSomething(), args: [Ljava.lang.Object;@46f5f779
doSomething
END
**** proxy: , method: public void com.caldarius.reflection.RealObject.somethingElse(java.lang.String), args: [Ljava.lang.Object;@71e7a66b
CGLIB Proxy
somethingElse CGLIB Proxy
END
*/