相信大家对动态代理这个词并不模式,Spring 中的 AOP 的实现和 RPC 框架都使用到了动态代理。
在此之前,我们先来复习一下代理设计模式,也被称为静态代理。
假设现在有一个需求:要在已有的实现类前后加入日志打印功能,但是不能修改原类的代码,这时可以使用静态代理解决。
通过往代理类 ModuleAProxy 中传入 ModuleA 中的引用,每次调用具体方法时,不再调用实现类,而改为调用代理类的同名方法,并在原有业务代码的基础上,实现代码增强。
public interface BusinessInterface {
void business();
}
public class ModuleA implements BusinessInterface{
@Override
public void business() {
System.out.println("business doing");
}
}
public class ModuleAProxy implements BusinessInterface {
BusinessInterface module;
public ModuleAProxy(BusinessInterface module) {
this.module = module;
}
@Override
public void business() {
System.out.println("------业务执行前------");
module.business();
System.out.println("------业务执行后------");
}
}
public class ModuleAProxyTest {
public static void consumer(BusinessInterface businessInterface) {
businessInterface.business();
}
public static void main(String[] args) {
consumer(new ModuleA());
consumer(new ModuleAProxy(new ModuleA()));
}
}
business doing
------业务执行前------
business doing
------业务执行后------
但静态代理还是存在一定的局限性,如果接口中有多个实现方法,则需要为每个方法编写新的代理方法。
接下来,我们一起来看看动态代理,动态代理可通过 JDK 原生类或 CGLIB 实现,本文采用 JDK 原生方法。
代理类需要实现 InvocationHandler 接口,再通过 Proxy.newProxyInstance() 方法创建动态代理,该方法需要的三个参数分别为:
- 一个类加载器(可以为被代理类或其实现接口)
- 希望代理实现的接口表
- 一个 InvocationHandler 接口的实现
public interface ModuleImp {
void methodA();
void methodB(String str);
void methodC(String str, int num);
}
public class ModuleB implements ModuleImp {
@Override
public void methodA() {
System.out.println("methodA doing");
}
@Override
public void methodB(String str) {
System.out.println("methodB doing str:" + str);
}
@Override
public void methodC(String str, int num) {
System.out.println("methodC doing str:" + str + " num:" + num);
}
}
public class ModuleDynamicHandler implements InvocationHandler {
private Object proxied;
public ModuleDynamicHandler(Object proxied) {
this.proxied = proxied;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("------业务执行前------");
// 可获取方法中的参数
if (args != null) {
for (Object arg : args) {
System.out.println(arg);
}
}
Object result = method.invoke(proxied, args);
System.out.println("------业务执行后------");
return result;
}
}
public class ModuleDynamicProxyTest {
public static void consumer(ModuleImp moduleImp) {
moduleImp.methodA();
moduleImp.methodB("ABC");
moduleImp.methodC("ABC", 123);
}
public static void main(String[] args) {
ModuleB moduleB = new ModuleB();
consumer(moduleB);
ModuleImp proxyInstance = (ModuleImp) Proxy.newProxyInstance(ModuleB.class.getClassLoader(), new Class[]{ModuleImp.class},
new ModuleDynamicHandler(moduleB));
consumer(proxyInstance);
}
}
methodA doing
methodB doing str:ABC
methodC doing str:ABC num:123
------业务执行前------
methodA doing
------业务执行后------
------业务执行前------
ABC
methodB doing str:ABC
------业务执行后------
------业务执行前------
ABC
123
methodC doing str:ABC num:123
值得一提的是,使用 method.invoke() 调用代理方法时,接口中的所有方法都默认会在代理类中执行。倘若想对部分方法进行过滤,希望 methodA 方法不执行日志打印,可通过方法名进行判断,参考以下代码:
public class ModuleDynamicHandler implements InvocationHandler {
private Object proxied;
public ModuleDynamicHandler(Object proxied) {
this.proxied = proxied;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("methodA")){
return method.invoke(proxied, args);
}
System.out.println("------业务执行前------");
if (args != null) {
for (Object arg : args) {
System.out.println(arg);
}
}
Object result = method.invoke(proxied, args);
System.out.println("------业务执行后------");
return result;
}
}
methodA doing
methodB doing str:ABC
methodC doing str:ABC num:123
methodA doing
------业务执行前------
ABC
methodB doing str:ABC
------业务执行后------
------业务执行前------
ABC
123
methodC doing str:ABC num:123
------业务执行后------
Process finished with exit code 0
上述是基于JDK原生的动态代理实现方法,存在一定的限制条件,实现类和代理类必须拥有相同接口,若实现类未拥有接口,则无法生效(CGLIB可以实现此类需要)。
本次分享至此结束,希望本文对你有所帮助,若能点亮下方的点赞按钮,在下感激不尽,谢谢您的【精神支持】。
若有任何疑问,也欢迎与我交流,若存在不足之处,也欢迎各位指正!
参考资料 | 引用地址 |
---|---|
Java 动态代理作用是什么? | https://www.zhihu.com/question/20794107 |