Spring 面向切面編程

AOP,也就是面向方面編程或者說面向面編程宴合,是一種很重要的思想卦洽。在企業(yè)級系統(tǒng)中經(jīng)常需要打印日志、事務(wù)管理這樣針對某一方面的需求蚤霞,但是傳統(tǒng)的面向?qū)ο缶幊虩o法很好的滿足這些需求昧绣。因此催生了面向切面編程這樣的思想。面向切面編程贪绘,通過動態(tài)代理這樣的功能兔簇,向要執(zhí)行的方法添加鉤子边酒,能夠在不改動原方法的情況下坯认,動態(tài)添加新功能。所以在現(xiàn)代系統(tǒng)中算是一項必需的功能了陋气。Spring框架也很好的支持了AOP。

AOP的幾個術(shù)語如下议慰,詳細(xì)的使用方法會在具體使用的時候說明别凹。

  • 切面(Aspect),官方的抽象定義為“一個關(guān)注點的模塊化拍霜,這個關(guān)注點可能會橫切多個對象”,上面所說的打印日志吠裆、事務(wù)管理這樣的需求,就是切面抠蚣。
  • 連接點(JoinPoint)祝旷,程序執(zhí)行過程中的某一行為。比如說我們計劃在某個方法執(zhí)行的時候打印日志嘶窄,那么這個方法就是連接點怀跛。
  • 通知(Advice),切面對于某個連接點產(chǎn)生的動作就是通知柄冲。比如說我們上面計劃在某個方法執(zhí)行的時候打印日志吻谋,那么打印日志這件事情就是通知。通知按照執(zhí)行時機可以分為前置通知现横、后置通知等五種通知阁最。
  • 切入點(Pointcut),可以簡單地理解為正則表達(dá)式之類的東西。我們想要在哪些方法上應(yīng)用打印日志的通知,就需要一個切入點來匹配感帅。
  • 目標(biāo)對象(Target Object)实苞,被切面通知的對象就是目標(biāo)對象。

環(huán)境配置

Spring核心的依賴注入功能不需要AOP等其他組件的支持即可使用。不過反過來AOP卻需要依賴注入的支持猪半。因此我們需要添加比較多的依賴。以下是Gradle的依賴配置,為了運行后面的Hibernate例子句葵,需要Hibernate等幾個額外的包轻专。

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.12'
    compile group: 'org.springframework', name: 'spring-core', version: springVersion
    compile group: 'org.springframework', name: 'spring-context', version: springVersion
    compile group: 'org.springframework', name: 'spring-aop', version: springVersion
    compile group: 'org.springframework', name: 'spring-test', version: springVersion
    compile group: 'org.springframework', name: 'spring-orm', version: springVersion
    compile group: 'org.projectlombok', name: 'lombok', version: '1.16.12'
    compile group: 'org.hibernate', name: 'hibernate-core', version: '5.2.6.Final'
    compile group: 'mysql', name: 'mysql-connector-java', version: '5.1.40'
    compile group: 'org.apache.commons', name: 'commons-dbcp2', version: '2.1.1'

}

定義服務(wù)

要使用AOP嫂侍,我們首先需要確定要把AOP用在什么地方诡挂。這里我定義了一個小服務(wù)抄课,執(zhí)行幾個方法沦童。這幾個方法列舉了最常用的幾種使用情景氏豌,無參方法、有參方法芜繁、有返回值方法榔袋。

public class MyService {
    public void doSomething() {
        System.out.println("做一些事情...");
    }

    public void printSomething(String msg) {
        System.out.println("信息室:" + msg);
    }

    public int calculateSomething(int a, int b) {
        return a + b;
    }

    public void throwSomething() {
        throw new RuntimeException("一個異常");
    }

    public void longWork() {
        int N = 10000;
        int sum = 0;
        for (int i = 0; i < N; ++i) {
            sum += i;
        }
    }
}

然后將這個服務(wù)注冊為Spring Bean罕容。

<bean id="myService" class="yitian.learn.aop.MyService"/>

XML方式配置AOP

定義切面

我們在這里定義一個切面,這個切面包含幾個方法柱蟀,將會在我們的服務(wù)執(zhí)行前伶椿、執(zhí)行后輸出信息看彼,追蹤服務(wù)的參數(shù)廊佩、返回值和異常信息等囚聚。在編程實踐中,一個切面一般是一個類标锄,其中包含若干方法將會代理到服務(wù)方法上顽铸。

public class MyAspect {
    public void before() {
        System.out.println("在方法之前");
    }

    public void after() {
        System.out.println("在方法之后");
    }

    public void printDataFlow(int input1, int input2, int output) {
        System.out.println(
                String.format("程序輸入是:%d,%d,輸出是:%d", input1, input2, output));
    }

    public void afterThrow(Exception e) {
        System.out.println("方法拋出了" + e);
    }
}

定義好日志切面之后料皇,我們同樣需要將其配置為一個Bean谓松。

<bean id="myAspect" class="yitian.learn.aop.MyAspect"/>

要將某個Bean配置為切面還需要一步,也就是在XML配置文件中beans根節(jié)點添加如下一行践剂,引用AOP的相關(guān)規(guī)則鬼譬。

xmlns:aop="http://www.springframework.org/schema/aop"

然后在配置文件中添加如下一節(jié)。將Bean聲明為切面逊脯。所有的AOP相關(guān)配置优质,都只能編寫在<aop:config>節(jié)點中,而且順序必須按照切入點军洼、通知和切面的順序聲明巩螃。

<aop:config>
    <aop:aspect id="logAspect" ref="logAspect">

    </aop:aspect>
</aop:config>

定義切入點

切入點可以理解為正則表達(dá)式,簡單地說匕争,切入點和目標(biāo)方法之間的關(guān)系就像正則表達(dá)式和要匹配的字符串的關(guān)系一樣避乏。切入點定義了一個模式,可以匹配一個或多個目標(biāo)方法甘桑。Spring的切入點表達(dá)式使用的是AspectJ的切入點表達(dá)式語法淑际,詳細(xì)信息可以參考Spring AspectJ文檔。Spring沒有支持所有的AspectJ語法扇住,只支持了一部分春缕。

Spring AOP支持以下幾種指示符:

  • execute,匹配指定方法執(zhí)行的連接點艘蹋,這是我們最常用的一種锄贼。
  • within,匹配指定類型內(nèi)的連接點女阀。
  • this宅荤,匹配bean引用(AOP代理)是指定類型的連接點。
  • target浸策,匹配目標(biāo)對象(被代理的對象)是指定類型的連接點冯键。
  • args,匹配方法參數(shù)是指定類型的連接點庸汗。
  • @target惫确,匹配目標(biāo)對象的類被指定注解標(biāo)記的連接點。
  • @args,匹配方法參數(shù)標(biāo)記有指定注解的連接點改化。
  • @within掩蛤,匹配被指定注解標(biāo)記的類型的連接點。
  • @annotation陈肛,匹配執(zhí)行方法含有指定注解的連接點揍鸟。
  • bean,Spring AOP特有的句旱,匹配指定id或名稱的Spring Bean的連接點阳藻。

在指示符后面,需要一組括號谈撒,括號內(nèi)容是方法的匹配稚配,語法如下:

指示符(返回類型 包名.類名.方法名(參數(shù)列表) )

下面這個切入點表示的是當(dāng)yitian.learn.aop.MyService類下的返回任意值的任意名稱和任意個參數(shù)的方法執(zhí)行時。這樣這個切入點代表的就是MyService類的所有方法港华。id屬性指定切入點標(biāo)識符,expression指定切入點表達(dá)式午衰。切入點既可以定義在切面內(nèi)部立宜,也可以定義在切面外。如果定義在切面外臊岸,就可以被多個切面所共享橙数。但是必須定義在所有切面之前,順序上面已經(jīng)說了帅戒。

這里使用到了兩個通配符灯帮。星號*代表單個的任意類型和名稱,兩個點..表示任意多個名稱或參數(shù)逻住。此外還有一個通配符+钟哥,用在某個類型之后,表示該類型的子類或者實現(xiàn)了該接口的某個類瞎访。

<aop:pointcut id="myService"
              expression="execution(* yitian.learn.aop.MyService.*(..))"/>

再來幾個例子腻贰。匹配任意公有方法。

execution(public * *(..))

匹配com.xyz.someapp.trading及其子包下所有方法執(zhí)行扒秸。

within(com.xyz.someapp.trading..*)

匹配以set開頭的所有方法執(zhí)行播演。

execution(* set*(..))

匹配com.xyz.service包下的任意類的任意方法。

execution(* com.xyz.service.*.*(..))

匹配任何實現(xiàn)了com.xyz.service.AccountService接口目標(biāo)對象的切入點伴奥。

target(com.xyz.service.AccountService)

切入點還可以疊加写烤,使用&&||拾徙、!表示切入點的與或非洲炊。由于在XML配置文件中存在字符轉(zhuǎn)義現(xiàn)象,所以在XML配置中還可以使用andor选浑、not來替代上面的關(guān)系運算符蓝厌。

定義通知

切面對于某個連接點所執(zhí)行的動作就是通知。通知有以下幾種:

  • 前置通知(before)古徒,在目標(biāo)方法執(zhí)行前執(zhí)行拓提、
  • 返回后通知(after-returning),在目標(biāo)方法正常返回之后執(zhí)行隧膘。
  • 異常后通知(after-throwing)代态,在目標(biāo)方法拋出異常之后執(zhí)行。
  • 后置通知(after)疹吃,在目標(biāo)方法結(jié)束(包括正常返回和拋出異常)之后執(zhí)行蹦疑。
  • 環(huán)繞通知(around),將目標(biāo)方法包裹到切面方法中執(zhí)行萨驶。

通知將切面和目標(biāo)方法之間聯(lián)系起來歉摧。pointcut-ref屬性指定命名切入點的引用,如果不想使用命名切入點也可以使用pointcut指定切入點表達(dá)式腔呜;method指定切面中當(dāng)連接點執(zhí)行時所執(zhí)行的方法叁温。通知需要定義在切面之中。下面定義了前置通知和后置通知核畴。其他通知的定義類似膝但,寫在上面通知的括號中了。

<aop:aspect id="aspect" ref="myAspect">
    <aop:before method="before" pointcut-ref="something"/>
    <aop:after method="after" pointcut-ref="something"/>
</aop:aspect>

這樣定義之后谤草,每當(dāng)連接點執(zhí)行的時候跟束,通知隨之執(zhí)行。如果AOP的功能僅僅是這樣的話顯然沒什么作用丑孩。在通知中冀宴,我們還可以獲取目標(biāo)方法的參數(shù)和返回值。下面定義了一個通知温学,切入點是當(dāng)calculateSomething方法執(zhí)行的時候胸竞;返回值使用returning屬性指明肌括;參數(shù)在切入點表達(dá)式中使用args指明樊零;最后指定了這幾個參數(shù)在切面方法中的順序刃鳄。這樣,連接的參數(shù)和返回值就可以正確的綁定到切面方法上了箩帚。

<aop:after-returning method="printDataFlow"
                     pointcut="execution(int yitian.learn.aop.MyService.calculateSomething(int,int)) and args(input1,input2)"
                     returning="output"
                     arg-names="input1,input2,output"/>

如果要獲取方法拋出的異常真友,需要throwing屬性,這樣切面方法就可以順利獲取到異常對象了紧帕。

<aop:after-throwing method="afterThrow"
                    pointcut="execution(* yitian..MyService.throwSomething())"
                    throwing="e"/>

最后來說說環(huán)繞通知盔然。相比而言環(huán)繞通知應(yīng)該是最復(fù)雜的通知了桅打。連接點會被包裹在環(huán)繞通知方法內(nèi)執(zhí)行。如何來處理連接點的執(zhí)行和返回值呢愈案?這需要環(huán)繞通知的方法具有一些特征:

  • 必須有一個org.aspectj.lang.ProceedingJoinPoint類型的參數(shù)作為方法的第一個參數(shù)挺尾,否則無法執(zhí)行方法。
  • 環(huán)繞通知方法最好有返回值站绪,如果沒有返回值遭铺,連接點方法的返回值將會丟失。

下面我們在MyAspect類中新建一個方法恢准,用于測試連接點方法執(zhí)行時間魂挂,因為只是測試執(zhí)行時間,因此這里沒有為方法添加返回值馁筐。

public void around(ProceedingJoinPoint pjp) {
    StopWatch watch = new StopWatch();
    watch.start();
    try {
        pjp.proceed();
    } catch (Throwable throwable) {
        throwable.printStackTrace();
    }
    watch.stop();
    System.out.println(watch.shortSummary());
}

然后涂召,我們定義一個環(huán)繞通知。

<aop:around method="around"
            pointcut="execution(void yitian..MyService.longWork())"/>

這樣的話敏沉,在執(zhí)行l(wèi)ongWork方法的時候就會自動包裹在around方法中執(zhí)行果正。環(huán)繞通知主要用于事務(wù)處理等必須包裹的情形當(dāng)中。使用前面幾種通知可以實現(xiàn)功能的話就不要使用環(huán)繞通知盟迟。

定義引入

引入(Introduction)是AOP的一項功能秋泳,可以在不改變源代碼的情況下,動態(tài)的讓某個對象實現(xiàn)某個接口队萤。

首先我們需要一個接口和一個默認(rèn)實現(xiàn)。

public interface Service {
    void doService();
}
public class ServiceImpl implements Service {
    @Override
    public void doService() {
        System.out.println("實現(xiàn)了Service接口");
    }
}

然后在<aop:aspect>中添加如下一節(jié)矫钓。<aop:declare-parents>來指定一個引入要尔。types-matching屬性指定要匹配的類;implement-interface屬性指定要實現(xiàn)的接口新娜;default-impl屬性指定該接口的默認(rèn)實現(xiàn)赵辕。

<aop:declare-parents types-matching="yitian.learn.aop.MyService"
                     implement-interface="yitian.learn.aop.Service"
                     default-impl="yitian.learn.aop.ServiceImpl"/>

然后我們就可以將MyService轉(zhuǎn)換成Service接口了。

Service s = context.getBean("myService", Service.class);
s.doService();

@AspectJ配置

前面用的是XML方式配置的AOP概龄,由于Spring AOP的很多概念和類直接來自于AspectJ開源項目还惠。當(dāng)然也支持AspectJ形式的注解配置。要啟用AspectJ注解形式的配置私杜,需要在Java配置類上添加@EnableAspectJAutoProxy注解蚕键。

@Configuration
@EnableAspectJAutoProxy
public class AppConfig {

}

如果使用XML配置Spring而使用注解配置Spring AOP,需要在配置文件中添加下面一行衰粹。

<aop:aspectj-autoproxy/>

定義切面

定義切面很簡單锣光,在切面類上應(yīng)用@Aspect即可。

@Aspect
public class MyAspect {
...
}

定義切入點

定義切入點需要在切面類中定義一個空方法铝耻,方法名會作為切入點的名稱誊爹,切入點表達(dá)式使用注解聲明蹬刷。這里這個方法的作用就是充當(dāng)一個占位符,所以方法體為空频丘,這個方法返回類型必須是void办成。

@Pointcut(value = "execution(* yitian..MyService.doSomething())")
private void something() {
}

定義通知

定義通知和配置XML文件類似。這里不說了搂漠。直接上代碼迂卢。

@Aspect
public class MyAspect {
    //定義切入點
    @Pointcut("execution(* yitian..MyService.doSomething())")
    private void something() {
    }

    //定義通知
    @Before("something()")
    public void before() {
        System.out.println("在方法之前");
    }

    @After("something()")
    public void after() {
        System.out.println("在方法之后");
    }

    @AfterReturning(pointcut = "execution(* yitian..MyService.calculateSomething(..)) && args(input1,input2)",
            returning = "output", argNames = "input1,input2,output")
    public void printDataFlow(int input1, int input2, int output) {
        System.out.println(
                String.format("程序輸入是:%d,%d,輸出是:%d", input1, input2, output));
    }

    @AfterThrowing(pointcut = "execution(* yitian..MyService.throwSomething())",
            throwing = "e")
    public void afterThrow(Exception e) {
        System.out.println("方法拋出了" + e);
    }

    @Around("execution(* yitian..MyService.longWork())")
    public void around(ProceedingJoinPoint pjp) {
        System.out.println("開始計時");
        StopWatch watch = new StopWatch();
        watch.start();
        try {
            pjp.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        watch.stop();
        System.out.println(watch.shortSummary());
    }

}

可以看到使用注解配置的優(yōu)勢就是配置文件和切面類在一起状答,閱讀方便冷守。如果使用XML配置的話,要查看切面應(yīng)用了什么方法惊科,需要同時查看XML和Java代碼拍摇,比較麻煩。

此外通知還有一個順序的問題馆截,在前面沒有說明充活。如果有兩個切面的相同通知(比如都是前置通知)要應(yīng)用到某個連接點上,我們就可以定義它們之間的順序蜡娶。有兩種方法混卵,第一種是讓通知所在的切面類實現(xiàn)org.springframework.core.Ordered接口,這個接口有一個getValue()方法窖张,我們可以實現(xiàn)這個方法來確定順序幕随。第二種方法就是在切面類上應(yīng)用Order注解,并給定一個值宿接。不論用哪種方法赘淮,值較小的通知會先執(zhí)行。同一切面中的通知睦霎,執(zhí)行順序是未定義的梢卸,也就是不確定的,我們無法指定它們的執(zhí)行順序副女。

定義引入

在切面類中定義一個接口類型的字段蛤高,然后應(yīng)用DeclareParents注解并定義要引入的類和該接口的默認(rèn)實現(xiàn)。

//定義引入
@DeclareParents(value = "yitian..MyService", defaultImpl = ServiceImpl.class)
private Service service;

理解Spring AOP

Spring AOP是一個基于代理實現(xiàn)的框架碑幅,因此有一些事情需要我們注意戴陡。舉個例子,我們定義如下一個類沟涨。

public class SimplePojo {

    public void foo() {
        System.out.println("調(diào)用了foo");
        bar();
    }

    public void bar() {
        System.out.println("調(diào)用了bar");
    }
}

然后定義一個切面和兩個通知猜欺,在目標(biāo)方法之后執(zhí)行。

@Aspect
public class PojoAspect {


    @AfterReturning(pointcut = "execution(* yitian..SimplePojo.foo())")
    public void afterFoo() {
        System.out.println("代理了foo");
    }

    @AfterReturning(pointcut = "execution(* yitian..SimplePojo.bar())")
    public void afterBar() {
        System.out.println("代理了bar");
    }
}

然后我們運行一下foo方法拷窜,看看會出現(xiàn)什么情況开皿。

@Test
public void testProxy() {
    ApplicationContext context = new AnnotationConfigApplicationContext(AOPConfig.class);
    SimplePojo pojo = context.getBean("simplePojo", SimplePojo.class);
    pojo.foo();
}

結(jié)果如下:

調(diào)用了foo
調(diào)用了bar
代理了foo

我們注意到一個事實涧黄,在foo方法中調(diào)用bar方法并沒有相應(yīng)的通知執(zhí)行。由于Spring AOP是一個基于代理的框架赋荆,因此我們從ApplicationContext中獲取到的Bean其實是一個代理笋妥,因此foo方法會執(zhí)行相應(yīng)的通知。但是窄潭,foo方法調(diào)用自己類中的bar方法春宣,使用的是this引用,沒有經(jīng)過代理嫉你,因此無法觸發(fā)AOP的通知執(zhí)行月帝。這一點需要注意。如果我們希望編寫一個目標(biāo)類型幽污,讓其能夠使用Spring AOP嚷辅,那么盡量不要出現(xiàn)調(diào)用自己類中的方法的情況。由于AspectJ不是基于代理的框架距误,因此如果你使用AspectJ簸搞,就不會出現(xiàn)上面的問題。

小例子

我們來使用環(huán)繞通知配置一下Hibernate的事務(wù)管理准潭。

首先需要定義一個實體類趁俊。

@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    @NaturalId
    private String username;
    @Column(nullable = false)
    private String password;
    @Column
    private String nickname;
    @Column
    private LocalDate birthday;
}

然后添加一個用戶服務(wù),向數(shù)據(jù)庫中添加用戶刑然。這里為了使用環(huán)繞通知來進行事務(wù)管理寺擂,故意將Session寫在參數(shù)中,方便環(huán)繞通知獲取Session泼掠。

public class UserService {

    public void add(Session session, User user) {
        session.save(user);

    }
}

然后我們需要一個切面和一個環(huán)繞通知怔软,環(huán)繞通知將連接點的代碼用事務(wù)處理語句環(huán)繞。

@Aspect
public class TransactionAspect {
    @Pointcut("execution(* yitian..UserService.add(..))&&args(session,user)")
    private void addUser(Session session, User user) {
    }

    @Around(value = "addUser(session,user)", argNames = "pjp,session,user")
    public void manageTransaction(ProceedingJoinPoint pjp, Session session, User user) {
        Transaction transaction = session.beginTransaction();
        try {
            pjp.proceed(new Object[]{session, user});
            transaction.commit();
        } catch (Throwable e) {
            transaction.rollback();
        }
    }
}

當(dāng)然上面這幾個類應(yīng)該注冊為Spring Bean武鲁。

@Configuration
@EnableAspectJAutoProxy
public class HibernateConfig {
    @Autowired
    private SessionFactory sessionFactory;

    @Bean
    public SessionFactory sessionFactory() {
        final StandardServiceRegistry registry = new StandardServiceRegistryBuilder()
                .configure()
                .build();
        try {
            SessionFactory sessionFactory = new MetadataSources(registry).buildMetadata().buildSessionFactory();
            return sessionFactory;
        } catch (Exception e) {
            StandardServiceRegistryBuilder.destroy(registry);
            throw new RuntimeException(e);
        }
    }

    @Bean
    public Session session() {
        return sessionFactory.openSession();
    }

    @Bean
    public UserService userService() {
        return new UserService();
    }
}

最后來測試一下爽雄,我們運行測試方法蝠检,然后查看一下數(shù)據(jù)庫沐鼠,看是否成功插入了。

@ContextConfiguration(classes = {HibernateConfig.class})
@RunWith(SpringRunner.class)
public class HibernateTest {

    @Autowired
    private UserService userService;

    @Autowired
    private Session session;


    @Test
    public void testTransactionAspect() {
        User user = new User();
        user.setUsername("yitian");
        user.setPassword("123456");
        user.setNickname("易天");
        user.setBirthday(LocalDate.now());
        userService.add(session, user);
    }
}

參考資料

https://my.oschina.net/sniperLi/blog/491854
http://blog.csdn.net/wangpeng047/article/details/8556800

項目代碼

項目在csdn代碼庫中叹谁,見下饲梭。
https://code.csdn.net/u011054333/spring-core-sample/tree/master

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市焰檩,隨后出現(xiàn)的幾起案子憔涉,更是在濱河造成了極大的恐慌,老刑警劉巖析苫,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件兜叨,死亡現(xiàn)場離奇詭異穿扳,居然都是意外死亡,警方通過查閱死者的電腦和手機国旷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門矛物,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人跪但,你說我怎么就攤上這事履羞。” “怎么了屡久?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵忆首,是天一觀的道長。 經(jīng)常有香客問我被环,道長糙及,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任蛤售,我火速辦了婚禮丁鹉,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘悴能。我一直安慰自己揣钦,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布漠酿。 她就那樣靜靜地躺著冯凹,像睡著了一般。 火紅的嫁衣襯著肌膚如雪炒嘲。 梳的紋絲不亂的頭發(fā)上宇姚,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天,我揣著相機與錄音夫凸,去河邊找鬼浑劳。 笑死,一個胖子當(dāng)著我的面吹牛夭拌,可吹牛的內(nèi)容都是我干的魔熏。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼鸽扁,長吁一口氣:“原來是場噩夢啊……” “哼蒜绽!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起桶现,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤躲雅,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后骡和,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體相赁,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡相寇,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了钮科。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片裆赵。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖跺嗽,靈堂內(nèi)的尸體忽然破棺而出战授,到底是詐尸還是另有隱情,我是刑警寧澤桨嫁,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布植兰,位于F島的核電站,受9級特大地震影響璃吧,放射性物質(zhì)發(fā)生泄漏楣导。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一畜挨、第九天 我趴在偏房一處隱蔽的房頂上張望筒繁。 院中可真熱鬧,春花似錦巴元、人聲如沸毡咏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽呕缭。三九已至,卻和暖如春修己,著一層夾襖步出監(jiān)牢的瞬間恢总,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工睬愤, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留片仿,地道東北人。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓尤辱,卻偏偏與公主長得像砂豌,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子啥刻,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,592評論 2 353

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理奸鸯,服務(wù)發(fā)現(xiàn)咪笑,斷路器可帽,智...
    卡卡羅2017閱讀 134,651評論 18 139
  • 解釋AOP AOP(Aspect-OrientedProgramming,面向方面編程)窗怒,可以說是OOP(Obje...
    jiangmo閱讀 912評論 0 2
  • 本章內(nèi)容: 面向切面編程的基本原理 通過POJO創(chuàng)建切面 使用@AspectJ注解 為AspectJ切面注入依賴 ...
    謝隨安閱讀 3,146評論 0 9
  • Spring提供了4種類型的AOP支持: 基于代理的經(jīng)典Spring AOP映跟; 純POJO切面蓄拣; @AspectJ...
    我弟是個程序員閱讀 209評論 0 0
  • 好多天沒去逛空間了,滑到最后看見他發(fā)了動態(tài)努隙,下面是一群留言的人球恤。 99,難得秀一回恩愛荸镊,樂色咽斧,最近流行和男的...
    施與樹閱讀 168評論 0 0