Spring容器是Spring框架的核心。容器将创建对象,把它们连接在一起,配置它们,并管理他们的整个生命周期从创建到销毁。Spring 容器使用依赖注入(DI)来管理组成一个应用程序的组件。这些对象被称为 Spring Beans。
IOC(Inversion of Control):控制反转(也称依赖倒置),是一种设计思想,也是容器。
IOC容器具有依赖注入功能的容器,它可以创建对象,IOC 容器负责实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。通常new一个实例,控制权由程序员控制,而"控制反转"是指new实例工作不由程序员来做而是交给Spring容器来做。在Spring中BeanFactory是IOC容器的实际代表者。
容器 | 描述 |
---|---|
BeanFactory | 它是最简单的容器,给 DI 提供了基本的支持,它用 org.springframework.beans.factory.BeanFactory 接口来定义。BeanFactory 或者相关的接口,如 BeanFactoryAware,InitializingBean,DisposableBean,在 Spring 中仍然存在具有大量的与 Spring 整合的第三方框架的反向兼容性的目的。 |
ApplicatoinContext | 该容器添加了更多的企业特定的功能,例如从一个属性文件中解析文本信息的能力,发布应用程序事件给感兴趣的事件监听器的能力。该容器是由 org.springframework.context.ApplicationContext 接口定义。 |
注1:BeanFactory和ApplicatonContext是Spring的两大核心接口。
注2:ApplicationContext是BeanFactory的子接口,它们都可以代表Spring容器。
注3:ApplicationContext 容器包括 BeanFactory 容器的所有功能,所以通常不建议使用BeanFactory。但BeanFactory 仍然可以用于轻量级的应用程序,如移动设备或基于 applet 的应用程序,其中它的数据量和速度是显著。
注4:IOC容器不只是Spring才有,很多框架也都有该技术。
Spring容器最基本的接口是:BeanFactory,负责配置、创建、管理Bean,还负责管理Bean与Bean之间的依赖关系。
BeanFactory接口包含如下几个基本方法:
boolean containsBean(String name)
判断Spring容器是否包含id为name的Bean对象。
获取Spring容器中属于requiredType类型的、唯一的Bean对象。
Object getBean(String name)
返回容器id为name的Bean对象。
返回容器中id为name,并且类型为requiredType的Bean。
Class> getType(String name)
返回容器中id为name的Bean实例的类型。
Spring IOC容器的实现,从根源上是BeanFactory,但真正可以作为一个可以独立使用的IOC容器还是DefaultListableBeanFactory,因此可以这么说,DefaultListableBeanFactory是整个Spring IOC的始祖。
ApplicationContext是BeanFactory的子接口,具有BeanFactory所有的功能。大部分时候,都不会使用BeanFactory实例作为Spring容器,而是使用ApplicationContext实例作为容器。
ApplicationContext称为Spring上下文,如果说BeanFactory是心脏,那么ApplicationContext就是身躯。
最常用的ApplicationContext接口实现类:
// 当前路径加载单个配置文件
ApplicationContext ctx = new FileSystemXmlApplicationContext("applicationContext.xml");
// 同时加载多个配置文件
String[] locations = {"bean1.xml", "bean2.xml", "bean3.xml"};
ApplicationContext ctx = new FileSystemXmlApplicationContext(locations );
// 根据具体路径加载文件
ApplicationContext ctx = new FileSystemXmlApplicationContext("D:/project/bean.xml");
// 加载单个配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml");
// 同时加载多个配置文件
String[] locations = {"bean1.xml", "bean2.xml", "bean3.xml"};
ApplicationContext ctx = new ClassPathXmlApplication(locations);
// 或者用通配符同时加载多个配置文件:
ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:/*.xml");
注:其中FileSystemXmlApplicationContext和ClassPathXmlApplicationContext与BeanFactory的xml文件定位方式一样是基于路径的。
ServletContext servletContext = request.getSession().getServletContext();
ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(servletContext);
面试题:
ClassPathXmlApplicationContext和FileSystemXmlApplicationContext的区别如下:
(1)Classpath:前缀是不需要的,默认就是指项目的classpath路径下面。
(2)如果要使用绝对路径,需要加上file:前缀表示这是绝对路径。
在Java项目开发中,IOC意味着将你设计好的对象交给容器控制,而不是传统的在对象内部直接控制(如new一个对象实例)。理解IOC的关键是要明确“谁控制谁?控制什么?为何是反转(有反转就应该有正转)?哪些方面反转了?”
谁控制谁?控制什么?
传统Java SE程序设计,我们直接通过new进行创建对象,是主动去创建依赖对象;
而IOC是有专门一个容器来创建这些对象,即由IOC容器来控制对象的创建。
谁控制谁?当然是IOC控制了Bean。
控制什么?主要控制了外部资源获取(不只是对象,包括文件等)。
为何是反转?哪些方面反转了?
有反转就有正转,传统程序是由我们主动控制去直接获取依赖对象,也就是正转。
而反转则是由容器来帮忙创建及注入依赖对象。
为何反转?因为由IOC帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转。
哪些方面反转了?依赖对象的获取被反转了,以前是主动的new对象,现在是被动的接收对象。
(传统应用程序示意)
传统应用程序(主动模式):
//用户类
public class User{
private UserInfo userInfo;
public void setUserInfo(UserInfo userInfo){
this.userInfo = userInfo;
}
public UserInfo getUserInfo(){
return this.userInfo;
}
}
//用户信息类
public class UserInfo{
private Integer userId;
private String userName;
public void setUserId(Integer userId){
this.userId = userId;
}
public Integer getUserId(){
return this.userId;
}
public void setUserName(String userName){
this.userName = userName;
}
}
//客户端类
public class Test{
public static void main(String[] args){
//主动创建UserInfo对象
UserInfo userInfo = new UserInfo();
//主动注入属性值
userInfo.setUserId(1);
userInfo.setUserName(“张三”);
//主动创建User对象
User user = new User();
//主动注入属性值
user.setUserInfo(userInfo);
}
}
IOC/DI容器应用程序(被动模式):
//用户类
public class User{
private UserInfo userInfo;
public void setUserInfo(UserInfo userInfo){
this.userInfo = userInfo;
}
public UserInfo getUserInfo(){
return this.userInfo;
}
}
//用户信息类
public class UserInfo{
private Integer userId;
private String userName;
public void setUserId(Integer userId){
this.userId = userId;
}
public Integer getUserId(){
return this.userId;
}
public void setUserName(String userName){
this.userName = userName;
}
}
IOC容器:applicationContext.xml
<bean id=”userInfo” class=”com.alibaba.model.UserInfo”>
<property name=”userId” value=”1”>property>
<property name=”userName” value=”张三”>property>
bean>
<bean id=”user” class=”com.alibaba.model.User”>
<property name=”userInfo” ref=”userInfo”>property>
bean>
单元测试
public class Test{
@Test
public void Test1(){
ApplicationContext context = new ClassPathXmlApplicatonContext(“applicationContext.xml”);
User user = (User)context.getBean(“user”);
UserInfo userInfo = user.getUserInfo();
System.out.println(userInfo.getUserId() +” ”+ userInfo.getUserName());
}
}
如何去理解控制反转?
反转:依赖对象的获得被反转,变为由自己创建,反转为从IOC容器中获取。
个人认为所谓控制反转就是:获得依赖对象的方式反转了。
带来的好处:
1、代码更为简洁,不需要再去new需要的对象;
2、面向接口编程,使用类和具体类解耦,易扩展,替换实现者;
3、方便进行AOP增强(没有IOC就无法AOP)。
IOC容器做什么工作?
负责创建和管理Bean(类的实例),它是一个工厂,负责对外提供Bean实例。
IOC容器就是工厂模式的实例,IOC容器也被称为Bean工厂。
IOC对编程最大的改变不是代码上,而是思想上,发生了“主从换位”的变化。传统程序想获得资源都要主动出击,但是IOC/DI思想中,程序就变成被动了,被动的等待IOC容器来创建并注入它所需要的资源。好莱坞法则:别找我们,我们找你。