spring源碼學習---Spring IoC 依賴注入

一、依賴注入Dependency injection

直接在容器啓動時通過構造器,getter setter等形式注入依賴。

優點:性能高,侵入小

與依賴查詢兩者區別:區別在於依賴的對象是否爲主動獲取,是的話,就是依賴查找,否則就是依賴注入,由框架綁定完成。

依賴查找 VS 依賴注入

在這裏插入圖片描述

二、依賴注入的模式和類型

手動模式-配置或者編程的方式,提前安排注入規則

    • XML 資源配置元信息
    • Java 註解配置元信息
    • API 配置元信息 (一般對容器擴展使用)

自動模式-實現方提供依賴自動關聯的方式,按照內建的注入規則

    • Autowiring(自動綁定)

•依賴注入類型

依賴注入類型  配置元數據舉例
Setter 方法  <proeprty name="user" ref="userBean"/>
構造器   <constructor-arg name="user" ref="userBean" />
字段  @Autowired User user;
方法   @Autowired public void user(User user) { ... }
接口回調  class MyBean implements BeanFactoryAware { ... }

2.1、Autowiring(自動綁定)

    Autowiring in Spring. Autowiring feature of spring framework enables you to inject the object dependency implicitly. It internally uses setter or constructor injection. Autowiring can't be used to inject primitive and string values. It works with reference only.

example:
    只需要在需要自動裝配的bean標籤上設置autowire=true即可,spring會自動在容器中查找依賴,並使用setter方法爲我們裝配依賴,我們需要做的就是在xml中聲明依賴,如果使用了annotation,那事情將會變得更簡單

代碼示例:

   首先是創建兩個類 Person 和 PersonHolder:

@Data
public class Person {
    private Long id;

    private String name;

    private Integer age;

    public Person() {
    }

    public Person(Long id, String name, Integer age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }
}
public class PersonHolder {
    
    private Person person;
    public PersonHolder(Person person) {
        this.person = person;
    }

    public PersonHolder() {
    }
}

xml配置:

 <bean id="person" class="com.dukun.study.injection.enetity.Person">
        <property name="id" value="1"/>
        <property name="name" value="坤仔"/>
        <property name="age" value="19"/>
    </bean>
    <!-- Autowiring 構造器自動注入-->
    <bean  id="personHolderConstructor" class="com.dukun.study.injection.enetity.PersonHolder"
           autowire="constructor">
    </bean>
    <!-- Autowiring setter自動注入 這裏會注入person 這個id-->
    <bean  id="personHolderSetterByName" class="com.dukun.study.injection.enetity.PersonHolder"
           autowire="byName">

2.2、Setter 方法注入:

xml示例:

  <!--  setter手動注入-->
    <bean id="personHolder" class="com.dukun.study.injection.enetity.PersonHolder">
          <property name="person" ref="person" />
    </bean>

2.3、構造器注入

xml示例:


    <!-- 構造器手動注入-->
    <bean  id="personHolderConstructorTwo" class="com.dukun.study.injection.enetity.PersonHolder">
        <constructor-arg name="person" ref="person" />
    </bean>

構造器注入和Setter 方法注入的區別:

  構造器注入 是按照構造器參數順序決定的,

  Setter 方法注入是無序的。由於 Java 反射 API 所返回的 public 方法熟順序並非定義順序,所以無法控制先後情況

 

2.4、字段注入

• 實現方法 
    • 手動模式 
        • Java 註解配置元信息 
            • @Autowired  根據類型注入 (@Autowired 會忽略掉靜態字段 也就是會注入失敗)
            • @Resource  根據名稱注入
            • @Inject(可選)

示例:

   @Autowired
    private
//    static // @Autowired 會忽略掉靜態字段
            UserHolder userHolder;

    @Resource
    private UserHolder userHolder2;

2.5 、方法注入

• 實現方法 
    • 手動模式 
        • Java 註解配置元信息 
            • @Autowired  
            • @Resource 
            • @Inject(可選)

示例:

    private UserHolder userHolder;

    private UserHolder userHolder2;

    @Autowired
    public void init1(UserHolder userHolder) {
        this.userHolder = userHolder;
    }

    @Resource
    public void init2(UserHolder userHolder2) {
        this.userHolder2 = userHolder2;
    }

方法注入和Setter注入有什麼不同?

 Setter 注入是通過 Java Beans 來實現的,而方法注入則是直接通過 Java 反射來做的。當然底層都是 Java 反射~

2.6、接口回調注入(接觸少重點看下)

接口回調注入是指 實現spring 提供的接口, 獲得相應的ioc容器,在向容器中注入.

     Spring內置了用於不同目的的大量回調接口,很多場合都會使用到它們。使用這些回調接口往往能夠達到事半功倍的效果。一旦目標受管Bean實現了回調接口,則當DI容器實例化受管Bean時,DI容器就會自動調用這些回調接口所定義的方法,進而將相關對象注入進來。最終,受管Bean便可使用它們了。

內建接口 內建接口
BeanFactoryAware 獲取IoC 容器-BeanFactory
ApplicationContextAware 獲取Spring 應用上下文-ApplicationContext 對象
EnvironmentAware 獲取Environment 對象
ResourceLoaderAware 獲取資源加載器對象-ResourceLoader
BeanClassLoaderAware 獲取加載當前Bean Class 的ClassLoader 
BeanNameAware 獲取當前Bean 的名稱
MessageSourceAware 獲取MessageSource 對象,用於Spring 國際化
ApplicationEventPublisherAware 獲取ApplicationEventPublishAware 對象,用於Spring 事件
EmbeddedValueResolverAware 獲取StringValueResolver 對象,用於佔位符處理

1、BeanFactoryAware回調接口 獲取IoC 容器-BeanFactory

public interface BeanFactoryAware extends Aware {

	void setBeanFactory(BeanFactory beanFactory) throws BeansException;

}

2. ApplicationContextAware回調接口

類似於BeanFactoryAware回調接口,ApplicationContextAware使得受管Bean能夠感知到IoC容器的存在, 它定義的回調方法如下。注意,ApplicationContextAware僅僅適合於ApplicationContext容器。

public interface ApplicationContextAware extends Aware {
	void setApplicationContext(ApplicationContext applicationContext) throws BeansException;

}

示例代碼:

/**
 * 基於 {@link Aware} 接口回調的依賴注入示例
 * Created by dukun on 2020/7/3.
 */
public class AwareInterfaceDependencyInjectionDemo implements BeanFactoryAware ,ApplicationContextAware {

    private static BeanFactory beanFactory;

    private static ApplicationContext applicationContext;

    public static void main(String[] args) {
        // 創建 BeanFactory 容器
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        // 註冊 Configuration Class(配置類) -> Spring Bean
        context.register(AwareInterfaceDependencyInjectionDemo.class);
        // 啓動 Spring 應用上下文
        context.refresh();
        System.out.println(beanFactory == context.getBeanFactory());
        System.out.println(applicationContext == context);
        // 顯示地關閉 Spring 應用上下文
        context.close();
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        AwareInterfaceDependencyInjectionDemo.beanFactory = beanFactory;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        AwareInterfaceDependencyInjectionDemo.applicationContext= applicationContext;
    }
}

其實 beanFactory和applicationContext屬性 和從容器中獲得是相等的 所以打印輸出都是 true

2.7 基於 API 實現依賴 注入

基於api的注入 要手動生成 類的 的 BeanDefinition 對象在注入到容器中

代碼示例:

/**
 *基於 API 實現依賴 Constructor 注入示例
 * 基於 API 實現依賴 Setter 方法注入示例
 * Created by dukun on 2020/7/3.
 */
public class ApiDependencyInjectionDemo {

    public static void main(String[] args) {
        // 創建 BeanFactory 容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        // 生成 UserHolder 的 BeanDefinition
        //基於 API 實現依賴  Constructor 方法注入示例
        BeanDefinition userHolderBeanDefinitionSetter = createUserHolderBeanDefinitionSetter();
        //基於 API 實現依賴  Constructor 方法注入示例
        BeanDefinition userHolderBeanDefinitionConstructor = createUserHolderBeanDefinitionConstructor();
        applicationContext.registerBeanDefinition("userHolder1",userHolderBeanDefinitionSetter);
        applicationContext.registerBeanDefinition("userHolder2",userHolderBeanDefinitionConstructor);
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(applicationContext);

        String xmlResourcePath = "classpath:/META-INF/dependency-lookup-context.xml";
        // 加載 XML 資源,解析並且生成 BeanDefinition
        beanDefinitionReader.loadBeanDefinitions(xmlResourcePath);

        // 啓動 Spring 應用上下文
        applicationContext.refresh();

        // 依賴查找並且創建 Bean
        UserHolder userHolder1 = (UserHolder) applicationContext.getBean("userHolder1");
        UserHolder userHolder2 = (UserHolder) applicationContext.getBean("userHolder2");
        System.out.println(userHolder1);
        System.out.println(userHolder2);
        // 顯示地關閉 Spring 應用上下文
        applicationContext.close();
    }

    /**
     * 基於 API 實現依賴 Setter 方法注入示例
     * 使用BeanDefinitionBuilder 生成BeanDefinition
     * @return
     */
    private static BeanDefinition createUserHolderBeanDefinitionSetter() {
        BeanDefinitionBuilder beanDefinitionBuilde=BeanDefinitionBuilder.genericBeanDefinition(UserHolder.class);
        beanDefinitionBuilde.addPropertyReference("user","superUser");
        return beanDefinitionBuilde.getBeanDefinition();
    }

    /**
     * 基於 API 實現依賴  Constructor 方法注入示例
     * 使用BeanDefinitionBuilder 生成BeanDefinition
     * @return
     */
    private static BeanDefinition createUserHolderBeanDefinitionConstructor() {
        BeanDefinitionBuilder beanDefinitionBuilde=BeanDefinitionBuilder.genericBeanDefinition(UserHolder.class);
        beanDefinitionBuilde.addConstructorArgReference("user");
        return beanDefinitionBuilde.getBeanDefinition();
    }
}

三、限定注入 @Qualifier 

•使用註解@Qualifier 限定

   • 通過Bean 名稱限定
    • 通過分組限定

• 基於註解@Qualifier 擴展限定
    • 自定義註解-如Spring Cloud @LoadBalanced

3.1、通過Bean 名稱限定

  @Autowired
    @Qualifier("person") // 指定 Bean 名稱或 ID
    private Person namedUser;

3.2、通過分組限定

/**
 * @link Qualifier} 註解依賴注入
 * Created by dukun on 2020/7/3.
 */
public class QualifierAnnotationDependencyInjectionDemo {

    @Autowired
    @Qualifier("person") // 指定 Bean 名稱或 ID
    private Person namedUser;

    @Autowired
    private Collection<Person> allPerson; //全部的bean

    @Autowired
    @Qualifier("group1") //限定使用分組獲取bean
    private List<Person> group1Beans;

    @Autowired
    @Qualifier("group2")
    private List<Person> group2Beans;


    public static void main(String[] args) {
        // 創建 BeanFactory 容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        // 註冊 Configuration Class(配置類) -> Spring Bean
        applicationContext.register(QualifierAnnotationDependencyInjectionDemo.class);
        // 啓動 Spring 應用上下文
        applicationContext.refresh();

        // 依賴查找 QualifierAnnotationDependencyInjectionDemo Bean
        QualifierAnnotationDependencyInjectionDemo demo = applicationContext.getBean(QualifierAnnotationDependencyInjectionDemo.class);
        System.out.println("demo.namedUser:" + demo.namedUser);
        System.err.println("demo.allPerso:" + demo.allPerson);
        System.err.println(" demo.group1Beans:" + demo.group1Beans);
        System.err.println(" demo.group2Beans:" + demo.group2Beans);

        applicationContext.close();
    }

    @Bean
    public Person person() {
        return createCallerInfo("person",0l);
    }

    @Bean
    @Qualifier("group1") //分組爲group1
    public Person person1() {
        return createCallerInfo("person1",1l);
    }
    @Bean
    @Qualifier("group1")
    public Person person2() {
        return createCallerInfo("person2",2l);
    }

    @Bean
    @Qualifier("group2") //分組爲group2
    public Person person3() {
        return createCallerInfo("person3",3l);
    }
    @Bean
    @Qualifier("group2")
    public Person person4() {
        return createCallerInfo("person4",4l);
    }

    private static Person createCallerInfo(String name,Long id) {
        Person person = new Person();
        person.setAge(18);
        person.setName(name);
        person.setId(id);
        return  person;
    }
}

3.3 基於註解@Qualifier 擴展限定  

    自定義註解-如Spring Cloud @LoadBalanced

  如果需要擴展只需要在 自定義註解上加上 @Qualifier 就可以就行分組限定

如:@LoadBalanced 只有 標註這個就說明是 開啓負載均衡

/**
 * Annotation to mark a RestTemplate bean to be configured to use a LoadBalancerClient
 * @author Spencer Gibb
 */
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}

自己自定義分組 自定義註解:

/**
 * 用戶組註解,擴展 {@link Qualifier @Qualifier}
 *
 * @author 
 * @since
 */
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Qualifier
public @interface PersonGroup {
}

使用:

  @Bean
    @PersonGroup //分組爲group2
    public Person person3() {
        return createCallerInfo("person3",3l);
    }
    @Bean
    @PersonGroup
    public Person person4() {
        return createCallerInfo("person4",4l);
    }

調用:

    @Autowired
    @PersonGroup
    private List<Person> group2Beans;

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章