关于本文

Spring 的内容已经学习了三篇内容,今天算是最后一篇文章,这节内容基本上是熟悉流程,本文要实现的功能:通过web页面访问数据库,实现对数据表中的数据插入和查询;与之前内容不同的是这次要创建 web 项目,而不再是 Java 项目,此外还要简单回顾一下 jsp 的内容,接触到的新知识:如何将Spring容器对象全局唯一

实现步骤

第一步:建表

这一步我们还是继续使用上一节内容中新建的表programmer,里面有三个字段:idnameage,很简单,这里不做具体流程展示了。

第二步:创建web项目并添加依赖

创建web项目其实和创建java项目的区别就是我们选择的模板不同,当然还是基于maven来创建的,模板选择xxx-maven-archetype-webapp就 ok 了。

创建完成之后,目录结构会比Java项目多出来一个webapp目录,相应的缺少了javaresources目录,我们手动创建即可。

pom.xml文件中添加依赖,不同于上节内容的是,这里需要再增加两个依赖(jsp和servlet),完整代码如下:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <groupId>com.javafirst</groupId>
  6. <artifactId>spring-webapp</artifactId>
  7. <version>1.0-SNAPSHOT</version>
  8. <packaging>war</packaging>
  9. <!-- <name>spring-webapp Maven Webapp</name>-->
  10. <!-- &lt;!&ndash; FIXME change it to the project's website &ndash;&gt;-->
  11. <!-- <url>http://www.example.com</url>-->
  12. <properties>
  13. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  14. <maven.compiler.source>1.8</maven.compiler.source>
  15. <maven.compiler.target>1.8</maven.compiler.target>
  16. </properties>
  17. <dependencies>
  18. <dependency>
  19. <groupId>junit</groupId>
  20. <artifactId>junit</artifactId>
  21. <version>4.11</version>
  22. <scope>test</scope>
  23. </dependency>
  24. <dependency>
  25. <groupId>mysql</groupId>
  26. <artifactId>mysql-connector-java</artifactId>
  27. <version>8.0.25</version>
  28. </dependency>
  29. <!--MyBatis依赖-->
  30. <dependency>
  31. <groupId>org.mybatis</groupId>
  32. <artifactId>mybatis</artifactId>
  33. <version>3.5.7</version>
  34. </dependency>
  35. <!-- Spring依赖 -->
  36. <dependency>
  37. <groupId>org.springframework</groupId>
  38. <artifactId>spring-context</artifactId>
  39. <version>5.3.14</version>
  40. </dependency>
  41. <!-- MyBatis集成Spring -->
  42. <dependency>
  43. <groupId>org.mybatis</groupId>
  44. <artifactId>mybatis-spring</artifactId>
  45. <version>1.3.3</version>
  46. </dependency>
  47. <!-- Spring事务-->
  48. <dependency>
  49. <groupId>org.springframework</groupId>
  50. <artifactId>spring-tx</artifactId>
  51. <version>5.3.14</version>
  52. </dependency>
  53. <dependency>
  54. <groupId>org.springframework</groupId>
  55. <artifactId>spring-jdbc</artifactId>
  56. <version>5.3.14</version>
  57. </dependency>
  58. <!-- AspectJ aop实现注解 -->
  59. <dependency>
  60. <groupId>org.springframework</groupId>
  61. <artifactId>spring-aspects</artifactId>
  62. <version>5.3.14</version>
  63. </dependency>
  64. <!-- 阿里数据库连接池 druid -->
  65. <dependency>
  66. <groupId>com.alibaba</groupId>
  67. <artifactId>druid</artifactId>
  68. <version>1.2.8</version>
  69. </dependency>
  70. <!-- servlet -->
  71. <dependency>
  72. <groupId>javax.servlet</groupId>
  73. <artifactId>javax.servlet-api</artifactId>
  74. <version>3.1.0</version>
  75. </dependency>
  76. <!-- jsp -->
  77. <dependency>
  78. <groupId>javax.servlet.jsp</groupId>
  79. <artifactId>javax.servlet.jsp-api</artifactId>
  80. <version>2.3.3</version>
  81. </dependency>
  82. </dependencies>
  83. <build>
  84. <resources>
  85. <resource>
  86. <directory>src/main/java</directory>
  87. <includes>
  88. <include>**/*.properties</include>
  89. <include>**/*.xml</include>
  90. </includes>
  91. <filtering>false</filtering>
  92. </resource>
  93. </resources>
  94. </build>
  95. </project>

本文在后面还会增加一个依赖,各位记得这个文件。

第三步:新建java实体类

这里新建的实体类中的字段没有和表中字段名保持一致,就这点区别,具体代码如下:

  1. public class Programmer {
  2. private Integer pId;
  3. private Integer pAge;
  4. private String pName;
  5. public Integer getpId() {
  6. return pId;
  7. }
  8. public void setpId(Integer pId) {
  9. this.pId = pId;
  10. }
  11. public Integer getpAge() {
  12. return pAge;
  13. }
  14. public void setpAge(Integer pAge) {
  15. this.pAge = pAge;
  16. }
  17. public String getpName() {
  18. return pName;
  19. }
  20. public void setpName(String pName) {
  21. this.pName = pName;
  22. }
  23. @Override
  24. public String toString() {
  25. return "Programmer信息:{" +
  26. "pId=" + pId +
  27. ", pAge=" + pAge +
  28. ", pName='" + pName + '\'' +
  29. '}';
  30. }
  31. }

第四步:创建dao和mapper文件

我们要实现插入数据和查询数据,所以我们的dao只需提供两个接口即可。

  1. public interface ProgrammerDaoNew {
  2. int insertProgrammer(Programmer programmer);
  3. Programmer selectProgrammerById(Integer id);
  4. }

对应mapper文件这里依旧是分开的,位置的resources目录下的mapper文件夹中,具体代码如下:

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE mapper
  3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  5. <mapper namespace="com.javafirst.dao.ProgrammerDaoNew">
  6. <insert id="insertProgrammer">
  7. insert into programmer(`name`,age) values (#{pName},#{pAge})
  8. </insert>
  9. <select id="selectProgrammerById" resultMap="selectMap">
  10. select * from programmer where id = #{id}
  11. </select>
  12. <resultMap id="selectMap" type="com.javafirst.domain.Programmer">
  13. <id column="id" property="pId"/>
  14. <id column="name" property="pName"/>
  15. <id column="age" property="pAge"/>
  16. </resultMap>
  17. </mapper>

第五步:配置MyBatis核心文件

这个和上一节内容一样,该配置文件中要完善的内容也大致相同,其实就和前面说过一样,基本上都是流程代码:

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE configuration
  3. PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  4. "http://mybatis.org/dtd/mybatis-3-config.dtd">
  5. <configuration>
  6. <typeAliases>
  7. <typeAlias type="com.javafirst.domain.Programmer"/>
  8. </typeAliases>
  9. <mappers>
  10. <mapper resource="mapper/ProgrammerDaoMapper.xml"/>
  11. </mappers>
  12. </configuration>

到这里大家应该有一个感受点吧:

以上流程基本上都是环环相扣的,也就是存在顺序,你看每一个文件中都会引入上一步创建的内容,写多了,自然就顺手了。

第六步:创建业务层service

我们的业务只有添加记录和查询记录,当然各位还可以在此基础上进行扩展,我们的接口内容如下:

  1. public interface ProgrammerService {
  2. int addProgrammer(Programmer programmer);
  3. Programmer findProgrammer(Integer id);
  4. }

其实现类中,是我们真实业务开发中需要做的工作,比较各种校验通过后才进行真正的添加记录工作等等。

  1. public class ProgrammerServiceImpl implements ProgrammerService {
  2. ProgrammerDaoNew programmerDao;
  3. public void setProgrammerDao(ProgrammerDaoNew programmerDao) {
  4. this.programmerDao = programmerDao;
  5. }
  6. @Override
  7. public int addProgrammer(Programmer programmer) {
  8. return programmerDao.insertProgrammer(programmer);
  9. }
  10. @Override
  11. public Programmer findProgrammer(Integer id) {
  12. return programmerDao.selectProgrammerById(id);
  13. }
  14. }

有人可能会好奇,这里的 setProgrammerDao() 方法什么时候会调用呢?其实这是Spring框架在处理,当我们在Spring的配置文件中声明了自定义的Service之后,就会自动进行设值注入,这是我们学习Spring第一节内容时掌握的知识。

第七步:配置Spring核心文件

其实都是样板代码,这个前面文章里也已经提到了

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
  6. <!-- 使用属性文件 配置数据源中数据库链接信息 -->
  7. <context:property-placeholder location="jdbc.properties"/>
  8. <!-- 声明数据源-->
  9. <bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
  10. <property name="url" value="${jdbc.url}"/>
  11. <property name="username" value="${jdbc.username}"/>
  12. <property name="password" value="${jdbc.password}"/>
  13. </bean>
  14. <!-- 声明 SQLSessionFactoryBean -->
  15. <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  16. <property name="dataSource" ref="myDataSource"/>
  17. <property name="configLocation" value="classpath:mybatis-config.xml"/>
  18. </bean>
  19. <!-- 声明 MapperScannerConfigurer -->
  20. <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
  21. <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
  22. <property name="basePackage" value="com.javafirst.dao"/>
  23. </bean>
  24. <bean id="programmerService" class="com.javafirst.service.ProgrammerServiceImpl">
  25. <property name="programmerDao" ref="programmerDaoNew"/>
  26. </bean>
  27. </beans>

这里我习惯将数据源的相关配置单独提取文件,这也是主流做法,推荐大家也养成习惯,jdbc.properties文件内容如下:

  1. jdbc.url=jdbc:mysql://localhost:3306/spring_demo
  2. jdbc.username=root
  3. jdbc.password=root

到这里其实我们已经完成了基本的配置,但我们创建的是web项目,不是java项目,所以我们要进行测试的话,还需要有web页面和简单的逻辑,这就要用到我们的webapp目录了以及jsp知识。

第八步:创建 Jsp 页面

我们需要两个jsp页面,一个用来录入信息和提供查询功能,也就是插入操作,页面我们提供姓名和年龄输入,然后提交就进行数据库的插入操作,这个文件名就叫register.jsp,其代码如下:

  1. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
  2. <html>
  3. <head>
  4. <title>录入一个工程师</title>
  5. </head>
  6. <body>
  7. <form method="post" action="add">
  8. <%-- 这里的 name 值就是 servlet 中取值的key --%>
  9. 姓名:<input type="text" name="name"><br>
  10. 年龄:<input type="text" name="age"><br>
  11. <input type="submit" value="录入"/>
  12. </form>
  13. <br><br>
  14. <form method="get" action="find">
  15. 查询工程师信息:<input type="text" name="pId">
  16. <input type="submit" value="查询">
  17. </form>
  18. </body>
  19. </html>

这个页面运行起来大概长这样:

推荐学java——Spring与web项目 - 图1

当我们录入成功后,页面会跳转到一个成功页面,就是我们的第二个jsp页面registerSuccess.jsp,我们不关联具体逻辑,只做简单展示,完整代码如下:

  1. <%--
  2. Created by studyingJava
  3. Date: 2022/1/24
  4. Time: 15:46
  5. --%>
  6. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
  7. <html>
  8. <head>
  9. <title>注册成功</title>
  10. </head>
  11. <body>
  12. 恭喜你,完成注册!即将为您跳转首页...
  13. </body>
  14. </html>

查询成功我们不做跳转页面,当前页面也不做展示,我们可以通过控制台看查询信息即可,因为我们在前面的实体类中已经重写了toString() 方法;当然各位如果感兴趣,可以自行将结果显示在页面上。

第九步:创建Servlet

页面的请求处理和展示我们同样需要创建两个Servlet,一个用来处理录入请求,也就是post提价过来的数据,另一个处理查询操作,也就是get请求数据操作,下面是代码:

AddProgrammerServlet.java代码如下:

  1. /**
  2. * desc:
  3. * author: 推荐学java
  4. * <p>
  5. * weChat: studyingJava
  6. */
  7. public class AddProgrammerServlet extends HttpServlet {
  8. @Override
  9. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  10. req.setCharacterEncoding("utf-8");
  11. // 取出表单提交过来的注册数据
  12. String name = req.getParameter("name");
  13. String age = req.getParameter("age");
  14. System.out.println("提交过来的数据:" + name + age);
  15. // 创建Spring容器 插入数据库
  16. String config = "spring-applicationContext.xml";
  17. ApplicationContext context = new ClassPathXmlApplicationContext(config);
  18. ProgrammerService service = (ProgrammerService) context.getBean("programmerService");
  19. Programmer programmer = new Programmer();
  20. programmer.setpName(name);
  21. programmer.setpAge(Integer.valueOf(age));
  22. service.addProgrammer(programmer);
  23. // 给用户反馈
  24. req.getRequestDispatcher("/registerSuccess.jsp").forward(req, resp);
  25. }
  26. @Override
  27. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  28. super.doGet(req, resp);
  29. }
  30. }

其实这里主要就是三步骤,大家可以看代码注释。

FindProgrammerServlet.java代码如下:

  1. /**
  2. * desc:
  3. * author: 推荐学java
  4. * <p>
  5. * weChat: studyingJava
  6. */
  7. public class FindProgrammerServlet extends HttpServlet {
  8. @Override
  9. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  10. }
  11. @Override
  12. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  13. req.setCharacterEncoding("utf-8");
  14. // 取出表单提交过来的注册数据
  15. String pId = req.getParameter("pId");
  16. System.out.println("提交过来的要查询的用户ID:" + pId);
  17. // 创建Spring容器 插入数据库
  18. String config = "spring-applicationContext.xml";
  19. ApplicationContext context = new ClassPathXmlApplicationContext(config);
  20. ProgrammerService service = (ProgrammerService) context.getBean("programmerService");
  21. Programmer programmer = service.findProgrammer(Integer.valueOf(pId));
  22. System.out.println(programmer);
  23. }
  24. }

逻辑其实比较简单,细心的朋友是不是发现,我们分别在不同的 Servlet 中创建了Spring容器对象,这就是本文开头提出的需要解决的问题,继续往下看。

第十步:配置Servlet

我们已经创建好了Servlet,现在进行配置,也就是让浏览器访问的时候能找到对应处理请求的功能承载页面,配置也比较简单,在webapp 目录下的WEB-INF下的 web.xml中完善内容如下:

  1. <!DOCTYPE web-app PUBLIC
  2. "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
  3. "http://java.sun.com/dtd/web-app_2_3.dtd" >
  4. <web-app>
  5. <display-name>Archetype Created Web Application</display-name>
  6. <servlet>
  7. <servlet-name>AddProgrammerServlet</servlet-name>
  8. <servlet-class>com.javafirst.controller.AddProgrammerServlet</servlet-class>
  9. </servlet>
  10. <servlet-mapping>
  11. <servlet-name>AddProgrammerServlet</servlet-name>
  12. <url-pattern>/add</url-pattern>
  13. </servlet-mapping>
  14. <servlet>
  15. <servlet-name>FindProgrammerServlet</servlet-name>
  16. <servlet-class>com.javafirst.controller.FindProgrammerServlet</servlet-class>
  17. </servlet>
  18. <servlet-mapping>
  19. <servlet-name>FindProgrammerServlet</servlet-name>
  20. <url-pattern>/find</url-pattern>
  21. </servlet-mapping>
  22. </web-app>

这里就是相当于入口,我们的Servlet就相当于页面,而每个页面要使用,就得现在这里注册。

第十一步:发布并测试

到这里,我们的所有工作都已就绪,但不同于java项目的是,web项目需要部署发布才可在浏览器进行访问,关于IDEA部署web项目,这里就不多讲了,注意一点是你必须安装并配置好 JDKtomcat环境

另外有可能会遇到8080端口被占用的错误提示,导致我们的项目Service启动不起来,这里给大家一个命令查找某个端口被哪个PID(进程ID)占用?

  1. netstat -aon | findstr 8080

找出该进程的ID,然后再通过命令将其 kill 掉:

  1. netstat -aon | findstr 14321

然后尝试重新启动项目即可。这里的两条命令是通过命令行窗口执行的。

使用 ContextLoaderListener

这就是我们需要解决前面提到的问题,使用ContextLoaderListener监听器,该监听器是Spring框架提供的,核心操作两步:

  • pom.xml中添加依赖spring-web
  • 在web.xml中配置context-param标签

spring-web依赖

  1. <!-- spring-web -->
  2. <dependency>
  3. <groupId>org.springframework</groupId>
  4. <artifactId>spring-web</artifactId>
  5. <version>1.2.6</version>
  6. </dependency>

web.xml中的配置如下:

  1. <!-- 配置 ContextLoaderListener -->
  2. <context-param>
  3. <param-name>contextConfigLocation</param-name>
  4. <param-value>classpath:spring-applicationContext.xml</param-value>
  5. </context-param>
  6. <listener>
  7. <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  8. </listener>

完成这两步配置后,接下来我们就可以使用Spring提供的工具类来修改我们的初始化Spring容器代码,具体如下:

  1. //String config = "spring-applicationContext.xml";
  2. //ApplicationContext context = new ClassPathXmlApplicationContext(config);
  3. // 使用这句就替代了我们前面的使用方式
  4. ApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());

这样这样在多个地方获取ApplicationContext对象,系统就不会重新创建,保证全局唯一。

总结

  • Spring与MyBatis结合使用其实非常方便,很多工作都可以配置好之后专注业务开发即可这可能就是框架带来的好处
  • 创建基于maven的Java项目和web项目流程必须会,能完善常用配置

学编程,推荐首选Java语言,小编创建了一个专注Java的原创公众号推荐学java,各位可以在微信搜索javaFirst 关注,一起开启Java旅途!