Springboot学习之基础2

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;
}

最后

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值