Spring5之IOC(含底层原理)

Spring5之IOC

Spring 概述

  1. Spring 是轻量级且开源的 JavaEE 框架。
  2. Spring 可以解决企业应用开发的复杂性。
  3. Spring 有两个核心部分:IOC 和 AOP
    • IOC:控制反转,把创建对象过程交给 Spring 管理
    • AOP:面向切面,不修改源代码进行功能增强
    • 注意:这两个只是核心部分,而Spring还有其它的部分!

官网https://spring.io/

Spring 特点

  1. 方便解耦,简化开发
  2. AOP 编程的支持
  3. 方便程序的测试
  4. 方便集成各种优秀框架(比如:Mybatis)
  5. 方便进行事务操作
  6. 降低 API 开发

入门案例

一、下载 Spring5

打开 https://spring.io/projects/spring-framework#learn 然后点击右上角的 GitHub,这里最新的稳定版本为:Spring5.3.13

在这里插入图片描述

进入 GitHub 后,找到并点击 Access to Binaries 中的链接

在这里插入图片描述

进入下载页面

在这里插入图片描述

找到对应目录下的 Spring 目录,然后点击打开对应的下载页面

在这里插入图片描述

下载地址https://repo.spring.io/ui/native/release/org/springframework/spring

在这里插入图片描述

在这里插入图片描述

二、创建一个java工程

  • 创建 java 工程很简单,这里就直接省略了。。。

在这里插入图片描述

三、导入 Spring5 的相关 jar 包

在这里插入图片描述

将以下五个包(logging是日志包,在网上下一下就好了)导入到刚才创建好的 java 工程中

在这里插入图片描述

其它几个包都在 libs 目录中(这个目录在刚才下载的 Spring 中)

创建lib文件夹,然后把包丢进去

在这里插入图片描述

最后引入jar包就好了

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

四、编写代码进行测试

创建普通类和一个普通方法

package com.spring.pojo;

public class User {
    public void add() {
        System.out.println("add......");
    }
}

创建 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.xsd">

    <!--
        配置 User 对象创建
        id:给对象取的名称
        class:指定对象
     -->
    <bean id="user" class="com.spring.test.User" />
</beans>

说明:Spring 配置文件使用 xml 格式,文件名没什么要求,但是实际开发中还是建议取个比较能看懂的名字

创建测试类,编写测试方法

package com.spring.test;

import com.spring.pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class UserTest {
    @Test
    public void testAdd() {
        // 1.加载 Spring 的配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");

        // 2.获取配置文件中创建的对象
        User user = context.getBean("user", User.class);

        System.out.println(user);	// 打印地址值
        user.add();		// 打印 add.....
    }
}

目录结构

在这里插入图片描述

IOC 容器

什么是 IOC?

1、IOC:控制反转,把对象创建和对象之间的调用过程,交给 Spring 进行管理

2、使用 IOC 的目的:为了降低耦合度

3、上面的入门案例就是 IOC 实现

底层原理

使用的技术

1、xml 解析

2、工厂模式

3、反射

原始调用对象的缺点

在这里插入图片描述

在原始方式中,我们如果需要调用某个类中的方法或者变量,就需要先 new 对象,然后在通过这个对象去调用所需要的值。

为什么会导致耦合度较高呢?

根据上图来说,UserService 和 UserDao 是进行的直接引用,如果 UserDao 改变了存放的位置,UserService 就获取不到了。

工厂模式的解耦过程

在这里插入图片描述

解析:

在工厂模式中创建 UserDao 对象,然后 UserService 中使用工厂模式获取到 UserDao 对象。

在这个过程中 UserService 就不会直接去 new UserDao 了,从而就能达到一定的解耦效果(注意:虽然有一定的效果,但是还是存在一定的耦合度的,而我们的目的是要将耦合度降到最低)

IOC 过程

  • 第一步:xml 配置文件,配置需要创建的对象
  • 第二步:创建工厂类,通过反射创建对象

在这里插入图片描述

此时如果 UserDao 改变了存放路径,那么我们只需要更改 xml 文件中的对象路径即可。

IOC(接口)

  • IOC 思想基于 IOC 容器完成,IOC 容器底层就是对象工厂。

  • Spring 提供了 IOC 容器实现的两种方式(两个接口):BeanFactotyApplicationContext

  • BeanFactory:是 IOC 容器中最基本的实现方式,该接口是 Spring 内部的使用接口,不提供给开发人员进行使用(但也可以用)

    特点:加载配置文件的时候不会创建对象,在获取对象(使用对象)的时候才去创建对象。

  • ApplicationContext:是 BeanFactory 接口的子接口,提供更多更强大的功能,一般是提供给开发人员进行使用

    特点:加载配置文件的时候把在配置文件中的对象进行创建。

ApplicationContext 接口中的实现类说明

ClassPathXmlApplicationContext 实现类:

  1. 根据类路径(相对路径)获取对应的对象

FileSystemXmlApplicationContext 实现类:

  1. 根据磁盘路径(绝对路径)获取对应的对象

IOC 容器之Bean 管理

什么是Bean管理?

  • Bean 管理指的是两个操作:Spring 创建对象 和 Spring 注入属性

Bean 管理是比较官方的说法,可以简单理解为上面的两个操作。

Bean 管理的两种操作方式

  • 基于 xml 配置文件的方式实现
  • 基于注解的方式实现

IOC 基于 xml 配置文件操作 Bean 管理

一、基于 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">

    <!--配置 User 对象创建-->
    <bean id="user" class="com.spring.pojo.User" />
</beans>

说明:

  1. 在 Spring 配置文件中,使用 bean 标签,标签里面添加对应属性,就可以实现对象创建

  2. 在 bean 标签中有很多属性,常用的属性说明:

    id 属性:唯一标识,可以理解为是一个不能重复的对象别名

    class 属性:类的全路径

    name:和 id 的作用差不多,但是 id 中的值不能添加一些符号,而 name 值中可以(比如 斜杆(/)之类的),但是 name 并不常用

  3. 创建对象的时候,默认也是执行无参构造方法(大家可以测试一下,在User类中创建一个有参构成方法,然后再去执行测试方法,就会报:NoSuchMethodException)

二、基于 xml 方式注入属性

  • DI:依赖注入,说白了也就是注入属性
第一种注入方式:使用 set 方法

原始写法

package com.spring.pojo;

public class Book {
    private String bname;

    public void setBname(String bname) {
        this.bname = bname;
    }
    
    public static void main(String[] args) {
        Book book = new Book();
        book.setBname("小白");
    }
}

Spring 写法

  • 创建类,并定于属性和对应的 set 方法
package com.spring.pojo;

/**
 * @ClassName Book
 * @Description: 使用 set 方法注入属性
 * @Author Laoyang
 * @Date 2021/12/16 12:32
 */
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;
    }

    public void demo() {
        System.out.println(bname + "---" + bauthor);
    }
}
  • 在 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.xsd">
    <!-- 1. 配置对象创建 -->
    <bean id="book" class="com.spring.pojo.Book">
        <!--
            2. set 方法注入属性(使用 property 标签实现)
            name属性:类中的属性名称
            value属性:向属性中注入的值
        -->
        <property name="bname" value="little Prince" />
        <property name="bauthor" value="Anthony" />
    </bean>
</beans>
  • 进行测试
package com.spring.test;

import com.spring.pojo.Book;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class BookTest {
    @Test
    public void testDemo() {
        // 加载 Spring 配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");

        // 获取配置文件中创建的对象
        Book book = context.getBean("book", Book.class);

        System.out.println(book);   // 地址值
        book.demo();       // little Prince---Anthony
    }
}
第二种注入方式:使用有参构造器

原始写法

package com.spring.pojo;

public class Book {
    private String bname;

    public Book(String bname) {
        this.bname = bname;
    }

    public static void main(String[] args) {
        Book book = new Book("小白");
    }
}

Spring 写法

  • 创建类,定义属性,并创建对应的有参构造器
package com.spring.pojo;

/**
 * @ClassName Order
 * @Description: 使用有参构造注入属性
 * @Author Laoyang
 * @Date 2021/12/16 15:34
 */
public class Order {
    private String oname;

    private String address;

    // 有参构造器
    public Order(String oname, String address) {
        this.oname = oname;
        this.address = address;
    }
}
  • 在 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.xsd">
    <!-- 1. 配置创建对象 -->
    <bean id="order" class="com.spring.pojo.Order">
        <!--
            2. 有参构造实现属性注入(使用 constructor-arg 标签实现)
            name属性:类中的属性名称
            value属性:向属性中注入的值
        -->
        <constructor-arg name="oname" value="Facial mask" />
        <constructor-arg name="address" value="China" />

        <!-- 通过参数索引值进行注入(不推荐)
        <constructor-arg index="0" value="Facial mask" />
        <constructor-arg index="1" value="China" />
        -->
    </bean>
</beans>
  • 测试
package com.spring.test;

import com.spring.pojo.Order;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class OrderTest {
    @Test
    public void testOrderDemo() {
        // 加载 Spring 配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");

        // 获取配置中创建的对象
        Order order = context.getBean("order", Order.class);

        System.out.println(order);  // 地址值
        order.orderDemo();      // Facial mask---China
    }
}
第三种注入方式:p名称空间注入(了解)
  • 使用 p 名称空间注入,可以简化基于 xml 配置方式(简化上面的 set 注入方式)
  • 这种方式用的并不多,所以大家了解一下就好了

实现步骤:

  1. 添加 p 名称空间的在配置文件中xmlns:p="http://www.springframework.org/schema/p"

    <?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:p="http://www.springframework.org/schema/p"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    </beans>
    
  2. 进行属性注入,在 bean 标签中进行操作

    <?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:p="http://www.springframework.org/schema/p"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    	<!-- set 方法注入属性(使用 property 标签实现)-->
        <bean id="book" class="com.spring.pojo.Book" p:bname="神雕侠侣" p:bauthor="金庸"/>
    </beans>
    
  3. 测试

    package com.spring.test;
    
    import com.spring.pojo.Book;
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class BookTest {
        @Test
        public void testDemo() {
            // 加载 Spring 配置文件
            ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
    
            // 获取配置文件中创建的对象
            Book book = context.getBean("book", Book.class);
    
            System.out.println(book);   // 地址值
            book.demo();       // 神雕侠侣---金庸
        }
    }
    

三、注入属性 - 字面量

注入空值

原始写法

package com.spring.pojo;

public class Book {
    // 空值
    private String bname = null;

    public getBname(String bname) {
        this.bname = bname;
    }

    public static void main(String[] args) {
        Book book = new Book();
        System.out.println(book.getBname());
    }
}

Spring 写法

  • 在 Book 类中添加 address 属性,并修改 demo 方法
package com.spring.pojo;

public class Book {
    private String bname;

    private String bauthor;

    private String address;

    public void setBname(String bname) {
        this.bname = bname;
    }

    public void setBauthor(String bauthor) {
        this.bauthor = bauthor;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public void demo() {
        System.out.println(bname + "---" + bauthor + "---" + address);
    }
}
  • 在 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.xsd">

    <!-- 创建对象 -->
    <bean id="book" class="com.spring.pojo.Book">
        <!--
            set 方法注入属性(使用 property 标签实现)
            name属性:类中的属性名称
            value属性:向属性中注入的值
        -->
        <property name="bname" value="little Prince"/>
        <property name="bauthor" value="Anthony"/>

        <!-- 在 Book 类中添加 address 字段,然后注入 null 值 -->
        <property name="address">
            <null/>
        </property>
    </bean>
</beans>
  • 测试
package com.spring.test;

import com.spring.pojo.Book;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class BookTest {
    @Test
    public void testDemo() {
        // 加载 Spring 配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");

        // 获取配置文件中创建的对象
        Book book = context.getBean("book", Book.class);

        System.out.println(book);   // 地址值
        book.demo();       // 神雕侠侣---金庸---null
    }
}
注入特殊字符

原始写法

package com.spring.pojo;

public class Book {
    // 特殊符号
    private String character = "<>";
    
     public getCharacter(String character) {
        this.character = character;
    }

    public static void main(String[] args) {
        Book book = new Book();
        System.out.println(book.getCharacter());
    }
}

Spring 写法

  • 在 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.xsd">
    
    <bean id="book" class="com.spring.pojo.Book">
        <property name="bname" value="little Prince"/>
        <property name="bauthor" value="Anthony"/>

        <!--属性值中包含特殊字符-->
<!--        <property name="address" value="<<北上广深>>"/>-->

        <!-- 方式一:把 <> 进行转义(大于:&gt; 小于:&lt;) -->
<!--        <property name="address" value="&lt;&lt;北上广深&gt;&gt;"/>-->

        <!-- 方式二:把带特殊符号的内容写到 CDATA -->
        <property name="address">
            <value><![CDATA[<<北上广深>>]]></value>
        </property>
    </bean>
</beans>
  • 测试
package com.spring.test;

import com.spring.pojo.Book;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class BookTest {
    @Test
    public void testDemo() {
        // 加载 Spring 配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");

        // 获取配置文件中创建的对象
        Book book = context.getBean("book", Book.class);

        System.out.println(book);   // 地址值
        book.demo();       // 神雕侠侣---金庸---<<北上广深>>
    }
}

四、注入属性 - 外部 bean

原始写法

  • 创建 UserService、UserDao(接口)、UserDaoImpl
package com.spring.dao;

public interface UserDao {
    public void update();
}
package com.spring.dao;

public class UserDaoImpl implements UserDao {
    @Override
    public void update() {
        System.out.println("dao update.....");
    }
}
  • 在UserService中调用 UserDao 的方法
package com.spring.service;

import com.spring.dao.UserDao;
import com.spring.dao.UserDaoImpl;

public class UserService {
    public void add() {
        System.out.println("service add......");

        // 原始方式:创建 UserDao 对象进行调用
        UserDao dao = new UserDaoImpl();
        dao.update();
    }
}

Spring 写法

  • 在 UserService 类中添加 userDao 属性
package com.spring.service;

import com.spring.dao.UserDao;
import com.spring.dao.UserDaoImpl;

public class UserService {
    // 创建 UserDao 类型属性,生成 set 方法
    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void add() {
        System.out.println("service add......");

        // 原始方式:创建 UserDao 对象
//        UserDao dao = new UserDaoImpl();
//        dao.update();

        // Spring方式:在配置文件中注入对象然后进行调用
        userDao.update();
    }
}
  • 在 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.xsd">

    <!-- 创建 service 和 dao 的对象 -->
    <bean id="userService" class="com.spring.service.UserService">
        <!--
            注入 userDao 对象
            name:类中的属性名称(这里指的是 UserService 中的属性名称)
            ref:创建 userDao 对象 bean 标签 id 值
         -->
        <property name="userDao" ref="userDaoImpl" />
    </bean>
    <bean id="userDaoImpl" class="com.spring.dao.UserDaoImpl" />
</beans>
  • 测试
package com.spring.test;

import com.spring.pojo.User;
import com.spring.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class UserTest {
    @Test
    public void testServiceAdd() {
        // 1.加载 Spring 的配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("bean5.xml");

        // 2.获取配置文件中创建的对象
        UserService userService = context.getBean("userService", UserService.class);

        System.out.println(userService);
        userService.add();
        /*
        打印结果为:
            地址值
            service add......
            dao update.....
         */
    }
}

五、注入属性 - 内部 bean

1、一对多:部门和员工(部门是一,员工是多)

2、在实体类中表示部门和员工的关系(因为没使用数据库,所以员工使用对象类型来表示所属部门)

package com.spring.bean;

/**
 * @ClassName Dept
 * @Description: 部门
 * @Author Laoyang
 * @Date 2021/12/17 10:43
 */
public class Dept {
    private String dname;

    public void setDname(String dname) {
        this.dname = dname;
    }

    @Override
    public String toString() {
        return "Dept{" +
                "dname='" + dname + '\'' +
                '}';
    }
}
package com.spring.bean;

/**
 * @ClassName Emp
 * @Description: 员工
 * @Author Laoyang
 * @Date 2021/12/17 10:44
 */
public class Emp {
    private String ename;

    private String gender;

    // 员工属于某一个部门,使用对象形式表示
    private Dept dept;

    public void setEname(String ename) {
        this.ename = ename;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public void setDept(Dept dept) {
        this.dept = dept;
    }

    public void add() {
        System.out.println(ename + "---" + gender + "---" + dept);
    }
}

3、在 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.xsd">

    <!-- 内部 Bean -->
    <bean id="emp" class="com.spring.bean.Emp">
        <!-- 先设置两个普通的属性 -->
        <property name="ename" value="小白"/>
        <property name="gender" value="" />

        <!-- 设置对象类型的属性 -->
        <!-- 内部注入 -->
        <property name="dept">
            <bean id="dept" class="com.spring.bean.Dept">
                <property name="dname" value="研发部" />
            </bean>
        </property>
    </bean>
</beans>

4、测试

package com.spring.test;

import com.spring.bean.Emp;
import com.spring.pojo.Order;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class EmpTest {
    @Test
    public void testAdd() {
        // 加载 Spring 配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("bean6.xml");

        // 获取配置中创建的对象
        Emp emp = context.getBean("emp", Emp.class);

        System.out.println(emp);    // 地址值
        emp.add();      // 小白---男---Dept{dname='研发部'}
    }
}

六、注入属性 - 级联赋值

第一种写法
  • 在 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.xsd">

    <!-- 级联赋值 -->
    <bean id="emp" class="com.spring.bean.Emp">
        <!-- 先设置两个普通的属性 -->
        <property name="ename" value="小白"/>
        <property name="gender" value="" />

        <!-- 级联赋值 -->
        <property name="dept" ref="dept" />
    </bean>
    <bean id="dept" class="com.spring.bean.Dept">
        <property name="dname" value="人事部" />
    </bean>
</beans>
  • 测试
package com.spring.test;

import com.spring.bean.Emp;
import com.spring.pojo.Order;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class EmpTest {
    @Test
    public void testAdd2() {
        // 加载 Spring 配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("bean7.xml");

        // 获取配置中创建的对象
        Emp emp = context.getBean("emp", Emp.class);

        System.out.println(emp);    // 地址值
        emp.add();      // 小白---男---Dept{dname='人事部'}
    }
}
第二种写法
  • 在 emp 类中设置 dept 的 get 方法
package com.spring.bean;

/**
 * @ClassName Emp
 * @Description: 员工
 * @Author Laoyang
 * @Date 2021/12/17 10:44
 */
public class Emp {
    private String ename;

    private String gender;

    // 员工属于某一个部门,使用对象形式表示
    private Dept dept;

    public void setEname(String ename) {
        this.ename = ename;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    // 生成 dept 的 get 方法
    public Dept getDept() {
        return dept;
    }

    public void setDept(Dept dept) {
        this.dept = dept;
    }

    public void add() {
        System.out.println(ename + "---" + gender + "---" + dept);
    }
}
  • 在 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.xsd">

    <!-- 级联赋值 -->
    <bean id="emp" class="com.spring.bean.Emp">
        <!-- 先设置两个普通的属性 -->
        <property name="ename" value="小白"/>
        <property name="gender" value="" />

        <!-- 级联赋值 -->
		<!-- 注意:这种写法必须要有对象属性的 get 方法,否则就获取不到 -->
        <property name="dept.dname" value="财务部" />
    </bean>
</beans>

七、注入集合属性

步骤
  1. 创建实体类(在里面编写集合类型的属性和set方法)
  2. 在 Spring 配置文件编写相关配置,注入相关属性
  3. 测试

相关类型:数组、List、Map、Set

具体实现

1、创建实体类(在里面编写集合类型的属性和set方法)

package com.laoyang.spring.collectiontype;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class Stu {
    /**
     * 创建一个数组类型的属性
     */
    private String[] courses;

    /**
     * 创建一个List集合类型的属性
     */
    private List<String> list;

    /**
     * 创建一个Map集合类型的属性
     */
    private Map<String, String> maps;

    /**
     * 创建一个Set集合类型的属性
     */
    private Set<String> set;

    public void setCourses(String[] courses) {
        this.courses = courses;
    }

    public void setList(List<String> list) {
        this.list = list;
    }

    public void setMaps(Map<String, String> maps) {
        this.maps = maps;
    }

    public void setSet(Set<String> set) {
        this.set = set;
    }

    /**
     * 测试方法
     */
    public void add() {
        System.out.println(Arrays.toString(courses));
        System.out.println(list);
        System.out.println(maps);
        System.out.println(set);
    }
}

2、在 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.xsd">

    <!-- 集合类型属性注入 -->
    <bean id="stu" class="com.laoyang.spring.collectiontype.Stu" >
        <!-- 注入数组类型的属性 -->
        <property name="courses">
            <array>
                <value>java</value>
                <value>mysql</value>
                <!-- ..... -->
            </array>
        </property>

        <!-- 注入List集合类型的属性 -->
        <property name="list">
            <list>
                <value>小李</value>
                <value>小王</value>
                <!-- ..... -->
            </list>
        </property>

        <!-- 注入Map集合类型的属性 -->
        <property name="maps">
            <map>
                <entry key="世界第一" value="java" />
                <entry key="世界第二" value="c++" />
                <!-- ..... -->
            </map>
        </property>

        <!-- 注入Set集合类型的属性 -->
        <property name="set">
            <set>
                <value>嘿嘿嘿</value>
                <value>哈哈哈</value>
                <!-- ..... -->
            </set>
        </property>
    </bean>
</beans>

3、测试

package com.laoyang.spring.test;

import com.laoyang.spring.collectiontype.Stu;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class StuTest {
    @Test
    public void testAdd() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");

        Stu stu = context.getBean("stu", Stu.class);

        System.out.println(stu);
        stu.add();
        /*
          打印结果为:
            地址值
            [java, mysql]
            [小李, 小王]
            {世界第一=java, 世界第二=c++}
            [嘿嘿嘿, 哈哈哈]
         */
    }
}
思考问题
  1. 上面的案例中,都是插入的 String 类型的数据,如果我想要插入一个对象类型,该如何实现呢?
  2. 上面的案例中,所有的集合属性都放在了 Stu 对象中,这样的话就不能给其它对象进行使用了,那么我们怎么把集合数据变为公共的呢?
解决问题
一、在集合里面设置对象类型的值
  • 创建课程类,并在 Stu 类中创建课程集合属性
package com.laoyang.spring.collectiontype;

/**
 * @ClassName Course
 * @Description: 课程类
 * @Author Laoyang
 * @Date 2021/12/17 11:54
 */
public class Course {
    /**
     * 课程名称
     */
    private String cname;

    public void setCname(String cname) {
        this.cname = cname;
    }

    @Override
    public String toString() {
        return "Course{" +
                "cname='" + cname + '\'' +
                '}';
    }
}
package com.laoyang.spring.collectiontype;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class Stu {
    // ... 此处省略前面的代码
    
    /**
     * 课程集合属性
     */
    private List<Course> courseList;
    
    public void setCourseList(List<Course> courseList) {
        this.courseList = courseList;
    }
    
    public void show() {
        System.out.println(courseList);
    }
}
  • 在 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.xsd">

    <!-- 集合类型属性注入 -->
    <bean id="stu" class="com.laoyang.spring.collectiontype.Stu" >
        <!-- ...此处省略前面的代码 -->

        <!-- 注入课程集合类型的属性 -->
        <property name="courseList">
            <list>
                <ref bean="course1" />
                <ref bean="course2" />
            </list>
        </property>
    </bean>

    <bean id="course1" class="com.laoyang.spring.collectiontype.Course">
        <property name="cname" value="Spring5课程" />
    </bean>
    <bean id="course2" class="com.laoyang.spring.collectiontype.Course">
        <property name="cname" value="SpringBoot课程" />
    </bean>
    <!-- ...... -->
</beans>
  • 测试
package com.laoyang.spring.test;

import com.laoyang.spring.collectiontype.Stu;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class StuTest {
    @Test
    public void testShow() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");

        Stu stu = context.getBean("stu", Stu.class);

        System.out.println(stu);
        stu.show();     // [Course{cname='Spring5课程'}, Course{cname='SpringBoot课程'}]
    }
}
二、把集合提取出来,当做公共数据
  • 创建Book类,用来测试效果
package com.laoyang.spring.collectiontype;

import java.util.List;

public class Book {
    private List<String> list;

    public void setList(List<String> list) {
        this.list = list;
    }

    public void show() {
        System.out.println(list);
    }
}
  • 在 Spring 配置文件中引入 util 名称空间
在xml文件头部声明中添加如下配置(具体配置位置,可看后面的代码):
xmlns:util="http://www.springframework.org/schema/util"
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
  • 使用 util 标签提取并使用 List 集合类型的属性
<?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:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
>

    <!-- 提取List集合类型的属性注入 -->
    <util:list id="bookList">
        <value>第一</value>
        <value>第二</value>
        <value>第三</value>
    </util:list>

    <!-- 提取出来的 List 集合该如何使用? -->
    <bean id="book" class="com.laoyang.spring.collectiontype.Book">
        <property name="list" ref="bookList" />
    </bean>
</beans>
  • 测试
package com.laoyang.spring.test;

import com.laoyang.spring.collectiontype.Book;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class BookTest {
    @Test
    public void testShow() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
        Book book = context.getBean("book", Book.class);

        System.out.println(book);
        book.show(); // [第一, 第二, 第三]
    }
}

工厂 Bean(FactoryBean)

说明

  • Spring 有两种 Bean,一种是普通 Bean,另一种就是工厂 Bean(FactoryBean)
  • 普通 Bean:在配置文件中定义的 bean 类型就是返回类型。
  • 工厂 Bean:在配置文件中定义的 bean 类型可以和返回类型不一样。

代码演示

  • 第一步:创建一个类,让这个类作为工厂 Bean,实现 FactoryBean 接口
  • 第二步:实现接口里面的方法,在实现的方法中定义返回的 Bean 类型
package com.laoyang.spring.factorybean;

import com.laoyang.spring.collectiontype.Course;
import org.springframework.beans.factory.FactoryBean;

/**
 * 第一步:创建一个类,让这个类作为工厂 Bean,实现 FactoryBean 接口
 * 第二步:实现接口里面的方法,在实现的方法中定义返回的 Bean 类型
 */
public class MyBean implements FactoryBean<Course> {
    /**
     * 定义返回的 bean 对象
     */
    @Override
    public Course getObject() throws Exception {
        Course course = new Course();
        course.setCname("abc");
        return course;
    }

    @Override
    public Class<?> getObjectType() {
        return null;
    }
}

配置文件

<?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">

    <bean id="myBean" class="com.laoyang.spring.factorybean.MyBean" />
</beans>

测试

package com.laoyang.spring.test;

import com.laoyang.spring.collectiontype.Book;
import com.laoyang.spring.collectiontype.Course;
import com.laoyang.spring.factorybean.MyBean;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyBeanTest {
    @Test
    public void testShow() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");

        // 未定义工厂 bean 时会返回 MyBean 的对象
//        MyBean myBean = context.getBean("myBean", MyBean.class);
//        System.out.println(myBean);

        // 定义工厂 bean 后会返回工厂 bean 所返回的类型对象
        Course myBean = context.getBean("myBean", Course.class);
        System.out.println(myBean);
    }
}

大家可以看到,配置文件中创建的是MyBean对象,而这里使用的是 Course 对象,因为工厂类中返回的就是Course 对象,如果这里还是使用 MyBean 就会报 BeanNotOfRequiredTypeException 异常
如果大家有兴趣尝试,可以先不让 MyBaen 类实现 FactoryBean 接口,然后测试查看效果

Bean 的作用域

  • Bean 的作用域:在 Spring 当中设置创建 bean 的实例是单实例还是多实例
  • 在 Spring 当中,默认情况下,创建的 bean 是单实例对象

验证当前是不是单实例

  • 在测试方法中调用两次,然后进行打印
  • 如果是单实例,此时打印的地址值应该是一样的
  • 如果是多实例,此时打印的地址值应该是不一样的
package com.laoyang.spring.test;

import com.laoyang.spring.collectiontype.Book;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class BookTest {
    @Test
    public void testShow2() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");

        Book book1 = context.getBean("book", Book.class);
        Book book2 = context.getBean("book", Book.class);

        /*
         如果是单实例,此时打印的地址值应该是一样的
         如果是多实例,此时打印的地址值应该是不一样的
         */
        System.out.println(book1);
        System.out.println(book2);
    }
}

设置多实例

  • 在 Spring 配置文件 bean 标签中有属性(scope)用于设置单实例还是多实例

  • scope 属性值:

    singleton:默认值,表示是单实例对象

    prototype:表示是多实例对象

  • singleton 和 prototype 的区别:

    1、singleton 表示单实例,prototype 表示多实例。

    2、如果 scope 设置为 singleton,那么加载配置文件的时候就会创建单实例对象;设置为 prototype 的时候,不是加载 Spring 配置文件的时候创建的对象,而是在调用 getBean 方法时才会创建多实例对象。

修改 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"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
>

    <!-- 提取List集合类型的属性注入 -->
    <util:list id="bookList">
        <value>第一</value>
        <value>第二</value>
        <value>第三</value>
    </util:list>

    <!-- 将Book对象设置为多实例对象 -->
    <bean id="book" class="com.laoyang.spring.collectiontype.Book" scope="prototype">
        <property name="list" ref="bookList" />
    </bean>
</beans>

测试

package com.laoyang.spring.test;

import com.laoyang.spring.collectiontype.Book;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class BookTest {
    @Test
    public void testShow2() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");

        Book book1 = context.getBean("book", Book.class);
        Book book2 = context.getBean("book", Book.class);

        /*
         如果是单实例,此时打印的地址值应该是一样的
         如果是多实例,此时打印的地址值应该是不一样的
         */
        System.out.println(book1);
        System.out.println(book2);
    }
}

Bean 的生命周期

  • 生命周期:从对象创建到对象销毁的过程就被称为生命周期。

Bean 生命周期的过程

  1. 通过构造器创建 bean 实例(无参构造器)
  2. 为 bean 的属性设置值和对其它 bean 的引用(调用 set 方法)
  3. 调用 bean 的初始化方法(需要进行配置初始化的方法)
  4. 使用 bean (对象获取到了)
  5. 当容器关闭的时候,调用 bean 的销毁方法(需要进行配置销毁的方法)

演示 Bean 的生命周期

  • 创建一个类,用来配合演示
package com.laoyang.spring.bean;

public class Orders {
    private String oname;

    public Orders() {
        System.out.println("第一步:执行无参构造器创建 bean 实例");
    }

    public void setOname(String oname) {
        this.oname = oname;
        System.out.println("第二步:执行set方法,设置属性值");
    }

    public void initMethod() {
        System.out.println("第三步:执行初始化方法");
    }

    public void destroyMethod() {
        System.out.println("第五步:执行销毁的方法");
    }
}
  • 在 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.xsd">

    <!--
        init-method:执行初始化方法
        destroy-method:执行销毁方法
     -->
    <bean id="orders" class="com.laoyang.spring.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">
        <property name="oname" value="电脑" />
    </bean>
</beans>
  • 使用获取的 bean 对象,使用完后进行销毁
package com.laoyang.spring.test;

import com.laoyang.spring.bean.Orders;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class OrdersTest {
    @Test
    public void testInitMethod() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
        Orders orders = context.getBean("orders", Orders.class);

        System.out.println("第四步:获取创建的对象");
        System.out.println(orders);

        /*
         销毁方法并不会自动调用,所以需要我们手动让 bean 实例销毁
         因为 ApplicationContext 接口不能直接调用 close(),所以可以强转为它的实现类然后在进行调用
         因为在配置文件中进行了相关配置,所以调用 close() 销毁的时候就会自动去调用 destroyMethod() 方法
         */
        ((ClassPathXmlApplicationContext) context).close();

        /*
        最后的打印结果:
            第一步:执行无参构造器创建 bean 实例
            第二步:执行set方法,设置属性值
            第三步:执行初始化方法
            第四步:获取创建的对象
            com.laoyang.spring.bean.Orders@569cfc36
            第五步:执行销毁的方法
         */
    }
}

Bean 的后置处理器

  • 加上 Bean 的后置处理器之后,Spring 的生命周期会变为 7 步。

Bean 生命周期的过程

  1. 通过构造器创建 bean 实例(无参构造器)
  2. 为 bean 的属性设置值和对其它 bean 的引用(调用 set 方法)
  3. 把 bean 的实例传递给 bean 的后置处理器的方法(postProcessBeforeInitialization()
  4. 调用 bean 的初始化方法(需要进行配置初始化的方法)
  5. 把 bean 的实例传递给 bean 的后置处理器的方法(postProcessAfterInitialization()
  6. 使用 bean (对象获取到了)
  7. 当容器关闭的时候,调用 bean 的销毁方法(需要进行配置销毁的方法)

演示添加后置处理器的效果

  • 创建类,实现 BeanPostProcessor 接口,创建后置处理器
package com.laoyang.spring.bean;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.lang.Nullable;

public class MyBeanPost implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("在初始化之前执行的方法");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("在初始化之后执行的方法");
        return bean;
    }
}
  • 在配置文件中配置后置处理器
<?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">

    <!--
        init-method:执行初始化方法
        destroy-method:执行销毁方法
     -->
    <bean id="orders" class="com.laoyang.spring.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">
        <property name="oname" value="电脑" />
    </bean>

    <!-- 配置后置处理器-->
    <bean id="myBeanPost" class="com.laoyang.spring.bean.MyBeanPost" />
</beans>
  • 测试效果
package com.laoyang.spring.test;

import com.laoyang.spring.bean.Orders;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class OrdersTest {
    @Test
    public void testInitMethod() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
        Orders orders = context.getBean("orders", Orders.class);

        System.out.println("第四步:获取创建的对象");
        System.out.println(orders);

        /*
         销毁方法并不会自动调用,所以需要我们手动让 bean 实例销毁
         因为 ApplicationContext 接口不能直接调用 close(),所以可以强转为它的实现类然后在进行调用
         */
        ((ClassPathXmlApplicationContext) context).close();

        /*
        最后的打印结果:
            第一步:执行无参构造器创建 bean 实例
            第二步:执行set方法,设置属性值
            在初始化之前执行的方法
            第三步:执行初始化方法
            在初始化之后执行的方法
            第四步:获取创建的对象
            com.laoyang.spring.bean.Orders@64f6106c
            第五步:执行销毁的方法
         */
    }
}

自动装配

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

演示自动装配的过程

  • 创建两个类,用来配合测试
package com.laoyang.spring.autowire;

public class Dept {
    @Override
    public String toString() {
        return "Dept{}";
    }
}
package com.laoyang.spring.autowire;

public class Emp {
    private Dept dept;

    public void setDept(Dept dept) {
        this.dept = dept;
    }

    @Override
    public String toString() {
        return "Emp{" +
                "dept=" + dept +
                '}';
    }
}
  • 在 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.xsd">

    <!--
        实现自动装配(使用 bean 标签中的 autowire 属性实现)
        autowire有两个常用值:
        byName:根据属性名称进行注入,注入的值(bean 的 id)和类属性的名称要一样
        byType:根据属性类型进行注入
     -->
<!--    <bean id="emp" class="com.laoyang.spring.autowire.Emp" autowire="byName" />-->
    <bean id="emp" class="com.laoyang.spring.autowire.Emp" autowire="byType" />

    <!-- 手动装配 -->
<!--    <bean id="emp" class="com.laoyang.spring.autowire.Emp">-->
<!--        <property name="dept" ref="dept" />-->
<!--    </bean>-->

    <bean id="dept" class="com.laoyang.spring.autowire.Dept" />
</beans>
  • 测试
package com.laoyang.spring.test;

import com.laoyang.spring.autowire.Emp;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class EmpTest {
    @Test
    public void test() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean5.xml");
        Emp emp = context.getBean("emp", Emp.class);
        System.out.println(emp);
    }
}

引入外部属性文件

引入相关 jar 包

在这里插入图片描述

直接配置数据库信息(配置德鲁伊连接池)

  • 直接在 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.xsd">

    <!-- 直接配置连接池 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://localhost:3306/userDb" />
        <property name="username" value="root" />
        <property name="password" value="123456" />
    </bean>
</beans>

引入外部属性文件配置数据库连接池

  • 创建 .properties 文件,配置相关属性
prop.driverClassName=com.mysql.jdbc.Driver
prop.url=jdbc:mysql://localhost:3306/userDb
prop.username=root
prop.password=123456
  • 在 Spring 配置文件头部声明中引入 context 名称空间
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
  • 在 Spring 配置文件中引入 properties 属性文件
<?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="classpath:jdbc.properties"/>

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${prop.driverClassName}"/>
        <property name="url" value="${prop.url}"/>
        <property name="username" value="${prop.username}"/>
        <property name="password" value="${prop.password}"/>
    </bean>
</beans>

IOC 基于注解操作 Bean 管理

注解复习

  • 注解:注解是代码中的特殊标记,格式:@注解名称(属性名称 = 属性值, 属性名称 = 属性值......)
  • 注解可以使用在 类上、方法上、属性上
  • 这里使用注解的主要目的:简化 xml 配置

注意:前两点说的是 JavaSE 中的注解,相当于是给大家巩固一下知识点。

Spring 相关注解

以下是 Spring 针对 Bean 管理中创建对象而提供的相关注解:

  • @Component
  • @Service
  • @Controller
  • @Repository

上面的四个注解功能是一样的,都可以用来创建 bean 实例。

基于注解方式实现对象创建

实现步骤
  1. 导入 spring-aop依赖
  2. 在 xml 配置文件中开启组件扫描(需要引入 context 名称空间)
  3. 创建类,在类上面添加创建对象的注解

建议重新弄个项目,用来测试效果

具体实现

导入 spring-aop依赖

在这里插入图片描述

在 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"
       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"
>

    <!--
        开启组件扫描(下面的两种方式是扫描多个包的写法,单个包直接写对应包名地址就好了)
        方式一:如果想要扫描多个包,可以使用多个逗号隔开(com.laoyang.spring.dao,com.laoyang.spring.service)
        方式二:扫描包的上层目录(com.laoyang.spring)
        > 根据需求而定
     -->
    <context:component-scan base-package="com.laoyang.spring" />

</beans>

需要引入 context 名称空间

创建类,在类上面添加创建对象的注解

package com.laoyang.spring.service;

import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

@Component(value = "userService")
// @Service
public class UserService {
    /*
    @Component(value = "userService")
    相当于
    <bean id="userService" class="com.laoyang.spring.service.UserService" />

    注解中的 value 属性值也可以省略不写,默认值是类的名称(首字母小写)
    比如这里的 UserService,那么默认就会变为 userService
     */

    public void add() {
        System.out.println("service add...");
    }
}

也可以把 @Component 设置为其它三个注解进行测试,最终效果都是一样的

测试效果

package com.laoyang.spring.test;

import com.laoyang.spring.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringBeanTest {
    @Test
    public void testUserService() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        UserService userService = context.getBean("userService", UserService.class);

        System.out.println(userService);
        userService.add();
    }
}

开启组件扫描的细节配置

  • 刚才实现对象创建的时候我们配置过相关的组件扫描了,接下来我们可以把组件扫描写的更加细致
  • 可以设置扫描内容的过滤器,只扫描我们需要的内容

案例一

<?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"
>
    <!--
        示例一
        use-default-filters="false":表示现在不使用默认 filter,自己配置 filter
        context:include-filter:设置要扫描哪些内容
     -->
    <context:component-scan base-package="com.laoyang.spring" use-default-filters="false">
        <!-- 只扫描有 @Controller 注解的类 -->
        <context:include-filter type="annotation"
                                expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
</beans>

案例二

<?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"
>
    <!--
        示例二
        下面的配置是扫描 base-package 设置的包路径下的所有内容
        context:exclude-filter:设置哪些内容不进行扫描
    -->
    <context:component-scan base-package="com.laoyang.spring">
        <!-- 如果是使用了 @Controller 注解的类就不进行扫描 -->
        <context:exclude-filter type="annotation"
                                expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
</beans>

基于注解方式实现属性注入

Spring 相关注解
  • @AutoWired:根据属性类型进行自动装配(注入)
  • @Qualifier:根据属性名称进行注入
  • @Resource:可以根据类型注入,也可以根据名称注入
  • @Value:注入普通类型属性

注意:以下的代码演示中,还是需要用到刚才的 xml 配置文件中的组件扫描!!!

@AutoWired 演示

步骤

  1. 把 Service 和 Dao 对象进行创建,在 Service 和 Dao 类中添加创建对象的注解
  2. 在 Service 中注入 Dao 对象,在 Service 类中添加 Dao 类型属性,在属性上使用注解
  3. 测试效果

创建相关接口和类,用于待会配合测试

package com.laoyang.spring.dao;

public interface UserDao {
    public void add();
}
package com.laoyang.spring.dao;

import org.springframework.stereotype.Repository;

// @Repository:创建 userDaoImpl 对象(相当于xml中的 bean 标签)
@Repository
public class UserDaoImpl implements UserDao {
    @Override
    public void add() {
        System.out.println("dao add....");
    }
}

在 service 中创建 dao 对象,并调用 dao 的 add 方法

package com.laoyang.spring.service;

import com.laoyang.spring.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {
    /**
     * 定义 dao 类型对象
     * 不需要添加 set 方法
     * @Autowired: 根据属性类型进行注入
     */
    @Autowired
    private UserDao userDao;

    public void add() {
        System.out.println("service add...");
        userDao.add();
    }
}

这里使用了 @Service,主要是为了给大家演示一下那四个注解都是相同效果,用哪个都可以,大家不要误解为只有 @Service 才能在这种时候使用!!!

测试效果

package com.laoyang.spring.test;

import com.laoyang.spring.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringBeanTest {
    @Test
    public void testUserService() {
        // 需要使用到 xml 配置文件中的组件扫描,如果没有配置组件扫描,则会报错
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        UserService userService = context.getBean("userService", UserService.class);

        System.out.println(userService);    // 地址值
        userService.add();  // service 和 dao 中 add 方法的打印结果
    }
}

正常测试完以后,大家可以在把 UserDao 中的 @Repository 注解注释掉,然后在测试,这个时候就会报错了…

@Qualifier 演示
  • @Qualifier 注解需要和上面的 @AutoWired 一起使用
  • 根据属性名称进行注入

在 @Repository 注解中定义对象属性名称

package com.laoyang.spring.dao;

import org.springframework.stereotype.Repository;

// 在 @Repository 注解中定义对象属性名称(相当于设置 bean 标签的 id 属性)
@Repository(value = "userDaoImpls")
public class UserDaoImpl implements UserDao {
    @Override
    public void add() {
        System.out.println("dao add....");
    }
}

在 Service 的 @Qualifier 注解中根据对象名称注入属性

package com.laoyang.spring.service;

import com.laoyang.spring.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

@Service
public class UserService {
    /**
     * 定义 dao 类型对象
     * 不需要添加 set 方法
     * @Qualifier(value = "属性名称"):根据属性名称进行注入
     */
    @Autowired
    @Qualifier(value = "userDaoImpls")
    private UserDao userDao;

    public void add() {
        System.out.println("service add...");
        userDao.add();
    }
}

如果找不到对应的属性名称,那么就会报错

测试效果

package com.laoyang.spring.test;

import com.laoyang.spring.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringBeanTest {
    @Test
    public void testUserService() {
        // 需要使用到 xml 配置文件中的组件扫描,如果没有配置组件扫描,则会报错
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        UserService userService = context.getBean("userService", UserService.class);

        System.out.println(userService);    // 地址值
        userService.add();  // service 和 dao 中 add 方法的打印结果
    }
}
@Resource 演示

在 Service 中的 dao 属性对象上添加 @Resource 注解

package com.laoyang.spring.service;

import com.laoyang.spring.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service
public class UserService {
    /**
     * 定义 dao 类型对象
     * 不需要添加 set 方法
     */
//    @Resource   // 根据类型进行注入
    @Resource(name = "userDaoImpls")   // 根据名称进行注入
    private UserDao userDao;

    public void add() {
        System.out.println("service add...");
        userDao.add();
    }
}

注意:@Resource 注解是 javax 包提供的,所以 Spring 官方并不建议我们使用 @Resource,而是建议我们使用 @Autowired 和 @Qualifier 注解

测试效果

package com.laoyang.spring.test;

import com.laoyang.spring.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringBeanTest {
    @Test
    public void testUserService() {
        // 需要使用到 xml 配置文件中的组件扫描,如果没有配置组件扫描,则会报错
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        UserService userService = context.getBean("userService", UserService.class);

        System.out.println(userService);    // 地址值
        userService.add();  // service 和 dao 中 add 方法的打印结果
    }
}
@Value 演示

在 UserService 类中创建一个属性,并使用 @Value 注解进行属性注入

package com.laoyang.spring.service;

import com.laoyang.spring.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;

@Service
public class UserService {
    @Resource(name = "userDaoImpls")   // 根据名称进行注入
    private UserDao userDao;

    @Value(value = "abc")
    private String name;

    public void add() {
        System.out.println("service add...");
        userDao.add();
        System.out.println(name);
    }
}

测试效果

package com.laoyang.spring.test;

import com.laoyang.spring.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringBeanTest {
    @Test
    public void testUserService() {
        // 需要使用到 xml 配置文件中的组件扫描,如果没有配置组件扫描,则会报错
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        UserService userService = context.getBean("userService", UserService.class);

        System.out.println(userService);    // 地址值
        userService.add(); 

        /**
         最后的打印结果为:
             地址值
             service add...
             dao add....
             abc
         */
    }
}

完全注解开发

  • 在上面的示例中,我们还是需要在 xml 配置文件中配置组件扫描,否则就没办法找到用注解创建的对象属性。
实现步骤
  1. 编写配置类,替代 xml 配置文件
  2. 编写测试类

注释掉 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"
       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:component-scan base-package="com.laoyang.spring" />-->
</beans>

编写配置类,替代 xml 配置文件

package com.laoyang.spring.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

// @Configuration:把当前类作为配置类,替代 xml 配置文件
@Configuration
@ComponentScan(basePackages = {"com.laoyang.spring"})
public class SpringConfig {
    /*
        @ComponentScan(basePackages = {"com.laoyang.spring"})
           相当于
        <context:component-scan base-package="com.laoyang.spring" />
     */
}

编写测试类

package com.laoyang.spring.test;

import com.laoyang.spring.config.SpringConfig;
import com.laoyang.spring.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringBeanTest {
    @Test
    public void testUserService2() {
        // 1.加载配置类
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);

        // 2.获取创建的对象
        UserService userService = context.getBean("userService", UserService.class);

        System.out.println(userService);    // 地址值
        userService.add();  // service 和 dao 中 add 方法的打印结果

        /**
         最后的打印结果为:
             地址值
             service add...
             dao add....
             abc
         */
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值