本人本科毕业,21届毕业生,一年工作经验,简历专业技能如下,现根据简历,并根据所学知识复习准备面试。

记录日期:2022.1.21

大部分知识点只做大致介绍,具体内容根据推荐博文链接进行详细复习。

框架原理 - Spring(一) 之 Spring AOP 引入

认识AOP

概念

AOP(Aspect Orient Programming) 是一种思想,是面向对象编程的一种补充,是一种面向切面编程的思想,AOP的主要作用就是,告诉我们可以通过动态代理的方式,织入一段代码到已经写到另一段代码中去。

AOP 是 OOP 的延续,是软件开发中的一个热点,也是 Spring 框架中的一个重要内容,是函数式编程的一种衍生范型。利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低提高程序的可重用性,同时提高了开发的效率。

主要应用于处理一些具有横切性质的系统级服务,如日志收集、事务管理、安全检查、缓存、对象池管理等。

相关概念

  • Joinpoint(连接点):在系统运行之前,AOP 的功能模块都需要织入到具体的功能模块中。要进行这种织入过程,我们需要知道在系统的哪些执行点上进行织入过程,这些将要在其之上进行织入操作的系统执行点就称之为 Joinpoint,最常见的 Joinpoint 就是方法调用。
  • Pointcut(切点):用于指定一组 Joinpoint,代表要在这一组 Joinpoint 中织入我们的逻辑,它定义了相应 Advice 将要发生的地方。通常使用正则表达式来表示。对于上面的例子,Pointcut 就是表示 “所有要加入日志记录的接口” 的一个 “表达式”。例如:“execution(* com.joonwhee.open.demo.service..*.*(..))”
  • Advice(通知/增强):Advice 定义了将会织入到 Joinpoint 的具体逻辑,通过 @Before、@After、@Around 来区别在 JointPoint 之前、之后还是环绕执行的代码。
  • Aspect(切面):Aspect 是对系统中的横切关注点逻辑进行模块化封装的 AOP 概念实体。类似于 Java 中的类声明,在 Aspect 中可以包含多个 Pointcut 以及相关的 Advice 定义。
  • Weaving(织入):织入指的是将 Advice 连接到 Pointcut 指定的 Joinpoint 处的过程,也称为:将 Advice 织入到 Pointcut 指定的 Joinpoint 处。
  • Target(目标对象):符合 Pointcut 所指定的条件,被一个或多个切面所织入 Advice 的对象。

Advice 类型

  • 前置通知(Before advice):在某连接点(join point)之前执行的通知,但不能阻止连接点前的执行(除非它抛出异常)。
  • 返回后通知(After returning advice):在某连接点(join point)正常完成后执行的通知。
  • 抛出异常后通知(After throwing advice):在方法抛出异常退出时执行的通知。
  • 后通知(After(finally) advice):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。
  • 环绕通知(Around Advice):包围一个连接点(join point)的通知。

AOP 实现方式

静态织入

典型代表:AspectJ

AspectJ

AspectJ 是一个易用的功能强大的 AOP 框架。

AspectJ 全称是 Eclipse AspectJ,截至目前最新版本为:1.9.7。

官方网址:The AspectJ Project | The Eclipse Foundation

引用官网描述:

  • a seamless aspect-oriented extension to the Javatm programming language(一种基于Java平台的面向切面编程的语言)
  • Java platform compatible(兼容Java平台,可以无缝扩展)
  • easy to learn and use(易学易用)

可以单独使用,也可以整合到其它框架中。

单独使用 AspectJ 时需要使用专门的编译器 ajc,而java 的编译器是 javac,AspectJ 的编译器是 ajc,aj 是首字母缩写,c 即 compiler。

动态织入(JDK动态代理、CGLib动态代理)

典型代表:SpringAOPJbossAOP

SpringAOP

SpringAOP 是借用了 AspectJ 的配置语法,现在直接使用AspectJ做动态代理的项目很少了,都是直接使用spring,spring内置了 AspectJ。

JbossAOP

不了解。

SpringAOP 和 AspectJ 的区别?
Spring AOP
  • 基于动态代理来实现,默认如果使用接口的,用JDK提供的动态代理实现,如果是方法则使用CGLIB实现。
  • Spring AOP 需要依赖 IOC 容器来管理,并且只能作用于Spring容器,使用纯Java代码实现。
  • 在性能上,由于 Spring AOP 是基于动态代理来实现的,在容器启动时需要生成代理实例,在方法调用上也会增加栈的深度,使得Spring AOP 的性能不如AspectJ的那么好。
AspectJ
  • AspectJ来自于Eclipse基金会。

  • AspectJ属于静态织入,通过修改代码来实现,有如下几个织入的时机:

    • 编译期织入(Compile-time weaving): 如类 A 使用 AspectJ 添加了一个属性,类 B 引用了它,这个场景就需要编译期的时候就进行织入,否则没法编译类 B。

    • 编译后织入(Post-compile weaving): 也就是已经生成了 .class 文件,或已经打成 jar 包了,这种情况我们需要增强处理的话,就要用到编译后织入。

    • 类加载后织入(Load-time weaving): 指的是在加载类的时候进行织入,要实现这个时期的织入,有几种常见的方法:

      • 自定义类加载器来干这个,这个应该是最容易想到的办法,在被织入类加载到 JVM 前去对它进行加载,这样就可以在加载的时候定义行为了
      • 在 JVM 启动的时候指定 AspectJ 提供的 agent:-javaagent:xxx/xxx/aspectjweaver.jar
  • AspectJ可以做 Spring AOP 干不了的事情,它是AOP编程的完全解决方案,Spring AOP则致力于解决企业级开发中最普遍的AOP(方法织入),而不是成为像AspectJ一样的AOP方案。

  • 因为AspectJ在实际运行之前就完成了织入,所以说它生成的类是没有额外运行时开销的。

使用Spring AOP

导入依赖

使用 Spring AOP 时,我们需要引入两个包,以SpringBoot项目来说,当我们引入 Spring-boot-starter-aop 包时,里面就间接引用这两个包:

  • spring-aop:是Spring对 AOP 的具体实现的包。
  • aspectjweaver:是spring的切入点表达式需要用的包。
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>5.2.3.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.5</version>
</dependency>

创建接口和实现类

接口代码:

public interface personServer
{
   
	public void save(String uname,int age);	
}

实现类代码:

@Service
public class personServerImpl implements personServer {
   
	@Override
	public void save(String uname,int age) {
   
        //打开下面两行引起报错,可触发异常通知
		int a = 0;
		age = age / a; 
		System.out.println("come in personServerImpl save method...");
	}
}

创建切面类

然后我们来编写具体AOP切面类:

@Aspect //声明这是一个切面
@Component
public class AspectIntercepter {
   

    @Pointcut(value = "execution(* com.test.service.personServerImpl.*(..))")
    private void pointCut(){
   
        // 定义一个切入点 后面的通知直接引入切入点方法pointCut即可
        // personServerImpl下面的所有方法
    }

    //环绕通知(连接到切入点开始执行,下一步进入前置通知,在下一步才是执行操作方法)
    @Around(value="pointCut()")
    public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable{
   
        System.out.println("@Around进入环绕通知...");
        Object object = pjp.proceed(); //执行该方法
        System.out.println(pjp.getThis() + "操作结束,退出方法;环绕[@Around]结束!...");
        return object;
    }

    //前置通知(进入环绕后执行,下一步执行方法)
    @Before(value = "pointCut()")
    public void doAccessCheck(JoinPoint joinPoint) {
   
        System.out.println("@Before前置通知:" + Arrays.toString(joinPoint.getArgs()));
    }

    //异常通知(出错时执行)
    @AfterThrowing(value = "pointCut()",throwing="ex")
    public void doAfterThrow(JoinPoint joinPoint,Throwable ex) {
   
        System.out.println("@AfterThrowing例外通知(异常通知)" + Arrays.toString(joinPoint.getArgs()));
        System.out.println("@AfterThrowing异常信息:" + ex);
    }

    //后置通知(返回之前执行)
    @After(value = "pointCut()")
    public void after() {
   
        System.out.println("@After后置通知...");
    }

    //最终通知(正常返回通知,最后执行)
    @AfterReturning(value = "pointCut()")
    public void doAfter() {
   
        System.out.println("@AfterReturning最终通知...End!");
    }
}

注意点

Spring 4 和 Spring 5中AOP的执行顺序

在Spring 4 和 Spring 5中AOP的执行顺序区别:Spring4和Spring5的AOP顺序

spring默认jdk动态代理而SpringBoot默认CGLib动态代理

参考文章:惊人!Spring5 AOP 默认使用Cglib? 从现象到源码深度分析