当前位置: 首页 > article >正文

Spring +Spirng MVC+Mybatis +SpringBoot

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的id
    public 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 {

    @Autowired
    private 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 {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // 判断环境变量中的OS,如果是mac,则返回true
        if (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 {

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // 判断环境变量中的OS,如果是 windows,则返回true

        String property = context.getEnvironment ().getProperty ("os.name");
        if (property.contains ("Windows")) {
            return true;
        }
        return false;
    }
}

** @Conditional 使用 **


    @Conditional(value = WindowsCondition.class)
    @Bean
    public User bill() {
        return new User ("bill", 25);

    }

    @Conditional(value = MacCondition.class)

    @Bean
    public 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;


@Configuration

public class WebConfig implements WebMvcConfigurer {

    @Autowired // 按照类型注入
    private TokenInterceptor tokenInterceptor;

    @Autowired
    private User user;

    @Resource
    private MacCondition macCondition;

    @Bean
    public User u2() {
        return new User ("李四", 21);

    }

    @Primary //  按照类型注入时 优先级最高
    @Bean
    public User u1() {
        return new User ("张三", 18);

    }

    @Bean
    public MacCondition macCondition() {
        return new MacCondition ();

    }

}

@Profile ---- 多环境


    @Profile({"dev", "default"})
    public User u2() {
        return new User ("李四", 21);

    }

    @Profile("test")
    @Bean
    public 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");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println ("afterPropertiesSet");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println ("destroy");
    }

    @PostConstruct // 构造器之后
    public void postConstruct() {
        System.out.println ("PostConstruct");
    }

    @PreDestroy
    public 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;


@Configuration

public class WebConfig {

    private MacCondition macCondition;

    @Autowired
    public 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 {

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println ("【postProcessAfterInitialization】: " + beanName);
        return bean;
    }

    @Override
    public 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 {


    @Autowired
    User user;

    @Test
    void 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 应用场景

在这里插入图片描述

  1. 日志记录(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;
    }
}

优势:

日志代码集中管理,业务代码保持简洁。

修改日志策略时 无需改动业务方法

  1. 事务管理(Transaction Management)
    场景:
    在数据库操作中自动开启、提交或回滚事务,避免手动重复编写事务代码。
    实现逻辑:

声明式事务:通过 @Transactional 注解标记事务边界。

底层实现:Spring AOP代理类拦截注解方法,结合PlatformTransactionManager管理事务。

示例配置:


@Service
public class UserService {
    @Transactional
    public void createUser(User user) {
        // 数据库操作(如插入用户记录)
    }
}

优势:

事务控制与业务逻辑解耦。

支持传播行为、隔离级别等高级配置。

  1. 权限校验(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("权限不足");
        }
    }
}

优势:

权限逻辑 集中化 ---------->>>避免 每个方法 重复校验

通过注解灵活控制权限粒度。

  1. 性能监控(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;
    }
}

优势:

无侵入式监控,代码零耦合。

实时发现性能问题。

  1. 缓存管理(Caching)
    场景:
    自动缓存方法返回值,减少重复计算或数据库查询。
    实现逻辑:

通过@Cacheable注解声明缓存规则。

示例代码:


@Service
public class ProductService {
    @Cacheable(value = "products", key = "#id")
    public Product getProductById(Long id) {
        return productRepository.findById(id); // 仅第一次调用会执行此方法
    }
}

底层原理:
Spring AOP拦截方法调用,优先从缓存中读取数据,未命中时执行方法并缓存结果。

  1. 数据校验(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容器的启动流程主要包括以下几个步骤:

  1. 读取 配置文件

    • Spring容器启动时会读取配置文件(如XML、注解或Java配置类),解析其中的Bean定义信息。
  2. 实例化BeanFactory

    • 创建BeanFactory或其子类(如DefaultListableBeanFactory)作为IOC容器的核心组件。
  3. 注册Bean定义

    • 将解析后的Bean定义信息注册到BeanDefinitionRegistry中,为后续的Bean创建做准备。
  4. 初始化Bean工厂后处理器

    • 如果配置中有BeanFactoryPostProcessor类型的Bean,则在此阶段调用它们。这些处理器可以在Bean实例化之前修改Bean定义属性。
  5. 实例化所有单例Bean

    • 对于所有非懒加载的单例Bean,Spring容器会提前实例化并初始化它们。这包括:
      • 实例化:根据Bean定义创建Bean实例。
      • 属性填充:通过依赖注入(DI)设置Bean的属性值。
      • 初始化前处理:如果存在InstantiationAwareBeanPostProcessor,则在此阶段进行处理。
      • 初始化方法调用:调用Bean的初始化方法(如@PostConstruct注解的方法或InitializingBean接口的afterPropertiesSet方法)。
      • 初始化后处理:如果存在BeanPostProcessor,则在此阶段调用postProcessAfterInitialization方法。
  6. 完成容器刷新

    • 容器完成所有必要的初始化工作后**,发布容器事件通知所有监听器。**
  7. 使用容器

    • 应用程序可以 通过容器获取Bean实例,并使用这些Bean执行业务逻辑。
  8. 销毁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 + @ResponseBody

public 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 name
    public 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 + @ResponseBody

public 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: 10MB
      max-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");

        // 以下代码永远不改
        // 文件太大会oom
        InputStreamResource 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 id
    public String resp(@PathVariable("id") Integer id) {

        return "  Hello World "+ id;
    }


}

在这里插入图片描述

2、三层架构

在这里插入图片描述

在这里插入图片描述

解耦

在这里插入图片描述

3、统一返回对象


@Data
@AllArgsConstructor
@NoArgsConstructor

public 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 {

    @Override
    public 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);
    }

    @Override
    public void init(FilterConfig filterConfig) {}

    @Override
    public 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 {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 在请求处理之前进行拦截逻辑

        response.getWriter ().write ("No Permission!");
        return false; // 返回true表示 继续处理请求,返回false则中断请求处理
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        // 在请求处理之后 进行拦截逻辑
    }

    @Override
    public 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 {

    @Autowired
    private MyInterceptor myInterceptor;


    @Override
    public 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
@AllArgsConstructor

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 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 返回校验结果
     */
    @Override
    public 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: test
  datasource:
    url: jdbc:mysql://localhost:3306/db?useSSL=false&serverTimezone=UTC
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver

# 日志
logging:
  level:
    # 设置mapper的日志级别为debug
    org.example.mapper: debug
# 添加MyBatis配置
mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true
    log-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);

    // 实际业务需要分页,不然容易OOM
    List<Emp> selectAllEmps();

    List<Emp> selectComplexEmp();


    @MapKey ("id")
    Map<Integer,Emp> getAllMap();
}

6、mapper .xml


<?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.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 emp
        set 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, salary
        from emp
        where 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.Driver
    url: jdbc:mysql://localhost:3306/user
    username: root
    password: 123456
    
    type: 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.phone
        from t_order o
        left join t_customer c on o.customer_id = c.id
        where 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 c
                 left join t_order o on c.id = o.customer_id
        where 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 c
                 left join t_order o on c.id = o.customer_id
        where c.id = #{id}
    </select>

在这里插入图片描述

3、开启延迟加载


mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true
    log-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 {

    @Autowired
    private 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 {

    @Autowired
    private 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 );

}


    @Autowired
    private 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 mapper
        PUBLIC "-//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_customer
        where 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=UTC
    username: root
    password: 123456
    driver-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.log
    name: boot.log
  level:
    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 {


    @Autowired
    private 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 {
   @Test
   public void  test() {
      System.out.println ("OK 666");
   }
}




http://www.kler.cn/a/570989.html

相关文章:

  • 一个易用的.Net测试模拟库
  • Hive面试:行列转换
  • C# Unity 唐老狮 No.3 模拟面试题
  • Xsens动作捕捉+AI训练家用机器人:迈向智能生活的新篇章
  • linux下自旋锁(spin_lock)
  • Flutter状态管理框架GetX最新版详解与实践指南
  • 如何进行虚拟机IP配置
  • 下载pyenv
  • 栈和队列的模拟实现
  • 蓝桥杯 2022 Java 研究生省赛 3 题 质因数个数
  • 51单片机中reg52.h与regx52.h在进行位操作时的不同
  • C++:面向对象编程(封装、继承和多态)
  • 深入理解 `udev`:Linux 设备管理的核心机制
  • Leetcode 49: 字母异位词分组
  • Java自动拆箱装箱/实例化顺序/缓存使用/原理/实例
  • 基于SpringBoot的校园二手交易平台(源码+论文+部署教程)
  • 通用查询类接口数据更新的另类实现
  • python爬虫报错信息解决方法
  • 02原理篇(D2_SpringBoot 自动装配原理)
  • 设计模式:记录所有设计模式方便后续复习