0
点赞
收藏
分享

微信扫一扫

6. 依赖注入

霸姨 2022-05-02 阅读 53

两层意思:

  • 注入 Spring Bean 或者可依赖对象(@Autowired)

  • 注入外部化配置(@Value)

6.1 依赖注入的模式

  • 手动模式

    • XML 资源配置元信息

    • Java 注解配置元信息

    • API 配置元信息(普通用户使用较少,框架开发者使用较多,需掌握)

  • 自动模式(官方不推荐使用自动绑定模式)

    • Autowiring(自动绑定)

依赖注入类型:

依赖注入类型配置元数据举例
Setter 方法<property name = "user" ref="userBean"/>
构造器<constructor-arg name="user" ref="userBean"/>
字段@Autowired User user;
方法@Autowired public void user(User user) {...}
接口回调class MyBean implements BeanFactoryAware {...}

6.2 自动绑定

6.2.1 自动绑定原因

Spring 容器能够根据自动绑定在合作的 Bean(依赖 Bean 和被依赖 Bean) 之间,通过某种策略自动处理这些合作者之间的关系

自动绑定优点:

  • 减少属性或者构造器参数的设置

  • 自动绑定能够更新配置

6.2.2 自动绑定模式

模式说明
no默认值,未激活 Autowiring,需要手动指定依赖注入对象
byName根据被注入属性的名称作为 Bean 名称进行依赖查找,并将对象设置到该属性
byType根据被注入属性的类型作为依赖类型进行查找,并将对象设置到该属性
constructor特殊 byType 类型,用于构造器参数

参考枚举类:org.springframework.beans.factory.annotation.Autowire

public enum Autowire {
​
    /**
     * Constant that indicates no autowiring at all.
     */
    NO(AutowireCapableBeanFactory.AUTOWIRE_NO),
​
    /**
     * Constant that indicates autowiring bean properties by name.
     */
    BY_NAME(AutowireCapableBeanFactory.AUTOWIRE_BY_NAME),
​
    /**
     * Constant that indicates autowiring bean properties by type.
     */
    BY_TYPE(AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE);
​
​
    private final int value;
​
​
    Autowire(int value) {
        this.value = value;
    }
​
    public int value() {
        return this.value;
    }
​
    /**
     * Return whether this represents an actual autowiring value.
     * @return whether actual autowiring was specified
     * (either BY_NAME or BY_TYPE)
     */
    public boolean isAutowire() {
        return (this == BY_NAME || this == BY_TYPE);
    }
​
}

6.2.3 自动绑定限制以及不足(XML 特有)

  • 自动绑定会被属性或者构造器参数等注入覆盖

  • 无法绑定 String、原生类型、Class 类型以及这些类型的数组类型

  • 缺乏精确性

  • 自动绑定的信息很难在一些工具类中进行呈现,比如自动生成的文档等

  • 当绑定对象不止一个会有歧义,并且会报错

6.3 Setter 方法依赖注入

  • 手动模式

    • XML 资源配置元信息

    • Java 注解配置元信息

    • API 配置元信息

  • 自动模式

    • byName

    • byType

6.3.1 XML 方式

public class XmlDependencySetterInjectionDemo {
    public static void main(String[] args) {
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
        reader.loadBeanDefinitions("classpath:dependency-setter-injection.xml");
        System.out.println(factory.getBean(UserHolder.class));
    }
}
<?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/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
​
    <import resource="beans-ioc-lookup.xml"/>
​
    <bean class="com.gcl.dependency.injection.UserHolder">
        <property name="user" ref="superUser"/>
    </bean>
​
</beans>

6.3.2 Java 注解

public class AnnotationDependencySetterInjectionDemo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(AnnotationDependencySetterInjectionDemo.class);
​
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
        reader.loadBeanDefinitions("classpath:beans-ioc-lookup.xml");
​
        context.refresh();
​
        System.out.println(context.getBean(UserHolder.class));
​
        context.close();
    }
​
    @Bean
    public UserHolder userHolder(User user) {
        // 构造器方式
        // return new UserHolder(user);
​
        // setter 方式
        UserHolder holder = new UserHolder();
        holder.setUser(user);
        return holder;
    }
}

6.3.3 API 方式

public class ApiDependencySetterInjectionDemo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        // 注册 BeanDefinition
        context.registerBeanDefinition("userHolder", createBeanDefinitionUserHolder());
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
        reader.loadBeanDefinitions("classpath:beans-ioc-lookup.xml");
​
        context.refresh();
​
        System.out.println(context.getBean(UserHolder.class));
​
        context.close();
    }
​
    private static BeanDefinition createBeanDefinitionUserHolder() {
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(UserHolder.class);
​
        builder.addPropertyReference("user", "superUser");
​
        return builder.getBeanDefinition();
    }
}

6.3.4 自动注入方式

public class AutowiringByNameDependencySetterInjectionDemo {
    public static void main(String[] args) {
        DefaultListableBeanFactory context = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
        reader.loadBeanDefinitions("autowiring-dependency-setter-injection.xml");
        System.out.println(context.getBean(UserHolder.class));
    }
}
<?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/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
​
    <import resource="beans-ioc-lookup.xml"/>
​
    <!-- byName -->
    <bean class="com.gcl.dependency.injection.UserHolder" autowire="byName"/>
    
    <!-- byType -->
<!--    <bean class="com.gcl.dependency.injection.UserHolder" autowire="byType"/>-->
​
</beans>

6.4 构造器依赖注入

  • 手动模式

    • XML 资源配置元信息

    • Java 注解配置元信息

    • API 配置元信息

  • 自动模式

    • constructor

6.4.1 XML 方式

public class XmlDependencyConstructorInjectionDemo {
    public static void main(String[] args) {
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
        reader.loadBeanDefinitions("classpath:dependency-constructor-injection.xml");
        System.out.println(factory.getBean(UserHolder.class));
    }
}
<?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/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
​
    <import resource="beans-ioc-lookup.xml"/>
​
    <bean class="com.gcl.dependency.injection.UserHolder">
        <constructor-arg name="user" ref="user"/>
    </bean>
​
</beans>

6.4.2 Java 注解方式

public class AnnotationDependencyConstructorInjectionDemo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(AnnotationDependencyConstructorInjectionDemo.class);
​
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
        reader.loadBeanDefinitions("classpath:beans-ioc-lookup.xml");
​
        context.refresh();
​
        System.out.println(context.getBean(UserHolder.class));
​
        context.close();
    }
​
    @Bean
    public UserHolder userHolder(User user) {
         // 构造器方式
         return new UserHolder(user);
    }
}

6.4.3 API 方式

public class ApiDependencyConstructorInjectionDemo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
​
        context.registerBeanDefinition("userHolder", createBeanDefinitionUserHolder());
​
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
        reader.loadBeanDefinitions("classpath:beans-ioc-lookup.xml");
​
        context.refresh();
​
        System.out.println(context.getBean(UserHolder.class));
​
        context.close();
    }
​
    private static BeanDefinition createBeanDefinitionUserHolder() {
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(UserHolder.class);
​
        // 按照构造器参数顺序进行设置
        builder.addConstructorArgReference("superUser");
​
        return builder.getBeanDefinition();
    }
}

6.4.4 自动注入方式

public class AutowiringByConstructorDependencySetterInjectionDemo {
    public static void main(String[] args) {
        DefaultListableBeanFactory context = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
        reader.loadBeanDefinitions("autowiring-dependency-constructor-injection.xml");
        System.out.println(context.getBean(UserHolder.class));
    }
}
<?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/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
​
    <import resource="beans-ioc-lookup.xml"/>
​
    <bean class="com.gcl.dependency.injection.UserHolder" autowire="constructor"/>
​
</beans>

6.5 字段注入

只有手动注入方式,以注解为主:

  • @Autowired(忽略静态字段)

  • @Resource

  • @Inject

public class AnnotationDependencyFieldInjectionDemo {
​
    // Autowire 会忽略静态字段
    @Autowired
    private UserHolder userHolder;
​
    @Resource
    private UserHolder userHolder2;
​
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(AnnotationDependencyFieldInjectionDemo.class);
​
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
        reader.loadBeanDefinitions("classpath:beans-ioc-lookup.xml");
​
        context.refresh();
​
        AnnotationDependencyFieldInjectionDemo demo = context.getBean(AnnotationDependencyFieldInjectionDemo.class);
        System.out.println(demo.userHolder);
        System.out.println(demo.userHolder == demo.userHolder2);
​
        context.close();
    }
​
    @Bean
    public UserHolder userHolder(User user) {
         // 构造器方式
         return new UserHolder(user);
    }
}

6.6 方法注入

手动注入:

  • @Autowired

  • @Resource

  • @Inject(可选)、

  • @Bean

public class AnnotationDependencyMethodInjectionDemo {
​
    private UserHolder userHolder;
​
    private UserHolder userHolder2;
​
    @Autowired
    public void initUserHolder(UserHolder userHolder) {
        this.userHolder = userHolder;
    }
​
    @Resource
    public void initUserHolder2(UserHolder userHolder2) {
        this.userHolder2 = userHolder2;
    }
​
    @Bean
    public UserHolder userHolder(User user) {
        // 构造器方式
        return new UserHolder(user);
    }
​
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(AnnotationDependencyMethodInjectionDemo.class);
​
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
        reader.loadBeanDefinitions("classpath:beans-ioc-lookup.xml");
​
        context.refresh();
​
        AnnotationDependencyMethodInjectionDemo demo = context.getBean(AnnotationDependencyMethodInjectionDemo.class);
        System.out.println(demo.userHolder);
        System.out.println(demo.userHolder == demo.userHolder2);
​
        context.close();
    }
}

6.7 回调注入

Aware 系列接口回调注入(内建依赖)

内建接口说明
BeanFactoryAware获取 IoC 容器——BeanFactory
ApplicationContextAware获取 Spring 应用上下文——ApplicationContext 对象
EnvironmentAware获取 Environment 对象
ResourceLoaderAware获取资源加载器对象——ResourceLoader
BeanClassLoaderAware获取加载当前 Bean Class 的 ClassLoader
BeanNameAware获取当前 Bean的名称
MessageSourceAware获取 MessageSource 对象,用于 Spring 国际化
ApplicationEventPublisherAware获取 ApplicationEventPublisher 对象,用于 Spring 事件
EmbeddedValueResolverAware获取 StringValueResolver 对象,用于占位符处理

6.8 依赖注入类型选择

  • 低依赖:构造器注入(Spring 官方推荐注入方式)

  • 多依赖:Setter 方法注入

  • 便利性:字段注入(Spring 和 Spring 都不推荐使用字段注入)

  • 声明类:方法注入

6.9 基础类型注入

  • 原生类型(Primitive):boolean、byte、char、short、int、long、float、double

  • 标量类型(Scalar):Number、Character、Boolean、Enum、Locale、Charset、Currency、Properties、UUID

  • 常规类型(General):Object、String、TimeZone、Calendar、Optional等

  • Spring 类型:Resource、InputSource、Formatter

<?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/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
​
    <!-- bean definitions here -->
    <bean id="user" class="com.gcl.bean.User">
        <property name="name" value="foo"/>
        <property name="age" value="18"/>
        <!-- 枚举类型注入 -->
        <property name="city" value="BEIJING"/>
        <!-- Resource 对象注入-->
        <property name="configFileLocation" value="classpath:user-config.properties"/>
    </bean>
    <bean id="superUser" class="com.gcl.bean.SuperUser" parent="user" primary="true">
        <property name="level" value="1"/>
    </bean>
    <bean id="userRepository" class="com.gcl.bean.UserRepository" autowire="byType"/>
    <!--        <property name="users">-->
    <!--            <util:list>-->
    <!--                <ref bean="user"/>-->
    <!--                <ref bean="superUser"/>-->
    <!--            </util:list>-->
    <!--        </property>-->
    <!--    </bean>-->
​
    <bean id="objectFactory" class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean">
        <property name="targetBeanName" value="user"/>
    </bean>
​
</beans>

6.10 集合类型注入

  • 数组类型(Array):原生类型、标量类型、常规类型、Spring 类型

  • 集合类型(Collection)

    • Collection:List、Set(SortedSet、NavigableSet、EnumSet)

    • Map:Properties

<?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/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
​
    <!-- bean definitions here -->
    <bean id="user" class="com.gcl.bean.User">
        <property name="name" value="foo"/>
        <property name="age" value="18"/>
        <property name="city" value="BEIJING"/>
        <property name="cities" value="BEIJING,HANGZHOU"/>
        <property name="cityList">
            <list>
                <value>BEIJING</value>
                <value>HANGZHOU</value>
                <value>SHANGHAI</value>
            </list>
        </property>
        <property name="citySet">
            <set>
                <value>HANGZHOU</value>
                <value>SHANGHAI</value>
            </set>
        </property>
        <property name="cityMap">
            <map>
                <entry key="beijing" value="BEIJING"/>
                <entry key="shanghai" value="SHANGHAI"/>
            </map>
        </property>
        <property name="configFileLocation" value="classpath:user-config.properties"/>
    </bean>
    <bean id="superUser" class="com.gcl.bean.SuperUser" parent="user" primary="true">
        <property name="level" value="1"/>
    </bean>
    <bean id="userRepository" class="com.gcl.bean.UserRepository" autowire="byType"/>
    <!--        <property name="users">-->
    <!--            <util:list>-->
    <!--                <ref bean="user"/>-->
    <!--                <ref bean="superUser"/>-->
    <!--            </util:list>-->
    <!--        </property>-->
    <!--    </bean>-->
​
    <bean id="objectFactory" class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean">
        <property name="targetBeanName" value="user"/>
    </bean>
​
</beans>

6.11 限定注入

  • @Qualifier 限定注入

    • 通过 Bean 名称限定

    • 通过分组限定

  • 基于 @Qualifier 扩展限定

    • 自定义注解——如 Spring Cloud @LoadBalance

public class AnnotationQualifierDependencyInjectionDemo {
​
    @Autowired
    @Qualifier("user")
    private User user;
​
    @Autowired
    private Collection<User> allUsers;
​
    @Autowired
    @Qualifier
    private Collection<User> qualifiedUsers;
​
    // 通过使用 @Qualifier 进行逻辑分组
    @Bean
    @Qualifier
    public User user1() {
        User user = new User();
        user.setName("user1");
        return user;
    }
​
    // 通过使用 @Qualifier 进行逻辑分组
    @Bean
    @Qualifier
    public User user2() {
        User user = new User();
        user.setName("user2");
        return user;
    }
​
​
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(AnnotationQualifierDependencyInjectionDemo.class);
​
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
        reader.loadBeanDefinitions("classpath:beans-ioc-lookup.xml");
        context.refresh();
​
        AnnotationQualifierDependencyInjectionDemo bean = context.getBean(AnnotationQualifierDependencyInjectionDemo.class);
        System.out.println(bean.user);
        System.out.println(bean.allUsers);
        System.out.println(bean.qualifiedUsers);
​
        context.close();
    }
}

6.12 延迟注入

  • 使用 API ObjectFactory 实现延迟注入

    • 单一类型

    • 集合类型

  • 使用 API ObjectProvider 实现延迟注入(推荐使用,安全)

    • 单一类型

    • 集合类型

6.13 依赖处理过程

  • 入口——DefaultListableBeanFactory#resolveDependency

  • 依赖描述符——DependencyDescriptor

  • 自动绑定候选对象处理器——AutowireCandidateResolver

6.14 @Autowire 注入原理

  • 元信息解析(MergedBeanDefinitionPostProcessor#postProcessorMergedBeanDefinition 方法中进行)

  • 依赖查找(注入过程中包含依赖查找)

  • 依赖注入(字段、方法)(AutowiredAnnotationBeanPostProcessor#postProcessorProperties 方法中进行)

6.15 JSR-330 @Inject 注入原理

public AutowiredAnnotationBeanPostProcessor() {
    this.autowiredAnnotationTypes.add(Autowired.class);
    this.autowiredAnnotationTypes.add(Value.class);
    try {
        this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
                                          ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
        logger.trace("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring");
    }
    catch (ClassNotFoundException ex) {
        // JSR-330 API not available - simply skip.
    }
}

@Inject 注解跟 @Autowired 注解注入流程是相似的,如果引入了 JSR-330 jar 包,就可以使用该注解进行注入

6.16 Java 通用注解注入原理(@Resource等)

CommonAnnotationBeanPostProcessor(比 AutowiredAnnotationBeanPostProcessor 支持更多的注解,除了注入注解,还支持生命周期注解)

  • 注入注解

    • javax.xml.ws.WebServiceRef

    • javax.ejb.EJB

    • javax.annotation.Resource

  • 生命周期注解(构建 LifecycleElement)

    • javax.annotation.PostConstructor

    • javax.annotation.PreDestroy

6.17 自定义依赖注入注解

  • 基于 AutowiredAnnotationBeanPostProcessor 实现

  • 自定义实现

    • 生命周期处理

      • InstantiationAwareBeanPostProcessor

      • MergedBeanDefinitionPostProcessor

    • 元数据

      • InjectElement

      • InjectMetadata

6.18 面试题

6.18.1 有多少种依赖注入方式

  • 构造器注入(少依赖,强制依赖)

  • Setter 注入(多依赖,非强制依赖)

  • 字段注入

  • 方法注入

  • 接口回调注入

6.18.2 你偏好构造器注入还是 Setter 注入?

两种依赖注入均可使用,如果是必须依赖,推荐使用构造器注入,Setter注入可选依赖

依赖注入少用外部注解,包括 Java 注解和 Spring 注解,sh奥用注解就少依赖,使用原始 API、原始构造器参数来进行操作,可以解决依赖问题,同时还能解决线程安全问题(构造器可以确保线程安全问题)

6.18.3 Spring 依赖注入来源有哪些?

依赖查找通过 getBean,依赖注入通过 resolveDependency

  • 自定义 Bean(XML、注解、API)

  • 内建 Bean

  • 内建依赖

举报

相关推荐

0 条评论