天天看點

Spring 的 AOP 和 IOC 的了解

IOC: Inversion of Control,控制反轉, 控制權從應用程式轉移到架構(如IOC容器),是架構共有的特性。

對于IOC的了解,可以把IOC看作是一個生産和管理bean對象的容器。原本程式中我們要手動自己建立(new)的對象統統交給Spring的IOC容器幫我們建立。同時這就意味着,要産生的單例的bean,這個對象的生命周期也是有IOC容器管理。

Spring中IOC的三種實作方式:

1.屬性注入,或叫做set方法注入;

2.構造方法注入;

3.注解注入,可能因為友善的原因,這種方式免去了臃腫的配置,是以比較常用。

Spring的有點主要是根據IOC和AOP實作的,之前有簡單提到AOP的實作思想,這些都是針對工廠模式和代理模式的友好封裝。IOC主要解決了代碼的高度耦合問題,AOP将面向切面的程式設計思想做出了最好的解釋和應用。

這裡順帶簡單解釋一下Spring中的IOC,DI,AOP:

       IOC就是控制反轉,通俗的說就是我們不用自己建立執行個體對象,這些都交給Spring的bean工廠幫我們建立管理。這也是Spring的核心思想,通過面向接口程式設計的方式來是實作對業務元件的動态依賴。這就意味着IOC是Spring針對解決程式耦合而存在的。在實際應用中,Spring通過配置檔案(xml或者properties)指定需要執行個體化的java類(類名的完整字元串),包括這些java類的一組初始化值,通過加載讀取配置檔案,用Spring提供的方法(getBean())就可以擷取到我們想要的根據指定配置進行初始化的執行個體對象。

       AOP就是将程式功能中的頻繁出現或者與主業務邏輯代碼相關度不高的代碼抽離出來,通過切面程式設計的方式在想要調用的時候引入調用的思想。而這種思想并不是隻限于Spring和java,AOP(面向切面)和OOP(面向對象)一樣都是一種程式設計思想,這種思想的實作機制在Spring中便是應用了java的動态代理和java的反射。在實際程式設計中,我們通常會遇到一些交叉業務邏輯(比如:日志,事務,安全等等),這是我們就可以封裝一個封面,然後注入到目标對象(具體的業務邏輯)中去。ps:很多方法都會抛出異常資訊,這是我們就可以寫一個攔截器,在這個類中實作記錄錯誤日志的功能,再在Spring的xml配置檔案中配置一個對這些要記錄日志的方法的AOP攔截器,在這個方法執行後調用這個攔截器來記錄日志。這樣就不用每次抛出異常都要手動的去單獨處理記錄,提高了程式的内聚性。這種在調用某個方法之前/後想要自動執行一系列自定義操作的就是AOP思想。

總結:

IOC即控制反轉,一種将控制權轉移的設計模式,由傳統的程式控制轉移到容器控制;

DI即依賴注入,将互相依賴的對象分離,在Spring的配置(注解)中描述它們之間的依賴關系,這些依賴關系也隻在使用時才被建立。

AOP即面向切面,一種程式設計思想,OOP的延續。将系統中非核心的業務提取出來,進行單獨處理。

Spring的AOP和IOC都是為了解決代碼的耦合度的實際應用,使得代碼的重用度變高,便于維護。但這些都不是Spring中特有的,我們可以說Spring将它們應用的更靈活。

引用前者:絡慶鐘朔

如果還是不明白,我們可以進行直白的了解:

一、什麼叫Ioc、DI

Ioc:Inversion of Control —— 控制反轉

DI:Dependency Injection —— 依賴注入

其實這兩個概念本質上是沒有差別的,那我們先來看看什麼叫做Ioc?

假設這麼一個場景:

在A類中調用B類的方法,那麼我們就稱 A依賴B,B為被依賴(對象),相信這點大家能夠了解。

傳統做法:

  • (1)直接在A(方法)中new出B類對象,然後調用B類方法 —— 寫死耦合;
  • (2)通過簡單工廠擷取B類對象,然後調用B類的方法 —— 擺脫了與B的耦合,卻又與工廠産生了耦合;

以上兩種做法,都是在A中主動去new或調用簡單工廠的方法産生B的對象,注意,關鍵字是“主動”

Spring架構

在spring中,B的執行個體對象被看成Bean對象,這個Bean對象由spring容器進行建立和管理,當我們在配置檔案中配置<Bean>下的<property>子元素時,spring就會自動執行在A中對B對象的setter方法(即A中需要有對B對象的setter方法),如此一來,A擷取B的執行個體對象就不是由自己主動去擷取,而是被動接受spring給它設值,那麼,這個主動變為被動,就可以了解為“控制反轉”。

而另一種說法,從spring容器的角度上看,它負責把A的依賴對象B(B是被依賴對象)注入給了A,是以我們可以了解為“依賴注入”

(spring中依賴注入的方式可不止調用setter方法一種,還有通過調用構造器的方式來實作,這裡隻是為了說明Ioc和DI,就不再累贅了)

<bean id="userAction" class="com.router.action.UserAction" scope="prototype">
        <!-- 注入Service -->
        <property name="userService" ref="userService" />
</bean>
<bean id="userService" class="com.router.serviceimpl.UserServiceImpl"></bean>
           
// 注入service
private UserService userService;
        
public void setUserService(UserService userService) {
        this.userService = userService;
    }
           

        以上代碼,spring通過<property>元素(當然内部有它的實作方法)自動調用userService的setter方法,userAction中就獲得了userService對象了。

        那麼我們來分析一下,通過使用spring的依賴注入功能,是怎麼達到解耦了呢?

        首先,我們的程式設計是面向接口程式設計(在實際開發開發中也是需要我們面向接口程式設計的),上面代碼中的UserService就是一個接口,UserServiceImpl就是其中的一個實作類。那麼當我們通過直接new的方式建立對象,則是UserService userService = new UserServiceImpl();,這句話是寫在源代碼裡頭中的,當實作類UserServiceImpl内部放生改變時,或者是不再想使用這個類,而是另一個新的實作類(比如說是UserServiceImpl2),那麼我們就得在源代碼中将UserService userService = new UserServiceImpl2();,而以後或許需求還會變,那麼就得不停地修改源代碼。

而使用spring架構後,隻需在配置檔案中的<Bean>配置所需要的相應接口的實作方法,然後通過setter方法注入進去即可,setter方法不管以後變不變實作類,都不需要修改,要改的隻是在spring的配置檔案中改掉實作類的全路徑即可,如此看來,這确實是達到了解耦!

二、什麼是AOP?

AOP —— Asepct-Orentid-Programming,面向切面程式設計

        那麼我們該怎麼了解AOP呢?我們可以通過OOP —— 面向對象程式設計來進行比較分析

        相信大家對于OOP的了解不難,就以人(people)來說,我們就可以把它看做一類對象,people有身高、體重、年齡等屬性,也有跑步、吃飯、睡覺、娛樂等行為,把這些屬于people的屬性和行為封裝在people類中,然後以統一調用的方式(建立一個people類執行個體對象,通過這個對象執行個體來調用這些屬性和行為)就叫做OOP思想,OOP給我們的感覺就是結構清晰,高内聚,易維護等。這些屬于一種從上到下的關系(即這個類封裝的所有屬性和方法都是屬于people的),而我們的AOP思想就是一種從左到右的關系,以切入的方式将業務邏輯功能應用到每一層結構中(可以了解為類方法,類方法也是一種對象的行為實作)。舉個例子,people也可以分為少年、青年、中年、和老年,這幾類人除了擁有自己的屬性和行為外,生活中,或許還需要去醫院看病,但是醫院看病這一個邏輯業務功能并不是屬于哪一類,而是誰生病了,才需要到醫院看病,而基于面向對象程式設計的思想,我們是不可能把這一個業務邏輯行為加到每一個類中的,這不符合OOP思想,而這個就是AOP所做也可以做到事情了,AOP就是把醫院看病這一個業務邏輯功能抽取出來,然後動态把這個功能注入到需要的方法(或行為)中,以後,不管是誰需要看病,就到醫院這個第三方機構看病(AOP就是相當于把這個第三方機構獨立出來),這樣從業務邏輯角度上,AOP達到了更近一步的的解耦,是以我們也稱AOP是對OOP的完善和增強。

而我們的程式設計中,常用到AOP的就是安全校驗、日志操作、事務操作等,接下來一張圖認識AOP思想

        AOP就是使用上圖所示的“橫切”技術,AOP把軟體系統分為兩個部分:核心關注點和橫切關注點。業務處理的主要流程是核心關注點,與之關系不大的部分是橫切關注點。橫切關注點的一個特點是,他們經常發生在核心關注點的多處,而各處都基本相似。比如權限認證、日志、事務處理。Aop 的作用在于分離系統中的各種關注點,将核心關注點和橫切關注點分離開來。正如Avanade公司的進階方案構架師Adam Magee所說,AOP的核心思想就是“将應用程式中的商業邏輯同對其提供支援的通用服務進行分離。”

AOP的相關術語

1.通知(Advice)

  就是你想要的功能,也就是上面說的 安全,事務,日志等。你給先定義好,然後在想用的地方用一下。

2.連接配接點(JoinPoint)

  這個更好解釋了,就是spring允許你使用通知的地方,那可真就多了,基本每個方法的前,後(兩者都有也行),或抛出異常時都可以是連接配接點,spring隻支援方法連接配接點,其他如aspectJ還可以讓你在構造器或屬性注入時都行,不過那不是咱關注的,隻要記住,和方法有關的前前後後(抛出異常),都是連接配接點。

3.切入點(Pointcut)

  上面說的連接配接點的基礎上,來定義切入點,你的一個類裡,有15個方法,那就有幾十個連接配接點了對把,但是你并不想在所有方法附近都使用通知(使用叫織入,以後再說),你隻想讓其中的幾個,在調用這幾個方法之前,之後或者抛出異常時幹點什麼,那麼就用切點來定義這幾個方法,讓切點來篩選連接配接點,選中那幾個你想要的方法。

4.切面(Aspect)

  切面是通知和切入點的結合。現在發現了吧,沒連接配接點什麼事情,連接配接點就是為了讓你好了解切點,搞出來的,明白這個概念就行了。通知說明了幹什麼和什麼時候幹(什麼時候通過方法名中的before,after,around等就能知道),而切入點說明了在哪幹(指定到底是哪個方法),這就是一個完整的切面定義。

5.引入(introduction)

  允許我們向現有的類添加新方法屬性。這不就是把切面(也就是新方法屬性:通知定義的)用到目标類中嗎

6.目标(target)

  引入中所提到的目标類,也就是要被通知的對象,也就是真正的業務邏輯,他可以在毫不知情的情況下,被咱們織入切面。而自己專注于業務本身的邏輯。

7.代理(proxy)

  怎麼實作整套aop機制的,都是通過代理,也就是說,AOP的實作原理是基于動态代理實作的。

8.織入(weaving)

  把切面應用到目标對象來建立新的代理對象的過程。

        在此筆者建議,如果不是很了解java動态代理的代理,可以先去熟悉下動态代理,這樣能更好的了解AOP的實作原理

        可看筆者另一篇文章 靜态代理與動态代理

AOP應用執行個體1 —— 動态代理的形式模拟AOP

(以下應用執行個體都是基于接口程式設計,筆者就不示出接口了)

public class UserAImpl implements UserA{
 
    @Override
    public void save() {
        System.out.println("正在儲存A類使用者……");
        
    }
 
    @Override
    public void update() {
        System.out.println("正在更新A類使用者……");
        
    }
 
}
           
public class UserBImpl implements UserB {
 
    @Override
    public void save() {
        System.out.println("正在儲存B類使用者……");
 
    }
 
    @Override
    public void update() {
        System.out.println("正在更新B類使用者……");
 
    }
 
}
           

AOP業務增強(通知)類

public class DataValidateImpl implements DataValidate {
 
    @Override
    public void validate() {
        System.out.println("正在進行資料校驗……");
        System.out.println("資料校驗完畢!");
        
    }
 
    @Override
    public void advice() {
        System.out.println("操作成功");
        
    }
 
}
           

代理工廠類

public class ProxyFactoryImpl implements ProxyFactory {
 
    //單例模式建立工廠
    private static ProxyFactoryImpl proxyFactorySingleton;
    
    private ProxyFactoryImpl() {}
    
    public static ProxyFactoryImpl getProxyFactorySingleton() {
        if (proxyFactorySingleton == null) {
            proxyFactorySingleton = new ProxyFactoryImpl();
        }
        return proxyFactorySingleton;
    }
    
    @Override
    public Object newProxyInstance(Object obj, InvocationHandler handler) {
        
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), 
                obj.getClass().getInterfaces(), 
                handler);
    }
 
}

           

測試類

public class AOPTest {
 
    public static void main(String[] args) {
        ProxyFactoryImpl proxyFactory = ProxyFactoryImpl.getProxyFactorySingleton();
        
        //操作A類使用者資料
        UserA ua = (UserA) proxyFactory.newProxyInstance(new UserAImpl(), 
                new UserAHandler(new UserAImpl(), new DataValidateImpl()));
        //得到的是代理對象
        System.out.println(ua.getClass().getName());
        
        ua.save();
        ua.update();
        
        System.out.println("********************");
        
        //操作B類使用者資料
        UserB ub = (UserB) proxyFactory.newProxyInstance(new UserBImpl(), 
                new UserBHandler(new UserBImpl(), new DataValidateImpl()));
        
        //得到的是代理對象
        System.out.println(ub.getClass().getName());
        
        ub.save();
        ub.update();
        
        //如果不用代理來調用,就是這樣的結果
        System.out.println("======================");
        UserB ub2 = new UserBImpl();
        ub2.save();
        ub2.update();
    }
}

           

運作結果:

Spring 的 AOP 和 IOC 的了解

AOP應用執行個體 —— spring注解方式使用AOP

User類

public class User {
 
    public void addUser(){
        System.out.println("添加成功!");
    }
}
           

增強類

@Aspect
public class MyUser {
 
    @Before(value = "execution(* com.xian.entity.User.*(..))")
    public void before() {
        System.out.println("before……");
    }
    
    @After(value = "execution(* com.xian.entity.User.*(..))")
    public void after() {
        System.out.println("after……");
    }
}
           

配置檔案

<bean id="user" class="com.xian.entity.User"></bean>
<bean id="myUser" class="com.xian.entity.MyUser"></bean>
    
    <!-- 配置檔案方式使用AOP -->
    <!-- <aop:config>
        配置切入點
        <aop:pointcut expression="execution(* com.xian.entity.User.*(..))" id="userPC1"/>
        
        配置切面
            将增強使用于方法上
        
         <aop:aspect ref="myUser">
             配置增強的類型
             <aop:before method="before" pointcut-ref="userPC1"/>
             <aop:after method="after" pointcut-ref="userPC1"/>
         </aop:aspect>
    </aop:config> -->
    
    <!-- 注解方式使用AOP -->
    <!-- 開啟AOP代理 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
           

測試類

public class UserTest {
    //private static Logger userLog = Logger.getLogger(User.class);
    @Test
    public void testUser(){
        
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        User user = (User) context.getBean("user");//通過bean容器獲得 的user其實隻是一個代理對象
        User user2 = new User();
        System.out.println(user == user2);
        MyUser mu = (MyUser) context.getBean("myUser");
        //userLog.info("開始調用User的Add方法……");
        user.addUser();//把這裡變成user2來調用add,就不會執行切面的增強邏輯功能了
        
        //userLog.info("正常結束……");
    }
}
           

運作結果:

Spring 的 AOP 和 IOC 的了解

希望對讀者有幫助!轉載請注明出處!

--------------------- 

作者:回夢遊先 

來源:CSDN 

原文:https://blog.csdn.net/h_xiao_x/article/details/72774496 

版權聲明:本文為部落客原創文章,轉載請附上博文連結!

上面都是你自己了解的,一下是面試的标準回答:

什麼是 Spring IOC 容器?

IOC 控制反轉:Spring IOC 負責建立對象,管理對象。通過依賴注入(DI),裝配對象,配置對象,并且管理這

些對象的整個生命周期

 IOC 的優點是什麼?

IOC 或 依賴注入把應用的代碼量降到最低。它使應用容易測試,單元測試不再需要單例和 JNDI 查找機制。最小

的代價和最小的侵入性使松散耦合得以實作。IOC 容器支援加載服務時的餓漢式初始化和懶加載。

簡單解釋一下 spring 的 AOP

       AOP(Aspect Oriented Programming),即面向切面程式設計,可以說是 OOP(Object Oriented Programming,

面向對象程式設計)的補充和完善。OOP 引入封裝、繼承、多态等概念來建立一種對象層次結構,用于模拟公共行為的一

個集合。不過 OOP 允許開發者定義縱向的關系,但并不适合定義橫向的關系,例如日志功能。日志代碼往往橫向地散

布在所有對象層次中,而與它對應的對象的核心功能毫無關系對于其他類型的代碼,如安全性、異常處理和透明的持

續性也都是如此,這種散布在各處的無關的代碼被稱為橫切(cross cutting),在 OOP 設計中,它導緻了大量代碼的

重複,而不利于各個子產品的重用。

      AOP 技術恰恰相反,它利用一種稱為"橫切"的技術,剖解開封裝的對象内部,并将那些影響了多個類的公共行為封裝到一個可重用子產品,并将其命名為"Aspect",即切面。所謂"切面",簡單說就是那些與業務無關,卻為業務子產品所共同調用的邏輯或責任封裝起來,便于減少系統的重複代碼,降低子產品之間的耦合度,并有利于未來的可操作性和可維護性。

     使用"橫切"技術,AOP 把軟體系統分為兩個部分:核心關注點和橫切關注點。業務處理的主要流程是核心關注點,

與之關系不大的部分是橫切關注點。橫切關注點的一個特點是,他們經常發生在核心關注點的多處,而各處基本相似,

比如權限認證、日志、事物。AOP 的作用在于分離系統中的各種關注點,将核心關注點和橫切關注點分離開來。

AOP 核心就是切面,它将多個類的通用行為封裝成可重用的子產品,該子產品含有一組 API 提供橫切功能。比如,一個日

志子產品可以被稱作日志的 AOP 切面。根據需求的不同,一個應用程式可以有若幹切面。在 Spring AOP 中,切面通過

帶有@Aspect 注解的類實作。