layout: ‘[draft]’
title: Spring-Boot相关及Dubbo集成
tags:
- Spring-Boot
- Dubbo
comments: true
toc: true
categories:
- 编程
date: 2017-04-16 11:45:23
We talk about Spring Boot integration with Dubbo this time.
Spring Boot Hello World
一个Spirng Boot的Hello World:
1
2
3
4
5
6
|
@SpringBootApplication
public class SpringBootSimpleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootSimpleApplication.class, args);
}
}
|
基础注解:@SpringBootApplication
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
@AliasFor(annotation = EnableAutoConfiguration.class, attribute = "exclude")
Class<?>[] exclude() default {};
/**
* Exclude specific auto-configuration class names such that they will never be
* applied.
* @return the class names to exclude
* @since 1.3.0
*/
@AliasFor(annotation = EnableAutoConfiguration.class, attribute = "excludeName")
String[] excludeName() default {};
/**
* Base packages to scan for annotated components. Use {@link #scanBasePackageClasses}
* for a type-safe alternative to String-based package names.
* @return base packages to scan
* @since 1.3.0
*/
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
/**
* Type-safe alternative to {@link #scanBasePackages} for specifying the packages to
* scan for annotated components. The package of each class specified will be scanned.
* <p>
* Consider creating a special no-op marker class or interface in each package that
* serves no purpose other than being referenced by this attribute.
* @return base packages to scan
* @since 1.3.0
*/
@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default {};
}
|
这个一个组合注解,包含了@SpringBootConfiguration
、@EnableAutoConfiguration
和@ComponentScan
。
关键是@EnableAutoConfiguration
这个注解负责自动配置,依次检查类路径,注解,配置文件来为你创建所需的配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
public class EnableAutoConfigurationImportSelector
extends AutoConfigurationImportSelector {
@Override
protected boolean isEnabled(AnnotationMetadata metadata) {
if (getClass().equals(EnableAutoConfigurationImportSelector.class)) {
return getEnvironment().getProperty(
EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class,
true);
}
return true;
}
}
|
1.5之后,主要功能迁移到了AutoConfigurationImportSelector
中,其中最为关键的是getCandidateConfiguration
方法:
1
2
3
4
5
6
7
8
9
|
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
Assert.notEmpty(configurations,
"No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
|
在这个方法中,使用了SpringFactoriesLoader.loadFactoryNames
方法来加载候选配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
try {
Enumeration<URL> urls = classLoader != null?classLoader.getResources("META-INF/spring.factories"):ClassLoader.getSystemResources("META-INF/spring.factories");
ArrayList result = new ArrayList();
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
String factoryClassNames = properties.getProperty(factoryClassName);
result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
}
return result;
} catch (IOException var8) {
throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + "META-INF/spring.factories" + "]", var8);
}
}
|
这个方法从类路径或者定义的资源路径下的META-INF/spring.factories
文件中加载候选配置。
org.springframework.boot.autoconfigure包下的spring.factories
如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener
# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnClassCondition
# Auto Configure
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\
...
|
spring.factories
文件定义了所有用于推断当前运行的应用程序的类型的逻辑和对应各个程序类型的自动配置方法。
以CloudAutoConfiguration
为例:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
@Configuration
@Profile("cloud")
@AutoConfigureOrder(CloudAutoConfiguration.ORDER)
@ConditionalOnClass(CloudScanConfiguration.class)
@ConditionalOnMissingBean(Cloud.class)
@ConditionalOnProperty(prefix = "spring.cloud", name = "enabled", havingValue = "true", matchIfMissing = true)
@Import(CloudScanConfiguration.class)
public class CloudAutoConfiguration {
// Cloud configuration needs to happen early (before data, mongo etc.)
public static final int ORDER = Ordered.HIGHEST_PRECEDENCE + 20;
}
|
这个配置函数会将当前应用配置为一个cloud应用,判断依据是@ConditionalOnClass
、@ConditionalOnMissingBean
和@ConditionalOnProperty
:类路径中存在CloudScanConfiguration
、容器当前没有Cloud
类的Bean实例而且配置参数中spring.cloud
值为true
或不存在这个配置项,即默认匹配。
@Profile
表示加载cloud
配置,@Import
表示如果上述条件满足,则加载CloudScanConfiguration
。
看完了注解 ,再来看看main函数。
SpringApplication
这个类起到了引导(bootstrap)的作用。
除了最基本的run
方法,还可以通过SpringApplication
提供的更多方法对应用进行定制。
Spring Boot Starter
可以把Starter理解为一个完整功能的自动配置。例如通过引入spring-boot-starter-jdbc,我们就可以直接通过@Autowired引入DataSource的bean,不需要再手动创建DataSource的相关实例。
Custom Starter
在上面我们可以看到,Spring Boot通过扫描spring.factories
文件来加载自动配置。
所以我们只需自定义一个CustomAutoConfiguration
,并添加到spring.factories
文件中,即可被Spring Boot自动加载。
如果不想污染spring.factories
文件,也可以通过@Import
注解手动导入CustomAutoConfiguration.class
。
Spring Boot Starter Dubbo
通过创建一个Dubbo的starter,我们可以快速的给应用添加dubbo支持。
开源社区中主要有以下相关项目提供了dubbo的starter:
teaey的项目比较中规中矩,而卷爷的项目添加了很多好的想法,但是只支持他开发的dubbo 3,为了兼容现有的dubbo 2.x 版本,我们可以综合一下这两个项目。
Autowired vs Reference
@Reference
是Dubbo提供的用于标示dubbo服务的注解,而@Autowired
是Spring原生支持的依赖注入的注解,为了减少侵入性,显然是后者更胜。
卷爷的项目中,巧妙的通过Provider额外提供的一个Starter,使得dubbo的Consumer可以直接使用@Autowired
引入Dubbo服务。
1
2
3
4
|
@Bean
public ReferenceBean<UicTemplate> uicTemplate() {
return getConsumerBean(UicTemplate.class, properties.getVersion(), properties.getTimeout());
}
|
所以我们可以给teaey的项目引入卷爷项目中的DubboBasedAutoConfiguration
来获得getConsumerBean()
方法的支持。
Spring Boot With Dubbo Project
Before Spring Boot:
After Spring Boot:
一个提供Dubbo服务的Spring Boot项目应该包含以下模块:
- Dubbo服务的接口API,如图中的
infiniti-api
- Dubbo服务的实现模块(可以是Web服务,也可以不是),如图中的
spring-boot-infiniti-web
- Dubbo服务的Reference封装模块,如图中的
spring-boot-starter-infiniti
- (可选)Dubbo服务的Consumer示例,如图中的
spring-boot-infiniti-demo-client
一个消费Dubbo服务的项目应该引入以下依赖:
- Dubbo Starter
- Dubbo服务的接口API
- 对应Dubbo服务的Reference封装模块
Other
因为一些意外的关系,博客停更了3个月,后面会继续保持更新,祝好。