在 Spring 中,那些组成应用程序的主体及由 Spring IOC 容器所管理的对象,被称之为 bean。简单地讲,bean 就是由 IOC 容器初始化、装配及管理的对象。
创建(装配)bean的方式
创建Bean,也就是在IOC容器中装配Bean,现在主要用的方式是注解方式。而装配Bean用的方式就是依赖注入,用的方式主要是属性注入。
在面向对象的程序中,要想调用某个类的成员方法,就需要先实例化该类的对象。根据配置的方式主要分为两大类。
XML方式配置
这种方式主要通过xml文件的配置
setter方式
java代码
public interface IUserDao {
void addUser();
void delUser();
void updateUser();
}
public class UserDaoImpl implements IUserDao {
public void addUser() {
System.out.println("addUser方法被调用了");
}
public void delUser() {
System.out.println("delUser方法被调用了");
}
public void updateUser() {
System.out.println("updateUser方法被调用了");
}
}
public class UserAction {
private IUserDao dao; //dao是一个依赖对象,要由springg进行管理,要生成 get set 方法
public void execute(){
dao.addUser();
dao.updateUser();
dao.delUser();
}
}
xml配置
//配置文件
<bean name="userAction_name" class="cat.action.UserAction" >
<property name="dao" ref="userDao_name" /> //引用的是下面的名称
</bean>
<bean name="userDao_name" class="cat.dao.UserDaoImpl" />
//测试
ClassPathXmlApplicationContext ctx=new ClassPathXmlApplicationContext("beans.xml");
UserAction action=(UserAction)ctx.getBean("userAction_name");
action.execute();
构造函数
java代码
public class UserAction {
//public UserAction(){} 可以保保留一个无参的构造函数
//这是几个依赖对象,不用生成get set方法了
private UserInfo user;
private String school;
private IUserDao dao;
//希望Spring 由构造函数注入依赖对象
public UserAction(IUserDao dao,UserInfo user,String school){
this.dao=dao;
this.school=school;
this.user=user;
}
public void execute(){
dao.addUser();
dao.updateUser();
dao.delUser();
System.out.println(user);
System.out.println(school);
}
xml文件配置
//配置文件
<bean name="userInfo_name" class="cat.beans.UserInfo" >
<property name="id" value="1" />
<property name="userName" value="周周" />
<property name="password" value="123" />
<property name="note" value="这是备注" />
</bean>
<bean name="userAction_name" class="cat.action.UserAction" >
<constructor-arg ref="userDao_name" />
<constructor-arg ref="userInfo_name" />
<constructor-arg value="哈尔滨师范大学" />
</bean>
/*
也可以指定 索引和 type 属性,索引和type 都可以不指定
<bean name="userAction_name" class="cat.action.UserAction" >
<constructor-arg index="0" ref="userDao_name" type="cat.dao.IUserDao" /> 如果是接口,就不能指定是实现类的类型
<constructor-arg index="1" ref="userInfo_name" type="cat.beans.UserInfo" />
<constructor-arg index="2" value="哈尔滨师范大学" />
</bean>
*/
<bean name="userDao_name" class="cat.dao.UserDaoImpl" />
静态工厂方式
java代码
//工厂,用来生成dao的实现类
public class UserDaoFactory {
public static IUserDao createUserDaoInstance(){
return new UserDaoOracleImpl();
}
}
public class UserAction {
private IUserDao dao;//使用工厂方式注值,也要生成set方法
public void execute(){
dao.addUser();
dao.updateUser();
dao.delUser();
}
public void setDao(IUserDao dao) {
this.dao = dao;
}
}
xml文件配置
//配置文件
<bean name="userAction_name" class="cat.action.UserAction" >
<property name="dao" ref="userDao_name" />
</bean>
<bean name="userDao_name" class="cat.dao.UserDaoFactory" factory-method="createUserDaoInstance" />
实例工厂
java代码
//工厂 =>
public class UserDaoFactory {
//这个方法不是静态的
public IUserDao createUserDaoInstance(){
return new UserDaoOracleImpl();
}
}
xml文件配置
//配置文件
<bean name="userAction_name" class="cat.action.UserAction" >
<property name="dao" ref="userDao_name" />
</bean>
<bean name="userDaoFactory_name" class="cat.dao.UserDaoFactory" />
<bean name="userDao_name" factory-bean="userDaoFactory_name" factory-method="createUserDaoInstance" />
自动装配
自动装配就是指 Spring 容器可以自动装配(autowire)相互协作的 Bean 之间的关联关系,将一个 Bean 注入其他 Bean 的 Property 中。要使用自动装配,就需要配置 元素的 autowire 属性。autowire 属性有五个值
注解方式
直接作用在java类上面,例如:@Component、@Repository、@Service、@Controller等等,这些注解的具体作用在后面会讲解到。
Java类配置方式
在标注了@Configuration的java类上,通过在类方法上标注@Bean定义一个bean。方法必须提供Bean的实例化逻辑。
几种方式的对比
应用场景:
Bean的作用域
Spring Framework支持五种作用域,五种作用域中,request、session 和 global session 三种作用域仅在基于web的应用中使用(不必关心你所采用的是什么web应用框架),只能用在基于 web 的 Spring ApplicationContext 环境。
singleton
唯一 bean 实例
当一个 bean 的作用域为 singleton,那么Spring IoC容器中只会存在一个共享的 bean 实例,并且所有对 bean 的请求,只要 id 与该 bean 定义相匹配,则只会返回bean的同一实例。
singleton 是单例类型(对应于单例模式),就是在创建起容器时就同时自动创建了一个bean的对象,不管你是否使用,但我们可以指定Bean节点的 lazy-init=”true” 来延迟初始化bean,这时候,只有在第一次获取bean时才会初始化bean,即第一次请求该bean时才初始化。 每次获取到的对象都是同一个对象。注意,singleton 作用域是Spring中的缺省作用域。
要在XML中将 bean 定义成 singleton ,可以这样配置:
<bean id="ServiceImpl" class="cn.csdn.service.ServiceImpl" scope="singleton">
也可以使用注解的方式:@Scope 注解(它可以显示指定bean的作用范围。)的方式
@Service
@Scope("singleton")
public class ServiceImpl{
}
prototype
每次请求都会创建一个新的 bean 实例
当一个bean的作用域为 prototype,表示一个 bean 定义对应多个对象实例。 prototype 作用域的 bean 会导致在每次对该 bean 请求(将其注入到另一个 bean 中,或者以程序的方式调用容器的 getBean() 方法)时都会创建一个新的 bean 实例。prototype 是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。根据经验,对有状态的 bean 应该使用 prototype 作用域,而对无状态的 bean 则应该使用 singleton 作用域。
在 XML 中将 bean 定义成 prototype ,可以这样配置:
<bean id="account" class="com.foo.DefaultAccount" scope="prototype"/>
或者
<bean id="account" class="com.foo.DefaultAccount" singleton="false"/>
也可以使用注解的方式配置:
@Service
@Scope("prototype")
public class ServiceImpl{
}
request
每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP request内有效。
request只适用于Web程序,每一次 HTTP 请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效,当请求结束后,该对象的生命周期即告结束。 在 XML 中将 bean 定义成 request ,可以这样配置:
xml文件配置:
<bean id="loginAction" class=cn.csdn.LoginAction" scope="request"/>
也可以使用注解的方式配置:
@Service
@Scope("request")
public class ServiceImpl{
}
session
每一次HTTP请求都会产生一个新的 bean,该bean仅在当前 HTTP session 内有效。
session只适用于Web程序,session 作用域表示该针对每一次 HTTP 请求都会产生一个新的 bean,同时该 bean 仅在当前 HTTP session 内有效.与request作用域一样,可以根据需要放心的更改所创建实例的内部状态,而别的 HTTP session 中根据 userPreferences 创建的实例,将不会看到这些特定于某个 HTTP session 的状态变化。当HTTP session最终被废弃的时候,在该HTTP session作用域内的bean也会被废弃掉。
xml文件配置:
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>
也可以使用注解的方式配置:
@Service
@Scope("session")
public class ServiceImpl{
}
globalSession
global session 作用域类似于标准的 HTTP session 作用域,不过仅仅在基于 portlet 的 web 应用中才有意义。Portlet 规范定义了全局 Session 的概念,它被所有构成某个 portlet web 应用的各种不同的 portle t所共享。在global session 作用域中定义的 bean 被限定于全局portlet Session的生命周期范围内。
xml文件配置:
<bean id="user" class="com.foo.Preferences "scope="globalSession"/>
也可以使用注解的方式配置:
@Service
@Scope("globalSession")
public class ServiceImpl{
}
五种作用域比较
Bean的生命周期
对于普通的java对象来说,它们的生命周期就是:实例化——》不再使用的时候通过垃圾回收机制进行回收。
Spring中Bean 的生命周期概括起来就是 4 个阶段:
- 实例化(Instantiation)
- 属性赋值(Populate)
- 初始化(Initialization)
- 销毁(Destruction)
Spring 容器可以管理 singleton 作用域 Bean 的生命周期,在此作用域下,Spring 能够精确地知道该 Bean 何时被创建,何时初始化完成,以及何时被销毁。而对于 prototype 作用域的 Bean,Spring 只负责创建,当容器创建了 Bean 的实例后,Bean 的实例就交给客户端代码管理,Spring 容器将不再跟踪其生命周期。每次客户端请求 prototype 作用域的 Bean 时,Spring 容器都会创建一个新的实例,并且不会管那些被配置成 prototype 作用域的 Bean 的生命周期。
Bean的初始化过程
- BeanDefinitionReader读取Resource所指向的配置文件资源,然后解析配置文件。配置文件中每一个解析成一个BeanDefinition对象,并保存到BeanDefinitionRegistry中;
容器扫描BeanDefinitionRegistry中的BeanDefinition;调用InstantiationStrategy进行Bean实例化的工作;使用BeanWrapper完成Bean属性的设置工作; - 单例Bean缓存池:Spring 在DefaultSingletonBeanRegistry类中提供了一个用于缓存单实例 Bean 的缓存器,它是一个用HashMap实现的缓存器,单实例的Bean以beanName为键保存在这个HashMap中。
BeanFactory中Bean的生命周期
ApplicationContext中Bean的生命周期
BeanFactory、ApplicationContext对比
这里说的Bean的生命周期讲的都是作用域为singleton。
- ApplicationContext会利用Java反射机制自动识别出配置文件中定义的BeanPostProcessor、 InstantiationAwareBeanPostProcesso 和BeanFactoryPostProcessor后置器,并自动将它们注册到应用上下文中。而BeanFactory需要在代码中通过手工调用addBeanPostProcessor()方法进行注册
- ApplicationContext在初始化应用上下文的时候就实例化所有单实例的Bean。而BeanFactory在初始化容器的时候并未实例化Bean,直到第一次访问某个Bean时才实例化目标Bean。