Java代理的几种实现方式总结
作者:铿然架构
1. 简介
本文将通过例子说明java代理的几种实现方式,并比较它们之间的差异。
1.1 例子应用场景
代理对象给被代理对象(目标对象),增加一个目标对象方法被调用的日志,用于后续通过日志采集统计方法被调用次数。
1.2 被代理对象代码
后面所有例子均使用这些相同代码。
● 被代理对象接口
public interface Animal { void say(); }
● 被代理对象Dog类
import lombok.extern.slf4j.Slf4j; @Slf4j public class Dog implements Animal { @Override public void say() { log.info("汪汪"); } }
2. 静态代理
简单点说,静态代理就是在编码阶段已经确定了代理对象。
这里简单回顾下代理模式的基本类结构:
类 | 描述 |
---|---|
Subject | 被代理目标类的接口。 |
RealSubject | 被代理目标类。 |
ProxySubject | 代理类,需要实现被代理目标类接口,同时持有被代理目标类对象实例,实际调用时,通过委托的方式调用被代理目标类。 |
SubjectFactory | 被代理目标类的工厂类,通过工厂类屏蔽创建Subject的细节,客户端看到的是Subject,至于具体是哪个实现类不需要关心。 |
2.1 代码示例
2.1.1 DogStaticProxy
Dog代理类:
import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; @Slf4j @AllArgsConstructor // 代理类,实现Subject接口 public class DogStaticProxy implements Animal { // 持有RealSubject对象实例 private Dog dog; @Override public void say() { // 增强的能力,打印方法调用日志 log.info("Dog.say() called."); // 通过委托的方式调用RealSubject对象实例方法 dog.say(); } }
2.1.2 DuckStaticProxy
Duck代理类:
import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; @Slf4j @AllArgsConstructor public class DuckStaticProxy implements Animal { private Duck duck; @Override public void say() { log.info("Dog.say() called."); duck.say(); } }
2.1.3 StaticProxyFactory
代理工厂类:
public class StaticProxyFactory { // 屏蔽创建Subject实例的细节,使用者看到的只有Subject接口,即Animal public static Animal createDog() { return new DogStaticProxy(new Dog()); } public static Animal createDuck() { return new DuckStaticProxy(new Duck()); } }
2.1.4 StaticProxyFactoryTest
测试代码:
import org.junit.jupiter.api.Test; class StaticProxyFactoryTest { @Test public void exec() { Animal dog = StaticProxyFactory.createDog(); Animal duck = StaticProxyFactory.createDuck(); dog.say(); duck.say(); } }
2.1.5 输出日志
2023-12-28 12:00:35 INFO DogStaticProxy:16 - Dog.say() called.
2023-12-28 12:00:35 INFO Dog:10 - 汪汪
2023-12-28 12:00:35 INFO DuckStaticProxy:13 - Dog.say() called.
2023-12-28 12:00:35 INFO Duck:9 - 嘎嘎
3. JDK动态代理
动态代理就是在编码阶段还没有确定代理对象,在运行期才动态确定。
如下是JDK动态代理的类结构:
类 | 描述 |
---|---|
Subject | 被代理目标类的接口。 |
RealSubject | 被代理目标类。 |
InvocationHandler | 可以理解为一个拦截器,被代理对象方法调用时都会先调用此接口的invoke方法,在invoke方法内部去调用被代理对象方法。 |
InvocationHandler实现类 | InvocationHandler的实现类 |
Proxy | JDK代理工具类,用于生成代理对象,生成代理对象时会用到RealSubject和InvocationHandler实现类。 |
3.1 代码示例
3.1.1 AnimalInvocationHandler
InvocationHandler的实现类:
import lombok.extern.slf4j.Slf4j; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; @Slf4j public class AnimalInvocationHandler implements InvocationHandler { private Animal animal; // 通过构造器传入目标类实例对象 public AnimalInvocationHandler(Animal animal) { this.animal = animal; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 增强能力,打印方法调用日志,采集后统计调用次数 log.info(getCalledMethodInfo(method)); // 调用目标类方法,参数是目标类实例和对应的方法参数 return method.invoke(animal, args); } // 辅助方法,返回被调用方法信息 private String getCalledMethodInfo(Method method) { StringBuilder builder = new StringBuilder(); builder.append(animal.getClass().getSimpleName()) .append(".") .append(method.getName()) .append("() called."); return builder.toString(); } }
3.1.2 JDKProxyFactory
代理工厂类:
import java.lang.reflect.Proxy; public class JDKProxyFactory { public static Animal createDog() { return getProxy(new Dog()); } public static Animal createDuck() { return getProxy(new Duck()); } private static Animal getProxy(Animal implInstance) { AnimalInvocationHandler handler = new AnimalInvocationHandler(implInstance); // 创建代理对象 Object proxy = Proxy.newProxyInstance(implInstance.getClass().getClassLoader(), implInstance.getClass().getInterfaces(), handler); return (Animal) proxy; } }
3.1.3 JDKProxyFactoryTest
测试类:
import org.junit.jupiter.api.Test; class JDKProxyFactoryTest { @Test public void exec() { Animal dog = JDKProxyFactory.createDog(); Animal duck = JDKProxyFactory.createDuck(); dog.say(); duck.say(); } }
3.1.4 输出日志
2023-12-28 12:27:37 INFO AnimalInvocationHandler:19 - Dog.say() called.
2023-12-28 12:27:37 INFO Dog:10 - 汪汪
2023-12-28 12:27:37 INFO AnimalInvocationHandler:19 - Duck.say() called.
2023-12-28 12:27:37 INFO Duck:9 - 嘎嘎
3.2 通用能力重构
打印方法调用日志不仅仅适用于Animal,也适用于其他所有有此需求的类,可以优化一下。
注:此优化非JDK动态代理的能力,存粹就是顺便处理一下,重构无处不在,只要你想做。
3.2.1 代码
3.2.1.1 MethodPrinterInvocationHandler
AnimalInvocationHandler只能适用于Animal,重构通过泛型实现:
import lombok.extern.slf4j.Slf4j; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; @Slf4j // 使用泛型,只要是有接口的实现类都可以使用 public class MethodPrinterInvocationHandler<T> implements InvocationHandler { private T t; public MethodPrinterInvocationHandler(T t) { this.t = t; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { log.info(getCalledMethod(method)); return method.invoke(t, args); } private String getCalledMethod(Method method) { StringBuilder builder = new StringBuilder(); builder.append(t.getClass().getSimpleName()) .append(".") .append(method.getName()) .append("() called."); return builder.toString(); } }
3.2.1.2 MethodPrinterJDKProxyFactory
代理工厂类同样通过泛型实现:
import java.lang.reflect.Proxy; public class MethodPrinterJDKProxyFactory { // 使用泛型,除了Animal接口和其子类,其他接口和子类也可以使用 public static <T, R> R getProxy(T implInstance ) { MethodPrinterInvocationHandler<T> handler = new MethodPrinterInvocationHandler(implInstance); return (R) Proxy.newProxyInstance(implInstance.getClass().getClassLoader(), implInstance.getClass().getInterfaces(), handler); } }
3.2.1.3 AnimalJDKProxyFactory
public class AnimalJDKProxyFactory { public static Animal createDuck() { return MethodPrinterJDKProxyFactory.getProxy(new Duck()); } public static Animal createDog() { return MethodPrinterJDKProxyFactory.getProxy(new Dog()); } }
3.2.1.4 MethodPrinterJDKProxyFactoryTest
import org.junit.jupiter.api.Test; class MethodPrinterJDKProxyFactoryTest { @Test public void exec() { Animal dog = AnimalJDKProxyFactory.createDog(); Animal duck = AnimalJDKProxyFactory.createDuck(); dog.say(); duck.say(); } }
3.2.1.5 打印日志
2023-12-28 12:54:00 INFO MethodPrinterInvocationHandler:18 - Dog.say() called.
2023-12-28 12:54:00 INFO Dog:10 - 汪汪
2023-12-28 12:54:00 INFO MethodPrinterInvocationHandler:18 - Duck.say() called.
2023-12-28 12:54:00 INFO Duck:9 - 嘎嘎
3.3 JDK动态代理限制
JDK动态代理只能代理接口,无法直接代理类,下面看个例子。
3.3.1 代码
3.3.1.1 Cat
新增一个类,没有实现Animal接口,用于证明没有接口无法使用JDK动态代理:
import lombok.extern.slf4j.Slf4j; @Slf4j public class Cat { public void say() { log.info("喵喵"); } }
3.3.1.2 CatInvocationHandler
拦截器也要调整,将Cat作为构造器参数,不能再使用Animal:
import lombok.extern.slf4j.Slf4j; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; @Slf4j public class CatInvocationHandler implements InvocationHandler { private Cat cat; public CatInvocationHandler(Cat cat) { this.cat = cat; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { log.info(getCalledMethod(method)); return method.invoke(cat, args); } private String getCalledMethod(Method method) { StringBuilder builder = new StringBuilder(); builder.append(cat.getClass().getSimpleName()) .append(".") .append(method.getName()) .append("() called."); return builder.toString(); } }
3.3.1.3 ErrorJDKProxyFactory
代理工厂类:
import java.lang.reflect.Proxy; public class ErrorJDKProxyFactory { public static Cat createCat() { return getProxy(new Cat()); } private static Cat getProxy(Cat cat) { CatInvocationHandler handler = new CatInvocationHandler(cat); // 这里的写法仍然不变,和之前一样,只是类型变化 return (Cat) Proxy.newProxyInstance(cat.getClass().getClassLoader(), cat.getClass().getInterfaces(), handler); } }
3.3.1.4 ErrorJDKProxyFactoryTest
测试类:
import org.junit.jupiter.api.Test; class ErrorJDKProxyFactoryTest { @Test void createCat() { Cat cat = ErrorJDKProxyFactory.createCat(); cat.say(); } }
3.3.1.5 输出日志
java.lang.ClassCastException: com.sun.proxy.$Proxy8 cannot be cast to com.kengcoder.javaawsome.proxy.jdkproxy.Cat at com.kengcoder.javaawsome.proxy.jdkproxy.ErrorJDKProxyFactory.getProxy(ErrorJDKProxyFactory.java:13) at com.kengcoder.javaawsome.proxy.jdkproxy.ErrorJDKProxyFactory.createCat(ErrorJDKProxyFactory.java:8)
3.3.1.6 原因分析
● 通过类代理
两个对象类型不一致,没法做强转。
● 通过接口代理
代理对象类型为Animal,可以强转为Animal接口。
4. CGLIB动态代理
CGLIB动态代理是三方库实现,和JDK动态代理的区别是:既可以代理接口,也可以代理类。
CGLIB动态代理实现的类结构如下:
类 | 描述 |
---|---|
Subject | 被代理目标类的接口。 |
RealSubject | 被代理目标类。 |
MethodInterceptor | 可以理解为一个拦截器,被代理对象方法调用时都会先调用此接口的intercept方法,在intercept方法内部去调用被代理对象方法。 |
MethodInterceptor实现类 | MethodInterceptor的实现类 |
Enhancer | 代理工具类,用于生成代理对象,生成代理对象时会用到RealSubject和MethodInterceptor实现类。 |
4.1 代码
4.1.1 CglibInterceptor
MethodInterceptor的实现类:
import lombok.extern.slf4j.Slf4j; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; @Slf4j public class CglibInterceptor implements MethodInterceptor { @Override public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { log.info(getCalledMethod(method)); return methodProxy.invokeSuper(target, args); } private String getCalledMethod(Method method) { StringBuilder builder = new StringBuilder(); builder.append(method.getDeclaringClass().getSimpleName()) .append(".") .append(method.getName()) .append("() called."); return builder.toString(); } }
4.1.2 CglibProxyFactory
CGLIB代理工厂类,抽象出通用方法:
import net.sf.cglib.proxy.Enhancer; public class CglibProxyFactory { public static <T> T getProxy(Class<T> clazz) { Enhancer enhancer = new Enhancer(); // 设置超类,字面看就是代理对象是目标类的子类,通过继承方式实现,而不是组合方式实现。 enhancer.setSuperclass(clazz); enhancer.setCallback(new CglibInterceptor()); return (T)enhancer.create(); } }
4.1.3 AnimalCglibProxyFactory
Animal工厂类:
public class AnimalCglibProxyFactory { public static Animal createDog() { return CglibProxyFactory.getProxy(Dog.class); } public static Animal createDuck() { return CglibProxyFactory.getProxy(Duck.class); } }
4.1.4 CglibProxyFactoryTest
import org.junit.jupiter.api.Test; class CglibProxyFactoryTest { @Test public void exec() { Animal dog = AnimalCglibProxyFactory.createDog(); Animal duck = AnimalCglibProxyFactory.createDuck(); dog.say(); duck.say(); } }
4.1.5 输出日志
2023-12-28 13:23:56 INFO CglibInterceptor:13 - Dog.say() called.
2023-12-28 13:23:56 INFO Dog:10 - 汪汪
2023-12-28 13:23:56 INFO CglibInterceptor:13 - Duck.say() called.
2023-12-28 13:23:56 INFO Duck:9 - 嘎嘎
5. 总结
本文介绍了几种java代理的实现方式,在过程中通过重构展示了如何通过java泛型实现通用能力,以及通过工厂模式屏蔽类实例创建细节,提升扩展性。
几种java代理的差别如下:
比较项 | 静态代理 | JDK动态代理 | CGLIB动态代理 |
---|---|---|---|
代理对象 | 接口和类 | 接口 | 接口和类 |
实现接口方法 | 需手动一一实现 | 自动实现 | 自动实现 |
拦截方法 | 每个方法独立实现,完全隔离开。 | 都在一处实现,当指定方法拦截时要增加判断逻辑。 | 同静态代理。 |
6. 最后
以上就是Java代理的几种实现方式总结的详细内容,更多关于Java代理实现方式的资料请关注脚本之家其它相关文章!