从装饰模式到aop
来源:互联网 发布:网易云课堂软件 编辑:程序博客网 时间:2024/05/01 06:55
在上次的spring的源码中,我们在ioc中气势随处都可以见到拦截机制的实现,很多时候,针对于系统本身固有的拦截点,是采用策略模式留出了拦截的空方法实现,这个一个框架在设计的时候必然应该考虑的问题;然而针对一些系统无法预知的拦截,比如bean类的某方法的拦截,那么,我们只能采用动态代理的方式;
最初我们的拦截机制来自于大名鼎鼎的decorator模式,我们看如下代码:
hello everyone
i am corey
可见在上述效果中,我们动态的增加了hello everyone;
但是,我们很快发现装饰模式一个不好的地方,比如现在Person有很多方法,而我们的Decorator只要改变其中的say,那么我们不得不把所有的方法重新实现一遍,其实这也是组合复用相对于继承复用的一个不足之处,组合复用事实上是采用委托的机制来实现,我们不得不把每一个功能都显示的进行委托,然而,继承复用有是一种编译器的复用,造成了代码的强耦合,比如现在IPerson的具体实现类有Student和Tearcher,那么我们如果我们要对两者都进行代理,我们不得不为两个类都派生出一个子类,然后作出装饰行为;
如:
而Tearcher同理,而不是运行期间在DecoratorPerson的构造中传入不同类型的IPerson;
针对于这种缺点,我们不得不感Proxy给我们带来的惊喜;我们来看:
在Invocationhandler中,我们的invoke方法把这个不得不显示委托的缺点解决啦,我们可以在这里对方法进行统一的装饰,很快,我们又发现了不方便,如:
delegation.getClass().getInterfaces()
这说明一个问题,我们的代理类不得不实现一个接口,虽然面向接口编程是oo所呼吁的,但是却并不能说我们就一定时时刻刻的遵守,对于没有实现接口的代理类,那是不是我们没有办法进行动态代理啦呢????
答案是no,我们必须再次感谢cglib给我们带来的这一切:
一下部分引子江南白衣blog:
cglib的Enhancer说起来神奇,用起来一页纸不到就讲完了。
它的原理就是用Enhancer生成一个原有类的子类,并且设置好callback到proxy, 则原有类的每个方法调用都会转为调用实现了MethodInterceptor接口的proxy的intercept() 函数:
在intercept()函数里,你可以在执行Objectresult=proxy.invokeSuper(o,args);来执行原有函数,在执行前后加入自己的东西,改变它的参数值,也可以瞒天过海,完全干别的。说白了,就是AOP中的around advice。
public class LogDAOProxy implements MethodInterceptor
{
private Logger log=Logger.getLogger(LogDAOProxy.class);
private Enhancer enhancer=new Enhancer();
//返回DAO的子类
public Object getDAO(Class clz)
{
enhancer.setSuperclass(clz);
enhancer.setCallback(this);
return enhancer.create();
}
//默认的拦截方法
public Object intercept(Object o,Method method,Object[] args,MethodProxy proxy) throws Throwable
{
log.info("调用日志方法"+method.getName());
Object result=proxy.invokeSuper(o,args);
return result;
}
}
应用的代码:
2.而在Spring的管理下应该略加修改的高级Decorator
上面的例子用return enhancer.create();创建子类实例,但在Spring管理下,一些Bean的实例必须由Spring来创建和管理,而不由enhancer来创建的。所以我对上述用法略加修改,使它真正当一个Proxy的角色,请对比黑体字的部分
可见,原来模式里在getDao()时由enhancer创建dao,而 调用intercept时则将enhancer创建的dao以Object o参数传回。
而新模式里,dao在getDao()时从外面传入,enhancer.create()返回的是一个proxy. 而调用intercept时,实际会用之前传入的dao进行操作,而忽略Object o参数传入的proxy.
有点遗憾, intercept函数里MethodProxy的Signature是固定的 , 即客户如果调用foo(String),你不可以用proxy.invoke偷换成foo(String,String);
最初我们的拦截机制来自于大名鼎鼎的decorator模式,我们看如下代码:
- package org.corey.demo;
- public interface IPerson {
- public void say();
- }
- package org.corey.demo;
- public class Person implements IPerson {
- public void say() {
- System.out.println("i am corey");
- }
- }
- package org.corey.demo;
- public class DecoratorPerson implements IPerson {
- private IPerson concertPerson;
- public DecoratorPerson(IPerson concertPerson) {
super();
this.concertPerson = concertPerson;
} - public void say() {
- System.out.println("hello everyone");
- this.concertPerson.say();
- }
- }
- package org.corey.demo;
- public class Demo {
- /**
- * @param args
- */
- public static void main(String[] args) {
- IPerson person=new Person();
- IPerson decorator=new DecoratorPerson(person);
- decorator.say();
- }
- }
i am corey
可见在上述效果中,我们动态的增加了hello everyone;
但是,我们很快发现装饰模式一个不好的地方,比如现在Person有很多方法,而我们的Decorator只要改变其中的say,那么我们不得不把所有的方法重新实现一遍,其实这也是组合复用相对于继承复用的一个不足之处,组合复用事实上是采用委托的机制来实现,我们不得不把每一个功能都显示的进行委托,然而,继承复用有是一种编译器的复用,造成了代码的强耦合,比如现在IPerson的具体实现类有Student和Tearcher,那么我们如果我们要对两者都进行代理,我们不得不为两个类都派生出一个子类,然后作出装饰行为;
如:
- Public class DecoratorStudent extends Student{
- public void say(){
- System.out.println("hello everyone");
- super.say();
- }
- }
针对于这种缺点,我们不得不感Proxy给我们带来的惊喜;我们来看:
- package org.corey.demo;
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
- public class CoreyInvocation implements InvocationHandler {
- Object delegation;
- public Object proxy(Object delegation) {
- this.delegation = delegation;
- return Proxy.newProxyInstance(delegation.getClass().getClassLoader(),
- delegation.getClass().getInterfaces(), this);
- };
- public Object invoke(Object proxy, Method method, Object[] arg2)
- throws Throwable {
- System.out.println("hello everyone");
- return method.invoke(delegation, arg2);
- }
- }
- package org.corey.demo;
- public class Demo {
- /**
- * @param args
- */
- public static void main(String[] args) {
- IPerson person=(IPerson)(new CoreyInvocation().proxy(new Person()));
- person.say();
- }
- }
在Invocationhandler中,我们的invoke方法把这个不得不显示委托的缺点解决啦,我们可以在这里对方法进行统一的装饰,很快,我们又发现了不方便,如:
delegation.getClass().getInterfaces()
这说明一个问题,我们的代理类不得不实现一个接口,虽然面向接口编程是oo所呼吁的,但是却并不能说我们就一定时时刻刻的遵守,对于没有实现接口的代理类,那是不是我们没有办法进行动态代理啦呢????
答案是no,我们必须再次感谢cglib给我们带来的这一切:
一下部分引子江南白衣blog:
cglib的Enhancer说起来神奇,用起来一页纸不到就讲完了。
它的原理就是用Enhancer生成一个原有类的子类,并且设置好callback到proxy, 则原有类的每个方法调用都会转为调用实现了MethodInterceptor接口的proxy的intercept() 函数:
public Object intercept(Object o,Method method,Object[] args,MethodProxy proxy)
在intercept()函数里,你可以在执行Objectresult=proxy.invokeSuper(o,args);来执行原有函数,在执行前后加入自己的东西,改变它的参数值,也可以瞒天过海,完全干别的。说白了,就是AOP中的around advice。
AOP没有出现以前,该领域经典的设计模式是Decorator,像Java IO Stream的设计就是如此.不过,如果为每个DAO, 每个方法的写Decorator函数会写死人的,所以用上cglib的好处是一次过拦截所有方法。
另外,cglib除了Enhancer之外,还有BulkBean和Transform,都是Hibernate持久化的基础,但文档贫乏,一时还没去看怎么用。
1.AOP里讲了一百遍阿一百遍的log aspect在cglib是这样做的:
public class LogDAOProxy implements MethodInterceptor
{
private Logger log=Logger.getLogger(LogDAOProxy.class);
private Enhancer enhancer=new Enhancer();
//返回DAO的子类
public Object getDAO(Class clz)
{
enhancer.setSuperclass(clz);
enhancer.setCallback(this);
return enhancer.create();
}
//默认的拦截方法
public Object intercept(Object o,Method method,Object[] args,MethodProxy proxy) throws Throwable
{
log.info("调用日志方法"+method.getName());
Object result=proxy.invokeSuper(o,args);
return result;
}
}
应用的代码:
LogDAOProxy proxy = new LogDAOProxy();
GoodsDAO dao = (GoodsDAO)proxy.getDAO(GoodsDAO.class);
dao.insert(goods);
GoodsDAO dao = (GoodsDAO)proxy.getDAO(GoodsDAO.class);
dao.insert(goods);
2.而在Spring的管理下应该略加修改的高级Decorator
上面的例子用return enhancer.create();创建子类实例,但在Spring管理下,一些Bean的实例必须由Spring来创建和管理,而不由enhancer来创建的。所以我对上述用法略加修改,使它真正当一个Proxy的角色,请对比黑体字的部分
public class LogDAOProxy implements MethodInterceptor
{
private Logger log=Logger.getLogger(LogDAOProxy.class);
private Object dao=null;
private Enhancer enhancer=new Enhancer();
//返回DAO的子类
public Object getDAO(Class clz,Object dao)
{
this.dao = dao;
enhancer.setSuperclass(clz);
enhancer.setCallback(this);
return enhancer.create();
}
//默认的拦截方法
public Object intercept(Object o,Method method,Object[] args,MethodProxy proxy) throws Throwable
{
log.info("调用日志方法"+method.getName());
Object result=proxy.invoke(dao, args);
return result;
}
}
{
private Logger log=Logger.getLogger(LogDAOProxy.class);
private Object dao=null;
private Enhancer enhancer=new Enhancer();
//返回DAO的子类
public Object getDAO(Class clz,Object dao)
{
this.dao = dao;
enhancer.setSuperclass(clz);
enhancer.setCallback(this);
return enhancer.create();
}
//默认的拦截方法
public Object intercept(Object o,Method method,Object[] args,MethodProxy proxy) throws Throwable
{
log.info("调用日志方法"+method.getName());
Object result=proxy.invoke(dao, args);
return result;
}
}
可见,原来模式里在getDao()时由enhancer创建dao,而 调用intercept时则将enhancer创建的dao以Object o参数传回。
而新模式里,dao在getDao()时从外面传入,enhancer.create()返回的是一个proxy. 而调用intercept时,实际会用之前传入的dao进行操作,而忽略Object o参数传入的proxy.
有点遗憾, intercept函数里MethodProxy的Signature是固定的 , 即客户如果调用foo(String),你不可以用proxy.invoke偷换成foo(String,String);
- 从装饰模式到aop
- 从BufferedReader到装饰设计模式
- 从装饰者模式到Context类族
- 从装饰者模式到 Context 类族
- 装饰模式和AOP
- 从代理模式到IOC/AOP
- 从代理设计模式到Spring AOP
- 装饰器模式(从放弃到入门)
- 重新认识装饰模式--装饰模式实现AOP
- 从咖啡谈装饰模式
- 从AOP到AOD,AOA
- AOP之代理模式与装饰着模式
- 从java.io包看装饰模式
- Javascript中的装饰者模式以及AOP简介
- spring从入门到上手-AOP
- AOP从理论到实践(一)
- C++设计模式::装饰模式or代理模式or面向切片编程(AOP)
- 装饰器与AOP
- 快速获取瑞星杀毒软件一年免费版
- 将24位BMP真彩图转换成BMP灰度图
- 浏览器还有这功能,强!!
- 字符编码问题
- CodeIgniter的几个问题
- 从装饰模式到aop
- Fedora 9 下使用apache+mongrel+rails轻松架设ROR应用
- firefox 网页部分图片无法显示
- FTP命令详解
- wince5.0 在xp64下安装
- 一小时asp入门
- 数据库设计第一范式
- 『转]用于刷新网页的javascript函数
- javascript定时器