介绍静态代理
代理模式在不改变原始类(或者叫被代理类)代码的情况下,通过引入代理类来给原始类附加功能。
下面我们通过代码来解释以下这句话。
具体的实现代码如下所示:
/**
* 原始类(或者称为被代理类)
* UserController 类只负责业务功能
*/
public class UserController implements IUserController {
public String login() {
// 业务逻辑
return "login() 的返回值";
}
public String register() {
// 业务逻辑
return "register() 的返回值";
}
}
/**
* 代理类
* UserControllerProxy 负责在业务代码执行前后附加其他逻辑代码,并通过委托的方式调用原始类来执行业务代码
*/
public class UserControllerProxy implements IUserController {
IUserController userController;
public UserControllerProxy(IUserController userController) {
this.userController = userController;
}
@Override
public String login() {
System.out.println("在 login() 业务代码执行前,附加的其他逻辑代码");
String login = userController.login();
System.out.println("在 login() 业务代码执行后,附加的其他逻辑代码");
return login;
}
@Override
public String register() {
System.out.println("在 register() 业务代码执行前,附加的其他逻辑代码");
String register = userController.register();
System.out.println("在 register() 业务代码执行后,附加的其他逻辑代码");
return register;
}
}
public interface IUserController {
public String login();
public String register();
}
public static void main(String[] args) {
UserControllerProxy userControllerProxy = new UserControllerProxy(new UserController());
userControllerProxy.login();
userControllerProxy.register();
/*
执行结果打印如下:
在 login() 业务代码执行前,附加的其他逻辑代码
在 login() 业务代码执行后,附加的其他逻辑代码
在 register() 业务代码执行前,附加的其他逻辑代码
在 register() 业务代码执行后,附加的其他逻辑代码
*/
}
代理类 UserControllerProxy 和原始类 UserController 实现相同的接口 IUserController。
- UserController 类只负责业务功能。
- 代理类 UserControllerProxy 负责在业务代码执行前后附加其他逻辑代码,并通过委托的方式调用原始类来执行业务代码。
上面的代码在不改变原始类(UserControler 类)代码的情况下,通过引入代理类(UserControllerProxy 代理类),来给原始类附加功能。
下面解释一下委托的意思,有两个对象参与处理同一个请求,接受请求的对象将请求委托给另一个对象来处理。
上面的代码中,两个对象(UserControler 的实例和 UserControllerProxy 的实例)参与处理同一个请求,接受请求的对象(UserControllerProxy 的实例)将请求委托给另一个对象(UserControler 的实例)来处理。
上面代码这种实现代理模式的前提是,原始类已经实现了某接口,或者原始类没有定义接口,但是我们有权修改原始类让它实现某接口。
但是,如果原始类并没有定义接口,并且原始类代码并不是我们开发维护的(比如它来自一个第三方的类库),我们也没办法直接修改原始类,给它重新定义一个接口。在这种情况下,我们该如何实现代理模式呢?
在这种情况下,我们可以让代理类继承原始类,具体的实现代码如下所示:
/**
* 原始类(或者称为被代理类)
* UserController 类只负责业务功能
*/
public class UserController {
public String login() {
// 业务逻辑
return "login() 的返回值";
}
public String register() {
// 业务逻辑
return "register() 的返回值";
}
}
/**
* 代理类
* UserControllerProxy 负责在业务代码执行前后附加其他逻辑代码,并通过委托的方式调用原始类来执行业务代码
*/
public class UserControllerProxy extends UserController {
@Override
public String login() {
System.out.println("在 login() 业务代码执行前,附加的其他逻辑代码");
String login = super.login();
System.out.println("在 login() 业务代码执行后,附加的其他逻辑代码");
return login;
}
@Override
public String register() {
System.out.println("在 register() 业务代码执行前,附加的其他逻辑代码");
String register = super.register();
System.out.println("在 register() 业务代码执行后,附加的其他逻辑代码");
return register;
}
}
public static void main(String[] args) {
UserControllerProxy userControllerProxy = new UserControllerProxy();
userControllerProxy.login();
userControllerProxy.register();
/*
执行结果打印如下:
在 login() 业务代码执行前,附加的其他逻辑代码
在 login() 业务代码执行后,附加的其他逻辑代码
在 register() 业务代码执行前,附加的其他逻辑代码
在 register() 业务代码执行后,附加的其他逻辑代码
*/
}
介绍动态代理
刚刚的代码实现实际上存在一定的问题:
- 一方面,我们需要在代理类中,将原始类的所有的方法,都重新实现一遍,并且为每个方法都附加相似的代码逻辑(附加功能)。
- 另一方面,如果要添加附加功能的类有不止一个,我们需要针对每个类都创建一个代理类。
简单来说就是,静态代理模式可能存在太多重复代码的问题。那这个问题怎么解决呢?
我们可以使用动态代理来解决这个问题。
动态代理就是,我们不事先为原始类创建代理类,而是在程序运行的过程中,动态的为原始类创建代理类。
在 Java 中,动态代理有很多种实现方式。其中两种最常见的实现方式是:
- JDK 自身提供的动态代理;
- cglib 动态代理。
JDK 自身提供的动态代理,主要利用了 Java 的反射机制。
cglib 动态代理,利用传说中更高性能的字节码操作机制。
下面我们逐一介绍 JDK 自身提供的动态代理 和 cglib 动态代理的实现。
JDK 动态代理
我们用 JDK 自身提供的动态代理来实现上面的功能,具体的代码实现如下所示:
/**
* 原始类(或者称为被代理类)
* UserController 类只负责业务功能
*/
public class UserController implements IUserController {
public String login() {
// 业务逻辑
return "login() 的返回值";
}
public String register() {
// 业务逻辑
return "register() 的返回值";
}
}
public class ProxyFactory {
public Object newProxyInstance(Object proxied) {
ClassLoader classLoader = proxied.getClass().getClassLoader();
Class<?>[] interfaces = proxied.getClass().getInterfaces();
myInvocationHandler myInvocationHandler = new myInvocationHandler(proxied);
Object proxyInstance = Proxy.newProxyInstance(classLoader, interfaces, myInvocationHandler);
return proxyInstance;
}
private class myInvocationHandler implements InvocationHandler {
private Object proxied;
public myInvocationHandler(Object proxied) {
this.proxied = proxied;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("在业务代码执行前,附加的其他逻辑代码" + method.getName());
Object result = method.invoke(proxied, args);
System.out.println("在业务代码执行后,附加的其他逻辑代码" + method.getName());
return result;
}
}
}
public interface IUserController {
public String login();
public String register();
}
public static void main(String[] args) {
ProxyFactory proxyFactory = new ProxyFactory();
IUserController userControllerProxy = (IUserController) proxyFactory.newProxyInstance(new UserController());
String login = userControllerProxy.login();
String register = userControllerProxy.register();
System.out.println(login);
System.out.println(register);
/*
执行结果打印如下:
在业务代码执行前,附加的其他逻辑代码login
在业务代码执行前,附加的其他逻辑代码login
在业务代码执行前,附加的其他逻辑代码register
在业务代码执行前,附加的其他逻辑代码register
login() 的返回值
register() 的返回值
*/
}
上面的 JDK 自身提供的动态代理的代码,其中最主要的就是 Proxy.newProxyInstance(classLoader, interfaces, myInvocationHandler);
这个方法,下面介绍一下这个方法定义的参数:
- ClassLoader loader:定义代理类的类加载器
- Class<?>[] interfaces:代理类要实现的接口列表
- InvocationHandler h:将方法调用分派给的调用处理程序
其中 InvocationHandler 类的 invoke() 方法的第一个参数 proxy 暗藏玄机,期待一起探索。
JDK 自身提供的动态代理的实现原理就是,代理类实例实现了指定的接口,当我们调用代理类实例的任意一个方法(接口中定义的方法)时,都会被分派到我们定义的调用处理程序中,也就是 InvocationHandler 实例的 invoke() 方法,在 invoke() 方法中通过反射将请求委托给原始类对象。
cglib 动态代理
上面介绍的 JDK 自身提供的动态代理仍然有局限性,因为要想创建原始类的代理类,那么原始类必须实现接口。如果原始类没有实现接口,那么 JDK 自身提供的动态代理时无法使用的。
那这个问题怎么解决呢?我们可以使用 cglib 动态代理解决这个问题。
cglib 动态代理采取的是创建原始类(被代理类)的子类的方式。
具体的代码实现如下所示:
因为是子类化,我们可以达到近似使用被调用者本身的效果。在 Spring 编程中,框架通常会处理这种情况,当然我们也可以显式指定。
Spring AOP 支持两种模式的动态代理
如利用传说中更高性能的字节码操作机制,类似 ASM、cglib(基于 ASM)、Javassist 等。
JDK 动态代理在设计和实现上与 cglib 等方式有什么不同,进而如何取舍?
动态代理解决的问题,及应用场景
动态代理解决了什么问题,它的应用场景是什么?
日志、用户鉴权、全局性异常处理、性能监控,甚至事务处理