Spring Bean 详解

Ioc实例化Bean的三种方式

1 创建Bean

1 使用无参构造函数

这也是我们常用的一种。在默认情况下,它会通过反射调⽤⽆参构造函数来创建对象。如果类中没有⽆参构造函数,将创建 失败。

  • class: 为需要注册Bean类文件的位置

applicationContext.xml配置文件

测试类:

/**
* @author : look-word
* 2022-08-25 11:36
**/
public class IocTest {
@Test
public void testIoc() {
ApplicationContext context = // 读取配置文件
new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
// 容器中根据名称获取Bean
ConnectionUtils connectionUtils
= (ConnectionUtils) context.getBean("connectionUtils");
System.out.println(connectionUtils);
}
}

结果:

# 输出了对象的地址
com.lagou.edu.utils.ConnectionUtils@3ecd23d9

2 使用静态方法创建

  • 简单来说,就是调用某个类的静态方法创建对象

在实际开发中,我们使⽤的对象有些时候并不是直接通过构造函数就可以创建出来的,它可能在创 建的过程 中会做很多额外的操作。此时会提供⼀个创建对象的⽅法,恰好这个⽅法是static修饰的 ⽅法,即是此种情况:

例如,我们在做Jdbc操作时,会⽤到java.sql.Connection接⼝的实现类,如果是mysql数据库,那 么⽤的就 是JDBC4Connection,但是我们不会去写 JDBC4Connection connection = new JDBC4Connection() ,因为我们要注册驱动,还要提供URL和凭证信息, ⽤ DriverManager.getConnection ⽅法来获取连接。那么在实际开发中,尤其早期的项⽬没有使⽤Spring框架来管理对象的创建,但是在设计时使⽤了 ⼯⼚模式 解耦,那么当接⼊spring之后,⼯⼚类创建对象就具有和上述例⼦相同特征,即可采⽤ 此种⽅式配置。

CreateBeanFactory

/**
* @author : look-word
* 2022-08-25 15:50
**/
public class CreateBeanFactory {
/**
* 使⽤静态⽅法创建对象的配置⽅式
*/
public static ConnectionUtils getInstanceStatic(){
return new ConnectionUtils();
}
}

applicationContext.xml配置文件


3 使⽤实例化调用成员⽅法创建

  • 简单来说,就是创建一个类,然后再通过这个类的某个方法创建我们需要的对象。(不推荐,需要创建额外对象)

CreateBeanFactory:

    /**
* 使⽤成员⽅法创建对象的配置⽅式
*/
public ConnectionUtils getInstance(){
return new ConnectionUtils();
}

applicationContext.xml配置文件


2 lazy—init 延迟加载

配置方式

xml配置延迟加载:

    <!--
lazy-init:延迟加载,true代表延迟,false代表立即加载,默认是false
-->
<bean id="lazyResult" class="com.lagou.edu.pojo.Result" lazy-init="true"/>

注解配值延迟加载:

可以配置到许多地方,如类似,方法上等等

@Lazy
@Component
public class XXXX {
...
}

加载原理

当使用上述三种配置后,Spring在扫描加载Bean时会读取@Lazy和@Component注解相应值,并设置Bean定义的lazyInit属性。读取注解配置时最终会调用ClassPathBeanDefinitionScanner及其子类实现的doScan方法,在这个方法中完成注解的读取配置。

public class ClassPathBeanDefinitionScanner
extends ClassPathScanningCandidateComponentProvider {
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
// 不管是读取注解或者XML配置方式bean,最终读取加载Bean时都会进入到该方法
// 对相应的包进行处理
// beanDefinitions是保存返回bean定义的集合
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
// 遍历多个包下的类
for (String basePackage : basePackages) {
// 获取满足条件的bean定义集合
Set<BeanDefinition> candidates =
findCandidateComponents(basePackage);
// 对每个bean定义进行处理
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver
.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator
.generateBeanName(candidate, this.registry);
// 这个方法会处理@ComponentScan中的lazyInit值,因为在使用
// @ComponentScan注解时会首先把该值赋值到beanDefinitionDefaults
// 默认bean定义值的对象中,在postProcessBeanDefinition方法中
// 会首先应用一次这些默认值,其中就包括lazyInit、autowireMode等
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition(
(AbstractBeanDefinition) candidate, beanName);
}
// 读取@Lazy、@Primary和@DependsOn等注解值
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils
.processCommonDefinitionAnnotations(
(AnnotatedBeanDefinition) candidate);
}
// 如果候选者满足要求则将其注册到Bean定义中心
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder =
new BeanDefinitionHolder(candidate, beanName);
definitionHolder = AnnotationConfigUtils
.applyScopedProxyMode(scopeMetadata,
definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
// 注册bean定义
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
protected void postProcessBeanDefinition(
AbstractBeanDefinition beanDefinition, String beanName) {
// 此处会应用默认值,如lazyInit、autowireMode、initMethod等
beanDefinition.applyDefaults(this.beanDefinitionDefaults);
if (this.autowireCandidatePatterns != null) {
beanDefinition.setAutowireCandidate(PatternMatchUtils
.simpleMatch(this.autowireCandidatePatterns, beanName));
}
}
}

经过ClassPathBeanDefinitionScanner或子类实现的扫描读取后,延迟加载的配置便被配置到了Bean定义中,等初始化时再使用该属性,这里需要注意的是@ComponentScan延迟加载属性是可以被@Lazy覆盖的,因为@Lazy是在@ComponentScan后面处理的。

使用细节

Spring框架延迟加载属性在调用getBean之后将会失效,因为getBean方法是初始化bean的入口,这不难理解,那么平时我们使用@Autowired等自动注入注解时能和@Lazy注解一起使用吗?接下来我们从两个实例来说明一下,这两个实例都是使用平时的使用用法,在Component上添加@Lazy注解,且让其实现InitializingBean接口,当Bean被加载时我们便能得知,看其是否会生效,示例如下:

@Lazy失效实例

声明一个Controller控制器:

@Controller
public class TestController implements InitializingBean{
@Autowired
private TestService testService;
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("testController Initializing");
}
}

再声明一个Service服务类:

@Lazy
@Service
public class TestService implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("testService Initializing");
}
}

启动程序后控制台输出:

testService Initializing
testController Initializing

启动完Spring程序后输出了TestService里面打印的字符串。这就奇怪了,明明使用了@Lazy注解,但是却并没有其作用,在Spring启动项目时还是加载了这个类?简单来说,就是在DI注入的时候,获取容器中获取对应的Bean,Autowired按照默认类型获取Resource按照默认名称获取,所以才会导致延迟加载失效问题。

@Lazy有效实例

修改先前的Controller

启动后会发现,延迟加载失效问题解决了。

@Lazy
@Controller
public class TestController implements InitializingBean{
@Autowired
private TestService testService;
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("testController Initializing");
}
}

3 BeanFactory和FactoryBean

  • BeanFactory是接口,提供了OC容器最基本的形式,给具体的IOC容器的实现提供了规范。
  • FactoryBean也是接口,为IOC容器中Bean的实现提供了更加灵活的方式,FactoryBean在IOC容器的基础上给Bean的实现加上了一个简单工厂模式和装饰模式(如果想了解装饰模式参考:修饰者模式(装饰者模式,Decoration) 我们可以在getObject()方法中灵活配置。其实在Spring源码中有很多FactoryBean的实现类.

区别:

BeanFactory是个Factory,也就是IOC容器或对象工厂,FactoryBean是个Bean。在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)来进行管理的。但对FactoryBean而言,这个Bean不是简单的Bean,而是一个能生产或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂模式和修饰器模式类似

4 后置处理器

Spring提供了两种后处理bean的扩展接⼝:

  • BeanFactoryPostProcessor

    • 在BeanFactory初始化之后可以使⽤BeanFactoryPostProcessor进⾏后置处理做⼀些事情
  • BeanPostProcessor
    • 在Bean对象实例化(并不是Bean的整个⽣命周期完成)之后可以使⽤BeanPostProcessor进⾏后置处 理做⼀些事情

springBean 声明周期

springBean 声明周期

Spring Bean 详解的更多相关文章

  1. Spring(3)——装配 Spring Bean 详解

    装配 Bean 的概述 前面已经介绍了 Spring IoC 的理念和设计,这一篇文章将介绍的是如何将自己开发的 Bean 装配到 Spring IoC 容器中. 大部分场景下,我们都会使用 Appl ...

  2. Spring Bean详解

    Spring Bean 在Spring的应用中,Spring IoC容器可以创建.装配和配置应用组件对象,这里的组件对象称为Bean. Bean的配置 Spring可以看作一个大型工厂,用于生产和管理 ...

  3. Spring二 Bean详解

    Bean详解 Spring框架的本质其实是:通过XML配置来驱动Java代码,这样就可以把原本由java代码管理的耦合关系,提取到XML配置文件中管理.这样就实现了系统中各组件的解耦,有利于后期的升级 ...

  4. (转)java之Spring(IOC)注解装配Bean详解

    java之Spring(IOC)注解装配Bean详解   在这里我们要详细说明一下利用Annotation-注解来装配Bean. 因为如果你学会了注解,你就再也不愿意去手动配置xml文件了,下面就看看 ...

  5. spring在IoC容器中装配Bean详解

    1.Spring配置概述 1.1.概述 Spring容器从xml配置.java注解.spring注解中读取bean配置信息,形成bean定义注册表: 根据bean定义注册表实例化bean: 将bean ...

  6. spring配置文件详解--真的蛮详细

    spring配置文件详解--真的蛮详细   转自: http://book.51cto.com/art/201004/193743.htm 此处详细的为我们讲解了spring2.5的实现原理,感觉非常 ...

  7. 【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理

    Spring AOP详解 . JDK动态代理.CGLib动态代理  原文地址:https://www.cnblogs.com/kukudelaomao/p/5897893.html AOP是Aspec ...

  8. J2EE进阶(四)Spring配置文件详解

    J2EE进阶(四)Spring配置文件详解 前言 Spring配置文件是用于指导Spring工厂进行Bean生产.依赖关系注入(装配)及Bean实例分发的"图纸".Java EE程 ...

  9. spring事务详解(二)简单样例

    系列目录 spring事务详解(一)初探事务 spring事务详解(二)简单样例 spring事务详解(三)源码详解 spring事务详解(四)测试验证 spring事务详解(五)总结提高 一.引子 ...

随机推荐

  1. 解决Mysql搭建成功后执行sql语句报错以及区分大小写问题

    刚搭建完mysql 8.0以后会: 一.表区分大小写, 二.执行正确的sql语句成功且会报:[Err] 1055 - Expression #1 of ORDER BY clause is not i ...

  2. Java绘图基础

    <零基础学Java> Java绘图基础 绘图是高级程序设计中非常重要的技术,例如,应用程序需要绘制闪屏图像.背景图像.组件外观Web程序可以绘制统计图.数据库存储的图像资源等. Graph ...

  3. javascript写淡入淡出效果的轮播图

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  4. 【Redis 系列】redis 学习十六,redis 字典(map) 及其核心编码结构

    redis 是使用 C 语言编写的,但是 C 语言是没有字典这个数据结构的,因此 C 语言自己使用结构体来自定义一个字典结构 typedef struct redisDb src\server.h 中 ...

  5. 字节跳动数据平台技术揭秘:基于 ClickHouse 的复杂查询实现与优化

    更多技术交流.求职机会.试用福利,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 ClickHouse 作为目前业内主流的列式存储数据库(DBMS)之一,拥有着同类型 DBMS 难以企及 ...

  6. 动态树 — Link_Cut_Tree

    [模板]动态树(Link Cut Tree) Link-cut-tree是一种维护动态森林的数据结构,在需要动态加边/删边的时候就需要LCT来维护. Link-cut-tree的核心是轻重链划分,每条 ...

  7. LMC7660即-5V产生电路

    LMC7660为小功率极性反转电源转换器,通过LMC7660电路产生-5V电压,其芯片管脚定义如下表所示. LMC7660负电压产生电路如下图所示. 其中6脚当供电电压大于等于5V时该脚必须悬空,当供 ...

  8. 利用噪声构建美妙的 CSS 图形

    在平时,我非常喜欢利用 CSS 去构建一些有意思的图形. 我们首先来看一个简单的例子.首先,假设我们实现一个 10x10 的格子: 此时,我们可以利用一些随机效果,优化这个图案.譬如,我们给它随机添加 ...

  9. VS无线振弦采集仪的常见问题

    1 无法开机( 1)检查电源连接是否正确,电压范围应为 DC10~24V,输出能力不低于 2A, 正负极连接正确.若电池极性接反,即便未进行过开机操作也会导致设备永久性损坏.( 2)若使用电池供电,则 ...

  10. 面试官:你确定 Redis 是单线程的进程吗?

    作者:小林coding 计算机八股文网站:https://xiaolincoding.com 大家好,我是小林. 这次主要分享 Redis 线程模型篇的面试题. Redis 是单线程吗? Redis ...