Spring Security 6.x 系列(7)—— 源码分析之Builder设计模式

一、Builder设计模式

WebSecurityHttpSecurityAuthenticationManagerBuilder 都是框架中的构建者,把他们放到一起看看他们的共同特点:

查看AuthenticationManagerBuilder的继承结构图:

查看HttpSecurity的继承结构图:

查看WebSecurity的继承结构图:

可以看出他们都有这样一条继承树:

bash 复制代码
|- SecurityBuilder
	|- AbstractSecurityBuilder
		|- AbstractConfiguredSecurityBuilder

二、SecurityBuilder

java 复制代码
/**
 * Interface for building an Object
 *
 * @param <O> The type of the Object being built
 * @author Rob Winch
 * @since 3.2
 */
public interface SecurityBuilder<O> {

	/**
	 * Builds the object and returns it or null.
	 * @return the Object to be built or null if the implementation allows it.
	 * @throws Exception if an error occurred when building the Object
	 */
	O build() throws Exception;

}

SecurityBuilder是一个接口,当调用它的 build() 方法时,会创建一个对象。将要创建的对象类型由泛型 O 限制。这个接口是所有构造器的顶级接口,也是Spring Security 框架中使用Builder设计模式的基础接口。

三、AbstractSecurityBuilder

java 复制代码
/**
 * A base {@link SecurityBuilder} that ensures the object being built is only built one
 * time.
 *
 * @param <O> the type of Object that is being built
 * @author Rob Winch
 *
 */
public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> {

	// 标记对象是否处于创建中
	private AtomicBoolean building = new AtomicBoolean();

	private O object;

	@Override
	public final O build() throws Exception {
		if (this.building.compareAndSet(false, true)) {
			// 对象的实际底构建过程再 doBuild() 方法中实现
			this.object = doBuild();
			return this.object;
		}
		throw new AlreadyBuiltException("This object has already been built");
	}

	/**
	 * 获取已生成的对象。如果尚未构建,则会引发异常。
	 * @return the Object that was built
	 */
	public final O getObject() {
		if (!this.building.get()) {
			throw new IllegalStateException("This object has not been built");
		}
		return this.object;
	}

	/**
	 * 子类需实现这个方法来执行对象构建。
	 * @return the object that should be returned by {@link #build()}.
	 * @throws Exception if an error occurs
	 */
	protected abstract O doBuild() throws Exception;

}

AbstractSecurityBuilderSecurityBuilder的一个基础实现抽象类 ,提供构造器的基础流程和控制,能够确保对象O只被创建一次。

这个类很简单:

  • 定义了一个原子操作的对象,用来标记当前对象是否处于构造中

    java 复制代码
    private AtomicBoolean building = new AtomicBoolean();
  • 实现SecurityBuilder接口的 build() 方法。

    调用 doBuild() 方法完成构造,并且在调用 doBuild() 之前需要原子修改 buildingtrue,只有修改成功才能执行 doBuild() 方法,这间接的保证了:对象只构造一次,确保构造的唯一性和原子性。

  • 定义一个 getObject() 方法,方便获取构造的对象。

  • 定义抽象方法 doBuild() ,加入模板模式,交给子类自行实现。

四、AbstractConfiguredSecurityBuilder

源码注释:

A base SecurityBuilder that allows SecurityConfigurer to be applied to it. This makes modifying the SecurityBuilder a strategy that can be customized and broken up into a number of SecurityConfigurer objects that have more specific goals than that of the SecurityBuilder.

一个基本的SecurityBuilder,允许将SecurityConfigurer应用于它。这使得修改SecurityBuilder的策略可以自定义并分解为许多SecurityConfigurer对象,这些对象具有比SecurityBuilder更具体的目标。

For example, a SecurityBuilder may build an DelegatingFilterProxy, but a SecurityConfigurer might populate the SecurityBuilder with the filters necessary for session management, form based login, authorization, etc.

请参阅:

WebSecurity

作者:

Rob Winch

类型形参:
<O> -- The object that this builder returns 此构造器返回的对象
<B> -- The type of this builder (that is returned by the base class) 此构造器的类型(由基类返回)

它继承自 AbstractSecurityBuilder ,在此之上又做了一些扩展。先来看看里面都有什么:

4.1 内部静态枚举类 BuildState

这个枚举类用来表示应用程序(构造器构造对象)的状态,代码相对简单,就不粘贴源码了。

枚举类中只有一个 int 类型的成员变量 order 表示状态编号:

  • UNBUILT(0) :未构造

    构造器的 build 方法被调用之前的状态

  • INITIALIZING(1) : 初始化中

    构造器的 build 方法第一次被调用,到所有 SecurityConfigurerinit 方法都被调用完这期间都是 INITIALIZING 状态

  • CONFIGURING(2): 配置中

    表示从所有的 SecurityConfigurerinit 方法都被调用完,直到所有 configure 方法都被调用

    意思就是所有配置器都初始化了,直到配置都被调用这段时间都时 CONFIGURING 状态

  • BUILDING(3) :对象构造中

    表示已经执行完所有的 SecurityConfigurerconfigure 方法,到刚刚执行完 AbstractConfiguredSecurityBuilderperformBuild 方法这期间

    意思就是从将所有配置器的配置都配置完成开始,到构造完这个对象这段时间都是 BUILDING 状态

  • BUILT(4) :对象已经构造完成

    表示对象已经构造完成。

枚举类中还有两个方法:

  • isInitializingINITIALIZING状态时返回true

  • isConfigured:大于等于 CONFIGURING 的时候返回 true

    也就是说配置器初始化完成时在构造器看来就算以配置状态了。

4.2 成员变量

java 复制代码
private final Log logger = LogFactory.getLog(getClass());

private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<>();

private final List<SecurityConfigurer<O, B>> configurersAddedInInitializing = new ArrayList<>();

private final Map<Class<?>, Object> sharedObjects = new HashMap<>();

private final boolean allowConfigurersOfSameType;

private BuildState buildState = BuildState.UNBUILT;

private ObjectPostProcessor<Object> objectPostProcessor;
  • configurers:所要应用到当前 SecurityBuilder 上的所有的 SecurityConfigurer
  • configurersAddedInInitializing:用于记录在初始化期间添加进来的 SecurityConfigurer
  • sharedObjects:共享对象。
  • ObjectPostProcessor:由外部调用者提供,这是一个后置处理对象,在创建完对象后会用到这个后置处理对象。

4.3 构造方法

java 复制代码
/***
 * Creates a new instance with the provided {@link ObjectPostProcessor}. This post
 * processor must support Object since there are many types of objects that may be
 * post processed.
 * @param objectPostProcessor the {@link ObjectPostProcessor} to use
 */
protected AbstractConfiguredSecurityBuilder(ObjectPostProcessor<Object> objectPostProcessor) {
	this(objectPostProcessor, false);
}

/***
 * Creates a new instance with the provided {@link ObjectPostProcessor}. This post
 * processor must support Object since there are many types of objects that may be
 * post processed.
 * @param objectPostProcessor the {@link ObjectPostProcessor} to use
 * @param allowConfigurersOfSameType if true, will not override other
 * {@link SecurityConfigurer}'s when performing apply
 */
protected AbstractConfiguredSecurityBuilder(ObjectPostProcessor<Object> objectPostProcessor,
		boolean allowConfigurersOfSameType) {
	Assert.notNull(objectPostProcessor, "objectPostProcessor cannot be null");
	this.objectPostProcessor = objectPostProcessor;
	this.allowConfigurersOfSameType = allowConfigurersOfSameType;
}

构造函数只对两个成员变量进行了赋值:

  • objectPostProcessor:由外部调用者提供,这是一个后置处理对象,在创建完对象后会用到这个后置处理对象。

  • allowConfigurersOfSameType:从源码可以看出,默认情况下 allowConfigurersOfSameTypefalse

    这个成员变量的含义:

    • true 表示允许相同类型的构造器,在应用配置器时不会覆盖相同类型的配。

4.4 方法

4.4.1 getOrBuild 方法

想要用构造器获取最终构造的对象时,需调用这个方法。

这个方法的逻辑很简单,调用 isUnbuilt() 方法判断对象创建状态是否未构建完成( return buildState == BuildState.UNBUILT ):

  • 如果对象已经创建就直接返回已经构建好的对象,
  • 否则调用构造器的 build() 方法构建对象并返回已构建完成的对象。

从刚才看到的父类 AbstractSecurityBuilder 代码中可以知道真正的构建过程是调用子类 doBuild() 方法完成的。

isUnbuilt() 方法中,对 configurers 成员变量加了锁(synchronized),保证获取到的构建完成状态时,对象真的已经构建好了。

java 复制代码
/**
 * Similar to {@link #build()} and {@link #getObject()} but checks the state to
 * determine if {@link #build()} needs to be called first.
 * @return the result of {@link #build()} or {@link #getObject()}. If an error occurs
 * while building, returns null.
 */
public O getOrBuild() {
	if (!isUnbuilt()) {
		return getObject();
	}
	try {
		return build();
	}
	catch (Exception ex) {
		this.logger.debug("Failed to perform build. Returning null", ex);
		return null;
	}
}

/**
 * Determines if the object is unbuilt.
 * @return true, if unbuilt else false
 */
private boolean isUnbuilt() {
	synchronized (this.configurers) {
		return this.buildState == BuildState.UNBUILT;
	}
}

4.4.2 doBuild 方法

使用以下步骤对configurers执行生成:

java 复制代码
/**
 * Executes the build using the {@link SecurityConfigurer}'s that have been applied
 * using the following steps:
 *
 * <ul>
 * <li>Invokes {@link #beforeInit()} for any subclass to hook into</li>
 * <li>Invokes {@link SecurityConfigurer#init(SecurityBuilder)} for any
 * {@link SecurityConfigurer} that was applied to this builder.</li>
 * <li>Invokes {@link #beforeConfigure()} for any subclass to hook into</li>
 * <li>Invokes {@link #performBuild()} which actually builds the Object</li>
 * </ul>
 */
@Override
protected final O doBuild() throws Exception {
	synchronized (this.configurers) {
		this.buildState = BuildState.INITIALIZING;
		beforeInit();
		init();
		this.buildState = BuildState.CONFIGURING;
		beforeConfigure();
		configure();
		this.buildState = BuildState.BUILDING;
		O result = performBuild();
		this.buildState = BuildState.BUILT;
		return result;
	}
}
  • 构建过程对 configurers 加锁。
  • 方法体中时构建对象的整个流程,包括状态变化。
  • 构建过程大致分为构建器初始化 beforeInit()init(),构建器配置 beforeConfigure()configure(),构建对象 performBuild()

构建过程对 configurers 加锁,也就意味着进入构建方法后 configurers 中的构建器应该都准备好了。这个时候如果再添加或者修改配置器都会失败。

4.4.3 beforeInit 方法 和 beforeConfigure 方法

这两个方法是抽象方法,由子类实现。子类通过覆盖这两个方法可以挂钩到对象构建的生命周期中,实现:在配置器(SecurityConfigurer)调用初始化方法或者配置方法之前做用户自定义的操作。

java 复制代码
/**
 * Invoked prior to invoking each {@link SecurityConfigurer#init(SecurityBuilder)}
 * method. Subclasses may override this method to hook into the lifecycle without
 * using a {@link SecurityConfigurer}.
 */
protected void beforeInit() throws Exception {
}

/**
 * Invoked prior to invoking each
 * {@link SecurityConfigurer#configure(SecurityBuilder)} method. Subclasses may
 * override this method to hook into the lifecycle without using a
 * {@link SecurityConfigurer}.
 */
protected void beforeConfigure() throws Exception {
}

4.4.4 init 方法

java 复制代码
@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 : this.configurersAddedInInitializing) {
		configurer.init((B) this);
	}
}

方法很简单功能很简单,就是遍历 configurersconfigurersAddedInInitializing ,对里面存储的配置器进行初始化。

配置器初始化的详细内容到看配置器源码时在了解。

4.4.5 configure 方法

java 复制代码
@SuppressWarnings("unchecked")
private void configure() throws Exception {
	Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
	for (SecurityConfigurer<O, B> configurer : configurers) {
		configurer.configure((B) this);
	}
}

private Collection<SecurityConfigurer<O, B>> getConfigurers() {
	List<SecurityConfigurer<O, B>> result = new ArrayList<>();
	for (List<SecurityConfigurer<O, B>> configs : this.configurers.values()) {
		result.addAll(configs);
	}
	return result;
}

遍历 configurers ,调用所有配置器的 configure(SecurityBuilder b) 方法对当前的构建器(this)进行配置。

配置器配置详细内容到看配置器源码时在了解。

4.4.6 performBuild 方法

这也是一个抽象方法,需要子类实现,完成对象的创建并返回。

4.4.7 apply 方法

java 复制代码
/**
 * Applies a {@link SecurityConfigurerAdapter} to this {@link SecurityBuilder} and
 * invokes {@link SecurityConfigurerAdapter#setBuilder(SecurityBuilder)}.
 * @param configurer
 * @return the {@link SecurityConfigurerAdapter} for further customizations
 * @throws Exception
 */
@SuppressWarnings("unchecked")
public <C extends SecurityConfigurerAdapter<O, B>> C apply(C configurer) throws Exception {
	configurer.addObjectPostProcessor(this.objectPostProcessor);
	configurer.setBuilder((B) this);
	add(configurer);
	return configurer;
}

/**
 * Applies a {@link SecurityConfigurer} to this {@link SecurityBuilder} overriding any
 * {@link SecurityConfigurer} of the exact same class. Note that object hierarchies
 * are not considered.
 * @param configurer
 * @return the {@link SecurityConfigurerAdapter} for further customizations
 * @throws Exception
 */
public <C extends SecurityConfigurer<O, B>> C apply(C configurer) throws Exception {
	add(configurer);
	return configurer;
}

这个方法的作用是将 SecurityConfigurerAdapter (配置器的适配器)或者 SecurityConfigurer (配置器)应用到当前的构建器。这两个方法是相互重载的,他们最后都调用了 add(configurer) 方法,将配置器添加到构建器,方便构建时使用(初始,配置)。

关于 SecurityConfigurerAdapterSecurityConfigurer 后面再详细了解。这里观察可以看出,他们实现了相同的接口,都可以作为add方法的参数。
而且 public <C extends SecurityConfigurerAdapter<O,B>> C apply(C configurer)throws Exception方法在6.2 版本标记为废弃。

4.4.8 add 方法

java 复制代码
/**
 * Adds {@link SecurityConfigurer} ensuring that it is allowed and invoking
 * {@link SecurityConfigurer#init(SecurityBuilder)} immediately if necessary.
 * @param configurer the {@link SecurityConfigurer} to add
 */
@SuppressWarnings("unchecked")
private <C extends SecurityConfigurer<O, B>> void add(C configurer) {
	Assert.notNull(configurer, "configurer cannot be null");
	Class<? extends SecurityConfigurer<O, B>> clazz = (Class<? extends SecurityConfigurer<O, B>>) configurer
			.getClass();
	synchronized (this.configurers) {
		if (this.buildState.isConfigured()) {
			throw new IllegalStateException("Cannot apply " + configurer + " to already built object");
		}
		List<SecurityConfigurer<O, B>> configs = null;
		if (this.allowConfigurersOfSameType) {
			configs = this.configurers.get(clazz);
		}
		configs = (configs != null) ? configs : new ArrayList<>(1);
		configs.add(configurer);
		this.configurers.put(clazz, configs);
		if (this.buildState.isInitializing()) {
			this.configurersAddedInInitializing.add(configurer);
		}
	}
}

这个方法将配置器添加到一个map集合里面,这个map中以配置器的类名为 Key,以存放这个类型的配置器的 List 集合为 Value

  • 在执行添加操作时会对 configurers 加锁(synchronized )。

  • 通过构造方法中设置的 allowConfigurersOfSameType 值判断是否允许添加相同类型的配置器,如果是 true ,那么在添加之前会根据类名先从 map 中获取该类型配置器链表(List),如果获取到了就把要添加的配置器追加到后面,然后把追加了新配置器的List再放回到 map 里面,如果获取到 null ,接创建一个新的 List 来存放配置器。

  • 添加配置器时,如果该构建器已经处于以配置状态(大于等于 CONFIGURING.order ),那么会抛出异常;如果该构建器已经处于 INITIALIZING 状态,那么久将这个适配器链表存放到 configurersAddedInInitializing 这个map中;否则将适配器链表存放到 configurers 这个 map 集合中。

遗留一个问题,没有看出来为什么要使用 configurersAddedInInitializing ,如果没有 configurersAddedInInitializing 这个设计会出现什么并发问题吗?

4.4.9 其他方法

剩下的方法都是一些getsetremove 方法很好理解,不做多余追述。

相关推荐
那你为何对我三笑留情10 小时前
六、Spring Boot集成Spring Security之前后分离项目认证流程最佳方案
java·spring boot·分布式·后端·spring·spring security
morning_judger4 天前
【设计模式系列】建造者模式(十)
java·设计模式·建造者模式
wrx繁星点点7 天前
创建型模式-建造者模式:构建复杂对象的优雅解决方案
java·开发语言·数据结构·数据库·spring·maven·建造者模式
ccmjga9 天前
建造者设计模式
java·spring boot·设计模式·gradle·spring security·1024程序员节
ccmjga11 天前
为什么选择 Spring data hadoop
java·spring boot·docker·设计模式·gradle·spring security
zzzhpzhpzzz12 天前
设计模式——建造者模式
设计模式·建造者模式
小白13 天前
C# 创建型设计模式----建造者模式
设计模式·建造者模式
ccmjga14 天前
适配器设计模式
java·spring boot·后端·设计模式·gradle·spring security·1024程序员节
阳光开朗_大男孩儿17 天前
原型模式和建造模式的区别
开发语言·c++·设计模式·建造者模式·原型模式
小猫猫猫◍˃ᵕ˂◍17 天前
浅析建造者模式
java·开发语言·建造者模式