文章目录
- XML 方式装配Bean
- 一、实例化 javaBean
- 二、属性注入
- 三、使用Spring 表达式语言进行装配(SpEL)
- 注解装配Bean
- 一、装配Bean
- 二、注入属性
- 整合多个Spring 文件
- 自动装配
前言
Spring 是一个基于容器的框架,但是如果没有配置那它就是一个空容器,所以我们需要配置Spring 来告诉Spring 加载哪些Bean以及如何装配这些Bean。
我们主要来讲在XML文件里怎么装配Bean。
XML 方式装配Bean
一、实例化 javaBean
首先我们需要创建Spring 配置。
在XML文件中声明Bean时,Spring配置文件的根元素来源于Spring beans 命名空间所定义的元素,下面是一个典型的Spring XML配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!--根标签-->
<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">
<!--bean 在这儿定义-->
</beans>
1.bean 实例化的三种方式
- 通过无参构造器创建
- 通过静态工厂创建
- 通过一般工厂创建
1.通过无参构造器创建
这是一个javaBean 类
public class Student{
public void show(){
System.out.println("Student show……");
}
}
在XML文件里面进行声明
<bean id = "student" class = "com.tulun.bean.Student" "/>
测试一下
//使用一般工厂获取对象
public static void test2() {
ApplicationContext context = new ClassPathXmlApplicationContext("springbean.xml");
Student student = (Student) context.getBean("student");
student.show();
}
2.通过静态工厂
这是一个静态工厂
public class StaticFactory {
public static Student getBean(){
return new Student();
}
}
在XML里面配置
<!--通过静态工厂创建student类-->
<bean id="student2" class="com.tulun.factory.StaticFactory" factory-method="getBean" scope="prototype"/>
class :表示我们想要的对应的类。
factory-method:表示我们要调用的类里面包含的方法
测试方法
//使用静态工厂获取对象
public static void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("springbean.xml");
Student student = (Student) context.getBean("student2");
student.show();
}
3.使用一般工厂创建对象
一般工厂类
public class Factory {
//通过普通工厂类创建student类
public Student getBean(){
return new Student();
}
}
配置类
<!--通过一般工厂来创建Student 类-->
<bean id="factory" class="com.tulun.factory.Factory" />
<bean id="student3" factory-bean="factory" factory-method="getBean"/>
测试
//使用一般工厂获取对象
public static void test2() {
ApplicationContext context = new ClassPathXmlApplicationContext("springbean.xml");
Student student = (Student) context.getBean("student3");
student.show();
}
注意:在这些Bean 中必须要有无参构造器,这些类是通过反射得到的,必须要有无参构造器。
2.Bean 的作用域
所有的Spring Bean 默认都是单例的。当容器分配一个Bean时,它总是返回的是同一个实例。那有时候我们需要获得唯一的Bean ,那么我们可以在配置Bean 时为其声明一个作用域
<bean id = "student" class = "com.tulun.bean.Student" scope="singleton"/>
scope 属性表示了这个Bean 的作用域
作用域 | 定义 |
singleton | 在每一个Spring容器中,一个Bean定义只有一个对象实例 |
prortotype | 允许Bean 的定义可以被实例化任意次(每次调用都创建一个实例) |
request | 在一次HTTP请求中,每个Bean 定义对应一个实例。该作用域仅在基于Web的Spring 上下文(例如Spring MVC)中才生效 |
session | 在一次HTTP Session请求中,每个Bean 定义对应一个实例。该作用域仅在基于Web的Spring 上下文(例如Spring MVC)中才生效 |
global-session | 在一个全局HTTP Sesion中,每个Bean定义对应一个实例。该作用域仅在Portlet上下文中才有效 |
3.Bean 的初始化和销毁
当实例化一个Bean 时,可能需要一些初始化的操作确保该Bean 处于可用状态。同样,当不再需要Bean,将其从容器中移除,可能也需要一些清除工作。Spring 提供了Bean生命周期的各种方法。初始化和销毁只需要用init-method 和destory-method 参数来配置元素。
给Student 类添加这么两个方法
public void start(){
System.out.println("开始");
}
public void end(){
System.out.println("结束");
}
修改配置文件
<bean id = "student"
class = "com.tulun.bean.Student"
scope="singleton"
init-method="start"
destroy-method="end">
</bean>
因为我们没有进行销毁,所以并没有调用end()方法,但是在对象刚被实例化的时候它就调用了start()方法,这足以说明问题。
InitializingBean 和DisposableBean
为Bean 定义初始化和销毁方法的另一种可选的方式是让Bean 实现Spring 的这两个接口。Spring 容器以特殊的方式对待实现这两个接口的Bean,允许它们进入Bean的生命周期。InitializingBean 声明了一个afterPropertiesSet()方法作为初始化方法。而DisPosableBean 声明了一个destory()方法,该方法在Bean 从应用文上下移除时会被调用。
优点:Spring 能够自动检测实现了这些接口的Bean,而无需额外的配置。
缺点:Bean 与Spring 的API产生了耦合。唯一可以使用Spring 生命周期接口的场景是开发一个明确在Spring 容器内使用的框架Bean。
默认的init-method 和destory-method
如果在上下文中定义的很多Bean都拥有相同名字的初始化和销毁方法,可以使用 元素的default-init-method 和defaul-destory-method属性。
default-init-method 属性为应用上下文中所有的bean 设置了共同的初始化方法。
defaul-destory-method 属性为应用上下文中所有的bean 设置了共同的销毁方法。
<?xml version="1.0" encoding="UTF-8"?>
<!--根标签-->
<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"
default-init-method="start"
default-destroy-method="end">
</beans>
二、属性注入
上面这个类没有任何属性,那么我们怎么样注入属性给javaBean呢?
java 支持三种属性注入
- 构造器注入
- setter 方法注入
- 接口注入
Spring 框架支持的属性注入
- 构造器注入
- setter 方法注入
1.一般属性注入
1.构造器注入
先给Student 类加一个属性name 吧。既然是构造器注入,那得加一个带参的构造函数吧。
public class Student {
private String name;
public Student() {
}
public Student(String name) {
System.out.println("构造器");
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
System.out.println("set 方法注入属性");
this.name = name;
}
}
配置文件
<bean id = "student" class = "com.tulun.bean.Student" scope="singleton">
<!--通过有参构造器进行属性注入-->
<constructor-arg name="name" value="张三"></constructor-arg>
</bean>
测试
public static void iocTest(){
//加载spring配置文件,创建spring对象
ApplicationContext context = new ClassPathXmlApplicationContext("springbean.xml");
Student student = (Student) context.getBean("student");
}
如果调用构造器那么控制台会打印出构造器三个字
2.setter 方法注入
<bean id = "student" class = "com.tulun.bean.Student" scope="singleton">
<property name="name" value="李四"></property>
</bean>
重新测试一下,控制台应该会打印“set 方法注入属性”
2.复杂对象的注入
对象
这里有两个类,UserService 和UserDao
public class UserService {
UserDao userDao1;
public UserDao getUserDao1() {
return userDao1;
}
public void setUserDao1(UserDao userDao1) {
this.userDao1 = userDao1;
}
public void add(){
userDao1.add();
}
}
public class UserDao {
public void add(){
System.out.println("图论");
}
}
在XML里面装配它
<!--注入一个对象-->
<!-- 创建dao 层对象-->
<bean id="userDao" class="com.tulun.dao.UserDao"></bean>
<!--创建service 层对象-->
<bean id="userService" class="com.tulun.service.UserService">
<property name="userDao1" ref="userDao"></property>
</bean>
ref 表示是一个引用。
我们给Student 里面增加这么几个属性
private String[] str;
private List<String> list;
private Map<String,String> map;
private Properties sp;
数组
<!--数组-->
<property name="str">
<list>
<value>1</value>
<value>2</value>
</list>
</property>
标签表示数组,在这个标签下有value 属性,我们可以定义任意多个 value 给数组
集合(List,Set)
<!--list-->
<property name="list">
<list>
<value>你</value>
<value>好</value>
</list>
</property>
list 集合和数组是一样的,当是Set集合的时候,可以把<list>标签改为<set> 即可
Map 集合
<!--map-->
<property name="map">
<map>
<entry key="tulun" value="tulun"></entry>
</map>
</property>
Map 集合和另外两个集合优点不一样,标签表示这是一个map,map 存储的是键值对,则代表一个节点,节点包括key 和value。
entry 中的元素不仅仅有这两个,看一下这个表
属性 | 用途 |
key | 指定map 中entry 的键为String |
key-ref | 指定map中entry 的键为Spring 上下文中其他的Bean 引用 |
value | 指定map中entry的值为String |
value-ref | 指定map中entry 的值为Spring上下文中其它的Bean 引用 |
Properties 集合
如果配置的map 和值都为String,我们可以考虑使用Properties代替map。Properties 提供了和map大致相同的功能,但是它指定键和值都必须为String
<!--properties-->
<property name="sp">
<props>
<prop key ="drive">wjm</prop>
</props>
</property>
<props>元素构建了一个Properties 值,这个Properties 的每一个成员都由<prop>元素定义,每一个元素都由一个key 属性,其定义了Properties 每个成员的键,而每一个成员由<prop>元素的内容所定义。
**装配内部Bean **
或许你可能很熟悉内部类的概念了,类似的,内部Bean 是定义在其他Bean 内部的Bean。
用那个UserService和UserDao 的例子
<bean id = "userService2" class = "com.tulun.service.UserService">
<property name="userDao1">
<bean class="com.tulun.dao.UserDao"></bean>
</property>
</bean>
内部Bean 不仅使用与setter 注入,还适用于构造器注入
<bean id ="userService3" class="com.tulun.service.UserService">
<constructor-arg>
<bean class="com.tulun.dao.UserDao"></bean>
</constructor-arg>
</bean>
内部Bean 没有id,我们也不可能通过名字来引用内部的Bean。所以内部Bean 也不能被复用,仅仅适用于一次性注入。
装配空值
在有些场景下,如果不能完全确定属性的值为null,那么就必须显式的为该属性装配一个null值。
<bean id="userService4" class="com.tulun.service.UserService">
<property name="userDao1"><null></null></property>
</bean>
3.使用Spring 的命名空间p来装配属性
命名空间p的schema URI为xmlns:p=“http://www.springframework.org/schema/p”,只需要在Spring 的XML配置中增加一下声明就可以用了
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
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">
可以使用p:作为<bean>元素所有属性的前缀来装配<bean>属性
<bean id = "student4"
class = "com.tulun.bean.Student"
p:name="wjm">
</bean>
三、使用Spring 表达式语言进行装配(SpEL)
SpEL是在Spring3时提出的,在这个过程中所使用的表达式会在运行时计算得到值,然后以一种强大和简洁的方式将值装配到Bean属性和构造器参数中去。
怎样使用SpEL表达式
SpE表达式要放到#{ }中,在括号里面就是表达式了。
表示字面值
#{1}
这就是一个简单的表达式,表示为1,这个表达式没有任何意义,直接用1也可以的,这只是为了让大家了解什么是表达式。不仅如此,它还可以用来表示浮点数、String以及Boolean,甚至数值还可以用科学计数法的方式进行表示。
#{1.36} //表示浮点
#{'hello'} //表示String
#{false} //表示Boolean
#{9.87E4} //科学计数法
引用Bean 、属性和方法
假设我们现在有一个ID 为 student 的bean,可以通过 #{student} 把这个Bean 装配给其它Bean
#{student}//调用bean
#{student.name}//调用属性
#{student.show()}//调用方法
在表达式中使用类型
如果要在SpEL表达式中使用类作用域的方法和常量就使用T{}
#{T(java.lang.Math).PI}//访问Math类的常量
#{T(java.lang.Math).random()}//访问Math类的静态方法
SpEL 运算符
算数运算 | + 、-、 *、 / 、%、^ |
比较运算 | <、>、==、<=、>=、 lt、gt、eq、le、ge |
逻辑运算 | and、 or、 not |
条件运算 | ?:(ternary)、?:(Elvis) |
正则表达式 | matches |
算数运算符和在java 里的作用一样
比较运算符也没变,结果是个Boolean 值。
计算集合
SpEL最神奇的地方就是与集合和数组相关的。[] 中括号运算符用来从数组或者集合中获取元素,不仅如此,它还可以从String 中获取一个字符
#{student.list[1]} //表示从student 这个bean 的list 属性拿到第2个元素,下标从0开始
#{student.name[2]} //从student 的name属性拿到了字符串中的第三个字符
SpEL表达式还提供了查询运算符(.?[])用来对集合进行过滤
假设我们有一个Student 的集合students,Student 有一个class 的属性
//我们得到了students 中subject 属性为math的所有的class
#{students.class.?[subject eq "math"]}
(.^[])和(.$[])运算符分别表示在集合中查询的第一个匹配项和最后一个匹配项
//在列表里查第一个subject 为math的 calss
#{students.class.^[subject eq "math"]}
//在列表里查最后一个subject 为math的 calss
#{students.class.$[subject eq "math"]}
SpEL 还提供了投影运算符( .![] ),它会从集合的每个成员中选择特定的属性放到另外一个集合中。
//将subject 属性映射到一个新的String 集合中去
#{students.class.![subject]}
上面说的这些运算符都可以混合使用
注解装配Bean
一、装配Bean
讲完了XML装配Bean,我们通过一个例子简单看一下注解方式怎么装配Bean。
XML 配置
使用注解之前,我们要先导入4+2+aop的jar包,同时引入约束 beans+context
<?xml version="1.0" encoding="UTF-8"?>
<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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!--创建Controller、service、dao层对象-->
<bean id="userDao" class="com.tulun.dao.UserDao"/>
<bean id="userService" class="com.tulun.service.UserService"/>
<bean id="userController" class="com.tulun.controller.UserController">
在配置文件加上开启注解扫描的标签
<!--
开启注解扫描
(1)到包里面扫描类方法、属性上是否有注解
-->
<context:component-scan base-package="com.tulun"></context:component-scan>
</beans>
加上这句话,Spring会自动的去我们指定的包里扫面类以及方法,看其是否有注解。
@Component(value = "user")
//等同于 <bean id = "user" class = "com.tulun.bean.User"/>
@Scope(value = "singleton")
public class User {
private int id;
@PostConstruct
public void add(){
System.out.println("add……");
}
}
- @Component(value = “user”)
这个注解对类女性标注,它可以被Spring 容器识别,Spring 容器将自动将类转化成容器管理的Bean。
除了@Component,Spring 还提供了三个与其等效的注解。一般用于Web 项目,对DAO,service,web层进行注解。
- @Repository:对DAO实现类进行注解
- @Service:对service实现类进行注解
- @Controller:对web层Controller实现类进行注解
之所以用着几个注解,是为了让注解类的用途清晰化。下面这个是设置Bean 的生命周期范围。
- @Scope(value = “singleton”):Bean 的作用域,和XML 中Scope 属性作用一样
下面这两个注解修饰方法,相当于bean的init-method和destory-method属性的功能。
- @PostConstruct:在类被创建时调用
- @PreDestroy:类被销毁是时调用
二、注入属性
1.注入一般的属性
- @Value:为Bean 进行属性注入
上面那个类里面有一个id的属性,用这个注解来为其注入一个值
@Value(value = "1")
private int id;
通过Setter 也可以注入
@Value(value = "1")
public void setId(int id) {
this.id = id;
}
2.注入引用类型属性
@Autowired
UserService userService;
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
除了@Autowired ,Spring 还提供了其它两个注解,三个注解的区别如下:
- @Autowired
@Autowired默认按类型匹配的方式,在容器中查找匹配的Bean,当有且只有一个匹配的Bean时,Spring将其注入到@Autowired注解的变量中。但是如果容器中有超过一个以上的匹配Bean时,例如有两个UserService类型的Bean,这时就不知道将哪个Bean注入到变量中,就会出现异常 - @Qualifier(“userservice”)
这个注解按照指定的名字注入属性 - @Resource(name=“userservice”)
结合和前两个注解,当容器中只有一个匹配的Bean 时,直接注入,当容器中有多个匹配的Bean时,按照指定的名字匹配
整合多个Spring 文件
对于一个大型项目而言,可能有多个XML配置文件,在启动Spring容器时,可以通过一个String数组指定这些配置文件。Spring还允许我们通过< import >标签将多个配置文件引入到一个文件中,进行配置文件的集成,这样启动Spring容器时,就仅需指定这个合并好的配置文件即可。
1.使用Spring 数组整合所有文件
ApplicationContext ac=new ClassPathXmlApplicationContext(new String[]{"bean1.xml","bean2.xml"});
2.使用import 标签
//resource属性指定配置文件位置,支持Spring标准的资源路径
<import resource="classthpath:com/cad/domain/bean1.xml"/>
<import resource="classthpath:com/cad/domain/bean2.xml"/>
自动装配
Spring 提供了四种自动装配的策略
- byName
把与Bean 的属性具有相同名字的其它Bean 自动装配到Bean 对应的属性中 - byType
把与Bean 的属性具有相同类型的Bean 自动装配到Bean 的属性中。但是如果当前应用上下文中有多个与Bean 属性具有相同类型的其它Bean,Spring 会抛出异常。
那么当有多个Bean 都满组条件的时候,我们可以为其首选一个Bean,可以使用<bean> 元素的primary属性,将这个属性设置为true即可,但是primary 属性默认被设置为true,所以等于所有的Bean 都是首选Bean,所以也就不存在首选了,所以我们不得不讲其它的都设置为false。
如果我们在自动装配时想忽略某个Bean,可以设置这些Bean 的autowire-candidate属性为false。这等于是取消了其作为候选Bean 的资格。 - constructor
把与Bean 的构造器入参具有相同类型的Bean 自动装配到构造器对应的入参中,值得注意的是,它还是用类型进行匹配,所以依然会碰到上面的问题,而且,当Bean 有多个构造器,Spring 不会帮我们判断使用哪个构造器,它会直接抛出异常。 - autodetect
它会首先使用constructor 进行动装配,如果失败再尝试用byType。
使用
只需要在Bean 标签里面加上autowire就可以啦
<bean id="userdao" class = "com.tulun.dao.UserDao" autowire="byType"></bean>
默认的自动装配 default-autowire
默认情况下这个属性为false,表示所有的属性都不使用自动郑培,除非在bena 上设置了autowire。
当我们想让配置文件中所有的Bean 都进行自动装配的时候,在beans 上增加属性default-autowire
<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"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
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-4.0.xsd"
default-autowire="byType">
注意
使用了默认的装配策略,我们依然可以使用<bean>元素的autowire 属性来覆盖默认的装配策略。
而且,我们使用了自动装配策略,并不代表我们不能对该Bean的某些属性进行显式装配。