1代理模式
代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式:即通过代理对象访问目标对象。这样做可以在目标对象实现的基础功能上,扩展目标对象的功能。
2静态代理
抽象角色:
package com.zs.proxy;
/**
* @author zhaoshuai06 <zhaoshuai06@kuaishou.com>
* Created on 2021-06-15
*/
public interface Subject {
public void buyMac();
}
真实角色:
package com.zs.proxy;
/**
* @author zhaoshuai06 <zhaoshuai06@kuaishou.com>
* Created on 2021-06-15
*/
public class RealSubject implements Subject{
@Override
public void buyMac() {
System.out.println("买一台Mac...");
}
}
代理角色:
package com.zs.proxy;
/**
* @author zhaoshuai06 <zhaoshuai06@kuaishou.com>
* Created on 2021-06-15
*/
public class ProxySubject implements Subject{
RealSubject realSubject;
@Override
public void buyMac() {
realSubject = new RealSubject();
realSubject.buyMac();
this.wrapMac();
}
public void wrapMac() {
System.out.println("用盒子包装好mac...");
}
}
客户类:
package com.zs.proxy;
/**
* @author zhaoshuai06 <zhaoshuai06@kuaishou.com>
* Created on 2021-06-15
*/
public class ProxyPattern {
public static void main(String[] args) {
Subject proxySubject = new ProxySubject();
proxySubject.buyMac();
}
}
静态代理的好处:可以使得我们的真实角色更加纯粹 ,不再去关注一些公共的事情,公共的业务由代理来完成 ;实现了业务的分工,公共业务发生扩展时变得更加集中和方便
缺点 :多了一些代理类,开发效率降低。我们想要静态代理的好处,又不想要静态代理的缺点,所以 , 就有了动态代理:
3动态代理
动态代理的代理类是动态生成的,静态代理的代理类是我们提前写好的
动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理:
基于接口的动态代理-——JDK动态代理
基于类的动态代理——cglib。
JDK的动态代理需要了解两个核心类 : InvocationHandler(调用处理程序) 和 Proxy
1为真实对象绑定代理对象:
// 创建一个代理对象
Proxy.newProxyInstance(obj.getClass().getClassLoader(), //指定使用哪个类加载器
obj.getClass().getInterfaces(), //代理对象实现了哪些接口
this //指定实现业务逻辑方法的代理类,此类需要实现 InvocationHandler 接口
);
返回指定接口的代理类的实例,该接口将方法调用分派给指定的调用处理程序。
2实现代理逻辑方法:
在 JDK 中,实现业务逻辑方法的代理类必须要实现 java.lang.reflect.InvocationHandler 接口,看到这个限定名我们也可以发现动态代理就是对反射机制的一种应用。在 InvocationHandler 接口中,只存在一个需要实现的方法:
public interface InvocationHandler {
public Object invoke(
Object proxy, // 代理对象,也就是使用 Proxy.newProxyInstance() 返回的对象
Method method, // 当前调度的方法,由反射获取,当使用代理对象调用接口中的方法时,会自动反射获取相应的真实对象实现的方法。
Object[] args // 调度方法的参数
) throws Throwable;
}
实例:
public class JDKProxy implements InvocationHandler {
Object target = null;
// 为真实对象绑定一个代理对象
public Object bind(Object target) {
this.target = target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
}
// 实现代理逻辑
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("执行真实对象方法前执行代理逻辑");
// 执行真实对象方法
Object obj = method.invoke(target, args);
System.out.println("执行真实对象方法后执行的逻辑");
return obj;
}
}
public class JDKProxyTest {
public static void main(String[] args) {
JDKProxy jdkProxy = new JDKProxy();
SayHello proxy = (SayHello) jdkProxy.bind(new SayHelloImpl());
proxy.sayHello("Eric");
}
}
4Cglib代理
静态代理和动态代理模式都是要求目标对象是实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用以目标对象子类的方式类实现代理,这种方法就叫做:Cglib代理。
Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展,JDK的动态代理有一个限制,代理对象必须实现一个或多个接口,如果想代理没有实现接口的类,就可以使用Cglib实现。
Cglib包的底层是通过使用一个小而快的字节码处理框架ASM来转换字节码并生成新的类。
Cglib子类代理实现方法:
1.需要引入cglib的jar文件,但是Spring的核心包中已经包括了Cglib功能,所以直接引入spring-core-3.2.5.jar即可.
2.引入功能包后,就可以在内存中动态构建子类
3.代理的类不能为final,否则报错
4.目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法。
目标对象类:UserDao.java
/**
* 目标对象,没有实现任何接口
*/
public class UserDao {
public void save() {
System.out.println("----已经保存数据!----");
}
}
Cglib代理工厂:ProxyFactory.java
/**
* Cglib子类代理工厂
* 对UserDao在内存中动态构建一个子类对象
*/
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 {
@Test
public void test(){
//目标对象
UserDao target = new UserDao();
//代理对象
UserDao proxy = (UserDao)new ProxyFactory(target).getProxyInstance();
//执行代理对象的方法
proxy.save();
}
}