Spring AOP 面向切面編程

? ? ? ?AOP全稱Aspect Oriented Programming馏鹤。在OOP
(面向對象程序設計)中米绕,正是這種分散在各處且與對象核心功能無關的代碼(橫切代碼)的存在垢村,使得模塊復用難度增加割疾。AOP則將封裝好的對象剖開,找出其中對多個對象產(chǎn)生影響的公共行為肝断,并將其封裝為一個可重用的模塊,這個模塊被命名為“切面”(Aspect)驰凛,切面將那些與業(yè)務無關胸懈,卻被業(yè)務模塊共同調用的邏輯提取并封裝起來,減少了系統(tǒng)中的重復代碼恰响,降低了模塊間的耦合度趣钱,同時提高了系統(tǒng)的可維護性。

? ? ? ?AOP(Aspect-OrientedProgramming胚宦,面向方面編程)首有,可以說是OOP(Object-Oriented Programing,面向對象編程)的補充和完善枢劝。OOP引入封裝井联、繼承和多態(tài)性等概念來建立一種對象層次結構,用以模擬公共行為的一個集合您旁。當我們需要為分散的對象引入公共行為的時候烙常,OOP則顯得無能為力。也就是說鹤盒,OOP允許你定義從上到下的關系蚕脏,但并不適合定義從左到右的關系侦副。例如日志功能。日志代碼往往水平地散布在所有對象層次中驼鞭,而與它所散布到的對象的核心功能毫無關系秦驯。對于其他類型的代碼,如安全性挣棕、異常處理和透明的持續(xù)性也是如此译隘。這種散布在各處的無關的代碼被稱為橫切(cross-cutting)代碼,在OOP設計中穴张,它導致了大量代碼的重復细燎,而不利于各個模塊的重用。

一 AOP編程相關名詞

  • 切面(Aspect):在代碼體系中皂甘,對象與對象之間玻驻,方法與方法之間,模塊與模塊之間都可以認為是一個個的切面偿枕。Spring中通過@Aspect:來描述一個切面璧瞬。

  • 連接點(Joinpoint):在程序執(zhí)行過程中某個特定的點,比如某方法調用的時候或者處理異常的時候渐夸。在Spring AOP中嗤锉,一個連接點總是表示一個方法的執(zhí)行。

  • 通知(Advice):在切面的某個特定的連接點上執(zhí)行的動作墓塌。其中包括了“around”瘟忱、“before”和“after”等不同類型的通知。許多AOP框架(包括Spring)都是以攔截器做通知模型苫幢,并維護一個以連接點為中心的攔截器鏈访诱。

  • 切入點(Pointcut):定義在什么時候切人方法。匹配連接點的斷言韩肝。通知和一個切入點表達式關聯(lián)触菜,并在滿足這個切入點的連接點上運行(例如,當執(zhí)行某個特定名稱的方法時)哀峻。切入點表達式如何和連接點匹配是AOP的核心:Spring缺省使用AspectJ切入點語法涡相。Spring里面通過@Pointcut來引入切入點。

  • 引入(Introduction):用來給一個類型聲明額外的方法或屬性(也被稱為連接類型聲明(inter-type declaration))剩蟀。Spring允許引入新的接口(以及一個對應的實現(xiàn))到任何被代理的對象催蝗。例如,你可以使用引入來使一個bean實現(xiàn)IsModified接口育特,以便簡化緩存機制记劝。

  • 目標對象(Target Object):被一個或者多個切面所通知的對象痕囱。也被稱做被通知(advised)對象阱当。既然Spring AOP是通過運行時代理實現(xiàn)的盆赤,這個對象永遠是一個被代理(proxied)對象。

  • AOP代理(AOP Proxy):AOP框架創(chuàng)建的對象,用來實現(xiàn)切面契約(例如通知方法執(zhí)行等等)。在Spring中,AOP代理可以是JDK動態(tài)代理或者CGLIB代理截酷。

  • 織入(Weaving):把切面連接到其它的應用程序類型或者對象上,并創(chuàng)建一個被通知的對象乾戏。這些可以在編譯時(例如使用AspectJ編譯器)迂苛,類加載時和運行時完成。Spring和其他純Java AOP框架一樣鼓择,在運行時完成織入三幻。

二 連接點(Joinpoint)

? ? ? ?連接點是在應用執(zhí)行過程中能夠插入切面的一個點,這個點可以是調用方法時呐能,拋出異常時念搬,甚至是修改一個字段時,切面代碼可以利用這些連接點插入到應用的正常流程中摆出,并添加新的行為朗徊,如日志、安全偎漫、事務爷恳、緩存等。具體代碼中象踊,連接點體現(xiàn)在每個通知方法的參數(shù)中温亲。
比如如下前置@Before通知方法的第一個參數(shù)就是連接點,通過連接點我們可以獲取到一些上下文的信息。

    /**
     * 前置通知:目標方法執(zhí)行之前執(zhí)行以下方法體的內容
     */
    @Before(value = "operateLog()")
    public void beforeMethod(JoinPoint jp) {
        String methodName = jp.getSignature().getName();
        System.out.println("【前置通知】the method 【" + methodName + "】");
    }

? ? ? ?@Before杯矩、@After栈虚、@AfterReturning、@AfterThrowing都是使用的org.aspectj.lang.JoinPoint接口表示目標類連接點對象菊碟,@Around使用org.aspectj.lang.ProceedingJoinPoint表示連接點對象节芥。兩個接口里面的方法也不復雜在刺。主要方法如下逆害。

JoinPoint接口主要方法如下

public interface JoinPoint {

    /**
     * 獲取代理對象本身
     */
    Object getThis();

    /**
     * 獲取連接點所在的目標對象

     */
    Object getTarget();

    /**
     * 獲取連接點方法運行時的入?yún)⒘斜?     */
    Object[] getArgs();

    /** 獲取連接點的方法簽名對象,進而可以獲取方法的名字蚣驼,方法修飾符這些
     */
    Signature getSignature();

    /**
     * 獲取連接點方法在文件中的信息魄幕,比如文件中第幾行啥的
     */
    SourceLocation getSourceLocation();

    /** 獲取連接點的方法的類型
     */
    String getKind();

    /**
     * 獲取封裝連接點的一個對象
     */
    JoinPoint.StaticPart getStaticPart();


}

ProceedingJoinPoint主要方法如下,ProceedingJoinPoint繼承自JoinPoint

public interface ProceedingJoinPoint extends JoinPoint {

    /**
     * 這個函數(shù)咱們不能直接調用颖杏,不管他
     */
    void set$AroundClosure(AroundClosure arc);

    /**
     * P通過反射執(zhí)行目標對象的連接點處的方法
     */
    public Object proceed() throws Throwable;

    /**
     * 通過反射執(zhí)行目標對象連接點處的方法纯陨,不過使用新的入?yún)⑻鎿Q原來的入?yún)?     */
    public Object proceed(Object[] args) throws Throwable;
}

三 通知(Advice)

? ? ? ?通知定義了切面是什么以及何時調用,何時調用。通知包含以下幾種:

Advice Spring注解 解釋
Before @Before 前置通知翼抠,在方法被調用之前調用
After @After 最終通知, 在方法完成之后調用咙轩,無論方法執(zhí)行是否成功
After-returning @AfterReturning 后置通知,在方法成功執(zhí)行之后調用
After-throwing @AfterThrowing 異常通知,在方法拋出異常后調用
Around @Around 環(huán)繞通知, 包圍一個連接點的通知

? ? ? ?@Before阴颖、@After活喊、@AfterReturning、@AfterThrowing這幾個通知應該都還好理解量愧。就@Around稍稍復雜一點钾菊。

? ? ? ?環(huán)繞通知:包圍一個連接點的通知,如方法調用偎肃。這是最強大的一種通知類型煞烫。環(huán)繞通知可以在方法調用前后完成自定義的行為。它也會選擇是否繼續(xù)執(zhí)行連接點或直接返回它自己的返回值或拋出異常來結束執(zhí)行累颂。

? ? ? ?環(huán)繞通知最麻煩滞详,也最強大,其是一個對方法的環(huán)繞喘落,具體方法會通過代理傳遞到切面中去茵宪,切面中可選擇執(zhí)行方法與否,執(zhí)行方法幾次等瘦棋。

? ? ? ?環(huán)繞通知使用一個代理ProceedingJoinPoint類型的對象來管理目標對象稀火,所以此通知的第一個參數(shù)必須是ProceedingJoinPoint類型,在通知體內赌朋,調用ProceedingJoinPoint的proceed()方法會導致后臺的連接點方法執(zhí)行凰狞。proceed 方法也可能會被調用并且傳入一個Object[]對象-該數(shù)組中的值將被作為方法執(zhí)行時的參數(shù)。

四 切點(Pointcut)

? ? ? ?切點定義了何處沛慢,切點的定義會匹配通知所要織入的一個或多個連接點赡若,我們通常使用明確的類的方法名稱來指定這些切點,或是利用正則表達式定義匹配的類和方法名稱來指定這些切點团甲。

? ? ? ?切點(Pointcut)的使用關鍵在切點表達式逾冬,一般由下列方式來定義或者通過 &&、 ||躺苦、 !身腻、 的方式進行組合:

切入點表達式指示符 解釋
execution 匹配子表達式(匹配方法執(zhí)行)
within 匹配連接點所在的Java類或者包
this 用于向通知方法中傳入代理對象的引用
target 用于向通知方法中傳入目標對象的引用
args 用于將參數(shù)傳入到通知方法中
@within 匹配在類一級使用了參數(shù)確定的注解的類,其所有方法都將被匹配(在類上添加注解)
@target 和@within的功能類似匹厘,但必須要指定注解接口的保留策略為RUNTIME
@args 傳入連接點的對象對應的Java類必須被@args指定的Annotation注解標注
@annotation 匹配當前執(zhí)行方法持有指定注解的方法

咱們可以簡單的認為嘀趟,通知(Advice)定義了什么時候調用,切點(Pointcut)定義了哪個地方愈诚。一個指定了when,另一個指定了where她按。

4.1 execution

? ? ? ?"execution(方法表達式)":匹配方法執(zhí)行的連接點牛隅。

? ? ? ?execution方法表達式語法如下:

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern.?name-pattern(param-pattern)throws-pattern?) 
  • 修飾符匹配(modifier-pattern?): 可選。修飾符:public酌泰、private媒佣、protected。

  • 返回值匹配(ret-type-pattern): 必填陵刹。匹配返回值類類型丈攒。也可以為*表示任何返回值。

  • 類路徑匹配(declaring-type-pattern.?): 可選授霸。匹配類名巡验。 (相當于某個類下面的哪個方法。注意:后面是有一個點號的碘耳,然后才接的方法名)

  • 方法名匹配(name-pattern): 必填显设。匹配方法名 也可以為表示代表所有方法, 還可以類似set的形式,代表以set開頭的所有方法辛辨。

  • 參數(shù)匹配((param-pattern)): 必填捕捂。 匹配具體的參數(shù)類型,多個參數(shù)間用“,”隔開斗搞,各個參數(shù)也可以用“”來表示匹配任意類型的參數(shù)指攒,如(String)表示匹配一個String參數(shù)的方法;(,String) 表示匹配有兩個參數(shù)的方法僻焚,第一個參數(shù)可以是任意類型允悦,而第二個參數(shù)是String類型;可以用(..)表示零個或多個任意參數(shù)虑啤。

  • 異常類型匹配(throws-pattern?): 可選隙弛。函數(shù)拋出異常類型匹配。

@Pointcut execution 表達式其實是很好理解的狞山,咱們可以把他看成一個函數(shù)就好了全闷,函數(shù)有修飾符、返回值萍启、方法名总珠、參數(shù)、異常勘纯。declaring-type-pattern? 和 name-pattern 共同組合匹配方法匹配方法局服。 如果寫了declaring-type-pattern注意,后面要帶一個點號屡律。

? ? ? ?比如如下實例腌逢,匹配com.tuacy.microservice.framework.user.manage.controller包下面直接子類所有方法

/**
 * 日志AOP 切面類
 */

@Aspect
@Component("logAspect")
public class LoggingAspect {

    /**
     * 匹配com.tuacy.microservice.framework.user.manage.controller包下面直接子類所有方法降淮。
     */
    @Pointcut("execution(* com.tuacy.microservice.framework.user.manage.controller.*.*(..))")
    public void operateLog() {
    }
    
    /**
     * 前置通知:目標方法執(zhí)行之前執(zhí)行以下方法體的內容
     */
    @Before(value = "operateLog()")
    public void beforeMethod(JoinPoint jp) {
        String methodName = jp.getSignature().getName();
        System.out.println("【前置通知】the method 【" + methodName + "】");
    }
    
    
}

4.2 within

? ? ? ?"within(類型表達式)":匹配連接點所在的Java類或者包超埋。within()函數(shù)定義的連接點是針對目標類而言的搏讶。with所指定的連接點最小范圍是類。所以within能實現(xiàn)的功能霍殴,execution也能實現(xiàn)媒惕。

/**
 * 日志AOP 切面類
 */

@Aspect
@Component("logAspect")
public class LoggingAspect {


    /**
     * 匹配com.tuacy.microservice.framework.user.manage.controller包下直接子類所有方法。
     */
    @Pointcut("within(com.tuacy.microservice.framework.user.manage.controller.*)")
    public void operateLog() {
    }


    /**
     * 前置通知:目標方法執(zhí)行之前執(zhí)行以下方法體的內容
     */
    @Before(value = "operateLog()")
    public void beforeMethod(JoinPoint jp) {
        String methodName = jp.getSignature().getName();
        System.out.println("【前置通知】the method 【" + methodName + "】");
    }


}

匹配 com.tuacy.microservice.framework.user.manage.controller包下面来庭,子類的所有方法妒蔚。(不包含子孫包,如果想包含子孫包需要改為:within(com.tuacy.microservice.framework.user.manage.controller..*))月弛。

? ? ? ?在比如如果A繼承了接口B肴盏,則within("B")不會匹配到A,但是within("B+")可以匹配到A帽衙。

? ? ? ?在比如一個菜皂,匹配 所有添加了com.tuacy.microservice.framework.user.manage.annotation.LogginAnnotation注解的方法。

    @Pointcut("within(@com.tuacy.microservice.framework.user.manage.annotation.LogginAnnotation *)")
    public void operateLog() {
    }

4.3 this

? ? ? ?this 向通知方法中傳入代理對象的引用厉萝。

/**
 * 日志AOP 切面類
 */

@Aspect
@Component("logAspect")
public class LoggingAspect {

    /**
     * 匹配UserController類所有的方法
     */
    @Pointcut("execution(* com.tuacy.microservice.framework.user.manage.controller.UserController.*(..))")
    public void operateLog() {
    }

    /**
     * 前置通知:目標方法執(zhí)行之前執(zhí)行以下方法體的內容, 通過this傳入了代理對象的引用
     */
    @Before(value = "operateLog() && this(param)")
    public void beforeMethod(JoinPoint jp, UserController param) {
        System.out.println(param.toString());
        String methodName = jp.getSignature().getName();
        System.out.println("【前置通知】the method 【" + methodName + "】");
    }

}

4.4 target

? ? ? ?target 向通知方法中傳入目標對象的引用恍飘。

/**
 * 日志AOP 切面類
 */

@Aspect
@Component("logAspect")
public class LoggingAspect {

    /**
     * 匹配UserController類所有的方法
     */
    @Pointcut("execution(* com.tuacy.microservice.framework.user.manage.controller.UserController.*(..))")
    public void operateLog() {
    }

    /**
     * 前置通知:目標方法執(zhí)行之前執(zhí)行以下方法體的內容, 通過target傳入了目標對象的引用
     */
    @Before(value = "operateLog() && target(param)")
    public void beforeMethod(JoinPoint jp, UserController param) {
        System.out.println(param.toString());
        String methodName = jp.getSignature().getName();
        System.out.println("【前置通知】the method 【" + methodName + "】");
    }

}

4.5 args

? ? ? ?args 將參數(shù)傳入到通知方法中。如果有多個參數(shù)逗號隔開谴垫。

切面

/**
 * 日志AOP 切面類
 */

@Aspect
@Component("logAspect")
public class LoggingAspect {

    /**
     * 匹配UserController類所有的方法
     */
    @Pointcut("execution(* com.tuacy.microservice.framework.user.manage.controller.UserController.*(..))")
    public void operateLog() {
    }

    /**
     * 前置通知:目標方法執(zhí)行之前執(zhí)行以下方法體的內容, 通過args傳入了目標方法的參數(shù)
     */
    @Before(value = "operateLog() && args(param)")
    public void beforeMethod(JoinPoint jp, UserParam param) {
        System.out.println(param.toString());
        String methodName = jp.getSignature().getName();
        System.out.println("【前置通知】the method 【" + methodName + "】");
    }

}

目標

@RestController
public class UserController extends BaseController implements IUserControllerApi {

    private IUserService userService;

    @Autowired
    public void setUserService(IUserService userService) {
        this.userService = userService;
    }

    public ResponseDataEntity<UserInfoEntity> getUser(@RequestBody UserParam param) {
        ResponseDataEntity<UserInfoEntity> responseDataEntity = new ResponseDataEntity<>();
        try {
            responseDataEntity.setMsg(ResponseResultType.SUCCESS.getDesc());
            responseDataEntity.setStatus(ResponseResultType.SUCCESS.getValue());
            responseDataEntity.setData(userService.getUserInfo());
        } catch (Exception e) {
            e.printStackTrace();
        }

        return responseDataEntity;
    }
}

4.6 @within

? ? ? ?"@within(注解類型)":匹配類上添加了指定注解的該類的所有方法章母;注解類型也必須是全限定類型名。比如如下實例會匹配到所有添加了ClassAnnotation注解的類的所有方法翩剪。

/**
 * 日志AOP 切面類
 */

@Aspect
@Component("logAspect")
public class LoggingAspect {


    /**
     * 匹配所有添加了ClassAnnotation注解的方法
     */
    @Pointcut("@within(com.tuacy.microservice.framework.user.manage.annotation.ClassAnnotation)")
    public void operateLog() {
    }

    /**
     * 前置通知:目標方法執(zhí)行之前執(zhí)行以下方法體的內容
     */
    @Before(value = "operateLog()")
    public void beforeMethod(JoinPoint jp) {
        String methodName = jp.getSignature().getName();
        System.out.println("【前置通知】the method 【" + methodName + "】");
    }

}

4.7 @target

? ? ? ?"@target(注解類型)":@within的功能類似乳怎,但必須要指定注解接口的保留策略為RUNTIME。注解類型也必須是全限定類型名前弯。

? ? ? ?關于 @target的使用舞肆,編寫的實例代碼一直會報錯。不曉得為啥博杖。代碼如下:

/**
 * 日志AOP 切面類
 */

@Aspect
@Component("logAspect")
public class LoggingAspect {


    /**
     * 匹配所有添加了ClassAnnotation注解的方法
     */
    @Pointcut("@target(com.tuacy.microservice.framework.user.manage.annotation.ClassAnnotation)")
    public void operateLog() {
    }

    /**
     * 前置通知:目標方法執(zhí)行之前執(zhí)行以下方法體的內容
     */
    @Before(value = "operateLog()")
    public void beforeMethod(JoinPoint jp) {
        String methodName = jp.getSignature().getName();
        System.out.println("【前置通知】the method 【" + methodName + "】");
    }

}
/**
 * 添加在類上的注解
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface ClassAnnotation {
    public String value() default "";
}

4.8 @args

? ? ? ?"@args(注解列表)":匹配當前執(zhí)行的方法傳入的參數(shù)持有指定注解椿胯。注解類型也必須是全限定類型名。

? ? ? ?關于@args的使用剃根,代碼也是報錯哩盲,不曉得為啥。

/**
 * 日志AOP 切面類
 */

@Aspect
@Component("logAspect")
public class LoggingAspect {


    /**
     * 匹配所有方法參數(shù)添加了RequestBody注解的方法
     */
    @Pointcut("@args(org.springframework.web.bind.annotation.RequestBody)")
    public void operateLog() {
    }

    /**
     * 前置通知:目標方法執(zhí)行之前執(zhí)行以下方法體的內容
     */
    @Before(value = "operateLog()")
    public void beforeMethod(JoinPoint jp) {
        String methodName = jp.getSignature().getName();
        System.out.println("【前置通知】the method 【" + methodName + "】");
    }

}

4.9 @annotation

? ? ? ?"@annotation(注解類型)":匹配所有添加了指定注解類型的方法狈醉。注解類型也必須是全限定類型名廉油。比如下面的實例會匹配到所有添加了OperateLogAnnotation注解的方法。

@Aspect
@Component("logAspect")
public class LoggingAspect {

    /**
     * 匹配所有添加了LoggingAnnotation注解的方法
     */
    @Pointcut("@annotation(com.tuacy.microservice.framework.user.manage.annotation.LoggingAnnotation)")
    public void operateLog() {
    }

    /**
     * 前置通知:目標方法執(zhí)行之前執(zhí)行以下方法體的內容
     */
    @Before(value = "operateLog()")
    public void beforeMethod(JoinPoint jp) {
        String methodName = jp.getSignature().getName();
        System.out.println("【前置通知】the method 【" + methodName + "】");
    }

}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末苗傅,一起剝皮案震驚了整個濱河市抒线,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌渣慕,老刑警劉巖嘶炭,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件抱慌,死亡現(xiàn)場離奇詭異,居然都是意外死亡眨猎,警方通過查閱死者的電腦和手機抑进,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來睡陪,“玉大人寺渗,你說我怎么就攤上這事±计龋” “怎么了信殊?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長汁果。 經(jīng)常有香客問我鸡号,道長,這世上最難降的妖魔是什么须鼎? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任鲸伴,我火速辦了婚禮,結果婚禮上晋控,老公的妹妹穿的比我還像新娘汞窗。我一直安慰自己,他們只是感情好赡译,可當我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布仲吏。 她就那樣靜靜地躺著,像睡著了一般蝌焚。 火紅的嫁衣襯著肌膚如雪裹唆。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天只洒,我揣著相機與錄音许帐,去河邊找鬼。 笑死毕谴,一個胖子當著我的面吹牛成畦,可吹牛的內容都是我干的。 我是一名探鬼主播涝开,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼循帐,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了舀武?” 一聲冷哼從身側響起拄养,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎银舱,沒想到半個月后瘪匿,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體跛梗,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年柿顶,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片操软。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡嘁锯,死狀恐怖,靈堂內的尸體忽然破棺而出聂薪,到底是詐尸還是另有隱情家乘,我是刑警寧澤,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布藏澳,位于F島的核電站仁锯,受9級特大地震影響,放射性物質發(fā)生泄漏翔悠。R本人自食惡果不足惜业崖,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蓄愁。 院中可真熱鬧双炕,春花似錦、人聲如沸撮抓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽丹拯。三九已至站超,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間乖酬,已是汗流浹背死相。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留咬像,地道東北人媳纬。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像施掏,于是被迫代替她去往敵國和親钮惠。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內容

  • 本章內容: 面向切面編程的基本原理 通過POJO創(chuàng)建切面 使用@AspectJ注解 為AspectJ切面注入依賴 ...
    謝隨安閱讀 3,157評論 0 9
  • IoC 容器 Bean 的作用域 自定義作用域實現(xiàn) org.springframework.beans.facto...
    Hsinwong閱讀 2,476評論 0 7
  • AOP七芭,也就是面向方面編程或者說面向面編程素挽,是一種很重要的思想。在企業(yè)級系統(tǒng)中經(jīng)常需要打印日志狸驳、事務管理這樣針對某...
    樂百川閱讀 909評論 0 8
  • 本文基于《Spring實戰(zhàn)(第4版)》所寫预明。 在軟件開發(fā)中缩赛,散布于應用中多的功能被稱為橫切關注點(cross-cu...
    陽光的技術小棧閱讀 477評論 0 0
  • 又回到了一個人住的狀態(tài),好孤單啊撰糠。我竟然還沒有習慣這種狀態(tài)酥馍。 算了,想想有人陪的日子阅酪。想想自己心里那些好的記憶旨袒。想...
    BelovedNutan閱讀 191評論 0 0