搭建项目骨架
创建 Maven 项目,写好
pom.xml
,引入依赖(Spring、SpringMVC、MyBatis、数据库驱动、JSTL
等)。
- 建好基本目录结构:
controller/
、service/
、mapper/
、pojo/
、resources/
、webapp/WEB-INF/
。
使用maven导入依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
| <!--Spring以及基础组件 + SpringMvc--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</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-tx</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency>
<!--jackson内容协商Converter 用于支持application/json的@RequestBody/@ResponseBody--> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.18.2</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> <scope>test</scope> </dependency>
<!--Mybatis与Mybatis-Spring整合包--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.15</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>3.0.3</version> </dependency>
<!--Servlet--> <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> <version>6.0.0</version> <scope>provided</scope> </dependency> <!--JSP--> <dependency> <groupId>jakarta.servlet.jsp.jstl</groupId> <artifactId>jakarta.servlet.jsp.jstl-api</artifactId> <version>3.0.0</version> <scope>provided</scope> </dependency>
<!--数据库相关--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> </dependency>
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.25</version> </dependency>
<!--logback日志--> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.5.6</version> </dependency>
<!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.38</version> </dependency>
|
需要注意的是使用${spring.version}
要在<properties>
中配置<spring.version>6.0.6</spring.version>
配置底层支撑
数据库配置
写
src/main/resources/jdbc.properties
,配置数据库连接信息。
1 2 3 4
| jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql:///test jdbc.username=root jdbc.password=123456
|
准备 MyBatis 配置文件
src/main/resources/mybatis-config.xml
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "https://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration>
<settings> <setting name="useGeneratedKeys" value="true"/> <setting name="mapUnderscoreToCamelCase" value="true"/> <setting name="autoMappingBehavior" value="FULL"/> <setting name="lazyLoadingEnabled" value="true"/> <setting name="aggressiveLazyLoading" value="false"/> </settings> <typeAliases> <package name="com.wq3stone.pojo"/> </typeAliases> </configuration>
|
实体类 + Mapper 接口
实体类
在 pojo/
下建实体类 Employee.java
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| package com.wq3stone.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;
@Data @AllArgsConstructor @NoArgsConstructor public class Employee { private Integer empId;
private String empName;
private Double empSalary; }
|
@Data注解
@Data是lombok提供的一个组合注解,它整合了多个基础注解的功能,包括
- @Getter:提供get方法
- @Setter:提供set方法
- @ToString:提供toString方法
- @EqualsAndHashCode:提供equals和hashCode方法
- @RequiredArgsConstructor:生成该类下被final修饰或者带有@NonNull的构造方法
@AllArgsConstructor注解
生成包含所有参数的构造函数
@NoArgsConstructor注解
生成无参构造函数
Mapper接口
在 mapper/
下建接口EmployeeMapper.java
1 2 3 4 5 6 7 8 9 10 11 12 13
| package com.wq3stone.mapper; import com.wq3stone.pojo.Employee; import java.util.List;
public interface EmployeeMapper { List<Employee> selectList(); }
|
在 resources/mapper/
下写对应的 XML
EmployeeMapper.xml
(SQL 语句)。
1 2 3 4 5 6 7 8 9 10
| <?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"> <mapper namespace="com.wq3stone.mapper.EmployeeMapper">
<select id="selectList" resultType="com.wq3stone.pojo.Employee"> SELECT * FROM t_emp </select> </mapper>
|
MybatisConfig
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| package com.wq3stone.config;
import com.alibaba.druid.pool.DruidDataSource; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.PropertySource; import org.springframework.core.env.Environment; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
@PropertySource("classpath:jdbc.properties") @MapperScan(basePackages = "com.wq3stone.mapper") @EnableTransactionManagement public class MybatisConfig {
@Autowired private Environment env;
@Bean public DataSource dataSource() { DruidDataSource ds = new DruidDataSource(); ds.setDriverClassName(env.getProperty("jdbc.driverClassName")); ds.setUrl(env.getProperty("jdbc.url")); ds.setUsername(env.getProperty("jdbc.username")); ds.setPassword(env.getProperty("jdbc.password")); return ds; }
@Bean public SqlSessionFactory sqlSessionFactory() throws Exception { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dataSource()); sqlSessionFactoryBean.setConfigLocation(new PathMatchingResourcePatternResolver().getResource("classpath:mybatis-config.xml")); sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/*.xml")); return sqlSessionFactoryBean.getObject(); }
@Bean public PlatformTransactionManager transactionManager() { return new DataSourceTransactionManager(dataSource()); }
}
|
RootConfig
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| package com.wq3stone.config;
import org.springframework.context.annotation.*; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RestControllerAdvice;
@Configuration @EnableAspectJAutoProxy @Import({MybatisConfig.class}) @ComponentScan(value = "com.wq3stone", excludeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION, value = {Controller.class}), @ComponentScan.Filter(type = FilterType.ANNOTATION, value = {RestControllerAdvice.class}) }) public class RootConfig {
}
|
✅ 到这一步,可以用 JUnit 测试 Service/DAO 层,确保
MyBatis 和数据库能正常交互。
配置业务层 (Service)
service接口
在 service/
下写接口(EmployeeService
)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package com.wq3stone.service;
import com.wq3stone.pojo.Employee;
import java.util.List;
public interface EmployeeService { List<Employee> findList(); }
|
service实现类
在service/impl/
下写实现类(EmployeeServiceImpl
)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| package com.wq3stone.service.impl;
import com.wq3stone.mapper.EmployeeMapper; import com.wq3stone.pojo.Employee; import com.wq3stone.service.EmployeeService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List;
@Service @Transactional public class EmployeeServiceImpl implements EmployeeService {
@Autowired private EmployeeMapper employeeMapper;
@Override public List<Employee> findList() { return employeeMapper.selectList(); } }
|
@Service注解
标记的类专注于业务逻辑,通过组件扫描,Spring 自动检测使用
@Service
注解的类,并注册为 Spring 容器的
Bean。其作用相当于在applicationContext.xml
写了
1 2 3
| <bean id="EmployeeServiceImpl" class="com.wq3stone.servicve.impl.EmployeeServiceImpl" scope="prototype"> </bean>
|
@Autowired注解
实现依赖注入,用于自动装配Spring Bean的依赖关系
@Transactional注解
当 @Transactional
标注在类上时,表示这个类 所有的
public
方法 默认都开启事务
✅ 此时可以写 JUnit 测试 Service
方法,确保业务逻辑能正常调用 Mapper。
配置 Web 层 (SpringMVC)
WebConfig
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| package com.wq3stone.config;
import com.wq3stone.interceptor.MyInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.ViewResolver; import org.springframework.web.servlet.config.annotation.*; import org.springframework.web.servlet.view.InternalResourceViewResolver;
@Configuration @EnableWebMvc @ComponentScan("com.wq3stone.controller,com.wq3stone.exceptionhandler") public class WebConfig implements WebMvcConfigurer {
@Bean public ViewResolver viewResolver() { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setPrefix("/WEB-INF/views/"); viewResolver.setSuffix(".jsp"); return viewResolver; }
@Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable(); }
@Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/").setViewName("index"); }
@Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new MyInterceptor()); } }
|
Controller + 页面
EmployeeController
在 controller/
下写控制器
EmployeeController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| package com.wq3stone.controller;
import com.wq3stone.pojo.Employee; import com.wq3stone.service.EmployeeService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController @RequestMapping("/employee") public class EmployeeController {
@Autowired private EmployeeService employeeService;
@GetMapping public List<Employee> findList() { return employeeService.findList(); } }
|
在 views/
下写 JSP(如
index.jsp
、list.jsp
)。
补充功能
配置拦截器(interceptor/
)
MyInterceptor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| package com.wq3stone.interceptor;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView;
@Slf4j public class MyInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { log.info("MyInterceptor.preHandle"); return true; }
@Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { log.info("MyInterceptor.postHandle"); }
@Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { log.info("MyInterceptor.afterCompletion"); } }
|
配置全局异常处理(exceptionhandler/
)
GlobalExceptionHandler
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| package com.wq3stone.exceptionhandler;
import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice;
@Slf4j @RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(Exception.class) public String exceptionHandler(Exception e) { log.error("发生内部错误",e); return "出现了未知错误:" + e.getMessage(); } }
|
Web 容器初始化(initialize/
)
WebApplicationInitializer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| package com.wq3stone.initialize;
import com.wq3stone.config.RootConfig; import com.wq3stone.config.WebConfig; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class WebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override protected Class<?>[] getRootConfigClasses() { return new Class[]{RootConfig.class}; }
@Override protected Class<?>[] getServletConfigClasses() { return new Class[]{WebConfig.class}; }
@Override protected String[] getServletMappings() { return new String[]{"/"}; } }
|
优化事务(<tx:annotation-driven>
)。