[spring]applicationContext.xml-mvc标签

紧接着上文,我们来学习mvc标签。

Spring版本:4.3.14。

6.mvc 命名空间

  mvc命名空间内的标签是用于Spring MVC的配置,一般情况下Spring的配置文件和Spring-MVC的配置文件都是分开配置的,Spring MVC的配置文件默认情况下是 dispatcher-servlet.xml,所以mvc的配置通常都是在该配置文件中。下面我们来学习下每个标签的用处。

  首先,记得先引入命名空间,并且先搭建好Spring MVC环境,这些都很简单,这里就不多说了。

6.1 <mvc:annotation-driven>标签

mvc:annotation-driven标签是一种简写的形式,用于为Spring MVC分发请求到Controller上,不过该标签支持的功能很多,比如@InitBinder数据绑定,@NumberFormatannotation数据格式化,@DateTimeFormat日期格式化,数据校验,@RequestBody @ResponseBody的json支持等;下面来看一下该标签中各个属性的配置。

6.1.1 ignore-default-model-on-redirect元素

顾名思义,用于请求重定向时是否忽略model的参数,布尔值,true和false,默认是false,但文档中建议应该设置为true:

The default setting is false but new applications should consider setting it to true.

有的时候,我们重定向的时候需要传参数,我们通过Model添加参数,通过ModelAttribute来获取URL的参数,但有些时候在重定向的时候,我们不想把参数带过去,比如说我们重定向的地址是:http://localhost:8080/hello/test.html?users=user1&users=user2&users=user3&users=user4...,这时候我们可以通过配置该属性为true,来忽略URL后面这一堆参数。
具体使用可参考:https://stackoverflow.com/questions/13247239/spring-mvc-controller-redirect-without-parameters-being-added-to-my-url%3C/b%3E

6.1.2 enable-matrix-variables元素

一般情况下,我们URL中很少会出现name-value这种类似于键值对的形式,而Spring MVC却提供了对这种形式的支持,在Spring MVC中,它们被称为matrix-variables。而如果要在Spring中使用该功能,需要先配置enable-matrix-variables属性,该属性是布尔类型,有true和false,默认情况下是false,也就是不开启的状态。

matrix variables的形式如:/cars;color=red,green;year=2012,其中各个变量之间使用分号分隔,每个变量如果多个值,使用逗号分割;简单看下官网的几个例子:

// GET /pets/42;q=11;r=22
@GetMapping("/pets/{petId}")
public void findPet(@PathVariable String petId, @MatrixVariable int q) {

    // petId == 42
    // q == 11
}

这里简单说下,GetMapping是Spring4.3之后的一个组合注解。我们在URL中配置相应的name-value组合之后,可以通过注解@MatrixVariable来获取该组合的值。如果要查看更多有关matrix-variables相关,可参考官网文件:https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-matrix-variables

6.1.3 conversion-service元素

在我们通过浏览器向我们的服务器提交数据的时候,也就是在字段绑定期间,用于类型转换的ConversionService的bean的名称,默认情况下的实现是FormattingConversionService,如果我们需要自定义转换类型的时候可以通过配置该bean指向我们自定义的转换器来实现。

自定义转换器:

public class DateConverter implements Converter<String, Date> {
    @Override
    public Date convert(String dateString) {
        try {
            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(dateString);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return null;
    }
}

引入我们的转换器,当然我们开可以配置多个转换器:

<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <set>
            <bean class="expend.DateConverter"/>
        </set>
    </property>
</bean>

其实Spring已经给我们提供了许多种类型转换器,位于org.springframework.core.convert.support目录下,在以上转换器都不满足的情况下,我们可以自定义我们的类型转换器;并且使用的时候可以结合@InitBinder注解来一并学习使用。

6.1.4 message-codes-resolver元素

一般情况下,在浏览器传递参数到服务器的时候,我们都需要进行数据绑定,并且进行参数验证,如果绑定失败或者验证失败,对应的错误码会保存到Spring的错误对象中,而我们可以通过MessageCodesResolver来对错误code构建相应的message信息。默认情况下,Spring将使用DefaultMessageCodesResolver对象进行解析,当然如果有需要,我们也可以自定义我们的解析器。

6.1.5 validator元素

  上面我们也已经说过,对于从浏览器端传递过来的参数,前端的JS可以涵盖大部分的校验职责,但为了避免用户绕过浏览器,使用HTTP工具向后台直接发送一些非正常的数据,我们后台的校验也是必须的。
  而在这一点上,Java中的JSR303/JSR-349定义了相应的规范,而Bean Validation则是该规范的具体实现。Bean Validation是一个通过配置注解来验证参数的框架,它包含两部分Bean Validation API和Hibernate Validator:

  • Bean Validation API是Java定义的一个验证参数的规范。
  • Hibernate Validator是Bean Validation API的一个实现。

而我们如果在Spring中使用该框架的话,只需要简单配置即可:

<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">  
    <property name="providerClass" value="org.hibernate.validator.HibernateValidator"/>  
    <!-- 如果不加默认,则到使用classpath下的 ValidationMessages.properties -->  
    <property name="validationMessageSource" ref="messageSource"/>  
</bean>  
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">  
    <property name="basenames">  
        <list>  
            <!-- 在web环境中一定要定位到classpath 否则默认到当前web应用下找  -->  
            <value>classpath:messages</value>  
            <value>classpath:org/hibernate/validator/ValidationMessages</value>  
        </list>  
    </property>  
    <property name="useCodeAsDefaultMessage" value="false"/>  
    <property name="defaultEncoding" value="UTF-8"/>  
    <property name="cacheSeconds" value="60"/>  
</bean>  

其中消息文件 ValidationMessages.properties 定义了我们配置的一些信息:

user.id.null=用户编号不能为空  
user.name.null=用户名不能为空  
user.name.length.illegal=用户名长度必须在5到20之间  
user.name.illegal=用户名必须是字母  
user.password.null=密码不能为空  

以上参考自:http://jinnianshilongnian.iteye.com/blog/1990081

6.1.6 content-negotiation-manager元素

  该元素中文一般翻译为内容协商,意思是同一资源可以多种展示方式,比如说当输入后缀是html的时候,返回html页面,当输入json后缀的时候,返回j'son格式的数据,当然是在同一个方法里,其实就是根据请求规则决定返回什么样的内容类型。
  比如说类似于这种:@RequestMapping(value={"/user/{id}","/user/{id}.json"}),也就是说当输入user/1的时候返回对应的试图,当输入user/1.json的时候,返回json格式的数据,这种就是所谓的内容协商了,而Spring中content-negotiation-manager该元素就是用于实现这个功能的,当然不单单是json,还有其他方式,比如XML等等。
在RESTful该特性的实现方式大概有以下三种:

  • 1.使用http request header: Accept
GET /user/123 HTTP/1.1
Accept: application/xml                 //将返回xml格式数据

GET /user/123 HTTP/1.1
Accept: application/json               //将返回json格式数据
    1. 使用扩展名
/user/123.xml  将返回xml格式数据
/user/123.json 将返回json格式数据
/user/123.html 将返回html格式数据
    1. 使用参数
/user/123?format=xml          //将返回xml数据
/user/123?format=json          //将返回json数据

而Spring则是支持以上三种方式的,ContentNegotiatingViewResolver是根据客户提交的MimeType(如 text/html,application/xml)来跟服务端的一组viewResover的MimeType相比较,如果符合,即返回viewResover的数据。
而 /user/123.xml, ContentNegotiatingViewResolver会首先将 .xml 根据mediaTypes属性将其转换成 application/xml,然后完成前面所说的比较。

<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
    <!-- 设置为true以忽略对Accept Header的支持-->
    <property name="ignoreAcceptHeader" value="true"/>
    <!-- 在没有扩展名时即: "/user/1" 时的默认展现形式 -->
    <property name="defaultContentType" value="text/html"/>
    <property name="mediaTypes">
        <value>
            json=application/json
            xml=application/xml
        </value>
    </property>
</bean>
<!-- 配置ContentNegotiatingViewResolver -->
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
    <property name="contentNegotiationManager" ref="contentNegotiationManager"/>
    <property name="defaultViews">
        <list>
            <bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"/>
        </list>
    </property>
</bean>

如果想更多的了解内容协商相关的问题,可参考:
Spring MVC Content Negotiation
https://spring.io/blog/2013/05/11/content-negotiation-using-spring-mvc/
官网文档-mvc-config-content-negotiation

6.1.7 mvc:argument-resolvers子标签

该标签用于自定义参数解析器,比如说我们通过@RequestBody来解析json格式的参数,通过@RequestParam来解析表单相关的参数,如果Spring自带的参数解析器满足不了我们的需要,我们可以自定义解析器,实现HandlerMethodArgumentResolver接口,然后配合注解来完成自定义解析参数的功能。

<mvc:annotation-driven>
    <mvc:argument-resolvers>
        <bean class="..."/>
    </mvc:argument-resolvers>
</mvc:annotation-driven>
6.1.8 mvc:return-value-handlers子标签

该标签表示允许我们对特定的返回类型做处理,看一个例子就明白了:、

@RequestMapping("/testReturnHandlers")
public User testHandlerReturnMethod(){
    User u  = new User();
    u.setUserName("test");
    return u;
}

  由于上面testReturnHandlers接口返回的是一个pojo类型,正常情况下Spring MVC无法解析,最后会由DefaultRequestToViewNameTranslator解析处一个缺省的view name,转到 testReturnHandlers.jsp,如果没有的话就会报错。这时候我们就可以通过该标签引入我们自定义的 HandlerMethodReturnValueHandler的实现来完成数据的处理。

这时候我们先自定义我们的返回Handler处理器:

public class UserHandlers implements HandlerMethodReturnValueHandler {
    Logger logger = LoggerFactory.getLogger(this.getClass());
    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
        Class<?> type = returnType.getParameterType();
        if(User.class.equals(type))
        {
            return true;
        }
        return false;
    }

    @Override
    public void handleReturnValue(Object returnValue,
            MethodParameter returnType, ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest) throws Exception {
        logger.info("handler  for return type users ");
        mavContainer.setViewName("helloworld");
    }
}

然后进行相关配置:

<mvc:annotation-driven>
    <mvc:return-value-handlers>
        <bean class="UserHandlers"/>
    </mvc:return-value-handlers>
</mvc:annotation-driven>

最终我们再访问testReturnHandlers的时候,Spring就会通过我们自定义的UserHandlers来处理返回类型是User的返回值;

6.1.9 mvc:path-matching子标签

该标签用于控制Spring MVC请求URL的路径匹配,比如说,我们的某个方法配置如下:

@RequestMapping("/world")
public String hello() {
    return "hello";
}

默认情况下,Spring MVC会自动匹配所有.*的请求,就上面这个例子而言,我们请求/world/wold.html/world.do都会匹配成功,然后执行相应的操作。如果我们不想按照Spring的这种默认的匹配规则来的话,就可以通过配置该标签来完成我们所需的操作。下面来简单看下该标签下的几个参数:

  1. suffix-pattern元素:布尔类型,true和false,该元素表示URL匹配的时候是否使用默认的后缀模式匹配全部(.*),默认是true,如果我们配置为false的话,那么就上面例子来说,我们再使用/world.*类似的就匹配不到了。
  2. trailing-slash元素:布尔类型,true和false,该属性表示匹配的时候是否不考虑斜杠/,默认是true,也就是不考虑斜杠的存在,那么映射到/users的方法也就可以匹配/users/
  3. registered-suffixes-only元素:布尔类型,true和false,具体作用不明,官网解释:
      Whether to use suffix pattern match for registered file extensions only when matching patterns to requests.If enabled, a controller method mapped to "/users" also matches to "/users.json" assuming ".json" is a file extension registered with the provided ContentNegotiationManager. This can be useful for allowing only specific URL extensions to be used as well as in cases where a "." in the URL path can lead to ambiguous interpretation of path variable content, (e.g. given "/users/{user}" and incoming URLs such as "/users/john.j.joe" and "/users/john.j.joe.json").
      If enabled, this attribute also enables suffix-pattern. The default value is false.
  1. path-matcher元素:我们自定义的地址匹配PathMatcher的实现,如果我们不配置,默认的实现是AntPathMatcher
  2. path-helper元素:我们自定义的用于解析路径匹配的UrlPathHelper的实现,默认的是UrlPathHelper。
<mvc:annotation-driven>
    <mvc:path-matching
        suffix-pattern="true"
        trailing-slash="false"
        registered-suffixes-only="true"
        path-helper="pathHelper"
        path-matcher="pathMatcher"/>
</mvc:annotation-driven>

<bean id="pathHelper" class="org.example.app.MyPathHelper"/>
<bean id="pathMatcher" class="org.example.app.MyPathMatcher"/>
6.1.10 mvc:message-converters子标签

  该标签表示配置Spring的消息转换器。我们都使用过@RequestBody和@ResponseBody这两个注解,当使用RequestBody注解,对请求的数据进行对象映射时,Spring会根据Request对象header部分的content-Type类型,选择合适的HttpMessageConverter来读取数据;而ResponseBody,则是在进行响应时,同样选择合适的HttpMessageConverter来进行转换,返回对应格式的数据。

  假如我们通过RequestBody注解定义一个对象,来接收前台传入的json参数时,有的时候可能会由于属性的不匹配出现400错误,这时候,我们就可以自定义HttpMessageConverter来完成我们的参数匹配。具体的实例可参考:SpringMVC自定义配置消息转换器踩坑总结

该标签有一个参数:register-defaults,表示除了我们自定义的之外,是否还使用默认的HttpMessageConverter来处理。布尔类型,默认是true,大多数情况下这个值都是true类型。

<mvc:message-converters register-defaults="true">
    <!-- 字符串返回时的编码问题 -->
    <bean class="org.springframework.http.converter.StringHttpMessageConverter">
        <constructor-arg value="UTF-8"/>
    </bean>
    <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
        <property name="supportedMediaTypes">
            <list>
                <value>application/json;charset=UTF-8</value>
                <value>text/html;charset=UTF-8</value>
            </list>
        </property>
        <property name="objectMapper"><ref bean="objectMapper"/> </property>
    </bean>
</mvc:message-converters>
6.1.11 mvc:async-support子标签

  Spring MVC异步请求的配置选项。一般情况下,在同步请求中,浏览器发起请求,服务器开启一个线程响应,响应完成返回一个值给浏览器,而异步的话返回的一般是Callable对象或者DeferredResult对象,然后后端会通过一个回调线程来处理调用的结果,处理完成后再返回。这里请求线程与后台处理是两个线程,并且Callable对象和DeferredResult对象略有些不同,感兴趣的可以自行搜索一下。

  • default-timeout属性,默认超时时间,单位毫秒,如果不配置,则使用底层实现的默认超时时间,比如Servlet3中Tomcat上的10秒;
  • task-executor属性,线程池配置,默认是使用SimpleAsyncTaskExecutor线程池来执行Callable的代码,不过该线程池不重用任何线程,或者说它每次调用都启动一个新线程,性能不是太高,我们可以使用自定义线程池来实现。
    实现可参考:Spring MVC 异步处理请求,提高程序性能

下面看下该标签下的两个子标签:

  1. mvc:callable-interceptors,异步操作的拦截器处理,我们可以自定义拦截器来处理异步操作过程中的一些问题,拦截的响应对象是Callable,比如说超时了咋滴整等;
    The ordered set of interceptors that intercept the lifecycle of concurrently executed requests, which start after a controller returns a java.util.concurrent.Callable.
  2. mvc:deferred-result-interceptors,和上面这个标签类似,不过该标签拦截的是DeferredResult对象;
    The ordered set of interceptors that intercept the lifecycle of concurrently executed requests, which start after a controller returns a DeferredResult.
6.2 mvc:interceptors标签

Spring MVC中的拦截器的配置,拦截日志,登陆,权限等操作,我们直接来看一下该标签下各元素的使用。

  • path-matcher元素:表示我们可以自定义拦截器路径的匹配规则,默认采用的匹配规则是AntPathMatcher,具有ant风格的路径规则,如?表示任何单字符,*表示0个或多个字符,**表示0个或多个目录。
6.2.1 mvc:interceptor子标签

mvc:interceptors 标签下有三种子标签,也就是有两种类型的配置,其中bean和ref表示拦截所有的请求,而mvc:interceptor则是一种更细粒度的配置,可以自定义拦截的路径和要排除的路径。当有多个拦截器的时候,执行的顺序将按照它们配置的顺序来执行。

  • mvc:mapping,表示要拦截的路径,其中属性path为具体要拦截的路径,ref或bean表示具体实现的拦截器;
  • mvc:exclude-mapping,表示要排除掉不需要拦截的路径;
6.3 mvc:default-servlet-handler标签

  Spring MVC用于处理静态资源的一种方式。为了实现优雅的Rest风格的的方式,我们配置DispatcherServlet的请求路径是一般都是配置为/,这种情况下Spring MVC将会捕获Web容器所有的请求,包括静态资源的请求,然后分发到具体的controller去处理,如果找不到则会出现异常。这种情况下,对于静态资源请求的处理,则显得尤为重要了,而Spring MVC自从3.0之后就提供了两种比较优雅的方式:

  1. 一种是配置mvc:default-servlet-handler,配置之后,Spring MVC上下文中会定义一个DefaultServletHttpRequestHandler,请求进入DispatcherServlet之后,会被分发到各个HandlerMapping进行处理,如果该请求最后处理不了,会交给默认的DefaultServletHttpRequestHandler来进行处理;
  2. 一般Web应用服务器默认的Servlet名称是"default",假如我们的Web应用服务器的默认Servlet名称不是"default",则需要通过default-servlet-name属性显示指定;
<mvc:default-servlet-handler default-servlet-name="_ah_default"/>

而DefaultServletHttpRequestHandler中各个服务器对应的servlet名称如下:

/** Default Servlet name used by Tomcat, Jetty, JBoss, and GlassFish */
private static final String COMMON_DEFAULT_SERVLET_NAME = "default";

/** Default Servlet name used by Google App Engine */
private static final String GAE_DEFAULT_SERVLET_NAME = "_ah_default";

/** Default Servlet name used by Resin */
private static final String RESIN_DEFAULT_SERVLET_NAME = "resin-file";

/** Default Servlet name used by WebLogic */
private static final String WEBLOGIC_DEFAULT_SERVLET_NAME = "FileServlet";

/** Default Servlet name used by WebSphere */
private static final String WEBSPHERE_DEFAULT_SERVLET_NAME = "SimpleFileServlet";
6.4 mvc:resources标签

而另一种处理静态资源的方式就是mvc:resources标签了。mvc:default-servlet-handler标签将静态资源的处理最终是由Spring MVC交给了Web服务器进行处理,而mvc:resources则是由Spring MVC自己处理,并且还可以配置一些简单的优化方式。

<mvc:resources mapping="/javascript/" location="/WEB-INF/js/" order="1" cache-period="31536000"/>
  1. location元素:首先,<mvc:resources/> 允许静态资源放在任何地方,如WEB-INF目录下、类路径下等,你甚至可以将JavaScript等静态文件打到JAR包中,通过location属性指定静态资源的位置,当然支持通配符的方式;
  2. cache-period元素:我们可以通过配置该属性指定静态资源在浏览器端的缓存时间,单位是秒,默认是没有缓存。在接收到静态资源的获取请求时,会检查请求头的Last-Modified值,如果静态资源没有发生变化,直接从缓存中取值,提高程序性能;
  3. mapping元素:映射地址,比如当我们要引用/WEB-INF/js/文件夹下的文件时,可以直接引用/javascript/**.js,这样就会去我们的WEB-INF/js/里面去找。也就是说,配置了mapping之后,Spring会做一层映射,然后去映射所对应的location下查找静态资源,因为location的位置不是固定的;
  4. order元素:如果配置了多个mvc:resources,那么可以通过order元素指定过滤查找时的优先级,order值越小优先级越高,默认的order值是:Integer.MAX_VALUE - 1

以上静态资源的问题都是基于我们web.xml中配置的url-pattern是拦截全部/的情况。而如果我们的配置是*.do等相关格式的话,就不会有以上相关的问题了。

6.5 mvc:view-controller标签

  对于WEB-INF目录下的页面,我们知道,由于安全性的问题我们无法直接通过URL访问到,一般都是通过在控制器中使用转发的方式来进行访问。而有一些时候我们如果单纯的只是想访问该目录下的页面,并没有一些逻辑操作的时候,可以通过配置该标签将要访问的URL与相应的视图进行映射,不用再通过控制器就可以解析为对应的视图。

 <mvc:view-controller path="/test" view-name="hello/hello"/>
  1. path参数,是要访问的URL,比如localhost:8080/test
  2. view-name参数,要映射的文件的路径,比如某一个jsp的文件路径是WEB-INF/hello/hello.jsp,那对应的view-name则就是hello/hello,不过不要忘记配置视图解析器。另外,view-name的另外一种方式是重定向的方式:<... view-name="redirect:/admin/index">,有兴趣的可以尝试一下。

总结

到这里,Spring MVC命名空间下的主要标签都学习完了。MVC下的标签针对的是Http请求过程中的各种配置,了解了这些配置,可以让我们在写Web相关的接口的时候更加游刃有余。

本文主要参考资料:官网资料及StackOverFlow网站

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,594评论 18 139
  • 紧接着上文,我们来学习其他标签。 Spring 版本:4.3.14。 5. context命名空间 看完了bean...
    骑着乌龟去看海阅读 3,479评论 2 3
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,429评论 25 707
  • 作为一个在工厂里做事的人,我实在看不惯大多数人。实在是幸运还是有那么一撮人让我很是佩服。 我换到这个岗位一年又三个...
    简如一阅读 628评论 0 0
  • 一杯咖啡,速溶的卖2块,星巴克的卖40,同样的口味价格却千差万别,这是为什么呢? 除了他们的材质和研磨方式有所不同...
    Elaine的生活之道阅读 689评论 6 12