开发工具
idea打开多微服务面板(workspace.xml)
<component name="RunDashboard"> <option name="configurationTypes"> <set> <option value="SpringBootApplicationConfigurationType" /> </set> </option> <option name="ruleStates"> <list> <RuleState> <option name="name" value="ConfigurationTypeDashboardGroupingRule" /> </RuleState> <RuleState> <option name="name" value="StatusDashboardGroupingRule" /> </RuleState> </list> </option> </component>
1.github提交一个项目 (1)github添加新仓库jucdemo2 (2)添加客户端id_ras.pub秘钥(C:UsersCzz.sshid_ras.pub) (3)添加远程仓库: # 初始化本地仓库,并提交到本地仓库 $ cd D:ideawkjucdemo $ git init $ git add . $ git commit -m 'first commit' # 关联远程仓库并推送项目 $ git remote add origin git@github.com:line007/jucdemo2.git # 第一次推送 $ git push -u origin master # 非第一次推送 $ git push origin master 2.git checkout远程分支、标签 命令:git clone --branch [tags标签] [git地址] 或者 git clone --b [tags标签] [git地址] 例如:git clone -b 1.4.1 https://github.com/jumpserver/coco.git git clone -b v6.3.0 https://github.com/theme-next/hexo-theme-next themes/next6.3
idea-pom项目无法识别
File->Settings->Build,Excecution,Deployment->Build Tools->Maven->Ignored Files
查看是否存在maven pom被勾选,去掉勾选即可。
linux
1.常用命令 -- 查看异常 $ tail -n 400 /usr/local/xx/logs/app-demo/error.log -- 查看端口 $ netstat -tunlp $ ps -aux | grep 668 -- 查看前10个最大的文件 $ du -a / | sort -nr | head -n 10 $ nohup java -jar xxl-job-admin.jar & $ nohup java -jar xxl-job-executor-sample.jar & 2.nginx常用命令 $ nginx -t $ ./nginx -s reload 3.redis常用命令 -- 查询删除KEY $ key * $ get menu_details::1_menu $ del menu_details::1_menu -- 清库 // 删除当前数据库中的所有Key $ flushdb // 删除所有数据库中的key $ flushall
Spring
// 核心原理:定义WebResponseExceptionTranslator一个实现类,并将自定义异常处理类添加到认证服务器配置 @Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Autowired private WebResponseExceptionTranslator webResponseExceptionTranslator; /** 用来配置授权(authorization)以及令牌(token)的访问端点和令牌服务(token services) */ @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { ... endpoints.exceptionTranslator(webResponseExceptionTranslator); ... } ... } // 1.定义继承OAuth2Exception的异常类 @JsonSerialize(using = PigAuth2ExceptionSerializer.class) public class PigAuth2Exception extends OAuth2Exception { @Getter private String errorCode; public PigAuth2Exception(String msg) { super(msg); } public PigAuth2Exception(String msg, String errorCode) { super(msg); this.errorCode = errorCode; } } // 2.定义序列化实现类 public class PigAuth2ExceptionSerializer extends StdSerializer<PigAuth2Exception> { public PigAuth2ExceptionSerializer() { super(PigAuth2Exception.class); } @Override @SneakyThrows public void serialize(PigAuth2Exception value, JsonGenerator gen, SerializerProvider provider) { gen.writeStartObject(); gen.writeObjectField("code", CommonConstants.FAIL); gen.writeStringField("msg", value.getMessage()); gen.writeStringField("data", value.getErrorCode()); gen.writeEndObject(); } } // 3.自定义实现异常转换类 /** * @author lengleng * @date 2019/2/1 * 异常处理,重写oauth 默认实现 */ @Slf4j public class PigWebResponseExceptionTranslator implements WebResponseExceptionTranslator { private ThrowableAnalyzer throwableAnalyzer = new DefaultThrowableAnalyzer(); @Override @SneakyThrows public ResponseEntity<OAuth2Exception> translate(Exception e) { // Try to extract a SpringSecurityException from the stacktrace Throwable[] causeChain = throwableAnalyzer.determineCauseChain(e); Exception ase = (AuthenticationException) throwableAnalyzer.getFirstThrowableOfType(AuthenticationException.class, causeChain); if (ase != null) { return handleOAuth2Exception(new UnauthorizedException(e.getMessage(), e)); } ase = (AccessDeniedException) throwableAnalyzer .getFirstThrowableOfType(AccessDeniedException.class, causeChain); if (ase != null) { return handleOAuth2Exception(new ForbiddenException(ase.getMessage(), ase)); } ase = (InvalidGrantException) throwableAnalyzer .getFirstThrowableOfType(InvalidGrantException.class, causeChain); if (ase != null) { return handleOAuth2Exception(new InvalidException(ase.getMessage(), ase)); } ase = (HttpRequestMethodNotSupportedException) throwableAnalyzer .getFirstThrowableOfType(HttpRequestMethodNotSupportedException.class, causeChain); if (ase != null) { return handleOAuth2Exception(new MethodNotAllowed(ase.getMessage(), ase)); } ase = (OAuth2Exception) throwableAnalyzer.getFirstThrowableOfType( OAuth2Exception.class, causeChain); if (ase != null) { return handleOAuth2Exception((OAuth2Exception) ase); } return handleOAuth2Exception(new ServerErrorException(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase(), e)); } private ResponseEntity<OAuth2Exception> handleOAuth2Exception(OAuth2Exception e) { int status = e.getHttpErrorCode(); HttpHeaders headers = new HttpHeaders(); headers.set(HttpHeaders.CACHE_CONTROL, "no-store"); headers.set(HttpHeaders.PRAGMA, "no-cache"); if (status == HttpStatus.UNAUTHORIZED.value() || (e instanceof InsufficientScopeException)) { headers.set(HttpHeaders.WWW_AUTHENTICATE, String.format("%s %s", OAuth2AccessToken.BEARER_TYPE, e.getSummary())); } // 客户端异常直接返回客户端,不然无法解析 if (e instanceof ClientAuthenticationException) { return new ResponseEntity<>(e, headers, HttpStatus.valueOf(status)); } return new ResponseEntity<>(new PigAuth2Exception(e.getMessage(), e.getOAuth2ErrorCode()), headers, HttpStatus.valueOf(status)); } } 4.将自定义异常处理类添加到认证服务器配置
// springboot启动添加注解@EnableScheduling @EnableScheduling public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } // task @Slf4j @Component public class DemoTask { @Autowired private XxService xxService; @Scheduled(cron="0 30 2 * * ? ") protected void process(){ log.info("EbOrderSyncTask同步开始 =========>" + LocalDateTime.now()); ... log.info("EbOrderSyncTask同步结束 =========>" + LocalDateTime.now()); } }
@Component @Primary public class DocumentationConfig implements SwaggerResourcesProvider { @Override public List<SwaggerResource> get() { List resources = new ArrayList<>(); // service-id => xx-appName // swagger url => "/admin/v2/api-docs" resources.add(swaggerResource("xx-appName", "/admin/v2/api-docs", "2.0")); return resources; } private SwaggerResource swaggerResource(String name, String location, String version) { SwaggerResource swaggerResource = new SwaggerResource(); swaggerResource.setName(name); swaggerResource.setLocation(location); swaggerResource.setSwaggerVersion(version); return swaggerResource; } } @Configuration @EnableSwagger2 public class SystemSwaggerConfig extends WebMvcConfigurationSupport { @Bean public Docket api() { return new Docket(DocumentationType.SWAGGER_2) .host("128.0.0.1:27000") .apiInfo(apiInfo()) .select() // 自行修改为自己的包路径 -> com.xx.controller .apis(RequestHandlerSelectors.basePackage("com.xx.controller")) .paths(PathSelectors.any()) .build() .globalOperationParameters(getParams()); } private List<Parameter> getParams() { //添加head参数start ParameterBuilder tokenPar = new ParameterBuilder(); List<Parameter> pars = new ArrayList<Parameter>(); tokenPar.name("Authorization").description("令牌").modelRef(new ModelRef("string")) .parameterType("header").required(false) .defaultValue("Bearer 15265af2-607d-4c8b-ad54-0c434af82849") .build(); pars.add(tokenPar.build()); return pars; } @Override protected void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");; registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/"); super.addResourceHandlers(registry); } private ApiInfo apiInfo() { return new ApiInfoBuilder().title("接口文档").build(); } }
// 1.路径请求 mockMvc.perform(MockMvcRequestBuilders.请求方式("url/{path}",参数值); // 2.表单请求 mockMvc.perform(MockMvcRequestBuilders.请求方式("url").param("键","值").contentType(MediaType.APPLICATION_FORM_URLENCODED); // 3.JSON请求 mockMvc.perform(MockMvcRequestBuilders.请求方式,一般为POST("url").content(JSONObject.toJSONString(map)).contentType(.contentType(MediaType.APPLICATION_JSON));
所有@Enable* 注解都是有@Import的组合注解,@Enable* 自动开启的实现其实就是导入了一些自动配置的Bean,@Import 注解的最主要功能就是导入额外的配置信息。
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Import(SchedulingConfiguration.class) @Documented public @interface EnableScheduling { } /** * 可以看到EnableScheduling注解直接导入配置类SchedulingConfiguration,这个类注解了@Configuration,且注册了一个scheduledAnnotationProcessor的Bean */ @Configuration @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public class SchedulingConfiguration { @Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME) @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() { return new ScheduledAnnotationBeanPostProcessor(); } }
/** 如果并不确定引入哪个配置类,需要根据@Import注解所标识的类或者另一个注解(通常是注解)里的定义信息选择配置类的话,用这种方式。*/ // ImportSelector接口只有一个方法: String[] selectImports(AnnotationMetadata importingClassMetadata); /** 注解类 */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(AsyncConfigurationSelector.class) public @interface EnableAsync { Class<? extends Annotation> annotation() default Annotation.class; boolean proxyTargetClass() default false; AdviceMode mode() default AdviceMode.PROXY; int order() default Ordered.LOWEST_PRECEDENCE; } /** * 实现选择器 * AsyncConfigurationSelector继承AdviceModeImportSelector,AdviceModeImportSelector类实现ImportSelector接口 根据AdviceMode的不同来选择生明不同的Bean */ public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> { private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME = "org.springframework.scheduling.aspectj.AspectJAsyncConfiguration"; @Override @Nullable public String[] selectImports(AdviceMode adviceMode) { switch (adviceMode) { case PROXY: return new String[] {ProxyAsyncConfiguration.class.getName()}; case ASPECTJ: return new String[] {ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME}; default: return null; } } }
/** 一般只要用户确切知道哪些Bean需要放入容器的话,自己可以通过spring 提供的注解来标识就可以了,比如@Component,@Service,@Repository,@Bean等。 如果是不确定的类,或者不是spring专用的,所以并不想用spring的注解进行侵入式标识,那么就可以通过@Import注解,实现ImportBeanDefinitionRegistrar接口来动态注册Bean。 比如: */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(AspectJAutoProxyRegistrar.class) public @interface EnableAspectJAutoProxy { boolean proxyTargetClass() default false; boolean exposeProxy() default false; } /** * AspectJAutoProxyRegistrar实现了ImportBeanDefinitionRegistrar接口,ImportBeanDefinitionRegistrar的作用是在运行时自动添加Bean到已有的配置类,通过重写方法: */ AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions( AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry); AnnotationAttributes enableAspectJAutoProxy = AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class); if (enableAspectJAutoProxy != null) { if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) { AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); } if (enableAspectJAutoProxy.getBoolean("exposeProxy")) { AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry); } } } }
<!-- resource/logback-spring.xml --> <configuration debug="false" scan="false"> <springProperty scop="context" name="spring.application.name" source="spring.application.name" defaultValue=""/> <property name="log.path" value="logs/${spring.application.name}"/> <!-- Console log output --> <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>${CONSOLE_LOG_PATTERN}</pattern> </encoder> </appender> <!-- Log file debug output --> <appender name="debug" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${log.path}/debug.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> <fileNamePattern>${log.path}/%d{yyyy-MM, aux}/debug.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern> <maxFileSize>50MB</maxFileSize> <maxHistory>30</maxHistory> </rollingPolicy> <encoder> <pattern>%date [%thread] %-5level [%logger{50}] %file:%line - %msg%n</pattern> </encoder> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>DEBUG</level> </filter> </appender> <!-- Log file error output --> <appender name="error" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${log.path}/error.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> <fileNamePattern>${log.path}/%d{yyyy-MM}/error.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern> <maxFileSize>50MB</maxFileSize> <maxHistory>30</maxHistory> </rollingPolicy> <encoder> <pattern>%date [%thread] %-5level [%logger{50}] %file:%line - %msg%n</pattern> </encoder> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>ERROR</level> </filter> <appender> <!-- Level: FATAL 0 ERROR 3 WARN 4 INFO 6 DEBUG 7 --> <root level="INFO"> <appender-ref ref="console"/> <appender-ref ref="debug"/> <appender-ref ref="error"/> </root> </configuration>
1.启用task配置 (1) 配置文件的方式:application.properties spring.task.scheduling.pool.size=20 spring.task.scheduling.thread-name-prefix=Job-Thread- (2) 配置类的方式 @Configuration @EnableScheduling @ComponentScan(basePackages = {"com.xkcoding.task.job"}) public class TaskConfig implements SchedulingConfigurer { @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { taskRegistrar.setScheduler(taskExecutor()); } @Bean public Executor taskExecutor() { return new ScheduledThreadPoolExecutor(20, new BasicThreadFactory.Builder().namingPattern("Job-Thread-%d").build()); } } 2.新增TaskJob类 @Component @Slf4j public class TaskJob { /** * 按照标准时间来算,每隔 10s 执行一次 */ @Scheduled(cron = "0/10 * * * * ?") public void job1() { log.info("【job1】开始执行:{}", DateUtil.formatDateTime(new Date())); } /** * 从启动时间开始,间隔 2s 执行 * 固定间隔时间 */ @Scheduled(fixedRate = 2000) public void job2() { log.info("【job2】开始执行:{}", DateUtil.formatDateTime(new Date())); } /** * 从启动时间开始,延迟 5s 后间隔 4s 执行 * 固定等待时间 */ @Scheduled(fixedDelay = 4000, initialDelay = 5000) public void job3() { log.info("【job3】开始执行:{}", DateUtil.formatDateTime(new Date())); } }
spring类的生命周期
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd"> <bean id="person1" destroy-method="myDestroy" init-method="myInit" class="com.test.spring.life.Person"> <property name="name"> <value>jack</value> </property> </bean> <!-- 配置自定义的后置处理器 --> <bean id="postProcessor" class="com.pingan.spring.life.MyBeanPostProcessor" /> </beans> /** * 1.实例化Bean对象 * 2.设置对象属性 * 3.调用BeanNameAware.setBeanName() * 4.调用BeanNameAware.setBeanFactory() * 5.调用ApplicationContextAware.setApplicationContext() * 6.调用BeanPostProcessor.postProcessBeforeInitialzation() -> 前置处理器 * 7.调用InitializingBean.afterPropertiesSet() * 8.调用init-method方法 * 9.调用BeanPostProcessor.postProcessAfterInitialization() -> 后置处理器 * 10.缓存一份Bean实例 -> scope="singleton" * 11.容器关闭,调用DisposableBean.destroy() * 12.调用自定义的destroy-method */ public class Person implements BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean, DisposableBean { private String name; public Person() { System.out.println("PersonService类构造方法"); } public String getName() { return name; } public void setName(String name) { this.name = name; System.out.println("set方法被调用"); } //自定义的初始化函数 public void myInit() { System.out.println("myInit被调用"); } //自定义的销毁方法 public void myDestroy() { System.out.println("myDestroy被调用"); } public void destroy() throws Exception { // TODO Auto-generated method stub System.out.println("destory被调用"); } public void afterPropertiesSet() throws Exception { // TODO Auto-generated method stub System.out.println("afterPropertiesSet被调用"); } public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { // TODO Auto-generated method stub System.out.println("setApplicationContext被调用"); } public void setBeanFactory(BeanFactory beanFactory) throws BeansException { // TODO Auto-generated method stub System.out.println("setBeanFactory被调用,beanFactory"); } public void setBeanName(String beanName) { // TODO Auto-generated method stub System.out.println("setBeanName被调用,beanName:" + beanName); } public String toString() { return "name is :" + name; } } /** Bean前后置处理器 */ public class MyBeanPostProcessor implements BeanPostProcessor { public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { // TODO Auto-generated method stub System.out.println("postProcessBeforeInitialization被调用"); return bean; } public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { // TODO Auto-generated method stub System.out.println("postProcessAfterInitialization被调用"); return bean; } } /** 测试类 */ public class AcPersonServiceTest { public static void main(String[] args) { System.out.println("开始初始化容器"); ApplicationContext ac = new ClassPathXmlApplicationContext("com/test/spring/life/applicationContext.xml"); System.out.println("xml加载完毕"); Person person1 = (Person) ac.getBean("person1"); System.out.println(person1); System.out.println("关闭容器"); ((ClassPathXmlApplicationContext)ac).close(); } } /** 开始初始化容器 九月 25, 2016 10:44:50 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh 信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@b4aa453: startup date [Sun Sep 25 22:44:50 CST 2016]; root of context hierarchy 九月 25, 2016 10:44:50 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions 信息: Loading XML bean definitions from class path resource [com/test/spring/life/applicationContext.xml] Person类构造方法 set方法被调用 setBeanName被调用,beanName:person1 setBeanFactory被调用,beanFactory setApplicationContext被调用 postProcessBeforeInitialization被调用 afterPropertiesSet被调用 myInit被调用 postProcessAfterInitialization被调用 xml加载完毕 name is :jack 关闭容器 九月 25, 2016 10:44:51 下午 org.springframework.context.support.ClassPathXmlApplicationContext doClose 信息: Closing org.springframework.context.support.ClassPathXmlApplicationContext@b4aa453: startup date [Sun Sep 25 22:44:50 CST 2016]; root of context hierarchy destory被调用 myDestroy被调用 */
@Data @ConfigurationProperties("example.service") @Component public class WrapServiceProperties { private String prefix; private String suffix; } <!-- application.properties --> example.service.prefix=### example.service.suffix=@@@
1.部署xx-job-admin服务端 (1) 下载项目 $ git clone https://github.com/xuxueli/xxl-job (2) 初始化doc/tables-xxl_job.sql脚本 (3) 启动服务,并验证: http://127.0.0.1:8080/xxl-job-admin/ 初始账号密码:admin/123456 2.引入客户端 (1) 引入jar包(2.2.0版本+服务端2.2.3-SNAPSHOT版本) appname:在xxl-job配置的执行器的appname accessToken:调度中心通讯TOKEN,与xxl-job-admin配置文件的token一致即可 <!-- pom.xml --> <dependency> <groupId>com.xuxueli</groupId> <artifactId>xxl-job-core</artifactId> <version>2.2.0</version> </dependency> (2) 配置xxl-job服务器 <!-- application.yml --> xxl: job: admin: addresses: http://127.0.0.1:8000/xxl-job-admin executor: appname: xxl-job-client ip: port: 9999 logpath: /logs/xxl-job/jobhandler logretentiondays: 30 accessToken: 6889b70769ffb1e955ba69396f0cca6b (3) 将XxlJobConfig交给spring管理 @Configuration public class XxlJobConfig { @Value("${xxl.job.admin.addresses}") private String adminAddresses; @Value("${xxl.job.executor.appname}") private String appName; @Value("${xxl.job.executor.ip}") private String ip; @Value("${xxl.job.executor.port}") private int port; @Value("${xxl.job.accessToken}") private String accessToken; @Value("${xxl.job.executor.logpath}") private String logPath; @Value("${xxl.job.executor.logretentiondays}") private int logRetentionDays; // 2.2.0+版本移除 //@Bean(initMethod = "start", destroyMethod = "destroy") @Bean public XxlJobSpringExecutor xxlJobExecutor() { // logger.info(">>>>>>>>>>> xxl-job config init."); XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor(); xxlJobSpringExecutor.setAdminAddresses(adminAddresses); xxlJobSpringExecutor.setAppName(appName); xxlJobSpringExecutor.setIp(ip); xxlJobSpringExecutor.setPort(port); xxlJobSpringExecutor.setAccessToken(accessToken); xxlJobSpringExecutor.setLogPath(logPath); xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays); return xxlJobSpringExecutor; } } (4) 写一个定时任务 /** * XxlJob开发示例(Bean模式) * * 开发步骤: * 1、在Spring Bean实例中,开发Job方法,方式格式要求为 "public ReturnT<String> execute(String param)" * 2、为Job方法添加注解 "@XxlJob(value="自定义jobhandler名称", init = "JobHandler初始化方法", destroy = "JobHandler销毁方法")",注解value值对应的是调度中心新建任务的JobHandler属性的值。 * 3、执行日志:需要通过 "XxlJobLogger.log" 打印执行日志; * * @author xuxueli 2019-12-11 21:52:51 */ @Component @Sl4j public class SampleXxlJob { /** * 1、简单任务示例(Bean模式) */ @XxlJob("demoJobHandler") public ReturnT<String> demoJobHandler(String param) throws Exception { log.info("XXL-JOB, Hello World."); for (int i = 0; i < 5; i++) { XxlJobLogger.log("beat at:" + i); TimeUnit.SECONDS.sleep(2); } return ReturnT.SUCCESS; } } (5) 在服务端添加刚刚创建的定时任务 界面输入参数: JobHandler:demoJobHandler
1.引入ActiveMQ支持 (1) pom.xml <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-activemq</artifactId> </dependency> </dependencies> (2) application.properties spring.activemq.broker-url=tcp://localhost:61616 spring.activemq.user=admin spring.activemq.password=admin 2.写demo (1) 消费者-Consumer @Component @EnableJms @Slf4j public class ActiveMQListener { @JmsListener(destination = "test-queue") public void listener(String message) { log.info("Message received {} ", message); } } (2) 生产者-Producer <!-- JmsConfig.java --> package com.test.activemq.configuration; import org.apache.activemq.command.ActiveMQQueue; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jms.annotation.EnableJms; import javax.jms.Queue; @Configuration public class JmsConfig { @Bean public Queue queue(){ return new ActiveMQQueue("test-queue"); } } <!-- Producer.java --> package com.test.activemq.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.jms.core.JmsTemplate; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.jms.Queue; @RestController @RequestMapping("/api") public class MessageController { @Autowired private Queue queue; @Autowired private JmsTemplate jmsTemplate; @GetMapping("message/{message}") public ResponseEntity<String> publish(@PathVariable("message") final String message){ jmsTemplate.convertAndSend(queue, message); return new ResponseEntity(message, HttpStatus.OK); } }
package com.line.demo.task; import java.util.Arrays; import java.util.List; /** * @author: cent * @email: 292462859@qq.com * @date: 2019/1/16. * @description: */ @Configuration @EnableScheduling @Slf4j public class DynamicSchedule implements SchedulingConfigurer { /** * 测试数据,实际可从数据库获取 */ private List<Task> tasks = Arrays.asList( new Task(1, "任务1", "*/30 * * * * *"), new Task(2, "任务2", "*/30 * * * * *"), new Task(3, "任务3", "*/30 * * * * *"), new Task(4, "任务4", "*/30 * * * * *"), new Task(5, "任务5", "*/30 * * * * *"), new Task(6, "任务6", "*/30 * * * * *"), new Task(7, "任务7", "*/30 * * * * *"), new Task(8, "任务8", "*/30 * * * * *"), new Task(9, "任务9", "*/30 * * * * *"), new Task(10, "任务10", "*/30 * * * * *") ); @Override public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) { tasks.forEach(task -> { //任务执行线程 Runnable runnable = () -> log.info("execute task {}", task.getId()); //任务触发器 Trigger trigger = triggerContext -> { //获取定时触发器,这里可以每次从数据库获取最新记录,更新触发器,实现定时间隔的动态调整 CronTrigger cronTrigger = new CronTrigger(task.getCron()); return cronTrigger.nextExecutionTime(triggerContext); }; //注册任务 scheduledTaskRegistrar.addTriggerTask(runnable, trigger); }); } @Data @AllArgsConstructor static class Task { /** * 主键ID */ private int id; /** * 任务名称 */ private String name; /** * cron表达式 */ private String cron; } }
@Slf4j @RestControllerAdvice public class GlobalExceptionHandler { /** * 全局异常. * * @param e the e * @return R */ @ExceptionHandler(Exception.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public R exception(Exception e) { log.error("全局异常信息 ex={}", e.getMessage(), e); return R.failed(e); } /** * validation Exception * * @param exception * @return R */ @ExceptionHandler({MethodArgumentNotValidException.class, BindException.class}) @ResponseStatus(HttpStatus.BAD_REQUEST) public R bodyValidExceptionHandler(MethodArgumentNotValidException exception) { List<FieldError> fieldErrors = exception.getBindingResult().getFieldErrors(); log.warn(fieldErrors.get(0).getDefaultMessage()); return R.failed(fieldErrors.get(0).getDefaultMessage()); } /** * 处理所有业务异常 * * @param e * @return */ @ExceptionHandler(BusinessException.class) public R handleBusinessException(BusinessException e) { log.error("业务异常异常信息 ex={}", e.getMessage(), e); return R.failed(e); } } @NoArgsConstructor public class BusinessException extends RuntimeException { private static final long serialVersionUID = 1L; public BusinessException(String message) { super(message); } public BusinessException(Throwable cause) { super(cause); } public BusinessException(String message, Throwable cause) { super(message, cause); } public BusinessException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } } /** * 响应信息主体 * * @param <T> * @author xw */ @ToString @NoArgsConstructor @AllArgsConstructor @Accessors(chain = true) public class R<T> implements Serializable { private static final long serialVersionUID = 1L; @Getter @Setter private int code; @Getter @Setter private String msg; @Getter @Setter private T data; public static <T> R<T> ok() { return restResult(null, CommonConstants.SUCCESS, null); } public static <T> R<T> ok(T data) { return restResult(data, CommonConstants.SUCCESS, null); } public static <T> R<T> ok(T data, String msg) { return restResult(data, CommonConstants.SUCCESS, msg); } public static <T> R<T> failed() { return restResult(null, CommonConstants.FAIL, null); } public static <T> R<T> failed(String msg) { return restResult(null, CommonConstants.FAIL, msg); } public static <T> R<T> failed(T data) { return restResult(data, CommonConstants.FAIL, null); } public static <T> R<T> failed(T data, String msg) { return restResult(data, CommonConstants.FAIL, msg); } private static <T> R<T> restResult(T data, int code, String msg) { R<T> apiResult = new R<>(); apiResult.setCode(code); apiResult.setData(data); apiResult.setMsg(msg); return apiResult; } }
String[] beanNamesForType = SpringContextHolder.getApplicationContext().getBeanNamesForType(AbstractObserver.class);
@FeignClient(name = "microservice-provider-user") public interface UserFeignClient { @RequestMapping(value = "/get", method = RequestMethod.GET) public User get1(@RequestParam("id") Long id, @RequestParam("username") String username); } @FeignClient(name = "microservice-provider-user") public interface UserFeignClient { @RequestMapping(value = "/get", method = RequestMethod.GET) public User get2(@RequestParam Map<String, Object> map); } @RestController public class UserController { @PostMapping("/post") public User post(@RequestBody User user) { ... } } @FeignClient(name = "microservice-provider-user") public interface UserFeignClient { @RequestMapping(value = "/post", method = RequestMethod.POST) public User post(@RequestBody User user); }
Mysql
使用root账号创建数据库db_name并授权给test账号 $ mysql -uroot -h0 -P3306 -p123456 $ create database db_name; $ grant all privileges on db_name.* to test@'localhost' identified by '123456'; $ grant all privileges on db_name.* to test@'127.0.0.1' identified by '123456';
-- 导出db_name整个数据库 mysqldump -u test -p123456 db_name -P 3306 > db_name.sql -- 导出某数据库指定的表(db_name.table_name) mysqldump -u test -p123456 db_name table_name > table_name.sql
$ mysql -u test -p123456 db_name < /opt/sql/xx.sql
-- 添加PRIMARY KEY(主键索引) ALTER TABLE `table_name` ADD PRIMARY KEY index_name( `column` ) -- 添加UNIQUE(唯一索引) ALTER TABLE `table_name` ADD UNIQUE index_name( `column` ) -- 添加INDEX(普通索引) ALTER TABLE `table_name` ADD INDEX index_name ( `column` ) -- 添加FULLTEXT(全文索引) ALTER TABLE `table_name` ADD FULLTEXT ( `column`) -- 添加多列索引 ALTER TABLE `table_name` ADD INDEX index_name ( `column1`, `column2`, `column3` ) -- 删除索引 drop index index_name on table_name ;
-- 添加表的字段 alter table 表名 add 字段名 字段的类型 alter table table1 add transactor varchar(10) not Null; -- 修改表的字段类型 ALTER TABLE 表名 MODIFY COLUMN 字段名 字段类型定义; ALTER TABLE chatter_users MODIFY COLUMN ip VARCHAR(50); -- 修改表的字段名 alter table 表名 change 原字段名 新字段名 字段的类型 alter table student change physics physisc char(10) not null; -- 删除表的字段 alter table 表名 drop column 字段名 alter table `user_movement_log` drop column Gatewayid; -- 调整表的顺序: ALTER TABLE `user_movement_log` CHANGE `GatewayId` `GatewayId` int not null default 0 AFTER RegionID -- 表的重命名 alter table 原表名 rename 现表名; alter table t1 rename t2; -- 删除表的数据 delete from 表名 where (条件) id 不是从1开始; truncate table 表名 id是从1 开始的; -- 创建表的例子 CREATE TABLE hlh_message ( id int(11) NOT NULL AUTO_INCREMENT COMMENT '健康表id', title varchar(40) NOT NULL COMMENT '健康标题', hlh_url text DEFAULT NULL COMMENT '图片地址', bewrite VARCHAR(350) NOT NULL COMMENT '描述', content VARCHAR(350) NOT NULL COMMENT '内容', type tinyint(1) NOT NULL DEFAULT '0' COMMENT '健康知识 0 健康咨询 1', create_time date DEFAULT NULL COMMENT '发布消息的时间', PRIMARY KEY (id) )ENGINE=INNODB DEFAULT CHARSET=utf8 COMMENT='健康表';
-- mysql日期格式化 DATE_FORMAT( created_date, "%Y-%m-%d")
-- mysql查询奇偶行 set @row = 0; select id from (select (@row:=case when @row is null then 1 else @row+1 end) as RNr, a.id,a.order_no,a.sku,a.send_count,b.count from cg_supplier_shipping_order_detail a
inner join cg_buyer_purchase_order b on a.buyer_purchase_order_order_no=b.order_no and a.sku=b.sku where b.order_no='PO119092540003' and b.send_total>b.count) t where t.RNr%2 = 0; -- 查看慢sql show variables like '%query%';
-- 查看所有表 show tables; -- 查看表结构 desc sys_log; -- 查看建表语句 show create table tablename;
-- show processlist; -- show variables like '%tmp%'; -- show variables like '%query%';
Mybatis
// 使用方法 public interface XxMapper extends BaseMapper<Xx> { // dataScope--数据权限载体 IPage<Xx> getXxPage(Page page, @Param("xx") Xx xx,@Param("dataScope") DataScope dataScope); } @Data @EqualsAndHashCode(callSuper = true) public class DataScope extends HashMap { /** * 限制范围的字段名称 */ private String firstScopeName = "warehouse_id"; /** * 具体的数据范围 */ private List<Integer> firstIds; /** * 是否只查询本部门 */ private Boolean isOnly = false; ... } /** * @author admin * @date 2019/2/1 */ @Configuration @MapperScan("com.demo.mapper") public class MybatisPlusConfigurer { /** * 分页插件 * * @return PaginationInterceptor */ @Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor().setLimit(10000); } /** * 数据权限插件 * * @return DataScopeInterceptor */ @Bean public DataScopeInterceptor dataScopeInterceptor() { return new DataScopeInterceptor(); } /** * 逻辑删除 * * @return */ @Bean public ISqlInjector sqlInjector() { return new LogicSqlInjector(); } } /** * @author xw * @date 2019/7/30 * <p> * mybatis 数据权限拦截器 */ @Slf4j @Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})}) public class DataScopeInterceptor extends AbstractSqlParserHandler implements Interceptor { @Override @SneakyThrows public Object intercept(Invocation invocation) { StatementHandler statementHandler = (StatementHandler) PluginUtils.realTarget(invocation.getTarget()); MetaObject metaObject = SystemMetaObject.forObject(statementHandler); this.sqlParser(metaObject); // 先判断是不是SELECT操作 MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement"); if (!SqlCommandType.SELECT.equals(mappedStatement.getSqlCommandType())) { return invocation.proceed(); } BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql"); String originalSql = boundSql.getSql(); Object parameterObject = boundSql.getParameterObject(); //查找参数中包含DataScope类型的参数 DataScope dataScope = findDataScopeObject(parameterObject); if (dataScope == null) { return invocation.proceed(); } else { // 数据权限规则 仓库id+平台id StringBuilder sb = new StringBuilder(" where 1=1 "); // 第一个范围 String firstScopeName = dataScope.getFirstScopeName(); List<Integer> firstIds = dataScope.getFirstIds(); if (StrUtil.isNotBlank(firstScopeName) && CollectionUtil.isNotEmpty(firstIds) && firstIds.size() >= 1) { sb.append(String.format(" and temp_data_scope.%s in (%s)", firstScopeName, CollectionUtil.join(firstIds, ","))); } // 第二个范围(平台权限,当数据平台id为null时,默认所有人可见) String secondScopeName = dataScope.getSecondScopeName(); List<Integer> secondIds = dataScope.getSecondIds(); if (StrUtil.isNotBlank(secondScopeName) && CollectionUtil.isNotEmpty(secondIds) && secondIds.size() >= 1) { sb.append(String.format(" and (temp_data_scope.%s in (%s) or temp_data_scope.%s is null)", secondScopeName, CollectionUtil.join(secondIds, ","), secondScopeName)); } originalSql = "select * from (" + originalSql + ") temp_data_scope" + sb.toString(); metaObject.setValue("delegate.boundSql.sql", originalSql); return invocation.proceed(); } } /** * 生成拦截对象的代理 * * @param target 目标对象 * @return 代理对象 */ @Override public Object plugin(Object target) { if (target instanceof StatementHandler) { return Plugin.wrap(target, this); } return target; } /** * mybatis配置的属性 * * @param properties mybatis配置的属性 */ @Override public void setProperties(Properties properties) { } /** * 查找参数是否包括DataScope对象 * * @param parameterObj 参数列表 * @return DataScope */ private DataScope findDataScopeObject(Object parameterObj) { if (parameterObj instanceof DataScope) { return (DataScope) parameterObj; } else if (parameterObj instanceof Map) { for (Object val : ((Map<?, ?>) parameterObj).values()) { // { param0:xx, param1:xx, xxBean:xx: ...} if (val instanceof DataScope) { return (DataScope) val; } } } return null; } }
/** * BaseEntity * * @author xw * @date 2020/5/6 */ public class BaseEntity<T extends Model<?>> extends Model<T> { @ApiModelProperty("创建时间") private LocalDateTime createTime; @ApiModelProperty("创建人") private Integer createUser; @ApiModelProperty("更新时间") private LocalDateTime updateTime; @ApiModelProperty("更新人") private Integer updateUser; @ApiModelProperty(value = "删除标识:1-删除,0-正常,默认为0") private Integer delFlag; // setter、getter } /** * BaseServiceImpl * * @author xw * @date 2020/5/6 */ public class BaseServiceImpl<M extends BaseMapper<T>, T extends BaseEntity> extends ServiceImpl<M, T> { @Override public boolean save(T entity) { entity.setCreateUser(UserUtils.getCurrentUserId()); entity.setCreateTime(LocalDateTime.now()); return super.save(entity); } @Override public boolean saveBatch(Collection<T> entityList, int batchSize) { if (CollectionUtil.isNotEmpty(entityList)) { entityList.stream().forEach(m -> { m.setCreateUser(UserUtils.getCurrentUserId()); m.setCreateTime(LocalDateTime.now()); }); } return super.saveBatch(entityList, batchSize); } @Override public boolean updateById(T entity) { entity.setUpdateUser(UserUtils.getCurrentUserId()); entity.setUpdateTime(LocalDateTime.now()); return super.updateById(entity); } @Override public boolean updateBatchById(Collection<T> entityList, int batchSize) { if (CollectionUtil.isNotEmpty(entityList)) { entityList.stream().forEach(m -> { m.setUpdateUser(UserUtils.getCurrentUserId()); m.setUpdateTime(LocalDateTime.now()); }); } return super.updateBatchById(entityList, batchSize); } }
/**example * 删除时 update user set deleted=1 where id =1 and deleted=0 * 查找时 select * from user where deleted=0 */ @TableLogic private Integer deleted; // 忽略实体bean非数据库字段 @TableField(exist=false) // 新增、修改值为null字段 @TableField(fill = FieldFill.INSERT_UPDATE)
$condition = Wrappers.<~>query().lambda().eq(SysUserRole::getRoleId, 5);
xxService.list($condition)
xxService.getOne($condition)
...
public Page<T> selectPage(Page<T> page, EntityWrapper<T> entityWrapper) { if (null != entityWrapper) { entityWrapper.orderBy(page.getOrderByField(), page.isAsc()); } page.setRecords(baseMapper.selectPage(page, entityWrapper)); return page; }
MybatisPlus拼原生
EntityWrapper<User> ew = new EntityWrapper<User>(); ew.setEntity(new User(1)); ew.where("user_name={0}", "'zhangsan'").and("id=1") .orNew("user_status={0}", "0").or("status=1") .notLike("user_nickname", "notvalue") .andNew("new=xx").like("hhh", "ddd") .andNew("pwd=11").isNotNull("n1,n2").isNull("n3") .groupBy("x1").groupBy("x2,x3") .having("x1=11").having("x3=433") .orderBy("dd").orderBy("d1,d2"); System.out.println(ew.getSqlSegment());
List<User> selectMyPage(RowBounds rowBounds, @Param("ew") Wrapper<T> wrapper); <select id="selectMyPage" resultType="User"> SELECT * FROM user <where> ${ew.sqlSegment} </where> </select>
Mybatis-like
<if test="xx.name != null and xx.name != ''"> AND name like CONCAT('%',#{xx.name},'%') </if>
// 方式一 CkQcOrderInfo qcOrderInfo = this.getById(qcOrderDetail.getQcOrderId()); this.update(Wrappers.<CkQcOrderInfo>lambdaUpdate() .eq(CkQcOrderInfo::getId, qcOrderDetail.getQcOrderId()) .set(CkQcOrderInfo::getQcQty, NumUtils.add(qcOrderInfo.getQcQty(), qcOrderDetail.getQcQty())) .set(CkQcOrderInfo::getPassQty, NumUtils.add(qcOrderInfo.getPassQty(), qcOrderDetail.getPassQty()))); // 方式二 CkQcOrderInfo qcOrderInfo = this.getById(qcOrderDetail.getQcOrderId()); qcOrderInfo.setQcQty(NumUtils.add(qcOrderInfo.getQcQty(), qcOrderDetail.getQcQty())); qcOrderInfo.setPassQty(NumUtils.add(qcOrderInfo.getPassQty(), qcOrderDetail.getPassQty())); this.updateById(qcOrderInfo); @Override public boolean updateById(CkQcOrderInfo entity) { entity.setUpdateUser(UserUtils.getCurrentUserId()); entity.setUpdateTime(LocalDateTime.now()); return super.updateById(entity); }
<select id="getSimilarity" parameterType="java.util.HashMap" resultType="java.util.HashMap"> select * from table where ids in <foreach item="item" index="index" collection="ids.split(',')" open="(" separator="," close=")"> #{item} </foreach> </select>
<if test="xx.startTime != null"> AND create_time >= #{xx.startTime} </if> <if test="xx.endTime != null"> AND create_time <= #{xx.endTime} </if>
其它
// 1.toPageVo -- $beanName$从clipboard()获取 private IPage<$beanName$VO> toPageVo(IPage<$beanName$> page) { return new Page<$beanName$VO>(page.getCurrent(), page.getSize(), page.getTotal()) .setRecords(page.getRecords().stream().map(m -> beanToVo(m)).collect(Collectors.toList())); } private $beanName$VO beanToVo($beanName$ m) { if (Objects.isNull(m)) { return null; } $beanName$VO vo = new $beanName$VO(); BeanUtils.copyProperties(m, vo); $END$ return vo; } // 2.ifeq:判断相等 if (Objects.equals($v1$, $v2$)) { $END$ } // 3.ifn:判断空 if (Objects.isNull($VAR$)) { $END$ } // 4.pstr:private String /** * $desc$ */ @ApiModelProperty(value = "$desc$") private String $v$; $END$ // 5.psdate /** * 起始时间 */ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") @ApiModelProperty(value = "起始时间") private LocalDateTime $END$startTime; // 6.pedate /** * 结束时间 */ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") @ApiModelProperty(value = "结束时间") private LocalDateTime $END$endTime; // 7.plist /** * $desc$ */ @ApiModelProperty(value = "$desc$") private List<$type$> items; $END$ // 8.tcc try { $END$ } catch(Exception e) { e.printStackTrace(); }
hutool工具类
JDK8常规操作
public class Apple { private Integer id; private String name; private BigDecimal money; private Integer num; public Apple(Integer id, String name, BigDecimal money, Integer num) { this.id = id; this.name = name; this.money = money; this.num = num; } } // 测试数据 List<Apple> appleList = new ArrayList<>();//存放apple对象集合 Apple apple1 = new Apple(1,"苹果1",new BigDecimal("3.25"),10); Apple apple12 = new Apple(1,"苹果2",new BigDecimal("1.35"),20); Apple apple2 = new Apple(2,"香蕉",new BigDecimal("2.89"),30); Apple apple3 = new Apple(3,"荔枝",new BigDecimal("9.99"),40); appleList.add(apple1); appleList.add(apple12); appleList.add(apple2); appleList.add(apple3); //1.List转Map /** * List -> Map * 需要注意的是: * toMap 如果集合对象有重复的key,会报错Duplicate key .... * apple1,apple12的id都为1。 * 可以用 (k1,k2)->k1 来设置,如果有重复的key,则保留key1,舍弃key2 */ Map<Integer, Apple> appleMap = appleList.stream().collect(Collectors.toMap(Apple::getId, a -> a,(k1,k2)->k1)); //2.分组 //List 以ID分组 Map<Integer,List<Apple>> Map<Integer, List<Apple>> groupBy = appleList.stream().collect(Collectors.groupingBy(Apple::getId)); System.err.println("groupBy:"+groupBy); {1=[Apple{id=1, name='苹果1', money=3.25, num=10}, Apple{id=1, name='苹果2', money=1.35, num=20}], 2=[Apple{id=2, name='香蕉', money=2.89, num=30}], 3=[Apple{id=3, name='荔枝', money=9.99, num=40}]} //3.过滤filter //过滤出符合条件的数据 List<Apple> filterList = appleList.stream().filter(a -> a.getName().equals("香蕉")).collect(Collectors.toList()); System.err.println("filterList:"+filterList); [Apple{id=2, name='香蕉', money=2.89, num=30}] //4.求和 //计算 总金额 BigDecimal totalMoney = appleList.stream().map(Apple::getMoney).reduce(BigDecimal.ZERO, BigDecimal::add); System.err.println("totalMoney:"+totalMoney); //totalMoney:17.48 //计算 数量int sum = appleList.stream().mapToInt(Apple::getNum).sum(); System.err.println("sum:"+sum); //sum:100 // 5.stream下标问题 AtomicInteger sort = new AtomicInteger(1); list = list.stream().map(m -> { m.setImgSort(sort.get()); sort.incrementAndGet(); return m; }).collect(Collectors.toList()); // 6.LocalDate常用 LocalDate date = LocalDate.of(2014, 3, 18); DayOfWeek dow = date.getDayOfWeek(); // -》TUESDAY int len = date.lengthOfMonth(); // -》31 boolean leap = date.isLeapYear(); // -》false (not a leap year) LocalTime time = LocalTime.of(13, 45, 20); //-》13:45:20 LocalTime time = LocalTime.parse("13:45:20"); LocalDate date1 = dt1.toLocalDate(); LocalTime time1 = dt1.toLocalTime(); // Date转LocalDateTime Date date = new Date(); LocalDateTime dateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()); // 相差天数 long days = ChronoUnit.DAYS.between(LocalDate.of(2014, 3, 8), LocalDate.of(2014, 4, 18)); // 日期比较 isBefore、isAfter LocalDate t1 = LocalDate.of(2020, 2, 1); LocalDate t2 = LocalDate.of(2020, 3, 2); System.out.println(t1.isBefore(t2) + t1.isAfter(t2)); // LocalDateTime GET方式请求数据如何接收 /** 结算起始时间 */ @ApiModelProperty(value = "结算起始时间") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime startTime; /** 结算结束时间 */ @ApiModelProperty(value = "结算结束时间") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime endTime; // 当月第一天、最后一天 LocalDateTime date = LocalDateTime.now(); LocalDateTime firstday = date.with(TemporalAdjusters.firstDayOfMonth()); LocalDateTime lastDay = date.with(TemporalAdjusters.lastDayOfMonth()); System.out.println(“firstday:” + firstday); System.out.println(“lastDay:” + lastDay);
常用正则工具类
/** * 1.按正则匹配字符串某一个子串 */ // "@pms.hasPermission('cg_cgshiperrorder_edit')" -> cg_cgshiperrorder_edit private final static Pattern pattern = Pattern.compile("@pms.hasPermission\('(.*?)'\)"); private static String getPermission(String str) { Matcher m = pattern.matcher(str); String result = null; while (m.find()) { result = m.group(1); break; } return result; } // 2.否定先行断言 (?!pattern) // a(?!b) -> 匹配后面不是跟着 "b" 的 "a" // 3.指定正则表达式的模式 // (?i) -> 使正则忽略大小写 // (?s) -> 使正则的 . 匹配所有字符,包括换行符。 // (?m) -> 使正则的 ^ 和 $ 匹配字符串中每行的开始和结束。 // 4.横向匹配 var regex = /a[123]b/g; var string = "a0b a1b a2b a3b a4b"; console.log( string.match(regex) ); // ["a1b", "a2b", "a3b"] // 5.纵向模糊匹配 var regex = /a[123]b/g; var string = "a0b a1b a2b a3b a4b"; console.log( string.match(regex) ); // ["a1b", "a2b", "a3b"] // 6.范围[123456abcdefGHIJKLM] -> [1-6a-fG-M] // 因为连字符有特殊用途,那么要匹配“a”、“-”、“z”这三者中任意一个字符 -> [-az]或[az-]或[a-z] // 7.排除字符组 [^abc] -> 某位字符可以是任何东西,但就不能是"a"、"b"、"c" // 8.常见的简写形式 // d -> [0-9] 数字 // D -> [^0-9] 非数字 // w -> [0-9a-zA-Z_] 单词 // W -> [^0-9a-zA-Z_] 非单词 // s -> [ v f] 空白 // S -> [^ v f] 非空白 // . -> [^ u2028u2029] 通配符,表示几乎任意字符。换行符、回车符、行分隔符和段分隔符除外。 // 匹配任意字符 [dD]、[wW]、[sS]、和[^]中任何的一个 // 9.量词 // {m,} -> 至少出现m次 // {m} -> 出现m次 // ? -> 0或1次 // + -> 至少出现1次 // * -> 出现任意次,有可能不出现
Junit测试
(1) 常用测试
// 1.正常测试 @Test public void test_detail() { CkQcOrderInfoDetailVO vo = ckQcOrderInfoService.getDetailById(1L); assertTrue("成功", Objects.isNull(vo.getId())); } // 2.异常测试 @Test public void test_qc() { try { CkQcOrderDetailDTO qcOrderDetail = new CkQcOrderDetailDTO(); qcOrderDetail.setQcOrderId(1L); qcOrderDetail.setQcQty(5); qcOrderDetail.setPassQty(5); ckQcOrderInfoService.qc(qcOrderDetail); } catch (IllegalArgumentException e) { assertThat(e.getMessage(), containsString("单号不存在或已删除!")); } fail("单号不存在或已删除!"); }
(2) springboot test
@RunWith(SpringRunner.class) @SpringBootTest(classes = DemoApplication.class) @AutoConfigureMockMvc @Slf4j //@Transactional public class BaseTest { @Autowired private WebApplicationContext context; protected MockMvc mockMvc; @Before public void setup() { mockMvc = MockMvcBuilders .webAppContextSetup(context) .build(); } }
(3) REST接口测试
@RunWith(SpringRunner.class) @SpringBootTest(classes = DemoApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @AutoConfigureMockMvc @Slf4j public class OAuthMvcTest { protected HttpMethod GET = HttpMethod.GET; protected HttpMethod POST = HttpMethod.POST; protected HttpMethod PUT = HttpMethod.PUT; protected HttpMethod DELETE = HttpMethod.DELETE; @Autowired private TestRestTemplate restTemplate; final static String API_URL = "http://localhost:9999/admin"; protected static String USRE_NAME = "admin"; protected static String PASSWORD = "123456"; @Before public void setup() { } @Test public void test_access_token_then200() { String token = obtainAccessToken("admin", "123456"); log.info("access_token->{}", token); } protected String execute(String requestControllerMethodPath, HttpMethod method, Object param) { return execute(requestControllerMethodPath, method, param, String.class); } /** * 执行测试用例 * @param requestControllerMethodPath controller+method请求路径 * @param method HttpMethod.GET|POST|PUT|DELETE * @param param 请求参数,controller里面的对象 */ protected <T> T execute(String requestControllerMethodPath, HttpMethod method, Object param, Class<T> clazz) { return execute(requestControllerMethodPath, method, param, USRE_NAME, PASSWORD, clazz); } /** * 执行测试用例 * @param requestControllerMethodPath controller+method请求路径 * @param method HttpMethod.GET|POST|PUT|DELETE * @param param 请求参数,controller里面的对象 * @param username 用户名 * @param password 密码 */ protected <T> T execute(String requestControllerMethodPath, HttpMethod method, Object param, String username, String password, Class<T> clazz) { if (!requestControllerMethodPath.startsWith("/")) { requestControllerMethodPath = "/" + requestControllerMethodPath; } HttpHeaders headers = new HttpHeaders(); headers.set(HttpHeaders.AUTHORIZATION, OAuth2AccessToken.BEARER_TYPE + " " + obtainAccessToken(username, password)); HttpEntity entity = new HttpEntity<>(param, headers); String endpoint = API_URL + requestControllerMethodPath; log.info("request->{}" + entity); ResponseEntity responseEntity = restTemplate.exchange(endpoint, method, entity, clazz); log.info("body->{}", responseEntity.getBody()); TestCase.assertEquals(HttpStatus.OK, responseEntity.getStatusCode()); if (null == responseEntity.getBody()) { return null; } return (T) responseEntity.getBody(); } /** * 获取登录token * @param username 用户名 * @param password 密码 * @return */ protected String obtainAccessToken(String username, String password) { return obtainAccessInfo(username, password).getStr("access_token"); } protected JSONObject obtainAccessInfo(String username, String password) { HttpHeaders headers = new HttpHeaders(); headers.set(HttpHeaders.AUTHORIZATION, "Basic dGVzdDp0ZXN0"); HttpEntity entity = new HttpEntity<>(null, headers); String endpoint = "http://localhost:9999/auth/oauth/token?username="+ username + "&password="+ password +"&randomStr=77371563933589077&code=dw2m&grant_type=password&scope=server"; ResponseEntity responseEntity = restTemplate.exchange(endpoint, HttpMethod.POST, entity, String.class); log.info("token->{}", responseEntity.getBody()); assertEquals(HttpStatus.OK, responseEntity.getStatusCode()); JSONObject userInfo = JSONUtil.parseObj(responseEntity.getBody()); return userInfo; } }