Java 代理模式
2021-02-08 本文已影响0人
索性流年
一句话总计代理模式
- 让别人帮你做事,便是代理模式
什么是代理模式?
*为其他对象生成一个代理,以控制这个对象的访问
为什么使用代理模式?
-
中介隔离:在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到承上启下的作用,其特征是代理类和委托类实现相同的接口。
-
开闭原则,增加功能代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后对返回结果的处理等。代理类本身并不真正实现服务,而是同过调用委托类的相关方法,来提供特定的服务。真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的委托类。
代理模式实现原理
- 代理模式主要包含三个角色,即抽象主题角色(Subject)、委托类角色(被代理角色,Proxied)以及代理类角色(Proxy)
生么是静态代理?
-
静态代理是由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。
-
总结: 自己手写代理类就是静态代理。
静态代理的缺点?
- 如若项目中被代理类过多,那么就会生成很多代理类,代码会非常冗余
动态代理
- 动态代理是在实现阶段不用关心代理类,而在运行阶段才指定哪一个对象,动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成。
代理模式两种创建方式
- 继承、实现
静态代理与动态代理区别
- 静态代理是程序员手动创建的代理,而动态代理则是由系统在运行时自动创建的代理。
JDK动态代理与CGLIB代理区别
- JDK实现动态代理,被代理类必须实现接口,底层是采用反射机制执行
- CGLIB实现动态代,采用继承代理实现类的方式,底层是采用ASM(字节码)生成子类
JDK动态代理实现原理
- 拼接java 源码
- 编译为 class 文件
- 使用类加载器将class 文件读取到程序中
- JDK的动态代理,走拦截回调,通过实现接口生成动态代理类,使用反射技术执行目标方法
CGLIB代理实现原理
- 通过ASM字节码技术生成class 文件
- 使用类加载器将class 文件读取到程序中
- 采用FastClass 机制调用目标方法
- CGLIB 动态代理采用集成的方式生成代理类,底层通过ASM 字节码技术实现
- FastClass 会将我们目标对象下所有的方法生成对应索引标记,直接根据索引标记调用目标方法
应用案例
- 小朋友要吃糖,他只能自己去找糖,撕开糖纸,在把糖吃进嘴里,这时候小朋友就会想,要是能有人帮我把糖拿过来,撕开糖纸,把糖塞进我嘴里多好。
这就是代理模式,让别人把你做事
实现案例
- 被代理对象
/**
* 被代理对象
*
* @author ext.liuyan10
* @date 2021/2/5 14:34
*/
public class User {
public void test() {
System.out.println("小朋友:糖真甜");
}
}
静态代理
- 代理对象 UserProxy
/**
* 代理对象
*
* @author ext.liuyan10
* @date 2021/2/5 13:38
*/
public class UserProxy implements UserApi {
private User user;
public UserProxy(User user) {
this.user = user;
}
@Override
public void test() {
System.out.println("将糖找来");
System.out.println("撕开糖纸,喂给小朋友");
user.test();
}
}
- 调用代理对象 TestApp
/**
* @author liunian
* @date 2021/2/5 13:42
*/
public class TestApp {
public static void main(String[] args) {
User user = new User();
UserProxy userProxy = new UserProxy(user);
userProxy.test();
}
}
动态代理
CGLIB动态代理
-
对被代理对象无要求,可以是接口,也可以是具体类
-
引入依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.10</version>
</dependency>
- 生成代理对象(可多次调用生成不同代理对象)
/**
* 生成动态代理对象
*
* @author liunian
* @date 2021/2/5 14:35
*/
public class UserCglibInvocation implements MethodInterceptor {
private Object target;
public UserCglibInvocation(Object target) {
this.target = target;
}
public Object getProxyInstance() {
//工具类
Enhancer en = new Enhancer();
//设置父类
en.setSuperclass(target.getClass());
//设置回调函数
en.setCallback(this);
//创建子类代理对象
return en.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("将糖找来");
System.out.println("撕开糖纸,喂给小朋友");
Object invoke = method.invoke(target);
return invoke;
}
}
- 调用方法
/**
* @author liunian
* @date 2021/2/5 13:42
*/
public class TestApp {
public static void main(String[] args) {
User user = new User();
UserCglibInvocation userInvocation = new UserCglibInvocation(user);
User proxyInstance = (User) userInvocation.getProxyInstance();
proxyInstance.test();
}
}
JDK动态代理
-
要求被代理对象是一个接口并且以被实现
-
被代理对象 UserApi
/**
* 被代理对象
*
* @author liunian
* @date 2021/2/5 13:39
*/
public interface UserApi {
void test();
}
- 被代理对象实现 UserImpl
/**
* 被代理对象实现
*
* @author liunian
* @date 2021/2/5 13:36
*/
public class UserImpl implements UserApi{
@Override
public void test() {
System.out.println("小朋友:糖真甜");
}
}
- 生成代理对象(可多次调用生成不同代理对象)
/**
* 生成代理对象
*
* @author ext.liuyan10
* @date 2021/2/5 14:03
*/
public class UserInvocation implements InvocationHandler {
Object target;
public UserInvocation(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("将糖找来");
System.out.println("撕开糖纸,喂给小朋友");
Object invoke = method.invoke(target);
return invoke;
}
}
- 调用方法
/**
* @author liunian
* @date 2021/2/5 13:42
*/
public class TestApp {
public static void main(String[] args) {
UserApi user = new UserImpl();
UserInvocation userInvocation = new UserInvocation(user);
UserApi userApi = (UserApi) Proxy.newProxyInstance(user.getClass().getClassLoader(), user.getClass().getInterfaces(), userInvocation);
userApi.test();
}
}
- 调用结果
Connected to the target VM, address: '127.0.0.1:51870', transport: 'socket'
将糖找来
撕开糖纸,喂给小朋友
小朋友:糖真甜
Disconnected from the target VM, address: '127.0.0.1:51870', transport: 'socket'
Process finished with exit code 0