天天看點

IOC和DIIOC和DI

文章目錄

  • IOC和DI
    • Spring控制依賴
    • IOC容器介紹
      • BeanFactory
      • ApplicationContext
      • 主要使用的三種建立IOC的方式
        • ClassPathXmlApplicationContext
        • FileSystemXmlAplicationContext
        • XmlWebAplicationContext
        • 其他建立
      • BeanFactory和ApplicationContext的關系和差別使用和作用點
      • Bean的執行個體化方式
        • 基于XML配置方式裝配Bean
          • *基于無參構造函數建立對象*重點
          • 基于靜态工廠方式建立對象
          • 基于普通工廠方法實作Bean
        • 基于注解的方式裝配Bean
          • application配置檔案引入context
          • 在交給IOC容器管理的類上添加注解
          • 通過IOC擷取對象
          • 四種注解類型标記Bean
        • Spring中DI
          • 基于XML配置檔案注入
            • 基于有參構造函數注入依賴
            • 基于set方法注入依賴
            • 自定義類型注入依賴
            • 注入集合類型
          • 基于注解的注入
      • 問題
        • 依賴解析過程
          • 循環依賴

IOC和DI

IOC(inversion of Control) 控制反轉容器

如何了解控制反轉?

将對象的建立,配置和維護交給第三方管理,解決了依賴的耦合度,大大降低。

由容器來進行建立,配置和維護等工作,使用者需要調用對象,就可以在IOC容器中擷取。

對象于對象之間的依賴關系在spring中如何解決

Spring中對象在建立之後,需要依賴其他對象時,就需要

依賴注入DI

(Dependecy injection)

控制反轉是将對象交給外部管理,依賴注入問題也可以交給IOC容器進行管理

IOC核心在于:對象不由使用方雙方管理,而是交予第三方管理。

優點為:

  1. 資源集中管理,實作資源的可配置和維護
  2. 降低使用資源雙方的依賴程度,降低耦合度。

Spring控制依賴

  1. 增加一個spring的配置檔案
  2. 解析XML檔案擷取管理對象,

    反射

  3. 将解析的bean放入BeanFactory工廠類
  4. 在工廠類中通過反射建立處person類

IOC容器介紹

解決對象的建立和對象之間的依賴關系。

IOC和DIIOC和DI

ApplicationContext容器中的接口的繼承關系,ApplicationContext是BeanFactory的子接口之一,即BeanFactory是Spring IOC容器定義的最底層的接口,ApplicationCentext是BeanFactory的進階實作之一,是對BeanFactory功能做了許多的擴充。

BeanFactory

BeanFactory是最底層的IOC容器實作

現在已經不用了

//擷取IOC容器
XmlBeanFactory xmlBeanFactory = new XmlBeanFactory(new ClassPathResource("application.xml"));
           

ApplicationContext

是IOC的進階使用,對BeanFectory的擴充,

實際過程中使用此類

主要使用的三種建立IOC的方式

ClassPathXmlApplicationContext

相對路徑讀取classpath中的資源,讀本項目下

ClassPathXmlApplicationContext("application1.xml");
           

FileSystemXmlAplicationContext

讀取指定路徑下的資源

new FileSystemXmlApplicationContext("全路徑");
           

XmlWebAplicationContext

需要在Web環境下讀取資源,讀取網絡中的IOC路徑

其他建立

IOC和DIIOC和DI

BeanFactory和ApplicationContext的關系和差別使用和作用點

BeanFactory是最底層的IOC容器實作,而ApplicationContext 而是BeanFactory的拓展,是IOC的進階使用

作用:BeanFactory 負責讀取bean配置文檔,管理bean的加載,執行個體化,維護bean之間的依賴關系,負責bean的聲明周期

而ApplicationContext除了滿足以上的功能外,還提供了完整的架構功能 1.國際化支援 2.資源通路 3.事件傳遞

使用:

BeanFactory

//擷取IOC容器
XmlBeanFactory xmlBeanFactory = new XmlBeanFactory(new ClassPathResource("application.xml"));
           

AoolicationContext

Bean的執行個體化方式

Spring容器裝配Bean的方式主要是兩種

  1. 基于XML配置方式
  2. 基于注解的方式,實際使用最多

基于XML配置方式裝配Bean

Bean的裝配執行個體化方式

基于無參構造函數建立對象重點
//id 類名   class 包路徑
<bean id="Student" class="org.example.Student"></bean>
           

必須確定Student的構造函數時無參的

package org.example;
public class Student {
    
public Student() {
    this.name = name;
    this.sex = sex;
    this.age = age;
}
}
           
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
Student student = (Student)context.getBean("Student");
student.setName("張三");
System.out.println(student.getName());
           
基于靜态工廠方式建立對象

在确定實作類中沒有

無參構造

給定靜态工廠類來擷取Student類

package org.example;
public class Student {
  public Student(String name,String sex , int age) {
        this.name = name;
        this.sex = sex;
        this.age = age;
    }
    public static Student getStudent(){
        return new Student("張三","男",20);
    }
}
           
<!--基于靜态工廠方式執行個體化bean-->
    <bean id="Student" class="org.example.Student" factory-method="getStudent"/>
           

class屬性指定的靜态工廠類的全路徑 ,factory-method屬性即對應的方法,目前擷取Student類在靜态工廠下提供的getStudent方法可擷取該對象

app實作

ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
Student student = (Student)context.getBean("Student");
student.setName("張三");
System.out.println(student.getName());
           
基于普通工廠方法實作Bean
package org.example;

public class Student {
    private String name;
    private String sex;
    private int age;

public Student getStudent(){
    return new Student();
}
}
           

配置Bean資訊

<bean id="factoty" class="org.example.Student"/>
    <bean id="Student" factory-bean="factoty" factory-method="getStudent"/>
           

基于注解的方式裝配Bean

比XML形式裝配bean會更加簡單

application配置檔案引入context
<?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
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    
    <!--開啟注解掃描:在指定的包路徑下所有的類名,屬性等上的注解都會進行掃描-->
    <context:component-scan base-package="org.example"/>

    <!--開啟注解掃描:掃描屬性上的注解:不推薦使用-->
    <!--<context:annotation-config></context:annotation-config>-->
</beans>
           

注意:

使用标簽一定要引入context限制,該限制下才提供

在交給IOC容器管理的類上添加注解
package org.example.bean;
import org.springframework.stereotype.Component;

//@Component("Student") 與下面等同
@Component(value = "Student")
//等同于<bean id = "person" class = "org.example.bean.Student"></bean>
//預設給定的名稱為類名首字母小寫
public class Student {
    private String name;
    private String sex;
    private int age;
}
           

需要給定名字 不然預設時類名首字母小寫

通過IOC擷取對象
ApplicationContext context = new ClassPathXmlApplicationContext("application2.xml");
Student student = (Student)context.getBean("Student");
student.setName("zs");
System.out.println(student.getName());
           
四種注解類型标記Bean

使用注解在配置檔案中指定掃描的包路徑或者類路徑後,交給IOC管理得類上添加注解即可

@Component 通用的标注的注解 連接配接前端

@Repository 對dao層實作類進行标注 連接配接資料庫

@Service 對service層實作類進行标注

@Controller 對Controller層實作類進行标注 讓元件掃描将這個類别識别為一個元件

@Component 是 Spring 提供的通用的元件注解

@Repository、@Service 、@Controller都是Component其衍生出來,功能都是一樣的,可以互換,

主要是為了區分被注解的類處在不同的業務層。

Spring中DI

DI-Dependency injection

依賴注入

元件之間的依賴關系由容器在運作時決定,IOC動态的為某個依賴注入到元件中。

那麼他們之間 誰注入誰? IOC的作用是什麼? 為什麼要依賴?注入什麼?

容器:IOC 元件(某個特定的類) 資源(元件依賴的内容)

誰依賴于誰:應用程式依賴于IOC容器

為什麼需要依賴:應用程式需要IOC容器提供元件需要的外部資源

誰注入誰:IOC容器注入應用程式需要的資源,元件依賴的資源

注入什麼:注入了某個對象所需要的外部資源

基于XML配置檔案注入

基于有參構造函數注入依賴

給定User對象,定義有參構造

public class User1 {
    private Integer id;
    private String name;
    private String passwd;
    private String address;

    //    有參構造函數
    public User1(Integer id, String name, String passwd, String address) {
        this.id = id;
        this.name = name;
        this.passwd = passwd;
        this.address = address;
    }
    }
           

xml檔案配置注入依賴

使用

注意

value隻支援基本類型作為string,自定義類型使用的是标簽

<!--基于XML形式的依賴注入:有參構造-->
<bean id="user" class="org.example.service.User1">
    <!--注入屬性-->
    <constructor-arg name="id" value="1"></constructor-arg>
    <constructor-arg name="name" value="張三"></constructor-arg>
    <constructor-arg name="address" value="address"></constructor-arg>
    <constructor-arg name="passwd" value="passwd"></constructor-arg>
    
    
</bean>
           

app實作

ApplicationContext  context =new ClassPathXmlApplicationContext("application.xml");
User1 user = (User1) context.getBean("user");
System.out.println(user);
           

基于set方法注入依賴

public class User2 {
    private Integer id;
    private String name;
    private String passwd;
    private String address;
    
    public User2() {
        this.id = id;
        this.name = name;
        this.passwd = passwd;
        this.address = address;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPasswd() {
        return passwd;
    }

    public void setPasswd(String passwd) {
        this.passwd = passwd;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "User1{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", passwd='" + passwd + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
           

set方法注入依賴使用注解

xml配置

<!--基于XML形式的依賴注入-->
<bean id = "user2" class="org.example.service.User2">
    <property name="id" value="1"></property>
    <property name="passwd" value="passwd"></property>
    <property name="address" value="address"></property>
    <property name="name" value="張三"></property>
</bean>
           

app實作

ApplicationContext  context =new ClassPathXmlApplicationContext("application.xml");
        User2 user2 = (User2) context.getBean("user2");
        System.out.println(user2);

           

注入資料基本類型,自定義類型 Spring中還支援List,map,set,array等類型的資料注入

自定義類型注入依賴

前提得先注入自定義類型的依賴

<bean id="user" class="org.example.service.User1">
    <!--注入屬性-->
    <constructor-arg name="id" value="1"></constructor-arg>
    <constructor-arg name="name" value="張三"></constructor-arg>
    <constructor-arg name="address" value="address"></constructor-arg>
    <constructor-arg name="passwd" value="passwd"></constructor-arg>
</bean>
           

注入集合類型

<!--注入List類型-->
<property name="list">
    <list>
        <value>12</value>
        <value>13</value>
    </list>
</property>
     <!--注入map類型資料-->
<property name="ms">
    <map>
      <entry key="a" value="1"/>
      <entry key="b" value="2"/>
    </map>
</property>
           
基于注解的注入

在對應需要的依賴上添加注解@Autowired

@Value注入普通的類型屬性

@Resource 注入的是對象類型

@Autowired 注入對象類型

@Service(value = "UserLogin")
public class UserLogin {
    @Autowired
     private LoginText loginText;

     public void login(String name){
         User2 user2 = new User2(1,"zs","passwd","xa");
         loginText.printLogin(user2);
     }
}

@Service(value = "LoginText")
public class LoginText {
    public void printLogin(User2 user2){
        System.out.println(user2.getName()+"歡迎登陸");
    }
}
@Component(value = "user2")
public class User2 {

    private Integer id;
    private String name;
    private String passwd;
    private String address;
    //private Student student;

    private List<String> list;

    public User2(){

    }
    public User2(Integer id, String name, String passwd, String address) {
        this.id = id;
        this.name = name;
        this.passwd = passwd;
        this.address = address;
    }
    //以下是一些get和set方法
    }
           

可以看到user2中(idea) 有一個無參構造,再删除掉無參構造時,有參構造會報出錯誤,而且編譯報錯。

問題

--------------------------問題----------------------------------------------------------------------------

在注解形式中:Spring的反射要求這個bean必須要有一個無參構造器

可能在idea中不支援帶參數的注解注入

通常在javaBean中,一般參數在四個以上的,不推薦使用帶參數的構造函數指派,多使用get/set方法。

------------------------------------------------------------------------------------------------------------

@Resource和@Autowired的差別?

關系:

  1. @Resource和@Autowired都是用來做bean的注入時使用
  2. @Resource和@Autowired有時可以互相替換使用,當都作為bean注入使用時,在接口僅有一個實作類時,兩個注解的修飾效果相同,可以互相替換

不同點:

  1. @Resource是Java自己的注解,@Resource有兩個屬性較為重要 name,type,spring使用name解析為根據bean中的名稱定位,使用type時解析為根據類型定位,如果沒有給定屬性值,Spring的反射機制通過byName來自動注入屬性
  2. @Autowired是spring提供的注解,@Autowired在spring2.5後引入的隻能根據type進行注入,不用name,如果涉及到type無法識别注入對象時(比如,有多個相同類型的類時),隻使用Autowired是無法注入的,需要借助其他注解才能完成注入,比如@Primary @Qualifler,通過它指定哪個是真正需要注入的。@Autowired隻有一個屬性required,預設值為true,為true時,找不到就抛異常,為false時,找不到就指派為null。

依賴解析過程

在spring中的依賴解析過程:

  1. ApplicationContex通過配置的中繼資料來建立和初始化,這些中繼資料描述了所有的bean,中繼資料的資訊可以通過注解,xml或者Java代碼來描述。
  2. 對于每一個bean,他的依賴屬性,構造方法或者是靜态工廠方法等形式來表達,bean被建立好之後這些以來會提供給他。
  3. 每一個屬性或構造方法都要被設定實際定義,或者是對容器的另一個bean(自定義類型)的引用。
  4. 每個屬性或者構造方法上的值的實際定義都會被轉化為目前執行個體bean的實際的值。

在容器建立的過程中,spring容器會驗證每一個bean的配置,在實際建立bean之前,bean的屬性不會被設定,單例和被設定為首先加載的bean會在容器初始化後就建立出來,其他的bean隻會在需要的時候才會建立,建立bean過程可能會引起一系列的bean被建立,

循環依賴

A -->B A依賴B

B -->A 而B又依賴A

解決:

這些類可以通過set注入,避免使用構造方法注入

A B

構造函數構造A ,依賴B 建立順序:B-A

Set方法建立A, 注入B 建立順序:A-B