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

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

很多朋友对于源码分析:Spring是如何实现AOP的?和不太懂,今天就由小编来为大家分享,希望可以帮助到大家,下面一起来看看吧!

AOP可以解决此类问题。可以在不改变原有方法的情况下,通过配置来增强类,增强功能。

这样的话,无论有多少地方需要统计执行时间,我们都可以轻松实现。我们先来看看AOP的原理。

Spring AOP的原理

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

动态代理是指在程序运行过程中动态地为目标类创建代理类,从而达到为目标类添加一些附加功能的目的。

我们用一张图来解释一下

请原谅我的画技^_^

当从IOC容器获取普通对象时,容器将创建该对象的实例并返回它。当从IOC容器获取需要增强的对象时,会创建两个对象:代理对象和目标对象。我们实际上调用的是代理对象。然后代理对象调用目标对象。想想为什么多了一个代理对象呢?

前面说过,AOP的目的就是给类添加一些额外的功能。然后,在不修改目标类的情况下,就会生成一个代理类。这个代理类相当于对目标类进行了一层包装。包含了目标类的所有功能,还增加了一些额外的功能点

反正就是这个道理~~~

AOP中几个重要的概念

在介绍这些概念之前,我们先思考一个问题。如果让你设计AOP,你应该如何实现?

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

所以,如果我们想要生成一个代理对象,肯定需要弄清楚几个问题

我需要知道谁生成代理对象(目标对象)以及我可以在哪里添加附加功能(连接点)。我需要添加额外功能的实际点(切点)。我需要在目标对象执行之前或之后添加附加功能。功能(增强) 以上问题可以表述为AOP中的几个重要概念

联合点(connection point)代表程序中明确定义的点,例如方法调用、成员访问、异常处理等。这些点都是AOP可以织进去的点。 Pointcut(切点)代表AOP需要织入的具体点。还有就是从joint point(连接点)过滤出来的一些点advice(enhancement),表示我要对需要强行进去的点做哪些操作。它以before、after和around来区分,也就是before以及每个关节点之后。或者不是执行代码Target(目标对象),而是需要增强(编织)的具体对象,为目标对象添加额外功能的流程Aspect(方面),PointCut和Advice Advice类型的组合:

before 建议在连接点之前执行的建议after return 建议在连接点之后执行的建议正常返回after throwing 建议在连接点之后执行的建议抛出异常after (final) 建议无论连接点正常退出还是异常退出发生异常。 Advice.around通知将在连接点之前和连接点退出之后执行。这是最常用的建议介绍。您可以向原始对象添加新的属性和方法。只有理解了这些概念,才能对AOP有一个全面的理解。理解

看个例子吧

1.定义两个接口

public interface UserService { public void login();} public interface OrderService { public void commitOrder();} 2. 实现

@Componentpublic class AppUserService Implements UserService { @Override public void login() { System.out.println(‘APP用户登录’); } }}@Componentpublic class AppOrderService Implements OrderService { @Override public void commitOrder() { System.out.println(‘应用提交订单’); } }}@Componentpublic class WechatUserService { public void login() { System.out.println(‘微信用户登录’); }} 3. 定义方面

@Aspect@Componentpublic 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’)@EnableAspectJAutoProxypublic 类AppConfig {}5.测试一下

公共类AopMain { 公共静态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是如何实现AOP的?

记录登录前的时间。 APP用户登录。 记录登录前的时间。 微信用户登录App提交订单。可以看到,在执行APP用户登录和微信用户登录之前,都会执行我们Aspect中的mark方法,因为我们定义的是前缀Notification(@Before),会在目标方法之前执行

我们从源码的角度看一下Spirng AOP是如何实现的。

Spring AOP是如何实现的

我们都知道AOP的实现方式是动态代理,就是在程序运行过程中动态地为目标类创建代理,从而实现对目标类的增强。

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

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

JDK通过在代理类和目标类之间实现相同的接口来实现代理。 CGlib通过继承目标类来实现代理。一是实现接口,二是类继承。

从上面的例子可以看出,Spring既可以对接口实现AOP增强,也可以对普通类实现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的。

首先我们从入口开始

公共类AopMain { 公共静态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(类名)受保护的对象initializeBean(final String beanName, Final Object bean, @Nullable RootBeanDefinition mbd) {if (System.getSecurityManager() !=null) {AccessController.doPrivileged((PrivilegedActionObject) () – {invokeAwareMethods( beanName, bean);return null;}, getAccessControlContext());}else {invokeAwareMethods(beanName, bean);}对象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, ‘调用init method failed’, ex);}if (mbd==null || !mbd.isSynthetic()) {wrappedBean=applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);}returnwrappedBean;}从方法名可以看出,initializeBean方法主要用于Bean初始化,初始化后有一个后处理方法applyBeanPostProcessorsAfterInitialization

该方法用于处理动态代理

//AbstractAutowireCapableBeanFactory(类名) public Object applyBeanPostProcessorsAfterInitialization(Object ExistingBean, String beanName) throws BeansException {Object result=ExistingBean;for (BeanPostProcessor handler : getBeanPostProcessors()) {Object current=process.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(类名)受保护的对象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 通知,则创建代理对象//如果有通知,则创建代理。Object[] SpecificInterceptors=getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); if (specicInterceptors !=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());返回代理;}this.advisedBeans.put(cacheKey, Boolean.FALSE);返回bean;}//AbstractAutoProxyCreator(类名)protected Object createProxy(Class? BeanClass, @nulLeable String Beanname, @Nullable Object [] Specificinterceptors, TargetSource Targetsource) BlelistableBeanFactory) {Autoproxyutils.exposeTargetClass (ConfigurableListableBeanFactory) this.BeanFactory, Beanname);} PROXYFACT Over 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 );}返回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(‘创建JDK动态代理:’ + this.advised.getTargetSource());}Class?[ ] proxiedInterfaces=AopProxyUtils.completeProxiedInterfaces(this.advised, true);findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);//JDK动态代理创建代理对象return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);}//CglibAopProxy(class name) public Object getProxy (@Nullable ClassLoader classLoader) {if (logger.isTraceEnabled()) {logger.trace(‘创建CGLIB proxy: ‘ + this.advised.getTargetSource());}尝试{类? rootClass=this.advised.getTargetClass(); Assert.state(rootClass !=null, ‘目标类必须可用于创建CGLIB 代理’);Class? proxySuperClass=rootClass;if (ClassUtils.isCglibProxyClass(rootClass)) {proxySuperClass=rootClass.getSuperclass();Class?[ ]additionalInterfaces=rootClass.getInterfaces();for (Class?additionalInterface :additionalInterfaces) {this.advised.addInterface(additionalInterface) );}}//验证类,根据需要写入日志消息。validateClassIfNecessary(proxySuperClass, classLoader);//配置CGLIB 增强器.增强器增强器=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[] 调用=getCallbacks(rootClass);Class?[] types=new Class?[callbacks.length];for (int x=0; x 类型.长度; x++) {类型[x]=回调[x].getClass(); }//fixedInterceptorMap仅在上面的getCallbacks调用之后填充enhancer.setCallbackFilter(new ProxyCallbackFilter(this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));enhancer.setCallbackTypes(types);//生成代理类并创建代理实例。return createProxyClassAndInstance(enhancer, callbacks);}catch (CodeGenerationException | IllegalArgumentException ex) { throw new AopConfigException(‘无法生成CGLIB 子类’ + this.advised.getTargetClass() +’: 的常见原因此问题包括使用最终类或不可见类’,ex);}catch (Throwable ex) {//TargetSource.getTarget() failedthrow new AopConfigException(‘意外的AOP 异常’, ex);}}protected Object createProxyClassAndInstance (Enhancer增强器,Callback[]回调) {enhancer.setInterceptDuringConstruction(false);enhancer.setCallbacks(callbacks);//CGLIB创建代理对象实例return (this.constructorArgs !=null this.constructorArgTypes !=null ?enhancer.create ( this.constructorArgTypes, this.constructorArgs) :enhancer.create());} 至此,已经创建了两种类型的动态代理对象。当从Spring容器获取对象时,返回代理对象。我们添加额外的功能然后添加的功能就会生效。

用户评论

源码分析:Spring是如何实现AOP的?
我一个人

一直在用 Spring 不知道它内部是怎么工作的!这篇博客深入浅出地讲解了 AOP 的实现原理,醍醐灌顶啊,感谢分享!

    有7位网友表示赞同!

源码分析:Spring是如何实现AOP的?
|赤;焰﹏゛

以前只记得 Spring 强大,没有想过它底层源码也这么巧妙!读完之后感觉对 AOP 有了更清晰的理解,学习一下,说不定以后能自己写点东西。

    有6位网友表示赞同!

源码分析:Spring是如何实现AOP的?
墨城烟柳

AOP 本身就挺复杂的概念,这个博文讲得详细又容易懂,对于刚接触 AOP 的开发者来说非常实用,赞一个!

    有8位网友表示赞同!

源码分析:Spring是如何实现AOP的?
珠穆郎马疯@

源码分析确实难啃,不过这篇博客把它拆解得很清晰,结合实例讲解更是加深了理解。我本来以为 AOP 特别神秘,现在觉得其实原理还是挺简单的!

    有6位网友表示赞同!

源码分析:Spring是如何实现AOP的?
巷陌繁花丶

代码看得有点累…也许是技术苦手 😅 但博主把关键点解释得还不错,至少让我对 Spring 如何实现 AOP 有了一个大概的了解。希望以后能看到更多实战案例的讲解。

    有16位网友表示赞同!

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

感觉这篇文章偏理论了,我希望可以有更多关于实际应用的例子,毕竟学好一个技术首先要看它在现实中的运用场景!

    有11位网友表示赞同!

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

虽然文章分析得相当透彻,但是对于新手来说,有些概念还是比较抽象,也许能加入一些更直观的图解或者动画会更有帮助!

    有19位网友表示赞同!

源码分析:Spring是如何实现AOP的?
笑傲苍穹

Spring 真的太牛了,AOP 的实现简直是艺术!这种底层设计让我对 Spring 作者的技艺佩服得五体投地。

    有16位网友表示赞同!

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

我一直在想为什么说 AOP 能提高代码可维护性,看完这篇博客才明白它的原理,原来它可以将不同的功能分离到代理类中,这样就能更加方便修改和测试!

    有11位网友表示赞同!

源码分析:Spring是如何实现AOP的?
柠夏初开

这篇文章的讲解很到位,把核心概念都解释得很清晰。之前对 AOP 的理解比较模糊,现在终于明白了它是如何工作的!

    有7位网友表示赞同!

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

源码分析确实太烧脑了,我感觉自己还是新手入门阶段😅。不过这篇博客让我看到了 Spring 作者设计的精妙之处,感觉很有动力继续学习!

    有18位网友表示赞同!

源码分析:Spring是如何实现AOP的?
开心的笨小孩

觉得文章讲解有点偏理论,缺少一些具体的实战案例说明,更能加深理解和学习效果。

    有7位网友表示赞同!

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

总觉得Spring这个框架太复杂了,AOP的实现更是让人头疼。希望有更直观的教学资源可以帮助新手更好地入门!

    有13位网友表示赞同!

源码分析:Spring是如何实现AOP的?
┲﹊怅惘。

以前用 Spring 就感觉很强大,现在看了这篇源码分析才知道为什么它这么好!作者的讲解真棒!

    有11位网友表示赞同!

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

文章结构很有逻辑,一步一步地把 AOP 的原理解释得清清楚楚。我已经对 Spring 里的代理类有了更深的了解。

    有20位网友表示赞同!

源码分析:Spring是如何实现AOP的?
青衫负雪

虽然我是 Java 开发者,但关于代码分析的感觉还是比较陌生。希望以后能看到更多结合实践的讲解,比如实际项目中的应用场景。

    有9位网友表示赞同!

源码分析:Spring是如何实现AOP的?
夏日倾情

源码深入理解确实是个挑战,这篇博客让我对 Spring AOP 的底层实现有了清晰认识,感谢作者分享如此宝贵经验!

    有19位网友表示赞同!

原创文章,作者:小su,如若转载,请注明出处:https://www.sudun.com/ask/118616.html

(0)
小su的头像小su
上一篇 2024年9月1日 上午11:06
下一篇 2024年9月1日 上午11:09

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注