有幻想,有情怀,有温度,干货满满; 关注一下我吧。

一、注解驱动IoC

xml驱动的IoC容器应用的是ClassPathXmlApplicationContext读取xml内bean信息

注解驱动的IoC容器应用的是AnnotationConfigApplicationContext读取Java类中的bean信息

1. AnnotationConfigApplicationContext 的注册应用

相比于xml文件作为驱动, 注解驱动须要指明配置类 一个配置类能够了解为"相当于"一个xml 配置类只须要在类上标注注解 @Configuration

@Configurationpublic class DemoConfiguration {}

在xml中申明bean的形式

<bean id="person" class="com.huodd.bean.Person"></bean>

在配置类中应用的是@Bean注解

@Beanpublic 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

@Componentpublic 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@Componentpublic @interface Service {}

这样 咱们在进行合乎三层架构的开发时 对于相应的如 ServiceImpl等 就能够间接标注 @Service 等注解了

@Configuration

@Configuration 底层也有标注@Component

@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Componentpublic @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进去 之后返回对象

@Beanpublic 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的时候注入属性时 就须要同时指定参数值

@Beanpublic 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注解 本人尝试的小伙伴要留神哦 否则会报错的)

@Componentpublic 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 次要用来导入内部配置文件

  1. 这里咱们创立一个 dog.properties
dog.id=1dog.name=wangcaidog.age=3
  1. 引入配置文件
@PropertySource("classpath:di/dog.properties")@ComponentScan("com.huodd.bean")@Configurationpublic class DemoComponentScanConfiguration {}
  1. Dog类中属性注入 这里@Value须要配合占位符 来获取properties配置文件中的内容
@Value("${dog.id}")private Integer id;@Value("${dog.name}")private String name;@Value("${dog.age}")private Integer age;
  1. 批改一下启动类
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 → 在属性上标注

@Componentpublic class Dog {    // ......    @Autowired    private Person person;}

办法2 → 应用结构器注入形式

@Componentpublic class Dog {    // ......    private Person person;    @Autowired    public Dog(Person person) {        this.person = person;    }}

办法3 → 应用setter办法注入

@Componentpublic 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 ....

@Componentpublic class Dog {    // ......    @Autowired    @Qualifier("xiaowang")    private Person person;}

上面如果应用@Resource 能够更不便些 代码如下

@Componentpublic 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 雷同了

@Componentpublic 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注入原理是什么?

  1. 先拿属性对应的类型,去IoC容器中找相应的Bean
  2. 如果没有找到 间接抛出NoUniqueBeanDefinitionException异样
  3. 如果找到一个 间接返回
  4. 如果找到多个雷同类型的bean 再拿属性名去与这多个bean的id进行比照
  5. 如果有多个或者没有 则会抛出NoUniqueBeanDefinitionException异样
  6. 如果只有一个 间接返回

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,开始做一件事件最好的机会,要么是十年前,要么就是当初。 感激各位的 点赞珍藏评论