Spring AOP、Spring MVC工作原理、发展演变、常用注解
Spring AOP
概念
AOP全称为Aspect Oriented Programming,表示面向切面编程。切面指的是将那些与业务无关,但业务模块都需要使用的功能封装起来的技术。
AOP基本术语
**连接点(Joinpoint):**连接点就是被拦截到的程序执行点,因为Spring只支持方法类型的连接点,所以在Spring中连接点就是被拦截到的方法。连接点由两个信息确定:
- 方法( 表示程序执行点,即在哪个目标方法)
- 相对点(表示方位,即目标方法的什么位置,比如调用前,后等)
切入点(Pointcut): 一般认为,所有的方法都可以认为是连接点,但是我们并不希望在所有的方法上都添加通知,而切入点的作用就是提供一组规则来匹配连接点,给满足规则的连接点添加通知。
**通知、增强(Advice) : **可以为切入点添加额外功能,分为:前置通知、后置通知、异常通知、环绕通知、最终通知等。
**目标对象(Target)**目标对象指将要被增强的对象,即包含主业务逻辑的类对象。或者说是被一个或者多个切面所通知的对象。
**织入(Weaving):**织入是将切面和业务逻辑对象连接起来, 并创建通知代理的过程。织入可以在编译时,类加载时和运行时完成。在编译时进行织入就是静态代理,而在运行时进行织入则是动态代理。
**代理(Proxy):**被AOP织入通知后,产生的结果类。
**切面(Aspect):*切面是一个横切关注点的模块化,一个切面能够包含同一个类型的不同增强方法,比如说事务处理和日志处理可以理解为两个切面。切面由切入点和通知组成。Spring AOP就是负责实施切面的框架,它将切面所定义的横切逻辑织入到切面所指定的连接点中。
应用
配置pom文件:
<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.11</version> </dependency> <!-- 切面相关的包 --> <dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.8.9</version> </dependency>
编写业务层:
接口:
public interface UserService {int saveUser(Map<String,Object> params); }
实现类:
public class UserServiceImpl implements UserService{@Overridepublic int saveUser(Map<String, Object> params) {System.out.println("保存用户信息" + params);return 0;} }
配置业务层:
spring-aop.xml:
<!--业务层对象--><bean id="userService" class="com.qf.aop.service.UserServiceImpl"/>
编写通知类:
通知分为前置通知、后置通知、异常抛出通知、环绕通知、最终通知(没什么用这里不实现)。前置通知:
接口为MethodBeforeAdvice,其底层实现如下:
public interface MethodBeforeAdvice extends BeforeAdvice {/*** Callback before a given method is invoked.*/void before(Method method, Object[] args, @Nullable Object target) throws Throwable; }
使用前置通知需要实现这个接口:
public class BeforeAdvice implements MethodBeforeAdvice{@Overridepublic void before(Method method, Object[] args, Object target) throws Throwable {String methodName = method.getName();String className = method.getDeclaringClass().getName();System.out.println("准备执行方法:" + className + "." + methodName + "参数:" + Arrays.toString(args));} }
写完通知类后需要配置通知:
spring-aop.xml:
<!--业务层对象--> <bean id="userService" class="com.qf.aop.service.UserServiceImpl"/><!--配置通知对象--> <bean id="before" class="com.qf.aop.advice.BeforeAdvice"/>
当通知对象和业务层对象都纳入IOC容器管理之后,需要将通知对象作用在业务层对象上。Spring提供了aop标签来完成这一功能。
<!--aop配置--><aop:config><!--pointcut表示切点,也就是通知会在哪些位置触发expression表示切点表达式,切点表达式必须是execution(), execution()方法中的参数必须配置到方法上比如 * com.qf.spring.aop.service..*(..)第一个 * 表示任意访问修饰符com.qf.spring.aop.service.. 最后的两个..表示service包下面的所有子包中的类*(..) 表示任意方法, 如果()中没有..,则表示不带参数的方法;有,就表示带任意参数--><!--切入点配置--><aop:pointcut id="pc" expression="execution(* com.qf.aop.service..*(..))"/><!--通知配置--><aop:advisor advice-ref="before" pointcut-ref="pc"/></aop:config> </beans>
测试:
public class AopTest {@Testpublic void saveUserTest(){ApplicationContext context = new ClassPathXmlApplicationContext("spring-aop.xml");UserService userService = context.getBean("userService", UserService.class);HashMap<String, Object> map = new HashMap<>();map.put("name","爱德华");map.put("sex","男");int i = userService.saveUser(map);} }
注:利用ClassPathXmlApplicationContext拿到配置文件上下文对象,进而拿到bean对象。
后置通知接口:AfterReturningAdvice.
剩下的流程和前置接口相同,编写通知类,配置通知类对象,配置通知。
<!--配置通知对象--><bean id="before" class="com.qf.aop.advice.BeforeAdvice"/><bean id="after" class="com.qf.aop.advice.AfterAdvice"/><!--aop配置--><aop:config><!--切入点配置--><aop:pointcut id="pc" expression="execution(* com.qf.aop.service..*(..))"/><!--通知配置--><aop:advisor advice-ref="before" pointcut-ref="pc"/><aop:advisor advice-ref="after" pointcut-ref="pc"/></aop:config> </beans>
异常抛出通知
异常抛出接口为ThrowsAdvice。
注意:异常通知类接口没有要重写的方法,而是自定义。
public class ExceptionAdvice implements ThrowsAdvice {public void afterThrowing(Method method, Object[] args, Object target, Exception ex){String methodName = method.getName();String className = method.getDeclaringClass().getName();System.out.println("执行方法时:" + className + "." + methodName + ",参数:" + Arrays.toString(args) + ",发生了异常:" + ex.getMessage());} }
配置和前面相同:
<bean id="exception" class="com.qf.aop.advice.ExceptionAdvice" /><aop:advisor advice-ref="exception" pointcut-ref="pc"/>
环绕通知
接口:MethodInterceptor
注意:1.这里重写的方法参数为MethodInvocation invocation,可以通过invocation.getMethod();//获取被拦截的方法。
public class AroundAdvice implements MethodInterceptor {@Overridepublic Object invoke(MethodInvocation invocation) throws Throwable {Method method = invocation.getMethod();//获取被拦截的方法对象Object[] args = invocation.getArguments();//获取方法的参数Object target = invocation.getThis();//获取代理对象String methodName = method.getName();String className = method.getDeclaringClass().getName();System.out.println("准备执行方法:" + className + "." + methodName + ",参数:" + Arrays.toString(args));Object returnVal = method.invoke(target, args);System.out.println("执行完方法:" + className + "." + methodName + ",参数:" + Arrays.toString(args) + ",得到返回值:" + returnVal);return returnVal;} }
环绕通知可以实现前置通知、后置通知、异常抛出通知的功能,所以配置文件中只需要配置环绕通知即可。
<!--业务层对象--> <bean id="userService" class="com.qf.aop.service.UserServiceImpl"/><!--配置通知对象--><bean id="around" class="com.qf.aop.advice.AroundAdvice"/><!--aop配置--> <aop:config><!--切入点配置--><aop:pointcut id="pc" expression="execution(* com.qf.aop.service..*(..))"/><!--通知配置--><aop:advisor advice-ref="around" pointcut-ref="pc"/> </aop:config>
AspectJ
简介:
AspectJ是一个面向切面的框架,它扩展了Java语言,定义了AOP 语法,能够在编译期提供代码的织入。Spring通过集成AspectJ实现了以注解的方式定义增强类,大大减少了配置文件中的工作量
注解:
- @Aspect 切面标识
- @Pointcut 切入点
- @Before 前置通知
- @AfterReturning 后置通知
- @Around 环绕通知
- @AfterThrowing 异常抛出通知
通知类编写:
package com.qf.aop.advice;import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.*; import org.aspectj.lang.reflect.MethodSignature;import java.lang.reflect.Method; import java.util.Arrays;@Aspect public class AspectJAdvice {@Before(value = "execution(* com.qf.aop.service..*(..))")public void before(JoinPoint jp){Signature signature = jp.getSignature();//获取签名Object[] args = jp.getArgs();//获取方法参数if(signature instanceof MethodSignature){//如果签名是方法签名Method method = ((MethodSignature) signature).getMethod();//获取方法String methodName = method.getName();String className = method.getDeclaringClass().getName();System.out.println("准备执行方法:" + className + "." + methodName + ",参数:" + Arrays.toString(args));}}@AfterReturning(value = "execution(* com.qf.aop.service..*(..))", returning = "returnValue")public void after(JoinPoint jp, Object returnValue){Object[] args = jp.getArgs(); //获取方法参数Signature signature = jp.getSignature(); //获取签名if(signature instanceof MethodSignature){ //如果签名是方法签名Method method = ((MethodSignature) signature).getMethod(); //获取方法String methodName = method.getName();String className = method.getDeclaringClass().getName();System.out.println("执行完方法:" + className + "." + methodName + ",参数:" + Arrays.toString(args) + ",得到返回值:" + returnValue);}}@AfterThrowing(value = "execution(* com.qf.aop.service..*(..))", throwing = "t")public void exception(JoinPoint jp, Throwable t){Object[] args = jp.getArgs(); //获取方法参数Signature signature = jp.getSignature(); //获取签名if(signature instanceof MethodSignature){ //如果签名是方法签名Method method = ((MethodSignature) signature).getMethod(); //获取方法String methodName = method.getName();String className = method.getDeclaringClass().getName();System.out.println("执行方法时:" + className + "." + methodName + ",参数:" + Arrays.toString(args) + ",发生了异常:" + t.getMessage());}}@Around("execution(* com.qf.aop.service..*(..))")public Object around(ProceedingJoinPoint pjp) throws Throwable {Object[] args = pjp.getArgs();//获取方法的参数Object target = pjp.getTarget(); //获取代理对象Signature signature = pjp.getSignature(); //获取签名if(signature instanceof MethodSignature) { //如果签名是方法签名Method method = ((MethodSignature) signature).getMethod(); //获取被拦截的方法对象String methodName = method.getName();String className = method.getDeclaringClass().getName();try {System.out.println("准备执行方法:" + className + "." + methodName + ",参数:" + Arrays.toString(args));Object returnValue = method.invoke(target, args);System.out.println("执行完方法:" + className + "." + methodName + ",参数:" + Arrays.toString(args) + ",得到返回值:" + returnValue);return returnValue;} catch (Throwable t){System.out.println("执行方法时:" + className + "." + methodName + ",参数:" + Arrays.toString(args) + ",发生了异常:" + t.getMessage());throw t;}}return null;} }
启用注解支持:
<!--配置通知对象--> <bean class="com.qf.aop.advice.AspectJAdvice"/> <!--启动AspectJ注解 自动为类生成代理--><aop:aspectj-autoproxy proxy-target-class="true"/>
SpringMVC
简介
1. Spring MVC
SpringMVC是一个Java 开源框架, 是Spring Framework生态中的一个独立模块,它基于 Spring 实现了Web MVC(数据、业务与展现)设计模式的请求驱动类型的轻量级Web框架,为简化日常开发,提供了很大便利。
2. Spring MVC 核心组件
DispatcherServlet 前置控制器
负责接收请求、分发请求
Handler 处理器
处理器包括了拦截器、控制器中的方法等,主要负责处理请求
HandlerMapping 处理器映射器
解析配置文件、扫描注解,将请求与处理器进行匹配
HandlerAdpter 处理器适配器
根据请求来找到匹配的处理器,这个过程称为适配
ViewResolver 视图解析器
处理器执行后得到的结果可能是一个视图,但这个视图属于逻辑视图(页面中存在逻辑代码,比如循环、判断),需要使用视图解器行处理,这个过程称为渲染视图
Spring MVC工作原理
mvc工作原理
前端发送的请求由DispatcherServlet接收到,然后根据提供的HandlerMapping来分发过去,在分发请求过程中使用到HandlerAdapter来适配处理器(因为处理的类型无法确定),找到对应的处理器适配器之后就会执行这个处理器,执行会得到一个ModelAndView,然后交给ViewResolver进行解析得到视图位置,然后对视图进行渲染,渲染完成后将渲染好的视图交给DispatcherServlet,然后传回前端展示。
Spring MVC发展演变
1.Bean的名字或ID匹配URL请求
由于版本更新,使用新版本会无法完成过时的功能,但是为了更好地理解演变的过程,这里使用低版本:
<!--低版本--> <dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>4.3.9.RELEASE</version> </dependency> <dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>4.3.9.RELEASE</version> </dependency> <dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>4.0.1</version><scope>provided</scope> </dependency>
首先需要再web.xml配置文件中配置DispatcherServlet,包括初始化参数(全局上下文,自己项目的配置文件路径以及使得项目启动时就创建servlet的初始化参数)
<servlet><servlet-name>dispatcherServlet</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:spring-mvc.xml</param-value></init-param><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>dispatcherServlet</servlet-name><url-pattern>/</url-pattern></servlet-mapping>
然后是自己写的spring-mvc.xml配置,这里需要写视图解析器、处理器映射器(处理器适配器采用默认的,在底层mvc框架中会根据处理器类型寻找合适的处理器适配器)。具体的逻辑为前端发送请求->处理器映射方式、配置控制器找到控制器->控制器返回modelandview->视图解析器解析路径找到jsp文件:
<!--视图解析器--><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="prefix" value="/"/><property name="suffix" value=".jsp"/></bean><!--处理器映射的方式:使用bean的名字或者id的值来与请求匹配--><bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/><!--通过id值匹配请求的URL--><bean id="/user" class="com.qf.controller.UserController"/>
处理器映射器给出映射的方式:使用bean的名字或者id,然后DispatcherServlet找到处理器适配器,处理器适配器提供id和处理器的路径。然后底层会根据路径找到处理器。
处理器:
public class UserController extends AbstractController {@Overrideprotected ModelAndView handleRequestInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {return new ModelAndView("user");} }
处理器返回ModelAndView给适配器,DispatcherServlet根据id拿到返回的ModelAndView,并交给视图解析器进行处理,最终将处理好的路径进行渲染。
但是这样做有一个问题,每一个请求都需要一个控制器与之对应,如果有很多请求,那么就要写很多个控制器。开发效率极为低下,而Spring提供了方法名匹配请求来解决这个问题。
2.Bean方法名匹配请求
方法名解析器:InternalPathMethodNameResolver,将方法名作为匹配URL请求的依据,与控制器关联起来。
这样一来请求就可以直接与控制器中的方法关联,那么控制器中的方法就应该有多个。
多操作控制器:
MultiActionController控制器类,供其他控制器类继承,在其子类中可以编写多个处理请求的方法,然后使用方法名解析器去匹配请求。
控制器:
public class UserMultiController extends MultiActionController {//这个方法匹配/login请求public ModelAndView login(HttpServletRequest request, HttpServletResponse response){return new ModelAndView("login");}//这个方法匹配/register请求public ModelAndView register(HttpServletRequest request,HttpServletResponse response){return new ModelAndView("register");} }
编写完控制器后需要写相应的控制器映射器(视图解析器不变):
spring-mvc.xml:
<!--方法名解析器,处理映射的方式:使用方法名--><bean id="methodNameResolver" class="org.springframework.web.servlet.mvc.multiaction.InternalPathMethodNameResolver"/><!--/login 请求使用该bean对象处理--><bean id="/login" class="com.qf.controller.UserMultiController"><property name="methodNameResolver" ref="methodNameResolver"/></bean><!--/register 请求使用该bean对象处理--><bean id="/register" class="com.qf.controller.UserMultiController"><property name="methodNameResolver" ref="methodNameResolver"/></bean>
按照这种匹配请求的方式,如果一个控制器要处理多个请求,就会导致此配置文件无限扩展,变得冗杂,后期难以维护,这时如何解决?
Spring提供了SimpleUrlHandlerMapping映射器,该映射器支持一个控制器与多个请求匹配的同时也解决了配置信息繁多的问题。
3.简单URL处理器映射
在Bean方法名匹配请求方式的控制器不变的基础上,只需要改动控制器映射器即可:
spring-mvc.xml:
<!--使用简单URL处理器映射--><bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"><property name="mappings"><props><prop key="/view">userController</prop><prop key="/user/*">userMultiController</prop></props></property></bean><bean id="userController" class="com.qf.controller.UserController"/><bean id="userMultiController" class="com.qf.controller.UserMultiController"/>
随着业务的增加,控制器的数量也为增加,请求的匹配也会增多,xml文件里虽然减少了冗余,但每次增加方法也会增加代码量,如何解决?
-Spring提供了DefaultAnnotationHandlerMapping映射器,支持使用注解来匹配请求,这样就解决了请求匹配导致配置信息繁多的问题,同时还提升了开发效率。
注解匹配请求
控制器中通过@Controller注解说明这是一个处理器,方法中通过@RequestMapping注解注明映射信息。
controller:
@Controller public class UserAnnotationController {@RequestMapping(value = "/login",method = RequestMethod.GET)public String login(){return "login";}@RequestMapping(value = "register",method = RequestMethod.GET)public String register(){return "register";} }
注意:这次的controller不需要实现接口或者继承抽象类了,也就意味着可以自定义方法,只需要在方法上加注解就可以达到映射的效果。
写好处理器后需要配置(视图解析器还是不用变,只需要改变处理器映射器就行):
spring-mvc.xml:<!--类上的注解处理器--><bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/><!--方法上的注解处理器--><bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"><!--扫描包,使得该包下类以及类中定义的方法上所使用的注解生效--><context:component-scan base-package="com.qf.controller" />
新的版本配置
<!--较新的版本使用该标签启动注解支持--> <mvc:annotation-driven/> <!--扫描包,使类和类方法注解生效--> <context:component-scan base-package="com.qf.controller"/>
相当于使用一句话代替了原来对类和方法上注解处理器的声明。
Spring MVC常用注解
@Controller
控制器的标识
@Controller public class UserController{}
@RequestMapping
该注解用于匹配请求(注明URI)
@Controller @RequestMapping("/user") public class UserController{@RequestMapping(value="/login", method=RequestMethod.POST)public int login(){return 1;} }
@RequestBody
该方法只能用在方法的参数上,用于从请求体中获取数据并注入参数中,并且获取的数据只能是JSON格式的数据。
@Controller @RequestMapping("/user") public class UserController{@RequestMapping(value="/login", method=RequestMethod.POST)public int login(@RequestBody User user){return 1;} }
@ResponseBody
该注解用于向页面传递数据,如果没有该注解,那么controller方法中返回的任何数据都会被认为是一个页面字符串。
@Controller @RequestMapping("/user") public class UserController{@RequestMapping(value="/login", method=RequestMethod.POST)@ResponseBodypublic int login(@RequestBody User user){return 1;} }
@RequestParam
该注解只能用在方法的参数上, 用于从 URL 查询字符串或表单参数中提取参数。
@Controller @RequestMapping("/user") public class UserController{@RequestMapping(value="/search", method=RequestMethod.GET)@ResponseBodypublic List<User> searchUsers(@RequestParam(value="name") String name){return new ArrayList<>();} }
注意:@RequestParam和@PathVariable的区别:
@PathVariable:
- 用于从 URL 路径中提取参数。
- 例如:提取
http://example.com/user/john
中的john
。- 用于 RESTful 风格的 URL。
@RequestParam:
- 用于从 URL 查询字符串或表单参数中提取参数。
- 例如:提取
http://example.com/user/search?name=john
中的name
,或提取表单提交的数据。- 适用于查询字符串参数和表单参数。
@PathVariable
该注解只能应用在方法的参数上,用于从请求路径中获取数据并注入至参数中
@Controller @RequestMapping("/user") public class UserController{// /user/admin@RequestMapping(value="/{username}", method=RequestMethod.GET)@ResponseBodypublic User queryUser(@PathVariable("username") String username){return new User();} }
注意: 花括号
{}
用于定义路径变量,表示 URL 中的动态部分,这些部分将被提取并传递给控制器方法的参数。 前端在发送请求时,必须用具体的username
替换路径变量 。
@RequestHeader
该注解只能应用在方法的参数上,用于从请求头中获取数据
@RequestMapping("/find") public void findUsers(@RequestHeader("Content-Type") String contentType) {//从请求头中获取Content-Type的值 }
@CookieValue
该注解只能应用在方法的参数上,用于从请求中获取cookie的值
@RequestMapping("/find") public void findUsers(@CookieValue("JSESSIONID") String jsessionId) {//从请cookie中获取jsessionId的值 }
@ControllerAdvice
该注解只能应用在类上,表示这个类就是处理异常的控制器
/*** 异常处理的控制器*/ @ControllerAdvice //这个注解就是spring mvc提供出来做全局异常统一处理的 public class ExceptionController { }
@ExceptionHandler
该注解只能应用在@ControllerAdvice或者@RestControllerAdvice标识的类的方法上用来处理异常
/*** 异常处理的控制器*/ @ControllerAdvice //这个注解就是spring mvc提供出来做全局异常统一处理的 public class ExceptionController {@ExceptionHandler //异常处理器@ResponseBody //响应至页面public String handleException(Exception e){return e.getMessage();} }
Spring 对 RESTFUL的支持
@RestController
相当于@Controller 和 @ResponseBody 注解的组合。表示该类中的所有方法执行完成后所返回的结果直接向页面输出。
@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
静态资源处理
静态资源无法访问的原因
静态资源包含html、js、css、图片、字体文件等。静态文件没有url-pattern,所以默认是无法访问的。之所以可以访问,是因为tomcat中有一个全局的servlet:org.apache.catalina.servlets.DefaultServlet,它的url-pattern是 “/”, 所以项目中不能匹配的静态资源请求,都由这个Servlet来处理。但在SpringMVC中DispatcherServlet也采用了"/" 作为url-pattern, 那么项目中不会再使用全局的Serlvet,这样就造成了静态资源不能完成访问。
处理方案
方案一:修改DispatcherServlet对应的url-pattern修改为"/"以外的其他匹配样式。
方案二(建议):将所有的静态资源放进一个static包中,如果需要访问,则将defaultServlet的url-pattern的url-mapping改为/static/*
<!-- web.xml --> <servlet-mapping><servlet-name>default</servlet-name><url-pattern>/static/*</url-pattern></servlet-mapping>
方案三:利用default-servlet-handler 将处理静态资源的请求转发给容器的默认 Servlet ,而不是给DispatcherServlet。
<!-- spring-mvc.xml --> <!-- 这个handler就是处理静态资源的,它的处理方式就是将请求转会到tomcat中名为default的Servlet --> <mvc:default-servlet-handler/> <!-- mapping是访问路径,location是静态资源存放的路径 --> <mvc:resources mapping="/static/**" location="/static/" />
中文乱码处理
在web.xml中配置字符编码过滤器CharacterEncodingFilter
<filter><filter-name>encodingFilter</filter-name><!--字符编码过滤器--><filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class><init-param><!--编码格式--><param-name>encoding</param-name><param-value>UTF-8</param-value></init-param><init-param><!--强制编码--><param-name>forceEncoding</param-name><param-value>true</param-value></init-param></filter><filter-mapping><filter-name>encodingFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping>
相关文章:

Spring AOP、Spring MVC工作原理、发展演变、常用注解
Spring AOP 概念 AOP全称为Aspect Oriented Programming,表示面向切面编程。切面指的是将那些与业务无关,但业务模块都需要使用的功能封装起来的技术。 AOP基本术语 **连接点(Joinpoint):**连接点就是被拦截到的程序执…...

grid布局下的展开/收缩过渡效果【vue/已验证可正常运行】
代码来自GPT4o:国内官方直连GPT4o <template><div class"container"><button class"butns" click"toggleShowMore">{{ showAll ? 收回 : 显示更多 }}</button><transition-group name"slide-fade&…...

Qt/C++编写地图应用/离线地图下载/路径规划/轨迹回放/海量点/坐标转换
一、前言说明 这个地图组件写了很多年了,最初设计的比较粗糙,最开始只是为了满足项目需要,并没有考虑太多拓展性,比如最初都是按照百度地图写死在代码中,经过这几年大量的现场实际应用,以及大量的用户提出…...

最新版Python安装教程
一、安装Python 1.下载Python 访问Python官网: https:/www.oython.orgl 点击downloads按钮,在下拉框中选择系统类型(windows/Mac OS./Linux等) 选择下载最新稳定版本的Python 以下内容以演示安装Windows操作系统64位的python 左边是稳定发布版本Stabl…...
1.3镜像管理
【必知必会】了解镜像的命名及导入导出镜像 1.3.1镜像的命名 一般情况下,镜像是按照如下格式命名的 服务器ip:端口/分类/镜像名:tag 如果不指明端口,默认是80,tag是latest,比如:192.168.26.101:5000,再比如hub.c.163.com/library/mysql:latest.分类也可以不写,比如d…...

黑马|最新AI+若依 |初识项目
本章主要内容是: 1.快速搭建了若依前后端项目在本地 2.实现了单表的增删改查快速生成 文章目录 介绍1.若依介绍2.若依的不同版本3.项目运行环境 初始化前后端项目1.下载若依项目2.初始化后端a.把表导入到数据库中b.更改application.yml文件 3.初始化前端a.安装依赖…...

ArrayList综合案例-模拟外卖中的商家系统
一案例要求: 二代码要求: package 重修;import java.util.ArrayList; import java.util.Random; import java.util.Scanner;import static java.lang.System.exit;public class first {public static void main(String[] args) {Scanner scnew Scanne…...
Postgres JSON字段怎么修改key的名称
场景 当你不小心将 key 的名称写错了(人员类别:多了一个冒号),或者想把引文改为中文(type改为类型) 大致思路是添加一个新的 key,然后将旧的 key 删除 sql语句 假如 JSON 列为 extra&#x…...

GStreamer学习5----probe数据探测
参考资料: gstreamer中如何使用probe(探针)获取帧数据_gstreamer 视频编码时获取视频关键帧信息-CSDN博客 Gstreamer中可以使用AppSink作为一个分支来查看管线中的数据,还可以使用probe去处理。 在GStreamer中,probe…...

Open3D 点云的圆柱形邻域搜索
目录 一、概述 1.1原理 1.2应用 二、代码实现 2.1完整代码 2.2程序说明 三、实现效果 3.1原始点云 3.2搜索后点云 一、概述 1.1原理 圆柱邻域搜索的基本思想是确定点云中的哪些点位于给定圆柱的内部。一个圆柱可以由以下几个参数定义: 中心点:…...

python如何设计窗口
PyQt是一个基于Qt的接口包,可以直接拖拽控件设计UI界面,下面我简单介绍一下这个包的安装和使用,感兴趣的朋友可以自己尝试一下: 1、首先,安装PyQt模块,这个直接在cmd窗口输入命令“pip install pyqt5”就行…...
C语言获取当前时间
一共有两段代码,一个是获取当前时间,一个是获取到现在的总毫秒数 求关注😄 互粉必回 获取当前时间 #include <stdio.h> #include <time.h> int main() { time_t rawtime; struct tm * timeinfo; char buffer[20]; // 获取当前…...

【每日一练】python三目运算符的用法
""" 三目运算符与基础运算的对比 """ a 1 b 2#1.基础if运算判断写法: if a > b:print("基础判断输出:a大于b") else:print("基础判断输出: a不大于b")#2.三目运算法判断:…...

CentOS 7.9 停止维护(2024-6-30)后可用在线yum源 —— 筑梦之路
众所周知,centos 7 在2024年6月30日,生命周期结束,官方不再进行支持维护,而很多环境一时之间无法完全更新替换操作系统,因此对于yum源还是需要的,特别是对于互联网环境来说,在线yum源使用方便很…...
Git 常用命令备忘
1、删除 (1)、git push origin --delete dev 删除远程分支 (2)、git branch -d dev 删除本地分支 git branch -D dev 强制删除本地分支 2、创建分支 (1)、git checkout -b dev 创建本地分支 (2)、git push origin dev 创建远程分支,此时本地分支与远程…...
Ubuntu24.04安装Skynet环境
安装依赖 sudo apt-get -y install gcc sudo apt-get -y install g sudo apt-get -y install make sudo apt-get install -y autoconf automake libtool sudo apt-get install -y git 或者可以用: sudo apt-get -y install gcc g make autoconf automake libtool…...

【C++】cout.self()函数
📢博客主页:https://blog.csdn.net/2301_779549673 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! 📢本文作为 JohnKi 学习笔记,借鉴了部分大佬案例 📢未来很长&#…...
VueQuill 富文本编辑器技术文档快速上手
VueQuill 富文本编辑器技术文档 1. 安装 VueQuill2. 配置 VueQuill3. 在组件中使用 VueQuill4. 配置选项5. 事件处理6. 数据格式7. 自定义工具栏8. 示例项目结构9. 常见问题如何添加图片上传功能?如何自定义编辑器主题? 在此之前,我讲解过关于…...

链式二叉树oj题
1.输入k ,找第k层节点个数 int TreeKlevel(BTNode*root,int k) {if (root NULL) {return 0;}if (k 1) {return 1;}return TreeKlevel(root->left, k - 1)TreeKlevel(root->right, k - 1); } 在这里我们要确定递归子问题,第一个就是NULL时返回&…...
Curator 是一个开源工具为 Elasticsearch 集群设计,用于自动化索引的维护任务。
Elasticsearch 使用 Curator 进行索引生命周期管理是一种常见的做法,Curator 是一个开源的 Python 工具,专为 Elasticsearch 集群设计,用于自动化索引的维护任务。以下是使用 Curator 进行索引生命周期管理的一些关键步骤和概念: …...

Linux应用开发之网络套接字编程(实例篇)
服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...

工业安全零事故的智能守护者:一体化AI智能安防平台
前言: 通过AI视觉技术,为船厂提供全面的安全监控解决方案,涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面,能够实现对应负责人反馈机制,并最终实现数据的统计报表。提升船厂…...
三维GIS开发cesium智慧地铁教程(5)Cesium相机控制
一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点: 路径验证:确保相对路径.…...
Qt Http Server模块功能及架构
Qt Http Server 是 Qt 6.0 中引入的一个新模块,它提供了一个轻量级的 HTTP 服务器实现,主要用于构建基于 HTTP 的应用程序和服务。 功能介绍: 主要功能 HTTP服务器功能: 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...

k8s业务程序联调工具-KtConnect
概述 原理 工具作用是建立了一个从本地到集群的单向VPN,根据VPN原理,打通两个内网必然需要借助一个公共中继节点,ktconnect工具巧妙的利用k8s原生的portforward能力,简化了建立连接的过程,apiserver间接起到了中继节…...
[Java恶补day16] 238.除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O(n) 时间复杂度…...

初学 pytest 记录
安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...

Linux nano命令的基本使用
参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时,显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...

如何应对敏捷转型中的团队阻力
应对敏捷转型中的团队阻力需要明确沟通敏捷转型目的、提升团队参与感、提供充分的培训与支持、逐步推进敏捷实践、建立清晰的奖励和反馈机制。其中,明确沟通敏捷转型目的尤为关键,团队成员只有清晰理解转型背后的原因和利益,才能降低对变化的…...

【C++】纯虚函数类外可以写实现吗?
1. 答案 先说答案,可以。 2.代码测试 .h头文件 #include <iostream> #include <string>// 抽象基类 class AbstractBase { public:AbstractBase() default;virtual ~AbstractBase() default; // 默认析构函数public:virtual int PureVirtualFunct…...