SpringMVC的簡介和工作流程

一伊佃、簡介

?Spring MVC屬于SpringFrameWork的后續(xù)產(chǎn)品辅斟,已經(jīng)融合在Spring Web Flow里面。Spring 框架提供了構(gòu)建 Web 應(yīng)用程序的全功能 MVC 模塊噩斟。SpringMVC是一種web層的mvc框架阳欲,用于替代servlet(處理響應(yīng)請求巴元,獲取表單參數(shù)毡咏,表單驗證等)

二、工作流程

image.png
  1. 用戶發(fā)送請求至前端控制器DispatcherServlet逮刨。
  2. DispatcherServlet收到請求調(diào)用HandlerMapping處理器映射器呕缭。
  3. 處理器映射器找到具體的處理器(可以根據(jù)xml配置、注解進行查找)禀忆,生成處理器對象及處理器攔截器(如果有則生成)一并返回給DispatcherServlet臊旭。
  4. DispatcherServlet調(diào)用HandlerAdapter處理器適配器。
  5. HandlerAdapter經(jīng)過適配調(diào)用具體的處理器(Controller箩退,也叫后端控制器)离熏。
  6. Controller執(zhí)行完成返回ModelAndView。
  7. HandlerAdapter將controller執(zhí)行結(jié)果ModelAndView返回給DispatcherServlet戴涝。
  8. DispatcherServlet將ModelAndView傳給ViewReslover視圖解析器滋戳。
  9. ViewReslover解析后返回具體View
  10. DispatcherServlet根據(jù)View進行渲染視圖(即將模型數(shù)據(jù)填充至視圖中)。
  11. DispatcherServlet響應(yīng)用戶啥刻。

三奸鸯、理解

1、為什么要使用springMVC可帽?

?SpringMVC是一種基于Java娄涩,實現(xiàn)了Web MVC設(shè)計模式,請求驅(qū)動類型的輕量級Web框架,即使用了MVC架構(gòu)模式的思想蓄拣,將Web層進行職責(zé)解耦扬虚。基于請求驅(qū)動指的就是使用請求-響應(yīng)模型球恤,框架的目的就是幫助我們簡化開發(fā)辜昵,SpringMVC也是要簡化日常Web開發(fā)。(處理業(yè)務(wù)數(shù)據(jù)的對象和顯示業(yè)務(wù)數(shù)據(jù)的視圖之間存在緊密耦合)

2咽斧、什么是MVC設(shè)計模式堪置?

?MVC即Model-View-Controller,將應(yīng)用按照Model(模型)张惹、View(視圖)舀锨、Controller(控制)這樣的方式分離。
?視圖(View):代表用戶交互界面诵叁,對于Web應(yīng)用來說雁竞,可以是HTML钦椭,也可能是jsp拧额、XML和Applet等。一個應(yīng)用可能有很多不同的視圖彪腔,MVC設(shè)計模式對于視圖的處理僅限于視圖上數(shù)據(jù)的采集和處理侥锦,以及用戶的請求,而不包括在視圖上的業(yè)務(wù)流程的處理德挣。業(yè)務(wù)流程的處理交予模型(Model)處理恭垦。
? 模型(Model):是業(yè)務(wù)的處理以及業(yè)務(wù)規(guī)則的制定。模型接受視圖請求的數(shù)據(jù)格嗅,并返回最終的處理結(jié)果番挺。業(yè)務(wù)模型的設(shè)計是MVC最主要的核心。MVC設(shè)計模式告訴我們屯掖,把應(yīng)用的模型按一定的規(guī)則抽取出來玄柏,抽取的層次很重要,抽象與具體不能隔得太遠贴铜,也不能太近粪摘。MVC并沒有提供模型的設(shè)計方法,而只是組織管理這些模型绍坝,以便于模型的重構(gòu)和提高重用性徘意。
? 控制(Controller):可以理解為從用戶接收請求, 將模型與視圖匹配在一起,共同完成用戶的請求轩褐。劃分控制層的作用也很明顯椎咧,它清楚地告訴你,它就是一個分發(fā)器把介,選擇什么樣的模型勤讽,選擇什么樣的視圖竹宋,可以完成什么樣的用戶請求〉丶迹控制層并不做任何的數(shù)據(jù)處理蜈七。

3、SpringMVC的特點

  • 清晰的角色劃分:控制器(controller)莫矗、驗證器(validator)飒硅、 命令對象(command object)、表單對象(formobject)作谚、模型對象(model object)三娩、 Servlet分發(fā)器(DispatcherServlet)、處理器映射(handler mapping)妹懒、視圖解析器(view resolver)等雀监。每一個角色都可以由一個專門的對象來實現(xiàn)。
  • 強大而直接的配置方式:將框架類和應(yīng)用程序類都能作為JavaBean配置眨唬,支持跨多個context的引用会前,例如,在web控制器中對業(yè)務(wù)對象和驗證器(validator)的引用匾竿。
  • 可適配瓦宜、非侵入:可以根據(jù)不同的應(yīng)用場景,選擇合適的控制器子類 (simple型岭妖、command型临庇、form型、wizard型昵慌、multi-action型或者自定義)假夺,而不是從單一控制器 (比如Action/ActionForm)繼承。
  • 可重用的業(yè)務(wù)代碼:可以使用現(xiàn)有的業(yè)務(wù)對象作為命令或表單對象斋攀,而不需要去擴展某個特定框架的基類已卷。
  • 可定制的綁定(binding) 和驗證(validation):比如將類型不匹配作為應(yīng)用級的驗證錯誤, 這可以保存錯誤的值蜻韭。再比如本地化的日期和數(shù)字綁定等等悼尾。在其他某些框架中,你只能使用字符串表單對象肖方,需要手動解析它并轉(zhuǎn)換到業(yè)務(wù)對象闺魏。
  • 可定制的handlermapping和view resolution:Spring提供從最簡單的URL映射, 到復(fù)雜的俯画、專用的定制策略析桥。與某些webMVC框架強制開發(fā)人員使用單一特定技術(shù)相比,Spring顯得更加靈活。
  • 靈活的model轉(zhuǎn)換:在Springweb框架中泡仗,使用基于Map的 鍵/值對來達到輕易地與各種視圖技術(shù)的集成埋虹。
    可定制的本地化和主題(theme)解析:支持在JSP中可選擇地使用Spring標簽庫、支持JSTL娩怎、支持Velocity(不需要額外的中間層)等等搔课。
  • 簡單而強大的JSP標簽庫(SpringTag Library):支持包括諸如數(shù)據(jù)綁定和主題(theme) 之類的許多功能。
  • JSP表單標簽庫:在Spring2.0中引入的表單標簽庫截亦,使得在JSP中編寫 表單更加容易爬泥。
  • Spring Bean的生命周期可以被限制在當(dāng)前的HTTP Request或者HTTP Session。

4崩瓤、SpringMVC的優(yōu)點

  • 讓我們能非常簡單的設(shè)計出干凈的Web層和薄薄的Web層
  • 進行更簡潔的Web層的開發(fā)
  • 天生與Spring框架集成(如IoC容器袍啡、AOP等)
  • 提供強大的約定大于配置的契約式編程支持
  • 非常靈活的數(shù)據(jù)驗證、格式化和數(shù)據(jù)綁定機制
  • 支持Restful風(fēng)格

5却桶、SpringMVC的入門程序

web.xml

<web-app>
    <servlet>
        <!-- 加載前端控制器 -->
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 
           加載配置文件
           默認加載規(guī)范:
           * 文件命名:xxx-servlet.xml(xxx=定義的servlet-name 即<servlet-name>springmvc</servlet-name>)====springmvc-servlet.xml
           * 路徑規(guī)范:必須在WEB-INF目錄下面
           修改加載路徑:
        -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>   
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>
</web-app>

springmvc.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:mvc="http://www.springframework.org/schema/mvc"
    xmlns:aop="http://www.springframework.org/schema/aop"
    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
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
        <!-- 1.處理器映射器 -->
        <!-- 方式一  配置映射處理器:根據(jù)bean(自定義Controller)的name屬性的url去尋找handler境输;springmvc默認的映射處理器是BeanNameUrlHandlerMapping-->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean>
    <!-- 方式二 -->
    <!-- <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <props>
                <prop key="/hello.do">myController</prop>
            </props>
        </property>
    </bean>
        <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="urlMap">
            <map>
                <entry key="/login1.action" value="userController"></entry>
                <entry key="/login2.action" value="userController2"></entry>
            </map>
        </property>
    </bean> -->
    
        <!-- 2.處理器適配器 -->
    <!-- 方式一 配置處理器適配器來執(zhí)行Controller ,springmvc默認的是SimpleControllerHandlerAdapter
    -->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean>
    <!-- 方式二 貼近原生態(tài)  -->
    <!-- <bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter"></bean>-->

    <!-- 3.處理器:配置自定義Controller -->
    <bean id="myController" name="/hello.do" class="com.howick.controller.MyController"></bean>
    <bean id="userController2"  name="/login2.action" class="com.neuedu.controller.UserController2"></bean> 
    
    <!-- 4.配置sprigmvc視圖解析器:解析邏輯視圖; 
        后臺返回邏輯視圖:index
        視圖解析器解析出真正物理視圖:前綴+邏輯視圖+后綴====/WEB-INF/jsps/index.jsp
    -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsps/"></property>
        <property name="suffix" value=".jsp"></property>        
    </bean>
</beans>

自定義處理器(適配器方式一)實現(xiàn)Controller接口

public class MyController implements Controller{
 
    public ModelAndView handleRequest(HttpServletRequest arg0,
            HttpServletResponse arg1) throws Exception {
        ModelAndView mv = new ModelAndView();
        //設(shè)置頁面回顯數(shù)據(jù)
        mv.addObject("hello", "歡迎學(xué)習(xí)springmvc颖系!");
        
        //返回物理視圖
        //mv.setViewName("/WEB-INF/jsps/index.jsp");
        
        //返回邏輯視圖
        mv.setViewName("index");
        return mv;
    }
}

自定義處理器(適配器方式二)實現(xiàn)HttpRequestHandler 接口

public class UserController2 implements HttpRequestHandler {

    @Override
    public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        
        //保存消息 
        HttpSession session = request.getSession();
        session.setAttribute("message", "恭喜你成功訪問第一個springMVC環(huán)境成功了嗅剖!--實現(xiàn)方式為:HttpRequestHandlerAdapter");
        
        //頁面跳轉(zhuǎn)(使用重定向,保存消息那里集晚,我們要用 窗悯? request session application pageContext )
        response.sendRedirect(request.getContextPath()+"/main.jsp");
        

    }

}

SpringMVC注解方式處理器適配器区匣、映射器

注解映射器

在spring3.1版本之前偷拔,系統(tǒng)默認加載DispatcherServlet.properoties文件中的org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping注解映射器,3.1版本之后要使用org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping這個注解映射器亏钩。在springmvc.xml中進行RequestMappingHandlerMapping的配置:

<!--注解處理器映射器-->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>

使用RequestMappingHandlerMapping需要在Handler 中使用@controller標識此類是一個控制器莲绰,使用@requestMapping指定Handler方法所對應(yīng)的url。

注解適配器

Spring3.1之前默認加載DispatcherServlet.properoties中的注解適配器是
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter姑丑,3.1版本之后要使用:
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter這個注解適配器蛤签。在springmvc.xml中進行如下配置:

<!--注解的適配器-->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>

RequestMappingHandlerAdapter,不要求Handler實現(xiàn)任何接口栅哀,它需要和RequestMappingHandlerMapping注解映射器配對使用震肮,主要解析Handler方法中的形參。

也可以使用使用 <mvc:annotation-driven/> 標簽來配置留拾,它是一種簡寫模式戳晌,它會自動注冊處理器適配器,配置方式如下:

<!-- 處理器映射器 處理器適配器 他們兩個可以通過注解進行配置痴柔,而且一個注解即可搞定兩步配置 -->
<mvc:annotation-driven></mvc:annotation-driven>

自定義控制器

//使用Controller來標識它是一個控制器
@Controller
public class TestControllerTest{
    @RequestMapping("/testurl")
    public ModelAndView testurl() throws Exception{
        //邏輯代碼
    }
}

然后在springmvc.xml中對該Handler進行配置:

<!--注解的Handler不用id沦偎,url在@RequestMapping注解中已經(jīng)聲明-->
<bean class="com.howick.controller.TestControllerTest"/>

上面我們通過對單個注解Handler的配置,也可以使用組件掃描<context:component-scan base-package="包名"/>對整個包下的Handler進行配置。

<context:component-scan base-package="com.howick.controller"/>

6豪嚎、SpringMVC常用注解及其作用

@Controller:標識這個類是一個控制器
@RequestMapping:給控制器方法綁定一個uri
@ResponseBody:將java對象轉(zhuǎn)成json搔驼,并且發(fā)送給客戶端
@RequestBody:將客戶端請求過來的json轉(zhuǎn)成java對象
@RequestParam:當(dāng)表單參數(shù)和方法形參名字不一致時,做一個名字映射
@PathVarible:用于獲取uri中的參數(shù),比如user/1中1的值

Rest風(fēng)格的新api

@RestController相當(dāng)于@Controller+ @ResponseBody
@GetMapping@DeleteMapping@PostMapping@PutMapping

其他注解

@SessionAttribute:聲明將什么模型數(shù)據(jù)存入session
@CookieValue:獲取cookie值
@ModelAttribute:將方法返回值存入model中
@HeaderValue:獲取請求頭中的值

7侈询、SpringMVC和Struts2的對比

框架機制:SpringMVC的入口是servlet舌涨,而Struts2是filter。
Filter在容器啟動后就初始化扔字,服務(wù)停止后銷毀泼菌,晚于Servlet;Servlet是在調(diào)用之后初始化并且先于Filter調(diào)用啦租,服務(wù)停止后銷毀哗伯。
調(diào)用順序:filter的調(diào)用順序:

  1. 按照web.xml中的映射配置順序按照配置條件從后向前調(diào)用
  2. 層次調(diào)用doFilter()方法中FilterChain.doFilter()之前的內(nèi)容(filter-mapping的name先調(diào)用doFilter方法,但是每個dofilter方法的內(nèi)部存在chain.dofilter會調(diào)用下一個filter-mapping,一直到不存在下一個filter后在返回篷角,執(zhí)行chain.dofilter()后面的代碼)(相當(dāng)于遞歸調(diào)用)
  3. 調(diào)用Servlet中的service()方法
  4. service方法執(zhí)行完畢后焊刹,層次調(diào)用doFilter()中FilterChain.doFilter()之后的方法,順序與之前的相反

例如:

  • filter和servlet同時存在恳蹲,且容器初始化都要加載虐块,則先加載filter再加載servlet的init方法。
  • 如果請求的url既匹配filter又匹配servlet嘉蕾,并且servlet的init方法沒有在容器初始化加載贺奠,則先加載匹配的servlet的最后一個servlet的init方法,再按順序執(zhí)行filter方法错忱,最后再執(zhí)行匹配的最后一個servlet方法

攔截機制:

  • Struts2:
  1. Struts2框架是類級別的攔截儡率,每次請求就會創(chuàng)建一個Action,和Spring整合時Struts2的ActionBean注入作用域是原型模式prototype(否則會出現(xiàn)線程并發(fā)問題)以清,然后通過setter儿普,getter吧request數(shù)據(jù)注入到屬性;
  2. 一個Action對應(yīng)一個request掷倔,response上下文眉孩,在接收參數(shù)時,可以通過屬性接收勒葱,說明屬性參數(shù)是讓多個方法共享的浪汪;
  3. Action的一個方法可以對應(yīng)一個url,而其類屬性卻被所有方法共享凛虽,這也就無法用注解或其他方式標識其所屬方法了死遭。
  • SpringMVC:
  1. SpringMVC是方法級別的攔截,一個方法對應(yīng)一個Request上下文涩维,所以方法直接基本上是獨立的殃姓,獨享request袁波,response數(shù)據(jù)。而每個方法同時又何一個url對應(yīng)蜗侈,參數(shù)的傳遞是直接注入到方法中的篷牌,是方法所獨有的。處理結(jié)果通過ModeMap返回給框架踏幻;
  2. 在Spring整合時枷颊,SpringMVC的Controller Bean默認單例模式Singleton,所以默認對所有的請求该面,只會創(chuàng)建一個Controller夭苗,有應(yīng)為沒有共享的屬性,所以是線程安全的隔缀,如果要改變默認的作用域题造,需要添加@Scope注解修改;
    Struts2有自己的攔截Interceptor機制猾瘸,SpringMVC這是用的是獨立的Aop方式界赔,這樣導(dǎo)致Struts2的配置文件量還是比SpringMVC大。

性能方面:SpringMVC實現(xiàn)了零配置牵触,由于SpringMVC基于方法的攔截淮悼,有加載一次單例模式bean注入。而Struts2是類級別的攔截揽思,每次請求對應(yīng)實例一個新的Action袜腥,需要加載所有的屬性值注入,所以钉汗,SpringMVC開發(fā)效率和性能高于Struts2羹令。

配置方面:spring MVC和Spring是無縫的。從這個項目的管理和安全上也比Struts2高(當(dāng)然Struts2也可以通過不同的目錄結(jié)構(gòu)和相關(guān)配置做到SpringMVC一樣的效果儡湾,但是需要xml配置的地方不少)特恬;
SpringMVC可以認為已經(jīng)100%零配置。

設(shè)計思想:Struts2更加符合OOP的編程思想徐钠, SpringMVC就比較謹慎,在servlet上擴展役首。

集成方面:SpringMVC集成了Ajax尝丐。

注意:springmvc是單例模式的框架,但它是線程安全的,因為springmvc沒有成員變量,所有參數(shù)的封裝都是基于方法的,屬于當(dāng)前線程的私有變量. 因此是線程安全的框架。所以效率高衡奥。

struts action是多例的爹袁。所以可以使用成員變量獲取參數(shù)。所以效率低矮固。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末失息,一起剝皮案震驚了整個濱河市譬淳,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌盹兢,老刑警劉巖邻梆,帶你破解...
    沈念sama閱讀 216,692評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異绎秒,居然都是意外死亡浦妄,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評論 3 392
  • 文/潘曉璐 我一進店門见芹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來剂娄,“玉大人,你說我怎么就攤上這事玄呛≡呐常” “怎么了?”我有些...
    開封第一講書人閱讀 162,995評論 0 353
  • 文/不壞的土叔 我叫張陵徘铝,是天一觀的道長故黑。 經(jīng)常有香客問我,道長庭砍,這世上最難降的妖魔是什么场晶? 我笑而不...
    開封第一講書人閱讀 58,223評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮怠缸,結(jié)果婚禮上诗轻,老公的妹妹穿的比我還像新娘。我一直安慰自己揭北,他們只是感情好扳炬,可當(dāng)我...
    茶點故事閱讀 67,245評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著搔体,像睡著了一般恨樟。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上疚俱,一...
    開封第一講書人閱讀 51,208評論 1 299
  • 那天劝术,我揣著相機與錄音,去河邊找鬼呆奕。 笑死养晋,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的梁钾。 我是一名探鬼主播绳泉,決...
    沈念sama閱讀 40,091評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼姆泻!你這毒婦竟也來了零酪?” 一聲冷哼從身側(cè)響起冒嫡,我...
    開封第一講書人閱讀 38,929評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎四苇,沒想到半個月后孝凌,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,346評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡蛔琅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,570評論 2 333
  • 正文 我和宋清朗相戀三年胎许,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片罗售。...
    茶點故事閱讀 39,739評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡辜窑,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出寨躁,到底是詐尸還是另有隱情穆碎,我是刑警寧澤,帶...
    沈念sama閱讀 35,437評論 5 344
  • 正文 年R本政府宣布职恳,位于F島的核電站所禀,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏放钦。R本人自食惡果不足惜色徘,卻給世界環(huán)境...
    茶點故事閱讀 41,037評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望操禀。 院中可真熱鬧褂策,春花似錦、人聲如沸颓屑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽揪惦。三九已至遍搞,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間器腋,已是汗流浹背溪猿。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蒂培,地道東北人再愈。 一個月前我還...
    沈念sama閱讀 47,760評論 2 369
  • 正文 我出身青樓,卻偏偏與公主長得像护戳,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子垂睬,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,647評論 2 354