结合SpringBoot中的自定义starter中间件开发的方式,统一处理所有需要白名单的地方
对接口的包装提供出接口服务的门面模式后面补充
设计知识点
自定义注解的使用
- @Retention(RetentionPolicy.RUNTIME)
@Retention 注解用于指定自定义注解的保留策略。RetentionPolicy.RUNTIME 表示该注解在运行时也可见,因此可以通过反射机制读取。 - @Target(ElementType.METHOD)
@Target 注解用于指定自定义注解可以应用的 Java 元素类型。ElementType.METHOD 表示这个注解只能应用于方法上。 - key() default “”:当在方法上使用 @DoDoor 注解时,可以选择性地提供 key 的值,如果不提供,则默认为空字符串
- returnJson() default “”::当在方法上使用 @DoDoor 注解时,可以选择性地提供 key 的值,如果不提供,则默认为空字符串
总结:@DoDoor 是一个自定义注解,可以应用于方法上,并提供两个可选的元素 key 和 returnJson。使用此注解可以为方法添加额外的元数据,这些元数据可以在运行时通过反射被读取和使用
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DoDoor {
String key() default "";
String returnJson() default "";
}
SpringBoot的starter中间件开发方式
流程:StarterServiceProperties读取application.yml配置文件中配置内容,并将配置文件内容的值设置给StarterServiceProperties类的属性,StarterAutoConfigure类引入StarterServiceProperties类型的属性并在方法中获取StarterServiceProperties从配置文件获取的值设置给starterService类的属性
StarterServiceProperties 类使用了 @ConfigurationProperties(“itstack.door”) 注解,spring Boot 会查找配置文件(如 application.properties 或 application.yml)中前缀为 itstack.door 的属性,并将它们自动绑定到 StarterServiceProperties 类的字段上。StarterServiceProperties 类中,定义了一个 userStr 字段,以及对应的 getter 和 setter 方法, Spring Boot 通过反射调用这些方法来设置字段的值。
@ConfigurationProperties("itstack.door")
public class StarterServiceProperties {
private String userStr;
public String getUserStr() {
return userStr;
}
public void setUserStr(String userStr) {
this.userStr = userStr;
}
}
@Configuration 注解表明该类是一个配置类,它定义了一个或多个 @Bean 方法,这些方法会被 Spring 容器处理,生成 Spring 管理的 bean。在自动配置类中,通常不直接定义 @Bean 方法,而是使用其他机制(如条件注解)来间接地配置 beans。
@ConditionalOnClass 是一个条件注解,它表明只有在指定的类存在于类路径上时,才会应用配置。@ConditionalOnClass(StarterService.class) 表示只有当 StarterService 类在类路径上可用时,StarterAutoConfigure 类的配置才会被激活。
@EnableConfigurationProperties(StarterServiceProperties.class) 表示 Spring Boot 会将 StarterServiceProperties 类中的属性绑定到配置文件中的属性,并将 StarterServiceProperties 的实例作为 bean 注册到 Spring 容器中,以便在其他地方注入使用
@Bean: 这个注解告诉 Spring 容器这个方法会返回一个StarterService 对象,并且这个对象会被 Spring 管理。
@ConditionalOnMissingBean: 这个注解表示只有当 Spring 容器中不存在指定类型的 bean 时,当前带有 @Bean 注解的方法才会被执行
@ConditionalOnProperty:据配置文件中的属性值来决定是否创建 bean,只有当 itstack.door.enabled 的值为 true 时,starterService 方法才会被执行,进而创建 StarterService 类型的 bean
@Configuration
@ConditionalOnClass(StarterService.class)
@EnableConfigurationProperties(StarterServiceProperties.class)
public class StarterAutoConfigure {
@Autowired
private StarterServiceProperties properties;
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "itstack.door", value = "enabled", havingValue = "true")
StarterService starterService() {
return new StarterService(properties.getUserStr());
}
}
StarterService
public class StarterService {
private String userStr;
public StarterService(String userStr) {
this.userStr = userStr;
}
public String[] split(String separatorChar) {
return StringUtils.split(this.userStr, separatorChar);
}
}
面向切面编程和反射
@Aspect 注解用于标识一个类作为切面(Aspect),而 @Component 注解则用于将类声明为 Spring 容器管理的组件。由于 DoJoinPoint 类被标记为 @Component,Spring 会在启动时自动扫描并创建这个类的实例,并将其作为切面注册到 AOP 代理中。这样,当应用运行时,任何匹配 doorPointcut 切点的方法调用都会触发相应的通知逻辑。
在 Spring AOP(Aspect-Oriented Programming,面向切面编程)中,@Pointcut 注解用于定义一个切点(pointcut),即一个或多个连接点(joinpoint)的集合,这些连接点通常对应于方法执行、异常抛出等程序执行过程中的特定点。
定义了一个切点 aopPoint,它匹配所有标注有 cn.bugstack.design.door.annotation.DoDoor 注解的方法,也就是说所有标注DoDoor注解的方法都会被拦截,切点aopPoint() 方法本身不需要任何逻辑,因为 @Pointcut 注解只是声明了一个切点,具体的通知(advice)逻辑将在其他地方定义
@Aspect
@Component
public class DoJoinPoint {
private Logger logger = LoggerFactory.getLogger(DoJoinPoint.class);
@Autowired
private StarterService starterService;
@Pointcut("@annotation(cn.bugstack.design.door.annotation.DoDoor)")
public void aopPoint() {
}
@Around("aopPoint()")
public Object doRouter(ProceedingJoinPoint jp) throws Throwable {
Method method = getMethod(jp);
DoDoor door = method.getAnnotation(DoDoor.class);
String keyValue = getFiledValue(door.key(), jp.getArgs());
logger.info("itstack door handler method:{} value:{}", method.getName(), keyValue);
if (null == keyValue || "".equals(keyValue)) return jp.proceed();
String[] split = starterService.split(",");
// 白名单过滤
for (String str : split) {
if (keyValue.equals(str)) {
return jp.proceed();
}
}
// 拦截
return returnObject(door, method);
}
}
controller定义
@RestController 注解用于标识一个类为控制器(Controller),并且这个控制器返回的数据将自动转换为 JSON 或 XML 等格式,直接作为 HTTP 响应体返回给客户端,而不是视图名称。这意味着你不需要为返回的数据添加 @ResponseBody 注解,因为 @RestController 已经隐含了 @ResponseBody 的功能。当访问/api/queryUserInfo请求时,queryUserInfo方法会被调用,并且返回UserInfo对象 会自动转换为 JSON 或 XML(取决于客户端的请求头)并作为 HTTP 响应返回。在普通的 Spring MVC 项目中,也可以使用 @Controller 和 @ResponseBody 来达到类似的效果,但 @RestController 提供了更简洁的语法。
@RestController
public class HelloWorldController {
@Value("${server.port}")
private int port;
@DoDoor(key = "userId", returnJson = "{\"code\":\"1111\",\"info\":\"非白名单可访问用户拦截!\"}")
@RequestMapping(path = "/api/queryUserInfo", method = RequestMethod.GET)
public UserInfo queryUserInfo(@RequestParam String userId) {
return new UserInfo("虫虫:" + userId, 19, "天津市南开区旮旯胡同100号");
}
}
SpringApplication
@SpringBootApplication是一个组合注解,它包括了@Configuration、@EnableAutoConfiguration和@ComponentScan三个注解的功能。
@Configuration:表明该类是一个配置类,用于定义bean。
@EnableAutoConfiguration:让Spring Boot根据添加的jar依赖自动配置应用。比如添加了spring-boot-starter-web,则自动配置成web应用。
@ComponentScan:让Spring扫描到Configuration类并把它加入到程序上下文。
@SpringBootApplication
@Configuration
public class HelloWorldApplication {
public static void main(String[] args) {
SpringApplication.run(HelloWorldApplication.class, args);
}
}
白名单效果
非白名单效果