项目结构

auth-starter/
├── src/main/java
│   └── com/example/auth
│       ├── config/
│       ├── controller/
│       ├── entity/
│       ├── mapper/          # MyBatis-Plus Mapper接口
│       ├── service/
│       ├── service/impl/
│       ├── dto/
│       └── AuthAutoConfiguration.java
├── src/main/resources
│   └── META-INF
│       ├── spring.factories
│       └── mybatis-mapper/  # SQL映射文件(可选)
└── pom.xml

1. POM.xml 配置

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    
    <groupId>com.example</groupId>
    <artifactId>auth-mybatisplus-starter</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>
    
    <properties>
        <java.version>1.8</java.version>
        <spring-boot.version>2.7.0</spring-boot.version>
        <mybatis-plus.version>3.5.3.1</mybatis-plus.version>
    </properties>
    
    <dependencies>
        <!-- Spring Boot Starter依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <scope>provided</scope>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <scope>provided</scope>
        </dependency>
        
        <!-- MyBatis-Plus -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatis-plus.version}</version>
            <scope>provided</scope>
        </dependency>
        
        <!-- Redis -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <scope>provided</scope>
        </dependency>
        
        <!-- 数据库驱动由使用方提供 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>provided</scope>
            <optional>true</optional>
        </dependency>
        
        <!-- Lombok可选 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
            <optional>true</optional>
        </dependency>
    </dependencies>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <skip>true</skip>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

2. 实体类定义

@Data
@TableName("${auth.table-prefix}user")
public class User {
    @TableId(type = IdType.AUTO)
    private Long id;
    
    private String username;
    private String password;
    private String email;
    private Integer status;
    private Date createTime;
    private Date updateTime;
}

@Data
@TableName("${auth.table-prefix}role")
public class Role {
    @TableId(type = IdType.AUTO)
    private Long id;
    
    private String roleName;
    private String roleCode;
    private String description;
    private Date createTime;
}

@Data
@TableName("${auth.table-prefix}permission")
public class Permission {
    @TableId(type = IdType.AUTO)
    private Long id;
    
    private String permName;
    private String permCode;
    private String description;
}

// 用户-角色关联表
@Data
@TableName("${auth.table-prefix}user_role")
public class UserRole {
    @TableId(type = IdType.AUTO)
    private Long id;
    
    private Long userId;
    private Long roleId;
}

// 角色-权限关联表
@Data
@TableName("${auth.table-prefix}role_permission")
public class RolePermission {
    @TableId(type = IdType.AUTO)
    private Long id;
    
    private Long roleId;
    private Long permissionId;
}

3. Mapper接口

public interface UserMapper extends BaseMapper<User> {
    // 自定义查询方法
    @Select("SELECT r.* FROM ${auth.table-prefix}role r " +
            "INNER JOIN ${auth.table-prefix}user_role ur ON r.id = ur.role_id " +
            "WHERE ur.user_id = #{userId}")
    List<Role> selectRolesByUserId(@Param("userId") Long userId);
}

public interface RoleMapper extends BaseMapper<Role> {
    // 根据用户ID查询角色
    List<Role> selectRolesByUserId(Long userId);
}

public interface PermissionMapper extends BaseMapper<Permission> {
    // 根据角色ID查询权限
    List<Permission> selectPermissionsByRoleId(Long roleId);
}

public interface UserRoleMapper extends BaseMapper<UserRole> {
}

public interface RolePermissionMapper extends BaseMapper<RolePermission> {
}

4. Service层实现

public interface UserService extends IService<User> {
    User getUserWithRoles(Long userId);
    boolean assignRoleToUser(Long userId, Long roleId);
    boolean removeRoleFromUser(Long userId, Long roleId);
}

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    
    @Autowired
    private RoleMapper roleMapper;
    
    @Autowired
    private UserRoleMapper userRoleMapper;
    
    @Override
    public User getUserWithRoles(Long userId) {
        User user = baseMapper.selectById(userId);
        if (user != null) {
            List<Role> roles = roleMapper.selectRolesByUserId(userId);
            // 设置角色信息(需要DTO或扩展实体)
        }
        return user;
    }
    
    @Override
    public boolean assignRoleToUser(Long userId, Long roleId) {
        UserRole userRole = new UserRole();
        userRole.setUserId(userId);
        userRole.setRoleId(roleId);
        return userRoleMapper.insert(userRole) > 0;
    }
    
    @Override
    public boolean removeRoleFromUser(Long userId, Long roleId) {
        QueryWrapper<UserRole> wrapper = new QueryWrapper<>();
        wrapper.eq("user_id", userId).eq("role_id", roleId);
        return userRoleMapper.delete(wrapper) > 0;
    }
}

5. Controller层

@RestController
@RequestMapping("/api/auth")
public class AuthController {
    
    @Autowired
    private UserService userService;
    
    @Autowired
    private RoleService roleService;
    
    @GetMapping("/users")
    public Result<List<User>> listUsers() {
        return Result.success(userService.list());
    }
    
    @PostMapping("/users")
    public Result<User> createUser(@RequestBody User user) {
        userService.save(user);
        return Result.success(user);
    }
    
    @PutMapping("/users/{id}")
    public Result<User> updateUser(@PathVariable Long id, @RequestBody User user) {
        user.setId(id);
        userService.updateById(user);
        return Result.success(user);
    }
    
    @DeleteMapping("/users/{id}")
    public Result<Boolean> deleteUser(@PathVariable Long id) {
        return Result.success(userService.removeById(id));
    }
    
    @PostMapping("/users/{userId}/roles/{roleId}")
    public Result<Boolean> assignRole(@PathVariable Long userId, @PathVariable Long roleId) {
        return Result.success(userService.assignRoleToUser(userId, roleId));
    }
}

// 统一返回结果
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Result<T> {
    private Integer code;
    private String message;
    private T data;
    
    public static <T> Result<T> success(T data) {
        return new Result<>(200, "成功", data);
    }
    
    public static <T> Result<T> error(String message) {
        return new Result<>(500, message, null);
    }
}

6. 自动配置类

@Configuration
@EnableConfigurationProperties(AuthProperties.class)
@ConditionalOnClass({SqlSessionFactory.class, MybatisPlusAutoConfiguration.class})
@ConditionalOnProperty(prefix = "auth", name = "enabled", havingValue = "true", matchIfMissing = true)
@AutoConfigureAfter(MybatisPlusAutoConfiguration.class)
public class AuthAutoConfiguration {
    
    @Bean
    @ConditionalOnMissingBean
    public UserMapper userMapper() {
        // MyBatis-Plus会自动配置Mapper
        return null; // 实际不需要显式创建,这里为了条件检测
    }
    
    @Bean
    @ConditionalOnMissingBean
    public UserService userService(UserMapper userMapper, RoleMapper roleMapper, UserRoleMapper userRoleMapper) {
        return new UserServiceImpl(userMapper, roleMapper, userRoleMapper);
    }
    
    @Bean
    @ConditionalOnMissingBean
    public AuthController authController(UserService userService, RoleService roleService) {
        return new AuthController(userService, roleService);
    }
    
    // 配置MyBatis-Plus分页插件(如果使用方没有配置)
    @Bean
    @ConditionalOnMissingBean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

7. 配置属性类

@Data
@ConfigurationProperties(prefix = "auth")
public class AuthProperties {
    private boolean enabled = true;
    private String tablePrefix = "sys_";
    private boolean initData = false;
}

8. 使用方配置

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/your_db
    username: your_username
    password: your_password
    driver-class-name: com.mysql.cj.jdbc.Driver

  # MyBatis-Plus配置
  mybatis-plus:
    mapper-locations: classpath*:mapper/**/*.xml
    configuration:
      map-underscore-to-camel-case: true
      log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

# Auth Starter配置
auth:
  enabled: true
  table-prefix: auth_  # 自定义表前缀
  init-data: true      # 是否初始化示例数据

9. 使用方POM依赖

<dependency>
    <groupId>com.example</groupId>
    <artifactId>auth-mybatisplus-starter</artifactId>
    <version>1.0.0</version>
</dependency>

<!-- 必须提供的依赖 -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.3.1</version>
</dependency>

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

10. SQL初始化脚本(可选)

-- 建表语句
CREATE TABLE IF NOT EXISTS auth_user (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) NOT NULL UNIQUE,
    password VARCHAR(100) NOT NULL,
    email VARCHAR(100),
    status INT DEFAULT 1,
    create_time DATETIME,
    update_time DATETIME
);

-- 其他表结构...

Spring Boot Starter配置详解

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>provided</scope>
    <optional>true</optional>  <!-- 可选依赖 -->
</dependency>

配置意义:

  • optional=true:表示这个依赖是可选的

  • 使用方可以根据实际使用的数据库添加相应的驱动依赖

  • 避免强制绑定特定数据库,提高starter的灵活性

2. 自动配置类注解解析

@Configuration
@EnableConfigurationProperties(AuthProperties.class)
@ConditionalOnClass({SqlSessionFactory.class, MybatisPlusAutoConfiguration.class})
@ConditionalOnProperty(prefix = "auth", name = "enabled", havingValue = "true", matchIfMissing = true)
@AutoConfigureAfter(MybatisPlusAutoConfiguration.class)
public class AuthAutoConfiguration {
    // 配置内容
}
各注解作用:
@Configuration

声明这是一个配置类

Spring会扫描这个类中的@Bean方法

@EnableConfigurationProperties(AuthProperties.class)

启用配置属性绑定

将application.yml中的配置映射到AuthProperties类

@ConditionalOnClass({SqlSessionFactory.class, MybatisPlusAutoConfiguration.class})

条件化配置:只有当类路径中存在这些类时才生效

确保只有在使用方引入了MyBatis-Plus时才启用配置

避免缺少依赖时出现ClassNotFoundException

@ConditionalOnProperty(prefix = "auth", name = "enabled", havingValue = "true", matchIfMissing = true)
prefix = "auth":配置前缀

name = "enabled":配置属性名

havingValue = "true":期望的值

matchIfMissing = true:如果配置缺失,默认启用

意义:允许使用方通过auth.enabled=false来禁用starter

@AutoConfigureAfter(MybatisPlusAutoConfiguration.class)

指定配置顺序:在MyBatis-Plus配置之后执行

确保数据库连接等基础配置先完成

避免因配置顺序问题导致的bean创建失败

3. Bean定义配置

@Bean
@ConditionalOnMissingBean
public UserService userService(UserMapper userMapper, RoleMapper roleMapper, UserRoleMapper userRoleMapper) {
    return new UserServiceImpl(userMapper, roleMapper, userRoleMapper);
}

@Bean

  • 声明这是一个Spring Bean

  • Spring会管理这个bean的生命周期

@ConditionalOnMissingBean

  • 条件化bean创建:只有当容器中不存在UserService类型的bean时才创建

  • 允许使用方自定义实现来覆盖默认实现

  • 提供扩展性和灵活性

4. 配置属性类解析

@Data
@ConfigurationProperties(prefix = "auth")
public class AuthProperties {
    private boolean enabled = true;
    private String tablePrefix = "sys_";
    private boolean initData = false;
}

@ConfigurationProperties(prefix = "auth")

  • 将配置文件中的属性绑定到Java对象

  • prefix = "auth":对应配置文件中的auth.*属性

属性默认值:

  • enabled = true:默认启用starter

  • tablePrefix = "sys_":默认表前缀,避免表名冲突

  • initData = false:默认不初始化数据,需要时手动开启

5. spring.factories配置

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.auth.config.AuthAutoConfiguration

配置意义:

  • Spring Boot自动配置的注册机制

  • 告诉Spring Boot在启动时自动加载AuthAutoConfiguration

  • 不需要使用方手动@Import配置类

6. 条件注解详解

常用条件注解:

@ConditionalOnClass

  • 当指定类存在于classpath时生效

  • 用于检测必要的依赖是否存在

@ConditionalOnMissingBean

  • 当容器中不存在指定类型的bean时生效

  • 用于提供默认实现,同时允许覆盖

@ConditionalOnProperty

  • 当配置属性满足条件时生效

  • 用于功能开关控制

@ConditionalOnWebApplication

  • 当应用是Web应用时生效

7. 表名配置解析

@TableName("${auth.table-prefix}user")
public class User {
    // 实体类
}

配置意义:

  • ${auth.table-prefix}:使用配置属性动态设置表名

  • 允许使用方自定义表前缀,避免与现有表名冲突

  • 支持多租户或不同环境下的表名区分

8. 使用方配置示例

# 使用方的application.yml
auth:
  enabled: true          # 启用auth starter
  table-prefix: "app_"   # 自定义表前缀
  init-data: true        # 初始化示例数据

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydb
    username: user
    password: pass
  redis:
    host: localhost
    port: 6379

9. 配置的最佳实践

1. 提供合理的默认值

java

private String tablePrefix = "sys_"; // 有意义的默认值

2. 支持功能开关

@ConditionalOnProperty(prefix = "auth", name = "enabled", matchIfMissing = true)

3. 避免强制的依赖

xml

<scope>provided</scope>
<optional>true</optional>

4. 支持自定义覆盖

@ConditionalOnMissingBean // 允许使用方提供自己的实现

5. 明确的配置顺序

@AutoConfigureAfter(MybatisPlusAutoConfiguration.class) // 确保依赖配置先完成
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐