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圣经

全部评论

相关推荐

评论
3
8
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务