IoC,Inversion of Control 控制反转,是一个过程。仅通过构造函数、工厂方法或在对象实例化后在对象实例上设置属性来定义其依赖关系。容器负责这些工作,这个过程从本质上来说是bean本身的反向,因此称为反向控制。
负责实例化、配置及装配bean。容器从配置元数据那获取该怎么实例化、配置及装配bean。而配置来源主要有三个:1)XML;2)java注释;3)java代码。xml比较常用。
@Configuration // 将一个普通类标志为IoC容器
public class CustomConfig {
@Bean // 定义一个bean
public User customUser() {
return new User();
}
}
private static void test1() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(CustomConfig.class);
User user = context.getBean(User.class);
System.out.println(user);
}
上面代码展示了java代码配置bean的方式。通过AnnotationConfigApplicationContext来获取容器。
图 ApplicationContext的方法及实现类
BeanFactory 接口提供了管理Bean的方法,而ApplicationContext继承了该接口,并有以下扩展:
1)更容易与Spring 的aop集成。
2)消息资源处理。
3)事件发布。
4)针对web项目,提供了特定的子类。
图 ApplicationContext的方法及实现类
构成程序主干并由IoC容器管理的对象称为bean。
在模块化开发中,不同模块的容器可能存在依赖了同一bean的bean。有时,我们考虑到扩展或者在某个模块中有特定的命名规范,所依赖的这个bean的命名可能会不同。 比如模块A、B依赖同一个数据源配置bean。 在模块A中该bean命名为datasourceA,在模块B中命名为datasourceB。这时候需要用到别名。
xml配置中,实例化bean有三种方式:1)构造函数;2)静态工厂方法;3)bean的工厂方法。
public class GoodsDao {
}
public class GoodsService {
public GoodsDao makeGoodsDao() {
return new GoodsDao();
}
}
public class StaticFactory {
public static GoodsService makeGoodsService() {
return new GoodsService();
}
}
依赖是指对象在运行中需要用到的其他对象。在IoC中由容器负责注入。注入方式有两种:1)构造函数;2)set方法。
public class OtherDao {
}
public class ReportDao {
}
public class ReportService {
private ReportDao reportDao;
private String name;
private Double num;
public ReportService(ReportDao reportDao, String name, Double num) {
this.reportDao = reportDao;
this.name = name;
this.num = num;
}
private Properties dataProperties;
private Map stockInfoMap;
private List links;
private OtherDao otherDao;
public void setDataProperties(Properties dataProperties) {
this.dataProperties = dataProperties;
}
public void setStockInfoMap(Map stockInfoMap) {
this.stockInfoMap = stockInfoMap;
}
public void setLinks(List links) {
this.links = links;
}
public void setOtherDao(OtherDao otherDao) {
this.otherDao = otherDao;
}
@Override
public String toString() {
return "ReportService{" +
"reportDao=" + reportDao +
", name='" + name + '\'' +
", num=" + num +
", dataProperties=" + dataProperties +
", stockInfoMap=" + stockInfoMap +
", links=" + links +
", otherDao=" + otherDao +
'}';
}
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("dependency.xml");
ReportService reportService = applicationContext.getBean(ReportService.class);
System.out.println(reportService);
}
}
localhost
admin
123
容器创建一个bean时,会先创建起依赖的bean。但是有时两个bean直接没有直接依赖,但是希望在创建这个bean之前,先创建其他的bean。可用depends-on来完成这个需求:
在创建teacherDao这个bean之前,会先创建userDao及baseDao这两个bean。
在容器中,默认会在项目加载时把所有的bean都创建完成,这样做的好处是某个bean的配置错误能在运行时被发现。但是有时不希望创建所有的bean,希望当要使用这个bean时再来创建,default-lazy-init 懒加载属性可以作用于全局的beans,也可以作用于单个的beans。当值为true时,bean在第一次使用时才会被创建。
假如bean1的某个方法,在每次调用时都需要一个特定的bean2(不是bean1的直接依赖,即非bean1的字段)。传统方法是,可以在该方法中通过ApplicationContext获取bean2。这是这样加大了耦合度,容器提供了Lookup标签来实现此类需求:
public class Bean2 {
}
public class Bean1 {
private final static ApplicationContext applicationContext = new ClassPathXmlApplicationContext("method.xml");
/**
* 传统方式
*/
public void showBean2OfTradition() {
Object bean2 = applicationContext.getBean("bean2");
System.out.println(bean2);
}
/**
* Lookup 方法
*/
public Bean2 getBean2() {
return null;
}
public static void main(String[] args) {
Bean1 bean1 = applicationContext.getBean(Bean1.class);
bean1.showBean2OfTradition();
System.out.println(bean1.getBean2());
}
}
bean 也可以指定作用域(生命周期),Spring支持六种作用域。bean的scope属性来指定该bean的作用域。
singleton |
默认作用域。不同容器生成的bean不同。 |
prototype |
在同一容器中,不同bean依赖的bean被创建的实例不同。 |
request |
每次请求都会创建不同的bean。 |
session |
每个session都会创建不同 bean。 |
application |
每个servletContext生成不同的bean。 |
websocket |
每个websocket连接生成不同的bean。 |
表 spring 的六种bean的作用域