代理模式
代理模式是一种结构型设计模式,它通过创建一个代理对象来控制对真实对象的访问。这种模式可以用于提供额外的功能操作,或者扩展目标对象的功能。
在代理模式中,代理对象与真实对象实现相同的接口,以便在任何地方都可以使用相同的接口来调用真实对象的方法。这样做的好处是可以在不改变原始代码的情况下,增加或修改代码的行为。
根据创建代理对象的方式和时机,代理模式可以被分为静态代理、动态代理等类型。其中,静态代理是由程序员在编译时期就定义好的代理类,而动态代理则是在程序运行时期通过Java反射机制动态生成的。
在实际生活中,有许多应用了代理模式的场景,例如租房、卖房、家政等业务,通常由中介机构作为代理来进行操作。
静态代理
静态代理在编译期间就已经确定代理类的代码。具体来说,要实现静态代理需要手动编写代理类的代码,因此这种方式的灵活性相对较差,但由于代理类是直接编写的,所以其运行效率较高。
首先定义购房者的行为
java
/**
* 购房者
*
* @author LionLi
*/
public interface Homebuyer {
/**
* 需求
*/
String need();
/**
* 购买
*/
void buy();
}
定义真实购房者
java
/**
* 购房者 张三
*
* @author LionLi
*/
public class Zhangsan implements Homebuyer {
/**
* 需求
*/
@Override
public String need() {
String need = "100平以上三室两厅两卫";
System.out.println("张三: " + need);
return need;
}
/**
* 购买
*/
@Override
public void buy() {
System.out.println("张三: 我已付款");
}
}
java
/**
* 购房者 王五
*
* @author LionLi
*/
public class Wangwu implements Homebuyer {
/**
* 需求
*/
@Override
public String need() {
String need = "70平左右两室一厅";
System.out.println("王五: " + need);
return need;
}
/**
* 购买
*/
@Override
public void buy() {
System.out.println("张三: 我已付款");
}
}
定义房产中介
java
/**
* 房产中介代理人
*
* @author LionLi
*/
public class HouseAgentProxy implements Homebuyer {
private Homebuyer homebuyer;
public HouseAgentProxy(Homebuyer homebuyer) {
this.homebuyer = homebuyer;
}
@Override
public String need() {
System.out.println("中介: 你对房子有什么需求 放心交给我");
String need = homebuyer.need();
System.out.println("中介: 寻找房源中........");
System.out.println("中介: 寻找房源中........");
System.out.println("中介: 寻找房源中........");
String str = "中介: 为您找到" + need + "的房子";
System.out.println(str);
return str;
}
@Override
public void buy() {
System.out.println("中介: 请支付购买房子");
homebuyer.buy();
System.out.println("中介: 合同生效中.....");
System.out.println("中介: 房证办理中.....");
System.out.println("中介: 恭喜您 这套房子属于您了");
}
}
测试
java
/**
* @author LionLi
*/
public class Test {
public static void main(String[] args) {
Homebuyer zhangsan = new Zhangsan();
HouseAgentProxy agent1 = new HouseAgentProxy(zhangsan);
agent1.need();
agent1.buy();
System.out.println("-----------------------------");
Homebuyer wangwu = new Wangwu();
HouseAgentProxy agent2 = new HouseAgentProxy(wangwu);
agent2.need();
agent2.buy();
}
}
两位购房者分别根据需求在中介的带领下买到了房子 真是可喜可贺啊
动态代理
动态代理允许在运行时动态地创建代理对象。代理对象可以在调用实际对象的方法前后执行一些额外的操作,比如日志记录、权限检查等。
动态代理的实现方式有两种:基于接口和基于继承。基于接口的方式是最常用的,它使用Java的反射机制来实现代理对象。基于继承的方式则需要创建一个实现了目标类接口的子类,并重写其中的方法。
优点: 可以降低系统的耦合度,提高代码的可维护性和可扩展性。缺点: 需要使用反射机制,性能比静态代理略低。
首先定义购房者的行为与实际购房者
使用上方代码不变
定义房产中介
java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 动态房产中介代理人
*
* @author LionLi
*/
public class DynamicHouseAgentProxy implements InvocationHandler {
private Homebuyer homebuyer;
public Homebuyer getInstance(Homebuyer homebuyer) {
this.homebuyer = homebuyer;
return (Homebuyer) Proxy.newProxyInstance(homebuyer.getClass().getClassLoader(), homebuyer.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("need")) {
System.out.println("中介: 你对房子有什么需求 放心交给我");
String need = (String) method.invoke(homebuyer, args);
System.out.println("中介: 寻找房源中........");
System.out.println("中介: 寻找房源中........");
System.out.println("中介: 寻找房源中........");
String str = "中介: 为您找到" + need + "的房子";
System.out.println(str);
return str;
} else if (method.getName().equals("buy")) {
System.out.println("中介: 请支付购买房子");
Object invoke = method.invoke(homebuyer, args);
System.out.println("中介: 合同生效中.....");
System.out.println("中介: 房证办理中.....");
System.out.println("中介: 恭喜您 这套房子属于您了");
return invoke;
}
return null;
}
}
测试
java
/**
* @author LionLi
*/
public class Test {
public static void main(String[] args) {
DynamicHouseAgentProxy agent = new DynamicHouseAgentProxy();
Homebuyer zhangsan = new Zhangsan();
Homebuyer proxy1 = agent.getInstance(zhangsan);
proxy1.need();
proxy1.buy();
System.out.println("-----------------------------");
Homebuyer wangwu = new Wangwu();
Homebuyer proxy2 = agent.getInstance(wangwu);
proxy2.need();
proxy2.buy();
}
}
结果不变 两位购房者成功买到房子
Cglib动态代理
其他代码不变只变更中介类
java
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* 动态房产中介代理人
*
* 这里由于cglib已经不支持jdk17了 所以我们使用spring内部自带的cglib工具
*
* @author LionLi
*/
public class DynamicHouseAgentProxy implements MethodInterceptor {
public Homebuyer getInstance(Homebuyer homebuyer) {
Enhancer enhancer = new Enhancer();
// 设置继承父类
enhancer.setSuperclass(homebuyer.getClass());
// 设置回调
enhancer.setCallback(this);
return (Homebuyer) enhancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
if (method.getName().equals("need")) {
System.out.println("中介: 你对房子有什么需求 放心交给我");
String need = (String) proxy.invokeSuper(obj, args);
System.out.println("中介: 寻找房源中........");
System.out.println("中介: 寻找房源中........");
System.out.println("中介: 寻找房源中........");
String str = "中介: 为您找到" + need + "的房子";
System.out.println(str);
return str;
} else if (method.getName().equals("buy")) {
System.out.println("中介: 请支付购买房子");
Object invoke = proxy.invokeSuper(obj, args);
System.out.println("中介: 合同生效中.....");
System.out.println("中介: 房证办理中.....");
System.out.println("中介: 恭喜您 这套房子属于您了");
return invoke;
}
return null;
}
}
Spring中代理模式的应用
要说起Spring中的代理模式 要首当其冲的肯定是AOP了 接下来我们来看看在AOP中是如何使用代理模式的
这里涉及到一个注解 学过AOP的都知道 如果需要开启AOP那么需要增加这个注解
通过搜索找到 AspectJAutoProxyRegistrar
类, 该方法的作用就是往Spring容器中添加一个 BeanDefinition
类型为 AnnotationAwareAspectJAutoProxyCreator
用于创建代理对象
接下来我们进入 AnnotationAwareAspectJAutoProxyCreator
看看是如何创建代理的
由于 AnnotationAwareAspectJAutoProxyCreator
中并没有创建代理的方法 那么我们只能向上去父类里找, 通过不断的探索在 AnnotationAwareAspectJAutoProxyCreator
-> AspectJAwareAdvisorAutoProxyCreator
-> AbstractAdvisorAutoProxyCreator
-> AbstractAutoProxyCreator
抽象类 AbstractAutoProxyCreator
中找到了 createProxy
方法
此方法是bean初始化的前置处理器 postProcessBeforeInstantiation
中使用的, 也就是说bean在初始化之前, 代理对象就已经创建好了
进入 createProxy
方法
进入 buildProxy
方法
这里我们可以看出 实际的代理对象是通过 ProxyFactory
工厂的 getProxyClass
与 getProxy
两个方法创建的
我们来看一下这两个方法具体实现
通过 getProxyClass
方法一直往下点 找到最终创建方法 DefaultAopProxyFactory#createAopProxy
为止
最后我们来分析最终是如何创建代理对象的