Spring 之 代理笙以、注解在AOP中的應(yīng)用

今天記錄一下春天(Spring)的AOP拴事,從以下幾個(gè)部分介紹:

  • 代理模式
  • Java注解(Annotation)
  • AOP中的Java動(dòng)態(tài)代理兩種方式
  • AOP中的術(shù)語(yǔ)與應(yīng)用

AOP為Aspect Oriented Programming践剂,意為:面向切面編程,通過(guò)預(yù)編譯方式和運(yùn)行期動(dòng)態(tài)代理實(shí)現(xiàn)程序功能的統(tǒng)一維護(hù)的一種技術(shù)陨瘩。降低程序業(yè)務(wù)之間的耦合程度腕够,提高可維護(hù)性與開(kāi)發(fā)效率。里面的基礎(chǔ)知識(shí)就是Java代理與注解舌劳。最后就是AOP相關(guān)的術(shù)語(yǔ)與應(yīng)用燕少。

代理模式

之前在Android源碼中也經(jīng)常遇到代理模式,比如Binder進(jìn)程間通信蒿囤,使用AIDL過(guò)程中客们,當(dāng)前線程傳遞數(shù)據(jù)會(huì)判斷是否需要跨進(jìn)程,如果需要跨進(jìn)程材诽,此時(shí)封裝數(shù)據(jù)為Parcelable對(duì)象底挫,然后再執(zhí)行transact函數(shù),而此時(shí)AIDL中封裝的代理完成了這些操作脸侥。類似下面例子:

//吃東西的接口
public interface Eat {
    void eat();
}
//吃蘋(píng)果類建邓,實(shí)現(xiàn)吃的接口
public class EatApple implements Eat {

    @Override
    public void eat() {
        System.out.println(">>>>吃掉蘋(píng)果");
    }
}

//洗蘋(píng)果工具類
public class WashApple {

    public static void washAnApple() {
        System.out.println("先洗洗蘋(píng)果");
    }
}
//代理類,洗完了再安全的吃睁枕。
public class EatAppleProxy {

    EatApple eatApple;

    public EatAppleProxy() {
        eatApple = new EatApple();
    }

    //健康的吃蘋(píng)果
    public void eatApple() {
        //先把蘋(píng)果洗干凈
        WashApple.washAnApple();
        //吃
        eatApple.eat();
    }
}

我們只需要調(diào)用代理類官边,EatAppleProxy就可以了,而不需要自己再去寫(xiě)其中的過(guò)程外遇。

Java注解(Annotation)

注解(Annotation) 為我們?cè)诖a中添加信息提供了一種形式化的方法注簿,是我們可以在稍后某個(gè)時(shí)刻方便地使用這些數(shù)據(jù),Annotation(注解)是JDK1.5及以后版本引入的跳仿。它可以用于創(chuàng)建文檔诡渴,跟蹤代碼中的依賴性,甚至執(zhí)行基本編譯時(shí)檢查菲语。簡(jiǎn)單點(diǎn)講就是給一些代碼添加標(biāo)記妄辩,便于后續(xù)對(duì)添加統(tǒng)一標(biāo)記的代碼做處理惑灵。
學(xué)習(xí)注解,最好學(xué)會(huì)自定義注解眼耀,這樣能更好理解注解原理英支。
元注解的作用就是負(fù)責(zé)注解其他注解。Java5.0定義了4個(gè)標(biāo)準(zhǔn)的meta-annotation類型哮伟,它們被用來(lái)提供對(duì)其它 annotation類型作說(shuō)明干花。Java5.0定義的元注解:

  • 1@Target,
  • 2@Retention,
  • 3@Documented,
  • 4@Inherited
@Target

說(shuō)明了Annotation所修飾的對(duì)象范圍,取值(ElementType)有:

  • CONSTRUCTOR:用于描述構(gòu)造器
  • FIELD:用于描述域
  • LOCAL_VARIABLE:用于描述局部變量
  • METHOD:用于描述方法
  • PACKAGE:用于描述包
  • PARAMETER:用于描述參數(shù)
  • TYPE:用于描述類、接口(包括注解類型) 或enum聲明
@Retention

定義了該Annotation被保留的時(shí)間長(zhǎng)短

  • 在源文件中有效(即源文件保留)
  • CLASS:在class文件中有效(即class保留)
  • RUNTIME:在運(yùn)行時(shí)有效(即運(yùn)行時(shí)保留)
@Documented

用于生成Java的Doc澈吨。

@Inherited

元注解是一個(gè)標(biāo)記注解把敢,@Inherited闡述了某個(gè)被標(biāo)注的類型是被繼承的寄摆。如果一個(gè)使用了@Inherited修飾的annotation類型被用于一個(gè)class谅辣,則這個(gè)annotation將被用于該class的子類。

如何自定義注解:

/* 
 * 定義注解 Test 
 * 為方便測(cè)試:注解目標(biāo)為類 方法婶恼,屬性及構(gòu)造方法 
 * 注解中含有三個(gè)元素 id ,name和 gid; 
 * id 元素 有默認(rèn)值 0
 */ 
@Target({TYPE,METHOD,FIELD,CONSTRUCTOR})
@Retention(RetentionPolicy.RUNTIME)
public @interface TestA {
    String name();
    int id() default 0;
    Class<Long> gid();
}

使用自定義注解

/**
 * 這個(gè)類專門用來(lái)測(cè)試注解使用
 * @author tmser
 */
 
@TestA(name="type",gid=Long.class) //類成員注解
public class UserAnnotation {
    
    @TestA(name="param",id=1,gid=Long.class) //類成員注解
    private Integer age;
    
    @TestA (name="construct",id=2,gid=Long.class)//構(gòu)造方法注解
    public UserAnnotation(){
        
    }
    @TestA(name="public method",id=3,gid=Long.class) //類方法注解
    public void a(){
        Map<String,String> m = new HashMap<String,String>(0);
    }
    
    @TestA(name="protected method",id=4,gid=Long.class) //類方法注解
    protected void b(){
        Map<String,String> m = new HashMap<String,String>(0);
    }
    
    @TestA(name="private method",id=5,gid=Long.class) //類方法注解
    private void c(){
        Map<String,String> m = new HashMap<String,String>(0);
    }
    
    public void b(Integer a){ 
        
    }
}

** 最重要的桑阶,獲取注解上的內(nèi)容**:java1.5中增加注解,也同樣增加了讀取注解的方法勾邦,在java.lang.reflect包中新增了AnnotatedElement接口蚣录,JDK源碼如下:

public interface AnnotatedElement {
        //判斷是否標(biāo)注了指定注解
        boolean isAnnotationPresent(Class<? extends Annotation> annotationClass);
        //獲取指定注解,沒(méi)有則返回null
        <T extends Annotation> T getAnnotation(Class<T> annotationClass);
        //獲取所有注解眷篇,包括繼承自基類的萎河,沒(méi)有則返回長(zhǎng)度為0的數(shù)組
        Annotation[] getAnnotations();
        //獲取自身顯式標(biāo)明的所有注解,沒(méi)有則返回長(zhǎng)度為0的數(shù)組
        Annotation[] getDeclaredAnnotations();
}

請(qǐng)看測(cè)試?yán)樱?/p>

public class ParseAnnotation {
 
    /**
     * 簡(jiǎn)單打印出UserAnnotation 類中所使用到的類注解
     * 該方法只打印了 Type 類型的注解
     * @throws ClassNotFoundException
     */
    public static void parseTypeAnnotation() throws ClassNotFoundException {  
        Class clazz = Class.forName("com.tmser.annotation.UserAnnotation"); 
        
        Annotation[] annotations = clazz.getAnnotations();  
        for (Annotation annotation : annotations) {  
            TestA testA = (TestA)annotation;
            System.out.println("id= \""+testA.id()+"\"; name= \""+testA.name()+"\"; gid = "+testA.gid());  
        }  
    } 
    
    /**
     * 簡(jiǎn)單打印出UserAnnotation 類中所使用到的方法注解
     * 該方法只打印了 Method 類型的注解
     * @throws ClassNotFoundException
     */
    public static void parseMethodAnnotation(){
        Method[] methods = UserAnnotation.class.getDeclaredMethods();  
        for (Method method : methods) {  
            /* 
             * 判斷方法中是否有指定注解類型的注解 
             */  
            boolean hasAnnotation = method.isAnnotationPresent(TestA.class);  
            if (hasAnnotation) {  
                /* 
                 * 根據(jù)注解類型返回方法的指定類型注解 
                 */  
                TestA annotation = method.getAnnotation(TestA.class);  
                System.out.println("method = " + method.getName()  
                        + " ; id = " + annotation.id() + " ; description = "  
                        + annotation.name() + "; gid= "+annotation.gid());  
            }  
        }  
    }
    
    /**
     * 簡(jiǎn)單打印出UserAnnotation 類中所使用到的方法注解
     * 該方法只打印了 Method 類型的注解
     * @throws ClassNotFoundException
     */
    public static void parseConstructAnnotation(){
        Constructor[] constructors = UserAnnotation.class.getConstructors();  
        for (Constructor constructor : constructors) { 
            /* 
             * 判斷構(gòu)造方法中是否有指定注解類型的注解 
             */  
            boolean hasAnnotation = constructor.isAnnotationPresent(TestA.class);  
            if (hasAnnotation) {  
                /* 
                 * 根據(jù)注解類型返回方法的指定類型注解 
                 */  
                TestA annotation =(TestA) constructor.getAnnotation(TestA.class);  
                System.out.println("constructor = " + constructor.getName()  
                        + " ; id = " + annotation.id() + " ; description = "  
                        + annotation.name() + "; gid= "+annotation.gid());  
            }  
        }  
    }
    
    public static void main(String[] args) throws ClassNotFoundException {
        parseTypeAnnotation();
        parseMethodAnnotation();
        parseConstructAnnotation();
    }
}

Java動(dòng)態(tài)代理

Java動(dòng)態(tài)代理分為JDK動(dòng)態(tài)代理和CGLib動(dòng)態(tài)代理蕉饼。Java作為一門靜態(tài)語(yǔ)言虐杯,不像JavaScript、php昧港、python等具有標(biāo)準(zhǔn)的動(dòng)態(tài)特性擎椰。但Java逐漸加入了一些特性來(lái)支持動(dòng)態(tài)特性,主要包括:反射機(jī)制创肥、動(dòng)態(tài)編譯达舒、動(dòng)態(tài)字節(jié)碼操作動(dòng)態(tài)類型轉(zhuǎn)換 等叹侄。

JDK動(dòng)態(tài)代理

基于JDK的反射(reflect)機(jī)制巩搏;在JDK中,提供了InvocationHandler這個(gè)接口趾代。源碼中解釋如下:

InvocationHandler is the interface implemented by the invocation handler of a proxy instance.
Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.

該接口由被代理對(duì)象的handler所實(shí)現(xiàn)塔猾;當(dāng)調(diào)用代理對(duì)象的方法時(shí),該方法調(diào)用將被編碼稽坤,然后交給代理對(duì)象的invoke方法去執(zhí)行丈甸。

具體使用:

public class ProxyFactory implements InvocationHandler {
    private Object delegate;

    public Object bind(Object delegate){
        this.delegate= delegate;
        return Proxy.newProxyInstance(delegate.getClass().getClassLoader(), 
                delegate.getClass().getInterfaces(), this);
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //前期處理
        WashApple.washAnApple();
        Object result=null;
        try {
            //調(diào)用原類中的方法
            result=method.invoke(delegate, args);
        } catch (Exception e) {
            // TODO: handle exceptions
        }
        return result;
    }
}

CGLib代理方式

一個(gè)強(qiáng)大的糯俗,高性能,高質(zhì)量的Code生成類庫(kù)睦擂,它可以在運(yùn)行期擴(kuò)展Java類與實(shí)現(xiàn)Java接口得湘。底層是通過(guò)使用一個(gè)小而快的字節(jié)碼處理框架ASM,來(lái)轉(zhuǎn)換字節(jié)碼并生成新的類顿仇√哉可見(jiàn)CGLib是從字節(jié)碼層改變Java執(zhí)行的。具體使用例子如下:

public class CglibProxyFactory  implements MethodInterceptor{

    private Object delegate;

    public Object bind(Object delegate){
        this.delegate=delegate;
        Enhancer enhancer= new Enhancer();
        enhancer.setSuperclass(delegate.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }
    public Object intercept(Object object, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        //前期處理
        WashApple.washAnApple();
        Object o =proxy.invoke(this.delegate, args);
        PerformanceUtil.endPerformance();
        return o;
    }

}

很明顯臼闻,這里的MethodInterceptor類似于JDK動(dòng)態(tài)代理中的InvocationHandler鸿吆。

AOP中的術(shù)語(yǔ)與應(yīng)用

  • 切面(Aspect):切面就是一個(gè)關(guān)注點(diǎn)的模塊化,如事務(wù)管理述呐、日志管理惩淳、權(quán)限管理等;
  • 連接點(diǎn)(Joinpoint):程序執(zhí)行時(shí)的某個(gè)特定的點(diǎn)乓搬,在Spring中就是一個(gè)方法的執(zhí)行思犁;
  • 通知(Advice):通知就是在切面的某個(gè)連接點(diǎn)上執(zhí)行的操作,也就是事務(wù)管理进肯、日志管理等激蹲;
  • 切入點(diǎn)(Pointcut):切入點(diǎn)就是描述某一類選定的連接點(diǎn),也就是用來(lái)指定某一類要織入通知的方法的描述江掩;
  • 目標(biāo)對(duì)象(Target):就是被AOP動(dòng)態(tài)代理的目標(biāo)對(duì)象学辱;
2472711-880e6b092a8cef90.png

上圖表示了以上術(shù)語(yǔ)之間的關(guān)系。

通知類型:

  • 前置通知(Before advice):在某連接點(diǎn)之前執(zhí)行的通知环形,但這個(gè)通知*
    不能阻止連接點(diǎn)之前的執(zhí)行流程(除非它拋出一個(gè)異常)策泣。
  • 后置通知(After returning advice):在某連接點(diǎn)正常完成后執(zhí)行的通知:例如,一個(gè)方法沒(méi)有拋出任何異常斟赚,正常返回着降。
  • 異常通知(After throwing advice):在方法拋出異常退出時(shí)執(zhí)行的通知。
  • 最終通知(After (finally) advice):當(dāng)某連接點(diǎn)退出的時(shí)候執(zhí)行的通知(不論是正常返回還是異常退出)拗军。
  • 環(huán)繞通知(Around Advice):包圍一個(gè)連接點(diǎn)的通知任洞,如方法調(diào)用。這是最強(qiáng)大的一種通知類型发侵。環(huán)繞通知可以在方法調(diào)用前后完成自定義的行為交掏。它也會(huì)選擇是否繼續(xù)執(zhí)行連接點(diǎn)或直接返回它自己的返回值或拋出異常來(lái)結(jié)束執(zhí)行。
    Spring的AOP文檔里提到:

Spring目前僅支持使用方法調(diào)用作為連接點(diǎn)(join point)(在Spring bean上通知方法的執(zhí)行)刃鳄。雖然可以在不影響到Spring AOP核心API的情況下加入對(duì)成員變量攔截器支持扶认,但Spring并沒(méi)有實(shí)現(xiàn)成員變量攔截器多糠。如果你需要把對(duì)成員變量的訪問(wèn)和更新也作為通知的連接點(diǎn)唠倦,可以考慮其它的語(yǔ)言据过,如AspectJ与涡。

注意Spring的AOP和AspectJ不是一回事。Spring側(cè)重于提供一種AOP實(shí)現(xiàn)和Spring IoC容器之間的整合,用于幫助解決在企業(yè)級(jí)開(kāi)發(fā)中的常見(jiàn)問(wèn)題。

Spring中使用AOP可以采用Spring特有的xml配置解取,或者使用AspectJ實(shí)現(xiàn)AOP。

Spring的xml配置
//目標(biāo)類
 <bean id="userServiceId" class="cn.leyue.a_aop.c_spring.UserServiceImpl"></bean>
 //切面類(通知)
 <bean id="myAspectId" class="cn.leyue.a_aop.c_spring.MyAspect"></bean>

    <!--
        創(chuàng)建代理類
        * 使用工廠bean FactoryBean ,底層調(diào)用getObject()返回特殊bean
        * ProxyFactoryBean 用于創(chuàng)建代理工程bean,生成特殊代理對(duì)象,
            interfaces:確定接口們
                通過(guò)<array>可以設(shè)置多個(gè)值
                只有一個(gè)值時(shí),value=""
             target 確定目標(biāo)類
                 interceptorNames:通知切面類的名稱,類型String[],如果設(shè)置一個(gè)值 value=""
             optimize:強(qiáng)制使用cglib
                 <property name="optimize" value="true"></property>
        * 底層機(jī)制
             如果目標(biāo)類接口,采用jdk動(dòng)態(tài)代理
             如果沒(méi)有接口,采用cglib 字節(jié)碼增強(qiáng)
             如果生命optimize=true 無(wú)論是否有接口,都采用cglib

    -->
    <bean id="proxyServiceId" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="interfaces" value="cn.leyue.a_aop.c_spring.UserService"></property>
        <property name="target" ref="userServiceId"></property>
        <property name="interceptorNames" value="myAspectId"></property>
        <property name="optimize" value="true"></property>
    </bean>

目標(biāo)類

public interface UserService {
    void addUser();
    void updateUser();
    void deleteUser();
}

public class UserServiceImpl implements UserService {

    @Override
    public void addUser() {
        System.out.println("b_cglib------addUser");
    }

    @Override
    public void updateUser() {
        System.out.println("b_cglib------updateUser");
    }

    @Override
    public void deleteUser() {
        System.out.println("b_cglib------deleteUser");

    }
}

切面類

/**
 * 切面類中確定通知,需要實(shí)現(xiàn)不同接口,接口就是規(guī)范,從而確定方法名稱.
 * 采用"環(huán)繞通知"MethodInterceptor
 */
public class MyAspect implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        System.out.println("c_spring_方法之前");
        //手動(dòng)執(zhí)行目標(biāo)方法
        Object obj = methodInvocation.proceed();
        System.out.println("c_spring_方法之后");
        return obj;

    }

}

測(cè)試類

public class SpringTest {
    @Test
    public void spring() {
        String path = "cn/leyue/a_aop/c_spring/beans.xml";
        ClassPathXmlApplicationContext application = new ClassPathXmlApplicationContext(path);
        UserService service = application.getBean("proxyServiceId", UserService.class);
        service.addUser();
        service.updateUser();
        service.deleteUser();
    }
}
AspectJ

這部分語(yǔ)法比較多返顺,建議使用查文檔即可禀苦,當(dāng)然記住最好啦,傳送門遂鹊,AspectJ使用

This is the end, that's all, Thank U.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末振乏,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子秉扑,更是在濱河造成了極大的恐慌慧邮,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,695評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件邻储,死亡現(xiàn)場(chǎng)離奇詭異赋咽,居然都是意外死亡旧噪,警方通過(guò)查閱死者的電腦和手機(jī)吨娜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)淘钟,“玉大人宦赠,你說(shuō)我怎么就攤上這事∶啄福” “怎么了勾扭?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,130評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)铁瞒。 經(jīng)常有香客問(wèn)我妙色,道長(zhǎng),這世上最難降的妖魔是什么慧耍? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,648評(píng)論 1 297
  • 正文 為了忘掉前任身辨,我火速辦了婚禮,結(jié)果婚禮上芍碧,老公的妹妹穿的比我還像新娘煌珊。我一直安慰自己,他們只是感情好泌豆,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布定庵。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蔬浙。 梳的紋絲不亂的頭發(fā)上猪落,一...
    開(kāi)封第一講書(shū)人閱讀 52,268評(píng)論 1 309
  • 那天,我揣著相機(jī)與錄音畴博,去河邊找鬼许布。 笑死,一個(gè)胖子當(dāng)著我的面吹牛绎晃,可吹牛的內(nèi)容都是我干的蜜唾。 我是一名探鬼主播,決...
    沈念sama閱讀 40,835評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼庶艾,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼袁余!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起咱揍,我...
    開(kāi)封第一講書(shū)人閱讀 39,740評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤颖榜,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后煤裙,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體掩完,經(jīng)...
    沈念sama閱讀 46,286評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評(píng)論 3 340
  • 正文 我和宋清朗相戀三年硼砰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了且蓬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,505評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡题翰,死狀恐怖恶阴,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情豹障,我是刑警寧澤冯事,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站血公,受9級(jí)特大地震影響昵仅,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜累魔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評(píng)論 3 333
  • 文/蒙蒙 一摔笤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧薛夜,春花似錦籍茧、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,357評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)渴析。三九已至,卻和暖如春吮龄,著一層夾襖步出監(jiān)牢的瞬間俭茧,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,466評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工漓帚, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留母债,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,921評(píng)論 3 376
  • 正文 我出身青樓尝抖,卻偏偏與公主長(zhǎng)得像毡们,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子昧辽,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理衙熔,服務(wù)發(fā)現(xiàn),斷路器搅荞,智...
    卡卡羅2017閱讀 134,701評(píng)論 18 139
  • 因?yàn)楣ぷ餍枨蠛炻龋约喝チ私庖幌耡op并做下的記錄,當(dāng)然大部分都是參考他人博客以及官方文檔咕痛。 目錄 [關(guān)于 AOP](...
    forip閱讀 2,278評(píng)論 1 20
  • 什么是Spring Spring是一個(gè)開(kāi)源的Java EE開(kāi)發(fā)框架痢甘。Spring框架的核心功能可以應(yīng)用在任何Jav...
    jemmm閱讀 16,475評(píng)論 1 133
  • 基本知識(shí) 其實(shí), 接觸了這么久的 AOP, 我感覺(jué), AOP 給人難以理解的一個(gè)關(guān)鍵點(diǎn)是它的概念比較多, 而且坑爹...
    永順閱讀 8,232評(píng)論 5 114
  • Author:ProZoom Hobby:愛(ài)折騰、愛(ài)思考茉贡,想靜靜的ProZoom Github --- 簡(jiǎn)書(shū) ...
    ProZoom閱讀 364評(píng)論 0 0