2024-11-26-Tue-T-SSM

news/2025/11/23 21:32:45/文章来源:https://www.cnblogs.com/fei1010/p/19261584

SSM

SSM三者的关系

1 Spring IoC容器

父子工程

<?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.learning.springmvc</groupId><artifactId>ssm</artifactId><version>1.0-SNAPSHOT</version><!--父工程定义了版本, 子模块不需要再定义版本--><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.4.0</version><relativePath/> <!-- lookup parent from repository --></parent><!--父项目--><!--父项目一般不写代码--><!--适用场景:1. 组织和管理多个子模块的公共配置(如依赖、插件、构建配置)。2. 聚合子模块,使它们可以通过 mvn install 或 mvn package 一次性构建所有子模块。3. 父项目自身没有具体的功能实现,仅作为管理和继承的基础。--><!--通常情况下,父项目的打包方式是 pom,这是最佳实践--><!--当父项目的打包方式为 pom 时,父项目本身不会生成实际的可部署包(如 jar 或 war),而仅作为一个配置聚合的容器--><packaging>pom</packaging><modules><module>ssm-spring-ioc</module></modules><!--当父工程要统一管理依赖时, 使用<dependencyManagement>进行管理, 子模块引用时, 不用再指定版本--><!--<dependencyManagement><dependencies><dependency><groupId></groupId><artifactId></artifactId></dependency></dependencies></dependencyManagement>--><properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties></project>

1.1 组件到容器

注册组件的方式:

  1. 使用@Import注解标识启动类
@Import({Dog.class, Person.class})
@SpringBootApplication
public class Application {public static void main(String[] args) {//1. 跑起一个Spring的应用, 获取一个ApplicationContext, 即ioc容器ConfigurableApplicationContext ioc = SpringApplication.run(Application.class, args);}
  1. 使用@Bean注解标识配置类方法
@Configuration
public class DogConfig {@Beanpublic Dog dog() {return new Dog();}
}

1.2 @Scope调整组件的作用域

四个作用域:

  1. singleton: 默认值, 单例模式, 容器启动时创建对象, 并且只会创建一个实例, 无论调用多少次getBean方法, 都会返回同一个对象
  2. prototype: 多例模式, 每次调用getBean方法, 都会创建一个新的对象
  3. request: 同一个请求创建一个单实例
  4. session: 同一个会话创建一个单实例
@Configuration
public class PersonConfig {@Scope("prototype")@Beanpublic Person person(){return new Person();}
}

1.3 @Lazy懒加载

  1. 默认情况下, 容器启动时, 会创建所有的单实例对象
  2. @Lazy注解, 容器启动时不创建对象, 而是调用getBean方法时才创建对象
  3. @Lazy注解, 只对单实例有效, 多实例无效
@Configuration
public class PersonConfig {@Lazy@Beanpublic Person person(){return new Person();}
}

1.4 FactoryBean

FactoryBean, 用于创建复杂对象, 并且返回对象, 而不是直接返回对象

@Component
public class BYD implements FactoryBean<Car>{/*** Spring调用此方法给ioc容器创建对象* @return* @throws Exception*/@Overridepublic Car getObject() throws Exception {Car car = new Car();return car;}@Overridepublic Class<?> getObjectType() {return Car.class;}@Overridepublic boolean isSingleton() {return FactoryBean.super.isSingleton();}
}

1.5 @Conditional条件注册

@Conditional注解, 用于判断条件是否成立, 如果成立, 则执行对应的配置
@Conditional使用范围:

  • @Bean注解标注的方法上
  • @Configuration注解标注的类上

场景:
判断当前电脑系统是否是windows系统还是Mac, 如果是windows, 则创建一个windowsMouse, 否则创建一个macMouse

  1. 定义一个Mouse类
@Data
public class Mouse {private int id;private String name;public Mouse(int id, String name) {this.id = id;this.name = name;}
}
  1. 定义两个条件类, 实现Spring context的Condition接口
public class WindowEnvCondition implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {String osEnv = context.getEnvironment().getProperty("OS");System.out.println(osEnv);if ("Windows".equals(osEnv)) {return true;}//返回true标识条件匹配return false;}
}
public class MacEnvCondition implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {String osEnv = context.getEnvironment().getProperty("OS");System.out.println(osEnv);if ("Mac".equals(osEnv)) {return true;}//返回true标识条件匹配return false;}
}
  1. 在配置类中, 使用@Conditional注解, 指定条件类, 如果条件成立, 则创建对应的Mouse对象
@Configuration
public class MouseConfig {@Bean("macMouse")@Conditional({MacEnvCondition.class})public Mouse macMouse() {return new Mouse(1,"mac's mouse");}@Bean("windowsMouse")@Conditional({WindowEnvCondition.class})public Mouse windowMouse() {return new Mouse(2,"window's mouse");}}
  1. 测试
@SpringBootApplication
public class Application {public static void main(String[] args) {//1. 跑起一个Spring的应用, 获取一个ApplicationContext, 即ioc容器ConfigurableApplicationContext ioc = SpringApplication.run(Application.class, args);System.out.println("=============================");System.out.println(ioc.getBean(Mouse.class));}
}

Spring中也实现了一些条件类, 如:
@ConditionalOnBean(name = "")
@ConditionalOnMissingBean(name = "")
@ConditionalOnClass(name = "")等等

1.6 注入

  1. @Autowired注解: 依赖注入, 根据类型自动注入
  2. @Qualifier注解: 依赖注入, 根据名称自动注入, 配合@Autowired注解使用
  3. @Resource注解: 依赖注入, 根据名称自动注入, 不属于Spring官方注解
  4. @Value注解: 注入普通值
    1. @Value("hello"): 注入普通值
    2. @Value("${name}"): 注入配置文件中的值
    3. @Value("#{SpEL}"): 注入SpEL表达式, 可以结合2使用 @Value("#{'${app.name}'.toUpperCase()}")
  5. @Profile注解: 根据环境变量, 注入不同的配置, 比如开发环境: @Profile("dev"), 对应的配置文件中的内容为: spring.profiles.active=dev
  6. @PropertiesSource注解: 注入配置文件, 不使用此注解的默认配置文件是application.properties
  7. @Primary注解: 默认注入的组件, 如果有多个同类型的组件, 则会报错, 需要使用@Primary注解指定一个默认的组件

1.7 组件生命周期

  1. @Bean注解标注的方法, 在Bean初始化完成后调用

  2. InitializingBean: 在Bean初始化完成后调用

  3. DisposableBean: 在Bean销毁前调用

  4. @PostConstruct: 在Bean初始化完成后调用, 可以在构造方法之后调用

  5. @PreDestroy: 在Bean销毁前调用

  6. BeanPostProcessor: 在Bean初始化前后调用, 可以在Bean初始化前后进行操作(很强大, 可以修改Bean的任何内容)

  7. @Bean注解标注的方法, 在Bean初始化完成后调用

//定义一个User类
@Data
public class User {private String name;private int age;public void initMethod() {System.out.println("User initMethod ......");}public void destroyMethod() {System.out.println("User destroyMethod ......");}
}
//定义一个UserConfig类
@Configuration
public class UserConfig {@Bean(initMethod = "initMethod", destroyMethod = "destroyMethod")public User user() {User user = new User();user.setName("tom");user.setAge(18);return user;}
}

测试

@SpringBootApplication
public class Application {public static void main(String[] args) {ConfigurableApplicationContext ioc = SpringApplication.run(Application.class, args);System.out.println(ioc.getBean(User.class));}
}

结果

User initMethod ......
User(name=tom, age=18)
User destroyMethod ......
  1. InitializingBean: 在Bean初始化前调用
  2. DisposableBean: 在Bean销毁前调用
@Data
public class User implements InitializingBean, DisposableBean {//在Bean销毁前调用@Overridepublic void destroy() throws Exception {System.out.println("[DisposableBean] ======= User destroy");}//在属性设置之后调用@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("[initialization]======User afterPropertiesSet ......");}
}
  1. @PostConstruct: 在构造方法之后调用
  2. @PreDestroy: 在Bean销毁前调用
//User类中@PreDestroypublic void preDestroy() {System.out.println("[preDestroy]======User preDestroy");}@PostConstructpublic void postConstruct() {System.out.println("[postConstruct]======User postConstruct");}
  1. BeanPostProcessor: 后置处理器, 需要编写继承类并注解@Component
package com.learning.ssm.spring.ioc.processor;import com.learning.ssm.spring.ioc.bean.User;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;/*** Author fei* Version 1.0* Description TODO* DATA 2024/11/26  21:04*/
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {if(!(bean instanceof User)){return bean;}System.out.println("初始化后置处理器=================  " + beanName);return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);}@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {if(!(bean instanceof User)){return bean;}System.out.println("初始化前置处理器==================   " + beanName);return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);}
}

2. 单元测试

3. Spring AOP

  1. 添加aop依赖
        <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency>
@Component
@Aspect
public class LogAspect {@Pointcut("execution(public * com.learning.ssm.ssmspringaop.calculator.MathCalculator.*(..))")public void pointCut(){}@Before("execution(public * com.learning.ssm.ssmspringaop.calculator.MathCalculator.add(int, *))")public void aspectBefore(JoinPoint joinPoint) throws Throwable {System.out.println(joinPoint.getSignature().getDeclaringTypeName() + "====.====" + joinPoint.getSignature().getName());System.out.println("before ..... ");}@After("pointCut()")public void aspectAfter() throws Throwable {System.out.println("after ..... ");}
}

切入点表达式

3.1 切入点执行顺序

通过@Order注解指定执行顺序
数字越小, 优先级越高

@Component
@Aspect
@Order(1) // 
public class LogAspect {@Before("execution(public * com.learning.ssm.ssmspringaop.calculator.MathCalculator.add(int, *))")public void aspectBefore(JoinPoint joinPoint) throws Throwable {System.out.println(joinPoint.getSignature().getDeclaringTypeName() + "====.====" + joinPoint.getSignature().getName());System.out.println("before ..... ");}
}

3.2 环绕通知

@Aspect
@Component
public class AroundAspect {@Around("execution(public int com.learning..calculator.*.add(int,int))")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {System.out.println("[环绕] ----- 前置通知");Object result = null;try {result = joinPoint.proceed();System.out.println("[环绕] ----- 返回通知");}catch (Throwable throwable){System.out.println("[环绕] --- 异常通知");}finally {System.out.println("[环绕] ----- 后置通知");}return result;}
}

4. Spring 声明式事务

通过注解等方式配置事务管理器

@SpringBootApplication
@EnableTransactionManagement //开启基于事务的自动化事务管理
public class TxApplication {public static void main(String[] args) {SpringApplication.run(TxApplication.class, args);}}
    @Transactional // 对方法进行事务管理    @Overridepublic void checkout(String username, Integer bookId, Integer buyBum) {//1. 查询书本信息Book bookById = bookDao.getBookById(bookId);// 1.1 计算扣减额度BigDecimal multiply = new BigDecimal(buyBum).multiply(bookById.getPrice());//2. 修改账户金额accountDao.updateBalanceByUsername(username,multiply);//3. 修改库存bookDao.updateBookStock(bookId,buyBum);}

4.1 隔离级别

  • 读未提交: 事务可以读取其他事务未被提交到数据, 易产生脏读和幻读等问题
  • 读已提交: 事务只能读取到其他事务已经提交的数据, 不能读取到未提交的数据, 读取期间可以修改添加
  • 可重复读: 事务可以读取到其他事务已经提交的数据, 且读取期间不能修改, 可以添加
  • 串行化: 事务只能读取到其他事务已经提交的数据, 且读取期间不能修改, 不能添加

前三种隔离级别导致的问题:

  • 脏读:读取到未提交的数据。
  • 不可重复读:两次读取同一数据,结果不一致(数据被修改)。
  • 幻读:两次读取同样条件的记录,结果集条数发生变化(记录被插入或删除)。

4.2 事务的传播行为

事务的传播行为,即一个事务方法被另一个事务方法调用时,该调用事务方法的事务行为。

  • REQUIRED(默认): 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中,这是最常见的选择。 (常用)
  • SUPPORTS: 支持当前事务,如果当前没有事务,就以非事务方式执行。
  • MANDATORY: 必须存在事务,如果当前没有事务,就抛出异常。
  • REQUIRES_NEW: 无论是否有事务, 都新建一个事务。 (常用)
  • NOT_SUPPORTED: 以非事务方式执行,如果当前存在事务,就把当前事务挂起。
  • NEVER: 以非事务方式执行,如果当前存在事务,则抛出异常。 (常用)
  • NESTED: 新建事务,如果当前存在事务,则把当前事务挂起。

子事务会继承父事务的隔离级别,以及除了传播行为的外的其他属性。

5. SpringMVC

Web应用的核心就是处理HTTP请求和HTTP响应

		<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-tomcat</artifactId><scope>provided</scope></dependency>

如果是前后分离的开发方式, 对于Controller, 统一使用@RestController注解

/*** consumes代表服务器接收的数据类型* */
@RequestMapping(value = "/consume", consumes = "application/json")
public String consume(){return "hello world! --- Consume";
}
// produces代表服务器返回的数据类型
@RequestMapping(value = "/produce", produces = "application/json")
public String produce(){return "hello world! --- Produce";
}@RequestMapping(value = "/html", produces = "text/html;charset=UTF-8")
public String html(){return "<h1>这是HTML页面内容</h1>";
}

5.1 HTTP请求与响应

HTTP请求会携带各种数据:

  1. 请求首行: (请求方式、请求路径、 请求协议)
  2. 请求头: 键值对信息 K:V (请求头信息、请求参数、Cookie、Session等)
  3. 请求体: 特定格式的请求数据

而响应会携带响应头和响应体。

5.2 三种请求的处理

  1. 普通参数请求

// localhost:8080/user/pojo?name=zhangsan&age=18
@RequestMapping(value = "/pojo")
public String pojo(User user){System.out.println(user);return "hello world! --- File";
}
  1. Json参数请求
@RequestMapping(value = "/pojoJson")
public String pojoJson(@RequestBody Person person){System.out.println(person);return "hello world! --- File";
}
  1. 文件请求
    @RequestMapping(value = "/file")public String file(MultipartFile file) throws IOException {System.out.println(file.getOriginalFilename());file.transferTo(new File( file.getOriginalFilename()));return "hello world! --- File";}
server:port: 8081  # 配置端口号tomcat:basedir: your directory # 配置Tomcat的basedir属性,指定Tomcat的根目录。  spring:servlet:multipart:max-file-size: 100MB # 设置上传文件的最大大小max-request-size: 200MB # 设置上传请求的总大小resolve-lazily: true # 设置是否立即解析请求参数,默认为false,即在请求参数解析之前就进行文件上传location: /tmp # 设置上传文件的临时目录file-size-threshold: 10KB # 设置上传文件的大小阈值,超过该值则使用临时文件存储,默认为0,即不启用enabled: true # 设置是否启用文件上传功能,默认为true

5.3 两种返回数据格式

  1. 返回JSON数据
@RequestMapping(value = "/person")
public Person person(){Person person = new Person();person.setName("zhangsan");person.setAge(18);person.setAddress(new String[]{"China","Japan"});return person;
}
  1. 下载文件
    因为是文件,需要设置响应头,告诉浏览器下载文件. 所以既要修改响应头,还需要修改响应体
    Spring框架因此提供了ResponseEntity对象,用来封装响应头和响应体, 多用于文件的下载操作
@RequestMapping(value = "/download")
public ResponseEntity<InputStreamResource> download(String fileName) throws IOException {FileInputStream fileInputStream = new FileInputStream("/Users/fei/JavaCode/Java_basic/ssm/springmvc-01-helloworld/src/main/resources/static/dev.yaml");// 1. 解决文件名中文乱码String encode = URLEncoder.encode("中文dev.yaml", StandardCharsets.UTF_8);// 2. 解决文件内容过大(文件太大,如果读取byte[]结束再传输会导致oom内存溢出)InputStreamResource inputStreamResource = new InputStreamResource(fileInputStream);return ResponseEntity.ok().header("Content-Disposition","attachment;filename=" + encode).contentType(MediaType.APPLICATION_OCTET_STREAM).contentLength(fileInputStream.available()) // 内容大小.body(inputStreamResource);
}

5.4 RestFul

调用别人的功能的方式:

  1. API: 给第三方发送请求, 获取响应数据
  2. SDK: 导入jar包

5.5 后端允许前端跨域

原理: 服务器给浏览器的响应头中添加字段, Access-Control-Allow-Origin: *
跨域访问过程:

  1. 浏览器发送OPTIONS预检请求--> 请求到服务器
  2. 服务器返回响应头Access-Control-Allow-Origin: *
  3. 浏览器收到响应头后, 允许跨域访问, 发送真正的请求
@CrossOrigin // 默认允许所有源跨域访问, value 指定允许的源, 例如 "http://localhost:80"
@RequestMapping(value = "/person")
public Person person(){Person person = new Person();
}

5.4 @PathVariable 路径变量

  1. /resource/{id}: {}中的值封装到id变量中
    1. /resource/1
  2. /resource/{*path}: {}中的值封装到path变量中, {}中可以是多重路径
    1. /resource/a.png
    2. /resource/a/b/c.png
  3. /resource/{filename:\\w+}.png: {}中的值封装到filename变量中, 正则表达式匹配
    1. /resource/a.png
    2. /resource/a_b.png
@RequestMapping(value = "/resource/{id}")
public String resource(@PathVariable("id") Integer id){return "hello world! --- Resource";
}

5.5 拦截器

过滤器(Filter):它的作用范围更广,通常用于处理整个请求和响应流。它不仅可以作用于 Spring MVC 的请求,也可以处理其他非 Spring 的请求(例如:静态资源、外部 API 请求等)。因此,如果你的需求是对整个应用层面的请求进行处理,应该选择过滤器。

拦截器(Interceptor):拦截器是 Spring MVC 专用的,主要用于控制器方法的调用过程。如果你只需要在控制器级别进行操作(例如:权限校验、日志记录、方法执行的前后处理),那么拦截器更加合适。

一般情况, 只要使用Spring, 优先使用拦截器。

  1. 创建拦截器
@Component
public class MyInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("preHandle.......");return HandlerInterceptor.super.preHandle(request, response, handler);}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("postHandle.......");HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);}
}
  1. 配置拦截器
@Configuration
public class MyMVCConfig implements WebMvcConfigurer {@AutowiredMyInterceptor myInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(myInterceptor).addPathPatterns("/interceptor"); //拦截interceptor请求}
}

5.6 异常处理

两种异常处理方式:

  1. 编程式异常处理: try-catch、throw exception
  2. 声明式异常处理: @ControllerAdvice + @ExceptionHandler

测试

@RestControllerAdvice
public class GlobalExceptionController { // 此类一般放到advice中@ExceptionHandler(ArithmeticException.class)public R arithmeticError(ArithmeticException e) {System.out.println(e.getMessage());return R.error(e, "算数异常" + e.getMessage());}
}

5.7 数据校验

后端的简单数据校验可以使用JSR303规范, JSR303是java为Bean数据合法性提供的标准框架, 它已经包含在JavaEE6标准中.
JSR303通过Bean属性上标注类似于@NotBlank/@NotNull/@Size/@Min/@Max/@Pattern等注解, 来声明属性的合法性.

校验流程:

  1. 引入校验依赖: spring-boot-starter-validation
  2. 定义封装的数据Bean
  3. 给Bean的字段添加校验注解, 并指定校验错误的消息提示
  4. 在Controller上添加@Valid注解,@Validated 激活校验
  5. 编写一个全局异常处理器, 拦截MethodArgumentNotValidException异常, 返回校验失败的错误信息
  6. 使用自定义校验注解 + 校验器(implements ConstraintValidator)完成自定义校验
  7. 结合校验注解message属性于i18n文件, 实现错误消息国际化

编写校验规则

@Data
public class User {@NotNull(message = "id 不能为空")private Integer id;@NotNull(message = "名字不能为空")private String name;@NotBlank(message = "密码不能为空")private String password;
}

激活校验

    @RequestMapping("/validateGlobalException")public R VE(@RequestBody @Validated User user) {return R.ok(user);}

校验失败的异常处理

    @ExceptionHandler(MethodArgumentNotValidException.class)public R methodArgumentNotValidException(MethodArgumentNotValidException e){Map<String,String> errorsMap = new HashMap<>();e.getBindingResult().getFieldErrors().forEach(fieldError -> {//获取属性名String field = fieldError.getField();//获取校验失败后的错误消息String message = fieldError.getDefaultMessage();errorsMap.put(field,message);});return R.error( 400, "校验失败" ,errorsMap);}

使用正则表达式

@Pattern(regexp = "^男|女$", message = "性别只能为: “男” 或 “女” ")
@NotBlank
private String gender;

自定义校验

  1. 定义自定义验证器
public class GenderValidator implements ConstraintValidator<Gender, String> {/**** @param s 用户侧提交的还未校验的属性值* @param constraintValidatorContext 校验上下文* @return*/@Overridepublic boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {return "male".equals(s) || "female".equals(s);}
}
  1. 定义校验注解
@Documented
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {GenderValidator.class}) // 校验器完成真正的校验功能
public @interface Gender {String message() default "{validator.gender}";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};}
  1. 定义国际化文件
# messages.properties
validator.gender=只能为男性或女性# messages_en_US.properties
validator.gender=only can be male or female!# messages_zh_CN.properties
validator.gender=只能为男性或女性
  1. 测试-Controller
    @RequestMapping("/validateGlobalException")public R VE(@RequestBody @Validated User user) {return R.ok(user);}
  1. 测试-Postman
curl --location --request GET 'http://localhost:8080/validateGlobalException' \
--header 'Accept-Language: en-US' \
--header 'Content-Type: application/json' \
--data '{"name": "jack","id": 32,"password": "","gender": "mafle"}'
  1. 结果
{"code": 400,"msg": "校验失败","data": {"password": "密码不能为空","gender": "only can be male or female!"}
}

虽然通过以上方式可以实现数据的校验, 但是实际在生产当中, 为了准守单一职责的设计模式, 我们不会使用上述方式进行数据校验
各种xxO:

  1. DAO: Data Access Object, 数据访问对象
  2. TO: Transfer Object, 传输对象
  3. BO: Business Object, 业务对象
  4. VO: View Object, 视图对象(用于与前端数据交互)
  5. DTO: Data Transfer Object, 数据传输对象

5.8 接口文档Swagger

Swagger可以快速生成接口文档,方便前后端开发人员协调沟通
Knife4j --> Swagger --> OpenAPI

  1. 添加依赖
<dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId><version>4.4.0</version>
</dependency>
  1. 配置
# springdoc-openapi项目配置
springdoc:swagger-ui:path: /swagger-ui.htmltags-sorter: alphaoperations-sorter: alphaapi-docs:path: /v3/api-docsgroup-configs:- group: 'default'paths-to-match: '/**'packages-to-scan: com.learning.ssm.springmvc02validation_exception.controller
# knife4j的增强配置,不需要增强可以不配
knife4j:enable: truesetting:language: zh_cn
  1. 访问
    地址: http://localhost:8080/doc.html

5.9 SpringMVC运行流程

6. MyBatis

MyBatis 是一款优秀的持久层框架

开发工具准备: 在IDEA中安装插件MyBatisX
使用MyBatis流程:

  1. 配置数据库连接池
spring:application:name: mybatis-01-helloworlddatasource:url: jdbc:mysql://localhost:3306/mybatis-exampleusername: rootpassword: rootdriver-class-name: com.mysql.jdbc.Driver
  1. 编写Dao层接口(Mapper)
@Mapper //告诉Spring, 这是Mybatis操作的接口
public interface empMapper {Emp getEmpById(int id);
}
  1. 编写Mapper.xml文件(位于resource/mapper文件夹下)
    可以通过MyBatisX插件生成
<?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="com.learning.ssm.mybatis.dao.empMapper"><!--namespace代表这个mapper接口和此xml文件进行绑定--><!-- select标签代表查询, id绑定方法名 --><select id="getEmpById" resultType="com.learning.ssm.mybatis.bean.Emp">select id, emp_name empName, age, emp_salary empSalary from t_tmp where id = #{id};</select>
</mapper>
  1. 通知MyBatis各个Mapper接口对应的Mapper.xml文件地址
spring:application:name: mybatis-01-helloworlddatasource:url: jdbc:mysql://localhost:3306/mybatis-exampleusername: rootpassword: rootdriver-class-name: com.mysql.jdbc.Drivermybatis:mapper-locations: classpath:mapper/**.xml # 配置mapper.xml文件的位置configuration:map-underscore-to-camel-case: true # 开启下划线自动转驼峰命名规则
logging:level:com.learning.ssm.mybatis.mapper: debug # 开启mybatis的日志级别为debug

6.1 两种取值方式的区别

  1. #{id}: 占位符, 占位符的值是运行时从参数对象中取出对应的值
  2. ${id}: 拼串, 拼串的值是运行时从参数对象中取出对应的值, 但是拼串的值会被直接拼在sql中, 有sql注入的风险

对于表名, 可以使用${tableName}传入表名, 但是不能使用#{tableName}

6.2 自定义返回结果集

  • id标签: id标签用于定义主键
  • result标签: result标签用于定义普通值
  • association标签: association标签用于定义对象
  • collection标签: collection标签用于定义集合
    <resultMap id="resultRM" type="com.learning.ssm.mybatis.bean.Emp"><id column="id" property="id"></id>  <!--定义组件映射关系--><result column="emp_name" property="empName" javaType="java.lang.String" jdbcType="VARCHAR"></result> <!--定义普通值映射关系--><result column="emp_salary" property="empSalary"></result><result column="age" property="age"></result></resultMap><select id="getEmpById_" resultMap="resultRM">select *from t_tmpwhere id = #{id}</select>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/974249.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HTML游戏创建:利用视频作为特效自动播放的方法

HTML游戏创建:利用视频作为特效自动播放的方法pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", &…

第四章-Tomcat线程模型与运行方式 - 指南

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

11-21

今日学习了 Date 类的使用,掌握了 SimpleDateFormat 类的日期格式化(yyyy-MM-dd HH:mm:ss)与解析功能,完成了当前日期输出与生日计算案例。 明日计划学习异常处理机制,重点理解 try-catch-finally 语句,以及常见…

11-25

今日学习了字符流的适用场景(文本文件处理),掌握了 FileReader、FileWriter 的读写操作,以及缓冲流(BufferedReader/BufferedWriter)的效率优化。 明日计划学习集合框架,重点理解 List 接口的实现类(ArrayList…

11-24

今日掌握了字节流的核心类(FileInputStream、FileOutputStream),学会了使用字节流读取文件内容、写入数据到文件,完成了图片复制案例。 明日计划学习字符流(Reader/Writer),理解字节流与字符流的区别,重点掌握…

2023-10-15-R-如何阅读一本书

从五月份开始直至昨天,断断续续读完了这本书,但这个过程还是过于走马观花。因为是走马观花,所以大部分的内容根本没有深入理解,更谈不让将之运用于实践了。不过我知道这是一本好书,一本对于我的个人发展是一本好书…

2023-10-11-T-JAVA

1. 易忘知识点用于记录已遗忘的知识点1.1 基础语法一个JAVA文件中的不同类中均可以有public static 方法, 并且可用java 类名的方式运行public static方法javadoc 用于生成java文件的文档,使用方式javadoc xxx.java.…

通过SSH反向隧道让远程服务器走本地代理

通过建立 SSH 反向隧道,使无法直连外网的远程 Linux 服务器能借助本地 Windows 电脑的代理网络执行 git pull 等操作。1. 场景描述 先说一下笔者遇到的情况:本地 Windows 电脑 已经配置好代理或者加速工具,能正常访…

2023-09-19-R-金字塔原理

前言 简介 金字塔原理是美国作家芭芭拉明托在1966年所著,她是著名咨询公司麦肯锡聘用的第一位女性咨询顾问。金字塔原理是她在1966年被派往英国,担任负责麦肯锡欧洲员工写作能力的职位后总结得出的。金字塔原理介绍了…

2023-09-19-E-文章管理

文章管理2023年9月19日生效时间-文章类型-文章名称 文章类型:日记随想:D : Thoughts总结反思:S : Summary技术文章:T : Tech读书笔记:R : Reading效率管理:E : Efficiency日记: 记录一天经历的事件或所思所想 技…

11-6

今日深入学习了 for、while、do-while 三种循环的区别与适用场景,通过循环嵌套完成了九九乘法表、菱形图案打印。 明日计划学习数组的定义、初始化方式,以及数组的遍历、排序等基础操作。

11-18

今日深入学习了 String 类的不可变性,掌握了 charAt ()、substring ()、equals ()、indexOf () 等常用方法,完成了字符串反转、判断回文案例。 明日计划学习 StringBuffer 与 StringBuilder 类的使用,对比三者的效率…

11-12

今日深入学习了封装的意义,通过 private 修饰成员变量,编写 getter/setter 方法实现数据的安全访问,完成了学生类的封装案例。 明日计划学习继承的语法(extends 关键字),理解父类与子类的关系,以及方法的重写。…

11-11

今日理解了面向对象的核心思想(封装、继承、多态),掌握了类的定义(成员变量、成员方法)与对象的实例化(new 关键字)。 明日计划学习封装的实现方式,重点掌握 private 访问修饰符、getter/setter 方法的编写。

苹果app开发上架流程

苹果App开发上架流程主要包括注册账号、开发设置、打包上传、完善信息及提交审核等步骤,具体如下: 1. 注册Apple iOS开发者账号:访问Apple开发者网站,选择“Account”进行注册。填写真实信息,可能需人脸识别和上传…

11-10

今日学习了递归的原理与边界条件设置,通过递归完成了阶乘计算、斐波那契数列输出,理解了递归的优缺点。 明日计划正式进入面向对象编程,学习类与对象的概念,掌握类的定义与对象的创建。

P14566 【MX-S12-T1】取模

先将 \(a\) 排序。 显然,取 \(a_n + 1\) 是一种比较不错的方案,但此时如果最小值很大就会寄掉。 此时 \(p\) 只能够取 \(\le a_n\) 的数,无论取什么最大值都不会超过严格次大值(分类讨论即可),此时让 \(p = a_n\…

TCP 消息分段与粘包问题的完整解决方案 - 指南

TCP 消息分段与粘包问题的完整解决方案 - 指南pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", &q…

语音技术助力非标准语音人群沟通

Voiceitt开发个性化语音识别技术,通过卷积神经网络模型帮助有语言障碍的人群使用语音助手和与他人交流,让非标准语音用户也能享受语音技术带来的便利。Voiceitt将语音革命延伸至非标准语音人群 根据某机构的数据,美…

洛谷 B4357:[GESP202506 二级] 幂和数 ← 嵌套循环

​【题目来源】https://www.luogu.com.cn/problem/B4357【题目描述】对于正整数 n,如果 n 可以表为两个 2 的次幂之和,即 n=2^x+2^y(x,y 均为非负整数),那么称 n 为幂和数。给定正整数 l,r,请你求出满足 l≤n≤…