zoukankan      html  css  js  c++  java
  • spring aop介绍和示例

    参考:《Spring in Action》

    一、AOP介绍

    AOP是Aspect Oriented Programming的缩写,意思是面向切面编程。

    应用中有一些功能使用非常普遍,比如事务、安全、缓存,它们和业务没有直接关系,但是往往要嵌入到应用的业务逻辑当中,这些功能被称为横切关注点。而将这些横切关注点与业务逻辑相分离、减少之间的耦合性,就是面向切面编程(AOP)所要做的工作。

    下面是AOP的常用术语:

    1、切面(Aspect)

    相似的横切关注点可以被集中模块化为一个特殊的类,这个类被称为切面。

    例如应用里dao层的一些方法里分布着很多事务代码,现在将这些代码抽取出来,集中到一个事务处理类中(切面),dao层之后可以只关注自己的核心功能,而将事务部分移交给事务处理类来做。

    切面是通知和切点结合,它们共同定义了切面的全部内容:它做什么、在何时做、在何处做。

    3、通知(Advice)

    切面要做的工作被称为通知,不同类型的通知又决定了切面的执行时机。

    spring切面可以应用5种类型的通知:

    before:在方法被调用之前调用通知
    after:在方法完成之后调用通知,无论方法执行是否成功
    after-returning:在方法成功执行之后调用通知
    after-throwing:在方法抛出异常后调用通知
    around:通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为
    

    4、连接点(Joinpoint)

    连接点是在应用执行过程中能够插入通知的一个点,这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。

    5、切点(Poincut)

    切点的定义会匹配通知要织入的一个或多个连接点,如果通知定义了切面"做什么"和"在何时做",切点就定义了切面"在何处做"。切点的内容可以是类和方法的名称,也可以是一段正则表达式,有的AOP框架还支持运行时创建的动态切点。

    6、织入(Weaving)

    织入是通知在指定的连接点被插入到应用中(目标类)的过程,这个连接点就叫做切点,而被织入的某类通知就叫做切面。

    在目标类的生命周期里有多个时机可以进行织入:

    编译期:通知在目标类编译时被织入,这种方式需要特殊的编译器,AspectJ的织入编译器就是用这种方式
    类加载期:通知在目标类加载到JVM时被织入,这种方式需要特殊的类加载器,AspectJ5的LTW支持这种方式
    运行期:通知在应用运行的某个时刻被织入,一般情况在织入时AOP容器会为目标对象动态地创建一个代理对象,spring aop就是用这种方式
    

    二、spring对AOP的支持

    1、目前主流AOP框架

    AspectJ
    JBoss AOP
    Spring AOP
    

    Spring AOP包含了AspectJ的一些功能,能满足许多应用的切面需求,不过和AspectJ相比功能较弱,有许多类型的切点不支持。

    2、spring提供的AOP支持

    基于代理的经典AOP
    纯POJO切面
    @AspectJ注解驱动的切面
    注入式AspectJ切面
    

    前三种属于Spring AOP,因为是基于代理的,所以对AOP的支持只有方法拦截,如果需要构造器拦截和属性拦截,就需要用第四种AspectJ

    第一种经典AOP已经不常用了,实际情况中我们使用最多的是xml风格的纯POJO切面和注解风格的@AspectJ切面。

    之后书本里有更详细的说明,这里就不写了,举两个栗子。

    在之前配置hibernate访问mysql这篇里所附代码的基础上进行测试

    三、xml风格的纯POJO切面

    1、添加jar包引用,修改pom.xml文件,加入:

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>${spring.version}</version>
    </dependency>
     
    <!-- aop -->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjrt</artifactId>
        <version>1.7.4</version>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.7.4</version>
    </dependency>
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.1</version>
    </dependency>
    

    2、在"src/main/java"代码文件夹的"org.xs.demo1"的包下新建"aopAdvice.java"通知类,内容为:

    package org.xs.demo1;
     
    import org.aspectj.lang.ProceedingJoinPoint;
     
    public class aopAdvice {
         
        public void doBefore() {
            System.out.println("aop——before");
        }
         
        public void doAfter() {
            System.out.println("aop——after");
        }
         
        public void doAfterReturning() {
            System.out.println("aop——after-returning");
        }
         
        public void doAfterThrowing() {
            System.out.println("aop——after-throwing");
        }
         
        //joinpoint参数是被通知的方法
        //如果被通知的方法有返回值,在环绕方法里面也需要返回
        public Object doAround(ProceedingJoinPoint joinpoint) {
            Object result = null;
            try {
                System.out.println("aop——around-before");
                 
                //调用被通知的方法
                result = joinpoint.proceed();
                 
                System.out.println("aop——around-after");
            } catch (Throwable e) {
                System.out.println("aop——gg");
            }
            return result;
        }
    }
    

    3、在"src/main/resources"代码文件夹中新建文件"spring-context-aop.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:context="http://www.springframework.org/schema/context" 
        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.2.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context-3.2.xsd
            http://www.springframework.org/schema/aop 
            http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">
                
        <description>AOP Configuration</description>
         
        <!-- 要织入的通知(切面) -->
        <bean id="aopAdvice" class="org.xs.demo1.aopAdvice"></bean>
         
        <!-- AOP配置 -->
        <aop:config>
            <!-- 定义切面 -->
            <aop:aspect ref="aopAdvice">
                <!-- 定义切点 -->
                <aop:pointcut id="performance" expression="execution(* org.xs.demo1.testDao.getList(..))" />
                <!-- 定义通知 -->
                <aop:before pointcut-ref="performance" method="doBefore" />
                <aop:after pointcut-ref="performance" method="doAfter" />
                <aop:after-returning pointcut-ref="performance" method="doAfterReturning" />
                <aop:after-throwing pointcut-ref="performance" method="doAfterThrowing" />
                <aop:around pointcut-ref="performance" method="doAround" />
            </aop:aspect>
        </aop:config>
    </beans>
    

    4、运行测试(访问localhost:8080/demo1/hello/mysql)

    在Console窗口会输出信息

    四、注解风格的@AspectJ切面

    1、在"src/main/java"代码文件夹的"org.xs.demo1"的包下新建"aopAdvice2.java"通知类,内容为:

    package org.xs.demo1;
     
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.AfterThrowing;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
     
    @Aspect
    public class aopAdvice2 {
         
        @Pointcut("execution(* org.xs.demo1.testDao.getList(..))")
        public void performance() { }
         
        @Before("performance()")
        public void doBefore() {
            System.out.println("aop——before");
        }
         
        @After("performance()")
        public void doAfter() {
            System.out.println("aop——after");
        }
         
        @AfterReturning("performance()")
        public void doAfterReturning() {
            System.out.println("aop——after-returning");
        }
         
        @AfterThrowing("performance()")
        public void doAfterThrowing() {
            System.out.println("aop——after-throwing");
        }
         
        //joinpoint参数是被通知的方法
        //如果被通知的方法有返回值,在环绕方法里面也需要返回
        @Around("performance()")
        public Object doAround(ProceedingJoinPoint joinpoint) {
            Object result = null;
            try {
                System.out.println("aop——around-before");
                 
                //调用被通知的方法
                result = joinpoint.proceed();
                 
                System.out.println("aop——around-after");
            } catch (Throwable e) {
                System.out.println("aop——gg");
            }
            return result;
        }
    }
    

    2、修改"spring-context-aop.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:context="http://www.springframework.org/schema/context" 
        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.2.xsd
            http://www.springframework.org/schema/context 
            http://www.springframework.org/schema/context/spring-context-3.2.xsd
            http://www.springframework.org/schema/aop 
            http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">
                
        <description>AOP Configuration</description>
         
        <!-- 要织入的通知(切面) -->
        <!-- <bean id="aopAdvice" class="org.xs.demo1.aopAdvice"></bean> -->
         
        <!-- AOP配置 -->
        <!-- <aop:config> -->
            <!-- 定义切面 -->
            <!-- <aop:aspect ref="aopAdvice"> -->
                <!-- 定义切点 -->
                <!-- <aop:pointcut id="performance" expression="execution(* org.xs.demo1.testDao.getList(..))" /> -->
                <!-- 定义通知 -->
                <!-- <aop:before pointcut-ref="performance" method="doBefore" />
                <aop:after pointcut-ref="performance" method="doAfter" />
                <aop:after-returning pointcut-ref="performance" method="doAfterReturning" />
                <aop:after-throwing pointcut-ref="performance" method="doAfterThrowing" />
                <aop:around pointcut-ref="performance" method="doAround" />
            </aop:aspect>
        </aop:config> -->
         
        <!-- 启动@AspectJ支持 -->
        <aop:aspectj-autoproxy/>
         
        <!-- 指定自动搜索Bean组件,自动搜索切面类 -->
        <context:component-scan base-package="org.xs.demo1" use-default-filters="false">
            <context:include-filter type="annotation" expression="org.aspectj.lang.annotation.Aspect" />
        </context:component-scan>
    </beans>
    

    3、运行测试

    效果和之前的一样

    注:如果要织入的是Controller类,需要把"spring-context-aop.xml"内的内容移动到"spring-mvc.xml"里,或者将"spring-context-aop.xml"文件名加入到DispatcherServlet里(用逗号分隔)。原因是在父上下文里的内容无法读取在子上下文里配置的Controller,只有将AOP的bean也放在子上下文里加载才行。

    实例代码地址:https://github.com/ctxsdhy/cnblogs-example

  • 相关阅读:
    Intersection
    B-number
    Intersecting Lines
    Segments
    G. Swapping Places
    Toy Storage
    TOYS
    哈密顿绕行世界问题
    java试题复盘——11月25日
    java试题复盘——11月13日
  • 原文地址:https://www.cnblogs.com/ctxsdhy/p/6375719.html
Copyright © 2011-2022 走看看