反射
clazz.getDeclaredConstructor().newInstance()
优于 clazz.newInstance()
需要注意的是,如果你想调用的构造函数是 public 的,那么可以直接使用 clazz.newInstance(args) 来创建实例,它与 clazz.getDeclaredConstructor().newInstance(args) 的效果是一样的。但是如果构造函数是 protected、private 或默认的(即没有修饰符),那么就必须使用 getDeclaredConstructor() 方法来获取构造函数,否则会抛出 NoSuchMethodException 异常。
Java 8
LocalDateTime/LocalDate 加减运算
1
2
3
4
5
6
7
8
// 在 Java 8 中,可以使用 plus 和 minus 方法实现 LocalDateTime 和 LocalDate 的加减运算。
// 例如,计算 7 天后的 LocalDateTime,可以使用以下代码:
LocalDateTime now = LocalDateTime.now();
LocalDateTime later = now.plusDays(7);
// 类似地,如果要计算 7 天前的 LocalDate,可以使用以下代码:
LocalDate today = LocalDate.now();
LocalDate before = today.minusDays(7);
除了 plusDays() 和 minusDays(),还可以使用类似的方法 plusHours()、plusMinutes()、plusSeconds()等,来进行加减运算。
Java 并发
什么叫做线程自旋
很多synchronized里面的代码只是一些很简单的代码,执行时间非常快,此时等待的线程都加锁可能是一种不太值得的操作,因为线程阻塞涉及到用户态和内核态切换的问题。既然synchronized里面的代码执行地非常快,不妨让等待锁的线程不要被阻塞,而是在synchronized的边界做忙循环,这就是自旋。如果做了多次忙循环发现还没有获得锁,再阻塞,这样可能是一种更好的策略。
线程阻塞涉及到用户态和内核态切换
阻塞线程会释放cpu资源,因而会由内核态切换到用户态,当需要抢占cpu资源的时候就会进入内核态
Linux
curl命令 curl 是常用的命令行工具,用来请求 Web 服务器。它的名字就是客户端(client)的 URL 工具的意思。它的功能非常强大,命令行参数多达几十种。如果熟练的话,完全可以取代 Postman 这一类的图形界面工具。
进入用户主目录
cd ~
获取root权限
su
,然后输入密码;exit
,退出root
切换到更目录
cd /
Linux中的隐藏文件默认以’.’开头
请求方式和postman
- postman发送Ajax——post请求:headers设置:X-Requested-With:XMLHttpRequest,Content-Type:application/x-www-form-urlencoded,在x-www-form-urlencoded中填写数据
系统设计
- 业务往往伴随着参数校验
后端与数据库交互
- 数据库的日期类型是date,后端参数为String时也可以存入,取数据的时候,也可以直接赋值给String,范围查询也可以直接使用日期字符串作为参数,总结似乎存在一个转换器能自动将String转换成数据库类型Date,和将数据库类型Date转换成String,同时月或日只有一位时,数据库会自动补零
日期的格式化
1
2
3
4
5
6
7
8
// SpringBoot提供的日期格式化注解,经测试这个不起作用
@DateTimeFormat
// jackson提供的注解,如果前后端传的数据都是json格式,那么后台接数据,传数据都可以用@JsonFormat ;
@JsonFormat
// fastjson提供的注解,兼具上面两个注解的功能
@JSONField
目前这三个注解还没测试
数据库sql
- default约束不能使用函数
- enum枚举类型的用法,
字段名 enum('0','1');
,枚举类型不能够起到严格的校验,,但可以保证不会有范围外的数据入库,可参考博文: enum的使用 - from后面的派生表必须有别名,否则报错,像下面这段:
1
2
3
4
5
6
7
8
delete from Person where id not in
(
select t.id from # from后面的派生表必须有别名,否则报错
(
select min(id) as id from Person group by Email
) t
)
Spring事务
锁
- 悲观锁:数据库的默认是心啊
- 共享锁:A事务加了共享锁,其他事务也只能加共享锁,能够读但不能改
- 排他锁:A事务加了排他锁,其他事务不能加任何锁,所以既不能读也不能写
- 乐观锁
- 在更新数据前,判断他人是否修改,如果修改放弃更新否则提交
- 通常通过版本号判断数据是否修改,如果被修改版本号+1
传播机制
编程式事务
Spring邮件服务
- Spring Boot application.yml邮件服务配置:
Spring参数校验
- @Validated和@Valid只会在父方法中生效,在实现方法中覆盖方法的参数校验规则会失败且会报错:
javax.validation.ConstraintDeclarationException: HV000151: A method overriding another method must not redefine the parameter constraint configuration
,所以写参数校验时应该在父方法中定义配置,子类继承就好了,不用修改 - 在Service中使用参数校验,需要用@Validated和@Valid组合使用,@Validated的作用是:提示Spring为组件开启数据校验功能,相比与@Valid,Validated能为@Controller、@Service、@Component等标注的组件开启数据校验功能、而@Valid只能为Controller组件开启数据校验,但是@Validated不支持嵌套验证
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import com.flameking.community.entity.User;
import org.springframework.validation.annotation.Validated;
import javax.validation.Valid;
/**
* service接口
*/
@Validated
public interface UserService {
/**
* 根据用户id查询用户信息
* @param id
* @return 帖子对应的发帖人的信息
*/
User selectUserById(Integer id);
/**
* 注册用户信息
* @param user
* @return
*/
int register(@Valid User user);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import lombok.Data;
import javax.validation.constraints.NotBlank;
import java.util.Date;
@Data
public class User {
private Integer id;
@NotBlank
private String username;
@NotBlank
private String password;
private String salt;
@NotBlank
private String email;
private Integer type;
private Integer status;
private String activationCode;
private String headerUrl;
private Date createTime;
}
Spring Boot测试配置
- SpringBoot2.4.x之后,改为默认仅集成JUnit5,干掉了兼容JUnit4,所以如果想使用Junit4可以选择使用低版本的(低版本同时兼容Junit5和Junit4),当然也可以直接引入Junit4的依赖
Spring Boot数据库配置
- 数据库配置问题:
java.sql.SQLException: No timezone mapping entry for 'HongKong'
,将其修改为serverTimezone=Asia/Shanghai - 数据库serverTimezone配置的不一样可能会使得数据库日期类查询与我们当前时间有差别,比如可能差8个小时,但我们一般设置为serverTimezone=Asia/Shanghai是没有问题的,另外MySQL8.0+版本不写serverTimezone好像会报错,虽然我用的是5.7.33
Spring Boot Bean的问题
- IDEA,注入的mapper类总是报错:
Spring Boot mybatis配置的问题
type-aliases-package的作用:
sql 输出日志配置:
1
2
3
4
5
# 如果是 mybatis,mybatis-plus => mybatis
mybatis-plus:
mapper-locations: classpath:/mapper/*.xml
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
Spring Boot Thymeleaf引擎
- Thymeleaf命名空间:xmlns:th=”http://www.thymeleaf.org”
- Thymeleaf的公共页面复用:
注意:复用的原理是直接用公共部分替换原来的元素,所以其实原来的元素标签与复用的公共部分标签不同也没问题,样式不同也没问题,因为最后都会替换成公共部分
Spring Boot 日志
java web 下有好几种日志框架,比如:logback,log4j,log4j2(slj4f 并不是一种日志框架,它相当于定义了规范,实现了这个规范的日志框架就能够用 slj4f 调用)。其中性能最高的应该是 logback 了,而且 springboot 默认使用的也是 logback 日志,在application.yml中我们可以定义日志:
在application中我们只能对日志进行简单的配置,实际业务需求中我们可能需要:将不同级别的日志放在不同的文件中,每个项目的日志放在项目文件下;要进行这样复杂的配置就需要使用到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
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<contextName>community</contextName>
<property name="LOG_PATH" value="E:/log/"/>
<property name="APPDIR" value="community"/> <!--日志最终会被存在D:/work/data/community下-->
<!-- error file -->
<appender name="FILE_ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/${APPDIR}/log_error.log</file> <!--该日志命名为log_error.log-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/${APPDIR}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <!--新生成日志存放的地址-->
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>5MB</maxFileSize> <!--日志内容最大不能超过5MB,否则会另外生成新的文件存储-->
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>30</maxHistory> <!--日志文件的最大存储期限是30天,超过会自动删除-->
</rollingPolicy>
<append>true</append>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d %level [%thread] %logger{10} [%file:%line] %msg%n</pattern> <!--日志输出格式-->
<charset>utf-8</charset> <!--日志字符集utf-8支持中文-->
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter"> <!-- 在 appender 中设置 filter 子节点,在默认级别上再此过滤,配置 onMatch,onMismatch 可实现只输出单个级别,当前配置只会在文件中输出 error 级别的日志-->
<level>error</level> <!--过滤级别是error-->
<onMatch>ACCEPT</onMatch> <!--如果匹配就接受否则拒绝-->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- warn file -->
<appender name="FILE_WARN" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/${APPDIR}/log_warn.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/${APPDIR}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>5MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>30</maxHistory>
</rollingPolicy>
<append>true</append>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d %level [%thread] %logger{10} [%file:%line] %msg%n</pattern>
<charset>utf-8</charset>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>warn</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- info file -->
<appender name="FILE_INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/${APPDIR}/log_info.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/${APPDIR}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>5MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>30</maxHistory>
</rollingPolicy>
<append>true</append>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d %level [%thread] %logger{10} [%file:%line] %msg%n</pattern>
<charset>utf-8</charset>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>info</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- console -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d %level [%thread] %logger{10} [%file:%line] %msg%n</pattern>
<charset>utf-8</charset>
</encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>debug</level>
</filter>
</appender>
<logger name="com.flameking.community" level="debug"/> <!--com.nowcoder.community包下的日志级别是debug,sql 语句的打印结果是 debug 级别,所以这里 level 如果设置 debug 以上的级别,如 info,就不会显示结果,只会有 sql 语句-->
<root level="info"> <!--整个项目下所有的jar包日志级别是info,低于info的debug、trace都不会输出-->
<appender-ref ref="FILE_ERROR"/>
<appender-ref ref="FILE_WARN"/>
<appender-ref ref="FILE_INFO"/> <!--日志级别从大到小:error、warn、info、debug、trace-->
<appender-ref ref="STDOUT"/>
</root>
</configuration>
首先这个文件是被Spring Boot自动识别的,我们只需要将其放在resources文件夹下,并将其命名为logback-spring.xml
,当然根据不同的环境这些命名都是可以的 logback-spring.xml, logback-spring.groovy, logback.xml, logback.groovy,其中 logback-spring.xml、logback.xml 在 Spring Boot 环境的下都是可以的,然后在代码中我们就可以使用 @slf4j
来打印日志了
当然也可自定义命名,想要自定义文件名可配置:logging.config 指定配置文件名:
1
2
logging:
config: classpath:logging-config.xml
像上面我们的自定义命名是:logging-config.xml
什么时候改打印什么日志
logback.xml 获取到 spring boot yaml 文件中的配置
SpringMVC转发和重定向
- 我们在使用重定向的时候往往是为了解耦两个不相关的业务,比如登录和注册、删除记录和查询记录
- 转发关键词:forward,重定向:redirect
- 两个不同类型的请求互相转发会报错
- 请求转发后存入model的参数在目标请求的model中是不存在的,此时需要使用@RequestAttribute注解获取对应的参数
- 请求转发后依然会进入拦截器
SpringMVC请求注解
@RequestBody
,这个是专门用来接收前端json数据的,这些数据放在请求体中
SpringMVC接收参数
- 对于checkbox的输入框,在是否选择时,如果后端使用Boolean类型接收,当没选中时,Boolean接收值总是null,而用boolean就没问题
- application/x-www-form-urlencoded 的参数,后端使用 JavaBean 接收会出现属性值为空的情况,这时推荐直接用 @RequestParam 配合属性参数接收,而不是 Javabean,同时也可使用原生的 HttpServletRequest 进行接收,好像还可以使用 Map(paramMap) 类型进行接收。
SpringMVC拦截器
- 如果任何一个拦截器的preHandle方法返回false或者抛出异常,或者handler方法中抛出异常都不会执行postHandle方法。
mybatis,mapper.xml和mapper.java需要注意的问题
使用注解@Param的时机:
- 在动态SQL中用到了参数作为判断条件,就算SQL对应的的方法有且只有这一个参数,如果不加@Param就会报错,比如使用 if 和 when 动态参数
1
2
3
4
5
6
<select id="getUserById" resultType="com.flameking.community.User">
select * from user
<if test="id!=null">
where id=#{id}
</if>
</select>
1
2
3
4
@Mapper
public interface UserMapper {
List<User> getUserById(@Param("id")Integer id);
}
- 需要为参数指定别名
- 有多个参数时
- mapper.xml中的SQL使用了$,那么参数中也需要@Param注解,$ 会有注入漏洞的问题,但是有的时候你不得不使用$ 符号,例如要传入列名或者表名的时候,这个时候必须要添加 @Param 注解,例如:
1
2
3
4
@Mapper
public interface UserMapper {
List<User> getAllUsers(@Param("order_by")String order_by);
}
1
2
3
4
5
6
<select id="getAllUsers" resultType="com.flameking.community.User">
select * from user
<if test="order_by!=null and order_by!=''">
order by ${order_by} desc
</if>
</select>
ajax请求
1
2
3
4
5
6
7
var json
const target = $.getJSON("/power/electron/" + getDateByOffset(offset), (res)=>{
console.log(res) //这里是可以得到JSon数据的
json = res
})
console.log(json) //但是就算再回调函数中赋值了json,函数外输出时值依然是undefined,因为函数里面的数据是异步加载的
json = target.responseJSon
因为ajax异步加载的原因,responsJSon我们再函数之外是获取不到的
关闭异步的方法:
1
$.ajaxSettings.async = false;
但是执行过后记得重新开启起步$.ajaxSettings.async = true;
,避免影响到其他的ajax异步请求
前缀树过滤敏感词
前缀树的特点
- 根节点为null
- 除了根节点其余节点只存储一个字符,且每个子节点包含的字符互不相同,如果相同则合并
- 根节点的子节点到叶节点连起来的字符是一个敏感词
- 会标记节点是否为叶节点
- 存在三个指针、指针一遍历前缀树,指针2和指针3作为开始指针和结尾指针遍历字符串
前缀树为什么块
某种意义上树的分支越多,查询效率越快
Redis
- key只允许String类型
- 基本数据类型:String、Hash、List、Set、SortedSet
- Redis为什么这么快:把所有数据存储在内存中,内存操作远比磁盘IO快,采用的IO多路复用技术(多个连接复用同一个线程)
- 数据持久化:
- 快照(RDB):一次性把数据存入磁盘、适合数据体积小,以及每小时备份一次
- AOF,存储操作日志,并且对操作日志进行追加存,缺点比较占用空间,数据回复比较慢(取出日志文件并重新执行一遍,比从磁盘中直接取出数据要慢),优点实时较号(因为存日志的操作比较快)
- Redis学习简单使用简单(主要学会命令操作)
- 常见应用场景:缓存、排行榜、计数器、社交网络(点赞、点踩、关注、收藏)
- SortedSet可以使用Range进行分页查询
Vue
计算属性computed
推荐使用计算属性来描述依赖响应式状态的复杂逻辑,我们在计算属性中定义某个方法A,这个方法里面就是复杂的计算逻辑,使用的时候就像调用属性一样直接就行了,注意不用加()
占位符
特指router中的path定义:/profile/:username,即:username就是占位符,是用来传参的
beforeRouteEnter
beforeRouteEnter 函数内部的 this 为 undefined,这是因为 beforeRouteEnter 是在页面创建前就执行的,先执行 beforeRouteEnter,再执行生命周期钩子函数 beforeCreate、created 等。
beforeRouteEnter 有三个参数,to、from、next to:里面是当前页面的路由对象。 from:里面是上一个页面的路由对象。 next:表示进入当前页面,beforeRouteEnter 内必须执行 next() ,否则无法进入页面,可以传入参数 vm 访问组件实例,相当于 this
beforeRouteUpdate
使用场景:组件复用,路由跳转;
1
2
3
4
5
6
beforeRouteUpdate (to, from, next) {
// 在当前路由改变,但是该组件被复用时调用(即路由参数不同)
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
beforeRouteLeave
1
2
3
4
beforeRouteLeave (to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
}
全局事件总线
this.$bus.$emit('方法名', data)
,其中data是传递的数据,这个方法会触发某个组件绑定在bus上的方法,然后该组件的方法就会在组件内部触发执行 this.$bus.$on('方法名', this.method)
,第二个参数就是组件在method中定义的方法,第一个参数就是emit触发方法时传递的第一个参数