Spring学习日记03: Spring AOP


Spring03学习日记:AOP 面向切面编程

简单来说,是操作过程的复用

前言

在面向对象(OOP)的时代。很多人在日常开发中会发现有些东西不像一个对象,其实是一个流程。

假设有这么一个流程1:对一个数据库发送请求-》拿数据1-》拿数据2-》展示数据

image-20220630194724492

假设又有一个流程2,步骤比流程1多了一个步骤:

对一个数据库发送请求-》要求判断权限 -》拿数据1-》拿数据2-》展示数据

image-20220630194736855

这种情况下,抽象这种流程是很难的,如果是用继承实现会有很大的耦合性,这个时候就要用到面向切面编程了。这种情况下就要使用代理了。

image-20220630201406548

关于代理模式(静态代理、动态代理),以后专门拿出来写。。。

通过代理Proxy类对RealSubject类方法的调用,也可以添加属于代理类自己的一些代码(例如检查权限)

然后上面流程根据实际需求来决定是调用Proxy还是RealSubject去JointPoint

实际上,切面就有点像一个流程的类,JointPoint有点像流程的方法,然后通过动态代理的模式实现流程代码的复用。

一个简单的例子

根据前言所举得例子,进行代码的实现,使得概念了解的更加明白。

先用OOP举例,比如实现一个流程process,里面有三个步骤action1、action2、action3

public class process extends AbstractProcess{
    // 步骤1
    public void action1(){
        System.out.println("action 1");
    }
    // 步骤2.
    public void action2(){
        System.out.println("action 2");
        System.out.println("action 2 again");
    }
    // 步骤3.
    public void action3(){
        System.out.println("action 3");
    }
}

这时候假如说又要开发一个流程process2,里面有的action2要在action1的基础上增加一些东西,使用OOP的话,如果以后再次增加新的流程process3、process4、….,我们很难将这些四处分散的代码模块化。

因此用代理模式比较可行。将proces作为RealSubject,利用Proxy模式去实现流程2、流程3等等。。代码如下:

先创建一个接口IProcess:

public interface IProcess {
    void action1();
    void action2();
    void action3();
}

创建一个抽象类AbstractProcess实现IProcess

public abstract class AbstractProcess implements IProcess{
    private IProcess process; // 实现类
    public IProcess getProcess() {
        return process;
    }
    public void setProcess(IProcess process) {
        this.process = process;
    }
    public static void doActions(IProcess process) {
        // 步骤1.
        process.action1();
        // 步骤2.
        process.action2();
        // 步骤3.
        process.action3();
    }
}

创建动态代理调用类

public class Process2InvocationHandler implements InvocationHandler {
    private IProcess process;
    public IProcess getProcess() {
        return process;
    }
    public void setProcess(IProcess process) {
        this.process = process;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object returnValue = null;
        // 通过调用的名称来判断
        if ("action2".equals(method.getName())) {
            System.out.println("before action 2");
            returnValue = method.invoke(this.process, args);
            System.out.println("after action 2");
        } else {
            returnValue = method.invoke(this.process, args);
        }
        return returnValue;
    }
}

实现Process2

public static void main( String[] args )
{
    Process2InvocationHandler handler = new Process2InvocationHandler();
    handler.setProcess(process);
    IProcess process2 = (IProcess) Proxy.newProxyInstance(process.getClass().getClassLoader(),
                       AbstractProcess.class.getInterfaces(),
                                                          handler);
}

概念

JoinPoint:那些可以切分开的一个一个的动作或者步骤

PointCut:那些我们企图劫持的JoinPoint(例如例子中的Process2动态代理对action1进行劫持)

Advice:当劫持了一个PointCut后,需要提前或者后置的动作,这个动作通常是调用一个回调函数,这个动作是Advice。

  • 前置通知(在action以前通知)
  • 后置通知(在action以后通知)
  • 异常通知(类似于try … catch)
  • 最终通知(类似于finally)
  • 环绕通知(自己设置如何通知)

Introduction:通过反射实现的动态代理来实现Advice功能的过程

Proxy

  • Target:被代理的对象,真正干活的对象

  • Weaving:代理对象被创建的过程

    • Spring:反射实现动态代理(这里不介绍)

    • Spring + AspectJ (一个支持AOP的框架) :在编译时织入,在类装载时织入

Aspect:切面=PointCut + Advice

Spring AOP+AspectJ

maven依赖

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <!--spring依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>5.1.9.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>5.1.9.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.1.9.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-expression</artifactId>
      <version>5.1.9.RELEASE</version>
    </dependency>
    <!--spring aop依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>5.1.9.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>5.1.9.RELEASE</version>
    </dependency>
    <!--spring test-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>5.1.9.RELEASE</version>
    </dependency>
    <!--AspectJ-->
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.6</version>
    </dependency>
  </dependencies>

配置文件方式配置AOP

新建一个切面类Logger,以便观察

public class Logger {
    public void printLog(){
        System.out.println("Logger say xxx here!");
    }
    public Object processAdvice(ProceedingJoinPoint pjp){ // 环绕通知配置方式
        System.out.println("Logger before action.");
        Object rtValue = null;
        try {
            Object[] args = pjp.getArgs();
            rtValue = pjp.proceed(args);
            System.out.println("Logger after-returning action");
        } catch (Throwable e) {
            System.out.println("Logger after-throwing action");
        } finally {
            System.out.println("Logger after action");
        }
        return rtValue;
    }

}

配置文件命名为bean.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">
    <!-- Target => proxy  -->
    <bean id="process" class="cn.silvercorridors.Process"></bean>
    <!-- Logger   -->
    <bean id="logger" class="cn.silvercorridors.Logger"></bean>
    <!-- AspectJ 直接修改process,使他变成一个proxy -->
    <!--
    pointcut = " execution(<方法表达式>) "
    <方法表达式>:public void cn.silvercorridors.Process.action2()
    -->
    <aop:config>
        <!-- 设定Aspect -->
        <aop:aspect id="logAdvice" ref="logger"><!-- ref表示通知的类 -->
            <aop:before method="printLog" pointcut="execution(public void cn.silvercorridors.Process.action2())"></aop:before><!--pointCut为要拦截的方法-->
            <aop:after method="printLog" pointcut="execution(public void cn.silvercorridors.Process.action2())"></aop:after>
        </aop:aspect>
    </aop:config>
</beans>

运行函数

ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
IProcess process = (IProcess) ac.getBean("process");
Process.doActions(process);

注解方式配置AOP

新建一个配置文件类

@Configuration
@ComponentScan(basePackages = "cn.qyx")
@EnableAspectJAutoProxy
public class SpringConfiguration {

}

新建切面类Logger,以便观察,首先给Process与Logger加上与相应的component注解,然后直接在切面类中给相应的方法加注解

@Component("process")
@Component("logger")

切面类

@Component("logger")
public class Logger {
    //@Around("action2()")//环绕
    public Object processAdvice(ProceedingJoinPoint pjp){
        System.out.println("Logger before action.");
        Object rtValue = null;
        try {
            Object[] args = pjp.getArgs();
            rtValue = pjp.proceed(args);
            System.out.println("Logger after-returning action");
        } catch (Throwable e) {
            System.out.println("Logger after-throwing action");
        } finally {
            System.out.println("Logger after action");
        }
        return rtValue;
    }
    @Pointcut("execution(void cn.silvercorridors.Process.action2())")
    private void action2(){};
        
    @Before("action2()")
    public void printBefore(){
        System.out.println("Logger say Before here!");
    }
    @AfterReturning("action2()")
    public void printAfterReturn(){
        System.out.println("Logger say after return here!");
    }
    @AfterThrowing("action2()")
    public void printAfterThrowing(){
        System.out.println("Logger say after throwing here!");
    }
    @After("action2()")
    public void printAfter(){
        System.out.println("Logger say after here!");
    }

运行函数

ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
IProcess process = (IProcess) ac.getBean("process");
Process.doActions(process);

文章作者: 银色回廊
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 银色回廊 !
评论
  目录