一、传统方式创建对象

缺点:

  • 耦合太高:比如UserService的类有了新的实现,那么就需要修改所有调用UserService的地方

示例:

  1. package com.wells.demo.ioc.tradition;
  2. /**
  3. * Description 传统方式: 通过 new 对象来调用,带来的问题:
  4. * 1、耦合高:如果userDao有了新的实现,需要修改所有调用userDao的地方;
  5. * 因此,为了降低耦合,所以需要工厂模式来做,而Spring恰恰用了:xml配置(注解)、工厂模式、反射来进行bean的创建
  6. * Created by wells on 2020-07-18 08:26:41
  7. */
  8. public class UserService {
  9. public void delUser() {
  10. System.out.println("delete user");
  11. }
  12. public static void main(String[] args) {
  13. UserService userService = new UserService();
  14. userService.delUser();
  15. }
  16. }

二、基于XML方式创建对象

  • 在 spring 配置文件中,使用 bean 标签创建对象;标签里面添加对应属性,就可以实现属性注入
  • 在 bean 标签有很多属性,介绍常用的属性
    • id 属性:唯一标识
    • class 属性:类全路径(包类路径)
  • 创建对象时候,默认执行无参数构造方法完成创建对象

示例:

  1. package com.wells.demo.ioc.xml;
  2. import org.springframework.context.ApplicationContext;
  3. import org.springframework.context.support.ClassPathXmlApplicationContext;
  4. /**
  5. * Description 通过ApplicationContext读取xml配置文件创建对象
  6. * Created by wells on 2020-07-18 08:26:41
  7. */
  8. public class UserService {
  9. public void addUser() {
  10. System.out.println("add user");
  11. }
  12. public static void main(String[] args) {
  13. ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
  14. UserService userService = applicationContext.getBean("userService", UserService.class);
  15. userService.addUser();
  16. }
  17. }
  1. <!-- 使用xml方式创建对象 -->
  2. <bean id="userService" class="com.wells.demo.ioc.xml.UserService"/>

三、基于 XML 方式注入基础属性

DI:依赖注入,就是注入属性

2.1、使用set方式注入属性

  1. package com.wells.demo.ioc.xml;
  2. import org.junit.Test;
  3. import org.springframework.context.ApplicationContext;
  4. import org.springframework.context.support.ClassPathXmlApplicationContext;
  5. /**
  6. * Description 通过set方法注入属性
  7. * Created by wells on 2020-07-18 22:08:35
  8. */
  9. public class Book {
  10. private String bname;
  11. private String bauthor;
  12. public void setBname(String bname) {
  13. this.bname = bname;
  14. }
  15. public void setBauthor(String bauthor) {
  16. this.bauthor = bauthor;
  17. }
  18. @Override
  19. public String toString() {
  20. return "Book{" +
  21. "bname='" + bname + '\'' +
  22. ", bauthor='" + bauthor + '\'' +
  23. '}';
  24. }
  25. /**
  26. * 通过set方法注入属性
  27. */
  28. @Test
  29. public void testWiredPropertyBySetter() {
  30. ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
  31. Book book = applicationContext.getBean("book", Book.class);
  32. System.out.println(book.toString());
  33. }
  34. }
  1. <!-- 使用set方式注入属性 -->
  2. <bean id="book" class="com.wells.demo.ioc.xml.Book">
  3. <!-- 使用 property 完成属性注入
  4. name:类里面属性名称
  5. value:向属性注入的值
  6. -->
  7. <property name="bname" value="西游记"/>
  8. <property name="bauthor" value="吴承恩"/>
  9. </bean>

2.2、使用有参构造方法注入属性

  1. package com.wells.demo.ioc.xml;
  2. import org.junit.Test;
  3. import org.springframework.context.ApplicationContext;
  4. import org.springframework.context.support.ClassPathXmlApplicationContext;
  5. /**
  6. * Description 使用有参构造方法注入属性
  7. * Created by wells on 2020-07-18 22:14:14
  8. */
  9. public class User {
  10. private String name;
  11. private Integer age;
  12. public User(String name, Integer age) {
  13. this.name = name;
  14. this.age = age;
  15. }
  16. @Override
  17. public String toString() {
  18. return "User{" +
  19. "name='" + name + '\'' +
  20. ", age=" + age +
  21. '}';
  22. }
  23. /**
  24. * 通过有参构造方法注入属性
  25. */
  26. @Test
  27. public void testWiredPropertyByConstructor() {
  28. ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
  29. User user = applicationContext.getBean("user", User.class);
  30. System.out.println(user.toString());
  31. }
  32. }
  1. <!-- 使用有参构造方法注入属性 -->
  2. <bean id="user" class="com.wells.demo.ioc.xml.User">
  3. <constructor-arg name="name" value="wells"/>
  4. <constructor-arg name="age" value="18"/>
  5. </bean>

2.3、使用 p 名称空间注入(了解)

  1. package com.wells.demo.ioc.xml;
  2. import org.junit.Test;
  3. import org.springframework.context.ApplicationContext;
  4. import org.springframework.context.support.ClassPathXmlApplicationContext;
  5. /**
  6. * Description 通过P名称空间注入属性
  7. * Created by wells on 2020-07-18 22:31:53
  8. */
  9. public class Person {
  10. private String name;
  11. private Integer age;
  12. public void setName(String name) {
  13. this.name = name;
  14. }
  15. public void setAge(Integer age) {
  16. this.age = age;
  17. }
  18. @Override
  19. public String toString() {
  20. return "Person{" +
  21. "name='" + name + '\'' +
  22. ", age=" + age +
  23. '}';
  24. }
  25. @Test
  26. public void testWiredPropertyByPTag() {
  27. ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
  28. Person person = applicationContext.getBean("person", Person.class);
  29. System.out.println(person.toString());
  30. }
  31. }
  1. <!-- 通过p名称空间注入属性 -->
  2. <bean id="person" class="com.wells.demo.ioc.xml.Person" p:name="Tom" p:age="18"/>

四、基于XML注入其他类型属性

4.1、注入 Null 值

  1. <!-- 使用set方式注入属性 -->
  2. <bean id="book" class="com.wells.demo.ioc.xml.Book">
  3. <!-- 使用 property 完成属性注入
  4. name:类里面属性名称
  5. value:向属性注入的值
  6. -->
  7. <property name="bname" value="西游记"/>
  8. <property name="bauthor" value="吴承恩"/>
  9. <!-- 注入Null值 -->
  10. <property name="addr">
  11. <null/>
  12. </property>
  13. </bean>

4.2、属性值包含特殊符号

例如: <、>

  1. <!-- 使用set方式注入属性 -->
  2. <bean id="book" class="com.wells.demo.ioc.xml.Book">
  3. <!-- 使用 property 完成属性注入
  4. name: 类里面属性名称
  5. value: 向属性注入的值
  6. -->
  7. <property name="bname" value="西游记"/>
  8. <property name="bauthor" value="吴承恩"/>
  9. <!-- 注入 Null 值 -->
  10. <property name="addr">
  11. <null/>
  12. </property>
  13. <!-- 注入特殊符号值 -->
  14. <property name="btime">
  15. <value><![CDATA[<2020-07-18 22:50:00>]]></value>
  16. </property>
  17. </bean>

4.3、注入自定义bean

4.3.1、外部创建方式

  • 创建两个类:service 类和 dao 类
  • 在 service 调用 dao 里面的方法
  • 在 spring 配置文件中进行配置
  1. <!-- 通过set方法注入自定义bean: 外部创建 -->
  2. <bean id="empOut" class="com.wells.demo.ioc.xml.Emp">
  3. <property name="ename" value="Jerry"/>
  4. <property name="gender" value="男"/>
  5. <property name="dept" ref="deptOut"/>
  6. </bean>
  7. <bean id="deptOut" class="com.wells.demo.ioc.xml.Dept">
  8. <property name="dname" value="技术部"/>
  9. </bean>

4.3.2、内部创建方式

部门和员工关系

  1. <!-- 通过set方法注入自定义bean: 内部创建 -->
  2. <bean id="empIn" class="com.wells.demo.ioc.xml.Emp">
  3. <property name="ename" value="Lily"/>
  4. <property name="gender" value="女"/>
  5. <property name="dept">
  6. <bean class="com.wells.demo.ioc.xml.Dept">
  7. <property name="dname" value="测试部"/>
  8. </bean>
  9. </property>
  10. </bean>

4.5、注入自定义bean-级联属性

4.5.1、外部注入

  1. <!-- 在注入自定义bean时,注入级联属性,比如:dept的dname -->
  2. <bean id="empLinkOne" class="com.wells.demo.ioc.xml.Emp">
  3. <property name="ename" value="Tom"/>
  4. <property name="gender" value="男"/>
  5. <property name="dept" ref="dept"/>
  6. </bean>
  7. <bean name="dept" class="com.wells.demo.ioc.xml.Dept">
  8. <property name="dname" value="安全部"/>
  9. </bean>

4.5.2、内部注入

  1. <!-- 在注入自定义bean时,注入级联属性,比如:dept的dname -->
  2. <bean id="empLinkTwo" class="com.wells.demo.ioc.xml.Emp">
  3. <property name="ename" value="Daivd"/>
  4. <property name="gender" value="男"/>
  5. <property name="dept" ref="dept"/>
  6. <!-- 需要dept属性的get方法 -->
  7. <property name="dept.dname" value="业务部"/>
  8. </bean>

4.6、集合属性注入

  • 注入数组类型属性
  • 注入List集合类型属性:基本类型、自定义类型、抽取
  • 注入Map集合类型属性
  1. <!-- 注入集合属性 -->
  2. <bean id="student" class="com.wells.demo.ioc.xml.Student">
  3. <property name="course">
  4. <array>
  5. <value>线性代数</value>
  6. <value>高等数学</value>
  7. </array>
  8. </property>
  9. <!-- 注入基本属性集合 -->
  10. <property name="books">
  11. <list>
  12. <value>《SpringBoot编程思想》</value>
  13. <value>《SpringCloud微服务实战》</value>
  14. </list>
  15. </property>
  16. <property name="teachers">
  17. <map>
  18. <entry key="大学物理" value="Tom"/>
  19. <entry key="高等数学" value="Jerry"/>
  20. </map>
  21. </property>
  22. <!-- 注入自定义bean集合 -->
  23. <property name="courseList">
  24. <list>
  25. <ref bean="c1"/>
  26. <ref bean="c2"/>
  27. </list>
  28. </property>
  29. <property name="friends" ref="friends"/>
  30. </bean>
  31. <bean id="c1" class="com.wells.demo.ioc.xml.Course" p:cname="大学物理"/>
  32. <bean id="c2" class="com.wells.demo.ioc.xml.Course" p:cname="编译原理"/>
  33. <!-- 提取list -->
  34. <util:list id="friends">
  35. <value>Lily</value>
  36. <value>Wells</value>
  37. </util:list>

五、FactoryBean

Spring 有两种类型 bean,一种普通 bean,另外一种工厂 bean(FactoryBean)

  • 普通 bean:在配置文件中定义 bean 类型就是返回类型
  • 工厂 bean:在配置文件定义 bean 类型可以和返回类型不一样
    • 第一步 创建类,让这个类作为工厂 bean,实现接口 FactoryBean
    • 第二步 实现接口里面的方法,在实现的方法中定义返回的 bean 类型
  1. package com.wells.demo.ioc.xml;
  2. import org.junit.Test;
  3. import org.springframework.beans.factory.FactoryBean;
  4. import org.springframework.context.ApplicationContext;
  5. import org.springframework.context.support.ClassPathXmlApplicationContext;
  6. /**
  7. * Description FactoryBean
  8. * Created by wells on 2020-07-19 09:21:56
  9. */
  10. public class MyBean implements FactoryBean<Course> {
  11. public Course getObject() throws Exception {
  12. Course course = new Course();
  13. course.setCname("大学英语");
  14. return course;
  15. }
  16. public Class<?> getObjectType() {
  17. return null;
  18. }
  19. public boolean isSingleton() {
  20. return false;
  21. }
  22. @Test
  23. public void testFactoryBean(){
  24. ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
  25. Course course = applicationContext.getBean("myBean", Course.class);
  26. System.out.println(course.toString());
  27. }
  28. }
  1. <!-- FactoryBean -->
  2. <bean id="myBean" class="com.wells.demo.ioc.xml.MyBean"/

六、Bean实例数

在 Spring 里面,默认情况下,bean 是单实例对象

如何设置单实例还是多实例?

  • 在 spring 配置文件 bean 标签里面有属性(scope)用于设置单实例还是多实例
  • scope 属性值
    • 第一个值 singleton,默认值,表示是单实例对象
    • 第二个值 prototype,表示是多实例对象

【Spring5】第二篇:IOC之XML方式 - 图1

singleton 和 prototype 区别

  • 第一 singleton单实例,prototype多实例
  • 第二 设置 scope 值是 singleton 时候,加载 spring 配置文件时候就会创建单实例对象;设置 scope 值是 prototype 时候,不是在加载 spring 配置文件时候创建对象,而是在调用 getBean 方法时候创建多实例对象;

七、Bean生命周期

生命周期:从对象创建到对象销毁的过程

7.1、无处理器的bean生命周期(五步)

  • 通过构造器创建 bean 实例(无参数构造)
  • 为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)
  • 调用 bean 的初始化的方法(需要进行配置初始化的方法)
  • bean 可以使用了(对象获取到了)
  • 当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)
  1. package com.wells.demo.ioc.xml;
  2. import org.junit.Test;
  3. import org.springframework.context.support.ClassPathXmlApplicationContext;
  4. /**
  5. * Description Bean实例生命周期
  6. * Created by wells on 2020-07-19 09:30:56
  7. */
  8. public class Order {
  9. private String oname;
  10. public Order() {
  11. System.out.println("第一步: 通过无参构造方法创建bean实例");
  12. }
  13. public void setOname(String oname) {
  14. this.oname = oname;
  15. System.out.println("第二步: 通过set方法注入属性");
  16. }
  17. /**
  18. * Bean初始化方法
  19. */
  20. public void initMethod() {
  21. System.out.println("第三步: 调用bean初始化方法");
  22. }
  23. /**
  24. * Bean销毁方法
  25. */
  26. public void destroyMethod() {
  27. System.out.println("第五步: 调用bean销毁方法");
  28. }
  29. @Override
  30. public String toString() {
  31. return "Order{" +
  32. "oname='" + oname + '\'' +
  33. '}';
  34. }
  35. @Test
  36. public void testBeanLife() {
  37. ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
  38. Order order = applicationContext.getBean("order", Order.class);
  39. System.out.println("第四步: 使用bean");
  40. System.out.println(order.toString());
  41. // 手动调用销毁bean实例
  42. applicationContext.close();
  43. }
  44. }
  1. <!-- bean生命周期 -->
  2. <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,会被执行多次

  1. package com.wells.demo.ioc.xml;
  2. import org.springframework.beans.BeansException;
  3. import org.springframework.beans.factory.config.BeanPostProcessor;
  4. /**
  5. * Description Bean处理器,需要特别注意的是:这个处理器是对所有的bean生效;所以会出现有几个bean,就执行几次
  6. * Created by wells on 2020-07-19 09:58:12
  7. */
  8. public class BeanProcess implements BeanPostProcessor {
  9. /**
  10. * 处理器前置方法
  11. */
  12. public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
  13. System.out.println("第处理器前置方法");
  14. return bean;
  15. }
  16. /**
  17. * 处理器后缀方法
  18. */
  19. public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
  20. System.out.println("处理器后置方法");
  21. return bean;
  22. }
  23. }

八、XML自动装配

什么是自动装配?

  • 根据指定装配规则(属性名称或者属性类型),Spring 自动将匹配的属性值进行注入

8.1、根据属性名称自动注入

需要注意的是:类中的属性名,需要与xml中的id名相同

  1. public class Emp {
  2. // ... 略
  3. private Dept dept;
  4. public void setDept(Dept dept) {
  5. this.dept = dept;
  6. }
  7. // ... 略
  8. @Test
  9. public void testAutoWiredByNameInXML(){
  10. ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans-auto-wired.xml");
  11. Emp emp = applicationContext.getBean("empByName", Emp.class);
  12. System.out.println(emp.toString());
  13. }
  14. }
  1. <bean id="empByName" class="com.wells.demo.ioc.xml.Emp" autowire="byName"/>
  2. <bean id="dept" class="com.wells.demo.ioc.xml.Dept">
  3. <property name="dname" value="运维部"/>

8.2、根据属性类型自动注入

  1. public class Emp {
  2. // ... 略
  3. private Dept dept;
  4. public void setDept(Dept dept) {
  5. this.dept = dept;
  6. }
  7. // ... 略
  8. @Test
  9. public void testAutoWiredByTypeInXML(){
  10. ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans-auto-wired.xml");
  11. Emp emp = applicationContext.getBean("empByType", Emp.class);
  12. System.out.println(emp.toString());
  13. }
  14. }
  1. <bean id="empByType" class="com.wells.demo.ioc.xml.Emp" autowire="byType"/>
  2. <bean id="dept2" class="com.wells.demo.ioc.xml.Dept">
  3. <property name="dname" value="运维部"/>
  4. </bean>

需要注意的是:xml中不能出现多个类型一样的bean实例,否则会报错,如下图:
【Spring5】第二篇:IOC之XML方式 - 图2

九、引入外部属性

  1. package com.wells.demo.ioc.xml;
  2. import org.junit.Test;
  3. import org.springframework.context.ApplicationContext;
  4. import org.springframework.context.support.ClassPathXmlApplicationContext;
  5. /**
  6. * Description
  7. * Created by wells on 2020-07-19 10:19:12
  8. */
  9. public class Blog {
  10. private String bname;
  11. public void setBname(String bname) {
  12. this.bname = bname;
  13. }
  14. @Override
  15. public String toString() {
  16. return "Blog{" +
  17. "bname='" + bname + '\'' +
  18. '}';
  19. }
  20. @Test
  21. public void testImportPorp() {
  22. ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans-import-prop.xml");
  23. Blog blog = applicationContext.getBean("blog", Blog.class);
  24. System.out.println(blog.toString());
  25. }
  26. }

blog.properties文件内容:

  1. 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>

代码示例

Spring IOC XML