目录
- 1. 前言
- 2. 静态代理
- 2.1 简介
- 2.2 实例
- 3. 动态代理
- 3.1 简介
- 3.2 实例
- 3.3 基于 Jdk 的动态代理
- 3.4 cglib 代理
1. 前言
代理模式是一种设计模式,提供了对目标对象额外的访问形式,即通过代理对象来实现对目标对象的访问,能够在不修改原目标对象的前提下提供额外的功能操作,实现对目标对象的功能扩展。
简单来说,代理模式就是 设置一个中间代理来控制访问原目标对象,从而增加原对象的功能和简化访问方式。
AbstractSubject
:抽象对象,用于接口或抽象类来实现;RealSubject
:真实对象,被代理的对象;Proxy
:代理对象,用于代理真实对象,一般会做一些附属操作;Client
:客户,用代理对象来进行实际操作;
代理模式可以分为 动态代理 和 静态代理,我们能在不改变原来代码的情况下实现了对原来功能的增强,是 Spring 中 AOP 的核心思想。
2. 静态代理
2.1 简介
静态代理需要 代理对象和目标对象实现一样的接口,优点在于 能够在不修改目标对象的前提下扩展目标对象的功能。当缺点也很明显:
- 冗余:由于代理对象要和目标对象实现一样的接口,所以会导致产生过多的代理类;
- 不易维护:一旦接口增加了方法,目标对象和代理对象都要进行修改;
2.2 实例
- 定义一个抽象对象接口
/**
* @InterfaceName : Buy
* @Author : cunyu
* @Date : 2020/7/18 12:23
* @Version : 1.0
* @Description : 抽象对象
**/
public interface Buy {
public void buy();
}
- 定义真实对象实现接口;
/**
* @author : cunyu
* @version : 1.0
* @className : User
* @date : 2020/7/18 12:23
* @description : 真实对象
*/
public class User implements Buy{
@Override
public void buy() {
System.out.println("买书");
}
}
- 创建代理角色;
/**
* @author : cunyu
* @version : 1.0
* @className : UserProxy
* @date : 2020/7/18 12:25
* @description : 代理对象
*/
public class UserProxy implements Buy {
private User user;
public UserProxy() {
}
public UserProxy(User user) {
this.user = user;
}
@Override
public void buy() {
user.buy();
read();
}
public void read() {
System.out.println("看书");
}
}
- 创建客户对象
/**
* @author : cunyu
* @version : 1.0
* @className : Client
* @date : 2020/7/18 12:29
* @description : 客户对象
*/
public class Client {
public static void main(String[] args) {
User user = new User();
UserProxy userProxy = new UserProxy(user);
userProxy.buy();
}
}
3. 动态代理
3.1 简介
动态代理利用了 JDK API,动态地在内存中构建代理对象,从而实现对目标对象的代理功能,动态代理又叫做 JDK 代理 或 接口代理。
动态代理具有静态的优点,能够在不修改目标对象的前提下扩展目标对象的功能,但是不需要实现接口,但 要求目标对象必须实现接口,否则不能使用动态代理。此外,一个动态代理一般代理某一类业务,而且可以代理多个类(代理的是接口)。
静态代理和动态代理的区别:
- 静态代理在 编译时 已经实现,编译完成后代理类是一个实际的
.class
文件; - 动态代理在 运行时动态生成,即编译后没有实际的
.class
文件。而是在运行时动态生成类字节码,同时加载到 JVM 中。
动态代理可以分为两类,一类是 基于接口动态代理(JDK 动态代理),一类是 基于类的动态代理(cglib)。
3.2 实例
- 定义抽象对象
/**
* @InterfaceName : Buy
* @Author : cunyu
* @Date : 2020/7/18 12:23
* @Version : 1.0
* @Description : 抽象对象
**/
public interface Buy {
public void buy();
}
- 创建真实对象
/**
* @author : cunyu
* @version : 1.0
* @className : User
* @date : 2020/7/18 12:23
* @description : 真实对象
*/
public class User implements Buy{
@Override
public void buy() {
System.out.println("买书");
}
}
- 创建代理对象
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author : cunyu
* @version : 1.0
* @className : ProxyInvocationHandler
* @date : 2020/7/18 14:08
* @description : 动态代理对象
*/
public class ProxyInvocationHandler implements InvocationHandler {
private Buy buy;
public void setBuy(Buy buy) {
this.buy = buy;
}
/**
* @param
* @return
* @description 生成代理对象
* @date 2020/7/18 14:10
* @author cunyu1943
* @version 1.0
*/
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(), buy.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(buy, args);
read();
return result;
}
public void read() {
System.out.println("读书");
}
}
- 创建客户对象
/**
* @author : cunyu
* @version : 1.0
* @className : Client
* @date : 2020/7/18 12:29
* @description : 客户对象
*/
public class Client {
public static void main(String[] args) {
User user = new User();
// 代理实例的调用处理程序
ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();
// 将真实对象放置进去
proxyInvocationHandler.setBuy(user);
// 动态生成对应的代理类
Buy proxy = (Buy) proxyInvocationHandler.getProxy();
proxy.buy();
}
}
3.3 基于 Jdk 的动态代理
- 先定义一个接口;
public interface MyCalculator{
int mul(int num1, int num2);
}
- 实现接口;
public class MyCalculatorImpl implements MyCalculator{
@Override
public int mul(int num1, int num2){
return num1 * num2;
}
}
- 实现代理类
public class CalculatorProxy{
public static Object getInstance(final MyCalculatorImpl myCalculator){
return Proxy.newProxyInstance(CalculatorProxy.class.getClassLoader(), myCalculator.getClass().getInterfaces(), new InvocationHandler(){
/**
* @param proxy 代理对象
* @param method 代理的方法
* @param args 方法的参数
* @return
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
System.out.println(method.getName() "方法开始执行...");
Object invoke = method.invoke(myCalculator, args);
System.out.println(method.getName() "方法执行结束...");
}
});
}
}
其中 Proxy.newProxyInstance
方法一共接收了三个参数,分别代表:
- 一个
classloader
- 代理多项实现的接口
- 代理对象方法的处理器
3.4 cglib 代理
由于静态代理需要实现目标对象的相同接口,就可能导致代理类增多,难以维护,这个时候可以用动态代理。而动态代理也有个限制:目标对象一定要有接口,否则就不能实现动态代理,为了突破这个限制,于是才出现了 cglib 代理。
cglib 代理也叫子代理,能够从内存中构建出一个子类来扩展目标对象。是一个强大的高性能代码生成包,能在运行期间扩展 Java 类和实现 Java 接口,被许多 AOP 的框架广泛使用,为其提供方法的 interception(拦截)。
3.4.1 编写 cglib 代理
要实现 cglib 代理,通常要进行如下步骤:
- 首先引入
cglib-jar
文件,但 Spring 的核心包中已经包括该功能,所以直接引入spring-core
即可; - 然后就可以在内存中动态构建子类,需要注意的一点:代理的类不能是
final
,否则将会报错。此外,如果目标对象的方法为final/static
,那么则不会被拦截(即不会执行目标对象额外的业务方法)。
// 实现 MethodInterceptor 接口
public class ProxyFactory implements MethodInterceptor{
// 维护目标对象
private Object target;
public ProxyFactory(Object target){
this.target = target;
}
// 给目标对象创建代理对象
public Object getProxyInstance(){
//1. 工具类
Enhancer en = new Enhancer();
//2. 设置父类
en.setSuperclass(target.getClass());
//3. 设置回调函数
en.setCallback(this);
//4. 创建子类(代理对象)
return en.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("开始事务.....");
// 执行目标对象的方法
Object returnValue = method.invoke(target, args);
System.out.println("提交事务.....");
return returnValue;
}
}
- 测试
public class App {
public static void main(String[] args) {
UserDao userDao = new UserDao();
UserDao factory = (UserDao) new ProxyFactory(userDao).getProxyInstance();
factory.save();
}
}