Spring入門之IOC詳解

西瓜尖尖最好吃發表於2020-10-15

眾所周知,Spring 是目前市面上最主流的一款功能非常強大的框架,中文意思是春天,也讓我們開發人員從JSP、sevlet的高耦合的開發中徹底的解救出來。廢話少說,接下來就進行詳細介紹。

【1】Spring是什麼

Spring是一個開源框架,Spring的核心是控制反轉(IOC)和麵向切面(AOP)。

簡單來說,Spring是一個分層的JavaSE/EE full-stack(一站式) 輕量級開源框架。

輕量級:與EJB對比,依賴資源少,銷燬的資源少。
分層: 一站式,每一個層都提供的解決方案

  • web層:struts2,spring-MVC
  • service層:spring
  • dao層:hibernate,mybatis , jdbcTemplate --> spring-data

【2】Spring優勢

Spring 出現是為了解決JavaEE 實際問題:

  • 方便解耦,簡化開發 (IOC)

    Spring就是一個大工廠(容器),可以將所有物件建立和依賴關係維護,交給Spring管理

    Spring工廠是用於生成bean

  • AOP程式設計的支援

    ​ Spring提供面向切面程式設計,可以方便的實現對程式進行許可權攔截、執行監控等功能

  • 宣告式事務的支援

    ​ 只需要通過配置就可以完成對事務的管理,而無需手動程式設計

  • 方便程式的測試

    ​ Spring對Junit4支援,可以通過註解方便的測試Spring程式

  • 方便整合各種優秀框架

    ​ Spring不排斥各種優秀的開源框架,其內部提供了對各種優秀框架(如:Struts、Hibernate、MyBatis、Quartz等)的直接支援

  • 降低JavaEE API的使用難度

    ​ Spring 對JavaEE開發中非常難用的一些API(JDBC、JavaMail、遠端呼叫等),都提供了封裝,使這些API應用難度大大降低

基於這些特性,我們也會俗稱Spring為開發架構的粘合劑

【3】Spring體系結構

可以訪問官網進行自主學習:https://spring.io/

Spring框架至今已整合了20多個模組,這些模組分佈在以下模組中:

核心容器(Core Container)
資料訪問/整合(Data Access/Integration)層
Web層
AOP(Aspect Oriented Programming)模組
植入(Instrumentation)模組
訊息傳輸(Messaging)
測試(Test)模組

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-WhlRxQDj-1602776666517)(E:/Linux/%E6%A1%86%E6%9E%B6%E9%A2%84%E4%B9%A0%E8%B5%84%E6%96%99/%E6%A1%86%E6%9E%B6/spring-day01/01%E8%AE%B2%E4%B9%89/img/spring-framework.png)]

【4】Spring核心

  • 控制反轉(Inversion of Control,IOC
  • 面向切面程式設計(aspect-oriented programming,AOP

Spring所有功能都依賴於這兩個核心

注意:IOC和AOP並不是一種技術,而是一種思想,這點要清楚。

接下來就來講一下IOC

【5】IOC

IOC介紹:IOC 全稱為 Inversion of Control,翻譯為 “控制反轉”。

控制:控制物件的建立與銷燬

反轉:將物件的控制權(建立與銷燬)交給Spring的IOC容器。

作用:解耦。

由於Java是物件導向開發,開發過程中由N個物件構成,各物件互相合作完成需求。但是此過程存在一個很大的問題,就是各個物件間耦合度太高,導致後期維護困難。

而Spring提供了一種IOC思想,就是引用“第三方”:IOC容器實現具有依賴關係的物件之間的解耦。全部物件的控制權都交給IOC容器,包括建立、銷燬,這樣各物件間就解除了關係,獨立存在。

其他解釋:

什麼是ioc:控制反轉,以前我們要獲取物件,我們自己new.主動獲取。現在有了工廠模式,我們需要獲取物件,是工廠建立,我們被動接受工廠建立的物件.這就是控制反轉.說白了ioc就是工廠模式. 

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

spring框架提供了一個大工廠介面:ApplicationContext(父介面Beanfactroy)

spring-IOC的配置採取的是什麼型別?

spring使用XML格式的檔案儲存配置
	<bean id="唯一標識" 
          class="實現類的全限定名">
	</bean>

spring-IOC是怎麼載入配置檔案的呢?

ApplicationContext工廠使用ClassPathXmlApplicationContext載入配置檔案

基於多個實現類需要進行功能切換,我們只需要修改配置檔案,不需要改java程式碼,所以不需要重新編譯。

【6】IOC的XML開發

使用一個入門小案例來講解

使用ApplicationContext:spring-IOC容器建立物件

1.配置pom.xml,引入spring-context依賴

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.vv.spring</groupId>
  <artifactId>spring-ioc</artifactId>
  <version>1.0-SNAPSHOT</version>

  <name>spring-ioc</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>
    
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <!-- spring版本 -->
    <spring.version>5.1.11.RELEASE</spring.version>
  </properties>

  <dependencies>
      <!--spring ioc依賴-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.6.RELEASE</version>
        </dependency>
       <!-- junit單元測試-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
  </dependencies>
  <build>
        <plugins>
            <!-- java編譯外掛 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.2</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>

        </plugins>
    </build>
</project>

2.編寫配置檔案applicationContext.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--bean
        作用:
            宣告類交給spring容器
        屬性:
            id: 唯一標識
            class:全路徑限定名稱
        細節:
            預設使用無參建構函式例項化-->
<bean id="accountDao" class="com.vv.spring.dao.Impl.AccountDaoImpl"/>
<bean id="accountService" class="com.vv.spring.service.Impl.AccountServiceImpl"/>
</beans>

3.通過spring的工廠獲取物件

public class SpringTest {
    public static void main(String[] args) {
        //1.建立spring的工廠
        ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        //2.通過spring的工廠獲取物件
        Object userService = ac.getBean("userService");
        System.out.println(userService);
    }
}

bean標籤詳解

<?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 definitions here -->
    <!--
        bean標籤:  建立物件,將物件裝配到spring容器(泛指spring工廠類)。
            id: 物件的唯一標識
            class:實現類的路徑,底層就是通過反射機制建立物件的。
            init-method:  物件建立時,要呼叫的初始化方法
            destroy-method:  物件銷燬時,要呼叫的銷燬方法
            scope:用來配置bean的作用域
                singleton: 單例(預設)
                prototype: 多例
            物件的生命週期:
                applicationContext: 工廠初始化時,物件就建立完畢。(單例情況下)
                單例:
                    工廠初始化時建立物件。
                    只要工廠存在,物件就存在。
                    工廠銷燬,物件銷燬。
                多例:
                    什麼時候用,什麼時候建立。
                    工廠存在,物件存在。
                    工廠銷燬,物件不銷燬。物件只能等待垃圾回收機制回收。
    -->
    <bean id="userService" scope="prototype" init-method="init" destroy-method="destroy" class="com.itheima.service.impl.UserServiceImpl"></bean>
</beans>

UserServiceImpl

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

Test

public class SpringTest {
    public static void main(String[] args) {
        System.out.println("************工廠初始化start***********");
        ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        System.out.println("************工廠初始化end*************");
        //通過spring的工廠獲取物件
        Object userService = ac.getBean("userService");
        Object userService2 = ac.getBean("userService");
        System.out.println(userService);
        System.out.println(userService2);
        //銷燬工廠
        System.out.println("*************銷燬工廠*****************");
        ac.close();
    }
}

執行結果

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

建立物件的4種方式

<?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">


    <!--
        spring建立物件的4種方式
            1.預設 無參構造
            2.工廠類的靜態方法
            3.工廠類的動態方法
            4.BeanFactory介面方式,後面ssm整合再去介紹
    -->
    <!-- 預設無參構造方式-->
    <bean id="userService" class="com.vv.service.impl.UserServiceImpl"></bean>

    <!-- 工廠類的靜態方式 -->
    <bean id="userService2" class="com.vv.utils.BeanFactory" factory-method="getUserService"></bean>

    <!-- 工廠類的動態方法-->
    <bean id="factoryBean2" class="com.vv.utils.BeanFactory2"></bean>
    <bean id="userService3" factory-bean="factoryBean2" factory-method="getUserService" ></bean>
</beans>
public class UserServiceImpl implements UserService {
    //無參構造
    public UserServiceImpl() {
        System.out.println("UserServiceImpl的無參構造被呼叫了");
    }
}
public class BeanFactory {
	//工廠類的靜態方法
    public static UserService getUserService(){
        System.out.println("工廠類的靜態方法");
        return new UserServiceImpl();
    }
}
public class BeanFactory2 {
	//工廠類的動態方法
    public  UserService getUserService(){
        System.out.println("工廠類的動態方法");
        return new UserServiceImpl();
    }
}

【7】spring的依賴注入DI

Dependency Injection (依賴注入):

spring建立物件的時候,給物件的屬性賦值,就是依賴注入。

注意是spring建立物件!!!

方式一:有參構造

applicationContext.xml

    <!-- 有參構造方式-->
    <bean id="userService" class="com.vv.service.impl.UserServiceImpl">
        <!--
            給哪個屬性賦什麼值
            確定屬性
                name: 通過屬性名確定屬性
				index: 通過索引來確定屬性
                type: 通過屬性的型別確定屬性
            賦值:
                value:基本型別的賦值
                ref: 引用型型別的賦值
        -->
        <constructor-arg name="age" value="23"  ></constructor-arg>
        <constructor-arg name="game" value="LOL"></constructor-arg>
        <constructor-arg name="username" value="張三"></constructor-arg>
        <constructor-arg name="userDao" ref="userDao"></constructor-arg>
    </bean>

    <!-- 有參構造方式-->
    <bean id="userService2" class="com.vv.service.impl.UserServiceImpl">
        <constructor-arg index="1" value="23"  ></constructor-arg>
        <constructor-arg index="2" value="LOL"></constructor-arg>
        <constructor-arg index="0" value="張四"></constructor-arg>
        <constructor-arg index="3" ref="userDao"></constructor-arg>
    </bean>

    <!-- 有參構造方式-->
    <bean id="userService3" class="com.vv.service.impl.UserServiceImpl">
        <constructor-arg type="java.lang.Integer" value="23"  ></constructor-arg>
        <constructor-arg type="java.lang.String" value="王五"></constructor-arg>
        <constructor-arg type="java.lang.String" value="LOL"></constructor-arg>
        <constructor-arg type="com.vv.dao.UserDao" ref="userDao"></constructor-arg>
    </bean>

UserServiceImpl

public class UserServiceImpl implements UserService {
    private String username;
    private Integer age;
    private String game;
    private UserDao userDao;
    //有參構造
    public UserServiceImpl(String username, Integer age, String game, UserDao userDao) {
        this.username = username;
        this.age = age;
        this.game = game;
        this.userDao = userDao;
    }
    public UserServiceImpl() {
    }
    @Override
    public void login() {
        userDao.findUserByUser();
    }
}

Test

public class SpringTest {
    //這是一個servlet
    public static void main(String[] args) {
        //呼叫service的方法
        ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        UserService userService = (UserService)ac.getBean("userService");
        userService.login();
    }
}

方式二:set方法

applicationContext.xml

 <!-- 屬性的set方法 -->
<bean id="userService" class="com.vv.service.impl.UserServiceImpl">
    <!--
        name:用來確定屬性的。
            name屬性的值是set的方法名去掉set後的駝峰命名名字。
        value:基本型別的屬性賦值
        ref:引用型型別的屬性賦值
    -->
    <property name="username" value="趙六"></property>
    <property name="age" value="25"></property>
    <property name="game" value="王者榮耀"></property>
    <property name="userDao" ref="userDao"></property>
</bean>

UserServiceImpl

public class UserServiceImpl implements UserService {
    private String username;
    private Integer age;
    private String game;
    private UserDao userDao;
    public UserServiceImpl(String username, Integer age, String game, UserDao userDao) {
        this.username = username;
        this.age = age;
        this.game = game;
        this.userDao = userDao;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public void setGame(String game) {
        this.game = game;
    }
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
    public UserServiceImpl() {
    }
    @Override
    public void login() {
        userDao.findUserByUser();
    }
}

Test

public class SpringTest {
    //這是一個servlet
    public static void main(String[] args) {
        //呼叫service的方法
        ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        UserService userService = (UserService)ac.getBean("userService");
        userService.login();
    }
}

使用名稱空間簡寫依賴注入

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-2SY5FQHt-1602776666530)(C:\Users\Ali\AppData\Roaming\Typora\typora-user-images\1602772655928.png)]

注入複雜型別的依賴

UserServiceImpl

public class UserServiceImpl2 implements UserService {
    private String[] gameNames;
    private List<String> dogType;
    private Set<String> catType;
    private Map<String,String> map;
    public void setGameNames(String[] gameNames) {
        this.gameNames = gameNames;
    }
    public void setDogType(List<String> dogType) {
        this.dogType = dogType;
    }
    public void setCatType(Set<String> catType) {
        this.catType = catType;
    }
    public void setMap(Map<String, String> map) {
        this.map = map;
    }
    @Override
    public void login(){
    }
}

applicationContext.xml

<!--複雜型別的注入-->
<bean id="userService" class="com.vv.service.impl.UserServiceImpl2">
    <property name="gameNames">
        <array>
            <value>LOL</value>
            <value>PUBG</value>
            <value>王者榮耀</value>
        </array>
    </property>
    <property name="dogType">
        <list>
            <value>博美</value>
            <value>柴犬</value>
            <value>哈士奇</value>
        </list>
    </property>
    <property name="catType">
        <set>
            <value>英短</value>
            <value>橘貓</value>
            <value>無毛貓</value>
        </set>
    </property>
    <property name="map">
       <map>
           <entry value="科比" key="name"></entry>
       </map>
    </property
</bean>

【8】JdbcTemplate

Spring提供的JdbcTemplatemybatis框架一樣,都是對jdbc的封裝,用於支撐持久層的操作,但企業開發中mybatis應用較廣。

下面就對JdbcTemplate的用法進行簡單介紹。

步驟:

1、搭建資料庫環境;
2、建立工程匯入依賴

<dependencies>
    <!-- spring的jdbcTemplate相關,注意,spring技術的所有版本要保證統一-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.0.6.RELEASE</version>
    </dependency>
    <!-- junit單元測試-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    <!-- spring的ioc相關-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.0.6.RELEASE</version>
    </dependency>
    <!-- mysql驅動-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.38</version>
    </dependency>
</dependencies>

3、建立pojo層
4、實現dao層介面和實現類
5、實現service層介面和實現類
6、通過配置檔案實現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">

    <!--accountService-->
    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
        <!--注入accountDao-->
        <property name="accountDao" ref="accountDao"></property>
    </bean>

    <!--accountDao-->
    <bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
        <!--依賴注入jdbcTemplate-->
        <property name="jdbcTemplate" ref="jdbcTemplate"></property>
    </bean>

    <!--jdbcTemplate物件-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!--依賴注入資料來源-->
        <constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
    </bean>

    <!--建立資料來源物件-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <!-- set方法依賴注入-->
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/itheima115_spring_day02"></property>
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
    </bean>
</beans>

7、建立測試用例完成測試
8、優化-載入外部資料來源

pom.xml

<!--德魯伊連線池-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.0.9</version>
</dependency>
<!-- c3p0-->
<dependency>
    <groupId>c3p0</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.1.2</version>
</dependency>
<!--dbcp-->
<dependency>
    <groupId>commons-dbcp</groupId>
    <artifactId>commons-dbcp</artifactId>
    <version>1.4</version>
</dependency>

applicationContext.xml

<!--德魯伊連線池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <!-- set方法依賴注入-->
    <property name="username" value="${jdbc.username}"></property>
    <property name="password" value="${jdbc.password}"></property>
    <property name="url" value="${jdbc.url}"></property>
    <property name="driverClassName" value="${jdbc.driverClass}"></property>
</bean>

<!--c3p0-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!-- set方法依賴注入-->
        <property name="user" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <property name="jdbcUrl" value="${jdbc.url}"></property>
        <property name="driverClass" value="${jdbc.driverClass}"></property>
    </bean>

<!--dbcp-->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
    <!-- set方法依賴注入-->
    <property name="username" value="${jdbc.username}"></property>
    <property name="password" value="${jdbc.password}"></property>
    <property name="url" value="${jdbc.url}"></property>
    <property name="driverClassName" value="${jdbc.driverClass}"></property>
</bean>

【9】IOC的註解開發

註解開發的優點:提高開發效率。

主要分為下面幾個型別使用:

建立物件:@component; @Controller; @Service; @Repository;
依賴注入:@Autowired; @Qualifier; @Resource(name=beanid); @Value;
生命週期:@Scope; @PostConstruct; @PreDestroy;
純註解開發相關:@Configuration; @ComponentScan; @PropertySource; @Import; @Bean
Spring整合單元測試:@RunWith; @ContextConfiguration;

具體的使用方法還沒來得及總結,有空發出來。

相關文章