Spring AOP(补充)

 

首先看几个关键术语:
aspect(方面):类似于JAVA编程语言中的类。方面定义切入点和通知,并由方面通知编译器来编译,以便将 横切(包含动态和静态)织入(interweave)现有对象中。
join point(连接点):是程序执行中的一个精确执行点,比如类中的一个方法或者一个异常抛出等等。连接点是个抽象的概念;不用主动的定义一个连接点。我们在上篇文章中Computer类的buy方法就是连接点!
point cut(切入点):本质上一个用于捕捉连接点的机构。切入点是用来捕捉连接点调用的方法,切入点需要在方面 中定义,也可以说是连接点的集合。
advice(通知):是切面的实际实现。
introduction(引入):引入允许你为已存在的类添加新方法和属性。
target(目标对象):是被通知的对象。
proxy(代理):代理是将 通知应用到目标对象后创建的对象。
weaving(织入):是将方面应用到目标对象,从而创建一个新的代理对象的过程。切面在指定接入点被织入到目标对象中。织入发生在对象生命周期中的多个点

而在Spring中所有的通知都是以JAVA类的形式编写,这意味着你可以旬开发普通JAVA开发那样在集成开发环境(IDE)中开发切面,而在什么地方应用切入点通常写在Spring配置文件中。这意味着切面代码和配置语法对于JAVA开发人员来说都是很熟悉的!
相对比AspectJ则需要写特定的语法来编写切面和定义切入点。而Spring则非常方便简单!
另外Spring AOP实现了AOP Allinace所有的规范接口!但它与JBOSS,AspectJ不同之处在于Spring它只支持方法切入点,而不支持属性做为切入点!
Spring的代理方式有二种;
第一种:如果目标对对象实现一个或多个接口,则使用java.lang.reflect.Proxy类创建代理!
第二种:如果目标对象没有实现任何的接口,则Spring使用CGLIB库生成目标对象的子类!
需要注意的是:
对接口创建代理优于对类创建代理,这样可以更加降低偶合
标记为final的方法不能被通知
Spring只支持方法做为连接点
通知有如下4种类型:
Around拦截目标方法的调用org.aopalliance.intercept.MethodInterceptor
Before在目标方法被调用之前调用org.springframework.aop.MethodBeforeAdvice
After在目标方法被调用之后调用org.springframework.aop.AfterReturningAdvice
Thorws在目标方法抛出异常时调用org.springframework.aop.ThrowsAdvice

我们可以使用最简单的方式也就是使用ProxyFactoryBean来创建AOP。
我们还是接着上一篇中的例子来创建一个!
Step1:我们重新建立一个代理类Subst.java让其继承MethodInterceptor接口!

让其可以在拦截目标方法的调用!
我们还是接着上一篇中的例子来创建一个!
Step1:我们重新建立一个代理类Subst.java让其继承MethodInterceptor接口!

None.gif package  fengyan.efly;
ExpandedBlockStart.gifContractedBlock.gif
/** */ /**
InBlock.gif * authod:fengyan
InBlock.gif * date:2007-01-01 00:42
ExpandedBlockEnd.gif 
*/

None.gif
import  org.aopalliance.intercept.MethodInterceptor;
None.gif
import  org.aopalliance.intercept.MethodInvocation;
None.gif
ExpandedBlockStart.gifContractedBlock.gif
public   class  Subst  implements  MethodInterceptor  dot.gif {
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif    
public Object invoke(MethodInvocation arg0) throws Throwable dot.gif{
InBlock.gif
InBlock.gif        System.out.println(
"送鼠标");
InBlock.gif        Object result 
= arg0.proceed();//拦截方法arg0调用其进行proceed方法
InBlock.gif
        return result;
ExpandedSubBlockEnd.gif    }

InBlock.gif
ExpandedBlockEnd.gif}

None.gif

Step2:编写applicationContext.xml文件!右键创建一个Bean,其id为subst   bean class即为上一步创建的代理类。
Step3:在applicationContext.xml中配置我们的Computer1  Bean
Step4:然后我们再配置一个代理类的bean,继承自ProxyFactoryBean接口。如下:

单击Add Class Properties一次性添加入类需要的属性,但我们需要改动一些,第一个就是你代理的是哪个类,我们已经有了Ipc接口,那么首先我们要设定代理类的接口是哪个。选中proxyInterface编辑:

它是一个值,就是我们接口的包的路径
接下来就是我们这里面哪个是充当通知,就是我们前面建立的代理了,我们选中target编辑

它的值为引用bean 就是前面我们创建的xomputer1
然后我再还需要设置一个要代理的目标类,编辑interceptorNames

它是一个复数,所以是List,我们再添加其List elements

其值就是我们在Step2中建立的bean ID。单击完成后我们将 一些我们用不上的properties去掉!最后的配置文件代码如下:
None.gif <? xml version="1.0" encoding="UTF-8" ?>
None.gif
<! DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd" >
None.gif
None.gif
< beans >
None.gif    
< bean  id ="subst"  class ="fengyan.efly.Subst"  abstract ="false"
None.gif        singleton
="true"  lazy-init ="default"  autowire ="default"
None.gif        dependency-check
="default" >
None.gif    
</ bean >
None.gif    
< bean  id ="computer1"  class ="fengyan.efly.Computer"  abstract ="false"
None.gif        singleton
="true"  lazy-init ="default"  autowire ="default"
None.gif        dependency-check
="default" >
None.gif    
</ bean >
None.gif    
< bean  id ="proxy"
None.gif        class
="org.springframework.aop.framework.ProxyFactoryBean"
None.gif        abstract
="false"  singleton ="true"  lazy-init ="default"
None.gif        autowire
="default"  dependency-check ="default" >
None.gif        
<!--  我们保留以下三属性  -->
None.gif        
None.gif        
None.gif        
<!--  对哪个对象进行代理  -->
None.gif        
< property  name ="target" >
None.gif            
< ref  bean ="computer1"   />
None.gif        
</ property >
None.gif    
None.gif        
<!--  谁是代理  -->
None.gif        
< property  name ="interceptorNames" >
None.gif            
< list >
None.gif                
< value > subst </ value >
None.gif            
</ list >
None.gif        
</ property >
None.gif        
None.gif        
<!--  原对象的接口是哪个  -->
None.gif        
< property  name ="proxyInterfaces" >
None.gif            
< value > fengyan.efly.Ipc </ value >
None.gif        
</ property >     
None.gif    
<!--  这就是使用ProxyFactoryBean作为的一种简单代理  -->
None.gif    
</ bean ></ beans >
最后我们在主方法中这样调用:
None.gif import  org.springframework.context.ApplicationContext;
None.gif
import  org.springframework.context.support.FileSystemXmlApplicationContext;
None.gif
ExpandedBlockStart.gifContractedBlock.gif
public   class  TestMain  dot.gif {
InBlock.gif    
ExpandedSubBlockStart.gifContractedSubBlock.gif    
public static void main(String[] args) dot.gif{    
InBlock.gif        
InBlock.gif        ApplicationContext context 
= new FileSystemXmlApplicationContext("src/fengyan/efly/applicationContext.xml");
InBlock.gif        
//然后我们获取该对象
InBlock.gif
        Ipc ipc = (Ipc)context.getBean("proxy");//获取代理Bean
InBlock.gif
        ipc.buy();
ExpandedSubBlockEnd.gif    }

InBlock.gif
ExpandedBlockEnd.gif}

我们运行得到如下结果:
                                          送鼠标
                                          获取:IBM电脑一台
这时候我们看到功能和上一篇是一样的!这就是使用ProxyFactoryBean来做的代理。

那么现在如果我们的一个类中有很多方法同时要做切面,就是很多方法共有的东西我们一起做代理,如何做呢,我们首先改Ipc接口

None.gif package  fengyan.efly;
None.gif
ExpandedBlockStart.gifContractedBlock.gif
public   interface  Ipc  dot.gif {
InBlock.gif    
InBlock.gif    
public void buyPC();
InBlock.gif    
public void buySoft();
InBlock.gif
ExpandedBlockEnd.gif}
然后改下Computer类
None.gif public   void  buyPC()
ExpandedBlockStart.gifContractedBlock.gif    
dot.gif {
InBlock.gif        System.out.println(
"获取:"+pcName+"电脑一台");
ExpandedBlockEnd.gif        }

None.gif    
public   void  buySoft()
ExpandedBlockStart.gifContractedBlock.gif    
dot.gif {
InBlock.gif        System.out.println(
"江南收费系统");
ExpandedBlockEnd.gif    }

现在就是无论我买PC还是买收费系统软件都要送鼠标,如何实现?
这时候就需要在配置文件中添加一新的bean,继承org.springframework.aop.support.RegexpMethodPointcutAdvisor接口

并为其添加几个属性,第一个为advice 是引用要代理的Bean  subst

第二个是我们哪些地方需要用到代理patterns我们给它一个值,而这值是正则表达式,

正则表达式符号有如下几种:
.匹配任意一个字符
+匹配前一个字符,一次或多次
*匹配前一个字符,零次或多次
\转义字符,把正则表达式中的字符转义
这样上面用.*buy.*表示所有buy方法
None.gif      < bean  id ="buyadvicor"
None.gif        class
="org.springframework.aop.support.RegexpMethodPointcutAdvisor"
None.gif        abstract
="false"  singleton ="true"  lazy-init ="default"
None.gif        autowire
="default"  dependency-check ="default" > <!--  通过方法拦截的类  -->
None.gif        
< property  name ="advice" >
None.gif            
< ref  bean ="subst"   />
None.gif        
</ property >
None.gif        
< property  name ="patterns" >
None.gif            
< value > .*buy.* </ value > <!--  我们定义一个方法的切入点只要是带buy的  -->
None.gif        
</ property >
None.gif    
</ bean >
这样有了它之后 我们原先的proxy要改动一下!
None.gif      <!--  谁是代理  -->
None.gif        
< property  name ="interceptorNames" >
None.gif            
< list >
None.gif                
< value > buyadvicor </ value > <!--  subst换为buyadvicor  -->
None.gif            
</ list >
None.gif        
</ property >

这时无论我们是buyPC还是buySoft都会送鼠标了,
注意这里面用到了正则表达式,如果我们改下正则表达式如下:

None.gif      < bean  id ="buyadvicor"
None.gif        class
="org.springframework.aop.support.RegexpMethodPointcutAdvisor"
None.gif        abstract
="false"  singleton ="true"  lazy-init ="default"
None.gif        autowire
="default"  dependency-check ="default" > <!--  通过方法拦截的类  -->
None.gif        
< property  name ="advice" >
None.gif            
< ref  bean ="subst"   />
None.gif        
</ property >
None.gif        
< property  name ="patterns" >
None.gif            
< value > .*fengyan\.efly\.Ipc.* </ value >
None.gif            
<!--  .*buy.*改为.*fengyan\.efly\.Ipc.*则只要是该接口中的方法被调用都会切入
None.gif            此时在接口中增加一个方法test依然会输出送鼠标 
-->
None.gif        
</ property >
None.gif    
</ bean >

则无论Ipc接口中的什么方法被调用都会切入!这是针对一个类中所有的方法进行拦截,这时就是只要你某个类中的方法被调用 它就会出一个通知。
现在再假设如果这是一个购物系统,我们希望当顾客购买东西之前做一件事情,以及买完后也做一优缺点 事情,这里简单的输出欢迎光临和欢迎下次再来!那么我们要将 我们的Subst.java类改为继承自MethodBeforeAdvice接口,并穿实现before方法,代码如下:

None.gif package  fengyan.efly;
None.gif
None.gif
import  java.lang.reflect.Method;
None.gif
None.gif
import  org.springframework.aop.MethodBeforeAdvice;
None.gif
ExpandedBlockStart.gifContractedBlock.gif
public   class  Subst  implements  MethodBeforeAdvice  dot.gif {
InBlock.gif
InBlock.gif    
public void before(Method arg0, Object[] arg1, Object arg2)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
throws Throwable dot.gif{
InBlock.gif        
// TODO Auto-generated method stub
InBlock.gif
        System.out.println("欢迎光临本商店");
ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

假设该代理是购买之前的,那么我们再写一个代理类是购买之后的,它则需要继承AfterReturningAdvice接口,如下
None.gif package  fengyan.efly;
None.gif
None.gif
import  java.lang.reflect.Method;
None.gif
None.gif
import  org.springframework.aop.AfterReturningAdvice;
None.gif
ExpandedBlockStart.gifContractedBlock.gif
public   class  After  implements  AfterReturningAdvice  dot.gif {
InBlock.gif
InBlock.gif    
public void afterReturning(Object arg0, Method arg1, Object[] arg2,
ExpandedSubBlockStart.gifContractedSubBlock.gif            Object arg3) 
throws Throwable dot.gif{
InBlock.gif        
InBlock.gif        System.out.println(
"欢迎下次再来");
InBlock.gif
ExpandedSubBlockEnd.gif    }

InBlock.gif
ExpandedBlockEnd.gif}

None.gif
之后我们改动配置文件如下:
None.gif <? xml version="1.0" encoding="GBK" ?>
None.gif
<! DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd" >
None.gif
None.gif
< beans >
None.gif     
< bean  id ="subst"  class ="fengyan.efly.Sbust"  abstract ="false"
None.gif        singleton
="true"  lazy-init ="default"  autowire ="default"
None.gif        dependency-check
="default" >
None.gif    
</ bean >
None.gif    
< bean  id ="computer1"  class ="fengyan.efly.Computer"  abstract ="false"
None.gif        singleton
="true"  lazy-init ="default"  autowire ="default"
None.gif        dependency-check
="default" >
None.gif    
</ bean >
None.gif    
None.gif    
< bean  id ="proxy"
None.gif        class
="org.springframework.aop.framework.ProxyFactoryBean"
None.gif        abstract
="false"  singleton ="true"  lazy-init ="default"
None.gif        autowire
="default"  dependency-check ="default" >
None.gif            
None.gif        
<!--  对哪个对象进行代理  -->
None.gif        
< property  name ="target" >
None.gif            
< ref  bean ="computer1"   />
None.gif        
</ property >
None.gif    
None.gif         
<!--  谁是代理  -->
None.gif        
< property  name ="interceptorNames" >
None.gif            
< list >
None.gif                
< value > buyBeforeAdvicor </ value > <!--  改为购买之前的代理  -->
None.gif                
< value > buyAfterAdvicor </ value > <!--  并加一个购买之后的代理  -->
None.gif            
</ list >
None.gif        
</ property >
None.gif        
None.gif        
<!--  原对象的接口是哪个  -->
None.gif        
< property  name ="proxyInterfaces" >
None.gif            
< value > fengyan.efly.Ipc </ value >
None.gif        
</ property >     
None.gif    
None.gif    
</ bean >
None.gif    
<!--  购买之前的代理bean  -->
None.gif    
< bean  id ="buyBeforeAdvicor"
None.gif        class
="org.springframework.aop.support.RegexpMethodPointcutAdvisor"
None.gif        abstract
="false"  singleton ="true"  lazy-init ="default"
None.gif        autowire
="default"  dependency-check ="default" > <!--  通过方法拦截的类  -->
None.gif        
< property  name ="advice" >
None.gif            
< ref  bean ="subst"   />      <!--  它的代理类为subst Bean 在上面已经定义  -->
None.gif        
</ property >  
None.gif        
< property  name ="patterns" >
None.gif            
< value > .*buy.* </ value >          
None.gif        
</ property >
None.gif    
</ bean >
None.gif    
<!--  购买之后的代理bean  -->
None.gif    
< bean  id ="buyAfterAdvicor"
None.gif        class
="org.springframework.aop.support.RegexpMethodPointcutAdvisor"
None.gif        abstract
="false"  singleton ="true"  lazy-init ="default"
None.gif        autowire
="default"  dependency-check ="default" >
None.gif        
< property  name ="advice" >
None.gif            
< ref  bean ="after"   />      <!--  它的代理类为after Bean 在下面已丰富 -->
None.gif        
</ property >
None.gif        
< property  name ="patterns" >
None.gif            
< value > .*buy.* </ value >         
None.gif        
</ property >
None.gif    
</ bean >
None.gif    
None.gif    
< bean  id ="after"  class ="fengyan.efly.After"  abstract ="false"
None.gif            singleton
="true"  lazy-init ="default"  autowire ="default"
None.gif            dependency-check
="default" >
None.gif    
</ bean >
None.gif        
None.gif
</ beans >


现在我们再运行如果是buyPC或者buySoft都会在之前发出欢迎光临,之后发生欢迎下次再来的通知!而如果是test方法则没有什么消息!

本篇仅仅是AOP的缩水版,仅仅是让大家初步了解AOP!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值