spring中那些让你爱不释手的代码技巧(续集下)

简介: spring中那些让你爱不释手的代码技巧(续集下)

正文


五. 跨域问题的解决方案


关于跨域问题,前后端的解决方案还是挺多的,这里我重点说说spring的解决方案,目前有三种:

=========================.png


一.使用@CrossOrigin注解


@RequestMapping("/user")
@RestController
public class UserController {
    @CrossOrigin(origins = "http://localhost:8016")
    @RequestMapping("/getUser")
    public String getUser(@RequestParam("name") String name) {
        System.out.println("name:" + name);
        return "success";
    }
}

该方案需要在跨域访问的接口上加@CrossOrigin注解,访问规则可以通过注解中的参数控制,控制粒度更细。如果需要跨域访问的接口数量较少,可以使用该方案。


二.增加全局配置


@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowedMethods("GET", "POST")
                .allowCredentials(true)
                .maxAge(3600)
                .allowedHeaders("*");
    }
}

该方案需要实现WebMvcConfigurer接口,重写addCorsMappings方法,在该方法中定义跨域访问的规则。这是一个全局的配置,可以应用于所有接口。


三.自定义过滤器


@WebFilter("corsFilter")
@Configuration
public class CorsFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        httpServletResponse.setHeader("Access-Control-Allow-Origin", "*");
        httpServletResponse.setHeader("Access-Control-Allow-Methods", "POST, GET");
        httpServletResponse.setHeader("Access-Control-Max-Age", "3600");
        httpServletResponse.setHeader("Access-Control-Allow-Headers", "x-requested-with");
        chain.doFilter(request, response);
    }
    @Override
    public void destroy() {
    }
}

该方案通过在请求的header中增加Access-Control-Allow-Origin等参数解决跨域问题。

顺便说一下,使用@CrossOrigin注解 和 实现WebMvcConfigurer接口的方案,spring在底层最终都会调用到DefaultCorsProcessor类的handleInternal方法:

=============================.png

最终三种方案殊途同归,都会往header中添加跨域需要参数,只是实现形式不一样而已。


六. 如何自定义starter


以前在没有使用starter时,我们在项目中需要引入新功能,步骤一般是这样的:

  • 在maven仓库找该功能所需jar包
  • 在maven仓库找该jar所依赖的其他jar包
  • 配置新功能所需参数

以上这种方式会带来三个问题:

  1. 如果依赖包较多,找起来很麻烦,容易找错,而且要花很多时间。
  2. 各依赖包之间可能会存在版本兼容性问题,项目引入这些jar包后,可能没法正常启动。
  3. 如果有些参数没有配好,启动服务也会报错,没有默认配置。

「为了解决这些问题,springboot的starter机制应运而生」

starter机制带来这些好处:

  1. 它能启动相应的默认配置。
  2. 它能够管理所需依赖,摆脱了需要到处找依赖 和 兼容性问题的困扰。
  3. 自动发现机制,将spring.factories文件中配置的类,自动注入到spring容器中。
  4. 遵循“约定大于配置”的理念。

在业务工程中只需引入starter包,就能使用它的功能,太爽了。

下面用一张图,总结starter的几个要素:

===================================.png

接下来我们一起实战,定义一个自己的starter。

第一步,创建id-generate-starter工程:

0.png

其中的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>
    <version>1.3.1</version>
    <groupId>com.sue</groupId>
    <artifactId>id-generate-spring-boot-starter</artifactId>
    <name>id-generate-spring-boot-starter</name>
    <dependencies>
        <dependency>
            <groupId>com.sue</groupId>
            <artifactId>id-generate-spring-boot-autoconfigure</artifactId>
            <version>1.3.1</version>
        </dependency>
    </dependencies>
</project>

第二步,创建id-generate-spring-boot-autoconfigure工程:

00.png

该项目当中包含:

  • pom.xml
  • spring.factories
  • IdGenerateAutoConfiguration
  • IdGenerateService
  • IdProperties 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">
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.4.RELEASE</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <version>1.3.1</version>
    <groupId>com.sue</groupId>
    <artifactId>id-generate-spring-boot-autoconfigure</artifactId>
    <name>id-generate-spring-boot-autoconfigure</name>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

spring.factories配置如下:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.sue.IdGenerateAutoConfiguration

IdGenerateAutoConfiguration类:

@ConditionalOnClass(IdProperties.class)
@EnableConfigurationProperties(IdProperties.class)
@Configuration
public class IdGenerateAutoConfiguration {
    @Autowired
    private IdProperties properties;
    @Bean
    public IdGenerateService idGenerateService() {
        return new IdGenerateService(properties.getWorkId());
    }
}

IdGenerateService类:

public class IdGenerateService {
    private Long workId;
    public IdGenerateService(Long workId) {
        this.workId = workId;
    }
    public Long generate() {
        return new Random().nextInt(100) + this.workId;
    }
}

IdProperties类:

@ConfigurationProperties(prefix = IdProperties.PREFIX)
public class IdProperties {
    public static final String PREFIX = "sue";
    private Long workId;
    public Long getWorkId() {
        return workId;
    }
    public void setWorkId(Long workId) {
        this.workId = workId;
    }
}

这样在业务项目中引入相关依赖:

<dependency>
      <groupId>com.sue</groupId>
      <artifactId>id-generate-spring-boot-starter</artifactId>
      <version>1.3.1</version>
</dependency>

就能使用注入使用IdGenerateService的功能了

@Autowired
  private IdGenerateService idGenerateService;

完美。


七.项目启动时的附加功能


有时候我们需要在项目启动时定制化一些附加功能,比如:加载一些系统参数、完成初始化、预热本地缓存等,该怎么办呢?

好消息是springboot提供了:

  • CommandLineRunner
  • ApplicationRunner

这两个接口帮助我们实现以上需求。

它们的用法还是挺简单的,以ApplicationRunner接口为例:

@Component
public class TestRunner implements ApplicationRunner {
    @Autowired
    private LoadDataService loadDataService;
    public void run(ApplicationArguments args) throws Exception {
        loadDataService.load();
    }
}

实现ApplicationRunner接口,重写run方法,在该方法中实现自己定制化需求。

如果项目中有多个类实现了ApplicationRunner接口,他们的执行顺序要怎么指定呢?

答案是使用@Order(n)注解,n的值越小越先执行。当然也可以通过@Priority注解指定顺序。

springboot项目启动时主要流程是这样的:

000.png

SpringApplication类的callRunners方法中,我们能看到这两个接口的具体调用:

0000.png

最后还有一个问题:这两个接口有什么区别?

  • CommandLineRunner接口中run方法的参数为String数组
  • ApplicationRunner中run方法的参数为ApplicationArguments,该参数包含了String数组参数 和 一些可选参数。


唠唠家常


写着写着又有这么多字了,按照惯例,为了避免篇幅过长,今天就先写到这里。预告一下,后面会有AOP、BeanPostProcessor、Configuration注解等核心知识点的专题,每个主题的内容都挺多的,可以期待一下喔。

相关文章
|
5月前
|
安全 Java 应用服务中间件
Spring Boot + Java 21:内存减少 60%,启动速度提高 30% — 零代码
通过调整三个JVM和Spring Boot配置开关,无需重写代码即可显著优化Java应用性能:内存减少60%,启动速度提升30%。适用于所有在JVM上运行API的生产团队,低成本实现高效能。
582 3
|
4月前
|
人工智能 监控 Java
零代码改造 + 全链路追踪!Spring AI 最新可观测性详细解读
Spring AI Alibaba 通过集成 OpenTelemetry 实现可观测性,支持框架原生和无侵入探针两种方式。原生方案依赖 Micrometer 自动埋点,适用于快速接入;无侵入探针基于 LoongSuite 商业版,无需修改代码即可采集标准 OTLP 数据,解决了原生方案扩展性差、调用链易断链等问题。未来将开源无侵入探针方案,整合至 AgentScope Studio,并进一步增强多 Agent 场景下的观测能力。
2081 62
|
4月前
|
安全 Java 测试技术
《深入理解Spring》单元测试——高质量代码的守护神
Spring测试框架提供全面的单元与集成测试支持,通过`@SpringBootTest`、`@WebMvcTest`等注解实现分层测试,结合Mockito、Testcontainers和Jacoco,保障代码质量,提升开发效率与系统稳定性。
|
5月前
|
安全 IDE Java
Spring 的@FieldDefaults和@Data:Lombok 注解以实现更简洁的代码
本文介绍了如何在 Spring 应用程序中使用 Project Lombok 的 `@Data` 和 `@FieldDefaults` 注解来减少样板代码,提升代码可读性和可维护性,并探讨了其适用场景与限制。
206 0
Spring 的@FieldDefaults和@Data:Lombok 注解以实现更简洁的代码
|
8月前
|
Java 数据库连接 数据库
Spring boot 使用mybatis generator 自动生成代码插件
本文介绍了在Spring Boot项目中使用MyBatis Generator插件自动生成代码的详细步骤。首先创建一个新的Spring Boot项目,接着引入MyBatis Generator插件并配置`pom.xml`文件。然后删除默认的`application.properties`文件,创建`application.yml`进行相关配置,如设置Mapper路径和实体类包名。重点在于配置`generatorConfig.xml`文件,包括数据库驱动、连接信息、生成模型、映射文件及DAO的包名和位置。最后通过IDE配置运行插件生成代码,并在主类添加`@MapperScan`注解完成整合
1352 1
Spring boot 使用mybatis generator 自动生成代码插件
|
7月前
|
安全 Java Nacos
0代码改动实现Spring应用数据库帐密自动轮转
Nacos作为国内被广泛使用的配置中心,已经成为应用侧的基础设施产品,近年来安全问题被更多关注,这是中国国内软件行业逐渐迈向成熟的标志,也是必经之路,Nacos提供配置加密存储-运行时轮转的核心安全能力,将在应用安全领域承担更多职责。
ext portal+dwr+spring实现个性主页面拖拉效果的核心代码
ext portal+dwr+spring实现个性主页面拖拉效果的核心代码
160 7
|
8月前
|
Java 调度 流计算
基于Java 17 + Spring Boot 3.2 + Flink 1.18的智慧实验室管理系统核心代码
这是一套基于Java 17、Spring Boot 3.2和Flink 1.18开发的智慧实验室管理系统核心代码。系统涵盖多协议设备接入(支持OPC UA、MQTT等12种工业协议)、实时异常检测(Flink流处理引擎实现设备状态监控)、强化学习调度(Q-Learning算法优化资源分配)、三维可视化(JavaFX与WebGL渲染实验室空间)、微服务架构(Spring Cloud构建分布式体系)及数据湖建设(Spark构建实验室数据仓库)。实际应用中,该系统显著提升了设备调度效率(响应时间从46分钟降至9秒)、设备利用率(从41%提升至89%),并大幅减少实验准备时间和维护成本。
416 0
|
10月前
|
人工智能 前端开发 Java
十几行代码实现 Manus,Spring AI Alibaba Graph 快速预览
Spring AI Alibaba Graph 的核心开发已完成,即将发布正式版本。开发者可基于此轻松构建工作流、智能体及多智能体系统,功能丰富且灵活。文章通过三个示例展示了其应用:1) 客户评价处理系统,实现两级问题分类与自动处理;2) 基于 ReAct Agent 的天气预报查询系统,循环执行用户指令直至完成;3) 基于 Supervisor 多智能体的 OpenManus 实现,简化了流程控制逻辑并优化了工具覆盖度。此外,还提供了运行示例的方法及未来规划,欢迎开发者参与贡献。