Spring IoC/DI

529 阅读15分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第20天,点击查看活动详情


⭐️前面的话⭐️

✉️坚持和努力一定能换来诗与远方!
💭推荐书籍:📚《王道408》,📚《深入理解 Java 虚拟机-周志明》,📚《Java 核心技术卷》
💬算法刷题:✅力扣🌐牛客网
🎈Github
🎈码云Gitee


三、IoC/DI相关内容

7 IOC相关内容

7.1 bean基础配置

7.1.1 bean基础配置(id与class)

bean标签的功能、使用方式以及id和class属性的作用,我们通过一张图来描述下

 # class属性能不能写接口如BookDao的类全名呢?
 答案肯定是不行,因为接口是没办法创建对象的。
 # 前面提过为bean设置id时,id必须唯一,但是如果由于命名习惯而产生了分歧后,该如何解决?
 配置别名

7.1.2 bean的name属性指定别名

 <!--name:为bean指定别名,别名可以有多个,使用逗号,分号,空格进行分隔-->
 <bean id="bookService" name="service service4 bookEbi" class="com.itheima.service.impl.BookServiceImpl">
     <property name="bookDao" ref="bookDao"/>
 </bean>

获取bean无论是通过id还是name获取,如果无法获取到,将抛出异常 NoSuchBeanDefinitionException

7.1.3 bean作用范围scope配置

 <!--scope:为bean设置作用范围,可选值为单例singloton,非单例prototype-->
 <bean id="bookDao" name="dao" class="com.itheima.dao.impl.BookDaoImpl" scope="prototype"/>

 # 为什么bean默认为单例?
 bean为单例的意思是在Spring的IOC容器中只会有该类的一个对象
 bean对象只有一个就避免了对象的频繁创建与销毁,达到了bean对象的复用,性能高
 ​
 # 哪些bean对象适合交给容器进行管理?
 表现层对象
 业务层对象
 数据层对象
 工具对象
 ​
 # 哪些bean对象不适合交给容器进行管理?
 封装实例的域对象,因为会引发线程安全问题,所以不适合。

那么单例 bean是怎么造出来的呢?

7.2 bean实例化

 # 在讲解这三种创建方式之前,我们需要先确认一件事:
 bean本质上就是对象,对象在new的时候会使用构造方法完成,那创建bean也是使用构造方法完成的。

代码详见 spring_03_bean_instance

7.2.1 方法1:构造方法(常用)

 public class BookDaoImpl implements BookDao {
     // 类中提供构造函数测试
     public BookDaoImpl() {
         System.out.println("book dao constructor is running ....");
     }
 ​
     public void save() {
         System.out.println("book dao save ...");
     }
 ​
 }

Spring底层用的是反射。

Spring底层使用的是类的无参构造方法。

因为每一个类默认都会提供一个无参构造函数,所以其实真正在使用这种方式的时候,我们什么也不需要做。这也是我们以后比较常用的一种方式。

分析Spring的错误信息:错误信息从下往上依次查看,因为上面的错误大都是对下面错误的一个包装,最核心错误是在最下面。

7.2.2 方法2:静态工厂(了解)

 //静态工厂创建对象
 public class OrderDaoFactory {
     public static OrderDao getOrderDao(){
         System.out.println("factory setup....");
         return new OrderDaoImpl();
     }
 }

在spring的配置文件 applicationContext.xml中

 <!--方式二:使用静态工厂实例化bean-->
 <bean id="orderDao" class="com.itheima.factory.OrderDaoFactory" factory-method="getOrderDao"/>

这种方式一般是用来兼容早期的一些老系统,所以了解为主

7.2.3 方法3:使用实例工厂实例化bean(了解)

 //实例工厂创建对象
 public class UserDaoFactory {
     public UserDao getUserDao(){
         return new UserDaoImpl();
     }
 }

配置

 <!--方式三:使用实例工厂实例化bean-->
 <bean id="userFactory" class="com.itheima.factory.UserDaoFactory"/>
 <bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/>

实例工厂实例化的方式就已经介绍完了,配置的过程还是比较复杂,所以Spring为了简化这种配置方式就提供了一种叫FactoryBean的方式来简化开发。

7.2.4 方法4:使用FactoryBean实例化bean(务必掌握)

 //FactoryBean创建对象
 public class UserDaoFactoryBean implements FactoryBean<UserDao> {
     //代替原始实例工厂中创建对象的方法
     public UserDao getObject() throws Exception {
         return new UserDaoImpl();
     }
     // bean类型
     public Class<?> getObjectType() {
         return UserDao.class;
     }
 ​
 }

配置

 <!--方式四:使用FactoryBean实例化bean-->
 <bean id="userDao" class="com.itheima.factory.UserDaoFactoryBean"/>

这种方式在Spring去整合其他框架的时候会被用到,所以这种方式需要大家理解掌握。

总结一下:

 - 构造方法(常用)
 - 静态工厂(了解)
 - 实例工厂(了解)
   - FactoryBean(实用)

7.3 bean的生命周期

对于生命周期,我们主要围绕着bean生命周期控制来讲解。

 # 生命周期
 从创建到消亡的完整过程
 ​
 # bean生命周期
 bean对象从创建到销毁的整体过程。
 ​
 # bean生命周期控制
 在bean创建后到销毁前做一些事情。

添加初始化和销毁方法

 public class BookDaoImpl implements BookDao {
     public void save() {
         System.out.println("book dao save ...");
     }
     //表示bean初始化后对应的操作
     public void init(){
         System.out.println("init...");
     }
     //表示bean销毁前对应的操作
     public void destroy(){
         System.out.println("destroy...");
     }
 ​
 }

配置生命周期

 <!--init-method:设置bean初始化生命周期回调函数-->
 <!--destroy-method:设置bean销毁生命周期回调函数,仅适用于单例对象-->
 <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" init-method="init" destroy-method="destroy"/>

测试

 # 从结果中可以看出,init方法执行了,但是destroy方法却未执行,这是为什么呢?
 - Spring的IOC容器是运行在JVM中
 - 运行main方法后,JVM启动,Spring加载配置文件生成IOC容器,从容器获取bean对象,然后调方法执行
 - main方法执行完后,JVM退出,这个时候IOC容器中的bean还没有来得及销毁就已经结束了
 - 所以没有调用对应的destroy方法
 ​
 # 知道了出现问题的原因,具体该如何解决呢?
 - ApplicationContext中没有close方法,需要将ApplicationContext更换成ClassPathXmlApplicationContext,
 调用ctx的close()方法
 - 调用ctx的registerShutdownHook()方法
 在容器未关闭之前,提前设置好回调函数,让JVM在退出之前回调此函数来关闭容器
 public class AppForLifeCycle {
     public static void main( String[] args ) {
         ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
 ​
         BookDao bookDao = (BookDao) ctx.getBean("bookDao");
         bookDao.save();
         //注册关闭钩子函数,在虚拟机退出之前回调此函数,关闭容器
         //ctx.registerShutdownHook();
         //关闭容器
         ctx.close();
     }
 }
 # 分析上面的实现过程,会发现添加初始化和销毁方法,即需要编码也需要配置,实现起来步骤比较多也比较乱。
 # Spring提供了两个接口来完成生命周期的控制,好处是可以不用再进行配置init-method和 destroy-method
 修改BookServiceImpl类,添加两个接口InitializingBean, DisposableBean
 并实现接口中的两个方法afterPropertiesSet和destroy

实现2个接口

 public class BookServiceImpl implements BookService, InitializingBean, DisposableBean {
     private BookDao bookDao;
 ​
     public void setBookDao(BookDao bookDao) {
         System.out.println("set .....");
         this.bookDao = bookDao;
     }
 ​
     public void save() {
         System.out.println("book service save ...");
         bookDao.save();
     }
 ​
     public void destroy() throws Exception {
         System.out.println("service destroy");
     }
 ​
     public void afterPropertiesSet() throws Exception {
         System.out.println("service init");
     }
 }

输出

 init...
 set .....
 service init
 book dao save ...
 service destroy
 destroy...

总结一下:

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

8 DI相关内容

接下来就进入第二个大的模块DI依赖注入

 # 思考🤔:向一个类中传递数据的方式有几种?
 - 普通方法(set方法)
 - 构造方法
 ​
 # 思考🤔:依赖注入描述了在容器中建立bean与bean之间的依赖关系的过程,如果bean运行需要的是数字或字符串呢?
 - 引用类型
 - 简单类型(基本数据类型与String)
 ​
 # Spring就是基于上面这些知识点,为我们提供了两种注入方式,分别是:
 - setter注入
   -简单类型
   -引用类型
 ​
 - 构造器注入
   -简单类型
   -引用类型

8.1 setter注入 property

注入引用数据类型

步骤1:声明属性并提供setter方法

 public class BookServiceImpl implements BookService{
     private BookDao bookDao;
     private UserDao userDao;
     //setter注入需要提供要注入对象的set方法
     public void setUserDao(UserDao userDao) {
         this.userDao = userDao;
     }
     //setter注入需要提供要注入对象的set方法
     public void setBookDao(BookDao bookDao) {
         this.bookDao = bookDao;
     }
 ​
     public void save() {
         System.out.println("book service save ...");
         bookDao.save();
         userDao.save();
     }
 }

步骤2:配置文件中进行注入配置

 <!--注入简单类型-->
 <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
 <bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
 ​
 <!--注入引用类型-->
 <bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
   <!--property标签:设置注入属性-->
   <!--name属性:设置注入的属性名,实际是set方法对应的名称-->
   <!--ref属性:设置注入引用类型bean的id或name-->
   <property name="bookDao" ref="bookDao"/>
   <property name="userDao" ref="userDao"/>
 </bean>

注入基本数据类型

步骤1:声明属性并提供setter方法

 public class BookDaoImpl implements BookDao {
 ​
     private String databaseName;
     private int connectionNum;
     //setter注入需要提供要注入对象的set方法
     public void setConnectionNum(int connectionNum) {
         this.connectionNum = connectionNum;
     }
     //setter注入需要提供要注入对象的set方法
     public void setDatabaseName(String databaseName) {
         this.databaseName = databaseName;
     }
 ​
     public void save() {
         System.out.println("book dao save ..."+databaseName+","+connectionNum);
     }
 }

步骤2:配置文件中进行注入配置

     <!--注入简单类型-->
     <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
         <!--property标签:设置注入属性-->
         <!--name属性:设置注入的属性名,实际是set方法对应的名称-->
         <!--value属性:设置注入简单类型数据值-->
         <property name="connectionNum" value="100"/>
         <property name="databaseName" value="mysql"/>
     </bean>
 ​
     <bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
 ​
     <!--注入引用类型-->
     <bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
         <!--property标签:设置注入属性-->
         <!--name属性:设置注入的属性名,实际是set方法对应的名称-->
         <!--ref属性:设置注入引用类型bean的id或name-->
         <property name="bookDao" ref="bookDao"/>
         <property name="userDao" ref="userDao"/>
     </bean>

需要注意的是:

 - 对于引用数据类型使用的是<property name="" ref=""/>
 - 对于简单数据类型使用的是<property name="" value=""/>

8.2 构造器(构造方法)注入 constructor-arg

构造器注入引用数据类型

构造器注入多个引用数据类型

步骤1:删除setter方法并提供构造方法

 public class BookServiceImpl implements BookService{
     private BookDao bookDao;
     private UserDao userDao;
 ​
     public BookServiceImpl(BookDao bookDao, UserDao userDao) {
         this.bookDao = bookDao;
         this.userDao = userDao;
     }
 ​
     public void save() {
         System.out.println("book service save ...");
         bookDao.save();
         userDao.save();
     }
 }

步骤2:配置文件中进行配置构造方式注入

 <!--解决参数类型重复问题,使用位置解决参数匹配-->
 <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
 <bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
 ​
 <bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
   <constructor-arg name="userDao" ref="userDao"/>
   <constructor-arg name="bookDao" ref="bookDao"/>
 </bean>
  • name属性对应的值为构造函数中方法形参的参数名,必须要保持一致。
  • ref属性指向的是spring的IOC容器中其他bean对象。

构造器注入多个简单数据类型

步骤1:添加多个简单属性并提供构造方法

 public class BookDaoImpl implements BookDao {
     private String databaseName;
     private int connectionNum;
 ​
     public BookDaoImpl(String databaseName, int connectionNum) {
         this.databaseName = databaseName;
         this.connectionNum = connectionNum;
     }
 ​
     public void save() {
         System.out.println("book dao save ..."+databaseName+","+connectionNum);
     }
 }

步骤2:配置完成多个属性构造器注入

 <!--
     标准书写
     <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
         根据构造方法参数名称注入
         <constructor-arg name="connectionNum" value="10"/>
         <constructor-arg name="databaseName" value="mysql"/>
     </bean>
     <bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
 ​
     <bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
         <constructor-arg name="userDao" ref="userDao"/>
         <constructor-arg name="bookDao" ref="bookDao"/>
     </bean>
 -->
 <!--
     解决形参名称的问题,与形参名不耦合
     <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
         根据构造方法参数类型注入
         <constructor-arg type="int" value="10"/>
         <constructor-arg type="java.lang.String" value="mysql"/>
     </bean>
     <bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
 ​
     <bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
         <constructor-arg name="userDao" ref="userDao"/>
         <constructor-arg name="bookDao" ref="bookDao"/>
     </bean>-->
 ​
     <!--解决参数类型重复问题,使用位置解决参数匹配-->
     <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
         <!--根据构造方法参数位置注入-->
         <constructor-arg index="0" value="mysql"/>
         <constructor-arg index="1" value="100"/>
     </bean>
     <bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
 ​
     <bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
         <constructor-arg name="userDao" ref="userDao"/>
         <constructor-arg name="bookDao" ref="bookDao"/>
     </bean>
 </beans>

上面已经完成了构造函数注入的基本使用,但是会存在一些问题:

  • 当构造函数中方法的参数名发生变化后,配置文件中的name属性也需要跟着变
  • 这两块存在紧耦合,具体该如何解决?

在解决这个问题之前,需要提前说明的是,这个参数名发生变化的情况并不多,所以上面的还是比较主流的配置方式,下面介绍的,大家

都以了解为主。

 # 方式一:删除name属性,添加type属性,按照类型注入
 - 这种方式可以解决构造函数形参名发生变化带来的耦合问题
 - 但是如果构造方法参数中有类型相同的参数,这种方式就不太好实现了
 # 方式二:删除type属性,添加index属性,按照索引下标注入,下标从0开始
 - 这种方式可以解决参数类型重复问题
 - 但是如果构造方法参数顺序发生变化后,这种方式又带来了耦合问题

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

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

8.3 自动配置 autowire属性

前面花了大量的时间把Spring的注入去学习了下,总结起来就一个字麻烦。

8.3.1 什么是依赖自动装配?

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

8.3.2 自动装配方式有哪些?

  • 按类型(常用)
  • 按名称
  • 按构造方法
  • 不启用自动装配
 <bean class="com.itheima.dao.impl.BookDaoImpl"/>
 <!--autowire属性:开启自动装配,通常使用按类型装配-->
 <bean id="bookService" class="com.itheima.service.impl.BookServiceImpl" autowire="byType"/>

注意事项:

 # 需要注入属性的类中对应属性的setter方法不能省略
 # 被注入的对象必须要被Spring的IOC容器管理
 # 按照类型在Spring的IOC容器中如果找到多个对象,会报NoUniqueBeanDefinitionException
 # 一个类型在IOC中有多个对象,还想要注入成功,这个时候就需要按照名称注入byName
 # 按照名称注入中的名称指的是什么
 - 对外部类来说,setBookDao方法名,去掉set后首字母小写是其属性名
 ​
 # 两种方式介绍完后,以后用的更多的是按照类型注入
 1. 自动装配用于引用类型依赖注入,不能对简单类型进行操作
 2. 使用按类型装配时(byType)必须保障容器中相同类型的bean唯一,推荐使用
 3. 使用按名称装配时(byName)必须保障容器中具有指定名称的bean,因变量名与配置耦合,不推荐使用
 4. 自动装配优先级低于setter注入与构造器注入,同时出现时自动装配配置失效

8.4 集合注入

前面我们已经能完成引用数据类型和简单数据类型的注入,但是还有一种数据类型集合,集合中既可以装简单数据类型也可以装引用数据

类型,对于集合,在Spring中该如何注入呢?

 # 先来回顾下,常见的集合类型有哪些?
 数组
 List
 Set
 Map
 Properties
 ​
 # 针对不同的集合类型,该如何实现注入呢?

下面的所有配置方式,都是在bookDao的bean标签中进行注入

 <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"> 
 </bean>

8.4.1 注入数组类型数据

 <!--数组注入-->
 <property name="array">
   <array>
     <value>100</value>
     <value>200</value>
     <value>300</value>
   </array>
 </property>

8.4.2 注入List类型数据

 <!--list集合注入-->
 <property name="list">
   <list>
     <value>itcast</value>
     <value>itheima</value>
     <value>boxuegu</value>
     <value>chuanzhihui</value>
   </list>
 </property>

8.4.3 注入Set类型数据

 <!--set集合注入-->
 <property name="set">
   <set>
     <value>itcast</value>
     <value>itheima</value>
     <value>boxuegu</value>
     <value>boxuegu</value>
   </set>
 </property>

8.4.4 注入map类型数据

 <!--map集合注入-->
 <property name="map">
   <map>
     <entry key="country" value="china"/>
     <entry key="province" value="henan"/>
     <entry key="city" value="kaifeng"/>
   </map>
 </property>

8.4.5 注入Properties类型数据

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

输出结果

 book dao save ...
 遍历数组:[100, 200, 300]
 遍历List[itcast, itheima, boxuegu, chuanzhihui]
 遍历Set[itcast, itheima, boxuegu]
 遍历Map{country=china, province=henan, city=kaifeng}
 遍历Properties{province=henan, city=kaifeng, country=china}

说明

 # property标签表示setter方式注入,构造方式注入constructor-arg标签内部也可以写<array><list><set><map><props>标签
 # List的底层也是通过数组实现的,所以<list><array>标签是可以混用
 # 集合中要添加引用类型,只需要把<value>标签改成<ref>标签,这种方式用的比较少
 # day02
 掌握IOC/DI配置管理第三方bean
 掌握IOC/DI的注解开发
 掌握IOC/DI注解管理第三方bean
 完成Spring与Mybatis及Junit的整合开发

9 IOC/DI配置管理第三方bean

前面所讲的知识点都是基于我们自己写的类,现在如果有需求让我们去管理第三方jar包中的类,该如何管理?

9.1 案例:数据源对象管理

在这一节中,我们将通过一个案例来学习下对于第三方bean该如何进行配置管理。

以后我们会用到很多第三方的bean,本次案例将使用咱们前面提到过的数据源Druid(德鲁伊)和C3P0来配置学习下。

思路分析

 # 需求:使用Spring的IOC容器来管理Druid连接池对象
 ​
 1.使用第三方的技术,需要在pom.xml添加依赖
 2.在配置文件中将【第三方的类】制作成一个bean,让IOC容器进行管理
 3.数据库连接需要基础的四要素驱动、连接、用户名和密码,【如何注入】到对应的bean中
 4.从IOC容器中获取对应的bean对象,将其打印到控制台查看结果

实现Druid的管理

步骤1:导入druid的依赖

 <dependency>
     <groupId>com.alibaba</groupId>
     <artifactId>druid</artifactId>
     <version>1.1.16</version>
 </dependency>

步骤2:配置第三方bean

在applicationContext.xml配置文件中添加DruidDataSource的配置

 <!--    管理DruidDataSource对象-->
 <bean class="com.alibaba.druid.pool.DruidDataSource">
     <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
     <property name="url" value="jdbc:mysql://localhost:3306/spring_db"/>
     <property name="username" value="root"/>
     <property name="password" value="root"/>
 </bean>

步骤3:从IOC容器中获取对应的bean对象

步骤4:运行程序

 # 思考🤔
 # 第三方的类指的是什么?
 DruidDataSource
 # 如何注入数据库连接四要素?
 setter注入

实现C3P0管理

同上

 <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
     <property name="driverClass" value="com.mysql.jdbc.Driver"/>
     <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring_db"/>
     <property name="user" value="root"/>
     <property name="password" value="root"/>
     <property name="maxPoolSize" value="1000"/>
 </bean>

9.2 加载properties文件

 # 上节中我们已经完成两个数据源druid和C3P0的配置,但是其中包含了一些问题,我们来分析下:
 - 这两个数据源中都使用到了一些固定的常量如数据库连接四要素,把这些值写在Spring的配置文件中不利于后期维护
 - 需要将这些值提取到一个外部的properties配置文件中
 - Spring框架如何从配置文件中读取属性值来配置就是接下来要解决的问题。

第三方bean属性优化

 # 需求:将数据库连接四要素提取到properties配置文件,spring来加载配置信息并使用这些信息来完成属性注入。
 1.在resources下创建一个jdbc.properties(文件的名称可以任意) 
 2.将数据库连接四要素配置到配置文件中
 3.在Spring的配置文件中[ 加载properties文件 ]
 4.使用加载到的值实现属性注入
 其中第34步骤是需要大家重点关注,具体是如何实现。

(1)准备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)在applicationContext.xml中开context命名空间

(3)加载properties配置文件

 <context:property-placeholder location="jdbc.properties"/>

(4)完成属性注入

使用 ${key} 来读取properties配置文件中的内容并完成属性注入

 <!--    3.使用属性占位符${}读取properties文件中的属性-->
 <!--    说明:idea自动识别${}加载的属性值,需要手工点击才可以查阅原始书写格式-->
 <bean 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>

读取单个属性

 # 需求:从properties配置文件中读取key为name的值,并将其注入到BookDao中并在save方法中进行打印。
 1.在项目中添加BookDao和BookDaoImpl类
 2.为BookDaoImpl添加一个name属性并提供setter方法
 3.在jdbc.properties中添加数据注入到bookDao中打印方便查询结果
 4.在applicationContext.xml添加配置完成配置文件加载、属性注入(${key})

1,2

 public class BookDaoImpl implements BookDao {
     private String name;
 ​
     public void setName(String name) {
         this.name = name;
     }
 ​
     public void save() {
         System.out.println("book dao save ..." + name);
     }
 }

3

 username=root666

4

 <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
     <property name="name" value="${username}"/>
 </bean>

10 核心容器

 这里所说的核心容器,大家可以把它简单的理解为ApplicationContext,前面虽然已经用到过,但是并没有系统的学习。
 接下来咱们从以下几个问题入手来学习下容器的相关知识:
 - 如何创建容器?
 - 创建好容器后,如何从容器中获取bean对象?
 - 容器类的层次结构是什么?
 - BeanFactory是什么?

10.1 创建容器

 //1.加载类路径下的配置文件
 ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
 //2.从文件系统下加载配置文件
 //ApplicationContext ctx = new FileSystemXmlApplicationContext("D:\workspace\spring\spring_10_container\src\main\resources\applicationContext.xml");

10.2 获取bean的3种方式

方式一:使用bean名称获取

 BookDao bookDao = (BookDao) ctx.getBean("bookDao");

这种方式存在的问题是每次获取的时候都需要进行类型转换,有没有更简单的方式呢?

方式二:使用bean名称获取并指定类型

 BookDao bookDao = ctx.getBean("bookDao",BookDao.class);

方式三:使用bean类型获取

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

这种方式就类似我们之前所学习依赖注入中的按类型注入。必须要确保IOC容器中该类型对应的bean对象只能有一个。

10.3 容器类层次结构

10.4 BeanFactory

顶级接口

10.5 核心容器总结

容器相关

  • BeanFactory是IoC容器的顶层接口,初始化BeanFactory对象时,加载的bean延迟加载

  • ApplicationContext接口是Spring容器的核心接口,初始化时bean立即加载

  • ApplicationContext接口提供基础的bean操作相关方法,通过其他接口扩展其功能

  • ApplicationContext接口常用初始化类

    • ClassPathXmlApplicationContext (常用)
    • FileSystemXmlApplicationContext

bean相关

其实整个配置中最常用的就两个属性idclass

把scope、init-method、destroy-method框起来的原因是,后面注解讲解的时候还会用到,所以大家对这三个属性关注下。

依赖注入相关