今天,我们就来学习一下Bean
就在昨天的入门程序中,我们使用bean来完成DI和IoC,其目的就是减少代码之间的耦合度。
昨天的案例中,我们用到了id和class属性,其实,除此之外,bean还有很多的属性,比如name,scope等等。
在Spring中,对bean的实例化总共有三种方式:
1. 构造器实例化(常用)
2.静态工厂实例化
3.实例化工厂实例化
下面,我们来详述这几种实例化的本质思想!
构造器实例化:
本质上就是通过默认的无参构造来实例化,我们昨天的例子中,采用的都是构造器实例化的方式。这里就不再罗嗦了。
静态工厂实例化:
创建UserDao
public class UserDao {
public void say() {
System.out.println("hello world");
}
}
创建工厂:
public class Factory {
public static UserDao createUserDao() {
return new UserDao();
}
}
配置applicationContext.xml
<bean id="factory" class="cn.edu.nsu.dao.Factory" factory-method="createUserDao">
</bean>
测试层:
public class test {
@Test
public void fun() {
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao=(UserDao)context.getBean("factory");
userDao.say();
}
}
最后测试发现,成功的打印出了hello world。
仔细研究它是如何实现的呢:通过在Factory类中静态获取UserDao对象实例,然后在配置文件中定义了id为factory的一个bean,通过factory-method属性选择调用该方法,最后在测试层实例化获得UserDao实例,最后调用say()方法成功打印初hello world。
实例工厂方式实例化
修改上述代码的applicationContext.xml
<!-- 配置工厂 -->
<bean id="factory" class="cn.edu.nsu.dao.Factory"/>
<!-- 使用factory-bean指向实例化工厂。使用factory-method确定调用的方法 -->
<bean id="userDao" factory-bean="factory" factory-method="createUserDao"/>
修改上述代码的Factory
public class Factory {
public UserDao createUserDao() {
return new UserDao();
}
}
修改上述代码的测试层
public class test {
@Test
public void fun() {
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao=(UserDao)context.getBean("userDao");
userDao.say();
}
}
最后测试发现,也仍然可以打印出hello world.
仔细研究发现:通过配置工厂,然后定义了一个需要实例化的bean,其id为userDao,使用factory-bean执行工厂,使用factory-method调用工厂的createUserDao方法,最后在测试层实例化UserDao,调用say()方法成功打印出hello world。
对比发现,确实使用构造实例化最为方便。
bean的作用域有其中,其中常见的有singleton(单例,默认), propotype(原型)。他们的区别在于:singleton只存在一个bean实例,spring知道bean的生命周期,适用于无会话状态,而propotype则每次请求都会创立一个bean。
Bean的装配方式有三种:1.基于XML装配,2.基于注解装配(常用),3.自动装配
下面来详细说说这几种装配方式
基于XML装配
基于xml装配有两种方式:1.设值注入 2.构造注入
设值注入:
创建UserService:
public class UserService {
private String username;
private String password;
private List<String> hobbies;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public List<String> getHobbies() {
return hobbies;
}
public void setHobbies(List<String> hobbies) {
this.hobbies = hobbies;
}
@Override
public String toString() {
return "UserService [username=" + username + ", password=" + password + ", hobbies=" + hobbies + "]";
}
}
创建applicationContext.xml
<bean id="userService" class="cn.edu.nsu.service.UserService">
<property name="username" value="张三"></property>
<property name="password" value="李四"></property>
<property name="hobbies">
<list>
<value>"唱歌"</value>
<value>"跳舞"</value>
</list>
</property>
</bean>
测试层:
public class test {
@Test
public void fun() {
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService=(UserService)context.getBean("userService");
System.out.println(userService.toString());
}
}
测试结果:
认真分析不难发现:通过定义一个bean,根据property属性,设置其name为UserService的属性,value设置为其对应的值,这就完成了传参赋值的过程。
构造注入
修改UserService:
public class UserService {
private String username;
private String password;
private List<String> hobbies;
public UserService(String username,String password,List<String> hobbies) {
super();
this.username=username;
this.password=password;
this.hobbies=hobbies;
}
@Override
public String toString() {
return "UserService [username=" + username + ", password=" + password + ", hobbies=" + hobbies + "]";
}
}
修改applicationContext.xml:
<bean id="userService" class="cn.edu.nsu.service.UserService">
<constructor-arg index="0" value="张三"/>
<constructor-arg index="1" value="李四"/>
<constructor-arg index="2">
<list>
<value>"唱歌"</value>
<value>"跳舞"</value>
</list>
</constructor-arg>
</bean>
不难发现,构造注入采用的是有参构造,采用constructor-arg属性,以参数序列号作为索引进行赋值,而设置注入则必须无参构造。采用property属性,以属性名作为索引进行赋值。如果组件的依赖关系比较稳定,适合构造注入!
Annotation(注解)的装配
常见的注解如下:
1.@Component : 通用注解
2.@Respository:用于Dao层
3.@Service: 用于Service层
4.@Controller 用于控制层
5.@Autowired 按照类型用于对bean的属性,setter方法,构造方法进行标注
6.@Resource 按照名称用于对bean的属性,setter方法,构造方法进行标注
7.@Qualifier 配合Autowired使用,将Autowired变为Resourde(功能上修改)
实例:
创建UserDao:
@Repository
public class UserDao {
public void say() {
System.out.println("hello world");
}
}
创建UserService:
@Service
public class UserService {
@Autowired()
@Qualifier("userDao")
private UserDao userDao;
public void say() {
userDao.say();
}
}
创建UserController
@Controller
public class UserController {
@Resource
private UserService userService;
public void say() {
userService.say();
}
}
创建applicationContext.xml
<context:component-scan base-package="cn.edu.nsu"></context:component-scan>
创建测试层:
public class test {
@Test
public void fun() {
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
UserController userController=(UserController)context.getBean("userController");
userController.say();
}
}
最后经测试,成功的打印出了hello world。
仔细研究可以发现:使用注解的方式,比如UserController的注解就相当于:
<bean id="userController" class="cn.edu.nsu.UserController">
<property name="userService" ref="userService"></property>
</bean>
采用下面这种方式进行包扫描,避免冗余
<context:component-scan base-package="cn.edu.nsu"></context:component-scan>
很明显就看得出来,使用注解的方式来装配Bean比基于xml要简单得多。
自动装配
修改上述的UserService:
@Service
public class UserService {
@Autowired()
@Qualifier("userDao")
private UserDao userDao;
public UserDao getUserDao() {
return userDao;
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void say() {
userDao.say();
}
}
修改上述的UserController:
@Controller
public class UserController {
@Resource
private UserService userService;
public UserService getUserService() {
return userService;
}
public void setUserService(UserService userService) {
this.userService = userService;
}
public void say() {
userService.say();
}
}
修改上述的applicationContext.xml
<bean id="userDao" class="cn.edu.nsu.dao.UserDao"></bean>
<bean id="userService" class="cn.edu.nsu.service.UserService" autowire="byName"></bean>
<bean id="userController" class="cn.edu.nsu.controller.UserController" autowire="byName"></bean>
最后测试结果依然为hello world。
细心的朋友会发现,上述的代码我仍然写了注解的,现在把所有注解去掉,然后运行会发现,仍然运行结果为hello world。说明了注解无效。因为如果是注解装配,不采用报扫描,我们是需要在配置中开启注解解释器:
<context:annotation-config/>
而且,这里的UserService和UserController都需要有无参构造和setter方法。
今日总结
1.Bean的实例化分为三种:构造器实例化 静态工厂实例化 实例工厂实例化
2.Bean的作用域有七种,其中singleton(单例,默认) propotype(原型)最为常见
3.Bean的装配方式有三种:基于XML装配(设值注入:无参+setter , 构造注入:有参) 基于Annotation装配(无参,常用) 自动装配(无参+setter)