Spring之路(35)–使用JavaConfig配置Spring+SpringMVC+MyBatis(SSM)项目完整实例

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
云数据库 RDS PostgreSQL,高可用系列 2核4GB
简介: 本文目录1. 背景2. 本篇讲述方式3. 新建项目4. 导入jar包5. 配置web项目6. 配置Spring容器6.1 将xml配置改为配置类6.2 修改sqlSessionFactory配置6.3 修改dataSource配置6.4 修改MapperScannerConfigurer配置6.5 修改扫描包6.6 其他配置7. 配置mybatis-config.xml8. 编写映射文件9. 开发各层逻辑代码10. 总结

1. 背景

本篇的表面目的,就时去掉烦人的xml文件,全部使用JavaConfig(如果要表达的更加严谨,其实除了JavaConfig配置,还有注解配置)开发一个SSM项目。


真实目的是演示,xml、注解、JavaConfig只是配置bean的不同方式,功能目的是一致的。在本系列文章中,我曾经很多次做过一件事——将xml+注解配置的SSM项目翻译为JavaConfig+注解配置的SSM项目,目的就是让大家深刻体会这一点。理解三种配置方式殊途同归,是学习和理解Spring的基石。


实际上SpringBoot跟全部使用JavaConfig+注解配置的SSM项目,高度相似,除了更加简洁了点,技术上没有多少新增的内容。所以如果Spring基础掌握的好,学习SpringBoot,简直就是不费吹灰之力!当然,后续Spring之路系列完结之后,我也会继续推出SpringBoot之路,敬请期待。


2. 本篇讲述方式

我们会通过逐一对比的方式,逐一对比上篇文章中xml配置的各个步骤,然后将步骤修改为JavaConfig配置,以便于大家体会理解。坐稳了老弟,开整~~~~


3. 新建项目

原来:

File-New-Other Project-Dynamic Web Project,建立一个动态网站项目,项目名称xmlssmdemo。

修改:

项目名称改为jcssmdemo,jc为JavaConfig缩写,因为javaconfigssmdemo这个项目名称实在是太长了。


4. 导入jar包

原来:

除了spring相关的jar包(已经指示过多次不再具体指示),还需要导入下面的jar包:


commons-logging-1.2.jar 日志相关

jackson-annotations-2.8.0.jar json相关

jackson-core-2.8.0.jar json相关

jackson-databind-2.8.0.jar json相关

mysql-connector-java-5.1.48.jar mysql驱动

druid-1.1.21.jar 数据库连接池

mybatis-3.5.3.jar mybatis相关

mybatis-spring-2.0.3.jar mybatis-spring相关

修改:这一步不需要修改


5. 配置web项目

原来:


<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns="http://xmlns.jcp.org/xml/ns/javaee"

xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"

id="WebApp_ID" version="3.1">

<display-name>xmlssmdemo</display-name>

<servlet>

 <servlet-name>springmvc</servlet-name>

 <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

 <init-param>

  <param-name>contextConfigLocation</param-name>

  <!-- 引入spring-config.xml配置文件,该文件用来配置spring容器 -->

  <param-value>/WEB-INF/spring-config.xml</param-value>

 </init-param>

 <load-on-startup>1</load-on-startup>

</servlet>

<servlet-mapping>

 <servlet-name>springmvc</servlet-name>

 <!-- 配置DispatcherServlet对应所有请求(*表示所有) -->

 <url-pattern>/*</url-pattern>

</servlet-mapping>

</web-app>


修改:

通过WebInit引导web项目加载,指定Spring容器的配置类为SpringConfig.class,注意WebInit本身并不是Spring容器的组件,而是通过继承AbstractAnnotationConfigDispatcherServletInitializer实现引导容器加载的作用。


package org.maoge.jcssmdemo;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class WebInit extends AbstractAnnotationConfigDispatcherServletInitializer {

@Override

protected Class<?>[] getRootConfigClasses() {

 return null;

}

@Override

protected Class<?>[] getServletConfigClasses() {

 // <!-- 引入SpringConfig配置类,该类用来配置spring容器 -->

 return new Class[] { SpringConfig.class };

}

@Override

protected String[] getServletMappings() {

 // <!-- 配置DispatcherServlet对应所有请求(*表示所有) -->

 return new String[] { "/*" };

}

}


6. 配置Spring容器

这一步的配置比较复杂,我们要详细说明。


6.1 将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:mvc="http://www.springframework.org/schema/mvc"

xsi:schemaLocation="http://www.springframework.org/schema/beans

       http://www.springframework.org/schema/beans/spring-beans-4.0.xsd

       http://www.springframework.org/schema/context

       http://www.springframework.org/schema/context/spring-context-4.0.xsd

       http://www.springframework.org/schema/mvc

       http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">

</beans>


改为:


package org.maoge.jcssmdemo;

import org.springframework.context.annotation.Configuration;

@Configuration // 表明该类是一个Spring配置类

public class SpringConfig {

}

注意这两个都是对Spring容器配置的描述,没啥功能区别,但是很明显类的方式更加简洁点。


6.2 修改sqlSessionFactory配置

原来


<!-- 第一步,配置MyBatis用来操作数据,sqlSessionFactory就是MyBatis中用来操作数据库的 -->

<bean id="sqlSessionFactory"

 class="org.mybatis.spring.SqlSessionFactoryBean">

 <property name="dataSource" ref="dataSource" />

 <!-- 引入Mybatis的配置文件,MyBatis组件需要它 -->

 <property name="configLocation"

  value="classpath:org/maoge/xmlssmdemo/config/mybatis-config.xml" />

</bean>


改为


  @Bean // 配置sqlSessionFactory

public SqlSessionFactoryBean sqlSessionFactory() {

 SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();

 sqlSessionFactory.setDataSource(dataSource());

 // <!-- 引入Mybatis的配置文件,MyBatis组件需要它 -->

 Resource resource = new ClassPathResource("org/maoge/jcssmdemo/config/mybatis-config.xml");

 sqlSessionFactory.setConfigLocation(resource);

 return sqlSessionFactory;

}


可以看出没啥区别,一个是xml定义bean,一个是通过方法添加@Bean注册bean;一个是通过property标签注入依赖项,一个是通过setter方法注入依赖项。


6.3 修改dataSource配置

原来


  <!-- 第二步,配置数据源,可以看到sqlSessionFactory需要注入该数据源 -->

<bean id="dataSource"

 class="com.alibaba.druid.pool.DruidDataSource">

 <property name="driverClassName"

  value="com.mysql.jdbc.Driver"></property>

 <property name="url"

  value="jdbc:mysql://127.0.0.1:3306/myblog?useUnicode=true&amp;characterEncoding=utf-8"></property>

 <property name="username" value="root"></property>

 <property name="password" value="xxx"></property>

</bean>


改为


@Bean // 配置数据源

public DataSource dataSource() {

 DruidDataSource dataSource = new DruidDataSource();

 dataSource.setDriverClassName("com.mysql.jdbc.Driver");

 dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/myblog?useUnicode=true&characterEncoding=utf-8");

 dataSource.setUsername("root");

 dataSource.setPassword("xxx");

 return dataSource;

}


没啥好说的,一个意思。


6.4 修改MapperScannerConfigurer配置

原来


<!-- 第三步,将org.maoge.xmlssmdemo.dao包下的类注册为bean,注意此处是注册为MyBatis方式管理的可操作数据库的bean -->

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">

 <property name="sqlSessionFactoryBeanName"

  value="sqlSessionFactory" />

 <property name="basePackage" value="org.maoge.xmlssmdemo.dao" />

</bean>


改为


@Bean

public MapperScannerConfigurer initMapperScannerConfigurer() {

 MapperScannerConfigurer msc = new MapperScannerConfigurer();

 msc.setSqlSessionFactoryBeanName("sqlSessionFactory");

 // 扫描包

 msc.setBasePackage("org.maoge.jcssmdemo.dao");

 return msc;

}


没啥好说的,一个意思。


6.5 修改扫描包

原来


<!-- 第四步,扫描控制器层和服务层 -->

<context:component-scan

 base-package="org.maoge.xmlssmdemo.controller" />

<context:component-scan

 base-package="org.maoge.xmlssmdemo.service" />


改为


@Configuration // 表明该类是一个Spring配置类

@ComponentScan({ "org.maoge.jcssmdemo.controller", "org.maoge.jcssmdemo.service" }) // 对应第四步,扫描包

public class SpringConfig extends WebMvcConfigurerAdapter {

}


通过标签指定扫描,改为了通过注解。


6.6 其他配置

剩下的两个配置通过添加@EnableWebMvc注解和addResourceHandlers方法实现,此处我们给出配置类的最终完整代码:


原来


<!--第五步,开启通过注解配置访问路径与方法的匹配 -->

<mvc:annotation-driven />

<!--第六步,配置静态资源映射 -->

<mvc:resources mapping="/static/**" location="/static/" />


新的配置类完整代码


package org.maoge.jcssmdemo;


import javax.sql.DataSource;


import org.mybatis.spring.SqlSessionFactoryBean;

import org.mybatis.spring.mapper.MapperScannerConfigurer;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.ComponentScan;

import org.springframework.context.annotation.Configuration;

import org.springframework.core.io.ClassPathResource;

import org.springframework.core.io.Resource;

import org.springframework.web.servlet.config.annotation.EnableWebMvc;

import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;

import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;


import com.alibaba.druid.pool.DruidDataSource;


@Configuration // 表明该类是一个Spring配置类

@ComponentScan({ "org.maoge.jcssmdemo.controller", "org.maoge.jcssmdemo.service" }) // 对应第四步,扫描包

@EnableWebMvc // 实现SpringMVC功能

public class SpringConfig extends WebMvcConfigurerAdapter {

@Bean // 配置sqlSessionFactory

public SqlSessionFactoryBean sqlSessionFactory() {

 SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();

 sqlSessionFactory.setDataSource(dataSource());

 // <!-- 引入Mybatis的配置文件,MyBatis组件需要它 -->

 Resource resource = new ClassPathResource("org/maoge/jcssmdemo/config/mybatis-config.xml");

 sqlSessionFactory.setConfigLocation(resource);

 return sqlSessionFactory;

}

@Bean // 配置数据源

public DataSource dataSource() {

 DruidDataSource dataSource = new DruidDataSource();

 dataSource.setDriverClassName("com.mysql.jdbc.Driver");

 dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/myblog?useUnicode=true&characterEncoding=utf-8");

 dataSource.setUsername("root");

 dataSource.setPassword("xxx");

 return dataSource;

}

@Bean

public MapperScannerConfigurer initMapperScannerConfigurer() {

 MapperScannerConfigurer msc = new MapperScannerConfigurer();

 msc.setSqlSessionFactoryBeanName("sqlSessionFactory");

 // 扫描包

 msc.setBasePackage("org.maoge.jcssmdemo.dao");

 return msc;

}

@Override

public void addResourceHandlers(ResourceHandlerRegistry registry) {

 registry.addResourceHandler("/static/**").addResourceLocations("/static/");

}

}


注意以下部分就跟之前基本一样了,除了项目名称和包名称,具体如下:


7. 配置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>

<mappers><!-- 映射器告诉MyBatis到哪里去找映射文件 -->

 <mapper resource="org/maoge/jcssmdemo/config/BlogMapper.xml" />

</mappers>

</configuration>


8. 编写映射文件

<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE mapper

       PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"

       "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="org.maoge.jcssmdemo.dao.BlogDao">

<!-- 获取一个 -->

<select id="getById" parameterType="Long"

 resultType="org.maoge.jcssmdemo.xdo.BlogDo">

 select * from blog where id = #{id}

</select>

<!-- 获取列表 -->

<select id="getList"

 resultType="org.maoge.jcssmdemo.xdo.BlogDo">

 select * from blog

</select>

<!-- 插入 -->

<insert id="insert"

 parameterType="org.maoge.jcssmdemo.xdo.BlogDo">

 insert into

 blog(author,content,title)values(#{author},#{content},#{title})

</insert>

<!-- 更新 -->

<update id="update"

 parameterType="org.maoge.jcssmdemo.xdo.BlogDo">

 update blog set

 author=#{author},content=#{content},title=#{title} where

 id=#{id}

</update>

<!-- 删除 -->

<delete id="delete" parameterType="Long">

 delete from blog where

 id=#{id}

</delete>

</mapper>


9. 开发各层逻辑代码

package org.maoge.jcssmdemo.dao;


import java.util.List;


import org.maoge.jcssmdemo.xdo.BlogDo;

import org.springframework.stereotype.Repository;


@Repository

public interface BlogDao {

  BlogDo getById(Long id);


  List<BlogDo> getList();


  int insert(BlogDo blog);


  int update(BlogDo blog);


  int delete(Long id);

}



package org.maoge.jcssmdemo.xdo;

/**

* @theme 数据对象--博客

* @author maoge

* @date 2020-01-29

*/

public class BlogDo {

  private Long id;

  private String title;

  private String author;

  private String content;

  // 省略get get


package org.maoge.jcssmdemo.service;


import java.util.List;


import org.maoge.jcssmdemo.dao.BlogDao;

import org.maoge.jcssmdemo.xdo.BlogDo;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;


@Service

public class BlogService {

@Autowired

private BlogDao blogDao;


/**

 * 获取博客列表

 */

public List<BlogDo> getBlogList() {

 return blogDao.getList();

}


/**

 * 按id获取博客信息

 */

public BlogDo getBlogById(Long id) {

 return blogDao.getById(id);

}


/**

 * 新增博客

 */

public void addBlog(BlogDo blog) {

 blogDao.insert(blog);

}


/**

 * 根据博客id更新博客信息

 */

public void updateBlog(BlogDo blog) {

 blogDao.update(blog);

}


/**

 * 根据博客id删除对应博客

 */

public void deleteBlog(Long id) {

 blogDao.delete(id);

}

}


package org.maoge.jcssmdemo.controller;


import java.util.List;


import org.maoge.jcssmdemo.service.BlogService;

import org.maoge.jcssmdemo.xdo.BlogDo;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.bind.annotation.DeleteMapping;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.PathVariable;

import org.springframework.web.bind.annotation.PostMapping;

import org.springframework.web.bind.annotation.PutMapping;

import org.springframework.web.bind.annotation.RequestBody;

import org.springframework.web.bind.annotation.RestController;


/**

* @theme 控制器--博客

* @author maoge

* @date 2020-01-28

*/

@RestController // 通过该注解,第一将BlogController注册为控制器,第二将其中方法返回值转换为json

public class BlogController {

@Autowired // 自动装配blogService

private BlogService blogService;


/**

 * 查询博客信息 1、@GetMapping表示可以使用get方法请求该api

 * 2、"/blog/{id}"表示请求路径为/blog/{id}的形式,其中{id}为占位符

 * 3、@PathVariable("id")表示将占位符{id}的值传递给id 4、也就是说/blog/123请求的话,会将123传递给参数id

 */

@GetMapping(value = "/blog/{id}")

public BlogDo getOne(@PathVariable("id") long id) {

 return blogService.getBlogById(id);

}


/**

 * 查询博客列表,使用get方法

 */

@GetMapping("/blog")

public List<BlogDo> getList() {

 return blogService.getBlogList();

}


/**

 * 新增博客 1、@PostMapping表示使用post方法

 * 2、@RequestBody表示将请求中的json信息转换为BlogDo类型的对象信息,该转换也是由SpringMVC自动完成的

 */

@PostMapping("/blog")

public void add(@RequestBody BlogDo blog) {

 blogService.addBlog(blog);

}


/**

 * 修改博客 实际上此处也可以不在路径中传递id,而是整个使用json传递对象信息,但是我查询了一些文档,貌似使用路径传递id更加规范一些,此处不用纠结

 */

@PutMapping("/blog/{id}")

public void update(@PathVariable("id") long id, @RequestBody BlogDo blog) {

 // 修改指定id的博客信息

 blog.setId(id);

 blogService.updateBlog(blog);

}


/**

 * 删除博客

 */

@DeleteMapping("/blog/{id}")

public void delete(@PathVariable("id") long id) {

 blogService.deleteBlog(id);

}

}


10. 总结

纸上得来终觉浅,绝知此事要躬行。


之前我也曾无数次看过Spring依赖注入的各种例子,但是理解始终不深刻。直到工作原因需要将一些之前xml配置的项目,改为JavaConfig+注解配置,有些需要改为SpringBoot配置。写的多了,就发现三种配置方式各有各的好处也各有缺点局限性,如果都掌握了会很有用处,起码在读别人的代码时,不管混合用了几种方式,我们都能读懂。


Spring就是通过这种灵活的组件注册,包容万象,现在实际上都成为了Java开发的事实规范了,所以可以总结一句就是:如果是搞Java开发的,对Spring花再多时间学习体会,都是超值的。

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
相关文章
|
28天前
|
Java 关系型数据库 MySQL
Spring Boot自动配置:魔法背后的秘密
Spring Boot 自动配置揭秘:只需简单配置即可启动项目,背后依赖“约定大于配置”与条件化装配。核心在于 `@EnableAutoConfiguration` 注解与 `@Conditional` 系列条件判断,通过 `spring.factories` 或 `AutoConfiguration.imports` 加载配置类,实现按需自动装配 Bean。
|
29天前
|
人工智能 Java 开发者
【Spring】原理解析:Spring Boot 自动配置
Spring Boot通过“约定优于配置”的设计理念,自动检测项目依赖并根据这些依赖自动装配相应的Bean,从而解放开发者从繁琐的配置工作中解脱出来,专注于业务逻辑实现。
|
22天前
|
缓存 Java 应用服务中间件
Spring Boot配置优化:Tomcat+数据库+缓存+日志,全场景教程
本文详解Spring Boot十大核心配置优化技巧,涵盖Tomcat连接池、数据库连接池、Jackson时区、日志管理、缓存策略、异步线程池等关键配置,结合代码示例与通俗解释,助你轻松掌握高并发场景下的性能调优方法,适用于实际项目落地。
232 4
|
1月前
|
传感器 Java 数据库
探索Spring Boot的@Conditional注解的上下文配置
Spring Boot 的 `@Conditional` 注解可根据不同条件动态控制 Bean 的加载,提升应用的灵活性与可配置性。本文深入解析其用法与优势,并结合实例展示如何通过自定义条件类实现环境适配的智能配置。
探索Spring Boot的@Conditional注解的上下文配置
|
7月前
|
Java 测试技术 微服务
微服务——SpringBoot使用归纳——Spring Boot中的项目属性配置——少量配置信息的情形
本课主要讲解Spring Boot项目中的属性配置方法。在实际开发中,测试与生产环境的配置往往不同,因此不应将配置信息硬编码在代码中,而应使用配置文件管理,如`application.yml`。例如,在微服务架构下,可通过配置文件设置调用其他服务的地址(如订单服务端口8002),并利用`@Value`注解在代码中读取这些配置值。这种方式使项目更灵活,便于后续修改和维护。
105 0
|
7月前
|
Java 数据库 微服务
微服务——SpringBoot使用归纳——Spring Boot中的项目属性配置——指定项目配置文件
在实际项目中,开发环境和生产环境的配置往往不同。为简化配置切换,可通过创建 `application-dev.yml` 和 `application-pro.yml` 分别管理开发与生产环境配置,如设置不同端口(8001/8002)。在 `application.yml` 中使用 `spring.profiles.active` 指定加载的配置文件,实现环境快速切换。本节还介绍了通过配置类读取参数的方法,适用于微服务场景,提升代码可维护性。课程源码可从 [Gitee](https://gitee.com/eson15/springboot_study) 下载。
261 0
|
7月前
|
Java 微服务 Spring
微服务——SpringBoot使用归纳——Spring Boot中的项目属性配置——少量配置信息的情形
在微服务架构中,随着业务复杂度增加,项目可能需要调用多个微服务。为避免使用`@Value`注解逐一引入配置的繁琐,可通过定义配置类(如`MicroServiceUrl`)并结合`@ConfigurationProperties`注解实现批量管理。此方法需在配置文件中设置微服务地址(如订单、用户、购物车服务),并通过`@Component`将配置类纳入Spring容器。最后,在Controller中通过`@Resource`注入配置类即可便捷使用,提升代码可维护性。
108 0
|
Java 测试技术 数据库
Spring Boot中的项目属性配置
本节课主要讲解了 Spring Boot 中如何在业务代码中读取相关配置,包括单一配置和多个配置项,在微服务中,这种情况非常常见,往往会有很多其他微服务需要调用,所以封装一个配置类来接收这些配置是个很好的处理方式。除此之外,例如数据库相关的连接参数等等,也可以放到一个配置类中,其他遇到类似的场景,都可以这么处理。最后介绍了开发环境和生产环境配置的快速切换方式,省去了项目部署时,诸多配置信息的修改。
|
Java 关系型数据库 MySQL
Spring Boot项目属性配置
Spring Boot项目属性配置
242 0
|
Java 数据库
【SpringBoot】项目属性配置
【SpringBoot】项目属性配置
179 0