在 Java - servlet 3.0 这篇文章中我们大概了解了一下 Servlet 3.0,在日常 web 开发中我们一般都是使用 SpringMVC 框架,所以这里以 Servlet 3.0 注解的方式来搭建一下 SSM 框架

1、准备工作

1.1、创建 Maven 项目

servlet项目转为springboot_servlet

填写项目的 GAV 等信息:

servlet项目转为springboot_java_02

项目的基本结构就是下面这样:

servlet项目转为springboot_java_03

1.2、配置依赖

首先我们在 pom.xml 中配置 SpringSpringMVCMybatis 相关的依赖,借助于 Maven 的依赖传递机制,我们只需要引入核心的依赖即可:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>ssm-example</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>

        <spring.version>5.3.22</spring.version>
    </properties>

    <dependencies>
        <!--Spring begin-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <!--Spring end-->

        <!--Mybatis begin-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.9</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.6</version>
        </dependency>

        <!--数据库连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.8</version>
        </dependency>

        <!--junit5-->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.8.2</version>
            <scope>test</scope>
        </dependency>

        <!--mysql驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.28</version>
            <scope>runtime</scope>
        </dependency>

		<dependency>
            <groupId>jakarta.servlet</groupId>
            <artifactId>jakarta.servlet-api</artifactId>
            <version>4.0.4</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

</project>

业务都是不断完善的,架构也是慢慢演化的,所以这里的依赖不是固定的,有需要可以再添加

1.3、准备一个 mysql 数据库 example,并新建一张 user

CREATE TABLE `user`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `gender` tinyint(4) NULL DEFAULT NULL,
  `nickname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

2、整合 SpringMVC

2.1、整合分析

SpringMVC 官方文档 中我们知道,不仅可以通过 web.xml 的方式来配置 DispatcherServlet,还可以通过 Java configuration 的方式来配置 DispatcherServlet

  • 使用 ServletContext API 的形式配置
public class MyWebApplicationInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletContext) {

        // Load Spring web application configuration
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        context.register(AppConfig.class);

        // Create and register the DispatcherServlet
        DispatcherServlet servlet = new DispatcherServlet(context);
        ServletRegistration.Dynamic registration = servletContext.addServlet("app", servlet);
        registration.setLoadOnStartup(1);
        registration.addMapping("/app/*");
    }
}
  • 拓展 AbstractAnnotationConfigDispatcherServletInitializer 的形式配置
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[] { RootConfig.class };
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[] { App1Config.class };
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/app1/*" };
    }
}

这里我们使用第二种方式:实现 AbstractAnnotationConfigDispatcherServletInitializer 来整合 SpringMVC,这种方式也是官方推荐的方式:

servlet项目转为springboot_spring_04

2.2、整合

  • 定义 Root WebApplicationContext
@Configuration
public class SpringConfig {
}
  • 定义 Servlet WebApplicationContext
@Configuration
// SpringMVC 只需要扫描控制层即可
@ComponentScan(value = "com.example.controller")
// 开启 SpringMVC 高级配置
@EnableWebMvc
public class WebMvcConfig implements WebMvcConfigurer {

    /**
     * 配置静态资源访问
     *
     * @param configurer
     */
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
}
  • 配置 DispatcherServlet
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[]{SpringConfig.class};
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{WebMvcConfig.class};
    }

    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }

    @Override
    protected Filter[] getServletFilters() {
        // 配置编码过滤器
        CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
        characterEncodingFilter.setEncoding("UTF-8");
        characterEncodingFilter.setForceEncoding(true);

        // 配置处理请求方式的过滤器
        HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();

        return new Filter[]{
                characterEncodingFilter,
                hiddenHttpMethodFilter
        };
    }
}

代码仓库地址

servlet项目转为springboot_mybatis_05

3、整合 Spring

SpringMVC 官方文档 中我们知道,Servlet 容器只需要扫描控制层即可,但是 Root 容器需要扫描 Service 层和 Repositiries 持久化层

servlet项目转为springboot_spring_06

@Configuration
@ComponentScan(value = "com.example", excludeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = RestController.class),
        @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class),
})
public class SpringConfig {
	...
}

3.1、配置数据源 druid

3.1.1、新建一个 jdbc.properties 来配置数据库连接信息

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://192.168.80.232:3306/example?serverTimezone=UTC
jdbc.username=root
jdbc.password=123456

3.1.2、新建一个常量类 JdbcConsts 来读取配置信息

@Data
@Component
public class JdbcConsts {

    @Value("${jdbc.driver}")
    private String driver;

    @Value("${jdbc.url}")
    private String url;

    @Value("${jdbc.username}")
    private String username;

    @Value("${jdbc.password}")
    private String password;
}

当然了,只是这样并不能读取到 jdbc.properties 中的数据,我们还需要使用 @PropertySource 注解来把数据读取到内存中

@Configuration
@ComponentScan(value = "com.example", excludeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = RestController.class),
        @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class),
})
// 加载文件到内存
@PropertySource("classpath:jdbc.properties")
public class SpringConfig {
	...
}

3.1.3、配置数据源

@Configuration
@ComponentScan(value = "com.example", excludeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = RestController.class),
        @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class),
})
@PropertySource("classpath:jdbc.properties")
public class SpringConfig {

    @Bean
    public DataSource dataSource(JdbcConsts jdbcConsts) {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(jdbcConsts.getDriver());
        dataSource.setUrl(jdbcConsts.getUrl());
        dataSource.setUsername(jdbcConsts.getUsername());
        dataSource.setPassword(jdbcConsts.getPassword());
        return dataSource;
    }
}

4、整合 Mybatis

4.1、准备工作

首先创建数据表 user 对应的 Mybatis 需要的文件:User 实体、UserMapper 接口、UserMapper.xml

servlet项目转为springboot_ssm_07

4.2、配置 SqlSessionFactory

@Configuration
@ComponentScan(value = "com.example", excludeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = RestController.class),
        @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class),
})
@PropertySource("classpath:jdbc.properties")
public class SpringConfig {

    @Bean
    public DataSource dataSource(JdbcConsts jdbcConsts) {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(jdbcConsts.getDriver());
        dataSource.setUrl(jdbcConsts.getUrl());
        dataSource.setUsername(jdbcConsts.getUsername());
        dataSource.setPassword(jdbcConsts.getPassword());
        return dataSource;
    }

    @Bean
    public SqlSessionFactory sqlSessionFactoryBean(DataSource dataSource) {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        System.out.println(dataSource);
        sqlSessionFactoryBean.setDataSource(dataSource);

        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        sqlSessionFactoryBean.setTypeAliasesPackage("com.example.domain");
        //sqlSessionFactoryBean.setConfigLocation();
        //sqlSessionFactoryBean.setPlugins();
        Properties properties = new Properties();
        properties.setProperty("mapUnderscoreToCamelCase", "true");
        sqlSessionFactoryBean.setConfigurationProperties(properties);
        try {
            sqlSessionFactoryBean.setMapperLocations(resolver.getResources("classpath:/mapper/*.xml"));
            return sqlSessionFactoryBean.getObject();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

}

4.3、配置 mapper 扫描

@Configuration
@ComponentScan(value = "com.example", excludeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = RestController.class),
        @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class),
})
@PropertySource("classpath:jdbc.properties")
// 扫描 Mybatis Mapper 层
@MapperScan(basePackages = "com.example.mapper")
public class SpringConfig {
	...
}

5、配置事务管理

...
@EnableTransactionManagement
public class SpringConfig {

    ...

    @Bean
    public DataSourceTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

6、测试功能

6.1、测试添加数据接口

@RestController
@RequestMapping("user")
public class UserController {

    @Autowired
    private UserService userService;

    @PostMapping("add")
    public Integer add(@RequestBody User user) {
        return userService.add(user);
    }
}

注意:这里需要使用到 SpringMVC 的数据绑定功能,所以需要添加 jackson-databind 依赖。

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>x.x.x</version>
</dependency>

6.2、测试分页查询功能

可以配置分页插件 PageHelper,参考:Mybatis - PageHelper 分页插件

这一部分代码比较多,就不粘贴在这里了,有需要的朋友可以从 Git 地址 中拉取代码