轩辕李的博客 轩辕李的博客
首页
  • Java
  • Spring
  • 其他语言
  • 工具
  • HTML&CSS
  • JavaScript
  • 分布式
  • 代码质量管理
  • 基础
  • 操作系统
  • 计算机网络
  • 编程范式
  • 安全
  • 中间件
  • 心得
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

轩辕李

勇猛精进,星辰大海
首页
  • Java
  • Spring
  • 其他语言
  • 工具
  • HTML&CSS
  • JavaScript
  • 分布式
  • 代码质量管理
  • 基础
  • 操作系统
  • 计算机网络
  • 编程范式
  • 安全
  • 中间件
  • 心得
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Java

  • Spring

    • 基础

    • 框架

      • Spring容器初始化过程和Bean生命周期探究
      • Spring容器:从依赖管理到注解配置全解析
        • 一、Spring 容器的依赖管理
          • 1、依赖注入
          • 1.1、构造器注入
          • 1.2、Setter 方法注入
          • 1.3、字段注入
          • 2、依赖与配置的详细信息
          • 2.1、详细的依赖和配置
          • 2.2、使用depends-on
          • 3、懒加载的 Beans
          • 3.1、懒加载 Beans 的原理
          • 3.2、懒加载 Beans 的应用场景
          • 4、自动装配协作器
          • 4.1、自动装配的原理
          • 4.2、自动装配的配置
          • 5、方法注入
        • 二、基于注解的容器配置
          • 1、使用@Autowired
          • 1.1、@Autowired的基本用法
          • 1.2、@Autowired的高级特性
          • 2、使用@Primary或@Fallback微调自动装配
          • 2.1、@Primary的用法
          • 2.2、@Fallback的用法
          • 3、使用限定符微调自动装配
          • 3.1、限定符的基本概念
          • 3.2、限定符的应用示例
          • 4、使用泛型作为自动装配限定符
          • 4.1、泛型限定符的原理
          • 4.2、泛型限定符的示例
          • 5、使用CustomAutowireConfigurer
          • 6、使用@Resource注入
          • 6.1、@Resource的基本用法
          • 6.2、@Resource与@Autowired的比较
          • 7、使用@Value
          • 7.1、@Value的基本用法
          • 7.2、@Value的高级用法
          • 8、使用@PostConstruct和@PreDestroy
          • 8.1、@PostConstruct的用法
          • 8.2、@PreDestroy的用法
        • 三、定制 Bean 的性质
          • 1、实现 InitializingBean 和 DisposableBean 接口
          • 2、使用 BeanPostProcessor 接口
          • 3、使用 FactoryBean 接口
        • 四、使用 JSR 330 标准注解
          • 1、@Inject 注解
          • 2、@Named 注解
          • 3、@Provider 注解
        • 五、基于 Java 的容器配置
          • 1、基本概念
          • 1.1、@Bean和@Configuration
          • 1.2、实例化 Spring 容器
          • 2、使用@Bean注解
          • 2.1、@Bean的基本用法
          • 2.2、@Bean的高级特性
          • 3、使用@Configuration注解
          • 3.1、@Configuration的基本概念
          • 3.2、@Configuration的应用示例
          • 4、组合基于 Java 的配置类
          • 4.1、配置类的组合方式
          • 4.2、以 @Configuration 类为中心结合使用 @ImportResource 和 XML
          • 5、高级配置技巧
          • 5.1、条件化注册(@Conditional)
          • 5.2、Profile 环境隔离
          • a、什么是 Profile
          • b、如何使用 Profile
          • 使用 @Profile 注解
          • 在 XML 配置中使用 Profile
          • c、激活 Profile
          • 通过 JVM 参数激活
          • 通过代码激活
          • d、多 Profile 组合
          • e、默认 Profile
          • f、属性文件的Profile隔离
        • 六、总结
      • Spring事件探秘
      • Spring AOP的应用
      • Spring 事务管理
      • Spring中的资源访问:Resource接口
      • Spring中的验证、数据绑定和类型转换
      • Spring表达式语言(SpELl)
      • Spring中的属性占位符
      • Spring数据缓冲区与编解码器详解
      • Spring对于原生镜像和AOT的支持
      • Spring中的数据访问:JDBC、R2DBC、ORM、Object-XML
      • Spring中的Web访问:Servlet API支持
      • Spring中的Web访问:WebSocket支持
      • Spring中的Web访问:响应式栈 WebFlux
      • Spring中的集成测试与单元测试
      • Spring与多种技术的集成
      • Spring框架版本新特性
    • Spring Boot

    • 集成

  • 其他语言

  • 工具

  • 后端
  • Spring
  • 框架
轩辕李
2024-03-03
目录

Spring容器:从依赖管理到注解配置全解析

# 一、Spring 容器的依赖管理

# 1、依赖注入

依赖注入(Dependency Injection,简称 DI)是一种设计模式,它通过将对象依赖关系的创建和管理从对象内部转移到外部,从而降低对象之间的耦合度。

在 Spring 框架中,依赖注入是实现控制反转(Inversion of Control,简称 IoC)的主要方式。

通过依赖注入,Spring 容器负责创建对象实例,并将对象所依赖的其他对象注入到该对象中,使得对象之间的依赖关系由容器来管理,而不是由对象自身来创建和维护。

# 1.1、构造器注入

构造器注入是依赖注入的一种方式,它通过对象的构造函数来注入依赖对象。

在 Spring 配置文件中,可以使用<constructor-arg>标签来指定构造函数的参数。

构造器注入的优点是可以确保对象在创建时就已经拥有了所有必要的依赖,并且依赖关系清晰明了。缺点是如果依赖过多,构造函数的参数列表会变得冗长,不利于维护。

示例代码:

<bean id="userService" class="com.example.UserService">
   <constructor-arg ref="userRepository"/>
</bean>

<bean id="userRepository" class="com.example.UserRepository"/>
public class UserService {

   private UserRepository userRepository;

   public UserService(UserRepository userRepository) {
       this.userRepository = userRepository;
   }

   // 业务方法
}

# 1.2、Setter 方法注入

Setter 方法注入是另一种常见的依赖注入方式,它通过对象的 Setter 方法来注入依赖对象。在 Spring 配置文件中,使用<property>标签来指定 Setter 方法的参数。Setter 方法注入的优点是灵活性高,可以在对象创建后动态地修改依赖关系。缺点是可能会导致对象在使用时依赖尚未被注入,从而引发空指针异常。

示例代码:

<bean id="userService" class="com.example.UserService">
   <property name="userRepository" ref="userRepository"/>
</bean>

<bean id="userRepository" class="com.example.UserRepository"/>
public class UserService {

   private UserRepository userRepository;

   public void setUserRepository(UserRepository userRepository) {
       this.userRepository = userRepository;
   }

   // 业务方法
}

# 1.3、字段注入

字段注入是一种通过直接在类的字段上使用注解(如@Autowired)来实现依赖注入的方式。这种方式简洁明了,代码量少,不需要编写构造函数或 Setter 方法。然而,字段注入也有一些缺点,例如它使得代码的可测试性变差,因为无法在测试时轻易地替换依赖对象。

示例代码:

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

@Service
public class UserService {

   @Autowired
   private UserRepository userRepository;

   // 业务方法
}

# 2、依赖与配置的详细信息

# 2.1、详细的依赖和配置

在 Spring 中,依赖和配置信息可以通过 XML 配置文件或注解来定义。

依赖配置包括指定依赖对象的类型、名称以及如何创建依赖对象等信息。配置信息还可以包括对象的属性值、初始化方法和销毁方法等。

通过详细的依赖和配置,Spring 容器能够准确地创建和管理对象及其依赖关系。

# 2.2、使用depends-on

depends-on属性用于指定一个 Bean 在初始化之前需要依赖的其他 Bean。当一个 Bean 使用了depends-on属性时,Spring 容器会先初始化depends-on指定的 Bean,然后再初始化当前 Bean。

这在某些情况下非常有用,例如当一个 Bean 需要依赖另一个 Bean 的初始化结果时。

示例代码:

<bean id="dataSource" class="com.example.DataSource"/>

<bean id="userRepository" class="com.example.UserRepository" depends-on="dataSource">
   <property name="dataSource" ref="dataSource"/>
</bean>

# 3、懒加载的 Beans

# 3.1、懒加载 Beans 的原理

懒加载(Lazy Loading)是指在需要使用 Bean 时才进行初始化,而不是在容器启动时就初始化所有的 Bean。

Spring 通过在 Bean 的定义中设置lazy-init="true"来实现懒加载。在使用注解配置时,可以使用@Lazy注解来实现懒加载。

懒加载的原理是利用代理机制,在容器启动时创建一个代理对象,当真正需要使用 Bean 时,代理对象才会去创建实际的 Bean 实例。

# 3.2、懒加载 Beans 的应用场景

懒加载适用于那些初始化过程比较耗时或者在应用启动阶段不需要立即使用的 Bean。例如,一些与数据库连接、远程服务调用相关的 Bean,使用懒加载可以加快应用的启动速度,提高系统的性能。

示例代码:

<bean id="userService" class="com.example.UserService" lazy-init="true"/>

# 4、自动装配协作器

# 4.1、自动装配的原理

自动装配(Autowiring)是 Spring 容器根据一定的规则自动为 Bean 装配依赖对象的机制。

Spring 提供了多种自动装配的方式,如按类型(byType)、按名称(byName)、构造函数(constructor)和自动检测(autodetect)等。

自动装配的原理是通过扫描容器中的 Bean 定义,根据自动装配的规则找到匹配的依赖对象,并将其注入到目标 Bean 中。

# 4.2、自动装配的配置

在 Spring 配置文件中,可以通过<bean>标签的autowire属性来配置自动装配的方式。例如,autowire="byType"表示按类型自动装配,autowire="byName"表示按名称自动装配。在使用注解配置时,可以使用@Autowired注解来实现自动装配。

示例代码:

<bean id="userService" class="com.example.UserService" autowire="byType"/>
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {

   private UserRepository userRepository;

   @Autowired
   public UserService(UserRepository userRepository) {
       this.userRepository = userRepository;
   }

   // 业务方法
}

# 5、方法注入

方法注入是指 Spring 容器能够动态地替换 Bean 中的方法实现。这种方式通常用于解决单例 Bean 中依赖非单例 Bean 的问题。Spring 通过使用 CGLIB 库来实现方法注入,它可以在运行时生成一个子类,并重写目标方法,从而实现对方法的替换。

示例代码:

import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {

   @Bean
   public MethodInvokingFactoryBean methodInvokingFactoryBean() {
       MethodInvokingFactoryBean factoryBean = new MethodInvokingFactoryBean();
       factoryBean.setTargetObject(new SomeService());
       factoryBean.setTargetMethod("someMethod");
       factoryBean.setArguments(new Object[]{"arg1", "arg2"});
       return factoryBean;
   }

}

class SomeService {

   public String someMethod(String arg1, String arg2) {
       return arg1 + " " + arg2;
   }

}

# 二、基于注解的容器配置

# 1、使用@Autowired

# 1.1、@Autowired的基本用法

@Autowired是 Spring 框架中最常用的注解之一,用于实现自动装配。它可以应用在字段、Setter 方法和构造函数上。当 Spring 容器扫描到带有@Autowired注解的元素时,会根据类型在容器中查找匹配的 Bean,并将其注入到目标元素中。

示例代码:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {

   @Autowired
   private UserRepository userRepository;

   // 业务方法
}

# 1.2、@Autowired的高级特性

@Autowired注解还支持一些高级特性,例如可以使用required属性来指定依赖是否必须存在。默认情况下,required属性为true,表示依赖必须存在,如果在容器中找不到匹配的 Bean,会抛出异常。

当required属性设置为false时,如果找不到匹配的 Bean,Spring 容器不会抛出异常,而是将依赖对象设置为null。

示例代码:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {

   @Autowired(required = false)
   private UserRepository userRepository;

   // 业务方法
}

# 2、使用@Primary或@Fallback微调自动装配

# 2.1、@Primary的用法

@Primary注解用于指定当有多个类型相同的 Bean 可供选择时,优先选择被@Primary注解标记的 Bean 进行自动装配。这在解决自动装配时的歧义问题时非常有用。

示例代码:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;

@Service
public class UserService {

   @Autowired
   private DataSource dataSource;

   // 业务方法
}

@Configuration
public class AppConfig {

   @Bean
   @Primary
   public DataSource dataSource1() {

       // 创建并返回数据源1

   }

   @Bean
   public DataSource dataSource2() {

       // 创建并返回数据源2

   }

}

# 2.2、@Fallback的用法

@Fallback注解用于指定当主要的 Bean 不可用时,作为备用的 Bean 进行自动装配。它通常与@Primary注解配合使用,提供一种更加灵活的自动装配策略。

示例代码:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Fallback;
import org.springframework.stereotype.Service;

@Service
public class UserService {

   @Autowired
   private DataSource dataSource;

   // 业务方法
}

@Configuration
public class AppConfig {

   @Bean
   @Primary
   public DataSource dataSource1() {

       // 创建并返回数据源1

   }

   @Bean
   @Fallback
   public DataSource dataSource2() {

       // 创建并返回数据源2

   }

}

# 3、使用限定符微调自动装配

# 3.1、限定符的基本概念

限定符(Qualifier)是 Spring 提供的一种更细粒度的自动装配控制方式。它可以在多个相同类型的 Bean 中,根据限定符的值来选择特定的 Bean 进行自动装配。限定符可以通过@Qualifier注解来定义。

# 3.2、限定符的应用示例

假设容器中有两个类型为DataSource的 Bean,分别命名为dataSource1和dataSource2。

可以通过@Qualifier注解来指定使用哪个DataSource进行自动装配,例如:@Autowired @Qualifier("dataSource1") DataSource dataSource;。

示例代码:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service
public class UserService {

   @Autowired
   @Qualifier("dataSource1")
   private DataSource dataSource;

   // 业务方法
}

@Configuration
public class AppConfig {

   @Bean
   public DataSource dataSource1() {
       // 创建并返回数据源1
   }

   @Bean
   public DataSource dataSource2() {
       // 创建并返回数据源2
   }

}

# 4、使用泛型作为自动装配限定符

# 4.1、泛型限定符的原理

使用泛型作为自动装配限定符是一种基于泛型类型信息来进行自动装配的方式。Spring 容器在进行自动装配时,会根据泛型的类型信息来匹配相应的 Bean。这种方式可以在一定程度上简化配置,并且提高代码的可读性和维护性。

# 4.2、泛型限定符的示例

假设有一个泛型接口Repository<T>,以及两个实现类UserRepository和OrderRepository。可以通过在注入时指定泛型类型来实现自动装配,例如:@Autowired Repository<User> userRepository;。

示例代码:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {

   @Autowired
   private Repository<User> userRepository;

   // 业务方法
}

public interface Repository<T> {
   // 接口方法
}

public class UserRepository implements Repository<User> {
   // 实现接口方法
}

# 5、使用CustomAutowireConfigurer

CustomAutowireConfigurer 是 Spring 框架中的一个类,它允许你自定义自动装配的规则。

通过 CustomAutowireConfigurer,你可以指定哪些类型应该通过自动装配注入到哪些属性中,这在一些特殊的场景下非常有用,比如你想要覆盖 Spring 默认的自动装配行为。

首先,我们定义几个示例类,用于演示 CustomAutowireConfigurer 的用法。

// 定义一个接口
interface MyService {
    void doSomething();
}

// 实现接口
class MyServiceImpl implements MyService {
    @Override
    public void doSomething() {
        System.out.println("Doing something...");
    }
}

// 定义一个需要注入 MyService 的类
class MyClient {
    private MyService myService;

    public void setMyService(MyService myService) {
        this.myService = myService;
    }

    public void performAction() {
        if (myService != null) {
            myService.doSomething();
        }
    }
}

使用 CustomAutowireConfigurer 进行自定义自动装配配置:

import org.springframework.beans.factory.config.CustomAutowireConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

@Configuration
public class AppConfig {

    // 定义 MyService 的 Bean
    @Bean
    public MyService myService() {
        return new MyServiceImpl();
    }

    // 定义 MyClient 的 Bean
    @Bean
    public MyClient myClient() {
        return new MyClient();
    }

    // 配置 CustomAutowireConfigurer
    @Bean
    public CustomAutowireConfigurer customAutowireConfigurer() {
        CustomAutowireConfigurer configurer = new CustomAutowireConfigurer();
        Map<Class<?>, Object> customEditors = new HashMap<>();
        // 指定 MyService 类型的属性应该注入 myService Bean
        customEditors.put(MyService.class, myService());
        configurer.setCustomEditors(customEditors);
        return configurer;
    }
}

测试配置:

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {
    public static void main(String[] args) {
        // 创建 Spring 应用上下文
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

        // 获取 MyClient Bean
        MyClient myClient = context.getBean(MyClient.class);

        // 调用方法
        myClient.performAction();

        // 关闭上下文
        context.close();
    }
}

代码解释:

  1. 定义示例类:MyService 是一个接口,MyServiceImpl 是它的实现类,MyClient 类包含一个 MyService 类型的属性,需要进行注入。
  2. 配置 CustomAutowireConfigurer:在 AppConfig 类中,我们创建了 CustomAutowireConfigurer 的 Bean,并通过 setCustomEditors 方法指定了 MyService 类型的属性应该注入 myService Bean。
  3. 测试配置:在 Main 类中,我们创建了 Spring 应用上下文,获取 MyClient Bean,并调用其 performAction 方法。由于我们使用了 CustomAutowireConfigurer 进行自定义自动装配,MyClient 类中的 MyService 属性会被正确注入。

注意事项:

  • CustomAutowireConfigurer 通常用于覆盖 Spring 默认的自动装配行为,在大多数情况下,Spring 的默认自动装配机制已经足够满足需求。
  • 确保在配置 CustomAutowireConfigurer 时,指定的 Bean 名称和类型是正确的,否则可能会导致注入失败。

# 6、使用@Resource注入

# 6.1、@Resource的基本用法

@Resource是 JSR-250 规范中定义的注解,用于实现依赖注入。它可以应用在字段和 Setter 方法上。

@Resource注解默认按照名称进行装配,如果找不到匹配的名称,则会按照类型进行装配。

示例代码:

import javax.annotation.Resource;

import org.springframework.stereotype.Service;

@Service
public class UserService {

   @Resource

   private UserRepository userRepository;

   // 业务方法
}

# 6.2、@Resource与@Autowired的比较

@Resource和@Autowired都可以实现依赖注入,但它们有一些区别。

@Autowired是 Spring 框架特有的注解,默认按照类型进行装配,而@Resource是 JSR-250 规范中的注解,默认按照名称进行装配。

在实际使用中,可以根据具体的需求选择合适的注解。

# 7、使用@Value

# 7.1、@Value的基本用法

@Value注解用于将配置文件中的值注入到 Bean 的属性中。它可以应用在字段、Setter 方法和构造函数参数上。例如,可以使用@Value("${database.url}")将配置文件中database.url的值注入到相应的属性中。

示例代码:

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Service
public class DatabaseService {

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

   // 业务方法
}

# 7.2、@Value的高级用法

@Value注解还支持一些高级用法,例如可以使用 SpEL 表达式来动态计算属性值。例如,@Value("#{systemProperties['os.name']}")可以获取系统属性os.name的值,并将其注入到属性中。

示例代码:

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Service
public class SystemInfoService {

   @Value("#{systemProperties['os.name']}")
   private String osName;

   // 业务方法
}

# 8、使用@PostConstruct和@PreDestroy

# 8.1、@PostConstruct的用法

@PostConstruct注解用于标记一个方法,该方法会在 Bean 被创建并初始化完成后自动调用。通常用于在 Bean 初始化后执行一些额外的操作,例如加载数据、建立连接等。

示例代码:

import javax.annotation.PostConstruct;
import org.springframework.stereotype.Service;

@Service
public class DataService {

   @PostConstruct
   public void init() {
       // 初始化操作,如加载数据
   }

   // 业务方法
}

# 8.2、@PreDestroy的用法

@PreDestroy注解用于标记一个方法,该方法会在 Bean 被销毁之前自动调用。通常用于在 Bean 销毁前执行一些清理操作,例如关闭连接、释放资源等。

示例代码:

import javax.annotation.PreDestroy;
import org.springframework.stereotype.Service;

@Service
public class DataService {

   @PreDestroy
   public void destroy() {
       // 销毁前操作,如关闭连接
   }

   // 业务方法
}

# 三、定制 Bean 的性质

定制 Bean 的性质是指可以通过各种方式来改变 Bean 的行为和属性。

例如,可以通过实现InitializingBean接口和DisposableBean接口来定义 Bean 的初始化方法和销毁方法;可以通过使用BeanPostProcessor接口来对 Bean 进行后置处理,在 Bean 初始化前后执行自定义的逻辑;还可以通过使用FactoryBean接口来自定义 Bean 的创建逻辑。

# 1、实现 InitializingBean 和 DisposableBean 接口

InitializingBean接口只有一个afterPropertiesSet方法,在 Bean 的属性设置完成后,Spring 容器会调用该方法进行初始化操作;DisposableBean接口只有一个destroy方法,在 Bean 被销毁时,Spring 容器会调用该方法进行清理操作。

示例代码:

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;

@Component
public class CustomBean implements InitializingBean, DisposableBean {

   @Override
   public void afterPropertiesSet() throws Exception {
       System.out.println("CustomBean初始化操作");
   }

   @Override
   public void destroy() throws Exception {
       System.out.println("CustomBean销毁操作");
   }

}

# 2、使用 BeanPostProcessor 接口

BeanPostProcessor接口允许在 Bean 初始化前后执行自定义逻辑。通过实现该接口的postProcessBeforeInitialization和postProcessAfterInitialization方法,可以对 Bean 进行修改或增强。

示例代码:

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

@Component
public class CustomBeanPostProcessor implements BeanPostProcessor {

   @Override
   public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
       System.out.println("在" + beanName + "初始化之前执行");
       return bean;
   }

   @Override
   public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
       System.out.println("在" + beanName + "初始化之后执行");
       return bean;
   }

}

# 3、使用 FactoryBean 接口

FactoryBean接口用于自定义 Bean 的创建逻辑。通过实现该接口的getObject、getObjectType和isSingleton方法,可以创建出特殊的 Bean 实例。

示例代码:

import org.springframework.beans.factory.FactoryBean;
import org.springframework.stereotype.Component;

@Component
public class CustomFactoryBean implements FactoryBean<CustomBean> {

   @Override
   public CustomBean getObject() throws Exception {
       return new CustomBean();
   }

   @Override
   public Class<?> getObjectType() {
       return CustomBean.class;
   }

   @Override
   public boolean isSingleton() {
       return true;
   }

}

# 四、使用 JSR 330 标准注解

JSR 330(Java Specification Requests 330)是 Java 依赖注入的标准规范,Spring 对其提供了支持。主要注解包括@Inject、@Named和@Provider等。

# 1、@Inject 注解

@Inject注解类似于 Spring 的@Autowired,用于实现依赖注入。它按照类型进行自动装配。

示例代码:

import javax.inject.Inject;
import javax.inject.Named;

@Named
public class UserService {

   private UserRepository userRepository;

   @Inject
   public UserService(UserRepository userRepository) {
       this.userRepository = userRepository;
   }

   // 业务方法
}

# 2、@Named 注解

@Named注解用于给 Bean 命名,相当于 Spring 的@Component注解。可以在使用@Inject注解时,通过名称来指定要注入的 Bean。

示例代码:

import javax.inject.Inject;
import javax.inject.Named;

@Named("userRepository")

public class UserRepositoryImpl implements UserRepository {
   // 实现接口方法
}

@Named
public class UserService {

   private UserRepository userRepository;

   @Inject
   @Named("userRepository")
   public UserService(UserRepository userRepository) {
       this.userRepository = userRepository;
   }

   // 业务方法
}

# 3、@Provider 注解

@Provider注解用于提供一个创建对象的方法,当需要延迟创建对象或根据条件创建不同对象时非常有用。

示例代码:

import javax.inject.Provider;
import javax.inject.Singleton;

@Singleton
public class UserServiceProvider implements Provider<UserService> {

   @Override
   public UserService get() {
       return new UserService(new UserRepositoryImpl());
   }

}

# 五、基于 Java 的容器配置

# 1、基本概念

# 1.1、@Bean和@Configuration

@Bean注解用于在 Java 配置类中定义一个 Bean,相当于 XML 配置中的<bean>标签;@Configuration注解用于标记一个 Java 类为配置类,该类中的@Bean方法会被 Spring 容器扫描并注册 Bean。

# 1.2、实例化 Spring 容器

通过AnnotationConfigApplicationContext类可以实例化基于 Java 配置的 Spring 容器。

示例代码:

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {

   public static void main(String[] args) {
       AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
       UserService userService = context.getBean(UserService.class);

       // 使用userService
       context.close();
   }

}

# 2、使用@Bean注解

# 2.1、@Bean的基本用法

在配置类中,使用@Bean注解标记的方法返回的对象会被注册为 Spring 容器中的 Bean。

示例代码:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {

   @Bean
   public UserRepository userRepository() {
       return new UserRepositoryImpl();
   }

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

}

# 2.2、@Bean的高级特性

@Bean注解支持一些高级特性,例如可以指定 Bean 的作用域(@Scope)、初始化方法和销毁方法等。

示例代码:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

@Configuration
public class AppConfig {

   @Bean
   @Scope("prototype")
   public UserRepository userRepository() {
       return new UserRepositoryImpl();
   }

   @Bean(initMethod = "init", destroyMethod = "destroy")
   public UserService userService() {
       return new UserService(userRepository());
   }

}

# 3、使用@Configuration注解

# 3.1、@Configuration的基本概念

@Configuration注解标记的类是一个配置类,其中的@Bean方法会被 Spring 容器解析并注册 Bean。配置类本身也会被注册为一个 Bean,默认的 Bean 名称是配置类的类名首字母小写。

# 3.2、@Configuration的应用示例

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {

   @Bean
   public DataSource dataSource() {
       // 创建并返回数据源
   }

   @Bean
   public UserRepository userRepository() {
       return new UserRepositoryImpl(dataSource());
   }

}

# 4、组合基于 Java 的配置类

# 4.1、配置类的组合方式

可以通过@Import注解将多个配置类组合在一起,也可以在一个配置类中通过调用其他配置类的@Bean方法来复用配置。

组合配置类的示例:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
@Import(DataSourceConfig.class)
public class AppConfig {

   @Bean
   public UserRepository userRepository(DataSource dataSource) {
       return new UserRepositoryImpl(dataSource);
   }

}

@Configuration
public class DataSourceConfig {

   @Bean
   public DataSource dataSource() {
       // 创建并返回数据源
   }

}

# 4.2、以 @Configuration 类为中心结合使用 @ImportResource 和 XML

在以 @Configuration 类作为配置容器主要方式的应用程序中,有时仍需要使用一些 XML 配置。在这种场景下,你可以使用 @ImportResource 注解,仅定义所需的少量 XML 配置。

这样做可以实现以 Java 为中心的容器配置方式,并将 XML 配置量降至最低。

以下示例(包含一个配置类、一个定义 Bean 的 XML 文件、一个属性文件和 main() 方法)展示了如何使用 @ImportResource 注解实现按需使用 XML 的以 Java 为中心的配置方式:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.datasource.DriverManagerDataSource;

import javax.sql.DataSource;

@Configuration
@ImportResource("classpath:/com/acme/properties-config.xml")
public class AppConfig {

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

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

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

    @Bean
    public DataSource dataSource() {
        return new DriverManagerDataSource(url, username, password);
    }

    @Bean
    public AccountRepository accountRepository(DataSource dataSource) {
        return new JdbcAccountRepository(dataSource);
    }

    @Bean
    public TransferService transferService(AccountRepository accountRepository) {
        return new TransferServiceImpl(accountRepository);
    }
}

properties-config.xml 文件:

<beans>
    <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
</beans>

jdbc.properties 文件:

jdbc.url=jdbc:hsqldb:hsql://localhost/xdb
jdbc.username=sa
jdbc.password=

Java 主程序代码:

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
        TransferService transferService = ctx.getBean(TransferService.class);
        // ...
    }
}

上述代码中,AppConfig 类使用 @ImportResource 注解引入了 properties - config.xml 文件,该 XML 文件用于加载 jdbc.properties 中的属性配置。

在 AppConfig 类中,通过 @Value 注解注入属性值,并定义了数据源、账户仓库和转账服务等 Bean。最后,在 main 方法中创建 Spring 应用上下文并获取 TransferService Bean。

# 5、高级配置技巧

# 5.1、条件化注册(@Conditional)

@Configuration
public class DatabaseConfig {
    @Bean
    @Conditional(ProdEnvCondition.class)
    public DataSource prodDataSource() {
        return new HikariDataSource(prodConfig());
    }

    @Bean
    @ConditionalOnProperty(name = "cache.enabled", havingValue = "true")
    public CacheManager redisCache() {
        return new RedisCacheManager(redisTemplate());
    }
}

class ProdEnvCondition implements Condition {
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return "prod".equals(context.getEnvironment().getProperty("spring.profiles.active"));
    }
}

核心要点:

  • 通过实现Condition接口创建自定义条件判断逻辑
  • 条件注解可与属性值(@ConditionalOnProperty)、类存在性(@ConditionalOnClass)等组合使用
  • 条件检查发生在Bean定义注册阶段

# 5.2、Profile 环境隔离

# a、什么是 Profile

在软件开发中,我们通常会有不同的运行环境,如开发环境(Development)、测试环境(Testing)、生产环境(Production)等。每个环境可能有不同的配置,例如数据库连接信息、日志级别、缓存配置等。Spring 的 Profile 机制提供了一种方式来隔离这些不同环境的配置,使得我们可以根据不同的环境加载不同的 Bean 定义和配置。

Profile 本质上是一种逻辑分组,它允许我们将一组 Bean 定义和配置归为一个特定的环境。在应用启动时,我们可以指定要激活的 Profile,Spring 容器会只加载与该 Profile 相关的 Bean 定义。

# b、如何使用 Profile
# 使用 @Profile 注解

在 Java 配置类中,我们可以使用 @Profile 注解来指定某个 Bean 或配置类只在特定的 Profile 下生效。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

@Configuration
public class AppConfig {

    @Bean
    @Profile("dev")
    public DataSource devDataSource() {
        // 开发环境的数据源配置
        return new DriverManagerDataSource("jdbc:mysql://localhost:3306/devdb", "devuser", "devpassword");
    }

    @Bean
    @Profile("prod")
    public DataSource prodDataSource() {
        // 生产环境的数据源配置
        return new DriverManagerDataSource("jdbc:mysql://prodserver:3306/proddb", "produser", "prodpassword");
    }
}

在上述代码中,devDataSource 方法定义的数据源 Bean 只在 dev 这个 Profile 激活时才会被创建,而 prodDataSource 方法定义的数据源 Bean 只在 prod 这个 Profile 激活时才会被创建。

# 在 XML 配置中使用 Profile

除了 Java 配置,我们也可以在 XML 配置文件中使用 Profile。

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd">

    <beans profile="dev">
        <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="url" value="jdbc:mysql://localhost:3306/devdb"/>
            <property name="username" value="devuser"/>
            <property name="password" value="devpassword"/>
        </bean>
    </beans>

    <beans profile="prod">
        <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="url" value="jdbc:mysql://prodserver:3306/proddb"/>
            <property name="username" value="produser"/>
            <property name="password" value="prodpassword"/>
        </bean>
    </beans>
</beans>

在这个 XML 配置中,不同 Profile 下定义了不同的数据源 Bean。

# c、激活 Profile
# 通过 JVM 参数激活

在启动应用程序时,我们可以通过 -Dspring.profiles.active 参数来指定要激活的 Profile。

java -Dspring.profiles.active=dev -jar myapp.jar

上述命令指定了激活 dev 这个 Profile,Spring 容器会加载与 dev Profile 相关的 Bean 定义。

# 通过代码激活

在 Java 代码中,我们也可以通过编程的方式激活 Profile。

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.getEnvironment().setActiveProfiles("dev");
        context.register(AppConfig.class);
        context.refresh();

        // 使用上下文获取 Bean 等操作
    }
}
# d、多 Profile 组合

有时我们可能需要同时激活多个 Profile。可以通过逗号分隔的方式指定多个 Profile。

java -Dspring.profiles.active=dev,logging -jar myapp.jar

在上述命令中,同时激活了 dev 和 logging 两个 Profile,Spring 容器会加载与这两个 Profile 相关的所有 Bean 定义。

# e、默认 Profile

如果没有显式指定要激活的 Profile,Spring 会使用默认的 Profile。我们可以通过 spring.profiles.default 属性来设置默认的 Profile。

java -Dspring.profiles.default=dev -jar myapp.jar

或者在 Java 代码中设置:

context.getEnvironment().setDefaultProfiles("dev");
# f、属性文件的Profile隔离

除了Bean配置的方式,Spring也支持属性文件的 Profile 切换,它允许你根据不同的运行环境(如开发、测试、生产)加载不同的属性配置。

Spring 支持为不同的 Profile 定义不同的属性文件。属性文件的命名遵循特定的规则,一般是 application-{profile}.properties 或 application-{profile}.yaml 格式,其中 {profile} 是 Profile 的名称。

当激活某个 Profile 时,Spring 会优先加载对应的属性文件。


假设项目的资源目录(src/main/resources)下有以下属性文件:

src/main/resources/
├── application.properties
├── application-dev.properties
├── application-test.properties
└── application-prod.properties

application.properties:这是通用的属性文件,包含所有环境都适用的配置。

# 通用配置
app.name=MyApp

application-dev.properties:开发环境的特定配置。

# 开发环境数据库配置
db.url=jdbc:mysql://localhost:3306/dev_db
db.username=dev_user
db.password=dev_password

application-test.properties:测试环境的特定配置。

# 测试环境数据库配置
db.url=jdbc:mysql://testserver:3306/test_db
db.username=test_user
db.password=test_password

application-prod.properties:生产环境的特定配置。

# 生产环境数据库配置
db.url=jdbc:mysql://prodserver:3306/prod_db
db.username=prod_user
db.password=prod_password

在启动应用程序时,可以通过 -Dspring.profiles.active 参数指定要激活的 Profile。例如,要激活开发环境的 Profile,可以使用以下命令:

java -Dspring.profiles.active=dev -jar myapp.jar

此时,Spring 会先加载 application.properties 文件中的通用配置,然后再加载 application-dev.properties 文件中的配置,并覆盖相同属性名的配置。


也可以在 application.properties 文件中指定默认的 Profile:

spring.profiles.active=dev

这样,在没有通过其他方式指定 Profile 时,应用程序会默认激活 dev 这个 Profile。


在 Java 代码中,也可以通过编程的方式激活 Profile。以下是一个使用 Spring Boot 的示例:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;

@SpringBootApplication
public class MyApp {
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(MyApp.class);
        ConfigurableApplicationContext context = app.run(args);
        ConfigurableEnvironment env = context.getEnvironment();
        // 激活测试环境的 Profile
        env.setActiveProfiles("test");

        // 重新启动应用以加载新的 Profile 配置
        context.close();
        app.run(args);
    }
}

当存在多个属性文件和不同的配置方式时,Spring 的属性加载顺序如下:

  1. application.properties(或 application.yml)中的通用配置。
  2. 激活的 Profile 对应的属性文件(如 application-dev.properties)中的配置,相同属性名会覆盖通用配置。
  3. 通过 JVM 参数、系统环境变量等方式指定的属性,会覆盖前面加载的配置。

# 六、总结

通过以上全面的介绍,希望能帮助你深入理解 Spring 容器的各种配置方式及其应用。

在实际项目中,根据具体需求选择合适的配置方式,能够提高开发效率和系统的可维护性。

*祝你变得更强!

编辑 (opens new window)
上次更新: 2025/04/01
Spring容器初始化过程和Bean生命周期探究
Spring事件探秘

← Spring容器初始化过程和Bean生命周期探究 Spring事件探秘→

最近更新
01
Spring Boot版本新特性
09-15
02
Spring框架版本新特性
09-01
03
Spring Boot开发初体验
08-15
更多文章>
Theme by Vdoing | Copyright © 2018-2025 京ICP备2021021832号-2 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式