SpringAop

什么是AOP

什么是AOP

AOP意为:面向切面编程,通过预编译和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是对OOP的一种延续。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务之间的耦合性降低,提高代码的重用性。

image-20210627110949962

AOP在Spring中的作用

提供声明式事务;允许用户自定义切面

以下名词需要了解

  • 横切关注点:跨越应用程序多个模块的方法或功能。即是与我们业务逻辑无关的,但是我们需要关注的部分就是横切点。如日志,安全,缓存,事务等。

  • 切面: 横切关注点被模块化的特殊对象。他是一个类。

  • 通知(advice):切面必须要完成的工作。他是类中的一个方法。

  • 目标(target):被通知的对象。

  • 代理(proxy): 向目标应用通知之后创建的对象。

  • 切入点(Point Cut):切面通知执行的地点的定义。

  • 连接点(JionPoint):与切面点匹配的执行点。

使用Spring实现Aop

使用AOP织入,需要导入一个依赖包

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
  1. 通过SpringAPI实现

    首先编写接口

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public interface UserService {

    public void add();

    public void delete();

    public void update();

    public void search();

    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public class UserServiceImpl implements UserService{

    @Override
    public void add() {
    System.out.println("增加用户");
    }

    @Override
    public void delete() {
    System.out.println("删除用户");
    }

    @Override
    public void update() {
    System.out.println("更新用户");
    }

    @Override
    public void search() {
    System.out.println("查询用户");
    }
    }

    然后我们写一个增强类,一个前置增强,一个后置强

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class Log implements MethodBeforeAdvice {

    //method : 要执行的目标对象的方法
    //objects : 被调用的方法的参数
    //Object : 目标对象
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
    System.out.println( o.getClass().getName() + "的" + method.getName() + "方法被执行了");
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class AfterLog implements AfterReturningAdvice {
    //returnValue 返回值
    //method被调用的方法
    //args 被调用的方法的对象的参数
    //target 被调用的目标对象
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
    System.out.println("执行了" + target.getClass().getName()
    +"的"+method.getName()+"方法,"
    +"返回值:"+returnValue);
    }
    }

    最后把我们写的增强类在spring的文件中注册,并实现AOP切入实现,注意导入约束:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    <?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: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/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--注册bean-->
    <bean id="userService" class="com.kuang.service.UserServiceImpl"/>
    <bean id="log" class="com.kuang.log.Log"/>
    <bean id="afterLog" class="com.kuang.log.AfterLog"/>

    <!--aop的配置-->
    <aop:config>
    <!--切入点 告诉aop从哪里切入 expression:表达式匹配要执行的方法-->
    <aop:pointcut id="pointcut" expression="execution(* com.kuang.service.UserServiceImpl.*(..))"/>
    <!--执行环绕; advice-ref执行方法 . pointcut-ref切入点-->
    <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
    <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
    </aop:config>

    </beans>

    AOP就是把公共业务(日志,事务等)和领域业务结合起来,当执行领域业务的时候,把公共业务加进来。实现公共业务的重复利用,领域业务更加纯粹!其本质就是动态代理!

    1. 自定义类来实现AOP

      目标业务不变依旧是userServiceIml

      第一步写我们的切入类

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      public class DiyPointcut {

      public void before(){
      System.out.println("---------方法执行前---------");
      }
      public void after(){
      System.out.println("---------方法执行后---------");
      }

      }

      在spring中配置

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      <!--第二种方式自定义实现-->
      <!--注册bean-->
      <bean id="diy" class="com.kuang.config.DiyPointcut"/>

      <!--aop的配置-->
      <aop:config>
      <!--第二种方式:使用AOP的标签实现-->
      <aop:aspect ref="diy">
      <aop:pointcut id="diyPonitcut" expression="execution(* com.kuang.service.UserServiceImpl.*(..))"/>
      <aop:before pointcut-ref="diyPonitcut" method="before"/>
      <aop:after pointcut-ref="diyPonitcut" method="after"/>
      </aop:aspect>
      </aop:config>

      就是自己写一个切入类,切入类写好通知方法,把它注册为一个javabeen然后把它定义为一个切面,首先配置他的切入点,再配置通知advice,通知对用的method就是我们在切入类里面写的方法!

  2. 使用注解实现

    编写一个注解实现的增强类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    package com.kuang.config;

    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;

    @Aspect
    public class AnnotationPointcut {
    @Before("execution(* com.kuang.service.UserServiceImpl.*(..))")
    public void before(){
    System.out.println("---------方法执行前---------");
    }

    @After("execution(* com.kuang.service.UserServiceImpl.*(..))")
    public void after(){
    System.out.println("---------方法执行后---------");
    }

    @Around("execution(* com.kuang.service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint jp) throws Throwable {
    System.out.println("环绕前");
    System.out.println("签名:"+jp.getSignature());
    //执行目标方法proceed
    Object proceed = jp.proceed();
    System.out.println("环绕后");
    System.out.println(proceed);
    }
    }

    第二步:在spring配置文件中,注册bean 并增加支持注解的配置

    1
    2
    3
    <!--第三种方式:注解实现-->
    <bean id="annotationPointcut" class="com.kuang.config.AnnotationPointcut"/>
    <aop:aspectj-autoproxy/>

    aop:aspectj-autoproxy说明

    通过aop命名空间的aop:aspectj-aotuproxy声明自动为spring容器中那些配置@aspectj切面的bean创建代理,组织切面。当然,spring内部依旧采用AnnotationAwareAspectjAotuProxyCreator进行自动代理的创建工作,但是具体实现的细节已经被aop:aspectj-autoproxy 隐藏起来了

    aop:aspectj-autoproxy有一个pro-target-class属性,默认为false,表示使用jdk动态代理织入增强,当配 为aop:aspectj-autoproxy proxy-target-class=true时,表示使用CGLIB动态代理技术织入增强。不过即使proxy-target-class设置为false,如果目标没有声明接口。则spring将自动使用cglib动态代理。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!