diff --git a/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/config/DynamicThreadPoolAutoConfiguration.java b/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/config/DynamicThreadPoolAutoConfiguration.java index 1f3a5b44..5c1dee41 100644 --- a/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/config/DynamicThreadPoolAutoConfiguration.java +++ b/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/config/DynamicThreadPoolAutoConfiguration.java @@ -59,6 +59,7 @@ import cn.hippo4j.springboot.starter.remote.ServerHealthCheck; import cn.hippo4j.springboot.starter.remote.ServerHttpAgent; import cn.hippo4j.springboot.starter.support.DynamicThreadPoolConfigService; import cn.hippo4j.springboot.starter.support.DynamicThreadPoolPostProcessor; +import cn.hippo4j.springboot.starter.support.ThreadPoolPluginRegisterPostProcessor; import lombok.AllArgsConstructor; import org.springframework.boot.autoconfigure.ImportAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; @@ -224,4 +225,10 @@ public class DynamicThreadPoolAutoConfiguration { public HttpAgent httpAgent(BootstrapProperties properties) { return new ServerHttpAgent(properties); } + + @Bean + public ThreadPoolPluginRegisterPostProcessor threadPoolPluginRegisterPostProcessor() { + return new ThreadPoolPluginRegisterPostProcessor(); + } + } diff --git a/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/support/ThreadPoolPluginRegisterPostProcessor.java b/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/support/ThreadPoolPluginRegisterPostProcessor.java new file mode 100644 index 00000000..d82b65f1 --- /dev/null +++ b/hippo4j-spring-boot/hippo4j-spring-boot-starter/src/main/java/cn/hippo4j/springboot/starter/support/ThreadPoolPluginRegisterPostProcessor.java @@ -0,0 +1,152 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.hippo4j.springboot.starter.support; + +import cn.hippo4j.common.toolkit.Assert; +import cn.hippo4j.core.plugin.ThreadPoolPlugin; +import cn.hippo4j.core.plugin.manager.DefaultGlobalThreadPoolPluginManager; +import cn.hippo4j.core.plugin.manager.GlobalThreadPoolPluginManager; +import cn.hippo4j.core.plugin.manager.ThreadPoolPluginRegistrar; +import cn.hippo4j.core.plugin.manager.ThreadPoolPluginSupport; +import lombok.extern.slf4j.Slf4j; +import org.springframework.aop.framework.autoproxy.AutoProxyUtils; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanInitializationException; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.config.AutowireCapableBeanFactory; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.ApplicationContextException; +import org.springframework.context.ApplicationEventPublisherAware; +import org.springframework.context.MessageSourceAware; +import org.springframework.context.ResourceLoaderAware; + +import java.util.Objects; + +/** + *
The extension implementation of {@link GlobalThreadPoolPluginManager} and {@link BeanPostProcessor}, + * used to register {@link ThreadPoolPlugin} for the bean initialization stage of the {@link ThreadPoolPluginSupport}. + * + *
NOTE: + * If the {@link ThreadPoolPlugin}, {@link ThreadPoolPluginRegistrar}, and {@link ThreadPoolPluginSupport} is set to lazy load, + * The processor will not perceive the bean unless the user actively triggers the initialization of the bean. + * + * @see ThreadPoolPluginSupport + * @see ThreadPoolPluginRegistrar + * @see ThreadPoolPlugin + * @see GlobalThreadPoolPluginManager + * @see DefaultGlobalThreadPoolPluginManager + */ +@Slf4j +public class ThreadPoolPluginRegisterPostProcessor extends DefaultGlobalThreadPoolPluginManager implements BeanPostProcessor, ApplicationContextAware { + + /** + * application context + */ + private ConfigurableListableBeanFactory beanFactory; + + /** + *
Post process bean, if bean is instance of {@link ThreadPoolPlugin}, + * {@link ThreadPoolPluginRegistrar} or {@link ThreadPoolPluginSupport}, + * then take beans as an available component and register to {@link GlobalThreadPoolPluginManager}. + * + * @param bean the new bean instance + * @param beanName the name of the bean + * @return the bean instance to use, either the original or a wrapped one; + * if {@code null}, no subsequent BeanPostProcessors will be invoked + * @throws BeansException in case of errors + * @see GlobalThreadPoolPluginManager#enableThreadPoolPlugin + * @see GlobalThreadPoolPluginManager#enableThreadPoolPluginRegistrar + * @see GlobalThreadPoolPluginManager#registerThreadPoolPluginSupport + */ + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + Class> beanType = null; + try { + beanType = AutoProxyUtils.determineTargetClass(beanFactory, beanName); + } catch (NoSuchBeanDefinitionException ex) { + if (log.isDebugEnabled()) { + log.debug("Could not resolve target class for bean with name '" + beanName + "'", ex); + } + } + if (Objects.isNull(beanType)) { + log.warn("cannot resolve type for bean [{}]", beanName); + return bean; + } + + // register bean if necessary + registerThreadPoolPluginRegistrarIfNecessary(bean, beanType); + registerThreadPoolPluginIfNecessary(bean, beanType); + registerThreadPoolPluginSupportIfNecessary(bean, beanType); + return bean; + } + + private void registerThreadPoolPluginSupportIfNecessary(Object bean, Class> beanType) { + if (ThreadPoolPluginSupport.class.isAssignableFrom(beanType)) { + ThreadPoolPluginSupport support = (ThreadPoolPluginSupport)bean; + if (registerThreadPoolPluginSupport(support) && log.isDebugEnabled()) { + log.info("register ThreadPoolPluginSupport [{}]", support.getThreadPoolId()); + } + } + } + + private void registerThreadPoolPluginIfNecessary(Object bean, Class> beanType) { + if (ThreadPoolPlugin.class.isAssignableFrom(beanType)) { + ThreadPoolPlugin plugin = (ThreadPoolPlugin)bean; + if (enableThreadPoolPlugin(plugin) && log.isDebugEnabled()) { + log.info("register ThreadPoolPlugin [{}]", plugin.getId()); + } + } + } + + private void registerThreadPoolPluginRegistrarIfNecessary(Object bean, Class> beanType) { + if (ThreadPoolPluginRegistrar.class.isAssignableFrom(beanType)) { + ThreadPoolPluginRegistrar registrar = (ThreadPoolPluginRegistrar)bean; + if (enableThreadPoolPluginRegistrar(registrar) && log.isDebugEnabled()) { + log.info("register ThreadPoolPluginRegistrar [{}]", registrar.getId()); + } + } + } + + /** + * Set the ApplicationContext that this object runs in. + * Normally this call will be used to initialize the object. + *
Invoked after population of normal bean properties but before an init callback such + * as {@link InitializingBean#afterPropertiesSet()} + * or a custom init-method. Invoked after {@link ResourceLoaderAware#setResourceLoader}, + * {@link ApplicationEventPublisherAware#setApplicationEventPublisher} and + * {@link MessageSourceAware}, if applicable. + * + * @param applicationContext the ApplicationContext object to be used by this object + * @throws ApplicationContextException in case of context initialization errors + * @throws BeansException if thrown by application context methods + * @see BeanInitializationException + */ + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + AutowireCapableBeanFactory factory = applicationContext.getAutowireCapableBeanFactory(); + Assert.isTrue( + factory instanceof ConfigurableListableBeanFactory, + "factory cannot cast to ConfigurableListableBeanFactory"); + this.beanFactory = (ConfigurableListableBeanFactory) factory; + } + +}