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 方法很好理解,不做多余追述。

相关推荐
SchneeDuan1 天前
建造者模式
运维·服务器·建造者模式
小白不太白9501 天前
设计模式之建造者模式
java·设计模式·建造者模式
p-knowledge1 天前
建造者模式(Builder Pattern)
java·开发语言·建造者模式
捕鲸叉2 天前
C++创建型模式之生成器模式
开发语言·c++·建造者模式
疯一样的码农2 天前
Spring Security PasswordEncoder接口(密码编码)
java·spring·spring security
疯一样的码农2 天前
Spring Security AuthenticationEntryPoint接口(处理未认证访问)
java·spring·spring security
海风极客2 天前
浅谈Go语言Optional模式和Builder模式
开发语言·golang·建造者模式
孤华暗香3 天前
Python设计模式详解之4 —— 建造者模式
python·设计模式·建造者模式
疯一样的码农3 天前
Spring Security SecurityContextHolder(安全上下文信息)
java·spring·spring security
吾与谁归in8 天前
【C#设计模式(4)——构建者模式(Builder Pattern)】
设计模式·c#·建造者模式