2.1 Bean的配置
最常用XML文件来注册并管理Bean之间的依赖关系。
XML配置文件的根元素是< beans >,其中包含很多个< bean >子元素,每一个子元素定义了一个Bean,并描述了该Bean如何被装配到Spring容器中。
在Spring的配置文件中,通常一个普通的Bean只需要定义id(或name)和class两个属性即可。
如果在Bean中未指定id和name,那么Spring会将class值当做id来使用
2.2 Bean的作用域
2.2.1 作用域的种类
其中singleton和prototype是常用的两种。
2.2.2 singleton作用域
singleton是Spring容器默认的作用域,当Bean的作用域为singleton时,Spring容器就只会存在一个共享的Bean实例,并且所有对Bean的请求,只要id与该Bean的id属性相匹配,就会返回同一个Bean的实例。
Bean的作用域是通过< bean>元素的scope属性来选择的。
<bean id="scope" class="com.ssm.scope.Scope" scope="singleton"/>
示例 2-1
- 创建web项目 在示例1的基础上增加spring-aop-5.2.2.RELEASE.jar包
- 创建com.ssm.scope包,创建Scope类,不需要写方法。
package com.ssm.scope;
public class Scope {
}
- 在包中创建Spring配置文件,并在配置文件中创建一个id为Scope的Bean,通过class属性指定对应的实现类。
<?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 id="scope" class="com.ssm.scope.Scope"/>
</beans>
- 创建测试类ScopeTest来测试作用域
package com.ssm.scope;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ScopeTest {
public static void main(String[] args){
// 1.初始化Spring容器,加载配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
// 2.输出获得的实例
System.out.println(applicationContext.getBean("scope"));
System.out.println(applicationContext.getBean("scope"));
}
}
- 执行程序后 输出结果如下,两次结果相同,说明Spring容器只创建了一个Scope实例。
不设置scope=“singleton”输出结果也是一个实例,因为Spring容器默认作用域是singleton。
2.2.3 prototype作用域
对需要保持会话的Bean使用prototype作用域。
在使用prototype作用域时,Spring容器会为每个请求都创建一个新的实例。
<bean id="scope" class="com.ssm.scope.Scope" scope="prototype"/>
将示例2-1中的配置文件更改为上面的代码,再次运行测试类。
两次输出结果不同,说明创建了两个不同实例。
2.3 Bean的装配方式
Bean的装配可以理解为依赖关系注入。
Bean的装配方式即Bean的依赖注入的方式。
Spring容器支持多种形式的Bean装配方式,如基于XML的装配、基于Annotation(注解)的装配和自动装配等。
2.3.1 基于XML的装配
Spring提供了两种基于XML的装配方式:设值注入和构造注入
在Spring实例化Bean的过程中,Spring首先会调用Bean的默认构造方法来实例化Bean对象,然后通过反射的方式调用setter()方法来注入属性值。
因此 设值注入要求Bean满足两点要求
- Bean类必须提供一个默认的无参构造方法。
- Bean类必须为需要注入的属性提供对应的setter()方法。
使用设值注入时,在Spring配置文件中需要使用< bean>元素的子元素< property>来为每个属性注入值。而使用构造注入时,在配置文件中需要使用< construcot-arg>来定义构造方法的参数,可以使用其value属性来设置参数值。
示例2-2
- 创建com.ssm.assemble包,创建User类,定义username、password和list集合3个属性以及对应的setter()方法。
package com.ssm.assemble;
import java.util.List;
public class User {
private String username;
private String password;
private List<String> list;
/**
* 1.使用构造注入
* 1.1 提供带有参数的构造方法
*/
public User(String username,String password,List<String> list){
super();
this.username=username;
this.password=password;
this.list=list;
}
@Override
public String toString(){
return "User [username="+username+",password="+password+",list="+list+"]";
}
/**
* 2.使用设值注入
* 2.1 提供默认空参构造方法
* 2.2 为所有属性提供setter()方法
*/
public User(){
super();
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public void setList(List<String> list) {
this.list = list;
}
}
由于要使用构造注入 所以编写有参和无参两种构造方法。
- 在Spring的配置文件中,增加通过构造注入和设值注入的方法装配User实例的两个Bean。
<bean id="user1" class="com.ssm.assemble.User">
<constructor-arg index="0" value="zhangsan"/>
<constructor-arg index="1" value="111111"/>
<constructor-arg index="2" >
<list>
<value>"constructorValue1"</value>
<value>"constructorValue2"</value>
</list>
</constructor-arg>
</bean>
<bean id="user2" class="com.ssm.assemble.User">
<property name="username" value="lisi"/>
<property name="password" value="222222"/>
<property name="list">
<list>
<value>"listValue1"</value>
<value>"listValue2"</value>
</list>
</property>
</bean>
在上述代码中,< constructor-arg>元素用于定义构造方法的参数,其属性index表示索引(从0开始),value属性用于设置注入的值,其子元素< list>为User类对应的list集合属性注入值。
然后又使用设置注入的方法装配User类的实例,其中< property>元素用于调用Bean实例中的setter()方法完成属性赋值,从而完成依赖注入,而子元素< list>同样为User类中对应的list集合属性注入值。
- 在包中创建测试类XmlAssembleTest,在类中分别获取并输出配合文件中的user1和user2实例。
package com.ssm.assemble;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class XmlAssembleTest {
public static void main(String[] args){
//1.初始化Spring容器,加载配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//2.输出获得的实例
System.out.println(applicationContext.getBean("user1"));
System.out.println(applicationContext.getBean("user2"));
}
}
运行后结果如下,已经成功地使用基于XML装配的构造注入和设值注入两种方式装配了User实例
2.3.2 基于Annotation的装配
虽然@Repository、@Service和@Controller的功能与@Component注解的功能相同,但为了使标注类本身用提更加清晰,建议使用@Repository、@Service和@Controller分别进行标注
示例2-3
- 创建com.ssm.annotation包,创建接口UserDao,在接口中定义save()方法。
package com.ssm.annotation;
public interface UserDao {
public void save();
}
- 创建接口实现类
package com.ssm.annotation;
import org.springframework.stereotype.Repository;
//使用@Repository注解将UserDaoImpl类标识为Bean
@Repository("userDao")
public class UserDaoImpl implements UserDao {
public void save() {
System.out.println("userDao.save()");
}
}
@Repository注解将UserDaoImpl标识为Bean,其写法相当于配置文件中的<bean id="userDao" class="com.ssm.annotation.UserDaoImol"/>
然后在save方法中打印一句话,用于验证。
- 创建接口UserService,在接口中定义save方法
package com.ssm.annotation;
public interface UserService {
public void save();
}
- 创建实现类
package com.ssm.annotation;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
//使用@Service注解将UserServiceImpl标识为Bean
@Service("userService")
public class UserServiceImpl implements UserService {
//使用@Resource注解注入
@Resource
private UserDao userDao;
public void save() {
this.userDao.save();
System.out.println("执行userService.save()");
}
}
这里可能@Resource标红,导入java的jar包即可
使用@Service将UserServiceImpl标识为Bean,相当于<bean id="userService" class="com.ssm.annotation.UserServiceImpl"/>
然后使用@Resource注解标注在属性UserDao上,相当于<property name="userDao" ref="userDao"/>
最后在该类的save方法中调用UserDao中的save方法,并输出一句话。
- 创建控制器UserController
package com.ssm.annotation;
import org.springframework.stereotype.Controller;
import javax.annotation.Resource;
//使用@Controller注解将UserController标识为Bean
@Controller("UserController")
public class UserController {
//使用@Resource注解注入
@Resource(name="userService")
private UserService userService;
public void save(){
this.userService.save();
System.out.println("运行userController.save()");
}
}
首先使用@Controller注解标注了UserController类,相当于<bean id="userController" class="com.ssm.annotation.UserController"/>
然后使用@Resource注解标注在UserService属性上,相当于<property name="userService" ref="userService"/>
最后在save()方法中调动UserService中的save()方法,并输出一句话
- 创建配置文件beans1.xml,在配置文件中编写基于Annotation装配的代码
<?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
">
<!-- 使用context命名空间在配置文件中开启相应的注解处理器-->
<context:annotation-config/>
<!-- 分别定义3个Bean实例-->
<bean id="userDao" class="com.ssm.annotation.UserDaoImpl"/>
<bean id="userService" class="com.ssm.annotation.UserServiceImpl"/>
<bean id="userController" class="com.ssm.annotation.UserController"/>
</beans>
- 包含了context的约束信息
- 通过配置开启注解处理器
- 分别定义了3个实例
- 不再需要配置子元素< property>
上述注解方式虽然较大程度简化了XML文件的Bean配置,但仍需在Spring文件中配置相应的Bean,为此Spring注解提供了另一种高效的注解配置方式
<context:componet- scan base-package="Bean所在的包路径"/>
则3个实例可进行替换
<?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
">
<!-- 使用context命名空间通知Spring扫描指定包下所有的bean类,进行注解解析-->
<context:component-scan base-package="com.ssm.annotation"/>
</beans>
- 创建测试类,编写测试方法并定义配置文件的路径,然后通过Spring容器加载配置文件并获取UserController实例,最后调用save方法。
package com.ssm.annotation;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AnnotationAssembleTest {
private static ApplicationContext applicationContext;
public static void main(String[] args){
//定义配置文件路径
String xmlPath="com/ssm/annotation/beans1.xml";
applicationContext=new ClassPathXmlApplicationContext(xmlPath);
//获取UserController实例
UserController userController= (UserController) applicationContext.getBean("UserController");
//调用UserController中的save方法
userController.save();
}
}
- 运行结果:
这里报错是因为beansl.xml中有错误,schemaLocation中再加一行
http://www.springframework.org/schema/context/spring-context.xsd
2.3.3 自动装配
Spring的< bean>元素包含一个autowire属性,我们可以通过设置autowire属性来自动装配Bean。
所谓自动装配,就是将一个Bean自动注入其他Bean的Property中。
示例2-4
- 修改UserServiceImpl.java和UserController.java,增加setter()方法
- 修改beans1.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
">
<!-- 使用bean元素的autowire属性完成自动装配-->
<bean id="userDao" class="com.ssm.annotation.UserDaoImpl"/>
<bean id="userService" class="com.ssm.annotation.UserServiceImpl" autowire="byName"/>
<bean id="userController" class="com.ssm.annotation.UserController"autowire="byName"/>
</beans>
运行后结果一致