代理模式?
代理模式的本质是⼀个中间件,主要⽬的是解耦合服务提供者和使⽤者。 使⽤者通过代理间接的访问服务提供者,便于后者的封装和控制。是⼀种 结构性模式。
来自于大话设计模式的解释:为其他对象提供一种代理以控制对这个对象的访问。
说道代理就会有动态代理和静态代理
静态代理和动态代理的区别
1. 静态代理
静态代理实现步骤:
- 定义一个接口及其实现类;
- 创建一个代理类同样实现这个接口
- 将目标对象注入进代理类,然后在代理类的对应方法调用目标类中的对应方法。这样的话,我们就可以通过代理类屏蔽对目标对象的访问,并且可以在目标方法执行前后做一些自己想做的事情。
代码如下:
1. 业务接口
/**
* 业务接口
* @author WHM
*
*/
public interface IUserDao {
void save();
}
2. 实现类,被代理对象
/**
* 被代理对象
* @author WHM
*
*/
public class UserDao implements IUserDao {
public void save() {
System.out.println("保存数据");
}
}
3. 代理类,这边的UserDao,可以当参数传入save方法中。
public class UserDaoProxy implements IUserDao{
private IUserDao target = new UserDao();
public void save() {
System.out.println("开始事务...");
target.save();//执行目标对象的方法
System.out.println("提交事务...");
}
}
4. 验证
/**
* 因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护.
如何解决静态代理中的缺点呢?答案是可以使用动态代理方式
* @author WHM
*
*/
public class Tmain {
public static void main(String[] args) {
//代理对象
UserDaoProxy proxy = new UserDaoProxy();
//执行的是代理的方法
proxy.save();
}
}
5 结果:
2. 动态代理
在 Java 动态代理机制中 InvocationHandler
接口和 Proxy
类是核心。
Proxy
类中使用频率最高的方法是:newProxyInstance()
,这个方法主要用来生成一个代理对象。
这个方法一共有 3 个参数:
- loader :类加载器,用于加载代理对象。
- interfaces : 被代理类实现的一些接口;
- h : 实现了
InvocationHandler
接口的对象;
要实现动态代理的话,还必须需要实现InvocationHandler
来自定义处理逻辑。 当我们的动态代理对象调用一个方法时,这个方法的调用就会被转发到实现InvocationHandler
接口类的 invoke
方法来调用。
invoke()
方法有下面三个参数:
- proxy :动态生成的代理类
- method : 与代理类对象调用的方法相对应
- args : 当前 method 方法的参数
也就是说:你通过Proxy
类的 newProxyInstance()
创建的代理对象在调用方法的时候,实际会调用到实现InvocationHandler
接口的类的 invoke()
方法。 你可以在 invoke()
方法中自定义处理逻辑,比如在方法执行前后做什么事情。
2.1 jdk代码如下:
1. 创建代理对象 部分代码引用之前静态代理的
/*
* 创建动态代理对象
* 动态代理不需要实现接口,但是需要指定接口类型
*/
public class ProxyFactory {
//维护一个目标对象
private Object target;
public ProxyFactory(Object target){
this.target=target;
}
//给目标对象生成代理对象
public Object getProxyInstance(){
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开始事务2");
//执行目标对象方法
Object returnValue = method.invoke(target, args);
System.out.println("提交事务2");
return returnValue;
}
}
);
}
}
2. 验证类
public class Tmain {
public static void main(String[] args) {
// 目标对象
IUserDao target = new UserDao();
// 原始的类型
System.out.println(target.getClass());
// 给目标对象,创建代理对象
IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();
// 内存中动态生成的代理对象
System.out.println(proxy.getClass());
// 执行方法 【代理对象】
proxy.save();
}
}
3. 结果
2 Cglib代理
JDK 动态代理有一个最致命的问题是其只能代理实现了接口的类。
例如 Spring 中的 AOP 模块中:如果目标对象实现了接口,则默认采用 JDK 动态代理,否则采用 CGLIB 动态代理。
在 CGLIB 动态代理机制中 MethodInterceptor
接口和 Enhancer
类是核心。
你需要自定义 MethodInterceptor
并重写 intercept
方法,intercept
用于拦截增强被代理类的方法。
主要要引入包:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
</dependencies>
- obj : 被代理的对象(需要增强的对象)
- method : 被拦截的方法(需要增强的方法)
- args : 方法入参
- proxy : 用于调用原始方法
你可以通过 Enhancer
类来动态获取被代理类,当代理类调用方法的时候,实际调用的是 MethodInterceptor
中的 intercept
方法。
CGLIB 动态代理类使用步骤
- 定义一个类;
- 自定义
MethodInterceptor
并重写intercept
方法,intercept
用于拦截增强被代理类的方法,和 JDK 动态代理中的invoke
方法类似; - 通过
Enhancer
类的create()
创建代理类;
2.2 cglib代码如下:
1. 定义对象 部分代码还是引用之前静态代理的
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();
}
// 干预
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("开始事务3...");
// 执行目标对象的方法
Object returnValue = method.invoke(target, args);
System.out.println("提交事务3...");
return returnValue;
}
}
2. 验证类
/**
* 1.需要引入cglib的jar文件,但是Spring的核心包中已经包括了Cglib功能
* 2.代理的类不能为final,否则报错
* 3.目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.
* @author WHM
*/
public class Tmain {
public static void main(String[] args) {
// 目标对象
UserDao target = new UserDao();
// 代理对象
UserDao proxy = (UserDao) new ProxyFactory(target).getProxyInstance();
// 执行代理对象的方法
proxy.save();
}
}
3. 结果
总结:
1. 概念
1.静态代理:需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类,被代理类固定。
2.动态代理:代理对象,不需要实现接口,代理对象由JDK代理生成
3.cglib代理:并没有实现任何的接口,这个时候就可以使用以目标对象子类
2 对别
2.1JDK 动态代理和 CGLIB 动态代理对比
- JDK 动态代理只能代理实现了接口的类或者直接代理接口,而 CGLIB 可以代理未实现任何接口的类。 另外, CGLIB 动态代理是通过生成一个被代理类的子类来拦截被代理类的方法调用,因此不能代理声明为 final 类型的类和方法。
- 就二者的效率来说,大部分情况都是 JDK 动态代理更优秀,随着 JDK 版本的升级,这个优势更加明显。
2.2 静态代理和动态代理的对比
- 灵活性 :动态代理更加灵活,不需要必须实现接口,可以直接代理实现类,并且可以不需要针对每个目标类都创建一个代理类。另外,静态代理中,接口一旦新增加方法,目标对象和代理对象都要进行修改,这是非常麻烦的!
- JVM 层面 :静态代理在编译时就将接口、实现类、代理类这些都变成了一个个实际的 class 文件。而动态代理是在运行时动态生成类字节码,并加载到 JVM 中的。
3. 应用
在Spring的AOP编程中: 如果加入容器的目标对象有实现接口,用JDK代理 如果目标对象没有实现接口,用Cglib代理。
4. 优缺点
优点 1.增强额外的功能操作,即扩展目标对象的功能,符合开闭原则. 2.代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度
缺点 1.在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢。 2.增加了系统的复杂度。