当前位置: 首页 > news >正文

无锡网站维护公司网页设计板式重构

无锡网站维护公司,网页设计板式重构,淄博做网站的,求职网站排名SpringMVC SpringMVC是隶属于Spring框架的一部分#xff0c;主要是用来进行Web开发#xff0c;是对Servlet进行了封装。 对于SpringMVC我们主要学习如下内容: SpringMVC简介 请求与响应 REST风格 SSM整合(注解版) 拦截器 SpringMVC是处理Web层/表现层的框架#xff…SpringMVC SpringMVC是隶属于Spring框架的一部分主要是用来进行Web开发是对Servlet进行了封装。 对于SpringMVC我们主要学习如下内容: SpringMVC简介 请求与响应 REST风格 SSM整合(注解版) 拦截器 SpringMVC是处理Web层/表现层的框架所以其主要的作用就是用来接收前端发过来的请求和数据然后经过处理并将处理的结果响应给前端所以如何处理请求和响应是SpringMVC中非常重要的一块内容。 REST是一种软件架构风格可以降低开发的复杂性提高系统的可伸缩性后期的应用也是非常广泛。 SSM整合是把咱们所学习的SpringMVCSpringMybatis整合在一起来完成业务开发是对我们所学习这三个框架的一个综合应用。 对于SpringMVC的学习最终要达成的目标: 掌握基于SpringMVC获取请求参数和响应json数据操作熟练应用基于REST风格的请求路径设置与参数传递能够根据实际业务建立前后端开发通信协议并进行实现基于SSM整合技术开发任意业务模块功能 1. SpringMVC概述 学习SpringMVC我们先来回顾下现在web程序是如何做的咱们现在web程序大都基于三层架构来实现。 三层架构: 浏览器发送一个请求给后端服务器后端服务器现在是使用Servlet来接收请求和数据 如果所有的处理都交给Servlet来处理的话所有的东西都耦合在一起对后期的维护和扩展极为不利 将后端服务器Servlet拆分成三层分别是web、service和dao web层主要由servlet来处理负责页面请求和数据的收集以及响应结果给前端 service层主要负责业务逻辑的处理 dao层主要负责数据的增删改查操作 servlet处理请求和数据的时候存在的问题是一个servlet只能处理一个请求 针对web层进行了优化采用了MVC设计模式将其设计为controller、view和Model controller负责请求和数据的接收接收后将其转发给service进行业务处理 service根据需要会调用dao对数据进行增删改查 dao把数据处理完后将结果交给service,service再交给controller controller根据需求组装成Model和View,Model和View组合起来生成页面转发给前端浏览器 这样做的好处就是controller可以处理多个请求并对请求进行分发执行不同的业务操作。 随着互联网的发展上面的模式因为是同步调用性能慢慢的跟不是需求所以异步调用慢慢的走到了前台是现在比较流行的一种处理方式。 因为是异步调用所以后端不需要返回view视图将其去除 前端如果通过异步调用的方式进行交互后台就需要将返回的数据转换成json格式进行返回 SpringMVC主要负责的就是 controller如何接收请求和数据 如何将请求和数据转发给业务层 如何将响应数据转换成json发回到前端 介绍了这么多对SpringMVC进行一个定义 SpringMVC是一种基于Java实现MVC模型的轻量级Web框架 优点 使用简单、开发便捷(相比于Servlet) 灵活性强 这里所说的优点就需要我们在使用的过程中慢慢体会。 2.SpringMVC入门案例 2.1 SpringMVC实现流程 SpringMVC的具体实现流程 1.创建web工程(Maven结构) 2.设置tomcat服务器加载web工程(tomcat插件) 3.导入坐标(SpringMVCServlet) 4.定义处理请求的功能类(UserController) Controller public class UserController {RequestMapping(/save)ResponseBodypublic void save(){System.out.println(user save ...);} }5.设置SpringMVC的配置类 Configuration ComponentScan(com.itheima.controller) public class SpringMvcConfig { }6.将SpringMVC设定加载到Tomcat容器中,设置请求映射(配置映射关系) public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {//加载springmvc配置类protected WebApplicationContext createServletApplicationContext() {//初始化WebApplicationContext对象AnnotationConfigWebApplicationContext ctx new AnnotationConfigWebApplicationContext();//加载指定配置类ctx.register(SpringMvcConfig.class);return ctx;}//设置由springmvc控制器处理的请求映射路径protected String[] getServletMappings() {return new String[]{/};}//加载spring配置类protected WebApplicationContext createRootApplicationContext() {return null;} }对于上述的配置方式Spring还提供了一种更简单的配置方式可以不用再去创建AnnotationConfigWebApplicationContext对象不用手动register对应的配置类如何实现? public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {protected Class?[] getRootConfigClasses() {return new Class[]{SpringConfig.class};}protected Class?[] getServletConfigClasses() {return new Class[]{SpringMvcConfig.class};}protected String[] getServletMappings() {return new String[]{/};} }知识点1Controller 名称Controller 类型类注解位置SpringMVC控制器类定义上方作用设定SpringMVC的核心控制器bean 知识点2RequestMapping 名称RequestMapping 类型类注解或方法注解位置SpringMVC控制器类或方法定义上方作用设置当前控制器方法请求访问路径相关属性value(默认)请求访问路径 知识点3ResponseBody 名称ResponseBody 类型类注解或方法注解位置SpringMVC控制器类或方法定义上方作用设置当前控制器方法响应内容为当前返回值无需解析 2.2 入门案例总结 一次性工作 创建工程设置服务器加载工程 导入坐标 创建web容器启动类加载SpringMVC配置并设置SpringMVC请求拦截路径 SpringMVC核心配置类设置配置类扫描controller包加载Controller控制器bean 多次工作 定义处理请求的控制器类 定义处理请求的控制器方法并配置映射路径RequestMapping与返回json数据ResponseBody 2.3 工作流程解析 为了更好的使用SpringMVC,我们将SpringMVC的使用过程总共分两个阶段来分析分别是启动服务器初始化过程和单次请求过程 2.3.1 启动服务器初始化过程 服务器启动执行ServletContainersInitConfig类初始化web容器 功能类似于以前的web.xml 执行createServletApplicationContext方法创建了WebApplicationContext对象 该方法加载SpringMVC的配置类SpringMvcConfig来初始化SpringMVC的容器 加载SpringMvcConfig配置类 执行ComponentScan加载对应的bean 扫描指定包及其子包下所有类上的注解如Controller类上的Controller注解 加载UserController每个RequestMapping的名称对应一个具体的方法 此时就建立了 /save 和 save方法的对应关系 执行getServletMappings方法设定SpringMVC拦截请求的路径规则 /代表所拦截请求的路径规则只有被拦截后才能交给SpringMVC来处理请求 2.3.2 单次请求过程 发送请求http://localhost/save web容器发现该请求满足SpringMVC拦截规则将请求交给SpringMVC处理 解析请求路径/save 由/save匹配执行对应的方法save( 上面的第五步已经将请求路径和方法建立了对应关系通过/save就能找到对应的save方法 执行save() 检测到有ResponseBody直接将save()方法的返回值作为响应体返回给请求方 3. 请求与响应 SpringMVC是web层的框架主要的作用是接收请求、接收数据、响应结果所以这一章节是学习SpringMVC的重点内容我们主要会讲解四部分内容: 请求映射路径 请求参数 日期类型参数传递 响应json数据 3.1 设置请求映射路径 Controller RequestMapping(/user) public class UserController {RequestMapping(/save)ResponseBodypublic String save(){System.out.println(user save ...);return {module:user save};}RequestMapping(/delete)ResponseBodypublic String save(){System.out.println(user delete ...);return {module:user delete};} }Controller RequestMapping(/book) public class BookController {RequestMapping(/save)ResponseBodypublic String save(){System.out.println(book save ...);return {module:book save};} }3.2 请求参数 GET发送参数及中文乱码 发送请求与参数: 接收参数 Controller public class UserController {RequestMapping(/commonParam)ResponseBodypublic String commonParam(String name,int age){System.out.println(普通参数传递 name name);System.out.println(普通参数传递 age age);return {module:commonParam};} }中文乱码解决方案 修改pom.xml buildpluginsplugingroupIdorg.apache.tomcat.maven/groupIdartifactIdtomcat7-maven-plugin/artifactIdversion2.1/versionconfigurationport80/port!--tomcat端口号--path//path !--虚拟目录--uriEncodingUTF-8/uriEncoding!--访问路径编解码字符集--/configuration/plugin/plugins/buildPOST发送参数及中文乱码 发送请求与参数: 接收参数 和GET一致不用做任何修改 Controller public class UserController {RequestMapping(/commonParam)ResponseBodypublic String commonParam(String name,int age){System.out.println(普通参数传递 name name);System.out.println(普通参数传递 age age);return {module:commonParam};} }中文乱码解决方案 配置过滤器在web容器配置类中 public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {protected Class?[] getRootConfigClasses() {return new Class[0];}protected Class?[] getServletConfigClasses() {return new Class[]{SpringMvcConfig.class};}protected String[] getServletMappings() {return new String[]{/};}//乱码处理Overrideprotected Filter[] getServletFilters() {CharacterEncodingFilter filter new CharacterEncodingFilter();filter.setEncoding(UTF-8);return new Filter[]{filter};} }3.3 五种类型参数传递 常见的参数种类有: 普通参数 POJO类型参数 嵌套POJO类型参数 数组类型参数 集合类型参数 3.3.1 普通参数 如果形参与地址参数名不一致该如何解决 解决方案:使用RequestParam注解 RequestMapping(/commonParamDifferentName)ResponseBodypublic String commonParamDifferentName(RequestParam(name) String userName , int age){System.out.println(普通参数传递 userName userName);System.out.println(普通参数传递 age age);return {module:common param different name};}注意:写上RequestParam注解框架就不需要自己去解析注入能提升框架处理性能 3.3.2 POJO数据类型 简单数据类型一般处理的是参数个数比较少的请求如果参数比较多那么后台接收参数的时候就比较复杂这个时候我们可以考虑使用POJO数据类型。 POJO参数请求参数名与形参对象属性名相同定义POJO类型形参即可接收参数 此时需要使用前面准备好的POJO类先来看下User public class User {private String name;private int age;//setter...getter...略 }发送请求和参数: 后台接收参数: //POJO参数请求参数与形参对象中的属性对应即可完成参数传递 RequestMapping(/pojoParam) ResponseBody public String pojoParam(User user){System.out.println(pojo参数传递 user user);return {module:pojo param}; }注意: POJO参数接收前端GET和POST发送请求数据的方式不变。请求参数key的名称要和POJO中属性的名称一致否则无法封装。 3.3.3 嵌套POJO类型参数 如果POJO对象中嵌套了其他的POJO类如 public class Address {private String province;private String city;//setter...getter...略 } public class User {private String name;private int age;private Address address;//setter...getter...略 }嵌套POJO参数请求参数名与形参对象属性名相同按照对象层次结构关系即可接收嵌套POJO属性参数 发送请求和参数: 后台接收参数: //POJO参数请求参数与形参对象中的属性对应即可完成参数传递 RequestMapping(/pojoParam) ResponseBody public String pojoParam(User user){System.out.println(pojo参数传递 user user);return {module:pojo param}; }注意: 请求参数key的名称要和POJO中属性的名称一致否则无法封装 3.3.4 数组类型参数 数组参数请求参数名与形参对象属性名相同且请求参数为多个定义数组类型即可接收参数 发送请求和参数: 后台接收参数: //数组参数同名请求参数可以直接映射到对应名称的形参数组对象中RequestMapping(/arrayParam)ResponseBodypublic String arrayParam(String[] likes){System.out.println(数组参数传递 likes Arrays.toString(likes));return {module:array param};}3.3.5 集合类型参数 发送请求和参数: 后端接收参数 使用RequestParam注解 //集合参数同名请求参数可以使用RequestParam注解映射到对应名称的集合对象中作为数据 RequestMapping(/listParam) ResponseBody public String listParam(RequestParam ListString likes){System.out.println(集合参数传递 likes likes);return {module:list param}; }如果不加RequestParam运行会报错 错误的原因是:SpringMVC将List看做是一个POJO对象来处理将其创建一个对象并准备把前端的数据封装到对象中但是List是一个接口无法创建对象所以报错。 集合保存普通参数请求参数名与形参集合对象名相同且请求参数为多个RequestParam绑定参数关系 对于简单数据类型使用数组会比集合更简单些。 知识点1RequestParam 名称RequestParam 类型形参注解位置SpringMVC控制器方法形参定义前面作用绑定请求参数与处理器方法形参间的关系相关参数required是否为必传参数 defaultValue参数默认值 3.4 JSON数据传输参数 现在比较流行的开发方式为异步调用。前后台以异步方式进行交换传输的数据使用的是JSON, 对于JSON数据类型我们常见的有三种: json普通数组[“value1”,“value2”,“value3”,…] json对象{key1:value1,key2:value2,…} json对象数组[{key1:value1,…},{key2:value2,…}] 对于上述数据前端如何发送后端如何接收? 3.4.1 JSON普通数组 步骤 添加jackson依赖用来处理json的转换 postman发送JSON数据 开启SpringMVC注解支持EnableWebMvc,其中一个功能就是支持将JSON转换成对象 Configuration ComponentScan(com.itheima.controller) //开启json数据类型自动转换 EnableWebMvc public class SpringMvcConfig { }参数前添加RequsetBody //使用RequestBody注解将外部传递的json数组数据映射到形参的集合对象中作为数据 RequestMapping(/listParamForJson) ResponseBody public String listParamForJson(RequestBody ListString likes){System.out.println(list common(json)参数传递 list likes);return {module:list common for json param}; }3.4.2 JSON对象数据 请求和数据的发送: 后端接收数据 RequestMapping(/pojoParamForJson) ResponseBody public String pojoParamForJson(RequestBody User user){System.out.println(pojo(json)参数传递 user user);return {module:pojo for json param}; }3.4.3 JSON对象数组 请求和数据的发送: 后端接收数据: RequestMapping(/listPojoParamForJson) ResponseBody public String listPojoParamForJson(RequestBody ListUser list){System.out.println(list pojo(json)参数传递 list list);return {module:list pojo for json param}; }小结 SpringMVC接收JSON数据的实现步骤为: (1)导入jackson包 (2)使用PostMan发送JSON数据 (3)开启SpringMVC注解驱动在配置类上添加EnableWebMvc注解 (4)Controller方法的参数前添加RequestBody注解 知识点1EnableWebMvc 名称EnableWebMvc 类型配置类注解位置SpringMVC配置类定义上方作用开启SpringMVC多项辅助功能转换JSON数据等 知识点2RequestBody 名称RequestBody 类型形参注解位置SpringMVC控制器方法形参定义前面作用将请求中请求体所包含的数据传递给请求参数此注解一个处理器方法只能使用一次 RequestBody与RequestParam区别 区别 RequestParam用于接收url地址传参表单传参【application/x-www-form-urlencoded】(主要是针对形参名与请求参数名不一致以及传递集合的情况) RequestBody用于接收json数据【application/json】 应用 后期开发中发送json格式数据为主RequestBody应用较广 如果发送非json格式数据选用RequestParam接收请求参数 3.5 日期类型参数传递 对于日期的格式有N多中输入方式比如: 2088-08-18 2088/08/18 08/18/2088 … 前端发送请求和参数 后端接收日期类型的参数 使用DateTimeFormat RequestMapping(/dataParam) ResponseBody public String dataParam(Date date,DateTimeFormat(patternyyyy-MM-dd) Date date1,DateTimeFormat(patternyyyy/MM/dd HH:mm:ss) Date date2)System.out.println(参数传递 date date);System.out.println(参数传递 date1(yyyy-MM-dd) date1);System.out.println(参数传递 date2(yyyy/MM/dd HH:mm:ss) date2);return {module:data param}; }知识点1DateTimeFormat 名称DateTimeFormat 类型形参注解位置SpringMVC控制器方法形参前面作用设定日期时间型数据格式相关属性pattern指定日期时间格式字符串 3.6 响应 对于响应主要就包含两部分内容 响应页面 响应数据 文本数据 json数据 3.6.1 响应页面[了解] Controller public class UserController {RequestMapping(/toJumpPage)//注意//1.此处不能添加ResponseBody,如果加了该注入会直接将page.jsp当字符串返回前端//2.方法需要返回Stringpublic String toJumpPage(){System.out.println(跳转页面);//此处应该是转发操作return page.jsp;}}3.6.2 返回文本数据[了解] Controller public class UserController {RequestMapping(/toText)//注意此处该注解就不能省略如果省略了,会把response text当前页面名称去查找如果没有回报404错误ResponseBodypublic String toText(){System.out.println(返回纯文本数据);return response text;}}3.6.3 响应JSON数据 响应POJO对象 Controller public class UserController {RequestMapping(/toJsonPOJO)ResponseBodypublic User toJsonPOJO(){System.out.println(返回json对象数据);User user new User();user.setName(itcast);user.setAge(15);return user;}}返回值为实体类对象设置返回值为实体类类型即可实现返回对应对象的json数据需要依赖ResponseBody注解和EnableWebMvc注解 响应POJO集合对象 Controller public class UserController {RequestMapping(/toJsonList)ResponseBodypublic ListUser toJsonList(){System.out.println(返回json集合数据);User user1 new User();user1.setName(传智播客);user1.setAge(15);User user2 new User();user2.setName(黑马程序员);user2.setAge(12);ListUser userList new ArrayListUser();userList.add(user1);userList.add(user2);return userList;}}知识点1ResponseBody 名称ResponseBody 类型方法\类注解位置SpringMVC控制器方法定义上方和控制类上作用设置当前控制器返回值作为响应体, 写在类上该类的所有方法都有该注解功能相关属性pattern指定日期时间格式字符串 当方法上有ReponseBody注解后 方法的返回值为字符串会将其作为文本内容直接响应给前端 方法的返回值为对象会将对象转换成JSON响应给前端 4.Rest风格 对于Rest风格我们需要学习的内容包括: REST简介 REST入门案例 REST快速开发 案例:基于RESTful页面数据交互 4.1REST简介 RESTRepresentational State Transfer表现形式状态转换,它是一种软件架构风格 当我们想表示一个网络资源的时候可以使用两种方式: 传统风格资源描述形式 http://localhost/user/getById?id1 查询id为1的用户信息 http://localhost/user/saveUser 保存用户信息 REST风格描述形式 http://localhost/user/1 http://localhost/user REST的优点有: 隐藏资源的访问行为无法通过地址得知对资源是何种操作 书写简化 但是一个相同的url地址即可以是新增也可以是修改或者查询那么到底我们该如何区分该请求到底是什么操作呢? 按照REST风格访问资源时使用行为动作区分对资源进行了何种操作 http://localhost/users 查询全部用户信息 GET查询 http://localhost/users/1 查询指定用户信息 GET查询 http://localhost/users 添加用户信息 POST新增/保存 http://localhost/users 修改用户信息 PUT修改/更新 http://localhost/users/1 删除用户信息 DELETE删除 PS:根据REST风格对资源后台服务进行访问称为RESTful。 4.2 RestFul入门案例 1.创建web的maven项目导入坐标 2.创建servletspringmvc配置类 3.编写pojo类Book 4.编写BookController控制器类 部分代码如下 save方法 RequestMapping(value /users,method RequestMethod.POST)ResponseBodypublic String save(RequestBody Book book){System.out.println(book save... book);return {module:book save};}delete方法 后端获取参数需要做如下修改: ● 修改RequestMapping的value属性将其中修改为/users/{id}目的是和路径匹配 ● 在方法的形参前添加PathVariable注解 Controller public class UserController {//设置当前请求方法为DELETE表示REST风格中的删除操作RequestMapping(value /users/{id}/{name},method RequestMethod.DELETE)ResponseBodypublic String delete(PathVariable Integer id,PathVariable String name) {System.out.println(user delete... id,name);return {module:user delete};} }小结 RESTful入门案例我们需要学习的内容如下: (1)设定Http请求动作(动词) RequestMapping(value“”,method RequestMethod.POST|GET|PUT|DELETE) (2)设定请求参数(路径变量) RequestMapping(value“/users/{id}”,method RequestMethod.DELETE) ReponseBody public String delete(PathVariable Integer id){ } 知识点1PathVariable 名称PathVariable 类型形参注解位置SpringMVC控制器方法形参定义前面作用绑定路径参数与处理器方法形参间的关系要求路径参数名与形参名一一对应 接收参数的注解RequestBody、RequestParam、PathVariable 关于接收参数我们学过三个注解RequestBody、RequestParam、PathVariable,这三个注解之间的区别和应用分别是什么? 区别 RequestParam用于接收url地址传参或表单传参 RequestBody用于接收json数据请求体 PathVariable用于接收路径参数使用 {参数名称} 描述路径参数 应用 后期开发中发送请求参数超过1个时以json格式为主RequestBody应用较广 如果发送非json格式数据选用RequestParam接收请求参数 采用RESTful进行开发当参数数量较少时例如1个可以采用PathVariable接收请求路径变量通常用于传递id值 4.3RestFul快速开发 知识点1RestController 名称RestController 类型类注解位置基于SpringMVC的RESTful开发控制器类定义上方作用设置当前控制器类为RESTful风格 等同于Controller与ResponseBody两个注解组合功能 知识点2GetMapping PostMapping PutMapping DeleteMapping 名称GetMapping PostMapping PutMapping DeleteMapping 类型方法注解位置基于SpringMVC的RESTful开发控制器方法定义上方作用设置当前控制器方法请求访问路径与请求动作每种对应一个请求动作 例如GetMapping对应GET请求相关属性value默认请求访问路径 4.4RestFul实例 后端 编写Controller类并使用RESTful进行配置: RestController RequestMapping(/books) public class BookController {PostMappingpublic String save(RequestBody Book book){System.out.println(book save book);return {module:book save success};}GetMappingpublic ListBook getAll(){System.out.println(book getAll is running ...);ListBook bookList new ArrayListBook();Book book1 new Book();book1.setType(计算机);book1.setName(SpringMVC入门教程);book1.setDescription(小试牛刀);bookList.add(book1);Book book2 new Book();book2.setType(计算机);book2.setName(SpringMVC实战教程);book2.setDescription(一代宗师);bookList.add(book2);Book book3 new Book();book3.setType(计算机丛书);book3.setName(SpringMVC实战教程进阶);book3.setDescription(一代宗师呕心创作);bookList.add(book3);return bookList;} }前端 想要访问静态资源时由于SpringMVC拦截静态资源导致无法访问 (1)出现错误的原因? SpringMVC拦截了静态资源根据/pages/books.html去controller找对应的方法找不到所以会报404的错误。 (2)SpringMVC为什么会拦截静态资源呢? (3)解决方案? SpringMVC需要将静态资源进行放行。 Configuration public class SpringMvcSupport extends WebMvcConfigurationSupport {//设置静态资源访问过滤当前类需要设置为配置类并被扫描加载Overrideprotected void addResourceHandlers(ResourceHandlerRegistry registry) {//当访问/pages/????时候从/pages目录下查找内容registry.addResourceHandler(/pages/**).addResourceLocations(/pages/);registry.addResourceHandler(/js/**).addResourceLocations(/js/);registry.addResourceHandler(/css/**).addResourceLocations(/css/);registry.addResourceHandler(/plugins/**).addResourceLocations(/plugins/);} }该配置类是在config目录下SpringMVC扫描的是controller包所以该配置类还未生效要想生效需要将SpringMvcConfig配置类进行修改 Configuration ComponentScan({com.itheima.controller,com.itheima.config}) EnableWebMvc public class SpringMvcConfig { }或者Configuration ComponentScan(com.itheima) EnableWebMvc public class SpringMvcConfig { }最后由html文件发送ajax异步请求调用controller的各个方法即可 //添加saveBook () {axios.post(/books,this.formData).then((res){});},//主页列表查询getAll() {axios.get(/books).then((res){this.dataList res.data;});}5.SSM整合 前面我们已经把Mybatis、Spring和SpringMVC三个框架进行了学习今天主要的内容就是把这三个框架整合在一起完成我们的业务功能开发具体如何来整合我们一步步来学习。 流程分析: 5.1 创建工程 创建一个Maven的web工程 pom.xml添加SSM需要的依赖jar包 ?xml version1.0 encodingUTF-8?project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersiongroupIdcom.itheima/groupIdartifactIdspringmvc_08_ssm/artifactIdversion1.0-SNAPSHOT/versionpackagingwar/packagingdependenciesdependencygroupIdorg.springframework/groupIdartifactIdspring-webmvc/artifactIdversion5.2.10.RELEASE/version/dependencydependencygroupIdorg.springframework/groupIdartifactIdspring-jdbc/artifactIdversion5.2.10.RELEASE/version/dependencydependencygroupIdorg.springframework/groupIdartifactIdspring-test/artifactIdversion5.2.10.RELEASE/version/dependencydependencygroupIdorg.mybatis/groupIdartifactIdmybatis/artifactIdversion3.5.6/version/dependencydependencygroupIdorg.mybatis/groupIdartifactIdmybatis-spring/artifactIdversion1.3.0/version/dependencydependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactIdversion5.1.47/version/dependencydependencygroupIdcom.alibaba/groupIdartifactIddruid/artifactIdversion1.1.16/version/dependencydependencygroupIdjunit/groupIdartifactIdjunit/artifactIdversion4.12/versionscopetest/scope/dependencydependencygroupIdjavax.servlet/groupIdartifactIdjavax.servlet-api/artifactIdversion3.1.0/versionscopeprovided/scope/dependencydependencygroupIdcom.fasterxml.jackson.core/groupIdartifactIdjackson-databind/artifactIdversion2.9.0/version/dependency/dependenciesbuildpluginsplugingroupIdorg.apache.tomcat.maven/groupIdartifactIdtomcat7-maven-plugin/artifactIdversion2.1/versionconfigurationport80/portpath//path/configuration/plugin/plugins/build /project编写Web项目的入口配置类ServletConfig实现AbstractAnnotationConfigDispatcherServletInitializer重写以下方法 getRootConfigClasses() 返回Spring的配置类-需要SpringConfig配置类 getServletConfigClasses() 返回SpringMVC的配置类-需要SpringMvcConfig配置类 getServletMappings() : 设置SpringMVC请求拦截路径规则 getServletFilters() 设置过滤器解决POST请求中文乱码问题 5.2 SSM整合[重点是各个配置的编写] SpringConfig 标识该类为配置类 Configuration 扫描Service所在的包 ComponentScan 在Service层要管理事务 EnableTransactionManagement 读取外部的properties配置文件 PropertySource 整合Mybatis需要引入Mybatis相关配置类 Import 第三方数据源配置类 JdbcConfig 构建DataSource数据源DruidDataSouroce,需要注入数据库连接四要素 Bean Value 构建平台事务管理器DataSourceTransactionManager,Bean Mybatis配置类 MybatisConfig 构建SqlSessionFactoryBean并设置别名扫描与数据源Bean 构建MapperScannerConfigurer并设置DAO层的包扫描 创建mapper接口的自动代理对象Bean SpringMvcConfig 标识该类为配置类 Configuration 扫描Controller所在的包 ComponentScan 开启SpringMVC注解支持 EnableWebMvc ,开启如转换json数据等多种辅助功能 5.3 功能模块[与具体的业务模块有关] 创建数据库表 根据数据库表创建对应的模型类pojo 通过Dao层完成数据库表的增删改查(接口自动代理) 编写Service层[Service接口实现类] Service Transactional ​ 整合Junit对业务层进行单元测试 RunWith ContextConfiguration Test 编写Controller层 接收请求 RequestMapping GetMapping PostMapping PutMapping DeleteMapping 接收数据 简单、POJO、嵌套POJO、集合、数组、JSON数据类型 RequestParam PathVariable RequestBody 转发业务层 Autowired 响应结果 ResponseBody PSSSM整合完之后就可以进行junit测试service类以及postman测试controller类 6.统一结果封装 SSM整合以及功能模块开发完成后接下来我们在上述案例的基础上分析下有哪些问题需要我们去解决下。首先第一个问题是: 随着业务的增长我们需要返回的数据类型会越来越多。对于前端开发人员在解析数据的时候就比较凌乱了所以对于前端来说如果后台能够返回一个统一的数据结果前端在解析的时候就可以按照一种方式进行解析。开发就会变得更加简单。 所以我们就想能不能将返回结果的数据进行统一具体如何来做大体的思路为: 为了封装返回的结果数据:创建结果模型类封装数据到data属性中 为了封装返回的数据是何种操作及是否操作成功:封装操作结果到code属性中 操作失败后为了封装返回的错误信息:封装特殊消息到message(msg)属性中 根据分析我们可以设置统一数据返回结果类 1.Result: public class Result {//描述统一格式中的数据private Object data;//描述统一格式中的编码用于区分操作可以简化配置0或1表示成功失败private Integer code;//描述统一格式中的消息可选属性private String msg;public Result() {}//构造方法是方便对象的创建public Result(Integer code,Object data) {this.data data;this.code code;}//构造方法是方便对象的创建public Result(Integer code, Object data, String msg) {this.data data;this.code code;this.msg msg;}//setter...getter...省略 }**注意:**Result类名及类中的字段并不是固定的可以根据需要自行增减提供若干个构造方法方便操作。 2.Code: //状态码 public class Code {public static final Integer SAVE_OK 20011;public static final Integer DELETE_OK 20021;public static final Integer UPDATE_OK 20031;public static final Integer GET_OK 20041;public static final Integer SAVE_ERR 20010;public static final Integer DELETE_ERR 20020;public static final Integer UPDATE_ERR 20030;public static final Integer GET_ERR 20040; }**注意:**code类中的常量设计也不是固定的可以根据需要自行增减例如将查询再进行细分为GET_OK,GET_ALL_OK,GET_PAGE_OK等。 3.修改Controller类的返回值为Result //统一每一个控制器方法返回值 RestController RequestMapping(/books) public class BookController {Autowiredprivate BookService bookService;PostMappingpublic Result save(RequestBody Book book) {boolean flag bookService.save(book);return new Result(flag ? Code.SAVE_OK:Code.SAVE_ERR,flag);}PutMappingpublic Result update(RequestBody Book book) {boolean flag bookService.update(book);return new Result(flag ? Code.UPDATE_OK:Code.UPDATE_ERR,flag);}DeleteMapping(/{id})public Result delete(PathVariable Integer id) {boolean flag bookService.delete(id);return new Result(flag ? Code.DELETE_OK:Code.DELETE_ERR,flag);}GetMapping(/{id})public Result getById(PathVariable Integer id) {Book book bookService.getById(id);Integer code book ! null ? Code.GET_OK : Code.GET_ERR;String msg book ! null ? : 数据查询失败请重试;return new Result(code,book,msg);}GetMappingpublic Result getAll() {ListBook bookList bookService.getAll();Integer code bookList ! null ? Code.GET_OK : Code.GET_ERR;String msg bookList ! null ? : 数据查询失败请重试;return new Result(code,bookList,msg);} }至此我们的返回结果就已经能以一种统一的格式返回给前端。前端根据返回的结果先从中获取code,根据code判断如果成功则取data属性的值如果失败则取msg中的值做提示。 7.统一异常处理 先来看下异常的种类及出现异常的原因: 框架内部抛出的异常因使用不合规导致 数据层抛出的异常因外部服务器故障导致例如服务器访问超时 业务层抛出的异常因业务逻辑书写错误导致例如遍历业务书写操作导致索引异常等 表现层抛出的异常因数据收集、校验等规则导致例如不匹配的数据类型间导致异常 工具类抛出的异常因工具类书写不严谨不够健壮导致例如必要释放的连接长期未释放等 思考 各个层级均出现异常异常处理代码书写在哪一层? 所有的异常均抛出到表现层进行处理 异常的种类很多表现层如何将所有的异常都处理到呢? 异常分类 表现层处理异常每个方法中单独书写代码书写量巨大且意义不强如何解决? AOP 7.1异常处理器 对于上面这些问题及解决方案SpringMVC已经为我们提供了一套解决方案: 异常处理器: 集中的、统一的处理项目中出现的异常并返回数据给前端 知识点1RestControllerAdvice 名称RestControllerAdvice 类型类注解位置Rest风格开发的控制器增强类定义上方作用为Rest风格开发的控制器类做增强 知识点2ExceptionHandler 名称ExceptionHandler 类型方法注解位置专用于异常处理的控制器方法上方作用设置指定异常的处理方案功能等同于控制器方法 出现异常后终止原始控制器执行,并转入当前方法执行 **说明**此类方法可以根据处理的异常不同制作多个方法分别处理对应的异常 7.2项目异常处理方案 7.2.1异常分类 异常处理器我们已经能够使用了那么在咱们的项目中该如何来处理异常呢? 因为异常的种类有很多如果每一个异常都对应一个ExceptionHandler那得写多少个方法来处理各自的异常所以我们在处理异常之前需要对异常进行一个分类: 业务异常BusinessException 规范的用户行为产生的异常 用户在页面输入内容的时候未按照指定格式进行数据填写如在年龄框输入的是字符串 不规范的用户行为操作产生的异常 如用户故意传递错误数据 系统异常SystemException 项目运行过程中可预计但无法避免的异常 比如数据库或服务器宕机 其他异常Exception 编程人员未预期到的异常如:用到的文件不存在 将异常分类以后针对不同类型的异常要提供具体的解决方案: 7.2.2异常解决方案 业务异常BusinessException 发送对应消息传递给用户提醒规范操作 大家常见的就是提示用户名已存在或密码格式不正确等 系统异常SystemException 发送固定消息传递给用户安抚用户 系统繁忙请稍后再试 系统正在维护升级请稍后再试 系统出问题请联系系统管理员等 发送特定消息给运维人员提醒维护 可以发送短信、邮箱或者是公司内部通信软件 记录日志 发消息和记录日志对用户来说是不可见的属于后台程序 其他异常Exception 发送固定消息传递给用户安抚用户 发送特定消息给编程人员提醒维护纳入预期范围内 一般是程序没有考虑全比如未做非空校验等 记录日志 7.2.3异常解决方案的具体表现 步骤1:自定义异常类 //自定义异常处理器用于封装异常信息对异常进行分类 public class SystemException extends RuntimeException{private Integer code;public Integer getCode() {return code;}public void setCode(Integer code) {this.code code;}public SystemException(Integer code, String message) {super(message);this.code code;}public SystemException(Integer code, String message, Throwable cause) {super(message, cause);this.code code;}}//自定义异常处理器用于封装异常信息对异常进行分类 public class BusinessException extends RuntimeException{private Integer code;public Integer getCode() {return code;}public void setCode(Integer code) {this.code code;}public BusinessException(Integer code, String message) {super(message);this.code code;}public BusinessException(Integer code, String message, Throwable cause) {super(message, cause);this.code code;}}说明: 让自定义异常类继承RuntimeException的好处是后期在抛出这两个异常的时候就不用在try…catch…或throws了 自定义异常类中添加code属性的原因是为了更好的区分异常是来自哪个业务的 步骤2:将其他异常包成自定义异常 假如在BookServiceImpl的getById方法抛异常了该如何来包装呢? public Book getById(Integer id) {//模拟业务异常包装成自定义异常if(id 1){throw new BusinessException(Code.BUSINESS_ERR,请不要使用你的技术挑战我的耐性!);}//模拟系统异常将可能出现的异常进行包装转换成自定义异常try{int i 1/0;}catch (Exception e){throw new SystemException(Code.SYSTEM_TIMEOUT_ERR,服务器访问超时请重试!,e);}return bookDao.getById(id); }具体的包装方式有 方式一:try{}catch(){}在catch中重新throw我们自定义异常即可。 方式二:直接throw自定义异常即可 上面为了使code看着更专业些我们在Code类中再新增需要的属性 //状态码 public class Code {public static final Integer SAVE_OK 20011;public static final Integer DELETE_OK 20021;public static final Integer UPDATE_OK 20031;public static final Integer GET_OK 20041;public static final Integer SAVE_ERR 20010;public static final Integer DELETE_ERR 20020;public static final Integer UPDATE_ERR 20030;public static final Integer GET_ERR 20040;public static final Integer SYSTEM_ERR 50001;public static final Integer SYSTEM_TIMEOUT_ERR 50002;public static final Integer SYSTEM_UNKNOW_ERR 59999;public static final Integer BUSINESS_ERR 60002; }步骤3:处理器类中处理自定义异常 //RestControllerAdvice用于标识当前类为REST风格对应的异常处理器 RestControllerAdvice public class ProjectExceptionAdvice {//ExceptionHandler用于设置当前处理器类对应的异常类型ExceptionHandler(SystemException.class)public Result doSystemException(SystemException ex){//记录日志//发送消息给运维//发送邮件给开发人员,ex对象发送给开发人员return new Result(ex.getCode(),null,ex.getMessage());}ExceptionHandler(BusinessException.class)public Result doBusinessException(BusinessException ex){return new Result(ex.getCode(),null,ex.getMessage());}//除了自定义的异常处理器保留对Exception类型的异常处理用于处理非预期的异常ExceptionHandler(Exception.class)public Result doOtherException(Exception ex){//记录日志//发送消息给运维//发送邮件给开发人员,ex对象发送给开发人员return new Result(Code.SYSTEM_UNKNOW_ERR,null,系统繁忙请稍后再试);} }8.前后台协议协调 项目结构 !DOCTYPE htmlhtmlhead!-- 页面meta --meta charsetutf-8meta http-equivX-UA-Compatible contentIEedgetitleSpringMVC案例/titlemeta contentwidthdevice-width,initial-scale1,maximum-scale1,user-scalableno nameviewport!-- 引入样式 --link relstylesheet href../plugins/elementui/index.csslink relstylesheet href../plugins/font-awesome/css/font-awesome.min.csslink relstylesheet href../css/style.css/headbody classhold-transitiondiv idappdiv classcontent-headerh1图书管理/h1/divdiv classapp-containerdiv classboxdiv classfilter-containerel-input placeholder图书名称 v-modelpagination.queryString stylewidth: 200px; classfilter-item/el-inputel-button clickgetAll() classdalfBut查询/el-buttonel-button typeprimary classbutT clickhandleCreate()新建/el-button/divel-table sizesmall current-row-keyid :datadataList stripe highlight-current-rowel-table-column typeindex aligncenter label序号/el-table-columnel-table-column proptype label图书类别 aligncenter/el-table-columnel-table-column propname label图书名称 aligncenter/el-table-columnel-table-column propdescription label描述 aligncenter/el-table-columnel-table-column label操作 aligncentertemplate slot-scopescopeel-button typeprimary sizemini clickhandleUpdate(scope.row)编辑/el-buttonel-button typedanger sizemini clickhandleDelete(scope.row)删除/el-button/template/el-table-column/el-table!-- 新增标签弹层 --div classadd-formel-dialog title新增图书 :visible.syncdialogFormVisibleel-form refdataAddForm :modelformData :rulesrules label-positionright label-width100pxel-rowel-col :span12el-form-item label图书类别 proptypeel-input v-modelformData.type//el-form-item/el-colel-col :span12el-form-item label图书名称 propnameel-input v-modelformData.name//el-form-item/el-col/el-rowel-rowel-col :span24el-form-item label描述el-input v-modelformData.description typetextarea/el-input/el-form-item/el-col/el-row/el-formdiv slotfooter classdialog-footerel-button clickdialogFormVisible false取消/el-buttonel-button typeprimary clickhandleAdd()确定/el-button/div/el-dialog/div!-- 编辑标签弹层 --div classadd-formel-dialog title编辑检查项 :visible.syncdialogFormVisible4Editel-form refdataEditForm :modelformData :rulesrules label-positionright label-width100pxel-rowel-col :span12el-form-item label图书类别 proptypeel-input v-modelformData.type//el-form-item/el-colel-col :span12el-form-item label图书名称 propnameel-input v-modelformData.name//el-form-item/el-col/el-rowel-rowel-col :span24el-form-item label描述el-input v-modelformData.description typetextarea/el-input/el-form-item/el-col/el-row/el-formdiv slotfooter classdialog-footerel-button clickdialogFormVisible4Edit false取消/el-buttonel-button typeprimary clickhandleEdit()确定/el-button/div/el-dialog/div/div/div/div/body!-- 引入组件库 -- script src../js/vue.js/scriptscript src../plugins/elementui/index.js/scriptscript typetext/javascript src../js/jquery.min.js/scriptscript src../js/axios-0.18.0.js/script!-- 重点--scriptvar vue new Vue({el: #app,data:{pagination: {},dataList: [],//当前页要展示的列表数据formData: {},//表单数据dialogFormVisible: false,//控制表单是否可见dialogFormVisible4Edit:false,//编辑表单是否可见rules: {//校验规则type: [{ required: true, message: 图书类别为必填项, trigger: blur }],name: [{ required: true, message: 图书名称为必填项, trigger: blur }]}},//钩子函数VUE对象初始化完成后自动执行created() {this.getAll();},methods: {//列表getAll() {//发送ajax请求axios.get(/books).then((res){this.dataList res.data.data;});},//弹出添加窗口handleCreate() {this.dialogFormVisible true;this.resetForm();},//重置表单resetForm() {this.formData {};},//添加handleAdd () {//发送ajax请求axios.post(/books,this.formData).then((res){console.log(res.data);//如果操作成功关闭弹层显示数据if(res.data.code 20011){this.dialogFormVisible false;this.$message.success(添加成功);}else if(res.data.code 20010){this.$message.error(添加失败);}else{//服务器异常等this.$message.error(res.data.msg);}}).finally((){this.getAll();});},//弹出编辑窗口handleUpdate(row) {// console.log(row); //row.id 查询条件//查询数据根据id查询axios.get(/books/row.id).then((res){// console.log(res.data.data);if(res.data.code 20041){//展示弹层加载数据this.formData res.data.data;this.dialogFormVisible4Edit true;}else{this.$message.error(res.data.msg);}});},//编辑handleEdit() {//发送ajax请求axios.put(/books,this.formData).then((res){//如果操作成功关闭弹层显示数据if(res.data.code 20031){this.dialogFormVisible4Edit false;this.$message.success(修改成功);}else if(res.data.code 20030){this.$message.error(修改失败);}else{this.$message.error(res.data.msg);}}).finally((){this.getAll();});},// 删除handleDelete(row) {//1.弹出提示框this.$confirm(此操作永久删除当前数据是否继续,提示,{type:info}).then((){//2.做删除业务axios.delete(/books/row.id).then((res){if(res.data.code 20021){this.$message.success(删除成功);}else{this.$message.error(删除失败);}}).finally((){this.getAll();});}).catch((){//3.取消删除this.$message.info(取消删除操作);});}}})/script/html9.拦截器interceptor 浏览器访问资源路径流程: (1)浏览器发送一个请求会先到Tomcat的web服务器 (2)Tomcat服务器接收到请求以后会去判断请求的是静态资源还是动态资源 (3)如果是静态资源会直接到Tomcat的项目部署目录下去直接访问 (4)如果是动态资源就需要交给项目的后台代码进行处理 (5)在找到具体的方法之前我们可以去配置过滤器(可以配置多个)按照顺序进行执行 (6)然后进入到到中央处理器(SpringMVC中的内容)SpringMVC会根据配置的规则进行拦截 (7)如果满足规则则进行处理找到其对应的controller类中的方法进行执行,完成后返回结果 (8)如果不满足规则则不进行处理 (9)这个时候如果我们需要在每个Controller方法执行的前后添加业务具体该如何来实现? 这个就是拦截器要做的事。 9.1 拦截器概念 拦截器Interceptor是一种动态拦截方法调用的机制在SpringMVC中动态拦截控制器方法的执行 作用: 在指定的方法调用前后执行预先设定的代码 阻止原始方法的执行 总结拦截器就是用来做增强(底层就是AOP) 拦截器和过滤器之间的区别是什么? 归属不同Filter属于Servlet技术Interceptor属于SpringMVC技术 拦截内容不同Filter对所有访问进行增强Interceptor仅针对SpringMVC的访问进行增强 9.2 拦截器入门案例 项目结构 1.创建拦截器类 Component //定义拦截器类实现HandlerInterceptor接口 //注意当前类必须受Spring容器控制 public class ProjectInterceptor implements HandlerInterceptor {Override//原始方法调用前执行的内容public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println(preHandle...);return true;}Override//原始方法调用后执行的内容public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println(postHandle...);}Override//原始方法调用完成后执行的内容public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println(afterCompletion...);} }**注意:**拦截器类要被SpringMVC容器扫描到。 2.配置拦截器类 Configuration public class SpringMvcSupport extends WebMvcConfigurationSupport {Autowiredprivate ProjectInterceptor projectInterceptor;Overrideprotected void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler(/pages/**).addResourceLocations(/pages/);}Overrideprotected void addInterceptors(InterceptorRegistry registry) {//配置拦截器registry.addInterceptor(projectInterceptor).addPathPatterns(/books,/books/* );} }**注意**此处也可以简化SpringMvcSupport的编写直接让SpringMvcConfig实现WebMvcConfigurer接口即可,此后就不用再写SpringMvcSupport类了 Configuration ComponentScan({com.itheima.controller}) EnableWebMvc //实现WebMvcConfigurer接口可以简化开发但具有一定的侵入性 public class SpringMvcConfig implements WebMvcConfigurer {Autowiredprivate ProjectInterceptor projectInterceptor;Overridepublic void addInterceptors(InterceptorRegistry registry) {//配置多拦截器registry.addInterceptor(projectInterceptor).addPathPatterns(/books,/books/*);} }拦截器执行流程 pshandle[controller的方法] 9.3 拦截器参数 preHandle 原始方法之前运行 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {HandlerMethod hm (HandlerMethod)handler;String methodName hm.getMethod().getName();//可以获取方法的名称System.out.println(preHandle...methodName);return true; }request:请求对象 response:响应对象 handler:被调用的处理器对象本质上是一个方法对象[HandleMethod]对反射中的Method对象进行了再包装 postHandle 原始方法运行后运行如果原始方法被拦截则不执行 public void postHandle(HttpServletRequest request,HttpServletResponse response,Object handler,ModelAndView modelAndView) throws Exception {System.out.println(postHandle); }前三个参数和上面的是一致的。 modelAndView:如果处理器执行完成具有返回结果可以读取到对应数据与页面信息并进行调整 因为咱们现在都是返回json数据所以该参数的使用率不高。 afterCompletion 拦截器最后执行的方法无论原始方法是否执行 public void afterCompletion(HttpServletRequest request,HttpServletResponse response,Object handler,Exception ex) throws Exception {System.out.println(afterCompletion); }前三个参数与上面的是一致的。 ex:如果处理器执行过程中出现异常对象可以针对异常情况进行单独处理 因为我们现在已经有全局异常处理器类所以该参数的使用率也不高。 这三个方法中最常用的是preHandle,在这个方法中可以通过返回值来决定是否要进行放行我们可以把业务逻辑放在该方法中如果满足业务则返回true放行不满足则返回false拦截。 9.4 拦截器链配置 preHandle与配置顺序相同必定运行 postHandle:与配置顺序相反可能不运行 afterCompletion:与配置顺序相反可能不运行。 这个顺序不太好记最终只需要把握住一个原则即可:以最终的运行结果为准
http://www.dnsts.com.cn/news/231594.html

相关文章:

  • 原型样网站小程序后端怎么开发
  • 一个简单的个人网站wordpress获取文章中的图片不显示
  • 网站开发工作怎样想做电商怎么找货源
  • 邢台网站开发销量不高的网站怎么做
  • 相城专业的网站建设做电影网站如何推广方案
  • 佛山狮山网站建设图片设计 五星级酒店网站
  • 宝安高端网站设计怎么样wordpress 超简洁主题
  • 重庆综合网站建设配件有赞官网
  • 成都网站制作哪家好wordpress文章中文版
  • 济南哪里有网站建设公司形象型网站
  • 什么叫网站规划免费ppt下载网
  • 做视频网站资源采集什么是营销型网站
  • 美容院网站源码商城网站建设招聘
  • 济南做网站建设公司诚信通网站怎么做
  • 南京做公司网站公司前端开发师
  • 莱芜温州网站优化价格
  • 中国站免费推广入口wordpress 开启注册
  • 诸城哪有做公司网站和的wordpress 极验
  • 做网站挣钱么经典网站赏析
  • 长沙县工程建设质监站网站清除网站黑链
  • 网站建设算不算固定资产厦门市建设工程造价网站首页
  • 企业营销型网站建设费用公司网页怎么关闭
  • 自己如何建设微信网站程序开发教程
  • 电子商务网站建设第二版论文建设一个网站需要哪些
  • 做网站怎么排版好看岳阳做网站 公司电话
  • 肥东网站制作1920的网站做字体大小
  • 智库建设网站方案厦门建筑人才网
  • 网站内容设计上的特色网站建设制作公司
  • 出口退税备案在哪个网站做wordpress好玩的主题
  • 查分网站制作网站色彩的搭配原则有哪些