一、Bean的配置
Spring支持XML和Properties两种配置文件,实际开发中,常用XML配置。Spring中XML配置文件的根标签是<beans>,<beans>包含多个<bean>子元素。每一个<bean>子元素定义一个Bean
<bean>元素的常用属性以及其子元素:
- id:一个bean的唯一标识,Spring对Bean的配置、管理都通过该属性完成。
- name:name属性中可以为Bean指定多个名称,每个名称之间用逗号或分号分开。
- class:指定Bean的具体实现类。必须是一个完整的类名,类的全限定名。
- scope:设定Bean实例的作用域,其属性有:singleton(单例)、prototype(原型)、request、session、global session、application、和websocket,默认为singleton
- constructor-arg:<bean>的子元素,可以使用钙元素传入构造函数参数进行实例化。index属性指定构造参数的序号(从0开始),type指定构造参数的类型,参数值可以用ref或value属性指定。
- property:<bean>的子元素,用于调用Bean实例中的setter方法完成属性的赋值,从而完成依赖注入,该元素中的name属性指定Bean实例中的相应属性名。
- ref:用于指定对Bean工厂中某个Bean实例的引用。
- value:可直接指定一个常量值。
- list、set、map:分别用于封装相应类型的依赖注入,其中list可以封装数组类型。
- entry:用于设置一个键值对。其key属性指定字符串类型的键值,ref或value子元素指定其值。
在配置文件中,通常一个普通的Bean只需要定义id(或者name)和class属性即可,如果在Bean中未指定id或者name,则Spring会将class值作为id使用。
二、Bean的实例化
spring中,要想使用容器中的Bean,需要实例化Bean。实例化Bean有三种方式,分别为构造器实例化(最常用)、静态工厂方式实例化和实例工厂方式实例化。
1、构造器实例化
构造器实例化是指Spring容器通过Bean对应类中默认的无参构造函数方法来实例化Bean。创建项目后,在constructor包下创建Bean1类如下:
public class Bean1 {
}
在此包下创建Spring的配置文件beans.xml,并在配置文件中定义一个id为bean1的Bean,并通过class属性指定其对应的实现类Bean1,代码如下:
<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-4.3.xsd">
<bean id = "bean1" class = "com.constructor.Bean1"/>
</beans>
创建测试类代码如下:
public class InstanceTest1 {
public static void main(String[] args) {
// TODO Auto-generated method stub
//定义配置文件路径
String xmlPath = "com/itheima/instance/constructor/beans1.xml";
//ApplcaitionContext在加载配置文件时,对Bean进行实例化
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
Bean1 bean1 = (Bean1) applicationContext.getBean("bean1");
System.out.println(bean1);
}
}
运行测试类后,首先spring容器ApplicationContext会加载配置文件,加载时,spring容器根据id为bean1的实现类Bean中默认的无参构造方法对Bean进行实例化。
2、静态工厂方式实例化
该方式要求创建一个静态工厂的方法来实现创建Bean的实例,其Bean配置中的class属性所指定的不再是Bean实例的实现类,而是静态工厂类。同时还需要使用factory-method属性来指定所创建的静态工厂方法。创建项目后,在static_factory包下创建Bean2类,同样无任何方法。
同时创建MyBeanFactory类,并在类中创建一个静态方法createBean()来返回Bean2的实例,代码如下:
public class MyBean2Factory {
public static Bean2 createBean() {
return new Bean2();
}
}
配置文件bean2.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-4.3.xsd">
<bean id = "bean2" class = "com.static_factory.MyBean2Factory" factory-method="createBean"/>
</beans>
上述配置中,首先通过<bean>元素的id定义了一个名为bean2的Bean,然后由于使用静态工厂方法,所以用class属性指定其对应的静态工厂实现类MyBeanFactory,同时增加factory-method属性告诉spring容器,其工厂方法为creatBean。
测试类instanceTest2:
public class InstanceTest2 {
public static void main(String[] args) {
// TODO Auto-generated method stub
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("com/itheima/instance/static_factory/beans2.xml");
System.out.println(applicationContext.getBean("bean2"));
}
}
3、实例工厂方式实例化
采用直接创建Bean实例的方式,同时在配置文件中,需要实例化的Bean也不是通过class属性直接指向的实例化类,而是通过factory-bean属性指向配置的实例工厂,然后使用factory-method属性确定使用工厂中的哪个方法。具体代码:
工厂类:使用默认无参构造方法输出。creatBean创建bean对象。
public class MyBean3Factory {
public MyBean3Factory() {
System.out.println("bean3工厂实例化中。。。");
}
public Bean3 createBean() {
return new Bean3();
}
}
配置文件:
<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-4.3.xsd">
<bean id = "myBean3Factory" class = " com.itheima.instance.factory.MyBean3Factory" />
<bean id = "bean3" factory-bean = "myBean3Factory" factory-method = "createBean"/>
</beans>
测试:
public class InstaceTest3 {
public static void main(String[] args) {
// TODO Auto-generated method stub
String xml = "com/itheima/instance/factory/beans3.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xml);
System.out.println(applicationContext.getBean("bean3"));
}
}
三、Bean的作用域
1、作用域的种类
- singleton(单例):在spring容器中只有一个实例,也就是说,无论有多少个Bean引用它,始终指向同一个对象。默认的作用域。
- prototype(原型):每次通过spring容器获取其定义的Bean时,容器都将创建一个新的Bean实例。
- request:在一次http请求中,容器都会返回该Bean的同一个实例,不同的http请求会产生一个新的Bean,而且仅在当前http request中有效。
- session:一次http session中,容器返回该Bean 的同一个实例。
- global session: 在一个全局的http session中,容器会返回改Bean的同一个实例。
- application:为servletContext对象创建一个实例,仅在web相关的applicationContext中有效
- websocket:为每个websocket对象创建一个实例。
2、singleton作用域
只要id与该Bean的id属性相匹配,就会返回同一个Bean实例。singleton作用域对于无状态会话的Bean(dao组件 service组件)为理想的选择。
设置为singleton方式:
<!-- 单例模式 默认 -->
<bean id = "scope" class="com.itheima.scope.Scope" scope = "singleton"/>
测试文件:
public class ScopeTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
String xmlPath = "com/itheima/scope/beans4.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
Scope scope1 = (Scope)applicationContext.getBean("scope");
Scope scope2 = (Scope)applicationContext.getBean("scope");
System.out.println(scope1);
System.out.println(scope2);
}
}
控制台的两次输出相同,正品spring容器之创建了一个scope类的实例。
3、prototype作用域
使用prototype作用域可以保持会话状态的Bean,spring容器会为每个队该Bean的请求都创建一个新的实例。
<bean id = "scope" class="com.itheima.scope.Scope" scope = "prototype"/>
四、Bean的装配方式
Bean的装配可以理解为依赖关系注入。
1、基于XML的装配
spring提供了两种基于XML的装配方式:设置注入(setter injection)和构造注入(constructior injection)。在bean实例化过程中,spring首先会调用Bean的默认构造方法来实例化Bean对象,然后通过反射的方式调用setter方法来注入属性值。因此,设置注入要求一个Bean满足以下两点要求:
- Bean类必须提供一个默认的无参构造方法。
- Bean类必须为需要注入的属性提供对应的setter方法。
设置注入时,在配置文件中,需要使用<bean>元素的子元素<property>来为每个属性注入值;而使用构造注入时,在配置文件中,需要使用<bean>元素的子元素<constructor-arg>来定义构造方法的参数,可以使用value属性来设置该参数的值。
在上一个项目中创建assemble包,并穿件User类,定义username,password和list集合三个属性及其对应的setter方法:
public class User {
private String userName;
private Integer password;
private List<String> list;
private String[] arrs;
private Map<String,String> map;
/**
* 使用构造注入
* 提供带所有参数的有参构造方法
*/
public User(String userName,Integer password, List<String> list,String[] arrs, Map<String,String> map) {
this.userName = userName;
this.password = password;
this.list = list;
this.arrs = arrs;
this.map = map;
}
/**
* 使用设值注入
* 提供默认空的构造方法
* 为所有的属性提供setter方法
*
*/
public User() {
}
public void setUserName(String userName) {
this.userName = userName;
}
public void setPassword(Integer password) {
this.password = password;
}
public void setList(List<String> list) {
this.list = list;
}
public void show() {
System.out.println("user......");
for(int i = 0 ;i <= arrs.length-1;i++) {
System.out.println(arrs[i]);
}
System.out.println(map);
System.out.println(list);
}
public void setArrs(String[] arrs) {
this.arrs = arrs;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
@Override
public String toString() {
return "User[username="+userName+",password="+password+",list="+list+"]";
}
}
创建配置文件beans5.xml,在配置文件中通过构造柱人和设置注入的方式装配User类的实例:
<?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-4.3.xsd">
<!-- 使用构造注入方式装配User实例 -->
<bean id = "user1" class = "com.itheima.assemble.User">
<constructor-arg name = "userName" value = "tom"></constructor-arg>
<constructor-arg name = "password" value = "123456"></constructor-arg>
<constructor-arg name = "list">
<list>
<value>"constructorvalue1"</value>
<value>"constructorvalue2"</value>
</list>
</constructor-arg>
<constructor-arg name = "arrs">
<list>
<value>小王</value>
<value>小李</value>
</list>
</constructor-arg>
<constructor-arg name = "map">
<map>
<entry key="first" value="小王"></entry>
<entry key="second" value="小赵"></entry>
</map>
</constructor-arg>
</bean>
<!-- 注入对象类型 -->
<bean id = "user" class = "com.itheima.assemble.User"></bean>
<bean id = "service" class = "com.itheima.assemble.UserService">
<!-- 注入user对象 -->
<property name="user" ref="user"></property>
</bean>
<!-- 使用设值注入方式装配User实例 -->
<bean id = "user2" class = "com.itheima.assemble.User">
<property name="userName" value = "张三"></property>
<property name="password" value = "654321"></property>
<!-- 注入list集合 -->
<property name="list">
<list>
<value>"setlistvalue1"</value>
<value>"setlistvalue2"</value>
</list>
</property>
<property name="arrs">
<list>
<value>嘻嘻</value>
<value>哈哈</value>
</list>
</property>
<property name="map">
<map>
<entry key="a" value="aaa"></entry>
<entry key="b" value="bbb"></entry>
</map>
</property>
</bean>
</beans>
上述配置文件中<constructor-arg>元素用于定义构造方法的参数,其属性index为其索引,value用于设置注入的值。<list>元素用于为User类对应的list集合注入值。然后又使用设置注入方式装配User类的实例。其中<property>元素用于调用Bean实例中的setter方法完成属性赋值,从而完成依赖注入。测试类:
public class XmlBeanAssembleTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
String xmlPath = "com/itheima/assemble/beans5.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
//构造方式输出:
System.out.println(applicationContext.getBean("user1"));
System.out.println(applicationContext.getBean("user2"));
User user1 = (User) applicationContext.getBean("user1");
User user2 = (User) applicationContext.getBean("user2");
user1.show();
user2.show();
}
}
2、基于Annotation的装配
spring定义了一系列的注解:(较为重要)
- @Component:可以使用此注解描述Spring中的Bean,但是一个泛化的概念,仅仅代表一个组件(Bean),并可以作用在任何层次。
- @Repository:用于将数据访问层(DAO层)的类标识为Spring中的Bean,功能与@Component相同。
- @Service:通常作用在业务成(Service层),用于将业务层的类标识为Spring 中的Bean,功能与@Component相同。
- @Controller:通常作用在控制层(如springMVC中的controller),用于将控制层的类标识为Spring 中的Bean,功能与@Component相同。
- @Autowired:用于对Bean的属性变量,属性的setter方法以及构造方法进行标注。配合对应的注解处理器完成Bean的自动配置工作。默认按照Bean的类型进行装配。
- @Resource:起作用与@Autowried一样。其区别在于@Autowried默认按照Bean类型装配,而@Resource默认按照Bean实例名进行装配。@Resource有两个重要的属性:name和type。spring将name属性解析为Bean的实例名,type解析为Bean的实例类型。
- @Qualifier:与@Autowried注解配合使用,默认的按Bean类型装配修改为按Bean的实例名进行装配。
在上一个项目中创建annotation包,并创建UserDao接口,定义save方法如下:
public interface UserDao {
public void save();
}
创建UserDao接口的实现类:
@Repository("userDao")
public class UserDaoImpl implements UserDao {
@Override
public void save() {
// TODO Auto-generated method stub
System.out.println("UserDao...save...");
}
}
首先使用@Repository注解将UserDaoImpl类标识为Spring的Bean,,创建接口UserService,同样定义一个save方法:
public interface UserService {
public void save();
}
创建UserService接口的实现类:
@Service("userService")
public class UserServiceImpl implements UserService {
@Resource(name="userDao")
private UserDao userDao;
@Override
public void save() {
// TODO Auto-generated method stub
// System.out.println("调用userDao。。。");
this.userDao.save();
System.out.println("userService...save");
}
public UserDao getUserDao() {
return userDao;
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
首先使用@Service注解将UserServiceImpl类标识为Spring的Bean,然后使用@Resource注解标注在属性UserDao上。创建控制类UserController:
@Controller("userController")
public class UserController {
@Resource(name="userService")
private UserService userService;
public void save() {
// System.out.println("调用userService");
this.userService.save();
System.out.println("userController...save...");
}
public UserService getUserService() {
return userService;
}
public void setUserService(UserService userService) {
this.userService = userService;
}
}
使用@Controller注解标注该类。创建配置文件beans6.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"
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命名空间,再配置文件中开启相应的注释处理器 -->
<context:annotation-config/>
<!-- 分别定义三个Bean实例 -->
<!-- 使用bean元素的autowire属性自动装配 -->
<bean id = "userDao" class = "com.itheima.annotation.UserDaoImpl"/>
<bean id = "userService" class = "com.itheima.annotation.UserServiceImpl" autowire = "byName"/>
<bean id = "userController" class = "com.itheima.annotation.UserController" autowire = "byName"/>
</beans>
通过配置<context:annotation-config/> 来开启注解处理器。最后分别定义三个Bean对应编写的三个实例。同时另外一种高效的注解配置方式:
<!-- 高效的注释配置方式 对包路径下的所有Bean进行扫描-->
<!-- 使用context命名空间,通知Spring扫描指定包下所有Bean类,进行注释解析 -->
<context:component-scan base-package="com.itheima.annotation"/>
创建测试类:
public class AnnotationAssembleTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
//定义配置文件路径
String xmlPath = "com/itheima/annotation/beans6.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
UserController controller = (UserController) applicationContext.getBean("userController");
controller.save();
}
}
3、自动装配
可以通过设置<bean>元素中包好的autowried属性来自动装配Bean,就是讲一个Bean自动注入到其他Property中。将配置文件改成自动装配方式:
<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命名空间,再配置文件中开启相应的注释处理器 -->
<!-- <context:annotation-config/> -->
<!-- 分别定义三个Bean实例 -->
<!-- 使用bean元素的autowire属性自动装配 -->
<bean id = "userDao" class = "com.itheima.annotation.UserDaoImpl"/>
<bean id = "userService" class = "com.itheima.annotation.UserServiceImpl" autowire = "byName"/>
<bean id = "userController" class = "com.itheima.annotation.UserController" autowire = "byName"/>
<!-- 高效的注释配置方式 对包路径下的所有Bean进行扫描-->
<!-- 使用context命名空间,通知Spring扫描指定包下所有Bean类,进行注释解析 -->
<!-- <context:component-scan base-package="com.itheima.annotation"/> -->
</beans>
设置了autowried="byName"后,spring会自动血爪userServiceBean中的属性,并将其属性名称与配置文件中定义的Bean 匹配。