spring框架

IOC

声明一个简单的bean

第一个例子:
首先设置一个接口Perofrmance表示参赛者。

package com.moonlit.myspring;
public interface Performer {    
      void perform() throws PerformanceException;
}

创建一个Juggler(杂技师)类继承Performer表示参赛者是杂技师。

package com.moonlit.myspring;
public class Juggler implements Performer {    
private int beanBags = 3;    
public Juggler() {
    }    
    public Juggler(int beanBags) {        
    this.beanBags = beanBags;
    }    
    public void perform() throws PerformanceException {
        System.out.println("JUGGLING " + beanBags + " BEANBAGS");
    }
}

在spring-idol.xml配置文件中定义一个名为duke的bean,他对应Juggler类(把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-3.0.xsd">    
  <bean id="duke" class="com.moonlit.myspring.Juggler" />    
</beans>

测试代码:

package com.moonlit.practice;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.moonlit.myspring.PerformanceException;
import com.moonlit.myspring.Performer;
public class FirstBean {    
public static void main(String[] args) throws PerformanceException {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-idol.xml");
        Performer performer = (Performer) context.getBean("duke");
        performer.perform();
    }
}

运行结果如下:

JUGGLING 3 BEANBAGS

理解:首先定义了一个接口Performer,然后写了一个类Juggler继承自Peformer,Juggler有一个私有成员变量beanBags,他的默认值是3,然后Juggler实现了Performer的perform方法,方法的输出带有beanBags的数量。
然后在测试的程序中,通过ApplicationContext类型对象加载了spring-idol.xml文件的内容,而在xml文件中定义了名为"duke"的bean,然后刚好就用到了。
然后bean返回的是一个Juggler,所以将

Performer performer = (Performer) context.getBean("duke");

改成

Juggler performer = (Juggler) context.getBean("duke");

也是可以的,但是在这里想看的效果是通过application context返回的是不是一个Juggler,因为通过输出的结果就可以知道了,所以这里用(Performer),对中输出的效果显示bean对应的Performer真的是一个Juggler,这就是通过xml定义一个bean并通过application context获得这个bean对象的整个过程。

构造器注入

之前讲到的名为"duke"的bean有一个私有成员变量beanBags代表这个杂技师bean的一次性能够抛出的最多的数量,Juggler有一个构造函数,构造函数的第一个参数(这里只有一个参数)beanBags是一个整型的值,用于传递给Juggler的私有成员变量beanBags。
构造器注入的方法是:在bean中添加一个constructor-arg(如果构造函数的参数有两个,那就添加两个constructor-arg)。
在spring-idol.xml中修改bean "duke"如下:

<bean id="duke" class="com.moonlit.myspring.Juggler" >
    <constructor-arg name="beanBags" value="15" />  
  </bean>

再次运行FirstBean程序,输出如下:

JUGGLING 15 BEANBAGS

可以看到通过构造器诸如已经把duke的beanBags改为了15。
构造函数中的参数可能不是一个基础类型的变量,而可能是一个变量,这个时候只要把constructor-arg的value改成ref即可,ref对应的值需要被声明称一个bean元素。
使用一个会唱歌的杂技师PoeticJuggler类来演示,PoeticJuggler继承自Juggler,它具有一个Poem类型的私有成员变量poem,代表他要朗诵的诗歌。
Poem类:

package com.moonlit.myspring;
public interface Poem 
{    
  void recite();
}

定义一首名为Sonnet29的类用于表示名为一首sonnet29的诗:http://shakespeare-online.com/sonnets/29.html
Sonnet29实现了Poem接口。

package com.moonlit.myspring;
public class Sonnet29 implements Poem {    
private static String[] LINES = {
            "When, in disgrace with fortune and men's eyes,",
            "I all alone beweep my outcast state,",
            "And trouble deaf heaven with my bootless cries,",
            "And look upon myself, and curse my fate,",
            "Wishing me like to one more rich in hope,",
            "Featur'd like him, like him with friends possess'd,",
            "Desiring this man's art and that man's scope,",
            "With what I most enjoy contented least;",
            "Yet in these thoughts myself almost despising,",
            "Haply I think on thee, and then my state,",
            "Like to the lark at break of day arising",
            "From sullen earth, sings hymns at heaven's gate;",
            "For thy sweet love remember'd such wealth brings",
            "That then I scorn to change my state with kings.",
    };    

    public Sonnet29() {
    }   

     public void recite() {        
     for (String line : LINES) 
            System.out.println(line);
    }
}

有了Poem和他的一个实现类Sonnet29之后,开始来写PoeticJuggler,他继承自Juggler并且有一个Poem类型私有成员变量poem。

package com.moonlit.myspring;
public class PoeticJuggler extends Juggler {    
private Poem poem;    
public PoeticJuggler(Poem poem) {        
super();        
this.poem = poem;
    }    
    public PoeticJuggler(int beanBags, Poem poem) {        
    super(beanBags);        
    this.poem = poem;
    }    
    public void perform() throws PerformanceException {        
    super.perform();
        System.out.println("While reciting...");
        poem.recite();
    }
}

并且,需要在xml文件中声明Sonnet29和PoeticJuggler类对应的bean。

<bean id="sonnet29" class="com.moonlit.myspring.Sonnet29" />
  <bean id="poeticDuke" class="com.moonlit.myspring.PoeticJuggler">
    <constructor-arg value="16" />
    <constructor-arg ref="sonnet29" />    
  </bean>

可以看到,"poeticDuke"使用了两个constructor-arg来声明参数,第一个参数使用value,第二个参数使用ref,Sonnet29类型的bean--"connet29"。
使用测试程序查看效果:

package com.moonlit.practice;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.moonlit.myspring.PerformanceException;
import com.moonlit.myspring.Performer;

public class FirstBean {
    public static void main(String[] args) throws PerformanceException {
        ApplicationContext context = new ClassPathXmlApplicationContext(
                "spring-idol.xml");
        Performer performer = (Performer) context.getBean("duke");
        performer.perform();
    }
}

程序输出如下:

JUGGLING 16 BEANBAGS
While reciting...
When, in disgrace with fortune and men's eyes,
I all alone beweep my outcast state,
And trouble deaf heaven with my bootless cries,
And look upon myself, and curse my fate,
Wishing me like to one more rich in hope,
Featur'd like him, like him with friends possess'd,
Desiring this man's art and that man's scope,
With what I most enjoy contented least;
Yet in these thoughts myself almost despising,
Haply I think on thee, and then my state,
Like to the lark at break of day arising
From sullen earth, sings hymns at heaven's gate;
For thy sweet love remember'd such wealth brings
That then I scorn to change my state with kings.

理解:可以通过构造器注入来模拟构造函数传入的参数,通过constructor-arg value="XX"传递一个基本类型的参数XX,通过constructor-arg ref="XX"传递一个bean。

注入各种bean属性

这里通过一个MoonlightPoet类来演示了注入Bean属性property的效果。

package com.moonlit.myspring;

import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.Properties;

public class MoonlightPoet {
    private String name;
    private int age;
    private Poem poem;
    private List<String> list;
    private Map<String, String> map;

    public void perform() {
        System.out.println("name : " + name);
        System.out.println("age : " + age);
        poem.recite();
        for (String val : list) 
            System.out.println("in list : " + val);
        for (Entry<String, String> entry : map.entrySet())
            System.out.println("in map : " + entry.getKey() + " -- " + entry.getValue());
    }
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext(
                "spring-idol.xml");
        MoonlightPoet moonlightPoet = (MoonlightPoet) context.getBean("moonlightPoet");
        moonlightPoet.perform();
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public Poem getPoem() {
        return poem;
    }
    public void setPoem(Poem poem) {
        this.poem = poem;
    }
    public List<String> getList() {
        return list;
    }
    public void setList(List<String> list) {
        this.list = list;
    }
    public Map<String, String> getMap() {
        return map;
    }
    public void setMap(Map<String, String> map) {
        this.map = map;
    }

}

该bean在xml文件中定义如下:

<bean id="moonlightPoet" class="com.moonlit.myspring.MoonlightPoet">
    <property name="name" value="moonlit" />
    <property name="age" value="22" />
    <property name="poem" ref="sonnet29" />
    <property name="list">
      <list>
        <value>hello</value>
        <value>world</value>
        <!-- if bean, use <ref bean="XX"> -->
      </list>
    </property>
    <property name="map">
      <map>
        <entry key="key1" value="value1" />
        <entry key="key2" value="value2" />
        <entry key="key3" value="value3" />
      </map>
    </property>
</bean>

输出结果:

name : moonlit
age : 22
When, in disgrace with fortune and men's eyes,
I all alone beweep my outcast state,
And trouble deaf heaven with my bootless cries,
And look upon myself, and curse my fate,
Wishing me like to one more rich in hope,
Featur'd like him, like him with friends possess'd,
Desiring this man's art and that man's scope,
With what I most enjoy contented least;
Yet in these thoughts myself almost despising,
Haply I think on thee, and then my state,
Like to the lark at break of day arising
From sullen earth, sings hymns at heaven's gate;
For thy sweet love remember'd such wealth brings
That then I scorn to change my state with kings.
in list : hello
in list : world
in map : key1 -- value1
in map : key2 -- value2
in map : key3 -- value3

理解:
注入简单值:
<property name="XX" value="YY" />
其中XX是变量名,YY是值。
引用其他Bean:
<property name="XX" ref="YY">
其中XX是变量名,YY是引用的bean的id。

装配List:
<property name="XX">
<value>YY</value>
或者
<ref bean="ZZ">
</property>
其中XX是变量名,YY是值,ZZ是引用的bean。
装配Map:
<map>
<entry key="XX" value="YY" />
或者
<entry key="XX" value-ref="YY" />
或者
<entry key-ref="XX" value="YY" />
或者
<entry key-ref="XX" value-ref="YY" />
</map>
因为map的key和value可以对应一个基础类型的值,也可以对应一个bean,所以key,value对应值,key-ref,value-ref对应bean。

使用Spring的命名空间p装配属性
可以在beans中添加

xmlns:p="http:www.springframework.org/schema/beans"

来使用p:作为<bean>元素所有属性的前缀来装配Bean的属性。用法如下:

<bean id="kenny" class="XX"
    p:song = "Jingle Bells"
    p:instrument-ref = "saxphone" />

-ref后缀作为一个标识来告知Spring应该装配一个引用而不是字面值。

自动装配bean属性

Spring提供了四种类型的自动装配策略:

byName – 把与Bean的属性具有相同名字(或者ID)的其他Bean自动装配到Bean的对应属性中。

byType – 把与Bean的属性具有相同类型的其他Bean自动装配到Bean的对应属性中。

constructor – 把与Bean的构造器入参具有相同类型的其他Bean自动装配到Bean的对应属性中。

autodetect – 首先使用costructor进行自动装配。如果失败,再尝试使用byType进行自动装配。
这里以关羽和青龙偃月刀为例: 首先定义一个武器接口Weapon:

package com.moonlit.myspring;

public interface Weapon {    
    public void attack();
}

然后定义一个Weapon接口的实现Falchion类:

package com.moonlit.myspring;

public class Falchion implements Weapon {    
public void attack() {
        System.out.println("falcon is attacking!");
    }
}

定义一个英雄接口Hero:

package com.moonlit.myspring;

public interface Hero {    
    public void perform();
}

然后定义一个Hero接口的实现Guanyu类(代表关羽):

package com.moonlit.myspring;

public class GuanYu implements Hero {    

    private Weapon weapon;    
    public void perform() {
        System.out.println("GuanYu pick up his weapon.");
        weapon.attack();
    }    
    public Weapon getWeapon() {        

        return weapon;
    }    

    public void setWeapon(Weapon weapon) {        

        this.weapon = weapon;
    }
}

在不涉及自动装配的情况下,想要通过Spring的DI将Fachion类对象注入到Guanyu类的weapon属性中,可以新建一个xml文件(这里取名为spring-idol.xml)并在里面填写:
spring-idol.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-3.0.xsd">

  <bean id="falchion" class="com.moonlit.myspring.Falchion" />
  <bean id="guanyu" class="com.moonlit.myspring.GuanYu">
    <property name="weapon" ref="falchion" />
  </bean>
    </beans>

其中最主要的内容就是两个bean的声明部分:

<bean id="falchion" class="com.moonlit.myspring.Falchion" />
  <bean id="guanyu" class="com.moonlit.myspring.GuanYu">
    <property name="weapon" ref="falchion" />
  </bean>

第一个bean标签定义了一个Falchion类型的bean,第二个bean标签中将第一个bean作为weapon的值装配到了weapon属性中。 然后可以写一个测试程序来查看效果:

package com.moonlit.practice;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.moonlit.myspring.Hero;

public class AutowirePractice {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-idol.xml");
        Hero guanyu = (Hero) context.getBean("guanyu");
        guanyu.perform();
    }
}

输出结果如下:

GuanYu pick up his weapon.
falcon is attacking!

到目前为止还没有涉及到自动装配的内容,接下来开始讲述自动装配的内容。

byName自动装配

改变spring-idol.xml中bean声明内容的形式如下:

  <bean id="weapon" class="com.moonlit.myspring.Falchion" />
  <bean id="guanyu" class="com.moonlit.myspring.GuanYu" autowire="byName" />

得到一样的结果。

将Falchion类的id去了一个和Guanyu类的属性weapon一样的名字,并且在guanyu bean中添加了autowire="byName"用于指明装配类型是byName自动装配。这个时候guanyu bean就是在上下文中找名为weapon的bean装配到他自己的weapon属性中。

byType自动装配

改变spring-idol.xml中bean声明内容的形式如下:

  <bean id="falchion" class="com.moonlit.myspring.Falchion" />
  <bean id="guanyu" class="com.moonlit.myspring.GuanYu" autowire="byType" />

得到一样的结果。

这里已经不用关注Falchion类对应的bean的id是什么了,因为已经定义guanyu bean的autowire属性为"byType"。这个时候guanyu bean会在上下文中寻找和weapon具有相同类型的类对应的bean。
因为Guanyu类的weapon实现Weapon借口,整个上下文中目前只有一个Weapon接口的实现Falchion类,所以以"byType"类型就检测到了falchion bean并将其注入到了guanyu bean的weapon属性中。
但是也会出现一种情况就是检测的时候可能会出现多个相同type的bean,这个时候就不知道要装配那个了。比如,在新建一个实现Weapon接口的方天画戟类HalBerd:

package com.moonlit.myspring;

public class Halberd implements Weapon {    

    public void attack() {
        System.out.println("halberd is attacking!!!");
    }
}

并且在xml文件中声明一个新的halberd bean:

<bean id="halberd" class="com.moonlit.myspring.Halberd" />

在这种情况下就会出错,因为有两个bean满足byType的结果。

这个时候有两种解决办法:
第一种方法是将其中一个bean的primary属性设为false,比如:将方天画戟falchion bean的primary属性设为true,以防冠以使用方天画戟(很好奇吕布死了之后,赤兔马归关羽了,方天画戟去哪里了):

  <bean id="falchion" class="com.moonlit.myspring.Falchion"  />
  <bean id="halberd" class="com.moonlit.myspring.Halberd" primary="true" />
  <bean id="guanyu" class="com.moonlit.myspring.GuanYu" autowire="byType" />

输出结果如下:

GuanYu pick up his weapon.
halberd is attacking!!!

从输出结果中可以看到,关羽没有使用青龙偃月刀,而是使用方天画戟进行攻击了。

primary的默认属性是false。
第二种方法是设置其中一个bean的autowire-candidate属性为false,比如:将方天画戟的autowire-candidate属性设为false:

  <bean id="falchion" class="com.moonlit.myspring.Falchion"  />
  <bean id="halberd" class="com.moonlit.myspring.Halberd" primary="true" autowire-candidate="false" />
  <bean id="guanyu" class="com.moonlit.myspring.GuanYu" autowire="byType" />

这个时候测试程序的输出如下:

GuanYu pick up his weapon.
falcon is attacking!

可以看到这个时候关羽又重拾了青龙偃月刀。可以看到,当halberd bean的autowire-candidate属性设为false时,他将不会作为自动装配的竞选bean之一,这个时候虽然halberd的primary属性为true,但是halberd bean没有参与自动装配的竞选,所以自动装配到了falchion。

这种感觉就好像:“隔壁村李小花觊觎已久,但是一个要成为海贼王的男人,于是拒绝了她……最终她嫁给了隔壁老王,过上了幸福的生活”。

使用注解装配bean

使用@Autowired注解

从Spring2.5开始,最有趣的一种装配Spring Bean的方式是使用注解自动装配Bean的属性。
Spring默认禁用注解装配,最简单的启用方式是使用Spring的context命名空间配置中的<context:annotation-config>元素,如下所示:


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd">

  <context:annotation-config />

  <!-- bean declarations here -->
    </beans>

继续上一节的例子,在xml文件中定义两个bean:falchion bean和guanyu bean,为了实现@Autowired自动装配,在GuanYu类中的setWeapon()方法前添加了@Autowired注解,如下:
GuanYu.java:


package com.moonlit.myspring;
import org.springframework.beans.factory.annotation.Autowired;
public class GuanYu implements Hero {    

    private Weapon weapon;    
    public void perform() {
        System.out.println("Guan Yu pick up his weapon.");
        weapon.attack();
    }    
    public Weapon getWeapon() {        

        return weapon;
    }
    @Autowired    
    public void setWeapon(Weapon weapon) {        

        this.weapon = weapon;
    }
}

通过基于注解的方式,可以不用在xml文件中为guanyu bean添加autowire属性了。
spring-idol内部的代码:

<context:annotation-config />

  <bean id="falchion" class="com.moonlit.myspring.Falchion"  />
  <bean id="guanyu" class="com.moonlit.myspring.GuanYu" />

不仅可以使用@Autowired注解标注setter方法,还可以标注需要自动装配bean引用的任一方法,比如,给GuanYu类的setWeapon方法改名为pickupWeapon,如下:

package com.moonlit.myspring;
import org.springframework.beans.factory.annotation.Autowired;

public class GuanYu implements Hero {    

    private Weapon weapon;    
    public void perform() {
        System.out.println("Guan Yu pick up his weapon.");
        weapon.attack();
    }    
    public Weapon getWeapon() {        

        return weapon;
    }
    @Autowired    
    public void pickupWeapon(Weapon weapon) {        

        this.weapon = weapon;
    }
}

再运行测试程序AutowirePractice,输出的结果是一样的,因为虽然没有了setWeapon方法,但是通过@Autowired注解通过pickupWeapon方法将falchion bean传递给了guanyu bean。
@Autowired注解甚至可以标注构造器,这样的话甚至连set方法都可以不写了:


package com.moonlit.myspring;
import org.springframework.beans.factory.annotation.Autowired;
public class GuanYu implements Hero {
    @Autowired    
    private Weapon weapon;    
    public void perform() {
        System.out.println("Guan Yu pick up his weapon.");
        weapon.attack();
    }
}

@Autowired注解存在两种限制:
1.没有匹配Bean
2.匹配多个Bean

不过都有解决办法。
可选的自动装配
默认情况下,@Autowired属性具有强契约特征,其所标注的属性或参数必须是可装配的。如果没有Bean可以装配到@Autowired所标注的属性或参数中,自动装配就会失败(抛出令人讨厌的NoSuchBeanDefinitionException)。
属性不一定非要装配,null值也是可以接受的。在这种场景下,可以通过设置@Autowired的required属性为false来配置自动装配式可选的。例如:

@Autowired(required=false)
private Weapon weapon;

限定歧义性的依赖
有可能存在多个bean满足装配条件,比如,这里,falchion bean和halberd bean都满足装配到guanyu bean的weapon属性中的条件。此时如果只是用@Autowired注解的话就会出问题,才@Autowired注解下添加@Qualifier注解如下:

    @Autowired
    @Qualifier("falchion")    
    private Weapon weapon;

就会将falchion bean装入到weapon中。
如上所示,@Qualifier注解将尝试注入ID为falchion的Bean。
除了通过Bean的ID来限定,也可以给Bean添加一个qualifier属性,通过这个qualifier属性来获得限定,如:
给halberd bean添加一个qualifier,值为"weaponOfGuanYu":

<bean id="halberd" class="com.moonlit.myspring.Halberd">
    <qualifier value="weaponOfGuanYu" />
  </bean>

然后对GuanYu类weapon类的注解如下:

    @Autowired
    @Qualifier("weaponOfGuanYu")    
    private Weapon weapon;

输出如下:

Guan Yu pick up his weapon.
halberd is attacking!!!

可以看出,@qualifier降低了@Autowired的匹配范围,最终筛选得到了halberd bean装入weapon属性。
这里的<qualifier>元素限定了方天画戟(halberd)Bean是关羽使用的武器(weaponOgGuanYu)。除了可以在XML中指定qualifier,还可以使用Qualifier类来标注Halberd类:


package com.moonlit.myspring;

import org.springframework.beans.factory.annotation.Qualifier;

@Qualifier("weaponOfGuanYu")
public class Halberd implements Weapon {    
public void attack() {
        System.out.println("halberd is attacking!!!");
    }
}

程序运行将得到相同的结果。
即使<context:annotation-config>有助于完全消除Spring配置文件中的元素,但是还是不能完全消除,仍然需要使用<bean>元素显示定义Bean。因此<context:component-scan>元素出现了,它除了完成<context:annotation-config>一样的工作,还允许Spring自动检测Bean和定义Bean。这就意味着不使用<bean>元素,Spring应用中的大多数(或者所有)Bean都能够自动实现定义和装配。

自动检测

为了配置Spring自动检测,需要使用<context:component-scan>元素来代替<context:annotation-config>元素:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
      http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.moonlit.myspring">
    </context:component-scan>

</beans>

<context:component-scan>元素会扫描指定的包以及所有子包,并查找出能够自动注册为Spring Bean的类。base-package属性标示了<context:component-scan>元素所扫描的包。

为自动检测标注Bean

默认情况下,<context:component-scan>查找使用构造型(stereotype)注解所标注的类,这些特殊的注解如下:

类型 说明
@component 通用的构造型注解,标示该类为Spring 组件。
@Controller 标识将该类定义为Spring MVC controller。
@Repository 标识将该类定义为数据仓库(例如:Dao层)。
@Service 标识将该类定义为服务(例如:Service层)。

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

推荐阅读更多精彩内容