源码
扫描包的流程

刷新容器
概述
Spring 容器创建后会调用 refresh 方法刷新 Spring 应用上下文。
源码
refresh
AbstractApplicationContext # refresh 源码如下
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//刷新前的预处理;
prepareRefresh();
// 获取BeanFactory;默认实现是DefaultListableBeanFactory,在创建容器的时候创建的
// 让本类的子类刷新内部的 bean 工厂,下面两个子类各有不同的逻辑:
// AbstractRefreshableApplicationContext: 实际上就是重新创建一个 bean 工厂,并设置工厂的一些属性
// GenericApplicationContext: 获取创建容器的 bean 工厂,并设置工厂的ID
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// BeanFactory的预准备工作(BeanFactory进行一些设置,比如context的类加载器,BeanPostProcessor和XXXAware自动装配等)
// 即在执行本方法之前,工厂内部相当于是空的,无法执行任何逻辑。本文有介绍该方法的源码。
prepareBeanFactory(beanFactory);
try {
//BeanFactory准备工作完成后进行的后置处理工作
postProcessBeanFactory(beanFactory);
//执行BeanFactoryPostProcessor的方法;
invokeBeanFactoryPostProcessors(beanFactory);
//注册BeanPostProcessor(Bean的后置处理器),在创建bean的前后等执行
registerBeanPostProcessors(beanFactory);
//初始化MessageSource组件(做国际化功能;消息绑定,消息解析);
initMessageSource();
//初始化事件派发器
initApplicationEventMulticaster();
//子类重写这个方法,在容器刷新的时候可以自定义逻辑;如创建Tomcat,Jetty等WEB服务器
onRefresh();
//注册应用的监听器。就是注册实现了ApplicationListener接口的监听器bean,这些监听器是注册到ApplicationEventMulticaster中的
registerListeners();
//初始化所有剩下的非懒加载的单例bean
finishBeanFactoryInitialization(beanFactory);
//完成context的刷新。主要是调用LifecycleProcessor的onRefresh()方法,并且发布事件(ContextRefreshedEvent)
finishRefresh();
}
......
}
prepareBeanFactory
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
//设置BeanFactory的类加载器
beanFactory.setBeanClassLoader(getClassLoader());
//设置支持表达式解析器
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
//添加部分BeanPostProcessor【ApplicationContextAwareProcessor】
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
//设置忽略的自动装配的接口EnvironmentAware、EmbeddedValueResolverAware、xx,因为ApplicationContextAwareProcessor#invokeAwareInterfaces已经把这5个接口的实现工作做了
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
//注册可以解析的自动装配;我们能直接在任何组件中自动注入:BeanFactory、ResourceLoader、ApplicationEventPublisher、ApplicationContext
//其他组件中可以通过 @autowired 直接注册使用
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
//添加BeanPostProcessor【ApplicationListenerDetector】后置处理器,在bean初始化前后的一些工作
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
// Detect a LoadTimeWeaver and prepare for weaving, if found.
if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
// Set a temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
//给BeanFactory中注册一些能用的组件;
if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
//环境信息ConfigurableEnvironment
beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
}
if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
//系统属性,systemProperties【Map<String, Object>】
beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
}
if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
//系统环境变量systemEnvironment【Map<String, Object>】
beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
}
}
解读
prepareRefresh
表示在真正做refresh操作之前需要准备做的事情:
- 设置Spring容器的启动时间,
- 开启活跃状态,撤销关闭状态,。
- 初始化context environment(上下文环境)中的占位符属性来源。
- 验证环境信息里一些必须存在的属性
应用
看上面源码的注释可知,如果自定义类继承该抽象类的子类再覆盖方法 onRefresh 则可以在容器创建之后,单例 bean 被创建放入容器之前执行自定义的逻辑。
循环依赖
概述
spring 解决循环依赖的方法是通过三级缓存:(下面3个都是 Map 对象)
- singletonObjects 单例池,存放经历了完整生命周期产生的 bean
- earlySingletonObjects 早期单例池,即实例化后填充属性前的对象放入该缓存中
- singletonFactories 用来创建早期 bean 对象的工厂
额外:
- singletonsCurrentlyInCreation
- earlyProxyReferences
为什么需要三级
解决循环依赖的逻辑
在正常的 bean 生命周期中需要像下图一样在前期插入得到代理对象的逻辑


FactoryBean

总结上面的使用,类似 BeanFacotry,FactoryBean 是生产功能类似的多个 Bean 的工具类型的 Bean。
Bean生命周期
Bean生命周期流程图

Spring 扫描的流程

寻找注入点

在注入点进行注入

resolveDependency

Bean 名称的特殊例子
beanName 同类型名称
当类的名称是 AService 时, Spring 创建的 bean 名称也是 AService。spring 源码中在构建 bean 名称时会检测前面两个字母如果都是大写的那么就直接返回类名称作为 bean 的名称
依赖注入
注解 @Bean
在方法头上使用注解 @Bean 可以将方法返回的对象纳入 spring 容器管理。设置注解参数 @Bean(autowire=Autowire.BY_NAME) 可以指定方法的返回对象的属性通过 BY_NAME 的方法自动注入。但是 @Bean 注解不会注入基本类型 (java提供的类型 - 非用户自定义的引用类型)。即类似下面的代码, AppConfig # userService 返回的对象中不会自动注入 number 属性为1
// AppConfig 类中代码:
@Bean
public Number number(){
return 1;
}
// AppConfig 类中代码:
@Bean
public UserService userService(){
return new UserService();
}
// 其他自定义类中
public class UserService {
@Autowired
private Number number;
}
注解 @Autowired
上面代码基础上去掉行号8的代码块,采用下面的逻辑(其他服务类中属性 userService)通过 @Autowired 自动注入的属性 userService.number =1
@Component
public class OrderService {
@Autowired
private UserService userService;
}
原因是 @Bean 注解中有对基本类型的过滤,而注解 @Autowired 则没有过滤基本类型
销毁钩子
销毁bean时调用
类似 @PostConstruct 效果,可以设置在 spring 容器回收(销毁)bean 时执行自定义逻辑,有下面几种实现方法
- bean 类实现接口 DisposableBean 或者 Autocloseable
- 在自定义方法头上使用注解 PreDestroy
- 通过接口 MergedBeanDefinitionPostProcessor: 该接口会要求实现方法 postProcessMergedBeanDefinition,本方法有传入参数 beanDefinition 通过
beanDefinition.setDestroyMethodName("onDestroy");可以设置 bean 类的普通方法为销毁时被钩子调用的方法。或者beanDefinition.setDestroyMethodName("(inferred)")然后在 bean 内定义名称为 close (shutdown)的方法。 设定为 (inferred) 后 spring 会查找 bean 内名称为 close 或者 shutdown 的方法在销毁时调用。
触发销毁
通过 spring 上下文调用 close 方法:context.close(),即会触发调用销毁钩子。或者 context.registerShutdownHook();
spring核心概念
BeanDefinition
BeanDefinition表示Bean定义,BeanDefinition中存在很多属性用来描述一个Bean的特点。比如:
- class,表示Bean类型
- scope,表示Bean作用域,单例或原型等
- lazyInit:表示Bean是否是懒加载
- initMethodName:表示Bean初始化时要执行的方法
- destroyMethodName:表示Bean销毁时要执行的方法
- 还有很多...
在Spring中,我们经常会通过以下几种方式来定义Bean:
- @Bean
- @Component(@Service,@Controller)
这些,我们可以称之声明式定义Bean。 我们还可以编程式定义Bean,那就是直接通过BeanDefinition,比如:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// 生成一个BeanDefinition对象,并设置beanClass为User.class,并注册到ApplicationContext中
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition.setBeanClass(User.class);
context.registerBeanDefinition("user", beanDefinition);
System.out.println(context.getBean("user"));
我们还可以通过BeanDefinition设置一个Bean的其他属性:
beanDefinition.setScope("prototype"); // 设置作用域
beanDefinition.setInitMethodName("init"); // 设置初始化方法
beanDefinition.setLazyInit(true); // 设置懒加载
和申明式事务、编程式事务类似,通过,@Bean,@Component等申明式方式所定义的Bean,最终都会被Spring解析为对应的BeanDefinition对象,并放入Spring容器中。
BeanDefinitionReader
接下来,我们来介绍几种在Spring源码中所提供的BeanDefinition读取器(BeanDefinitionReader),这些BeanDefinitionReader在我们使用Spring时用得少,但在Spring源码中用得多,相当于Spring源码的基础设施。
AnnotatedBeanDefinitionReader
可以直接把某个类转换为BeanDefinition,并且会解析该类上的注解,比如
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
AnnotatedBeanDefinitionReader annotatedBeanDefinitionReader = new AnnotatedBeanDefinitionReader(context);
// 将User.class解析为BeanDefinition
annotatedBeanDefinitionReader.register(User.class);
System.out.println(context.getBean("user"));
注意:它能解析的注解是:@Conditional,@Scope、@Lazy、@Primary、@DependsOn、@Role、@Description
XmlBeanDefinitionReader
可以解析标签
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(context);
int i = xmlBeanDefinitionReader.loadBeanDefinitions("spring.xml");
System.out.println(context.getBean("user"));
ClassPathBeanDefinitionScanner
ClassPathBeanDefinitionScanner是扫描器,但是它的作用和BeanDefinitionReader类似,它可以进行扫描,扫描某个包路径,对扫描到的类进行解析,比如,扫描到的类上如果存在@Component注解,那么就会把这个类解析为一个BeanDefinition,比如:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.refresh();
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
scanner.scan("com.zhouyu");
System.out.println(context.getBean("userService"));
BeanFactory
BeanFactory表示Bean工厂,所以很明显,BeanFactory会负责创建Bean,并且提供获取Bean的API。 而 ApplicationContext 是 BeanFactory的一种,在Spring源码中,是这么定义的:
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
...
}
首先,在Java中,接口是可以多继承的,我们发现 ApplicationContext继承了 ListableBeanFactory和HierarchicalBeanFactory,而 ListableBeanFactory和 HierarchicalBeanFactory都继承至 BeanFactory,所以我们可以认为 ApplicationContext继承了BeanFactory,相当于苹果继承水果,宝马继承汽车一样,ApplicationContext也是BeanFactory的一种,拥有BeanFactory支持的所有功能,不过ApplicationContext比BeanFactory更加强大,ApplicationContext还基础了其他接口,也就表示ApplicationContext还拥有其他功能,比如MessageSource表示国际化,ApplicationEventPublisher表示事件发布,EnvironmentCapable表示获取环境变量,等等,关于ApplicationContext后面再详细讨论。 在Spring的源码实现中,当我们new一个ApplicationContext时,其底层会new一个BeanFactory出来,当使用ApplicationContext的某些方法时,比如getBean(),底层调用的是BeanFactory的getBean()方法。
在Spring源码中,BeanFactory接口存在一个非常重要的实现类是:DefaultListableBeanFactory,也是非常核心的。具体重要性,随着后续课程会感受更深。 所以,我们可以直接来使用DefaultListableBeanFactory,而不用使用ApplicationContext的某个实现类,比如:
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition.setBeanClass(User.class);
beanFactory.registerBeanDefinition("user", beanDefinition);
System.out.println(beanFactory.getBean("user"));
DefaultListableBeanFactory是非常强大的,支持很多功能,可以通过查看DefaultListableBeanFactory的类继承实现结构来看

它实现了很多接口,表示,它拥有很多功能:
- AliasRegistry:支持别名功能,一个名字可以对应多个别名
- BeanDefinitionRegistry:可以注册、保存、移除、获取某个BeanDefinition
- BeanFactory:Bean工厂,可以根据某个bean的名字、或类型、或别名获取某个Bean对象
- SingletonBeanRegistry:可以直接注册、获取某个单例Bean
- SimpleAliasRegistry:它是一个类,实现了AliasRegistry接口中所定义的功能,支持别名功能
- ListableBeanFactory:在BeanFactory的基础上,增加了其他功能,可以获取所有BeanDefinition的beanNames,可以根据某个类型获取对应的beanNames,可以根据某个类型获取{类型:对应的Bean}的映射关系
- HierarchicalBeanFactory:在BeanFactory的基础上,添加了获取父BeanFactory的功能
- DefaultSingletonBeanRegistry:它是一个类,实现了SingletonBeanRegistry接口,拥有了直接注册、获取某个单例Bean的功能
- ConfigurableBeanFactory:在HierarchicalBeanFactory和SingletonBeanRegistry的基础上,添加了设置父BeanFactory、类加载器(表示可以指定某个类加载器进行类的加载)、设置Spring EL表达式解析器(表示该BeanFactory可以解析EL表达式)、设置类型转化服务(表示该BeanFactory可以进行类型转化)、可以添加BeanPostProcessor(表示该BeanFactory支持Bean的后置处理器),可以合并BeanDefinition,可以销毁某个Bean等等功能
- FactoryBeanRegistrySupport:支持了FactoryBean的功能
- AutowireCapableBeanFactory:是直接继承了BeanFactory,在BeanFactory的基础上,支持在创建Bean的过程中能对Bean进行自动装配
- AbstractBeanFactory:实现了ConfigurableBeanFactory接口,继承了FactoryBeanRegistrySupport,这个BeanFactory的功能已经很全面了,但是不能自动装配和获取beanNames
- ConfigurableListableBeanFactory:继承了ListableBeanFactory、AutowireCapableBeanFactory、ConfigurableBeanFactory
- AbstractAutowireCapableBeanFactory:继承了AbstractBeanFactory,实现了AutowireCapableBeanFactory,拥有了自动装配的功能
- DefaultListableBeanFactory:继承了AbstractAutowireCapableBeanFactory,实现了ConfigurableListableBeanFactory接口和BeanDefinitionRegistry接口,所以DefaultListableBeanFactory的功能很强大
ApplicationContext
上面有分析到,ApplicationContext是个接口,实际上也是一个BeanFactory,不过比BeanFactory更加强大,比如:
- HierarchicalBeanFactory:拥有获取父BeanFactory的功能
- ListableBeanFactory:拥有获取beanNames的功能
- ResourcePatternResolver:资源加载器,可以一次性获取多个资源(文件资源等等)
- EnvironmentCapable:可以获取运行时环境(没有设置运行时环境功能)
- ApplicationEventPublisher:拥有广播事件的功能(没有添加事件监听器的功能)
- MessageSource:拥有国际化功能
具体的功能演示,后面会有。 我们先来看ApplicationContext两个比较重要的实现类:
- AnnotationConfigApplicationContext
- ClassPathXmlApplicationContext
AnnotationConfigApplicationContext

- ConfigurableApplicationContext:继承了ApplicationContext接口,增加了,添加事件监听器、添加BeanFactoryPostProcessor、设置Environment,获取ConfigurableListableBeanFactory等功能
- AbstractApplicationContext:实现了ConfigurableApplicationContext接口
- GenericApplicationContext:继承了AbstractApplicationContext,实现了BeanDefinitionRegistry接口,拥有了所有ApplicationContext的功能,并且可以注册BeanDefinition,注意这个类中有一个属性(DefaultListableBeanFactory beanFactory)
- AnnotationConfigRegistry:可以单独注册某个为类为BeanDefinition(可以处理该类上的**@Configuration注解**,已经可以处理**@Bean注解**),同时可以扫描
- AnnotationConfigApplicationContext:继承了GenericApplicationContext,实现了AnnotationConfigRegistry接口,拥有了以上所有的功能
ClassPathXmlApplicationContext

它也是继承了AbstractApplicationContext,但是相对于AnnotationConfigApplicationContext而言,功能没有AnnotationConfigApplicationContext强大,比如不能注册BeanDefinition
国际化
先定义一个MessageSource:
@Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("messages");
return messageSource;
}
有了这个Bean,你可以在你任意想要进行国际化的地方使用该MessageSource。 同时,因为ApplicationContext也拥有国家化的功能,所以可以直接这么用:
context.getMessage("test", null, new Locale("en_CN"))
资源加载
ApplicationContext还拥有资源加载的功能,比如,可以直接利用ApplicationContext获取某个文件的内容:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.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(AppConfig.class);
Resource resource = context.getResource("file://D:\\IdeaProjects\\spring-framework-5.3.10\\tuling\\src\\main\\java\\com\\zhouyu\\service\\UserService.java");
System.out.println(resource.contentLength());
System.out.println(resource.getFilename());
Resource resource1 = context.getResource("https://www.baidu.com");
System.out.println(resource1.contentLength());
System.out.println(resource1.getURL());
Resource resource2 = context.getResource("classpath:spring.xml");
System.out.println(resource2.contentLength());
System.out.println(resource2.getURL());
还可以一次性获取多个:
Resource[] resources = context.getResources("classpath:com/zhouyu/*.class");
for (Resource resource : resources) {
System.out.println(resource.contentLength());
System.out.println(resource.getFilename());
}
获取运行时环境
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
Map<String, Object> systemEnvironment = context.getEnvironment().getSystemEnvironment();
System.out.println(systemEnvironment);
System.out.println("=======");
Map<String, Object> systemProperties = context.getEnvironment().getSystemProperties();
System.out.println(systemProperties);
System.out.println("=======");
MutablePropertySources propertySources = context.getEnvironment().getPropertySources();
System.out.println(propertySources);
System.out.println("=======");
System.out.println(context.getEnvironment().getProperty("NO_PROXY"));
System.out.println(context.getEnvironment().getProperty("sun.jnu.encoding"));
System.out.println(context.getEnvironment().getProperty("zhouyu"));
注意,可以利用
@PropertySource("classpath:spring.properties")
来使得某个properties文件中的参数添加到运行时环境中
事件发布
先定义一个事件监听器
@Bean
public ApplicationListener applicationListener() {
return new ApplicationListener() {
@Override
public void onApplicationEvent(ApplicationEvent event) {
System.out.println("接收到了一个事件");
}
};
}
然后发布一个事件:
context.publishEvent("kkk");
类型转化
在Spring源码中,有可能需要把String转成其他类型,所以在Spring源码中提供了一些技术来更方便的做对象的类型转化,关于类型转化的应用场景, 后续看源码的过程中会遇到很多。
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<>();
// 表示StringToUserPropertyEditor可以将String转化成User类型,在Spring源码中,如果发现当前对象是String,而需要的类型是User,就会使用该PropertyEditor来做类型转化
propertyEditorMap.put(User.class, StringToUserPropertyEditor.class);
customEditorConfigurer.setCustomEditors(propertyEditorMap);
return customEditorConfigurer;
}
假设现在有如下Bean:
@Component
public class UserService {
@Value("xxx")
private User user;
public void test() {
System.out.println(user);
}
}
那么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);
OrderComparator
OrderComparator是Spring所提供的一种比较器,可以用来根据@Order注解或实现Ordered接口来执行值进行笔记,从而可以进行排序。比如:
public class A implements Ordered {
@Override
public int getOrder() {
return 3;
}
@Override
public String toString() {
return this.getClass().getSimpleName();
}
}
public class B implements Ordered {
@Override
public int getOrder() {
return 2;
}
@Override
public String toString() {
return this.getClass().getSimpleName();
}
}
public class Main {
public static void main(String[] args) {
A a = new A(); // order=3
B b = new B(); // order=2
OrderComparator comparator = new OrderComparator();
System.out.println(comparator.compare(a, b)); // 1
List list = new ArrayList<>();
list.add(a);
list.add(b);
// 按order值升序排序
list.sort(comparator);
System.out.println(list); // B,A
}
}
另外,Spring中还提供了一个OrderComparator的子类:AnnotationAwareOrderComparator,它支持用@Order来指定order值。比如:
@Order(3)
public class A {
@Override
public String toString() {
return this.getClass().getSimpleName();
}
}
@Order(2)
public class B {
@Override
public String toString() {
return this.getClass().getSimpleName();
}
}
public class Main {
public static void main(String[] args) {
A a = new A(); // order=3
B b = new B(); // order=2
AnnotationAwareOrderComparator comparator = new AnnotationAwareOrderComparator();
System.out.println(comparator.compare(a, b)); // 1
List list = new ArrayList<>();
list.add(a);
list.add(b);
// 按order值升序排序
list.sort(comparator);
System.out.println(list); // B,A
}
}
BeanPostProcessor
BeanPostProcess表示Bena的后置处理器,我们可以定义一个或多个BeanPostProcessor,比如通过一下代码定义一个BeanPostProcessor:
@Component
public class ZhouyuBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if ("userService".equals(beanName)) {
System.out.println("初始化前");
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if ("userService".equals(beanName)) {
System.out.println("初始化后");
}
return bean;
}
}
一个BeanPostProcessor可以在任意一个Bean的初始化之前以及初始化之后去额外的做一些用户自定义的逻辑,当然,我们可以通过判断beanName来进行针对性处理(针对某个Bean,或某部分Bean)。 我们可以通过定义BeanPostProcessor来干涉Spring创建Bean的过程。
BeanFactoryPostProcessor
BeanFactoryPostProcessor表示Bean工厂的后置处理器,其实和BeanPostProcessor类似,BeanPostProcessor是干涉Bean的创建过程,BeanFactoryPostProcessor是干涉BeanFactory的创建过程。比如,我们可以这样定义一个BeanFactoryPostProcessor:
@Component
public class ZhouyuBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("加工beanFactory");
}
}
我们可以在postProcessBeanFactory()方法中对BeanFactory进行加工。
FactoryBean
上面提到,我们可以通过BeanPostPorcessor来干涉Spring创建Bean的过程,但是如果我们想一个Bean完完全全由我们来创造,也是可以的,比如通过FactoryBean:
@Component
public class ZhouyuFactoryBean implements FactoryBean {
@Override
public Object getObject() throws Exception {
UserService userService = new UserService();
return userService;
}
@Override
public Class<?> getObjectType() {
return UserService.class;
}
}
通过上面这段代码,我们自己创造了一个UserService对象,并且它将成为Bean。但是通过这种方式创造出来的UserService的Bean,只会经过初始化后,其他Spring的生命周期步骤是不会经过的,比如依赖注入。 有同学可能会想到,通过@Bean也可以自己生成一个对象作为Bean,那么和FactoryBean的区别是什么呢?其实在很多场景下他俩是可以替换的,但是站在原理层面来说的,区别很明显,@Bean定义的Bean是会经过完整的Bean生命周期的。
ExcludeFilter和IncludeFilter
这两个Filter是Spring扫描过程中用来过滤的。ExcludeFilter表示排除过滤器,IncludeFilter表示包含过滤器。 比如以下配置,表示扫描com.zhouyu这个包下面的所有类,但是排除UserService类,也就是就算它上面有@Component注解也不会成为Bean。
@ComponentScan(value = "com.zhouyu",
excludeFilters = {@ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE,
classes = UserService.class)}.)
public class AppConfig {
}
再比如以下配置,就算UserService类上没有@Component注解,它也会被扫描成为一个Bean。
@ComponentScan(value = "com.zhouyu",
includeFilters = {@ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE,
classes = UserService.class)})
public class AppConfig {
}
FilterType分为:
- ANNOTATION:表示是否包含某个注解
- ASSIGNABLE_TYPE:表示是否是某个类
- ASPECTJ:表示否是符合某个Aspectj表达式
- REGEX:表示是否符合某个正则表达式
- CUSTOM:自定义
在Spring的扫描逻辑中,默认会添加一个AnnotationTypeFilter给includeFilters,表示默认情况下Spring扫描过程中会认为类上有@Component注解的就是Bean。
MetadataReader、ClassMetadata、AnnotationMetadata
在Spring中需要去解析类的信息,比如类名、类中的方法、类上的注解,这些都可以称之为类的元数据,所以Spring中对类的元数据做了抽象,并提供了一些工具类。 MetadataReader表示类的元数据读取器,默认实现类为SimpleMetadataReader。比如:
public class Test {
public static void main(String[] args) throws IOException {
SimpleMetadataReaderFactory simpleMetadataReaderFactory = new SimpleMetadataReaderFactory();
// 构造一个MetadataReader
MetadataReader metadataReader = simpleMetadataReaderFactory.getMetadataReader("com.zhouyu.service.UserService");
// 得到一个ClassMetadata,并获取了类名
ClassMetadata classMetadata = metadataReader.getClassMetadata();
System.out.println(classMetadata.getClassName());
// 获取一个AnnotationMetadata,并获取类上的注解信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
for (String annotationType : annotationMetadata.getAnnotationTypes()) {
System.out.println(annotationType);
}
}
}
需要注意的是,SimpleMetadataReader去解析类时,使用的ASM技术。 为什么要使用ASM技术,Spring启动的时候需要去扫描,如果指定的包路径比较宽泛,那么扫描的类是非常多的,那如果在Spring启动时就把这些类全部加载进JVM了,这样不太好,所以使用了ASM技术。
启动相关
Spring 核心知识点
- Bean的生命周期底层原理
- 依赖注入底层原理
- 初始化底层原理
- 推断构造方法底层原理
- AOP底层原理
- Spring事务底层原理
Spring 启动
下面是 spring 启动的代码
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = (UserService) context.getBean("userService");
userService.test();
ClassPathXmlApplicationContext其实已经过时了,在新版的Spring MVC和Spring Boot的底层主要用的都是AnnotationConfigApplicationContext,比如:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
//ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = (UserService) context.getBean("userService");
userService.test();
可以看到AnnotationConfigApplicationContext的用法和ClassPathXmlApplicationContext是非常类似的,只不过需要传入的是一个class,而不是一个xml文件。 而AppConfig.class和spring.xml一样,表示Spring的配置,比如可以指定扫描路径,可以直接定义Bean,比如: spring.xml中的内容为:
<context:component-scan base-package="com.zhouyu"/>
<bean id="userService" class="com.zhouyu.service.UserService"/>
AppConfig中的内容为:
@ComponentScan("com.zhouyu")
public class AppConfig {
@Bean
public UserService userService(){
return new UserService();
}
}
所以spring.xml和AppConfig.class本质上是一样的。 目前,我们基本很少直接使用上面这种方式来用Spring,而是使用Spring MVC,或者Spring Boot,但是它们都是基于上面这种方式的,都需要在内部去创建一个ApplicationContext的,只不过:
- Spring MVC创建的是XmlWebApplicationContext,和ClassPathXmlApplicationContext类似,都是基于XML配置的
- Spring Boot创建的是AnnotationConfigApplicationContext
因为AnnotationConfigApplicationContext是比较重要的,并且AnnotationConfigApplicationContext和ClassPathXmlApplicationContext大部分底层都是共同的,后续课程我们会着重将AnnotationConfigApplicationContext的底层实现,对于ClassPathXmlApplicationContext,同学们可以在课程结束后作为作业,业余时间看看相关源码即可。
Spring中是如何创建一个对象?
其实不管是 AnnotationConfigApplicationContext 还是 ClassPathXmlApplicationContext,目前,我们都可以简单的将它们理解为就是用来创建Java对象的,比如调用 getBean() 就会去创建对象(此处不严谨,getBean可能也不会去创建对象,后续课程详解)。 在Java语言中,肯定是根据某个类来创建一个对象的。我们在看一下实例代码:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = (UserService) context.getBean("userService");
userService.test();
当我们调用context.getBean("userService")时,就会去创建一个对象,但是getBean方法内部怎么知道"userService"对应的是UserService类呢? 所以,我们就可以分析出来,在调用AnnotationConfigApplicationContext的构造方法时,也就是第一行代码,会去做一些事情:
解析AppConfig.class,得到扫描路径
遍历扫描路径下的所有Java类,如果发现某个类上存在@Component、@Service等注解,那么Spring就把这个类记录下来,存在一个Map中,比如Map<String, Class>。(实际上,Spring源码中确实存在类似的这么一个Map,叫做BeanDefinitionMap)
Spring会根据某个规则生成当前类对应的beanName,作为key存入Map,当前类作为value (一般将类名的首字母小写作为默认的
bean名称)
这样,但调用context.getBean("userService")时,就可以根据"userService"找到UserService类,从而就可以去创建对象了。
Bean的创建过程
那么Spring到底是如何来创建一个Bean的呢,这个就是Bean创建的生命周期,大致过程如下:
- 推断构造方法: 利用该类的构造方法来实例化得到一个对象(但是如果一个类中有多个构造方法,Spring则会进行选择,这个叫做推断构造方法)
- 依赖注入: 得到一个对象后,Spring会判断该对象中是否存在被@Autowired注解了的属性,把这些属性找出来并由Spring进行赋值
- Aware回调: 依赖注入后,Spring会判断该对象是否实现了BeanNameAware接口、BeanClassLoaderAware接口、BeanFactoryAware接口,如果实现了,就表示当前对象必须实现该接口中所定义的setBeanName()、setBeanClassLoader()、setBeanFactory()方法,那Spring就会调用这些方法并传入相应的参数()
- 初始化前: Aware回调后,Spring会判断该对象中是否存在某个方法被@PostConstruct注解了,如果存在,Spring会调用当前对象的此方法
- 初始化: 紧接着,Spring会判断该对象是否实现了InitializingBean接口,如果实现了,就表示当前对象必须实现该接口中的afterPropertiesSet()方法,那Spring就会调用当前对象中的afterPropertiesSet()方法
- 初始化后: 最后,Spring会判断当前对象需不需要进行AOP,如果不需要那么Bean就创建完了,如果需要进行AOP,则会进行动态代理并生成一个代理对象做为Bean
- 执行接口方法 afterSingletonsInstantiated

通过最后一步,我们可以发现,当Spring根据UserService类来创建一个Bean时:
- 如果不用进行AOP,那么Bean就是UserService类的构造方法所得到的对象。
- 如果需要进行AOP,那么Bean就是UserService的代理类所实例化得到的对象,而不是UserService本身所得到的对象 - 使用代理类实例化对象替代原始实例对象。
Bean对象创建出来后:
- 单例池: 如果当前Bean是单例Bean,那么会把该Bean对象存入一个Map<String, Object>,Map的key为beanName,value为Bean对象。这样下次getBean时就可以直接从Map中拿到对应的Bean对象了。(实际上,在Spring源码中,这个Map就是单例池)
- 多例 Bean: 如果当前Bean是原型Bean,那么后续没有其他动作,不会存入一个Map,下次getBean时会再次执行上述创建过程,得到一个新的Bean对象。
推断构造方法
Spring在基于某个类生成Bean的过程中,需要利用该类的构造方法来实例化得到一个对象,但是如果一个类存在多个构造方法,Spring会使用哪个呢? Spring的判断逻辑如下:
- 如果一个类只存在一个构造方法,不管该构造方法是无参构造方法,还是有参构造方法,Spring都会用这个构造方法
- 如果一个类存在多个构造方法
- 这些构造方法中,存在一个无参的构造方法,那么Spring就会用这个无参的构造方法
- 这些构造方法中,不存在一个无参的构造方法,那么Spring就会报错
Spring的设计思想是这样的:
- 如果一个类只有一个构造方法,那么没得选择,只能用这个构造方法
- 如果一个类存在多个构造方法,Spring不知道如何选择,就会看是否有无参的构造方法,因为无参构造方法本身表示了一种默认的意义
- 不过如果某个构造方法上加了@Autowired注解,那就表示程序员告诉Spring就用这个加了注解的方法,那Spring就会用这个加了@Autowired注解构造方法了
需要重视的是,如果Spring选择了一个有参的构造方法,Spring在调用这个有参构造方法时,需要传入参数,那这个参数是怎么来的呢?
有参构造单例 Bean 的逻辑
Spring会根据入参的类型和入参的名字去Spring中找Bean对象(以单例Bean为例,Spring会从单例池那个Map中去找):
- 先根据入参类型找,如果只找到一个,那就直接用来作为入参
- 如果根据类型找到多个,则再根据入参名字来确定唯一一个
- 最终如果没有找到,则会报错,无法创建当前Bean对象
确定用哪个构造方法,确定入参的Bean对象,这个过程就叫做推断构造方法。
AOP大致流程
AOP就是进行动态代理,在创建一个Bean的过程中,Spring在最后一步会去判断当前正在创建的这个Bean是不是需要进行AOP,如果需要则会进行动态代理。 如何判断当前Bean对象需不需要进行AOP:
- 找出所有的切面Bean
- 遍历切面中的每个方法,看是否写了@Before、@After等注解
- 如果写了,则判断所对应的Pointcut是否和当前Bean对象的类是否匹配
- 如果匹配则表示当前Bean对象有匹配的的Pointcut,表示需要进行AOP
利用cglib进行AOP的大致流程:
- 生成代理类UserServiceProxy,代理类继承UserService
- 代理类中重写了父类的方法,比如UserService中的test()方法
- 代理类中还会有一个target属性,该属性的值为被代理对象(也就是通过UserService类推断构造方法实例化出来的对象,进行了依赖注入、初始化等步骤的对象)
- 代理类中的test()方法被执行时的逻辑如下:
- 执行切面逻辑(@Before)
- 调用target.test()
- 调用切面后逻辑 @after
当我们从Spring容器得到UserService的Bean对象时,拿到的就是UserServiceProxy所生成的对象,也就是代理对象。 UserService代理对象.test()--->执行切面逻辑--->target.test(),注意target对象不是代理对象,而是被代理对象。
Spring事务
概述
当我们在某个方法上加了@Transactional注解后,就表示该方法在调用时会开启Spring事务,而这个方法所在的类所对应的Bean对象会是该类的代理对象。 Spring事务的代理对象执行某个方法时的步骤:
- 判断当前执行的方法是否存在@Transactional注解
- 如果存在,则利用事务管理器(TransactionMananger)新建一个数据库连接
- 修改数据库连接的autocommit为false
- 执行target.test(),执行程序员所写的业务逻辑代码,也就是执行sql
- 执行完了之后如果没有出现异常,则提交,否则回滚
Spring事务是否会失效的判断标准:**某个加了 @Transactional 注解的方法被调用时,要判断到底是不是直接被代理对象调用的,如果是则事务会生效,如果不是则失效。
事务失效的几种情况
- 数据库引擎是否支持事务(MYSQL的 MyIsam 引擎不支持事务) MYSQL 从 5.5.5 开始默认InnoDB,之前默认 MyIsam。
- @Trasanctional 注解的方法所在的类是否被 spring 容器管理(是否加载为 spring bean)
- 方法必须是 public 的
- 调用自身的方法 - 事务注解的方法A内部调用和A在同一个类内的其他方法
- 数据源没有配置事务管理器
- 异常被 catch 住,没有抛出,或者抛出其他类型的异常(默认回滚的是 RuntimeException)
解决调用自身方法导致事务失效
- 配置中启用循环依赖
- 在服务中注入自己作为属性,通过该属性调用其他方法

Bean
Spring创建的bean和new对象的不同
通过 spring 创建的 bean 都会通过依赖注入为属性赋值,而 new 出来的对象的属性则没有值
Spring创建 bean 的过程
- 调用无参构造方法创建对象
- 依赖注入 - @Autowired
- 初始化前 - @PostConstruct
- 初始化 - spring调用接口 InitializingBean 的方法 afterPropertiesSet(实现了该接口的 bean class)
- 初始化后 - 如果有切面则创建代理对象,并将前面得到的普通对象赋值给代理对象中名称为 target 的属性 最终由新生成的代理对象替代前面创建的普通对象
- 得到完全体 bean
创建对象的构造方法
- 没有构造方法 - 使用隐含的无参构造创建对象
- 只有一个构造方法 - 无论有没有参数都调用该构造方法创建对象
- 多个构造方法中有一个无参的 - 调用无参构造方法
- 多个构造方法中没有无参的 - 报错,无法创建对象,spring 不知道要调用哪个构造方法 要解决该问题需要使用 @Autowired 指定使用哪个构造方法
单例池
bean 被创建出来后会存放在类型是 Map<String,Object> 的 map 对象中,key 是 bean 的名称,value 是 bean 对象。之后通过 getBean 获取 bean 时只要从该 map 对象中获取即可。
依赖注入时有多个同类型的 bean
依赖注入时先从容器中按照类型查找 bean 如果只有一个则直接使用,如果有多个再按照 bean 的名称匹配,如果匹配不到则报错。
AOP
在创建 bean 的最后环节根据是否有AOP决定是否生成代理对象替换原始对象放入容器 - 有AOP则创建代理对象放入容器。代理对象中有名称为 target 的属性,将创建 bean 过程中前面部分创建好的普通对象赋值。 调用方法也是调用 target 的方法,而不是通过 super 调用
事务
事务的执行过程:
- 事务管理器先获取一个新的数据库连接(TransactionManager),并设置自动提交为 false
- 指定用户定义的方法
- 出现异常则回滚,否则提交
事务失效的其中一种情况是在事务方法内又调用了自身的事务方法。解决该情况有两种方法:
- 将后面逻辑调用的事务方法拆分到其他类中,调用该其他类的该事务方法
- 服务类注入自身 bean 作为属性,通过该属性调用事务方法
3个Aware接口
spring 容器初始化过程中会扫描带有这些接口的 bean 类并调用这些接口的实现方法,来达到调用用户代码的目的。实际也可以在需要的地方通过使用注解自动注入,如下面代码
@Autowired
private ApplicationContext context;
这样 bean 类就不用实现接口 ApplicationContextAware ,也能获取到 ApplicationContext 
