一、代理模式
代理设计模式的原理:
使用一个代理将对象包装起来, 然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。(原始对象:明星,代理对象:经纪人。原始对象:耐克服装,代理对象:衣服加工厂)
现阶段代理模式分为:
二、静态代理
静态代理特征是代理类和目标对象的类都是在编译期间确定下来,不利于程序的扩展。同时,每一个代理类只能为一个接口服务,这样一来程序开发中必然产生过多的代理。最好可以通过一个代理类完成全部的代理功能。
这种代理方式需要代理对象和目标对象实现一样的接口。
优点:可以在不修改目标对象的前提下扩展目标对象的功能。
缺点:
- 冗余。由于代理对象要实现与目标对象一致的接口,会产生过多的代理类。
- 不易维护。一旦接口增加方法,目标对象与代理对象都要进行修改。
举例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| interface ClothFactory{ void produceCloth(); }
class NikeClothFactory implements ClothFactory{
@Override public void produceCloth() { System.out.println("Nike工厂生产一批运动服"); } }
class ProxyClothFactory implements ClothFactory{ private ClothFactory factory;
public ProxyClothFactory(ClothFactory factory){ this.factory = factory; }
@Override public void produceCloth() { System.out.println("代理工厂开始做一些准备工作");
factory.produceCloth();
System.out.println("代理工厂做一些后续收尾工作"); } }
public class StaticProxyTest { public static void main(String[] args) { NikeClothFactory nike = new NikeClothFactory(); ProxyClothFactory proxyClothFactory = new ProxyClothFactory(nike); proxyClothFactory.produceCloth(); } }
|
代理工厂开始做一些准备工作
Nike工厂生产一批运动服
代理工厂做一些后续收尾工作
三、动态代理
动态代理是指在程序运行期间根据实际调用的原始目标,动态创建目标类的代理对象。运行的时候调用了哪个对象,就为该对象创建一个代理对象,编译的时候是不知道的
动态代理使用场合:调试、远程方法调用
动态代理相比于静态代理的优点:抽象角色中(接口)声明的所有方法都被转移到调用处理器一个集中的方法中处理,这样,我们可以更加灵活和统一的处理众多的方法。
要想实现动态代理,需要解决的问题?
- 问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象。
- 问题二:当通过代理类的对象调用方法a时,如何动态的去调用被代理类中的同名方法a。
主要靠一个Java提供的方法:Proxy.newProxyInstance()
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
| interface Human{ String getBelief();
void eat(String food); }
class SuperMan implements Human {
@Override public String getBelief() { return "I believe I can fly!"; }
@Override public void eat(String food) { System.out.println("I like eat " + food); } }
class ProxyFactory { public static Object getProxyInstance(Object obj){ MyInvocationHandler handler = new MyInvocationHandler(obj); return Proxy.newProxyInstance( obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler ); } }
class MyInvocationHandler implements InvocationHandler{ private Object obj;
public MyInvocationHandler(Object obj){ this.obj = obj; }
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object returnValue = method.invoke(obj, args); return returnValue; } }
public class DynamicProxyTest { public static void main(String[] args) { SuperMan superman = new SuperMan(); Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superman); String belief = proxyInstance.getBelief(); System.out.println(belief); proxyInstance.eat("火锅"); } }
|
I believe I can fly!
I like eat 火锅
此处未演示代理的扩展功能,下面结合AOP一起演示
四、动态代理与AOP
使用 Proxy 生成一个动态代理时,往往并不会凭空产生一个动态代理,这样没有太大的意义。通常都是为指定的目标对象生成动态代理。
下面介绍AOP,AOP是Aspect Oriented Programming,即面向切面编程。
首先回顾一下OOP:Object Oriented Programming,OOP作为面向对象编程的模式,获得了巨大的成功,OOP的主要功能是数据封装、继承和多态。
而AOP是一种新的编程方式,它和OOP不同,OOP把系统看作多个对象的交互,AOP把系统分解为不同的关注点,或者称之为切面(Aspect)
以书的数据库管理业务BookService为例,它有几个业务方法:
- createBook:添加新的Book;
- updateBook:修改Book;
- deleteBook:删除Book。
由于涉及到数据库管理,除了业务逻辑,还需要安全检查、日志记录和事务处理,它的代码像这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| public class BookService { public void createBook(Book book) { securityCheck(); Transaction tx = startTransaction(); try { tx.commit(); } catch (RuntimeException e) { tx.rollback(); throw e; } log("created book: " + book); } public void updateBook(Book book) { securityCheck(); Transaction tx = startTransaction(); try { tx.commit(); } catch (RuntimeException e) { tx.rollback(); throw e; } log("updated book: " + book); } public void deleteBook(Book book) { ... } }
|
不难发现,对于安全检查、日志、事务等代码,它们会重复出现在每个业务方法中。使用OOP,我们很难将这些四处分散的代码模块化。
此时就可以使用AOP的思想,将安全检查视作一种切面(Aspect),日志、事务也视为切面,然后,以某种自动化的方式,把切面织入到核心逻辑中,用动态代理来实现。以AOP的视角来编写上述业务,可以依次实现:
- 核心逻辑,即BookService;
- 切面逻辑,即:权限检查的Aspect、日志的Aspect、事务的Aspect
AOP技术看上去比较神秘,但实际上,它本质就是一个动态代理,让我们把一些常用功能如权限检查、日志、事务等,从每个业务方法中剥离出来。
简言之,AOP可以在执行目标方法之前、之后插入一些通用处理

举个简单的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| interface TargetInterface { public void save(); }
class Target implements TargetInterface { public void save() { System.out.println("save running....."); } }
class Advice { public void before(){ System.out.println("前置增强...."); } public void afterReturning(){ System.out.println("后置增强...."); } }
public class AOPTest { public static void main(String[] args) { final Target target = new Target();
final Advice advice = new Advice();
TargetInterface proxy = (TargetInterface) Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { advice.before(); Object invoke = method.invoke(target, args); advice.afterReturning(); return invoke; } } ); proxy.save(); } }
|
前置增强….
save running…..
后置增强….
动态代理与AOP的思想能够为目标方法执行的前后带上通用方法。AOP对于解决特定问题,例如事务管理非常有用,这是因为分散在各处的事务代码几乎是完全相同的,并且它们需要的参数(JDBC的Connection)也是固定的。
另一些特定问题,如日志,就不那么容易实现,因为日志虽然简单,但打印日志的时候,经常需要捕获局部变量,如果使用AOP实现日志,我们只能输出固定格式的日志,因此,使用AOP时,必须适合特定的场景。
动态代理的代码或许不用自己能完全写出来,但要能看懂,后续很多框架的底层都是基于动态代理的