栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring5之IOC(含底层原理)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring5之IOC(含底层原理)

Spring5之IOC
  • Spring 概述
    • Spring 特点
  • 入门案例
    • 一、下载 Spring5
    • 二、创建一个java工程
    • 三、导入 Spring5 的相关 jar 包
    • 四、编写代码进行测试
  • IOC 容器
    • 什么是 IOC?
    • 底层原理
      • 使用的技术
      • 原始调用对象的缺点
      • 工厂模式的解耦过程
      • IOC 过程
      • IOC(接口)
        • ApplicationContext 接口中的实现类说明
  • IOC 容器之Bean 管理
    • 什么是Bean管理?
    • Bean 管理的两种操作方式
    • IOC 基于 xml 配置文件操作 Bean 管理
      • 一、基于 xml 方式创建对象
      • 二、基于 xml 方式注入属性
        • 第一种注入方式:使用 set 方法
        • 第二种注入方式:使用有参构造器
        • 第三种注入方式:p名称空间注入(了解)
      • 三、注入属性 - 字面量
        • 注入空值
        • 注入特殊字符
      • 四、注入属性 - 外部 bean
      • 五、注入属性 - 内部 bean
      • 六、注入属性 - 级联赋值
        • 第一种写法
        • 第二种写法
      • 七、注入集合属性
        • 步骤
        • 具体实现
        • 思考问题
        • 解决问题
          • 一、在集合里面设置对象类型的值
          • 二、把集合提取出来,当做公共数据
    • 工厂 Bean(FactoryBean)
      • 说明
      • 代码演示
    • Bean 的作用域
      • 验证当前是不是单实例
      • 设置多实例
    • Bean 的生命周期
      • 演示 Bean 的生命周期
      • Bean 的后置处理器
      • 演示添加后置处理器的效果
    • 自动装配
      • 演示自动装配的过程
    • 引入外部属性文件
      • 直接配置数据库信息(配置德鲁伊连接池)
      • 引入外部属性文件配置数据库连接池
    • IOC 基于注解操作 Bean 管理
      • 注解复习
      • Spring 相关注解
      • 基于注解方式实现对象创建
        • 实现步骤
        • 具体实现
      • 开启组件扫描的细节配置
      • 基于注解方式实现属性注入
        • Spring 相关注解
        • @AutoWired 演示
        • @Qualifier 演示
        • @Resource 演示
        • @Value 演示
      • 完全注解开发
        • 实现步骤

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 配置文件,在配置文件中配置创建的对象




    
    

说明: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 容器实现的两种方式(两个接口):BeanFactoty 和 ApplicationContext

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

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

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

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

ApplicationContext 接口中的实现类说明

ClassPathXmlApplicationContext 实现类:

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

FileSystemXmlApplicationContext 实现类:

  1. 根据磁盘路径(绝对路径)获取对应的对象
IOC 容器之Bean 管理 什么是Bean管理?
  • Bean 管理指的是两个操作:Spring 创建对象 和 Spring 注入属性

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

Bean 管理的两种操作方式
  • 基于 xml 配置文件的方式实现
  • 基于注解的方式实现
IOC 基于 xml 配置文件操作 Bean 管理 一、基于 xml 方式创建对象



    
    

说明:

  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;


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 配置文件中配置对象创建,在配置属性注入


    
    
        
        
        
    

  • 进行测试
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;


public class Order {
    private String oname;

    private String address;

    // 有参构造器
    public Order(String oname, String address) {
        this.oname = oname;
        this.address = address;
    }
}
  • 在 Spring 的配置文件中进行配置


    
    
        
        
        

        
    

  • 测试
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"

    
    
    
    
  2. 进行属性注入,在 bean 标签中进行操作

    
    
    
    	
        
    
    
  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 配置文件中进行配置



    
    
        
        
        

        
        
            
        
    

  • 测试
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 配置文件中进行配置


    
    
        
        

        


        


        
        
            >]]>
        
    

  • 测试
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 配置文件中进行配置



    
    
        
        
    
    

  • 测试
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();
        
    }
}
五、注入属性 - 内部 bean

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

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

package com.spring.bean;


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;


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 配置文件中进行相关配置




    
    
        
        
        

        
        
        
            
                
            
        
    

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 配置文件中进行相关配置



    
    
        
        
        

        
        
    
    
        
    

  • 测试
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;


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 配置文件中进行相关配置



    
    
        
        
        

        
		
        
    

七、注入集合属性 步骤
  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;

    
    private List list;

    
    private Map maps;

    
    private Set set;

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

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

    public void setMaps(Map maps) {
        this.maps = maps;
    }

    public void setSet(Set 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 配置文件编写相关配置,注入相关属性




    
    
        
        
            
                java
                mysql
                
            
        

        
        
            
                小李
                小王
                
            
        

        
        
            
                
                
                
            
        

        
        
            
                嘿嘿嘿
                哈哈哈
                
            
        
    

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();
        
    }
}
思考问题
  1. 上面的案例中,都是插入的 String 类型的数据,如果我想要插入一个对象类型,该如何实现呢?
  2. 上面的案例中,所有的集合属性都放在了 Stu 对象中,这样的话就不能给其它对象进行使用了,那么我们怎么把集合数据变为公共的呢?
解决问题 一、在集合里面设置对象类型的值
  • 创建课程类,并在 Stu 类中创建课程集合属性
package com.laoyang.spring.collectiontype;


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 courseList;
    
    public void setCourseList(List courseList) {
        this.courseList = courseList;
    }
    
    public void show() {
        System.out.println(courseList);
    }
}
  • 在 Spring 配置文件中进行相关配置



    
    
        

        
        
            
                
                
            
        
    

    
        
    
    
        
    
    

  • 测试
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 list;

    public void setList(List 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 集合类型的属性



    
    
        第一
        第二
        第三
    

    
    
        
    

  • 测试
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;


public class MyBean implements FactoryBean {
    
    @Override
    public Course getObject() throws Exception {
        Course course = new Course();
        course.setCname("abc");
        return course;
    }

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

配置文件:




    

测试:

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 配置:




    
    
        第一
        第二
        第三
    

    
    
        
    

测试:

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 配置文件中进行相关配置



    
    
        
    

  • 使用获取的 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);

        
        ((ClassPathXmlApplicationContext) context).close();

        
    }
}
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;
    }
}
  • 在配置文件中配置后置处理器



    
    
        
    

    
    

  • 测试效果
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);

        
        ((ClassPathXmlApplicationContext) context).close();

        
    }
}
自动装配
  • 自动装配:根据指定的装配规则(属性名称或属性类型),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 配置文件中进行相关配置



    

    

    




    

  • 测试
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 配置文件中进行配置:



    
    
        
        
        
        
    

引入外部属性文件配置数据库连接池
  • 创建 .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 属性文件


    
    

    
        
        
        
        
    

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

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

Spring 相关注解

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

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

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

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

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

具体实现

导入 spring-aop依赖

在 xml 配置文件中开启组件扫描




    
    


需要引入 context 名称空间

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

package com.laoyang.spring.service;

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

@Component(value = "userService")
// @Service
public class 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();
    }
}
开启组件扫描的细节配置
  • 刚才实现对象创建的时候我们配置过相关的组件扫描了,接下来我们可以把组件扫描写的更加细致
  • 可以设置扫描内容的过滤器,只扫描我们需要的内容

案例一:



    
    
        
        
    

案例二:



    
    
        
        
    

基于注解方式实现属性注入 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 {
    
    @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 {
    
    @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 {
    
//    @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(); 

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

注释掉 xml 文件中的组件扫描配置



    


编写配置类,替代 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 {
    
}

编写测试类

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 方法的打印结果

        
    }
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/679140.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号