Java框架核心知识详解 一、Spring框架深入解析 1.1 Spring核心概念与原理 Spring框架是一个分层的企业级应用开发框架,其核心是控制反转(IoC)和面向切面编程(AOP)。
IoC容器原理深入分析 IoC(控制反转)的本质: 传统开发中,对象的创建和依赖关系由程序代码直接控制,而IoC将这个控制权交给了外部容器。Spring通过依赖注入(DI)来实现IoC。
IoC容器的实现机制:
BeanFactory : 基础容器,提供基本的IoC功能
ApplicationContext : 高级容器,继承BeanFactory,提供更多企业级功能
Bean的生命周期详解:
1 实例化 → 属性赋值 → 初始化前处理 → 初始化 → 初始化后处理 → 使用 → 销毁前处理 → 销毁
详细生命周期步骤:
Bean元数据解析(XML、注解、Java配置)
调用Bean构造函数实例化
依赖注入(setter方法、构造函数、字段注入)
如果实现了BeanNameAware,调用setBeanName()
如果实现了BeanFactoryAware,调用setBeanFactory()
如果实现了ApplicationContextAware,调用setApplicationContext()
如果有BeanPostProcessor,调用postProcessBeforeInitialization()
如果实现了InitializingBean,调用afterPropertiesSet()
如果配置了init-method,调用自定义初始化方法
如果有BeanPostProcessor,调用postProcessAfterInitialization()
Bean可以被使用
容器关闭时,如果实现了DisposableBean,调用destroy()
如果配置了destroy-method,调用自定义销毁方法
DI的三种注入方式对比
AOP原理深入分析 AOP实现机制: Spring AOP基于代理模式实现,支持两种代理方式:
JDK动态代理 : 针对实现了接口的类
CGLIB代理 : 针对没有实现接口的类
我们来系统性地讲解一下 Spring AOP 的实现机制 ,包括:
JDK 动态代理
CGLIB 动态代理
静态代理(对比说明)
🌟 一、Spring AOP 实现机制概述 Spring AOP(面向切面编程)是基于 代理模式 实现的,它通过在目标方法执行前后织入逻辑(增强),实现横切关注点(如日志、安全、事务等)的分离。
Spring AOP 仅支持方法级别的代理(即对方法进行增强),不支持字段、构造器等底层字节码增强(这要用 AspectJ)。
🧩 二、Spring AOP 的两种动态代理方式 1️⃣ JDK 动态代理(基于接口) ✅ 原理:
基于 Java 的 java.lang.reflect.Proxy
类实现。
必须要有接口,Spring 会为接口生成代理类。
代理类在运行时实现接口,并将调用委托给 InvocationHandler。
📦 示例: 1 2 3 4 5 6 7 8 9 public interface UserService { void addUser () ; } public class UserServiceImpl implements UserService { public void addUser () { System.out.println("添加用户" ); } }
Spring AOP 为 UserService
创建代理:
1 2 3 4 5 6 7 8 9 10 11 UserService proxy = (UserService) Proxy.newProxyInstance( userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), new InvocationHandler () { public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { System.out.println("前置增强" ); Object result = method.invoke(userService, args); System.out.println("后置增强" ); return result; } });
🎯 特点:
特性
描述
是否依赖接口
✅ 是
性能
较高(比 CGLIB 略快)
生成的代理类
实现接口
2️⃣ CGLIB 动态代理(基于子类) ✅ 原理:
使用 CGLIB 库在运行时 生成目标类的子类 ,并重写其方法来实现增强。
适用于 没有接口 的类,或者想对类本身增强而非接口。
🧪 示例: 1 2 3 4 5 public class UserService { public void addUser () { System.out.println("添加用户" ); } }
Spring 通过 CGLIB 生成子类:
1 2 3 4 5 6 7 8 9 10 11 Enhancer enhancer = new Enhancer ();enhancer.setSuperclass(UserService.class); enhancer.setCallback(new MethodInterceptor () { public Object intercept (Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("前置增强" ); Object result = proxy.invokeSuper(obj, args); System.out.println("后置增强" ); return result; } }); UserService proxy = (UserService) enhancer.create();
🎯 特点:
特性
描述
是否依赖接口
❌ 否
是否可代理 final 类/方法
❌ 否(final 无法被继承/重写)
生成的代理类
子类
🏗 三、静态代理(对比动态代理) ✅ 原理:
自己写一个代理类,实现相同接口 ,将方法调用委托给目标对象,并在调用前后手动添加增强逻辑。
🧪 示例: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public interface UserService { void addUser () ; } public class UserServiceImpl implements UserService { public void addUser () { System.out.println("添加用户" ); } } public class UserServiceProxy implements UserService { private UserService target; public UserServiceProxy (UserService target) { this .target = target; } public void addUser () { System.out.println("前置增强" ); target.addUser(); System.out.println("后置增强" ); } }
🎯 特点:
特性
描述
是否自动生成
❌ 需手动编码
灵活性
差(每个类都要写一个代理类)
是否支持 AOP
❌ 无法动态织入多个切面
🔚 四、三者对比总结
特性
静态代理
JDK 动态代理
CGLIB 动态代理
是否依赖接口
✅ 是
✅ 是
❌ 否
是否自动生成代理类
❌ 手动编写
✅ 运行时生成
✅ 运行时生成
是否可代理类
❌ 否(接口为主)
❌ 否(接口为主)
✅ 是(类的子类)
性能
一般
较高
略慢(但可接受)
是否支持 final 方法
✅
✅
❌ 否
✅ Spring 如何选择代理方式?
默认使用 JDK 动态代理(如果目标类实现了接口)。
如果目标类没有接口,Spring 自动切换为 CGLIB。
可以强制使用 CGLIB:
1 @EnableAspectJAutoProxy(proxyTargetClass = true)
AOP核心概念:
切面(Aspect) : 横切关注点的模块化
连接点(Joinpoint) : 程序执行中的特定点
切点(Pointcut) : 连接点的集合
通知(Advice) : 切面在特定连接点执行的代码
目标对象(Target) : 被代理的对象
代理对象(Proxy) : AOP框架创建的对象
JDK动态代理 vs. CGLIB动态代理
JDK动态代理 :
基于接口实现
使用Proxy.newProxyInstance()创建代理
只能代理实现了接口的类
CGLIB动态代理 :
基于继承实现
使用Enhancer类创建代理
可以代理普通类
不能代理final类和方法
1.2 Spring核心注解详解 基础配置注解 1 2 3 4 5 6 @Configuration @ComponentScan(basePackages = "com.example") @EnableAutoConfiguration @SpringBootApplication @Import(OtherConfig.class) @PropertySource("classpath:application.properties")
Bean定义注解 1 2 3 4 5 6 7 8 9 10 @Component @Service @Repository @Controller @RestController @Bean @Scope("singleton/prototype/request/session") @Lazy @Primary @Qualifier("beanName")
依赖注入注解 1 2 3 4 5 @Autowired @Resource @Inject @Value("${property.name}") @ConfigurationProperties(prefix = "app")
生命周期注解 1 2 @PostConstruct @PreDestroy
AOP相关注解 1 2 3 4 5 6 7 @Aspect @Pointcut @Before @After @AfterReturning @AfterThrowing @Around
1.3 Spring常见使用场景 场景1:服务层事务管理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Service @Transactional public class UserService { @Autowired private UserRepository userRepository; @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class) public User createUser (User user) { return userRepository.save(user); } @Transactional(readOnly = true) public User findById (Long id) { return userRepository.findById(id); } }
场景2:缓存管理 1 2 3 4 5 6 7 8 9 10 11 12 13 @Service public class ProductService { @Cacheable(value = "products", key = "#id") public Product getProduct (Long id) { return productRepository.findById(id); } @CacheEvict(value = "products", key = "#product.id") public Product updateProduct (Product product) { return productRepository.save(product); } }
1.4 Spring常见面试问题 Q1: Spring IoC容器的初始化过程是怎样的?
A: Spring IoC容器初始化分为三个阶段:
Resource定位 : 定位配置文件
BeanDefinition载入 : 将配置信息转换为Spring内部数据结构
BeanDefinition注册 : 将BeanDefinition注册到IoC容器的HashMap中
具体流程:
创建ApplicationContext
加载配置元数据(XML、注解、Java配置)
解析配置,创建BeanDefinition
注册BeanDefinition到BeanDefinitionRegistry
实例化非懒加载的单例Bean
Q2: Spring中的循环依赖是如何解决的?
A: Spring通过三级缓存解决循环依赖:
singletonObjects : 一级缓存,存放完整的Bean实例
earlySingletonObjects : 二级缓存,存放早期的Bean实例
singletonFactories : 三级缓存,存放Bean工厂
解决过程:
A依赖B,B依赖A
创建A时,将A的工厂放入三级缓存
A需要注入B,开始创建B
B需要注入A,从缓存中获取A的早期实例
B创建完成,A继续创建完成
Q3: Spring AOP的实现原理?
A: Spring AOP基于代理模式实现:
JDK动态代理 : 目标类实现接口时使用,基于反射机制
CGLIB代理 : 目标类没有接口时使用,基于字节码技术
代理创建过程:
Spring在Bean初始化后,检查是否需要AOP
如果需要,创建代理对象替换原始Bean
代理对象拦截方法调用,执行切面逻辑
二、Spring Boot深入解析 2.1 Spring Boot核心原理 自动配置原理: Spring Boot通过@EnableAutoConfiguration注解启用自动配置机制。
核心类分析:
SpringBootApplication : 组合注解,包含@Configuration、@EnableAutoConfiguration、@ComponentScan
AutoConfigurationImportSelector : 负责导入自动配置类
spring.factories : META-INF/spring.factories文件定义自动配置类
自动配置流程:
SpringBoot启动时扫描所有jar包下的META-INF/spring.factories文件
加载文件中定义的自动配置类
根据条件注解(@ConditionalOnClass等)判断是否生效
生效的配置类会创建相应的Bean
2.2 Spring Boot启动流程详解 SpringApplication.run()方法执行流程:
准备阶段 :
创建SpringApplication实例
确定应用类型(SERVLET、REACTIVE、NONE)
加载ApplicationContextInitializer
加载ApplicationListener
启动阶段 :
启动计时器
配置Headless模式
获取并启动监听器
准备环境(Environment)
打印Banner
容器创建阶段 :
创建ApplicationContext
准备ApplicationContext
刷新ApplicationContext
刷新后处理
完成阶段 :
2.3 Spring Boot核心注解 启动类注解 1 2 3 4 5 6 7 8 9 10 @SpringBootApplication @Configuration @EnableAutoConfiguration @ComponentScan @SpringBootConfiguration @EnableScheduling @EnableAsync @EnableCaching
条件注解 1 2 3 4 5 6 @ConditionalOnClass(DataSource.class) @ConditionalOnMissingBean(DataSource.class) @ConditionalOnProperty(name = "app.enabled", havingValue = "true") @ConditionalOnWebApplication @ConditionalOnNotWebApplication @Profile("dev")
配置属性注解 1 2 3 4 5 6 7 @ConfigurationProperties(prefix = "app.datasource") public class DataSourceProperties { private String url; private String username; private String password; }
2.4 Spring Boot实际应用场景 场景1:微服务架构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @SpringBootApplication @EnableEurekaClient @EnableFeignClients public class UserServiceApplication { public static void main (String[] args) { SpringApplication.run(UserServiceApplication.class, args); } } @FeignClient(name = "order-service") public interface OrderServiceClient { @GetMapping("/orders/{userId}") List<Order> getOrdersByUserId (@PathVariable Long userId) ; }
场景2:数据访问层整合 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @SpringBootApplication @EnableJpaRepositories public class Application { @Bean @Primary @ConfigurationProperties("spring.datasource.primary") public DataSourceProperties primaryDataSourceProperties () { return new DataSourceProperties (); } @Bean @ConfigurationProperties("spring.datasource.secondary") public DataSourceProperties secondaryDataSourceProperties () { return new DataSourceProperties (); } }
2.5 Spring Boot面试问题 Q1: Spring Boot的启动原理是什么?
A: Spring Boot启动原理核心在于自动配置:
@SpringBootApplication 组合了三个注解,其中@EnableAutoConfiguration是关键
AutoConfigurationImportSelector 会扫描所有引入的jar包,查找其META-INF/spring.factories文件中org.springframework.boot.autoconfigure.EnableAutoConfiguration键所对应的配置类全限定名列表
根据条件注解判断哪些自动配置类生效
生效的配置类会向容器中注册相应的Bean
Q2: Spring Boot如何实现自动配置?
A: 自动配置通过以下机制实现:
条件注解 : @ConditionalOnClass、@ConditionalOnBean等判断配置是否生效
配置文件 : spring.factories定义自动配置类列表
配置属性 : @ConfigurationProperties绑定配置文件中的属性
默认配置 : 提供合理的默认值,用户可覆盖
Q3: Spring Boot Starter的工作原理?
A: Starter是Spring Boot自动配置的载体:
依赖管理 : 通过Maven/Gradle引入相关依赖
自动配置 : 包含AutoConfiguration类
属性绑定 : 提供ConfigurationProperties类
条件装配 : 使用条件注解控制Bean的创建
三、Spring MVC深入解析 3.1 Spring MVC核心组件 DispatcherServlet处理流程:
接收请求 : DispatcherServlet接收HTTP请求
查找Handler : HandlerMapping查找处理请求的Handler
获取HandlerAdapter : 获取能够执行Handler的HandlerAdapter
执行Handler : HandlerAdapter执行Handler(Controller方法)
处理结果 : 返回ModelAndView
视图解析 : ViewResolver解析视图名称
渲染视图 : View渲染模型数据
返回响应 : 响应结果返回给客户端
核心组件详解:
DispatcherServlet : 前端控制器,统一处理请求
HandlerMapping : 处理器映射器,URL与Handler的映射
HandlerAdapter : 处理器适配器,执行Handler
Handler : 处理器,即Controller
ViewResolver : 视图解析器
View : 视图
3.2 Spring MVC核心注解 控制器注解 1 2 3 4 5 6 7 8 @Controller @RestController @RequestMapping("/api") @GetMapping("/users") @PostMapping("/users") @PutMapping("/users/{id}") @DeleteMapping("/users/{id}") @PatchMapping("/users/{id}")
参数绑定注解 1 2 3 4 5 6 7 8 @RequestParam("name") @PathVariable("id") @RequestBody @RequestHeader("Content-Type") @CookieValue("sessionId") @ModelAttribute @SessionAttribute @RequestPart
响应处理注解 1 2 3 4 5 @ResponseBody @ResponseStatus(HttpStatus.CREATED) @ExceptionHandler @ControllerAdvice @RestControllerAdvice
3.3 Spring MVC实际应用场景 场景1:RESTful API开发 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 @RestController @RequestMapping("/api/users") @Validated public class UserController { @Autowired private UserService userService; @GetMapping public ResponseEntity<PageResult<User>> getUsers ( @RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "10") int size, @RequestParam(required = false) String keyword) { PageResult<User> result = userService.findUsers(page, size, keyword); return ResponseEntity.ok(result); } @PostMapping public ResponseEntity<User> createUser (@Valid @RequestBody UserCreateRequest request) { User user = userService.createUser(request); return ResponseEntity.status(HttpStatus.CREATED).body(user); } @PutMapping("/{id}") public ResponseEntity<User> updateUser ( @PathVariable Long id, @Valid @RequestBody UserUpdateRequest request) { User user = userService.updateUser(id, request); return ResponseEntity.ok(user); } @DeleteMapping("/{id}") public ResponseEntity<Void> deleteUser (@PathVariable Long id) { userService.deleteUser(id); return ResponseEntity.noContent().build(); } }
场景2:全局异常处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 @RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(ValidationException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public ErrorResponse handleValidationException (ValidationException e) { return ErrorResponse.builder() .code("VALIDATION_ERROR" ) .message(e.getMessage()) .timestamp(LocalDateTime.now()) .build(); } @ExceptionHandler(EntityNotFoundException.class) @ResponseStatus(HttpStatus.NOT_FOUND) public ErrorResponse handleEntityNotFoundException (EntityNotFoundException e) { return ErrorResponse.builder() .code("ENTITY_NOT_FOUND" ) .message(e.getMessage()) .timestamp(LocalDateTime.now()) .build(); } @ExceptionHandler(Exception.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public ErrorResponse handleGenericException (Exception e) { log.error("Unexpected error occurred" , e); return ErrorResponse.builder() .code("INTERNAL_ERROR" ) .message("An unexpected error occurred" ) .timestamp(LocalDateTime.now()) .build(); } }
场景3:文件上传处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 @RestController @RequestMapping("/api/files") public class FileController { @PostMapping("/upload") public ResponseEntity<FileUploadResponse> uploadFile ( @RequestPart("file") MultipartFile file, @RequestParam(required = false) String description) { if (!isValidFileType(file.getContentType())) { throw new InvalidFileTypeException ("Invalid file type" ); } if (file.getSize() > MAX_FILE_SIZE) { throw new FileSizeExceededException ("File size exceeds limit" ); } String fileName = fileService.saveFile(file, description); FileUploadResponse response = FileUploadResponse.builder() .fileName(fileName) .originalName(file.getOriginalFilename()) .size(file.getSize()) .contentType(file.getContentType()) .uploadTime(LocalDateTime.now()) .build(); return ResponseEntity.ok(response); } }
3.4 Spring MVC面试问题 Q1: Spring MVC的执行流程是怎样的?
A: Spring MVC的执行流程如下:
用户发送请求到DispatcherServlet
DispatcherServlet调用HandlerMapping查找Handler
HandlerMapping返回HandlerExecutionChain(包含Handler和拦截器)
DispatcherServlet调用HandlerAdapter执行Handler
Handler执行完成后返回ModelAndView
DispatcherServlet调用ViewResolver解析视图名称
ViewResolver返回View对象
DispatcherServlet调用View的render方法渲染视图
响应结果返回给用户
Q2: Spring MVC中的拦截器是如何工作的?
A: 拦截器基于AOP思想,在Handler执行前后进行处理:
HandlerInterceptor接口 提供三个方法:
preHandle(): 前置处理,返回false则中断请求
postHandle(): 后置处理,Handler执行后调用
afterCompletion(): 完成处理,视图渲染后调用
执行顺序 :
多个拦截器按配置顺序执行preHandle()
按相反顺序执行postHandle()和afterCompletion()
Q3: @RequestBody和@ResponseBody的工作原理?
A: 这两个注解基于HttpMessageConverter工作:
@RequestBody :
使用HttpMessageConverter将HTTP请求体转换为Java对象
常用的转换器:MappingJackson2HttpMessageConverter处理JSON
@ResponseBody :
使用HttpMessageConverter将Java对象转换为HTTP响应体
根据Accept头选择合适的转换器
四、MyBatis深入解析 4.1 MyBatis核心原理 MyBatis架构分析: MyBatis采用分层架构设计:
API接口层 : 提供给外部使用的接口API
数据处理层 : 参数映射、SQL解析、结果映射
基础支撑层 : 连接管理、事务管理、配置加载、缓存处理
MyBatis核心组件:
SqlSessionFactory : 会话工厂,负责创建SqlSession
SqlSession : 会话,执行SQL的核心接口
Executor : 执行器,实际执行SQL
StatementHandler : 语句处理器,处理SQL语句
ParameterHandler : 参数处理器,处理SQL参数
ResultSetHandler : 结果集处理器,处理查询结果
MappedStatement : 映射语句,封装SQL配置信息
4.2 MyBatis执行流程详解 SQL执行流程:
解析配置 : 解析mybatis-config.xml和Mapper XML文件
创建会话 : 通过SqlSessionFactory创建SqlSession
获取Mapper : 通过动态代理创建Mapper接口实例
执行SQL : 调用Mapper方法,转换为MappedStatement执行
参数处理 : ParameterHandler处理输入参数
执行查询 : StatementHandler执行SQL语句
结果映射 : ResultSetHandler处理结果集
返回结果 : 将结果返回给调用方
动态代理原理: MyBatis使用JDK动态代理为Mapper接口创建代理对象:
1 2 3 4 5 6 7 8 public class MapperProxy <T> implements InvocationHandler { @Override public Object invoke (Object proxy, Method method, Object[] args) { } }
4.3 MyBatis核心注解 基础映射注解 1 2 3 4 5 6 7 8 9 10 11 12 @Select("SELECT * FROM users WHERE id = #{id}") User findById (@Param("id") Long id) ; @Insert("INSERT INTO users(name, email) VALUES(#{name}, #{email})") @Options(useGeneratedKeys = true, keyProperty = "id") int insert (User user) ;@Update("UPDATE users SET name = #{name} WHERE id = #{id}") int update (User user) ;@Delete("DELETE FROM users WHERE id = #{id}") int delete (@Param("id") Long id) ;
高级映射注解 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Results({ @Result(column = "user_id", property = "id"), @Result(column = "user_name", property = "name"), @Result(column = "create_time", property = "createTime") }) @Select("SELECT user_id, user_name, create_time FROM users") List<User> findAll () ; @One(select = "findUserById") @Result(column = "user_id", property = "user") Order findOrderById (@Param("id") Long id) ; @Many(select = "findOrdersByUserId") @Result(column = "id", property = "orders") User findUserWithOrders (@Param("id") Long id) ;
动态SQL注解 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @SelectProvider(type = UserSqlProvider.class, method = "findUsers") List<User> findUsers (@Param("name") String name, @Param("email") String email) ; public class UserSqlProvider { public String findUsers (Map<String, Object> params) { return new SQL () {{ SELECT("*" ); FROM("users" ); if (params.get("name" ) != null ) { WHERE("name LIKE CONCAT('%', #{name}, '%')" ); } if (params.get("email" ) != null ) { WHERE("email = #{email}" ); } }}.toString(); } }
4.4 MyBatis缓存机制 一级缓存(默认开启):
作用域:SqlSession级别
生命周期:与SqlSession相同
存储:HashMap结构,key为CacheKey
二级缓存(需要配置):
作用域:Mapper级别
生命周期:与应用程序相同
配置:@CacheNamespace注解或标签
1 2 3 4 5 6 7 8 9 @CacheNamespace( eviction = LRU.class, flushInterval = 60000, size = 1024, readWrite = true ) public interface UserMapper { }
4.5 MyBatis实际应用场景 场景1:复杂查询场景 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 @Mapper public interface OrderMapper { @Select(""" SELECT o.*, u.name as user_name, u.email as user_email FROM orders o LEFT JOIN users u ON o.user_id = u.id WHERE o.status = #{status} AND o.create_time BETWEEN #{startTime} AND #{endTime} ORDER BY o.create_time DESC LIMIT #{offset}, #{limit} """) @Results({ @Result(column = "id", property = "id"), @Result(column = "user_id", property = "userId"), @Result(column = "user_name", property = "user.name"), @Result(column = "user_email", property = "user.email") }) List<OrderVO> findOrdersWithUser ( @Param("status") String status, @Param("startTime") LocalDateTime startTime, @Param("endTime") LocalDateTime endTime, @Param("offset") int offset, @Param("limit") int limit ) ; @Select(""" SELECT COUNT(*) as total_count, SUM(amount) as total_amount, AVG(amount) as avg_amount FROM orders WHERE status = #{status} AND create_time >= #{startTime} """) OrderStatistics getOrderStatistics ( @Param("status") String status, @Param("startTime") LocalDateTime startTime ) ;}
场景2:批量操作场景 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 @Mapper public interface BatchMapper { @Insert(""" <script> INSERT INTO users (name, email, create_time) VALUES <foreach collection="users" item="user" separator=","> (#{user.name}, #{user.email}, #{user.createTime}) </foreach> </script> """) int batchInsertUsers (@Param("users") List<User> users) ; @Update(""" <script> <foreach collection="users" item="user" separator=";"> UPDATE users SET name = #{user.name}, email = #{user.email}, update_time = NOW() WHERE id = #{user.id} </foreach> </script> """) int batchUpdateUsers (@Param("users") List<User> users) ; }
场景3:动态SQL场景 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 @SelectProvider(type = UserSqlProvider.class, method = "searchUsers") List<User> searchUsers (UserSearchCriteria criteria) ; public class UserSqlProvider { public String searchUsers (UserSearchCriteria criteria) { return new SQL () {{ SELECT("u.*, p.name as profile_name" ); FROM("users u" ); LEFT_OUTER_JOIN("user_profiles p ON u.id = p.user_id" ); if (StringUtils.hasText(criteria.getName())) { WHERE("u.name LIKE CONCAT('%', #{name}, '%')" ); } if (StringUtils.hasText(criteria.getEmail())) { WHERE("u.email = #{email}" ); } if (criteria.getMinAge() != null ) { WHERE("u.age >= #{minAge}" ); } if (criteria.getMaxAge() != null ) { WHERE("u.age <= #{maxAge}" ); } if (criteria.getCreateTimeStart() != null ) { WHERE("u.create_time >= #{createTimeStart}" ); } if (criteria.getCreateTimeEnd() != null ) { WHERE("u.create_time <= #{createTimeEnd}" ); } if (CollectionUtils.isNotEmpty(criteria.getStatuses())) { WHERE("u.status IN (" + criteria.getStatuses().stream() .map(s -> "'" + s + "'" ) .collect(Collectors.joining("," )) + ")" ); } if (StringUtils.hasText(criteria.getSortField())) { if ("desc" .equalsIgnoreCase(criteria.getSortDirection())) { ORDER_BY("u." + criteria.getSortField() + " DESC" ); } else { ORDER_BY("u." + criteria.getSortField() + " ASC" ); } } else { ORDER_BY("u.create_time DESC" ); } }}.toString(); } }
4.6 MyBatis性能优化 优化策略1:合理使用缓存 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @CacheNamespace( eviction = LRU.class, // 缓存回收策略 flushInterval = 300000, // 缓存刷新间隔(5分钟) size = 1024, // 缓存大小 readWrite = true, // 读写缓存 blocking = false // 非阻塞缓存 ) public interface ProductMapper { @Select("SELECT * FROM products WHERE category_id = #{categoryId}") @Options(useCache = true) List<Product> findByCategory (@Param("categoryId") Long categoryId) ; @Select("SELECT * FROM products WHERE id = #{id}") @Options(useCache = false) Product findRealTimeById (@Param("id") Long id) ; }
优化策略2:批量操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @Insert(""" <script> INSERT INTO order_items (order_id, product_id, quantity, price) VALUES <foreach collection="items" item="item" separator=","> (#{item.orderId}, #{item.productId}, #{item.quantity}, #{item.price}) </foreach> </script> """) int batchInsertOrderItems (@Param("items") List<OrderItem> items) ;public void batchUpdateProducts (List<Product> products) { try (SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH)) { ProductMapper mapper = sqlSession.getMapper(ProductMapper.class); for (Product product : products) { mapper.updateProduct(product); } sqlSession.commit(); } }
优化策略3:延迟加载 1 2 3 4 5 6 7 8 9 10 11 12 13 @ResultMap("userResultMap") @Select("SELECT * FROM users WHERE id = #{id}") User findUserById (@Param("id") Long id) ; <resultMap id="userResultMap" type="User" > <id column="id" property="id" /> <result column="name" property="name" /> <collection property="orders" select="findOrdersByUserId" column="id" fetchType="lazy" /> </resultMap>
4.7 MyBatis常见问题与解决方案 问题1:N+1查询问题 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 @Select("SELECT * FROM users") List<User> findAllUsers () ; @Select("SELECT * FROM orders WHERE user_id = #{userId}") List<Order> findOrdersByUserId (@Param("userId") Long userId) ; @Select(""" SELECT u.*, o.id as order_id, o.amount, o.status as order_status FROM users u LEFT JOIN orders o ON u.id = o.user_id """) @Results({ @Result(column = "id", property = "id"), @Result(column = "name", property = "name"), @Result(column = "order_id", property = "orders.id"), @Result(column = "amount", property = "orders.amount"), @Result(column = "order_status", property = "orders.status") }) List<User> findUsersWithOrders () ; public List<User> findUsersWithOrdersOptimized (List<Long> userIds) { List<User> users = userMapper.findUsersByIds(userIds); if (!users.isEmpty()) { List<Long> ids = users.stream().map(User::getId).collect(Collectors.toList()); List<Order> orders = orderMapper.findOrdersByUserIds(ids); Map<Long, List<Order>> orderMap = orders.stream() .collect(Collectors.groupingBy(Order::getUserId)); users.forEach(user -> user.setOrders(orderMap.get(user.getId()))); } return users; }
问题2:大数据量查询内存溢出 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 @Select("SELECT * FROM large_table") List<LargeData> findAllData () ; public List<LargeData> findAllDataWithPaging () { List<LargeData> allData = new ArrayList <>(); int pageSize = 1000 ; int offset = 0 ; List<LargeData> pageData; do { pageData = mapper.findDataWithLimit(offset, pageSize); allData.addAll(pageData); offset += pageSize; } while (pageData.size() == pageSize); return allData; } @Select("SELECT * FROM large_table WHERE process_status = 'PENDING'") @Options(resultSetType = ResultSetType.FORWARD_ONLY, fetchSize = 1000) Cursor<LargeData> findPendingDataCursor () ; public void processLargeData () { try (Cursor<LargeData> cursor = mapper.findPendingDataCursor()) { cursor.forEach(this ::processData); } }
问题3:SQL注入防护 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 @Select("SELECT * FROM users WHERE name = '${name}'") List<User> findByNameUnsafe (@Param("name") String name) ; @Select("SELECT * FROM users WHERE name = #{name}") List<User> findByNameSafe (@Param("name") String name) ; @SelectProvider(type = UserSqlProvider.class, method = "findUsersWithSort") List<User> findUsersWithSort (@Param("sortField") String sortField, @Param("sortDirection") String sortDirection) ;public class UserSqlProvider { private static final Set<String> ALLOWED_SORT_FIELDS = Set.of("id" , "name" , "email" , "create_time" , "update_time" ); public String findUsersWithSort (Map<String, Object> params) { String sortField = (String) params.get("sortField" ); String sortDirection = (String) params.get("sortDirection" ); if (!ALLOWED_SORT_FIELDS.contains(sortField)) { sortField = "id" ; } if (!"DESC" .equalsIgnoreCase(sortDirection)) { sortDirection = "ASC" ; } return "SELECT * FROM users ORDER BY " + sortField + " " + sortDirection; } }
4.8 MyBatis面试高频问题 Q1: MyBatis的执行流程是怎样的?
A: MyBatis的执行流程包括以下步骤:
配置解析 : 解析mybatis-config.xml配置文件和Mapper XML文件,创建Configuration对象
SqlSessionFactory创建 : 根据Configuration创建SqlSessionFactory
SqlSession创建 : 通过SqlSessionFactory.openSession()创建SqlSession
Mapper获取 : 通过SqlSession.getMapper()获取Mapper接口的代理对象
方法调用 : 调用Mapper接口方法,通过动态代理转换为SQL执行
SQL执行 : 通过Executor执行SQL,包括参数处理、语句执行、结果映射
结果返回 : 将执行结果返回给调用方
Q2: MyBatis的一级缓存和二级缓存有什么区别?
A: 两级缓存的主要区别:
一级缓存(默认开启):
作用域:SqlSession级别
生命周期:与SqlSession相同,SqlSession关闭时缓存清空
存储结构:HashMap,key为CacheKey(由SQL、参数、分页等组成)
失效条件:执行update、insert、delete操作或手动清空
二级缓存(需要配置):
作用域:Mapper级别,多个SqlSession可以共享
生命周期:与应用程序相同
存储结构:可配置(HashMap、LRU、FIFO等)
配置方式:@CacheNamespace注解或标签
注意事项:需要序列化,可能存在脏读问题
Q3: MyBatis如何防止SQL注入?
A: MyBatis通过以下方式防止SQL注入:
参数绑定 : 使用#{}而不是${}
#{}:预编译处理,参数作为占位符传递
${}:字符串替换,直接拼接到SQL中(危险)
类型检查 : MyBatis会对参数类型进行检查
白名单验证 : 对于动态排序等场景,使用白名单验证
1 2 3 4 5 6 7 @Select("SELECT * FROM users WHERE name = #{name} AND age > #{age}") List<User> findUsers (@Param("name") String name, @Param("age") Integer age) ; @Select("SELECT * FROM users WHERE name = '${name}'") List<User> findUsersUnsafe (@Param("name") String name) ;
Q4: MyBatis中#{}和${}的区别?
A: 两者的主要区别:
#{}(推荐使用):
预编译处理,生成PreparedStatement
参数会被处理为占位符?
可以防止SQL注入
会进行类型转换
适用于参数值传递
${}(谨慎使用):
字符串替换,直接拼接到SQL中
不会进行预编译
存在SQL注入风险
不会进行类型转换
适用于动态表名、列名等场景
Q5: MyBatis的动态SQL是如何实现的?
A: MyBatis的动态SQL通过以下标签实现:
if标签 : 条件判断
choose/when/otherwise : 类似switch-case
where标签 : 智能处理WHERE条件
set标签 : 智能处理SET语句
foreach标签 : 循环处理
trim标签 : 去除多余的字符
实现原理:
使用OGNL表达式进行条件判断
在SQL解析阶段根据参数值动态生成SQL
通过SqlNode树结构表示动态SQL
在执行时遍历SqlNode树生成最终SQL
五、框架整合与最佳实践 5.1 Spring Boot + MyBatis整合 完整配置示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 @SpringBootApplication @MapperScan("com.example.mapper") public class Application { public static void main (String[] args) { SpringApplication.run(Application.class, args); } } @Configuration public class DataSourceConfig { @Bean @Primary @ConfigurationProperties("spring.datasource.primary") public DataSourceProperties primaryDataSourceProperties () { return new DataSourceProperties (); } @Bean @Primary public DataSource primaryDataSource () { return primaryDataSourceProperties() .initializeDataSourceBuilder() .type(HikariDataSource.class) .build(); } @Bean public SqlSessionFactory sqlSessionFactory (DataSource dataSource) throws Exception { SqlSessionFactoryBean factory = new SqlSessionFactoryBean (); factory.setDataSource(dataSource); org.apache.ibatis.session.Configuration config = new org .apache.ibatis.session.Configuration(); config.setMapUnderscoreToCamelCase(true ); config.setLogImpl(Slf4jImpl.class); config.setCacheEnabled(true ); config.setLazyLoadingEnabled(true ); config.setAggressiveLazyLoading(false ); factory.setConfiguration(config); return factory.getObject(); } }
事务管理配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 @Service @Transactional public class OrderService { @Autowired private OrderMapper orderMapper; @Autowired private OrderItemMapper orderItemMapper; @Autowired private ProductMapper productMapper; @Transactional(rollbackFor = Exception.class) public Order createOrder (CreateOrderRequest request) { Order order = new Order (); order.setUserId(request.getUserId()); order.setStatus("PENDING" ); order.setCreateTime(LocalDateTime.now()); orderMapper.insert(order); BigDecimal totalAmount = BigDecimal.ZERO; List<OrderItem> orderItems = new ArrayList <>(); for (CreateOrderItemRequest itemRequest : request.getItems()) { Product product = productMapper.findById(itemRequest.getProductId()); if (product == null ) { throw new ProductNotFoundException ("Product not found: " + itemRequest.getProductId()); } if (product.getStock() < itemRequest.getQuantity()) { throw new InsufficientStockException ("Insufficient stock for product: " + product.getName()); } productMapper.decreaseStock(itemRequest.getProductId(), itemRequest.getQuantity()); OrderItem orderItem = new OrderItem (); orderItem.setOrderId(order.getId()); orderItem.setProductId(itemRequest.getProductId()); orderItem.setQuantity(itemRequest.getQuantity()); orderItem.setPrice(product.getPrice()); orderItems.add(orderItem); totalAmount = totalAmount.add( product.getPrice().multiply(BigDecimal.valueOf(itemRequest.getQuantity())) ); } if (!orderItems.isEmpty()) { orderItemMapper.batchInsert(orderItems); } order.setTotalAmount(totalAmount); orderMapper.updateAmount(order.getId(), totalAmount); return order; } @Transactional(readOnly = true) public OrderDetailVO getOrderDetail (Long orderId) { Order order = orderMapper.findById(orderId); if (order == null ) { throw new OrderNotFoundException ("Order not found: " + orderId); } List<OrderItem> orderItems = orderItemMapper.findByOrderId(orderId); return OrderDetailVO.builder() .order(order) .items(orderItems) .build(); } }
5.2 统一异常处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 @RestControllerAdvice @Slf4j public class GlobalExceptionHandler { @ExceptionHandler(ValidationException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public ApiResponse<Void> handleValidationException (ValidationException e) { log.warn("Validation error: {}" , e.getMessage()); return ApiResponse.error("VALIDATION_ERROR" , e.getMessage()); } @ExceptionHandler(MethodArgumentNotValidException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public ApiResponse<Void> handleMethodArgumentNotValid (MethodArgumentNotValidException e) { List<String> errors = e.getBindingResult() .getFieldErrors() .stream() .map(error -> error.getField() + ": " + error.getDefaultMessage()) .collect(Collectors.toList()); return ApiResponse.error("VALIDATION_ERROR" , String.join(", " , errors)); } @ExceptionHandler(DataIntegrityViolationException.class) @ResponseStatus(HttpStatus.CONFLICT) public ApiResponse<Void> handleDataIntegrityViolation (DataIntegrityViolationException e) { log.error("Data integrity violation" , e); return ApiResponse.error("DATA_CONFLICT" , "Data conflict occurred" ); } @ExceptionHandler(OptimisticLockingFailureException.class) @ResponseStatus(HttpStatus.CONFLICT) public ApiResponse<Void> handleOptimisticLockingFailure (OptimisticLockingFailureException e) { log.warn("Optimistic locking failure: {}" , e.getMessage()); return ApiResponse.error("OPTIMISTIC_LOCK_ERROR" , "Resource has been modified by another user" ); } @ExceptionHandler(Exception.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public ApiResponse<Void> handleGenericException (Exception e) { log.error("Unexpected error occurred" , e); return ApiResponse.error("INTERNAL_ERROR" , "An unexpected error occurred" ); } }
5.3 接口文档与验证 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 @Data @Builder @NoArgsConstructor @AllArgsConstructor public class ApiResponse <T> { private boolean success; private String code; private String message; private T data; private Long timestamp; public static <T> ApiResponse<T> success (T data) { return ApiResponse.<T>builder() .success(true ) .code("SUCCESS" ) .data(data) .timestamp(System.currentTimeMillis()) .build(); } public static <T> ApiResponse<T> error (String code, String message) { return ApiResponse.<T>builder() .success(false ) .code(code) .message(message) .timestamp(System.currentTimeMillis()) .build(); } } @Data @Valid public class CreateUserRequest { @NotBlank(message = "用户名不能为空") @Size(min = 2, max = 20, message = "用户名长度必须在2-20个字符之间") @Pattern(regexp = "^[a-zA-Z0-9_\\u4e00-\\u9fa5]+$", message = "用户名只能包含字母、数字、下划线和中文") private String username; @NotBlank(message = "邮箱不能为空") @Email(message = "邮箱格式不正确") private String email; @NotBlank(message = "密码不能为空") @Size(min = 8, max = 20, message = "密码长度必须在8-20个字符之间") @Pattern( regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]+$", message = "密码必须包含大小写字母、数字和特殊字符" ) private String password; @NotNull(message = "年龄不能为空") @Min(value = 1, message = "年龄必须大于0") @Max(value = 150, message = "年龄不能超过150") private Integer age; @NotEmpty(message = "角色不能为空") private List<@NotBlank(message = "角色名称不能为空") String> roles; }
5.4 性能监控与优化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 @Aspect @Component @Slf4j public class PerformanceMonitorAspect { @Around("@annotation(org.springframework.web.bind.annotation.RequestMapping) || " + "@annotation(org.springframework.web.bind.annotation.GetMapping) || " + "@annotation(org.springframework.web.bind.annotation.PostMapping) || " + "@annotation(org.springframework.web.bind.annotation.PutMapping) || " + "@annotation(org.springframework.web.bind.annotation.DeleteMapping)") public Object monitorPerformance (ProceedingJoinPoint joinPoint) throws Throwable { long startTime = System.currentTimeMillis(); String methodName = joinPoint.getSignature().toShortString(); try { Object result = joinPoint.proceed(); long endTime = System.currentTimeMillis(); long executionTime = endTime - startTime; if (executionTime > 1000 ) { log.warn("Slow API detected: {} took {}ms" , methodName, executionTime); } else { log.info("API performance: {} took {}ms" , methodName, executionTime); } return result; } catch (Exception e) { long endTime = System.currentTimeMillis(); log.error("API error: {} took {}ms, error: {}" , methodName, endTime - startTime, e.getMessage()); throw e; } } } @Component public class DataSourceHealthIndicator implements HealthIndicator { @Autowired private DataSource dataSource; @Override public Health health () { try { if (dataSource instanceof HikariDataSource) { HikariDataSource hikariDataSource = (HikariDataSource) dataSource; HikariPoolMXBean poolBean = hikariDataSource.getHikariPoolMXBean(); return Health.up() .withDetail("database" , "MySQL" ) .withDetail("activeConnections" , poolBean.getActiveConnections()) .withDetail("idleConnections" , poolBean.getIdleConnections()) .withDetail("totalConnections" , poolBean.getTotalConnections()) .withDetail("threadsAwaitingConnection" , poolBean.getThreadsAwaitingConnection()) .build(); } try (Connection connection = dataSource.getConnection()) { return Health.up() .withDetail("database" , connection.getMetaData().getDatabaseProductName()) .build(); } } catch (Exception e) { return Health.down() .withDetail("error" , e.getMessage()) .build(); } } }
5.5 高频面试综合题 Q1: 在微服务架构中,如何设计一个高并发的订单系统?
A: 设计高并发订单系统需要考虑以下几个方面:
1. 架构设计:
使用分布式架构,订单服务、库存服务、支付服务分离
引入消息队列处理异步任务
使用Redis做缓存和分布式锁
数据库读写分离,分库分表
2. 并发控制:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 @Service public class OrderService { @Autowired private RedisTemplate<String, String> redisTemplate; @Transactional(rollbackFor = Exception.class) public Order createOrder (CreateOrderRequest request) { String lockKey = "order:lock:" + request.getUserId(); Boolean locked = redisTemplate.opsForValue() .setIfAbsent(lockKey, "1" , Duration.ofSeconds(30 )); if (!locked) { throw new OrderCreationException ("Order creation in progress" ); } try { return processOrder(request); } finally { redisTemplate.delete(lockKey); } } }
3. 性能优化:
使用批量操作减少数据库交互
合理使用缓存策略
异步处理非核心业务逻辑
数据库连接池优化
Q2: 如何处理分布式事务?
A: 分布式事务处理方案:
1. 2PC/3PC协议:
强一致性,但性能较差
适用于对一致性要求极高的场景
2. TCC模式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @TccTransaction public class OrderTccService { public void tryCreateOrder (CreateOrderRequest request) { orderService.reserveOrder(request); stockService.reserveStock(request.getItems()); paymentService.reservePayment(request.getPaymentInfo()); } public void confirmCreateOrder (CreateOrderRequest request) { orderService.confirmOrder(request); stockService.confirmStock(request.getItems()); paymentService.confirmPayment(request.getPaymentInfo()); } public void cancelCreateOrder (CreateOrderRequest request) { orderService.cancelOrder(request); stockService.cancelStock(request.getItems()); paymentService.cancelPayment(request.getPaymentInfo()); } }
3. 消息队列最终一致性:
1 2 3 4 5 6 7 8 9 10 11 12 @Service public class OrderEventService { @EventListener @Async public void handleOrderCreated (OrderCreatedEvent event) { notificationService.sendOrderNotification(event.getOrder()); inventoryService.updateInventory(event.getOrderItems()); pointsService.addPoints(event.getUserId(), event.getAmount()); } }
Q3: Spring Boot应用如何优化启动速度?
A: Spring Boot启动优化策略:
1. 依赖优化:
移除不必要的依赖
使用spring-boot-starter-web替代完整的spring-web
避免引入大量自动配置类
2. 配置优化:
1 2 3 4 5 6 7 8 9 10 spring: main: lazy-initialization: true jpa: hibernate: ddl-auto: none show-sql: false devtools: restart: enabled: false
3. JVM优化:
1 2 3 4 5 6 java -XX:+UnlockExperimentalVMOptions -XX:+UseZGC -XX:+UseTransparentHugePages -XX:MaxMetaspaceSize=256m -Xms512m -Xmx1024m -jar application.jar
4. 代码优化:
使用@Lazy注解延迟Bean初始化
避免在@PostConstruct中执行耗时操作
合理使用@ConditionalOn*注解
好的,这是一份详细的解释,我们来逐个分析这些 Java 虚拟机(JVM)参数:
Bash
1 java -XX:+UnlockExperimentalVMOptions -XX:+UseZGC -XX:+UseTransparentHugePages -XX:MaxMetaspaceSize=256m -Xms512m -Xmx1024m -jar application.jar
内存管理和垃圾回收 这部分参数主要用于优化 JVM 的内存使用和垃圾回收机制,以获得更好的性能。
-XX:+UnlockExperimentalVMOptions
: 这个参数是用来解锁实验性(experimental)的 JVM 选项 。ZGC 曾经是实验性功能,所以需要这个参数才能使用。
-XX:+UseZGC
: 这条命令是启用 Z 垃圾回收器(Z Garbage Collector) 。ZGC 是一种为超大堆内存 (从几GB到几十TB)设计的、低延迟 的垃圾回收器。它的主要目标是在垃圾回收时,尽量减少应用程序的停顿时间,这对于需要极低延迟的应用程序非常重要。
-XX:+UseTransparentHugePages
: 这个参数开启了透明大页(Transparent Huge Pages)的支持。在操作系统层面,通常内存是以 4KB 的小页来管理的。而大页(通常是 2MB 或 1GB)可以减少 CPU 在内存管理上的开销,从而提升性能,尤其对于内存占用较大 的应用效果更明显。
-XX:MaxMetaspaceSize=256m
: 这个参数设置了元空间(Metaspace)的最大大小为 256MB 。元空间是 JVM 存储类的元数据(如类的名称、方法、字段等信息)的地方。在 Java 8 之后,它取代了之前的“永久代”(PermGen)。
-Xms512m
: 这条命令设定了 JVM 堆的初始内存为 512MB 。堆是用来存放 Java 对象的地方。-Xms
决定了 JVM 启动时会申请的最小内存。
-Xmx1024m
: 这条命令设定了 JVM 堆的最大内存为 1024MB(即 1GB) 。这是 JVM 运行时可以使用的最大内存。当堆内存用尽时,JVM 会触发垃圾回收,如果依然不足,就会抛出 OutOfMemoryError
。
好的,我们来更详细地聊聊 JVM 堆内存的结构,特别是分代设计和具体的划分比例。
堆内存的传统分代结构 在绝大多数的垃圾回收器中(例如 CMS、G1 之前的串行和并行 GC),堆内存被分为以下几个代(Generation):
1. 年轻代(Young Generation) 年轻代是新对象的诞生地。 绝大多数对象,比如局部变量创建的临时对象,都会在年轻代被创建。年轻代被设计成较小且垃圾回收频率高的区域。
Eden 区 :这是年轻代的主要部分,新创建的对象首先被分配到这里。
Survivor 区(幸存者区) :有两个大小相等的 Survivor 区,通常命名为 S0 和 S1 。它们的作用是保存每次垃圾回收后,还存活的对象。
划分比例
在默认情况下,年轻代内部的划分比例通常是:
Eden 区 : S0 区 : S1 区 = 8 : 1 : 1
这个比例可以通过 JVM 参数进行调整,比如使用 -XX:SurvivorRatio=8
。这意味着 Eden 区的大小是单个 Survivor 区的 8 倍。
2. 老年代(Old Generation) 老年代用于存放生命周期较长的对象。 当一个对象在年轻代经过多次垃圾回收(通常是 15 次,这个次数也可以通过 -XX:MaxTenuringThreshold
参数调整)后仍然存活,或者年轻代放不下的大对象,就会被“晋升”到老年代。
老年代的垃圾回收频率远低于年轻代,但每次回收的开销更大。
划分比例
堆内存中,年轻代和老年代的默认划分比例通常是:
例如,如果你设置 -Xms1200m -Xmx1200m
,那么年轻代大约是 400MB,老年代大约是 800MB。这个比例可以通过 -XX:NewRatio
参数来调整。例如,-XX:NewRatio=2
表示老年代与年轻代的大小比值为 2:1。
垃圾回收过程(以 Minor GC 为例) 理解了这些区域,我们再来看看垃圾回收是怎么进行的:
当 Eden 区 满了,会触发一次 Minor GC 。
Minor GC 会检查 Eden 区和其中一个 Survivor 区(比如 S0)。
它会将所有还存活的对象 复制到另一个空的 Survivor 区(比如 S1)。
同时,那些不再被引用的对象 则会被清理。
所有被复制到 S1 区的对象,它们的年龄(age
)会加一。
下一次 Minor GC 发生时,同样会扫描 Eden 区和 S1 区,将存活对象复制到 S0 区,清空 Eden 和 S1 区,并增加对象的年龄。
当对象的年龄达到某个阈值时,它就会被晋升 到老年代。
现代垃圾回收器(如 G1 和 ZGC) 值得注意的是,像 G1 垃圾回收器 已经打破了这种严格的分代比例。它将堆划分为一个个大小相等的区域(Region) ,每个区域都可以是 Eden、Survivor 或者老年代。G1 能够更智能地选择要回收的区域,从而在保证低停顿的同时,提高吞吐量。
而 ZGC 则更进一步,如我之前所说,它完全没有年轻代、老年代的概念 ,而是通过着色指针和读屏障技术,在不中断应用程序的情况下并发地进行垃圾回收,实现了更低的停顿。
因此,当你使用 ZGC 时,传统的分代比例就不适用了。不过,了解传统的分代结构对于理解 Java 内存管理的基础依然非常重要。
以下是针对Spring Cloud Alibaba及其他分布式技术的深度补充,包含实现原理和核心组件的详细说明:
六、Spring Cloud与分布式技术详解 6.1 Spring Cloud Alibaba核心组件 Nacos深度解析 服务发现原理 :
注册流程 :
服务启动时向Nacos Server发送注册请求(HTTP/GRPC)
注册信息包含元数据、健康检查方式(TCP/HTTP/MySQL)
客户端本地缓存服务列表(故障转移)
健康检查机制 :
1 2 3 4 5 6 7 8 spring: cloud: nacos: discovery: health-check-enabled: true health-check-interval: 10s health-check-timeout: 5s
配置中心实现 :
长轮询机制 (Push+Pull混合模式):
客户端发起长轮询请求(默认30秒超时)
服务端配置变更时立即响应
客户端收到变更后拉取最新配置
1 2 3 4 5 6 7 @RefreshScope @RestController public class ConfigController { @Value("${app.config.item}") private String configItem; }
Sentinel核心原理 流量控制规则 :
滑动窗口算法 :
统计周期(1秒)分为多个格子(如20个50ms)
实时淘汰过期格子数据
QPS计算基于当前窗口总请求数
熔断降级策略 :
策略类型
计算公式
适用场景
慢调用比例
响应时间 > RT阈值 && 比例 > 阈值
接口性能波动
异常比例
异常数 / 请求数 > 阈值
依赖服务不稳定
异常数
异常数 > 阈值(时间窗口内)
关键业务熔断
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @PostConstruct public void initRules () { FlowRuleManager.register2Property( new NacosDataSourceWrapper ( "nacos-server:8848" , "sentinel-flow-rules" , new Converter <String, List<FlowRule>>() { @Override public List<FlowRule> convert (String source) { return JSON.parseArray(source, FlowRule.class); } } ) ); }
RocketMQ集成 消息轨迹追踪 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @Bean public RocketMQTemplate rocketMQTemplate () { RocketMQTemplate template = new RocketMQTemplate (); template.setProducer(new DefaultMQProducer ("producer_group" ) {{ setVipChannelEnabled(false ); setTraceDispatcher(new AsyncTraceDispatcher ("trace_topic" )); }}); return template; } @RocketMQMessageListener( topic = "order_topic", consumerGroup = "order_consumer", enableMsgTrace = true ) public class OrderConsumer implements RocketMQListener <String> { @Override public void onMessage (String message) { } }
6.2 分布式事务增强(Seata原理) AT模式工作流程
一阶段 :
解析SQL生成前后镜像
注册分支事务到TC(Transaction Coordinator)
本地事务提交前记录undo_log
二阶段 :
成功:异步删除undo_log
失败:根据undo_log补偿(反向SQL)
undo_log表示例 :
1 2 3 4 5 6 7 8 9 10 11 12 CREATE TABLE `undo_log` ( `id` bigint NOT NULL AUTO_INCREMENT, `branch_id` bigint NOT NULL , `xid` varchar (100 ) NOT NULL , `context` varchar (128 ) NOT NULL , `rollback_info` longblob NOT NULL , `log_status` int NOT NULL , `log_created` datetime NOT NULL , `log_modified` datetime NOT NULL , PRIMARY KEY (`id`), UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`) );
TCC模式最佳实践 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public interface AccountTccService { @TwoPhaseBusinessAction( name = "deduct", commitMethod = "confirmDeduct", rollbackMethod = "cancelDeduct" ) boolean prepareDeduct ( @BusinessActionContextParameter(paramName = "userId") String userId, @BusinessActionContextParameter(paramName = "amount") BigDecimal amount ) ; boolean confirmDeduct (BusinessActionContext context) ; boolean cancelDeduct (BusinessActionContext context) ; } @GlobalTransactional public void placeOrder (Order order) { inventoryTccService.prepare(null , order.getProductId(), order.getCount()); accountTccService.prepareDeduct(order.getUserId(), order.getAmount()); orderMapper.insert(order); }
6.3 分布式缓存深度优化 Redis多级缓存架构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 @Bean public CacheManager cacheManager (RedisConnectionFactory factory) { return new CaffeineRedisCacheManager ( Caffeine.newBuilder() .expireAfterWrite(10 , TimeUnit.MINUTES) .maximumSize(1000 ), RedisCacheWriter.nonLockingRedisCacheWriter(factory), RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofHours(1 )) ); } public <T> T getWithHotspotProtection (String key, Class<T> type) { T value = localCache.getIfPresent(key); if (value != null ) return value; value = redisTemplate.opsForValue().get(key); if (value != null ) { localCache.put(key, value); return value; } String lockKey = "lock:" + key; try { if (redisLock.tryLock(lockKey, 3 , 30 )) { value = databaseLoader.load(key); redisTemplate.opsForValue().set( key, value, ThreadLocalRandom.current().nextInt(30 ) + 30 , TimeUnit.MINUTES ); localCache.put(key, value); } } finally { redisLock.unlock(lockKey); } return value; }
6.4 消息队列高级特性 RocketMQ事务消息 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public void sendTransactionMessage (Order order) { TransactionSendResult result = rocketMQTemplate.sendMessageInTransaction( "order-tx-group" , MessageBuilder.withPayload(order) .setHeader(RocketMQHeaders.TRANSACTION_ID, order.getId()) .build(), order ); } @RocketMQTransactionListener(txProducerGroup = "order-tx-group") public class OrderTransactionListenerImpl implements RocketMQLocalTransactionListener { @Override public RocketMQLocalTransactionState executeLocalTransaction (Message msg, Object arg) { try { Order order = (Order) arg; orderService.createOrder(order); return RocketMQLocalTransactionState.COMMIT; } catch (Exception e) { return RocketMQLocalTransactionState.ROLLBACK; } } @Override public RocketMQLocalTransactionState checkLocalTransaction (Message msg) { String orderId = msg.getHeaders().get("TRANSACTION_ID" ).toString(); return orderService.exists(orderId) ? RocketMQLocalTransactionState.COMMIT : RocketMQLocalTransactionState.ROLLBACK; } }
Kafka精确一次语义 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @Bean public ProducerFactory<String, String> producerFactory () { Map<String, Object> configs = new HashMap <>(); configs.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, true ); configs.put(ProducerConfig.TRANSACTIONAL_ID_CONFIG, "tx-producer-1" ); return new DefaultKafkaProducerFactory <>(configs); } @Bean public ConsumerFactory<String, String> consumerFactory () { Map<String, Object> configs = new HashMap <>(); configs.put(ConsumerConfig.ISOLATION_LEVEL_CONFIG, "read_committed" ); return new DefaultKafkaConsumerFactory <>(configs); } @KafkaListener(topics = "order-topic") @Transactional public void processOrder (ConsumerRecord<String, String> record) { OrderEvent event = parseEvent(record.value()); orderService.process(event); }
七、云原生支持(新增) 7.1 Kubernetes集成方案 服务发现适配 1 2 3 4 5 6 7 8 9 10 spring: cloud: kubernetes: discovery: all-namespaces: true nacos: discovery: server-addr: ${NACOS_HOST:nacos-headless}:${NACOS_PORT:8848} namespace: ${POD_NAMESPACE:default}
配置管理方案 1 2 3 4 5 6 7 8 9 10 11 @Configuration @ConfigurationProperties(prefix = "app") @RefreshScope public class AppConfig { @Value("${configFromNacos}") private String nacosConfig; @Value("${configFromK8s}") private String k8sConfig; }
7.2 Service Mesh整合 Istio流量治理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: order-service spec: hosts: - order-service http: - route: - destination: host: order-service subset: v1 weight: 90 - destination: host: order-service subset: v2 weight: 10
八、性能优化深度实践 8.1 全链路压测方案 影子库表配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 spring: shardingsphere: datasource: names: ds-real,ds-shadow rules: shadow: enable: true data-sources: shadow-data-source: source-data-source-name: ds-real shadow-data-source-name: ds-shadow tables: t_order: shadow-algorithm-names: [simple-hint-algorithm ]
流量染色标记 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class PressureTestContext { private static final ThreadLocal<Boolean> FLAG = ThreadLocal.withInitial(() -> false ); public static void markPressureTest () { FLAG.set(true ); } public static boolean isPressureTest () { return FLAG.get(); } } @Intercepts(@Signature(type=Executor.class, method="update", args={MappedStatement.class,Object.class})) public class ShadowDbInterceptor implements Interceptor { @Override public Object intercept (Invocation invocation) throws Throwable { if (PressureTestContext.isPressureTest()) { RoutingContext.setShadowDataSource(); } return invocation.proceed(); } }