- Dubbo学习之监听器
本文基于Spring Boot 2.6.6
,dubbo-spring-boot-starter 3.0.6
环境。
引入dubbo-spring-boot-starter
包,就会引入其依赖dubbo-spring-boot-autoconfigure
,该依赖实现了Dubbo自动配置功能,其spring.factories
文件内容如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.apache.dubbo.spring.boot.autoconfigure.DubboRelaxedBinding2AutoConfiguration
dubbo-spring-boot-autoconfigure
会引入依赖dubbo-spring-boot-autoconfigure-compatible
,该依赖也存在一个spring.factories
文件,其内容如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.apache.dubbo.spring.boot.autoconfigure.DubboAutoConfiguration,\
org.apache.dubbo.spring.boot.autoconfigure.DubboRelaxedBindingAutoConfiguration,\
org.apache.dubbo.spring.boot.autoconfigure.DubboListenerAutoConfiguration
org.springframework.context.ApplicationListener=\
org.apache.dubbo.spring.boot.context.event.WelcomeLogoApplicationListener
org.springframework.boot.env.EnvironmentPostProcessor=\
org.apache.dubbo.spring.boot.env.DubboDefaultPropertiesEnvironmentPostProcessor
org.springframework.context.ApplicationContextInitializer=\
org.apache.dubbo.spring.boot.context.DubboApplicationContextInitializer
综上可知,dubbo-spring-boot-autoconfigure
引入的自动配置类如下:
org.apache.dubbo.spring.boot.autoconfigure.DubboRelaxedBinding2AutoConfiguration
;org.apache.dubbo.spring.boot.autoconfigure.DubboAutoConfiguration
;org.apache.dubbo.spring.boot.autoconfigure.DubboRelaxedBindingAutoConfiguration
;org.apache.dubbo.spring.boot.autoconfigure.DubboListenerAutoConfiguration
;
DubboAutoConfiguration
是Dubbo自动配置的核心类,代码如下:
// 默认开启,除非指定dubbo.enabled=false
@ConditionalOnProperty(prefix = DUBBO_PREFIX, name = "enabled", matchIfMissing = true)
@Configuration
// 在DubboRelaxedBindingAutoConfiguration之后配置
@AutoConfigureAfter(DubboRelaxedBindingAutoConfiguration.class)
// 因为配置属性类DubboConfigurationProperties
@EnableConfigurationProperties(DubboConfigurationProperties.class)
// 开启Dubbo
@EnableDubboConfig
public class DubboAutoConfiguration {
// 存在dubbo.scan.base-packages属性,且存在"dubbo-service-class-base-packages" Bean,则引入后置处理器ServiceAnnotationPostProcessor,用于解析DubboService注解
// Sprin Boot 2.6.6环境下,BASE_PACKAGES_BEAN_NAME由DubboRelaxedBinding2AutoConfiguration完成注入
// Spring Boot 1.x环境下,BASE_PACKAGES_BEAN_NAME由DubboRelaxedBindingAutoConfiguration完成注入
@ConditionalOnProperty(prefix = DUBBO_SCAN_PREFIX, name = BASE_PACKAGES_PROPERTY_NAME)
@ConditionalOnBean(name = BASE_PACKAGES_BEAN_NAME)
@Bean
public ServiceAnnotationPostProcessor serviceAnnotationBeanProcessor(@Qualifier(BASE_PACKAGES_BEAN_NAME)
Set<String> packagesToScan) {
return new ServiceAnnotationPostProcessor(packagesToScan);
}
}
核心功能为:
- 引入属性配置类
DubboConfigurationProperties
; - 通过注解
EnableDubboConfig
开启Dubbo功能; - 注入
ServiceAnnotationPostProcessor
用于解析DubboService
注解;
DubboConfigurationProperties
声明了Dubbo中各组件的配置参数信息,核心代码如下:
@ConfigurationProperties(DUBBO_PREFIX)
public class DubboConfigurationProperties {
@NestedConfigurationProperty
private Config config = new Config();
@NestedConfigurationProperty
private Scan scan = new Scan();
// Single Config Bindings
@NestedConfigurationProperty
private ApplicationConfig application = new ApplicationConfig();
@NestedConfigurationProperty
private ModuleConfig module = new ModuleConfig();
@NestedConfigurationProperty
private RegistryConfig registry = new RegistryConfig();
@NestedConfigurationProperty
private ProtocolConfig protocol = new ProtocolConfig();
@NestedConfigurationProperty
private MonitorConfig monitor = new MonitorConfig();
@NestedConfigurationProperty
private ProviderConfig provider = new ProviderConfig();
@NestedConfigurationProperty
private ConsumerConfig consumer = new ConsumerConfig();
@NestedConfigurationProperty
private ConfigCenterBean configCenter = new ConfigCenterBean();
@NestedConfigurationProperty
private MetadataReportConfig metadataReport = new MetadataReportConfig();
@NestedConfigurationProperty
private MetricsConfig metrics = new MetricsConfig();
// Multiple Config Bindings
private Map<String, ModuleConfig> modules = new LinkedHashMap<>();
private Map<String, RegistryConfig> registries = new LinkedHashMap<>();
private Map<String, ProtocolConfig> protocols = new LinkedHashMap<>();
private Map<String, MonitorConfig> monitors = new LinkedHashMap<>();
private Map<String, ProviderConfig> providers = new LinkedHashMap<>();
private Map<String, ConsumerConfig> consumers = new LinkedHashMap<>();
private Map<String, ConfigCenterBean> configCenters = new LinkedHashMap<>();
private Map<String, MetadataReportConfig> metadataReports = new LinkedHashMap<>();
private Map<String, MetricsConfig> metricses = new LinkedHashMap<>();
}
EnableDubboConfig
EnableDubboConfig
的核心就是引入DubboConfigConfigurationRegistrar
,代码如下:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
// 借助Import机制引入DubboConfigConfigurationRegistrar
@Import(DubboConfigConfigurationRegistrar.class)
public @interface EnableDubboConfig {
// 现在不用了,详见DubboConfigConfigurationRegistrar
boolean multiple() default true;
}
DubboConfigConfigurationRegistrar
的registerBeanDefinitions
方法就是初始化Dubbo相关的Bean,代码如下:
public class DubboConfigConfigurationRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 初始化Dubbo 相关Bean
DubboSpringInitializer.initialize(registry);
// Config beans creating from props have move to ConfigManager
// AnnotationAttributes attributes = AnnotationAttributes.fromMap(
// importingClassMetadata.getAnnotationAttributes(EnableDubboConfig.class.getName()));
//
// boolean multiple = attributes.getBoolean("multiple");
//
// // Single Config Bindings
// registerBeans(registry, DubboConfigConfiguration.Single.class);
//
// if (multiple) { // Since 2.6.6 https://github.com/apache/dubbo/issues/3193
// registerBeans(registry, DubboConfigConfiguration.Multiple.class);
// }
}
}
DubboSpringInitializer
DubboSpringInitializer
是Dubbo Spring初始化的入口,其initialize
代码如下:
public static void initialize(BeanDefinitionRegistry registry) {
// 放入DubboSpringInitContext
if (contextMap.putIfAbsent(registry, new DubboSpringInitContext()) != null) {
// 若已存在,则已初始化,直接退出
return;
}
DubboSpringInitContext context = contextMap.get(registry);
// 根据BeanDefinitionRegistry查找ConfigurableListableBeanFactory
ConfigurableListableBeanFactory beanFactory = findBeanFactory(registry);
// 初始化DubboSpringInitContext
initContext(context, registry, beanFactory);
}
private static void initContext(DubboSpringInitContext context, BeanDefinitionRegistry registry,
ConfigurableListableBeanFactory beanFactory) {
context.setRegistry(registry);
context.setBeanFactory(beanFactory);
// 支持用户定制化处理DubboSpringInitContext
customize(context);
// 初始化ModuleModel
ModuleModel moduleModel = context.getModuleModel();
if (moduleModel == null) {
// 如果当前DubboSpringInitContext中不存在ModuleModel,则使用默认值
ApplicationModel applicationModel;
if (findContextForApplication(ApplicationModel.defaultModel()) == null) {
// 第一个DubboSpringInitContext使用ApplicationModel.defaultModel()
applicationModel = ApplicationModel.defaultModel();
logger.info("Use default application: " + safeGetModelDesc(applicationModel));
} else {
// 后续DubboSpringInitContext使用FrameworkModel.defaultModel().newApplication()
applicationModel = FrameworkModel.defaultModel().newApplication();
logger.info("Create new application: " + safeGetModelDesc(applicationModel));
}
moduleModel = applicationModel.getDefaultModule();
context.setModuleModel(moduleModel);
logger.info("Use default module model of target application: " + safeGetModelDesc(moduleModel));
} else {
logger.info("Use module model from customizer: " + safeGetModelDesc(moduleModel));
}
logger.info("Bind " + safeGetModelDesc(moduleModel) + " to spring container: " + ObjectUtils.identityToString(registry));
if (context.getModuleAttributes().size() > 0) {
context.getModuleModel().getAttributes().putAll(context.getModuleAttributes());
}
// 注册Dubbo 初始化上下文到Spring容器中
registerContextBeans(beanFactory, context);
// 标记绑定标识
context.markAsBound();
// 注册Dubbo Bean
DubboBeanUtils.registerCommonBeans(registry);
}
DubboSpringInitializer.initContext
将初始化后的DubboSpringInitContext
注册到Spring容器中,用户可实现自定义DubboSpringInitCustomizer
来定制化处理DubboSpringInitContext
;再借助DubboBeanUtils
注册其它Dubbo Bean;
DubboBeanUtils.registerCommonBeans
方法代码如下:
static void registerCommonBeans(BeanDefinitionRegistry registry) {
registerInfrastructureBean(registry, ServicePackagesHolder.BEAN_NAME, ServicePackagesHolder.class);
registerInfrastructureBean(registry, ReferenceBeanManager.BEAN_NAME, ReferenceBeanManager.class);
// Since 2.5.7 Register @Reference Annotation Bean Processor as an infrastructure Bean
registerInfrastructureBean(registry, ReferenceAnnotationBeanPostProcessor.BEAN_NAME,
ReferenceAnnotationBeanPostProcessor.class);
// TODO Whether DubboConfigAliasPostProcessor can be removed ?
// Since 2.7.4 [Feature] https://github.com/apache/dubbo/issues/5093
registerInfrastructureBean(registry, DubboConfigAliasPostProcessor.BEAN_NAME,
DubboConfigAliasPostProcessor.class);
// Since 2.7.4 Register DubboBootstrapApplicationListener as an infrastructure Bean
// registerInfrastructureBean(registry, DubboBootstrapApplicationListener.BEAN_NAME,
// DubboBootstrapApplicationListener.class);
// register ApplicationListeners
registerInfrastructureBean(registry, DubboDeployApplicationListener.class.getName(), DubboDeployApplicationListener.class);
registerInfrastructureBean(registry, DubboConfigApplicationListener.class.getName(), DubboConfigApplicationListener.class);
// Since 2.7.6 Register DubboConfigDefaultPropertyValueBeanPostProcessor as an infrastructure Bean
registerInfrastructureBean(registry, DubboConfigDefaultPropertyValueBeanPostProcessor.BEAN_NAME,
DubboConfigDefaultPropertyValueBeanPostProcessor.class);
// Dubbo config initializer
registerInfrastructureBean(registry, DubboConfigBeanInitializer.BEAN_NAME, DubboConfigBeanInitializer.class);
// register infra bean if not exists later
registerInfrastructureBean(registry, DubboInfraBeanRegisterPostProcessor.BEAN_NAME, DubboInfraBeanRegisterPostProcessor.class);
}
ServiceAnnotationPostProcessor
当存在配置参数dubbo.scan.base-packages
且该参数以dubbo-service-class-base-packages
Bean存在于Spring容器中,那么就需要注入ServiceAnnotationPostProcessor
用于发现注解DubboService
标注的类;
ServiceAnnotationPostProcessor
支持的注解类型如下:
private final static List<Class<? extends Annotation>> serviceAnnotationTypes = asList(
// @since 2.7.7 Add the @DubboService , the issue : https://github.com/apache/dubbo/issues/6007
DubboService.class,
// @since 2.7.0 the substitute @com.alibaba.dubbo.config.annotation.Service
Service.class,
// @since 2.7.3 Add the compatibility for legacy Dubbo's @Service , the issue : https://github.com/apache/dubbo/issues/4330
com.alibaba.dubbo.config.annotation.Service.class
);
ServiceAnnotationPostProcessor
实现了接口BeanFactoryPostProcessor
,可用于在Bean初始化前对BeanFactory做处理;AbstractApplicationContext.refresh
方法中执行invokeBeanFactoryPostProcessors(beanFactory)
,会执行Spring容器中所有的BeanFactoryPostProcessor
;ServiceAnnotationPostProcessor.postProcessBeanDefinitionRegistry
主要是将指定扫描包路径下的标注了Dubbo Service注解的类包装为AbstractBeanDefinition
并注入Spring容器,核心代码如下:
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
this.registry = registry;
scanServiceBeans(resolvedPackagesToScan, registry);
}
private void scanServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
scaned = true;
if (CollectionUtils.isEmpty(packagesToScan)) {
// 如果不存在指定的扫描路径,则无需扫描
if (logger.isWarnEnabled()) {
logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!");
}
return;
}
DubboClassPathBeanDefinitionScanner scanner =
new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);
BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);
scanner.setBeanNameGenerator(beanNameGenerator);
for (Class<? extends Annotation> annotationType : serviceAnnotationTypes) {
scanner.addIncludeFilter(new AnnotationTypeFilter(annotationType));
}
ScanExcludeFilter scanExcludeFilter = new ScanExcludeFilter();
scanner.addExcludeFilter(scanExcludeFilter);
for (String packageToScan : packagesToScan) {
// 如果已经扫描过则无需再扫描
if (servicePackagesHolder.isPackageScanned(packageToScan)) {
if (logger.isInfoEnabled()) {
logger.info("Ignore package who has already bean scanned: " + packageToScan);
}
continue;
}
// 注册packageToScan包下Dubbo Service BeanDefinition
scanner.scan(packageToScan);
// 查找packageToScan包下所有的Dubbo Service,并包装为BeanDefinitionHolder
Set<BeanDefinitionHolder> beanDefinitionHolders =
findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);
if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {
if (logger.isInfoEnabled()) {
List<String> serviceClasses = new ArrayList<>(beanDefinitionHolders.size());
for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
serviceClasses.add(beanDefinitionHolder.getBeanDefinition().getBeanClassName());
}
logger.info("Found " + beanDefinitionHolders.size() + " classes annotated by Dubbo @Service under package [" + packageToScan + "]: " + serviceClasses);
}
for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
// 处理经过扫描得到的BeanDefinitionHolder
processScannedBeanDefinition(beanDefinitionHolder, registry, scanner);
// 缓存该Class,避免再次扫描
servicePackagesHolder.addScannedClass(beanDefinitionHolder.getBeanDefinition().getBeanClassName());
}
} else {
if (logger.isWarnEnabled()) {
logger.warn("No class annotated by Dubbo @Service was found under package ["
+ packageToScan + "], ignore re-scanned classes: " + scanExcludeFilter.getExcludedCount());
}
}
// 缓存该包,避免再次扫描
servicePackagesHolder.addScannedPackage(packageToScan);
}
}
private void processScannedBeanDefinition(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry,
DubboClassPathBeanDefinitionScanner scanner) {
Class<?> beanClass = resolveClass(beanDefinitionHolder);
// 获取Dubbo Service注解
Annotation service = findServiceAnnotation(beanClass);
// 获取Dubbo Service注解的属性值,忽略默认值
Map<String, Object> serviceAnnotationAttributes = AnnotationUtils.getAttributes(service, true);
// 获取接口类
String serviceInterface = resolveInterfaceName(serviceAnnotationAttributes, beanClass);
String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();
// 构造ServiceBean BeanName
String beanName = generateServiceBeanName(serviceAnnotationAttributes, serviceInterface);
// 构造AbstractBeanDefinition
AbstractBeanDefinition serviceBeanDefinition =
buildServiceBeanDefinition(serviceAnnotationAttributes, serviceInterface, annotatedServiceBeanName);
// 注册AbstractBeanDefinition
registerServiceBeanDefinition(beanName, serviceBeanDefinition, serviceInterface);
}
private AbstractBeanDefinition buildServiceBeanDefinition(Map<String, Object> serviceAnnotationAttributes,
String serviceInterface,
String refServiceBeanName) {
BeanDefinitionBuilder builder = rootBeanDefinition(ServiceBean.class);
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
String[] ignoreAttributeNames = of("provider", "monitor", "application", "module", "registry", "protocol",
"interface", "interfaceName", "parameters");
propertyValues.addPropertyValues(new AnnotationPropertyValuesAdapter(serviceAnnotationAttributes, environment, ignoreAttributeNames));
//set config id, for ConfigManager cache key
//builder.addPropertyValue("id", beanName);
// 设置ref属性
addPropertyReference(builder, "ref", refServiceBeanName);
// 这是interface属性
builder.addPropertyValue("interface", serviceInterface);
// 设置parameters属性
builder.addPropertyValue("parameters", DubboAnnotationUtils.convertParameters((String[]) serviceAnnotationAttributes.get("parameters")));
// 处理methods属性如果存在的话
List<MethodConfig> methodConfigs = convertMethodConfigs(serviceAnnotationAttributes.get("methods"));
if (!methodConfigs.isEmpty()) {
builder.addPropertyValue("methods", methodConfigs);
}
// 处理provider属性如果存在的话
String providerConfigId = (String) serviceAnnotationAttributes.get("provider");
if (StringUtils.hasText(providerConfigId)) {
addPropertyValue(builder, "providerIds", providerConfigId);
}
// 处理registry属性如果存在的话
String[] registryConfigIds = (String[]) serviceAnnotationAttributes.get("registry");
if (registryConfigIds != null && registryConfigIds.length > 0) {
resolveStringArray(registryConfigIds);
builder.addPropertyValue("registryIds", StringUtils.join(registryConfigIds, ','));
}
// 处理protocol属性如果存在的话
String[] protocolConfigIds = (String[]) serviceAnnotationAttributes.get("protocol");
if (protocolConfigIds != null && protocolConfigIds.length > 0) {
resolveStringArray(protocolConfigIds);
builder.addPropertyValue("protocolIds", StringUtils.join(protocolConfigIds, ','));
}
// TODO Could we ignore these attributes: applicatin/monitor/module ? Use global config
// 处理monitor属性如果存在的话
String monitorConfigId = (String) serviceAnnotationAttributes.get("monitor");
if (StringUtils.hasText(monitorConfigId)) {
addPropertyReference(builder, "monitor", monitorConfigId);
}
// deprecate application reference
// String applicationConfigId = (String) serviceAnnotationAttributes.get("application");
// if (StringUtils.hasText(applicationConfigId)) {
// addPropertyReference(builder, "application", applicationConfigId);
// }
// 处理module属性如果存在的话
String moduleConfigId = (String) serviceAnnotationAttributes.get("module");
if (StringUtils.hasText(moduleConfigId)) {
addPropertyReference(builder, "module", moduleConfigId);
}
return builder.getBeanDefinition();
}
DubboRelaxedBindingAutoConfiguration
DubboRelaxedBindingAutoConfiguration
会注入dubbo-service-class-base-packages
Bean,用于配置ServiceAnnotationPostProcessor
,其配置条件如下:
// 默认开启,除非指定dubbo.enabled=false
@ConditionalOnProperty(prefix = DUBBO_PREFIX, name = "enabled", matchIfMissing = true)
// 要求类路径存在RelaxedPropertyResolver
@ConditionalOnClass(name = "org.springframework.boot.bind.RelaxedPropertyResolver")
如上所述,本类适配Spring Boot 1.x,而本文基于Spring Boot 2.6.6,故不会触发DubboRelaxedBindingAutoConfiguration
自动配置;DubboRelaxedBinding2AutoConfiguration
适配Spring Boot 2.0及以上;
和DubboRelaxedBindingAutoConfiguration
作用一样,DubboRelaxedBinding2AutoConfiguration
会注入dubbo-service-class-base-packages
Bean,用于配置ServiceAnnotationPostProcessor
,其配置条件如下:
// 默认开启,除非指定dubbo.enabled=false
@ConditionalOnProperty(prefix = DUBBO_PREFIX, name = "enabled", matchIfMissing = true)
// 要求类路径存在Binder
@ConditionalOnClass(name = "org.springframework.boot.context.properties.bind.Binder")
// 优先于DubboRelaxedBindingAutoConfiguration进行自动配置
@AutoConfigureBefore(DubboRelaxedBindingAutoConfiguration.class)
本文基于Spring Boot 2.6.6,故会触发DubboRelaxedBinding2AutoConfiguration
自动配置,其注入Bean代码如下:
public PropertyResolver dubboScanBasePackagesPropertyResolver(ConfigurableEnvironment environment) {
ConfigurableEnvironment propertyResolver = new AbstractEnvironment() {
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
// 查找dubbo.scan相关配置参数
Map<String, Object> dubboScanProperties = getSubProperties(environment.getPropertySources(), DUBBO_SCAN_PREFIX);
propertySources.addLast(new MapPropertySource("dubboScanProperties", dubboScanProperties));
}
};
ConfigurationPropertySources.attach(propertyResolver);
return propertyResolver;
}
// 如果还不存在BASE_PACKAGES_BEAN_NAME就注入BASE_PACKAGES_BEAN_NAME
@ConditionalOnMissingBean(name = BASE_PACKAGES_BEAN_NAME)
@Bean(name = BASE_PACKAGES_BEAN_NAME)
public Set<String> dubboBasePackages(ConfigurableEnvironment environment) {
// 获取dubbo.scan相关配置属性
PropertyResolver propertyResolver = dubboScanBasePackagesPropertyResolver(environment);
// 返回dubbo.scan.base-packages配置参数
// 即注入到Spring容器
return propertyResolver.getProperty(BASE_PACKAGES_PROPERTY_NAME, Set.class, emptySet());
}
// 如果还不存在RELAXED_DUBBO_CONFIG_BINDER_BEAN_NAME就注入RELAXED_DUBBO_CONFIG_BINDER_BEAN_NAME
@ConditionalOnMissingBean(name = RELAXED_DUBBO_CONFIG_BINDER_BEAN_NAME, value = ConfigurationBeanBinder.class)
@Bean(RELAXED_DUBBO_CONFIG_BINDER_BEAN_NAME)
@Scope(scopeName = SCOPE_PROTOTYPE)
public ConfigurationBeanBinder relaxedDubboConfigBinder() {
// 注入BinderDubboConfigBinder到Spring容器
return new BinderDubboConfigBinder();
}
DubboListenerAutoConfiguration
DubboListenerAutoConfiguration
用于注册Dubbo相关的ApplicationListener
Bean如果不存在的话,代码如下:
// 默认开启,除非指定dubbo.enabled=false
@ConditionalOnProperty(prefix = DUBBO_PREFIX, name = "enabled", matchIfMissing = true)
@Configuration
public class DubboListenerAutoConfiguration {
@ConditionalOnMissingBean
@Bean
public DubboConfigBeanDefinitionConflictApplicationListener dubboConfigBeanDefinitionConflictApplicationListener() {
// 注入DubboConfigBeanDefinitionConflictApplicationListener
return new DubboConfigBeanDefinitionConflictApplicationListener();
}
@ConditionalOnMissingBean
@Bean
public AwaitingNonWebApplicationListener awaitingNonWebApplicationListener() {
// 注入AwaitingNonWebApplicationListener
return new AwaitingNonWebApplicationListener();
}
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)