org.zodiac.autoconfigure.context.AppRefreshAutoConfiguration Maven / Gradle / Ivy
package org.zodiac.autoconfigure.context;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.PostConstruct;
import org.springframework.aop.scope.ScopedProxyUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.weaving.LoadTimeWeaverAware;
import org.springframework.core.env.Environment;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.instrument.classloading.LoadTimeWeaver;
import org.springframework.stereotype.Component;
import org.zodiac.commons.constants.Constants;
import org.zodiac.commons.constants.SystemPropertiesConstants;
import org.zodiac.commons.util.Colls;
import org.zodiac.commons.util.lang.Strings;
import org.zodiac.core.context.refresh.AppContextRefreshHistory;
import org.zodiac.core.context.refresh.AppContextRefreshListener;
import org.zodiac.core.context.refresh.AppContextRefresher;
import org.zodiac.core.event.context.AppRefreshEventListener;
import org.zodiac.core.logging.AppLoggingRebinder;
@SpringBootConfiguration
@ConditionalOnClass(value = {org.zodiac.core.context.scope.refresh.AppRefreshScope.class})
@ConditionalOnProperty(name = SystemPropertiesConstants.Zodiac.SPRING_BOOTSTRAP_REFRESH_SCOPE_ENABLED, havingValue = "true", matchIfMissing = true)
@AutoConfigureBefore(value = {HibernateJpaAutoConfiguration.class})
public class AppRefreshAutoConfiguration {
public AppRefreshAutoConfiguration() {
super();
}
@Bean
@ConditionalOnMissingBean
@ConfigurationProperties(prefix = SystemPropertiesConstants.Zodiac.SPRING_BOOTSTRAP_REFRESH_SCOPE_PREFIX, ignoreInvalidFields = true)
protected AppRefreshProperties appRefreshProperties() {
return new AppRefreshProperties();
}
@Bean
@ConditionalOnMissingBean(value = {org.zodiac.core.context.scope.refresh.AppRefreshScope.class})
protected static org.zodiac.core.context.scope.refresh.AppRefreshScope appRefreshScope(AppRefreshProperties appRefreshProperties) {
return new org.zodiac.core.context.scope.refresh.AppRefreshScope().setEager(appRefreshProperties.isScopeEager());
}
@Bean
@ConditionalOnMissingBean(value = {AppLoggingRebinder.class})
protected static AppLoggingRebinder appLoggingRebinder() {
return new AppLoggingRebinder();
}
@Bean
@ConditionalOnMissingBean(value = {AppContextRefreshHistory.class})
protected AppContextRefreshHistory appContextRefreshHistory(AppRefreshProperties appRefreshProperties) {
return new AppContextRefreshHistory().setMaxSize(appRefreshProperties.getHistoryMaxSize());
}
@Bean
@ConditionalOnMissingBean(value = {AppContextRefresher.class})
protected AppContextRefresher appContextRefresher(ConfigurableApplicationContext context,
org.zodiac.core.context.scope.refresh.AppRefreshScope scope,
AppRefreshProperties appRefreshProperties, AppContextRefreshHistory appContextRefreshHistory,
ObjectProvider listenersProvider) {
return new AppContextRefresher(context, scope, appRefreshProperties, appContextRefreshHistory, listenersProvider.stream().collect(Collectors.toList()));
}
@Bean
protected AppRefreshEventListener appRefreshEventListener(AppContextRefresher contextRefresher) {
return new AppRefreshEventListener(contextRefresher);
}
@SpringBootConfiguration
@ConditionalOnClass(name = {"javax.persistence.EntityManagerFactory"})
protected static class AppJpaInvokerConfiguration implements LoadTimeWeaverAware {
// @Autowired
private ListableBeanFactory beanFactory;
// public JpaInvokerConfiguration(ListableBeanFactory beanFactory) {
// this.beanFactory = beanFactory;
// }
public AppJpaInvokerConfiguration(ListableBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@PostConstruct
public void init() {
String cls = "org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerInvoker";
if (this.beanFactory.containsBean(cls)) {
this.beanFactory.getBean(cls);
}
}
@Override
public void setLoadTimeWeaver(LoadTimeWeaver ltw) {}
}
@Component
protected static class AppRefreshScopeBeanDefinitionEnhancer
implements BeanDefinitionRegistryPostProcessor, EnvironmentAware {
private Environment environment;
private boolean bound = false;
/**
* Class names for beans to post process into refresh scope. Useful when you don't control the bean definition
* (e.g. it came from auto-configuration).
*/
private Set refreshables = Colls.set("com.zaxxer.hikari.HikariDataSource");
public AppRefreshScopeBeanDefinitionEnhancer() {
super();
}
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
public Set getRefreshable() {
return this.refreshables;
}
public void setRefreshable(Set refreshables) {
if (this.refreshables != refreshables) {
this.refreshables.clear();
this.refreshables.addAll(refreshables);
}
}
public void setExtraRefreshable(Set refreshables) {
this.refreshables.addAll(refreshables);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
bindEnvironmentIfNeeded(registry);
for (String name : registry.getBeanDefinitionNames()) {
BeanDefinition definition = registry.getBeanDefinition(name);
if (isApplicable(registry, name, definition)) {
BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, name);
BeanDefinitionHolder proxy = ScopedProxyUtils.createScopedProxy(holder, registry, true);
definition.setScope(Constants.Zodiac.BOOTSTRAP_REFRESH_SCOPE_NAME);
if (registry.containsBeanDefinition(proxy.getBeanName())) {
registry.removeBeanDefinition(proxy.getBeanName());
}
registry.registerBeanDefinition(proxy.getBeanName(), proxy.getBeanDefinition());
}
}
}
private boolean isApplicable(BeanDefinitionRegistry registry, String name, BeanDefinition definition) {
String scope = definition.getScope();
if (Constants.Zodiac.BOOTSTRAP_REFRESH_SCOPE_NAME.equals(scope)) {
/*Already refresh scoped*/
return false;
}
String type = definition.getBeanClassName();
if (!Strings.hasText(type) && registry instanceof BeanFactory) {
Class> cls = ((BeanFactory)registry).getType(name);
if (cls != null) {
type = cls.getName();
}
}
if (type != null) {
return this.refreshables.contains(type);
}
return false;
}
private void bindEnvironmentIfNeeded(BeanDefinitionRegistry registry) {
if (!this.bound) {
/*only bind once*/
if (this.environment == null) {
this.environment = new StandardEnvironment();
}
Binder.get(this.environment).bind(
SystemPropertiesConstants.Zodiac.SPRING_BOOTSTRAP_REFRESH_SCOPE_PREFIX, Bindable.ofInstance(this));
this.bound = true;
}
}
}
}