在做中间件产品的时候,为了给业务方一个好用的客户端,我们一般会提供一个自定义的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
我们看到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 dependsOns = new ArrayList<>();
/**
* 发布的任务模板列表
*/
private Set templates = new HashSet<>();
/**
* 发布者的自定义过滤器列表
* Pair格式: (filterBeanName, filterClass)
*/
private Set>> filters = new HashSet<>();
private ConfigurableListableBeanFactory beanFactory;
PublisherBeanDefinitionBuilder() {
}
PublisherBeanDefinitionBuilder property(DispatchProperty property) {
this.property = property;
return this;
}
PublisherBeanDefinitionBuilder templates(Set 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>> filters) {
if (filters != null) {
this.filters = filters;
}
return this;
}
PublisherBeanDefinitionBuilder beanFactory(ConfigurableListableBeanFactory beanFactory) {
this.beanFactory = beanFactory;
return this;
}
PublisherBeanDefinitionBuilder filter(Pair> filter) {
if (filter == null) {
return this;
}
if (this.filters == null) {
this.filters = new HashSet<>();
}
this.filters.add(filter);
return this;
}
PublisherBeanDefinitionBuilder dependsOns(List 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
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>> filters = new HashSet<>();
/**
* 监听器列表
* Map格式: (templateCode, (listenerBeanName, listenerClass))
*/
private Map>> listeners = new HashMap<>();
private ConfigurableListableBeanFactory beanFactory;
SubscriberBeanDefinitionBuilder() {
}
SubscriberBeanDefinitionBuilder property(DispatchProperty property) {
this.property = property;
return this;
}
SubscriberBeanDefinitionBuilder filters(Set>> filters) {
if (filters != null) {
this.filters = filters;
}
return this;
}
SubscriberBeanDefinitionBuilder filter(Pair> filter) {
if (filter == null) {
return this;
}
if (this.filters == null) {
this.filters = new HashSet<>();
}
this.filters.add(filter);
return this;
}
SubscriberBeanDefinitionBuilder listeners(Map>> listeners) {
if (listeners != null) {
this.listeners = listeners;
}
return this;
}
SubscriberBeanDefinitionBuilder listener(String templateCode, Pair> 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> filterPair : this.filters) {
if (filterPair.getRight().getAnnotation(Component.class) == null) {
continue;
}
builder.addDependsOn(filterPair.getLeft());
}
for (Map.Entry>> 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