今天来总结Spring容器的Ioc(控制反转)和DI(依赖注入)!
Spring的核心思想,ioc 和 di
Ioc:ioc强调由第三方容器根据客户的需求创建对象,然后根据客户提供的方法将对象传递给客户。
Di: 强调第三方容器创建对象以后, 通过什么方法将对象传递过去.
理解示例图:
模拟实现springIoc
首先新建两个实体类Boy和Girl:
package com.wtu.spring.base;
public class Boy {
private String id;
private String name;
private Double salary;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getSalary() {
return salary;
}
public void setSalary(Double salary) {
this.salary = salary;
}
@Override
public String toString() {
return "Boy [id=" + id + ", name=" + name + ", salary=" + salary + "]";
}
}
package com.wtu.spring.base;
public class Girl {
private String id;
private String name;
private Double salary;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getSalary() {
return salary;
}
public void setSalary(Double salary) {
this.salary = salary;
}
@Override
public String toString() {
return "Girl [id=" + id + ", name=" + name + ", salary=" + salary + "]";
}
}
在同目录下, 新建一个spring.xml文件, 模拟Spring的配置文件:
然后, 采用dom4j的方式解析xml文件来获取属性, 最后通过反射获取对象.
SpringIOC.java
package com.wtu.spring.base;
import org.apache.commons.beanutils.BeanUtils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.xml.sax.SAXException;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
public class SpringIOC {
private String xmlPath;
public SpringIOC(String xmlPath) {
this.xmlPath = xmlPath;
}
public Object getBean(String id) throws Exception {
//解析spring.xml文件 得到Document对象
Document document = this.getDocument(xmlPath);
//根据 bean的id 获得bean元素
Element beanElement = this.getBeanElement(document, id);
//获取bean元素的class属性值
String classAttributeValue = this.getAttributeValue(beanElement);
//根据class属性值通过反射创建对象
Object obj = this.getObject(classAttributeValue);
//获取bean下property元素的name属性值和value属性 并且赋值给obj
obj = this.setProperty(obj, beanElement);
return obj;
}
/**
* 获取property元素 然后得到name 和 value的值 赋值给obj对象
*
* @param obj
* @param beanElement
* @return
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
private Object setProperty(Object obj, Element beanElement) throws Exception {
List elementList = beanElement.elements("property");
for (Element element : elementList) {
String name = element.attributeValue("name");
String value = element.attributeValue("value");
// 通过beanUtils来给对象赋值
BeanUtils.setProperty(obj, name, value);
}
return obj;
}
/**
* 根据字符串 通过反射创建对象
*
* @param classAttributeValue
* @return
* @throws ClassNotFoundException
* @throws IllegalAccessException
* @throws InstantiationException
*/
private Object getObject(String classAttributeValue) throws Exception {
return Class.forName(classAttributeValue).newInstance();
}
/**
* 根据bean元素 获取该元素中class属性的属性值
*
* @param beanElement
* @return
*/
private String getAttributeValue(Element beanElement) {
String attributeValue = beanElement.attributeValue("class");
return attributeValue;
}
/**
* 根据bean标签的id属性值 获取到该bean标签对应的元素对象 采用xpath查找
*
* @param document
* @param id
* @return
*/
private Element getBeanElement(Document document, String id) {
//传递xpath路径 然后document根据路径找到该元素
String xpath = "//bean[@id='" + id + "']";
Element element = (Element) document.selectSingleNode(xpath);
return element;
}
/**
* 根据xml文件的路径 解析得到Document对象
*
* @param xmlPath
* @return
* @throws SAXException
*/
private Document getDocument(String xmlPath) throws Exception {
SAXReader read = new SAXReader();
return read.read(new File(xmlPath));
}
}
最后写测试类:
package com.wtu.spring.base;
public class TestSpringIOC {
public static void main(String[] args) throws Exception {
SpringIOC springIOC = new SpringIOC("src/com/wtu/spring/base/spring.xml");
Boy boy = (Boy) springIOC.getBean("boyId");
System.out.println(boy);
Girl girl = (Girl) springIOC.getBean("girlId");
System.out.println(girl);
Girl girl2 = new Girl();
System.out.println(girl2);
}
}
运行结果:
Ioc容器的两个实现类
FileSystemXmlApplicationContext
ClassPathXmpApplicationContext
在web项目中能不能使用FileSystemXmlApplicationContext
为什么?
答:当项目部署服务器以后, src目录已经不存在了, 该目录下的配置文件全部到了classes目录下面, 这个目录是一个类路径.
Spring配置文件
//启动IOC容器
//FileSystemXmlApplicationContext(String... configLocations);
ApplicationContext ac = new FileSystemXmlApplicationContext(
new String[]{"src/com/wtu/spring/ioc/type1/spring3.0.xml"});
Boy boy = (Boy) ac.getBean("boyId");
System.out.println(boy);
// 启动IOC容器
// ClassPathXmpApplicationContext(String... configLocations);
ApplicationContext ac = new ClassPathXmlApplicationContext("com/wtu/spring/ioc/type3/spring3.0.xml");
Boy boy = (Boy) ac.getBean("boyId");
System.out.println(boy);
SpringIOC容器的本质
它就是一个map集合
Spring中bean的一些操作
1. bean的创建
a) 通过无参构造方法创建对象
public Boy() {
System.out.println("spring容器创建对象");
}
// 启动IOC容器
// ClassPathXmpApplicationContext(String... configLocations);
ApplicationContext ac = new ClassPathXmlApplicationContext("com/wtu/spring/bean/create/spring3.0.xml");
Boy boy = (Boy) ac.getBean("boyId");
System.out.println(boy);
b) 通过中间类的静态方法
package com.wtu.spring.bean.create2;
/**
* 中间类 创建对象
* @Author menglanyingfei
* @Created on 2018.01.16 14:37
*/
public class Middle {
public Middle() {
System.out.println("spring IOC 容器创建中间类对象");
}
public static IUserDao getInstance() {
return new UserDaoImpl("带参");
}
}
c) 通过中间类的非静态方法
package com.wtu.spring.bean.create3;
/**
* 中间类 创建对象
* @Author menglanyingfei
* @Created on 2018.01.16 14:37
*/
public class Middle {
public Middle() {
System.out.println("spring IOC 容器创建中间类对象");
}
public IUserDao getInstance() {
return new UserDaoImpl("带参");
}
}
2. bean的继承
Spring配置:
测试:
//启动IOC容器
//ClassPathXmpApplicationContext(String... configLocations);
ApplicationContext ac = new ClassPathXmlApplicationContext("com/wtu/spring/bean/extends_/spring3.0.xml");
Son son = (Son) ac.getBean("sonId");
System.out.println(son);
//Son{id='002', name='曹丕', salary='1000000.0'}
3. bean的生命周期
测试类:
//启动IOC容器
//ClassPathXmpApplicationContext(String... configLocations);
AbstractApplicationContext aac = new ClassPathXmlApplicationContext("com/wtu/spring/bean/life/spring3.0.xml");
/*
为什么在bean中配置了destroy-method, 但是还是不能够执行销毁的方法?
那是因为容器ApplicationContext不具备监听销毁的功能, 所以为了执行销毁, 得换一个容器
AbstractApplicationContext
*/
// 监听销毁执行bean的销毁的方法, 该方法会立即执行bean的销毁方法
// aac.close();
// 监听销毁执行bean的销毁的方法, 该方法不会立即执行bean的销毁方法, 等到关闭JVM的
// 那一刻才执行
aac.registerShutdownHook();
Thread.sleep(2000);
4. bean的单例和多例
测试类
//启动IOC容器
//ClassPathXmpApplicationContext(String... configLocations);
ApplicationContext ac = new ClassPathXmlApplicationContext("com/wtu/spring/bean/scope/spring3.0.xml");
// spring IOC容器创建bean默认是单例模式
IUserDao iUserDao = (IUserDao) ac.getBean("userDaoImpl");
IUserDao iUserDao2 = (IUserDao) ac.getBean("userDaoImpl");
System.out.println(iUserDao == iUserDao2);
// System.out.println(iUserDao);
5. bean的创建时间
Spring的DI
依赖注入第一种方式: 将bean中的属性通过set方式赋值给bean对象.
1. 注入简单类型(基本数据类型+String)
2. 注入类类型(以Date类型为例)
3.集合类型<里面的元素是基本类型>
4. 集合类型<里面的元素是自定义类型>
完整代码:
实体类:
public class Customer {
private Integer id;
private String name;
private String sex;
private Integer age;
private Date birthday;
private List address;
private Set phone;
private Map add_pho;
public Customer() {
System.out.println("IOC 创建对象!");
}
// getter and setter omitted
Spring配置:
北京
上海
广州
010
020
023
测试类:
// 启动容器
ApplicationContext ac = new ClassPathXmlApplicationContext(
new String[]{"com/wtu/spring/di/set/spring3.0.xml"}
);
Customer customer = (Customer) ac.getBean("customer");
System.out.println(customer.getId() + "--" + customer.getName());
System.out.println(customer.getBirthday().toLocaleString());
for (String addr : customer.getAddress()) {
System.out.println(addr);
}
for (String phone : customer.getPhone()) {
System.out.println(phone);
}
for (Map.Entry entry : customer.getAdd_pho().entrySet()) {
System.out.println(entry.getKey() + "--" + entry.getValue());
}
为里面元素为自定义类型的集合DI:
实体类:
public class Addr {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Phone {
private String number;
public void setNumber(String number) {
this.number = number;
}
public String getNumber() {
return number;
}
}
public class Customer {
private List address;
private Set phone;
private Map add_pho;
public Customer() {
System.out.println("IOC 创建对象!");
}
// getter and setter omitted
}
Spring配置:
测试类:
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.Map;
/**
* @Author menglanyingfei
* @Created on 2018.01.16 16:21
*/
public class TestSpringIoc {
public static void main(String[] args) {
// 启动容器
ApplicationContext ac = new ClassPathXmlApplicationContext(
new String[]{"com/wtu/spring/di/set2/spring3.0.xml"}
);
Customer customer = (Customer) ac.getBean("customer");
for (Addr addr : customer.getAddress()) {
System.out.println(addr.getName());
}
for (Phone phone : customer.getPhone()) {
System.out.println(phone.getNumber());
}
for (Map.Entry e : customer.getAdd_pho().entrySet()) {
System.out.println(e.getKey() + "-->" + e.getValue());
}
}
}
运行结果:
至于DI(依赖注入)的构造器注入, 我留到AOP下一节内容总结!
敬请期待!
所有完整示例代码见Github
https://github.com/menglanyingfei/SSMLearning/tree/master/spring_day01