静态代理模式
public interface Singer {
void sing();
}
public class SHESinger implements Singer {
@Override
public void sing() {
System.out.println("SHE 唱《中国话》");
}
}
// 实现某接口并持有该接口的引用
public class Proxy implements Singer {
private Singer singer;
public Proxy() {
// 无需传入, 关系明确, 直接内定
this.singer = new SHESinger();
}
@Override
public void sing() {
System.out.println("演出前谈合同");
singer.sing();
System.out.println("演出后收钱");
}
}
public class Test {
public static void main(String[] args) {
Singer proxy = new Proxy();
proxy.sing();
}
}
演出前谈合同
SHE 唱《中国话》
演出后收钱
静态代理模式和装饰器模式的异同
两者都是一个类实现某个接口并持有该接口的引用
代理模式: 代理和被代理两者的关系很明确, 使用者无需关注被代理对象. 如 dubbo 的 rpc 访问
装饰模式: 主要是给某个主体扩展功能, 功能顺序随意且可叠加. 如 dubbo 的 mock,cluster,failover 等功能
动态代理模式
jdk
JDK动态代理主要涉及java.lang.reflect包下边的两个类:Proxy和InvocationHandler。其中,InvocationHandler是一个接口,可以通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态地将横切逻辑和业务逻辑编织在一起。
代理某个对象, 利用反射生成一个接口的实现类, 在调用具体方法前调用 InvocationHandler 来处理
通过这种方式生成的代理类都会继承 Proxy, Java 是单继承, 所以只能实现接口了, 所以说 jdk 只适用于接口方式的代理
public class SingerInvocationHandler implements InvocationHandler {
private Singer singer;
public SingerInvocationHandler(Singer singer) {
this.singer = singer;
}
/**
* @param proxy 代理对象(用来代理singer)
* @param method 被代理的方法
* @param args 被代理的方法的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("演出前商谈");
// 这里切记 invoke 的对象不是 target 而是 入参proxy
// 入参proxy 是生成的代理对象实例, 调用其method会被代理到invoke方法, 从而导致循环调用, 堆栈溢出
Object result = method.invoke(singer, args);
System.out.println("演出后分收钱");
return result;
// return proxy;
}
public static Singer getProxy(Singer singer) {
return (Singer) Proxy.newProxyInstance(singer.getClass().getClassLoader(), singer.getClass().getInterfaces(), new SingerInvocationHandler(singer));
}
}
public class Test {
public static void main(String[] args) throws Throwable {
Singer singer = new SHESinger();
Singer proxy = SingerInvocationHandler.getProxy(singer);
proxy.sing();
}
}
演出前商谈
SHE 唱《中国话》
演出后分收钱
cglib (code generator library)
CGLIB是一个基于ASM的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。CGLIB通过继承方式实现代理,在子类中采用方法拦截的技术拦截所有父类方法的调用并顺势织入横切逻辑。
创建子类继承现有类, 所以要代理的类不能加 final, 子类要覆盖父类的方法(super.method()前后添加扩展逻辑), 所以要代理的方法不能加 final, 加了后子类不能覆盖该方法, 所以调用的直接就是父类定义的方法了, 丢失了扩展逻辑
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
public class SHESingerMethodInterceptor implements MethodInterceptor {
/**
* @param o 被代理的对象
* @param method 被拦截的方法
* @param objects 被拦截的方法的参数
* @param methodProxy 要触发父类的方法对象
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
if ("sing".equals(method.getName())) {
System.out.println("演出前商谈");
// invokeSuper 而不是 invoke
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("演出后分收钱");
return result;
}
return null;
}
}
public class Test {
public static void main(String[] args) throws Throwable {
// 通过CGLIB动态代理获取代理对象的过程
Enhancer enhancer = new Enhancer();
// 设置enhancer对象的父类
enhancer.setSuperclass(SHESinger.class);
// 设置enhancer的回调对象
enhancer.setCallback(new SHESingerMethodInterceptor());
// 创建代理对象
SHESinger proxy = (SHESinger) enhancer.create();
// 通过代理对象调用目标方法
proxy.sing();
}
}
演出前商谈
SHE 唱《中国话》
演出后分收钱