Spring的核心特性

Spring 是一个开源的 Java 平台框架,最初由 Rod Johnson 在 2003 年提出,旨在简化企业级 Java 应用的开发。它通过提供全面的基础设施支持,帮助开发者专注于业务逻辑,而无需处理底层的复杂性。

1. 控制反转(IoC - Inversion of Control)​

IoC的核心概念

(1) 传统开发模式(主动控制)​

在传统代码中,对象的创建和依赖管理由开发者手动完成:

public class UserService {
    private UserRepository userRepo = new UserRepository(); // 主动创建依赖对象
}

​问题​​:

  • 代码耦合度高(UserService 直接依赖 UserRepository 的具体实现)。
  • 难以替换依赖(例如切换为 MockUserRepository 进行测试)。

​(2) IoC 模式(被动接受依赖)​

通过容器管理对象,开发者只需声明依赖,由容器负责注入:

public class UserService {
    private UserRepository userRepo; // 不主动创建,依赖由容器注入
    
    // 通过构造器接受依赖(推荐方式)
    public UserService(UserRepository userRepo) {
        this.userRepo = userRepo;
    }
}

​优势​​:

  • ​解耦​​:对象不关心依赖的具体实现。
  • ​可测试性​​:轻松注入 Mock 对象。
  • ​灵活性​​:配置变更无需修改代码(如切换数据库实现)。

 

2. ​依赖注入(DI - Dependency Injection)​

IoC 的具体实现方式​​,通过容器自动装配对象之间的依赖关系。Spring 通过 ​​IoC 容器​​(如 ApplicationContext)管理对象的生命周期,具体实现依赖以下技术:

什么是依赖?​

  • 在面向对象编程中,如果一个类(如 UserService)需要使用另一个类(如 UserRepository),则称 UserService ​​依赖​​ UserRepository
  • ​传统方式​​:直接在代码中 new 依赖对象,导致紧耦合。
    public class UserService {
        private UserRepository userRepo = new UserRepository(); // 硬编码依赖
    }

依赖注入的作用​

  • ​解耦​​:将依赖关系的创建和绑定从代码中移除,交由容器管理。
  • ​灵活性​​:可以轻松替换依赖的实现(如测试时注入 Mock 对象)。
  • ​可维护性​​:依赖关系清晰,便于扩展和修改。

DI 的底层实现原理​

Spring 容器(如 ApplicationContext)在启动时,会通过以下步骤完成依赖注入:

  1. ​扫描组件​​:通过 @ComponentScan 或 XML 配置找到所有需要管理的 Bean(如 @Service@Repository)。
  2. ​创建 Bean 实例​​:调用构造器或无参构造方法实例化 Bean。
  3. ​解析依赖​​:检查 Bean 的依赖(如 @Autowired 标注的字段或方法)。
  4. ​注入依赖​​:从容器中查找匹配的 Bean 并注入。
  5. ​初始化​​:调用 @PostConstruct 方法(如果有)。

​示例流程​

@Service
public class OrderService {
    private final PaymentService paymentService;

    @Autowired
    public OrderService(PaymentService paymentService) {
        this.paymentService = paymentService; // 依赖注入
    }
}

@Repository
public class PaymentService { ... }

​容器的工作流程​​:

  1. 发现 OrderService 和 PaymentService 都是 Spring Bean。
  2. 先创建 PaymentService(无依赖)。
  3. 创建 OrderService 时,发现需要 PaymentService,于是从容器中获取并注入。

怎么依赖注入

​(1) 依赖注入(DI - Dependency Injection)​

IoC 的核心实现手段,常见注入方式:

​注入方式​​示例代码​​适用场景​
​构造器注入​

@Autowired

public UserService(UserRepo repo) { ... }

​推荐​​,强制依赖,不可变
​Setter 注入​

@Autowired

public void setUserRepo(UserRepo repo) { ... }

可选依赖,灵活性高
​字段注入​

@Autowired

private UserRepo repo;

简单但不推荐(难测试、隐藏依赖)

​(2) 配置方式​

Spring 支持多种配置依赖关系的方式:

  • ​XML 配置​​(传统方式):
    <bean id="userService" class="com.example.UserService">
        <constructor-arg ref="userRepository"/> <!-- 构造器注入 -->
    </bean>
  • ​注解配置​​(现代主流):
    @Service
    public class UserService {
        @Autowired
        private UserRepository userRepo;
    }
  • ​Java Config​​(显式配置):
    @Configuration
    public class AppConfig {
        @Bean
        public UserRepository userRepo() {
            return new UserRepository();
        }
    }

​Spring 依赖注入(DI)相关注解详解​

Spring 提供了一系列注解来简化依赖注入(DI)的配置,以下是核心注解的分类和用法说明:

重要关注标红的注解

​1. 核心 DI 注解​
​(1) @Autowired

​作用​​:自动注入依赖对象(默认按类型匹配)。
​适用场景​​:构造器、Setter 方法、字段。
​示例​​:

@Service
public class UserService {
    @Autowired  // 字段注入(不推荐)
    private UserRepository userRepo;

    @Autowired  // Setter 注入
    public void setUserRepo(UserRepository userRepo) {
        this.userRepo = userRepo;
    }

    // 构造器注入(推荐,Spring 4.3+ 可省略 @Autowired)
    @Autowired
    public UserService(UserRepository userRepo) {
        this.userRepo = userRepo;
    }
}

​注意​​:

  • 如果存在多个同类型 Bean,需配合 @Qualifier 指定名称。
  • 字段注入虽然简单常用,但​难测试、违反封装性。

​(2) @Qualifier

​作用​​:当同一类型有多个 Bean 时,按名称指定注入目标。
​示例​​:

@Repository("mysqlUserRepo")
public class MySQLUserRepository implements UserRepository { ... }

@Repository("mongoUserRepo")
public class MongoUserRepository implements UserRepository { ... }

@Service
public class UserService {
    @Autowired
    @Qualifier("mysqlUserRepo")  // 明确指定注入的 Bean
    private UserRepository userRepo;
}

​(3) @Primary

​作用​​:标记某个 Bean 为同类型的默认注入选项。
​示例​​:

@Configuration
public class AppConfig {
    @Bean
    @Primary  // 优先注入此 Bean
    public UserRepository mysqlUserRepo() {
        return new MySQLUserRepository();
    }

    @Bean
    public UserRepository mongoUserRepo() {
        return new MongoUserRepository();
    }
}

​2. 组件扫描与注册注解​
​(1) @Component

​作用​​:通用注解,标记一个类为 Spring 管理的组件(Bean)。
​衍生注解​​(功能相同,语义化分类):

  • @Service:业务逻辑层
  • @Repository:数据访问层(在 MyBatis 项目中,​​优先用 @Mapper
  • @Controller:Web 控制层
  • @Configuration:配置类

​示例​​:

@Service
public class UserService { ... }

@Repository
public class UserRepository { ... }

​(2) @Bean

​作用​​:在配置类中显式定义 Bean(适用于第三方库的类或需要自定义初始化的 Bean)。
​示例​​:

@Configuration
public class AppConfig {
    @Bean  // 方法返回的对象由 Spring 管理
    public DataSource dataSource() {
        return new HikariDataSource();
    }
}

​(3) @ComponentScan

​作用​​:指定 Spring 扫描哪些包下的组件(@Component@Service 等),放在启动类。
​示例​​:

@SpringBootApplication
@ComponentScan(basePackages = "com.example")
public class MyApp { ... }

​3. 条件化注入注解​
​(1) @Conditional

​作用​​:根据条件决定是否注册 Bean(需实现 Condition 接口)。
​示例​​:

@Bean
@Conditional(OnProductionEnvCondition.class)
public DataSource prodDataSource() {
    return new ProductionDataSource();
}

​(2) @Profile

​作用​​:根据环境配置文件(如 devprod)激活 Bean。
​示例​​:

@Configuration
@Profile("dev")  // 仅在 dev 环境下生效
public class DevConfig {
    @Bean
    public DataSource devDataSource() {
        return new EmbeddedDatabaseBuilder().build();
    }
}

​4. 其他实用注解​
​(1) @Lazy

​作用​​:延迟初始化 Bean(首次使用时创建)。
​示例​​:

@Service
@Lazy
public class HeavyService { ... }  // 启动时不初始化

​(2) @Scope

​作用​​:定义 Bean 的作用域(如单例、原型、请求、会话等)。
​示例​​:

@Service
@Scope("prototype")  // 每次请求创建新实例
public class PrototypeService { ... }

​(3) @Value

​作用​​:注入外部配置(如 application.properties)的值。
​示例​​:

@Service
public class MyService {
    @Value("${app.timeout:5000}")  // 默认值 5000
    private int timeout;
}

​3. 面向切面编程(AOP - Aspect-Oriented Programming)​

面向切面编程(​​AOP,Aspect-Oriented Programming​​)是 Spring 框架的核心特性之一,用于​​将横切关注点(Cross-Cutting Concerns)模块化​​,避免代码重复,提高可维护性。

​1. AOP 的核心概念​

​(1) 什么是横切关注点?​

横切关注点是指那些​​分散在多个模块中​​,但与核心业务逻辑无关的功能,例如:

  • ​日志记录​​(Logging)
  • ​事务管理​​(@Transactional
  • ​权限校验​​(Security)
  • ​性能监控​​(Performance Monitoring)
  • ​异常处理​​(Exception Handling)

​传统方式的问题​​:
这些代码会重复出现在多个地方,导致:

  • ​代码冗余​​:相同的逻辑在多个方法中重复。
  • ​维护困难​​:修改日志或事务逻辑时,需要修改所有相关方法。

​(2) AOP 的解决方案​

AOP 通过​​动态代理​​技术,在不修改原有代码的情况下,​​横向插入​​额外逻辑(增强)。
​核心思想​​:

  • ​分离核心业务逻辑和横切逻辑​​。
  • ​在运行时动态织入(Weaving)增强代码​​。

​2. AOP 的核心术语​

​术语​​说明​​示例​
​切面(Aspect)​封装横切逻辑的模块(如日志、事务)。@Aspect 标注的类。
​连接点(Join Point)​程序执行过程中的某个点(如方法调用、异常抛出)。UserService.addUser() 方法执行时。
​通知(Advice)​切面在连接点执行的具体逻辑(如前置、后置、环绕等)。@Before@After@Around 注解的方法。
​切入点(Pointcut)​定义哪些连接点会被切面拦截(通过表达式匹配)。@Pointcut("execution(* com.example.service.*.*(..))")
​目标对象(Target)​被代理的原始对象(如 UserService)。未增强的 UserServiceImpl 实例。
​织入(Weaving)​将切面应用到目标对象,生成代理对象的过程。Spring AOP 使用动态代理(JDK 或 CGLIB)实现。

​3. Spring AOP 的通知类型​

Spring 支持 5 种通知(Advice),通过注解实现:

​通知类型​​执行时机​​注解​​示例​
​前置通知​目标方法执行前@Before@Before("pointcut()")
​后置通知​目标方法执行后(无论是否异常)@After@After("execution(* com.example.service.*.*(..))")
​返回通知​目标方法​​正常返回​​后@AfterReturning@AfterReturning(pointcut="pointcut()", returning="result")
​异常通知​目标方法​​抛出异常​​后@AfterThrowing@AfterThrowing(pointcut="pointcut()", throwing="ex")
​环绕通知​控制目标方法执行(最强大)@Around@Around("pointcut()")

​4. 代码示例​

​(1) 定义切面(Aspect)​

import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Aspect  // 声明这是一个切面
@Component  // 交给 Spring 管理
public class LoggingAspect {

    // 定义切入点(拦截 service 包下的所有方法)
    @Pointcut("execution(* com.example.service.*.*(..))")
    public void serviceMethods() {}

    // 前置通知:在方法执行前打印日志
    @Before("serviceMethods()")
    public void logBefore(JoinPoint jp) {
        System.out.println("调用方法: " + jp.getSignature().getName());
    }

    // 环绕通知:计算方法执行耗时
    @Around("serviceMethods()")
    public Object logExecutionTime(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = pjp.proceed();  // 执行目标方法
        long duration = System.currentTimeMillis() - start;
        System.out.println("方法执行耗时: " + duration + "ms");
        return result;
    }
}

​(2) 目标服务类​

@Service
public class UserService {
    public void addUser(String username) {
        System.out.println("添加用户: " + username);
    }
}

​(3) 测试调用​

@SpringBootApplication
public class MyApp {
    public static void main(String[] args) {
        ApplicationContext ctx = SpringApplication.run(MyApp.class, args);
        UserService userService = ctx.getBean(UserService.class);
        userService.addUser("Alice");
    }
}

​输出结果​​:

调用方法: addUser  
添加用户: Alice  
方法执行耗时: 2ms

​5. AOP 的实现原理​

Spring AOP 通过​​动态代理​​实现,分为两种方式:

  1. ​JDK 动态代理​​(默认)
    • 要求目标类必须实现接口(基于 InvocationHandler)。
  2. ​CGLIB 代理​
    • 通过字节码增强,代理无接口的类(需添加 spring-core 依赖)。

​代理对象生成流程​​:

  1. Spring 容器启动时,扫描 @Aspect 注解的类。
  2. 根据切入点表达式匹配目标 Bean。
  3. 为目标 Bean 创建代理对象(JDK 或 CGLIB)。
  4. 调用代理对象时,按通知类型执行增强逻辑。

​4. 声明式事务管理​

 ​​基于AOP(面向切面编程)​​ 技术通过注解简化事务控制​,替代手动 JDBC 事务代码,无侵入,业务代码纯净,适用常规 CRUD 操作。

  • ​关键注解​​:@Transactional
  • ​特性​​:
    • 支持传播行为(如 Propagation.REQUIRED)。
    • 隔离级别、超时设置、回滚规则。
@Service
public class OrderService {

    @Autowired
    private OrderDao orderDao;

    @Autowired
    private PaymentDao paymentDao;

    // 声明事务:方法内所有数据库操作作为一个原子单元
    @Transactional
    public void placeOrder(Order order) {
        orderDao.save(order);          // 插入订单
        paymentDao.process(order);      // 处理支付
        // 若此处抛出异常,事务会自动回滚
    }
}

关键属性​

​属性​​作用​​默认值​
propagation事务传播行为(如新建事务、加入现有事务等)REQUIRED(如果当前没有事务,新建一个事务;如果已存在事务,则加入该事务
isolation事务隔离级别(解决脏读、幻读等问题)DEFAULT(数据库默认)
timeout事务超时时间(秒),超时后自动回滚-1(无超时)
readOnly是否只读事务(优化查询性能)false
rollbackFor指定哪些异常触发回滚(默认仅回滚 RuntimeException{}
noRollbackFor指定哪些异常不触发回滚{}

​示例​​:

@Transactional(
    propagation = Propagation.REQUIRED,
    isolation = Isolation.READ_COMMITTED,
    timeout = 30,
    rollbackFor = {SQLException.class, BusinessException.class}
)
public void updateData() { ... }

​5. 模块化与集成能力​

  • ​模块化设计​​:按需引入模块(如 spring-webmvcspring-data-jpa)。
  • ​无缝集成​​:
    • 数据库:JPA/Hibernate、MyBatis、JDBC。
    • 消息队列:JMS、Kafka、RabbitMQ。
    • 安全:Spring Security(OAuth2、JWT)。
    • 测试:@SpringBootTest 支持集成测试。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值