03.Spring的 IOC 和 DI

IOC的概念和作用

这两行代码就明显的揭露出IOC的含义

//  private IAccountDao dao = new AccountDaoImpl();
    private IAccountDao accountDao = (IAccountDao) BeanFactory.getBean("accountDao");

这两种代码代表两种截然不同创建对象的方式
当我们用new创建对象的时候 我们APP直接和资源取得联系 他们直接有必然的联系 资源独立和应用独立变得很难 它有明显的依赖关系
当我们用第二种方式创建对象的时候 APP断开了与资源的联系 而是找工厂要资源 让工厂与资源取得联系 并把想要的对象转给应用 从而实现了资源与应用必然的依赖关系
基于这种思想 就是我们所说的IOC

IOC

为什么叫控制反转而不是降低依赖呢?
我们在编写Service的时候 用new 或者 工厂 都是自己决定的AccountServiceImpl 这个类可以有自主使用的权力 它在寻找Dao的时候可以自主选择自己想要的Dao的(通过 new)
但是它把自主选择Dao的权利交给了BeanFactory 然后由固定的名称找到bean对象 这个bean对象是不是我们能用的 我们就不得而知了 我们不能自主控制 通过 控制权发生了转移 所以是控制反转
带来的好处就是 降低耦合

Spring中IOC的前期准备

IOC只能解耦 降低程序间的依赖关系
前面讲了工厂模式来解耦 现在使用配置的方式来实现这些
准备Spring的开发包
下载地址: https://repo.spring.io/libs-release-local/org/springframework/spring/
打开它可以看见目录结构

spring的开发包

doc:API和开发规范
libs:jar包和源码
schema:约束
libs

spring基于XML的IOC环境搭建和入门

新建一个工程


新建一个工程

之前代码里的dao和service都能用,copy过来,删掉Factory,原来使用工厂创建对象 改为用new创建


copy之前的代码

搭建环境
pom.xml

导入denpendency以后 多了这些jar包 spring把你可能会用到的jar包都导进来了


image.png

show dependencies

通过 IDEA maven 的 show dependencies 功能 可以看到 beans core aop expreesion 这四个核心容器
简单来说核心容器就是个map 里面放着封装的对象
show dependencies

仍然需要一个配置文件,所以创建beans.xml,然后添加约束
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

接下来让对象的创建让spring来管理


beans.xml

接下来 在Client这个类中,之前的代码需要改造下.
这个类要做到两件事:
1.获取核心容器对象
2.根据ID获取Beans
获取核心容器的 ApplicationContext类 有两个实现类 分别是
①ClassPathXmlApplicationContext
②FileSystemXmlApplicationContext
这两个都是基于Xml配置的


Spring中工厂的类结构图
public class Client {
    /**
     * 获取核心容器对象并据ID获取对象
     * @param args
     */
    public static void main(String[] args) {
        //获取核心容器对象
        ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
        //根据id获取Bean对象
        IAccountService as = (IAccountService) ac.getBean("accountService");
       IAccountDao adao = ac.getBean("acccountDao",IAccountDao.class);
        System.out.println(as);
        System.out.println(adao);
    }
} 

getBean的另外一种方式,传入一字节码,根据字节码来获取对象类型,另外一种是Object类型 我们自己强转.


getBean

ApplicationContext的三个实现类

①ClassPathXmlApplicationContext:它可以加载类路径下的所有配置文件,要求配置文件必须在类路径下.不在的话加载不了
②FlieSystemXmlApplicationContext:它可以加载磁盘任意路径下的配置文件(必须要有访问权限)
③AnotationXmlApplicationContext:它是用注解创建容器的
所以用FileSystemXmlApplicationContext来获取核心容器的话,改动仅需把

ApplicationContext ac = new FileSystemXmlApplicationContext("D:\\WorkSpace\\day01_eesy_03spring\\src\\main\\resources\\beans.xml");

这里改动即可

public class Client {
    /**
     * 获取核心容器对象并据ID获取对象
     * @param args
     */
    public static void main(String[] args) {
        //获取核心容器对象
        //ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
        ApplicationContext ac = new FileSystemXmlApplicationContext("D:\\WorkSpace\\day01_eesy_03spring\\src\\main\\resources\\beans.xml");
        //根据id获取Bean对象
        IAccountService as = (IAccountService) ac.getBean("accountService");
        IAccountDao adao = ac.getBean("accountDao",IAccountDao.class);
        System.out.println(as);
        System.out.println(adao);
    }
}

BeanFactory和ApplicationContext的区别
核心容器的两个接口引发出的问题
ApplicationContext:它在构建核心容器的时候,采用的立即加载的方式,也就是说只要一读取完配置文件就马上加载.
BeanFactory:它在构建核心容器的时候,采用的是延迟加载的方式,也就是说什么时候根据id获取对象了,什么时候才创建.
验证 ApplicationContext 的加载:
在Service中增加一个构造函数

public class AccountServiceImpl implements IAccountService {
    /**
     * 模拟保存账户
     */
    private IAccountDao accountDao = new AccountDaoImpl();

    public AccountServiceImpl() {
        System.out.println("AccountServiceImpl创建了");
    }
    @Override
    public void saveAccount() {

    }
}

在Client中打上断点 可以看到刚读取完配置文件,对象就被创建了


ApplicationContext

BeanFactory接口:
首先 先定义BeanFactory factory = null; Alt + Ctrl + B 可以看见BeanFactory的实现类,我们选择了 XmlBeanFactory,虽然它已经过时了


XmlBeanFactory

于是 BeanFactory factory = new XmlBeanFactory();
XmlBeanFactory 需要一个参数对象 ,ctrl + 左键 可以看到 需要一个Resource对象

打开类视图

查看实现类,其实ctrl+alt+B 就能看到

ctrl+alt+B的查看方式

查看类的实现 是一个很好的思维方式,从老师这里学到的.
选择了 ClassPathResource 我们需要去类路径寻找我们的bean,xml
再往后的步骤都一样了

 public static void main(String[] args) {
      /*--------------------BeanFactory------------------------*/
        Resource resource = new ClassPathResource("beans.xml");
        BeanFactory factory = new XmlBeanFactory(resource);
        //通过id获取Bean对象
        IAccountService as = (IAccountService) factory.getBean("accountService");
        System.out.println(as);
    }

打断点 看执行的时候什么时候创建对象 可以看到 再读完配置文件 配置好工厂后 都没有创建好对象 通过id获取Bean对象的时候 才真正创建了对象


断点执行

所以两种创建对象的时间点是不一样的

立即加载 在工厂模式下 Service和Dao由于没有类成员,不存在线程安全的问题,所以在此基础上,可以直接选用单例模式创建对象,既然是单例模式,对象只会创建一次,所以可以使用立即加载的方式.只要容器创建,就马上创建对象,适用于单例对象.

延迟加载 什么时候用,什么时候才真正的创建对象 适用于多例模式

如果在一加载容器的时候就创建对象,第二次使用的时候又再创建一次对象,不如在什么时候用的时候什么时候创建更合适

Spring 可以根据配置上的不同 可以改变对象创建的方式
BeanFactory 是个顶层接口 功能不是那么完善 实现类和子接口会在BeanFactory的功能上进行拓展,所以实际开发中使用applicationContext比较多

applicationContext 如何判定单例还是多例

spring中bean的细节之三种创建Bean对象的方式

准备一个新的工程


创建new Project

找到03里的一些源码直接复制到04工程中,bean.xml也复制一份.删掉dao.(为了更简洁)

Spring对Bean的管理细节

  • 创建Bean的三种方式
    • 使用默认构造函数创建
      在Spring的配置文件中 使用bean标签中 配以id和class属性以后 没有其他的属性和标签时 采用的就是使用默认构造函数创建对象 此时如果类中没有默认的构造函数 则对象无法创建.
      在原来的代码中 已存在 AccountServiceImpl类的构造函数 在原有的默认构造函数上添加一个变量 让它不再是默认的构造函数 此时点击运行 报错信息为:No default constructor found;


      验证默认构造函数创建Bean对象

      ,
      这个时候在bean,xml 中 也可以看到配置文件中的报错 它没有找到这个类的默认构造函数


      beam,xml中也在报错
    • 使用普通工厂中的方法创建对象(使用某个类中的方法创建对象并存入Spring容器
/**
*     模拟一个工厂类(该类存在于jar包中 我们无法通过修改源码的方式来提供默认构造函数),,
*/
public class InstanceFactory {
   public IAccountService getAccountService(){
     return new AccountServiceImpl();
   }
}

模拟一个工厂类(该类存在于jar包中 我们无法通过修改源码的方式来提供默认构造函数)
在实际开发中 有可能遇上别人写好的类 这中类存在jar包中 属于字节码文件 我们无法修改 同时里面可能提供一个方法,比如说 getAccountService 这个方法可以得到一个 accountService ,如果这个类存在与Jar包,如何获得这个service对象?
在bean.xml中,我们通过instanceFactory这个id 通过反射创建这个类的对象 ,但我们是需要用这个工厂对象吗?不是要用这个类中方法的返回值的对象吗?

       <bean id="instanceFactory" class="itheima.factory.InstanceFactory" ></bean>

我们要用的是accoutService,只不过accountService不再是通过我们accountServiceImpl来得到的 ,我们要用工厂类的方法 于是,完整版bean,xml的配置是

    
       <bean id="instanceFactory" class="itheima.factory.InstanceFactory" ></bean>
       <!--指定工厂beans 和 方法-->
       <bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>

  • 使用静态工厂中的静态方法创建对象(或者某个类中的静态方法创建对象并存入spring容器)
    Copy一份InstanceFactory的代码,只需要把方法 加一个static的修饰即可.
public class StaticFactory {
    public static IAccountService getAccountService(){
      return new AccountServiceImpl();
    }
}

此时在beam.xml中,通过bean 的id反射得到实例对象是staticFactory,而不是service的实例,所以需要配置factory-method 这个属性来获得这个方法返回的service实例

    <bean id="accountService" class="itheima.factory.StaticFactory" factory-method="getAccountService"></bean>
  • Bean对象的作用范围
    • bean的作用范围:
      bean标签的scope属性
      作用:指定bean的作用范围

取值:

  • singleton:单例的(默认的)(常用)
  • prototype:多例的(常用)
  • request:作用于web的请求请求范围
  • session:作用于wdb的会话的请求范围
  • globe-session:作用于集群环境的范围
    单例的
    <bean id="accountService" class="itheima.factory.StaticFactory" factory-method="getAccountService" scope="singleton"></bean>

多例的

    <bean id="accountService" class="itheima.factory.StaticFactory" factory-method="getAccountService" scope="singleton"></bean>

在Client中再实例化一个对象,然后看看单例与多例的区别

    public static void main(String[] args) {
        Resource resource = new ClassPathResource("beans.xml");
        BeanFactory factory = new XmlBeanFactory(resource);
        //通过id获取Bean对象
        IAccountService as = (IAccountService) factory.getBean("accountService");
        IAccountService as1 = (IAccountService) factory.getBean("accountService");
        System.out.println(as == as1);
        //as.saveAccount();
    }
}

单例的结果:

AccountServiceImpl创建了
true

多例的结果:

AccountServiceImpl创建了
AccountServiceImpl创建了
false
  • Bean对象的生命周期
  • 单例的
    1.创建:容器创建时 对象出生
    2.活着:只要容器还在 对象一直活着
    3.销毁:容器销毁,对象消亡.
    在 AccountServiceImpl 中 新增 init() 和destory()方法

public class AccountServiceImpl implements IAccountService {
    /**
     * 模拟保存账户
     */

    public AccountServiceImpl() {
        System.out.println("AccountServiceImpl创建了");
    }
    @Override
    public void saveAccount() {
        System.out.println("service中的saveAccount执行了");
    }
    public void init(){
        System.out.println("对象初始化了");
    }
    public  void destory(){
        System.out.println("对象销毁了");
    }
}

在Client中执行

  public static void main(String[] args) {
        Resource resource = new ClassPathResource("beans.xml");
        BeanFactory factory = new XmlBeanFactory(resource);
        //通过id获取Bean对象
        IAccountService as = (IAccountService) factory.getBean("accountService");
        //IAccountService as1 = (IAccountService) factory.getBean("accountService");
        //System.out.println(as == as1);
        as.saveAccount();
    }
}

得到结果:

AccountServiceImpl 创建了
AccountServiceImpl 对象初始化了
service中的saveAccount执行了

为什么desrory方法没有执行 main方法是一切应用程序的入口 当main方法结束后 当前应用中线程占用的内存全部释放 也包括我们的容器 但是此时还没有调用销毁方法 就已经释放了内存
如果想让销毁方法出现 需要手动释放
(然后我发现我的Client中使用的是BeanFactory这个接口来获取核心容器的,下面改成为用ApplicationContext的方式)

 public static void main(String[] args) {
        //获取核心容器对象
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("beans.xml");
        //获取Bean对象
        IAccountService accountService = (IAccountService) classPathXmlApplicationContext.getBean("accountService");
        accountService.saveAccount();
        //手动关闭容器
        classPathXmlApplicationContext.close();
    }
}

输出结果

AccountServiceImpl 创建了
AccountServiceImpl 对象初始化了
service中的saveAccount执行了
AccountServiceImpl对象销毁了
![image.png](https://upload-images.jianshu.io/upload_images/13543313-4d005be8bc2641b2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

如果 以这样的方式获取核心容器对象,是得不到close方法的.

        ApplicationContext ApplicationContext = new ClassPathXmlApplicationContext("beans.xml");

因为我们把ClassPathXmlApplicationContext看成了接口类型,如果看成父类对象的时候,只能调用父类方法,(多态性)所以要让这个对象是自己的对象.
所以用了ClassPathXmlApplicationContext自己的对象

 ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("beans.xml");
  • 多例的
    1.创建:使用的时候创建
    2.活着:在使用过程中就一直活着
    3.销毁:当对象长时间不用且没有别的对象引用时,由Java的垃圾回收期来执行回收.

spring的依赖注入

  • 依赖注入是指 Dpendency Injection

  • IOC的作用 是降低程序间的耦合
    依赖关系的管理 是指依赖关系以后都交给spring来维护,在当前类所需要用到其他类都由spring为我们提供,我们只需要在配置文件中说明依赖关系的维护,这就称之为依赖注入

  • 依赖注入

    • 能注入的数据有三类
      • 基本类型和String
      • 其他bean类型 (在配置文件中或者注解配置过的bean
      • 复杂类型/集合类型
  • 注入的方式有三种

    • 构造函数注入

    • 由set方法提供

    • 由注解提供

      • 构造函数注入
        使用的标签是 constructor-arg
        标签出现的位置:bean标签内部
        标签中的属性
        type :用于要指定所注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型(如果参数1和参数3是同一类型,它不会知道是赋值给哪一个参数,所以不能独立的实现注入的功能)
        index :用于指定要注入的数据给构造函数中指定位置的参数赋值,索引的位置是从0开始(但这个方式要记住参数的位置,所以有点麻烦)
        name : 用于指定给构造函数中指定名称的参数赋值(常用)
        ==================以上三个用于指定给构造函数中哪个参数赋值===================
        value:用于提供基本类型和String类型的数据
        ref:用于指定其他的bean类型数据,它指的就是在spring的Ioc核心容器中出现过的bean对象.
    优势:在获取bean对象时,注入数据是必须的操作,否则对象无法创建成功
    弊端:改变了类或者bean对象的实例化方式,使我们在创建对象时如果用不到这些数据也必须提供
    

老规矩,新建一个工程,spring01_eesy_05DI,copy上一个工程的代码.先在AccountServiceImpl中创建构造函数

public class AccountServiceImpl implements IAccountService {
    private String name;
    private Integer age;
    private Date birthday;
    public AccountServiceImpl() {
        System.out.println("AccountServiceImpl 创建了");
    }
    public AccountServiceImpl(String name,Integer age,Date birthday) {
        this.name = name;
        this.age = age;
        this.birthday = birthday;
        System.out.println("AccountServiceImpl 创建了"+name+"=="+age+"==="+birthday);
    }

在bean,xml中,为参数赋值
第一个参数

    <constructor-arg name="name" value="小强"></constructor-arg>

第二个参数赋值的时候,赋值的12,和Interge类型并不匹配,在xml中 value 的值都是字符串,是spring它把这些数据类型帮我们转换了.


image.png

第三个参数 Date 赋值,如果写成2019-09-22 是无法为Date类型赋值的,因为它是字符串类型
所以构造函数赋值,只能赋值基本类型和字符串

    <constructor-arg name="birthday" value="2019-09-2"></constructor-arg>

那要如何为Date类型赋值呢?可以用引用.
我们可以配置一个日期对象,它会通过class的全限定名反射创建对象并存入spring容器中,通过now id取出

<bean id ="now" class="java.util.Date"></bean>

所以 beam.xml中

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
   <bean id="accountService" class="itheima.service.impl.AccountServiceImpl">
   <constructor-arg name="name" value="小强"></constructor-arg>
       <constructor-arg name="age" value="12"></constructor-arg>
       <constructor-arg name="birthday" ref="now"></constructor-arg>
   </bean>
    <bean id ="now" class="java.util.Date"></bean>
</beans>
  • Set方法注入(更常用)

    • 涉及的标签:property
    • 出现的位置:bean标签内部
    • 标签的属性:
      • name:用于指定注入时所调用的set方法名称,
      • value:用于提供基本类型和Sting类型的数据
      • ref:用于指定其他bean类型数据.它指的就是在spring的Ioc核心容器中出现过的bean对象.
        优势:创建对象时没有明确的限制,可以直接使用默认构造函数
        弊端:如果有某个成员必须有值,则获取对象是有可能set方法方法没有执行.

    Copy一个AccountServiceImpl,为成员变量生成set方法.

public class AccountServiceImpl2 implements IAccountService {
    private String name;
    private Integer age;
    private Date birthday;

    public AccountServiceImpl2() {
        System.out.println("AccountServiceImpl2 创建了");
    }
    public void setName(String name) {
        this.name = name;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public void saveAccount() {
        System.out.println("service中的saveAccount执行了"+name + age + birthday);
    }
}

在Beans.xml中,写法如下

  <bean id="now" class="java.util.Date"></bean>
    <bean id="accountService2" class="itheima.service.impl.AccountServiceImpl2">
        <property name="name" value="xiaoming"></property>
        <property name="age" value="18"></property>
        <property name="birthday" ref="now"></property>
    </bean>
  • name:用于指定注入时所调用的set方法名称,如果把setName 改成为setUsername的话,beam.xml中的自动联想也从name变成username,由此可见name属性用于指定注入时所调用set方法名称,

执行结果

AccountServiceImpl2 创建了
service中的saveAccount执行了xiaoming18Mon Sep 23 10:35:44 GMT+08:00 2019
  • 复杂类型/集合类型的注入
    再复制一个accountserviceImpl3,加上数组,list,map,set.properties等数据的成员变量,并生成set方法.
public class AccountServiceImpl3 implements IAccountService {
    private String[] mystrs;
    private List<String> mylist;
    private Map<String, String> mymap;
    private Set<String> myset;
    private Properties myprops;

    public AccountServiceImpl3() {
        System.out.println("AccountServiceImpl2 创建了");
    }

    public void setMystrs(String[] mystrs) {
        this.mystrs = mystrs;
    }

    public void setMylist(List<String> mylist) {
        this.mylist = mylist;
    }

    public void setMymap(Map<String, String> mymap) {
        this.mymap = mymap;
    }

    public void setMyset(Set<String> myset) {
        this.myset = myset;
    }

    public void setMyprops(Properties myprops) {
        this.myprops = myprops;
    }

    public void saveAccount() {
        System.out.println("service中的saveAccount执行了");
        //数组类型需要tostring方法转换一下 否则直接打印是内存地址
        System.out.println(Arrays.toString(mystrs));
        System.out.println(mylist);
        System.out.println(mymap);
        System.out.println(myset);
        System.out.println(myprops);
    }
}

在beam.xml中,因为都不是基本类型和String类型,所以<property>标签里的value赋值也不再有意义

 <property name="list" value="xiaoming"></property>

<properties>标签内,有更多的子标签提供使用.


image.png

array类型,array标签,value赋值

        <property name="mystrs">
            <array>
                <value>AAA</value>
                <value>BBB</value>
                <value>CCC</value>
            </array>
        </property>

list类型,list标签,value赋值

        <property name="mylist">
            <list>
                <value>AAA</value>
                <value>BBB</value>
                <value>CCC</value>
            </list>
        </property>

set类型,set标签,value赋值

        <property name="mylist">
            <list>
                <value>AAA</value>
                <value>BBB</value>
                <value>CCC</value>
            </list>
        </property>

其中,如果把list,array,set的标签呼唤,也能正常运行,

map类型,用map标签,里面有entry标签,entry有key和value两个属性.

<property name="mymap">
            <map>
                <entry key="AAA" value="111"></entry>
                <entry key="BBB" value="222"></entry>
                <entry key="CCC" value="333"></entry>
            </map>
        </property>

\之前这样的写法是有问题的,输出的结果格式会不一样,然后发现自己写错了

 <property name="myset">
            <set>
                <value>
                    AAA,BBB,CCC
                </value>
            </set>
        </property>

properties类型 ,里面有<props>标签,props里有<prop>的子标签,赋值只有一个属性,是<key>

  <property name="myprops">
            <props>
                <prop key="AAA">aaa</prop>
                <prop key="BBB">bbb</prop>
                <prop key="CCC">ccc</prop>
            </props>
        </property>

如果map标签与prop标签呼唤,执行结果也不会有问题.

      <property name="myprops">
            <map>
                <entry key="AAA" value="111"></entry>
                <entry key="BBB" value="222"></entry>
                <entry key="CCC" value="333"></entry>
            </map>
        </property>
        <property name="mymap">
            <props>
                <prop key="AAA">aaa</prop>
                <prop key="BBB">bbb</prop>
                <prop key="CCC">ccc</prop>
            </props>
        </property>
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容