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>

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

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

相关文章

AQWA | 水动力分析 二阶波浪力

&#x1f3c6;本文收录于「Bug调优」专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&&…

Midjourney对图片细微调整和下载保存

点击v2是对第二图片细微调整。 点击u3对第3张图片进行放大。 保存图片: 对点击u3放大的图片&#xff0c;双击 , 右键保存图片

停车场小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;车主管理&#xff0c;商家管理&#xff0c;停车场信息管理&#xff0c;预约停车管理&#xff0c;商场收费管理&#xff0c;留言板管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;停车场信息…

审核平台前端新老仓库迁移

背景 审核平台接入50业务&#xff0c;提供在线审核及离线质检、新人培训等核心能力&#xff0c;同时提供数据报表、资源追踪、知识库等工具。随着平台的飞速发展&#xff0c;越来越多的新业务正在或即将接入审核平台&#xff0c;日均页面浏览量为百万级别。如今审核平台已是公司…

代码提交错分支了怎么办?

你有么有遇到过正在开发的代码&#xff0c;提交到生产环境的分支去&#xff0c;遇到这种情况怎么办&#xff1f; 问题重现&#xff1a; 这段注释// AAAAAAAAAAA 本来应该写在dev分支的&#xff0c;现在提交并push到master分支了 现在第一步&#xff0c;撤回提交 第二步&…

MySQL的Geometry数据处理之WKB方案

MySQL的Geometry数据处理之WKT方案&#xff1a;https://blog.csdn.net/qq_42402854/article/details/140134357 MySQL的Geometry数据处理之WKT方案中&#xff0c;介绍WTK方案的优点&#xff0c;也感受到它的繁琐和缺陷。比如&#xff1a; 需要借助 ST_GeomFromText和 ST_AsTex…

qt结合vs2022安装

进入清华大学开源软件&#xff1a; 清华大学开源软件镜像站 | Tsinghua Open Source Mirror 下载完成后&#xff0c;双击进行安装&#xff1a; 进入邮箱进行验证&#xff1a; 可能是因为网络问题&#xff0c;无法安装。 重新安装5.12.12版本。 安装后启动失败&#xff0c;重新…

横截面交易策略:概念与示例

数量技术宅团队在CSDN学院推出了量化投资系列课程 欢迎有兴趣系统学习量化投资的同学&#xff0c;点击下方链接报名&#xff1a; 量化投资速成营&#xff08;入门课程&#xff09; Python股票量化投资 Python期货量化投资 Python数字货币量化投资 C语言CTP期货交易系统开…

数据结构--单链表实现

欢迎光顾我的homepage 前言 链表和顺序表都是线性表的一种&#xff0c;但是顺序表在物理结构和逻辑结构上都是连续的&#xff0c;但链表在逻辑结构上是连续的&#xff0c;而在物理结构上不一定连续&#xff1b;来看以下图片来认识链表与顺序表的差别 这里以动态顺序表…

WGAN(Wassertein GAN)

WGAN E x ∼ P g [ log ⁡ ( 1 − D ( x ) ) ] E x ∼ P g [ − log ⁡ D ( x ) ] \begin{aligned} & \mathbb{E}_{x \sim P_g}[\log (1-D(x))] \\ & \mathbb{E}_{x \sim P_g}[-\log D(x)] \end{aligned} ​Ex∼Pg​​[log(1−D(x))]Ex∼Pg​​[−logD(x)]​ 原始 GAN …

springboot基于Java的超市进销存系统+ LW+ PPT+源码+讲解

第三章系统分析与设计 3.1 可行性分析 一个完整的系统&#xff0c;可行性分析是必须要有的&#xff0c;因为他关系到系统生存问题&#xff0c;对开发的意义进行分析&#xff0c;能否通过本网站来补充线下超市进销存管理模式中的缺限&#xff0c;去解决其中的不足等&#xff0c…

6域名系统DNS

《计算机网络》第7版&#xff0c;谢希仁 每次记不清楚的知识点&#xff0c;通过上网查找&#xff0c;总是只能看到很零碎的答案。最后还是最喜欢看这个版本的书&#xff0c;一看就回忆起来了&#xff0c;逻辑严谨&#xff0c;循循善诱&#xff0c;知识讲解的全面又清晰&#xf…

架构师应该在团队中发挥怎样的作用?

架构师分为5种&#xff1a; 1.企业架构师EA(Enterprise Architect) EA的职责是决定整个公司的技术路线和技术发展方向。 2.基础结构架构师IA(Infrastructure Architect) IA的工作就是提炼和优化技术方面积累和沉淀形成的基础性的、公共的、可复用的框架和组件&#xff0c;这…

Qt 基础组件速学 鼠标和键盘事件

学习目标&#xff1a; 鼠标事件和键盘事件应用 前置环境 运行环境:qt creator 4.12 学习内容和效果演示&#xff1a; 1.鼠标事件 根据鼠标的坐标位置&#xff0c;做出对应的事件。 2.键盘事件 根据键盘的输入做出对应操作 详细主要代码 1.鼠标事件 #include "main…

一文读懂轻量日志收集系统Loki工作原理

Loki 是由 Grafana Labs 开发的日志聚合系统&#xff0c;设计目标是提供一种高效、低成本的日志收集和查询解决方案。与传统的日志系统&#xff08;如 ELK Stack&#xff09;不同&#xff0c;Loki 不会对日志内容进行索引&#xff0c;而是仅对日志的元数据进行索引&#xff0c;…

FTP、http 、tcp

HTTP VS FTP HTTP &#xff1a;HyperText Transfer Protocol 超文本传输协议&#xff0c;是基于TCP协议 FTP&#xff1a; File Transfer Protocol 文件传输协议&#xff0c; 基于TCP协议&#xff0c; 基于UDP协议的FTP 叫做 TFTP HTTP 协议 通过一个SOCKET连接传输依次会话数…

FIND_IN_SET使用案例--[sql语句根据多ids筛选出对应数据]

一 FIND_IN_SET select id,system_ids from intellect_client_info where FIND_IN_SET(5, system_ids) > 0;

Spring Boot 中的监视器是什么?有什么作用?

前言&#xff1a; 监听器相信熟悉 Spring、Spring Boot 的都知道&#xff0c;但是监视器又是什么&#xff1f;估计很多人一脸懵的状态&#xff0c;本篇分享一下 Spring Boot 的监视器。 Spring Boot 系列文章传送门 Spring Boot 启动流程源码分析&#xff08;2&#xff09; …

Apache DolphinScheduler 与 AWS 的 EMR/Redshift 集成实践分享

引言 这篇文章将给大家讲解关于DolphinScheduler与AWS的EMR和Redshift的集成实践&#xff0c;通过本文希望大家能更深入地了解AWS智能湖仓架构&#xff0c;以及DolphinScheduler在实际应用中的重要性。 AWS智能湖仓架构 首先&#xff0c;我们来看一下AWS经典的智能湖仓架构图…

高考选专业,兴趣与就业前景该如何平衡?

从高考结束的那一刻开始&#xff0c;有些家长和学生就已经变得焦虑了&#xff0c;因为他们不知道成绩出来的时候学生应该如何填报志愿&#xff0c;也不知道选择什么样的专业&#xff0c;毕竟大学里面的专业丰富多彩&#xff0c;如何选择确实是一门学问&#xff0c;而对于学生们…