文档章节

Spring 容器、Bean与生命周期

trayvon
 trayvon
发布于 03/19 20:29
字数 2395
阅读 97
收藏 4

Spring 容器、Bean与生命周期

简介

Spring最重要的部分之一就是提供了IoC容器,为什么需要IoC容器呢?IoC是依赖倒置,或者说是控制反转的意思,控制反转这个词也许我们耳朵已经听出茧子了,也正是因为如此,也阻碍了我们重新思考这个词。

现在,我们重新思考一下什么是"控制反转"?我们不下定义,我们就结合业务场景来思考,例如,我们的web应用,在Controller层会依赖Service层的逻辑,不同的Controller可能会依赖相同的service层对象。例如,库存和积分的Controller都会依赖用户的Service。

如果没有Spring,我们常见的做法是在每一个Controller中都创建用户的Service,这些创建逻辑可能比较复杂。

这样带来的问题就是,创建对象的逻辑分散的到处都是,每一地方都会创建新的对象。

Spring怎么解决这个问题的呢?使用IoC容器。依赖倒置提现在什么地方?原来是Controller依赖Service,现在不依赖了么?不是,不过控制权转移了,都是有Spring来管理,Spring创建对象,然后注入到需要的对象之中。

这样的好处是显而易见的,创建逻辑都不用我们管了,依赖对象也不用我们关心了,很多对象可以共享,不用重复创建、销毁。

Spring IoC容器创建流程

Spring工厂初始化

BeanDefinition

BeanDefinition

BeanDefinition解析

BeanDefinition解析有2个重要的接口,它们分别是BeanDefinitionReader和BeanDefinitionParser。

BeanDefinitionReader的作用是读取bean的信息,例如,BeanDefinitionReader的间接实现类XmlBeanDefinitionReader就是从xml文件中读取bean的信息。

读取到bean信息了,还需要把这些信息解析为BeanDefinition,这个过程是BeanDefinitionParser的职责,例如,AnnotationDrivenBeanDefinitionParser就是从注解中解析BeanDefinition。

在实际的解析中可能具体的解析类是其他的,例如XmlBeanDefinitionReader,实际注册是利用BeanDefinitionDocumentReader接口的实现类DefaultBeanDefinitionDocumentReader。

DefaultBeanDefinitionDocumentReader实际上只处理了一些标签,例如beans、alias、name、import、resource、profile等。

像常见的标签bean、ref等标签实际是BeanDefinitionParserDelegate处理的,BeanDefinitionParserDelegate也不会处理全部标签。

BeanDefinitionParserDelegate会通过NamespaceHandler来处理一些自定义标签,像下面的标签都是通过对应的NamespaceHandler实现类来处理的。

<tx:annotation-driven/>
<tx:advice id="txAdvice" transaction-manager="myTracnsactionManager">
<aop:config>
<dubbo:reference id="xxxService" interface="xxx.XxxService"/>
<context:annotation-config />
<mvc:interceptors>

NamespaceHandler会通过NamespaceHandlerSupport来找到一个合适的BeanDefinitionParser,然后通过BeanDefinitionParser的parse方法来解析出对应的BeanDefinition。

Spring Bean创建

有了BeanDefinition,怎样通过BeanDefinition创建真正的bean的呢?

bean创建

Spring生命周期

Spring钩子

BeanFactoryPostProcessor

BeanPostProcessor

BeanFactoryAware

BeanNameAware

InitializingBean

DisposableBean

InstantiationAwareBeanPostProcessorAdapter

InstantiationAwareBeanPostProcessor

bean注入

@Resource

  1. 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常
  2. 如果指定了name,则从上下文中查找name匹配的bean进行装配,找不到则抛出异常
  3. 如果指定了type,则从上下文中查找type匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常
  4. 如果既没有指定name也没有指定type,那么先按name方式,后安type方式,找不到则抛异常

@Autowired

按照类型匹配,默认必须找到,找不到就抛异常,如果不是必须,可以设置require=false。这个和通过xml配置autowire属性(也就是@Bean的autowire属性。)差不多。

不过xml方式autowire必须有setter方法,而@Autowired可以不用setter方法,并且@Autowired只有通过type注入,要通过name方式还得多加一个@Qualifier("name")注解。

@Autowired的注解处理器是AutowiredAnnotationBeanPostProcessor。

在AbstractAutowireCapableBeanFactory中有一个ignoreDependencyInterface方法,可以设置不允许setter注入,这个主要是为了避免一些特殊的类被随意注入了,例如ApplicationContext、BeanFactory等。这样要获取ApplicationContext必须实现ApplicationContextAware,而不仅仅是在类中添加一个ApplicationContext属性就可以了。

数据类型转换

最早的数据类型转换是使用的PropertyEditor,支持的是String和Object之间的转换,在Spring中主要用在2个地方。

  1. Spring xml bean配置属性,从xml的property的字符串转换为对象
  2. Spring MVC中HTTP request请求参数字符串转换为对象

一般不会直接实现PropertyEditor接口,而是直接继承PropertyEditorSupport类。

PropertyEditor是通过org.springframework.beans.factory.config.CustomEditorConfigurer注册的。也可以实现PropertyEditorRegistrar,然后将PropertyEditorRegistrar设置到CustomEditorConfigurer中。

注意PropertyEditorRegistry和PropertyEditorRegistrar,Registry是登记处,Registrar是以ar结尾,表示登记员的意思。所以顾名思义PropertyEditorRegistry是注册PropertyEditor的地方,PropertyEditorRegistrar的作用是把PropertyEditor注册到自己管理的PropertyEditorRegistry中。

PropertyEditor

Spring 3开始增加了一个Converter接口,可以完成任意2个类型之间的转换,这点比PropertyEditor强,PropertyEditor只能是String与Object之间的转换。

Converter的一般玩法是,实现ConverterFactory接口,在实现类中给一个静态内部类实现Converter。ConverterFactory的getConverter返回这个Converter就可以了。

如果是更加复杂的转换可以考虑GenericConverter和ConditionalConverter接口。

ConversionService接口为类型转换提供统一转换API来执行运行时的转换逻辑,使用的是外观模式(facade)。配置ConversionService只需要配置一个id为conversionService的org.springframework.context.support.ConversionServiceFactoryBean就可以了。自定义的Converter也可以通过ConversionServiceFactoryBean设置。

org.springframework.format.Formatter接口和PropertyEditor接口差不多,不过结构更加清晰,PropertyEditor接口有很多GUI相关的方法,而Spring的Formatter接口,就是继承了从类型T到String的Printer接口和从String到类型T的Parser接口。

注解驱动的Formatter可以实现AnnotationFormatterFactory提供支持,Formatter也有registry和registrar,它们分别是FormatterRegistry和FormatterRegistrar。

Spring MVC的AnnotationDrivenBeanDefinitionParser中如果,如果包含“conversion-service”就会注册一个FormattingConversionServiceFactoryBean,FormattingConversionServiceFactoryBean持有一个FormattingConversionService。

MVC数据转换

mvc中的数据转换是HttpInputMessage、HttpOutputMessage和Java数据类型之间的转换,实现HttpMessageConverter接口就可以了。

SpringMVC数据转换

我们知道MVC中有2个重要的接口一个是HandlerAdapter,另一个是HandlerMapping。简化点说HandlerMapping做的事情就是把请求URL找到对应的method来处理。HandlerAdapter看名字有点像适配器模式,实际上更像是代理模式。

HandlerAdapter的作用就是处理一些公共的逻辑,例如解析@RequestParam、@PathVariable、@RequestBody、@ResponseBody等注解...

想一想HTTP协议或者直接通过Servlet处理需要处理的逻辑,再对比一下我们使用@Controller和@RequestMapping注解之后的方法的逻辑,就知道HandlerAdapter做了多少事情了。正是因为如此、HandlerAdapter的可以定制的程度也高。

现在spring mvc中默认使用的HandlerAdapter和HandlerMapping是: RequestMappingHandlerAdapter RequestMappingHandlerMapping 这2个类是在DispatcherServlet.properties配置的。

使用mvc:annotation-driven标签,默认注册的也是这2个类,前面已经介绍了这种标签的解析,感兴趣可以自己查看一下MvcNamespaceHandler和AnnotationDrivenBeanDefinitionParser这2个类,NamespaceHandler是在META-INF的spring.handler中配置的,自定义NamespaceHandler的时候注意创建对应的xsd和spring.handler文件。

spring mvc中处理参数和返回值的主要接口和类:

HandlerMethodReturnValueHandlerComposite
HandlerMethodReturnValueHandler
HandlerMethodArgumentResolverComposite
HandlerMethodArgumentResolver

典型的组合模式,不过Composite中采用的不是遍历,而是找到第一个支持的Handler来处理。 看RequestMappingHandlerAdapter源码中有下面2个方法,用来初始化默认的HandlerMethodReturnValueHandler和HandlerMethodArgumentResolver。

RequestMappingHandlerAdapter#getDefaultArgumentResolvers
RequestMappingHandlerAdapter#getDefaultReturnValueHandlers

还有一个HttpMessageConverter集合,是用来给RequestResponseBodyMethodProcessor、RequestPartMethodArgumentResolver、HttpEntityMethodProcessor、ResponseBodyEmitterReturnValueHandler这几个参数返回值处理器使用的。@ResponseBody和@RequestBody非常常见,一般我们知道出问题是在什么地方,但是当参数和返回值是HttpEntity的时候,也会使用到HttpMessageConverter,这个需要注意,例如,使用HttpEntity作为返回值传输文件,就得看一下有没有配置ByteArrayHttpMessageConverter这个转换器。

其他

FactoryBean 与 BeanFactory

BeanFactory,以Factory结尾,表示它是一个工厂类(接口),用于管理Bean的一个工厂。在Spring中,BeanFactory是IOC容器的核心接口,它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。

FactoryBean,以Bean结尾,表示它是一个Bean,不同于普通Bean的是它实现了FactoryBean<T>接口的Bean,根据该Bean的ID从BeanFactory中获取的实际上是FactoryBean的getObject()返回的对象,而不是FactoryBean本身,如果要获取FactoryBean对象,请在id前面加一个&符号来获取。

Environment

Environment主要是读取系统变量和属性,当然也会出来classpath或者指定路径下的properties文件,主要涉及的类有:

  1. StandardServletEnvironment
  2. StandardEnvironment
  3. PropertyResolver
  4. PropertyPlaceholderHelper
  5. @PropertySource
  6. @TestPropertySource
  7. @ConfigurationProperties
@PropertySource({"classpath:base.properties"})
@Component
public class BaseBean {

    @Value("${host:127.0.0.1}")
    private String host;

    @Value("${base.host:127.0.0.1}")
    private String baseHost;

    @Value("#{'Hello World'.concat('!')}")
    private String helloWorld;

    @Value("#{'${base.server.name}'.split(',')}")
    private List<String> servers;

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public String getBaseHost() {
        return baseHost;
    }

    public void setBaseHost(String baseHost) {
        this.baseHost = baseHost;
    }

    public String getHelloWorld() {
        return helloWorld;
    }

    public void setHelloWorld(String helloWorld) {
        this.helloWorld = helloWorld;
    }

    public List<String> getServers() {
        return servers;
    }

    public void setServers(List<String> servers) {
        this.servers = servers;
    }

    @Override
    public String toString() {
        return "BaseBean{" +
                "host='" + host + '\'' +
                ", baseHost='" + baseHost + '\'' +
                ", helloWorld='" + helloWorld + '\'' +
                ", servers=" + servers +
                '}';
    }
}

@ConfigurationProperties是springboot的中的,不能和@Value同时使用,只需要使用一个就可以了。

© 著作权归作者所有

trayvon
粉丝 16
博文 140
码字总数 212221
作品 1
程序员
私信 提问
Spring中管理Bean依赖注入之后和Bean销毁之前的行为

对于Singleton作用域的Bean,Spring容器将会跟踪它们的生命周期,容器知道何时实例化结束、何时销毁。Spring可以管理Bean在实例化结束之后和Bean销毁之前的行为。 Bean依赖关系注入之后的行为...

摆渡者
2014/03/06
5.4K
0
Spring Bean的生命周期,《Spring 实战》书中的官方说法

连着两天的面试 ,都问到了 Spring 的Bean的生命周期,其中还包括 昨晚一波阿里的电话面试。这里找到了Spring 实战中的官方说法。希望各位要面试的小伙伴记住,以后有可能,或者是有时间 去看...

致未来的自己
2018/08/02
0
0
Spring Bean的生命周期

1.当调用者通过getBean(beanName)向容器请求某一个Bean时,如果容器注册了org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor接口,在实例化Bean之前,将调用接...

我吃草莓
2014/04/10
224
0
spring4.0之二:@Configuration的使用

从Spring3.0,@Configuration用于定义配置类,可替换xml配置文件,被注解的类内部包含有一个或多个被@Bean注解的方法,这些方法将会被AnnotationConfigApplicationContext或AnnotationConfi...

文文1
2018/06/01
34
0
分析spring源码第七篇:bean的生命周期

bean的生命周期 一. 其实在查看源码BeanFactory时,就会发现源码就介绍了bean的生命周期,原文如下: 二. bean的实例化过程如下图: bean的生命周期流程图如下图: 三. Bean实例生命周期的执...

潇洒的活着
2018/08/30
1K
0

没有更多内容

加载失败,请刷新页面

加载更多

SpringBoot设置项目访问路径

今天配置Springboot项目访问路径遇到一个小坑,这里做一个记录。针对Springboot2.0以下版本和以上版本的配置方式是不同的。这里针对yml配置文件做记录,properties文件配置方式相同。 Spring...

王子城
31分钟前
5
0
Redis 序列化方式StringRedisSerializer、FastJsonRedisSerializer和KryoRedisSerializer

当我们的数据存储到Redis的时候,我们的键(key)和值(value)都是通过Spring提供的Serializer序列化到数据库的。RedisTemplate默认使用的是JdkSerializationRedisSerializer,StringRedis...

xiaolyuh
41分钟前
5
0
jquery通过css的color属性实现选取特殊颜色的内容

今天一个朋友问到怎么使用jQuery选取特定颜色的内容,以前没有遇到过这样的需求。首先,很多人可能想到使用jQuery属性选择器来实现,这是不可以的,因为 color 是 css 的属性,而不是 html 的...

前端老手
53分钟前
3
0
python3 网络爬虫开发实战 win10环境下 图形验证码识别需要安装的工具及地址

先下载安装tesseract exe文件 tesseract exe文件安装下载地址:https://digi.bib.uni-mannheim.de/tesseract/ win下直接下载双击安装即可,注意路径,下面用于配置环境变量。 参考网址: ht...

S三少S
今天
10
0
PHP学习记录整理

PHP 输出文本的基础指令:echo 和 print。 echo和print的区别 echo是PHP语句, print和print_r是函数,语句没有返回值,函数可以有返回值(即便没有用) echo 输出一个或者多个字符串。 print ...

半缘修道半缘君丶
今天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部