共计 9023 个字符,预计需要花费 23 分钟才能阅读完成。
有幻想,有情怀,有温度,干货满满;关注一下我吧。
一、注解驱动 IoC
xml 驱动 的 IoC 容器应用的是 ClassPathXmlApplicationContext
读取 xml 内 bean 信息
注解驱动 的 IoC 容器应用的是 AnnotationConfigApplicationContext
读取 Java 类中的 bean 信息
1. AnnotationConfigApplicationContext 的注册应用
相比于 xml 文件作为驱动, 注解驱动须要指明 配置类 一个配置类能够了解为 ” 相当于 ” 一个 xml 配置类只须要在类上标注注解 @Configuration
@Configuration | |
public class DemoConfiguration {} |
在 xml 中申明 bean 的形式
<bean id="person" class="com.huodd.bean.Person"></bean>
在配置类中应用的是 @Bean
注解
@Bean | |
public Person person() {return new Person(); | |
} |
阐明: 向 IoC 容器注册一个类型为 Persion,id 为 Person 的 Bean
办法名示意的是 bean 的 id 返回值示意的是注册的 bean 的类型
@Bean
注解也能够显示的申明 bean 的 id 如 @Bean("person1")
2. 注解 IoC 容器的初始化
public class AnnotationConfigApplication {public static void main(String[] args) {ApplicationContext ctx = new AnnotationConfigApplicationContext(DemoConfiguration.class); | |
Person person = ctx.getBean(Person.class); | |
System.out.println(person); | |
} | |
} |
运行后 Person 控制台打印后果
com.huodd.bean.Person@55536d9e
3. 组件的注册和扫描
上述初始化时 咱们在应用 AnnotationConfigApplicationContext
时传递了参数 Class<?>... componentClasses
翻看 AnnotationConfigApplicationContext
的构造方法能够发现还能够传递参数的参数类型还有 String... basePackages
这里就波及到 组件的注册和扫描
这里能够思考一个问题, 如果咱们要注册的组件特地多, 那进行编写这些
@Bean
的时候代码工作量也会特地多, 这时候该如何解决呢?
Spring 给咱们提供了几个注解, 能够帮忙咱们疾速注册须要的组件, 这些注解被称为 模式注解(stereotype annotations)
@Component
@Component 能够说是所有组件注册的本源 在类上标注 @Component
代表该类被注册到 IoC 容器中作为一个 Bean
@Component | |
public class Person {} |
如果未指定 Bean 的名称 默认规定是 “类名称首字母小写” 下面的 bean 名称默认会是 person
如果要自定义 bean 的名称 能够在 @Component
申明 value 的值即可 如
@Component("person1") | |
public class Person {} |
在 xml 中相当于
<bean id="person1" class="com.huodd.bean.Person"/>
@ComponentScan
这个时候 如果咱们间接运行启动类 获取 Person
的 bean 对象, 会报错NoSuchBeanDefinitionException
这是为什么呢?
因为咱们只是申明了组件, 而后间接启动了 IoC 容器, 这样容器是感知不到有 @Component
存在的,
解决方案 1:
咱们须要在写配置类时再额定标注一个新的注解@ComponentScan
目标是通知 IoC 容器 我要扫描哪个包上面的带有 @Component
注解的类
@Configuration | |
@ComponentScan("com.huodd.bean") | |
public class DemoComponentScanConfiguration {} |
注: 如果不指定扫描门路, 则 默认扫描本类所在包及所有子包 下带有 @Component
的组件
启动类代码如下:
public class AnnotationConfigApplication {public static void main(String[] args) {ApplicationContext ctx = new AnnotationConfigApplicationContext(DemoComponentScanConfiguration.class); | |
Person person = ctx.getBean(Person.class); | |
System.out.println(person); | |
} | |
} |
解决方案 2:
这里也能够不写 @ComponentScan
而间接在AnnotationConfigApplicationContext
办法参数内传入 String 类型的 包扫描门路 代码如下
public class AnnotationConfigApplication {public static void main(String[] args) {ApplicationContext ctx = new AnnotationConfigApplicationContext("com.huodd.bean"); | |
Person person = ctx.getBean(Person.class); | |
System.out.println(person); | |
} | |
} |
PS: 组件扫描并非是注解驱动 IoC 所特有的, 其实在 xml 驱动的 IoC 模式下 同样能够启用组件扫描, 只须要在 xml 中申明一个标签即可
<context:component-scan base-package="com.huodd.bean"/>
这里须要留神下: 如须要扫描多个门路, 须要写多个标签 也就是 一个标签只能申明一个根包
组件注册的补充
SpringFramework 提供了在进行 Web 开发三层架构时的扩大注解: 别离为 @Controller
、@Service
、@Repository
小伙伴有没有很相熟?
别离代表 体现层 、 业务层 、 长久层 这三个注解的作用与 @Component
齐全一样 扒开源码咱们能够看到 底层在这三个注解类上又增加了 @Component
@Target({ElementType.TYPE}) | |
@Retention(RetentionPolicy.RUNTIME) | |
@Documented | |
@Component | |
public @interface Service {} |
这样 咱们在进行合乎三层架构的开发时 对于相应的如 ServiceImpl 等 就能够间接标注 @Service
等注解了
@Configuration
@Configuration
底层也有标注@Component
@Target({ElementType.TYPE}) | |
@Retention(RetentionPolicy.RUNTIME) | |
@Documented | |
@Component | |
public @interface Configuration {...} |
由此能够阐明, 配置类不是向咱们所想的那样,只是单纯的做一个配置而已, 它也会被视为 bean,也被注册到 IoC 容器外面
4. 注解驱动与 xml 驱动相互援用
4.1 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" | |
xmlns:context="http://www.springframework.org/schema/context" | |
xsi:schemaLocation="http://www.springframework.org/schema/beans | |
https://www.springframework.org/schema/beans/spring-beans.xsd | |
http://www.springframework.org/schema/context | |
https://www.springframework.org/schema/context/spring-context.xsd"> | |
<!-- 开启注解配置 --> | |
<context:annotation-config /> | |
<!-- 注册配置类 --> | |
<bean class="com.huodd.config.AnnotationConfigConfiguration"/> | |
</beans> |
4.2 注解援用 XMl
需在配置类上标注 @ImportResource
并申明配置文件的门路
@Configuration | |
@ImportResource("classpath:annotation/demo-beans.xml") | |
public class ImportXmlAnnotationConfiguration {} |
二、IoC 的依赖注入
1.Setter 属性注入
创建对象 将属性值 set 进去 之后返回对象
@Bean | |
public Person person() {Person person = new Person(); | |
person.setId(1); | |
person.setName("PoXing"); | |
person.setAge(18); | |
return person; | |
} |
xml 中的 setter 注入
<bean id="person" class="com.huodd.bean.Person"> | |
<property name="id" value="1"/> | |
<property name="name" value="PoXing"/> | |
<property name="age" value="18"/> | |
</bean> |
2. 结构器注入
应用结构器注入, 须要在 bean 自身增加有参构造方法, 如在 Person 中增加有参构造方法如下
public Person(Integer id, String name, Integer age) { | |
this.id = id; | |
this.name = name; | |
this.age = age; | |
} |
注解驱动 中, 咱们创立 bean 的时候注入属性时 就须要同时指定参数值
@Bean | |
public Person person() {return new Person(1, "PoXing", 18); | |
} |
xml 驱动 中如下
<bean id="person" class="com.huodd.bean.Person"> | |
<!-- | |
index:示意结构器的参数索引 | |
value:示意对应的参数值 | |
--> | |
<constructor-arg index="0" value="1"/> | |
<constructor-arg index="1" value="PoXing"/> | |
<constructor-arg index="2" value="18"/> | |
</bean> |
3. 注解式属性注入
这里先阐明一下, 为何会有注解式属性值注入. 仔细的小伙伴可能会发现 下面咱们谈到的 Setter 属性注入 、 结构器注入 如同在只能是在应用 @Bean
注解的时候时候应用, 然而 如果是通过标注 @Component
注解的组件呢 (像后面咱们的 Person 类中标注了@Component
注解), 怎么给它设定属性值, 该节次要就是说一下这部分
@Component 下的属性注入
这里咱们应用 Dog 类做为演示(这里我悄悄的增加了 @Component
注解 本人尝试的小伙伴要留神哦 否则会报错的)
@Component | |
public class Dog { | |
private Integer id; | |
private String name; | |
private Integer age; | |
... 省略 Getter、Setter | |
... 省略 toString | |
} |
这里要实现注解式属性注入, 能够间接在要注入的字段上标注 @Value
注解 如
@Value("1") | |
private Integer id; | |
@Value("wangcai") | |
private String name; | |
@Value("3") | |
private Integer age; |
启动类代码如下
public class DiApplication {public static void main(String[] args) {ApplicationContext ctx = new AnnotationConfigApplicationContext("com.huodd.bean"); | |
Dog dog = ctx.getBean(Dog.class); | |
System.out.println(dog); | |
} | |
} |
控制台打印后果
Dog{id=1, name='wangcai', age=3}
内部配置文件(@PropertySource)
这里次要是解决下面的 @Value 中注入 咱们把属性值间接固定写死了, 如果要批改 还要去 Java 代码中去批改, 很不合乎开发标准,
SpringFramework 为咱们扩大了新的注解@PropertySource
次要用来导入内部配置文件
- 这里咱们创立一个
dog.properties
dog.id=1 | |
dog.name=wangcai | |
dog.age=3 |
- 引入配置文件
@PropertySource("classpath:di/dog.properties") | |
@ComponentScan("com.huodd.bean") | |
@Configuration | |
public class DemoComponentScanConfiguration {} |
- Dog 类中属性注入 这里
@Value
须要配合 占位符 来获取 properties 配置文件中的内容
@Value("${dog.id}") | |
private Integer id; | |
@Value("${dog.name}") | |
private String name; | |
@Value("${dog.age}") | |
private Integer age; |
- 批改一下启动类
public class DiApplication {public static void main(String[] args) {ApplicationContext ctx = new AnnotationConfigApplicationContext(DemoComponentScanConfiguration.class); | |
Dog dog = ctx.getBean(Dog.class); | |
System.out.println(dog); | |
} | |
} |
控制台打印后果如下
Dog{id=1, name='wangcai', age=3}
此时配置文件的属性曾经注入胜利
4. 主动注入
在 xml 模式中有 ref
属性 能够将一个 bean 注入到另外一个 bean 中, 注解模式中也同样能够
@Autowired
给 Dog 的 bean 中注入 Person 的 Bean (即 给 dog 指定它的客人)
办法 1 → 在属性上标注
@Component | |
public class Dog { | |
// ...... | |
@Autowired | |
private Person person; | |
} |
办法 2 → 应用结构器注入形式
@Component | |
public class Dog { | |
// ...... | |
private Person person; | |
@Autowired | |
public Dog(Person person) {this.person = person;} | |
} |
办法 3 → 应用 setter 办法注入
@Component | |
public class Dog { | |
// ...... | |
private Person person; | |
@Autowired | |
public void setPerson(Person person) {this.person = person;} | |
} |
JSR250 标准下的 @Resource
@Resource
也是用来属性注入的注解
它与 @Autowired
的区别是:
@Autowired
是依照 类型 注入@Resource
是依照 属性名 (也就是 bean 的名称) 注入
@Resource
注解相当于标注 @Autowired
和 @Qualifier
@Qualifier
这里简要阐明下, 为指定 bean 的名称而存在, 如果存在多个雷同的 bean, 而 bean 的名称不同, 咱们能够应用 @Autowired
配置 @Qualifier
注解
如: 上面示意该 Dog 类注入的客人 Bean 是名称为 xiaowang 的, 而以后容器内可能存在多个 客人 bean 对象 比方 xiaoli、xiaoming ….
@Component | |
public class Dog { | |
// ...... | |
@Autowired | |
@Qualifier("xiaowang") | |
private Person person; | |
} |
上面如果应用@Resource
能够更不便些 代码如下
@Component | |
public class Dog { | |
// ...... | |
@Resource(name="xiaowang") | |
private Person person; | |
} |
JSR330 标准下的 @Inject
@Inject
注解也是 依照类型注入 , 与@Autowire
的策略一样, 不过如要应用@Inject
须要额定的导入依赖
<!-- jsr330 --> | |
<dependency> | |
<groupId>javax.inject</groupId> | |
<artifactId>javax.inject</artifactId> | |
<version>1</version> | |
</dependency> |
前面的应用办法就与 SpringFramework 原生的 @Autowire
+ @Qualifier
雷同了
@Component | |
public class Dog { | |
@Inject // 等同于 @Autowired | |
@Named("xiaowang") // 等同于 @Qualifier | |
private Person person; |
它与 @Autowired
的区别是:
@Autowired
所在的包为org.springframework.beans.factory.annotation.Autowired
即为 SpringFramework 提供的@Inject
所在的包为javax.inject.Inject
属于 JSR 的标准 也就是说如果不应用 SpringFramework 时能够应用该注解
5. 简单类型注入
Array 注入
<property name="names"> | |
<array> | |
<value>PoXing</value> | |
<value>LaoWang</value> | |
</array> | |
</property> |
List 注入
<property name="tels"> | |
<list> | |
<value>13000000000</value> | |
<value>13000000001</value> | |
</list> | |
</property> |
Set 注入 -
<!-- 曾经提前申明好的 Dog --> | |
<bean id="wangcai" class="com.huodd.bean.ext.Dog"/> | |
--- | |
<property name="dogs"> | |
<set> | |
<bean class="com.huodd.bean.Dog"/> | |
<ref bean="wangcai"/> | |
</set> | |
</property> |
Map 注入
<property name="homesMap"> | |
<map> | |
<entry key="1" value="main"> | |
<ref bean="myHome1" /> | |
</entry> | |
<entry key="2" value="other"> | |
<ref bean="myHome2" /> | |
</entry> | |
</map> | |
</property> |
Properties 注入
<property name="props"> | |
<props> | |
<prop key="sex"> 男 </prop> | |
<prop key="age">18</prop> | |
</props> | |
</property> |
面试题
1.@Autowired 注入原理是什么?
- 先拿属性对应的类型, 去 IoC 容器中找相应的 Bean
- 如果没有找到 间接抛出
NoUniqueBeanDefinitionException
异样 - 如果找到一个 间接返回
- 如果找到多个雷同类型的 bean 再拿属性名去与这多个 bean 的 id 进行比照
- 如果有多个或者没有 则会抛出
NoUniqueBeanDefinitionException
异样 - 如果只有一个 间接返回
2. 依赖注入的形式有哪些, 都有什么区别
注入形式 | 被注入对象是否可扭转 | 是否依赖 IOC 框架的 API | 应用场景 |
---|---|---|---|
结构器注入 | 不可变 | 否 | 不可变的固定注入 |
参数注入 | 不可变 | 否 | 注解配置类中 @Bean 办法注册 bean |
属性注入(注解式属性注入) | 不可变 | 是(只能通过标注注解来侵入式注入) | 通常用于不可变的固定注入 |
setter 注入 | 可变 | 否 | 可选属性的注入 |
3. 主动注入的注解比照
注解 | 注入形式 | 是否反对 @Primary | 起源 | Bean 不存在时解决 |
---|---|---|---|---|
@Autowired | 依据类型注入 | 是 | SpringFramework 原生注解 | 可指定 required=false 来防止注入失败 |
@Resource | 依据名称注入 | 是 | JSR250 标准 | 容器中不存在指定 Bean 会抛出异样 |
@Inject | 依据类型注入 | 是 | JSR330 标准 (须要导 jar 包) | 容器中不存在指定 Bean 会抛出异样 |
@Qualifier
:如果被标注的成员 / 办法在依据类型注入时发现有多个雷同类型的 Bean,则会依据该注解申明的 name 寻找特定的 bean
@Primary
:如果有多个雷同类型的 Bean 同时注册到 IOC 容器中,应用“依据类型注入”的注解时会注入标注 @Primary
注解的 bean 即默认策略
4. 应用依赖注入有什么优缺点
- 依赖注入作为 IOC 的实现形式之一,目标就是 解耦,咱们不须要间接去 new 那些依赖的类对象就能够间接从容器中去取来应用, 如果组件存在多级依赖,依赖注入能够将这些依赖的关系简化。
- 依赖对象的 可配置:通过 xml 或者注解申明,能够指定和调整组件注入的对象,借助 Java 的多态个性,能够不须要大批量的批改就实现依赖注入的对象替换
我是 Baoxing,开始做一件事件最好的机会,要么是十年前,要么就是当初 。感激各位的 点赞 、 珍藏 和评论