返回 元婴・Spring 道韵观想

01Spring核心:从IOC容器到AOP切面的深度解析

博主
大约 8 分钟

Spring核心:从IOC容器到AOP切面的深度解析

Spring不仅仅是一个框架,更是一种编程思想

开篇:为什么Spring如此重要?

在Java生态系统中,Spring框架已经成为事实上的标准。它不仅简化了企业级应用开发,更重要的是,它引入了一系列优秀的设计理念和编程模式,如依赖注入、面向切面编程等。

Spring框架

IOC容器:从"new"到"依赖注入"的思维转变

IOC(Inversion of Control,控制反转)是Spring的核心概念之一。它将对象的创建和依赖管理从代码中分离出来,交由容器统一管理。

传统方式:主动创建对象

// 传统方式:硬编码依赖
public class UserService {
    private UserDao userDao = new UserDaoImpl(); // 硬编码依赖
    
    public void saveUser(User user) {
        userDao.save(user);
    }
}

Spring方式:依赖注入

// Spring方式:依赖注入
public class UserService {
    private UserDao userDao; // 只声明依赖
    
    // 构造器注入
    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }
    
    // 或者 setter 注入
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
    
    public void saveUser(User user) {
        userDao.save(user);
    }
}

配置方式

XML配置:

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

<bean id="userService" class="com.example.service.UserService">
    <!-- 构造器注入 -->
    <constructor-arg ref="userDao"/>
    <!-- 或者 setter 注入 -->
    <!-- <property name="userDao" ref="userDao"/> -->
</bean>

注解配置:

// 组件扫描
@Component
public class UserDaoImpl implements UserDao {
    // 实现方法
}

@Service
public class UserService {
    @Autowired // 自动注入
    private UserDao userDao;
    
    // 业务方法
}

// 启用组件扫描
@Configuration
@ComponentScan("com.example")
public class AppConfig {
    // 配置类
}

Java配置:

@Configuration
public class AppConfig {
    @Bean
    public UserDao userDao() {
        return new UserDaoImpl();
    }
    
    @Bean
    public UserService userService() {
        return new UserService(userDao());
    }
}

AOP切面:从"散落在各处的代码"到"集中管理"的优雅解决方案

AOP(Aspect-Oriented Programming,面向切面编程)是Spring的另一个核心概念。它允许你将横切关注点(如日志、事务、安全等)从业务逻辑中分离出来,集中管理。

AOP核心概念

  • 切面(Aspect):横切关注点的模块化
  • 连接点(Join Point):程序执行过程中的点
  • 通知(Advice):在连接点执行的代码
  • 切点(Pointcut):匹配连接点的表达式
  • 引入(Introduction):向现有类添加方法或字段
  • 织入(Weaving):将切面与目标对象结合

示例:日志切面

// 定义切面
@Aspect
@Component
public class LoggingAspect {
    // 定义切点
    @Pointcut("execution(* com.example.service.*.*(..))")
    public void serviceMethods() {}
    
    // 前置通知
    @Before("serviceMethods()")
    public void beforeAdvice(JoinPoint joinPoint) {
        System.out.println("调用方法:" + joinPoint.getSignature().getName());
        System.out.println("参数:" + Arrays.toString(joinPoint.getArgs()));
    }
    
    // 后置通知
    @AfterReturning(
        pointcut = "serviceMethods()",
        returning = "result"
    )
    public void afterReturningAdvice(JoinPoint joinPoint, Object result) {
        System.out.println("方法返回值:" + result);
    }
    
    // 异常通知
    @AfterThrowing(
        pointcut = "serviceMethods()",
        throwing = "exception"
    )
    public void afterThrowingAdvice(JoinPoint joinPoint, Exception exception) {
        System.out.println("方法抛出异常:" + exception.getMessage());
    }
    
    // 环绕通知
    @Around("serviceMethods()")
    public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        try {
            // 执行目标方法
            Object result = proceedingJoinPoint.proceed();
            long endTime = System.currentTimeMillis();
            System.out.println("方法执行时间:" + (endTime - startTime) + "ms");
            return result;
        } catch (Throwable throwable) {
            System.out.println("方法执行异常:" + throwable.getMessage());
            throw throwable;
        }
    }
}

Spring Bean:生命周期与作用域

Bean生命周期

  1. 实例化:创建Bean实例
  2. 属性赋值:设置Bean的属性
  3. 初始化前:执行BeanPostProcessor的postProcessBeforeInitialization方法
  4. 初始化:执行InitializingBean的afterPropertiesSet方法或自定义初始化方法
  5. 初始化后:执行BeanPostProcessor的postProcessAfterInitialization方法
  6. 使用:Bean可以被应用程序使用
  7. 销毁:执行DisposableBean的destroy方法或自定义销毁方法

Bean作用域

  • singleton:单例,默认作用域
  • prototype:原型,每次获取都创建新实例
  • request:请求,每个HTTP请求创建一个实例
  • session:会话,每个HTTP会话创建一个实例
  • application:应用,每个ServletContext创建一个实例
  • websocket:WebSocket,每个WebSocket会话创建一个实例

事务管理:从"手动控制"到"声明式事务"的飞跃

Spring提供了强大的事务管理能力,支持编程式事务和声明式事务。

声明式事务

@Service
public class UserService {
    @Autowired
    private UserDao userDao;
    
    @Transactional // 声明式事务
    public void transferMoney(Long fromUserId, Long toUserId, BigDecimal amount) {
        // 扣款
        userDao.decreaseBalance(fromUserId, amount);
        
        // 这里如果发生异常,事务会自动回滚
        // int i = 1 / 0;
        
        // 存款
        userDao.increaseBalance(toUserId, amount);
    }
}

事务属性

@Transactional(
    propagation = Propagation.REQUIRED, // 传播行为
    isolation = Isolation.DEFAULT, // 隔离级别
    timeout = 30, // 超时时间
    readOnly = false, // 是否只读
    rollbackFor = Exception.class, // 回滚异常
    noRollbackFor = RuntimeException.class // 不回滚异常
)
public void transferMoney(Long fromUserId, Long toUserId, BigDecimal amount) {
    // 业务逻辑
}

条件注解:根据环境动态配置

Spring 4.0引入了条件注解,允许根据特定条件决定是否创建Bean。

@Configuration
public class DatabaseConfig {
    @Bean
    @Conditional(DevCondition.class) // 开发环境条件
    public DataSource devDataSource() {
        // 开发环境数据源
        return createDataSource("dev");
    }
    
    @Bean
    @Conditional(ProdCondition.class) // 生产环境条件
    public DataSource prodDataSource() {
        // 生产环境数据源
        return createDataSource("prod");
    }
    
    private DataSource createDataSource(String environment) {
        // 创建数据源
        return dataSource;
    }
}

// 开发环境条件
public class DevCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return "dev".equals(context.getEnvironment().getProperty("spring.profiles.active"));
    }
}

// 生产环境条件
public class ProdCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return "prod".equals(context.getEnvironment().getProperty("spring.profiles.active"));
    }
}

事件机制:松耦合的组件通信

Spring提供了事件机制,允许组件之间通过事件进行松耦合通信。

// 自定义事件
public class UserRegisteredEvent extends ApplicationEvent {
    private User user;
    
    public UserRegisteredEvent(Object source, User user) {
        super(source);
        this.user = user;
    }
    
    public User getUser() {
        return user;
    }
}

// 事件发布者
@Service
public class UserService {
    @Autowired
    private ApplicationEventPublisher eventPublisher;
    
    public void registerUser(User user) {
        // 注册用户
        // ...
        
        // 发布事件
        eventPublisher.publishEvent(new UserRegisteredEvent(this, user));
    }
}

// 事件监听器
@Component
public class UserEventListener {
    @EventListener
    public void handleUserRegisteredEvent(UserRegisteredEvent event) {
        User user = event.getUser();
        // 处理用户注册事件,如发送邮件、短信等
        System.out.println("用户注册成功:" + user.getUsername());
    }
}

实战建议

  1. 优先使用注解配置:注解配置更简洁,易于维护
  2. 合理使用作用域:根据实际需求选择合适的Bean作用域
  3. 善用AOP:将横切关注点(如日志、事务、安全)分离出来
  4. 理解生命周期:了解Bean生命周期有助于编写更健壮的代码
  5. 合理配置事务:根据业务需求配置合适的事务属性
  6. 使用条件配置:根据环境动态调整配置
  7. 利用事件机制:实现松耦合的组件通信

结语

Spring框架的核心价值在于它引入的设计理念和编程模式,而不仅仅是它的功能。通过理解IOC、AOP等核心概念,你可以编写出更加模块化、可测试、可维护的代码。

Spring的生态系统非常庞大,除了核心框架外,还包括Spring Boot、Spring Cloud、Spring Security等一系列优秀的项目。掌握Spring核心概念,是学习整个Spring生态的基础。

在实际开发中,要根据具体需求选择合适的Spring特性,避免过度设计。记住,技术是为业务服务的,选择合适的工具和方法,才能事半功倍。

知识点测试

读完文章了?来测试一下你对知识点的掌握程度吧!

评论区

使用 GitHub 账号登录后即可发表评论,支持 Markdown 格式。

如果评论系统无法加载,请确保:

  • 您的网络可以访问 GitHub
  • giscus GitHub App 已安装到仓库
  • 仓库已启用 Discussions 功能