AOP

在AOP(Aspect Oriented Programming,面向切面编程)中,切面、切点和通知是三个核心概念,它们的含义如下:
切面(Aspect)
切面是AOP中的一个核心概念,它封装了横切关注点,如日志记录、性能监控、安全控制等。横切关注点是指那些在多个类中重复出现,但又与业务逻辑无关的代码,如日志记录、性能监控、安全控制等。通过切面,可以将这些横切关注点从业务逻辑中分离出来,实现关注点的模块化。
切点(Pointcut)
切点是AOP中的另一个核心概念,它定义了切面所关注的代码范围,即哪些代码需要被切面所影响。切点通常是一个匹配规则,用于匹配那些需要被切面所影响的方法或类。例如,你可以定义一个切点,用于匹配所有在com.example.service包下的类的所有方法。
通知(Advice)
通知是AOP中的另一个核心概念,它定义了切面在切点处所执行的代码,即当切点处的代码被调用时,切面所执行的代码。通知通常是一个方法,它在切点处的代码被调用时被调用。通知有多种类型,如前置通知(Before Advice)、后置通知(After Advice)、环绕通知(Around Advice)等。
总的来说,AOP是一种编程范式,它通过切面、切点和通知等概念,将横切关注点从业务逻辑中分离出来,实现关注点的模块化。切面封装了横切关注点,切点定义了切面所关注的代码范围,通知定义了切面在切点处所执行的代码。通过AOP,可以实现代码的解耦,提高代码的可维护性和可扩展性。

在Spring AOP(Aspect Oriented Programming,面向切面编程)中,切面(Aspect)和通知(Advice)是两个核心概念,它们之间存在层级关系。
切面(Aspect):切面是封装了横切关注点的模块,它由一个或多个通知和一个或多个切入点组成。切面是AOP的核心,它定义了哪些代码将被增强,以及如何增强。在Spring AOP中,切面通常由一个类实现,这个类包含了通知和切入点的定义。
通知(Advice):通知是在切面定义的切入点上执行的代码,它是在程序执行的某个特定点插入的代码,如方法调用前、方法调用后、方法抛出异常后等。在Spring AOP中,通知通常由一个方法实现,这个方法将在匹配的切入点上执行。
切入点(Pointcut):切入点是通知将被应用到的代码的某个位置,它定义了通知将被应用到哪些连接点(Joinpoint)上。在Spring AOP中,切入点通常由一个表达式定义,这个表达式匹配了将被增强的方法或类。
因此,切面、通知和切入点之间的层级关系如下:
切面包含了通知和切入点,一个切面可以包含一个或多个通知,一个切面可以包含一个或多个切入点。
通知是在切面定义的切入点上执行的代码,一个通知只能在一个切面中定义,但是一个切面可以包含一个或多个通知。
切点定义了通知将被应用到的代码的某个位置,一个切点只能在一个切面中定义,但是一个切面可以包含一个或多个切点。
在Redis中,你可以同时使用RDB(Redis Database Backup)和AOF(Append Only File)两种持久化方式。当Redis启动时,如果同时存在RDB和AOF文件,那么Redis会优先使用AOF文件来恢复数据,因为AOF文件的数据更完整。当Redis运行时,RDB和AOF两种持久化方式会同时工作,RDB会定期创建数据集的时间点快照,AOF会记录服务器执行的所有写入操作命令。当Redis需要进行数据恢复时,它会优先使用AOF文件,如果AOF文件不存在或者无法使用,那么它会使用RDB文件。

MySQL提供了多种类型的日志文件,每种日志文件都有其特定的用途。以下是一些主要的日志文件:
错误日志(Error Log):错误日志记录了MySQL服务器启动、关闭以及运行时发生的错误信息。错误日志对于诊断和解决问题非常有帮助。错误日志的文件名默认为hostname.err。
二进制日志(Binary Log):二进制日志记录了所有更改数据的SQL语句,但不包括SELECT或SHOW语句。二进制日志主要用于数据恢复和主从复制。二进制日志的文件名默认为hostname-bin.index和hostname-bin.000001等。
慢查询日志(Slow Query Log):慢查询日志记录了执行时间超过指定阈值的SQL语句。慢查询日志对于性能调优非常有帮助。慢查询日志的文件名默认为hostname-slow.log。
一般查询日志(General Query Log):一般查询日志记录了所有发送到MySQL服务器的SQL语句,包括SELECT和SHOW语句。一般查询日志的文件名默认为hostname.log。
审计日志(Audit Log):审计日志记录了所有对MySQL服务器的访问和操作,包括登录、登出、查询、修改等。审计日志对于安全审计非常有帮助。
InnoDB日志(InnoDB Log):InnoDB日志包括重做日志(Redo Log)和回滚日志(Undo Log)。重做日志记录了所有更改数据的操作,用于数据恢复。回滚日志记录了所有未提交的事务,用于事务回滚。
慢启动日志(Slow Start Log):慢启动日志记录了启动时间超过指定阈值的线程。慢启动日志对于性能调优非常有帮助。
以上日志文件的配置和管理可以通过MySQL的配置文件my.cnf或my.ini,以及SHOW VARIABLES和SET等SQL语句进行。

MySQL的主从复制是MySQL提供的一种数据复制机制,可以将一个MySQL服务器(主服务器)的数据复制到一个或多个MySQL服务器(从服务器)。主从复制可以用于数据备份、读写分离、负载均衡等场景。以下是MySQL主从复制的实现步骤:
开启二进制日志:在主服务器上,需要开启二进制日志。二进制日志记录了所有更改数据的SQL语句,但不包括SELECT或SHOW语句。在my.cnf或my.ini配置文件中,设置log_bin参数,例如log_bin = /var/log/mysql/mysql-bin.log。
设置服务器ID:在主服务器和从服务器上,需要设置唯一的服务器ID。在my.cnf或my.ini配置文件中,设置server-id参数,例如server-id = 1。
创建复制用户:在主服务器上,需要创建一个用于复制的用户。例如,执行以下SQL语句:CREATE USER 'repl'@'%' IDENTIFIED BY 'password';,GRANT REPLICATION SLAVE ON . TO 'repl'@'%';。
获取二进制日志的位置:在主服务器上,需要获取二进制日志的文件名和位置。例如,执行以下SQL语句:SHOW MASTER STATUS;。
配置从服务器:在从服务器上,需要配置复制源和复制位置。在my.cnf或my.ini配置文件中,设置server-id参数,例如server-id = 2。然后,执行以下SQL语句:CHANGE MASTER TO MASTER_HOST='主服务器IP', MASTER_USER='repl', MASTER_PASSWORD='password', MASTER_LOG_FILE='mysql-bin.000001', MASTER_LOG_POS=107;,其中MASTER_LOG_FILE和MASTER_LOG_POS是主服务器上获取的二进制日志的文件名和位置。
启动复制:在从服务器上,需要启动复制。执行以下SQL语句:START SLAVE;。
检查复制状态:在从服务器上,可以检查复制的状态。执行以下SQL语句:SHOW SLAVE STATUS\G;。
以上是MySQL主从复制的基本实现步骤,但是实际的配置和管理可能需要根据具体的需求和环境进行调整。例如,可以设置复制过滤规则,只复制部分数据库或表;可以设置复制延迟,使从服务器的数据落后于主服务器;可以设置复制模式,只复制部分类型的SQL语句;可以设置复制故障自动恢复,使从服务器在故障后自动恢复复制等。

MASTER_LOG_POS=107中的107指的是二进制日志(Binary Log)的位置,具体来说,是二进制日志文件中的一个字节位置。第107个字节是上一次复制的记录的结束位置,那么当你在从服务器上执行CHANGE MASTER TO MASTER_LOG_POS=107;时,从服务器就会从mysql-bin.000001文件的第107个字节开始读取二进制日志,复制之后的数据 ,MASTER_LOG_POS参数的值需要在主服务器上获取,通常是在执行SHOW MASTER STATUS;语句后,从返回的结果中找到File和Position字段

InnoDB Log(InnoDB日志):
InnoDB是MySQL的一种存储引擎,它提供了事务、行级锁和外键等特性。InnoDB日志包括重做日志(Redo Log)和回滚日志(Undo Log)。
重做日志(Redo Log):当InnoDB存储引擎在事务提交时,会将事务的更改信息写入重做日志。如果服务器崩溃,InnoDB会使用重做日志来恢复未写入磁盘的数据,保证事务的持久性。重做日志的使用场景通常是在服务器重启或崩溃后,用于恢复未提交的事务。
回滚日志(Undo Log):当InnoDB存储引擎在事务开始时,会将事务的原始数据写入回滚日志。如果事务回滚,InnoDB会使用回滚日志来恢复数据,保证事务的原子性。回滚日志的使用场景通常是在事务回滚时,用于恢复数据。
Binary Log(二进制日志):
二进制日志是MySQL的一种日志,它记录了所有更改数据的SQL语句,但不包括SELECT或SHOW语句。二进制日志的使用场景通常是在主从复制和数据恢复中。
主从复制:在MySQL的主从复制中,从服务器会读取主服务器的二进制日志,执行其中的SQL语句,将数据复制到从服务器。二进制日志的使用场景通常是在主从复制中,用于复制数据。
数据恢复:在MySQL的数据恢复中,可以使用二进制日志来恢复数据。例如,如果在某个时间点,数据被意外删除或修改,可以使用二进制日志来恢复数据。二进制日志的使用场景通常是在数据恢复中,用于恢复数据。
总的来说,InnoDB日志和二进制日志的使用场景不同,InnoDB日志主要用于保证事务的原子性和持久性,而二进制日志主要用于主从复制和数据恢复。但是,InnoDB日志和二进制日志也可以结合使用,例如,在主从复制中,可以使用InnoDB日志来保证事务的原子性和持久性,同时使用二进制日志来复制数据。
spring容器在创建bean的过程中,会判断bean是否为ApplicationListener类型,进而会将其作为监听器注册到AbstractApplicationContext#applicationEventMulticaster中,这块的源码在下面这个方法中:org.springframework.context.support.ApplicationListenerDetector#postProcessAfterInitialization

处理@EventListener注解源码位于:org.springframework.context.event.EventListenerMethodProcessor#afterSingletonsInstantiated

//getServletContext()方法返回javax.servlet.ServletContext的属性ApplicationContext(此处作为event的source),属性ApplicationContext中的standardContext(含servletmappings、mimemappings、innitiallize如:JasperInitializer、SpringServletContainerInitializer等)
org.apache.catalina.core.StandardContext#listenerStart{
...ServletContextEvent event = new ServletContextEvent(getServletContext())...
...listener.contextInitialized(event)...
}->(传入了ApplicationContext参数由tomcat进入spring-web)org.springframework.web.context.ContextLoaderListener#contextInitialized(ServletContextEvent event)->org.springframework.web.context.ContextLoader#initWebApplicationContext{
//org.springframework.web.context.ContextLoader#createWebApplicationContext
//由ContextLoader获取默认的WebApplicationContext。ContextLoader.properties文件定义了WebApplicationContext默认为XmlWebApplicationContext
...this.context = createWebApplicationContext(servletContext)...
...cwac = (ConfigurableWebApplicationContext) this.context...
//servletContext即方法参数传进来的tomcat中的ApplicationContext
...configureAndRefreshWebApplicationContext(cwac, servletContext){
//spring上下文中设置servletContext
...wac.setServletContext(sc);
//spring配置文件从tomcat的ApplicationContext中获取属性contextConfigLocation的值classpath:applicationContext.xml
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocationParam != null) {
wac.setConfigLocation(configLocationParam);
}...
//运行initializerClasses如web.xml中定义的globalInitializerClasses、contextInitializerClasses等
...customizeContext(sc, wac);
//刷新spring
wac.refresh(){
...finishRefresh{
//此时不会触发org.springframework.web.servlet.FrameworkServlet.ContextRefreshListener的监听,因为此时ContextRefreshListener还没有被bean管理也就不会被ApplicationListenerDetector识别,它是在org.springframework.web.servlet.FrameworkServlet#configureAndRefreshWebApplicationContext方法中wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()))加进去的,同理不会触发org.springframework.web.servlet.resource.ResourceUrlProvider implements ApplicationListener 它也是在递归循环识别@import注解时(org.springframework.context.annotation.ConfigurationClassParser#processImports->getImports->collectImports->collectImports)通过识别类或注解的元注解是否含有import并注入对应的bean如识别@EnableWebMvc导入DelegatingWebMvcConfiguration(extends WebMvcConfigurationSupport)类注册.WebMvcConfigurationSupport通过@Bean注入了类ResourceUrlProvider(映射静态资源放入handlerMap对应于simpleUrlhandlermapping适配)。所以这两者都是容器加载时候添加进去的
...publishEvent(new ContextRefreshedEvent(this))...
}...
}...
}...
...servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context)...//servletContext设置属性ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE值为创建的spring context(XmlWebApplicationContext)。后续dispatchservlet初始化mvc环境设置它为父parent
}...
}->
//FrameworkServlet extends HttpServletBean(左边是spring-webmvc包中的类,右边是servlet包) extends HttpServlet extends GenericServlet extends Servlet
org.apache.catalina.core.StandardWrapper#load->loadServlet->initServlet->servlet.init->javax.servlet.GenericServlet#init->org.springframework.web.servlet.HttpServletBean#initServletBean->org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext{
//基于继承关系,此处的getServletContext方法继承父类获取servlet的ApplicationContextFacade。getWebApplicationContext方法获取到的为监听器初始化spring后的XmlWebApplicationContext
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext(){
//获取的监听器初始化spring环境时将spring的XmlWebApplicationContext放到ServletContext中的ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE属性值
return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE){
...sc.getAttribute(attrName)...
}
})...
...createWebApplicationContext(rootContext){
//parent即入参rootContext
return createWebApplicationContext((ApplicationContext) parent){
//实例化新的XmlWebApplicationContext contextClass 为org.springframework.web.servlet.FrameworkServlet#DEFAULT_CONTEXT_CLASS= XmlWebApplicationContext.class
...wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass)...
//容器上下文XmlWebApplicationContext设置Spring的XmlWebApplicationContext为parent(对应后面getbean找不到去父里面找)
...wac.setParent(parent)
//获取
String configLocation = getContextConfigLocation();
if (configLocation != null) {
wac.setConfigLocation(configLocation);
}
//调用refresh()->finishRefresh->发布ContextRefreshedEvent事件触发org.springframework.web.servlet.FrameworkServlet#onApplicationEvent->onRefresh->initStrategies(initHandlerMappings、initHandlerAdapters、initViewResolvers等)
configureAndRefreshWebApplicationContext(wac)...
}
}...
}->

ContextNamespaceHandler(spring.handlers)->component-scan(ComponentScanBeanDefinitionParser仅会解析一层componet,其中的parse->registerComponents->registerAnnotationConfigProcessors会注入ConfigurationClassPostProcessor类解析import等)

org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanFactory->processConfigBeanDefinitions->(筛选spring中的配置bean(Configuration、Component、ComponentScan、Import、ImportResource))parser.parse(candidates)->parse->processConfigurationClass->doProcessConfigurationClass{
//循环处理类,如果类中有子类是配置类-》processConfigurationClass
...processMemberClasses...
//陆续处理@PropertySource、@ComponentScan、@Import、@ImportResource、@Bean、default methods on interfaces等
...this.componentScanParser.parse->scanner.doScan{
...findCandidateComponents...
...beanDefinitions.add(definitionHolder)... @1
...registerBeanDefinition...
}...
//检查@1的beanDefinitions是否配置类(Configuration、Component、ComponentScan、Import、ImportResource),如果是就递归解析
...if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) { //org.springframework.context.annotation.ConfigurationClassParser#parse(java.lang.String, java.lang.String)->processConfigurationClass递归扫描解析
parse(bdCand.getBeanClassName(), holder.getBeanName());
}...
}->
//参数没有对应解析器异常捕获
org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver#readWithMessageConverters(org.springframework.http.HttpInputMessage, org.springframework.core.MethodParameter, java.lang.reflect.Type)->throw new HttpMessageNotReadableException 被 org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver#doResolveException捕获统一处理
参数解析org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor#resolveArgument{
//binder含属性BeanPropertyBindingResult存放检验错误信息
...WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name)...
...validateIfApplicable(binder, parameter){
//检验,校验不通过抛出异常被上层捕获。接着org.springframework.web.servlet.DispatcherServlet#processDispatchResult判断入参dispatchException,如果不为空则processHandlerException->processHandlerException->resolveException->resolveException->org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver#doResolveException抛出(org.springframework.web.servlet.DispatcherServlet#initHandlerExceptionResolvers初始化加载的org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#handlerExceptionResolver)
...binder.validate...
}...
}
Map中entryset解析:EntrySet没有自己的属性,创建的对象没有存什么变量 是空的,但是一创建就获得了iterator() 等方法,EntrySet 的iterator() 方法最终会创建HashIterator对象,而HashIterator里面访问的就是HashMap的table!!所以虽然EntrySet自己没有存table,但因为内部类可以访问外部类的成员,HashMap的table就相当于EntrySet的table。
java.util.HashMap#entrySet{
...return (es = entrySet) == null ? (entrySet = new EntrySet(){
...public final Iterator<Map.Entry<K,V>> iterator() {
return new EntryIterator(){
...public final Map.Entry<K,V> next() { return nextNode(){
//Node<K,V>[] t = table 其中Node<K,V>[] table为java.util.HashMap#table中定义的哈希表,Node[]数组也揭示了map的数据结构
...next = t[index++]...
}...
}
}...
}...
}

@HandlerException\type @Builder

Base64:

当不可见字节流在网络上传输时,比如说从 A 计算机传到 B 计算机,往往要经过多个路由设备,由于不同的设备(特指老的路由设备)对字节流的处理方式有一些不同,这样那些不可见字节就有可能被处理错误,这是不利于传输的。所以就先把数据先做一个 base64 编码,统统变成可见字节,也就是 ascii 码可表示的可见字符,确保数据可靠传输。base64 的内容是有 0 ~9,a ~z,A ~Z,+,/组成(另加一个“=”,实际是65个字符),正好 64 个字符,这些字符是在 ascii 可表示的范围内,属于 95 个可见字符的一部分。
对于现在的路由设备,只要是文本字符(无论是否是可见字符)都可以转为字节流(字节类型数据)在网络上传输。而字节类型的数据(图片,音频,视频,语音等二进制数据), 本身就是字节流,所以可以直接在网络上传输。但是,在企业开发中,我们为了一致性约定,往往对要发送的数据先进行序列化,然后再编码(encoding)为字节流在网络上发送,而对方收到数据后,要先把字节流解码(decoding)为字符串,然后再反序列化还原为原始数据。
我们可以把字节类型数据转为字符串类型数据,然后再使用 json 序列化,然而不幸的是,对于字节类型的图片,音频,视频,语音等二进制数据里面含有很多不可见字节,而这些不可见字节无法转为字符串。但是,我们可以先用 base64 把这些不可见字节数据编码为可见字节数据,然后再转为字符串类型,然后在通过 json 进行序列化,然后再编码为字节流通过网络发送出去。而对方收到数据后,要先把字节流解码为字符串,然后再反序列化,然后再转为字节类型数据,然后再使用 base64 解码还原成原始数据

Base64一般用于在HTTP协议下传输二进制数据,由于HTTP协议是文本协议,所以在HTTP写一下传输二进制数据需要将二进制数据转化为字符数据,
网络传输只能传输可打印字符,
在ASCII码中规定,0-31、128这33个字符属于控制字符,
32~127这95个字符属于可打印字符
那么其它字符怎么传输呢,Base64就是其中一种方式

附加:传参base64时的+号变空格问题 https://blog.csdn.net/Maisu/article/details/129817846
由x-www-form-urlencoded引发的接口对接失败 https://mp.weixin.qq.com/s/lTznRb5zvnZspml2EgOTaw

form-data与x-www-form-urlencoded的区别:
multipart/form-data:既可以上传键值对,也可以上传文件。当上传的字段是文件时,会有Content-Type来表名文件类型;content-disposition,用来说明字段的一些信息;
由于有boundary隔离,所以multipart/form-data既可以上传文件,也可以上传键值对,它采用了键值对的方式,所以可以上传多个文件
POST HTTP/1.1
Host: test.app.com
Cache-Control: no-cache
Postman-Token: 59227787-c438-361d-fbe1-75feeb78047e
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW

------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="filekey"; filename=""
Content-Type:

------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="textkey"

tttttt
------WebKitFormBoundary7MA4YWxkTrZu0gW--

x-www-form-urlencoded:只能上传键值对,而且键值对都是通过&间隔分开的
Content-Type: application/x-www-form-urlencoded;charset=utf-8
title=test&sub%5B%5D=1&sub%5B%5D=2&sub%5B%5D=3

3.raw对应的是入参是任意格式的可以上传任意格式的【文本】,可以上传text、json、xml、html等
(4)binary相当于Content-Type:application/octet-stream,只可以上传二进制数据,通常用来上传文件,但是一次只能上传一个文件

Linux系统 tcpdump 抓包命令使用教程: https://zhuanlan.zhihu.com/p/74812069 https://www.cnblogs.com/wongbingming/p/13212306.html

Wireshark 使用教程: https://zhuanlan.zhihu.com/p/631821119

arp: https://blog.csdn.net/syy0201/article/details/84754602

posted @ 2025-05-14 23:26  gsluofu  阅读(13)  评论(0)    收藏  举报