在spring中,不再需要通过传统的方式创建,而是根据注解或者xml将类直接解析成bean,本文讲讲解关于bean的一些知识点。
一、bean的定义
Spring容器集中管理Bean的实例化,Bean实例可以通过BeanFactory的getBean(String beanid)方法得到。BeanFactory是一个工厂,程序只需要获取BeanFactory引用,即可获得Spring容器管理全部实例的引用。程序不需要与具体实例的实现过程耦合。大部分Java EE应用里,应用在启动时,会自动创建Spring容器,组件之间直接以依赖注入的方式耦合,甚至无须主动访问Spring容器本身。
1.基于xml的半定义(需要定义setter方法)
<beans…/>元素是Spring配置文件的根元素,<beans…/>元素可以包含多个<bean…/>子元素,每个<bean…/>元
素可以定义 一个Bean实例,每一个Bean对应Spring容器里的一个Java实例定义Bean时通常需要指定两个属性。
Id:确定该Bean的唯一标识符,容器对Bean管理、访问、以及该Bean的依赖关系,都通过该属性完成。Bean的id属性
在Spring容器中是唯一的。
Class:指定该Bean的具体实现类。注意这里不能使接口。通常情况下,Spring会直接使用new关键字创建该Bean的实
例,因此,这里必须提供Bean实现类的类名。
当我们在配置文件中通过<bean id=”xxxx” class=”xx.XxClass”/>方法配置一个Bean时,这样就需要该Bean实现
类中必须有 一个无参构造器。
示例:
在applicationContext.xml中配置bean信息
<bean name="userDao" class="com.spring.User"></bean>
2.基于注解的bean定义(不需要提供setter方法)
Spring为此提供了四个注解,这些注解的作用与上面的XML定义bean效果一致,在于将组件交给Spring容器管理。
组件的名称默认是类名(首字母变小写),也可以自己修改:
@Component:当对组件的层次难以定位的时候使用这个注解
@Controller:表示控制层的组件
@Service:表示业务逻辑层的组件
@Repository:表示数据访问层的组件
示例:
注解定义bean
//不知道该组件属于什么层次时,可以直接使用该注解@Component
@Component("address")
public class Address{
private Integer id;
//对于非bean id时的注入直接用@Value赋值
@Value("湖北省黄石市阳新县黄双口镇金星村")
private String name;
public String getName() {
return name;
}
}
//不知道该组件属于什么层次时,可以直接使用该注解@Component
@Component("user")
public class Teacher {
//对于非bean id时的注入直接用@Value赋值
@Value("25")
private Integer age;
//对于非bean id时的注入直接用@Value赋值
@Value("石破天")
private String name;
//对于bean id类型使用@Resource注解进行注入
@Resource
private Address address;
public Integer getAge() {
return name;
}
public String getName() {
return name;
}
}
applicationContext.xml 中添加一下配置信息。
<!--扫描组件的包目录-->
<context:component-scan base-package="entity"/>
3.基于Java类的bean定义(需要提供setter方法)
@Configuration
public class BeansConfiguration {
@Bean
public Student student(){
Student student=new Student();
student.setName("张三");
student.setTeacher(teacher());
return student;
}
@Bean
public Teacher teacher(){
Teacher teacher=new Teacher();
teacher.setName("李四");
return teacher;
}
}
二、bean的作用域
当通过Spring容器创建一个Bean实例时,不仅可以完成Bean实例的实例化,还可以为Bean指定特定的作用域。
Spring支持5种作用域:
Singleton:单例模式。在整个SpringIoC容器中,使用singleton定义的Bean将只有一个实例,这是默认的配置。
Prototype:原型模式。每次通过容器的getBean方法获取prototype定义的Bean时,都将产生一个新的Bean实
例。
request:对于每次HTTP请求,使用request定义的Bean都将产生一个新的实例,即每次HTTP请求都会产生不同
的Bean实例。当然只有在WEB应用中使用Spring时,该作用域才真正有效。
session:对于每次HTTPSession,使用session定义的Bean都将产生一个新的实例时,即每次HTTP Session都将
产生不同的Bean实例。同HTTP一样,只有在WEB应用才会有效。
global session:每个全局的HTTPSession对应一个Bean实例。仅在portlet Context的时候才有效。
比较常用的singleton和prototype。如果一个Bean实例被设置为singleton,那么每次请求该
Bean 时都会获得相同的实例。容器负责跟踪Bean实例的状态,负责维护Bean实例的生命
周期行为。如果一个Bean实例被设置为prototype,那么每次请求该di的Bean,Spring都会
创建一个新的Bean实例返回给程序,在这种情况下,Spring容器仅仅使用new关键字创建
Bean实例,一旦创建成功,容器将不会再跟踪实例,也不会维护Bean实例的状态。
如果我们不指定Bean的作用域,则Spring会默认使用singleton作用域。设置Bean的作用域是通过scope属性来指
定。 可以接受Singleton、prototype、request、session、global session 5个值。
三、bean的创建
Spring支持如下三种方式创建Bean
1:调用构造器创建Bean
调用构造方法创建Bean是最常用的一种情况Spring容器通过new关键字调用构造器来创建Bean实例,通过class
属性指定Bean实例的实现类,也就是说,如果使用构造器创建Bean方法,则<bean/>元素必须指定class属性,
其实Spring容器也就是相当于通过实现类new了一个Bean实例。调用构造方法创建Bean实例,通过名字也可以
看出,我们需要为该Bean类提供无参数的构造器。
因为是通过构造函数创建Bean,因此我们需要提供无参数构造函数,另外我们定义了一个name属性,并提供了
setter方法,Spring容器通过该方法为name属性注入参数。
示例:(以xml形式定义bean)
package com.spring;
public class User implements IUser {
private String name;
private Integer age;
public User(){
System.out.println("通过spring会通过new关键字调用无参构造器实例化");
}
//提供setter方法,在xml中定义bean时,将调用该方法给对应的属性赋值即所谓的依赖注入
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
}
applicationContext.xml
<?xml version="1.0" encoding="GBK"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
<!-- 指定class属性,通过构造方法创建Bean实例 -->
<!--通过<bean>元素的id属性指定该bean的唯一名称,class属性指定该bean的实现类,可以理解成
Spring容器就是通过该实现类new了一个Bean-->
<bean id="user" class="com.spring.usermanage.entity.User">
<!-- 通过<constructor-arg>标签的name属性和value属性指定了:构造方法赋值 -->
<constructor-arg name="name" value="魔术师"></constructor-arg>
</bean>
</beans>
2:调用静态工厂方法创建Bean
通过静态工厂创建Bean是把创建Bean的任务交给了静态工厂,而不是
构造函数,这个静态工厂就是一个Java类,在使用静态工厂创建Bean需要在<bean/>元素内添加class属性,
静态工厂是一个Java类,那么该class属性指定的就是该工厂的实现类,而不再是Bean的实现类,告诉Spring
这个Bean应该由哪个静态工厂创建,另外我们还需要添加factory-method属性来指定由工厂的哪个方法来创建
Bean实例,因此使用静态工厂方法创建Bean实例需要为<bean/>元素指定如下属性:
class:指定静态工厂的实现类,告诉Spring该Bean实例应该由哪个静态工厂创建(指定工厂地址)
factory-method:指定由静态工厂的哪个方法创建该Bean实例(指定由工厂的哪个车间创建Bean)
如果静态工厂方法需要参数,则使用<constructor-arg.../>元素传入
示例:
步骤: 1.定义一个接口 Person
2.定一个实现Person接口的实现类HuBeiMan
3.定义另外一个实现Person接口的实现类ShenZenMan
4.定义一个静态工厂,主要是用来创建实体类
5.xml定义bean(创建bean实例,实际实例由静态工厂创建)
1
public interface Person{
public void say();
}
2
public class HuBeiMan implements Person{
private String msg;
//提供setter方法
public void setMsg(String msg)
{
this.msg = msg;
}
@Override
public void say(){
System.out.println("湖北人说的方言很难懂");
}
}
public class ShenZenMan implements Person{
private String msg;
//提供setter方法
public void setMsg(String msg)
{
this.msg = msg;
}
@Override
public void say(){
System.out.println("深圳人大部分来自全国各地");
}
}
3
//在xml中定义bean时,需要创建什么类的bean实例,可以class直接指向静态工厂类
public class PersonFactory {
//调用对应方法,并传递对应参数
public static Person getPerson(String arg) {
根据指定name来获取对应的实例,该name在xml中
if(arg.equalsIgnoreCase("chinese"))
{
return new Chinese();
}
else
{
return new American();
}
}
}
4
<?xml version="1.0" encoding="GBK"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
<!-- 定义HuBeiMan Bean 由PersonFactory工厂的getPerson方法创建 -->
<bean id="HuBeiMan" class="com.spring.PersonFactory" factory-method="getPerson">
<!-- 为静态工厂的getPerson()方法传参 -->
<constructor-arg value="HuBeiMan"/>
<!-- 调用setMsg()方法为msg属性注入参数值 -->
<property name="msg" value="我是中国人"/>
</bean>
<!-- 创建ShenZenMan Bean -->
<bean id="ShenZenMan" class="com.spring.PersonFactory" factory-method="getPerson">
<constructor-arg value="ShenZenMan"/>
<property name="msg" value="我是深圳人"></property>
</bean>
</beans>
3:调用实例工厂方法创建Bean
静态工厂通过class指定静态工厂实现类然后通过相应的方法创建即可,调用实例工厂则需要先创建该工厂的Bean
实例,然后引用该实例工厂Bean的id创建其他Bean,在实例工厂中通过factory-bean指定工厂Bean的实例,在调
用实例化工厂方法中,不用在<bean/>中指定class属性,因为这时,咱们不用直接实例化该Bean,而是通过调用
实例化工厂的方法,创建Bean实例。调用实例化工厂需要为<bean/>指定一下两个属性:
factory-bean :该属性指定工厂Bean的id
factory-method:该属性指定实例工厂的工厂方法。
示例:在上一种方法示例的基础上修改对应的配置文件
<?xml version="1.0" encoding="GBK"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
<!-- 配置工厂Bean,class指定该工厂的实现类,该Bean负责产生其他Bean实例 -->
<bean id="personFactory" class="com.spring.PersonFactory"/>
<!-- 由实例工厂Bean的getPerson()方法,创建HuBeiMan Bean, -->
<bean id="HuBeiMan" factory-bean="personFactory" factory-method="getPerson">
<!-- 为该方法传入参数为HuBeiMan-->
<constructor-arg value="HuBeiMan"/>
</bean>
<!-- 由实例工厂Bean的getPerson()方法,创建ShenZen Bean, -->
<bean id="ShenZen" factory-bean="personFactory" factory-method="getPerson">
<constructor-arg value="ShenZen"></constructor-arg>
</bean>
</beans>
实例工厂创建Bean和调用静态工厂的区别
调用实例工厂将工厂单独拿了出来(先实例化工厂)创建一个工厂Bean,通过工厂<bean>的class属性指定工厂的
实现类,然后再需要创建其他Bean时,只需要在该<bean/>元素添加factory-bean、factory-method指定该Bean
是有哪个实例工厂的哪个方法创建就可以了,放在现实生活中就是:我们先找一个地方创建一个工厂,id指定工
厂注册名字(xxx有限公司),class指定公司所在地,工厂里面有车间(创造其他Bean的方法),那好有了工厂
我们再需要创建其他Bean时,只需要指定这个Bean是由,哪个工厂的哪个车间创建的即可,也就是只需要在该
<bean/>元素添加factory-bean、factory-method属性即可。
四、bean的注入
1 注入bean
在Spring中,注入依赖对象可以采用手工装配或自动装配,一般是使用手工注入,因为自动装配容易出现未知问题,
自动装配是在配置文件中实现的,不用再配置文件中写<property>,但是在类中还是要生成依赖对象的setter方法,
如下:
<bean id="***" class="***" autowire="byType"> 只需要配置一个autowire属性即可完成自动装配
autowire属性值有:
no:默认的。默认为不自动装配
byName:根据属性名称自动进行查找与该name相同的bean并装配,如果没有找到,则属性值为null
byType:根据属性类型自动进行装配,如有多个会报异常,如果没有找到,则属性值为null
以下三种方式都属于手工装配。
1.1 setter属性注入
①ref标识符
<!--通过ref来注入有两种形式-->
<bean id="address" class="com.spring.Address">
<property name="content" value="湖北省黄石市阳新县黄双口镇金星村">
</bean>
<!--1:单独添加ref元素-->
<bean id="user" class="com.spring.User">
<property name="address">
<!--其他的bean的id-->
<ref bean="address"/>
</property>
</bean>
<!--2:property元素中的ref属性-->
<bean id="user" class="com.spring.User">
<property name="address" ref="address">
</property>
</bean>
② value
<bean id="address" class="com.spring.Address">
<!--注入属性非符合类型,即不是bean id时,直接用value进行赋值-->
<property name="content" value="湖北省黄石市阳新县黄双口镇金星村">
</bean>
或者
<bean id="address" class="com.spring.Address">
<!--注入属性非符合类型,即不是bean id时,直接用value进行赋值-->
<property name="content">
<value>湖北省黄石市阳新县黄双口镇金星村</value>
</bean>
1.2 使用构造器注入
示例:
第一步:定义的类符合以下要求
public class User{
//声明依赖对象
private Address address= null;
//生成无参构造函数
public User(){
super;
}
//生成有参构造函数
public User(Address address){
super;
this.address=address;
}
}
第二步:在配置文件中配置该类的bean,并配置构造器,关键元素节点<constructor-arg> ,该节点有四个属
性:
index是索引,指定注入的属性,从0开始,如:0代表personDao,1代表str属性;
type是指该属性所对应的类型,如Persondao对应的是com.aptech.dao.PersonDAO;
ref 是指引用的依赖对象;
value 当注入的不是依赖对象,而是基本数据类型时,就用value;
<!-- 利用构造器配置依赖注入 -->
<!--userDao bean实例-->
<bean id="userDao" class="com.spring.UserDaoImpl"></bean>
<!--service bean实例-->
<bean id="service" class="com.spring.ServiceImpl">
<!--调用service的有参构造函数将userDao作为第一个type为UserDao的bean注入到service bean中-->
<constructorarg index="0" type="com.spring.UserDao" ref="userDao"></constructor-arg>
<!--非bean id直接赋值,不需要指定type-->
<constructor-arg index="1" value="石破天"></constructor-arg>
</bean>
1.3 使用注解注入
在类的内部定义依赖属性上加上注解,并在配置文件中加上<context:annotation-config></context:annotation-config>
信息,该配置信息配置隐式的注册了多个对注释进行解析的处理器:器AutowiredAnnotationBeanPostProcessor、
CommonAnnotationBeanPostProcessor、PersistenceAnnotationBeanPostProcessor等。
注解:
@Resource:默认按类型装配
用@Autowired为上面的代码UserDAO接口的实例对象进行注解,它会到Spring容器中去寻找与
UserDao对象相匹配的类型,如果找到该类型则将该类型注入到userdao字段中;
@Autowired:它先会根据指定的name属性去Spring容器中寻找与该名称匹配的类型。
例如:@Resource(name="userDao"),如果没有找到该名称,则会按照类型去寻找,找到之后,
会对字段userDao进行注入。
示例:
第一步:在依赖属性上加上注解
//不需要setter方法
public class UserServiceImpl implements UserService {
@Resource(name="userDao") //或者@AutoWired
private UserDAO userDao = null;
public void addUser() {
this.userDao.addUser();
}
}
第二步:bean的定义(有三种,这里以xml定义为例)
<bean id="userDao" class="com.spring.UserDaoImpl"></bean>
<bean id="userService" class="com.spring.UserServiceImpl"></bean>
第四步:在applicationContext.xml中加上 <context:annotation-config></context:annotation-config>
2 集合注入
2.1 list
<bean id="test" class="com.spring.test">
<property name="lists">
<!--注入时调用的setter方法的参数类型为List类型时-->
<list>
<value>1</value>
<value>2</value>
<value>3</value>
</list>
</property>
</bean>
2.2 map
<bean id="test" class="com.spring.test">
<property name="map">
<!--注入时调用的setter方法的参数类型为Map类型时-->
<map>
<entry key="key1">
<value>value1</value>
</entry>
<entry key="key2">
<value>key2</value>
</entry>
</map>
</property>
</bean>
2.3 props
<bean id="test" class="com.spring.test">
<property name="props">
<props>
<prop key="key1">value1</prop>
<prop key="key2">value2</prop>
</props>
</property>
</bean>
2.4 set
<bean id="test" class="com.pring.Test">
< property name ="interest" >
<!--注入时调用的setter方法的参数类型为Set类型时-->
< set >
< value > 唱歌 </ value >
< value > 跳舞 </ value >
< value > 书法 </ value >
</ set >
</ property >
</bean>