JavaWeb
考点:
1. HTTP协议
概念:
- HTTP:Hyper Text Transfer Protocol(超文本传输协议),规定了浏览器与服务器之间数据传输的规则。HTTP协议是无状态协议: 对于数据没有记忆能力。每次请求-响应都是独立的,不会记录任何信息。
基于两个点实现:
基于TCP协议: 面向连接,安全
基于请求-响应模型: 请求和响应是一一对应关系,没有请求,就没有响应,一次请求对应一次响应(先请求后响应)
优缺点:
优点: 速度快
缺点: 多次请求间不能共享数据
无法共享数据可以使用会话技术(Cookie、Session)来解决这个问题。
2. spring中请求参数接收
- 简单参数:
- springboot参数名一致(原始方式使用极少)
- 原始方式:通过Servlet中提供的API:HttpServletRequest(请求对象),获取请求的相关信息。通过HttpServletRequest的对象request.getParameter(“请求参数名”)
- 请求参数名与形参变量名相同,定义同名的形参即可接收参数。
- springboot参数名不一致
- 通过@RequestParam注解完成映射
url请求参数名:name @RequestParam("name") String username
- 实体参数:(简单实体对象)
- 请求参数名与实体类的属性名相同:
- 参数名和实体类属性名一致时正常赋值
- 参数名和实体类属性名不一致时:
- 赋值为null
- 复杂实体对象的封装,需要遵守如下规则:
- 请求参数名与形参对象属性名相同,按照对象层次结构关系即可接收嵌套实体类属性参数。通过对象名.属性名的方式
- 数组集合参数:
- 数组参数:
- 请求参数名与形参数组名称相同且请求参数为多个,定义数组类型形参即可接收参数。
- 前端请求时,有两种传递形式:?hobby=game&hobby=java or ?hobby=game,java
- 集合参数:
- 请求参数名与形参集合对象名相同且请求参数为多个,@RequestParam 绑定参数关系。
- 默认情况下,请求中参数名相同的多个值,是封装到数组。如果要封装到集合,要使用@RequestParam绑定参数关系
- 两种传递形式:?hobby=game&hobby=java or ?hobby=game,java
- 日期参数:
- 参数名和形参变量名保持一致,变量名通过@DateTimeFormat注解,以及其pattern属性来设置日期的格式。
- 格式:@DateTimeFormat(pattern = “yyyy-MM-dd HH:mm:ss”) LocalDateTime updateTime
- JSON参数:
- 服务端Controller方法接收JSON格式数据:
- 传递json格式的参数,在Controller中会使用实体类进行封装。
- 封装规则:JSON数据键名与形参对象属性名相同,定义POJO类型形参即可接收参数。需要使用 @RequestBody标识。
- @RequestBody注解:将JSON数据映射到形参的实体类对象中(JSON中的key和实体类中的属性名保持一致)
- 路径参数:
- 前端:通过请求URL直接传递参数
- 后端:使用{…}来标识该路径参数,需要使用@PathVariable获取路径参数
- @RequestMapping(“/path/{id}/{name}”) —> @PathVariable Integer id, @PathVariable String name
3. sql语句:聚合函数,分组,排序,分页,模糊查询…
select 字段列表 from 表名 [where 条件列表] [group by 分组字段 ] order by 字段1 排序方式1 , 字段2 排序方式2 … ;
注意事项:
分组之后,查询的字段一般为聚合函数和分组字段,查询其他字段无任何意义
执行顺序:where > 聚合函数 > having
where与having区别(面试题)
执行时机不同:where是分组之前进行过滤,不满足where条件,不参与分组;而having是分组之后对结果进行过滤。
判断条件不同:where不能对聚合函数进行判断,而having可以。
4. mybatis参数占位符,动态sql标签
占位符:在Mybatis中提供的参数占位符有两种:${…} 、#{…}
#{…}
执行SQL时,会将#{…}替换为?,生成预编译SQL,会自动设置参数值。参数值中的特殊符号会被转义
#例子 select * from emp where username='abc' and password='\' or \'1\' = \'1'
使用时机:参数传递,都使用#{…}
${…}
拼接SQL。直接将参数拼接在SQL语句中,存在SQL注入问题
使用时机:如果对表名、列表进行动态设置时使用
注意事项:在项目开发中,建议使用#{…},生成预编译SQL,防止SQL注入安全。
动态sql标签
<if>
:用于判断条件是否成立。使用test属性进行条件判断,如果条件为true,则拼接SQL。
<if test="条件表达式"> 要拼接的sql语句 </if>
<where>
只会在子元素有内容的情况下才插入where子句,而且会自动去除子句的开头的AND或OR
<set>
:动态的在SQL语句中插入set关键字,并会删掉额外的逗号。(用于update语句中)使用
<foreach>
遍历deleteByIds方法中传递的参数ids集合
<foreach collection="集合名称" item="集合遍历出来的元素/项" separator="每一次遍历使用的分隔符" open="遍历开始前拼接的片段" close="遍历结束后拼接的片段"> </foreach>
可以对重复的代码片段进行抽取,将其通过
<sql>
标签封装到一个SQL片段,然后再通过<include>
标签进行引用。
<sql>
:定义可重用的SQL片段<include>
:通过属性refid,指定包含的SQL片段
5. 事务相关概念
可以将要执行的多个操作看作是一个集合,这个集合中的要么全部执行成功提交,要么执行成功的碰到失败的进行回滚,全部不提交。
面试题:事务有哪些特性? 原子性(Atomicity):事务是不可分割的最小单元,要么全部成功,要么全部失败。 一致性(Consistency):事务完成时,必须使所有的数据都保持一致状态。 隔离性(Isolation):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行。 持久性(Durability):事务一旦提交或回滚,它对数据库中的数据的改变就是永久的。 事务的四大特性简称为:ACID 原子性(Atomicity) :原子性是指事务包装的一组sql是一个不可分割的工作单元,事务中的操作要么全部成功,要么全部失败。 一致性(Consistency):一个事务完成之后数据都必须处于一致性状态。 如果事务成功的完成,那么数据库的所有变化将生效。 如果事务执行出现错误,那么数据库的所有变化将会被回滚(撤销),返回到原始状态。 隔离性(Isolation):多个用户并发的访问数据库时,一个用户的事务不能被其他用户的事务干扰,多个并发的事务之间要相互隔离。 一个事务的成功或者失败对于其他的事务是没有影响。 持久性(Durability):一个事务一旦被提交或回滚,它对数据库的改变将是永久性的,哪怕数据库发生异常,重启之后数据亦然存在。
@Transactional注解当中的两个常见的属性:
- 异常回滚的属性:rollbackFor
- 默认情况下,只有出现RuntimeException(运行时异常)才会回滚事务。
- 假如想让所有的异常都回滚,需要来配置@Transactional注解当中的rollbackFor属性,通过rollbackFor这个属性可以指定出现何种异常类型回滚事务。@Transactional(rollbackFor=Exception.class)
- 事务传播行为:propagation
- 事务的传播行为(propagation属性):当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行事务控制。
REQUIRED 【默认值】需要事务,有则加入,无则创建新事务 REQUIRES_NEW 需要新事务,无论有无,总是创建新事务 SUPPORTS 支持事务,有则加入,无则在无事务状态中运行 NOT_SUPPORTED 不支持事务,在无事务状态下运行,如果当前存在已有事务,则挂起当前事务 MANDATORY 必须有事务,否则抛异常 NEVER 必须没事务,否则抛异常
6. spring的ioc和aop概念,aop底层原理(设计模式)
IOC:Inversion of Control
控制反转,将对象的创建和管理权交由IOC容器来进行管理。
AOP:Aspect Oriented Programming
(面向切面编程、面向方面编程),面向切面编程就是面向特定方法编程。AOP面向切面编程的一些优势:
- 代码无侵入:没有修改原始的业务方法,就已经对原始的业务方法进行了功能的增强或者是功能的改变
- 减少了重复代码
- 提高开发效率
- 维护方便
- 连接点:JoinPoint,可以被AOP控制的方法(暗含方法执行时的相关信息)
- 连接点指的是可以被aop控制的方法。例如:入门程序当中所有的业务方法都是可以被aop控制的方法。
- 通知:Advice,指哪些重复的逻辑,也就是共性功能(最终体现为一个方法)。
- 切入点:PointCut,匹配连接点的条件,通知仅会在切入点方法执行时被应用。
- 切面:Aspect,描述通知与切入点的对应关系(通知+切入点)
- 目标对象:Target,通知所应用的对象
通知类型: @Around:环绕通知,此注解标注的通知方法在目标方法前、后都被执行 @Before:前置通知,此注解标注的通知方法在目标方法前被执行 @After :后置通知,此注解标注的通知方法在目标方法后被执行,无论是否有异常都会执行 @AfterReturning : 返回后通知,此注解标注的通知方法在目标方法后被执行,有异常不会执行 @AfterThrowing : 异常后通知,此注解标注的通知方法发生异常后执行
切入点表达式:
- execution(访问修饰符? 返回值 包名.类名.?方法名(方法参数) throws 异常?)
- 其中带
?
的表示可以省略的部分- 可以使用通配符描述切入点
*
:单个独立的任意符号,可以通配任意返回值、包名、类名、方法名、任意类型的一个参数,也可以通配包、类、方法名的一部分..
:多个连续的任意符号,可以通配任意层级的包,或任意类型、任意个数的参数- @annotation基于注解的方式来匹配切入点方法。
- @Before(“@annotation(com.yishooo.anno.MyLog)”)
AOP底层原理:动态代理
- 基于jdk的原生操作(java.lang.reflect)
- 基于cglib操作(org.springframework.cglib.proxy) spring-core
public interface UserService { public void add(); public void edit(); public void delete(Integer id); public int selectCount(); }
public class UserServiceImpl implements UserService { @Override public void add() { System.out.println("保存用户"); } @Override public void edit() { System.out.println("编辑用户"); } @Override public void delete(Integer id) { System.out.println("删除" + id); } @Override public int selectCount() { System.out.println("统计总记录数"); return 10; } }
package com.yishooo.jdk; import com.yishooo.service.UserService; import com.yishooo.service.impl.UserServiceImpl; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * @Auther: Yishooo * @Date: 2023-5-9 - 05 - 09 - 19:52 * @Description: 基于jdk的aop底层原理 */ public class Demo { public static void main(String[] args) { UserService userService = new UserServiceImpl();//目标对象 /** * 参数1:类加载对象,传递方式固定,一般目标对象是哪个类,加载器加载的就传递哪个类加载器 * 参数2:接口的字节码数组,传递方式固定,目标对象所在类实现了哪些接口,就传递哪些接口的字节码数组 * 参数3:方法调用的处理器,需要在内部的 invoke 方法中编写增强的核心逻辑 */ UserService proxy = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), new InvocationHandler() { /** * 调用代理对象的方法,都会触发invoke * @param proxy 代理对象的引用。注意:尽可能不要在 invoke 中操作 proxy,特别是调用它的方法 * @param method 被调用的代理对象的方法 * @param args 调用代理对象的方法时传递的实际参数 * @return 用于控制调用代理对象方法后得到的返回值 * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if ("add".equals(method.getName())) { System.out.println("方法执行前"); return method.invoke(userService, args); } else if ("delete".equals(method.getName())) { args[0] = 100; return method.invoke(userService, args); } else if ("selectCount".equals(method.getName())) { return method.invoke(userService, args); } //其他方法原样调用 return method.invoke(userService, args); } }); proxy.add(); /** 执行结果: * 方法执行前 * 保存用户 */ } }
package com.yishooo.cglib; import com.yishooo.service.impl.UserServiceImpl2; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.Method; /** * @Auther: Yishooo * @Date: 2023-5-9 - 05 - 09 - 20:03 * @Description: 基于cglib的aop底层原理 */ public class Demo { public static void main(String[] args) { UserServiceImpl2 userServiceImpl2 = new UserServiceImpl2();//内容与UserServiceImpl一致 UserServiceImpl2 proxy = (UserServiceImpl2) Enhancer.create(userServiceImpl2.getClass(), new MethodInterceptor() { @Override public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { if ("add".equals(method.getName())){ System.out.println("方法执行前"); return method.invoke(userServiceImpl2,args); }else if ("delete".equals(method.getName())){ args[0] = 100;//修改传入参数9为100 return method.invoke(userServiceImpl2,args); }else if ("selectCount".equals(method.getName())){ return method.invoke(userServiceImpl2, args); } //其他方法原样调用 return method.invoke(userServiceImpl2, args); } }); proxy.delete(9);//执行结果:删除100 } }
7.springboot自动配置原理,springboot常用注解
SpringBoot的自动配置就是当Spring容器启动后,一些配置类、bean对象就自动存入到了IOC容器中,不需要手动去声明,从而简化了开发,省去了繁琐的配置操作。
自动配置原理源码入口就是@SpringBootApplication注解,在这个注解中封装了3个注解,分别是:
@SpringBootConfiguration
声明当前类是一个配置类
@ComponentScan
进行组件扫描(SpringBoot中默认扫描的是启动类所在的当前包及其子包)
@EnableAutoConfiguration
封装了@Import注解(Import注解中指定了一个ImportSelector接口的实现类)
在实现类重写的selectImports()方法,读取当前项目下所有依赖jar包中META-INF/spring.factories、META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports两个文件里面定义的配置类(配置类中定义了@Bean注解标识的方法)。
当SpringBoot程序启动时,就会加载配置文件当中所定义的配置类,并将这些配置类信息(类的全限定名)封装到String类型的数组中,最终通过@Import注解将这些配置类全部加载到Spring的IOC容器中,交给IOC容器管理。在META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件中定义的配置类非常多,而且每个配置类中又可以定义很多的bean,这些bean并不是都会注册到Spring的IOC容器中。在声明bean对象时,上面有加一个以@Conditional开头的注解,这种注解的作用就是按照条件进行装配,只有满足条件之后,才会将bean注册到Spring的IOC容器中。
8.jwt
- 概念:
- JWT全称:JSON Web Token 是一种简洁的、自包含的格式,用于在通信双方以json数据格式安全的传输信息。jwt令牌实质是一个字符串由Header(头),Payload(有效载荷),Signature(签名)三个部分组成,三个部分之间使用英文的点来分割。
- 组成:
- 第一部分:Header(头), 记录令牌类型、签名算法等。 例如:{“alg”:“HS256”,“type”:“JWT”}
- 第二部分:Payload(有效载荷),携带一些自定义信息、默认信息等。 例如:{“id”:“1”,“username”:“Tom”}
- 第三部分:Signature(签名),防止Token被篡改、确保安全性。将header、payload,并加入指定秘钥,通过指定签名算法计算而来。
- 应用场景登录认证。
- 在浏览器发起请求来执行登录操作,此时会访问登录的接口,如果登录成功之后,需要生成一个jwt令牌,将生成的 jwt令牌返回给前端。
- 前端拿到jwt令牌之后,会将jwt令牌存储起来。在后续的每一次请求中都会将jwt令牌携带到服务端。
- 服务端统一拦截请求之后,先来判断一下这次请求有没有把令牌带过来,如果没有带过来,直接拒绝访问,如果带过来了,还要校验一下令牌是否是有效。如果有效,就直接放行进行请求的处理。
- JWT是如何将原始的JSON格式数据,转变为字符串的呢?
- 其实在生成JWT令牌时,会对JSON格式的数据进行一次编码:进行base64编码
- Base64:是一种基于64个可打印的字符来表示二进制数据的编码方式。既然能编码,那也就意味着也能解码。所使用的64个字符分别是A到Z、a到z、 0- 9,一个加号,一个斜杠,加起来就是64个字符。任何数据经过base64编码之后,最终就会通过这64个字符来表示。当然还有一个符号,那就是等号。等号它是一个补位的符号
- 需要注意的是Base64是编码方式,而不是加密方式。