如何写一个给自己的框架写一个优雅的Java Config模块(二)

本文解析了配置模式中的Configurer、Builder和Configuration三者之间的关系及其工作原理。介绍了如何通过Configurer配置Builder,再由Builder构建对象并传递给Configuration,最终将对象注入到IOC容器中。

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

  1. Configurer保存配置信息
  2. Builder将Configurer串起来,一个Builder可能保存有很多的Configurer。多个类型的Configurer,同一个类型的多个Configurer。参看:AbstractConfiguredSecurityBuilder。
  3. 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);
}
设计一个社区养老健康服务系统的基本Java架构通常会包含几个核心模块: 1. **用户管理模块**:包括用户注册、登录、个人信息管理等功能,可以使用Spring Security等框架实现身份验证。 ```java public class UserService { public User register(String username, String password) { ... } public boolean login(String username, String password) { ... } } ``` 2. **健康档案模块**:存储老年人的医疗记录、疾病史、日常活动数据等,可以利用JPA或Hibernate进行持久化操作。 ```java @Entity class HealthRecord { @Id private Long id; private User user; private Date lastCheckupDate; // getters and setters } ``` 3. **医疗服务预约模块**:允许老人预约医生、理疗师等专业人员的服务,并发送提醒通知。 ```java @Service class AppointmentService { public void scheduleAppointment(User老人, Doctor doctor, Date时间) { ... } public void sendReminder(Appointment appointment) { ... } } ``` 4. **在线咨询服务**:提供实时聊天或视频咨询功能,可以集成第三方通信库如WebRTC或WebSocket。 5. **数据分析与报告**:生成并分析老年人的健康状况趋势报告,可能需要用到Elasticsearch等工具。 6. **安全管理**:保护用户的隐私信息,遵守GDPR等相关法规,通过HTTPS加密传输数据。 ```java @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/api/**").authenticated() .anyRequest().permitAll(); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陈振阳

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值