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. 定制.NET 6.0的依赖注入

    大家好,我是张飞洪,感谢您的阅读,我会不定期和你分享学习心得,希望我的文章能成为你成长路上的垫脚石,让我们一起精进. 在本章中,我们将学习ASP.NET Core的依赖项注入(DI)以及如何自定义它. ...

  2. LOJ数列分块 9 题解

    \(1.\) 题意 给定一个长度 \(n\) 序列,每次查询区间 \(l, r\) 的众数. \(2.\) 思路 如果边界是 \([l,r]\),\(l\) 在第 \(a\) 块,\(r\) 在第 \ ...

  3. element-ui table组件使用v-if时的问题

    element-ui项目中经常遇到需要使用v-if指令来根据情况动态显示隐藏某些列情况,这时就会出现滚动条样式异常.列错乱.列宽错乱等问题 解决办法:在el-table上添加:key="Ma ...

  4. 02 RESTFul接口和HTTP的幂等性分析

    RESTFul接口和HTTP的幂等性分析 REST全称是Representational State Transfer,中文为表述性状态转移,REST指的是一组架构约束条件和原则 RESTful表述的 ...

  5. 基于Svelte3.x桌面端UI组件库Svelte UI

    Svelte-UI,一套基于svelte.js开发的桌面pc端ui组件库 最近一直忙于写svelte-ui,一套svelte3开发的桌面端ui组件库.在设计及功能上借鉴了element-ui组件库.所 ...

  6. 【问题解决】Alpine镜像中执行jstack、arthas等命令提示Unable to get pid of LinuxThreads manager thread

    问题现象 最近在处理项目上问题发现之前同事构建的AlpineLinux的镜像不能执行jstack等JDK命令,报错如下. Unable to get pid of LinuxThreads manag ...

  7. 腾讯云数据库公有云市场稳居TOP 2!

    7月4日,国际权威机构IDC发布的<2021年下半年中国关系型数据库软件市场跟踪报告>显示,腾讯云数据库在关系型数据库软件市场(公有云模式)中,位列第二. IDC报告显示,2021下半年中 ...

  8. chrome请求cgi遇到net::ERR_INCOMPLETE_CHUNKED_ENCODING 200 (OK)

    测试一个web demo的时候,通过chrome请求板子上的web server的cgi时总是提示:net::ERR_INCOMPLETE_CHUNKED_ENCODING 200 (OK) 搜遍整个 ...

  9. linux服务配置IP或者说可以远程连接

    切换目录 cd /etc/sysconfig/network-scripts ls查看当前目录下的东西 找到ipcfg- 开头的,而且不是iocfg-lo,而上图就是那个ifcfg-ens33. 则进 ...

  10. harbor之HTTPS安装

    1.下载解压 # tar -xvf harbor-offline-installer-v1.7.6.tgz # cd /harbror 2.下载python2.7 # apt install pyth ...