SpringAOP配置详解带实例(基于XML配置文件方式)

来源:互联网 发布:iphone投影到mac 编辑:程序博客网 时间:2024/05/20 08:44

AOP:即面向切面编程,是一种编程思想,OOP的延续。在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等等。耐心看完本篇文章,会对理解AOP有一定的帮助。

1.了解AOP相关术语(如有疑惑可先跳过该部分,等理解了AOP配置再回头来看)

1.通知(Advice):在切面的某个特定的连接点上执行的动作,即当程序到达一个执行点后会执行相对应的一段代码。也称为增强处理。通知共有如下5种类型:    A 前置通知(Before advice):在某连接点(JoinPoint)之前执行的通知。xml中在<aop:aspect>里面使用<aop:before>    元素进行声明    B 后置通知(After advice) :当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。xml中在<aop:aspect>里    面使用<aop:after>元素进行声明    C 返回通知(After return advice) :在某连接点正常执行完成后,可以取到该连接点的返回值的通知。xml中在    <aop:aspect>里面使用<after-returning>元素进行声明    D 环绕通知(Around advice):包围一个连接点的通知,可以在调用方法前后完成自定义行为。xml中在<aop:aspect>里面使    用<aop:around>元素进行声明    E 抛出异常后通知(After throwing advice):在方法抛出异常退出时执行的通知。xml中在<aop:aspect>里面使用    <aop:after-throwing>元素进行声明    通知执行顺序:前置通知→环绕通知连接点之前→连接点执行→环绕通知连接点之后→返回通知→后置通知                                                          →(如果发生异常)异常通知→返回通知→后置通知2.连接点(JoinPoint):程序执行的某个特定位置,例如类初始化前,类初始化后,方法执行前,方法执行后等,Spring只支持方法的连接点,即方法执行前,方法执行后,方法抛出异常时。3.切入点(Pointcut):切入点是一个筛选连接点的过程。因为在你的工程中可能有很多连接点,你只是想让其中几个,在调用这几个方法之前、之后或者抛出异常时干点什么,那么就用切入点来定义这几个方法,让切点来筛选连接点,选中那几个你想要的方法。4.切面(Aspect):切面通常是指一个类,是通知和切入点的结合。到这里会发现连接点就是为了让你好理解切点产生的。通俗来说,切面的配置可以理解为:什么时候在什么地方做什么事。切入点说明了在哪里干(指定到方法),通知说明了什么时候干什么(什么时候通过before,after,around等指定)。5.引入(Introduction):允许一个切面声明一个通知对象实现指定接口,并且提供了一个接口实现类来代表这些对象。引入的定义使用<aop:aspect>中的<aop:declare-parents>元素。(例子:通过JMX输出统计信息)6.目标对象(Target Object):包含连接点的对象,也被称作被通知或被代理对象。7.AOP代理(AOP Proxy):AOP框架创建的对象,代理就是目标对象的加强。在Spring AOP中有两种代理方式,JDK动态代理和CGLIB代理。默认情况下,TargetObject实现了接口时,则采用JDK动态代理,强制使用CGLIB代理需要将 <aop:config>的 proxy-target-class属性设为true。8.织入(Weaving):把切面应用到目标对象来创建新的代理对象的过程,织入一般发生在如下几个时机:    (1)编译时:当一个类文件被编译时进行织入,这需要特殊的编译器才可以做的到,例如AspectJ的织入编译器    (2)类加载时:使用特殊的ClassLoader在目标类被加载到程序之前增强类的字节代码    (3)运行时:切面在运行的某个时刻被织入,SpringAOP就是以这种方式织入切面的,原理应该是使用了JDK的动态代理技术

2.切面配置方式实例

2.1 在pom.xml添加Spring相关的依赖

<properties>    <!-- Spring -->    <spring.version>4.1.3.RELEASE</spring.version></properties><dependencies>    <!-- Spring Begin -->    <dependency>        <groupId>org.springframework</groupId>        <artifactId>spring-context</artifactId>        <version>${spring.version}</version>    </dependency>    <dependency>        <groupId>org.springframework</groupId>        <artifactId>spring-beans</artifactId>        <version>${spring.version}</version>    </dependency>    <dependency>        <groupId>org.springframework</groupId>        <artifactId>spring-webmvc</artifactId>        <version>${spring.version}</version>    </dependency>    <dependency>        <groupId>org.springframework</groupId>        <artifactId>spring-jdbc</artifactId>        <version>${spring.version}</version>    </dependency>    <dependency>        <groupId>org.springframework</groupId>        <artifactId>spring-aspects</artifactId>        <version>${spring.version}</version>    </dependency>    <dependency>        <groupId>org.springframework</groupId>        <artifactId>spring-test</artifactId>        <version>${spring.version}</version>    </dependency>    <!-- Spring End --></dependencies>

2.2 配置web.xml 配置Spring监听器

<listener>    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener><!-- spring配置文件的配置 --><context-param>    <param-name>contextConfigLocation</param-name>    <param-value>classpath:applicationContext.xml</param-value></context-param>

2.3 业务相关类

public interface HelloWorldService {    /**     *      * 功能描述: <br>     * 〈HelloWorldService层〉     *     * @since [1.0.0](可选)     */    public String SayHelloWorld();}public class HelloWorldServiceImpl implements HelloWorldService {    @Override    public String SayHelloWorld() {        System.out.println("Hello World~");        return "执行业务方法的返回值为:Hello World";    }}

2.4 切面类

public class HelloWorldAspect {    public void beforeAdvice() {        System.out.println("前置通知执行了");    }    public void afterAdvice() {        System.out.println("后置通知执行了");    }    public void afterReturnAdvice(String result) {        System.out.println("返回通知执行了" + "运行业务方法返回的结果为" + result);    }    public String aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {        String result = "";        try {            System.out.println("环绕通知开始执行了");            long start = System.currentTimeMillis();            result = (String) proceedingJoinPoint.proceed();            long end = System.currentTimeMillis();            System.out.println("环绕通知执行结束了");            System.out.println("执行业务方法共计:" + (end - start) + "毫秒。");        } catch (Throwable e) {        }        return result;    }    public void throwingAdvice(JoinPoint joinPoint, Exception e) {        StringBuffer stringBuffer = new StringBuffer();        stringBuffer.append("异常通知执行了.");        stringBuffer.append("方法:").append(joinPoint.getSignature().getName()).append("出现了异常.");        stringBuffer.append("异常信息为:").append(e.getMessage());        System.out.println(stringBuffer.toString());    }}

2.5 配置文件配置切面等

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd            http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd        http://www.springframework.org/schema/aop        http://www.springframework.org/schema/aop/spring-aop.xsd">    <!-- 配置切面Bean -->    <bean id="helloWorldService" class="com.spring.aop.service.impl.HelloWorldServiceImpl" />    <bean id="helloWorldAspectBean" class="com.spring.aop.advisor.HelloWorldAspect" />    <!-- 配置一个切面 -->    <aop:config>        <aop:aspect id="helloWorldAspect" ref="helloWorldAspectBean">            <aop:pointcut id="helloWorldServicePointcut" expression="execution(* com.spring.aop.service.*.*(..))" />            <!-- 配置前置通知 -->            <aop:before pointcut-ref="helloWorldServicePointcut" method="beforeAdvice" />            <!-- 配置前置通知 -->            <aop:after pointcut-ref="helloWorldServicePointcut" method="afterAdvice" />            <!-- 配置后置返回通知 -->            <aop:after-returning pointcut-ref="helloWorldServicePointcut" method="afterReturnAdvice" returning="result" />            <!-- 配置环绕通知 -->            <aop:around pointcut-ref="helloWorldServicePointcut" method="aroundAdvice" />            <!-- 异常通知 -->            <aop:after-throwing pointcut-ref="helloWorldServicePointcut" method="throwingAdvice" throwing="e" />        </aop:aspect>    </aop:config></beans>

2.6 测试类测试

@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(locations = { "classpath:applicationContext.xml" })public class SpringAopTest extends AbstractJUnit4SpringContextTests {    @Resource(name = "helloWorldService")    private HelloWorldService helloWorldService;    @Test    public void testHelloWord() throws Exception {        helloWorldService.SayHelloWorld();    }}

2.7 输出结果

前置通知执行了环绕通知开始执行了Hello World~环绕通知执行结束了执行业务方法共计:0毫秒。返回通知执行了运行业务方法返回的结果为执行业务方法的返回值为:Hello World后置通知执行了

2.8 修改业务方法 抛出异常 查看测试结果

public class HelloWorldServiceImpl implements HelloWorldService {    @Override    public String SayHelloWorld() {        System.out.println("Hello World~");        System.out.println(1 / 0);        return "执行业务方法的返回值为:Hello World";    }}输出结果为:前置通知执行了环绕通知开始执行了Hello World~异常通知执行了.方法:SayHelloWorld出现了异常.异常信息为:/ by zero返回通知执行了运行业务方法返回的结果为后置通知执行了

2.9 解读配置文件
SpringAop配置解读

切入点表达式可参考: 切入点表达式

3.为切点单独配置通知方式

3.1 配置文件

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd            http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd        http://www.springframework.org/schema/aop        http://www.springframework.org/schema/aop/spring-aop.xsd">    <!-- IOC创建helloWorldService -->    <bean id="helloWorldService" class="com.spring.aop.service.impl.HelloWorldServiceImpl" />    <!-- 配置切面Bean -->    <bean id="helloWorldAspectBean" class="com.spring.aop.advisor.HelloWorldAspect" />    <aop:config>        <aop:pointcut id="helloWorldServicePointcut" expression="execution(* com.spring.aop.service.*.*(..))" />        <aop:advisor pointcut-ref="helloWorldServicePointcut" advice-ref="helloWorldAspectBean"/>    </aop:config></beans>

3.2 通知方法 实现MethodInterceptor接口

public class HelloWorldAspect implements MethodInterceptor {    @Override    public Object invoke(MethodInvocation methodInvocation) throws Throwable {        // 获取被增强对象参数列表        Object object = methodInvocation.getArguments();        // 获取被增强对象的方法        Method method = methodInvocation.getMethod();        // 继续执行业务方法        System.out.println("为切点单独配置通知 业务方法执行前");        Object result = methodInvocation.proceed();        System.out.println("为切点单独配置通知 业务方法执行后");        return result;    }}

3.3 执行测试

输出结果为:为切点单独配置通知 业务方法执行前Hello World~为切点单独配置通知 业务方法执行后

4.切入点执行顺序

在配置切面和通知的时候,可以指定order参数,来区分切入执行先后顺序,order的值越小,说明越先被执行。<aop:aspect id="helloWorldAspect" ref="helloWorldAspectBean" order="0">

5.注意事项

1.在Spring的配置文件中,所有的切面和通知都必须定义在<aop:config>元素内部。(一个application context可以包含多个<aop:config>)。 一个<aop:config>可以包含pointcut,advisor和aspect元素(注意这三个元素必须按照这个顺序进行声明)。2.当我们使用<aop:config/>方式进行配置时,可能与Spring的自动代理方式相互冲突,因此,建议要么全部使用<aop:config/>配置方式,要么全部使用自动代理方式,不要把两者混合使用。

以上观点仅代表个人理解 如有错误 欢迎纠正~

1 0