Spring AOP

 

Spring AOP

        Spring中所有的通知都是java类的形式编写,定义在什么地方应用通用的切入点通常编写在spring的配置文件中。需明确的几个概念:

                通知(Advice):用于告知系统将有哪些新的行为。

                切入点(Pointcut):定义了通知应该在应用到那些连接点。

                目标对象(Target):被通知的对象。 

                代理(Proxy):将通知应用到目标对象后创建的对象。

        代理Bean只有在第一次被应用系统需要的时候才被创建,如果使用的是ApplicationContext,代理对象在BeanFactory 载入所有Bean的时候创建。

        Spring2种代理创建方式:

            1.如果目标对象实现了一个或多个接口暴露的方法,Spring将使用JDKjava.lang.reflect.Proxy类创建代理,这个类让Spring动态产生一个新的类,它实现了所有的接口,织入了通知,并且代理对目标对象的所有请求。

            2.如果目标对象没有实现任何接口,Spring使用CGLIB库生成目标对象的子类,在创建子类的时候,Spring将通过织入,并且将对目标对象的调 用委托给这个子类,使用这种方式应注意两个要点:

                (1)对接口创建代理优于对类创建代理,因为这样会产生更加松耦合的系统,对类生成代理是让遗留系统或者 无法实现接口的第三方类库同样的可以得到通知,这种方式是备用方案

                (2)标记为final的方法不能被通知,Spring是为目标类产生子类,任何需要被 通知的方法都会被复写,将通知织入,final方法是不可能做到的。

        通知有以下几种:

            1.Around(周围同志)---org.aopllliance.intercept.MethodInterceptor拦截对目标对象方法调用

            2.Before(前通知)---org.springframework.aop.BeforeAdvice在目标方法被调用前调用

            3.After(返回后通知)---org.springframework.aop.AfterRetruningAdvice在方法被调用之后调用

            4.Throws(抛出后通知)---org.springframework.aop.ThrowsAdvice在目标方法抛出异常时调用

            5.Introduction(引入)--- org.springframework.aop.IntroductionInterceptor

 

    示例:

Public class AudienceAdvice implements MethodBeforeAdvice,AfterReturningAdvice,ThrowsAdvice
{
  //前通知要求实现的方法
  public void before(Method method,Object[] args,Object target) throws Throwable
  {
  }
  //返回后通知要求实现的方法,Object returnValue表示被调用方法的返回值
  public void  afterReturning(Object returnValue,Method method,Object[] args,Object target) throws Throwable
  {
  }
  //抛出后通知不需要实现任何方法,它只是一个标记接口,告诉spring相应的通知要求处理被抛出的异常,除最后一个参数外,其他参数都是可选的
  public void afterThrowing(Method method,Object[] args,Object target,NullPointerException e) throws Throwable
  {
    //当发生NullPointerException 时,记录日志
    LOGGER.error("NPE thrown from "+method.getName());
  }
}

        其中的Method method参数,表示要使用这个通知的方法,第二个参数Object[] args是方法被调用时要传递的参数。最后一个是方法调用的目标也就是被调用方法所在的对象。

 

        周围通知相当于前通知、后通知、抛出后通知的结合。

Public class AudienceAdvice implements MethodInterceptor
{
  Public Object invoke(MethodInvocation invocation) throws Throwable
  {
    Try
    {
      ..............................    //在方法调用之前
        Object returnValue=invocation.proceed();    调用目标方法
      .............................    //在方法调用之后
    }
    catch(PerformanceException e)
    {
      .............................    /在出现异常之后
    }
  }
}
  

 

切点

 

1.正则切点

        切点的作用主要在于选择需要使用通知的方法,比如下面的正则表达式切点:

<bean id="performancePointcut" class="org.springframework.aop.support.JbkRegexpMethodPointcut">
  <property name="pattern" value=".*perform" />
</bean>
// Pattern属性用于制定方法匹配所使用的切点模板,本例中被设置为一个正则表达式,它应该匹配任何类里名为perform的方法。

        定义切点后,把切点与通知关联

<bean id="audienceAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
  <property name="advice" value="audienceAdvice" />
  <property name="pointcut" value="performancePointcut" />
</bean>
//DefaultPointcutAdvisor是一个通知者类,它只是把通知关联到一个切点。

        也可以使用RegexpMethodPointcutAdvisor是一个特殊的通知者类,可以在一个Bean里面定义切点和通知者

<bean id="audienceAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
  <property name="advice" value="audienceAdvice" />
  <property name="pattern" value=".*perform" />
</bean>
  

2.定义AspectJ切点

 

        正则表达式虽然可以作为切点定义语言来使用,但是它并不是针对切点而创建的,其主要用途还是文本解析。AspectJ里定义切点的方法就可以看出AspectJ的切点语言是一种真正的切点表达式语言。

<bean id="performancePointcut" class="org.springframework.aop.aspectj.AspectJExpressionPointcut">
  <property name="expression" value="execution(* Performer+.perform(..))" />
</bean>

        可以使用DefaultPointcutAdvisor把它和通知关联起来,就像前面一样。同样,也可以设置一个特殊的通知者

<bean id="audienceAdvisor" class="org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor">
  <property name="advice" value="audienceAdvice" />
  <property name="expression" value="execution(* *.perform(..))" />
</bean>

        execution* *.perform(..)) 

        execution表示执行方法时,* *表示任意返回类型,.perform表示perform方法,(..)表示任意参数设置。

 

使用ProxyFactoryBean

<bean id="duke" class="org.springframework.aop.framework.ProxyFactoryBean">
  <property name="proxyInterfaces">
    <value>genericdaotest.dao.PersonDao</value>
  </property>
  <property name="target">
    <bean parent="abstractDaoTarget">
      <constructor-arg>
        <value>genericdaotest.domain.Person</value>
      </constructor-arg>
    </bean>
  </property>
  <property name="interceptorNames">
    <list>
      <value>audienceAdvisor</value>
    </list>
  </property>
</bean>

        Spring里面的ProxyFactoryBean是个工厂Bean,用于生成一个代理,把一个或者多个拦截者(和通知者)应用到Bean,有targetinterceptorNamesproxyInterfaces三个属性,后两个都是数组属性target表示要代理的对象。

        interceptorNames属性告诉ProxyFactoryBean哪个通知者要应用于被代理的

        beanproxyInterfaces告诉ProxyFactoryBean代理应该实现哪个接口。本例中代理的是genericdaotest.dao.PersonDao接口。

        target告诉ProxyFactoryBean,哪个Bean是代理。

 

自动代理

        使用ProxyFactoryBean来显式的创建AOP代理在很多场合下,会使编写配置文件的工作量大大增加;由于要从ProxyFactoryBean获得代理对象,也会使应用和Spring之间的耦合度增加。下面介绍使用Spring提供的自动代理机制来解决这类问题。

 

1.使用BeanNameAutoProxyCreator

        Spring提供的BeanNameAutoProxyCreator类允许我们通过Beanname属性来指定代理的Bean。它暴露了java.lang.String[]类型的beanNames interceptorNames属性。beanNames可以指定被代理的Bean名字列表,支持“*”通配符,例如“*DAO”表示所有名字以“DAO”结尾的BeaninterceptorNames指定通知(Advice)列表,或者通知者(Advisor)列表。

        下面通过一个例程来演示如何使用BeanNameAutoProxyCreator。在例子中,有两个BeanTestBeanABeanB,并在TestMain类中的main方法中调用其MyMethod()方法。自动代理将会在方法调用前自动的执行配置的前置通知,输出提示信息。

        新建一个名字为AOP_Test4.10的工程,添加SpringIoCAOP库后,新建一aop.test包,再分别创建两个类TestBeanABeanB,添加MyMethod()方法,代码如下:

package aop.test;  

public class TestBeanA 
{  
  public void MyMethod() 
  {  
    System.out.println(this.getClass().getName()  + ".MyMethod() is run!");  
  }  
}  
package aop.test;    

public class BeanB 
{      
  public void MyMethod() 
  {  
    System.out.println(this.getClass().getName()  + ".MyMethod() is run!");  
  }  
}  

        再创建前置通知类BeforeAdvice

package aop.test;    

import java.lang.reflect.Method;  
import org.springframework.aop.MethodBeforeAdvice;    
   
public class BeforeAdvice implements MethodBeforeAdvice 
{    
  public void before(Method method, Object[] args, Object target) throws Throwable 
  {  
    System.out.println(method.getName() + "(),将要运行!");  
  }  
}  

        最后创建含有main方法的测试类TestMain

package aop.test;  

import org.springframework.context.ApplicationContext;  
import org.springframework.context.support.ClassPathXmlApplicationContext;    

public class TestMain 
{   
  public static void main(String[] args) 
  {  
    ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");          
    TestBeanA beanA = (TestBeanA)ac.getBean("TestBeanA");  
    beanA.MyMethod();  
    BeanB beanB = (BeanB)ac.getBean("BeanB");  
    beanB.MyMethod();          
  }  
}  

        在配置文件中配置Bean和自动代理Bean,完成后代码如下:

<?xml version="1.0" encoding="UTF-8"?>  
<beans …………>   
  <bean id="TestBeanA" class="aop.test.TestBeanA"/>  
  <bean id="BeanB" class="aop.test.BeanB"/>    
  <bean id="BeforeAdvice" class="aop.test.BeforeAdvice"></bean>  
  <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">  
    <property name="beanNames">  
      <list>  
        <value>Test*</value>  
      </list>  
    </property>  
    <property name="interceptorNames">  
      <list>  
        <value>BeforeAdvice</value>  
      </list>  
    </property>  
  </bean>  
</beans>  

        运行主类,输出结果如下:

      

        可以看到,在主类TestMain中,我们是直接从Spring IoC容器中获取收管Bean而不是像以前那样从ProxyFactoryBean中获取代理,但是我们的前置通知BeforeAdvice仍然在TestBeanA对象的MyMethod()方法执行前被触发,这说明我们的自动代理正在工作。

 

2.使用DefaultAdvisorAutoProxyCreator

        DefaultAdvisorAutoProxyCreator允许我们只需定义相应的Advisor通知者,就可以完成自动代理。配置好DefaultAdvisorAutoProxyCreator受管Bean后,它会自动查找配置文件中定义的Advisor,并将它们作用于所有的Bean

        修改例程4.10的配置文件,使用DefaultAdvisorAutoProxyCreator来完成自动代理。完成后配置文件代码如下(本例完整工程代码见例程4.11):

<?xml version="1.0" encoding="UTF-8"?>  
<beans ……>  
  <bean id="TestBeanA" class="aop.test.TestBeanA" />  
  <bean id="BeanB" class="aop.test.BeanB" />  
  <bean id="BeforeAdvice" class="aop.test.BeforeAdvice"/>  
  <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" />      
  <bean class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">  
    <property name="advice" ref="BeforeAdvice" />  
    <property name="mappedNames">  
      <list>  
        <value>*Method*</value>  
      </list>  
    </property>  
  </bean>      
</beans>   

        运行主类输出结果如下:

      

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值