一、传统方式创建对象
缺点:
- 耦合太高:比如UserService的类有了新的实现,那么就需要修改所有调用UserService的地方
示例:
package com.wells.demo.ioc.tradition;/*** Description 传统方式: 通过 new 对象来调用,带来的问题:* 1、耦合高:如果userDao有了新的实现,需要修改所有调用userDao的地方;* 因此,为了降低耦合,所以需要工厂模式来做,而Spring恰恰用了:xml配置(注解)、工厂模式、反射来进行bean的创建* Created by wells on 2020-07-18 08:26:41*/public class UserService {public void delUser() {System.out.println("delete user");}public static void main(String[] args) {UserService userService = new UserService();userService.delUser();}}
二、基于XML方式创建对象
- 在 spring 配置文件中,使用 bean 标签创建对象;标签里面添加对应属性,就可以实现属性注入
- 在 bean 标签有很多属性,介绍常用的属性
- id 属性:唯一标识
- class 属性:类全路径(包类路径)
- 创建对象时候,默认执行无参数构造方法完成创建对象
示例:
package com.wells.demo.ioc.xml;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;/*** Description 通过ApplicationContext读取xml配置文件创建对象* Created by wells on 2020-07-18 08:26:41*/public class UserService {public void addUser() {System.out.println("add user");}public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");UserService userService = applicationContext.getBean("userService", UserService.class);userService.addUser();}}
<!-- 使用xml方式创建对象 --><bean id="userService" class="com.wells.demo.ioc.xml.UserService"/>
三、基于 XML 方式注入基础属性
DI:依赖注入,就是注入属性
2.1、使用set方式注入属性
package com.wells.demo.ioc.xml;import org.junit.Test;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;/*** Description 通过set方法注入属性* Created by wells on 2020-07-18 22:08:35*/public class Book {private String bname;private String bauthor;public void setBname(String bname) {this.bname = bname;}public void setBauthor(String bauthor) {this.bauthor = bauthor;}@Overridepublic String toString() {return "Book{" +"bname='" + bname + '\'' +", bauthor='" + bauthor + '\'' +'}';}/*** 通过set方法注入属性*/@Testpublic void testWiredPropertyBySetter() {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");Book book = applicationContext.getBean("book", Book.class);System.out.println(book.toString());}}
<!-- 使用set方式注入属性 --><bean id="book" class="com.wells.demo.ioc.xml.Book"><!-- 使用 property 完成属性注入name:类里面属性名称value:向属性注入的值--><property name="bname" value="西游记"/><property name="bauthor" value="吴承恩"/></bean>
2.2、使用有参构造方法注入属性
package com.wells.demo.ioc.xml;import org.junit.Test;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;/*** Description 使用有参构造方法注入属性* Created by wells on 2020-07-18 22:14:14*/public class User {private String name;private Integer age;public User(String name, Integer age) {this.name = name;this.age = age;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +'}';}/*** 通过有参构造方法注入属性*/@Testpublic void testWiredPropertyByConstructor() {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");User user = applicationContext.getBean("user", User.class);System.out.println(user.toString());}}
<!-- 使用有参构造方法注入属性 --><bean id="user" class="com.wells.demo.ioc.xml.User"><constructor-arg name="name" value="wells"/><constructor-arg name="age" value="18"/></bean>
2.3、使用 p 名称空间注入(了解)
package com.wells.demo.ioc.xml;import org.junit.Test;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;/*** Description 通过P名称空间注入属性* Created by wells on 2020-07-18 22:31:53*/public class Person {private String name;private Integer age;public void setName(String name) {this.name = name;}public void setAge(Integer age) {this.age = age;}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +'}';}@Testpublic void testWiredPropertyByPTag() {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");Person person = applicationContext.getBean("person", Person.class);System.out.println(person.toString());}}
<!-- 通过p名称空间注入属性 --><bean id="person" class="com.wells.demo.ioc.xml.Person" p:name="Tom" p:age="18"/>
四、基于XML注入其他类型属性
4.1、注入 Null 值
<!-- 使用set方式注入属性 --><bean id="book" class="com.wells.demo.ioc.xml.Book"><!-- 使用 property 完成属性注入name:类里面属性名称value:向属性注入的值--><property name="bname" value="西游记"/><property name="bauthor" value="吴承恩"/><!-- 注入Null值 --><property name="addr"><null/></property></bean>
4.2、属性值包含特殊符号
例如: <、>
<!-- 使用set方式注入属性 --><bean id="book" class="com.wells.demo.ioc.xml.Book"><!-- 使用 property 完成属性注入name: 类里面属性名称value: 向属性注入的值--><property name="bname" value="西游记"/><property name="bauthor" value="吴承恩"/><!-- 注入 Null 值 --><property name="addr"><null/></property><!-- 注入特殊符号值 --><property name="btime"><value><![CDATA[<2020-07-18 22:50:00>]]></value></property></bean>
4.3、注入自定义bean
4.3.1、外部创建方式
- 创建两个类:service 类和 dao 类
- 在 service 调用 dao 里面的方法
- 在 spring 配置文件中进行配置
<!-- 通过set方法注入自定义bean: 外部创建 --><bean id="empOut" class="com.wells.demo.ioc.xml.Emp"><property name="ename" value="Jerry"/><property name="gender" value="男"/><property name="dept" ref="deptOut"/></bean><bean id="deptOut" class="com.wells.demo.ioc.xml.Dept"><property name="dname" value="技术部"/></bean>
4.3.2、内部创建方式
部门和员工关系
<!-- 通过set方法注入自定义bean: 内部创建 --><bean id="empIn" class="com.wells.demo.ioc.xml.Emp"><property name="ename" value="Lily"/><property name="gender" value="女"/><property name="dept"><bean class="com.wells.demo.ioc.xml.Dept"><property name="dname" value="测试部"/></bean></property></bean>
4.5、注入自定义bean-级联属性
4.5.1、外部注入
<!-- 在注入自定义bean时,注入级联属性,比如:dept的dname --><bean id="empLinkOne" class="com.wells.demo.ioc.xml.Emp"><property name="ename" value="Tom"/><property name="gender" value="男"/><property name="dept" ref="dept"/></bean><bean name="dept" class="com.wells.demo.ioc.xml.Dept"><property name="dname" value="安全部"/></bean>
4.5.2、内部注入
<!-- 在注入自定义bean时,注入级联属性,比如:dept的dname --><bean id="empLinkTwo" class="com.wells.demo.ioc.xml.Emp"><property name="ename" value="Daivd"/><property name="gender" value="男"/><property name="dept" ref="dept"/><!-- 需要dept属性的get方法 --><property name="dept.dname" value="业务部"/></bean>
4.6、集合属性注入
- 注入数组类型属性
- 注入List集合类型属性:基本类型、自定义类型、抽取
- 注入Map集合类型属性
<!-- 注入集合属性 --><bean id="student" class="com.wells.demo.ioc.xml.Student"><property name="course"><array><value>线性代数</value><value>高等数学</value></array></property><!-- 注入基本属性集合 --><property name="books"><list><value>《SpringBoot编程思想》</value><value>《SpringCloud微服务实战》</value></list></property><property name="teachers"><map><entry key="大学物理" value="Tom"/><entry key="高等数学" value="Jerry"/></map></property><!-- 注入自定义bean集合 --><property name="courseList"><list><ref bean="c1"/><ref bean="c2"/></list></property><property name="friends" ref="friends"/></bean><bean id="c1" class="com.wells.demo.ioc.xml.Course" p:cname="大学物理"/><bean id="c2" class="com.wells.demo.ioc.xml.Course" p:cname="编译原理"/><!-- 提取list --><util:list id="friends"><value>Lily</value><value>Wells</value></util:list>
五、FactoryBean
Spring 有两种类型 bean,一种普通 bean,另外一种工厂 bean(FactoryBean)
- 普通 bean:在配置文件中定义 bean 类型就是返回类型
- 工厂 bean:在配置文件定义 bean 类型可以和返回类型不一样
- 第一步 创建类,让这个类作为工厂 bean,实现接口 FactoryBean
- 第二步 实现接口里面的方法,在实现的方法中定义返回的 bean 类型
package com.wells.demo.ioc.xml;import org.junit.Test;import org.springframework.beans.factory.FactoryBean;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;/*** Description FactoryBean* Created by wells on 2020-07-19 09:21:56*/public class MyBean implements FactoryBean<Course> {public Course getObject() throws Exception {Course course = new Course();course.setCname("大学英语");return course;}public Class<?> getObjectType() {return null;}public boolean isSingleton() {return false;}@Testpublic void testFactoryBean(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");Course course = applicationContext.getBean("myBean", Course.class);System.out.println(course.toString());}}
<!-- FactoryBean --><bean id="myBean" class="com.wells.demo.ioc.xml.MyBean"/
六、Bean实例数
在 Spring 里面,默认情况下,bean 是单实例对象
如何设置单实例还是多实例?
- 在 spring 配置文件 bean 标签里面有属性(scope)用于设置单实例还是多实例
- scope 属性值
- 第一个值 singleton,默认值,表示是单实例对象
- 第二个值 prototype,表示是多实例对象

singleton 和 prototype 区别
- 第一 singleton单实例,prototype多实例
- 第二 设置 scope 值是 singleton 时候,加载 spring 配置文件时候就会创建单实例对象;设置 scope 值是 prototype 时候,不是在加载 spring 配置文件时候创建对象,而是在调用 getBean 方法时候创建多实例对象;
七、Bean生命周期
生命周期:从对象创建到对象销毁的过程
7.1、无处理器的bean生命周期(五步)
- 通过构造器创建 bean 实例(无参数构造)
- 为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)
- 调用 bean 的初始化的方法(需要进行配置初始化的方法)
- bean 可以使用了(对象获取到了)
- 当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)
package com.wells.demo.ioc.xml;import org.junit.Test;import org.springframework.context.support.ClassPathXmlApplicationContext;/*** Description Bean实例生命周期* Created by wells on 2020-07-19 09:30:56*/public class Order {private String oname;public Order() {System.out.println("第一步: 通过无参构造方法创建bean实例");}public void setOname(String oname) {this.oname = oname;System.out.println("第二步: 通过set方法注入属性");}/*** Bean初始化方法*/public void initMethod() {System.out.println("第三步: 调用bean初始化方法");}/*** Bean销毁方法*/public void destroyMethod() {System.out.println("第五步: 调用bean销毁方法");}@Overridepublic String toString() {return "Order{" +"oname='" + oname + '\'' +'}';}@Testpublic void testBeanLife() {ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");Order order = applicationContext.getBean("order", Order.class);System.out.println("第四步: 使用bean");System.out.println(order.toString());// 手动调用销毁bean实例applicationContext.close();}}
<!-- bean生命周期 --><bean id="order" class="com.wells.demo.ioc.xml.Order" init-method="initMethod" destroy-method="destroyMethod" p:oname="裤子"/>
7.2、有处理器的bean生命周期(七步)
- 通过构造器创建 bean 实例(无参数构造)
- 为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)
- 把 bean 实例传递到 bean 后置处理器的方法 postProcessBeforeInitialization
- 调用 bean 的初始化的方法(需要进行配置初始化的方法)
- ==把 bean 实例传递到 bean 后置处理器的方法 postProcessAfterInitialization ==
- bean 可以使用了(对象获取到了)
- 当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)
需要特别注意的是:processor是对所有的bean生效,因此如果创建多个bean,会被执行多次
package com.wells.demo.ioc.xml;import org.springframework.beans.BeansException;import org.springframework.beans.factory.config.BeanPostProcessor;/*** Description Bean处理器,需要特别注意的是:这个处理器是对所有的bean生效;所以会出现有几个bean,就执行几次* Created by wells on 2020-07-19 09:58:12*/public class BeanProcess implements BeanPostProcessor {/*** 处理器前置方法*/public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("第处理器前置方法");return bean;}/*** 处理器后缀方法*/public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("处理器后置方法");return bean;}}
八、XML自动装配
什么是自动装配?
- 根据指定装配规则(属性名称或者属性类型),Spring 自动将匹配的属性值进行注入
8.1、根据属性名称自动注入
需要注意的是:类中的属性名,需要与xml中的id名相同
public class Emp {// ... 略private Dept dept;public void setDept(Dept dept) {this.dept = dept;}// ... 略@Testpublic void testAutoWiredByNameInXML(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans-auto-wired.xml");Emp emp = applicationContext.getBean("empByName", Emp.class);System.out.println(emp.toString());}}
<bean id="empByName" class="com.wells.demo.ioc.xml.Emp" autowire="byName"/><bean id="dept" class="com.wells.demo.ioc.xml.Dept"><property name="dname" value="运维部"/>
8.2、根据属性类型自动注入
public class Emp {// ... 略private Dept dept;public void setDept(Dept dept) {this.dept = dept;}// ... 略@Testpublic void testAutoWiredByTypeInXML(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans-auto-wired.xml");Emp emp = applicationContext.getBean("empByType", Emp.class);System.out.println(emp.toString());}}
<bean id="empByType" class="com.wells.demo.ioc.xml.Emp" autowire="byType"/><bean id="dept2" class="com.wells.demo.ioc.xml.Dept"><property name="dname" value="运维部"/></bean>
需要注意的是:xml中不能出现多个类型一样的bean实例,否则会报错,如下图:
九、引入外部属性
package com.wells.demo.ioc.xml;import org.junit.Test;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;/*** Description* Created by wells on 2020-07-19 10:19:12*/public class Blog {private String bname;public void setBname(String bname) {this.bname = bname;}@Overridepublic String toString() {return "Blog{" +"bname='" + bname + '\'' +'}';}@Testpublic void testImportPorp() {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans-import-prop.xml");Blog blog = applicationContext.getBean("blog", Blog.class);System.out.println(blog.toString());}}
blog.properties文件内容:
blog.bname=wells
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 引入外部属性文件 -->
<context:property-placeholder location="blog.properties"/>
<bean id="blog" class="com.wells.demo.ioc.xml.Blog">
<property name="bname" value="${blog.bname}"/>
</bean>
</beans>
