当前位置: 首页 > news >正文

ssm速通1(2/2)

本篇是对ssm速通1的补充

2、注入组件

依赖注入(Dependency Injection, DI)是Spring框架的核心特性之一,它通过将对象依赖关系的创建和管理外部化,实现了松耦合和更易测试的代码结构。

2.1、依赖注入的基本方式

字段最省事,构造最靠谱,Setter 最灵活

①、构造器注入 (Constructor Injection)

推荐方式,特别是对于必需依赖:

  @Servicepublic class UserService {private final UserRepository userRepository; // 可 final// Spring 4.3+ 对于单构造器可以省略@Autowiredpublic UserService(UserRepository userRepository) {this.userRepository = userRepository;}}

特点

  • 官方推荐(Spring 团队、Effective Spring)

  • 支持 finalimmutable 对象,测试直接 new

  • 启动期即可发现循环依赖,失败早暴露

  • 多依赖时参数列表过长 → 可转用 Setter / 工厂方法配置聚合

②、Setter注入 (Setter Injection)

适用于可选依赖:

  @Servicepublic class OrderService {private UserService userService;​@Autowiredpublic void setUserService(UserService userService) {this.userService = userService;}}

特点

  • 可选依赖:方法可带 @Autowired(required = false)

  • 适合“有默认值、运行期可能重新设置”场景

  • 可被多次调用(re-configuration、测试 mock)

  • 无法使用 final

③、字段注入 (Field Injection)

不推荐在生产代码中使用,主要用于测试或快速原型:

  @Servicepublic class ProductService {@Autowired        // 或 @Inject / @Resourceprivate ProductRepository productRepository;}

特点

  • 代码最少,可读性高

  • 测试需反射或容器,不易写纯单元测试

  • 无法加 final,线程安全语义弱

  • 循环依赖时容器只能降级用代理,问题被推迟到运行时

2.2、依赖注入的注解
①、@Autowired

@Autowired 是 Spring 框架的依赖注入(DI)核心注解,用来自动把容器里的 Bean 装配到字段、构造器、方法参数里,省去手动 getBean()

Ⅰ、基本语法
  @Servicepublic class OrderService {@Autowired                  // 1. 字段注入private UserService userService;}​// -------------------------------------------------------------@Servicepublic class OrderService {private final UserService userService;@Autowired                  // 2. 构造器注入(Spring 4.3+ 可省略)public OrderService(UserService userService) {this.userService = userService;}}​// -------------------------------------------------------------@Autowired                      // 3. 方法注入(也可用在 setter)public void prepare(UserService userService, ProductService productService) {...}
Ⅱ、特性
  • 默认要求依赖必须存在,可通过@Autowired(required=false)设为可选

  • 可与@Qualifier配合使用解决歧义

Ⅲ、自动装配流程

先按照类型找

  • 有且仅有一个找到,则直接注入,不在乎名字

  • 若按照类型找到多个,则再按照名称找,变量名就是要找的名称

    • 如果找到则直接注入

    • 否则没找到直接报错

②、@Resource

JSR-250标准注解,优先按名称匹配:

  @Resource(name="mySpecialBean")private SpecialBean bean;
③、@Inject

JSR-330标准注解,功能类似@Autowired

  @Injectprivate PaymentService paymentService;
④、@Resource@Autowired 注解深度对比

在Spring依赖注入中,@Resource@Autowired是两个最常用的注解,它们有相似之处但也有重要区别

Ⅰ、包来源即兼容性
特性@Autowired@Resource
所属标准Spring框架自有注解JSR-250 (Java标准注解)
包路径org.springframework.beans.factory.annotationjavax.annotation
引入版本Spring 2.5+Java EE 5+
环境@Autowired@Resource
Spring原生应用完全支持支持
JavaEE/JakartaEE需要Spring原生支持
Quarkus/Micronaut有限支持更好支持
单元测试需要Spring更通用

即理论上 @Resource 更灵活,可使用多个其他框架(包含 Spring 框架),但是 Spring 一家独大,所以使用 @Autowired 也够

Ⅱ、注入规则

@Autowired 注入规则

  • 默认按类型匹配 (byType)

  • 配合 @Qualifier 可实现按名称匹配

@Resource 注入规则

  • 默认按名称匹配 (byName)

  • 名称未指定时回退到按类型匹配

建议的话,二者均可用,在纯Spring环境中,@Autowired提供了更丰富的功能集成,而@Resource则在跨环境兼容性上更有优势。使用 @Autowired 就够了

2.3、处理依赖歧义

当同一类型有多个实现时,即上方提到的自动装配过程中按照类型找到多个,我们需要进行消除歧义:

①、使用@Qualifier精确选取

在 Spring 容器里,“先按类型、再按名称” 的注入策略遇到多个同类型 Bean 时,会抛NoUniqueBeanDefinitionException。@Qualifier 的核心价值就是“给每个候选 Bean 打标签”,然后在注入点按标签精确点名,从而消除歧义

前提:我们在Bean定义时已经给了名字

  @Configurationpublic class RepoConfig {@Bean("mysqlUserRepo")        // ① 定义时给名字public UserRepository mysqlRepo() { return new MySqlUserRepo(); }​@Bean("mongoUserRepo")public UserRepository mongoRepo() { return new MongoUserRepo(); }}@Servicepublic class UserService {@Autowired@Qualifier("mysqlUserRepo")   // 注入时点名private UserRepository userRepository;}// 相当于我们先通过查找 UserRepository 该类型的组件,发现多个实现,使用 @Qualifier 将其精确锁定到了一个名为 mysqlUserRepo 的 UserRepository 类型的实现
②、使用自定义限定符注解

前提:我们在Bean定义时已经给了名字

 @Bean@DatabaseType("mysql")public DataSource mysqlUserRepo() { return new MySqlUserRepo(); }​@Bean@DatabaseType("mongo")public DataSource mongoUserRepo() { return new MongoUserRepo(); }@Target({ElementType.FIELD, ElementType.PARAMETER})@Retention(RetentionPolicy.RUNTIME)@Qualifier       // 核心元注解public @interface DatabaseType {String value();}​// 使用@Autowired@DatabaseType("mysql")private DataSource dataSource;
③、使用@Primary默认组件

前提:我们在Bean定义时已经给了名字

 @Bean@DatabaseType("mysql")public DataSource mysqlUserRepo() { return new MySqlUserRepo(); }​@Primary       // 使用该注解,即当按照类型找到多个,默认选择这个@Bean@DatabaseType("mongo")public DataSource mongoUserRepo() { return new MongoUserRepo(); }@Qualifier("mysql")  // 当然即使默认是选择名为 mongo 的组件,可以使用 @Qualifier 切换为其他组件@Autowiredprivate DataSource dataSource;

注意有 @Primary 存在,修改属性名就不生效

2.4、特殊类型的依赖注入
①、集合注入
  @Autowiredprivate List<Validator> validators; // 注入所有Validator实现​@Autowiredprivate Map<String, Processor> processors; // key为bean名称
②、可选依赖
  // Java 8+@Autowiredprivate Optional<CacheManager> cacheManager;​// 或@Autowired(required = false)private CacheManager cacheManager;
③、延迟注入
  @Autowiredprivate ObjectProvider<ExpensiveService> expensiveServiceProvider;​public void someMethod() {ExpensiveService service = expensiveServiceProvider.getIfAvailable();// 或ExpensiveService service = expensiveServiceProvider.getObject();}
2.5、依赖注入的最佳实践
  1. 优先使用构造器注入,特别是对于必需依赖

  2. 对于可选依赖,考虑使用Setter注入或ObjectProvider

  3. 避免字段注入在生产代码中使用

  4. 使用@Qualifier或自定义限定符处理多实现情况

  5. 保持依赖不可变(final字段)

  6. 避免循环依赖,这是设计问题的信号

  7. 对于复杂对象的创建,考虑使用FactoryBean

2.6、Spring Boot中的依赖注入

Spring Boot简化了依赖注入的配置:

①、自动配置
  @SpringBootApplicationpublic class MyApp {public static void main(String[] args) {SpringApplication.run(MyApp.class, args);}}
②、条件化Bean
  @Bean@ConditionalOnMissingBeanpublic DataSource dataSource() {// 默认数据源配置}
③、属性驱动注入
  @Configuration@EnableConfigurationProperties(AppProperties.class)public class AppConfig {}​@ConfigurationProperties(prefix = "app")public class AppProperties {private String name;// getters/setters}
2.7、补充注解
①、@Value

@Value是Spring提供的最常用的属性注入注解,用于从各种来源注入值到Spring管理的Bean中。

Ⅰ、基本语法
  @Componentpublic class AppConfig {// 注入简单值@Value("defaultValue")      // 即默认 simpleValue = defaultValueprivate String simpleValue;// 注入系统属性@Value("${user.home}")private String userHome;// 注入环境变量@Value("${JAVA_HOME}")private String javaHome;// 注入配置文件属性@Value("${tom.age}")   // 即默认 myage = tom.age,tom.age是在 application.properties 中配置的private int myage;@Value("${app.timeout:30}")   // 即默认 timeout 取不到 app.timeout 时默认是 30 private int timeout;// 注入SpEL表达式,即使用#{}内可加 Spring 表达式语言@Value("#{1>2?1:2}")// 相当于 int max = 1>2?1:2;private int max;@Value("#{T(java.util.UUID).randomUUID().toString}")// 相当于 String myUUID = UUID.randomUUID().toStringprivate String myUUID;}
Ⅱ、支持的内容类型
表达式类型示例说明
字面量@Value("hello")直接字符串值
属性占位符@Value("${app.name}")从Environment解析
SpEL表达式@Value("#{systemProperties['user.region']}")Spring表达式语言
默认值@Value("${app.threads:4}")冒号后为默认值
类型转换@Value("${app.percentage}") double percent自动类型转换
②、@PropertySource

@PropertySource 是 Spring 3.1 开始提供的 “外挂”式配置加载器让容器在启动时把任意 .properties(或 XML/YAML,自定义工厂即可)文件读进来,转成 Environment 里的 PropertySource,随后就可通过 @Value@ConfigurationPropertiesEnvironment 接口随取随用。它只负责“加载”,不做注入;一次声明,全局可用

Ⅰ、核心功能
  1. 指定文件路径(classpath / file / URL)

  2. 支持多文件、编码、忽略缺失、自定义解析工厂

  3. 加载后内容并入 Environment,优先级 高于 application.properties 低于 命令行参数

  4. @Value@ConfigurationProperties 无缝配合

Ⅱ、基本语法

首先在 application.properties 的当前目录先创建对应的 jdbc.properties

  jdbc.url=jdbc:mysql://127.0.0.1:3306/demojdbc.user=rootjdbc.pwd=root

之后加载配置类即可

  @Configuration@PropertySource("classpath:jdbc.properties")   // 加载public class DataSourceConfig {jdbc.properties@Value("${jdbc.url}")                      // 使用,相当于从你自定义创建的 jdbc.properties 中取出// jdbc.url=jdbc:mysql://127.0.0.1:3306/demo 并且赋值给 urlprivate String url;​@Value("${jdbc.user}")private String user;​@Value("${jdbc.pwd}")private String pwd;​@Beanpublic DataSource dataSource() {return DataSourceBuilder.create().url(url).username(user).password(pwd).build();}}

即该注解可以帮助我们将原来所有要写入 application.properties 的配置属性分类存放,如商铺对应的配置属性新建一个 shop.properties 存放,用户对应的配置属性新建一个 user.properties 存放,之后使用 @PropertySource("classpath:shop.properties") 加载进我们的商铺配置类,使用 @PropertySource("classpath:user.properties") 加载进我们的用户配置类即可

注意:

  • 从自己项目中找 properties 使用 classpath:后面接 application.properties 到要寻找的 properties 的路径

  • 从第三方项目中找 properties 使用 classpath*:后面接要寻找的 properties 的路径

③、@Profile

@Profile 是 Spring 的 “环境开关”只有当前激活的 Profile 与注解值匹配时,相应的 Bean / 配置类才会被注册到容器;不匹配就整体跳过,实现“一套代码,多环境差异化部署”。

举例:

  @Profile("test")      // 只有激活 test 环境才可与进行以下配置的实现@Configurationpublic class DataSourceConfig {​@Bean@Profile("dev")                    // 仅 dev 环境生效public DataSource devDs() {return DataSourceBuilder.create().url("jdbc:h2:mem:dev").build();}​@Bean@Profile("prod")                   // 仅 prod 环境生效public DataSource prodDs() {return DataSourceBuilder.create().url("jdbc:mysql://prod/db").username("${db.user}").password("${db.pwd}").build();}@Bean@Profile("build")                    // 仅 build 环境生效public DataSource devDs() {return DataSourceBuilder.create().url("jdbc:h2:mem:build").build();}}

如上数据源有三个,@Profile 中配置的环境是自定义的(如 test、dev、prod等),默认是 default

  @Componentpublic class DeliveryDao {@Autowiredprivate MyDataSource myDataSource;​public void saveDelivery() {System.out.println("数据源:保存数据" + myDataSource);}}

当我们在其他组件内使用 @Autowired 注入数据源时,由于数据源有多个,则自动注入不知道选择哪个,默认选择带有 default 标识的,没有则报错

此时我们可以在 @Profile("") 内再添加一个 default 标识,如@Profile("build","default"),则会默认使用 build 环境的数据源,当然可以在 application.properties 中激活数据源环境,使用spring.profiles.active=你要激活的环境

3、生命周期

大多数 Java 组件(Servlet、Spring Bean、EJB 等)都遵循类似的四个主要生命周期阶段:

  1. 创建(Instantiation)

  2. 初始化(Initialization)

  3. 运行(In Service/Runtime)

  4. 销毁(Destruction)

3.1、创建阶段
  • 实例化对象:通过构造函数或工厂方法创建组件实例

  • 依赖注入:为对象的属性/字段注入依赖项(在IoC容器中)

3.2、初始化阶段
  • Aware接口回调(Spring特有):

    • BeanNameAware.setBeanName()

    • BeanFactoryAware.setBeanFactory()

    • ApplicationContextAware.setApplicationContext()

  • 初始化前处理

    • Spring: BeanPostProcessor.postProcessBeforeInitialization()

  • 初始化操作

    • 调用 @PostConstruct 注解的方法

    • 实现 InitializingBean 接口的 afterPropertiesSet() 方法(Spring)

    • 调用自定义的 init-method(XML配置或@Bean(initMethod="...")

  • 初始化后处理

    • Spring: BeanPostProcessor.postProcessAfterInitialization()

3.3、运行阶段
  • 组件处于就绪状态,处理业务请求

  • 对于有状态组件可能涉及状态转换:

    • EJB 的激活/钝化(@PostActivate, @PrePassivate

    • Web 应用的会话管理

3.4、销毁阶段
  • 销毁前处理

    • 调用 @PreDestroy 注解的方法

    • 实现 DisposableBean 接口的 destroy() 方法(Spring)

    • 调用自定义的 destroy-method(XML配置或@Bean(destroyMethod="...")

  • 资源释放

    • 关闭数据库连接

    • 释放文件句柄

    • 清理缓存等

以下给出一个流程图:

http://www.dtcms.com/a/470273.html

相关文章:

  • Android GPS定位与行车轨迹追踪完整实战
  • [持续更新] HPC高性能计算CUDA/C++面试知识点
  • 【有源码】基于Hadoop生态的大数据共享单车数据分析与可视化平台-基于Python与大数据的共享单车多维度数据分析可视化系统
  • 上海做网站推荐做景观要用的植物网站
  • 珠海 网站建设和推广万网网站空间
  • Jasperreport 导出word 多个element重叠部分导致不显示(不支持)
  • GRU(门控循环单元) 笔记
  • 莱州网站建设哪家好做网站要会哪些知识
  • ubuntu离线安装 xl2tpd
  • 如何在百度上做网站最好用的免费建站
  • 关联网站有那些wordpress超级排版器插件
  • 熊猫比分 APP:开启体育赛事观赛新“姿势”
  • 第二章:模块的编译与运行-9 Platform Dependency
  • java多模块概念
  • 小企业网站维护什么东西互联网培训
  • 找人做网站做的很烂网站自助建设推广
  • uhttpd HTTPS 在嵌入式与 OpenWrt 上的实战部署与排查
  • 合肥网站建设正规公司抖音如何推广引流
  • [cpprestsdk] 构建HTTP消息 | http_headers.h
  • SCI论文写作:从实验设计到发表(选题、文献调研、实验设计、数据分析、论文结构及语言规范)
  • 西安哪里有做网站的网页界面ps制作步骤
  • 《彻底理解C语言指针全攻略(2)》
  • JavaScript 性能优化实战:从原理到落地
  • 网上公司注册申请的流程江西短视频搜索seo推荐
  • 网站建设哪家好知道数字化档案馆及网站的建设
  • 汽车行业密钥灌装解决方案:构建可信的车载安全启动与通信体系
  • Vue2+Django TodoList项目跨域解决方案实战
  • 网页结构解析入门:HTML、CSS、JS 与爬虫的关系
  • Mac查看本机发出请求的IP地址
  • 《基于 YOLOv11 的武器装备视觉检测系统构建与专 利申请指南》