Bootstrap

Spring IoC详解

1.Spring简介

1.1 什么是Spring框架?

Spring 是一款开源的轻量级 Java 开发框架,旨在提高开发人员的开发效率以及系统的可维护性。

我们一般说 Spring 框架指的都是 Spring Framework,它是很多模块的集合,使用这些模块可以很方便地协助我们进行开发,比如说 Spring 支持 IoC(Inversion of Control:控制反转) 和 AOP(Aspect-Oriented Programming:面向切面编程)、可以很方便地对数据库进行访问、可以很方便地集成第三方组件(电子邮件,任务,调度,缓存等等)、对单元测试支持比较好、支持 RESTful Java 应用程序的开发。

1.2 Spring的特性

  • 非侵入式:基于Spring开发的应用中的对象可以不依赖于Spring的API
  • 控制反转:IOC——Inversion of Control,指的是将对象的创建权交给 Spring 去创建。使用 Spring 之前,对象的创建都是由我们自己在代码中new创建。而使用 Spring 之后。对象的创建都是给了 Spring 框架。
  • 依赖注入:DI——Dependency Injection,是指依赖的对象不需要手动调用 setXX 方法去设置,而是通过配置赋值。
  • 面向切面编程:Aspect Oriented Programming——AOP
  • 容器:Spring 是一个容器,因为它包含并且管理应用对象的生命周期
  • 组件化:Spring 实现了使用简单的组件配置组合成一个复杂的应用。在 Spring 中可以使用XML和Java注解组合这些对象。
  • 一站式:在 IOC 和 AOP 的基础上可以整合各种企业应用的开源框架和优秀的第三方类库(实际上 Spring 自身也提供了表现层的 SpringMVC 和持久层的 Spring JDBC)

1.3.Spring的体系结构

Spring 框架根据功能的不同大体可分为 Data Access/Integration(数据访问与集成)WebAOP、Aspects、Instrumentation(检测)、Messaging(消息处理)Core Container(核心容器)Test
Spring框架的组成结构图如下所示:
Spring框架

2.程序的耦合与解耦

  • **耦合:**耦合指的就是对象之间的依赖关系。对象之间的耦合越高,维护成本越高。

  • **案例:**没有引入IOC容器时系统的Web层、业务层、持久层存在耦合

/**
 * 模拟表现层
 */
public class Client {
	private UserService userService = new UserServiceImpl();
	
    public static void main(String[] args) {
        //硬编码:此处有依赖关系
        userService.addUser();
    }
}
/**
 * 业务层实现类
 */
public class UserServiceImpl implements UserService {
    //硬编码:此处有依赖关系
    private UserDao userDao = new UserDaoImpl();
	@Override
    public void addUser(){
        userDao.addUser();
    }
}
/**
 * 持久层实现类
 */
public class UserDaoImpl implements UserDao {
    @Override
    public void addUser(){
        System.out.println("insert into tb_user......");
    }
}
  • 问题分析

    上边的代码service层在依赖dao层的实现类,此时如果更改dao了层的实现类或此时没有dao层实现类,编译将不能通过。

  • IOC(工厂模式)解耦:

    1. 把所有的dao和service对象使用配置文件配置起来
    2. 当服务器启动时读取配置文件
    3. 把这些对象通过反射创建出来并保存在容器中
    4. 在使用的时候,直接从工厂拿

3.工厂模式解耦

3.1 基础版本

每次调用,在工厂中直接new对象

/**
 * bean工厂
 */
public class BeanFactory_v1 {

    /**
     * 获得UserServiceImpl对象
     * @return
     */
    public static UserService getUserService(){
        return new UserServiceImpl();
    }

    /**
     * 获得UserDaoImpl对象
     * @return
     */
    public static UserDao getUserDao(){
        return new UserDaoImpl();
    }
}

3.1 进阶版本

每次调用,在工厂中通过反射创建对象,多例模式

工厂模式创建对象思路:

  1. 用反射的方式来创建对象
  2. 通过读取配置文件的方式得到能反射的全类名
#配置文件beans.properties:
#配置要使用的dao和service
UserDao=com.by.dao.UserDaoImpl
UserService=com.by.service.UserServiceImpl
/**
 * bean工厂
 */
public class BeanFactory_v2 {

    private static Properties prop = new Properties();

    /**
     * 根据全类名获取bean对象
     * @param beanName
     * @return
     * @throws ClassNotFoundException
     */
    public static Object getBean(String beanName) {
        try {
            //不能使用:web工程发布后没有src目录
            //InputStream is = new FileInputStream("src/bean.properties");
            InputStream is = 
            BeanFactory_v2.class.getClassLoader()
                .getResourceAsStream("bean.properties");
            prop.load(is);
            return Class.forName(prop.getProperty(beanName)).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void main(String[] args) {
        System.out.println(prop.get("UserService"));
        System.out.println(getBean("UserService"));
    }
}
/**
 * 业务层实现类
 */
public class UserServiceImpl implements UserService {
    
    private UserDao userDao = (UserDao) BeanFactory.getBean("UserDao");

    public void addUser(){
      userDao.addUser();
    }
}
/**
 * 模拟表现层
 */
public class Client {
    public static void main(String[] args) {
        //直接引用接口实现类
      for (int i = 0; i < 5; i++) {
            UserService userService = 
              (UserService)BeanFactory.getBean("UserService");
            System.out.println(userService);
        }
    }
}

3.3 最终版本

  1. 加载类时直接读取配置文件,通过反射创建配置文件中所有类的实例并存到map中,单例模式
  2. 调用方法获取对象时直接从map中读取
/**
 * bean工厂
 */
public class BeanFactory_v3 {

    //1.定义一个容器,用于存放对象
    private static Map<String, Object> beans = new HashMap<>();

    /**
     * 加载配置文件
     */
    static {
        try {
            //2、读取配置文件
            	//不能使用:web工程发布后没有src目录
            	//InputStream is = new FileInputStream("src/bean.properties");
            InputStream is = BeanFactory_v3.class.getClassLoader().getResourceAsStream("bean.properties");

            //3、通过反射创建对象,把对象存到容器中
            Properties prop = new Properties();
            prop.load(is);
            Set<Map.Entry<Object, Object>> entrySet = prop.entrySet();
            for (Map.Entry<Object, Object> entry : entrySet) {
                String key = entry.getKey().toString();
                String beanName = entry.getValue().toString();
                Object value = Class.forName(beanName).newInstance();
                beans.put(key, value);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 4、在使用的时候,直接从工厂拿
     * @param beanName
     * @return
     */
    public static Object getBean(String beanName) {
        try {
            return beans.get(beanName);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void main(String[] args) {
        System.out.println(getBean("UserService"));
    }
}

4.Spring IoC解耦

4.1 什么是 IoC?

IoC(Inversion of Control:控制反转) 是一种设计思想,而不是一个具体的技术实现。IoC 的思想就是将原本在程序中手动创建对象的控制权,交由 Spring 框架来管理。

简单来说就是把本来在类内部控制的对象,反转到类外部进行创建后注入,不在由类本身镜像控制,这就是IOC的本质。不过, IoC 并非 Spring 特有,在其他语言中也有应用。

  • 为什么叫控制反转?

控制:指的是对象创建(实例化、管理)的权力
反转:控制权交给外部环境(Spring 框架、IoC 容器)

  • 普通方式获取对象:

    ​ 我们在获取对象时,都是采用new的方式。是主动的。

    普通方式

  • 工厂模式获取对象:

​ 我们获取对象时,同时跟工厂要,有工厂为我们查找或者创建对象。是被动的。

现在方式

​ 这种被动接收的方式获取对象的思想就是控制反转,它是spring框架的核心之一。

4.2 IoC和工厂模式区别

Spring IOC(Inversion of Control,控制反转)与工厂模式的主要区别体现在实现方式、依赖管理和解耦程度上。

  • 实现方式:

    • Spring IOC 通过依赖注入(DI)实现,容器负责创建对象、管理对象以及对象之间的依赖关系。这意味着,当容器创建一个对象时,会自动处理对象之间的依赖关系。
    • 工厂模式 通过专门定义一个类(工厂类)负责创建对象,将对象创建逻辑集中在一个地方。这意味着对象之间的依赖关系需要手动在工厂类中处理。
  • 依赖管理:

    • Spring IOC 容器负责管理对象之间的依赖关系,当容器创建一个对象时,会自动处理这些依赖。
    • 工厂模式 由工厂类负责管理对象之间的依赖关系,需要手动在工厂类中处理这些依赖。
  • 解耦程度:

    • Spring IOC 通过依赖注入实现了解耦,使得对象之间不需要直接进行依赖,而是通过容器进行依赖管理,提高了系统的灵活性和可维护性。
    • 工厂模式 虽然也能实现一定程度的解耦,但相较于Spring IOC,其解耦程度较低,对象之间的依赖关系仍然需要在工厂类中进行处理。

4.3 IoC解耦

4.3.1 创建工程

  • 工程结构
    创建工程

  • pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.by</groupId>
    <artifactId>Spring_IOC_Xml</artifactId>
    <version>1.0-SNAPSHOT</version>
    
    <properties>
        <!-- 项目源码及编译输出的编码 -->
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <!-- 项目编译JDK版本 -->
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    
    <dependencies>
        <!-- Spring常用依赖 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.8.RELEASE</version>
        </dependency>
    </dependencies>
</project>

注意:Jar包彼此存在依赖,只需引入最外层Jar即可由Maven自动将相关依赖Jar引入到项目中。

Spring常用功能的Jar包依赖关系
包依赖

​ 核心容器由 beans、core、context 和 expression(Spring Expression Language,SpEL)4个模块组成。

  • dao
/**
 * 持久层实现类
 */
public class UserDaoImpl implements UserDao {

    @Override
    public void addUser(){
        System.out.println("insert into tb_user......");
    }
}
  • service
/**
 * 业务层实现类
 */
public class UserServiceImpl implements UserService {
    //此处有依赖关系
    private UserDao userDao = new UserDaoImpl();

    public void addUser(){
        userDao.addUser();
    }
}

4.3.2 applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--1、注意:要导入schema约束-->
<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">
    
    <!--
		2、把对象交给spring来创建
       		id:给对象在容器中提供一个唯一标识。用于获取对象	
		   	class:指定类的全限定类名。用于反射创建对象。默认情况下调用无参构造函数
	-->
    <bean id="userDao" class="com.by.dao.UserDaoImpl"></bean>
    <bean id="userService" class="com.by.service.UserServiceImpl"></bean>
</beans>

注意:命名无限制,约定俗成命名有:spring-context.xml、applicationContext.xml、beans.xml

4.3.3 测试

/**
 * 模拟表现层
 */
public class Client {
    public static void main(String[] args) {
        //1.使用ApplicationContext接口,就是在获取spring容器
        // ac对象就类似工厂模式中的工厂
        ApplicationContext ac = new 
            ClassPathXmlApplicationContext("applicationContext.xml");
        //2.根据bean的id获取对象
        UserDao userDao = (UserDao) ac.getBean("userDao");
        System.out.println(userDao);

        UserService userService = (UserService) ac.getBean("userService");
        System.out.println(userService);
        userService.addUser();
    }
}
  • 问题:service层仍然耦合

4.4 依赖注入(DI)

4.4.1依赖注入概述

DI—Dependency Injection,即依赖注入:组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。

  • 为什么需要依赖注入?

IoC虽然能帮我们创建对象,但是要怎么获取对象呢?

如果每次获取对象都使用getBean方法来获取,那么与工厂(spring容器)直接又形成耦合了。而依赖注入就可以解决这个问题。

  • IOC和DI的关系

ioc就是容器,di就是注入这一行为,那么di确实就是ioc的具体功能的实现。控制反转是通过依赖注入实现的,其实它们是同一个概念的不同角度描述。通俗来说就是IoC是设计思想,DI是实现方式。

  • 依赖注入的类型

    • 基本类型和String
    • 其它类型(在配置文件中或者注解配置过的bean)
    • 复杂类型/集合类型
  • 依赖注入的方式
    在 Spring 中实现依赖注入的常见方式有以下 3 种:

    • 属性注入(Field Injection)
    • Setter 注入(Setter Injection)
    • 构造方法注入(Constructor Injection)

4.4.2 构造函数注入

  • 在XML配置方式中,<constructor-arg>是通过构造函数参数注入,比如下面的xml:
<?xml version="1.0" encoding="UTF-8"?>
<!--1、注意:要导入schema约束-->
<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">
    <!--2、把对象交给spring来创建-->
    <bean id="userDao" class="com.by.dao.UserDaoImpl"></bean>
    <bean id="userService" class="com.by.service.UserServiceImpl">
        <!--
               要求:类中需要提供一个对应参数列表的构造函数。
               标签:constructor-arg
                       ==给谁赋值:==
				           name:指定参数在构造函数中的名称
				       ==赋什么值:==
				           ref:它能赋的值是其他bean类型,也就是说,必须得是在配置文件中配置过的bean
        -->
        <!--注入bean对象-->
        <constructor-arg name="userDao" ref="userDao"></constructor-arg>

    </bean>
</beans>

顾名思义,就是使用类中的构造函数,给成员变量赋值。注意,赋值的操作不是我们自己做的,而是通过配置的方式,让spring框架来为我们注入。具体代码如下:

/**
 * 业务层实现类
 */
public class UserServiceImpl implements UserService {

    private UserDao userDao;
	//需要提供构造方法
    public UserServiceImpl(UserDao userDao) {
        this.userDao = userDao;
    }

    public void addUser(){
        userDao.addUser();
    }
}

4.4.3 setter方式注入

  • 在XML配置方式中,property都是setter方式注入,比如下面的xml:
<?xml version="1.0" encoding="UTF-8"?>
<!--1、注意:要导入schema约束-->
<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">
    <!--2、把对象交给spring来创建-->
    <bean id="userDao" class="com.by.dao.UserDaoImpl"></bean>
    <bean id="userService" class="com.by.service.UserServiceImpl">
        <!--
               要求:实现类中必须包含对应的set方法
               标签:property
                       ==给谁赋值:==
				           name:找的是类中set方法后面的部分
				       ==赋什么值:==
				           ref:它能赋的值是其他bean类型,也就是说,必须得是在配置文件中配置过的bean
        -->
        <property name="userDao" ref="userDao"></property>
    </bean>
</beans>

本质上包含两步:

  • 第一步,通过反射创建对象,相当于new UserServiceImpl(), 所以需要默认构造函数
  • 第二步,调用setUserDao()函数注入userDao的值, 所以需要setUserDao()函数

所以对应的service类是这样的:

/**
 * 业务层实现类
 */
public class UserServiceImpl implements UserService {

    private UserDao userDao;
	//需要提供setter方法
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void addUser(){
        userDao.addUser();
    }
}

4.4.4 属性注入——自动注入

自动注入不需要在 标签中添加其他标签注入属性值,而是自动从容器中找到相应的bean对象设置为属性值。

自动注入有两种配置方式:

  • 全局配置:在 <beans> 中设置 default-autowire 属性可以定义所有bean对象的自动注入策略。
  • 局部配置:在 <bean> 中设置 autowire 属性可以定义当前bean对象的自动注入策略。

autowire的取值如下:

  • no:不会进行自动注入。
  • default:全局配置default相当于no,局部配置default表示使用全局配置
  • byName:在Spring容器中查找id与属性名相同的bean,并进行注入。需要提供set方法。
  • byType:在Spring容器中查找类型与属性类型相同的bean,并进行注入。需要提供set方法。
  • constructor:在Spring容器中查找id与属性名相同的bean,并进行注入。需要提供构造方法。

具体代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<!--1、注意:要导入schema约束-->
<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">
    <!--2、把对象交给spring来创建-->
    <bean id="userDao" class="com.by.dao.UserDaoImpl"></bean>
    <!--autowire="byType":按照类型自动注入值-->
    <bean id="userService" class="com.by.service.UserServiceImpl" autowire="byType">
    </bean>
</beans>
/**
 * 业务层实现类
 */
public class UserServiceImpl implements UserService {

    private UserDao userDao;
	//需要提供setter方法
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void addUser(){
        userDao.addUser();
    }
}

4.4.5 注入集合类型的属性

顾名思义,就是给类中的集合成员传值,它用的也是set方法注入的方式,只不过变量的数据类型都是集合。我们这里介绍注入数组,List,Set,Map。具体代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<!--1、注意:要导入schema约束-->
<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">
    <!--2、把对象交给spring来创建-->
    <bean id="userDao" class="com.by.dao.UserDaoImpl"></bean>
    <bean id="userService" class="com.by.service.UserServiceImpl">
        <!--
                要求:property
                标签:constructor-arg
                        ==给谁赋值:==
				            name:找的是类中set方法后面的部分
				        ==赋什么值:==
				            value:它能赋的值是基本数据类型和String类型
				            ref:它能赋的值是其他bean类型,也就是说,必须得是在配置文件中配置过的bean
        -->
        <property name="userDao" ref="userDao"></property>
        <!-- 给mySet集合注入数据 -->
        <property name="mySet">
            <set>
                <value>AAA</value>
                <value>BBB</value>
                <value>CCC</value>
            </set>
        </property>
        <!-- 注入array数组数据 -->
        <property name="myArray">
            <array>
                <value>AAA</value>
                <value>BBB</value>
                <value>CCC</value>
            </array>
        </property>
        <!-- 注入list集合数据 -->
        <property name="myList">
            <list>
                <value>AAA</value>
                <value>BBB</value>
                <value>CCC</value>
            </list>
        </property>
        <!-- 注入Map数据 -->
        <property name="myMap">
            <map>
                <entry key="testA" value="aaa"></entry>
                <entry key="testB" value="bbb"></entry>
            </map>
        </property>
    </bean>
</beans>

service类具体代码:

/**
 * 业务层实现类
 */
public class UserServiceImpl implements UserService {

    private UserDao userDao;
    private String[] myStrs;
    private List<String> myList;
    private Set<String> mySet;
    private Map<String,String> myMap;

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

    public void setMyStrs(String[] myStrs) {
        this.myStrs = myStrs;
    }

    public void setMyList(List<String> myList) {
        this.myList = myList;
    }

    public void setMySet(Set<String> mySet) {
        this.mySet = mySet;
    }

    public void setMyMap(Map<String, String> myMap) {
        this.myMap = myMap;
    }

    public void addUser(){
        System.out.println(Arrays.toString(myStrs));
        System.out.println(myList);
        System.out.println(mySet);
        System.out.println(myMap);
        userDao.addUser();
    }
}

4.5 Spring的工厂类

工厂类

  • 工厂类 BeanFactory 和 ApplicationContext 的区别。

    1. ApplicationContext 是 BeanFactory 的子接口,提供了比父接口更多的功能。

    2. 在生成 bean 实例的时候,生成的时机是不一样的。

      • BeanFactory 在工厂实例化后,在调用 getBean 时创建实例
      • ApplicationContext 在一加载配置文件的时候,将配置文件中所有单例模式生成的类全部实例化

现在一般使用 ApplicationContext,不建议使用 BeanFactory。

4.6 bean的介绍

4.6.1 什么是bean?

在Spring框架中,“Bean” 是一个概念,它表示由Spring IoC容器管理的对象。Bean是Spring框架的基本构建块,它可以是一个Java对象、一个组件、一个服务,或者是你的应用程序中的任何一个对象。

bean是计算机自动生成的类,bean是一个由Spring IoC容器实例化、组装和管理的对象。也就是说,bean并不是程序员编辑的,而是程序运行时,由spring通过反射生成的

4.6.2 五种作用域

  • 目前Spring Bean的作用域或者说范围主要有五种:

    作用域说明
    singleton默认值, IoC 容器中只有唯一的 bean 实例。Spring 中的 bean 默认都是单例的,是对单例设计模式的应用。
    prototype每次获取都会创建一个新的 bean 实例。也就是说,连续 getBean() 两次,得到的是不同的 Bean 实例。
    request(仅 Web 应用可用): 每一次 HTTP 请求都会产生一个新的 bean(请求 bean),该 bean 仅在当前 HTTP request 内有效。
    session(仅 Web 应用可用) : 每一次来自新 session 的 HTTP 请求都会产生一个新的 bean(会话 bean),该 bean 仅在当前 HTTP session 内有效。
    application(仅 Web 应用可用):每个 Web 应用在启动时创建一个 Bean(应用 Bean),该 bean 仅在当前应用启动时间内有效。
    websocket(仅 Web 应用可用):每一次 WebSocket 会话产生一个新的 bean。
  • 可以通过 <bean> 标签的scope 属性控制bean的作用范围,其配置方式如下所示:

  <bean id="..." class="..." scope="singleton"/>  
  • 需要根据场景决定对象的单例、多例模式

    单例:Service、DAO、SqlSessionFactory(或者是所有的工厂)

    多例:Connection、SqlSession

4.6.3 将一个类声明为 Bean 的注解有哪些?

  • @Component:通用的注解,可标注任意类为 Spring 组件。如果一个 Bean 不知道属于哪个层,可以使用@Component 注解标注。
  • @Repository : 对应持久层即 Dao 层,主要用于数据库相关操作。
  • @Service : 对应服务层,主要涉及一些复杂的逻辑,需要用到 Dao 层。
  • @Controller : 对应 Spring MVC 控制层,主要用于接受用户请求并调用 Service 层返回数据给前端页面。

4.6.4 Bean的生命周期

生命周期

Bean生命周期可以粗略的划分为五大步:

  • 第一步:实例化Bean
  • 第二步:Bean属性赋值
  • 第三步:初始化Bean
  • 第四步:使用Bean
  • 第五步:销毁Bean

案例:

<!--
	singleton:单例
	prototype:多例
-->
<bean id="userService" class="com.by.service.UserServiceImpl"
      			scope="singleton" init-method="init" destroy-method="destroy">
/**
 * 业务层实现类
 */
public class UserServiceImpl implements UserService {

    private UserDao userDao;

    public UserServiceImpl() {
        System.out.println("调用构造方法创建bean...");
    }

    public void setUserDao(UserDao userDao) {
        System.out.println("调用set方法注入值...");
        this.userDao = userDao;
    }

    public void init(){
        System.out.println("调用init方法初始化bean...");
    }

    public void destroy(){
        System.out.println("调用destroy方法销毁bean...");
    }

    public void addUser(){
        userDao.addUser();
    }
}
/**
 * 模拟表现层
 */
public class Client {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext ac = 
            	new ClassPathXmlApplicationContext("applicationContext.xml");
        //使用对象
        
        ac.getBean("userService");
        /**
        *singleton:对象在容器中,关闭容器时执行bean的销毁方法
        *prototype:对象不在容器中,而是受jvm管理,jvm回收bean时执行销毁方法
        */
        ac.close();
    }
}
  • 生命周期
  • 单例(singleton):[容器启动]—>构造方法(实例化)—>set方法(注入)—>init方法(初始化)—>[容器关闭]—>destroy方法(销毁)
  • 多例(prototype):[使用对象]---->构造方法(实例化)—>set方法(注入)—>init方法(初始化)—>[JVM垃圾回收]—>destroy方法(销毁)

5.基于注解的IOC配置

学习基于注解的IOC配置,大家脑海里首先得有一个认知,即注解配置和xml配置要实现的功能都是一样的,都是要降低程序间的耦合。只是配置的形式不一样。

5.1 创建工程

创建工程

  • pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.by</groupId>
    <artifactId>Spring_IOC_Annotation</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <!-- Spring常用依赖 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.8.RELEASE</version>
        </dependency>
    </dependencies>
</project>
  • dao
/**
 * 持久层实现类
 */
public class UserDaoImpl implements UserDao {

    @Override
    public void addUser(){
        System.out.println("insert into tb_user......");
    }
}
  • service
/**
 * 业务层实现类
 */
public class UserServiceImpl implements UserService {

    private UserDao userDao;

    @Override
    public void addUser(){
        userDao.addUser();
    }
}

5.2 IOC

  • applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       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
				http://www.springframework.org/schema/context
      			http://www.springframework.org/schema/context/spring-context.xsd ">

    <!-- 告知spring框架,在读取配置文件,创建容器时扫描包,依据注解创建对象,并存入容器中 -->
    <context:component-scan base-package="com.by"></context:component-scan>
</beans>
  • dao
@Repository
public class UserDaoImpl implements UserDao {
	... ...
}
  • service
@Service
public class UserServiceImpl implements UserService {
	... ...
}

5.3 DI

  • service
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    public void addUser() {
        userDao.addUser();
    }
}
  • 测试
/**
 * 模拟表现层
 */
public class Client {
    public static void main(String[] args) {
        ApplicationContext ac = 
            new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = ac.getBean("userServiceImpl",UserService.class);
        userService.addUser();
    }
}

5.3 常用注解

5.3.1 用于创建bean的

以下四个注解的作用及属性都是一模一样的,都是针对一个的衍生注解只不过是提供了更加明确的语义化。

5.3.1.1 @Controller
  • 作用:把资源交给spring来管理,相当于:<bean id="" class="">;一般用于表现层。

  • 属性:value:指定bean的id;如果不指定value属性,默认bean的id是当前类的类名,首字母小写;

5.3.1.2 @Service
  • 作用:把资源交给spring来管理,相当于:<bean id="" class="">;一般用于业务层。

  • 属性:value:指定bean的id;如果不指定value属性,默认bean的id是当前类的类名,首字母小写;

  • 案例

  //@Service("userService")声明bean,且id="userService"
  @Service//声明bean,且id="userServiceImpl"
  public class UserServiceImpl implements UserService {
   	...   
  }
5.3.1.3 @Repository
  • 作用:把资源交给spring来管理,相当于:<bean id="" class="">;一般用于持久层。

  • 属性:value:指定bean的id;如果不指定value属性,默认bean的id是当前类的类名,首字母小写;

  • 案例

  //@Repository("userDao")声明bean,且id="userDao"
  @Repository//声明bean,且id="userDaoImpl"
  public class UserDaoImpl implements UserDao {
  
      @Override
      public void addUser(){
          System.out.println("insert into tb_user......");
      }
  }
5.3.1.4 @Component
  • 作用:把资源交给spring来管理,相当于:<bean id="" class="">;通用。

  • 属性:value:指定bean的id;如果不指定value属性,默认bean的id是当前类的类名,首字母小写;

@Component、@Controller、@Repositor和@Service 的区别?

  • @Component:最普通的组件,可以被注入到spring容器进行管理。
  • @Controller:将类标记为 Spring Web MVC 控制器。
  • @Service:将类标记为业务层组件。
  • @Repository:将类标记为数据访问组件,即DAO组件。
5.3.1.5 @Scope
  • 作用:指定bean的作用域范围。

  • 属性:value:指定范围的值,singleton prototype request session。

5.3.2 用于属性注入的

以下四个注解的作用相当于:<property name="" ref="">

5.3.2.1 @Autowired
  • 作用:自动按照类型注入。set方法可以省略。

  • 案例:

  @Service
  public class UserServiceImpl implements UserService {
  
      @Autowired //注入类型为UserDAO的bean
      private UserDao userDao;
  
      public void addUser(){
          userDao.addUser();
      }
  }
5.3.2.2 @Qualifier
  • 作用:注入指定名称的bean

  • 案例:

  @Service
  public class UserServiceImpl implements UserService {
  
      @Autowired //注入类型为UserDAO的bean
      @Qualifier("userDaoImpl")//有多个相同类型的bean时,可使用Qualifier指定bean
      private UserDao userDao;
  
      public void addUser(){
          userDao.addUser();
      }
  }
5.3.2.3 @Resource
  • 作用:自动按照名字注入。set方法可以省略。

  • 属性:name:指定bean的id。

  • 案例:

  @Service
  public class UserServiceImpl implements UserService {
  
      @Resource(name="userDaoImpl")//注入id=“userDaoImpl”的bean
      private UserDao userDao;
  
      public void addUser(){
          userDao.addUser();
      }
  }
5.3.2.4 @Value
  • 作用:注入基本数据类型和String类型数据的

  • 属性:value:用于指定值

  • 案例一

  @Service
  public class UserServiceImpl implements UserService {
  
      @Resource(name="userDaoImpl") //注入id=“userDaoImpl”的bean
      private UserDao userDao;
      @Value("张三")//注入String
      private String name;
      @Value("18")//注入Integer
      private Integer age;
  
      public void addUser(){
          System.out.println(name+","+age);
          userDao.addUser();
      }
  }
  • 案例二

    1. 创建config.properties
     name=张三
     age=18
  1. 加载配置文件
     <?xml version="1.0" encoding="UTF-8"?>
     <beans xmlns="http://www.springframework.org/schema/beans"
            xmlns:context="http://www.springframework.org/schema/context"
            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
     			http://www.springframework.org/schema/context
           		http://www.springframework.org/schema/context/spring-context.xsd ">
         <!--加载config.properties-->
         <context:property-placeholder location="config.properties"/>
         <context:component-scan base-package="com.by"></context:component-scan>
     </beans>
  1. 注入属性值
     @Service
     public class UserServiceImpl implements UserService {
     
         @Autowired
         private UserDao userDao;
         @Value("${name}")//注入String
         private String name;
         @Value("${age}")//注入Integer
         private Integer age;
     
         public void addUser() {
             System.out.println(name+","+age);
             userDao.addUser();
         }
     }
;