Spring——AOP

AOP概述

AOP为Aspect Oriented Programming的缩写,意为:面向切面编程

通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术

AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型

利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率

主要意图:

将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码

通俗来说就是:在不改变原有的代码情况下,添加新的功能,对原有的功能进行一个增强的操作

Spring中AOP的底层采用了动态代理的方式来实现

Spring 框架一般都是基于 AspectJ 实现 AOP 操作

AspectJ 不是 Spring 组成部分,独立 AOP 框架,一般把 AspectJ 和 Spirng 框架一起使用,进行 AOP 操作

基于 AspectJ 实现 AOP 操作可以基于 xml 配置文件实现,也可以基于注解方式实现

在项目工程里面需要引入 AOP 相关依赖

spring-aspects-5.2.18.RELEASE.jar
com.springsource.net.sf.cglib-2.2.0.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar

前置知识

AOP术语

通知(Advice):实际增强的逻辑部分

目标(Target):被通知对象

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

切入点(PointCut):实际被增强(通知)的方法

连接点(JointPoint):类中那些方法可以被增强(通知)

切面(Aspect):把通知应用到切入点,就是将增强的逻辑部分应用到被增强的方法上

通知类型

前置通知(Before advice):在连接点之前运行的通知,但没有能力阻止执行流继续到连接点(除非它抛出异常)

后置通知(After returning advice):在连接点正常完成后运行的通知,如果抛出异常就不会通知

异常通知(After throwing advice):如果方法通过抛出异常退出,则运行通知

最终通知(After (finally) advice):不管连接点退出的方式(正常或异常返回)都将运行的通知

环绕通知(Around advice):环绕通知可以在方法调用之前和之后执行自定义行为,有多个通知时,可以使用环绕通知来一次实现

切入点表达式

切入点表达式作用:知道对哪个类里面的哪个方法进行增强

语法结构: execution([权限修饰符] [返回类型] [类全路径] [方法名称]([参数列表]))

例 1:对 com.wcy.dao.BookDao 类里面的 add 进行增强
execution(* com.wcy.dao.BookDao.add(..))

例 2:对 com.wcy.dao.BookDao 类里面的所有的方法进行增强
execution(* com.wcy.dao.BookDao.* (..))

例 3:对 com.wcy.dao 包里面所有类,类里面所有方法进行增强
execution(* com.wcy.dao.*.* (..))

使用注解实现AOP操作

增强方法

创建一个普通类,一个增强类,并注册到IOC容器中

@Component
public class User {

    public void add(){
        System.out.println("User的add方法执行了");
    }
}

在增强类的上面添加@Aspect注解

@Aspect
@Component
public class UserPlus01 {

    public void before(){
        System.out.println("UserPlus01中的before方法执行!");
    }

}

编写配置文件

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

    <!--开启注解扫描-->
    <context:component-scan base-package="com.wcy"/>

    <!-- 开启 Aspect 生成代理对象 -->
    <aop:aspectj-autoproxy/>

</beans>
  • <aop:aspectj-autoproxy/>开启 Aspect 生成代理对象,就可以实现增强

配置不同类型的通知,增强User中的add方法

@Aspect
@Component
public class UserPlus01 {

    /**
     * 前置通知
     */
    @Before("execution(* com.wcy.aopanno.User.add(..))")
    public void before(){
        System.out.println("UserPlus01中的before方法执行!");
    }

    /**
     * 最终
     */
    @After("execution(* com.wcy.aopanno.User.add(..))")
    public void after(){
        System.out.println("UserPlus01中的after方法执行!");
    }

    /**
     * 后置通知
     */
    @AfterReturning("execution(* com.wcy.aopanno.User.add(..))")
    public void afterReturning(){
        System.out.println("UserPlus01中的afterReturning方法执行!");
    }

    /**
     * 异常通知
     */
    @AfterThrowing("execution(* com.wcy.aopanno.User.add(..))")
    public void afterThrowing(){
        System.out.println("UserPlus01中的afterThrowing方法执行!");
    }

    /**
     * 环绕通知
     * 使用Around注解必须要有参数 ProceedingJoinPoint 和返回值 Object
     */
    @Around("execution(* com.wcy.aopanno.User.add(..))")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("UserPlus01中的around方法执行1!");
        Object proceed = joinPoint.proceed();
        System.out.println("UserPlus01中的around方法执行2!");
        return proceed;
    }
}

最后调用user.add()的结果为
image
只有异常通知没有被执行

当在add方法中出现异常时
image
后置异常没有被执行
环绕异常的环绕后也没有执行

这样就通过aop的方式对我们的方法进行了增强,并没有改变源码

提取切入点表达式

在上面的例子中,我们增强的是同一个方法,但是却写了很多的切入点表达式,可以将其提取出来

@Aspect
@Component
public class UserPlus01 {

    @Pointcut("execution(* com.wcy.aopanno.User.add(..))")
    public void pointCut(){}

    /**
     * 前置通知
     */
    @Before("pointCut()")
    public void before(){
        System.out.println("UserPlus01中的before方法执行!");
    }
}

通过使用注解@Pointcut将表达式提取出来,放在一个方法上
在通过注解 @Before("pointCut()")调用这个方法就可以复用表达式

设置增强类的优先级

如果我们一个方法又多个增强类,可以设置他们的执行优先级
再添加一个增强类

@Aspect
@Component
public class UserPlus02 {

    @Pointcut("execution(* com.wcy.aopanno.User.add(..))")
    public void pointCut(){}

    /**
     * 前置通知
     */
    @Before("pointCut()")
    public void before(){
        System.out.println("UserPlus01中的before方法执行!");
    }
}

使用@Order注解来设置他们的优先级,Order的value值越小,优先级越高

@Aspect
@Component
@Order(2)
public class UserPlus02 {
}
@Aspect
@Component
@Order(1)
public class UserPlus01 {
}

结果是01先执行
image

纯注解AOP

之前我们还使用了配置文件来开启注解扫描和代理生成,现在编写java配置类来实现纯注解开发

编写配置类

@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ComponentScan("com.wcy")
public class SpringConfig {
}


@EnableAspectJAutoProxy(proxyTargetClass = true)的功能和配置文件中<aop:aspectj-autoproxy/>相同

通过xml配置文件配置AOP

一个普通类,一个增强类

public class User {
    public void add(){
        System.out.println("User的add方法执行了");
    }
}
public class UserPlus01 {

    /**
     * 前置通知
     */
    public void before(){
        System.out.println("UserPlus01中的before方法执行!");
    }

    /**
     * 最终
     */
    public void after(){
        System.out.println("UserPlus01中的after方法执行!");
    }

    /**
     * 后置通知
     */
    public void afterReturning(){
        System.out.println("UserPlus01中的afterReturning方法执行!");
    }

    /**
     * 异常通知
     */
    public void afterThrowing(){
        System.out.println("UserPlus01中的afterThrowing方法执行!");
    }

    /**
     * 环绕通知
     */
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("UserPlus01环绕前!");
        Object proceed = joinPoint.proceed();
        System.out.println("UserPlus01环绕后!");
        return proceed;
    }
}

编写配置文件

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


    <!-- 开启 Aspect 生成代理对象-->
    <aop:aspectj-autoproxy/>

    <!--注册bean-->
    <bean id="user" class="com.wcy.aopxml.User"/>
    <bean id="userPlus" class="com.wcy.aopxml.UserPlus01"/>

    <!--aop配置-->
    <aop:config>
        <!--切入点-->
        <aop:pointcut id="point" expression="execution(* com.wcy.aopxml.User.add(..))"/>

        <!--
            配置切面
            就是将增强方法作用在切入点上
            ref的值是增强类
        -->
        <aop:aspect ref="userPlus">
            <!--配置各种通知-->
            <aop:before method="before" pointcut-ref="point"/>
            <aop:after method="after" pointcut-ref="point"/>
            <aop:after-returning method="afterReturning" pointcut-ref="point"/>
            <aop:after-throwing method="afterThrowing" pointcut-ref="point"/>
            <aop:around method="around" pointcut-ref="point"/>
        </aop:aspect>
    </aop:config>

</beans>

结果是一致的

AOP基本功能就大致结束了~

posted @ 2021-12-07 20:26  茶音白  阅读(69)  评论(0)    收藏  举报