Spring

Spring介紹

Spring是一個(gè)開(kāi)源框架锅减,Spring是于2003 年興起的一個(gè)輕量級(jí)的Java開(kāi)發(fā)框架,由Rod Johnson 在其著作Expert One-On-One J2EE Development and Design中闡述的部分理念和原型衍生而來(lái)炫七。它是為了解決企業(yè)應(yīng)用開(kāi)發(fā)的復(fù)雜性而創(chuàng)建的。Spring使用基本的JavaBean來(lái)完成以前只可能由EJB完成的事情苟呐。然而汪厨,Spring的用途不僅限于服務(wù)器端的開(kāi)發(fā)低斋。從簡(jiǎn)單性蜂厅、可測(cè)試性和松耦合的角度而言,任何Java應(yīng)用都可以從Spring中受益
簡(jiǎn)單來(lái)說(shuō)膊畴,Spring是一個(gè)輕量級(jí)的控制反轉(zhuǎn)(IoC)和面向切面(AOP)的容器框架掘猿。

Spring好處

方便解耦,簡(jiǎn)化開(kāi)發(fā):
  • Spring就是一個(gè)大工廠唇跨,專門負(fù)責(zé)生成Bean稠通,可以將所有對(duì)象創(chuàng)建和依賴關(guān)系維護(hù)由Spring管理
AOP編程的支持:
  • Spring提供面向切面編程,可以方便的實(shí)現(xiàn)對(duì)程序進(jìn)行權(quán)限攔截买猖、運(yùn)行監(jiān)控等功能
聲明式事務(wù)的支持:
  • 只需要通過(guò)配置就可以完成對(duì)事務(wù)的管理改橘,而無(wú)需手動(dòng)編程
方便程序的測(cè)試:
  • Spring對(duì)Junit4支持,可以通過(guò)注解方便的測(cè)試Spring程序
    方便集成各種優(yōu)秀框架:
Spring不排斥各種優(yōu)秀的開(kāi)源框架
  • 其內(nèi)部提供了對(duì)各種優(yōu)秀框架(如:Struts玉控、Hibernate飞主、MyBatisQuartz等)的支持
降低JavaEE API的使用難度Spring:
  • 對(duì)JavaEE開(kāi)發(fā)中一些難用的APIJDBC奸远、JavaMail既棺、遠(yuǎn)程調(diào)webservice用等),都提供了封裝懒叛,使這些API應(yīng)用難度大大降低

Spring體系結(jié)構(gòu)

Spring 框架是一個(gè)分層架構(gòu),,它包含一系列的功能要素并被分為大約20個(gè)模塊。這些模塊分為Core Container耽梅、Data Access/Integration薛窥、WebAOP(Aspect Oriented Programming)Instrumentation和測(cè)試部分,如下圖所示:

Spring架構(gòu)

Spring在項(xiàng)目中的架構(gòu)
  • web層:Struts,SpringMVC
  • dao層:Hibernate,mybatis
    Spring在項(xiàng)目中的架構(gòu)

Spring IOC的底層實(shí)現(xiàn)原理

Spring IOC的底層實(shí)現(xiàn)原理

Spring核心jar包

  • spring-core-3.2.2.RELEASE.jar
    包含Spring框架基本的核心工具類诅迷,Spring其它組件要都要使用到這個(gè)包里的類,是其它組件的基本核心佩番。

  • spring-beans-3.2.2.RELEASE.jar
    所有應(yīng)用都要用到的,它包含訪問(wèn)配置文件罢杉、創(chuàng)建和管理bean
    以及進(jìn)行Inversion of Control(IoC) / Dependency Injection(DI)操作相關(guān)的所有類

  • spring-context-3.2.2.RELEASE.jar
    Spring提供在基礎(chǔ)IoC功能上的擴(kuò)展服務(wù)趟畏,此外還提供許多企業(yè)級(jí)服務(wù)的支持,
    如郵件服務(wù)、任務(wù)調(diào)度滩租、JNDI定位赋秀、EJB集成、遠(yuǎn)程訪問(wèn)律想、緩存以及各種視圖層框架的封裝等猎莲。

  • spring-expression-3.2.2.RELEASE.jar
    Spring表達(dá)式語(yǔ)言

  • com.springsource.org.apache.commons.logging-1.1.1.jar
    第三方的主要用于處理日志

Spring IOC/DI

  • IOC Inverse of Control反轉(zhuǎn)控制的概念, 就是將原本在程序中手動(dòng)創(chuàng)建對(duì)象的控制權(quán), 交給Spring框架管理, 簡(jiǎn)單的說(shuō), 就是將創(chuàng)建對(duì)象控制權(quán)被翻轉(zhuǎn)到了Spring框架
  • DI Dependency Injection依賴注入的概念, 就是在Spring創(chuàng)建這個(gè)對(duì)象的過(guò)程中, 將這個(gè)對(duì)象所依賴的屬性注入進(jìn)去

簡(jiǎn)單的Spring框架使用

創(chuàng)建一個(gè)UserService接口

public void sayHello();

和它的實(shí)現(xiàn)類UserServiceImpl

public void sayHello {
    private String name;
    public String getName() {
          return name;
     }
    public void setName(name) {
          this.name = name;
    }
    public void sayHello() {
          System.out.println("Spring hello" + name)
    }
    
}
  • 傳統(tǒng)使用UserService
public void demo() {
    UserService userService = new UserServiceImpl();
    userService.setName("李四");
    userService.sayHello();
}

輸出結(jié)果

Spring hello李四
  • 使用Spring
    在resource里創(chuàng)建一個(gè)applicationContext.xml文件, 里面寫入
// IOC
<bean id = "userService" class="com.rui.ioc.demo.UserServiceImpl>
  <property name = "name" value="李四" />
</bean>
public void demo () {
    // 創(chuàng)建Spring工廠, 加載classPath下的配置文件
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
    // 通過(guò)工廠獲得類
    UserService userService = (UserService) applicationContext.getBean("userService");
    userService.sayHello();
}

輸出結(jié)果

Spring hello李四
  • IOC=>
使用`Spring`工廠獲得類叫做`IOC`, 即將創(chuàng)建對(duì)象的權(quán)利交給Spring
  • DI =>
<property name = "name" value="李四" />

叫做依賴注入, 就是在Spring創(chuàng)建這個(gè)對(duì)象的過(guò)程中, 將這個(gè)對(duì)象所依賴的屬性注入進(jìn)去

Spring的工廠類

Spring的工廠類
  • ClassPathXmlApplicationContextBeanFactory的區(qū)別

    • BeanFactoryspring中比較原始,比較古老的Factory技即。因?yàn)楸容^古老著洼,所以BeanFactory無(wú)法支持spring插件,例如:AOP而叼、Web應(yīng)用等功能身笤。
    • 如果使用ApplicationContext,如果配置的beansingleton葵陵,那么不管你有沒(méi)有或想不想用它展鸡,它都會(huì)被實(shí)例化。好處是可以預(yù)先加載埃难,壞處是浪費(fèi)內(nèi)存莹弊。
    • BeanFactory,當(dāng)使用BeanFactory實(shí)例化對(duì)象時(shí)涡尘,配置的bean不會(huì)馬上被實(shí)例化忍弛,而是等到你使用該bean的時(shí)候(getBean)才會(huì)被實(shí)例化。好處是節(jié)約內(nèi)存考抄,壞處是速度比較慢细疚。多用于移動(dòng)設(shè)備的開(kāi)發(fā)。
    • 沒(méi)有特殊要求的情況下川梅,應(yīng)該使用ApplicationContext完成疯兼。因?yàn)?code>BeanFactory能完成的事情,ApplicationContext都能完成贫途,并且提供了更多接近現(xiàn)在開(kāi)發(fā)的功能吧彪。
  • 加載某個(gè)位置下的配置文件

public void demo () {
  // 創(chuàng)建Spring工廠
  ApplicationContext applicationContext = new FileSystemXmlApplicationContext("c:\\applicationContext.xml");
  // 通過(guò)工廠獲得類
  UserService userService = (UserService) applicationContext.getBean("userService");
  userService.sayHello();
}

Spring的Bean管理方式

  • XML

    • 三種實(shí)例化Bean的方式

      • 使用類構(gòu)造器實(shí)例化(默認(rèn)無(wú)參數(shù))
    // 對(duì)象
    public class Bean1 {
        public Bean1() {
            System.out.println("Bean1被實(shí)例化了...");
        }
    }
    
    // Spring配置文件
    <bean id="bean1" class="com.rui.demo.Bean1" />
    
    @Test
    public void demo () {
      // 創(chuàng)建Spring工廠, 加載classPath下的配置文件
      ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
      Bean1 bean1 = (bean1) applicationContext.getBean("bean1");
    }
    
    輸出結(jié)果為
    Bean1被實(shí)例化了...
    
    • 使用靜態(tài)工廠實(shí)例化(簡(jiǎn)單工廠模式)
    // 對(duì)象
    public class Bean2 {
        public Bean2() {
            System.out.println("Bean2被實(shí)例化了...");
        }
    }
    
     // Bean2的靜態(tài)工廠
    public class Bean2Factory {
        public static Bean2 createBean2() {
            System.out.println("Bean2被實(shí)例化了...");
            return new Bean2();
        }
    }
    
    <bean id="bean2" class="com.rui.demo.Bean2Factory"  factory-method="createBean2" />
    
    public void demo () {
      // 創(chuàng)建Spring工廠, 加載classPath下的配置文件
      ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
      Bean2 bean2 = (bean2) applicationContext.getBean("bean2");
    }
    
    輸出結(jié)果為
    Bean2被實(shí)例化了...
    
    • 使用實(shí)例工廠方法實(shí)例化(工廠方法模式)
    // 對(duì)象
    public class Bean3 {
        public Bean3() {
            System.out.println("Bean3被實(shí)例化了...");
        }
    }
    
     // Bean3的實(shí)例化工廠方法
    public class Bean3Factory {
        public Bean3 createBean3() {
            System.out.println("Bean3被實(shí)例化了...");
            return new Bean3();
        }
    }
    
    <bean id="bean3Factory" class="com.rui.demo.Bean3Factory" />
    <bean id="bean3" actory-bean="bean3Factory"  factory-method="createBean3" />
    
    public void demo () {
      // 創(chuàng)建Spring工廠, 加載classPath下的配置文件
      ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
      Bean3 bean3 = (bean3) applicationContext.getBean("bean3");
    }
    
    輸出結(jié)果為
    Bean3被實(shí)例化了...
    
    • Bean的常用配置

      • id和name
        • 一般情況下, 裝配一個(gè)Bean的時(shí)候, 通過(guò)一個(gè)id屬性作為Bean的名稱
        • id屬性在IOC容器里必須是唯一的
        • 如果Bean的名稱中含有特殊字符, 就需要使用name屬性
      • class
        • class用于設(shè)置一個(gè)類的完全路徑名稱, 主要作用是IOC容器生成類的實(shí)例
      • Bean的作用域Scope
      類別 說(shuō)明
      singleton(默認(rèn)) SpringIOC容器中僅存在一個(gè)Bean實(shí)例, Bean以單例的方式存在
      prototype 每次調(diào)用getBean()時(shí)都返回一個(gè)新的實(shí)例
      request 每次Http請(qǐng)求都會(huì)創(chuàng)建一個(gè)新的Bean, 該作用域僅適用于WebApplicationContext環(huán)境
      Session 同一個(gè)HTTP Session共享一個(gè)Bean, 不同的HTTP Session使用不同的Bean, 該作用域僅適用于WebApplicationContext環(huán)境
    • Spring容器中Bean的生命周期

      Spring初始化bean或銷毀bean的時(shí)候, 有時(shí)候需要做一些處理工作, 因此Spring可以在創(chuàng)建和銷毀bean的時(shí)候調(diào)用bean的兩個(gè)聲明周期方法

      類別 說(shuō)明
      init-method 初始化方法
      destory-method 銷毀方法, 僅在scope=singleton有效
        // 當(dāng)bean被載入到容器的時(shí)候調(diào)用init, 當(dāng)bean從容器中刪除的時(shí)候調(diào)用destory(僅在`scope=singleton有效)
          <bean id="xxx" class="xxxx" init-method="init" destory-method="destory" />
      
      Spring容器中Bean的生命周期
      1. instantiate bean對(duì)象實(shí)例化
      2. populate properties 封裝對(duì)象
      3. 如果Bean實(shí)現(xiàn)BeanNameAware執(zhí)行setBeanName
      4. 如果Bean實(shí)現(xiàn)BeanFactoryAware或者ApplicationContextAware設(shè)置工廠
      5. 如果存在類實(shí)現(xiàn)BeanPostProcessor(后處理Bean), 執(zhí)行postProcessBeforeInitialization
      6. 如果Bean實(shí)現(xiàn)InitializingBean執(zhí)行afterPropertiesSet
      7. 調(diào)用<bean init-method="init" />指定初始化init
      8. 如果存在類實(shí)現(xiàn)BeanPostProcessor(處理Bean), 執(zhí)行postProcessAfterInitiazation
      9. 執(zhí)行業(yè)務(wù)邏輯
      • Spring中beanPostProcessor使用案例
        • applicationContext.xml
        <bean class="com.rui.ioc.MyBeanPostProcessor" />
        <bean id="userDao" class="com.rui.ioc.UserDaoImpl" />
        
        • UserDao
          public interface UserDao {
              public void findAll();
              public void save();
              public void update();
              public void delete();
           }  
        
        • UserDaoImpl
         public class UserDaoImpl implements UserDao {
          @Override
          public void findAll() {
          System.out.println("查");
          }
          @Override
           public void save() {
              System.out.println("存");
            }
          @Override
          public void update() {
              System.out.println("改");
           }
          @Override
          public void delete() {
              System.out.println("刪");
          }
        }
        
        • MyBeanPostProcessor
           public class MyBeanPostProcessor implements BeanPostProcessor {
             @Override
             public Object postProcessBeforeInitialization(Object bean, String beanName)
           throws BeansException {
                   return bean;
           }
              @Override
              public Object postProcessAfterInitialization(final Object bean, String beanName)
             throws BeansException {
                 if ("userDao".equals(beanName)) {
                       Object proxy =
                   java.lang.reflect.Proxy.newProxyInstance(bean.getClass().getClassLoader(),
                           bean.getClass().getInterfaces(), new InvocationHandler() {
                               public Object invoke(Object proxy, Method method, Object[] args)
                                       throws Throwable {
                                   if ("save".equals(method.getName())) {
                                       System.out.println("權(quán)限調(diào)用");
                                       return method.invoke(bean, args);
                                   }
                                   return method.invoke(bean, args);
                               }
                           });
                   return proxy;
               } else {
                   return bean;
             }
           }
        }
        
        • 輸出
           第八步2222
           查
           權(quán)限調(diào)用
           存
           改
           刪
          
    • Spring的屬性注入

      • 構(gòu)造函數(shù)注入, 在對(duì)象中, 必須實(shí)現(xiàn)對(duì)應(yīng)的構(gòu)造函數(shù)參數(shù)為設(shè)置的屬性
        <bean id="user" class="com.rui.demo.User">
           <contructor-arg name="name" value="1223">
           <contructor-arg name="age" value="23">
        </bean>
        
      • 屬性setter方法注入, 在Spring配置文件中, 通過(guò)<property>設(shè)置注入的屬性, 在對(duì)象中必須實(shí)現(xiàn)對(duì)應(yīng)的參數(shù)的setter方法
        <bean id="user" class="com.rui.demo.User">
           <property name="name" value="1223">
           <property name="age" value="23">
        </bean>
         ```
         ```
        // User對(duì)象中含有一個(gè)對(duì)象Cat
        <bean id="user" class="com.rui.demo.User">
           <property name="name" value="1223">
           <property name="age" value="23">
           <property name="cat" ref="cat"> // ref引入別的bean的id
        </bean>
        <bean id="cat" class="com.rui.demo.Cat">
           <property name="name" value="ketty">
        </bean>     
         ```
        
      • P名稱空間注入
         <!--xmlns:p="http://www.springframework.org/schema/p"         需要引入-->
         <bean id="userDao" class="com.rui.ioc.UserDaoImpl" p:age="12"
         p:name="shsj" />
        
      • SpEL注入
          <bean id="catogery" class="com.rui.ioc.Category">
              <property name="name" value="#{'服裝'}" />
          </bean>
          <bean id="productInfo" class="com.rui.ioc.ProductInfo" />
          <bean id="productInfo" class="com.rui.ioc.Product">
               <property name="name" value="#{'男裝'}" />
               <!--        <property name="price" value="#{'1999'}" />-->
               <property name="price" value="#{productInfo.caculatePrice}" />
              <property name="category" value="#{catogery}" />
          </bean>
        
      • 復(fù)雜類型的屬性注入(一般用于整合其他框架)
      // 數(shù)組類型
      <property name="arrs">
          <list>
              <value>aaa</value>
          </list>
      </property>
      // list類型
      <property name="list">
          <list>
              <value>aaa</value>
          </list>
      </property>
      // 數(shù)組類型
      <property name="arrs">
          <list>
              <value>aaa</value>
          </list>
      </property>
      // set類型
      <property name="set">
          <set>
              <value>aaa</value>
          </set>
      </property>
      // Map類型
      <property name="map">
          <set>
              <value>aaa</value>
          </set>
      </property>
      // Map類型
      <property name="arrs">
          <map>
              <entry key="aaa" value="bbb" />
          </map>
      </property>
      // properties類型
      <property name="properties">
          <props>
              <prop key="username">root</prop>
              <prop key="password">12345</prop>
          </props>
      </property>
      

Spring的Bean注解方式

  • Bean的管理

applicationContext.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"
       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.rui.ioc"/>
</beans>

就會(huì)自動(dòng)掃描ioc下所有的類, 在類上標(biāo)注@Component/@Service/@Repository/@Controller

1、@controller 控制器(注入服務(wù))
2丢早、@service 服務(wù)(注入dao
3姨裸、@repository dao(實(shí)現(xiàn)dao訪問(wèn))
4秧倾、@component (@component("xxx"))(把普通pojo實(shí)例化到spring容器中,相當(dāng)于配置文件中的<bean id="xxx" class=""/>

  • 屬性的注入-注解方式

    • 簡(jiǎn)單屬性注入
       @value("coding")
       private String something; // something輸出值為coding
      
    • @Autowired進(jìn)行自動(dòng)注入
      • @Autowired默認(rèn)按照類型進(jìn)行注入
        • 如果兩個(gè)相同的Bean類型相同, 則按照名稱進(jìn)行注入
      • 通過(guò)@Autowiredrequired屬性, 設(shè)置一定要找到匹配的Bean
      • 使用@Qualifier指定注入Bean的名稱
        同時(shí)使用@Autowired@Qualifier可以指定固定的Bean, 效果和使用@Resource相同
    • @PostConstruct: 初始化
    • @PreDestroy: 銷毀
    • @Scope("prototype"): 多例, 默認(rèn)為單例

Spring AOP

  • 什么是AOP

    AOP Aspect Oriented Programing 面向切面編程
    AOP采取橫向抽取機(jī)制, 取代了傳統(tǒng)縱向繼承體系重復(fù)性代碼(性能監(jiān)視, 事務(wù)管理, 安全檢查, 緩存)

  • AOP術(shù)語(yǔ)

    AOP術(shù)語(yǔ)
  • JDK動(dòng)態(tài)代理

UserDao

package com.rui.aop;

public interface UserDao {
    public void findAll();
    public void save();
    public void update();
    public void delete();
}

UserDaoImpl

package com.rui.aop;
public class UserDaoImpl implements UserDao {
    @Override
    public void findAll() {
        System.out.println("查找用戶~(yú)");
    }
    @Override
    public void save() {
        System.out.println("保存用戶~(yú)");
    }
    @Override
    public void update() {
        System.out.println("更新用戶~(yú)");
    }
    @Override
    public void delete() {
        System.out.println("刪除用戶~(yú)");
    }
}

MyJdkProxy

package com.rui.aop;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class MyJDKProxy implements InvocationHandler {
    private UserDao userDao;
    public MyJDKProxy(UserDao userDao) {
        this.userDao = userDao;
    }
    public Object createProxy() {
        Object proxy = Proxy.newProxyInstance(userDao.getClass().getClassLoader(), userDao
                .getClass().getInterfaces(), this);
        return proxy;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if ("save".equals(method.getName())) {
            System.out.println("保存用戶之前的操作");
            return method.invoke(userDao, args);
        }
        return method.invoke(userDao, args);
    }
}

aopTest

package com.rui.aop;
import org.junit.Test;
public class aopTest {
    @Test
    public void demo1() {
        UserDao userDao = new UserDaoImpl();
        userDao = (UserDao)new MyJDKProxy(userDao).createProxy();
        userDao.findAll();
        userDao.delete();
        userDao.save();
        userDao.update();
    }
}

輸出結(jié)果

查找用戶~(yú)
刪除用戶~(yú)
保存用戶之前的操作
保存用戶~(yú)
更新用戶~(yú)
  • 使用CGLIB生成代理

MyCglibProxy

public class MyCglibProxy implements MethodInterceptor {
    private ProductDao productDao;

    public MyCglibProxy(ProductDao productDao) {
        this.productDao = productDao;
    }
    public Object createProxy() {
        // 1. 創(chuàng)建核心類
        Enhancer enhancer = new Enhancer();
        // 2. 設(shè)置父類
        enhancer.setSuperclass(productDao.getClass());
        // 3. 設(shè)置回調(diào)
        enhancer.setCallback(this);
        // 4.生成代理
        Object proxy = enhancer.create();
        return proxy;
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        if ("save".equals(method.getName())) {
            System.out.println
                    ("============================權(quán)限校驗(yàn)==========================");
            return methodProxy.invokeSuper(o, objects);
        }
        return methodProxy.invokeSuper(o, objects);
    }
}

ProductDao

public class ProductDao {
    public void findAll() {
        System.out.println("查找用戶~(yú)");
    }
    public void save() {
        System.out.println("保存用戶~(yú)");
    }
    public void update() {
        System.out.println("更新用戶~(yú)");
    }
    public void delete() {
        System.out.println("刪除用戶~(yú)");
    }
}

Test

@Test
public void demo1() {
     ProductDao productDao = new ProductDao();
     ProductDao proxy = (ProductDao)new MyCglibProxy(productDao).createProxy();
     proxy.findAll();
     proxy.delete();
     proxy.save();
     proxy.update();
}

輸出

查找用戶~(yú)
刪除用戶~(yú)
============================權(quán)限校驗(yàn)==========================
保存用戶~(yú)
更新用戶~(yú)

代理相關(guān)

  • Spring在運(yùn)行期, 生成動(dòng)態(tài)代理對(duì)象, 不需要特殊的編譯器
  • Spring AOP的底層就是通過(guò)JDK動(dòng)態(tài)代理或CGLib動(dòng)態(tài)代理技術(shù)為目標(biāo)Bean執(zhí)行橫向織入
    • 若目標(biāo)對(duì)象實(shí)現(xiàn)了若干接口, Spring使用JDKjava.lang.reflect.Proxy類代理
    • 若目標(biāo)對(duì)象沒(méi)有實(shí)現(xiàn)任何接口, spring使用cglib庫(kù)生成目標(biāo)對(duì)象的子類
  • 程序中應(yīng)該優(yōu)先對(duì)接口創(chuàng)建代理, 便于程序解耦維護(hù)
  • 標(biāo)記為final的方法, 不能被代理, 因?yàn)闊o(wú)法覆蓋
    • JDK動(dòng)態(tài)代理, 是針對(duì)接口生成子類, 接口中方法不能使用final修飾
    • CGLib是針對(duì)目標(biāo)類生產(chǎn)子類, 因此類或方法, 不能使用final
  • Spring只支持方法連接點(diǎn), 不提供屬性連接點(diǎn)

Spring AOP增強(qiáng)類型

  • AOP聯(lián)盟為通知Advice定義了org.aopallinance.aop.Interface.Advice
  • Spring按照通知Advice在目標(biāo)類方法的鏈接點(diǎn)位置, 可以分為5類
    • 前置通知 org.springframework.aop.MethodBeforeAdvice
      • 在目標(biāo)方法執(zhí)行前實(shí)施增強(qiáng)
    • 后置通知 org.springframework.aop.AfterReturningAdvice
      • 在目標(biāo)方法執(zhí)行后實(shí)施增強(qiáng)
    • 環(huán)繞通知 org.springframework.intercept.MethodInterceptor
      • 在目標(biāo)方法執(zhí)行前后都實(shí)施增強(qiáng)
    • 異常拋出通知 org.springframework.ThrowsAdvice
      • 在方法拋出異常后實(shí)施增強(qiáng)
    • 引介通知 org.springframework.IntroductionInterceptor
      • 在目標(biāo)類中添加一些新的方法和屬性

Spring AOP切面類型

  • Advisor: 代表一般切面, Advice本身就是一個(gè)切面, 對(duì)目標(biāo)類所有方法進(jìn)行攔截
  • PointcutAdvisor: 代表具有切點(diǎn)的切面, 可以指定攔截目標(biāo)類哪些方法
  • IntroductionAdvisor: 代表引介切面, 針對(duì)引介通知而使用切面

自動(dòng)創(chuàng)建代理

  • BeanNameAutoProxyCreator 根據(jù)Bean名稱創(chuàng)建代理
  • DefaultAdvisorAutoProxyCreator 根據(jù)Advisor本身包含信息創(chuàng)建代理
  • AnnotationAwareAspectJAutoProxyCreator 基于Bean中的AspectJ注解進(jìn)行自動(dòng)代理

AspectJ AOP

  • AspectJ是一個(gè)基于java語(yǔ)言的AOP框架
  • Spring 2.0以后新增了對(duì)AspectJ切點(diǎn)表達(dá)式的支持

1.Spring開(kāi)啟Aspectj

 <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"
       xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation="http://www.springframework.org/schema/bea      ns  http://www.springframework.org/schema/beans/spring-beans.xsd
     http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
     http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
 
   <!--    開(kāi)啟aspectj的注解開(kāi)發(fā)-->
   <aop:aspectj-autoproxy/>
 </beans>

2.@AspectJ提供不同的通知類型

  • @Before 前置通知, 相當(dāng)于BeforeAdvice
  • @AfterReturning 后置通知, 相當(dāng)于AfterReturningAdvice
  • @Around 環(huán)繞通知, 相當(dāng)于MethodInterceptor
  • @AfterThrowing 異常跑出通知, 相當(dāng)于ThrowAdvice
  • @After 最終final通知, 不管是否異常, 該通知都會(huì)執(zhí)行
  • @DeclareParents 引介通知, 相當(dāng)于IntroductionInterceptor

3. 在通知中通過(guò)value屬性定義切點(diǎn)

  • 通過(guò)excution函數(shù), 可以定義切點(diǎn)的方法切入
  • 語(yǔ)法
excution(<訪問(wèn)修飾符>?<返回類型><方法名>(<參數(shù)>)<異常>)
  • eg.
- 匹配所有類public方法 excution(public * *(...))
- 匹配指定包下所有類方法 excution(* com.imooc.dao.*(...)) 不包含子包
- 匹配指定包下所有類方法 excution(* com.imooc.dao..*(...)) 包含子包
- 匹配指定類所有方法 excution(* com.imooc.service.UserService.*(..))
- 匹配實(shí)現(xiàn)特定接口所有類方法 excution(* com.imooc.service.dao.GenericDAO+.*(..))
- 匹配所有save開(kāi)頭的方法 excution(* save*(..))

4. @Before/@AfterReturning/@Around/AfterThrowing/After

  • 切面類
@Aspect
public class MyAspect {
  @Pointcut(value="execution(* com.rui.aspectj.ProductDao.save(..))")
  private void save() {}

  @Pointcut(value = "execution(* com.rui.aspectj.ProductDao.update(..))")
  private void update() {}

  @Pointcut(value = "execution(* com.rui.aspectj.ProductDao.delete(..))")
  private void delete() {}

  @Before(value="save()")
  public void before(JoinPoint joinPoint) {
      System.out.println("前置通知============" + joinPoint);
  }
  @AfterReturning(value="update()",
  returning = "result")
  public void afterReturning(Object result) {
      // result為返回值
      System.out.println("后置通知============" + result);
  }
  @Around(value="delete()")
  public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
      System.out.println("環(huán)繞前通知=====================");
      Object obj = joinPoint.proceed(); // 執(zhí)行目標(biāo)方法
      System.out.println("環(huán)繞后通知=====================");
      return obj;
  }
  @AfterThrowing(value="execution(* com.rui.aspectj.ProductDao.findOne(..))",
  throwing = "e")
  public void afterThrowing(Throwable e) {
      System.out.println("異常拋出通知===========" + e.getMessage());
  }
  @After(value = "execution(* com.rui.aspectj.ProductDao.findAll(..))")
  public void after() {
      System.out.println("最終通知!================");
  }
}
  • Dao
public class ProductDao {
  public void save() {
      System.out.println("保存商品...");
  }
  public String update() {
      System.out.println("更新商品...");
      return "hello world";
  }
  public void delete() {
      System.out.println("刪除商品...");
  }
  public void findOne() {
      System.out.println("查找一個(gè)商品...");
  }
  public void findAll() {
      System.out.println("查找所有商品...");
  }
}
  • Spring 配置
<!--    開(kāi)啟aspectj的注解開(kāi)發(fā)-->
  <aop:aspectj-autoproxy/>
<!--    目標(biāo)類-->
  <bean id="productDao" class="com.rui.aspectj.ProductDao" />
  <!--定義切面-->
  <bean class="com.rui.aspectj.MyAspect" />
  • 測(cè)試代碼
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AspectTest {
  @Resource(name = "productDao")
  private ProductDao productDao;
  @Test
  public void demo1() {
      productDao.save();
      productDao.delete();
      productDao.findAll();
      productDao.findOne();
      productDao.update();
  }
}
  • output
前置通知============execution(void com.rui.aspectj.ProductDao.save())
保存商品...
環(huán)繞前通知=====================
刪除商品...
環(huán)繞后通知=====================
查找所有商品...
最終通知!================
查找一個(gè)商品...
異常拋出通知===========/ by zero

JDBC Template

使用

  • Mysql驅(qū)動(dòng)
  • Spring組件(Core, beans, context, aop)
  • JDBCTemplate(jdbc, tx)
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://local........." />
    <property name="username" value="root" />
    <property name="password" value="root" />
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="datasource" ref="datasource" />
</bean>

具體方法

  • update方法
// 對(duì)數(shù)據(jù)進(jìn)行增刪改操作
int update(String sql, Object[] args)
int update(String sql, Object... args)
public void testUpdate() {
    String sql = "insert into student(name, sex) values(?,?)";
    jdbcTemplate.update(sql, new Object[]{"zhagsan", "man"});
    // 或
    jdbcTemplate.update(sql, "zhagsan", "man");
}
  • batchUpdate方法
// 對(duì)數(shù)據(jù)進(jìn)行批量增刪改操作
int [] batchUpdate(String [] sql)
int [] batchUpdate(String sql, List<Object[]> args)
public void testBatchUpdate() {
    String[] sqls = {
              "insert into student(name, sex) values ('關(guān)羽', '男')",
              "insert into student(name, sex) values ('張飛', '男')"
    };
    jdbcTemplate.batchUpdate(sqls);
}
public void testBatchUpdate() {
    String sql = insert into student(name, sex) values (?, ?)";
    List<Object []> list = new ArrayList<Object[]>();
    list.add(new Object[]{"關(guān)羽", "男"});
    list.add(new Object[]{"張飛", "男"});
    jdbcTemplate.batchUpdate(sql, list);
}
  • 查詢簡(jiǎn)單數(shù)據(jù)項(xiàng)

    • 獲取一個(gè)
    T queryForObject(String sql, Class<T> type)
    T queryForObject(String sql, Object[] args, Class<T> type)
    T queryForObject(String sql, Class<T> type, Object... args)
    
    public void testQuerySimple() {
        String sql = "select count(*) from student";
        int count = jdbcTemplate.queryForObject(sql, Integer.class);
    }
    
    • 獲取多個(gè)
    List<T> queryForList(String sql, Class<T> type)
    List<T> queryForList(String sql, Object[] args, Class<T> type)
    List<T> queryForList(String sql, Class<T> type, Object... args)
    
    public void testQuerySimple() {
        String sql = "select name from student where sex=?";
        List<String> names = jdbcTemplate.queryForList(sql, String.class, "男");
    }
    
  • 查詢復(fù)雜對(duì)象(封裝為Map)

    • 獲取一個(gè)
    Map queryForMap(String sql)
    Map queryForMap(String sql, Object[] args)
    Map queryForMap(String sql, Object... args)
    
    public void testMap() {
        String sql = "select * from student where id = ?";
        Map<String, Object> stu = jdbcTemplate.queryForMap(sql, 1003);
    }
    
    • 獲取多個(gè)
    List<Map<String, Object>> queryForList(String sql)
    List<Map<String, Object>>queryForList(String sql, Object[] args)
    List<Map<String, Object>>queryForList(String sql, Object... args)
    
    public void testMap() {
        String sql = "select * from student";
        Map<String, Object> stu = jdbcTemplate.queryForMap(sql);
    }
    
  • 查詢復(fù)雜對(duì)象(封裝為實(shí)體對(duì)象)

    • RowMapper接口
      • 獲取一個(gè)
      T queryForObject(String sql, RowMapper<T> mapper)
      T queryForObject(String sql, Object[] args, RowMapper<T> mapper)
      T queryForObject(String sql, RowMapper<T> mapper, Object... arg)
      
      public void testQueryEntity() {
          String sql = "select * from student where id = ?";
          Student stu = jdbcTemplate.queryForObject(sql, new RowMapper<Student>() {
              public Student mapRow(ResultSet resultSet, int i) throw SQLException {
                  Student stu = new Student();
                  stu.setId(resultSet.getInt("id"));
                  stu.setName(resultSet.getInt("name"));
                  stu.setSex(resultSet.getInt("Sex"));
                  stu.setBorn(resultSet.getInt("born"));
                  return stu;
              }
          }, 1004)
      }
      
      • 獲取多個(gè)
      List<T> query(String sql, RowMapper<T> mapper)
      List<T> query(String sql, Object[] args, RowMapper<T> mapper)
      List<T> query(String sql, RowMapper<T> mapper, Object... arg)
      
      public void testQueryEntityList() {
          String sql = "select * from student";
          List<Student> stus = jdbcTemplate.query(sql, new RowMapper<Student>() {
              public Student mapRow(ResultSet resultSet, int i) throw SQLException {
                  Student stu = new Student();
                  stu.setId(resultSet.getInt("id"));
                  stu.setName(resultSet.getInt("name"));
                  stu.setSex(resultSet.getInt("Sex"));
                  stu.setBorn(resultSet.getInt("born"));
                  return stu;
              }
          })
      }
      
      在日常開(kāi)發(fā)中, 匿名類的形式比較冗余, 一般進(jìn)行封裝, 將RowMapper單獨(dú)實(shí)現(xiàn)
      private class StudentRowMapper implements RowMapper<Student> {
              public Student mapRow(ResultSet resultSet, int i) throw SQLException {
                  Student stu = new Student();
                  stu.setId(resultSet.getInt("id"));
                  stu.setName(resultSet.getInt("name"));
                  stu.setSex(resultSet.getInt("Sex"));
                  stu.setBorn(resultSet.getInt("born"));
                  return stu;
              }
      }
      
      使用如下:
      public void testQueryEntityList() {
          String sql = "select * from student";
          List<Student> stus = jdbcTemplate.query(sql, new StudentRowMapper());
      
      但是在實(shí)際開(kāi)發(fā)中, 每個(gè)類都要實(shí)現(xiàn)一個(gè)RowMapper會(huì)讓開(kāi)發(fā)變得極其繁瑣, 一般實(shí)現(xiàn)一個(gè)通用的工具類來(lái)做轉(zhuǎn)換的功能:
  • JDBCTemplate優(yōu)缺點(diǎn)

    • 優(yōu)點(diǎn)
      • 簡(jiǎn)單
      • 靈活
    • 缺點(diǎn)
      • sql和java代碼牽扯的比較雜亂
      • 功能不夠豐富, 比如分頁(yè)的功能沒(méi)有等

Spring事務(wù)管理

什么是事務(wù)

  • 事務(wù)就是正確執(zhí)行一系列的操作(或者動(dòng)作), 使數(shù)據(jù)庫(kù)從一種狀態(tài)轉(zhuǎn)換成另一種狀態(tài)且保證操作全部成功, 或者全部失敗

事務(wù)的特點(diǎn)

  • 原子性(Atomicity)
    • 即不可分割性, 事務(wù)要么全部被執(zhí)行, 要么就全部不執(zhí)行
  • 一致性(Consistency)
    • 事務(wù)的執(zhí)行使得數(shù)據(jù)庫(kù)從一種正確的狀態(tài)轉(zhuǎn)換成另一種正確的狀態(tài)
  • 隔離性(Isolation)
    • 在事務(wù)正確提交之前, 它可能的結(jié)果不應(yīng)該顯示給任何其他事務(wù)
  • 持久性(Durability)
    • 事務(wù)正確提交以后, 其結(jié)果將永久保存在數(shù)據(jù)庫(kù)中

Java事務(wù)的產(chǎn)生

  • 程序操作數(shù)據(jù)庫(kù)的需要. 在Java編寫的程序或者系統(tǒng)中, 實(shí)現(xiàn)ACID的操作
  • Java事務(wù)實(shí)現(xiàn)范圍
    • 通過(guò)jdbc相應(yīng)方法間接來(lái)實(shí)現(xiàn)對(duì)數(shù)據(jù)庫(kù)的增刪改查, 把事務(wù)轉(zhuǎn)移到Java程序代碼中進(jìn)行控制
    • 確保事務(wù), 要么全部執(zhí)行成功, 要么撤銷不執(zhí)行
  • Java事務(wù)機(jī)制和原理就是確保數(shù)據(jù)庫(kù)操作的ACID特性

Java事務(wù)實(shí)現(xiàn)模式

  • Java事務(wù)的實(shí)現(xiàn)
    • 通過(guò)Java代碼來(lái)實(shí)現(xiàn)對(duì)數(shù)據(jù)庫(kù)的事務(wù)性操作
  • Java事務(wù)類型
    • JDBC事務(wù): 用Connection對(duì)象控制, 包括手動(dòng)模式和自動(dòng)模式;
    • JTA(Java Transaction API)事務(wù): 與實(shí)現(xiàn)無(wú)關(guān), 與協(xié)議無(wú)關(guān)的API;
    • 容器事務(wù): 應(yīng)用服務(wù)器提供的, 且大多是基于JTA完成(通晨酰基于JNDI的, 相當(dāng)復(fù)雜的API實(shí)現(xiàn))

三種事務(wù)的差異

  • JDBC事務(wù): 控制的局限性在一個(gè)數(shù)據(jù)庫(kù)連接內(nèi), 但是其使用簡(jiǎn)單
  • JTA事務(wù): 功能強(qiáng)大, 可跨越多個(gè)數(shù)據(jù)庫(kù)或者多個(gè)DAO, 使用比較復(fù)雜
  • 容器事務(wù): 主要指的是J2EE應(yīng)用服務(wù)器提供的事務(wù)管理, 局限于EJB

事務(wù)接口

  • 事務(wù)接口架構(gòu)


    事務(wù)接口架構(gòu)

事務(wù)讀取類型

- 臟讀: 事務(wù)沒(méi)提交, 提前讀取
- 不可重復(fù)讀: 多次讀取到的數(shù)據(jù)不一致
- 幻讀: 事務(wù)不是獨(dú)立執(zhí)行時(shí)發(fā)生的一種非預(yù)期現(xiàn)象

事務(wù)隔離級(jí)別

隔離級(jí)別

事務(wù)傳播行為

事務(wù)傳播行為

事務(wù)超時(shí)

  • 事務(wù)超時(shí)就是事務(wù)的一個(gè)定時(shí)器, 在特定時(shí)間內(nèi)事務(wù)如果沒(méi)有執(zhí)行完畢, 那么久會(huì)自動(dòng)回滾, 而不是一直等待其結(jié)束

事務(wù)回滾

  • 默認(rèn)情況下, 事務(wù)只有遇到運(yùn)行期異常才會(huì)回滾, 而在遇到檢查型異常時(shí)不會(huì)回滾
  • 自定義回滾策略
    • 生命失誤在遇到特定的檢查型異常時(shí)像遇到運(yùn)行期異常那樣回滾
    • 聲明事務(wù)遇到特別的異常不回滾, 即使這些異常時(shí)運(yùn)行期異常

Spring事務(wù)狀態(tài)

  • 事務(wù)接口
    • 通過(guò)事務(wù)管理器獲取TransactionStatus實(shí)例;
    • 控制事務(wù)在回滾或提交的時(shí)候需要應(yīng)用對(duì)應(yīng)的事務(wù)狀態(tài);
    • Spring事務(wù)接口
    // Spring事務(wù)狀態(tài)接口
    // 通過(guò)調(diào)用PlatformTransactionManager的getTransaction()
    // 獲取事務(wù)狀態(tài)實(shí)例
    public interface TransactionStatus {
        boolean isNewTransaction(); // 是否是新的事務(wù)
        boolean hasSavepoint(); // 是否有恢復(fù)點(diǎn)
        boolean setRollbackOnly(); // 設(shè)置為只回滾
        boolean isRollbackOnly(); // 是否為只回滾
        boolean isCompleted(); // 是否已完成
    }
    

編程式事務(wù)實(shí)現(xiàn)方式

事務(wù)管理器(PlatformTransactionManager)方式

  • 類似應(yīng)用JTA UserTransaction API方式, 但異常處理更簡(jiǎn)潔;
  • 核心類為: Spring事務(wù)管理的三個(gè)接口類以及JdbcTemplate
  • 步驟:
    • 獲取事務(wù)管理器
    • 創(chuàng)建事務(wù)屬性對(duì)象
    • 獲取事務(wù)狀態(tài)對(duì)象
    • 創(chuàng)建JDBC模板對(duì)象
    • 業(yè)務(wù)數(shù)據(jù)操作處理

模板事務(wù)(TransactionTemplate)的方式

  • 此為Spring官方團(tuán)隊(duì)推薦的編程式事務(wù)管理方式
  • 主要工具為JdbcTemplate

聲明式事務(wù)管理

聲明式事務(wù)實(shí)現(xiàn)原理

  • 基于AOP模式機(jī)制, 對(duì)方法前后進(jìn)行攔截

聲明式事務(wù)管理的配置類型:

  • 5種類型: 獨(dú)立代理; 共享代理, 攔截器, tx攔截器, 全注釋(注解方式) (前三類2.0版本以后不推薦使用)

聲明式事務(wù)管理配置實(shí)現(xiàn)方式:

  <!--    引入配置文件-->
  <bean id="propertyConfigurer"
        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
      <property name="order" value="2"/>
      <property name="ignoreUnresolvablePlaceholders" value="true"/>
      <property name="locations">
          <list>
              <!--配置屬性文件-->
              <value>classpath:datasource.properties</value>
          </list>
      </property>
      <property name="fileEncoding" value="utf-8"/>
  </bean>

  <!--配置連接池-->
  <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
      <property name="driverClassName" value="${db.driverClassName}"/>
      <property name="url" value="${db.url}"/>
      <property name="username" value="${db.username}"/>
      <property name="password" value="${db.password}"/>
      <!-- 連接池啟動(dòng)時(shí)的初始值 -->
      <property name="initialSize" value="${db.initialSize}"/>
      <!-- 連接池的最大值 -->
      <property name="maxActive" value="${db.maxActive}"/>
      <!-- 最大空閑值.當(dāng)經(jīng)過(guò)一個(gè)高峰時(shí)間后那先,連接池可以慢慢將已經(jīng)用不到的連接慢慢釋放一部分,一直減少到maxIdle為止 -->
      <property name="maxIdle" value="${db.maxIdle}"/>
      <!-- 最小空閑值.當(dāng)空閑的連接數(shù)少于閥值時(shí)赡艰,連接池就會(huì)預(yù)申請(qǐng)去一些連接售淡,以免洪峰來(lái)時(shí)來(lái)不及申請(qǐng) -->
      <property name="minIdle" value="${db.minIdle}"/>
      <!-- 最大建立連接等待時(shí)間。如果超過(guò)此時(shí)間將接到異常慷垮。設(shè)為-1表示無(wú)限制 -->
      <property name="maxWait" value="${db.maxWait}"/>
      <!--#給出一條簡(jiǎn)單的sql語(yǔ)句進(jìn)行驗(yàn)證 -->
      <!--<property name="validationQuery" value="select getdate()" />-->
      <property name="defaultAutoCommit" value="${db.defaultAutoCommit}"/>
      <!-- 回收被遺棄的(一般是忘了釋放的)數(shù)據(jù)庫(kù)連接到連接池中 -->
      <!--<property name="removeAbandoned" value="true" />-->
      <!-- 數(shù)據(jù)庫(kù)連接過(guò)多長(zhǎng)時(shí)間不用將被視為被遺棄而收回連接池中 -->
      <!--<property name="removeAbandonedTimeout" value="120" />-->
      <!-- #連接的超時(shí)時(shí)間揖闸,默認(rèn)為半小時(shí)。 -->
      <property name="minEvictableIdleTimeMillis" value="${db.minEvictableIdleTimeMillis}"/>

      <!--# 失效檢查線程運(yùn)行時(shí)間間隔换帜,要小于MySQL默認(rèn)-->
      <property name="timeBetweenEvictionRunsMillis" value="40000"/>
      <!--# 檢查連接是否有效-->
      <property name="testWhileIdle" value="true"/>
      <!--# 檢查連接有效性的SQL語(yǔ)句-->
      <property name="validationQuery" value="SELECT 1 FROM dual"/>
  </bean>
  <!-- 使用@Transactional進(jìn)行聲明式事務(wù)管理需要聲明下面這行 -->
  <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />
  <!-- 事務(wù)管理 -->
  <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
      <property name="dataSource" ref="dataSource"/>
      <property name="rollbackOnCommitFailure" value="true"/>
  </bean>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末楔壤,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子惯驼,更是在濱河造成了極大的恐慌蹲嚣,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,865評(píng)論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件祟牲,死亡現(xiàn)場(chǎng)離奇詭異隙畜,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)说贝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,296評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門议惰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人乡恕,你說(shuō)我怎么就攤上這事言询。” “怎么了傲宜?”我有些...
    開(kāi)封第一講書人閱讀 169,631評(píng)論 0 364
  • 文/不壞的土叔 我叫張陵运杭,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我函卒,道長(zhǎng)辆憔,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 60,199評(píng)論 1 300
  • 正文 為了忘掉前任报嵌,我火速辦了婚禮虱咧,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘锚国。我一直安慰自己腕巡,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,196評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布跷叉。 她就那樣靜靜地躺著逸雹,像睡著了一般营搅。 火紅的嫁衣襯著肌膚如雪云挟。 梳的紋絲不亂的頭發(fā)上梆砸,一...
    開(kāi)封第一講書人閱讀 52,793評(píng)論 1 314
  • 那天,我揣著相機(jī)與錄音园欣,去河邊找鬼帖世。 笑死,一個(gè)胖子當(dāng)著我的面吹牛沸枯,可吹牛的內(nèi)容都是我干的日矫。 我是一名探鬼主播,決...
    沈念sama閱讀 41,221評(píng)論 3 423
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼绑榴,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼哪轿!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起翔怎,我...
    開(kāi)封第一講書人閱讀 40,174評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤窃诉,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后赤套,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體飘痛,經(jīng)...
    沈念sama閱讀 46,699評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,770評(píng)論 3 343
  • 正文 我和宋清朗相戀三年容握,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了宣脉。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,918評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡剔氏,死狀恐怖塑猖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情谈跛,我是刑警寧澤羊苟,帶...
    沈念sama閱讀 36,573評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站币旧,受9級(jí)特大地震影響践险,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜吹菱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,255評(píng)論 3 336
  • 文/蒙蒙 一巍虫、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧鳍刷,春花似錦占遥、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,749評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)芬萍。三九已至,卻和暖如春搔啊,著一層夾襖步出監(jiān)牢的瞬間柬祠,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,862評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工负芋, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留漫蛔,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,364評(píng)論 3 379
  • 正文 我出身青樓旧蛾,卻偏偏與公主長(zhǎng)得像莽龟,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子锨天,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,926評(píng)論 2 361

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