10 装配SpringBean
10.1 依赖注入的3种方式
-
Spring主要使用依赖注入的方式来完成IoC
-
依赖注入可以分为3种方式
- 构造器注入
setter
注入- 接口注入
10.1 构造器注入
-
简介
- 构造器注入依赖于构造方法的实现
- 为了让
Spring
完成对应的构造注入,我们有必要去描述具体的类、构造方法并设置对应的参数,这样Spring
就可以通过对应的信息用反射的形式创建对象
-
实例: 构造器注入
-
创建一个只有有参构造器的
POJO
1
2
3
4
5
6
7
8
9
10
11
12public class Role {
private Long id;
private String roleName;
private String note;
public Role(String roleName, String note) {
this.roleName = roleName;
this.note = note;
}
/*getter and setter*/
} -
在
spring-config.xml
中配置对象1
2
3
4<bean id="role1" class="Role">
<constructor-arg index="0" value="总经理"/>
<constructor-arg index="1" value="公司管理者"/>
</bean>
-
-
构造器配置
constructor-arg
元素用于定义类构造方法的参数index
用于定义参数的位置value
则是设置值- 通过这样的定义Spring便知道使用
Role(String, String)
这样的构造方法去创建对象
10.1.2 使用setter注入
-
简介
setter
注入是Spring中最主流的注入方式,它利用Java Bean
规范所定义的setter
方法来完成注入,灵活且可读性高- 通过Java反射技术,Spring会调用没有参数的构造方法来生成对象,同时通过反射对应的
setter
注入配置的值
-
实例:配置
setter
注入1
2
3
4<bean id="role2" class="Role">
<property name="roleName" value="高级工程师"/>
<property name="note" value="重要人员"/>
</bean>
10.1.3 接口注入
- 简介
- 有些时候资源并非来自于自身系统,而是来自于外界
- 比如数据库连接资源完全可以在Tomcat下配置,然后通过JNDI的方式去获取它
10.2 装配Bean概述
-
本节需要学习的是如何将自己开发的
Bean
装配到Spring IoC
容器中 -
Spring提供了3种方法进行配置
- 在XML中显式配置
- 在Java的接口和类中实现配置
- 隐式Bean的发现机制和自动装配原则
-
3种方法的优先级
- 基于约定优于配置的原则,最优先的应该是通过隐式Bean的发现机制和自动装配的原则。这样的好处是减少程序开发者的决定权,简单而不失灵活
- 在没有办法使用自动装配原则的情况下应该优先考虑Java接口和类中实现配置,这样的好处是避免XML配置的泛滥
- 在上述方法都无法使用的情况下,只能选择XML去配置Spring IoC容器
10.3 通过XML配置装配Bean
-
首先给出XML文件的模板
1
2
3
4
5
6
<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">
<!-- Spring Bean 配置代码 -->
</beans>- 在上述代码中引入了一个
beans
的定义,它是一个根元素,而XSD文件也被引入了
- 在上述代码中引入了一个
10.3.1 装配简易值
-
示例1: 简易XML装配
bean
1
2
3
4
5<bean id="role2" class="Role">
<property name="id" value="1"/>
<property name="roleName" value="高级工程师"/>
<property name="note" value="重要人员"/>
</bean>id
属性是Spring
对于这个Bean
的编号,如果没有显式指定id
,则Spring
将采用全限定名#{number}
的格式生成编号class
是一个类的全限定名property
元素是定义类的属性,其中name
属性定义的是属性名称,而value
是属性值
-
示例2: 有时需要向类A中注入自定义的类B
1
2
3
4
5
6
7
8
9<bean id="source" class="Source">
<property name="fruit" value="橙汁"/>
<property name="sugar" value="少糖"/>
<property name="size" value="大杯"/>
</bean>
<bean id="juiceMaker" class="JuiceMaker">
<property name="beverageShop" value="贡茶"/>
<property name="source" ref="source"/>
</bean>- 可以通过
ref
属性来引用对应的Bean
- 可以通过
10.3.2 装配集合
-
Spring主要使用下列标签完成对集合的装配
List
标签为对应的<list>
元素进行装配,然后通过多个<value>
元素设值Map
属性为对应的<map>
元素进行装配,然后通过多个<entry>
元素设值,只是entry
包含一个键(key)和一个值(value)的设置。Properties
属性,为对应的<properties>
元素进行装配,通过多个<property>
元素设置,只是prop
元素有一个必填属性key
,然后可以设置值。Set
属性为对应的<set>
元素进行装配,然后通过多个<value>
元素设值。- 对于数组而言,可以使用
<array>
设置值,然后通过多个<value>
元素设值。
-
示例: 装配集合类
-
首先定义一个复杂的类
1
2
3
4
5
6
7
8
9public class ComplexAssembly {
private Long id;
private List<String> list;
private Map<String, String> map;
private Properties prop;
private Set<String> set;
private String[] array;
}
/*getter and setter*/ -
装配这个集合类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38<bean id="complexAssembly" class="ComplexAssembly">
<property name="id" value="1"/>
<property name="list">
<list>
<value>value_list_1</value>
<value>value_list_2</value>
<value>value_list_3</value>
</list>
</property>
<property name="map">
<map>
<entry key="key1" value="value_key_1"/>
<entry key="key2" value="value_key_2"/>
<entry key="key3" value="value_key_3"/>
</map>
</property>
<property name="prop">
<props>
<prop key="prop1">value_prop_1</prop>
<prop key="prop2">value_prop_2</prop>
<prop key="prop3">value_prop_3</prop>
</props>
</property>
<property name="set">
<set>
<value>value_set_1</value>
<value>value_set_2</value>
<value>value_set_3</value>
</set>
</property>
<property name="array">
<array>
<value>value_array_1</value>
<value>value_array_2</value>
<value>value_array_3</value>
</array>
</property>
</bean>
-
10.3.3 命名空间装配
10.4 通过注解装配Bean
-
简介: 注解功能更加强大,它既可以实现
XML
的功能,也提供了自动装配的功能 -
在
Spring
中,它提供了两种方式来让Spring IoC
容器发现Bean
- 组件扫描: 通过定义资源的方式,让
Spring IoC
容器扫描对应的包,从而把Bean
装配进来 - 自动装配: 通过注解定义,使得一些依赖关系可以通过注解完成
- 组件扫描: 通过定义资源的方式,让
-
通过扫描和自动装配,大部分的工程都可以用
Java
配置完成,而不是XML
,这样可以有效减少配置和引入大量XML
10.4.1 使用@Component
装配Bean
-
示例1: 使用注解装配简单的
Bean
-
定义
POJO
1
2
3
4
5
6
7
8
9
10
11
12
13package ComponentDemo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
"role") (value =
public class Role {
"1") (
private Long id;
"role_name_1") (
private String roleName;
"role_note_1") (
private String note;- 注解
@Component
代表Spring IoC
会把这个类扫描生成Bean
实例,其中的value
属性代表这个类在Spring
中的id
- 注解
@Value
代表的是值的注入,这里只是注入了一些简单值
- 注解
-
定义
Java Config
类1
2
3
4
5
6
7package ComponentDemo;
import org.springframework.context.annotation.ComponentScan;
public class PojoConfig {
}- 这个类的作用是告诉
Spring IoC
去哪里扫描对象 - 注解
@ComponentScan
代表进行扫描,默认是扫描当前包的路径,POJO
的包名和它保持一致时才能扫描
- 这个类的作用是告诉
-
使用注解生成
Spring IoC
容器1
2
3
4
5
6
7
8
9
10
11
12
13package ComponentDemo;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(PojoConfig.class);
Role role = ctx.getBean(Role.class);
System.out.println(role.getNote());
((AnnotationConfigApplicationContext) ctx).close();
}
}
-
-
@ComponentScan
存在着两个配置项basePackages
: 它可以配置一个Java包的数组,Spring
会根据它的配置扫描对应的包和子包,将配置好的Bean
装配进来basePackageClasses
: 它可以配置多个类,Spring
会根据配置的类所在的包,为包和子包进行扫描装配对应的配置Bean
- 如果采用多个
@ComponentScan
去定义对应的包,但是每定义一个@ComponentScan
,Spring
就会为所定义的类产生一个新的对象 - 对于
basePackages
和basePackageClasses
来说,前一种可读性更好,所以会在项目中优先选择前一种
10.4.2 自动装配—@Autowired
-
自动装配简介
Spring
一般先完成Bean
的定义和生成,然后再寻找需要注入的资源- 当
Spring
生成所有的Bean
后,如果发现这个注解,它就会再Bean
中查找,然后找到对应的类型,将其注入进来,这样就完成了依赖注入 - 所谓自动装配技术是一种由
Spring
自己发现对应的Bean
,自动完成装配工作的方式,它会应用到一个十分常见的注解@Autowired
-
示例: 一个简单的自动装配例子
-
定义
POJO
: 如前文的Role
类 -
定义
RoleService
接口1
2
3
4
5package ComponentDemo;
public interface RoleService {
public void printRoleInfo();
} -
定义
RoleServiceImpl
实现类1
2
3
4
5
6
7
8
9
10
11
12
13
14
15package ComponentDemo;
import org.springframework.stereotype.Component;
"roleServiceImpl") (value =
public class RoleServiceImpl implements RoleService {
private Role role = null;
public void printRoleInfo() {
System.out.println("id = " + role.getId());
System.out.println("roleName = " + role.getRoleName());
System.out.println("note = " + role.getNote());
}
} -
定义
Java Config
类:略 -
测试:略
-
-
其他
@Autowired
的注解表示在Spring IoC
定位所有的Bean
后,这个字段需要按类型注入,这样IoC
容器会寻找资源,然后将其注入- IoC容器有时候会寻找失败,在默认的情况下寻找失败它就会抛出异常
- 通过
@Autowired(required=false)
配置,可以在找不到资源时,不注入,但这样会产生空指针问题
- 通过
10.4.3 自动装配的歧义性(@Primary
和@Quaifier
)
-
背景: 在java中一个接口可能有多个实现类,这个时候
@Autowired
注解无法判断应该把哪个实现类的对象注入进来,从而抛出异常 -
方案1: 注解
@Primary
- 这个注解会告诉
Spring IoC
容器,优先使用拥有此注解的类注入 - 例如
1
2
3
4import org.springframework.context.annotation.Primary
"roleService3") (
public class RoleServiceImpl3 implements RoleService{} - 这个注解会告诉
-
方案2:注解
@Qualifier
- 这个注解会使用名称查找而不是类型查找的方式
- 例如
1
2
3
4
5
6public class RoleController{
"roleService3") (
private RoleService roleService = null;
//...
}
10.4.4 装载带有参数的构造方法类
-
背景: 某些时候构造方法是有参数的,对于带参数的构造方法,我们也可以通过注解进行注入
-
方案: 使用
@Autowired
或者@Qualifer
来修饰参数 -
例如
1
2
3public RoleController(@Autowired RoleService roleService){
this.roleService = roleService;
}
10.4.5 使用@Bean
装配Bean
-
背景: 对于java来说,大部分开发都需要引入第三方的包,而且往往没有这些包的源码,这时候无法为这些包的类加入
@Component
注解,让它们成为开发环境中的Bean
-
方案:这个时候Spring给予一个注解
@Bean
,它可以注解到方法上,并且将方法返回的对象作为Spring
的Bean
,存放到IoC
容器中 -
例如: 通过注解
@Bean
装配DataSource
的Bean
1
2
3
4
5
6
7
8
9
10
11"dataSource") (name=
public DataSource getDataSource(){
Properties props = new Properties();
props.setProperty("driver","com.mysql.jdbc.Driver");
DataSource dataSource = null;
try{
dataSource = BasicDataSourceFactory.createDataSource(props);
}catch(Exception e){
e.printStackTrace();
}
} -
这里还配置了
@Bean
的name
选项为dataSource
,这就意味着Spring生成该Bean
的时候就会使用dataSource
作为其BeanName
。和其他Bean
一样,它也可以通过@Autowired
或者@Qualifier
等注解注入到别的Bean
中
10.5 装配的混合使用
Spring同时支持XML和注解两种装配方式,无论采用哪种,本质都是将Bean
装配到Spring IoC
容器中
建议
- 在自己的工程中所开发的类尽量使用注解方法,因为使用它更为简单
- 对于引入第三方或者服务的类,尽量使用
XML
方式,这样可以尽量减少对第三方包细节的理解,更清晰
如何将xml
定义的bean
引入java
配置中?
- 可以使用注解
@ImportResourse
,它可以配置多个XML
配置文件,将这些文件中定义的bean
全部引入 - 例如
1
2
3
4
5
6package com.ssm.annotation.config
import org.springframework.context.anotation.ComponentScan;
import org.springframework.context.anotation.ImportResource;
"com.ssm.chapter10.annotation"}) (basePackages={
"classpath:spring-dataSource.xml"}) ({
public class ApplicationConfig{}
10.6 使用Profile
背景: 在软件开发中,可能开发人员使用一套环境,而测试人员使用另一套环境,这就有了在不同的环境中进行切换的需求。Spring也对这样的场景进行了支持,在Spring中可以定义Bean
的Profile
,方式有两种
- 使用注解
@Profile
配置 - 使用
xml
定义Profile
10.6.1 使用注解@Profile配置
例子如下
1 | package com.ssm.chapter10.profile; |
10.6.2 使用xml定义Profile
例子如下
1 |
|
10.6.3 启动Profile
当启动Java
配置或者XML
配置Profile
时,可以发现这两个Bean
并不会被加载到SpringIoC
容器中,需要自行激活Profile
。激活Profile
的方法有5种
- 在使用
SpringMVC
的情况下可以配置web
上下文参数,或者DispatchServlet
参数 - 作为
JNDI
条目 - 配置环境变量
- 配置
JVM
启动参数。 - 在集成测试环境中使用
@ActiveProfiles
10.7 加载属性(properties)文件
略
10.8 条件化装配Bean
背景: 在某些条件下需要进行条件判断,判断是否需要装配Bean
。
方案: 这时Spring
提供了注解@Conditional
去配置,通过它可以配置一个或多个实现了Condition
接口的类
例子:
-
如何放置条件判断
1
2
3
4
5"dataSource") (name=
({DataSourceCondition.class})
public DataSource getDataSource(){
//.....
} -
条件判断类的实现
1
2
3
4
5
6public class DataSourceCondition implements Condition{
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata){
//....
}
}- 这里要求实现接口
Condition
的matches
方法。如果此方法返回true
则Spring
会创建对应的Bean
- 这里要求实现接口
10.9 Bean的作用域
在默认情况下,SpringIoc
容器只会对一个Bean
创建一个实例,有时候我们希望容器可以产生多实例,就需要修改Spring
的作用域
Spring
提供4种作用域
- 单例(singleton): 默认选项,在整个应用中,
Spring
只会为其生成一个Bean
实例 - 原型(prototype): 当每次通过容器获取
Bean
时都会创建一个实例 - 会话(session): 在会话过程中
spring
只创建一个实例 - 请求(request): 在一个请求中
spring
只创建一个实例
10.10 使用Spring表达式(SpringEL)
SpringEL的主要功能
- 使用
Bean
的id
来引用Bean
- 使用指定对象的方法和访问对象的属性
- 进行运算
- 提供正则表达式进行匹配
- 集合配置
也就是为注解提供一定的逻辑运算功能,具体使用方式略