Spring AOP 面向切面编程

它依旧是一种设计思想 本质还是为了松散耦合

先去分一个概念

  1. OOP面向对象编程
    实体及其属性行为
  2. AOP面向切面编程
    某个阶段或者步骤

看下图解:

image

image

代码业务的实现都是纵向 而AOP切面实现为横向

AOP的一些术语:

连接点(Jointpoint):表示需要在程序中插入横切关注点的扩展点,连接点可能是类初始化、方法执行、方法调用、字段调用或处理异常等等,Spring只支持方法执行连接点,在AOP中表示为在哪里干;
切入点(Pointcut): 选择一组相关连接点的模式,即可以认为连接点的集合,Spring支持perl5正则表达式和AspectJ切入点模式,Spring默认使用AspectJ语法,在AOP中表示为在哪里干的集合;
通知(Advice):在连接点上执行的行为,通知提供了在AOP中需要在切入点所选择的连接点处进行扩展现有行为的手段;包括前置通知(before advice)、后置通知(after advice)、环绕通知(around advice),在Spring中通过代理模式实现AOP,并通过拦截器模式以环绕连接点的拦截器链织入通知;在AOP中表示为干什么;
方面/切面(Aspect):横切关注点的模块化,比如上边提到的日志组件。可以认为是通知、引入和切入点的组合;在Spring中可以使用Schema和@AspectJ方式进行组织实现;在AOP中表示为在哪干和干什么集合;
引入(inter-type declaration):也称为内部类型声明,为已有的类添加额外新的字段或方法,Spring允许引入新的接口(必须对应一个实现)到所有被代理对象(目标对象), 在AOP中表示为干什么(引入什么);
目标对象(Target Object):需要被织入横切关注点的对象,即该对象是切入点选择的对象,需要被通知的对象,从而也可称为被通知对象;由于Spring AOP 通过代理模式实现,从而这个对象永远是被代理对象,在AOP中表示为对谁干;
织入(Weaving):把切面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象。这些可以在编译时(例如使用AspectJ编译器),类加载时和运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。在AOP中表示为怎么实现的;
AOP代理(AOP Proxy):AOP框架使用代理模式创建的对象,从而实现在连接点处插入通知(即应用切面),就是通过代理来对目标对象应用切面。在Spring中,AOP代理可以用JDK动态代理或CGLIB代理实现,而通过拦截器模型应用切面。在AOP中表示为怎么实现的一种典型方式;


通知类型:

通知类型:前置通知(Before advice):在某连接点之前执行的通知,但这个通知不能阻止连接点之前的执行流程(除非它抛出一个异常)。
后置通知(After returning advice):在某连接点正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回。
异常通知(After throwing advice):在方法抛出异常退出时执行的通知。最终通知(After (finally) advice):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。
环绕通知(Around Advice):包围一个连接点的通知,如方法调用。这是最强大的一种通知类型。环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它自己的返回值或抛出异常来结束执行。


我们看一个例子:

先引入依赖:
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.20.RELEASE</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.4</version>
        </dependency>
AspectJ是什么呀

AspectJ是一个java实现的AOP框架,它能够对java代码进行AOP编译(一般在编译期进行),让java代码具有AspectJ的AOP功能(当然需要特殊的编译器)

关于AOP的配置方式 和 AspectJ动态静态织入 直接看例子:


业务:
public interface UserService {
    public void add();
    public void delete();
    public void select();
    public void update();
}
public class UserServiceImpl implements UserService {
    public void add(){
        System.out.println("增加");
    }
    public void delete(){
        System.out.println("删除");
    }
    public void select(){
        System.out.println("查询");
    }
    public void update(){
        System.out.println("新增");
    }
}

package log;

import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;

public class AfterLog implements AfterReturningAdvice {
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("执行了"+method.getName()+"方法 返回结果为:"+returnValue);
    }
}

package log;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class BeforeLog implements MethodBeforeAdvice {

    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass().getName()+"的"+method.getName()+"执行了");
    }
}
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: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 id="userService" class="Service.UserServiceImpl"/>
    <bean id="BeforeLog" class="log.BeforeLog"/>
    <bean id="AfterLog" class="log.AfterLog"/>

    <aop:config>
<!--        切入点-->
<!--        *(方法修饰符):这表示匹配任何方法修饰符,如public、private、protected等。-->
<!--        Service.UserServiceImpl(返回类型和方法名):这里实际上看起来像是包和类的组合,但在Java中这不是有效的全限定类名。通常,你会看到类似com.example.service.UserServiceImpl这样的全限定类名。-->
<!--        *(方法名):这表示匹配任何方法名。-->
<!--        (..)(参数列表):两个点(..)表示匹配任何数量和类型的参数-->
        <aop:pointcut id="pointcut" expression="execution(* Service.UserServiceImpl.*(..))"/>
<!--        环绕执行-->
        <aop:advisor advice-ref="BeforeLog" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="AfterLog" pointcut-ref="pointcut"/>
    </aop:config>
</beans>

测试:
        //第一种方式 实现spring API接口
        ApplicationContext context = new ClassPathXmlApplicationContext("applicitionContext.xml");
         //动态代理的为接口 即UserService
        UserService userService = context.getBean("userService", UserService.class);
        userService.add();
        System.out.println("================================");

自定义类实现
public class DiyAOP {
    public void before(){
        System.out.println("方法前");
    }
    public void after(){
        System.out.println("方法后");
    }
}
<?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 id="userService" class="Service.UserServiceImpl"/>
    <bean id="diy" class="diy.DiyAOP"/>
    <aop:config>
<!--        切面-->
        <aop:aspect ref="diy">
            <aop:pointcut id="pointcut" expression="execution(* Service.UserServiceImpl.*(..))"/>
            <aop:before method="before" pointcut-ref="pointcut"/>
            <aop:after method="after" pointcut-ref="pointcut"/>
        </aop:aspect>
    </aop:config>

</beans>
测试:
        //第二种方式 自定义类
        ApplicationContext context2 = new ClassPathXmlApplicationContext("applicationContextDiy.xml");
        UserService userService2 = context2.getBean("userService", UserService.class);
        userService2.add();
        System.out.println("================================");

注解配置

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

@Aspect
public class DiyAnnotaion {
    @Before("execution(* Service.UserServiceImpl.*(..))")
    public void before() {
        System.out.println("方法前");
    }

    @After("execution(* Service.UserServiceImpl.*(..))")
    public void after() {
        System.out.println("方法后");
    }
}
<?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 id="userService" class="Service.UserServiceImpl"/>
    <bean id="diyAnnotation" class="diy.DiyAnnotaion"/>
<!--    开启注解支持-->
    <aop:aspectj-autoproxy/>
</beans>


        //第三种方式 注解
        ApplicationContext context3 = new ClassPathXmlApplicationContext("applicationContextDiyAnnotion.xml");
        UserService userService3 = context3.getBean("userService", UserService.class);
        userService3.add();

效果如图:

image

关于一些spring 中xml的约束声明 参考:https://blog.csdn.net/qq_43418737/article/details/122666966
文章更深入详细 参考:
https://pdai.tech/md/spring/spring-x-framework-aop.html

posted on 2024-05-20 10:19  蒸饺  阅读(27)  评论(0)    收藏  举报