Spring核心 - 控制反转 IOC , 用来大量例子来解释
上节我们通过一个简单示例讲解了 Spring 的使用和控制反转,重点就记住一个创建实例不用手动 new 对象,而是通过 xml 或者 java 配置类 或者 注解配置创建实例,然后getBean 获取实例,体现了依赖倒置原则,对象之间解耦。
这节继续讨论 spring 注册bean的那些事。
- Spring框架管理这些Bean的创建工作,即由用户管理Bean转变为框架管理Bean,这个就叫控制反转 - Inversion of Control (IoC)
- Spring 框架托管创建的Bean放在哪里呢? 这便是IoC Container;
- pring 框架为了更好让用户配置Bean,必然会引入不同方式来配置Bean? 这便是xml配置,Java配置,注解配置等支持
- Spring 框架既然接管了Bean的生成,必然需要管理整个Bean的生命周期等;
- 应用程序代码从Ioc Container中获取依赖的Bean,注入到应用程序中,这个过程叫 依赖注入(Dependency Injection,DI) ; 所以说控制反转是通过依赖注入实现的,其实它们是同一个概念的不同角度描述。通俗来说就是IoC是设计思想,DI是实现方式
- 在依赖注入时,有哪些方式呢?这就是构造器方式,@Autowired, @Resource, @Qualifier… 同时Bean之间存在依赖(可能存在先后顺序问题,以及循环依赖问题等)
理解IOC
先理解 spring Bean , 这个大家都不陌生,@Bean 注解用的不少,Bean 的概念在 spring 中就是一个对象实例,跟手动 new 出来的对象差不多,区别在于被 spring 容器管理的对象才能叫做 Bean. 手动new 的仅仅只是一个实例,当你给方法打上 @Bean 注解就代表方法的返回对象会交给 Spring 容器创建,管理,销毁。
再来理解 IOC 控制反转,控制反转是一种设计思想,并不是一种技术。什么是思想,比如类的单一职责就是一种思想,一种指导方法,这个方法存在于你的脑海里,实现靠你手写代码,并不是某一种技术帮你实现了单一职责。什么是技术?那就是具体可用的工具,IOC是一种设计思想,那么spring对这种思想的具体实现就是依赖注入(DI)。 再换一个大家都熟悉的例子举例思想和技术, 面向对象编程(OOP) 这是一种设计思想,实现这个思想的技术是 Java 的类和接口。
刚开始我会把思想、协议、技术混淆,这里做一个生活的类比。
- 思想 = 哲学(告诉你该怎么思考和设计)
- 协议 = 法律(规定参与方必须遵守的规则)
- 技术 = 工具(拿来解决问题的手段)
继续 IOC 的学习,控制反转这种思想是什么意思呢?
Ioc 意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。
深入分析搞清楚两个问题:
- 谁控制谁,控制了什么?
- 为何是反转,哪些方面反转了?
谁控制谁,控制了什么?
传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对 象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么? IoC 容器控制了对象及其依赖的整个生命周期:创建、依赖注入、初始化、销毁,以及资源的统一管理,比如配置文件application.yml 、 数据库连接池、消息队列连接、文件资源等在 spring 中都交给了 IOC 容器管理。
举例看看
当我们在 spring 框架的配置文件中写的配置信息,我们是可以注入到自己的 java 代码中,要知道配置文件不属于 java 语言的一部分,为什么能注入到java语言的变量中呢?这就是 IOC 容器干的事。
app:name: MyAppversion: 1.0
你可以用 @Value
或者 @ConfigurationProperties
注入 :
@Component
@ConfigurationProperties(prefix = "app")
public class AppConfig {private String name;private String version;// getter/setter
}
你在配置文件中写的配置,spring 会帮你加载到 IOC 容器中,然后通过 @ConfigurationProperties 注入给 java 的变量。
在配置文件中写的数据库连接
spring:rabbitmq:host: localhostport: 5672username: guestpassword: guest
同样交给容器管理, Spring 容器会自动帮你创建一个 DataSource Bean(通常是 HikariCP 连接池) ,底层的 spring 帮我们做了不少事。数据库连接池你只需要注入即可:
@Service
public class UserService {@Autowiredprivate DataSource dataSource; // 连接池 Bean 由 IoC 容器管理
}
不过鱼总经常带我们使用 spring + mybatis plus 开发,对于 DataSource Bean 我们是没有感知的, MyBatis-Plus 本身不提供连接池,它只需要一个 DataSource 来获取连接, 在 Spring Boot 环境下 ,MybatisAutoConfiguration
会自动读取 Spring 容器里的 DataSource Bean ,所以我们不需要上面这一段注入连接池的配置, MyBatis-Plus 自动帮你把 DataSource 用起来了 。
IOC 谁控制了谁,控制了什么资源这个问题大概讲清楚了,交给IOC控制省了我们不少事。
IOC 容器下一个要搞明白的问题: 为何是反转,哪些方面反转了?
为何是反转,哪些方面反转了?
有反转就有正转,传统应用程序是由我们自己在对象中主动控制(主动 new)去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。
传统java程序中,我们要用到另一个程序的方法,那就需要 new 一个实例对象,也就是当前对象依赖了另一个对象,这个依赖对象是当前对象自己管理的。而spring 程序是不用对象自己管理依赖对象,把控制权交给容器,对象自己不创建依赖对象,被迫接收从容器注入来的依赖对象,这就是反转。
用图说明,我们的客户端 App 类,想要调用 UserService 类 和 UserDao 类 , 需要自己 new 对象。
当有了IoC/DI的容器后,在客户端类中不再主动去创建这些对象了,如图
可以看到 IOC 容器主动创建了两个依赖对象(Bean) , 客户端就不用 new 对象了,从容器中获取依赖对象。
反转的问题也讲明白了。
怎么样对于IOC这个设计思想是不是理解了不少,感受到 IOC 容器带来解耦的爽点了吧,加上 spring 的配置文件,把数据库连接,消息队列,redis 各种 bean 都解耦了,我们只需要写个配置文件,IOC 容器就帮我们创建好对象,然后我们使用注解自动注入一个 Bean , 就能用 redis 了,太方便了。 我们都试过在某个类中自动注入很多Bean,如果全用 new 来创建对象,对象关系耦合度是不是很高,很难管理。
理解了 IOC 思想,继续探讨 IOC 思想发挥了什么作用。
IOC 能做什么?
其实IoC对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在IoC/DI思想中,应用程序就变成被动的了,被动的等待IoC容器来创建并注入它所需要的资源了。
IoC很好的体现了面向对象设计法则之一—— 好莱坞法则:“别找我们,我们找你”;即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。
IoC和DI是什么关系
控制反转是通过依赖注入实现的,其实它们是同一个概念的不同角度描述。文章前面也提到了 IoC是设计思想,DI是实现方式。
先来探索一下 DI 。先说明一下,在这里的 spring 容器的语境中, 组件就是容器管理的对象 ,可以是 @Component @Service @Controller 标记的类,甚至容器管理的 资源类、工具类、连接池等, 强调它在系统里的角色和功能,而不限定是具体哪种类。
DI—Dependency Injection,即依赖注入:组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。
我们来深入分析一下:
- 谁依赖于谁?
当然是应用程序依赖于IoC容器;
- 为什么需要依赖?
应用程序需要IoC容器来提供对象需要的外部资源;
- 谁注入谁?
很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象;
- 注入了什么?
就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。
- IoC和DI有什么关系呢?
其实它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以2004年大师级人物Martin Fowler又给出了一个新的名字:“依赖注入”,相对IoC 而言,“依赖注入”明确描述了“被注入对象 依赖 IoC容器 配置 依赖对象”。通俗来说就是IoC是设计思想,DI是实现方式。
回到实践的角度,帮助大家理解 IOC 与依赖注入,其实大家都往 IOC 容器配置过Bean, 也从使用过依赖注入,接下来就是归纳一下用法。
Ioc 配置的三种方式
IOC 三种配置方式:
- xml 配置
- 注解
- Java 配置
上一篇文章讲述了三种配置方式了,这里不过再次归纳而已,总体上目前的主流方式是 注解 + Java 配置。我们使用spring boot 开发经常用的注解 @Component 就是注解方式给 IOC 容器配置/注册Bean。 @Bean 是 java 配置类 方式。
1、XML 配置
顾名思义,就是将bean的信息配置.xml文件里,通过Spring加载文件为我们创建bean。这种方式出现很多早前的SSM项目中,将第三方类库或者一些配置工具类都以这种方式进行配置,主要原因是由于第三方类不支持Spring注解。
- 优点: 可以使用于任何场景,结构清晰,通俗易懂
- 缺点: 配置繁琐,不易维护,枯燥无味,扩展性差
举例:
- 配置xx.xml文件
- 声明命名空间和配置bean
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!-- services --><bean id="userService" class="com.fency.springframework.service.UserServiceImpl"><property name="userDao" ref="userDao"/><!-- additional collaborators and configuration for this bean go here --></bean><!-- more bean definitions for services go here -->
</beans>
2、注解配置
通过在类上加注解的方式,来声明一个类交给Spring管理,Spring会自动扫描带有@Component,@Controller,@Service,@Repository这四个注解的类,然后帮我们创建并管理,前提是需要先配置Spring的注解扫描器。
- 优点:开发便捷,通俗易懂,方便维护。
- 缺点:具有局限性,对于一些第三方资源,无法添加注解。只能采用XML或JavaConfig的方式配置
举例:
- 对类添加@Component相关的注解,比如@Controller,@Service,@Repository
- 设置ComponentScan的basePackage, 比如
<context:component-scan base-package='com.fency.springframework'>
, 或者@ComponentScan("com.fency.springframework")
注解,或者new AnnotationConfigApplicationContext("com.fency.springframework")
指定扫描的basePackage.
@Service
public class UserServiceImpl {/*** user dao impl.*/@Autowiredprivate UserDaoImpl userDao;/*** find user list.** @return user list*/public List<User> findUserList() {return userDao.findUserList();}}
3、Java 配置
将类的创建交给我们配置的JavcConfig类来完成,Spring只负责维护和管理,采用纯Java创建方式。其本质上就是把在XML上的配置声明转移到Java配置类中
- 优点:适用于任何场景,配置方便,因为是纯Java代码,扩展性高,十分灵活
- 缺点:由于是采用Java类的方式,声明不明显,如果大量配置,可读性比较差
举例:
- 创建一个配置类, 添加@Configuration注解声明为配置类
- 创建方法,方法上加上@bean,该方法用于创建实例并返回,该实例创建后会交给spring管理,方法名建议与实例名相同(首字母小写)。注:实例类不需要加任何注解
@Configuration
public class BeansConfig {/*** @return user dao*/@Bean("userDao")public UserDaoImpl userDao() {return new UserDaoImpl();}/*** @return user service*/@Bean("userService")public UserServiceImpl userService() {UserServiceImpl userService = new UserServiceImpl();userService.setUserDao(userDao());return userService;}
}
依赖注入的三种方式
用的注入方式主要有三种:构造方法注入(Construct注入),setter注入,基于注解的注入(接口注入)
setter方式
- 在XML配置方式中,property都是setter方式注入,比如下面的xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!-- services --><bean id="userService" class="com.fency.springframework.service.UserServiceImpl"><property name="userDao" ref="userDao"/><!-- additional collaborators and configuration for this bean go here --></bean><!-- more bean definitions for services go here -->
</beans>
本质上包含两步:
- 第一步,需要new UserServiceImpl()创建对象, 所以需要默认构造函数
- 第二步,调用setUserDao()函数注入userDao的值, 所以需要setUserDao()函数
所以对应的service类是这样的:
public class UserServiceImpl {/*** user dao impl.*/private UserDaoImpl userDao;/*** init.*/public UserServiceImpl() {}/*** find user list.** @return user list*/public List<User> findUserList() {return this.userDao.findUserList();}/*** set dao.** @param userDao user dao*/public void setUserDao(UserDaoImpl userDao) {this.userDao = userDao;}
}
- 在注解和Java配置方式下
public class UserServiceImpl {/*** user dao impl.*/private UserDaoImpl userDao;/*** find user list.** @return user list*/public List<User> findUserList() {return this.userDao.findUserList();}/*** set dao.** @param userDao user dao*/@Autowiredpublic void setUserDao(UserDaoImpl userDao) {this.userDao = userDao;}
}
在Spring3.x刚推出的时候,推荐使用注入的就是这种, 但是这种方式比较麻烦,所以在Spring4.x版本中推荐构造函数注入。
构造函数
- 在XML配置方式中,
<constructor-arg>
是通过构造函数参数注入,比如下面的xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!-- services --><bean id="userService" class="com.fency.springframework.service.UserServiceImpl"><constructor-arg name="userDao" ref="userDao"/><!-- additional collaborators and configuration for this bean go here --></bean><!-- more bean definitions for services go here -->
</beans>
本质上是new UserServiceImpl(userDao)创建对象, 所以对应的service类是这样的:
public class UserServiceImpl {/*** user dao impl.*/private final UserDaoImpl userDao;/*** init.* @param userDaoImpl user dao impl*/public UserServiceImpl(UserDaoImpl userDaoImpl) {this.userDao = userDaoImpl;}/*** find user list.** @return user list*/public List<User> findUserList() {return this.userDao.findUserList();}}
- 在注解和Java配置方式下
@Service
public class UserServiceImpl {/*** user dao impl.*/private final UserDaoImpl userDao;/*** init.* @param userDaoImpl user dao impl*/@Autowired // 这里@Autowired也可以省略public UserServiceImpl(final UserDaoImpl userDaoImpl) {this.userDao = userDaoImpl;}/*** find user list.** @return user list*/public List<User> findUserList() {return this.userDao.findUserList();}}
在Spring4.x版本中推荐的注入方式就是这种.
注解注入
也叫做字段注入,filed 注入
以@Autowired(自动注入)注解注入为例,修饰符有三个属性:Constructor,byType,byName。默认按照byType注入。
- constructor:通过构造方法进行自动注入,spring会匹配与构造方法参数类型一致的bean进行注入,如果有一个多参数的构造方法,一个只有一个参数的构造方法,在容器中查找到多个匹配多参数构造方法的bean,那么spring会优先将bean注入到多参数的构造方法中。
- byName:被注入bean的id名必须与set方法后半截匹配,并且id名称的第一个单词首字母必须小写,这一点与手动set注入有点不同。
- byType:查找所有的set方法,将符合符合参数类型的bean注入。
比如:
@Service
public class UserServiceImpl {/*** user dao impl.*/@Autowiredprivate UserDaoImpl userDao;/*** find user list.** @return user list*/public List<User> findUserList() {return userDao.findUserList();}}
注解注入方式并不是 spring boot 出现才有的哦~ spring 本身支持注解注入。
这三种依赖注入方式,spring 官方最推荐的是构造器注入,实践中跟着鱼总开发用的最多的是注解注入,构造器注入有见过几次,这就是典型的 “理论 VS 实践”差异。注解注入太方便了!
不过还是要讲一讲为什么官方推荐构造器注入。
为什么推荐构造器注入方式?
先来看看Spring在文档里怎么说:
The Spring team generally advocates constructor injection as it enables one to implement application components as immutable objects and to ensure that required dependencies are not null. Furthermore constructor-injected components are always returned to client (calling) code in a fully initialized state.
简单的翻译一下:这个构造器注入的方式能够保证注入的组件不可变,并且确保需要的依赖不为空。此外,构造器注入的依赖总是能够在返回客户端(组件)代码的时候保证完全初始化的状态。
下面来简单的解释一下:
- 依赖不可变:其实说的就是final关键字。
- 依赖不为空(省去了我们对其检查):当要实例化UserServiceImpl的时候,由于自己实现了有参数的构造函数,所以不会调用默认构造函数,那么就需要Spring容器传入所需要的参数,所以就两种情况:1、有该类型的参数->传入,OK 。2:无该类型的参数->报错。
- 完全初始化的状态:这个可以跟上面的依赖不为空结合起来,向构造器传参之前,要确保注入的内容不为空,那么肯定要调用依赖组件的构造方法完成实例化。而在Java类加载实例化的过程中,构造方法是最后一步(之前如果有父类先初始化父类,然后自己的成员变量,最后才是构造方法),所以返回来的都是初始化之后的状态。
所以通常是这样的
@Service
public class UserServiceImpl {/*** user dao impl.*/private final UserDaoImpl userDao;/*** init.* @param userDaoImpl user dao impl*/public UserServiceImpl(final UserDaoImpl userDaoImpl) {this.userDao = userDaoImpl;}}
如果使用setter注入,缺点显而易见,对于IOC容器以外的环境,除了使用反射来提供它需要的依赖之外,无法复用该实现类。而且将一直是个潜在的隐患,因为你不调用将一直无法发现NPE的存在。
那我们通过例子来找出这个 NPE:
@Service
public class UserService {private UserRepository userRepository;@Autowiredpublic void setUserRepository(UserRepository userRepository) {this.userRepository = userRepository;}public void doSomething() {userRepository.saveUser();}
}
容器会在 对象创建后 调用 setUserRepository()
方法,把依赖注入进来。也就是说:对象本身一开始是 半成品,需要调用 setter 才完整。
如果一个普通的java程序里调用了 doSomething() , 可不会等待容器初始化,类加载直接开始跑。所以@Autowired 还没注入对象呢,就会 NPE
UserService userService = new UserService();
userService.doSomething(); // 会报错!userRepository 是 NPE
那么我们用最多的**注解注入(field 字段注入)**有啥问题?
循环依赖,都遇到过哈~
即A里面注入B,B里面又注入A:
public class A {@Autowiredprivate B b;
}public class B {@Autowiredprivate A a;
}
@Autowired和@Resource以及@Inject等注解注入有何区别?
经典面试题
首先先来系统学习一下三个注解,了解了这三个注解,区别自然浮现于水面。
@Autowired
- Autowired注解源码
在Spring 2.5 引入了 @Autowired 注解
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {boolean required() default true;
}
从Autowired注解源码上看,可以使用在下面这些地方:
@Target(ElementType.CONSTRUCTOR) #构造函数
@Target(ElementType.METHOD) #方法
@Target(ElementType.PARAMETER) #方法参数
@Target(ElementType.FIELD) #字段、枚举的常量
@Target(ElementType.ANNOTATION_TYPE) #注解
还有一个value属性,默认是true。
- 简单总结:
1、@Autowired是Spring自带的注解,通过AutowiredAnnotationBeanPostProcessor 类实现的依赖注入
2、@Autowired可以作用在CONSTRUCTOR、METHOD、PARAMETER、FIELD、ANNOTATION_TYPE
3、@Autowired默认是根据类型(byType )进行自动装配的
4、如果有多个类型一样的Bean候选者,需要指定按照名称(byName )进行装配,则需要配合@Qualifier。
指定名称后,如果Spring IOC容器中没有对应的组件bean抛出NoSuchBeanDefinitionException。也可以将@Autowired中required配置为false,如果配置为false之后,当没有找到相应bean的时候,系统不会抛异常
- 简单使用代码:
在字段属性上。
@Autowired
private HelloDao helloDao;
或者
private HelloDao helloDao;
public HelloDao getHelloDao() {return helloDao;
}
@Autowired
public void setHelloDao(HelloDao helloDao) {this.helloDao = helloDao;
}
或者
private HelloDao helloDao;
//@Autowired
public HelloServiceImpl(@Autowired HelloDao helloDao) {this.helloDao = helloDao;
}
// 构造器注入也可不写@Autowired,也可以注入成功。
将@Autowired写在被注入的成员变量上,setter或者构造器上,就不用再xml文件中配置了。
如果有多个类型一样的Bean候选者,则默认根据设定的属性名称进行获取。如 HelloDao 在Spring中有 helloWorldDao 和 helloDao 两个Bean候选者。
@Autowired
private HelloDao helloDao;
首先根据类型获取,发现多个HelloDao,然后根据helloDao进行获取,如果要获取限定的其中一个候选者,结合@Qualifier进行注入。
@Autowired
@Qualifier("helloWorldDao")
private HelloDao helloDao;
注入名称为helloWorldDao 的Bean组件。@Qualifier(“XXX”) 中的 XX是 Bean 的名称,所以 @Autowired 和 @Qualifier 结合使用时,自动注入的策略就从 byType 转变成 byName 了。
注意:使用@Qualifier 时候,如何设置的指定名称的Bean不存在,则会抛出异常,如果防止抛出异常,可以使用:
@Qualifier("xxxxyyyy")
@Autowired(required = false)
private HelloDao helloDao;
在SpringBoot中也可以使用@Bean+@Autowired进行组件注入,将@Autowired加到参数上,其实也可以省略。
@Bean
public Person getPerson(@Autowired Car car){ // @Autowired 其实也可以省略return new Person();
}
@Resource
- Resource注解源码
@Target({TYPE, FIELD, METHOD})
@Retention(RUNTIME)
public @interface Resource {String name() default "";// 其他省略
}
从Resource注解源码上看,可以使用在下面这些地方:
@Target(ElementType.TYPE) #接口、类、枚举、注解
@Target(ElementType.FIELD) #字段、枚举的常量
@Target(ElementType.METHOD) #方法
name 指定注入指定名称的组件。
- 简单总结:
1、@Resource是JSR250规范的实现,在javax.annotation包下
2、@Resource可以作用TYPE、FIELD、METHOD上
3、@Resource是默认根据属性名称进行自动装配的,如果有多个类型一样的Bean候选者,则可以通过name进行指定进行注入
- 简单使用代码:
@Component
public class SuperMan {@Resourceprivate Car car;
}
按照属性名称 car 注入容器中的组件。如果容器中BMW还有BYD两种类型组件。指定加入BMW。如下代码:
@Component
public class SuperMan {@Resource(name = "BMW")private Car car;
}
name 的作用类似 @Qualifier
@Inject
- Inject注解源码
@Target({ METHOD, CONSTRUCTOR, FIELD })
@Retention(RUNTIME)
@Documented
public @interface Inject {}
从Inject注解源码上看,可以使用在下面这些地方:
@Target(ElementType.CONSTRUCTOR) #构造函数
@Target(ElementType.METHOD) #方法
@Target(ElementType.FIELD) #字段、枚举的常量
- 简单总结:
1、@Inject是JSR330 (Dependency Injection for Java)中的规范,需要导入javax.inject.Inject jar包 ,才能实现注入
2、@Inject可以作用CONSTRUCTOR、METHOD、FIELD上
3、@Inject是根据类型进行自动装配的,如果需要按名称进行装配,则需要配合@Named;
- 简单使用代码:
@Inject
private Car car;
指定加入BMW组件。
@Inject
@Named("BMW")
private Car car;
@Named 的作用类似 @Qualifier!
有没有看出重点?
JSR 标准不理解对吧
JSR 是 Java Specification Request(Java 规范提案),它是 Java 社区制定的标准规范,描述如何实现某种功能或接口。
这就是重点区别,@Autowired
→ Spring 专有,只能在 Spring 容器下用 → 没有 JSR 标准, 换容器就可能不兼容
@Resource 是 JSR-250 规范的一部分 → 意味着它是 Java 官方推荐的标准方式,任何支持 JSR-250 的容器都能用。
总结@Resource 注解符号java规范,兼容性更强,使用方式也类似于 @Autowired , 这就是为啥鱼总最推荐使用此注解。
参考文章:https://pdai.tech/md/spring/spring-x-framework-ioc.html