Spring学习笔记(二)IoC Bean

IoC相关

Bean的配置

基础配置

id class

别名

除了通过id获取对应的bean,还可以通过name属性为bean设置别名,别名之间可以用,空格或;分隔,且ref可以引用id也可以引用name。

 <bean id="bookDao" name="dao dao1" class="com.yuan.dao.impl.BookDaoImpl"/>

Bean的作用范围

默认为单例singleton,可以通过scope属性更改为多例prototype

 <bean id="bookDao" name="dao dao1" class="com.yuan.dao.impl.BookDaoImpl" scope="prototype"/>

tips :关于为什么bean默认为单例:本来Spring容器管理的就是经常复用的对象,没有必要建造多例。

适合交给容器管理的bean(单例):表现层对象;业务层对象eg.service;数据层对象eg.dao;工具对象eg.utils。

不适合交给容器管理的bean(多例):封装实体的域对象(有具体属性值eg.student1 student2)

Bean的实例化

1.使用无参构造方法创建实例(本质上是反射)

 <!--方式一:使用构造方法实例化bean-->
 <bean id="bookDao" class="com.yuan.dao.impl.BookDaoImpl"/>

2.静态工厂方式(不常用)

步骤1.建立工厂

 public class OrderDaoFactory {
     public static OrderDao getOrderDao(){
         System.out.println("factory setup....");
         return new OrderDaoImpl();
    }
 }

步骤2.applicationContext.xml中配置bean,主要留意参数factory-method,用于指定使用工厂类中哪一个方法。

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

步骤3.调用

 public class App {
     public static void main(String[] args) {
         ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
         OrderDao orderDao = (OrderDao) ctx.getBean("orderDao");
         orderDao.save();
    }
 }

这时当调用ctx.getBean("orderDao")时,根据配置文件里的bean的配置,会访问工厂模式来实例化bean对象。对应关系如下:

image-20230227212842850

tips:关于为什么要使用工厂模式而不直接new:一方面降低耦合度;另一方面使用工厂模式时可以在工厂类中进行必要的配置,写在工厂里更清晰方便。

3.实例工厂与FactoryBean

与2中静态工厂不同,该方法采用实例工厂,没有static限定。细节不再赘述,可以看黑马讲解

额外讲一下factoryBean:因为这里采用实例工厂,所以xml文件中配置实例化要使用的bean对象前还需要先配置其对应的Factory,这段代码只是为了配合前一个bean对象,无实际意义,如下图。

image-20230227214551713

所以Spring提供了FactoryBean接口,可以创造其实现类,相当于结合了2的Factory.java和applicationContext.xml中关于factory的bean配置,减少代码简化配置。

image-20230227214725381
image-20230227215132943

Bean的生命周期

生命周期指bean从创建到消亡的过程。

控制bean的生命周期

方法一

可以在bean对应的java类中定义init和destory方法,并在bean中进行绑定配置:

     <bean id="bookService" class="com.yuan.service.impl.BookServiceImpl" init-method="init" destroy-method="destroy"/>

执行会发现destroy()方法没有执行,是因为所有java代码都运行在java虚拟机中,程序init和执行程序结束之后虚拟机退出了,没有提供销毁机会。所以需要额外处理:

方法1.

在虚拟机退出之前手动关闭容器,即调用ClassPathXmlApplicationContext的close()方法;

方法2.

进行配置,告诉虚拟机关闭之前要先关闭容器,调用ClassPathXmlApplicationContext的registerShutdownHook()方法即可。

方法二

在bean对应的java类中实现两个接口:InitializingBeanDisposableBean.

然后分别重写对应的两个方法即可:

     @Override
     public void afterPropertiesSet() throws Exception {
         System.out.println("service init");
    }
 ​
     @Override
     public void destroy() throws Exception {
         System.out.println("service destroy");
    }

Bean的注入方式

1.setter注入

在bean配置中添加property标签,可以为bean对应的java类中的属性赋值,需要在类中先设置好set方法。name为对应java类中属性名,value为需要注入的属性值。

简单数据类型用value;引用数据类型用refref引用另一个配置好的bean的id或name。

 <bean id="bookService" class="com.yuan.service.impl.BookServiceImpl">
         <property name="bookName" value="111"/>
 </bean>

2.构造器注入

使用方法类似于setter注入,但针对的是构造方法的参数。name可以不写,spring会按照构造方法的参数顺序赋值,必要时可以使用type指定参数类型来区分不同参数。

 <!--name为构造方法中形参名-->
 <bean id="bookService" class="com.yuan.service.impl.BookServiceImpl">
         <constructor-arg name="bookName" value="111"/>
 </bean>

tips: 考虑这两种依赖注入的方式,建议强制依赖使用构造器注入,可选依赖使用setter注入。

3.自动装配

以上介绍的两种方式都需要手动依赖注入,Spring还提供了自动装配,即 IoC容器根据bean所依赖的资源在容器中自动查找并注入到bean中。

使用方法:配置bean时不需要手动添加property,只需要多加一个autowire(当然也需要在类中写好set方法,否则自动装配没有可调用的接口):

 <bean id="bookService" class="com.yuan.service.impl.BookServiceImpl" autowire="byType"/>

autowire参数有几个可选值:byType | byName | constructor | default | no.

tips:

  • 自动装配只能用于对引用类型依赖注入,不能用于简单类型
  • 自动装配优先级低于setter注入和构造器注入

4.集合注入

注入数组类型数据
 <property name="array">
     <array>
         <value>100</value>
         <value>200</value>
         <value>300</value>
     </array>
 </property>
注入List类型数据
 <property name="list">
     <list>
         <value>itcast</value>
         <value>itheima</value>
         <value>boxuegu</value>
         <value>chuanzhihui</value>
     </list>
 </property>
注入Set类型数据
 <property name="set">
     <set>
         <value>itcast</value>
         <value>itheima</value>
         <value>boxuegu</value>
         <value>boxuegu</value>
     </set>
 </property>
注入Map类型数据
 <property name="map">
     <map>
         <entry key="country" value="china"/>
         <entry key="province" value="henan"/>
         <entry key="city" value="kaifeng"/>
     </map>
 </property>
注入Properties类型数据
 <property name="properties">
     <props>
         <prop key="country">china</prop>
         <prop key="province">henan</prop>
         <prop key="city">kaifeng</prop>
     </props>
 </property>

以上内容可以直接写在bean标签的子标签内,也可以单独列出,然后直接通过ref引用即可,单独列出需要加namespace限制,即加上前缀util:,示例:

 <util:list id="studentList">
         <ref bean="studentOne"/>
         <ref bean="studentTwo"/>
         <ref bean="studentThree"/>
  </util:list>

IoC/DI配置管理第三方Bean

Druid

以druid第三方datasource为例,配置管理第三方bean时,先在pom.xml文件中添加第三方依赖,之后在applicationContext.xml中配置对应的bean信息即可,配置如下:

  <bean id="druidDatasource" 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="root72760"/>
  </bean>

加载properties文件

上述配置中密码、连接url等内容直接写死在了bean配置文件中,不清晰也不方便,所以可以把这些信息单独摘出来写成一个properties配置文件,再在bean的xml中关联配置文件参数即可,其实和mybatis的配置差不多。变动如下:

添加properties文件

 jdbc.driver = com.mysql.cj.jdbc.Driver
 jdbc.url = jdbc:mysql://localhost:3306/spring_db?serverTimezone=UTC
 jdbc.username = root
 jdbc.password = root72760

修改bean的配置文件applicationContext.xml,这个过程引入了一个新的命名空间context。

 <!--使用context命名空间指定properties文件-->
 <context:property-placeholder location="jdbc.properties" />
 <!--使用${}获取值-->
 <bean id="druidDatasource" 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>

tips : 观察properties文件左边参数的命名方式发现都添加了jdbc.,其实是为了防止命名冲突,eg假设直接命名为username,则可能查询到的是系统username而非properties中的指定值。解决这种问题除了添加例如jdbc.的限制,也可以在context标签中添加如下限制,指定不使用系统属性:

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

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注