You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Security(认证服务器)Spring Boot 3.1.0:为何大量使用final类?

Spring Security 3.1.0中Final类的设计原因与扩展解决方案

一、为什么大量使用Final类?

Spring Security团队选择大量使用final类,主要基于以下设计考量:

  • API稳定性:框架核心组件被final修饰后,能避免子类化带来的意外逻辑破坏。如果允许随意继承,子类可能重写核心方法导致框架内部逻辑混乱,Spring无法保证不同版本间的兼容性。
  • 不可变对象与线程安全:final类配合private final成员变量,更容易实现不可变对象。这类对象天生线程安全,适合Spring这种多线程环境下的Bean管理。
  • 推崇组合而非继承:Spring现在更鼓励用组合模式替代继承来扩展功能,避免继承带来的紧耦合问题。final类的设计也是在引导开发者采用更灵活的组合方式。

二、针对UserBuilder的扩展方案

因为UserBuilder是final类无法继承,你可以通过以下方式实现自定义UserDetails:

  1. 自定义构建器:完全实现自己的CustomUserBuilder来构建CustomUser实例,不用依赖原生的UserBuilder:
public class CustomUser implements UserDetails {
    private final String username;
    private final String password;
    private final String fullName; // 自定义字段
    private final Collection<? extends GrantedAuthority> authorities;

    private CustomUser(Builder builder) {
        this.username = builder.username;
        this.password = builder.password;
        this.fullName = builder.fullName;
        this.authorities = builder.authorities;
    }

    // 实现UserDetails的所有方法
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return authorities;
    }

    // 其他getter方法...

    public static class Builder {
        private String username;
        private String password;
        private String fullName;
        private Collection<? extends GrantedAuthority> authorities = Collections.emptyList();

        public Builder username(String username) {
            this.username = username;
            return this;
        }

        public Builder password(String password) {
            this.password = password;
            return this;
        }

        public Builder fullName(String fullName) {
            this.fullName = fullName;
            return this;
        }

        public Builder authorities(Collection<? extends GrantedAuthority> authorities) {
            this.authorities = authorities;
            return this;
        }

        public CustomUser build() {
            return new CustomUser(this);
        }
    }
}
  1. 组合原生User对象:如果想复用原生User的逻辑,可以把User作为CustomUser的成员变量,通过委托方式实现UserDetails接口:
public class CustomUser implements UserDetails {
    private final User user;
    private final String fullName;

    public CustomUser(User user, String fullName) {
        this.user = user;
        this.fullName = fullName;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return user.getAuthorities();
    }

    @Override
    public String getPassword() {
        return user.getPassword();
    }

    @Override
    public String getUsername() {
        return user.getUsername();
    }

    // 其他UserDetails方法直接委托给user,自定义字段单独处理
    public String getFullName() {
        return fullName;
    }
}

三、针对OAuth2AuthorizationServerPropertiesMapper的解决方案

由于OAuth2AuthorizationServerPropertiesMapper是final且构造方法包私有,无法直接继承或实例化,你可以这样处理:

  1. 复制扩展核心逻辑:Spring是开源框架,你可以查看该类的源码,把核心的属性映射逻辑复制到自己的自定义映射器中,再添加需要的扩展逻辑:
@Component
public class CustomOAuth2AuthorizationServerPropertiesMapper {
    public AuthorizationServerSettings map(OAuth2AuthorizationServerProperties properties) {
        // 复制原类中的核心映射代码
        AuthorizationServerSettings.Builder builder = AuthorizationServerSettings.builder();
        if (properties.getIssuer() != null) {
            builder.issuer(properties.getIssuer());
        }
        // ... 其他原有的映射逻辑
        // 添加你的自定义映射规则
        builder.tokenEndpoint("/custom-token-endpoint");
        return builder.build();
    }
}
  1. 使用BeanPostProcessor修改结果:如果只是需要在原映射结果基础上做微调,可以通过BeanPostProcessor拦截AuthorizationServerSettings的创建过程,修改其属性:
@Component
public class AuthorizationServerSettingsPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof AuthorizationServerSettings) {
            AuthorizationServerSettings original = (AuthorizationServerSettings) bean;
            return AuthorizationServerSettings.from(original)
                    .tokenEndpoint("/custom-token-endpoint")
                    .build();
        }
        return bean;
    }
}

四、是否需要深入Java手册?

Java手册中关于final关键字的基础内容是需要了解的,但更核心的是理解Spring的设计理念:组合优于继承、不可变对象的优势、框架API稳定性的重要性。这些理念才是理解这类设计的关键,Java语法只是实现这些理念的工具。

内容的提问来源于stack exchange,提问作者Buks van der Lingen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 12:57:23