Spring中扩展 ¶
AutowireCapableBeanFactory ¶
在Spring中,AutowireCapableBeanFactory 是一个非常强大的工具类,它允许在Spring容器之外手动创建对象并为其注入依赖。这对于处理第三方类或动态创建的对象非常有用。
使用场景
- 动态创建对象:在运行时动态创建对象,并为其注入Spring管理的Bean。
- 非托管对象的依赖注入:对于不在Spring生命周期管理中的对象,可以手动注入依赖。
- 后期依赖注入:在对象创建后,根据运行时确定的依赖关系注入依赖。
使用方法
获取
AutowireCapableBeanFactory实例;通过Spring的ApplicationContext获取AutowireCapableBeanFactory:java1@Autowired 2private ApplicationContext context; 3 4AutowireCapableBeanFactory beanFactory = context.getAutowireCapableBeanFactory();手动创建对象并注入依赖;假设有一个第三方类
ThirdPartyClass,它需要注入一个Spring管理的BeanMyDependency:java1public class ThirdPartyClass { 2 private MyDependency myDependency; 3 4 public void setMyDependency(MyDependency myDependency) { 5 this.myDependency = myDependency; 6 } 7 8 public void doSomething() { 9 myDependency.doSomething(); 10 } 11}java1// 手动注入依赖 2ThirdPartyClass thirdPartyClass = new ThirdPartyClass(); 3beanFactory.autowireBean(thirdPartyClass); // 自动注入依赖 4thirdPartyClass.doSomething(); // 调用方法验证依赖是否注入成功
关键方法
autowireBean(Object existingBean):- 自动装配给定的现有Bean。此方法会尝试对整个Bean进行自动装配,包括调用
@PostConstruct注解的方法。 - 适用于需要完整初始化流程的场景。
- 自动装配给定的现有Bean。此方法会尝试对整个Bean进行自动装配,包括调用
autowireBeanProperties(Object existingBean, int autowireMode, boolean dependent):- 自动装配给定Bean的属性,支持指定自动装配模式(如
AUTOWIRE_BY_NAME或AUTOWIRE_BY_TYPE)。 - 适用于只需要部分属性注入的场景。
- 自动装配给定Bean的属性,支持指定自动装配模式(如
initializeBean(Object existingBean, String beanName):- 初始化给定的Bean,包括调用
Aware接口和@PostConstruct注解的方法。
- 初始化给定的Bean,包括调用
注意事项
- 线程安全:
AutowireCapableBeanFactory的使用需要考虑线程安全问题。 - 性能开销:手动注入依赖会比Spring自动管理的Bean有额外的性能开销,但通常可以忽略。
- 适用场景:尽量只在必要时使用,例如处理第三方类或动态创建的对象。
通过 AutowireCapableBeanFactory,可以灵活地为第三方类或动态创建的对象注入Spring管理的Bean,而不必将其纳入Spring容器的管理范围。
SessionFixationProtectionStrategy ¶
SessionFixationProtectionStrategy是 Spring Security 框架里用于防止会话固定攻击的重要组件。
此策略主要运用HttpServletRequest.invalidate()方法来防范会话固定攻击。当用户成功认证后,系统会创建一个新的会话,以此避免攻击者利用旧会话 ID 进行非法操作。
工作流程
会话无效化处理:当用户认证成功时,原本的会话会被系统无效化。新会话创建操作:系统会生成一个全新的会话,从而分配新的会话 ID。属性迁移机制:能够把旧会话中的属性迁移到新会话中,不过像 Spring Security 相关的内部属性,无论怎样都会被迁移。会话超时时间设置:新会话会沿用旧会话的超时时间配置。
关键属性
migrateSessionAttributes:这是一个布尔类型的属性,默认值为true。它主要用于控制是否将旧会话中的属性迁移到新会话。
核心方法解析
- extractAttributes(HttpSession session)
- 该方法的作用是从即将无效化的旧会话中提取需要迁移的属性。
- 当
migrateSessionAttributes被设置为false时,除了 Spring Security 相关属性外,其他应用属性都会被丢弃。 - 若有特殊的属性迁移需求,可以通过重写此方法来实现自定义的迁移逻辑。
- applySessionFixation(HttpServletRequest request)
- 这是处理会话固定攻击的核心方法,它会先无效化旧会话,然后创建新会话。
- 其执行步骤为:获取当前会话并记录原始会话 ID,提取需要迁移的属性,无效化旧会话,创建新会话,将提取的属性迁移到新会话,最后设置新会话的超时时间。
- transferAttributes(Map**<**String, Object> attributes, HttpSession newSession)
- 此方法负责将提取的属性添加到新创建的会话中。
- createMigratedAttributeMap(HttpSession session)
- 这是一个辅助方法,用于创建需要迁移的属性映射。
- 它会遍历旧会话中的所有属性,根据
migrateSessionAttributes的值来决定是否保留某个属性。
自定义需要迁移的属性
继承SessionFixationProtectionStrategy类并重写extractAttributes方法
1public class CustomSessionFixationProtectionStrategy extends SessionFixationProtectionStrategy {
2
3 // 定义需要保留的应用属性白名单
4 private final String[] retainedAttributes;
5
6 public CustomSessionFixationProtectionStrategy(String[] retainedAttributes) {
7 this.retainedAttributes = retainedAttributes;
8 // 禁用默认的属性迁移,完全由自定义逻辑控制
9 super.setMigrateSessionAttributes(false);
10 }
11
12 @Override
13 protected Map<String, Object> extractAttributes(HttpSession session) {
14 Map<String, Object> attributesToMigrate = new HashMap<>();
15
16 // 1. 始终保留Spring Security相关属性
17 Enumeration<String> attributeNames = session.getAttributeNames();
18 while (attributeNames.hasMoreElements()) {
19 String key = attributeNames.nextElement();
20 if (key.startsWith("SPRING_SECURITY_")) {
21 attributesToMigrate.put(key, session.getAttribute(key));
22 }
23 }
24
25 // 2. 添加应用特定的保留属性
26 if (retainedAttributes != null) {
27 for (String attribute : retainedAttributes) {
28 Object value = session.getAttribute(attribute);
29 if (value != null) {
30 attributesToMigrate.put(attribute, value);
31 }
32 }
33 }
34
35 return attributesToMigrate;
36 }
37}Java 配置类中使用自定义策略
1@Configuration
2@EnableWebSecurity
3public class SecurityConfig extends WebSecurityConfigurerAdapter {
4 @Override
5 protected void configure(HttpSecurity http) throws Exception {
6 // 定义需要保留的应用属性
7 String[] retainedAttributes = {"userLocale", "themePreference"};
8
9 http
10 .sessionManagement()
11 .sessionFixation()
12 .sessionAuthenticationStrategy(new CustomSessionFixationProtectionStrategy(retainedAttributes));
13 // 其他会话管理配置
14 }
15}路径匹配 ¶
PathMatcher ¶
PathMatcher是个很实用的接口,它主要用于对字符串路径和路径模式进行匹配操作;借助这个接口,能够判断某个路径是否与特定的模式相契合,这在路由匹配、资源处理等场景中经常会用到。
核心接口与实现类
Spring Boot 提供了多种PathMatcher的实现,其中AntPathMatcher是最常用的一个,它支持 Ant 风格的路径模式。Spring Boot 中默认会有一个 AntPathMatcher 实例 Bean。
Ant 风格路径模式的规则
?:用于匹配单个字符。*:能够匹配任意数量的任意字符,但不包括路径分隔符/。**:可以递归地匹配任意数量的任意字符,包括路径分隔符/。
示例
1PathMatcher matcher = new AntPathMatcher();
2
3// pathMatcher.setCaseSensitive(false); // 设置路径匹配不区分大小写
4// pathMatcher.setPathSeparator("|"); // 设置路径分隔符为|
5
6// 精确匹配
7matcher.match("/users/{id}", "/users/123"); // 返回true
8matcher.match("/users/{id}", "/users/123/profile"); // 返回false
9
10// 使用*匹配
11matcher.match("/static/*.js", "/static/main.js"); // 返回true
12matcher.match("/static/*.js", "/static/css/style.css"); // 返回false
13
14// 使用**递归匹配
15matcher.match("/static/**/*.js", "/static/js/main.js"); // 返回true
16matcher.match("/static/**/*.js", "/static/css/style.css"); // 返回falsePathPatternParser ¶
在 Spring Framework 5.3 及之后的版本中,PathPatternParser 作为新的路径匹配器被引入,它逐渐替代了旧的 AntPathMatcher。PathPatternParser 提供了更高效、更安全的路径匹配功能,尤其在 WebFlux 和 Spring MVC 中得到了广泛应用。
核心概念与优势
- 基于路径模式的匹配:
PathPatternParser将路径模式编译为PathPattern对象,这种编译后的模式在匹配时效率更高。 - 线程安全:编译后的
PathPattern对象是线程安全的,可以被多个线程共享使用。 - 更好的性能:相较于
AntPathMatcher,PathPatternParser在频繁匹配相同模式的场景下,性能有显著提升。 - 标准化的路径处理:它会自动处理路径中的斜杠、解码等问题,避免了一些潜在的安全风险。
路径模式语法
PathPatternParser 支持的路径模式语法与 AntPathMatcher 类似,但也有一些细微的差别:
{variable}:用于匹配并捕获路径中的变量部分。例如,/users/{id}可以匹配/users/123,并将123捕获为变量id。{variable:regex}:可以使用正则表达式来限制变量的匹配范围。比如,/users/{id:\\d+}要求id必须是数字。**:用于递归匹配任意路径片段。例如,/static/**/*.js可以匹配/static/js/main.js等路径。?:匹配单个字符。*:匹配零个或多个字符,但不包括路径分隔符/。
示例
1// 创建PathPatternParser实例
2PathPatternParser parser = new PathPatternParser();
3// parser.setCaseSensitive(false); // 设置路径匹配不区分大小写
4// parser.setMatchOptionalTrailingSeparator(false); // 严格模式,尾部斜杠也必须匹配
5// parser.setPathOptions(PathContainer.Options.create(
6// '/', // 分隔符
7// true // true 完整解码路径段并解析路径参数;false 仅解码分隔符的转义序列(例如:将 %2F 解码为 /,但不解码其他 URL 编码字符)
8// ));
9
10
11// 编译路径模式
12PathPattern pattern1 = parser.parse("/users/{id}/profile");
13pattern1.matches(PathContainer.parsePath("/users/12321/profile")); // true
14pattern1.matchAndExtract(PathContainer.parsePath("/users/12321/profile")); // {id=12321}
15
16PathPattern pattern2 = parser.parse("/users/*");
17pattern2.matches(PathContainer.parsePath("/users/add")); // true
18pattern2.matchAndExtract(PathContainer.parsePath("/users/add/12")); // false
19
20PathPattern pattern3 = parser.parse("/users/**");
21pattern2.matches(PathContainer.parsePath("/users/add")); // true
22pattern2.matchAndExtract(PathContainer.parsePath("/users/add/12")); // true类型转换ConversionService ¶
ConversionService 是 Spring 框架自 3.0 起引入的“统一类型转换”总入口,它把任意 Java 类型 → 任意 Java 类型的转换逻辑抽象成一套可扩展的服务,从而彻底取代早期仅支持 String↔Object 且线程不安全的 PropertyEditor。
接口定义
java1public interface ConversionService { 2 // 判断是否可以将 sourceType 转换为 targetType 3 boolean canConvert(Class<?> sourceType, Class<?> targetType); 4 5 // 更细致的类型判断(支持泛型) 6 boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType); 7 8 // 执行转换(无类型描述符) 9 <T> T convert(Object source, Class<T> targetType); 10 11 // 执行转换(带类型描述符,支持泛型和注解信息) 12 Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType); 13}核心实现体系
GenericConversionService: 最底层、线程安全、无状态,内部维护一张Converters并发哈希表,负责“根据源→目标类型”路由具体转换器。DefaultConversionService: 在 1 的基础上预注册了大量常用转换器(String↔Number、String↔Enum、String↔Date、数组/集合互转等),开箱即用。FormattingConversionService: 继承2, 额外支持字段格式化(@NumberFormat、@DateTimeFormat),Spring MVC 默认装配的就是它。ApplicationConversionService: Spring Boot 中,在 3 的基础上再追加 Boot 自身所需的一套转换器(String→Duration、String→DataSize等),由ApplicationConversionService.getSharedInstance()单例提供。
转换器三种形态及注册方式
Converter<S, T>: 最基础的转换器接口,用于将类型
S转换为类型Tjava1public class StringToLocalDateConverter implements Converter<String, LocalDate> { 2 @Override 3 public LocalDate convert(String source) { 4 return LocalDate.parse(source, DateTimeFormatter.ISO_LOCAL_DATE); 5 } 6}GenericConverter: 更灵活的转换器,支持多源类型到多目标类型的转换,还能获取类型上下文(如注解、泛型信息)
java1public interface GenericConverter { 2 // 返回支持的类型对(源类型→目标类型) 3 Set<ConvertiblePair> getConvertibleTypes(); 4 5 // 执行转换,可利用类型描述符获取更多信息 6 Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType); 7}ConverterFactory<S, R>: 用于将源类型
S转换为R类型的子类型(如将String转换为任意Enum类型)java1public interface ConverterFactory<S, R> { 2 <T extends R> Converter<S, T> getConverter(Class<T> targetType); 3}
使用方式
创建 ConversionService, 通常直接使用
DefaultConversionService,它已内置常用转换器java1ConversionService conversionService = new DefaultConversionService();注册自定义转换器
java1GenericConversionService conversionService = new GenericConversionService(); 2// 注册 Converter 3conversionService.addConverter(new StringToLocalDateConverter()); 4// 注册 ConverterFactory 5conversionService.addConverterFactory(new StringToEnumConverterFactory());执行转换
java1// 字符串转整数 2Integer num = conversionService.convert("123", Integer.class); 3 4// 字符串转LocalDate 5LocalDate date = conversionService.convert("2023-10-01", LocalDate.class); 6 7// 集合转换(需借助TypeDescriptor处理泛型) 8List<String> strList = Arrays.asList("1", "2", "3"); 9TypeDescriptor sourceType = TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(String.class)); 10TypeDescriptor targetType = TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(Integer.class)); 11List<Integer> intList = (List<Integer>) conversionService.convert(strList, sourceType, targetType);