0%

Java Web后端要点简记:Java & JVM、Spring、Spring MVC与Spring Boot

后端内容仅碎片化记录本人学习过程中碰到的问题,并没有前端与数据库部分那么完整的整理。

前端:Java Web前端要点整理:HTML、CSS、JavaScript、Vue、HTTP协议与Tomcat

数据库与缓存:Java Web数据库与缓存要点整理:MySQL、Redis、Spring Data与MyBatis

Java & JVM

Java这注解跟踏马魔法一样啊。。。

以下是 Java 中类的加载、实例化流程及生命周期的详细解析,以及类加载器的核心作用:


一、类的生命周期

Java 类的生命周期分为 7 个阶段,按顺序触发:

  1. 加载(Loading)
    • 作用:将 .class 字节码文件加载到 JVM 内存。
    • 触发条件:首次使用类时(如 new、调用静态方法、访问静态字段等)。
    • 关键产物:生成一个 Class 对象(存储在方法区)。
  2. 验证(Verification)
    • 作用:检查字节码是否符合 JVM 规范(防止恶意代码)。
    • 检查内容:文件格式、元数据、字节码逻辑等。
  3. 准备(Preparation)
    • 作用:为 类变量(静态变量)分配内存,并赋予初始值(零值)。
    • 示例static int x 初始化为 0static Object obj 初始化为 null
  4. 解析(Resolution)
    • 作用:将符号引用(如类名、方法名)转换为直接引用(内存地址)。
  5. 初始化(Initialization)
    • 作用:执行类构造器 <clinit>(),为静态变量赋真实值,执行静态代码块。
    • 触发条件:首次主动使用类时(如 new、访问静态字段)。
    • 线程安全<clinit>() 由 JVM 保证同步执行。
  6. 使用(Using)
    • 作用:通过实例化、调用方法等操作使用类。
  7. 卸载(Unloading)
    • 作用:从内存中移除类的 Class 对象。
    • 触发条件:类的所有实例被回收,且类加载器被回收(通常只有自定义类加载器加载的类可卸载)。

二、类加载器(ClassLoader)的核心作用

类加载器负责 将字节码加载到 JVM,并决定类的唯一性(同一类由不同加载器加载视为不同类)。

1. 类加载器的层次结构

Java 采用 双亲委派模型(Parent Delegation Model),包含三层核心加载器:

加载器 作用 加载路径示例
Bootstrap ClassLoader 加载核心 JDK 类(如 java.lang.*),由 C++ 实现,无 Java 对象 jre/lib/rt.jar
Extension ClassLoader 加载扩展类(如 javax.* jre/lib/ext/*.jar
Application ClassLoader 加载应用类(用户编写的类,或第三方库) CLASSPATH-classpath 指定的路径

2. 双亲委派模型的工作流程

1
用户请求加载类 → 由子加载器逐级向上委派 → 父加载器尝试加载 → 若父加载器无法加载,子加载器自行加载
  • 优势
    • 避免重复加载(父加载器已加载的类,子加载器不再加载)。
    • 安全性(防止用户伪造核心类,如 java.lang.String)。

3. 自定义类加载器

通过继承 ClassLoader 并重写 findClass() 实现,常见用途: - 热部署(如 Tomcat 的 WebApp 类加载器)。 - 加载非标准来源的类(如网络、加密字节码)。 - 模块化隔离(不同模块使用独立加载器)。


三、类的实例化流程

当通过 new 关键字实例化对象时,流程如下:

  1. 检查类是否已加载
    • 若未加载,触发类加载流程(加载 → 验证 → 准备 → 解析 → 初始化)。
  2. 分配内存
    • 在堆中为对象分配连续内存空间(具体大小由类结构决定)。
  3. 初始化零值
    • 为对象的实例变量赋予默认值(如 int 初始化为 0,引用类型初始化为 null)。
  4. 设置对象头(Object Header)
    • 存储对象的元数据(如哈希码、GC 分代年龄、锁状态、类元数据指针等)。
  5. 执行构造方法 <init>()
    • 按代码顺序执行实例变量初始化代码和构造方法。
    • 示例
      1
      2
      3
      4
      5
      6
      public class MyClass {
      private int a = 10; // 实例变量初始化
      public MyClass() { // 构造方法
      a = 20;
      }
      }

四、关键问题与示例

1. 类初始化触发的场景

  • 直接 new 对象。
  • 访问类的静态变量或静态方法。
  • 反射调用(如 Class.forName("com.example.MyClass"))。
  • 子类初始化时,若父类未初始化,会先触发父类初始化。

2. 类加载器的核心作用总结

  • 定位资源:通过类加载器查找 .class 文件或资源(如 getResource())。
  • 隔离性:不同加载器加载的类互相不可见(如 Tomcat 隔离 Web 应用)。
  • 安全性:防止恶意代码替换核心类。

3. 常见异常

  • ClassNotFoundException:类加载器找不到指定类。
  • NoClassDefFoundError:类加载成功,但初始化失败(如静态代码块抛出异常)。

五、完整流程示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class MyClass {
static {
System.out.println("类初始化:静态代码块");
}

public MyClass() {
System.out.println("对象实例化:构造方法");
}

public static void main(String[] args) throws Exception {
// 触发类加载、初始化
MyClass obj = new MyClass();
}
}

输出

1
2
类初始化:静态代码块
对象实例化:构造方法


通过理解类的生命周期和类加载器的作用,可以更好地解决类冲突、资源加载、热部署等实际问题。

Spring

常用注解

Spring常用注解:

  • @RestController:等价于@Controller@ResponseBody的组合,置于Controller前

  • @ResponseBody:将返回值作为响应体,置于方法或Controller前

  • @RequestMapping(URL):路由请求,置于方法前

  • @RequestParam:多请求参数单Controller形参默认封装至数组,形参前加上该注解将封装至集合,如ArrayList

  • @RequestParam(name = "请求参数名"):将请求参数绑定到Controller形参;该注解默认属性required=true,要求必须传递目标参数

  • @Component:IOC(控制反转)注解,将当前类交给IOC容器管理、注册该类为IOC的Bean的基础注解,可通过value属性为Bean命名,默认名称为类名但首字母小写,置于类定义前

    • @Controller:标识该类属于Controller组件,专属Controller的Bean注册方式,同样可命名Bean,置于类定义前
    • @Service:标识该类属于Service组件,专属Service的Bean注册方式,同样可命名Bean,置于类定义前
    • @Repository:标识该类属于DAO组件,专属DAO的Bean注册方式,同样可命名Bean,置于类定义前,但是这个用的少,用MyBatis的就行

    Bean注解要生效必须被组件扫描注解@ComponentScan扫描到,启动类src/main/java/com.项目名/utils/SpringBootWebReqRespApplication.java已声明了默认扫描注解@SpringBootApplication,默认组件扫描范围是启动类所在包及其子包,要扫描其他包需要在启动类添加注解@ComponentScan({"包名", "包名"})指定扫描范围,这会覆盖默认扫描范围,但是按规范应该是把要扫描的类放到启动类所在包下,可以移动启动类

  • @Primary:多个Bean实现同一接口时,优先选择这个Bean,置于Bean声明注解前

  • @Qualifier("Bean名"):根据Bean名称选择特定Bean注入,置于@Autowired后、方法前

  • @Resource(name="Bean类名"):根据Bean类名选择特定Bean注入,置于方法前但这时注入无需@Autowired,注意该注解不是Spring提供的而是JDK提供的

  • @Autowired:DI(依赖注入)注解,根据声明的类型依赖注入Bean,默认不允许多个Bean实现同一接口,但可以利用@Primary@Qualifier@Resource避免,置于对象声明前

三层架构

Spring开发中通常遵循三层架构:Controller、Service与Dao。

  • Controller:控制层,接收前端请求,处理请求并响应数据,前端控制器如HttpServletRequest接收响应并传递给Controller,Controller将数据返回给前端控制器如HttpServletResponse
  • Service:业务逻辑层、服务层,处理具体的业务逻辑
  • DAO(Data Access Object):数据访问层、持久层,对数据进行操作(增删查改),常面向接口编程

IOC & DI

把所有的代码和逻辑都写在Controller当然被允许,但是不方便维护。

控制反转与依赖注入是Spring用来帮助更好地实现分层解耦的。

  • 控制反转(Inversion of Control,IOC):对象的创建控制权由程序自身转移至外部容器
  • 依赖注入(Dependency Injection,DI):容器为应用提供运行时所依赖的资源,例如Service的代码不必显式的new特定的Dao对象
  • Bean对象:IOC容器中创建、管理的对象

通过Spring的注解就可以实现。

解释:一个典型的流程是某个Controller需要某个Service对象,该Service接口被若干不同的ServiceImp实现了不同的逻辑,Spring通过控制反转把ServiceImp放进容器中,Controller从容器中通过依赖注入获取需要的对象,而不必在自身的逻辑中嵌入Service实例化代码,实现高内聚、低耦合