spring aop 详解.docx
- 文档编号:12642176
- 上传时间:2023-04-21
- 格式:DOCX
- 页数:24
- 大小:23.57KB
spring aop 详解.docx
《spring aop 详解.docx》由会员分享,可在线阅读,更多相关《spring aop 详解.docx(24页珍藏版)》请在冰豆网上搜索。
springaop详解
引用地址:
谨供参考
感谢老许老师
springAOP
(1)2
springAOP
(2)9
springAOP(3)16
springAOP
(1)
分类:
spring系列2008-06-1523:
441082人阅读评论(19)收藏举报
spring里面有个概念叫aop(面向切面编程),很好很强大又很让人费解,很多开发人员会用并且天天挂在嘴边但是不理解其核心原理,今天周末有空,我想用一个小系列的文章给大家把aop分析清楚。
要理解aop,首先要掌握java中的代理模式。
在日常生活中,会遇到各种各样的中介机构,比如猎头公司,律师事务所,婚姻介绍所,房产公司等。
在这些单位工作的人员均可称为代理人。
代理人的共同特征是可以代替委托人去和第三方通信。
譬如:
律师代替委托人打官司,猎头代替委托人物色人才,红娘代替委托人寻找对象,房产代理人代替委托人出租房屋。
代理人可以在第三方和委托人之间转发或过滤消息,但是不能取代委托人的任务。
譬如你要找女朋友,委托你一要好的哥们去帮你物色,结果被他给物色了。
这就不叫代理。
代理模式的作用是:
为其他对象提供一种代理以控制对这个对象的访问。
在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
代理模式一般涉及到的角色有:
抽象角色:
声明真实对象和代理对象的共同接口;
代理角色:
代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。
同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。
真实角色:
代理角色所代表的真实对象,是我们最终要引用的对象。
我们来看个例子:
packagecom.wepull.aop.staticProxy;
/**
*@authorleno
*抽象角色
*/
publicinterfaceIHello{
Stringgreeting(Stringwho);
}
packagecom.wepull.aop.staticProxy;
/**
*@authorleno
*真实角色
*/
publicclassHelloImplimplementsIHello{
publicStringgreeting(Stringwho){
System.out.println("greetingmethodisinvoked.....");
return"hello,"+who;
}
}
试想一下,如果这时候我们要对问候的业务逻辑方法进行日志记录。
我们当然可以这样做:
packagecom.wepull.aop.staticProxy;
/**
*@authorleno
*真实角色
*/
publicclassHelloImplimplementsIHello{
privateLoggerlogger=Logger.getLogger(this.getClass().getName());
publicvoidlog(Stringmessage)
{
logger.log(Level.INFO,message);
}
publicStringgreeting(Stringwho){
log("starting...");
System.out.println("greetingmethodisinvoked.....");
log("stopping...");
return"hello,"+who;
}
可问题来了,项目经理发话,不容许修改现存的类的实现。
怎么办?
这时候我们就要就想到代理模式了。
我们再加一个代理类:
静态代理类:
packagecom.wepull.aop.staticProxy;
importjava.util.logging.Level;
importjava.util.logging.Logger;
publicclassHelloLoggerProxyimplementsIHello{
privateLoggerlogger=Logger.getLogger(this.getClass().getName());
privateIHellohelloImpl;
publicHelloLoggerProxy(IHellohelloImpl){
super();
this.helloImpl=helloImpl;
}
publicStringgreeting(Stringwho){
log("starting...");
Stringhello=helloImpl.greeting(who);
log("stopping...");
returnhello;
}
publicvoidlog(Stringmessage)
{
logger.log(Level.INFO,message);
}
}
客户端测试代码:
packagecom.wepull.aop.staticProxy;
publicclassTestStaticProxy{
publicstaticvoidmain(String[]args){
IHellohello=newHelloLoggerProxy(newHelloImpl());
hello.greeting("leno");
}
}
由以上代码可以看出,客户实际需要调用的是HelloImpl类的greeting()方法,现在用HelloLoggerProxy来代理HelloImpl类,同样达到目的,同时还在方法调用的前后都做了日志记录。
这就是静态代理。
但是,如果要按照上述的方法使用代理模式,那么真实角色必须是事先已经存在的,并将其作为代理对象的内部属性。
但是实际使用时,一个真实色必须对应角一个代理角色,如果大量使用会导致类的急剧膨胀;而且,如果项目中要做日志的类有100个,每个类里面有100个方法。
重复代码就太可怕了。
有人说,不怕,我就一愚公。
好,有天项目经理突然要求修改做日志的方式,你再看看这些方法,估计撞墙的心都有了。
那怎么办?
我们就改用动态代理。
JDK1.3以后提供了动态代理的支持,程序员通过实现java.lang.reflect.InvocationHandler接口提供一个执行处理器,然后通过java.lang.reflect.Proxy得到一个代理对象,通过这个代理对象来执行商业方法,在商业方法被调用的同时,执行处理器会被自动调用。
动态代理类:
packagecom.wepull.aop.dynamicProxy;
importjava.lang.reflect.InvocationHandler;
importjava.lang.reflect.Method;
importjava.lang.reflect.Proxy;
importjava.util.logging.Level;
importjava.util.logging.Logger;
publicclassDynamicLoggerProxyimplementsInvocationHandler{
privateLoggerlogger=Logger.getLogger(this.getClass().getName());
privateObjectdelegate;
@SuppressWarnings("unchecked")
publicObjectbind(Objectdelegate)
{
this.delegate=delegate;
Classcls=delegate.getClass();
returnProxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(),this);
}
publicObjectinvoke(Objecto,Methodmethod,Object[]args)
throwsThrowable{
log("starting...");
Objectobj=method.invoke(delegate,args);
log("stopping...");
returnnull;
}
publicvoidlog(Stringmessage)
{
logger.log(Level.INFO,message);
}
}
客户端测试代码:
packagecom.wepull.aop.dynamicProxy;
publicclassTestDynamicProxy{
publicstaticvoidmain(String[]args){
IHellohello=(IHello)newDynamicLoggerProxy().bind(newHelloImpl());
hello.greeting("leno");
}
}
大家看到,一个动态代理类就代替了无数个静态代理类。
一劳永逸。
这里涉及到一些java反射的知识,我会在以后单独讲述。
java的动态代理非常实用和强大,可它有一个前提,需要我们的处理业务逻辑的类必须至少实现一个接口,虽然面向接口编程是
需要提倡的,但如果我们有些现存的类就是没有实现接口,那如何代理呢?
这就需要CGLIB的帮助了。
继续改进我们的代码:
没有实现任何接口的类:
packagecom.wepull.aop.cglibProxy;
publicclassHello{
publicvoidgreeting(Stringwho){
System.out.println("hello,"+who);
}
}
CGLIB代理类:
packagecom.wepull.aop.cglibProxy;
importjava.lang.reflect.Method;
importjava.util.logging.Level;
importjava.util.logging.Logger;
importnet.sf.cglib.proxy.Enhancer;
importnet.sf.cglib.proxy.MethodInterceptor;
importnet.sf.cglib.proxy.MethodProxy;
publicclassCGLIBLoggerProxyimplementsMethodInterceptor{
privatefinalLoggerlogger=Logger.getLogger(this.getClass().getName());
publicObjectbind(Objectdelegate)
{
Enhancerenhancer=newEnhancer();
enhancer.setCallback(this);
enhancer.setSuperclass(delegate.getClass());
returnenhancer.create();
}
publicObjectintercept(Objecto,Methodmethod,Object[]args,
MethodProxyproxy)throwsThrowable{
log("starting...");
proxy.invokeSuper(o,args);
log("stopping...");
returnnull;
}
publicvoidlog(Stringmessage)
{
logger.log(Level.INFO,message);
}
}
客户端测试代码:
packagecom.wepull.aop.cglibProxy;
publicclassTestCGLIBProxy{
publicstaticvoidmain(String[]args){
Hellohello=(Hello)newCGLIBLoggerProxy().bind(newHello());
hello.greeting("leno");
}
}
好了,有了动态代理和CGLIB,那我们就可以给任何实际的类提供代理了。
从而可以在我们的代理类中加入一些与业务逻辑无关的系统级的功能服务,如:
日志记录,事务处理,安全管理等。
而且不会影响到我们现有的系统。
看起来似乎很完美了。
但是且慢,如果我们的应用中有些需要这些系统级的功能服务,而有些又不需要呢?
如果把代码写死了显然达不到我们的要求。
怎么办?
这时候就要用到一个非常重要的思想:
用配置代替编码
springAOP
(2)
分类:
spring系列2008-10-0722:
26590人阅读评论
(2)收藏举报
前面写过一篇关于SpringAOP方面的文章,探讨了SpringAOP底层实现的一些细节知识,这里面涉及到了JAVA反射机制,代理模式以及CGLIB库的使用。
也就是说,SpringAOP底层实现就是靠动态代理(针对有接口的类)和CGLIB(针对没有实现接口的一般类),那么,有了这些知识,再辅佐对核心配置XML文件解析的能力,其实就可以实现一个简易的基于IOC和AOP的小框架,大家可以自己尝试着写一下。
下面呢我们就由浅入深地来看看在Spring中AOP是怎么实现的。
最简单的AOP实现只需要涉及3个概念:
目标(Target),通知(Advice)和代理(Proxy)。
目标呢,当然就是真实的需要被代理的对象,一般它会实现至少一个接口。
通知呢,就是当目标的方法调用时需要调用的代码,也叫拦截器。
而代理,毫无疑问就是加入了通知的目标了,它可以作为目标的替身出现。
为了说明这三者的关系,我们来看一个网上有趣的小例子:
一间书店开始打折促销,规则是每一名顾客只能买一本书,并且当顾客来到书店时,要说喜欢您来。
顾客走的时候,还要说喜欢您再来!
(麦当劳啊^_^)顾客如果买到
呵呵,好啦,洪哥,我们动手吧!
packagecom.wepull.spring.book;
publicclassNoThisBookExceptionextendsException{
publicNoThisBookException(Stringmsg){
super(msg);
}
}
packagecom.wepull.spring.book;
publicinterfaceBuyBook{
publicvoidbuyBook(Stringcustomer,Stringbook)throwsNoThisBookException;
}
packagecom.wepull.spring.book;
publicclassMyBuyBookimplementsBuyBook{
publicvoidbuyBook(Stringcustomer,Stringbook)
throwsNoThisBookException{
if(book.equals("
thrownewNoThisBookException("对不起,没有"+book+"的存货了!
");
System.out.println(customer+",你好,你已经购买了一本"+book+"!
");
}
}
看上面,我们做了一个异常类和一个简单买书的接口,并且目标MyBuyBook也已经出现。
OK,再来看看通知吧!
Spring中主要有以下四种通知类型:
(1)Around通知
(2)Before通知
(3)Throws通知
(4)AfterReturning通知
例子分别如下:
packagecom.wepull.spring.book;
importjava.util.HashSet;
importjava.util.Set;
importorg.aopalliance.intercept.MethodInterceptor;
importorg.aopalliance.intercept.MethodInvocation;
publicclassMyAroundAdviceimplementsMethodInterceptor{
privateSetcustomers=newHashSet();//保存购过书的顾客信息
publicObjectinvoke(MethodInvocationinvocation)throwsThrowable{
Object[]args=invocation.getArguments();
if(customers.contains(args[0])){
System.out.println("对不起,一名顾客只能买一本打折书!
");
returnnull;
}
customers.add(args[0]);
returninvocation.proceed();
}
}
packagecom.wepull.spring.book;
importjava.lang.reflect.Method;
importorg.springframework.aop.MethodBeforeAdvice;
publicclassMyBeforeAdviceimplementsMethodBeforeAdvice{
publicvoidbefore(Methodarg0,Object[]arg1,Objectarg2)
throwsThrowable{
Stringcustomer=(String)arg1[0];//第2个参数组就是被通知的方法传入的参数,本例中即customer,book
System.out.println("喜欢您来!
"+customer+"!
");//显示欢迎信息!
,在buyBook方法前调用
}
}
packagecom.wepull.spring.book;
importjava.lang.reflect.Method;
importorg.springframework.aop.ThrowsAdvice;
publicclassMyThrowsAdviceimplementsThrowsAdvice{
publicvoidafterThrowing(Methodmethod,Object[]args,Objecttarget,NoThisBookExceptione){
//可以定义多个方法,只要传入的参数是不同异常
System.out.println("对不起"+args[0]+",没货了。
通知仓库,赶紧加书!
");
}
}
packagecom.wepull.spring.book;
importjava.lang.reflect.Method;
importorg.springframework.aop.AfterReturningAdvice;
publicclassMyAfterAdviceimplementsAfterReturningAdvice{
publicvoidafterReturning(Objectarg0,Methodarg1,Object[]arg2,
Objectarg3)throwsThrowable{
Stringcustomer=(String)arg2[0];//同前置通知一样,参数组3为传入参数,具体见springdoc
System.out.println("喜欢您再来!
"+customer+"!
");//显示欢送信息!
}
}
现在目标类有了,通知类也写好了,写了就要配,这是框架的基本使用原则。
呵呵,下面就来看看核心配置文件src/applicationContext.xml:
xmlversion="1.0"encoding="UTF-8"?
>
//www.springframework.org/schema/beans" xmlns: xsi="http: //www.w3.org/2001/XMLSchema-instance" xsi: schemaLocation="http: //www.springframework.org/schema/beanshttp: //www.springframework.org/schema/beans/spring-beans-2.5.xsd"> --各种通知--> --前置通知--> class="com.wepull.spring.book.MyBeforeAdvice"> --后置通知--> class="com.wepull.spring.book.MyAfterAdvice"> --异常通知--> class="com.wepull.spring.book.MyThrowsAdvice"> --环绕通知--> class="com.wepull.spring.book.MyAroundAdvice"> --目标对象--> class=
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- spring aop 详解