Spring AOP

Spring AOP

Author 阿飞

@date2016-11-06

 

1.1 AOP介绍

    一般意义上的AOP请参考笔者的另一篇笔记《AOP.docx》

1.2 Spring AOP介绍

spring aop注解采用的是AspectJ的注解规则。

1.2.1几个基础概念(Aspect,Joinpoint,Pointcut…)

1. Aspect(切面):切面是一个抽象概念,它是由切入点(Pointcut)、增强(Advice)组成的一个模块,其实就是一个class,这个类中包含了横切(干扰)其它代码(目标对象)的内容。

2. Join point(连接点):指所有的方法。

3. Advice(通知/增强):通过切面在一个特定的连接点处执行的动作。

4. Pointcut(切入点):可以插入增强处理的连接点(通过配置指定),一个切入点包含两部分:

    1) the pointcut signature(切入点签名):其实就是声明一个普通的方法

    2) the pointcut expression(切入点表达式):指@Pointcut注解的参数,其参数是切入点表达式。如下示例:

    @Pointcut("execution(*transfer(..))")// the pointcut expression

    privatevoid anyOldTransfer() {}// the pointcut signature

5. Introduction(引入):

6. Target object(目标对象):指被增强处理的对象。

7. AOP proxy(AOP代理):指AOP框架创建的目标对象的代理对象。

8. Weaving(织入):是一个动作,代表将增强内容和原始目标对象进行结合的过程,织入的结果是产生了一个AOP代理对象。

1.2.2切入点表达式

我们以常用的execution切入点指示符为例,说明一下它的切入点表达式规则,如下:

 

execution(modifiers-pattern?ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)

            throws-pattern?)

 

括号内的各项含义如下:

 

1)modifiers-pattern 方法访问修饰符,可省略。

    2)ret-type-pattern  方法返回值类型,使用“*”代表任意返回值类型。

    3)declaring-type-pattern 方法所在类的完全限定性名称。

    4)name-pattern 方法名,使用“*”代表任意方法名。

5)param-pattern 方法参数列表,“*”代表匹配只含有一个任意类型参数的方法,“..”代表匹配含有任意多个参数的方法。可空,比如使用()代表匹配无参方法。

    6)throws-pattern  方法抛出的异常,“*”表示匹配任意一个异常,可省略。

 

注:切入点表达式的目的是匹配目标对象的特定方法。

1.2.3切入点指示符

spring切入点表达式有多种,但是常用的是execution。另外还有:within,target,agrs。可以从附件中资料获取详细信息。

1.2.4 AOP代理(AOP proxies)

    SpringAOP默认采用JDK动态代理作为AOP代理,来为目标接口生成代理对象。但是Spring也支持使用CGLIB代理,CGLIB代理是为类而非接口生成代理对象,它是一种运行时代理,不会对原始目标对象的字节码产生影响。

    在笔者的测试代码中有这么一个配置<aop:aspectj-autoproxy/>,不要被名字误解,该配置只是告诉Spring AOP使用AspectJ的注解功能,并不会使用AspectJ的AOP功能,即不会使用AspectJ去改变目标对象的字节码,这一点在Spring官方文档中有提及:


    注意到配置<aop:aspectj-autoproxy/>,此处是没有配置使用何种代理,那么默认使用JDK动态代理,要想强制使用CGLIB代理,可以在aop:aspectj-autoproxy元素中添加属性proxy-target-class="true",即,

<aop:aspectj-autoproxy proxy-target-class="true"/>

 

1.2.5增强(Advice)的种类

说明:1.2.5.1 – 1.2.5.5中的例子来自附录参考资料[1]

1.2.5.1 Beforeadvice

@Before("com.xyz.myapp.System.dataOperation()")

public voiddoAccessCheck() {

    // ...

}

 

在目标方法执行之前执行的操作,若要访问目标方法的参数,需要将Before advice中方法的第一个参数设置为JoinPoint类型的。

请注意:1、在Before advice中是无法执行目标方法的。

2、若目标方法未执行,则Before advice中的操作也不会被执行。

1.2.5.2 After returningadvice

@AfterReturning("com.xyz.myapp.System.dataOperation()")

public voiddoAccessCheck() {

    // ...

}

 

    Afterreturning advice中的操作只有在目标方法被正常执行或是不执行目标对象方法时才会被执行。

1.2.5.3 After throwingadvice

@AfterThrowing("com.xyz.myapp.System.dataOperation()")

public voiddoRecoveryActions() {

    // ...

}

 

After throwingadvice中的操作在目标方法正常执行时不会被执行,在不执行目标方法时也不会被执行,它只会在目标方法执行发生异常时才会被执行。

请注意:若是包裹目标方法的代码片段的异常被处理了,那么视为目标方法没有发生异常。

1.2.5.4 After(finally)advice

@After("com.xyz.myapp.System.dataOperation()")

public voiddoReleaseLock() {

    // ...

}

 

    Afteradvice中的操作无论何时都会被执行,不管是正常执行目标方法,还是执行发生异常,还是未执行目标方法。

1.2.5.5 Aroundadvice

@Around("com.xyz.myapp.System.businessService()")

public Object aroundM(ProceedingJoinPoint pjp) throws Throwable {

    // start stopwatch

    Object retVal = pjp.proceed();

    // stop stopwatch

    return retVal;

}

 

    Aroundadvice中的操作无论何时都会被执行。

1.2.6增强(Advice)的执行顺序

1.2.6.1 在同一个切面类中,增强的执行顺序

    在同一个切面类中,增强的执行顺序是(以在Around advice中调用目标方法为例):

 

1.2.6.2 在不同切面类中,对同一个目标方法增强的执行顺序

参考附件中资料。

 

1.3 Spring AOP开发

1.3.1使用注解开发

1.3.1.1基本开发步骤

前提:使用注解开发spring项目的基本配置要具备。

Step-1. 启用@AspectJ 注解支持(Enabling @AspectJ Support)

Step-2. 声明切面(Declaring an aspect)

Step-3. 声明切入点 (Declaring a pointcut)

Step-4. 声明增强(Declaring advice)

 

Step-1.启用@AspectJ 注解支持(Enabling@AspectJ Support)

    1)aspectjweaver.jar(1.6.8及之后)

    2)在spring配置文件中添加aop schema:

    <?xmlversion="1.0" encoding="UTF-8"?>

    <beansxmlns="http://www.springframework.org/schema/beans"

        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

        xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="

            http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd

            http://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- beandefinitions here -->

 

    </beans>

    3)在spring配置文件中配置 <aop:aspectj-autoproxy/>

   

Step-2.声明切面(Declaring an aspect)

    1)在类上面使用注解@Aspect,示例:

        @Aspect

        publicclass NotVeryUsefulAspect {

 

        }

    说明:同时要在xml文件或使用注解(比如@Component)使得切面类由spring负责实例化。

   

Step-3.声明切入点(Declaring a pointcut)

    1)在切面类中使用@Pointcut(切入点表达式)声明切入点,示例:

    @Pointcut("execution(*com.xyz.someapp..service.*.*(..))")

   public void businessService() {}

   

    2) 关于切入点表达式的规则参见本文档中的1.2.2 section。

 

Step-4.声明增强(Declaring advice)

    1)在切面类中使用@Before、@After等增强注解声明增强,示例:

    @Before("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")

   public void doAccessCheck() {

       // ...

    }

1.3.1.2代码示例

以下示例是笔者基于maven、spring mvc注解、spring aop注解的测试Demo中的核心部分:

 

pom.xml文件:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

   <modelVersion>4.0.0</modelVersion>

   <groupId>com.xtu.spring.aop</groupId>

   <artifactId>spring-aop-test-Maven</artifactId>

   <packaging>war</packaging>

   <version>0.0.1</version>

   <name>spring-aop-test-Maven Webapp</name>

   <url>http://maven.apache.org</url>

   <dependencies>

      <dependency>

         <groupId>junit</groupId>

         <artifactId>junit</artifactId>

         <version>3.8.1</version>

         <scope>test</scope>

      </dependency>

      <!-- Spring AOP核心包 aspectjweaver -->

      <dependency>

         <groupId>org.aspectj</groupId>

         <artifactId>aspectjweaver</artifactId>

         <version>1.8.0</version>

         <type>jar</type>

      </dependency>

      <dependency>

         <groupId>org.springframework</groupId>

         <artifactId>spring-expression</artifactId>

         <version>4.0.1.RELEASE</version>

      </dependency>

      <dependency>

         <groupId>org.springframework</groupId>

         <artifactId>spring-context-support</artifactId>

         <version>4.0.1.RELEASE</version>

      </dependency>

      <dependency>

         <groupId>org.springframework</groupId>

         <artifactId>spring-context</artifactId>

         <version>4.0.1.RELEASE</version>

      </dependency>

      <dependency>

         <groupId>org.springframework</groupId>

         <artifactId>spring-beans</artifactId>

         <version>4.0.1.RELEASE</version>

      </dependency>

      <dependency>

         <groupId>org.springframework</groupId>

         <artifactId>spring-core</artifactId>

         <version>4.0.1.RELEASE</version>

      </dependency>

     

      <dependency>

         <groupId>org.springframework</groupId>

         <artifactId>spring-web</artifactId>

         <version>4.0.1.RELEASE</version>

      </dependency>

      <dependency>

         <groupId>org.springframework</groupId>

         <artifactId>spring-webmvc</artifactId>

         <version>4.0.1.RELEASE</version>

      </dependency>

      <dependency>

            <groupId>org.springframework</groupId>

            <artifactId>spring-jdbc</artifactId>

            <version>4.0.1.RELEASE</version>

        </dependency>

      <dependency>

         <groupId>org.springframework</groupId>

         <artifactId>spring-test</artifactId>

         <version>4.0.1.RELEASE</version>

         <scope>test</scope>

      </dependency>

      <dependency>

            <groupId>commons-dbcp</groupId>

            <artifactId>commons-dbcp</artifactId>

            <version>1.4</version>

        </dependency>

        <dependency>

            <groupId>javax.servlet</groupId>

            <artifactId>servlet-api</artifactId>

            <version>2.5</version>

            <scope>provided</scope>

        </dependency>

   </dependencies>

   <build>

       <plugins>

          <plugin>

            <groupId>org.apache.tomcat.maven</groupId>

            <artifactId>tomcat7-maven-plugin</artifactId>

            <version>2.0</version>

            <configuration>

                <update>true</update>

                <charset>utf-8</charset>

                <uriEncoding>UTF-8</uriEncoding>

                <url>http://localhost/manager/text</url>

                <server>tomcat7</server>

                <port>80</port>

                <path>/</path>

            </configuration>

         </plugin>

      </plugins>

   </build>

 

</project>

 

 

web.xml文件:

<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

   xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"

   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"

   version="2.5">

   <display-name>spring mvc test</display-name>

  

   <listener>

      <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

   </listener>

  

   <servlet>

      <servlet-name>spring-aop</servlet-name>

      <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

      <load-on-startup>1</load-on-startup>

   </servlet>

   <servlet-mapping>

      <servlet-name>spring-aop</servlet-name>

      <url-pattern>/</url-pattern>

   </servlet-mapping>

  

   <welcome-file-list>

      <welcome-file>/index.jsp</welcome-file>

   </welcome-file-list>

 

</web-app>

 

spring-aop-servlet.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:context="http://www.springframework.org/schema/context"

   xmlns:mvc="http://www.springframework.org/schema/mvc"

   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/context 

                        http://www.springframework.org/schema/context/spring-context-3.1.xsd  

                        http://www.springframework.org/schema/mvc 

                        http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd

                        http://www.springframework.org/schema/aop

                        http://www.springframework.org/schema/aop/spring-aop.xsd">

<!-- 启用AOP,此处只是使用了aspectj的注解功能(这样我们便可以使用类似@Aspect,@Pointcut等注解了),具体AOP实现还是采用spring原本的运行时增强处理 -->

<!-- 此处没有配置使用何种代理,那么默认使用JDK动态代理 -->

    <!-- 要想强制使用CGLIB代理,可以在aop:aspectj-autoproxy元素中添加属性 proxy-target-class="true" -->

    <aop:aspectj-autoproxy/>

   <!-- 自动扫描该包,实现bean的自动创建 -->

   <context:component-scan base-package="com.xtu"/>

   <context:annotation-config />

  

   <!-- 启动SpringMVC的注解功能,完成请求和注解POJO的映射 -->

   <bean

      class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">

   </bean>

   <!-- 定义跳转的文件的前后缀,视图模式配置-->

   <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">

      <!-- 这里的配置我的理解是自动给后面action的方法return的字符串加上前缀和后缀,变成一个可用的url地址 -->

      <property name="prefix" value="/WEB-INF/" />

      <property name="suffix" value=".jsp" />

   </bean>

  

   <!-- 此配置甚为关键,没有annotation-driven,注解便不起作用 -->

   <mvc:annotation-driven></mvc:annotation-driven>

 

</beans>

 

切面类:

package com.xtu.aop.aspect;

 

import org.aspectj.lang.JoinPoint;

import org.aspectj.lang.ProceedingJoinPoint;

import org.aspectj.lang.annotation.After;

import org.aspectj.lang.annotation.Around;

import org.aspectj.lang.annotation.Before;

import org.aspectj.lang.annotation.Pointcut;

import org.springframework.stereotype.Component;

 

/**

 * 定义一个切面,名称为"Aspect"

 *

 * @author阿飞

 * @date 2016-11-03

 */

@Component

@org.aspectj.lang.annotation.Aspect

public class Aspect {

 

    /**

     * 定义切入点

     * 切入点包含两部分:切入点表达式,方法声明。

     * 通常方法名字一般代表切入点

     */

    @Pointcut("execution(public * com.xtu.aop.controller.*.*(..))")

    public void pointCutMethod() {

    }

 

    /**

     * 定义Before advice

     *

     * @Description:@Before注解中的参数既可以是一个已经定义好的切入点(如"pointCutMethod()"),

     * 也可以是一个切入点表达式(如"execution(public * com.xtu.aop.controller.*.*(..))"

     *

     * @Notice "before advice"只能在目标方法执行之前做一些事情,它不能传入ProceedingJoinPoint参数。

     *

     * @param joinPoint 当需要访问目标方法的参数时,第一个参数只能是JoinPoint类型

     */

    @Before("pointCutMethod()")

    public void beforeMethod(JoinPoint joinPoint) {

 

        System.out.println("beforeMethod...");

 

    }

   

    /**

     * 定义After advice

     *

     * @param joinPoint 当需要访问目标方法的参数时,第一个参数只能是JoinPoint类型

     */

    @After("pointCutMethod()")

    public void afterMethod(JoinPoint joinPoint) {

 

       System.out.println("afterMethod");

 

    }

   

    /**

     * 定义Around advice

     *

     * @param pJoinPoint 当需要执行目标方法时,第一个参数只能是ProceedingJoinPoint类型

     * @throws Throwable

     */

    @Around("pointCutMethod()")

    public void aroundMethod(ProceedingJoinPoint pJoinPoint) throws Throwable {

 

       System.out.println("aroundMethod guaguo...");

       pJoinPoint.proceed();

       System.out.println("aroundMethod guaguo proceed end");

    }

 

}

 

 

目标类:

package com.xtu.aop.controller;

 

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.RequestMapping;

 

/**

 * 目标类

 *

 * @author阿飞

 * @date 2016-10-23

 */

@Controller

@RequestMapping("/userController")

public class UserController {

 

    public UserController (){

       super();

       System.out.println("UserController create...");

    }

   

    @RequestMapping(value = "/getUser1")

    public String getUser1(){

       System.out.println("Method getUser1 in ...");

       return "userInfoList";

    }

   

}

 

 

附录

参考资料:

[1]. http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html

(Spring关于AOP的官方资料)

[2]. 轻量级javaEE 企业应用(第三版) 李刚 中的AOP部分

[3]. http://blog.csdn.net/chenmeng2192089/article/details/7970255

(spring对AOP的支持 JDK动态代理和CGLIB的区别)


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

笑看人生三百年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值