Spring Boot 注解 常用注解说明

Spring Boot是一个快速开发框架,快速的将一些常用的第三方依赖整合(通过Maven子父亲工程的方式),简化xml配置,全部采用注解形式,内置Http服务器(Jetty和Tomcat),最终以Java应用程序进行执行。Spring DIP

一、注解Controller 和 RestController

RestController的作用相当于Controller加ResponseBody共同作用的结果,但采用RestController请求方式一般会采用Restful风格的形式。

Controller的作用:声明该类是Controller层的Bean,将该类声明进入Spring容器中进行管理

ResponseBody的作用:表明该类的所有方法的返回值都直接进行提交而不经过视图解析器,且返回值的数据自动封装为json的数据格式

RestController的作用:包含上面两个的作用,且支持Restful风格的数据提交方式

Restful风格:

    get:获取数据时用的请求方式

    post:增加数据时的请求方式

    put:更新数据时的请求方式

    delete:删除数据时的请求方式

在每个Controller类的上边加速注解,用于形成API接口交付给前端

二、@NoArgsConstructor、@AllArgsConstructor、@RequiredArgsConstructor

区别:

  • @NoArgsConstructor:生成无参的构造方法。
  • @AllArgsConstructor:生成该类下全部属性的构造方法。
  • @RequiredArgsConstructor:生成该类下被final修饰或者non-null字段生成一个构造方法。

场景:

在springboot中,对于一个bean类,注入其他bean的时候,常见的是使用@Autowired,实际上也可以使用构造函数注入,
这个时候就可以使用@AllArgsConstructor或者@RequiredArgsConstructor来代替。

三、泛型

以下代码编译器会报错(com.yang.memorytest.Person.this’ cannot be referenced from a static context),因为泛型类型只有在类实例化的时候才确定,而静态方法可以直接通过类来调用;

如果静态方法需要使用泛型,可以在方法定义泛型,

    public class Person {    
        public static <T>void a(T data){        
        }
    }
    public static <T> Result<T> sucess(){
        return new Result<>(20008, "sucess", null);
    }

四、@Resource @Autowired

spring的管理机制Bean工厂已经把对象创建好了 你只需要调用就行!

一般代码中对象普遍是new Class把对象实例出来,注入之后呢,spring帮你new好了,你可以直接用了

@Resource主要做依赖注入,从容器中自动获取bean,作用如下:

在启动Spring时,首先要启动容器
在启动Sprng容器时,会默认寻找容器扫描范围内的可加载的bean,然后查找哪些bean上的属性和方法有@resource注解
找到@resource注解后,判断其name属性是否为空,若为空,看Spring容器中的bean中的id与@resource要注解的那个变量属性名是否相同,如相同,匹配成功;如不同,看spring容器中bean的id对应的类型是否与@resource要注解的那个变量属性对应的类型是否相等,若相等,匹配成功,若不相等,匹配失败
如果@resource注解括号中的name属性不为空,看name的属性值和容器中的bean的id名是否相等,如相等,则匹配成功,如果不相等,则匹配失败

@Resource和@Autowired注解都是用来实现依赖注入的。只是@Autowired按by type自动注入,而@Resource默认按by name自动注入

@Autowired 和 @Resource 都是用来实现依赖注入的注解(在 Spring/Spring Boot 项目中),但二者却有着 5 点不同:

来源不同:@Autowired 来自 Spring 框架,而 @Resource 来自于(Java)JSR-250;
依赖查找的顺序不同:@Autowired 先根据类型再根据名称查询,而 @Resource 先根据名称再根据类型查询;
支持的参数不同:@Autowired 只支持设置 1 个参数,而 @Resource 支持设置 7 个参数;
依赖注入的用法支持不同:@Autowired 既支持构造方法注入,又支持属性注入和 Setter 注入,而 @Resource 只支持属性注入和 Setter 注入;
编译器 IDEA 的提示不同:当注入 Mapper 对象时,使用 @Autowired 注解编译器会提示错误,而使用 @Resource 注解则不会提示错误。

https://www.jb51.net/article/260980.htm

五、@Component和@Bean

  1. @Component 和 @Bean 是两种使用注解来定义bean的方式。

  2. @Component(和@Service和@Repository)用于自动检测和使用类路径扫描自动配置bean。注释类和bean之间存在隐式的一对一映射(即每个类一个bean)。这种方法对需要进行逻辑处理的控制非常有限,因为它纯粹是声明性的。

  3. @Bean用于显式声明单个bean,而不是让Spring像上面那样自动执行它。它将bean的声明与类定义分离,并允许您精确地创建和配置bean。

  4. @Bean则常和@Configuration注解搭配使用

    1.Component注解表明一个类会作为组件类,并告知Spring要为这个类创建bean。
    
    2.Bean注解告诉Spring这个方法将会返回一个对象,这个对象要注册成Spring应用上下文中的bean。通常方法体包含了最终产生bean实例的逻辑。
  5. @Autowired或者@Resource注解注入

  6. 为什么有了@Compent,还需要@Bean呢?

    如果想将第三方的类变成组件,你又没有没有源代码,也就没办法使用@Component进行自动配置,这种时候使用@Bean就比较合适了。不过同样的也可以通过xml方式来定义。
    
    另外@Bean注解的方法返回值是对象,可以在方法中为对象设置属性。
    
    另外了解一下Spring的Starter机制,就是通过@Bean注解来定义bean。可以搭配@ConditionalOnMissingBean注解 
    
    @ConditionalOnMissingClass注解,如果本项目中没有定义该类型的bean则会生效。避免在某个项目中定义或者通过congfig注解来声明大量重复的bean。

区别:

- Component通常是通过类路径扫描来自动侦测以及自动装配到Spring容器中
- Bean注解通常是我们在标有该注解的方法中定义产生这个bean的逻辑。
- Component用在类上,Bean用在方法上。

Spring DIP 依赖倒置原则

springboot中文社区

一、java 基础

在Java中,定义方法返回值为接口类型,需要实现该接口的具体实现类,并在方法中返回该实现类的实例对象。

例如,定义一个方法返回值为List接口类型,可以通过以下方式实现:

public List<String> getList() {
    List<String> list = new ArrayList<>();
    // add elements to the list
    return list;
}

在这个例子中,实现了List接口的具体实现类ArrayList,并在getList方法中返回了ArrayList的实例对象。由于ArrayList实现了List接口,因此可以将其作为List类型的返回值。

当调用该方法时,可以使用List类型来接收返回值:

List<String> result = getList();

这样做的好处是,如果后续需要修改返回值的类型,只需要修改返回的实现类即可,而无需修改调用该方法的代码。同时,使用接口作为返回类型也符合面向对象编程的原则,提高了代码的可扩展性和可维护性。

二、Spring 中如何注入Bean

在Spring中,可以使用@Autowired或@Resource注解来注入返回值类型是接口的Bean。

例如,定义一个接口UserService:

    public interface UserService {
        void addUser(User user);
        User getUserById(int id);
    }

然后有一个实现类UserServiceImpl:

    @Service
    public class UserServiceImpl implements UserService {
        // ...
    }

使用@Autowired注解来注入UserService:

@Service
public class SomeService {
    @Autowired
    private UserService userService;
    // ...
}

使用@Resource注解来注入UserService:

@Service
public class SomeService {
    @Resource
    private UserService userService;
    // ...
}

需要注意的是,如果有多个实现类实现了同一个接口,需要使用@Qualifier注解来指定注入的实现类。例如:

@Service
public class SomeService {
    @Autowired
    @Qualifier("userServiceImpl2")
    private UserService userService;
    // ...
}

三、针对接口编程

针对接口编程而不是具体实现类,是一种编程思想,也是面向对象编程的核心原则之一——依赖倒置原则(DIP)。这种编程方式可以使代码更加灵活、可扩展、易于维护。

在 Java 中,可以使用接口作为方法参数、返回值或成员变量的类型,从而实现针对接口编程而不是具体实现类。下面是一个简单的例子:

假设有一个接口 Animal 和两个实现类 Dog 和 Cat:

public interface Animal {
    public void makeSound();
}

public class Dog implements Animal {
    @Override
    public void makeSound() {
        System.out.println("Wang Wang!");
    }
}

public class Cat implements Animal {
    @Override
    public void makeSound() {
        System.out.println("Miao Miao!");
    }
}

现在我们需要一个方法来处理动物的声音,但是我们不确定传入的具体是什么动物,这时候我们可以使用 Animal 接口来作为参数类型:

public class AnimalSound {
    public void makeSound(Animal animal) {
        animal.makeSound();
    }
}

这样,无论传入的是 Dog 还是 Cat,都可以正确输出声音。

使用接口编程还有一个好处是,可以方便地实现依赖注入。比如,我们有一个使用 Animal 接口作为参数类型的类:

public class AnimalService {
    private Animal animal;

    public AnimalService(Animal animal) {
        this.animal = animal;
    }

    public void makeSound() {
        animal.makeSound();
    }
}

使用 Spring 容器进行依赖注入时,只需要将具体的实现类配置为 Bean,然后在 AnimalService 中使用 @Autowired 注解注入即可:

@Configuration
public class AppConfig {
    @Bean
    public Animal dog() {
        return new Dog();
    }

    @Bean
    public AnimalService animalService(Animal animal) {
        return new AnimalService(animal);
    }
}

这样,无论是注入 Dog 还是 Cat,都可以正确输出声音。同时,如果需要新增一种动物实现,只需要实现 Animal 接口,并将其配置为 Bean 即可,不需要修改 AnimalService 类的代码。这就体现了针对接口编程而不是具体实现类的优势。

四、具体实例

有这样一个类

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class MySecurity {
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
}

这段代码是一个Spring的配置类,使用@Configuration注解标记,表示这是一个Java配置文件。

其中@Bean注解用于声明一个方法,这个方法返回一个PasswordEncoder类型的对象,并且这个对象会交给Spring容器管理,可以被其他组件注入使用。

具体来说,这段代码中声明了一个名为passwordEncoder的Bean,这个Bean的类型是PasswordEncoder,返回的是一个BCryptPasswordEncoder对象。BCryptPasswordEncoder是Spring Security提供的一种密码加密工具,用于对用户密码进行加密处理。

这个Bean的作用是在Spring Security中使用加密功能时提供PasswordEncoder实例,可以通过在其他组件中使用@Autowired注解来引用这个Bean,从而使用BCryptPasswordEncoder对密码进行加密处理。

假设我们需要在一个服务类中使用 PasswordEncoder 接口,可以使用 Spring 的依赖注入来获取该接口的实例。假设服务类的名称为 MyService,注入过程如下:

import org.springframework.security.crypto.password.PasswordEncoder;

@Service
public class MyService {
    @Autowired
    private PasswordEncoder passwordEncoder;

    // 其他代码
}

已经在Spring容器中定义了一个实现PasswordEncoder接口的Bean。在代码中使用@Autowired注解进行自动注入时,Spring会自动查找容器中是否有符合类型的Bean,然后注入到对应的字段中。因此,如果MySecurity配置类中定义了一个返回值类型为PasswordEncoder的Bean,并且该类被正确扫描到并加载到Spring容器中,那么在其他需要使用该Bean的地方,可以使用@Autowired进行自动注入。

虽然PasswordEncoder是一个接口,但是在MySecurity配置类中,使用@Bean注解定义了一个passwordEncoder()方法并返回了一个实现了PasswordEncoder接口的BCryptPasswordEncoder对象。这样在Spring容器启动时会创建该对象并将其注册到容器中,因此在其他需要使用PasswordEncoder的地方,可以通过@Autowired注解将其注入进去,实际上注入的是BCryptPasswordEncoder对象。这也是Spring中面向接口编程的一个重要原则:针对接口编程而不是具体实现类。

springboot gradle springboot源码学习 jetbrain 环境配置

springboot gradle,笔者用到的所有工具的版本号不是最新的,而且笔者哪怕去找较新的版本也不会使用最新版本,因为最新版本往往会有很多不可控的问题,所以一般最高使用的是次新版本。springboot下载源码时可能遇到的问题在之前的篇幅中已经介绍过了。本篇重点记录在jetbrain IntelliJ IDEA中配置springboot源码基于gradle构建的环境并运行部分smoke-test的过程。

  1. 环境版本说明
  • Windows 10
  • java version “1.8.0_40”
  • IntelliJ IDEA 2018.3.6
  • Apache Maven 3.6.1
  • Gradle 7.2 (笔者最初设想的版本,但是编译时会有坑,后边有修正成6.9.1)
  • git version 2.21.0.windows.1
  1. 工具安装配置

(1) JDK, 网上的教程很多,基本的思路和操作方式都是差不多的,大同小异,笔者提供一般不错的教程地址: https://www.runoob.com/w3cnote/windows10-java-setup.html

(2) Git & tortoisegit笔者在写教程的时候 git的最新版本已经到了2.34.0,与笔者的版本有出入,读者可以根据自己的需求去下载。下载地址的最后一个win说明是对应windows系统的,如果读者是mac或者linux系统可以去对应的地址下载。https://git-scm.com/download/win我们还可以再安装一个,tortoisegit–git的一个比较好用的客户端。它的下载地址如是: https://tortoisegit.org/download/ Tortosegit安装教程:https://www.cnblogs.com/xuwenjin/p/8573603.html 

(3) Maven & gradleMaven下载地址: https://maven.apache.org/download.cgiGradle 下载地址: https://services.gradle.org/distributions/Maven和Gradle 在windows系统的安装配置的过程同样不再提供具体的细节,读者自行查询,或者用笔者提供的教程地址去阅读后跟着安装配置https://www.runoob.com/maven/maven-repositories.html

(4) IntelliJ IDEA 2018.3.6下载地址: https://www.jetbrains.com/idea/download/#section=windowsIntellij IDEA的专业版有时间限制,网上有各种的破解方案又获取可以去某bao买一个便宜点的账号,如果读者有安全的白嫖方案亦可。

  1. GitHub找到springboot及源码拉取

找个gayhub的springboot地址:https://github.com/spring-projects/spring-boot

springboot github源码下载示例

在页面的右上角可以看到它stars和fork数量。我们想要得到源码一般就是这么几种方式,一是把源码fork到自己的github地址下,这么做的优势是可以做一个自己的分支,而且自己在源码中做一些改动还可以提交到自己分支中,并且还可以跟着主分支一起更新最新的代码,保持先进行;二是直接git clone master分支的源码,再一个就是直接下载当前版本的zip文件。笔者采用的是将源码fork到自己的分支下,然后直接用git下载到pc上。

  1. Git clone下载springboot

我们打开一个想要用来存放源代码的文件夹—>在文件夹的空白处【右键】–>会弹出如下的界面:我们会看到Git Bash Here 和 Git Clone (如果没有安装TortoiseGit没有Git Clone这个选项) 这些选项,这两个选项任意一个都可以把我们需要的代码下载到本地。

区别: Git Bash Here 命令行客户端; Git Clone 可视化客户端

TortoiseGit windows 快捷方式
springboot 下载示例
springboot git命令下载示例

问题一:直接git clone 总是time out,跟网络有一定的关系,不可描述的原因导致有些时候我们用gayhub的时候必须科学上网,如果没法科学上网那就多尝试几次,总有一年可以搞定。

timeout 报错示例

问题二:OpenSSL SSL_read: SSL_ERROR_SYSCALL, errno 10054, 这个时候需要关闭SSL CERT verification,通过命令 git config http.sslVerify “false”

如果还是报错,那就把全局的也调一下 git config –global http.sslVerify “false”

SSL_ERROR_SYSCALL 错误示例

问题三: error: unable to create file *****: Filename too long
原因是: git有可以创建4096长度的文件名,然而在windows最多是260处理方法是执行命令: git config –global core.longpaths true

  1. Intellij IDEA配置springboot源码及编译

打开我们的Intellij IDEA,然后通过打开按钮选择我们下载好的springboot, 如下图,(如果是不同版本的IDEA在操作界面上是有一些区别的,有的版本open 和 import Project是在一起的),找到我们的项目点击OK。

idea 载入springboot
idea 配置指定本地gradle 和maven仓库

问题一: MultipleCompilationErrorsException: startup failed 一般这种错误是Gradle的版本问题导致,而我们的教程在上图中已经给出来了答案,我们制定了版本但是在打开文件的时候由于笔者以前gradle历史版本的记录而没有修改成最新的gradle版本,也就是上图中的Gradle home那一栏的的默认值导致的gradle版本不同。

MultipleCompilationErrorsException 示例

问题二:AssertionFailedError  在编译的时候可能会遇到这种错误。类似下图, 这个问题的本质同样是由于gradle的版本问题导致的, 一般来说是gradle的版本比较高,没有与我们编译的springboot相对应才导致的这种错误。

distributionUrl=https\://services.gradle.org/distributions/gradle-6.9-bin.zip
AssertionFailedError 示例

最好的方式就是使用官方指定的gradle版本,而这个版本就在源码根目录下的gradle–wrapper–gradle-wrapper.properties文件中的distributionUrl给出了,我们只需要把对应版本的gradle下载到本地,并在IDEA的 File –>  Settings –> Build, Execution, Deployment –> Build Tools –> Gradle 中找到Gradle home 指定到本地的对应目录即可。如下图

gradlehome 配置

除了gradle的版本要对应正确之外,最关键的是要有几个配置项需要调整,在笔者参考的连接页面中已经解释的比较详尽了,但是笔者此处还是要强调一下,源码中调整的配置文件主要是为了配置maven的国内加速镜像以及调整gradle每次编译时使用的主要jar包工具等,总而言之就是为了爽。

  • 项目根目录下build.gradle[即文件路径:spring-boot/build.gradle],文件最开始添加如下配置
buildscript{
   repositories{
      maven { url 'https://maven.aliyun.com/nexus/content/groups/public/'}
      maven { url 'https://maven.aliyun.com/nexus/content/repositories/jcenter'}
      maven { url 'https://repo.spring.io/plugins-release'}
   }
}
  • 项目根目录下settings.gradle[即文件路径:spring-boot/settings.gradle], 在pluginManagement–> repositories 中添加两行
pluginManagement {
   repositories {
      maven { url 'https://maven.aliyun.com/nexus/content/groups/public/'}
      maven { url 'https://maven.aliyun.com/nexus/content/repositories/jcenter'}

      mavenCentral() 
      ...
}
  • 项目子根目录buildSrc下的build.gradle[即文件路径:spring-boot/buildSrc/build.gradle], 在repositories中添加maven加速镜像的三行
repositories {
   maven { url 'https://maven.aliyun.com/nexus/content/groups/public/'}
   maven { url 'https://maven.aliyun.com/nexus/content/repositories/jcenter'}
   maven { url 'https://repo.spring.io/plugins-release'}
   mavenCentral()
   gradlePluginPortal()
   maven { url "https://repo.spring.io/release" }
}
  • 项目子根目录buildSrc下的settings.gradle[即文件路径:spring-boot/buildSrc/settings.gradle], 在repositories中添加maven加速镜像的三行
pluginManagement {
   repositories {
      maven { url 'https://maven.aliyun.com/nexus/content/groups/public/'}
      maven { url 'https://maven.aliyun.com/nexus/content/repositories/jcenter'}
      maven { url 'https://repo.spring.io/plugins-release'}
      mavenCentral()
      gradlePluginPortal()
   }
  • 项目子根目录gradle下的radle-wrapper.properties[即文件路径:spring-boot/gradle/wrapper/gradle-wrapper.properties], 修改distributionUrl,即gradle编译工具为本地的路径文件,否则每次编译的时候总是会去网上拉取文件
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
#distributionUrl=https\://services.gradle.org/distributions/gradle-6.9-bin.zip
distributionUrl=file:///C:/AppConfigs/gradle-6.9-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

只要gradle的版本对应好,基本的maven仓库和gradle配置没有问题的话再十几分钟的编译后总是可以通过的,当然读者可能会遇到各种Heap堆内存没有足够的空间的问题,那是读者的PC硬件参数偏低或者IDEA配置的问题,可以按照这个地址https://blog.csdn.net/smart_an/article/details/107219821的方式配置一下内存参数。

springboot编译成功
  1. 源码smoke-test冒烟测试

笔者主要运行了两个模块的smoke-test,分别是spring-boot-smoke-test-hibernate52和spring-boot-smoke-test-data-redis。笔者在运行冒泡测试Hibernate52Application的时候遇到了以下的问题。解决方法是升级一下kotlin插件,settings -> Languages & Frameworks -> kotlin(如下图)

Information:Kotlin: kotlinc-jvm 1.3.11 (JRE 1.8.0_40-b25)Information:2021/11/23 16:34 - Compilation completed with 1 error and 0 warnings in 12 s 48 msError:Kotlin: [Internal Error] java.lang.LinkageError: loader constraint violation: loader (instance of org/jetbrains/kotlin/cli/jvm/plugins/PluginURLClassLoader$SelfThenParentURLClassLoader) previously initiated loading for a different type with name "kotlin/sequences/Sequence"
升级kotlin插件

运行redis冒烟测试的时候如果没有任何的额外配置和修改,则会默认连接redis的本地地址路径,我们可以在模块下添加一个ymal文件来指定自己的redis地址,如下图在模块的main下添加一个resources文件夹,新建一个yml文件且文件被ide识别成绿叶子的样子即可。在文件中添加redis的配置信息。

somke-redis增加配置文件
spring:
  redis:
    port: 6739
    host: 1*.*.*.*
    password: ****

不论我们运行哪一个冒烟测试,如果需要额外的配置都可以按照spring的预定新建yaml文件或者properties文件;而且如果要debug进源代码也是可以的,跟中springboot的源码主要是跟进main函数的run方法,一层层的run方法点进去就可以看到springboot启动的时候所有的流程细节。