最完整的SSM框架搭建流程

1.新建一个Maven项目
  • 这里以Idea工具为例,打开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>
<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文件目录下右键,然后输入两个包的全路径: c o m . y i x i a o j u n . s s d m d e m o . c o n t r o l l e r \color{blue}{com.yixiaojun.ssdmdemo.controller} com.yixiaojun.ssdmdemo.controller c o m . y i x i a o j u n . s s d m d e m o . s e r v i c e \color{green}{com.yixiaojun.ssdmdemo.service} com.yixiaojun.ssdmdemo.service,这样就可以出现两个子包在同一级下的效果了,然后就可以右键父包,输入其他子包的名字,从而创建出上图中的包结构了。
5.配置项目
  • 当上面的任务都完成了之后,就可以对项目进行配置了,在进行配置之前,我们需要按照下图的文件结构创建几个文件夹和配置文件。
    在这里插入图片描述
6.配置文件的内容如下
  • 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后面的值需要换成自己的数据库地址, j d b c . u s e r n a m e \color{blue}{jdbc.username} jdbc.username后面的值需要换成自己的数据库用户名, j d b c . p a s s w o r d \color{blue}{jdbc.password} 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>
</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</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>
<!--&lt;!&ndash; 设置对打印sql语句的支持 &ndash;&gt;-->
<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
*/
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;
INSERT INTO `course` VALUES ('01', '语文', '02');

INSERT INTO `course` VALUES ('02', '数学', '01');

INSERT INTO `course` VALUES ('03', '英语', '03');
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;
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');
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;
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', '女');
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;
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', '男');
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;
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:xpf

* @Date:2019/7/8

* @Description:

*/

@AllArgsConstructor

@Data

public class ResponseContent {

private String code;
private String message;
private Object contents;
}
  • User.java
package com.yixiaojun.ssmdemo.bean;

import lombok.Data;

/**

* @Author:xpf

* @Date:2019/7/8

* @Description:

*/

@Data

public class User {
int uId;
int role;
}
  • Student.java
package com.yixiaojun.ssmdemo.bean;

import lombok.Data;

/**

* @Author:xpf

* @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");
Useruser=userService.login(map.get("account").toString(),map.get("password").toString());
response.getWriter().print(JsonResolver.getInstance().jsonAssembly(Constant.SUCCESS_CODE,Constant.SUCCESS_MESSAGE,user));
}
}
  • 至此,我们的代码已经全部写好了,但是在将我们的代码跑起来之前,我们还需要配置下Tomcat:
  • 1.首先,在工具栏找到下图中的Edit Configurations,点击进入配置窗口。
    在这里插入图片描述
  • 2.接下来在配置窗口添加Tomcat服务器。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  • 到这里,我们就配置好了Tomcat服务器了,可以愉快地点击工具栏的运行按钮来运行项目了.
    在这里插入图片描述
  • 考虑到有的同学不知道war和war exploded的区别,我在这里简单地解释一下:
  • war模式:将WEB工程以包的形式上传到服务器
  • war exploded模式:将WEB工程以当前文件夹的位置关系上传到服务器
    理 解 \color{blue}{理解}
  • 1)war模式这种可以称之为是发布模式,看名字也知道,这是先打成war包,再发布
  • 2)war exploded模式是直接把文件夹、jsp页面 、classes等等移到Tomcat 部署文件夹(target目录)里面,进行加载部署。因此这种方式支持热部署,一般在开发的时候也是用这种方式。
  • 3)在平时开发的时候,使用热部署的话,应该对Tomcat进行相应的设置,这样的话修改的jsp界面才可以及时的显示出来。
    对 比 \color{blue}{对比}
  • 通过war模式获取项目的相对路径是Tomcat的安装目录所在位置,看出通过war模式是最终打包部署到Tomcat的安装目录的所在位置。
  • 通过war exploded模式获取项目的相对路径是项目target目录的位置,看出通过war模式是最终打包部署到项目target目录的位置。
    结 论 \color{blue}{结论}
  • 两种方式得部署方式是不一样的,因此在获取项目的相对路径的时候得到的结果是不一样的。
8.使用Postman测试接口
  • 将以上工作都做好了之后,我们就可以在Postman测试一下我们写的接口是否可以正常使用了,我们新建一个测试请求,按照下图填好相关的接口URL和参数和请求方式。
    在这里插入图片描述
  • 然后我们点击一下右上角的Send按钮,就可以在下方看见接口返回的数据了。
    在这里插入图片描述
  • 6
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值