springboot

springboot特性

  1. 起步依赖,解决 Spring 程序配置繁琐的问题
    1. 本质上就是一个Maven坐标,整合了完成一个功能需要的所有坐标
  2. 自动配置,解决 Spring 程序依赖设置繁琐的问题
    1. 遵循约定大约配置的原则,在boot程序启动后,一些bean对象会自动注入到ioc容器,不需要手动声明,简化开发
  3. 其他特性
    1. 内嵌的Tomcat、Jetty(无需部署WAR文件)使用 SpringBoot 内置的服务器
    2. 外部化配置
    3. 不需要XML配置(properties/yml)

yml配置信息的获取()

  1. @Value注解(可以写在方法的形参里)
yml文件:
    emil:
        user: 123@qq.com

类文件
@Value("${email.user}")
private String emailUser;
  1. @ConfigurationProperties注解
@Component
@ConfigurationProperties(prefix = "email")//指定前缀
public class EmailProperties {

    private String user;//实体类的成员变量名与配置文件中的键名保持一致

    // getter and setter
    public String getUser() {
        return user;
    }

    public void setUser(String user) {
        this.user = user;
    }
}

需要加入以下依赖

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-configuration-processor</artifactId>
  <optional>true</optional>
</dependency>

Bean注册

  1. 要注册的Bean是自定义的,使用以下注解image.png
  2. 要注册的Bean是第三方的,使用以下注解
    1. @Bean注解,会将方法的返回结果注入到IOC容器,生成的Bean对象的默认名为方法的名,当然可以指定@Bean(name =“myCustomName”)
      如果方法的内部需要使用到ioc容器中已经存在的bean对象,那么只需要在方法上声明即可,spring会自动的注入
      1. 启动类的主方法的run方法会返回IOC容器
        image.png
      2. 不建议在启动类注册bean;应该在统一的配置类中进行注册
        image.png
      3. image.png
    2. @Import注解 (前面的config配置类依然是在Applicaiton的扫描范围下)
      1. 如果不在扫描范围,但是依然想把配置类声明的Bean对象加到IOC容器
@SpringBootApplication
@Import({CommonConfig.class})
public class Application {
}
  2. 前面的Import中的{}填写多个配置类文件的话,{}内容太多不美观{ConfigClass.class,ConfigClass.class,ConfigClass.class,ConfigClass.class}<br />可以导入一个实现了ImportSelector接口的类。这个类可以返回一个包含需要注册的Bean的全类名的字符串数组:
public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{CommonConfig.class.getName(), ThirdPartyClass2.class.getName()};
        //也可以直接写“类名”
    }
}


在启动类Import进来
@SpringBootApplication
@Import(MyImportSelector.class)
public class Application {
}

3.注册条件:
image.png

自动配置原理image.png
springboot2.7版本之前读取的配置文件是spring.factories
2.7-3.0是spring.factories和后缀名为import的文件
3.0之后就只有.import的这个文件了
image.png
补充一下:AutoConfigurationImportSelector类实现DeferredImportSelector接口,DeferredImportSelector接口包括了ImportSelector接口
在Spring Boot项目中引入一个依赖时,这个依赖的jar包中可能包含了一些带有@Configuration和@EnableAutoConfiguration注解的配置类。这些配置类定义了如何配置和初始化这个依赖,以便它可以在你的应用中使用。
当Spring Boot应用启动时,它会通过@EnableAutoConfiguration注解来启动自动配置过程。这个过程包括扫描类路径下所有的配置类,并根据一些条件(比如类路径下是否存在某个类,配置文件中是否定义了某个属性等)来决定是否启用这些配置类。

自定义starter以下是创建自定义MyBatis Starter的完整步骤和代码示例:

  1. 创建自动配置模块:首先,你需要创建一个名为mybatis-spring-boot-autoconfigure的模块,这个模块将提供自动配置功能。在这个模块中,你需要定义一个配置文件META-INF/spring.factories,并在其中列出所有的自动配置类。
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.mybatis.autoconfigure.MybatisAutoConfiguration
  1. 定义配置属性类:在自动配置模块中,你需要定义一个配置属性类,例如MybatisProperties。这个类需要使用@ConfigurationProperties注解,并提供一些属性,如数据库URL、用户名、密码等。这些属性可以在application.propertiesapplication.yml文件中进行配置。
@ConfigurationProperties(prefix = "mybatis")
public class MybatisProperties {
    private String url;
    private String username;
    private String password;
    // getters and setters...
}
  1. 定义自动配置类:在自动配置模块中,你还需要定义一个自动配置类,例如MybatisAutoConfiguration。这个类需要使用@Configuration@EnableConfigurationProperties(MybatisProperties.class)注解。在这个类中,你可以定义一些@Bean方法,用于创建和配置MyBatis的SqlSessionFactorySqlSessionTemplate等组件。
@Configuration
@EnableConfigurationProperties(MybatisProperties.class)
public class MybatisAutoConfiguration {
    @Autowired
    private MybatisProperties properties;

    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        // other configurations...
        return factoryBean.getObject();
    }

    @Bean
    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}
  1. 创建Starter模块:然后,你需要创建一个名为mybatis-spring-boot-starter的模块。在这个模块中,你需要引入刚才创建的自动配置模块。在mybatis-spring-boot-starter模块的pom.xml文件中,你需要添加对mybatis-spring-boot-autoconfigure模块的依赖。
<dependencies>
    <dependency>
        <groupId>com.example</groupId>
        <artifactId>mybatis-spring-boot-autoconfigure</artifactId>
        <version>1.0.0</version>
    </dependency>
    <!-- other dependencies... -->
</dependencies>

完成以上步骤后,你就可以在其他Spring Boot项目中引入你自定义的mybatis-spring-boot-starter,然后在application.propertiesapplication.yml文件中配置MyBatis的属性,Spring Boot就会自动配置MyBatis。

Spring Validation(参数校验,包括实体类的参数校验,分组校验,包括自定义注解来自定义参数校验)Spring 提供的一个参数校验框架,使用预定义的注解完成参数校验

  1. 引入Spring Validation 起步依赖
    image.png
  2. 在参数前面添加@Pattern注解
    image.png
  3. 在Controller类上添加@Validated注解

实体类的参数校验

  1. 实体类的成员变量上添加注解
    1. @NotNull
    2. @NotEmpty
    3. @Email
  2. 接口方法的实体参数上添加@Validated注解

image.png
@Pattern 是 Java 的 Bean Validation(JSR 303)规范中的一个注解,用于验证一个字符串是否符合指定的正则表达式。

在Spring Boot中,对参数进行分组校验主要涉及以下几个步骤:

  1. 定义分组接口:在实体类中定义不同的分组接口,用于区分不同场景下的校验规则。(校验项属于默认分组,分组之间可以继承)
public class User {
  // 定义分组接口
  public interface BasicInfo {}
  public interface UpdateInfo extends Default {}
}
  1. 设置校验规则并指定分组:在实体类的字段上添加校验注解,并通过groups属性指定该校验规则属于哪个分组。
public class User {
  @NotNull(groups = {BasicInfo.class, UpdateInfo.class})
  private String username;
  
  @NotNull(groups = UpdateInfo.class)
  private String email;

  @NotNull//属于Default分组
  private String nickname;
}
  1. 校验时指定分组:在接口方法参数上添加@Validated注解,并通过value属性指定本次校验需要检查的分组。
@RestController
@RequestMapping("/user")
public class UserController {
  
  @PostMapping("/register")
  public Result register(@RequestBody @Validated(User.BasicInfo.class) User user) {
    // 使用BasicInfo分组进行校验
  }
  
  @PutMapping("/update")
  public Result update(@RequestBody @Validated(User.UpdateInfo.class) User user) {
    // 使用UpdateInfo分组进行校验
  }
}
  1. **自定义注解 ****@State**:这个注解将用于标记需要校验的状态字段。

  2. **自定义校验类 ****StateValidation**:这个类将实现 ConstraintValidator 接口,用于校验状态是否符合规定。

  3. 使用自定义注解进行校验:在需要校验状态的地方使用 @State 注解。
    下面是具体的实现步骤:

  4. 自定义注解 @State

import javax.validation.Constraint;
import java.lang.annotation.*;

@Constraint(validatedBy = StateValidation.class)
@Target({ ElementType.FIELD })//元注解,用于指定被它标注的注解可以应用的范围或目标。换句话说,它定义了其他注解能够标注的 Java 构件类型。
@Retention(RetentionPolicy.RUNTIME)
public @interface State {
    //提供校验失败后的提示信息
    String message() default "Invalid state";
    //指定分组
    Class<?>[] groups() default {};
    //负载  获取到state注解的附加信息
    Class<? extends Payload>[] payload() default {};
}
  1. 自定义校验类 StateValidation
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class StateValidation implements ConstraintValidator<State, String> {//String指的是校验的数据的类型
    @Override
    public void initialize(State constraintAnnotation) {
    }
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        // 这里添加校验逻辑,例如校验状态是否是预定义的几个有效状态
        String[] validStates = {"Active", "Inactive", "Suspended"};
        for (String state : validStates) {
            if (state.equals(value)) {
                return true;
            }
        }
        return false;
    }
}
  1. 使用自定义注解进行校验
    在实体类中,您可以在需要校验的状态字段上使用 @State 注解。
import javax.persistence.Entity;
import javax.validation.constraints.NotNull;
@Entity
public class User {
    @NotNull
    @State
    private String state;
    // Getters and setters
}

这样,在运行时,Spring 框架会自动根据 @State 注解调用 StateValidation 类来进行校验。如果状态不符合预定义的有效状态列表,将抛出校验异常。

全局异常处理器在Spring Boot中,全局异常处理器可以用来捕获和处理所有未被处理的异常。以下是实现全局异常处理器的步骤:

  1. 创建一个全局异常处理器类,该类应该使用@ControllerAdvice注解。
  2. @ControllerAdvice注解可以把异常处理器应用到所有控制器,而不是单个控制器。
  3. 在处理器类中,定义处理不同异常的方法。使用@ExceptionHandler注解指定处理的异常类型。
  4. 对异常按阶段进行分类,大体可以分成:进入Controller前的异常和Service层异常。
  5. 目标就是消灭95%以上的try catch代码块,并以优雅的Assert(断言)方式来校验业务的异常情况,只关注业务逻辑,而不用花费大量精力写冗余的try catch代码块。
@ControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 处理空指针异常
     */
    @ExceptionHandler(NullPointerException.class)
    public String handleNullPointerException(Exception e) {
        return "null_pointer_error";
    }

    /**
     * 处理算术异常
     */
    @ExceptionHandler(ArithmeticException.class)
    public String handleArithmeticException(Exception e) {
        return "arithmetic_error";
    }

    /**
     * 处理其他异常
     */
    @ExceptionHandler(Exception.class)
    public String handleOtherException(Exception e) {
        return "other_error";
    }
}

登录认证(jwt(json web token)令牌)作用

  1. 承载业务数据, 减少后续请求查询数据库的次数
  2. 防篡改, 保证信息的合法性和有效性

jwt(json web token)
image.png
定义了一种简洁的,自包含的格式,用于通信双方以ison数据格式安全的传输信息。

  1. 组成:
    第一部分:header(头),记录令牌类型,签名算法等。例如:{“alg:'hs256”,“type”:“JWT”}
  2. 第二部分:payload(有效载荷),携带一些自定义信息,默认信息等。例如:{"id=“1”,“username”=“tom”}
  3. 第三部分:signature(签名),防止token被篡改,确保安全性。将header,payload,并加入指定秘钥,通过指定签名算法计算而来。

image.png
base64是一种编码方式,不是加密

//JWT令牌生成
@Test
void genToken() {
    Map<String, Object> user = new HashMap<>();
    user.put(“id”,1);
    user.put(“username”, “张三”);

    String token = JWT.create()
            .withClaim(“user”, user)
            .withExpiresAt(new Date(System.currentTimeMillis() + 1000*60*60))//有效时间
            .sign(Algorithm.HMAC256(“秘钥"));
    System.out.println(token);
}

//JWT令牌验证
@Test
void parseToken() {
    String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…….";
    JWTVerifierjwtVerifier = JWT.require(Algorithm.HMAC256("密钥")).build();
    DecodedJWTdecodedJWT = jwtVerifier.verify(token);
    Map<String, Claim> claims = decodedJWT.getClaims();
    System.out.println(claims.get("user"));
}

使用redis实现令牌主动失效机制在SpringBoot项目中集成Redis,你可以按照以下步骤进行:

  1. 添加依赖:在你的pom.xml文件中添加Redis的SDK¹²³。
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  1. 配置文件:在application.properties或者application.yml文件中,添加Redis的配置信息。
spring.redis.host=127.0.0.1
spring.redis.port=6379
  1. 创建配置类:创建一个配置类,用于读取配置文件中的信息,并创建RedisTemplate Bean。
@Configuration  // 标记这个类为配置类,Spring容器会根据它来生成Bean定义和服务请求
public class RedisConfig {

    @Bean  // 标记方法返回的对象作为Bean注入到Spring容器中
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        // 创建RedisTemplate实例,并设置泛型类型<String, Object>分别表示键和值的类型
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        
        // 设置连接工厂,用于创建与Redis的连接
        template.setConnectionFactory(connectionFactory);

        // 设置键序列化器为StringRedisSerializer,这意味着键在Redis中会以字符串形式存储
        template.setKeySerializer(new StringRedisSerializer());

        // 设置值序列化器为GenericJackson2JsonRedisSerializer,
        // 这意味着值在Redis中会以JSON格式存储,方便以对象的形式进行存储和读取
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());

        // 返回配置好的RedisTemplate实例
        return template;
    }
}

Spring Boot的自动配置机制会自动创建一个RedisTemplate Bean,并添加到IOC容器中。这个自动创建的RedisTemplate使用的是JDK的序列化机制,键(key)和值(value)都是通过JDK的序列化方式来序列化的。
然而,这种序列化方式可能并不是我们想要的。例如,我们可能希望键(key)是字符串类型,而值(value)是JSON格式。这就是为什么我们需要创建一个配置类,自定义RedisTemplate的序列化方式。
如果你的项目中不需要自定义序列化方式,那么你可以直接使用Spring Boot自动配置的RedisTemplate。你可以通过@Autowired注解来注入这个Bean,然后在你的代码中使用它。

  1. 使用RedisTemplate:在服务类或控制器中注入RedisTemplate,并使用它来进行Redis操作。
@Autowired
private RedisTemplate<String, Object> redisTemplate;

// 设置键值对,并指定过期时间(例如:10秒)
redisTemplate.opsForValue().set("key", "value", 10, TimeUnit.SECONDS);

// 获取值
String value = (String) redisTemplate.opsForValue().get("key");

// 删除键值对
redisTemplate.delete("key");
  1. 登录成功后,给浏览器响应令牌的同时,把该令牌存储到redis中
  2. LoginInterceptor拦截器中,需要验证浏览器携带的令牌,并同时需要获取到redis中存储的与之相同的令牌(用equal()方法)
  3. 当用户修改密码成功后,删除redis中存储的旧令牌

拦截器(先过滤器后拦截器)在Spring Boot中,你可以创建一个实现了HandlerInterceptor接口的拦截器。以下是创建拦截器的步骤和代码:

  1. 创建拦截器类:首先,创建一个新的Java类,实现HandlerInterceptor接口。在这个类中,你可以重写preHandlepostHandleafterCompletion方法,分别对应于处理器执行前,返回模型视图之前,和处理器执行完成后这三个阶段。
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 在这里编写处理器执行前的逻辑
        return true;//放行
    }

    @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 {
        // 在这里编写处理器执行完成后的逻辑
    }
}
  1. 注册拦截器:然后,你需要在一个配置类中注册你的拦截器。这个配置类需要实现WebMvcConfigurer接口,并重写addInterceptors方法。
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 {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyInterceptor1())
        .addPathPatterns("/**") // 对所有路径进行拦截
        .excludePathPatterns("/user/login"); // 对"/user/login"路径不进行拦截
        registry.addInterceptor(new MyInterceptor2());
        // 你可以继续添加更多的拦截器
    }
}

在这个例子中,MyInterceptor就是你刚刚创建的拦截器类。你可以在addInterceptor方法中添加多个拦截器,它们会按照添加的顺序执行。
以上就是在Spring Boot中创建和注册拦截器的基本步骤和代码。你可以根据你的需求修改这些代码,以实现你想要的功能。

controller层获取请求头中的数据

  1. 通过HttpServletRequest对象获取请求头(拦截器中可以使用)
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;

@RestController
public class MyController {

    @GetMapping("/my-endpoint")
    public String myEndpoint(HttpServletRequest request) {
        // 使用HttpServletRequest对象的getHeader方法获取请求头
        String token = request.getHeader("Authorization");
        // 在这里,你可以使用这个令牌来进行身份验证或者其他操作
        return "获取到的JWT令牌是:" + token;
    }
}
  1. 通过@RequestHeader注解获取请求头
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MyController {

    @GetMapping("/my-endpoint")
    public String myEndpoint(@RequestHeader(name="Authorization") String token) {
        // 使用@RequestHeader注解获取请求头
        // 在这里,你可以使用这个令牌来进行身份验证或者其他操作
        return "获取到的JWT令牌是:" + token;
    }
}

这两种方法各有优点和缺点:

  1. 通过HttpServletRequest对象获取请求头
  • 优点:这种方法更加通用,因为HttpServletRequest是Servlet API的一部分,可以在任何使用Servlet API的地方使用,不仅限于Spring MVC。
  • 缺点:这种方法需要你手动处理请求头的名称,如果你的代码中多次使用到同一个请求头,可能会导致代码重复。
  1. 通过@RequestHeader注解获取请求头
  • 优点:这种方法更加简洁,可以直接在方法参数中获取请求头,无需手动调用API。此外,如果请求头不存在,Spring MVC会抛出一个异常,这可以帮助你更好地处理错误情况。
  • 缺点:这种方法只能在Spring MVC的控制器方法中使用,不能在其他地方使用。

ThreadLocal```java
import java.util.HashMap;
import java.util.Map;

/**

  • ThreadLocal 工具类
    */
    @SuppressWarnings(“all”)
    public class ThreadLocalUtil {
    //提供ThreadLocal对象,
    private static final ThreadLocal THREAD_LOCAL = new ThreadLocal();

    //根据键获取值
    public static T get(){
    return (T) THREAD_LOCAL.get();
    }

    //存储键值对
    public static void set(Object value){
    THREAD_LOCAL.set(value);
    }

    //清除ThreadLocal 防止内存泄漏
    public static void remove(){
    THREAD_LOCAL.remove();
    }
    }

我们可以在拦截器中将解析出来的 map 存入 `ThreadLocal`,然后在 controller 中获取这个 map。以下是一个简单的示例:
```java
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;

public class TokenInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 获取并解析 JWT 令牌
        String token = request.getHeader("Authorization");
        Map<String, Object> claims = parseJwt(token); // 假设你已经实现了 parseJwt 方法

        // 将解析出来的 map 存入 ThreadLocal
        ThreadLocalUtil.set(claims);

        return true;
    }
}

然后在你的 controller 中,你可以这样获取这个 map:

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MyController {
    @GetMapping("/my-endpoint")
    public String myEndpoint() {
        Map<String, Object> claims = ThreadLocalUtil.get();

        // 使用 claims...

        return "Success";
    }
}

最后,别忘了在请求处理完毕后清除 ThreadLocal 中的数据,以防止内存泄漏。你可以在 TokenInterceptor 中的 afterCompletion 方法中做这个操作:

import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;

public class TokenInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 获取并解析 JWT 令牌
        String token = request.getHeader("Authorization");
        Map<String, Object> claims = parseJwt(token); // 假设你已经实现了 parseJwt 方法

        // 将解析出来的 map 存入 ThreadLocal
        ThreadLocalUtil.set(claims);

        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        ThreadLocalUtil.remove();//释放线程
    }
}

这样,你就可以在 controller 中获取到拦截器中解析出来的 map 了。

文件上传```java
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
@RestController
public class FileUploadController {
@PostMapping(“/upload”)
public String handleFileUpload(@RequestParam(“file”) MultipartFile file) {
String filePath = “c://user/files/”;
try {
// 获取原始文件名
String originalFilename = file.getOriginalFilename();
// 获取文件扩展名
String fileExtension = originalFilename.substring(originalFilename.lastIndexOf(“.”));
// 生成时间戳
String timestamp = new SimpleDateFormat(“yyyyMMddHHmmss”).format(new Date());
// 生成随机数以避免同一秒内上传的文件名冲突
Random random = new Random();
int randomNumber = random.nextInt(1000);
// 新的文件名
String newFilename = timestamp + “_” + randomNumber + fileExtension;
File dest = new File(filePath + newFilename);
file.transferTo(dest);
return "File uploaded successfully: " + dest.getAbsolutePath();
} catch (IOException e) {
e.printStackTrace();
return "Failed to upload file: " + e.getMessage();
}
}
}

在这个代码中,我们首先获取原始文件的扩展名,然后创建一个包含当前时间戳和随机数的字符串作为新文件名。这样可以确保每个文件名都是唯一的,即使多个文件有相同的原始文件名。

布置阿里云OSS在SpringBoot项目中集成阿里云OSS,你可以按照以下步骤进行:

1. **添加依赖**:在你的`pom.xml`文件中添加阿里云OSS的SDK。
```xml
<dependency>
    <groupId>com.aliyun.oss</groupId>
    <artifactId>aliyun-sdk-oss</artifactId>
    <version>3.15.0</version>
</dependency>
  1. 配置文件:在application.properties或者application.yml文件中,添加阿里云OSS的配置信息。
# 阿里云OSS配置
aliyun:
  oss:
    end-point: oss-cn-hangzhou.aliyuncs.com
    access-key-id: LTAI5t9w*****
    access-key-secret: OXK24t76*****
    bucket-name: oss-test-img
  1. 创建配置类:创建一个配置类,用于读取配置文件中的信息。
@ConfigurationProperties(prefix = "aliyun.oss")//属性绑定,将配置文件(application)的属性绑定到对应字段
@Configuration
@Data
public class OSSConfig {
    private String endPoint;
    private String accessKeyId;
    private String accessKeySecret;
    private String bucketName;
}
  1. 编写服务类:编写一个服务类,用于处理文件上传的业务。
@Service
@Slf4j
public class FileServiceImpl implements FileService {
    @Autowired
    private OSSConfig ossConfig;

    @Override
    public String upload(MultipartFile file) {
        //获取相关配置
        String bucketName = ossConfig.getBucketName();
        String endPoint = ossConfig.getEndPoint();
        String accessKeyId = ossConfig.getAccessKeyId();
        String accessKeySecret = ossConfig.getAccessKeySecret();

        //创建OSS对象
        OSS ossClient = new OSSClientBuilder().build(endPoint, accessKeyId, accessKeySecret);

        //其他上传逻辑...
    }
}

接口层代码

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
@RestController
public class FileController {
    @Autowired
    private FileService fileService;
    @PostMapping("/upload")
    public String uploadFile(@RequestParam("file") MultipartFile file) {
        try {
            // 调用服务层的上传方法
            String fileUrl = fileService.upload(file);
            return "File uploaded successfully: " + fileUrl;
        } catch (Exception e) {
            e.printStackTrace();
            return "Failed to upload file: " + e.getMessage();
        }
    }
}

springboot属性配置方式

  1. 命令行参数(最高优先级):
    • 例子:java -jar myapp.jar --server.port=8081
    • 这会覆盖其他配置源中的server.port属性。
  2. 操作系统环境变量
    • 例子:export SERVER_PORT=8081
    • 设置环境变量SERVER_PORT来配置服务器端口。
  3. Jar包所在目录下的application.yml文件
    • 例子:在JAR文件同一目录下的application.yml中设置server.port: 8081
    • 这个配置会覆盖项目内部application.yml中的相同属性。
  4. 项目中resources目录下的application.yml(最低优先级):
    • 例子:在src/main/resources/application.yml中设置server.port: 8080
    • 这是默认的配置文件,其配置值会被其他更高优先级的配置所覆盖。
      总结:命令行参数具有最高优先级,会覆盖其他所有配置。其次是操作系统环境变量,然后是外部配置文件,最后是项目内部资源目录下的配置文件。

springboot多环境开发SpringBoot提供的Profiles可以用来隔离应用程序配置的各个部分,并在特定环境下指定部分配置生效
如果多个环境有相同的配置部分,您可以将这些通用配置放在默认配置部分,这样可以避免重复并简化配置文件。在Spring Boot中,默认配置会应用到所有环境,除非被特定环境的配置所覆盖。

# 默认配置
server:
  port: 8080
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/my_database
    username: my_user
    password: my_password
# 使用'---'来分隔不同环境的配置
---
# 开发环境配置
spring:
  profiles: dev
server:
  port: 8081
---
# 测试环境配置
spring:
  profiles: test
server:
  port: 8082
---
# 生产环境配置
spring:
  profiles: prod
server:
  port: 8083
# 指定活动配置文件
spring:
  profiles:
    active: dev  # 这里指定开发环境配置生效

在这个例子中,spring.datasource配置是所有环境共有的,因此它被放在默认配置部分。每个特定环境的配置块只覆盖了端口号。这样,您可以保持配置的DRY(Don’t Repeat Yourself)原则,同时也使得配置文件更加清晰和维护。
也可以将不同环境的配置分别写在不同的文件中。Spring Boot支持将每个环境的配置放在单独的文件中,这样可以进一步组织和简化配置管理。通常的命名约定是application-{profile}.ymlapplication-{profile}.properties
例如,您可以有以下几个配置文件:

  • application.yml:默认配置,适用于所有环境。
  • application-dev.yml:专门为开发环境定制的配置。
  • application-test.yml:专门为测试环境定制的配置。
  • application-prod.yml:专门为生产环境定制的配置。
    application.yml中,您不需要分隔不同环境的配置,而是将共用的配置放在这里。特定环境的配置则放在对应的文件中。例如:
    application.yml:
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/my_database
    username: my_user
    password: my_password

然后,在默认的application.yml或通过命令行参数、环境变量等方式来指定活动配置文件。例如,在application.yml中指定:

spring:
  profiles:
    active: dev  # 激活开发环境配置

或者,通过命令行参数激活:

java -jar myapp.jar --spring.profiles.active=prod

这种方式使得每个环境的配置更加模块化,便于管理和维护。

环境文件按功能再分组
image.png
按照配置的类别,把配置信息配置到不同的配置文件中
application-分类名.yml
在application.yml中定义分组
spring.profiles.group
在application.yml中激活分组
spring.profiles.active(不再是指定某个文件了,而是组的名称)

https://blog.csdn.net/qq_44378854/article/details/135873981

切换web服务器引入spring-boot-starter-web依赖后,也会引入spring-boot-starter-tomcat依赖;使用标签来排除tomcat依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <groupId>org.springframework.boot</groupId>
        </exclusion>
    </exclusions>
</dependency>

引入jetty服务器,成功切换服务器;

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

Mybatis开启驼峰命名(包括实体类的字段和数据库字段对应的方法)在MyBatis中,你可以通过设置mapUnderscoreToCamelCase属性为true来开启驼峰命名规则¹。这样,MyBatis会自动将数据库表中的列名或表名转换为相应的驼峰命名形式。以下是几种开启驼峰命名的方式:

  1. 在MyBatis的配置文件中设置¹:
<configuration>
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true" />
    </settings>
</configuration>
  1. 在Spring Boot的application配置文件中设置¹²:
mybatis.configuration.mapUnderscoreToCamelCase=true

或者

mybatis.configuration.map-underscore-to-camel-case=true
  1. 在Spring Boot中使用自定义配置类的方式配置¹:
@Configuration
public class MyBatisConfig {
    @Bean
    public ConfigurationCustomizer configurationCustomizer() {
        return new ConfigurationCustomizer() {
            @Override
            public void customize(org.apache.ibatis.session.Configuration configuration) {
                configuration.setMapUnderscoreToCamelCase(true);
            }
        };
    }
}

如果你不想使用这个配置,或者你的命名规则比较复杂,不适合使用这个配置,你可以在实体类中使用@Column注解来指定属性对应的数据库表列名。以下是一个例子:

import javax.persistence.Column;

public class User {
    @Column(name = "user_name")
    private String userName;
    // 其他属性和方法
}

在这个例子中,userName属性对应的数据库表列名是user_name。这样,即使你的Java属性名和数据库列名不一致,你也可以通过@Column注解来确保它们能够正确地映射。

Mybatis(PageHelper帮助实现分页查询)MyBatis 提供了一种特殊的分页插件,用于优化分页查询。这个插件通常被称为 MyBatis PageHelper。它通过拦截器(Interceptor)的方式,在执行SQL查询时自动添加分页语句,从而简化了分页查询的代码实现。
1. 添加依赖(pom.xml)

<dependencies>
    <!-- PageHelper -->
    <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper-spring-boot-starter</artifactId>
        <version>1.3.0</version>
    </dependency>
    <!-- 其他依赖... -->
</dependencies>

2. 配置插件
  • 方式一:使用Spring Boot自动配置(application.properties)
pagehelper.helper-dialect=mysql
pagehelper.reasonable=true
pagehelper.support-methods-arguments=true
pagehelper.params=count=countSql
  • 方式二:使用自定义的**MyBatisConfig**类进行配置(MyBatisConfig.java)
@Configuration
public class MyBatisConfig {
    @Bean
    public PageHelper pageHelper() {
        PageHelper pageHelper = new PageHelper();
        Properties p = new Properties();
        p.setProperty("offsetAsPageNum", "true");
        p.setProperty("rowBoundsWithCount", "true");
        p.setProperty("reasonable", "true");
        pageHelper.setProperties(p);
        return pageHelper;
    }
}

3. Mapper 接口
@Mapper
public interface ArticleMapper {
    List<Article> findAllArticles();
}
//在服务层,您将使用PageHelper的startPage方法来
//指定当前页和每页的记录数。
//这将告诉PageHelper您想要执行分页查询。
//然后,您只需像平常一样调用Mapper接口中的方法。
//PageHelper会拦截这个查询,并根据您提供的分页参数修改SQL语句。

4. 服务层
@Service
public class ArticleService {
    @Autowired
    private ArticleMapper articleMapper;
    public List<Article> findArticles(int page, int size) {
        PageHelper.startPage(page, size);
        return articleMapper.findAllArticles();
    }
}

5. 控制器层
@RestController
@RequestMapping("/articles")
public class ArticleController {
    @Autowired
    private ArticleService articleService;
    @GetMapping
    public List<Article> getArticles(
        @RequestParam(defaultValue = "0") int page,
        @RequestParam(defaultValue = "10") int size
    ) {
        return articleService.findArticles(page, size);
    }
}

依赖注入的方式

  1. @autowired注解
  2. @resource注解
  3. 构造器注入

在Spring框架中,依赖注入是实现控制反转(IoC)的主要方式,它允许Spring容器自动装配Bean的依赖关系。有几种不同的方法可以实现依赖注入,包括使用@Autowired注解、@Resource注解以及构造器注入。每种方法都有其优缺点:

  1. @Autowired注解
    • 优点:这是Spring框架提供的原生注解,使用简单、直观。
    • 缺点:过度使用@Autowired可能导致代码的耦合度增加,使得单元测试变得困难。此外,IDE可能会针对字段注入发出警告,因为这种方式不易于理解和管理依赖关系。
  2. @Resource注解
    • 优点@Resource是Java EE的标准,因此可以在其他符合JSR-250标准的框架中使用。它提供了更多的灵活性,可以通过名称和类型来指定依赖。
    • 缺点:与Spring框架的整合可能不如@Autowired紧密,因此在某些复杂情况下可能不如@Autowired方便。
  3. 构造器注入
    • 优点:构造器注入通常被认为是最安全、最推荐的依赖注入方式。它可以确保所有的依赖在对象创建时就被设置,从而避免了空指针异常。这种方式也使得依赖关系更加明确,有利于代码的清晰度和可维护性。
    • 缺点:当依赖关系较多时,构造器参数列表可能会变得很长,导致代码可读性下降。此外,复杂的构造器可能会导致难以理解和维护的对象创建逻辑。
    • @AllArgsConstructor@RequiredArgsConstructor都是Lombok库中的注解,用于自动生成构造函数,但它们的作用略有不同:
  • @AllArgsConstructor:此注解会为类中的所有字段生成一个构造函数。
  • @RequiredArgsConstructor:此注解会为类中的每一个final字段或者@NonNull字段生成一个构造函数。
  • 如果你在使用@Value注解进行注入,@RequiredArgsConstructor可以成功注入,而@AllArgsConstructor可能会导致注入失败。

@Value注解通常与依赖注入框架(如Spring)一起使用,这些框架通过构造函数来注入值。当您在类上使用@Value注解时,依赖注入框架会使用该注解自动生成的构造函数来创建对象实例,并将值注入到该对象中。
然而,如果您同时使用@AllArgsConstructor注解,Lombok会为您的类生成另一个包含所有字段的构造函数。这个构造函数会优先于依赖注入框架的操作执行。因此,当您创建对象实例时,首先调用的是@AllArgsConstructor生成的构造函数,此时依赖注入框架还没有机会注入任何值。这意味着在构造函数内部,您尝试访问的任何通过@Value注解本应注入的字段都会是null
所以,当@AllArgsConstructor@Value一起使用时,@AllArgsConstructor的构造函数会先执行,导致@Value的注入逻辑失效,因为这些字段在注入发生之前就已经被赋予null值了。

在选择依赖注入方式时,建议根据具体场景和需求来决定。例如,对于关键或复杂的依赖关系,使用构造器注入是一个好选择,因为它可以确保这些依赖关系在对象创建时就准备就绪。对于简单的依赖关系,@Autowired可能更为方便。而@Resource则适用于需要更细粒度控制的场景。

  • 13
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值