百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程网 > 正文

源码分析:Spring是如何实现AOP的?

yuyutoo 2025-01-23 22:12 3 浏览 0 评论

什么是AOP

AOP(Aspect Oriented Programming),即面向切面编程,是Spring框架中的一个核心内容,主要目的是对业务逻辑进行隔离,让业务代码更加关注于实现功能,降低耦合度

举个例子,AOP的典型使用场景就是日志和安全,比如我们要记录一段代码的执行时间,很容易想到的方法就是在执行的开始处打印一个时间,在执行的结尾处再打印一个时间,两个时间相减就是代码真正的执行时间。

但这样会有一个问题,就是假如我有很多这样的方法需要统计执行时间,难道我每一个方法都要去加这个时间打印吗?

AOP可以解决这样的问题,它通过配置的方式可以实现对类的增强,在不改动原有方法的基础上进行功能的增强

这样的话,不管有多少个地方需要统计执行时间,我们都可以轻松的实现,下面来看一下AOP的原理

Spring AOP的原理

Spring AOP的实现原理是Java的动态代理

动态代理,说的是在程序运行期间动态的为目标类创建一个代理类,从而实现为目标类额外添加一些功能的目的

下面用一张图来说明一下

请原谅我的画图技术 ^ _ ^

  • 从IOC容器中获取普通对象时,容器会创建一个对象的实例并返回
  • 从IOC容器中获取一个需要被增强的对象时,会创建两个对象:代理对象、目标对象,我们实际调用的是代理对象,由代理对象再调用目标对象

想想为啥会多出一个代理对象?

上面提到AOP的指职是要实现在类上增加一些额外的功能,那么在不修改目标类的情况下,就会生成一个代理类,这个代理类相当于对目标类进行了一层包装,它包含目标类的所有功能并且还额外的新增了一些功能点

反正原理就是这么个原理~~~

AOP中几个重要的概念

在介绍这些概念之前,我们先想一个问题,假如让你来设计AOP,你应该怎么实现?

首先,我们知道AOP的主要用途是在不改变目标类的情况下,对目标类添加一些额外的功能进行增强,主要原理是使用动态代理来动态的生成代理对象,在代理对象中添加额外的功能

那么,我们要生成代理对象,肯定需要搞清楚几个问题

  • 我需要对谁生成代理对象(目标对象)
  • 哪些地方可以让我来添加额外的功能(连接点)
  • 实际我需要添加额外功能的点(切点)
  • 我需要在目标对象执行之前或之后来添加额外的功能(增强)

以上问题可以表述为AOP里面几个重要的概念

  • Joint point(连接点),表示程序中明确定义的点,比如方法调用、成员访问、异常处理这些点都是AOP可以织入的点
  • Pointcut(切点),表示AOP具体需要织入的点,也就是从Joint point(连接点)中过滤出来的一些点
  • Advice(增强),表示我要对需要强入的点做什么操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码
  • Target(目标对象),需要增强的具体对象
  • Weaving(织入),将额外的功能添加到目标对象的过程
  • Aspect(切面),PointCut和Advice的组合

Advice类型:

  • before advice 在 join point 前被执行的 advice
  • after return advice 在join point 正常返回后执行的 advice
  • after throwing advice 当一个 join point 抛出异常后执行的 advice
  • after(final) advice 无论一个 join point 是正常退出还是发生了异常, 都会被执行的 advice.
  • around advice 在 join point 前和 joint point 退出后都执行的 advice. 这个是最常用的 advice
  • introduction 可以为原有的对象增加新的属性和方法

理解了这些概念,才能对AOP有全面的认识

看个例子吧

1、定义两个接口

public interface UserService {
    public void login();
}

public interface OrderService {
    public void commitOrder();
}

2、实现

@Component
public class AppUserService implements UserService {
    @Override
    public void login() {
        System.out.println("APP用户登录");
    }
}

@Component
public class AppOrderService implements OrderService {
    @Override
    public void commitOrder() {
        System.out.println("App提交订单");
    }
}

@Component
public class WechatUserService {
    public void login() {
        System.out.println("微信用户登录");
    }
}

3、定义切面

@Aspect
@Component
public class UserAspectJ {
    @Before("execution(* org.kxg.springDemo.aop.UserService.login(..)) || execution(* org.kxg.springDemo.aop.WechatUserService.login(..))")
    public void mark(){
        System.out.println("登录之前记录一下时间");
    }
}

从这里可以看出来,我们对接口和普通类都做了增强处理

4、使用自动配置的加载类

@Configuration
@ComponentScan(basePackages = "org.kxg.springDemo.aop")
@EnableAspectJAutoProxy
public class AppConfig {

}

5、测试一下

public class AopMain {
    public static void main(String[] args){
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        UserService appUserService = context.getBean("appUserService",UserService.class);
        WechatUserService wechatUserService = context.getBean("wechatUserService",WechatUserService.class);
        OrderService orderService = context.getBean("appOrderService",OrderService.class);
        appUserService.login();
        wechatUserService.login();
        orderService.commitOrder();
    }
}

运行结果:

登录之前记录一下时间
APP用户登录
登录之前记录一下时间
微信用户登录
App提交订单

可以看出来,在执行APP用户登录和微信用户登录之前,执行了我们切面中的mark方法,因为我们定义的是前置通知(@Before),会在目标方法之前执行

下面我们通过源码的角度来看一下Spirng AOP是如何实现的

Spring AOP是如何实现的

我们都知道AOP的实现方法是动态代理,就是在程序运行期间动态的为目标类创建代理,从而实现目标类的增强

Java的动态代理有两种实现方式:

  • JDK的动态代理
  • CGlib动态代理

这两种动态代理的主要区别:

  • JDK是通过代理类跟目标类实现同一个接口来实现代理的
  • CGlib是通过继承目标类来实现代理的

一个是实现接口,一个是类继承

从上面的例子可以看出来,Spring对于接口和普通类都可以实现AOP增强,那Spring在实现AOP的时候到底是用的JDK动态代理,还是CGlib动态代理呢?

官方给出的解释是,对实现了接口的类进行增强时使用JDK的动态代理,普通类的增强使用CGlib

下面我们来看一下是不是如官方所说

AppUserService是实现了接口的类,使用的是JDK动态代理

WechatUserService是个普通类,使用的是CBLIB动态代理

既然CGLIB可以实现普通类的增强,那它必然也是支持实现了接口的类的增强,因为实现了接口的类也是普通类

我们只要修改一个参数,就可以实现全部使用CGLIB动态代理

@Configuration
@ComponentScan(basePackages = "org.kxg.springDemo.aop")
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AppConfig {

}

proxyTargetClass = true 表示所有类都使用CGLIB动态代理,我们修改参数后,运行结果如下图所示


可以看出来,AppUserService是实现了接口的类,默认情况下是使用JDK动态代理,当把@EnableAspectJAutoProxy注解的proxyTargetClass属性改为true时,所有类都使用CBLIB动态代理

下面我们来具体跟一下代码,看看Spring是如何实现AOP的

首先我们还是从入口处开始

public class AopMain {
    public static void main(String[] args){
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        UserService appUserService = context.getBean("appUserService",UserService.class);
        WechatUserService wechatUserService = context.getBean("wechatUserService",WechatUserService.class);
        OrderService orderService = context.getBean("appOrderService",OrderService.class);
        appUserService.login();
        wechatUserService.login();
        orderService.commitOrder();
    }
}

当从Spring IOC容器中获取对象时,假如对象需要进行AOP增强,就会生成代理对象,所以我们从getBean方法入手,getBean就是从容器中获取对象

从Spring容器中获取对象的过程,这里就不再详细展开了,我们只看主要的方法

//AbstractAutowireCapableBeanFactory(类名)
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
		if (System.getSecurityManager() != null) {
			AccessController.doPrivileged((PrivilegedAction) () -> {
				invokeAwareMethods(beanName, bean);
				return null;
			}, getAccessControlContext());
		}
		else {
			invokeAwareMethods(beanName, bean);
		}

		Object wrappedBean = bean;
		if (mbd == null || !mbd.isSynthetic()) {
			wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
		}

		try {
			invokeInitMethods(beanName, wrappedBean, mbd);
		}
		catch (Throwable ex) {
			throw new BeanCreationException(
					(mbd != null ? mbd.getResourceDescription() : null),
					beanName, "Invocation of init method failed", ex);
		}
		if (mbd == null || !mbd.isSynthetic()) {
			wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
		}

		return wrappedBean;
	}

从方法名就可以看出来,initializeBean方法主要是用来做Bean初始化的,在初始化之后有一个后处理的方法
applyBeanPostProcessorsAfterInitialization

这个方法就是用来处理动态代理的

//AbstractAutowireCapableBeanFactory(类名)
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
			throws BeansException {

		Object result = existingBean;
		for (BeanPostProcessor processor : getBeanPostProcessors()) {
			Object current = processor.postProcessAfterInitialization(result, beanName);
			if (current == null) {
				return result;
			}
			result = current;
		}
		return result;
	}
//AbstractAutoProxyCreator(类名)
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
		if (bean != null) {
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			if (this.earlyProxyReferences.remove(cacheKey) != bean) {
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		return bean;
	}
//AbstractAutoProxyCreator(类名)
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
		if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
			return bean;
		}
		if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
			return bean;
		}
		if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
			this.advisedBeans.put(cacheKey, Boolean.FALSE);
			return bean;
		}

       //注意看这里的注释:这里就是创建代理对象的地方,如果有AOP的advice就创建代理对象
		// Create proxy if we have advice.
		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
		if (specificInterceptors != DO_NOT_PROXY) {
			this.advisedBeans.put(cacheKey, Boolean.TRUE);
			//这里进行代理对象的创建
			Object proxy = createProxy(
					bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
			this.proxyTypes.put(cacheKey, proxy.getClass());
			return proxy;
		}

		this.advisedBeans.put(cacheKey, Boolean.FALSE);
		return bean;
	}
//AbstractAutoProxyCreator(类名)
protected Object createProxy(Class beanClass, @Nullable String beanName,
			@Nullable Object[] specificInterceptors, TargetSource targetSource) {

		if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
			AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
		}

		ProxyFactory proxyFactory = new ProxyFactory();
		proxyFactory.copyFrom(this);

		if (!proxyFactory.isProxyTargetClass()) {
			if (shouldProxyTargetClass(beanClass, beanName)) {
				proxyFactory.setProxyTargetClass(true);
			}
			else {
				evaluateProxyInterfaces(beanClass, proxyFactory);
			}
		}

		Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
		proxyFactory.addAdvisors(advisors);
		proxyFactory.setTargetSource(targetSource);
		customizeProxyFactory(proxyFactory);

		proxyFactory.setFrozen(this.freezeProxy);
		if (advisorsPreFiltered()) {
			proxyFactory.setPreFiltered(true);
		}

		return proxyFactory.getProxy(getProxyClassLoader());
	}
public Object getProxy(@Nullable ClassLoader classLoader) {
		return createAopProxy().getProxy(classLoader);
	}

这里可以看到,获取代理对象的具体实现有两种,分别对应的是JDK的动态代理和CGLIB的动态代理

//JdkDynamicAopProxy(类名)
public Object getProxy(@Nullable ClassLoader classLoader) {
		if (logger.isTraceEnabled()) {
			logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
		}
		Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
		findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
		//JDK动态代理创建代理对象
		return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
	}
//CglibAopProxy(类名)
public Object getProxy(@Nullable ClassLoader classLoader) {
		if (logger.isTraceEnabled()) {
			logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource());
		}

		try {
			Class rootClass = this.advised.getTargetClass();
			Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");

			Class proxySuperClass = rootClass;
			if (ClassUtils.isCglibProxyClass(rootClass)) {
				proxySuperClass = rootClass.getSuperclass();
				Class[] additionalInterfaces = rootClass.getInterfaces();
				for (Class additionalInterface : additionalInterfaces) {
					this.advised.addInterface(additionalInterface);
				}
			}

			// Validate the class, writing log messages as necessary.
			validateClassIfNecessary(proxySuperClass, classLoader);

			// Configure CGLIB Enhancer...
			Enhancer enhancer = createEnhancer();
			if (classLoader != null) {
				enhancer.setClassLoader(classLoader);
				if (classLoader instanceof SmartClassLoader &&
						((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
					enhancer.setUseCache(false);
				}
			}
			enhancer.setSuperclass(proxySuperClass);
			enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
			enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
			enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));

			Callback[] callbacks = getCallbacks(rootClass);
			Class[] types = new Class[callbacks.length];
			for (int x = 0; x < types.length; x++) {
				types[x] = callbacks[x].getClass();
			}
			// fixedInterceptorMap only populated at this point, after getCallbacks call above
			enhancer.setCallbackFilter(new ProxyCallbackFilter(
					this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
			enhancer.setCallbackTypes(types);

			// Generate the proxy class and create a proxy instance.
			return createProxyClassAndInstance(enhancer, callbacks);
		}
		catch (CodeGenerationException | IllegalArgumentException ex) {
			throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +
					": Common causes of this problem include using a final class or a non-visible class",
					ex);
		}
		catch (Throwable ex) {
			// TargetSource.getTarget() failed
			throw new AopConfigException("Unexpected AOP exception", ex);
		}
	}
protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
		enhancer.setInterceptDuringConstruction(false);
		enhancer.setCallbacks(callbacks);
		//CGLIB创建代理对象实例
		return (this.constructorArgs != null && this.constructorArgTypes != null ?
				enhancer.create(this.constructorArgTypes, this.constructorArgs) :
				enhancer.create());
	}

至此两种类型的动态代理对象都创建好了,从Spring容器中获取对象时,返回的就是代理的对象,我们在代理对象中额外添加的功能也就可以生效了

现在来整体梳理一下整个SpringAop的具体流程

  • Spirng会把类装载到IOC容器中
  • 当需要创建对象时,会直接从IOC容器中获取
  • 从IOC容器中获取对象时,发现对象需要进行增强时就会生成代理对象,将增强部分生成到代理类中
  • 实际业务中调用的是增强后的代理对象,由代理对象再去调用目标对象,代理对象相当于做了一层包装

总结一下

  • AOP是Spring框架中的一个核心内容,主要目的是对业务逻辑进行隔离,让业务代码更加关注于实现功能,降低耦合度
  • Spring AOP的实现原理是Java的动态代理
  • Java的动态代理有两种实现方式
  • JDK动态代理
  • CGLIB动态代理
  • AOP中几个重要的概念
  • Joint point(连接点),表示程序中明确定义的点,比如方法调用、成员访问、异常处理这些点都是AOP可以织入的点
  • Pointcut(切点),表示AOP具体需要织入的点,也就是从Joint point(连接点)中过滤出来的一些点
  • Advice(增强),表示我要对需要强入的点做什么操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码
  • Target(目标对象),需要增强的具体对象
  • Weaving(织入),将额外的功能添加到目标对象的过程
  • Aspect(切面),PointCut和Advice的组合
  • Spring对于实现了接口的类默认情况下是使用JDK动态代理生成代理对象,对没有实现接口的类使用CGLIB动态代理生成代理对象,当然你也可以修改配置来让所有的情况都通过CGLIB动态代理来生成代理对象
  • SpringAOP的具体实现流程
  • Spirng会把类装载到IOC容器中
  • 当需要创建对象时,会直接从IOC容器中获取
  • 从IOC容器中获取对象时,发现对象需要进行增强时就会生成代理对象,将增强部分生成到代理类中
  • 实际业务中调用的是增强后的代理对象,由代理对象再去调用目标对象,代理对象相当于做了一层包装

作者:果子爸聊技术

原文链接:
https://blog.csdn.net/pzjtian/article/details/110354827

相关推荐

WMS系统设计拆解-入库管理1(wms入库作业流程图)

入库业务是仓储管理系统(WMS)中最基础、最关键的环节之一,直接影响到库存准确性、作业效率以及后续的出库和库内操作。作为仓储产品经理,深入理解入库业务的复杂性并设计出高效、灵活的解决方案,是提升仓储整...

WMS系统的序列号管理(smart cover序列号)

在现代物流和仓储管理中,序列号管理扮演着至关重要的角色。它不仅帮助企业追踪商品流转,还能有效防止盗窃和伪造。然而,许多企业在实施序列号管理时常常面临各种挑战。本文将详细介绍在WMS(仓库管理系统)中,...

什么是系统管理?#企业管理(系统管理主要包括哪些内容?)

什么是系统管理?邱奕玮,营利增长地图顾问。我们老说要搭建管理系统,搭建管理体系到底什么是管理系统?很多时候老板其实概念是很模管理,给员工说你要做流程,员工就做一套流程文件,然后又说要做绩效考,员工又做...

不愧是总经理,加班两周整理的“公司各系统管理流程汇编”太神了

...

公务员管理系统(公务员管理系统hzb怎么导出)

公务员管理系统headerfooter...

物流管理系统(比亚迪物流管理系统)

以管理员身份登录,进入系统业绩管理页面。1、创建界面后,继续完成相关操作。2、根据业绩管理模块的指引进行软件操作。3、更多模块请参见哲程CRM客户管理软件物流版官方操作手册。(9744083)...

更多驱动适配 + 跨平台共享,deepin深度操作系统打印管理升级

IT之家4月8日消息,深度操作系统今日发文宣布:为了解决打印机驱动的兼容性问题,deepin正式推出全新驱动下载平台,并升级了打印管理器,重点扩展驱动覆盖范围、优化跨平台兼容性、并深度支持...

ERP系统功能拆解——生产管理是什么?怎么用?

本文深入探讨了ERP系统中的生产管理功能,揭示了如何通过高效的计划和控制来优化生产过程。从生产工单的制定到生产入库,每一个环节都被详细拆解,展示了简道云ERP管理系统如何实现流程自动化和数据跟踪。这是...

如何在NetBeans IDE中写出更好的Javadoc

作为一名在IDR解决方案上的开发者,我花了我很多时间来增加Javadoc中JPedal的JavaPDF库,使之更易于使用。我认为这可能是非常有用的,显示你的NetBeansIDE中如何简化这个过程...

6个最佳的Netbeans扩展插件(netbeans gui)

作为一个IDR解决方案的开发者,我花费大量的时间了解NetBeans的PDF查看器和NetBeans插件。下面跟大家分享一下哪些插件是比较值得安装的。下面是我认为比较有趣并值得安装的6个NetBean...

HTTP404...前端必知,精辟简介(前端设置https)

1、介绍HTTP,超文本传输协议,是互联网中最为常用的一种网络协议。2、组成HTTP协议有HTTP请求和HTTP响应组成。...

会计人的 Python 速成指南:20 个常用代码块解析

--【AI会计革命:从账房先生到数据军师的跨界突围】【能力重构篇】泰山医院岳涛引言在数字化浪潮的冲击下,会计行业正经历着深刻变革。传统的账房先生式工作模式逐渐被智能化、自动化的流程所取代。对于会...

EasyExcel导出Excel表格到浏览器,通过Postman测试导出Excel

一、前言小编最近接到一个导出Excel的需求,需求还是很简单的,只需要把表格展示的信息导出成Excel就可以了,也没有复杂的合并列什么的。...

用 Superb AI Suite 和 NVIDIA TAO Toolkit 创建高质量的计算机视觉应用

数据标记和模型训练一直被认为是团队在构建AI或机器学习基础设施时所面临的最大挑战。两者都是机器学习应用开发过程中的重要步骤,如果执行不当就会导致结果不准确和性能下降。...

二 计算机网络 前端学习 物理层 链路层 网络层 传输层 应用层 HTTP

一、基础概念请求和响应报文客户端发送一个请求报文给服务器,服务器根据请求报文中的信息进行处理,并将处理结果放入相应报文中返回给客户端。...

取消回复欢迎 发表评论: