关于 Spring 配置类的 Configurer 模式

  • 前言
  • demo
  • SampleComponent
  • SampleComponentConfigurer
  • AbstractSampleComponentConfiguration
  • SampleComponentConfiguration
  • TestDemo
  • 小结
  • Spring Async
  • ProxyAsyncConfiguration
  • AbstractAsyncConfiguration
  • AsyncConfigurer
  • demo
  • Spring Cache
  • ProxyCachingConfiguration
  • AbstractCachingConfiguration
  • CachingConfigurer
  • demo
  • 总结


前言

Spring 作为拓展性做到极致的工程,对核心功能组件的配置都支持用户的自定义拓展,其中一种常见的模式就是 Configurer 回调配置,比如 AsyncConfigurer CacheConfigurer 等类

本文从一个简单的模拟示例入手,再借助 Spring 的应用协助理解一下这种模式

demo

SampleComponent

public class SampleComponent {

    private String name;
    private Integer age;

    // 支持自定义配置
    public void configurer(Supplier<String> name, Supplier<Integer> age) {
        this.name = name.get();
        this.age = age.get();
    }
    
    // getter & setter ...
    
}
  • 模拟一个核心组件
  • configurer 方法支持自定义组件属性

SampleComponentConfigurer

public interface SampleComponentConfigurer {

    default String getName() {

        return "default";
    }

    default Integer getAge() {

        return 18;
    }

}
  • 这是针对 SampleComponentConfigurer 回调配置接口
  • 它可以通过覆盖方法的形式来自定义 SampleComponent 组件的属性

AbstractSampleComponentConfiguration

@Configuration
public abstract class AbstractSampleComponentConfiguration {

    Supplier<String> name;
    Supplier<Integer> age;

    /**
     * 基于自定义提供的 SampleComponentConfigurer 来配置对应属性
     */
    @Autowired
    public void setConfigurer(ObjectProvider<SampleComponentConfigurer> configurers) {

        List<SampleComponentConfigurer> configurerList =
                configurers.stream().collect(Collectors.toList());

        Supplier<SampleComponentConfigurer> supplier = () -> {
            if (CollectionUtils.isEmpty(configurerList)) {
                return null;
            }
            if (configurerList.size() > 1) {
                throw new RuntimeException("只允许指定一个 SampleComponentConfigurer");
            }
            return configurerList.get(0);
        };

        name = adapt(supplier, SampleComponentConfigurer::getName);
        age = adapt(supplier, SampleComponentConfigurer::getAge);
    }

    // Supplier<SampleComponentConfigurer> 到 Supplier<T> 的映射,T 即属性类型
    private <T> Supplier<T> adapt(Supplier<SampleComponentConfigurer> supplier
            , Function<SampleComponentConfigurer, T> function) {

        return () -> {
            SampleComponentConfigurer sampleComponentConfigurer = supplier.get();
            return sampleComponentConfigurer == null ? null : function.apply(sampleComponentConfigurer);
        };
    }

}
  • 这种自定义回调配置行为通常由基类定义
  • 它收集用户提供的 SampleComponentConfigurer 配置类(通常只允许一个),然后基于此获取指定的对应属性
  • adapt 方法从 SampleComponentConfigurer 中提取对应的属性
  • 知识点:
  • @Autowired 注解的方法在属性后处理阶段执行
  • ObjectProvider 类型的返回值会包装对应的所有 bean 组件

SampleComponentConfiguration

@Configuration
public class SampleComponentConfiguration extends AbstractSampleComponentConfiguration {

    @Bean
    public SampleComponent sampleComponent() {
        SampleComponent sampleComponent = new SampleComponent();

        // 基于 SampleComponentConfigurer 进行配置
        sampleComponent.configurer(name, age);
        return sampleComponent;
    }

}
  • 模拟针对功能组件的配置,这种配置类一般都是框架提供的,基于 Import 等机制引入
  • 因此我们可以在不修改配置类的情况下提供自己的 SampleComponentConfigurer 来配置组件

TestDemo

public class TestDemo {

    @Configuration
    @Import({ SampleComponentConfiguration.class })
    public static class Config implements SampleComponentConfigurer {

        // 自定义组件属性
        @Override
        public String getName() {
            return "dd";
        }
    }

    @Test
    public void test() {
        AnnotationConfigApplicationContext applicationContext
                = new AnnotationConfigApplicationContext(Config.class);
        SampleComponent bean = applicationContext.getBean(SampleComponent.class);
        System.out.println(bean.getName());
        System.out.println(bean.getAge());
    }
}
  • 这里模拟应用场景,@Import({ SampleComponentConfiguration.class }) 功能一般以类似 @EnableXXX 的模式提供
  • 我们提供的配置类 Config 实现 SampleComponentConfigurer 接口以支持组件属性的自定义配置
  • 示例中,我们覆盖了 SampleComponentname 属性

小结

可以看到,在真正的应用场景中(TestDemo),我们只需要提供一个 SampleComponentConfigurer 就可以自定义组件的属性(而不是自己注册一个组件来覆盖)

这符合 对修改关闭,对拓展开方 的原则

Spring Async

ProxyAsyncConfiguration

@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration {

	@Bean(name = TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME)
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public AsyncAnnotationBeanPostProcessor asyncAdvisor() {
		
		// 注册一个 AsyncAnnotationBeanPostProcessor
		AsyncAnnotationBeanPostProcessor bpp = new AsyncAnnotationBeanPostProcessor();

		// 基于 AsyncConfigurer 自定义配置属性
		bpp.configure(this.executor, this.exceptionHandler);
		
		// ...
		
		return bpp;
	}

}
  • 该类由 @EnableAsync 引入
  • 可以看到它在注册 AsyncAnnotationBeanPostProcessor 时会基于 configure 方法进行自定义配置
  • 此处的 AsyncAnnotationBeanPostProcessor 就相当于之前示例中的 SampleComonent
  • ProxyAsyncConfiguration 类就相当于之前示例中的 SampleComponentConfiguration

AbstractAsyncConfiguration

@Configuration(proxyBeanMethods = false)
public abstract class AbstractAsyncConfiguration implements ImportAware {

	@Nullable
	protected Supplier<Executor> executor;

	@Nullable
	protected Supplier<AsyncUncaughtExceptionHandler> exceptionHandler;

	// ...

	// 获取容器中的 AsyncConfigurer
	@Autowired
	void setConfigurers(ObjectProvider<AsyncConfigurer> configurers) {
		Supplier<AsyncConfigurer> configurer = SingletonSupplier.of(() -> {
			List<AsyncConfigurer> candidates = configurers.stream().collect(Collectors.toList());
			if (CollectionUtils.isEmpty(candidates)) {
				return null;
			}
			if (candidates.size() > 1) {
				throw new IllegalStateException("Only one AsyncConfigurer may exist");
			}
			return candidates.get(0);
		});

		// 如果有且仅有一个 AsyncConfigurer 实例,则从中解析 executor 和 exceptionHandler
		this.executor = adapt(configurer, AsyncConfigurer::getAsyncExecutor);
		this.exceptionHandler = adapt(configurer, AsyncConfigurer::getAsyncUncaughtExceptionHandler);
	}

	private <T> Supplier<T> adapt(Supplier<AsyncConfigurer> supplier, Function<AsyncConfigurer, T> provider) {
		return () -> {
			AsyncConfigurer configurer = supplier.get();
			return (configurer != null ? provider.apply(configurer) : null);
		};
	}

}
  • 它就是 ProxyAsyncConfiguration 的配置基类,相当于之前示例中的 AbstractSampleComponentConfiguration
  • 具体的细节跟示例如出一辙,就是支持基于 AsyncConfigurer 的自定义配置属性

AsyncConfigurer

public interface AsyncConfigurer {

	/**
	 * 自定义 Spring Async 任务执行的线程池
	 */
	@Nullable
	default Executor getAsyncExecutor() {
		return null;
	}

	/**
	 * 自定义配置异常处理器
	 */
	@Nullable
	default AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
		return null;
	}

}
  • 自定义配置回调处理类,相当于之前示例中的 SampleComponentConfigurer
  • 此处支持 AsyncAnnotationBeanPostProcessor 的属性自定义配置

demo

@EnableAsync
@Configuration
public class AsyncConfig implements AsyncConfigurer {

    @Override
    public Executor getAsyncExecutor() {

        return new ThreadPoolExecutor(
                2
                , 4
                , 10
                , TimeUnit.SECONDS
                , new LinkedBlockingQueue<>()
        );
    }
}
  • Spring Async 的配置类
  • 实现 AsyncConfigurer 接口覆盖 getAsyncExecutor 方法为异步任务指定默认的 Executor

Spring Cache

ProxyCachingConfiguration

@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyCachingConfiguration extends AbstractCachingConfiguration {

	// ...

	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public CacheInterceptor cacheInterceptor(CacheOperationSource cacheOperationSource) {
		CacheInterceptor interceptor = new CacheInterceptor();
		/**
		 * 基于 CacheConfigurer 自定义配置属性
		 */
		interceptor.configure(this.errorHandler, this.keyGenerator, this.cacheResolver, this.cacheManager);
		interceptor.setCacheOperationSource(cacheOperationSource);
		return interceptor;
	}

}

如出一辙,支持 CacheInterceptor 的自定义配置

AbstractCachingConfiguration

@Configuration(proxyBeanMethods = false)
public abstract class AbstractCachingConfiguration implements ImportAware {

	@Nullable
	protected Supplier<CacheManager> cacheManager;

	@Nullable
	protected Supplier<CacheResolver> cacheResolver;

	@Nullable
	protected Supplier<KeyGenerator> keyGenerator;

	@Nullable
	protected Supplier<CacheErrorHandler> errorHandler;

	// ...
	
	@Autowired
	void setConfigurers(ObjectProvider<CachingConfigurer> configurers) {

		// 收集容器中用户定义的所有 CachingConfigurer
		Supplier<CachingConfigurer> configurer = () -> {
			List<CachingConfigurer> candidates = configurers.stream().collect(Collectors.toList());

			// 没有就算了
			if (CollectionUtils.isEmpty(candidates)) {
				return null;
			}
			// 只允许有一个
			if (candidates.size() > 1) {
				throw new IllegalStateException("...");
			}
			return candidates.get(0);
		};

		// 基于提供的 CachingConfigurer 对属性进行配置
		useCachingConfigurer(new CachingConfigurerSupplier(configurer));
	}

	protected void useCachingConfigurer(CachingConfigurerSupplier cachingConfigurerSupplier) {
		this.cacheManager = cachingConfigurerSupplier.adapt(CachingConfigurer::cacheManager);
		this.cacheResolver = cachingConfigurerSupplier.adapt(CachingConfigurer::cacheResolver);
		this.keyGenerator = cachingConfigurerSupplier.adapt(CachingConfigurer::keyGenerator);
		this.errorHandler = cachingConfigurerSupplier.adapt(CachingConfigurer::errorHandler);
	}

	// 内部类协助 CachingConfigurer 的处理
	protected static class CachingConfigurerSupplier {

		private final Supplier<CachingConfigurer> supplier;

		public CachingConfigurerSupplier(Supplier<CachingConfigurer> supplier) {
			this.supplier = SingletonSupplier.of(supplier);
		}

		// CachingConfigurer 到属性的映射
		@Nullable
		public <T> Supplier<T> adapt(Function<CachingConfigurer, T> provider) {
			return () -> {
				CachingConfigurer cachingConfigurer = this.supplier.get();
				return (cachingConfigurer != null ? provider.apply(cachingConfigurer) : null);
			};
		}

	}

}
  • 配置基类,提供基于 CacheConfigurer 配置的方法
  • 实现细节稍有不同,但逻辑一样

CachingConfigurer

public interface CachingConfigurer {

	/**
	 * 管理对应的 Cache 集合
	 */
	@Nullable
	default CacheManager cacheManager() {
		return null;
	}

	/**
	 * 用来解析对应的 Cache,通常基于 CacheManager  
	 */
	@Nullable
	default CacheResolver cacheResolver() {
		return null;
	}

	/**
	 * 用来生成缓存 key
	 */
	@Nullable
	default KeyGenerator keyGenerator() {
		return null;
	}

	/**
	 * 缓存异常处理类
	 */
	@Nullable
	default CacheErrorHandler errorHandler() {
		return null;
	}

}
  • 自定义回调配置类
  • 支持 Spring Cache 的组件 CacheInterceptorcacheManager cacheResolver keyGenerator errorHandler 属性的配置
  • 上述属性的优先级低于缓存注解上的指定,可以理解为缺省属性

demo

@Configuration
@EnableCaching
public class CacheConfig implements CachingConfigurer {

    // 注册 ConcurrentMapCacheManager
    @Bean
    @Override
    public CacheManager cacheManager() {
        ConcurrentMapCacheManager cacheManager
                = new ConcurrentMapCacheManager();
        return cacheManager;
    }

}
  • Spring Cache 的配置类
  • 指定了缺省的 CacheManager
  • 注册为 bean 组件是为了让 Spring 管理 CacheManager 的生命周期

总结

优秀的 自定义回调处理配置 案例,示例 demo 结合 Spring 的应用可以更加深入的了解,希望对开发工作有帮助