Java秋招之Spring生态系统
第6章 Spring生态系统
面试重要程度:⭐⭐⭐⭐⭐
常见提问方式:Spring IOC原理、AOP实现、循环依赖解决
预计阅读时间:45分钟
开场白
兄弟,Spring绝对是Java后端面试的重中之重!我敢说,99%的Java面试都会问到Spring相关的问题。不管是IOC、AOP,还是Spring Boot,这些都是必考内容。
今天我们就把Spring的核心原理彻底搞清楚,让你在面试中展现出对Spring框架的深度理解。
🌱 6.1 Spring Boot 3.0+新特性
GraalVM原生镜像支持
面试热点:
面试官:"Spring Boot 3.0有什么重要的新特性?GraalVM原生镜像有什么优势?"
核心特性:
// 1. 支持AOT(Ahead-of-Time)编译
@SpringBootApplication
public class NativeApplication {
public static void main(String[] args) {
SpringApplication.run(NativeApplication.class, args);
}
}
// 2. 构建原生镜像
// mvn -Pnative native:compile
// 或者使用Docker
// docker build -f Dockerfile.native -t myapp-native .
原生镜像的优势:
启动时间:从秒级降到毫秒级(50-100ms) 内存占用:从几百MB降到几十MB 镜像大小:从几百MB降到几十MB 冷启动:适合Serverless和容器化场景
实际对比:
# 传统JVM应用 启动时间:2-5秒 内存占用:200-500MB 镜像大小:200-300MB # GraalVM原生镜像 启动时间:50-100ms 内存占用:20-50MB 镜像大小:50-100MB
响应式编程WebFlux
面试重点:
// 传统的Spring MVC(阻塞式)
@RestController
public class TraditionalController {
@Autowired
private UserService userService;
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
return userService.findById(id); // 阻塞调用
}
}
// Spring WebFlux(响应式)
@RestController
public class ReactiveController {
@Autowired
private ReactiveUserService userService;
@GetMapping("/users/{id}")
public Mono<User> getUser(@PathVariable Long id) {
return userService.findById(id); // 非阻塞调用
}
@GetMapping("/users")
public Flux<User> getAllUsers() {
return userService.findAll()
.delayElements(Duration.ofMillis(100)); // 背压处理
}
}
响应式编程的优势:
// 高并发处理能力
@Service
public class ReactiveUserService {
@Autowired
private ReactiveUserRepository repository;
public Flux<User> findAll() {
return repository.findAll()
.publishOn(Schedulers.parallel()) // 并行处理
.map(this::enrichUser) // 数据增强
.filter(user -> user.isActive()) // 过滤
.onErrorResume(ex -> { // 错误处理
log.error("Error processing user", ex);
return Flux.empty();
});
}
private User enrichUser(User user) {
// 异步调用其他服务
return user;
}
}
🏗️ 6.2 IOC容器核心原理
Bean生命周期详解
面试必问:
面试官:"说说Spring Bean的生命周期,每个阶段都做什么?"
完整生命周期:
@Component
public class LifecycleBean implements BeanNameAware, BeanFactoryAware,
ApplicationContextAware, InitializingBean, DisposableBean {
private String beanName;
private BeanFactory beanFactory;
private ApplicationContext applicationContext;
// 1. 构造器
public LifecycleBean() {
System.out.println("1. 构造器执行");
}
// 2. 设置属性
@Value("${app.name:default}")
private String appName;
// 3. Aware接口回调
@Override
public void setBeanName(String name) {
this.beanName = name;
System.out.println("3. setBeanName: " + name);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
System.out.println("3. setBeanFactory");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
System.out.println("3. setApplicationContext");
}
// 4. BeanPostProcessor前置处理
// 由框架调用,这里只是说明
// 5. InitializingBean接口
@Override
public void afterPropertiesSet() {
System.out.println("5. afterPropertiesSet");
}
// 6. 自定义初始化方法
@PostConstruct
public void customInit() {
System.out.println("6. @PostConstruct");
}
// 7. BeanPostProcessor后置处理
// 由框架调用,这里只是说明
// 8. Bean可以使用了
public void doSomething() {
System.out.println("8. Bean正在工作...");
}
// 9. DisposableBean接口
@Override
public void destroy() {
System.out.println("9. destroy");
}
// 10. 自定义销毁方法
@PreDestroy
public void customDestroy() {
System.out.println("10. @PreDestroy");
}
}
自定义BeanPostProcessor:
@Component
public class CustomBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
if (bean instanceof LifecycleBean) {
System.out.println("4. BeanPostProcessor前置处理: " + beanName);
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean instanceof LifecycleBean) {
System.out.println("7. BeanPostProcessor后置处理: " + beanName);
}
return bean;
}
}
循环依赖三级缓存解决方案
面试高频:
面试官:"Spring是如何解决循环依赖的?三级缓存是什么?"
循环依赖场景:
@Service
public class ServiceA {
@Autowired
private ServiceB serviceB;
public void methodA() {
serviceB.methodB();
}
}
@Service
public class ServiceB {
@Autowired
private ServiceA serviceA;
public void methodB() {
serviceA.methodA();
}
}
三级缓存机制:
public class DefaultSingletonBeanRegistry {
// 一级缓存:完成初始化的单例Bean
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 二级缓存:完成实例化但未初始化的Bean
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
// 三级缓存:单例Bean的工厂
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 1. 从一级缓存获取
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
// 2. 从二级缓存获取
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
// 3. 从三级缓存获取
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
// 放入二级缓存
this.earlySingletonObjects.put(beanName, singletonObject);
// 从三级缓存移除
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
}
解决过程详解:
// 循环依赖解决过程 // 1. 创建ServiceA // - 实例化ServiceA(构造器) // - 将ServiceA的ObjectFactory放入三级缓存 // - 填充ServiceA的属性(发现需要ServiceB) // 2. 创建ServiceB // - 实例化ServiceB(构造器) // - 将ServiceB的ObjectFactory放入三级缓存 // - 填充ServiceB的属性(发现需要ServiceA) // 3. 获取ServiceA // - 从三级缓存获取ServiceA的ObjectFactory // - 调用ObjectFactory.getObject()获取ServiceA实例 // - 将ServiceA放入二级缓存,从三级缓存移除 // 4. ServiceB初始化完成 // - ServiceB获得ServiceA的引用 // - ServiceB初始化完成,放入一级缓存 // 5. ServiceA初始化完成 // - ServiceA获得ServiceB的引用 // - ServiceA初始化完成,放入一级缓存
@Autowired vs @Resource
面试对比:
@Service
public class InjectionDemo {
// @Autowired:Spring注解,按类型注入
@Autowired
private UserService userService1;
// @Autowired + @Qualifier:指定Bean名称
@Autowired
@Qualifier("userServiceImpl")
private UserService userService2;
// @Resource:JDK注解,按名称注入
@Resource(name = "userServiceImpl")
private UserService userService3;
// @Resource:如果不指定name,按字段名查找
@Resource
private UserService userServiceImpl; // 会查找名为userServiceImpl的Bean
}
注入顺序对比:
@Autowired注入顺序: 1. 按类型查找Bean 2. 如果有多个,按@Primary注解 3. 如果没有@Primary,按@Qualifier指定 4. 如果没有@Qualifier,按字段名匹配 @Resource注入顺序: 1. 如果指定name,按name查找 2. 如果没有指定name,按字段名查找 3. 如果按名称找不到,按类型查找 4. 如果按类型找到多个,报错
🎯 6.3 AOP面向切面编程
JDK动态代理 vs CGLIB
面试重点:
面试官:"Spring AOP是如何实现的?JDK动态代理和CGLIB有什么区别?"
JDK动态代理实现:
// 1. 目标接口
public interface UserService {
void saveUser(String username);
User findUser(Long id);
}
// 2. 目标实现类
@Service
public class UserServiceImpl implements UserService {
@Override
public void saveUser(String username) {
System.out.println("保存用户: " + username);
}
@Override
public User findUser(Long id) {
System.out.println("查找用户: " + id);
return new User(id, "user" + id);
}
}
// 3. JDK动态代理
public class JdkProxyDemo {
public static void main(String[] args) {
UserService target = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("JDK代理 - 方法执行前: " + method.getName());
Object result = method.invoke(target, args);
System.out.println("JDK代理 - 方法执行后: " + method.getName());
return result;
}
}
);
proxy.saveUser("张三");
}
}
CGLIB代理实现:
// 1. 目标类(不需要接口)
@Service
public class OrderService {
public void createOrder(String orderNo) {
System.out.println("创建订单: " + orderNo);
}
}
// 2. CGLIB代理
public class CglibProxyDemo {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OrderService.class);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("CGLIB代理 - 方法执行前: " + method.getName());
Object result = proxy.invokeSuper(obj, args);
System.out.println("CGLIB代理 - 方法执行后: " + method.getName());
return result;
}
});
OrderService proxy = (OrderService) enhancer.create();
proxy.createOrder("ORDER001");
}
}
两种代理方式对比:
JDK动态代理: ✅ JDK原生支持,无需额外依赖 ✅ 性能较好(方法调用) ❌ 只能代理接口,不能代理类 ❌ 目标类必须实现接口 CGLIB代理: ✅ 可以代理类,不需要接口 ✅ 功能更强大 ❌ 需要额外依赖 ❌ 不能代理final类和final方法 ❌ 性能稍差(字节码生成)
Spring AOP实现原理
切面定义:
@Aspect
@Component
public class LoggingAspect {
// 切点表达式
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceLayer() {}
// 前置通知
@Before("serviceLayer()")
public void logBefore(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
System.out.println("方法执行前: " + methodName + ", 参数: " + Arrays.toString(args));
}
// 后置通知
@After("serviceLayer()")
public void logAfter(JoinPoint joinPoint) {
System.out.println("方法执行后: " + joinPoint.getSignatu
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
Java面试圣经 文章被收录于专栏
Java面试圣经,带你练透java圣经
查看9道真题和解析