如何描述控制反转与依赖注入?
-
控制反转(IoC) 是 Spring Framework 的核心原则之一。IoC 使得对象的创建和管理不再由应用程序本身控制,而是由 Spring 容器控制。这种设计使得应用程序的组件之间的耦合度降低,提高了模块的独立性和可测试性。
-
依赖注入(DI) 是 IoC 的一种实现方式,它允许 Spring 容器将所需的依赖自动注入到 Bean 中,而不是让 Bean 自行创建或查找依赖。这种方式减少了组件之间的硬编码依赖,使得应用程序更加灵活和可维护。
基于构造器的依赖注入
首先,为需要注入的依赖提供对应的构造器。然后,在XML中对<bean>
标签增加子标签<construtor-arg>
进行依赖注入。对于形参值的指定主要有几种方式:
name
<bean id="bookService2" class="space.mora.service.impl.BookServiceImpl2">
<constructor-arg name="userDao" ref="userDao"/>
<constructor-arg name="bookDao" ref="bookDao"/>
</bean>
注意: 此处的name是指形参名称,而非依赖的属性名称的。
type
<bean id="bookService2" class="space.mora.service.impl.BookServiceImpl2">
<constructor-arg type="java.lang.String" value="BOOK SERVICE #2"/>-->
<constructor-arg ref="userDao"/>
<constructor-arg ref="bookDao"/>
</bean>
注意:仅当每个类型的参数只有一个时方可使用。type
若需指定引用类型名,需要给出该引用类的完全限定名。例如String
为java.lang.String
。此外,若使用ref
引用其他bean,则Spring会自己判断依赖bean的类型,type可以省略。
index
<bean id="bookService2" class="space.mora.service.impl.BookServiceImpl2">
<constructor-arg index="0" value="12"/>
<constructor-arg value="BOOK SERVICE #2">
<constructor-arg ref="userDao"/>
<constructor-arg ref="bookDao"/>
</bean>
注意: 索引从0开始,表示所使用构造器的对应形参。index标志具有最高优先级
,当有constructor-arg指定了index属性后,会先按照index将形参进行填充,剩余constructor-arg无论是否有name
或type
属性标识,其绝对位置随意,但相对顺序必须与形参列表一样。
举例:4个参数,0和2的constructor-arg使用index指定了,那么剩下两个如果不用index指定的话,其绝对位置可以随意在0和2的前后,但是相对顺序必须为先1后3。否则依赖会注入错误,或者干脆类型不匹配抛UnsatisfiedDependencyException
异常。
基于setter的依赖注入
首先,为Bean类中需要注入的依赖提供set
方法。然后,在XML中对<bean>
标签增加子标签<property>
进行依赖注入。对于引用类型依赖,使用ref
属性引用其他bean;对于字面值类型的依赖,使用value
设置具体值。
<bean id="bookDao" class="space.mora.dao.impl.BookDaoImpl"/>
<bean id="userDao" class="space.mora.dao.impl.UserDaoImpl"/>
<bean id="bookService1" class="space.mora.service.impl.BookServiceImpl">
<property name="serviceName" value="BOOK SERVICE #1"/>
<property name="connNum" value="15"/>
<property name="bookDao" ref="bookDao"/>
<property name="userDao" ref="userDao"/>
</bean>
请注意:value
属性为""
时,代表空字符串,若想设置为null
,请采用:
<bean ......>
<property ......>
<null/>
</property>
</bean>
那么,依赖注入究竟选择构造器方式还是setter模式呢?参考黑马SSM教程
-
- 强制依赖使用构造器进行,使用setter注入有概率不进行注入导致null
- 可选依赖使用setter注入进行,灵活性强
- Spring框架倡导使用构造器,第三方框架内部大多数采用构造器注入的形式进行数据初始化,相对严谨
- 如果有必要可以两者同时使用,使用构造器注入完成强制依赖的注入,使用setter注入完成可选依赖的注入
- 实际开发过程中还要根据实际情况分析,如果受控对象没有提供setter方法就必须使用构造器注入
- 自己开发的模块推荐使用setter注入
自动注入(自动装配)
Spring容器可以通过检查 ApplicationContext
的内容,来自动处理Bean之间的依赖关系。通过XML配置中,<bean>
标签的autorwire
属性完成自动注入。该属性可选值有4种:
- no。默认情况下,不启用自动装配。
byType
。若容器配置中存在唯一类型Bean,则引用该Bean作为依赖。byName
。若容器配置中存在id
与装配目标成员属性名相同的Bean,则引用该Bean作为依赖。byConstructor
。与byType
类似,自动寻找类型与装配目标构造器形参列表类型相同的其他Bean作为依赖。
<bean id="bookDao" class="space.mora.dao.impl.BookDaoImpl"/>
<bean id="userDao" class="space.mora.dao.impl.UserDaoImpl"/>
<!-- 3. 自动装配 -->
<!-- 3.1 byType -->
<bean id="bookService3" class="BookServiceImpl3" autowire="byType"/>
<!-- 3.2 byName -->
<bean id="bookService3" class="BookServiceImpl3" autowire="byName"/>
<!-- 3.3 byConstructor-->
<bean id="bookService3" class="BookServiceImpl3" autowire="constructor"/>
请注意,仅Bean
,即引用类型的依赖可以自动注入,而字面值类型不行。并且,构造器与setter注入方式会直接覆盖
自动注入。
集合注入
示例,比较少用。
<bean id="bookService4" class="space.mora.service.impl.BookServiceImpl4">
<property name="array">
<array>
<value>10</value>
<value>20</value>
</array>
</property>
<property name="list">
<list>
<value>15</value>
<value>25</value>
</list>
</property>
<property name="set">
<set>
<value>cug</value>
<value>whu</value>
</set>
</property>
<property name="map">
<map>
<entry key="cug" value="211"/>
<entry key="whu" value="985"/>
</map>
</property>
<property name="properties">
<props>
<prop key="cug">211</prop>
<prop key="whu">985</prop>
</props>
</property>
</bean>
Properties文件加载
<context:property-placeholder>
是 Spring Framework 中的一个 XML 配置标签,用于加载和解析外部属性文件,以便在 Spring 的应用上下文中使用这些属性。它通常用于在应用启动时加载配置文件中的属性,并将这些属性值注入到 Spring beans 中。
功能
- 属性加载:
<context:property-placeholder>
可以从指定的位置加载属性文件,支持类路径、文件系统路径等。 - 占位符解析:在 Spring 配置文件中,你可以使用
${property.name}
语法引用这些属性,Spring 会在运行时将这些占位符替换为属性文件中的实际值。 - 属性覆盖:如果多个属性文件中定义了相同的属性,后加载的文件中的属性值会覆盖先前加载的值。
使用方法
- 首先在
<beans>
标签中启用context
命名空间
<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
属性指定资产文件位置。
<context:property-placeholder location="classpath*:*.properties" system-properties-mode="NEVER"/>
location
是必选的,具体语法可以参照下方小节system-properties-mode
(可选):用来定义系统属性(从Java系统环境获取的属性)如何与从属性文件加载的属性交互。这个属性可以设置为以下三个值之一:NEVER
**(默认值):不使用系统属性,占位符只会使用属性文件中的值。FALLBACK
(SYSTEM_PROPERTIES_MODE_FALLBACK
):如果在属性文件中找不到对应的属性,则会回退到系统属性。OVERRIDE
(SYSTEM_PROPERTIES_MODE_OVERRIDE
):系统属性将始终覆盖属性文件中的同名属性,即使属性文件中存在这些属性。
重名问题
:如果加载的多个properties文件中有重名prop,那么后加载的将覆盖先加载的属性。
- 加载的属性可以在XML配置中以
${propname}
的形式使用。(当然也可以通过配置注入资源类以在Java代码中使用,这个暂不讨论)
context location相关语法
<context:property-placeholder>
标签的location
属性时,这个属性用来指定属性文件的位置,从而加载所需的配置。这里的语法非常关键,因为它定义了如何找到和读取这些属性文件。以下是关于location
属性的一些详细解释和语法规则:
-
基本语法
location
属性可以接受一个或多个文件路径,这些路径可以是绝对的或相对的,并且可以包含Ant风格的通配符(如*
和?
)。 -
路径类型
- 本地文件系统路径:可以直接使用文件系统的路径,如
file:C:/config/myconfig.properties
或file:/etc/config/myconfig.properties
。 - 类路径资源:使用
classpath:
前缀,表示文件位于类路径上,如classpath:config/myconfig.properties
。 - 通配符支持:使用
classpath*:
前缀加通配符支持从所有类路径(包括JAR文件)中加载匹配的资源,如classpath*:config/**/*.properties
。
- 本地文件系统路径:可以直接使用文件系统的路径,如
-
通配符规则
*
:匹配文件路径中的0个或多个字符,但不跨越目录。**
:匹配路径中的0个或多个目录。?
:匹配文件路径中的一个字符。
-
多路径支持
如果需要从多个位置加载属性文件,可以使用逗号(,
)分隔各个路径,如:<context:property-placeholder location="classpath:props/base.properties, file:/etc/app/config.override.properties"/>
-
变量使用
可以在location
属性中使用已经定义的系统属性或环境变量,使用${varName}
语法,如:<context:property-placeholder location="file:${user.home}/config/app.properties"/>
-
例子
下面是一个实际例子,演示如何配置Spring来加载多个属性文件,包括类路径下和文件系统中的文件:<context:property-placeholder location="classpath:config/app.properties, file:/path/to/external.properties"/>
这个配置将会首先加载类路径下的
app.properties
,然后加载文件系统路径/path/to/
下的external.properties
。