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对象。对应关系如下:

tips:关于为什么要使用工厂模式而不直接new:一方面降低耦合度;另一方面使用工厂模式时可以在工厂类中进行必要的配置,写在工厂里更清晰方便。
3.实例工厂与FactoryBean
与2中静态工厂不同,该方法采用实例工厂,没有static限定。细节不再赘述,可以看黑马讲解。
额外讲一下factoryBean:因为这里采用实例工厂,所以xml文件中配置实例化要使用的bean对象前还需要先配置其对应的Factory,这段代码只是为了配合前一个bean对象,无实际意义,如下图。

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


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类中实现两个接口:InitializingBean
和DisposableBean
.
然后分别重写对应的两个方法即可:
@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
;引用数据类型用ref
,ref
引用另一个配置好的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"/>