二、Spring AOP面向切面编程

参考:

Spring AOP——Spring 中面向切面编程 - SharpCJ - 博客园

一、背景

AOP (Aspect Orient Programming),直译过来就是 面向切面编程。

AOP 是一种编程思想,是面向对象编程(OOP)的一种补充。面向对象编程将程序抽象成各个层次的对象,而面向切面编程是将程序抽象成各个切面。

1.1 为什么需要 AOP

想象下面的场景,开发中在多个模块间有某段重复的代码,我们通常是怎么处理的?显然,没有人会靠“复制粘贴”吧。在传统的面向过程编程中,我们也会将这段代码,抽象成一个方法,然后在需要的地方分别调用这个方法,这样当这段代码需要修改时,我们只需要改变这个方法就可以了。然而需求总是变化的,有一天,新增了一个需求,需要再多出做修改,我们需要再抽象出一个方法,然后再在需要的地方分别调用这个方法,又或者我们不需要这个方法了,我们还是得删除掉每一处调用该方法的地方。实际上涉及到多个地方具有相同的修改的问题我们都可以通过 AOP 来解决。

1.2 AOP 实现分类

AOP 要达到的效果是,保证开发者不修改源代码的前提下,去为系统中的业务组件添加某种通用功能。AOP 的本质是由 AOP 框架修改业务组件的多个方法的源代码,AOP 其实就是代理模式的典型应用。
按照 AOP 框架修改源代码的时机,可以将其分为两类:

  • 静态 AOP 实现, AOP 框架在编译阶段对程序源代码进行修改,生成了静态的 AOP 代理类(生成的 *.class 文件已经被改掉了,需要使用特定的编译器),比如 AspectJ。
  • 动态 AOP 实现, AOP 框架在运行阶段对动态生成代理对象(在内存中以 JDK 动态代理,或 CGlib 动态地生成 AOP 代理类),如 SpringAOP。

二、AOP 术语

AOP 领域中的特性术语:

  • 通知(Advice): AOP 框架中的增强处理。通知描述了切面何时执行以及如何执行增强处理。
  • 连接点(join point): 连接点表示应用执行过程中能够插入切面的一个点,这个点可以是方法的调用、异常的抛出。在 Spring AOP 中,连接点总是方法的调用。
  • 切点(PointCut): 可以插入增强处理的连接点。
  • 切面(Aspect): 切面是通知和切点的结合。
  • 引入(Introduction):引入允许我们向现有的类添加新的方法或者属性。
  • 织入(Weaving): 将增强处理添加到目标对象中,并创建一个被增强的对象,这个过程就是织入。

三、讲解

我的理解就是:在不修改源代码的前提下,在某些方法 之前 或者之后 添加一些 通用的功能

如: 我想在 所有调用 HelloServiceImpl.Hi 方法 的场景,这个Hi方法之前或之后,执行我新添加的一些方法

一共两步:

1、通过@Aspect 设置成切面类,里面包含自己设置的切点与想要添加的逻辑

切点就是指定,想在哪写方法加想要的逻辑

通过@Pointcut 来设置切点

2、 在执行入口,添加@EnableAspectJAutoProxy 来启动 切面

 
  

栗子

接口HelloService

package com.bt.study_spring.demo12;

public interface HelloService {
    String hi(String name);
    void hello();
}

接口实现类HelloServiceImpl 

package com.bt.study_spring.demo12;

import org.springframework.stereotype.Component;

@Component
public class HelloServiceImpl implements HelloService {
    @Override
    public String hi(String name) {
        System.out.println("大家好");

        return "hi,"+name;
    }

    @Override
    public void hello() {
        System.out.println("hello");

    }
}

运行入口APP

package com.bt.study_spring.demo12;


import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan
@EnableAspectJAutoProxy
public class App {
    public static void main(String[] args) {

        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(App.class);
        HelloService helloService = applicationContext.getBean(HelloService.class);
        String xiaoxinxin = helloService.hi("xiaoxinxin");
        helloService.hello();

        Hello1 hello1 = applicationContext.getBean(Hello1.class);
        hello1.hello();
        System.out.println(xiaoxinxin);

    }
}

--------------------------

想要在所有调用HelloServiceImpl 类下任意方法的场景下,插入一段逻辑

新建切面类LogAspect

package com.bt.study_spring.demo12;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Component
@Aspect // 切面
public class LogAspect {
    // 切点
    @Pointcut("execution(public * com.bt.study_spring.demo12.*.*(..))")
    public void log(){

    }

    /*
    @Before 前置通知,在执行方法之前,先执行before里的方法
    execution()固定写法
    public * com.bt.study_spring.demo12.HelloServiceImpl.*(..))
    public 指的是,你要控制的那个方法的访问权限
    * 返回值,不限制
    com.bt.study_spring.demo12.HelloServiceImpl 你要控制的那个方法的全类名
    * 这个类下的任意方法
    (..) 参数列表任意
    public * com.bt.study_spring.demo12.HelloServiceImpl.*(..) 代表控制到类
    public * com.bt.study_spring.demo12.*.*(..) 控制的包

     */
//    @Before("execution(public * com.bt.study_spring.demo12.*.*(..))")
    @Before("log()")
    public void before(JoinPoint point){
        // 通过 JoinPoint point 来获取 类、方法的一些信息
        String name = point.getSignature().getName();
        String declaringTypeName = point.getSignature().getDeclaringTypeName();
        System.out.println("方法名"+name);
        System.out.println("类名"+declaringTypeName );
        System.out.println("before AAAA");
    }

    /*
    @After 后置通知,在执行方法之后,再执行after里的方法
     */
    @After("execution(public * com.bt.study_spring.demo12.HelloServiceImpl.*(..))")
    public void after(){
        System.out.println("After BBB");
    }

    // 异常通知,出现异常的时候写它
    @AfterThrowing("log()")
    public void error(){
        System.out.println("error");

    }

    // 最终通知,obj用来接收返回值的
    @AfterReturning(value = "log()",returning = "obj")
    public void afterReturning(Object obj){
        System.out.println(">>>>:"+obj);
        System.out.println("afterReturning DDD");
    }


}

a、通过注解@Aspect 使这个类成为 切面,

   @Component 把这个类交给Spring管理

b、 通过@Pointcut设置切点。就是指定哪些方法来添加新逻辑


    @Pointcut("execution(public * com.bt.study_spring.demo12.*.*(..))")
    public void log(){}

  

这里就跟正则有点像,去匹配对应的类/方法

execution()固定写法
public * com.bt.study_spring.demo12.HelloServiceImpl.*(..))
public 指的是,你要控制的那个方法的访问权限
* 返回值,不限制
com.bt.study_spring.demo12.HelloServiceImpl 你要控制的那个方法的全类名
* 这个类下的任意方法
(..) 参数列表任意
public * com.bt.study_spring.demo12.HelloServiceImpl.*(..) 代表控制到类
public * com.bt.study_spring.demo12.*.*(..) 控制的包

c、通知类型:前置通知、后置通知、异常通知、环绕通知

前置通知 @Before

后置通知@After

异常通知@AfterThrowing

最终通知@AfterReturning

环绕通知 @Around

后置通知

    @After("log()")
    public void after(){
        System.out.println("After BBB");
    }

@After() 注解成为后置通知

("log()") 标明切点,(指定实现范围)

前置通知

    @Before("log()")
    public void before(JoinPoint point){
        // 通过 JoinPoint point 来获取 类、方法的一些信息
        String name = point.getSignature().getName();
        String declaringTypeName = point.getSignature().getDeclaringTypeName();
        System.out.println("方法名"+name);
        System.out.println("类名"+declaringTypeName );
        System.out.println("before AAAA");
    }

我们想获取,我们匹配的那个方法/类的一些信息,可以在传参里加

JoinPoint point

获取方法名

 String name = point.getSignature().getName();

获取方法的类名

String declaringTypeName = point.getSignature().getDeclaringTypeName();

d、启动的时候,在入口加@EnableAspectJAutoProxy

四、依赖包:

        
            org.springframework
            spring-aop
            4.3.30.RELEASE
        
        
            org.aspectj
            aspectjweaver
            1.8.9
        

五、报错处理:

运行代码报错

警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.aop.config.internalAutoProxyCreator': Initialization of bean failed; nested exception is java.lang.NoSuchMethodError: org.springframework.util.ReflectionUtils$MethodFilter.and(Lorg/springframework/util/ReflectionUtils$MethodFilter;)Lorg/springframework/util/ReflectionUtils$MethodFilter;
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.aop.config.internalAutoProxyCreator': Initialization of bean failed; nested exception is java.lang.NoSuchMethodError: org.springframework.util.ReflectionUtils$MethodFilter.and(Lorg/springframework/util/ReflectionUtils$MethodFilter;)Lorg/springframework/util/ReflectionUtils$MethodFilter;

因为缺少这个依赖包:

        
            org.aspectj
            aspectjweaver
            1.8.9
        


 

你可能感兴趣的:(JAVA测开,java)