(京東面試題)java動態(tài)代理主要怎么實現(xiàn)的,spring aop 原理 如下類

京東面試題

1、java動態(tài)代理主要怎么實現(xiàn)的,spring aop 原理

如下類

public class Test {
  public void example(){
    System.out.println("example");
  }
}

如何實現(xiàn)在方法example 之前打印一句話,之后打印一句話

Spring aop 主要是在不改變原有代碼的基礎(chǔ)上,通過sppring動態(tài)添加代碼

是對oop面向?qū)ο蟮挠幸嫜a充

AOP的本質(zhì)是在一系列縱向的控制流程中,把那些相同的子流程提取成一個橫向的面

通過前置通知,后置通知和環(huán)繞通知可以實現(xiàn)


輕松理解AOP(面向切面編程)

本文主要介紹AOP思想,而不是Spring,Spring在本文只做為理解AOP的工具和例子,所以也不打算介紹Spring的Aspect、Join point、Advice、AOP proxy等概念,那樣初學(xué)者會很難理解,如果你懂了AOP的思想,那么Spring的AOP,還是AspectJ都容易理解了。

Spring如此流行,當(dāng)我第一次接觸Spring的時候,到網(wǎng)上看了一些文章,都講得神乎其乎,最后我篇也沒看懂,我當(dāng)時就是認(rèn)為這個東西一定很高深,于是我就遇到做WEB開發(fā)的人就會打聽一下。得到最多的一個回答就是“Spring是一個框架”,然后我就會問框架是什么,但都沒有一種說法不讓我感覺玄乎乎的,同時也沒有聽懂,于是,我更感覺Spring很神了,這可能叫做朦朧美。還有一種說法就是“Spring兩個首要的特性就是AOP和IoC”,這種說法讓我感覺Spring簡直神的飛上天了,我都不敢接著問了,再問可能想上天去找Spring了,后來我就懷疑這玩意兒真有那么高深嗎,強列的好奇心讓我實在hold,決定一定要試一下這個神器,于是就看了一些Step By Step的文章,自己寫了一個HelloWorld,發(fā)現(xiàn)這AOP確實是一個比較新穎的思想,也算是打破了常規(guī),是從不同方面思考問題。不過沒有那些童鞋說得那么神。

本文旨在幫助還沒有理解AOP的童鞋看透弄懂AOP,也歡迎高手批評指正

先說一個Spring是什么吧,大家都是它是一個框架,但框架這個詞對新手有點抽象,以致于越解釋越模糊,不過它確實是個框架的,但那是從功能的角度來定義的,從本質(zhì)意義上來講,Spring是一個庫,一個Java庫,所以我個人覺得應(yīng)該這樣回答Spring是什么:Spring是一個庫,它的功能是提供了一個軟件框架,這個框架目的是使軟件之間的邏輯更加清晰,配置更靈活,實現(xiàn)這個目的的手段使用AOP和IoC,而AOP和IoC是一種思想,是一種什么樣的思想呢,等下細(xì)說,先說AOP在Java里是利用反射機制實現(xiàn)(你也可以認(rèn)為是動態(tài)代理,不過動態(tài)代理也是反射機制實現(xiàn)的,所以還是先不要管動態(tài)代理,我們這里化繁為簡,不讓它干擾咱們對AOP的理解),如何使用AOP呢,很簡單滴,等下介紹。

下面先說AOP是什么樣的思想,我們一步一步慢慢來,先看一下傳統(tǒng)程序的流程,比如銀行系統(tǒng)會有一個取款流程

我們可以把方框里的流程合為一個,另外系統(tǒng)還會有一個查詢余額流程,我們先把這兩個流程放到一起:

有沒有發(fā)現(xiàn),這個兩者有一個相同的驗證流程,我們先把它們?nèi)ζ饋碓僬f下一步:

有沒有想過可以把這個驗證用戶的代碼是提取出來,不放到主流程里去呢,這就是AOP的作用了,有了AOP,你寫代碼時不要把這個驗證用戶步驟寫進(jìn)去,即完全不考慮驗證用戶,你寫完之后,在另我一個地方,寫好驗證用戶的代碼,然后告訴Spring你要把這段代碼加到哪幾個地方,Spring就會幫你加過去,而不要你自己Copy過去,這里還是兩個地方,如果你有多個控制流呢,這個寫代碼的方法可以大大減少你的時間,不過AOP的目的不是這樣,這只是一個“副作用”,真正目的是,你寫代碼的時候,事先只需考慮主流程,而不用考慮那些不重要的流程,懂C的都知道,良好的風(fēng)格要求在函數(shù)起始處驗證參數(shù),如果在C上可以用AOP,就可以先不管校驗參數(shù)的問題,事后使用AOP就可以隔山打牛的給所有函數(shù)一次性加入校驗代碼,而你只需要寫一次校驗代碼。不知道C的沒關(guān)系,舉一個通用的例子,經(jīng)常在debug的時候要打log吧,你也可以寫好主要代碼之后,把打log的代碼寫到另一個單獨的地方,然后命令A(yù)OP把你的代碼加過去,注意AOP不會把代碼加到源文件里,但是它會正確的影響最終的機器代碼。

現(xiàn)在大概明白了AOP了嗎,我們來理一下頭緒,上面那個方框像不像個平面,你可以把它當(dāng)塊板子,這塊板子插入一些控制流程,這塊板子就可以當(dāng)成是AOP中的一個切面。所以AOP的本質(zhì)是在一系列縱向的控制流程中,把那些相同的子流程提取成一個橫向的面,這句話應(yīng)該好理解吧,我們把縱向流程畫成一條直線

這個驗證用戶這個子流程就成了一個條線,也可以理解成一個切面,aspect的意思我認(rèn)為是方面,你用什么實物去類比,只要你能理解都可以。這里的切面只插了兩三個流程,如果其它流程也需要這個子流程,也可以插到其它地方去。

講了這么多,那到AOP該如何使用呢?我們要寫一個HelloWorld嗎,我看還是算了,關(guān)于這種類型的文章,網(wǎng)上已經(jīng)泛濫成災(zāi),我再寫也不一定比人家寫得好,所以,我會在下面貼幾個我認(rèn)為寫得不錯的文章鏈接,但我在這里先介紹一下Spring如何實現(xiàn)AOP的吧。其實也不難理解,Spring的實現(xiàn)是基于函數(shù)(或叫方法)的,就是說,你寫好了一個函數(shù)后,你還可以在不更改原來的代碼情況,通過Spring在函數(shù)前或函數(shù)后動態(tài)的加入新的代碼。比如你原來的代碼是這樣的:

void foo() {    
    System.out,println("in foo()");
}

然后你想在函數(shù)執(zhí)行前(當(dāng)然后也可以加到執(zhí)行后,或前后都加,原理是一樣)加一句:

System.out.println("before execute foo()");  

你也可以多加幾句,通過Spring,你可以把這些代碼動態(tài)的加到函數(shù)前面,而不用改變原來的代碼。從而會得到與以下等效的執(zhí)行碼:

void foo() {
    System.out.println("before execute foo()");
    System.out.println("in foo()");
}

我這樣一說你可能更想親手試試了,可以看看以下這篇文章,寫得很好,我們在此也感謝其作者的辛勤付出。

一個簡單的Spring的AOP例子

參考資料:

Spring AOP Guide: http://docs.spring.io/spring/docs/2.5.4/reference/aop.html


一個簡單的Spring的AOP例子

經(jīng)過這段日子的學(xué)習(xí)和使用Spring,慢慢地體會到Spring的優(yōu)妙之處,正在深入地吸收Spring的精華,呵呵。現(xiàn)在寫的這個只是個簡單AOP例子,包括前置通知,后置通知,環(huán)繞通知,和目標(biāo)對象。寫這個例子的主要目標(biāo)只是想讓想學(xué)AOP的能更快地入門,了解一下如何去配置AOP里面的東東。

目標(biāo)對象的接口:IStudent.java

package com.dragon.study;

public interface IStudent {
    
    public void addStudent(String name);
    
}

目標(biāo)類:StudentImpl.java

package com.dragon.study.Impl;

import com.dragon.study.IStudent;

public class StudentImpl implements IStudent{

    public void addStudent(String name) {
        System.out.println("歡迎"+name+" 你加入Spring家庭!");
    }
    
}

前置通知:BeforeAdvice.java

package com.dragon.Advice;

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

public class BeforeAdvice implements MethodBeforeAdvice{

    public void before(Method method, Object[] args, Object target) throws Throwable {
    
        System.out.println("這是Before類的before方法");
    
    }

}

后置通知:AfterAdvice.java

package com.dragon.Advice;

import java.lang.reflect.Method;

import org.springframework.aop.AfterReturningAdvice;

public class AfterAdvice implements AfterReturningAdvice{

    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("這是AfterAdvince類的afterReturning方法");
    }
    
    
}

環(huán)繞通知:CompareInterceptor.java

package com.dragon.Advice;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class CompareInterceptor implements MethodInterceptor{

    public Object invoke(MethodInvocation invocation) throws Throwable {
        Object result = null;
        String stu_name = invocation.getArguments()[0].toString();
        if(stu_name.equals("dragon")) {
            //如果學(xué)生是dragon時,執(zhí)行目標(biāo)方法,
            result = invocation.proceed();
        } else {
            System.out.println("此學(xué)生是"+stu_name+"而不是dragon,不批準(zhǔn)其加入.");
        }
        
        return result;
    }
    
}

配置文件applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>

    <bean id="beforeAdvice" class="com.dragon.Advice.BeforeAdvice"></bean>
    <bean id="afterAdvice" class="com.dragon.Advice.AfterAdvice"></bean>
    <bean id="compareInterceptor" class="com.dragon.Advice.CompareInterceptor"></bean>
    <bean id="studenttarget" class="com.dragon.study.Impl.StudentImpl"></bean>

    <bean id="student" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="proxyInterfaces">
            <value>com.dragon.study.IStudent</value>
        </property>
        <property name="interceptorNames">
            <list>
                <value>beforeAdvice</value>
                <value>afterAdvice</value>
                <value>compareInterceptor</value>
            </list>
        </property>
        <property name="target">
            <ref bean="studenttarget" />
        </property>

    </bean>


</beans>

現(xiàn)在開始寫測試類,Test.java

package com;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

import com.dragon.study.IStudent;

public class Test {
    public static void main(String[] args) {
        AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        
        IStudent person = (IStudent) ctx.getBean("student");
        person.addStudent("dragon");
    }
}

代碼結(jié)構(gòu)


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容