背景:小明是一个客户,他让小红帮忙买香水然后拿来送人,这里小红就是一个代理对象,而香水提供商是一个真实对象,他可以售卖香水,小明通过代理商小红买到了香水,这就是一个代购的例子
这个故事就是最典型的一个代理模式,代购从供应商那里买货物后返回给购买者(调用者),也就是需要代理的小明
代理可以分为静态代理和动态代理
静态代理
-优点:代码结构简单,容易实现
-缺点:无法适配所有的代理场景,如果有新的代理场景,需要往代理类添加新的功能,不符合开闭原则(扩展是开放的,修改是封闭的),例如现在小红只是代理香水,如果想要他代理红酒,那那么必须修改小红内部的逻辑,这样会使得小红的代码越来越臃肿
动态代理
-优点:能够动态适配所有代理场景,扩展性好,符合开闭原则
-缺点:动态代理需要利用反射或者动态生成字节码文件,性能会比静态差一点,但是可以因为他的优点忽略他的缺点
如果想要小红代理红酒,我们无需小红内部的逻辑,因为是扩展代理红酒,因此我们只需要实例化新的类,通过一些转换就能小红既能代理红酒又能代理香水了
示例
售卖香水功能
public interface SellPerfume {
void sellPerfume(double price);
}
香奈儿提供商,他也可以售卖香水,实现售卖接口
public class ChanelFactory implements SellPerfume {
@Override
public void sellPerfume(double price) {
System.out.println("售卖香奈儿香水,价格是:" + price + "ز ");
}
}
代理商小红,他也可以售卖,他功能必须跟被代理对象香奈儿一样,香奈儿能做的事情他也能做,不然怎么代理?因此他也有售卖功能,实现售卖接口;他又是特地代理香奈儿的,因此他有香奈儿的对象,然后去调用香奈儿的售卖接口
public class XiaoHongSellProxy implements SellPerfume {
private SellPerfume sellPerfumeFactory;
public XiaoHongSellProxy(SellPerfume sellPerfumeFactory) {
this.sellPerfumeFactory = sellPerfumeFactory;
}
@Override
public void sellPerfume(double price) {
// 售卖前做一些事情,例如涨价或者打折
doSomethingBeforeSell();
sellPerfumeFactory.sellPerfume(price);
// 售卖之后做一些事情,例如送礼品
doSomethingAfterSell();
}
private void doSomethingBeforeSell() {
System.out.println(售卖前...");
}
private void doSomethingAfterSell() {
System.out.println("售卖后...");
}
}
买家出现:小明,他想通过小红这个代理商去买香奈儿香水,且要买价值2000的香水
public class XiaoMing {
public static void main(String[] args) {
// 想要买的香水,被代理对象
ChanelFactory factory = new ChanelFactory();
// 代购、代理对象小红出现,且必须告知小红说要买什么类型的香水
XiaoHongSellProxy proxy = new XiaoHongSellProxy(factory);
// 被代理对象的方法参数
proxy.sellPerfume(1999.99);
}
}
以上几个类的关系结构
实现代理模式,需要经历以下几个步骤
-定义真实对象跟代理对象的公共接口,只有真实对象售卖香水,代理对象也售卖香水,才能通过代理对象去真实对象那里买香水
-代理对象内部要有真实对象的引用,代理对象要买香水也得真实对象里有人接单才对
-访问者不能直接访问目标对象,只能通过代理对象访问真实目标对象
代理对象并没有真正提供服务,它只是作为一个中间人,真正提供服务的还是目标对象,而代理对象就是在目标对象在提供服务之前和服务之后执行一些额外的逻辑
代理模式的定义:给目标对象提供一个代理对象,代理对象包含该目标对象(访问者创建的好,代理对象自己创建也好),并控制对该目标对象的访问
代理模式的目的
-通过代理对象的隔离,可以在对目标对象访问前后增加一些额外的业务逻辑,事项功能增强
-通过代理对象访问目标对象,可以访问系统大量的直接对真实对象不正确地访问
静态代理跟动态代理的共同点
-都能实现代理模式
-代理对象和目标对象都要共同实现一个公共接口
不同点
-动态代理的代理对象是在运行时动态生成的,它是没有Java源文件的,直接生成字节码文件实例化代理对象;而静态代理对象在编译时就写好Java文件了,直接new一个对象即可
-动态代理比静态代理更加稳健,可维护性和可扩展性更好
假设现在小明还想让小红帮忙买红酒,那这时候怎么做?
-创建售卖红酒的接口
-售卖红酒的提供商(真实对象)、小红(代理对象)都需要实现这个接口
这里如果使用静态代理即会违背开闭原则,面对新的需求需要修改代理类,增加新的接口和方法,导致代理类越来越大,越来越难维护
为了满足开闭原则,引入了动态代理
动态代理解决的是面对新的需求时,不需要修改代理对象的代码,只需要新增接口和真实对象,在客户端调用即可完成新的代理
JDK动态代理
JDK动态代理设计到两个核心类:Proxy、InvocationHandler(调用处理程序)
从理解上就是动态代理是通过一个代理工厂生成各类各样的代理对象,代理对象本身不存在java源文件,而静态代理是通过生成一个代理对象
现在的两个关注点
-如何实现一个代理工厂
-如何通过代理工厂生成代理对象
首先,代理工厂需要实现InvocationHandler接口,并实现invoke()方法
public class SellProxyFactory implements InvocationHandler {
private Object realObject;
public SellProxyFactory(Object realObject) {
this.realObject = realObject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws
Throwable {
doSomethingBefore();
Object obj = method.invoke(realObject, args);
doSomethingAfter();
return obj;
}
private void doSomethingAfter() {
System.out.println("执行代理之后的额外操作...");
}
private void doSomethingBefore() {
System.out.println("执行代理之前的额外操作...");
}
}
invoke()方法是一个代理方法,即客户端请求执行的就是这个方法,这个方法执行完就结束了。这个方法有三个参数,第一个是代理对象,第二个目标方法,第三个是目标方法参数
那么代理工厂是如何动态生成代理对象的?
生成代理对象需要一个叫做Proxy的类,这个类可以帮我们生成任意一个代理对象,里面提供了一个叫做newProxyInstance的静态方法
Proxy.newProxyInstance(ClassLoader loader, Class>[] interfaces,InvocationHandler h);
实例化代理对象时需要传进三个参数
-ClassLoader loader,加载动态代理的类加载器
- Class>[] interfaces,代理类实现的接口,是一个数组,可以传进多个
-InvocationHandler h,指定代理类的调用处理程序,即调用接口中的方法时,会找到代理工厂h,然后执行invoke()方法
调用逻辑
public class XiaoMing {
public static void main(String[] args) {
// 真实对象
ChanelFactory chanelFactory = new ChanelFactory();
// 代理工厂
SellProxyFactory sellProxyFactory = new SellProxyFactory(chanelFactory);
// 代理对象
SellPerfume sellPerfume = (SellPerfume)
// 调用代理方法
Proxy.newProxyInstance(chanelFactory.getClass().getClassLoader(),
chanelFactory.getClass().getInterfaces(),
sellProxyFactory);
sellPerfume.sellPerfume(1999.99);
}
}
其实无论是静态代理还是动态代理,大体上都具有以下步骤
-调用者生成一个真实对象
-调用者将真实对象传给代理类、代理工厂,生成代理对象
-代理对象调用相应的代理方法
特别注意的是,相比静态代理的前置增强和后置增强,动态代理的增强少了小红两字,即他的增强是通用的
实现一个买红酒的示例
需要实现两个类:红酒提供商类和售卖红酒接口
// 售卖红酒接口
public interface SellWine {
void sellWine(double price);
}
// 红酒供应商
public class RedWineFactory implements SellWine {
@Override
public void sellWine(double price) {
System.out.println("红酒价格:" + price +);
}
}
小明的请求过程
public class XiaoMing {
public static void main(String[] args) {
// 实例化一个红酒销售商(目标对象)
RedWineFactory redWineFactory = new RedWineFactory();
// 实例化代理工厂,传进红酒销售商引用来控制对其的访问
SellProxyFactory sellProxyFactory = new SellProxyFactory(redWineFactory);
// 实例化代理对象,该对象可以代理售卖红酒
SellWine sellWineProxy = (SellWine)
Proxy.newProxyInstance(redWineFactory.getClass().getClassLoader(),
redWineFactory.getClass().getInterfaces(),
sellProxyFactory);
// 代理售卖红酒
sellWineProxy.sellWine(1999.99);
}
}
通过动态代理新增一个代理功能时,需要两个步骤:
-创建新的红酒提供商SellWineFactory和售卖红酒接口SellWine
-在客户端实例化一个代理对象,然后向该代理对象购买红酒
动态代理完美符合开闭原则,面向扩展开放,面向修改关闭
JDK动态代理的总结:
- 使用方法
1)代理工厂需要实现InvocationHandler接口,调用代理方法时会转向执行invoke()方法
2)生成代理对象需要使用Proxy对象中的newProxyInstance()方法,返回对象可强转成传入的其中一个接口,然后接口方法即可实现代理
-特点
目标对象强制需要实现一个接口,否则无法使用JDK动态代理
CGLIB代理
CGLIB代理不是JDK自带的动态代理,需要引入第三方jar包,是一个字节码生成类库,能够在运行时动态生成代理类
Cglib代理跟jdk代理最大的区别在于cglib可以代理没有实现接口的Java类
使用方法
1)导入依赖
2)cglib代理中有两个核心类,MethodInterceptor接口和Enhancer类,前者是实现一个代理工厂的根接口,后者是创建动态代理对象的类
代理工厂SellProxyFactory
public class SellProxyFactory implements MethodInterceptor {
// 关联真实对象
private Object realObject;
// 从代理工厂中获取一个代理对象实例
public Object getProxyInstance(Object realObject) {
this.realObject = realObject;
Enhancer enhancer = new Enhancer();
// 设置需要增强类的类加载器
enhancer.setClassLoader(realObject.getClass().getClassLoader());
// 设置被代理类,真实对象,这里返回的enhancer是真实对象的子类
enhancer.setSuperclass(realObject.getClass());
// 设置方法拦截器,代理工厂
enhancer.setCallback(this);
// 创建代理类
return enhancer.create();
}
@Override
// o:被代理对象,method:被拦截的方法,objects:被拦截方法的所有入参值
// methodProxy:方法代理,用于调用原始的方法
public Object intercept(Object o, Method method, Object[] objects,
MethodProxy methodProxy) throws Throwable {
doSomethingBefore();
Object object = methodProxy.invokeSuper(o, objects);
doSomethingAfter();
return object;
}
private void doSomethingBefore() {
}
private void doSomethingAfter() {
}
}
Cglib动态代理依然是先生成一个代理对象,然后再通过代理对象调用目标方法
public class XiaoMing {
public static void main(String[] args) {
SellProxyFactory sellProxyFactory = new SellProxyFactory();
// ឴获取一个代理实例
SellPerfumeFactory proxyInstance =
(SellPerfumeFactory) sellProxyFactory.getProxyInstance(new
SellPerfumeFactory());
// 调用方法
proxyInstance.sellPerfume(1999.99);
}
}
Cglib代理的使用方法
-代理工厂需要实现MethodInterceptor接口,代理工厂暴露一个getInstance方法,用于从代理工厂中获取一个代理对象实例
JDKd动态代理和CGLIB动态代理的对比
相似点:
-JDK动态代理代理工厂实现的是InvocationHandler,而CGLIB代理工厂实现的是MethodInterceptor
-JDK动态代理通过构造Proxy代理对象给client服务,CGLIB通过构造Enhancer代理对象给client服务
不同点
-CGLIB可以代理大部分类,而JDK只能代理实现接口的类
-CGLIB采用动态创建被代理类的子类实现方法拦截,子类内部重写被拦截的方法,所以CGIB不能代理被final修饰的类和方法
动态代理的实际应用
传统的oop编程只符合从上往下的编码关系,即只满足从上往下执行方法。而spring aop允许我们将重复的代码逻辑抽取出来 形成一个单独的覆盖层,可以用来记录日志或者事务的开启(@Transactional)



