[Java]SpringMVC 学习笔记(动力节点王鹤王妈妈2020)(五)

简介: [Java]SpringMVC 学习笔记(动力节点王鹤王妈妈2020)(五)

springmvc.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
               http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
  <!-- 控制器包扫描 -->
  <context:component-scan base-package="cw.springmvc.controller"/>
  <!-- 视图解析器 -->
  <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/view/"/>
    <property name="suffix" value=".jsp"/>
  </bean>
  <!-- 异常处理组件扫描 -->
  <context:component-scan base-package="cw.springmvc.handler"/>
  <!-- 注解驱动器 -->
  <mvc:annotation-driven/>
</beans>

测试


拦截器

拦截器概述

  • 拦截器需要实现框架中的指定接口,HandlerInterceptor,实现这个接口的类都为拦截器
  • 拦截器的主要作用是拦截指定的用户请求,并进行相应的预处理与后处理。
  • 拦截器与过滤器类似,过滤器是用来过滤器请求参数,设置编码字符集等工作;拦截器是拦截用户的请求,做请求做判断处理的。
  • 拦截器的拦截的时间点在“处理器映射器根据用户提交的请求映射出了所要执行的处理器类,并且也找到了要执行该处理器类的处理器适配器,在处理器适配器执行处理器之前”。
  • 拦截器是全局的,可以对多个Controller做拦截。 一个项目中可以有0个或多个拦截器, 他们在一起拦截用户的请求。
  • 拦截器常用在:用户登录处理,权限检查, 记录日志。
  • 拦截器的使用步骤:
  • 1.定义类实现HandlerInterceptor接口
  • 2.在springmvc配置文件中,声明拦截器, 让框架知道拦截器的存在。
  • 拦截器的执行时间:
  • 1)在请求处理之前, 也就是controller类中的方法执行之前先被拦截。
  • 2)在控制器方法执行之后也会执行拦截器。
  • 3)在请求处理完成后也会执行拦截器。

创建拦截器类

  • 自定义拦截器,需要实现HandlerInterceptor接口,然后根据需求实现HandlerInterceptor接口中的三个方法

拦截器类方法讲解

package cw.springmvc.handler;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * ClassName: MyInterceptor
 * Package: cw.springmvc.handler
 * Description:
 * 拦截器类,拦截用户的请求
 *
 * @Author tcw
 * @Create 2023-06-05 23:40
 * @Version 1.0
 */
public class MyInterceptor implements HandlerInterceptor{
    /**
     * preHandle 预处理方法
     * 该方法在控制器方法执行前执行,用户的请求会先到达此方法
     * 在该方法中可以获取请求的信息,验证请求信息是否符合要求
     * 可以验证用户是否登录, 验证用户是否有权限访问某个连接地址(url)。
     * 如果验证失败,可以截断请求,请求不能被处理;如果验证成功,可以放行请求,此时控制器方法才能执行。
     * 
     * 重要:是整个项目的入口,门户。 
     * 当preHandle返回true 请求可以被处理。
     * preHandle返回false,请求到此方法就截止。
     *
     * @param request
     * @param response
     * @param handler 被拦截的控制器对象
     * @return 布尔值
     *         true: 请求是通过了拦截器的验证,可以执行处理器方法。
     *         false: 请求没有通过拦截器的验证,请求到达拦截器就截止了。 请求没有被处理
     *                后面的方法:处理器方法、后处理方法、afterCompletion都不执行
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return HandlerInterceptor.super.preHandle(request, response, handler);
    }
    /**
     * postHandle:后处理方法。
     * 在处理器方法之后执行的(MyController.doSome())
     * 能够获取到处理器方法的返回值ModelAndView,
     * 可以修改ModelAndView中的数据和视图,可以影响到最后的执行结果。
     * 主要是对原来的执行结果做二次修正
     *
     * @param request
     * @param response
     * @param handler 被拦截的处理器对象 Controller
     * @param modelAndView 处理器方法的返回值
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }
    /**
     * afterCompletion:最后执行的方法
     * 在请求处理完成后执行的。
     * 框架中规定是当你的视图处理完成后,对视图执行了forward。就认为请求处理完成。
     * 一般做资源回收工作的, 程序请求过程中创建了一些对象,在这里可以删除,把占用的内存回收。
     *
     * @param request
     * @param response
     * @param handler 被拦截器的处理器对象
     * @param ex 程序中发生的异常
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

拦截器图示

SpringMVC 配置文件中配置拦截器

<!-- 配置拦截器,拦截器可以配置0到多个 -->
<mvc:interceptors>
  <!-- 配置第一个拦截器 -->
  <mvc:interceptor>
    <!-- 
      指定拦截器拦截的URI地址
      可以使用通配符 
        ** 表示任意的字符,文件或者多级目录和目录中的文件
        /user/** 以user开头的请求地址都会被该拦截器拦截
        /** 该拦截器会拦截所有的请求
    -->
    <mvc:mapping path="/**"/>
    <!-- 需要进行拦截的地址,对应的拦截器对象 -->
    <bean class="cw.springmvc.handler.MyInterceptor"/>
  </mvc:interceptor>
</mvc:interceptors>

拦截器中方法编写

package cw.springmvc.handler;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * ClassName: MyInterceptor
 * Package: cw.springmvc.handler
 * Description:
 * 拦截器类,拦截用户的请求
 *
 * @Author tcw
 * @Create 2023-06-05 23:40
 * @Version 1.0
 */
public class MyInterceptor implements HandlerInterceptor{
    private long btime = 0; // 用于统计请求的处理时间
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("拦截器preHandle预处理方法执行...");
        btime = System.currentTimeMillis(); // 记录请求开始处理时间
        // 返回真,后面的方法才能执行
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("拦截器postHandle后处理方法执行...");
        // 原来的doSome执行结果需要调整,进行modelAndView修改。
        if( modelAndView != null){
            //修改数据
            // modelAndView.addObject("mydate",new Date());
            //修改视图
            modelAndView.setViewName("other");
        }
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("拦截器afterCompletion:最后执行的方法执行...");
        long etime = System.currentTimeMillis(); // 请求处理结束时间
        System.out.println("计算从preHandle到请求处理完成的时间:"+(etime - btime ));
    }
}

控制器

@Controller
public class MyController {
    @RequestMapping(value = "/some.do")
    public ModelAndView doSome(String name, Integer age) throws MyUserException {
        System.out.println("控制器方法执行...");
        ModelAndView mv = new ModelAndView();
        mv.addObject("name", name);
        mv.addObject("age", age);
        mv.setViewName("show");
        return mv;
    }
}

测试


拦截器执行流程图示

多个拦截器的执行顺序

  • 拦截器在配置文件中,先声明的会先执行,后声明的后执行,声明的若干个拦截器在框架中是使用ArrayList集合进行保存的,是按照声明的先后顺序放入到ArrayList集合中

多个拦截器代码实现

  • 拦截器1
package cw.springmvc.handler;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * ClassName: MyInterceptor
 * Package: cw.springmvc.handler
 * Description:
 * 拦截器类,拦截用户的请求
 *
 * @Author tcw
 * @Create 2023-06-05 23:40
 * @Version 1.0
 */
public class MyInterceptor implements HandlerInterceptor{
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("拦截器-1-preHandle预处理方法执行...");
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("拦截器-1-postHandle后处理方法执行...");
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("拦截器-1-afterCompletion:最后执行的方法执行...");
    }
}
  • 拦截器2
package cw.springmvc.handler;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * ClassName: MyInterceptor
 * Package: cw.springmvc.handler
 * Description:
 * 拦截器类,拦截用户的请求
 *
 * @Author tcw
 * @Create 2023-06-05 23:40
 * @Version 1.0
 */
public class MyInterceptor2 implements HandlerInterceptor{
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("拦截器-2-preHandle预处理方法执行...");
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("拦截器-2-postHandle后处理方法执行...");
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("拦截器-2-afterCompletion:最后执行的方法执行...");
    }
}
  • 拦截器配置
<!-- 配置拦截器,拦截器可以配置0到多个 -->
<mvc:interceptors>
  <!-- 配置第一个拦截器 -->
  <mvc:interceptor>
    <mvc:mapping path="/**"/>
    <bean class="cw.springmvc.handler.MyInterceptor"/>
  </mvc:interceptor>
  <!-- 配置第二个拦截器 -->
  <mvc:interceptor>
    <mvc:mapping path="/**"/>
    <bean class="cw.springmvc.handler.MyInterceptor2"/>
  </mvc:interceptor>
</mvc:interceptors>

多个拦截器执行顺序图示


不同拦截器 preHandle 不同返回值分析

  • 只有当拦截器的方法preHandle返回true才可以向后执行,如果其中一个拦截器的preHandle方法返回值为false,那么请求会被拦截,不会被控制器方法处理,即只要有拦截器的preHandle方法返回值为false那么处理器方法就不会执行
  • 拦截器-1-preHandle返回true,拦截器-2-preHandle返回false
  • 拦截器-1-preHandle返回false,拦截器-2-preHandle返回true
  • 只要有一个 preHandle()方法返回 false,则上部的执行链将被断开,其后续的处理器方法与 postHandle()方法将无法执行。但,无论执行链执行情况怎样,只要方法栈中有方法,即执行链中只要有 preHandle()方法返回 true,就会执行方法栈中preHandle()方法返回 true相对应的afterCompletion()方法。最终都会给出响应。

拦截器和过滤器的区别

  1. 过滤器是servlet规范中的对象,拦截器是框架中自己定义的对象,拦截器局限于框架之中,离开了SpringMVC框架,拦截器就不能使用了
  2. 过滤器实现Filter接口的对象, 拦截器是实现HandlerInterceptor
  3. 过滤器是用来设置request,response的参数,属性的,侧重对数据过滤的。拦截器是用来验证请求的,能截断请求。
  4. 过滤器是在拦截器之前先执行的。
  5. 过滤器是tomcat服务器创建的对象。拦截器是springmvc容器中创建的对象
  6. 过滤器是一个执行时间点。拦截器有三个执行时间点
  7. 过滤器可以处理jsp,js,html等等。拦截器是侧重拦截对Controller的请求, 如果你的请求不能被DispatcherServlet接收, 这个请求不会执行拦截器内容
  8. 拦截器拦截普通类方法(控制器方法)执行,过滤器过滤servlet请求响应
  1. 控制器其实是普通的Java类,框架给了控制器能够处理请求的能力

拦截器案例:登录验证

  • 注意,前端控制器DispatcherServlet的路径配置,不同的路径配置将会影响到对jsp页面的请求是否会交给DispatcherServlet处理,/DispatcherServlet不会处理jsp页面的请求,/*DispatcherServlet会处理jsp页面的请求,*.doDispatcherServlet不会处理jsp页面的请求

index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="./login" method="post">
    username:<input type="text" name="username"><br/>
    <input type="submit" name="submit"><br/>
</form>
</body>
</html>

controller

@Controller
public class MyController {
    @RequestMapping(value = "/login")
    public ModelAndView doSome(String name, Integer age) throws MyUserException {
        System.out.println("控制器方法执行...");
        ModelAndView mv = new ModelAndView();
        mv.setViewName("login");
        return mv;
    }
}

拦截器

public class MyInterceptor implements HandlerInterceptor{
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 获取登录的用户名
        String username = request.getParameter("username");
        System.out.println(username);
        // 判断会话作用域中是否有用户名,判断是否登录过
        Object username1 = request.getSession().getAttribute("username");
        System.out.println(username1);
        // 验证输入的用户名
        if (username == null || username.length() == 0 || !"admin".equals(username)) {
            // 验证是否登录过
            if (username1 == null || !"admin".equals(username1)) {
                // 验证失败
                request.getRequestDispatcher("./tips.jsp").forward(request, response);
                return false;
            }
        }
        // 验证成功
        // 将用户名添加到会话作用域
        request.getSession().setAttribute("username", username);
        return true;
    }
}

jsp

  • login.jsp
<%--
  Created by IntelliJ IDEA.
  User: cw
  Date: 2023-06-06
  Time: 10:00
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>模拟登录</h1>
<p>用户【<%= session.getAttribute("username") %>】登录成功</p>
<a href="./logout.jsp">退出登录</a>
</body>
</html>
  • logout.jsp
<%--
  Created by IntelliJ IDEA.
  User: cw
  Date: 2023-06-06
  Time: 10:03
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<p>【<%= session.getAttribute("username") %>】退出登录</p>
</body>
</html>
<%
    // 删除信息
    session.removeAttribute("username");
%>
  • tips.jsp
<%--
  Created by IntelliJ IDEA.
  User: cw
  Date: 2023-06-06
  Time: 10:07
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>登录失败</h1>
</body>
</html>

DispatcherServlet 配置

<servlet>
  <servlet-name>springmvc</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath: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>

springmvc.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
               http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
  <!-- 控制器包扫描 -->
  <context:component-scan base-package="cw.springmvc.controller"/>
  <!-- 视图解析器 -->
  <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/"/>
    <property name="suffix" value=".jsp"/>
  </bean>
  <!-- 配置拦截器,拦截器可以配置0到多个 -->
  <mvc:interceptors>
    <!-- 配置第一个拦截器 -->
    <mvc:interceptor>
      <mvc:mapping path="/**"/>
      <bean class="cw.springmvc.handler.MyInterceptor"/>
    </mvc:interceptor>
  </mvc:interceptors>
</beans>

运行测试

  • 再直接浏览器访问:http://localhost:8080/springmvc/login

SpringMVC 执行流程

  • 浏览器发送请求,请求会最先被Tomcat接收,Tomcat将和DispatcherServlet对应路径相匹配的请求交给DispatcherServlet进行处理
  • DispatcherServlet调用处理器映射器,处理器映射器根据请求,从SpringMVC容器中获取处理器对象
  • 相当于执行:SpringMVC容器对象.getBean("beanName")
  • 处理器映射器:SpringMVC中的一种对象,框架把实现了HandlerMapping接口的类都叫映射器
  • 处理器映射器对象有多个
  • 处理器映射器接口HandlerMapping有很多子接口和子类,因为在没有注解之前需要通过实现接口来创建控制器,控制器种类一多,接口也就多了
  • HandlerExecutionChain mappedHandler = this.getHandler(processedRequest);
  • 框架会把找到的处理器对象放到处理器执行链的类中进行保存,将处理器执行链给DispatcherServlet
public class HandlerExecutionChain {
    private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);
    private final Object handler; // 保存处理器对象
    private final List<HandlerInterceptor> interceptorList; // 项目中所有的拦截器
    private int interceptorIndex;
}
  • DispatcherServlet将处理器执行链中的处理器对象交给处理器适配器,处理器适配器调用执行处理器的方法,将处理器方法的返回值ModelAndView返回给中央调度器
  • 处理器适配器:SpringMVC框架中的对象,需要实现HandlerAdapter
  • 处理器适配器对象有多个
  • 处理器适配器接口HandlerAdapter有很多子接口和子类,因为适配器的种类多
  • HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
  • mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
  • DispatcherServlet接收到处理器适配器的返回结果ModelAndView,调用视图解析器,将ModelAndView交给视图解析器,根据使用解析器的配置,组成视图的完整路径,并创建View对象,将视图对象交给DispatcherServlet
  • 视图解析器需要实现ViewResoler接口
  • 视图解析器可以存在多个
  • View是一个接口,表示视图,在框架中,jsp、html是使用View和其实现类表示
  • mv.setView(new RedirectView("/a.jsp"))mv.setViewName("a")) 效果差不多,只不过前者是显示创建和使用视图对象
  • InternalResourceView表示jsp文件的视图类,视图解析器会创建InternalResourceView对象,这对象中有相应的jsp文件的路径url
  • DispatcherServlet调用视图View,调用视图View对象的方法将Model中的数据放到request作用域中,视图将数据放到视图页面中,DispatcherServlet最后将视图响应给浏览器,向客户端呈现处理结果
  • 使用中央调度器按照处理流程调用相应的对象在不同阶段进行处理,有利于解耦合,其中某个对象进行了修改,不会影响其他对象的正常执行
相关文章
|
2月前
|
分布式计算 Java MaxCompute
ODPS MR节点跑graph连通分量计算代码报错java heap space如何解决
任务启动命令:jar -resources odps-graph-connect-family-2.0-SNAPSHOT.jar -classpath ./odps-graph-connect-family-2.0-SNAPSHOT.jar ConnectFamily 若是设置参数该如何设置
|
2月前
|
Java 数据库连接 API
Spring 框架的介绍(Java EE 学习笔记02)
Spring是一个由Rod Johnson开发的轻量级Java SE/EE一站式开源框架,旨在解决Java EE应用中的多种问题。它采用非侵入式设计,通过IoC和AOP技术简化了Java应用的开发流程,降低了组件间的耦合度,支持事务管理和多种框架的无缝集成,极大提升了开发效率和代码质量。Spring 5引入了响应式编程等新特性,进一步增强了框架的功能性和灵活性。
54 0
|
3月前
|
分布式计算 资源调度 Hadoop
大数据-01-基础环境搭建 超详细 Hadoop Java 环境变量 3节点云服务器 2C4G XML 集群配置 HDFS Yarn MapRedece
大数据-01-基础环境搭建 超详细 Hadoop Java 环境变量 3节点云服务器 2C4G XML 集群配置 HDFS Yarn MapRedece
100 4
|
3月前
|
分布式计算 Java Hadoop
Hadoop-30 ZooKeeper集群 JavaAPI 客户端 POM Java操作ZK 监听节点 监听数据变化 创建节点 删除节点
Hadoop-30 ZooKeeper集群 JavaAPI 客户端 POM Java操作ZK 监听节点 监听数据变化 创建节点 删除节点
76 1
|
4月前
|
缓存 前端开发 Java
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
Soring Boot的起步依赖、启动流程、自动装配、常用的注解、Spring MVC的执行流程、对MVC的理解、RestFull风格、为什么service层要写接口、MyBatis的缓存机制、$和#有什么区别、resultType和resultMap区别、cookie和session的区别是什么?session的工作原理
|
4月前
|
存储 安全 Java
Java修仙之路,十万字吐血整理全网最完整Java学习笔记(进阶篇)
本文是Java基础的进阶篇,对异常、集合、泛型、Java8新特性、I/O流等知识进行深入浅出的介绍,并附有对应的代码示例,重要的地方带有对性能、底层原理、源码的剖析。适合Java初学者。
Java修仙之路,十万字吐血整理全网最完整Java学习笔记(进阶篇)
|
3月前
|
Java 数据安全/隐私保护
java学习笔记(基础习题)
java学习笔记(基础习题)
50 0
|
3月前
|
Java 程序员 开发工具
java学习笔记
java学习笔记
50 0
|
4月前
|
JSON 前端开发 Java
【Java笔记+踩坑】SpringMVC基础
springmvc简介、入门案例、bean加载控制、PostMan工具的使用、普通和JSON和日期格式请求参数传递、响应JSON或jsp或文本、Rest风格
【Java笔记+踩坑】SpringMVC基础
|
4月前
|
存储 安全 Java
Java修仙之路,十万字吐血整理全网最完整Java学习笔记(高级篇)
本文是“Java学习路线”中Java基础知识的高级篇,主要对多线程和反射进行了深入浅出的介绍,在多线程部分,详细介绍了线程的概念、生命周期、多线程的线程安全、线程通信、线程同步,并对synchronized和Lock锁;反射部分对反射的特性、功能、优缺点、适用场景等进行了介绍。