前提
有接口Lock和它的实现类RedisLock和DbLock,在spring注入时如何指定具体的实现呢?
Lock
接口定义在lock.jar
public interface Lock {
void lock();
void unlock();
}
RedisLock
该实现类在redis-lock.jar
中
public class RedisLock implements Lock {
@Override
public void lock() {
System.out.println("redis.lock()");
}
@Override
public void unlock() {
System.out.println("redis.unlock()");
}
}
DbLock
该实现类在db-lock.jar
中
public class DbLock implements Lock {
@Override
public void lock() {
System.out.println("数据库lock()");
}
@Override
public void unlock() {
System.out.println("数据库unlock()");
}
}
UserService
service定义在我们的应用app
中,此时我们并不知道Lock的具体类型。
@Component
public class UserService implements InitializingBean {
@Autowired
private Lock lock;
@Override
public void afterPropertiesSet() throws Exception {
lock.lock();
lock.unlock();
}
}
如何指定Lock的具体实现类型呢? 如果没有注入,上述的UserService
会抛出NPE
异常;
方法1
通常我们会这样使用,在我们的应用app中添加如下configuration类:
@Configuration
public class MyLockConfiguration {
@Bean
public Lock lock(){
return new RedisLock();
}
}
方法2
RedisLockConfiguration
redis-lock.jar
中添加RedisLockConfiguration类:
//注意,此处没有@Configuration注解
public class RedisLockConfiguration {
@Bean
@ConditionalOnMissingBean(name = "lock")
//方法名即为beanName
public Lock lock(){
return new RedisLock();
}
}
@ConditionalOnMissingBean(name = "lock")
如果spring容器中已有同名bean,则不再注入;
DbLockConfiguration
db-lock.jar
中添加DbLockConfiguration类:
public class DbLockConfiguration {
@Bean
@ConditionalOnMissingBean(name = "lock")
public Lock lock(){
return new DbLock();
}
}
在我们的应用中添加如下configuration,通过它来@import配置类
:
@Configuration
@Import({RedisLockConfiguration.class}) //指定redisConfiguration
public class MyLockConfiguration {
}
方法3
对DbLockConfiguration
和RedisLockConfiguration
做改造,根据@ConditionalOnProperty
注解+配置文件lock.type: XXX
来使用:
RedisLockConfiguration
redis-lock.jar
中RedisLockConfiguration
做改造:
@Configuration
@ConditionalOnProperty(prefix = "lock" ,name = "type",havingValue = "redis")
public class RedisLockConfiguration {
@Bean
@ConditionalOnMissingBean(name = "lock")
public Lock lock(){
return new RedisLock();
}
}
DbLockConfiguration
db-lock.jar
中添加DbLockConfiguration做改造,如下:
@Configuration
@ConditionalOnProperty(prefix = "lock" ,name = "type",havingValue = "db")
public class DbLockConfiguration {
@Bean
@ConditionalOnMissingBean(name = "lock")
public Lock lock(){
return new DbLock();
}
}
最后在应用app中的配置文件中添加如下:
方法4: 使用@Enable*
在方法2的基础上,增加@EnableDbLock
,@EnableRedisLock
@EnableRedisLock
redis-lock.jar
中添加@EnableRedisLock
注解类:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(RedisLockConfiguration.class)
public @interface EnableRedisLock {
}
@EnableDbLock
db-lock.jar
中添加@EnableDbLock
注解类:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(DbLockConfiguration.class)
public @interface EnableDbLock {
}
最后在app应用的启动类(任意bean都可以)上添加@Enable**
注解:
@SpringBootApplication
@EnableDbLock
public class GecServiceBootstrap extends SpringBootServletInitializ{
}
@Enable
的作用其实就是为了@Import配置类
5. ImportSelector+EnableLock
**LockType **
在lock.jar
中定义好所有的支持类型:
public enum LockType {
REDIS,DB;
}
EnableLock
在lock.jar
中定义好EnableLock
:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(LockSelector.class)
public @interface EnableLock {
//支持DB,REDIS两种类型
LockType lockType() default LockType.DB;
}
LockSelector
在lock.jar
中定义好LockSelector
:
public class LockSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
Class<EnableLock> annoType = EnableLock.class;
Map<String, Object> annotationAttributes = importingClassMetadata
.getAnnotationAttributes(annoType.getName(), false);
AnnotationAttributes attributes = AnnotationAttributes
.fromMap(annotationAttributes);
LockType lockType = attributes.getEnum("lockType");
String[] arr = new String[1];
switch (lockType){
case DB:
arr[0] = DbLock.class.getName();
break;
case REDIS:
arr[0] = RedisLock.class.getName();
break;
default:
}
return arr;
}
}
用此种方式,不支持对
客户端
进行扩展;
所有的实现方式都需要在core.jar
中提前定义好。
多知道一点 @Import的加载过程
我们都知道SpringBoot的启动入口都为如下形式:
@SpringBootApplication
public class App extends SpringBootServletInitializer implements WebApplicationInitializer {
public static void main(String... args) {
SpringApplication.run(App .class, args);
}
}
SpringApplication
public ConfigurableApplicationContext run(String... args) {
ConfigurableApplicationContext context = createApplicationContext();
//这里内部调用了:postProcessApplicationContext(context);
prepareContext(context, environment, listeners, applicationArguments,printedBanner);
//refresh()
refreshContext(context);
}
AbstractApplicationContext处理流程
//AbstractApplicationContext
public void refresh(){
prepareRefresh();
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
prepareBeanFactory(beanFactory);
//空实现---subclass实现
postProcessBeanFactory(beanFactory);
//执行BeanFactoryPostProcessors ---工厂级别 ----入口
invokeBeanFactoryPostProcessors(beanFactory);
//注册beanPostProcesspr,后续的getBean("your_bean")生成bean实例时,会遍历执行BeanPostProcessors中;
registerBeanPostProcessors(beanFactory);
//略.....
}
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
//略...
}
PostProcessorRegistrationDelegate
//PostProcessorRegistrationDelegate
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
//默认情况下只配置了一个:"org.springframework.context.annotation.internalConfigurationAnnotationProcessor"
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
//for-each执行: postProcessor.postProcessBeanDefinitionRegistry(registry);
//currentRegistryProcessors只有一条记录:ConfigurationClassPostProcessor
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
}
ConfigurationClassPostProcessor
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
//内部调用:new ConfigurationClassParser().parse(candidates);
//candidates 为我们springBoot的启动类; "App.class"
processConfigBeanDefinitions(registry);
}
ConfigurationClassParser
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
//代码略....
// Recursively process any member (nested) classes first
// Process any @PropertySource annotations
// Process any @ComponentScan annotations
//处理@Import
// Process any @Import annotations
// Process any @ImportResource annotations
// Process individual @Bean methods
// Process default methods on interfaces
// Process superclass, if any
// No superclass -> processing is complete
return null;
}
processImports
针对Import
的class
不同类型,分别做不同处理;
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
for (SourceClass candidate : importCandidates) {
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
//处理ImportSelector略...
}
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
//处理ImportBeanDefinitionRegistrar
}
else {
// 当作一个正常的@Configuration class处理.
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
processConfigurationClass(candidate.asConfigClass(configClass));
}
}
}