曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享
曹工说Spring Boot源码(2)-- Bean Definition到底是什么,咱们对着接口,逐个方法讲解
曹工说Spring Boot源码(3)-- 手动注册Bean Definition不比游戏好玩吗,我们来试一下
曹工说Spring Boot源码(4)-- 我是怎么自定义ApplicationContext,从json文件读取bean definition的?
曹工说Spring Boot源码(5)-- 怎么从properties文件读取bean
曹工说Spring Boot源码(6)-- Spring怎么从xml文件里解析bean的
曹工说Spring Boot源码(7)-- Spring解析xml文件,到底从中得到了什么(上)
曹工说Spring Boot源码(8)-- Spring解析xml文件,到底从中得到了什么(util命名空间)
曹工说Spring Boot源码(9)-- Spring解析xml文件,到底从中得到了什么(context命名空间上)
曹工说Spring Boot源码(10)-- Spring解析xml文件,到底从中得到了什么(context:annotation-config 解析)
曹工说Spring Boot源码(11)-- context:component-scan,你真的会用吗(这次来说说它的奇技淫巧)
曹工说Spring Boot源码(12)-- Spring解析xml文件,到底从中得到了什么(context:component-scan完整解析)
工程代码地址 思维导图地址
工程结构图:
概要本篇已经是spring源码第13篇,前一篇讲了context:component-scan的完整解析,本篇,继续解析context命名空间里的另一个重量级元素:load-time-weaver。它可以解决你用aop搞不定的事情。
大家如果熟悉aop,会知道aop的原理是基于beanPostProcessor的。比如平时,我们会在service类的部分方法上加@transactional,对吧,transactional是基于aop实现的。最终的效果就是,注入到controller层的service,并不是原始的service bean,而是一个动态代理对象,这个动态代理对象,会去执行你的真正的service方法前后,去执行事务的打开和关闭等操作。
aop的限制就在于:被aop的类,需要被spring管理,管理的意思是,需要通过@component等,弄成一个bean。
那,假设我们想要在一个第三方的,没被spring管理的类的一个方法前后,做些aop的事情,该怎么办呢?
一般来说,目前的方法主要是通过修改class文件。
class文件在什么时候才真正生效?答案是:在下面这个方法执行完成后:
public Class<?> loadClass(String name) throws ClassNotFoundException { return loadClass(name, false); }一旦通过上述方法,获取到返回的Class对象后,基本就不可修改了。
那根据这个原理,大致有3个时间节点(第二种包含了2个时间点),对class进行修改:
编译器织入,比如aspectJ的ajc编译器,假如你自己负责实现这个ajc编译器,你当然可以自己夹带私货,悄悄地往要编译的class文件里,加点料,对不?这样的话,编译出来的class,和java源文件里的,其实是不一致的;
自己实现classloader,在调用上述的loadClass(String name)时,自己加点料;通俗地说,这就是本课要讲的load-time-weaving,即,加载时织入;
其中,又分为两种,因为我们知道,classloader去loadClass的时候,其实是分两步的,一个是java代码层面,一个是JVM层面。
java代码层面:你自定义的classloader,想怎么玩就怎么玩,比如针对传进来的class,获取到其inputStream后,对其进行修改(增强或进行解密等)后,再丢给JVM去加载为一个Class;
JVM层面:Instrumentation机制,具体理论的东西我也说不清,简单来说,就是java命令启动时,指定agent参数,agent jar里,有一个premain方法,该方法可以注册一个字节码转换器。
字节码转换器接口大致如下:
public interface ClassFileTransformer { // 这个方法可以对参数中指定的那个class进行转换,转换后的class的字节码,通过本方法的返回参数返回 // 即,本方法的返回值,就是最终的class的字节码 byte[] transform( ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException; }大家参考下面两篇文章。
Java Instrumentation,这一篇原文没代码,我自己整理了下,附上了具体的步骤,放在码云
参考文章2
第一种,需要使用aspectj的编译器来进行编译,还是略显麻烦;这里我们主讲第二种,LTW。
LTW其实,包含了两部分,一部分是切面的问题(切点定义切哪儿,通知定义在切点处要嵌进去的逻辑),一部分是切面怎么生效的问题。
我们下面分别来讲。
Aspectj的LTW怎么玩我们可以参考aspectj的官网说明:
https://www.eclipse.org/aspectj/doc/released/devguide/ltw-configuration.html