当前位置: 首页 > news >正文

代理模式和Spring MVC

Spring是一个分层的轻量级的开源Java框架。核心是IOC(Inverse of Control 控制反转)和AOP(Aspect Oriented Programming 面向切面编程)


AOP 面向切面

AOP (Aspect Orient Programming),直译过来就是 面向切面编程,AOP 是一种编程思想,是面向对象编程(OOP)的一种补充。AOP是通过动态代理实现的面向切面的编程,实现在不修改源代码的情况下给程序动态统一添加额外功能的一种技术。

AOP的应用场景

日志、事务、权限、监测


Spring AOP的术语:

名称说明
Joinpoint(连接点)指那些被拦截到的点,在Spring中,指可以被动态代理拦截目标类的方法。
Pointcut(切入点)指要对哪些Joinpoint进行拦截,即被拦截的连接点。
Advice(通知)指拦截到Joinpoint之后要做的事情,即对切入点增强的内容。
Target(目标)指代理的目标对象
Weaving(植入/织入)指把增强代码应用到目标上,生成代理对象的过程
Proxy(代理)指生成的代理对象
Aspect(切面)切入点和通知的结合

通知分类有五种:

即通知切入的五种时机

通知说明
before(前置通知)通知方法在目标方法调用之前执行
after(后置通知)通知方法在目标方法返回或异常之后执行
after-returning(返回后通知)通知方法会在目标方法返回后执行
after-throwing(抛出异常通知)通知方法会在目标方法抛出异常后执行
around(环绕通知)通知方法会将目标方法封装起来

Spring AOP 织入时期

时期说明
编译期切面在目标类编译时被织入,这种方式需要特殊的编译器,AspectJ的织入编译器就是以这种方式织入切面的
类加载期切面的目标类加载到JVM时被织入,这种方式需要特殊的类加载器(ClassLoader)它可以再目标类引入应用之前增强目标类的字节码。
运行期切面在应用运行到某个时期被织入,一般情况下,在织入切面时,AOP容器会为目标动态的创建一个代理对象,Spring AOP 采用的就是这种织入方式。

Spring AOP 基于AspectJ

AspectJ是一个基于Java语言的AOP框架,Spring2.0以后新增了对AspectJ切点表达式支持

定义切点:通过execution函数来定义切点

        语法:execution(访问修饰符 返回类型 方法名 参数 异常)

1.匹配所有类public方法:execution(public * *(..))第一个*表示返回值 ..表示任意个任意类型的参数

2.匹配指定包下所有类所有方法: execution(* cn.xxx.dao.*.*(..)) 第一个想*表示忽略权限和返回值类型

3.匹配指定包下所有类所有方法:execution(* cn.xxx.dao..*(..))包含子包

4.匹配指定类所有方法: execution(* cn.xxx.service.UserService.*(..))

5.匹配实现特定接口所有类方法 : execution(* cn.xxx.dao.GenericDAO+.*(..))

6.匹配所有save开头的方法: execution(* save*(..))

7.匹配某个指定的方法: execution(* com.yh.dao.StudentService.save(..))

通过使用 @Aspect 注解,我们可以将一个类标记为切面类。

使用 @Pointcut 注解用于定义切点表达式

@Aspect
@Component
public class AOPObj {//定义切点@Pointcut("execution(* com.easy.controller.EasyAController.testA(..))")public void pointCuttesta(){}@Before(value = "pointCuttesta()")public void before(){System.out.println("-----前置通知");}@AfterThrowing(value = "pointCuttesta()")public void afterThrowing(){System.out.println("-----异常通知");}@AfterReturning(value = "pointCuttesta()")public void afterReturn(){System.out.println("-----返回通知");}@After(value = "pointCuttesta()")public void after(){System.out.println("-----前置通知");}
}

Spring 的代理模式

Spring的代理模式指的是Spring框架中使用代理对象来管理和增强目标对象的行为

意图:为其他对象提供一种代理以控制对这个对象的访问。

主要解决:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。

何时使用:想在访问一个类时做一些控制。


静态代理

静态代理的实现方式是创建一个实现了与目标对象相同接口的代理类,该代理类在方法调用前后添加额外的逻辑。代理类将目标对象注入进来,并在代理方法中调用目标对象的对应方法。这样,我们可以在不修改目标对象代码的前提下,通过代理类来对目标对象进行增强和控制

例如,定义一个Shopping接口:

public interface Shopping {void shopping();
}

有一个接口实现类EasyA

public class EasyA implements Shopping{@Overridepublic void shopping(){System.out.println("去购物");}
}

创建一个实现了相同接口的代理类:传入代理类的目标对象,在方法调用前后增加额外的业务逻辑,并在代理方法中调用目标对象的对应方法

public class Proxy implements Shopping{//静态代理Shopping s;//需要传入对象public Proxy(Shopping s){this.s=s;}//前后增加业务@Overridepublic void shopping(){System.out.println("------一支穿云箭");s.shopping();System.out.println("------千军万马来相见");}
}

这里通过测试工厂类创建对象并演示:

静态代理的优点是实现简单,易于理解和调试。缺点是:

1.代码冗余:当多个被代理类,多个需增强方法的增强内容一样时,代码重复冗余

2.不易维护:每个被代理类需要专门维护一个代理类,类成倍增加,需增强方法增加时,需要同时维护代理类和被代理类


动态代理

动态代理在运行时动态地生成类字节码,加载进JVM,分为两种:

JDK动态代理

JDK动态代理是通过接口实现的代理方式。当目标对象实现了接口时,Spring会利用JDK的java.lang.reflect.Proxy类来创建代理对象,代理对象会实现与目标对象相同的接口,并且在代理方法中添加自定义的增强逻辑。

演示:定义一个接口:

public interface EasyInterface {void easy();
}

接口实现类中是基本的正常业务逻辑

public class EasyObj implements EasyInterface{@Overridepublic void easy(){System.out.println("-----正常业务逻辑");}
}

定义代理处理器,实现 InvocationHandler 接口,在代理方法中添加自定义的增强逻辑。

//定义代理处理器
public class EasyInvocationHandler implements InvocationHandler {private  Object proxyedObj;//被代理对象public EasyInvocationHandler(Object proxyedObj){this.proxyedObj=proxyedObj;//构造方法}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object result=null;//定义方法的返回对象System.out.println("-------执行方法之前执行的业务");try{result=method.invoke(proxyedObj,args);//正常执行业务逻辑}catch (Exception e){System.out.println("抛出异常");}finally {System.out.println("-------执行方法之后要做的处理");}return result;}
}

声明代理工厂,通过Proxy.newProxyInstance方法创建了一个代理对象。该方法需要三个参数:

  • ClassLoader:用于加载代理类的类加载器。
  • interfaces:目标对象所实现的接口数组,代理类会实现这些接口。
  • InvocationHandler:代理对象调用方法时的处理器。
public class Factory {public static Object getProxy(Object obj){//JDK代理只能实现接口中的方法return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),new EasyInvocationHandler(obj));}
}

测试方法:

    public static void main(String[] args) {EasyObj easy=new EasyObj();Object obj=getProxy(easy);//动态生成的一个代理类的对象if(obj instanceof EasyInterface){System.out.println("obj是代理对象是EasyInterface的实例");EasyInterface e = (EasyInterface) obj;e.easy();}Class c = obj.getClass();System.out.println(c);EasyA easya=new EasyA();obj=getProxy(easya);System.out.println(obj instanceof Shopping);Shopping s=(Shopping) obj;s.shopping();System.out.println(easy.getClass());}

动态代理的优点是更加灵活,可以应用于多个目标对象,但实现和理解相对复杂一些。


CGLIB代理

CGLIB代理是通过继承实现的代理方式。当目标对象没有实现任何接口时,Spring会利用CGLIB库来创建代理对象,代理对象将继承目标对象的类,并覆盖其中的方法来添加自定义的增强逻辑。(目标对象的类不可以用final修饰)

被定义切点的类之后使用@Autoward注入时会注入此类的代理类(此类的子类,包含代理处理器,处理程序等等),而不是本类,通过动态代理实现。

前面在EasyAController的testA上定义了切点:

 @Pointcut("execution(* com.easy.controller.EasyAController.testA(..))"

这里我们通过测试打印类对象发现:注入的是此类通过CGLIB动态代理自动生成的代理类:


Spring MVC

项目有三层架构:控制层、业务层、数据访问层

与项目的三层架构不同,Spring MVC的三层架构基于MVC设计模式。它将应用程序的不同部分分为三个独立的层:模型层(Model)、视图层(View)和控制层(Controller)


@Controller 注解

在Spring MVC中,@Controller是一个用于标记控制器类的注解。使用@Controller注解的类被Spring容器识别为控制器,可以处理来自用户的请求。

@RequestMapping 注解

@RequestMapping是一个用于映射请求路径的注解。它可以用在类级别和方法级别上,用于标识控制器类或方法应该处理的请求路径。

修饰方法:

 @RequestMapping 注解被标注在方法上时,value 属性值就表示访问该方法的 URL 地址。当用户发送过来的请求想要访问该 Controller 下的控制器方法时,请求路径就必须与这个 value 值相同

修饰类:

 @RequestMapping 注解标注在控制器类上时,value 属性的取值就是这个控制器类中的所有控制器方法 URL 地址的父路径。也就是说,访问这个 Controller 下的任意控制器方法都需要带上这个父路径。

@RestController
@RequestMapping("user")
public class EasyDController {// ...
}

@RequestMapping注解可以传入多种属性:

1. value 属性

  • 在 @RequestMapping 注解中,value 属性用来设置控制器方法的请求映射地址。所有能够匹配到该请求映射地址的请求,都可以被该控制器方法处理
  • value 属性是 @RequestMapping 注解的默认属性,如果我们在 @RequestMapping 注解中只设置了一个 value 属性,则该属性名可以被省略
  • value 属性的取值是一个字符串类型的数组,表示该控制器方法可以匹配多个请求地址。

2. name 属性

  • name 属性相当于方法的注释,用于解释这个方法是用来干什么的,使方法更易理解。

如:@RequestMapping(value = "toUser",name = "获取用户信息")

3. method 属性

  • method 属性用来设置控制器方法支持的请求方式。如果一个控制器方法没有设置 @RequestMapping 注解的 method 属性,则说明该控制器方法支持全部请求类型,可以处理所有类型的请求。
  • method 属性的取值是一个 RequestMethod 类型的数组,表示一个控制器方法支持多种方式的请求,常用的请求方式有 GET、POST、DELETE、PUT 等。
请求资源状态转换
请求方式作用
GET(获取资源)用于从服务器获取数据,可以在URL中传递参数,参数会暴露在URL上,通常用于获取数据。
POST(提交数据)用于向服务器提交数据,数据不会暴露在URL上,通常用于提交表单数据、上传文件等。
DELETE(删除资源)用于删除服务器上的资源,通常用于删除指定的数据。
PUT(更新资源)用于更新服务器上的资源,通常用于更新指定的数据。

如:@RequestMapping(value = "/toUser",method = RequestMethod.GET)

  • 我们也可以为同一个控制器方法指定支持多种类型的请求。例如,一个方法既支持 GET 方式的请求,也支持 POST 方式的请求

如:@RequestMapping(value = "/toUser",method = {RequestMethod.GET,RequestMethod.POST})

4. params 属性

  • params 属性用于指定请求中的参数,只有当请求中携带了符合条件的参数时,控制器方法才会对该请求进行处理。

  • params 属性的取值是一个字符串类型的数组,表示只有请求中同时携带了 params 属性指定的全部参数时,控制器方法才会对该请求进行处理。

如:@RequestMapping(value = "/testParam", params = {"name=javaeasy", "url=www.easy.com"})

5. headers 属性

  • headers 属性用于设置请求中请求头信息,只有当请求中携带指定的请求头信息时,控制器方法才会处理该请求。

  • header 属性是一个字符换类型的数组,表示只有当请求同时携带数组中规定的所有头信息时,控制器方法才会对该请求进行处理。

如:@RequestMapping(value = "toUser",headers = "Referer=http://c.biancheng.net")


映射地址支持 Ant 风格的路径

Spring MVC 还提供了对 Ant 风格路径的支持,我们可以在 @RequestMapping 注解的 value 属性中使用 Ant 风格的统配符,来设置请求映射地址。


Spring MVC 获取请求参数

1.通过形参获取请求参数

在 Controller 的控制器方法中设置与请求参数同名的形参,以获取请求中携带的参数。当浏览器发送的请求匹配到这个控制器方法时,Spring MVC 会自动将请求参数赋值给相应的方法形参。

注意:

(1)必须保证参数名一致

  • 我们必须保证控制器方法的形参名称与请求中携带参数的名称完全一致(区分大小写),否则控制器方法接收到的请求参数值会是 null。
  • 如果由于一些特殊原因,实在无法保证参数名严格一致,可以通过 @RequestParam 注解来解决。

(2)无视数据类型

  • 在控制器方法中使用 String 字符串类型的形参接收所有的请求参数,也可以根据实际情况在控制器方法中使用对应数据类型的参数来接收请求参数,而无须自行进行数据类型转换。

(3)不适用于请求参数过多的请求

同名请求参数的处理方式:

    //接收前端的参数   方法的参数名称和前台传递的参数名一样@RequestMapping("parama")public String paramA(String name){return "spingmvc接收到的参数:"+name;}

    //获取地址上的参数@RequestMapping("paramd/{id}")public String paramD(@PathVariable Integer id, HttpServletRequest request){String username= request.getParameter("username");return "接收到的参数是:"+id+"----nsername:"+username;}

@PathVariable是Spring MVC中用于将URL路径中的变量提取到方法参数中的注解。它可以用于在控制器方法中获取URI路径中的值,并将其绑定到方法的参数上。


使用@RequestParam

可以在控制器方法中通过 @RequestParam 注解,在请求参数与控制器方法的形参之间建立起映射关系,将它们绑定起来。这样即使请求参数与控制器方法中的形参名称不一致,也能获取到对应的请求参数值。

    //同时接收多个参数 Map 接收,非常灵活,但是有安全问题@RequestMapping("paramb")public Map paramB(@RequestParam Map params){//默认是model的容器,加了注解就作为存储参数的容器// 将请求的参数注入到对象中return params;}

使用Map可以非常灵活的接收多个参数,但会将传入的参数全盘接收,受到恶意攻击有安全问题


2.通过实体类对象获取(推荐)

在 Controller 控制器方法的形参中设置一个实体类形参,如果请求参数的参数名与实体类中的属性名一致,那么 Spring MVC 会自动将请求参数封装到该实体类对象中。此时我们就可以通过该实体类对象获取所需的请求参数了。

    //使用封装对象接收 程序中只接收我们需要的数据@RequestMapping("paramc")public Staff paramc(Staff staff){return staff;}

这种方式当传入多个参数时,只会保留匹配到所需的参数,能够有效地解决“控制器方法形参不适用于请求参数过多的请求”问题。


请求转发和重定向

请求转发(Forward)是指将当前的HTTP请求转发给另一个资源进行处理,然后将处理结果再返回给客户端。请求转发是在服务器内部完成的,客户端对此过程是无感知的。在请求转发时,URL地址栏不会改变。

转发:同一个服务器中不同的服务进行转发,可以转发到项目中受保护的资源 如 WEB-INF,是request对象执行forward方法。

当控制器方法中所设置的逻辑视图名称以“forward:”为前缀时,该逻辑视图名称不会被 Spring MVC 配置的视图解析器解析,而是会将前缀“forward:”去掉,以剩余部分作为最终路径通过转发的方式实现跳转。


请求重定向(Redirect)是指服务器收到客户端的请求后,返回一个特殊的响应码和一个新的URL地址给客户端,让客户端重新发送一个新的请求到这个URL地址。客户端会根据新的URL地址再次发起请求,并在URL地址栏显示新的地址。重定向是在客户端完成的。

重定向:浏览器发送了两次请求,可以访问到本项目之外的外网资源,重定向在Java Servlet中是通过response对象通知浏览器重新访问,使用redirect来重定向。

    @RequestMapping("methoda")public String methodA(){System.out.println("-----methodA");return "redirect:/methodb";}@RequestMapping("methodb")@ResponseBody//将返回值直接写入回应对象public String methodB(){System.out.println("-----methodB");return "this is methodB";}

请求转发和重定向都可以用于页面跳转和流程控制,但它们之间有一些区别:

  1. 请求转发是服务器内部的操作,速度较快,而重定向是需要客户端重新发起请求,速度较慢。
  2. 请求转发是在服务器内部进行的,因此共享了同一个HTTP请求对象;而重定向是两次独立的请求,每次请求都是独立的,因此无法共享数据。
  3. 请求转发可以在同一个请求中访问被转发的资源,而重定向则会创建一个新的请求,无法直接访问之前的资源。

Spring MVC 拦截器

拦截器(Interceptor)是 Spring MVC 提供的一种强大的功能组件。它可以对用户请求进行拦截,并在请求进入控制器(Controller)之前handler、控制器处理完请求后、甚至是渲染视图后,执行一些指定的操作。

在 Spring MVC 中,拦截器的作用与 Servlet 中的过滤器类似,它主要用于拦截用户请求并做相应的处理,例如通过拦截器,我们可以执行权限验证、记录请求信息日志、判断用户是否已登录等操作。

定义拦截器

Spring MVC 在 org.springframework.web.servlet 包中提供了一个 HandlerInterceptor 接口

@Component
public class EasyInterceptor implements HandlerInterceptor {//三个方法,前置,后置,处理完毕@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("-----preHandle");return true;//return HandlerInterceptor.super.preHandle(request, response, handler);}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("-----postHandle");//HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("-----afterCompletion--整个请求处理完毕");//HandlerInterceptor.super.afterCompletion(request, response, handler, ex);}
}

在三种方法中实现业务逻辑,上述代码仅作为演示过程使用。

配置拦截器

@Configuration
public class InterceptorConfig implements WebMvcConfigurer{//拦截器的注册中心@AutowiredEasyInterceptor easyInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(easyInterceptor)//要拦截的地址.addPathPatterns("/methodc")//排除在外的地址.excludePathPatterns("/","/index");}
}


Spring MVC 的工作流程

  1. 用户通过浏览器发起一个 HTTP 请求,该请求会被 DispatcherServlet(前端控制器)拦截;
  2. DispatcherServlet 调用 HandlerMapping(处理器映射器)找到具体的处理器(Handler)及拦截器,
  3. HandlerMapping将Handler以 HandlerExecutionChain 执行链的形式返回给 DispatcherServlet。
  4. DispatcherServlet 将执行链返回的 Handler 信息发送给 HandlerAdapter(处理器适配器);
  5. HandlerAdapter 根据 Handler 信息找到并执行相应的 Handler(即 Controller 控制器)对请求进行处理;
  6. Handler 执行完毕后会返回给 HandlerAdapter 一个 ModelAndView 对象(Spring MVC 的底层对象,包括 Model 数据模型和 View 视图信息);
  7. HandlerAdapter 接收到 ModelAndView 对象后,将其返回给 DispatcherServlet ;
  8. DispatcherServlet 接收到 ModelAndView 对象后,会请求 ViewResolver(视图解析器)对视图进行解析;
  9. ViewResolver 解析完成后,会将 View 视图并返回给 DispatcherServlet;
  10. DispatcherServlet 接收到具体的 View 视图后,进行视图渲染,将 Model 中的模型数据填充到 View 视图中的 request 域,生成最终的 View(视图);
  11. 视图负责将结果显示到浏览器(客户端)。

Spring MVC 常用(核心)组件

Spring MVC 的常用组件共有 5 个,它们分别是: DispatcherServlet(前端控制器)、HandlerMapping(处理器映射器)、HandlerAdapter(处理器适配器)、Handler(处理器)、ViewResolver(视图解析器)


Spring MVC 异常处理

在实际的应用开发中,经常会不可避免地遇到各种可预知的、不可预知的异常,此时我们就需要对这些异常处理,以保证程序正常运行。

Spring MVC 提供了一个名为 HandlerExceptionResolver 的异常处理器接口,它可以对控制器方法执行过程中出现的各种异常进行处理。

Srping MVC 为 HandlerExceptionResolver 接口提供了多个不同的实现类:

DefaultHandlerExceptionResolver

ResponseStatusExceptionResolver

ExceptionHandlerExceptionResolver

SimpleMappingExceptionResolver

  • ExceptionHandlerExceptionResolver、ResponseStatusExceptionResolver 和 DefaultHandlerExceptionResolver 是 Spring MVC 的默认异常处理器。
  • 如果程序发生异常,Spring MVC 会按照 ExceptionHandlerExceptionResolver → ResponseStatusExceptionResolver → DefaultHandlerExceptionResolver 的顺序,依次使用这三个异常处理器对异常进行解析,直到完成对异常的解析工作为止。

这里编写一个请求结果类,用于在浏览器发出请求后反馈自定义的对应结果:

public class CommonResult {private int code;private String message;private Object data;public CommonResult(int code, String message, Object data) {this.code = code;this.message = message;this.data = data;}public static CommonResult success(int code, String message, Object data){return new CommonResult(code, message, data);}public static CommonResult success(){return new CommonResult(200, "操作成功", null);}public static CommonResult fail(int code, String message, Object data){return new CommonResult(code, message, data);}public static CommonResult fail(){return new CommonResult(400,"操作失败",null);}//其他不同参数的方法重载...//getter和setter方法...
}

定义异常处理器:

局部处理

  • Spring MVC 允许我们在控制器类(Controller 类)中通过 @ExceptionHandler 注解来定义一个处理异常的方法,以实现对控制器类内发生异常的处理。
  • @ExceptionHandler 注解中包含了一个 value 属性,我们可以通过该属性来声明一个指定的异常。如果在程序运行过程中,这个 Controller 类中的方法发生了这个指定的异常,那么 ExceptionHandlerExceptionResolver 就会调用这个 @ExceptionHandler 方法对异常进行处理。
  • 定义在某个控制器类中的 @ExceptionHandler 方法只在当前的控制器中有效,它只能处理其所在控制器类中发生的异常。
  • @ExceptionHandler 方法的优先级--指定异常类型最精确的
    @ExceptionHandler(Exception.class)@ResponseBodypublic CommonResult exh(){return CommonResult.success(200,"稍有问题");}

使用 @ResponseBody 注解时,可以在控制器方法上添加该注解,将方法的返回值直接转换为指定格式(如JSON、XML等)的响应,并发送给客户端。

全局处理

  • @ExceptionHandler 方法定义在一个使用了 @ControllerAdvice 注解的类中。使用 @ControllerAdvice 注解的类可以包含多个不同的带有 @ExceptionHandler 注解的方法
@RestController
@ControllerAdvice
//这样下面的异常处理器就不止处理本类的异常,可以处理整个项目的异常
public class StaffController {//...
}

API接口测试

Apipost是一个用于测试和调试API接口的工具。它可以帮助开发人员在开发和调试API接口时,方便地发送HTTP请求,并查看请求和响应的内容和结果。

编写如下控制类用于测试,启动项目:

@RestController
public class StaffController {@GetMapping("staff")public CommonResult getList(Staff staff){List<Staff> list=null;System.out.println("获取数据");return CommonResult.success(list);}@PostMapping("staff")public CommonResult addStaff(Staff staff){System.out.println("新增数据");return CommonResult.success();}@DeleteMapping("staff/{id}")public CommonResult deleteStaff(@PathVariable int id){System.out.println("删除数据"+id);return CommonResult.success();}@PutMapping("staff")public CommonResult editStaff(Staff staff){System.out.println("编辑数据");return CommonResult.success();}
}

打开Apipost,新建接口:

输入端口号,地址并选择请求方式,发送请求:

相关文章:

代理模式和Spring MVC

Spring是一个分层的轻量级的开源Java框架。核心是IOC(Inverse of Control 控制反转)和AOP(Aspect Oriented Programming 面向切面编程) AOP 面向切面 AOP &#xff08;Aspect Orient Programming&#xff09;,直译过来就是 面向切面编程&#xff0c;AOP 是一种编程思想&#x…...

深入理解Vue slot的原理

文章目录 前言为什么需要插槽作用域插槽插槽的原理总结 前言 插槽是Vue中一个重要的特性&#xff0c;它有很多种用法&#xff1a;默认插槽、具名插槽、作用域插槽。尤其作用域插槽&#xff0c;还有一堆特性&#xff0c;比如解构prop&#xff0c;解构prop的时候还可以进行属性名…...

git fetch作用与用法

目录 git fetch作用 git fetch用法 git fetch作用 git fetch 命令在 Git 版本控制系统中扮演着重要的角色。其主要作用是从远程仓库获取最新版本的项目文件&#xff0c;但不会自动合并或修改你当前的工作。这意味着&#xff0c;使用 git fetch 后&#xff0c;你需要手动合并…...

pycharm如何查看git历史版本变更信息

通过名字查看不同版本 查看版本不同地方...

【2.2 python中的变量】

2.2 python中的变量 在Python中&#xff0c;变量是存储数据值的容器。Python是一种动态类型语言&#xff0c;这意味着你不需要在声明变量时指定变量的类型&#xff1b;Python会根据你赋给变量的值自动确定其类型。下面我将详细介绍Python中的变量&#xff0c;包括保留字&#…...

Python软体中找出一组字符串的最长公共前缀:算法与实现

Python软体中找出一组字符串的最长公共前缀:算法与实现 在处理字符串数据时,寻找多个字符串之间的共同特征是一个常见的需求。特别是在文件名、URL、或其他文本数据中,找到最长公共前缀(Longest Common Prefix, LCP)可以帮助我们进行更高效的搜索和分类。本文将详细介绍如…...

git lfs使用(huggingface下载大模型文件)-教程记录

写的比较清楚的教程&#xff0c;用于之后有需求时查找用&#xff1a; https://blog.csdn.net/flyingluohaipeng/article/details/130788293...

1. 什么是操作系统

文章目录 1.1 从功能上来看操作系统1.2 硬件资源 1.1 从功能上来看操作系统 对用户来说&#xff0c;操作系统是一个控制软件&#xff0c;可以用来管理应用程序&#xff0c;它可以限制不同的程序来占用的资源。对内部的软件来说&#xff0c;操作系统是一个管理外设和分配资源的…...

数据科学 - 数据预处理 (数据清洗,结构化数据)

1. 前言 数据清洗与结构化数据在数据分析和机器学习项目中扮演着至关重要的角色。随着大数据时代的到来&#xff0c;数据的质量、准确性和可用性成为决定项目成功与否的关键因素。 数据清洗提高数据质量&#xff0c;保证数据集的一致性&#xff1b;促进数据分析与挖掘&#xf…...

基于SpringBoot+Vue的校车调度管理系统(带1w+文档)

基于SpringBootVue的校车调度管理系统(带1w文档) 基于SpringBootVue的校车调度管理系统(带1w文档) 如今&#xff0c;因为无线网相关技术的快速&#xff0c;尤其是在网上进行资源的上传下载、搜索查询等技术&#xff0c;以及信息处理和语言开发技术的进步&#xff0c;同时编程语…...

基于改进拥挤距离的多模态多目标优化差分进化(MMODE-ICD)求解无人机三维路径规划(MATLAB代码)

一、无人机多目标优化模型 无人机三维路径规划是无人机在执行任务过程中的非常关键的环节&#xff0c;无人机三维路径规划的主要目的是在满足任务需求和自主飞行约束的基础上&#xff0c;计算出发点和目标点之间的最佳航路。 1.1路径成本 无人机三维路径规划的首要目标是寻找…...

opencascade AIS_Trihedron源码学习 绘制三轴坐标系

opencascade AIS_Trihedron 前言 //! 创建一个可选择的三轴坐标系 //! 该三轴坐标系包括一个原点&#xff0c;三个轴线和三个标签。 //! 标签的默认文本为 “X”, “Y”, “Z”。 //! 可以更改原点和任意轴线的颜色&#xff0c;箭头和标签的颜色也可以改变。 //! 可视化呈现可…...

【C++】C++应用案例-通讯录管理系统

目录 一、整体介绍 1.1、需求和目标 1.2、整体功能描述 二、页面及功能描述 2.1 主菜单 2.2 添加联系人菜单 2.3 显示联系人菜单 2.4 修改联系人菜单 2.5 退出功能 三、流程设计 3.1 主流程 3.2 添加操作流程 3.3 显示联系人操作流程 3.4 修改联系人操作流程 四…...

使用Python自动批量提取增值税发票信息并导出为Excel文件

要批量提取增值税发票的关键信息并将其导出为 Excel 文件&#xff0c;可以使用 Python 脚本结合 pdfplumber&#xff08;用于解析 PDF 内容&#xff09;、pandas&#xff08;用于处理数据并导出 Excel&#xff09;等库来实现。以下是实现这一目标的详细步骤。 1. 环境设置 首…...

vitis (eclipse) 的Indexer不能搜索、不能跳转到函数和变量定义和声明不能打开调用层次的解决方法

在使用vitis(2021.1) 过程中&#xff0c;有一个非常方便实用的功能&#xff0c;就是在函数或变量等源代码上通过右键菜单或快捷键F3、F4、CtrlAltH&#xff0c;也可以按住Ctrl键然后鼠标停留在函数名或变量名上&#xff0c;点击出现的链接&#xff0c;可以跳转到函数或变量的定…...

最佳HR软件指南:11款高效管理工具

文章介绍了11款人力资源管理工具&#xff1a;Moka、友人才、北森HRSaaS、同鑫eHR、i人事、红海eHR、BambooHR、Skuad、Hibob、OrangeHRM、Verint。 在选择人力资源管理软件时&#xff0c;选错不仅浪费时间和金钱&#xff0c;还会影响团队的工作效率和员工满意度。本文总结了11款…...

家长为孩子出国留学择校的四个步骤

如何为孩子选择最好的学校&#xff1f;无论您是选择公立或私立学校还是在家上学&#xff0c;无论您是否支付学费&#xff0c;都必须仔细规划。在为孩子选择学校的过程中&#xff0c;以下部分有供您考虑的问题。 写下对你来说最重要的五件事 在考虑选择学校时&#xff0c;您可…...

数据挖掘可以挖掘什么类型的模式?

一、挖掘频繁模式、关联和相关性 频繁模式&#xff08;frequent pettern&#xff09;是在数据中频繁出现的模式。 频繁项集一般是指频繁的在事务数据集中一起出现的商品的集合。 频繁出现的子序列&#xff0c;如顾客倾向于先买相机&#xff0c;再买内存卡这样的模式就是一个…...

JAVA中的隐式参数this

在Java中&#xff0c;this 关键字是一个非常重要的隐式参数&#xff0c;它代表当前对象的引用。通过 this&#xff0c;你可以访问类中的字段&#xff08;属性&#xff09;、方法以及构造函数中的参数&#xff08;当参数名与字段名相同时&#xff0c;用于区分&#xff09;。虽然…...

ThreadLocal 使用和详解避坑

在多线程编程中&#xff0c;每个线程都有自己的线程栈和线程本地存储。线程栈用于存储方法调用的信息&#xff0c;而线程本地存储则是每个线程私有的存储空间&#xff0c;用于存储线程的局部变量。ThreadLocal类提供了一种简单的方式来实现线程本地存储&#xff0c;它允许将线程…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例

使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件&#xff0c;常用于在两个集合之间进行数据转移&#xff0c;如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model&#xff1a;绑定右侧列表的值&…...

Debian系统简介

目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版&#xff…...

大型活动交通拥堵治理的视觉算法应用

大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动&#xff08;如演唱会、马拉松赛事、高考中考等&#xff09;期间&#xff0c;城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例&#xff0c;暖城商圈曾因观众集中离场导致周边…...

cf2117E

原题链接&#xff1a;https://codeforces.com/contest/2117/problem/E 题目背景&#xff1a; 给定两个数组a,b&#xff0c;可以执行多次以下操作&#xff1a;选择 i (1 < i < n - 1)&#xff0c;并设置 或&#xff0c;也可以在执行上述操作前执行一次删除任意 和 。求…...

基于数字孪生的水厂可视化平台建设:架构与实践

分享大纲&#xff1a; 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年&#xff0c;数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段&#xff0c;基于数字孪生的水厂可视化平台的…...

【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)

升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点&#xff0c;但无自动故障转移能力&#xff0c;Master宕机后需人工切换&#xff0c;期间消息可能无法读取。Slave仅存储数据&#xff0c;无法主动升级为Master响应请求&#xff…...

return this;返回的是谁

一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请&#xff0c;不同级别的经理有不同的审批权限&#xff1a; // 抽象处理者&#xff1a;审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...

Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档&#xff09;&#xff0c;如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下&#xff0c;风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...

MySQL JOIN 表过多的优化思路

当 MySQL 查询涉及大量表 JOIN 时&#xff0c;性能会显著下降。以下是优化思路和简易实现方法&#xff1a; 一、核心优化思路 减少 JOIN 数量 数据冗余&#xff1a;添加必要的冗余字段&#xff08;如订单表直接存储用户名&#xff09;合并表&#xff1a;将频繁关联的小表合并成…...

Chromium 136 编译指南 Windows篇:depot_tools 配置与源码获取(二)

引言 工欲善其事&#xff0c;必先利其器。在完成了 Visual Studio 2022 和 Windows SDK 的安装后&#xff0c;我们即将接触到 Chromium 开发生态中最核心的工具——depot_tools。这个由 Google 精心打造的工具集&#xff0c;就像是连接开发者与 Chromium 庞大代码库的智能桥梁…...