本文未经作者允许,禁止内容转载,引用请标明出处。
需求背景:由于最近总是接到一些需求,需要配合前端团队快速建设移动端UI应用或web应用及后台业务逻辑支撑的需求,若每次都复用之前复杂业务应用的项目代码,总会携带很多暂时不会用到的功能或组件,这样的初始工程就存在冗余代码。
在本文中,我们将使用Java语言开发集成环境IntelliJ IDEA(其倡言是智能编码☺),应用maven构建SpringMVC整合Mybatis+MySQL5.7(流行框架)的web项目;目的在于快速构建一个简洁纯净版的web应用工程,将其作为一个基础web-demo,以便类似的项目都可以复用本demo。本文旨在快速搭建web项目,并未深入研究原理,有兴趣的可参考其他文章研究Spring及mybatis原理。
本项目的工程代码,已经提交到github上,方便每次下载使用。(github源代码链接),新添加了本次demo的war包文件,亦可在github上下载。
一、IDEA下构建maven的web项目
1、新建工程New-->Project,创建maven的web项目,选择maven-archetype-webapp,并配置Project SDK,本次选用的是本机已安装的jdk1.8,如下图所示:
2、下一步,填写自己项目的GroupId,ArtifactId,如下图:
3、下一步,设置maven的安装目录,代替IDEA自带的maven版本,我换成了自己手动安装的maven版本,路径存在D盘;设置maven本地的仓库,我配置了本项目单独的仓库,但没去复用之前项目所用的maven仓库,为的是让本工程的依赖jar包保持纯净(虽然无关紧要,原谅我有莫名的洁癖☺)。
4、下一步,设置工程的Project Name。
5、Finish,maven的web项目就创建完成,接下来maven会下载相应的资源,下载时速度会很慢(maven加载jar包过程,默认的是先扫描本地仓库,若本地仓库没有,则扫描远程仓库下载。默认的conf/settings.xml文件没有配置远程仓库,所以扫描的是maven的中央仓库(资源镜像一般在国外),所以慢);由于我使用的网络是FQ外网,直接默认maven下载方式速度还是可以的(或许也是人品好一点☺),如果大家想快一点,也可以参考网上配置maven使用国内镜像的教程。
二、搭建基于SpringMVC整合mybatis的web工程
1、等待maven下载资源完成后,注意到工程目录中会出现src的文件夹,src下默认会存在main目录,main下默认存在resources和webapp文件夹,其中resources文件夹主要存放项目的配置或属性等文件,webapp则是web应用的根目录,会存放HTML,CSS,js,JSP或资源文件等。我们需要再main目录下创建java文件夹,java目录下存放的将是Java程序的package及class文件等。由于我们将搭建springMVC框架,我们可以提前搭建好项目的文件目录结构,如下图所示:
其中,java下创建项目的package com.charlie,其下可以再分类不同的package,包括controller,service,service-->impl,entity,dao,dao-->mapper,dao-->xml,common,util等,这些包是基于springmvc+mybatis所需的,还有可以存放一些公共类或工具类。
webapp下可创建css,js,image,WEB-INF-->views等。
src可创建main的同级目录test,可以用于后续项目开发过程中的Junit程序测试。
2、配置pom.xml文件,maven添加web项目必需的以及SpringMVC所必需的dependencies包。
2.1、以下是引入Junit,日志,MySQL驱动的依赖包,注意此次引入的是最新的mysql-connector-java-6.0.6,会影响后续的SpringMVC及jdbc等的特殊配置,这将和旧版本的mysql驱动包有区别。
1 <!-- junit测试包--> 2 <dependency> 3 <groupId>junit</groupId> 4 <artifactId>junit</artifactId> 5 <version>4.12</version> 6 <scope>test</scope> 7 </dependency> 8 <!--日志包--> 9 <dependency> 10 <groupId>org.slf4j</groupId> 11 <artifactId>slf4j-log4j12</artifactId> 12 <version>1.8.0-alpha2</version> 13 </dependency> 14 <!--mysql驱动包--> 15 <dependency> 16 <groupId>mysql</groupId> 17 <artifactId>mysql-connector-java</artifactId> 18 <version>6.0.6</version> 19 </dependency>
这样,已经支持了日志的输出,我们仍然需要日志的属性文件,在resources目录下,新增日志属性文件log4j.properties,并配置如下(配置方式很多种,大家选择一种即可):
#配置根Logger 后面是若干个Appender
log4j.rootLogger=DEBUG,A1,R
#log4j.rootLogger=INFO,A1,R
# ConsoleAppender 输出
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS} [%c]-[%p] %m%n
# File 输出 一天一个文件,输出路径可以定制,一般在根路径下
log4j.appender.R=org.apache.log4j.DailyRollingFileAppender
log4j.appender.R.File=log.txt
log4j.appender.R.MaxFileSize=500KB
log4j.appender.R.MaxBackupIndex=10
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c] [%p] - %m%n
其中,开发者可设置rootLogger,开发调试环境时,可设置为DEBUG模式,输出系统的详细日志;生产环境时,可设置为INFO模式。
2.2、以下是引入SpringMVC所必需的依赖包,由于maven自动依赖功能,引入以下包时,会自动引入其他相关的依赖包,其中就包括spring-core,spring-context等重要的包(注意只要能够引入所有的Spring依赖包即可)。
1 <!--spring相关包--> 2 <dependency> 3 <groupId>org.springframework</groupId> 4 <artifactId>spring-web</artifactId> 5 <version>4.3.8.RELEASE</version> 6 </dependency> 7 <dependency> 8 <groupId>org.springframework</groupId> 9 <artifactId>spring-webmvc</artifactId> 10 <version>4.3.8.RELEASE</version> 11 </dependency> 12 <dependency> 13 <groupId>org.springframework</groupId> 14 <artifactId>spring-context-support</artifactId> 15 <version>4.3.8.RELEASE</version> 16 </dependency> 17 <dependency> 18 <groupId>org.springframework</groupId> 19 <artifactId>spring-oxm</artifactId> 20 <version>4.3.8.RELEASE</version> 21 </dependency> 22 <dependency> 23 <groupId>org.springframework</groupId> 24 <artifactId>spring-tx</artifactId> 25 <version>4.3.8.RELEASE</version> 26 </dependency> 27 <dependency> 28 <groupId>org.springframework</groupId> 29 <artifactId>spring-test</artifactId> 30 <version>4.3.8.RELEASE</version> 31 </dependency> 32 <dependency> 33 <groupId>org.springframework</groupId> 34 <artifactId>spring-jdbc</artifactId> 35 <version>4.3.8.RELEASE</version> 36 </dependency> 37 <!--aspectj start--> 38 <dependency> 39 <groupId>org.aspectj</groupId> 40 <artifactId>aspectjweaver</artifactId> 41 <version>1.8.10</version> 42 </dependency> 43 <dependency> 44 <groupId>org.aspectj</groupId> 45 <artifactId>aspectjrt</artifactId> 46 <version>1.8.10</version> 47 </dependency>
2.3、以下是引入JSP的包,以支持jsp视图的功能。
1 <!-- 支持jsp --> 2 <dependency> 3 <groupId>javax.servlet</groupId> 4 <artifactId>jstl</artifactId> 5 <version>1.2</version> 6 </dependency> 7 <dependency> 8 <groupId>taglibs</groupId> 9 <artifactId>standard</artifactId> 10 <version>1.1.2</version> 11 </dependency> 12 <!--servlet/jsp api start--> 13 <dependency> 14 <groupId>javax.servlet</groupId> 15 <artifactId>servlet-api</artifactId> 16 <version>3.0-alpha-1</version> 17 </dependency> 18 <dependency> 19 <groupId>javax.servlet.jsp</groupId> 20 <artifactId>jsp-api</artifactId> 21 <version>2.2.1-b03</version> 22 </dependency>
2.4、以下是引入datasource数据源的包,引入了2种方式,但本次项目中,我们使用的是alibaba的Druid DataSource。
1 <!-- JDBC连接池 --> 2 <dependency> 3 <groupId>com.mchange</groupId> 4 <artifactId>c3p0</artifactId> 5 <version>0.9.5.2</version> 6 </dependency> 7 <!-- DruidDataSource,本工程的dataSource配置使用的Druid --> 8 <dependency> 9 <groupId>com.alibaba</groupId> 10 <artifactId>druid</artifactId> 11 <version>1.0.29</version> 12 </dependency>
到此,我们已经引入了mysql驱动包,DataSource连接池,我们还需要配置jdbc属性文件,在resources下创建jdbc.properties文件,内容如下:
#JDBC Global Setting #jdbc.driver=com.mysql.jdbc.Driver jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/charlie_web_demo?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=GMT%2B8 jdbc.username=root jdbc.password=p@ssw0rd ##DataSource Global Setting #配置初始化大小、最小、最大 ds.initialSize=1 ds.minIdle=1 ds.maxActive=20 #配置获取连接等待超时的时间 ds.maxWait=60000 #配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 ds.timeBetweenEvictionRunsMillis=60000 #配置一个连接在池中最小生存的时间,单位是毫秒 ds.minEvictableIdleTimeMillis=300000
其中,配置了mysql数据库的连接,用户名,密码等信息。由于使用了最新版本的mysql-connector-java-6.0.6驱动,这要求driver和url的特殊配置,否则项目启动时会报1个警告和2个异常。jdbc.driver配置为com.mysql.cj.jdbc.Driver,可以避免出现警告信息(具体警告信息没有记录下来,大家配置过程中可能会遇到);jdbc.url里添加useSSL属性---配置为true;若出现The server time zone value ‘?й???????’ is unrecognized or represents more than one time zone的错误,则需添加serverTimezone属性,设置为GMT%2B8即可解决。
2.5、以下是引入一些其他可能需要的包,包括文件上传等。
1 <!--其他需要的包--> 2 <dependency> 3 <groupId>org.apache.commons</groupId> 4 <artifactId>commons-lang3</artifactId> 5 <version>3.5</version> 6 </dependency> 7 <dependency> 8 <groupId>commons-fileupload</groupId> 9 <artifactId>commons-fileupload</artifactId> 10 <version>1.3.2</version> 11 </dependency>
3、配置pom.xml文件,设置项目的编译属性,编译后的war包名称(即finalName),存放配置或属性文件到resources目录下。
1 <build> 2 <finalName>CharlieWebDemo</finalName> 3 <resources> 4 <!--表示把java目录下的有关xml文件,properties文件编译/打包的时候放在resource目录下--> 5 <resource> 6 <directory>${basedir}/src/main/java</directory> 7 <includes> 8 <include>**/*.properties</include> 9 <include>**/*.xml</include> 10 </includes> 11 </resource> 12 <resource> 13 <directory>${basedir}/src/main/resources</directory> 14 </resource> 15 </resources> 16 </build>
经过以上的pom.xml的配置,项目及springmvc所需的依赖包就已经基本引入了,通过IDEA右侧栏中的maven projects的reimport按钮实现依赖包的引入下载(并且,其中常用的是clean,package,install等,clean可以清除生成的target目录,install可以重新生成target目录),若pom.xml种引入/修改了新的依赖包/插件或者Dependencies存在红色提示,需要重新reimport以下。
4、配置项目的web.xml文件,包括首页,异常跳转,会话超时,字符编码过滤器CharacterEncodingFilter,监听器ContextLoaderListener,前置控制器DispatcherServlet,静态文件单独处理等等。代码及解释如下:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> <display-name>Archetype Created Web Application</display-name> <!--welcome pages--> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <!--当系统出现404错误,跳转到页面NoPage.html--> <error-page> <error-code>404</error-code> <location>/WEB-INF/NoPage.html</location> </error-page> <!--当系统出现java.lang.NullPointerException,跳转到页面error.html--> <error-page> <exception-type>java.lang.NullPointerException</exception-type> <location>/WEB-INF/error.html</location> </error-page> <!--会话超时配置,单位分钟--> <session-config> <session-timeout>360</session-timeout> </session-config> <!--Spring框架给我们提供过滤器CharacterEncodingFilter 这个过滤器就是针对于每次浏览器请求进行过滤的,然后再其之上添加了父类没有的功能即处理字符编码。 其中encoding用来设置编码格式,forceEncoding用来设置是否理会 request.getCharacterEncoding()方法,设置为true则强制覆盖之前的编码格式。--> <filter> <filter-name>characterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>characterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 项目中使用Spring时,applicationContext.xml配置文件中并没有BeanFactory,要想在业务层中的class文件中直接引用Spring容器管理的bean可通过以下方式--> <!--1、在web.xml配置监听器ContextLoaderListener。ContextLoaderListener的作用就是启动Web容器时,自动装配ApplicationContext的配置信息。因为它实现了ServletContextListener这个接口,在web.xml配置这个监听器,启动容器时,就会默认执行它实现的方法。 在ContextLoaderListener中关联了ContextLoader这个类,所以整个加载配置过程由ContextLoader来完成。--> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!--2、部署applicationContext的xml文件。如果在web.xml中不写任何参数配置信息,默认的路径是"/WEB-INF/applicationContext.xml, 在WEB-INF目录下创建的xml文件的名称必须是applicationContext.xml。 如果是要自定义文件名可以在web.xml里加入contextConfigLocation这个context参数: 在<param-value> </param-value>里指定相应的xml文件名,如果有多个xml文件,可以写在一起并以“,”号分隔,也可以这样applicationContext-*.xml采用通配符,匹配的文件都会一同被载入。 在ContextLoaderListener中关联了ContextLoader这个类,所以整个加载配置过程由ContextLoader来完成。--> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <!--如果你的DispatcherServlet拦截"/",为了实现REST风格,拦截了所有的请求,那么同时对*.js,*.jpg等静态文件的访问也就被拦截了。--> <!--方案一:激活Tomcat的defaultServlet来处理静态文件--> <!--要写在DispatcherServlet的前面,让defaultServlet先拦截请求,这样请求就不会进入Spring了--> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.css</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.swf</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.gif</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.jpg</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.png</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.js</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.html</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.xml</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.json</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.map</url-pattern> </servlet-mapping> <!--使用Spring MVC,配置DispatcherServlet是第一步。DispatcherServlet是一个Servlet,,所以可以配置多个DispatcherServlet--> <!--DispatcherServlet是前置控制器,配置在web.xml文件中的。拦截匹配的请求,Servlet拦截匹配规则要自已定义,把拦截下来的请求,依据某某规则分发到目标Controller(我们写的Action)来处理。--> <!--配置SpringMVC DispatcherServlet--> <servlet> <!--在DispatcherServlet的初始化过程中,框架会在web应用的 WEB-INF文件夹下寻找名为[servlet-name]-servlet.xml 的配置文件,生成文件中定义的bean。--> <servlet-name>springMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <!--指明了配置文件的文件名,不使用默认配置文件名,而使用spring-mvc.xml配置文件。--> <param-name>contextConfigLocation</param-name> <!--其中<param-value>**.xml</param-value> 这里可以使用多种写法--> <!--1、不写,使用默认值:/WEB-INF/<servlet-name>-servlet.xml--> <!--2、<param-value>/WEB-INF/classes/dispatcher-servlet.xml</param-value>--> <!--3、<param-value>classpath*:dispatcher-servlet.xml</param-value>--> <!--4、多个值用逗号分隔--> <param-value>classpath:spring-mvc.xml</param-value> </init-param> <!--是启动顺序,让这个Servlet随Servlet容器一起启动。--> <load-on-startup>1</load-on-startup> <async-supported>true</async-supported> </servlet> <servlet-mapping> <!--这个Servlet的名字是dispatcher,可以有多个DispatcherServlet,是通过名字来区分的。每一个DispatcherServlet有自己的WebApplicationContext上下文对象。同时保存的ServletContext中和Request对象中.--> <!--ApplicationContext是Spring的核心,Context我们通常解释为上下文环境,我想用“容器”来表述它更容易理解一些,ApplicationContext则是“应用的容器”了,Spring把Bean放在这个容器中,在需要的时候,用getBean方法取出--> <servlet-name>springMVC</servlet-name> <!--Servlet拦截匹配规则可以自已定义,当映射为@RequestMapping("/user/add")时,为例,拦截哪种URL合适?--> <!--1、拦截*.do、*.htm, 例如:/user/add.do,这是最传统的方式,最简单也最实用。不会导致静态文件(jpg,js,css)被拦截。--> <!--2、拦截/,例如:/user/add,可以实现现在很流行的REST风格。很多互联网类型的应用很喜欢这种风格的URL。弊端:会导致静态文件(jpg,js,css)被拦截后不能正常显示。 --> <url-pattern>/</url-pattern><!--会拦截URL中带“/”的请求。--> </servlet-mapping> </web-app>
5、由web.xml的配置中看出,我们还需要配置applicationContext.xml和spring-mvc.xml,我们在spring-mvc.xml中配置springmvc的相关配置,如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!--启用spring的一些annotation --> <context:annotation-config/> <!-- 自动扫描该包,使SpringMVC认为包下用了@controller注解的类是控制器 --> <context:component-scan base-package="com.charlie.controller"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> <!--扫描service--> <context:component-scan base-package="com.charlie.service"/> <!--HandlerMapping 无需配置,SpringMVC可以默认启动--> <!--静态资源映射--> <!--本项目把静态资源放在了WEB-INF的子目录下,资源映射如下--> <mvc:resources mapping="/css/**" location="/css/"/> <mvc:resources mapping="/js/**" location="/js/"/> <mvc:resources mapping="/image/**" location="/image/"/> <!-- 配置注解驱动 可以将request参数与绑定到controller参数上 --> <mvc:annotation-driven/> <!-- 对模型视图名称的解析,即在模型视图名称添加前后缀(如果最后一个还是表示文件夹,则最后的斜杠不要漏了) 使用JSP--> <!-- 默认的视图解析器在上边的解析错误时使用 (默认使用html)- --> <bean id="defaultViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/> <!--设置JSP文件的目录位置--> <property name="prefix" value="/WEB-INF/views/"/> <property name="suffix" value=".jsp"/> </bean> <!-- SpringMVC文件上传需要配置的节点--> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="maxUploadSize" value="20971500"/> <property name="defaultEncoding" value="UTF-8"/> <property name="resolveLazily" value="true"/> </bean> <!-- 支持返回json,用来处理json格式转换,避免IE执行ajax时,返回json出现下载文件 --> <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> <property name="messageConverters"> <list > <ref bean="mappingJacksonHttpMessageConverter" /> </list> </property> </bean> <bean id="mappingJacksonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <property name="supportedMediaTypes"> <list> <value>application/json;charset=UTF-8</value> </list> </property> </bean> </beans>
其中:
注解配置:<context:annotation-config/>;
注解驱动:<mvc:annotation-driven/>;
自动扫描controller类,自动扫描service类:context:component-scan;
静态资源映射(我们springmvc配置的是拦截“/”,例如:/user/getUserInfo,REST风格,但会导致静态文件(jpg,js,css等)被拦截后不能正常显示):mvc:resources;
模型视图名称的解析(默认路径及后缀):InternalResourceViewResolver;
支持返回json格式(前后端分离场景下,返回数据格式):AnnotationMethodHandlerAdapter,MappingJackson2HttpMessageConverter。通常在前后端分离项目中,后台要向前端返回json格式的相应数据,以上已经在spring-mvc.xml中配置了json格式转换的处理,我们仍需要引入json支持的依赖包,在pom.xml文件中添加以下:
<!-- 支持json,旧包 --> <dependency> <groupId>org.codehaus.jackson</groupId> <artifactId>jackson-core-asl</artifactId> <version>1.9.13</version> </dependency> <dependency> <groupId>org.codehaus.jackson</groupId> <artifactId>jackson-mapper-asl</artifactId> <version>1.9.13</version> </dependency> <!-- 使用MappingJackson2HttpMessageConverter,加入最新的FastJackson依赖 --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.9.0.pr3</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.0.pr3</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.9.0.pr3</version> </dependency> <!-- google的json格式支持 --> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.8.0</version> </dependency>
我们注意到,json依赖包存在旧包和新包,由于本项目使用的spring4.3.8,所以mappingJacksonHttpMessageConverter使用的是MappingJackson2HttpMessageConverter,代替了MappingJacksonHttpMessageConverter,否则项目启动时会报错;而使用MappingJackson2HttpMessageConverter时,我们就需要引入新的json依赖包了,如以上程序注释所示(jackson-core,jackson-databind,jackson-annotations)。我们还添加了google的json格式依赖包Gson,Gson提供了fromJson()
和toJson()
两个直接用于解析和生成的方法,前者实现反序列化,后者实现了序列化,后续的json数据返回程序示例中会再次提及。
6、配置applicationContext.xml文件,在这里我们配置数据源以及与mybatis的整合配置,内容如下所示:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation=" http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 自动扫描SpringMVC包 ,将带有注解的类 纳入spring容器管理 --> <context:component-scan base-package="com.charlie"/> <!-- 引入jdbc配置文件 --> <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <list> <value>classpath*:jdbc.properties</value> </list> </property> </bean> <!-- dataSource 配置 --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <!-- 基本属性 url、user、password --> <property name="driverClassName" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> <!-- 配置初始化大小、最小、最大 --> <property name="initialSize" value="${ds.initialSize}"/> <property name="minIdle" value="${ds.minIdle}"/> <property name="maxActive" value="${ds.maxActive}"/> <!-- 配置获取连接等待超时的时间 --> <property name="maxWait" value="${ds.maxWait}"/> <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 --> <property name="timeBetweenEvictionRunsMillis" value="${ds.timeBetweenEvictionRunsMillis}"/> <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 --> <property name="minEvictableIdleTimeMillis" value="${ds.minEvictableIdleTimeMillis}"/> <property name="validationQuery" value="SELECT 'x'"/> <property name="testWhileIdle" value="true"/> <property name="testOnBorrow" value="false"/> <property name="testOnReturn" value="false"/> <!-- 打开PSCache,并且指定每个连接上PSCache的大小 --> <property name="poolPreparedStatements" value="false"/> <property name="maxPoolPreparedStatementPerConnectionSize" value="20"/> <!-- 配置监控统计拦截的filters --> <property name="filters" value="stat"/> </bean> <!-- mybatis文件配置,扫描所有mapper.xml文件 --> <!-- 配置mybatisSqlSessionFactoryBean --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="configLocation" value="classpath:mybatis.xml"/> <property name="mapperLocations" value="classpath*:com/charlie/dao/xml/*Mapper.xml"/> </bean> <!-- 配置SqlSessionTemplate --> <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate"> <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory" /> </bean> <!-- 配置mybatis mapper接口,扫描所有dao --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.charlie.dao"/> <property name="sqlSessionTemplateBeanName" value="sqlSessionTemplate"/> </bean> <!-- 事务管理 通知 --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <!-- 对insert,update,delete 开头的方法进行事务管理,只要有异常就回滚 --> <tx:method name="insert*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/> <tx:method name="update*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/> <tx:method name="delete*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/> <!-- select,count开头的方法,开启只读,提高数据库访问性能 --> <tx:method name="select*" read-only="true"/> <tx:method name="count*" read-only="true"/> <!-- 对其他方法 使用默认的事务管理 --> <tx:method name="*"/> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="serviceMethods" expression="execution(* com.charlie.service..*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethods"/> </aop:config> <!-- 配置使Spring采用CGLIB代理 --> <aop:aspectj-autoproxy proxy-target-class="true"/> <!-- 对dataSource 数据源进行事务管理 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" p:dataSource-ref="dataSource"/> <!-- 使用annotation注解方式配置事务,启用对事务注解的支持 --> <tx:annotation-driven transaction-manager="transactionManager"/> </beans>
在这里,我们引入了之前配置的jdbc.properties属性文件来配置我们的DataSource;配置了对dataSource 数据源进行事务管理;使用annotation注解方式配置事务,启用对事务注解的支持;
注意在这里我们添加了对mybatis的设置,实现springmvc与mybatis的整合,配置可扫描所有mapper.xml和mapper dao文件以及对事务的管理。
Springmvc整合mybatis,需要引入mybatis包,在pom.xml中添加以下依赖包:
<!--mybatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.4</version> </dependency> <!--mybatis spring整合--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.1</version> </dependency>
我们如果想使用mybatis逆向工程插件生成mybatis的mapper,entity等代码,则需要在之前的<build>标签下添加mybatis-generator插件:
<build> <plugins> <!--mybatis 逆向工程插件--> <plugin> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-maven-plugin</artifactId> <version>1.3.5</version> <configuration> <verbose>true</verbose> <overwrite>true</overwrite> </configuration> </plugin> </plugins> </build>
添加后,我们reimport下,会在maven project下新增出相应的插件,我们之后会使用到mybatis-generator:generate自动生成实体类,mapper文件,如下图:
7、在applicationContext.xml的配置中看出,我们仍需要配置mybatis.xml的文件,进行mybatis相关的配置,如下所示:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!-- 配置mybatis的缓存,延迟加载等等一系列属性 --> <settings> <!-- 该配置影响的所有映射器中配置的缓存的全局开关。默认true --> <setting name="cacheEnabled" value="true" /> <!-- 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态。默认false --> <setting name="lazyLoadingEnabled" value="true" /> <!-- 是否允许单一语句返回多结果集(需要兼容驱动)。 默认true --> <setting name="multipleResultSetsEnabled" value="true" /> <!-- 使用列标签代替列名。不同的驱动在这方面会有不同的表现, 具体可参考相关驱动文档或通过测试这两种不同的模式来观察所用驱动的结果。默认true --> <setting name="useColumnLabel" value="true" /> <!-- 允许 JDBC 支持自动生成主键,需要驱动兼容。 如果设置为 true 则这个设置强制使用自动生成主键,尽管一些驱动不能兼容但仍可正常工作(比如 Derby)。 默认false --> <setting name="useGeneratedKeys" value="false" /> <!-- 指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示取消自动映射;PARTIAL 只会自动映射没有定义嵌套结果集映射的结果集。 FULL 会自动映射任意复杂的结果集(无论是否嵌套)。 默认 PARTIAL --> <setting name="autoMappingBehavior" value="PARTIAL" /> <setting name="autoMappingUnknownColumnBehavior" value="WARNING" /> <!-- 配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(prepared statements); BATCH 执行器将重用语句并执行批量更新。默认SIMPLE --> <setting name="defaultExecutorType" value="SIMPLE" /> <!-- 设置超时时间,它决定驱动等待数据库响应的秒数。Not Set (null) --> <setting name="defaultStatementTimeout" value="25" /> <!-- 为驱动的结果集获取数量(fetchSize)设置一个提示值。此参数只可以在查询设置中被覆盖。 --> <setting name="defaultFetchSize" value="100" /> <!-- 允许在嵌套语句中使用分页(RowBounds)。 If allow, set the false. --> <setting name="safeRowBoundsEnabled" value="false" /> <!-- 是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。 --> <setting name="mapUnderscoreToCamelCase" value="false" /> <!-- MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。 默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 的不同调用将不会共享数据。 --> <setting name="localCacheScope" value="SESSION" /> <!-- 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。 --> <setting name="jdbcTypeForNull" value="OTHER" /> <!-- 指定哪个对象的方法触发一次延迟加载。 --> <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString" /> </settings> </configuration>
OK,到目前为止,该web-demo项目的环境搭建就基本完成了。
三、web实例
webdemo环境搭建完成后,我们进行程序的实例,实现springmvc+mybatis的逻辑层级,数据库的访问及视图展示等。
示例数据准备:在数据库中创建表user,并插入一条数据:
CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(32) NOT NULL COMMENT '用户名称', `birthday` date DEFAULT NULL COMMENT '生日', `sex` char(1) DEFAULT NULL COMMENT '性别', `address` varchar(256) DEFAULT NULL COMMENT '地址', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8;
1、通过mybatis-generator插件,生成User表相应的实体类,mapper文件,我们首先需要在resources下创建一个generatorConfig.xml文件,内容如下(注意xml中&特殊字符的转义,使用&表示):
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"> <generatorConfiguration> <classPathEntry location="F:\IdeaProjects\MavenRepositoryForCharlie\mysql\mysql-connector-java\6.0.6\mysql-connector-java-6.0.6.jar"/> <context id="testTables" targetRuntime="MyBatis3" > <commentGenerator> <!-- 是否去除自动生成的注释 true:是 : false:否 --> <property name="suppressAllComments" value="true" /> </commentGenerator> <!--数据库连接的信息:驱动类、连接地址、用户名、密码 --> <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/charlie_web_demo?characterEncoding=UTF-8&useSSL=true&serverTimezone=GMT%2B8" userId="root" password="p@ssw0rd"> </jdbcConnection> <!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL 和 NUMERIC 类型解析为java.math.BigDecimal --> <javaTypeResolver> <property name="forceBigDecimals" value="false" /> </javaTypeResolver> <!-- targetProject:生成PO类的位置 --> <javaModelGenerator targetPackage="com.charlie.entity" targetProject="src\main\java"> <!-- enableSubPackages:是否让schema作为包的后缀 --> <property name="enableSubPackages" value="false" /> <!-- 从数据库返回的值被清理前后的空格 --> <property name="trimStrings" value="true" /> </javaModelGenerator> <!-- targetProject:mapper映射文件生成的位置 --> <sqlMapGenerator targetPackage="com.charlie.dao.xml" targetProject="src\main\java"> <!-- enableSubPackages:是否让schema作为包的后缀 --> <property name="enableSubPackages" value="false" /> </sqlMapGenerator> <!-- targetPackage:mapper接口生成的位置 --> <javaClientGenerator type="XMLMAPPER" targetPackage="com.charlie.dao.mapper" targetProject="src\main\java"> <!-- enableSubPackages:是否让schema作为包的后缀 --> <property name="enableSubPackages" value="false" /> </javaClientGenerator> <!-- 指定数据库表 --> <table tableName="user"></table> <!-- 有些表的字段需要指定java类型 <table schema="" tableName=""> <columnOverride column="" javaType="" /> </table> --> </context> </generatorConfiguration>
我们需要设置相应的mysql驱动包的路径,本demo中使用的是mysql-connector-java-6.0.6.jar;设置数据库连接信息;指定mybatis-generator生成的实体类、mapper映射文件、mapper接口文件的包路径;并指定需要生成的数据库表,本示例演示的是user表。我们通过执行maven projects的mybatis-generator:generate来生成相应的程序,如下图所示:
执行完毕,会生成相应的实体类及dao文件,如下目录结构所示:
2、在controller包下,编写UserController.java类,代码如下:
package com.charlie.controller; import com.charlie.common.GenericController; import com.charlie.entity.User; import com.charlie.service.UserService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.List; @Controller @RequestMapping(value = "/user") public class UserController extends GenericController { private static final Logger logger = LoggerFactory.getLogger(UserController.class); @Autowired private UserService userService; //返回jsp视图展示 @RequestMapping(value = "/getUserModel",method = RequestMethod.GET) public ModelAndView getUsers1(@RequestParam Integer userId) { ModelAndView modelAndView = new ModelAndView(); //调用service方法得到用户列表 List<User> users = userService.getUsers(userId); //将得到的用户列表内容添加到ModelAndView中 modelAndView.addObject("users",users); //设置响应的jsp视图 modelAndView.setViewName("getUsers"); logger.info("===============================成功查询用户列表!"); return modelAndView; } //返回json格式数据,形式1 @RequestMapping(value = "/getUserJson1",method = RequestMethod.GET) @ResponseBody public List getUsers2(@RequestParam Integer userId, HttpServletRequest request, HttpServletResponse response) { //调用service方法得到用户列表 List<User> users = userService.getUsers(userId); logger.info("===============================成功查询用户列表!"); return users; } //返回json格式数据,形式2(自定义了返回的格式) @RequestMapping(value = "/getUserJson2",method = RequestMethod.GET) public void getUsers3(@RequestParam Integer userId, HttpServletRequest request, HttpServletResponse response) { //调用service方法得到用户列表 List<User> users = userService.getUsers(userId); logger.info("===============================成功查询用户列表!"); renderSuccessString(response, users); } }
其中,@controller注解标注本类为controller类;
@RequestMapping注解实现web REST风格请求的映射;
@Autowired用来绑定service实现类;
方法getUsers1实现了JSP视图的解析和展示,由于在spring-mvc.xml中已经设置了jsp视图解析的配置,这里modelAndView.setViewName("getUsers")就直接响应的jsp视图为/WEB-INF/views下的getUsers.jsp文件(详见github项目代码)。
由于在spring-mvc.xml中已经配置了支持json数据的处理,这里我们可直接使用@ResponseBody注解就可实现json格式数据的返回,如方法getUsers2所示。
方法getUsers3则使用的Gson的json格式支持,我们在程序中,通过common包下的GenericController类的renderSuccessString等方法结合Result自定义类自定义了返回数据的json内容。
以上3种方法的效果会在后续介绍中展示。
3、在dao-->mapper包下,编写UserMapper.java,@Repository注解标注本类为mapper dao类,本次示例我们直接使用selectByExample方法。
package com.charlie.dao.mapper; import com.charlie.entity.User; import com.charlie.entity.UserExample; import java.util.List; import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; @Repository public interface UserMapper { long countByExample(UserExample example); int deleteByExample(UserExample example); int insert(User record); int insertSelective(User record); List<User> selectByExample(UserExample example); int updateByExampleSelective(@Param("record") User record, @Param("example") UserExample example); int updateByExample(@Param("record") User record, @Param("example") UserExample example); }
dao文件对应的mapper映射文件UserMapper.xml,使用的是mybatis-generator默认生成的,此处不再描述。
4、在service包下编写UserService.java类,并在service-impl包下编写UserServiceImpl.java实现此类的方法,如下:
UserService.java
package com.charlie.service; import com.charlie.entity.User; import java.util.List; public interface UserService { List<User> getUsers(Integer userId); }
UserServiceImpl.java(使用@Service注解,标注为service实现类,并@Autowired绑定相应的UserMapper)
package com.charlie.service.impl; import com.charlie.entity.User; import com.charlie.dao.mapper.UserMapper; import com.charlie.entity.UserExample; import com.charlie.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; @Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; public List<User> getUsers(Integer userId) { UserExample example = new UserExample(); UserExample.Criteria criteria = example.createCriteria(); if (!"".equals(userId == null ? "" : userId)) { criteria.andIdEqualTo(userId); } return userMapper.selectByExample(example); } }
至此,我们的程序示例编写完成,下面我们以REST访问方式,展示下访问的效果。
①测试JSP视图展示效果,访问http://localhost:8080/user/getUserModel?userId=1,效果如下:
②测试原始的@ResponseBody注解的json数据返回格式效果,访问http://localhost:8080/user/getUserJson1?userId=1,效果如下:
注意到,使用这种json方式,birthday将数据库的日期格式转化为了时间戳,通常这不是我们所期望的,我们可以通过特定的方法调整json返回的日期格式转换;也可以通过一下的Gson的方式处理json数据。
③测试Gson自定义了具体的json数据返回格式效果,访问http://localhost:8080/user/getUserJson2?userId=1,效果如下:
提示:以上2种浏览器中格式化的json数据是由JSON View插件实现的。若用户在调试过程中想要达到格式化的json数据展示,可以通过安装Chrome浏览器的插件JSON View实现。
本文,旨在快速搭建一个springmvc+mybatis整合的通用的web-demo,其中springmvc或mybatis深入的使用方法,需要大家再去研究,这里不再赘述。
在github源代码链接里,新添加了本次demo的war包文件CharlieWebDemo.war,感兴趣也可以下载下来运行测试。