SSM框架搭建
1.新建一个Maven项目
打开IDEA,然后File->New->Project ->Maven->Create from archetype
2.在pom.xml中添加项目所需的依赖包
<!-- SpringMVC,包含有spring-aop,spring-beans,spring-context,spring-core,springexpression,
spring-web,spring-webmvc包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.14.RELEASE</version>
</dependency>
<!-- Spring事务包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.3.14.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.14.RELEASE</version>
</dependency>
<!-- Spring事务包 -->
<!--AOP依赖包-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.2</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.2</version>
</dependency>
<!--AOP依赖包-->
<!--AOP的CGLIB动态代理实现包-->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.2</version>
</dependency>
<!-- servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<!-- c3p0 数据库连接池 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<!-- MySQL驱动包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.15</version>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
<!-- mybatis -->
<!-- Gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
3.新建类目录和资源目录
在src下面的main文件夹上右击,New->Directory,新建一个Java目录和一个resources目录
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>
<!-- SLF4J日志.注意版本问题,高版本与java8不兼容 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.9.1</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.9.1</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-web</artifactId>
<version>2.9.1</version>
<scope>runtime</scope>
</dependency>
<!-- SLF4J日志结束 -->
<!--高性能的异步处理框架,提高log4j2并发性能-->
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.4.2</version>
<scope>runtime</scope>
</dependency>
<!-- LomBok,POJO管理框架-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
<scope>provided</scope>
</dependency>
3.新建类目录和资源目录
在src下面的main文件夹上右击,New->Directory,新建一个Java目录和一个resources目录
然后在Java目录上右键,Mark Directory as -> Sources Root
接着在resources目录上右键,Mark Directory as -> Resources Root
最终效果如下图,注意java与resources文件夹的颜色与样式。
如果需要测试文件目录的,还可以在main目录下面创建一个test文件,然后右键test目录,Mark Directory as -> Test Sources Root,类似地,测试的资源目录也是一样,将其标记为Test Resources Root就可以了。
4.新建项目包结构
在java目录右击鼠标,New->Package,新建项目的包名,将需要用到的包名建好,一般是以公司或者组织的组织Id开头,然后在下面细分为controller,service,dao,bean,utils等子包。
项目包结构建好之后整个项目的目录结构如下图:
PS:考虑到初学者可能无法顺利的创建出上面的包结构,这里给出一个小提示,如果要实现上图目录中的两个子包(比如controller和service)在同一级(同一父包,com.yixiaojun.ssdmdemo如)下,则需要在java文件目录下右键,然后输入两个包的全路径:com.yixiaojun.ssdmdemo.controller,com.yixiaojun.ssdmdemo.service,这样就可以出现两个子包在同一级下的效果了,然后就可以右键父包,输入其他子包的名字,从而创建出上图中的包结构了。
5.配置项目
当上面的任务都完成了之后,就可以对项目进行配置了,在进行配置之前,我们需要按照下图的文件结构创建几个文件夹和配置文件。
配置文件的内容如下:
database.properties
jdbc.driverClass=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/sc?
serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=true
jdbc.username=root
jdbc.password=123456
jdbc.maxPoolSize=100
jdbc.minPoolSize=10
jdbc.initialPoolSize=20
jdbc.maxIdleTime=25000
jdbc.acquireIncrement=5
jdbc.maxStatements=5
jdbc.idleConnectionTestPeriod=18000
jdbc.url后面的值需要换成自己的数据库地址,jdbc.username后面的值需要换成自己的数据库用户名,jdbc.password后面的值需要换成自己的用户密码。
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--定义web应用的名称-->
<display-name>ssmdemo</display-name>
<!--指定Spring上下文参数配置文件的路径-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/spring-context.xml</param-value>
</context-param>
<!-- Spring上下文监听器,用来加载Spring的上下文配置并初始化Spring -->
<listener>
<description>启动spring容器</description>
<listenerclass>
org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- LOG4J上下文监听器,用来加载LOG4J2的配置并初始化LOG4J -->
<listener>
<listenerclass>
org.apache.logging.log4j.web.Log4jServletContextListener</listener-class>
</listener>
<!-- 字符编码过滤器,将编码改为UTF-8-->
<filter>
<filter-name>encodingFilter</filter-name>
<filterclass>
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>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- SpringMVC前置控制器,拦截匹配的请求,把拦截下来的请求,根据相应的规则分发到目标
Controller来处理-->
<servlet>
<servlet-name>spring-mvc</servlet-name>
<servletclass>
org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 指定路径SpringMVC上下文配置路径-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/spring-mvc.xml</param-value>
</init-param>
<!-- 随web应用启动而启动 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
spring-context.xml
<servlet-name>spring-mvc</servlet-name>
<!--指定所有请求都通过DispatcherServlet来处理-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
spring-context.xml
<?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:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:property-placeholder
location="classpath:database/database.properties"/>
<!-- 配置c3p0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="minPoolSize" value="${jdbc.minPoolSize}"></property>
<property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property>
<property name="initialPoolSize" value="${jdbc.initialPoolSize}">
</property>
<property name="acquireIncrement" value="${jdbc.acquireIncrement}">
</property>
<property name="maxIdleTime" value="${jdbc.maxIdleTime}"></property>
<property name="maxStatements" value="${jdbc.maxStatements}"></property>
<property name="idleConnectionTestPeriod"
value="${jdbc.idleConnectionTestPeriod}"></property>
<property name="testConnectionOnCheckin" value="false"></property>
<property name="testConnectionOnCheckout" value="true"></property>
</bean>
<!-- 配置事务管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 使用注解来控制事务 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- 配置mybatis, 绑定c3p0-->
<bean id="sqlSessionFactory"
class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
spring-mvc.xml
<property name="configLocation" value="/WEB-INF/mybatis/mybatisconfig.
xml"></property>
<property name="mapperLocations">
<list>
<value>classpath:mapper/*.xml</value>
</list>
</property>
</bean>
<!-- 在会话工厂中取出SqlSessionTemplate对象,用于执行项目的持久化操作 -->
<bean class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
</bean>
<!-- 自动扫描 将Mapper接口生成代理注入到Spring -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--给出需要扫描的接口,注意此处包名要换成自己项目中的Mapper接口所在的包名,如果有多
个包,可以使用分号或者逗号隔开-->
<property name="basePackage" value="com.yixiaojun.ssmdemo.dao">
</property>
</bean>
</beans>
spring-mvc.xml
<?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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--开启Spring MVC注解驱动,它会自动注册DefaultAnnotationHandlerMapping与
AnnotationMethodHandlerAdapter两个bean,-->
<!--这两个bean是Spring MVC为@Controllers分发请求所必须的,即解决了@Controller注解使用的前提配置。-->
<mvc:annotation-driven />
<!--开启跨域功能-->
<!--/**的意思是所有文件夹及里面的子文件夹-->
<!--/*是所有文件夹,不含子文件夹-->
<!--/是web项目的根目录-->
<mvc:cors>
<mvc:mapping path="/**"/>
</mvc:cors>
<!-- 让Spring扫描这个包下所有的类,让标注Spring注解的类生效,此处的包名应改为自己项目的包名 -->
<context:component-scan base-package="com.yixiaojun.ssmdemo" />
<!--为Spring容器中那些配置@Aspect切面的bean创建代理,织入切面,根据运行类选择 JDK 或
CGLIB 代理-->
<aop:aspectj-autoproxy/>
</beans>
mybatis-config.xml
<?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>
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="defaultStatementTimeout" value="3000"/>
<!--开启自动驼峰命名规则映射,如将数据库列名 A_COLUMN 映射成Java属性名aColumn-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!--使用LOG4J2日志-->
<setting name="logImpl" value="LOG4J2"/>
<!-- 指定CGLIB代理 -->
<setting name="proxyFactory" value="CGLIB"/>
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
</configuration>
log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL-->
<!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成
trace时,你会看到log4j2内部各种详细输出-->
<!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数-->
<configuration status="WARN" monitorInterval="1800">
<Properties>
<!-- 日志默认存放的位置,这里设置为项目根路径下,也可指定绝对路径 -->
<!-- ${web:rootDir}是web项目根路径,java项目没有这个变量,需要删掉,否则会报异常 -->
<property name="basePath">${web:rootDir}/log4j2Logs</property>
<!-- <property name="basePath">d:/qfxSpringMVCMybaitsDemoLogs</property>-->
<!-- 控制台默认输出格式,"%-5level":日志级别,"%l":输出完整的错误位置,是小写的L,因为有行号显示,所以影响日志输出的性能 -->
<property name="console_log_pattern">%d{yyyy-MM-dd HH:mm:ss.SSS}[%-5level] %l - %m%n</property>
<!-- 日志文件默认输出格式,不带行号输出(行号显示会影响日志输出性能);%C:大写,类名;%M:方法名;%m:错误信息;%n:换行 -->
<property name="log_pattern">%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level]%C.%M - %m%n</property>
<!-- 日志默认切割的最小单位 -->
<property name="every_file_size">20MB</property>
<!-- 日志默认输出级别 -->
<property name="output_log_level">DEBUG</property>
<!-- 日志默认存放路径(所有级别日志) -->
<property name="rolling_fileName">${basePath}/all.log</property>
<!-- 日志默认压缩路径,将超过指定文件大小的日志,自动存入按"年月"建立的文件夹下面并进行压缩,作为存档 -->
<property name="rolling_filePattern">${basePath}/%d{yyyy-MM}/all-%d{yyyy-MM-dd}-%i.log.gz</property>
<!-- 日志默认同类型日志,同一文件夹下可以存放的数量,不设置此属性则默认为7个 -->
<property name="rolling_max">50</property>
<!-- Info日志默认存放路径(Info级别日志) -->
<property name="info_fileName">${basePath}/info.log</property>
<!-- Info日志默认压缩路径,将超过指定文件大小的日志,自动存入按"年月"建立的文件夹下面并进行压缩,
作为存档 ->
<property name="info_filePattern">${basePath}/%d{yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log.gz</property>
<!-- Info日志默认同一文件夹下可以存放的数量,不设置此属性则默认为7个 -->
<property name="info_max">10</property>
<!-- Warn日志默认存放路径(Warn级别日志) -->
<property name="warn_fileName">${basePath}/warn.log</property>
<!-- Warn日志默认压缩路径,将超过指定文件大小的日志,自动存入按"年月"建立的文件夹下面
并进行压缩,作为存档 -->
<property name="warn_filePattern">${basePath}/%d{yyyy-MM}/warn-%d{yyyy-
MM-dd}-%i.log.gz</property>
<!-- Warn日志默认同一文件夹下可以存放的数量,不设置此属性则默认为7个 -->
<property name="warn_max">10</property>
<!-- Error日志默认存放路径(Error级别日志) -->
<property name="error_fileName">${basePath}/error.log</property>
<!-- Error日志默认压缩路径,将超过指定文件大小的日志,自动存入按"年月"建立的文件夹下面并进行压缩,作为存档 -->
<property name="error_filePattern">${basePath}/%d{yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log.gz</property>
<!-- Error日志默认同一文件夹下可以存放的数量,不设置此属性则默认为7个 -->
<property name="error_max">10</property>
<!-- 控制台显示的日志最低级别 -->
<property name="console_print_level">DEBUG</property>
</Properties>
<!--定义appender -->
<appenders>
<!-- 用来定义输出到控制台的配置 -->
<Console name="Console" target="SYSTEM_OUT">
<!-- 设置控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
<ThresholdFilter level="${console_print_level}" onMatch="ACCEPT"onMismatch="DENY"/>
<!-- 设置输出格式,不设置默认为:%m%n -->
<PatternLayout pattern="${console_log_pattern}"/>
</Console>
<!-- 打印root中指定的level级别以上的日志到文件 -->
<RollingFile name="RollingFile" fileName="${rolling_fileName}"
filePattern="${rolling_filePattern}">
<PatternLayout pattern="${log_pattern}"/>
<SizeBasedTriggeringPolicy size="${every_file_size}"/>
<!-- 设置同类型日志,同一文件夹下可以存放的数量,如果不设置此属性则默认存放7个文件
-->
<DefaultRolloverStrategy max="${rolling_max}" />
<!-- 匹配INFO以及以上级别 -->
<Filters>
<ThresholdFilter level="INFO" onMatch="ACCEPT"
onMismatch="DENY"/>
</Filters>
</RollingFile>
<!-- 打印INFO级别的日志到文件 -->
<RollingFile name="InfoFile" fileName="${info_fileName}"
filePattern="${info_filePattern}">
<PatternLayout pattern="${log_pattern}"/>
<SizeBasedTriggeringPolicy size="${every_file_size}"/>
<DefaultRolloverStrategy max="${info_max}" />
<!-- 匹配INFO级别 -->
<Filters>
<ThresholdFilter level="WARN" onMatch="DENY"
onMismatch="NEUTRAL"/>
<ThresholdFilter level="INFO" onMatch="ACCEPT"
onMismatch="DENY"/>
</Filters>
</RollingFile>
<!-- 打印WARN级别的日志到文件 -->
<RollingFile name="WarnFile" fileName="${warn_fileName}"
filePattern="${warn_filePattern}">
<PatternLayout pattern="${log_pattern}"/>
<SizeBasedTriggeringPolicy size="${every_file_size}"/>
<DefaultRolloverStrategy max="${warn_max}" />
<!-- 匹配WARN级别 -->
<Filters>
<ThresholdFilter level="ERROR" onMatch="DENY"
onMismatch="NEUTRAL"/>
<ThresholdFilter level="WARN" onMatch="ACCEPT"
onMismatch="DENY"/>
</Filters>
</RollingFile>
<!-- 打印ERROR级别的日志到文件 -->
<RollingFile name="ErrorFile" fileName="${error_fileName}"
filePattern="${error_filePattern}">
<PatternLayout pattern="${log_pattern}"/>
<SizeBasedTriggeringPolicy size="${every_file_size}"/>
<DefaultRolloverStrategy max="${error_max}" />
<!-- 匹配ERROR级别 -->
<Filters>
<ThresholdFilter level="FATAL" onMatch="DENY"
onMismatch="NEUTRAL"/>
<ThresholdFilter level="ERROR" onMatch="ACCEPT"
onMismatch="DENY"/>
</Filters>
</RollingFile>
</appenders>
<!--然后定义logger,只有定义了logger并引入的appender,appender才会生效-->
<loggers>
<!--<!– 设置对打印sql语句的支持 –>-->
<logger name="java.sql" level="INFO">
<appender-ref ref="Console"/>
</logger>
<logger name="org.apache.ibatis" level="INFO">
<appender-ref ref="Console"/>
</logger>
<!--建立一个默认的root的logger-->
<root level="${output_log_level}">
<appender-ref ref="RollingFile"/>
<appender-ref ref="Console"/>
<appender-ref ref="InfoFile"/>
<appender-ref ref="WarnFile"/>
<appender-ref ref="ErrorFile"/>
</root>
</loggers>
</configuration>
至此,整个项目的基本配置已经配置好了,如果需要添加其他的功能,则可以根据需求来进行配置。
6.数据库搭建
在正式写我们的代码之前,我们还需要搭建一下数据库,下面给出我的SQL代码:
/*
Navicat MySQL Data Transfer
Source Server : 本机
Source Server Version : 50553
Source Host : localhost:3306
Source Database : sc
Target Server Type : MYSQL
Target Server Version : 50553
File Encoding : 65001
Date: 2019-07-8 09:43:22
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for course
-- ----------------------------
DROP TABLE IF EXISTS `course`;
CREATE TABLE `course` (
`CId` varchar(10) DEFAULT NULL,
`Cname` varchar(10) DEFAULT NULL,
`TId` varchar(10) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
-- ----------------------------
-- Records of course
-- ----------------------------
INSERT INTO `course` VALUES ('01', '语文', '02');
INSERT INTO `course` VALUES ('02', '数学', '01');
INSERT INTO `course` VALUES ('03', '英语', '03');
-- ----------------------------
-- Table structure for sc
-- ----------------------------
DROP TABLE IF EXISTS `sc`;
CREATE TABLE `sc` (
`SId` varchar(10) DEFAULT NULL,
`CId` varchar(10) DEFAULT NULL,
`score` decimal(18,1) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
-- ----------------------------
-- Records of sc
-- ----------------------------
INSERT INTO `sc` VALUES ('01', '01', '80.0');
INSERT INTO `sc` VALUES ('01', '02', '90.0');
INSERT INTO `sc` VALUES ('01', '03', '99.0');
INSERT INTO `sc` VALUES ('02', '01', '70.0');
INSERT INTO `sc` VALUES ('02', '02', '60.0');
INSERT INTO `sc` VALUES ('02', '03', '80.0');
INSERT INTO `sc` VALUES ('03', '01', '80.0');
INSERT INTO `sc` VALUES ('03', '02', '80.0');
INSERT INTO `sc` VALUES ('03', '03', '80.0');
INSERT INTO `sc` VALUES ('04', '01', '50.0');
INSERT INTO `sc` VALUES ('04', '02', '30.0');
INSERT INTO `sc` VALUES ('04', '03', '20.0');
INSERT INTO `sc` VALUES ('05', '01', '76.0');
INSERT INTO `sc` VALUES ('05', '02', '87.0');
INSERT INTO `sc` VALUES ('06', '01', '31.0');
INSERT INTO `sc` VALUES ('06', '03', '34.0');
INSERT INTO `sc` VALUES ('07', '02', '89.0');
INSERT INTO `sc` VALUES ('07', '03', '98.0');
-- ----------------------------
-- Table structure for student
-- ----------------------------
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
`SId` int(11) NOT NULL,
`Uid` int(11) NOT NULL,
`Sname` varchar(10) DEFAULT NULL,
`Sage` datetime DEFAULT NULL,
`Ssex` varchar(10) DEFAULT NULL,
PRIMARY KEY (`SId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
-- ----------------------------
-- Records of student
-- ----------------------------
INSERT INTO `student` VALUES ('1', '1', '赵雷', '1990-01-01 00:00:00', '男');
INSERT INTO `student` VALUES ('2', '3', '钱电', '1990-12-21 00:00:00', '男');
INSERT INTO `student` VALUES ('3', '4', '孙风', '1990-12-20 00:00:00', '男');
INSERT INTO `student` VALUES ('4', '5', '李云', '1990-12-06 00:00:00', '男');
INSERT INTO `student` VALUES ('5', '8', '周梅', '1991-12-01 00:00:00', '女');
INSERT INTO `student` VALUES ('6', '9', '吴兰', '1992-01-01 00:00:00', '女');
INSERT INTO `student` VALUES ('7', '10', '郑竹', '1989-01-01 00:00:00', '女');
INSERT INTO `student` VALUES ('9', '11', '张三丰', '2017-12-20 00:00:00', '女');
INSERT INTO `student` VALUES ('10', '12', '李武', '2017-12-25 00:00:00', '女');
INSERT INTO `student` VALUES ('11', '13', '李洋', '2012-06-06 00:00:00', '女');
INSERT INTO `student` VALUES ('12', '14', '赵六', '2013-06-13 00:00:00', '女');
INSERT INTO `student` VALUES ('13', '15', '孙七', '2014-06-01 00:00:00', '女');
-- ----------------------------
-- Table structure for teacher
-- ----------------------------
DROP TABLE IF EXISTS `teacher`;
CREATE TABLE `teacher` (
`TId` int(10) NOT NULL AUTO_INCREMENT,
`Uid` int(11) NOT NULL,
`Tname` varchar(10) DEFAULT NULL,
`Tage` datetime DEFAULT NULL,
`TSex` varchar(10) DEFAULT NULL,
PRIMARY KEY (`TId`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
-- ----------------------------
-- Records of teacher
-- ----------------------------
INSERT INTO `teacher` VALUES ('1', '2', '张三', '1970-03-01 00:00:00', '男');
INSERT INTO `teacher` VALUES ('2', '6', '李四', '1982-01-01 00:00:00', '女');
INSERT INTO `teacher` VALUES ('3', '7', '王五', '1993-01-01 00:00:00', '男');
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`Uid` int(11) NOT NULL AUTO_INCREMENT,
`account` varchar(20) NOT NULL,
`password` varchar(20) NOT NULL,
`role` int(11) DEFAULT '1' COMMENT '1是学生,2是老师',
PRIMARY KEY (`Uid`),
UNIQUE KEY `account` (`account`)
) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', '123', '123', '1');
INSERT INTO `user` VALUES ('2', '1234', '1234', '2');
INSERT INTO `user` VALUES ('3', '12345', '12345', '1');
INSERT INTO `user` VALUES ('4', '123456', '12345', '1');
INSERT INTO `user` VALUES ('5', 'abc', '1234', '1');
INSERT INTO `user` VALUES ('6', 'abc123', '1234', '2');
INSERT INTO `user` VALUES ('7', 'admin', 'admin', '2');
INSERT INTO `user` VALUES ('8', 'test', 'test', '1');
INSERT INTO `user` VALUES ('9', 'admin123', '1234', '1');
INSERT INTO `user` VALUES ('10', 'zhengzhu', '1234', '1');
INSERT INTO `user` VALUES ('11', 'zhangsanfeng', '1234', '1');
INSERT INTO `user` VALUES ('12', 'liwu', '1234', '1');
INSERT INTO `user` VALUES ('13', 'liyang', '1234', '1');
INSERT INTO `user` VALUES ('14', 'zhaoliu', '1234', '1');
INSERT INTO `user` VALUES ('15', 'sunqi', '1234', '1');
7.写一个登录接口
首先,我们先来看一下项目代码结果,看一下编写一个登录接口需要哪些类。
接下来是具体的代码:
首先是Model层的代码:
ResponseContent.java
package com.yixiaojun.ssmdemo.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
/**
* @Author:yixiaojun
* @Date:2019/7/8
* @Description:
*/
@AllArgsConstructor
@Data
public class ResponseContent {
private String code;
User.java
Student.java
接下来是工具类:
Constant.java
private String message;
private Object contents;
}
User.java
package com.yixiaojun.ssmdemo.bean;
import lombok.Data;
/**
* @Author:yixiaojun
* @Date:2019/7/8
* @Description:
*/
@Data
public class User {
int uId;
int role;
}
Student.java
package com.yixiaojun.ssmdemo.bean;
import lombok.Data;
/**
* @Author:yixiaojun
* @Date:2019/7/8
* @Description:
*/
@Data
public class User {
int uId;
int role;
}
接下来是工具类:
Constant.java
package com.yixiaojun.ssmdemo.utils;
import java.util.HashMap;
import java.util.Map;
/**
* @Author:yixiaojun
* @Date:2019/7/8
* @Description:
*/
public class Constant {
public final static String SUCCESS_CODE = "CO1";
public final static String ERROR_CODE = "E01";
JsonResolver.java
public final static String SUCCESS_MESSAGE = "调用成功";
public final static Map<String,String> SEESION_MAP = new HashMap<>();
}
JsonResolver.java
package com.yixiaojun.ssmdemo.utils;
import com.google.gson.*;
import com.google.gson.reflect.TypeToken;
import com.yixiaojun.ssmdemo.bean.ResponseContent;
import lombok.Cleanup;
import lombok.SneakyThrows;
import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* @Author:yixiaojun
* @Date:2019/7/8
* @Description:
*/
public class JsonResolver {
private final static int BUFFER_SIZE = 4096;
private Gson gson;
private static JsonResolver jsonResolver;
private JsonResolver(){
//根据自己需要将Json解析成的类型来创建一个Gson对象,此处的参数类型为
HashMap<String, Object>
gson = new GsonBuilder().registerTypeAdapter(new
TypeToken<HashMap<String, Object>>() {}.getType(), new
JsonDeserializer<HashMap<String,Object>>() {
@Override
public HashMap<String, Object> deserialize(JsonElement jsonElement,
Type type, JsonDeserializationContext jsonDeserializationContext) throws
JsonParseException {
HashMap<String, Object> hashMap = new HashMap<>(16);
JsonObject jsonObject = jsonElement.getAsJsonObject();
Set<Map.Entry<String,JsonElement>> entrySet =
jsonObject.entrySet();
for (Map.Entry<String,JsonElement> entry :entrySet){
if (entry.getValue() instanceof JsonArray){
hashMap.put(entry.getKey(),entry.getValue());
}else {
hashMap.put(entry.getKey(),entry.getValue().toString().replace("\"",""));
}
}
return hashMap;
}
}).create();
}
/**
* 使用单例模式生成一个对象JsonResolver对象
* @return 返回一个JsonResolver对象
*/
public static JsonResolver getInstance(){
if (jsonResolver == null){
jsonResolver = new JsonResolver();
}
return jsonResolver;
}
/**
* 将request对象里面的参数通过Gson转成HashMap<String, Object>
* @param request
* @return 存放解析之后的参数的map
*/
@SneakyThrows
public HashMap<String, Object> request2HashMap(HttpServletRequest request){
HashMap<String,Object> map = new HashMap<>(16);
String requestParm = null;
requestParm = new String(inputStreamToByte(request),"UTF-8");
requestParm = java.net.URLDecoder.decode(requestParm,"UTF-8");
map = gson.fromJson(requestParm,new TypeToken<HashMap<String,Object>>()
{}.getType());
return map;
}
/**
* 将获取到的输入流转成字节数组
* @param request
* @return
* @throws IOException
*/
@SneakyThrows
private byte[] inputStreamToByte(HttpServletRequest request) {
@Cleanup ByteArrayOutputStream outStream = new ByteArrayOutputStream();
byte[] data = new byte[BUFFER_SIZE];
int count = -1;
while ((count = request.getInputStream().read(data, 0, BUFFER_SIZE)) !=
-1){
outStream.write(data, 0, count);
}
return outStream.toByteArray();
}
/**
* 将要返回给前端数据转换成Json格式
* @param code 标识码,标识接口调用成功或失败
* @param message 提示信息
* @param content 真正要获取的内容
* @return
*/
@SneakyThrows
public String jsonAssembly(String code, String message, Object content) {
ResponseContent responseContent = new
ResponseContent(code,message,content);
LogInterceptor.java
String jsonStr = null;
jsonStr = gson.toJson(responseContent);
return jsonStr;
}
}
LogInterceptor.java
package com.yixiaojun.ssmdemo.utils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
/**
* @Author:yixiaojun
* @Date:2019/7/8
* @Description:
*/
@Slf4j
@Aspect
@Component
public class LogInterceptor {
/**
* 拦截所有的controller包下面的接口(方法),使用了AOP的方法,在调用controller包下的
方法时都会先调用这个方法,经过处理之后才会转发到真正的方法
* @param joinPoint
* @throws Throwable
*/
@Around("execution(* com.yixiaojun.ssmdemo.controller..*.*(..))")
public void process(ProceedingJoinPoint joinPoint) throws Throwable {
String apiName = joinPoint.getTarget().getClass().getName() + "."
+joinPoint.getSignature().getName();
HttpServletRequest request = (HttpServletRequest)joinPoint.getArgs()[0];
//将前端传过来的数据解析成Map
HashMap<String,Object> map =
JsonResolver.getInstance().request2HashMap(request);
HttpServletResponse response = (HttpServletResponse)joinPoint.getArgs()
[1];
//将字符编码改成UTF-8,解决部分浏览器在控制台显示乱码问题
response.setContentType("application/json;charset=UTF-8");
log.info("------接口-----"+ apiName + "()-------start-----");
String loginFunName =
"com.yixiaojun.ssmdemo.controller.UserController.login";
String changePasswordFunName =
"com.yixiaojun.ssmdemo.controller.UserController.changePassword";
String registerFunName =
"com.yixiaojun.ssmdemo.controller.UserController.register";
//如果不是调用登录、修改密码、注册三个接口,就进行一次sessionId验证,验证不通过直接返
回
if (!(loginFunName.equals(apiName) ||
changePasswordFunName.equals(apiName) || registerFunName.equals(apiName))){
String sessionIdKey = "sessionId";
if
(com.yixiaojun.ssmdemo.utils.Constant.SEESION_MAP.get(map.get(sessionIdKey).toSt
ring()) == null){
response.getWriter().print(JsonResolver.getInstance().jsonAssembly(com.yixiaoju
n.ssmdemo.utils.Constant.ERROR_CODE, "调用失败", null));
log.info("------接口-----"+ apiName + "()-------end-----");
return;
}
}
/**
* Spring中request.getInputStream() 和request.getReader()只能被获取一次,
* 而@RequestBody注解参数的底层实现也是通过流来获取请求参数的,
* 所以需要将解析完的参数用一个键值对存起来,以便在控制层直接读取
*/
request.setAttribute("paramMap",map);
Object[] args = new Object[]{request, response};
joinPoint.proceed(args);
log.info("------接口-----"+ apiName + "()-------end-----");
}
}
接着是Dao层的代码:
UserDao.java
package com.yixiaojun.ssmdemo.dao;
import com.yixiaojun.ssmdemo.bean.User;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
/**
* @Author:yixiaojun
* @Date:2019/7/8
* @Description:
*/
@Repository
public interface UserDao {
/**
* 根据帐号密码查询用户
接着是Service层的代码:
UserService.java
UserServiceImpl.java
* @Author:yixiaojun
* @param account 帐号
* @param password 密码
* @return 根据帐号密码查询到的用户信息或者为空
*/
User login(@Param("account") String account, @Param("password") String
password);
}
接着是Service层的代码:
UserService.java
package com.yixiaojun.ssmdemo.service;
import com.yixiaojun.ssmdemo.bean.User;
/**
* @Author:yixiaojun
* @Date:2019/7/8
* @Description:
*/
public interface UserService {
/**
* 根据帐号密码查询用户
* @Author:yixiaojun
* @param account 帐号
* @param password 密码
* @return 根据帐号密码查询到的用户信息
*/
User login(String account, String password);
}
UserServiceImpl.java
package com.yixiaojun.ssmdemo.service;
import com.yixiaojun.ssmdemo.bean.User;
import com.yixiaojun.ssmdemo.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @Author:yixiaojun
* @Date:2019/7/8
* @Description:
*/
@Service(value = "UserServiceImpl")
public class UserServiceImpl implements UserService {
@Autowired
UserDao userDao;
@Override
public User login(String account, String password) {
return userDao.login(account,password);
}
}
最后是控制层的代码:
UserController.java
package com.yixiaojun.ssmdemo.controller;
import com.yixiaojun.ssmdemo.bean.User;
import com.yixiaojun.ssmdemo.service.UserService;
import com.yixiaojun.ssmdemo.utils.Constant;
import com.yixiaojun.ssmdemo.utils.JsonResolver;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
/**
* @Author:yixiaojun
* @Date:2019/7/8
* @Description:
*/
@Controller
@RequestMapping("/user")
public class UserController {
@Resource(name = "UserServiceImpl")
UserService userService;
/**
* 登录接口,用户登录时调用
* @Author:yixiaojun
* @param request 前端传来的参数,参数为Json格式,示例为:
{"account":"1234","password":"1234"},分别代表账号和密码
* @param response
* @throws IOException
*/
@RequestMapping(value = "/login",method = RequestMethod.POST)
public void login(HttpServletRequest request, HttpServletResponse response)
throws IOException {
@SuppressWarnings("unchecked")
HashMap<String,Object> map =
(HashMap<String,Object>)request.getAttribute("paramMap");
User user =
userService.login(map.get("account").toString(),map.get("password").toString());
response.getWriter().print(JsonResolver.getInstance().jsonAssembly(Constant.SUC
CESS_CODE,Constant.SUCCESS_MESSAGE,user));
}
}
至此,我们的代码已经全部写好了,但是在将我们的代码跑起来之前,我们还需要配置一下Tomcat:
1.首先,在工具栏找到下图中的Edit Configurations,点击进入配置窗口。
2.接下来在配置窗口添加Tomcat服务器。
到这里,我们就配置好了Tomcat服务器了,可以愉快地点击工具栏的运行按钮来运行项目了。
考虑到有的同学不知道war和war exploded的区别,我在这里简单地解释一下:
war模式:将WEB工程以包的形式上传到服务器 ;
war exploded模式:将WEB工程以当前文件夹的位置关系上传到服务器
理解:
(1)war模式这种可以称之为是发布模式,看名字也知道,这是先打成war包,再发布;
(2)war exploded模式是直接把文件夹、jsp页面 、classes等等移到Tomcat 部署文件夹(target目
录)里面,进行加载部署。因此这种方式支持热部署,一般在开发的时候也是用这种方式。
(3)在平时开发的时候,使用热部署的话,应该对Tomcat进行相应的设置,这样的话修改的jsp界面
什么的东西才可以及时的显示出来。
对比:
通过war模式获取项目的相对路径是Tomcat的安装目录所在位置,看出通过war模式是最终打包部
署到Tomcat的安装目录的所在位置。
通过war exploded模式获取项目的相对路径是项目target目录的位置,看出通过war模式是最终打
包部署到项目target目录的位置。
结论:
两种方式得部署方式是不一样的,因此在获取项目的相对路径的时候得到的结果是不一样的。
8.使用Postman测试接口
将以上工作都做好了之后,我们就可以在Postman测试一下我们写的接口是否可以正常使用了,我们新建一个测试请求,按照下图填好相关的接口URL和参数和请求方式。
然后我们点击一下右上角的Send按钮,就可以在下方看见接口返回的数据了。