LD is tigger forever,CG are not brothers forever, throw the pot and shine forever.
Modesty is not false, solid is not naive, treacherous but not deceitful, stay with good people, and stay away from poor people.
talk is cheap, show others the code,Keep progress,make a better result.
Survive during the day and develop at night。
目录
概述
上面的解释截取自官方文档,第一段大概的意思就是说通过使用 @Configuration 注解的类会将其中 @Bean 注解的方法所生成并返回的对象交由 Spring 容器来进行管理,而第二段的意思大概就是我们同样可以 @Configuration 和 @ComponentScan 组合的方式完成 Bean 的自动扫描,对于 @Configuration 的使用方法和其作用这里就不做过的解释,如果有兴趣可以直接去上面提供的官方文档中查看。
当我们使用 @Configuration 注解的时候,一般是将其所注解的类作为 Spring 的配置类,也就是对应于 xml 配置的一种方式,通常的使用方法是将其对应的类信息作为入参来实例化一个 AnnotationConfigApplicationContext 对象,从而完成 Spring 容器的初始化。
。但是你有没有测试过,假如当我们去掉 @Configuration 注解,再将这个类作为 AnnotationConfigApplicationContext 实例化方法的入参时,会有什么样的效果,下面我们就来测试一下。
首先在这里我们统一使用的测试代码如下,SpringConfig 类就是我们所说的那个配置类,我们在初始化容器后再从容器中将这个配置类所对应的 Bean 拿出来。
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
System.out.println(context.getBean(SpringConfig.class));
}
为什么在使用了 @Configuration 注解后,要对这个配置类进行代理,这样的作用和意义是什么?
BeanDefinition 和 BeanDefinitionMap
BeanDefinition ,BeanDefinitionMap
如果这样你还是觉得很模糊的话,可以再具体一点,那就是每个 BeanDefinition 对象都对应描述了一个类,在这个对象的属性中会记录一些类的基本信息(类的类型信息)和该类所对应的位于 Spring 中 Bean 的一些属性(是否为懒加载等)。说白了它就是用来记录类的一些特征,并且在 Spring 当中并不是通过类来直接生成相应的对象的,而是通过 BeanDefinition 来生成的。
BeanDefinitionMap
从命名即可得其作用,BeanDefinitionMap 顾名思义就是用来存储 BeanDefinition 的一个 Map 结构,是 BeanFactory 中的一个属性,并可以算是 Spring 容器中至关重要的一个组件,因为大部分几乎所有的 Bean 所对应的 BeanDefinition 都会存储在这里。它的位置是在 DefaultListableBeanFactory 中,下面直接贴出它的代码。
/** Map of bean definition objects, keyed by bean name. */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
经常使用的代理一般分为动态代理和静态代理两种,对于静态代理就是在代码中手动创建代理类,即在编译前代理类就已经确定(在编译器层面实现),而动态代理则恰好相反,它不会在代码中显示的实现代理类,而是在编译过程中自动实现代理类的创建工作,因此相对于静态代理,动态代理具有更高的灵活性,并且使用动态代理的代码一般具备更加良好的可扩展性。
对于动态代理一般又存在两种实现方式,第一种就是 JDK 1.3 就引入的 JDK 动态代理,这种原生的 JDK 动态代理主要的原理是使用 Java 的反射机制,并要求被代理的类必须实现一个或多个接口,如果一个类没有实现任何接口,那么它是无法被代理的
另一种就是 CGLIB 动态代理,它的底层是直接通过使用一个小而快的字节码处理框架 ASM,来转换字节码并生成新的类,所有的代理工作是在编译的过程中动态织入,同时其不要求被代理的类必须实现接口(因为其原理主要是通过继承被代理类,创建其子类来实现对被代理类的代理工作),所以具备更高的灵活性,在 Spring 的 AOP 中就大量的用到了这种代理技术。
三、源码解析
3.1 概述
在介绍完必要的预备知识之后,从这里开始,我们进入到源码的分析阶段,我们的入手点就是通过调用有参构造器而实例化 AnnotationConfigApplicationContext 对象。
/**
* Create a new AnnotationConfigApplicationContext, deriving bean definitions
* from the given annotated classes and automatically refreshing the context.
* @param annotatedClasses one or more annotated classes,
* e.g. {@link Configuration @Configuration} classes
*/
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
this();
register(annotatedClasses);
refresh();
}
这个方法的主要功能就是创建一个 AnnotationConfigApplicationContext 对象,从给定的带注释的类派生bean 定义(BeanDefinition)并自动刷新容器上下文。同时它给了一个注解示例,正是我们所说的 @Configuration,然后在这个方法的第一行它调用了自身的无参构造器(无参构造器主要是创建了两个不同种类的 Bean 扫描器,其中一个注解扫描器的属性名为 reader,这个注解扫描器会在下面的 register 方法中用到),然后接下来调用了 register 方法 进行注册,因此我们跟进这个方法。
/**
* Register one or more annotated classes to be processed.
* <p>Note that {@link #refresh()} must be called in order for the context
* to fully process the new classes.
* @param annotatedClasses one or more annotated classes,
* e.g. {@link Configuration @Configuration} classes
* @see #scan(String...)
* @see #refresh()
*/
public void register(Class<?>... annotatedClasses) {
Assert.notEmpty(annotatedClasses, "At least one annotated class must be specified");
this.reader.register(annotatedClasses);
}
这里它直接调用了注解扫描器 reader 的 register 方法,我们继续跟进。
这个方法主要作用就是注册 Bean 类并从其类声明中获取元数据(也就是获取注解信息),然后没做别的事就又直接调了 doRegisterBean 方法,因此我们继续跟进。
3.3 refresh(BeanClass 的替换)
但是对于此文我们仅分析 @Configuration 的作用方式和其相关的代码,所以会省略掉一些代码的分析,下面直接进入到 refresh 方法的分析。
问题
实现思路分析
相关工具如下:
分析:
小结:
主要讲述了注解配置方式的@Configuration原理剖析, 里面有许多不足,请大家指正~