Spring系列(23-28)
二十三、父子容器
什么是父子容器?
为什么需要用父子容器?
父子容器如何使用?
BeanFactory的方式
//创建父容器 parentFactoryDefaultListableBeanFactory parentFactory = new DefaultListableBeanFactory(); //创建一个子容器 childFactoryDefaultListableBeanFactory childFactory = new DefaultListableBeanFactory(); //调用setParentBeanFactory指定父容器 childFactory.setParentBeanFactory(parentFactory);
ApplicationContext的方式
//创建父容器 AnnotationConfigApplicationContext parentContext = new AnnotationConfigApplicationContext(); //启动父容器 parentContext.refresh(); //创建子容器 AnnotationConfigApplicationContext childContext = new AnnotationConfigApplicationContext(); //给子容器设置父容器 childContext.setParent(parentContext); //启动子容器 childContext.refresh();
父子容器特点
父容器和子容器是相互隔离的,他们内部可以存在名称相同的bean。
子容器可以访问父容器中的bean,而父容器不能访问子容器中的bean。
调用子容器的getBean方法获取bean的时候,会沿着当前容器开始向上面的容器进行查找,直到找到对应的bean为止。
子容器中可以通过任何注入方式注入父容器中的bean,而父容器中是无法注入子容器中的bean,原因是第2点。
父子容器使用注意点
org.springframework.beans.factory.BeanFactory org.springframework.beans.factory.ListableBeanFacto
String[] getBeanNamesForType(@Nullable Class<?> type)
有没有方式解决ListableBeanFactory接口不支持层次查找的问题?
org.springframework.beans.factory.BeanFactoryUtils
问题1:Springmvc中只使用一个容器是否可以?
问题2:那么Springmvc中为什么需要用到父子容器?
二十四、@PropertySource、@Value注解及动态刷新实现
@Value的用法
@Value数据来源
@Value动态刷新的问题
@Value的用法
@Value使用步骤
@PropertySource({"配置文件路径1","配置文件路径2"...})使用@Value注解引用配置文件的值
@Value("${配置文件中的key:默认值}")
@Value("${配置文件中的key}")@Value数据来源
org.springframework.core.env.PropertySource
可以将其理解为一个配置源,里面包含了key->value的配置信息,可以通过这个类中提供的方法获取key对应的value信息,内部有个方法:
public abstract Object getProperty(String name);
org.springframework.core.env.Environment
用来表示环境配置信息,这个接口有几个方法比较重要
// 用来解析 ${text} 的,@Value注解最后就是调用这个方法来解析的。
String resolvePlaceholders(String text);
// 返回MutablePropertySources对象,来看一下这个类
MutablePropertySources getPropertySources();返回MutablePropertySources对象,来看一下这个类
public class MutablePropertySources implements PropertySources {
private final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<>();
}将@Value注解的value参数值作为Environment.resolvePlaceholders方法参数进行解析。
Environment内部会访问MutablePropertySources来解析。
MutablePropertySources内部有多个PropertySource,此时会遍历PropertySource列表,调用PropertySource.getProperty方法来解析key对应的值。
@Test
public void test2() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
/*下面这段是关键 start*/
//模拟从db中获取配置信息
Map<String, Object> mailInfoFromDb = DbUtil.getMailInfoFromDb();
//将其丢在MapPropertySource中(MapPropertySource类是spring提供的一个类,是 PropertySource的子类)
MapPropertySource mailPropertySource = new MapPropertySource("mail", mailInfoFromDb);
//将mailPropertySource丢在Environment中的PropertySource列表的第一个中,让优先级最高
context.getEnvironment().getPropertySources().addFirst(mailPropertySource);
/*上面这段是关键 end*/
context.register(MainConfig2.class);
context.refresh();
MailConfig mailConfig = context.getBean(MailConfig.class);
System.out.println(mailConfig);
}@Value动态刷新
ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;
这个参数的值是个ScopedProxyMode类型的枚举,值有下面4中
public enum ScopedProxyMode {
DEFAULT,
NO,
INTERFACES,
TARGET_CLASS;
}具体实现
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
// 指定自定义Scope解析类
@Scope(BeanRefreshScope.SCOPE_REFRESH)
@Documented
public @interface RefreshScope {
// 关键:使用的是ScopedProxyMode.TARGET_CLASS
ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;
}import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;
import org.springframework.lang.Nullable;
import java.util.concurrent.ConcurrentHashMa
public class BeanRefreshScope implements Scope {
public static final String SCOPE_REFRESH = "refresh";
private static final BeanRefreshScope INSTANCE = new BeanRefreshScope();
//来个map用来缓存bean
private ConcurrentHashMap<String, Object> beanMap = new ConcurrentHashMap<>();
private BeanRefreshScope() {}
public static BeanRefreshScope getInstance() {
return INSTANCE;
}
/**
* 清理当前
*/
public static void clean() {
INSTANCE.beanMap.clear();
}
@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
Object bean = beanMap.get(name);
if (bean == null) {
bean = objectFactory.getObject();
beanMap.put(name, bean);
}
return bean;
}
}/**
* 邮件配置信息
*/
@Component
// 使用了自定义的作用域@RefreshScope
@RefreshScope
@Data
public class MailConfig {
@Value("${mail.username}")
private String username;
}再来个普通的bean,内部会注入MailConfig
@Component
@Data
public class MailService {
@Autowired
private MailConfig mailConfig;
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
}
}来个类,用来从db中获取邮件配置信息
public class DbUtil {
/**
* 模拟从db中获取邮件配置信息
*
* @return
*/
public static Map<String, Object> getMailInfoFromDb() {
Map<String, Object> result = new HashMap<>();
result.put("mail.username", UUID.randomUUID().toString());
return result;
}
}来个spring配置类,扫描加载上面的组件
@Configuration
@ComponentScan("com.zender.test")
public class MainConfig {
}来个工具类
public class RefreshConfigUtil {
/**
* 模拟改变数据库中都配置信息
*/
public static void updateDbConfig(AbstractApplicationContext context) {
//更新context中的mailPropertySource配置信息
refreshMailPropertySource(context);
//清空BeanRefreshScope中所有bean的缓存
BeanRefreshScope.clean();
}
public static void refreshMailPropertySource(AbstractApplicationContext context) {
Map<String, Object> mailInfoFromDb = DbUtil.getMailInfoFromDb();
//将其丢在MapPropertySource中(MapPropertySource类是spring提供的一个类,是PropertySource的子类)
MapPropertySource mailPropertySource = new MapPropertySource("mail", mailInfoFromDb);
context.getEnvironment().getPropertySources().addFirst(mailPropertySource);
}
}@Test
public void test4() throws InterruptedException {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.getBeanFactory().registerScope(BeanRefreshScope.SCOPE_REFRESH, BeanRefreshScope.getInstance());
context.register(MainConfig.class);
//刷新mail的配置到Environment
RefreshConfigUtil.refreshMailPropertySource(context);
context.refresh();
MailService mailService = context.getBean(MailService.class);
System.out.println("配置未更新的情况下,输出3次");
for (int i = 0; i < 3; i++) { //@1
System.out.println(mailService);
TimeUnit.MILLISECONDS.sleep(200);
}
System.out.println("模拟3次更新配置效果");
for (int i = 0; i < 3; i++) { //@2
RefreshConfigUtil.updateDbConfig(context); //@3
System.out.println(mailService);
TimeUnit.MILLISECONDS.sleep(200);
}
}二十五、国际化
public Locale(String language, String country) {
this(language, country, "");
}static public final Locale SIMPLIFIED_CHINESE = createConstant("zh", "CN");//zh_CN
static public final Locale UK = createConstant("en", "GB"); //en_GB
static public final Locale US = createConstant("en", "US"); //en_US
static public final Locale CANADA = createConstant("en", "CA"); //en_CAMessageSource接口
org.springframework.context.MessageSource
内部有3个常用的方法用来获取国际化信息
public interface MessageSource {
/**
* 获取国际化信息
* @param code 表示国际化资源中的属性名;
* @param args用于传递格式化串占位符所用的运行期参数;
* @param defaultMessage 当在资源找不到对应属性名时,返回defaultMessage参数所指定的
默认信息;
* @param locale 表示本地化对象
*/
@Nullable
String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale);
/**
* 与上面的方法类似,只不过在找不到资源中对应的属性名时,直接抛出NoSuchMessageException
异常
*/
String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException;
/**
* @param MessageSourceResolvable 将属性名、参数数组以及默认信息封装起来,它的功能和第一个方法相同
*/
String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;
}常见3个实现类
ResourceBundleMessageSource
ReloadableResourceBundleMessageSource
StaticMessageSource
Spring中使用国际化的3个步骤
name=姓名
personal_introduction=个人介绍:{0},{1},{0}message_en_GB.properties:英文
name=Full
namepersonal_introduction=personal_introduction:{0},{1},{0}步骤二:向容器中注册一个MessageSource类型的bean,bean名称必须为:messageSource
@Configuration
public class MainConfig1 {
@Bean
public ResourceBundleMessageSource messageSource() {
ResourceBundleMessageSource result = new ResourceBundleMessageSource();
// 可以指定国际化化配置文件的位置,格式:路径/文件名称,注意不包含【语言_国 家.properties】含这部分
result.setBasenames("com/javacode2018/lesson002/demo19/message"); //@1
return result;
}
}步骤三:调用AbstractApplicationContext中的getMessage来获取国际化信息,其内部将交给第二步中注册的messageSource名称的bean进行处理
@Test
public void test1() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(MainConfig1.class);
context.refresh();
//未指定Locale,此时系统会取默认的locale对象,本地默认的值中文【中国】,即:zh_CN
System.out.println(context.getMessage("name", null, null));
System.out.println(context.getMessage("name", null, Locale.CHINA));
//CHINA对应:zh_CN
System.out.println(context.getMessage("name", null, Locale.UK)); //UK对应en_GB
//动态参数使用
System.out.println(context.getMessage("personal_introduction", new String[]{"spring高手", "java高手"}, Locale.CHINA)); //CHINA对应:zh_CN
System.out.println(context.getMessage("personal_introduction", new String[]{"spring", "java"}, Locale.UK)); //UK对应en_GB
}监控国际化文件的变化
public void setCacheMillis(long cacheMillis)
@Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource result = new
ReloadableResourceBundleMessageSource();
//设置缓存时间1000毫秒
result.setCacheMillis(1000);
return result;
}国际化信息存在db中
public void addMessage(String code, Locale locale, String msg); public void addMessages(Map<String, String> messages, Locale locale);
public class MessageSourceFromDb extends StaticMessageSource implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
//此处我们在当前bean初始化之后,模拟从db中获取国际化信息,然后调用addMessage来配置国际化信息
this.addMessage("desc", Locale.CHINA, "我是从db来的信息");
this.addMessage("desc", Locale.UK, "MessageSource From Db");
}
}@Bean
public MessageSource messageSource(){
return new MessageSourceFromDb();
}@Test
public void test4() throws InterruptedException {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(MainConfig3.class);
context.refresh();
System.out.println(context.getMessage("desc", null, Locale.CHINA));
System.out.println(context.getMessage("desc", null, Locale.UK));
}bean名称为什么必须是messageSource
org.springframework.context.support.AbstractApplicationContext#refresh内部会调用
org.springframework.context.support.AbstractApplicationContext#initMessageSource这个方法用来初始化MessageSource,方法内部会查找当前容器中是否有messageSource名称的bean,如果有就将其作为处理国际化的对象
如果没有找到,此时会注册一个名称为messageSource的MessageSource
自定义bean中使用国际化
public interface MessageSourceAware extends Aware {
void setMessageSource(MessageSource messageSource);
}二十六、Spring事件详解
为什么需要使用事件这种模式?
spring中实现事件有几种方式?
spring中事件监听器消费事件是否支持异步模式?
spring中事件监听器消费事件是否支持自定义顺序?
事件模式中的几个概念
ApplicationContext容器中事件的支持
AnnotationConfigApplicationContext和ClassPathXmlApplicationContext都继承了AbstractApplicationContext
AbstractApplicationContext实现了ApplicationEventPublisher接口
AbstractApplicationContext内部有个ApplicationEventMulticaster类型的字段
ApplicationEventPublisher接口
@FunctionalInterface
public interface ApplicationEventPublisher {
default void publishEvent(ApplicationEvent event) {
publishEvent((Object) event);
}
void publishEvent(Object event);
}获取ApplicationEventPublisher对象
public interface ApplicationEventPublisherAware extends Aware {
void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher);
}面相接口的方式
面相@EventListener注解的方式
面相接口的方式
/**
* 用户注册事件
*/
@Data
public class UserRegisterEvent extends ApplicationEvent {
//用户名
private String userName;
public UserRegisterEvent(Object source, String userName) {
super(source);
this.userName = userName;
}
}发送邮件监听器
/**
* 用户注册成功发送邮件
*/
@Component
public class SendEmailListener implements ApplicationListener<UserRegisterEvent>{
@Override
public void onApplicationEvent(UserRegisterEvent event) {
System.out.println(String.format("给用户【%s】发送注册成功邮件!",
event.getUserName()));
}
}用户注册服务
/**
* 用户注册服务
*/
@Component
public class UserRegisterService implements ApplicationEventPublisherAware {
private ApplicationEventPublisher applicationEventPublisher;
/**
* 负责用户注册及发布事件的功能
* @param userName 用户名
*/
public void registerUser(String userName) {
//用户注册(将用户信息入库等操作)
System.out.println(String.format("用户【%s】注册成功", userName));
//发布注册成功事件
this.applicationEventPublisher.publishEvent(new UserRegisterEvent(this,userName));
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
}测试用例
@Test
public void test2() throws InterruptedException {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(MainConfig2.class);
context.refresh();
//获取用户注册服务
com.javacode2018.lesson003.demo1.test2.UserRegisterService
userRegisterService = context.getBean(com.javacode2018.lesson003.demo1.test2.UserRegisterService.class);
//模拟用户注册
userRegisterService.registerUser("路人甲Java");
}面相@EventListener注解方式
/**
* 用户注册监听器
*/
@Component
public class UserRegisterListener {
@EventListener
public void sendMail(UserRegisterEvent event) {
System.out.println(String.format("给用户【%s】发送注册成功邮件!",
event.getUserName()));
}
@EventListener
public void sendCompon(UserRegisterEvent event) {
System.out.println(String.format("给用户【%s】发送优惠券!",
event.getUserName()));
}
}监听器支持排序功能
方式1:实现org.springframework.core.Ordered接口
方式2:实现org.springframework.core.PriorityOrdered接口
方式3:类上使用@org.springframework.core.annotation.Order注解
监听器异步模式
private Executor taskExecut
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType :
resolveDefaultEventType(event));
Executor executor = getTaskExecutor();
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) { // @1
executor.execute(() -> invokeListener(listener, event));
} else {
invokeListener(listener, event);
}
}
}public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
this.applicationEventMulticaster = beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
} else {
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
}
}@ComponentScan
@Configuration
public class MainConfig5 {
@Bean
public ApplicationEventMulticaster applicationEventMulticaster() { //@1
//创建一个事件广播器
SimpleApplicationEventMulticaster result = new SimpleApplicationEventMulticaster();
//给广播器提供一个线程池,通过这个线程池来调用事件监听器
Executor executor = this.applicationEventMulticasterThreadPool().getObject();
//设置异步执行器
result.setTaskExecutor(executor);//@1
return result;
}
@Bean
public ThreadPoolExecutorFactoryBean applicationEventMulticasterThreadPool() {
ThreadPoolExecutorFactoryBean result = new ThreadPoolExecutorFactoryBean();
result.setThreadNamePrefix("applicationEventMulticasterThreadPool-");
result.setCorePoolSize(5);
return result;
}
}定义了一个名称为 applicationEventMulticaster 的事件广播器,内部设置了一个线程池用来异步调用监听器
@Test
public void test5() throws InterruptedException {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(MainConfig5.class);
context.refresh();
//获取用户注册服务
UserRegisterService userRegisterService = context.getBean(UserRegisterService.class);
//模拟用户注册
userRegisterService.registerUser("路人甲Java");
}spring中事件是使用接口的方式还是使用注解的方式?具体使用哪种方式都可以,不过在公司内部最好大家都统一使用一种方式
异步事件的模式,通常将一些非主要的业务放在监听器中执行,因为监听器中存在失败的风险,所以使用的时候需要注意。如果只是为了解耦,但是被解耦的次要业务也是必须要成功的,可以使用消息中间件的方式来解决这些问题。
二十七、循环bean详解
什么是循环依赖?
public class A{
B b;
}
public class B{
C c;
}
public class C{
A a;
}如何检测是否存在循环依赖?
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}singletonsCurrentlyInCreation 就是用来记录目前正在创建中的bean名称列表,this.singletonsCurrentlyInCreation.add(beanName) 返回 false ,说明beanName已经在当前列表中了,此时会抛循环依赖的异常 BeanCurrentlyInCreationException ,这个异常对应的源码:
public BeanCurrentlyInCreationException(String beanName) {
super(beanName,"Requested bean is currently in creation: Is there anunresolvable circular reference?");
}//检查正在创建的bean列表中是否存在beanName,如果存在,说明存在循环依赖,抛出循环依赖的异常
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
//判断scope是否是prototype
if (mbd.isPrototype()) {
Object prototypeInstance = null;
try {
//将beanName放入正在创建的列表中
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
//将beanName从正在创建的列表中移除
afterPrototypeCreation(beanName);
}
}Spring如何解决循环依赖的问题
步骤1:实例化bean,即调用构造器创建bean实例。
步骤2:填充属性,注入依赖的bean,比如通过set方式、@Autowired注解的方式注入依赖的bean。
步骤3:bean的初始化,比如调用init方法。
通过步骤1中构造器的方式注入依赖
通过步骤2注入依赖
@Component
public class ServiceA {
private ServiceB serviceB;
public ServiceA(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
@Component
public class ServiceB {
private ServiceA serviceA;
public ServiceB(ServiceA serviceA) {
this.serviceA = serviceA;
}
}@Component
public class ServiceA {
private ServiceB serviceB;
@Autowired
public void setServiceB(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
@Component
public class ServiceB {
private ServiceA serviceA;
@Autowired
public void setServiceA(ServiceA serviceA) {
this.serviceA = serviceA;
}
}如果我们采用硬编码的方式创建上面2个对象,过程如下:
//创建serviceA ServiceA serviceA = new ServiceA(); //创建serviceB ServiceB serviceB = new ServiceB(); //将serviceA注入到serviceB中 serviceB.setServiceA(serviceA); //将serviceB注入到serviceA中 serviceA.setServiceB(serviceB);
由于单例bean在spring容器中只存在一个,所以spring容器中肯定是有一个缓存来存放所有已创建好的单例bean;获取单例bean之前,可以先去缓存中找,找到了直接返回,找不到的情况下再去创建,创建完毕之后再将其丢到缓存中,可以使用一个map来存储单例bean,比如下面这个:
Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
下面来看一下spring中set方法创建上面2个bean的过程:
1.spring轮询准备创建2个bean:serviceA和serviceB 2.spring容器发现singletonObjects中没有serviceA 3.调用serviceA的构造器创建serviceA实例 4.serviceA准备注入依赖的对象,发现需要通过setServiceB注入serviceB 5.serviceA向spring容器查找serviceB 6.spring容器发现singletonObjects中没有serviceB 7.调用serviceB的构造器创建serviceB实例 8.serviceB准备注入依赖的对象,发现需要通过setServiceA注入serviceA 9.serviceB向spring容器查找serviceA 10.此时又进入步骤2了
/** 第一级缓存:单例bean的缓存 */ private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); /** 第二级缓存:早期暴露的bean的缓存 */ private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); /** 第三级缓存:单例bean工厂的缓存 */ private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
//1.查看缓存中是否已经有这个bean了
Object sharedInstance = getSingleton(beanName); //@1
if (sharedInstance != null && args == null) {
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}else {
//若缓存中不存在,准备创建这个bean
if (mbd.isSingleton()) {
//2.下面进入单例bean的创建过程
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
}
return (T) bean;
}@1:查看缓存中是否已经有这个bean了,如下:
public Object getSingleton(String beanName) {
return getSingleton(beanName, true);
}然后进入下面方法,会依次尝试从3级缓存中查找bean,注意下面的第2个参数,为ture的时候,才会从第3级中查找,否则只会查找1、2级缓存
//allowEarlyReference:是否允许从三级缓存singletonFactories中通过getObject拿到bean
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//1.先从一级缓存中找
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
//2.从二级缓存中找
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
//3.二级缓存中没找到 && allowEarlyReference为true的情况下,从三级缓存中找
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//三级缓存返回的是一个工厂,通过工厂来获取创建bean
singletonObject = singletonFactory.getObject();
//将创建好的bean丢到二级缓存中
this.earlySingletonObjects.put(beanName, singletonObject);
//从三级缓存移除
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}刚开始,3个缓存中肯定是找不到的,会返回null,接着会执行下面代码准备创建 serviceA
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
}进入 getSingleton 方法,而 getSingleton 方法代码比较多,为了方便大家理解,无关的代码我给剔除了,如下:
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
//单例bean创建之前调用,将其加入正在创建的列表中,上面有提到过,主要用来检测循环依赖用的
beforeSingletonCreation(beanName);
boolean newSingleton = false;
try {
//调用工厂创建bean
singletonObject = singletonFactory.getObject();//@1
newSingleton = true;
} finally {
//单例bean创建之前调用,主要是将其从正在创建的列表中移除
afterSingletonCreation(beanName);
}
if (newSingleton) {
//将创建好的单例bean放入缓存中
addSingleton(beanName, singletonObject);//@2
}
}
return singletonObject;
}
}上面@1和@2是关键代码,先来看一下@1,这个是一个ObjectFactory类型的,从外面传入的,如下
BeanWrapper instanceWrapper = null;
if (instanceWrapper == null) {
//通过反射调用构造器实例化serviceA
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
//变量bean:表示刚刚同构造器创建好的bean示例
final Object bean = instanceWrapper.getWrappedInstance();
//判断是否需要暴露早期的bean,条件为(是否是单例bean && 当前容器允许循环依赖 && bean名称存在于正在创建的bean名称清单中)
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
//若earlySingletonExposure为true,通过下面代码将早期的bean暴露出去
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));//@1
}protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
//第1级缓存中不存在bean
if (!this.singletonObjects.containsKey(beanName)) {
//将其丢到第3级缓存中
this.singletonFactories.put(beanName, singletonFactory);
//后面的2行代码不用关注
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//1.先从一级缓存中找
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
//2.从二级缓存中找
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
//3.二级缓存中没找到 && allowEarlyReference为true的情况下,从三级缓存中找
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//三级缓存返回的是一个工厂,通过工厂来获取创建bean
singletonObject = singletonFactory.getObject();
//将创建好的bean丢到二级缓存中
this.earlySingletonObjects.put(beanName, singletonObject);
//从三级缓存移除
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
//将bean放入第1级缓存中
this.singletonObjects.put(beanName, singletonObject);
//将其从第3级缓存中移除
this.singletonFactories.remove(beanName);
//将其从第2级缓存中移除
this.earlySingletonObjects.remove(beanName);
}
}1.从容器中获取serviceA 2.容器尝试从3个缓存中找serviceA,找不到 3.准备创建serviceA 4.调用serviceA的构造器创建serviceA,得到serviceA实例,此时serviceA还未填充属性,未进行其他任何初始化的操作 5.将早期的serviceA暴露出去:即将其丢到第3级缓存singletonFactories中 6.serviceA准备填充属性,发现需要注入serviceB,然后向容器获取serviceB 7.容器尝试从3个缓存中找serviceB,找不到 8.准备创建serviceB 9.调用serviceB的构造器创建serviceB,得到serviceB实例,此时serviceB还未填充属性,未进行其他任何初始化的操作 10.将早期的serviceB暴露出去:即将其丢到第3级缓存singletonFactories中 11.serviceB准备填充属性,发现需要注入serviceA,然后向容器获取serviceA 12.容器尝试从3个缓存中找serviceA,发现此时serviceA位于第3级缓存中,经过处理之后,serviceA会从第3级缓存中移除,然后会存到第2级缓存中,然后将其返回给serviceB,此时serviceA通过serviceB中的setServiceA方法被注入到serviceB中 13.serviceB继续执行后续的一些操作,最后完成创建工作,然后会调用addSingleton方法,将自己丢到第1级缓存中,并将自己从第2和第3级缓存中移除 14.serviceB将自己返回给serviceA 15.serviceA通过setServiceB方法将serviceB注入进去 16.serviceB继续执行后续的一些操作,最后完成创建工作,然后会调用addSingleton方法,将自己丢到第1级缓存中,并将自己从第2和第3级缓存中移除
循环依赖无法解决的情况
情况1
情况2
1.从容器中获取serviceB 2.serviceB由于是多例的,所以缓存中肯定是没有的 3.检查serviceB是在正在创建的bean名称列表中,没有 4.准备创建serviceB 5.将serviceB放入正在创建的bean名称列表中 6.实例化serviceB(由于serviceB是多例的,所以不会提前暴露,必须是单例的才会暴露) 7.准备填充serviceB属性,发现需要注入serviceA 8.从容器中查找serviceA 9.尝试从3级缓存中找serviceA,找不到 10.准备创建serviceA 11.将serviceA放入正在创建的bean名称列表中 12.实例化serviceA 13.由于serviceA是单例的,将早期serviceA暴露出去,丢到第3级缓存中 14.准备填充serviceA的属性,发现需要注入serviceB 15.从容器中获取serviceB 16.先从缓存中找serviceB,找不到 17.检查serviceB是在正在创建的bean名称列表中,发现已经存在了,抛出循环依赖的异常
探讨:为什么需要用3级缓存
问题
原因
二十八、BeanFactory扩展(BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor)
阶段1:Bean注册阶段,此阶段会完成所有bean的注册。
阶段2:BeanFactory后置处理阶段。
阶段3:注册BeanPostProcessor。
阶段4:bean创建阶段,此阶段完成所有单例bean的注册和装载操作。
阶段1:Bean注册阶段
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}实现 org.springframework.core.PriorityOrdered 接口
实现 org.springframework.core.Ordered 接口
PriorityOrdered.getOrder() asc Ordered.getOrder() asc
@Configuration @ComponentScan @Import @ImportResource @PropertySource
阶段2:BeanFactory后置处理阶段
@FunctionalInterface
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}实现 org.springframework.core.PriorityOrdered 接口
实现 org.springframework.core.Ordered 接口
PriorityOrdered.getOrder() asc Ordered.getOrder() asc
这个接口的几个重要实现类
PropertySourcesPlaceholderConfigurer
<bean class="xxxxx">
<property name="userName" value="${userName}"/>
<property name="address" value="${address}"/>
</bean>CustomScopeConfigurer
EventListenerMethodProcessor
使用注意
总结
注意Spring的4个阶段:bean定义阶段、BeanFactory后置处理阶段、BeanPostProcessor注册阶段、单例bean创建组装阶段。
BeanDefinitionRegistryPostProcessor会在第一个阶段被调用,用来实现bean的注册操作,这个阶段会完成所有bean的注册。
BeanFactoryPostProcessor会在第2个阶段被调用,到这个阶段时候,bean此时已经完成了所有bean的注册操作,这个阶段中你可以对BeanFactory中的一些信息进行修改,比如修改阶段1中一些bean的定义信息,修改BeanFactory的一些配置等等。。
阶段2的时候,2个禁止操作:禁止注册bean、禁止从容器中获取bean。
本文介绍的2个接口的实现类可以通过 PriorityOrdered 接口或者 Ordered 接口来指定顺序。
版权声明
非特殊说明,本文由Zender原创或收集发布,欢迎转载。
ZENDER



发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。