摘要
java基础我们大致掌握,现在我们将开始新的系列《spring 揭秘》,spring框架是为围绕我们java web最重要的框架。基于spring框架的spring boot,可以让我更加容易编写java项目,就如spring官网中spring boot 栏目所说的 Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can "just run".
本系列是基于《Spring 揭秘》这一本书来讲解的,结合spring 5.x.x源码。
spring 整体架构
spring框架迭代了很多次,其本质是始终不变的,都是为了提供各种服务简便我们的java开发。大体的框架如图下所示。
ioc容器
spring的架构图我们可以了解为一个树,ioc容器做为根部,枝叶部分就是spring的扩展,所以我们先从ioc容器开始了解spring。
IoC,它的全称为Inversion of Control,中文通常翻译为 “控制反转”, 通常我们把他叫做依赖注入。
我们需要什么功能,他就能主动为我们提供什么功能。
举一个简单的例子,在没有ioc容器的时代,我口渴了想喝水,首先我要去拿杯子,其次去找饮水机打水,
最后我才能愉快的把水喝上。现在有ioc容器的时候,我只要通知ioc容器,他就给我送上一瓶饮用水,我
直接就能喝了。省去了找饮水机这麻烦的事情。前提是ioc容器要注册一个送水的功能。
上面的例子我用伪代码展示,我们就能更好的理解ioc容器
//没有ioc容器时代
//饮水机
class Dispenser{
pubilc int carriage = 1;//送水服务
}
//我自己
class My{
private int water;// 0-时不能喝水 1-可以喝水
pubilc void setWater(dispenser){this.water = dispenser.carriage}
pubilc void drink(){
if(water == 1){
print("有水喝了");
}
}
//运行main方法
main{
My my = new my()
//寻找饮水机,送水
my.setWater(new Dispenser())
my.drink();
}
}
我要喝水一定要依赖饮水机
//ioc容器时代
//饮水机
@Compent//将饮水机注册到ioc容器中
class Dispenser{
pubilc int carriage = 1;//送水服务
}
class My{
private int water;// 0-时不能喝水 1-可以喝水
@Autowired//依赖注入
pubilc void setWater(dispenser){this.water = dispenser.carriage}
pubilc void drink(){
if(water == 1){
print("有水喝了");
}
}
//运行main方法
main{
My my = new my()
//寻找饮水机,送水
//my.setWater(new Dispenser())
my.drink();
}
}
直接省去了寻找饮水机的过程,通过Ioc容器的特殊能力依赖注入,就完成了,我们就可以直接进入我们
最终的目的。
书中有一个更加生动的图文。使用上ioc后,你身边就有一个超级管家,他什么都帮你做好。
Spring提供了两种ioc容器类型:BeanFactory和ApplicationContext。
BeanFactory
BeanFactory。基础类型IoC容器,提供完整的IoC服务支持。如果没有特殊指定,默认采用延迟初始化策略(lazy-load)。只有当客户端对象需要访问容器中的某个受管对象的时候,才对该受管对象进行初始化以及依赖注入操作。所以,相对来说,容器启动初期速度较快,所需要的资源有限。对于资源有限,并且功能要求不是很严格的场景,BeanFactory是比较合适的IoC容器选择。
ApplicationContext
ApplicationContext。ApplicationContext在BeanFactory的基础上构建,是相对比较高级的容器实现,除了拥有BeanFactory的所有支持,ApplicationContext还提供了其他高级特性,比如事件发布、国际化信息支持等。ApplicationContext所管理的对象,在该类型容器启动之后,默认全部初始化并绑定完成。所以,相对于BeanFactory来说,ApplicationContext要求更多的系统资源,同时,因为在启动时就完成所有初始化,容器启动时间较之BeanFactory也会长一些。 在那些系统资源充足, 并且要求更多功能的场景中,ApplicationContext类型的容器是比较合适的选择。
ApplicationContext继承BeanFactory,也就是扩展的BeanFactory的能力,其本质还是提供bean的能力,bean也就是我们常说的java对象。那么BeanFactory是如何完成依赖注入这一功能的?我们通过spring 5.x的源代码来查找原因。
从spring 5.X源码来看,启动spring项目是怎么样加载beanfactory的?
加载spring上下文applicationContext
// Invoke factory processors registered as beans in the context.
// 将我们的bean全部注册到applicationContext,我们继续跟着代码走
invokeBeanFactoryPostProcessors(beanFactory);
//bean扫描器,获取包名
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
//将该包下的所有带有spring注解的累注册到beanFactory
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
for (String basePackage : basePackages) {
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
applicationContext的特性是先注册bean,后通过getBean的方法完成bean的初始化
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
↓
// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();
我们来看下spring的getBean方法,是如何完成类的依赖注入的。
目前我们只看单例模式下的bean
AbstractAutowireCapableBeanFactory#populateBean实现依赖注入
通过不同的处理器完成注入
//AutowiredAnnotationBeanPostProcessor:这里就是解析该Bean的Autowired信息,然后给inject进去
@Override
public PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
metadata.inject(bean, beanName, pvs);
} catch (BeanCreationException ex) {
throw ex;
} catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
}
return pvs;
}
//CommonAnnotationBeanPostProcessor:逻辑和上面一毛一样,它处理的JSR-250的注解,比如@Resource
@Override
public PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);
try {
metadata.inject(bean, beanName, pvs);
} catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex);
}
return pvs;
}
//RequiredAnnotationBeanPostProcessor:它就是去校验,标注了@Required注解的,必须是是存在这个Bean的
@Override
public PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
if (!this.validatedBeanNames.contains(beanName)) {
if (!shouldSkip(this.beanFactory, beanName)) {
List<String> invalidProperties = new ArrayList<>();
for (PropertyDescriptor pd : pds) {
if (isRequiredProperty(pd) && !pvs.contains(pd.getName())) {
invalidProperties.add(pd.getName());
}
}
if (!invalidProperties.isEmpty()) {
throw new BeanInitializationException(buildExceptionMessage(invalidProperties, beanName));
}
}
this.validatedBeanNames.add(beanName);
}
return pvs;
}
在不同的处理的inject方法完成
#inject
一般我们常用到@Autowired
AutowiredAnnotationBeanPostProcessor#inject
@Override
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
// 拿到这个字段名
Field field = (Field) this.member;
Object value;
// 大多数情况下,这里都是false
if (this.cached) {
value = resolvedCachedArgument(beanName, this.cachedFieldValue);
} else {
DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
desc.setContainingClass(bean.getClass());
Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
Assert.state(beanFactory != null, "No BeanFactory available");
// 转换器,没有手动注册,默认都是SimpleTypeConverter===============
TypeConverter typeConverter = beanFactory.getTypeConverter();
try {
// 把当前bean所依赖的这个Bean解析出来(从Spring容器里面拿,或者别的地方获取吧~~~)
value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
} catch (BeansException ex) {
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
}
synchronized (this) {
if (!this.cached) {
// 如果已经拿到了这个Bean实例,
if (value != null || this.required) {
this.cachedFieldValue = desc;
// 把该Bean的依赖关系再注册一次
registerDependentBeans(beanName, autowiredBeanNames);
// 因为是按照类型注入,所以肯定只能指导一个这个的依赖Bean,否则上面解析就已经报错了
if (autowiredBeanNames.size() == 1) {
String autowiredBeanName = autowiredBeanNames.iterator().next();
// 这里是来处理放置缓存的字段值
if (beanFactory.containsBean(autowiredBeanName) &&
beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
this.cachedFieldValue = new ShortcutDependencyDescriptor(
desc, autowiredBeanName, field.getType());
}
}
} else {
this.cachedFieldValue = null;
}
this.cached = true;
}
}
}
// 依赖注入
if (value != null) {
ReflectionUtils.makeAccessible(field);
field.set(bean, value);
}
}
}
小总结
Ioc容器依赖注入,从解析bean信息开始,然后到bean的注入,都是交给不同bean去处理,这样的设计理念使的spring框架扩展能力极强。spring在前面做的都是非常非常多的准备工作,最后才进行注入过程,这样严格的方式,是的ioc的过程适合更多的场景。