Spring Boot启动时ApplicationListener<ContextRefreshedEvent>未执行问题排查
我是编程新手,正在使用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 ...
排查关键点
- 补充缺失的PasswordEncoder Bean:SetupDataLoader依赖
PasswordEncoder,但当前安全配置类中未定义该Bean,导致Spring无法实例化SetupDataLoader组件。在SequrityConfig中添加如下代码:
@Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); }
确认组件扫描范围:检查
PrototypeApplication.java的@SpringBootApplication注解是否覆盖config包。默认主类所在包ru.dikun.prototype的子包会被自动扫描,若主类路径异常,需显式指定scanBasePackages = {"ru.dikun.prototype"}。添加调试输出:在
onApplicationEvent方法开头添加System.out.println("SetupDataLoader开始执行初始化"),确认方法是否被调用,排除逻辑跳过的可能。检查JPA配置有效性:确保
application.properties中数据库连接配置正确,且主类上的@SpringBootApplication注解已自动启用JPA仓库扫描和实体扫描(默认包含@EnableJpaRepositories和@EntityScan)。修正拼写问题:
SequrityConfig类名缺失字母u(建议改为SecurityConfig),PrivilegeEntity的表名拼写为priveleges(正确应为privileges),虽不影响组件扫描,但可能导致数据库操作异常,建议修正。
内容的提问来源于stack exchange,提问作者AlexDikun

