项目实战一
这里实战的是我Javaweb项目实战(后端篇)的改写
Javaweb项目实战用到的技术是servlet+vue3
这里用到的是spring+springmvc+mybatis+vue3
项目结构
步骤一:导入需要依赖
XML
<!--mybatis核心-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.16</version>
</dependency>
<!--德鲁伊连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.32</version>
</dependency>
<!--简化实体类的getset-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.32</version>
</dependency>
<!-- 方便使用JdbcTemplate事务也需要它-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>6.0.6</version>
</dependency>
<!--spring context依赖-->
<!--当你引入Spring Context依赖之后,表示将Spring的基础依赖引入了-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.6</version>
</dependency>
<!--junit5测试-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.10.2</version>
</dependency>
<!--spring test依赖快速使用ioc容器-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>6.0.6</version>
<scope>test</scope>
</dependency>
<!--spring aop依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>6.0.6</version>
</dependency>
<!-- 声明式事务依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>6.0.6</version>
</dependency>
<!--springmvc依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>6.0.6</version>
</dependency>
<!--web项目需要servlet-->
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-web-api</artifactId>
<version>9.1.0</version>
<scope>provided</scope>
</dependency>
<!--json-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.1</version>
</dependency>
<!-- jsp需要依赖! jstl-->
<dependency>
<groupId>jakarta.servlet.jsp.jstl</groupId>
<artifactId>jakarta.servlet.jsp.jstl-api</artifactId>
<version>3.0.0</version>
</dependency>
<!--整和需要 加载ioc容器-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>6.0.6</version>
</dependency>
<!--整合需要 spring整合mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>3.0.3</version>
</dependency>
<!--日志-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.5.6</version>
</dependency>
步骤二:配置类
2.1 SpringConfig类
java
package org.example.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
@Configuration
@ComponentScan({"org.example.service"})
@EnableAspectJAutoProxy
@EnableTransactionManagement
public class SpringConfig {
//开始事务注解
@Bean
public TransactionManager transactionManager(DataSource dataSource){
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(dataSource);
return dataSourceTransactionManager;
}
}
2.2 JdbcConfig类
java
package org.example.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import javax.sql.DataSource;
@Configuration
@PropertySource({"classpath:jdbc.properties"})
public class JdbcConfig {
@Value("${jdbc.driverClassName}")
private String driverClassName;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean
public DataSource getDataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
}
2.3 MybatisConfig类
java
package org.example.config;
import com.github.pagehelper.PageInterceptor;
import jakarta.servlet.jsp.jstl.core.Config;
import org.apache.ibatis.logging.slf4j.Slf4jImpl;
import org.apache.ibatis.session.AutoMappingBehavior;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
import java.util.Properties;
@Configuration
public class MybatisConfig {
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
SqlSessionFactoryBean sqlSession= new SqlSessionFactoryBean();
sqlSession.setDataSource(dataSource);
org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
configuration.setLogImpl(Slf4jImpl.class); //日志
configuration.setMapUnderscoreToCamelCase(true); //驼峰映射
configuration.setAutoMappingBehavior(AutoMappingBehavior.FULL);//自动映射 数据库表和java对象
sqlSession.setConfiguration(configuration);
sqlSession.setTypeAliasesPackage("org.example.pojo"); //别名
//分页插件
PageInterceptor pageInterceptor = new PageInterceptor();
Properties properties = new Properties();
properties.setProperty("helperDialect","mysql");
pageInterceptor.setProperties(properties);
sqlSession.addPlugins(pageInterceptor);
return sqlSession;
}
@Bean
public MapperScannerConfigurer mapperScannerConfigurer(){
MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
mapperScannerConfigurer.setBasePackage("org.example.mapper");
return mapperScannerConfigurer;
}
}
2.4 SpringMvcConfig类
java
package org.example.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
@Configuration
@ComponentScan({"org.example.controller"})
@EnableWebMvc
public class SpringMvcConfig implements WebMvcConfigurer {
//全局异常处理
@Override
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
}
//静态资源
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
2.5 JavaConfig类
java
package org.example.config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class JavaConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{JdbcConfig.class, SpringConfig.class, MybatisConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
步骤三:创建日志文件 logback.xml
java
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
<!-- 指定日志输出的位置,ConsoleAppender表示输出到控制台 -->
<appender name="STDOUT"
class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<!-- 日志输出的格式 -->
<!-- 按照顺序分别是:时间、日志级别、线程名称、打印日志的类、日志主体内容、换行 -->
<pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%msg]%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 设置全局日志级别。日志级别按顺序分别是:TRACE、DEBUG、INFO、WARN、ERROR -->
<!-- 指定任何一个日志级别都只打印当前级别和后面级别的日志。 -->
<root level="DEBUG">
<!-- 指定打印日志的appender,这里通过"STDOUT"引用了前面配置的appender -->
<appender-ref ref="STDOUT"/>
</root>
<!-- 根据特殊需求指定局部日志级别,可也是包名或全类名。 -->
<logger name="org.example.mapper" level="DEBUG"/>
</configuration>
步骤四 创建MD5类,全局统一json类
4.1MD5
java
package org.example.util;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class MD5Util {
public static String encrypt(String strSrc) {
try {
char hexChars[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8',
'9', 'a', 'b', 'c', 'd', 'e', 'f' };
byte[] bytes = strSrc.getBytes(); //使用平台的默认字符集将此 String 编码为 byte 序列,并 将结果存储到一个新的 byte 数组中。
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(bytes);
bytes = md.digest();
int j = bytes.length;
char[] chars = new char[j * 2];
int k = 0;
for (int i = 0; i < bytes.length; i++) {
byte b = bytes[i];
chars[k++] = hexChars[b >>> 4 & 0xf];
chars[k++] = hexChars[b & 0xf];
}
return new String(chars);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
throw new RuntimeException("MD5加密出错");
}
}
}
4.2json
java
package org.example.json;
//全局统一响应的JSON格式处理类
public class Result <T>{ //因为不确定返回消息是什么类型 ,所以这里声明了<T>
// 返回码
private Integer code;
// 返回消息
private String message;
// 返回数据
private T data;
public Result(){}
// 返回数据
protected static <T> Result<T> build(T data) { //将data添加进去
Result<T> result = new Result<T>();
if (data != null)
result.setData(data);
return result; //返回一个result对象
}
public static <T> Result<T> build(T body, Integer code, String message) { //
Result<T> result = build(body); // 创建一个result对象,并添加data对象
result.setCode(code);
result.setMessage(message);
return result;
}
public static <T> Result<T> build(T body, ResultCodeEnum resultCodeEnum) {
Result<T> result = build(body); //创建result对象并添加data数据
result.setCode(resultCodeEnum.getCode()); //给响应码添加枚举类中的数据
result.setMessage(resultCodeEnum.getMessage()); //给响应消息添加枚举类中的数据
return result;
}
/**
* 操作成功
* @param data baseCategory1List
* @param <T>
* @return
*/
public static<T> Result<T> ok(T data){ //返回一个result对象,这是一个操作成功的数据,如果操作成功就执行它
Result<T> result = build(data); //创建result对象并添加data数据
return build(data, ResultCodeEnum.SUCCESS); //调用上面的方法,将枚举类中的ResultCodeEnum.SUCCESS赋值给响应码和响应消息并返回result
}
public Result<T> message(String msg){
this.setMessage(msg);
return this;
}
public Result<T> code(Integer code){
this.setCode(code);
return this;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
java
package org.example.json;
public enum ResultCodeEnum {
SUCCESS(200,"success"),
USERNAEM_ERROR(501,"usernameError"),
PASSWORD_ERROR(503,"passwordError"),
NOTLOGIN(504,"notlogin"),
USERNAME_USED(505,"usernameUsed");
private Integer code;
private String message;
private ResultCodeEnum(Integer code ,String message){
this.code= code;
this.message = message;
}
public Integer getCode() {
return code;
}
public String getMessage() {
return message;
}
}
步骤五:三层架构
mapper层
1.账户表SysUserMapper
java
public interface SysUserMapper {
int add(SysUser sysUser);
SysUser findByUsername(String username);
}
对应的xml文件
java
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace等于mapper接口类的全限定名,这样实现对应 -->
<mapper namespace="org.example.mapper.SysUserMapper">
<insert id="add">
insert into sys_user values(default,#{username},#{userPwd})
</insert>
<select id="findByUsername" resultType="sysUser">
select * from sys_user where username =#{username}
</select>
</mapper>
2.数据表SysSchedule
java
package org.example.mapper;
import org.example.pojo.SysSchedule;
import java.util.List;
public interface SysScheduleMapper {
/**
* 用于向数据中增加一条日程记录
* @param schedule 日程数据以SysSchedule实体类对象形式入参
* @return 返回影响数据库记录的行数, 行数为0说明增加失败,行数大于0说明增加成功
*/
int addSchedule(int uid);
/**
* 查询所有用户的所有日程
* @return 将所有日程放入一个:List<SysSchedule>集合中返回
*/
List<SysSchedule> findAll();
List<SysSchedule> findItemListByUid(int uid);
Integer addDefault(int uid);
Integer updateSchedule(SysSchedule schedule);
Integer removeSchedule(int sid);
}
对应的xml文件
java
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace等于mapper接口类的全限定名,这样实现对应 -->
<mapper namespace="org.example.mapper.SysScheduleMapper">
<insert id="addDefault">
insert into sys_schedule values (DEFAULT,#{uid},'请输入日程',0)
</insert>
<insert id="addSchedule">
insert into sys_schedule values(DEFAULT,#{uid},#{title},#{completed})
</insert>
<update id="updateSchedule">
update sys_schedule set title = #{title} ,completed = #{completed} where sid = #{sid}
</update>
<delete id="removeSchedule">
delete from sys_schedule where sid = #{sid}
</delete>
<select id="findItemListByUid" resultType="org.example.pojo.SysSchedule">
select sid ,uid,title,completed from sys_schedule where uid = #{uid}
</select>
<select id="findAll" resultType="org.example.pojo.SysSchedule">
select * from sys_schedule
</select>
</mapper>
Service层
1.账户表SysUserMapper
java
public interface SysUserService {
int regist(SysUser sysUser);
/**
* 根据用户名获得完整用户信息的方法
* @param username 要查询的用户名
* @return 如果找到了返回SysUser对象,找不到返回null
*/
SysUser findByUsername(String username);
}
实现类
java
@Service
public class SysUserServiceImpl implements SysUserService {
@Autowired
private SysUserMapper sysUserMapper;
@Override
public int regist(SysUser sysUser) {
sysUser.setUserPwd(MD5Util.encrypt(sysUser.getUserPwd()));
return sysUserMapper.add(sysUser);
}
@Override
public SysUser findByUsername(String username) {
return sysUserMapper.findByUsername(username);
}
}
2.数据表SysSchedule
java
public interface SysScheduleService {
List<SysSchedule> findItemListByUid(int uid);
Integer addDefault(int uid);
Integer updateSchedule(SysSchedule schedule);
Integer removeSchedul(int sid);
}
实现类
java
@Service
public class SysScheduleServiceImpl implements SysScheduleService {
@Autowired
private SysScheduleMapper sysScheduleMapper;
@Override
public List<SysSchedule> findItemListByUid(int uid) {
return sysScheduleMapper.findItemListByUid(uid);
}
@Override
public Integer addDefault(int uid) {
return sysScheduleMapper.addDefault(uid);
}
@Override
public Integer updateSchedule(SysSchedule schedule) {
return sysScheduleMapper.updateSchedule(schedule);
}
@Override
public Integer removeSchedul(int sid) {
return sysScheduleMapper.removeSchedule(sid);
}
}
controller层
1.账户表SysUserMapper
java
@Controller
@CrossOrigin
@ResponseBody
@RequestMapping("user")
@Slf4j
public class SysUserController {
@Autowired
private SysUserService service;
@RequestMapping("checkUsernameUsed")
public Result checkUsernameUsed(String username) {
SysUser sysUser = service.findByUsername(username);
Result result = Result.ok(null);
if (sysUser != null) {
result = Result.build(null, ResultCodeEnum.USERNAME_USED);
}
log.info("用户名是否被占用:{}", result);
return result;
}
@RequestMapping(value = "login",method = RequestMethod.POST)
public Result login(@RequestBody SysUser sysUser) {
//2 调用服务层方法,根据用户名查询用户信息
SysUser loginUser = service.findByUsername(sysUser.getUsername());
Result result = null;
if (null == loginUser) {
result = Result.build(null, ResultCodeEnum.USERNAEM_ERROR);
} else if (!MD5Util.encrypt(sysUser.getUserPwd()).equals(loginUser.getUserPwd())) {
result = Result.build(null, ResultCodeEnum.PASSWORD_ERROR);
} else {
// 登录程序,将用户uid和username响应给客户端
Map data = new HashMap();
loginUser.setUserPwd("");
data.put("loginUser", loginUser);
result = Result.ok(data);
}
log.info("用户名是否被占用:{}", result);
// 3将登录结果响应给客户端
return result;
}
@RequestMapping("regist")
public Result regist(@RequestBody SysUser sysUser){
int regist = service.regist(sysUser);
Result result = Result.ok(null);
if (regist < 1) {
result = Result.build(null, ResultCodeEnum.USERNAME_USED);
}
log.info("用户注册:{}", result);
return result;
}
@RequestMapping("select")
public Result select(String username) {
SysUser sysUser = service.findByUsername(username);
Result result = Result.ok(null);
if (sysUser != null) {
result = Result.build(null, ResultCodeEnum.USERNAME_USED);
}
log.info("用户名是否被占用:{}", result);
return result;
}
}
2.数据表SysSchedule
java
@Slf4j
@CrossOrigin
@ResponseBody
@Controller
@RequestMapping("schedule")
public class SysScheduleController {
@Autowired
private SysScheduleService service;
@RequestMapping("removeSchedule")
public Result removeSchedule(int sid){
Integer i = service.removeSchedul(sid);
if(i<0){
return Result.build(null, ResultCodeEnum.NOTLOGIN);
}else {
return Result.ok(null);
}
}
@RequestMapping(value = "updateSchedule",method = RequestMethod.POST)
public Result updateSchedule(@RequestBody SysSchedule schedule){
Integer i = service.updateSchedule(schedule);
if(i<0){
return Result.build(null, ResultCodeEnum.NOTLOGIN);
}else {
return Result.ok(null);
}
}
@RequestMapping("addDefaultSchedule")
public Result addDefaultSchedule(int uid){
Integer i = service.addDefault(uid);
if(i<0){
return Result.build(null, ResultCodeEnum.NOTLOGIN);
}else {
return Result.ok(null);
}
}
@RequestMapping("findAllSchedule")
public Result findAllSchedule(int uid){
List<SysSchedule> itemList = service.findItemListByUid(uid);
// 将用户的所有日程放入一个Result对象
Map data =new HashMap();
data.put("itemList",itemList);
Result result = Result.ok(data);
// 将Result对象转换为json响应给客户端
return result;
}
}
总结
与servlet相比
ssm
简化了响应json的WebUtil
简化了JDBCUtil
简化了全局统一 处理servlet的BaseContoller
简化了跨域CrosFilter的java文件
ssm 使用@CrossOrigin注解就使当前文件可以跨域大大减少了代码量
ssm使用@ResponseBody注解就使响应数据变为json形式
ssm使用@RequestMapping就相当于一个servlet所以无需统一处理servlet