SpringBoot

springboot启动

正常启动
@SpringBootApplication
public class Application {
  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }
}

启动时输出pid文件
@SpringBootApplication
public class Application {
  public static void main(String[] args) {
    SpringApplication application = new SpringApplication(Application.class);
    application.addListeners(new ApplicationPidFileWriter());
    ConfigurableApplicationContext applicationContext = application.run(args);
    applicationContext.registerShutdownHook();
  }
}
指定项目访问名称
项目访问路径
server.servlet.context-path = itemAccessName

springboot启动指定参数

--server.port=8081                                           启动指定端口号
--spring.config.location=/opt/application.properties         启动指定配置文件

启动时指定日志路径:
-Domp.logging.path=logs
${sys:omp.logging.path:-./log/}
添加指定系统参数
System.setProperty("", "");

springboot配置文件

spring.pid.file=application.pid                              默认pid文件名称

spring.application.name=itemName                             启动项目名称

#eureka上显示IP地址配置下面两项
eureka.instance.prefer-ip-address = true
eureka.instance.preferIpAddress = true
#eureka注册增加实例名称
eureka.instance.instance-id = ${spring.cloud.client.ip-address}:${server.port}

#springboot配置日志文件位置
logging.config = config/logback-spring.xml

springboot加载资源文件

InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("a.txt");
IOUtils.toString(inputStream);

springboot配置文件加载顺序

application.properties                                      配置文件加载顺序
file:./config/
file:./
classpath:/config/
classpath:/
加载优先级由高到低,高优先级的配置会覆盖低优先级的配置

sprinboot测试用例

@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
@Transactional
@AutoConfigureMockMvc

出现下面错误时查询是否是导入的org.junit.Test
org.junit.runners.model.InvalidTestClassError: Invalid test class

AOP

@EnableAspectJAutoProxy
@Aspect @Pointcut @Before @After @Around @AfterThrowing @AfterReturning

@EnableTransactionManagement
InfrastructureAdvisorAutoProxyCreator

@EnableAspectJAutoProxy
AnnotationAwareAspectJAutoProxyCreator

CglibAopProxy DynamicAdvisedInterceptor  intercept

ReflectiveMethodInvocation.proceed()

ExposeInvocationInterceptor.invoke()

异常返回通知
AspectJAfterThrowingAdvice.invoke()

正常返回通知
AfterReturningAdviceInterceptor.invoke()

后置通知
AspectJAfterAdvice.invoke()

环绕通知
AspectJAroundAdvice.invoke()

前置通知
MethodBeforeAdviceInterceptor.invoke()


正常过程
@Arount:执行目标方法之前
@Before-----除法运行之前
----div----
@Arount:执行目标方法之后
@After-----除法结束
@AfterReturning-----除法正常返回

异常过程
@Arount:执行目标方法之前
@Before-----除法运行之前
----div----
@After-----除法结束
@AfterThrowing-----运行异常

// 切入点
execution(* * cn.com.spring.service.impl..*.*(..))
1)* 所有的修饰符(如public)
2)* 所有的返回类型(如void)
3).. 所有的包及其子包下的文件
4)* 所有的类名
5)* 所有的方法名
6)* ..所有的参数名

配置文件参数自动转List类型

参数自动转为List
@Value("#{'${csp.security:menuList,portalList}'.split(',')}")
private List<String> verifyTokenUrlList;

maven jar包

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.7.13</version>
</parent>

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

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

打包插件

org.springframework.boot           mvn打包springboot项目jar或war
spring-boot-maven-plugin

org.apache.maven.plugins           测试用例插件(默认已有)
maven-surefire-plugin

org.apache.maven.plugins           资源插件(处理资源文件)
maven-resources-plugin

org.apache.maven.plugins           部署插件(发布远程仓库)
maven-deploy-plugin

org.apache.maven.plugins           jar包插件(打成可运行jar包)
maven-jar-plugin

org.apache.maven.plugins           依赖插件(添加额外jar包)
maven-dependency-plugin

pl.project13.maven             git插件(获取git版本信息)
git-commit-id-plugin

maven-assembly-plugin            装配插件(支持定制化打包)

@InitBinder日期格式化

@InitBinder
protected void initBinder(WebDataBinder binder) {
    binder.registerCustomEditor(Date.class, new PropertyEditorSupport() {
        @Override
        public void setAsText(String text) throws IllegalArgumentException {
            Date date = null;
            try {
                date = new SimpleDateFormat("yyyy-MM-ddHH:mm:ss").parse(text);
            } catch (ParseException e) {
            }
            if (date == null) {
                try {
                    date = new SimpleDateFormat("yyyy-MM-dd").parse(text);
                } catch (ParseException e1) {
                }
            }
            setValue(date);
        }
    });
}

前后端交互日期格式化

前台传后台时间格式化(form表单形式)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date date;

前台传后台时间格式化(json形式)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTime;

后台传前台时间格式化
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTime;

手动控制事务

DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(def);
try {
   // TODO 保存数据
   dataSourceTransactionManager.commit(transactionStatus);
} catch (Exception e) {
   log.error("", e);
   dataSourceTransactionManager.rollback(transactionStatus);
   throw new RuntimeException(e);
}

Validation参数验证使用时调用

Validation.buildDefaultValidatorFactory().getValidator().validate(obj).forEach((s)->{
   throw new RuntimeException("["+s.getPropertyPath()+s.getMessage()+"]");
});

newDecimalFormat数字科学计数法显示

newDecimalFormat("#,###").format(123456789)) -> 123,456,789

Springboot项目无法加载配置文件时添加插件

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <configuration>
        <excludes>
            <exclude>logback-spring.xml</exclude>
            <exclude>application.properties</exclude>
        </excludes>
    </configuration>
</plugin>

修复跨站脚本漏洞

org.springframework.web.util.HtmlUtils.htmlEscape(str);

spring boot关闭actuator路径

关闭actuator所有端点
management.endpoints.enabled-by-default=false

禁用监控HTTP端点,因为http端口的范围是:165535,因此-1是访问不了的,此时访问/actuator路径也是404了
management.server.port = -1

actuator默认开启了health、info端点,可以单独关闭
management.endpoint.info.enabled=false
management.endpoint.health.enabled=false

单独开启服务端点,如开启info、health
management.endpoint.info.enabled=true
management.endpoint.health.enabled=true

批量暴露端点,例如暴露mapping、metrics端点
management.endpoints.web.exposure.include=mapping,metric

暴露所有端点
management.endpoints.web.exposure.include=*

日志

<dependency>
 <groupId>org.projectlombok</groupId>
 <artifactId>lombok</artifactId>
 <version>1.18.20</version>
</dependency>

@Slf4j
或者Logger log = LoggerFactory.getLogger(A.class);

异常统一处理

@ControllerAdvice
@ExceptionHandler({Exception.class})
@ResponseBody
public void methodException(HttpServletRequest request, Exception ex, HttpServletResponse response) throws IOException {
 response.setStatus(HttpStatus.SC_NOT_ACCEPTABLE);
 response.setContentType("application/json;charset=UTF-8");
    //response.setContentType("text/plain;charset=UTF-8");
 response.getWriter().write(JSON.toJSONString(new JSONObject().fluentPut("code", "111").fluentPut("msg", ex.getMessage())));
}

url匹配判断

PathMatcher antPathMatcher = new AntPathMatcher();
antPathMatcher.match("/url/**", req.getServletPath());

url请求强匹配

@Configuration
public class WebConfig extends WebMvcConfigurationSupport {
 @Override
 public void configurePathMatch(PathMatchConfigurer configurer) {
  configurer.setUseSuffixPatternMatch(false).setUseTrailingSlashMatch(true);
 }
}

post请求时,验证传递的参数名是否必须在实体类中存在

@Configuration
public class WebConfig extends WebMvcConfigurationSupport {
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().build();
        // 设置false时忽略不存在的属性,设置true时验证属性必须存在
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        converters.add(new MappingJackson2HttpMessageConverter(objectMapper));
    }
}

自动时间转换

在实体类的Date类型字段上添加此注解,在入参、及转成json格式时会转成字符串
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")

spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8

Valid参数验证实例

@Valid  支持嵌套,可以验证类中类属性
@Validated 不支持嵌套,仅可以验证本类属性

常用的用法
@NotNull
@NotEmpty
@Pattern(regexp = "^[0-9]?$")

嵌套实例
@Data
public class A{
    @Valid
    @NotEmpty
    @NotNull
    private List<B> list;

    @Data
    public static class B{
        @NotNull
        private Long objectId;
    }
}

获取request、respone

//获取请求体 request
HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
//获取响应体 response
HttpServletResponse response = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getResponse();

http响应返回状态码

响应分为五类:信息响应(100199),成功响应(200299),重定向(300399),客户端错误(400499)和服务器错误 (500599):

日志可以使用这种方式替换变量

log.info("消息内容替换[{}]", "message");
输出为:消息内容替换[message]

过滤器、拦截器

// 过滤器优于拦截器先执行

// 拦截器示例
@Component
public class MyInterceptor extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return super.preHandle(request, response, handler);
    }
}

@Configuration
public class MyConfig extends WebMvcConfigurationSupport {
 @Autowired
 private MyInterceptor myInterceptor;

 @Override
 public void addInterceptors(InterceptorRegistry registry) {
  // addPathPatterns 添加拦截规则
  // excludePathPatterns 排除拦截
  registry.addInterceptor(myInterceptor).addPathPatterns("/**").excludePathPatterns("/error/**");
 }
}

// 过滤器示例
@WebFilter(filterName = "MyFilter", urlPatterns = "/*")
public class MyFilter implements Filter {
    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
        chain.doFilter(req, resp);
    }
}
// 过滤器生效需要在启动类上添加注解:@ServletComponentScan(basePackages = { "扫描filter的包" })

添加过滤器判断系统仅支持post请求

@WebFilter(filterName = "MyFilter", urlPatterns = "/*")
public class MyFilter implements Filter {
    private final static PathMatcher antPathMatcher = new AntPathMatcher();
    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        if (!antPathMatcher.match("/info/**", request.getServletPath())) {
            if ("GET".equals(request.getMethod())) {
                req.getRequestDispatcher("/error/handleError").forward(req, resp);
                return;
            }
        }
        chain.doFilter(req, resp);
    }
}

// 添加错误访问方法,为了跳转到全局异常过滤器
@RestController
@RequestMapping(value = "/api/error")
public class ErrorController {
    @GetMapping("handleError")
    public void handleError() {
        throw new ServiceException("Request method 'GET' not supported");
    }
}

获取处理项目的URL

// spring获取请求url地址
UrlPathHelper helper = new UrlPathHelper();
String uri = helper.getOriginatingServletPath(request);

// 获取系统所有的url
for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : requestMappingHandlerMapping.getHandlerMethods().entrySet()) {
    for (String url : entry.getKey().getPatternsCondition().getPatterns()) {
        System.out.println(url);
    }
}

// 获取url的请求匹配类方法
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : RequestMappingHandlerMapping.getHandlerMethods().entrySet()) {
    RequestMappingInfo m = entry.getKey().getMatchingCondition(request);
    Set<String> urlPatterns = m.getPatternsCondition().getPatterns();
    HandlerMethod value = entry.getValue();
}

spring自带缓存功能

@CacheEvict(allEntries=true)
@Cacheable(value="a", key="#id")

https://www.cnblogs.com/goloving/p/16040705.html

feign的调用

Object obj = Feign.builder().client(applicatonContext.gerBean(Client.class))
 .encoder(applicatonContext.gerBean(Encoder.class))
 .decoder(applicatonContext.gerBean(Decoder.class))
 .contract(new SpringMvcContract())
 .target(Object.class, "http://microServiceName/path");

springcloud高版本ribbon问题

springcloud高版本不能使用ribbon,可以直接删除ribbon,直接使用loadbalancer
https://blog.csdn.net/SirLZF/article/details/117127361

解决类上@FeignClient、@RequestMapping同时存在时的报错问题

@Bean
@Primary
public SpringMvcContract feignContract(List<AnnotatedParameterProcessor> parameterProcessors,
            ConversionService feignConversionService,
            FeignClientProperties feignClientProperties) {
 boolean decodeSlash = feignClientProperties == null || feignClientProperties.isDecodeSlash();
 return new SpringMvcContract(parameterProcessors, feignConversionService, decodeSlash) {
  @Override
  protected void processAnnotationOnClass(MethodMetadata data, Class<?> clz) {
   CollectionFormat collectionFormat = findMergedAnnotation(clz, CollectionFormat.class);
   if (collectionFormat != null) {
    data.template().collectionFormat(collectionFormat.value());
   }
  }
 };
}

测试用例注解

@Transactional
@Rollback(true)
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)

配置druid的sql监控生效的配置

spring.datasource.druid.web-stat-filter.enabled = true
spring.datasource.druid.stat-view-servlet.enabled = true
spring.datasource.druid.stat-view-servlet.allow = 127.0.0.1/0
spring.datasource.druid.filter.stat.enabled = true
spring.datasource.druid.filter.stat.log-slow-sql = true
spring.datasource.druid.filter.stat.merge-sql = true
spring.datasource.druid.filter.stat.slow-sql-millis = 3000
spring.datasource.druid.stat-view-servlet.reset-enable = false
spring.datasource.druid.stat-view-servlet.login-username = admin
spring.datasource.druid.stat-view-servlet.login-password = admin@123

定时任务设置

@Scheduled(cron = "${refresh.space:0 0 3 * * ?}")

初始化后执行方法
@PostConstruct

日期自动转换器

import org.springframework.core.convert.converter.Converter;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class DateConverter implements Converter<String, Date> {
    private static final List<String> FORMAT_LIST = new ArrayList<>();
    static {
        FORMAT_LIST.add("yyyy-MM");
        FORMAT_LIST.add("yyyy-MM-dd");
        FORMAT_LIST.add("yyyy-MM-dd HH:mm");
        FORMAT_LIST.add("yyyy-MM-dd HH:mm:ss");
    }

    @Override
    public Date convert(String source) {
        String value = source.trim();
        if ("".equals(value)) {
            return null;
        }
        if (value.matches("^\\d{4}-\\d{1,2}$")) {
            return parseDate(value, FORMAT_LIST.get(0));
        } else if (value.matches("^\\d{4}-\\d{1,2}-\\d{1,2}$")) {
            return parseDate(source, FORMAT_LIST.get(1));
        } else if (value.matches("^\\d{4}-\\d{1,2}-\\d{1,2} {1}\\d{1,2}:\\d{1,2}$")) {
            return parseDate(source, FORMAT_LIST.get(2));
        } else if (value.matches("^\\d{4}-\\d{1,2}-\\d{1,2} {1}\\d{1,2}:\\d{1,2}:\\d{1,2}$")) {
            return parseDate(value, FORMAT_LIST.get(3));
        } else {
            throw new IllegalArgumentException("Invalid boolean value '" + source + "'");
        }
    }

    private Date parseDate(String dateStr, String format) {
        Date date = null;
        try {
            DateFormat dateFormat = new SimpleDateFormat(format);
            date = dateFormat.parse(dateStr);
        } catch (Exception e) {
            log.error("格式化日期异常", e);
        }
        return date;
    }
}

SpringBoot如何解决跨域问题

跨域可以在前端通过 JSONP 来解决,但是 JSONP 只可以发送 GET 请求,
无法发送其他类型的请求,在 RESTful 风格的应用中,就显得非常鸡肋,
因此我们推荐在后端通过 (CORSCross-origin resource sharing)来解决跨域问题。
这种解决方案并非SpringBoot特有的,在传统的SSM框架中,就可以通过 CORS 来解决跨域问题,
只不过之前我们是在 XML 文件中配置 CORS ,
现在可以通过实现WebMvcConfigurer接口然后重写addCorsMappings方法解决跨域问题。
@Override
public void addCorsMappings(CorsRegistry registry) {
    registry.addMapping("/**")
            .allowedOrigins("*")
            .allowCredentials(true)
            .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
            .maxAge(3600);
}

请求参数加签名

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

@WebFilter(filterName = "GetParamFilter", urlPatterns = "/*", dispatcherTypes = DispatcherType.REQUEST)
public class GetParamFilter implements Filter {
    private static final String SIGN_NAME = "sign";
	
    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        if ("GET".equalsIgnoreCase(request.getMethod())) {
            if (!validSign(request)) {
                responseJsonMessage(resp, "错误信息提示");
                return;
            }
        }
        chain.doFilter(req, resp);
    }

    public static boolean validSign(HttpServletRequest request) {
        Map<String, String> paramMap = new HashMap<>();
        Enumeration<String> enumeration = request.getParameterNames();
        while (enumeration.hasMoreElements()) {
            String paramName = enumeration.nextElement();
            paramMap.put(paramName, request.getParameter(paramName));
        }
        paramMap.remove(SIGN_NAME);

        if (MapUtils.isEmpty(paramMap) || StringUtils.isBlank(request.getParameter(SIGN_NAME))) {
            return true;
        }
        return StringUtils.equals(request.getParameter(SIGN_NAME), cn.hutool.crypto.SecureUtil.md5("待加密的字符串"));
    }

    private void responseJsonMessage(ServletResponse resp, Object obj) throws IOException {
        resp.setContentType("application/json; charset=utf-8");
        PrintWriter writer = resp.getWriter();
        writer.print(JSONObject.toJSONString(obj, SerializerFeature.WriteMapNullValue, SerializerFeature.WriteDateUseDateFormat));
        writer.close();
        resp.flushBuffer();
    }
}

适配管理库时,需要添加下面数据库适配器

# 添加下面后,在xml中可以使用_databaseId来进行各个数据库的判断了
@Configuration
public class DatabaseAdapterConfig {
    @Bean
    public DatabaseIdProvider getDatabaseIdProvider() {
        DatabaseIdProvider databaseIdProvider = new VendorDatabaseIdProvider();
        Properties p = new Properties();
        p.setProperty("PostgreSQL", "postgres");
        p.setProperty("DM DBMS", "dm7");
        p.setProperty("Oracle", "oracle");
        p.setProperty("MySQL", "mysql");
        databaseIdProvider.setProperties(p);
        return databaseIdProvider;
    }
}

jpa低版本适配达梦

#方案一 添加兼容包加配置参数:
1、添加兼容包:DmDialect-for-hibernate
2、添加参数参数(涉及类JpaProperties,):spring.jpa.database-platform = org.hibernate.dialect.DmDialect
<dependency>
	<groupId>com.dameng</groupId>
	<artifactId>DmDialect-for-hibernate5.6</artifactId>
	<version>8.1.3.140</version>
</dependency>

#重写了类JpaBaseConfiguration的方法:jpaVendorAdapter(),并注册bean
@Configuration
public class JpaVendorAdapterConfig {
    @Value("${spring.jpa.database-platform:org.hibernate.dialect.DmDialect}")
    private String databasePlatform;
    @Value("${dm.driver.class.name:dm.jdbc.driver.DmDriver}")
    private String dmDriverClassName;
    @Value("${spring.datasource.driver-class-name}")
    private String springDatasourceDriverClassName;

    @Bean
    @Primary
    public JpaVendorAdapter jpaVendorAdapter(JpaProperties properties) {
        AbstractJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
        adapter.setShowSql(properties.isShowSql());
        if (properties.getDatabase() != null) {
            adapter.setDatabase(properties.getDatabase());
        }

        if (StringUtils.isNotBlank(properties.getDatabasePlatform())) {
            adapter.setDatabasePlatform(properties.getDatabasePlatform());
        } else if (StringUtils.equalsIgnoreCase(springDatasourceDriverClassName, dmDriverClassName)) {
            adapter.setDatabasePlatform(databasePlatform);
        }

        adapter.setGenerateDdl(properties.isGenerateDdl());
        return adapter;
    }
}

方案二 通过url添加参数让jpa使用oracle的类
url连接添加参数:?compatibleMode=oracle&comOra=true 添加1个参数即可(配置参数的作用位置DmdbDatabaseMetaData #getDatabaseProductName())
posted @   rbcd  阅读(156)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· 上周热点回顾(2.17-2.23)
点击右上角即可分享
微信分享提示