用注解的方式启动Spring容器
ProviderConfiguration是自定义的启动类。
public class AnnotationProviderMain {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ProviderConfiguration.class);
context.start();
try {
System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
}
}
源码分析
在我的启动类AnnotationProviderMain中调用了AnnotationConfigApplicationContext(Class... annotatedClasses)构造方法,在这个构造方法里面,又调用了AnnotationConfigApplicationContext无参构造方法。无参构造方法中实例化了AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner。
public AnnotationConfigApplicationContext(Class... annotatedClasses) {
this();
this.register(annotatedClasses);
this.refresh();
}
public AnnotationConfigApplicationContext() {
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
public void register(Class... annotatedClasses) {
Assert.notEmpty(annotatedClasses, "At least one annotated class must be specified");
this.reader.register(annotatedClasses);
}
调用了AnnotatedBeanDefinitionReader的registerBean方法
public void register(Class... annotatedClasses) {
Class[] var2 = annotatedClasses;
int var3 = annotatedClasses.length;
for(int var4 = 0; var4 < var3; ++var4) {
Class<?> annotatedClass = var2[var4];
this.registerBean(annotatedClass);
}
}
public void registerBean(Class<?> annotatedClass) {
this.registerBean(annotatedClass, (String)null, (Class[])null);
}
public void registerBean(Class<?> annotatedClass, String name, Class... qualifiers) {
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
if (!this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
abd.setScope(scopeMetadata.getScopeName());
String beanName = name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry);
AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
if (qualifiers != null) {
Class[] var7 = qualifiers;
int var8 = qualifiers.length;
for(int var9 = 0; var9 < var8; ++var9) {
Class<? extends Annotation> qualifier = var7[var9];
if (Primary.class == qualifier) {
abd.setPrimary(true);
} else if (Lazy.class == qualifier) {
abd.setLazyInit(true);
} else {
abd.addQualifier(new AutowireCandidateQualifier(qualifier));
}
}
}
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}
}
来看看BeanDefinitionReaderUtils的registerBeanDefinition方法。通过(beanName, bean)和(beanName, alias别名)的方式注册bean。
public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException {
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
String[] var4 = aliases;
int var5 = aliases.length;
for(int var6 = 0; var6 < var5; ++var6) {
String alias = var4[var6];
registry.registerAlias(beanName, alias);
}
}
}
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
this(registry, getOrCreateEnvironment(registry));
}
private static Environment getOrCreateEnvironment(BeanDefinitionRegistry registry) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
return (Environment)(registry instanceof EnvironmentCapable ? ((EnvironmentCapable)registry).getEnvironment() : new StandardEnvironment());
}
使用注解向容器注册类
@Configuration
@EnableDubbo(scanBasePackages = "gdut.ff.provider")
public class ProviderConfiguration {
//提供者配置
@Bean
public ProviderConfig providerConfig() {
return new ProviderConfig();
}
//应用信息配置
@Bean
public ApplicationConfig applicationConfig() {
ApplicationConfig applicationConfig = new ApplicationConfig();
applicationConfig.setName("annotation-provider");
return applicationConfig;
}
//注册中心配置
@Bean
public RegistryConfig registryConfig() {
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setProtocol("zookeeper");
registryConfig.setAddress("127.0.0.1");
registryConfig.setPort(2181);
return registryConfig;
}
//协议配置
@Bean
public ProtocolConfig protocolConfig() {
ProtocolConfig protocolConfig = new ProtocolConfig();
protocolConfig.setName("dubbo");
protocolConfig.setPort(20880);
return protocolConfig;
}
}
自定义的ProviderConfiguration使用了两个注解,一个是Configuration,另一个是EnableDubbo。
@Configuration标注在类上,相当于把该类作为spring的xml配置文件中的<beans>,作用为:配置spring容器(应用上下文)
@Bean标注在方法上(返回某个实例的方法),等价于spring的xml配置文件中的<bean>,作用为:注册bean对象
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
String value() default "";
}
/**
* 能使Dubbo components像Spring Beans一样,是DubboComponentScan和EnableDubboConfig的结合
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@EnableDubboConfig
@DubboComponentScan
public @interface EnableDubbo {
/**
* 定义扫描注解@Service的包
* 使用类型安全的scanBasePackageClasses()替代基于字符串的包名称
* @return 扫描的包
*/
@AliasFor(annotation = DubboComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
/**
*scanBasePackages()的类型安全替代方案,用于指定要扫描带注释的@Service类的程序包。 指定类别的包将被扫描。
*return 从包中扫描的类
*/
@AliasFor(annotation = DubboComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default {};
/**
* 它指示AbstractConfig是否绑定到多个Spring Bean。
* return 返回默认值false
*/
@AliasFor(annotation = EnableDubboConfig.class, attribute = "multiple")
boolean multipleConfig() default false;
}
@EnableDubbo使用了两个注解@EnableDubboConfig和@DubboComponentScan
ConfugurationClassParser的processImports方法调用了DubboConfigConfigurationSelector.selectImports()方法
@EnableDubboConfig根据multiple返回的布尔值,来决定绑定的bean的属性前缀。若为true,表示多个Bean绑定的属性前缀,如果是false,表示单个Bean绑定的属性前缀。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Import(DubboConfigConfigurationSelector.class)
public @interface EnableDubboConfig {
/**
* 是否绑定多个Bean
*/
boolean multiple() default false;
}
Beanmultiple的值 | false | true |
---|---|---|
ApplicationConfig | dubbo.application | dubbo.applications |
ModuleConfig | dubbo.module | dubbo.modules |
RegistryConfig | dubbo.registry | dubbo.registries |
ProtocolConfig | dubbo.protocol | dubbo.protocols |
MonitorConfig | dubbo.monitor | dubbo.monitors |
ProviderConfig | dubbo.provider | dubbo.providers |
ConsumerConfig | dubbo.consumer | dubbo.consumers |
@EnableDubboConfig还使用了@Import(DubboConfigConfigurationSelector.class)注解
来看看DubboConfigConfigurationSelector类的
selectImports方法。获取EnableDubboConfig的属性multiple的值,
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
AnnotationAttributes attributes = AnnotationAttributes.fromMap(
importingClassMetadata.getAnnotationAttributes(EnableDubboConfig.class.getName()));
boolean multiple = attributes.getBoolean("multiple");
if (multiple) {
return of(DubboConfigConfiguration.Multiple.class.getName());
} else {
return of(DubboConfigConfiguration.Single.class.getName());
}
}
来看看DubboConfigConfiguration的实现
public class DubboConfigConfiguration {
/**
* Single Dubbo {@link AbstractConfig Config} Bean Binding
*/
@EnableDubboConfigBindings({
@EnableDubboConfigBinding(prefix = "dubbo.application", type = ApplicationConfig.class),
@EnableDubboConfigBinding(prefix = "dubbo.module", type = ModuleConfig.class),
@EnableDubboConfigBinding(prefix = "dubbo.registry", type = RegistryConfig.class),
@EnableDubboConfigBinding(prefix = "dubbo.protocol", type = ProtocolConfig.class),
@EnableDubboConfigBinding(prefix = "dubbo.monitor", type = MonitorConfig.class),
@EnableDubboConfigBinding(prefix = "dubbo.provider", type = ProviderConfig.class),
@EnableDubboConfigBinding(prefix = "dubbo.consumer", type = ConsumerConfig.class)
})
public static class Single {
}
/**
* Multiple Dubbo {@link AbstractConfig Config} Bean Binding
*/
@EnableDubboConfigBindings({
@EnableDubboConfigBinding(prefix = "dubbo.applications", type = ApplicationConfig.class, multiple = true),
@EnableDubboConfigBinding(prefix = "dubbo.modules", type = ModuleConfig.class, multiple = true),
@EnableDubboConfigBinding(prefix = "dubbo.registries", type = RegistryConfig.class, multiple = true),
@EnableDubboConfigBinding(prefix = "dubbo.protocols", type = ProtocolConfig.class, multiple = true),
@EnableDubboConfigBinding(prefix = "dubbo.monitors", type = MonitorConfig.class, multiple = true),
@EnableDubboConfigBinding(prefix = "dubbo.providers", type = ProviderConfig.class, multiple = true),
@EnableDubboConfigBinding(prefix = "dubbo.consumers", type = ConsumerConfig.class, multiple = true)
})
public static class Multiple {
}
}
再来看看@EnableDubboConfigBindings的实现
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(DubboConfigBindingsRegistrar.class)
public @interface EnableDubboConfigBindings {
/**
* The value of {@link EnableDubboConfigBindings}
*
* @return non-null
*/
EnableDubboConfigBinding[] value();
}
调用了DubboConfigBindingsRegistrar。
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotationAttributes attributes = AnnotationAttributes.fromMap(
importingClassMetadata.getAnnotationAttributes(EnableDubboConfigBindings.class.getName()));
//获取EnableDubboConfigBindings注解全部的value值。
AnnotationAttributes[] annotationAttributes = attributes.getAnnotationArray("value");
DubboConfigBindingRegistrar registrar = new DubboConfigBindingRegistrar();
registrar.setEnvironment(environment);
for (AnnotationAttributes element : annotationAttributes) {
//注册到Spring容器。
registrar.registerBeanDefinitions(element, registry);
}
}
@DubboComponentScan调用了DubboComponentScanRegistrar注解,会生成ServiceAnnotationBeanPostProcessor和ReferenceAnnotationBeanPostProcessor
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);
registerServiceAnnotationBeanPostProcessor(packagesToScan, registry);
registerReferenceAnnotationBeanPostProcessor(registry);
}
参考资料
《深入理解Apache Dubbo与实战》
https://blog.csdn.net/u014199...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。