第496集SpringBoot | 字数总计: 3.4k | 阅读时长: 18分钟 | 阅读量:
SpringBoot 1. 概述 1.1 SpringBoot的重要性 SpringBoot 是Spring框架的扩展,通过约定优于配置的理念,简化了Spring应用的开发和部署,是Java企业级应用开发的主流框架。
本文内容 :
SpringBoot基础 :核心概念和特性
自动配置 :自动配置原理和自定义
Web开发 :RESTful API、WebMVC
数据访问 :JPA、MyBatis、Redis
安全认证 :Spring Security
监控管理 :Actuator监控
部署运维 :打包部署
1.2 本文内容结构 本文将从以下几个方面深入探讨SpringBoot:
SpringBoot基础 :核心概念和快速开始
自动配置 :自动配置原理
Web开发 :RESTful API开发
数据访问 :数据库集成
安全认证 :Spring Security
监控管理 :Actuator监控
部署运维 :打包和部署
2. SpringBoot基础 2.1 SpringBoot特性 2.1.1 核心特性 SpringBoot核心特性 :
自动配置 :自动配置Spring和第三方库
起步依赖 :简化依赖管理
内嵌服务器 :内嵌Tomcat、Jetty等
生产就绪 :Actuator监控和管理
无需XML配置 :基于Java配置和注解
SpringBoot优势 :
快速开发 :减少配置,提高开发效率
约定优于配置 :遵循约定,减少决策
独立运行 :可执行JAR,易于部署
生产就绪 :内置监控和管理功能
2.2 快速开始 2.2.1 创建SpringBoot项目 方式1:使用Spring Initializr
访问 https://start.spring.io/,选择依赖,生成项目。
方式2:使用Maven
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 <?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 > <parent > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-parent</artifactId > <version > 2.7.0</version > <relativePath /> </parent > <groupId > com.example</groupId > <artifactId > springboot-demo</artifactId > <version > 1.0.0</version > <name > SpringBoot Demo</name > <properties > <java.version > 1.8</java.version > </properties > <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-test</artifactId > <scope > test</scope > </dependency > </dependencies > <build > <plugins > <plugin > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-maven-plugin</artifactId > </plugin > </plugins > </build > </project >
主应用类 :
1 2 3 4 5 6 7 8 9 10 11 12 package com.example.demo;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication public class Application { public static void main (String[] args) { SpringApplication.run(Application.class, args); } }
Controller示例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 package com.example.demo.controller;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;@RestController public class HelloController { @GetMapping("/hello") public String hello () { return "Hello SpringBoot!" ; } }
2.3 核心注解 2.3.1 常用注解 SpringBoot核心注解 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 @SpringBootApplication public class Application { } @RestController @RequestMapping("/api") public class ApiController { @GetMapping("/users") public List<User> getUsers () { return userService.findAll(); } @PostMapping("/users") public User createUser (@RequestBody User user) { return userService.save(user); } } @Service public class UserService { } @Repository public interface UserRepository extends JpaRepository <User, Long> { } @Component public class CustomComponent { }
3. 自动配置 3.1 自动配置原理 3.1.1 自动配置机制 自动配置原理 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @SpringBootApplication public class Application { } @Configuration @ConditionalOnClass(DataSource.class) @EnableConfigurationProperties(DataSourceProperties.class) public class DataSourceAutoConfiguration { @Bean @ConditionalOnMissingBean public DataSource dataSource (DataSourceProperties properties) { return properties.initializeDataSourceBuilder().build(); } }
条件注解 :
@ConditionalOnClass:类路径存在指定类时生效
@ConditionalOnMissingBean:容器中不存在指定Bean时生效
@ConditionalOnProperty:配置属性满足条件时生效
3.2 自定义自动配置 3.2.1 创建自动配置类 自定义自动配置 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 @ConfigurationProperties(prefix = "custom") public class CustomProperties { private String name; private int timeout = 1000 ; public String getName () { return name; } public void setName (String name) { this .name = name; } public int getTimeout () { return timeout; } public void setTimeout (int timeout) { this .timeout = timeout; } } @Configuration @EnableConfigurationProperties(CustomProperties.class) @ConditionalOnClass(CustomService.class) public class CustomAutoConfiguration { @Bean @ConditionalOnMissingBean public CustomService customService (CustomProperties properties) { return new CustomService (properties); } }
3.3 配置文件 3.3.1 application.properties application.properties配置 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 server.port =8080 server.servlet.context-path =/api spring.datasource.url =jdbc:mysql://localhost:3306/test spring.datasource.username =root spring.datasource.password =password spring.datasource.driver-class-name =com.mysql.cj.jdbc.Driver spring.jpa.hibernate.ddl-auto =update spring.jpa.show-sql =true spring.jpa.properties.hibernate.format_sql =true logging.level.root =INFO logging.level.com.example =DEBUG logging.file.name =application.log custom.name =MyApp custom.timeout =5000
application.yml配置 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 server: port: 8080 servlet: context-path: /api spring: datasource: url: jdbc:mysql://localhost:3306/test username: root password: password driver-class-name: com.mysql.cj.jdbc.Driver jpa: hibernate: ddl-auto: update show-sql: true properties: hibernate: format_sql: true logging: level: root: INFO com.example: DEBUG file: name: application.log custom: name: MyApp timeout: 5000
多环境配置 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 spring: profiles: active: dev --- server: port: 8080 --- server: port: 8080 logging: level: root: WARN
4. Web开发 4.1 RESTful API 4.1.1 RESTful控制器 RESTful API示例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 @RestController @RequestMapping("/api/users") public class UserController { @Autowired private UserService userService; @GetMapping public ResponseEntity<List<User>> getAllUsers () { List<User> users = userService.findAll(); return ResponseEntity.ok(users); } @GetMapping("/{id}") public ResponseEntity<User> getUser (@PathVariable Long id) { User user = userService.findById(id); if (user == null ) { return ResponseEntity.notFound().build(); } return ResponseEntity.ok(user); } @PostMapping public ResponseEntity<User> createUser (@RequestBody User user) { User created = userService.save(user); return ResponseEntity.status(HttpStatus.CREATED).body(created); } @PutMapping("/{id}") public ResponseEntity<User> updateUser (@PathVariable Long id, @RequestBody User user) { user.setId(id); User updated = userService.update(user); return ResponseEntity.ok(updated); } @DeleteMapping("/{id}") public ResponseEntity<Void> deleteUser (@PathVariable Long id) { userService.deleteById(id); return ResponseEntity.noContent().build(); } }
4.2 参数绑定 4.2.1 请求参数处理 参数绑定示例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 @RestController @RequestMapping("/api") public class ParameterController { @GetMapping("/users/{id}") public User getUser (@PathVariable Long id) { return userService.findById(id); } @GetMapping("/users") public List<User> getUsers ( @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size, @RequestParam(required = false) String name) { return userService.findUsers(page, size, name); } @PostMapping("/users") public User createUser (@RequestBody User user) { return userService.save(user); } @GetMapping("/info") public String getInfo (@RequestHeader("User-Agent") String userAgent) { return "User-Agent: " + userAgent; } @GetMapping("/cookie") public String getCookie (@CookieValue("sessionId") String sessionId) { return "SessionId: " + sessionId; } }
4.3 异常处理 4.3.1 全局异常处理 全局异常处理 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 @RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(BusinessException.class) public ResponseEntity<ErrorResponse> handleBusinessException (BusinessException e) { ErrorResponse error = new ErrorResponse (e.getCode(), e.getMessage()); return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error); } @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity<ErrorResponse> handleValidationException ( MethodArgumentNotValidException e) { String message = e.getBindingResult().getFieldErrors().stream() .map(FieldError::getDefaultMessage) .collect(Collectors.joining(", " )); ErrorResponse error = new ErrorResponse ("VALIDATION_ERROR" , message); return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error); } @ExceptionHandler(Exception.class) public ResponseEntity<ErrorResponse> handleException (Exception e) { ErrorResponse error = new ErrorResponse ("INTERNAL_ERROR" , e.getMessage()); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error); } } public class ErrorResponse { private String code; private String message; private long timestamp; public ErrorResponse (String code, String message) { this .code = code; this .message = message; this .timestamp = System.currentTimeMillis(); } }
4.4 拦截器 4.4.1 自定义拦截器 拦截器示例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 @Component public class LoggingInterceptor implements HandlerInterceptor { private static final Logger logger = LoggerFactory.getLogger(LoggingInterceptor.class); @Override public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) { logger.info("Request: {} {}" , request.getMethod(), request.getRequestURI()); return true ; } @Override public void postHandle (HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) { logger.info("Response status: {}" , response.getStatus()); } @Override public void afterCompletion (HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { if (ex != null ) { logger.error("Exception occurred" , ex); } } } @Configuration public class WebMvcConfig implements WebMvcConfigurer { @Autowired private LoggingInterceptor loggingInterceptor; @Override public void addInterceptors (InterceptorRegistry registry) { registry.addInterceptor(loggingInterceptor) .addPathPatterns("/api/**" ) .excludePathPatterns("/api/public/**" ); } }
5. 数据访问 5.1 JPA 5.1.1 Spring Data JPA JPA配置 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 @Entity @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false) private String name; @Column(unique = true) private String email; } @Repository public interface UserRepository extends JpaRepository <User, Long> { List<User> findByName (String name) ; List<User> findByEmailContaining (String email) ; @Query("SELECT u FROM User u WHERE u.name LIKE %:name%") List<User> searchByName (@Param("name") String name) ; @Query(value = "SELECT * FROM users WHERE email = ?1", nativeQuery = true) User findByEmailNative (String email) ; } @Service @Transactional public class UserService { @Autowired private UserRepository userRepository; public List<User> findAll () { return userRepository.findAll(); } public User findById (Long id) { return userRepository.findById(id) .orElseThrow(() -> new BusinessException ("User not found" )); } public User save (User user) { return userRepository.save(user); } public void deleteById (Long id) { userRepository.deleteById(id); } }
5.2 MyBatis 5.2.1 MyBatis集成 MyBatis配置 :
1 2 3 4 5 6 <dependency > <groupId > org.mybatis.spring.boot</groupId > <artifactId > mybatis-spring-boot-starter</artifactId > <version > 2.2.2</version > </dependency >
1 2 3 4 5 6 mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.entity configuration: map-underscore-to-camel-case: true
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Mapper public interface UserMapper { @Select("SELECT * FROM users WHERE id = #{id}") User findById (Long id) ; @Insert("INSERT INTO users(name, email) VALUES(#{name}, #{email})") @Options(useGeneratedKeys = true, keyProperty = "id") void insert (User user) ; @Update("UPDATE users SET name = #{name} WHERE id = #{id}") void update (User user) ; @Delete("DELETE FROM users WHERE id = #{id}") void deleteById (Long id) ; }
5.3 Redis 5.3.1 Redis集成 Redis配置 :
1 2 3 4 5 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-data-redis</artifactId > </dependency >
1 2 3 4 5 6 7 8 9 10 11 12 13 spring: redis: host: localhost port: 6379 password: database: 0 timeout: 3000 lettuce: pool: max-active: 8 max-idle: 8 min-idle: 0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 @Configuration public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate (RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate <>(); template.setConnectionFactory(factory); Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer <>(Object.class); template.setValueSerializer(serializer); template.setKeySerializer(new StringRedisSerializer ()); template.afterPropertiesSet(); return template; } } @Service public class CacheService { @Autowired private RedisTemplate<String, Object> redisTemplate; public void set (String key, Object value) { redisTemplate.opsForValue().set(key, value, 1 , TimeUnit.HOURS); } public Object get (String key) { return redisTemplate.opsForValue().get(key); } public void delete (String key) { redisTemplate.delete(key); } }
6. 安全认证 6.1 Spring Security 6.1.1 Spring Security配置 Spring Security配置 :
1 2 3 4 5 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-security</artifactId > </dependency >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsService; @Override protected void configure (HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/api/public/**" ).permitAll() .antMatchers("/api/admin/**" ).hasRole("ADMIN" ) .anyRequest().authenticated() .and() .formLogin() .loginPage("/login" ) .defaultSuccessUrl("/" ) .and() .logout() .logoutUrl("/logout" ) .logoutSuccessUrl("/login" ) .and() .csrf().disable(); } @Override protected void configure (AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService) .passwordEncoder(passwordEncoder()); } @Bean public PasswordEncoder passwordEncoder () { return new BCryptPasswordEncoder (); } }
6.2 JWT认证 6.2.1 JWT集成 JWT配置 :
1 2 3 4 5 6 <dependency > <groupId > io.jsonwebtoken</groupId > <artifactId > jjwt</artifactId > <version > 0.9.1</version > </dependency >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 @Component public class JwtUtil { private String secret = "mySecretKey" ; private long expiration = 86400000 ; public String generateToken (UserDetails userDetails) { Map<String, Object> claims = new HashMap <>(); return createToken(claims, userDetails.getUsername()); } private String createToken (Map<String, Object> claims, String subject) { return Jwts.builder() .setClaims(claims) .setSubject(subject) .setIssuedAt(new Date ()) .setExpiration(new Date (System.currentTimeMillis() + expiration)) .signWith(SignatureAlgorithm.HS512, secret) .compact(); } public String getUsernameFromToken (String token) { return getClaimFromToken(token, Claims::getSubject); } public <T> T getClaimFromToken (String token, Function<Claims, T> claimsResolver) { final Claims claims = getAllClaimsFromToken(token); return claimsResolver.apply(claims); } private Claims getAllClaimsFromToken (String token) { return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody(); } public Boolean validateToken (String token, UserDetails userDetails) { final String username = getUsernameFromToken(token); return (username.equals(userDetails.getUsername()) && !isTokenExpired(token)); } private Boolean isTokenExpired (String token) { final Date expiration = getExpirationDateFromToken(token); return expiration.before(new Date ()); } private Date getExpirationDateFromToken (String token) { return getClaimFromToken(token, Claims::getExpiration); } }
7. 监控管理 7.1 Actuator 7.1.1 Actuator配置 Actuator配置 :
1 2 3 4 5 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-actuator</artifactId > </dependency >
1 2 3 4 5 6 7 8 9 10 11 12 13 management: endpoints: web: exposure: include: health,info,metrics,prometheus endpoint: health: show-details: always metrics: export: prometheus: enabled: true
Actuator端点 :
/actuator/health:健康检查
/actuator/info:应用信息
/actuator/metrics:指标信息
/actuator/prometheus:Prometheus指标
7.2 自定义端点 7.2.1 创建自定义端点 自定义端点 :
1 2 3 4 5 6 7 8 9 10 11 12 @Component @Endpoint(id = "custom") public class CustomEndpoint { @ReadOperation public Map<String, Object> custom () { Map<String, Object> info = new HashMap <>(); info.put("status" , "OK" ); info.put("timestamp" , System.currentTimeMillis()); return info; } }
8. 部署运维 8.1 打包部署 8.1.1 打包可执行JAR Maven打包 :
1 2 3 4 5 6 7 8 mvn clean package java -jar target/springboot-demo-1.0.0.jar java -jar target/springboot-demo-1.0.0.jar --spring.profiles.active=prod
Docker部署 :
1 2 3 4 FROM openjdk:8 -jre-alpineVOLUME /tmp COPY target/springboot-demo-1.0.0.jar app.jar ENTRYPOINT ["java" ,"-jar" ,"/app.jar" ]
1 2 3 4 5 docker build -t springboot-demo:1.0.0 . docker run -d -p 8080:8080 springboot-demo:1.0.0
8.2 生产环境配置 8.2.1 生产环境优化 生产环境配置 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 server: port: 8080 compression: enabled: true spring: jpa: show-sql: false hibernate: ddl-auto: validate logging: level: root: WARN com.example: INFO file: name: /var/log/application.log max-size: 10MB max-history: 30
9. 总结 9.1 核心要点
自动配置 :SpringBoot通过自动配置简化开发
起步依赖 :简化依赖管理
内嵌服务器 :可独立运行
生产就绪 :Actuator提供监控和管理
9.2 关键理解
约定优于配置 :遵循约定,减少配置
开箱即用 :提供默认配置,快速开发
易于扩展 :支持自定义配置和扩展
9.3 最佳实践
合理使用自动配置 :理解自动配置原理
多环境配置 :使用profile管理不同环境
完善监控 :使用Actuator监控应用
安全配置 :合理配置安全策略
相关文章 :