在做中间件产品的时候,为了给业务方一个好用的客户端,我们一般会提供一个自定义的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里面是如何实现的。
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();
}
}
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() {
}
@Override
public 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);
}
}
}
@Override
public void afterPropertiesSet() throws Exception {
}
@Override
public Class<?> getObjectType() {
return Client.class;
}
@Override
public void setEnvironment(Environment environment) {
this.environment = (ConfigurableEnvironment) environment;
}
@Override
public 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,此实现也类似,详见如下:
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);
}
}
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();
}
}
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() {
}
@Override
public 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();
}
@Override
public void afterPropertiesSet() throws Exception {
}
@Override
public Class<?> getObjectType() {
return PublisherImpl.class;
}
@Override
public void setEnvironment(Environment environment) {
this.environment = (ConfigurableEnvironment) environment;
}
@Override
public 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;
}
}
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);
}
}
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();
}
}
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() {
}
@Override
public 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();
}
@Override
public void afterPropertiesSet() throws Exception {
}
@Override
public Class<?> getObjectType() {
return PublisherImpl.class;
}
@Override
public void setEnvironment(Environment environment) {
this.environment = (ConfigurableEnvironment) environment;
}
@Override
public 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;
}
}