1. 程式人生 > 實用技巧 >JAVA框架-Spring01(配置和基礎使用)

JAVA框架-Spring01(配置和基礎使用)

什麼是Spring

Spring是一個開源的,輕量級Java開發框架; 其核心特性是可以用於開發任何 Java 應用程式,Spring 框架的目標是使 JavaEE應用程式的開發變得更加容易,核心概念是IOC和AOP;這也是學習Spring的重點所在;

Spring不是針對某個具體功能,具體層級的框架; 也就是說以前該有的系統分層,結構,設計模式都不需要改變,而是讓Spring加入進來,讓開發變得更簡單; 記住Spring並不想取代某個已存在的框架,反而可以讓各個框架的配合使用難度降低,它就像502膠水,可快速的在系統中整合其他優秀的框架

Spring的優點

  • Spring 對JavaEE中的一些API(JDBC、JavaMail、遠端呼叫等),提供了封裝,使這些API使用難度降低;
  • 一站式框架,可簡單快速的整合其他框架;
  • IOC,利用依賴注入,極大的降低各元件間的耦合,提高整體擴充套件性;
  • AOP(面向切面)程式設計的支援,可以方便的對程式進行許可權攔截,執行監控等;
  • 宣告式事務支援,通過配置就可以完成對事務的管理,無序進行手動程式設計;
  • 容器化,Spring包含並管理應用物件的配置和生命週期,你可以配置每個bean如何被建立以及bean是一個單獨的例項或者每次需要時都生成一個新的例項,以及它們是如何相互關聯的。

什麼是IOC和DI

控制反轉(Inversion of Control,縮寫為IoC),是面向物件程式設計中的一種設計原則,可以用來減低計算機程式碼之間的耦合度。

將原本由程式實現的一部分邏輯反過來交給系統來完成就稱之為控制反轉

其中最常見的方式叫做依賴注入(Dependency Injection,簡稱DI)通過控制反轉,可以說,依賴被注入到物件中。

依賴注入是實現控制反轉的一種手段;

舉個例子:自己搭配組裝機,需要考慮各種部件的相容性,和自己的效能的要求,如CPU,記憶體,顯示卡等等;但有了專門的組裝機伺服器商(IOC容器),你只要告訴他你的基本需求,比如,要一臺吃雞電腦,剩下的就交給服務商去做了;

Spring體系結構

core,提供了框架基礎組成部分,包括IoC和DI;

beans,提供了BeanFactory,是工廠模式的實現,提供普通物件和單例物件的獲取

context,建立在core和bean的基礎上,可將其他庫整合到Spring中

SpEL(spring-expression Language)提供了表示式語言支援,其對JSP中的EL進行了擴充套件

AOP,提供了面向切面程式設計實現

Aspects 模組提供了與 AspectJ 的整合,是一個功能強大且成熟的AOP框架

Instrumentation 用於代理監控JVM執行的JAVA程式,對位元組碼修改以實現AOP

Messaging 模組為 STOMP 提供了支援,主要處理來自 WebSocket 客戶端的 STOMP 資訊

第一次使用Spring的配置

首先我們建立一個maven專案,配置pom.xml

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.2.RELEASE</version>
    </dependency>
</dependencies>

然後我們建立配置檔案applicationContext.xml

實際上我們到這裡就已經配置好了,applicationContext.xml的內容我實際就是告訴spring我們需要得到一個什麼樣的Bean物件(Spring可以理解成能夠自動生成物件的容器)

IOC容器

工廠只負責建立物件,而Spring當然不僅僅是一個物件工廠;其核心是一個物件容器,由於具備控制反轉的能力,所以也叫它IOC容器;

容器可以理解為存放物件的地方,當然不僅僅是儲存,還有物件的管理,包括-建立-銷燬-裝配; 這樣原本程式要做的事情交給了Spring,所以這屬於IOC,稱之為IOC容器;

Spring有兩個容器介面ApplicationContext是BeanFactory的子介面。它們都可以作為Spring的容器;

  • BeanFactory採取的懶載入的方式,在獲取物件時才會例項化
  • ApplicationContext會在工廠初始化時立即例項化物件
  • BeanFactory作為頂級介面主要面向於Spring框架本身,僅提供了基本的容器功能如DI
  • ApplicationContext,是BeanFactory的子介面,意味著功能比BeanFactory更多,諸如國際化,註解配置,XML配置等等,因此ApplicationContext使用更多
    • ApplicationContext的兩個實現類區別:
      • ClassPath表示從類路徑中獲取配置檔案,
      • FileSystem表示從檔案系統獲取配置檔案

Bean的例項化

使用類構造器(預設無參構造方法)

在書寫applicationContext.xml之前,先把具體的介面和實現類寫好

package service;

/**
 * Created by Jeason Luna on 2020/6/23 11:19
 */
public interface PersonService {
    public void sayHi();
}
package service;

/**
 * Created by Jeason Luna on 2020/6/23 11:22
 */
public class PersonServiceImpl implements PersonService {
    @Override
    public void sayHi() {
        System.out.println("Hello  Spring!");
    }
}

加入我們是在MVC設計模式下進行的Spring獲取物件操作,那麼我們應該還有Contrller層。故我們搭建的目錄結構如下:

然後我們在applicationContext.xml新增如下,告訴spring我們要得到的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">

	<!--1.呼叫空參構造器來例項化物件-->
	<bean id="PersonService" class="service.PersonServiceImpl">
</beans>

這樣我們就可以直接得到Bean物件:

package contrller;

import org.springframework.context.support.ClassPathXmlApplicationContext;
import service.PersonService;

/**
 * Created by Jeason Luna on 2020/6/23 11:23
 */
public class PersonContrller {

    private PersonService service;

    public PersonContrller(){
        //載入spring配置檔案
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Object personService = context.getBean("PersonService");
	
        this.service = (PersonService)personService;
    }
    
    public void sayHi(){
        service.sayHi();
    }
	
    public static void main(String[] args) {
        PersonContrller personContrller = new PersonContrller();
        personContrller.sayHi();
    }
}

使用靜態工廠方法

同時我們還可以使用工廠模式獲得Bean物件,首先我們寫好工廠類:

package service;

/**
 * Created by Jeason Luna on 2020/6/23 14:58
 */
public class PsesonFactory {

    public static PersonService getService(){
        System.out.println("靜態工廠!!!");
        return new PersonServiceImpl();
    }

    public  PersonService getService2(){
        System.out.println("工廠物件方法!!!");
        return new PersonServiceImpl();
    }
}

這裡我們配置檔案要替換成:

    <bean id="PersonService" class="service.PsesonFactory" factory-method="getService"></bean>

使用工廠物件方法

xml配置;

<bean id="factory" class="service.PsesonFactory"></bean>
<bean id="PersonService" factory-bean="factory" factory-method="getService2"></bean>

生命週期

Spring提供了非入侵(不強制類繼承或實現)方式的生命週期方法,可以在Bean的初始化以及銷燬時做一些額外的操作。

執行順序及其含義:

1 構造物件
2 設定屬性
3 瞭解Bean在容器中的name
4 瞭解關聯的beanFactory
5 初始化前處理
6 屬性設定完成
7 自定義初始化方法
8 初始化後處理
9 業務方法
10 Bean銷燬方法
11 自定義銷燬方法

依賴注入

依賴指的是當前物件在執行過程中需要使用到的其他引數或物件,Spring可以幫助我們來完成這個依賴關係的建立,說的簡單點就是把你需要引數的給你,而你不用管引數怎麼來的,已達到儘可能的解耦 ;

舉個例子:

Controller 中需要Service物件,Spring可以把Service自動丟到你的Controller中,你不需要關心Service是怎麼來的,用就完了;

要使用依賴注入,必須現在需要依賴的一方(Controller)中為被依賴的一方(Service)定義屬性,用以接收注入;

注入有兩種方法,一種是直接利用有引數的構造方法,另一種是可以依靠無參的構造方法(此方式必須要有無參構造方法),得到了物件後使用set方法來關聯依賴:

構造方法注入

public class User2 {
    private String name;
    private int age;
    private Phone phone;

    public User2(String name, int age, Phone phone) {
        this.name = name;
        this.age = age;
        this.phone = phone;
    }
    @Override
    public String toString() {
        return "User2{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", phone=" + phone +
                '}';
    }
}

xml

<!--user需要的依賴phoneBean-->
<bean id="phone" class="service.Phone"/>

<bean id="user" class="service.User2">
    <!--按引數名稱注入        -->
    <constructor-arg name="name" value="jeason"/>
    <!--按引數位置注入           -->
    <constructor-arg index="1" value="18"/>
    <!--引數型別為其他bean物件時value換成ref        -->
    <constructor-arg name="phone" ref="phone"/>
</bean>

setter方法注入

<!--setter方法注入(屬性注入)  -->
<bean id="user2" class="service.User2">
    <property name="name" value="jeason"/> <!--注入常量值-->
    <property name="age" value="20"/>
    <property name="phone" ref="phone"/> <!--注入其他Bean-->
</bean>

注意:上述配置要求User2必須存在空參構造器

名稱空間

上面通過巢狀標籤constructor的方式注入依賴,在需要注入的依賴較多時導致xml顯得很臃腫,C名稱空間來簡化xml中<constructor-arg>標籤的書寫

使用前需要先在xml頭部進行宣告

xmlns:c="http://www.springframework.org/schema/c"

這樣我們就不用寫一大堆的物件屬性的配置了:

<!--c名稱空間的使用-->
<bean id="user3" class="service.User2" c:name="jeason" c:_1="21" c:phone-ref="phone"></bean>

<!--
    c:name  			指定為name引數賦值
    c:_1    			指定為建構函式的第2個引數賦值
    c:phone-ref   指定為建構函式的phone引數賦值為id為"phone"的Bean
-->

同樣的,我們還有p名稱空間:xmlns:p="http://www.springframework.org/schema/p"

容器型別的注入

<!--    容器資料型別注入-->
<bean id="user100" class="service.User3">
    <!--set注入        -->
    <property name="set">
        <set>
            <value>3</value>
            <value>3</value>
            <value>a</value>
        </set>
    </property>
    
    <!--list注入        -->
    <property name="list">
        <list>
            <value>3</value>
            <value>3</value>
            <value>a</value>
        </list>
    </property>
    
    <!--map注入        -->
    <property name="map">
        <map>
            <entry key="name" value="jerry"/>
            <entry key="age" value="18"/>
            <entry key="sex" value="man"/>
        </map>
    </property>
    
    <!--properties注入        -->
    <property name="properties">
        <props>
            <prop key="jdbc.user">root</prop>
            <prop key="jdbc.password">3692512</prop>
            <prop key="jdbc.driver">com.mysql.jdbc.Driver</prop>
        </props>
    </property>
</bean>

註解方法獲得Bean

註解配置

上面的配置方法還是太麻煩了,需要寫一堆的配置檔案,spring支援註解模式:

通用註解

@Component 用於在Spring中加入Bean

MVC場景下

@Controller 等價於 @Component 標註控制層

@Service 等價於 @Component 標註業務層

@Repository 等價於 @Component 標註資料訪問層(DAO)

在實現上沒有任何不同,僅僅是為了對Bean進行分層是結構更清晰

使用步驟:

1.需要依賴context和aop兩個jar包

2.新增名稱空間(idea下在xml中輸入<context:可自動補全)

<!--指定要掃描註解的包 會自動掃描子包   -->
<context:component-scan base-package="service.demo"/>

3.指定掃描的註解所在的包

4.書寫需要註冊的Bean

import org.springframework.stereotype.Component;

@Component("userService")
public class UserService {
    public String hello(String name){
        return "hello " + name;
    }
}

5.測試

public class Tester {
    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService service = (UserService) context.getBean("userService");
        System.out.println(service.hello("jeason"));
    }
}

註解注入

@Value用於對基本型別屬性進行注入

@Autowired將容器中的其他Bean注入到屬性中

@Qualifier("BeanID") 指定要注入的Bean的ID(和Autowired搭配使用)

準備UserDAO類:

import org.springframework.stereotype.Repository;

@Repository("userDAO")
public class UserDAO {
    public void save(){
        System.out.println("user saved!");
    }
}

UserService類:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component("userService")
public class UserService {

    @Value("hello")//基本型別
    private String info;
    @Value("#{true and false}") //SPEL表示式
    private String info1;
    @Value("${jdbc.user}") //注入屬性配置
    private String info1;
  
		//@Autowired(required = false) //預設為true表示屬性時必須的不能為空
    @Autowired //注入型別匹配的Bean
    //@Qualifier("userDAO") //明確指定需要的BeanID
    private UserDAO userDAO;

//set/get.....
}

測試:

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Tester {
    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService service = (UserService) context.getBean("userService");
        System.out.println(service.getInfo());//普通屬性測試
        service.getUserDAO().save();//物件屬性測試
    }
}

Qualifier和Autowired書寫繁瑣,@Resource可將兩個標籤的功能整合,即注入指定ID的Bean

@Resource標準註解的支援是JSR-250中定義的,所以時使用需要匯入擴充套件包,Maven依賴如下:

<dependency>
    <groupId>javax.annotation</groupId>
    <artifactId>javax.annotation-api</artifactId>
    <version>1.3.2</version>
</dependency>

Resource預設按照使用屬性名稱作為name查詢,查詢失敗則使用型別查詢

可以利用name屬性指定要查詢的id

也可通過type指定型別,當出現相同型別的多個Bean時丟擲異常

import javax.annotation.Resource;

@Component("userService")
public class UserService {
		//@Resource()//預設按照id/name
    //@Resource(name="xx")//指定name
    //@Resource(type = PersonDaoImpl1.class) //指定type
    @Resource(name="xx",type = PersonDaoImpl1.class)//同時指定name和type
    private PersonDao personDao;
}

@Scope用於標註Bean的作用域

@Repository()
@Scope("prototype") //每次get都建立新的 
public class UserDAO {
    public void save(){
        System.out.println("user saved!");
    }
}

XML和註解配合使用

因為註解的表達能力有限,很多時候無法滿足使用需求;我們可以將註解和XML配合使用,讓XML負責管理Bean,註解僅負責依賴注入;當然也需要先宣告context名稱空間;

 <!--註冊Bean-->
<bean id="userDao" class="demo2.UserDao"/>
<bean id="userService" class="demo2.UserService"/>


<!--在已經註冊的Bean中啟用註解注入-->
<context:annotation-config/>

UserService:

import javax.annotation.Resource;
public class UserService {
    @Resource
    private UserDao userDao;

    public UserDao getUserDao() {return userDao;}

    public void setUserDao(UserDao userDao) {this.userDao = userDao;}
}

與註解掃描的不同之處,註解掃描用於將Bean放入容器中同時進行依賴注入,而後者僅僅是在已存在的Bean中進行依賴注入;

多配置檔案的使用

當配置檔案中的內容過多是不便於維護,Spring支援多配置檔案

方法1:

在初始化容器時指定要載入的所有配置檔案

@Test public void test(){
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml","applicationContext2.xml");
    UserService userService = (UserService) context.getBean("userService");
    userService.getUserDao().save();
    this.getClass().getClassLoader().getResource("");
}

方法2:

在配置檔案中使用import來引入其他的配置檔案

<import resource="applicationContext2.xml"/>