文章目录
AOP面向切面编程
概述
定义:
AOP 面向切面编程,Aspect Oriented Programming。
将复杂的需求分解为不同的切面,将散布在系统中的公共属性集中处理;
优点:
降低模块之间的耦合度;
更好的代码复用;
....
特殊概念:
切面 = 横切关注点,被模块化的抽象对象;
通知 = 切面对象完成的工作;
目标 = 被通知的对象
代理 = 切面、通知、目标混合之后的对象
连接点 = 通知要插入业务代码的具体位置
切点 = AOP通过切点定位到连接点
实现方式:
1 动态代理;
2 基于Spring方式;
参考博客:https://blog.csdn.net/pedro7k/article/details/115415675
实现
动态代理
pom.xml引入项目依赖,spring-aop、spring-aspects;
创建接口interfacexx,定义方法;
创建实现该接口的类classxx,重写具体的方法(包含日志信息处理逻辑),存在较强的耦合性;
动态代理实现日志信息输出;
涉及到内容:invocationHandler接口
动态代理核心代码:
public class invocationHandlerxxx implement invocationHandler{
// 实现InvocationHandler接口,创建动态代理对象的辅助类
private Object object = null;
// 接收委托都对象,使用静态代理对象的类;
// bind对象 获取代理对象
public Object bind(Object object){
// 通过委托对象获取对应的代理类;
this.object = object;
//创建代理对象:所需参数:类加载器、委托对象的全部接口、当前的辅助类;
// 返回内容是动态代理对象;
return Proxy.newProxyInstance(object.getClass().getClassloader(),object.getClass().getInterfaces(),this);
}
@Overrid
// 该方法写代理对象的功能,参数为代理对象、对应方法、方法参数;
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
// 处理过程
sout(method.getName()+"方法参数:"+Arrays.toString(args));
// 调用方法本身的业务逻辑
Object result = method.invoke(this.object,args);
// 结果输出
sout(method.getName()+"的结果是:"+result);
return result;
}
}
Spring实现AOP
添加依赖,spring-aop、spring-aspects、spring-context等,在pom.xml文件中;
定义接口interfacexxx,以及抽象方法;
定义实现类classxxx,已经普通方法;
创建切面类;
切面类:
@Slf4j
// 日志注解,使用logger对象
@Aspect
// 声明是一个切面类
@Component
// 把切面类交给IoC容器管理
public class LogsAspect {
@Around("(execution(* com.unicom.xxx.xxx.xxx.xxx.*.*(..))))")
// @Around 表示在方法的执行前后插入代码,即环绕通知;
// execution 用于匹配方法的执行;
// 表示在正则表达式匹配的所有方法 执行前后插入代码
public Object aroundMethod(ProceedingJoinPoint pjp) throws Throwable {
// 环绕通知方法的声明,ProceedingJoinPoint是Spring AOP中的一个连接点的接口,它允许你调用被通知的方法;
try {
// 获取当前的业务跟踪信息
ServiceTrace.BusinessTrace trace = ServiceTrace.BUSINESS_TRACE.get();
ServiceId serviceId = null;
if(trace == null || trace.getCover()){
// 获取当前执行的方法的签名 给service赋值
serviceId = ((MethodSignature) pjp.getSignature()).getMethod().getAnnotation(ServiceId.class);
//绑定 服务描述
if(ObjectUtils.isNotEmpty(serviceId)){
ServiceTrace.bind(null,serviceId.value().name());
}else{
ServiceTrace.bind();
}
}else{
ServiceTrace.bind();
}
// 调用实际的方法执行,并获取返回结果。
Object response = pjp.proceed();
log.info(LogConstant.response_format, response);
return response;
} finally {
// 释放serviceTrace资源
ServiceTrace.release();
}
}
}
// Pointcut定义切点,后续括号的参数是:匹配到所有方法的执行
@Pointcut("execution(* com.unicom.anon.sub.local.controller.*.*(..))")
// 用来声明一个后置返回通知;会在目标方法成功执行并返回结果之后执行;
// pointcut 指定通知应该应用的切点;
// returning 指定通知方法可以访问目标方法的返回值;result是目标方法返回值的别名;
@AfterReturning(pointcut = "controllerLayerPointcut()", returning = "result")
// joinPoint 当前的连接点,被通知的方法
// result 目标方法的返回值
public void logAfterReturning(JoinPoint joinPoint, Object result) {
// 获取方法名
String methodName = joinPoint.getSignature().toShortString();
// 记录日志
// logger.info("Method [{}] returned with object: {}", methodName, GsonBuilderUtil.GSON_DEFAULT.toJson(result));
AnonSubLog.log("Method [{}], returned with object: {}", methodName, GsonBuilderUtil.GSON_DEFAULT.toJson(result));
}
实战
@Before不起作用
描述:
给xxxAop配置了@Aspect和@Order注解,但还是没有办法调用@Before修饰的方法;
分析:
发现请求没有到达服务侧,采用的是post请求不行,但是get请求可以;
@Before获取接口参数
@Before("execution(* com.unicom.opn.api.core.controller.RiskController.*(..))")
// 定义一个切面的切入点,指定了哪些方法调用会被拦截,以便在这些方法调用前后执行特定的增强逻辑;
public void beforeMethod() {
// 接收到请求,记录请求内容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
// RequestContextHolder,用于检索和存储当前线程的上下文请求;
// .getRequestAttributes 获取当前线程的请求上下文;
// ServletRequestAttributes 用于处理基于Servlet的Web请求;
HttpServletRequest request = attributes.getRequest();
// 获取当前请求的HttpServletRequest对象
if ("GET".equalsIgnoreCase(request.getMethod()))
// 获取当前HTTP请求的方法类型
else if ("POST".equalsIgnoreCase(request.getMethod())) // 获取当前HTTP请求的方法类型
String requestBody = request.getReader().lines().collect(Collectors.joining(System.lineSeparator()));
// request.getReader() 获取BufferedReader对象;
// lines() 将BufferedReader中的所有行转换为一个Stream<String>
// collect() 将 Stream<String> 中的所有行合并成一个单一的字符串,每行之间用系统默认的行分隔符(如 \n 或 \r\n)连接。
集成 web开发(业务核心)
静态资源导入
方式:
通过resource/static文件夹 (现有项目就是该方式);
通过http://localhost:8080/webjars/xxx/xxx/xxx.js 实现;
其中webjars 等同于 /META-INF/resources/webjars/ 目录;
pom中导入依赖实现,具体的依赖形式需要从https://www.webjars.org/ 网站查看;
通过http://localhost:8080/xxx.js
项目calsspath即项目/src/main/resources 其下面的目录 /resources/ /static/ /public/ 都可以放静态资源;
各个目录的优先级:resources > static > public
自定义静态资源目录,配置文件中写:spring.mvc.static-path-pattern=xxxx,其余配置文件都失效;
通过xxx查看可以有哪种方式导入静态资源:
webMVCAutoConfiguration.java--webMvcAutoConfigurationAdaptor内部类--addResourceHandlers方法;
web首页如何定制
通过xxx查看可以有哪种方式导入静态资源:
webMVCAutoConfiguration.java--welcomePageHandlerMapping内部类
放置路径:
http://localhost:8080/webjars/xxx/xxx/xxx.js webjars = 文件路径/META-INF/resources/webjars/
http://localhost:8080/xxx.js 文件路径 classpath: /resources/ /static/ /public/
classpath:/resources/template:
在该目录下的静态页面,只能通过controller实现访问;
但是需要加模板引擎,Thymeleaf等;
项目降springboot版本:
修改pom文件中的parent/version。
模板引擎
定义:
如thymeleaf
使用文档:https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#using-thtext-and-externalizing-text
作用:
核心是替换,将模板文件的标签字符替换成指定的业务数据,生成html文件;
跳转html页面:
引入pom依赖 ==》通过maven官网下载:https://mvnrepository.com/
在/resource/template/ 文件夹下添加 xxx.html
在java路径下 写controller类,@RequestMapper("/xxx") 修饰某个方法,跳转xxx.html
html页面java代码中给定的值:
java方法:
@RequestMapper("/xxx")
public String xxx(Model model){
model.addAttribute("msg","cptbtptpbcptdtptp");
return "xxx";
}
xxx.html:
<div th:text="${msg}"></div>
注意:
所有引入的包 都有xxxProperties类;
过滤器 Filter
概念:
可以改变一个request 和 修改一个response;
即客户端请求访问后端资源之前,可以拦截;服务器响应发送回客户端之前,可以拦截;
filter使用
实现Filter接口
🍇 过滤器定义
public class ExceptionFilter implements Filter {
// 含有三个方法:init、doFilter、destory
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void destroy() {}
// 核心方法
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { }
// 三个参数:请求-request,响应-response,过滤器链-chain
// 实现链式调用;
// 处理请求
chain.doFilter(request,response);
// 处理响应;
}
继承OncePerRequestFilter抽象类
在JWT中介绍
过滤器注册
有三种方式:
1. 利用WebFilter注解配置
在自定义filter类上添加注解 即
@WebFileter(filterName="xxx",urlPatterns={"xxx"}) // filterName 过滤器名字;urlPatterns需要拦截的访问路径
public class xxxFilter extends Filter{}
缺点:无法保证过滤器之间的执行顺序;
当前方式:是按照名字的字母表方式,执行过滤器;
还需要在启动类上 添加@ServletComponentScan注解;
2. FilterRegistrationBean方式
需要新定义一个类;
public class FilterRegistration{
@Bean
public FilterRegistrationBean filterRegistrationBean(){
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new xxxFilter()); // 设置filter
registration.setName("xxxFilter"); // 定义过滤器名称
registration.addUrlPatterns("xxx"); // 设置拦截路径
registration.setOrder(10); // 设置顺序
return registration;
}
}
3. 配置成Bean
将过滤器配置成Bean,注册到Spring容器中去;
@Component
@Order(xxx) //可以设置过滤器的级别
public class xxxFilter extends Filter{}
缺点:不能设置拦截规则,即拦截路径;(已知项目采用的方式)
过滤器链
定义:
每个Filter程序都可以针对某一个URL进行拦截,如果多个Filter程序对同一个URL进行拦截,这些Filter就会组成一个Filter链,即过滤器链;
理解:
假设访问服务器资源会经过两个过滤器,即filter1和filter2,首先filter1会对url进行处理,然后通过filter1的doFilter()方法将请求传递给filter2.
其中filter1和filter2都会执行filterx.doFilter();方法
使用:
xxxFilter对象中的doFilter方法,该方法参数包含ServletRequest request, ServletResponse response, FilterChain chain参数,其中使用chain.doFilter(request,response) 就是过滤器链。
执行流程:
过滤器初始化,现一般采用@Component注解;
请求到达过滤器链,每个过滤器在doFilter方法执行自己的逻辑,然后调用过滤器链上的下一个过滤器的doFilter方法;
Servlet处理请求,处理请求,返回给过滤器链;
响应经过过滤器链,按照与请求相反的顺序经过过滤器,执行自己的doFilter方法,然后调用下一个过滤器的doFilter方法;
过滤器销毁,应用关闭或者卸载时,过滤器被销毁;
相关思考
多次处理问题
描述:
同一个应用中使用过滤器filter,可能会存在单个请求被同一个过滤器多次处理的情况;
分析:
如何产生?
重定向的情况:当一个请求被重定向时,浏览器会发送一个新的请求到指定位置;就算重定向前的请求经过了某个过滤器,重定向请求仍有可能再次经过某过滤器;
转发:当Servlet容器内部对请求进行转发时,转发的目标资源可能还会再次经过相同的过滤器;
循环引用:存在复杂逻辑的应用中,可能存在循环引用的情况,可能会在某种逻辑上导致请求会重新回到之前经过的过滤器。
解决方式:
// xFilter过滤器的doFilter方法
Object isProcessed = request.getAttribute("logfilter.processed");
// 给请求设置某个参数标识 请求经过x过滤器;
if (isProcessed != null && (boolean) isProcessed) {
// 已经经过LogFilter的处理。
chain.doFilter(request, response);
return;
}
request.setAttribute("logfilter.processed", true);
拦截器 Interceptor
断言 Assert
是什么?
断言(Assert)是一种常用的编程手段,用于校验业务逻辑中的异常情况。
断言可以简化代码,消除冗长的if-else语句,提高代码的可读性和维护性。
JWT
概述
定义:
用于生成一个 JSON Web Token (JWT) 的过程。
JWT 是一种轻量级的、基于 JSON 的标准,用于在双方之间安全地传输信息。
案例
🍇 案例1
Jwts.builder() // 是 JWT 库(通常是 io.jsonwebtoken)中的一个方法,用于创建一个 JWT 令牌的构建器(builder)。
.setSubject() // 设置 JWT 令牌的 主题(Subject) 。主题通常表示令牌的主体,比如用户的唯一标识。
.setIssuedAt() // 设置 颁发时间(Issued At) 。
.setClaims() // 设置 声明(Claims) 。声明是 JWT 中包含的其他信息,可以是任何 JSON 数据。参数可以是Map集合;
.setExpiration() // 设置 过期时间(Expiration);
.signWith() // 设置 签名算法(Signature Algorithm) 和 密钥(Secret Key)
.compact(); // 生成最终的 JWT 令牌字符串。这个方法将所有设置的内容(主题、颁发时间、声明、过期时间等)编码并签名,生成一个紧凑的字符串形式的 JWT。
🍇 案例2
Jwts.parser() // 创建一个 JWT 解析器
.setSigningKey() // 设置用于验证签名的密钥 参数String类型
.parseClaimsJws() // 验证 JWT 的签名,并解析出头部、负载和签名。参数String类型;
.getBody(); // 提取负载部分claims
SpringSecurity
HTTPSecurity
案例:
http
.csrf().disable() // 禁用跨站请求伪造(CSRF)保护。
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() // 设置会话创建策略为无状态(STATELESS)
.authorizeRequests() // 定义请求的授权规则
.antMatchers("").permitAll() // 允许某些请求无需认证即可访问。
.antMatchers("").hasAuthority("USER") // 某些请求需要添加USER权限才能访问。
.anyRequest("").authenticated() // 所有未明确配置的请求都需要认证才能访问。
.headers().frameOptions().disable(); // 禁用点击劫持保护(X-Frame-Options头)
http.addFilterBefore(); // 添加自定义过滤器
集成 数据库 Druid
分布式开发:Dubbo(RPC) + zookeeper
swagger:接口文档
任务调度
SpringSecurit:Shiro
本质上是:拦截器与过滤器就可以实现安全功能;
Linux项目部署
异常处理
try-catch
全局异常处理
🍇 异常1
@ExceptionHandler({Throwable.class}) // 该方法可以捕获所有异常
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) // 此方法处理后,http响应的状态码会被设置为500
@ResponseBody // 该方法返回的内容将直接作为http响应体的一部分
public Object throwable(Throwable e) {
log.error("全局异常信息 ex : {}", e.toString(), e);
/*
* 如果為api接口則按照api接口的方式進行
**/
ResultDto result = ResultDto.result(ResultCodeEnum.SystemInternalError);
log.info(LogConstant.response_format,result);
return result;
}
2555

被折叠的 条评论
为什么被折叠?



