今天来总结Spring容器的Ioc(控制反转)和DI(依赖注入)!
Ioc.jpeg
Spring的核心思想,ioc 和 di
Ioc:ioc强调由第三方容器根据客户的需求创建对象,然后根据客户提供的方法将对象传递给客户。
Di: 强调第三方容器创建对象以后, 通过什么方法将对象传递过去.
理解示例图:
Ioc&DI.png
模拟实现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的配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<bean id="boyId" class="com.wtu.spring.base.Boy">
<property name="id" value="007"/>
<property name="name" value="狗娃"/>
<property name="salary" value="7000"/>
</bean>
<bean id="girlId" class="com.wtu.spring.base.Girl">
<property name="id" value="008"/>
<property name="name" value="翠花"/>
<property name="salary" value="6000"/>
</bean>
</root>
然后, 采用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<Element> 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);
}
}
运行结果:
运行结果.png
Ioc容器的两个实现类
FileSystemXmlApplicationContext
ClassPathXmpApplicationContext
在web项目中能不能使用FileSystemXmlApplicationContext
为什么?
答:当项目部署服务器以后, src目录已经不存在了, 该目录下的配置文件全部到了classes目录下面, 这个目录是一个类路径.
Spring配置文件
<?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-3.0.xsd">
<bean id="boyId" class="com.wtu.spring.ioc.type1.Boy">
<property name="id" value="001"></property>
<property name="name" value="狗剩"></property>
<property name="salary" value="8000"></property>
</bean>
<!-- 在一个xml文件中, id不能有相同的 -->
<bean id="girlId" class="com.wtu.spring.ioc.type1.Girl">
<property name="id" value="002"></property>
<property name="name" value="翠花"></property>
<property name="salary" value="7500"></property>
</bean>
</beans>
//启动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集合
map.png
Spring中bean的一些操作
1. bean的创建
a) 通过无参构造方法创建对象
public Boy() {
System.out.println("spring容器创建对象");
}
<bean id="boyId" class="com.wtu.spring.bean.create.Boy">
<property name="id" value="001"></property>
<property name="name" value="李四"></property>
<property name="salary" value="3000.0"></property>
</bean>
// 启动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);
无参构造方法.png
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("带参");
}
}
<!-- 注册UserDaoImpl对象
通过中间类的静态方法来创建对象
factory-method: 通过中间类的哪个方法创建对象 -->
<bean id="userDaoId" class="com.wtu.spring.bean.create2.Middle" factory-method="getInstance">
</bean>
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("带参");
}
}
<!--
通过中间类的非静态方法来创建对象:
factory-bean: 引用中间类的对象
factory-method: 中间类创建UserDaoImpl对象的方法
-->
<!--注册中间类对象 -->
<bean id="middleId" class="com.wtu.spring.bean.create3.Middle">
</bean>
<bean id="userDaoId" factory-bean="middleId"
factory-method="getInstance" />
2. bean的继承
Spring配置:
<!-- bean的继承: 一个bean可以继承另一个bean的property属性
parent: 父bean的id
-->
<bean id="fatherId" class="com.wtu.spring.bean.extends_.Father">
<property name="id" value="001"/>
<property name="name" value="曹总"/>
<property name="salary" value="1000000"/>
</bean>
<bean id="sonId" class="com.wtu.spring.bean.extends_.Son" parent="fatherId">
<property name="id" value="002"/>
<property name="name" value="曹丕"/>
</bean>
测试:
//启动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的生命周期
<!-- 配置bean的生命周期方法
init-method: 初始化的方法
destroy-method: 销毁的方法
-->
<bean id="iUserDao" class="com.wtu.spring.bean.life.UserDaoImpl"
init-method="myInit" destroy-method="myDestroy"/>
测试类:
//启动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的单例和多例
<!--
bean的scope属性: 默认是singleton, 单例模式, 那么启动容器时, 会创建对象
scope: prototype, 多例模式, 那么启动容器时, 不创建对象
注意: 以后遇到Spring框架和Struts2框架的整合, 那么需要对Struct2的Action对象进行配置
一定要让scope="prototype"
-->
<bean id="userDaoImpl" class="com.wtu.spring.bean.scope.UserDaoImpl"
scope="prototype"/>
测试类
//启动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);
结果.png
5. bean的创建时间
<!--
lazy-init: default 启动容器是 单例, 创建对象; 多例, 不创建对象.
true: 懒, 不管是单例还是多例, 都不创建对象
false: 不懒, 启动容器是很勤快,
如果是单例就创建对象, 如果是多例, 也不创建对象
因为不知道需要多少个对象.
-->
<bean id="IUserDao" class="com.wtu.spring.bean.time.UserDaoImpl"
lazy-init="true"/>
Spring的DI
依赖注入第一种方式: 将bean中的属性通过set方式赋值给bean对象.
1. 注入简单类型(基本数据类型+String)
image.png2. 注入类类型(以Date类型为例)
<!-- 注册一个Date对象 -->
<bean id="dateId" class="java.util.Date"/>
<bean id="customer" class="com.wtu.spring.di.set.Customer">
<property name="birthday" ref="dateId"/>
</bean>
3.集合类型<里面的元素是基本类型>
集合类型1.png4. 集合类型<里面的元素是自定义类型>
image.pngimage.png
完整代码:
实体类:
public class Customer {
private Integer id;
private String name;
private String sex;
private Integer age;
private Date birthday;
private List<String> address;
private Set<String> phone;
private Map<String, String> add_pho;
public Customer() {
System.out.println("IOC 创建对象!");
}
// getter and setter omitted
Spring配置:
<!-- 注册一个Date对象 -->
<bean id="dateId" class="java.util.Date"/>
<bean id="customer" class="com.wtu.spring.di.set.Customer">
<property name="id" value="1"/>
<property name="name" value="李四"/>
<property name="sex" value="女"/>
<property name="age" value="18"/>
<property name="birthday" ref="dateId"/>
<property name="address">
<list>
<value>北京</value>
<value>上海</value>
<value>广州</value>
</list>
</property>
<property name="phone">
<set>
<value>010</value>
<value>020</value>
<value>023</value>
</set>
</property>
<property name="add_pho">
<map>
<entry key="北京">
<value>020</value>
</entry>
<entry key="上海">
<value>010</value>
</entry>
<entry key="广州">
<value>030</value>
</entry>
</map>
</property>
</bean>
测试类:
// 启动容器
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());
}
结果.png
为里面元素为自定义类型的集合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<Addr> address;
private Set<Phone> phone;
private Map<Addr, Phone> add_pho;
public Customer() {
System.out.println("IOC 创建对象!");
}
// getter and setter omitted
}
Spring配置:
<!-- 注册Addr对象和Phone对象 -->
<bean id="addr1" class="com.wtu.spring.di.set2.Addr">
<property name="name" value="北京"/>
</bean>
<bean id="addr2" class="com.wtu.spring.di.set2.Addr">
<property name="name" value="上海"/>
</bean>
<bean id="addr3" class="com.wtu.spring.di.set2.Addr">
<property name="name" value="广州"/>
</bean>
<bean id="phone1" class="com.wtu.spring.di.set2.Phone">
<property name="number" value="010"/>
</bean>
<bean id="phone2" class="com.wtu.spring.di.set2.Phone">
<property name="number" value="020"/>
</bean>
<bean id="phone3" class="com.wtu.spring.di.set2.Phone">
<property name="number" value="023"/>
</bean>
<!-- 注册Customer对象 -->
<bean id="customer" class="com.wtu.spring.di.set2.Customer">
<property name="address">
<list>
<ref bean="addr1"/>
<ref bean="addr2"/>
<ref bean="addr3"/>
</list>
</property>
<property name="phone">
<set>
<ref bean="phone1"/>
<ref bean="phone2"/>
<ref bean="phone3"/>
</set>
</property>
<property name="add_pho">
<map>
<entry key-ref="addr1">
<ref bean="phone1"/>
</entry>
<entry key-ref="addr2">
<ref bean="phone2"/>
</entry>
<entry key-ref="addr3">
<ref bean="phone3"/>
</entry>
</map>
</property>
</bean>
测试类:
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<Addr, Phone> e : customer.getAdd_pho().entrySet()) {
System.out.println(e.getKey() + "-->" + e.getValue());
}
}
}
运行结果:
image.png
至于DI(依赖注入)的构造器注入, 我留到AOP下一节内容总结!
敬请期待!
所有完整示例代码见Github
https://github.com/menglanyingfei/SSMLearning/tree/master/spring_day01
网友评论