精华内容
下载资源
问答
  • SpringBoot中常用的注解

    2020-11-03 22:13:13
    SpringBoot中常用的注解 1.@SpringBootApplication 此注解是SpringBoot的核心注解,有此注解的类是SpringBoot的启动类, 它会在启动时自动扫描以下几个注解:@Component、@Repository、@Service、@Controller 2.@...

    SpringBoot中常用的注解

    1.@SpringBootApplication

    此注解是SpringBoot的核心注解,有此注解的类是SpringBoot的启动类,

    它会在启动时自动扫描以下几个注解:@Component、@Repository、@Service、@Controller

    2.@Service(业务层)

    用于标注业务层组件

    3.@Controller(控制层/Web层)

    用于标注控制层组件

    4.@Repository(持久层)

    用于标注数据访问组件,即DAO组件

    5.@Component

    泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。

    例如用户配置类

    注意:

    这 3 个注释和 @Component 是等效的,但是从注释类的命名上,很容易看出这 3 个注释分别和持久层、业务层和控制层(Web 层)相对应。
    虽然目前这3 个注释和 @Component 相比没有什么新意,但 Spring 将在以后的版本中为它们添加特殊的功能。
    所以,如果 Web 应用程序采用了经典的三层分层结构的话,最好在持久层、业务层和控制层分别采用上述注解对分层中的类进行注释。

    6.@Value

    只能给属性赋字符串类型或整数的值,可用在实体类中的属性上

    7.@RestController

    该注解一般标记在控制层。它是@Controller和@ResponseBody注解的组合

    • @Controller注解:表示此类是一个控制器类
    • @ResponseBody注解:表示输出的是JSON格式的数据

    注意2:

    @RestController和@Controller的区别:

    @RestController:加了此注解页面无法跳转,返回内容就是return中的内容

    @Controller:页面可以跳转,并且可以携带数据

    8.@Autowired

    是一个自动装配的注解,它会在容器使用此类时自动注入,也就意味着在使用某个类时不用手动 new 对象,这是一个很重要的注解

    9.@GetMapping("/xx")

    它是一个组合注解,是@RequestMapping(value="/xx", method = RequestMethod.GET)的缩写版本,该注解将HTTP Get 映射到 特定的处理方法上。

    10.RequestMapping("/xx")

    注意3:

    Get/Post请求的区别:

    1. 哪一些情况下,浏览器会发送get请求

      a. 直接在浏览器地址栏输入某个地址

      b. 点击链接

      c. 表单默认的提交方式

    2. 哪一些情况下,浏览器会发送post请求?

      a. 设置表单method = “post

    3. get请求的特点

      a. 请求参数会添加到请求资源路劲的后面,只能添加少量参数(因为请求行只有一行,大约只能存放2K左右的数据)(2K左右的数据,看起来也不少。。。)

      b. 请求参数会显示在浏览器地址栏,路由器会记录请求地址

    4. post请求的特点

      a. 请求参数添加到实体内容里面,可以添加大量的参数(也解释了为什么浏览器地址栏不能发送post请求,在地址栏里我们只能填写URL,并不能进入到Http包的实体当中)

      b. 相对安全,但是,post请求不会对请求参数进行加密处理(可以使用https协议来保证数据安全)

      springboot框架还在学习中,后续会添加更多常用注解

    展开全文
  • Springboot中常用的注解

    2020-09-06 20:38:18
    文章目录Spring注解SpringBoot注解swagger其余注解 Spring注解 @Configuration用于定义配置类,可替换xml配置文件 @ComponentScan告诉Spring 哪个packages 注解标识类 会被spring自动扫描并且装入bean容器...

    Spring注解

    1. @Configuration用于定义配置类,可替换xml配置文件

    2. @ComponentScan告诉Spring 哪个packages 的用注解标识的类 会被spring自动扫描并且装入bean容器。例如,如果你有个类用@Controller注解标识了,那么,如果不加上@ComponentScan,自动扫描该controller,那么该Controller就不会被spring扫描到,更不会装入spring容器中,因此你配置的这个Controller也没有意义。

    3. @Repository 代表数据访问层(DAO)的时候

    4. @Service 当一个组件代表业务层时

    5. @Controller 一个组件作为前端交互的控制层

    6. @Autowired 注解可用于为类的属性、构造器、方法进行注值

    SpringBoot注解

    1. @SpringBootApplication 注解
    很多Spring Boot 开发者总是使用 @Configuration , @EnableAutoConfiguration 和 @ComponentScan 注解他们的 main 类。由于这些注解被如此频繁地一块使用(特别是你遵循以上最佳实践时), Spring Boot 提供一个方便的 @SpringBootApplication 选择。
    
    该 @SpringBootApplication 注解等价于以默认属性使用 @Configuration , @EnableAutoConfiguration 和 @ComponentScan 。
    
    1. @RestController继承自Controller,标注为一个Controller,同时返回数据为json,开发 REST 服务,用在类上面。responsebody用在方法上面。

    2. @requestMapping 该注解可以填写,请求的方法,请求路径。SpringBoot中,还有Get mapping,postmapping等

    3. @EnableAutoConfiguration 注解

    4. @ResponseBody
      表示该方法的返回结果直接写入HTTP response body 中

    一般在异步获取数据时使用,在使用@RequestMapping 后,返回值通常解析为跳转路径,加上

    @responsebody 后返回结果不会被解析为跳转路径,而是直接写入 HTTP response body 中。比如

    异步获取json 数据,加上 @responsebody 后,会直接返回 json 数据

    swagger

    1.ApiOperation

    swagger内的注解,(value = “接口说明”, httpMethod = “接口请求方式”, response = “接口返回参数类型”, notes = “接口发布说明”)

    1. API

    其余注解

    1.Transactional

    //对于一组数据库操作特别是增删改操作,为了保证原子性,通过需要用事务来控制,要么全部成功,要么全部失败。Spring中可以通过注解@Transaction
    
    @Transactional
    public void testTransaction(User user) {
        int rowNum = userMapper.insertUser(user);
        List<User> userList = userMapper.selectAllUsers();
    }
    
    //将两个操作insert和select当作原子操作,如果在testTransaction方法中有异常,则回滚。
    

    2.Param

    dao层定义接口时,如果传入多个参数,就需要用到@Param注

    采用#{}的方式把@Param注解括号内的参数进行引用
    #{} 能够防止sql注入

    dao层示例
    
    Public User selectUser(@param(“userName”) String name,@param(“userpassword”) String password);
    
    xml映射对应示例
    
    
    <select id=" selectUser" resultMap="BaseResultMap">  
       select  *  from user_user_t   where user_name = #{userName,jdbcType=VARCHAR} and user_password=#{userPassword,jdbcType=VARCHAR}  
    </select>
    
    

    @Param注解JavaBean对象

    dao层示例
    
    public List<user> getUserInformation(@Param("user") User user);
    
    xml映射对应示例
    
    
    <select id="getUserInformation" parameterType="com.github.demo.vo.User" resultMap="userMapper">  
            select   
            <include refid="User_Base_Column_List" />  
            from mo_user t where 1=1  
                          <!-- 因为传进来的是对象所以这样写是取不到值得 -->  
                <if test="user.userName!=null  and user.userName!=''">   and   t.user_name = #{user.userName}  </if>  
                <if test="user.userAge!=null  and user.userAge!=''">   and   t.user_age = #{user.userAge}  </if>  
        </select>  
    
    1. @PropertySource
      注解加载指定的属性文件

    2. @EnableAspectJAutoProxy开启AOP

    3. @RequestParamRequestParam从request中接收请求的,两个都可以接收参数
      用在方法的参数前面:

    http://localhost:8080/springmvc/hello/101?param1=10&param2=20
    

    根据上面的这个URL,可以这样来获取参数。

    public String getDetails(
        @RequestParam(value="param1", required=true) String param1,
            @RequestParam(value="param2", required=false) String param2){
    ...
    }
    

    @PathVariable,URL中的路径变量会成为方法参数。

    http://localhost:8080/springmvc/hello/101?param1=10&param2=20
    

    上面的一个url你可以这样写:

    @RequestMapping("/hello/{id}")
        public String getDetails(@PathVariable(value="id") String id,
        @RequestParam(value="param1", required=true) String param1,
        @RequestParam(value="param2", required=false) String param2){
    .......
    }
    
    1. @RequestBody
      一般是post请求的时候才会使用这个请求,把参数丢在requestbody里面
    展开全文
  • springboot中常用的注解

    2021-01-25 19:13:02
    该注解的用法是:如果某个类使用了被@Inherited标注的注解,则该类的子类会自动继承该注解,及@Inherited的作用就是标记注解是否是可被继承的。 分析如下: 该注解源码如下: java.lang.annotation.Inherited @...

    1:@Inherited

    该注解的用法是:如果某个类使用了被@Inherited标注的注解,则该类的子类会自动继承该注解,即@Inherited的作用就是标记注解是否是可被继承
    分析如下:
    该注解源码如下:

    java.lang.annotation.Inherited
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.ANNOTATION_TYPE)
    public @interface Inherited {
    }
    

    @Target(ElementType.ANNOTATION_TYPE)可以看出该注解是用在注解上的注解,因此是元注解

    1.1:标注了@Inherited

    如果是某个注解使用了@Inherited注解进行标注,如下:

    @Inherited
    @Retention(RetentionPolicy.RUNTIME)
    public @interface InheritedTest {
        String value();
    }
    

    这里我们定义了注解InheritedTest,然后我们将该注解使用在类上,如下:

    @InheritedTest("我是被@Inherited标注的注解")
    public class Parent {
    }
    

    然后我们直接看Parent的注解信息:

    public static void main(String[] args) throws NoSuchMethodException, SecurityException, NoSuchFieldException {
    	Class<Parent> parentClass = Parent.class;
    	boolean annotationPresent = parentClass.isAnnotationPresent(InheritedTest.class);
    	if (annotationPresent) {
    		String value = parentClass.getAnnotation(InheritedTest.class).value();
    		System.out.println(value);
    	}
    }
    

    输出:

    我是被@Inherited标注的注解
    

    可以看到,是有@InheritedTest("我是被@Inherited标注的注解")注解信息的(有点废话,直接获取被标注的类,肯定能获取,不过也是为了说明问题),接下来我们定义一个子类:

    public class Son extends Parent {
    }
    

    然后获取Son的注解:

    public static void main(String[] args) throws NoSuchMethodException, SecurityException, NoSuchFieldException {
    	Class<Son> sonClass = Son.class;
    	boolean annotationPresent = sonClass.isAnnotationPresent(InheritedTest.class);
    	if (annotationPresent) {
    		String value = sonClass.getAnnotation(InheritedTest.class).value();
    		System.out.println(value);
    	}
    }
    

    输出:

    我是被@Inherited标注的注解
    

    同样也输出了,说明Son继承了Parent的@InheritedTest的注解。接下来我们再测试下没有被标注的情况。

    1.2:没有标注@Inherited

    定义一个没有使用@Inherited注解的注解:

    public @interface NoInheriedAnnotation {
        String value();
    }
    

    将注解是用在类上:

    @NoInheriedAnnotation("我是没有被@Inherited标注的注解")
    public class NoInheriedAnnotationParent {
    }
    

    定义子类:

    public class NoInheriedAnnotationSon extends NoInheriedAnnotationParent {
    }
    

    查看子类是否继承了注解:

    public static void main(String[] args) throws NoSuchMethodException, SecurityException, NoSuchFieldException {
    	Class<NoInheriedAnnotationSon> noInheriedAnnotationSonClass = NoInheriedAnnotationSon.class;
    	boolean annotationPresent = noInheriedAnnotationSonClass.isAnnotationPresent(NoInheriedAnnotation.class);
    	if (annotationPresent) {
    		String value = noInheriedAnnotationSonClass.getAnnotation(NoInheriedAnnotation.class).value();
    		System.out.println(value);
    	} else {
    		System.out.println("没有继承注解NoInheriedAnnotation");
    	}
    }
    

    输出:

    没有继承注解NoInheriedAnnotation
    

    可以看到注解没有被继承。

    2:@Repeatable

    该注解是jdk定义的一个注解,源码如下:

    java.lang.annotation.Repeatable
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.ANNOTATION_TYPE)
    public @interface Repeatable {
       
        Class<? extends Annotation> value();
    }
    

    被该注解标注的注解可以在类上被重复的使用,下面看个实例。从源码中可以看到其value是一个Class<? extends Annotation>,实际上Class<? extends Annotation>是最终用来容纳被使用了多次的注解的容器,因此我们先来定义这个注解容器,我们假定这个可以重复定义的注解是RepeatableAnnotation,后续会定义:

    @Target(ElementType.TYPE)
    // 存放可重复注解的容器的注解
    @Retention(RetentionPolicy.RUNTIME)
    public @interface RepeatableAnnotationContainer {
        // 存放被重复使用了n次的可重复注解
        RepeatableAnnotation[] value();
    }
    

    接着我们来定义可重复定义注解,即被@Repeatable注解的注解,并指定存放自己的重复定义的容器注解为前面定义的@RepeatableAnnotationContainer:

    // 定义可重复定义的注解,并指定其重复定义的注解容器为RepeatableAnnotationContainer.class
    @Repeatable(RepeatableAnnotationContainer.class)
    public @interface RepeatableAnnotation {
        String yourName() default "";
    }
    

    接着我们定义一个类来使用我们自定义的可重复注解:

    @RepeatableAnnotation(yourName = "张三")
    @RepeatableAnnotation(yourName = "李四")
    @RepeatableAnnotation(yourName = "王五")
    @RepeatableAnnotation(yourName = "赵六")
    public class RepeatableAnnotationUse {
    }
    

    可以看到我们重复定义了4次,接下来我们来获取重复定义的注解:

    public static void main(String[] args) {
        Annotation[] annotations = RepeatableAnnotationUse.class.getAnnotations();
        System.out.println(annotations.length);
    }
    

    输出结果:
    1,并不是我们期望的4
    我们来通过debug看下具体类型:
    在这里插入图片描述
    可以看到是我们定义的容器注解RepeatableAnnotationContainer,我们来通过容器注解获取注解的内容:

    public static void main(String[] args) {
        Annotation[] annotations = RepeatableAnnotationUse.class.getAnnotations();
        RepeatableAnnotationContainer annotationContainer = (RepeatableAnnotationContainer) annotations[0];
        for (RepeatableAnnotation repeatableAnnotation : annotationContainer.value()) {
            System.out.println(repeatableAnnotation.yourName());
        }
    }
    

    输出如下:

    李四
    王五
    赵六
    张三
    

    3:@Indexed

    该注解是在spring5中定义的,其源码如下:

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Indexed {
    }
    

    用来解决通过@ComponentScan扫描包过多时,导致spring容器初始化慢的问题,在编译后会在META-INF/spring.components文件中生成需要扫描的索引条目,但是注意需要引入spring-context-indexer依赖,下面测试一下。
    定义一个类:

    @Indexed
    @Component
    public class AService {
    
        public void sayHi() {
            System.out.println("AService hi");
        }
    }
    

    引入索引需要的依赖:

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-indexer</artifactId>
        <optional>true</optional>
    </dependency>
    

    编译打包:
    在这里插入图片描述
    内容如下不用深究

    #
    #Thu Jan 28 19:14:53 CST 2021
    dongshi.daddy.service.AService=org.springframework.stereotype.Component,dongshi.daddy.service.AService
    dongshi.daddy.HelloWorldMainApplication=org.springframework.stereotype.Component
    dongshi.daddy=package-info
    dongshi.daddy.controller.HelloController=org.springframework.stereotype.Component
    

    之后在运行阶段,spring就可以直接读取这个文件来初始化spring容器了,就不需要再扫描文件了,从而提高容器初始化的速度。
    在spring项目中,该注解会用在@Component注解上,该注解是一个用来标记是一个spring bean的注解,其实就是使用了@Component注解就会自动在编译时进行索引,如下源码:

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Indexed
    public @interface Component {
    	String value() default "";
    }
    

    4:@Component

    该注解时spring定义的用来标记成为spring bean的注解,源码如下:

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Indexed
    public @interface Component {
    	String value() default "";
    }
    

    其中被@Indexed注解标记,代表会在编译时期构建索引。

    5:@Configuration

    该注解的作用时标记类为java config类,即使用java类来代替配置文件的一种机制,源码如下:

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Component
    public @interface Configuration {
    
    	@AliasFor(annotation = Component.class)
    	String value() default "";
    
    }
    

    从源码看到该注解被@Component标注,因此使用了该注解的bean,在是一个java config类的同时,也是一个spring bean。

    6:@Import

    该注解是在spring的context包中定义的,是和spring bean的上下文相关的一个注解,其作用是导入java config的配置类,即使用了@Configuration的类,源码如下:

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Import {
    	Class<?>[] value();
    }
    

    可以看到其value是一个Class的数组,一般就是我们使用@Configuration配置的类,下面我们定义一个这样的类:

    @Configuration
    public class OutConfiguration {
    
        @Bean
        public Student student() {
            Student student = new Student();
            student.setName("张三小姐");
            return student;
        }
    }
    

    上面的类有一个问题需要注意,那就是不要放到springboot能够扫描到的包路径下,否则自动扫描机制会自动引入该类,影响我们的测试,基本上放在和springboot启动类所在包的平级包或者是更上级包里就可以了。
    接着我们来定义Student类:

    public class Student {
        private String name;
    
        ...getter setter tostring...
    }
    

    然后定义springboot启动类如下:

    @SpringBootApplication
    //@Import(OutConfiguration.class)
    public class SpringbootHelloworldApplication {
    
        public static void main(String[] args) {
            ConfigurableApplicationContext cac = SpringApplication.run(SpringbootHelloworldApplication.class, args);
            Student student = cac.getBean(Student.class);
            System.out.println(student);
        }
    
    }
    

    注意此时我们的@Import(OutConfiguration.class)是注释掉的,输出如下:
    在这里插入图片描述
    可以看到配置类并没有被引入,这时我们放开@Import(OutConfiguration.class)再重新启动测试:

    2021-01-30 09:52:43.808  INFO 50409 --- [           main] d.d.s.SpringbootHelloworldApplication    : Started SpringbootHelloworldApplication in 4.718 seconds (JVM running for 6.951)
    Student{name='张三小姐'}
    

    引入配置类除了直接指定配置类的class外,还可以指定org.springframework.context.annotation.ImportSelector接口的实现类,该接口定义如下:

    public interface ImportSelector {
    	String[] selectImports(AnnotationMetadata importingClassMetadata);
    
    	@Nullable
    	default Predicate<String> getExclusionFilter() {
    		return null;
    	}
    }
    

    方法selectImports(importingClassMetadata)就是我们需要实现的方法,该方法的入参是在启动类上定义的注解信息,返回的参数是一个数组,是我们自定义的需要引入的java config配置类的全限定名的数组,同样使用上面自定义的javaconfig类实现该接口:

    public class MyImportSelector implements ImportSelector {
    
        /**
         * 获取需要import的配置类数组
         * @param importingClassMetadata 在启动类上定义的注解信息
         * @return 配置类权限定名称数组
         */
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            importingClassMetadata.getAnnotationTypes().forEach(System.out::println);
            return new String[] { OutConfiguration.class.getName() };
        }
    
        @Override
        public Predicate<String> getExclusionFilter() {
            return null;
        }
    }
    

    然后修改@Import(OutConfiguration.class)@Import(MyImportSelector.class)然后再进行测试,可以看到效果完全相同:

    ...
    org.springframework.boot.autoconfigure.SpringBootApplication
    org.springframework.context.annotation.Import
    ...
    Student{name='张三小姐'}
    

    不管在@Import中设置的值是java config的class还是ImportSelector接口的实现类的class,最终都是通过java config的@Bean注解定义的方法返回需要放到IOC容器中的bean对象,这里其实还支持使用BeanDefinition的方式,这种方式我们需要实现org.springframework.context.annotation.ImportBeanDefinitionRegistrar接口,该接口源码如下:

    public interface ImportBeanDefinitionRegistrar {
    
    	
    	default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
    			BeanNameGenerator importBeanNameGenerator) {
    
    		registerBeanDefinitions(importingClassMetadata, registry);
    	}
    
    	default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    	}
    
    }
    

    我们只需要实现registerBeanDefinitions的两个参数的方法,然后定义自己的BeanDefinition,之后注册到注册机中就可以了,如下我们的定义:

    public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            // 定义Student的bean定义
            BeanDefinition studentBeanDefinition = new GenericBeanDefinition();
            // 设置class
            studentBeanDefinition.setBeanClassName(Student.class.getName());
    
            // 设置构造函数的值
            /**
             * public Student(String name) {
             *    this.name = name;
             * }
             */
            ConstructorArgumentValues constructorArgumentValues = new ConstructorArgumentValues();
            // 设置构造函数第一个参数的值
            constructorArgumentValues.addIndexedArgumentValue(0, "李四的大姑");
            // 设置参数定义到bean定义中
            ((GenericBeanDefinition) studentBeanDefinition)
                    .setConstructorArgumentValues(constructorArgumentValues);
            // 注册到注册机中
            registry.registerBeanDefinition("student", studentBeanDefinition);
        }
    }
    

    然后修改的@Import注解为@Import(MyImportBeanDefinitionRegistrar.class),启动测试:

    2021-01-30 10:59:30.705  INFO 53382 --- [           main] d.d.s.SpringbootHelloworldApplication    : Started SpringbootHelloworldApplication in 2.629 seconds (JVM running for 3.803)
    Student{name='李四的大姑'}
    

    我们注意到,以上的方式组中都是将某个对象引入作为IOC容器的bean,殊途同归,那么我们能不能直接将某个想要引入的bean直接作为@Import注解的值呢?也是可以的,我们用这种方式改造启动类:

    @SpringBootApplication
    @Import(Student.class)
    public class SpringbootHelloworldApplication {
    
        public static void main(String[] args) {
            ConfigurableApplicationContext cac = SpringApplication.run(SpringbootHelloworldApplication.class, args);
            Student student = cac.getBean(Student.class);
            System.out.println(student);
        }
    
    }
    

    为了更方便的看到效果,我们改造下Student类:

    public class Student {
        private String name;
    
        public Student() {
            this.name = "王五的媳妇";
        }
        ...snip...
    }
    

    启动测试:

    2021-01-30 17:35:45.709  INFO 59451 --- [           main] d.d.s.SpringbootHelloworldApplication    : Started SpringbootHelloworldApplication in 2.591 seconds (JVM running for 3.861)
    Student{name='王五的媳妇'}
    

    另,关于该注解解析相关源码分析,可以参考这篇文章

    7:@SpringBootConfiguration

    该注解源码如下:

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Configuration
    public @interface SpringBootConfiguration {
    
    	@AliasFor(annotation = Configuration.class)
    	boolean proxyBeanMethods() default true;
    
    }
    

    可以看到该注解仅仅是继承了@Configuration,因此只是更加明确了是使用在springboot中 java config配置类的注解,可以认为和@Configuration功能是完全相同的,所以该注解的含义就是这是一个springboot的java config的配置类

    8:@ComponentScan

    用来扫描被@Component以及其子注解,如@Controller,@Service,@Reporitory等,源码如下:

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    @Repeatable(ComponentScans.class)
    public @interface ComponentScan {
    }
    

    另外从源码中可以看到,继承了注解@Repeatable代表该注解在类上是可以重复定义的,因为该注解是定义扫描路径的,因此可能会定义多次,其中重复定义的ComponentScan注解会放到注解org.springframework.context.annotation.ComponentScans中,源码如下:

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    public @interface ComponentScans {
    
    	ComponentScan[] value();
    
    }
    

    9:@EnableAutoConfiguration

    该注解是springboot定义的注解,用于开启自动配置功能,是spring-boot-autoconfigure项目最核心的注解,其源码如下:

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @AutoConfigurationPackage
    @Import(AutoConfigurationImportSelector.class)
    public @interface EnableAutoConfiguration {
    }
    

    其自动配置功能,是通过其父注解来辅助实现的来分别看下这些注解。
    @AutoConfigurationPackage,该注解源码如下:

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @Import(AutoConfigurationPackages.Registrar.class)
    public @interface AutoConfigurationPackage {
    
    }
    

    其中@Import(AutoConfigurationPackages.Registrar.class)中的AutoConfigurationPackages.Registrar源码如下:

    org.springframework.boot.autoconfigure.AutoConfigurationPackages.Registrar
    static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
    
    	@Override
    	public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
    		register(registry, new PackageImport(metadata).getPackageName());
    	}
    
    	@Override
    	public Set<Object> determineImports(AnnotationMetadata metadata) {
    		return Collections.singleton(new PackageImport(metadata));
    	}
    
    }
    

    实现了ImportBeanDefinitionRegistrar因此可以将bean注入到springIOC容器中的功能,完成注册的源码如下:

    public static void register(BeanDefinitionRegistry registry, String... packageNames) {
        // 正常进else,因此这里只看else
    	if (registry.containsBeanDefinition(BEAN)) {
    		BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
    		ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues();
    		constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames));
    	}
    	else {
    		GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
    		beanDefinition.setBeanClass(BasePackages.class);
    		// 将要扫描的包路径注册进来,后续springboot就可以对该路径进行扫描了
    		beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
    		beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    		registry.registerBeanDefinition(BEAN, beanDefinition);
    	}
    }
    

    debug如下:
    在这里插入图片描述
    这里的包路径就是我的启动类所在的包路径,如下:
    在这里插入图片描述
    这样,springboot就有了拥有需要扫描的路径信息的spring bean了,后续的工作也就可以从这里展开了。
    @Import(AutoConfigurationImportSelector.class),源码如下:

    public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
    		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
    }
    

    其中DeferredImportSelector实现了接口org.springframework.context.annotation.ImportSelector,因此AutoConfigurationImportSelector就是一个importselector,用来引入标注了@Configuration注解的java config类。接下来我们再重点看下这个类。
    该类是用在org.springframework.boot.autoconfigure.EnableAutoConfiguration定义,真正完成资源的导入,下面我们来看下。
    首先启动后会执行到方法org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#process,源码如下:

    @Override
    public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
    	Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
    			() -> String.format("Only %s implementations are supported, got %s",
    					AutoConfigurationImportSelector.class.getSimpleName(),
    					deferredImportSelector.getClass().getName()));
    	// 获取配置
    	AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
    			.getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
    	this.autoConfigurationEntries.add(autoConfigurationEntry);
    	for (String importClassName : autoConfigurationEntry.getConfigurations()) {
    		this.entries.putIfAbsent(importClassName, annotationMetadata);
    	}
    }
    

    继续看方法getAutoConfigurationEntry,源码如下:

    protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
    		AnnotationMetadata annotationMetadata) {
    	if (!isEnabled(annotationMetadata)) {
    		return EMPTY_ENTRY;
    	}
    	AnnotationAttributes attributes = getAttributes(annotationMetadata);
    	// <getAutoConfigurationEntry_1>获取候选的配置
    	List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    	configurations = removeDuplicates(configurations);
    	Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    	checkExcludedClasses(configurations, exclusions);
    	configurations.removeAll(exclusions);
    	// <getAutoConfigurationEntry_2>
    	configurations = filter(configurations, autoConfigurationMetadata);
    	fireAutoConfigurationImportEvents(configurations, exclusions);
    	return new AutoConfigurationEntry(configurations, exclusions);
    }
    

    我们先来看<getAutoConfigurationEntry_2>,因为并非所有的自动配置类都需要进行自动配置,因此就需要过滤掉那些不需要自动配置的类,使用的是AutoConfigurationImportSelector相关的API,详细可以参考这里
    继续看方法<getAutoConfigurationEntry_1>:

    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    	// getSpringFactoriesLoaderFactoryClass() -> interface org.springframework.boot.autoconfigure.EnableAutoConfiguration
    	// 该代码就是获取META-INF/spring.factories文件夹下获取key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的所有的接口的实现类,其实这些类就是springboot已经实现的自动配置的实现类了
    	List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
    			getBeanClassLoader());
    	Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
    			+ "are using a custom packaging, make sure that file is correct.");
    	return configurations;
    }
    

    结合注释查看后,我们先来看下META-INF/spring.factories文件的对应的配置比较多,这里就截取部分

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    ...
    org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
    ...
    

    可以看到这里定义里所有的自动配置的类,其中的org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,就是web框架springmvc的自动配置类,我们看下List<String> configurations的结果:
    在这里插入图片描述
    我们可以进一步看下WebMvcAutoConfiguration代码,自动配置类位置,debug信息对比看下,如图:
    在这里插入图片描述
    其实到这里基于java config的自动配置类都已经成功获取了,但是,我们继续来看下程序到底是怎么执行到这里的,我们来看下方法的调用链:
    在这里插入图片描述
    其中1处的代码可以从springboot的启动过程详细分析中找到,从而也就是可以找到我们通过SpringApplication.run(SpringbootHelloworldApplication.class, args);启动springboot程序如何最终调用到这里,完成自动配置了。

    展开全文

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,220
精华内容 488
关键字:

springboot中常用的注解

spring 订阅