Spring IOC与AOP详解

Spring框架是Java开发中最流行的框架之一,其核心特性IOC(控制反转)和AOP(面向切面编程)构成了Spring的基础。本文将深入解析这两个核心概念。

第一部分:IOC(控制反转)

什么是IOC?

IOC(Inversion of Control,控制反转)是Spring的核心思想,它将对象的创建、管理和依赖关系的维护交给Spring容器来处理,而不是由对象自己管理。

传统开发方式的问题

public class UserService {
    private UserDao userDao;

    public UserService() {
        // 强依赖:直接创建依赖对象,耦合度高
        this.userDao = new UserDaoImpl();
    }

    public void addUser(User user) {
        userDao.save(user);
    }
}

这种方式的问题:

  • 耦合度高,难以替换实现
  • 不便于单元测试
  • 对象生命周期难以管理

IOC的解决方案

public class UserService {
    private UserDao userDao;

    // 通过构造函数注入依赖
    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }

    public void addUser(User user) {
        userDao.save(user);
    }
}

IOC容器

Spring提供了两种IOC容器:

  1. BeanFactory:基础容器,提供基本的IOC功能
  2. ApplicationContext:高级容器,提供更多企业级特性

BeanFactory

// BeanFactory接口定义
public interface BeanFactory {

    // 根据名称获取Bean
    Object getBean(String name) throws BeansException;

    // 根据类型获取Bean
    <T> T getBean(Class<T> requiredType) throws BeansException;

    // 判断是否包含指定Bean
    boolean containsBean(String name);

    // 判断Bean是否是单例
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
}

ApplicationContext

// 常用的ApplicationContext实现
// 1. ClassPathXmlApplicationContext:从类路径加载配置
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

// 2. FileSystemXmlApplicationContext:从文件系统加载配置
ApplicationContext context = new FileSystemXmlApplicationContext("D:/config/applicationContext.xml");

// 3. AnnotationConfigApplicationContext:基于注解的配置
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

// 4. WebApplicationContext:Web应用的容器(在Spring MVC中使用)

依赖注入(DI)的三种方式

1. 构造函数注入(推荐)

@Service
public class UserService {
    private final UserDao userDao;
    private final EmailService emailService;

    // 构造函数注入:保证依赖不可变,易于测试
    public UserService(UserDao userDao, EmailService emailService) {
        this.userDao = userDao;
        this.emailService = emailService;
    }

    public void registerUser(User user) {
        userDao.save(user);
        emailService.sendWelcomeEmail(user.getEmail());
    }
}

2. Setter方法注入

@Service
public class UserService {
    private UserDao userDao;
    private EmailService emailService;

    @Autowired
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    @Autowired
    public void setEmailService(EmailService emailService) {
        this.emailService = emailService;
    }

    public void registerUser(User user) {
        userDao.save(user);
        emailService.sendWelcomeEmail(user.getEmail());
    }
}

3. 字段注入(不推荐)

@Service
public class UserService {
    @Autowired
    private UserDao userDao;

    @Autowired
    private EmailService emailService;

    public void registerUser(User user) {
        userDao.save(user);
        emailService.sendWelcomeEmail(user.getEmail());
    }
}

为什么推荐构造函数注入?

  • 保证依赖不可变(final)
  • 明确表达类的依赖关系
  • 便于单元测试(可以轻松mock依赖)
  • 避免循环依赖

Bean的配置方式

XML配置方式

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 扫描注解 -->
    <context:component-scan base-package="com.example"/>

    <!-- 定义Bean -->
    <bean id="userDao" class="com.example.dao.UserDaoImpl"/>

    <bean id="userService" class="com.example.service.UserService">
        <!-- 构造函数注入 -->
        <constructor-arg ref="userDao"/>
    </bean>
</beans>

注解配置方式

@Configuration
@ComponentScan("com.example")
public class AppConfig {

    @Bean
    public UserDao userDao() {
        return new UserDaoImpl();
    }

    @Bean
    public UserService userService(UserDao userDao) {
        return new UserService(userDao);
    }
}

自动装配注解

// @Component:通用的组件注解
@Component
public class UserDaoImpl implements UserDao {
    // 实现代码
}

// @Repository:数据访问层
@Repository
public class UserDaoImpl implements UserDao {
    // 实现代码
}

// @Service:业务逻辑层
@Service
public class UserService {
    // 实现代码
}

// @Controller:控制器层
@Controller
public class UserController {
    // 实现代码
}

// @Autowired:自动装配
@Service
public class UserService {
    @Autowired
    private UserDao userDao;

    @Autowired
    @Qualifier("userDaoImpl") // 指定Bean名称
    private UserDao userDao2;

    @Resource(name = "userDaoImpl") // JSR-250注解
    private UserDao userDao3;
}

Bean的生命周期

Spring Bean的生命周期可以分为以下几个阶段:

@Component
public class LifeCycleBean implements BeanNameAware, BeanFactoryAware,
        InitializingBean, DisposableBean {

    private String beanName;

    // 1. 实例化Bean(构造函数)
    public LifeCycleBean() {
        System.out.println("1. 实例化Bean");
    }

    // 2. 设置属性值
    @Autowired
    private UserDao userDao;
    public void setUserDao(UserDao userDao) {
        System.out.println("2. 设置属性值");
        this.userDao = userDao;
    }

    // 3. BeanNameAware接口回调
    @Override
    public void setBeanName(String name) {
        System.out.println("3. BeanNameAware.setBeanName: " + name);
        this.beanName = name;
    }

    // 4. BeanFactoryAware接口回调
    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
        System.out.println("4. BeanFactoryAware.setBeanFactory");
    }

    // 5. BeanPostProcessor - 前置处理
    @PostConstruct
    public void initMethod() {
        System.out.println("5. @PostConstruct初始化方法");
    }

    // 6. InitializingBean接口回调
    @Override
    public void afterPropertiesSet() {
        System.out.println("6. InitializingBean.afterPropertiesSet");
    }

    // 7. BeanPostProcessor - 后置处理
    @Bean(initMethod = "customInit")
    public void customInit() {
        System.out.println("7. 自定义初始化方法");
    }

    // 8. Bean就绪,可以使用

    // 9. 容器关闭时销毁Bean
    @PreDestroy
    public void preDestroy() {
        System.out.println("9. @PreDestroy销毁方法");
    }

    @Override
    public void destroy() {
        System.out.println("10. DisposableBean.destroy");
    }
}

Bean的作用域

// Singleton(默认):单例,整个容器中只有一个实例
@Service
@Scope("singleton")
public class SingletonBean {
    // 实现代码
}

// Prototype:原型,每次请求都创建新实例
@Service
@Scope("prototype")
public class PrototypeBean {
    // 实现代码
}

// Request:Web应用中,每个HTTP请求一个实例
@Service
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestBean {
    // 实现代码
}

// Session:Web应用中,每个HTTP Session一个实例
@Service
@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class SessionBean {
    // 实现代码
}

第二部分:AOP(面向切面编程)

什么是AOP?

AOP(Aspect-Oriented Programming,面向切面编程)是一种编程范式,它将横切关注点(如日志、事务、安全等)从业务逻辑中分离出来,提高代码的模块化程度。

AOP的核心概念

// 1. 切面(Aspect):横切关注点的模块化
@Aspect
@Component
public class LoggingAspect {

    // 2. 连接点(Join Point):程序执行的特定点(如方法调用)
    // 3. 切点(Pointcut):匹配连接点的表达式
    @Pointcut("execution(* com.example.service.*.*(..))")
    public void serviceLayer() {}

    // 4. 通知(Advice):在切点执行的代码
    // 前置通知:在目标方法执行前执行
    @Before("serviceLayer()")
    public void logBefore(JoinPoint joinPoint) {
        System.out.println("执行方法: " + joinPoint.getSignature().getName());
    }

    // 后置通知:在目标方法执行后执行(无论成功或失败)
    @After("serviceLayer()")
    public void logAfter(JoinPoint joinPoint) {
        System.out.println("方法执行完成: " + joinPoint.getSignature().getName());
    }

    // 返回通知:在目标方法成功返回后执行
    @AfterReturning(pointcut = "serviceLayer()", returning = "result")
    public void logAfterReturning(JoinPoint joinPoint, Object result) {
        System.out.println("方法返回: " + result);
    }

    // 异常通知:在目标方法抛出异常时执行
    @AfterThrowing(pointcut = "serviceLayer()", throwing = "exception")
    public void logAfterThrowing(JoinPoint joinPoint, Throwable exception) {
        System.out.println("方法异常: " + exception.getMessage());
    }

    // 环绕通知:最强大的通知类型,可以完全控制方法的执行
    @Around("serviceLayer()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();

        System.out.println("开始执行: " + joinPoint.getSignature().getName());

        try {
            Object result = joinPoint.proceed();
            long endTime = System.currentTimeMillis();
            System.out.println("执行完成,耗时: " + (endTime - startTime) + "ms");
            return result;
        } catch (Exception e) {
            System.out.println("执行异常: " + e.getMessage());
            throw e;
        }
    }
}

切点表达式详解

execution表达式

// 语法:execution(修饰符 返回值类型 包名.类名.方法名(参数) 异常)

// 1. 匹配所有public方法
@Pointcut("execution(public * *(..))")
public void anyPublicMethod() {}

// 2. 匹配com.example.service包下所有类的所有方法
@Pointcut("execution(* com.example.service.*.*(..))")
public void servicePackage() {}

// 3. 匹配com.example.service包及其子包下所有类的所有方法
@Pointcut("execution(* com.example.service..*.*(..))")
public void serviceAndSubPackage() {}

// 4. 匹配所有以save开头的方法
@Pointcut("execution(* save*(..))")
public void saveMethods() {}

// 5. 匹配UserService接口的所有方法
@Pointcut("execution(* com.example.service.UserService.*(..))")
public void userServiceMethods() {}

// 6. 匹配第一个参数为String的方法
@Pointcut("execution(* *(String, ..))")
public void firstParamString() {}

// 7. 匹配返回值为void的方法
@Pointcut("execution(void com.example.service.*.*(..))")
public void voidMethods() {}

其他表达式类型

@Aspect
@Component
public class VariousPointcuts {

    // within:限定类范围
    @Pointcut("within(com.example.service.*)")
    public void withinServicePackage() {}

    // this:限定代理对象类型
    @Pointcut("this(com.example.service.UserService)")
    public void thisUserService() {}

    // target:限定目标对象类型
    @Pointcut("target(com.example.service.UserService)")
    public void targetUserService() {}

    // args:限定参数类型
    @Pointcut("args(com.example.model.User)")
    public void userArgument() {}

    // @annotation:限定注解
    @Pointcut("@annotation(com.example.annotation.Loggable)")
    public void loggableMethod() {}

    // @within:限定类上的注解
    @Pointcut("@within(com.example.annotation.Service)")
    public void serviceClass() {}

    // @target:限定目标对象上的注解
    @Pointcut("@target(com.example.annotation.RestController)")
    public void restControllerClass() {}

    // bean:限定Bean名称
    @Pointcut("bean(userService)")
    public void userServiceBean() {}

    // 组合表达式
    @Pointcut("servicePackage() && !voidMethods()")
    public void serviceAndNotVoid() {}

    @Pointcut("servicePackage() || controllerMethods()")
    public void serviceOrController() {}
}

AOP的实际应用场景

1. 日志记录

@Aspect
@Component
@Slf4j
public class LoggingAspect {

    @Around("@annotation(com.example.annotation.Loggable)")
    public Object logMethodExecution(ProceedingJoinPoint joinPoint) throws Throwable {
        String className = joinPoint.getTarget().getClass().getSimpleName();
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();

        log.info("{}.{}() - 开始执行,参数: {}", className, methodName, Arrays.toString(args));

        long startTime = System.currentTimeMillis();
        Object result;
        try {
            result = joinPoint.proceed();
            long endTime = System.currentTimeMillis();
            log.info("{}.{}() - 执行成功,返回值: {}, 耗时: {}ms",
                    className, methodName, result, endTime - startTime);
            return result;
        } catch (Exception e) {
            log.error("{}.{}() - 执行异常: {}", className, methodName, e.getMessage(), e);
            throw e;
        }
    }
}

// 使用自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Loggable {
    String value() default "";
}

// 在方法上使用注解
@Service
public class UserService {
    @Loggable("用户注册")
    public void registerUser(User user) {
        // 业务逻辑
    }
}

2. 性能监控

@Aspect
@Component
public class PerformanceAspect {

    private final Map<String, Long> methodStats = new ConcurrentHashMap<>();

    @Around("execution(* com.example.service.*.*(..))")
    public Object monitorPerformance(ProceedingJoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().toShortString();
        long startTime = System.currentTimeMillis();

        try {
            return joinPoint.proceed();
        } finally {
            long duration = System.currentTimeMillis() - startTime;
            methodStats.merge(methodName, duration, Long::sum);
            if (duration > 1000) {
                System.out.println("警告: " + methodName + " 执行时间过长: " + duration + "ms");
            }
        }
    }

    @Scheduled(fixedRate = 60000) // 每分钟输出一次统计信息
    public void printStatistics() {
        methodStats.forEach((method, totalTime) ->
            System.out.println(method + ": 总耗时 " + totalTime + "ms"));
    }
}

3. 缓存

@Aspect
@Component
public class CacheAspect {

    private final Map<String, Object> cache = new ConcurrentHashMap<>();

    @Around("@annotation(cacheable)")
    public Object cacheResult(ProceedingJoinPoint joinPoint, Cacheable cacheable) throws Throwable {
        String cacheKey = generateKey(joinPoint, cacheable);

        Object cachedResult = cache.get(cacheKey);
        if (cachedResult != null) {
            System.out.println("从缓存中获取: " + cacheKey);
            return cachedResult;
        }

        Object result = joinPoint.proceed();
        cache.put(cacheKey, result);
        System.out.println("存入缓存: " + cacheKey);

        return result;
    }

    private String generateKey(ProceedingJoinPoint joinPoint, Cacheable cacheable) {
        StringBuilder keyBuilder = new StringBuilder();
        keyBuilder.append(joinPoint.getSignature().toShortString());
        for (Object arg : joinPoint.getArgs()) {
            keyBuilder.append(":").append(arg);
        }
        return keyBuilder.toString();
    }
}

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Cacheable {
    String key() default "";
}

// 使用
@Service
public class UserService {
    @Cacheable
    public User findById(Long id) {
        // 从数据库查询
        return userRepository.findById(id);
    }
}

4. 事务管理

@Aspect
@Component
public class TransactionAspect {

    @Around("@annotation(transactional)")
    public Object manageTransaction(ProceedingJoinPoint joinPoint, Transactional transactional) throws Throwable {
        Connection connection = null;
        try {
            connection = getDatabaseConnection();
            connection.setAutoCommit(false);

            Object result = joinPoint.proceed();

            connection.commit();
            return result;
        } catch (Exception e) {
            if (connection != null) {
                connection.rollback();
            }
            throw e;
        } finally {
            if (connection != null) {
                connection.close();
            }
        }
    }
}

// 使用
@Service
public class UserService {
    @Transactional
    public void transferMoney(Long fromId, Long toId, BigDecimal amount) {
        // 转账逻辑
    }
}

Spring AOP的实现原理

Spring AOP主要通过动态代理实现:

// JDK动态代理(基于接口)
public interface UserService {
    void saveUser(User user);
}

public class UserServiceImpl implements UserService {
    public void saveUser(User user) {
        System.out.println("保存用户: " + user.getName());
    }
}

// 代理实现
public class UserServiceProxy implements InvocationHandler {

    private Object target;

    public UserServiceProxy(Object target) {
        this.target = target;
    }

    public Object getProxy() {
        return Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            this
        );
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("前置通知: " + method.getName());
        Object result = method.invoke(target, args);
        System.out.println("后置通知: " + method.getName());
        return result;
    }
}

// CGLIB代理(基于类)
public class UserServiceCglibProxy implements MethodInterceptor {

    private Object target;

    public UserServiceCglibProxy(Object target) {
        this.target = target;
    }

    public Object getProxy() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("前置通知: " + method.getName());
        Object result = proxy.invoke(target, args);
        System.out.println("后置通知: " + method.getName());
        return result;
    }
}

IOC与AOP的综合应用

下面是一个完整的综合示例:

// 1. 定义实体类
@Data
public class User {
    private Long id;
    private String username;
    private String email;
}

// 2. 定义DAO接口和实现
@Repository
public interface UserDao {
    User save(User user);
    User findById(Long id);
    void delete(Long id);
}

@Repository
public class UserDaoImpl implements UserDao {
    @Override
    public User save(User user) {
        // 数据库保存逻辑
        System.out.println("保存用户到数据库");
        return user;
    }

    @Override
    public User findById(Long id) {
        // 数据库查询逻辑
        System.out.println("从数据库查询用户");
        return new User();
    }

    @Override
    public void delete(Long id) {
        // 数据库删除逻辑
        System.out.println("从数据库删除用户");
    }
}

// 3. 定义Service层
@Service
public class UserService {
    private final UserDao userDao;
    private final EmailService emailService;

    public UserService(UserDao userDao, EmailService emailService) {
        this.userDao = userDao;
        this.emailService = emailService;
    }

    @Loggable("用户注册")
    @Transactional
    public void registerUser(User user) {
        userDao.save(user);
        emailService.sendWelcomeEmail(user.getEmail());
    }

    @Cacheable
    public User getUserById(Long id) {
        return userDao.findById(id);
    }

    @Transactional
    public void deleteUser(Long id) {
        userDao.delete(id);
    }
}

// 4. 定义切面
@Aspect
@Component
public class ApplicationAspect {

    // 日志切面
    @Around("@annotation(com.example.annotation.Loggable)")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("方法开始执行: " + methodName);

        try {
            Object result = joinPoint.proceed();
            System.out.println("方法执行成功: " + methodName);
            return result;
        } catch (Exception e) {
            System.out.println("方法执行异常: " + methodName + ", " + e.getMessage());
            throw e;
        }
    }

    // 性能监控切面
    @Around("execution(* com.example.service.*.*(..))")
    public Object monitorPerformance(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        long duration = System.currentTimeMillis() - startTime;

        if (duration > 100) {
            System.out.println("性能警告: " + joinPoint.getSignature().toShortString()
                    + " 执行时间: " + duration + "ms");
        }
        return result;
    }
}

// 5. 配置类
@Configuration
@ComponentScan("com.example")
@EnableAspectJAutoProxy
public class AppConfig {
}

// 6. 启动类
public class Application {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

        UserService userService = context.getBean(UserService.class);

        User user = new User();
        user.setUsername("testuser");
        user.setEmail("test@example.com");

        userService.registerUser(user);
        User foundUser = userService.getUserById(1L);
    }
}

总结

IOC的优势

  1. 降低耦合:通过依赖注入降低组件之间的耦合度
  2. 便于测试:可以轻松mock依赖进行单元测试
  3. 集中管理:统一管理Bean的生命周期和配置
  4. 提高可维护性:代码结构清晰,易于理解和维护

AOP的优势

  1. 关注点分离:将横切关注点从业务逻辑中分离
  2. 代码复用:避免重复的横切逻辑代码
  3. 灵活配置:通过切点表达式灵活地定义切面
  4. 增强功能:在不修改原有代码的情况下增强功能

最佳实践

  1. 优先使用构造函数注入
  2. 合理使用注解,避免过度使用
  3. AOP切面保持简单,避免复杂逻辑
  4. 合理定义Bean的作用域
  5. 注意循环依赖问题
  6. 性能敏感的代码慎用AOP

学习建议

  1. 从简单的例子开始,逐步理解IOC和AOP的概念
  2. 阅读Spring源码,了解底层实现原理
  3. 在实际项目中应用,积累经验
  4. 学习设计模式,理解IOC和AOP的设计思想

参考资源

  • Spring Framework官方文档
  • 《Spring源码深度解析》
  • 《Spring实战》
  • 《Effective Java》