SSM标准整合案例
SSM整合(后端层)
整合配置
整合之前学的Mybatis。spring springmvc框架共同完成后面的项目
1.以web模板创建个maven工程
2.配置相关依赖
3.创建包的层次结构
4.完成基础配置文件配置
spring
@Configuration @ComponentScan({"com.ember.service"}) @PropertySource("jdbc.properties") @Import({JdbcConfig.class,MyBatisConfig.class}) public class SpringConfig { }
jdbc
public class JdbcConfig { @Value("${jdbc.driver}") private String driver; @Value("${jdbc.url}") private String url; @Value("${jdbc.user}") private String username; @Value("${jdbc.password}") private String password; @Bean public DataSource dataSource(){ DruidDataSource dataSource=new DruidDataSource(); dataSource.setDriverClassName(driver); dataSource.setUrl(url); dataSource.setUsername(username); dataSource.setPassword(password); return dataSource; } }
mybatis
public class MyBatisConfig { @Bean public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){ SqlSessionFactoryBean factoryBean=new SqlSessionFactoryBean(); //传入参数datasources(参数的datasources是spring容器根据类型封装好的) factoryBean.setDataSource(dataSource); //扫描要封装的实体类所在文件夹 factoryBean.setTypeAliasesPackage("com.ember.domain"); return factoryBean; } public MapperScannerConfigurer mapperScannerConfigurer(){ MapperScannerConfigurer msc=new MapperScannerConfigurer(); //扫描映射所在文件夹 msc.setBasePackage("com.ember.dao"); return msc; } }
web
如果业务有form表单提交的功能可以直接加上post乱码过滤器
//post乱码处理 @Override protected Filter[] getServletFilters() { //spring-mvc提供的过滤器 CharacterEncodingFilter filter=new CharacterEncodingFilter(); //设置解码 filter.setEncoding("UTF-8"); //传入返回的过滤器,多个过滤器{filter,filter1,.....} return new Filter[]{filter}; }
//替代web.xml的配置类 public class ServletConfig extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class<?>[] getRootConfigClasses() { return new Class[]{SpringConfig.class}; } @Override protected Class<?>[] getServletConfigClasses() { return new Class[]{SpringMvcConfig.class}; } @Override protected String[] getServletMappings() { return new String[]{"/"}; } }
mvc
@Configuration @ComponentScan("com.ember.controller") @EnableWebMvc public class SpringMvcConfig { }
功能模块
造表
DROP TABLE IF EXISTS tbl_book; CREATE TABLE tbl_book ( id integer NOT NULL AUTO_INCREMENT, `type` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 13 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; INSERT INTO `tbl_book` VALUES (1, '计算机理论', 'Spring实战 第5版', 'Spring入门经典教程,深入理解Spring原理技术内幕'); INSERT INTO `tbl_book` VALUES (2, '计算机理论', 'Spring 5核心原理与30个类手写实战', '十年沉淀之作,手写Spring精华思想'); INSERT INTO `tbl_book` VALUES (3, '计算机理论', 'Spring 5 设计模式', '深入Spring源码剖析Spring源码中蕴含的10大设计模式'); INSERT INTO `tbl_book` VALUES (4, '计算机理论', 'Spring MVC+MyBatis开发从入门到项目实战', '全方位解析面向Web应用的轻量级框架,带你成为Spring MVC开发高手'); INSERT INTO `tbl_book` VALUES (5, '计算机理论', '轻量级Java Web企业应用实战', '源码级剖析Spring框架,适合已掌握Java基础的读者'); INSERT INTO `tbl_book` VALUES (6, '计算机理论', 'Java核心技术 卷I 基础知识(原书第11版)', 'Core Java 第11版,Jolt大奖获奖作品,针对Java SE9、10、11全面更新'); INSERT INTO `tbl_book` VALUES (7, '计算机理论', '深入理解Java虚拟机', '5个维度全面剖析JVM,大厂面试知识点全覆盖'); INSERT INTO `tbl_book` VALUES (8, '计算机理论', 'Java编程思想(第4版)', 'Java学习必读经典,殿堂级著作!赢得了全球程序员的广泛赞誉'); INSERT INTO `tbl_book` VALUES (9, '计算机理论', '零基础学Java(全彩版)', '零基础自学编程的入门图书,由浅入深,详解Java语言的编程思想和核心技术'); INSERT INTO `tbl_book` VALUES (10, '市场营销', '直播就该这么做:主播高效沟通实战指南', '李子柒、李佳琦、薇娅成长为网红的秘密都在书中'); INSERT INTO `tbl_book` VALUES (11, '市场营销', '直播销讲实战一本通', '和秋叶一起学系列网络营销书籍'); INSERT INTO `tbl_book` VALUES (12, '市场营销', '直播带货:淘宝、天猫直播从新手到高手', '一本教你如何玩转直播的书,10堂课轻松实现带货月入3W+');
实体类
dao(接口+自动代理)
public interface BookDao { @Insert("INSERT INTO tbl_book VALUES (null,#{type},#{name},#{description})") public void save(Book book); @Update("UPDATE tbl_book SET type=#{type},name=#{name},description=#{description}") public void update(Book book); @Delete("DELETE FROM tbl_book WHERE id=#{id}") public void delete(Integer id); @Select("SELECT * FROM tbl_book WHERE id=#{id}") public Book getById(Integer id); @Select("SELECT * FROM tbl_book") public List<Book> getAll(); }
service(接口+实现类)
没写完这里
@Service public class BookServiceImpl implements BookService { @Autowired private BookDao bookDao; @Override public boolean save(Book book) { bookDao.save(book); return true; } @Override public boolean update(Book book) { bookDao.update(book); return true; } @Override public boolean delete(Integer id) { bookDao.delete(id); return true; } @Override public Book getById(Integer id) { return bookDao.getById(id); } @Override public List<Book> getAll() { return bookDao.getAll(); } }
controller
@RestController @RequestMapping("/books") public class BookController { @Autowired private BookService bookService; @PostMapping public boolean save(@RequestBody Book book) { return bookService.save(book); } @PutMapping public boolean update(@RequestBody Book book) { return bookService.update(book); } @DeleteMapping("/{id}") public boolean delete(@PathVariable Integer id) { return bookService.delete(id); } @GetMapping("/{id}") public Book getById(@PathVariable Integer id) { return bookService.getById(id); } @GetMapping public List<Book> getAll() { return bookService.getAll(); } }
接口测试
业务层接口测试
我这里两次因为mybatis的配置类写错了导致junit无法运行,写测试类之前检查这两个地方
随后完成测试
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = SpringConfig.class) public class BookServiceTest { @Autowired private BookService bookService; @Test public void testGetById(){ Book book= bookService.getById(1); System.out.println(book); } @Test public void GetAll(){ List<Book> bookList= bookService.getAll(); System.out.println(bookList); } }
这里也可以测一下controller层是否能用
要导入tomcat插件这里使用pom文件中导入
<plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> <configuration> <port>80</port> <path>/</path> </configuration> </plugin>
这里出现了一个bug tomcat无法正常启动后来发现
这个地方一定要写classpath或者加一个/
依次写好测试
都能返回对象或者true就成功了
开启注解式事务驱动
springconfig上加@EnableTransactionManagement
jdbcconfig中加上方法
@Bean public PlatformTransactionManager transactionManager(DataSource dataSource){ DataSourceTransactionManager ds=new DataSourceTransactionManager(); ds.setDataSource(dataSource); return ds; }
在service接口上挂上事务标志
@Transactional public interface BookService {
表现层数据封装
我们在controller下创建个结果类
以下只是属性和需要的构造方法,别忘了set get tostring
public class Result { private Object data; private Integer code; private String msg; public Result( Integer code,Object data, String msg) { this.data = data; this.code = code; this.msg = msg; } public Result(Integer code,Object data) { this.data = data; this.code = code; } public Result() { }
code类用于存放状态编码
public class Code { public static final Integer SAVE_OK=20011; public static final Integer DELETE_OK=20021; public static final Integer UPDATE_OK=20031; public static final Integer GET_OK=20041; public static final Integer SAVE_ERR=20010; public static final Integer DELETE_ERR=20020; public static final Integer UPDATE_ERR=20030; public static final Integer GET_ERR=20040; }
这两个类都是需要根据业务进行更改的
更改web层
随后将我们的web层代码全部改成返回值为结果类类型
@RestController @RequestMapping("/books") public class BookController { @Autowired private BookService bookService; @PostMapping public Result save(@RequestBody Book book) { boolean flag= bookService.save(book); return new Result(flag?Code.SAVE_OK:Code.SAVE_ERR,flag); } @PutMapping public Result update(@RequestBody Book book) { boolean flag=bookService.update(book); return new Result(flag?Code.UPDATE_OK:Code.UPDATE_ERR,flag); } @DeleteMapping("/{id}") public Result delete(@PathVariable Integer id) { boolean flag= bookService.delete(id); return new Result(flag?Code.DELETE_OK:Code.DELETE_ERR,flag); }
测试接口是否正常
异常处理
业务中有多种异常,一般将其抛出到表现层进行处理,而且一般要分类处理,代码量大种类多所以可以用AOP
思想,对此mvc提供了专门的异常处理方式,以便统一的,方便的处理异常
当出现异常时,如果后端不使用异常处理器,前端会出现一大堆报错,使用异常处理器过后,出现异常系统会根据你写的处理方式返回或者不返回数据(都是自定义的)
如下在controller下创建异常处理器
//标志异常处理器(该注解一定要被mvc配置类扫描到) @RestControllerAdvice public class ProjectExceptionAdvice { //定义要抓取的异常种类,下面是所有异常 @ExceptionHandler(Exception.class) public Result doException(Exception ex){ //处理方式自己定义 System.out.println("出现异常"); return new Result(666,null,"Controller处出现异常"); } }
项目异常处理方案
在主目录创建个自定义异常包,里面管理自定义异常(异常分类)
代码都是差不多的
//这里本来应该继承Exception,但是每个方法后面都要加serviceSystemException,而RuntimeException默认出现异常可以不处理自动往上抛 public class SystemException extends RuntimeException{ //给定异常编号,帮助识别异常种类 private Integer code; public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } public SystemException(Integer code, String message) { super(message); this.code = code; } public SystemException(Integer code,String message, Throwable cause) { super(message, cause); this.code = code; } }
我们模仿异常并将异常转化为自定义的异常(这里再service实现类中byid方法中模拟异常)
//模仿服务端异常(不规范操作) if(id==1){ throw new BusinessException(Code.BUSICESS_ERR,"请合理操作"); }
这些异常编号都是保存在code类里面的根据需要增加减少
抛出这些异常后我们要在异常处理器中写好对应异常的解决方案
一般分为自定义异常解决方案和其他异常解决方案(根据需求完成代码)
//标志异常处理器(该注解一定要被mvc配置类扫描到) @RestControllerAdvice public class ProjectExceptionAdvice { //定义要抓取的异常种类,下面是所有异常 @ExceptionHandler(SystemException.class) public Result doException(SystemException ex){ //处理方式自己定义 //记录日志 //发送消息给运维 //发送消息给开发人员 return new Result(ex.getCode(),"null",ex.getMessage()); } @ExceptionHandler(BusinessException.class) public Result doException(BusinessException ex){ //处理方式自己定义 //记录日志 //发送消息给运维 //发送消息给开发人员 return new Result(ex.getCode(),"null",ex.getMessage()); } //处理其他异常 @ExceptionHandler(Exception.class) public Result doException(Exception ex){ //处理方式自己定义 //记录日志 //发送消息给运维 //发送消息给开发人员 return new Result(Code.SYSTEM_UNKNOW_ERR,"null","系统繁忙请稍后再试!"); } }
------------------------------
SSM整合(前端层)
导入页面,放行资源
@Configuration public class SpringMvcSupport extends WebMvcConfigurationSupport { @Override protected void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/pages/**").addResourceLocations("/pages/"); registry.addResourceHandler("/js/**").addResourceLocations("/js/"); registry.addResourceHandler("/css/**").addResourceLocations("/css/"); registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/"); } }
我们使用的页面和之前web的页面有点不同,因为现在我们的接口简化了,所以方法全部重新写,且移除了分页功能
列表
methods: { //列表 getAll() { //发送ajax请求 axios.get("/books").then((res)=>{ this.dataList=res.data.data; }) },
添加按钮
更改代码如下完成点击按钮弹窗,提交表单发送请求且关闭弹窗刷新界面
这里要做是否成功的判断,应为后端我们已经根据请求是否成功返回了状态码,所以直接用状态码做判断条件
这里还要改下service层和dao层,原本是无返回值的我们修改数据后应该返回其行记数
//弹出添加窗口 handleCreate() { // 显示弹窗 this.dialogFormVisible=true; this.resetForm(); }, //重置表单 resetForm() { this.formData={}; }, //添加 handleAdd () { // 发送ajax请求 axios.post("/books",this.formData).then((res)=>{ //如果操作成功关闭弹层 if(res.data.code==20011){ this.dialogFormVisible=false; this.$message.success("添加成功"); }else if(res.data.code==20010){ this.$message.error("添加失败"); }else{ this.$message.error(res.data.msg); } }).finally(()=>{ this.getAll(); }) },
修改
重要的是根据点击行的数据获取相应id传入后端查询数据回显到表单上
按钮上绑定了返回行数据的操作
//弹出编辑窗口 handleUpdate(row) { axios.get("/books/"+row.id).then((res)=>{ if(res.data.code==20041){ this.formData=res.data.data; this.dialogFormVisible4Edit=true; }else{ this.$message.error(res.data.msg); } }) },
随后更改的代码和添加基本是一样的(要改请求方法和弹窗名称)
//编辑 handleEdit() { // 发送ajax请求 axios.put("/books",this.formData).then((res)=>{ //如果操作成功关闭弹层 if(res.data.code==20031){ this.dialogFormVisible4Edit=false; this.$message.success("修改成功"); }else if(res.data.code==20030){ this.$message.error("修改失败"); }else{ this.$message.error(res.data.msg); } }).finally(()=>{ this.getAll(); }) },
删除
在修改的基础上要弹出提示框判断是否删除
// 删除 handleDelete(row) { // 弹出提示框 this.$confirm("此操作永久删除该条数据,是否继续?","提示",{ type:'info'}).then(()=>{ // 删除业务 // 发送ajax请求 axios.delete("/books/"+row.id).then((res)=>{ //如果操作成功关闭弹层 console.log(res.data); if(res.data.code==20021){ this.$message.success("删除成功"); }else{ this.$message.error("删除失败"); } }); }).catch(()=>{ // 取消 this.$message.info("取消删除"); }).finally(()=>{ this.getAll(); }); } } })
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)