1. 数据绑定流程

  1. SpringMVC 主框架将 ServletRequest 对象及目标方法的入参实例传递给 WebDataBinderFactory

    实例,以创建 DataBinder 实例对象;
  2. DataBinder 调用装配在 SpringMVC 上下文中的 ConversionService 组件进行数据类型转换,

    数据格式化工作; 将 Servlet 中的请求信息填充到入参对象中;
  3. 调用 Validator 组件对已经绑定了请求消息的入参对象进行数据合法性校验,并最终生成数据绑定结果

    BindingData 对象;
  4. Spring MVC 抽取 BindingResult 中的入参对象和校验错误对象,将它们赋给处理方法的响应入参;
  5. 总结: Spring MVC 通过反射机制对目标处理方法进行解析,将请求消息绑定到处理方法的入参中.

    数据绑定的核心部件是 DataBinder.

2. Spring 支持的转换器

  • Spring 定义了三种类型的转换器接口,实现任意一个转换接口都可以作为自定义转换器注册到

    ConversionServiceFacotoryBean 中:

    • Converter<S,T>: 将 S 类型的对象转换为 T 类型对象;
    • ConverterFactory: 将相同系列多个"同质"Converter封装在一起; 将一种类型的对象转换为

      另一种类型及其子类的对象.例如,将String转换为 Number 及 Number 子类Integer,Long,

      Double等对象;
    • GenericConverter: 会根据源类对象及目标类对象所在的宿主类中的上下文信息进行类型转换;

2.1 自定义类型转换器

  • ConversionService 是 Spring 类型转换体系的核心接口;
  • 可以利用 ConversionServiceFatoryBean 在 Spring 的 IOC 容器中定义一个 ConversionService;

    Spring 将自动识别出 IOC 容器中的 ConversionService, 并在 Bean 属性配置及 SpringMVC

    处理方法入参绑定等场合使用它进行数据的转换;
  • 可通过 ConversionServiceFactoryBean 的 converters 属性注册自定义的类型转换器;
  1. // 从 java.lang.String 转换为 POJO 类
  2. // index.jsp
  3. <form action="${pageContext.request.contextPath}/empConvert" method="post">
  4. <!-- 姓名;邮箱;性别;部门 -->
  5. <input type="text" name="employee" value="lisi;lisi@163.com;1;105"/>
  6. <input type="submit" value="类型转换"/>
  7. </form>
  8. // EmployeeHanlder.java
  9. @RequestMapping(value="/empConvert",method=RequestMethod.POST)
  10. public String empConvert(@RequestParam("employee") Employee employee){
  11. employeeService.save(employee);
  12. return "redirect:/emps";
  13. }
  14. // EmployeeConverter.java
  15. // 类型转换,将String类型转换为 Employee, 即自定义类型转换器
  16. @Component
  17. public class EmployeeConverter implements Converter<String,Employee>{
  18. public Employee convert(String source){
  19. Employee result = null;
  20. if(null != resource){
  21. // 将字符串分割
  22. String[] empInfos = source.split(";");
  23. if(null != empInfos && empInfos.length == 4){
  24. result = new Employee();
  25. result.setLastName(empInfos[0]);
  26. result.setEmail(empInfos[1]);
  27. // 将String 类型转换为 Integer 类型
  28. result.setGender(Integer.parseInt(empInfos[2]));
  29. Department department = new Department();
  30. department.setId(Integer.parseInt(empInfos[3]));
  31. result.setDepartment(department);
  32. }
  33. }
  34. return result;
  35. }
  36. }
  37. // SpringDispatcherServlet-servlet.xml
  38. // 在SpringMVC配置文件中,注册自定义类型转换器
  39. <!-- 引用转换器 -->
  40. <mvc:annotation-driven conversion-service="conversionServiceFactoryBean">
  41. </mvc:annotation-driven>
  42. <!-- 注册 -->
  43. <bean id="conversionServiceFactoryBean"
  44. class="org.springframework.context.support.ConversionServiceFactoryBean">
  45. <property name="converters">
  46. <!-- 第一种写法: 首字母小写Bean -->
  47. <list>
  48. <ref bean="employeeConverter"/>
  49. </list>
  50. <!-- 第二种写法: 全类名的写法
  51. <list>
  52. <bean class="cn.itcast.springmvc.converts.EmployeeConvert"></bean>
  53. </list>
  54. -->
  55. </perperty>
  56. </bean>

2.2 mvc:annotation-driven

  1. <mvc:annotation-driven/>会自动注册 RequestMappingHandlerMapping,

    RequestMappingHandlerAdapterExceptionHandlerExceptionResolver 三个Bean;
  2. 还提供以下支持:
    • 支持使用 ConversionService 实例对表单参数进行类型转换;
    • 支持使用 @NumberFormatannotation, @DataTimeFormat 注解完成数据类型的格式化;
    • 支持使用 @Valie 注解对 JavaBean 实例进行 JSR 303 验证;
    • 支持使用 @RequestBody@ResponseBody 注解;

2.3 @InitBinder

  • @InitBinder标识的方法,可以对 WebDataBinder 对象进行初始化,WebDataBinder 是 DataBinder

    的子类,用于完成由表单字段到 JavaBean 属性的绑定;
  • @InitBinder方法不能有返回值,它必须声明为 void;
  • @InitBinder 方法的参数通常是 WebDataBinder
  1. @InitBinder
  2. public void initBinder(WebDataBinder webDataBinder){
  3. // 不自动绑定对象中的 email 属性
  4. webDataBinder.setDisallowedFields("email");
  5. }

3. 数据格式化

  1. 对属性对象的输入/输出进行格式化,从其本质上讲依然属于"类型转换"的范畴;
  2. Spring 在格式化模块中定义了一个实现了ConversionService接口的FormattingConversionService

    实现类,该实现类扩展了GenericConversionService,因此它既具有类型转换的功能,也具有格式化

    的功能;
  3. FormattingConversionService拥有一个FormattingConversionServiceFactoryBean

    厂类,后者用于在 Spring上下文中构造前者;
  4. FormattingConversionServiceFactoryBeanConversionServiceFactoryBean比较
    • ConversionService只有数据转换功能,与 ConversionServiceFactoryBean对应;
    • FormattingConversionService具有数据转换和数据格式化功能,
    • FormattingConversionServiceFactoryBean既可以注册自定义的转换器,也可以注册自定义

      的注解驱动器逻辑;
  5. <mvc:annotation-driven>标签内部默认创建的一个 conversionService 实例就是一个

    FormattingConversionServiceFactoryBean;
  1. // 日期类: @DateTimeFormat
  2. // 数字类: @NumberFormat
  3. public class Employee{
  4. private Integer id;
  5. private String lastName;
  6. private String email;
  7. // 更改输入的日期格式, 默认格式为 yyyy/MM/dd
  8. @DateTimeFormat(pattern="yyyy-MM-dd")
  9. private Date birth;
  10. @NumberFormat(patter="#,###.##")
  11. private Double salary;
  12. getset 方法
  13. }
  14. // SpringDispatcherServlet-servlet.xml
  15. // 配置 FormattingConversionServiceFacotryBean
  16. <mvc:annotation-driven conversion-service="conversionService"/>
  17. <bean id="conversionService"
  18. class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
  19. <!-- 自定义转换器 -->
  20. <property name="converters">
  21. <list>
  22. <ref bean='employeeConverter'/>
  23. </list>
  24. </property>
  25. </bean>

3.1 BindingResult

  • BindingResult 是接口,继承Errors;
  1. // 保存客户
  2. @RequestMapping(value="/emp",method=RequestMethod.POST)
  3. public String save(Employee employee,BindingResult bindingResult){
  4. if(bindingResult.getErrorCount()>0){
  5. List<FieldError> list = bindingResult.getFieldErrors();
  6. for(FieldError fieldError : list){
  7. System.out.println(fieldError.getField()+"\t"+fieldError.getCode());
  8. }
  9. throw new RuntimeException("录入信息出错!");
  10. }
  11. employeeService.save(employee);
  12. return "redirect:/emps";
  13. }

3.2 JSR 303 数据验证

  1. JSR303 是Java为Bean数据合法性校验提供的标准框架,它已经包含在 JavaEE6.0 中;
  2. JSR303 通过在 Bean 属性上标注类似于 @NotNull,@Max等标准的注解指定校验规则,并通过

    标准的验证接口对 Bean 进行验证;
  3. Hibernate Validator 是 JSR303 的一个具体实现,除支持所有标准的校验注解外,它还支持以下的

    扩展注解

    • @Email: 被注释的元素必须是邮箱地址;
    • @Length: 被注释的字符串大小必须在指定的范围内;
    • @NotEmpty: 被注释的字符串必须非空;
    • @Range: 被注释的元素必须在合适的范围内;
  4. 在 SpringMVC 中,可直接通过注解驱动的方式进行数据校验;
  5. Spring 的 LocalValidatorFactoryBean, 既实现了 Spring 的 Validator 接口,也实现了

    JSR303 的 Validator 接口.只要在 Spring 容器中定义了一个 LocalValidatorFactoryBean,

    即可将其注入到需要数据校验的Bean中;
  6. Spring 本身并没有提供JSR303的实现,所以必须将JSR的实现者的jar包放到类路径下;
  1. // 步骤:
  2. /*
  3. * 1. 导入 HibetnateValidator 验证框架的 jar 包(5个):
  4. * classmate-0.8.0.jar, hibernate-validator-5.0.0.CR2.jar,
  5. * hibernate-validator-annotation-processor-5.0.0.CR2.jar,
  6. * jboss-logging-3.1.1.GA.jar, validation-api-1.1.0.CR1.jar
  7. *
  8. * 2. 添加注解 <mvc:annotation-driven></mvc:annotation-driven>, 会默认装配好一个
  9. * LocalValicatorFactoryBean
  10. * 3. Employee.java 中添加 Field 验证要求的相关注解;比如名字不为空, Email格式要合法等;
  11. * 4. Save 方法对应的 POJO 参数前面添加 @Valid;
  12. * 5. 发生异常后,重新回到录入页面;
  13. */
  14. // Employee.java
  15. pubic class Employee{
  16. private Integer id;
  17. @NotEmpty
  18. private String lastName; // 名字不能为空
  19. @Email
  20. private String email; // 校验Email格式
  21. .....(略)
  22. }
  23. // EmployeeHandler.java
  24. public String save(@Valid Employee employee,BindingResult bindingResult,
  25. Map<String,Object> map){
  26. if(bindingResult.getErrorCount()>0){
  27. List<FieldError> list = bindingResult.getFieldErrors();
  28. for(FieldError fieldError : list){
  29. System.out.println(fieldError.getField()+"\t"
  30. +fieldError.getDefaultMessage());
  31. }
  32. // 返回到登录页面
  33. // 1. 查询出全部部门
  34. map.put("departments",departmentService.getDepartments());
  35. // 2. 查询出性别
  36. map.put("genders",getGenderUtils());
  37. return "input";
  38. }
  39. employeeService.save(employee);
  40. return "redirect:/emps";
  41. }
  42. // 页面显示错误信息
  43. // index.jsp
  44. <form:form action="${pageContext.request.contextPath}/emp" method="post"
  45. modelAttribute="employee">
  46. lastName:<form:input path="lastName"/>
  47. &nbsp;&nbsp;<form:errors path="lastName"></form:errors><br/>
  48. email:<form:input path="email"/>
  49. &nbsp;&nbsp;<form:errors path="email"></form:errors><br/>
  50. <input type="submit" value="添加"/>
  51. </form:form>
  52. // 自定义错误信息
  53. // SpringDispatcherServlet-servlet.xml 配置 国际化信息
  54. <bean id="messageSource"
  55. class="org.springframework.context.support.ResourceBundleMessageSource">
  56. <property name="basename" value="i18n"></property>
  57. </bean>
  58. // 在src目录下,新建 i18n.properties
  59. // 命名规则: 注解名+类名+需要限制的field
  60. NotEmpty.employee.lastName=用户名不能为空
  61. Email.employee.email=邮件地址格式不正确

3.3 SpringMVC 数据校验

  • 需校验的 Bean 对象和其绑定结果对象或错误对象是成对出现的,它们之间不允许声明其他的参数;
  • Errors 接口提供了获取错误信息的方法, 如getErrorCount()getFieldErrors(String field)
  • BindingResult 扩展了 Errors 接口;

4. 处理JSON

  1. 导入 jar 包:

    • jackson-annotations-2.7.0.jar
    • jackson-core-2.7.0.jar
    • jackson-databind-2.7.0.jar
  2. 编写目标方法,使其返回 JSON 对应的对象(POJO)或集合(Map);
  3. 在方法上添加@ResponseBody注解;
  1. // 需求: 查询所有员工
  2. // Demo.java
  3. public class Demo{
  4. @Autowired
  5. private EmployeeService employeeService;
  6. // 导入 jackson 的jar包,并且标注 @ResponseBody
  7. // HttpConverter 会自动识别出以 JSON 格式返回给前台
  8. @ResponseBody
  9. @RequestMapping(value="/testJson",method=RequestMethod.GET)
  10. public Collection<Employee> qryAllEmployee(){
  11. // 直接返回 Collection 集合
  12. return employeeService.getAll();
  13. }
  14. }
  15. // index.jsp
  16. <head>
  17. <script type="text/javascript">
  18. $(function(){
  19. $("#testJson").click(function(){
  20. var url=$(this).attr("href");
  21. var args = {name:"zhangsan"};
  22. $.post(url,args,function(data){
  23. for(var i=0; i<data.length; i++){
  24. var id = data[i].id;
  25. var lastName = data[i].lastName;
  26. alert(id+" "+lastName);
  27. }
  28. });
  29. });
  30. })
  31. </script>
  32. </head>
  33. <body>
  34. JSON 示例: <a id="testJson" href="${pageContext.request.contextPath}/testJson">
  35. 点击这里
  36. </a>
  37. </body>

4.1 HttpMessageConverter

  1. HttpMessageConverter<T>接口负责将请求信息转换为一个对象(类型为 T),或者将对象(类型为 T)

    输出为响应信息;
  2. HttpMessageConverter<T>接口定义的方法:
    • Boolean canRead(Class<?>clazz, MediaType mediaType): 表示转换器是否可以将请求信息

      转换为 clazz 类型的对象,同时,指定支持 MIME 类型(text/html,application/json等);
    • Boolean canWrite(Class<?>clazz, MediaType mediaType): 表示转换器是否可以将 clazz

      类型的对象写入到响应流中;
    • T read(Class<? extends T>clazz,HttpInputMessage msg): 将请求信息流转换为 T 类型的

      对象;
    • void write(T t, MediaType contentType, HttpOutputMessage msg): 将 T 类型的对象

      写到响应流中,同时,指定响应的媒体类型为 contentType;
  3. 常见HttpMessageConverter<T>的实现类
    • StringHttpMessageConverter: 将请求信息转换为字符串;
    • XmlAwareFormHttpMessageConverter
    • ByteArrayHttpMessageConverter: 读写二进制数据;
    • SourceHttpMessageConverter: 读写 javax.xml.transform.Source 类型的数据;
  4. Spring 中使用 HttpMessageConverter<T>:
    • 使用 @RequestBody/@ResponseBody 对处理方法进行标注;
    • 使用 HttpEntity<T>/ResponseEntity<T>作为处理方法的入参或返回值;
  5. 当控制处理方法使用到@RequestBody/@ResponseBodyHttpEntity<T>/ResponseEntity<T>

    时, Spring 首先根据请求头或响应头的 Accept 属性选择匹配的 HttpMessageConverter, 进而

    根据参数类型或泛型类型的过滤得到匹配的 HttpMessageConverter, 若找不到可用的

    HttpMessageConverter, 将报错;
  6. @RequestBody@ResponseBody 不需要成对出现;

  1. // 示例一: @RequestBody
  2. // index.jsp
  3. <h2>注册</h2>
  4. <form action="${pageContext.request.contextPath}/testRequestBody" method="post"
  5. enctype="multipart/form-data">
  6. 用户名:<input type="text" name="username"/><br/>
  7. 密码: <input type="password" name="password"/><br/>
  8. 上传文件: <input type="file" name="upload"/><br/>
  9. <input type="submit" value="上传"/><br/>
  10. </form>
  11. // Demo.java
  12. @RequestMapping(value="/testRequestBody",method=RequestMethod.POST)
  13. public String testRequestBody(@RequestBody String content){
  14. System.out.println("请求内容为:"+content);
  15. return "ok";
  16. }

  1. // 示例二: 下载 @ResponseEntity<byte[]>
  2. // index.jsp
  3. 下载示例: <a href="${pageContext.request.contextPath}/testDownload">下载</a>
  4. // Demo.java
  5. @RequestMapping(value="/testDownload", method=RequestMethod.GET)
  6. public ReponseEntity<byte[]> testDownload() throws IOException{
  7. byte[] body = null;
  8. FileInputStream input = new FileInputStream(new File("/Users/用户名/Documents/a.js"));
  9. body = new byte[input.available()];
  10. input.read(body);
  11. input.close();
  12. HttpHeaders headers = new HttpHeaders();
  13. headers.add("Content-Disposition","attachment;filename=a.js");
  14. HttpStatus statusCode = HttpStatus.OK;
  15. ResponseEntity<byte[]> result = new ResponseEntity<byte[]>(body,headers,statusCode);
  16. return result;
  17. }

5. 国际化

  • AcceptHeaderLocaleResolver: 根据 HTTP 请求头的Accept-Language参数确定本地化类型,

    如果没有显示定义本地化解析器,SpringMVC默认使用该解析器;
  • CookieLocaleResolver: 根据指定的 Cookie 值确定本地化类型;
  • SessionLocaleResolver: 根据 Session 中特定的属性确定本地化类型;
  • LocaleChangeInterceptor: 从请求参数中获取本次请求对应的本地化类型;
  1. // 第一种方式
  2. // src 目录下,新建 i18n.properties, i18n_en_US.properties, i18n_zh_CN.properties
  3. // i18n.properties 和 i18n_en_US.properties
  4. i18n.username=username
  5. i18n.password=password
  6. // i18n_zh_CN.properties
  7. i18n.username=用户名
  8. i18n.password=密码
  9. // index.jsp
  10. 国际化:<a href="${pageContext.request.contextPath}/i18n"></a>
  11. // ok.jsp
  12. 用户名: <fmt:message key="i18n.username"></fmt:message><br/>
  13. 码: <fmt:message key="i18n.password"></fmt:message><br/>
  14. // SpringDispatcherServlet-servlet.xml 配置
  15. <!-- 国际化配置 -->
  16. <bean id="messageSource"
  17. class="org.springframework.context.support.ResourceBundleMessageSource">
  18. <property name="basename" value="i18n"></property>
  19. </bean>
  20. // Demo.java
  21. @RequestMapping(value="/i18n",method=RequestMethod.GET)
  22. public String test(){
  23. return "ok";
  24. }
  25. // 第二种方式: 注入ResourceBundleMessageSource
  26. // Demo.java
  27. public class Demo{
  28. @Autowired
  29. private ResourceBundleMessageSource messageSource;
  30. @RequestMapping(value="/i18n2",method=RequestMethod.GET)
  31. public String test2(Locale locale){
  32. String v1 = messageSource.getMessage("i18n.username",null,locale);
  33. String v2 = messageSource.getMessage("i18n.password",null,locale);
  34. System.out.println(v1+"...."+v2);
  35. return "ok";
  36. }
  37. }
  38. // 第三种方式, 配置 SessionLocalResolver + LocaleChangeInterceptor
  39. // springDispatcherServlet-servlet.xml
  40. <!-- 配置 SessionLocaleResolver -->
  41. <bean id="localeResolver"
  42. class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
  43. </bean>
  44. <mvc:interceptors>
  45. <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"/>
  46. </mvc:interceptors>
  47. // index.jsp
  48. <h2>示例</h2>
  49. <a href="${pageContext.request.contextPath}/test3?locale=zh_CN">中文</a><br/>
  50. <a href="${pageContext.request.contextPath}/test3?locale=en_US">英文</a><br/>
  51. // Demo.java
  52. public class Demo{
  53. @RequestMapping(value="/test3",method=RequestMethod.GET)
  54. public String test3(){
  55. return "ok";
  56. }
  57. }

5.1 SessionLocaleResolver & LocaleChangeInterceptor 的工作原理

参考资料

SpringMVC 之数据转换和国际化的更多相关文章

  1. springMVC源码分析--国际化实现Session和Cookie(二)

    上一篇博客springMVC源码分析--国际化LocaleResolver(一)中我们介绍了springMVC提供的国际化的解决方案,接下来我们根据springMVC提供的解决方案来简单的实现一个多语 ...

  2. springMVC之本地化和国际化

    spring框架的大部分都支持国际化,就像springMVC一样.DispatcherServlet使你能够动态的通过客户端的本地语言进行配置.这是通过LocaleResolver完成的.   当一个 ...

  3. SpringMVC的数据转换,格式化和数据校验

          在SpringMVC中,根据请求方法签名不同,将请求消息中的消息以一定的方式转换并绑定到请求方法的参数中,在请求信息到达真正调用处理方法的这一段时间内,SpringMVC还会完成很多其他的 ...

  4. springMVC源码分析--国际化LocaleResolver(一)

    springMVC给我们提供了国际化支持,简单来说就是设置整个系统的运行语言,然后根据系统的运行语言来展示对应语言的页面,一般我们称之为多语言.springMVC国际化机制就是可以设置整个系统的运行语 ...

  5. SpringMVC的数据转换&&数据格式化&&数据校验

    1 SpringMVC的数据绑定流程 SpringMVC将ServletRequest对象及目标方法的入参实例传递给WebDataBinderFactory实例,以创建DataBinder实例对象. ...

  6. SpringMVC(8)国际化

    在SpringMVC(七)格式化显示中我们讲了数据的格式化显示,Spring在做格式化展示的时候已经做了国际化处理,那么如何将我们网站的其它内容(如菜单.标题等)做国际化处理呢?这就是本篇要将的内容- ...

  7. SpringMVC中数据转换

    SpringMVC中接收到的数据都是String形式,然后再根据反射机制将String转换成对应的类型.如果此时想接收一个Date类型,那么我们可以定义一个转换器来完成. 例如,我们有下面的Emp类: ...

  8. 学习SpringMVC——国际化+上传+下载

    每个星期一道菜,这个星期也不例外~~~ 一个软件,一个产品,都是一点点开发并完善起来的,功能越来越多,性能越来越强,用户体验越来越好……这每个指标的提高都需要切切实实的做点东西出来,好比,你的这个产品 ...

  9. SpringMVC国际化与文件上传

    点击阅读上一章 其实SpringMVC中的页面国际化与上一章的验证国际化基本一致. 1.对页面进行国际化 1)首先我们对Spring配置文件中添加国际化bean配置 <!-- 注册国际化信息,必 ...

随机推荐

  1. Scala具体解释---------控制结构和函数

    条件表达式: Scala的if else语法结构和Java的一样.只是,Scala的if else表达式有值.这个值就是跟在if或者else后面的表达式的值. 比如: if(x>0) 0 els ...

  2. 新标准C++程序设计读书笔记_运算符重载

    形式 返回值类型 operator 运算符(形参表) { …… } 运算符重载 (1)运算符重载的实质是函数重载(2)可以重载为普通函数,也可以重载为成员函数 class Complex { publ ...

  3. poj Buy Tickets

    题目链接:http://poj.org/problem?id=2828 类似的题目:http://www.cnblogs.com/lovychen/p/3674048.html 测试数据: in: 4 ...

  4. 八大CMS内容管理系统推荐

    cms系统哪个好 感谢 64320 的投递 时间:2015-03-05 来源:http://www.iisp.com/ztview/ID_16129.html?s=bios 耐思尼克 很多新手站长初次 ...

  5. linux vi/vim文本编辑

    在linux环境下,我们用的最多的文本编辑命令就是vi了,vim是vi的增强版.学习vim时候整理了一下常用的命令行,一起学习. 介绍一下vim的三种常见状态模式: 命令模式,插入模式(编辑),正常模 ...

  6. android跨进程通信(IPC)——AIDL

    转载请标明出处: http://blog.csdn.net/sinat_15877283/article/details/51026711: 本文出自: [温利东的博客] 近期在看 @任玉刚 大神编写 ...

  7. What is Web Application Architecture? How It Works, Trends, Best Practices and More

    At Stackify, we understand the amount of effort that goes into creating great applications. That’s w ...

  8. 解决VMware10虚拟机客户机操作系统无苹果MacOSX

    安装完VMwareWorkstation10.0.3虚拟机,满心希望安装苹果系统MAC OS X 10.9 Mavericks玩一把,却发现VMware10虚拟机客户机操作系统选项里并没有苹果Mac ...

  9. Docker 和一个正常的虚拟机有何区别?

    问: 我多次重读Docker.io文档,希望搞明白Docker.io和一个完全的虚拟机的区别.Docker是如何做到提供一个完整的文件系统,独立的网络环境等等这些功能,同时还没有如此庞大? 为什么部署 ...

  10. 第二百三十八节,Bootstrap输入框和导航组件

    Bootstrap输入框和导航组件 学习要点: 1.输入框组件 2.导航组件 3.导航条组件 本节课我们主要学习一下Bootstrap的两个个组件功能:输入框组件和导航导航条组件. 一.输入框组件 文 ...