车子的个人博客

正确的事情通常都是优雅的

类加载过程详解

类的生命周期

加载 → 验证 → 准备 → 解析 → 初始化 → 使用 → 卸载

加载阶段

  1. 通过类全名获取类的二进制字节流
  2. 将字节流转化为方法区数据结构
  3. 在堆中生成java.lang.Class对象

验证阶段

  • 文件格式验证
  • 元数据验证
  • 字节码验证
  • 符号引用验证

准备阶段

  • 分配内存
  • 初始化零值
  • 设置类变量初始值
1
2
// 准备阶段后 public static int value = 123; value = 0
// 初始化阶段后 value = 123

解析阶段

  • 将符号引用转为直接引用
  • 类或接口解析
  • 字段解析
  • 方法解析

初始化阶段

  • 执行()方法
  • 赋值static变量
  • 执行static代码块
1
2
3
4
5
public class Init {
static {
System.out.println("初始化");
}
}

类加载器

  • 启动类加载器(Bootstrap)
  • 扩展类加载器(Extension)
  • 应用类加载器(App)

总结

类加载是JVM运行时的基础,理解加载过程有助于解决类冲突和优化启动性能。

JVM内存划分详解

JVM内存区域

程序计数器

  • 记录当前线程执行的字节码行号
  • 线程私有,各线程独立计数
  • 唯一不会OOM的区域

虚拟机栈

  • 描述Java方法执行的内存模型
  • 每个方法创建一个栈帧
  • 包含:局部变量表、操作数栈、动态链接、方法返回地址
1
2
3
4
// 栈深度过深会抛出StackOverflowError
public void deepRecursion() {
deepRecursion();
}

本地方法栈

  • 为Native方法服务
  • 与虚拟机栈类似

  • 最大内存区域
  • 存放对象实例
  • 垃圾回收主要区域
  • 分代:新生代、老年代

方法区

  • 存储类信息、静态变量、常量
  • JVM规范中的”永久代”
  • JDK 8后用元空间替代

内存分配

  • 对象优先在Eden区分配
  • 大对象直接进入老年代
  • 长期存活对象进入老年代

总结

理解JVM内存划分是调优基础,不同区域有不同的存储内容和垃圾回收策略。

Spring全家桶笔记十一:配置管理与消息队列

配置管理

Spring Cloud Config

服务端配置

1
2
3
4
5
6
7
8
9
10
11
spring:
application:
name: config-server
cloud:
config:
server:
git:
uri: https://github.com/your-repo/config-repo
username: your-username
default-label: master
search-paths: '{application}'
1
2
3
@EnableConfigServer
@SpringBootApplication
public class ConfigServerApplication {}

客户端配置

1
2
3
4
5
6
7
spring:
cloud:
config:
uri: http://localhost:8888
profile: dev
label: master
name: application

动态刷新

1
2
3
4
5
6
7
@RefreshScope
@RestController
public class TestController {

@Value("${custom.property}")
private String property;
}

触发刷新:POST /actuator/refresh

Nacos配置中心

基本使用

1
2
3
4
5
6
7
8
9
10
spring:
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
config:
server-addr: 127.0.0.1:8848
file-extension: yaml
namespace: dev
group: DEFAULT_GROUP

配置优先级

  • 动态配置 > 本地配置
  • 规则:服务名+环境 > 服务名 > 默认

共享配置

1
2
3
4
5
6
7
8
spring:
cloud:
nacos:
config:
shared-configs:
- data-id: common.yaml
group: DEFAULT_GROUP
refresh: true

消息队列概述

为什么要用消息队列

  • 解耦:生产者和消费者解耦
  • 异步:提高系统响应速度
  • 削峰:应对高并发流量
  • 日志处理:日志收集与分析

常见消息队列

  • ActiveMQ:老牌 MQ,功能全面
  • RabbitMQ:功能强大,生态丰富
  • Kafka:高性能,日志系统常用
  • RocketMQ:阿里巴巴开源,Java 友好

RabbitMQ

核心概念

  • Producer:生产者
  • Consumer:消费者
  • Exchange:交换机
  • Queue:队列
  • Binding:绑定

Exchange 类型

类型 说明
Direct 完全匹配
Fanout 广播
Topic 模式匹配
Headers 属性匹配

Spring AMQP 集成

1
2
3
4
5
6
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest

生产者

1
2
3
4
5
6
7
8
9
10
@Component
public class MessageProducer {

@Autowired
private RabbitTemplate rabbitTemplate;

public void send(String message) {
rabbitTemplate.convertAndSend("exchange", "routing.key", message);
}
}

消费者

1
2
3
4
5
6
7
8
@Component
public class MessageConsumer {

@RabbitListener(queues = "queue.name")
public void handleMessage(String message) {
System.out.println("Received: " + message);
}
}

消息确认

1
2
3
4
5
6
7
8
9
@RabbitListener(queues = "queue.name")
public void handleMessage(String message, Message message2, Channel channel) {
try {
// 处理消息
channel.basicAck(message2.getMessageProperties().getDeliveryTag(), false);
} catch (Exception e) {
channel.basicNack(message2.getMessageProperties().getDeliveryTag(), false, true);
}
}

Kafka

核心概念

  • Topic:消息主题
  • Partition:分区
  • Replication:副本
  • Consumer Group:消费者组
  • Offset:偏移量

Spring Kafka 集成

1
2
3
4
5
6
7
8
9
10
spring:
kafka:
bootstrap-servers: localhost:9092
producer:
key-serializer: org.apache.kafka.common.serialization.StringSerializer
value-serializer: org.apache.kafka.common.serialization.StringSerializer
consumer:
group-id: my-group
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
value-deserializer: org.apache.kafka.common.serialization.StringDeserializer

生产者

1
2
3
4
5
6
@Autowired
private KafkaTemplate<String, String> kafkaTemplate;

public void send(String topic, String message) {
kafkaTemplate.send(topic, message);
}

消费者

1
2
3
4
@KafkaListener(topics = "my-topic", groupId = "my-group")
public void listen(ConsumerRecord<String, String> record) {
System.out.println("Received: " + record.value());
}

消息顺序性

  • 单分区保证顺序
  • 同一 key 的消息发送到同一分区

消息队列最佳实践

1. 消息可靠性

  • 消息持久化
  • 消费者确认机制
  • 死信队列处理

2. 消息幂等性

  • 唯一标识 + 去重表
  • 乐观锁/版本号

3. 消息顺序性

  • 同一分区
  • 消息分区key

4. 消息积压

  • 消费者扩容
  • 临时队列消费

总结

配置管理和消息队列是微服务架构的重要组成部分。配置中心实现配置的集中管理和动态刷新,消息队列实现系统间的解耦和异步通信。在实际应用中,需要根据业务场景选择合适的方案。

Spring全家桶笔记十:服务调用与负载均衡

服务调用的演进

  1. 传统方式:直接通过HTTP或RPC调用
  2. 服务注册发现:Eureka、Nacos、Consul
  3. 负载均衡:Ribbon、LoadBalancer
  4. 服务网格:Istio、Envoy

RestTemplate服务调用

基本使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@RestTemplate
@LoadBalanced // 开启负载均衡
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}

@Service
public class OrderService {

@Autowired
private RestTemplate restTemplate;

public Order getOrder(Long id) {
// 通过服务名调用
String url = "http://user-service/user/" + id;
return restTemplate.getForObject(url, Order.class);
}
}

拦截器实现认证

1
2
3
4
5
6
7
8
9
10
11
12
public class AuthInterceptor implements ClientHttpRequestInterceptor {

@Override
public ClientHttpResponse intercept(
HttpRequest request,
byte[] body,
ClientHttpRequestExecution execution) throws IOException {

request.getHeaders().add("Authorization", "Bearer token");
return execution.execute(request, body);
}
}

Feign服务调用

快速开始

1
2
3
4
5
6
7
8
9
@FeignClient(name = "user-service")
public interface UserClient {

@GetMapping("/user/{id}")
User getUserById(@PathVariable("id") Long id);

@GetMapping("/user/list")
List<User> getUsers(@RequestParam("ids") List<Long> ids);
}

配置与优化

1
2
3
4
5
6
7
@FeignClient(
name = "user-service",
url = "http://localhost:8080",
configuration = FeignConfig.class,
fallback = UserClientFallback.class
)
public interface UserClient {}
1
2
3
4
5
6
7
8
9
10
11
12
public class FeignConfig {

@Bean
public Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}

@Bean
public RequestInterceptor requestInterceptor() {
return template -> template.header("X-Request-Id", UUID.randomUUID().toString());
}
}

日志配置

1
2
3
logging:
level:
com.example.client: DEBUG

负载均衡策略

常见策略

策略 说明
RoundRobinRule 轮询(默认)
RandomRule 随机
RetryRule 重试
WeightedResponseTimeRule 响应时间加权
BestAvailableRule 最空闲服务器
AvailabilityFilteringRule 可用性过滤

自定义策略

1
2
3
4
5
6
7
8
@Configuration
public class MyRuleConfig {

@Bean
public IRule myRule() {
return new MyCustomRule();
}
}

负载均衡原理

  1. 服务注册:提供者向注册中心注册
  2. 服务发现:消费者从注册中心获取服务列表
  3. 负载均衡:根据策略选择服务实例
  4. 服务调用:发起HTTP请求

Nacos服务发现

服务注册

1
2
3
4
5
6
7
spring:
application:
name: order-service
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848

服务消费者

1
2
3
4
5
@LoadBalanced
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}

服务元数据

1
2
3
4
5
6
7
spring:
cloud:
nacos:
discovery:
metadata:
version: v1
weight: 1.0

实际应用建议

  1. 优先使用Feign,代码更简洁
  2. 合理设置超时时间
  3. 配置合理的负载均衡策略
  4. 做好服务降级和熔断
  5. 使用Nacos进行服务治理

总结

Spring Cloud提供了完整的服务调用解决方案,从服务发现到负载均衡,配合Hystrix/Resilience4j实现服务容错,构建稳定的微服务架构。

JDK参数配置详解

内存参数

1
2
3
4
5
-Xms256m          # 初始堆大小
-Xmx512m # 最大堆大小
-Xss512k # 栈大小
-Xmn256m # 年轻代大小
-XX:MetaspaceSize=128m # 元空间大小

垃圾回收参数

1
2
3
4
5
-XX:+UseSerialGC       # Serial收集器
-XX:+UseParallelGC # Parallel收集器
-XX:+UseConcMarkSweepGC # CMS收集器
-XX:+UseG1GC # G1收集器
-XX:MaxGCPauseMillis=200 # 最大GC停顿时间

调试参数

1
2
3
-XX:+PrintGCDetails    # 打印GC详情
-Xloggc:gc.log # GC日志文件
-XX:+HeapDumpOnOutOfMemoryError # OOM时堆 dumps

常用配置

1
2
3
4
5
# 性能优化
-server
-Xms4g -Xmx4g
-XX:+UseG1GC
-XX:MaxGCPauseMillis=100

总结

合理配置JVM参数能显著提升应用性能,需要根据实际情况调整。

MySQL索引知识点总结

什么是索引

索引是数据库中用于加速数据检索的数据结构,类似于书的目录。合理的索引可以大幅提升查询性能。

索引的分类

按数据结构

  • B+Tree索引:MySQL默认索引类型,适用于范围查询
  • Hash索引:适用于等值查询,Memory引擎支持
  • 全文索引:适用于文本搜索
  • R-Tree索引:适用于空间数据

按存储方式

  • 聚簇索引:数据与索引存储在一起,每个表只能有一个
  • 非聚簇索引:索引与数据分离,通过指针关联

按字段特性

  • 主键索引:不允许重复,不允许为空
  • 唯一索引:允许NULL,但不允许重复
  • 普通索引:允许重复
  • 前缀索引:索引字段前缀部分

索引的优缺点

优点

  • 加快数据检索速度
  • 减少IO操作
  • 加速表连接和排序

缺点

  • 占用磁盘空间
  • 增加写操作的开销(INSERT/UPDATE/DELETE)
  • 过多索引会降低维护效率

何时创建索引

  • WHERE子句中频繁使用的字段
  • JOIN操作的连接字段
  • ORDER BY和GROUP BY的字段
  • 区分度高的字段

何时避免使用索引

  • 数据量少的表
  • 频繁更新的字段
  • 区分度低的字段(如性别、状态)
  • 不需要查询的字段

索引使用技巧

1. 覆盖索引

查询的列都在索引中,无需回表

1
SELECT id, name FROM user WHERE name = '张三';

2. 最左前缀原则

复合索引遵循最左前缀,跳跃会导致索引失效

1
2
3
-- 索引 (a, b, c)
-- 可以使用: a / a,b / a,b,c
-- 不可使用: b / b,c / c

3. 索引下推

在索引层面过滤数据,减少回表次数

索引失效的常见情况

  1. 使用函数或运算
  2. 类型转换
  3. 使用OR连接(部分情况)
  4. 模糊查询以%开头
  5. 隐式类型转换

性能优化建议

  1. 分析慢查询日志
  2. 使用EXPLAIN分析执行计划
  3. 避免SELECT *
  4. 控制索引数量(建议不超过5个)
  5. 定期维护碎片

总结

索引是优化数据库查询的关键技术。需要根据实际业务场景合理设计索引,同时注意索引带来的负面影响。

Spring MVC默认配置失效问题

问题

自定义配置后,默认配置不生效。

原因

自定义配置会覆盖默认配置。

解决

1
2
3
4
5
6
7
8
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

@Override
public void addViewControllers(ViewControllerRegistry registry) {
// 覆盖默认配置
}
}

总结

了解配置覆盖机制,合理规划配置。

Spring全家桶笔记九:Web层与RESTful

Spring MVC概述

Spring MVC是Spring框架的Web层模块,基于Servlet API构建,实现了MVC设计模式。

请求处理流程

  1. 请求 → DispatcherServlet
  2. HandlerMapping查找处理器
  3. HandlerAdapter执行处理器
  4. Controller处理业务逻辑
  5. ViewResolver解析视图
  6. 渲染视图 → 响应

核心组件

DispatcherServlet

前端控制器,统一处理请求和响应。

1
2
3
4
5
6
7
8
9
10
// web.xml配置
<servlet>
<servlet-name>dispatcher</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>

@Controller vs @RestController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 返回视图
@Controller
public class UserController {
@GetMapping("/user/{id}")
public String getUser(Model model) {
model.addAttribute("user", userService.findById(id));
return "userDetail";
}
}

// 返回JSON
@RestController
public class UserController {
@GetMapping("/user/{id}")
public User getUser(@PathVariable Long id) {
return userService.findById(id);
}
}

请求参数绑定

路径参数

1
2
3
4
@GetMapping("/user/{id}")
public User getUser(@PathVariable Long id) {
return userService.findById(id);
}

请求参数

1
2
3
4
5
6
7
8
@GetMapping("/user/list")
public List<User> list(
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int size,
@RequestParam(required = false) String name
) {
return userService.list(page, size, name);
}

请求体

1
2
3
4
@PostMapping("/user")
public User create(@RequestBody User user) {
return userService.save(user);
}

表单提交

1
2
3
4
5
6
7
8
@PostMapping("/user/save")
public String save(@ModelAttribute User user, BindingResult result) {
if (result.hasErrors()) {
return "userForm";
}
userService.save(user);
return "redirect:/user/list";
}

RESTful API设计

规范

方法 用途 示例
GET 查询 GET /users
POST 创建 POST /users
PUT 更新 PUT /users/1
DELETE 删除 DELETE /users/1

响应结构

1
2
3
4
5
6
7
8
9
10
11
12
13
public class ApiResponse<T> {
private int code;
private String message;
private T data;

public static <T> ApiResponse<T> success(T data) {
return new ApiResponse<>(200, "success", data);
}

public static <T> ApiResponse<T> error(String message) {
return new ApiResponse<>(500, message, null);
}
}

统一异常处理

1
2
3
4
5
6
7
8
9
10
11
12
13
@RestControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler(BusinessException.class)
public ApiResponse<Void> handleBusinessException(BusinessException e) {
return ApiResponse.error(e.getMessage());
}

@ExceptionHandler(Exception.class)
public ApiResponse<Void> handleException(Exception e) {
return ApiResponse.error("系统错误");
}
}

文件上传

1
2
3
4
5
6
7
8
9
@PostMapping("/upload")
public ApiResponse<String> upload(@RequestParam("file") MultipartFile file) {
if (file.isEmpty()) {
return ApiResponse.error("文件为空");
}
String filename = file.getOriginalFilename();
// 保存文件
return ApiResponse.success(filename);
}

跨域处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@CrossOrigin(origins = "*")
@RestController
public class UserController {}

// 或全局配置
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE");
}
}

拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Component
public class AuthInterceptor implements HandlerInterceptor {

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String token = request.getHeader("Authorization");
if (token == null) {
response.setStatus(401);
return false;
}
return true;
}
}

@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new AuthInterceptor())
.addPathPatterns("/api/**");
}
}

总结

Spring MVC提供了强大的Web开发能力,RESTful风格的API设计已成为现代Web服务的主流选择。掌握请求参数绑定、异常处理、拦截器等核心功能是开发高效Web应用的基础。

Spring全家桶笔记八:依赖注入与Bean管理

依赖注入方式

构造器注入

1
2
3
4
5
6
7
8
9
10
@Service
public class OrderService {
private final UserService userService;
private final OrderDao orderDao;

public OrderService(UserService userService, OrderDao orderDao) {
this.userService = userService;
this.orderDao = orderDao;
}
}

Setter注入

1
2
3
4
5
6
7
8
9
@Service
public class PaymentService {
private PaymentGateway paymentGateway;

@Autowired
public void setPaymentGateway(PaymentGateway paymentGateway) {
this.paymentGateway = paymentGateway;
}
}

字段注入

1
2
3
4
5
@Service
public class OrderService {
@Autowired
private UserService userService;
}

Bean作用域

1
2
3
4
5
6
7
@Service
@Scope("prototype") // 多例
public class MyService {}

@Service
@Scope("request") // 请求作用域
public class RequestService {}

@Conditional条件装配

1
2
3
4
5
6
7
8
9
10
11
12
13
@ConditionalOnProperty(name = "feature.enabled", havingValue = "true")
@Service
public class FeatureService {}

@ConditionalOnMissingBean(UserService.class)
@Bean
public UserService userService() {
return new UserService();
}

@ConditionalOnClass(RedisTemplate.class)
@Configuration
public class RedisConfig {}

Bean生命周期

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Component
public class MyBean implements InitializingBean, DisposableBean {

@PostConstruct
public void init() {
System.out.println("初始化");
}

@PreDestroy
public void destroy() {
System.out.println("销毁");
}

@Override
public void afterPropertiesSet() {}

@Override
public void destroy() {}
}

外部化配置

1
2
3
4
5
6
7
8
9
10
11
@Component
public class MyComponent {
@Value("${custom.property}")
private String property;

@Value("${db.url:localhost}")
private String dbUrl;

@Value("#{systemProperties['user.dir']}")
private String userDir;
}

总结

依赖注入是Spring核心特性,合理选择注入方式、配置Bean作用域、条件装配等是构建健壮应用的关键。

数据库事务隔离级别详解

什么是事务隔离级别

事务隔离级别定义了事务在并发执行时,多个事务之间的可见性和相互影响程度。隔离级别越高,数据一致性越好,但并发性能可能越差。

SQL标准定义的四种隔离级别

1. READ UNCOMMITTED(读未提交)

  • 最低隔离级别
  • 允许读取未提交的数据(脏读)
  • 实际应用中很少使用

问题:脏读、不可重复读、幻读都可能发生

2. READ COMMITTED(读已提交)

  • 只能读取已提交的数据
  • 大多数数据库的默认隔离级别(如Oracle、SQL Server)

解决脏读:但仍可能出现不可重复读和幻读

问题:不可重复读、幻读

3. REPEATABLE READ(可重复读)

  • 保证同一事务中多次读取同一数据结果一致
  • MySQL InnoDB的默认隔离级别

解决脏读和不可重复读:但仍可能出现幻读

问题:幻读

4. SERIALIZABLE(串行化)

  • 最高隔离级别
  • 强制事务顺序执行,完全避免并发问题

解决所有问题:但并发性能最差

各隔离级别存在的问题

隔离级别 脏读 不可重复读 幻读
READ UNCOMMITTED
READ COMMITTED ×
REPEATABLE READ × ×
SERIALIZABLE × × ×

MySQL中的实现

查看当前隔离级别

1
SELECT @@transaction_isolation;

设置隔离级别

1
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

InnoDB的MVCC

  • 使用多版本并发控制(MVCC)提高并发性能
  • 通过隐藏字段和undo日志实现
  • 快照读 vs 当前读

实际应用建议

  1. 一般业务:READ COMMITTED 足够
  2. 需要高一致性:REPEATABLE READ
  3. 金融类业务:SERIALIZABLE 或结合锁

总结

理解事务隔离级别是数据库并发控制的基础。在实际开发中,需要根据业务场景选择合适的隔离级别,在数据一致性和并发性能之间取得平衡。

0%