springIOC

1. 概念

  • IoC = inversion of control 控制反转 ,指的是把对象创建和管理的控制权交给框架、容器来管理。
  • DI=dependency injection 依赖注入,是把对象依赖的其他对象通过不同方法传递给该对象的设计模式,通过依赖注入模式,避免对象因依赖关系产生的高耦合
  • IoC容器 是通过使用管理容器来实现 IoC、DI,第一步是注册对象间的依赖关系到container中,第二步是通过给出的对象依赖关系,由容器来管理和创建所有对象,通过依赖注入模式,实现控制反转,实现对象的自动化管理。

2. IoC容器

  • 管理对象依赖关系
    • 直接编码写入
    • xml配置文件
    • 元数据(注解)
  • 负责对象的创建和管理

3. BeanFactory

BeanFactory 默认是lazy-load,而applicationContext则默认是全部init完成的。

BeanFactory和ApplicationContext
BeanFactory接口的主要方法:

public interface BeanFactory {
	Object getBean(String name) throws BeansException;
	<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
containsBean  
isSingleton
isPrototype
isTypeMatch
isTypeMatch
getType
getAliases
}
  • 对象注册和依赖关系的表达方式-configuration metadata
    1. 直接编码
    2. 外部配置(configuration.xml、properties文件)通过xmlBeanDefinationReader、PropertiesBeanDefinationReader读取器来读取配置文件,把对应的配置读取到BeanDefination中,然后再把BeanDefination添加进BeanDefinationRegistry,同时Registry和BeanFactory是一体两面的,最终也就得到了可用的IOC容器。
    3. 注解方式
      classpath-scaning 类路径扫描,也就是通过扫描器来对指定的路劲进行加载扫描,把对应注解的类进行指定加载从而实现注解功能。
      context:componant-scan/ 扫描器功能
      @autowired、@resource 一个是bytype、一个是byname的加载模式

BeanDefinationRegistry 负责实现bean的注册管理工作,而BeanFactory则是负责从容器中对bean的获取访问。
Registry如何管理bean的注册?通过管理BeanDefination,beanDefination负责保存bean和bean的依赖关系的描述组成的bean对象的必要信息,当BeanFactory向容器请求一个对象的时候,就是通过Registry来查找到对应的BeanDefination,通过BeanDefination来构建出需要的对象实例。

BeanFactory和BeanDefinationRegistry

xml配置

  • beans
    1. 属性
      default-lazy-init 默认 false
      default-autowire 默认no, no、byname、bytype、constructor等
      default-dependency-check 默认no
      default-init-method和default-destroy-method 初始化方法和销毁方法
    2. 子标签
      description:描述信息
      import 导入其他的配置文件
      alias 可以给某些bean进行别名定义
      bean 对bean的定义
  • bean的scope 作用域
    singleton
    prototype
    request、session和global session

工厂方法FactoryBean

Public interface FactoryBean<T>{
T getObject() throws Exception;
Class<?> getObjectType();
default boolean isSingleton() {
		return true;
	}
}

FactoryBean接口在ioc容器中使用时,ioc容器会自动调用FactoryBean的getObject方法获取到对应的Bean对象,这样就可以通过FactoryBean实现复杂的组装和装配过程。这是IOC容器的内部机制!

容器启动的过程

  1. 通过BeanDefinationReader加载configuration metadata
  2. 把BeanDefination 保存到BeanDefinationRegistry中
  3. Registry加载到BeanFactory中(一般BeanFactory同时会实现BeanDefinationRegistry接口)
  4. Bean实例化, 在请求容器获取Bean时,会检查Bean是否存在,若不存在则实例化并保存后返回给出使用,若已经存在则直接返回(此流程是单例scope的情况)
  • 容器的实例化干涉机制:BeanFactoryPostProcessor
    org.springframework.beans.factory.config.PropertyPlaceholderConfigurer,进行properties中的占位符信息替换 ${}
    org.springframework.beans.factory.config.PropertyOverrideConfigurer,进行properties信息覆盖

Bean的创建过程

Bean实例化

可以在org.springframework.beans.factory.support.AbstractBeanFactory类的代
码中查看到getBean()方法的完整实现逻辑,可以在其子类org.springframework.beans.
factory.support.AbstractAutowireCapableBeanFactory的代码中一窥createBean()方
法的全貌。

在完成容器的初始化过程后,此时容器中已经拥有了所有的BeanDefinition对象,但并未进行bean的实例化,BeanFactory通过显示的getBean来实例化,ApplicationContext则是在初始化完成后内部调用getBean进行隐式初始化创建(也即自动创建)

applicationcontext 默认在启动后实例化所有的Bean:
参考:org.springframework.context.support.AbstractApplicationContext.refresh();

  1. BeanWrapper
    org.springframework.beans.factory.support.InstantiationStrategy
    CglibSubclassingInstantiationStrategy
    image.png
    BeanWrapper是对bean实例的一个包装器类,该类可以通过其实现的接口方便的给bean进行属性赋值操作,从而实现bean的实例化->属性赋值->init等动作。
    image.png
Object provider =Class.forName("package.name.FXNewsProvider").newInstance(); 
Object listener=Class.forName("package.name.DowJonesNewsListener").newInstance();
Object persister=Class.forName("package.name.DowJonesNewsPersister").newInstance();
Class providerClazz = provider.getClass();
Field listenerField = providerClazz.getField("newsListener");
listenerField.set(provider, listener);
Field persisterField = providerClazz.getField("newsListener");
persisterField.set(provider, persister);
assertSame(listener, listenerField.get(provider));
assertSame(persister, persisterField.get(provider)); 
  1. aware接口
    在通过beanwraper实例化出bean并完成了属性赋值之后,spring容器会检查bean是否实现了各种aware接口,并把这些aware的类注入到bean中
  2. BeanPostProcessor
    类似于BeanFactoryPostProcessor,用在bean的实例化后,返回调用前,进行后置的拦截处理
  3. InitializingBean和init-Method
    在postprocessor处理之后,会调用设置的这两个方法进行初始化操作
  4. distroy-method
    在使用完bean之后,进行bean的回收和distroy

4. ApplicationContext

统一资源、资源加载

org.springframework.core.io.Resource
org.springframework.core.io.ResourceLoader
Resource作为spring中的统一的资源抽象和访问接口,用来表示spring使用的所有的资源。
ResourceLoader 是资源查找定位策略的统一抽象。

public interface ResourceLoader {

	/** Pseudo URL prefix for loading from the class path: "classpath:" */
	String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;


	/**
	 * Return a Resource handle for the specified resource location.
	 * <p>The handle should always be a reusable resource descriptor,
	 * allowing for multiple {@link Resource#getInputStream()} calls.
	 * <p><ul>
	 * <li>Must support fully qualified URLs, e.g. "file:C:/test.dat".
	 * <li>Must support classpath pseudo-URLs, e.g. "classpath:test.dat".
	 * <li>Should support relative file paths, e.g. "WEB-INF/test.dat".
	 * (This will be implementation-specific, typically provided by an
	 * ApplicationContext implementation.)
	 * </ul>
	 * <p>Note that a Resource handle does not imply an existing resource;
	 * you need to invoke {@link Resource#exists} to check for existence.
	 * @param location the resource location
	 * @return a corresponding Resource handle (never {@code null})
	 * @see #CLASSPATH_URL_PREFIX
	 * @see Resource#exists()
	 * @see Resource#getInputStream()
	 */
	Resource getResource(String location);

	/**
	 * Expose the ClassLoader used by this ResourceLoader.
	 * <p>Clients which need to access the ClassLoader directly can do so
	 * in a uniform manner with the ResourceLoader, rather than relying
	 * on the thread context ClassLoader.
	 * @return the ClassLoader (only {@code null} if even the system
	 * ClassLoader isn't accessible)
	 * @see org.springframework.util.ClassUtils#getDefaultClassLoader()
	 */
	ClassLoader getClassLoader();

}

从loader接口定义中可以知道spring中的常用的资源前缀符号和文件定位:

  1. file:C:/test.dat
  2. classpath:test.dat
  3. WEB-INF/test.dat

具体资源前缀修饰符在org.springframework.util.ResourceUtils中定义
io包中的resourceUML

org.springframework.context.support.AbstractApplicationContext-refresh 是加载的入口,通过在该方法中的刷新加载applicationcontext,并且通过定义的一系列postprocess 来对applicationContext来进行处理!

  • applicationcontext与resourceloader的关系
    abstractapplicationcontext同时实现了resourceloader和resourcePatternResolver,也就是同时拥有getResource()和getResources()两个方法,在具体实现中,protected ResourcePatternResolver getResourcePatternResolver() { return new PathMatchingResourcePatternResolver(this); }内部使用该方法构建并使用自身作为resourceloader传入,巧妙的利用自身实现双分派构建
    abstractApplicationContext的UML
  • applicationcontext在resource方面的应用
  1. 作为resourceloader使用
    applicationcontext本身就是一个resourceloader,因此如果要使用resourceloader,可以直接使用applicationcontext进行加载
  2. resourceloader的注入
    也可以让bean实现applicationcontextaware或者resourceloaderaware接口,这样spring会自动注入到bean中一个resourceloader。
  3. resource类型的自动注入
    applicationcontext 在启动时会使用一个org.springframework.beans.support.ResourceEditorRegistrar来注册Spring提供的针对Resource类型的PropertyEditor实现到容器中,这个PropertyEditor叫 做org.springframework.core.io.ResourceEditor。同时针对多个resource,会注册一个org.springframework.core.io.support.ResourceArrayPropertyEditor实现来进行array加载。我们通过CustomerEditorConfigurar来为applicationcontext配置加载的bean即可
  4. ApplicationContext中的Resource的资源路径定位
    在ApplicationContext中,由于对ResourceLoader和ResourcePatternResolver的实现添加了Classpath:和classpath*:两个前缀协议,
    而对于不同的ApplicationContext实现,也有不同的加载行为,classpathxmlApplicationContext默认会在classpath中加载资源,即使没有使用classpath前缀,而filesystemApplicationContext则会从文件系统加载,除非显示使用classpath,因此在使用时需要查看javadoc!
  • ApplicationContext内的事件机制
    通过java.util.EventListenerjava.util.EventObject的继承和复用,在org。springframework.context包中定义了ApplicationEvent、ApplicationListener、ApplicationEventPublisher接口,ApplicationContext就实现了一套事件发布机制,ApplicationContext本身继承并实现了ApplicationEvent,但具体工作委托给了其内部字段ApplicationEventMulticaster来进行处理。
    image.png
    在业务实现时,如果需要通过使用容器内的事件机制进行事件发布,一般可以使用这些方法
  1. ApplicationEventPublisherAware 直接注入一个Publisher来进行发布
  2. ApplicationContextAware,使用ApplicationContext作为publisher进行发布操作

IoC扩展知识

  • @autowired
    @autowired的自动注解,按照bytype的类型模式来进行依赖注入,可以使用在字段、方法、set方法和contructor上。其具体实现则是通过 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor这个Bean的PostProcessor来对Bean的实例化过程进行初始化拦截,从而实现注解解析功能。
  • @Qualifier
    @qualifier注解是按照byname 的方式进行模型注入,qualifier通过设置name字段,来告诉ApplicationContext为我们绑定哪个名字的bean
  • @Resource 和@PostConstructor、@PreDistroy
    这时Jsr250中的3个注解,其中的@Resource类似于@Qualifier,通过byname进行注入,但是其实现是通过不同的postprocessor,org.springframework.context.annotation.CommonAnnotationBeanPostProcessor,因此注意区分一下
    | 这两种分别属于不同的注解派系,虽然功能相似,但最好使用一个派系中的
  • classpath-scan
    <context:component-scan base-package=“org.spring21”/> 开启bean的自动扫描加载功能
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值