熟練掌握spring框架第三篇

接上篇【熟練掌握spring框架第二篇】

bean的生命周期

參考:http://javainsimpleway.com/spring-bean-life-cycle/

這是一個(gè)比較基礎(chǔ)但是又比較高頻的面試題歼郭。如果面試官問(wèn)你spring bean的生命周期都有哪些含滴?那應(yīng)該怎樣回答呢虽另?在回答之前可以先分析一下這個(gè)題目。首先想想面試官問(wèn)這個(gè)問(wèn)題的目的是什么谢床?換位思考兄一,如果我是面試官,我希望通過(guò)這個(gè)題目了解求職者對(duì)spring框架的了解程度识腿,它是如何管理bean的出革。在整個(gè)bean對(duì)生命周期中都有哪些是我們可以參與的。常用的場(chǎng)景是什么渡讼?不同類型的bean的生命周期有什么不同嗎骂束?如果求職者這幾個(gè)問(wèn)題都能清楚的表示出來(lái),那我認(rèn)為這道面試題他pass了成箫。學(xué)習(xí)bean的生命周期目的還是為了在實(shí)際工作中可以進(jìn)行自由擴(kuò)展展箱。以滿足業(yè)務(wù)需要。那下面就從這幾個(gè)方面分析下bean的生命周期蹬昌。

先看下下面這張圖混驰,來(lái)源:http://javainsimpleway.com/spring-bean-life-cycle/

img
  1. 首先實(shí)例化bean
  2. populateBean
  3. 調(diào)用初始化方法之前首先調(diào)用所有bean有感知的方法,包括BeanNameAware皂贩,BeanClassLoaderAware栖榨,BeanFactoryAware
  4. 然后執(zhí)行BeanPostProcessorpostProcessBeforeInitialization
  5. 執(zhí)行初始化方法明刷,如果bean實(shí)現(xiàn)了InitializingBean會(huì)調(diào)用他的afterPropertiesSet方法婴栽。比如之前提到的RepositoryFactoryBeanSupport就通過(guò)afterPropertiesSet進(jìn)行repository的創(chuàng)建。
  6. 反射調(diào)用自定義init-method方法辈末。
  7. 然后執(zhí)行BeanPostProcessorpostProcessAfterInitialization

其中當(dāng)執(zhí)行到ApplicationContextAwareProcessorpostProcessBeforeInitialization時(shí)愚争,調(diào)用bean的應(yīng)用級(jí)的有感知的方法。比如ApplicationContextAware挤聘,EnvironmentAware這些轰枝。

我們熟悉的BeanPostProcessor還有AutowiredAnnotationBeanPostProcessor,用來(lái)進(jìn)行屬性自動(dòng)裝配组去。

RequiredAnnotationBeanPostProcessor狸膏,它可以確保聲明"必需"屬性的bean實(shí)際上已配置了值,否則就會(huì)爆出類似下面這樣的錯(cuò)誤

image-20210506213820813

CommonAnnotationBeanPostProcessor處理@PostConstruct@PreDestroy添怔,執(zhí)行@PostConstruct的邏輯是在它的父類InitDestroyAnnotationBeanPostProcessorpostProcessBeforeInitialization里進(jìn)行的湾戳。執(zhí)行@PreDestroy的邏輯是在InitDestroyAnnotationBeanPostProcessorpostProcessBeforeDestruction里進(jìn)行的。

所以@PostConstruct執(zhí)行的時(shí)候广料,bean的屬性已經(jīng)裝填完成了砾脑。并且只會(huì)被執(zhí)行一次,可以執(zhí)行一些需要依賴項(xiàng)的初始化工作艾杏。

@PreDestroy的原理是利用了jdk的shutdown hook韧衣,可以實(shí)現(xiàn)應(yīng)用程序的優(yōu)雅關(guān)閉。注意shutdown hook不應(yīng)該執(zhí)行耗時(shí)的操作购桑,這樣會(huì)導(dǎo)致程序不能正常退出畅铭。一般運(yùn)維寫腳本的時(shí)候都會(huì)設(shè)置一個(gè)超時(shí)時(shí)間,一旦超過(guò)勃蜘,就使用kill -9強(qiáng)制退出硕噩。

Spring管理的Bean默認(rèn)是單例的。bean的所有scope有如下這些

image-20210507120640098

來(lái)源:spring官方文檔

request session application 只存在于web應(yīng)用上下文中缭贡。websocket存在websocket環(huán)境中炉擅。這些本文不做詳細(xì)描述,singleton詳細(xì)讀者已經(jīng)很熟悉了阳惹,那么我們著重關(guān)注下prototype這個(gè)類型谍失。

@Component
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class A {  
}

定義一個(gè)簡(jiǎn)單的類,聲明為scope為prototype莹汤。spring啟動(dòng)后調(diào)用applicationContext.getBean("a")快鱼,代碼流程大致如下。

  1. 調(diào)用AbstractBeanFactorydoGetBean方法
  2. 判斷如果原型bean正在創(chuàng)建則直接拋出異常纲岭。
  3. 拿到相應(yīng)的BeanDefinition抹竹,判斷如果是Prototype類型
  4. 調(diào)用beforePrototypeCreation標(biāo)記正在創(chuàng)建
  5. createBean創(chuàng)建bean,和創(chuàng)建單例bean是同一個(gè)方法荒勇。
  6. 調(diào)用afterPrototypeCreation清除標(biāo)記

所以prototype類型的bean是不支持循環(huán)依賴的柒莉。另外由于和創(chuàng)建singletonbean是同一個(gè)方法,所以bean的所有有感知的方法也都是差不多的沽翔。一個(gè)很重要的不同就是原型bean@PreDestroy是不會(huì)執(zhí)行的兢孝。原因很簡(jiǎn)單destroy方法是通過(guò)shutdownhook調(diào)用beanFactorydestroySingletons方法實(shí)現(xiàn)的。spring沒有定義prototypebean的銷毀動(dòng)作仅偎。

更多詳細(xì)的解釋可以參考:https://bluebreeze0812.github.io/learn/2019/10/17/Spring-Destroy-Prototype-Beans/

spring 動(dòng)態(tài)代理與AOP

代理模式

image-20210507160352635

代理模式是GoF 23種Java常用設(shè)計(jì)模式之一跨蟹,隸屬于結(jié)構(gòu)型模式。一個(gè)隨處可見的應(yīng)用場(chǎng)景就是rpc框架比如dubbo里面的service調(diào)用橘沥。本地調(diào)用的service實(shí)際上是遠(yuǎn)程對(duì)象的代理對(duì)象窗轩。調(diào)用代理對(duì)象的方法實(shí)際是調(diào)用了遠(yuǎn)程對(duì)象的方法。又比如 JAVA RMI 座咆,當(dāng)然了對(duì)遠(yuǎn)程代理這里不做過(guò)多描述痢艺。今天我們要講的是spring的動(dòng)態(tài)代理仓洼。眾所周知,Spring代理實(shí)際上是對(duì)JDK代理CGLIB代理做了一層封裝堤舒。那么我們先來(lái)看下jdk和cglib代理色建。這也是烹飪spring aop這道大菜比不可少的佐料。

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

public class JdkProxyDemo {
    public interface Calculator {
        int add(int a, int b);
        int subtract(int a, int b);
    }
    public static class CalculatorImpl implements Calculator {
        @Override
        public int add(int a, int b) {
            return a + b;
        }
        @Override
        public int subtract(int a, int b) {
            return a - b;
        }
    }
    public static class ProxyFactory implements InvocationHandler {
        private final Calculator real;
        public ProxyFactory(Calculator real) {
            this.real = real;
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("before");
            Object result = method.invoke(real, args);
            System.out.println("after");
            return result;
        }
    }
    public static void main(String[] args) {
        Calculator real = new CalculatorImpl();
        ProxyFactory proxyFactory = new ProxyFactory(real);
        Calculator proxy = (Calculator) Proxy.newProxyInstance(real.getClass().getClassLoader(), new Class[]{Calculator.class}, proxyFactory);
        System.out.println(proxy.add(1, 2));
        System.out.println(proxy.subtract(2, 1));
    }
}

由上面這個(gè)簡(jiǎn)單的例子可以總結(jié)出jdk動(dòng)態(tài)代理有如下特點(diǎn)舌缤。

  1. 創(chuàng)建代理對(duì)象需要三要素:類加載器箕戳,代理對(duì)象需要實(shí)現(xiàn)的接口列表。InvocationHandler實(shí)例国撵。
image-20210507183624908
  1. 代理對(duì)象的class是com.sun.proxy.$Proxy0實(shí)現(xiàn)了Calculator接口
  2. 代理對(duì)象持有InvocationHandler實(shí)例的引用陵吸,而InvocationHandler持有被代理對(duì)象的引用。
  3. InvocationHandler的invoke方法代理了接口的所有方法介牙。你可以在被代理對(duì)象執(zhí)行前后添加邏輯壮虫,你甚至不調(diào)用代理對(duì)象的方法都可以。
  4. 代理對(duì)象需要實(shí)現(xiàn)的接口列表是必須的耻瑟。這也是jdk動(dòng)態(tài)代理最大的特點(diǎn)旨指。代理對(duì)象和被代理對(duì)象都實(shí)現(xiàn)了共同的接口。否則是無(wú)法代理的喳整。

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

字節(jié)碼生成類庫(kù)谆构,它封裝了ASM,它是一個(gè)字節(jié)碼操作框架框都,類似的框架還有javaassit搬素,大概原理就是解析.class文件然后動(dòng)態(tài)修改它。

public class CglibProxyDemo {
    public static class Calculator {
        public int add(int a, int b) {
            return a + b;
        }
    }
    public static class CalculatorInterceptor implements MethodInterceptor {
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("before add");
            Object o1 = methodProxy.invokeSuper(o, objects);
            System.out.println("after add");
            return o1;
        }
    }
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Calculator.class);
        enhancer.setCallback(new CalculatorInterceptor());
        Calculator calculator = (Calculator) enhancer.create();
        System.out.println(calculator.add(1, 2));
    }
}

由上面這個(gè)簡(jiǎn)單的例子我們可以總結(jié)出cglib動(dòng)態(tài)代理有如下特點(diǎn):

  1. 生成的代理對(duì)象的class是com.family.spring.core.CglibProxyDemo$Calculator$$EnhancerByCGLIB$$b4da3734
  2. 它是Calculator類的子類魏保。遵循繼承規(guī)則熬尺,子類不能覆蓋父類的私有方法。也就是說(shuō)私有方法是不能被代理的谓罗。
  3. MethodInterceptor定義了一個(gè)方法攔截器粱哼。這個(gè)攔截器會(huì)攔截代理類的所有可以代理的方法。你也可以決定是否調(diào)用父類真實(shí)的方法檩咱。
  4. cglib代理和jdk代理有兩個(gè)很重要的區(qū)別揭措,第一就是不需要共同的接口,第二不需要準(zhǔn)備一個(gè)被代理的對(duì)象刻蚯。

如果讀者對(duì)于代理的class結(jié)構(gòu)到底是什么樣感興趣的話绊含。也可以使用java代理技術(shù)讀取jvm里面相應(yīng)的class文件,進(jìn)行分析炊汹。

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

為什么需要AOP

軟件開發(fā)是一個(gè)演變的過(guò)程躬充,從最初的POP(面向過(guò)程程序設(shè)計(jì))到OOP(面向?qū)ο蟪绦蛟O(shè)計(jì))再到AOP(面向切面編程),未來(lái)可能還有一堆的OP,每種編程思想都是軟件開發(fā)進(jìn)化的產(chǎn)物充甚。都是為了解決特定的問(wèn)題應(yīng)運(yùn)而生的以政。那么AOP產(chǎn)生的背景是什么呢。我認(rèn)為隨著軟件系統(tǒng)的復(fù)雜化津坑,一些與核心業(yè)務(wù)邏輯無(wú)關(guān)的內(nèi)容越來(lái)越多妙蔗。比如:記錄日志,權(quán)限驗(yàn)證疆瑰,事務(wù)控制,錯(cuò)誤信息檢測(cè)昙啄。而這些邏輯又散落在程序的每一個(gè)地方穆役。這樣不僅會(huì)增加寫代碼的復(fù)雜性和工作量,還會(huì)大大增加代碼的維護(hù)成本梳凛。比如權(quán)限驗(yàn)證耿币,如果每個(gè)接口都手寫代碼去判斷當(dāng)前用戶是否有該接口的訪問(wèn)權(quán)限的話,那真的很蛋疼韧拒。所以聰明的程序員們就想把這些代碼放到同一個(gè)地方淹接,然后采取動(dòng)態(tài)植入的方式添加到業(yè)務(wù)代碼執(zhí)行前后,這樣代碼統(tǒng)一起來(lái)了叛溢,而且業(yè)務(wù)邏輯里面幾乎看不到添加的代碼塑悼,程序員就可以專心致志的進(jìn)行CRUD了,這種設(shè)計(jì)思想有個(gè)高大上的名字就是AOP楷掉,英文全稱是Aspect Oriented Programming厢蒜,維基百科管這個(gè)叫編程范式。為了讓這個(gè)設(shè)計(jì)理念更加專業(yè)化烹植,還特地引入一堆的專業(yè)術(shù)語(yǔ)斑鸦。下面就簡(jiǎn)單闡述下每個(gè)術(shù)語(yǔ)的含義。

術(shù)語(yǔ) 含義
通知 Advice 類似于前面說(shuō)的權(quán)限驗(yàn)證草雕,springaop支持的通知有:前置通知巷屿,后置通知,異常通知墩虹,最終通知嘱巾,環(huán)繞通知五種
連接點(diǎn) JoinPoint 就是允許使用通知的地方,比如說(shuō)方法連接點(diǎn)(方法執(zhí)行前后)败晴,異常連接點(diǎn)(拋出異常時(shí))等
切點(diǎn) Pointcut 織入通知的連接點(diǎn)就叫做切點(diǎn)浓冒。
切面 Aspect 切面就是通知和切點(diǎn)的結(jié)合,兩者組合一起定義了切面三要素:要做什么尖坤,何時(shí)做稳懒,何地做
織入 weaving 把切面應(yīng)用到目標(biāo)對(duì)象來(lái)創(chuàng)建新的代理對(duì)象的過(guò)程

有了上面的概念理解,我們對(duì)spring aop仍然是理論層面的场梆。那么他的實(shí)現(xiàn)是怎樣的呢墅冷。下面就以一個(gè)簡(jiǎn)單的例子一探究竟。
核心代碼:

@Aspect
@Component
public class MonitorAspect {
    @Pointcut("execution(* com.family.spring.core..*.*(..))  ")
    public void pointCut() {
    }
    @Around("pointCut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        Object result = pjp.proceed();
        stopWatch.stop();
        System.out.println("執(zhí)行" + pjp.getSignature().getName() + "共花費(fèi)了" + stopWatch.getTotalTimeMillis() + "毫秒");
        return result;
    }
}
@SpringBootApplication
@EnableAspectJAutoProxy
public class SpringAopDemoApplication implements ApplicationRunner {
    @Autowired
    private ApplicationContext applicationContext;
    public static void main(String[] args) {
        SpringApplication.run(SpringAopDemoApplication.class, args);
    }
    @Override
    public void run(ApplicationArguments args) throws Exception {
        UserService userService = (UserService) applicationContext.getBean("userService");
        userService.login();
        userService.register();
    }
}
//userService很簡(jiǎn)單或油,就定義了兩個(gè)方法: login register

程序輸出是這樣的:

執(zhí)行l(wèi)ogin共花費(fèi)了1000毫秒
執(zhí)行register共花費(fèi)了2000毫秒
執(zhí)行run共花費(fèi)了3009毫秒

分析:getBean拿到的userService肯定是代理之后的對(duì)象寞忿。那它是什么時(shí)候被代理的呢。debug發(fā)現(xiàn)在執(zhí)行bean的初始化時(shí)顶岸,會(huì)調(diào)用所有的BeanPostProcessor逐個(gè)處理腔彰。其中有一個(gè)特別的Processor是:AnnotationAwareAspectJAutoProxyCreator,而這個(gè)processor就是@EnableAspectJAutoProxy引入的辖佣。打開注解 @EnableAspectJAutoProxy的源碼發(fā)現(xiàn)霹抛,它的核心是導(dǎo)入了一個(gè)AspectJAutoProxyRegistrar(AspectJ自動(dòng)代理登記員)的類。而這個(gè)類的作用就是往注冊(cè)中心注冊(cè)AnnotationAwareAspectJAutoProxyCreator這個(gè)BeanPostProcessor卷谈。是不是和之前說(shuō)的@EnableJpaRepositories 如出一轍杯拐。線索找到了,接下來(lái)就是解刨它的postProcessAfterInitialization方法了世蔗。

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
        if (bean != null) {
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            if (this.earlyProxyReferences.remove(cacheKey) != bean) {
                return wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return bean;
}
//wrapIfNecessary就是用來(lái)生成代理對(duì)象的端逼。

繼續(xù)跟進(jìn),終于找到了進(jìn)行對(duì)象代理的罪魁禍?zhǔn)琢宋哿堋>褪俏覀兊?code>ProxyFactory 了

ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);

if (!proxyFactory.isProxyTargetClass()) {
  if (shouldProxyTargetClass(beanClass, beanName)) {
    proxyFactory.setProxyTargetClass(true);
  }
  else {
    evaluateProxyInterfaces(beanClass, proxyFactory);
  }
}

Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors);
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);

proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
  proxyFactory.setPreFiltered(true);
}

return proxyFactory.getProxy(getProxyClassLoader());

這是spring對(duì)jdk和cglib動(dòng)態(tài)代理的一個(gè)封裝類顶滩。它的getProxy里的createAopProxy方法是這樣的。

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        if (!IN_NATIVE_IMAGE &&
                (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {
            Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class: " +
                        "Either an interface or a target is required for proxy creation.");
            }
            if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                return new JdkDynamicAopProxy(config);
            }
            return new ObjenesisCglibAopProxy(config);
        }
        else {
            return new JdkDynamicAopProxy(config);
        }
}

翻譯成自然語(yǔ)言就是optimize芙沥,proxyTargetClass诲祸,被代理的類沒有接口這三個(gè)條件其中任何一個(gè)成立,就有機(jī)會(huì)走cglib動(dòng)態(tài)代理而昨,否則都是走jdk動(dòng)態(tài)代理救氯。另外就算判斷有機(jī)會(huì)走cglib的話,如果目標(biāo)類是接口還是會(huì)走jdk動(dòng)態(tài)代理歌憨。下面看下sping aop中關(guān)于切面的抽象

image-20210508145708694

使用ProxyFactory代理對(duì)象着憨,是必須要添加通知的。如果沒有通知就好比代理對(duì)象收了錢务嫡,但是啥事也沒干甲抖。一種簡(jiǎn)單的添加方式是,傳入一個(gè)MethodInterceptor心铃,實(shí)現(xiàn)攔截准谚。

proxyFactory.addAdvice((MethodInterceptor) invocation -> {
      System.out.println("before");
      Object result = invocation.proceed();
      System.out.println("after");
      return result;
});

但是更高級(jí)的方式就是添加Advisor,可以翻譯為顧問(wèn)去扣,讓顧問(wèn)告訴我通知是什么柱衔?spring內(nèi)置了一個(gè)強(qiáng)大的顧問(wèn),名為InstantiationModelAwarePointcutAdvisorImpl,它的getAdvice方法唆铐,可以動(dòng)態(tài)的返回不同類型的通知哲戚。詳見:ReflectiveAspectJAdvisorFactorygetAdvice方法。前面說(shuō)的那個(gè)BeanPostProcessor正是添加了這個(gè)顧問(wèn)實(shí)現(xiàn)了環(huán)繞通知艾岂。

未完待續(xù)顺少,更多內(nèi)容請(qǐng)關(guān)注【熟練掌握spring框架】第四篇

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市王浴,隨后出現(xiàn)的幾起案子脆炎,更是在濱河造成了極大的恐慌,老刑警劉巖氓辣,帶你破解...
    沈念sama閱讀 222,183評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件腕窥,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡筛婉,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門癞松,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)爽撒,“玉大人,你說(shuō)我怎么就攤上這事响蓉∷段穑” “怎么了?”我有些...
    開封第一講書人閱讀 168,766評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵枫甲,是天一觀的道長(zhǎng)源武。 經(jīng)常有香客問(wèn)我,道長(zhǎng)想幻,這世上最難降的妖魔是什么粱栖? 我笑而不...
    開封第一講書人閱讀 59,854評(píng)論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮脏毯,結(jié)果婚禮上闹究,老公的妹妹穿的比我還像新娘。我一直安慰自己食店,他們只是感情好渣淤,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著吉嫩,像睡著了一般价认。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上自娩,一...
    開封第一講書人閱讀 52,457評(píng)論 1 311
  • 那天用踩,我揣著相機(jī)與錄音,去河邊找鬼。 笑死捶箱,一個(gè)胖子當(dāng)著我的面吹牛智什,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播丁屎,決...
    沈念sama閱讀 40,999評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼荠锭,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了晨川?” 一聲冷哼從身側(cè)響起证九,我...
    開封第一講書人閱讀 39,914評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎共虑,沒想到半個(gè)月后愧怜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,465評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡妈拌,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評(píng)論 3 342
  • 正文 我和宋清朗相戀三年拥坛,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片尘分。...
    茶點(diǎn)故事閱讀 40,675評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡猜惋,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出培愁,到底是詐尸還是另有隱情著摔,我是刑警寧澤,帶...
    沈念sama閱讀 36,354評(píng)論 5 351
  • 正文 年R本政府宣布定续,位于F島的核電站谍咆,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏私股。R本人自食惡果不足惜摹察,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評(píng)論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望庇茫。 院中可真熱鬧港粱,春花似錦、人聲如沸旦签。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)宁炫。三九已至偿曙,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間羔巢,已是汗流浹背望忆。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工罩阵, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人启摄。 一個(gè)月前我還...
    沈念sama閱讀 49,091評(píng)論 3 378
  • 正文 我出身青樓稿壁,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親歉备。 傳聞我的和親對(duì)象是個(gè)殘疾皇子傅是,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評(píng)論 2 360

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

  • 前言 熟練掌握這個(gè)詞相信很多同行在寫簡(jiǎn)歷的時(shí)候都用到過(guò)。熟練掌握這四個(gè)字是根據(jù)每個(gè)人水平不一樣蕾羊,理解也不一樣喧笔。比如...
    java論劍閱讀 388評(píng)論 0 0
  • 156、Spring MVC的工作原理是怎樣的龟再? 答:SpringMVC的工作原理如下圖所示: ① 客戶端的所有請(qǐng)...
    sherlock_6981閱讀 1,086評(píng)論 0 3
  • 接上篇【熟練掌握spring框架第一篇】 spring依賴注入是怎么實(shí)現(xiàn)的 依賴注入的方式有哪些 基于構(gòu)造器注入 ...
    java論劍閱讀 225評(píng)論 0 0
  • 一书闸、Spring簡(jiǎn)介 1.1 spring的七個(gè)模塊 (1)Spring Core: Core封裝包是框架的最基礎(chǔ)...
    這一刻_776b閱讀 215評(píng)論 0 0
  • 今天感恩節(jié)哎,感謝一直在我身邊的親朋好友利凑。感恩相遇浆劲!感恩不離不棄。 中午開了第一次的黨會(huì)哀澈,身份的轉(zhuǎn)變要...
    迷月閃星情閱讀 10,572評(píng)論 0 11