目录
三、AnnotatedBeanDefinitionReader
四、ClassPathBeanDefinitionScanner
七、AnnotationConfigApplicationContext
先对一些基础的类以及概念有个初步的了解,建立一个宏观的认知,后面进入spring源码的处理逻辑再慢慢细化研究其功能与扩展点。
spring版本:spring-framework-5.0.x
一、BeanDefinition
BeanDefinition接口源码
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
- BeanDefinition接口规定了,BeanDefinition对象必须的一些功能,具体的实现由子类完成。
- BeanDefinition对象是对spring中的bean的一个定义
- spring会先将@Bean 、@Component 等标注的类解析成一个BeanDefinition对象。
- 后面创建对象都是根据BeanDefinition对象中的属性进行创建。
可以类比java中的Class对象,但是BeanDefinition对象的描述要更加的复杂,并且里面的规则只适用于spring框架。
二、BeanDefinitionReader
BeanDefinitionReader接口源码
public interface BeanDefinitionReader {
这个接口的主要功能是将某些资源,加载解析成对应的BeanDefinition对象然后保存到BeanFactory对象中。
BeanDefinitionReader接口的主要实现类是XmlBeanDefinitionReader
XmlBeanDefinitionReader 类源码
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
这个类主要是加载spring.xml等xml配置文件,将文件中配置的<bean>标签数据,解析成对应的BeanDefinition对象。
来个案例:
测试类:
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
//创建XmlBeanDefinitionReader对象,加载指定的xml文件,将里面的<bean>标签解析成BeanDefinition对象
//将解析后的BeanDefinition对象注册到context对象中
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
//返回解析成功的个数
int i = reader.loadBeanDefinitions("spring.xml");
System.out.println(i);
}
spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd" default-autowire="byType">
<bean id="a" class="com.zzl.lg.demo1.A">
</bean>
</beans>
A类
public class A {
}
三、AnnotatedBeanDefinitionReader
AnnotatedBeanDefinitionReader类源码:
public class AnnotatedBeanDefinitionReader {
private final BeanDefinitionRegistry registry;
private BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();
private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver();
private ConditionEvaluator conditionEvaluator;
这个类的作用是:
- 直接将指定的某个没有使用spring提供的@Bean 、@Component 等注解的类解析成BeanDefinition对象并保存到容器中
- 如果被解析的类上有spring的其他注解,也会被解析。
案例:
测试类:
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
//创建AnnotatedBeanDefinitionReader对象,将指定的类加载成BeanDefinition对象
//将解析后的BeanDefinition对象注册到context对象中
AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(context);
//解析指定的类
reader.register(A.class);
//刷新容器
context.refresh();
//获取A对象
System.out.println(context.getBean(A.class));
}
A类
@Scope
public class A {
}
四、ClassPathBeanDefinitionScanner
ClassPathBeanDefinitionScanner类源码:
public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider {
private final BeanDefinitionRegistry registry;
ClassPathBeanDefinitionScanner类主要的作用是扫描指定包路径下的 @Component 注解,将有这个注解的类解析成BeanDefinition对象并放入容器中
案例:
测试类:
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
//创建ClassPathBeanDefinitionScanner对象,将指定包路径下标注有@Component注解的类解析成BeanDefinition对象
//将解析后的BeanDefinition对象注册到context对象中
ClassPathBeanDefinitionScanner reader = new ClassPathBeanDefinitionScanner(context);
//扫描指定的包路径
reader.scan("com.zzl.lg.demo1");
//刷新容器
context.refresh();
//获取A对象
System.out.println(context.getBean(A.class));
}
A类
import org.springframework.stereotype.Component;
@Component
public class A {
}
//扫描指定的包路径 reader.scan("com.zzl.lg.demo1");
这行代码里面的逻辑就是,扫描指定路径下的所有.class文件,并且将标有@Component 注解的类解析出来
扩展点:
这里有一个扩展点就是,让自己的自定义注解也可以拥有@Component注解相同的作用被spring扫描成BeanDefinition对象
具体的源码逻辑以及如何扩展,后面会深入研究
五、BeanFactory
BeanFactory接口源码:
public interface BeanFactory {
这是一个spring容器的顶级接口
他有一个非常重要的实现类 DefaultListableBeanFactory
DefaultListableBeanFactory类源码
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
DefaultListableBeanFactory类的结构关系图:
大图地址:https://www.processon.com/view/link/5fafa01ae401fd7d35c98c0e
DefaultListableBeanFactory实现或者继承了许多的类,那么自然会具备很多的功能,我们大概来了解一下他都具备些什么功能:
AliasRegistry接口:别名注册器
- 可以为某个名字,注册对应的别名
- 注册别名的方法 void registerAlias(String name, String alias);
SimpleAliasRegistry类:实现了AliasRegistry接口
- 具体的实现了注册别名的逻辑,根据源码可以看出别名可以同时设置多个
- 核心源码:this.aliasMap.put(alias, name); 可以看出map中的key是别名,那么就可以实现多个别名对应一个name
BeanDefinitionRegistry接口:主要功能是注册BeanDefinition对象
BeanFactory:bean工厂
- 提供根据名字,类型等或者bean对象的方法
SingletonBeanRegistry接口:注册和获取单例bean对象
DefaultSingletonBeanRegistry类:实现了SingletonBeanRegistry接口
- 可以直接注册对象到单例池中
- 核心代码:this.singletonObjects.put(beanName, singletonObject);
- 这里思考一个问题:我们添加到单例池的对象是否走完了spring的生命周期
HierarchicalBeanFactory接口:主要是获取父bean工厂的功能
- 继承了BeanFactory接口
ListableBeanFactory接口:在BeanFactory的基础上,增加了一些针对bean的操作或查询方法
- 获取所有BeanDefinition的beanNames
- 根据类型获取所有的bean等一些功能
FactoryBeanRegistrySupport:支持了FactoryBean的功能
ConfigurableBeanFactory:在HierarchicalBeanFactory和SingletonBeanRegistry的基础上再次增加功能
- 添加了设置父BeanFactory、类加载器(表示可以指定某个类加载器进行类的加载)、
- 设置Spring EL表达式解析器(表示该BeanFactory可以解析EL表达式)、
- 设置类型转化服务(表示该BeanFactory可以进行类型转化)、
- 可以添加BeanPostProcessor(表示该BeanFactory支持Bean的后置处理器),
- 可以合并BeanDefinition,
- 可以销毁某个Bean等等功能
AutowireCapableBeanFactory:是直接继承了BeanFactory
- 在BeanFactory的基础上,支持在创建Bean的过程中能对Bean进行自动装配
AbstractBeanFactory类:实现了ConfigurableBeanFactory接口,继承了FactoryBeanRegistrySupport,
- 这个BeanFactory的功能已经很全面了,但是不能自动装配和获取beanNames
ConfigurableListableBeanFactory接口:
- 继承了ListableBeanFactory、AutowireCapableBeanFactory、ConfigurableBeanFactory
- 没有提供新的功能,主要是整合上面三个接口
AbstractAutowireCapableBeanFactor类:
- 继承了AbstractBeanFactory,
- 实现了AutowireCapableBeanFactory,
- 拥有了自动装配的功能
DefaultListableBeanFactory:
- 继承了AbstractAutowireCapableBeanFactory,
- 实现了ConfigurableListableBeanFactory接口和BeanDefinitionRegistry接口,
- 所以DefaultListableBeanFactory的功能很强大很全面
直接使用DefaultListableBeanFactory对象的功能示例
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition.setBeanClass(User.class);
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 注册BeanDefinition
beanFactory.registerBeanDefinition("user", beanDefinition);
// 注册别名
beanFactory.registerAlias("user", "user1");
// 注册BeanPostProcessor
beanFactory.addBeanPostProcessor(new LubanBeanPostProcessor());
// 获取Bean对象
System.out.println(beanFactory.getBean("user1"));
// 根据类型获取beanNames
System.out.println(beanFactory.getBeanNamesForType(User.class));
六、ApplicationContext
ApplicationContext接口源码:
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory,
HierarchicalBeanFactory,
MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
ApplicationContext是个接口,可以把它理解为一个特殊的BeanFactory
ApplicationContext接口的关系图:
大图地址:https://www.processon.com/view/link/5fafc0397d9c08138ec727e9
ApplicationContext大概具备哪些功能:
- HierarchicalBeanFactory:拥有获取父BeanFactory的功能
- ListableBeanFactory:拥有获取beanNames的功能
- ResourcePatternResolver:资源加载器,可以一次性获取多个资源(文件资源等等)
- EnvironmentCapable:可以获取运行时环境(没有设置运行时环境功能)
- ApplicationEventPublisher:拥有广播事件的功能(没有添加事件监听器的功能)
- MessageSource:拥有国际化功能
一些功能示例:
国际化
先定义一个MessageSource:
@Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("messages");
return messageSource;
}
注:配置文件的命名规则是: 前缀_后缀 (messages_sn_en)
国际化配置文件:
messages.properties
name=tom
messages_sn_en.properties
name=汤姆
测试类:
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
String message = context.getMessage("name", null, new Locale("sn_en"));
System.out.println(message);
}
资源加载
ApplicationContext还拥有资源加载的功能,比如,可以直接利用ApplicationContext获取某个文件的内容:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
Resource resource = context.getResource("file://D:\\IdeaProjects\\spring-framework\\luban\\src\\main\\java\\com\\luban\\entity\\User.java");
System.out.println(resource.contentLength());
你可以想想,如果你不使用ApplicationContext,而是自己来实现这个功能,就比较费时间了。
还比如你可以:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
Resource resource = context.getResource("classpath:com/luban/entity/User.class");
System.out.println(resource.contentLength());
还可以一次性获取多个:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
Resource[] resources = context.getResources("classpath:com/luban/service/*.class");
for (Resource resource : resources) {
System.out.println(resource.contentLength());
}
这个功能用到了策略模式。
获取运行时环境
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
// 获取JVM所允许的操作系统的环境
context.getEnvironment().getSystemEnvironment();
// 获取JVM本身的一些属性,包括-D所设置的
context.getEnvironment().getSystemProperties();
// 还可以直接获取某个环境或properties文件中的属性
context.getEnvironment().getProperty("yyy")
注意,可以利用
@PropertySource("classpath:application.properties")
来使得某个properties文件中的参数添加到运行时环境中
事件发布
先定义一个事件监听器
@Bean
public ApplicationListener applicationListener() {
return new ApplicationListener() {
@Override
public void onApplicationEvent(ApplicationEvent event) {
System.out.println("接收到了一个事件");
}
};
}
然后发布一个事件:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
context.publishEvent("kkk");
七、AnnotationConfigApplicationContext
AnnotationConfigApplicationContext类源码:
public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {
private final AnnotatedBeanDefinitionReader reader;
private final ClassPathBeanDefinitionScanner scanner;
AnnotationConfigApplicationContext类的结构图:
大图地址:https://www.processon.com/view/link/5fafcae007912964bbad2918
AnnotationConfigApplicationContext类的一些功能介绍:
- AnnotationConfigRegistry接口:
- 可以单独注册某个为类为BeanDefinition(可以处理该类上的@Configuration注解,已经可以处理@Bean注解)
- 同时可以扫描指定包路径下的类文件,并将符合条件的类封装成BeanDefinition对象
- ConfigurableApplicationContext接口:继承了ApplicationContext接口,
- 增加了,添加事件监听器、
- 添加BeanFactoryPostProcessor、
- 设置Environment,
- 获取ConfigurableListableBeanFactory等功能
- AbstractApplicationContext类:实现了ConfigurableApplicationContext接口
- GenericApplicationContext类:继承了AbstractApplicationContext,实现了BeanDefinitionRegistry接口,
- 拥有了所有ApplicationContext的功能,
- 并且可以注册BeanDefinition,
- 注意这个类中有一个属性(DefaultListableBeanFactory beanFactory)
- AnnotationConfigApplicationContext类:
- 继承了GenericApplicationContext,
- 实现了AnnotationConfigRegistry接口,
- 拥有了以上所有的功能
八、类型转化
PropertyEditor
JDK中提供的类型转化工具类
public class StringToUserPropertyEditor extends PropertyEditorSupport implements PropertyEditor {
@Override
public void setAsText(String text) throws IllegalArgumentException {
User user = new User();
user.setName(text);
this.setValue(user);
}
}
测试
StringToUserPropertyEditor propertyEditor = new StringToUserPropertyEditor();
propertyEditor.setAsText("1");
User value = (User) propertyEditor.getValue();
System.out.println(value);
如何向Spring中注册PropertyEditor:
@Bean
public CustomEditorConfigurer customEditorConfigurer() {
CustomEditorConfigurer customEditorConfigurer = new CustomEditorConfigurer();
Map<Class<?>, Class<? extends PropertyEditor>> propertyEditorMap = new HashMap<>();
propertyEditorMap.put(User.class, StringToUserPropertyEditor.class);
customEditorConfigurer.setCustomEditors(propertyEditorMap);
return customEditorConfigurer;
}
假设现在有如下Bean:
@Component
public class UserService {
@Value("true")
User test;
public void test() {
System.out.println(test);
}
}
那么test属性就能正常的完成属性赋值
ConversionService
Spring中提供的类型转化服务,它比PropertyEditor更强大
public class StringToUserConverter implements ConditionalGenericConverter {
@Override
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return sourceType.getType().equals(String.class) && targetType.getType().equals(User.class);
}
@Override
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(String.class, User.class));
}
@Override
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
User user = new User();
user.setName((String)source);
return user;
}
}
DefaultConversionService conversionService = new DefaultConversionService();
conversionService.addConverter(new StringToUserConverter());
User value = conversionService.convert("1", User.class);
System.out.println(value);
如何向Spring中注册ConversionService:
@Bean
public ConversionServiceFactoryBean conversionService() {
ConversionServiceFactoryBean conversionServiceFactoryBean = new ConversionServiceFactoryBean();
conversionServiceFactoryBean.setConverters(Collections.singleton(new StringToUserConverter()));
return conversionServiceFactoryBean;
}
TypeConverter
整合了PropertyEditor和ConversionService的功能,是Spring内部用的
SimpleTypeConverter typeConverter = new SimpleTypeConverter();
typeConverter.registerCustomEditor(User.class, new StringToUserPropertyEditor());
//typeConverter.setConversionService(conversionService);
User value = typeConverter.convertIfNecessary("1", User.class);
System.out.println(value);
九、BeanPostProcessor
Bean的后置处理器,可以在创建每个Bean的过程中进行干涉,是属于BeanFactory中一个属性
十、BeanFactoryPostProcessor
Bean工厂的后置处理器,是属于ApplicationContext中的一个属性,是ApplicationContext在实例化一个BeanFactory后,可以利用BeanFactoryPostProcessor继续处理BeanFactory。
程序员可以通过BeanFactoryPostProcessor间接的设置BeanFactory,比如上文中的CustomEditorConfigurer就是一个BeanFactoryPostProcessor,我们可以通过它向BeanFactory中添加自定义的PropertyEditor。
十一、FactoryBean
允许程序员自定义一个对象通过FactoryBean间接的放到Spring容器中成为一个Bean。
那么它和@Bean的区别是什么?因为@Bean也可以自定义一个对象,让这个对象成为一个Bean。
区别在于利用FactoryBean可以更加强大,因为你通过定义一个XxFactoryBean的类,可以再去实现Spring中的其他接口,比如如果你实现了BeanFactoryAware接口,那么你可以在你的XxFactoryBean中获取到Bean工厂,从而使用Bean工厂做更多你想做的,而@Bean则不行。
我大概走了一下断点,研究了一下获取一个FactoryBean对象的时候spring内部是怎么做的。有一个比较注意的点是通过FactoryBean创建的对象是没有走spring bean的生命周期的,就是直接将创建好的对象返回了。
接下来我们写个简单的示例 跟着断点大概看一下获取FactoryBean创建对象的过程。
测试类源码:
public class User {
}
@Component
public class MyFactoryBean implements FactoryBean {
/**
* 默认返回的对象是单例的 就是说这个方法只会被调用一次,
* 之后再想获取对象是从spring缓存中获取的
* @return
* @throws Exception
*/
@Override
public Object getObject() throws Exception {
//根据我们自己的逻辑,自定义创建对象
User user = new User();
return user;
}
@Override
public Class<?> getObjectType() {
return User.class;
}
}
@ComponentScan(value = "com.zzl.lg.factorybean") //扫描的包路径填你自己的包路径就可以了
@Configuration
public class MyConfig {
}
测试类
public class Aplication {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
MyFactoryBean myFactoryBean = (MyFactoryBean) context.getBean("myFactoryBean");
System.out.println(myFactoryBean.getObject());
}
}
接下来我会把一些关键点的断点截图:
首先我们断点的开始位置:
我们将第一个断点打在测试类的这行代码,对象最终是getBean方法返回的,那么我们肯定就是去研究getBean方法内部的逻辑。
核心代码类和方法截图:
核心的代码逻辑就在AbstractBeanFactory类的doGetBean方法中实现的。
doGetBean方法中跟获取FactoryBean中自定义对象有关的代码就是方法开始的这一部分代码,其中有两处关建点,就是我写了注释的两行代码
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
/*验证bean的名字是否合法*/
/*
* 1.判断当前传入的名字,是否是以&开头的,如果是将&截取掉,返回剩余的作为BeanName
* 2.将1.返回的BeanName作为别名去别名池中拿,如果拿到了将拿到真实的BeanName返回,如果没有拿到将自己返回
*
* */
final String beanName = transformedBeanName(name);
Object bean;
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isDebugEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
}
}
//获取FactoryBean内部创建的对象,或者获取FactoryBean对象本身
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
其实这两行关键代码里面的逻辑都很简单,我大概说一下,相信大家自己点进去肯定是可以看懂的。
final String beanName = transformedBeanName(name);
这行代码里面是对我们传入的name的处理,有两个逻辑:
- 1.首先判断是否以&开头,如果是将&去掉(可以去掉多个&知道name前面没有&为止)如果不是直接返回name
- 2.将第一步返回的name作为别名,在别名池中去拿是否有与其对应的真实的BeanName,如果有返回BeanName没有返回自己
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
这行代码就是返回我们最终需要的对象,这里面有两个逻辑:
- 返回真实的FactoryBean对象
- 返回FactoryBean对象生成的对象
首先 方法中的 sharedInstance 属性就是从spring容器单例池中获取的 FactoryBean对象:
- 如果你外面调用的时候传入的名字开头是以&开头的,那么直接返回 sharedInstance 对象
- 如果你外面调用的时候传入的名字开头不是以&开头的,那么会调用sharedInstance 对象的getObject方法,返回我们自定义的对象,并将其放入使用工厂生成对象的专属的缓存池中(不是spring的单例池)