1-Spring5-2-AOP&&jdbcTemplate

1市俊、什么是 AOP

(1)面向切面編程(方面)庄蹋,利用 AOP 可以對業(yè)務(wù)邏輯的各個(gè)部分進(jìn)行隔離官研,從而使得
業(yè)務(wù)邏輯各部分之間的耦合度降低婆殿,提高程序的可重用性丛肢,同時(shí)提高了開發(fā)的效率围肥。
(2)通俗描述:不通過修改源代碼方式,在主干功能里面添加新功能
(3)使用登錄例子說明 AOP


登入AOP案例

2蜂怎、AOP(底層原理)

1穆刻、AOP 底層使用動態(tài)代理
(1)有兩種情況動態(tài)代理
第一種 有接口情況,使用 JDK 動態(tài)代理
? 創(chuàng)建接口實(shí)現(xiàn)類代理對象杠步,增強(qiáng)類的方法


JDK動態(tài)代理

第二種 沒有接口情況氢伟,使用 CGLIB 動態(tài)代理
? 創(chuàng)建子類的代理對象,增強(qiáng)類的方法


cglib
2幽歼、AOP(JDK 動態(tài)代理)

1朵锣、使用 JDK 動態(tài)代理,使用 Proxy 類里面的方法創(chuàng)建代理對象


Proxy

(1)調(diào)用 newProxyInstance 方法


newProxyInstance

方法有三個(gè)參數(shù):
第一參數(shù)甸私,類加載器

第二參數(shù)诚些,增強(qiáng)方法所在的類,這個(gè)類實(shí)現(xiàn)的接口皇型,支持多個(gè)接口
第三參數(shù)诬烹,實(shí)現(xiàn)這個(gè)接口 InvocationHandler,創(chuàng)建代理對象弃鸦,寫增強(qiáng)的部分

2绞吁、編寫 JDK 動態(tài)代理代碼
(1)創(chuàng)建接口,定義方法

public interface UserDao {
    public int add(int a, int b);

    public String update(String id);
}

(2)創(chuàng)建接口實(shí)現(xiàn)類唬格,實(shí)現(xiàn)方法

public class UserDaoImpl implements UserDao {
    @Override
    public int add(int a, int b) {
        return a + b;
    }

    @Override
    public String update(String id) {
        return id;
    }
}

(3)使用 Proxy 類創(chuàng)建接口代理對象

public class JDKProxy {
    public static void main(String[] args) {
        //創(chuàng)建接口實(shí)現(xiàn)類代理對象
        Class[] interfaces = {UserDao.class};
/*  匿名內(nèi)部類寫法
    Proxy.newProxyInstance (JDKProxy.class.getClassLoader ( ), interfaces, new InvocationHandler ( ) {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                return null;
            }
        });
*/
        UserDaoImpl userDao = new UserDaoImpl ( );
        UserDao dao = (UserDao) Proxy.newProxyInstance (JDKProxy.class.getClassLoader ( ), interfaces, new UserDaoProxy (userDao));
        int result = dao.add (1, 2);
        System.out.println ("result:" + result);
    }
}

//創(chuàng)建代理對象
class UserDaoProxy implements InvocationHandler {
    //1 把創(chuàng)建的是誰的代理對象家破,把誰傳遞過來
    //有參數(shù)構(gòu)造傳遞
    private Object obj;

    public UserDaoProxy(Object obj) {
        this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //方法之前
        System.out.println ("方法之前執(zhí)行...." + method.getName ( ) + " :傳遞的參數(shù)..." + Arrays.toString (args));
        //被增強(qiáng)的方法執(zhí)行
        Object res = method.invoke (obj, args);
        //方法之后
        System.out.println ("方法之后執(zhí)行...." + obj);
        return res;

    }
}

---------------------------輸出打印結(jié)果---------------------------------------
方法之前執(zhí)行....add :傳遞的參數(shù)...[1, 2]
方法之后執(zhí)行....com.kk.jdk.UserDaoImpl@2b193f2d
result:3

3、AOP(術(shù)語)

1西轩、連接點(diǎn):類里面哪些可以被增強(qiáng)的方法
2员舵、切入點(diǎn):實(shí)際被增強(qiáng)的方法
3脑沿、通知(增強(qiáng)):
(1)實(shí)際增強(qiáng)的邏輯部分稱為通知(增強(qiáng))
(2):通知的類型


通知的類型

4藕畔、切面:是動作,把通知應(yīng)用到切入點(diǎn)的過程

AOP 操作(準(zhǔn)備工作)

1庄拇、Spring 框架一般都是基于 AspectJ 實(shí)現(xiàn) AOP 操作
(1)AspectJ 不是 Spring 組成部分注服,獨(dú)立 AOP 框架韭邓,一般把 AspectJ 和 Spirng 框架一起使
用,進(jìn)行 AOP 操作
2溶弟、基于 AspectJ 實(shí)現(xiàn) AOP 操作
(1)基于 xml 配置文件實(shí)現(xiàn)
(2)基于注解方式實(shí)現(xiàn)(使用)
3女淑、在項(xiàng)目工程里面引入 AOP 相關(guān)依賴

aop依賴

4、切入點(diǎn)表達(dá)式
(1)切入點(diǎn)表達(dá)式作用:知道對哪個(gè)類里面的哪個(gè)方法進(jìn)行增強(qiáng)
(2)語法結(jié)構(gòu): execution([權(quán)限修飾符] [返回類型] [類全路徑] [方法名稱]([參數(shù)列表] ))
舉例 1:對 com.kk.dao.BookDao 類里面的 add 進(jìn)行增強(qiáng)
execution(* com.kk.dao.BookDao.add(..))
舉例 2:對 com.kk.dao.BookDao 類里面的所有的方法進(jìn)行增強(qiáng)
execution(* com.kk.dao.BookDao.* (..))
舉例 3:對 com.kk.dao 包里面所有類辜御,類里面所有方法進(jìn)行增強(qiáng)
execution(* com.kk.dao.. (..))

AOP 操作(AspectJ 注解)

1鸭你、創(chuàng)建類,在類里面定義方法

public class User {
    public void add() {
        System.out.println ("add.......");
    }
}

2擒权、創(chuàng)建增強(qiáng)類(編寫增強(qiáng)邏輯)
(1)在增強(qiáng)類里面袱巨,創(chuàng)建方法,讓不同方法代表不同通知類型

//增強(qiáng)的類
public class UserProxy {
    public void before() {//前置通知
        System.out.println ("before......");
    }
}

3碳抄、進(jìn)行通知的配置
(1)在 spring 配置文件中愉老,開啟注解掃描

<?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"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!-- 開啟注解掃描 -->
    <context:component-scan base-package="com.kk.aopanno"></context:component-scan>
</beans>

(2)使用注解創(chuàng)建 User 和 UserProxy 對象


image.png

(3)在增強(qiáng)類上面添加注解 @Aspect

//增強(qiáng)的類
@Component
@Aspect //生成代理對象
public class UserProxy {
    public void before() {//前置通知
        System.out.println ("before......");
    }
}

(4)在 spring 配置文件中開啟生成代理對象

    <!-- 開啟 Aspect 生成代理對象--> 
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

4、配置不同類型的通知
(1)在增強(qiáng)類的里面剖效,在作為通知方法上面添加通知類型注解嫉入,使用切入點(diǎn)表達(dá)式配置

//增強(qiáng)的類
@Component
@Aspect //生成代理對象
public class UserProxy {
    //前置通知
    //@Before 注解表示作為前置通知
    @Before(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
    public void before() {
        System.out.println ("before.........");
    }
    //后置通知(返回通知)
    @AfterReturning(value = "execution(* com.kk.aopanno.User.add(..))")

    public void afterReturning() {
        System.out.println ("afterReturning.........");
    }

    //最終通知
    @After(value = "execution(* com.kk.aopanno.User.add(..))")
    public void after() {
        System.out.println ("after.........");
    }
    //異常通知
    @AfterThrowing(value = "execution(* com.kk.aopanno.User.add(..))")

    public void afterThrowing() {
        System.out.println ("afterThrowing.........");
    }

    //環(huán)繞通知
    @Around(value = "execution(* com.kk.aopanno.User.add(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws
            Throwable {
        System.out.println ("環(huán)繞之前.........");
        //被增強(qiáng)的方法執(zhí)行
        proceedingJoinPoint.proceed ( );
        System.out.println ("環(huán)繞之后.........");
    }
}

5、相同的切入點(diǎn)抽取

    //相同切入點(diǎn)抽取
    @Pointcut(value = "execution(* com.kk.aopanno.User.add(..))")
    public void pointdemo() {
    }

    //前置通知
    //@Before 注解表示作為前置通知
    @Before(value = "pointdemo()")
    public void before() {
        System.out.println ("before.........");
    }

6璧尸、有多個(gè)增強(qiáng)類多同一個(gè)方法進(jìn)行增強(qiáng)咒林,設(shè)置增強(qiáng)類優(yōu)先級
(1)在增強(qiáng)類上面添加注解 @Order(數(shù)字類型值),數(shù)字類型值越小優(yōu)先級越高

@Component
@Aspect
@Order(1)
public class PersonProxy

7爷光、完全使用注解開發(fā)
(1)創(chuàng)建配置類映九,不需要創(chuàng)建 xml 配置文件

    @Configuration
    @ComponentScan(basePackages = {"com.kk"})
    @EnableAspectJAutoProxy(proxyTargetClass = true)
    public class ConfigAop {
    }

AOP 操作(AspectJ 配置文件)

1、創(chuàng)建兩個(gè)類瞎颗,增強(qiáng)類和被增強(qiáng)類件甥,創(chuàng)建方法

2、在 spring 配置文件中創(chuàng)建兩個(gè)類對象

 <bean id="user" class="com.kk.aopanno.User"></bean>
 <bean id="userProxy" class="com.kk.aopanno.UserProxy"></bean>

3哼拔、在 spring 配置文件中配置切入點(diǎn)

    <!--配置 aop 增強(qiáng)-->
    <aop:config>
        <!--切入點(diǎn)-->
        <aop:pointcut id="p" expression="execution(* com.kk.aopanno.User.add(..))"/>
        <!--配置切面-->
        <aop:aspect ref="userProxy">
            <!--增強(qiáng)作用在具體的方法上-->
            <aop:before method="before" pointcut-ref="p"/>
        </aop:aspect>
    </aop:config>

4引有、JdbcTemplate(概念和準(zhǔn)備)

1、什么是 JdbcTemplate
(1)Spring 框架對 JDBC 進(jìn)行封裝倦逐,使用 JdbcTemplate 方便實(shí)現(xiàn)對數(shù)據(jù)庫操作
2譬正、準(zhǔn)備工作
(1)引入相關(guān) jar 包


JdbcTemplate依賴

(2)在 spring 配置文件配置數(shù)據(jù)庫連接池

  <!-- 數(shù)據(jù)庫連接池 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
          destroy-method="close">
        <property name="url" value="jdbc:mysql:///test"/>
        <property name="username" value="root"/>
        <property name="password" value="a1b2c3"/>
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    </bean>

(3)配置 JdbcTemplate 對象,注入 DataSource

    <!-- JdbcTemplate 對象 -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!--注入 dataSource-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

(4)創(chuàng)建 service 類檬姥,創(chuàng)建 dao 類曾我,在 dao 注入 jdbcTemplate 對象
? 配置文件

<!-- 組件掃描 -->
<context:component-scan base-package="com.kk"></context:component-scan>

? Service

@Service
public class BookService {
    //注入 dao
    @Autowired
    private BookDao bookDao;
}

? Dao

@Repository
public class BookDaoImpl implements BookDao {
    //注入 JdbcTemplate
    @Autowired
    private JdbcTemplate jdbcTemplate;
}

4.1、JdbcTemplate 操作數(shù)據(jù)庫(添加)

1健民、對應(yīng)數(shù)據(jù)庫創(chuàng)建實(shí)體類


user

2抒巢、編寫 service 和 dao
(1)在 dao 進(jìn)行數(shù)據(jù)庫添加操作
(2)調(diào)用 JdbcTemplate 對象里面 update 方法實(shí)現(xiàn)添加操作


update

? 有兩個(gè)參數(shù)
? 第一個(gè)參數(shù):sql 語句

? 第二個(gè)參數(shù):可變參數(shù),設(shè)置 sql 語句值

public class BookDaoImpl implements BookDao {
   //注入 JdbcTemplate
   @Autowired
   private JdbcTemplate jdbcTemplate;

   //添加的方法
   @Override
   public void add(Book book) {
       //1 創(chuàng)建 sql 語句
       String sql = "insert into t_book values(?,?,?)";
       //2 調(diào)用方法實(shí)現(xiàn)
       Object[] args = {book.getUserId ( ), book.getUsername ( ),
               book.getUsetatus ( )};
       int update = jdbcTemplate.update (sql, args);
       System.out.println (update);
   }
}

3秉犹、測試類

    @Test
    public void testJdbcTemplate() {
        ApplicationContext context =
                new ClassPathXmlApplicationContext ("bean7.xml");
        BookService bookService = context.getBean("bookService",
                BookService.class);
        Book book = new Book();
        book.setUserId("1");
        book.setUsername("java");
        book.setUsetatus("a");
        bookService.addBook(book);
    }

4.2蛉谜、JdbcTemplate 操作數(shù)據(jù)庫(修改和刪除)

1稚晚、修改

    @Override
    public void updateBook(Book book) {
        String sql = "update t_book set username=?,ustatus=? where user_id=?";
        Object[] args = {book.getUsername ( ), book.getUsetatus ( ), book.getUserId ( )};
        int update = jdbcTemplate.update (sql, args);
        System.out.println (update);
    }

2、刪除

    @Override
    public void delete(String id) {
        String sql = "delete from t_book where user_id=?";
        int update = jdbcTemplate.update (sql, id);
        System.out.println (update);
    }

4.3型诚、JdbcTemplate 操作數(shù)據(jù)庫(查詢返回某個(gè)值)

1客燕、查詢表里面有多少條記錄,返回是某個(gè)值
2狰贯、使用 JdbcTemplate 實(shí)現(xiàn)查詢返回某個(gè)值代碼


queryForObject

? 有兩個(gè)參數(shù)
? 第一個(gè)參數(shù):sql 語句
? 第二個(gè)參數(shù):返回類型 Class

   //查詢表記錄數(shù)
   @Override
   public int selectCount() {
       String sql = "select count(*) from t_book";
       Integer count = jdbcTemplate.queryForObject (sql, Integer.class);
       return count;
   }

4.4也搓、JdbcTemplate 操作數(shù)據(jù)庫(查詢返回對象)

1、場景:查詢圖書詳情
2涵紊、JdbcTemplate 實(shí)現(xiàn)查詢返回對象


queryForObject

? 有三個(gè)參數(shù)
? 第一個(gè)參數(shù):sql 語句
? 第二個(gè)參數(shù):RowMapper 是接口还绘,針對返回不同類型數(shù)據(jù),使用這個(gè)接口里面實(shí)現(xiàn)類完成
數(shù)據(jù)封裝
? 第三個(gè)參數(shù):sql 語句值

   //查詢返回對象
   @Override
   public Book findBookInfo(String id) {
       String sql = "select * from t_book where user_id=?";
       //調(diào)用方法
       Book book = jdbcTemplate.queryForObject (sql, new
               BeanPropertyRowMapper<Book> (Book.class), id);
       return book;
   }

4.5栖袋、JdbcTemplate 操作數(shù)據(jù)庫(查詢返回集合---分頁)

1拍顷、場景:查詢圖書列表分頁…
2、調(diào)用 JdbcTemplate 方法實(shí)現(xiàn)查詢返回集合


query

? 有三個(gè)參數(shù)
? 第一個(gè)參數(shù):sql 語句
? 第二個(gè)參數(shù):RowMapper 是接口塘幅,針對返回不同類型數(shù)據(jù)昔案,使用這個(gè)接口里面實(shí)現(xiàn)類完成
數(shù)據(jù)封裝
? 第三個(gè)參數(shù):sql 語句值

   //查詢返回集合
   @Override
   public List<Book> findAllBook() {
       String sql = "select * from t_book";
       //調(diào)用方法
       List<Book> bookList = jdbcTemplate.query (sql, new
               BeanPropertyRowMapper<Book> (Book.class));
       return bookList;
   }

4.6、JdbcTemplate 操作數(shù)據(jù)庫(批量操作)

1电媳、批量操作:操作表里面多條記錄
2踏揣、JdbcTemplate 實(shí)現(xiàn)批量添加操作


batechUpdate

? 有兩個(gè)參數(shù)
? 第一個(gè)參數(shù):sql 語句
? 第二個(gè)參數(shù):List 集合,添加多條記錄數(shù)據(jù)

    //批量添加
    @Override
    public void batchAddBook(List<Object[]> batchArgs) {
        String sql = "insert into t_book values(?,?,?)";
        int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
        System.out.println(Arrays.toString(ints));
    }

    //批量添加測試
    List<Object[]> batchArgs = new ArrayList<> ();
    Object[] o1 = {"3","java","a"};
    Object[] o2 = {"4","c++","b"};
    Object[] o3 = {"5","MySQL","c"};
    batchArgs.add(o1);
    batchArgs.add(o2);
    batchArgs.add(o3);
    //調(diào)用批量添加
    bookService.batchAdd(batchArgs);

3匾乓、JdbcTemplate 實(shí)現(xiàn)批量修改操作

    //批量修改
    @Override
    public void batchUpdateBook(List<Object[]> batchArgs) {
        String sql = "update t_book set username=?,ustatus=? where user_id=?";
        int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
        System.out.println(Arrays.toString(ints));
    }
    //批量修改
    List<Object[]> batchArgs = new ArrayList<>();
    Object[] o1 = {"java0909","a3","3"};
    Object[] o2 = {"c++1010","b4","4"};
    Object[] o3 = {"MySQL1111","c5","5"};
    batchArgs.add(o1);
    batchArgs.add(o2);
    batchArgs.add(o3);
    //調(diào)用方法實(shí)現(xiàn)批量修改
    bookService.batchUpdate(batchArgs);

4捞稿、JdbcTemplate 實(shí)現(xiàn)批量刪除操作

    //批量刪除
    @Override
    public void batchDeleteBook(List<Object[]> batchArgs) {
        String sql = "delete from t_book where user_id=?";
        int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
        System.out.println(Arrays.toString(ints));
    }
    //批量刪除
    List<Object[]> batchArgs = new ArrayList<>();
    Object[] o1 = {"3"};
    Object[] o2 = {"4"};
    batchArgs.add(o1);
    batchArgs.add(o2);
    //調(diào)用方法實(shí)現(xiàn)批量刪除
    bookService.batchDelete(batchArgs);

5、事務(wù)操作(事務(wù)概念)

1拼缝、事務(wù)操作(事務(wù)概念)

1娱局、什么事務(wù)
(1)事務(wù)是數(shù)據(jù)庫操作最基本單元,邏輯上一組操作咧七,要么都成功衰齐,如果有一個(gè)失敗所有操
作都失敗
(2)典型場景:銀行轉(zhuǎn)賬
*^ lucy 轉(zhuǎn)賬 100 元 給 mary
*^ lucy 少 100,mary 多 100
2继阻、事務(wù)四個(gè)特性(ACID) (1)原子性
(2)一致性
(3)隔離性
(4)持久性

2耻涛、事務(wù)操作(搭建事務(wù)操作環(huán)境)
業(yè)務(wù)模擬圖

1、創(chuàng)建數(shù)據(jù)庫表瘟檩,添加記錄


模擬數(shù)據(jù)

2抹缕、創(chuàng)建 service,搭建 dao墨辛,完成對象創(chuàng)建和注入關(guān)系
(1)service 注入 dao卓研,在 dao 注入 JdbcTemplate,在 JdbcTemplate 注入 DataSource


service&&dao

3背蟆、在 dao 創(chuàng)建兩個(gè)方法:多錢和少錢的方法鉴分,在 service 創(chuàng)建方法(轉(zhuǎn)賬的方法)
   @Repository
   public class UserDaoImpl implements UserDao {
       @Autowired
       private JdbcTemplate jdbcTemplate;

       //lucy 轉(zhuǎn)賬 100 給 mary
       //少錢
       @Override
       public void reduceMoney() {
           String sql = "update t_account set money=money-? where username=?";
           jdbcTemplate.update (sql, 100, "lucy");
       }

       //多錢
       @Override
       public void addMoney() {
           String sql = "update t_account set money=money+? where username=?";
           jdbcTemplate.update (sql, 100, "mary");
       }
   }

   @Service
   public class UserService {
       //注入 dao
       @Autowired
       private UserDao userDao;

       //轉(zhuǎn)賬的方法
       public void accountMoney() {
           //lucy 少 100
           userDao.reduceMoney ( );
           //mary 多 100
           userDao.addMoney ( );
       }
   }

4哮幢、上面代碼带膀,如果正常執(zhí)行沒有問題的志珍,但是如果代碼執(zhí)行過程中出現(xiàn)異常,有問題


問題代碼

(1)使用事務(wù)解決上面問題:


事務(wù)解決
3垛叨、事務(wù)操作(Spring 事務(wù)管理介紹)

1伦糯、事務(wù)添加到 JavaEE 三層結(jié)構(gòu)里面 Service 層(業(yè)務(wù)邏輯層)
2、在 Spring 進(jìn)行事務(wù)管理操作
(1)有兩種方式:編程式事務(wù)管理和聲明式事務(wù)管理(使用)
3嗽元、聲明式事務(wù)管理
(1)基于注解方式(使用)
(2)基于 xml 配置文件方式
4敛纲、在 Spring 進(jìn)行聲明式事務(wù)管理,底層使用 AOP 原理
5剂癌、Spring 事務(wù)管理 API
(1)提供一個(gè)接口淤翔,代表事務(wù)管理器,這個(gè)接口針對不同的框架提供不同的實(shí)現(xiàn)類

PlatformTransactionManager結(jié)構(gòu)樹

4佩谷、事務(wù)操作(注解聲明式事務(wù)管理)

1旁壮、在 spring 配置文件配置事務(wù)管理器

    <!--創(chuàng)建事務(wù)管理器-->
    <bean id="transactionManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--注入數(shù)據(jù)源-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

2、在 spring 配置文件谐檀,開啟事務(wù)注解
(1)在 spring 配置文件引入名稱空間 tx

<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"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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 
       http://www.springframework.org/schema/aop 
       http://www.springframework.org/schema/aop/spring-aop.xsd 
       http://www.springframework.org/schema/tx 
       http://www.springframework.org/schema/tx/spring-tx.xsd">
</beans>

(2)開啟事務(wù)注解

    <!--開啟事務(wù)注解-->
    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>

3抡谐、在 service 類上面(或者 service 類里面方法上面)添加事務(wù)注解
(1)@Transactional,這個(gè)注解添加到類上面桐猬,也可以添加方法上面
(2)如果把這個(gè)注解添加類上面麦撵,這個(gè)類里面所有的方法都添加事務(wù)
(3)如果把這個(gè)注解添加方法上面,為這個(gè)方法添加事務(wù)

@Service
@Transactional
public class UserService {
}
5溃肪、事務(wù)操作(聲明式事務(wù)管理參數(shù)配置)

1免胃、在 service 類上面添加注解@Transactional,在這個(gè)注解里面可以配置事務(wù)相關(guān)參數(shù)

image.png

2惫撰、propagation:事務(wù)傳播行為
(1)多事務(wù)方法直接進(jìn)行調(diào)用杜秸,這個(gè)過程中事務(wù) 是如何進(jìn)行管理的
多事務(wù)模擬圖

(2)事務(wù)的傳播行為是由傳播屬性指定的,Spring定義了7中類傳播行為
7中傳播行為

3润绎、ioslation:事務(wù)隔離級別
(1)事務(wù)有特性成為隔離性撬碟,多事務(wù)操作之間不會產(chǎn)生影響。不考慮隔離性產(chǎn)生很多問題
(2)有三個(gè)讀問題:臟讀莉撇、不可重復(fù)讀呢蛤、虛(幻)讀
(3)臟讀:一個(gè)未提交事務(wù)讀取到另一個(gè)未提交事務(wù)的數(shù)據(jù)
隔離級別

(4)不可重復(fù)讀:一個(gè)未提交事務(wù)讀取到另一提交事務(wù)修改數(shù)據(jù)
不可重復(fù)讀

(5)虛讀:一個(gè)未提交事務(wù)讀取到另一提交事務(wù)添加數(shù)據(jù)
(6)解決:通過設(shè)置事務(wù)隔離級別,解決讀問題
隔離級別

4棍郎、timeout:超時(shí)時(shí)間
(1)事務(wù)需要在一定時(shí)間內(nèi)進(jìn)行提交其障,如果不提交進(jìn)行回滾
(2)默認(rèn)值是 -1 ,設(shè)置時(shí)間以秒單位進(jìn)行計(jì)算
5涂佃、readOnly:是否只讀
(1)讀:查詢操作励翼,寫:添加修改刪除操作
(2)readOnly 默認(rèn)值 false蜈敢,表示可以查詢,可以添加修改刪除操作
(3)設(shè)置 readOnly 值是 true汽抚,設(shè)置成 true 之后抓狭,只能查詢
6、rollbackFor:回滾
(1)設(shè)置出現(xiàn)哪些異常進(jìn)行事務(wù)回滾
7造烁、noRollbackFor:不回滾
(1)設(shè)置出現(xiàn)哪些異常不進(jìn)行事務(wù)回滾

6否过、事務(wù)操作(XML 聲明式事務(wù)管理)

1、在 spring 配置文件中進(jìn)行配置
第一步 配置事務(wù)管理器
第二步 配置通知
第三步 配置切入點(diǎn)和切面

    <!--1 創(chuàng)建事務(wù)管理器-->
    <bean id="transactionManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--注入數(shù)據(jù)源-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--2 配置通知-->
    <tx:advice id="txadvice">
        <!--配置事務(wù)參數(shù)-->
        <tx:attributes>
            <!--指定哪種規(guī)則的方法上面添加事務(wù)-->
            <tx:method name="accountMoney" propagation="REQUIRED"/>
            <!--<tx:method name="account*"/>-->
        </tx:attributes>
    </tx:advice>
    <!--3 配置切入點(diǎn)和切面-->
    <aop:config>
        <!--配置切入點(diǎn)-->
        <aop:pointcut id="pt" expression="execution(* com.kk.service.UserService.*(..))"/>
        <!--配置切面-->
        <aop:advisor advice-ref="txadvice" pointcut-ref="pt"/>
    </aop:config>
7惭蟋、事務(wù)操作(完全注解聲明式事務(wù)管理)--注解在java中代替Xml的配置

1苗桂、創(chuàng)建配置類,使用配置類替代 xml 配置文件


@Configuration //配置類
@ComponentScan(basePackages = "com.kk") //組件掃描
@EnableTransactionManagement //開啟事務(wù)
public class TxConfig {
    //創(chuàng)建數(shù)據(jù)庫連接池
    @Bean
    public DruidDataSource getDruidDataSource() {
        DruidDataSource dataSource = new DruidDataSource ( );
        dataSource.setDriverClassName ("com.mysql.jdbc.Driver");
        dataSource.setUrl ("jdbc:mysql:///user_db");
        dataSource.setUsername ("root");
        dataSource.setPassword ("root");
        return dataSource;
    }

    //創(chuàng)建 JdbcTemplate 對象
    @Bean
    public JdbcTemplate getJdbcTemplate(DataSource dataSource) {//import javax.sql.DataSource;
        //到 ioc 容器中根據(jù)類型找到 dataSource
        JdbcTemplate jdbcTemplate = new JdbcTemplate ( );
        //注入 dataSource
        jdbcTemplate.setDataSource (dataSource);
        return jdbcTemplate;
    }

    //創(chuàng)建事務(wù)管理器
    @Bean
    public DataSourceTransactionManager
    getDataSourceTransactionManager(DataSource dataSource) {//import javax.sql.DataSource;
        DataSourceTransactionManager transactionManager = new
                DataSourceTransactionManager ( );
        transactionManager.setDataSource (dataSource);
        return transactionManager;
    }
}

Spring5 框架新功能

1告组、整個(gè) Spring5 框架的代碼基于 Java8煤伟,運(yùn)行時(shí)兼容 JDK9,許多不建議使用的類和方
法在代碼庫中刪除
2木缝、Spring 5.0 框架自帶了通用的日志封裝
(1)Spring5 已經(jīng)移除 Log4jConfigListener便锨,官方建議使用 Log4j2
(2)Spring5 框架整合 Log4j2
第一步 引入 jar 包


Log4j2_lib

第二步 創(chuàng)建 log4j2.xml 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!--日志級別以及優(yōu)先級排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--Configuration后面的status用于設(shè)置log4j2自身內(nèi)部的信息輸出,可以不設(shè)置氨肌,當(dāng)設(shè)置成trace時(shí)鸿秆,可以看到log4j2內(nèi)部各種詳細(xì)輸出-->
<configuration status="INFO">
   <!--先定義所有的appender-->
   <appenders>
       <!--輸出日志信息到控制臺-->
       <console name="Console" target="SYSTEM_OUT">
           <!--控制日志輸出的格式-->
           <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - >%msg%n"/>
       </console>
   </appenders>
   <!--然后定義logger,只有定義了logger并引入的appender怎囚,appender才會生效-->
   <!--root:用于指定項(xiàng)目的根日志卿叽,如果沒有單獨(dú)指定Logger,則會使用root作為默認(rèn)的日志輸出-->
   <loggers>
       <root level="info">
           <appender-ref ref="Console"/>
       </root>
   </loggers>
</configuration>

3恳守、Spring5 框架核心容器支持@Nullable 注解

(1)@Nullable 注解可以使用在方法上面考婴,屬性上面,參數(shù)上面催烘,表示方法返回可以為空沥阱,屬性值可以
為空,參數(shù)值可以為空
(2)注解用在方法上面伊群,方法返回值可以為空


方法

(3)注解使用在方法參數(shù)里面考杉,方法參數(shù)可以為空


參數(shù)

(4)注解使用在屬性上面,屬性值可以為空
屬性

4舰始、Spring5 核心容器支持函數(shù)式風(fēng)格 GenericApplicationContext

    //函數(shù)式風(fēng)格創(chuàng)建對象崇棠,交給 spring 進(jìn)行管理
    @Test
    public void testGenericApplicationContext() {
        //1 創(chuàng)建 GenericApplicationContext 對象
        GenericApplicationContext context = new GenericApplicationContext ( );
        //2 調(diào)用 context 的方法對象注冊
        context.refresh ( );
        context.registerBean ("user1", User.class, () -> new User ( ));
        //3 獲取在 spring 注冊的對象
        // User user = (User)context.getBean("com.kk.test.User");
        User user = (User) context.getBean ("user1");
        System.out.println (user);
    }

5、Spring5 支持整合 JUnit5

(1)整合 JUnit4(4可忽略直接了解5)
第一步 引入 Spring 相關(guān)針對測試依賴:


引入

第二步 創(chuàng)建測試類丸卷,使用注解方式完成:

@RunWith(SpringJUnit4ClassRunner.class) //單元測試框架
   @ContextConfiguration("classpath:bean1.xml") //加載配置文件
   public class JTest4 {
       @Autowired
       private UserService userService;

       @Test
       public void test1() {
           userService.accountMoney ( );
       }
   }

(2)Spring5 整合 JUnit5
第一步 引入 JUnit5 的 jar 包:


ju5

第二步 創(chuàng)建測試類枕稀,使用注解完成:

@ExtendWith(SpringExtension.class)
   @ContextConfiguration("classpath:bean1.xml")
   public class JTest5 {
       @Autowired
       private UserService userService;

       @Test
       public void test1() {
           userService.accountMoney ( );
       }
   }

(3)使用一個(gè)復(fù)合注解替代上面兩個(gè)注解完成整合:

   @SpringJUnitConfig(locations = "classpath:bean1.xml")
   public class JTest5 {
       @Autowired
       private UserService userService;

       @Test
       public void test1() {
           userService.accountMoney ( );
       }
   }

二、Spring5 框架新功能(Webflux)

1、SpringWebflux 介紹

(1)是 Spring5 添加新的模塊萎坷,用于 web 開發(fā)的凹联,功能和 SpringMVC 類似的,Webflux 使用
當(dāng)前一種比較流程響應(yīng)式編程出現(xiàn)的框架哆档。


web-webFlux

(2)使用傳統(tǒng) web 框架蔽挠,比如 SpringMVC,這些基于 Servlet 容器虐呻,Webflux 是一種異步非阻
塞的框架象泵,異步非阻塞的框架在 Servlet3.1 以后才支持寞秃,核心是基于 Reactor 的相關(guān) API 實(shí)現(xiàn)
的斟叼。

(3)解釋什么是異步非阻塞

? 異步和同步
? 非阻塞和阻塞
** 上面都是針對對象不一樣
** 異步和同步針對調(diào)用者,調(diào)用者發(fā)送請求春寿,如果等著對方回應(yīng)之后才去做其他事情就是同
步朗涩,如果發(fā)送請求之后不等著對方回應(yīng)就去做其他事情就是異步
** 阻塞和非阻塞針對被調(diào)用者,被調(diào)用者受到請求之后绑改,做完請求任務(wù)之后才給出反饋就是阻
塞谢床,受到請求之后馬上給出反饋然后再去做事情就是非阻塞
(4)Webflux 特點(diǎn):
第一 非阻塞式:在有限資源下,提高系統(tǒng)吞吐量和伸縮性厘线,以 Reactor 為基礎(chǔ)實(shí)現(xiàn)響應(yīng)式編程
第二 函數(shù)式編程:Spring5 框架基于 java8识腿,Webflux 使用 Java8 函數(shù)式編程方式實(shí)現(xiàn)路由請求
(5)比較 SpringMVC


比較圖

第一 兩個(gè)框架都可以使用注解方式,都運(yùn)行在 Tomet 等容器中
第二 SpringMVC 采用命令式編程造壮,Webflux 采用異步響應(yīng)式編程

2渡讼、響應(yīng)式編程(Java 實(shí)現(xiàn))

(1)什么是響應(yīng)式編程
響應(yīng)式編程是一種面向數(shù)據(jù)流和變化傳播的編程范式。這意味著可以在編程語言中很方便
地表達(dá)靜態(tài)或動態(tài)的數(shù)據(jù)流耳璧,而相關(guān)的計(jì)算模型會自動將變化的值通過數(shù)據(jù)流進(jìn)行傳播成箫。
電子表格程序就是響應(yīng)式編程的一個(gè)例子。單元格可以包含字面值或類似"=B1+C1"的公
式旨枯,而包含公式的單元格的值會依據(jù)其他單元格的值的變化而變化蹬昌。
(2)Java8 及其之前版本
? 提供的觀察者模式兩個(gè)類 Observer 和 Observable

public class ObserverDemo extends Observable {
   public static void main(String[] args) {
       ObserverDemo observer = new ObserverDemo ( );
       //添加觀察者
       observer.addObserver ((o, arg) -> {
           System.out.println ("發(fā)生變化");
       });
       observer.addObserver ((o, arg) -> {
           System.out.println ("手動被觀察者通知,準(zhǔn)備改變");
       });
       observer.setChanged ( ); //數(shù)據(jù)變化
       observer.notifyObservers ( ); //通知
   }


}

3攀隔、響應(yīng)式編程(Reactor 實(shí)現(xiàn))

(1)響應(yīng)式編程操作中皂贩,Reactor 是滿足 Reactive 規(guī)范框架
(2)Reactor 有兩個(gè)核心類,Mono 和 Flux昆汹,這兩個(gè)類實(shí)現(xiàn)接口 Publisher明刷,提供豐富操作
符。Flux 對象實(shí)現(xiàn)發(fā)布者筹煮,返回 N 個(gè)元素遮精;Mono 實(shí)現(xiàn)發(fā)布者,返回 0 或者 1 個(gè)元素
(3)Flux 和 Mono 都是數(shù)據(jù)流的發(fā)布者,使用 Flux 和 Mono 都可以發(fā)出三種數(shù)據(jù)信號:
元素值本冲,錯誤信號准脂,完成信號,錯誤信號和完成信號都代表終止信號檬洞,終止信號用于告訴
訂閱者數(shù)據(jù)流結(jié)束了狸膏,錯誤信號終止數(shù)據(jù)流同時(shí)把錯誤信息傳遞給訂閱者


Flux-Mono

(4)代碼演示 Flux 和 Mono
第一步 引入依賴:

   <dependency>
       <groupId>io.projectreactor</groupId>
       <artifactId>reactor-core</artifactId>
       <version>3.1.5.RELEASE</version>
   </dependency>

第二步 編程代碼:

   public static void main(String[] args) {
       //just 方法直接聲明
       Flux.just (1, 2, 3, 4);
       Mono.just (1);
       //其他的方法
       Integer[] array = {1, 2, 3, 4};
       Flux.fromArray (array);

       List<Integer> list = Arrays.asList (array);
       Flux.fromIterable (list);
       Stream<Integer> stream = list.stream ( );
       Flux.fromStream (stream);
   }

(5)三種信號特點(diǎn)
? 錯誤信號和完成信號都是終止信號,不能共存的
? 如果沒有發(fā)送任何元素值添怔,而是直接發(fā)送錯誤或者完成信號湾戳,表示是空數(shù)據(jù)流
? 如果沒有錯誤信號,沒有完成信號广料,表示是無限數(shù)據(jù)流
(6)調(diào)用 just 或者其他方法只是聲明數(shù)據(jù)流砾脑,數(shù)據(jù)流并沒有發(fā)出,只有進(jìn)行訂閱之后才會觸發(fā)數(shù)據(jù)流艾杏,不訂閱什么都不會發(fā)生的


just方法聲明

(7)操作符
? 對數(shù)據(jù)流進(jìn)行一道道操作韧衣,成為操作符,比如工廠流水線
第一 map 元素映射為新元素


Map->流

第二 flatMap 元素映射為流
? 把每個(gè)元素轉(zhuǎn)換流购桑,把轉(zhuǎn)換之后多個(gè)流合并大的流
flatMap->流

4畅铭、SpringWebflux 執(zhí)行流程和核心 API

SpringWebflux 基于 Reactor,默認(rèn)使用容器是 Netty勃蜘,Netty 是高性能的 NIO 框架硕噩,異步非阻塞的框架
(1)Netty
? BIO:


BIO

? NIO:


NIO

(2)SpringWebflux 執(zhí)行過程和 SpringMVC 相似的
? SpringWebflux 核心控制器 DispatchHandler,實(shí)現(xiàn)接口 WebHandler
? 接口 WebHandler 有一個(gè)方法
WebHandler-1
WebHandler-2

(3)SpringWebflux 里面 DispatcherHandler缭贡,負(fù)責(zé)請求的處理
? HandlerMapping:請求查詢到處理的方法
? HandlerAdapter:真正負(fù)責(zé)請求處理
? HandlerResultHandler:響應(yīng)結(jié)果處理
(4)SpringWebflux 實(shí)現(xiàn)函數(shù)式編程炉擅,兩個(gè)接口:
RouterFunction(路由處理)
和 HandlerFunction(處理函數(shù))

5、SpringWebflux(基于注解編程模型)

SpringWebflux 實(shí)現(xiàn)方式有兩種:注解編程模型和函數(shù)式編程模型使用注解編程模型方式匀归,和之前SpringMVC 使用相似的坑资,只需要把相關(guān)依賴配置到項(xiàng)目中,SpringBoot 自動配置相關(guān)運(yùn)行容器穆端,默認(rèn)情況下使用 Netty 服務(wù)器
? 第一步 創(chuàng)建 SpringBoot 工程袱贮,引入 Webflux 依賴


創(chuàng)建boot工程

引入依賴

? 第二步 配置啟動端口號


配端口

? 第三步 創(chuàng)建包和相關(guān)類
? ? 實(shí)體類
實(shí)體類

? ? 創(chuàng)建接口定義操作的方法
接口方法

? ? 接口實(shí)現(xiàn)類

public class UserServiceImpl implements UserService {
   //創(chuàng)建 map 集合存儲數(shù)據(jù)
   private final Map<Integer, User> users = new HashMap<> ( );

   public UserServiceImpl() {
       this.users.put (1, new User ("lucy", "nan", 20));
       this.users.put (2, new User ("mary", "nv", 30));
       this.users.put (3, new User ("jack", "nv", 50));
   }

   //根據(jù) id 查詢
   @Override
   public Mono<User> getUserById(int id) {
       return Mono.justOrEmpty (this.users.get (id));
   }

   //查詢多個(gè)用戶
   @Override
   public Flux<User> getAllUser() {
       return Flux.fromIterable (this.users.values ( ));
   }

   //添加用戶
   @Override
   public Mono<Void> saveUserInfo(Mono<User> userMono) {
       return userMono.doOnNext (person -> {
           //向 map 集合里面放值
           int id = users.size ( ) + 1;
           users.put (id, person);
       }).thenEmpty (Mono.empty ( ));
   }
}

? ? 創(chuàng)建Controller:


Contriller

? ? 說明:
SpringMVC 方式實(shí)現(xiàn),同步阻塞的方式体啰,基于 SpringMVC+Servlet+Tomcat
SpringWebflux 方式實(shí)現(xiàn)攒巍,異步非阻塞 方式,基于 SpringWebflux+Reactor+Netty

6荒勇、SpringWebflux(基于函數(shù)式編程模型)

(1)在使用函數(shù)式編程模型操作時(shí)候柒莉,需要自己初始化服務(wù)器
(2)基于函數(shù)式編程模型時(shí)候,有兩個(gè)核心接口:RouterFunction(實(shí)現(xiàn)路由功能沽翔,請求轉(zhuǎn)發(fā)
給對應(yīng)的 handler)和 HandlerFunction(處理請求生成響應(yīng)的函數(shù))兢孝。核心任務(wù)定義兩個(gè)函數(shù)
式接口的實(shí)現(xiàn)并且啟動需要的服務(wù)器窿凤。
( 3 ) SpringWebflux 請 求 和 響 應(yīng) 不 再 是 ServletRequest 和 ServletResponse ,而是
ServerRequest 和 ServerResponse
第一步 把注解編程模型工程復(fù)制一份 跨蟹,保留 entity 和 service 內(nèi)容
第二步 創(chuàng)建 Handler(具體實(shí)現(xiàn)方法)

暫無--------

第三步 初始化服務(wù)器雳殊,編寫 Router
? 創(chuàng)建路由的方法


? 創(chuàng)建服務(wù)器完成適配


? 最終調(diào)用


(4)使用 WebClient 調(diào)用


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市窗轩,隨后出現(xiàn)的幾起案子夯秃,更是在濱河造成了極大的恐慌,老刑警劉巖痢艺,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件仓洼,死亡現(xiàn)場離奇詭異,居然都是意外死亡堤舒,警方通過查閱死者的電腦和手機(jī)色建,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來植酥,“玉大人镀岛,你說我怎么就攤上這事弦牡∮淹裕” “怎么了?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵驾锰,是天一觀的道長卸留。 經(jīng)常有香客問我,道長椭豫,這世上最難降的妖魔是什么耻瑟? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮赏酥,結(jié)果婚禮上喳整,老公的妹妹穿的比我還像新娘。我一直安慰自己裸扶,他們只是感情好框都,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著呵晨,像睡著了一般魏保。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上摸屠,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天谓罗,我揣著相機(jī)與錄音,去河邊找鬼季二。 笑死檩咱,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播刻蚯,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蜂筹,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了芦倒?” 一聲冷哼從身側(cè)響起艺挪,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎兵扬,沒想到半個(gè)月后麻裳,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡器钟,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年津坑,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片傲霸。...
    茶點(diǎn)故事閱讀 39,902評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡疆瑰,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出昙啄,到底是詐尸還是另有隱情穆役,我是刑警寧澤,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布梳凛,位于F島的核電站耿币,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏韧拒。R本人自食惡果不足惜淹接,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望叛溢。 院中可真熱鬧塑悼,春花似錦、人聲如沸楷掉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽靖诗。三九已至郭怪,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間刊橘,已是汗流浹背鄙才。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留促绵,地道東北人攒庵。 一個(gè)月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓嘴纺,卻偏偏與公主長得像,于是被迫代替她去往敵國和親浓冒。 傳聞我的和親對象是個(gè)殘疾皇子栽渴,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評論 2 354