代理模式
介绍
代理模式的定义:由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。
代理模式的主要优点有:
- 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
- 代理对象可以扩展目标对象的功能;
- 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度,增加了程序的可扩展性
其主要缺点是:
- 代理模式会造成系统设计中类的数量增加
- 在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
- 增加了系统的复杂度;
代理就是一种代替关系,用一个事物来代替另一个事物实现功能
代替者称为代理对象
被代替者称为目标对象
实现
静态代理
缺点:可能发生代理类实现太多接口,或者代理类中出现太多属性。
房东想要对外租房,但是自己要上班没有时间,所以找到一个租房中介,让中介代替自己对外租房。
//目标对象对应的接口
public interface TargetClass {
//目标对象要实现的功能
public void method();
}
//这是目标对象对应的实现类 --- 目标类房东类
public class TargetClassImpl implements TargetClass{
//目标对象要实现的方法 --- 卖房子功能
@Override
public void method() {
//扩展其他代码
System.out.println("房东卖房子。。。。。");
//扩展其他代码
}
}
//代理对象对应的类 同样实现了接口 -- 看做房屋中介类 对象就是中介人
public class ProxyClass implements TargetClass{
//代替的关系如何生成? --- 目标属性 --- 房东类的属性
private TargetClassImpl targetClass;
//给属性赋值
public void setTargetClass(TargetClassImpl targetClass) {
this.targetClass = targetClass;
}
//代理对象要实现的功能 -- 卖房子的功能
@Override
public void method() {
//中介只是中间人,真正卖房子的还是房东
//代理对象调用当前的method方法实现功能,但是真正实现的功能还是目标对象
//要扩展的代码
System.out.println("扩展功能。。。。。。。");
targetClass.method();
//要扩展的代码
}
}
public class ProxyTest {
public static void main(String[] args) {
//目标对象 -- 房东
TargetClassImpl tci = new TargetClassImpl();
//代理对象 -- 中介人
ProxyClass pc = new ProxyClass();
//房东找到中介人
pc.setTargetClass(tci);
pc.method();
}
}
动态代理
缺点:不同的目标方法要扩展不同的功能实现不方便的
1、动态代理有俩种实现:JDK动态代理和CGLIB动态代理
2、在spring框架中AOP思想的实现,底层依赖的就是俩种动态代理的实现方案
3、核心思想:代替和扩展
JDK动态代理
房东的业务扩展了,房东现在卖房子、卖烧饼和收废品。但是自己还是要上班。这回房东找了一个全能的中介公司,让中介公司代替自己卖房子、卖烧饼和收废品,然后中介公司派出员工进行卖房子、卖烧饼和收废品。
//目标对象 对应的接口 --- 房东类所要实现的接口
public interface TargetJDKClass {
public void method();
public void method1();
public void method2();
}
//目标对象 对应的类 -- 房东对象所属的类 生成房东对象
public class TargetJDKClassImpl implements TargetJDKClass{
@Override
public void method() {
System.out.println("卖房子。。。。。。。");
}
@Override
public void method1() {
System.out.println("卖烧饼.........");
}
@Override
public void method2() {
System.out.println("收废品........");
}
}
//工厂类,通过此类获取代理对象
public class ProxyJDKFactoryClass implements InvocationHandler {
//目标类的属性 -- 适合所有的目标类
private Object obj;
public void setObj(Object obj) {
this.obj = obj;
}
//被重写的方法中实现:原始功能得调用(目标对象调方法),功能扩展
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//method形成对应的实参是目标对象要调用的方法
//目标对象要调用的方法的实参在args中保存
//反射方式实现方法的调用
String name = method.getName();
if("method".equals(name)){
//扩展甲功能段
//扩展功能
System.out.println("扩展功能。。。。。。。甲");
}else if("method1".equals(name)){
//扩展乙功能段
//扩展功能
System.out.println("扩展功能。。。。。。。乙");
}else if("method2".equals(name)){
//扩展丙功能段
//扩展功能
System.out.println("扩展功能。。。。。。。丙");
}
Object o = method.invoke(obj,args);//等价于目标对象调用方法obj.method(实参)
return o;
}
//获取代理对象
/*
* newProxyInstance静态方法的内部通过传入的三个参数,
* 进行代理对象所属类的创建,
* 然后再根据类创建出代理对象,并最终通过方法返回出来
* */
public Object getProxy(){
//工具类加工具方法的调用,实现创建代理对象
//三个参数:第一个参数是类加载器(任意,比如获取目标类被加载,加载器)
//第二个参数是目标接口
//第三个参数是当前类对象,
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),this);
}
}
public class ProxyTest {
public static void main(String[] args) {
//目标对象 -- 房东
TargetJDKClassImpl tjci = new TargetJDKClassImpl();
//工厂对象 -- 中介公司
ProxyJDKFactoryClass pjfc = new ProxyJDKFactoryClass();
//工厂中把目标对象设置进去 -- 房东找到中介公司
pjfc.setObj(tjci);
//代理对象 -- 中介公司派出一个中介人
TargetJDKClass tjc = (TargetJDKClass) pjfc.getProxy();
//代理代替目标完成功能 -- 中介人代替房东卖房子
//tjc.method();
System.out.println("--------------");
tjc.method1();
System.out.println("--------------");
//tjc.method2();
}
}
CGLIB动态代理
//目标接口
public interface TargetCGLIBClass {
public void method();
}
//目标类
public class TargetCGLIBClassImpl implements TargetCGLIBClass{
@Override
public void method() {
System.out.println("房东卖房子。。。。。。。");
}
}
public class ProxyCGLIBFactoryClass implements MethodInterceptor {
//目标属性
private Object obj;
//set方法
public void setObj(Object obj) {
this.obj = obj;
}
//原功能调用,扩展功能
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("扩展的功能。。。。。。。。");
//原功能实现
Object o1 = method.invoke(obj,args);
return o1;
}
//获取代理对象
public Object getProxy(){
Enhancer enhancer = new Enhancer();
//动态生成代理对象所属的类
//超类 -- 目标类做超类
enhancer.setSuperclass(obj.getClass());
//当前类对象
enhancer.setCallback(this);
return enhancer.create();
}
}
public class ProxyTest {
public static void main(String[] args) {
//目标对象
TargetCGLIBClassImpl tci = new TargetCGLIBClassImpl();
//工厂
ProxyCGLIBFactoryClass factoryClass = new ProxyCGLIBFactoryClass();
factoryClass.setObj(tci);
//代理对象
TargetCGLIBClassImpl t2 = (TargetCGLIBClassImpl) factoryClass.getProxy();
//代理代替目标完成功能
t2.method();
}
}
jdk和CGLIB动态代理的区别
jdk代理必须实现接口而cglib代理不需要实现接口。
这篇博客讲的很详细很全面,想要了解详情的小伙伴可以去瞅瞅
https://www.cnblogs.com/sandaman2019/p/12636727.html
代理模式的应用场景
当无法或不想直接引用某个对象或访问某个对象存在困难时,可以通过代理对象来间接访问。使用代理模式主要有两个目的:一是保护目标对象,二是增强目标对象。
前面分析了代理模式的结构与特点,现在来分析以下的应用场景。
- 远程代理,这种方式通常是为了隐藏目标对象存在于不同地址空间的事实,方便客户端访问。例如,用户申请某些网盘空间时,会在用户的文件系统中建立一个虚拟的硬盘,用户访问虚拟硬盘时实际访问的是网盘空间。
- 虚拟代理,这种方式通常用于要创建的目标对象开销很大时。例如,下载一幅很大的图像需要很长时间,因某种计算比较复杂而短时间无法完成,这时可以先用小比例的虚拟代理替换真实的对象,消除用户对服务器慢的感觉。
- 安全代理,这种方式通常用于控制不同种类客户对真实对象的访问权限。
- 智能指引,主要用于调用目标对象时,代理附加一些额外的处理功能。例如,增加计算真实对象的引用次数的功能,这样当该对象没有被引用时,就可以自动释放它。
- 延迟加载,指为了提高系统的性能,延迟对目标的加载。例如,Hibernate 中就存在属性的延迟加载和关联表的延时加载。