Spring 学习笔记(三):Spring Bean
1 Bean
配置
Spring
可以看做是一个管理Bean
的工厂,开发者需要将Bean
配置在XML
或者Properties
配置文件中。实际开发中常使用XML
的格式,其中<bean>
中的属性或子元素如下:
id
:Bean
在BeanFactory
中的唯一标识,在代码中通过BeanFactory
获取Bean
的实例时候需要以此作为索引class
:Bean
的具体实体类,使用包名+类名
的形式指定scope
:指定Bean
实例的作用域<constructor-arg>
:使用构造方法注入,指定构造方法的参数,index
表示序号,ref
指定对BeanFactory
中其他Bean
的引用关系,type
指定参数类型,value
指定参数常量值<property>
:用于设置一个属性,表示使用setter
注入,name
指定属性的名字,value
指定要注入的值,ref
指定注入的某个Bean
的id
<list>
:用于封装List
或者数组类型的依赖注入<map>
:封装Map
类型的依赖注入<set>
:封装Set
类型的依赖注入<entry>
:<map>
的子元素,用于设置一个键值对
2 Bean
实例化
Spring
实例化Bean
有三种方式:
- 构造方法实例化
- 静态工厂实例化
- 实例工厂实例化
下面进行简单的演示。
2.1 构造方法实例化
Spring
可以调用Bean
对应的类的无参构造方法进行实例化,比如:
public class TestBean {
public TestBean()
{
System.out.println("构造方法实例化");
}
}
配置文件如下:
<bean id="testBean" class="TestBean"/>
则会调用无参构造方法初始化。
其实就是只写一个<bean>
就可以了,默认的话会调用无参构造方法初始化。
2.2 静态工厂实例化
静态工厂实例化需要在工厂类中配置一个静态方法来创建Bean
,并添加factory-method
元素,首先创建工厂类:
public class TestBeanFactory {
private static final TestBean testBean = new TestBean();
public static TestBean getInstance()
{
return testBean;
}
}
接着配置文件通过class
指定该工厂类,通过factory-method
指定获取实例的方法:
<bean id="testBeanFactory" class="TestBeanFactory" factory-method="getInstance"/>
这样就可以通过id
获取了:
TestBean test = (TestBean) context.getBean("testBeanFactory");
2.3 实例工厂实例化
实例工厂实例化与静态工厂实例化类似,不过是非静态方法,然后加上一个factory-bean
元素,同样首先创建工厂类:
public class TestBeanFactory {
public TestBean getInstance()
{
return new TestBean();
}
}
在配置文件需要添加两个Bean
,一个指定工厂类,一个指定使用哪一个工厂类以及使用工厂类的哪一个方法:
<bean id="factory" class="TestBeanFactory" /> <!--指定工厂类-->
<bean id="testBeanFactory" factory-bean="factory" factory-method="getInstance" /> <!--指定工厂Bean以及哪一个工厂方法-->
获取:
TestBean test = (TestBean) context.getBean("testBeanFactory");
3 Bean
作用域
3.1 分类
<bean>
中的scope
可以指定的作用域如下:
singleton
:默认作用域,在Spring
容器只有一个Bean
实例prototype
:每次获取Bean
都会返回一个新的实例request
:在一次HTTP
请求中只返回一个Bean
实例,不同HTTP
请求返回不同的Bean
实例,仅在Spring Web
应用程序上下文使用session
:在一个HTTP Session
中,容器将返回同一个Bean
实例,仅在Spring Web
应用程序上下文中使用application
:为每个ServletContext
对象创建一个实例,即同一个应用共享一个Bean
实例,仅在Spring Web
应用程序上下文使用websocket
:为每个WebSocket
对象创建一个Bean
实例,仅在Spring Web
应用程序上下文使用
下面具体说一下最常用的两个:singleton
和prototype
。
3.2 singleton
scope
设置为singleton
时,Spring IoC
仅生成和管理一个Bean
实例,使用id
/name
获取Bean
实例时,IoC
容器返回共享的Bean
实例。设置方式如下:
<bean id="testBean" class="TestBean"/>
<bean id="testBean" class="TestBean" scope="singleton"/>
因为这是默认的作用域,设置的话IDE
也智能提示是多余的:
所以通过不需要加上scope
,测试例子:
TestBean test1 = (TestBean) context.getBean("testBean");
TestBean test2 = (TestBean) context.getBean("testBean");
System.out.println(test1 == test2);
输入的结果为True
。
3.3 prototype
每次获取Bean
时都会创建一个新的实例,例子如下:
<bean id="testBean" class="TestBean" scope="prototype"/>
TestBean test1 = (TestBean) context.getBean("testBean");
TestBean test2 = (TestBean) context.getBean("testBean");
System.out.println(test1 == test2);
测试结果为False
。
4 Bean
生命周期
Spring
可以管理作用域为singleton
的生命周期,在此作用域下Spring
能精确知道Bean
何时被创建,何时初始化完成以及何时被摧毁。Bean
的整个生命周期如下:
- 实例化
Bean
- 进行依赖注入
- 如果
Bean
实现了BeanNameAware
,调用setBeanName
- 如果
Bean
实现了BeanFactoryAware
,调用setBeanFactory
- 如果
Bean
实现了ApplicationContextAware
,调用setApplicationContext
- 如果
Bean
实现了BeanPostProcessor
,调用postProcessBeforeInitialization
- 如果
Bean
实现了InitializingBean
,调用afterPropertiesSet
- 如果配置文件配置了
init-method
属性,调用该方法 - 如果实现了
BeanPostProcessor
,调用postProcessAfterInitialization
,注意接口与上面的相同但是方法不一样 - 不需要时进入销毁阶段
- 如果
Bean
实现了DisposableBean
,调用destroy
- 如果配置文件配置了
destroy-method
,调用该方法
下面用代码进行演示:
public class TestBean implements BeanNameAware, BeanFactoryAware, ApplicationContextAware, BeanPostProcessor, InitializingBean, DisposableBean {
public TestBean()
{
System.out.println("调用构造方法");
}
@Override
public void setBeanName(String s) {
System.out.println("调用BeanNameAware的setBeanName");
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("调用BeanFactoryAware的setBeanFactory");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("调用ApplicationContextAware的setApplicationContext");
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("调用BeanPostProcessor的postProcessBeforeInitialization");
return null;
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("调用InitializingBean的afterPropertiesSet");
}
public void initMethod()
{
System.out.println("调用XML配置的init-method");
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("调用BeanPostProcessor的postProcessAfterInitialization");
return null;
}
@Override
public void destroy() throws Exception {
System.out.println("调用DisposableBean的destroy");
}
public void destroyMethod()
{
System.out.println("调用XML配置的destroy-method");
}
}
配置文件如下,指定了init-method
以及destroy-method
:
<bean id="testBean" class="TestBean" init-method="initMethod" destroy-method="destroyMethod"/>
测试:
public static void main(String[] args) {
ConfigurableApplicationContext context = new FileSystemXmlApplicationContext("classpath:applicationContext.xml");
TestBean test = (TestBean) context.getBean("testBean");
((BeanDefinitionRegistry) context.getBeanFactory()).removeBeanDefinition("testBean");
}
输出如下:
如果没有最后一行的手动删除Bean
定义是不会看见最后两行的输出的,另外,这里没有调用BeanPostProcessor
接口的两个方法,如果把scope
改为prototype
,输出如下:
可以看到首先对Bean
进行一次初始化,并且再次生成一个新的实例,而且调用了BeanPostProcessor
的两个方法。但是需要注意Spring
不会管理scope
为prototype
的销毁,所以图中没有看到调用销毁的方法。
5 Bean
装配方式
Spring
支持以下两种装配方式:
- 基于
XML
装配 - 基于注解装配
- 显式
Bean
装配
Bean
的装配方式也就是Bean
的依赖注入方式,下面分别进行阐述。
5.1 基于XML
装配
基于XML
装配也就是在XML
文件中指定使用构造方法注入或者setter
注入,比如:
public class TestBean {
private final List<String> stringList;
private String s;
public TestBean(List<String> stringList) {
this.stringList = stringList;
}
public void setS(String s)
{
this.s = s;
}
@Override
public String toString() {
return stringList.toString() + "\n" + s + "\n";
}
}
该Bean
有一个带参数的构造方法以及一个setter
,接着在XML
中指定相应的值即可:
<bean id="testBean" class="TestBean">
<constructor-arg index="0">
<list>
<value>1</value>
<value>2</value>
</list>
</constructor-arg>
<property name="s" value="444" />
</bean>
测试:
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
System.out.println(context.getBean("testBean"));
5.2 基于注解装配
尽管XML
方式可以简单地装配Bean
,但是一旦Bean
过多就会造成XML
文件过于庞大,不方便以后的升级和维护,因此推荐使用基于注解的装配方式,先来看一下常用的注解:
@Autowired
:自动装配,默认按照Bean
的类型进行装配,这是Spring
的注解@Resource
:与@Autowired
类似,但是是按名称进行装配,当找不到与名称匹配的Bean
时才按照类型进行装配,这是JDK
的注解@Qualifier
:与@Autowired
配合使用,因为@Autowired
默认按Bean
类型进行装配,使用@Qualifier
可以按名称进行装配@Bean
:方法上的注解,用于产生一个Bean
,然后交由Spring
管理@Component
:表示一个组件对象,加上了该注解就能实现自动装配,默认的Bean
的id
为使用小驼峰命名法的类@Repository
/@Service
/@Controller
:实际上是@Component
的别名,只不过是专门用于持久层/业务层/控制层的,从源码可以看出三个注解的定义除了名字不一样其他都一致,并且都是@Component
的别名:
官方文档也提到相比起使用@Component
,使用@Repository
/@Service
/@Controller
在持久层/业务层/控制层更加合适,而不是统一使用@Component
:
5.3 注解使用示例
5.3.1 @Bean
@Bean
示例如下:
public class TestBean implements BeanNameAware{
@Override
public void setBeanName(String s) {
System.out.println("setBeanName");
}
}
@Configuration
public class Config {
@Bean
public TestBean getBean()
{
return new TestBean();
}
}
public class Main {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
context.getBean("getBean");
}
}
注意通过@Bean
自动产生的Bean
的id
为方法名,而不是Bean
的类名的小驼峰形式。
5.3.2 其他
@Autowired
/@Resource
/@Qualifier
/@Repository
/@Service
/@Controller
综合示例,首先创建如下包以及文件:
@Controller
public class TestController {
@Resource
private TestService service;
public void save()
{
System.out.println("controller save");
service.save();
}
}
@Service
public class TestService {
@Autowired
@Qualifier("testRepository1")
private TestInterface repository1;
@Autowired
@Qualifier("testRepository2")
private TestInterface repository2;
public void save()
{
System.out.println("service save");
repository1.save();
repository2.save();
}
}
@Repository
public class TestRepository1 implements TestInterface{
@Override
public void save() {
System.out.println("repository1 save");
}
}
@Repository
public class TestRepository2 implements TestInterface{
@Override
public void save() {
System.out.println("repository2 save");
}
}
public interface TestInterface {
void save();
}
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
((TestController)context.getBean("testController")).save();
}
}
配置文件:
<context:component-scan base-package="bean" />
在TestService
中,使用了@Qualifier
:
@Autowired
@Qualifier("testRepository1")
private TestInterface repository1;
@Autowired
@Qualifier("testRepository2")
private TestInterface repository2;
因为TestInterface
有两个实现类,@Autowired
不知道是选择TestRepository1
还是TestRepository2
,因此需要加上@Qualifier
,指定需要注入的Bean
的id
,或者使用@Resouce
:
@Resource
private TestInterface testRepository1;
@Resource
private TestInterface testRepository2;
但是要注意这样默认了成员的名字就是Bean
的id
,可以看到这里的名字是testRepository1
与testRepository2
而不是repository1
和repository2
。
Spring 学习笔记(三):Spring Bean的更多相关文章
- Spring学习笔记三:Bean管理
转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6775827.html 一:如何使用Spring获取对象 1:定义bean类:要按照注入方式来定义对应的bea ...
- spring学习笔记(一) Spring概述
博主Spring学习笔记整理大部分内容来自Spring实战(第四版)这本书. 强烈建议新手购入或者需要电子书的留言. 在学习Spring之前,我们要了解这么几个问题:什么是Spring?Spring ...
- Java架构师之路 Spring学习笔记(一) Spring介绍
前言 这是一篇原创的Spring学习笔记.主要记录我学习Spring4.0的过程.本人有四年的Java Web开发经验,最近在面试中遇到面试官总会问一些简单但我不会的Java问题,让我觉得有必要重新审 ...
- Spring学习笔记(三)之装配Bean
除了组件扫描与自动装配之外还有基于Java代码的装配与基于XML的装配. 有一些场景是我们不能用自动装配的,比如我们要给第三方库中的组件装配到我们的应用中,这时自动装配无效,因为自动装配只能扫描本应用 ...
- Spring学习笔记之 Spring IOC容器(二) 之注入参数值,自动组件扫描方式,控制Bean实例化方式,使用注解方式
本节主要内容: 1. 给MessageBean注入参数值 2. 测试Spring自动组件扫描方式 3. 如何控制ExampleBean实例化方式 4. 使用注解方式重构Jdb ...
- Spring学习笔记之 Spring IOC容器(一)之 实例化容器,创建JavaBean对象,控制Bean实例化,setter方式注入,依赖属性的注入,自动装配功能实现自动属性注入
本节主要内容: 1.实例化Spring容器示例 2.利用Spring容器创建JavaBean对象 3.如何控制Bean实例化 4.利用Spring实现bean属性sett ...
- 1.2(Spring学习笔记)Spring中的Bean
一.<Bean>的属性及子元素 在1.1中我们对<Bean>有了初步的认识,了解了一些基本用法. 现在我们进一步理解<Bean>属性及子元素. 我们先来看下< ...
- Spring 学习笔记(2) Spring Bean
一.IoC 容器 IoC 容器是 Spring 的核心,Spring 通过 IoC 容器来管理对象的实例化和初始化(这些对象就是 Spring Bean),以及对象从创建到销毁的整个生命周期.也就是管 ...
- [Spring学习笔记 1 ] Spring 简介,初步知识--Ioc容器详解 基本原理。
一.Spring Ioc容器详解(1) 20131105 1.一切都是Bean Bean可是一个字符串或者是数字,一般是一些业务组件. 粒度一般比较粗. 2.Bean的名称 xml配置文件中,id属性 ...
- Spring学习笔记之Spring概述
概述 Spring是一个java应用最广的开源框架,它是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson 在其著作Expert One-On-One J2EE Deve ...
随机推荐
- ffmpeg:为视频添加静态水印
在ffmpeg中,添加水印需要用overlay滤镜,这是一个复杂滤镜,因为它需要两个输入,默认第一个输入是主画面,第二输入为水印,先执行一个简单的看看. 下面有两个文件,一个是可爱的大雄兔,一个是可爱 ...
- [C#] (原创)一步一步教你自定义控件——06,MaskLayer(遮罩层)
一.前言 技术没有先进与落后,只有合适与不合适. 本篇的自定义控件是:遮罩层(MaskLayer). 遮罩层对软件的美观与易用性上的提高是很大的,在日常使用过程中也会经常看到各种遮罩层,虽然WinFo ...
- 在js中如何将字符串类型的日期("2020-11-30T02:21:42.000+0000")进行格式化
1.引入方法 import { formatDateNew } from '@/utils' 2.在方法中使用,注意要先将字符串进行new Date(),否则报错date.getFullYear is ...
- SpringBoot2.x整合JavaMail以qq邮箱发送邮件
本文参考spring官网email接口文档所写. spring-email官方网址:https://docs.spring.io/spring/docs/5.1.8.RELEASE/spring-fr ...
- WPF -- 一种实现本地化的方法
本文介绍一种WPF程序实现本地化的方法. 步骤 首先,假设xaml文件中存在一个Button按钮,内容为"按钮",实现本地化的步骤如下: 展开程序的Properties,双击Res ...
- jQuery实现全网热播视频
<section id="play"> <h1>全网热播视频</h1> <ul> <li><img src=&qu ...
- C# 应用 - 封装类访问 Postgresql 数据库
引入库类 连接数据库 访问数据库 1)增删改数据库 2)查数据库 数据转换 事务 1. 引入库类 引入 Npgsql.dll using Npgsql; using NpgsqlTypes; 2. 连 ...
- 【Azure 应用服务】Azure App Service 自带 FTP服务
问题描述 Azure PaaS服务是否有FTP/S服务呢? 回答问题 应用服务(Web App/App Service)在创建时候,默认创建了FTP服务并自动开启,用于应用部署.但它不是适合作为FTP ...
- LZZY高级语言程序设计之169页**5.17
import java.util.Scanner;public class MQ3 { public static void main(String[] args) { Scanner sc = ne ...
- P1962 斐波那契数列 【矩阵快速幂】
一.题目 P1962 斐波那契数列 二.分析 比较基础的递推式转换为矩阵递推,这里因为$n$会超出$int$类型,所以需要用矩阵快速幂加快递推. 三.AC代码 1 #include <bits/ ...