SpringMVC总结

打印 上一主题 下一主题

主题 523|帖子 523|积分 1569

一.SpringMVC简介和体验

(一)先容




(二)创建SpringMVC项目

1.创建Maven项目

2.导入依赖

  1. <properties>
  2.     <spring.version>6.0.6</spring.version>
  3.     <servlet.api>9.1.0</servlet.api>
  4.     <maven.compiler.source>17</maven.compiler.source>
  5.     <maven.compiler.target>17</maven.compiler.target>
  6.     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  7. </properties>
  8. <dependencies>
  9.     <!-- springioc相关依赖  -->
  10.     <dependency>
  11.         <groupId>org.springframework</groupId>
  12.         <artifactId>spring-context</artifactId>
  13.         <version>${spring.version}</version>
  14.     </dependency>
  15.     <!-- web相关依赖  -->
  16.     <!-- 在 pom.xml 中引入 Jakarta EE Web API 的依赖 -->
  17.     <!--
  18.         在 Spring Web MVC 6 中,Servlet API 迁移到了 Jakarta EE API,因此在配置 DispatcherServlet 时需要使用
  19.          Jakarta EE 提供的相应类库和命名空间。错误信息 “‘org.springframework.web.servlet.DispatcherServlet’
  20.          is not assignable to ‘javax.servlet.Servlet,jakarta.servlet.Servlet’” 表明你使用了旧版本的
  21.          Servlet API,没有更新到 Jakarta EE 规范。
  22.     -->
  23.     <dependency>
  24.         <groupId>jakarta.platform</groupId>
  25.         <artifactId>jakarta.jakartaee-web-api</artifactId>
  26.         <version>${servlet.api}</version>
  27.         <scope>provided</scope>
  28.     </dependency>
  29.     <!-- springwebmvc相关依赖  -->
  30.     <dependency>
  31.         <groupId>org.springframework</groupId>
  32.         <artifactId>spring-webmvc</artifactId>
  33.         <version>${spring.version}</version>
  34.     </dependency>
  35. </dependencies>
复制代码
3.转成maven/web步调

用jbljavatoweb插件将Java项目转换为Web项目

webapp出现蓝点代表乐成转成maven/web步调

4.controller声明

handler就是controller内部的具体方法
  1. @Controller
  2. public class HelloController {
  3.     @RequestMapping("springmvc/hello")//对外访问的地址 到handlerMapping注册的注解
  4.     @ResponseBody//直接返回字符串给前端,不去找视图解析器
  5.     public String hello() {
  6.         System.out.println("hello,springmvc");
  7.         return "hello springmvc!";
  8.     }
  9. }
复制代码
5.Spring MVC焦点组件设置类

声明springmvc涉及组件信息的设置类
(1)controller设置ioc容器
(2).handlerMapping handlerAdapter加入到ioc容器
  1. @Configuration
  2. @ComponentScan("com.yan.controller")
  3. public class MvcConfig {
  4.     @Bean
  5.     public RequestMappingHandlerMapping handlerMapping() {
  6.         return new RequestMappingHandlerMapping();
  7.     }
  8.      @Bean
  9.     public RequestMappingHandlerAdapter handlerAdapter(){
  10.         return new RequestMappingHandlerAdapter();
  11.      }
  12. }
复制代码
6.SpringMVC情况搭建

本类可以被web项目加载,会初始化ioc容器,会设置dispatcherServlet的地址
  1. public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
  2.     /**
  3.      * 指定service / mapper层的配置类
  4.      */
  5.     @Override
  6.     protected Class<?>[] getRootConfigClasses() {
  7.         return null;
  8.     }
  9.     /**
  10.      * 指定springmvc的配置类
  11.      *
  12.      * @return
  13.      */
  14.     @Override
  15.     protected Class<?>[] getServletConfigClasses() {
  16.         return new Class<?>[]{MvcConfig.class};
  17.     }
  18.     /**
  19.      * 设置dispatcherServlet的处理路径!
  20.      * 一般情况下为 / 代表处理所有请求!
  21.      */
  22.     @Override
  23.     protected String[] getServletMappings() {
  24.         return new String[]{"/"};
  25.     }
  26. }
复制代码
 7将攻击部署在tomcat上,启动测试



(三)WebApplicationInitializer接口

每当web项目启动,就会自动调用WebApplicationInitializer接口的onStartup方法AbstractAnnotationConfigDispatcherServletInitializer, 用于 DispatcherServlet 初始化 (实现了WebApplicationInitializer接口),该基类既要完成 WebApplicationInitializer 接口中设置servlet容器的功能,又完成了设置MVC的功能,即同时设置了 DispatcherServlet 和 ContextLoaderListener 。
二.SpringMVC接收数据

(一)访问路径设置

@RequestMapping注解的作用就是将哀求的 URL 地址和处理哀求的方式(handler方法)关联起来,建立映射关系。
@WebServlet:必须用/开头
@RequestMapping:不必利用/开头
1.精准路径

精准地址可以有一个或多个
@RequestMapping(value = {"/user/register"})
2.含糊路径

/* 为单层恣意字符串,只能匹配URL地址中的一层,如果想正确匹配两层,那么就写“/*/*”以此类推
/** 为恣意层恣意字符串,可以匹配URL地址中的多层。
3.类和方法级别区别

(1)设置到类级别

类上提取通用的访问地址
(2)设置到方法级别

方法上是具体的handler地址
访问:类地址+方法地址即可
4.哀求方式指定

  1. public enum RequestMethod {
  2.   GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE
  3. }
复制代码
(1)默认情况下:

@RequestMapping("/logout")
只要地址精确,任何哀求方式都可以访问!
(2)指定哀求方式下:

@RequestMapping(value = "regist", method = {RequestMethod.GET})
不符合哀求方式会出现405异常
5.进阶注解

@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping
进阶注解只能添加到handler方法上,无法添加到类上!
(二)接收参数

1.param和json参数比较

(1)参数编码

param类型:ASCII码
JSON类型:UTF-8
(2)参数顺序

param参数没有顺序限定,但是JSON类型的参数是有序的,JSON采用键值对形式
(3)数据类型

param类型的参数支持字符串类型,数组类型和布尔类型等简单类型
JSON适合更复杂的格式
(4)可读性

param类型参数更简单,易读但是JSON格式在传递嵌套数据结构更勤洗易懂
2.param参数接收

(1)直接接值

-形参列表填写对应名称的参数即可 哀求参数名=形参参数名
-可以不给形参传递值
  1.   @RequestMapping("data")
  2.     @ResponseBody
  3.     public String data(String name, int age) {
  4.         System.out.println("name=" + name + ",age" + age);
  5.         return "name = " + name + "age = " + age;
  6.     }
复制代码
(2)@RequestParam注解

可以利用 @RequestParam 解释将 Servlet 哀求参数(即查询参数或表单数据)绑定到控制器中的方法参数。
@RequestParam(
value="指定哀求参数,如果形参名和参数名同等,可以省略",
required=false 前端是否必须传递,默认为必须,不传报错400异常,
defaultValue="1" 当非必须传递时,可以设置默认值)
利用场景
-指定绑定的哀求参数名
-要求哀求参数必须传递
-为哀求参数提供默认值
  1.   @RequestMapping("data1")
  2.     @ResponseBody
  3.     public String data1(@RequestParam(value = "account") String username, @RequestParam(required = false, defaultValue = "1") int page) {
  4.         return "username = " + username + " , page= " + page;
  5.     }
复制代码
(3)特殊场景接值

-一名多值

多选框,提交的数据的时间一个key对应多个值,我们可以利用集合进行接收!
不加@RequestParam将hbs对应的一个字符赋值给集合,类型异常
加了注解,HandlerAdapter用集合的add方法加入字符串
  1.    //hbs=吃&hbs=玩&hbs=打
  2.     @GetMapping("data2")
  3.     @ResponseBody
  4.     public String data2(@RequestParam List<String> hbs) {
  5.         System.out.println("hbs=" + hbs);
  6.         return "ok";
  7.     }
复制代码
-实体接收

可以在方法内部直接利用对象的属性来访问哀求参数,要求声明实体类属性名等于哀求参数名
  1. @Data
  2. public class User {
  3.     private String name;
  4.     private int age;
  5. }
复制代码
  1.   @RequestMapping("data3")
  2.     @ResponseBody
  3.     public String data3(User user) {
  4.         System.out.println(user);
  5.         return user.toString();
  6.     }
复制代码

3.路径参数接收

Spring MVC 框架提供了 @PathVariable 注解来处理路径传递参数。
@PathVariable 注解允许将 URL 中的占位符映射到控制器方法中的参数
-动态路径设计: /user/{动态部分}/{动态部分} 动态部分利用{}包罗即可! {}内部动态标识!
-形参列表取值:
@PathVariable Long id 如果形参名 = {动态标识} 自动赋值!
@PathVariable("动态标识") Long id 如果形参名 != {动态标识} 可以通过指定动态标识赋值!
  1. @RequestMapping("{account}/{password}")
  2.     public String login(@PathVariable(value = "account", required = true) String username, @PathVariable String password) {
  3.         return username+password;
  4.     }
复制代码
4.JSON参数接收

界说一个用于接收 JSON 数据的 Java 类
  1. @Data
  2. public class Person {
  3.     private String name;
  4.     private int age;
  5.     private String gender;
  6. }
复制代码
在控制器中,利用 @RequestBody 注解来接收 JSON 数据,并将其转换为 Java 对象,例如:
  1. @RequestMapping("json")
  2. @Controller
  3. @ResponseBody
  4. public class JsonController {
  5.     @PostMapping("data")
  6.     public String data(@RequestBody Person person) {
  7.         System.out.println("person:" + person);
  8.         return person.toString();
  9.     }
  10. }
复制代码
 测试:

原因: 
Java原生的api只支持路径参数和param参数,不支持json
 json是前端格式
解决:导入json处理的依赖  ,handlerAdopter设置json转换器
 利用此注解@EnableWebMvc加入json处理器
  1. @EnableWebMvc //handlerAdopter 配置json转换器
  2. @Configuration
  3. @ComponentScan("com.yan")
  4. public class MvcConfig {
  5.     @Bean
  6.     public RequestMappingHandlerMapping handlerMapping() {
  7.         return new RequestMappingHandlerMapping();
  8.     }
  9.      @Bean
  10.     public RequestMappingHandlerAdapter handlerAdapter(){
  11.         return new RequestMappingHandlerAdapter();
  12.      }
  13. }
复制代码
pom.xml 加入jackson依赖
  1. <dependency>
  2.     <groupId>com.fasterxml.jackson.core</groupId>
  3.     <artifactId>jackson-databind</artifactId>
  4.     <version>2.15.0</version>
  5. </dependency>
复制代码
@EnableWebMvc注解说明
添加 HandlerMapping,HandlerAdapter,给HandlerAdapter添加jacksonJson处理器
(三)接收Cookie数据

  1. @Controller
  2. @RequestMapping("cookie")
  3. @ResponseBody
  4. public class CookieController {
  5.     @RequestMapping("data")
  6.     public String data(@CookieValue(value = "cookieName") String value) {
  7.         System.out.println("value=" + value);
  8.         return value;
  9.     }
  10.     @GetMapping("save")
  11.     public String save(HttpServletResponse response){
  12.         Cookie cookie = new Cookie("cookieName", "root");
  13.         response.addCookie(cookie);
  14.         return "ok";
  15.     }
  16. }
复制代码
(四)接收哀求头数据

  1. @Controller
  2. @RequestMapping("header")
  3. @ResponseBody
  4. public class HeaderController {
  5.     @GetMapping("data")
  6.     public String data(@RequestHeader("Host") String host) {
  7.         System.out.println("host = " + host);
  8.         return "host = " + host;
  9.     }
  10. }
复制代码
(五)原生Api对象获取

想要获取哀求或者相应对象,或者会话等,可以直接在形参列表传入,而且不分先后顺序!
举例:
获取ServletContext和HttpSession对象
  1. @GetMapping("api")
  2.     public  String getApi(HttpServletRequest req, HttpServletResponse resp, HttpSession httpSession){
  3.         ServletContext servletContext = req.getServletContext();
  4.         return null;
  5.     }
复制代码
 springmvc会在初始化容器的时间,将servletContext对象存储到ioc容器中,也可以用@Autowired注解自动装配servletContext对象
三.SpringMVC相应数据

(一)转发和重定向

1.方法的返回值都是String
2.不加responseBody注解
3.返回字符串前面 redirect:/重定向地址 forward:/哀求转发地址
转发和重定向都一样,都是项目下路径!转到项目内的资源都不需要添加项目根路径!
转发只能向项目下的资源跳转
重定向可以是项目下的资源也可以是项目外的资源,重定向到项目外的资源如百度网址
redirect:http://www.baidu.com
  1. @GetMapping("forward")
  2.     public String forward() {
  3.         return "forward:/index";
  4.     }
  5.     @GetMapping("redirect")
  6.     public String redirect() {
  7.         return "redirect:/jsp/index";
  8.     }
复制代码

(二)返回JSON数据


1.前置准备

(1)导入json依赖
(2)@EnableWebMvc
2.@ResponseBody注解

@RestController =@Controller + @ResponseBody
以是利用了 @RestController 注解就相当于给类中的每个方法都加了 @ResponseBody 注解。
如果类中每个方法上都标志了 @ResponseBody 注解,那么这些注解就可以提取到类上。
  1. @RequestMapping("json")
  2. @Controller
  3. @ResponseBody
  4. public class JsonController {
  5.     @GetMapping("data")
  6.     public User data() {
  7.         User user = new User();
  8.         user.setName("two dogs");
  9.         user.setAge(2);
  10.         return user;
  11.     }
  12.     @GetMapping("data2")
  13.     public List<User> data1() {
  14.         User user = new User();
  15.         user.setName("two dogs");
  16.         user.setAge(2);
  17.         List<User> users = new ArrayList<>();
  18.         users.add(user);
  19.         return users;
  20.     }
  21. }
复制代码
(三)返回静态资源

1.原因

对 SpringMVC 来说,必须有对应的 @RequestMapping 才气找随处理哀求的方法
现在 images/photo.jpg 哀求没有对应的 @RequestMapping 以是返回 404


2.问题解决

在 SpringMVC 设置设置类:
  1. @EnableWebMvc // 配置json转换器
  2. @Configuration
  3. @ComponentScan("com.yan")
  4. public class MvcConfig implements WebMvcConfigurer {
  5.     //开启静态资源处理
  6.     @Override
  7.     public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
  8.         configurer.enable();
  9.     }
  10. }
复制代码
四.RESTful风格设计和实践

(一)哀求方式要求

GET用来获取资源
POST用来新建资源(也可以用于更新资源)
PUT用来更新资源
DELETE用来删除资源
(二)路径要求

利用URL+哀求方式确定具体的动作,handler的地址+哀求方式都相同才是重复


通用原则:
- 路径参数应该用于指定资源的唯一标识或者 ID,而哀求参数应该用于指定查询条件或者操作参数。
- 哀求参数应该限定在 10 个以内,过多的哀求参数可能导致接口难以维护和利用。
- 对于敏感信息,最好利用 POST 和哀求体来传递参数。
(三)接口设计


为什么查询用户详情,就利用路径传递参数,多条件含糊查询,就利用哀求参数传递?
-对于查询用户详情,利用路径传递参数是因为这是一个单一资源的查询,即查询一条用户记录。利用路径参数可以明确指定所哀求的资源,便于服务器定位并返回对应的资源,也符合 RESTful 风格的要求。
-而对于多条件含糊查询,利用哀求参数传递参数是因为这是一个资源集合的查询,即查询多条用户记录。利用哀求参数可以通过组合差别参数来限定查询结果,路径参数的组合和分列可能会很多,不如利用哀求参数更加灵活和简便。
(四)代码实现

  1. public class User {
  2.     private Integer id;
  3.     private String name;
  4.     private Integer age;
  5. }
复制代码
  1. @RestController
  2. @RequestMapping("user")
  3. public class UserController {
  4.     @GetMapping
  5.     public List<User> page(@RequestParam(required = false, defaultValue = "1") int page,
  6.                            @RequestParam(required = false, defaultValue = "10") int size) {
  7.         System.out.println("page=" + page + "size" + size);
  8.         return null;
  9.     }
  10.     @PostMapping
  11.     public User save(@RequestBody User user) {
  12.         return user;
  13.     }
  14.     @GetMapping("{id}")
  15.     public User detail(@PathVariable Integer id) {
  16.         return null;
  17.     }
  18.     @PutMapping
  19.     public User update(@RequestBody User user) {
  20.         return user;
  21.     }
  22.     @DeleteMapping("{id}")
  23.     public User delete(@PathVariable Integer id) {
  24.         return null;
  25.     }
  26.     @GetMapping("search")
  27.     public List<User> search(String keywork,
  28.                              @RequestParam(required = false, defaultValue = "1") int page,
  29.                              @RequestParam(required = false, defaultValue = "10") int size) {
  30.         return null;
  31.     }
  32. }
复制代码
 五.SpringMVC扩展

(一)全局异常处理机制

声明式异常处理:则是将异常处理的逻辑从具体的业务逻辑中分离出来,通过设置等方式进行统一的管理和处理。
1.声明异常处理控制器类

@ControllerAdvice 代表当前类的异常处理controller!
@RestControllerAdvice=@ControllerAdvice+@ResponseBody
2.声明异常处理hander方法

@ExceptionHandler(HttpMessageNotReadableException.class)
 该注解标志异常处理Handler,而且指定发生异常调用该方法!指定的异常,可以精准查找,找不到才查找父异常
3.设置文件扫描控制器类设置

确保异常处理控制类被扫描
  1. @RestControllerAdvice
  2. public class GlobalExceptionHandler {
  3.     @ExceptionHandler(ArithmeticException.class)
  4.     public Object ArithmeticExceptionHandler(ArithmeticException e) {
  5.         //自定义处理异常即可
  6.         String message = e.getMessage();
  7.         return message;
  8.     }
  9.     @ExceptionHandler(Exception.class)
  10.     public Object ExceptionHandler(Exception e) {
  11.         String message = e.getMessage();
  12.         return message;
  13.     }
  14. }
复制代码
(二)拦截器的利用

1.创建拦截器类

  1. public class MyInterceptor implements HandlerInterceptor {
  2.     /**
  3.      * 执行handeler之前,调用的拦截方法
  4.      *
  5.      * @param request
  6.      * @param response
  7.      * @param handler  我们要调用的方法的对象
  8.      * @return
  9.      * @throws Exception
  10.      */
  11.     @Override
  12.     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  13.         return true;
  14.     }
  15.     /**
  16.      * 当handler执行完毕后触发的方法,没有拦截机制
  17.      *
  18.      * @param request
  19.      * @param response
  20.      * @param handler
  21.      * @param modelAndView 返回的视图和共享域数据对象
  22.      * @throws Exception
  23.      */
  24.     @Override
  25.     public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
  26.     }
  27.     /**
  28.      * 整体处理完毕
  29.      *
  30.      * @param request
  31.      * @param response
  32.      * @param handler
  33.      * @param ex       报错了异常对象
  34.      * @throws Exception
  35.      */
  36.     @Override
  37.     public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
  38.     }
  39. }
复制代码
2.修改设置类添加拦截器

  1. @Configuration
  2. @ComponentScan("com.yan")
  3. @EnableWebMvc
  4. public class MvcConfig implements WebMvcConfigurer {
  5.     //添加拦截器
  6.     @Override
  7.     public void addInterceptors(InterceptorRegistry registry) {
  8.         registry.addInterceptor(new MyInterceptor());
  9.     }
  10. }
复制代码
3.设置详解

a.默认拦截全部

  1. @Configuration
  2. @ComponentScan("com.yan")
  3. @EnableWebMvc
  4. public class MvcConfig implements WebMvcConfigurer {
  5.     @Override
  6.     public void addInterceptors(InterceptorRegistry registry) {
  7.         registry.addInterceptor(new MyInterceptor());
  8.                
  9.     }
  10. }
复制代码
b.精准设置

设置拦截器处理指定哀求 路径可以设置一个或者多个,为项目下路径即可
也支持 /* 和 /** 含糊路径。 * 恣意一层字符串 ** 恣意层 恣意字符串
  1. @Configuration
  2. @ComponentScan("com.yan")
  3. @EnableWebMvc
  4. public class MvcConfig implements WebMvcConfigurer {
  5.     @Override
  6.     public void addInterceptors(InterceptorRegistry registry) {
  7.         registry.addInterceptor(new MyInterceptor())
  8.                 .addPathPatterns("/user/data");
  9.     }
  10. }
复制代码
c.排除设置

排除匹配,排除应该在匹配的范围内排除
  1. @Configuration
  2. @ComponentScan("com.yan")
  3. @EnableWebMvc
  4. public class MvcConfig implements WebMvcConfigurer {
  5.     @Override
  6.     public void addInterceptors(InterceptorRegistry registry) {
  7.         registry.addInterceptor(new MyInterceptor())
  8.                 .addPathPatterns("/user/**").excludePathPatterns("/user/data");
  9.     }
  10. }
复制代码
4.多个拦截器的执行顺序


preHandle() 方法:SpringMVC 会把所有拦截器收集到一起,然后按照设置顺序调用各个 preHandle() 方法。
postHandle() 方法:SpringMVC 会把所有拦截器收集到一起,然后按照设置相反的顺序调用各个 postHandle() 方法。
afterCompletion() 方法:SpringMVC 会把所有拦截器收集到一起,然后按照设置相反的顺序调用各个 afterCompletion() 方法。
(三)参数校验

1.导入依赖

  1. <!-- 校验注解 -->
  2. <dependency>
  3.     <groupId>jakarta.platform</groupId>
  4.     <artifactId>jakarta.jakartaee-web-api</artifactId>
  5.     <version>9.1.0</version>
  6.     <scope>provided</scope>
  7. </dependency>
  8.         
  9. <!-- 校验注解实现-->        
  10. <!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator -->
  11. <dependency>
  12.     <groupId>org.hibernate.validator</groupId>
  13.     <artifactId>hibernate-validator</artifactId>
  14.     <version>8.0.0.Final</version>
  15. </dependency>
  16. <!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator-annotation-processor -->
  17. <dependency>
  18.     <groupId>org.hibernate.validator</groupId>
  19.     <artifactId>hibernate-validator-annotation-processor</artifactId>
  20.     <version>8.0.0.Final</version>
  21. </dependency>
复制代码
2.创建实体类,为其添加校验对象

要求:
(1)name 不为null且不为空 字符串
@NotBlank 集合 @NotEmpty 包装 @NotNull
(2)password长度大于6
(3)age必须>=1
(4)email 邮箱格式字符串
(5)birthday 过去时间
  1. public class User {
  2.     @NotBlank
  3.     private String name;
  4.     @Length(min = 6)//标注值字符串大小必须在指定的范围内
  5.     private String password;
  6.     @Min(1) //标注值必须大于或等于 value
  7.     private int age;
  8.     @Email //标注值必须是格式正确的 Email 地址
  9.     private String email;
  10.     @Past //标注值只能用于日期型,且必须是过去的日期
  11.     private Date birthday;
  12. }
复制代码
3.handler(@Validated 实体类 对象)

要接收json,还要加上@RequestBody 注解
  1. @RequestMapping("user")
  2. @RestController
  3. public class UserController {
  4.     @GetMapping
  5.     public User regist(@Validated @RequestBody User user){
  6.     return  user;
  7.     }
  8. }
复制代码
测试:

4.handler标志和绑定错误收集

捕捉绑定错误信息
在实体类参数和 BindingResult 之间不能有任何其他参数
BindingResult可以接受错误信息,避免信息抛出
  1.     @GetMapping
  2.     public Object regist(@Validated @RequestBody User user, BindingResult result) {
  3.         if (result.hasErrors()) {
  4.             Map data = new HashMap();
  5.             data.put("code", 400);
  6.             data.put("msg", "参数校验异常");
  7.             return data;
  8.         }
  9.         return user;
  10.     }
复制代码
测试:


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

前进之路

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表