這裏教你怎麼使用SpringIOC,你還是不懂SpringIOC?什麼是控制反轉?

在這裏插入圖片描述


寫在前面: 最近學習Spring告一段落了,這文章可以簡單的告訴你怎麼使用Spring的IOC控制反轉,以及IOC註解開發。 作者是一個學生沒有能力寫得太深,需要的可以去看看大佬們的手撕Spring源碼
本文將告訴你:
IOC控制反轉是什麼?
如何使用Spring的IOC控制反轉?
XML創建bean的三種形式
IOC控制反轉的註解開發

如果對你有幫助可以點贊支持一下^ _ ^
公衆號:小白編碼

什麼是Spring?

Spring是分層的Java SE/EE應用 full-stack輕量級開源框架,以IoC(Inverse Of Control:反轉控制)和AOP(Aspect Oriented Programming:面向切面編程)爲內核,提供了展現層Spring MVC和持久層Spring JDBC以及業務層事務管理等衆多的企業級應用技術,還能整合開源世界衆多著名的第三方框架和類庫,逐漸成爲使用最多的Java EE企業應用開源框架。

Spring能做什麼?

  • 方便解耦,簡化開發
    通過Spring提供的IoC容器,可以將對象間的依賴關係交由Spring進行控制,避免硬編碼所造成的過度程序耦合。用戶也不必再爲單例模式類、屬性文件解析等這些很底層的需求編寫代碼,可以更專注於上層的應用。
  • AOP編程的支持
    通過Spring的AOP功能,方便進行面向切面的編程,許多不容易用傳統OOP實現的功能可以通過AOP輕鬆應付。
  • 聲明式事務的支持
    可以將我們從單調煩悶的事務管理代碼中解脫出來,通過聲明式方式靈活的進行事務的管理,提高開發效率和質量。
  • 方便程序的測試
    可以用非容器依賴的編程方式進行幾乎所有的測試工作,測試不再是昂貴的操作,而是隨手可做的事情。
  • 方便集成各種優秀框架
    Spring可以降低各種框架的使用難度,提供了對各種優秀框架(Struts、Hibernate、Hessian、Quartz等)的直接支持。
  • 降低JavaEE API的使用難度
    Spring對JavaEE API(如JDBC、JavaMail、遠程調用等)進行了薄薄的封裝層,使這些API的使用難度大爲降低。
  • Java源碼是經典學習範例
    Spring的源代碼設計精妙、結構清晰、匠心獨用,處處體現着大師對Java設計模式靈活運用以及對Java技術的高深造詣。它的源代碼無意是Java技術的最佳實踐的範例。

Spring的體系結構

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-fU2GuDKZ-1590552750288)(C:\Users\JUN\AppData\Roaming\Typora\typora-user-images\image-20200522194850281.png)]

IOC控制反轉是什麼?

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-SEdGrKPh-1590552750291)()]

我們都知道Service層需要Dao層的支持,使用Dao獲取數據處理業務:(這裏需要AccountDao的實例)
在這裏插入圖片描述
上圖使用了工廠獲取實例。 目的就是爲了解耦。

我獲取AccountDao的實例的時候有兩種方法:

  • 第一種:通過new AccountDaoImpl() 實例的方式
  • 第二種:通過工廠模式,獲取AccountDao的實例

那麼問題來了?到底什麼是控制反轉?

通過下圖可知:獲取實例的方式轉變爲從工廠裏獲取而不是直接new一個實例,它將獲取實例的控制權 ,交給了BeanFactory來做,而不是自己直接面對實例資源。讓工廠來面對實例資源。這種控制權發生了轉變。就叫控制反轉。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-DEpPUt7q-1590552750296)()]

那麼如何使用Spring的IOC控制反轉?

開發前準備:

1.導入Maven依賴座標:或者官方下載: http://repo.springsource.org/libs-release-local/org/springframework/spring

<dependency>
            <!--SpringIOC依賴-->
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
</dependency>

依賴的結構圖:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-oVXcKr2F-1590552750299)()]
2.準備好Dao層,以及Service層:

Dao層:(接口自己寫)

public class AccountDaoImpl implements IAccountDao {

    @Override
    public void saveAccount() {
        System.out.println("保存一個賬戶");
    }
}

Service層:(接口自己寫)

public class AccountService implements IAccountService {

    private IAccountDao iAccountDao =  new AccountDaoImpl();//這裏我還是先使用new的方式

    @Override
    public void saveAccount() {
        iAccountDao.saveAccount();
    }
}

Xml配置IOC容器:

<?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
        http://www.springframework.org/schema/beans/spring-beans.xsd">

	<!-- bean標籤:用於配置讓spring創建對象,並且存入ioc容器之中 id屬性:對象的唯一標識。
 	 class屬性:指定要創建對象的全限定類名 -->
    <!--配置AccountService的實現類,放入容器-->
    <bean id="accountService" class="cn.codewhite.service.Impl.AccountService"></bean>
    <!--配置AccountDao的實現類,放入容器-->
    <bean id="accountDao" class="cn.codewhite.dao.Impl.AccountDaoImpl"></bean>

</beans>

測試程序:

public class Clinet {

    public static void main(String[] args) {



        //1.獲取核心容器對象
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        
        //        IAccountService iAccountService = new AccountService();
        //在使用SpringIOC控制反轉前,我們是通過new 的方式獲取Service實例
        // 現在配置了IOC控制反轉,new實例交給了Spring管理,根據配置的id獲取容器裏配置的實現類
        IAccountService accountService = (IAccountService) ac.getBean("accountService");
        
        //        IAccountDao accountDao = new AccountDaoImpl();原先是通過new方式獲取Dao實例
        //通過字節碼文件,獲得運行時類
        IAccountDao accountDao = ac.getBean("accountDao", IAccountDao.class);

        System.out.println(accountService);
        System.out.println(accountDao);
		//測試保存賬戶
        accountService.saveAccount();

    }
}

運行結果:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-2wiM2gRi-1590552750300)()]

結論:通過這個簡單的案例,我們就知道了,可以配置將實現類,配置到Spring的IOC容器中,在需要獲取Dao或者Service等其他實現類的時候,從IOC容器中獲取。而不是直接new的方式獲取實例。實現了控制反轉
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-axbjnWmp-1590552750301)()]

XML創建bean的三種形式:

創建Bean,也就是配置bean之後(放入IOC容器),通過getBean()來獲取實例。

第一種方式:

使用默認構造函數創建。

​ 在spring的配置文件中使用bean標籤,配以id和class屬性之後,且沒有其他屬性和標籤時。
​ 採用的就是默認構造函數創建bean對象,此時如果類中沒有默認構造函數(空參構造器),則對象無法創建。

<bean id="accountService" class="cn.codewhite.service.Impl.AccountServiceImpl"></bean>

AccountService實現類:

public class AccountServiceImpl implements IAccountService {
  	//必須是空參構造器,否則第一種方式無法創建對象
    public AccountServiceImpl() {
        
    }

    @Override
    public void saveAccount() {
        System.out.println("service中的saveAccount()執行了");
    }
}

配置完之後,就可以通過根據配置的id獲取實例。

    	//1.獲取核心容器對象
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.根據id獲取Bean對象,獲取實例
        IAccountService accountService1 = (IAccountService) ac.getBean("accountService");

第二種方式:

使用普通工廠中的方法創建對象(使用某個類中的方法創建對象,並存入spring容器)

<!--配置工廠到容器-->
<bean id="instanceFactory" class="cn.codewhite.factory.InstanceFactory"></bean>
<!--配置accountservice實例,是從工廠的getAccountService裏獲取的實例-->
<bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>

工廠類: (AccountServce實現類看上邊)

/**
 * 模擬一個工廠類(該類可能是存在於jar包中的,我們無法通過修改源碼的方式來提供默認構造函數)
 */
public class InstanceFactory {
	//獲取AccountService的實例	
    public IAccountService getAccountService() {
        return new AccountServiceImpl();
    }
}

測試: 此時的實例service的實例是從配置過的IOC裏的工廠裏獲取的實例。

public class Clinet {

    public static void main(String[] args) {

        //1.獲取核心容器對象
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.根據id獲取Bean對象
        IAccountService accountService = (IAccountService) ac.getBean("accountService");
        accountService.saveAccount();

    }
}

結果:

service中的saveAccount()執行了

第三種方式:

使用工廠中的靜態方法創建對象(使用某個類中的靜態方法創建對象,並存入spring容器)

xml:(配置工廠裏的靜態方法)

    <bean id="accountService" class="cn.codewhite.factory.StaticFactory" factory-method="getAccountService"></bean>

工廠類:

public class StaticFactory {
	//靜態方法
    public static IAccountService getAccountService() {
		//返回AccontService實例
        return new AccountServiceImpl();
    }
}

測試如第二種方式的結果。

bean標籤

作用:用於配置對象讓spring來創建的。

默認情況下它調用的是類中的無參構造函數。如果沒有無參構造函數則不能創建成功。

屬性:

  • id:給對象在容器中提供一個唯一標識用於獲取對象。

  • class:指定類的全限定類名。用於反射創建對象。默認情況下調用無參構造函數。

  • scope:指定對象的作用範圍。常用: singleton :默認值,單例的. prototype :多例的.

  • request :WEB項目中,Spring創建一個Bean的對象,將對象存入到request域中. 作用於web應用的請求範圍
  • session :WEB項目中,Spring創建一個Bean的對象,將對象存入到session域中.作用於web應用的會話範圍
  • global session :WEB項目中,應用在Portlet環境.如果沒有Portlet環境那麼globalSession相當於session. 作用於集羣環境的會話範圍(全局會話範圍),當不是集羣環境時,它就是session
  • init-method:指定類中的初始化方法名稱。

  • destroy-method:指定類中銷燬方法名稱。
    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-csriD7mt-1590552750302)()]

bean依賴注入

**依賴注入:Dependency Injection。(DI)**它是spring框架核心ioc的具體實現。 簡單說就是:(爲指定的類裏面的屬性賦值實例。相當於賦值,)

依賴關係的管理:我們的程序在編寫時,通過控制反轉,把對象的創建交給了spring,但是代碼中不可能出現沒有依賴的情況。ioc解耦只是降低他們的依賴關係,但不會消除。

IOC的作用: 降低程序間的耦合(依賴關係)

例如:我們的業務層仍會調用持久層的方法。 那這種業務層和持久層的依賴關係,在使用spring之後,就讓spring來維護了。 簡單的說,就是坐等**框架把持久層對象傳入業務層,而不用我們自己去獲取。**在當前類需要用到其他類的對象,我們只需要在配置文件中說明

構造函數注入:

使用的標籤:constructor-arg
標籤出現的位置:bean標籤的內部

標籤中的屬性:
type:用於指定要注入的數據的數據類型,該數據類型也是構造函數中某個或某些參數的類型
index:用於指定要注入的數據給構造函數中指定索引位置的參數賦值。索引的位置是從0開始
name: ★用於指定給構造函數中指定名稱的參數賦值 常用的
以上三個用於指定給構造函數中哪個參數賦值
value: 用於提供基本類型和String類型的數據
ref: 用於指定其他的bean類型數據。它指的就是在spring的Ioc核心容器中出現過的bean對象

xml配置:

<!--將Service放入IOC容器,以及配置Service裏的屬性依賴-->
<bean id="accountService" class="cn.codewhite.service.Impl.AccountServiceImpl">
   		 <!--配置Service裏的String name屬性:賦值爲:小白-->
        <constructor-arg name="name" value="小白"></constructor-arg>
        <constructor-arg name="age" value="18"></constructor-arg>
    	<!--這種類型需要引入,所以使用ref引入-->
        <constructor-arg name="birthday" ref="now"></constructor-arg>
</bean>
<bean id="now" class="java.util.Date"></bean>

Service:

public class AccountServiceImpl implements IAccountService {

    private String name;
    private Integer age;
    private Date birthday;

    public AccountServiceImpl(String name, Integer age, Date birthday) {
        this.name = name;
        this.age = age;
        this.birthday = birthday;
    }
}

set方法注入(Bean中必須設有Set方法) ★ (更常用)

涉及的標籤:property
出現的位置:bean標籤的內部

標籤的屬性:
name: 用於指定注入時所調用的set方法名稱
value: 用於提供基本類型和String類型的數據
ref: 用於指定其他的bean類型數據。它指的就是在spring的Ioc核心容器中出現過的bean對象
優勢: 創建對象時沒有明確的限制,可以直接使用默認構造函數
弊端: 如果有某個成員必須有值,則獲取對象是有可能set方法沒有執行。

xml:

<!--將Service放入IOC容器,以及配置Service裏的屬性依賴-->
<bean id="accountService2" class="cn.codewhite.service.Impl.AccountServiceImpl2">
    <!--配置Service裏的String name屬性:通過set方法賦值爲:小白-->
    <property name="name" value="小白"></property>
    <property name="age" value="18"></property>
    <property name="birthday" ref="now"></property>
</bean>
<bean id="now" class="java.util.Date"></bean>

Service: (需要提供set方法)

public class AccountServiceImpl2 implements IAccountService {
	private String name;
	private Integer age;
	private Date birthday;

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

	public void setAge(Integer age) {
	    this.age = age;
	}

	public void setBirthday(Date birthday) {
 	   this.birthday = birthday;
	}
}

複雜類型的注入/集合類型的注入:

用於給List結構集合注入的標籤: listarrayset
用於個Map結構集合注入的標籤: mapprops
結構相同,標籤可以互換

Bean.xml:

<!--將Service放入IOC容器,以及配置Service裏的屬性依賴-->
<bean id="accountService3" class="cn.codewhite.service.Impl.AccountServiceImpl3">
     <!--以下是List結構-->
    <property name="myStrs">
        <array>
            <value>aaa</value>
        </array>
    </property>
    <property name="myList">
        <list>
            <value>AA</value>
        </list>
    </property>
    <property name="mySet">
        <set>
            <value>AAA</value>
        </set>
    </property>
    <property name="myMap">
        <!--以下是Map結構-->
        <map>
            <entry key="keyA" value="AAA"></entry>
            <entry key="keyB" >
                <value>BBB</value>
            </entry>
            <entry key="keyC" value="CCC"></entry>
        </map>
    </property>
    <property name="myProps">
        <props>
            <prop key="PropA">AAA</prop>
        </props>
    </property>
</bean>

Servcie: (set方法我這裏沒寫!需要自己補充,否則無法注入)

public class AccountServiceImpl3 implements IAccountService {

    private String[] myStrs;
    private List<String> myList;
    private Set<String> mySet;
    private Map<String, String> myMap;
    private Properties myProps;
}

IOC控制反轉的註解開發

環境搭建:

Dao層:

AccountDaoImpl:

@Repository("accountDao1")
public class AccountDaoImpl implements IAccountDao {

    @Override
    public void saveAccount() {
        System.out.println("accountDao1中:保存一個賬戶");
    }
}

AccountDaoImpl2:

@Repository("accountDao2")
public class AccountDaoImpl2 implements IAccountDao {

    @Override
    public void saveAccount() {
        System.out.println("accountDao2中:保存一個賬戶");
    }
}

Service:

@Service("accountService")
@Scope("singleton")
public class AccountServiceImpl implements IAccountService {

    //    @Autowired
//    @Qualifier("accountDao1")
    @Resource(name = "accountDao2")
    private IAccountDao iAccountDao;

    @PostConstruct
    public void init() {
        System.out.println("init創建。。。。");
    }

    @PreDestroy
    public void destroy() {
        System.out.println("destroy銷燬。。。。");
    }


    @Override
    public void saveAccount() {
        iAccountDao.saveAccount();
    }
}

綁定使用註解的包:(非常重要)

<?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.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <!--配置cn.codewhite包下所有的註解都能被掃描到-->
    <context:component-scan base-package="cn.codewhite"></context:component-scan>
</beans>

用於創建對象的註解

相當於:<bean id="" class="">

@Component

作用: 把資源讓spring來管理。相當於在xml中配置一個bean。

屬性: value:指定bean的id。 如果不指定value屬性,默認bean的id是當前類的類名。首字母小寫。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-cRVYodN5-1590552750303)()]

@Controller @Service @Repository

他們三個註解都是針對一個的衍生註解,他們的作用及屬性都是一模一樣的 .他們三個是spring框架爲我們提供明確的三層使用的註解,使我們的三層對象更加清晰

@Controller:一般用於表現層的註解。

@Service:一般用於業務層的註解。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-4iJsn4BJ-1590552750303)()]

@Repository:一般用於持久層的註解。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-cfz2zFsF-1590552750304)()]

細節:如果註解中有且只有一個屬性要賦值時,且名稱是value,value在賦值是可以不寫。

用於注入數據(依賴注入)的註解

相當於:<property name="" ref="">
<property name="" value="">

@Autowired

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Y9WfPmY9-1590552750305)(]

作用: 自動按照類型注入。只要容器中有唯一的一個bean對象類型和要注入的變量類型匹配,就可以注入成功
如果ioc容器中沒有任何bean的類型和要注入的變量類型匹配則報錯。
如果Ioc容器中有多個類型匹配時: 出現位置:可以是變量上,也可以是方法上
細節:在使用註解注入時,set方法就不是必須的了,可以省略。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-VZzV8Gjl-1590552750305)()]

@Qualifier

作用:按照類中注入的基礎之上再按照名稱注入。它在給類成員注入時不能單獨使用。必須和@Autowire一起使用;但是給方法參數注入時,可以獨立使用。
屬性:
value:用於指定注入bean的id。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-QDehRpXi-1590552750306)()]

AccountDao1:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-7vsmEoK7-1590552750306)()]
調用Service結果:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-n0ToOng2-1590552750307)()]

給方法傳入:[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Tg3iHo1X-1590552750309)()]

@Resource

作用: 直接按照Bean的id注入。可以獨立使用。它也只能注入其他bean類型。

屬性: name:指定bean的id。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-jdeg0PzU-1590552750310)()]

調用Service測試結果:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-S64q7O2G-1590552750310)()]
以上三個注入都只能注入其他bean類型的數據,而基本類型和String類型無法使用上q述註解實現。
另外,集合類型的注入只能通過XML來實現。

@Value

作用:用於注入基本類型和String類型的數據 ,
屬性:value:用於指定數據的值。它可以使用spring中SpEL(也就是spring的el表達式)
SpEL的寫法:${表達式}

注入配置文件中的key:(需要使用@PropertySource(“classpath:jdbcConfig.properties”))引用
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-9S9LNgPM-1590552750311)()]

配置文件:

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring
jdbc.username=root
jdbc.password=123

用於改變作用範圍的註解

相當於:<bean id="" class="" scope="">

@Scope

作用: 指定bean的作用範圍。

屬性: value:指定範圍的值。

取值:(singleton或prototype 常用)request,session,globalsession

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-7FaDEjrM-1590552750311)()]

測試是否單例:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-mhjncKuA-1590552750312)(C:\Users\JUN\AppData\Roaming\Typora\typora-user-images\image-20200524153228839.png)]

和生命週期相關的註解:(瞭解)

相當於:<bean id="" class="" init-method="" destroy-method="" />

@PostConstruct

作用: 用於指定初始化方法。

@PostConstruct

作用: 用於指定銷燬方法。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-oTirMtjh-1590552750313)()]
測試結果:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-vEduFsA9-1590552750313)()]

寫在後邊:

寫得不好,請見諒,如果需要PDF版的可以找我。

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