@Test

public void AOPTest(){

AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

UserService bean = ac.getBean(UserService.class);

bean.queryAll();

}

我们运行 AOPTest 方法,可以看到控制台打印出:

image-20210107221911049

咱们就在以上代码的基础上对功能进行增强。

1. 直接增强

现在我们需要给业务代码执行前后加上打印日志,没有aop的时候,咱们可以直接在 service 业务中增加相关方法进行增强:

@Service

public class UserService {

public void queryAll(){

System.out.println(“before----业务代码执行前打印日志…”);

System.out.println(“业务代码:查询所有数据”);

System.out.println(“after----业务代码执行前打印日志…”);

}

}

这样一来,就把增强代码和业务代码放到了一起,这是很不合理的,并且增加了耦合,不利于代码的拓展。

2. 动态代理增强

所谓的动态代理,需要一个代理类,这个代理类是动态生成的,字节码要用的时候就创建,要用的时候就加载,在不修改源码的基础上对方法进行增强。有两种代理机制,一种是基于JDK的动态代理,另一种是基于CGLib的动态代理,bean没有接口时使用 CGLib 代理,bean有接口则使用 JDK 代理。由于上面的案例中没有使用接口,所以这里用CGLib代理。

有关动态代理可以参考之前的博客:https://blog.csdn.net/One_L_Star/article/details/101016627

@Test

public void AOPTest1(){

final UserService bean = new UserService();

UserService cglibProducer = (UserService) Enhancer.create(bean.getClass(), new MethodInterceptor(){

/**

  • 作用:执行被代理对象的任何借口方法都会经过该方法

  • @param proxy:代理对象的引用

  • @param method:当前执行的方法

  • @param args:当前执行方法所需的参数

  • @return:和被代理对象方法有相同的返回值

  • @throws Throwable

*/

@Override

public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)

throws Throwable {

System.out.println(“记录日志”);

Object result = method.invoke(bean, args);

return result;

}

});

cglibProducer.queryAll();

}

执行后打印如下:

image-20210108105149157

可以看到,经过CGLib代理后,不修改业务代码的基础上,对方法进行了增强,而在spring aop 的底层,也是使用的动态代理,不过要远远复杂于上面的代码,如果要深究,需要查看spring的源码,这里只讲基本原理,源码有点太费头发。

3. AOP切面增强

最后,咱们来看看spring是如何增强的,AOP是一个标准规范,而为了实现这个标准规范,有几种方式:

  1. 基于代理的AOP

  2. @AspectJ注解驱动的切面

  3. 纯POJO切面

  4. 注入式AspectJ切面

这四种方式都是实现aop的方法,这里讲一下通过AspectJ提供的注解实现AOP,但在spring官网中,有AspectJ 的概念,主要是因为在spring2.x的时候,spring aop的语法过于复杂,spring想进行改进,而改进的时候就借助了AspectJ 的语法、编程风格来完场aop的配置功能,这里使用AspectJ 注解方式来实现。

  • @EnableAspectJAutoProxy注解

在配置类中添加@EnableAspectJAutoProxy注解,开启切面编程功能,添加后如下:

@Configuration

@ComponentScan(“com.star”)

@EnableAspectJAutoProxy

public class AppConfig {

}

  • 增加切面类

使用@Aspect注解声明一个切面,并使用@Before、@After等注解表明连接点

@Aspect

@Component

public class LogAspect {

@Pointcut(“execution(* com.star.service….(…))”)

public void pointCut(){};

@Before(“pointCut()”)

public void logStart(){

System.out.println(“查询之前打印日志…”);

}

@After(“pointCut()”)

public void logEnd(){

System.out.println(“查询之后打印日志…”);

}

@AfterReturning(“pointCut()”)

public void logReturn(){

System.out.println(“查询之后正常返回…”);

}

@AfterThrowing(“pointCut()”)

public void logException(){

System.out.println(“查询之后返回异常…”);

}

}

  • 测试运行类不变

@Test

public void AOPTest(){

AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

UserService bean = ac.getBean(UserService.class);

bean.queryAll();

}

直接运行测试类,可以看到对方法进行了增强

image-20210108113000239

直接获取一个代理对象 ,首先产生一个目标对象,然后对目标对象进行代理,返回代理对象,把目标对象放到了map中

在spring初始化的时候就已经完成了代理,也就是执行下面代码的时候就完成了代理

AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AopConfig.class);

三、AOP原理理解


1. AOP术语

术语的理解参考:https://yq.aliyun.com/articles/638791

在上面,我们已经通过实例实现了通过AOP对方法进行增强,现在我们来理解一下,首先,我们必须要了解AOP的术语,这些术语在上面的AOP切面增强案例中都有体现,这里结合案例来理解一下。

  • 连接点(Joinpoint):连接点的最小单位称之为方法,每一个方法称之为连接点,如类开始初始化前、类初始化后、类某个方法调用前、调用后、方法抛出异常后。一个类或一段程序代码拥有一些具有边界性质的特定点,这些点中的特定点就称为“连接点”。在上面的案例中,@Before、@After等注解所在的方法都称之为连接点

  • 切点(Pointcut):切点是连接点的集合,每个程序类都拥有多个连接点,如一个拥有两个方法的类,这两个方法都是连接点,即连接点是程序类中客观存在的事物。AOP通过“切点”定位特定的连接点。连接点相当于数据库中的记录,而切点相当于查询条件。切点和连接点不是一对一的关系,一个切点可以匹配多个连接点。在上面案例中,@Pointcut(“execution(* com.star.service….(…))”)就是一个切点

连接点是一个比较空泛的概念,就是定义了哪一些地方是可以切入的,也就是所有允许你通知的地方。

切点就是定义了通知被应用的位置 (配合通知的方位信息,可以确定具体连接点)

  • 通知(Advice):切入连接点的时机和切入连接点的内容称为通知,Spring切面可以应用5种类型的通知:

  • 前置通知(Before):在目标方法被调用之前调用通知功能;

  • 后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么;

  • 返回通知(After-returning):在目标方法成功执行之后调用通知;

  • 异常通知(After-throwing):在目标方法抛出异常后调用通知;

  • 环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。

通知就定义了,需要做什么,以及在某个连接点的什么时候做。 上面的切点定义了在哪里做。

  • 目标对象(Target):指的是被增强的对象,也就是被通知的对象,也就是真正的业务逻辑,在上面案例中,UserService就是目标对象

  • 引介(Introduction):允许我们向现有的类添加新方法属性。通过引介,我们可以动态地为该业务类添加接口的实现逻辑,让业务类成为这个接口的实现类。

  • 织入(Weaving):织入是将通知添加到目标类具体连接点上的过程。AOP像一台织布机,将目标类、通知或引介通过AOP这台织布机天衣无缝地编织到一起。根据不同的实现技术,AOP有三种织入的方式:

  1. 编译期织入,这要求使用特殊的Java编译器。

  2. 类装载期织入,这要求使用特殊的类装载器。
    自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后总结

搞定算法,面试字节再不怕,有需要文章中分享的这些二叉树、链表、字符串、栈和队列等等各大面试高频知识点及解析

最后再分享一份终极手撕架构的大礼包(学习笔记):分布式+微服务+开源框架+性能优化

image

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
!(备注Java获取)**

img

最后总结

搞定算法,面试字节再不怕,有需要文章中分享的这些二叉树、链表、字符串、栈和队列等等各大面试高频知识点及解析

最后再分享一份终极手撕架构的大礼包(学习笔记):分布式+微服务+开源框架+性能优化

[外链图片转存中…(img-RKADJ7Kb-1713426458375)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐