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

Spring Boot启动时ApplicationListener<ContextRefreshedEvent>未执行问题排查

SetupDataLoader类未执行问题排查

我是编程新手,正在使用Spring Boot 3.3.3与Java 17开发包含用户、角色、权限模块的简单应用。编写了SetupDataLoader类用于启动时初始化数据库(创建初始管理员、角色及对应权限),该类实现ApplicationListener<ContextRefreshedEvent>接口并标注@Component注解,也尝试过为onApplicationEvent方法添加@EventListener注解,但应用启动后,该类代码始终未执行——数据库users表无新增数据,控制台也未输出该类中的调试信息,恳请协助排查原因。


实体类

UserEntity

@Entity
@Table(name = "users")
@Data
public class UserEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Long id;

    @Column(name = "login")
    private String login;

    @Column(name = "password")
    private String password;

    @ManyToMany
    @JoinTable(
        name = "users_roles",
        joinColumns = @JoinColumn(
            name = "user_id", referencedColumnName = "id"),
        inverseJoinColumns = @JoinColumn(
            name = "role_id", referencedColumnName = "id"))
    private Collection<RoleEntity> roles;
    
    public UserEntity() {}
}

RoleEntity

@Entity
@Table(name = "roles")
@Data
public class RoleEntity {
    
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Long id;

    @Column(name = "name")
    private String name;

    @ManyToMany
    @JoinTable(
        name = "roles_privileges", 
        joinColumns = @JoinColumn(
          name = "role_id", referencedColumnName = "id"), 
        inverseJoinColumns = @JoinColumn(
          name = "privilege_id", referencedColumnName = "id"))
    private Collection<PrivilegeEntity> privileges;

    public RoleEntity() {}

    public RoleEntity(final String name) {
        this.name = name;
    }   
}

PrivilegeEntity

@Entity
@Table(name = "priveleges")
@Data
public class PrivilegeEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(name = "name")
    private String name;

    @ManyToMany(mappedBy = "privileges")
    private Collection<RoleEntity> roles;

    public PrivilegeEntity() {}

    public PrivilegeEntity(final String name) {
        this.name = name;
    }
}

仓库接口

UserRepo

public interface UserRepo extends CrudRepository<UserEntity, Long> {
    Optional<UserEntity> findByLogin (String login);
}

RoleRepo

public interface RoleRepo extends CrudRepository<RoleEntity, Long> {
    Optional<RoleEntity> findByName(String name);
}

PrivilegeRepo

public interface PrivilegeRepo extends CrudRepository<PrivilegeEntity, Long> {
    Optional<PrivilegeEntity> findByName(String name);
}

安全配置

@EnableWebSecurity 
@Configuration
public class SequrityConfig {
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf(AbstractHttpConfigurer::disable)
            .securityContext((securityContext) -> securityContext.requireExplicitSave(true))
            .authorizeHttpRequests(authorize -> authorize
                .requestMatchers(HttpMethod.GET, "/roleHierarchy")
                .hasRole("STAFF"));

        return http.build();
    }
    
    @Bean
    public SecurityExpressionHandler<FilterInvocation> customWebSecurityExpressionHandler() {
        DefaultWebSecurityExpressionHandler expressionHandler = new DefaultWebSecurityExpressionHandler();
        expressionHandler.setRoleHierarchy(roleHierarchy());
        return expressionHandler;
    }

    @Bean
    public RoleHierarchy roleHierarchy() {
        RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
        String hierarchy = "ROLE_ADMIN > ROLE_MODER \n ROLE_MODER > ROLE_STAFF";
        roleHierarchy.setHierarchy(hierarchy);
        return roleHierarchy;
    }
}

SetupDataLoader类

@Component
public class SetupDataLoader implements ApplicationListener<ContextRefreshedEvent> {

    private boolean alreadySetup = false;

    @Autowired
    UserRepo userRepo;

    @Autowired
    RoleRepo roleRepo;

    @Autowired
    PrivilegeRepo privilegeRepo;

    @Autowired
    PasswordEncoder passwordEncoder; 

    @Override
    @Transactional
    public void onApplicationEvent(final @NonNull ContextRefreshedEvent event) {
        if (alreadySetup) {
            return;
        }

        // == create init privileges
        final PrivilegeEntity readPrivilege = createPrivilege("READ_PRIVILEGE");
        final PrivilegeEntity userWritePrivilege = createPrivilege("USER_WRITE_PRIVILEGE");
        final PrivilegeEntity switchStatePrivilege = createPrivilege("CHANGE_STATE_PRIVILEGE");
        final PrivilegeEntity switchWritePrivilege = createPrivilege("SWITCH_WRITE_PRIVILEGE");
        final PrivilegeEntity commentWritePrivilege = createPrivilege("COMMENT_WRITE_PRIVILEGE");

        // == create init roles;
        final List<PrivilegeEntity> adminPrivileges = new ArrayList<>(Arrays
            .asList(readPrivilege, 
                    userWritePrivilege, 
                    switchStatePrivilege, 
                    switchWritePrivilege, 
                    commentWritePrivilege));

        final List<PrivilegeEntity> moderPrivileges = new ArrayList<>(Arrays
            .asList(readPrivilege, 
                    switchWritePrivilege, 
                    commentWritePrivilege));
                    
        final List<PrivilegeEntity> staffPrivileges = new ArrayList<>(Arrays.asList(readPrivilege));

        final RoleEntity adminRole = createRole("ROLE_ADMIN", adminPrivileges);
        createRole("ROLE_MODER", moderPrivileges);
        createRole("ROLE_STAFF", staffPrivileges);

        // == create init users FIRST ADMIN in db
        createUser("ADMIN", "ADMIN", new ArrayList<>(Arrays.asList(adminRole)));

        alreadySetup = true;
    }

    @Transactional
    public PrivilegeEntity createPrivilege(final String name) {
        Optional<PrivilegeEntity> optPrivilege = privilegeRepo.findByName(name);
        PrivilegeEntity privilege;

        if (optPrivilege.isPresent()) 
            privilege = optPrivilege.get();
        else {
            privilege = new PrivilegeEntity(name);
            privilege = privilegeRepo.save(privilege);
        }

        return privilege;
    }

    @Transactional
    public RoleEntity createRole(final String name, final Collection<PrivilegeEntity> privileges) {
        Optional<RoleEntity> optRole = roleRepo.findByName(name);
        RoleEntity role;

        if (optRole.isPresent()) 
            role = optRole.get();
        else {
            role = new RoleEntity(name);
        }
        role.setPrivileges(privileges);
        role = roleRepo.save(role);
        return role;
    }

    @Transactional
    public UserEntity createUser(final String login, final String password, final Collection<RoleEntity> roles) {
        Optional<UserEntity> optUser = userRepo.findByLogin(login);
        UserEntity user;

        if (optUser.isPresent()) 
            user = optUser.get();
        else {
            user = new UserEntity();
            user.setLogin(login);
            user.setPassword(passwordEncoder.encode(password));
        }
        user.setRoles(roles);
        user = userRepo.save(user);
        return user;
    }
}

数据库查询结果

prototype=# select * from users;
 id | login | password 
----+-------+----------
(0 rows)

项目结构

🗁 src 
└─── 🗁 main
    ├─── 🗁 java/ru/dikun/prototype
        ├─── 🗁 config 
        │   ├───SequrityConfig.java
        │   └───SetupDataLoader.java
        ├─── 🗁 controllers
        ├─── 🗁 domain
        │   ├───PrivilegeEntity.java
        │   ├───RoleEntity.java
        │   └───UserEntity.java
        ├─── 🗁 repos
        │   ├───PrivilegeRepo.java
        │   ├───RoleRepo.java
        │   └───UserRepo.java
        ├─── 🗁 services
        │   └───UserService.java
        └───  PrototypeApplication.java
    └─── 🗁 resources
        └─── application.properties

调试日志

DEBUG 26790 --- [prototype] [  restartedMain] o.s.b.a.ApplicationAvailabilityBean      : Application availability state LivenessState changed to CORRECT
DEBUG 26790 --- [prototype] [  restartedMain] o.s.boot.devtools.restart.Restarter      : Creating new Restarter for thread Thread[main,5,main]
DEBUG 26790 --- [prototype] [  restartedMain] o.s.boot.devtools.restart.Restarter      : Immediately restarting application
DEBUG 26790 --- [prototype] [  restartedMain] o.s.boot.devtools.restart.Restarter      : Starting application ru.dikun.prototype.PrototypeApplication with URLs [file:/home/ogurchik/workspace/prototype/target/classes/]
DEBUG 26790 --- [prototype] [  restartedMain] o.s.b.a.ApplicationAvailabilityBean      : Application availability state ReadinessState changed to ACCEPTING_TRAFFIC
DEBUG 26790 --- [prototype] [ionShutdownHook] o.s.b.a.ApplicationAvailabilityBean      : Application availability state ReadinessState changed from ACCEPTING_TRAFFIC to REFUSING_TRAFFIC
DEBUG 26790 --- [prototype] [ionShutdownHook] ConfigServletWebServerApplicationContext : Closing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@1cbe84e7, started on ...

排查关键点

  1. 补充缺失的PasswordEncoder Bean:SetupDataLoader依赖PasswordEncoder,但当前安全配置类中未定义该Bean,导致Spring无法实例化SetupDataLoader组件。在SequrityConfig中添加如下代码:
@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}
  1. 确认组件扫描范围:检查PrototypeApplication.java的@SpringBootApplication注解是否覆盖config包。默认主类所在包ru.dikun.prototype的子包会被自动扫描,若主类路径异常,需显式指定scanBasePackages = {"ru.dikun.prototype"}。

  2. 添加调试输出:在onApplicationEvent方法开头添加System.out.println("SetupDataLoader开始执行初始化"),确认方法是否被调用,排除逻辑跳过的可能。

  3. 检查JPA配置有效性:确保application.properties中数据库连接配置正确,且主类上的@SpringBootApplication注解已自动启用JPA仓库扫描和实体扫描(默认包含@EnableJpaRepositories和@EntityScan)。

  4. 修正拼写问题:SequrityConfig类名缺失字母u(建议改为SecurityConfig),PrivilegeEntity的表名拼写为priveleges(正确应为privileges),虽不影响组件扫描,但可能导致数据库操作异常,建议修正。


内容的提问来源于stack exchange,提问作者AlexDikun

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 22:12:02