SpringBoot 常用功能和封装代码小计
一. Spring框架
通过静态方式获取上下文对象(ApplicationContext)
创建一个名为
ApplicationContextProvider
的类,实现ApplicationContextAware
接口。实现这个接口后,
Spring
会在ApplicationContext
初始化时调用setApplicationContext
方法,在这个方法中将ApplicationContext
赋值给静态变量context
。然后即可在任何地方通过
ApplicationContextProvider
类的静态方法getApplicationContext()
来获取ApplicationContext
对象
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class ApplicationContextProvider implements ApplicationContextAware {
private static ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
context = applicationContext;
}
public static ApplicationContext getApplicationContext() {
return context;
}
}
获取示例:
ApplicationContext context = ApplicationContextProvider.getApplicationContext();
通过静态方式获取请求对象(HttpServletRequest)
SpringBoot 项目中, 可以在非
Controller
类中使用RequestContextHolder
来获取当前请求的HttpServletRequest
对象。但是请注意,这种方式不是推荐的做法,因为它会使代码与Spring框架耦合度较高。
示例
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
public class MyService {
public void doSomething() {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// 使用request对象获取请求信息
String userAgent = request.getHeader("User-Agent");
System.out.println("User-Agent: " + userAgent);
}
}
SpringBoot 项目打包为可执行 Jar 包
添加POM依赖
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <!-- 版本号使用默认版本号, 或者根据实际情况调整 --> <!-- <version>2.5.2</version> --> <configuration> <executable>true</executable> </configuration> </plugin> </plugins> </build>
执行打包命令
mvn clean package
测试是否正常运行
java -jar app.jar
SpringBoot 项目加载外部配置文件
通过命令行参数指定配置文件路径
在启动应用程序时,可以使用
--spring.config.location
参数来指定配置文件的路径,例如:java -jar app.jar --spring.config.location=file:/custom.properties
通过配置文件配置
在
application.properties
或application.yml
中指定配置文件路径:在
application.properties
文件中添加如下配置:spring.config.location=file:/custom.properties
或者在
application.yml
文件中添加如下配置:spring: config: location: file:/custom.properties
二. SpringMVC
全局统一响应对象封装
创建
Result<T>
类import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; /** * 全局统一响应对象 * @author TheEnd */ @Data @Accessors(chain = true) @NoArgsConstructor @AllArgsConstructor public class Result<T> implements Serializable { /** * 响应码 */ private Integer code; /** * 消息 */ private String msg; /** * 数据 */ private T data; public Result(Integer code) { this.code = code; } public Result(String msg) { this.msg = msg; } public Result(Integer code, String msg) { this.code = code; this.msg = msg; } public Result(T data) { this.data = data; } public static Result<String> success() { return new Result<>(Code.SUCCESS); } public static <T> Result<T> success(T t) { return new Result<>(Code.SUCCESS, "", t); } public static Result<String> fail() { return new Result<>(Code.FAIL, "发生异常"); } public static Result<String> fail(Integer code) { return new Result<>(code, "发生异常"); } public static Result<String> fail(String msg) { return new Result<>(Code.FAIL, msg); } public static Result<String> fail(Integer code, String msg) { return new Result<>(code, msg); } public static Result<String> authFail() { return new Result<>(Code.AUTH, "权限不足"); } public static Result<String> businessFail() { return new Result<>(Code.BUSINESS, "业务异常"); } public static Result<String> remoteFail() { return new Result<>(Code.REMOTE, "业务异常"); } public static class Code { public static final Integer SUCCESS = 200; public static final Integer FAIL = 0; public static final Integer AUTH = 400; public static final Integer BUSINESS = 500; public static final Integer REMOTE = 600; } }
全局统一异常处理
创建自定义异常
默认异常
/** * 默认异常 * @author TheEnd */ public class DefaultException extends RuntimeException { public DefaultException() { } public DefaultException(String message) { super(message); } public DefaultException(String message, Throwable cause) { super(message, cause); } public DefaultException(Throwable cause) { super(cause); } public DefaultException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } }
业务异常
/** * 业务异常 * @author TheEnd */ public class BusinessException extends RuntimeException { public BusinessException() { } public BusinessException(String message) { super(message); } public BusinessException(String message, Throwable cause) { super(message, cause); } public BusinessException(Throwable cause) { super(cause); } public BusinessException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } }
远程服务异常
/** * 远程服务异常 * @author TheEnd */ public class RemoteServiceException extends RuntimeException { public RemoteServiceException() { } public RemoteServiceException(String message) { super(message); } public RemoteServiceException(String message, Throwable cause) { super(message, cause); } public RemoteServiceException(Throwable cause) { super(cause); } public RemoteServiceException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } }
配置全局异常处理器
创建
GlobalExceptionHandler
类import lombok.extern.slf4j.Slf4j; import org.example.blog.common.Result; import org.example.blog.exception.BusinessException; import org.example.blog.exception.DefaultException; import org.example.blog.exception.RemoteServiceException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; /** * 全局异常处理器 * @author Lenovo */ @Slf4j @RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(RemoteServiceException.class) public Result<String> handleException(RemoteServiceException e) { log.error("远程服务发生异常", e); return Result.remoteFail().setMsg(e.getMessage()); } @ExceptionHandler(BusinessException.class) public Result<String> handleException(BusinessException e) { log.error("发生业务异常", e); return Result.businessFail().setMsg(e.getMessage()); } @ExceptionHandler(DefaultException.class) public Result<String> handleException(DefaultException e) { log.error("发生异常", e); return Result.fail(e.getMessage()); } @ExceptionHandler(Exception.class) public Result<String> handleException(Exception e) { log.error("发生异常", e); return Result.fail(e.getMessage()); } }
三. Spring-Data-Jdbc
SpringBoot 项目配置动态数据源
通过AbstractRoutingDataSource
实现动态数据源
创建数据源类型枚举
根据项目数据源实际情况修改
/** * 数据源类型枚举 * @author TheEnd */ public enum DatasourceType { /** * 主库 */ PRIMARY, /** * 三方库 */ SECONDARY }
创建
DynamicDataSourceContextHolder
对象用于切换数据源
/** * 动态数据源切换器 * @author TheEnd */ public class DynamicDataSourceContextHolder { private static final ThreadLocal<DatasourceType> CONTEXT_HOLDER = new ThreadLocal<>(); /** * 设置当前线程使用的数据源类型 * @param dataSourceType 数据源类型 */ public static void setDataSourceType(DatasourceType dataSourceType) { CONTEXT_HOLDER.set(dataSourceType); } /** * 获取当前线程使用的数据源类型 * @return 数据源类型 */ public static DatasourceType getDataSourceType() { return CONTEXT_HOLDER.get(); } /** * 清除当前线程使用的数据源类型 */ public static void clearDataSourceType() { CONTEXT_HOLDER.remove(); } }
创建
DynamicDataSource
对象动态数据源对象, 继承至
AbstractRoutingDataSource
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; /** * 动态数据源对象 * @author TheEnd */ public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { // 返回当前线程的数据源类型 return DynamicDataSourceContextHolder.getDataSourceType(); } }
创建
DataSourceConfig
对象在SpringBoot中配置动态数据源
import org.example.datasource.DatasourceType; import org.example.datasource.DynamicDataSource; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.jdbc.core.JdbcTemplate; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; /** * 动态数据源配置 * @author TheEnd */ @Configuration public class DataSourceConfig { /** * 主数据源 * @return 主数据源对象 */ @Bean @ConfigurationProperties(prefix = "datasource.primary") public DataSource primaryDataSource() { return DataSourceBuilder.create().build(); } /** * 三方数据源 * @return 三方数据源对象 */ @Bean @ConfigurationProperties(prefix = "datasource.secondary") public DataSource secondaryDataSource() { return DataSourceBuilder.create().build(); } /** * 动态数据源 * @param primaryDataSource 主数据源 * @param secondaryDataSource 三方数据源 * @return 动态数据源对象 */ @Bean @Primary public DynamicDataSource dataSource(DataSource primaryDataSource, DataSource secondaryDataSource) { // 将数据源封装为 Map Map<Object, Object> targetDataSources = new HashMap<>(4); targetDataSources.put(DatasourceType.PRIMARY, primaryDataSource); targetDataSources.put(DatasourceType.SECONDARY, secondaryDataSource); // 配置动态数据源对象 DynamicDataSource dataSource = new DynamicDataSource(); dataSource.setTargetDataSources(targetDataSources); // 设置默认数据源为主数据源 dataSource.setDefaultTargetDataSource(primaryDataSource); return dataSource; } @Bean public JdbcTemplate jdbcTemplate(DynamicDataSource dataSource) { return new JdbcTemplate(dataSource); } }
数据源切换示例
使用
DynamicDataSourceContextHolder
切换数据源import org.example.datasource.DatasourceType; import org.example.datasource.DynamicDataSourceContextHolder; /** * @author TheEnd */ public class DatasourceService { public void datasourceChange() { // 设置使用主数据源 DynamicDataSourceContextHolder.setDataSourceType(DatasourceType.PRIMARY); // 设置使用三方数据源 DynamicDataSourceContextHolder.setDataSourceType(DatasourceType.SECONDARY); // 清除数据源配置 DynamicDataSourceContextHolder.clearDataSourceType(); } }
如果发生循环依赖异常, 取消SpringBoot
中数据源自动配置即可
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class);
基于AOP实现通过注解自动切换数据源
创建自定义注解
DataSourceSwitch
注解可以标注在类上或方法上。
在方法上标注注解后, 在方法执行前自动切换数据源,并在方法执行后还原数据源。
同时,也可以在类级别上使用该注解,使类中的所有方法都具有相同的切换数据源行为。
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @author TheEnd */ @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface DataSourceSwitch { DatasourceType value(); }
创建切面类
DataSourceSwitchAspect
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.reflect.MethodSignature; import org.example.datasource.DatasourceType; import org.example.datasource.DynamicDataSourceContextHolder; import org.springframework.stereotype.Component; import java.lang.reflect.Method; /** * @author Lenovo */ @Aspect @Component public class DataSourceSwitchAspect { private final ThreadLocal<DatasourceType> previousDataSourceType = new ThreadLocal<>(); @Before("@annotation(dataSourceSwitch) || @within(dataSourceSwitch)") public void switchDataSource(JoinPoint joinPoint, DataSourceSwitch dataSourceSwitch) { MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); Method method = methodSignature.getMethod(); DataSourceSwitch methodAnnotation = method.getAnnotation(DataSourceSwitch.class); if (methodAnnotation != null) { DatasourceType dataSourceType = methodAnnotation.value(); previousDataSourceType.set(DynamicDataSourceContextHolder.getDataSourceType()); DynamicDataSourceContextHolder.setDataSourceType(dataSourceType); } else { DatasourceType classDataSourceType = dataSourceSwitch.value(); previousDataSourceType.set(DynamicDataSourceContextHolder.getDataSourceType()); DynamicDataSourceContextHolder.setDataSourceType(classDataSourceType); } } @After("@annotation(dataSourceSwitch) || @within(dataSourceSwitch)") public void restoreDataSource(JoinPoint joinPoint, DataSourceSwitch dataSourceSwitch) { DynamicDataSourceContextHolder.setDataSourceType(previousDataSourceType.get()); previousDataSourceType.remove(); } }
使用注解
DataSourceSwitch
实现自动切换数据源import org.example.datasource.DatasourceType; import org.example.datasource.DynamicDataSourceContextHolder; import org.example.ds.DataSourceSwitch; import org.springframework.stereotype.Service; /** * @author Lenovo */ @Service @DataSourceSwitch(DatasourceType.SECONDARY) public class DsService { @DataSourceSwitch(DatasourceType.PRIMARY) public DatasourceType test() { return DynamicDataSourceContextHolder.getDataSourceType(); } @DataSourceSwitch(DatasourceType.SECONDARY) public DatasourceType test2() { return DynamicDataSourceContextHolder.getDataSourceType(); } public DatasourceType test3() { return DynamicDataSourceContextHolder.getDataSourceType(); } }
评论已关闭