在做中间件产品的时候,为了给业务方一个好用的客户端,我们一般会提供一个自定义的xxx-spring-boot-starter,那么我们就可能涉及到将自己的客户端中的某个类初始化并注入到Spring上下文中去。为了更标准化去初始化这个类,让Spring来管理我们这个对象的生命周期,那么我们经常会使用BeanDefinition来定义并通过自定义的xxxFactoryBean来真正初始化我们的对象。
先看下使用示例:
private void resolveRegistryClientBeanDefinition(BeanDefinitionRegistry registry, DispatchProperty dispatchProperty) {String beanName = Client.class.getName();ClientBeanDefinitionBuilder beanDefinitionBuilder = new ClientBeanDefinitionBuilder();beanDefinitionBuilder.property(dispatchProperty);BeanDefinition beanDefinition = beanDefinitionBuilder.build();if (!context.containsBean(beanName)) {registry.registerBeanDefinition(beanName, beanDefinition);LOGGER.info("NBP-CLIENT-STARTER", "registered beanDefinition of {} in spring context.", beanName);} else {LOGGER.warn("NBP-CLIENT-STARTER", "beanDefinition of {} has already registered in spring context.", beanName);}}
以上,我们使用ClientBeanDefinitionBuilder来定义如何构建ClientFactoryBean,然后通过ClientFactoryBean来创建Client对象,并通过registry.registerBeanDefinition(beanName, beanDefinition); 来将其注入到Spring上下文中去。
我们再来看下ClientBeanDefinitionBuilder和ClientFactoryBean里面是如何实现的。
ClientBeanDefinitionBuilder
package com.xxx.arch.mw.nbp.client.spring;import com.xxx.arch.mw.nbp.client.configuration.DispatchProperty;
import com.xxx.arch.mw.nbp.client.constant.ClientConstants;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;/*** @created 2022-11-30 3:08 PM* @description:*/
public class ClientBeanDefinitionBuilder {private DispatchProperty property;ClientBeanDefinitionBuilder() {}ClientBeanDefinitionBuilder property(DispatchProperty property) {this.property = property;return this;}BeanDefinition build() {BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(ClientFactoryBean.class);builder.addPropertyValue("property", this.property);builder.addPropertyReference("publisher", ClientConstants.NBP_PUBLISHER_IMPL_BEAN_NAME);builder.addPropertyReference("subscriber", ClientConstants.NBP_SUBSCRIBER_IMPL_BEAN_NAME);builder.addDependsOn(ClientConstants.NBP_PUBLISHER_IMPL_BEAN_NAME);builder.addDependsOn(ClientConstants.NBP_SUBSCRIBER_IMPL_BEAN_NAME);builder.setInitMethodName(ClientConstants.INIT_METHOD);return builder.getBeanDefinition();}}
ClientFactoryBean
package com.xxx.arch.mw.nbp.client.spring;import com.xxx.arch.mw.nbp.client.Client;
import com.xxx.arch.mw.nbp.client.DefaultClient;
import com.xxx.arch.mw.nbp.client.configuration.DispatchProperty;
import com.xxx.arch.mw.nbp.client.publish.Publisher;
import com.xxx.arch.mw.nbp.client.subscribe.Subscriber;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;/*** @created 2022-11-30 3:32 PM* @description:*/
public class ClientFactoryBean implements FactoryBean<Object>, EnvironmentAware, InitializingBean {private ConfigurableEnvironment environment;private DispatchProperty property;private Client client;private Publisher publisher;private Subscriber subscriber;public ClientFactoryBean() {}@Overridepublic Object getObject() throws Exception {return client;}public void start() throws Exception {if (client == null) {client = new DefaultClient(this.property);if (publisher != null) {((DefaultClient) client).setPublisher(publisher);}if (subscriber != null) {((DefaultClient) client).setSubscriber(subscriber);}}}@Overridepublic void afterPropertiesSet() throws Exception {}@Overridepublic Class<?> getObjectType() {return Client.class;}@Overridepublic void setEnvironment(Environment environment) {this.environment = (ConfigurableEnvironment) environment;}@Overridepublic boolean isSingleton() {return true;}public DispatchProperty getProperty() {return property;}public void setProperty(DispatchProperty property) {this.property = property;}public Publisher getPublisher() {return publisher;}public void setPublisher(Publisher publisher) {this.publisher = publisher;}public Subscriber getSubscriber() {return subscriber;}public void setSubscriber(Subscriber subscriber) {this.subscriber = subscriber;}
}
我们看到Client内部还分别依赖了Publisher和Subscriber,此实现也类似,详见如下:
如何构建Publisher
private void resolveRegistryPublisherImplBeanDefinition(ConfigurableListableBeanFactory beanFactory,BeanDefinitionRegistry registry,DispatchProperty dispatchProperty) {String beanName = ClientConstants.NBP_PUBLISHER_IMPL_BEAN_NAME;// 额外增加主子任务需要发布者订阅的模板PUBLISHER_TEMPLATE_SET.addAll(MAPREDUCE_PUBLISHER_TEMPLATE_SET);BeanDefinition beanDefinition = new PublisherBeanDefinitionBuilder().property(dispatchProperty).beanFactory(beanFactory).templates(PUBLISHER_TEMPLATE_SET).filters(CUSTOM_PUBLISH_FILTER_SET).build();if (!context.containsBean(beanName)) {registry.registerBeanDefinition(beanName, beanDefinition);LOGGER.info("NBP-CLIENT-STARTER","registered beanDefinition of {} in spring context.", beanName);} else {LOGGER.warn("NBP-CLIENT-STARTER","beanDefinition of {} has already registered in spring context.", beanName);}}
PublisherBeanDefinitionBuilder
package com.xxx.arch.mw.nbp.client.spring;import com.xxx.arch.mw.nbp.client.configuration.DispatchProperty;
import com.xxx.arch.mw.nbp.client.constant.ClientConstants;
import com.xxx.arch.mw.nbp.common.domain.Template;
import com.xxx.commons.data.domain.tuple.Pair;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;/*** @created 2022-11-30 3:08 PM* @description:*/
public class PublisherBeanDefinitionBuilder {private DispatchProperty property;/*** 依赖的bean名称列表*/private List<String> dependsOns = new ArrayList<>();/*** 发布的任务模板列表*/private Set<Template> templates = new HashSet<>();/*** 发布者的自定义过滤器列表* Pair格式: (filterBeanName, filterClass)*/private Set<Pair<String, Class<?>>> filters = new HashSet<>();private ConfigurableListableBeanFactory beanFactory;PublisherBeanDefinitionBuilder() {}PublisherBeanDefinitionBuilder property(DispatchProperty property) {this.property = property;return this;}PublisherBeanDefinitionBuilder templates(Set<Template> templates) {if (templates != null) {this.templates = templates;}return this;}PublisherBeanDefinitionBuilder template(Template template) {if (template == null) {return this;}if (this.templates == null) {this.templates = new HashSet<>();}this.templates.add(template);return this;}PublisherBeanDefinitionBuilder filters(Set<Pair<String, Class<?>>> filters) {if (filters != null) {this.filters = filters;}return this;}PublisherBeanDefinitionBuilder beanFactory(ConfigurableListableBeanFactory beanFactory) {this.beanFactory = beanFactory;return this;}PublisherBeanDefinitionBuilder filter(Pair<String, Class<?>> filter) {if (filter == null) {return this;}if (this.filters == null) {this.filters = new HashSet<>();}this.filters.add(filter);return this;}PublisherBeanDefinitionBuilder dependsOns(List<String> dependsOns) {if (dependsOns != null) {this.dependsOns = dependsOns;}return this;}PublisherBeanDefinitionBuilder dependsOn(String dependsOn) {if (dependsOn == null) {return this;}if (this.dependsOns != null) {this.dependsOns = new ArrayList<>();}this.dependsOns.add(dependsOn);return this;}BeanDefinition build() {BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(PublisherFactoryBean.class);builder.addPropertyValue("property", this.property);builder.addPropertyValue("templates", this.templates);builder.addPropertyValue("filters", this.filters);builder.addPropertyValue("beanFactory", this.beanFactory);builder.setInitMethodName(ClientConstants.INIT_METHOD);for (String dependsOn : dependsOns) {builder.addDependsOn(dependsOn);}return builder.getBeanDefinition();}}
PublisherFactoryBean
package com.xxx.arch.mw.nbp.client.spring;import com.xxx.arch.mw.nbp.client.PublisherImpl;
import com.xxx.arch.mw.nbp.client.configuration.DispatchProperty;
import com.xxx.arch.mw.nbp.client.publish.Publisher;
import com.xxx.arch.mw.nbp.common.domain.Template;
import com.xxx.arch.mw.nbp.common.extension.Filter;
import com.xxx.commons.data.domain.tuple.Pair;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;import java.util.HashSet;
import java.util.Set;/*** @created 2022-11-30 3:32 PM* @description:*/
public class PublisherFactoryBean implements FactoryBean<Object>, EnvironmentAware, InitializingBean {private ConfigurableEnvironment environment;private DispatchProperty property;private Set<Template> templates = new HashSet<>();private Set<Pair<String, Class<?>>> filters = new HashSet<>();private ConfigurableListableBeanFactory beanFactory;private Publisher publisher;public PublisherFactoryBean() {}@Overridepublic Object getObject() throws Exception {return publisher;}public void start() throws Exception {if (publisher == null) {publisher = new PublisherImpl(this.property);for (Template template : templates) {publisher.register(template.getTemplateCode());}for (Pair<String, Class<?>> filterPair : filters) {Object filter = filterPair.getRight().getAnnotation(Component.class) != null ?beanFactory.getBean(filterPair.getLeft(), filterPair.getRight()) :filterPair.getRight().getDeclaredConstructor().newInstance();publisher.addFilter((Filter) filter);}}publisher.start();}@Overridepublic void afterPropertiesSet() throws Exception {}@Overridepublic Class<?> getObjectType() {return PublisherImpl.class;}@Overridepublic void setEnvironment(Environment environment) {this.environment = (ConfigurableEnvironment) environment;}@Overridepublic boolean isSingleton() {return true;}public DispatchProperty getProperty() {return property;}public void setProperty(DispatchProperty property) {this.property = property;}public Set<Template> getTemplates() {return templates;}public void setTemplates(Set<Template> templates) {this.templates = templates;}public Set<Pair<String, Class<?>>> getFilters() {return filters;}public void setFilters(Set<Pair<String, Class<?>>> filters) {this.filters = filters;}public ConfigurableListableBeanFactory getBeanFactory() {return beanFactory;}public void setBeanFactory(ConfigurableListableBeanFactory beanFactory) {this.beanFactory = beanFactory;}
}
如何构建Subscriber
private void resolveRegistrySubscriberImplBeanDefinition(ConfigurableListableBeanFactory beanFactory,BeanDefinitionRegistry registry,DispatchProperty dispatchProperty) {String beanName = ClientConstants.NBP_SUBSCRIBER_IMPL_BEAN_NAME;BeanDefinition beanDefinition = new SubscriberBeanDefinitionBuilder().beanFactory(beanFactory).property(dispatchProperty).filters(CUSTOM_EXECUTE_FILTER_SET).listeners(DISPATCH_LISTENER_MAP).build();if (!context.containsBean(beanName)) {registry.registerBeanDefinition(beanName, beanDefinition);LOGGER.info("NBP-CLIENT-STARTER", "registered beanDefinition of {} in spring context.", beanName);} else {LOGGER.warn("NBP-CLIENT-STARTER", "beanDefinition of {} has already registered in spring context.", beanName);}}
SubscriberBeanDefinitionBuilder
package com.xxx.arch.mw.nbp.client.spring;import com.xxx.arch.mw.nbp.client.configuration.DispatchProperty;
import com.xxx.arch.mw.nbp.client.constant.ClientConstants;
import com.xxx.arch.mw.nbp.client.remoting.Discover;
import com.xxx.commons.data.domain.tuple.Pair;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.stereotype.Component;import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;/*** @created 2022-11-30 3:08 PM* @description:*/
public class SubscriberBeanDefinitionBuilder {private DispatchProperty property;/*** 执行者的自定义过滤器列表* Pair格式: (filterBeanName, filterClass)*/private Set<Pair<String, Class<?>>> filters = new HashSet<>();/*** 监听器列表* Map格式: (templateCode, (listenerBeanName, listenerClass))*/private Map<String, Pair<String, Class<?>>> listeners = new HashMap<>();private ConfigurableListableBeanFactory beanFactory;SubscriberBeanDefinitionBuilder() {}SubscriberBeanDefinitionBuilder property(DispatchProperty property) {this.property = property;return this;}SubscriberBeanDefinitionBuilder filters(Set<Pair<String, Class<?>>> filters) {if (filters != null) {this.filters = filters;}return this;}SubscriberBeanDefinitionBuilder filter(Pair<String, Class<?>> filter) {if (filter == null) {return this;}if (this.filters == null) {this.filters = new HashSet<>();}this.filters.add(filter);return this;}SubscriberBeanDefinitionBuilder listeners(Map<String, Pair<String, Class<?>>> listeners) {if (listeners != null) {this.listeners = listeners;}return this;}SubscriberBeanDefinitionBuilder listener(String templateCode, Pair<String, Class<?>> listener) {if (templateCode == null) {return this;}if (listener == null) {return this;}if (this.listeners == null) {this.listeners = new HashMap<>();}this.listeners.put(templateCode, listener);return this;}SubscriberBeanDefinitionBuilder beanFactory(ConfigurableListableBeanFactory beanFactory) {this.beanFactory = beanFactory;return this;}BeanDefinition build() {BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(SubscriberFactoryBean.class);builder.addPropertyValue("property", this.property);builder.addPropertyValue("filters", this.filters);builder.addPropertyValue("beanFactory", this.beanFactory);builder.addPropertyValue("listeners", this.listeners);builder.addPropertyReference("publisher", ClientConstants.NBP_PUBLISHER_IMPL_BEAN_NAME);builder.addDependsOn(ClientConstants.NBP_PUBLISHER_IMPL_BEAN_NAME);for (Pair<String, Class<?>> filterPair : this.filters) {if (filterPair.getRight().getAnnotation(Component.class) == null) {continue;}builder.addDependsOn(filterPair.getLeft());}for (Map.Entry<String, Pair<String, Class<?>>> entry : this.listeners.entrySet()) {if (Discover.IGNORED_TEMPLATE_CODE.equals(entry.getKey())) {continue;}builder.addDependsOn(entry.getValue().getLeft());}builder.setInitMethodName(ClientConstants.INIT_METHOD);return builder.getBeanDefinition();}}
SubscriberFactoryBean
package com.xxx.arch.mw.nbp.client.spring;import com.xxx.arch.mw.nbp.client.PublisherImpl;
import com.xxx.arch.mw.nbp.client.SubscriberImpl;
import com.xxx.arch.mw.nbp.client.configuration.DispatchProperty;
import com.xxx.arch.mw.nbp.client.publish.Publisher;
import com.xxx.arch.mw.nbp.client.remoting.Discover;
import com.xxx.arch.mw.nbp.client.subscribe.Subscriber;
import com.xxx.arch.mw.nbp.common.domain.Listener;
import com.xxx.arch.mw.nbp.common.extension.Filter;
import com.xxx.commons.data.domain.tuple.Pair;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;/*** @created 2022-11-30 3:32 PM* @description:*/
public class SubscriberFactoryBean implements FactoryBean<Object>, EnvironmentAware, InitializingBean {private ConfigurableEnvironment environment;private DispatchProperty property;private Set<Pair<String, Class<?>>> filters = new HashSet<>();private Map<String, Pair<String, Class>> listeners = new HashMap<>();private ConfigurableListableBeanFactory beanFactory;private Subscriber subscriber;private Publisher publisher;public SubscriberFactoryBean() {}@Overridepublic Object getObject() throws Exception {return subscriber;}public void start() throws Exception {if (subscriber == null) {subscriber = new SubscriberImpl(this.property, publisher);for (Pair<String, Class<?>> filterPair : filters) {Object filter = filterPair.getRight().getAnnotation(Component.class) != null ?beanFactory.getBean(filterPair.getLeft(), filterPair.getRight()) :filterPair.getRight().getDeclaredConstructor().newInstance();subscriber.addFilter((Filter) filter);}for (Map.Entry<String, Pair<String, Class>> entry : this.listeners.entrySet()) {if (Discover.IGNORED_TEMPLATE_CODE.equals(entry.getKey())) {continue;}subscriber.subscribe(entry.getKey(), (Listener) beanFactory.getBean(entry.getValue().getLeft(), entry.getValue().getRight()));}}subscriber.start();}@Overridepublic void afterPropertiesSet() throws Exception {}@Overridepublic Class<?> getObjectType() {return PublisherImpl.class;}@Overridepublic void setEnvironment(Environment environment) {this.environment = (ConfigurableEnvironment) environment;}@Overridepublic boolean isSingleton() {return true;}public DispatchProperty getProperty() {return property;}public void setProperty(DispatchProperty property) {this.property = property;}public Publisher getPublisher() {return publisher;}public void setPublisher(Publisher publisher) {this.publisher = publisher;}public Set<Pair<String, Class<?>>> getFilters() {return filters;}public void setFilters(Set<Pair<String, Class<?>>> filters) {this.filters = filters;}public Map<String, Pair<String, Class>> getListeners() {return listeners;}public void setListeners(Map<String, Pair<String, Class>> listeners) {this.listeners = listeners;}public ConfigurableListableBeanFactory getBeanFactory() {return beanFactory;}public void setBeanFactory(ConfigurableListableBeanFactory beanFactory) {this.beanFactory = beanFactory;}
}