什么是Ioc
IoC 也称为依赖注入 (DI)又名控制反转。Ioc是一个过程,可以有以下方式来实现。
- 接口注入:接口注入基于接口将调用与实现分离。这种依赖注入方式必须实现容器所规定的接口,使程序代码和容器的API绑定在一起,这不是理想的依赖注入方式。Spring不支持这种方式的依赖注入.
- Setter方式注入:基于JavaBean的Setter方法为属性赋值。在实际开发中得到了广泛应用。
- 构造器注入:基于构造方法为属性赋值。容器通过调用类的构造方法,将其所需的依赖关系注入其中。
基本依赖
org.springframework.beans和org.springframework.context包是Spring框架的IoC容器的基础。由于我们的Spring Boot HelloWorld 使用了spring-boot-starter-web,其包含了这两个依赖,所以我们可以不再添加这两个依赖。
Ioc容器
org.springframework.context.ApplicationContext接口代表 Spring IoC 容器,负责实例化、配置和组装 bean。
独立应用程序中ApplicationContext实例化
Spring 提供了ApplicationContext接口的几种实现。在独立应用程序中,通常使用如下两种方式
- ClassPathXmlApplicationContext:读取类的根路径的xml配置(spring Boot中resource目录即为类的根路径)
- FileSystemXmlApplicationContext:通过文件实际路径来读取xml配置。
其继承结构图如下:
web应用程序中ApplicationContext实例化
在web.xml中声明如下配置即可:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/daoContext.xml /WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
- 侦听器检查contextConfigLocation参数。如果该参数不存在,则侦听器将/WEB-INF/applicationContext.xml用作默认值。
- 当参数确实存在时,侦听器String通过使用预定义的分隔符(逗号、分号和空格)来分隔参数。
- 也支持 *号匹配
- /WEB-INF/*Context.xml(在WEB-INF目录中所有以Context.xml结尾的xml文件)
- /WEB-INF/**/*Context.xml (在WEB-INF目录及其子目录中所有以Context.xml结尾的xml文件)
容器中bean的配置方式
容器通过读取配置元数据来获取有关要实例化、配置和组装哪些对象的指令。配置元数据有以下3种方式配置:
- XML配置:将Bean的所以依赖配置都放在xml配置文件中配置
- Java 注解配置:Spring 2.5 引入了对基于注解的配置元数据的支持。@Component、@Autowired等
- Java 代码配置:从 Spring 3.0 开始,可以使用代码配置。因此,我们可以使用 Java 而不是 XML 文件来定义应用程序类外部的 bean。@Configuration, @Bean, @Import,和@DependsOn等注释
xml配置
首先创建一个简单的Bean对象Emp
package com.yyoo.boot.bean;
import java.util.Date;
public class Emp {
private Long empNo;
private String eName;
private String job;
private Long mgr;
private Date hiredate;
private Double sal;
private Double comm;
private Long deptno;
public Emp(){}
public Emp(Long empNo, String eName, String job, Long mgr, Date hiredate, Double sal, Double comm, Long deptno) {
super();
this.empNo = empNo;
this.eName = eName;
this.job = job;
this.mgr = mgr;
this.hiredate = hiredate;
this.sal = sal;
this.comm = comm;
this.deptno = deptno;
}
/**
* 工厂静态方法注入
* @return
*/
public static Emp newInstance(){
Emp emp = new Emp();
emp.setEmpNo(1000L);
emp.setEName("工厂静态方法注入");
return emp;
}
/**
* 工厂实例方法注入
* @return
*/
public Emp getInstance(){
Emp emp = new Emp();
emp.setEmpNo(1001L);
emp.setEName("工厂实例方法注入");
return emp;
}
public Long getEmpNo() {
return empNo;
}
public void setEmpNo(Long empNo) {
this.empNo = empNo;
}
public String geteName() {
return eName;
}
public void setEName(String eName) {
this.eName = eName;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
public Long getMgr() {
return mgr;
}
public void setMgr(Long mgr) {
this.mgr = mgr;
}
public Date getHiredate() {
return hiredate;
}
public void setHiredate(Date hiredate) {
this.hiredate = hiredate;
}
public Double getSal() {
return sal;
}
public void setSal(Double sal) {
this.sal = sal;
}
public Double getComm() {
return comm;
}
public void setComm(Double comm) {
this.comm = comm;
}
public Long getDeptno() {
return deptno;
}
public void setDeptno(Long deptno) {
this.deptno = deptno;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("Emp [empNo=");
builder.append(empNo);
builder.append(", eName=");
builder.append(eName);
builder.append(", job=");
builder.append(job);
builder.append(", mgr=");
builder.append(mgr);
builder.append(", hiredate=");
builder.append(hiredate);
builder.append(", sal=");
builder.append(sal);
builder.append(", comm=");
builder.append(comm);
builder.append(", deptno=");
builder.append(deptno);
builder.append("]");
return builder.toString();
}
}
在resource目录新建spring-ioc.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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<beans>
<!--
setter方式注入
property标签有3个属性:
name:属性的名称
value:属性的值
ref:属性的引用对象(一般引用其他定义好的bean,使用bean标签的id属性的 值)
-->
<bean id="emp1" class="com.yyoo.boot.bean.Emp">
<property name="comm" value="800"></property>
<property name="deptno" value="10"></property>
<property name="empNo" value="1220"></property>
<property name="eName" value="张三"></property>
<!-- 给hiredate注入null值 -->
<property name="hiredate"><null/></property>
<property name="job" value="saler"></property>
<property name="mgr" value="2281"></property>
<property name="sal" value="2000"></property>
</bean>
<!-- 构造器注入
constructor-arg标签有5个属性
name:构造方法参数的名称
value:构造方法参数的值
ref:构造方法参数的引用对象(一般引用其他定义好的bean,使用bean标签的id属性的 值)
index:构造方法参数的索引位置,下标从0开始
type:构造方法参数的类型
其中:名称、(类型 + 索引位置)均可以单独确定唯一的参数
多个构造方法,如:this(int a,int b) 和 this(double c,double d),
如果只使用index就无法确定到底调用哪个方法,所以再加上类型,才能唯一确定。
-->
<bean id="emp2" class="com.yyoo.boot.bean.Emp">
<constructor-arg index="0" value="2281"></constructor-arg>
<constructor-arg name="comm" value="300"></constructor-arg>
<constructor-arg index="1" type="java.lang.String" value="姓名"></constructor-arg>
<constructor-arg name="job" value="saler"></constructor-arg>
<constructor-arg name="deptno" value="10"></constructor-arg>
<constructor-arg name="mgr" value="2000"></constructor-arg>
<constructor-arg name="sal" value="3000"></constructor-arg>
<!-- 给hiredate注入null值 -->
<constructor-arg name="hiredate"><null/></constructor-arg>
</bean>
<!--
spring还支持一个工厂方法方式实例化对象,这里就不过多介绍了
这里的工厂class就是Emp本身,其实也可以是另外的Factory类的
-->
<!-- 工厂模式_静态方法 -->
<bean id="emp3" class="com.yyoo.boot.bean.Emp" factory-method="newInstance"></bean>
<!-- 工厂模式_成员方法 -->
<bean id="emp4" factory-bean="emp3" factory-method="getInstance"></bean>
</beans>
</beans>
这里一口气讲完了Emp对象在spring中的4中实例化方式。
测试主程序
package com.yyoo.boot.ioc;
import com.yyoo.boot.bean.Emp;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Demo1 {
@Test
public void test1(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring-ioc.xml");
Emp emp1 = context.getBean("emp1", Emp.class);
System.out.println(emp1);
Emp emp2 = context.getBean("emp2", Emp.class);
System.out.println(emp2);
Emp emp3 = context.getBean("emp3", Emp.class);
System.out.println(emp3);
Emp emp4 = context.getBean("emp4", Emp.class);
System.out.println(emp4);
}
}
运行结果
Emp [empNo=1220, eName=张三, job=saler, mgr=2281, hiredate=null, sal=2000.0, comm=800.0, deptno=10]
Emp [empNo=2281, eName=姓名, job=saler, mgr=2000, hiredate=null, sal=3000.0, comm=300.0, deptno=10]
Emp [empNo=1000, eName=工厂静态方法注入, job=null, mgr=null, hiredate=null, sal=null, comm=null, deptno=null]
Emp [empNo=1001, eName=工厂实例方法注入, job=null, mgr=null, hiredate=null, sal=null, comm=null, deptno=null]
工程结构图
我们的Demo使用了JUnit,所以我们将它写在了test包。
Ioc容器功能示意图
所以的配置元数据都交由Spring 容器管理,程序中通过容器获得的实例都是通过spring 容器 produces(组装、生产)之后成为可直接使用的实例。
上一篇:001-Spring Boot HelloWorld
下一篇:003-Spring Ioc Bean的初始化过程与循环依赖