Spring學習筆記-01

1. Spring簡介

一句話歸納Spring,Spring是一個開源的、輕量的控制反轉(IOC)面向切面編程(AOP)的框架。html

1.1 Spring、SpringMVC、Spring Boot和Spring Cloud的區別

SpringMVC是在Spring基礎之上結合了MVC三層架構的框架,使用DispatchServlet和視圖解析器作到了更方便的視圖跳轉和控制器調度處理,主要負責處理WEB應用開發。SpringMVC是Spring的一個子模塊。java

SpringBoot則使用了約定大於配置的理念,簡化了Spring配置,專一於微服務的開發。spring

SpringCloud專一於全局微服務的整合、管理。express

2. 控制反轉—IOC

2.1 基本概念

IOC:控制反轉,是一種設計思想。即將對象建立的控制權轉移給了Spring容器編程

假設咱們建立了類A,類A中有個成員變量類B。緩存

  • 傳統開發:咱們須要手動建立類B的對象,將他賦給類A的對象,這叫主動控制對象的建立;
  • IOC:Spring建立好B對象,而後存儲到一個容器裏面,當A對象須要使用B對象時,Spring就從存放對象的那個容器裏面取出A要使用的那個B對象。

使用傳統方式,A對B產生了依賴,也就是A和B之間存在一種耦合關係(假設B的實現有了改動,咱們可能還須要改動A的源碼),而經過IOC的思想,這種依賴關係就被容器取代了,全部對象的建立依賴於容器,在啓動的時候,Spring會讀取配置文件,並將建立的對象先注入容器,以後咱們想要使用直接從容器中取就能夠。架構

DI:依賴注入,能夠理解爲是Spring實現IOC的具體方式。框架

2.2 Spring建立對象方式

class UserService {
    private UserDao dao;
    
    public void setUserDao(UserDao dao) {
        this.userDao = dao;
    }
}

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

2.2.1 構造函數

<bean id="dao" class="com.dong.dao.UserDao">
    <!--使用name定位構造函數-->
    <constructor-arg name="name" value="dongjh"></constructor-arg>
</bean>


<bean id="service" class="com.dong.service.UserService">
    <!--使用type定位構造函數-->
    <constructor-arg type="com.dong.dao.UserDao" ref="dao"></constructor-arg>
</bean>

注:ide

  1. 若是某類有無參構造方法,則默認調用無參構造方法;
  2. 若是某類只有一個構造方法,則默認調用該構造方法。

2.2.2 property(調用set方法建立)

<bean id="dao" class="com.dong.service.UserDao">
    <!--基本類型使用value注入屬性-->
    <property name="name" value="dongjh"></property>
</bean>

<bean id="service" class="com.dong.service.UserService">
    <!--引用類型使用ref屬性-->
    <property name="userDao" ref="dao"></property>
</bean>

注:函數

  1. 使用屬性注入時,容器會默認調用無參構造方法建立實例,因此須要保證要建立的bean有無參的構造方法;
  2. 使用屬性注入等價於調用了setXXX方法,XXX是property標籤中的name,假設上面的代碼的setName方法改爲了setName1,則第一個bean的property需這樣寫:
<property name="name1" value="dongjh"></property>

2.3 Bean的自動裝配(autowiring)

自動裝配即Spring在上下文中自動尋找,自動給bean裝配屬性。

bean的自動裝配主要有三種策略:

  1. byname:把與某bean(A)屬性具備相同名字的其餘bean(B)自動裝配到該bean(A)的屬性中;
  2. byType:把與某bean(A)屬性具備相同類型的其餘bean(B)自動裝配到該bean(A)的屬性中;
  3. constructor:把與某bean(A)的構造器入參具備相同類型的其餘bean(B)自動裝配到該bean(A)的屬性中;

2.3.1 byName

xml配置方式
<!--因爲該bean的id爲userDao,因此會直接自動裝配下面service的userDao屬性中-->
<bean id="userDao" class="com.dong.service.UserDao">
    <property name="name" value="dongjh"></property>
</bean>

<!--該類有setUserDao方法,且設置了屬性自動裝配byName-->
<bean id="service" class="com.dong.service.UserService" autowired="byName">
</bean>

2.3.2 byType

xml配置方式
<!--該bean的類型爲com.dong.service.UserDao,因此會直接自動裝配下面service的userDao屬性中-->
<bean id="userDao111" class="com.dong.service.UserDao">
    <property name="name" value="dongjh"></property>
</bean>

<!--該類一個com.dong.service.UserDao類型的屬性,且設置了屬性自動裝配byType-->
<bean id="service" class="com.dong.service.UserService" autowired="byType">
</bean>

2.4 Spring註解配置

JDK1.5開始支持註解,而Spring是2.5開始支持。

使用Spring註解配置須要先導入約束: 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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <!--注:只會尋找在同一個應用上下文中定義的註解,
    好比將這個聲明放置到DispatcherServlet的配置上下文中,
    它就只會檢查controllers的上下文,而不會檢查services的上下文,
    詳情見https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-annotation-config-->
    <context:annotation-config/>
    <!--指定註解掃描返回-->
    <context:component-scan base-package="com.dong.dao"></context:component-scan>

</beans>

@Autowired

以後在須要自動裝配的屬性或set方法上使用@AutoWired註解,就能夠實現自動裝配。

//詳細的可修飾範圍
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})

注:@Autowired會先使用byType,再按byName的順序自動裝配。

@Autowired有一個經常使用屬性:required,默認值爲true,這個屬性表示修飾的成員變量是否必定須要裝配一個bean:

  • 當required設置爲true時,表示必須有一個能夠自動裝配到該成員變量的bean,不然報錯;
  • 當required設置爲false時,則表示該成員變量能夠爲null;

@Qualifier

當有多個bean知足自動裝配條件時,可使用這個指定裝配的bean,通常配合@Autowired使用。

@Qualifier有個屬性value,給這個value設置想要裝配的bean的id便可。

@Component

修飾類,表示將該類的bean對象注入到Spring容器中。

@Component有個屬性value,表示注入時設置的id。

如:

//等價於<bean id="user" class="User">
@Component(value="user")
public class User {}

@Component有一些衍生註解,如Web開發會按照MVC架構分層,根據不一樣的功能層,衍生出了一些註解:

  • @Repository:針對dao層
  • @Service:針對service層
  • @Controller:針對controller層

這四個註解功能一致,都表示將某類的bean對象注入到Spring容器中。

@Value

給屬性注入值,主要用於給成員變量注入值,簡單的能夠注入基本類型的值,複雜時能夠注入一些操做系統屬性或其餘bean的屬性。

@Scope

用於設置bean的做用域,先介紹最多見的:

  • singleton:全局惟一,單例;
  • prototype:每次注入建立一個新的對象。

註解和配置的區別

xml功能更增強大,方便管理維護。註解適用於簡單的屬性注入。

2.5 JavaConfig配置

Spring 3.0以後開始支持了一種更優的配置方式:基於Java類的配置方式。經過增長@Configuration註解代表這是一個配置類,其底層爲@Component(即該類的bean對象也會被Spring容器接管)。

@Configuration結合@Bean註解能夠實現XML配置實現注入的功能,效果與

<beans>
    <bean>...</bean>
    <bean>...</bean>
</beans>

相似,在使用@Bean註解時,表示將該方法返回的對象加載進Spring容器,在使用@Bean修飾方法時須要注意:

  1. 方法帶返回值,且返回類型爲想要被管理的bean的類型;
  2. 方法名即爲默認的bean name,可以使用@Bean的name屬性自定義;
  3. 方法可帶參數,則此時傳入的參數也必須是被Spring容器管理的bean

以建立UserService和UserDao爲例:

@Configuration
public class UserConfiguration {
    @Bean
    public UserService userService() {
        return new UserService();
    }
    
    @Bean
    public UserDao userDao() {
        return new UserDao();
    }
}

當Spring掃描該類時,會自動將UserConfiguration、UserService、UserDao的bean注入到Spring容器中進行託管。

3. 面向切面編程—AOP

3.1 基本概念

在軟件開發中,咱們須要編寫許多的業務代碼,但還有不少代碼是業務無關的,如

  • 日誌功能
  • 緩存清理
  • 資源回收
  • 權限檢查

爲了實現業務代碼和非業務代碼的分離,Spring利用代理模式抽出了非業務代碼,造成了AOP思想。

AOP全稱Aspect Oriented Programming,意爲面向切面編程,假設業務代碼自己是一條縱向的邏輯流,咱們在其中找幾個點(切點,Pointcut),插入非業務代碼,就像是一個個切面(Aspect),這就是一個簡單的AOP例子。

切面和切點很容易被混淆,嚴格來講,切點是切面的一部分信息,切面主要包含兩部分信息:

  • 切入點;
  • 切入後要執行的動做(Advice)。

而在具體的實現中,Spring爲切面還加了一個信息:在切點的哪一個階段執行?由此衍生出有:

  • 前置通知(Before):在目標方法被調用以前調用通知功能;
  • 後置通知(After):在目標方法被調用以後調用通知功能;
  • 返回通知(After-returning):在目標方法成功執行以後調用通知;
  • 異常通知(After-throwing):在目標方法拋出異常後調用通知;
  • 環繞通知(Around):通知包裹了被通知的方法,在被通知的方法調用以前和調用以後執行自定義的行爲。

關於AOP在Spring的實現有三種方式:

  1. 實現原生的Spring API接口
  2. 自定義切面類
  3. 註解

3.2 實現原生Spring API接口

Spring提供了諸如MethodBeforeAdvice、AfterAdvice、AfterReturningAdvice等原生接口,咱們能夠經過實現這些接口構建切面,如

@Component
public class Log implements MethodBeforeAdvice {
    /**
     * @param method  要執行的目標對象方法
     * @param objects 參數
     * @param o       目標對象
     * @throws Throwable
     */
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println(o.getClass().getName() + "-" + method.getName() + "將要執行");
    }
}

該Log類實現了MethodBeforeAdvice,成爲了一個在方法執行前插入的切面,其次,咱們須要在XML中進行配置,設置切點並創建其和切面的鏈接:

<aop:config>
    <!--切入點:execution表達式,execution(要執行的方法!)-->
    <aop:pointcut id="pointcut" expression="execution(* com.dong.service.UserServiceImpl.*(..))"/>
    <aop:advisor advice-ref="log" pointcut-ref="pointcut"></aop:advisor>
</aop:config>

execution表示在方法執行時觸發;*表示方法返回值能夠是任意類型,com.dong.service.UserServiceImpl.*使用全限定類名和方法名指定要添加前置通知的方法,使用*表示對com.dong.service.UserServiceImpl類下的全部方法都執行切入;(..)表示方法的參數列表,使用(..)表示方法的入參能夠是任意類型。

須要注意,Log必須是一個被Spring容器管理的bean對象,不然Spring將沒法找到該切面的bean,所以使用@Component註解修飾(也可使用XML配置等方式注入)。

3.3 自定義切面類

自定義切面不須要實現Spring的原生接口,首先須要定義一個切面類,並將加載給Spring容器進行託管。

@Component(value="diy")
public class DiyPointCut {
    public void before() {
        System.out.println("方法執行前");
    }

    public void after() {
        System.out.println("方法執行後");
    }
}

與3.2類似,該方式須要經過XML配置設置切點以及其和切面的鏈接。

<aop:config>
    <!--自定義切面,ref:要引用的bean-->
    <aop:aspect ref="diy">
        <aop:pointcut id="pointcut" expression="execution(* com.dong.service.UserServiceImpl.*(..))"/>
        <!--指定advice和切點的關係-->
        <aop:before method="before" pointcut-ref="pointcut"></aop:before>
        <aop:after method="after" pointcut-ref="pointcut"></aop:after>
    </aop:aspect>
</aop:config>

3.4 註解

3.4.1 啓用註解功能

首先,使用註解有兩種方式:

  1. 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"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            https://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/aop
            https://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <!--啓用註解-->
        <context:annotation-config/>、
        <!--註解包掃描-->
        <context:component-scan base-package="com.dong.dao"></context:component-scan>
        <context:component-scan base-package="com.dong.log"></context:component-scan>
        <!--啓用aop註解-->
        <aop:aspectj-autoproxy proxy-target-class="true"/>
    </beans>
  2. 使用@EnableAspectJAutoProxy註解須要被切的類

    @Component(value = "userService")
    @EnableAspectJAutoProxy
    public class UserServiceImpl implements UserService {}

其次,使用註解時,須要注意兩部分:

  • 聲明切面:聲明某類是切面以及切入後實際要執行的動做(Advice);
  • 定位切點:須要經過execution表達式定位須要切入的方法。

下面介紹一下註解的使用方式。

3.4.2 註解使用

只要對一個類使用@Aspect註解修飾,就代表這是一個切面。能夠對其中的方法使用

  • @Before
  • @After
  • @AfterReturning
  • @Around
  • @AfterThrowing

註解指定切入時機,切入點經過execution表達式指定,舉例以下:

@Aspect
@Component
public class CustomPointCut {
    @Before("execution(* com.dong.service.UserServiceImpl.*(..))")
    public void before() {
        System.out.println("aop:方法發生前");
    }
}

4. 參考資料

  1. Spring最新5完整教程
  2. Spring入門(十):Spring AOP使用講解
  3. Spring AOP - 註解方式使用介紹
相關文章
相關標籤/搜索