面试总结之Spring篇

一、AOP

1、什么是AOP

1.1、概述

  • AOP(Aspect-Oriented Programming):面向切面编程,即把一些业务逻辑中的相同代码抽取出来,让业务逻辑更加简练清爽
    在这里插入图片描述
  • 如果要CRUD写一堆业务,可如何实现业务代码前后进行打印日志和参数的校验?
    可以把日志记录和数据校验可重用的功能模块分离出来,然后在程序合适的位置动态地植入这些代码并执行,如此,让业务逻辑只包含核心的业务代码,而没有通用逻辑的代码,使业务模块更简洁,实现了业务逻辑和通用逻辑的代码分离,便于维护和升级,降低了业务逻辑和通用逻辑的耦合性
    在这里插入图片描述
  • AOP可以将遍布应用的功能分离出来形成可重用的组件,在编译期间、装载期间或运行期间实现给原程序动态添加功能(在不修改源代码的情况下),从而实现对业务逻辑的隔离,提高代码的模块化能力
    在这里插入图片描述
  • AOP的核心是动态代理,如果实现了接口,就使用JDK的动态代理,不然就使用CGLIB代理,主要应用于处理具有横切性质的系统级功能,如日志收集、事务管理、安全检查、缓存、对象池管理等

1.2、AOP的核心概念

  • 目标对象(Target):代理的目标对象
    在这里插入图片描述
  • 切面(Aspect):类是对物体特征的抽象,切面就是对横切关注点的抽象,在Spring中,通过@Aspect注解声明当前类为切面,一般要在切面定义切入点和通知
  • 连接点(JoinPoint):被拦截的点,由于Spring只支持方法类型的连接点,所以在Spring中,连接点指的是被拦截到的方法,实际上连接点还可以是字段或者构造器
  • 切点(PointCut):带有通知的连接点,在程序中主要体现为书写切入点表达式
    在这里插入图片描述
// 以自定义注解 @CustomLog为切点
@Pointcut("@annotation(com.example.interviewStudy.annotation.CustomLog)")
public void logPcut() {
}
  • 通知(Advice):拦截到连接点之后要执行的代码,也称作增强
  • 织入(Weave):将切面/ 切面类和目标类动态接入
    编译器织入:切面在目标类编译时织入
    类加载期织入:切面在目标类加载到JVM时织入,需要特殊的类加载器,可以在目标类被引入应用之前增强该目标类的字节码,AspectJ采用编译期织入和类加载器织入
    运行期织入:切面在应用运行的某时刻被织入,一般情况下,在织入切面时,AOP容器会为目标对象动态地创建一个代理对象,这也是SpringAOP织入切面的方式
  • 增强器(advisor):筛选类中的哪些方法是连接点(哪些方法需要被拦截)
  • 引介(introduction):⼀种特殊的增强,可以动态地为类添加⼀些属性和方法

1.3、AOP的环绕方式

AOP有五种通知的方式:

  • 前置通知 (@Before):在切入点方法执行之前执行
  • 环绕通知 (@Around):手动调用切入点方法并对其进行增强的通知方式
  • 后置通知 (@After):在切入点方法执行之后执行,无论切入点方法内部是否出现异常,后置通知都会执行
  • 异常通知 (@AfterThrowing):在切入点方法执行之后执行,只有当切入点方法内部出现异常之后才执行
  • 返回通知 (@AfterReturning):在切入点方法执行之后执行,如果切入点方法内部出现异常将不会执行

当有多个切面的情况下,可以通过 @Order指定先后顺序,数字越小,优先级越高

2、AOP在项目中的运用

2.1、日志输出

  • 在SpringBoot项目中,使用AOP 打印接口的入参和出参日志,以及执行时间
    1)引入依赖
  <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.8</version><relativePath/> <!-- lookup parent from repository --></parent>
  <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency>
  • 2)自定义注解 作为切入点
import java.lang.annotation.*;@Target({ElementType.METHOD})   // 指定注解使用在方法上
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CustomLog {String info();
}
  • 3)配置AOP切面
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;@Aspect   // 标识当前类为切面
@Component
public class CustomLogAspect {// getLogger(Class<?> clazz)public static final Logger logger = LoggerFactory.getLogger(CustomLogAspect.class);// 以自定义注解 @CustomLog为切点@Pointcut("@annotation(com.example.interviewStudy.annotation.CustomLog)")public void logPcut() {}// 前置通知: 在切点之前织入@Before("logPcut()")public void doBefore(JoinPoint joinPoint) throws JsonProcessingException {ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = attributes.getRequest();logger.info("========== 开始打印请求参数 ===========");logger.info("URL: {}", request.getRequestURL().toString());logger.info("HTTP Method: {}", request.getMethod());logger.info("Controller的全路径 和 执行方法: {} , {}方法", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());logger.info("请求入参:{}", new ObjectMapper().writeValueAsString(joinPoint.getArgs()));}// 后置通知,在切入点之后织入@After("logPcut()")public void doAfter() {logger.info("======== 请求日志输出完毕 ========");}/*** 环绕通知: ProceedingJoinPoint对象调用proceed方法,实现 原本目标方法的调用*   ProceedingJoinPoint 只支持环绕通知,如果其他通知也采用ProceedingJoinPoint作为连接点,就会出现异常*   ==> Caused by: java.lang.IllegalArgumentException: ProceedingJoinPoint is only supported for around advice* */@Around("logPcut()")public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {long start = System.currentTimeMillis();Object result = joinPoint.proceed();long end = System.currentTimeMillis();logger.info("请求结果: {}", new ObjectMapper().writeValueAsString(result));logger.info("请求处理耗时: {} ms", (end - start));return result;}
}
  • 4)在接口上添加自定义注解
@RestController
@RequestMapping("/aop")
public class CustomAspectController {@GetMapping("/hello")@CustomLog(info = "hello,使用AOP实现请求日志输出")public String hello(String uname) {return "Hello,welcome to studing AOP, your name is " + uname;}
}

执行结果:
在这里插入图片描述

3、JDK和CGLIB的动态代理

  • 动态代理主要有JDK动态代理和CGLIB的动态代理

1)JDK动态代理

  • Interface:对于JDK动态代理,目标类需要实现一个Interface
  • InvocationHandler:通过实现InvocationHandler接口,定义横切逻辑,再通过反射机制(invoke)调用目标类的方法,在此过程,可能包装逻辑,对目标方法进行前置/ 后置处理
  • Proxy:利用InvocationHandler动态创建一个符合目标类实现接口的实例,生成目标类的代理对象

我们来看⼀个常见的⼩场景,客服中转,解决⽤户问题:
在这里插入图片描述
代码实现:
在这里插入图片描述

  • 接口
public interface ISolver {public String solve();
}
  • 目标类:需要实现对应接口
public class ProblemSolver implements ISolver{@Overridepublic String solve() {System.out.println("ProblemSolver,solve方法 ==> 问题正在解决中...");return "OKK";}
}
  • 动态代理工厂:ProxyFactory,直接用反射生成一个目标对象的代理对象,如下是用匿名内部类的方式重写了InvocationHandler的方法
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;public class ProxyFactory {// 维护一个目标对象private Object target;public ProxyFactory(Object target){this.target = target;}// 为目标对象生成代理对象public Object getProxyInstance(){return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("请描述您的问题:");// 通过反射机制 调用目标对象方法Object result = method.invoke(target, args);System.out.println("问题已经得到解决!!");// 返回 目标对象方法的返回值return result;}});}
}
  • 客户端:Client,生成一个代理对象实例,通过代理对象 调用目标对象的方法
public class Client {public static void main(String[] args) {ISolver developer = new ProblemSolver();// 创建代理对象实例ISolver instance = (ISolver) new ProxyFactory(developer).getProxyInstance();// 代理对象调用目标对象方法,得到目标方法的返回值并输出String res = instance.solve();System.out.println(res);}
}

执行结果:
在这里插入图片描述

2)CGLIB动态代理

  • 目标类(不需要像JDK动态代理一样实现接口):
public class CglibSolver {public String solve(){System.out.println("Testing implement proxy by cglib");return "CglibSolver ==> solve方法";}
}
  • 动态代理工厂:
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;import java.lang.reflect.Method;public class ProxyFactory implements MethodInterceptor, Callback {private Object target;public ProxyFactory(Object target){this.target = target;}public Object getProxyInstance(){Enhancer enhancer = new Enhancer();// 设置父类enhancer.setSuperclass(target.getClass());// 设置回调函数,用于监听当前事件enhancer.setCallback(this);// 创建子类对象代理return enhancer.create();}@Overridepublic Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {System.out.println("请问有什么可以帮到您?");// 调用目标对象的方法Object result = method.invoke(target, args);System.out.println("问题得到解决啦哈!");return result;}
}
  • 客户端:Client
public class CgClient {public static void main(String[] args) {CglibSolver solver = new CglibSolver();// 创建代理对象CglibSolver proxy = (CglibSolver) new ProxyFactory(solver).getProxyInstance();// 通过代理对象实例调用目标对象方法String result = proxy.solve();System.out.println("result : " + result);}
}

执行结果(代理对象替目标对象执行调用方法):
在这里插入图片描述

4、Spring AOP和AspectJ AOP的区别

1) Spring AOP

Spring AOP属于运行时增强,主要具有如下特点:

  • 基于动态代理来实现,默认如果使用接口的方式来实现,则使用JDK提供的动态代理;如果是方法,则使用CGLIB来实现
  • Spring AOP需要依赖IOC容器来管理,并且只能作用于Spring容器,使用纯Java代码实现
  • 在性能上,由于Spring AOP是基于动态代理来实现的,在容器启动时需要生成代理实例,在方法调用上也会增加栈的深度,使得Spring AOP的性能不如Aspect好

2)AspectJ

  • AspectJ是功能强大的AOP框架,属于编译时增强,可以单独使用,也可以整合到其他框架中,是AOP编程的完全解决方案

  • AspectJ属于静态织入,通过修改代码来实现,在实际运行之前就完成了织入,生成的类没有额外运行时开销,可织入时机如下:
    A、编译期织入(Compile-time weaving):如 A类使用AspectJ添加了某属性,B类引用了A类,该场景就需要编译期进行织入,否则没法编译B类
    B、编译后织入(Post-compile weaving):在已生成了字节码/ class文件,或已经打包成jar包后,该情况需要增强,就需要使用到编译后织入
    C、类加载后织入(Load-time weaving):在加载类时进行织入

  • 两者整体对比如下:
    在这里插入图片描述

二、事务

1、Spring事务的种类

Spring支持编程式事务声明式事务管理两种方式:
1)编程式事务管理:使用TransactionTemplate,需要显示地执行事务
2)声明式事务管理:建立在AOP之上,其本质是通过AOP功能,对方法前后进行拦截,将事务处理的功能编织到拦截的方法中,即在目标方法开始之前启动事务,在执行完目标方法之后根据执行情况 进行提交或回滚事务

  • 优点:不需要在业务逻辑代码中掺杂事务管理的代码,只需要在配置文件中进行相关的事务规则声明或通过 @Transactional注解声明事务(以及在启动类上添加@EnableTransactionManagement注解开启事务管理),将事务规则应用到业务逻辑中,减少业务代码的侵入
  • 缺点:最细粒度只能作用到方法级别,无法做到像编程式事务那样作用到代码块级别

2、声明式事务的失效情况

  • 1)@Transactional 应用在非public修饰的方法上
    在Spring AOP代理时,TransactionInterceptor(事务拦截器)在目标方法执行前后进行拦截,DynamicAdvisedInterceptor(CglibAopProxy 的内部类)的intercept方法或 JdkDynamicAopProxy 的 invoke 方法会间接调用 AbstractFallbackTransactionAttributeSource
    的 computeTransactionAttribute方法,获取 Transactional 注解的事务配置信息,
    computeTransactionAttribute会检查目标方法的修饰符是否为public,不是public则不会获取@Transactional的属性配置信息
    在这里插入图片描述
  • 2)@Transactional 注解属性 propagation设置错误
    TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行
    TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则将当前事务挂起
    TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常
    在这里插入图片描述
  • 3)@Transactional 注解属性 rollbackFor 设置错误
    rollbackFor可以指定能够触发事务回滚的异常类型,Spring默认抛出了未检查Unchecked异常(继承自RuntimeException的异常)或者Error才回滚事务,其他异常不会触发回滚事务
    在这里插入图片描述
// 希望自定义的异常可以进行回滚
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = MyException.class)

如果在目标方法中抛出的异常为rollbackFor指定的异常子类,事务同样回滚

  • 4)事务方法被同类中的其他方法调用
    开发中避免不了会对同一个类中的方法调用,比如
    当事务方法被当前类以外的代码调用时,才会由Spring生成的代理对象来管理(使用Spring AOP动态代理造成)
    ==》 举例来说:
    Test类中的方法A调用本类的方法B(无论方法B是用public还是private修饰),B方法有声明注解事务,但A方法没有声明注解事务,当外部调用方法A后,方法B的事务不会起作用
    如果B方法内部抛了异常,而A方法此时通过try-catch捕获了B方法的异常,则该事务就不能正常回滚,会抛出异常:
org.springframework.transaction.UnexpectedRollbackException:Transaction rolled back because i t has been marked a s rollback - only

3、声明式事务的实现原理

4、Spring事务的隔离级别

  • Spring的接口TransactionDefinition定义了表示隔离级别的常量,主要是对应后端数据库的事务隔离级别:
  • ISOLATION_DEFAULT:使用后端数据库默认的隔离级别,MySQL默认可重复读,Oracle默认读已提交
  • ISOLATION_READ_UNCOMMITTED:读未提交
  • ISOLATION_READ_COMMITTED:读已提交
  • ISOLATION_REPEATABLE_READ:可重复读
  • ISOLATION_SERIALIZABLE:串行化

5、Spring事务的传播机制

未完待续…

三、MVC

1、SpringMVC的工作流程

在这里插入图片描述

  • 1)客户端向服务端发送一次请求,该请求会先到前端控制器/ 中央控制器 DispatcherServlet
  • 2)DispatcherServlet 接收到请求后会调用HandlerMapping处理器映射器,由此得知,该请求由哪个Controller来处理(此时并不调用Controller)
  • 3)DispatcherServlet调用HandlerAdapter处理器适配器,告诉处理器适配器应该要去执行哪个Controller
  • 4)HandlerAdapter处理器适配器去执行Controller并得到ModelAndView(数据和视图),并层层返回给DispatcherServlet
  • 5)DispatcherServlet 将 ModelAndView 交给 ViewResolver视图解析器解析,然后返回真正的视图
  • 6)DispatcherServlet将模型数据填充到视图中
  • 7)DispatcherServlet将结果响应给客户端

SpringMVC虽然整体流程复杂,但大部分的组件不需要开发人员创建和管理,只需要通过配置文件的方式完成配置即可,真正需要开发人员处理的只有Handler(Controller)、View、Model

2、SpringMVC的核心组件

  • 1)DispatcherServlet:前置控制器,是整个流程控制的核心,控制其他组件的执行,进行统一调度,降低组件之间的耦合性,相当于总指挥
  • 2)Handler:处理器,完成具体的业务逻辑,相当于Servlet或Action
  • 3)HandlerMapping:DispatcherServlet接收到请求后,通过HandlerMapping将不同的请求映射到不同的Handler
  • 4)HandlerInterceptor:处理拦截器的接口,如果需要完成一些拦截处理,可以实现该接口
  • 5)HandlerExecutionChain:处理器执行链,包括两部分内容:Handler 和 HandlerInterceptor (系统有默认的HandlerInterceptor,如果需要额外设置拦截,可以添加拦截器)
  • 6)HandlerAdapter:处理器适配器,Handler执行业务方法之前,需要进行一系列的操作,包括表单数据的验证、数据类型的转换、将表单数据封装到JavaBean等,这些操作都是由HandlerAdapter来完成,开发者只需要关注于业务逻辑的处理即可,DispatcherServlet通过HandlerAdapter执行不同的Handler
  • 7)ModelAndView:装载了模型数据和视图信息,作为Handler的处理结果,返回给DispatcherServlet
  • 8)ViewResolver:视图解析器,DispatcherServlet通过视图解析器将逻辑视图 解析为物理视图,最终将渲染结果响应给客户端

3、SpringMVC Restful风格的接口流程

  • Restful接口,响应格式是json,需要使用@ResponseBody注解:
@GetMapping("/user")
@ResponseBody
public User user() {return new User(1,"张三");
}

在这里插入图片描述

  • 加入@ResponseBody注解后,整体流程和ModelAndView大致相同,只是在细节上有所不同:
    1)客户端向服务端发送请求,该请求会先到达前端控制器DispatcherServlet
    2)DispatcherServlet接收到请求后会调用HandlerMapping处理器映射器,由此得知,该请求应由哪个Controller来处理
    3)DispatcherServlet 调用HandlerAdapter处理器适配器,告知处理器适配器应该要去执行哪个Controller
    4)Controller被封装成了 ServletInvocableHandlerMethod,HandlerAdapter处理器适配器去执行invokeAndHandle方法,完成对Controller的请求处理
    5)HandlerAdapter执行完对Controller的请求,会调用HandlerMethodReturnValueHandler去处理返回值,主要的过程如下:

  • A、调用RequestResponseBodyMethodProcessor,创建ServletServerHttp实例(该实例是Spring对原生ServerHttpResponse的封装)

  • B、使用HttpMessageConverter的write方法,将返回值写入ServletServerHttpResponse的OutputStream 输出流中

  • C、在写入的过程中,使用JsonGenerator(默认使用Jackson)对返回值进行Json序列化
    6)执行完请求后,返回的ModelAndView为null,ServletServerHttpResponse中也写入响应,所以不用关心View的处理

四、SpringBoot

1、介绍下SpringBoot

  • Spring Boot本身不提供Spring框架的核心特性和扩展功能,而是用于快速、敏捷地开发新一代基于Spring框架的应用程序,是和Spring紧密结合,用于提升Spring开发者体验的工具
  • Spring Boot以约定大于配置的思想来实现,相比Spring的优势如下:
    1)Spring Boot可以快速地创建独立的Spring应用程序
    2)Spring Boot内嵌了如Tomcat、Jetty 和 Undertow等容器,可以直接运行,不需要再部署
    3)Spring Boot无需再像Spring使用一堆繁琐的xml文件配置,而改为使用Java配置,将bean注入改为使用注解注入的方式(如@Autowired),并将多个xml、properties配置浓缩在一个application.yml配置文件中
    4)Spring Boot可以快速整合常用依赖(开发库,如spring-webmvc、jackson-json)、validation-api 和Tomcat等,提供的pom可以简化Maven配置,当开发者引入核心依赖时,SpringBoot会自动引入其他依赖

2、SpringBoot自动配置原理

  • 启动类上添加的@SpringBootApplication注解是一个复合注解,包含@EnableAutoConfiguration
    在这里插入图片描述
  • 由上图可见,自动装配核心功能 实际上是通过AutoConfigurationImportSelector类实现的
package org.springframework.boot.autoconfigure;import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.io.support.SpringFactoriesLoader;/*** @see ConditionalOnBean* @see ConditionalOnMissingBean* @see ConditionalOnClass* @see AutoConfigureAfter* @see SpringBootApplication*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)   // 加载自动装配类
public @interface EnableAutoConfiguration {/*** 当自动配置生效时,可用于重写环境属性*/String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";/*** 排除那些没有被使用的、具体的自动配置类* @return the classes to exclude*/Class<?>[] exclude() default {};/*** 排除那些没有被使用的、具体的自动配置类的类名* @return the class names to exclude*/String[] excludeName() default {};}
  • AutoConfigurationImportSelector实现了ImportSelector接口,用来收集需要导入的配置类,配合@Import注解就可以将相应的类导入到Spring容器中
  • 获取注入类的方法是selectImports方法,实际调用的是getAutoConfigurationEntry,该方法是获取自动装配类的关键,主要流程可以分为如下几步:
    1)获取注解的属性,用于后面的排除
    2)获取所有需要自动装配的配置类的路径(从 META-INF/spring.factories 获取自动配置类的路径
    3)去掉重复的配置类和需要排除的重复类,把需要自动加载的配置类的路径存储起来
    @Overridepublic String[] selectImports(AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return NO_IMPORTS;}AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());}
	/*** @return the auto-configurations that should be imported*         返回需要导入的自动装配*/protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return EMPTY_ENTRY;}// 1.获取到注解的属性AnnotationAttributes attributes = getAttributes(annotationMetadata);// 2.获取需要自动装配的所有配置类  ==》读取META-INF/spring.factories,读取自动配置类的路径List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);// 3.1、移除重复的配置configurations = removeDuplicates(configurations);// 3.2、处理需要排除的配置Set<String> exclusions = getExclusions(annotationMetadata, attributes);checkExcludedClasses(configurations, exclusions);configurations.removeAll(exclusions);configurations = getConfigurationClassFilter().filter(configurations);fireAutoConfigurationImportEvents(configurations, exclusions);return new AutoConfigurationEntry(configurations, exclusions);}

3、如何自定义SpringBoot Starter

4、SpringBoot启动原理

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/93556.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机竞赛 深度学习驾驶行为状态检测系统(疲劳 抽烟 喝水 玩手机) - opencv python

文章目录 1 前言1 课题背景2 相关技术2.1 Dlib人脸识别库2.2 疲劳检测算法2.3 YOLOV5算法 3 效果展示3.1 眨眼3.2 打哈欠3.3 使用手机检测3.4 抽烟检测3.5 喝水检测 4 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于深度学习的驾…

WebSocket的那些事(6- RabbitMQ STOMP目的地详解)

目录 一、目的地类型二、Exchange类型目的地三、Queue类型目的地四、AMQ Queue类型目的地五、Topic类型目的地 一、目的地类型 在上节 WebSocket的那些事&#xff08;5-Spring STOMP支持之连接外部消息代理&#xff09;中我们已经简单介绍了各种目的地类型&#xff0c;如下图&…

【强化算法专题一】双指针算法

【强化算法专题一】双指针算法 1.双指针算法--移动零2.双指针算法--复写零3.双指针算法--快乐数4.双指针算法--盛水最多的容器5.双指针算法--有效三角形的个数6.双指针算法--和为s的两个数7.双指针算法--三数之和8.双指针算法--四数之和 1.双指针算法–移动零 算法原理解析----…

【JavaScript】读取本地json文件并绘制表格

本文为避免跨域问题&#xff0c;使用了改造过的本地json文件的方法实现读取json数据并绘制表格。 如果发起http请求获取本地 json文件中数据&#xff0c;需要架设本地服务器&#xff0c;本文不做阐述。 概述 1、json在本地&#xff0c;并不需要从服务器下载。 2、采用jquery…

国庆作业day5

应用层&#xff1a;提供用户与网络应用程序之间的接口。表示层&#xff1a;负责数据的格式转换、加密和解密。会话层&#xff1a;负责建立、管理和终止会话。它提供会话控制和同步&#xff0c;允许应用程序之间建立连接和交换数据。传输层&#xff1a;提供端到端的连接。网络层…

postgresql-管理数据表

postgresql-管理数据表 创建表数据类型字段约束表级约束模式搜索路径 修改表添加字段删除字段添加约束删除约束修改字段默认值修改字段数据类型重命名字段重命名表 删除表 创建表 在 PostgreSQL 中&#xff0c;使用 CREATE TABLE 语句创建一个新表&#xff1a; CREATE TABLE …

专业PDF编辑阅读工具PDF Expert mac中文特点介绍

PDF Expert mac是一款专业的PDF编辑和阅读工具。它可以帮助用户在Mac、iPad和iPhone等设备上查看、注释、编辑、填写和签署PDF文档。 PDF Expert mac软件特点 PDF编辑&#xff1a;PDF Expert提供了丰富的PDF编辑功能&#xff0c;包括添加、删除、移动、旋转、缩放、裁剪等操作…

树莓派4B与STM32串口通信

目录 2上篇文章的补充 2.1 树莓派通信设置 3树莓派与STM32通信 3.1接线准备 3.2代码 3.2.1 STM32代码&#xff1a; 3.2.2树莓派代码&#xff1a; 2上篇文章的补充 2.1 树莓派通信设置 在上篇文章的基础上&#xff0c;进一步的设置 终端输入&#xff1a;sudo minicom …

从 0 到 1 ,手把手教你编写《消息队列》项目(Java实现) —— 核心类持久化存储

文章目录 一、持久化存储的方式与路径二、公共模块序列化 / 反序列化异常规定 三、持久化存储数据库数据管理文件数据管理读写规定新增 /删除规定内存中 Message 的规定存储规定代码编写 硬盘数据管理 一、持久化存储的方式与路径 交换机,队列,绑定关系,这些我们使用数据库来管…

四、浏览器渲染过程,DOM,CSSDOM,渲染,布局,绘制详细介绍

知识点&#xff1a; 1、为什么不能先执行 js文件&#xff1f;&#xff1f; 我们不能先执行JS文件&#xff0c;必须等到CSSOM构建完成了才能执行JS文件&#xff0c;因为前面已经说过渲染树是需要DOM和CSSOM构建完成了以后才能构建&#xff0c;而且JS是可以操控CSS样式的&#…

springboot的配置文件(properties和yml/yaml)

springboot的配置文件有两种格式分别是properties和yml/yaml 创建配置文件 在创建springboot项目时候&#xff0c;会默认生成application.properties这种格式 书写风格 端口 application.propertis server.port8080 application.yml server:port: 8080 连接数据库 applica…

<Xcode> Xcode IOS无开发者账号打包和分发

关于flutter我们前边聊到的初入门、数据解析、适配、安卓打包、ios端的开发和黑苹果环境部署&#xff0c;但是对于苹果的打包和分发&#xff0c;我只是给大家了一个链接&#xff0c;作为一个顶级好男人&#xff0c;我认为这样是对大家的不负责任&#xff0c;那么这篇就主要是针…

【计算机网络黑皮书】应用层

【事先声明】 这是对于中科大的计算机网络的网课的学习笔记&#xff0c;感谢郑烇老师的无偿分享 书籍是《计算机网络&#xff08;自顶向下方法 第6版&#xff09;》 需要的可以私信我&#xff0c;无偿分享&#xff0c;课程简介下也有 课程连接 目录 应用层网络应用的原理应用架…

作业 day4

完成父子进程通信

Socket通信

优质博文IT-BLOG-CN 一、简介 Socket套接字&#xff1a;描述了计算机的IP地址和端口&#xff0c;运行在计算机中的程序之间采用socket进行数据通信。通信的两端都有socket&#xff0c;它是一个通道&#xff0c;数据在两个socket之间进行传输。socket把复杂的TCP/IP协议族隐藏在…

Linux基本指令(二)

&#x1f493;博主个人主页:不是笨小孩&#x1f440; ⏩专栏分类:数据结构与算法&#x1f440; C&#x1f440; 刷题专栏&#x1f440; C语言&#x1f440; &#x1f69a;代码仓库:笨小孩的代码库&#x1f440; ⏩社区&#xff1a;不是笨小孩&#x1f440; &#x1f339;欢迎大…

[spring] spring core - 配置注入及其他内容补充

[spring] spring core - 配置注入及其他内容补充 上篇 [sping] spring core - 依赖注入 这里主要补一些 core 相关内容补充&#xff0c;同时添加了 java config bean 的方法 java config bean 是除了 XML、java 注解之外另一给实现 DI 的方法 java config bean 这个方法不…

Tomcat报404问题的原因分析

1.未配置环境变量 按照需求重新配置即可。 2.IIs访问权限问题 注意:这个问题有的博主也写了,但是这个问题可有可无,意思是正常情况下,有没有都是可以访问滴放心 3.端口占用问题 端口占用可能会出现这个问题,因为tomcat的默认端口号是8080,如果在是运行tomcat时计算机的…

司空见惯 - 奈尔宝的NTTP

联合国对21世纪人才定义的标准&#xff0c;包括六种核心技能&#xff0c;即批判性思维&#xff08;critical thinking)、人际交往&#xff08;communication)、与人合作&#xff08;collaboration)、创造性&#xff08;creativity)、信息素养&#xff08;information literacy)…

【匠心打造】从0打造uniapp 可视化拖拽设计 c_o 第十篇

一、click one for uniapp置顶&#xff1a; 全部免费开源 (你商业用途也没关系&#xff0c;不过可以告诉我公司名或者项目名&#xff0c;放在官网上好看点。哈哈-_-) 二、写在之前 距离上一篇更新已经大约4个月了&#xff0c;公司的事情&#xff0c;自己的一些琐事一直没时间…