spring家族包括很多产品和组件,比如spring-framework、springboot、springcloud等等,其中spring-framework是springboot的基础,而springboot又是springcloud的基础,本文就spring-framework5.x相关知识进行学习。本文主要以spring注解的方式进行学习和总结。
在spring-framework中,最为核心的组件为IOC和AOP,本文就AOP的基础知识进行学习和总结。文章下面所谓的spring 特指spring-framework。
注意:使用spring的AspectJ来实现AOP时,必须要使用注解@EnableAspectJAutoProxy开启对Aspectj的支持
定义:面向切面编程,就像面向对象编程一样,是一种编程思想。
当我们在做业务时,首先关注的是功能性需求的实现,同时还需要关注些其他非功能性需求,如限制用户访问频率、对用户访问进行鉴权、数据库事务、记录日志等等,而这些非功能性需求都是独立于功能性需求之外的程序逻辑,如果将其植入到功能性需求对应的代码逻辑中,那么代码将更加复杂、耦合性更高,不利于后期的系统维护,
因此我们需要一种手段,让功能性需求和非功能性需求分开,同时还能实现功能需求和这些非功能需求,而AOP面向切面编程的思想就是解决这个问题的一种手段。
AOP是一种编程思想,而springAOP只是AOP的一种实现方式,可以理解成AOP是一个接口,而springAOP是一个具体的实现。
在spring中,有两种方式都能实现AOP:
注意:spring的AspectJ并不是以AspectJ为基础进行的扩展,而只是借助了AspectJ的语法而已。AspectJ也是一种AOP的实现方式,但和spring是竞争关系。
这样说还是比较抽象,我们看看如下代码,结合注释,就很容易理解上述相关名称了:
@Component //需要让spring加载
@Aspect //声明这是一个切面类
public class MyAop {
/**
* Pointcut 定义一个切点
* execution(* xx.UserController.*(..)) 类xx.UserController下的每个方法为一个连接点
* 这些连接点一起组成一个切点
*/
@Pointcut("execution(* com..study.spring.controller.UserController.*(..))")
private void cutMethod() {}
/**
* 通知,包括2方面,
* 执行时机:在执行目标方法的前后执行,目标方法为切点cutMethod()对应的切点
* 执行逻辑:方法的代码即为执行逻辑
*/
@Around("cutMethod()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("AOP前操作");
Object object = joinPoint.proceed();
System.out.println("AOP后操作");
return object;
}
}
具体逻辑见如下示例和示例结果
@Component
@Aspect
public class MyAop {
@Pointcut("execution(* com..study.spring.controller.UserController.*(..))")
private void cutMethod() {}
@Before("cutMethod()")
public void befor(){
System.out.println("Before前置通知");
}
@AfterReturning("cutMethod()")
public void afterReturning(){
System.out.println("AfterReturning返回通知");
}
@AfterThrowing("cutMethod()")
public void afterThrowing(){
System.out.println("AfterThrowing异常通知");
}
@After("cutMethod()")
public void after(){
System.out.println("After结束通知");
}
@Around("cutMethod()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("AOP环绕通知-前操作");
Object object = joinPoint.proceed();
System.out.println("AOP环绕通知-后操作");
return object;
}
}
上述代码,如果没有抛出异常,则执行结果如下:
同样是上述代码,如果目标方法抛出异常,则执行结果如下:
需求:对UserServiceImpl类的方法进行增强
@Component
public class UserServiceImpl{
public List<UserPO> findAll(){
List<UserPO> list = new ArrayList<>();
System.out.println("UserServiceImpl......");
return list;
}
}
@Component //需要让spring加载
@Aspect //声明这是一个切面类
public class MyAop {
/**
* Pointcut 定义一个切点
* execution(* xx.UserServiceImpl.*(..)) 类UserServiceImpl下的每个方法为一个连接点
* 这些连接点一起组成一个切点
*/
@Pointcut("execution(* com.study.spring.service.UserServiceImpl.*(..))")
private void cutMethod() {}
/**
* 通知,包括2方面,
* 执行时机:在执行目标方法的前后执行,目标方法为切点cutMethod()对应的切点
* 执行逻辑:方法的代码即为执行逻辑
*/
@Around("cutMethod()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("AOP前操作");
Object object = joinPoint.proceed();
System.out.println("AOP后操作");
return object;
}
}
@ComponentScan("com.study.spring")
@EnableAspectJAutoProxy //切记要加上这个注解,开启AspectJ的支持
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main.class);
UserService bean = context.getBean(UserService.class);
bean.findAll();
System.out.println("1111111");
}
}
我们先复习下springIOC文章中,Java bean变成springbean的一个过程,即spring启动过程,如下图所示:
通过上一篇springIOC文章的学习和总结,我们知道,spring会为目标对象生成一个代理对象,并将代理对象存储到单例池中,那么spring是如何根据目标对象生成代理对象的呢?
当目标对象经历实例化、初始化等操作后,spring会调用所有的BeanPostProcessor,进行初始化后处理,即postProcessAfterInitialization方法。
在这些后置处理器中,有一个叫AnnotationAwareAspectJAutoProxyCreator的处理器,
在其父类AbstractAutoProxyCreator中实现了BeanPostProcessor接口的前后处理方法,前置处理方法直接返回bean,后置方法如下图所示:
wrapIfNecessary方法内容如下:
这个方法先从缓存中获取,如果获取到对象了则直接返回,否则就会创建一个对象的代理,缓存代理并返回代理对象。createProxy源码如下:
该方法主要是创建一个代理工厂,并对代理工厂进行相关设置,重点在最后一句代理工厂获取代理。
进入createAopProxy方法:
getAopProxyFactory返回一个AopProxyFactory类型对象。AopProxyFactory是一个接口,并且只有一个实现类DefaultAopProxyFactory。接下来看看createAopProxy方法。
该方法根据给定的类配置来创建不同的代理 AopProxy,该方法返回一个AopProxy,AopProxy是一个接口,有3个实现类:CglibAopProxy、JdkDynamicAopProxy、ObjenesisCglibAopProxy(该类为CglibAopProxy的子类)。
JdkDynamicAopProxy的继承关系如下:
CglibAopProxyCglibAopProxy 继承结构没什么,主要是众多内部类,这些内部类是为了实现了 Cglib 的各种回调而实现的。主要实现了 MethodInterceptor 接口,
Callback 接口,Joinpoint 接口,Invocation 接口等待,总之是实现了Spring 的 cglib 模块的各种接口。
最终返回一个AopProxy对象,可能是jdk动态代理也可能是CGLIB的动态代理。回到getProxy方法中,根据AopProxy创建具体对象。
下图为JdkDynamicAopProxy方式创建代理:
下图为CglibAopProxy方式创建代理:
如上2图所示,这里我们能看到jdk或者CGLIB包中的相关类了,到此,代理对象创建完毕。重点类和方法调用时序图:
上述就是springAOP的全部知识,需要能帮助到大家。