《Spring 揭秘》 -- (1)

779 阅读6分钟

摘要

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的过程适合更多的场景。