# ISO
**Repository Path**: wan_you_to/iso
## Basic Information
- **Project Name**: ISO
- **Description**: Java学习笔记项目
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2024-09-14
- **Last Updated**: 2024-11-07
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 异常处理
## Throwable:所有错误和异常的超类
Error:程序本身无法处理的一种严重问题,一般是系统级别的问题,如:系统崩溃、虚拟机错误等,无法恢复、无法捕捉。
Exception:异常,是程序设计方面的错误,为非致命的错误,一般可以通过异常捕捉使来处理。
RuntimeException(不检查):运行时异常,是程序缺陷所引起的异常,java虚拟机不会去检查此错误,程序应该从逻辑角度尽可能避免这类异常的发生。
NullPointerException :空指针异常
classnotfoundexception:类不存在异常
illegalargumentexception:方法的参数异常
NoSuchMethodError:方法不存在
NumberFormatException :数字格式异常
ClassCastException :类型强制转换异常。
IllegalAccessException :无访问权限异常
IllegalArgumentException:传递非法参数异常。
ArithmeticException :算术运算异常(用了0作为除数)
ArrayIndexOutOfBoundsException :数组下标越界异常
IndexOutOfBoundsException :下标越界异常
………
非运行时异常(检查):是必须进行处理的异常,如果不处理,程序就不能编译通过。
IOException :io异常
SQLException :sql语句异常
在Java中异常的种类多种多样,若是使用try…catch…finally去匹配异常类型,会导致大量的代码冗余,且用户端在报异常时拿到的数据不可控
所以需要配置一个全局的异常处理,当程序报错时返回给用户的数据,若是接口中使用了try…catch…finally则不影响原逻辑。
## 使用
```java
// 全局异常处理器
/*
* 此配置将所有的异常全部格式化,以规定的处理抛出异常
* 原本的异常是直接抛出,而使用全局异常则会将异常拦截后处理抛出
*
* */
@RestControllerAdvice
public class Global {
@ExceptionHandler(Exception.class)// Exception.class 所有的异常
public ResultList ex(Exception ex){
ex.printStackTrace();
return ResultList.error("系统错误,请联系管理员");
}
}
```
# JWT登录鉴权
## 作用
对用户登录的信息进行加密返回给用户,用户对接其他需鉴权的接口携带参数进行鉴权
## 使用
### 配置
需要在pom.xml文件中加入依赖即可
```xml
io.jsonwebtoken
jjwt
0.9.1
io.jsonwebtoken
jjwt-api
0.11.2
io.jsonwebtoken
jjwt-impl
0.11.2
runtime
io.jsonwebtoken
jjwt-jackson
0.11.2
runtime
```
### 加密
```java
import java.util.Map;
public String GetJWT(Map claims) {
// JWT加密生成token
return Jwts.builder()
.signWith(SignatureAlgorithm.HS256, "zhangsan")// 签名算法HS256,加密字符串;
.setClaims(claims)// 自定义加密内容
.setExpiration(new Date(System.currentTimeMillis() + 3600 * 1000))// 设置有效期为1h
.compact();// 生成jwt返回字符串
}
Map claims = new HashMap<>();
claims.put("id","张三");
String jwt = GetJWT(claims);
```
加密过程主要通过HS256进行加密,并且设置时效为1h
### 解密
```java
public Claims ParseJWT(String str){
return Jwts.parser()
.setSigningKey("zhangsan")// 解密字符串
.parseClaimsJws(str)// 解密内容
.getBody();// 返回数据
}
```
## 区别
cookie和seesion与token
1. cookie在响应体中携带具体数据,每次在用户的请求体中浏览器会自动携带,数据未加密不安全,并且浏览器可限制禁用token
2. seesion是服务端发送id值的形式给用户,用户得到的不是具体值,具体数据在服务端,相对安全,但是不适合当下的服务器集群环境。并且具备cookie的所有缺点
3. token是将数据加密后进行传输,浏览器只能删除token值删除后可以重新获取token,前端也可验证token是否过期,使用HS256加密相对安全
# 拦截器与过滤器
## 过滤器
### @WebFilter(urlPatterns = "/*")
在过滤器类上加上@WebFilter注解表示为该此程序开启一个过滤器,对对应的接口生效
配置过滤器需要过滤哪些数据/*表示过滤全部接口,/login表示只会拦截login路径,/emps/*只会拦截emps下的所有路径比如/emps/1
### 状态
1. init:在程序开始时执行一次,
2. doFilter:在用户每次访问接口时都需要执行,**由于该方法用于控制接口返回,所以需要在合适的位置放行否则接口内容无法访问**
3. destory:在程序关闭时执行一次,多用于记录程序的异常关闭的情况
```java
@WebFilter(urlPatterns = "/*")
public class DemoFilter implements Filter {
public void init(){};// 初始化方法,只会调用一次
// 拦截请求之后调用,会调用多次
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain){
chain.doFilter(request,response);
return;
}
public void destory(){}// 销毁方法,只会调用一次
}
```
### 使用
常用的过滤器方法是doFilter,该方法会在每次调用接口时都运行。一般用于应用的前置登录鉴权
1. ServletRequest request:请求体
2. ServletResponse response:数据返回体
3. FilterChain chain:放行拦截
### 搭配JWT登录鉴权使用
```java
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
log.info("请求过滤器启动。。。");
HttpServletRequest req= (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
// 获取请求url
String url = req.getRequestURL().toString();
log.info("请求过滤器获取到的url地址为:{}",url);
// 判断请求url中是否包含login,如果包含,说明是登录操作,放行
if(url.contains("login")){
log.info("登录操作,放行...");
// 放行
chain.doFilter(request,response);
return;
}
// 获取请求头中的令牌
String token = req.getHeader("token");
// 判断令牌是否存在,如果不存在则返回错误(未登录)
if(!StringUtils.hasLength(token)){
log.info("token不存在,未返回登录的信息");
ResultList error = ResultList.error("请求头中未携带token");
// 手动转换对象--json------->阿里巴巴fastjson
String notLogin = JSONObject.toJSONString(error);
// 使用response.getWriter().write返回数据给前端
response.getWriter().write(notLogin);
return;
}
// 解析token ,如果解析失败则令牌过期
try{
JWTUtils.ParseJWT(token);
}catch (Exception e){
log.info("token已过期");
ResultList error = ResultList.error(401,"token已过期,请重新登录");
// 手动转换对象--json------->阿里巴巴fastjson
String notLogin = JSONObject.toJSONString(error);
// 使用response.getWriter().write返回数据给前端
response.getWriter().write(notLogin);
return;
}
// 放行
chain.doFilter(request,response);
}
```
## 拦截器
### 配置
拦截器启动需要单独声明一个配置类来启动拦截器
```java
// 配置请求拦截器
@Configuration
@EnableAspectJAutoProxy
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LoginCheckInterceptor loginCheckInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(loginCheckInterceptor)
.addPathPatterns("/**") // 拦截所以请求
.excludePathPatterns("/interceptor/**"); // 排除以interceptor开头的接口
}
}
```
### 方法
1. preHandle:目标资源方法运行前运行,返回true放行,返回false不放行
2. postHandle:目标资源接口方法运行后运行
3. afterCompletion:视图渲染完毕后运行,最后运行
### 使用
拦截器在日常使用中preHandle使用最多,在preHandle方法中必须返回一个Boolean值判断程序是否进行下去
```java
@Override // 目标资源方法运行前运行,返回true放行,返回false不放行
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 获取请求url
String url = request.getRequestURI().toString();
log.info("请求拦截器获取到的url地址为:{}",url);
// 如果请求以/interceptor开头,直接放行
if (url.contains("interceptor")) {
log.info("请求为/interceptor开头,放行...");
return true;
}
// 判断请求url中是否包含login,如果包含,说明是登录操作,放行
if(url.contains("login")){
log.info("登录操作,放行...");
return true;
}
// 获取请求头中的令牌
String token = request.getHeader("token");
// 判断令牌是否存在,如果不存在则返回错误(未登录)
if(!StringUtils.hasLength(token)){
log.info("token不存在,未返回登录的信息");
ResultList error = ResultList.error("请求头中未携带token");
// 手动转换对象--json------->阿里巴巴fastjson
String notLogin = JSONObject.toJSONString(error);
// 使用response.getWriter().write返回数据给前端
response.getWriter().write(notLogin);
return false;
}
// 解析token ,如果解析失败则令牌过期
try{
JWTUtils.ParseJWT(token);
}catch (Exception e){
log.info("token已过期");
ResultList error = ResultList.error(401,"token已过期,请重新登录");
// 手动转换对象--json------->阿里巴巴fastjson
String notLogin = JSONObject.toJSONString(error);
// 使用response.getWriter().write返回数据给前端
response.getWriter().write(notLogin);
return false;
}
// 放行
return true;
}
```
## 拦截器与过滤器的对比
拦截器与过滤器的方式基本一样,但是拦截器的配置相对麻烦。需要单独声明一个配置类去使用
在http请求中是先经过filter过滤器再通过拦截器后去访问资源,所以按优先级来看拦截器的优先级更高

# 事务
## @Transactional
在类上加上@Transactional注解会在这个类开启一个事务
此事务遵循一个原则(同时提交或同时回滚)
xmlUser.creatStudent(stu);
int num = 1/0;
xmlUser.deleteFrom(12);
在执行上述代码时1/0会报Runtime Exception运行时异常错误,所以导致xmlUser.deleteFrom(12);不执行,加上注解事务会整体回滚
即xmlUser.creatStudent(stu);也执行不成功
## 事务属性
### rollbackFor
默认情况下,只有出现Runtime Exception运行时异常才会回滚,rollbackFor属性用于控制出现异常类型,回滚事务
@Transactional(rollbackFor = {IOException.class, SQLException.class})
常用的异常类有:
Exception:所有异常都触发回滚。
RuntimeException:所有运行时异常都触发回滚。(默认值)
Throwable:所有可抛出的异常都触发回滚。
### propagation
| 属性值 | 含义
|:---------------|:------------------------------|
| **REQUIRED** | 默认值,支持当前事务,如果不存在,则新建一个事务。 (常用)
| **REQUIRES_NEW** | 总是新建一个事务,如果存在一个事务,则挂起当前事务。(常用)
| NOT_SUPPORTED | 以非事务方式运行,如果存在事务,则挂起当前事务。
| MANDATORY | 如果存在事务,则在该事务中运行;否则抛出异常。
| NEVER | 以非事务方式运行,如果存在事务,则抛出异常。
| NESTED | 如果存在事务,则在嵌套事务中运行;否则新建一个事务。
# AOP(面向切面编程)
## 配置安装依赖
```xml
org.springframework.boot
spring-boot-starter-aop
```
## @Aspect
在类上添加该注解表示为spring项目添加开启一个AOP
## 定义通知类型
| 名称 | 注解名 | 含义
|:----|:----------------|:-------|
|前置通知| @Before |在目标方法调用之前调用通知
|后置通知| @After |在目标方法完成之后调用通知
|环绕通知| @Around(常用) |在被通知的方法调用之前和调用之后执行自定义的方法
|返回通知| @AfterReturning |在目标方法成功执行之后调用通知
|异常通知| @AfterThrowing |在目标方法抛出异常之后调用通知
通知注解中需要传入作用于哪个作用域方法的具体参数
## execution
```java
@Around("execution(* com.example.controller.*.*(..))")
/*
execution(修饰符?匹配目标类路径?具体方法?(参数个数))
* :表示访问修饰符(public,*)
com.example.controller: 目标方法在哪个路径下
.* :controller目录下所有的类<若是想只作用于哪个类需要直接.类名>
*server<通配server开头的类> server*<通配server结尾的类>
(..)方法需要携带的参数,..<任意个数的参数>,(*)方法必须携带一个参数的
*/
```
### @Pointcut
此注解可以提前声明类名路径即通知类型参数,全局可用,本类中使用直接方法名()其他AOP使用可以具体路径()
使用方法
```java
@Pointcut("execution(* com.example.controller.upload.*(..))")
public void put() {
}
// 声明类中使用
@Around("put()")
public void BeforeTime(){}
// 其他AOP类使用
@Around("com.example.aop.insertAop.put()")
public void BeforeTime(){}
```
## @annotation
同样这个也是作用于当前通知类型中的参数,不过这个是单类匹配,也就是需要哪个aop控制哪个方法就在那个方法上加注解就行
### 使用:
1. 声明对应注解
2. 将该注解声明在对应方法上
3. 在通知注解中携带@annotation(注解具体地址)
### 例如:
#### A. 声明注解
```java
//声明注解
/*
* @Retention(RetentionPolicy.RUNTIME):表示这个注解在运行时仍然有效,可以被反射机制读取和使用。
* @Target(ElementType.METHOD):表示这个注解只能被放置在类的方法上。
* 当前注解起一个标识的作用
*
* */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyLog {
}
```
#### B.作用注解到对应方法
```java
import com.example.aspect.MyLog;
@MyLog
public void List(){}
```
#### C.使用注解在AOP
```java
@Around("@annotation(com.example.aspect.MyLog)")
// @annotation(com.example.aspect.MyLog)这个标识符表示只有加上这个注解的类才会被aop控制
public Object recordTime(ProceedingJoinPoint joinPoint) throws Throwable {}
```
## @Order()
在AOP控制中可能会有多个AOP控制类,执行顺序为:
before:按类名字母排序正序
after:按类名字母排序倒序
AOP控制类多的情况下去改类名会比较麻烦,若是需要在修改执行先后顺序可以使用@Order(数字),括号中数字越小越先执行
## @Around
这里着重讲解一下Around这个通知,因为AOP中这个通知类使用最多,可操作最多
```java
// 获取目标对象的类名
String className = joinPoint.getTarget().getClass().getName();
// 获取目标方法的方法名
String methodName = joinPoint.getSignature().getName();
// 获取目标方法运行时传入的参数
Object[] args = joinPoint.getArgs();
// 获取目标方法运行后的返回值
Object result = joinPoint.proceed();// 调用原始操作
return result;// 在Around通知类中必须返回值,否则目标方法无法获取到值,相当于在这里把数据拦截了
```
上述讲解了一些Around中最常见的方法使用,总得来说AOP更像一种监控,监控管理员的增删改这种危险操作。以作备份操作权限
## @Bean
### 作用
1. 第三方的bean对象无法使用@Component以及其他衍生注解声明beaan,所以需要管理第三方bean就需要使用@Bean对第三方bean进行管理
2. 若是要对第三方的bean对象进行管理,建议对这些bean进行分类配置,可以通过@Configuration注解进行声明一个配置类
### 使用方式
1. 配置文件(推荐)
```java
@Configuration// 配置类注解
public class WebConfig implements WebMvcConfigurer {
@Bean// 声明Bean全局使用
public logInfo loginfo(){
return new logInfo();
}
}
```
2. 启动类配置(不推荐)
```java
@SpringBootApplication// springboot启动注解
class StudentsApplicationTests {
@Bean// 声明Bean全局使用
public logInfo loginfo(){
return new logInfo();
}
}
```
### 注意方式
1. 通过@Bean注解的name或value属性可以声明bean的名称,如果不指定,默认bean的名称就是方法名
2. 如果第三方bean需要依赖其他bean对象,直接在bean定义方法中设置形参即可,容器会默认自动装配
```java
import javax.xml.parsers.SAXParser;
@Bean// 声明Bean全局使用
public logInfo loginfo(SAXParser Saxparser) {
// 第三方需要这个bean依赖则可以直接以形参的方式装配
return new logInfo();
}
```
# maven版本锁定
## 锁定
在pom.xml文件中,挨个去找依赖的版本号是比较麻烦的,并且在工程与工程之间有联系的情况下无法准确的控制依赖版本
可以使用标签进行版本声明
```xml
3.17.4
com.aliyun.oss
aliyun-sdk-oss
${oss.version}
```
在上述演示中在properties标签中声明了oss.version对oss版本进行控制。