Spring 系列(八)AOP應(yīng)用

1. AOP術(shù)語

JointPoint(連接點):指的是可用于把增強代碼加入到業(yè)務(wù)主線中的點扛门。方法開始、結(jié)束纵寝、正常運行完畢论寨、方法異常時等這些特殊的時機點星立,我們稱之為連接點。
PointPut(切點):指的是那些已經(jīng)把增強代碼假如到業(yè)務(wù)主線進(jìn)來之后的連接點葬凳。每個程序類都擁有多個連接點绰垂,如一個擁有兩個方法的類,這兩個方法都是連接點火焰,即連接點是程序類中客觀存在的事物劲装。AOP通過“切點”定位特定的連接點。連接點相當(dāng)于數(shù)據(jù)庫中的記錄昌简,而切點相當(dāng)于查詢條件占业。切點和連接點不是一對一的關(guān)系,一個切點可以匹配多個連接點纯赎。
Advice(通知/增強):指的是切面類中用于提供增強功能的方法谦疾。
Target(目標(biāo)對象):指的是代理的目標(biāo)對象,即被代理對象犬金。
Proxy(代理):指的是一個類被AOP織入增強后念恍,產(chǎn)生的代理類,即代理對象佑附。
Weaving(織入):指的是把增強應(yīng)用到目標(biāo)對象來創(chuàng)建新的代理對象的過程樊诺。Spring采用動態(tài)代理織入,而AspectJ采用編譯期織入和類裝載期織入音同。
Aspect(切面):指的是增強的代碼所關(guān)注的方面词爬,把這些相關(guān)的增強代碼定義到一個類中,這個類就是切面類权均。例如顿膨,事務(wù)切?,它??定義的?法就是和事務(wù)相關(guān)的叽赊,像開啟事務(wù)恋沃,提交事務(wù),回滾事務(wù)等等必指,不會定義其他與事務(wù)?關(guān)的?法囊咏。
Aspect切? = 切?點+增強= 切?點(鎖定?法) + ?位點(鎖定?法中的特殊時機)+ 橫切邏輯。
眾多的概念塔橡,?的就是為了鎖定要在哪個地?插?什么橫切邏輯代碼

2. Spring中AOP的代理選擇

Spring實現(xiàn)AOP思想使用的是動態(tài)代理技術(shù)梅割。
默認(rèn)下Spring會根據(jù)被代理對象是否實現(xiàn)接口來選擇適用JDK還是CGLIB。當(dāng)被代理對象中沒有實現(xiàn)任何接口時葛家,Spring會選擇CGLIB,如果對象實現(xiàn)接口户辞,則會使用JDK官方的代理技術(shù)。不過我們可以通過配置的方式癞谒,讓Spring強制使用CGLIB底燎。

3. Spring AOP中的配置方式

在Spring的AOP配置中刃榨,也和IOC一樣,支持三種配置
第?類:使?XML配置
第?類:使?XML+注解組合配置
第三類:使?純注解配置

4. Spring中AOP實現(xiàn)

需求:橫切邏輯代碼是打印?志双仍,希望把打印?志的邏輯織?到?標(biāo)?法的特定位置(service層transfer?法)

4.1 XML模式

  • 坐標(biāo)
<dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-aop</artifactId>
 <version>5.1.12.RELEASE</version>
</dependency>
<dependency>
 <groupId>org.aspectj</groupId>
 <artifactId>aspectjweaver</artifactId>
 <version>1.9.4</version>
</dependency>
  • AOP核心配置
<!--
 Spring基于XML的AOP配置前期準(zhǔn)備:
 在spring的配置?件中加?aop的約束
 xmlns:aop="http://www.springframework.org/schema/aop"
 http://www.springframework.org/schema/aop 
https://www.springframework.org/schema/aop/spring-aop.xsd 
 
 Spring基于XML的AOP配置步驟:
 第?步:把通知Bean交給Spring管理
 第?步:使?aop:config開始aop的配置
 第三步:使?aop:aspect配置切?
 第四步:使?對應(yīng)的標(biāo)簽配置通知的類型
 ??案例采?前置通知枢希,標(biāo)簽為aop:before
-->
<!--把通知bean交給spring來管理-->
<bean id="logUtil" class="com.lagou.utils.LogUtil"></bean>
<!--開始aop的配置-->
<aop:config>
<!--配置切?-->
 <aop:aspect id="logAdvice" ref="logUtil">
 <!--配置前置通知-->
 <aop:before method="printLog" pointcut="execution(public *
com.lagou.service.impl.TransferServiceImpl.updateAccountByCardNo(com.lagou
.pojo.Account))"></aop:before>
 </aop:aspect>
</aop:config>
  • 細(xì)節(jié)

a. 關(guān)于切入點表達(dá)式
上述配置實現(xiàn)了對 TransferServiceImpl 的 updateAccountByCardNo ?法進(jìn)?增強,在其執(zhí)?之前朱沃,輸出了記錄?志的語句晴玖。這??,我們接觸了?個?較陌?的名稱:切?點表達(dá)式为流。
切?點表達(dá)式,也稱之為AspectJ切?點表達(dá)式让簿,指的是遵循特定語法結(jié)構(gòu)的字符串敬察,其作?是?于對符合語法格式的連接點進(jìn)?增強。(可以百度一下具體規(guī)則)

b. 改變代理方式的配置
Spring在選擇創(chuàng)建代理對象時尔当,會根據(jù)被代理對象的實際情況來選擇的莲祸。被代理對象實現(xiàn)了接?,則采?基于接?的動態(tài)代理椭迎。當(dāng)被代理對象沒有實現(xiàn)任何接?的時候锐帜,Spring會?動切換到基于?類的動態(tài)代理?式。
但是我們都知道畜号,?論被代理對象是否實現(xiàn)接?缴阎,只要不是final修飾的類(被final修飾的類無法被繼承)都可以采?cglib提供的?式創(chuàng)建代理對象。所以Spring也考慮到了這個情況简软,提供了配置的?式實現(xiàn)強制使?基于?類的動態(tài)代理(即cglib的?式)蛮拔,配置的?式有兩種

  1. 使用aop:config標(biāo)簽配置
<aop:config proxy-target-class="true">
  1. 使用aop:aspectj-autoproxy標(biāo)簽配置
<!--此標(biāo)簽是基于XML和注解組合配置AOP時的必備標(biāo)簽,表示Spring開啟注解配置AOP的?持-->
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj?autoproxy>

c. 五種通知類型

  1. 前置通知
  2. 正常執(zhí)行時通知
  3. 異常通知
  4. 最終通知
  5. 環(huán)繞通知

配置方式demo 痹升,以環(huán)繞通知舉例:

<!--
 作?:
 ?于配置環(huán)繞通知建炫。
 出現(xiàn)位置:
 它只能出現(xiàn)在aop:aspect標(biāo)簽的內(nèi)部
 屬性:
 method:?于指定環(huán)繞通知的?法名稱
 pointcut:?于指定切?點表達(dá)式
 pointcut-ref:?于指定切?點表達(dá)式的引?
 -->
<aop:around method="aroundPrintLog" pointcut-ref="pt1"></aop:around>

4.1 XML+注解模式

  • XML中開啟Spring對注解AOP的支持
<!--開啟spring對注解aop的?持-->
<aop:aspectj-autoproxy/>
  • 示例
/**
* 模擬記錄?志
* @author 應(yīng)癲
*/
@Component
@Aspect
public class LogUtil {
 /**
 * 我們在xml中已經(jīng)使?了通?切?點表達(dá)式,供多個切?使?疼蛾,那么在注解中如何使?呢肛跌?
 * 第?步:編寫?個?法
 * 第?步:在?法使?@Pointcut注解
 * 第三步:給注解的value屬性提供切?點表達(dá)式
 * 細(xì)節(jié):
 * 1.在引?切?點表達(dá)式時,必須是?法名+()察郁,例如"pointcut()"衍慎。
 * 2.在當(dāng)前切?中使?,可以直接寫?法名绳锅。在其他切?中使?必須是全限定?法名西饵。
 */
 @Pointcut("execution(* com.lagou.service.impl.*.*(..))")
 public void pointcut(){}
 @Before("pointcut()")
 public void beforePrintLog(JoinPoint jp){
 Object[] args = jp.getArgs();
 System.out.println("前置通知:beforePrintLog,參數(shù)是:"+
Arrays.toString(args));
 }
 @AfterReturning(value = "pointcut()",returning = "rtValue")
 public void afterReturningPrintLog(Object rtValue){
 System.out.println("后置通知:afterReturningPrintLog鳞芙,返回值
是:"+rtValue);
 }
 @AfterThrowing(value = "pointcut()",throwing = "e")
 public void afterThrowingPrintLog(Throwable e){
 System.out.println("異常通知:afterThrowingPrintLog眷柔,異常是:"+e);
 }
 @After("pointcut()")
 public void afterPrintLog(){
 System.out.println("最終通知:afterPrintLog");
 }
 /**
 * 環(huán)繞通知
 * @param pjp
 * @return
 */
 @Around("pointcut()")
 public Object aroundPrintLog(ProceedingJoinPoint pjp){
 //定義返回值
 Object rtValue = null;
 try{
 //前置通知
 System.out.println("前置通知");
 //1.獲取參數(shù)
 Object[] args = pjp.getArgs();
 //2.執(zhí)?切?點?法
 rtValue = pjp.proceed(args);
 //后置通知
 System.out.println("后置通知");
 }catch (Throwable t){
 //異常通知
 System.out.println("異常通知");
 t.printStackTrace();
 }finally {
 //最終通知
 System.out.println("最終通知");
 }
 return rtValue;
 }
}

4.3 注解模式

在使用注解驅(qū)動開發(fā)aop時期虾,我們要明確的是,是注解替換掉配置文件中下面的這行配置

<!--開啟spring對注解aop的?持-->
<aop:aspectj-autoproxy/>

在配置類中使用如下注解進(jìn)行替換上述配置

@Configuration
@ComponentScan("com.lagou")
@EnableAspectJAutoProxy //開啟spring對注解AOP的?持
public class SpringConfiguration {
}

5. Spring 聲明型事務(wù)的支持

編程型事務(wù):在業(yè)務(wù)中添加事務(wù)控制代碼驯嘱,這樣的事務(wù)控制機制就叫做編程型事務(wù)镶苞。
聲明型事務(wù):通過xml或者注解配置的方式達(dá)到事務(wù)控制的目的,叫做聲明型事務(wù)鞠评。

5.1 事務(wù)的四大特性

  • 原子性(Atomicity):原子性是指事務(wù)是一個不可分割的單位茂蚓,事務(wù)中的操作要么都發(fā)生,要么都不發(fā)生剃幌。
  • 持久性(Durability):持久性是指一個事務(wù)一旦被提交聋涨,它對數(shù)據(jù)庫的數(shù)據(jù)的改變就是永久性的,接下來即使數(shù)據(jù)庫發(fā)生故障也不應(yīng)對其由任何影響负乡。
  • 一致性(Consistency):事務(wù)必須使數(shù)據(jù)庫從一致性狀態(tài)變換到另一個一致性狀態(tài)牍白。
  • 隔離性(Isolation):事務(wù)的隔離性是多個用戶并發(fā)訪問數(shù)據(jù)庫時,數(shù)據(jù)庫為每一個用戶開啟的事務(wù)抖棘,每個事務(wù)不能被其他事務(wù)的操作數(shù)據(jù)所干擾茂腥,多個并發(fā)事務(wù)之間要相互隔離。

5.2 事務(wù)的隔離級別

不考慮隔離級別切省,會出現(xiàn)以下情況:(以下情況都是錯誤的)最岗,也即為隔離級別在解決事務(wù)并發(fā)問題

  • 臟讀:一個線程中的事務(wù)讀到了另外一個線程中未提交的數(shù)據(jù)。
  • 不可重復(fù)讀:一個線程中的事務(wù)讀到了另一個線程中已經(jīng)提交的update數(shù)據(jù)(前后內(nèi)容不一樣)

場景(不可重復(fù)讀):
員工A發(fā)起事務(wù)1朝捆,查詢工資般渡,工資為1w,此時事務(wù)1未關(guān)閉
財務(wù)人員發(fā)起事務(wù)2右蹦,給A漲了2000塊诊杆,并提交了事務(wù)。
此時A通過事務(wù)1再次發(fā)起查詢請求何陆,發(fā)現(xiàn)工資為1.2w晨汹,原來讀出來的1w已經(jīng)讀不到了,叫做不可重復(fù)讀贷盲。

  • 幻讀:一個線程中的事務(wù)讀到了另外一個線程中已經(jīng)提交的insert或delete的數(shù)據(jù)(前后條數(shù)不一樣)

場景(幻讀)
事務(wù)1查詢所有工資為1w的員工的總數(shù)淘这,查詢出來10個人,此時事務(wù)未關(guān)閉巩剖。
事務(wù)2財務(wù)人員發(fā)起铝穷,新來員工,工資1w佳魔,向表中插入了2條數(shù)據(jù)曙聂,并且提交了事務(wù)。
事務(wù)1再次查詢工資1w的員工發(fā)現(xiàn)有12個人鞠鲜,叫做幻讀宁脊。

數(shù)據(jù)庫共定義了四種隔離級別:

  • Serializable(串行化):可避免臟讀断国,不可重復(fù)讀,幻讀等情況發(fā)生榆苞。
  • Repeatable read(可重復(fù)讀):可避免臟讀稳衬、不可重復(fù)讀等情況的發(fā)生(幻讀可能發(fā)生),該機制下會對要update的行進(jìn)行加鎖坐漏。
  • Read committed(讀已提交):可避免臟讀情況發(fā)生薄疚,不可重復(fù)讀和幻讀一定會發(fā)生。
  • Read uncommitted(讀未提交):最低級別赊琳,以上情況均無法保證街夭。
    注意:級別依次升高,效率依次降低躏筏。
    Mysql默認(rèn)隔離級別為 Repeatable read莱坎。

5.3 事務(wù)的傳播行為

事務(wù)往往由service層進(jìn)行控制,如果出現(xiàn)service層方法A調(diào)用另一個service方法B寸士,A和B方法本身被添加了事務(wù)控制,那么A調(diào)用B的時候碴卧,就需要進(jìn)行事物的一些協(xié)商弱卡,這就叫做事務(wù)的傳播行為。
A調(diào)用B住册,我們站在B的角度來定義事務(wù)的傳播行為婶博。

  • propagation_required: 如果當(dāng)前沒有事務(wù),就新建?個事務(wù)荧飞,如果已經(jīng)存在?個事務(wù)中凡人,加?到這個事務(wù)中。這是最常?的選擇

第一點:如果serviceA沒有事務(wù)叹阔,serviceB有事務(wù)挠轴,serviceB拋異常,則serviceB回滾耳幢,serviceA不回滾岸晦。為什么呢,因為當(dāng)前沒有事務(wù)睛藻,則新建一個事務(wù)启上。新建的事務(wù)不受serviceB的影響。新建的事務(wù)和serviceB的事務(wù)相互獨立店印。
第二點:如果serviceA有事務(wù)冈在,serviceB有事務(wù),serviceB拋異常按摘,則serviceB回滾包券,serviceA回滾纫谅。為什么呢,因為當(dāng)前有事務(wù)兴使,則支持當(dāng)前事務(wù)系宜。serviceA的事務(wù)和serviceB的事務(wù)建立了聯(lián)系,不是相互獨立的发魄。

  • propagation_supports:支持當(dāng)前事務(wù)盹牧,如當(dāng)前沒有就以非事務(wù)方式執(zhí)行。(有點類似不加事務(wù)注解)
  • propagation_mandatory:使?當(dāng)前的事務(wù)励幼,如果當(dāng)前沒有事務(wù)汰寓,就拋出異常。
  • propagation_requires_new: 新建事務(wù)苹粟,如果當(dāng)前存在事務(wù)有滑,把當(dāng)前事務(wù)掛起
  • propagation_not_supported: 以?事務(wù)?式執(zhí)?操作,如果當(dāng)前存在事務(wù)嵌削,就把當(dāng)前事務(wù)掛起毛好。
  • propagation_never: 以?事務(wù)?式執(zhí)?,如果當(dāng)前存在事務(wù)苛秕,則拋出異常肌访。
  • propagation_nested: 如果當(dāng)前存在事務(wù),則在嵌套事務(wù)內(nèi)執(zhí)?艇劫。如果當(dāng)前沒有事務(wù)吼驶,則執(zhí)?與PROPAGATION_REQUIRED類似的操作。

5.4 Spring中事務(wù)的API

mybatis:sqlSession.commit;
hibernate:session.commit;
PlatformTransactionManager

public interface PlatformTransactionManager extends TransactionManager {
//獲取事務(wù)狀態(tài)信息
    TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;
//提交事務(wù)
    void commit(TransactionStatus var1) throws TransactionException;
//回滾事務(wù)
    void rollback(TransactionStatus var1) throws TransactionException;
}

作用
此接口是Spring的事務(wù)管理器核心接口店煞。Spring本身并不支持事務(wù)實現(xiàn)蟹演,只是提供標(biāo)準(zhǔn),應(yīng)用底層支持什么樣的事務(wù)顷蟀,需要提供具體實現(xiàn)類酒请。此處也是策略模式的具體應(yīng)用。

5.5 Spring聲明型事務(wù)配置

  • 純xml模式
<dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-context</artifactId>
 <version>5.1.12.RELEASE</version>
</dependency>
<dependency>
 <groupId>org.aspectj</groupId>
 <artifactId>aspectjweaver</artifactId>
 <version>1.9.4</version>
</dependency>
<dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-jdbc</artifactId>
 <version>5.1.12.RELEASE</version>
</dependency>
<dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-tx</artifactId>
 <version>5.1.12.RELEASE</version>
</dependency>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
 <!--定制事務(wù)細(xì)節(jié)鸣个,傳播?為蚌父、隔離級別等-->
 <tx:attributes>
 <!--?般性配置 
propagation 事務(wù)傳播行為
isolation="DEFAULT" 采用數(shù)據(jù)庫默認(rèn)隔離級別
-->
 <tx:method name="*" read-only="false"
propagation="REQUIRED" isolation="DEFAULT" timeout="-1"/>
 <!--針對查詢的覆蓋性配置-->
 <tx:method name="query*" read-only="true"
propagation="SUPPORTS"/>
 </tx:attributes>
 </tx:advice>
 <aop:config>
 <!--advice-ref指向增強=橫切邏輯+?位-->
 <aop:advisor advice-ref="txAdvice" pointcut="execution(*
com.lagou.edu.service.impl.TransferServiceImpl.*(..))"/>
 </aop:config>
  • 基于XML+注解
    XML配置
<!--配置事務(wù)管理器-->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManage
r">
 <property name="dataSource" ref="dataSource"></property>
</bean>
<!--開啟spring對注解事務(wù)的?持-->
<tx:annotation-driven transaction-manager="transactionManager"/>

在接?、類或者?法上添加@Transactional注解

@Transactional(readOnly = true,propagation = Propagation.SUPPORTS)
  • 基于純注解
    Spring基于注解驅(qū)動開發(fā)的事務(wù)控制配置毛萌,只需要把xml配置部分改為注解實現(xiàn)苟弛。只是需要一個注解替換掉xml配置文件中的
    <tx:annotation-driven transaction-manager="transactionManager"/>配置。
    在Spring的配置類上添加@EnableTransactionManagement 注解即可阁将。
@EnableTransactionManagement//開啟spring注解事務(wù)的?持
public class SpringConfiguration {
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末膏秫,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌缤削,老刑警劉巖窘哈,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異亭敢,居然都是意外死亡滚婉,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進(jìn)店門帅刀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來让腹,“玉大人,你說我怎么就攤上這事扣溺『希” “怎么了?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵锥余,是天一觀的道長腹纳。 經(jīng)常有香客問我,道長驱犹,這世上最難降的妖魔是什么嘲恍? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮雄驹,結(jié)果婚禮上蛔钙,老公的妹妹穿的比我還像新娘。我一直安慰自己荠医,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布桑涎。 她就那樣靜靜地躺著彬向,像睡著了一般。 火紅的嫁衣襯著肌膚如雪攻冷。 梳的紋絲不亂的頭發(fā)上娃胆,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天,我揣著相機與錄音等曼,去河邊找鬼里烦。 笑死,一個胖子當(dāng)著我的面吹牛禁谦,可吹牛的內(nèi)容都是我干的胁黑。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼州泊,長吁一口氣:“原來是場噩夢啊……” “哼丧蘸!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起遥皂,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤力喷,失蹤者是張志新(化名)和其女友劉穎刽漂,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體弟孟,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡贝咙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了拂募。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片庭猩。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖没讲,靈堂內(nèi)的尸體忽然破棺而出眯娱,到底是詐尸還是另有隱情,我是刑警寧澤爬凑,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布徙缴,位于F島的核電站,受9級特大地震影響嘁信,放射性物質(zhì)發(fā)生泄漏于样。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一潘靖、第九天 我趴在偏房一處隱蔽的房頂上張望穿剖。 院中可真熱鬧,春花似錦卦溢、人聲如沸糊余。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽贬芥。三九已至,卻和暖如春宣决,著一層夾襖步出監(jiān)牢的瞬間蘸劈,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工尊沸, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留威沫,地道東北人站刑。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓邻薯,卻偏偏與公主長得像,于是被迫代替她去往敵國和親立莉。 傳聞我的和親對象是個殘疾皇子屁商,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,877評論 2 345

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