4、spring核心AOP

 

Spring AOP

1. Spring AOP 概述 1-1

1.1. 何为AOP 1-1

1.2. AOP要解决什么问题? 1-1

1.3. AOP实际项目应用场景? 1-2

1.4. AOP底层原理实现分析? 1-2

2. Spring AOP 编程实现 2-2

2.1. AOP 基本步骤 2-2

2.2. AOP 基于xml实现 2-2

2.2.1. 创建项目添加依赖 2-3

2.2.2. 添加spring配置文件 2-3

2.2.3. 创建核心业务类 2-4

2.2.4. 创建日志处理类 2-4

2.2.5. 配置bean对象 2-5

2.2.6. 编写测试类 2-6

3. 总结 3-7

3.1. 重点和难点分析 3-7

3.2. 常见FAQ 3-7

1. Spring AOP 概述

  1.1. 何为AOP

    AOP 是软件设计领域中的面向切面编程,它是面向对象编程的一种补充和完善实际项目中我们通常将面向对象理解为一个静态过程(例如一个系统有多少模块,一个模块有哪些对象,对象有哪些属性),面向切面理解为一个动态过程(在对象运行时动态织入一些功能。)

 

  1.2. AOP要解决什么问题?

    实际项目中通常会将系统两大部分:核心关注点和非核心关注点

    思考?

    编程过程中首先要完成的是什么?核心关注点(核心业务)

    非核心关注点如何切入到系统中?硬编码(违背OCP)AOP(推荐)

    AOP就是要在基于OCP在不改变原有系统核心业务代码的基础上动态添加一些扩展功能。

  1.3. AOP实际项目应用场景?

    AOP 通常应用于日志的处理,事务处理,权限处理,缓存处理等等。

  

  1.4. AOP底层原理实现分析?

     AOP底层基于代理机制实现功能扩展:(了解)

      1) 假如目标对象(被代理对象)实现接口,则底层默认采用JDK动态代理机制为目标对象创建代理对象(目标类和代理类会实现共同接口)

      2) 假如目标对象(被代理对象)没有实现接口,则底层默认采用CGLIB代理机制为目标对象创建代理对象(默认创建的代理类会继承目标对象类型)。

  2. Spring AOP 编程实现

     2.1. AOP 基本步骤

       step1:创建maven java 项目

      step2:添加aop依赖

      step3:配置aop 核心(基于xml,基于注解)

      step4:定义核心业务(核心关注点):推荐先写接口再写实现类

      step5:定义扩展业务(非核心关注点)

      step6:基于配置实现非核心业务的切入

      step7:编写测试类进行单元测试

  2.2. AOP 基于xml实现

     通过AOP为核心业务类添加日志处理

    2.2.1. 创建项目添加依赖

    创建maven java 项目然后添加依赖

     2.2.2. 添加spring配置文件

    2.2.3. 创建核心业务类

    创建接口

  

public interface HelloService {
     void sayHello(String msg);
}

 

创建接口实现类

public class HelloServiceImpl implements HelloService {
    public void sayHello(String msg) {
        //假设这条语句是我们系统中的核心业务
        System.out.println(msg);
    }
}

2.2.4. 创建日志处理类

public class LoggingAspect {
     public void beforeMethod(){
         System.out.println("method start");
     }
     public void afterMethod(){
         System.out.println("method end");
     }
}

2.2.5. 配置bean对象

<?xml version="1.0" encoding="UTF-8"?>
<beans 
    default-lazy-init="true"
    xmlns="http://www.springframework.org/schema/beans" 
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:aop="http://www.springframework.org/schema/aop" 
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:util="http://www.springframework.org/schema/util"
    xmlns:jpa="http://www.springframework.org/schema/data/jpa"
    xsi:schemaLocation="  
       http://www.springframework.org/schema/beans   
       http://www.springframework.org/schema/beans/spring-beans-4.3.xsd  
       http://www.springframework.org/schema/mvc   
       http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd   
       http://www.springframework.org/schema/tx   
       http://www.springframework.org/schema/tx/spring-tx-4.3.xsd   
       http://www.springframework.org/schema/aop 
       http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
       http://www.springframework.org/schema/util 
       http://www.springframework.org/schema/util/spring-util-4.3.xsd
       http://www.springframework.org/schema/data/jpa 
       http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-4.3.xsd"> 
   
    <!-- 核心业务对象 -->
    <bean id="helloService"
          class="spring.beans.HelloServiceImpl"/>
    <!-- 配置非核心业务对象(日志处理对象):切面 -->
    <bean id="log" 
          class="spring.aop.LoggingAspect"/>
    <!-- AOP配置(切入点,切面) -->  
    <aop:config>
       <!-- 配置切入点 -->
       <aop:pointcut 
            expression="within(spring.beans.HelloServiceImpl)" 
            id="logPointCut"/>
       <!-- 配置日志处理 -->
       <aop:aspect ref="log" >
           <aop:before method="beforeMethod" 
                       pointcut-ref="logPointCut"/>
           <aop:after  method="afterMethod"
                       pointcut-ref="logPointCut"/>
       </aop:aspect>
    </aop:config>
</beans>

2.2.6. 编写测试类

//1.初始化spring容器
        ClassPathXmlApplicationContext ctx=
        new ClassPathXmlApplicationContext(
                "applicationContext.xml");
        //2.获取Bean对象
        HelloService helloService=
        ctx.getBean("helloService",HelloService.class);
        //3.执行业务
        helloService.sayHello("CGB1710");//proxy的方法
        //4.释放资源
        ctx.close();

 

2.3. AOP 基于注解实现

先参考AOP参考文档进行整理,后续上传 

  1.1.1. 创建maven 项目添加依赖

    <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>4.3.9.RELEASE</version>
       </dependency>
        <!--
        Spring AOP的实现依托于Aspect框架
        所以要引用1.8.5有问题
        -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.8.9</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.9</version>
        </dependency>

 

  1.1.2. 创建核心业务类

    定义一个订单接口,此接口中定义多个业务操作。

public interface OrderService {
     public void saveOrder();
     public void deleteOrder();
}

  1.1.3. 创建时间处理类

    将此时间处理类作为核心业务增强(一个横切面对象)类,用于输出业务开始执行时间,以及业务结束执行时间。

    横切面对象主要由两部分构成:切入点(用于@Pointcut标识),以及功能增强(用通知@Before@After等进行标识)

@Aspect
@Service
public class TimeAspect {
    @Pointcut("bean(orderService)")
    public void pointcut(){}
    /**增强功能:前置通知(业务方法执行之前执行)*/
    @Before("pointcut()")
    public void begin(){
      System.out.println("start:"+System.nanoTime());
    }
    /**增强功能:最终通知(业务方法执行最后执行,
     *无论业务方法是否执行成功,此功能都要执行)*/
    @After("pointcut()")
    public void end(){
      System.out.println("end:"+System.nanoTime());
    }
}

 

    其中:

      @Aspect 注解用于标识此类为一个AOP横切面对象

      @Pointcut 注解用于定义本类中的切入点,本案例中切入点表达式用的是bean表达式,这个表达式以bean开头,bean括号中的内容为一个spring管理的某个bean对象的id

      @Before 用于定义一个前置通知(满足切入点表达式的核心业务方法执行之前要执行的一个操作)

      @After  用于定义一个后置通知(满足切入点表达式的核心业务方法执行之后要执行的一个操作)

    术语增强:

      切面:用于封装扩展业务的一个类的对象。

      通知:切面扩展业务中的一个操作。

  1.1.4. 配置AOP实现

    对于基于注解方式的配置一般有两种方式,一种是直接在xml核心配置文件中进行配置,还有一种在类中基于注解进行配置。例如

     基于xml方式配置对注解的应用

<?xml version="1.0" encoding="UTF-8"?>
<beans 
    default-lazy-init="true"
    xmlns="http://www.springframework.org/schema/beans" 
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:aop="http://www.springframework.org/schema/aop" 
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:util="http://www.springframework.org/schema/util"
    xmlns:jpa="http://www.springframework.org/schema/data/jpa"
    xsi:schemaLocation="  
       http://www.springframework.org/schema/beans   
       http://www.springframework.org/schema/beans/spring-beans-4.3.xsd  
       http://www.springframework.org/schema/mvc   
       http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd   
       http://www.springframework.org/schema/tx   
       http://www.springframework.org/schema/tx/spring-tx-4.3.xsd   
       http://www.springframework.org/schema/aop 
       http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
       http://www.springframework.org/schema/util 
       http://www.springframework.org/schema/util/spring-util-4.3.xsd
       http://www.springframework.org/schema/data/jpa 
       http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-4.3.xsd"> 
   
       <!-- 配置对类组件的扫描 -->
       <context:component-scan 
       base-package="com.spring"/>
       <!-- 启用AOP注解(自动为目标对象创建代理对象) -->
       <aop:aspectj-autoproxy/>

</beans>

  在类中基于注解方式的配置

@ComponentScan("com.spring.beans")
@EnableAspectJAutoProxy
public class AppConfig {

}

  1.1.5. 编写测试类

public class TestAOP01 {

    public static void main(String[] args) {
        //1.初始化容器
        ClassPathXmlApplicationContext ctx=
        new ClassPathXmlApplicationContext(
                "applicationContext.xml");
        //2.获取bean对象
        OrderService os=(OrderService)
        ctx.getBean("orderService", 
                OrderService.class);
        //3.执行业务方法
        os.saveOrder();
        os.deleteOrder();
        //4.释放资源
        ctx.close();
    }
}

    基于类中注解方式配置的测试实现

public class TestAOP02 {
    public static void main(String[] args) {
        //1.初始化容器对象
        AnnotationConfigApplicationContext ctx=
        new AnnotationConfigApplicationContext(
                AppConfig.class);
        //2.获取Bean对象
        OrderService orderService=
        ctx.getBean("orderService", OrderService.class);
        //3.执行业务
        orderService.saveOrder();
        //orderService.deleteOrder();
        //4.释放资源
        ctx.close();
    }
}

      基于类的注解配置初始化工厂时需要初始化

      AnnotationConfigApplicationContext对象

  2. Spring AOP 编程增强

    2.1. 切面表达式增强

        Spring中通过切入点表达式定义具体切入点,其常用AOP切入点表达式定义及说明:

 

指示符

作用

 

bean

用于匹配指定bean id的的方法执行

 

within

用于匹配指定包名下类型内的方法执行

 

execution

用于进行细粒度方法匹配执行具体业务

    2.1.1. Bean表达式应用增强

         bean应用于类级别,实现粗粒度的控制:

bean(UserServiceImpl))

指定一个类

bean(*Service)

指定所有的后缀为service的类

 

    2.1.2. Within表达式应用增强

         within应用于类级别,实现粗粒度的控制:

within(aop.service.UserServiceImpl))

指定类,只能指定一个类

within(aop.service.*))

只包括当前目录下的类

within(aop.service..*))

指定当前目录包含所有子目录中的类

 

    2.1.3. Execution表达式应用增强

        execution方法级别,细粒度的控制:

        语法:execution(返回值类型 包名.类名.方法名(参数列表))

execution(void aop.service.UserServiceImpl.addUser())

匹配方法

execution(void aop.service.PersonServiceImpl.addUser(String))

方法参数必须为字符串

execution(* aop.service..*.*(..))

万能配置

     2.2. 切面通知增强

      在AOP编程中有五种类型的通知:

        1) 前置通知

        2) 后置通知

        3) 异常通知

        4) 最终通知

        5) 环绕通知

    2.2.1. Xml方式通知配置增强

      切入点及前置通知,后置通知,返回通知,异常通知,环绕通知的配置

<aop:config>
        <aop:pointcut id="pc"expression="execution(* 
com.company.spring.service..*.*(..))" >
        <aop:aspect ref="loggingAspect">
            <aop:before method="beforeMethod" pointcut-ref="pc"/>
            <aop:after method="afterMethod" pointcut-ref="pc"/>
            <aop:after-returning method="returnMethod"
 pointcut-ref="pc"/>
            <aop:after-throwing method="throwMethod" 
pointcut-ref="pc"/>
        </aop:aspect>
    </aop:config>

  

切入点及环绕通知的配置(了解)
    <aop:config>
        <aop:pointcut id="pc"expression="execution(* 
com.company.spring.service..*.*(..))" >
        <aop:aspect ref="loggingAspect">
            <aop:around method="aroundMethod" pointcut-ref="pc"/>
        </aop:aspect>
    </aop:config>

2.2.2. 注解方式通知配置增强

切入点及前置通知,后置通知,返回通知,异常通知,环绕通知的配置

@Aspect
@Service
public class TxAspect {
    @Before("bean(orderService)")
    public void startTx(){
        System.out.println("开启事务");
    }
    @AfterReturning("bean(orderService)")
    public void commitTx(){
        System.out.println("提交事务");
    }
    @AfterThrowing("bean(orderService)")
    public void rollbackTx(){
        System.out.println("回滚事务");
    }
    @After("bean(orderService)")
    public void closeResource(){
        System.out.println("释放资源");
    }
}

切入点及环绕通知的配置

@Component
@Aspect
public class TxManager {
    @Pointcut("execution(com.company.spring.service..*.*(..))")
    public void pointCut() {}
    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) 
            throws Throwable{
            System.out.println("事务开始");
            Object result = joinPoint.proceed();
            System.out.println("事务结束");
            return result;
    }
}

2.3. 切面执行顺序配置增强

实际项目中可能会有多个切面,切面之间的执行可能需要一定的顺序

2.3.1. Xml方式配置执行顺序

<aop:config>
        <aop:pointcut id="pc"
             expression="execution(* 
com.company.spring.service..*.*(..))"/>
        <aop:aspect ref="loggingAspect" order="1">
            <aop:around method="aroundMethod" pointcut-ref="pc"/>
        </aop:aspect>
        <aop:aspect ref="txManager" order="2">
            <aop:around method="aroundMethod" pointcut-ref="pc"/>
        </aop:aspect>
    </aop:config>

2.3.2. 注解方式配置执行顺序

注解方式顺序配置需要借助@Order注解

@Order(1)
@Aspect
@Component
public class TxManager {
    @Pointcut("execution(* com.company.spring.service..*.(..))")
    public void pointCut() {}
    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) 
            throws Throwable{
            System.out.println("事务开始");
            Object result = joinPoint.proceed();
            System.out.println("事务结束");
            return result;
    }
}

注解方式顺序配置

@Order(2)
@Aspect
@Component
public class LoggingAspect {
    @Pointcut("execution(* com.company.spring.service..*.(..))")
    public void pointCut() {}
    @Before("pointCut()")
    public void beforeMethod() {
        System.out.println("beforeMethod");
    }
    @Before("pointCut()")
    public void afterMethod() {
        System.out.println("afterMethod");
    }
}

 

  3. 总结

    3.1. 重点和难点分析

  1. AOP 是什么,解决了什么问题,实现原理,应用场景
  2. AOP 编程基本步骤及基本实现
  3. AOP 编程中重点掌握基于注解方式配置

    3.2. 常见FAQ

  1. 什么是OCP原则(开闭原则)
  2. 什么是单一职责原则(一个类或接口的职责不要太多)
  3. springAOP的有哪些配置方式(xml和注解)
  4. Spring AOP 的通知有哪些基本类型
  5. Spring AOP是如何为Bean对象创建代理对象的

3.3. 作业

  1. 总结课堂知识点(笔记再详细也是别人的,不是自己的,拿来主义对自己的提高会很慢,无论是否认同,可以试试,看看最后效果)
  2. 完成课堂AOP基本案例
  3. 了解代理模式及应用场景,实现方式(最好自己尝试实现)
原文地址:https://www.cnblogs.com/xiangyuqi/p/8655444.html