Spring IoC容器和Beans介绍
全称
IoC:Inverse of Control 控制反转
DI:Dependecy Injection 依赖注入
以上两者是相同的。均表示将控制权交给Spring容器。
IoC的基础包
org.springframework.beans
org.springframework.context
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.4.RELEASE</version>
</dependency>
BeanFactory
BeanFactory 接口提供了一种高级配置机制,能够管理任何类型的对象。
ApplicationContext 是BeanFactory的一个 子接口。
ApplicationContext增加了以下几个功能:
1、更容易与Spring的AOP特性集成。
2、消息资源处理(用于国际化)。
3、事件发布。
4、特定应用层上下文,例如用于web应用程序的 WebApplicationContext。
在Spring中,构成应用程序主干并由Spring IoC容器管理的对象称为 beans。
beans以及它们之间的依赖关系反映在容器使用的配置元数据中。
容器概述
org.springframework.context.ApplicationCotenxt 接口代表Spring IoC容器,它负责实例、配置和装配beans。
配置的元数据放到 XML、Java注解 或者 Java代码。
创建容器实例
在独立应用程序中,通常创建 ClassPathXmlApplicationContext 或 FileSystemXmlApplicationContext 的实例。
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(path);
ApplicationContext applicationContext = new FileSystemXmlApplicationContext(path);
path为配置文件路径,其中ClassPathXmlApplicationContext的路径为类路径的 相对路径,FileSystemXmlApplication的路径为 绝对路径。
配置元数据
基于XML配置文件的beans要放在<beans>标签的<bean>标签中。
基于注解配置的beans需要放在含有 @Configuration 类中,并用 @Bean。
XML例子:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
<!-- more bean definitions go here -->
</beans>
其中id为标识,class为类。可用import标签导入其他xml文件,其中import标签的resource属性表示文件路径。
使用容器
使用ApplicationContext的 getBean 方法。
Bean配置
class属性
封装成的类,通常是必须的,可以通过两种方式用class属性。
1、Spring容器通过反射直接调用类的构造方法。
2、使用静态工厂方法。
构造内部类:使用 外部类$内部类。
通过构造器实例化
<bean id="exampleBean" class="examples.ExampleBean"/>
<bean name="anotherExample" class="examples.ExampleBeanTwo"/>
通过静态工厂方法实例化
使用属性 factory-method。
<bean id="clientService"
class="examples.ClientService"
fatory-metho="createInstance"/>
public class ClientService {
private static ClientService clientService = new ClientService();
private ClientService() {}
public static ClientService createInstance() {
return clientService;
}
}
使用实例工厂实例化
使用属性 factory-bean 和 factory-method。
<bean id="serviceLocator" class="examples.DefaultServiceLocator"/>
<bean id="clientService"
factory-bean="serviceLocaor"
factory-method="createClientServiceInstance"/>
public class DefaultServiceLocator {
private static ClientService clientService = new ClientServiceImpl();
public clientService createClientServiceInstance() {
return clientService;
}
}
name属性
bean的名称,当有多个名称时可以用 , ,; 或 空格 隔开,除了第一个名称,其他被当作为别名。
id属性为 xsd:id 类型,不能包含特殊的字符,唯一性是容器强制约束的。
name属性为 xsd:string 类型,可以包含特殊的字符。
bean可以通过 ref属性 与其他bean关联(值为该bean的标识)。
别名:
<alias name="myApp-dataSource" alias="subsystemA-dataSource"/>
<alias name="myApp-dataSource" alias="subsystemB-dataSource"/>
scope属性
设定bean实例的作用域:
singleton(单例,默认)、prototype(原型)、request、session、application、websocket。
当单例的bean要使用非单例的bean时,会出现问题,可以通过实现 ApplicationContextAware 接口解决。这种方法与Spring框架绑定,并不可取。
lookup方法注入
基于CGLIB类包,CGLIB可以在运行期操作Class字节码,为Bean动态创建子类或实现类。
package fiona.apple;
// no more Spring imports!
public abstract class CommandManager {
public Object process(Object commandState) {
// grab a new instance of the appropriate Command interface
Command command = createCommand();
// set the state on the (hopefully brand new) Command instance
command.setState(commandState);
return command.execute();
}
// okay... but where is the implementation of this method?
protected abstract Command createCommand();
}
要注入的方法名称格式:
<public|protected> [abstract] <return-type> theMethodName(no-arguments);
<!-- a stateful bean deployed as a prototype (non-singleton) -->
<bean id="myCommand" class="fiona.apple.AsyncCommand" scope="prototype">
<!-- inject dependencies here as required -->
</bean>
<!-- commandProcessor uses statefulCommandHelper -->
<bean id="commandManager" class="fiona.apple.CommandManager">
<lookup-method name="createCommand" bean="myCommand"/>
</bean>
或者使用 @Lookup 注解。
public abstract class CommandManager {
public Object process(Object commandState) {
Command command = createCommand();
command.setState(commandState);
return command.execute();
}
@Lookup("myCommand")
protected abstract Command createCommand();
}
任意方法替换(Arbitrary Method Replacement):
不如Lookup。实现 MethodReplacer 接口。
public class MyValueCalculator {
public String computeValue(String input) {
// some real code...
}
// some other methods...
}
/**
* meant to be used to override the existing computeValue(String)
* implementation in MyValueCalculator
*/
public class ReplacementComputeValue implements MethodReplacer {
public Object reimplement(Object o, Method m, Object[] args) throws Throwable {
// get the input value, work with it, and return a computed result
String input = (String) args[0];
...
return ...;
}
}
<bean id="myValueCalculator" class="x.y.z.MyValueCalculator">
<!-- arbitrary method replacement -->
<replaced-method name="computeValue" replacer="replacementComputeValue">
<arg-type>String</arg-type>
</replaced-method>
</bean>
<bean id="replacementComputeValue" class="a.b.c.ReplacementComputeValue"/>
constructor-arg标签
注入构造器中的参数来创建实例。
package x.y;
public class ThingOne {
public ThingOne(ThingTwo thingTwo, ThingThree thingThreee) {
//...
}
}
<beans>
<bean id="beanOne" class="x.y.ThingOne">
<constructor-arg ref="beanTwo"/>
<constructor-arg ref="beanThree"/>
</bean>
<bean id="beanTwo" class="x.y.ThingTwo"/>
<bean id="beanThree" class="x.y.ThingThree"/>
</beans>
简单的数据类型可以用value属性,Spring无法判断类型,可以用type属性。
package examples;
public class ExampleBean {
private int years;
private String ultimateAnswer;
public ExampleBean(int years, String ultimateAnswer) {
this.years =years;
this.ultimateAnswer = ultimateAnswer;
}
}
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg type="int" value="7500000"/>
<constructor-arg type="java.lang.String" value="42"/>
</bean>
使用索引注入 index属性,可以解决参数有多个相同类型的问题。
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg index="0" value="7500000"/>
<constructor-arg index="1" value="42"/>
</bean>
使用构造器参数的名字 name属性,需要使用 @ConstructorProperties 指定名称。
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg name="years" value="7500000"/>
<constructor-arg name="ultimateAnswer" value="42"/>
</bean>
package examples;
public class ExampleBean {
@ConstructerProperties({"years", "ultimateAnswer"})
public ExampleBean(int years, String ultimateAnswer) {
this.years = years;
this.ultimateAnswer = ultimateAnswer;
}
}
property标签
需要有 无参构造器 或者 无参静态工厂、setting方法。
<bean id="exampleBean" class="examples.ExampleBean">
<!-- setter injection using the nested ref element -->
<property name="beanOne">
<ref bean="anotherExampleBean"/>
</property>
<!-- setter injection using the neater ref attribute -->
<property name="beanTwo" ref="yetAnotherBean"/>
<property name="integerProperty" value="1"/>
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
public class ExampleBean {
private AnotherBean beanOne;
private YetAnotherBean beanTwo;
private int i;
public void setBeanOne(AnotherBean beanOne) {
this.beanOne = beanOne;
}
public void setBeanTwo(YetAnotherBean beanTwo) {
this.beanTwo = beanTwo;
}
public void setIntegerProperty(int i) {
this.i = i;
}
}
property标签内可以有bean标签,可用于数据的注入。
<bean id="outer" class="...">
<!-- instead of using a reference to a target bean, simply define the target bean inline -->
<property name="target">
<bean class="com.example.Person"> <!-- this is the inner bean -->
<property name="name" value="Fiona Apple"/>
<property name="age" value="25"/>
</bean>
</property>
</bean>
property标签内可以有list、set、map、props标签,分别对应List、Set、Map和Properties,可用于容器的数据注入。
<bean id="moreComplexObject" class="example.ComplexObject">
<!-- results in a setAdminEmails(java.util.Properties) call -->
<property name="adminEmails">
<props>
<prop key="administrator">administrator@example.org</prop>
<prop key="support">support@example.org</prop>
<prop key="development">development@example.org</prop>
</props>
</property>
<!-- results in a setSomeList(java.util.List) call -->
<property name="someList">
<list>
<value>a list element followed by a reference</value>
<ref bean="myDataSource" />
</list>
</property>
<!-- results in a setSomeMap(java.util.Map) call -->
<property name="someMap">
<map>
<entry key="an entry" value="just some string"/>
<entry key ="a ref" value-ref="myDataSource"/>
</map>
</property>
<!-- results in a setSomeSet(java.util.Set) call -->
<property name="someSet">
<set>
<value>just some string</value>
<ref bean="myDataSource" />
</set>
</property>
</bean>
merge属性可以整合父类与本身的集合。
<beans>
<bean id="parent" abstract="true" class="example.ComplexObject">
<property name="adminEmails">
<props>
<prop key="administrator">administrator@example.com</prop>
<prop key="support">support@example.com</prop>
</props>
</property>
</bean>
<bean id="child" parent="parent">
<property name="adminEmails">
<!-- the merge is specified on the child collection definition -->
<props merge="true">
<prop key="sales">sales@example.com</prop>
<prop key="support">support@example.co.uk</prop>
</props>
</property>
</bean>
<beans>
此时的adminEmails的值为:
administrator=administrator@example.com
sales=sales@example.com
support=support@example.co.uk
depends-on属性
depends-on属性表明依赖关系(不一定会引用),这个依赖关系决定了被依赖的bean必定会在依赖bean之前被实例化,反过来,容器关闭时,依赖bean会在被依赖的bean之前被销毁。
<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao">
<property name="manager" ref="manager" />
</bean>
<bean id="manager" class="ManagerBean" />
<bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />
lazy-init属性
设置为true时,使用时才实例化,否则容器启动时实例化。
也可以在beans标签中使用 default-lazy-init 属性。
autowire属性
自动装配,不用显示使用ref。
有四种模式:no(默认)、byName、byType、constructor
init-method属性
org.springframework.beans.factory.InitializingBean 接口可以设置初始化工作。它只有以下一个方法,不过不推荐使用。
void afterPropertiesSet() throws Exception;
可以用bean的init-method属性指定初始化方法或者用 @PostConstruct 注解。初始化的方法不含参数。
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
public class ExampleBean {
public void init() {
//...
}
}
destroy-method属性
与init-method类似。可以实现 org.springframework.beans.factory.DisposableBean 接口,不过不推荐。也可以用 @PreDestroy 注解或者 bean的destroy-method属性。同样不能带参数。
DisposableBean接口的方法:
void destroy() throws Exception;
default-init-method和default-destroy-method
可以在beans标签中使用这两个属性设置默认方法。
bean的生命周期
Lifecycle接口
public interface Lifecycle {
void start();
void stop();
boolean isRunning();
}
所有Spring管理的对象都可以实现该接口。ApplicationContext接收到启动和停止信号时,它将自动调用上下文内所有Lifecycle的实现。通过委托给 LifecycleProcessor 来做这个工作。
public interface LifecycleProcessor extends Lifecycle {
void onRefresh();
void onclose();
}
LifecycleProcessor是Lifecycle的子接口。
public interface Phased {
int getPhase();
}
public interface SmartLifecycle extends Lifecycle, Phased {
boolean isAutoStartup();
void stop(Runnable callback);
}
常规的Lifecycle接口只有在容器上下文显示调用start/stop方法时,都会去回调Lifecycle的实现类的start/stop方法,并不意味着上下文刷新时自动启动。可以用SmartLifecycle解决。
stop通知在销毁前不能保证到达,在常规关闭时,所有生命周期bean将首先收到停止通知,然后才传播一般的销毁回调,但是,在上下文生命周期内的热刷新或中止刷新尝试时,只调用销毁方法。
getPhase()默认返回0,数值越小越先启动,越后停止。
DefaultLifecycleProcessor是LifecycleProcessor的默认实现,将等待每个阶段中的对象组的超时值来调用callback的run()方法,默认的每个阶段超时为30秒。可以通过以下方法修改:
<bean id="lifecycleProcessor" class="org.springframework.context.support.DefaultLifecycleProcessor">
<!-- timeout value in milliseconds -->
<property name="timeoutPerShutdownPhase" value="10000"/>
</bean>
非Web应用程序关闭Spring IoC容器
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public final class Boot {
public static void main(final String[] args) throws Exception {
ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
// add a shutdown hook for the above context...
ctx.registerShutdownHook();
// app runs here...
// main method exits, hook is called prior to the app shutting down...
}
}
向JVM注册一个关闭钩子,确保正常关机,并调用singleton bean上相关销毁方法,以便释放所有资源。仍然必须正确配置和实现这些销毁回调。
Aware
能够获取自身在Spring容器中的一些属性。
public interface ApplicationContextAware {
void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}
public interface BeanNameAware {
void setBeanName(String name) throws BeansException;
}
例如applicationContextAware为bean的IoC容器上下文,name为bean的id。
继承
使用parent属性。
<bean id="inheritedTestBean" abstract="true"
class="org.springframework.beans.TestBean">
<property name="name" value="parent"/>
<property name="age" value="1"/>
</bean>
<bean id="inheritsWithDifferentClass"
class="org.springframework.beans.DerivedTestBean"
parent="inheritedTestBean" init-method="initialize">
<property name="name" value="override"/>
<!-- the age property value of 1 will be inherited from parent -->
</bean>
如果父bean没有指定类,则一定要显式将父bean标记为抽象。
<bean id="inheritedTestBeanWithoutClass" abstract="true">
<property name="name" value="parent"/>
<property name="age" value="1"/>
</bean>
<bean id="inheritsWithClass" class="org.springframework.beans.DerivedTestBean"
parent="inheritedTestBeanWithoutClass" init-method="initialize">
<property name="name" value="override"/>
<!-- age will inherit the value of 1 from the parent bean definition-->
</bean>
被标志为抽象的bean将不能实例化,只能作为一个模板。