SSM整合
1.项目结构分析
如图所示,SSM框架大致如上,主要由配置类、业务层、表现层、数据层组成。下面会对每个包中的代码进行分析和解释。
- 配置类(config):加载初始化Bean,加载配置文件,初始化Spring核心容器和Tomcat中Servlet容器,数据库相关的配置。
- 业务层(Service):实现业务逻辑。
- 数据层(dao):负责数据库操作,实现增删改查等数据库操作。
- 表现层(controller):调用业务层提供各种方法实现一些组合操作,满足用户需求,并返回网页页面需要的数据。
需要导入的依赖坐标
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.22.RELEASE</version>
<scope>test</scope>
</dependency>
<!--提供spring所有基础功能-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.22.RELEASE</version>
</dependency>
<!--tomcat中servlet容器需要-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<!--提供数据格式转换-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
<!--提供mybatis功能-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.11</version>
</dependency>
<!-- spring中管理mybatis需要-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.1.0</version>
</dependency>
<!--连接mysql数据库-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version>
</dependency>
<!--数据连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.15</version>
</dependency>
<!--提供jdbc基本功能 主要是使用了它的事务管理 其他基础功能在spring-webmvc已有-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.22.RELEASE</version>
</dependency>
<!--实现实体类的各种便捷操作 不是刚需-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<scope>provided</scope>
</dependency>
<!--提供测试功能-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
</dependencies>
2. config包解析
2.1. 目录结构
主要五个配置类,起作用分别为:
JbbcConfig
:配置DataSource
(数据库)和事务管理。MyBatisConfig
:配置MyBatis的扫描domain类和dao,实现SQL语句。ServletContainerConfig
:将Spring核心容器的Bean加载到Servlet容器中,能够在web容器启动项目的SpringMvc,和对访问资源路径的判断处理。SpringConfig
:管理Spring容器的Bean的加载和初始化。SpringMvcConfig
:在web容器中启动该项目。
2.2. SpringMvcConfig实现
@Configuration
@ComponentScan({"com.cqut.controller"})
@EnableWebMvc
public class SpringMvcConfig implements WebMvcConfigurer{
@Autowired
private ProjectInterceptor projectInterceptor;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/html/**").addResourceLocations("/html/");
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/js/**").addResourceLocations("/js/");
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(projectInterceptor).addPathPatterns("/books", "/books/*");
}
}
@Configuration
:声明为配置类,加载各种资源。@ComponentScan
:扫描包,springmvc主要是控制表现层,需要对表现层的包"com.cqut.controller"
进行扫描,必要时为了加载某些配置类也可以对配置类所在的包"com.cqut.config"
进行扫描。@EnableWebMvc
:开启数据格式转换的功能,还有其他等功能。- 这里的
SpringMvcConfig
继承了WebMvcConfigurer
类,可以覆盖父类提供的方法,对静态资源进行放行,和设置拦截器。public void addResourceHandlers(ResourceHandlerRegistry registry)
:对静态资源进行放行,例如:当url中发起请求html/*
的路径访问,则访问对应目录下的文件,而不是url的servlte请求。public void addInterceptors(InterceptorRegistry registry)
:对servlet请求设置拦截器,当访问/books
和/books/*
进行拦截处理。这里的拦截器是通过自动注@Autowired
入拿到的。
- 这种写法的侵入式较强,将Spring和SpringMvc绑定在了一起。可以有以下写法,将对静态资源的访问和拦截器在写一个配置类。
@Configuration
public class SpringMvcSupportConfig extends WebMvcConfigurationSupport {
@Autowired
private ProjectInterceptor projectInterceptor;
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/html/**").addResourceLocations("/html/");
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/js/**").addResourceLocations("/js/");
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
@Override
protected void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(projectInterceptor).addPathPatterns("/books", "/books/*");
}
}
@Configuration
@ComponentScan({"com.cqut.controller","com.cqut.config"})
@EnableWebMvc
public class SpringMvcConfig{
}
拦截器
- 拦截器(Interceptor)是一种动态拦截方法调用的机制,在SpringMVC中动态拦截控制器方法的执行
- 作用:
- 在指定的方法调用前后执行预先设定的代码
- 阻止原始方法的执行
- 拦截器的主要思想:AOP面向切面编程
- Filter属于Servlet技术,Interceptor属于SpringMVC技术
- Filter对所有访问进行增强,Interceptor仅对SpringMVC的访问增强
- 拦截器流程如下
这里需要对拦截器的执行流程理解,在此基础上对拦截器链进行充分理解,这里不在赘述。
2.3. SpringConfig实现
@Configuration
@ComponentScan({"com.cqut.service"})
@Import({JdbcConfig.class, MyBatisConfig.class})
@PropertySource({"classpath:jdbc.properties"})
public class SpringConfig {
}
- 这里对
"com.cqut.service"
业务层进行扫描,不需要再对"com.cqut.dao"
数据库包进行扫描了,具体实现由mybatis来实现。 @Import({JdbcConfig.class, MyBatisConfig.class})
这里到导入了JdbcConfig
和MyBatisConfig
两个配置类进行导入,得到了数据库连接池,对数据库进行操作。@PropertySource({"classpath:jdbc.properties"})
:这个注意要写上classpath
,不然可能找不到文件在哪报错。文件位置见目录结构。
2.4. JdbcConfig实现
public class JdbcConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
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;
}
@Bean
public PlatformTransactionManager transactionManager (DataSource dataSource){
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
return transactionManager;
}
}
@Value("${xxx}")
:通过读取jdbc.properties
文件来赋值。public DataSource dataSource()
:建立数据库连接池。这里使用的是druid
中提供的数据库操作。public PlatformTransactionManager transactionManager (DataSource dataSource)
:事务管理,保证事务的一致性。- 方法前加上
@Bean
会把方法的返回值初始化为一个Bean,同时public PlatformTransactionManager transactionManager (DataSource dataSource)
的形参dataSource
和public DataSource dataSource()
初始化的Bean要是同一个对象。
2.5. MybatisConfig实现
public class MyBatisConfig {
@Bean
public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource);
factoryBean.setTypeAliasesPackage("com.cqut.domain");
return factoryBean;
}
@Bean
public MapperScannerConfigurer mapperScannerConfigurer(){
MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
mapperScannerConfigurer.setBasePackage("com.cqut.dao");
return mapperScannerConfigurer;
}
}
public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource)
:对实体类进行映射public MapperScannerConfigurer mapperScannerConfigurer()
:对数据层(dao)进行映射
2.6. ServletContainerInitConfig实现
public class ServletContainerInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
protected String[] getServletMappings() {
return new String[]{"/"};
}
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("utf-8");
return new Filter[]{filter};
}
}
- 注意父类
AbstractAnnotationConfigDispatcherServletInitializer
。 protected Class<?>[] getRootConfigClasses()
:是对SpringConfig
的设置。protected Class<?>[] getServletConfigClasses()
:是对SpringMvcConfig
的设置。protected String[] getServletMappings()
:设置请求路径来对请求处理。protected Filter[] getServletFilters()
:可以设置多个过滤器,这里只对body中中文乱码设置过滤器进行处理。
3. controller包解析
3.1.目录结构
- 表现层:包括了表现层、数据格式统一,异常处理等处理类。
3.2. ProjectInterceptor实现
@Component
public class ProjectInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle ....");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle ....");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion ....");
}
}
- 自定一个拦截器,可以再执行流程中对数据进行处理。
- 添加上
@Component
,在SpringMvcConfig
扫描包可以扫描到初始定义为bean。 public boolean preHandle()
的返回值决定是否是执行controller中的代码,如果为true执行,false中止方法执行。
3.3. Code实现
public class Code {
public static Integer ADD_OK = 20011;
public static Integer DELETE_OK = 20021;
public static Integer SELECT_OK = 20031;
public static Integer UP_DATA_OK = 20041;
public static Integer ADD_ERR = 20010;
public static Integer DELETE_ERR = 20020;
public static Integer SELECT_ERR = 20030;
public static Integer UP_DATA_ERR = 20040;
public static Integer SYSTEM_ERR = 50010;
public static Integer SYSTEM_TIMEOUT_ERR = 50020;
public static Integer BUSINESS_ERR = 50030;
public static Integer SYSTEM_UNKNOW_ERR = 59999;
}
- 定义了各种状态码,便于前端的数据使用。
3.4. ProjectExceptionAdvice实现
@RestControllerAdvice
public class ProjectExceptionAdvice{
@ExceptionHandler(SystemException.class)
public Result doSystemException(SystemException exception){
return new Result(Code.SYSTEM_ERR, null, exception.getMessage());
}
@ExceptionHandler(BusinessException.class)
public Result doBusinessException(BusinessException exception){
return new Result(Code.BUSINESS_ERR, null, exception.getMessage());
}
@ExceptionHandler(Exception.class)
public Result doException(Exception exception){
return new Result(Code.SYSTEM_TIMEOUT_ERR, null, "系统超时,请稍后再试");
}
}
- 针对各种异常进行处理,也便于前端来处理异常情况。
@RestControllerAdvice
是一个用于全局异常处理的注解。它可以用于定义一个类,该类中的方法会在控制器中抛出异常时被调用,从而统一处理异常。可以将全局异常处理逻辑集中到一个类中,减少代码重复,提高代码的可维护性和可读性。@ExceptionHandler
是一个注解,用于标记一个方法,该方法用于处理特定类型的异常。当控制器中抛出指定类型的异常时,被@ExceptionHandler
注解标记的方法会被调用,用于处理该异常。该方法可以包含任意的业务逻辑,比如记录日志、返回自定义的错误信息等。SystemException
和BusinessException
是自定义的异常类。
3.5. Result实现
@Data
public class Result {
private Integer code;
private Object data;
private String msg;
public Result(Integer code, Object data, String msg) {
this.code = code;
this.data = data;
this.msg = msg;
}
public Result(Integer code, Object data) {
this.code = code;
this.data = data;
}
}
- 后端返回给前端的数据做一个统一的数据格式封装,同一个格式,包括状态码、数据、异常信息。
3.6. BookController实现
@RestController
@RequestMapping("/books")
public class BookController {
@Autowired
private BookService bookService;
@PostMapping
public Result add(@RequestBody Book book) {
int i = bookService.add(book);
return new Result(i > 0 ? Code.ADD_OK: Code.ADD_ERR, i > 0 ? "True" : "False");
}
@DeleteMapping("/{id}")
public Result deleteById(@PathVariable int id) {
int i = bookService.deleteById(id);
return new Result(i > 0 ? Code.DELETE_OK: Code.DELETE_ERR, i > 0 ? "True" : "False");
}
@PutMapping()
public Result upDataById(@RequestBody Book book) {
int i = bookService.upDataById(book);
return new Result(i > 0 ? Code.UP_DATA_OK: Code.UP_DATA_ERR, i > 0 ? "True" : "False");
}
@GetMapping("/{id}")
public Result selectById(@PathVariable int id) {
Book book = bookService.selectById(id);
return new Result(book != null? Code.SELECT_OK: Code.SELECT_ERR, book);
}
@GetMapping
public Result selectAll() {
List<Book> books = bookService.selectAll();
return new Result(books != null? Code.SELECT_OK: Code.SELECT_ERR, books);
}
}
-
@RestController
:@RestController
是一个注解,用于标记一个类或方法,表示该类或方法是一个控制器(Controller),并且返回的结果直接写入 HTTP 响应体中。@RestController 注解的作用包括:
- 标记类为控制器:当一个类被
@RestController
注解标记时,Spring 将会扫描该类,并将其注册为一个控制器,用于处理客户端的 HTTP 请求。 - 处理请求和返回响应:被
@RestController
注解标记的方法可以通过 @RequestMapping 注解来处理特定的 HTTP 请求,包括 GET、POST、PUT、DELETE 等。方法的返回值会被自动转换为 JSON 或 XML 格式,并写入 HTTP 响应体中,返回给客户端。 - 自动序列化和反序列化:被
@RestController
注解标记的方法可以直接接收和返回 Java 对象,Spring 会自动进行序列化和反序列化,将对象转换为 JSON 或 XML 格式,或者将请求体中的 JSON 或 XML 数据转换为 Java 对象。
- 标记类为控制器:当一个类被
-
统一使用规定好的Result数据格式进行数据返回。
4. dao包解析
public interface BookDao {
@Insert("insert into tb_books (name, series, description) values(#{name}, #{series}, #{description})")
public int add(Book book);
@Delete("delete from tb_books where id = #{id}")
public int deleteById(int id);
@Update("update tb_books set name = #{name}, series = #{series}, description = #{description} where id = #{id}")
public int upDataById(Book book);
@Select("select * from tb_books where id = #{id};")
public Book selectById(int id);
@Select("select * from tb_books")
public List<Book> selectAll();
}
- SQL语句执行,这里通过注解方式实现的,若查询条件复杂还是使用xml形式来书写SQL语句。
- 这里需要注意数据库的字段名称、参数名称对应的问题。
5. domain包
@Data
public class Book {
private Integer id;
private String name;
private String series;
private String description;
}
6. exception包
- 自定义了两个类
SystemException
(系统异常)和BusinessException
(业务异常)
public class BusinessException extends RuntimeException{
private Integer code;
public Integer getCode() {
return code;
}
public BusinessException(Integer code, String message) {
super(message);
this.code = code;
}
public BusinessException(Integer code, String message, Throwable cause) {
super(message, cause);
this.code = code;
}
}
public class SystemException extends RuntimeException{
private Integer code;
public Integer getCode() {
return 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;
}
}
7. service包
7.1. 目录结构
- 定义一个service的接口,通过结构来定义一个具体的实现类。
7.2. 代码实现
public interface BookService {
public int add(Book book);
public int deleteById(int id);
public int upDataById(Book book);
public Book selectById(int id);
public List<Book> selectAll();
}
@Service
@Transactional
public class BookServiceImp implements BookService {
@Autowired
private BookDao bookDao;
public int add(Book book) {
return bookDao.add(book);
}
public int deleteById(int id) {
return bookDao.deleteById(id);
}
public int upDataById(Book book) {
return bookDao.upDataById(book);
}
public Book selectById(int id) {
return bookDao.selectById(id);
}
public List<Book> selectAll() {
return bookDao.selectAll();
}
}
-
@Service
注解的作用是将一个类标记为服务组件,用于实现业务逻辑的处理,并作为控制器和持久层之间的中间层,提供可重用、可测试和可维护的代码。 -
@Transactional
是一个注解,用于标记一个方法或类,表示该方法或类需要在事务管理下进行操作。@Transactional
注解的作用包括:-
启用事务管理:当一个方法或类被
@Transactional
注解标记时,Spring 将会为该方法或类启用事务管理,确保方法的执行在一个事务中进行。 -
提供事务边界:被
@Transactional
注解标记的方法或类将会创建一个事务边界,该边界内的数据库操作将被视为一个原子操作,要么全部成功提交,要么全部失败回滚。 -
控制事务传播行为:通过 @Transactional 注解的属性,可以控制事务的传播行为,包括传播行为、隔离级别、只读属性等。例如,可以指定一个方法在一个已存在的事务中运行,或者在没有事务的情况下启动一个新的事务。
-
简化事务管理:使用
@Transactional
注解可以简化事务管理的配置,不再需要手动编写事务的开始、提交、回滚等代码,Spring 会自动处理事务的管理。
-
-
@Service
注解的作用是将一个类标记为服务组件,用于实现业务逻辑的处理,并作为控制器和持久层之间的中间层,提供可重用、可测试和可维护的代码。 -
@Transactional
是一个注解,用于标记一个方法或类,表示该方法或类需要在事务管理下进行操作。@Transactional
注解的作用包括:-
启用事务管理:当一个方法或类被
@Transactional
注解标记时,Spring 将会为该方法或类启用事务管理,确保方法的执行在一个事务中进行。 -
提供事务边界:被
@Transactional
注解标记的方法或类将会创建一个事务边界,该边界内的数据库操作将被视为一个原子操作,要么全部成功提交,要么全部失败回滚。 -
控制事务传播行为:通过 @Transactional 注解的属性,可以控制事务的传播行为,包括传播行为、隔离级别、只读属性等。例如,可以指定一个方法在一个已存在的事务中运行,或者在没有事务的情况下启动一个新的事务。
-
简化事务管理:使用
@Transactional
注解可以简化事务管理的配置,不再需要手动编写事务的开始、提交、回滚等代码,Spring 会自动处理事务的管理。
总之,@Transactional 注解的作用是启用事务管理,并为方法或类提供事务边界,控制事务的传播行为,简化事务管理的配置。使用 @Transactional 注解可以确保方法的执行在一个事务中进行,保证数据的一致性和完整性。
-