Spring注解开发和XML开发

news2024/11/20 2:41:03

目录

  • Spring
    • 简介
    • 发展史
    • Spring Framework系统架构
    • spring 核心概念
      • IOC、IOC容器、Bean、DI
      • IOC快速入门
      • DI快速入门
    • IOC
      • Bean基础配置
        • id与class属性
        • name属性
        • scope属性
      • Bean的实例化
        • 构造方法
        • 静态工厂
        • 实例工厂
        • FactoryBean的使用(工厂实例的简化)
      • Bean的生命周期
        • 添加初始化和销毁方法
        • close关闭容器
        • 注册钩子关闭容器
        • 关闭方式的区别
        • 通过接口来简化关闭容器
    • DI
      • setter注入
        • 注入引用数据类型
        • 注入简单数据类型
      • 构造器注入
        • 构造器注入多个数据类型
        • 构造器注入多个简单数据类型
        • 解决构造函数中参数名紧耦合
      • 自动装配
        • 什么是依赖自动装配
        • 自动装配的方式有哪些?
        • 按照类型完成自动装配的配置
        • 按照名称完成自动注入装配的配置
        • 注意事项(配置特征)
      • 集合注入
      • 加载 properties 文件
        • 读取单个属性
    • 核心容器
      • 容器
      • Bean 的三种获取方式
      • 容器类层次结构
      • BeanFactory的使用
    • IOC/DI注解开发
      • 注解开发定义bean
      • 纯注解开发模式
      • 注解开发bean作用范围与生命周期管理
        • bean的作用范围
        • bean的生命周期
      • 注解开发依赖注入
        • 注解实现按照类型注入
        • 注解实现按照名称注入
        • 简单数据类型注入
        • 注解读取 properties 配置文件
      • IOC/DI注解开发管理第三方bean
        • 注解开发管理第三方bean
        • 引入外部配置类
          • 使用包扫描引入
          • 使用@Import注解引入
        • 注解开发实现为第三方bean注入资源
          • 简单数据库类型
          • 引用数据类型
        • XML开发和注解的对比

Spring

官网:Spring | Home

简介

随着时代发展,软件规模与功能都呈几何式增长,开发难度也在不断递增,Spring可以简化开发,降低企业级开发的复杂性,使开发变得更简单快捷

随着项目规模与功能的增长,遇到的问题就会增多,为了解决问题会引入更多的框架,Spring可以框架整合,高效整合其他技术,提高企业级应用开发与运行效率

Spring发展到今天已经形成了一种开发的生态圈,Spring提供了若干个项目,每个项目用于完成特定的功能

在这里插入图片描述

  • Spring Framework: Spring框架,是Spring中最早最核心的技术,也是所有其他技术的基础。
  • SpringBoot: Spring是来简化开发,而SpringBoot是来帮助Spring在简化的基础上能更快速进行开发。
  • SpringCloud: 这个是用来做分布式之微服务架构的相关开发。

发展史

在这里插入图片描述

  • IBM(IT公司-国际商业机器公司)在1997年提出了EJB思想,早期的JAVAEE开发大都基于该思想。
  • Rod Johnson(Java和J2EE开发领域的专家)在2002年出版的Expert One-on-One J2EE Design and Development ,书中有阐述在开发中使用EJB该如何做。
  • Rod Johnson在2004年出版的Expert One-on-One J2EE Development without EJB ,书中提 出了比EJB思想更高效的实现方案,并且在同年将方案进行了具体的落地实现,这个实现就是 Spring1.0。
  • 随着时间推移,版本不断更新维护,目前最新的是Spring5
    • Spring1.0是纯配置文件开发
    • Spring2.0为了简化开发引入了注解开发,此时是配置文件加注解的开发方式
    • Spring3.0已经可以进行纯注解开发,使开发效率大幅提升
    • Spring4.0根据JDK的版本升级对个别API进行了调整
    • Spring5.0已经全面支持JDK8

Spring Framework系统架构

Spring Framework 是 Spring 生态圈中最基础的项目,是其他项目的根基

Spring Framework的发展也经历了很多版本的变更,每个版本都有相应的调整

在这里插入图片描述

Spring Framework的5版本目前没有最新的架构图,而最新的是4版本,所以接下来主要研究的 是4的架构图

在这里插入图片描述

  1. 核心层
    • Core Container:核心容器,这个模块是 Spring 最核心的模块,其他的都需要依赖该模块
  2. AOP层
    • AOP:面向切面编程,它依赖核心层容器,目的是在不改变原有代码的前提下对其进行功能增强
    • Aspect:AOP思想,Aspects 是对 AOP 思想的具体实现
  3. 数据层
    • Data Access:数据访问,Spring 全家桶中有对数据访问的具体实现技术
    • Data Integration:数据集成,Spring 支持整合其他数据层解决方案,比如MyBatis
    • Transactions:事务,Spring 中事务管理是 Spring AOP 的一个具体实现
  4. Web层
    • Spring MVC 框架
  5. Test层
    • Spring 主要整合了 Junit 来完成单元测试和集成测试

spring 核心概念

IOC、IOC容器、Bean、DI

在这里插入图片描述

在这个代码中,由于业务层代码中需要数据层的对象,导致两层之间的耦合度很高,针对这个问题 Spring 提出了一个解决方案:使用对象时,在程序中不要主动使用 new 产生对象,转换为由外部提供对象

  1. IOC(Inversion of Control)控制反转:控制反转的是对象的创建权

    • 使用对象时,由主动new产生对象转换为由外部提供对象,此过程中对象创建的控制权由程序转移到外部,此思想称为控制反转
    • Spring技术对IOC思想进行了实现,提供了一个容器,称为IOC容器,用来充当IOC思想的 “ 外部 ”
    • IOC容器负责对象的创建、初始化等一系列工作,被创建或被管理的对象在IOC容器中统称为 Bean对象
  2. DI(Dependency Injection)依赖注入:绑定对象与对象之间的依赖关系

    在这里插入图片描述

    但是现在又有一个问题,当 IOC 容器中创建好 service 和 dao 对象后,因为 service 运行需要依赖 dao 对象,但是 service 对象和 dao 对象没有任何关系,导致程序无法正确运行,而在容器中建立对象与对象之间的绑定关系就要用到 DI

    • 在容器中建立 bean 与 bean 之间的依赖关系的整个过程,称为依赖注入

介绍完 Spring 的 IOC 和 DI 的概念后,我们会发现这两个概念的最终目标就是:充分解耦,具体实现靠:

  • 使用 IOC 容器管理 bean(IOC)
  • 在 IOC 容器内将有依赖关系的 bean 进行关系绑定(DI)
  • 最终结果为:使用对象时不仅可以直接从 IOC 容器中获取,并且获取到的 bean 已经绑定了所有的依赖关系.
  1. IOC容器:Spring 创建了一个容器用来存放所创建的对象,这个容器就叫 IOC 容器
  2. Bean:容器中所存放的一个个对象就叫 Bean 或 Bean 对象

IOC快速入门

  1. 创建 Maven 项目

  2. pom.xml引入依赖

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.10.RELEASE</version>
    </dependency>
    
  3. resources下创建 spring 配置文件(applicationContext.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">
        <!--bean标签标示配置bean
        id属性标示给bean起名字
        class属性表示给bean定义类型
        -->
            <bean id="bookDao" class="项目下的Dao实现类路径"/>
            <bean id="bookService" class="项目下的Service实现类路径"/>
        </beans>
    

    注意事项: bean 定义时 id 属性在同一个配置文件中不能重复

  4. 使用 Spring 提供的接口完成 IOC 容器的创建

  5. 从容器中获取对象进行方法调用

    public class App {
        public static void main(String[] args) {
        //获取IOC容器
        ApplicationContext ctx = new
        ClassPathXmlApplicationContext("applicationContext.xml");//这里的这个xml就是刚才创建的Spring的配置文件
        BookDao bookDao = (BookDao) ctx.getBean("bookDao");//通过容器来获取Bean对象
        bookDao.save();
        }
    }
    
    

DI快速入门

实现依赖注入,必须要基于 IOC 管理 Bean

  1. 去除代码中的 new

  2. 为属性提供 setter 方法

    public class BookServiceImpl implements BookService {
        //删除业务层中使用new的方式创建的dao对象
        //private BookDao bookDao = new BookDaoImpl();
        private BookDao bookDao;
        public void save() {
            System.out.println("book service save ...");
            bookDao.save();
        }
        //提供对应的set方法
        public void setBookDao(BookDao bookDao) {
            this.bookDao = bookDao;	
        }
    }
    
  3. 在配置文件中添加依赖注入的配置

    <?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属性表示给bean起名字
            class属性表示给bean定义类型
        -->
        <bean id="bookDao" class="com.XXX.dao.impl.BookDaoImpl"/>
            
        <bean id="bookService" class="com.XXX.service.impl.BookServiceImpl">
            <!--配置server与dao的关系-->
            <!--property标签表示配置当前bean的属性
                name属性表示配置哪一个具体的属性
                ref属性表示参照哪一个bean
            -->
            <property name="bookDao" ref="bookDao"/>
        </bean>
    </beans>
    

    注意: 配置中的两个 bookDao 的含义是不一样的

    • name="bookDao"中bookDao的作用是让Spring的IOC容器在获取到名称后,将首字母大写,前 面加set找对应的setBookDao()方法进行对象注入
    • ref="bookDao"中 bookDao 的作用是让 Spring 能在IOC容器中找到id为bookDao的Bean对象给 bookService 进行注入

IOC

IOC 中对象的相关配置以及实例化方式和生命周期

Bean基础配置

id与class属性

在这里插入图片描述

注意: 因为 bookDao 和 bookService 一般在项目中是接口,接口不能实现,所以一般是使用它的实现类来进行对象的创建

name属性

在这里插入图片描述

XML中的配置写法

<!--name属性:为bean指定别名,别名可以有多个,使用逗号,分号,空格进行分隔-->
<!-- Ebi全称Enterprise Business Interface,翻译为企业业务接口 --> 
    <bean id="bookService" name="service service4 bookEbi"
        class="BookServiceImpl的类路径">
        <property name="bookDao" ref="bookDao"/>
    </bean>

    <bean id="bookDao" name="dao" class="BookDaoImpl的类路径"/>

注意:

  • 通过 ref 属性来指定 bean ,被指定的 bean 必须在容器中存在,而且 ref 属性值可以是 bean 的 name 属性值
  • 获取bean无论是通过id还是name获取,如果无法获取到,将抛出异常 NoSuchBeanDefinitionException
scope属性

在这里插入图片描述

注意:

  • bean 为单例的意思是在 Spring 的 IOC 容器中只会有该类的一个对象,避免了对象的频繁创建与销毁,达到了 bean 对象的复用,性能高
  • bean在容器中
    • 如果对象是有状态对象,即该对象有成员变量可以用来存储数据的,因为所有请求线程共用一个bean对象,所以会存在线程安全问题。
    • 如果对象是无状态对象,即该对象没有成员变量没有进行数据存储的,因方法中的局部变量在方法调用完成后会被销毁,所以不会存在线程安全问题。
  • 表现层对象、业务层对象、数据层对象、工具对象适合交给容器进行管理,封装实例的域对象,因为会引发线程安全问题,所以不适合交给容器进行管理

Bean的实例化

bean本质上就是对象,对象在new的时候会使用构造方法完成,那创建bean也是使用构造方法完成的,基于此,Bean 有三种创建方式

构造方法

Spring底层使用的是类的无参构造方法,而且是通过反射来访问到类中的方法的

静态工厂

这种方式一般是用来兼容早期的一些老系统,需要在Spring的配置文件中加入以下内容:

<bean id="orderDao" class="OrderDaoFactory类路径名" factory-method="getOrderDao"/>
<!-- class:工厂类的类全名 --> 
<!-- factory-mehod:具体工厂类中创建对象的方法名 --> 

在这里插入图片描述

虽然在工厂类中也是直接new对象,但是在工厂的静态方法中,我们除了new 对象外还可以做一些必不可少的业务操作

实例工厂

在Spring的配置文件中加入以下内容:

<bean id="userFactory" class="UserDaoFactory的类路径"/>
<bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/>

实例化工厂运行的顺序是:

  1. 创建实例化的工厂对象,对应的是第一行配置
  2. 调用对象中的方法来创建bean对象,对应的是第二行配置
    • factory-bean:工厂对象
    • factory-method:工厂对象中具体创建 bean 对象的方法名,对应关系如下:
    • 在这里插入图片描述
FactoryBean的使用(工厂实例的简化)

这种方式在Spring去整合其他框架的时候会被用到

让工厂类(这里以UserDaoFactory为例)实现FactoryBean接口,重写接口的方法:

  • 方法一: getObject(),被重写后,在方法中进行对象的创建并返回
  • 方法二: getObjectType(),被重写后,主要返回的是被创建类的Class对象
  • 方法三: 没有被重写,因为它已经给了默认值 true(单例,false为非单例),作用是设置对象是否为单例

在Spring的配置文件中进行配置

 <bean id="userDao" class="UserDaoFactoryBean类路径"/>

Bean的生命周期

bean 的生命周期:bean 对象,从创建到消亡的完整过程

bean 生命周期控制的是 bean 对象从创建后到销毁前做一些事情

关于 Spring 中对 bean 生命周期控制提供了两种方式:

  • 在配置文件中的 bean 标签中添加 init-method属性和destory-method属性
  • 类实现InitializingBean接口和DisposableBean接口
添加初始化和销毁方法
<bean id="bookDao" class="com.XXX.dao.impl.BookDaoImpl" init-method="init"
destroy-method="destory"/>

init-method 属性是初始化方法,而 destroy-method 属性是销毁的方法

但是执行后只会执行初始化的 init 方法而未执行销毁方法destory,为什么?

原因是 Spring 的 IOC 容器是运行在 JVM 中,运行 main 方法后,JVM 启动,Spring 加载配置文件生成 IOC 容器,从容器获取 bean 对象,然后调方法执行,main 方法执行完后,JVM 退出,这个时候 IOC 容器中的 bean 还未来得及销毁就已经结束了,所以没有调用对应的 destory 方法

close关闭容器

ApplicationContext 中没有 close 方法,所以需要将 ApplicationContext 更换为 ClassPathXmlApplicationContext(ClassPathXmlApplicationContext 是 ApplicationContext 的子类) 来调用 close 方法,这样就可以正常的执行容器的销毁了

注册钩子关闭容器

在容器未关闭之前,提前设置好回调函数,让 JVM 在退出之前回调此函数来关闭容器,调用 registerShutdownHook 方法

注意: RegisterShutdownHook 方法在 ApplicationContext 中也没有,所以依然要使用 ClassPathXmlApplicationContext

关闭方式的区别

close 方法和 RegisterShutdownHook 方法区别:

  • 相同点:两种方法都能用来关闭容器
  • 不同点:close 方法是在执行调用的时候关闭,registerShutdownHook 方法是在 JVM 退出前自动调用关闭
通过接口来简化关闭容器

在实现类中实现 InitializingBean 和 DisposableBean 两个接口之后重写 afterPropertiesSet 方法和 destory 方法

注意: InitializingBean 接口中的 afterPropertiesSet 方法,翻译过来为 属性设置之后,对于 servce 层中的实现类方法来说,Dao 层的实现类为它的一个属性,setxxxDao 方法是 Spring 的 IOC 容器为它注入属性的方法,而setxxxDao方法先执行,afterPropertiesSet 方法后执行

总结:

对于 bean 的生命周期控制在 bean 的整个生命周期中所处的位置

  • 初始化容器
    • 1.创建对象(内存分配)
    • 2.执行构造方法
    • 3.执行属性注入(set操作)
    • 4.执行 bean 初始化方法
  • 使用 bean
    • 执行业务操作
  • 关闭/销毁容器
    • 执行 bean 销毁方法

DI

Spring 中提供了两种注入方式:

  • setter 注入
    • 简单类型(基础数据类型和字符串)
    • 引用类型
  • 构造器注入
    • 简单类型(基础数据类型和字符串)
    • 引用类型

setter注入

  1. 在 bean 中定义引用类型属性,并提供可访问的 set 方法

    public class BookServiceImpl implements BookService {
        private BookDao bookDao;
        public void setBookDao(BookDao bookDao) {
            this.bookDao = bookDao;
        }
    }
    
  2. 配置中使用 property 标签 ref 属性注入引用类型对象

    <bean id="bookService" class="BookServiceImpl类路径">
        <property name="实现类中的bookDao的属性" ref="bean中的bookDao对象"/>
    </bean>
    
    <bean id="bookDao" class="BookDaoImpl类路径"/>
    
注入引用数据类型
  1. 在实现类中声明属性并提供 setter 方法

    public class BookServiceImpl implements BookService{
        private BookDao bookDao;
        private UserDao userDao;//声明属性
    
        public void setUserDao(UserDao userDao) {//提供setter方法
            this.userDao = userDao;
        }
        public void setBookDao(BookDao bookDao) {
            this.bookDao = bookDao;
        }
    
    }
    
  2. 配置文件中进行注入配置

    <bean id="bookDao" class="BookDaoImpl类路径"/>
    <bean id="userDao" class="UserDaoImpl类路径"/>
    
    <bean id="bookService" class="BookServiceImpl类路径">
        <property name="实现类中的bookDao属性" ref="bean中的bookDao对象"/>
        <property name="实现类中的userDao属性" ref="bean中的userDao对象"/>
    </bean>
    
注入简单数据类型
  1. 在实现类中声明对应的简单数据类型的属性,并提供对应的 setter 方法

    public class BookDaoImpl implements BookDao {
        private String databaseName;//声明属性
        private int connectionNum;//声明属性
        
        // 提供 setter 方法
        public void setConnectionNum(int connectionNum) {
        	this.connectionNum = connectionNum;
        }
        public void setDatabaseName(String databaseName) {
        	this.databaseName = databaseName;
        }
        
    }
    
  2. 在配置问文件中进行注入配置

    <bean id="bookDao" class="com.XXX.dao.impl.BookDaoImpl">
        <property name="实现类方法中的参数databaseName(形参)" value="形参中的数据值"/>
        <property name="实现类方法中的参数connectionNum(形参)" value="形参中的数据值"/>
    </bean>
    
    <bean id="userDao" class="com.XXX.dao.impl.UserDaoImpl"/>
    
    <bean id="bookService" class="BookServiceImpl类路径">
        <property name="实现类中的bookDao属性" ref="bean中的bookDao对象"/>
        <property name="实现类中的userDao属性" ref="bean中的userDao对象"/>
    </bean>
    

构造器注入

构造器注入也就是构造方法注入,无非是把实现类中的 setter 方法改为构造器方法

  1. 删除实现类中的 setter 方法并提供构造方法

    public class BookServiceImpl implements BookService{
        private BookDao bookDao;
        
        // 在构造方法中对属性进行设置
        public BookServiceImpl(BookDao bookDao) {
            this.bookDao = bookDao;
        }
        
    }
    
  2. 在配置文件中配置构造方法注入

    <bean id="bookDao" class="BookDaoImpl类路径"/>
    
    <bean id="bookService" class="BookServiceImpl类路径">
        <constructor-arg name="实现类中构造方法的参数bookDao(形参)" ref="bean中的bookDao对象"/>
        <!-- 这里的constructor-arg标签就是通过构造方法注入的标签 -->
    </bean>
    

注意:

  • name 属性对应的值为构造函数中方法形参的参数名,必须保持一致
  • ref 属性指向的是 spring 的 IOC 容器中其他的 bean 对象
构造器注入多个数据类型
  1. 提供多个属性的构造函数

    public class BookServiceImpl implements BookService{
        private BookDao bookDao;// 声明引用类型属性
        private UserDao userDao;// 声明引用类型属性
        
        // 构造方法注入
        public BookServiceImpl(BookDao bookDao,UserDao userDao) {
            this.bookDao = bookDao;
            this.userDao = userDao;
        }
    }
    
  2. 配置文件中配置

    <bean id="bookDao" class="BookDaoImpl类路径"/>
    <bean id="userDao" class="UserDaoImpl类路径"/>
    
    <bean id="bookService" class="BookServiceImpl类路径">
        <constructor-arg name="实现类中构造方法的参数bookDao" ref="bean中的bookDao对象"/>
        <constructor-arg name="实现类中构造方法的参数userDao" ref="bean中的userDao对象"/>
    </bean>
    

    注意: constructor-arg 标签的顺序可以任意

构造器注入多个简单数据类型
  1. 添加多个简单属性并提供构造方法

    public class BookDaoImpl implements BookDao {
        private String databaseName; //声明简单类型属性
        private int connectionNum; //声明简单类型属性
    
        // 通过构造器注入
        public BookDaoImpl(String databaseName, int connectionNum) {
            this.databaseName = databaseName;
            this.connectionNum = connectionNum;
        }
    }
    
  2. 配置文件中配置

    <bean id="bookDao" class="BookDaoImpl类路径">
        <constructor-arg name="实现类中构造方法的参数databaseName" value="参数的值"/>
        <constructor-arg name="实现类中构造方法的参数connectionNum" value="参数的值"/>
    </bean>
    
    <bean id="userDao" class="UserDaoImpl类路径"/>
    
    <bean id="bookService" class="BookServiceImpl类路径">
        <constructor-arg name="实现类中构造方法的参数bookDao" ref="bean中的bookDao对象"/>
        <constructor-arg name="实现类中构造方法的参数userDao" ref="bean中的userDao对象"/>
    </bean>
    
    
解决构造函数中参数名紧耦合

当构造函数中方法的参数名发生变化后,配置文件中的name属性也需要跟着变 ,两块存在紧耦合

解决方法:

方式一:删除 name 属性,添加 type 属性,按照类型注入

<bean id="bookDao" class="BookDaoImpl类路径">
    <constructor-arg type="int" value="10"/>
    <constructor-arg type="java.lang.String" value="mysql"/>
</bean>
  • 这种方式可以解决构造函数形参名发生变化带来的耦合问题
  • 但是如果构造方法参数中有类型相同的参数,这种方式就不太好实现了

方式二:删除 type 属性,添加 index 属性,按照索引下标注入,下标从0开始

<bean id="bookDao" class="BookDaoImpl类路径">
    <constructor-arg index="1" value="100"/>
    <constructor-arg index="0" value="mysql"/>
</bean>
  • 这种方式可以解决参数类型重复问题
  • 但是如果构造方法参数顺序发生变化后,这种方式又带来了耦合问题

介绍完这两种参数的注入方式,具体我们该如果选择?

  1. 强制依赖使用构造器进行,使用setter注入有概率不进行注入导致 null 对象出现
    • 强制依赖指对象在创建的过程中必须要注入指定的参数
  2. 可选依赖使用 setter 注入进行,灵活性强
    • 可选依赖指对象在创建过程中注入的参数可有可无
  3. Spring 框架倡导使用构造器,第三方框架内部大多数采用构造器注入的形式进行数据初始化,相对严谨
  4. 如果有必要可以两者同时使用,使用构造器注入完成强制依赖的注入,使用 setter 注入完成可选依赖的注入
  5. 实际开发过程中还要根据实际情况分析,如果受控对象没有提供 setter 方法就必须使用构造器注入
  6. 自己开发的模块推荐使用 setter 注入

自动装配

相对于之前的手动配置比较麻烦,Spring 提供了自动装配的方式

什么是依赖自动装配

IOC 容器根据 bean 所依赖的资源在容器中自动查找并注入到 bean 中的过程称为自动装配

自动装配的方式有哪些?
  • 按类型(常用)
  • 按名称
  • 按构造方法
  • 不启用自动装配
按照类型完成自动装配的配置

自动装配只需要修改 xml 配置文件即可

  1. 在 bean 标签中移除 property 标签
  2. 在 bean 标签中添加 autowire 属性

按照类型注入的配置

<bean class="BookDaoImpl类路径"/>
<!--autowire属性:开启自动装配,通常使用按类型装配-->
<bean id="bookService" class="BookServiceImpl类路径" autowire="byType"/>

注意:

  • 需要注入的属性的类中对应属性的 setter 方法不能省略
  • 被注入的对象必须要被 Spring 的 IOC 容器管理
  • 按照类型在 Spring 的 IOC 容器中如果找到多个对象,会报NoUniqueBeanDefinitionExcepetion
按照名称完成自动注入装配的配置

如果一个类型在 IOC 中有多个对象,还想要注入成功,这个时候就需要按照名称注入,配置方式:

<bean class="BookDaoImpl类路径"/>
<!--autowire属性:开启自动装配,通常使用按类型装配-->
<bean id="bookService" class="BookServiceImpl类路径" autowire="byName"/>

注意:

  • 按照名称注入中的名称是指对象实现类中的 set 方法的名称去掉 set 后剩下的部分的首字母小写的名称

  • 在这里插入图片描述

  • 以上图为例,因为 bookDao 是 private 修饰的,外部类无法直接访问,而外部类访问只能通过属性的 set 方法进行访问

  • 而 set 方法生成的默认规则是,set 方法把属性名的首字母大写前面加上 set 形成方法名,所以按照名称注入,其实是和对应的 set 方法有关,但是如果按照标准起名称,属性名和 set 对应的名是一致的

  • 如果按照名称去找对应的 bean 对象,找不到则注入 Null

  • 当某一个类型在 IOC 容器中有多个对象,按照名称注入只找其指定名称对应的 bean 对象,不会报错

注意事项(配置特征)
  1. 自动装配用于引用类型依赖注入,不能对简单类型进行操作
  2. 使用按类型装配时(byType)必须保障容器中相同类型的 bean 唯一
  3. 使用按名称装配时(byName)必须保障容器中具有指定名称的bean,因变量名与配置耦合
  4. 自动装配优先级低于 setter 注入与构造器注入,同时出现时自动装配配置失效

集合注入

数据还有一种类型是集合,而 Spring 当然也支持集合的注入

Spring 中支持的集合类型

  • Array

    • <!-- 注入数组类型数据 -->
      <property name="array">
          <array>
              <value>100</value>
              <value>200</value>
              <value>300</value>
          </array>
      </property>
      
  • List

    • <!-- 注入List类型数据 -->
      <property name="list">
          <list>
              <value>caixunkun</value>
              <value>xiaoheizi</value>
              <value>ikun</value>
              <value>rap</value>
          </list>
      </property>
      
  • Set

    • <!-- 注入Set类型数据 -->
      <property name="set">
          <set>
              <value>caixunkun</value>
              <value>xiaoheizi</value>
              <value>ikun</value>
              <value>rap</value>
          </set>
      </property>
      
  • Map

    • <!-- 注入Map类型数据 -->
      <property name="map">
          <map>
              <entry key="country" value="china"/>
              <entry key="province" value="henan"/>
              <entry key="city" value="kaifeng"/>
          </map>
      </property>
      
  • Properties

    • <!-- 注入Properties类型数据 -->
      <property name="properties">
          <props>
              <prop key="country">china</prop>
              <prop key="province">henan</prop>
              <prop key="city">kaifeng</prop>
          </props>
      </property>
      

注意:

  • property 标签是 bean 标签下的子标签
  • property 标签表示 setter 方式注入,构造方法注入 construstor-arg 标签内部也可以写array、list、set、map 标签
  • List 底层也是通过数组实现的,所以 list 和 array 标签是可以混用的
  • 集合中要添加引用类型,只需要把 value 标签改成 ref 标签,这种方式用的比较少

加载 properties 文件

对于有些数据写在配置文件中不利于后期维护,那么需要将这些值提取到一个外部的 properties 文件中,然后让 Spring 框架从外部 properties 文件中读取属性值

  1. 准备 properties 配置文件

    • resource 下创建一个 xxx.properties 文件,并添加对应的属性键值对,比如:

    • jdbc.driver=com.mysql.jdbc.Driver
      jdbc.url=jdbc:mysql://127.0.0.1:3306/spring_db
      jdbc.username=root
      jdbc.password=root
      
  2. 开启 context 命名空间

    • 在 applicationContext.xml(也就是 bean 对象定义的配置文件)中开启context命名空间

    • <?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">
      </beans>
      
    • 在这里插入图片描述

  3. 加载 properties 配置文件

    • <context:property-placeholder location="jdbc.properties"/>
      
    • 在配置文件中使用context命名空间下的标签来加载 properties 配置文件

  4. 完成属性注入

    • 使用 ${key} 来读取 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"
          <!-- 开启context空间 -->
          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               
              <!-- 开启context空间 -->
              http://www.springframework.org/schema/context
              http://www.springframework.org/schema/context/spring-context.xsd">
          
          <!-- 加载配置文件 -->
          <context:property-placeholder location="jdbc.properties"/>
          
          <!-- 属性注入 -->
          <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
              <property name="driverClassName" value="${jdbc.driver}"/>
              <property name="url" value="${jdbc.url}"/>
              <property name="username" value="${jdbc.username}"/>
              <property name="password" value="${jdbc.password}"/>
          </bean>
      </beans>
      
读取单个属性
  1. 在实现类中添加对应的属性和属性的设置(setter)方法

    • 这里实现类用 BookDao 的实现类 BookDaoImpl
  2. 完成配置文件的读取与注入

    • 在 applicationContext.xml 添加配置,bean的配置管理、读取外部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="jdbc.properties"/>
        
        <bean id="bookDao" class="BookDaoImpl类路径">
            <!-- 属性注入 -->
            <property name="name" value="${jdbc.driver}"/>
        </bean>
    </beans>
    

注意事项:

  • 问题一:properties 配置文件中,配置键值对的优先级问题

    • 在配置文件中 key 键为 username 这个使用会有问题,假设给其设置的 value 值为 root666,而打印出来的则不是 root666 而是自己电脑的用户名

    • 因为 context:property-placeholer 标签会加载系统的环境变量,而环境变量的值会优先加载

    • 要解决需要在 context:property-placeholer 标签中加一个 system-properties-mode 属性并给他值设置为 NEVER,当然还有就是不使用username作为属性的key

    • <context:property-placeholder location="jdbc.properties" system-properties-mode="NEVER"/>
      
  • 问题二:当有多个 properties 配置文件需要被加载

    1. 调整配置文件内容,在 resource 下添加多个配置文件

    2. 修改 applicationContext.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:property-placeholder
              location="jdbc.properties,jdbc2.properties" system-properties-mode="NEVER"/>
              <!--方式二-->
              <context:property-placeholder location="*.properties" systemproperties-mode="NEVER"/>
              <!--方式三-->
              <context:property-placeholder location="classpath:*.properties"
              system-properties-mode="NEVER"/>
              <!--方式四-->
              <context:property-placeholder location="classpath*:*.properties"
              system-properties-mode="NEVER"/>
      </beans>
      
    • 注意:

    • 方式一:如果配置文件多的话,需要每个都配置

    • 方式二:: *.properties代表所有以properties结尾的文件都会被加载,可以解决方式一的问题,但是不标准

    • 方式三:标准的写法,classpath:代表的是从根路径下开始查找,但是只能查询当前项目的根路径

    • 方式四:不仅可以加载当前项目还可以加载当前项目所依赖的所有项目的根路径下的properties配置文件

核心容器

这里说的核心容器可以简单的理解为ApplicationgContext

容器

容器的创建方法有两种:

  • ApplicationContext ctx = new ClassPathXmlApplicationContext(“applicationContext.xml”);

    • 翻译过来为:类路径下的 XML 配置文件
  • ApplicationContext ctx = new FileSystemXmlApplicationContext(“D:\workspace\spring\spring_10_container\s rc\main\resources\applicationContext.xml”);

    • 翻译过来为:文件系统下的 XML 配置文件
    • 该方式是从项目路径下开始查找applicationContext.xml,所以要把括号内参数修改为绝对路径(也就是从盘符开始写起)

Bean 的三种获取方式

  • 方式一:

    • BookDao bookDao = (BookDao) ctx.getBean(“bookDao”);

    • 这种方式存在的问题就是获取的时候都需要进行类型转换

  • 方式二:

    • BookDao bookDao = ctx.getBean(“bookDao”, BookDao.class);

    • 这种方式可以解决类型强转问题,但是参数又多加了一个,相对来说没有简化多少

  • 方式三:

    • BookDao bookDao = ctx.getBean(BookDao.class);

    • 这种方式就类似依赖注入中的按类型注入,必须要确保 IOC 容器中该类型对应的 bean 对象只能有一个。

Bean 标签属性的总结

在这里插入图片描述

容器类层次结构

在 IDEA 中双击shift,输入 BeanFactory,点击进入 BeanFactory 类,ctrl+h,就能查看到如下结构的层次关系中可以看出,容器类也是从无到有根据需要一层层叠加上来的,BeanFactory 是 IoC 容器的顶层接口,ApplicationContext 接口是 Spring 容器的核心接口,接口提供基础的 bean 操作相关方法,通过其他接口扩展其功能

在这里插入图片描述

BeanFactory的使用

使用BeanFactory来创建IOC容器的具体实现方式为:

public class AppForBeanFactory {
    public static void main(String[] args) {
        Resource resources = new ClassPathResource("applicationContext.xml");
        BeanFactory bf = new XmlBeanFactory(resources);
        BookDao bookDao = bf.getBean(BookDao.class);
        bookDao.save();
    }
}

BeanFactory 和 ApplicationContext 区别:

  • BeanFactory 是延迟加载,只有在获取 bean 对象的时候才会去创建
  • ApplicationContext 是立即加载,容器加载的时候就会创建 bean 对象

ApplicationContex 要想称为延迟加载,只需要如下配置

<?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标签下使用 lazy-init属性,值设置为true -->
    <bean id="bookDao" class="BookDaoImpl类路径" lazy-init="true"/>
</beans>

依赖注入相关总结:

在这里插入图片描述

IOC/DI注解开发

在 Spring 发展到2.0的时候提供了注解开发,而且通过配置文件的开发发现太过于繁琐,那么接下来是 Spring2.5 的注解开发

注解开发定义bean

步骤:

  1. 删除原 XML 配置(将配置文件中的 bean 标签删除掉)

    <bean id="bookDao" class="BookDaoImpl实现类"/>
    
  2. Dao 上添加注解

    @Component("bookDao")
        public class BookDaoImpl implements BookDao {
            public void save() {
            System.out.println("book dao save ..." );
        }
    }
    
    • 注意: @Componet注解不可以添加在接口上,因为接口是无法创建对象的
    • XML与注解配置的对应关系:
  3. 配置 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">
          <context:component-scan base-package="com.xxx"/>
      </beans>
      
    • 说明:

      • component-scan
      • component:组件,Spring 将管理的 bean 视作自己的一个组件
      • scan:扫描
      • base-package 指定 Spring 框架扫描的包路径,它会扫描指定包及其子包中的所有类上的注解。
      • 包路径越多[如:com.XXX.dao.impl],扫描的范围越小速度越快
      • 包路径越少[如:com.XXX],扫描的范围越大速度越慢
      • 一般扫描到项目的组织名称即Maven的groupId下[如:com.XXX]即可。
  4. Service 上添加注解

    • 在 BookServiceImpl 类上也添加@Component交给 Spring 框架管理

    • @Component
      public class BookServiceImpl implements BookService {
          private BookDao bookDao;
              public void setBookDao(BookDao bookDao) {
              this.bookDao = bookDao;
          }
          public void save() {
              System.out.println("book service save ...");
              bookDao.save();
          }
      }
      
      
    • @Component注解如果不起名称,会有一个默认值就是当前类名首字母小写,所以也可以按照名称获取,如

    • BookService bookService = (BookService)ctx.getBean("bookServiceImpl");
      
    • 对于@Component注解,还衍生出了其他三个注解@Controller@Service@Repository 通过查看源码会发现:在这里插入图片描述

    • 这三个注解和@Component注解的作用是一样的,方便后期在编写类的时候能很好的区分出这个类是属于表现层、业务层还是数据层的类。

    • 名称@Component/@Controller/@Service/@Repository
      类型类注解
      位置类定义上方
      作用设置该类为 spring 管理的 bean
      属性value(默认):定义 bean 的id

纯注解开发模式

上面虽然是通过注解简化了一些操作,但是还是有配置文件,显得繁琐,Spring3.0 提供了纯注解开发,使用 Java 类替代配置文件,开启了 Spring 快速开发

  1. 创建配置类

  2. 标识该类为配置类

    @Configuration
    //配置类注解,标识了该类为配置类,用来替代applicationContext.xml
    public class SpringConfig {//配置类
    }
    
  3. 用注解替换包扫描配置

    //在配置类上添加包扫描注解@ComponentScan替换application.xml文件中的<context:component-scan base-package=""/>
    @Configuration
    @ComponentScan("com.XXX")
    public class SpringConfig {
    }
    // application.xml文件就可以退休了
    

至此,纯注解开发方式已完成,主要包括:

  • Java 类替换 Spring 核心配置文件

  • @Configuration 注解用于设定当前类为配置类

  • @ComponentScan注解用于设定扫描路径,此注解只能添加一次,多个数据用数组格式

     @ComponentScan({com.XXX.service","com.XXX.dao"})
    
  • 读取 Spring 核心配置文件初始化容器对象切换为读取 Java 配置类初始化容器对象

    //加载配置文件初始化容器
    ApplicationContext ctx = new
    ClassPathXmlApplicationContext("applicationContext.xml");
    //加载配置类初始化容器
    ApplicationContext ctx = new
    AnnotationConfigApplicationContext(SpringConfig.class);
    
    
  • 名称@Configuration
    类型类注释
    位置类定义上方
    作用设置该类为 Spring 配置类
    位置value(默认值):定义 bean 的id
  • 名称@ComponentScan
    类型类注解
    位置类定义上方
    作用设置 Spring 配置类扫描路径,用于加载使用注解格式定义的 bean
    位置value(默认值):扫描路径,此路径可以逐层向下扫描
  • applicationContext.xml 中<context:component-san/>的作用是指定扫描包路径,注解为@ComponentScan

  • @Configuration标识该类为配置类,使用类替换applicationContext.xml文件

  • ClassPathXmlApplicationContext 是加载XML配置文件

  • AnnotationConfigApplicationContext 是加载配置类

注解开发bean作用范围与生命周期管理

作用范围主要是指是否是单例模式,而生命周期则是指初始化和销毁的方法定义

bean的作用范围

使用@Scope注解

@Repository
//@Scope设置bean的作用范围
@Scope("prototype")
public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println("book dao save ...");
    }
}
名称@Scope
类型类注解
位置类定义上方
作用设置该类创建对象的作用范围,可用于设置创建出的 bean 是否是单例对象
属性value:定义 bean 作用范围,默认值 singleton(单例),可选填 prototype(非单例)
bean的生命周期
  1. 在 BookDaoImpl 中添加两个方法,initdestroy,方法名可以任意

  2. 在对应的方法上添加@PostConstruct@PreDestroy 注解即可

    @Repository
    public class BookDaoImpl implements BookDao {
        public void save() {
            System.out.println("book dao save ...");
        }
        @PostConstruct //在构造方法之后执行,替换 init-method
        public void init() {
            System.out.println("init ...");
        }
        @PreDestroy //在销毁方法之前执行,替换 destroy-method
        public void destroy() {
            System.out.println("destroy ...");
        }
    }
    

注意:

  • @PostConstruct@PreDestory注解如果找不到,需要导入以下 Jar 包

    <dependency>
        <groupId>javax.annotation</groupId>
        <artifactId>javax.annotation-api</artifactId>
        <version>1.3.2</version>
    </dependency>
    
  • 因为从 JDK9之后 jdk 中的 javax.annotation 包被移除了,这两个注解刚好就在这个包中

名称@PostConstruct
类型方法注解
位置方法上
作用设置该方法为初始化方法
属性
名称@PreDestroy
类型方法注解
位置方法上
作用设置该方法为销毁方法
属性

在这里插入图片描述

注解开发依赖注入

Spring 为了使用注解简化开发,并没有提供构造函器注入、setter注入对应的注解,只提供了自动装配的注解实现。

注解实现按照类型注入

在 BookServiceImpl 类的 bookDao 属性上添加@Autowired注解

@Service
public class BookServiceImpl implements BookService {
    @Autowired
    private BookDao bookDao;
    // public void setBookDao(BookDao bookDao) {
    // this.bookDao = bookDao;
    // }
    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
    }
}

注意:

  • @Autowired可以写在属性上,也可以写在 setter 方法上,最简单的处理方式是写在属性上并将setter方法注释掉
  • 自动装配是基于反射创建对象并通过暴力反射为私有属性进行设值
  • 普通反射只能获取 public 修饰的内容
  • 暴力反射除了获取 public 修饰的内容还可以获取 private 修饰的内容,所以无需提供 setter 方法
  • @Autowired是按照类型注入,那么对应 BookDao 接口如果有多个实现类,或导致报错,那么就需要使用按照名称注入
名称@Autowired
类型属性注解或方法注解或方法形参注解
位置属性定义上方或标准 set 方法上方或类 set 方法上方或方法形参前面
作用为引用类型属性设置值
属性required:true/false,定义该属性是否允许为null
注解实现按照名称注入

当根据类型在容器中找到多个 bean,注入参数的属性名又和容器中bean的名称不一致,这个时候就需要使用到@Qualifier来指定注入哪个名称的bean对象。

@Service
public class BookServiceImpl implements BookService {
    @Autowired
    @Qualifier("bookDao1")
    private BookDao bookDao;
        public void save() {
        System.out.println("book service save ...");
        bookDao.save();
    }
}

注意: @Qualifier注解后的值就是需要注入的bean的名称,@Qualifier不能单独使用,必须和@Autowired一起使用

名称@Qualifier
类型属性注解或方法注解
位置属性定义上方或标准 set 方法上方或类 set 方法上方或
作用为引用类型属性指定注入的 beanId
属性value:设置注入的 beanId
简单数据类型注入

简单类型注入的是基本数据类型或者字符串类型

假设在 BookDaoImpl 类中添加一个 name 属性,用其进行简单类型注入

@Repository("bookDao")
public class BookDaoImpl implements BookDao {
    private String name;
    public void save() {
        System.out.println("book dao save ..." + name);
    }
}

数据类型换了,对应的注解也要跟着换,这次使用@Value注解,将值写入注解的参数中就行了

@Repository("bookDao")
public class BookDaoImpl implements BookDao {
    @Value("XXXX")
    private String name;
    public void save() {
        System.out.println("book dao save ..." + name);
    }
}

注意: 数据格式要匹配,如将 “abc” 注入给 int 值,这样程序就会报错。

名称@Value
类型属性注解或方法注解
位置属性定义上方或标准 set 方法上方或类 set 方法上方或
作用为基本数据类型或字符串类型属性设置值
属性value:要注入的属性值
注解读取 properties 配置文件

@Value一般会被用在从 properties 配置文件中读取内容进行使用

接下来进行演示

  1. 准备 properties 文件

    //jdbc.properties
    name=XXXX888
    
  2. 加载 properties 文件

    @Configuration
    @ComponentScan("com.XXX")
    @PropertySource("jdbc.properties")
    public class SpringConfig {
    }
    
  3. 使用value读取配置文件中的内容

    @Repository("bookDao")
    public class BookDaoImpl implements BookDao {
        @Value("${name}")
        private String name;
        public void save() {
            System.out.println("book dao save ..." + name);
        }
    }
    

注意:

  • 如果读取的 properties 配置文件有多个,可以使用@PropertySource的属性来指定多个

    @PropertySource({"jdbc.properties","xxx.properties"})
    
  • @PropertySource注解属性中不支持使用通配符*,运行会报错的

    @PropertySource({"*.properties"})
    
  • @PropertySourcez注解属性中可以把classpath:加上,代表从当前项目的根路径找文件,而且也不可以加上*

    @PropertySource({"classpath:jdbc.properties"})
    
名称@PropertySource
类型类注解
位置类定义上方
作用加载 properties 文件中的属性值
属性value:设置加载的 properties 文件对应的文件名或文件名组成的数组

IOC/DI注解开发管理第三方bean

对于第三方在 jar 包中的类,需要使用@Bean注解

注解开发管理第三方bean

以对Druid数据源的管理为例

  1. 导入 Druid 对应的 jar 包

    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.16</version>
    </dependency>
    
    
  2. 在配置类中添加一个方法

    @Configuration
    public class SpringConfig {
        public DataSource dataSource(){
            //该方法的返回值就是要创建的Bean对象类型
            DruidDataSource ds = new DruidDataSource();
            ds.setDriverClassName("com.mysql.jdbc.Driver");
            ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
            ds.setUsername("root");
            ds.setPassword("root");
            return ds;
        }
    }
    
  3. 在方法上添加@Bean注解

    @Configuration
    public class SpringConfig {
        @Bean
        //@Bean注解的作用是将方法的返回值制作为Spring管理的一个bean对象
        public DataSource dataSource(){
            DruidDataSource ds = new DruidDataSource();
            ds.setDriverClassName("com.mysql.jdbc.Driver");
            ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
            ds.setUsername("root");
            ds.setPassword("root");
            return ds;
        }
    }
    

注意: 不能使用DataSource ds = new DruidDataSource()接口来实例化,因为 DataSource 接口中没有对应的 setter 方法来设置属性。

名称@Bean
类型方法注解
位置方法定义上方
作用设置该方法的返回值作为 Spring 管理的 bean
属性value:定义 bean 的 id
引入外部配置类

如果把所有的第三方 bean 都配置到 Spring 的配置类 SpringConfig 中,虽然可以,但是不利于代码阅读和分类管理,可以按照类别将这些 bean 配置到不同的配置类中

比如:

public class JdbcConfig {
    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
        ds.setUsername("root");
        ds.setPassword("root");
        return ds;
    }
}

这个配置类如何能被 Spring 配置类加载到,并创建 DataSource 对象在 IOC 容器中

使用包扫描引入
  1. 在 Spring 配置类上添加包扫描

    @Configuration
    @ComponentScan("com.XXX.config")
    public class SpringConfig {
    }
    
  2. 在 JdbcConfig 上添加配置注解

    @Configuration
    public class JdbcConfig {
        @Bean
        public DataSource dataSource(){
            DruidDataSource ds = new DruidDataSource();
            ds.setDriverClassName("com.mysql.jdbc.Driver");
            ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
            ds.setUsername("root");
            ds.setPassword("root");
            return ds;
        }
    }
    

这种方式虽然能够扫描到,但是不能很快的知晓都引入了哪些配置类

使用@Import注解引入
  1. 去除 JdbcConfig 类上的注解

    public class JdbcConfig {
        @Bean
        public DataSource dataSource(){
            DruidDataSource ds = new DruidDataSource();
            ds.setDriverClassName("com.mysql.jdbc.Driver");
            ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
            ds.setUsername("root");
            ds.setPassword("root");
            return ds;
        }
    }
    
    
  2. 在 Spring 配置类中引入

    @Configuration
    //@ComponentScan("com.XXX.config")
    @Import({JdbcConfig.class})
    public class SpringConfig {
    }
    

注意:

  • @Import参数需要的是一个数组,可以引入多个配置类。

  • @Import注解在配置类中只能写一次,下面的方式是不允许的

    @Configuration
    //@ComponentScan("com.XXX.config")
    @Import(JdbcConfig.class)
    @Import(Xxx.class)
    public class SpringConfig {
    }
    
    
名称@Import
类型类注解
位置类定义上方
作用导入配置类
属性value:定义导入的配置类类名,当配置类有多个时使用数组格式一次性导入多个配置类
注解开发实现为第三方bean注入资源

在使用@Bean创建bean对象的时候,如果方法在创建的过程中需要其他资源,这些资源会有两大类,分别是简单数据类型和引用数据类型

以下面 Durid 的管理为例

public class JdbcConfig {
    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
        ds.setUsername("root");
        ds.setPassword("root");
        return ds;
    }
}
简单数据库类型
  1. 类中提供四个属性

  2. 使用@Value注解引用

    public class JdbcConfig {
        @Value("com.mysql.jdbc.Driver")
        private String driver;
        @Value("jdbc:mysql://localhost:3306/spring_db")
        private String url;
        @Value("root")
        private String userName;
        @Value("password")
        private String password;
        
        @Bean
        public DataSource dataSource(){
            DruidDataSource ds = new DruidDataSource();
            ds.setDriverClassName(driver);
            ds.setUrl(url);
            ds.setUsername(userName);
            ds.setPassword(password);
            return ds;
        }
    }
    

但是一般数据库连接的四要素是写在 properties 配置文件中的

  1. resources目录下添加 jdbc.properties
  2. 配置文件中提供四个键值对分别是数据库的四要素
  3. 使用@PropertySource加载 jdbc.properties 配置文件
  4. 修改@Value注解属性的值,将其修改为 ${key},key 就是键值对中的键的值
引用数据类型

假设在构建 DataSource 对象的时候,需要用到 BookDao 对象,该如何把 BookDao 对象注入进方法内

  1. 在 SpringConfig 中扫描 BookDao

    //扫描的目的是让Spring能管理到BookDao,也就是说要让IOC容器中有一个bookDao对象
    @Configuration
    @ComponentScan("com.XXX.dao")
    @Import({JdbcConfig.class})
    public class SpringConfig {
    }
    
  2. 在 JdbcConfig 类的方法上添加参数

    @Bean
    public DataSource dataSource(BookDao bookDao){//直接将需要的类对象作为参数,写在方法的参数位置
        System.out.println(bookDao);
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(userName);
        ds.setPassword(password);
        return ds;
    }
    //引用类型注入只需要为bean定义方法设置形参即可,容器会根据类型自动装配对象。
    
XML开发和注解的对比

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1570423.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

160 Linux C++ 通讯架构实战14,epoll 反应堆模型

到这里&#xff0c;我们需要整理一下之前学习的epoll模型&#xff0c;并根据之前的epoll模型&#xff0c;提出弊端&#xff0c;进而整理epoll反应堆模型&#xff0c;进一步深刻理解&#xff0c;这是因为epoll实在是太重要了。 复习之前的epoll的整体流程以及思路。 参考之前写…

DHCP工作过程以及抓包分析

从PC1的e0/0/1接口进行抓包 客户端基于UDP、源端口68、目标端口67进行广播请求&#xff0c;源IP0.0.0.0&#xff0c;&#xff08;无效地址&#xff0c;代表本地无地址&#xff09;目标IP255.255.255.255&#xff1b; 从下面截图可以看出&#xff1a; 源mac为电脑mac&#xff…

采用C#.net6.0+Vue,Ant-Design技术开发的一套大型医院手术麻醉信息系统源码,系统成熟,运行稳定

手术麻醉信息系统源码&#xff0c;C#手麻系统源码&#xff0c;自主版权应用案例&#xff08;适合上项目&#xff09; 手术麻醉信息系统可以实现手术室监护仪、麻醉机、呼吸机、输液泵等设备输出数据的自动采集&#xff0c;采集的数据能据如实准确地反映患者生命体征参数的变化&…

【Leetcode笔记】102.二叉树的层序遍历

目录 知识点Leetcode代码&#xff1a;ACM模式代码&#xff1a; 知识点 vector、queue容器的操作 对vector<int> vec;做插入元素操作&#xff1a;vec.push_back(x)。对queue<TreeNode*> que;做插入元素操作&#xff1a;que.push(root);。队列有四个常用的操作&…

Redis从入门到精通(六)Redis实战(三)优惠券秒杀

↑↑↑下载测试项目原代码↑↑↑ 文章目录 前言4.3 优惠券秒杀4.3.1 数据表与实体类4.3.2 添加优惠券4.3.2.1 添加普通券代码4.3.2.2 添加秒杀券代码 4.3.3 实现秒杀下单4.3.3.1 秒杀下单逻辑分析4.3.3.2 获取秒杀订单ID4.3.3.3 获取用户ID4.3.3.4 实现秒杀下单 前言 Redis实战…

团体程序设计天梯赛-练习集 01

天梯赛题解合集 团体程序设计天梯赛-练习集 (L1-001 - L1-012) 团体程序设计天梯赛-练习集 (L1-013 - L1-024) 团体程序设计天梯赛-练习集 (L1-025 - L1-036) 团体程序设计天梯赛-练习集 (L1-037 - L1-048) L1-001 Hello World 输出题 样例 输入 输出 Hello World!思…

笔记本电脑win7 Wireless-AC 7265连不上wifi6

1.背景介绍 旧路由器连接人数有限&#xff0c;老旧&#xff0c;信号不稳定更换了新路由器&#xff0c;如 TL-XDR5430易展版用户电脑连不上新的WIFI网络了&#xff0c;比较着急 核心问题&#xff1a;有效解决笔记本连接wifi上网问题&#xff0c;方法不限 2.环境信息 Windows…

深入探索MySQL:成本模型解析与查询性能优化,及未来深度学习与AI模型的应用展望

码到三十五 &#xff1a; 个人主页 在数据库管理系统中&#xff0c;查询优化器是一个至关重要的组件&#xff0c;它负责将用户提交的SQL查询转换为高效的执行计划。在MySQL中&#xff0c;查询优化器使用了一个称为“成本模型”的机制来评估不同执行计划的优劣&#xff0c;并选择…

No dashboards are active for the current data set.

再次记录一下这个离谱的问题 之前出现这个问题是因为目录没写对 今天遇到这个问题的原因是目录是对的&#xff0c;跟目录是否带有中文也没关系 是writer写入的时候写的是空的&#xff0c;离谱的是写入是空的情况下也会生成events日志文件&#xff0c;看起来好像成功写入了一样&…

朗之万方程,机器学习与液体中的粒子运动

目录 一、说明二、朗之万方程的诞生2.1 牛顿力学2.2 流体中的随机运动 三、小质量物体布朗运动方程四、布朗运动的Python代码五、稳定性讨论5.1 波尔兹曼分布5.2 梯度下降算法 六、随机梯度下降&#xff08;SGD&#xff09;和小批量梯度下降七、机器学习与物理&#xff0c;作为…

C++ 类(初篇)

类的引入 C语言中&#xff0c;结构体中只能定义变量&#xff0c;在C中&#xff0c;结构体内不仅可以定义变量&#xff0c;也可以定义函数。 而为了区分C和C我们将结构体重新命名成class去定义 类的定义 标准格式&#xff1a; class className {// 类体&#xff1a;由成员函…

练习14 Web [极客大挑战 2019]Upload

phtml格式绕过&#xff0c;burp修改content-type绕过&#xff0c;常见的文件上传存放目录名 题目就叫upload&#xff0c;打开靶机 直接上传一个图片格式的一句话木马&#xff0c;返回如下&#xff1a; 提交练习5和9中的两种可以执行图片格式php代码的文件&#xff0c;修改con…

Three.js真实相机模拟

有没有想过如何在 3D Web 应用程序中模拟物理相机&#xff1f; 在这篇博文中&#xff0c;我将向你展示如何使用 Three.js和 OpenCV 来完成此操作。 我们将从模拟针孔相机模型开始&#xff0c;然后添加真实的镜头畸变。 具体来说&#xff0c;我们将仔细研究 OpenCV 的两个失真模…

VMware提示 该虚拟机似乎正在使用中,如何解决?

VMware提示 该虚拟机似乎正在使用中,如何解决&#xff1f; 问题描述解决方法1.找到安装VMware的文件目录2.在VMware目录下.lck后缀的文件夹删除或重命名3.运行VMware 问题描述 该虚拟机似乎正在使用中。 如果该虚拟机未在使用&#xff0c;请按“获取所有权(T)”按钮获取它的所…

github生成新的SSH密钥

首先是参考官方文档 生成新的 SSH 密钥并将其添加到 ssh-agent述 当你在创建SSH密钥时遇到提示&#xff1a; Enter file in which to save the key (/c/Users/YOU/.ssh/id_ALGORITHM):这一步是让你选择保存生成的SSH密钥对的文件名和位置。如果你直接按回车键&#xff08;[Pr…

数据结构入门系列-栈的结构及栈的实现

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” 栈 栈的概念及结构 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一段进行插入和删除元素操作&#xff0c;进行数据输入和删除操作的一端称为栈顶&#xff0c;另…

一、Docker部署GitLab(详细步骤)

Docker部署GitLab&#xff08;详细步骤&#xff09; 一、拉取镜像二、启动容器三、修改配置四、修改密码五、浏览器访问 一、拉取镜像 docker安装教程&#xff1a;https://qingsi.blog.csdn.net/article/details/131270071 docker pull gitlab/gitlab-ce:latest二、启动容器 …

CSS 实现航班起飞、飞行和降落动画

CSS 实现航班起飞、飞行和降落动画 效果展示 航班起飞阶段 航班飞行阶段 航班降落 CSS 知识点 animation 属性的综合运用:active 属性的运营 动画分解 航班滑行阶段动画 实现航班的滑行阶段动画&#xff0c;需要使用两个核心物件&#xff0c;一个是跑动动画&#x…

LeetCode-994. 腐烂的橘子【广度优先搜索 数组 矩阵】

LeetCode-994. 腐烂的橘子【广度优先搜索 数组 矩阵】 题目描述&#xff1a;解题思路一&#xff1a;多源广度优先搜索&#xff08;队列实现&#xff09;解题思路二&#xff1a;哈希表实现&#xff0c;先找出所有腐烂和新鲜橘子的集合{}类似于set()。每剔除一次time1解题思路三&…

代码随想录|Day32|动态规划01|509.斐波那契数列、70.爬楼梯、746.使用最小花费爬楼梯

509.斐波那契数列 动规五步曲&#xff1a; 确定 dp[i] 含义&#xff1a;第 i 个斐波那契数值为 dp[i]递推公式&#xff1a;dp[i] dp[i - 1] dp[i - 2]dp数组初始化&#xff1a;dp[0] dp[1] 1遍历顺序&#xff1a;从前向后打印dp数组 class Solution:def fib(self, n: int) …