SSM+Spring Boot+Vue.js3期末复习

本文仅对该课程进行一些重点内容概述,不做其他任何用途。

常见题型:选择,填空,简答,原理分析,开发配置,程序设计

一.SSM板块

SSM包括Spring,Spring MVC,MyBatis三部分

1.Spring

Spring是什么?

Spring 是一个轻量级 Java 开发框架,最早由 Rod Johnson 创建,目的是解决企业级应用开发的业务逻辑层和其他各层的耦合问题 。它是一个分层的 JavaSE/EE Full-stack(一站式)轻量级开源框架,为开发 Java 应用程序提供全面的基础架构支持 。Spring 负责基础架构,因此 Java 开发者可以专注于应用程序的开发

Spring体系结构(重点)

Spring 的功能模块被有组织地分散到约 20 个模块中,这些模块分布在核心容器(Core Container)层、数据访问 / 集成(Data Access/Integration)层、Web 层、面向切面的编程(Aspect Oriented Programming,AOP)模块、植入(Instrumentation)模块、消息传输(Messaging)和测试(Test)模块中。

Spring AOP

AOP,即面向切面编程。它与 OOP(Object-Oriented Programming,面向对象编程)相辅相成,提供了与 OOP 不同的抽象软件结构的视角。在 OOP 中,以类作为程序的基本单元,而 AOP 中的基本单元是 Aspect(切面)。Struts 2 的拦截器设计就是基于 AOP 的思想,是个比较经典的应用。

在业务处理代码中,通常都有日志记录、性能统计、安全控制、事务处理、异常处理等操作。尽管使用 OOP 可以通过封装或继承的方式达到代码的重用,但仍然存在同样的代码分散到各个方法中。因此,采用 OOP 处理日志记录等操作,不仅增加了开发者的工作量,而且提高了升级维护的困难。为了解决此类问题,AOP 思想应运而生。AOP 采取横向抽取机制,即将分散在各个方法中的重复代码提取出来,然后在程序编译或运行阶段,再将这些抽取出来的代码应用到需要执行的地方。这种横向抽取机制采用传统的 OOP 是无法办到的,因为 OOP 实现的是父子关系的纵向重用 。但是 AOP 不是 OOP 的替代品,而是 OOP 的补充 ,它们是相辅相成的。

Spring AOP实验(重点)

基于注解开发AspectJ的过程。该示例的具体要求是:首先在DAO层的实现类中,定义 save、modify 和 delete 三个待增强的方法;然后,使用@Aspect注解定义一个切面,在该切面中定义各类型通知,增强DAO层中的 save、modify 和 delete 方法。

具体实现步骤如下:

上机实践

① 使用Eclipse创建Web应用并导入JAR包

使用Eclipse创建一个名为ch1_3的Dynamic Web Project,除了将Spring的4个基础包、spring-aop-5.3.2.jar和第三方依赖包commons-logging-1.2.jar复制到ch1_3的WEB-INF/lib目录中外,还需要将Spring为AspectJ框架提供的实现spring-aspects-5.3.2.jar以及AspectJ框架所提供的规范包aspectjweaver-xxx.jar复制到WEB-INF/lib目录中。

AspectJ框架所提供的规范包aspectjweaver-xxx.jar可通过地址http://mvnrepository.com/artifact/org.aspectj/aspectjweaver下载,本书使用的是aspectjweaver-1.9.6.jar。

② 创建接口及实现类

在src目录中,创建一个名为aspectj.dao的包,并在该包中创建接口TestDao和接口实现类TestDaoImpl。该实现类作为目标类,在切面类中对其方法进行增强处理。使用注解@Repository将目标类aspectj.dao.TestDaoImpl注解为目标对象。

TestDao的代码如下:

java 复制代码
package aspectj.dao;
public interface TestDao {
    public void save();
    public void modify();
    public void delete();
}

TestDaoImpl的代码如下:

java 复制代码
package aspectj.dao;
import org.springframework.stereotype.Repository;
@Repository("testDao")
public class TestDaoImpl implements TestDao {
    @Override
    public void save() {
        System.out.println("保存");
    }
    @Override
    public void modify() {
        System.out.println("修改");
    }
    @Override
    public void delete() {
        System.out.println("删除");
    }
}

③ 创建切面类

在src目录中,创建一个名为aspectj.annotation的包,并在该包中创建切面类MyAspect。在该类中,首先使用@Aspect注解定义一个切面类,由于该类在Spring中是作为组件使用的,所以还需要使用@Component注解;然后使用@Pointcut注解定义切入点表达式,并通过定义方法表示切入点名称;最后在每个通知方法上添加相应的注解,并将切入点名称作为参数传递给需要执行增强的通知方法。

MyAspect 的核心代码如下:

java 复制代码
/**
 * 切面类,在此类中编写各种类型通知
 */
@Aspect //@Aspect 声明一个切面
@Component //@Component 让此切面成为 Spring 容器管理的 Bean
public class MyAspect {
    /**
     * 定义切入点,通知增强哪些方法。
     * "execution(* aspectj.dao.*.*(..))" 是定义切入点表达式,
     * 该切入点表达式的意思是匹配aspectj.dao包中任意的任意方法的执行。
     * 其中execution()是表达式的主体,第一个*表示返回类型,*代表所有类型;
     * aspectj.dao表示需要匹配的包名,后面第二个*表示类名,使用*代表匹配包中所有的类;
     * 第三个*表示方法名,使用*表示所有方法;后面(..)表示方法的参数,其中".."表示任意参数。
     * 另外,注意第一个*与包名之间有一个空格。
     */
    @Pointcut("execution(* aspectj.dao.*.*(..))")
    private void myPointCut() {
    }
    /**
     * 前置通知,使用 Joinpoint 接口作为参数获得目标对象信息
     */
    @Before("myPointCut()") //myPointCut()是切入点的定义方法
    public void before(JoinPoint jp) {
        System.out.print("前置通知:模拟权限控制");
        System.out.println(",目标类对象:" + jp.getTarget()
                + ",被增强处理的方法:" + jp.getSignature().getName());
    }
    /**
     * 后置返回通知
     */
    @AfterReturning("myPointCut()")
    public void afterReturning(JoinPoint jp) {
        System.out.print("后置返回通知:" + "模拟删除临时文件");
        System.out.println(",被增强处理的方法:" + jp.getSignature().getName());
    }
    /**
     * 环绕通知
     * ProceedingJoinPoint 是 JoinPoint 子接口,代表可以执行的目标方法
     * 返回值类型必须是 Object
     * 必须有一个参数是 ProceedingJoinPoint 类型
     * 必须是 throws Throwable
     */
    @Around("myPointCut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        //开始
        System.out.println("环绕开始:执行目标方法前,模拟开启事务");
        //执行当前目标方法
        Object obj = pjp.proceed();
                //结束
        System.out.println("环绕结束:执行目标方法后,模拟关闭事务");
        return obj;
    }
    /**
     * 异常通知
     */
    @AfterThrowing(value = "myPointCut()", throwing = "e")
    public void except(Throwable e) {
        System.out.println("异常通知:" + "程序执行异常" + e.getMessage());
    }
    /**
     * 后置(最终)通知
     */
    @After("myPointCut()")
    public void after() {
        System.out.println("最终通知:模拟释放资源");
    }
}

步骤④ 创建配置文件

在 src 目录中,创建一个名为aspectj.config的包,并在该包中创建配置文件applicationContext.xml,在配置文件中指定需要扫描的包,使注解生效。同时,需要启动基于注解的 AspectJ 支持。

applicationContext.xml的代码如下:

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframeworkframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframeworkframework.org/schema/aop"
       xmlns:context="http://www.springframeworkframework.org/schema/context"
       xsi:schemaLocation="http://www.springframeworkframework.org/schema/beans
        http://www.springframeworkframework.org/schema/beans/spring-beans.xsd
        http://www.springframeworkframework.org/schema/aop
        http://www.springframeworkframework.org/schema/aop/spring-aop.xsd
        http://www.springframeworkframework.org/schema/context
        http://www.springframeworkframework.org/schema/context/spring-context.xsd">
    <!-- 指定需要扫描的包,使注解生效 -->
    <context:component-scan base-package="aspectj"/>
    <!-- 启动基于注解的AspectJ支持 -->
    <aop:aspectj-autoproxy/>
</beans>

步骤⑤ 创建测试类

在 src 目录中,创建一个名为 aspectj.test 的包,并在该包中创建测试类 AspectJAOPTest。

java 复制代码
public class AspectJAOPTest {
    public static void main(String[] args) {
        @SuppressWarnings("resource")
        ApplicationContext appCon = 
            new ClassPathXmlApplicationContext("aspectj/config/applicationContext.xml");
//从容器中,获取增强后的目标对象
TestDao testDaoAdvice = (TestDao) appCon.getBean("testDao");
//执行方法
testDaoAdvice.save();
System.out.println("================");
testDaoAdvice.modify();
System.out.println("================");
testDaoAdvice.delete();

步骤⑥ 运行测试类

运行测试类AspectJAOPTestmain方法

事务管理(填空)

Spring 的声明式事务管理是通过 AOP 技术实现的事务管理,其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。

声明式事务管理最大的优点是不需要通过编程的方式管理事务,因而不需要在业务逻辑代码中掺杂事务处理的代码,只需相关的事务规则声明,便可以将事务规则应用到业务逻辑中。通常情况下,在开发中使用声明式事务处理,不仅因为其简单,更主要是因为这样使得纯业务代码不被污染,极大地方便了后期的代码维护。

和编程式事务管理相比,声明式事务管理唯一不足 的地方是,最细粒度只能作用到方法级别,无法做到像编程式事务管理那样可以作用到代码块级别 。但即便有这样的需求,也可以通过变通的方法进行解决,例如,可以将需要进行事务处理的代码块独立为方法。Spring 的声明式事务管理可以通过两种方式来实现,一种是基于 XML 的方式 ,另一种是基于 @Transactional 注解的方式。

2.Spring MVC

Spring MVC是什么?

MVC 思想将一个应用分成三个基本部分:Model(模型)、View(视图)和 Controller(控制器) ,让这三个部分以最低的耦合 进行协同工作 ,从而提高应用的可扩展性及可维护性

Spring MVC 是一款优秀的基于 MVC 思想的应用框架,它是 Spring 提供的一个实现了 Web MVC 设计模式的轻量级 Web 框架。

Spring MVC工作原理

Spring MVC 框架是高度可配置的,包含多种视图技术,例如 JSP 技术、Velocity、Tiles、iText 和 POI。Spring MVC 框架并不关心使用的视图技术,也不会强迫开发者只使用 JSP 技术,但本章使用的视图是 JSP。

Spring MVC 框架主要由 DispatcherServlet、处理器映射、控制器、视图解析器、视图组成,其工作原理如图 所示。

从图可总结出 Spring MVC 的工作流程如下:

  1. 客户端请求提交到 DispatcherServlet。
  2. 由 DispatcherServlet 控制器寻找一个或多个 HandlerMapping,找到处理请求的 Controller。
  3. DispatcherServlet 将请求提交到 Controller。
  4. Controller 调用业务逻辑处理后,返回 ModelAndView。
  5. DispatcherServlet 寻找一个或多个 ViewResolver 视图解析器,找到 ModelAndView 指定的视图。
  6. 视图负责将结果显示到客户端。

图中包含 4 个 Spring MVC 接口:DispatcherServlet、HandlerMapping、Controller 和 ViewResolver。

Spring MVC 所有的请求都经过 DispatcherServlet 来统一分发。DispatcherServlet 将请求分发给 Controller 之前,需要借助于 Spring MVC 提供的 HandlerMapping 定位到具体的 Controller。HandlerMapping 接口负责完成客户请求到 Controller 映射。

Controller 接口将处理用户请求,这和 Java Servlet 扮演的角色是一致的。一旦 Controller 处理完用户请求,则返回 ModelAndView 对象给 DispatcherServlet 前端控制器,ModelAndView 中包含了模型(Model)和视图(View)。从宏观角度考虑,DispatcherServlet 是整个 Web 应用的控制器;从微观角度考虑,Controller 是单个 Http 请求处理过程中的控制器,而 ModelAndView 是 Http 请求过程中返回的模型(Model)和视图(View)。

ViewResolver 接口(视图解析器)在 Web 应用中负责查找 View 对象,从而将相应结果渲染给客户。

怎么构建MVC项目?(实验)

Spring MVC 入门程序的实现过程。具体实现步骤如下:

① 创建 Web 应用 ch2_1 并导入 JAR 包

创建 Web 应用 ch2_1,导入如 2.2.1 节所述的 JAR 包。

② 在 web.xml 文件中部署 Spring MVC 核心控制器 DispatcherServlet

在开发 Spring MVC 应用时,需要在 WEB-INF 目录下创建 web.xml 文件,并在该文件中部署 DispatcherServlet,示例代码如下:

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<web-app
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
id="WebApp_ID"
version="4.0">
<!--配置springmvcDispatcherServlet-->
<servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>

上述 DispatcherServlet 的 servlet 对象 springmvc 初始化时,默认情况下,将在应用程序的 WEB-INF 目录下查找 Spring MVC 配置文件,该配置文件的命名规则是 "servletName-servlet.xml",例如 springmvc-servlet.xml。

另外,也可以将 Spring MVC 的配置文件存放在应用程序目录中的任何地方,但需要使用 servlet 的 init-param 元素加载配置文件,示例代码如下:

xml 复制代码
<!--配置 Spring MVC DispatcherServlet -->
<servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
    <param-name>contextConfigLocation</param-name>
    <!-- classpath是指到src目录查找配置文件 -->
    <param-value>classpath:config/springmvc.xml</param-value>
    <!-- 如果没有classpath,是到WebContent目录找,示例如下 -->
<!-- <param-value>/WEB-INF/spring-config/springmvc.xml</param-value> -->
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
</servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

③创建 Web 应用首页

在 ch2_1 应用的 WebContent 目录下,有个应用首页 index.jsp。index.jsp 的核心代码如下:

jsp 复制代码
<body>
    没注册的用户,请<a href="index/register">注册</a>!<br>
    已注册的用户,去<a href="index/login">登录</a>!
</body>

④ 创建 Controller 类

在 ch2_1 应用的 src 目录下,创建包 controller,并在该包中创建基于注解的名为 IndexController 的控制器类,该类中有两个处理请求方法,分别处理首页 index.jsp 中 "注册(index/register)" 和 "登录(index/login)" 超链接请求。IndexController 的代码如下:

java 复制代码
package controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
 * @Controller表示IndexController的实例是一个控制器
 * @Controller相当于@Controller("indexController")
 * 或@Controller(value = "indexController")
 */
@Controller
@RequestMapping("/index")
public class IndexController {
    @RequestMapping("/login")
    public String login() {
        /*
         * login代表逻辑视图名称,需要根据Spring MVC配置
         * 文件中internalResourceViewResolver的前缀和后缀找到对应的物理视图
         */
        return "login";
    }
    @RequestMapping("/register")
    public String register() {
        return "register";
    }
}

⑤ 创建 Spring MVC 的配置文件

在 Spring MVC 中,使用扫描机制找到应用中所有基于注解的控制器类。所以,为了让控制器类被 Spring MVC 框架扫描到,需要在配置文件中声明 spring-context,并使用<context:component-scan/>元素指定控制器类的基本包(请确保所有控制器类都在基本包及其子包中)。另外,需要在配置文件中定义 Spring MVC 的视图解析器(ViewResolver),示例代码如下:

xml 复制代码
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
      id="internalResourceViewResolver">
    <!-- 前缀 -->
    <property name="prefix" value="/WEB-INF/jsp/" />
    <!-- 后缀 -->
    <property name="suffix" value=".jsp" />
</bean>

上述视图解析器配置了前缀和后缀两个属性。因此,控制器类中视图路径仅需提供registerlogin,视图解析器将会自动添加前缀和后缀。

在 ch2_1 应用的 src 目录下,创建名为 config 的包,并在该包中创建名为 springmvc.xml 的配置文件(此时需要在 web.xml 配置文件中指定 springmvc.xml 文件的具体位置,见步骤②),其代码如下:

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframeworkframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframeworkframework.org/schema/context"
       xsi:schemaLocation="
        http://www.springframeworkframework.org/schema/beans
        http://www.springframeworkframework.org/schema/beans/spring-beans.xsd
        http://www.springframeworkframework.org/schema/context
        http://www.springframeworkframework.org/schema/context/spring-context.xsd">
    <!-- 使用扫描机制,扫描控制器类 -->
    <context:component-scan base-package="controller"/>
    <!-- 配置视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
          id="internalResourceViewResolver">
        <!-- 前缀 -->
        <property name="prefix" value="/WEB-INF/jsp/" />
        <!-- 后缀 -->
        <property name="suffix" value=".jsp" />
    </bean>
</beans>

⑥ 应用的其他页面

IndexController 控制器的register方法处理成功后,跳转到/WEB-INF/jsp/register.jsp视图;IndexController 控制器的login方法处理成功后,跳转到/WEB-INF/jsp/login.jsp视图。因此,应用的/WEB-INF/jsp目录下应有register.jsplogin.jsp页面,此两个 JSP 页面代码略。

⑦ 发布并运行 Spring MVC 应用

在 Eclipse 中第 1 次运行 Spring MVC 应用时,需要将应用发布到 Tomcat。例如,运行 ch2_1 应用时,可以右击应用名称 ch2_1,选择Run As/Run on Server,即完成发布并运行。

重定向与转发(概念)

重定向是将用户从当前处理请求定向到另一个视图(如 JSP)或处理请求,以前的请求(request)中存放的信息全部失效,并进入一个新的 request 作用域。

转发是将用户对当前的请求转发给另一个视图或处理请求,以前的 request 中存放的信息不会失效。

容器内部完成请求处理和转发动作,将目标资源发送给客户;在这里,转发的路径必须是同一个 Web 容器下的 URL,其不能转向到其他的 Web 路径上去,中间传递的是自己的容器内的 request。在客户浏览器的地址栏中显示的仍然是其第一次访问的路径,也就是说客户是感觉不到服务器做了转发的。转发行为是浏览器只做了一次访问请求。

重定向过程:客户浏览器发送 http 请求,Web 服务器接受后发送 302 状态码响应及对应新的 location 给客户浏览器,客户浏览器发现是 302 响应,就自动再发送一个新的 http 请求,请求 URL 是新的 location 地址,服务器根据此请求寻找资源并发送给客户。在这里,可以重定向到任意 URL,既然是浏览器重新发出了请求,就没有什么 request 传递的概念了。在客户浏览器的地址栏中显示的是其重定向的路径,客户可以观察到地址的变化。重定向行为是浏览器做了至少两次的访问请求。

数据绑定是什么?如何实现绑定?

数据绑定是将用户参数输入值绑定到领域模型的一种特性。在 Spring MVC 的 Controller 和 View 参数数据传递中,所有 HTTP 请求参数的类型均为字符串。如果模型需要绑定的类型为 double 或 int,则需要手动进行类型转换;而有了数据绑定后,就不再需要手动将 HTTP 请求中的 String 类型转换为模型需要的类型。数据绑定的另一个好处是,当输入验证失败时,会重新生成一个 HTML 表单,无须重新填写输入字段。在 Spring MVC 中,为了方便、高效地使用数据绑定,还需要学习表单标签库。

拦截器是什么?两种定义方式

Spring MVC 的拦截器(Interceptor)与 Java Servlet 的过滤器(Filter)类似,它主要用于拦截用户的请求并做相应的处理,通常应用在权限验证、记录请求信息的日志、判断用户是否登录等功能上。

在 Spring MVC 框架中,定义一个拦截器可以通过两种方式:①一种是通过实现HandlerInterceptor接口或继承HandlerInterceptor接口的实现类来定义;② 另一种是通过实现WebRequestInterceptor接口或继承WebRequestInterceptor接口的实现类来定义。

二.Spring Boot框架开发

1.条件注解

//添加Spring Boot自动配置原理

最终 Spring Boot 是通过加载所有(in multiple JAR files)META-INF/spring.factories 配置文件进行自动配置的。所以,@SpringBootApplication 注解通过使用 @EnableAutoConfiguration 注解自动配置的原理是:从 classpath 中搜索所有 META-INF/spring.factories 配置文件,并将其中 org.springframework.boot.autoconfigure.EnableAutoConfiguration 对应的配置项通过 Java 反射机制进行实例化,然后汇总并加载到 Spring 的 IoC 容器。

在 Spring Boot 项目的 Maven Dependencies 的 spring-boot-autoconfigure-2.4.1.jar 目录下,可以找到 META-INF/spring.factories 配置文件,该文件中定义了许多自动配置。

//条件注解

打开 spring.factories 配置文件中任意一个 AutoConfiguration,一般都可以找到条件注解。例如,打开 org.springframework.boot.autoconfigure.aop.AopAutoConfiguration 的源代码,可以看到 @ConditionalOnClass 和 @ConditionalOnProperty 等条件注解。

通过 org.springframework.boot.autoconfigure.aop.AopAutoConfiguration 的源代码可以看出,Spring Boot 的自动配置是使用 Spring 的 @Conditional 注解实现的。因此,本节将介绍相关的条件注解,并讲述如何自定义 Spring 的条件注解。

所谓 Spring 的条件注解,就是应用程序的配置类满足某些特定条件才会自动启用该配置类的配置项。Spring Boot 的条件注解位于 spring-boot-autoconfigure-2.4.1.jar 的 org.springframework.boot.autoconfigure.condition 包下,具体如表

注解名 条件实现类 条件
@ConditionalOnBean OnBeanCondition Spring 容器中存在指定的实例 Bean
@ConditionalOnClass OnClassCondition 类加载器(类路径)中存在对应的类
@ConditionalOnCloudPlatform OnCloudPlatformCondition 是否在云平台
@ConditionalOnExpression OnExpressionCondition 判断 SpEL 表达式是否成立
@ConditionalOnJava OnJavaCondition 指定 Java 版本是否符合要求
@ConditionalOnJndi OnJndiCondition 在 JNDI(Java 命名和目录接口)存在的条件下查找指定的位置
@ConditionalOnMissingBean OnBeanCondition Spring 容器中不存在指定的实例 Bean
@ConditionalOnMissingClass OnClassCondition 类加载器(类路径)中不存在对应的类
@ConditionalOnNotWebApplication OnWebApplicationCondition 当前应用程序不是 Web 程序
@ConditionalOnProperty OnPropertyCondition 应用环境中属性是否存在指定的值
@ConditionalOnResource OnResourceCondition 是否存在指定的资源文件
@ConditionalOnSingleCandidate OnBeanCondition Spring 容器中是否存在且只存在一个对应的实例 Bean
@ConditionalOnWebApplication OnWebApplicationCondition 当前应用程序是 Web 程序

2.Redis两个模板

Redis是什么?

Redis 是一个开源的使用 ANSI C 语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库,并提供多种语言的 API。它支持字符串、哈希表、列表、集合、有序集合、位图、地理空间信息等数据类型,同时也可以作为高速缓存和消息队列代理。但是,Redis 在内存中存储数据,因此,存放在 Redis 中的数据不应该大于内存容量,否则会导致操作系统性能降低。

StringRedisTemplate和ReidsTemplate

三.Vue3

1.vue是什么?

Vue是一套构建用户界面的渐进式框架。与重量级框架不同的是,Vue 采用自底向上增量开发的设计。Vue 的核心库只关注视图层,它不仅易于上手,还便于与第三方库或既有项目整合。

Vue.js 是构建 Web 界面的 JavaScript 库,提供数据驱动的组件,有简单灵活的 API,使 MVVM(Model-View-ViewModel)更简单。MVVM 模式是由 MVC 衍生而来,当 View 变化时,将自动更新到 ViewModel(视图模型),反之亦然。View 和 ViewModel 之间通过双向绑定(data-binding)建立联系。

Vue.js 可以轻松构建 SPA(Single Web Application)应用程序,通过指令扩展 HTML,通过表达式将数据绑定到 HTML,最大程度解放 DOM 操作。

2.生命周期

每个 Vue 实例在被创建时都要经过一系列的初始化过程,例如数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等,同时在这个过程中也会调用一些生命周期钩子的函数,在适当的时机执行我们的业务逻辑。

Vue 的生命周期共分 8 个阶段,即对应 8 个与 created 类似的钩子函数。

beforeCreate(创建前):在 Vue 实例初始化后,数据观测和事件配置前调用,此时 el 和 data 并未初始化,因此无法访问 methods、computed 等方法和数据。

created(创建后):Vue 实例创建后被立即调用即 HTML 加载完成前。此时,Vue 实例已完成数据观测、属性和方法的运算、watch/event 事件回调、data 数据的初始化。然而,挂载阶段还没有开始,el 属性目前不可见。这是一个常用的生命周期钩子函数,可以调用 methods 中的方法、改变 data 中的数据、获取 computed 中的计算属性等,通常在此钩子函数中对实例进行预处理。

beforeMount(载入前):挂载开始前被调用,Vue 实例已完成编译模板、把 data 里面的数据和模板生成 HTML、el 和 data 初始化,注意此时还没有挂载 HTML 到页面上。

mounted(载入后):页面加载后调用该函数,这是一个常用的生命周期钩子函数,一般是第一个业务逻辑在此钩子开始,mounted 只会执行一次。

beforeUpdate(更新前):在数据更新前被调用,发生在虚拟 DOM 重新渲染和打补丁之前,可以在该钩子中进一步更改状态,不会触发附加的重渲染过程。

updated(更新后):在由数据更改导致虚拟 DOM 重新渲染和打补丁时调用,调用时,DOM 已经更新,所以可以执行依赖于 DOM 的操作,应该避免在此期间更改状态,这可能会导致更新无限循环。

beforeUnmount(销毁前):Vue 实例销毁前调用(离开页面前调用),这是一个常用的生命周期钩子函数,一般在此时做一些重置的操作,例如清除定时器和监听的 DOM 事件。

unmounted(销毁后):在实例销毁后调用,调用后,事件监听器被移出,所有子实例也被销毁。

3.父组件向子组件传值

组件除了把模板内容复用外,更重要的是向组件传递数据。传递数据的过程就是由props实现的。在组件中,使用选项props来声明从父级组件接收的数据,props的值可以是两种,一种是字符串数组,一种是对象。现在,先介绍数组类型的用法。

【例 14-15】构造两个数组props,一个数组接收来自父级组件的数据message(实现静态传递),一个数组接收来自父级组件的数据idtitle(实现动态传递),并将它们在组件模板中渲染。代码如下:

html 复制代码
<!--父组件显示-->
<template id="parent">
  <h4>{{ message }}</h4>
  <!--使用v-bind将父组件parent的data(posts)动态传递给props,children组件
  能在parent中-->
  <children v-for="post in posts" :id="post.id" :title="post.title"></children>
  <!--将一个对象的所有属性都作为prop传入,与上面一句等价-->
  <children v-for="post in posts" v-bind="post"></children>
</template>

<!--子组件显示-->
<template id="children">
  <h4>{{ id }}: {{ title }}</h4>
</template>
<div id="message-post-demo">
  <!--静态传递字符串,父组件就是Vue当前的实例-->
  <parent message="来自父组件的消息"></parent>
</div>
<script src="js/vue.global.js"></script>
<script>
  const messageApp = Vue.createApp({})
  messageApp.component('parent', {
    data() {
      return {
        //posts是对象数组
        posts: [
          { id: 1, title: 'My journey with Vue' },
          { id: 2, title: 'Blogging with Vue' },
          { id: 3, title: 'Why Vue is so fun' }
        ]
      }
    },
    props: ['message'],//接收父组件messageApp传递的数据
    components: {//创建子组件children
      'children': {
        props: ['id', 'title'],//接收父组件parent传递的数据
        template: '#children'
      }
    },
    template: '#parent'
  })
  messageApp.mount('#message-post-demo')
</script>

注意:如果不使用 v-bind 直接传递数字、布尔值、数组及对象时,就是以字符串值传递。另外,使用 props 实现数据传递都是单向的,即父组件数据变化时,子组件中所有的 prop 将刷新为最新的值,但是反过来不行。

使用 props 实现数据传递的同时,还可以为 props 指定验证要求。一般当组件需要提供给别人使用时,最好进行数据验证。例如某个数据必须是数字类型,如果传入字符串,Vue 将在浏览器控制台中弹出警告。

为了定制 props 的验证方式,可以为 props 的值提供带有验证需求的对象,而不是字符串数组。下面通过一个验证实例讲解 props 验证。

【例 14-16】给组件的 props 提供带有验证需求的对象。具体代码如下:

html 复制代码
<template id="validate">
  <div>
    <h4>{{ num }}</h4>
    <h4>{{ strnum }}</h4>
    <h4>{{ isrequired }}</h4>
    <h4>{{ numdefault }}</h4>
    <h4>{{ objectdefault }}</h4>
    <h4>{{ myfun }}</h4>
  </div>
</template>
<div id="validate-post-demo">
    </div>
<script src="js/vue.global.js"></script>
<script>
const messageApp = Vue.createApp({})
messageApp.component('validate-post', {
  props: {
    //基础的类型检查(null 和 undefined 会通过任何类型验证)
    num: Number,
    //多个可能的类型,字符串或数字
    strnum: [String, Number],
    //必填的字符串
    isrequired: {
      type: String,
      required: true
    },
    //带有默认值的数字
    numdefault: {
      type: Number,
      default: 100
    },
    //带有默认值的对象
    objectdefault: {
      type: Object,
      //对象或数组默认值必须从一个工厂函数获取
      default: function() {
        return { message: 'hello' }
      }
    },
    //自定义验证函数
    myfun: {
      validator: function(value) {
        //这个值必须匹配下列字符串中的一个
        return ['success', 'warning', 'danger'].indexOf(value)!==-1
      }
    }
  },
  template: '#validate'
})
messageApp.mount('#validate-post-demo')
</script>

上述验证实例中,type 的类型可以是 String、Number、Boolean、Array、Object、Date、Function、Symbol 等数据类型。

4.子组件向父组件传值

可通过 props 从父组件向子组件传递数据,并且这种传递是单向的。当需要从子组件向父组件传递数据时,需要首先给子组件自定义事件并使用 $emit (事件名,要传递的数据) 方法触发事件,然后父组件使用 v-on 或 @监听子组件的事件。下面通过一个实例讲解自定义事件的使用方法。

【例 14-17】子组件触发两个事件,分别实现字体变大和变小。具体代码如下:

html 复制代码
<template id="blog">
  <!--0.1 是传递给父组件 blogApp 的数据,可以不填。然后当在父组件监听这个事件时,可以
通过$event 访问这个数据。如果事件处理函数是一个方法,那么这个数据将会作为第一个参数传入该方法
(如onEnlargeText)-->
  <h4>{{ id }}: {{ title }}</h4>
  <button @click="$emit('enlarge-text', 0.1)">变大</button>
  <button @click="$emit('ensmall-text', 0.1)">变小</button>
</template>
<div id="blog-post-demo">
  <div v-bind:style="{ fontSize: postFontSize + 'em' }">
    <!--将一个对象的所有属性作为prop传给子组件,父组件监听事件并更新postFontSize值。-->
    <!--$event 接收子组件传递过来的数据0.1-->
    <blog-post v-for="post in posts" v-bind="post" @ensmall-text="postFontSize-=$event" @enlarge-text="onEnlargeText"></blog-post>
  </div>
</div>
<script src="js/vue.global.js"></script>
<script>
const blogApp = Vue.createApp({
  data() {
    return {
      //posts是对象数组
      posts: [
        { id: 1, title: 'My journey with Vue' },
        { id: 2, title: 'Blogging with Vue' },
        { id: 3, title: 'Why Vue is so fun' }
      ],
      postFontSize: 1
    }
  },
  methods: {
    onEnlargeText(enlargeAmount) {
      this.postFontSize += enlargeAmount
    }
  }
})
blogApp.component('blog-post', { //定义子组件
  props: ['id', 'title'],//接收父组件blogApp的两个参数id和title
  template: '#blog'
})
blogApp.mount('#blog-post-demo')
</script>

注意:上述代码中,事件名推荐使用短横线命名(例如 enlarge-text),这是因为 HTML 是不区分大小写的。如果事件名为 enlargeText,@enlargeText 将变成 @enlargetext,事件 enlargeText 不可能被父组件监听到。

除了自定义事件实现子组件向父组件传值外,还可以在子组件上使用 v-model 向父组件传值,实现双向绑定。下面通过一个实例讲解在子组件上使用 v-model 向父组件传值。

【例 14-18】使用 v-model 实现子组件向父组件传值,并实现双向绑定。具体代码如下:

html 复制代码
<template id="custom">
  <!--为了让子组件正常工作,子组件内的 <input> 必须将其 value 属性绑定到一个名为
modelValue 的 props 上,在其 input 事件被触发时,将新的值通过自定义的 update:modelValue 事
件传递-->
  <input :value="modelValue" @input="$emit('update:modelValue', $event.target.value)">
</template>
<div id="vmodel-post-demo">
  <input v-model="searchText"><br>
  <custom-input v-model="searchText"></custom-input><br>
  <!--这两个子组件等价-->
  <custom-input :model-value="searchText" @update:model-value="searchText=$event"></custom-input>
</div>
<script src="js/vue.global.js"></script>
<script>
const blogApp = Vue.createApp({
  data() {
    return {
      searchText: '陈恒'
    }
  }
})
blogApp.component('custom-input', {
  props: ['modelValue'],
  template: '#custom'
})
blogApp.mount('#vmodel-post-demo')
</script>

5.组件链传值

当需要将数据从父组件传递到子组件时,可以使用 props 实现。但有时有些子组件是深层嵌套的,如果将 props 传递到整个组件链中,将很麻烦,更不可取。对于这种情况,可以使用 provide 和 inject 实现组件链传值。父组件可以作为其所有子组件的依赖项提供程序,而不管组件层次结构有多深,父组件有一个 provide 选项提供数据,子组件有一个 inject 选项使用这个数据。下面通过一个实例演示组件链传值的用法。

【例 14-19】创建 Vue 实例为祖先组件,并使用 provide 提供一个数据供其子组件 inject 使用。具体代码如下:

html 复制代码
<template id="son">
  <div>{{ todos.length }}</div>
  <!--todo-son 是 todo-list 的私有组件-->
  <todo-son></todo-son>
</template>
<template id="grandson">
  <div>
    <!--使用注入的数据-->
    {{ todoLength }}
  </div>
</template>
<div id="vmodel-post-demo">
  <!--父组件Vue实例传递数据todos给子组件todo-list-->
  <todo-list :todos="todos"></todo-list>
</div>
<script src="js/vue.global.js"></script>
<script>
const app = Vue.createApp({
  data() {
    return {
      todos: ['Feed a cat', 'Buy tickets']
    }
  },
  provide() { //祖先组件 app 提供一个数据 todoLength
    return {
      todoLength: this.todos.length
    }
  }
})
app.component('todo-list', {
  props: ['todos'],
  components: { //在父组件todo-list 中定义子组件 todo-son
    'todo-son': {
      inject: ['todoLength'], //孙组件注入数据 todoLength 供自己使用
      template: '#grandson'
    }
  },
  template: '#son'
})
app.mount('#vmodel-post-demo')
</script>

ength }}
~~~

相关推荐
m0_471199636 小时前
【vue】通俗易懂的剖析vue3的响应式原理
前端·javascript·vue.js
honder试试6 小时前
Springboot实现Clickhouse连接池的配置和接口查询
spring boot·后端·clickhouse
武子康6 小时前
大数据-185 Logstash 7 入门实战:stdin/file 采集、sincedb/start_position 机制与排障
大数据·后端·logstash
想搞艺术的程序员6 小时前
Go语言环形队列:原理剖析、编程技巧与核心优势
后端·缓存·golang
一路向前的月光6 小时前
Eltable二次封装
javascript·vue.js·elementui
资深web全栈开发6 小时前
Golang 最常用的库介绍
开发语言·后端·golang
源码获取_wx:Fegn08957 小时前
基于springboot + vue考勤管理系统
java·开发语言·vue.js·spring boot·后端·spring·课程设计
Haooog7 小时前
RabbitMQ面试题(不定时更新)
分布式·后端·面试·rabbitmq·消息中间件
m0_471199637 小时前
【vue】diff算法简介
前端·vue.js·算法