spring中的单例bean线程安全吗

image.png

BeanFactory和FactoryBean区别

image.png
—by 马士兵连鹏举

一、BeanFactory
BeanFactory是一个接口,它是Spring中工厂的顶层规范,是SpringIoc容器的核心接口,它定义了getBean()、containsBean()等管理Bean的通用方法。Spring的容器都是它的具体实现如:

DefaultListableBeanFactory

XmlBeanFactory

ApplicationContext

这些实现类又从不同的维度分别有不同的扩展。

1.1、源码
public interface BeanFactory {

//对FactoryBean的转义定义,因为如果使用bean的名字检索FactoryBean得到的对象是工厂生成的对象,
//如果需要得到工厂本身,需要转义
String FACTORY_BEAN_PREFIX = “&”;

//根据bean的名字,获取在IOC容器中得到bean实例
Object getBean(String name) throws BeansException;

//根据bean的名字和Class类型来得到bean实例,增加了类型安全验证机制。
T getBean(String name, @Nullable Class requiredType) throws BeansException;

Object getBean(String name, Object… args) throws BeansException;

T getBean(Class requiredType) throws BeansException;

T getBean(Class requiredType, Object… args) throws BeansException;

//提供对bean的检索,看看是否在IOC容器有这个名字的bean
boolean containsBean(String name);

//根据bean名字得到bean实例,并同时判断这个bean是不是单例
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;

boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException;

//得到bean实例的Class类型
@Nullable
Class<?> getType(String name) throws NoSuchBeanDefinitionException;

//得到bean的别名,如果根据别名检索,那么其原名也会被检索出来
String[] getAliases(String name);
}

1.2、使用场景
从Ioc容器中获取Bean(byName or byType)

检索Ioc容器中是否包含指定的Bean

判断Bean是否为单例

二、FactoryBean
首先它是一个Bean,但又不仅仅是一个Bean。它是一个能生产或修饰对象生成的工厂Bean,类似于设计模式中的工厂模式和装饰器模式。它能在需要的时候生产一个对象,且不仅仅限于它自身,它能返回任何Bean的实例。

2.1、源码
public interface FactoryBean {

//从工厂中获取bean
@Nullable
T getObject() throws Exception;

//获取Bean工厂创建的对象的类型
@Nullable
Class<?> getObjectType();

//Bean工厂创建的对象是否是单例模式
default boolean isSingleton() {
return true;
}
从它定义的接口可以看出,FactoryBean表现的是一个工厂的职责。 即一个Bean A如果实现了FactoryBean接口,那么A就变成了一个工厂,根据A的名称获取到的实际上是工厂调用getObject()返回的对象,而不是A本身,如果要获取工厂A自身的实例,那么需要在名称前面加上’&’符号。

getObject(‘name’)返回工厂中的实例

getObject(’&name’)返回工厂本身的实例

通常情况下,bean 无须自己实现工厂模式,Spring 容器担任了工厂的 角色;但少数情况下,容器中的 bean 本身就是工厂,作用是产生其他 bean 实例。由工厂 bean 产生的其他 bean 实例,不再由 Spring 容器产生,因此与普通 bean 的配置不同,不再需要提供 class 元素。

2.2、示例
先定义一个Bean实现FactoryBean接口

@Component
public class MyBean implements FactoryBean {
private String message;
public MyBean() {
this.message = “通过构造方法初始化实例”;
}
@Override
public Object getObject() throws Exception {
// 这里并不一定要返回MyBean自身的实例,可以是其他任何对象的实例。
//如return new Student()…
return new MyBean(“通过FactoryBean.getObject()创建实例”);
}
@Override
public Class<?> getObjectType() {
return MyBean.class;
}
public String getMessage() {
return message;
}
}
MyBean实现了FactoryBean接口的两个方法,getObject()是可以返回任何对象的实例的,这里测试就返回MyBean自身实例,且返回前给message字段赋值。同时在构造方法中也为message赋值。然后测试代码中先通过名称获取Bean实例,打印message的内容,再通过&+名称获取实例并打印message内容。

@RunWith(SpringRunner.class)
@SpringBootTest(classes = TestApplication.class)
public class FactoryBeanTest {
@Autowired
private ApplicationContext context;
@Test
public void test() {
MyBean myBean1 = (MyBean) context.getBean(“myBean”);
System.out.println(“myBean1 = “ + myBean1.getMessage());
MyBean myBean2 = (MyBean) context.getBean(“&myBean”);
System.out.println(“myBean2 = “ + myBean2.getMessage());
System.out.println(“myBean1.equals(myBean2) = “ + myBean1.equals(myBean2));
}
}
myBean1 = 通过FactoryBean.getObject()初始化实例

myBean2 = 通过构造方法初始化实例
myBean1.equals(myBean2) = false

2.3、使用场景
说了这么多,为什么要有FactoryBean这个东西呢,有什么具体的作用吗?
FactoryBean在Spring中最为典型的一个应用就是用来创建AOP的代理对象。

我们知道AOP实际上是Spring在运行时创建了一个代理对象,也就是说这个对象,是我们在运行时创建的,而不是一开始就定义好的,这很符合工厂方法模式。更形象地说,AOP代理对象通过Java的反射机制,在运行时创建了一个代理对象,在代理对象的目标方法中根据业务要求织入了相应的方法。这个对象在Spring中就是——ProxyFactoryBean。

所以,FactoryBean为我们实例化Bean提供了一个更为灵活的方式,我们可以通过FactoryBean创建出更为复杂的Bean实例。

三、区别
他们两个都是个工厂,但FactoryBean本质上还是一个Bean,也归BeanFactory管理

BeanFactory是Spring容器的顶层接口,FactoryBean更类似于用户自定义的工厂接口。

总结
BeanFactory与FactoryBean的区别确实容易混淆,死记硬背是不行的,最好还是从源码层面,置于spring的环境中去理解。
————————————————
版权声明:本文为CSDN博主「你丫才CRUD」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_51439578/article/details/111166579

spring生命周期

image.png

image.png

image.png

spring哪个环节进行aop

image.png

spring如何解决循环依赖

image.png
image.png
image.png
https://www.bilibili.com/video/BV1nB4y1P7Gi?p=10

Spring如何解决循环依赖
1,Spring中单例Bean的三级缓存
image.png

  • 第一级缓存〈也叫单例池)singletonObjects:存放已经经历了完整生命周期的Bean对象
  • 第二级缓存: earlySingletonObjects,存放早期暴露出来的Bean对象,Bean的生命周期未结束(属性还未填充完整)
  • 第三级缓存: Map> singletonFactories,存放可以生成Bean的工厂

2,Spring中Bean的生命周期
image.png
image.png
3,Bean初始化主要方法
image.png

  • getSingleton:希望从容器里面获得单例的bean,没有的话
  • doCreateBean: 没有就创建bean
  • populateBean: 创建完了以后,要填充属性
  • addSingleton: 填充完了以后,再添加到容器进行使用

4,具体说明

  • A创建过程中需要B,于是A将自己放到三级缓存里面,去实例化B
  • B实例化的时候发现需要A,于是B先查一级缓存,没有,再查二级缓存,还是没有,再查三级缓存,找到了A然后把三级缓存里面的这个A放到二级缓存里面,并删除三级缓存里面的A
  • B顺利初始化完毕,将自己放到一级缓存里面(此时B里面的A依然是创建中状态)然后回来接着创建A,此时B已经创建结束,直接从一级缓存里面拿到B,然后完成创建,并将A放到一级缓存中。

5,图解
image.png


三、为什么使用三级缓存
1,使用一级缓存
实例化A -> 将半成品的A放入singletonObjects中->填充A的属性时发现取不到B->实例化B->从singletonObjects中取出A填充B的属性->将成品B放入singletonObjects->将B填充到A的属性中->将成品A放入singletonObjects。
问题:这种基本流程是通的,但是如果在整个流程进行中,有另一个线程要来取A,那么有可能拿到的只是一个属性都为null的半成品A,这样就会有问题。
2,使用二级缓存
a)使用singletonObjects和earlySingletonObjects
成品放在singletonObjects中,半成品放在earlySingletonObjects中
流程可以这样走:实例化A ->将半成品的A放入earlySingletonObjects中 ->填充A的属性时发现取不到B->实例化B->将半成品的A放入earlySingletonObjects中->从earlySingletonObjects中取出A填充B的属性->将成品B放入singletonObjects,并从earlySingletonObjects中删除B->将B填充到A的属性中->将成品A放入singletonObjects并删除earlySingletonObjects。
问题:这样的流程是线程安全的,不过如果A上加个切面(AOP),这种做法就没法满足需求了,因为earlySingletonObjects中存放的都是原始对象,而我们需要注入的其实是A的代理对象
b)使用singletonObjects和singletonFactories
成品放在singletonObjects中,半成品通过singletonFactories来获取
流程是这样的:实例化A ->创建A的对象工厂并放入singletonFactories中 ->填充A的属性时发现取不到B->实例化B->创建B的对象工厂并放入singletonFactories中->从singletonFactories中获取A的对象工厂并获取A填充到B中->将成品B放入singletonObjects,并从singletonFactories中删除B的对象工厂->将B填充到A的属性中->将成品A放入singletonObjects并删除A的对象工厂。

缓存的放置时间和清除时间
image.png

image.png
image.png
B站鲁班大师
https://www.bilibili.com/video/BV1ET4y1N7Sp?spm_id_from=333.337.search-card.all.click

spring设计模式

image.png

spring aop原理

image.png

Spring的事务如何回滚,事务大致原理

image.png

Spring事务传播

谈谈spring ioc

image.png
image.png
image.png

如何在启动spring时,执行我的一个方法?

1.方法一
我在spring的配置文件中添加上这条,这个配置只能在启动项目是执行一遍。
还有一点 要注意 这个方法不能是controller层的方法

  1. <-- class是类的全名加包名 这是指定运行的方法在那个类里面 -->
  2. <-- scope 值得范围 这里给的参数是 singleton -->
  3. <-- inti-method 是指要执行的方法 -->
  4. <bean id="startRun" class="com.shr.bojs.StartRun" scope="singleton" init-method="test"></bean>
  5. package com.shr.jobs;
  6. public class StartRun {
  7. public void test(){
  8. System.out.println("开始执行 startRun 方法!!!");
  9. }
  10. }
  11. log4j:WARN No appenders could be found for logger (org.springframework.web.context.ContextLoader).
  12. log4j:WARN Please initialize the log4j system properly.
  13. log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
  14. 开始执行 startRun 方法!!!
  15. SLF4J: Class path contains multiple SLF4J bindings.
  16. SLF4J: Found binding in [jar:file:/D:/tomcat7.manage/webapps/newBLManager/WEB-INF/lib/slf4j-log4j12-1.5.11.jar!/org/slf4j/impl/StaticLoggerBinder.class]
  17. SLF4J: Found binding in [jar:file:/D:/tomcat7.manage/webapps/newBLManager/WEB-INF/lib/slf4j-nop-1.5.2.jar!/org/slf4j/impl/StaticLoggerBinder.class]
  18. SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.

2.方法二

使用@PostContruct ,在方法上添加@PostConstruct注解

注意 一定要放在能被扫面到的地方,如果你写在一个无法被扫描到的位置是不能执行的。(service层肯定能被扫描到)

  1. @Service
  2. public class TestRun{
  3. @PostConstruct
  4. public void text(){
  5. System.out.println("项目开始运行我也就执行!!");
  6. }
  7. }

image.png

3.方法三
实现initiailzingBean接口。
afterPropertiesSet这个方法就会在项目启动时执行。

image.png