Spring Web MVC框架(八) 配置Spring Web MVC

這一篇文章對(duì)應(yīng)于Spring參考文檔 Configuring Spring MVC生真,講的是Spring Web MVC各部分的配置方法卿闹,包括Java代碼配置和XML文件配置以及MVC命名空間的使用方法。

啟用MVC Java配置和XML命名空間

默認(rèn)配置

要啟用MVC Java配置(@Controller等各種注解)和XML命名空間竞端,如果使用的是Java配置屎即,在配置類上再添加@EnableWebMvc注解即可。

@Configuration
@EnableWebMvc
public class WebAppConfig {

}

如果使用XML配置文件的話事富,添加下面一行即可技俐。

<mvc:annotation-driven/>

不論使用哪種方式,都會(huì)在Spring中注冊(cè)一些組件來提供最基本的MVC功能统台。這些功能在文檔中說的很清楚雕擂。我簡(jiǎn)單翻譯了一下:

上面的配置會(huì)注冊(cè)一個(gè)RequestMappingHandlerMapping,一個(gè)RequestMappingHandlerAdapter和一個(gè)ExceptionHandlerExceptionResolver來提供注解控制器和注解方法(比如@RequestMapping和@ExceptionHandler等)處理請(qǐng)求的功能贱勃。

還會(huì)啟用以下功能:

  • 通過一個(gè)ConversionService實(shí)例井赌,來進(jìn)行Spring 3 方式的類型轉(zhuǎn)換及數(shù)據(jù)綁定支持谤逼。
  • @NumberFormat格式化數(shù)字字段的支持
  • @DateTimeFormat格式化DateCalendar仇穗、Long森缠、JodaTime類型字段的支持。
  • 在控制器方法上使用@Valid驗(yàn)證Bean的支持仪缸,如果檢測(cè)到JSR-303 Bean驗(yàn)證的實(shí)現(xiàn)。
  • 一組HttpMessageConverter列肢,用于在字符串和所需Java類型之間進(jìn)行類型轉(zhuǎn)換恰画,具體的列表參見Spring文檔 22.16.1. Enabling the MVC Java Config or the MVC XML Namespace

通過這些默認(rèn)配置瓷马,我們即可開始最基本的Spring MVC使用拴还。

自定義配置

上面提供了最基本的配置。如果需要自定義某些配置也可以欧聘。如果使用Java配置的話片林,讓配置類實(shí)現(xiàn)WebMvcConfigurer接口,更常用的辦法是繼承WebMvcConfigurerAdapter基類怀骤,通過重寫基類中的方法即可配置相關(guān)功能费封。

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

        //有很多個(gè)方法可以重寫,來提供自定義功能

}

如果使用XML配置文件蒋伦,通過IDE的自動(dòng)補(bǔ)全功能查看一下<mvc:annotation-driven/>有哪些子屬性和子元素弓摘。

類型轉(zhuǎn)換和格式化

默認(rèn)情況下Spring注冊(cè)了Number(包括所有基本數(shù)字類型)和java.util.Date的類型轉(zhuǎn)換和格式化功能。要提供類型的轉(zhuǎn)換和格式化功能痕届,就需要自己注冊(cè)相應(yīng)的類型轉(zhuǎn)換器和格式化器韧献。

如果使用Java配置的話,重寫addFormatters(FormatterRegistry registry)方法并添加相應(yīng)功能即可研叫。

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        // Add formatters and/or converters
    }

}

如果使用XML配置的話锤窑,需要注冊(cè)一個(gè)ConversionService,然后添加到<mvc:annotation-driven>節(jié)點(diǎn)中嚷炉。

    <mvc:annotation-driven conversion-service="conversionService"/>

    <bean id="conversionService"
            class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
        <property name="converters">
            <set>
                <bean class="org.example.MyConverter"/>
            </set>
        </property>
        <property name="formatters">
            <set>
                <bean class="org.example.MyFormatter"/>
                <bean class="org.example.MyAnnotationFormatterFactory"/>
            </set>
        </property>
        <property name="formatterRegistrars">
            <set>
                <bean class="org.example.MyFormatterRegistrar"/>
            </set>
        </property>
    </bean>

驗(yàn)證功能

Spring自己提供了一組接口和類提供了一套驗(yàn)證功能渊啰。不過更通用的方法是使用Bean Validation進(jìn)行Java對(duì)象的驗(yàn)證,Bean Validation的一個(gè)實(shí)現(xiàn)就是Hibernate Validator渤昌。如果想簡(jiǎn)單了解一下Hibernate Validator虽抄,可以看一下我的文章Hibernate Validator簡(jiǎn)介。如果需要詳細(xì)使用方法請(qǐng)查看相關(guān)文檔博客独柑。

默認(rèn)情況下當(dāng)@EnableWebMvc<mvc:annotation-driven/>配置之后迈窟,如果Spring檢測(cè)到Bean Validation,就會(huì)自動(dòng)注冊(cè)一個(gè)LocalValidatorFactoryBean來提供驗(yàn)證功能忌栅。如果我們希望手動(dòng)處理驗(yàn)證過程车酣,可能希望將驗(yàn)證器實(shí)例注入到控制器中曲稼,這時(shí)候就不能使用自動(dòng)注冊(cè)的LocalValidatorFactoryBean了。這時(shí)候我們可以選擇手動(dòng)注冊(cè)一個(gè)LocalValidatorFactoryBeanBean實(shí)例湖员,然后注解@Primary讓自定義LocalValidatorFactoryBean被優(yōu)先使用贫悄。

還有一種辦法就是直接覆蓋Spring的默認(rèn)驗(yàn)證器配置。如果使用Java配置的話娘摔,重寫getValidator()方法即可窄坦。

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public Validator getValidator() {
        // return "global" validator
    }

}

如果使用XML配置文件,定義一個(gè)Validator然后添加到<mvc:annotation-driven>中凳寺。

<mvc:annotation-driven validator="globalValidator"/>

上面定義的都是全局驗(yàn)證器鸭津,我們也可以在某個(gè)控制器中定義一個(gè)局部驗(yàn)證器,然后和全局驗(yàn)證器結(jié)合起來使用肠缨。這時(shí)候需要使用@InitBinder注解方法逆趋。

@Controller
public class MyController {

    @InitBinder
    protected void initBinder(WebDataBinder binder) {
        binder.addValidators(new FooValidator());
    }

}

配置好驗(yàn)證器之后。當(dāng)Spring識(shí)別到@Valid注解的方法參數(shù)之后晒奕,就會(huì)執(zhí)行驗(yàn)證闻书,將驗(yàn)證結(jié)果綁定到BindingResult上,我們可以在方法中訪問BindingResult來獲取驗(yàn)證結(jié)果脑慧。

攔截器

我們實(shí)現(xiàn)了攔截器之后魄眉,就可以將其應(yīng)用到Web程序中。使用Java配置的話闷袒,重寫addInterceptors(InterceptorRegistry registry)方法杆融,然后在其中添加自己的攔截器即可。如果要配置攔截路徑和排除路徑也可以在這里配置霜运。

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LocaleInterceptor());
        registry.addInterceptor(new ThemeInterceptor()).addPathPatterns("/**").excludePathPatterns("/admin/**");
        registry.addInterceptor(new SecurityInterceptor()).addPathPatterns("/secure/*");
    }

}

使用XML配置文件的話可以使用MVC命名空間脾歇,配置也比較簡(jiǎn)單。

<mvc:interceptors>
    <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"/>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <mvc:exclude-mapping path="/admin/**"/>
        <bean class="org.springframework.web.servlet.theme.ThemeChangeInterceptor"/>
    </mvc:interceptor>
    <mvc:interceptor>
        <mvc:mapping path="/secure/*"/>
        <bean class="org.example.SecurityInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

視圖控制器

這是一種定義ParameterizableViewController的簡(jiǎn)單方式淘捡,當(dāng)該控制器被請(qǐng)求的時(shí)候不會(huì)執(zhí)行任何邏輯操作藕各,直接轉(zhuǎn)到相應(yīng)視圖。視圖控制器的常見用法是將網(wǎng)站的首頁直接和/請(qǐng)求映射焦除。

使用Java配置可以這樣寫激况,下面的配置將/映射到名為index的視圖。

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("index");
    }

}

使用XML配置也很簡(jiǎn)單膘魄。

<mvc:view-controller path="/" view-name="index"/>

視圖解析器

使用Java配置乌逐,只需要重寫configureViewResolvers(ViewResolverRegistry registry)方法即可。下面配置了JSP視圖创葡。如果需要其它視圖解析器可以參見其相應(yīng)文檔浙踢,以及ViewResolverRegistry的JavaDoc。

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        registry.enableContentNegotiation(new MappingJackson2JsonView());
        registry.jsp()
                .prefix("/WEB-INF/jsp/")
                .suffix(".jsp")
                .viewClass(JstlView.class);
    }

}

如果使用XML配置文件可以使用MVC命名空間簡(jiǎn)化配置灿渴。除了內(nèi)置的JSP解析器外洛波,其它視圖解析器可能還需要額外的配置胰舆,這里不再細(xì)述。

<mvc:view-resolvers>
    <mvc:jsp prefix="/WEB-INF/jsp/"
             suffix=".jsp"
             view-class="org.springframework.web.servlet.view.JstlView"/>
</mvc:view-resolvers>

資源處理

靜態(tài)資源處理

這里說的主要是靜態(tài)資源的處理蹬挤。前面說了很多關(guān)于控制器缚窿、視圖的知識(shí),但是如何映射CSS焰扳、JS文件倦零,前面沒有說明。配置方法在這里說明吨悍。

使用Java配置的話光绕,重寫addResourceHandlers(ResourceHandlerRegistry registry)方法,然后添加相應(yīng)的映射即可畜份。

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/**").addResourceLocations("/resources/static/");
    }

}

使用XML配置文件 也同樣簡(jiǎn)單。

<mvc:resources mapping="/static/**" location="/resources/static/"/>

這樣映射之后欣尼,假如我們有/resources/static/bootstrap.css文件爆雹,那么就可以使用/static/bootstrap.css路徑來訪問該文件了。同樣的在視圖文件中也可以如此引用愕鼓。還可以使用cache-period設(shè)置資源的過期時(shí)間钙态,單位是秒。如果需要指定多個(gè)資源位置菇晃,可以使用逗號(hào)分隔册倒。

資源的版本控制

有些頻繁更新的資源可能需要版本控制,強(qiáng)制讓客戶端使用最新的資源磺送。Spring框架也支持資源的版本控制驻子,我們需要定義資源鏈來實(shí)現(xiàn)這個(gè)功能。資源鏈由一個(gè)ResourceResolver實(shí)例和多個(gè)ResourceTransformer實(shí)例組成估灿。內(nèi)建的VersionResourceResolver能滿足我們的大部分需求崇呵,它可以定義一些策略來配置版本控制,例如FixedVersionStrategy會(huì)依據(jù)日期馅袁、版本號(hào)或者其他東西作為版本域慷;ContentVersionStrategy會(huì)計(jì)算資源的MD5值。

ContentVersionStrategy策略是一個(gè)不錯(cuò)的策略汗销,不過由于它會(huì)計(jì)算MD5犹褒,所以開銷比較大, 因此在使用這種策略的時(shí)候最好打開緩存來提高性能弛针。

如果使用Java配置的話叠骑,和前面的例子差不多,只不過需要多調(diào)用resourceChain(true)等方法并添加相應(yīng)的版本資源解析器和版本策略削茁。

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/**")
                .addResourceLocations("/resources/static/")
                .resourceChain(true).addResolver(
                    new VersionResourceResolver().addContentVersionStrategy("/**"));
    }

}

下面是使用XML配置的例子座云。

<mvc:resources mapping="/static/**" location="/resources/static/">
    <mvc:resource-chain>
        <mvc:resource-cache/>
        <mvc:resolvers>
            <mvc:version-resolver>
                <mvc:content-version-strategy patterns="/**"/>
            </mvc:version-resolver>
        </mvc:resolvers>
    </mvc:resource-chain>
</mvc:resources>

默認(rèn)Servlet

開啟這個(gè)選項(xiàng)可以讓DispatcherServlet處理根路徑/下的靜態(tài)資源請(qǐng)求疙赠,說的詳細(xì)點(diǎn)就是假如靜態(tài)文件是webapp/css/site.css,那么我們可以直接通過/css/site.css來訪問這個(gè)文件朦拖。如果不啟用這個(gè)功能圃阳,那么靜態(tài)文件就只能映射到其他路徑下比如/static

這個(gè)配置項(xiàng)實(shí)際上會(huì)配置一個(gè)DefaultServletHttpRequestHandler璧帝,映射到路徑/**捍岳,并具有最低的優(yōu)先級(jí)。由于DefaultServletHttpRequestHandler會(huì)將所有請(qǐng)求轉(zhuǎn)發(fā)到默認(rèn)Servlet睬隶,所以它必須被配置為最后一個(gè)處理映射才行锣夹。

使用Java配置的話,重寫configureDefaultServletHandling(...)方法并開啟該選項(xiàng)苏潜。

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

}

如果使用XML配置的話银萍,添加以下行。

<mvc:default-servlet-handler/>

消息轉(zhuǎn)換

如果我們需要覆蓋Spring默認(rèn)的消息轉(zhuǎn)換器恤左,可以重寫configureMessageConverters(List<HttpMessageConverter<?>> converters)方法贴唇,然后向converters參數(shù)添加我們自己的消息轉(zhuǎn)換器。如果僅僅希望增加自己的類型轉(zhuǎn)換器飞袋,重寫extendMessageConverters()方法戳气。

使用XML配置文件的話,可以使用MVC命名空間巧鸭。

<mvc:annotation-driven>
    <mvc:message-converters>
        <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
            <property name="objectMapper" ref="objectMapper"/>
        </bean>
        <bean class="org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter">
            <property name="objectMapper" ref="xmlMapper"/>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

高級(jí)自定義配置

上面的配置使用Spring提供的簡(jiǎn)化類或者M(jìn)VC命名空間瓶您,幫助我們快速配置功能。有時(shí)候可能需要更高級(jí)的功能定制纲仍,這樣就需要自己處理這些底層Bean的初始化和屬性設(shè)置呀袱。

Java配置自定義

我們先來看一看@EnableWebMvc注解的定義≈5可以看到它還使用了一個(gè)@Import注解压鉴,引用了DelegatingWebMvcConfiguration類。當(dāng)我們注解@EnableWebMvc的時(shí)候锻拘,實(shí)際上初始化和配置的底層類就是DelegatingWebMvcConfiguration油吭。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}

因此我們?nèi)绻远xMVC的話,第一件事就是移除注解@EnableWebMvc署拟。然后繼承DelegatingWebMvcConfiguration類并實(shí)現(xiàn)它的requestMappingHandlerAdapter()方法婉宰。

@Configuration
public class WebConfig extends DelegatingWebMvcConfiguration {

    @Override
    @Bean
    public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
        // 在這里進(jìn)行高級(jí)配置
    }

}

在項(xiàng)目中DelegatingWebMvcConfiguration子類和@EnableWebMvc注解配置類只能存在一個(gè),因?yàn)樗鼈冏龅氖虑閷?shí)際上是一樣的推穷。而且這里的配置并不影響Spring MVC的其他配置心包。

自定義MVC命名空間配置

這里的自定義配置更困難,因?yàn)镾pring沒有提供相應(yīng)的配置機(jī)制馒铃。如果實(shí)在需要自定義MVC命名空間配置蟹腾,可以考慮使用Spring提供的BeanPostProcessor機(jī)制痕惋,在檢測(cè)到Bean之后修改它的值。

@Component
public class MyPostProcessor implements BeanPostProcessor {

    public Object postProcessBeforeInitialization(Object bean, String name) throws BeansException {
        if (bean instanceof RequestMappingHandlerAdapter) {
            // 在這里自定義屬性
        }
    }

}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末娃殖,一起剝皮案震驚了整個(gè)濱河市值戳,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌炉爆,老刑警劉巖堕虹,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異芬首,居然都是意外死亡赴捞,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門郁稍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來赦政,“玉大人,你說我怎么就攤上這事耀怜』肿牛” “怎么了?”我有些...
    開封第一講書人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵封寞,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我仅财,道長(zhǎng)狈究,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任盏求,我火速辦了婚禮抖锥,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘碎罚。我一直安慰自己磅废,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開白布荆烈。 她就那樣靜靜地躺著拯勉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪憔购。 梳的紋絲不亂的頭發(fā)上宫峦,一...
    開封第一講書人閱讀 51,182評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音玫鸟,去河邊找鬼导绷。 笑死,一個(gè)胖子當(dāng)著我的面吹牛屎飘,可吹牛的內(nèi)容都是我干的妥曲。 我是一名探鬼主播贾费,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼檐盟!你這毒婦竟也來了褂萧?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤遵堵,失蹤者是張志新(化名)和其女友劉穎箱玷,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體陌宿,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡锡足,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了壳坪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片舶得。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖爽蝴,靈堂內(nèi)的尸體忽然破棺而出沐批,到底是詐尸還是另有隱情,我是刑警寧澤蝎亚,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布九孩,位于F島的核電站,受9級(jí)特大地震影響发框,放射性物質(zhì)發(fā)生泄漏躺彬。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一梅惯、第九天 我趴在偏房一處隱蔽的房頂上張望宪拥。 院中可真熱鬧,春花似錦铣减、人聲如沸她君。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽缔刹。三九已至,卻和暖如春劣针,著一層夾襖步出監(jiān)牢的瞬間桨螺,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工酿秸, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留灭翔,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像肝箱,于是被迫代替她去往敵國(guó)和親哄褒。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理煌张,服務(wù)發(fā)現(xiàn)呐赡,斷路器,智...
    卡卡羅2017閱讀 134,652評(píng)論 18 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,804評(píng)論 6 342
  • spring官方文檔:http://docs.spring.io/spring/docs/current/spri...
    牛馬風(fēng)情閱讀 1,670評(píng)論 0 3
  • 001 抓住人生的關(guān)鍵30秒 30秒非常短骏融,有可能是在等電梯链嘀、也可能是茶水間偶遇或者會(huì)議開始前過程中的間隙,這都是...
    做一個(gè)更好的普通人閱讀 433評(píng)論 3 5
  • 當(dāng)我們老了 可否像現(xiàn)在一樣偶爾小聚档玻, 當(dāng)我們老了 我們細(xì)數(shù)臉上的皺紋當(dāng)我們老了 看看曾經(jīng)的照片怀泊, 原來也曾經(jīng)年輕過...
    淋濕的愛情閱讀 326評(píng)論 2 2