0%

Springboot @value 注解注入静态属性

1
2
3
4
5
6
7
8
9
10
11
12

@Component
public class FileTool {

private static String proxyIp;

@Value("${cim.http.proxy_ip}")
public void setProxyIp(String _proxyIp) {
proxyIp = _proxyIp;
}
}

直接在属性上使用 @value 是无效的,要通过 set 方法注入, class 要加上 @Component 注解

mybatis-plus简称MP是一个 Mybatis 的增强工具,在 Mybatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

这是官方给的定义,关于mybatis-plus的更多介绍及特性,可以参考mybatis-plus官网 。那么它是怎么增强的呢?其实就是它已经封装好了一些crud方法,我们不需要再写xml了,直接调用这些方法就行,就类似于JPA。

下面是在MP为基础封装了一个查询类,实现了如果需要增加查询条件只需在前端修改即可。

查询工具

  • SearchModel
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    public class SearchModel<T> {

    private Integer pageIndex;
    private Integer pageSize;
    private List<Field> fields;
    private String orderField;
    private boolean isAsc;

    public IPage<T> getPage() {
    IPage<T> page = new Page<>(pageIndex, pageSize);
    if (!StringUtil.isEmptyOrNull(orderField)) {
    OrderItem orderItem = new OrderItem();
    orderItem.setAsc(isAsc);
    orderItem.setColumn(orderField);
    page.orders().add(orderItem);
    }
    return page;

    }

    public QueryWrapper<T> getQueryModel() {
    QueryWrapper<T> queryWrapper = new QueryWrapper<>();
    for (Iterator iter = this.fields.iterator(); iter.hasNext(); ) {
    Field field = (Field) iter.next();
    switch (field.getQueryMethod()) {
    case eq:
    queryWrapper.eq(true, field.getName(), field.getValue());
    break;
    case like:
    queryWrapper.like(true, field.getName(), field.getValue());
    }
    }

    if (!StringUtil.isEmptyOrNull(orderField)) {
    queryWrapper.orderBy(true, isAsc, orderField);
    }
    return queryWrapper;
    }
    }
  • Field
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public class Field {
    public Field(String name, Object value) {
    this.name = name;
    this.value = value;
    this.queryMethod = QueryMethod.eq;
    }

    public Field(String name, Object value, QueryMethod queryMethod) {
    this.name = name;
    this.value = value;
    this.queryMethod = queryMethod;
    }

    private String name;
    private Object value;
    private QueryMethod queryMethod;
    }
  • QueryMethod
    1
    2
    3
    4
    public enum QueryMethod {
    eq, like
    }

    调用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
"fields": [
{
"value": "v",
"name": "project_code",
"queryMethod": "eq"
},
{
"name": "type",
"queryMethod": "like",
"value": "b"
},
{
"name": "id",
"queryMethod": "like",
"value": "a"
}
],
"pageIndex": 1,
"pageSize": 8,
"orderField": "type",
"isAsc": "false"
}

在api中传入上面的json对象即可完成一个查询服务,查询条件通过前端传入的字段控制

BaseService

  • IBaseService

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48

    public interface IBaseService<T> {

    T save(T entity) throws Exception;

    boolean saveBatch(Collection<T> entityList);


    // TableId 注解存在更新记录,否插入一条记录
    boolean saveOrUpdate(T entity);

    // 根据updateWrapper尝试更新,否继续执行saveOrUpdate(T)方法
    boolean saveOrUpdate(T entity, Wrapper<T> updateWrapper);

    // 批量修改插入
    boolean saveOrUpdateBatch(Collection<T> entityList);

    // 根据 entity 条件,删除记录
    boolean remove(Wrapper<T> queryWrapper);

    // 根据 ID 删除
    boolean removeById(Serializable id);

    // 根据 columnMap 条件,删除记录
    boolean removeByMap(Map<String, Object> columnMap);

    // 删除(根据ID 批量删除)
    boolean removeByIds(Collection<? extends Serializable> idList);

    List<T> list();

    // 查询列表
    List<T> list(SearchModel<T> searchModel);

    // 查询(根据ID 批量查询)
    Collection<T> listByIds(Collection<? extends Serializable> idList);

    // 查询(根据 columnMap 条件)
    Collection<T> listByMap(Map<String, Object> columnMap);

    // 翻页查询
    IPage<T> page(SearchModel<T> searchModel);

    T selectById(Serializable id);

    T selectOne(Wrapper<T> queryWrapper);
    }

  • BaseServiceImpl

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    public class BaseServiceImpl<M extends BaseMapper<T>, T> implements IBaseService<T> {

    @Autowired
    private M baseMapper;


    @Override
    public T save(T entity) throws Exception {
    baseMapper.insert(entity);
    return entity;
    }

    @Transactional(rollbackFor = RuntimeException.class)
    @Override
    public boolean saveBatch(Collection<T> entityList) {
    Integer size = entityList.size();
    for (T entity : entityList) {
    baseMapper.insert(entity);
    size++;
    }
    return size == entityList.size();
    }

    @Override
    public boolean saveOrUpdate(T entity) {
    int rs = baseMapper.updateById(entity);
    if (rs > 0) return true;
    return baseMapper.insert(entity) > 0;
    }

    @Override
    public boolean saveOrUpdate(T entity, Wrapper<T> updateWrapper) {


    return false;
    }

    @Transactional(rollbackFor = RuntimeException.class)
    @Override
    public boolean saveOrUpdateBatch(Collection<T> entityList) {

    for (T entity : entityList) {
    saveOrUpdate(entity);
    }
    return true;
    }

    @Override
    public boolean remove(Wrapper<T> queryWrapper) {
    return baseMapper.delete(queryWrapper) > 0;
    }

    @Override
    public boolean removeById(Serializable id) {
    return baseMapper.deleteById(id) > 0;
    }

    @Override
    public boolean removeByMap(Map<String, Object> columnMap) {
    return baseMapper.deleteByMap(columnMap) > 0;
    }

    @Override
    public boolean removeByIds(Collection<? extends Serializable> idList) {
    return baseMapper.deleteBatchIds(idList) > 0;
    }

    @Override
    public List<T> list() {
    return baseMapper.selectList(new QueryWrapper<T>());
    }

    @Override
    public List<T> list(SearchModel<T> searchModel) {
    return baseMapper.selectList(searchModel.getQueryModel());
    }

    @Override
    public Collection<T> listByIds(Collection<? extends Serializable> idList) {
    return baseMapper.selectBatchIds(idList);
    }

    @Override
    public Collection<T> listByMap(Map<String, Object> columnMap) {
    return baseMapper.selectByMap(columnMap);
    }


    @Override
    public IPage<T> page(SearchModel<T> searchModel) {
    return baseMapper.selectPage(searchModel.getPage(), searchModel.getQueryModel());

    }

    @Override
    public T selectById(Serializable id) {
    return baseMapper.selectById(id);
    }

    @Override
    public T selectOne(Wrapper<T> queryWrapper) {
    return baseMapper.selectOne(queryWrapper);
    }
    }

MP实现了mapper层基础的CRUD方法,这里把一些常用的service层的方法整理了一下,又减少了一些代码量

为了项目的正常运行中,异常捕获,记录也是非常重要的,方便我们排查问题,定位问题

定义异常

为了方便定位异常,自定义了几种异常类,方便我们快速定位异常。

基类

1
2
3
4
5
public class HttpException extends RuntimeException {
protected String code;
protected Integer httpStatusCode = 500;
}

ParameterException

1
2
3
4
5
6
public class ParameterException extends HttpException {
public ParameterException(String code){
this.code = code;
this.httpStatusCode = 400;
}
}

ServerErrorException

1
2
3
4
5
6
7
public class ServerErrorException extends HttpException {
public ServerErrorException(String code) {
this.code = code;
this.httpStatusCode = 500;
}
}

UnAuthenticatedException

1
2
3
4
5
6
7
public class UnAuthenticatedException extends HttpException{
public UnAuthenticatedException(String code){
this.code = code;
this.httpStatusCode = 401;
}
}

ForbiddenException

1
2
3
4
5
6
public class ForbiddenException extends HttpException {
public ForbiddenException(String code) {
this.code = code;
this.httpStatusCode = 403;
}
}

NotFoundException

1
2
3
4
5
6
7
public class NotFoundException extends HttpException {
public NotFoundException(String code){
this.httpStatusCode = 404;
this.code = code;
}
}

这里定义了我在项目中常用的几种异常,也可根据实际情况定义自己所需的异常。

捕获异常

捕获异常需要用到一个注解@ControllerAdvice,关于它的详细解释可查看文档

使用方法如下,定义一个异常捕获类

1
2
3
4
@ControllerAdvice
public class GlobalExceptionAdvice {

}

这个类就已经实现了捕获全局异常的功能,下面在加上上面定义的几种异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@ControllerAdvice
public class GlobalExceptionAdvice {
@ExceptionHandler(UnAuthenticatedException.class)
public ResponseEntity unAuthenticatedException(UnAuthenticatedException e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(e.getCode());
}


@ExceptionHandler(ParameterException.class)
public ResponseEntity handleParameterException(ParameterException e) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getCode());
}

@ExceptionHandler(ForbiddenException.class)
public ResponseEntity handleForbiddenException(ForbiddenException e) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(e.getCode());
}

@ExceptionHandler(NotFoundException.class)
public ResponseEntity handleNotFoundException(NotFoundException e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(e.getCode());
}

@ExceptionHandler(RuntimeException.class)
public ResponseEntity handleRunTimeException(RuntimeException e) {

return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(500);
}


}

@ExceptionHandler注解表示该方法捕获的异常类型,就可以在不同的异常中进行不同的处理方式。

记录异常

捕获到异常之后我们要记录下来,方便我们对bug的追踪解决。

记录方法有多种多样的,比如记录到数据库或者log文件中。我使用了第二种方式。

加入依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-jcl</artifactId>
<version>2.2</version>
</dependency>

增加日志配置文件

logback.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
  <?xml version="1.0" encoding="UTF-8"?>
<configuration>

<!-- 控制台 appender, 几乎是默认的配置 -->
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<encoder charset="UTF-8">
<!-- 输出的日志文本格式, 其他的 appender 与之相同 -->
<pattern> %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} %L - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>

<!-- info 级别的 appender -->
<appender name="info" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 日志写入的文件名, 可以是相对目录, 也可以是绝对目录, 如果上级目录不存在会自动创建 -->
<file>./logs/info/log-stack.log</file>
<!-- 如果是 true, 日志被追加到文件结尾; 如果是 false, 清空现存文件. 默认是true -->
<append>true</append>
<!-- 日志级别过滤器, 只打 INFO 级别的日志-->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<!-- 下面2个属性表示: 匹配 level 的接受打印, 不匹配的拒绝打印 -->
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<!-- 最常用的滚动策略, 它根据时间来制定滚动策略, 既负责滚动也负责触发滚动 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 设置滚动文件规则, 如果直接使用 %d, 默认格式是 yyyy-MM-dd -->
<fileNamePattern>./logs/info/log-stack.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 保留14天的日志 -->
<maxHistory>30</maxHistory>
</rollingPolicy>
<!-- 定义日志输出格式 -->
<encoder charset="UTF-8">
<pattern> %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} %L - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>

<!-- error 级别的 appender -->
<appender name="error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>./logs/error/log-stack.log</file>
<append>true</append>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>./logs/error/log-stack.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 保留7天的日志 -->
<maxHistory>30</maxHistory>
</rollingPolicy>
<!-- 定义日志输出格式 -->
<encoder charset="UTF-8">
<pattern> %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} %L - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- error 级别的 appender -->
<appender name="debug" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>./logs/debug/log-stack.log</file>
<append>true</append>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>DEBUG</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>./logs/debug/log-stack.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 保留7天的日志 -->
<maxHistory>30</maxHistory>
</rollingPolicy>
<!-- 定义日志输出格式 -->
<encoder charset="UTF-8">
<pattern> %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} %L - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 指定 com.github 下的日志打印级别, appender -->
<logger name="com.github" level="debug" additivity="false">
<appender-ref ref="stdout"/>
<appender-ref ref="info"/>
<appender-ref ref="error"/>
<appender-ref ref="debug"/>

</logger>


<root level="info">
<appender-ref ref="stdout"/>
<appender-ref ref="info"/>
<appender-ref ref="error"/>
</root>

</configuration>

写入日志

1
2
3
4
5
6
7
8
9
@ControllerAdvice
@Slf4j
public class GlobalExceptionAdvice {
@ExceptionHandler(ParameterException.class)
public ResponseEntity handleParameterException(ParameterException e) {
log.error(e.getLocalizedMessage());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getCode());
}
}

完善异常信息

文章中的异常只定义了code,具体的异常信息可以写在配置文件中或者保存在数据库中,在捕获到异常之后,找到对应的描述信息返回调用者,增加友好度。

完善记录日志

以上如果发生了异常,在日志文件中是这样记录的

1
10:19:32.024 [http-nio-8080-exec-2] ERROR c.g.e.d.advice.GlobalExceptionAdvice 41 - / by zero

发现记录的行号是在GlobalExceptionAdvice类中,并非是代码真实的位置。

如果要记录到代码的真实位置可以这样实现

1
2
3
4
5
6
7
8
9
10
11
12
public  String getExceptionDetail(Exception e) {

StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(e.getClass() + System.getProperty("line.separator"));
stringBuilder.append(e.getLocalizedMessage() + System.getProperty("line.separator"));
StackTraceElement[] arr = e.getStackTrace();
for (int i = 0; i < arr.length; i++) {
stringBuilder.append(arr[i].toString() + System.getProperty("line.separator"));

}
return stringBuilder.toString();
}
1
log.error(getExceptionDetail(e));

根据实际情况选择适合自己的方式

完整代码

Babel 是什么

Babel 是一个工具链,主要用于将 ECMAScript 2015+ 版本的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。

Babel能用在哪里

NodeJs

使用 npm 初始化项目

script
1
2
$ mkdir es6-to-es5
$ npm init -y

安装Babel

script
1
$ npm install --save-dev @babel/core @babel/cli

安装完成之后, 你的 package.json 会加入这些代码

1
2
3
4
"devDependencies": {
+ "@babel/cli": "^7.12.8",
+ "@babel/core": "^7.12.9"
}

现在把 Babel 的运行命令放入到 npm 脚本中, 也是在 package.json

1
2
3
4
 "scripts": {
+ "build": "babel src -d lib"

}

创建.babelrc配置文件

script
1
2
$ npm install @babel/preset-env --save-dev

创建babel.config.json文件,写入以下内容

1
2
3
{
"presets": ["@babel/preset-env"]
}

环境都配置完成, 下面开始正式写代码了

写一段ES6代码

创建 /src/index.js

script
1
2
3
$  mkdir src
$ touch src/index.js

写入一个简单的箭头函数

1
2
3
4
5
6
let sayHello = () => {
console.log('hello xiaotaideng')
}

sayHello()

现在运行刚刚写好的启动脚本

script
1
2
$ npm run build

完成之后可以看到目录中新增了一个build文件夹,打开里面的index.js它的内容是这样的

1
2
3
4
5
6
7
"use strict";

var sayHello = function sayHello() {
console.log('hello xiaotaideng');
};

sayHello();

现在执行下面的命令

1
$  node build/index.js

可以正常的输出,到现在好像已经可以正常的使用了

点击获取源码

安装

使用nvm安装

1
$ wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.34.0/install.sh | bash

提示内容:
=> Downloading nvm as script to ‘/home/dhbm/.nvm’
=> Appending nvm source string to /home/dhbm/.bashrc
=> Appending bash_completion source string to /home/dhbm/.bashrc
=> Close and reopen your terminal to start using nvm or run the following to use it now:
export NVM_DIR=”$HOME/.nvm”
[ -s “$NVM_DIR/nvm.sh” ] && . “$NVM_DIR/nvm.sh” # This loads nvm
[ -s “$NVM_DIR/bash_completion” ] && . “$NVM_DIR/bash_completion” # This loads nvm bash_completion

按照提示,直接 copy 粘贴以上内容

1
2
3
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion
1
2
$ nvm install node # 安装nodejs
$ npm config set registry http://registry.npm.taobao.org/ # 设置淘宝镜像

开发环境设置

上一章

在日常开发的过程中,debug是避免不了的,我们都知道webpack是把文件打包了的,对于debug来说是非常不方便的,本章主要说一下怎么debug

准备环境

修改 webpack.config.js

1
2
3
module.exports = {
// mode: "development"
}

把这行代码注释掉

新增一个ts文件

src目录下新增一个名为Person.ts的文件,内容如下

修改index.ts文件

之前的代码全部注释或删除,新增以下内容

内容准备完成,现在来启动项目实践debug

启动调试

执行yarn start 现在开始调试项目

启动之后我们会发现在终端中会出现这样一个提示,出现这个提示的原因是因为我们在第一步修改的webpack.config.js文件,webpack不知道该以什么模式启动项目了。
mode的类型有两个,分别是developmentproduction,分别代表开会环境和正式环境。稍后在进行配置。

接下来打开浏览器会看到控制台

我们在调用方法的时候传入的参数是undefined,所以报这个错了,我们也知道怎么去改,可是在开发项目的过程中不可能每一个地方都记得特别清楚,我们是需要控制台去精准的提示给我们是哪里报凑了,从上图中我们可以看到是main.js文件的第2行报的错,我们点击它定位到错误位置。

出现在眼前的是这一行压缩的代码,它是打包过的代码,是不利于调试的。我们要使用devtool来解决这个问题

devtool

选择一种 source map 格式来增强调试过程。不同的值会明显影响到构建(build)和重新构建(rebuild)的速度。

devtool其实就是选择对应的source map,它有很多种,现在来看一下每种都有什么区别。

source-map

这种是比较常见的,设置方法就是在webpack.config.js中修改这个属性,代码如下

1
2
3
4
module.exports = {
mode: "development", //恢复设置
devtool: 'source-map',
}

设置完成之后启动项目看一下效果

精确的提示除了出错的文件和行数,方便我们准确的定位,那webpack究竟是做了什么呢。

  • source-map会给打包后的模块生成soucemap文件

debug-sourcemapurl.png

打包后的模块在最下面引用一个map文件,map文件就是打包后的文件和源文件之间的一个关联文件,里面记录着编译后的代码对应着源码中的位置

  • inline-source-map

使用inlinewebpack不会生成独立的map文件,而是将map文件内容以dataURL的形式插入到打包的文件中

debug-inline.png

  • cheap-source-map
    它与source-map一样也是会生成独立的map文件,不同的是它不包含源代码中的列信息

  • module-source-map

生成的map文件中还包含引用的一些第三方库

  • eval-source-map
    每个模块都被eval执行,并且使用@sourceURL的方式引入map文件

    不同的环境该使用哪种source-map

不同环境的配置使用,建议参考官方文档,只有官方文档才是最新最准确

获取代码

创建ssl证书

1
2
3
4
5
$ mkdir -p /etc/nginx/ssl
$ cd /etc/nginx/ssl
$ openssl genrsa -idea -out server.key 1024
$ openssl req -new -key server.key -out server.csr
$ openssl x509 -req -days 3650 -in server.csr -signkey server.key -out server.crt

注意要加过期时间,默认的有效期很短

Nginx 配置

1
2
$ cd /etc/nginx/conf.d
$ vim https.conf

输入以下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
server {
listen 443 ssl http2 default_server;
listen [::]:443 ssl http2 default_server;
server_name _;
root /usr/share/nginx/html;
ssl_certificate "/etc/nginx/ssl/server.crt";
ssl_certificate_key "/etc/nginx/ssl/server.key";
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 10m;
ssl_ciphers PROFILE=SYSTEM;
ssl_prefer_server_ciphers on;
location / {
}

error_page 404 /404.html;
location = /40x.html {
}

error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}

保存退出并重启nginx

因为我们的证书没有给相关机构认证,所以还是提示不安全,但是不影响我们测试使用

如果想部署个人服务器的话可以在各大服务器厂商申请免费的ssl证书,也是很方便的,前提是要有自己的域名。

我是在腾讯云申请的免费ssl证书。按照官网的提示操作很简单的。

1
2
$ sudo yum install -y epel-release
$ sudo yum install -y nginx

安装成功后,默认的网站目录为: /usr/share/nginx/html

默认的配置文件为:/etc/nginx/nginx.conf

自定义配置文件目录为: /etc/nginx/conf.d/

开启端口

1
2
3
$ sudo firewall-cmd --permanent --zone=public --add-service=http
$ sudo firewall-cmd --permanent --zone=public --add-service=https
$ sudo firewall-cmd --reload

开启80和443端口

常用操作

启动Nginx

1
$ systemctl start nginx

停止Nginx

1
$ systemctl stop nginx

重启Nginx

1
$ systemctl restart nginx

查看Nginx状态

1
systemctl status nginx

设置开机启动

1
$ systemctl enable nginx

禁止开机启动

1
$ systemctl disable nginx

反向代理

1
2
3
4
5
6
upstream tomcats {
server 127.0.0.1:8001;
server 127.0.0.1:8002;
server 127.0.0.1:8003;
}
# 要定义在http模块之内,server模块之外
1
2
3
4
5
6
7
8
9
10
11
12
server {
listen 80 ;
location / {
proxy_pass_header Server;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_pass http://tomcats;

}
}

设置缓存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
proxy_cache_path /opt/app/cache levels=1:2 
keys_zone=lzz_cache:10m max_size=10g inactive=60m use_temp_path=off;

server {

location / {
expires 1h;
proxy_pass_header Server;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_pass http://tomcats;
proxy_cache lzz_cache;
proxy_cache_valid 200 304 1m;
proxy_cache_valid any 10m;
proxy_cache_key $host$uri$is_args$args;
add_header Nginx-Cache "$upstream_cache_status";
proxy_next_upstream error timeout invalid_header http_500http_502 http_503 http_504;

}

}

你现在使用的输入法具体是什么?另外你是用 ibus 的吗?
在启动文件中输入

1
2
3
export XMODIFIERS="@im=ibus"
export GTK_IM_MODULE="ibus"
export QT_IM_MODULE="ibus"

然后启动 WebStorm 试试。
如果不行的话,你再换成下面的试试。

1
2
3
export GTK_IM_MODULE=fcitx
export QT_IM_MODULE=fcitx
export XMODIFIERS=@im=fcitx

如果还不能解决,参考下面的链接操作一下

https://youtrack.jetbrains.com/issue/IDEA-246833

https://www.jetbrains.com/help/idea/switching-boot-jdk.html

https://confluence.jetbrains.com/pages/viewpage.action?pageId=173178989

以上方案由官方提供,亲测好用