AI ------>>>直接使用阿里的 通义天问
Maven基础
介绍
Maven 介绍
Maven 作用
项目构建 比较简单~
核心功能
依赖管理
<!-- gavp属性--><groupId>com.example</groupId><artifactId>tials-manage</artifactId><version>0.0.1-SNAPSHOT</version><!-- 打包方式 jar--><packaging>jar</packaging><!-- 自定义属性--><properties><java.version>1.8</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><spring-boot.version>2.6.13</spring-boot.version></properties>
依赖传递
依赖传递 A----->B------>C
导入依赖,会自动导入依赖的依赖 即依赖传递
依赖冲突
解决: 谁短谁 优先—>>>引用的路径长度
继承
不需要写 版本
如果 子工程声明了依赖版本,以 子工程 依赖版本为主
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><!-- gavp属性--><groupId>com.example</groupId><artifactId>tials-manage</artifactId><version>0.0.1-SNAPSHOT</version><!-- 打包方式 jar--><packaging>jar</packaging><!-- 自定义属性--><properties><java.version>1.8</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><spring-boot.version>2.6.13</spring-boot.version></properties><!-- 导入依赖--><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><!-- 依赖范围--><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.9</version></dependency></dependencies><!-- 导入插件--><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><source>1.8</source><target>1.8</target><encoding>UTF-8</encoding></configuration></plugin><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>${spring-boot.version}</version><configuration><mainClass>com.example.tialsmanage.TialsManageApplication</mainClass><skip>true</skip></configuration><executions><execution><id>repackage</id><goals><goal>repackage</goal></goals></execution></executions></plugin></plugins></build><!-- 只做 依赖的版本控制,不导入依赖--><dependencyManagement></dependencyManagement></project>
Spring
介绍
IOC
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><!-- 依赖范围--><scope>test</scope>
</dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.ConfigurableApplicationContext;@SpringBootApplication
@ServletComponentScan // 扫描过滤器
public class TialsManageApplication {public static void main(String[] args) {ConfigurableApplicationContext ioc = SpringApplication.run (TialsManageApplication.class, args);Object zs = ioc.getBean ("zhangsan");}}
@ComponentScan(basePackages = "com.example.tialsmanage") // 扫描注解,只扫 Spring注解
介绍
@Bean + @Configuration
@Configuration // 配置类
public class PersonConfig {/*** 将返回值放入容器* 默认是单例的** @return*/@Scope@Bean("zhangsan") // 默认方法名就是 bean的idpublic User zhangsan() {return new User ("zhangsan", 18);}}
@Component 与衍生 分层注解
/*** 默认,分层注解 能起作用的前提是: 这些组件 必须在主程序所在的包及其子包结构下。** Spring为我们提供了快速的 MVC分层注解:* 1. @Controller 控制器* 2. @Service 服务层* 3. @Repository 持久层* 4. @Component 组件** @param args*/
@Scope + @Lazy
/*** @Scope 调整组件的作用域:* 1. @Scope("prototype"): 非单实例:* 容器启动的时候 不会 创建非单实例组件的对象。* 什么时候获取,什么时候创建~* 2. @Scope("singleton"): 单实例: 默认值* 容器启动的时候 会创建 单实例组件的对象。* 容器启动完成之前就会创建好* @Lazy: 懒加载* 容器启动 完成之前不会创建 懒加载组件的对象* 什么时候获取,什么时候创建* 3. @Scope("request"): 同一个请求单实例* 4. @Scope("session"): 同一次会话单实例** @return*/@Configuration
@Scope("singleton")
@Lazy // 单例模式下,可以继续调整为 懒加载public class WebConfig implements WebMvcConfigurer {@Autowiredprivate TokenInterceptor tokenInterceptor;}
@Conditional 条件注册 + @ConditionalOnMissingBean 没有则注册
MacCondition
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;/*** @author dc* @version 1.0* @date 2025/02/26 23:16*/
public class MacCondition implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {// 判断环境变量中的OS,如果是mac,则返回trueif (context.getEnvironment ().getProperty ("os.name").contains ("Mac")) {return true;}return false;}
}
WindowsCondition
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;/*** @author dc* @version 1.0* @date 2025/02/26 23:15*/
public class WindowsCondition implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {// 判断环境变量中的OS,如果是 windows,则返回trueString property = context.getEnvironment ().getProperty ("os.name");if (property.contains ("Windows")) {return true;}return false;}
}
** @Conditional 使用 **
@Conditional(value = WindowsCondition.class)@Beanpublic User bill() {return new User ("bill", 25);}@Conditional(value = MacCondition.class)@Beanpublic User qbs() {return new User ("qbs", 35);}
@Autowired + @Resource + @Primary
import com.example.tialsmanage.Interceptor.TokenInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;import javax.annotation.Resource;@Configurationpublic class WebConfig implements WebMvcConfigurer {@Autowired // 按照类型注入private TokenInterceptor tokenInterceptor;@Autowiredprivate User user;@Resourceprivate MacCondition macCondition;@Beanpublic User u2() {return new User ("李四", 21);}@Primary // 按照类型注入时 优先级最高@Beanpublic User u1() {return new User ("张三", 18);}@Beanpublic MacCondition macCondition() {return new MacCondition ();}}
@Profile ---- 多环境
@Profile({"dev", "default"})public User u2() {return new User ("李四", 21);}@Profile("test")@Beanpublic User u1() {return new User ("张三", 18);}
spring:profiles:active: dev
生命周期
import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;@Data
@AllArgsConstructor
public class User implements InitializingBean, DisposableBean {private String name;private Integer age;public void initUser() {System.out.println ("@Bean 初始化User");}public void destoryUser() {System.out.println ("@Bean 销毁User");}@Overridepublic void afterPropertiesSet() throws Exception {System.out.println ("afterPropertiesSet");}@Overridepublic void destroy() throws Exception {System.out.println ("destroy");}@PostConstruct // 构造器之后public void postConstruct() {System.out.println ("PostConstruct");}@PreDestroypublic void preDestroy() {System.out.println ("PreDestroy");}
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configurationpublic class WebConfig {private MacCondition macCondition;@Autowiredpublic void setMacCondition(MacCondition macCondition) {System.out.println ("自动注入属性"+macCondition); // 在 initMethod之前调用this.macCondition = macCondition;}/*** initMethod 在属性设置之后-->>set之后* @return*/@Bean(initMethod = "initUser",destroyMethod = "destoryUser")public User get() {return new User ("zs", 15);}}
import com.example.tialsmanage.config.User;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;@Component // 拦截所有Bean的后置处理器
public class MyTestBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println ("【postProcessAfterInitialization】: " + beanName);return bean;}@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println ("【postProcessBeforeInitialization】: " + beanName);if (bean instanceof User) { // bean为 User类User u = (User) bean;u.setName ("张三测试");}return bean;}
}
单元测试 @SpringBootTest
import com.example.tialsmanage.config.User;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;@SpringBootTest
@Slf4j // lombok 日志
class TialsManageApplicationTests {@AutowiredUser user;@Testvoid contextLoads() {System.out.println ("Hello World");}}
AOP
导入依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId><version>2.3.12.RELEASE</version>
</dependency>
切面
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;@Order(1) // 调顺序 ,数字越小--->>>优先级越高
@Component // 交给 spring 管理
@Aspect // 切面类
public class RecordTimeAspect {/*** 线程本地变量* 每个线程一个*/private static final ThreadLocal<Long> threadLocal = ThreadLocal.withInitial (() -> 3000L);@Around("bf()")public void get() {long val = threadLocal.get ().longValue ();System.out.println (val);}/*** 拦截*/@Pointcut(" execution(* com.example.tialsmanage.demos.web.UploadController.*(..))") // 抽取切点表达式public void bf() {}@Before("@annotation(Login)") // 拦截所有带Login注解的方法public void before() {System.out.println ("before");}@After("@annotation(Login)") // 拦截所有带Login注解的方法public void after() {System.out.println ("after");}}
@After ("@annotation(ReTime)")public Object recordTime(ProceedingJoinPoint joinPoint) {long start = System.currentTimeMillis ();Object proceed = null;try {proceed = joinPoint.proceed ();} catch (Throwable e) {throw new RuntimeException (e);}long end = System.currentTimeMillis ();System.out.println ("运行时间:" + (end - start));return proceed;}
AOP 应用场景
- 日志记录(Logging)
场景:
需要 记录方法 执行时间、参数、返回值或异常信息,但又 不希望日志代码侵入业务逻辑。
实现逻辑:
切面(Aspect):定义日志记录逻辑(如@Around或@AfterThrowing通知)。
切点(Pointcut):匹配需要记录的方法(如 execution(* com.example.service..(…)))。
示例代码(Spring AOP):
@Aspect
@Component
public class LoggingAspect {private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);@Around("execution(* com.example.service.*.*(..))")public Object logMethodExecution(ProceedingJoinPoint joinPoint) throws Throwable {long startTime = System.currentTimeMillis();Object result = joinPoint.proceed();long duration = System.currentTimeMillis() - startTime;logger.info("Method {} executed in {} ms", joinPoint.getSignature(), duration);return result;}
}
优势:
日志代码集中管理,业务代码保持简洁。
修改日志策略时 无需改动业务方法
。
- 事务管理(Transaction Management)
场景:
在数据库操作中自动开启、提交或回滚事务,避免手动重复编写事务代码。
实现逻辑:
声明式事务:通过 @Transactional
注解标记事务边界。
底层实现:Spring AOP代理类拦截注解方法,结合PlatformTransactionManager管理事务。
示例配置:
@Service
public class UserService {@Transactionalpublic void createUser(User user) {// 数据库操作(如插入用户记录)}
}
优势:
事务控制与业务逻辑解耦。
支持传播行为、隔离级别等高级配置。
- 权限校验(Authentication & Authorization)
场景:
在方法执行前验证用户权限(如角色校验、接口访问控制)。
实现逻辑:
自定义注解:定义权限校验注解(如@RequireRole(“ADMIN”))。
切面拦截:在方法执行前(@Before)校验权限。
示例代码:
@Aspect
@Component
public class SecurityAspect {@Before("@annotation(RequireRole)") // 拦截标注了RequireRole 注解的方法public void checkRole(JoinPoint joinPoint) {MethodSignature signature = (MethodSignature) joinPoint.getSignature();RequireRole annotation = signature.getMethod().getAnnotation(RequireRole.class);String requiredRole = annotation.value();// 从上下文中获取当前用户角色String currentRole = getCurrentUserRole();if (!requiredRole.equals(currentRole)) {throw new AccessDeniedException("权限不足");}}
}
优势:
权限逻辑 集中化 ---------->>>避免 每个方法 重复校验
。
通过注解灵活控制权限粒度。
- 性能监控(Performance Monitoring)
场景:
统计方法执行耗时,定位性能瓶颈。
实现逻辑:
使用@Around通知计算执行时间。
将耗时数据推送至监控系统(如Prometheus)。
示例代码:
@Aspect
@Component
public class PerformanceAspect {@Around("execution(* com.example.service.*.*(..))")public Object monitorPerformance(ProceedingJoinPoint joinPoint) throws Throwable {long start = System.currentTimeMillis();Object result = joinPoint.proceed();long elapsedTime = System.currentTimeMillis() - start;Metrics.recordTime(joinPoint.getSignature().getName(), elapsedTime);return result;}
}
优势:
无侵入式监控,代码零耦合。
实时发现性能问题。
- 缓存管理(Caching)
场景:
自动缓存方法返回值,减少重复计算或数据库查询。
实现逻辑:
通过@Cacheable注解声明缓存规则。
示例代码:
@Service
public class ProductService {@Cacheable(value = "products", key = "#id")public Product getProductById(Long id) {return productRepository.findById(id); // 仅第一次调用会执行此方法}
}
底层原理:
Spring AOP拦截方法调用,优先从缓存中读取数据,未命中时执行方法并缓存结果。
- 数据校验(Validation)
场景:
在方法执行前校验参数合法性(如非空检查、格式验证)。
实现逻辑:
结合JSR 303规范(如@Valid、@NotNull)。
自定义切面拦截参数校验。
示例代码:
@Aspect
@Component
public class ValidationAspect {@Before("execution(* com.example.service.*.*(..)) && args(.., @Valid param)")public void validateParameters(JoinPoint joinPoint, Object param) {// 手动触发校验逻辑(如Hibernate Validator)Set<ConstraintViolation<Object>> violations = validator.validate(param);if (!violations.isEmpty()) {throw new ConstraintViolationException(violations);}}
}
何时使用AOP?
横切关注点:多个模块需要相同功能(如日志、权限)。
代码复用:避免重复代码(DRY原则)。
解耦需求:非核心逻辑(如监控)与业务逻辑分离。
避免滥用AOP的情况
-
性能敏感场景:AOP代理可能引入额外开销。
-
过度复杂化:简单逻辑直接编码更清晰。
-
调试困难:链式代理可能增加调试复杂度。
循环依赖、三级缓存
源码------>>> 三级缓存
什么是循环依赖?
Spring可以解决 哪些情况
的循环依赖?
Spring 不支持基于构造器注入的循环依赖,但是假如AB循环依赖,如果一个是构造器注入,一个是 setter注入呢?
看看几种情形:
我们来看一下 三级缓存解决循环依赖
的过程:
当 A、B 两个类发生 循环依赖时:
A实例的初始化过程:
创建A实例,实例化的时候把 A对象⼯⼚放⼊三级缓存,表示 A开始实例化了,虽然我这个对象还不完整,但是先曝光出来让大家知道
A注⼊属性时,发现依赖B,此时B还没有被创建出来,所以去实例化B
同样,B注⼊属性时发现依赖A,它就会从缓存里找A对象。依次从⼀级到三级缓存查询A,从三级缓存通过对象⼯⼚拿到A,发现A虽然不太完善,但是存在,把A放⼊⼆级缓存,同时删除三级缓存中的A,此时,B已经实例化并且初始化完成,把B放入⼀级缓存。
接着A继续属性赋值,顺利从⼀级缓存拿到实例化且初始化完成的B对象,A对象创建也完成,删除⼆级缓存中的A,同时把A放⼊⼀级缓存
最后,⼀级缓存中保存着实例化、初始化都完成的A、B对象
所以,我们就知道为什么Spring能解决setter注入的循环依赖了,因为实例化和属性赋值是分开的,所以里面有操作的空间。如果都是构造器注入的化,那么都得在实例化这一步完成注入,所以自然是无法支持了。
为什么要三级缓存?⼆级不⾏吗?
不行,主要是为了⽣成代理对象。如果是没有代理的情况下,使用二级缓存解决循环依赖也是OK的。但是如果存在代理,三级没有问题,二级就不行了。
因为三级缓存中放的是⽣成具体对象的匿名内部类,获取Object的时候,它可以⽣成代理对象,也可以返回普通对象。使⽤三级缓存主要是为了保证不管什么时候使⽤的都是⼀个对象。
假设只有⼆级缓存的情况,往⼆级缓存中放的显示⼀个普通的Bean对象,Bean初始化过程中,通过 BeanPostProcessor 去⽣成代理对象之后,覆盖掉⼆级缓存中的普通Bean对象,那么 可能就导致取到的Bean对象不一致
了。
事务tx
声明式 事务
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-tx</artifactId><version>5.3.22</version>
</dependency>
实验
/*** 默认:出现运行时异常会回滚* 可以指定回滚类型* 默认只对同一个数据库的事务管理有效* 分布式事务可以实现跨数据库的事务管理** @param username* @param bookId* @param buyNum*/@Transactional(rollbackFor = Exception.class,timeout = 15,propagation = Propagation.REQUIRED) // 超时会回滚public void checkout(String username, Integer bookId, Integer buyNum) {// 1、查询图书信息Book book = bookDao.getBookById (bookId);BigDecimal price = book.getPrice ();// 2、计算扣减额度BigDecimal total = new BigDecimal (buyNum).multiply (price);// 3、扣减余额accountDao.updateBalanceByUsername (username, total.negate ());int i = 10 / 0; // 这行代码会导致除以零异常// 4、扣减库存bookDao.updateBookStock (bookId, buyNum);}
细节
- 默认只对同一个数据库的事务管理有效
详见:[Spring](https://blog.csdn.net/qq_30659573/article/details/127581112)
隔离级别、传播行为
双检查锁、IOC容器启动流程
双检查锁
// 私有的 静态实例变量private static SingletonLazy instance;// 私有的构造函数,防止外部实例化private SingletonLazy() {// 初始化逻辑}// 提供一个公共的静态方法,返回类的唯一实例public static SingletonLazy getInstance() {// 第一次检查实例是否存在,如果不存在,才进入同步块if (instance == null) {synchronized (SingletonLazy.class) {// 双检查// 第二次检查实例是否存在,防止多个线程同时进入同步块if (instance == null) {instance = new SingletonLazy();}}}// 返回实例return instance;}
IOC启动流程简述
在Java的Spring框架中,IOC容器的启动流程主要包括以下几个步骤:
-
读取 配置文件
- Spring容器启动时会读取配置文件(如XML、注解或Java配置类),解析其中的Bean定义信息。
-
实例化BeanFactory
- 创建
BeanFactory
或其子类(如DefaultListableBeanFactory
)作为IOC容器的核心组件。
- 创建
-
注册Bean定义
- 将解析后的Bean定义信息注册到
BeanDefinitionRegistry
中,为后续的Bean创建做准备。
- 将解析后的Bean定义信息注册到
-
初始化Bean工厂后处理器
- 如果配置中有
BeanFactoryPostProcessor
类型的Bean,则在此阶段调用它们。这些处理器可以在Bean实例化之前修改Bean定义属性。
- 如果配置中有
-
实例化所有单例Bean
- 对于所有非懒加载的单例Bean,Spring容器会提前实例化并初始化它们。这包括:
- 实例化:根据Bean定义创建Bean实例。
- 属性填充:通过依赖注入(DI)设置Bean的属性值。
- 初始化前处理:如果存在
InstantiationAwareBeanPostProcessor
,则在此阶段进行处理。 - 初始化方法调用:调用Bean的初始化方法(如
@PostConstruct
注解的方法或InitializingBean
接口的afterPropertiesSet
方法)。 - 初始化后处理:如果存在
BeanPostProcessor
,则在此阶段调用postProcessAfterInitialization
方法。
- 对于所有非懒加载的单例Bean,Spring容器会提前实例化并初始化它们。这包括:
-
完成容器刷新
- 容器完成所有必要的初始化工作后**,发布容器事件通知所有监听器。**
-
使用容器
- 应用程序可以 通过容器获取Bean实例,并使用这些Bean执行业务逻辑。
-
销毁Bean
- 当容器关闭时,会调用所有实现了
DisposableBean
接口或配置了销毁方法的Bean的销毁方法,以释放资源。
- 当容器关闭时,会调用所有实现了
Spirng MVC
介绍及入门
前后端分离开发: @ResponseBody 与 @RestController
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>2.7.5</version>
</dependency>
@RestController // @RestController= @Controller + @ResponseBodypublic class hello {@RequestMapping("/hello")public String hello(){return "hello sd";}
}
@RequestMapping("/hello/{name}")// @PathVariable("name")绑定路径中的name到参数 String name// @GetMapping 、@PostMapping 都可以public String hello(@PathVariable("name") String name) {return "hello sd" + name;}
HTTP 复习
请求头
响应格式
响应状态码
请求处理
1、@RequestParam 接收参数
@RestController // @RestController= @Controller + @ResponseBody
public class Hello {@RequestMapping("/hello")// 将请求参数name 传到参数 String namepublic String hello(@RequestParam(value = "name", required = false) String name,@RequestParam(value = "age", required = false) Integer age) {return "hello " + name + " " + age;}
}
2、接受参数 很多时,使用对象 进行封装
此时属性名和参数名 对应即可~
@Data
public class Person {private String name;private int age;private String sex;private String address;}
@RestController // @RestController= @Controller + @ResponseBodypublic class Hello {@RequestMapping("/hello")public String hello(Person person) {return "hello " + person;}
}
3、@RequestBody 将前端 json数据转为 对象 (需要该对象提供 无参构造方法 )
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;@Data
@AllArgsConstructor
@NoArgsConstructor // 无参构造,不然无法 将json转为对象
public class Person {private String name;private Integer age;private String sex;private String address;
}
@RestController // @RestController= @Controller + @ResponseBody
public class Hello {@PostMapping("/requestBody")// @RequestBody 要求对象提供无参构造方法,动态注入public String hello(@RequestBody Person person) {return "hello " + person;}
}
{"name": "ds","age": 25,"sex": "男","address": "北京"
}
4、原生API : HttpServletRequest 、HttpServletResponse、HttpSession
@RestController // @RestController= @Controller + @ResponseBody
public class Hello {@GetMapping("/set")public String set(HttpServletRequest request) {HttpSession session = request.getSession ();session.setAttribute ("name", "ds");return " set ok ";}@GetMapping("/get")public String get(HttpServletRequest request) {HttpSession session = request.getSession ();Object name = session.getAttribute ("name");return String.valueOf (name);}
}
5、文件上传
/*** MultipartFile 专门上传文件的** @param person* @param headerImg* @param lifeImg* @return*/@GetMapping("/load")public String load(Person person,@RequestParam("headerImg") MultipartFile headerImg,@RequestParam("lifeImg") MultipartFile[] lifeImg) { // 多文件HttpSession session = request.getSession ();session.setAttribute ("name", "ds");return " ok ";}
# 设置传输文件大小
spring:servlet:multipart:max-request-size: 10MBmax-file-size: 100MB
响应处理
1、返回 json
@ResponseBody 返回 json数据时,需要 对象提供无参构造和 set/get 方法
// 二合一注解
// @ResponseBody 返回json数据@RestController // @RestController= @Controller + @ResponseBody
public class Hello {@GetMapping("/resp")public String resp(HttpServletRequest req) {return " Hello World";}}
2、文件下载
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLEncoder;@RestController
public class FileDownloadController {/*** 文件下载** @return* @throws IOException*/@GetMapping("/download")public ResponseEntity<InputStreamResource> download() throws IOException {// 文件路径String filePath = "C:\\Users\\Administrator\\Desktop\\"+"算法刷题.pdf";File file = new File (filePath);// 创建 FileInputStream 对象FileInputStream inputStream = new FileInputStream (file);// 解决文件名中文乱码问题String encode = URLEncoder.encode (file.getName (), "UTF-8");// 以下代码永远不改// 文件太大会oomInputStreamResource resource = new InputStreamResource (inputStream);// 设置响应头信息HttpHeaders headers = new HttpHeaders ();// 内容类型:流headers.setContentType (MediaType.APPLICATION_OCTET_STREAM);// 内容大小headers.setContentLength (inputStream.available ());// 内容处理方式headers.set ("Content-Disposition", "attachment; filename=" + encode);// 返回 ResponseEntity 对象return ResponseEntity.ok ().headers (headers).body (resource);}
}
Restful风格
1、Restful风格 + @PathVariable 入门练习
@PathVariable
@RestController // @RestController= @Controller + @ResponseBody
public class Hello {@GetMapping("/get/{id}")// @PathVariable("id")------->>> 将路径中的id 绑定到 Integer idpublic String resp(@PathVariable("id") Integer id) {return " Hello World "+ id;}}
2、三层架构
解耦
3、统一返回对象
@Data
@AllArgsConstructor
@NoArgsConstructorpublic class Result {/*** 业务的状态码code,200是成功,剩下都是失败;前后端将来会一起商定 不同的业务状态码前端要 显示不同效果。* msg: 服务端返回给前端的提示消息* data: 服务器返回给前端的数据** 示例:* {* "code": 300,* "msg": "余额不足",* "data": null* }** 前端 统一处理:* 1. 前端发送请求,接受服务器数据* 2. 判断状态码,成功就显示数据,失败就显示提示消息(或者执行其他操作)。*/private Integer code;private String msg;private Object data;public static Result ok(Object data) {return new Result (200, "success", data);}
}
{"code": 200,"msg": "success","data": {"name": "ds","age": 15,"sex": "男","address": "北京"}
}
4、跨域处理
浏览器要 遵循同源策略
@RestController // @RestController= @Controller + @ResponseBody
@RequestMapping("/api/v1")
public class Hello {@GetMapping("/get/{id}")public String resp(@PathVariable("id") Integer id) {return " Hello World "+ id;}}
解决跨域
过滤器 Filter 实现:
public class CorsFilter implements Filter {@Overridepublic void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {HttpServletResponse response = (HttpServletResponse) res;response.setHeader("Access-Control-Allow-Origin", "*");response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");response.setHeader("Access-Control-Max-Age", "3600");response.setHeader("Access-Control-Allow-Headers", "x-requested-with");chain.doFilter(req, res);}@Overridepublic void init(FilterConfig filterConfig) {}@Overridepublic void destroy() {}
}
注解方式: @CrossOrigin // 允许跨域,注意:加在类上与 加在方法上的区别
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;/*** CORS policy: 同源策略* 解决跨域: 在Controller上加注解 @CrossOrigin*/@CrossOrigin // 允许跨域,注意:加在类上与 加在方法上的区别
@RestController // @RestController= @Controller + @ResponseBody
public class Hello {@GetMapping("/get/{id}")public String resp(@PathVariable("id") Integer id) {return " Hello World " + id;}}
拦截器
拦截 Controller
拦截器:
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;@Component
@Order(1) // 多个拦截器时,拦截器执行顺序,值越小,优先级越高
public class MyInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 在请求处理之前进行拦截逻辑response.getWriter ().write ("No Permission!");return false; // 返回true表示 继续处理请求,返回false则中断请求处理}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {// 在请求处理之后 进行拦截逻辑}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {// 在请求完成之后 进行拦截逻辑}
}
配置类:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class WebConfig implements WebMvcConfigurer {@Autowiredprivate MyInterceptor myInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor (myInterceptor).addPathPatterns ("/**") // 拦截所有请求.excludePathPatterns ("/login", "/public/**"); // 排除特定路径}
}
异常处理
全局 异常处理
1、统一返回对象
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;@Data
@AllArgsConstructor
@NoArgsConstructor
public class Result {private Integer code; // 状态码private String msg; // 状态码对应的信息private Object data; // 返回的数据public static Result ok(Object data) {return new Result (200, "success", data);}public static Result error(Integer code, String msg) {return new Result (code, msg, null);}
}
2、 定义 异常枚举类
/*** 枚举类,用于表示订单模块中的异常状态。*/
public enum BizExceptionEnum {ORDER_CLOSED (10001, "订单已关闭"),ORDER_NOT_EXIST (10002, "订单不存在"),ORDER_TIMEOUT (10003, "订单超时"),PRODUCT_STOCK_NOT_ENOUGH (20003, "库存不足"),PRODUCT_HAS_SOLD (20002, "商品已售完"),PRODUCT_HAS_CLOSED (20001, "商品已下架");private final int code;private final String msg;BizExceptionEnum(int code, String msg) {this.code = code;this.msg = msg;}public int getCode() {return code;}public String getMsg() {return msg;}
}
3、定义 异常类 ,继承 RuntimeException
import lombok.Data;@Data
// 继承 RuntimeException
public class BizException extends RuntimeException {private Integer code; // 业务异常码private String msg; // 业务异常信息public BizException(Integer code, String message) {super (message);this.code = code;this.msg = message;}public BizException(BizExceptionEnum exceptionEnum) {super (exceptionEnum.getMsg ());this.code = exceptionEnum.getCode ();this.msg = exceptionEnum.getMsg ();}
}
4、全局异常 处理器
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;@RestControllerAdvice // 告诉 Spring,这个类是一个全局异常处理类
public class GlobalExceptionHandler {/*** 默认 从上往下找* 可以建立 异常处理文档*/@ExceptionHandler(ArithmeticException.class)public Result ArithmeticException(ArithmeticException ex) {return Result.error (201, "数学错误");}/*** 业务异常** @param ex* @return*/@ExceptionHandler(BizException.class)public Result handleBizException(BizException ex) {return Result.error (ex.getCode (), ex.getMsg ());}@ExceptionHandler(Exception.class)public Result handleAllExceptions(Exception ex) {return Result.error (500, "未知异常");}}
5、 测试
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@CrossOrigin
@RestController // @RestController= @Controller + @ResponseBody
public class Hello {@GetMapping("/math")public Result resp() {int i = 1 / 0; // 数学异常return Result.ok ("OK");}@GetMapping("/biz")public Result biz() {int stock = -1;if (stock < 0) {// 业务异常,中断业务逻辑throw new BizException (BizExceptionEnum.ORDER_CLOSED);}return Result.ok ("OK");}}
数据校验
引入依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId><version>2.7.5</version>
</dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.24</version>
</dependency>
统一返回类
import lombok.AllArgsConstructor;
import lombok.Data;@Data
@AllArgsConstructorpublic class Result {private Integer code; // 状态码private String msg; // 状态码对应的信息private Object data; // 返回的数据public static Result ok(Object data) {return new Result (200, "success", data);}public static Result error(Integer code, String msg) {return error (code, msg, null);}public static Result error(Integer code, String msg, Object data) {return new Result (code, msg, data);}
}
自定义校验注解
// 校验注解,需要绑定校验器
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = {GenderValidator.class} // 指定校验器去完成校验
)
public @interface Gender {String message() default "性别只能为:男/女";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};}
自定义校验器
import org.example.Gender;import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;public class GenderValidator implements ConstraintValidator<Gender, String> { // 校验类型:String/*** @param value 前端提交来的,准备让我们进行校验的属性值* @param context 校验上下文* @return 返回校验结果*/@Overridepublic boolean isValid(String value, ConstraintValidatorContext context) {return "男".equals (value) || "女".equals (value);}
}
用户类User
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import javax.validation.constraints.Email;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.Size;@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {@Size(min = 2, max = 4, message = "用户名长度必须在2-4之间")private String name;@Max(value = 100, message = "最大不超过100岁")@Min(0)private Integer age;private String sex;@Email(message = "邮箱格式不正确")private String email;private String address;@Size(min = 11, max = 11, message = "手机号不正确")private String phone;@Gender // 自定义校验注解private String gender;}
全局异常处理
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;import java.util.Map;
import java.util.stream.Collectors;@RestControllerAdvice // 告诉 Spring,这个类是一个全局异常处理类
public class GlobalExceptionHandler {/*** 默认 从上往下找* 可以建立 异常处理文档*/@ExceptionHandler(ArithmeticException.class)public Result ArithmeticException(ArithmeticException ex) {return Result.error (201, "数学错误");}// 处理参数校验@ExceptionHandler(MethodArgumentNotValidException.class)public Result handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {BindingResult bindingResult = ex.getBindingResult ();// 使用 HashMap封装 字段校验错误信息Map<String, Object> map = bindingResult.getFieldErrors ().stream ().collect (Collectors.toMap (e -> e.getField (), e -> e.getDefaultMessage ()));return Result.error (500, "校验失败", map);}@ExceptionHandler(Exception.class)public Result handleAllExceptions(Exception ex) {return Result.error (500, "未知异常");}}
Controller 控制器
import org.example.Result;
import org.example.User;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;import javax.validation.Valid;@CrossOrigin
@RestController // @RestController= @Controller + @ResponseBody
public class Hello {@PostMapping("/test")public Result resp(@RequestBody @Valid User user) {return Result.ok (user);}}
测试
最佳实践
1、JavaBean 分层
2、 VO的最佳实践
@PostMapping("/employee")
public Result add(@RequestBody @Valid EmployeeAddVo vo) {// 把vo转为do:Employee employee = new Employee();// 属性对拷,Spirng 提供BeanUtils.copyProperties(vo, employee);// 保存员工信息employeeService.saveEmp(employee);return Result.ok();
}
3、接口文档
Swagger 接口文档
源码流程图【面试】
Mybatis
入门
1、安装插件
2、导入依赖
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.3</version></dependency><!-- web 依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- MySQL JDBC 驱动 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.30</version><scope>runtime</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.22</version><scope>compile</scope></dependency>
3、yaml
# 配置MySQL
spring:profiles:active: testdatasource:url: jdbc:mysql://localhost:3306/db?useSSL=false&serverTimezone=UTCusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driver# 日志
logging:level:# 设置mapper的日志级别为debugorg.example.mapper: debug
# 添加MyBatis配置
mybatis-plus:configuration:map-underscore-to-camel-case: truelog-impl: org.apache.ibatis.logging.stdout.StdOutImpl # SQL语句输出到控制台mapper-locations: classpath:mapper/*.xml
4 、 pojo
@Data
public class Emp {private Integer id;private String name;private Integer age;private String sex;private String email;private String address;private String phone;private double salary;}
5、Mapper 接口
@Mapper // Spring 会自动扫描这个接口,并创建一个实现类,并注入到 Spring 容器中
public interface EmpMapper {// 可以直接使用 #{对象属性名} 获取参数值void insertEmp(Emp emp);// 更新员工信息void updateEmp(@Param("e") Emp emp);void deleteEmpByID(@Param("id") Integer id);Emp selectEmpById(Integer id);// 实际业务需要分页,不然容易OOMList<Emp> selectAllEmps();List<Emp> selectComplexEmp();@MapKey ("id")Map<Integer,Emp> getAllMap();
}
6、mapper .xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="org.example.mapper.EmpMapper"><!-- #{name} 预编译SQL: 可以防止SQL注入--><!--useGeneratedKeys:自动生成主键keyProperty:指定主键的属性名,将生成的主键值 赋给id属性--><insert id="insertEmp" useGeneratedKeys="true" keyProperty="id">insert into emp (name, age, sex, email, address, phone, salary)values (#{name}, #{age}, #{sex}, #{email}, #{address}, #{phone}, #{salary})</insert><!-- 使用e--><update id="updateEmp">update empset name = #{e.name},age = #{e.age},sex = #{e.sex},email = #{e.email},address = #{e.address},phone = #{e.phone},salary = #{e.salary}WHERE id = #{id}e.</update><delete id="deleteEmpByID">delete from emp where id = #{id}</delete><select id="selectEmpById" resultType="org.example.pojo.Emp">select * from emp where id = #{id}</select><!-- 返回集合,要写集合中的元素类型--><select id="selectAllEmps" resultType="org.example.pojo.Emp">select * from emp</select><!-- 返回 map集合,resultType 写map中value的类型--><select id="getAllMap" resultType="java.util.Map">select * from emp</select><!-- 定义 ResultMap --><resultMap id="empResultMap" type="org.example.pojo.Emp"><!--property:Emp的属性名column:数据库的列名--><id property="id" column="id"/><result property="name" column="name"/><result property="age" column="age"/><result property="sex" column="sex"/><result property="email" column="email"/><result property="address" column="address"/><result property="phone" column="phone"/><result property="salary" column="salary"/></resultMap><!-- 复杂查询,使用 ResultMap 进行封装 --><select id="selectComplexEmp" resultMap="empResultMap">select id, name, age, sex, email, address, phone, salaryfrom empwhere age > 25 and salary > 5000</select></mapper>
数据库连接池
Mybatis 数据库连接池
<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.15</version>
</dependency>
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/userusername: rootpassword: 123456type: com.alibaba.druid.pool.DruidDataSource # 数据库 连接池druid:initial-size: 5 # 初始化大小max-active: 20 # 最大连接数min-idle: 5 # 最小连接数
关联查询
association 一对一
@Data
public class Order {private Long id;private String address;private BigDecimal amount;private Long customerId;private Customer customer;
}
@Data
public class Customer {private Long id;private String customerName;private String phone;
}
@Mapper
public interface OrderMapper {Order getOrderById(@Param("id") Integer id);
}
<?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.example.mapper.OrderMapper"><select id="getOrderById" resultMap="OrderRM">select o.*,c.id c_id,c.customer_name,c.phonefrom t_order oleft join t_customer c on o.customer_id = c.idwhere o.id = #{id}</select><!-- 自定义结果集--><resultMap id="OrderRM" type="org.example.pojo.Order"><id column="id" property="id"/><result column="address" property="address"/><result column="amount" property="amount"/><result column="customer_id" property="customerId"/><association property="customer" javaType="org.example.pojo.Customer"><id column="c_id" property="id"/><result column="customer_name" property="customerName"/><result column="phone" property="phone"/></association></resultMap></mapper>
collection 一对多
@Data
public class Customer {private Long id;private String customerName;private String phone;// 订单表List<Order> orders;
}
@Data
public class Order {private Long id;private String address;private BigDecimal amount;private Long customerId;private Customer customer;
}
<!-- CutomerRM --><resultMap id="CutomerRM" type="org.example.pojo.Customer"><id column="c_id" property="id"/><result column="customer_name" property="customerName"/><result column="phone" property="phone"/><!-- collection: 说明一对N的封装规则ofType: 集合中的元素类型--><collection property="orders" ofType="org.example.pojo.Order"><id column="id" property="id"/><result column="address" property="address"/><result column="amount" property="amount"/><result column="c_id" property="customerId"/></collection></resultMap><!-- select Customer --><select id="getCustomerByIdWithOrders" resultMap="CutomerRM">select c.id c_id,c.customer_name,c.phone,o.*from t_customer cleft join t_order o on c.id = o.customer_idwhere c.id = #{id}</select>
分步 查询
1、association 分步
@Data
public class Order {private Long id;private String address;private BigDecimal amount;private Long customerId;private Customer customer;
}
<!-- OrderCustomerStepRM --><resultMap id="OrderCustomerStepRM" type="org.example.pojo.Order"><id column="id" property="id"></id><result column="address" property="address"></result><result column="amount" property="amount"></result><result column="customer_id" property="customerId"></result><!-- customer属性关联一个对象,启动下一次查询,查询这个客户 --><association property="customer"select="getCustomerById"column="customer_id"></association></resultMap><select id="getCustomerById">select * from t_customer where id=#{id}</select>
2、 collection 分步
<!-- CutomerRM --><resultMap id="CutomerRM" type="org.example.pojo.Customer"><id column="c_id" property="id"/><result column="customer_name" property="customerName"/><result column="phone" property="phone"/><!-- collection: 说明一对N的封装规则ofType: 集合中的元素类型--><collection property="orders"ofType="org.example.pojo.Order"select="getOrderById"column="id"></collection></resultMap><!-- select Customer --><select id="getCustomerByIdWithOrders" resultMap="CutomerRM">select c.id c_id,c.customer_name,c.phone,o.*from t_customer cleft join t_order o on c.id = o.customer_idwhere c.id = #{id}</select>
3、开启延迟加载
mybatis-plus:configuration:map-underscore-to-camel-case: truelog-impl: org.apache.ibatis.logging.stdout.StdOutImpl # SQL语句输出到控制台lazy-loading-enabled: true # 懒加载mapper-locations: classpath:mapper/*.xml
分页查询
1、分页查询
Mapper
@Mapper
public interface EmpMapper {// 统计员工数量@Select("select count(*) from emp left join dept on emp.dept_id = dept.id")public Long count();/*** 分页 查询* 查询后 将d.name 取别名为 deptName* 因为 Emp类没有deptName属性,故在Emp类中添加deptName属性--->>进行封装** @return*/@Select("select e.*,d.name deptName from emp e left join dept d on e.dept_id = d.id" +"order by e.update_time desc limit #{start},#{size}")public List<Emp> list(@Param("start") Integer start, @Param("size") Integer size);}
Controller
@Slf4j
@RestController
@RequestMapping("/Dept") // 访问的公共前缀
public class DeptController {@GetMapping("/add")public Result add() {return Result.ok();}}
2、PageHelper
<dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper-spring-boot-starter</artifactId><version>1.4.5</version>
</dependency>
@Mapper
public interface EmpMapper {/*** PageHelper 实现分页,只需要指定从哪张表中查询,返回哪些字段** @return*/@Select("select e.*,d.name deptName from emp e left join dept d on e.dept_id = d.id" +"order by e.update_time desc")public List<Emp> list();}
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.List;@Service
public class DeptService {@Autowiredprivate EmpMapper empMapper;public PageInfo<Emp> selectList(Integer start, Integer size) {// 设置分页参数// 动态代理 实现分页PageHelper.startPage (start, size);// 执行查询// Page extends ArrayList<>Page<Emp> page = (Page<Emp>) empMapper.list ();// 封装分页结果return new PageInfo<Emp> (page.getTotal (), page.getResult ());}
}
Controller
@Slf4j
@RestController
@RequestMapping("/Dept") // 访问的公共前缀
public class DeptController {@Autowiredprivate DeptService deptService;@GetMapping("/add")public Result add(Integer start, Integer size) {PageInfo<Emp> pageInfo = deptService.selectList (start, size);long total = pageInfo.getTotal ();List<Emp> empList = pageInfo.getList ();return Result.ok (empList);}}
3、条件分页查询----->>> 优化版
请求参数太多的 优化,使用
对象 封装参数
动态SQL
1、 if 与 where 标签
@Mapper
public interface EmpMapper {/*** 查询* @param empQueryParam* @return*/public List<Emp> list(EmpQueryParam empQueryParam );}
@Autowiredprivate DeptService deptService;@GetMapping("/emps")public Result add(EmpQueryParam empQueryParam) {PageInfo<Emp> pageInfo = deptService.selectList (empQueryParam);long total = pageInfo.getTotal ();List<Emp> empList = pageInfo.getList ();return Result.ok (empList);}
Mapper 文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.example.tialsmanage.demos.web.EmpMapper"><select id="list" resultType="com.example.tialsmanage.demos.web.Demp">select emp.*,dept.name deptName from emp left join dept on dept.id=emp.dept_id-- #{name} 不能用在 ''中<where><if test="name !=null and name !='' ">and emp.name like concat('%',#{name},'%')</if><if test="gender !=null ">and emp.gender=#{gender}</if><if test="begin !=null and end !=null ">and emp.entry_date between #{begin} and #{end}</if></where></select>
</mapper>
2、set 标签
3、foreach 标签
void insertBatch(List<Emp> empList);
<insert id="insertBatch">insert into emp(name, gender, entry_date, dept_id) values<foreach collection="empList" item="emp" separator=",">(#{emp.name}, #{emp.gender}, #{emp.entry_date}, #{emp.deptId})</foreach>
</insert>
考虑使用 Mybatis-Plus
问题
插入几个数据之后 出现问题,无法回滚
此时考虑 事务
使用事务控制----> @Transactional
–>指定回滚类型(可见 事务管理内容)
缓存机制
1、事务级别 缓存 ( 一级缓存)
默认:事务期间 会开启缓存
2、二级缓存
** 注意:pojo 需要实现 序列化接口,不然会报错 ~~~**
<?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.example.mapper.OrderMapper"><!-- 所有的查询都会共享到二级缓存--><cache/> <!-- 开启 二级缓存--><select id="getCustomerById">select *from t_customerwhere id = #{id}</select></mapper>
@Data
public class Order implements Serializable {private static final long serialVersionUID = 1L;private Long id;private String address;private BigDecimal amount;private Long customerId;private Customer customer;
}
SpringBoot
介绍
简化 部署
<build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
使用Maven命令
mvn clean package
来 清理项目并进行打包。执行该命令后,会在项目的 target目录下生成一个可执行的Jar文件。
场景启动器
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId>
</dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
自动配置【面试、核心】
1、初步理解
2、自动导入 配置类
根据条件 @Conditional 注解 进行 导入
再 配置组件,将配置文件与属性类 绑定
基础
1、依赖
<dependencies><!-- web 依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>2.7.5</version></dependency><!-- MySQL JDBC 驱动 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.30</version><scope>runtime</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.24</version></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.9</version></dependency></dependencies>
2、 yaml 配置
# 配置mysql
spring:datasource:url: jdbc:mysql://localhost:3306/dw_test?useSSL=false&serverTimezone=UTCusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driver
3、启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class Main {public static void main(String[] args) {SpringApplication.run (Main.class, args);}
}
4、Controller 控制类
import org.example.Result;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;@CrossOrigin
@RestController // @RestController= @Controller + @ResponseBody
public class Hello {@PostMapping("/test")public Result resp(@RequestBody String name) {return Result.ok (name);}}
日志
1、lombok 依赖
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.24</version></dependency>
2、日志级别
# 日志级别
logging:level:root: debug
3、日志 输出到文件
# 日志级别
logging:file:# 指定输出的文件路径path: D://aaa.logname: boot.loglevel:root: debug
4、测试
@CrossOrigin
@RestController // @RestController= @Controller + @ResponseBody
@Slf4j
public class Hello {@PostMapping("/test")public Result resp(@RequestBody String name) {log.info("接收到请求,请求参数为:{}", name);return Result.ok (name);}}
总结:
多环境
@Configuration
public class WebConfig {@Bean@Profile("dev")public Result r1() {return Result.ok ("User dev");}@Bean@Profile("test")public Result r2() {return Result.ok ("User test");}
}
# 指定环境
spring:profiles:active: test
@CrossOrigin
@RestController // @RestController= @Controller + @ResponseBody
@Slf4j
public class Hello {@Autowiredprivate Result result;@GetMapping("/test")public Result resp() {log.info("接收到请求,请求参数为:{}", result);return Result.ok ("OK");}}
单元测试
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><version>2.7.5</version>
</dependency>
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;@SpringBootTest // Spring提供的测试环境
@Slf4j
public class HTest {@Testpublic void test() {System.out.println ("OK 666");}
}