码迷,mamicode.com
首页 > 编程语言 > 详细

Java开发工作中常见问题

时间:2020-07-07 13:17:15      阅读:140      评论:0      收藏:0      [点我收藏+]

标签:tostring   href   big   oauth   object   验证   exist   out   note   

1.开发工具

1.1 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.2 github提交一个项目

1.github提交一个项目
(1)github添加新仓库jucdemo2
(2)添加客户端id_ras.pub秘钥(C:\Users\Czz\.ssh\id_ras.pub)
(3)添加远程仓库:
# 初始化本地仓库,并提交到本地仓库
$ cd D:\ideawk\jucdemo
$ 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

1.3 idea-pom项目无法识别

File->Settings->Build,Excecution,Deployment->Build Tools->Maven->Ignored Files

查看是否存在maven pom被勾选,去掉勾选即可。

2.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

3.Spring

3.1 spring-auth2.0 自定义异常输出

参考网址

技术图片
// 核心原理:定义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.将自定义异常处理类添加到认证服务器配置
View Code

3.2 SpringBoot添加定时任务

技术图片
// 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());
   }
}
View Code

3.3 springboot+redis+AOP分布式

参考网址

3.4 spring-cloud引入swagger

技术图片
@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();
    }
}
View Code

3.5 spring-MockMvc请求方式

// 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)); 

3.6 spring-enable-xx注解原理

[参考网址1]
[参考网址2]

所有@Enable* 注解都是有@Import的组合注解,@Enable* 自动开启的实现其实就是导入了一些自动配置的Bean,@Import 注解的最主要功能就是导入额外的配置信息。

3.6.1 @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();
    }
}
View Code
3.6.2 实现 ImportSelector 接口
技术图片
/**
如果并不确定引入哪个配置类,需要根据@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;
        }
    }
}
View Code
3.6.3 实现 ImportBeanDefinitionRegistrar接口
技术图片
/**
一般只要用户确切知道哪些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);
            }
        }
    }
}
View Code
3.6.4 springboot自定义日志格式
技术图片
<!-- 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>
View Code
3.6.5 引入springboot-task
技术图片
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()));
    }
}
View Code
3.6.6 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被调用
*/
View Code
3.6.7 SpringBoot自定义配置文件
技术图片
@Data
@ConfigurationProperties("example.service")
@Component
public class WrapServiceProperties {
    private String prefix;
    private String suffix;
}

<!-- application.properties -->
example.service.prefix=###
example.service.suffix=@@@
View Code
3.6.8 Springboot-引入xxl-job
技术图片
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
View Code
3.6.9 Springboot-引入ActiveMQ
技术图片
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);
    }
}
View Code
3.6.10 springboot引入动态quartz
技术图片
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;
    }
}
View Code
3.6.11 springboot配置全局异常
技术图片
@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;
    }
}
View Code
3.6.12 spring获取某个类的所有子类
String[] beanNamesForType = SpringContextHolder.getApplicationContext().getBeanNamesForType(AbstractObserver.class);
3.6.13 spring-feign传多个参数
技术图片
@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);
}
View Code

4.Mysql

4.1 初始化数据库、用户

使用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;

4.2 导出数据库和表

-- 导出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

4.3 导入sql脚本

$ mysql -u test -p123456 db_name < /opt/sql/xx.sql

4.4 添加索引

技术图片
-- 添加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 ;
View Code

4.5 修改表字段

技术图片
-- 添加表的字段 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=健康表;
View Code

4.6 Mysql日期操作

-- mysql日期格式化
DATE_FORMAT( created_date, "%Y-%m-%d")

4.7 常用查询

-- 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%;

4.8 查看mysql表结构和表创建语句

-- 查看所有表
show tables;
-- 查看表结构
desc sys_log;
-- 查看建表语句
show create table tablename;

4.9 mysql慢sql查询

-- show processlist;
-- show variables like ‘%tmp%‘;
-- show variables like ‘%query%‘;

5.Mybatis

5.1 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;
    }
}
View Code

5.2 MybatisPlus-公共类

技术图片
/**
 * 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);
    }
}
View Code

5.3 MybatisPlus常用注解

技术图片
/**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)
View Code

5.4 MybatisPlus lamba表达式使用

技术图片
$condition = Wrappers.<~>query().lambda().eq(SysUserRole::getRoleId, 5);
xxService.list($condition)
xxService.getOne($condition)
...
View Code

5.5 MybatisPlus分页

技术图片
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; 
}
View Code

5.6 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());
View Code

5.7 MybatisPlus自定义sql

技术图片
List<User> selectMyPage(RowBounds rowBounds, @Param("ew") Wrapper<T> wrapper);
<select id="selectMyPage" resultType="User"> 
    SELECT * FROM user 
    <where> 
        ${ew.sqlSegment} 
    </where> 
</select>
View Code

5.8 Mybatis-like

技术图片
<if test="xx.name != null and xx.name != ‘‘">
    AND name like CONCAT(‘%‘,#{xx.name},‘%‘)
</if>
View Code

5.9 MybatisPlus-update

技术图片
// 方式一
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);
}
View Code

5.10 Mybatis-in

技术图片
<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>
View Code

5.11 Mybatis-time-between

技术图片
<if test="xx.startTime != null">
    AND create_time &gt;= #{xx.startTime}
</if>
<if test="xx.endTime != null">
    AND create_time &lt;= #{xx.endTime}
</if>
View Code

 

6.其它

6.1 Idea代码模板

技术图片
// 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();
}
View Code

6.2  hutool工具类

官网

6.3 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);
View Code

6.4 常用正则工具类

技术图片
/**
 * 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 -> [ \t\v\n\r\f] 空白
// \S -> [^ \t\v\n\r\f] 非空白
// . -> [^\n\r\u2028\u2029] 通配符,表示几乎任意字符。换行符、回车符、行分隔符和段分隔符除外。
// 匹配任意字符 [\d\D]、[\w\W]、[\s\S]、和[^]中任何的一个

// 9.量词
// {m,} -> 至少出现m次
// {m} -> 出现m次
// ? -> 0或1次
// + -> 至少出现1次
// * -> 出现任意次,有可能不出现
View Code

6.5 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;
    }
}

Java开发工作中常见问题

标签:tostring   href   big   oauth   object   验证   exist   out   note   

原文地址:https://www.cnblogs.com/ice-line/p/13259859.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!