news 2026/5/13 12:11:14

Spring Boot(1)【转】

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring Boot(1)【转】

以下转载和参考自:Spring Boot开发 - 廖雪峰的官方网站

1、配置Spring Boot

SpringBoot包含了Spring MVC和Tomca、日志等,启动SpringBoot就开启了Spring MVC和Tomcat。一个Spring Boot项目的组织结构如下所示,可以看到,Spring Boot中已经不需要专门的webapp目录了:

springboot
├── pom.xml
├── src
│ └── main
│ ├── java
│ ├── xsl
│ ├── Application.java
│ ├── entity
│ │ └── User.java
│ ├── service

│ │ ├── UserMapper.java
│ │ └── UserService.java
│ └── web
│ └── UserController.java
│ └── resources
│ ├── application.yml
│ ├── logback-spring.xml
│ ├── static
│ ├── css
│ └── bootstrap.css
│ └── js
│ ├── bootstrap.js
│ └── jquery.js
│ └── templates
│ ├── hello.html
│ └── signin.html
└── target

其中resources目录下的application.yml为Spring Boot默认配置文件,也可以使用.properties文件,如下所示为其和.properties的对比:

# application.yml server: #先从环境变量中查找APP_PORT,查找不到的话再使用8080对port赋值,可以在测试环境中使用默认值,正式环境使用环境变量中设置的值 port: ${APP_PORT:8080} spring: application: #先从环境变量中查找APP_NAME,查找不到的话再使用app_name对port赋值,可以在测试环境中使用默认值,正式环境使用环境变量中设置的值 name: ${APP_NAME:app_name} datasource: url: jdbc:mysql://127.0.0.1:3306/my_database?serverTimezone=GMT&useUnicode=true&characterEncoding=UTF-8&useSSL=false username: sa password: 123456 #使用mysql数据库驱动 driver-class-name: com.mysql.cj.jdbc.Driver #配置JDBC连接池 hikari: auto-commit: false connection-timeout: 3000 validation-timeout: 3000 max-lifetime: 60000 maximum-pool-size: 20 minimum-idle: 1 pebble: # 默认为".pebble",改为"": suffix: # 开发阶段禁用模板缓存: cache: false
# application.properties spring.application.name = ${ APP_NAME:unnamed } spring.datasource.url = jdbc:hsqldb:file:testdb spring.datasource.username = sa spring.datasource.password = spring.datasource.dirver - class - name = org.hsqldb.jdbc.JDBCDriver spring.datasource.hikari.auto - commit = false spring.datasource.hikari.connection - timeout = 3000 spring.datasource.hikari.validation - timeout = 3000 spring.datasource.hikari.max - lifetime = 60000 spring.datasource.hikari.maximum - pool - size = 20 spring.datasource.hikari.minimum - idle = 1 io.pebbletemplates.pebble.suffix = io.pebbletemplates.pebble.cache = false

如上所示,对于地址、用户名、密码这种类型的配置信息,可以使用环境变量,我们可以在启动程序时传入环境变量,如java -D PASSWORD=123456 -jar app.jar。

模板引擎中一些常用的默认属性值:

pebble.prefix=/templates/ 模板前缀,这里模板必须放在`/templates`目录
pebble.suffix=.pebble 模板后缀
pebble.encoding=UTF-8 模板编码
pebble.cache=true 使用模板缓存
pebble.content-type=text/html
pebble.defaultLocale=null
pebble.strictVariables=false

resources下的logback-spring.xml(也可以使用logback.xml)是日志logback配置文件,如下为一个标准的写法,它定义了一个控制台输出和文件输出,其中第三行<include resource=.../>引入了Spring Boot的一个缺省配置,这样我们就可以引用CONSOLE_LOG_PATTERN、FILE_LOG_PATTERN等变量:

<?xml version="1.0" encoding="UTF-8"?> <configuration> <include resource="org/springframework/boot/logging/logback/defaults.xml" /> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>${CONSOLE_LOG_PATTERN}</pattern> <charset>utf8</charset> </encoder> </appender> <appender name="APP_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender"> <encoder> <pattern>${FILE_LOG_PATTERN}</pattern> <charset>utf8</charset> </encoder> <file>app.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy"> <maxIndex>1</maxIndex> <fileNamePattern>app.log.%i</fileNamePattern> </rollingPolicy> <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"> <MaxFileSize>1MB</MaxFileSize> </triggeringPolicy> </appender> <root level="INFO"> <appender-ref ref="CONSOLE" /> <appender-ref ref="APP_LOG" /> </root> </configuration>

如下为pom.xml中引入的相关依赖,其中的<parent>...</parent>表示从spring-boot-starter-parent继承,这样就可以引入Spring Boot的预置配置。然后我们引入了Spring Boot、模板引擎(视图解析器)pebble、JDBC驱动的依赖。可以看到引入的Spring Boot没有指定版本号,因为引入的<parent>内已经指定了,引入的JDBC驱动也没有指定版本号,因为hsqldb已在spring-boot-starter-jdbc中预置了版本号:

<?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"> <!-- 从spring-boot-starter-parent继承 --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.0.RELEASE</version> </parent> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>Spring-Boot</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <!-- 设置使用的版本 --> <java.version>11</java.version> <pebble.version>3.1.2</pebble.version> <mybatis.version>3.5.4</mybatis.version> <mybatis-spring.version>2.0.4</mybatis-spring.version> </properties> <dependencies> <!-- 集成Spring Boot --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 集成Spring Boot JDBC--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <!-- 集成Pebble View --> <dependency> <groupId>io.pebbletemplates</groupId> <artifactId>pebble-spring-boot-starter</artifactId> <version>${pebble.version}</version> </dependency> <!--MyBatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>${mybatis.version}</version> </dependency> <!--集成MyBatis与Spring的库--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>${mybatis-spring.version}</version> </dependency> <!-- JDBC驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> </dependencies> </project>

Application 为Spring Boot启动类。Spring Boot要求main()方法所在的启动类必须放到根package下,我们项目中的根package为xsl,xsl下还有xsl.entity、xsl.service、xsl.web等子package。Spring Boot启动类使用注解@SpringBootApplication,该注解实际上又包含了:

  • @SpringBootConfiguration
    • @Configuration
  • @EnableAutoConfiguration
    • @AutoConfigurationPackage
  • @ComponentScan
/*Application.java*/ package xsl; @SpringBootApplication @MapperScan("xsl.service") public class Application { //Spring Boot启动类 public static void main(String[] args) throws Exception { SpringApplication.run(Application.class, args); //启动Spring Boot } @Bean//提供创建WebMvcConfigurer方法,使Spring MVC自动处理静态文件,并且映射路径为/static/** WebMvcConfigurer createWebMvcConfigurer(@Autowired HandlerInterceptor[] interceptors) { return new WebMvcConfigurer() { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { // 映射路径`/static/`到classpath路径: registry.addResourceHandler("/static/**") .addResourceLocations("classpath:/static/"); } }; } @Bean //提供给MyBatis创建SqlSessionFactoryBean对象的方法 SqlSessionFactoryBean createSqlSessionFactoryBean(@Autowired DataSource dataSource) { var sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dataSource); return sqlSessionFactoryBean; } }

下面是JavaBean、Service、Controller的代码:

/*User.java*/ package xsl.entity; public class User { private Long id; private String email; private String password; private String name; private long createdAt; public Long getId() { return id; } public void setId(Long id) { this.id = id; } ...... }
/*UserMapper.java*/ package xsl.service; //User类和数据库users表之间映射的Mapper public interface UserMapper { //SELECT查询方法 @Select("SELECT * FROM users WHERE id = #{id}") //getById()方法里对应执行的SQL语句 User getById(@Param("id") long id); //通过主键获得一行记录的方法,@Param指示该参数与SQL语句中"id"参数对应 @Select("SELECT id, name, created_time AS createdAt FROM users LIMIT #{offset}, #{maxResults}") //User类成员名要与查询记录的列名相同,列名和类属性名不同的话使用AS List<User> getAllLimit(@Param("offset") int offset, @Param("maxResults") int maxResults); //INSERT插入方法 //如果表的id是自增主键,那么,我们在SQL中不传入id,但希望获取插入后的主键,需要再加一个@Options注解 @Options(useGeneratedKeys = true/*设置自增主键*/, keyProperty = "id"/*主键值使用JavaBean的id属性值*/, keyColumn = "id"/*设置主键列名*/) @Insert("INSERT INTO users (email, password, name, createdAt) VALUES (#{user.email}, #{user.password}, #{user.name}, #{user.createdAt})") void insert(@Param("user") User user); //UPDATE更新方法 @Update("UPDATE users SET name = #{user.name}, createdAt = #{user.createdAt} WHERE id = #{user.id}") void update(@Param("user") User user); //DELETE删除方法 @Delete("DELETE FROM users WHERE id = #{id}") void deleteById(@Param("id") long id); }
/*UserService.java*/ package xsl.service; @Component public class UserService { final Logger logger = LoggerFactory.getLogger(getClass()); @Autowired UserMapper userMapper; ...... public User register(String email, String password, String name) { User user = new User(); user.setName(name); user.setEmail(email); user.setPassword(password); user.setCreatedAt(System.currentTimeMillis()); userMapper.insert(user); logger.info("user registered: {}", user.getEmail()); return user; } ...... }
/*UserController.java*/ package xsl.web; @Controller public class UserController { @Autowired UserService userService; @GetMapping("/test") public ModelAndView test(){ return new ModelAndView("test.html"); } @GetMapping("/index") public ModelAndView index(HttpSession session) { User user = (User) session.getAttribute("__user__"); Map<String, Object> model = new HashMap<>(); if (user != null) { model.put("user", user); } return new ModelAndView("index.html", model); } @PostMapping("/register") public ModelAndView doRegister(@RequestParam("name") String name, @RequestParam("password") String password, @RequestParam("email") String email) { User user = userService.register(email, password, name); return new ModelAndView("redirect:/signin"); } }

下面为templates下的页面文件及其展示效果:

test.html

<html> <body> <ul> <li>Coffee</li> <li>Tea</li> <li>Milk</li> </ul> </body> </html>

index.html、_base.html

{% extends "_base.html" %} {% block main %} {% if user == null %} <h3>Welcome!</h3> <p><a href="/signin">Sign In</a> or <a href="/register">Register</a> {% else %} <h3>Welcome {{ user.name }}!</h3> {% endif %} {% endblock %}
<!doctype html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <title>Spring Mvc Web App</title> <script src="/static/js/jquery.js"></script> <link href="/static/css/bootstrap.css" rel="stylesheet"> </head> <body style="font-size:16px"> <div class="d-flex flex-column flex-md-row align-items-center p-3 px-md-4 mb-3 bg-white border-bottom shadow-sm"> <h5 class="my-0 mr-md-auto font-weight-normal"><a href="/">Home</a></h5> <nav class="my-2 mr-md-3"> <a class="p-2 text-dark" target="_blank" href="https://www.liaoxuefeng.com/wiki/1252599548343744">Learn</a> </nav> {% if user==null %} <a href="/signin" class="btn btn-outline-primary">Sign In</a> {% else %} <span>Welcome, <a href="/user/profile">{{ user.name }}</a></span> &nbsp;&nbsp;&nbsp; <a href="/signout" class="btn btn-outline-primary">Sign Out</a> {% endif %} </div> <div class="container" style="max-width: 960px"> <div class="row"> <div class="col-12 col-md"> {% block main %} {% endblock %} </div> </div> <footer class="pt-4 my-md-5 pt-md-5 border-top"> <div class="row"> <div class="col-12 col-md"> <h5>Copyright&copy;2020 {{ __time__ }}</h5> <p> <a target="_blank" href="https://www.liaoxuefeng.com/">Learn</a> - <a target="_blank" href="https://www.liaoxuefeng.com/">Download</a> - <a target="_blank" href="https://github.com/michaelliao">Github</a> </p> </div> </div> </footer> </div> </body> </html>

我们可以在项目中点击运行来直接运行当前的SringBoot项目,发布的时候可以使用maven package生成jar包,将jar包和classes目录拷贝出来,使用java -jar jarName.jar来运行SpringBott,如下所示(实际运行的时候发现 在classes目录下仅保留application.yml配置文件程序也可以正常运行):

2、自动重启和打包

可以添加如下的依赖,然后我们开发的时候在IDEA中修改代码后不用重启服务,Spring Boot应用可以自动重启(热部署:只要classpath路径下发生变化,项目就会自动重启)。不起作用的话可以先设置Settings-Build-Compiler-勾选Build Project automatically,然后快捷键ctrl + shift + alt + /,选择Registry, 勾上 Compiler autoMake allow when app running。默认配置下,针对/static、/public和/templates目录中的文件修改,不会自动重启,因为禁用缓存后(yml配置文件里的pebble:cache为false),这些文件在IDEA里修改的话可以实时更新。

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency>

如下所示,可以添加spring-boot-maven-plugin插件,这样使用Maven打包的时候创建的是包含所有依赖(包括内嵌的Tomcat)的jar(另外还会生成一个以.jar.original结尾的的只包含我们自己类的包),所以将其上传到服务器上后可以直接使用java -jar jar_name.jar命令来运行。

<build> <finalName>awesome-app</finalName> <!-- 指定jar包名称 --> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>2.3.0.RELEASE</version> <!-- 与<parent>...</parent>中版本号一致 --> </plugin> </plugins> </build>

使用spring-boot-maven-plugin插件打包Spring Boot应用的话,因为包含了所有依赖,所以生成的jar包会有几十兆,每次修改代码都上传这个大文件的话就有点麻烦。可以使用使用spring-boot-thin-launcher,如下所示修改pom.xml,这样生成的jar包只有几十K(只包含我们自己代码编译后的class以及一些其它信息),使用java -jar jar_name.jar命令运行这个jar包的话,会先在指定目录搜索看看依赖的jar包是否都存在,如果不存在,先从Maven中央仓库下载到本地,然后,再执行main()方法启动程序。

<build> <finalName>awesome-app</finalName> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <dependencies> <dependency> <groupId>org.springframework.boot.experimental</groupId> <artifactId>spring-boot-thin-layout</artifactId> <version>1.0.27.RELEASE</version> </dependency> </dependencies> </plugin> </plugins> </build>

spring-boot-thin-launcher在启动时搜索的依赖库默认目录是用户主目录的.m2,我们也可以指定下载目录,例如,将下载目录指定为当前目录:java -D thin.root=. -jar jar_name.jar,这样当前目录下就会生成一个repository目录来存放依赖包。也可以专门用来下载依赖项而不执行程序:java -D thin.dryrun=true -D thin.root=. -jar jar_name.jar,这称之为“预热”(warm up)。如果服务器不允许从外网下载文件,那么可以在本地预热,然后把repository目录上传到服务器,只要依赖项没有变化,后续改动只需要上传jar包即可。

3、使用Actuator进行监控

前面我们已经介绍了使用JMX对Java应用程序包括JVM进行监控,Spring Boot也内置了一个监控功能,它叫Actuator。Actuator会把它能收集到的所有信息都暴露给JMX,其监控功能更丰富。Actuator 2默认会公开/actuator/health和/actuator/info两个地址(health和info被Actuator称为endpoint端点),比如我们访问http://localhost:8080/actuator/health的话,默认就会返回如下的内容。许多网关需要一个URL来探测后端应用是否存活,这个health端点可以提供给网关使用。

{ "status": "UP" }

使用Actuator只需添加如下依赖:

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

想要暴露更多的访问点的话,需要在application.yml中进行配置,如下所示暴露了info、health、beans、env四个端点。一些高危端点可能还需要配置management.endpoints.端点名.enabled=true才能开启,可以通过http://localhost:8080/actuator 来查看所有暴露出来的端点。

management: endpoints: web: exposure: include: info, health, beans, env

如果要暴露的端点比较多,可以配置management.endpoints.web.exposure.include=* 来开启全部端点,然后配置management.endpoints.web.exposure.exclude来指定不包含的端点。如果是通过JMX方式进行访问,则配置management.endpoints.jmx.exposure.include。

Actuator会暴露服务的内部详细信息,所以为了安全起见,建议添加安全控制的相关依赖spring-boot-starter-security,设置相关安全校验规则后,访问端点的时候就会提示输入用户名和密码,如下所示:

可以配置health端点显示详细的服务健康信息,通过配置 management.endpoint.health.show-details=always,如下所示,开启了详细信息展示后,会展示项目磁盘空间以及使用到的redis、数据库等组件的健康信息(status为down表示组件异常)。

Spring boot中包含了大量的HealthIndicator实现类来为health端点提供健康信息,如DiskSpaceHealthIndicator用来实现检查磁盘,RedisHealthIndicator用来检查redis,可以通过实现HealthIndicator的接口来增加自定义的健康组件信息(将该实现类注册为spring bean),如下所示,增加了MyHealthIndicator后,health端点就会增加MyHealthIndicator的健康信息。

info端点用来展示服务的相关信息,比如项目构建信息、git信息、自定义信息,可以在配置文件中来配置要显示的自定义信息,比如在management中配置了info.app.encoding=UTF-8后,访问info端点展示的JSON内容中"encoding"的值就会为"UTF-8",如下所示。

可以通过继承InfoContributor,然后实现该类中的 contribute 方法来实现自定义的info:

metrics端点可以查看jvm已用内存、tomcat当前线程数、request 次数和时间、http 请求调用情况(比如显示 10 个请求量最大,耗时最长的 URL)、数据库连接池hikaricp等指标信息。通过访问/actuator/metrics 接口可以看到metrics统计的所有指标,通过访问 /actuator/metrics/指标名 就可以获得指标的详细信息,比如访问 /actuator/metrics/jvm.memory.max的话,就会返回jvm最大内存的相关信息。

可以添加自定义指标到metrics端点,自定义指标可以为Gauge、Counter、Timer、Summary四种类型之一。Gauge(计量器)用来计量一些数据,如下所示我们通过Metrics.gauge()方法设置了user.test.gauge指标,并将其设置为3,通过访问/actuator/metrics/user.test.gauge 可以查看到该指标的value值为3。

Counter是计数器类型,如下所示,我们创建了名称为user.counter.total的计数器,然后在processCollectResult()方法中对其加1,假设processCollectResult()方法被调用了3次,那么查看该计数器的话,COUNT value就是3:

Timer是一种计时器,如下所示我们创建了名为user.test.timer的计时器,然后通过计时器的record()方法来调用createOrder()方法,假设hello()方法被执行了3次,那么查看该计时器的话,会看到其被使用的次数,总时长和最大时长:

Summary(摘要)用来统计数据,它不同于计量器(设置一个数的值)和计数器(递增一个数的值),它可以记录多个数值。如下所示创建了一个名为user.test.summary的摘要,然后通过其record()方法记录了多个数值:

/heapdump端点可以生成一个jvm的内存快照,可以通过JDK 自带的 Jvm 监控工具 VisualVM 打开此文件查看内存快照,如下所示:

/threaddump端点可以查看服务的线程的相关情况,如线程ID、线程状态、线程堆栈等:

/loggers端点可以查看服务中配置的所有logger的信息,还可以通过该端点来改变服务使用logger的等级(如将INFO修改为DEBUG)——通过向http://localhost:8080/actuator/loggers/root 发送POST请求,并携带数据 {"configuredLevel":"DEBUG"}。

/beans端点可以返回Spring 容器中所有bean的类型、别名、是否单例、依赖等信息。

/shutdown端口可以用来关闭服务,为了安全起见,一般不会开启这个端口。

4、使用Profiles

前面讲过Spring提供的Profiles功能,Profile表示一个环境的概念,如开发、测试和生产这3个环境,或者按git分支定义master、dev这些环境,Spring Boot对Profiles的支持在于,可以在application.yml中为每个环境进行配置。如下所示,分隔符---下面分别是test环境和production的配置,算上默认的环境default(启动应用的时候不指定任何Profile),这里面一共配置了三个环境:默认使用8080端口,在test环境下的话使用8000端口,在production环境下使用80端口并且启用Pebble的缓存。要以test环境启动程序的话,可以为:java -D spring.profiles.active=test -jar jar_name.jar。

spring: application: name: ${APP_NAME:unnamed} datasource: ...... pebble: suffix: cache: false server: port: ${APP_PORT:8080} --- spring: profiles: test server: port: 8000 --- spring: profiles: production server: port: 80 pebble: cache: true

如下所示,默认情况下in成员会使用Foo类注入,如果使用其它环境的话就使用Bar注入:

public interface MyInter { ... } @Component @Profile("default") public class Foo implements MyInter{ ... } @Component @Profile("!default") public class Bar implements MyInter{ ... } @Autowired private MyInter in;

5、条件注解

在Spring中可以通过@Conditional来使指定的类生效(能够通过Ioc实例化该类),Spring Boot中有更多的Conditional:

@ConditionalOnProperty:如果有指定的配置,条件生效;
@ConditionalOnBean:如果有指定的Bean,条件生效;
@ConditionalOnMissingBean:如果没有指定的Bean,条件生效;
@ConditionalOnClass:如果有指定的Class,条件生效;
@ConditionalOnMissingClass:如果没有指定的Class,条件生效;
@ConditionalOnWebApplication:在Web环境中条件生效;
@ConditionalOnExpression:根据表达式判断条件是否生效。

@Component @ConditionalOnProperty(name="app.smtp", havingValue="true") //配置文件中存在app.smtp=true,IoC才会实例化MailService,否则抛出异常 public class MailService { ... } @Component @ConditionalOnClass(name = "javax.mail.Transport") //classpath中存在类javax.mail.Transport,IoC才会实例化MailService,否则抛出异常 public class MailService { ... }

如果需要注入的是一个接口成员的话,IoC会自动搜索接口的实现类来注入该成员,如果有多个接口实现类的话,可以通过条件注解来设置使用哪个类,比如使用@ConditionalOnProperty来设置通过配置文件中配置来指定使用哪个类:

@Component @ConditionalOnProperty(name = "app.storage", havingValue = "file", matchIfMissing = true)//配置文件存在app.storage且其值为file,或者配置文件中不存在app.storage,该类有效 public class FileUploader implements Uploader { ... } @Component @ConditionalOnProperty(name = "app.storage", havingValue = "s3") //配置文件存在app.storage,且其值为s3时,该类有效 public class S3Uploader implements Uploader { ... } @Component public class UserImageService { @Autowired Uploader uploader; //通过配置文件中配置来选择使用哪个接口实现类来注入该成员 } @Configuration @ComponentScan public class App { public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(App.class); UserImageService ser = context.getBean(UserImageService.class); } }

6、读取配置文件

如下所示的application.yml配置文件中的内容,除了使用@Value来读取一个配置信息到成员中( 如@Value("${storage.local.max-size:102400}") ),还可以将配置文件中的一组信息保存到JavaBean中来读取,通过使用@ConfigurationProperties。如下所示,我们将storage.local中的所有配置信息保存到了StorageConfiguration对象中:

storage: local: root-dir: ${STORAGE_LOCAL_ROOT:/var/storage} max-size: ${STORAGE_LOCAL_MAX_SIZE:102400} allow-empty: false allow-types: jpg, png, gif
@Configuration //相当于@Component,也可以不写该注解,然后在@SpringBootApplication入口类中使用@Import(StorageConfiguration.class)来手动加载 @ConfigurationProperties("storage.local") //读取配置文件(默认为application.yml或application.properties)中storage.local的信息到本类 public class StorageConfiguration { private String rootDir; private int maxSize; private boolean allowEmpty; private List<String> allowTypes; String getRootDir(){...} ... } @Component public class StorageService { @Autowired StorageConfiguration storageConfig; @PostConstruct public void init() { storageConfig.getRootDir(); storageConfig.getMaxSize(); ... } }

7、禁用自动配置

Spring Boot大量使用自动配置和默认配置,极大地减少了代码,通常只需要加上几个注解,并对配置文件配置以下即可。例如,配置JDBC,默认情况下,只需要如下所示配置一个spring.datasource,Spring Boot就会自动创建出DataSource、JdbcTemplate、DataSourceTransactionManager,非常方便。

spring: datasource: url: jdbc:hsqldb:file:testdb username: sa password: dirver-class-name: org.hsqldb.jdbc.JDBCDriver

有时候,我们需要要禁用某些自动配置,比如系统有主从两个数据库,而Spring Boot的自动配置只能配一个,这时候就需要关闭数据库的自动配置。关于怎样使SpringBoot支持两个数据库,具体可以参考:禁用自动配置 - 廖雪峰的官方网站。

8、添加Filter

在SpringBoot中想要使用Filter的话,通过继承FilterRegistrationBean来实现,如下所示的AuthFilterRegistrationBean实现了过滤所有所有URL的Filter:

@Component public class AuthFilterRegistrationBean extends FilterRegistrationBean<Filter> { @Autowired UserService userService; @Override public Filter getFilter() { //返回要注册的Filter setOrder(10); //设置Filter的顺序 return new AuthFilter(); } class AuthFilter implements Filter { public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException{ userService.func(); //内部类可以直接使用外部类的成员 ... } } }

上面对所有请求都会过滤,也可以设置仅对指定路径有效:

@Component public class ApiFilterRegistrationBean extends FilterRegistrationBean<Filter> { @PostConstruct public void init() { setOrder(20); setFilter(new ApiFilter()); //设置要注册的Filter setUrlPatterns(List.of("/api/*")); //设置要过滤的路径 } class ApiFilter implements Filter { ... } }

9、全局异常处理

@ExceptionHandler注解用来处理控制器中的异常,如下所示,当MyController中的方法抛出异常的时候,handleRuntimeException()方法会被调用:

@RestController public class MyController { @ExceptionHandler(RuntimeException.class) public ResponseEntity<String> handleRuntimeException(RuntimeException ex) { // 自定义异常处理逻辑 return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ex.getMessage()); } }

如果希望能处理多种类型的异常,可以在@ExceptionHandler中传入多个异常类,比如@ExceptionHandler({IllegalArgumentException.class, NullPointerException.class})

也可以将所有控制器的异常放到一个类中统一进行处理,通过@ControllerAdvice,如下所示的GlobalExceptionHandler就是一个全局的异常处理器。Spring遵循“就近原则”来选择异常处理方法,如果有多个异常处理方法可用,Spring会选择与抛出的异常最匹配的那个,比如一个方法抛出了NumberFormatException,而存在多个处理不同异常类型的方法,Spring会选择专门处理NumberFormatException的方法。

@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(RuntimeException.class) public ResponseEntity<String> handleRuntimeException(RuntimeException ex) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ex.getMessage()); } @ExceptionHandler(Exception.class) public ResponseEntity<String> handleGenericException(Exception ex) { return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("发生错误: " + ex.getMessage()); } }

@RestControllerAdvice注解则表示专门用于处理RESTful控制器的异常,默认将处理结果写入响应体,无需@ResponseBody。

@ResponseStatus 注解可以为异常处理方法指定一个特定的 HTTP 状态码。

@RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(RuntimeException.class) @ResponseStatus(HttpStatus.TOO_MANY_REQUESTS) // 429状态码 public String handleRateLimitException(RuntimeException e) { return e.getMessage(); } }

处理异常的方法不仅可以接收异常对象,还可以接收其它参数,如 HttpServletRequest或WebRequest,比如handleRuntimeException(RuntimeException ex, WebRequest request)。

异常处理方法返回值可以为String、ModelAndView、ResponseEntity,返回值类型应与控制器中其他方法的返回值类型一致,比如控制器方法返回ModelAndView对象,异常处理方法也应该返回ModelAndView。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/13 12:10:11

告别格式壁垒:3分钟学会用stltostp将STL转为STEP文件 [特殊字符]

告别格式壁垒&#xff1a;3分钟学会用stltostp将STL转为STEP文件 &#x1f680; 【免费下载链接】stltostp Convert stl files to STEP brep files 项目地址: https://gitcode.com/gh_mirrors/st/stltostp 你是否遇到过这样的困扰&#xff1f;在3D打印领域常用的STL文件…

作者头像 李华
网站建设 2026/5/13 12:09:12

ICLR 2026|MathForge:用难题驱动强化学习,提升大模型数学推理

来源&#xff1a;机器之心 本文约3500字&#xff0c;建议阅读5分钟 MathForge 真正回答了一个非常关键的问题&#xff1a;在强化学习里&#xff0c;哪些题最值得学&#xff1f;在大模型数学推理的强化学习中&#xff0c;一个看似简单、却长期没有被认真回答的问题是&#xff1a…

作者头像 李华
网站建设 2026/5/13 12:08:12

VSCode安装clang-format插件及使用

VSCode安装clang-format插件及使用1.clang-format插件安装2.安装真正的格式化工具clang-format3.生成.clang-format配置文件并修改4.修改配置文件4.1全局配置文件修改4.2工作空间配置文件修改5.格式化代码1.clang-format插件安装 插件安装方式分为直接安装和离线安装两种。 直…

作者头像 李华
网站建设 2026/5/13 12:02:37

macOS百度网盘SVIP破解完整指南:3步实现无限速下载

macOS百度网盘SVIP破解完整指南&#xff1a;3步实现无限速下载 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘的龟速下载而烦恼吗&…

作者头像 李华
网站建设 2026/5/13 12:01:28

换背景底色怎么制作?2026年最实用的免费工具推荐指南

最近有很多朋友问我&#xff0c;怎样才能快速换掉照片的背景底色&#xff0c;特别是在制作证件照、商品图或者社交媒体头像时。说实话&#xff0c;这个需求确实很常见&#xff0c;但很多人不知道其实现在已经有超简便的方法了。今天我就来跟大家分享一下我用过这么久最顺手的几…

作者头像 李华