核心概念

Spring框架的两大核心特性:

  • IOC(Inversion of Control,控制反转):对象创建和依赖管理的控制权反转
  • AOP(Aspect Oriented Programming,面向切面编程):横切关注点的模块化

这两个特性相辅相成,构成了Spring框架的基石。

IOC(控制反转)

什么是IOC

控制反转是一种设计思想,将对象的创建、配置和生命周期管理的控制权从应用程序代码转移到Spring容器。

传统方式

public class UserService {
    // 应用程序主动创建依赖
    private UserDao userDao = new UserDaoImpl();
    
    public User findUser(Long id) {
        return userDao.findById(id);
    }
}

IOC方式

@Service
public class UserService {
    // 容器注入依赖
    @Autowired
    private UserDao userDao;
    
    public User findUser(Long id) {
        return userDao.findById(id);
    }
}

依赖注入(DI)

依赖注入是IOC的具体实现方式,Spring通过DI将依赖对象注入到目标对象中。

三种注入方式

@Service
public class OrderService {
    
    // 1. 构造器注入(推荐)
    private final OrderRepository orderRepository;
    private final UserService userService;
    
    @Autowired
    public OrderService(OrderRepository orderRepository, UserService userService) {
        this.orderRepository = orderRepository;
        this.userService = userService;
    }
    
    // 2. Setter注入
    private EmailService emailService;
    
    @Autowired
    public void setEmailService(EmailService emailService) {
        this.emailService = emailService;
    }
    
    // 3. 字段注入(不推荐)
    @Autowired
    private LogService logService;
}

推荐顺序:构造器注入 > Setter注入 > 字段注入

IOC容器

// BeanFactory - 基础容器
BeanFactory factory = new XmlBeanFactory(new ClassPathResource("beans.xml"));
User user = (User) factory.getBean("user");

// ApplicationContext - 增强容器(推荐)
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = context.getBean(UserService.class);

ApplicationContext的增强功能

  • 国际化支持(MessageSource)
  • 事件发布(ApplicationEventPublisher)
  • 资源加载(ResourceLoader)
  • AOP集成
  • Web应用支持

Bean生命周期

实例化 → 属性填充 → Aware接口回调 → 
初始化前处理 → 初始化 → 初始化后处理 → 
使用 → 销毁

关键扩展点:

  • BeanPostProcessor:初始化前后处理
  • InitializingBean:初始化回调
  • DisposableBean:销毁回调
  • @PostConstruct@PreDestroy:JSR-250注解

AOP(面向切面编程)

什么是AOP

面向切面编程是一种编程范式,用于将横切关注点(cross-cutting concerns)从业务逻辑中分离出来。

横切关注点:分散在多个模块中的通用功能,如:

  • 日志记录
  • 事务管理
  • 权限控制
  • 性能监控
  • 异常处理

AOP核心概念

@Aspect
@Component
public class LoggingAspect {
    
    // 切点:定义在哪里切入
    @Pointcut("execution(* com.example.service.*.*(..))")
    public void serviceLayer() {}
    
    // 前置通知:方法执行前
    @Before("serviceLayer()")
    public void logBefore(JoinPoint joinPoint) {
        System.out.println("Before: " + joinPoint.getSignature().getName());
    }
    
    // 后置通知:方法执行后
    @After("serviceLayer()")
    public void logAfter(JoinPoint joinPoint) {
        System.out.println("After: " + joinPoint.getSignature().getName());
    }
    
    // 返回通知:方法正常返回后
    @AfterReturning(pointcut = "serviceLayer()", returning = "result")
    public void logAfterReturning(JoinPoint joinPoint, Object result) {
        System.out.println("AfterReturning: " + result);
    }
    
    // 异常通知:方法抛出异常后
    @AfterThrowing(pointcut = "serviceLayer()", throwing = "ex")
    public void logAfterThrowing(JoinPoint joinPoint, Exception ex) {
        System.out.println("AfterThrowing: " + ex.getMessage());
    }
    
    // 环绕通知:完全控制方法执行
    @Around("serviceLayer()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        
        try {
            Object result = joinPoint.proceed();  // 执行目标方法
            return result;
        } finally {
            long end = System.currentTimeMillis();
            System.out.println("执行时间: " + (end - start) + "ms");
        }
    }
}

AOP术语

术语 说明 示例
切面(Aspect) 横切关注点的模块化 @Aspect
连接点(Join Point) 程序执行的某个点 方法调用、异常抛出
切点(Pointcut) 匹配连接点的表达式 execution(* service.*.*(..))
通知(Advice) 在切点执行的动作 @Before@After
目标对象(Target) 被代理的对象 业务Service
代理(Proxy) AOP创建的对象 JDK代理或CGLIB代理
织入(Weaving) 将切面应用到目标对象 运行时织入

AOP实现原理

Spring AOP基于动态代理实现:

1. JDK动态代理(基于接口)

// 目标接口
public interface UserService {
    void save(User user);
}

// 目标实现
@Service
public class UserServiceImpl implements UserService {
    @Override
    public void save(User user) {
        System.out.println("保存用户: " + user.getName());
    }
}

// Spring会创建JDK动态代理
UserService proxy = (UserService) Proxy.newProxyInstance(
    classLoader,
    new Class[]{UserService.class},
    new InvocationHandler() {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) {
            // 前置处理
            System.out.println("Before: " + method.getName());
            
            // 调用目标方法
            Object result = method.invoke(target, args);
            
            // 后置处理
            System.out.println("After: " + method.getName());
            
            return result;
        }
    }
);

特点

  • 必须基于接口
  • 使用Java标准库
  • 性能较好

2. CGLIB代理(基于类)

// 目标类(没有接口)
@Service
public class OrderService {
    public void createOrder(Order order) {
        System.out.println("创建订单: " + order.getId());
    }
}

// Spring会创建CGLIB代理(继承目标类)
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OrderService.class);
enhancer.setCallback(new MethodInterceptor() {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) {
        // 前置处理
        System.out.println("Before: " + method.getName());
        
        // 调用目标方法
        Object result = proxy.invokeSuper(obj, args);
        
        // 后置处理
        System.out.println("After: " + method.getName());
        
        return result;
    }
});

OrderService proxy = (OrderService) enhancer.create();

特点

  • 不需要接口
  • 通过继承实现
  • 无法代理final类和final方法

代理选择策略

// Spring的代理选择逻辑
if (目标对象实现了接口 && proxyTargetClass=false) {
    使用JDK动态代理
} else {
    使用CGLIB代理
}

// 强制使用CGLIB
@EnableAspectJAutoProxy(proxyTargetClass = true)

切点表达式

@Aspect
@Component
public class PointcutExamples {
    
    // 1. 执行任意公共方法
    @Pointcut("execution(public * *(..))")
    public void anyPublicMethod() {}
    
    // 2. 执行以save开头的方法
    @Pointcut("execution(* save*(..))")
    public void anySave() {}
    
    // 3. 执行service包下所有方法
    @Pointcut("execution(* com.example.service.*.*(..))")
    public void serviceLayer() {}
    
    // 4. 执行service包及子包下所有方法
    @Pointcut("execution(* com.example.service..*.*(..))")
    public void serviceLayerWithSubpackages() {}
    
    // 5. 特定类的所有方法
    @Pointcut("within(com.example.service.UserService)")
    public void userServiceMethods() {}
    
    // 6. 特定包下的所有类
    @Pointcut("within(com.example.service..*)")
    public void inServiceLayer() {}
    
    // 7. 带有特定注解的方法
    @Pointcut("@annotation(com.example.annotation.Log)")
    public void methodsWithLogAnnotation() {}
    
    // 8. 带有特定注解的类中的所有方法
    @Pointcut("@within(org.springframework.stereotype.Service)")
    public void serviceAnnotatedClasses() {}
    
    // 9. 组合切点
    @Pointcut("serviceLayer() && anyPublicMethod()")
    public void publicServiceMethods() {}
}

IOC与AOP的关系

1. AOP依赖IOC

AOP的实现依赖于IOC容器:

// AOP的织入发生在Bean初始化后置处理阶段
public abstract class AbstractAutoProxyCreator implements BeanPostProcessor {
    
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        if (bean != null) {
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            
            // 判断是否需要代理
            if (shouldProxy(bean, beanName)) {
                // 创建代理对象
                return createProxy(bean, beanName, specificInterceptors);
            }
        }
        return bean;
    }
}

时序

1. IOC容器启动
2. 创建Bean(实例化、属性填充)
3. 初始化Bean
4. BeanPostProcessor后置处理 ← AOP在这里创建代理
5. 返回代理对象(或原始对象)

2. IOC管理AOP组件

// 切面本身也是Spring Bean
@Aspect
@Component  // 由IOC管理
public class LoggingAspect {
    
    @Autowired  // 可以注入其他Bean
    private LogService logService;
    
    @Around("@annotation(log)")
    public Object logAround(ProceedingJoinPoint joinPoint, Log log) {
        logService.record(log.value());  // 使用注入的Bean
        return joinPoint.proceed();
    }
}

3. 典型应用:声明式事务

@Service
public class OrderService {
    
    @Autowired  // IOC注入
    private OrderRepository orderRepository;
    
    @Transactional  // AOP实现事务管理
    public void createOrder(Order order) {
        orderRepository.save(order);
        // 如果抛出异常,AOP会回滚事务
    }
}

// @Transactional的AOP实现(简化版)
@Aspect
public class TransactionAspect {
    
    @Around("@annotation(transactional)")
    public Object manageTransaction(ProceedingJoinPoint joinPoint, Transactional transactional) {
        TransactionStatus status = transactionManager.getTransaction(transactional);
        try {
            Object result = joinPoint.proceed();
            transactionManager.commit(status);  // 提交
            return result;
        } catch (Throwable ex) {
            transactionManager.rollback(status);  // 回滚
            throw ex;
        }
    }
}

实战应用

应用1:统一日志记录

@Aspect
@Component
public class LoggingAspect {
    
    private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);
    
    @Around("@annotation(com.example.annotation.Log)")
    public Object logMethod(ProceedingJoinPoint joinPoint) throws Throwable {
        String className = joinPoint.getTarget().getClass().getSimpleName();
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        
        logger.info("调用方法: {}.{},参数: {}", className, methodName, Arrays.toString(args));
        
        long startTime = System.currentTimeMillis();
        try {
            Object result = joinPoint.proceed();
            long endTime = System.currentTimeMillis();
            logger.info("方法执行成功,耗时: {}ms,返回值: {}", endTime - startTime, result);
            return result;
        } catch (Throwable e) {
            logger.error("方法执行异常: {}.{}", className, methodName, e);
            throw e;
        }
    }
}

// 使用
@Service
public class UserService {
    
    @Log  // 自动记录日志
    public User findById(Long id) {
        return userRepository.findById(id).orElse(null);
    }
}

应用2:权限控制

// 自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequirePermission {
    String value();  // 需要的权限
}

// AOP切面
@Aspect
@Component
public class PermissionAspect {
    
    @Autowired
    private UserContext userContext;
    
    @Before("@annotation(permission)")
    public void checkPermission(RequirePermission permission) {
        User currentUser = userContext.getCurrentUser();
        if (currentUser == null) {
            throw new UnauthorizedException("未登录");
        }
        
        if (!currentUser.hasPermission(permission.value())) {
            throw new ForbiddenException("权限不足");
        }
    }
}

// 使用
@RestController
@RequestMapping("/api/admin")
public class AdminController {
    
    @RequirePermission("ADMIN")
    @PostMapping("/users")
    public User createUser(@RequestBody User user) {
        return userService.create(user);
    }
}

应用3:性能监控

@Aspect
@Component
public class PerformanceAspect {
    
    @Around("execution(* com.example.service..*.*(..))")
    public Object monitorPerformance(ProceedingJoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().toShortString();
        
        StopWatch stopWatch = new StopWatch(methodName);
        stopWatch.start();
        
        try {
            return joinPoint.proceed();
        } finally {
            stopWatch.stop();
            
            if (stopWatch.getTotalTimeMillis() > 1000) {
                System.out.println("慢方法警告: " + methodName + 
                                 " 耗时: " + stopWatch.getTotalTimeMillis() + "ms");
            }
        }
    }
}

应用4:缓存管理

@Aspect
@Component
public class CacheAspect {
    
    @Autowired
    private CacheManager cacheManager;
    
    @Around("@annotation(cacheable)")
    public Object cache(ProceedingJoinPoint joinPoint, Cacheable cacheable) throws Throwable {
        String key = generateKey(joinPoint);
        Cache cache = cacheManager.getCache(cacheable.value());
        
        // 从缓存获取
        Object cached = cache.get(key);
        if (cached != null) {
            return cached;
        }
        
        // 执行方法
        Object result = joinPoint.proceed();
        
        // 写入缓存
        cache.put(key, result);
        
        return result;
    }
    
    private String generateKey(ProceedingJoinPoint joinPoint) {
        return joinPoint.getSignature().toShortString() + 
               Arrays.toString(joinPoint.getArgs());
    }
}

应用5:异常统一处理

@Aspect
@Component
public class ExceptionHandlingAspect {
    
    private static final Logger logger = LoggerFactory.getLogger(ExceptionHandlingAspect.class);
    
    @AfterThrowing(pointcut = "execution(* com.example.service..*.*(..))", 
                   throwing = "ex")
    public void handleException(JoinPoint joinPoint, Throwable ex) {
        String methodName = joinPoint.getSignature().toShortString();
        Object[] args = joinPoint.getArgs();
        
        logger.error("方法执行异常: {},参数: {}", methodName, Arrays.toString(args), ex);
        
        // 发送告警
        alertService.sendAlert("方法执行异常", methodName, ex);
    }
}

最佳实践

IOC最佳实践

  1. 优先使用构造器注入
    @Service
    public class UserService {
     private final UserRepository userRepository;
        
     @Autowired
     public UserService(UserRepository userRepository) {
         this.userRepository = userRepository;
     }
    }
    
  2. 避免循环依赖 ```java // ❌ 坏设计 @Service public class ServiceA { @Autowired private ServiceB serviceB; }

@Service public class ServiceB { @Autowired private ServiceA serviceA; }

// ✅ 好设计:引入中间层 @Service public class ServiceFacade { @Autowired private ServiceA serviceA; @Autowired private ServiceB serviceB; }


3. **合理使用Bean作用域**
```java
@Service  // 默认singleton,无状态
public class UserService {
    // 不要有可变成员变量
}

@Component
@Scope("prototype")  // 有状态
public class ShoppingCart {
    private List<Item> items = new ArrayList<>();
}

AOP最佳实践

  1. 切点粒度要合理 ```java // ❌ 太宽泛 @Pointcut(“execution(* .(..))”) // 所有方法

// ✅ 合适 @Pointcut(“execution(* com.example.service..(..))”) // 特定包


2. **使用自定义注解**
```java
// ✅ 更清晰、可控
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Auditable {
}

@Around("@annotation(Auditable)")
public Object audit(ProceedingJoinPoint joinPoint) {
    // ...
}
  1. 避免在切面中抛出未检查异常
    @Around("serviceLayer()")
    public Object handle(ProceedingJoinPoint joinPoint) throws Throwable {
     try {
         return joinPoint.proceed();
     } catch (Exception e) {
         // 记录日志
         logger.error("Error", e);
         // 重新抛出,不要吞掉异常
         throw e;
     }
    }
    
  2. 注意代理的限制 ```java // ❌ 同类调用不会触发AOP @Service public class UserService {

    @Transactional public void methodA() { // … }

    public void methodB() { this.methodA(); // ❌ 不会触发事务,因为是内部调用 } }

// ✅ 通过代理调用 @Service public class UserService {

@Autowired
private UserService self;  // 注入自己的代理

public void methodB() {
    self.methodA();  // ✅ 触发事务
} } ```

面试总结

IOC核心要点

  1. 本质:控制权反转,由容器管理对象生命周期
  2. 实现:依赖注入(构造器、Setter、字段)
  3. 容器:BeanFactory(基础)、ApplicationContext(增强)
  4. 生命周期:实例化 → 属性填充 → 初始化 → 使用 → 销毁
  5. 扩展点:BeanPostProcessor、Aware接口等

AOP核心要点

  1. 目的:分离横切关注点,提高模块化
  2. 实现:动态代理(JDK、CGLIB)
  3. 核心概念:切面、切点、通知、连接点
  4. 通知类型:Before、After、AfterReturning、AfterThrowing、Around
  5. 应用场景:日志、事务、权限、缓存、监控

常见面试问题

Q1: IOC和DI的区别?
A: IOC是思想,DI是实现。IOC强调控制权反转,DI是具体的注入方式。

Q2: Spring AOP和AspectJ的区别?
A: Spring AOP基于动态代理,运行时织入,只支持方法级别;AspectJ基于字节码,编译时织入,功能更强大。

Q3: JDK动态代理和CGLIB的区别?
A: JDK基于接口,CGLIB基于继承。有接口优先用JDK,无接口用CGLIB。

Q4: AOP在什么时候创建代理?
A: Bean初始化后置处理阶段(BeanPostProcessor.postProcessAfterInitialization)。

Q5: @Transactional的实现原理?
A: 基于AOP,在方法执行前开启事务,执行后提交,异常时回滚。