天天看點

Spring (Bean, IoC, AOP, SpringMVC)SpringTomcat 和 servlet

Spring - Bean, IoC, AOP, SpringMVC

  • Spring
    • 1. 核心容器
      • 1.1 Spring-beans
        • 1.1.1 Bean 的配置
          • 1.1.1.1 自動裝配
          • 1.1.1.2 JavaConfig
          • 1.1.1.3 xml 配置
        • 1.1.2 bean 的生命周期
          • 1.1.2.1 各種接口方法分類
        • 1.1.3 bean 作用域與線程安全
      • 1.2 IoC(Inverse of Control)
        • 1.2.1 Spring IoC 體系結構
          • 1.2.1.1 BeanFactory
          • 1.2.1.2 BeanDefinition
          • 1.2.1.3 Bean 的解析
        • 1.2.2 Spring IoC 初始化
          • 1.2.2.1 DefaultListableBeanFactory 的整個流程
          • 1.2.2.2 ApplicationContext 的整個流程
        • 1.2.3 Spring IoC容器的依賴注入
        • 1.2.4 Spring IoC容器的lazy-init屬性
    • 2. AOP 和 Instrumentation
      • 2.1 代理模式
        • 2.1.1 靜态代理
        • 2.1.2 動态代理
          • 2.1.2.1 JDK動态代理
          • 2.1.2.2 CGLIB動态代理
        • 2.1.3 靜态、JDK動态、CGLIB動态三者差別
      • 2.2 AOP
      • 2.3 Spring AOP 和 AspectJ的異同
      • 2.4 Spring AOP
        • 2.4.1 術語和概念
        • 2.4.2 Spring AOP的應用
          • 2.4.2.1 pom.xml配置
          • 2.4.2.2 xml配置檔案
          • 2.4.2.3 電腦實作類
          • 2.4.2.4 切面類
          • 2.4.2.5 main方法
        • 2.4.3 Spring AOP的實作
      • 2.5 AspectJ
    • 3. 消息(Messaging)
    • 4. 資料通路/內建(Data Access/ Integration)
    • 5. Web
      • SpringMVC
        • 圖解
        • 展現主要流程的部分源碼
    • 6. Test
  • Tomcat 和 servlet

Spring

Spring架構是一種分層架構,它包含了一系列的功能,大概由20種子產品組成。 這些子產品分為:
核心容器(Core Container),
資料通路/內建(Data Access/Integration),
Web,
AOP,
工具(Instrumentation),
消息(Messaging),
測試用例(Test)
Spring (Bean, IoC, AOP, SpringMVC)SpringTomcat 和 servlet

1. 核心容器

包含子產品spring-core, spring-beans, spring-context, spring-context-support,spring-expression:

spring-core

: Spring架構基本的核心工具類;

spring-beans

: 包含通路配置檔案、建立和管理bean以及進行IoC/DI操作的相關類. BeanFactory;

spring-context

: 建構與Core和Beans之上,繼承了Beans的特性,擴充添加了國際化、時間傳播、資源加載和對Context的建立和支援。ApplicationContext;

spring-expression

: 提供 一個強大的表達式語言用于在運作時查詢和操作對象,該語言支援設定/擷取屬性值,屬性的配置設定,方法的調用,通路數組上下文、容器和索引器、邏輯和算是運算符、命名變量以及從Spring的容器中根據名稱檢索對象。

Spring源碼分析之BeanFactory體系結構

Spring IOC 容器預啟動流程源碼探析

Spring源碼分析之BeanFactoryPostProcessor調用過程詳解

Spring(https://spring.io/) 系列目錄

1.1 Spring-beans

Spring 官方文檔對 bean 的解釋是:

In Spring, the objects that form the backbone of your application and that are managed by the Spring IoC container are called beans. A bean is an object that is instantiated, assembled, and otherwise managed by a Spring IoC container.

Spring

中,構成應用程式主幹并由

Spring IoC

容器管理的對象稱為

bean

bean

是一個由

Spring IoC

容器執行個體化、組裝和管理的對象。

spring bean是什麼(轉)

bean規範如下:
1、所有屬性為private
2、提供預設構造方法
3、提供getter和setter
4、實作serializable接口

1.1.1 Bean 的配置

Bean配置資訊定義了Bean的實作及依賴關系。

三種Bean的配置方案:
1、在

XML

中進行顯示配置;
2、使用

JavaConfig

進行顯示配置;
3、隐式的Bean發現機制和

自動裝配

很多場景下通過元件掃描和自動裝配實作Spring的自動化更為推薦,但是有時候行不通。比如引用第三方元件,沒辦法在它的類上添加

@Component

@Autowired

。是以就需要

JavaConfig

或者

XML

配置
Spring (Bean, IoC, AOP, SpringMVC)SpringTomcat 和 servlet

Spring Bean詳細講解 什麼是Bean?

spring中bean配置和bean注入

1.1.1.1 自動裝配

一般使用

@Autowired

注解自動裝配Bean,要将類表示成可用于

@Autowired

注解自動裝配的Bean類;

采用一下注解可實作:

@Component

:通用的注解,可标注任意類為 Spring 的Bean。如果一個Bean不知道屬于哪一個層(邏輯上的分層),可以該注解。

@Repository

:持久層(

Dao

層)名主要用于資料庫相關操作;

@Service

:對應服務層,主要設計一些複雜的邏輯(一般會調用Dao層);

@Controller

:對應

Spring MVC

控制層,主要使用者接受使用者請求并調用

Service

層傳回資料給前端頁面。

<context:component-scan>

:掃描指定包,如果發現有指定注解,那麼該類将由Spring進行管理。
1.1.1.2 JavaConfig
當需要将第三方庫中的元件裝配到應用中時,是沒有辦法在他的類上添加

@Component

或者

@Autowired

的,是以可以采用

JavaConfig

xml

,先來看

JavaConfig

配置。
一般遵循兩個原則:
1、

JavaConfig

是配置相關代碼,不含任何邏輯代碼;
2、通常會将

JavaConfig

放到單獨的包中。
2個關鍵注解:
1、

@Configuration

:用于定義配置類,可替換xml配置檔案,被注解的類内部包含有一個或多個被

@Bean

注解的方法,這些方法将會被

AnnotationConfigApplicationContext

AnnotationConfigWebApplicationContext

類進行掃描,并用于建構

bean

定義,初始化

Spring

容器。
2、

@Bean

:用于告訴方法,産生一個

Bean

對象,然後這個

Bean

對象交給

Spring

管理。(和

xml

配置中的

bean

标簽的作用是一樣的)
public class SgtPeppers implements CompactDisc {
    private String title = "White Horse";
    private String artist = "Talyor Swift";
    @Override
    public void play() {
        System.out.println("Playing " + title + " by " + artist);
    }
}
@Configuration // 這是一個配置類
public class CDPlayerConfig {
    @Bean // 下面的方法會産生一個Bean對象
    public CompactDisc sgtPeppers(){
        return new SgtPeppers();
    }
    @Bean // 參數注入
    public CDPlayer cdplayer(CompactDisc compactDisc){
        return new CDPlayer(compactDisc);
    }
    @Bean // 無參數注入
    public CDPlayer cdplayer(){
        return new CDPlayer(sgtPeppers());
    }
}
           

<context:component-scan>

:掃描指定包,如果發現有指定注解,那麼該類将由Spring進行管理。

spring使用JavaConfig進行配置

1.1.1.3 xml 配置
方式:
構造器注入
set方式注入
擴充方式注入

@Component

@Bean

的差別:
1、都是使用注解定義

Bean

的方式
2、一個應用于類,一個應用于方法
3、

@Component

用于自動檢測和使用類路徑掃描自動配置

Bean

@Bean

用于顯式聲明單個

Bean

,而不是讓

Spring

自動執行它。
4、

@Bean

可以對第三方的類進行配置;

@Component

不能對第三方的類進行自動配置。

1.1.2 bean 的生命周期

Spring (Bean, IoC, AOP, SpringMVC)SpringTomcat 和 servlet

生命周期:

對象建立:
1、從

Bean

的配置中擷取

BeanDefinition

,進行執行個體化;
2、設定

Bean

的屬性;
3、

Bean

級生命周期接口(

BeanNameAware

,

BeanFactoryAware

,

ApplicationContextAware

)的方法;
4、

BeanPostProcessor

postProcessorBeforeInitialization()

5、

InitializingBean

接口的

afterPropertiesSet()

6、自定義初始化方法

init-method

或者

@PostConstruct

标注的方法;
7、

BeanPostProcessor

postProcessorAfterInitialization()

8、

Bean

建立完畢。
Bean的銷毀:
1、

DiposibleBean

接口的

destory()

2、 自定義的

destory

方法或者

@PreDestory

标注的方法。

Spring中Bean的生命周期是怎樣的? - MOBIN-F的回答 - 知乎

Spring Bean的生命周期(執行個體講解)

1.1.2.1 各種接口方法分類
Bean的完整生命周期會經曆各種方法調用,這些方法可以劃分為以下幾類:
1、Bean自身的方法:這個包括了Bean本身調用的方法(如:構造方法,set方法)和通過配置檔案中

<bean>

init-method

destroy-method

指定的方法
2、Bean級生命周期接口方法:這個包括了

BeanNameAware

BeanFactoryAware

InitializingBean

DiposableBean

這些接口的方法
3、容器級生命周期接口方法:這個包括了

BeanPostProcessor

InstantiationAwareBeanPostProcessor

這兩個接口實作,一般稱它們的實作類為“後處理器”。
4、工廠後處理器接口方法:這個包括了

AspectJWeavingEnabler

,

ConfigurationClassPostProcessor

,

CustomAutowireConfigurer

等等非常有用的工廠後處理器接口的方法。工廠後處理器也是容器級的。在應用上下文裝配配置檔案之後立即調用。

BeanNameAware

接口是為了讓自身Bean能夠感覺到,并可以擷取到自身在

Spring

容器中的

id

屬性。

同理,其他的

Aware

接口也是為了能夠感覺到自身的一些屬性。比如實作了

ApplicationContextAware

接口的類,能夠擷取到

ApplicationContext

,實作了

BeanFactoryAware

接口的類,能夠擷取到

BeanFactory

對象。

Spring中的…Aware接口

1.1.3 bean 作用域與線程安全

Spring

容器中的

Bean

是否線程安全,容器本身并沒有提供

Bean

的線程安全政策,是以可以說

Spring

容器中的

Bean

本身不具備線程安全的特性,但是具體還是要結合具體

scope

Bean

去研究。
bean 作用域
1、 singleton:單例, 預設作用域。
2、 prototype:原型,每次建立一個新對象。
3、request:請求,每次Http請求建立一個新對象,适用于WebApplicationContext環境下。
4、session:會話,同一個會話共享一個執行個體,不同會話使用不用的執行個體。
5、global-session:全局會話,所有會話共享一個執行個體。
Spring (Bean, IoC, AOP, SpringMVC)SpringTomcat 和 servlet

bean對象是預設單例的,那麼Spring中單例Bean的線程安全問題如何處理?

若每個線程中對全局變量、靜态變量隻有讀操作,而無寫操作,一般來說,這個全局變量是線程安全的;若有多個線程同時執行寫操作,一般都需要考慮線程同步,否則就可能影響線程安全。
  1. Bean對象中盡量不使用可變的成員變量;
  2. 類中定義一個ThreadLocal成員變量

Spring 單例Bean和Java 單例模式的差別?

  1. Spring的的單例是基于BeanFactory也就是spring容器,單例Bean在此Spring容器内是單個的;
  2. Java的單例是基于JVM,每個JVM内一個單例。

Spring中的Bean是線程安全的嗎?

1.2 IoC(Inverse of Control)

控制反轉 (Inverse of Control, IoC):
将建立對象的權利交給

IoC

容器。
那麼必然的我們需要建立一個容器,同時需要一種描述來讓容器知道需要建立的對象與對象的關系。這個描述最具體表現就是我們可配置的檔案(見上一節Bean的配置)。
依賴注入(Dependency Injection, DI):
就是指對象是被動接受依賴類而不是自己主動去找,換句話說就是指對象不是從容器中查找它依賴的類,而是在容器執行個體化對象的時候主動将它依賴的類注入給它。
Spring (Bean, IoC, AOP, SpringMVC)SpringTomcat 和 servlet
為了不重複造輪子,一些基礎知識可以先閱讀一下以下三篇文章:IoC : 工廠模式 + 反射 + 配置檔案讀取、Web項目使用IoC的優勢、IoC的依賴倒置。
IoC :
依賴倒置原則( 六大設計原則之一)
工廠模式
配置檔案讀取
反射
總結來說 IoC 就是:基于依賴倒置原則,結合工廠模式,從配置檔案中讀取

Bean

配置資訊并通過反射建構Bean;在之後對

Bean

進行管理。(自己總結的!!!)

将建立對象的權利從程式猿手中拿走,并遞給了

IoC Container

以上隻是對IoC的大緻思想進行了總結,具體Spring IoC實作更加複雜。

1.2.1 Spring IoC 體系結構

  1. Bean

    是一個由

    Spring IoC

    容器執行個體化、組裝和管理的對象。
  2. Spring

    中,

    Bean

    是使用

    BeanDefinition

    描述的。

那麼

BeanDefinition

從加載、解析、處理、注冊到BeanFactory的過程是怎樣的呢?

注冊:通過

IoC

容器内部維護的一個

Map

來儲存得到的

BeanDefinition

的過程;
1.2.1.1 BeanFactory

Spring Bean

的建立是依靠典型的工廠模式,這一系列的

Bean

工廠(也即

IoC

容器)為開發者管理對象間的依賴關系提供了很多便利和基礎服務,在

Spring

中有許多的

IoC

容器的實作供使用者選擇和使用。

BeanFactory

作為最頂層的一個接口類,它定義了

IoC

容器的基本功能規範:

Spring (Bean, IoC, AOP, SpringMVC)SpringTomcat 和 servlet
public interface BeanFactory {    
	//對FactoryBean的轉義定義,因為如果使用bean的名字檢索FactoryBean得到的對象是工廠生成的對象,    
	//如果需要得到工廠本身,需要轉義           
	String FACTORY_BEAN_PREFIX = "&"; 
	//根據bean的名字,擷取在IOC容器中得到bean執行個體    
	Object getBean(String name) throws BeansException;    
	//根據bean的名字和Class類型來得到bean執行個體,增加了類型安全驗證機制。    
	Object getBean(String name, Class requiredType) throws BeansException;    
	//提供對bean的檢索,看看是否在IOC容器有這個名字的bean    
	boolean containsBean(String name);    
	//根據bean名字得到bean執行個體,并同時判斷這個bean是不是單例    
	boolean isSingleton(String name) throws NoSuchBeanDefinitionException;    
	//得到bean執行個體的Class類型    
	Class getType(String name) throws NoSuchBeanDefinitionException;    
	//得到bean的别名,如果根據别名檢索,那麼其原名也會被檢索出來    
	String[] getAliases(String name);    
}
           

BeanFactory

有三個子接口:

ListableBeanFactory

:可清單的工廠接口(表示Bean的集合)

HierarchicalBeanFactory

:表示Bean之間的繼承關系的接口(表示Bean之間的關系)

AutowireCapableBeanFactory

:Bean的自動裝配規則(表示Bean行為)

BeanFactory

之類的接口隻對IOC容器的基本行為作了定義,根本不關心Bean是如何加載的。正如我們隻關心工廠裡得到什麼的産品對象,至于工廠是怎麼生産這些對象的,這個基本的接口不關心。而要知道工廠是如何産生對象的,我們需要看具體的IoC容器實作:

IoC容器實作:
XmlBeanFactory
ClasspathXmlApplicationContext
ApplicationContext
… (還有很多)
1.2.1.2 BeanDefinition

Spring IoC

容器管理了我們定義的各種

Bean

對象及其互相的關系,

Bean

對象在

Spring

實作中是以

BeanDefinition

來描述的。

Spring (Bean, IoC, AOP, SpringMVC)SpringTomcat 和 servlet
/** bean 的名字為鍵,BeanDefinition為值,初始容量為256 */
private final Map<String, BeanDefinition> beanDefinitionMap 
    			= new ConcurrentHashMap<String, BeanDefinition>(256);
           

BeanDefinition

存儲在一個

Map

對象中,如果使用的是

DefaultListableBeanFactory

的話,它就存在一個

ConcurrentHashMap

對象中。

現在将

BeanDefinition

是如何一步步走到這個Map對象中的。

1.2.1.3 Bean 的解析

Bean

的解析過程非常複雜。

Bean

的解析主要就是對

Spring

配置檔案的解析。這個解析過程主要通過下圖中的類完成:

Spring (Bean, IoC, AOP, SpringMVC)SpringTomcat 和 servlet

1.2.2 Spring IoC 初始化

IoC

容器的初始化包括

BeanDefinition

Resource

定位、載入和注冊這三個基本的過程。我們以

ApplicationContext

為例講解,

ApplicationContext

系列容器也許是我們最熟悉的,因為web項目中使用的

XmlWebApplicationContext

就屬于這個繼承體系。

BeanDefinition

生成過程最簡化:
1、把

XML

檔案加載到記憶體中以

Document

對象的形式存在;
2、完成

Document

解析和處理的任務,擷取到

BeanDefinition

3、建構對象,并且存放在

BeanFactory

(如:

DefaultListableBeanFactory

)的

ConcurrentHashMap

中。
1.2.2.1 DefaultListableBeanFactory 的整個流程
// 根據Xml配置檔案建立Resource資源對象,該對象中包含了BeanDefinition的資訊
 ClassPathResource resource =new ClassPathResource("application-context.xml");
// 建立DefaultListableBeanFactory
 DefaultListableBeanFactory factory =new DefaultListableBeanFactory();
// 建立XmlBeanDefinitionReader讀取器,用于載入BeanDefinition。
// 之是以需要BeanFactory作為參數,是因為會将讀取的資訊回調配置給factory
 XmlBeanDefinitionReader reader =new XmlBeanDefinitionReader(factory);
// XmlBeanDefinitionReader執行載入BeanDefinition的方法,最後會完成Bean的載入和注冊。
// 完成後Bean就成功的放置到IOC容器當中,以後我們就可以從中取得Bean來使用
 reader.loadBeanDefinitions(resource);
           
1.2.2.2 ApplicationContext 的整個流程
  1. Bean

    定義資源的載入是從

    refresh()

    函數開始,也就說初始化的入口就是

    refresh()

    ;
  2. Bean

    的配置載入到

    IoC

    的方法是

    loadBeanDefinition

    1. ResourceLoader

      來完成資源檔案位置的定位( 從類路徑,檔案系統, URL 等方式來定為資源位置 );
    2. 定位到

      Bean

      配置檔案後,将其抽象成

      Resource

      來被

      IoC

      容器處理;
    3. 容器通過

      BeanDefinitionReader

      來完成

      Bean

      配置資訊的解析和

      Bean

      的資訊注冊;實際的處理過程是委托給

      BeanDefinitionParserDelegate

      來完成,進而得到

      Bean

      的定義資訊,這些資訊在

      Spring

      中使用

      BeanDefinition

      對象來表示;
    4. 容器解析得到

      BeanDefinition

      以後,需要把它在

      IoC

      容器中注冊,這由

      IoC

      實作

      BeanDefinitionRegistry

      接口來實作。
  3. 通過

    BeanFactory

    ApplicationContext

    來享受到

    Spring IoC

    的服務。
差別

Beanfactory

Factory Bean

  1. 其中

    BeanFactory

    指的是

    IoC

    容器的程式設計抽象,比如

    ApplicationContext

    XmlBeanFactory

    等,這些都是

    IoC

    容器的具體表現,需要使用什麼樣的容器由客戶決定,但

    Spring

    為我們提供了豐富的選擇;
  2. Factory Bean

    隻是一個可以在

    IoC

    而容器中被管理的一個

    Bean

BeanDefinition從加載、解析、處理、注冊到BeanFactory的過程

bean建立過程與九次beanPostProcessor調用時機(原圖位址)

Spring IoC原了解讀

Spring (Bean, IoC, AOP, SpringMVC)SpringTomcat 和 servlet

1.2.3 Spring IoC容器的依賴注入

1.2.4 Spring IoC容器的lazy-init屬性

2. AOP 和 Instrumentation

包含子產品spring-aop, spring-aspects, spring-instrument, spring-instrument-tomcat:

spring-aop

: 提供了一個AOP聯盟标準的 面向切面程式設計的實作,它允許你定義方法攔截器與切入點,進而将邏輯代碼與實作函數進行分離。

spring-aspects

: 提供了與AspectJ的內建

spring-instrument

: 提供了類工具的支援與classloader的實作,以便在特定的應用服務上使用。

spring-instrument-tomcat

: 包含了spring對于Tomcat的代理

2.1 代理模式

代理模式給某一個對象提供一個代理對象,并由代理對象控制對原對象的引用。通俗的來講代理模式就是我們生活中常見的中介。

Spring (Bean, IoC, AOP, SpringMVC)SpringTomcat 和 servlet
作用:
中介隔離作用: 在某些情況下,一個客戶類不想或者不能直接引用一個委托對象,而代理類對象可以在客戶類和委托對象之間起到中介的作用,其特征是代理類和委托類實作相同的接口。
開閉原則,增加功能: 代理類除了是客戶類和委托類的中介之外,我們還可以通過給代理類 增加額外的功能來擴充被代理類的功能,這樣做我們隻需要修改代理類而不需要再修改委托類,符合代碼設計的開閉原則。

設計模式—代理模式

2.1.1 靜态代理

Spring (Bean, IoC, AOP, SpringMVC)SpringTomcat 和 servlet
package ProxyTest.StaticProxy;

public interface KindWomen {
    void makeEyesWithMan();
}
           
package ProxyTest.StaticProxy;

public class JLPan implements KindWomen {
    @Override
    public void makeEyesWithMan() {
        System.out.println(">>>>>>before method.invoke(), 這裡代表代理對象在主業務邏輯方法執行前織入的代碼");
        kindWomen.makeEyesWithMan();
        System.out.println(">>>>>>>after method.invoke(),這裡代表代理對象在主業務邏輯方法執行後織入的代碼");
    }
}
           
package ProxyTest.StaticProxy;
// 代理類
public class WangPo implements KindWomen {
    private KindWomen kindWomen;        // 被代理對象
    public WangPo()
    {
        this.kindWomen = new JLPan();
    }
    public WangPo(KindWomen kindWomen)
    {
        this.kindWomen = kindWomen;
    }
    @Override
    public void makeEyesWithMan() {
        kindWomen.makeEyesWithMan();
    }
}
           
package ProxyTest.StaticProxy;

public class DaGuanRen {
    public static void main(String args[])
    {
        WangPo wangPo = new WangPo(new JLPan());
        wangPo.makeEyesWithMan();
    }
}
           
優點:
代理使用戶端不需要知道實作類是什麼,怎麼做的,而用戶端隻需知道代理即可( 解耦合),對于如上的用戶端代碼,
缺點:
1)代理類和被代理類實作了相同的接口,代理類通過實作被代理類中相同的方法。這樣就出現了大量的 代碼備援。如果接口增加一個方法,除了所有實作類需要實作這個方法外,所有代理類也需要實作此方法。增加了代碼維護的複雜度。
2) 代理對象隻服務于一種類型的對象,如果要服務多類型的對象。勢必要為每一種對象都進行代理,靜态代理在程式規模稍大時就無法勝任了。以上的代碼是隻為

JLPan

類的通路提供了代理,但是如果還要為其他類如

WuSong

類提供代理的話,就需要我們再次添加

WuSong

的代理類。

2.1.2 動态代理

根據對靜态代理的介紹,你會發現每個代理類隻能為一個接口服務,這樣程式開發中必然會産生許多的代理類,這樣會導緻代碼備援。是以我們就會想辦法可以通過一個代理類完成全部的代理功能,那麼我們就需要用動态代理。

Java動态代理又被分為:
JDK動态代理;
CGLIB動态代理。
2.1.2.1 JDK動态代理

動态代理是

代理模式

Java反射技術

結合的産物。

當我們得到一個對象,想動态的為其一些方法每次被調用前後追加一些操作時,我們将會用到

java動态代理

Java中動态代理的實作,關鍵就是這兩個東西:
Proxy
InvocationHandler

下面簡單說明一下Java如何實作動态代理的。

Spring (Bean, IoC, AOP, SpringMVC)SpringTomcat 和 servlet
package ProxyTest.JDKDynamicProxy;

public interface JDKKindWomen {
    void makeEyesWithMan();
}
           
package ProxyTest.JDKDynamicProxy;

public class JDKJLPan implements JDKKindWomen{
    @Override
    public void makeEyesWithMan() {
        System.out.println("小潘潘在抛媚眼了。。。");
    }
}
           
package ProxyTest.JDKDynamicProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class JDKWangPoHandler  implements InvocationHandler {
    private JDKKindWomen kindWomen;
    public  JDKWangPoHandler(){
        kindWomen = new JDKJLPan();
    }
    public  JDKWangPoHandler(JDKKindWomen kindWomen){
        this.kindWomen = kindWomen;
    }
    /**
     *  這個方法不會被我們顯示的去調用 
     *  
     * 第一個參數就是 代理者,如果你想對代理者做一些操作可以使用這個參數;
     * 第二個就是 被執行的方法,
     * 第三個是 執行該方法所需的參數。
     * */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(">>>>>>before method.invoke(), 這裡代表代理對象在主業務邏輯方法執行前織入的代碼");
        method.invoke(kindWomen, args);
        System.out.println(">>>>>>>after method.invoke(),這裡代表代理對象在主業務邏輯方法執行後織入的代碼");
        return null;
    }
    // 自定義一個建構代理對象的方法
    public static JDKKindWomen createProxy()
    {
        JDKJLPan jlPan = new JDKJLPan();
        JDKWangPoHandler wangPoHandler = new JDKWangPoHandler(jlPan);
        return (JDKKindWomen) Proxy.newProxyInstance(
                JDKJLPan.class.getClassLoader(), // jlPan.getClass().getClassLoader(),//
                JDKJLPan.class.getInterfaces(), //jlPan.getClass().getInterfaces(),
                wangPoHandler
        );
    }
}
           
package ProxyTest.JDKDynamicProxy;

public class JDKDaGuanRen {
    public static void main(String args[])
    {
        JDKKindWomen kindWomen = JDKWangPoHandler.createProxy();
        kindWomen.makeEyesWithMan();
    }
}
           
2.1.2.2 CGLIB動态代理
什麼是 CGLIB:

CGLib

是一個強大的、高性能的代碼生成庫,它可以在運作期擴充Java類與實作Java接口。

Hibernate

支援它來實作

PO(Persistent Object 持久化對象)

位元組碼的動态生成。

其被廣泛應用于

AOP

架構中,用以提供方法攔截操作。例如

Spring AOP

為他們提供方法的

interception

(攔截)。

CGLib

采用底層的位元組碼技術

ASM

, 可以為一個類建立子類, 在子類中采用方法攔截的技術攔截所有父類方法的調用, 并織入橫切邏輯。換句話說,

CGLIB

通過“繼承”可以繼承父類所有的公開方法,然後可以重寫這些方法,在重寫時對這些方法增強,這就是

CGLIB

的思想。
根據裡氏代換原則(

LSP

):父類需要出現的地方,子類可以出現,是以

CGLib

實作的代理也是可以被正常使用。
Spring (Bean, IoC, AOP, SpringMVC)SpringTomcat 和 servlet

pom導入包:

<dependencies>
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.3.0</version>
    </dependency>
</dependencies>

           
package ProxyTest.CglibDynamicProxy;

public class CglibJLPan {
    public void makeEyesWithMan() {
        System.out.println("小潘潘在抛媚眼了。。。");
    }
}
           
package ProxyTest.CglibDynamicProxy;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CglibWangPoHandler implements MethodInterceptor {
    /**
     *
     * @param o cglib生成的代理對象
     * @param method 被代理對象的方法
     * @param objects     傳入方法的參數
     * @param methodProxy 代理的方法
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println(">>>>>>before method.invoke(), 這裡代表代理對象在主業務邏輯方法執行前織入的代碼");
        Object obj = methodProxy.invokeSuper(o, objects);
        System.out.println(">>>>>>>after method.invoke(),這裡代表代理對象在主業務邏輯方法執行後織入的代碼");
        return obj;
    }
	// 自定義一個建構代理對象的方法
    public static CglibJLPan createProxy()
    {
        Enhancer enhancer = new Enhancer();             // 通過CGLIB動态代理擷取代理對象的過程
        enhancer.setSuperclass(CglibJLPan.class);       // 設定需要處理的對象父類
        enhancer.setCallback(new CglibWangPoHandler()); // 設定enhancer回調對象
        return (CglibJLPan) enhancer.create();          // 建立并且傳回代理對象
    }
}
           
package ProxyTest.CglibDynamicProxy;

public class CglibDaGuanRen {
    public static void main(String args[]){
        // 建立代理對象(這個方法是自己實作的)
        CglibJLPan jlPan = CglibWangPoHandler.createProxy();
        jlPan.makeEyesWithMan();
    }
}

           

2.1.3 靜态、JDK動态、CGLIB動态三者差別

方法 缺陷 優勢
靜态代理 代碼備援 中介隔離作用; 便于增加功能
JDK動态代理 需要接口 解決備援問題 ,不依賴第三方庫
CGLIB動态代理 運作時載入,使用第三方庫 不需要接口
Spring (Bean, IoC, AOP, SpringMVC)SpringTomcat 和 servlet
Spring (Bean, IoC, AOP, SpringMVC)SpringTomcat 和 servlet
Spring (Bean, IoC, AOP, SpringMVC)SpringTomcat 和 servlet

2.2 AOP

AOP

Aspect Oriented Programming

)稱為面向切面程式設計,在程式開發中主要用來解決一些系統層面上的問題,比如日志,事務,權限等待 等等。

  • AOP

    可以說是

    OOP

    Object Oriented Programming

    ,面向對象程式設計)的補充和完善。

    OOP

    引入封裝、繼承、多态等概念來建立一種對象層次結構,用于模拟公共行為的一個集合。不過

    OOP

    允許開發者定義縱向的關系,但并不适合定義橫向的關系,例如日志功能。日志代碼往往橫向地散布在所有對象層次中,而與它對應的對象的核心功能毫無關系對于其他類型的代碼,如安全性、異常處理和透明的持續性也都是如此,這種散布在各處的無關的代碼被稱為橫切(

    cross cutting

    ),在

    OOP

    設計中,它導緻了大量代碼的重複,而不利于各個子產品的重用。
  • AOP

    OOP

    一樣,隻是一種程式設計範式,

    AOP

    并沒有規定說,實作

    AOP

    協定的代碼,要用什麼方式去實作。

    spring AOP 及實作方式

    Comparing Spring AOP and AspectJ

    Spring (Bean, IoC, AOP, SpringMVC)SpringTomcat 和 servlet

Spring AOP

是AOP的一種實作,并且基于動态代理:
1、如果要代理的對象,實作了某個接口,那麼

Spring AOP

會使用

JDK Proxy

,去建立代理對象;
2、而對于沒有實作接口的對象,就無法使用

JDK Proxy

去進行代理了(在上面一節有提到),這時候

Spring AOP

會使用

CGLIB

,生成一個被代理對象的子類,來作為代理。
3、但是不是所有

AOP

的實作都是在 運作時進行織入的,因為這樣效率太低了,而且隻能針對方法進行

AOP

,無法針對構造函數、字段進行

AOP

。這種情況就引入了

AspectJ

,可以在 編譯成

class

時就織入,比如,當然

AspectJ

還提供了後編譯器織入和類加載期織入。
靜态代理(代碼備援) – JDK動态代理(需要接口) – CGLIB動态代理(運作時載入) – AspectJ(編譯時載入的)
Spring (Bean, IoC, AOP, SpringMVC)SpringTomcat 和 servlet
Tip : AOP是一種思想,Spring AOP 和 AspectJ是對應的不同實作。

2.3 Spring AOP 和 AspectJ的異同

特點 Spring AOP AspectJ
實作 純Java實作 使用Java程式設計語言的擴充來實作
單獨編譯過程 不需要 需要AspectJ編譯器(ajc),除非設定了LTW
織入方式 運作時 編譯時
織入水準 隻是針對方法 字段、方法、構造函數、靜态初始化、final Class
應用領域 Spring 所有領域
運作速度
學習難度 容易 更複雜

2.4 Spring AOP

  1. AspectJ風格(注解風格)
  2. xml配置風格(Schema-based AOP Support)
Spring (Bean, IoC, AOP, SpringMVC)SpringTomcat 和 servlet

2.4.1 術語和概念

切面 Aspect:
切點、連接配接點及其通知所在的那個類叫做切面。
切點 Pointcut:
連接配接點的集合
連接配接點 Joinpoint:
SpringAOP中連接配接點最小機關是方法,每個方法稱為一個連接配接點;連接配接點可以使用表達式表達,一個表達式可以表達多個連接配接點。
通知 Advice:
切入的時機和内容。
Spring (Bean, IoC, AOP, SpringMVC)SpringTomcat 和 servlet
Spring (Bean, IoC, AOP, SpringMVC)SpringTomcat 和 servlet

切入點表達式:

@Pointcut("execution(public * *(..))")
private void anyPublicOperation() {} 

@Pointcut("within(com.xyz.myapp.trading..*)")
private void inTrading() {} 

@Pointcut("anyPublicOperation() && inTrading()")
private void tradingOperation() {} 
           
  1. anyPublicOperation

    如果方法執行連接配接點代表任何公共方法的執行,則比對。
  2. inTrading

    如果交易子產品中有方法執行,則比對。
  3. tradingOperation

    如果方法執行代表交易子產品中的任何公共方法,則比對。

Advice的類型

1、

before advice

, 在

join point

前被執行的

advice

. 雖然

before advice

是在

join point

前被執行, 但是它并不能夠阻止

join point

的執行, 除非發生了異常(即我們在

before advice

代碼中, 不能人為地決定是否繼續執行

join point

中的代碼)
2、

after return advice

, 在一個

join point

正常傳回後執行的

advice

3、

after throwing advice

, 當一個

join point

抛出異常後執行的

advice

4、

after(final) advice

, 無論一個

join point

是正常退出還是發生了異常, 都會被執行的

advice

.
5、

around advice

, 在

join point

前和

joint point

退出後都執行的

advice

. 這個是最常用的

advice

.
6、

introduction

introduction

可以為原有的對象增加新的屬性和方法。
Spring (Bean, IoC, AOP, SpringMVC)SpringTomcat 和 servlet
圖檔:Spring AOP——簡單粗暴,小白教學

AOP

中的

Joinpoint

可以有多種類型:構造方法調用,字段的設定和擷取,方法的調用,方法的執行,異常的處理執行,類的初始化。

也就是說在

AOP

的概念中我們可以在上面的這些

Joinpoint

上織入我們自定義的

Advice

;

但是 在

Spring

中卻沒有實作上面所有的

joinpoint

,确切的說,

Spring

隻支援方法執行類型的

Joinpoint

Spring AOP應用-- 哔哩哔哩視訊

spring官方 : aop-aspectj-support

3幅圖讓你了解Spring AOP

Spring AOP:Spring 中面向切面程式設計

2.4.2 Spring AOP的應用

Spring

支援

XML

方式和實作注解的方式(也叫

AspectJ

方式)的

AOP

;打不死要使用

@Aspect

注解,需要引入

AspectJ

相關的

jar

aspectjrt

aspectjweaver

2.4.2.1 pom.xml配置

首先在pom.xml中添加相關jar包

<!-- aspectj 相關jar包-->
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjrt</artifactId>
      <version>1.9.1</version>
    </dependency>
    <!-- 做實驗時候缺了這個包,跑死跑不出來;還報異常
Exception encountered during context initialization - 
cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: 
Error creating bean with name '名字' ...
	-->
    <dependency> 
	  <groupId>org.aspectj</groupId>
	  <artifactId>aspectjweaver</artifactId>
	  <version>1.9.6</version>
	</dependency>
           
2.4.2.2 xml配置檔案

Spring

的配置檔案

mySpringAOP.xml

中引入

context

aop

對應的命名空間;配置自動掃描的包,同時使切面類中相關方法中的注解生效,需自動地為比對到的方法所在的類生成代理對象。

<?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:aop="http://www.springframework.org/schema/aop"
       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/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
    <!-- 配置自動掃描的包 -->
    <context:component-scan base-package="mySpringAOP" />
    <!-- 自動為切面方法中比對的方法所在的類生成代理對象。 -->
    <aop:aspectj-autoproxy/>
	<!--  
	這是 xml 配置方式,這裡的bean配置我們使用注解的方式,而不是xml
    <bean id="mathImpl" class="mySpringAOP.ArithmeticCalculatorImpl">
    </bean>
    -->
</beans>
           
2.4.2.3 電腦實作類

建立簡單電腦實作類

ArithmeticCalculatorImpl

package mySpringAOP;
import org.springframework.stereotype.Component;
// 将實作類加入Spring的IOC容器進行管理
@Component("mathImpl") // "ArithmeticCalculator"
public class ArithmeticCalculatorImpl {
    public int add(int i, int j) {
        System.out.println(i+" + "+ j +" = " + (i+j));
        return i+j;
    }
}
           
2.4.2.4 切面類

現在想在電腦實作類中的每個方法執行前、後、以及發生異常時等列印一些資訊,這時候我們建構一個切面類

SpringAOPAspect

package mySpringAOP;

import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Component // 類上使用 @Component 注解 把切面類加入到IOC容器中
@Aspect     // 使用 @Aspect 注解 使之成為切面類
public class SpringAOPAspect {
    // 切點(連接配接點的集合)
    @Pointcut("execution(* mySpringAOP.*.*(..))") // 使用Execution表達式表示連接配接點的集合
    private void justPointCut(){}
	// 這個execution表達式的含義:mySpringAOP包及所有子包下任何類的任何方法都會使用

    /** 通知:
     * ("justPointCut()") : 通知的位置(目标方法)
     * Before : 通知的時間
     * public void before{ ... } : 通知的内容
    */
    @Before("justPointCut()") // 
    public void before()    {
        System.out.println("============ Before ============");
    }
    @After("justPointCut()")
    public void after()  {
        System.out.println("============ After  ============");
    }
    @AfterReturning("justPointCut()")
    public void afterReturning()	{
        System.out.println("======= AfterReturning ========");
    }
    // 連接配接點方法執行抛出異常之後調用
    @AfterThrowing("justPointCut()")
    public void afterThrowing()    {
        System.out.println("======= AfterThrowing ========");
    }
    /**
    *  org.springframework.aop.AopInvocationException: Null return value from advice does not match primitive return type for...
    *  環繞監聽會對有傳回值的方法做處理 !!! 是以像下面這樣寫會報錯。
    @Around("justPointCut()")
    public void Around()    {
        System.out.println("============ Around ============");
    }
    */
}
           
Spring (Bean, IoC, AOP, SpringMVC)SpringTomcat 和 servlet

一個很不錯的AspectJ的Execution表達式說明

2.4.2.5 main方法
package mySpringAOP;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class mySpringAOPTest {
    public static void main(String args[])	{
    	// 讀取配置檔案,管理所有的bean
        ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:mySpringAOP.xml");
        // 生成Bean對應的對象
        ArithmeticCalculatorImpl ac = (ArithmeticCalculatorImpl)ctx.getBean("mathImpl");
        // 調用方法
        ac.add(3,5);
    }
}

           
執行結果如下圖所示:
Spring (Bean, IoC, AOP, SpringMVC)SpringTomcat 和 servlet
沒有抛出異常,是以

@AfterThrowing("justPointCut()")

注解的代碼沒有執行。

出現Exception encountered during context initialization - cancelling refresh attempt問題

2.4.3 Spring AOP的實作

Source Code Reading…

2.5 AspectJ

Writing…

3. 消息(Messaging)

spring framework 4

包含了

spring-messaging

子產品,其中使用了來自于

spring integration

項目的關鍵抽象,如

Message

,

MessageChannel

,

MessageHandler

等,他們可以作為基于消息的應用服務的基礎。該子產品還包含了一組可将消息映射到方法的注解,類似于

spring-mvc

的程式設計模型。

4. 資料通路/內建(Data Access/ Integration)

包含spring-jdbc, spring-tx, spring-orm, spring-oxm, spring-jms:

spring-jdbc

: 提供了JDBC抽象層,消除了冗長的JDBC編碼和解析資料庫廠商特有的錯誤代碼.

5. Web

包含spring-web, spring-webmvc, spring-websocket, spring-webmvc-portlet:

spring-web

提供了基于面向web內建的特性,如多檔案上傳功能、通過servlet listener初始化IoC容器與面向web的ApplicationContext,它還包含了HTTP用戶端與Spring遠端支援的web相關的部分.

spring-webmvc

(又名web-servlet)包含了Spring對于Web應用的MVC與REST實作,Spring MVC架構提供了領域模型代碼和Web表單之間的分離,并內建了Spring架構的所有其他特性.

spring-webmvc-portlet

(又名web-portlet)提供了基于Portlet環境使用MVC的實作.

SpringMVC

圖解

Spring (Bean, IoC, AOP, SpringMVC)SpringTomcat 和 servlet

展現主要流程的部分源碼

  1. 首先需要在

    web.xml

    配置

    DispatcherServlet

    ,這是

    SpringMVC

    的核心,用于分發不同的任務。
<!--配置springmvc DispatcherServlet-->
    <servlet>
        <servlet-name>springMVC</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <!--配置dispatcher.xml作為mvc的配置檔案-->
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:dispatcher-servlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
        <async-supported>true</async-supported>
    </servlet>
           
  1. dispatcher-servlet.xml

    配置
<!--此檔案負責整個mvc中的配置-->

    <!--啟用spring的一些annotation -->
    <context:annotation-config/>

    <!-- 配置注解驅動 可以将request參數與綁定到controller參數上 -->
    <mvc:annotation-driven/>

    <!--靜态資源映射-->
    <!--本項目把靜态資源放在了webapp的statics目錄下,資源映射如下-->
    <mvc:resources mapping="/css/**" location="WEB-INF/statics/css"/>
    <mvc:resources mapping="/js/**" location="WEB-INF/statics/js/"/>
    <mvc:resources mapping="/image/**" location="WEB-INF/statics/images/"/>
    <mvc:default-servlet-handler />
    <!--這句要加上,要不然可能會通路不到靜态資源,具體作用自行百度-->

    <!-- 對模型視圖名稱的解析,即在模型視圖名稱添加前字尾(如果最後一個還是表示檔案夾,則最後的斜杠不要漏了) 使用JSP-->
    <!-- 預設的視圖解析器 在上邊的解析錯誤時使用 (預設使用html)- -->
    <bean id="defaultViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
        <property name="prefix" value="/WEB-INF/views/"/><!--設定JSP檔案的目錄位置-->
        <property name="suffix" value=".jsp"/>
        <property name="exposeContextBeansAsAttributes" value="true"/>
    </bean>

    <!-- 自動掃描裝配 -->
    <context:component-scan base-package="example.controller"/>
           
  1. 執行

    DispatcherServlet

    doDispatch()

public class DispatcherServlet extends FrameworkServlet{
	...
	// 調用 Dispatch 方法
	doService(HttpServletRequest request, HttpServletResponse response)
	{
		...
		this.doDispatch(request, response);
		...
	}
	...
	protected void doDispatch(HttpServletRequest request, HttpServletResponse response)
	{
		...
		// 擷取handler的映射
		mappedHandler = this.getHandler(processedRequest);
		...
		// 擷取映handler擴充卡,不同的Controller實作對應的擴充卡不同
		HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
		...
		// 執行擴充卡的handle方法(本質上是Controller中的對應方法)
		mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
		...
		// View視圖解析,頁面渲染
		this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
		...
	}

}
           

SpringMVC源碼解析

springMvc 啟動流程

6. Test

spring-test

子產品通過

Junit

TestNG

spring

的元件提供了單元測試和內建測試。

Spring架構

Tomcat 和 servlet

Tomcat的工作機制