Spring框架深度学习--第二天

今天,我们就来学习一下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)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值