Spring AOP三种拦截方式举例详解

  • Post author:
  • Post category:其他



http://blog.sina.com.cn/s/blog_624a352c01018bsr.html


Spring的AOP是上面代理模式的深入。使用Spring AOP,开发者无需实现业务逻辑对象工厂,无需实现代理工厂,这两个工厂都由Spring容器充当。Spring AOP不仅允许使用XML文件配置目标方法,ProxyHandler也允许使用依赖注入管理,Spring AOP提供了更多灵活的选择。

在下面Spring AOP的示例中,InvocationHandler采用动态配置,需要增加的方法也采用动态配置,一个目标对象可以有多个拦截器(类似于代理模式中的代理处理器)。

下面是原始的目标对象:

//目标对象的接口

public interface Person

{



//该接口声明了两个方法


void info();


void run();

}

下面是原始目标对象的实现类,实现类的代码如下:

//目标对象的实现类,实现类实现Person接口

public class PersonImpl implements Person

{


// 两个成员属性


private String name;


private int age;



// name属性的 setter方法


public void setName(String name) {




this.name = name;


}



// age属性的setter方法


public void setAge(int age) {




this.age = age;


}



// info方法,该方法仅仅在控制台打印一行字符串


public void info() {




System.out.println(“我的名字是:

” + name + ” , 今年年龄为:

” + age);


}



// run方法,该方法也在控制台打印一行字符串。


public void run() {




if (age < 45) {





System.out.println(“我还年轻,奔跑迅速…”);



} else {





System.out.println(“我年老体弱,只能慢跑…”);



}


}

}

该Person实例将由Spring容器负责产生和管理,name属性和age属性也采用依赖注入管理。

为了充分展示Spring AOP的功能,此处为Person对象创建三个拦截器。第一个拦截器是调用方法前的拦截器,代码如下:

//调用目标方法前的拦截器,拦截器实现MethodBeforeAdvice接口

public class MyBeforeAdvice implements MethodBeforeAdvice

{



//实现MethodBeforeAdvice接口,必须实现before方法,该方法将在目标


//方法调用之前,自动被调用。


public void before(Method m, Object[] args, Object target) throws Throwable


{




System.out.println(“方法调用之前…”);



System.out.println(“下面是方法调用的信息:”);



System.out.println(“所执行的方法是:” + m);



System.out.println(“调用方法的参数是:” + args);



System.out.println(“目标对象是:” + target);


}

}

第二个拦截器是方法调用后的拦截器,该拦截器将在方法调用结束后自动被调用,拦截器代码如下:

//调用目标方法后的拦截器,该拦截器实现AfterReturningAdvice接口

public class MyAfterAdvice implements AfterReturningAdvice

{



//实现AfterReturningAdvice接口必须实现afterReturning方法,该方法将在目标方法


//调用结束后,自动被调用。


public void afterReturning(Object returnValue, Method m, Object[] args, Object target)throws

Throwable


{




System.out.println(“方法调用结束…”);



System.out.println(“目标方法的返回值是 : ” + returnValue);



System.out.println(“目标方法是 : ” + m);



System.out.println(“目标方法的参数是 : ” + args);



System.out.println(“目标对象是 : ” + target);


}

}

第三个拦截器是是Around拦截器,该拦截器既可以在目标方法之前调用,也可以在目标方法调用之后被调用。下面是Around拦截器的代码:

//Around拦截器实现MethodInterceptor接口

public class MyAroundInterceptor implements MethodInterceptor

{



//实现MethodInterceptor接口必须实现invoke方法


public Object invoke(MethodInvocation invocation) throws Throwable


{




//调用目标方法之前执行的动作



System.out.println(“调用方法之前: invocation对象:[” + invocation + “]”);



//调用目标方法



Object rval = invocation.proceed();



//调用目标方法之后执行的动作



System.out.println(“调用结束…”);



return rval;


}

}

利用Spring AOP框架,实现之前的代理模式相当简单。只需要实现对应的拦截器即可,无需创建自己的代理工厂,只需采用Spring容器作为代理工厂。下面在Spring配置文件中配置目标bean,以及拦截器。

下面是Spring配置文件的代码:

<?xml version=”1.0″ encoding=”gb2312″?>

<!– Spring配置文件的文件头–>

<!DOCTYPE beans PUBLIC “-//SPRING//DTD BEAN//EN”

“http://www.springframework.org/dtd/spring-beans.dtd”>

<!– Spring配置文件的根元素–>

<beans>

<!–

配置目标对象–>


<bean id=”personTarget” class=”lee.PersonImpl”>



<!–

为目标对象注入name属性值–>



<property name=”name”>




<value>Wawa</value>



</property>



<!–

为目标对象注入age属性值–>



<property name=”age”>




<value>51</value>



</property>


</bean>


<!–

第一个拦截器–>


<bean id=”myAdvice” class=”lee.MyBeforeAdvice” />


<!–

第二个拦截器–>


<bean id=”myAroundInterceptor” class=”lee.MyAroundInterceptor” />


<!–

将拦截器包装成Advisor,该对象还确定代理对怎样的方法增加处理–>


<bean id=”runAdvisor”



class=”org.springframework.aop.support.RegexpMethodPointcutAdvi

sor”>



<!–

advice属性确定处理bean–>



<property name=”advice”>




<!– 此处的处理bean定义采用嵌套bean,也可引用容器的另一个bean–>




<bean class=”lee.MyAfterAdvice” />



</property>



<!–

patterns确定正则表达式模式–>



<property name=”patterns”>




<list>





<!–

确定正则表达式列表–>





<value>.*run.*</value>




</list>



</property>


</bean>


<!–

使用ProxyFactoryBean 产生代理对象–>


<bean id=”person” class=”org.springframework.aop.framework.ProxyFactoryBean”>



<!–

代理对象所实现的接口–>



<property name=”proxyInterfaces”>




<value>lee.Person</value>



</property>



<!–

设置目标对象–>



<property name=”target”>




<ref local=”personTarget” />



</property>



<!–

代理对象所使用的拦截器–>



<property name=”interceptorNames”>




<list>





<value>runAdvisor</value>





<value>myAdvice</value>





<value>myAroundInterceptor</value>




</list>



</property>


</bean>

</beans>

该配置文件使用ProxyFactoryBean来生成代理对象,配置ProxyFactoryBean工厂bean时,指定了target属性,该属性值就是目标对象,该属性值为personTarget,指定代理的目标对象为personTarget。通过interceptorNames属性确定代理需要的拦截器,拦截器可以是普通的Advice,普通Advice将对目标对象的所有方法起作用,拦截器也可以是Advisor,Advisor是Advice和切面的组合,用于确定目标对象的哪些方法需要增加处理,以及怎样的处理。在上面的配置文件中,使用了三个拦截器,其中myAdvice、myAroundInterceptor都是普通Advice,它们将对目标对象的所有方法起作用。而runAdvisor则使用了正则表达式切面,匹配run方法,即该拦截器只对目标对象的run方法起作用。

下面是测试代理的主程序:

public class BeanTest

{


public static void main(String[] args)throws Exception

{




//创建Spring容器



ApplicationContext ctx = new FileSystemXmlApplication

Context(“bean.xml”);



//获取代理对象



Person p = (Person)ctx.getBean(“person”);



//执行info方法



p.info();



System.out.println(“===========================================”);



//执行run方法



p.run();


}

}

下面是程序的执行结果:

方法调用之前…

下面是方法调用的信息:

所执行的方法是:public abstract void lee.Person.info()

调用方法的参数是:null

目标对象是:lee.PersonImpl@b23210

调用方法之前: invocation对象:[invocation: method ‘info’, arguments

[]; target is of class [lee.PersonImpl]]

我的名字是: Wawa , 今年年龄为: 51

调用结束…

===========================================

方法调用之前…

下面是方法调用的信息:

所执行的方法是:public abstract void lee.Person.run()

调用方法的参数是:null

目标对象是:lee.PersonImpl@b23210

调用方法之前: invocation对象:[invocation: method ‘run’, arguments [

]; target is of class [lee.PersonImpl]]

我年老体弱,只能慢跑…

调用结束…

方法调用结束…

目标方法的返回值是 : null

目标方法是 : public abstract void lee.Person.run()

目标方法的参数是 : null

目标对象是 : lee.PersonImpl@b23210

程序的执行结果中一行“=”用于区分两次调用