Spring框架终极入门指南(12000字深度解析)

一、为什么学习Spring是Java开发的必经之路?

1.1 Spring框架的历史地位与革命意义

Spring发展历程回顾:

2003年:Rod Johnson发布《Expert One-on-One J2EE Design and Development》,提出轻量级J2EE解决方案

2004年:Spring 1.0正式发布,引入IoC容器

2006年:Spring 2.0发布,支持XML命名空间和注解

2009年:Spring 3.0发布,全面支持Java 5+和注解配置

2013年:Spring 4.0发布,支持Java 8和WebSocket

2017年:Spring 5.0发布,支持响应式编程

2022年:Spring 6.0发布,支持Java 17+和Native Image

Spring解决的问题清单:

传统J2EE开发的痛点:

  1. EJB过度复杂:配置繁琐、学习曲线陡峭
  2. 代码耦合度高:难以测试和维护
  3. 事务管理困难:需要手动控制
  4. 资源管理复杂:连接池、线程池等
  5. 开发效率低下:重复代码多

Spring提供的解决方案:

✓ 轻量级:无需重型应用服务器

✓ 松耦合:依赖注入降低耦合

✓ 声明式事务:@Transactional简化事务管理

✓ 模板化操作:JdbcTemplate等简化数据库操作

✓ AOP支持:面向切面编程解耦横切关注点

1.2 Spring核心模块全景图(含版本对比)

Spring 5.x vs Spring 6.x 核心变化:

java

// Spring 5.x 特性

@Nullable // 支持可空注解

@NonNullApi // 非空API

// Spring 6.x 新特性

// 1. 支持Java 17+(最低要求)

// 2. 支持Jakarta EE 9+(包名javax→jakarta)

// 3. 响应式编程增强

// 4. Native Image支持(GraalVM)

二、IoC容器:Spring框架的"心脏"

2.1 控制反转(IoC)与依赖注入(DI)深入理解

IoC的三种实现方式对比:

java

// 方式1:构造器注入(Spring推荐)

@Service

public class UserService {

private final UserRepository userRepository;

复制代码
// 构造器注入
public UserService(UserRepository userRepository) {
    this.userRepository = userRepository;
}

}

// 方式2:Setter注入

@Service

public class OrderService {

private PaymentService paymentService;

复制代码
// Setter注入
@Autowired
public void setPaymentService(PaymentService paymentService) {
    this.paymentService = paymentService;
}

}

// 方式3:字段注入(不推荐,仅演示)

@Service

public class ProductService {

@Autowired // 字段注入(存在隐患)

private InventoryService inventoryService;

}

// 方式4:方法注入(任意方法)

@Component

public class NotificationService {

private EmailService emailService;

复制代码
@Autowired  // 任意方法注入
public void prepare(EmailService emailService) {
    this.emailService = emailService;
}

}

四种注入方式的对比分析表:

注入方式 优点 缺点 使用场景

构造器注入 1. 依赖不可变(final)

  1. 完全初始化的对象

  2. 保证必需依赖 参数较多时代码冗长 推荐首选,特别是必需依赖

Setter注入 1. 可选依赖

  1. 灵活性高

  2. 可重新注入 1. 对象可能部分初始化

  3. 依赖可能为null 可选依赖或需要重新配置

字段注入 1. 代码简洁

  1. 无setter方法 1. 不能声明final

  2. 难以测试

  3. 隐藏依赖关系 不推荐使用,仅遗留代码

方法注入 1. 灵活性高

  1. 可在任意方法 1. 可能被多次调用

  2. 不如构造器明确 特殊初始化逻辑

2.2 Bean生命周期全流程解析(配时序图)

Bean从创建到销毁的完整历程:

java

@Component

public class LifecycleBean implements

BeanNameAware,

BeanFactoryAware,

ApplicationContextAware,

InitializingBean,

DisposableBean {

复制代码
private String name;

// 1. 构造器(实例化)
public LifecycleBean() {
    System.out.println("1. 构造器执行: 实例化Bean");
}

// 2. 设置属性(依赖注入)
@Value("${app.name}")
public void setName(String name) {
    System.out.println("2. 属性注入: name = " + name);
    this.name = name;
}

// 3. BeanNameAware接口
@Override
public void setBeanName(String name) {
    System.out.println("3. BeanNameAware: bean名称 = " + name);
}

// 4. BeanFactoryAware接口
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
    System.out.println("4. BeanFactoryAware: 设置BeanFactory");
}

// 5. ApplicationContextAware接口
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    System.out.println("5. ApplicationContextAware: 设置ApplicationContext");
}

// 6. BeanPostProcessor前置处理
// 由BeanPostProcessor实现类完成

// 7. @PostConstruct注解方法
@PostConstruct
public void postConstruct() {
    System.out.println("7. @PostConstruct: 初始化后执行");
}

// 8. InitializingBean接口
@Override
public void afterPropertiesSet() throws Exception {
    System.out.println("8. InitializingBean.afterPropertiesSet: 属性设置完成后");
}

// 9. 自定义init-method
public void customInit() {
    System.out.println("9. 自定义init-method: 执行自定义初始化");
}

// 10. BeanPostProcessor后置处理
// 由BeanPostProcessor实现类完成

// 11. Bean准备就绪,可以使用

// 12. @PreDestroy注解方法
@PreDestroy
public void preDestroy() {
    System.out.println("12. @PreDestroy: 销毁前执行");
}

// 13. DisposableBean接口
@Override
public void destroy() throws Exception {
    System.out.println("13. DisposableBean.destroy: 执行销毁");
}

// 14. 自定义destroy-method
public void customDestroy() {
    System.out.println("14. 自定义destroy-method: 执行自定义销毁");
}

}

// 配置类

@Configuration

public class LifecycleConfig {

复制代码
@Bean(initMethod = "customInit", destroyMethod = "customDestroy")
public LifecycleBean lifecycleBean() {
    return new LifecycleBean();
}

// BeanPostProcessor示例
@Bean
public static BeanPostProcessor myBeanPostProcessor() {
    return new BeanPostProcessor() {
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) {
            System.out.println("6. BeanPostProcessor前置: " + beanName);
            return bean;
        }
        
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) {
            System.out.println("10. BeanPostProcessor后置: " + beanName);
            return bean;
        }
    };
}

}

Bean生命周期时序图总结:

Bean创建流程:

  1. 实例化(构造器)
  2. 属性赋值(依赖注入)
  3. 设置BeanName
  4. 设置BeanFactory
  5. 设置ApplicationContext
  6. BeanPostProcessor前置处理
  7. @PostConstruct
  8. InitializingBean.afterPropertiesSet
  9. 自定义init-method
  10. BeanPostProcessor后置处理
    → Bean准备就绪

Bean销毁流程:

  1. @PreDestroy
  2. DisposableBean.destroy
  3. 自定义destroy-method
    → Bean被销毁
    2.3 Bean作用域深度解析(含实战应用)
    Spring支持的6种Bean作用域:

java

// 1. singleton(默认):单例,整个Spring容器只有一个实例

@Scope("singleton") // 或 @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)

@Component

public class SingletonService {

// 线程不安全!需要特别注意

private int counter = 0;

复制代码
public synchronized void increment() {
    counter++;
}

}

// 2. prototype:原型,每次获取都创建新实例

@Scope("prototype")

@Component

public class PrototypeService {

private final UUID id = UUID.randomUUID();

复制代码
public UUID getId() {
    return id;
}

}

// 3. request:Web请求作用域,每个HTTP请求一个实例

@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)

@Component

public class RequestScopedBean {

private final LocalDateTime created = LocalDateTime.now();

复制代码
public LocalDateTime getCreated() {
    return created;
}

}

// 4. session:Web会话作用域,每个HTTP Session一个实例

@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)

@Component

public class UserPreferences {

private String theme = "light";

private Locale locale = Locale.CHINA;

复制代码
// getter/setter省略

}

// 5. application:Web应用作用域,整个Web应用一个实例

@Scope(value = WebApplicationContext.SCOPE_APPLICATION)

@Component

public class ApplicationConfig {

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

复制代码
public void put(String key, Object value) {
    cache.put(key, value);
}

}

// 6. websocket:WebSocket会话作用域

@Scope(scopeName = "websocket")

@Component

public class WebSocketSessionBean {

private WebSocketSession session;

复制代码
// getter/setter省略

}

作用域选择指南:

场景 推荐作用域 原因 注意事项

无状态服务类 singleton 节省内存,提高性能 确保线程安全

有状态业务对象 prototype 避免状态污染 注意性能开销

请求相关数据 request 请求隔离 仅在Web环境可用

用户偏好设置 session 会话隔离 注意Session超时

应用级缓存 application 全局共享 注意线程安全

实时通信 websocket 连接隔离 WebSocket专用

三、AOP编程:Spring的"魔术"

3.1 AOP核心概念大白话解读

AOP解决了什么问题?

传统OOP的痛点:

  1. 代码重复:日志、事务、安全等代码散落各处
  2. 关注点耦合:业务逻辑与非业务逻辑混杂
  3. 维护困难:修改横切逻辑需要改动多处

AOP的核心思想:

"横向切割"关注点,将通用功能模块化

现实比喻:

假设业务代码是"汉堡包"的面包和肉饼

AOP就是添加的"生菜、番茄、酱料"

你可以在不改变面包和肉饼的情况下,统一添加配料

3.2 Spring AOP实战:5个最常见切面应用

切面1:方法执行日志

java

/**

  • 日志切面:记录方法入参、出参、执行时间

    */

    @Aspect

    @Component

    @Slf4j

    public class LoggingAspect {

    /**

    • 切入点定义:拦截所有Service层方法
      /
      @Pointcut("execution(
      com.example.service.. (...))")
      public void serviceLayer() {}

    /**

    • 前置通知:记录方法入参

      */

      @Before("serviceLayer()")

      public void logBefore(JoinPoint joinPoint) {

      String className = joinPoint.getTarget().getClass().getSimpleName();

      String methodName = joinPoint.getSignature().getName();

      Object[] args = joinPoint.getArgs();

      log.info("【方法调用】{}.{}() - 参数: {}",

      className, methodName, Arrays.toString(args));

      }

    /**

    • 后置通知:记录方法返回值

      */

      @AfterReturning(pointcut = "serviceLayer()", returning = "result")

      public void logAfterReturning(JoinPoint joinPoint, Object result) {

      String methodName = joinPoint.getSignature().getName();

      if (result != null) {

      // 大型对象只记录摘要

      if (result instanceof Collection) {

      Collection<?> collection = (Collection<?>) result;

      log.info("【方法返回】{}() - 返回集合,大小: {}",

      methodName, collection.size());

      } else if (result.getClass().isArray()) {

      log.info("【方法返回】{}() - 返回数组,长度: {}",

      methodName, Array.getLength(result));

      } else {

      log.info("【方法返回】{}() - 返回值: {}", methodName, result);

      }

      } else {

      log.info("【方法返回】{}() - 返回null", methodName);

      }

      }

    /**

    • 异常通知:记录异常信息
      */
      @AfterThrowing(pointcut = "serviceLayer()", throwing = "ex")
      public void logAfterThrowing(JoinPoint joinPoint, Exception ex) {
      String methodName = joinPoint.getSignature().getName();
      log.error("【方法异常】{}() - 异常类型: {}, 异常信息: {}",
      methodName, ex.getClass().getName(), ex.getMessage(), ex);
      }

    /**

    • 环绕通知:计算方法执行时间(最强大)

      */

      @Around("serviceLayer()")

      public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {

      long startTime = System.currentTimeMillis();

      String methodName = joinPoint.getSignature().getName();

      try {

      // 执行目标方法

      Object result = joinPoint.proceed();

      复制代码
       long elapsedTime = System.currentTimeMillis() - startTime;
       log.info("【方法执行】{}() - 执行时间: {}ms", methodName, elapsedTime);
       
       return result;

      } catch (IllegalArgumentException ex) {

      log.error("【参数非法】{}() - 参数错误: {}", methodName, joinPoint.getArgs());

      throw ex;

      }

      }

      }

      切面2:声明式事务管理

java

/**

  • 事务管理切面(简化版,Spring已有@Transactional)

  • 演示自定义事务逻辑

    */

    @Aspect

    @Component

    @Slf4j

    public class TransactionAspect {

    @Autowired

    private PlatformTransactionManager transactionManager;

    @Pointcut("@annotation(com.example.annotation.MyTransactional)")

    public void transactionalPointcut() {}

    @Around("transactionalPointcut()")

    public Object manageTransaction(ProceedingJoinPoint joinPoint) throws Throwable {

    TransactionStatus status = null;

    DefaultTransactionDefinition definition = new DefaultTransactionDefinition();

    definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

    definition.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);

    definition.setTimeout(30); // 30秒超时

    复制代码
     try {
         // 开启事务
         status = transactionManager.getTransaction(definition);
         log.info("【事务开始】方法: {}", joinPoint.getSignature().getName());
         
         // 执行目标方法
         Object result = joinPoint.proceed();
         
         // 提交事务
         transactionManager.commit(status);
         log.info("【事务提交】方法: {}", joinPoint.getSignature().getName());
         
         return result;
     } catch (Exception ex) {
         // 回滚事务
         if (status != null && !status.isCompleted()) {
             transactionManager.rollback(status);
             log.error("【事务回滚】方法: {} - 原因: {}", 
                      joinPoint.getSignature().getName(), ex.getMessage());
         }
         throw ex;
     }

    }

    }

// 自定义事务注解

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME)

public @interface MyTransactional {

Propagation propagation() default Propagation.REQUIRED;

Isolation isolation() default Isolation.DEFAULT;

int timeout() default -1;

boolean readOnly() default false;

}

切面3:接口限流

java

/**

  • 限流切面:防止接口被过度调用

    */

    @Aspect

    @Component

    @Slf4j

    public class RateLimitAspect {

    // 使用Guava的RateLimiter

    private final Map<String, RateLimiter> limiters = new ConcurrentHashMap<>();

    @Pointcut("@annotation(com.example.annotation.RateLimit)")

    public void rateLimitPointcut() {}

    @Around("rateLimitPointcut()")

    public Object rateLimit(ProceedingJoinPoint joinPoint) throws Throwable {

    MethodSignature signature = (MethodSignature) joinPoint.getSignature();

    Method method = signature.getMethod();

    RateLimit rateLimit = method.getAnnotation(RateLimit.class);

    复制代码
     // 生成限流器的key:类名+方法名
     String key = String.format("%s.%s", 
         method.getDeclaringClass().getName(), 
         method.getName());
     
     // 获取或创建限流器
     RateLimiter limiter = limiters.computeIfAbsent(key, 
         k -> RateLimiter.create(rateLimit.value()));
     
     // 尝试获取许可
     if (limiter.tryAcquire(rateLimit.timeout(), rateLimit.timeUnit())) {
         return joinPoint.proceed();
     } else {
         log.warn("【接口限流】{} 被限流,当前速率: {}/秒", 
                 key, rateLimit.value());
         throw new RateLimitException("请求过于频繁,请稍后再试");
     }

    }

    }

// 限流注解

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME)

public @interface RateLimit {

double value(); // 每秒允许的请求数

long timeout() default 0; // 等待超时时间

TimeUnit timeUnit() default TimeUnit.MILLISECONDS; // 时间单位

}

切面4:缓存切面

java

/**

  • 缓存切面:方法级缓存

    */

    @Aspect

    @Component

    @Slf4j

    public class CacheAspect {

    @Autowired

    private RedisTemplate<String, Object> redisTemplate;

    // 本地缓存(一级缓存)

    private final Cache<String, Object> localCache = Caffeine.newBuilder()

    .maximumSize(1000)

    .expireAfterWrite(5, TimeUnit.MINUTES)

    .build();

    @Pointcut("@annotation(com.example.annotation.Cacheable)")

    public void cacheablePointcut() {}

    @Around("cacheablePointcut()")

    public Object cache(ProceedingJoinPoint joinPoint) throws Throwable {

    MethodSignature signature = (MethodSignature) joinPoint.getSignature();

    Method method = signature.getMethod();

    Cacheable cacheable = method.getAnnotation(Cacheable.class);

    复制代码
     // 生成缓存key
     String cacheKey = generateCacheKey(joinPoint, cacheable);
     
     // 1. 尝试从本地缓存获取
     Object result = localCache.getIfPresent(cacheKey);
     if (result != null) {
         log.debug("【缓存命中】本地缓存 - key: {}", cacheKey);
         return result;
     }
     
     // 2. 尝试从Redis获取
     result = redisTemplate.opsForValue().get(cacheKey);
     if (result != null) {
         log.debug("【缓存命中】Redis缓存 - key: {}", cacheKey);
         // 回填本地缓存
         localCache.put(cacheKey, result);
         return result;
     }
     
     // 3. 执行目标方法
     log.debug("【缓存未命中】执行方法 - key: {}", cacheKey);
     result = joinPoint.proceed();
     
     if (result != null) {
         // 4. 写入缓存
         long ttl = cacheable.ttl();
         if (ttl > 0) {
             redisTemplate.opsForValue().set(cacheKey, result, ttl, TimeUnit.SECONDS);
         } else {
             redisTemplate.opsForValue().set(cacheKey, result);
         }
         localCache.put(cacheKey, result);
     }
     
     return result;

    }

    private String generateCacheKey(ProceedingJoinPoint joinPoint, Cacheable cacheable) {

    StringBuilder keyBuilder = new StringBuilder(cacheable.prefix());

    复制代码
     Object[] args = joinPoint.getArgs();
     if (args.length > 0) {
         keyBuilder.append(":");
         // 使用参数生成key
         for (Object arg : args) {
             if (arg != null) {
                 keyBuilder.append(arg.toString()).append("_");
             }
         }
     }
     
     // 添加方法签名
     keyBuilder.append(":").append(joinPoint.getSignature().toShortString());
     
     return keyBuilder.toString();

    }

    }

    切面5:权限校验

java

/**

  • 权限校验切面

    */

    @Aspect

    @Component

    @Slf4j

    public class PermissionAspect {

    @Autowired

    private HttpServletRequest request;

    @Pointcut("@annotation(com.example.annotation.RequiresPermission)")

    public void permissionPointcut() {}

    @Before("permissionPointcut()")

    public void checkPermission(JoinPoint joinPoint) {

    MethodSignature signature = (MethodSignature) joinPoint.getSignature();

    Method method = signature.getMethod();

    RequiresPermission requiresPermission = method.getAnnotation(RequiresPermission.class);

    复制代码
     // 从请求中获取用户信息(实际项目从Session或Token获取)
     String currentUser = (String) request.getSession().getAttribute("currentUser");
     if (currentUser == null) {
         throw new AuthenticationException("用户未登录");
     }
     
     // 获取用户权限(这里简化为从配置获取,实际从数据库查)
     Set<String> userPermissions = getUserPermissions(currentUser);
     String[] requiredPermissions = requiresPermission.value();
     
     // 检查是否拥有所有必需权限
     for (String requiredPermission : requiredPermissions) {
         if (!userPermissions.contains(requiredPermission)) {
             throw new AuthorizationException(
                 String.format("用户[%s]没有权限[%s]", currentUser, requiredPermission));
         }
     }
     
     log.info("【权限校验通过】用户: {}, 方法: {}", 
             currentUser, method.getName());

    }

    private Set getUserPermissions(String username) {

    // 模拟从数据库获取用户权限

    Set permissions = new HashSet<>();

    if ("admin".equals(username)) {

    permissions.addAll(Arrays.asList("user:add", "user:delete", "user:update", "user:query"));

    } else if ("user".equals(username)) {

    permissions.add("user:query");

    }

    return permissions;

    }

    }

四、Spring配置方式演进史

4.1 XML配置 vs 注解配置 vs Java配置

三种配置方式对比实例:

java

// ===================== 方式1:XML配置(传统方式) =====================

// applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>

复制代码
<!-- 开启注解扫描 -->
<context:component-scan base-package="com.example"/>

<!-- 数据源配置 -->
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
    <property name="driverClassName" value="${db.driver}"/>
    <property name="jdbcUrl" value="${db.url}"/>
    <property name="username" value="${db.username}"/>
    <property name="password" value="${db.password}"/>
    <property name="maximumPoolSize" value="${db.maxPoolSize}"/>
</bean>

<!-- 事务管理器 -->
<bean id="transactionManager" 
      class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

<!-- 开启注解事务 -->
<tx:annotation-driven transaction-manager="transactionManager"/>

// ===================== 方式2:注解配置(混合方式) =====================

@Configuration

@ComponentScan(basePackages = "com.example")

@PropertySource("classpath:application.properties")

@EnableTransactionManagement

public class AppConfig {

复制代码
@Value("${db.driver}")
private String driverClassName;

@Value("${db.url}")
private String url;

@Value("${db.username}")
private String username;

@Value("${db.password}")
private String password;

@Bean
public DataSource dataSource() {
    HikariDataSource dataSource = new HikariDataSource();
    dataSource.setDriverClassName(driverClassName);
    dataSource.setJdbcUrl(url);
    dataSource.setUsername(username);
    dataSource.setPassword(password);
    dataSource.setMaximumPoolSize(20);
    return dataSource;
}

@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
    return new DataSourceTransactionManager(dataSource);
}

}

// ===================== 方式3:Java配置(现代方式) =====================

@Configuration

public class DataSourceConfig {

复制代码
@ConfigurationProperties(prefix = "spring.datasource.hikari")
@Bean
public DataSource dataSource() {
    return DataSourceBuilder.create()
            .type(HikariDataSource.class)
            .build();
}

}

@Configuration

@EnableTransactionManagement

public class TransactionConfig {

复制代码
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
    return new DataSourceTransactionManager(dataSource);
}

}

配置方式演进路线图:

演进路径:XML → 注解+XML → Java配置+注解 → 自动配置

阶段1:Spring 1.x-2.x

主要方式:XML配置

优点:集中管理、与代码分离

缺点:冗长、类型不安全、重构困难

阶段2:Spring 2.5-3.x

主要方式:注解+XML混合

核心注解:@Component, @Autowired, @Service

优点:简化配置、类型安全

缺点:配置分散

阶段3:Spring 4.x-5.x

主要方式:Java配置为主

核心注解:@Configuration, @Bean

优点:类型安全、IDE友好、条件化配置

缺点:需要理解Java配置

阶段4:Spring Boot时代

主要方式:自动配置

核心理念:约定优于配置

优点:开箱即用、极简配置

缺点:黑盒、需要理解自动配置原理

4.2 条件化配置:智能Bean注册

java

// 根据环境注册不同的Bean

@Configuration

public class EnvironmentConfig {

复制代码
// 条件1:当存在某个类时
@ConditionalOnClass(name = "com.fasterxml.jackson.databind.ObjectMapper")
@Bean
public ObjectMapper objectMapper() {
    ObjectMapper mapper = new ObjectMapper();
    mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    return mapper;
}

// 条件2:根据配置属性
@ConditionalOnProperty(prefix = "app.cache", name = "type", havingValue = "redis")
@Bean
public CacheManager redisCacheManager(RedisConnectionFactory factory) {
    return RedisCacheManager.create(factory);
}

@ConditionalOnProperty(prefix = "app.cache", name = "type", havingValue = "caffeine")
@Bean
public CacheManager caffeineCacheManager() {
    CaffeineCacheManager cacheManager = new CaffeineCacheManager();
    cacheManager.setCaffeine(Caffeine.newBuilder()
        .maximumSize(1000)
        .expireAfterWrite(10, TimeUnit.MINUTES));
    return cacheManager;
}

// 条件3:根据环境激活的profile
@Profile("dev")
@Bean
public DataSource devDataSource() {
    // 开发环境使用H2内存数据库
    return new EmbeddedDatabaseBuilder()
        .setType(EmbeddedDatabaseType.H2)
        .addScript("schema-dev.sql")
        .build();
}

@Profile("prod")
@Bean
public DataSource prodDataSource(
        @Value("${db.url}") String url,
        @Value("${db.username}") String username,
        @Value("${db.password}") String password) {
    // 生产环境使用MySQL
    HikariDataSource dataSource = new HikariDataSource();
    dataSource.setJdbcUrl(url);
    dataSource.setUsername(username);
    dataSource.setPassword(password);
    return dataSource;
}

// 条件4:根据Bean是否存在
@ConditionalOnMissingBean(name = "defaultService")
@Bean
public DefaultService defaultService() {
    return new DefaultService();
}

// 条件5:自定义条件
@Conditional(OnWindowsCondition.class)
@Bean
public WindowsService windowsService() {
    return new WindowsService();
}

@Conditional(OnLinuxCondition.class)
@Bean  
public LinuxService linuxService() {
    return new LinuxService();
}

}

// 自定义条件类

public class OnWindowsCondition implements Condition {

@Override

public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {

String os = context.getEnvironment().getProperty("os.name");

return os != null && os.toLowerCase().contains("windows");

}

}

public class OnLinuxCondition implements Condition {

@Override

public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {

String os = context.getEnvironment().getProperty("os.name");

return os != null && os.toLowerCase().contains("linux");

}

}

五、Spring最佳实践与常见坑点

5.1 必须掌握的10个最佳实践

实践1:使用构造器注入

java

// ✅ 正确做法

@Service

public class UserService {

private final UserRepository userRepository;

private final EmailService emailService;

复制代码
// 构造器注入
public UserService(UserRepository userRepository, EmailService emailService) {
    this.userRepository = userRepository;
    this.emailService = emailService;
}

}

// ❌ 错误做法

@Service

public class UserService {

@Autowired // 字段注入

private UserRepository userRepository;

复制代码
@Autowired  // Setter注入(不必要)
public void setEmailService(EmailService emailService) {
    this.emailService = emailService;
}

}

实践2:合理使用Bean作用域

java

// 无状态服务使用singleton

@Scope("singleton")

@Service

public class CalculatorService {

// 线程安全的方法

public int add(int a, int b) {

return a + b;

}

}

// 有状态对象使用prototype

@Scope("prototype")

@Component

public class ShoppingCart {

private List items = new ArrayList<>();

复制代码
public void addItem(Product product) {
    items.add(product);
}

}

实践3:正确配置事务传播行为

java

@Service

@Transactional

public class OrderService {

复制代码
// REQUIRED(默认):如果当前没有事务,就新建一个事务
@Transactional(propagation = Propagation.REQUIRED)
public void createOrder(Order order) {
    // 业务逻辑
}

// REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateInventory(Long productId, int quantity) {
    // 库存更新,需要独立事务
}

// NOT_SUPPORTED:以非事务方式执行,如果当前存在事务,把当前事务挂起
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void generateReport() {
    // 生成报表,不需要事务
}

// NESTED:如果当前存在事务,则在嵌套事务内执行
@Transactional(propagation = Propagation.NESTED)
public void processPayment(Payment payment) {
    // 支付处理
}

}

实践4:异常处理策略

java

@RestControllerAdvice

public class GlobalExceptionHandler {

复制代码
// 处理业务异常
@ExceptionHandler(BusinessException.class)
public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException ex) {
    ErrorResponse error = new ErrorResponse(
        ex.getErrorCode(), 
        ex.getMessage(),
        LocalDateTime.now()
    );
    return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
}

// 处理数据验证异常
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleValidationException(
        MethodArgumentNotValidException ex) {
    List<String> errors = ex.getBindingResult()
        .getFieldErrors()
        .stream()
        .map(FieldError::getDefaultMessage)
        .collect(Collectors.toList());
    
    ErrorResponse error = new ErrorResponse(
        "VALIDATION_ERROR",
        "参数验证失败",
        errors
    );
    return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
}

// 处理未捕获异常
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleAllException(Exception ex) {
    log.error("未捕获异常: ", ex);
    
    ErrorResponse error = new ErrorResponse(
        "INTERNAL_ERROR",
        "系统内部错误,请稍后重试",
        LocalDateTime.now()
    );
    return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
}

}

5.2 必须避免的10个常见坑点

坑点1:循环依赖问题

java

// ❌ 错误的循环依赖

@Service

public class ServiceA {

@Autowired

private ServiceB serviceB; // 依赖B

}

@Service

public class ServiceB {

@Autowired

private ServiceA serviceA; // 依赖A,形成循环

}

// ✅ 解决方案1:使用构造器注入(Spring会检测)

@Service

public class ServiceA {

private final ServiceB serviceB;

复制代码
public ServiceA(ServiceB serviceB) {
    this.serviceB = serviceB;
}

}

// ✅ 解决方案2:使用@Lazy延迟加载

@Service

public class ServiceA {

@Lazy

@Autowired

private ServiceB serviceB;

}

// ✅ 解决方案3:重构设计,打破循环

@Service

public class ServiceA {

// 不直接依赖B,通过接口或中间层

}

@Service

public class ServiceB {

// 同样不直接依赖A

}

@Component

public class ServiceC {

// 协调A和B的交互

}

坑点2:事务失效场景

java

@Service

public class TransactionService {

复制代码
// 场景1:方法不是public
@Transactional
private void privateMethod() {  // ❌ 事务不会生效
    // ...
}

// 场景2:异常被catch吞掉
@Transactional
public void methodWithCatch() {
    try {
        // 数据库操作
        throw new RuntimeException("业务异常");
    } catch (Exception e) {
        // ❌ 异常被catch,事务不会回滚
        log.error("发生异常", e);
    }
}

// 场景3:异常类型不匹配
@Transactional(rollbackFor = RuntimeException.class)
public void methodWithException() throws Exception {
    throw new Exception("检查异常");  // ❌ Exception不是RuntimeException
}

// 场景4:自调用问题
public void selfInvocation() {
    this.transactionalMethod();  // ❌ 自调用,事务失效
}

@Transactional
public void transactionalMethod() {
    // ...
}

// ✅ 正确做法
@Transactional(rollbackFor = Exception.class)
public void correctMethod() {
    try {
        // 业务操作
    } catch (BusinessException e) {
        // 业务异常,需要回滚
        throw e;
    } catch (Exception e) {
        // 其他异常,记录日志后重新抛出
        log.error("系统异常", e);
        throw new RuntimeException(e);
    }
}

}

坑点3:Bean的线程安全问题

java

// ❌ 错误示例:singleton Bean中定义可变成员变量

@Service

public class UnsafeService {

private List data = new ArrayList<>(); // 可变的成员变量

复制代码
public void addData(String item) {
    data.add(item);  // ❌ 多线程下不安全
}

}

// ✅ 解决方案1:使用ThreadLocal

@Service

public class SafeService1 {

private ThreadLocal<List> threadLocalData =

ThreadLocal.withInitial(ArrayList::new);

复制代码
public void addData(String item) {
    threadLocalData.get().add(item);  // ✅ 线程隔离
}

public void clearThreadLocal() {
    threadLocalData.remove();  // 重要:使用后清理,防止内存泄漏
}

}

// ✅ 解决方案2:使用方法局部变量

@Service

public class SafeService2 {

// 不定义成员变量,所有数据通过方法参数传递

public List processData(List input) {

List result = new ArrayList<>(); // ✅ 局部变量,线程安全

// 处理逻辑

return result;

}

}

// ✅ 解决方案3:使用并发集合

@Service

public class SafeService3 {

private final ConcurrentHashMap<String, Object> cache =

new ConcurrentHashMap<>(); // ✅ 线程安全的集合

复制代码
public void putToCache(String key, Object value) {
    cache.put(key, value);
}

}

六、Spring学习路线图与实战建议

6.1 从入门到精通的5个阶段

阶段1:核心基础(1-2周)

学习重点:

✓ IoC容器原理与使用

✓ Bean生命周期与管理

✓ 依赖注入的三种方式

✓ 常用注解:@Component, @Autowired, @Service等

实战项目:学生管理系统

  • 使用XML配置实现
  • 使用注解配置重构
  • 使用Java配置再重构
    阶段2:AOP与事务(1-2周)

学习重点:

✓ AOP核心概念与实现

✓ 通知类型与使用场景

✓ 声明式事务管理

✓ 事务传播行为与隔离级别

实战项目:银行转账系统

  • 实现转账业务
  • 添加事务管理
  • 使用AOP添加日志和监控
    阶段3:数据访问(2-3周)

学习重点:

✓ Spring JDBC与JdbcTemplate

✓ Spring Data JPA

✓ 多数据源配置

✓ 缓存集成

实战项目:电商商品系统

  • 实现CRUD操作
  • 集成Redis缓存
  • 配置读写分离
    阶段4:Web开发(2-3周)

学习重点:

✓ Spring MVC架构

✓ RESTful API设计

✓ 参数绑定与验证

✓ 异常处理与统一响应

实战项目:博客系统API

  • 设计RESTful接口
  • 实现用户认证
  • 添加API文档
    阶段5:高级特性(3-4周)

学习重点:

✓ Spring Security安全框架

✓ 响应式编程WebFlux

✓ 消息队列集成

✓ 微服务与Spring Cloud

实战项目:微服务架构系统

  • 拆分微服务
  • 实现服务间通信
  • 添加服务监控

6.2 学习资源推荐

官方文档:

Spring Framework Documentation

Spring Guides

经典书籍:

《Spring实战(第5版)》- Craig Walls

《Spring Boot编程思想》- 小马哥

《Spring源码深度解析》- 郝佳

视频教程:

B站:尚硅谷Spring5框架最新版教程

慕课网:Spring MVC+Spring+MyBatis整合开发

极客时间:Spring Boot与Kubernetes云原生微服务实践

实战练习平台:

GitHub:搜索Spring相关开源项目

LeetCode:刷Spring相关面试题

自己动手:从0到1搭建完整项目

6.3 面试常见问题与回答

Q1:Spring框架的核心是什么?

A:Spring框架的核心是IoC(控制反转)和AOP(面向切面编程)。

IoC通过依赖注入实现对象的创建和依赖关系的管理,降低了组件间的耦合度。

AOP通过将横切关注点(如日志、事务)模块化,提高了代码的复用性和可维护性。

Q2:BeanFactory和ApplicationContext有什么区别?

A:BeanFactory是Spring的基础IoC容器,提供基本的依赖注入功能。

ApplicationContext是BeanFactory的子接口,提供了更多企业级功能:

  1. 国际化支持
  2. 事件发布机制
  3. 资源加载便利
  4. AOP集成
    在实际开发中,通常使用ApplicationContext。
    Q3:Spring Bean的作用域有哪些?

A:Spring支持6种Bean作用域:

  1. singleton:默认,每个Spring容器中只有一个实例
  2. prototype:每次请求都创建新实例
  3. request:每个HTTP请求创建一个实例(Web环境)
  4. session:每个HTTP Session创建一个实例(Web环境)
  5. application:每个ServletContext创建一个实例(Web环境)
  6. websocket:每个WebSocket会话创建一个实例(WebSocket环境)

结语:开启Spring之旅

Spring框架作为Java企业级开发的基石,掌握它不仅是为了应对工作需求,更是为了构建可维护、可扩展的软件系统。学习Spring的过程可能会遇到一些挑战,但记住:

每个遇到的问题都是成长的机会,每个解决的bug都是经验的积累。

下一步行动建议:

立即动手:不要只看不练,从今天开始写代码

由浅入深:先掌握核心概念,再学习高级特性

实践为主:通过实际项目巩固理论知识

参与社区:在CSDN、GitHub上与其他开发者交流

持续学习:Spring生态在不断发展,保持学习状态

学习宣言:

从今天起,做一个Spring开发者

关心IoC和AOP

我有一个项目,面向对象,春暖花开

资源福利:

关注我并私信"Spring入门",获取:

本文完整代码示例

Spring学习脑图PDF

常见问题解决方案集

实战项目模板

让我们在Spring的世界里,一起编写优雅的代码,构建强大的系统!🚀

下期预告:《SpringBoot入门:让Spring开发飞起来》

将带你了解:

SpringBoot如何简化Spring配置

自动配置原理揭秘

快速构建RESTful API

生产级应用的最佳实践

互动问题:

你在学习Spring过程中遇到的最大困难是什么?

在评论区分享,我将选取3位同学的问题进行详细解答!

今日行动:

点赞收藏本文,方便随时查阅

关注我,获取更多Spring系列教程

在本地环境运行第一个Spring程序

在评论区打卡你的学习进度

祝学习顺利,coding愉快! 💪

相关推荐
czliutz5 小时前
Windows系统创建启动Flask虚拟环境
windows·python·flask
GIS萬事通5 小时前
基于arcgis制作深度学习标签并基于python自动化预处理样本
python·深度学习·arcgis·边缘计算
青云交5 小时前
Java 大视界 -- Java 大数据实战:分布式架构重构气象预警平台(2 小时→2 分钟)
java·java 大数据 气象预警平台·flink 实时数据清洗·spark 区域定制模型·气象灾害预警系统
布局呆星5 小时前
面向对象中的封装-继承-多态
开发语言·python
柏林以东_5 小时前
异常的分类与用法
java·开发语言
sxy_97615 小时前
AX86u官方固件温度监控(CPU,WIFI芯片)
python·docker·curl·nc·nas·温度·ax86u
诗词在线5 小时前
适合赞美风景的诗词名句汇总
python·风景
专注API从业者5 小时前
淘宝商品 API 接口架构解析:从请求到详情数据返回的完整链路
java·大数据·开发语言·数据库·架构
学嵌入式的小杨同学5 小时前
【嵌入式 C 语言实战】栈、队列、二叉树核心解析:存储原理 + 应用场景 + 实现思路
linux·c语言·网络·数据结构·数据库·后端·spring