spring中线程安全
spring由ThreadLocal来为每一个线程都提供了变量的副本
转载:
史上最全ThreadLocal 详解(一)_倔强的不服的博客-CSDN博客_threadlocal
,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享
1.八大基本类型,byte范围,int几位
八大基本类型分别是:byte, short, int, long,
1字节 2字节 4字节 8字节
boolean、 char, float, double,
byte范围:-128-127
int 32位
2.jre jvm jdk
jre(Java runtime Environment) 运行环境
jvm (Java Virtual Machine) Java虚拟机
jdk (Java Development Kit) java扩展包
3.环境变量配置
找到bin目录然后添加到Path路径下,Java jdk配置还需要单独创建%JAVA_HOME%
4.switch支持类型,String支持吗
5.面向对象特征
封装,继承,多态
6.final修饰特征
final修饰的类不能被继承,有上无下
final修似的变量不能被修改
7.重载重写区别
重载是,同名方法,方法参数不同,参数顺序不同也可以重载
重写是子对父类方法的继承,必须完全一模一样(顺序个数类型)
8.static用法修饰
被static关键字修饰的方法或者变量不需要依赖于对象来进行访问,可以通过类名去进行访问。
静态方法中不能访问类的非静态成员变量和非静态方法,因为非静态成员变量和非静态方法都必须依赖于具体的对象才能被调用。
静态的成员变量只会在数据共享区中维护一份。
静态方法不能直接访问成员变量,除非实例化对象
9.访问控制修饰符种类,修饰范围
修饰词有:private<默认 10.抽象类,接口区别 abstract:抽象类不可以实例化,但可以被子类继承,也有构造方法,子类继承抽象类,必须实现抽象方法,抽象类不能有方法体; interface:接口是特殊的抽象类,所有的方法都是抽象方法,不能定义变量,只能定义常量,使用implements 关键字来实现 11.内部类,匿名内部类 内部类:Java中一个类嵌套的另外一个类叫内部类,内部类可以直接使用外部类属性,外部类需要实例化内部类对象才能调用. 匿名内部类:没有类名的内部类,一般为要实现接口时会创建内部类,避免为了调用一次接口的方法而单独继承实现(new xxx{}) 12.String 常用api int length():获取长度。 char charAt(int index):根据索引来获取对应的字符 int indexOf(int ch):根据字符获取该字符在字符串中位置。 int indexOf(String str, int fromIndex) :从fromIndex指定位置开始,获取str在字符串中出现的位置。 int lastIndexOf(int ch)最后一次出现的位置 boolean contains(str) 是否包含 boolean isEmpty(): 判空 String replace(oldchar,newchar);替换 String[] split(regex);切割 String trim();将字符串两端的多个空格去除。 13.String StringBuilder StringBuffer区别 String在创建完成后不可被修改.被final修饰 StringBuilder,StringBuffer是封装的数组 StringBuilder是线程不安全的,StringBuffer是线程安全的 14.equals和==区别 equals是比较值内容是否相同,==在比较基本类型时是在比较值,比较引用类型时是比较地址 15.异常分类,处理方式,自定义异常 异常分类:异常的根类为Throwable,Throwable下面又派生了两个子类:Error和Exception。 Exception:分为运行时异常( RuntimeException )和非运行时异常 运行时异常:数组越界..... 非运行异常:classnotfound io异常 自定义异常:throw new 异常名 对于异常有两种处理方式:1.try-catch-finally 2.throws抛出异常,交给上一级处理直达JVM虚拟机处理 16.线程创建,生命周期,并发处理 线程创建3种方式: 1.继承Thread类创建线程类,并重写该类的run方法,调用线程对象的start()方法来启动该线程。 2.通过Runnable接口创建线程类,并重写该类的run方法,调用线程对象的start()方法来启动该线程。 3.线程池创建 ExecutorService threadPool = Executors.newFixedThreadPool(2); threadPool.execute(r); 17.io种类,对象流 io分为字节流和字符流,字节流又分为字节输入流和字节输出流,字符流又分为字符输入流和字符输出流. 对象流:ObjectInputStream和ObjectOutputStream,序列化对象再反序列化. 18.list set map 区别 Java集合主要由2大体系构成,分别是Collection体系和Map体系. Collection主要有三个子接口,分别为List(列表)、Set(集)、Queue(队列)。其中,List、Queue中的元素有序可重复,而Set中的元素无序不可重复; Map同属于java.util包中,是集合的一部分,但与Collection是相互独立的,没有任何关系。Map中都是以key-value的形式存在,其中key必须唯一,主要有HashMap、HashTable、TreeMap三个实现类。 (1)List中主要有ArrayList、LinkedList两个实现类;Set中则是有HashSet实现类; 在List集合中,有ArrayList和LinkedList这两个类。 其中,ArrayList底层通过数组实现,随着元素的增加而动态扩容。而LinkedList底层通过链表来实现,随着元素的增加不断向链表的后端增加节点。 (2)Set继承于Collection接口,是一个不允许出现重复元素,并且无序的集合,主要有HashSet和TreeSet两大实现类。 HashSet是哈希表结构,主要利用HashMap的key来存储元素,计算插入元素的hashCode来获取元素在集合中的位置; TreeSet是红黑树结构,每一个元素都是树中的一个节点,插入的元素都会进行排序; (3)Map是一个独立的接口,与Collection相同级别的接口。主要实现类HashMap、TreeMap、HashTable HashMap基于哈希表,底层结构由散列表的存储原理来实现,添加到集合中的元素以“key--value”形式保存到数组中,在数组中key--value被包装成一个实体来处理---也就是上面Map接口中的Entry。 TreeMap与HashMap相比是一个能比较元素大小的Map集合,会对传入的key进行了大小排序。其中,可以使用元素的自然顺序,也可以使用集合中自定义的比较器来进行排序; HashTable的操作几乎和HashMap一致,主要的区别在于HashTable为了实现多线程安全,在几乎所有的方法上都加上了synchronized锁,而加锁的结果就是HashTable操作的效率十分低下。 19.arrylist linkedlist区别 arrylist底层是数组,插入慢,读取块 linkedlist底层是链表,插入快速,读取慢 20.map有序? 如何有序? map无序,使用treemap可以使用Comparator来进行自然排序排序 21.反射 在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。 Class.forName(类名.class)获取类对象 Method[] method1 = cls.getMethods();//获取该类下所有方法名 method.invoke(o);//执行方法 22.http协议,请求包含部分 http请求由请求行,消息报头,请求正文三部分构成 请求行由请求 GET /example.html HTTP/1.1 (CRLF) 消息报头一系列的键值对组成 Host: www.baidu.com 请求正文:只有在发送 23.包装类的作用及转换 采用基本数据类型包装的形式描述,让功能变得更加健壮 Boolean 24 .get post区别 最直观的区别就是GET把参数包含在URL中,POST通过request body传递参数,GET请求常用作请求服务器发送信息,POST向服务器添加信息. 25.数组复制排序 复制: 1.copyOf int[] newScores = (int[])Arrays.copyOf(scores,8);从头复制 2.copyOfRange int newScores[] = (int[]) Arrays.copyOfRange(scores, 0, 5);从指定的下标开始复制 3.arraycopy System.arraycopy(scores, 0, newScores, 2, 8); 4.clone int newScores[] = (int[]) scores.clone(); 排序: 1.桶排序,将需要排序的数组内容全都取出来放在另一个有序的数组中,然后在依次放回 2.冒泡排序,数组最前面的元素与之后的每个元素依次比较,后面的元素比前面的元素大,就获取后面的元素然后继续与后面元素比较,直到所有元素都比较过一遍. (菜鸟网原图) 3.选择排序,从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾.每次排序都找出最大或最小的那一个. 4.快速排序,先从数列中取出一个数作为基准数。分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。再对左右区间重复第二步,直到各区间只有一个数。 5.插入排序,将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。 6.希尔排序,对数组进行最小划分,两两一组,进行插入排序,再次分开插入排序 7.归并排序 26.html表单,序列元素本类 本包类 派生类 public √ √ √ protected √ √ √ 默认 √ √ private √ Method
, URL
字段和HTTP Version
三部分构成
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.6)
Gecko/20050225 Firefox/1.0.1
Connection: Keep-AlivePOST
请求时才会有请求正文,GET
方法并没有请求正文。 byte short int long float double char boolean Byte Short Integer Long Float Double Character
27.css选择器,使用方式,优先级
css选择器:
- <style>
-
- #a1{color: #f5860f;} /*id选择器*/
-
- .a2{color: #ff0026;} /*类选择器*/
-
- h3,#a1,.a2{background: #b6eeff}/*分组选择器*/
-
- input[type="text"]{background: darkcyan}/*属性选择器*/
-
- body div div div div{color: #689797} /*子孙后代选择器,包含后代只要在div里都会改颜色*/
-
- body >div >p{color: #689797} /*子元素选择器,不包含后代*/
-
- /*伪类选择器*/
- a:link{color:red }/*未访问*/
- a:visited{color:rebeccapurple }/*访问过*/
- a:hover{color: yellowgreen}/*悬停*/
- a:active{color: dimgrey}/*点击/激活*/
- style>
样式可以为内联>内部>外部,作用域越小,优先级越高
28.js 使用方式
JS的创建方式:Jscript书写和引用都在标签
,不能既引用,又写代码 。引用:
调用时:οnclick="f()">。
- function f(x,y) { //定义函数
- return x * y; //返回值
- }
29.vue生命周期,常用指令,命令
1、创建前(beforeCreate)
对应的钩子函数为beforeCreate。此阶段为实例初始化之后,此时的数据观察和事件机制都未形成,不能获得DOM节点。
2、创建后(created)
对应的钩子函数为created。在这个阶段vue实例已经创建,仍然不能获取DOM元素。
3、载入前(beforeMount)
对应的钩子函数是beforemount,在这一阶段,我们虽然依然得不到具体的DOM元素,但vue挂载的根节点已经创建,下面vue对DOM的操作将围绕这个根元素继续进行;beforeMount这个阶段是过渡性的,一般一个项目只能用到一两次。
4、载入后(mounted)
对应的钩子函数是mounted。mounted是平时我们使用最多的函数了,一般我们的异步请求都写在这里。在这个阶段,数据和DOM都已被渲染出来。
5、更新前(beforeUpdate)
对应的钩子函数是beforeUpdate。在这一阶段,vue遵循数据驱动DOM的原则;beforeUpdate函数在数据更新后虽然没立即更新数据,但是DOM中的数据会改变,这是Vue双向数据绑定的作用。
6、更新后(updated)
对应的钩子函数是updated。在这一阶段DOM会和更改过的内容同步。
7、销毁前(beforeDestroy)
对应的钩子函数是beforeDestroy。在上一阶段vue已经成功的通过数据驱动DOM更新,当我们不在需要vue操纵DOM时,就需要销毁Vue,也就是清除vue实例与DOM的关联,调用destroy方法可以销毁当前组件。在销毁前,会触发beforeDestroy钩子函数。
8、销毁后(destroyed)
对应的钩子函数是destroyed。在销毁后,会触发destroyed钩子函数。
npm run serve 运行vue项目,npm i 下载node包
30.数据库数据类型
整数类型:BIT、BOOL、TINY INT、SMALL INT、MEDIUM INT、 INT、 BIG INT
浮点数类型:FLOAT、DOUBLE、DECIMAL
字符串类型:CHAR、VARCHAR、TINY TEXT、TEXT、MEDIUM TEXT、LONGTEXT、TINY BLOB、BLOB、MEDIUM BLOB、LONG BLOB
日期类型:DATA、DATATIME、TIMESTAMP、TIME、
其他数据类型:BINARY、VARBINARY、ENUM、SET
31.数据库约束种类
主键约束、非空约束、外键约束、唯一约束、检查约束。
32.分页、排序、分组、去重,聚合函数
分页 select *from table limit 0,10 ,
排序 select *from table order by id desc/asc,
分组 select *from table group by id,
去重 select distinct *from table
AVG | 计算平均值。 |
COUNT | 计算记录数。 |
COUNT_IF | 计算指定表达式为True的记录数。 |
MAX | 计算最大值。 |
MIN | 计算最小值。 |
MEDIAN | 计算中位数。 |
STDDEV | 计算总体标准差。 |
STDDEV_SAMP | 计算样本标准差。 |
SUM | 计算汇总值。 |
33.关联查询
1.等值连接,使用"="连接关联字段 select e.name,d.name from emp e,dept d where e.dept_id=d.id;
2.内连接,使用join on 连接关联字段select e.name ,d.name from emp e join dept d on e.dept_id=d.id;
34.sql优化,视图,索引
优化:
视图:
视图是从一个或几个基本表(或视图)导出的表,多表联查,展示就是视图
索引:
创建或设置主键的时候,mysql会自动添加一个与主键对应的唯一索引,不需要再做额外的添加索引,创建 alter table classes add index my_name (name);查看show index from 表名;删除alter table classes drop index my_name;
35.crud基本语法
数据操纵语言DML:
增:insert into 表名(字段名...)values(字段名....)
删:delete from 表名 where 字段名
改:updata 表名 set 值=50 where 值='tom';
数据查询语言DQL:
查:select 查询内容 from 表名
数据定义语言DDL:
创建数据库:create database db2 charset=utf8
创建表:create table person(int id primary key auto_increment);
添加表字段:alter table per add tianjia int after laji;
删除字段:alter table 表名 drop 字段;
修改字段:alter table 表名 change 旧字段名 新字段名 类型;
36.事务特征
事务具有四大特征
Atomic(原子性):事务中包含的操作被看做一个逻辑单元,这个逻辑单元中的操作要么全部成 功,要么全部失败。
Consistency(一致性):事务完成时,数据必须处于一致状态,数据的完整性约束没有被破坏
Isolation(隔离性):如果有两个事务,运行在相同的时间内,执行 相同的功能,事务的隔离性将确保每一事务在系统中认为只有该事务在使用系统。防止事务操作间的混淆,在同一时间仅有一个请求用于同一数据。
Durability(持久性):事务结束后,事务处理的结果持久的保存在数据库之中,并不会被回滚。
37.sql分类
DDL:Data DefinitionLanguage 数据定义语言,用来定义数据库对象(数据库,表, 字段)
DML:Data Manipulation Language 数据操作语言,用来对数据库表中的数据进行增删改
DQL:Data Query Language 数据查询语言,用来查询数据库中表的记录
DCL:Data Control Language 数据控制语言,用来创建数据库用户、控制数据库的 访问权限。
38.jdbc连接步骤
添加依赖和驱动
-
-
-
mysql -
mysql-connector-java -
8.0.15 -
-
mysql -
mysql-connector-java -
runtime
1.获取连接对象
- //获取对象
- Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/empdb?characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false",
2.创建sql语句执行对象
- //创建执行SQL对象
- Statement s = conn.createStatement();
3.按需求调用sql语句api
- //创建表和数据库时推荐使用
- s.execute("create table hero (id int,name varchar(20),age int )");
-
- //更新表字段
- s.executeUpdate("insert into hero(name,age) values ('孙悟空',5000),('猪八戒',300)");
-
- //当使用查询语句时使用
- ResultSet rs = s.executeQuery("select *from emp");
- while (rs.next()) {
- String name = rs.getString("name");
- System.out.println(name);
39.SQL注入
通过对sql语句拼接来恶意访问,select * from '某字段' where id = 1’,由于后面有一个单引号,这样的语句不符合数据库语法的规范,所以会报错,从而判断出该处是否存在SQL注入。
40.连接池
- private static DruidDataSource dds;
- static {
- //创建数据库连接池
- dds = new DruidDataSource();
- //设置连接数据库的信息
- dds.setUrl("jdbc:mysql://localhost:3306/empdb?characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false");
- dds.setUsername("root");
- dds.setPassword("root");
- //设置初始连接数量
- dds.setInitialSize(3);
- //设置最大连接数量
- dds.setMaxActive(5);
- }
- public static Connection getConn() throws SQLException {
- //获取连接对象 异常抛出
- Connection conn = dds.getConnection();
- System.out.println("连接:"+conn);
- return conn;
- }
41.maven的作用,使用
maven主要是用来解决导入java类依赖的jar,编译java项目主要问题。
使用:更改阿里下载路径,(10条消息) maven111111111111111111111111-Javascript文档类资源-CSDN文库
配置maven的位置使用自己的set文件
42.Spring介绍,核心ioc di aop解释
Spring是一个轻量级的控制反转 (IoC)和面向切面 (AOP)的容器框架,解决对象的创建和管理,方便进行面向切面的编程,声明式事务的支持、方便集成各种优秀框架
IOC:即“控制反转”,一种设计思想,在Java开发中,Ioc意味着将你设计好的对象交给容器控制,解耦对象之间的关系,(在方法上添加@Bean注解就是添加到ioc管理)
DI:即依赖注入,组件之间依赖关系由容器在运行期决定,即容器动态的将某个依赖关系注入到组件当中,它是是 Spring 框架核心IOC技术的具体实现。(@Autowrid 注入)
AOP:面向切面编程,对于系统层面的一些需求比如日志,性能统计等,分散在软件的各个角落,维护起来很是不爽,这种问题的解决确是oop力所不能及的,于是AOP横空出世。通过动态代理来生成代理对象,在代理对象中对目标对象的方法进行增强 (常用注解@Aspect)(@Pointcut 注解用 于定义切面中的切入点表达式)切入点公式(@Pointcut("bean (sysUserServiceImpl)"))
后置通知(after):在目标方法执行之后执行
后置返回通知(after returning):在目标方法返回之后执行,先执行后置通知再执行后置返回通知。
异常通知(after throwing):在目标方法抛出异常时执行
环绕通知(around):在目标函数执行中执行
43.mvc流程
44.单例模式
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
- public class SingleObject {
-
- //创建 SingleObject 的一个对象
- private static SingleObject instance = new SingleObject();
-
- //让构造函数为 private,这样该类就不会被实例化
- private SingleObject(){}
-
- //获取唯一可用的对象
- public static SingleObject getInstance(){
- return instance;
- }
-
- }
45.Spring Boot优势,解释
Springboot是一个快速整合第三方框架,简化xml,内置Http服务器也就是之前所用Tomcat服务器,Springboot是java应用程序内置Tomcat服务器通过命令运行
传统项目中配置文件整合复杂。Springboot配置文件大量减少适合快速开发,Spring boot 底层实现版本统一,为所有Spring开发者更快的入门。SpringBoot开箱即用,提供各种默认配置来简化项目配置,内嵌式容器简化web项目,没有冗余代码生成和xml配置的要求。
优点:快速构建项目。对主流开发框架的无配置集成。项目可独立运行,无须外部依赖Servlet容器。提供运行时的应用监控。极大地提高了开发、部署效率。
46.mybatis作用
基于 java 的持久层框架,它内部封装了 jdbc,使开发者只需要关注 sql 语句本身,而不需要花费精力去处理加载驱动、创建连接、创建 statement 等繁杂的过程,mybatis 通过 xml 或注解的方式将要执行的各种 statement 配置起来,并通过 java 对象和 statement 中sql 的动态参数进行映射生成最终执行的 sql 语句,最后由 mybatis 框架执行 sql 并将结果映射为 java 对象并返回。
采用 ORM 思想解决了实体和数据库映射的问题,对 jdbc 进行了封装,屏蔽了 jdbc api 底层访问细节,使我们不用与 jdbc api 打交道,就可以完成对数据库的持久化操作。
47.动态sql
在组合复杂的的SQL语句的时候,需要去拼接,稍不注意哪怕少了个空格,都会导致错误。Mybatis的动态SQL功能正是为了解决这种问题, 其通过 if, choose, when, otherwise, trim, where, set, foreach标签,可组合成非常灵活的SQL语句,从而提高开发人员的效率。
常用标签有:
if会对传来的参数做判断,非空时才能放入
- <if test="id != null">
- id=#{id}
- if>
- <set>
- <if test="name != null">name = #{name},if>
- <if test="password != null">password = #{password},if>
- <if test="age != null">age = #{age},if>
- set>
collection="array" collection里存放的是遍历对象的类型 数组:arry, 集合:list
item="id" 作用为给遍历的对象起个别名,不一定为id
separator="," 在in中每个元素都必须用逗号隔开 in(id1,id2,id3.......)
open="(" close=")" in 的元素需要括号括起来 in(id1,id2,id3.......)
- <foreach item="item" index="index" collection="list"
- open="(" separator="," close=")">
- #{item}
- foreach>
48.# &区别
最大的区别则是#{}方式能够很大程度防止sql注入(安全),${}方式无法防止Sql注入。
#{}表示一个占位符号 相当于
jdbc
中的 ? 符号
#{}实现的是向prepareStatement中的预处理语句中设置参数值,sql语句中#{}表示一个占位符 即 ?
$ {}将传入的数据直接显示生成在sql中。如:
select * from user where id= $ {user_id}
,如果传入的值是11,那么解析成sql时的值为where id=11
49.用户,角色,权限设计,查询sql
RBAC用户通过角色和权限进行关联,权限系统的所有权限信息,用户是应用系统的具体操作者,用户可以自己拥有权限信息,可以归属于0~n个角色,可属于0~n个组。他的权限集是自身具有的权限、所属的各角色具有的权限,角色为了对许多拥有相似权限的用户进行分类管理,定义了角色的概念。
sql设计 select *from 用户表
jion 用户角色表 on 用户表.id=用户角色表.用户id
join 用户角色表.角色id on 权限角色表.角色id=用户角色表.角色id
join 权限表 on 权限角色表.权限id=权限表.id
where 用户表.id=?
50.接受参数的请求的方式种类
1.在处理方法入参处使用 @RequestParam,
public String testGet1(@RequestParam("paramName") String paramName)
2.@PathVariable注解
@GetMapping("/testGet2/{paramName}")
public String testGet2(@PathVariable String paramName)
3.springmvc的自动匹配
@GetMapping("/testGet3")
public String testGet3(String paramName,String paramName1, Model model)
4.将参数封装到对象中
@GetMapping("/testGet4")
public String testGet4(ParamsEntity paramsEntity)
5.从HttpServletRequest中提取参数
public String testQuery2(HttpServletRequest request) {
String username = request.getParameter("username");
String password = request.getParameter("password");}
51.restful风格
RESTFUL是一种网络应用程序的设计风格和开发方式,基于 HTTP ,它是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件,它主要用于客户端和服务端交互类的软件
GET(SELECT):从服务器取出资源(一项或多项)。
POST(CREATE):在服务器新建一个资源。
PUT(UPDATE):在服务器更新资源(客户端提供完整资源数据)。
PATCH(UPDATE):在服务器更新资源(客户端提供需要修改的资源数据)。
DELETE(DELETE):从服务器删除资源。
获取数据或者资源就用GET,更新数据就用PUT,删除数据就用DELETE,然后规定方法必须要传入哪些参数,每个资源都有一个地址。
52.redis解释,支持类型,应用场景
Redis是一种key-value 存储系统,是跨平台的非关系型数据库因为运行在内存中所以读写速度超快,虽然运行在内存中但是可以持久化到磁盘.
应用场景
53.击穿,穿透,雪崩,解决方案
击穿:也叫做热点key问题,就是一个被高并发并且缓存重建业务较复杂的key突然失效了,无数的请求访问会瞬间给数据库带来巨大的冲击
解决方案: 定时任务主动刷新缓存设计,使用redis的分布式锁将系统中key的缓存失效时间均匀地错开,防止统一时间点有大量的key对应的缓存失效;
重新设计缓存的使用方式,当我们通过key去查询数据时,首先查询缓存,如果此时缓存中查询不到,就通过分布式锁进行加锁,取得锁的进程查DB并设置缓存,然后解锁;其他进程如果发现有锁就等待,然后等解锁后返回缓存数据或者再次查询DB。
穿透:查询的key在redis中不存在,对应的ID在数据库也不存在,此时被非法用户进行攻击,大量的请求会直接冲入数据库上造成宕机,从而影响整个系统,这种现象称之为:缓存穿透
1.业务层校验
用户发过来的请求,根据请求参数进行校验,对于明显错误的参数,直接拦截返回。
比如,请求参数为主键自增id,那么对于请求小于0的id参数,明显不符合,可以直接返回错误请求。
2.不存在数据设置短过期时间
对于某个查询为空的数据,可以将这个空结果进行Redis缓存,但是设置很短的过期时间,比如30s,可以根据实际业务设定。注意一定不要影响正常业务。
雪崩:缓存雪崩:Redis中缓存的数据大面积同时失效,或者Redis宕机,从而会导致大量请求直接到数据库,压垮数据库。
1.设置有效期均匀分布
避免缓存设置相近的有效期,我们可以在设置有效期时增加随机值;
或者统一规划有效期,使得过期时间均匀分布。
2.数据预热
对于即将来临的大量请求,我们可以提前走一遍系统,将数据提前缓存在Redis中,并设置不同的过期时间。
3.保证Redis服务高可用
54.security解释,认证流程,授权
Spring Security是为基于Spring的应用程序提供声明式安全保护的安全性框架,它提供了完整的安全性解决方案,能够在web请求级别和方法调用级别处理身份证验证和授权。是一个认证(Authentication)/授权(Authorisation)框架,基于Servlet技术,更确切的说是基于Servlet的Filter技术.
具体实现:
(1)重写loadUserByUsername方法,该方法会返回adminDetails对象,该对象封装了权限等
UserDetailsServiceImpl implements UserDetailsService
public UserDetails loadUserByUsername(String s)
List
authorities = new ArrayList<>();
//获取权限放入
for (String permission : loginInfoByUserName.getPermissions()) {
GrantedAuthority authority = new SimpleGrantedAuthority(permission);
authorities.add(authority);
}
AdminDetails adminDetails = new AdminDetails(
loginInfoByUserName.getUsername(),
loginInfoByUserName.getPassword(),
loginInfoByUserName.getEnable() == 1,
authorities);
adminDetails.setId(loginInfoByUserName.getId());
return adminDetails;
(2)调用:authenticate会调用重写的loadUserByUsername方法
- Authentication authentication = new UsernamePasswordAuthenticationToken(
- adminLoginInfoDTO.getUsername(),
- adminLoginInfoDTO.getPassword());
- //开始认证
- Authentication authenticateResult = authenticationManager.authenticate(authentication);
(3)对SpringSecurity设置配置,添加白名单
- String[] url = {
- "/doc.html",
- "/**/*.json",
- "/**/*.css",
- "/**/*.js",
- "/swagger-resources",
- "/v2/api-docs",
- "/sys-admin",
- "/admins/login",
-
- };
- http.authorizeRequests()//对请求执行认证与授权
- //.antMatchers(url) // 匹配某些请求路径,严格匹配
- .mvcMatchers(url)//匹配某些请求路径,不看后缀名,eg:/admin.jpg或者/admin.js都可以匹配上
- .permitAll()// (对此前匹配的请求路径)不需要通过认证即允许访问
- .anyRequest()// 除以上配置过的请求路径以外的所有请求路径
- .authenticated();// 要求是已经通过认证的
(4)权限判断
@PreAuthorize("hasAuthority('/ams/admin/read')")
55.jwt组成,作用
jwt(Json Web Tonken),用于保存用户信息,保存在用户的请求头的Authorization中jwt由三部分组成
Header: token的类型(“JWT”)和算法名称(HMAC SHA256或者RSA等等)。
{
'alg': "HS256",
'typ': "JWT"
}
Payload:包含声明(要求)。声明是关于实体(通常是用户)和其他数据的声明。
{
"sub": '1234567890',
"name": 'john',
"admin":true
}
Signature:由后端服务器生成,通过Header+ Payload+盐生成
常用于在用户和服务器之间传递安全可靠的信息,他的两大使用场景是:认证和数据交换,由服务端根据规范生成一个令牌(token),并且发放给客户端。此时客户端请求服务端的时候就可以携带者令牌,以令牌来证明自己的身份信息。
- String jwt = Jwts.builder()
- //header 加密方式和类型
- .setHeaderParam("alg", "HS256")
- .setHeaderParam("typ", "JWT")
- //payload 携带参数和过期时间
- .setClaims(claims)
- .setExpiration(date)
- //Signature 加密方式和盐值
- .signWith(SignatureAlgorithm.HS256, secretKey)
- //整合
- .compact();
Security将jwt存入容器上下文
(1)添加过滤器到Spring过滤组件中
- //添加过滤器
- @Autowired
- JwtAuthorizationFilter authorizationFilter;
- //添加自己的过滤器到密码验证之前
- http.addFilterBefore(authorizationFilter, UsernamePasswordAuthenticationFilter.class);
(2)创建过滤器
- @Value("${csmall.jwt.secret-key}")
- String secretKey;
-
- private static final long JWT_MIN_LENGTH = 100;
-
- @Override
- protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
-
- //清除原有的SecurityContext的认证信息
- SecurityContextHolder.clearContext();
-
- //尝试获取用户的JWT
- String jwt = request.getHeader("Authorization");
- log.debug("接收到jwt数据:{}", jwt);
-
- //判断是否获取到jwt StringUtils.hasText(jwt)非空,非null
- if (!StringUtils.hasText(jwt) || jwt.length() < JWT_MIN_LENGTH) {
- log.trace("未获取到jwt直接放行");
- filterChain.doFilter(request, response);
- return;
- }
-
- //判断验证是否通过
- Claims claims=null;
- try {
- claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jwt).getBody();
- } catch (ExpiredJwtException e) {
- String message="JWT过期,请重新登录";
- JSONResult jsonResult =JSONResult.fail(ServiceCode.ERR_JWT_SIGNATURE,message);
- //转化Json数据
- String jsonResultString = JSON.toJSONString(jsonResult);
- PrintWriter printWriter = response.getWriter();
- printWriter.println(jsonResultString);
- printWriter.close();
- return;
- } catch (UnsupportedJwtException e) {
- JSONResult jsonResult =JSONResult.fail(ServiceCode.ERR_BAD_REQUEST,e.getMessage());
- //转化Json数据
- String jsonResultString = JSON.toJSONString(jsonResult);
- PrintWriter printWriter = response.getWriter();
- printWriter.println(jsonResultString);
- printWriter.close();
- return;
- } catch (MalformedJwtException e) {
- String message="非法访问";
- JSONResult jsonResult =JSONResult.fail(ServiceCode.ERR_JWT_MALFORMED,message);
- //转化Json数据
- String jsonResultString = JSON.toJSONString(jsonResult);
- PrintWriter printWriter = response.getWriter();
- printWriter.println(jsonResultString);
- printWriter.close();
- return;
- } catch (SignatureException e) {
- JSONResult jsonResult =JSONResult.fail(ServiceCode.ERR_BAD_REQUEST,e.getMessage());
- //转化Json数据
- String jsonResultString = JSON.toJSONString(jsonResult);
- PrintWriter printWriter = response.getWriter();
- printWriter.println(jsonResultString);
- printWriter.close();
- return;
- } catch (IllegalArgumentException e) {
- String message="非法访问";
- JSONResult jsonResult =JSONResult.fail(ServiceCode.ERR_JWT_SIGNATURE,message);
- //转化Json数据
- String jsonResultString = JSON.toJSONString(jsonResult);
- PrintWriter printWriter = response.getWriter();
- printWriter.println(jsonResultString);
- printWriter.close();
- return;
- }catch (Throwable e){
- e.printStackTrace();
- String message="服务器忙";
- JSONResult jsonResult =JSONResult.fail(ServiceCode.ERR,message);
- //转化Json数据
- String jsonResultString = JSON.toJSONString(jsonResult);
- PrintWriter printWriter = response.getWriter();
- printWriter.println(jsonResultString);
- printWriter.close();
- return;
- }
-
-
- //解析获取用户数据,setContentType设置文档类型
- log.trace("开始解析用户数据");
- response.setContentType("application/json;charset=utf-8");
- Long id = claims.get("id", Long.class);
- String username = claims.get("username", String.class);
- String authoritiesString = claims.get("authorities", String.class);
- log.trace("id: {}", id);
- log.trace("username: {}", username);
- log.trace("authoritiesString: {}", authoritiesString);
-
-
- //通过解析出来的数据创建认证
- //加入权限,将JSON类型转换为SimpleGrantedAuthority类SimpleGrantedAuthority是GrantedAuthority的实现类
- List
authorities=JSON.parseArray(authoritiesString, - SimpleGrantedAuthority.class);
- //创建放入SecurityContext中JWT
- LoginPrincipal loginPrincipal=new LoginPrincipal();
- loginPrincipal.setId(id);
- loginPrincipal.setUsername(username);
- Authentication authentication = new UsernamePasswordAuthenticationToken(
- loginPrincipal, null, authorities
- );
-
- //将认证添加到SecurityContext
- SecurityContext securityContext = SecurityContextHolder.getContext();
- securityContext.setAuthentication(authentication);
-
- //放行
- filterChain.doFilter(request, response);
(3)在过滤器中将认证信息放入上下文中
(3.1)创建认证信息
- //解析获取用户数据,setContentType设置文档类型
- log.trace("开始解析用户数据");
- response.setContentType("application/json;charset=utf-8");
- Long id = claims.get("id", Long.class);
- String username = claims.get("username", String.class);
- String authoritiesString = claims.get("authorities", String.class);
- log.trace("id: {}", id);
- log.trace("username: {}", username);
- log.trace("authoritiesString: {}", authoritiesString);
-
-
- //通过解析出来的数据创建认证
- //加入权限,将JSON类型转换为SimpleGrantedAuthority类SimpleGrantedAuthority是GrantedAuthority的实现类
- List
authorities=JSON.parseArray(authoritiesString, - SimpleGrantedAuthority.class);
- //创建放入SecurityContext中JWT
- LoginPrincipal loginPrincipal=new LoginPrincipal();
- loginPrincipal.setId(id);
- loginPrincipal.setUsername(username);
- Authentication authentication = new UsernamePasswordAuthenticationToken(
- loginPrincipal, null, authorities
- );
- //将认证添加到SecurityContext
- SecurityContext securityContext = SecurityContextHolder.getContext();
- securityContext.setAuthentication(authentication);
56.冒泡排序
两两比较,取大着,继续比较,每次都会得到最大的一个.
- public void resub(int[] a) {
- for (int i = 0; i < a.length; i++) {
- for (int j = 0; j < a.length - i; j++) {
- if (j+1==a.length){break;}
- if ((a[j] > a[j + 1])) {
- int t = 0;
- t = a[j + 1];
- a[j + 1] = a[j];
- a[j] = t;
- }
- }
- }
- for (int i = 0; i < a.length; i++) {
- System.out.println(a[i]);
- }
- }
-
- public static void main(String[] args) {
- AdminServiceTests tests = new AdminServiceTests();
- int[] a = new int[]{115, 212, 87, 35, 929, 100, 23};
- tests.resub(a);
- }
57.统计字符出现次数
将字符串存入数组,将数组循环比较,定义变量,加1.
- public void chacksub(String s1) {
- char[] s = s1.toCharArray();
- Map
map = new HashMap<>(); - for (int i = 0; i < s.length; i++) {
- int count = 0;
- for (int j = 0; j < s.length; j++) {
- if (s[i] == s[j]) {
- count++;
- map.put(s[i], count);
- }
- }
- }
-
- map.forEach((k, v) -> System.out.println("Item : " + k + " Count : " + v));
- }
- public static void main(String[] args) {
- AdminServiceTests tests = new AdminServiceTests();
- String b = "asdasas1251245 daqwedasd";
- tests.chacksub(b);
- }