Spring的AOP

来源:互联网 发布:女生必知护肤技巧少女 编辑:程序博客网 时间:2024/06/06 02:19

AOP的术语与概念

1.横切关注点Cross-cutting concern
一个用户方法在一个应用程序中常被安排到各个处理流程中,这些方法在AOP的术语中称为横切关注点。
2.横切关注面Aspect
将散落在各个业务中的横切关注点收集起来,设计为各个独立可重用的类,这种类称为横切关注面(Aspect)。
3.连接点Join point
连接点是指程序中的某一个点。AspectJ中的连接点主要有如下形式:
-方法调用:方法被调用时。
-方法执行:方法体的内容执行时。
-构造方法调用:构造方法被调用时。
-构造方法执行:构造方法体的内容执行时。
-静态初始化部分执行:类中的静态部分内容初始化时。
-对象预初始化:主要指执行构造方法中的this()及super()时。
-对象初始化:在初始化一个类时。
-属性引用:引用属性时。
-属性设置:设置属性时。
-异常执行:异常执行时。
-通知执行:当一个AOP通知执行时。
连接点使用系统提供的关键字来表示。连接点不能单独存在,需要与一定的上下文结合。
4.切入点Pointcuts
切入点是连接点的集合,tasteful程序中需要注入Advice的位置的集合,指明Advice要在什么条件下被触发。
5.通知Advice
通知定义了切面中的实际实现,是指在定义好的切入点出执行的程序代码。在Spring中提供了四种通知类型:Before Advice、After Advice、Around Advice、Throw Advice。
6.拦截器Interceptor
拦截器用来实现对连接点进行拦截,从而在连接点前后加入自定义的切面模块功能。在大多数的Java的AOP框架中,基本上都是用拦截器来实现字段访问及方法调用的拦截。
7.目标对象Target object
目标对象是指在基本拦截器机制实现的AOP框架中,位于拦截器链上最末端的对象实例。一般情况下,拦截器末端包含的目标对象就是实际业务对象。

Spring1.x的AOP支持

Spring1.x的AOP支持,它主要针对不同类型的拦截使用XML配置文件通过代理来完成,有4种通知:前置通知、后置通知、环绕通知、异常通知。
1.前置通知 Before Advice
在目标对象方法执行前被调用,应用前置通知需先设计一个借口,然后编写这个接口的实现类,接着编写前置通知的逻辑代码,该代码需实现MethodBeforeAdvice接口,并覆盖其before()方法,该逻辑代码主要编写一些需要前置的服务,最后通过配置XML文件来实现AOP的前置通知。
(1)定义接口

package org.aop.interfaces;public interface IHello {    public void sayHello1();    public void sayHello2();    public void sayHello3();}

(2)接口实现类

package org.aop.interfaces.impl;import org.aop.interfaces.IHello;public class Hello implements IHello{    @Override    public void sayHello1() {        // TODO Auto-generated method stub        System.out.print("Hello~1");    }    @Override    public void sayHello2() {        // TODO Auto-generated method stub        System.out.print("Hello~2");    }    @Override    public void sayHello3() {        // TODO Auto-generated method stub        System.out.print("Hello~3");    }}

(3)实现前置通知类
该类需实现MethodBeforeAdvice接口,并覆盖其before(方法)。

package org.aop.advice;import java.lang.reflect.Method;import org.springframework.aop.MethodBeforeAdvice;public class AdviceBeforeHello implements MethodBeforeAdvice{    @Override    public void before(Method arg0, Object[] arg1, Object arg2)            throws Throwable {        // TODO Auto-generated method stub        System.out.print("前置验证用户...");    }}

(4)配置前置通知
修改applicationContext.xml文件,添加如下内容

<!-- 注册前置通知类 -->    <bean id="beforeAdvice" class="org.aop.advice.AdviceBeforeHello"/>    <!-- 注册代理类 -->    <bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">        <!-- 指定应用的接口 -->        <property name="proxyInterfaces">            <value>org.aop.interfaces.IHello</value>        </property>        <!-- 目标对象,本例中为Hello对象 -->        <property name="target" ref="hello"></property>        <!-- 应用的前置通知,拦截去名称 -->        <property name="interceptorNames">            <list>                <value>beforeAdvice</value>            </list>        </property>    </bean>    <!-- 注册接口实现类 -->    <bean id="hello" class="org.aop.interfaces.impl.Hello"/>

(5)测试程序

package test;import org.aop.interfaces.IHello;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class Test {    public static void main(String[] args) {        // TODO Auto-generated method stub        //ClassPathXmlApplicationContext会从ClassPath中寻找支配文件applicationContext.xml        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");        IHello hello = (IHello) ac.getBean("proxy");        hello.sayHello1();        hello.sayHello2();        hello.sayHello3();    }}

运行结果:
这里写图片描述

2.后置通知 AfterAdvice
后置通知与前置通知相似,不同的是在目标对象的方法执行完成后才被调用,后置通知的逻辑代码需实现AfterReturningAdvice接口,并覆盖其afterReturning()方法。
(1)接口为IHello,接口实现类为Hello(见上文)。
(2)实现后置通知类

package org.aop.advice;import java.lang.reflect.Method;import org.springframework.aop.AfterReturningAdvice;public class AdviceAfterHello implements AfterReturningAdvice {    @Override    public void afterReturning(Object arg0, Method arg1, Object[] arg2,            Object arg3) throws Throwable {        // TODO Auto-generated method stub        System.out.print("后置验证用户...");    }}

(3)修改配置文件

<!-- 注册后置通知类 -->    <bean id="afterAdvice" class="org.aop.advice.AdviceAfterHello"/>    <!-- 注册代理类 -->    <bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">        <!-- 指定应用的接口 -->        <property name="proxyInterfaces">            <value>org.aop.interfaces.IHello</value>        </property>        <!-- 目标对象,本例中为Hello对象 -->        <property name="target" ref="hello"></property>        <!-- 应用的后置通知,拦截去名称 -->        <property name="interceptorNames">            <list>                <value>afterAdvice</value>            </list>        </property>    </bean>    <!-- 注册接口实现类 -->    <bean id="hello" class="org.aop.interfaces.impl.Hello"/>

(4)测试运行
这里写图片描述

3.环绕通知 Around Advice
环绕通知相当与前置通知和后置通知的结合使用,建立一个环绕通知需实现MethodInterceptor接口,并覆盖其invoke()方法。
(1)接口为IHello,接口实现类为Hello(见上文)。
(2)实现环绕通知类

package org.aop.advice;import org.aopalliance.intercept.MethodInterceptor;import org.aopalliance.intercept.MethodInvocation;public class AdviceAroundHello implements MethodInterceptor {    @Override    public Object invoke(MethodInvocation arg0) throws Throwable {        // TODO Auto-generated method stub        System.out.print("前置验证用户...");        Object result = null;        try{            result = arg0.proceed();        } finally{            System.out.print("后置验证用户");        }        return result;    }}

该程序调用了methodInvocation方法的preceed方法,即调用了目标对象Hello的sayHello*()等一些方法,在这个方法后面分别添加验证。
(3)修改配置文件

<!-- 注册环绕通知类 -->    <bean id="rondAdvice" class="org.aop.advice.AdviceAroundHello"/>    <!-- 注册代理类 -->    <bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">        <!-- 指定应用的接口 -->        <property name="proxyInterfaces">            <value>org.aop.interfaces.IHello</value>        </property>        <!-- 目标对象,本例中为Hello对象 -->        <property name="target" ref="hello"></property>        <!-- 应用的环绕通知,拦截去名称 -->        <property name="interceptorNames">            <list>                <value>rondAdvice</value>            </list>        </property>    </bean>    <!-- 注册接口实现类 -->    <bean id="hello" class="org.aop.interfaces.impl.Hello"/>

(4)测试运行
这里写图片描述

4.异常通知 Throw Advice
异常通知就是程序发送异常时执行相关的服务。为造成异常发送,可以人为抛出异常用于演示。
(1)定义接口

package org.aop.interfaces;public interface IHelloException {    public void sayHello1()throws Throwable;    public void sayHello2()throws Throwable;    public void sayHello3()throws Throwable;}

(2)实现异常通知类

package org.aop.interfaces.impl;import org.aop.interfaces.IHelloException;public class HelloException implements IHelloException {    @Override    public void sayHello1() throws Throwable {        // TODO Auto-generated method stub        System.out.println("SayHello1...");        throw new Exception("异常");    }    @Override    public void sayHello2() throws Throwable{        // TODO Auto-generated method stub        System.out.print("SayHello1...");        throw new Exception("异常");    }    @Override    public void sayHello3() throws Throwable{        // TODO Auto-generated method stub        System.out.print("SayHello1...");        throw new Exception("异常");    }}

(3)实现异常通知类

package org.aop.advice;import org.springframework.aop.ThrowsAdvice;public class AdviceThrow implements ThrowsAdvice {    public void afterThrowing(Throwable throwable){        System.out.println("有异常抛出。。。。");    }}

(4)修改配置文件

<!-- 注册异常通知类 -->    <bean id="exceptionAdvice" class="org.aop.advice.AdviceThrow"/>    <!-- 注册代理类 -->    <bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">        <!-- 指定应用的接口 -->        <property name="proxyInterfaces">            <value>org.aop.interfaces.IHelloException</value>        </property>        <!-- 目标对象,本例中为Hello对象 -->        <property name="target" ref="hello"></property>        <!-- 应用的异常通知,拦截去名称 -->        <property name="interceptorNames">            <list>                <value>exceptionAdvice</value>            </list>        </property>    </bean>    <!-- 注册接口实现类 -->    <bean id="hello" class="org.aop.interfaces.impl.HelloException"/>

(5)测试运行

package test;import org.aop.interfaces.IHello;import org.aop.interfaces.IHelloException;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class Test {    public static void main(String[] args) {        // TODO Auto-generated method stub        //ClassPathXmlApplicationContext会从ClassPath中寻找支配文件applicationContext.xml        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");        IHelloException hello = (IHelloException) ac.getBean("proxy");        try {            hello.sayHello1();            hello.sayHello2();            hello.sayHello3();        } catch (Throwable e) {            // TODO Auto-generated catch block            System.out.print(e);        }    }}

运行结果:
这里写图片描述
5.NameMatchMethodPointAdvisor
可通过NameMatchMethodPointcutAdvisor,对指定的方法调用通知。
(1)接口为IHello,实现类为Hello,调用通知类为AdviceBeforeHello;
(2)修改配置文件

<!-- 注册前置通知类 -->    <bean id="beforeAdvice" class="org.aop.advice.AdviceBeforeHello"/>    <!-- 注册NameMathMethodPointAdvisor的Bean -->    <bean id="helloAdvice" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">        <!-- 配置要执行通知的方法 -->        <property name="mappedName" value="*2"></property>        <!-- 指定使用的通知类,此处为前置通知 -->        <property name="advice" ref="beforeAdvice"></property>    </bean>    <!-- 注册代理类 -->    <bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">        <!-- 指定应用的接口 -->        <property name="proxyInterfaces">            <value>org.aop.interfaces.IHello</value>        </property>        <!-- 目标对象,本例中为Hello对象 -->        <property name="target" ref="hello"></property>        <!-- 应用的异常通知,拦截去名称 -->        <property name="interceptorNames">            <list>                <value>helloAdvice</value>            </list>        </property>    </bean>    <!-- 注册接口实现类 -->    <bean id="hello" class="org.aop.interfaces.impl.Hello"/>

(3)测试运行
这里写图片描述

6.RegexpMethodPointAdvisor
实现同NameMatchMethodPointcutAdvisor,对指定方法,调用通知。
(1)接口为IHello,实现类为Hello,调用通知类为AdviceBeforeHello;
(2)修改配置文件

<!-- 注册前置通知类 -->    <bean id="beforeAdvice" class="org.aop.advice.AdviceBeforeHello"/>    <!-- 注册NameMathMethodPointAdvisor的Bean -->    <bean id="reExpAdvice" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">        <!-- 配置要执行通知的方法 -->        <property name="pattern" value=".*2"></property>        <!-- 指定使用的通知类,此处为前置通知 -->        <property name="advice" ref="beforeAdvice"></property>    </bean>    <!-- 注册代理类 -->    <bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">        <!-- 指定应用的接口 -->        <property name="proxyInterfaces">            <value>org.aop.interfaces.IHello</value>        </property>        <!-- 目标对象,本例中为Hello对象 -->        <property name="target" ref="hello"></property>        <!-- 应用的异常通知,拦截去名称 -->        <property name="interceptorNames">            <list>                <value>reExpAdvice</value>            </list>        </property>    </bean>    <!-- 注册接口实现类 -->    <bean id="hello" class="org.aop.interfaces.impl.Hello"/>

(3)测试运行
这里写图片描述

Spring2.x的AOP支持

在Spring2.x除了支持Spring1.x的AOP支持外,还提供了如下两种实现AOP方式。
-基于XML的配置,使用基于Schema的XML配置来完成AOP,并且Advice不用再实现任何其他特定接口。
-使用JDK5的注释来完成AOP的实现,只需一个简单的标签即可完成AOP的整个过程。
1.基于XML Schema的前置通知
改写Spring1.x的前置通知例子,完成应用XML Scheme的前置通知。
(1)前置通知逻辑代码

package org.aop.advice;public class AdviceBeforeHello{    public void before(){        System.out.print("前置验证用户...");    }}

(2)修改Spring核心配置文件application.xml

<?xml version="1.0" encoding="UTF-8"?><beans    xmlns="http://www.springframework.org/schema/beans"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xmlns:p="http://www.springframework.org/schema/p"    **xmlns:aop="http://www.springframework.org/schema/aop"**    xsi:schemaLocation="http://www.springframework.org/schema/beans     http://www.springframework.org/schema/beans/spring-beans-3.1.xsd    **http://www.springframework.org/schema/aop    http://www.springframework.org/schema/aop/spring-aop-3.1.xsd**">    <!-- 注册前置通知类 -->    <bean id="beforeAdvice" class="org.aop.advice.AdviceBeforeHello"/>    **<!-- 定义aop:config -->    <aop:config>        <aop:pointcut id="beforePointcut" expression="execution(* org.aop.interfaces.IHello.*(..))"/>        <aop:aspect id="before" ref="beforeAdvice">            <aop:before pointcut-ref="beforePointcut"  method="before"/>        </aop:aspect>    </aop:config>**    <!-- 注册接口实现类 -->    <bean id="hello" class="org.aop.interfaces.impl.Hello"/></beans>

在配置文件Beans的属性中加入了schema的命名空间:

xmlns:aop="http://www.springframework.org/schema/aop"http://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-3.1.xsd">

然后,配置<aop:config>标签来配置一个aop片段,其配置规则为:

<aop:config>    <aop:pointcut id="pointcutName" expression="匹配的正则表达式"/>    <aop:advisor id="AdvisorName" pointcut-ref="pointcut的Bean Id">    <aop:aspect id="aspectName" ref="songBean">        <aop:adviceType pointcut-ref="应用的poincutName"  method="methodName"/>    </aop:aspect></aop:config>

配置内容:
-<aop:pointcut>:用来配置AOP的正则表达式,其他标签可以直接根据标签的id属性来引用。
注:在配置表达式是,“*”与后面的接口间要有一个空格。
-<aop:advisor>:用来配置AOP的切面,与pointcut相同,可以直接使用advisor的id来引用该切面。
-<aop:aspect>:用来配置一个切面,ref指定要应用的通知类,<aop:adviceType>用来配置通知的类型,可以是<aop:before>前置通知,<aop:after>后置通知,<aop:around>环绕通知,<aop:after-throwing>异常通知,pointcut-ref属性来引用一个pointcut,method属性用来指定要应用的通知类中的方法。
(3)编写测试类

package test;import org.aop.interfaces.IHello;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class Test {    public static void main(String[] args) {        // TODO Auto-generated method stub        //ClassPathXmlApplicationContext会从ClassPath中寻找支配文件applicationContext.xml        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");        IHello hello = (IHello) ac.getBean("hello");        hello.sayHello1();        hello.sayHello2();        hello.sayHello3();    }}

2.基于XML Schema的后置通知
基本思路与前置通知一样。

3.基于XML Schema的环绕通知
基本思路与前置通知一样。
其中后置通知类为:

package org.aop.advice;import org.aspectj.lang.ProceedingJoinPoint;public class AdviceAroundHello{    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {        // TODO Auto-generated method stub        System.out.print("前置验证用户...");        Object result = joinPoint.proceed();        System.out.print("后置验证用户");        return result;    }}

4.基于XML Schema的异常通知
思路与前置通知一样。

5.基于Annotation的前置通知
Spring2.x结合JDK 5及以上版本,提供了Annotation设置AOP通知,对AOP实现大大简化。
(1)修改通知类

package org.aop.advice;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;@Aspectpublic class AdviceBeforeHello{    @Before("execution(* org.aop.interfaces.IHello.*(..))")    public void before(){        System.out.print("前置验证用户...");    }}

使用@Aspect标签表示该类是一个Aspect
使用@Before标签表示该方法是一个前置通知,里面的”execution(* org.aop.interfaces.IHello.*(..))”为正则表达式。

6..基于Annotation的后置通知、环绕通知、异常通知
思路与前置通知一样。
与@Before相对应改为:@AfterReturning,@Around,@AfterThrowing。

0 0
原创粉丝点击