springboot @Enable*,@Import使用

本文介绍在Spring框架中如何选择性地注入Lock接口的不同实现类,包括RedisLock和DbLock,并探讨了五种不同的配置方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前提

有接口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

DbLockConfigurationRedisLockConfiguration做改造,根据@ConditionalOnProperty注解+配置文件lock.type: XXX来使用:
RedisLockConfiguration
redis-lock.jarRedisLockConfiguration做改造:

@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
针对Importclass不同类型,分别做不同处理;

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));
			}
		}
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值