├── cache-examples ├── README.md ├── src │ ├── main │ │ ├── resources │ │ │ └── application.yml │ │ └── java │ │ │ └── org │ │ │ └── example │ │ │ └── cacheexamples │ │ │ ├── dox │ │ │ └── User.java │ │ │ ├── CacheExamplesApplication.java │ │ │ ├── controller │ │ │ └── MyController.java │ │ │ ├── repository │ │ │ └── UserRepository.java │ │ │ ├── config │ │ │ └── SpringCacheConfig.java │ │ │ └── service │ │ │ └── UserService.java │ └── test │ │ ├── java │ │ └── org │ │ │ └── example │ │ │ └── cacheexamples │ │ │ └── CacheExamplesApplicationTests.java │ │ └── http │ │ └── cache.http └── pom.xml ├── consul-examples ├── README.md ├── src │ ├── main │ │ ├── resources │ │ │ └── application.yml │ │ └── java │ │ │ └── org │ │ │ └── example │ │ │ └── consulexamples │ │ │ ├── ConsulExamplesApplication.java │ │ │ └── controller │ │ │ └── IndexController.java │ └── test │ │ └── java │ │ └── org │ │ └── example │ │ └── consulexamples │ │ └── ConsulExamplesApplicationTests.java └── pom.xml ├── spring-examples ├── src │ ├── main │ │ ├── resources │ │ │ └── application.yml │ │ └── java │ │ │ └── org │ │ │ └── example │ │ │ └── springexamples │ │ │ ├── example02 │ │ │ └── jointpoint │ │ │ │ ├── AOPService02.java │ │ │ │ └── MyAspect02.java │ │ │ ├── exception │ │ │ └── XException.java │ │ │ ├── example01 │ │ │ └── aop │ │ │ │ ├── AOPService.java │ │ │ │ └── MyAspect.java │ │ │ ├── mock │ │ │ ├── dox │ │ │ │ └── User.java │ │ │ ├── repository │ │ │ │ └── UserRepositoryMock.java │ │ │ └── service │ │ │ │ └── UserService.java │ │ │ ├── example03 │ │ │ └── aopadvanced │ │ │ │ ├── MyAuthority.java │ │ │ │ ├── AOPService03.java │ │ │ │ └── MyAuthAspect.java │ │ │ └── SpringExamplesApplication.java │ └── test │ │ └── java │ │ └── org │ │ └── example │ │ └── springexamples │ │ ├── SpringExamplesApplicationTests.java │ │ ├── example02 │ │ └── AOPServiceTest.java │ │ ├── example01 │ │ └── AOPServiceTest.java │ │ ├── example03 │ │ └── AOPServiceTest.java │ │ └── mock │ │ └── UserServiceTest.java ├── README.md └── pom.xml ├── springmvc-examples ├── src │ ├── test │ │ ├── http │ │ │ ├── example04.http │ │ │ ├── example02.http │ │ │ ├── script_args.http │ │ │ ├── example09.http │ │ │ ├── example03.http │ │ │ ├── example06.http │ │ │ └── example01.http │ │ └── java │ │ │ └── org │ │ │ └── example │ │ │ └── springmvcexamples │ │ │ ├── SpringmvcExamplesApplicationTests.java │ │ │ ├── example04 │ │ │ └── PasswordEncoderTest.java │ │ │ └── example05 │ │ │ └── JWTTest.java │ └── main │ │ ├── java │ │ └── org │ │ │ └── example │ │ │ └── springmvcexamples │ │ │ ├── example02 │ │ │ └── handlingexception │ │ │ │ ├── dox │ │ │ │ └── User.java │ │ │ │ ├── service │ │ │ │ └── UserService02.java │ │ │ │ └── controller │ │ │ │ └── ExampleController02.java │ │ │ ├── example08 │ │ │ └── openapi │ │ │ │ ├── User08.java │ │ │ │ └── ExampleController08.java │ │ │ ├── exception │ │ │ ├── XException.java │ │ │ └── Code.java │ │ │ ├── example01 │ │ │ ├── dox │ │ │ │ ├── User.java │ │ │ │ └── Address.java │ │ │ └── ExampleController01.java │ │ │ ├── example07 │ │ │ └── timer │ │ │ │ └── MyTimer.java │ │ │ ├── SpringmvcExamplesApplication.java │ │ │ ├── example04 │ │ │ └── passwordencoder │ │ │ │ ├── dox │ │ │ │ └── User04.java │ │ │ │ ├── service │ │ │ │ └── UserService04.java │ │ │ │ └── controller │ │ │ │ └── ExampleController04.java │ │ │ ├── component │ │ │ ├── PasswordEncoderConfig.java │ │ │ ├── OpenAPIConfig.java │ │ │ └── JWTComponent.java │ │ │ ├── example03 │ │ │ └── beanvalidation │ │ │ │ ├── config │ │ │ │ └── MethodValidationConfig.java │ │ │ │ ├── dox │ │ │ │ └── User03.java │ │ │ │ └── controller │ │ │ │ └── ExampleController03.java │ │ │ ├── example06 │ │ │ └── interceptor │ │ │ │ ├── dox │ │ │ │ └── User06.java │ │ │ │ ├── service │ │ │ │ └── UserService06.java │ │ │ │ ├── interceptor │ │ │ │ ├── AdminInterceptor06.java │ │ │ │ └── LoginInterceptor06.java │ │ │ │ ├── config │ │ │ │ └── WebMvcConfig.java │ │ │ │ └── controller │ │ │ │ └── ExampleController06.java │ │ │ └── vo │ │ │ └── ResultVO.java │ │ └── resources │ │ └── application.yml ├── README.md └── pom.xml ├── springsecurity-examples ├── src │ ├── main │ │ ├── java │ │ │ └── org │ │ │ │ └── example │ │ │ │ └── springsecurityexamples │ │ │ │ ├── security │ │ │ │ ├── Roles.java │ │ │ │ ├── Tokens.java │ │ │ │ ├── UserDetails.java │ │ │ │ ├── Authorities.java │ │ │ │ ├── PasswordEncoderConfig.java │ │ │ │ ├── JWTComponent.java │ │ │ │ └── ResponseComponent.java │ │ │ │ ├── exception │ │ │ │ ├── XException.java │ │ │ │ └── Code.java │ │ │ │ ├── SpringsecurityExamplesApplication.java │ │ │ │ ├── entity │ │ │ │ └── User.java │ │ │ │ ├── vo │ │ │ │ └── ResultVO.java │ │ │ │ ├── service │ │ │ │ └── UserService.java │ │ │ │ ├── controller │ │ │ │ ├── ExceptionController.java │ │ │ │ ├── UserController.java │ │ │ │ ├── ManagerController.java │ │ │ │ └── LoginController.java │ │ │ │ └── filter │ │ │ │ ├── GlobalExceptionHandlingFilter.java │ │ │ │ └── JwtFilter.java │ │ └── resources │ │ │ └── application.yml │ └── test │ │ ├── java │ │ └── org │ │ │ └── example │ │ │ └── springsecurityexamples │ │ │ ├── SpringsecurityExamplesApplicationTests.java │ │ │ └── PasswordTest.java │ │ └── http │ │ └── test.http ├── README.md └── pom.xml ├── webflux-r2dbc-examples ├── src │ ├── test │ │ ├── http │ │ │ ├── login.http │ │ │ └── admin.http │ │ └── java │ │ │ └── org │ │ │ └── example │ │ │ └── webfluxr2dbcexamples │ │ │ ├── WebfluxR2dbcExamplesApplicationTests.java │ │ │ ├── ReactiveTest.java │ │ │ └── service │ │ │ └── UserServiceTest.java │ └── main │ │ ├── java │ │ └── org │ │ │ └── example │ │ │ └── webfluxr2dbcexamples │ │ │ ├── vo │ │ │ ├── RequestConstant.java │ │ │ └── ResultVO.java │ │ │ ├── exception │ │ │ ├── XException.java │ │ │ └── Code.java │ │ │ ├── WebfluxR2dbcExamplesApplication.java │ │ │ ├── component │ │ │ ├── PasswordEncoderConfig.java │ │ │ └── JWTComponent.java │ │ │ ├── repository │ │ │ └── UserRepository.java │ │ │ ├── dox │ │ │ └── UserReact.java │ │ │ ├── controller │ │ │ ├── AdminController.java │ │ │ ├── exception │ │ │ │ └── ExceptionController.java │ │ │ └── LoginController.java │ │ │ ├── service │ │ │ ├── InitService.java │ │ │ └── UserService.java │ │ │ └── filter │ │ │ ├── ResponseHelper.java │ │ │ └── LoginFilter.java │ │ └── resources │ │ ├── schema.sql │ │ └── application.yml ├── README.md └── pom.xml ├── jdbc-examples ├── src │ ├── main │ │ ├── java │ │ │ └── org │ │ │ │ └── example │ │ │ │ └── jdbcexamples │ │ │ │ ├── dto │ │ │ │ ├── GithubOptionType.java │ │ │ │ ├── AddressUser2.java │ │ │ │ ├── UserAddress.java │ │ │ │ ├── AddressUser.java │ │ │ │ └── UserAddress3.java │ │ │ │ ├── repository │ │ │ │ ├── AccountRepository.java │ │ │ │ ├── TeacherRepository.java │ │ │ │ ├── AccountPessRepository.java │ │ │ │ ├── AddressRepository.java │ │ │ │ ├── UserRepository.java │ │ │ │ └── GithubUserRepository.java │ │ │ │ ├── JdbcExamplesApplication.java │ │ │ │ ├── dox │ │ │ │ ├── AccountPess.java │ │ │ │ ├── Account.java │ │ │ │ ├── GithubUser.java │ │ │ │ ├── User.java │ │ │ │ ├── Address.java │ │ │ │ └── Teacher.java │ │ │ │ ├── service │ │ │ │ ├── UserTransService.java │ │ │ │ └── AccountService.java │ │ │ │ └── mapper │ │ │ │ ├── AddressUser2RowMapper.java │ │ │ │ ├── UserAddress3ResultSetExtractor.java │ │ │ │ └── UserAddressResultSetExtractor.java │ │ └── resources │ │ │ └── application.yml │ └── test │ │ └── java │ │ └── org │ │ └── example │ │ └── jdbcexamples │ │ ├── JdbcExamplesApplicationTests.java │ │ ├── repository │ │ ├── TeacherRepositoryTest.java │ │ ├── GithubUserRepositoryTest.java │ │ ├── UserRepositoryTest.java │ │ └── AddressRepositoryTest.java │ │ └── service │ │ ├── UserTransServiceTest.java │ │ └── AccountServiceTest.java └── pom.xml ├── redis-examples ├── src │ ├── test │ │ ├── java │ │ │ └── org │ │ │ │ └── example │ │ │ │ └── redisexamples │ │ │ │ ├── RedisExamplesApplicationTests.java │ │ │ │ ├── component │ │ │ │ └── UILDTest.java │ │ │ │ ├── service │ │ │ │ ├── ExpireServiceTest.java │ │ │ │ └── OrderServiceTest.java │ │ │ │ └── RedisTest.java │ │ └── redis.sql │ └── main │ │ ├── resources │ │ ├── application.yml │ │ └── mylib.lua │ │ └── java │ │ └── org │ │ └── example │ │ └── redisexamples │ │ ├── dox │ │ ├── Item.java │ │ └── Order.java │ │ ├── RedisExamplesApplication.java │ │ ├── listener │ │ ├── OrderMessageListener.java │ │ ├── CreateRedisStreamListener.java │ │ ├── MessageScheduledListener.java │ │ ├── LoadRedisScriptListener.java │ │ └── MessageListenerContainerConfiguration.java │ │ └── service │ │ ├── ExpireService.java │ │ └── OrderService.java ├── pom.xml └── README.md ├── .gitignore ├── project_mentor.md ├── LICENSE ├── pom.xml ├── README.md └── experiments.md /cache-examples/README.md: -------------------------------------------------------------------------------- 1 | # Spring Cache 2 | 3 | 可通过application.yml及代码配置缓存,代码更灵活。 4 | -------------------------------------------------------------------------------- /consul-examples/README.md: -------------------------------------------------------------------------------- 1 | # consul-examples 2 | 3 | consul,与nacos相似的注册发现/动态配置/健康检查中心,试用了一下挺方便。 4 | 5 | 需要先在本地运行consul,否则微服务会因无法找到注册中心启动失败。 -------------------------------------------------------------------------------- /spring-examples/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | logging: 2 | level: 3 | root: info 4 | org.example: debug 5 | pattern: 6 | console: '%-5level %C.%M[%line] - %msg%n' -------------------------------------------------------------------------------- /springmvc-examples/src/test/http/example04.http: -------------------------------------------------------------------------------- 1 | POST http://localhost:8080/api/example04/login 2 | Content-Type: application/json 3 | 4 | { 5 | "userName": "BO", 6 | "password": "123456" 7 | } 8 | 9 | ### 10 | 11 | -------------------------------------------------------------------------------- /springsecurity-examples/src/main/java/org/example/springsecurityexamples/security/Roles.java: -------------------------------------------------------------------------------- 1 | package org.example.springsecurityexamples.security; 2 | 3 | public interface Roles { 4 | String MANAGER = "manager"; 5 | String USER = "user"; 6 | } 7 | -------------------------------------------------------------------------------- /webflux-r2dbc-examples/src/test/http/login.http: -------------------------------------------------------------------------------- 1 | POST http://localhost:8080/api/login 2 | Content-Type: application/json 3 | 4 | { 5 | "account": "admin", 6 | "password": "admin" 7 | } 8 | > {% client.global.set("token", response.headers.valueOf("token")); %} 9 | ### -------------------------------------------------------------------------------- /webflux-r2dbc-examples/src/main/java/org/example/webfluxr2dbcexamples/vo/RequestConstant.java: -------------------------------------------------------------------------------- 1 | package org.example.webfluxr2dbcexamples.vo; 2 | 3 | public interface RequestConstant { 4 | String TOKEN = "token"; 5 | String UID = "uid"; 6 | String ROLE = "role"; 7 | } 8 | -------------------------------------------------------------------------------- /consul-examples/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: order-service 4 | cloud: 5 | consul: 6 | host: localhost 7 | port: 8500 8 | discovery: 9 | heartbeat: 10 | enabled: true # 启动心跳检测。不启动consul控制台显式checks错误 11 | -------------------------------------------------------------------------------- /springmvc-examples/src/test/http/example02.http: -------------------------------------------------------------------------------- 1 | POST http://localhost:8080/api/example02/login 2 | Content-Type: application/json 3 | 4 | { 5 | "userName": "sdf", 6 | "password": "123456" 7 | 8 | } 9 | 10 | ### 11 | GET http://localhost:8080/api/example02/exception 12 | 13 | ### 14 | -------------------------------------------------------------------------------- /springsecurity-examples/README.md: -------------------------------------------------------------------------------- 1 | # springsecurity-examples 2 | 3 | ### Introductions 4 | 5 | Spring Security在springmvc前执行,因此无法使用springmvc的统一异常处理,必须单独响应JSON数据。 6 | SecurityFilterChain声明配置,基于角色/路径的粗粒度权限校验,以及自定义过滤器。 7 | 在控制层方法,实现细粒度方法级权限校验。 8 | 在控制层方法,直接注入jwt解析并置于SecurityContext的Authentication数据。 9 | -------------------------------------------------------------------------------- /cache-examples/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | cache: 3 | caffeine: # 如果在代码声明配置并注入缓存管理器,此处配置无效 4 | spec: expireAfterWrite=240s, maximumSize=200 5 | 6 | logging: 7 | level: 8 | root: warn 9 | org.example: debug 10 | pattern: 11 | console: '%-5level %C.%M[%line] - %msg%n' 12 | -------------------------------------------------------------------------------- /springsecurity-examples/src/main/java/org/example/springsecurityexamples/security/Tokens.java: -------------------------------------------------------------------------------- 1 | package org.example.springsecurityexamples.security; 2 | 3 | public interface Tokens { 4 | String TOKEN = "token"; 5 | String UID = "uid"; 6 | String DEPID = "depid"; 7 | String AUTHORITIES = "authorities"; 8 | } 9 | -------------------------------------------------------------------------------- /springmvc-examples/src/main/java/org/example/springmvcexamples/example02/handlingexception/dox/User.java: -------------------------------------------------------------------------------- 1 | package org.example.springmvcexamples.example02.handlingexception.dox; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class User { 7 | private String userName; 8 | private String password; 9 | } 10 | -------------------------------------------------------------------------------- /cache-examples/src/main/java/org/example/cacheexamples/dox/User.java: -------------------------------------------------------------------------------- 1 | package org.example.cacheexamples.dox; 2 | 3 | import lombok.*; 4 | 5 | @Getter 6 | @Setter 7 | @ToString 8 | @NoArgsConstructor 9 | @AllArgsConstructor 10 | @Builder 11 | public class User { 12 | private String id; 13 | private String name; 14 | } 15 | -------------------------------------------------------------------------------- /jdbc-examples/src/main/java/org/example/jdbcexamples/dto/GithubOptionType.java: -------------------------------------------------------------------------------- 1 | package org.example.jdbcexamples.dto; 2 | 3 | public record GithubOptionType(String propName, String operator, Object value) { 4 | public static final String GREAT_EQUAL = ">="; 5 | public static final String LESS_EQUAL = "<="; 6 | public static final String EQUAL = "="; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /spring-examples/src/main/java/org/example/springexamples/example02/jointpoint/AOPService02.java: -------------------------------------------------------------------------------- 1 | package org.example.springexamples.example02.jointpoint; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | @Component 6 | public class AOPService02 { 7 | public String hello(String name) { 8 | return "welcome " + name; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /cache-examples/src/test/java/org/example/cacheexamples/CacheExamplesApplicationTests.java: -------------------------------------------------------------------------------- 1 | package org.example.cacheexamples; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class CacheExamplesApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /spring-examples/src/main/java/org/example/springexamples/exception/XException.java: -------------------------------------------------------------------------------- 1 | package org.example.springexamples.exception; 2 | 3 | import lombok.*; 4 | 5 | @EqualsAndHashCode(callSuper = true) 6 | @Data 7 | @Builder 8 | @NoArgsConstructor 9 | @AllArgsConstructor 10 | public class XException extends RuntimeException { 11 | private String message; 12 | } 13 | -------------------------------------------------------------------------------- /cache-examples/src/test/http/cache.http: -------------------------------------------------------------------------------- 1 | GET http://localhost:8080/api/users/1 2 | ### 3 | GET http://localhost:8080/api/users/2 4 | ### 5 | 6 | PATCH http://localhost:8080/api/users 7 | Content-Type: application/json 8 | 9 | { 10 | "id": "1", 11 | "name": "LIU" 12 | } 13 | ### 14 | DELETE http://localhost:8080/api/users/1 15 | ### 16 | GET http://localhost:8080/api/users/ -------------------------------------------------------------------------------- /spring-examples/src/main/java/org/example/springexamples/example01/aop/AOPService.java: -------------------------------------------------------------------------------- 1 | package org.example.springexamples.example01.aop; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.stereotype.Service; 5 | 6 | @Service 7 | @Slf4j 8 | public class AOPService { 9 | public void get() { 10 | log.debug("AOPService"); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /consul-examples/src/test/java/org/example/consulexamples/ConsulExamplesApplicationTests.java: -------------------------------------------------------------------------------- 1 | package org.example.consulexamples; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class ConsulExamplesApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /spring-examples/src/test/java/org/example/springexamples/SpringExamplesApplicationTests.java: -------------------------------------------------------------------------------- 1 | package org.example.springexamples; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class SpringExamplesApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /redis-examples/src/test/java/org/example/redisexamples/RedisExamplesApplicationTests.java: -------------------------------------------------------------------------------- 1 | package org.example.redisexamples; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class RedisExamplesApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /springmvc-examples/src/test/java/org/example/springmvcexamples/SpringmvcExamplesApplicationTests.java: -------------------------------------------------------------------------------- 1 | package org.example.springmvcexamples; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class SpringmvcExamplesApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /springmvc-examples/src/main/java/org/example/springmvcexamples/example08/openapi/User08.java: -------------------------------------------------------------------------------- 1 | package org.example.springmvcexamples.example08.openapi; 2 | 3 | import lombok.*; 4 | 5 | @Data 6 | @Builder 7 | @NoArgsConstructor 8 | @AllArgsConstructor 9 | public class User08 { 10 | private String id; 11 | private String name; 12 | private String account; 13 | private String password; 14 | } 15 | -------------------------------------------------------------------------------- /springmvc-examples/src/test/http/script_args.http: -------------------------------------------------------------------------------- 1 | POST http://localhost:8080/api/example06/login 2 | Content-Type: application/json 3 | 4 | { 5 | "userName": "BO", 6 | "password": "123456" 7 | } 8 | 9 | > {% client.global.set("token", response.headers.valueOf("token")); %} 10 | 11 | ### 基于脚本自动获取token数据。便于测试 12 | GET http://localhost:8080/api/example06/admin/welcome 13 | token: {{token}} 14 | 15 | ### -------------------------------------------------------------------------------- /jdbc-examples/src/main/java/org/example/jdbcexamples/repository/AccountRepository.java: -------------------------------------------------------------------------------- 1 | package org.example.jdbcexamples.repository; 2 | 3 | import org.example.jdbcexamples.dox.Account; 4 | import org.springframework.data.repository.CrudRepository; 5 | import org.springframework.stereotype.Repository; 6 | 7 | @Repository 8 | public interface AccountRepository extends CrudRepository { 9 | } 10 | -------------------------------------------------------------------------------- /webflux-r2dbc-examples/src/test/java/org/example/webfluxr2dbcexamples/WebfluxR2dbcExamplesApplicationTests.java: -------------------------------------------------------------------------------- 1 | package org.example.webfluxr2dbcexamples; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class WebfluxR2dbcExamplesApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /spring-examples/src/main/java/org/example/springexamples/mock/dox/User.java: -------------------------------------------------------------------------------- 1 | package org.example.springexamples.mock.dox; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @AllArgsConstructor 10 | @NoArgsConstructor 11 | @Builder 12 | public class User { 13 | private String id; 14 | private String name; 15 | } 16 | -------------------------------------------------------------------------------- /springsecurity-examples/src/test/java/org/example/springsecurityexamples/SpringsecurityExamplesApplicationTests.java: -------------------------------------------------------------------------------- 1 | package org.example.springsecurityexamples; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class SpringsecurityExamplesApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /spring-examples/src/main/java/org/example/springexamples/mock/repository/UserRepositoryMock.java: -------------------------------------------------------------------------------- 1 | package org.example.springexamples.mock.repository; 2 | 3 | 4 | import org.example.springexamples.mock.dox.User; 5 | import org.springframework.stereotype.Repository; 6 | 7 | @Repository 8 | public interface UserRepositoryMock { 9 | public User save(User user); 10 | 11 | public User findById(String id); 12 | } 13 | -------------------------------------------------------------------------------- /springmvc-examples/src/main/java/org/example/springmvcexamples/exception/XException.java: -------------------------------------------------------------------------------- 1 | package org.example.springmvcexamples.exception; 2 | 3 | import lombok.*; 4 | 5 | @EqualsAndHashCode(callSuper = true) 6 | @Data 7 | @Builder 8 | @NoArgsConstructor 9 | @AllArgsConstructor 10 | public class XException extends RuntimeException{ 11 | private Code code; 12 | private int codeN; 13 | private String message; 14 | } 15 | -------------------------------------------------------------------------------- /springmvc-examples/src/main/java/org/example/springmvcexamples/example01/dox/User.java: -------------------------------------------------------------------------------- 1 | package org.example.springmvcexamples.example01.dox; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @NoArgsConstructor 10 | @AllArgsConstructor 11 | @Builder 12 | public class User { 13 | private String id; 14 | private String name; 15 | } 16 | -------------------------------------------------------------------------------- /redis-examples/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | threads: 3 | virtual: 4 | enabled: true 5 | data: 6 | redis: 7 | host: 120.46.159.231 8 | port: 6379 # 使用默认端口时无需声明 9 | username: default 10 | password: 2046 11 | database: 0 # 替换数据库编号 12 | 13 | logging: 14 | level: 15 | root: warn 16 | org.example: debug 17 | pattern: 18 | console: '%-5level %C.%M[%line] - %msg%n' 19 | -------------------------------------------------------------------------------- /springsecurity-examples/src/main/java/org/example/springsecurityexamples/exception/XException.java: -------------------------------------------------------------------------------- 1 | package org.example.springsecurityexamples.exception; 2 | 3 | import lombok.*; 4 | 5 | @EqualsAndHashCode(callSuper = true) 6 | @Data 7 | @Builder 8 | @NoArgsConstructor 9 | @AllArgsConstructor 10 | public class XException extends RuntimeException{ 11 | private Code code; 12 | private int codeN; 13 | private String message; 14 | } 15 | -------------------------------------------------------------------------------- /webflux-r2dbc-examples/src/main/java/org/example/webfluxr2dbcexamples/exception/XException.java: -------------------------------------------------------------------------------- 1 | package org.example.webfluxr2dbcexamples.exception; 2 | 3 | 4 | import lombok.*; 5 | 6 | @EqualsAndHashCode(callSuper = true) 7 | @Data 8 | @Builder 9 | @NoArgsConstructor 10 | @AllArgsConstructor 11 | public class XException extends RuntimeException{ 12 | private Code code; 13 | private int codeN; 14 | private String message; 15 | } 16 | -------------------------------------------------------------------------------- /springsecurity-examples/src/main/java/org/example/springsecurityexamples/security/UserDetails.java: -------------------------------------------------------------------------------- 1 | package org.example.springsecurityexamples.security; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @Builder 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | public class UserDetails { 13 | private Long uid; 14 | private Long depId; 15 | } 16 | -------------------------------------------------------------------------------- /redis-examples/src/main/java/org/example/redisexamples/dox/Item.java: -------------------------------------------------------------------------------- 1 | package org.example.redisexamples.dox; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @AllArgsConstructor 10 | @NoArgsConstructor 11 | @Builder 12 | public class Item { 13 | public static final String PRE_KEY = "items:"; 14 | private String id; 15 | private int total; 16 | } 17 | -------------------------------------------------------------------------------- /cache-examples/src/main/java/org/example/cacheexamples/CacheExamplesApplication.java: -------------------------------------------------------------------------------- 1 | package org.example.cacheexamples; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class CacheExamplesApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(CacheExamplesApplication.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /springmvc-examples/src/main/java/org/example/springmvcexamples/example07/timer/MyTimer.java: -------------------------------------------------------------------------------- 1 | package org.example.springmvcexamples.example07.timer; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.scheduling.annotation.Scheduled; 5 | 6 | //@Component 7 | @Slf4j 8 | public class MyTimer { 9 | @Scheduled(cron = "0 0 8 10 * ?") 10 | public void paySalary() { 11 | log.debug("Your salary has been paid!"); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /jdbc-examples/src/main/java/org/example/jdbcexamples/JdbcExamplesApplication.java: -------------------------------------------------------------------------------- 1 | package org.example.jdbcexamples; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class JdbcExamplesApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(JdbcExamplesApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /redis-examples/src/main/java/org/example/redisexamples/RedisExamplesApplication.java: -------------------------------------------------------------------------------- 1 | package org.example.redisexamples; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class RedisExamplesApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(RedisExamplesApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /spring-examples/src/main/java/org/example/springexamples/example03/aopadvanced/MyAuthority.java: -------------------------------------------------------------------------------- 1 | package org.example.springexamples.example03.aopadvanced; 2 | 3 | import java.lang.annotation.*; 4 | 5 | @Retention(RetentionPolicy.RUNTIME) 6 | @Target({ElementType.TYPE, ElementType.METHOD}) 7 | public @interface MyAuthority { 8 | MyAuthorityType[] value() default MyAuthorityType.USER; 9 | 10 | enum MyAuthorityType { 11 | USER, ADMIN, SUPER_ADMIN 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /consul-examples/src/main/java/org/example/consulexamples/ConsulExamplesApplication.java: -------------------------------------------------------------------------------- 1 | package org.example.consulexamples; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class ConsulExamplesApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(ConsulExamplesApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /spring-examples/src/main/java/org/example/springexamples/SpringExamplesApplication.java: -------------------------------------------------------------------------------- 1 | package org.example.springexamples; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class SpringExamplesApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(SpringExamplesApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /springmvc-examples/src/main/java/org/example/springmvcexamples/SpringmvcExamplesApplication.java: -------------------------------------------------------------------------------- 1 | package org.example.springmvcexamples; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class SpringmvcExamplesApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(SpringmvcExamplesApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /springmvc-examples/src/test/http/example09.http: -------------------------------------------------------------------------------- 1 | ### 模拟脚本复杂,实际JS请求简单 2 | POST http://localhost:8080/api/example09/upload 3 | Content-Type: multipart/form-data;charset=utf-8 boundary=WebAppBoundary 4 | 5 | --WebAppBoundary 6 | Content-Disposition: form-data; name="pname"; 7 | Content-Type: text/plain 8 | 9 | 表单的其他附属信息 10 | --WebAppBoundary 11 | Content-Disposition: form-data; name="file"; filename="实验报告.docx" 12 | Content-Type: */* 13 | 14 | < E:/test/testing.mp4 15 | --WebAppBoundary-- 16 | ### -------------------------------------------------------------------------------- /springsecurity-examples/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | jackson: 3 | default-property-inclusion: non_null # 序列化时忽略空属性值 4 | 5 | server: 6 | servlet: 7 | encoding: # 可统一设置header charset 8 | charset: UTF-8 9 | force-request: true 10 | force-response: true 11 | 12 | logging: 13 | level: 14 | root: warn 15 | org.example: debug 16 | pattern: 17 | console: '%-5level %C.%M[%line] - %msg%n' 18 | 19 | my: 20 | secretkey: R28K42ZEJ8LWRHU5 21 | 22 | -------------------------------------------------------------------------------- /springsecurity-examples/src/test/http/test.http: -------------------------------------------------------------------------------- 1 | POST localhost:8080/api/open/login 2 | Content-Type: application/json 3 | 4 | { 5 | "account": "2046204601", 6 | "password": "2046204601" 7 | } 8 | > {% client.global.set("token", response.headers.valueOf("token")); %} 9 | 10 | ### 未登录测试 11 | GET localhost:8080/api/manager/read 12 | 13 | ### 有权限 14 | GET localhost:8080/api/manager/read 15 | token: {{token}} 16 | 17 | ### 无权限 18 | GET localhost:8080/api/manager/delete 19 | token: {{token}} 20 | 21 | -------------------------------------------------------------------------------- /springmvc-examples/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | threads: 3 | virtual: 4 | enabled: true 5 | jackson: 6 | default-property-inclusion: non_null # 序列化时忽略空属性值 7 | servlet: 8 | multipart: 9 | max-file-size: -1 # 单文件下载大小限制 10 | max-request-size: -1 # 单文件上传大小限制 11 | 12 | logging: 13 | level: 14 | root: warn 15 | org.example: debug 16 | pattern: 17 | console: '%-5level %C.%M[%line] - %msg%n' 18 | 19 | my: 20 | secretkey: R28K42ZEJ8LWRHU5 21 | -------------------------------------------------------------------------------- /webflux-r2dbc-examples/src/main/java/org/example/webfluxr2dbcexamples/WebfluxR2dbcExamplesApplication.java: -------------------------------------------------------------------------------- 1 | package org.example.webfluxr2dbcexamples; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class WebfluxR2dbcExamplesApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(WebfluxR2dbcExamplesApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /jdbc-examples/src/main/java/org/example/jdbcexamples/dto/AddressUser2.java: -------------------------------------------------------------------------------- 1 | package org.example.jdbcexamples.dto; 2 | 3 | 4 | import org.example.jdbcexamples.dox.Address; 5 | import org.example.jdbcexamples.dox.User; 6 | import lombok.AllArgsConstructor; 7 | import lombok.Builder; 8 | import lombok.Data; 9 | import lombok.NoArgsConstructor; 10 | 11 | @Data 12 | @AllArgsConstructor 13 | @NoArgsConstructor 14 | @Builder 15 | public class AddressUser2 { 16 | private User user; 17 | private Address address; 18 | } 19 | -------------------------------------------------------------------------------- /springsecurity-examples/src/main/java/org/example/springsecurityexamples/SpringsecurityExamplesApplication.java: -------------------------------------------------------------------------------- 1 | package org.example.springsecurityexamples; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class SpringsecurityExamplesApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(SpringsecurityExamplesApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /webflux-r2dbc-examples/src/main/resources/schema.sql: -------------------------------------------------------------------------------- 1 | create table if not exists `user_react` 2 | ( 3 | id char(19) not null primary key, 4 | name varchar(10) not null, 5 | account varchar(15) not null, 6 | password varchar(65) not null, 7 | role char(5) not null, 8 | insert_time datetime not null default current_timestamp, 9 | update_time datetime not null default current_timestamp on update current_timestamp, 10 | 11 | unique (account), 12 | index (role) 13 | ); 14 | -------------------------------------------------------------------------------- /jdbc-examples/src/main/java/org/example/jdbcexamples/dto/UserAddress.java: -------------------------------------------------------------------------------- 1 | package org.example.jdbcexamples.dto; 2 | 3 | import org.example.jdbcexamples.dox.Address; 4 | import org.example.jdbcexamples.dox.User; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Builder; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | 10 | import java.util.List; 11 | 12 | @Data 13 | @AllArgsConstructor 14 | @NoArgsConstructor 15 | @Builder 16 | public class UserAddress { 17 | private User user; 18 | private List
addresses; 19 | } 20 | -------------------------------------------------------------------------------- /springsecurity-examples/src/main/java/org/example/springsecurityexamples/security/Authorities.java: -------------------------------------------------------------------------------- 1 | package org.example.springsecurityexamples.security; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | @Component("auth") 6 | public final class Authorities { 7 | public static final String MANAGER_READ = "manager_read"; 8 | public static final String MANAGER_CREATE = "manager_create"; 9 | public static final String MANAGER_UPDATE = "manager_update"; 10 | public static final String MANAGER_DELETE = "manager_delete"; 11 | } 12 | -------------------------------------------------------------------------------- /jdbc-examples/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | mysql-account: 2046204601 # 可声明变量多处引用,避免出错 2 | spring: 3 | datasource: 4 | url: 'jdbc:mysql://120.46.159.231:3306/${mysql-account}?createDatabaseIfNotExist=true' 5 | username: ${mysql-account} 6 | password: ${mysql-account} 7 | hikari: 8 | maximum-pool-size: 5 9 | sql: 10 | init: 11 | mode: always 12 | 13 | logging: 14 | level: 15 | root: warn 16 | sql: debug 17 | org.example: debug 18 | pattern: 19 | console: '%-5level %C.%M[%line] - %msg%n' 20 | 21 | 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | application-secret.yml 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /jdbc-examples/src/test/java/org/example/jdbcexamples/JdbcExamplesApplicationTests.java: -------------------------------------------------------------------------------- 1 | package org.example.jdbcexamples; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.junit.jupiter.api.Test; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | 7 | import java.time.LocalDateTime; 8 | 9 | @SpringBootTest 10 | @Slf4j 11 | class JdbcExamplesApplicationTests { 12 | 13 | @Test 14 | void contextLoads() { 15 | LocalDateTime parse = LocalDateTime.parse("2023-06-13T11:24:44"); 16 | log.debug("{}", parse); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /consul-examples/src/main/java/org/example/consulexamples/controller/IndexController.java: -------------------------------------------------------------------------------- 1 | package org.example.consulexamples.controller; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.web.bind.annotation.GetMapping; 5 | import org.springframework.web.bind.annotation.RequestMapping; 6 | import org.springframework.web.bind.annotation.RestController; 7 | 8 | @Slf4j 9 | @RestController 10 | @RequestMapping("/api/") 11 | public class IndexController { 12 | @GetMapping("index") 13 | public String index() { 14 | return "Hello World!"; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /jdbc-examples/src/main/java/org/example/jdbcexamples/dox/AccountPess.java: -------------------------------------------------------------------------------- 1 | package org.example.jdbcexamples.dox; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | import org.springframework.data.annotation.CreatedBy; 8 | import org.springframework.data.annotation.Id; 9 | 10 | @Data 11 | @NoArgsConstructor 12 | @AllArgsConstructor 13 | @Builder 14 | public class AccountPess { 15 | @Id 16 | @CreatedBy 17 | private String id; 18 | private String name; 19 | private Float balance; 20 | } 21 | -------------------------------------------------------------------------------- /springmvc-examples/src/main/java/org/example/springmvcexamples/example01/dox/Address.java: -------------------------------------------------------------------------------- 1 | package org.example.springmvcexamples.example01.dox; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import java.time.LocalDateTime; 9 | 10 | @Data 11 | @NoArgsConstructor 12 | @AllArgsConstructor 13 | @Builder 14 | public class Address { 15 | private String id; 16 | private String detail; 17 | private String comment; 18 | private User user; 19 | private LocalDateTime inertTime; 20 | } 21 | -------------------------------------------------------------------------------- /springmvc-examples/src/main/java/org/example/springmvcexamples/exception/Code.java: -------------------------------------------------------------------------------- 1 | package org.example.springmvcexamples.exception; 2 | 3 | import lombok.Getter; 4 | import lombok.RequiredArgsConstructor; 5 | 6 | @Getter 7 | @RequiredArgsConstructor 8 | public enum Code { 9 | LOGIN_ERROR(Code.ERROR, "用户名密码错误"), 10 | BAD_REQUEST(Code.ERROR, "请求错误"), 11 | UNAUTHORIZED(401, "未登录"), 12 | TOKEN_EXPIRED(403, "过期请重新登录"), 13 | FORBIDDEN(403, "无权限"); 14 | public static final int ERROR = 400; 15 | private final int code; 16 | private final String message; 17 | } 18 | -------------------------------------------------------------------------------- /jdbc-examples/src/main/java/org/example/jdbcexamples/dto/AddressUser.java: -------------------------------------------------------------------------------- 1 | package org.example.jdbcexamples.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import java.time.LocalDateTime; 9 | 10 | @Data 11 | @AllArgsConstructor 12 | @NoArgsConstructor 13 | @Builder 14 | public class AddressUser { 15 | private String id; 16 | private String userId; 17 | private String name; 18 | private String detail; 19 | private LocalDateTime createTime; 20 | private LocalDateTime updateTime; 21 | } 22 | -------------------------------------------------------------------------------- /springmvc-examples/src/main/java/org/example/springmvcexamples/example04/passwordencoder/dox/User04.java: -------------------------------------------------------------------------------- 1 | package org.example.springmvcexamples.example04.passwordencoder.dox; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | @Data 10 | @Builder 11 | @AllArgsConstructor 12 | @NoArgsConstructor 13 | public class User04 { 14 | private String userName; 15 | @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) 16 | private String password; 17 | } 18 | -------------------------------------------------------------------------------- /springmvc-examples/src/test/http/example03.http: -------------------------------------------------------------------------------- 1 | ### 校验失败异常 2 | POST http://localhost:8080/api/example03/users 3 | Content-Type: application/json 4 | 5 | { 6 | "name": "T", 7 | "age": 17, 8 | "email": "abc" 9 | } 10 | 11 | ### 请求地址参数类型转换异常 12 | GET http://localhost:8080/api/example03/users/aaa/file 13 | 14 | ### json类型转换异常 15 | POST http://localhost:8080/api/example03/users 16 | Content-Type: application/json 17 | 18 | { 19 | "name": "e", 20 | "age": "sdsd", 21 | "email": "sdfsdf" 22 | } 23 | 24 | ### 请求地址参数校验 25 | GET http://localhost:8080/api/example03/users/s 26 | 27 | ### 28 | 29 | -------------------------------------------------------------------------------- /spring-examples/README.md: -------------------------------------------------------------------------------- 1 | # spring-examples 2 | 3 | ### AOP 4 | 基于自定义注解/AOP切面的细粒度权限控制 5 | 6 | ### Transactions 7 | spring托管事务,在spring容器捕获运行时异常时回滚 8 | 9 | ### example06 10 | 整合自定义注解/AOP/Redis,模拟spring-cache-redis缓存框架的实现 11 | 缓存默认5mins,支持在注解中指定时间及时间单位;ttl设为0,则永久缓存 12 | 13 | ### Mock 14 | 通过Mock实现基于未完成组件的测试。 15 | 16 | #### @MockBean 17 | 18 | 为修饰组件创建Mock对象,而非真实组件对象。 19 | 20 | #### Mockito 21 | 22 | when(),模拟Mock组件方法的调用 23 | anyX(),模拟调用Mock对象方法时传入参数 24 | thenReturn(),Mock组件方法调用时的返回值 25 | thenThrow(),Mock组件抛出指定异常对象 26 | doAnswer(),Mock组件方法调用时,执行操作 27 | 28 | UserRepository为未完成组件,测试用例中通过Mock模拟组件对象及方法调用。 -------------------------------------------------------------------------------- /springmvc-examples/src/main/java/org/example/springmvcexamples/component/PasswordEncoderConfig.java: -------------------------------------------------------------------------------- 1 | package org.example.springmvcexamples.component; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 6 | import org.springframework.security.crypto.password.PasswordEncoder; 7 | 8 | @Configuration 9 | public class PasswordEncoderConfig { 10 | @Bean 11 | public PasswordEncoder getPasswordEncoder() { 12 | return new BCryptPasswordEncoder(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /redis-examples/src/test/java/org/example/redisexamples/component/UILDTest.java: -------------------------------------------------------------------------------- 1 | package org.example.redisexamples.component; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.junit.jupiter.api.Test; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.context.SpringBootTest; 7 | 8 | @SpringBootTest 9 | @Slf4j 10 | public class UILDTest { 11 | @Autowired 12 | private ULID ulid; 13 | @Test 14 | void nextTest() { 15 | log.debug(ulid.nextULID()); 16 | log.debug(ulid.nextULID()); 17 | log.debug(ulid.nextULID()); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /springsecurity-examples/src/main/java/org/example/springsecurityexamples/security/PasswordEncoderConfig.java: -------------------------------------------------------------------------------- 1 | package org.example.springsecurityexamples.security; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 6 | import org.springframework.security.crypto.password.PasswordEncoder; 7 | 8 | @Configuration 9 | public class PasswordEncoderConfig { 10 | @Bean 11 | public PasswordEncoder getPasswordEncoder() { 12 | return new BCryptPasswordEncoder(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /webflux-r2dbc-examples/src/main/java/org/example/webfluxr2dbcexamples/component/PasswordEncoderConfig.java: -------------------------------------------------------------------------------- 1 | package org.example.webfluxr2dbcexamples.component; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 6 | import org.springframework.security.crypto.password.PasswordEncoder; 7 | 8 | @Configuration 9 | public class PasswordEncoderConfig { 10 | @Bean 11 | public PasswordEncoder getPasswordEncoder() { 12 | return new BCryptPasswordEncoder(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /springmvc-examples/src/main/java/org/example/springmvcexamples/example03/beanvalidation/config/MethodValidationConfig.java: -------------------------------------------------------------------------------- 1 | package org.example.springmvcexamples.example03.beanvalidation.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.validation.beanvalidation.MethodValidationPostProcessor; 6 | 7 | @Configuration 8 | public class MethodValidationConfig { 9 | @Bean 10 | public MethodValidationPostProcessor methodValidationPostProcessor() { 11 | return new MethodValidationPostProcessor(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /webflux-r2dbc-examples/src/main/java/org/example/webfluxr2dbcexamples/exception/Code.java: -------------------------------------------------------------------------------- 1 | package org.example.webfluxr2dbcexamples.exception; 2 | 3 | import lombok.Getter; 4 | 5 | @Getter 6 | public enum Code { 7 | LOGIN_ERROR(400, "用户名密码错误"), 8 | BAD_REQUEST(400, "请求错误"), 9 | UNAUTHORIZED(401, "未登录"), 10 | TOKEN_EXPIRED(403, "过期请重新登录"), 11 | FORBIDDEN(403, "无权限"); 12 | public static final int ERROR = 400; 13 | private final int code; 14 | private final String message; 15 | 16 | Code(int code, String message) { 17 | this.code = code; 18 | this.message = message; 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /spring-examples/src/main/java/org/example/springexamples/example03/aopadvanced/AOPService03.java: -------------------------------------------------------------------------------- 1 | package org.example.springexamples.example03.aopadvanced; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.stereotype.Component; 5 | 6 | @Component 7 | @MyAuthority 8 | @Slf4j 9 | public class AOPService03 { 10 | public void getUser() { 11 | log.debug("getUser()"); 12 | } 13 | 14 | @MyAuthority(value = { 15 | MyAuthority.MyAuthorityType.ADMIN, 16 | MyAuthority.MyAuthorityType.SUPER_ADMIN}) 17 | public void getAdmin() { 18 | log.debug("getAdmin()"); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /springsecurity-examples/src/main/java/org/example/springsecurityexamples/exception/Code.java: -------------------------------------------------------------------------------- 1 | package org.example.springsecurityexamples.exception; 2 | 3 | import lombok.Getter; 4 | import lombok.RequiredArgsConstructor; 5 | 6 | @Getter 7 | @RequiredArgsConstructor 8 | public enum Code { 9 | LOGIN_ERROR(Code.ERROR, "用户名密码错误"), 10 | BAD_REQUEST(Code.ERROR, "请求错误"), 11 | UNAUTHORIZED(401, "未登录"), 12 | TOKEN_EXPIRED(403, "过期请重新登录"), 13 | FORBIDDEN(403, "无权限"), 14 | TOKEN_ERROR(Code.ERROR, "Token错误"); 15 | public static final int ERROR = 400; 16 | private final int code; 17 | private final String message; 18 | } 19 | -------------------------------------------------------------------------------- /jdbc-examples/src/main/java/org/example/jdbcexamples/dto/UserAddress3.java: -------------------------------------------------------------------------------- 1 | package org.example.jdbcexamples.dto; 2 | 3 | import org.example.jdbcexamples.dox.Address; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | import java.time.LocalDateTime; 10 | import java.util.List; 11 | 12 | @Data 13 | @AllArgsConstructor 14 | @NoArgsConstructor 15 | @Builder 16 | public class UserAddress3 { 17 | private String id; 18 | private String name; 19 | private List
addresses; 20 | private LocalDateTime createTime; 21 | private LocalDateTime updateTime; 22 | } 23 | -------------------------------------------------------------------------------- /webflux-r2dbc-examples/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | mysql-account: 2046204601 2 | spring: 3 | r2dbc: 4 | url: r2dbcs:mysql://120.46.159.231:3306/${mysql-account} 5 | username: ${mysql-account} 6 | password: ${mysql-account} 7 | pool: 8 | enabled: true 9 | initial-size: 1 10 | 11 | sql: 12 | init: 13 | mode: always 14 | jackson: 15 | default-property-inclusion: non_null # 序列化时忽略空属性值 16 | 17 | logging: 18 | level: 19 | root: warn 20 | sql: debug 21 | web: debug 22 | org.example: debug 23 | pattern: 24 | console: '%-5level %C.%M[%line] - %msg%n' 25 | 26 | my: 27 | secretkey: '636eac2534bcfcc0' 28 | -------------------------------------------------------------------------------- /spring-examples/src/test/java/org/example/springexamples/example02/AOPServiceTest.java: -------------------------------------------------------------------------------- 1 | package org.example.springexamples.example02; 2 | 3 | import org.example.springexamples.example02.jointpoint.AOPService02; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.junit.jupiter.api.Test; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.boot.test.context.SpringBootTest; 8 | 9 | @SpringBootTest 10 | @Slf4j 11 | public class AOPServiceTest { 12 | @Autowired 13 | private AOPService02 aopService02; 14 | 15 | @Test 16 | public void test_hello() { 17 | log.debug(aopService02.hello("BO")); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /jdbc-examples/src/main/java/org/example/jdbcexamples/dox/Account.java: -------------------------------------------------------------------------------- 1 | package org.example.jdbcexamples.dox; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | import org.springframework.data.annotation.CreatedBy; 8 | import org.springframework.data.annotation.Id; 9 | import org.springframework.data.annotation.Version; 10 | 11 | @Data 12 | @NoArgsConstructor 13 | @AllArgsConstructor 14 | @Builder 15 | public class Account { 16 | @Id 17 | @CreatedBy 18 | private String id; 19 | private String name; 20 | private Float balance; 21 | @Version 22 | private Integer version; 23 | } 24 | -------------------------------------------------------------------------------- /jdbc-examples/src/main/java/org/example/jdbcexamples/dox/GithubUser.java: -------------------------------------------------------------------------------- 1 | package org.example.jdbcexamples.dox; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | import org.springframework.data.annotation.CreatedBy; 8 | import org.springframework.data.annotation.Id; 9 | 10 | @Data 11 | @NoArgsConstructor 12 | @AllArgsConstructor 13 | @Builder 14 | public class GithubUser { 15 | @Id 16 | @CreatedBy 17 | private String id; 18 | private String name; 19 | private Integer followers; 20 | private Integer stars; 21 | private String gender; 22 | private Integer repos; 23 | } 24 | -------------------------------------------------------------------------------- /jdbc-examples/src/test/java/org/example/jdbcexamples/repository/TeacherRepositoryTest.java: -------------------------------------------------------------------------------- 1 | package org.example.jdbcexamples.repository; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.junit.jupiter.api.Test; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.context.SpringBootTest; 7 | @SpringBootTest 8 | @Slf4j 9 | class TeacherRepositoryTest { 10 | @Autowired 11 | private TeacherRepository teacherRepository; 12 | 13 | @Test 14 | void updateDepartmentName() { 15 | var depId = "1"; 16 | var newName = "控制工程"; 17 | teacherRepository.updateDepartmentName(depId, newName); 18 | } 19 | } -------------------------------------------------------------------------------- /redis-examples/src/main/java/org/example/redisexamples/dox/Order.java: -------------------------------------------------------------------------------- 1 | package org.example.redisexamples.dox; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @AllArgsConstructor 10 | @NoArgsConstructor 11 | @Builder 12 | public class Order { 13 | public static final String PRE_KEY = "orders:"; 14 | public static final String STREAM_KEY = "orders:process"; 15 | public static final String GROUP_NAME = "consumer-group"; 16 | public static final String GROUP_CONSUMER = "consumer-1"; 17 | private String id; 18 | private String itemId; 19 | private String userId; 20 | } 21 | -------------------------------------------------------------------------------- /spring-examples/src/test/java/org/example/springexamples/example01/AOPServiceTest.java: -------------------------------------------------------------------------------- 1 | package org.example.springexamples.example01; 2 | 3 | 4 | import org.example.springexamples.example01.aop.AOPService; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.junit.jupiter.api.Test; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.test.context.SpringBootTest; 9 | 10 | @SpringBootTest 11 | @Slf4j 12 | public class AOPServiceTest { 13 | @Autowired 14 | private AOPService aopService; 15 | 16 | @Test 17 | public void test_get() { 18 | aopService.get(); 19 | log.debug(aopService.getClass().getName()); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /webflux-r2dbc-examples/src/test/http/admin.http: -------------------------------------------------------------------------------- 1 | POST http://localhost:8080/api/login 2 | Content-Type: application/json 3 | 4 | { 5 | "account": "admin", 6 | "password": "admin" 7 | } 8 | > {% client.global.set("token", response.headers.valueOf("token")); %} 9 | ### 10 | GET http://localhost:8080/api/admin/info 11 | token: {{token}} 12 | 13 | ### 已存在异常 14 | POST http://localhost:8080/api/admin/users 15 | token: {{token}} 16 | Content-Type: application/json 17 | 18 | { 19 | "account": "admin", 20 | "name": "admin" 21 | } 22 | ### 23 | ### 已存在异常 24 | POST http://localhost:8080/api/admin/users 25 | token: {{token}} 26 | Content-Type: application/json 27 | 28 | { 29 | "account": "1001", 30 | "name": "BO" 31 | } -------------------------------------------------------------------------------- /springmvc-examples/README.md: -------------------------------------------------------------------------------- 1 | # SpringMVC-Examples 2 | 3 | ### example03 Bean Validation 4 | 方法级校验,必须注入MethodValidationPostProcessor对象。 5 | 6 | ### example05 JWT 7 | 8 | ### example06 Interceptor 9 | 声明拦截器后,需配置拦截规范。 10 | 登录验证成功,将常用数据通过JWT加密后存于token,在登录拦截器解密后置于request,并注入controller组件使用。 11 | 12 | 可将角色数据声明为随机字符串。 13 | ```java 14 | public static final String ADMIN = "U8gR5"; 15 | public static final String USER = "oI6pz"; 16 | ``` 17 | 仅在序列化时忽略数据。 18 | ```java 19 | @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) 20 | ``` 21 | 22 | ### example07 Timer 23 | 24 | ### example08 SpringDoc 25 | api文档默认访问地址 26 | http://localhost:8080/swagger-ui/index.html 27 | 28 | https://springdoc.org/#how-do-i-add-authorization-header-in-requests -------------------------------------------------------------------------------- /springsecurity-examples/src/test/java/org/example/springsecurityexamples/PasswordTest.java: -------------------------------------------------------------------------------- 1 | package org.example.springsecurityexamples; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.junit.jupiter.api.Test; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.context.SpringBootTest; 7 | import org.springframework.security.crypto.password.PasswordEncoder; 8 | 9 | @SpringBootTest 10 | @Slf4j 11 | public class PasswordTest { 12 | @Autowired 13 | private PasswordEncoder encoder; 14 | 15 | @Test 16 | public void test() { 17 | var account = "2046204601"; 18 | var password = encoder.encode(account); 19 | log.debug(password); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /springmvc-examples/src/main/java/org/example/springmvcexamples/example06/interceptor/dox/User06.java: -------------------------------------------------------------------------------- 1 | package org.example.springmvcexamples.example06.interceptor.dox; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | @Data 10 | @Builder 11 | @NoArgsConstructor 12 | @AllArgsConstructor 13 | public class User06 { 14 | public static final String ADMIN = "U8gR5"; 15 | public static final String USER = "oI6pz"; 16 | 17 | private String id; 18 | private String userName; 19 | @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) 20 | private String password; 21 | private String role; 22 | } 23 | -------------------------------------------------------------------------------- /jdbc-examples/src/test/java/org/example/jdbcexamples/service/UserTransServiceTest.java: -------------------------------------------------------------------------------- 1 | package org.example.jdbcexamples.service; 2 | 3 | import org.example.jdbcexamples.dox.User; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.junit.jupiter.api.Test; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.boot.test.context.SpringBootTest; 8 | 9 | @SpringBootTest 10 | @Slf4j 11 | public class UserTransServiceTest { 12 | @Autowired 13 | private UserTransService userTransService; 14 | 15 | @Test 16 | public void addUserTest() { 17 | User u = User.builder().name("transaction").build(); 18 | User user = userTransService.addUser(u); 19 | log.debug("{}", user); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /springmvc-examples/src/main/java/org/example/springmvcexamples/example04/passwordencoder/service/UserService04.java: -------------------------------------------------------------------------------- 1 | package org.example.springmvcexamples.example04.passwordencoder.service; 2 | 3 | import org.example.springmvcexamples.example04.passwordencoder.dox.User04; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.stereotype.Service; 6 | 7 | @Slf4j 8 | @Service 9 | public class UserService04 { 10 | 11 | public User04 getUser(String userName) { 12 | return "BO".equals(userName) 13 | ? User04.builder() 14 | .userName("BO") 15 | .password("$2a$10$vbic.eN8nCmnzExjVIUUwOKsIAz0.NGEYC/IGwjWJHSCC8s37Kn9G") 16 | .build() 17 | : null; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /springmvc-examples/src/test/http/example06.http: -------------------------------------------------------------------------------- 1 | POST http://localhost:8080/api/example06/open/login 2 | Content-Type: application/json 3 | 4 | { 5 | "userName": "BO", 6 | "password": "123456" 7 | } 8 | 9 | > {% client.global.set("token", response.headers.valueOf("token")); %} 10 | ### token为空。未登录请求 11 | GET http://localhost:8080/api/example06/admin/welcome 12 | 13 | ### 手动模拟合法token与role测试 14 | GET http://localhost:8080/api/example06/admin/welcome 15 | token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiI5ODQxNjYyMTU4OCIsInJvbGUiOiJVOGdSNSIsImlhdCI6MTcxMzc3NzgwMCwiZXhwIjoxNzEzNzc3ODk0fQ.sEOw-6KO5FSfqmGk7iTOmaowvs2GZHM-PTfrimGOlSw 16 | role: U8gR5 17 | 18 | ### 基于脚本自动获取token数据。便于测试 19 | GET http://localhost:8080/api/example06/admin/welcome 20 | token: {{token}} -------------------------------------------------------------------------------- /jdbc-examples/src/main/java/org/example/jdbcexamples/dox/User.java: -------------------------------------------------------------------------------- 1 | package org.example.jdbcexamples.dox; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | import org.springframework.data.annotation.CreatedBy; 8 | import org.springframework.data.annotation.Id; 9 | import org.springframework.data.annotation.ReadOnlyProperty; 10 | 11 | import java.time.LocalDateTime; 12 | 13 | @Data 14 | @AllArgsConstructor 15 | @NoArgsConstructor 16 | @Builder 17 | public class User { 18 | @Id 19 | @CreatedBy 20 | private String id; 21 | private String name; 22 | @ReadOnlyProperty 23 | private LocalDateTime createTime; 24 | @ReadOnlyProperty 25 | private LocalDateTime updateTime; 26 | } 27 | -------------------------------------------------------------------------------- /spring-examples/src/main/java/org/example/springexamples/mock/service/UserService.java: -------------------------------------------------------------------------------- 1 | package org.example.springexamples.mock.service; 2 | 3 | 4 | import org.example.springexamples.mock.dox.User; 5 | import org.example.springexamples.mock.repository.UserRepositoryMock; 6 | import lombok.RequiredArgsConstructor; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.stereotype.Service; 9 | 10 | @Service 11 | @Slf4j 12 | @RequiredArgsConstructor 13 | public class UserService { 14 | private final UserRepositoryMock userRepositoryMock; 15 | 16 | public User addUser(User user) { 17 | return userRepositoryMock.save(user); 18 | } 19 | 20 | public User getUser(String uid) { 21 | return userRepositoryMock.findById(uid); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /springmvc-examples/src/main/java/org/example/springmvcexamples/example06/interceptor/service/UserService06.java: -------------------------------------------------------------------------------- 1 | package org.example.springmvcexamples.example06.interceptor.service; 2 | 3 | 4 | import org.example.springmvcexamples.example06.interceptor.dox.User06; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.stereotype.Service; 7 | 8 | @Service 9 | @Slf4j 10 | public class UserService06 { 11 | 12 | public User06 getUser(String userName) { 13 | return "BO".equals(userName) 14 | ? User06.builder().id("98416621588") 15 | .userName("BO") 16 | .role(User06.ADMIN) 17 | .password("$2a$10$vbic.eN8nCmnzExjVIUUwOKsIAz0.NGEYC/IGwjWJHSCC8s37Kn9G") 18 | .build() 19 | : null; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /webflux-r2dbc-examples/src/main/java/org/example/webfluxr2dbcexamples/repository/UserRepository.java: -------------------------------------------------------------------------------- 1 | package org.example.webfluxr2dbcexamples.repository; 2 | 3 | import org.example.webfluxr2dbcexamples.dox.UserReact; 4 | import org.springframework.data.r2dbc.repository.Query; 5 | import org.springframework.data.repository.reactive.ReactiveCrudRepository; 6 | import org.springframework.stereotype.Repository; 7 | import reactor.core.publisher.Flux; 8 | import reactor.core.publisher.Mono; 9 | 10 | @Repository 11 | public interface UserRepository extends ReactiveCrudRepository { 12 | 13 | Mono findByAccount(String account); 14 | 15 | @Query(""" 16 | select * from user_react u where u.role=:role; 17 | """) 18 | Flux findByRole(String role); 19 | } 20 | -------------------------------------------------------------------------------- /jdbc-examples/src/main/java/org/example/jdbcexamples/dox/Address.java: -------------------------------------------------------------------------------- 1 | package org.example.jdbcexamples.dox; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | import org.springframework.data.annotation.CreatedBy; 8 | import org.springframework.data.annotation.Id; 9 | import org.springframework.data.annotation.ReadOnlyProperty; 10 | 11 | import java.time.LocalDateTime; 12 | 13 | @Data 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | @Builder 17 | public class Address { 18 | @Id 19 | @CreatedBy 20 | private String id; 21 | private String detail; 22 | private String userId; 23 | @ReadOnlyProperty 24 | private LocalDateTime createTime; 25 | @ReadOnlyProperty 26 | private LocalDateTime updateTime; 27 | } 28 | -------------------------------------------------------------------------------- /jdbc-examples/src/main/java/org/example/jdbcexamples/dox/Teacher.java: -------------------------------------------------------------------------------- 1 | package org.example.jdbcexamples.dox; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | import org.springframework.data.annotation.CreatedBy; 8 | import org.springframework.data.annotation.Id; 9 | import org.springframework.data.annotation.ReadOnlyProperty; 10 | 11 | import java.time.LocalDateTime; 12 | 13 | @Data 14 | @AllArgsConstructor 15 | @NoArgsConstructor 16 | @Builder 17 | public class Teacher { 18 | @Id 19 | @CreatedBy 20 | private String id; 21 | private String name; 22 | private String department; 23 | @ReadOnlyProperty 24 | private LocalDateTime createTime; 25 | @ReadOnlyProperty 26 | private LocalDateTime updateTime; 27 | } 28 | -------------------------------------------------------------------------------- /spring-examples/src/test/java/org/example/springexamples/example03/AOPServiceTest.java: -------------------------------------------------------------------------------- 1 | package org.example.springexamples.example03; 2 | 3 | import org.example.springexamples.example03.aopadvanced.AOPService03; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.junit.jupiter.api.Test; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.boot.test.context.SpringBootTest; 8 | 9 | @SpringBootTest 10 | @Slf4j 11 | public class AOPServiceTest { 12 | @Autowired 13 | private AOPService03 aopService03; 14 | 15 | @Test 16 | public void test_getUser() { 17 | aopService03.getUser(); 18 | } 19 | 20 | @Test 21 | public void test_getAdmin() { 22 | // Assertions.assertThrows(XException.class, () -> aopService03.getAdmin()); 23 | aopService03.getAdmin(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /spring-examples/src/main/java/org/example/springexamples/example01/aop/MyAspect.java: -------------------------------------------------------------------------------- 1 | package org.example.springexamples.example01.aop; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.aspectj.lang.annotation.After; 5 | import org.aspectj.lang.annotation.Aspect; 6 | import org.aspectj.lang.annotation.Before; 7 | import org.aspectj.lang.annotation.Pointcut; 8 | import org.springframework.stereotype.Component; 9 | 10 | @Component 11 | @Aspect 12 | @Slf4j 13 | public class MyAspect { 14 | @Pointcut("execution(* org.example.springexamples.example01.aop..*.*(..))") 15 | public void pointcut(){} 16 | 17 | @Before("pointcut()") 18 | public void beforeAdvice() { 19 | log.debug("beforeAdvice()"); 20 | } 21 | @After("pointcut()") 22 | public void afterAdvice() { 23 | log.debug("afterAdvice()"); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /springmvc-examples/src/main/java/org/example/springmvcexamples/example02/handlingexception/service/UserService02.java: -------------------------------------------------------------------------------- 1 | package org.example.springmvcexamples.example02.handlingexception.service; 2 | 3 | import org.example.springmvcexamples.exception.XException; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.stereotype.Service; 6 | 7 | import java.io.IOException; 8 | import java.nio.file.Files; 9 | import java.nio.file.Path; 10 | 11 | @Service 12 | @Slf4j 13 | public class UserService02 { 14 | public void readFile() { 15 | try { 16 | Files.readString(Path.of("A:/aa.aa")); 17 | } catch (IOException e) { 18 | throw XException.builder() 19 | .codeN(500) 20 | .message("读取文件错误!" + e.getMessage()) 21 | .build(); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /jdbc-examples/src/main/java/org/example/jdbcexamples/repository/TeacherRepository.java: -------------------------------------------------------------------------------- 1 | package org.example.jdbcexamples.repository; 2 | 3 | import org.example.jdbcexamples.dox.Teacher; 4 | import org.springframework.data.jdbc.repository.query.Modifying; 5 | import org.springframework.data.jdbc.repository.query.Query; 6 | import org.springframework.data.repository.CrudRepository; 7 | import org.springframework.stereotype.Repository; 8 | 9 | @Repository 10 | public interface TeacherRepository extends CrudRepository { 11 | // json_set()函数,追加/更新指定JSON字段中的属性值。并返回结果 12 | @Modifying 13 | @Query(""" 14 | update teacher t 15 | set t.department=json_set(t.department, '$.departmentName', :name) 16 | where t.department ->> '$.depId'=:depId 17 | """) 18 | int updateDepartmentName(String depId, String name); 19 | } 20 | -------------------------------------------------------------------------------- /springsecurity-examples/src/main/java/org/example/springsecurityexamples/entity/User.java: -------------------------------------------------------------------------------- 1 | package org.example.springsecurityexamples.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | import java.util.List; 10 | 11 | @Data 12 | @Builder 13 | @NoArgsConstructor 14 | @AllArgsConstructor 15 | public class User { 16 | private Long id; 17 | private Long departmentId; 18 | private String name; 19 | private String account; 20 | @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) 21 | private String password; 22 | @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) 23 | private String role; 24 | @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) 25 | private List authorities; 26 | } 27 | -------------------------------------------------------------------------------- /springmvc-examples/src/test/http/example01.http: -------------------------------------------------------------------------------- 1 | ### 2 | GET http://localhost:8080/api/example01/index 3 | 4 | ### 5 | GET http://localhost:8080/api/example01/addresses 6 | 7 | ### 8 | GET http://localhost:8080/api/example01/user/addresses 9 | 10 | ### 11 | POST http://localhost:8080/api/example01/addresses 12 | Content-Type: application/json 13 | 14 | { 15 | "detail": "956", 16 | "comment": "测试" 17 | } 18 | 19 | ### 20 | GET http://localhost:8080/api/example01/addresses/3 21 | 22 | ### 23 | POST http://localhost:8080/api/example01/addresses02 24 | Content-Type: application/json 25 | 26 | { 27 | "detail": "956", 28 | "comment": "测试", 29 | "user": { 30 | "id": 10 31 | } 32 | } 33 | ### 34 | GET http://localhost:8080/api/example01/inject 35 | 36 | ### 37 | GET http://localhost:8080/api/example01/addresses-pages 38 | 39 | ### 40 | GET http://localhost:8080/api/example01/addresses-pages/2 -------------------------------------------------------------------------------- /webflux-r2dbc-examples/src/test/java/org/example/webfluxr2dbcexamples/ReactiveTest.java: -------------------------------------------------------------------------------- 1 | package org.example.webfluxr2dbcexamples; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import reactor.core.publisher.Flux; 5 | import reactor.core.publisher.Mono; 6 | 7 | import java.util.List; 8 | 9 | public class ReactiveTest { 10 | @Test 11 | public void FluxTest1() { 12 | // 创建一个包含多个元素的发布者 13 | Flux flux1 = Flux.just(1, 2, 3, 4); 14 | // 当此发布者被订阅时,顺序输出数据流中元素 15 | flux1.subscribe(System.out::println); 16 | System.out.println("-------------"); 17 | // 基于Flux发布者,将订阅元素转为聚合为List集合的Mono发布者 18 | Mono> listMono = flux1.collectList(); 19 | // 订阅Mono发布者,`推送`集合元素,响应式获取并操作 20 | listMono.subscribe(ints -> { 21 | ints.forEach(System.out::println); 22 | }); 23 | listMono.then(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /webflux-r2dbc-examples/src/test/java/org/example/webfluxr2dbcexamples/service/UserServiceTest.java: -------------------------------------------------------------------------------- 1 | package org.example.webfluxr2dbcexamples.service; 2 | 3 | import org.example.webfluxr2dbcexamples.dox.UserReact; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.junit.jupiter.api.Test; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.boot.test.context.SpringBootTest; 8 | import reactor.core.publisher.Mono; 9 | 10 | @SpringBootTest 11 | @Slf4j 12 | class UserServiceTest { 13 | @Autowired 14 | private UserService userService; 15 | @Test 16 | void addUser() { 17 | UserReact u = UserReact.builder().account("admin").build(); 18 | userService.addUser(u) 19 | .onErrorResume(e -> { 20 | log.debug(e.toString()); 21 | return Mono.empty(); 22 | }).block(); 23 | } 24 | } -------------------------------------------------------------------------------- /redis-examples/src/test/redis.sql: -------------------------------------------------------------------------------- 1 | ft.dropindex st_index 2 | 3 | ft.create st_index_v1 prefix 1 st: language 'chinese' schema title text content text 4 | 5 | ft.aliasadd st_index st_index_v1 6 | 7 | hset st:1001 title 'Java 24 正式版发布' content 'Java 24(Oracle JDK 24)已于 2025 年 3 月 18 日正式发布,包含 24 项功能。' 8 | 9 | hset st:2002 title 'ntpdate 同步系统时间' content '在 CentOS 7 上 ntpdate 是一个传统的命令行工具,用于手动同步系统时间。' 10 | 11 | hset st:3003 title 'Redis主从同步机制' content 'Redis 读写分离并不是完全的数据实时同步,因为从节点的数据可能会有一定的延迟。' 12 | 13 | ft.search st_index 'java' 14 | 15 | ft.search st_index '正式版' 16 | 17 | ft.search st_index '同步时间' 18 | 19 | ft.search st_index '同步' 20 | # OR 21 | ft.search st_index '同步|时间' 22 | # 检索指定属性 23 | ft.search st_index '(@title:同步|@title:时间)' 24 | # 索引信息 25 | ft.info st_index 26 | 27 | FT._LIST 28 | 29 | ft.explain st_index "中国人工智能" 30 | 31 | ft.explain st_index "同步时间" 32 | 33 | ft.explain st_index "Java 24 正式版发布" 34 | 35 | ft.search st_index '布' -------------------------------------------------------------------------------- /springmvc-examples/src/main/java/org/example/springmvcexamples/example03/beanvalidation/dox/User03.java: -------------------------------------------------------------------------------- 1 | package org.example.springmvcexamples.example03.beanvalidation.dox; 2 | 3 | import jakarta.validation.constraints.Email; 4 | import jakarta.validation.constraints.Max; 5 | import jakarta.validation.constraints.Min; 6 | import jakarta.validation.constraints.Size; 7 | import lombok.*; 8 | 9 | @Data 10 | @Builder 11 | @AllArgsConstructor 12 | @NoArgsConstructor 13 | public class User03 { 14 | private String id; 15 | @Size(min = 2, max = 6, 16 | message = "您输入的值为${validatedValue},用户名长度必须大于{min},小于{max}") 17 | private String name; 18 | @Min(value = 18, 19 | message = "您输入的值为${validatedValue},年龄不能小于{value}") 20 | @Max(value = 60, 21 | message = "您输入的值为${validatedValue},年龄不能大于{value}") 22 | private int age; 23 | @Email(message = "Email不合法") 24 | private String email; 25 | } 26 | 27 | -------------------------------------------------------------------------------- /project_mentor.md: -------------------------------------------------------------------------------- 1 | # 项目设计 2 | ### 项目要求 3 | 项目设计占总成绩40%。 4 | 每名学生独立完成以下任一项目的前端或后端的设计与实现。 5 | 可自由组队完成项目整合的设计与实现,有加分。 6 | 允许全部独立完成,有加分。 7 | 前端使用vue/react等框架,鼓励使用element-ui/vuetify等UI框架,有加分。 8 | 后端使用springboot等多种框架MySQL数据库等,鼓励使用cache/redis/nacos等框架,有加分。 9 | 部署在Docker容器中有加分。 10 | 11 | 必须提交到远程仓库,要有多次提交节点,github/gitee等均可。项目可写入简历,好好维护readme。 12 | 前后端同学,共同分析需求功能建模后,约定功能请求接口,约定功能所需数据结构。 13 | 后端基于数据结构设计DO/DTO类,前端基于数据结构设计接口。 14 | 15 | #### 课设需求 16 | 毕设选导师系统 17 | https://github.com/bwhyman/mentor-selection 18 | 19 | #### 基本功能 20 | 模拟若干学生:姓名,学号 21 | 模拟若干教师:姓名,工号,带学生总数,已选学生数 22 | 学号/工号为登录账号密码 23 | 登录后,账号密码相同时,弹出警告框提示修改密码,可忽略 24 | 登录后,可修改密码 25 | 登录后,从header role辨识角色身份,路由不同组件 26 | mock不同学生/教师账号演示 27 | 28 | #### 学生 29 | 学生显示教师列表,包括:姓名,带学生总数,当前已选学生数;名额已满教师可见但无法选择 30 | 选择教师后弹出最终确认框提交,教师已选学生数+1; 31 | 已选学生再次登录,不再加载教师列表,仅显示选择教师姓名。mock数据即可 32 | 33 | #### 教师 34 | 显示选择登录教师的学生列表 35 | 36 | #### Others 37 | 自己基于需求,合理设计接口结构 38 | 无需后端 39 | 无需读写excel -------------------------------------------------------------------------------- /redis-examples/src/test/java/org/example/redisexamples/service/ExpireServiceTest.java: -------------------------------------------------------------------------------- 1 | package org.example.redisexamples.service; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.junit.jupiter.api.Test; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.context.SpringBootTest; 7 | 8 | import java.util.concurrent.TimeUnit; 9 | 10 | @SpringBootTest 11 | @Slf4j 12 | public class ExpireServiceTest { 13 | @Autowired 14 | private ExpireService expireService; 15 | 16 | @Test 17 | void expireTest() throws InterruptedException { 18 | // 模拟API请求限流。 19 | var key = "expires:agentids:6561"; 20 | var count = 2; 21 | var expireSec = 5; 22 | for (int i = 0; i < 11; i++) { 23 | boolean success = expireService.requestCheck(key, count, expireSec); 24 | log.debug("{}", success); 25 | TimeUnit.SECONDS.sleep(1); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /springmvc-examples/src/test/java/org/example/springmvcexamples/example04/PasswordEncoderTest.java: -------------------------------------------------------------------------------- 1 | package org.example.springmvcexamples.example04; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.junit.jupiter.api.Test; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.context.SpringBootTest; 7 | import org.springframework.security.crypto.password.PasswordEncoder; 8 | 9 | @SpringBootTest 10 | @Slf4j 11 | public class PasswordEncoderTest { 12 | @Autowired 13 | private PasswordEncoder encoder; 14 | 15 | @Test 16 | public void test_password() { 17 | String pwd = "123456"; 18 | log.debug(encoder.encode(pwd)); 19 | log.debug(encoder.encode(pwd)); 20 | String result = encoder.encode(pwd); 21 | log.debug("{}", result.length()); 22 | log.debug("{}", encoder.matches("12345", result)); 23 | log.debug("{}", encoder.matches("123456", result)); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /jdbc-examples/src/main/java/org/example/jdbcexamples/service/UserTransService.java: -------------------------------------------------------------------------------- 1 | package org.example.jdbcexamples.service; 2 | 3 | import org.example.jdbcexamples.dox.User; 4 | import org.example.jdbcexamples.repository.UserRepository; 5 | import lombok.RequiredArgsConstructor; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.stereotype.Service; 8 | import org.springframework.transaction.annotation.Transactional; 9 | 10 | import java.io.IOException; 11 | import java.nio.file.Files; 12 | import java.nio.file.Path; 13 | 14 | @Service 15 | @Slf4j 16 | @RequiredArgsConstructor 17 | public class UserTransService { 18 | private final UserRepository userRepository; 19 | 20 | @Transactional 21 | public User addUser(User user) { 22 | User u = userRepository.save(user); 23 | try { 24 | Files.readString(Path.of("A:/aa.aa")); 25 | } catch (IOException e) { 26 | throw new RuntimeException(e); 27 | } 28 | return u; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /jdbc-examples/src/main/java/org/example/jdbcexamples/repository/AccountPessRepository.java: -------------------------------------------------------------------------------- 1 | package org.example.jdbcexamples.repository; 2 | 3 | import org.example.jdbcexamples.dox.AccountPess; 4 | import org.springframework.data.jdbc.repository.query.Modifying; 5 | import org.springframework.data.jdbc.repository.query.Query; 6 | import org.springframework.data.repository.CrudRepository; 7 | import org.springframework.stereotype.Repository; 8 | 9 | import java.util.Optional; 10 | 11 | @Repository 12 | public interface AccountPessRepository extends CrudRepository { 13 | @Query(""" 14 | select * from account_pess a where a.id=:uid for update 15 | """) 16 | Optional findByIdPess(String uid); 17 | 18 | @Modifying 19 | @Query(""" 20 | update account_pess a 21 | set a.balance=(a.balance-:spend) 22 | where a.id=:uid and a.balance>=:spend 23 | """) 24 | int updateBalance(String uid, float spend); 25 | } 26 | -------------------------------------------------------------------------------- /webflux-r2dbc-examples/README.md: -------------------------------------------------------------------------------- 1 | # webflux-r2dbc-examples 2 | 3 | ### Reactive Flux/Mono 4 | 5 | Flux/Mono发布者 6 | map()/flatMap()/filter()/handle()/mapNotNull()/switchIfEmpty()/defaultIfEmpty()/Mono.defer()/onErrorResume()/Mono.error()/mapNotNull()/Flux.merge()/Mono.when()/Mono.just()/Flux.just()/then()/thenReturn()/Flux.from()/Flux.fromIterable()/cast()等方法。 7 | 8 | ### WebFlux 9 | 10 | 异步非阻塞Web框架,支持背压。 11 | webflux中禁止阻塞操作,所有方法必须返回Flux/Mono订阅,直到controller方法返回。用户发送到controller的请求即为消息订阅者。 12 | 使用异步ServerHttpResponse/ServerHttpRequest接口替代Servlet接口,可直接通过@RequestAttribute注解注入controller方法。 13 | 14 | ### WebFilter 15 | 16 | 拦截器通过WebFilter接口实现 17 | 拦截路径不再单独注册声明,默认全部拦截,需手动通过请求路径判断拦截/排除规则。 18 | 通过order声明拦截顺序。 19 | 20 | ### WebExceptionHandler 21 | 22 | 在filter中抛出的异常无法在全局异常中捕获,必须通过WebExceptionHandler接口单独处理。 23 | 因此,在拦截器校验失败后,不再抛出异常,而是直接响应错误数据。 24 | 25 | ### R2DBC 26 | 27 | 标准JDBC是阻塞的,通过r2dbc实现整合webflux的全非阻塞实现。 28 | 29 | ### @EventListener 30 | 31 | 可通过注解实现应用多种事件的监听回调。可通过监听ApplicationReadyEvent事件在应用就绪后执行初始化数据的插入 32 | -------------------------------------------------------------------------------- /springmvc-examples/src/main/java/org/example/springmvcexamples/example03/beanvalidation/controller/ExampleController03.java: -------------------------------------------------------------------------------- 1 | package org.example.springmvcexamples.example03.beanvalidation.controller; 2 | 3 | import org.example.springmvcexamples.example03.beanvalidation.dox.User03; 4 | import jakarta.validation.Valid; 5 | import jakarta.validation.constraints.Size; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.validation.annotation.Validated; 8 | import org.springframework.web.bind.annotation.*; 9 | 10 | @Slf4j 11 | @RestController 12 | @RequestMapping("/api/example03/") 13 | @Validated 14 | public class ExampleController03 { 15 | 16 | @PostMapping("users") 17 | public void postUser(@Valid @RequestBody User03 user) { 18 | } 19 | 20 | @GetMapping("users/{uid}/file") 21 | public void getTypeMismatchException(@PathVariable int uid) { 22 | } 23 | 24 | @GetMapping("users/{owner}") 25 | public void getViolationException( 26 | @Size(min = 2, max = 6, message = "用户信息错误") 27 | @PathVariable String owner) { 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 BO 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /springmvc-examples/src/test/java/org/example/springmvcexamples/example05/JWTTest.java: -------------------------------------------------------------------------------- 1 | package org.example.springmvcexamples.example05; 2 | 3 | import org.example.springmvcexamples.component.JWTComponent; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.junit.jupiter.api.Test; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.boot.test.context.SpringBootTest; 8 | 9 | import java.util.Map; 10 | 11 | @SpringBootTest 12 | @Slf4j 13 | public class JWTTest { 14 | @Autowired 15 | private JWTComponent jwtComponent; 16 | 17 | @Test 18 | public void test() throws InterruptedException { 19 | String token = jwtComponent.encode(Map.of("uid", "1384896304762638307", "role", 9)); 20 | log.debug(token); 21 | log.debug("{}", token.length()); 22 | String uid = jwtComponent.decode(token).getClaim("uid").asString(); 23 | log.debug("{}", uid); 24 | 25 | // jwt组件中过期时间 26 | Thread.sleep(15000); 27 | String uid2 = jwtComponent.decode(token).getClaim("uid").asString(); 28 | log.debug("{}", uid2); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /springmvc-examples/src/main/java/org/example/springmvcexamples/example06/interceptor/interceptor/AdminInterceptor06.java: -------------------------------------------------------------------------------- 1 | package org.example.springmvcexamples.example06.interceptor.interceptor; 2 | 3 | import org.example.springmvcexamples.example06.interceptor.dox.User06; 4 | import org.example.springmvcexamples.exception.Code; 5 | import org.example.springmvcexamples.exception.XException; 6 | import jakarta.servlet.http.HttpServletRequest; 7 | import jakarta.servlet.http.HttpServletResponse; 8 | import lombok.RequiredArgsConstructor; 9 | import lombok.extern.slf4j.Slf4j; 10 | import org.springframework.stereotype.Component; 11 | import org.springframework.web.servlet.HandlerInterceptor; 12 | 13 | @Component 14 | @RequiredArgsConstructor 15 | @Slf4j 16 | public class AdminInterceptor06 implements HandlerInterceptor { 17 | @Override 18 | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { 19 | if(User06.ADMIN.equals(request.getAttribute("role"))) { 20 | return true; 21 | } 22 | throw XException.builder().code(Code.FORBIDDEN).build(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /redis-examples/src/main/java/org/example/redisexamples/listener/OrderMessageListener.java: -------------------------------------------------------------------------------- 1 | package org.example.redisexamples.listener; 2 | 3 | import org.example.redisexamples.dox.Order; 4 | import lombok.RequiredArgsConstructor; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.data.redis.connection.stream.ObjectRecord; 7 | import org.springframework.data.redis.core.RedisTemplate; 8 | import org.springframework.data.redis.stream.StreamListener; 9 | import org.springframework.stereotype.Component; 10 | 11 | @Component 12 | @Slf4j 13 | @RequiredArgsConstructor 14 | public class OrderMessageListener implements StreamListener> { 15 | 16 | private final RedisTemplate template; 17 | 18 | @Override 19 | public void onMessage(ObjectRecord message) { 20 | log.debug("onMessage: mid: {} / orderid: {}", message.getId(), message.getValue()); 21 | // 通知redis,确认消费者消费了消息 22 | template.opsForStream().acknowledge(Order.GROUP_NAME, message); 23 | // 移除已消费消息。为测试没有移除 24 | template.opsForStream().delete(Order.STREAM_KEY, message.getId()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /springmvc-examples/src/main/java/org/example/springmvcexamples/vo/ResultVO.java: -------------------------------------------------------------------------------- 1 | package org.example.springmvcexamples.vo; 2 | 3 | import org.example.springmvcexamples.exception.Code; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | 7 | import java.util.Map; 8 | 9 | @Data 10 | @Builder 11 | public class ResultVO { 12 | private int code; 13 | private String message; 14 | private Object data; 15 | 16 | private static final ResultVO EMPTY = ResultVO.builder() 17 | .code(200) 18 | .data(Map.of()) 19 | .build(); 20 | 21 | public static ResultVO ok() { 22 | return EMPTY; 23 | } 24 | 25 | public static ResultVO success(Object data) { 26 | return ResultVO.builder().code(200).data(data).build(); 27 | } 28 | 29 | public static ResultVO error(Code code) { 30 | return ResultVO.builder() 31 | .code(code.getCode()) 32 | .message(code.getMessage()) 33 | .build(); 34 | } 35 | 36 | public static ResultVO error(int code, String msg) { 37 | return ResultVO.builder().code(code).message(msg).build(); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /cache-examples/src/main/java/org/example/cacheexamples/controller/MyController.java: -------------------------------------------------------------------------------- 1 | package org.example.cacheexamples.controller; 2 | 3 | import org.example.cacheexamples.dox.User; 4 | import org.example.cacheexamples.service.UserService; 5 | import lombok.RequiredArgsConstructor; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.web.bind.annotation.*; 8 | 9 | import java.util.List; 10 | 11 | @RestController 12 | @Slf4j 13 | @RequiredArgsConstructor 14 | @RequestMapping("/api/") 15 | public class MyController { 16 | 17 | private final UserService userService; 18 | 19 | @GetMapping("users/{uid}") 20 | public User getUser(@PathVariable String uid) { 21 | return userService.getUser(uid); 22 | } 23 | 24 | @GetMapping("users") 25 | public List listUsers() { 26 | return userService.listUsers(); 27 | } 28 | 29 | @PatchMapping("users") 30 | public User patchUser(@RequestBody User user) { 31 | return userService.updateUser(user); 32 | } 33 | 34 | @DeleteMapping("users/{uid}") 35 | public void delUser(@PathVariable String uid) { 36 | userService.delUser(uid); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /webflux-r2dbc-examples/src/main/java/org/example/webfluxr2dbcexamples/vo/ResultVO.java: -------------------------------------------------------------------------------- 1 | package org.example.webfluxr2dbcexamples.vo; 2 | 3 | import org.example.webfluxr2dbcexamples.exception.Code; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | 7 | import java.util.Map; 8 | 9 | @Data 10 | @Builder 11 | public class ResultVO { 12 | private int code; 13 | private String message; 14 | private Object data; 15 | 16 | private static final ResultVO EMPTY = ResultVO.builder() 17 | .code(200) 18 | .data(Map.of()) 19 | .build(); 20 | 21 | public static ResultVO ok() { 22 | return EMPTY; 23 | } 24 | 25 | public static ResultVO success(Object data) { 26 | return ResultVO.builder().code(200).data(data).build(); 27 | } 28 | 29 | public static ResultVO error(int code, String msg) { 30 | return ResultVO.builder().code(code).message(msg).build(); 31 | } 32 | 33 | public static ResultVO error(Code code) { 34 | return ResultVO.builder() 35 | .code(code.getCode()) 36 | .message(code.getMessage()) 37 | .build(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /springmvc-examples/src/main/java/org/example/springmvcexamples/component/OpenAPIConfig.java: -------------------------------------------------------------------------------- 1 | package org.example.springmvcexamples.component; 2 | 3 | import io.swagger.v3.oas.models.OpenAPI; 4 | import io.swagger.v3.oas.models.security.SecurityRequirement; 5 | import io.swagger.v3.oas.models.security.SecurityScheme; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.http.HttpHeaders; 9 | 10 | @Configuration 11 | public class OpenAPIConfig { 12 | @Bean 13 | public OpenAPI customOpenAPI() { 14 | SecurityScheme token = new SecurityScheme() 15 | .type(SecurityScheme.Type.APIKEY) 16 | // 按约定命名的header中属性名称 17 | .name("token") 18 | .in(SecurityScheme.In.HEADER); 19 | SecurityRequirement securityRequirement = new SecurityRequirement() 20 | .addList(HttpHeaders.AUTHORIZATION); 21 | return new OpenAPI() 22 | .schemaRequirement(HttpHeaders.AUTHORIZATION, token) 23 | // 在全局请求添加header信息 24 | .addSecurityItem(securityRequirement); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /springsecurity-examples/src/main/java/org/example/springsecurityexamples/vo/ResultVO.java: -------------------------------------------------------------------------------- 1 | package org.example.springsecurityexamples.vo; 2 | 3 | import lombok.Builder; 4 | import lombok.Data; 5 | import org.example.springsecurityexamples.exception.Code; 6 | 7 | import java.util.Map; 8 | 9 | @Data 10 | @Builder 11 | public class ResultVO { 12 | private int code; 13 | private String message; 14 | private Object data; 15 | 16 | private static final ResultVO EMPTY = ResultVO.builder() 17 | .code(200) 18 | .data(Map.of()) 19 | .build(); 20 | 21 | public static ResultVO ok() { 22 | return EMPTY; 23 | } 24 | 25 | public static ResultVO success(Object data) { 26 | return ResultVO.builder().code(200).data(data).build(); 27 | } 28 | 29 | public static ResultVO error(Code code) { 30 | return ResultVO.builder() 31 | .code(code.getCode()) 32 | .message(code.getMessage()) 33 | .build(); 34 | } 35 | 36 | public static ResultVO error(int code, String msg) { 37 | return ResultVO.builder().code(code).message(msg).build(); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /redis-examples/src/main/resources/mylib.lua: -------------------------------------------------------------------------------- 1 | -- 模块通过spring事件监听器启动时自动注册 2 | local field = 'total' 3 | local function rushBuy_0(keys, args) 4 | -- 预操作商品记录的键 5 | local hkey = keys[1] 6 | -- 获取hash记录中的数量字段,按字符串比较 7 | if redis.call('hget', hkey, field) == '0' then 8 | return -1 9 | end 10 | -- 还有剩余则减一,并返回剩余数量 11 | -- hincrby指令返回的是执行后字段的`数字`而非字符串 12 | return redis.call('hincrby', hkey, field, -1) 13 | end 14 | redis.register_function('rushBuy_0', rushBuy_0) 15 | 16 | -- 判断expireSec秒内,执行超过count次返回flase 17 | local function expireAPICount_0(keys, args) 18 | local hkey = keys[1] 19 | -- lua函数参数仅支持传入java string/number类型 20 | -- 但均按redis string类型处理 21 | local expireSec = args[1] 22 | local count = args[2] 23 | -- 键不存在,则为时效内的第一次。此处返回数字`0/1` 24 | if redis.call('exists', hkey) == 0 then 25 | -- 同时创建记录与过期时间 26 | redis.call('setex', hkey, expireSec, 1) 27 | return true 28 | end 29 | -- 按字符串比较 30 | if redis.call('get', hkey) >= count then 31 | return false 32 | end 33 | -- incr指令仅支持+1 34 | redis.call('incr', hkey) 35 | return true 36 | end 37 | redis.register_function('expireAPICount_0', expireAPICount_0) -------------------------------------------------------------------------------- /webflux-r2dbc-examples/src/main/java/org/example/webfluxr2dbcexamples/dox/UserReact.java: -------------------------------------------------------------------------------- 1 | package org.example.webfluxr2dbcexamples.dox; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Builder; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | import org.springframework.data.annotation.CreatedBy; 10 | import org.springframework.data.annotation.Id; 11 | import org.springframework.data.annotation.ReadOnlyProperty; 12 | 13 | import java.time.LocalDateTime; 14 | 15 | @Data 16 | @NoArgsConstructor 17 | @AllArgsConstructor 18 | @Builder 19 | public class UserReact { 20 | public static final String ROLE_USER = "hOl7U"; 21 | public static final String ROLE_ADMIN = "yxp4r"; 22 | @Id 23 | @CreatedBy 24 | private String id; 25 | private String name; 26 | private String account; 27 | @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) 28 | private String password; 29 | @JsonIgnore 30 | private String role; 31 | @ReadOnlyProperty 32 | private LocalDateTime insertTime; 33 | @ReadOnlyProperty 34 | private LocalDateTime updateTime; 35 | } 36 | -------------------------------------------------------------------------------- /webflux-r2dbc-examples/src/main/java/org/example/webfluxr2dbcexamples/controller/AdminController.java: -------------------------------------------------------------------------------- 1 | package org.example.webfluxr2dbcexamples.controller; 2 | 3 | import org.example.webfluxr2dbcexamples.dox.UserReact; 4 | import org.example.webfluxr2dbcexamples.service.UserService; 5 | import org.example.webfluxr2dbcexamples.vo.RequestConstant; 6 | import org.example.webfluxr2dbcexamples.vo.ResultVO; 7 | import lombok.RequiredArgsConstructor; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.springframework.web.bind.annotation.*; 10 | import reactor.core.publisher.Mono; 11 | 12 | import java.util.Map; 13 | 14 | @RestController 15 | @Slf4j 16 | @RequiredArgsConstructor 17 | @RequestMapping("/api/admin/") 18 | public class AdminController { 19 | 20 | private final UserService userService; 21 | 22 | @PostMapping("users") 23 | public Mono postUsers(@RequestBody UserReact user) { 24 | return userService.addUser(user) 25 | .thenReturn(ResultVO.ok()); 26 | } 27 | 28 | @GetMapping("info") 29 | public Mono getInfo(@RequestAttribute(RequestConstant.UID) String uid) { 30 | return userService.getUserById(uid) 31 | .map(ResultVO::success); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /redis-examples/src/main/java/org/example/redisexamples/service/ExpireService.java: -------------------------------------------------------------------------------- 1 | package org.example.redisexamples.service; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.redisson.api.FunctionMode; 6 | import org.redisson.api.FunctionResult; 7 | import org.redisson.api.RFunction; 8 | import org.redisson.api.RedissonClient; 9 | import org.redisson.client.codec.IntegerCodec; 10 | import org.springframework.stereotype.Service; 11 | 12 | import java.util.List; 13 | 14 | @Slf4j 15 | @Service 16 | @RequiredArgsConstructor 17 | public class ExpireService { 18 | private final RedissonClient client; 19 | 20 | // expireSec秒内,执行超过count次返回flase 21 | // lua函数参数仅支持传入java string/number类型 22 | public boolean requestCheck(String key, int count, int expireSec) { 23 | // 获取redis 函数操作对象 24 | // 传入的参数会按默认codec序列化。因此显式声明按int传入 25 | RFunction rf = client.getFunction(IntegerCodec.INSTANCE); 26 | // 参数:调用模式;调用的注册函数名称;返回类型;keys;args... 27 | return rf.call( 28 | FunctionMode.WRITE, 29 | "expireAPICount_0", 30 | FunctionResult.BOOLEAN, 31 | List.of(key), 32 | expireSec, count); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /springmvc-examples/src/main/java/org/example/springmvcexamples/example02/handlingexception/controller/ExampleController02.java: -------------------------------------------------------------------------------- 1 | package org.example.springmvcexamples.example02.handlingexception.controller; 2 | 3 | import org.example.springmvcexamples.example02.handlingexception.dox.User; 4 | import org.example.springmvcexamples.example02.handlingexception.service.UserService02; 5 | import org.example.springmvcexamples.exception.Code; 6 | import org.example.springmvcexamples.vo.ResultVO; 7 | import lombok.RequiredArgsConstructor; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.springframework.web.bind.annotation.*; 10 | 11 | @Slf4j 12 | @RequiredArgsConstructor 13 | @RestController 14 | @RequestMapping("/api/example02/") 15 | public class ExampleController02 { 16 | 17 | private final UserService02 userService02; 18 | 19 | @GetMapping("exception") 20 | public void getException() { 21 | userService02.readFile(); 22 | } 23 | 24 | @PostMapping("login") 25 | public ResultVO login(@RequestBody User user) { 26 | if (!("BO".equals(user.getUserName()) && "123456".equals(user.getPassword()))) { 27 | return ResultVO.error(Code.LOGIN_ERROR); 28 | } 29 | return ResultVO.ok(); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /springmvc-examples/src/main/java/org/example/springmvcexamples/example06/interceptor/config/WebMvcConfig.java: -------------------------------------------------------------------------------- 1 | package org.example.springmvcexamples.example06.interceptor.config; 2 | 3 | import org.example.springmvcexamples.example06.interceptor.interceptor.AdminInterceptor06; 4 | import org.example.springmvcexamples.example06.interceptor.interceptor.LoginInterceptor06; 5 | import lombok.RequiredArgsConstructor; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.web.servlet.config.annotation.InterceptorRegistry; 8 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 9 | 10 | @Configuration 11 | @RequiredArgsConstructor 12 | public class WebMvcConfig implements WebMvcConfigurer { 13 | 14 | private final LoginInterceptor06 loginInterceptor06; 15 | private final AdminInterceptor06 adminInterceptor06; 16 | 17 | @Override 18 | public void addInterceptors(InterceptorRegistry registry) { 19 | registry.addInterceptor(loginInterceptor06) 20 | .addPathPatterns("/api/example06/**") 21 | .excludePathPatterns("/api/example06/open/**"); 22 | registry.addInterceptor(adminInterceptor06) 23 | .addPathPatterns("/api/example06/admin/**"); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /cache-examples/src/main/java/org/example/cacheexamples/repository/UserRepository.java: -------------------------------------------------------------------------------- 1 | package org.example.cacheexamples.repository; 2 | 3 | import org.example.cacheexamples.dox.User; 4 | import org.springframework.stereotype.Repository; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import java.util.Objects; 9 | 10 | @Repository 11 | public class UserRepository { 12 | private static final List USERS; 13 | static { 14 | User u1 = User.builder().id("1").name("BO").build(); 15 | User u2 = User.builder().id("2").name("SUN").build(); 16 | USERS = new ArrayList<>(); 17 | USERS.add(u1); 18 | USERS.add(u2); 19 | } 20 | public User getUser(String uid) { 21 | return USERS.stream() 22 | .filter(u -> u.getId().equals(uid)) 23 | .findFirst() 24 | .orElse(null); 25 | } 26 | // stream()产生新集合,而此模拟需要改变原集合 27 | public User updateUser(User user) { 28 | for (int i = 0; i < USERS.size(); i++) { 29 | if (Objects.equals(user.getId(), USERS.get(i).getId())) { 30 | USERS.set(i, user); 31 | } 32 | } 33 | return user; 34 | } 35 | public List listUsers() { 36 | return USERS; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /spring-examples/src/main/java/org/example/springexamples/example02/jointpoint/MyAspect02.java: -------------------------------------------------------------------------------- 1 | package org.example.springexamples.example02.jointpoint; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.aspectj.lang.JoinPoint; 5 | import org.aspectj.lang.ProceedingJoinPoint; 6 | import org.aspectj.lang.annotation.Around; 7 | import org.aspectj.lang.annotation.Aspect; 8 | import org.aspectj.lang.annotation.Pointcut; 9 | import org.springframework.stereotype.Component; 10 | 11 | @Component 12 | @Aspect 13 | @Slf4j 14 | public class MyAspect02 { 15 | @Pointcut("execution(* org.example.springexamples.example02..*.*(..))") 16 | public void pointcut() {} 17 | 18 | // @Before("pointcut()") 19 | public void before(JoinPoint joinPoint) { 20 | log.debug(joinPoint.getTarget().getClass().getName()); 21 | for (Object a : joinPoint.getArgs()) { 22 | log.debug("{}", a); 23 | } 24 | log.debug(joinPoint.getSignature().getName()); 25 | log.debug(joinPoint.getThis().getClass().getName()); 26 | } 27 | 28 | @Around("pointcut()") 29 | public Object around(ProceedingJoinPoint joinPoint) throws Throwable { 30 | Object[] objects = joinPoint.getArgs(); 31 | objects[0] = "SUN"; 32 | return joinPoint.proceed(objects); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /springsecurity-examples/src/main/java/org/example/springsecurityexamples/service/UserService.java: -------------------------------------------------------------------------------- 1 | package org.example.springsecurityexamples.service; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.example.springsecurityexamples.entity.User; 5 | import org.example.springsecurityexamples.security.Authorities; 6 | import org.example.springsecurityexamples.security.Roles; 7 | import org.springframework.stereotype.Service; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | @Slf4j 13 | @Service 14 | public class UserService { 15 | public User getUser(String account) { 16 | if(account.equals("2046204601")) { 17 | // 模拟2个权限,一个角色 18 | List authorities = new ArrayList<>(); 19 | authorities.add(Authorities.MANAGER_READ); 20 | authorities.add(Authorities.MANAGER_CREATE); 21 | return User.builder() 22 | .id(1415648458976919552L) 23 | .departmentId(1415649651350437888L) 24 | .name("BO") 25 | .password("$2a$10$6lqsVMLKAzMyXfwMIxsbzObqtgWB/WfGzp/Ic6X7GNLBj5CzA3Ng6") 26 | .role(Roles.MANAGER) 27 | .authorities(authorities) 28 | .build(); 29 | } 30 | return null; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /cache-examples/src/main/java/org/example/cacheexamples/config/SpringCacheConfig.java: -------------------------------------------------------------------------------- 1 | package org.example.cacheexamples.config; 2 | 3 | import com.github.benmanes.caffeine.cache.Cache; 4 | import com.github.benmanes.caffeine.cache.Caffeine; 5 | import org.springframework.cache.CacheManager; 6 | import org.springframework.cache.annotation.EnableCaching; 7 | import org.springframework.cache.caffeine.CaffeineCacheManager; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Configuration; 10 | 11 | import java.util.concurrent.TimeUnit; 12 | 13 | @Configuration 14 | @EnableCaching 15 | public class SpringCacheConfig { 16 | @Bean 17 | public CacheManager cacheManager() { 18 | // 创建Caffeine缓存管理器 19 | CaffeineCacheManager manager = new CaffeineCacheManager(); 20 | // 创建缓存配置策略 21 | Cache cache = Caffeine.newBuilder() 22 | // 10s内没有访问移除 23 | .expireAfterAccess(10, TimeUnit.SECONDS) 24 | .maximumSize(200) 25 | .build(); 26 | // 为指定名称的缓存,指定配置 27 | manager.registerCustomCache("user", cache); 28 | // 允许缓存值为空的键值对。避免缓存穿透 29 | manager.setAllowNullValues(true); 30 | // 将管理器注入容器,替换默认管理器对象 31 | return manager; 32 | } 33 | } 34 | 35 | -------------------------------------------------------------------------------- /redis-examples/src/main/java/org/example/redisexamples/listener/CreateRedisStreamListener.java: -------------------------------------------------------------------------------- 1 | package org.example.redisexamples.listener; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.example.redisexamples.dox.Order; 6 | import org.redisson.api.RStream; 7 | import org.redisson.api.RedissonClient; 8 | import org.redisson.api.stream.StreamCreateGroupArgs; 9 | import org.redisson.client.codec.StringCodec; 10 | import org.springframework.boot.context.event.ApplicationReadyEvent; 11 | import org.springframework.context.annotation.Configuration; 12 | import org.springframework.context.event.EventListener; 13 | 14 | @Configuration 15 | @Slf4j 16 | @RequiredArgsConstructor 17 | public class CreateRedisStreamListener { 18 | 19 | private final RedissonClient redissonClient; 20 | 21 | // 应用启动时在redis创建order stream 22 | // 确保使用时指定steam一定存在 23 | @EventListener(ApplicationReadyEvent.class) 24 | public void createOrderStream() { 25 | // 消息ID类型;消息体类型 26 | RStream stream = redissonClient.getStream(Order.STREAM_KEY, StringCodec.INSTANCE); 27 | if (!stream.isExists()) { 28 | // 如果键不存在,创建stream键/消费组/消费组 29 | stream.createGroup(StreamCreateGroupArgs.name(Order.GROUP_NAME).makeStream()); 30 | stream.createConsumer(Order.GROUP_NAME, Order.GROUP_CONSUMER); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /redis-examples/src/main/java/org/example/redisexamples/listener/MessageScheduledListener.java: -------------------------------------------------------------------------------- 1 | package org.example.redisexamples.listener; 2 | 3 | import org.example.redisexamples.dox.Order; 4 | import lombok.RequiredArgsConstructor; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.redisson.api.RStream; 7 | import org.redisson.api.RedissonClient; 8 | import org.redisson.api.StreamMessageId; 9 | import org.redisson.api.stream.StreamReadGroupArgs; 10 | import org.redisson.client.codec.StringCodec; 11 | import org.springframework.scheduling.annotation.Scheduled; 12 | 13 | import java.util.Map; 14 | 15 | //@Component 16 | //@EnableScheduling 17 | @Slf4j 18 | @RequiredArgsConstructor 19 | public class MessageScheduledListener { 20 | private final RedissonClient client; 21 | 22 | @Scheduled(fixedDelay = 2000) 23 | public void onMessage() { 24 | // 初始化消费组 25 | RStream stream = client.getStream(Order.STREAM_KEY, StringCodec.INSTANCE); 26 | Map> sm = stream.readGroup(Order.GROUP_NAME, Order.GROUP_CONSUMER, StreamReadGroupArgs.neverDelivered()); 27 | sm.forEach((mid, v) -> { 28 | log.debug("MessageId: {} / orderid: {}", mid, v); 29 | // 返回确认消费者消费了消息 30 | stream.ack(Order.GROUP_NAME, mid); 31 | // 移除已消费消息。为测试没有移除 32 | // stream.remove(mid); 33 | }); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /redis-examples/src/main/java/org/example/redisexamples/listener/LoadRedisScriptListener.java: -------------------------------------------------------------------------------- 1 | package org.example.redisexamples.listener; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.redisson.api.RFunction; 6 | import org.redisson.api.RedissonClient; 7 | import org.springframework.beans.factory.annotation.Value; 8 | import org.springframework.boot.context.event.ApplicationReadyEvent; 9 | import org.springframework.context.annotation.Configuration; 10 | import org.springframework.context.event.EventListener; 11 | import org.springframework.core.io.Resource; 12 | 13 | import java.io.IOException; 14 | import java.nio.charset.Charset; 15 | 16 | @Configuration 17 | @Slf4j 18 | @RequiredArgsConstructor 19 | public class LoadRedisScriptListener { 20 | 21 | private final RedissonClient redissonClient; 22 | 23 | // 直接从编译后的classpath路径下读取文件 24 | @Value("mylib.lua") 25 | private Resource resource; 26 | 27 | @EventListener(ApplicationReadyEvent.class) 28 | public void loadRedisScript() throws IOException { 29 | // 读取脚本内容 30 | String script = resource.getContentAsString(Charset.defaultCharset()); 31 | RFunction rf = redissonClient.getFunction(); 32 | // 由于使用的是同一个redis数据库实例,如果库/函数名称相同,加载时会覆盖 33 | // 因此,以数据库编号为后缀区别 34 | // replace,用于重启微服务时覆盖避免冲突 35 | rf.loadAndReplace("mylib_" + 0, script); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /springsecurity-examples/src/main/java/org/example/springsecurityexamples/controller/ExceptionController.java: -------------------------------------------------------------------------------- 1 | package org.example.springsecurityexamples.controller; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.example.springsecurityexamples.exception.Code; 5 | import org.example.springsecurityexamples.exception.XException; 6 | import org.example.springsecurityexamples.vo.ResultVO; 7 | import org.springframework.security.access.AccessDeniedException; 8 | import org.springframework.web.bind.annotation.ExceptionHandler; 9 | import org.springframework.web.bind.annotation.RestControllerAdvice; 10 | 11 | @RestControllerAdvice 12 | @Slf4j 13 | public class ExceptionController { 14 | 15 | // 捕获方法级无权限 16 | @ExceptionHandler(AccessDeniedException.class) 17 | public ResultVO handleAccessDeniedException(AccessDeniedException exception) { 18 | return ResultVO.error(Code.FORBIDDEN); 19 | } 20 | 21 | @ExceptionHandler(XException.class) 22 | public ResultVO handleXException(XException exception) { 23 | if (exception.getCode() != null) { 24 | return ResultVO.error(exception.getCode()); 25 | } 26 | return ResultVO.error(Code.ERROR, exception.getMessage()); 27 | } 28 | 29 | @ExceptionHandler(Exception.class) 30 | public ResultVO handleException(Exception exception) { 31 | return ResultVO.error(Code.ERROR, exception.getMessage()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /springsecurity-examples/src/main/java/org/example/springsecurityexamples/security/JWTComponent.java: -------------------------------------------------------------------------------- 1 | package org.example.springsecurityexamples.security; 2 | 3 | import com.auth0.jwt.JWT; 4 | import com.auth0.jwt.algorithms.Algorithm; 5 | import com.auth0.jwt.interfaces.DecodedJWT; 6 | import jakarta.annotation.PostConstruct; 7 | import org.springframework.beans.factory.annotation.Value; 8 | import org.springframework.stereotype.Component; 9 | 10 | import java.time.LocalDateTime; 11 | import java.time.ZoneId; 12 | import java.util.Date; 13 | import java.util.Map; 14 | 15 | @Component 16 | public class JWTComponent { 17 | // 私钥 18 | @Value("${my.secretkey}") 19 | private String secretkey; 20 | private Algorithm algorithm; 21 | // 组件初始化后,初始化加密算法对象。无需反复创建 22 | @PostConstruct 23 | private void init() { 24 | algorithm = Algorithm.HMAC256(secretkey); 25 | } 26 | 27 | public String encode(Map map) { 28 | // 1ds过期 29 | LocalDateTime time = LocalDateTime.now().plusDays(1); 30 | return JWT.create() 31 | .withPayload(map) 32 | .withIssuedAt(new Date()) 33 | .withExpiresAt(Date.from(time.atZone(ZoneId.systemDefault()).toInstant())) 34 | .sign(algorithm); 35 | } 36 | 37 | public DecodedJWT decode(String token) { 38 | return JWT.require(algorithm).build().verify(token); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /springsecurity-examples/src/main/java/org/example/springsecurityexamples/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package org.example.springsecurityexamples.controller; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.security.access.prepost.PreAuthorize; 6 | import org.springframework.security.core.GrantedAuthority; 7 | import org.springframework.security.core.annotation.AuthenticationPrincipal; 8 | import org.springframework.security.core.context.SecurityContextHolder; 9 | import org.springframework.web.bind.annotation.GetMapping; 10 | import org.springframework.web.bind.annotation.RequestMapping; 11 | import org.springframework.web.bind.annotation.RestController; 12 | 13 | @Slf4j 14 | @RestController 15 | @RequestMapping("/api/user/") 16 | @RequiredArgsConstructor 17 | public class UserController { 18 | 19 | @PreAuthorize("hasAuthority(T(org.example.springsecurityexamples.entity.User).ADMIN)") 20 | @GetMapping("test") 21 | public String test(@AuthenticationPrincipal String id) { 22 | log.debug(id); 23 | Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); 24 | log.debug(principal.toString()); 25 | for (GrantedAuthority authority : SecurityContextHolder.getContext().getAuthentication().getAuthorities()) { 26 | log.debug(authority.getAuthority()); 27 | } 28 | 29 | return "admin"; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /cache-examples/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.example 7 | springboot-course 8 | ${revision} 9 | 10 | cache-examples 11 | 0.0.1-SNAPSHOT 12 | cache-examples 13 | cache-examples 14 | 15 | 16 | 17 | com.github.ben-manes.caffeine 18 | caffeine 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-cache 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-starter-web 27 | 28 | 29 | 30 | org.projectlombok 31 | lombok 32 | true 33 | 34 | 35 | org.springframework.boot 36 | spring-boot-starter-test 37 | test 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /redis-examples/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.example 7 | springboot-course 8 | ${revision} 9 | 10 | redis-examples 11 | 0.0.1-SNAPSHOT 12 | redis-examples 13 | redis-examples 14 | 15 | 16 | 17 | 18 | org.redisson 19 | redisson-spring-boot-starter 20 | 3.52.0 21 | 22 | 23 | org.projectlombok 24 | lombok 25 | true 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-starter-test 30 | test 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /springsecurity-examples/src/main/java/org/example/springsecurityexamples/filter/GlobalExceptionHandlingFilter.java: -------------------------------------------------------------------------------- 1 | package org.example.springsecurityexamples.filter; 2 | 3 | import jakarta.servlet.FilterChain; 4 | import jakarta.servlet.ServletException; 5 | import jakarta.servlet.http.HttpServletRequest; 6 | import jakarta.servlet.http.HttpServletResponse; 7 | import lombok.RequiredArgsConstructor; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.example.springsecurityexamples.exception.XException; 10 | import org.example.springsecurityexamples.security.ResponseComponent; 11 | import org.springframework.stereotype.Component; 12 | import org.springframework.web.filter.OncePerRequestFilter; 13 | 14 | import java.io.IOException; 15 | 16 | @Slf4j 17 | @Component 18 | @RequiredArgsConstructor 19 | public class GlobalExceptionHandlingFilter extends OncePerRequestFilter { 20 | private final ResponseComponent responseComponent; 21 | @Override 22 | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { 23 | try { 24 | filterChain.doFilter(request, response); 25 | } catch (XException e) { 26 | if(e.getCode() != null) { 27 | responseComponent.response(response, e.getCode()); 28 | } 29 | responseComponent.response(response, e.getCodeN(), e.getMessage()); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /webflux-r2dbc-examples/src/main/java/org/example/webfluxr2dbcexamples/service/InitService.java: -------------------------------------------------------------------------------- 1 | package org.example.webfluxr2dbcexamples.service; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.example.webfluxr2dbcexamples.dox.UserReact; 6 | import org.example.webfluxr2dbcexamples.repository.UserRepository; 7 | import org.springframework.boot.context.event.ApplicationReadyEvent; 8 | import org.springframework.context.event.EventListener; 9 | import org.springframework.stereotype.Service; 10 | import org.springframework.transaction.annotation.Transactional; 11 | import reactor.core.publisher.Mono; 12 | 13 | @Service 14 | @Slf4j 15 | @RequiredArgsConstructor 16 | public class InitService { 17 | private final UserRepository userRepository; 18 | 19 | @Transactional 20 | @EventListener(classes = ApplicationReadyEvent.class) 21 | public Mono onApplicationReadyEvent() { 22 | String account = "admin"; 23 | return userRepository.findByAccount(account) 24 | .switchIfEmpty(Mono.defer(() -> { 25 | UserReact user = UserReact.builder() 26 | .name(account) 27 | .account(account) 28 | .role(UserReact.ROLE_ADMIN) 29 | .build(); 30 | return userRepository.save(user); 31 | })) 32 | .then(); 33 | 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /webflux-r2dbc-examples/src/main/java/org/example/webfluxr2dbcexamples/filter/ResponseHelper.java: -------------------------------------------------------------------------------- 1 | package org.example.webfluxr2dbcexamples.filter; 2 | 3 | import org.example.webfluxr2dbcexamples.exception.Code; 4 | import org.example.webfluxr2dbcexamples.vo.ResultVO; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import lombok.RequiredArgsConstructor; 7 | import lombok.SneakyThrows; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.springframework.core.io.buffer.DataBuffer; 10 | import org.springframework.http.MediaType; 11 | import org.springframework.http.server.reactive.ServerHttpResponse; 12 | import org.springframework.stereotype.Component; 13 | import org.springframework.web.server.ServerWebExchange; 14 | import reactor.core.publisher.Flux; 15 | import reactor.core.publisher.Mono; 16 | 17 | import java.nio.charset.StandardCharsets; 18 | 19 | @Component 20 | @Slf4j 21 | @RequiredArgsConstructor 22 | public class ResponseHelper { 23 | private final ObjectMapper objectMapper; 24 | 25 | @SneakyThrows 26 | public Mono response(Code code, ServerWebExchange exchange) { 27 | byte[] bytes = objectMapper.writeValueAsString(ResultVO.error(code)) 28 | .getBytes(StandardCharsets.UTF_8); 29 | ServerHttpResponse response = exchange.getResponse(); 30 | DataBuffer wrap = response.bufferFactory().wrap(bytes); 31 | response.getHeaders().setContentType(MediaType.APPLICATION_JSON); 32 | return response.writeWith(Flux.just(wrap)); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /springsecurity-examples/src/main/java/org/example/springsecurityexamples/security/ResponseComponent.java: -------------------------------------------------------------------------------- 1 | package org.example.springsecurityexamples.security; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import jakarta.servlet.http.HttpServletResponse; 5 | import lombok.RequiredArgsConstructor; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.example.springsecurityexamples.exception.Code; 8 | import org.example.springsecurityexamples.vo.ResultVO; 9 | import org.springframework.http.MediaType; 10 | import org.springframework.stereotype.Component; 11 | 12 | import java.io.IOException; 13 | import java.io.PrintWriter; 14 | import java.nio.charset.StandardCharsets; 15 | 16 | @Slf4j 17 | @Component 18 | @RequiredArgsConstructor 19 | public class ResponseComponent { 20 | 21 | private final ObjectMapper objectMapper; 22 | public void response(HttpServletResponse response, Code code) { 23 | response(response, code.getCode(), code.getMessage()); 24 | } 25 | 26 | public void response(HttpServletResponse response, int code, String message) { 27 | response.setCharacterEncoding(StandardCharsets.UTF_8.name()); 28 | response.setContentType(MediaType.APPLICATION_JSON_VALUE); 29 | try(PrintWriter writer = response.getWriter()) { 30 | var result = objectMapper.writeValueAsString(ResultVO.error(code, message)); 31 | writer.write(result); 32 | } catch (IOException e) { 33 | log.error(e.getMessage(), e); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /consul-examples/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.example 7 | springboot-course 8 | ${revision} 9 | 10 | consul-examples 11 | 0.0.1-SNAPSHOT 12 | consul-examples 13 | consul-examples 14 | 15 | 16 | 17 | org.springframework.boot 18 | spring-boot-starter-web 19 | 20 | 21 | org.springframework.cloud 22 | spring-cloud-starter-consul-discovery 23 | 24 | 25 | 26 | org.projectlombok 27 | lombok 28 | true 29 | 30 | 31 | org.springframework.boot 32 | spring-boot-starter-test 33 | test 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /jdbc-examples/src/main/java/org/example/jdbcexamples/mapper/AddressUser2RowMapper.java: -------------------------------------------------------------------------------- 1 | package org.example.jdbcexamples.mapper; 2 | 3 | import org.example.jdbcexamples.dox.Address; 4 | import org.example.jdbcexamples.dox.User; 5 | import org.example.jdbcexamples.dto.AddressUser2; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.jdbc.core.RowMapper; 8 | 9 | import java.sql.ResultSet; 10 | import java.sql.SQLException; 11 | import java.time.LocalDateTime; 12 | 13 | @Slf4j 14 | public class AddressUser2RowMapper implements RowMapper { 15 | 16 | @Override 17 | public AddressUser2 mapRow(ResultSet rs, int rowNum) throws SQLException { 18 | User user = User.builder() 19 | .id(rs.getString("u.id")) 20 | .name(rs.getString("name")) 21 | .createTime(rs.getObject("u.create_time", LocalDateTime.class)) 22 | .updateTime(rs.getObject("u.update_time", LocalDateTime.class)) 23 | .build(); 24 | Address address = Address.builder() 25 | .id(rs.getString("a.id")) 26 | .detail(rs.getString("detail")) 27 | .userId(rs.getString("user_id")) 28 | .createTime(rs.getObject("a.create_time", LocalDateTime.class)) 29 | .updateTime(rs.getObject("a.update_time", LocalDateTime.class)) 30 | .build(); 31 | return AddressUser2.builder() 32 | .user(user) 33 | .address(address) 34 | .build(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /springmvc-examples/src/main/java/org/example/springmvcexamples/example06/interceptor/interceptor/LoginInterceptor06.java: -------------------------------------------------------------------------------- 1 | package org.example.springmvcexamples.example06.interceptor.interceptor; 2 | 3 | import com.auth0.jwt.interfaces.DecodedJWT; 4 | import org.example.springmvcexamples.exception.Code; 5 | import org.example.springmvcexamples.exception.XException; 6 | import org.example.springmvcexamples.component.JWTComponent; 7 | import jakarta.servlet.http.HttpServletRequest; 8 | import jakarta.servlet.http.HttpServletResponse; 9 | import lombok.RequiredArgsConstructor; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.springframework.stereotype.Component; 12 | import org.springframework.web.servlet.HandlerInterceptor; 13 | 14 | @Component 15 | @Slf4j 16 | @RequiredArgsConstructor 17 | public class LoginInterceptor06 implements HandlerInterceptor { 18 | private final JWTComponent jwtComponent; 19 | @Override 20 | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { 21 | String token = request.getHeader("token"); 22 | if (token == null) { 23 | throw XException.builder().code(Code.UNAUTHORIZED).build(); 24 | } 25 | DecodedJWT decodedJWT = jwtComponent.decode(token); 26 | String uid = decodedJWT.getClaim("uid").asString(); 27 | String role = decodedJWT.getClaim("role").asString(); 28 | 29 | request.setAttribute("uid", uid); 30 | request.setAttribute("role", role); 31 | return true; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /cache-examples/src/main/java/org/example/cacheexamples/service/UserService.java: -------------------------------------------------------------------------------- 1 | package org.example.cacheexamples.service; 2 | 3 | import org.example.cacheexamples.dox.User; 4 | import org.example.cacheexamples.repository.UserRepository; 5 | import lombok.RequiredArgsConstructor; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.cache.annotation.CacheEvict; 8 | import org.springframework.cache.annotation.CachePut; 9 | import org.springframework.cache.annotation.Cacheable; 10 | import org.springframework.stereotype.Service; 11 | 12 | import java.util.List; 13 | 14 | @Service 15 | @Slf4j 16 | @RequiredArgsConstructor 17 | public class UserService { 18 | private final UserRepository userRepository; 19 | 20 | @Cacheable(value = "user", key = "#uid") 21 | public User getUser(String uid) { 22 | User user = userRepository.getUser(uid); 23 | log.debug("called UserService getUser() user: {}", user); 24 | return user; 25 | } 26 | @Cacheable(value = "users") 27 | public List listUsers() { 28 | return userRepository.listUsers(); 29 | } 30 | 31 | @CachePut(value = "user", key = "#user.id") 32 | // 以键值对缓存一个集合对象时,缓存对象是一个整体。无法修改其中某一个元素 33 | // 因此清空整个集合缓存 34 | @CacheEvict(value = "users", allEntries = true) 35 | public User updateUser(User user) { 36 | User u = userRepository.updateUser(user); 37 | log.debug("updateUser(), user: {}", u); 38 | return user; 39 | } 40 | 41 | @CacheEvict(value = "user", key = "#uid") 42 | public void delUser(String uid) { 43 | // 从缓存删除,没有调用模拟的持久层删除 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /webflux-r2dbc-examples/src/main/java/org/example/webfluxr2dbcexamples/controller/exception/ExceptionController.java: -------------------------------------------------------------------------------- 1 | package org.example.webfluxr2dbcexamples.controller.exception; 2 | 3 | import org.example.webfluxr2dbcexamples.exception.Code; 4 | import org.example.webfluxr2dbcexamples.exception.XException; 5 | import org.example.webfluxr2dbcexamples.vo.ResultVO; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.r2dbc.UncategorizedR2dbcException; 8 | import org.springframework.web.bind.annotation.ExceptionHandler; 9 | import org.springframework.web.bind.annotation.RestControllerAdvice; 10 | import reactor.core.publisher.Mono; 11 | 12 | @Slf4j 13 | @RestControllerAdvice 14 | public class ExceptionController { 15 | // Mono中异常转此处理 16 | // filter内无效,单独处理。 17 | @ExceptionHandler(XException.class) 18 | public Mono handleXException(XException exception) { 19 | if(exception.getCode() != null) { 20 | return Mono.just(ResultVO.error(exception.getCode())); 21 | } 22 | return Mono.just(ResultVO.error(exception.getCodeN(), exception.getMessage())); 23 | } 24 | 25 | @ExceptionHandler(Exception.class) 26 | public Mono handleException(Exception exception) { 27 | return Mono.just(ResultVO.error(Code.BAD_REQUEST.getCode(), exception.getMessage())); 28 | } 29 | 30 | @ExceptionHandler(UncategorizedR2dbcException.class) 31 | public Mono handelUncategorizedR2dbcException(UncategorizedR2dbcException exception) { 32 | return Mono.just(ResultVO.error(Code.BAD_REQUEST.getCode(), "唯一约束冲突!" + exception.getMessage())); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /springmvc-examples/src/main/java/org/example/springmvcexamples/example04/passwordencoder/controller/ExampleController04.java: -------------------------------------------------------------------------------- 1 | package org.example.springmvcexamples.example04.passwordencoder.controller; 2 | 3 | 4 | import org.example.springmvcexamples.example04.passwordencoder.dox.User04; 5 | import org.example.springmvcexamples.example04.passwordencoder.service.UserService04; 6 | import org.example.springmvcexamples.exception.Code; 7 | import org.example.springmvcexamples.vo.ResultVO; 8 | import lombok.RequiredArgsConstructor; 9 | import lombok.extern.slf4j.Slf4j; 10 | import org.springframework.security.crypto.password.PasswordEncoder; 11 | import org.springframework.web.bind.annotation.PostMapping; 12 | import org.springframework.web.bind.annotation.RequestBody; 13 | import org.springframework.web.bind.annotation.RequestMapping; 14 | import org.springframework.web.bind.annotation.RestController; 15 | 16 | @Slf4j 17 | @RestController 18 | @RequestMapping("/api/example04/") 19 | @RequiredArgsConstructor 20 | public class ExampleController04 { 21 | 22 | private final UserService04 userService; 23 | private final PasswordEncoder passwordEncoder; 24 | 25 | @PostMapping("login") 26 | public ResultVO login(@RequestBody User04 user) { 27 | // 先查询用户是否存在 28 | User04 u = userService.getUser(user.getUserName()); 29 | if (u == null || !passwordEncoder.matches(user.getPassword(), u.getPassword())) { 30 | log.debug("登录失败"); 31 | return ResultVO.error(Code.LOGIN_ERROR); 32 | } 33 | // 登录成功,添加token等操作 34 | log.debug("登录成功"); 35 | return ResultVO.success(u); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /jdbc-examples/src/main/java/org/example/jdbcexamples/repository/AddressRepository.java: -------------------------------------------------------------------------------- 1 | package org.example.jdbcexamples.repository; 2 | 3 | import org.example.jdbcexamples.dox.Address; 4 | import org.example.jdbcexamples.dto.AddressUser; 5 | import org.example.jdbcexamples.dto.AddressUser2; 6 | import org.example.jdbcexamples.mapper.AddressUser2RowMapper; 7 | import org.springframework.data.jdbc.repository.query.Modifying; 8 | import org.springframework.data.jdbc.repository.query.Query; 9 | import org.springframework.data.repository.CrudRepository; 10 | import org.springframework.stereotype.Repository; 11 | 12 | import java.util.List; 13 | 14 | @Repository 15 | public interface AddressRepository extends CrudRepository { 16 | 17 | @Query("select * from address a where a.user_id=:uid") 18 | List
find(String uid); 19 | 20 | // 可基于名称自动生成SQL语句 21 | List
findByUserId(String uid); 22 | 23 | @Query(""" 24 | select a.id as id, a.detail as detail, u.name as name, a.create_time as create_time, 25 | a.update_time as update_time, a.user_id as user_id 26 | from address a join user u on u.id = a.user_id 27 | where a.id=:aid 28 | """) 29 | AddressUser findAddressUserById(String aid); 30 | 31 | @Query(value = "select * from address a join user u on u.id = a.user_id where a.id=:aid", 32 | rowMapperClass = AddressUser2RowMapper.class) 33 | AddressUser2 findAddressUser2ById(String aid); 34 | 35 | @Modifying 36 | @Query("update address a set a.detail=:detail where a.id=:aid") 37 | void updateDetail(String detail, String aid); 38 | } 39 | -------------------------------------------------------------------------------- /spring-examples/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.example 7 | springboot-course 8 | ${revision} 9 | 10 | spring-examples 11 | 0.0.1-SNAPSHOT 12 | spring-examples 13 | spring-examples 14 | 15 | 16 | 17 | org.springframework.boot 18 | spring-boot-starter 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-aop 23 | 24 | 25 | org.projectlombok 26 | lombok 27 | true 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-starter-test 32 | test 33 | 34 | 35 | org.junit.platform 36 | junit-platform-launcher 37 | test 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /jdbc-examples/src/main/java/org/example/jdbcexamples/mapper/UserAddress3ResultSetExtractor.java: -------------------------------------------------------------------------------- 1 | package org.example.jdbcexamples.mapper; 2 | 3 | import org.example.jdbcexamples.dox.Address; 4 | import org.example.jdbcexamples.dto.UserAddress3; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.dao.DataAccessException; 7 | import org.springframework.jdbc.core.ResultSetExtractor; 8 | 9 | import java.sql.ResultSet; 10 | import java.sql.SQLException; 11 | import java.time.LocalDateTime; 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | @Slf4j 16 | public class UserAddress3ResultSetExtractor implements ResultSetExtractor { 17 | @Override 18 | public UserAddress3 extractData(ResultSet rs) throws SQLException, DataAccessException { 19 | List
addresses = new ArrayList<>(); 20 | UserAddress3.UserAddress3Builder builder = UserAddress3.builder(); 21 | while (rs.next()) { 22 | builder.id(rs.getString("user_id")); 23 | builder.name(rs.getString("name")); 24 | builder.createTime(rs.getObject("u.create_time", LocalDateTime.class)); 25 | builder.updateTime(rs.getObject("u.update_time", LocalDateTime.class)); 26 | Address a = Address.builder().id(rs.getString("id")) 27 | .detail(rs.getString("detail")) 28 | .userId(rs.getString("user_id")) 29 | .createTime(rs.getObject("a.create_time", LocalDateTime.class)) 30 | .updateTime(rs.getObject("a.update_time", LocalDateTime.class)) 31 | .build(); 32 | addresses.add(a); 33 | } 34 | return builder.addresses(addresses).build(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /redis-examples/src/test/java/org/example/redisexamples/service/OrderServiceTest.java: -------------------------------------------------------------------------------- 1 | package org.example.redisexamples.service; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.example.redisexamples.dox.Item; 5 | import org.junit.jupiter.api.Test; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.boot.test.context.SpringBootTest; 8 | 9 | import java.util.List; 10 | import java.util.Random; 11 | import java.util.concurrent.CountDownLatch; 12 | import java.util.concurrent.TimeUnit; 13 | 14 | 15 | @SpringBootTest 16 | @Slf4j 17 | class OrderServiceTest { 18 | @Autowired 19 | private OrderService orderService; 20 | 21 | @Test 22 | void addItemsTest() { 23 | List items = List.of( 24 | Item.builder().id("01HN7JNHG93N3RSPF60MEG4WE2").total(50).build(), 25 | Item.builder().id("01HN7JNHG93N3RSPF60MEG4WE3").total(30).build() 26 | ); 27 | orderService.addItems(items); 28 | } 29 | 30 | @Test 31 | void rushBuyTest() throws InterruptedException { 32 | // 模拟抢购的商品,抢购用户 33 | Item item = Item.builder().id("01HN7JNHG93N3RSPF60MEG4WE2").build(); 34 | final int AMOUNT = 200; 35 | CountDownLatch latch = new CountDownLatch(AMOUNT); 36 | var random = new Random(); 37 | for (int i = 0; i < AMOUNT; i++) { 38 | Thread.startVirtualThread(() -> { 39 | long quantity = orderService.rushBuy(item, String.valueOf(random.nextInt(100))); 40 | log.debug("剩余数量:{}", quantity); 41 | latch.countDown(); 42 | }); 43 | } 44 | latch.await(); 45 | // 让消息监听器有时间,读取消息队列中的消息 46 | TimeUnit.SECONDS.sleep(10); 47 | 48 | } 49 | } -------------------------------------------------------------------------------- /jdbc-examples/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.example 7 | springboot-course 8 | ${revision} 9 | 10 | jdbc-examples 11 | 0.0.1-SNAPSHOT 12 | jdbc-examples 13 | jdbc-examples 14 | 15 | 16 | 17 | org.springframework.boot 18 | spring-boot-starter-data-jdbc 19 | 20 | 21 | com.mysql 22 | mysql-connector-j 23 | runtime 24 | 25 | 26 | org.projectlombok 27 | lombok 28 | true 29 | 30 | 31 | org.springframework.boot 32 | spring-boot-starter-test 33 | test 34 | 35 | 36 | 37 | org.junit.platform 38 | junit-platform-launcher 39 | test 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /jdbc-examples/src/main/java/org/example/jdbcexamples/mapper/UserAddressResultSetExtractor.java: -------------------------------------------------------------------------------- 1 | package org.example.jdbcexamples.mapper; 2 | 3 | import org.example.jdbcexamples.dox.Address; 4 | import org.example.jdbcexamples.dox.User; 5 | import org.example.jdbcexamples.dto.UserAddress; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.dao.DataAccessException; 8 | import org.springframework.jdbc.core.ResultSetExtractor; 9 | 10 | import java.sql.ResultSet; 11 | import java.sql.SQLException; 12 | import java.time.LocalDateTime; 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | @Slf4j 17 | public class UserAddressResultSetExtractor implements ResultSetExtractor { 18 | @Override 19 | public UserAddress extractData(ResultSet rs) throws SQLException, DataAccessException { 20 | User user = null; 21 | List
addresses = new ArrayList<>(); 22 | while (rs.next()) { 23 | if (user == null) { 24 | user = User.builder() 25 | .id(rs.getString("u.id")) 26 | .name(rs.getString("name")) 27 | .createTime(rs.getObject("u.create_time", LocalDateTime.class)) 28 | .updateTime(rs.getObject("u.update_time", LocalDateTime.class)) 29 | .build(); 30 | } 31 | Address a = Address.builder().id(rs.getString("a.id")) 32 | .detail(rs.getString("detail")) 33 | .userId(rs.getString("user_id")) 34 | .createTime(rs.getObject("a.create_time", LocalDateTime.class)) 35 | .updateTime(rs.getObject("a.update_time", LocalDateTime.class)) 36 | .build(); 37 | addresses.add(a); 38 | } 39 | return UserAddress.builder() 40 | .user(user) 41 | .addresses(addresses) 42 | .build(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /springmvc-examples/src/main/java/org/example/springmvcexamples/example08/openapi/ExampleController08.java: -------------------------------------------------------------------------------- 1 | package org.example.springmvcexamples.example08.openapi; 2 | 3 | import org.example.springmvcexamples.component.JWTComponent; 4 | import org.example.springmvcexamples.exception.Code; 5 | import org.example.springmvcexamples.vo.ResultVO; 6 | import io.swagger.v3.oas.annotations.Operation; 7 | import io.swagger.v3.oas.annotations.tags.Tag; 8 | import jakarta.servlet.http.HttpServletRequest; 9 | import jakarta.servlet.http.HttpServletResponse; 10 | import lombok.RequiredArgsConstructor; 11 | import lombok.extern.slf4j.Slf4j; 12 | import org.springframework.web.bind.annotation.*; 13 | 14 | import java.util.Map; 15 | 16 | @RestController 17 | @Slf4j 18 | @RequiredArgsConstructor 19 | @RequestMapping("/api/example08/") 20 | @Tag(name = "测试openapi接口文档") 21 | public class ExampleController08 { 22 | private final JWTComponent jwtComponent; 23 | 24 | @Operation(summary = "登录", 25 | description = "账号密码:123。登录成功返回user对象,header返回token") 26 | @PostMapping("login") 27 | public ResultVO postLogin(@RequestBody User08 user, HttpServletResponse response) { 28 | if ("123".equals(user.getAccount()) && "123".equals(user.getPassword())) { 29 | var user08 = User08.builder().id("5454").name("BO").account("182").build(); 30 | var token = jwtComponent.encode(Map.of("id", user08.getId())); 31 | response.addHeader("token", token); 32 | return ResultVO.success(Map.of("user", user08)); 33 | } 34 | return ResultVO.error(Code.LOGIN_ERROR); 35 | } 36 | 37 | @Operation(summary = "测试请求header中是否包含token") 38 | @GetMapping("token") 39 | public ResultVO getToken(HttpServletRequest request) { 40 | var token = request.getHeader("token"); 41 | if (token != null) { 42 | return ResultVO.ok(); 43 | } 44 | return ResultVO.error(Code.BAD_REQUEST); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /springsecurity-examples/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.example 7 | springboot-course 8 | ${revision} 9 | 10 | springsecurity-examples 11 | 0.0.1-SNAPSHOT 12 | springsecurity-examples 13 | springsecurity-examples 14 | 15 | 16 | 17 | com.auth0 18 | java-jwt 19 | 4.5.0 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-security 24 | 25 | 26 | org.springframework.boot 27 | spring-boot-starter-web 28 | 29 | 30 | 31 | org.projectlombok 32 | lombok 33 | true 34 | 35 | 36 | org.springframework.boot 37 | spring-boot-starter-test 38 | test 39 | 40 | 41 | org.springframework.security 42 | spring-security-test 43 | test 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /jdbc-examples/src/test/java/org/example/jdbcexamples/repository/GithubUserRepositoryTest.java: -------------------------------------------------------------------------------- 1 | package org.example.jdbcexamples.repository; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.example.jdbcexamples.dox.GithubUser; 5 | import org.example.jdbcexamples.dto.GithubOptionType; 6 | import org.junit.jupiter.api.Test; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.test.context.SpringBootTest; 9 | import org.springframework.jdbc.core.JdbcTemplate; 10 | 11 | import java.util.List; 12 | 13 | @SpringBootTest 14 | @Slf4j 15 | class GithubUserRepositoryTest { 16 | @Autowired 17 | private GithubUserRepository githubUserRepository; 18 | @Autowired 19 | private JdbcTemplate jdbcTemplate; 20 | 21 | @Test 22 | public void addGithubUserTest() { 23 | GithubUser user = GithubUser.builder() 24 | .name("SUN") 25 | .followers(15) 26 | .stars(15) 27 | .repos(15) 28 | .gender("female") 29 | .build(); 30 | githubUserRepository.save(user); 31 | } 32 | 33 | @Test 34 | void findByDynamic() { 35 | GithubUser user = GithubUser.builder() 36 | .followers(10) 37 | .stars(8) 38 | .gender("female") 39 | .build(); 40 | githubUserRepository.findByDynamic(jdbcTemplate, user) 41 | .forEach(u -> log.debug("{}", u)); 42 | } 43 | 44 | @Test 45 | void findByDynamicType() { 46 | GithubOptionType followers = new GithubOptionType("followers",GithubOptionType.GREAT_EQUAL, 10); 47 | GithubOptionType stars = new GithubOptionType("stars", GithubOptionType.GREAT_EQUAL, 8); 48 | GithubOptionType gender = new GithubOptionType("gender", GithubOptionType.EQUAL, "female"); 49 | githubUserRepository.findByDynamic(jdbcTemplate, List.of(followers, stars, gender)) 50 | .forEach(u -> log.debug("{}", u)); 51 | } 52 | } -------------------------------------------------------------------------------- /springmvc-examples/src/main/java/org/example/springmvcexamples/example06/interceptor/controller/ExampleController06.java: -------------------------------------------------------------------------------- 1 | package org.example.springmvcexamples.example06.interceptor.controller; 2 | 3 | import org.example.springmvcexamples.component.JWTComponent; 4 | import org.example.springmvcexamples.example06.interceptor.dox.User06; 5 | import org.example.springmvcexamples.example06.interceptor.service.UserService06; 6 | import org.example.springmvcexamples.exception.Code; 7 | import org.example.springmvcexamples.vo.ResultVO; 8 | import jakarta.servlet.http.HttpServletResponse; 9 | import lombok.RequiredArgsConstructor; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.springframework.security.crypto.password.PasswordEncoder; 12 | import org.springframework.web.bind.annotation.*; 13 | 14 | import java.util.Map; 15 | 16 | @Slf4j 17 | @RestController 18 | @RequestMapping("/api/example06/open/") 19 | @RequiredArgsConstructor 20 | public class ExampleController06 { 21 | private final UserService06 userService; 22 | private final PasswordEncoder encoder; 23 | private final JWTComponent jwtComponent; 24 | 25 | @PostMapping("login") 26 | public ResultVO postLogin(@RequestBody User06 loginUser, HttpServletResponse response) { 27 | User06 u = userService.getUser(loginUser.getUserName()); 28 | if (u == null || !encoder.matches(loginUser.getPassword(), u.getPassword())) { 29 | return ResultVO.error(Code.LOGIN_ERROR); 30 | } 31 | // 登录成功,模拟获取用户id角色等信息,加密 32 | String result = jwtComponent.encode(Map.of("uid", u.getId(), "role", u.getRole())); 33 | log.debug(result); 34 | // 以指定键值对,置于响应header 35 | response.addHeader("token", result); 36 | response.addHeader("role", u.getRole()); 37 | return ResultVO.success(u); 38 | } 39 | 40 | @GetMapping("admin/welcome") 41 | public ResultVO getWelcome(@RequestAttribute("role") String role) { 42 | log.debug(role); 43 | return ResultVO.success(Map.of("msg", "欢迎回来")); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /webflux-r2dbc-examples/src/main/java/org/example/webfluxr2dbcexamples/component/JWTComponent.java: -------------------------------------------------------------------------------- 1 | package org.example.webfluxr2dbcexamples.component; 2 | 3 | import com.auth0.jwt.JWT; 4 | import com.auth0.jwt.algorithms.Algorithm; 5 | import com.auth0.jwt.exceptions.JWTDecodeException; 6 | import com.auth0.jwt.exceptions.SignatureVerificationException; 7 | import com.auth0.jwt.exceptions.TokenExpiredException; 8 | import com.auth0.jwt.interfaces.DecodedJWT; 9 | import org.example.webfluxr2dbcexamples.exception.Code; 10 | import org.example.webfluxr2dbcexamples.exception.XException; 11 | import lombok.extern.slf4j.Slf4j; 12 | import org.springframework.beans.factory.annotation.Value; 13 | import org.springframework.stereotype.Component; 14 | import reactor.core.publisher.Mono; 15 | 16 | import java.time.LocalDateTime; 17 | import java.time.ZoneId; 18 | import java.util.Date; 19 | import java.util.Map; 20 | 21 | @Component 22 | @Slf4j 23 | public class JWTComponent { 24 | // 私钥 25 | @Value("${my.secretkey}") 26 | private String secretkey; 27 | public String encode(Map map) { 28 | LocalDateTime time = LocalDateTime.now().plusMonths(1); 29 | return JWT.create() 30 | .withPayload(map) 31 | .withIssuedAt(new Date()) 32 | .withExpiresAt(Date.from(time.atZone(ZoneId.systemDefault()).toInstant())) 33 | .sign(Algorithm.HMAC256(secretkey)); 34 | } 35 | 36 | public Mono decode(String token) { 37 | try { 38 | DecodedJWT decodedJWT = JWT.require(Algorithm.HMAC256(secretkey)).build().verify(token); 39 | return Mono.just(decodedJWT); 40 | } catch (TokenExpiredException | SignatureVerificationException | JWTDecodeException e) { 41 | Code code = Code.FORBIDDEN; 42 | if (e instanceof TokenExpiredException) { 43 | code = Code.TOKEN_EXPIRED; 44 | } 45 | return Mono.error(XException.builder().code(code).build()); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /springsecurity-examples/src/main/java/org/example/springsecurityexamples/controller/ManagerController.java: -------------------------------------------------------------------------------- 1 | package org.example.springsecurityexamples.controller; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.example.springsecurityexamples.security.UserDetails; 6 | import org.example.springsecurityexamples.vo.ResultVO; 7 | import org.springframework.security.access.prepost.PreAuthorize; 8 | import org.springframework.security.core.Authentication; 9 | import org.springframework.security.core.GrantedAuthority; 10 | import org.springframework.security.core.annotation.AuthenticationPrincipal; 11 | import org.springframework.security.core.context.SecurityContextHolder; 12 | import org.springframework.web.bind.annotation.GetMapping; 13 | import org.springframework.web.bind.annotation.RequestMapping; 14 | import org.springframework.web.bind.annotation.RestController; 15 | 16 | @Slf4j 17 | @RestController 18 | @RequestMapping("/api/manager/") 19 | @RequiredArgsConstructor 20 | public class ManagerController { 21 | 22 | @PreAuthorize("hasAnyAuthority(@auth.MANAGER_READ, @auth.MANAGER_CREATE)") 23 | @GetMapping("read") 24 | public ResultVO read(@AuthenticationPrincipal UserDetails userDetails) { 25 | log.debug("@AuthenticationPrincipal uid: {}", userDetails); 26 | return ResultVO.success("read"); 27 | } 28 | 29 | @PreAuthorize("hasAnyAuthority(@auth.MANAGER_DELETE)") 30 | @GetMapping("delete") 31 | public ResultVO delete() { 32 | // 可获取当前线程SecurityContext对象以及Authentication认证对象 33 | Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); 34 | UserDetails userDetails = (UserDetails) authentication.getPrincipal(); 35 | log.debug("@AuthenticationPrincipal uid: {}", userDetails); 36 | // Authentication对象中封装的权限 37 | for (GrantedAuthority authority : authentication.getAuthorities()) { 38 | log.debug(authority.getAuthority()); 39 | } 40 | return ResultVO.success("delete"); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /webflux-r2dbc-examples/src/main/java/org/example/webfluxr2dbcexamples/service/UserService.java: -------------------------------------------------------------------------------- 1 | package org.example.webfluxr2dbcexamples.service; 2 | 3 | import org.example.webfluxr2dbcexamples.dox.UserReact; 4 | import org.example.webfluxr2dbcexamples.exception.Code; 5 | import org.example.webfluxr2dbcexamples.exception.XException; 6 | import org.example.webfluxr2dbcexamples.repository.UserRepository; 7 | import lombok.RequiredArgsConstructor; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.springframework.security.crypto.password.PasswordEncoder; 10 | import org.springframework.stereotype.Service; 11 | import org.springframework.transaction.annotation.Transactional; 12 | import reactor.core.publisher.Mono; 13 | 14 | import java.util.List; 15 | 16 | @Service 17 | @Slf4j 18 | @RequiredArgsConstructor 19 | public class UserService { 20 | private final UserRepository userRepository; 21 | private final PasswordEncoder passwordEncoder; 22 | 23 | public Mono getUser(String account) { 24 | return userRepository.findByAccount(account); 25 | } 26 | 27 | public Mono getUserById(String uid) { 28 | return userRepository.findById(uid); 29 | } 30 | 31 | @Transactional 32 | public Mono addUser(UserReact user) { 33 | return userRepository.findByAccount(user.getAccount()) 34 | // 如果用户已存在,直接抛出异常 35 | .flatMap(existingUser -> Mono.error(XException.builder() 36 | .codeN(Code.ERROR) 37 | .message("用户已存在") 38 | .build())) 39 | // 如果用户不存在 40 | .switchIfEmpty(Mono.defer(() -> { 41 | user.setRole(UserReact.ROLE_USER); 42 | user.setPassword(passwordEncoder.encode(user.getAccount())); 43 | return userRepository.save(user); 44 | })) 45 | .then(); 46 | } 47 | 48 | public Mono> listUsers(String role) { 49 | return userRepository.findByRole(role).collectList(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /springmvc-examples/src/main/java/org/example/springmvcexamples/component/JWTComponent.java: -------------------------------------------------------------------------------- 1 | package org.example.springmvcexamples.component; 2 | 3 | import com.auth0.jwt.JWT; 4 | import com.auth0.jwt.algorithms.Algorithm; 5 | import com.auth0.jwt.exceptions.JWTDecodeException; 6 | import com.auth0.jwt.exceptions.SignatureVerificationException; 7 | import com.auth0.jwt.exceptions.TokenExpiredException; 8 | import com.auth0.jwt.interfaces.DecodedJWT; 9 | import jakarta.annotation.PostConstruct; 10 | import org.example.springmvcexamples.exception.Code; 11 | import org.example.springmvcexamples.exception.XException; 12 | import org.springframework.beans.factory.annotation.Value; 13 | import org.springframework.stereotype.Component; 14 | 15 | import java.time.LocalDateTime; 16 | import java.time.ZoneId; 17 | import java.util.Date; 18 | import java.util.Map; 19 | 20 | @Component 21 | public class JWTComponent { 22 | // 私钥 23 | @Value("${my.secretkey}") 24 | private String secretkey; 25 | private Algorithm algorithm; 26 | // 组件初始化后,初始化加密算法对象。无需反复创建 27 | @PostConstruct 28 | private void init() { 29 | algorithm = Algorithm.HMAC256(secretkey); 30 | } 31 | public String encode(Map map) { 32 | // 1ds过期 33 | LocalDateTime time = LocalDateTime.now().plusDays(1); 34 | return JWT.create() 35 | .withPayload(map) 36 | .withIssuedAt(new Date()) 37 | .withExpiresAt(Date.from(time.atZone(ZoneId.systemDefault()).toInstant())) 38 | .sign(algorithm); 39 | } 40 | 41 | public DecodedJWT decode(String token) { 42 | try { 43 | return JWT.require(algorithm).build().verify(token); 44 | } catch (TokenExpiredException | SignatureVerificationException | JWTDecodeException e) { 45 | if (e instanceof SignatureVerificationException || e instanceof JWTDecodeException) { 46 | throw XException.builder().code(Code.FORBIDDEN).build(); 47 | } 48 | throw XException.builder().code(Code.TOKEN_EXPIRED).build(); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /jdbc-examples/src/main/java/org/example/jdbcexamples/repository/UserRepository.java: -------------------------------------------------------------------------------- 1 | package org.example.jdbcexamples.repository; 2 | 3 | import org.example.jdbcexamples.dox.User; 4 | import org.example.jdbcexamples.dto.AddressUser; 5 | import org.example.jdbcexamples.dto.UserAddress; 6 | import org.example.jdbcexamples.dto.UserAddress3; 7 | import org.example.jdbcexamples.mapper.UserAddress3ResultSetExtractor; 8 | import org.example.jdbcexamples.mapper.UserAddressResultSetExtractor; 9 | import org.springframework.data.domain.Pageable; 10 | import org.springframework.data.jdbc.repository.query.Query; 11 | import org.springframework.data.repository.CrudRepository; 12 | import org.springframework.stereotype.Repository; 13 | 14 | import java.util.List; 15 | 16 | @Repository 17 | public interface UserRepository extends CrudRepository { 18 | // 建议显式声明映射名称对应DTO中属性名称,避免冲突 19 | @Query(""" 20 | select a.id as id, a.create_time as create_time, a.update_time as update_time, name, detail, a.user_id as user_id 21 | from user u join address a 22 | on u.id = a.user_id 23 | where a.id=:aid 24 | """) 25 | AddressUser findByAddressId(String aid); 26 | 27 | @Query("select * from user u limit :offet, :pageSize") 28 | List findAll(long offset, int pageSize); 29 | 30 | @Query("select * from user u limit :#{#pageable.offset}, :#{#pageable.pageSize}") 31 | List findAll(Pageable pageable); 32 | 33 | @Query(""" 34 | select * from user u 35 | order by u.id desc 36 | limit :#{#pageable.offset}, :#{#pageable.pageSize} 37 | """) 38 | List findByIdDesc(Pageable pageable); 39 | 40 | @Query(value = "select * from address a join user u on u.id = a.user_id where u.id=:uid", 41 | resultSetExtractorClass = UserAddress3ResultSetExtractor.class) 42 | UserAddress3 findUserAddress3(String uid); 43 | 44 | @Query(value = "select * from user u join address a on u.id = a.user_id where u.id=:uid", 45 | resultSetExtractorClass = UserAddressResultSetExtractor.class) 46 | UserAddress findUserAddress(String uid); 47 | } 48 | -------------------------------------------------------------------------------- /springmvc-examples/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.example 7 | springboot-course 8 | ${revision} 9 | 10 | springmvc-examples 11 | 0.0.1-SNAPSHOT 12 | springmvc-examples 13 | springmvc-examples 14 | 15 | 16 | 17 | 18 | org.springdoc 19 | springdoc-openapi-starter-webmvc-ui 20 | 2.8.13 21 | 22 | 23 | 24 | com.auth0 25 | java-jwt 26 | 4.4.0 27 | 28 | 29 | org.springframework.security 30 | spring-security-crypto 31 | 32 | 33 | 34 | org.junit.platform 35 | junit-platform-launcher 36 | test 37 | 38 | 39 | org.springframework.boot 40 | spring-boot-starter-validation 41 | 42 | 43 | org.springframework.boot 44 | spring-boot-starter-web 45 | 46 | 47 | org.projectlombok 48 | lombok 49 | true 50 | 51 | 52 | org.springframework.boot 53 | spring-boot-starter-test 54 | test 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /springsecurity-examples/src/main/java/org/example/springsecurityexamples/controller/LoginController.java: -------------------------------------------------------------------------------- 1 | package org.example.springsecurityexamples.controller; 2 | 3 | import jakarta.servlet.http.HttpServletResponse; 4 | import lombok.RequiredArgsConstructor; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.example.springsecurityexamples.entity.User; 7 | import org.example.springsecurityexamples.exception.Code; 8 | import org.example.springsecurityexamples.security.JWTComponent; 9 | import org.example.springsecurityexamples.security.Tokens; 10 | import org.example.springsecurityexamples.service.UserService; 11 | import org.example.springsecurityexamples.vo.ResultVO; 12 | import org.springframework.security.crypto.password.PasswordEncoder; 13 | import org.springframework.web.bind.annotation.PostMapping; 14 | import org.springframework.web.bind.annotation.RequestBody; 15 | import org.springframework.web.bind.annotation.RequestMapping; 16 | import org.springframework.web.bind.annotation.RestController; 17 | 18 | import java.util.Map; 19 | 20 | @Slf4j 21 | @RestController 22 | @RequestMapping("/api/open/") 23 | @RequiredArgsConstructor 24 | public class LoginController { 25 | private final JWTComponent jwtComponent; 26 | private final UserService userService; 27 | private final PasswordEncoder encoder; 28 | 29 | @PostMapping("login") 30 | public ResultVO postLogin(@RequestBody User loginUser, HttpServletResponse response) { 31 | 32 | var user = userService.getUser(loginUser.getAccount()); 33 | if (user == null || !encoder.matches(loginUser.getPassword(), user.getPassword())) { 34 | return ResultVO.error(Code.LOGIN_ERROR); 35 | } 36 | // 将角色作为特殊authority写入token的authorities 37 | // 对于spring security,统一authority权限模型 38 | var authorities = user.getAuthorities(); 39 | // 加`ROLE_`前缀。hasRole()方法以`ROLE_authority`判断role 40 | authorities.add("ROLE_" + user.getRole()); 41 | // 42 | var payload = Map.of(Tokens.UID, user.getId(), 43 | Tokens.DEPID, user.getDepartmentId(), 44 | Tokens.AUTHORITIES, authorities); 45 | var result = jwtComponent.encode(payload); 46 | response.addHeader(Tokens.TOKEN, result); 47 | return ResultVO.ok(); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /webflux-r2dbc-examples/src/main/java/org/example/webfluxr2dbcexamples/controller/LoginController.java: -------------------------------------------------------------------------------- 1 | package org.example.webfluxr2dbcexamples.controller; 2 | 3 | import org.example.webfluxr2dbcexamples.component.JWTComponent; 4 | import org.example.webfluxr2dbcexamples.dox.UserReact; 5 | import org.example.webfluxr2dbcexamples.exception.Code; 6 | import org.example.webfluxr2dbcexamples.service.UserService; 7 | import org.example.webfluxr2dbcexamples.vo.RequestConstant; 8 | import org.example.webfluxr2dbcexamples.vo.ResultVO; 9 | import lombok.RequiredArgsConstructor; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.springframework.http.HttpHeaders; 12 | import org.springframework.http.server.reactive.ServerHttpResponse; 13 | import org.springframework.security.crypto.password.PasswordEncoder; 14 | import org.springframework.web.bind.annotation.PostMapping; 15 | import org.springframework.web.bind.annotation.RequestBody; 16 | import org.springframework.web.bind.annotation.RequestMapping; 17 | import org.springframework.web.bind.annotation.RestController; 18 | import reactor.core.publisher.Mono; 19 | 20 | import java.util.Map; 21 | 22 | @RestController 23 | @Slf4j 24 | @RequiredArgsConstructor 25 | @RequestMapping("/api/") 26 | public class LoginController { 27 | private final UserService userService; 28 | private final PasswordEncoder passwordEncoder; 29 | private final JWTComponent jwtComponent; 30 | 31 | @PostMapping("login") 32 | public Mono login(@RequestBody UserReact user, ServerHttpResponse response) { 33 | return userService.getUser(user.getAccount()) 34 | .filter(u -> passwordEncoder.matches(user.getPassword(), u.getPassword())) 35 | .map(u -> { 36 | // 将主键/角色置于header 37 | Map tokenM = Map.of( 38 | RequestConstant.UID, u.getId(), 39 | RequestConstant.ROLE, u.getRole()); 40 | String token = jwtComponent.encode(tokenM); 41 | HttpHeaders headers = response.getHeaders(); 42 | headers.add("token", token); 43 | headers.add("role", u.getRole()); 44 | return ResultVO.success(Map.of("user", u)); 45 | }) 46 | .defaultIfEmpty(ResultVO.error(Code.LOGIN_ERROR)); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /spring-examples/src/test/java/org/example/springexamples/mock/UserServiceTest.java: -------------------------------------------------------------------------------- 1 | package org.example.springexamples.mock; 2 | 3 | import org.example.springexamples.mock.dox.User; 4 | import org.example.springexamples.mock.repository.UserRepositoryMock; 5 | import org.example.springexamples.mock.service.UserService; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.junit.jupiter.api.Test; 8 | import org.mockito.Mockito; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.boot.test.context.SpringBootTest; 11 | import org.springframework.test.context.bean.override.mockito.MockitoBean; 12 | 13 | import java.util.List; 14 | 15 | @SpringBootTest 16 | @Slf4j 17 | class UserServiceTest { 18 | @MockitoBean 19 | private UserRepositoryMock userRepositoryMock; 20 | @Autowired 21 | private UserService userService; 22 | 23 | @Test 24 | void getUser() { 25 | // 模拟调用findById()方法,参数为`1`时,返回指定对象 26 | Mockito.when(userRepositoryMock.findById("1")) 27 | .thenReturn(User.builder().id("1").name("BO").build()); 28 | User u = userService.getUser("1"); 29 | log.debug("{}", u); 30 | // 没有模拟,因此为空 31 | User u2 = userService.getUser("2"); 32 | log.debug("{}", u2); 33 | } 34 | 35 | @Test 36 | void addUser() { 37 | Mockito.when(userRepositoryMock.save(Mockito.any())) 38 | .thenReturn(User.builder().id("1").name("BO").build()); 39 | User u = userService.addUser(User.builder().name("any").build()); 40 | log.debug("{}", u); 41 | } 42 | 43 | @Test 44 | void getUser2() { 45 | List users = List.of(User.builder().id("1").name("BO").build(), User.builder().id("2").name("SUN").build()); 46 | // 获取调用Mock组件时传入的参数,操作并返回结果对象 47 | Mockito.when(userRepositoryMock.findById(Mockito.anyString())) 48 | .thenAnswer(answer -> { 49 | String uid = answer.getArgument(0); 50 | return users.stream() 51 | .filter(u -> u.getId().equals(uid)) 52 | .findFirst() 53 | .orElse(null); 54 | }); 55 | 56 | User user = userService.getUser("2"); 57 | log.debug("{}", user); 58 | // 不存在 59 | User user2 = userService.getUser("100"); 60 | log.debug("{}", user2); 61 | } 62 | 63 | } -------------------------------------------------------------------------------- /jdbc-examples/src/test/java/org/example/jdbcexamples/repository/UserRepositoryTest.java: -------------------------------------------------------------------------------- 1 | package org.example.jdbcexamples.repository; 2 | 3 | import org.example.jdbcexamples.dox.User; 4 | import org.example.jdbcexamples.dto.AddressUser; 5 | import org.example.jdbcexamples.dto.UserAddress; 6 | import org.example.jdbcexamples.dto.UserAddress3; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.junit.jupiter.api.Test; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.boot.test.context.SpringBootTest; 11 | import org.springframework.data.domain.PageRequest; 12 | import org.springframework.data.domain.Pageable; 13 | 14 | 15 | @SpringBootTest 16 | @Slf4j 17 | class UserRepositoryTest { 18 | @Autowired 19 | private UserRepository userRepository; 20 | 21 | @Test 22 | public void saveUser() { 23 | User user = User.builder().name("BO").build(); 24 | userRepository.save(user); 25 | } 26 | 27 | @Test 28 | public void findById() { 29 | userRepository.findById("1530375756270878723") 30 | .ifPresent(user -> log.debug("{}", user)); 31 | } 32 | 33 | @Test 34 | void findAll() { 35 | int pageSize = 5; 36 | int page = 4; 37 | userRepository.findAll((page - 1) * pageSize, 5) 38 | .forEach(user -> log.debug(user.toString())); 39 | } 40 | 41 | @Test 42 | void findAllPageable() { 43 | int pageSize = 5; 44 | int page = 4; 45 | Pageable pageable = PageRequest.of(page - 1, pageSize); 46 | userRepository.findAll(pageable) 47 | .forEach(user -> log.debug(user.toString())); 48 | } 49 | 50 | @Test 51 | void findByIdDesc() { 52 | int pageSize = 5; 53 | int page = 1; 54 | Pageable pageable = PageRequest.of(page - 1, pageSize); 55 | userRepository.findByIdDesc(pageable) 56 | .forEach(user -> log.debug(user.toString())); 57 | } 58 | 59 | @Test 60 | void findAddressUser() { 61 | AddressUser addreddUser = userRepository.findByAddressId("1057571239761793024"); 62 | log.debug(addreddUser.toString()); 63 | } 64 | 65 | @Test 66 | void findUserAddress() { 67 | UserAddress userAddress = userRepository.findUserAddress("1057571239761793024"); 68 | log.debug("{}", userAddress.getUser()); 69 | userAddress.getAddresses().forEach(a -> log.debug("{}", a)); 70 | } 71 | 72 | @Test 73 | void findUserAddress3() { 74 | UserAddress3 u = userRepository.findUserAddress3("1057571239761793024"); 75 | log.debug("{}", u); 76 | } 77 | } -------------------------------------------------------------------------------- /webflux-r2dbc-examples/src/main/java/org/example/webfluxr2dbcexamples/filter/LoginFilter.java: -------------------------------------------------------------------------------- 1 | package org.example.webfluxr2dbcexamples.filter; 2 | 3 | import org.example.webfluxr2dbcexamples.component.JWTComponent; 4 | import org.example.webfluxr2dbcexamples.exception.Code; 5 | import org.example.webfluxr2dbcexamples.vo.RequestConstant; 6 | import lombok.RequiredArgsConstructor; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.core.annotation.Order; 9 | import org.springframework.http.server.reactive.ServerHttpRequest; 10 | import org.springframework.stereotype.Component; 11 | import org.springframework.web.server.ServerWebExchange; 12 | import org.springframework.web.server.WebFilter; 13 | import org.springframework.web.server.WebFilterChain; 14 | import org.springframework.web.util.pattern.PathPattern; 15 | import org.springframework.web.util.pattern.PathPatternParser; 16 | import reactor.core.publisher.Mono; 17 | 18 | import java.util.List; 19 | import java.util.Map; 20 | 21 | @Component 22 | @Slf4j 23 | @Order(1) 24 | @RequiredArgsConstructor 25 | public class LoginFilter implements WebFilter { 26 | private final PathPattern path = new PathPatternParser().parse("/api/**"); 27 | private final List excludesS = List.of(new PathPatternParser().parse("/api/login")); 28 | private final JWTComponent jwtComponent; 29 | private final ResponseHelper responseHelper; 30 | @Override 31 | public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { 32 | ServerHttpRequest request = exchange.getRequest(); 33 | // 排除 34 | for (PathPattern p : excludesS) { 35 | if (p.matches(request.getPath().pathWithinApplication())) { 36 | return chain.filter(exchange); 37 | } 38 | } 39 | // 非过滤路径,按异常处理 40 | if (!path.matches(request.getPath().pathWithinApplication())) { 41 | return responseHelper.response(Code.BAD_REQUEST, exchange); 42 | } 43 | String token = request.getHeaders().getFirst(RequestConstant.TOKEN); 44 | if (token == null) { 45 | return responseHelper.response(Code.UNAUTHORIZED, exchange); 46 | } 47 | return jwtComponent.decode(token) 48 | .flatMap(decode -> { 49 | Map attributes = exchange.getAttributes(); 50 | attributes.put(RequestConstant.UID, decode.getClaim(RequestConstant.UID).asString()); 51 | attributes.put(RequestConstant.ROLE, decode.getClaim(RequestConstant.ROLE).asString()); 52 | return chain.filter(exchange); 53 | }); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /redis-examples/src/main/java/org/example/redisexamples/service/OrderService.java: -------------------------------------------------------------------------------- 1 | package org.example.redisexamples.service; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.example.redisexamples.component.ULID; 6 | import org.example.redisexamples.dox.Item; 7 | import org.example.redisexamples.dox.Order; 8 | import org.redisson.api.*; 9 | import org.redisson.api.stream.StreamAddArgs; 10 | import org.redisson.client.codec.IntegerCodec; 11 | import org.redisson.client.codec.StringCodec; 12 | import org.springframework.stereotype.Service; 13 | 14 | import java.util.List; 15 | 16 | @Service 17 | @Slf4j 18 | @AllArgsConstructor 19 | public class OrderService { 20 | private final RedissonClient redissonClient; 21 | private final ULID ulid; 22 | 23 | // 将指定商品批量加入redis抢购数据库 24 | public void addItems(List items) { 25 | // 获取操作管道的批处理对象 26 | RBatch batch = redissonClient.createBatch(); 27 | for (Item item : items) { 28 | // 获取操作redis hash对象。以整数序列化 29 | RMapAsync map = 30 | batch.getMap(Item.PRE_KEY + item.getId(), IntegerCodec.INSTANCE); 31 | // 有则覆盖 32 | map.putAsync("total", item.getTotal()); 33 | } 34 | batch.execute(); 35 | } 36 | 37 | // 传入抢购的商品,抢购用户 38 | public long rushBuy(Item item, String userId) { 39 | var key = Item.PRE_KEY + item.getId(); 40 | // 调用redis抢购函数 41 | long quantity = redissonClient 42 | .getFunction() 43 | .call(FunctionMode.WRITE, "rushBuy_0", FunctionResult.LONG, List.of(key)); 44 | if (quantity == -1) { 45 | log.debug("抢光啦"); 46 | return quantity; 47 | } 48 | // 抢购成功,则基于用户ID执行扣款等操作 49 | log.debug("{},抢购成功", userId); 50 | // 创建订单 51 | Order order = Order.builder() 52 | .id(ulid.nextULID()) 53 | .itemId(item.getId()) 54 | .userId(userId) 55 | .build(); 56 | // 默认基于Kryo5Codec序列化对象,非json可读。 57 | RBucket bucket = redissonClient.getBucket(Order.PRE_KEY + order.getId()); 58 | // 持久化订单 59 | bucket.set(order); 60 | // 发送到订单处理消息队列,异步延迟处理订单,减轻服务器压力 61 | pushOrderQueue(order); 62 | return quantity; 63 | } 64 | 65 | private void pushOrderQueue(Order order) { 66 | // 消息ID类型;消息体类型 67 | RStream stream = redissonClient.getStream(Order.STREAM_KEY, StringCodec.INSTANCE); 68 | // 仅需在消息体添加订单ID 69 | stream.add(StreamAddArgs.entry("orderid", order.getId())); 70 | 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /redis-examples/src/main/java/org/example/redisexamples/listener/MessageListenerContainerConfiguration.java: -------------------------------------------------------------------------------- 1 | package org.example.redisexamples.listener; 2 | 3 | import org.example.redisexamples.dox.Order; 4 | import jakarta.annotation.PreDestroy; 5 | import lombok.RequiredArgsConstructor; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.data.redis.connection.RedisConnectionFactory; 10 | import org.springframework.data.redis.connection.stream.Consumer; 11 | import org.springframework.data.redis.connection.stream.ObjectRecord; 12 | import org.springframework.data.redis.connection.stream.ReadOffset; 13 | import org.springframework.data.redis.connection.stream.StreamOffset; 14 | import org.springframework.data.redis.stream.StreamMessageListenerContainer; 15 | 16 | import java.time.Duration; 17 | import java.util.concurrent.ExecutorService; 18 | import java.util.concurrent.Executors; 19 | import java.util.concurrent.TimeUnit; 20 | 21 | @Configuration 22 | @RequiredArgsConstructor 23 | @Slf4j 24 | public class MessageListenerContainerConfiguration { 25 | 26 | private final RedisConnectionFactory redisConnectionFactory; 27 | private final OrderMessageListener listener; 28 | // 创建基于虚拟线程的执行器。需在配置中启动。 29 | private final ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); 30 | 31 | @PreDestroy 32 | public void destroyExecutor() throws InterruptedException { 33 | executor.shutdown(); 34 | if (executor.awaitTermination(2, TimeUnit.SECONDS)) { 35 | log.debug("executor.awaitTermination"); 36 | } 37 | } 38 | @Bean 39 | public StreamMessageListenerContainer> register() { 40 | // 注册监听器 41 | StreamMessageListenerContainer.StreamMessageListenerContainerOptions> options = 42 | StreamMessageListenerContainer.StreamMessageListenerContainerOptions 43 | .builder() 44 | // 自定义单执行器。默认使用为执行短操作反复创建销毁的伪线程池 45 | .executor(executor) 46 | // Stream 读取消息时的阻塞时间 47 | .pollTimeout(Duration.ofMillis(100)) 48 | // 转换消息体为具体类型 49 | .targetType(String.class).build(); 50 | StreamMessageListenerContainer> container = StreamMessageListenerContainer.create(redisConnectionFactory, options); 51 | 52 | // 可使用receiveAutoAck()方法接收后自动返回确认。但不会自动移除,可在onMessage()消费后收到确认/移除 53 | // 消费组名称,消费者名称 54 | container.receive(Consumer.from(Order.GROUP_NAME, Order.GROUP_CONSUMER), StreamOffset.create(Order.STREAM_KEY, ReadOffset.lastConsumed()), listener); 55 | 56 | container.start(); 57 | return container; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.example 8 | springboot-course 9 | ${revision} 10 | pom 11 | 12 | 1.0-SNAPSHOT 13 | 21 14 | 2025.0.0 15 | 16 | 17 | org.springframework.boot 18 | spring-boot-starter-parent 19 | 3.5.5 20 | 21 | 22 | 23 | 24 | 25 | 26 | org.springframework.cloud 27 | spring-cloud-dependencies 28 | ${spring-cloud.version} 29 | pom 30 | import 31 | 32 | 33 | 34 | 35 | 36 | jdbc-examples 37 | spring-examples 38 | springmvc-examples 39 | cache-examples 40 | redis-examples 41 | webflux-r2dbc-examples 42 | consul-examples 43 | springsecurity-examples 44 | 45 | 46 | 47 | 48 | org.apache.maven.plugins 49 | maven-compiler-plugin 50 | 51 | 52 | 53 | org.projectlombok 54 | lombok 55 | 56 | 57 | 58 | 59 | 60 | org.springframework.boot 61 | spring-boot-maven-plugin 62 | 63 | 64 | 65 | org.projectlombok 66 | lombok 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /jdbc-examples/src/test/java/org/example/jdbcexamples/repository/AddressRepositoryTest.java: -------------------------------------------------------------------------------- 1 | package org.example.jdbcexamples.repository; 2 | 3 | import org.example.jdbcexamples.dox.Address; 4 | import org.example.jdbcexamples.dox.User; 5 | import org.example.jdbcexamples.dto.AddressUser; 6 | import org.example.jdbcexamples.dto.AddressUser2; 7 | import org.example.jdbcexamples.dto.UserAddress; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.junit.jupiter.api.Test; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.boot.test.context.SpringBootTest; 12 | 13 | import java.util.List; 14 | 15 | @SpringBootTest 16 | @Slf4j 17 | class AddressRepositoryTest { 18 | @Autowired 19 | private AddressRepository addressRepository; 20 | @Autowired 21 | private UserRepository userRepository; 22 | 23 | 24 | @Test 25 | void find() { 26 | for (Address a : addressRepository.find("1057571239761793024")) { 27 | log.debug("{}", a); 28 | } 29 | } 30 | 31 | @Test 32 | void findByUserId() { 33 | for (Address a : addressRepository.findByUserId("1057571239761793024")) { 34 | log.debug("{}", a); 35 | } 36 | } 37 | 38 | @Test 39 | void updateAddress() { 40 | // 更新detail属性,并更新userid属性为null 41 | /*Address address = Address.builder().id(908037103134662656L).detail("110").build(); 42 | addressRepository.save(address);*/ 43 | // 模拟传入的局部更新数据 44 | Address addressSource = Address.builder().id("985074876576772096").detail("110").build(); 45 | // 查询出全部数据 46 | addressRepository.findById("985074876576772096").ifPresent(result -> { 47 | result.setDetail(addressSource.getDetail()); 48 | addressRepository.save(result); 49 | }); 50 | } 51 | 52 | @Test 53 | void updateDetail() { 54 | // 模拟传入的局部更新数据 55 | Address addressSource = Address.builder().id("985074876576772096").detail("888").build(); 56 | addressRepository.updateDetail(addressSource.getDetail(), addressSource.getId()); 57 | } 58 | 59 | @Test 60 | void findAddressUserById() { 61 | AddressUser addressUser = addressRepository.findAddressUserById("1283819168057049188"); 62 | log.debug(addressUser.toString()); 63 | } 64 | 65 | @Test 66 | void findUserAddress() { 67 | User user = userRepository.findById("1057571239761793024").orElse(null); 68 | List
addresses = addressRepository.find("1057571239761793024"); 69 | UserAddress userAddress = UserAddress.builder().user(user).addresses(addresses).build(); 70 | log.debug(userAddress.toString()); 71 | } 72 | 73 | @Test 74 | void findAddressUser2ById() { 75 | AddressUser2 addressUser2 = addressRepository.findAddressUser2ById("1057590557849407488"); 76 | log.debug("{}", addressUser2.getUser()); 77 | log.debug("{}", addressUser2.getAddress()); 78 | } 79 | 80 | 81 | } -------------------------------------------------------------------------------- /redis-examples/README.md: -------------------------------------------------------------------------------- 1 | # redis-examples 2 | 3 | ### Introductions 4 | Redis服务器: redis:8.2。 5 | 支持Redis/RediSearch/RedisJSON等。 6 | 7 | Java客户端:redisson-spring-boot-starter。 8 | 支持redis扩展的实现,但还不支持向量数据检索; 9 | 支持原子变量/分布式锁/队列/集合消息等; 10 | 支持可扩展的Java数据结构; 11 | 基于非阻塞Netty框架,可以便捷的切换为响应式异步非阻塞编程模式; 12 | 13 | ### ULID 14 | 使用分布式ULID主键生成策略,由时间/随机数/base32编码组成的26位字符串。 15 | 16 | ### Redis Functions 17 | redis要求函数应仅基于传入的Redis keys操作记录。 18 | keys,需要操作的键列表,因此即使不用调用是也应传入空键。 19 | args,函数所需参数列表。 20 | LoadRedisScriptListener,基于springboot启动事件监听回调,在服务启动时自动加载函数脚本至redis服务器。 21 | 22 | ### Expire Keys 23 | 模拟第三方平台接口调用限流,每账号在Xs内允许调用N次。 24 | 简单的递增计数+数据时效性,通过Redis String实现即可 25 | - 收到请求后从请求中获取请求账号(校验合法性) 26 | - 调用redis函数,检索限流数据中是否包含指定账号记录 27 | - 不存在,说明时间段内没有调用请求。以账号为键创建String类型数据记录,初始化计数1,并添加过期时间,返回允许 28 | - 存在,则获取调用计数比较限制次数,超过返回禁止;未超过,递增计数+1,返回允许 29 | 30 | mylib.lua脚本,expireAPICount函数。 31 | ExpireTest,限流测试。指定时间段内的请求次数限制。 32 | 33 | ### Redis Streams & Message Queues 34 | OrderMessageListener,订单消息监听器,手动确认/移除消息。 35 | MessageListenerContainerConfiguration,注册订单消息监听器,并创建消费组/消费者。 36 | 不存在没有`消费组`的stream,因此测试时可整体移除stream,但不能仅移除stream下的`消费组`。 37 | 38 | Container 默认使用执行短操作,反复创建销毁的伪线程池。 39 | 也可通过spring Scheduled定时任务轮询redis消息队列。 40 | 41 | MKSTREAM,redis stream参数。创建group时stream不存在则自动创建,对应makeStream()方法。 42 | 43 | ### Limited-Time Offers 44 | 秒杀抢购3件套。限流/内存单线程数据库/消息队列。 45 | 46 | 需求:模拟商品抢购。 47 | 高并发下需确保抢购商品数量的一致性(SQL数据库的悲观锁),通过redis单线程执行hash hincrby指令特性,实现商品数量的原子性改变。 48 | 通过将订单处理数据发送至消息队列异步处理,减轻抢购服务器压力。 49 | 结合Redis函数/lua脚本函数/Hash数据类型/Stream消息队列实现 50 | 以商品ID为键,以Redis Hash存储商品数量 51 | 抢购时,基于商品键获取数量并判断是否售空; 52 | - 已售空,返回-1。如果返回0,则无法区分是已售空,还是本次抢购后数量为0(也可以将售空商品移除,通过键的存在判断售空)。 53 | - 未售空,数量减1,并返回剩余商品数量 54 | - 抢购成功后,创建存储订单数据,并将订单以消息发送至订单处理消息队列 55 | - 订单处理消息监听器,接收消息并处理订单 56 | 57 | mylib.lua 脚本,rushBuy 函数。 58 | OrderService.addItems(),向redis添加hash类型抢购商品数据。 59 | OrderService.rushBuy(),抢购方法,调用脚本抢购函数,创建订单记录。 60 | OrderService.pushOrderQueue(),发送订单处理消息方法。 61 | OrderServiceTest,测试类。 62 | 63 | ### Spring Cache Actuator 64 | 可通过spring-boot-starter-actuator监控缓存命中率等数据 65 | 66 | ### Others 67 | 基于redisson创建stream键与消费组 68 | ```shell 69 | RStream stream = client.getStream(Order.STREAM_KEY); 70 | if (!stream.isExists()) { 71 | // 如果stream键不存在,创建group的同时创建stream 72 | stream.createGroup(StreamCreateGroupArgs.name(Order.GROUP_NAME).makeStream()); 73 | } 74 | ``` 75 | 76 | ```shell 77 | if (Boolean.FALSE.equals(template.hasKey(Order.STREAM_KEY))) { 78 | redisConnectionFactory.getConnection().streamCommands() 79 | .xGroupCreate(Order.STREAM_KEY.getBytes(), 80 | Order.GROUP_NAME, 81 | ReadOffset.from("0-0"), 82 | true); 83 | } 84 | ``` 85 | 86 | ```shell 87 | client.getStream(Order.GROUP_NAME).ack(Order.GROUP_NAME, smid); 88 | ``` 89 | 90 | ```shell 91 | template.opsForStream().acknowledge(Order.GROUP_NAME, message); 92 | template.opsForStream().delete(Order.STREAM_KEY, message.getId()); 93 | ``` -------------------------------------------------------------------------------- /webflux-r2dbc-examples/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.example 7 | springboot-course 8 | ${revision} 9 | 10 | webflux-r2dbc-examples 11 | 0.0.1-SNAPSHOT 12 | webflux-r2dbc-examples 13 | webflux-r2dbc-examples 14 | 15 | 16 | 17 | org.springframework.security 18 | spring-security-crypto 19 | 20 | 21 | 22 | com.auth0 23 | java-jwt 24 | 4.4.0 25 | 26 | 27 | 28 | org.junit.platform 29 | junit-platform-launcher 30 | test 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-data-r2dbc 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-webflux 39 | 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-devtools 44 | runtime 45 | true 46 | 47 | 48 | com.mysql 49 | mysql-connector-j 50 | runtime 51 | 52 | 53 | io.asyncer 54 | r2dbc-mysql 55 | runtime 56 | 57 | 58 | org.projectlombok 59 | lombok 60 | true 61 | 62 | 63 | org.springframework.boot 64 | spring-boot-starter-test 65 | test 66 | 67 | 68 | io.projectreactor 69 | reactor-test 70 | test 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /spring-examples/src/main/java/org/example/springexamples/example03/aopadvanced/MyAuthAspect.java: -------------------------------------------------------------------------------- 1 | package org.example.springexamples.example03.aopadvanced; 2 | 3 | import org.example.springexamples.exception.XException; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.aspectj.lang.ProceedingJoinPoint; 6 | import org.aspectj.lang.annotation.Around; 7 | import org.aspectj.lang.annotation.Aspect; 8 | import org.aspectj.lang.reflect.MethodSignature; 9 | import org.springframework.stereotype.Component; 10 | 11 | @Component 12 | @Aspect 13 | @Slf4j 14 | public class MyAuthAspect { 15 | // 方法级 16 | @Around("@annotation(myAuthority)") 17 | public Object checkMethod(ProceedingJoinPoint joinPoint, MyAuthority myAuthority) throws Throwable { 18 | for (MyAuthority.MyAuthorityType au : myAuthority.value()) { 19 | log.debug("{}", au); 20 | // 此处模拟用户实际权限为user。应该动态获取 21 | // 如果用户不是admin则抛出异常 22 | if(!au.equals(MyAuthority.MyAuthorityType.USER)) { 23 | log.debug("@annotation not USER"); 24 | throw XException.builder().message("无权限").build(); 25 | } 26 | } 27 | log.debug("@annotation"); 28 | return joinPoint.proceed(); 29 | } 30 | 31 | // 类型级 32 | @Around("@within(myAuthority)") 33 | public Object checkType(ProceedingJoinPoint joinPoint, MyAuthority myAuthority) throws Throwable { 34 | MethodSignature ms = (MethodSignature) joinPoint.getSignature(); 35 | // 方法有注解则忽略,由方法级切面处理 36 | if (ms.getMethod().getAnnotation(MyAuthority.class) != null) { 37 | log.debug("{}", myAuthority); 38 | return joinPoint.proceed(); 39 | } 40 | // 方法没有注解,则按类型权限值 41 | for (MyAuthority.MyAuthorityType au : myAuthority.value()) { 42 | log.debug("{}", au); 43 | // 此处模拟用户实际权限为user。应该动态获取 44 | // 如果用户不是admin则抛出异常 45 | if(!au.equals(MyAuthority.MyAuthorityType.USER)) { 46 | log.debug("@within not admin"); 47 | throw XException.builder().message("无权限").build(); 48 | } 49 | } 50 | log.debug("@within"); 51 | return joinPoint.proceed(); 52 | } 53 | 54 | // 切入任何包含自定义注解的类与方法。顺序必须是先判断方法再判断类型 55 | //@Around("@within(myAuthority) || @annotation(myAuthority)") 56 | public Object checkMethodAndType(ProceedingJoinPoint joinPoint, MyAuthority myAuthority) throws Throwable { 57 | // 如果是类型级注解,而方法上没有,则需获取类型注解的值 58 | if(myAuthority == null) { 59 | myAuthority = joinPoint.getTarget().getClass().getAnnotation((MyAuthority.class)); 60 | } 61 | for (MyAuthority.MyAuthorityType au : myAuthority.value()) { 62 | log.debug("{}", au); 63 | // 此处模拟用户实际权限为user。应该动态获取 64 | // 如果用户不是admin则抛出异常 65 | if(!au.equals(MyAuthority.MyAuthorityType.USER)) { 66 | throw XException.builder().message("无权限").build(); 67 | } 68 | } 69 | log.debug("@within || @annotation"); 70 | return joinPoint.proceed(); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /jdbc-examples/src/main/java/org/example/jdbcexamples/service/AccountService.java: -------------------------------------------------------------------------------- 1 | package org.example.jdbcexamples.service; 2 | 3 | import org.example.jdbcexamples.dox.Account; 4 | import org.example.jdbcexamples.repository.AccountPessRepository; 5 | import org.example.jdbcexamples.repository.AccountRepository; 6 | import lombok.RequiredArgsConstructor; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.stereotype.Service; 9 | import org.springframework.transaction.annotation.Transactional; 10 | 11 | @Service 12 | @RequiredArgsConstructor 13 | @Slf4j 14 | public class AccountService { 15 | private final AccountRepository accountRepository; 16 | private final AccountPessRepository accountPessRepository; 17 | 18 | @Transactional 19 | public void update(String uid, float spend) { 20 | // 先查询 21 | Account account = accountRepository.findById(uid) 22 | .orElseThrow(() -> new RuntimeException("账户不存在")); 23 | log.debug("thread: {}; balance: {}", Thread.currentThread().getName(), account.getBalance()); 24 | // 再阻塞,模拟耗时业务 25 | try { 26 | Thread.sleep(1000); 27 | } catch (InterruptedException e) { 28 | throw new RuntimeException(e); 29 | } 30 | // 更新 31 | float balance = account.getBalance() - spend; 32 | if (balance < 0) { 33 | throw new RuntimeException("账户余额不足"); 34 | } 35 | account.setBalance(balance); 36 | // 同步 37 | accountRepository.save(account); 38 | } 39 | 40 | @Transactional 41 | public void updatePess(String uid, float spend) { 42 | // 基于for update悲观锁获取 43 | var account = accountPessRepository.findByIdPess(uid) 44 | .orElseThrow(() -> new RuntimeException("账户不存在")); 45 | log.debug("thread: {}; balance: {}", Thread.currentThread().getName(), account.getBalance()); 46 | try { 47 | Thread.sleep(1000); 48 | } catch (InterruptedException e) { 49 | throw new RuntimeException(e); 50 | } 51 | float balance = account.getBalance() - spend; 52 | if (balance < 0) { 53 | throw new RuntimeException("账户余额不足"); 54 | } 55 | account.setBalance(balance); 56 | // 同步 57 | accountPessRepository.save(account); 58 | } 59 | 60 | @Transactional 61 | public void updateBalance(String uid, float spend) { 62 | var account = accountPessRepository.findById(uid) 63 | .orElseThrow(() -> new RuntimeException("账户不存在")); 64 | log.debug("thread: {}; balance: {}", Thread.currentThread().getName(), account.getBalance()); 65 | // 数据库执行update语句时,添加行级排他锁 66 | var result = accountPessRepository.updateBalance(uid, spend); 67 | if (result == 0) { 68 | throw new RuntimeException("账户余额不足"); 69 | } 70 | var account2 = accountPessRepository.findById(uid) 71 | .orElseThrow(() -> new RuntimeException("账户不存在")); 72 | log.debug("thread: {}; balance: {}", Thread.currentThread().getName(), account2.getBalance()); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /jdbc-examples/src/test/java/org/example/jdbcexamples/service/AccountServiceTest.java: -------------------------------------------------------------------------------- 1 | package org.example.jdbcexamples.service; 2 | 3 | import org.example.jdbcexamples.dox.Account; 4 | import org.example.jdbcexamples.dox.AccountPess; 5 | import org.example.jdbcexamples.repository.AccountPessRepository; 6 | import org.example.jdbcexamples.repository.AccountRepository; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.junit.jupiter.api.Test; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.boot.test.context.SpringBootTest; 11 | 12 | import java.util.concurrent.CountDownLatch; 13 | 14 | @SpringBootTest 15 | @Slf4j 16 | public class AccountServiceTest { 17 | @Autowired 18 | private AccountRepository accountRepository; 19 | @Autowired 20 | private AccountPessRepository accountPessRepository; 21 | @Autowired 22 | private AccountService accountService; 23 | 24 | @Test 25 | public void addAccountTest() { 26 | Account account = Account.builder() 27 | .balance(1000F).name("BO").build(); 28 | accountRepository.save(account); 29 | } 30 | 31 | @Test 32 | public void update() throws InterruptedException { 33 | int count = 2; 34 | CountDownLatch latch = new CountDownLatch(count); 35 | // 使用逻辑线程 36 | for (int i = 0; i < count; i++) { 37 | Thread.ofPlatform() 38 | .start(() -> { 39 | try { 40 | accountService.update("1284555615357681664", 200); 41 | } finally { 42 | latch.countDown(); 43 | } 44 | }); 45 | } 46 | latch.await(); 47 | } 48 | 49 | // 50 | @Test 51 | void saveAccountPess() { 52 | var accountPess = AccountPess.builder() 53 | .balance(1000F) 54 | .name("BO") 55 | .build(); 56 | accountPessRepository.save(accountPess); 57 | } 58 | 59 | // 测试通过悲观锁实现 60 | @Test 61 | public void updatePess() throws InterruptedException { 62 | int count = 2; 63 | CountDownLatch latch = new CountDownLatch(count); 64 | // 使用VT 65 | for (int i = 0; i < count; i++) { 66 | Thread.startVirtualThread(() -> { 67 | try { 68 | accountService.updatePess("1284554842091601920", 200); 69 | } finally { 70 | latch.countDown(); 71 | } 72 | }); 73 | } 74 | latch.await(); 75 | } 76 | 77 | // 测试通过SQL语句实现更新 78 | @Test 79 | void updateBalance() throws InterruptedException { 80 | int count = 2; 81 | CountDownLatch latch = new CountDownLatch(count); 82 | // 使用VT 83 | for (int i = 0; i < count; i++) { 84 | Thread.startVirtualThread(() -> { 85 | try { 86 | accountService.updateBalance("1284554842091601920", 200); 87 | } finally { 88 | latch.countDown(); 89 | } 90 | }); 91 | } 92 | latch.await(); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Frameworks for Web Application - Springboot Course 2 | 3 | ### Overview 4 | 5 | China, Northeast Forestry University, Software Engineering, Frameworks for Web Application. 6 | 7 | Web系统框架,是东北林业大学软件工程专业第5学期的一门专业选修课。课程包含32+16学时。 8 | 主讲教师:王波老师。 9 | 10 | 课程基于Springboot/spring Cloud框架的后端微服务架构、设计思想与实现技术。 11 | 课程的具体技术内容包括: 12 | 13 | - 基于spring-data-jdbc的半自动面向对象持久化技术 14 | - 基于MySQL的关系型/非关系型混合开发技术 15 | - 基于Spring框架的业务逻辑层技术 16 | - 基于springmvc框架的控制层技术 17 | - 基于springdoc-openapi框架的API接口文档生成技术 18 | - 基于spring-security框架的安全服务技术 19 | - 基于JWT/自定义加密数据的分布式单点登录鉴权技术 20 | - 基于JSR349规范的数据校验技术 21 | - 基于Timer的定时服务技术 22 | - 基于AOP的切面技术 23 | - 基于自定义注解/反射/拦截器/AOP的细粒度权限控制与业务逻辑功能扩展 24 | - 基于自定义异常的全局异常处理 25 | - 基于Junit5/Spring-Testing/Mock框架的测试技术 26 | - 基于spring-cache的缓存技术 27 | - 基于Redis的缓存/限流/消息队列等技术 28 | - 基于webflux/r2dbc的异步非阻塞持久化技术 29 | - [基于spring-cloud的微服务网关/服务注册发现技术](https://github.com/bwhyman/uber-project-examples) 30 | - 基于spring-ai的AI应用程序开发技术 31 | 32 | ### Development Environments 33 | 34 | 开发环境/框架及版本: 35 | 36 | - IntelliJ IDEA Ultimate 37 | - OpenJDK ^21 38 | - Springboot ^3.5 39 | - Git ^2.4 40 | - MySQL ^8.4 41 | - Redis ^8.2 42 | 43 | ### Updates 44 | 45 | #### 2025-10-08 46 | 47 | 一直以为的零拷贝技术下载,实际由于servlet对outputstream的封装,底层根本无法调用原生函数,依然是传统的字节数组缓冲区。 48 | redis:8 全文检索示例。 49 | 50 | #### 2025-09-17 51 | 52 | springmvc-examples,启用VT。 53 | 54 | #### 2025-09-13 55 | 56 | springsecurity-examples 57 | 58 | ### 2025-06-25 59 | 60 | springboot: 3.5.0 61 | spring-cloud: 2025.0.0 62 | spring-ai-examples 63 | 64 | ### Examples 65 | 66 | 课程代码由单工程多模块组成: 67 | 68 | - /jdbc-examples 69 | - /spring-examples 70 | - /springmvc-examples 71 | - /cache-examples 72 | - /redis-examples 73 | - /webflux-r2dbc-examples 74 | - /consul-examples 75 | - /backend-examples,为前端项目提供模拟数据互交 76 | - [/spring-ai-example](https://github.com/bwhyman/spring-ai-examples) 77 | - /springsecurity-examples 78 | 79 | ### Online Tutorials 80 | 81 | - [在线课程](https://mooc1-1.chaoxing.com/course/208931964.html) 82 | 83 | ### Backend Integration 84 | 85 | [后端微服务框架整合](https://mooc1.chaoxing.com/nodedetailcontroller/visitnodedetail?courseId=208931964&knowledgeId=298571472) 86 | 87 | [前后端联调](https://mooc1.chaoxing.com/nodedetailcontroller/visitnodedetail?knowledgeId=300177471&courseId=208931964) 88 | 89 | ### Microservices 90 | 91 | 整合基于springboot的微服务/基于spring-cloud-gateway的异步非阻塞网关微服务/ 92 | 基于spring-cloud-alibaba-nacos的微服务注册与发现服务/华为云服务器/Docker容器的,开发测试与生产环境下部署的流程演示。 93 | 94 | - https://github.com/bwhyman/uber-project-examples 95 | - [视频讲解](https://mooc1-1.chaoxing.com/nodedetailcontroller/visitnodedetail?courseId=208931964&knowledgeId=394488338) 96 | 97 | ### Continuous Deployment 98 | 99 | 基于GitHub Actions工作流/GitHub Packages/华为云服务器的,持续集成/持续交付/持续部署示例 100 | 101 | - [视频讲解](https://mooc1-1.chaoxing.com/nodedetailcontroller/visitnodedetail?courseId=208931964&knowledgeId=326897803) 102 | 103 | ### Related Courses 104 | 105 | - https://github.com/bwhyman/java-course 106 | - https://github.com/bwhyman/web-course 107 | - https://github.com/bwhyman/springboot-course 108 | - https://github.com/bwhyman/vite-vue3-examples 109 | - https://github.com/bwhyman/flutter_examples 110 | -------------------------------------------------------------------------------- /redis-examples/src/test/java/org/example/redisexamples/RedisTest.java: -------------------------------------------------------------------------------- 1 | package org.example.redisexamples; 2 | 3 | import org.example.redisexamples.component.ULID; 4 | import org.example.redisexamples.dox.Order; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.junit.jupiter.api.Test; 7 | import org.redisson.api.RBucket; 8 | import org.redisson.api.RMap; 9 | import org.redisson.api.RedissonClient; 10 | import org.redisson.client.codec.StringCodec; 11 | import org.redisson.codec.TypedJsonJacksonCodec; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.boot.test.context.SpringBootTest; 14 | 15 | @SpringBootTest 16 | @Slf4j 17 | public class RedisTest { 18 | @Autowired 19 | private RedissonClient redissonClient; 20 | @Autowired 21 | private ULID ulid; 22 | 23 | @Test 24 | void setStringTest() { 25 | // 使用`类型:ID`命名键 26 | RBucket strSet = redissonClient.getBucket("name:" + "1", StringCodec.INSTANCE); 27 | log.debug("键为`name:1`的数据是否存在:{}", strSet.isExists()); 28 | // 不存在创建,存在则覆盖 29 | strSet.set("BO"); 30 | log.debug("键为`name:1`的数据是否存在:{}", strSet.isExists()); 31 | RBucket strGet = redissonClient.getBucket("name:" + "1", StringCodec.INSTANCE); 32 | log.debug(strGet.get()); 33 | } 34 | 35 | @Test 36 | void setHashTest() { 37 | // 映射指定键对应的redis hash为Java Map类型 38 | // 因为hash字段对应的值可能时字符串与整型,因此声明为Object 39 | RMap map = redissonClient.getMap("bike:2", StringCodec.INSTANCE); 40 | map.put("brand", "守夜人"); 41 | map.put("type", "Enduro bikes"); 42 | map.put("model", "Deimos"); 43 | map.put("price", 4972); 44 | } 45 | 46 | @Test 47 | void setOrderTest() { 48 | RMap map = redissonClient.getMap("bike:2", StringCodec.INSTANCE); 49 | if (map.isExists()) { 50 | map.forEach((k,v) -> log.debug("{} / {}", k, v)); 51 | } 52 | } 53 | @Test 54 | void getOrderTest() { 55 | // 模拟order ID 56 | var orderId = ulid.nextULID(); 57 | // 创建Order类型+ID的redis键 58 | var key = Order.PRE_KEY + orderId; 59 | // Kryo5序列化 60 | RBucket bucket = redissonClient.getBucket(key); 61 | bucket.set(Order.builder().id(orderId).userId("1").itemId("1").build()); 62 | // 映射的新对象 63 | RBucket bucketN = redissonClient.getBucket(key); 64 | // 反序列化为Java对象。因具体化了泛型类型,支持类型检测 65 | Order order = bucketN.get(); 66 | log.debug("{}", order); 67 | } 68 | 69 | @Test 70 | void getOrderJsonTest() { 71 | // 模拟order ID 72 | var orderId = ulid.nextULID(); 73 | // 创建Order类型+ID的redis键 74 | var key = Order.PRE_KEY + orderId; 75 | // 无类型标注JSON序列化 76 | RBucket bucket = redissonClient.getBucket(key, new TypedJsonJacksonCodec(Order.class)); 77 | bucket.set(Order.builder().id(orderId).userId("1").itemId("1").build()); 78 | // 映射的新对象 79 | RBucket bucketN = redissonClient.getBucket(key,new TypedJsonJacksonCodec(OrderX.class)); 80 | // 反序列化为Java对象。因具体化了泛型类型,支持类型检测 81 | OrderX order = bucketN.get(); 82 | log.debug("{}", order); 83 | } 84 | private record OrderX(String id, String userId, String itemId){} 85 | } 86 | -------------------------------------------------------------------------------- /experiments.md: -------------------------------------------------------------------------------- 1 | # Experiments 2 | ### 实验5 持久化实验 3 | **实验目的** 4 | 掌握基于idea的基本springboot工程项目的创建方法 5 | 理解maven项目结构,依赖配置声明管理 6 | 掌握DO类与数据表的映射规则 7 | 掌握基本的属性映射规则 8 | 理解雪花算法主键生成策略与特点 9 | 掌握基于spring-data-jdbc框架的基本CURD操作 10 | 11 | **实验内容** 12 | 创建springboot项目,添加spring-data-jdbc框架相关依赖 13 | 创建springboot项目配置文件,添加数据源/logging等相关配置 14 | 创建数据表生成脚本,编写user表结构 15 | 打开idea database视图,添加连接远程数据库 16 | 执行脚本生成测试数据表 17 | 编写user表对应的DO类,按映射规则声明属性 18 | 编写操作DO类的Repository组件 19 | 编写测试类,注入Repository组件,执行增删改等持久化方法 20 | 测试事务 21 | 22 | 23 | ### 实验6 关联查询映射实验 24 | **实验目的** 25 | 理解仅索引非外键关系的作用 26 | 掌握编写关联查询语句的方法 27 | 掌握关联查询的声明使用方法 28 | 掌握基于RowMapper/ResultSetExtractor接口的结果集映射方法 29 | 30 | **实验内容** 31 | 结合上一实验内容 32 | 在schema脚本添加user/address,声明若干字段,并添加至数据库 33 | 在address包含user表主键并设置为索引;user one-to-many address,address one-to-one user 34 | 编写repository接口 35 | 模拟1个user,对应2个address 36 | 按需创建不同方式封装user/address中信息的DTO类 37 | 实现以下查询 38 | 39 | - 基于userid,查询全部address信息,通过address repository查询即可 40 | - 基于addressid,查询address信息以及user信息,需DTO 41 | - 基于userid,查询user信息,以及全部address信息,需DTO 42 | 43 | 编写测试类测试 44 | 45 | ### 实验7 SpringMVC实验 46 | **实验目的** 47 | 理解并掌握REST API的设计及实现方法 48 | 理解并掌握controller组件的声明方法 49 | 理解并掌握HTTP GET/POST/PATCH请求的处理 50 | 理解并掌握JSON数据结构 51 | 理解并掌握基本基于Jackson的数据响应 52 | 理解并掌握基于json请求数据的封装 53 | 理解并掌握基于idea的HTTP REST API模拟请求脚本 54 | 55 | **实验内容** 56 | 创建一个支持SpringMVC的springboot项目 57 | 添加log/jackson忽略空属性等基本配置 58 | 59 | 需求0 60 | 创建ResultVO类,统一处理响应数据 61 | 在controller下,创建UserController控制组件,添加REST注解,声明/api根路径 62 | 创建处理/index,请求方法,返回ResultVO对象,封装普通字符串即可 63 | 64 | 需求+1 65 | 在dto下,创建User类,添加用户名/密码,为密码添加序列化忽略注解,当序列化user对象时,将忽略密码属性 66 | ```shell 67 | @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) 68 | ``` 69 | 在控制组件中模拟一个users集合,模拟封装若干user对象 70 | 创建处理/users,get请求方法,封装users集合 71 | 创建处理/users/{uid},get请求方法,获取请求地址user ID参数,基于参数从users集合中获取对象,封装到返回。注意Map.of()不能添加null,因此需处理如果集合中没有指定ID对象的实现 72 | 创建处理/users,post请求方法,将请求数据封装到User对象,作为参数注入方法,将对象添加到users集合 73 | 74 | 在test下,创建http文件夹,创建user.http测试脚本测试请求 75 | 76 | ### 实验8 SpringMVC拦截器实验 77 | **实验目的** 78 | 理解拦截器的作用与意义 79 | 掌握拦截器的声明配置方法 80 | 掌握拦截器过滤指定请求的配置方法 81 | 掌握拦截器的实现方法 82 | 了解拦截器回调方法的区别和使用场景 83 | 掌握在拦截器中请求权限验证 84 | 85 | **实验内容** 86 | 创建一个支持SpringMVC的springboot项目 87 | 88 | 需求0 89 | 添加Spring Security框架中的加密解密依赖。注意:不是在构建工程时添加Spring Security整体框架,仅需其中的crypto依赖 90 | 在application中添加log/jackson忽略空属性配置,以及自定义加密密钥与盐值 91 | 92 | 在component下,创建整合Jackson的加密/解密组件EncryptorComponent,注入密钥与盐值,实现对给定Map对象的加密/解密,对无法解密异常,转抛为自定义异常 93 | 创建SecurityConfiguration配置类,创建基于BCryptPasswordEncoder算法的PasswordEncoder对象,注入spring容器 94 | 在dto下创建User类,添加userName/password属性,getter/setter注解等;添加password的序列化忽略注解 95 | 在controller下,创建LoginController组件,注入密码编码组件,注入加密/解密组件。组件内创建一个Map,模拟基于用户名保存用户对象 96 | 创建处理/register post请求方法,将注册用户密码编码,将user对象保存在Map对象中(模拟数据库) 97 | 创建处理/login post请求方法,判断登录用户是否存在,用户密码是否正确;并将用户名加密保存在响应header中 98 | 在interceptor下,创建LoginInterceptor拦截器,从请求header中获取token数据,解密并将解密出的用户名置于requestattribute中 99 | 100 | 创建实现WebMvcConfigurer接口的配置实现类WebMvcConfiguration,重写addInterceptors()方法,注册拦截器,设置拦截规则过滤 101 | 102 | 在LoginController组件中,创建处理/index get请求,在方法中注入requestattribute中的用户名,并将用户名返回 103 | 104 | 测试 105 | 在test下,创建http目录,创建login.http测试脚本 106 | 编写注册测试脚本 107 | 编写登录测试脚本测试,在响应中获取token数据 108 | 编写/index请求测试脚本,在header中携带token发起请求 109 | -------------------------------------------------------------------------------- /jdbc-examples/src/main/java/org/example/jdbcexamples/repository/GithubUserRepository.java: -------------------------------------------------------------------------------- 1 | package org.example.jdbcexamples.repository; 2 | 3 | import org.example.jdbcexamples.dox.GithubUser; 4 | import org.example.jdbcexamples.dto.GithubOptionType; 5 | import org.springframework.data.repository.CrudRepository; 6 | import org.springframework.jdbc.core.BeanPropertyRowMapper; 7 | import org.springframework.jdbc.core.JdbcTemplate; 8 | import org.springframework.jdbc.core.RowMapper; 9 | import org.springframework.stereotype.Repository; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | @Repository 15 | public interface GithubUserRepository extends CrudRepository { 16 | // 注入原生JdbcTemplate组件手动实现复杂查询 17 | // 组件为接口,无法通过构造函数注入其他组件 18 | default List findByDynamic(JdbcTemplate jdbcTemplate, GithubUser user) { 19 | // `1=1` 是为了在参数全部为空时,`where`子句仍然成立 20 | var sql = "select * from github_user t1 where 1=1"; 21 | List args = new ArrayList<>(); 22 | if (user.getFollowers() != null) { 23 | sql += " and t1.followers >= ?"; 24 | args.add(user.getFollowers()); 25 | } 26 | if (user.getStars() != null) { 27 | sql += " and t1.stars >= ?"; 28 | args.add(user.getStars()); 29 | } 30 | if (user.getGender() != null) { 31 | sql += " and t1.gender = ?"; 32 | args.add(user.getGender()); 33 | } 34 | // `queryForList()`方法返回的是单列字段 35 | // RowMapper,手动映射 36 | // 返回单值,可以用queryObject()方法 37 | return jdbcTemplate.query(sql, mapper, args.toArray()); 38 | } 39 | default List findByDynamic(JdbcTemplate jdbcTemplate, List options) { 40 | var sql = new StringBuilder("select * from github_user t1 where 1=1"); 41 | List args = new ArrayList<>(); 42 | for (GithubOptionType option : options) { 43 | if (option.propName().equals("followers")) { 44 | String operator = option.operator(); 45 | Object value = option.value(); 46 | sql.append(" and t1.followers %s ?".formatted(operator)); 47 | args.add(value); 48 | } 49 | if (option.propName().equals("stars")) { 50 | String operator = option.operator(); 51 | Object value = option.value(); 52 | sql.append(" and t1.stars %s ?".formatted(operator)); 53 | args.add(value); 54 | } 55 | if (option.propName().equals("gender")) { 56 | String operator = option.operator(); 57 | Object value = option.value(); 58 | sql.append(" and t1.gender %s ?".formatted(operator)); 59 | args.add(value); 60 | } 61 | } 62 | return jdbcTemplate.query(sql.toString(), mapper, args.toArray()); 63 | } 64 | // 避免每次创建mapper对象 65 | RowMapper mapper = (rs, rowNum) -> GithubUser.builder() 66 | .id(rs.getString("id")) 67 | .name(rs.getString("name")) 68 | .stars(rs.getInt("stars")) 69 | .followers(rs.getInt("followers")) 70 | .gender(rs.getString("gender")) 71 | .repos(rs.getInt("repos")) 72 | .build(); 73 | // 当字段与属性名称全部相同时,可使用spring提供的封装工具简化开发 74 | RowMapper m = BeanPropertyRowMapper.newInstance(GithubUser.class); 75 | 76 | } 77 | -------------------------------------------------------------------------------- /springmvc-examples/src/main/java/org/example/springmvcexamples/example01/ExampleController01.java: -------------------------------------------------------------------------------- 1 | package org.example.springmvcexamples.example01; 2 | 3 | import org.example.springmvcexamples.example01.dox.Address; 4 | import org.example.springmvcexamples.example01.dox.User; 5 | import org.example.springmvcexamples.vo.ResultVO; 6 | import jakarta.servlet.http.HttpServletRequest; 7 | import jakarta.servlet.http.HttpServletResponse; 8 | import lombok.RequiredArgsConstructor; 9 | import lombok.extern.slf4j.Slf4j; 10 | import org.springframework.http.HttpHeaders; 11 | import org.springframework.web.bind.annotation.*; 12 | 13 | import java.time.LocalDateTime; 14 | import java.util.List; 15 | import java.util.Map; 16 | import java.util.Optional; 17 | 18 | @Slf4j 19 | @RestController 20 | @RequiredArgsConstructor 21 | @RequestMapping("/api/example01/") 22 | public class ExampleController01 { 23 | @GetMapping("index") 24 | public ResultVO getIndex() { 25 | return ResultVO.success(Map.of("name", "SUN")); 26 | } 27 | 28 | @GetMapping("addresses") 29 | public ResultVO getAddresses() { 30 | return ResultVO.success(ADDRESSES); 31 | } 32 | 33 | @GetMapping("user/addresses") 34 | public ResultVO getUserAddresses() { 35 | User u = User.builder().id("32").name("BO").build(); 36 | Map data = Map.of("user", u, "addresses", ADDRESSES); 37 | return ResultVO.success(data); 38 | } 39 | 40 | @PostMapping("addresses") 41 | public ResultVO postAddress(@RequestBody Address address) { 42 | log.debug(address.getDetail()); 43 | log.debug(address.getComment()); 44 | return ResultVO.ok(); 45 | } 46 | 47 | @PostMapping("addresses02") 48 | public ResultVO postAddress2(@RequestBody Address address) { 49 | log.debug(address.getDetail()); 50 | log.debug(address.getComment()); 51 | log.debug("{}", address.getUser().getId()); 52 | return ResultVO.ok(); 53 | } 54 | 55 | @GetMapping("addresses/{aid}") 56 | public ResultVO getAddress(@PathVariable String aid) { 57 | Address address = ADDRESSES.stream() 58 | .filter(a -> a.getId().equals(aid)) 59 | .findFirst() 60 | .orElse(null); 61 | return ResultVO.success(address); 62 | } 63 | 64 | // 无参的默认请求为第一页 65 | // 因为路径与以上冲突,单独命名 66 | @GetMapping({"addresses-pages", "addresses-pages/{number}"}) 67 | public ResultVO getAddressesPage(@PathVariable Optional number) { 68 | // 如果不存在,默认为第一页 69 | int pateNumber = number.orElse(1); 70 | log.debug("page number: {}", pateNumber); 71 | // 可基于RequestPage封装分页信息 72 | return ResultVO.success(ADDRESSES); 73 | } 74 | 75 | @GetMapping("inject") 76 | public void inject(HttpServletRequest request, 77 | HttpServletResponse response, 78 | @RequestHeader HttpHeaders headers) { 79 | log.debug(request.getRequestURI()); 80 | log.debug(String.valueOf(headers)); 81 | } 82 | 83 | private final List
ADDRESSES = create(); 84 | 85 | private List
create() { 86 | Address a1 = Address.builder() 87 | .id("1").detail("956").inertTime(LocalDateTime.now()) 88 | .build(); 89 | Address a2 = Address.builder() 90 | .id("2").detail("925").inertTime(LocalDateTime.now()) 91 | .build(); 92 | Address a3 = Address.builder() 93 | .id("3").detail("121").inertTime(LocalDateTime.now()) 94 | .build(); 95 | return List.of(a1, a2, a3); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /springsecurity-examples/src/main/java/org/example/springsecurityexamples/filter/JwtFilter.java: -------------------------------------------------------------------------------- 1 | package org.example.springsecurityexamples.filter; 2 | 3 | import com.auth0.jwt.exceptions.JWTDecodeException; 4 | import com.auth0.jwt.exceptions.SignatureVerificationException; 5 | import com.auth0.jwt.exceptions.TokenExpiredException; 6 | import com.auth0.jwt.interfaces.DecodedJWT; 7 | import jakarta.servlet.FilterChain; 8 | import jakarta.servlet.ServletException; 9 | import jakarta.servlet.http.HttpServletRequest; 10 | import jakarta.servlet.http.HttpServletResponse; 11 | import lombok.RequiredArgsConstructor; 12 | import lombok.extern.slf4j.Slf4j; 13 | import org.example.springsecurityexamples.exception.Code; 14 | import org.example.springsecurityexamples.exception.XException; 15 | import org.example.springsecurityexamples.security.JWTComponent; 16 | import org.example.springsecurityexamples.security.Tokens; 17 | import org.example.springsecurityexamples.security.UserDetails; 18 | import org.springframework.http.server.PathContainer; 19 | import org.springframework.security.core.authority.SimpleGrantedAuthority; 20 | import org.springframework.security.core.context.SecurityContextHolder; 21 | import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; 22 | import org.springframework.stereotype.Component; 23 | import org.springframework.web.filter.OncePerRequestFilter; 24 | import org.springframework.web.util.pattern.PathPattern; 25 | import org.springframework.web.util.pattern.PathPatternParser; 26 | 27 | import java.io.IOException; 28 | 29 | @Slf4j 30 | @Component 31 | @RequiredArgsConstructor 32 | public class JwtFilter extends OncePerRequestFilter { 33 | private final JWTComponent jwtComponent; 34 | 35 | private final PathPattern exclude = new PathPatternParser() 36 | .parse("/api/open/**"); 37 | // 排除指定路径请求 38 | @Override 39 | protected boolean shouldNotFilter(HttpServletRequest request) { 40 | return exclude.matches(PathContainer.parsePath(request.getServletPath())); 41 | } 42 | @Override 43 | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { 44 | var token = request.getHeader(Tokens.TOKEN); 45 | if(token == null){ 46 | // 也可不抛异常,直接返回response 47 | throw XException.builder().code(Code.UNAUTHORIZED).build(); 48 | } 49 | // 50 | DecodedJWT decode; 51 | try { 52 | decode = jwtComponent.decode(token); 53 | } catch (TokenExpiredException | SignatureVerificationException | JWTDecodeException e) { 54 | // 也可不抛异常,直接返回response 55 | throw XException.builder().code(Code.TOKEN_ERROR).build(); 56 | } 57 | var uid = decode.getClaim(Tokens.UID).asLong(); 58 | var depid = decode.getClaim(Tokens.DEPID).asLong(); 59 | // 取出authorities,转字符串集合 60 | var authorities = decode.getClaim(Tokens.AUTHORITIES).asList(String.class); 61 | // 转GrantedAuthority集合 62 | var anAuthorities = authorities.stream() 63 | .map(SimpleGrantedAuthority::new) 64 | .toList(); 65 | var userDetails = UserDetails.builder() 66 | .uid(uid) 67 | .depId(depid) 68 | .build(); 69 | // 创建Authentication认证对象,保存用户信息与权限 70 | var authentication = 71 | new PreAuthenticatedAuthenticationToken(userDetails, null, anAuthorities); 72 | // 将Authentication保存在SecurityContext 73 | SecurityContextHolder.getContext().setAuthentication(authentication); 74 | filterChain.doFilter(request, response); 75 | } 76 | } 77 | --------------------------------------------------------------------------------