- Configurer保存配置信息
- Builder将Configurer串起来,一个Builder可能保存有很多的Configurer。多个类型的Configurer,同一个类型的多个Configurer。参看:AbstractConfiguredSecurityBuilder。
- Configuration使用Builder的信息,将Builder中保存的信息转化成各种Bean注入到IOC容器中。例子:WebSecurityConfiguration使用WebSecurity(一个Builder)中的信息,构建了各种相关的Bean。
这样就对了,使用Builder将Configurer串起来,然后一起传给Configuration用户构造Bean注入IOC容器中。
一般会建立一个xxxConfigurerAdapter用于将多个Builder串起来。这就是之前一直混乱的原因。
问题1:
如何将Builder中的信息传给Configuration?
答案:
Builder和其下属的Configurer是如何互相传递信息的,
Builder会持有其所有下属的Configurer的引用,所以Builder可以主动与Configurer通信。
那么Configurer是怎么获取Builder对象的信息的呢,我们看一下Configurer的定义:
public interface WeiXinConfigurer<O, B extends WeiXinBuilder<O>> {
/**
* Initialize the {@link WeiXinBuilder}. Here only shared state should be
* created and modified, but not properties on the {@link WeiXinBuilder}
* used for building the object. This ensures that the
* {@link #configure(WeiXinBuilder)} method uses the correct shared objects
* when building.
*
* @param builder
* @throws Exception
*/
void init(B builder) throws Exception;
/**
* Configure the {@link WeiXinBuilder} by setting the necessary properties
* on the {@link WeiXinBuilder}.
*
* @param builder
* @throws Exception
*/
void configure(B builder) throws Exception;
}
可以看到,Configurer接口的两个方法的参数都是Builder,而这init和configure这两个方法都是持有此Configurer对象的引用的Builder在Builder初始化的合适的时机主动调用的。
比如在所有的Builder的上级抽象类AbstractConfiguredSecurityBuilder中有两个方法如下:
@SuppressWarnings("unchecked")
private void init() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
for (SecurityConfigurer<O, B> configurer : configurers) {
configurer.init((B) this);
}
for (SecurityConfigurer<O, B> configurer : configurersAddedInInitializing) {
configurer.init((B) this);
}
}
@SuppressWarnings("unchecked")
private void configure() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
for (SecurityConfigurer<O, B> configurer : configurers) {
configurer.configure((B) this);
}
}
这两个方法最终会在合适的时机调用:
@Override
protected final O doBuild() throws Exception {
synchronized (configurers) {
buildState = BuildState.INITIALIZING;
beforeInit();
init();
buildState = BuildState.CONFIGURING;
beforeConfigure();
configure();
buildState = BuildState.BUILDING;
O result = performBuild();
buildState = BuildState.BUILT;
return result;
}
}
问题2:
AbstractConfiguredSecurityBuilder是一个可以被配置的Builder,其中有个configurers字段,类型为LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>>,这个字段保存Builder下面的各个子模块的Configurer对象,在调用Builder的build方法构建对象时使用。
情况1:
一个大模块有很多小模块的情况。一个小模块一个Configurer,一个Builder将各个小模块的Configurer整合起来,比如WebSecurity是一个Builder,下面有FormLoginConfigurer、HeadersConfigurer和HttpBasicConfigurer等多个子模块。最后WebSecurity的build利用各个子模块的信息build出一个Filter对象,WebSecurityConfiguration会将Build出的这个Filter对象注入到IOC容器中。
这种情况适用于这个大模块最后只需要build出一个对象的情况,如果需要build出多个对象,那就不好使了。
情况2:
各个模块是同级别的。需要每个模块一个Builder。而且各个模块比较简单。
tip1:
顶级的Builder中只将需要暴露给Configuration的对象作为顶级对象就可以了。比如说HttpSecurity中只有一个filters和一个requestMatcher,这两个对象用来构建此Builder要build出的DefaultSecurityFilterChain对象,如下:
tip2:
一个顶级Builder一个SharedObjects对象。
tip3:
如果让一个子模块可以被任意一个模块调用?
泛型返回值和Builder。
public interface WeiXinConfigurer<O, B extends WeiXinBuilder<O>>
O:The object being built by the {@link WeiXinBuilder} B
B: The {@link WeiXinBuilder} that builds objects of type O. This is also the {@link WeiXinBuilder} that is being configured.
只要将这两个值泛型就可以被任意的模块调用了。
tip3:
每个子模块的配置加一个and方法,用于返回当前使用的Builder对象,接着进行Builder对象级别的配置。
@Override
protected DefaultSecurityFilterChain performBuild() throws Exception {
Collections.sort(filters, comparator);
return new DefaultSecurityFilterChain(requestMatcher, filters);
}