1.参看spring framework5.2.5官方文档
2.部分根据自己的理解借助机翻及其它博客整合
3.请参见github代码学习
1.本章涵盖了控制反转(IoC)原理的Spring框架实现。IoC也称为依赖注入(DI)。在此过程中,对象仅通过构造函数参数,工厂方法的参数or properties that are set on the object instance after it is constructed or returned from a factory method来定义其依赖关系(即,它们使用的其他对象)(即the other objects they work with)。容器在创建bean时注入这些依赖项。
2.org.springframework.beans和org.springframework.context包是是spring framework的IoC容器的基础。BeanFactory接口提供了一种先进的配置机制,能够管理任何类型的对象。
3.ApplicationContext是BeanFactory的一个子接口。它新增了:(1)更容易与Spring的AOP特性集成(2)消息资源处理(用于国际化)(3)事件发布(4)特定于应用程序层的上下文,如用于web应用程序的WebApplicationContext。简而言之,BeanFactory提供了配置框架和基本功能,ApplicationContext添加了更多企业特定的功能。ApplicationContext是BeanFactory的完整超集。
4.在Spring中,构成应用程序主干并由Spring IoC容器管理的对象称为bean。bean是由Spring IoC容器实例化、组装和管理的对象。bean及其之间的依赖关系反映在容器使用的配置元数据中。
1.org.springframework.context.ApplicationContext接口代表Spring IoC容器,并负责实例化,配置和组装Bean。容器通过读取配置元数据来获取实例化、配置和组装哪些对象的指令。配置元数据以XML、Java注释或Java代码表示。它允许您表达组成应用程序的对象以及这些对象之间丰富的相互依赖关系。
2.配置元数据的几种形式:
3.Spring提供了ApplicationContext接口的几个实现,比如ClassPathXmlApplicationContext、FileSystemXmlApplicationContext等等。
4.提供给ApplicationContext构造函数的一个或多个位置路径是资源字符串,允许容器从各种外部资源(如本地文件系统、Java类路径等)加载配置元数据。如下是基于XML的配置元数据的基本结构:其中id和ref元素之间的这种链接表示协作对象之间的依赖关系。
5.使用容器:
ApplicationContext是一个高级工厂的接口,能够维护不同bean及其依赖项的注册表。通过使用方法T getBean(String name,ClassrequiredType),可以检索bean的实例。
1.Spring IoC容器管理一个或多个bean。这些bean是使用您提供给容器的配置元数据创建的。在容器本身内,这些bean定义表示为BeanDefinition 对象,其中包含(除其他信息外)以下元数据:
2.除了包含有关如何创建特定bean的信息的bean定义之外,ApplicationContext implementations 还允许注册在容器外部(由用户)创建的现有对象。通过getBeanFactory()方法访问ApplicationContext的BeanFactory,该方法返回beanFactory接口的DefaultListableBeanFactory实现类。DefaultListableBeanFactory通过registerSingleton(…)和registerBeanDefinition(…)方法支持此注册。但是,典型的应用程序只处理通过常规bean定义元数据定义的bean。
3.几种实例化bean的方法如下:
(1)用构造函数实例化
当您使用构造函数方法创建bean时,所有普通类都可以由Spring使用并与之兼容。也就是说,正在开发的类不需要实现任何特定的接口或以特定的方式进行编码。只需指定bean类就足够了。但是,根据您对特定bean使用的IoC类型,您可能需要一个默认(空)构造函数。Spring IoC容器几乎可以管理您希望它管理的任何类,它不仅限于管理真正的javabean。使用基于XML的配置元数据,您可以如下指定bean类:
<bean id="exampleBean" class="examples.ExampleBean"/>
(2)静态工厂方法实例化
在定义使用静态工厂方法创建的bean时,使用class属性指定包含静态工厂方法的类,使用名为factory method的属性指定工厂方法本身的名称。您应该能够调用此方法(如后文所述,使用可选参数)并返回一个活动对象,该对象随后将被视为是通过构造函数创建的。这种bean定义的一个用途是在遗留代码中调用静态工厂。
public class ClientService {
private static ClientService clientService = new ClientService();
private ClientService() {}
public static ClientService createInstance() {
return clientService;
}
}
<bean id="clientService" class="examples.ClientService" factory-method="createInstance"/>
(3)使用实例工厂方法实例化
与通过静态工厂方法进行实例化类似,使用实例工厂方法的实例化从容器中调用现有bean的非静态方法来创建新bean。一个工厂可以包含一个以上的工厂方法。工厂Bean本身可以通过依赖项注入(DI)进行管理和配置。
public class DefaultServiceLocator {
private static ClientService clientService = new ClientServiceImpl();
private static AccountService accountService = new AccountServiceImpl();
public ClientService createClientServiceInstance() {
return clientService;
}
public AccountService createAccountServiceInstance() {
return accountService;
}
}
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
bean>
<bean id="clientService" factory-bean="serviceLocator" factory-method="createClientServiceInstance"/>
<bean id="accountService" factory-bean="serviceLocator" factory-method="createAccountServiceInstance"/>
1.依赖项注入(DI)是一个过程,在此过程中,对象仅通过构造函数参数、工厂方法的参数或从工厂方法构造或返回对象实例后在对象实例上设置的属性来定义其依赖项(即它们使用的其他对象)。容器然后在创建bean时注入这些依赖项。
2.基于构造函数的依赖注入
基于构造函数的DI是通过容器调用具有多个参数的构造函数来实现的,每个参数代表一个依赖项。
3.基于Setter的依赖注入
基于Setter的DI是在调用无参数构造函数或无参数静态工厂方法来实例化bean之后,容器在bean上调用Setter方法来实现的。
4.依赖性解析过程
(1)创建容器时,Spring容器验证每个bean的配置。但是,在实际创建bean之前,不会设置bean属性(properties )本身。创建容器时,将创建单例作用域且设置为预实例化(默认)的bean。作用域是在Bean作用域中定义的。否则,只有在请求时才会创建bean。
(2)如果你主要使用构造函数注入,可能会创建无法解析的循环依赖场景。例如:类A需要一个类B通过构造函数注入的实例,而类B需要一个类A通过构造函数注入的实例。如果您将bean配置为类A和B相互注入,Spring IoC容器将在运行时检测这个循环引用,并抛出一个BeanCurrentlyInCreationException异常。但您可以使用setter注入配置循环依赖项。与典型的情况(没有循环依赖关系)不同,bean a和bean B之间的循环依赖关系迫使一个bean在完全初始化之前被注入到另一个bean中(典型的先有鸡还是先有蛋的场景)。
(3)Spring在容器加载时检测配置问题,例如对不存在的bean和循环依赖项的引用。在实际创建bean时,Spring会尽可能晚地设置属性和解析依赖项。
(4)如果不存在循环依赖项,那么当一个或多个协作bean被注入依赖bean时,每个协作bean在被注入依赖bean之前都是完全配置的。这意味着,如果bean A依赖于bean B,那么Spring IoC容器在调用bean A上的setter方法之前完全配置了bean B。换句话说,bean是实例化的(如果它不是预先实例化的singleton),它的依赖是设置的,并调用相关的生命周期方法(例如配置的init方法或InitializingBean 回调方法)。
5.懒初始化bean(延迟初始化bean)
默认情况下,ApplicationContext implementations 将创建和配置所有的单例bean作为初始化过程的一部分。通常,这种预实例化是可取的,因为配置或周围环境中的错误会被立即发现。如果不希望这种行为,则可以通过将bean定义标记为延迟初始化来防止单例bean的预实例化。延迟初始化的bean告诉IoC容器在首次请求时(而不是在启动时)创建一个bean实例。比如
<bean id="lazy" class="com.something.ExpensiveToCreateBean" lazy-init="true"/>
6.方法注入
请参见github代码学习
在大多数应用程序场景中,容器中的大多数bean都是单例的。当一个单例bean需要与另一个单例bean协作或者一个非单例bean需要与另一个非单例bean协作时,通常通过将一个bean定义为另一个bean的属性来处理依赖关系。当bean生命周期不同时,就会出现一个问题。假设singleton bean A需要使用非singleton(prototype)bean B,可能是在A上的每个方法调用上。容器只创建一次singleton bean A,因此只有一次机会设置属性。容器不能在每次需要bean A时都为它提供一个新的bean B实例。解决的办法是放弃一些控制反转。通过实现ApplicationContextAware接口,并在每次bean A需要时调用容器的getBean(“B”)来请求bean B实例(通常是一个新的)。
1.bean作用域
注:从Spring3.0开始,线程作用域可用,但默认情况下不注册。
scope | description |
---|---|
singleton | (Default) Scopes a single bean definition to a single object instance for each Spring IoC container. |
(默认)为每个Spring IoC容器将单个bean定义限定为单个对象实例。 | |
prototype | Scopes a single bean definition to any number of object instances. |
将单个bean定义的作用域限定为任意数量的对象实例。 | |
request | Scopes a single bean definition to the lifecycle of a single HTTP request. That is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext. |
将单个bean定义限定为单个HTTP请求的生命周期。仅在支持web的Spring应用程序上下文中有效。 | |
session | Scopes a single bean definition to the lifecycle of an HTTP Session. Only valid in the context of a web-aware Spring ApplicationContext. |
将单个bean定义限定为HTTP会话的生命周期。仅在支持web的Spring应用程序上下文中有效。 | |
application | Scopes a single bean definition to the lifecycle of a ServletContext. Only valid in the context of a web-aware Spring ApplicationContext. |
将单个bean定义限定为ServletContext的生命周期。仅在支持web的Spring应用程序上下文中有效。 | |
websocket | Scopes a single bean definition to the lifecycle of a WebSocket. Only valid in the context of a web-aware Spring ApplicationContext. |
将单个bean定义限定为WebSocket的生命周期。仅在支持web的Spring应用程序上下文中有效。 |
2.The Singleton Scope
只管理一个单例bean的共享实例,所有对ID或ID与该bean定义匹配的bean的请求都会导致Spring容器返回一个特定的bean实例。换句话说,当您定义一个bean定义,并且它的作用域是一个singleton时,Spring IoC容器正好创建了该bean定义所定义对象的一个实例。这个单实例存储在这样的单例bean的缓存中,该命名bean的所有后续请求和引用都返回缓存的对象。单例作用域是Spring中的默认作用域。
3.The Prototype Scope
每次请求特定bean时都创建一个新的bean实例。通常,应将Prototype作用域用于所有有状态Bean,将单例作用域用于无状态Bean。A data access object (DAO) is not typically configured as a prototype, because a typical DAO does not hold any conversational state(数据访问对象(DAO)通常不配置为Prototype,因为典型的DAO不拥有任何对话状态)。与其他作用域不同,Spring不管理Prototype bean的完整生命周期。容器实例化、配置和以其他方式组装Prototype对象并将其交给客户端,而不再记录该Prototype实例。因此,尽管初始化生命周期回调方法在所有对象上都被调用而不管其作用域如何,但对于Prototype作用域,配置的销毁生命周期回调不会被调用。客户端代码必须清理Prototype作用域对象并释放Prototype bean所拥有的昂贵资源。要让Spring容器释放Prototype作用域bean所拥有的资源,请尝试使用自定义bean后处理器,该处理器保存对需要清理的bean的引用。
4.具有Prototype Bean依赖关系的Singleton Bean
(1)当您使用对Prototype bean有依赖性的单例作用域Bean时,请注意,依赖关系在实例化时已解决。
(2)如果依赖项将Prototype 作用域bean注入单例作用域bean,该注入仅在Spring容器实例化单例bean并解析和注入其依赖项时发生一次。 假设您希望单例作用域bean在运行时重复获取Prototype作用域bean的新实例,请参见方法注入。
5.Request, Session, Application, WebSocket作用域(了解即可)
请求、会话、应用程序和websocket作用域只有在使用支持web的Spring ApplicationContext实现(例如XmlWebApplicationContext)时才可用。如果将这些作用域与常规Spring IoC容器(如ClassPathXmlApplicationContext)一起使用,则会引发一个IllegalStateException,该异常抱怨未知的bean作用域。
6.自定义scope(了解即可)
要将自定义作用域集成到Spring容器中,您需要实现org.springframework.beans.factory.config.Scope接口。然后向Spring容器注册新作用域,void registerScope(String scopeName, Scope scope)此方法ConfigurableBeanFactory接口上声明。
请参见github代码学习
spring框架提供了许多接口,您可以使用这些接口来自定义bean的性质:
1.生命周期回调
(1)不推荐方法:为了与容器对bean生命周期的管理进行交互,可以实现Spring InitializingBean和DisposableBean接口。容器调用afterPropertiesSet(),destroy(),以便bean在初始化和销毁bean时执行某些操作。
1.初始化回调:org.springframework.beans.factory.InitializingBean容器在bean上设置了所有必需的属性后,该接口可让bean执行初始化工作。不推荐此方法因为它不必要地将代码耦合到Spring。
2.销毁回调:org.springframework.beans.factory.DisposableBean当包含该接口的容器被销毁时,实现该接口可使Bean获得回调。
(2)推荐方法:通常,JSR-250 @PostConstruct和@PreDestroy注释被认为是在现代Spring应用程序中接收生命周期回调的最佳实践。使用这些注释意味着您的bean没有耦合到特定于Spring的接口。
(3)推荐方法:如果你不希望使用JSR-250注解,但你仍然要删除耦合,考虑init-method和destroy-method bean基于XML的配置元数据。
1.基于xml的配置元数据: 。或者使用java配置,用@Bean的initMethod属性。
2.基于xml的配置元数据: 。或者使用java配置,用@Bean的destroyMethod 属性。
2.ApplicationContextAware和BeanNameAware(这个用的比较少 )接口
(1)当ApplicationContext创建实现org.springframework.context.ApplicationContextAware接口的对象实例时,将为该实例提供对该ApplicationContext的引用。一种用途是通过编程方式检索其他bean。
(2)当ApplicationContext创建一个实现该org.springframework.beans.factory.BeanNameAware接口的类时,该类将获得对其关联对象定义中定义的名称的引用。
3.Aware Interfaces
Spring offers a wide range of Aware callback interfaces that let beans indicate to the container that they require a certain infrastructure dependency,比如ApplicationContextAware,BeanClassLoaderAware,BeanFactoryAware,BeanNameAware,ServletConfigAware,ServletContextAware等等
Name | Injected Dependency |
---|---|
ApplicationContextAware | 声明ApplicationContext。 |
BeanClassLoaderAware | 用于加载bean类的类加载器。 |
BeanFactoryAware | 声明BeanFactory。 |
BeanNameAware | 声明bean的名称。 |
ServletConfigAware | 待在spring mvc学习中解释。 |
ServletContextAware | 待在spring mvc学习中解释。 |
通常,开发者不需要对 ApplicationContext 实现类进行子类化。相反,Spring IoC容器可以通过插入特殊集成接口的实现来扩展。接下来描述这些集成接口。
1.使用BeanPostProcessor自定义bean,请参看github代码学习。
(1)BeanPostProcessor接口定义回调方法,您可以实现这些方法来提供自己的(或重写容器的默认)实例化逻辑、依赖关系解析逻辑等。如果要在Spring容器完成实例化、配置和初始化 bean之后实现一些自定义逻辑,可以插入一个或多个自定义BeanPostProcessor实现。您可以配置多个BeanPostProcessor实例,并且可以通过设置order属性来控制这些BeanPostProcessor实例的执行顺序(只有BeanPostProcessor实现有序接口Ordered时,才能设置此属性)
(2)BeanPostProcessor实例在bean(或对象)实例上操作。也就是说,Spring IoC容器实例化一个bean实例,然后BeanPostProcessor实例执行它们的工作。
(3)BeanPostProcessor接口正好由两个回调方法组成。当这样的类注册为容器的后处理器时,对于容器创建的每个bean实例,后处理器在调用容器初始化方法(如initializengbean,afterPropertiesSet()或任何声明的init方法)之前和任何bean初始化之后从容器中gets a callback ,后处理器可以对bean实例执行任何操作,包括完全忽略回调(ignoring the callback )。Bean后处理器通常检查回调接口,或者可以用代理包装Bean。一些Spring AOP基础结构类被实现为bean后处理器,以提供代理包装逻辑。
(4)BeanPostProcessor需要提前实例化,以便应用于上下文中其他bean的初始化。
2.使用BeanFactoryPostProcessor自定义配置元数据
(1)此接口的语义与BeanPostProcessor相似,但有一个主要区别:BeanFactoryPostProcessor对bean配置元数据进行操作。也就是说,Spring IoC容器允许BeanFactoryPostProcessor读取配置元数据,并可能在容器实例化BeanFactoryPostProcessor实例以外的任何bean之前对其进行更改。可以配置多个BeanFactoryPostProcessor实例,还可以通过设置order属性来控制这些BeanFactoryPostProcessor实例的运行顺序(只有BeanFactoryPostProcessor实现有序接口Ordered时,才能设置此属性)。
(2)如果您要更改实际的bean实例(即,从配置元数据创建的对象),则需要使用BeanPostProcessor(请参见使用BeanPostProcessor自定义bean)。尽管在技术上可以使用BeanFactoryPostProcessor(例如,通过使用 BeanFactory.getBean())中的bean实例,但是这样做会导致bean实例化过早,从而违反了标准容器的生命周期。这可能会导致负面影响,例如绕过bean后处理。
(3)当在ApplicationContext中声明bean工厂后处理器时,它会自动执行,以便将更改应用于定义容器的配置元数据。Spring包括许多预定义的bean工厂后处理器,如PropertyOverrideConfigurer和PropertySourcesPlaceholderConfigurer。您也可以使用自定义BeanFactoryPostProcessor - 来注册自定义属性编辑器。
(4)PropertySourcesPlaceholderConfigurer(一个bean工厂后处理器),通过使用标准的Java属性格式,将bean定义中的属性值外部化到单独的文件中:
1.@Autowired
@Autowired应用于字段、构造函数和多参数方法,允许在参数级别通过限定符注释(qualifier )来缩小范围。
2.使用@Primary对基于注释的自动装配进行微调
由于按类型自动装配可能会导致多个候选项,因此通常需要对选择过程进行更多控制。@Primary表示,当多个bean候选自动连接到一个单值依赖项时,应该优先考虑特定的bean。如果候选对象中恰好存在一个primary bean,则它将成为自动装配值。
3.使用Qualifiers(限定符)对基于注释的自动装配进行微调
当您需要对选择过程进行更多控制时,可以使用Spring的@Qualifier注释。bean名称被视为默认限定符值(the bean name is considered a default qualifier value)。您还可以对单个构造函数参数或方法参数指定@Qualifier注释。
4.用@Resource注入
如果您打算按名称来表示注释驱动的注入,那么不要主要使用@Autowired。相反,请使用JSR-250@Resource注释,该注释在语义上定义为通过其唯一名称标识特定的目标组件,声明的类型与匹配进程无关。@Resource应用于字段和bean属性的 setter 方法上。@Resource具有名称属性。默认情况下,Spring将该值解释为要注入的Bean名称。
1.不需要使用XML来执行bean注册。
2.@Component是任何Spring管理的组件的通用原型。@Repository、@Service和@Controller是@Component在更具体的用例(分别在持久性层、服务层和表示层)中的专门化。
3.要自动检测这些类并注册相应的bean,您需要添加@ComponentScan到@Configuration类中,其中basePackages属性是bean的公共父包。或者使用xml如下:
<context:component-scan base-package="org.example"/>
(1)Spring的新Java配置支持的central artifacts是带@Configuration注释的类和带@Bean注释的方法。
(2)@bean表明一个方法实例化、配置和初始化由Spring IoC容器进行管理的新对象。
(3)@Configuration表明这个类的主要用途是作为bean定义的源。
(4)您可以对任何Spring @Component使用@bean注释方法。但是@bean通常与@Configuration一起使用。You can use @Bean-annotated methods with any Spring @Component. However, they are most often used with @Configuration beans.
@Bean注释的作用与<bean/>元素相同
<beans>
<bean id="myService" class="com.acme.services.MyServiceImpl"/>
beans>
@Configuration
public class AppConfig {
@Bean
public MyService myService() {
return new MyServiceImpl();
}
}
(5)Full @Configuration 对比 “lite” @Bean mode?
当@Bean方法在没有使用@Configuration注释的类中声明时,它们被称为以"lite"模式(精简模式)处理。在一个@Component或者甚至在一个普通的旧类中声明的Bean方法被认为是"精简版"。精简模式与Full @Configuration不同的是,它无法声明Bean间的依赖关系,Such a @Bean method should therefore not invoke other @Bean methods(精简模式中这样的bean方法不应该调用其他@Bean方法)。在常见的场景中,@Bean方法将在@Configuration类中声明,保始终使用"full"模式。
这个多功能的ApplicationContext实现不仅可以接受@Configuration类作为输入,还可以接受普通的@Component类和用JSR-330元数据注释的类。当@Configuration类作为输入提供时,@Configuration类本身注册为bean定义,并且类中所有声明的@bean方法也注册为bean定义。当提供@Component和JSR-330类时,它们被注册为bean定义,并且假设在必要时在这些类中使用诸如@Autowired或@Inject之类的DI元数据。
略
@Configuration是类级别的注释,指示对象是Bean定义的源。@Configuration类通过public @Bean注释方法声明bean 。对@Configuration类上的@Bean方法的调用也可以用来定义Bean间的依赖关系。
(1)xml中声明
<beans>
<context:annotation-config/>
<context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
bean>
beans>
(2)组件扫描,@Configuration是用@Component进行元注释的
<beans>
<context:component-scan base-package="com.acme"/>
beans>
环境接口是集成在容器中的抽象,它对应用程序环境的两个关键方面进行建模:profiles 和properties。
Web应用程序的方便的ApplicationContext实例化,可以使用ContextLoaderListener注册ApplicationContext,如下
<context-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:applicationContext_dao.xml,classpath:applicationContext_service.xmlparam-value>
context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
listener>
下表列出了BeanFactory 和 ApplicationContext接口和实现所提供的功能。
功能 | BeanFactory | ApplicationContext |
---|---|---|
bean实例化和自动装配 | 是 | 是 |
集成生命周期管理 | 否 | 是 |
自动BeanPostProcessor注册 | 否 | 是 |
自动BeanFactoryPostProcessor注册 | 否 | 是 |
方便的MessageSource访问(用于内部化) | 否 | 是 |
内置ApplicationEvent发布机制 | 否 | 是 |