【Maven从入门到实战教程】第八章 Maven项目拆分、继承、聚合,配套Maven综合案例

末蓝、 2024-04-28 13:18 182阅读 0赞

一、Maven项目拆分、继承、聚合

1.1 传统项目开发的问题

使用Java技术开发的工程项目,无论是数据处理系统还是Web网站,随着项目的不断发展,需求的不断细化与添加,工程项目中的代码越来越多,包结构也越来越复杂这时候工程的进展就会遇到各种问题:

1、传统的项目中,会将全部的业务都写到一个项目中,用一台Web应用服务器来部署。此时如果某一个业务操作造成服务器宕机,则全部的项目业务都不能访问。

2、不同方面的代码之间相互耦合,这时候一系统出现问题很难定位到问题的出现原因,即使定位到问题也很难修正问题,可能在修正问题的时候引入更多的问题。

3、多方面的代码集中在一个整体结构中,新入的开发者很难对整体项目有直观的感受,增加了新手介入开发的成本,需要有一个熟悉整个项目的开发者维护整个项目的结构(通常在项目较大且开发时间较长时这是很难做到的)。

4、开发者对自己或者他人负责的代码边界很模糊,这是复杂项目中最容易遇到的,导致的结果就是开发者很容易修改了他人负责的代码且代码负责人还不知道,责任追踪很麻烦。

将一个复杂项目拆分成多个模块是解决上述问题的一个重要方法,多模块的划分可以降低代码之间的耦合性(从类级别的耦合提升到jar包级别的耦合),每个模块都可以是自解释的(通过模块名或者模块文档),模块还规范了代码边界的划分,开发者很容易通过模块确定自己所负责的内容。

1.2 Maven项目拆分

不知你有没有想过,一个好好的maven工程为什么要进行拆分呢?面对当今互联网+的行业,软件项目变得越来越庞大,复杂程度越来越高,这大大地提高了开发与管理的成本。而工程的拆分可以实现分模块开发与测试,亦可实现多线程开发与管理,在提高工程代码复用度的同时也提高了软件的开发速度与效率。

例如,一个完整的早期开发好的crm项目,现在要使用maven工程对它进行拆分,这时候就可以将dao层拆解出来,形成一个独立的工程,同样service层以及web层也都进行这样的拆分。

概念讲解和配图原文链接:https://blog.csdn.net/yerenyuan\_pku/article/details/103680220

watermark_type_d3F5LXplbmhlaQ_shadow_50_text_Q1NETiBA5oiR5piv5rOi5ZOp5Liq5rOi_size_15_color_FFFFFF_t_70_g_se_x_16

理解工程拆分的好处:

watermark_type_d3F5LXplbmhlaQ_shadow_50_text_Q1NETiBA5oiR5piv5rOi5ZOp5Liq5rOi_size_20_color_FFFFFF_t_70_g_se_x_16

从上图可以看出,出现了一个问题,如果crm项目中dao层一旦出现bug需要修复时,erp项目与oa项目中的dao层也要做相应的修改,像这样重复的事情需要做三遍!实在是不可取,那怎么解决呢?

这时,就可以将crm项目中的dao层拆解出来了,并形成一个独立的工程,然后每个项目都来复用这个独立的工程。

watermark_type_d3F5LXplbmhlaQ_shadow_50_text_Q1NETiBA5oiR5piv5rOi5ZOp5Liq5rOi_size_15_color_FFFFFF_t_70_g_se_x_16 1

把工程拆分成一个个独立的工程,将来要用到的时候就把它们的坐标给引进来就行了,这就有点类似于搭积木一样。

watermark_type_d3F5LXplbmhlaQ_shadow_50_text_Q1NETiBA5oiR5piv5rOi5ZOp5Liq5rOi_size_16_color_FFFFFF_t_70_g_se_x_16

把积木搭建成各种项目:

watermark_type_d3F5LXplbmhlaQ_shadow_50_text_Q1NETiBA5oiR5piv5rOi5ZOp5Liq5rOi_size_16_color_FFFFFF_t_70_g_se_x_16 1

对于一个大型的项目,如果我们直接作为一个工程开发,由于相互之间的依赖我们只能从头到尾由一组人开发,否则就会出现一个类好多人开发,相互更改的混乱局面,这个时候我们就将项目进行了横向和纵向的拆分。

所谓的横向的拆分就是我们平常说的三层架构,将项目分成了web层,service层、dao层(web层也被叫做表现层,service层也被叫做业务层,dao层也被持久层),可以理解为将一个功能模块的不同调用过程进行了水平方向的拆分。

所谓的纵向拆分就是将一个项目的多个功能模块进行了拆分,横向拆分后,每个功能模块进行了单独的开发之后,项目整合的时候就需要有一个能够整合这些项目或者模块的工程,这就是所谓聚合工程的意义。

1.3 Maven项目聚合

项目开发通常是分组分模块开发的,每个模块开发完成后,要运行整个工程需要将每个模块聚合在一起运行,比如,dao、service以及web这三个工程最终会打一个独立的war包运行。

就拿一个完整的早期开发好的crm项目来说,把crm项目拆成多个子模块后,独立运行各个模块是无法完成软件项目的要求的,只有把它们都整合起来,分工合作才能完成工作。因此需要父工程来管理各个子模块,把它们聚合在一起运行,即把crm_dao、crm_service以及crm_web这三个工程打成一个独立的可运行的war包。

watermark_type_d3F5LXplbmhlaQ_shadow_50_text_Q1NETiBA5oiR5piv5rOi5ZOp5Liq5rOi_size_16_color_FFFFFF_t_70_g_se_x_16 2

这有点像把汽车的各个零部件组装起来,变成一辆可以行驶的车。以下是一堆的汽车各个零部件。

watermark_type_d3F5LXplbmhlaQ_shadow_50_text_Q1NETiBA5oiR5piv5rOi5ZOp5Liq5rOi_size_15_color_FFFFFF_t_70_g_se_x_16 2

将汽车的各个零部件组装起来,可以变成一辆可以行驶的车。

watermark_type_d3F5LXplbmhlaQ_shadow_50_text_Q1NETiBA5oiR5piv5rOi5ZOp5Liq5rOi_size_16_color_FFFFFF_t_70_g_se_x_16 3

建立聚合工程需要注意:
1、该聚合项目本身也做为一个Maven项目,它必须有自己的POM。
2、它的打包方式必须为:pom。
3、引入了新的元素:modules—-module。
4、版本:聚合模块的版本和被聚合模块版本一致。
5、relativePath:每个module的值都是一个当前POM的相对目录。
指定查找该父项目pom.xml的(相对)路径。默认顺序:relativePath > 本地仓库 > 远程仓库。
没有relativePath标签等同…/pom.xml, 即默认从当前pom文件的上一级目录找。
6、目录名称:为了方便的快速定位内容,模块所处的目录应当与其artifactId一致(Maven约定而不是硬性要求),总之,模块所处的目录必须和模块所处的目录相一致。
7、聚合模块减少的内容:聚合模块的内容仅仅是一个pom.xml文件,它不包含src/main/Java、src/test/java等目录,因为它只是用来帮助其它模块构建的工具,本身并没有实质的内容。
8、聚合模块和子模块的目录:他们可以是父子类,也可以是平行结构,当然如果使用平行结构,那么聚合模块的POM也需要做出相应的更改。
9、如果聚合项目的子模块新建完成后进行了删除操作,一定要在聚合项目中pom.xml中的modules选项卡中将这个子模块进行删除

  1. <modules>
  2. <module>../maven-util</module>
  3. <module>../maven-entity</module>
  4. <module>../maven-dao</module>
  5. <module>../maven-service</module>
  6. <module>../maven-web</module>
  7. </modules>

总结:

  1. 对于聚合模块来说,它知道有哪些被聚合的模块,而对于被聚合的模块来说,它们不知道被谁聚合了,也不知道它的存在
  2. 对于继承关系的父POM来说,它不知道自己被哪些子模块继承了,对于子POM来说,它必须知道自己的父POM是谁
  3. 在一些最佳实践中我们会发现:一个POM既是聚合POM,又是父POM,这么做主要是为了方便。

1.4 Maven项目的继承

类似Java中类的继承,都是为了消除重复。子类继承父类,父类里有的方法和属性在子类中就不需要再定义和实现了,使用的时候直接调用父类的就可以了。我们把crm项目拆分后,将会有一个父工程(例如crm)和若干个子工程(例如crm_dao、crm_service、crm_web),子工程中要用到的依赖都可以在父工程的pom.xml文件中进行依赖管理,将来子工程在开发的时候就不需要再定义版本了,这样做的目的是为了方便管理。

watermark_type_d3F5LXplbmhlaQ_shadow_50_text_Q1NETiBA5oiR5piv5rOi5ZOp5Liq5rOi_size_16_color_FFFFFF_t_70_g_se_x_16 4

继承除了能够避免重复,还有一个好处就是让项目更加安全。

继承时需要注意:
1、说到继承肯定是一个父子结构,那么我们在聚合项目中来创建一个parent project。
2、: 作为父模块的POM,其打包类型也必须为POM。
3、结构:父模块只是为了帮助我们消除重复,所以它也不需要src/main/java、src/test/java等目录。
4、新的元素:,它是被用在子模块中的。
5.元素的属性:: 表示父模块POM的相对路径,在构建的时候,Maven会先根据relativePath检查父POM,如果找不到,再从本地仓库查找。
6、relativePath的默认值:../pom.xml。
7、子模块省略groupId和version: 使用了继承的子模块中可以不声明groupId和version, 子模块将隐式的继承父模块的这两个元素。

二、Maven综合案例

对于一个大型的项目,大部分情况下都是将一个项目的多个功能模块进行了拆分,拆分后,每个功能模块进行了单独的开发之后,项目整合的时候就需要有一个能够整合这些项目或者模块的工程,这就是所谓聚合工程的意义。

我们现在不写那么多模块,把一个学生模块按照类的功能(三层架构)拆分成一个个模块,然后来体会项目的拆分、继承、聚合。

watermark_type_d3F5LXplbmhlaQ_shadow_50_text_Q1NETiBA5oiR5piv5rOi5ZOp5Liq5rOi_size_16_color_FFFFFF_t_70_g_se_x_16 5

2.1 搭建项目结构

2.1.1 创建管理父项目

1、创建管理父项目(maven-student),Maven管理类项目的打包方式为pom。管理类父项目只是用来帮助其它模块构建的工具,本身并没有实质的内容,可以删除src目录。

watermark_type_d3F5LXplbmhlaQ_shadow_50_text_Q1NETiBA5oiR5piv5rOi5ZOp5Liq5rOi_size_16_color_FFFFFF_t_70_g_se_x_16 6

2、创建完成之后,做如下配置:
2.1 修改项目打包方式为pom。
2.2 依赖版本管理。
2.3 依赖管理

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <modelVersion>4.0.0</modelVersion>
  6. <groupId>com.maven</groupId>
  7. <artifactId>maven-student</artifactId>
  8. <version>1.0-SNAPSHOT</version>
  9. <!-- 1、修改为管理项目,packaging为pom -->
  10. <packaging>pom</packaging>
  11. <!-- 2、依赖版本管理:对项目中所有的依赖的版本进行管理 -->
  12. <properties>
  13. <!-- properties里面可以定义自定义标签 -->
  14. <servlet.version>3.0.1</servlet.version>
  15. <jsp.version>2.1</jsp.version>
  16. <jstl.version>1.2</jstl.version>
  17. <junit.version>4.12</junit.version>
  18. <mysql.version>5.1.49</mysql.version>
  19. </properties>
  20. <!-- 3、依赖管理:对项目中所有的依赖进行管理 -->
  21. <!-- 依赖管理不会引入依赖 -->
  22. <dependencyManagement>
  23. <dependencies>
  24. <dependency>
  25. <groupId>javax.servlet</groupId>
  26. <artifactId>javax.servlet-api</artifactId>
  27. <!-- maven表达式获取版本 -->
  28. <version>${servlet.version}</version>
  29. <scope>provided</scope>
  30. </dependency>
  31. <dependency>
  32. <groupId>javax.servlet.jsp</groupId>
  33. <artifactId>jsp-api</artifactId>
  34. <version>${jsp.version}</version>
  35. <scope>provided</scope>
  36. </dependency>
  37. <dependency>
  38. <groupId>javax.servlet</groupId>
  39. <artifactId>jstl</artifactId>
  40. <version>${jstl.version}</version>
  41. </dependency>
  42. <dependency>
  43. <groupId>junit</groupId>
  44. <artifactId>junit</artifactId>
  45. <version>${junit.version}</version>
  46. <scope>test</scope>
  47. </dependency>
  48. <dependency>
  49. <groupId>mysql</groupId>
  50. <artifactId>mysql-connector-java</artifactId>
  51. <version>${mysql.version}</version>
  52. </dependency>
  53. </dependencies>
  54. </dependencyManagement>
  55. </project>

2.1.2 创建工具类子项目

1、创建工具类子项目(maven-student-util)。

注意:创建子项目的时候,创建的是一个Module。父项目右键 -> New -> Module,Module也是Maven项目。

watermark_type_d3F5LXplbmhlaQ_shadow_50_text_Q1NETiBA5oiR5piv5rOi5ZOp5Liq5rOi_size_16_color_FFFFFF_t_70_g_se_x_16 7

2、maven-student-util子模块的pom.xml文件里面会自动添加parent标签,标识当前模块的父项目或父模块。并且父项目的pom.xml文件中会自动添加modules,标识聚合的模块。

  1. <!-- Maven继承:子模块中的parent表示当前模块的父项目或父模块 -->
  2. <parent>
  3. <artifactId>maven-student</artifactId>
  4. <groupId>com.maven</groupId>
  5. <version>1.0-SNAPSHOT</version>
  6. </parent>
  7. <!-- 父项目管理的子模块列表:聚合 -->
  8. <modules>
  9. <module>maven-student-util</module>
  10. <module>maven-student-entity</module>
  11. </modules>

3、引入当前模块需要用到的依赖。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <!-- Maven继承:子模块中的parent表示当前模块的父项目或父模块 -->
  6. <parent>
  7. <artifactId>maven-student</artifactId>
  8. <groupId>com.maven</groupId>
  9. <version>1.0-SNAPSHOT</version>
  10. </parent>
  11. <modelVersion>4.0.0</modelVersion>
  12. <!-- 子模块可以继承groupId和version等,但是artifactId不能继承 -->
  13. <artifactId>maven-student-util</artifactId>
  14. <!-- 配置当前模块的依赖 -->
  15. <dependencies>
  16. <!-- 子模块中配置依赖无需再配置版本,都是参照父项目的版本号 -->
  17. <dependency>
  18. <groupId>mysql</groupId>
  19. <artifactId>mysql-connector-java</artifactId>
  20. </dependency>
  21. <dependency>
  22. <groupId>junit</groupId>
  23. <artifactId>junit</artifactId>
  24. <scope>test</scope>
  25. </dependency>
  26. </dependencies>
  27. </project>

2.1.3 创建实体类子项目

创建实体类子项目(maven-student-entity),并引入当前模块需要用到的依赖。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <parent>
  6. <artifactId>maven-student</artifactId>
  7. <groupId>com.maven</groupId>
  8. <version>1.0-SNAPSHOT</version>
  9. </parent>
  10. <modelVersion>4.0.0</modelVersion>
  11. <artifactId>maven-student-entity</artifactId>
  12. <!-- 配置当前模块的依赖 -->
  13. <dependencies>
  14. <dependency>
  15. <groupId>junit</groupId>
  16. <artifactId>junit</artifactId>
  17. <scope>test</scope>
  18. </dependency>
  19. </dependencies>
  20. </project>

2.1.4 创建持久层子项目

创建持久层子项目(maven-student-dao),并引入当前模块需要用到的依赖。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <parent>
  6. <artifactId>maven-student</artifactId>
  7. <groupId>com.maven</groupId>
  8. <version>1.0-SNAPSHOT</version>
  9. </parent>
  10. <modelVersion>4.0.0</modelVersion>
  11. <artifactId>maven-student-dao</artifactId>
  12. <!-- 配置当前模块的依赖 -->
  13. <dependencies>
  14. <dependency>
  15. <groupId>junit</groupId>
  16. <artifactId>junit</artifactId>
  17. <scope>test</scope>
  18. </dependency>
  19. <!-- 持久层需要用到工具类,那么就需要引入自己的模块,无需安装 -->
  20. <!-- 这里没有定义版本,是因为在父项目中对自己的模块进行依赖管理了 -->
  21. <dependency>
  22. <groupId>com.maven</groupId>
  23. <artifactId>maven-student-util</artifactId>
  24. </dependency>
  25. <dependency>
  26. <groupId>com.maven</groupId>
  27. <artifactId>maven-student-entity</artifactId>
  28. </dependency>
  29. </dependencies>
  30. </project>

在父项目中对自己的模块进行依赖管理,子模块中就不需要定义版本。

  1. <dependency>
  2. <groupId>com.maven</groupId>
  3. <artifactId>maven-student-util</artifactId>
  4. <!-- Maven表达式中的对象project -->
  5. <version>${project.version}</version>
  6. </dependency>
  7. <dependency>
  8. <groupId>com.maven</groupId>
  9. <artifactId>maven-student-entity</artifactId>
  10. <version>${project.version}</version>
  11. </dependency>

2.1.5 创建业务层子项目

创建业务层子项目(maven-student-service),并引入当前模块需要用到的依赖。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <parent>
  6. <artifactId>maven-student</artifactId>
  7. <groupId>com.maven</groupId>
  8. <version>1.0-SNAPSHOT</version>
  9. </parent>
  10. <modelVersion>4.0.0</modelVersion>
  11. <artifactId>maven-student-service</artifactId>
  12. <dependencies>
  13. <dependency>
  14. <groupId>com.maven</groupId>
  15. <artifactId>maven-student-dao</artifactId>
  16. </dependency>
  17. </dependencies>
  18. </project>

2.1.6 创建表现层子项目

1、创建表现层子项目(maven-student-web),修改项目打包方式为war。

2、引入当前模块需要用到的依赖

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <parent>
  6. <artifactId>maven-student</artifactId>
  7. <groupId>com.maven</groupId>
  8. <version>1.0-SNAPSHOT</version>
  9. </parent>
  10. <modelVersion>4.0.0</modelVersion>
  11. <artifactId>maven-student-web</artifactId>
  12. <packaging>war</packaging>
  13. <dependencies>
  14. <dependency>
  15. <groupId>com.maven</groupId>
  16. <artifactId>maven-student-service</artifactId>
  17. </dependency>
  18. <dependency>
  19. <groupId>javax.servlet</groupId>
  20. <artifactId>javax.servlet-api</artifactId>
  21. <scope>provided</scope>
  22. </dependency>
  23. <dependency>
  24. <groupId>javax.servlet.jsp</groupId>
  25. <artifactId>jsp-api</artifactId>
  26. <scope>provided</scope>
  27. </dependency>
  28. <dependency>
  29. <groupId>javax.servlet</groupId>
  30. <artifactId>jstl</artifactId>
  31. </dependency>
  32. </dependencies>
  33. </project>

最后,父项目的pom.xml文件的内容:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <modelVersion>4.0.0</modelVersion>
  6. <groupId>com.maven</groupId>
  7. <artifactId>maven-student</artifactId>
  8. <version>1.0-SNAPSHOT</version>
  9. <!-- 父项目管理的子模块列表:聚合 -->
  10. <modules>
  11. <module>maven-student-util</module>
  12. <module>maven-student-entity</module>
  13. <module>maven-student-dao</module>
  14. <module>maven-student-service</module>
  15. <module>maven-student-web</module>
  16. </modules>
  17. <!-- 1、修改为管理项目,packaging为pom -->
  18. <packaging>pom</packaging>
  19. <!-- 2、依赖版本管理:对项目中所有的依赖的版本进行管理 -->
  20. <properties>
  21. <!-- properties里面可以定义自定义标签 -->
  22. <servlet.version>3.0.1</servlet.version>
  23. <jsp.version>2.1</jsp.version>
  24. <jstl.version>1.2</jstl.version>
  25. <junit.version>4.12</junit.version>
  26. <mysql.version>5.1.38</mysql.version>
  27. </properties>
  28. <!-- 3、依赖管理:对项目中所有的依赖进行管理 -->
  29. <!-- 依赖管理不会引入依赖 -->
  30. <dependencyManagement>
  31. <dependencies>
  32. <dependency>
  33. <groupId>javax.servlet</groupId>
  34. <artifactId>javax.servlet-api</artifactId>
  35. <!-- maven表达式获取版本 -->
  36. <version>${servlet.version}</version>
  37. <scope>provided</scope>
  38. </dependency>
  39. <dependency>
  40. <groupId>javax.servlet.jsp</groupId>
  41. <artifactId>jsp-api</artifactId>
  42. <version>${jsp.version}</version>
  43. <scope>provided</scope>
  44. </dependency>
  45. <dependency>
  46. <groupId>javax.servlet</groupId>
  47. <artifactId>jstl</artifactId>
  48. <version>${jstl.version}</version>
  49. </dependency>
  50. <dependency>
  51. <groupId>junit</groupId>
  52. <artifactId>junit</artifactId>
  53. <version>${junit.version}</version>
  54. <scope>test</scope>
  55. </dependency>
  56. <dependency>
  57. <groupId>mysql</groupId>
  58. <artifactId>mysql-connector-java</artifactId>
  59. <version>${mysql.version}</version>
  60. </dependency>
  61. <dependency>
  62. <groupId>com.maven</groupId>
  63. <artifactId>maven-student-util</artifactId>
  64. <!-- Maven表达式中的对象project -->
  65. <version>${project.version}</version>
  66. </dependency>
  67. <dependency>
  68. <groupId>com.maven</groupId>
  69. <artifactId>maven-student-entity</artifactId>
  70. <version>${project.version}</version>
  71. </dependency>
  72. <dependency>
  73. <groupId>com.maven</groupId>
  74. <artifactId>maven-student-dao</artifactId>
  75. <version>${project.version}</version>
  76. </dependency>
  77. <dependency>
  78. <groupId>com.maven</groupId>
  79. <artifactId>maven-student-service</artifactId>
  80. <version>${project.version}</version>
  81. </dependency>
  82. </dependencies>
  83. </dependencyManagement>
  84. </project>

2.1.7 关于modules

modules可以加,也可以不加。但是modules有一个好处,当定义modules之后,我们通过父项目执行生命周期,所有的子module都会执行对应的生命周期。

注意:我们如果模块之间没有依赖,那么按照聚合配置的顺序来执行;如果有了依赖,肯定是被依赖的先构建。

通过父项目执行编译:

watermark_type_d3F5LXplbmhlaQ_shadow_50_text_Q1NETiBA5oiR5piv5rOi5ZOp5Liq5rOi_size_16_color_FFFFFF_t_70_g_se_x_16 8

2.2 项目后端实现

2.2.1 创建学生表

在MySQL数据库的test库下创建学生表,学生表字段如下:

watermark_type_d3F5LXplbmhlaQ_shadow_50_text_Q1NETiBA5oiR5piv5rOi5ZOp5Liq5rOi_size_16_color_FFFFFF_t_70_g_se_x_16 9

2.2.2 DBUtils工具类实现

在工具类子项目中创建com.maven.util包,创建DBUtils工具类。

DBUtils代码如下:

  1. package com.maven.util;
  2. import java.sql.*;
  3. import java.util.ArrayList;
  4. import java.util.HashMap;
  5. import java.util.List;
  6. import java.util.Map;
  7. /*
  8. * JDBC工具类的高度封装
  9. * */
  10. public class DBUtils {
  11. /*
  12. * private 为了不让外部调用
  13. * static 因为要在static代码块或方法中使用
  14. * final 不允许修改
  15. * */
  16. private static final String driverName = "com.mysql.jdbc.Driver";
  17. private static final String url = "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8";
  18. private static final String userName = "root";
  19. private static final String userPwd = "root";
  20. private static Connection connection;
  21. private static PreparedStatement preparedStatement;
  22. private static ResultSet resultSet;
  23. /*
  24. * 注册数据库驱动,注册驱动只需要执行一次即可
  25. * static静态代码块,在类加载的时候有且仅执行一次
  26. * */
  27. static {
  28. try {
  29. Class.forName(driverName);
  30. } catch (ClassNotFoundException e) {
  31. System.out.println("数据库驱动加载失败!");
  32. }
  33. }
  34. /*
  35. * 获取连接
  36. * */
  37. public static Connection getConnection() {
  38. try {
  39. connection = DriverManager.getConnection(url, userName, userPwd);
  40. } catch (SQLException throwables) {
  41. System.out.println("数据库连接获取失败!");
  42. }
  43. return connection;
  44. }
  45. /*
  46. * 查询数据的方法
  47. * 参数:
  48. * String sql,要执行的sql语句
  49. * List list,sql的参数
  50. * 返回值:
  51. * 不能返回ResultSet,因为资源释放之后,结果集不能在操作
  52. * 把结果集里面的数据用另外一种形式返回即可 List<Map<String,String>>
  53. * */
  54. public static List<Map<String, String>> query(String sql, List list) {
  55. List<Map<String, String>> resultList = new ArrayList<>();
  56. // 每次执行查询,都要获取得到连接
  57. getConnection();
  58. // 获取执行SQL语句的PrepareStatement对象
  59. try {
  60. preparedStatement = connection.prepareStatement(sql);
  61. // 给sql设置参数
  62. for (int i = 0; i < list.size(); i++) {
  63. // 因为不知道参数的类型,所以我们直接使用setObject(占位符索引,值); 占位符索引从1开始
  64. preparedStatement.setObject(i + 1, list.get(i));
  65. }
  66. // 执行查询
  67. resultSet = preparedStatement.executeQuery();
  68. // 获取结果集对应的结构
  69. ResultSetMetaData metaData = resultSet.getMetaData();
  70. // 把resultSet转换为List<Map<String,String>>
  71. while (resultSet.next()) {
  72. // resultSet里面每有一条数据,就创建一个Map集合
  73. Map<String, String> map = new HashMap<>();
  74. // map里面的key是列名,value是列对应的值
  75. // 结果集里面有多少列,就向map里面存储多少对值
  76. for (int i = 1; i <= metaData.getColumnCount(); i++) {
  77. map.put(metaData.getColumnName(i), resultSet.getString(i));
  78. }
  79. // 把map存储到list中
  80. resultList.add(map);
  81. }
  82. } catch (SQLException throwables) {
  83. System.out.println("SQL语句异常");
  84. } finally {
  85. close();
  86. }
  87. return resultList;
  88. }
  89. /*
  90. * 增删改的方法
  91. * 返回值: int类型,表示增、删、改的条目数
  92. * 参数:
  93. * String sql,要执行的sql语句
  94. * List list,sql的参数
  95. * */
  96. public static int update(String sql, List list) {
  97. int count = 0;
  98. // 每次执行更改操作,都要获取得到连接
  99. getConnection();
  100. // 获取执行SQL语句的PrepareStatement对象
  101. try {
  102. preparedStatement = connection.prepareStatement(sql);
  103. // 给sql设置参数
  104. for (int i = 0; i < list.size(); i++) {
  105. // 因为不知道参数的类型,所以我们直接使用setObject(占位符索引,值); 占位符索引从1开始
  106. preparedStatement.setObject(i + 1, list.get(i));
  107. }
  108. count = preparedStatement.executeUpdate();
  109. } catch (SQLException throwables) {
  110. System.out.println("SQL语句异常");
  111. } finally {
  112. close();
  113. }
  114. return count;
  115. }
  116. /*
  117. * 释放资源
  118. * */
  119. public static void close() {
  120. if (resultSet != null) {
  121. try {
  122. resultSet.close();
  123. } catch (SQLException throwables) {
  124. System.out.println("结果集关闭失败");
  125. }
  126. }
  127. if (preparedStatement != null) {
  128. try {
  129. preparedStatement.close();
  130. } catch (SQLException throwables) {
  131. System.out.println("Statement关闭失败");
  132. }
  133. }
  134. if (connection != null) {
  135. try {
  136. connection.close();
  137. } catch (SQLException throwables) {
  138. System.out.println("连接关闭失败!");
  139. }
  140. }
  141. }
  142. }

2.2.3 学生实体类实现

在实体类子项目中创建com.maven.entity包,创建StudentEntity实体类。

StudentEntity代码如下:

  1. package com.maven.entity;
  2. /**
  3. * 学生实体类
  4. */
  5. public class StudentEntity {
  6. private Integer id;
  7. private String name;
  8. private String sex;
  9. private String major;
  10. public StudentEntity() {
  11. }
  12. public StudentEntity(Integer id, String name, String sex, String major) {
  13. this.id = id;
  14. this.name = name;
  15. this.sex = sex;
  16. this.major = major;
  17. }
  18. public Integer getId() {
  19. return id;
  20. }
  21. public void setId(Integer id) {
  22. this.id = id;
  23. }
  24. public String getName() {
  25. return name;
  26. }
  27. public void setName(String name) {
  28. this.name = name;
  29. }
  30. public String getSex() {
  31. return sex;
  32. }
  33. public void setSex(String sex) {
  34. this.sex = sex;
  35. }
  36. public String getMajor() {
  37. return major;
  38. }
  39. public void setMajor(String major) {
  40. this.major = major;
  41. }
  42. @Override
  43. public String toString() {
  44. return "StudentEntity{" +
  45. "id=" + id +
  46. ", name='" + name + '\'' +
  47. ", sex='" + sex + '\'' +
  48. ", major='" + major + '\'' +
  49. '}';
  50. }
  51. }

2.2.4 学生持久层实现

在持久层子项目中创建com.maven.dao包,创建StudentDao接口。

在持久层子项目中创建com.maven.dao.impl包,创建StudentDaoImpl实现类。

注:我们仅仅实现简单的一套CRUD即可。

StudentDao接口代码如下:

  1. package com.maven.dao;
  2. import com.maven.entity.StudentEntity;
  3. import java.util.List;
  4. import java.util.Map;
  5. /*
  6. * 学生DAO接口
  7. */
  8. public interface StudentDao {
  9. /*
  10. * 新增学生的方法
  11. * @param studentEntity 包含新增学生的信息实体类
  12. * @return int 实际插入的数量
  13. */
  14. public abstract int insert(StudentEntity studentEntity);
  15. /*
  16. * 删除学生的方法
  17. * @param id 删除学生的id
  18. * @return int 实际删除的数量
  19. */
  20. public abstract int delete(Integer id);
  21. /*
  22. * 修改学生的方法
  23. * @param studentEntity 包含修改学生的信息实体类
  24. * @return int
  25. */
  26. public abstract int update(StudentEntity studentEntity);
  27. /*
  28. * 根据id查询学生的方法
  29. * @param id 查询学生的id
  30. * @return StudentEntity 查询到的学生信息
  31. */
  32. public abstract List<Map<String, String>> query(Integer id);
  33. /*
  34. * 查询所有学生的方法
  35. * @return List<StudentEntity> 查询到的所有数据
  36. */
  37. public abstract List<Map<String, String>> queryAll();
  38. }

StudentDaoImpl实现类代码如下:

  1. package com.maven.dao.impl;
  2. import com.maven.dao.StudentDao;
  3. import com.maven.entity.StudentEntity;
  4. import com.maven.util.DBUtils;
  5. import java.util.ArrayList;
  6. import java.util.Arrays;
  7. import java.util.List;
  8. import java.util.Map;
  9. /**
  10. * 学生DAO接口实现类
  11. */
  12. public class StudentDaoImpl implements StudentDao {
  13. @Override
  14. public int insert(StudentEntity studentEntity) {
  15. String sql = "insert into student(name,sex,major) values (?,?,?)";
  16. List list = Arrays.asList(studentEntity.getName(), studentEntity.getSex(), studentEntity.getMajor());
  17. return DBUtils.update(sql, list);
  18. }
  19. @Override
  20. public int delete(Integer id) {
  21. String sql = "delete from student where id = ?";
  22. List list = Arrays.asList(id);
  23. return DBUtils.update(sql, list);
  24. }
  25. @Override
  26. public int update(StudentEntity studentEntity) {
  27. String sql = "update student set name = ?,sex=?,major=? where id = ?";
  28. List list = Arrays.asList(studentEntity.getName(), studentEntity.getSex(), studentEntity.getMajor(),studentEntity.getId());
  29. return DBUtils.update(sql, list);
  30. }
  31. @Override
  32. public List<Map<String, String>> query(Integer id) {
  33. String sql = "select * from student where id = ?";
  34. List list = Arrays.asList(id);
  35. return DBUtils.query(sql,list);
  36. }
  37. @Override
  38. public List<Map<String, String>> queryAll() {
  39. String sql = "select * from student";
  40. List list = new ArrayList();
  41. return DBUtils.query(sql,list);
  42. }
  43. }

2.2.5 学生业务层实现

在业务层子项目中创建com.maven.service包,创建StudentService类。

StudentService类代码如下:

  1. package com.maven.service;
  2. import com.maven.dao.StudentDao;
  3. import com.maven.dao.impl.StudentDaoImpl;
  4. import com.maven.entity.StudentEntity;
  5. import java.util.ArrayList;
  6. import java.util.List;
  7. import java.util.Map;
  8. /**
  9. * 学生的业务逻辑层
  10. */
  11. public class StudentService {
  12. //创建DAO实现类对象
  13. private StudentDao studentDao = new StudentDaoImpl();
  14. public int insert(StudentEntity studentEntity) {
  15. return studentDao.insert(studentEntity);
  16. }
  17. public int delete(Integer id) {
  18. return studentDao.delete(id);
  19. }
  20. public int update(StudentEntity studentEntity) {
  21. return studentDao.update(studentEntity);
  22. }
  23. public StudentEntity query(Integer id) {
  24. StudentEntity studentEntity = null;
  25. List<Map<String, String>> query = studentDao.query(id);
  26. if (query.size() > 0) {
  27. Map<String, String> stringMap = query.get(0);
  28. studentEntity = new StudentEntity();
  29. studentEntity.setId(Integer.parseInt(stringMap.get("id")));
  30. studentEntity.setName(stringMap.get("name"));
  31. studentEntity.setSex(stringMap.get("sex"));
  32. studentEntity.setMajor(stringMap.get("major"));
  33. }
  34. return studentEntity;
  35. }
  36. public List<StudentEntity> queryAll() {
  37. List<StudentEntity> list = new ArrayList<>();
  38. List<Map<String, String>> query = studentDao.queryAll();
  39. for (int i = 0; i < query.size(); i++) {
  40. Map<String, String> stringMap = query.get(0);
  41. StudentEntity studentEntity = new StudentEntity();
  42. studentEntity.setId(Integer.parseInt(stringMap.get("id")));
  43. studentEntity.setName(stringMap.get("name"));
  44. studentEntity.setSex(stringMap.get("sex"));
  45. studentEntity.setMajor(stringMap.get("major"));
  46. list.add(studentEntity);
  47. }
  48. return list;
  49. }
  50. }

2.2.6 学生表现层Servlet实现

查询所有学生的Servlet:

  1. package com.maven.servlet;
  2. import com.maven.entity.StudentEntity;
  3. import com.maven.service.StudentService;
  4. import javax.servlet.ServletException;
  5. import javax.servlet.annotation.WebServlet;
  6. import javax.servlet.http.HttpServlet;
  7. import javax.servlet.http.HttpServletRequest;
  8. import javax.servlet.http.HttpServletResponse;
  9. import java.io.IOException;
  10. import java.util.List;
  11. /**
  12. * 查询所有学生的Servlet
  13. */
  14. @WebServlet("/student/list")
  15. public class StudentQueryAllServlet extends HttpServlet {
  16. @Override
  17. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  18. doPost(req, resp);
  19. }
  20. @Override
  21. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  22. req.setCharacterEncoding("utf-8");
  23. resp.setCharacterEncoding("utf-8");
  24. resp.setContentType("text/html;charset=utf-8");
  25. StudentService studentService = new StudentService();
  26. List<StudentEntity> list = studentService.queryAll();
  27. System.out.println(list);
  28. req.setAttribute("stuList", list);
  29. System.out.println(req.getServletContext().getContextPath());
  30. //在请求转发中,/表示项目根目录
  31. req.getRequestDispatcher("/student/list.jsp").forward(req, resp);
  32. }
  33. }

根据id查询学生的Servlet:

  1. package com.maven.servlet;
  2. import com.maven.entity.StudentEntity;
  3. import com.maven.service.StudentService;
  4. import javax.servlet.ServletException;
  5. import javax.servlet.annotation.WebServlet;
  6. import javax.servlet.http.HttpServlet;
  7. import javax.servlet.http.HttpServletRequest;
  8. import javax.servlet.http.HttpServletResponse;
  9. import java.io.IOException;
  10. /**
  11. * 根据学生id查询学生的Servlet
  12. */
  13. @WebServlet("/student/query")
  14. public class StudentQueryServlet extends HttpServlet {
  15. @Override
  16. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  17. doPost(req, resp);
  18. }
  19. @Override
  20. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  21. req.setCharacterEncoding("utf-8");
  22. resp.setCharacterEncoding("utf-8");
  23. resp.setContentType("text/html;charset=utf-8");
  24. //获取查询学生的id
  25. int id = Integer.parseInt(req.getParameter("id"));
  26. StudentService studentService = new StudentService();
  27. StudentEntity studentEntity = studentService.query(id);
  28. req.setAttribute("editStudentInfo", studentEntity);
  29. req.getRequestDispatcher("/student/edit.jsp").forward(req, resp);
  30. }
  31. }

添加学生的Servlet:

  1. package com.maven.servlet;
  2. import com.maven.entity.StudentEntity;
  3. import com.maven.service.StudentService;
  4. import javax.servlet.ServletException;
  5. import javax.servlet.annotation.WebServlet;
  6. import javax.servlet.http.HttpServlet;
  7. import javax.servlet.http.HttpServletRequest;
  8. import javax.servlet.http.HttpServletResponse;
  9. import java.io.IOException;
  10. /**
  11. * 添加学生的Servlet
  12. */
  13. @WebServlet("/student/add")
  14. public class StudentAddServlet extends HttpServlet {
  15. @Override
  16. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  17. doPost(req, resp);
  18. }
  19. @Override
  20. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  21. req.setCharacterEncoding("utf-8");
  22. resp.setCharacterEncoding("utf-8");
  23. resp.setContentType("text/html;charset=utf-8");
  24. String name = req.getParameter("name");
  25. String sex = req.getParameter("sex");
  26. String major = req.getParameter("major");
  27. StudentEntity studentEntity = new StudentEntity(null, name, sex, major);
  28. StudentService studentService = new StudentService();
  29. int i = studentService.insert(studentEntity);
  30. if (i > 0) {
  31. req.setAttribute("msg", "添加成功!");
  32. } else {
  33. req.setAttribute("msg", "添加失败!");
  34. }
  35. req.getRequestDispatcher("/student/list").forward(req, resp);
  36. }
  37. }

删除学生的Servlet:

  1. package com.maven.servlet;
  2. import com.maven.entity.StudentEntity;
  3. import com.maven.service.StudentService;
  4. import javax.servlet.ServletException;
  5. import javax.servlet.annotation.WebServlet;
  6. import javax.servlet.http.HttpServlet;
  7. import javax.servlet.http.HttpServletRequest;
  8. import javax.servlet.http.HttpServletResponse;
  9. import java.io.IOException;
  10. /**
  11. * 删除学生的Servlet
  12. */
  13. @WebServlet("/student/delete")
  14. public class StudentDeleteServlet extends HttpServlet {
  15. @Override
  16. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  17. doPost(req, resp);
  18. }
  19. @Override
  20. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  21. req.setCharacterEncoding("utf-8");
  22. resp.setCharacterEncoding("utf-8");
  23. resp.setContentType("text/html;charset=utf-8");
  24. //获取删除学生的id
  25. int id = Integer.parseInt(req.getParameter("id"));
  26. System.out.println("删除学生的id:"+id);
  27. StudentService studentService = new StudentService();
  28. int i = studentService.delete(id);
  29. if (i > 0) {
  30. req.setAttribute("msg", "删除成功!");
  31. } else {
  32. req.setAttribute("msg", "删除失败!");
  33. }
  34. req.getRequestDispatcher("/student/list").forward(req, resp);
  35. }
  36. }

修改学生的Servlet:

  1. package com.maven.servlet;
  2. import com.maven.entity.StudentEntity;
  3. import com.maven.service.StudentService;
  4. import javax.servlet.ServletException;
  5. import javax.servlet.annotation.WebServlet;
  6. import javax.servlet.http.HttpServlet;
  7. import javax.servlet.http.HttpServletRequest;
  8. import javax.servlet.http.HttpServletResponse;
  9. import java.io.IOException;
  10. /**
  11. * 修改学生的Servlet
  12. */
  13. @WebServlet("/student/update")
  14. public class StudentUpdateServlet extends HttpServlet {
  15. @Override
  16. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  17. doPost(req, resp);
  18. }
  19. @Override
  20. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  21. req.setCharacterEncoding("utf-8");
  22. resp.setCharacterEncoding("utf-8");
  23. resp.setContentType("text/html;charset=utf-8");
  24. int id = Integer.parseInt(req.getParameter("id"));
  25. String name = req.getParameter("name");
  26. String sex = req.getParameter("sex");
  27. String major = req.getParameter("major");
  28. StudentEntity studentEntity = new StudentEntity(id, name, sex, major);
  29. System.out.println(studentEntity);
  30. StudentService studentService = new StudentService();
  31. int i = studentService.update(studentEntity);
  32. if (i > 0) {
  33. req.setAttribute("msg", "修改成功!");
  34. } else {
  35. req.setAttribute("msg", "修改失败!");
  36. }
  37. req.getRequestDispatcher("/student/list").forward(req, resp);
  38. }
  39. }

2.3 项目前端实现

目录结构如下:

watermark_type_d3F5LXplbmhlaQ_shadow_50_text_Q1NETiBA5oiR5piv5rOi5ZOp5Liq5rOi_size_6_color_FFFFFF_t_70_g_se_x_16

2.3.1 项目首页实现

  1. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
  2. <html>
  3. <head>
  4. <title>Title</title>
  5. </head>
  6. <body>
  7. <h1>这里是项目首页</h1>
  8. <a href="${pageContext.request.contextPath}/student/list">查询所有学生信息</a>
  9. </body>
  10. </html>

2.3.2 学生列表页实现

  1. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
  2. <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
  3. <html>
  4. <head>
  5. <title>Title</title>
  6. <style>
  7. table, td, th {
  8. border: 1px solid black;
  9. border-collapse: collapse;
  10. }
  11. table {
  12. width: 600px;
  13. }
  14. tr {
  15. height: 40px;
  16. }
  17. </style>
  18. </head>
  19. <body>
  20. <h2>所有学生的信息: ${msg}</h2>
  21. <table>
  22. <tr>
  23. <th>学号</th>
  24. <th>姓名</th>
  25. <th>性别</th>
  26. <th>专业</th>
  27. <th>操作</th>
  28. </tr>
  29. <c:forEach items="${stuList}" var="stu">
  30. <tr>
  31. <td>${stu.id}</td>
  32. <td>${stu.name}</td>
  33. <td>${stu.sex}</td>
  34. <td>${stu.major}</td>
  35. <td>
  36. <a href="${pageContext.request.contextPath}/student/delete?id=${stu.id}">删除</a>
  37. <%--先根据id查询学生信息,然后跳转到修改页面--%>
  38. <a href="${pageContext.request.contextPath}/student/query?id=${stu.id}">修改</a>
  39. </td>
  40. </tr>
  41. </c:forEach>
  42. </table>
  43. </body>
  44. </html>

2.3.3 修改学生页面实现

  1. <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
  2. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
  3. <html>
  4. <head>
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. <h2>修改学生信息</h2>
  9. <form action="${pageContext.request.contextPath}/student/update" method="post">
  10. <p>
  11. 学号: <input type="text" name="id" value="${editStudentInfo.id}" readonly>
  12. </p>
  13. <p>
  14. 姓名: <input type="text" name="name" value="${editStudentInfo.name}">
  15. </p>
  16. <p>
  17. 性别:
  18. <c:if test="${editStudentInfo.sex == '男'}">
  19. <input type="radio" name="sex" value="男" checked>
  20. <input type="radio" name="sex" value="女">
  21. </c:if>
  22. <c:if test="${editStudentInfo.sex == '女'}">
  23. <input type="radio" name="sex" value="男">
  24. <input type="radio" name="sex" value="女" checked>
  25. </c:if>
  26. </p>
  27. <p>
  28. 专业: <input type="text" name="major" value="${editStudentInfo.major}">
  29. </p>
  30. <p>
  31. <button type="submit">修改</button>
  32. </p>
  33. </form>
  34. </body>
  35. </html>

2.3.4 添加学生页面实现

  1. <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
  2. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
  3. <html>
  4. <head>
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. <h2>添加学生信息</h2>
  9. <form action="${pageContext.request.contextPath}/student/add" method="post">
  10. <p>
  11. 姓名: <input type="text" name="name">
  12. </p>
  13. <p>
  14. 性别:
  15. <input type="radio" name="sex" value="男" checked>
  16. <input type="radio" name="sex" value="女">
  17. </p>
  18. <p>
  19. 专业: <input type="text" name="major">
  20. </p>
  21. <p>
  22. <button type="submit">新增</button>
  23. </p>
  24. </form>
  25. </body>
  26. </html>

发表评论

表情:
评论列表 (有 0 条评论,182人围观)

还没有评论,来说两句吧...

相关阅读