zoukankan      html  css  js  c++  java
  • spring 注解笔记

    1、@Configuration

    用于定义配置类,可替换xml配置文件,被注解的类内部包含有一个或多个被@Bean注解的方法,这些方法将会被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,并用于构建bean定义,初始化Spring容器。

    2、@Bean

    一般都在@Configuration指定下,替换xml中<bean>标签

    3、@Import

    将指定的Bean加入到IOC容器之中进行管理,有3种方式导入

    类型一 : 导入某个配置类

    类型二 : 导入某个ImportSelector接口实现类

    类型三 : 导入某个ImportBeanDefinitionRegistrar接口实现类

    3.1 导入配置类最简单,如下

    @Import(Student.class)

    作用就是new Student() 调用无参构造函数,其他都没做。

    @Import上面的使用方式属于静态的导入依赖,如果用动态导入呢,比如要导入很多类,你总不至于每个写class,100个呢?吃不消,所以动态解决比如都写在一个properties配置文件中,扫描到全部导入。

    这个时候就要用ImportSelector接口实现类

    3.2 ImportSelector接口实现类

    就只需要实现selectImports方法就行

    1 public interface ImportSelector {
    2     String[] selectImports(AnnotationMetadata var1);
    3 }

    只需要注意返回的数组是全名getName()

    1 public class MyImportSelector implements ImportSelector
    2 {
    3     @Override
    4     public String[] selectImports(AnnotationMetadata annotationMetadata)
    5     {
    6         return new String[]{Student.class.getName(), Address.class.getName()};
    7     }
    8 }
    1 @Configuration
    2 @Import(MyImportSelector.class)
    3 public class MainConfig
    4 {
    5 
    6 }

    @Import还是要导入自定的MyImportSelector

    Spring Boot就是这个原理用selectImports来扫描指定的文件导入

     1 public String[] selectImports(AnnotationMetadata annotationMetadata) 
     2 {
     3   if (!this.isEnabled(annotationMetadata)) {
     4       return NO_IMPORTS;
     5   } else {
     6       AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
     7       AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
     8       return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
     9   }
    10 }

    4、@Conditional

    spring boot中最常用的注解,是用来判断条件的。

    它的作用是按照一定的条件进行判断,满足条件给容器注册bean

    源码如下:

    1 @Target({ElementType.TYPE, ElementType.METHOD})
    2 @Retention(RetentionPolicy.RUNTIME)
    3 @Documented
    4 public @interface Conditional {
    5     Class<? extends Condition>[] value();
    6 }

    Condition是个接口,很明显填入的条件class必须实现Condition

    然后实现matches方法,根据返回值true和false来判断是否注入。true为注入。

    举例:

     1 @Configuration
     2 public class MyConfig
     3 {
     4     @Bean
     5     @Conditional(WindowsEnvironment.class)
     6     public Student student()
     7     {
     8         Student student = new Student();
     9         return student;
    10     }
    11 }
    12 
    13 public class WindowsEnvironment implements Condition
    14 {
    15     @Override
    16     public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata)
    17     {
    18         Environment environment = conditionContext.getEnvironment();
    19         System.out.println("environment:" + environment.getProperty("os.name"));
    20         return true;
    21     }
    22 }

    如果WindowsEnvironment的matches返回false,则不注册。

    4.1、@ConditionalOnClass

    继承于@Conditional

    当给定的类名在类路径上存在,则实例化当前Bean

    可以避免因为Class Not Found导致的编译异常了

    源码:

    1 @Target({ElementType.TYPE, ElementType.METHOD})
    2 @Retention(RetentionPolicy.RUNTIME)
    3 @Documented
    4 @Conditional({OnClassCondition.class})
    5 public @interface ConditionalOnClass {
    6     Class<?>[] value() default {};
    7 
    8     String[] name() default {};
    9 }

    既可以在Class上也可以在Method上。

     1 public class Curose
     2 {
     3     private String name;
     4 
     5     public String getName()
     6     {
     7         return name;
     8     }
     9 
    10     public void setName(String name)
    11     {
    12         this.name = name;
    13     }
    14 }
    15 
    16 @Configuration
    17 @ConditionalOnClass(value = Curose.class)
    18 public class MyConfig
    19 {
    20     @Bean
    21     public Person person()
    22     {
    23         return new Person();
    24     }
    25 
    26     @Bean
    27     public Student student()
    28     {
    29         Student student = new Student();
    30         return student;
    31     }
    32 }

    因为存在Curose类,所以@ConditionalOnClass 存在class则加载MyConfig配置。

    其中@ConditionalOnClass 可以写name,这个会更加便捷动态加载Class,其中name要写包名+类名。

    @ConditionalOnClass(name = "service.Curose")

    4.2、@ConditionalOnMissingClass

    当给定的类名在类路径上不存在,则实例化当前Bean

    4.3、@ConditionalOnBean

     当给定的在bean存在时,则实例化当前Bean

     源码如下:

     1 @Target({ElementType.TYPE, ElementType.METHOD})
     2 @Retention(RetentionPolicy.RUNTIME)
     3 @Documented
     4 @Conditional({OnBeanCondition.class})
     5 public @interface ConditionalOnBean {
     6     Class<?>[] value() default {};
     7 
     8     String[] type() default {};
     9 
    10     Class<? extends Annotation>[] annotation() default {};
    11 
    12     String[] name() default {};
    13 
    14     SearchStrategy search() default SearchStrategy.ALL;
    15 
    16     Class<?>[] parameterizedContainer() default {};
    17 }

    从上面的源代码中可以看出继承于@Conditional,判断是否加载bean逻辑都在OnBeanCondition.class中

    可以用于Type和Method

    例子:

    1、用于Type中既class上

    为了测试方便,我写了2个Configuration类MyConfig和MyConfig1

     1 @Configuration
     2 @ConditionalOnBean({Curose.class})
     3 public class MyConfig
     4 {
     5     @Bean
     6     public Student student()
     7     {
     8         Student student = new Student();
     9         return student;
    10     }
    11 }
    12 
    13 @Configuration
    14 public class MyConfig1
    15 {
    16     @Bean
    17     public Curose curose()
    18     {
    19         Curose curose = new Curose();
    20         return curose;
    21     }
    22 }

    主程序加载配置类

     1 public class ConditionalMain
     2 {
     3     public static void main(String[] args)
     4     {
     5         AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig1.class, MyConfig.class);
     6         String[] beanDefinitionNames = context.getBeanDefinitionNames();
     7         for (String name : beanDefinitionNames)
     8         {
     9             System.out.println(name);
    10         }
    11     }
    12 }

    AnnotationConfigApplicationContext 中加载的顺序注意下,我是先加载MyConfig1,再加载MyConfig,所以先会执行MyConfig1配置类再执行MyConfig

    在MyConfig上加入注解@ConditionalOnBean({Curose.class}),说明了如果Curose Bean类在Ioc容器中是否存在,如果存在则加载这配置类,如果不存在,则不会加载配置类,既不会执行以下代码。这也是我为啥MyConfig1配置类写在前面的原因,首先加载了

    MyConfig1配置,既将Curose类加入了容器中,ConditionalOnBean返回的是存在,既会加载MyConfig配置。

    运行结果:

    1 myConfig1
    2 myConfig
    3 curose
    4 student

    如果将上面的配置顺序换下,则不会加载MyConfig配置。

    new AnnotationConfigApplicationContext(MyConfig.class, MyConfig1.class);

    运行结果:

    1 myConfig1
    2 curose

    如果换成@ConditionalOnMissingBean({Curose.class}) 则正好相反,不存在加载配置,存在则不加载配置

    2、用于Method 方法上

    于加载类上的区别在于,类上不启作用则全部不会不执行,写在方法上则表示这个方法不会执行而已。

    例子:

     1 @Configuration
     2 public class MyConfig
     3 {
     4     @Bean
     5     public Person person()
     6     {
     7         return new Person();
     8     }
     9 
    10     @Bean
    11     @ConditionalOnBean({Curose.class})
    12     public Student student()
    13     {
    14         Student student = new Student();
    15         return student;
    16     }
    17 }

    加入了一个Person类

    执行结果:

    1 myConfig1
    2 myConfig
    3 curose
    4 person
    5 student

    全部都加载了

    如果MyConfig1和MyConfig顺序相反下则student不会加载。

    在方法上还有一个作用,如果是方法注入,如果没有这个注解必会抛出空指针异常,加入了则不会不执行就不会有异常,等待你加入即可

    例子:

     1 @Configuration
     2 public class MyConfig
     3 {
     4     @Bean
     5     public Student student(Person person)
     6     {
     7         String name = person.getName();
     8         Student student = new Student();
     9         return student;
    10     }
    11 }

    getName() 必然会出现空异常,因为Person类还没加载到Ioc容器中。

     1 @Configuration
     2 public class MyConfig
     3 {
     4     @Bean
     5     @ConditionalOnBean(Person.class)
     6     public Student student(Person person)
     7     {
     8         String name = person.getName();
     9         Student student = new Student();
    10         return student;
    11     }
    12 }

    加入了@ConditionalOnBean(Person.class) 后则Person不存在就不会执行了,等你哪天加载就执行,在Springboot 中很多应用就是这么干的,比如redis缓存。一开始创建项目就已经有RedisAutoConfiguration配置了,等你在pom.xml 加入spring-boot-starter-data-redis启动器后就会生效了。

     生效代码如下:

     1 @Configuration
     2 public class MyConfig
     3 {
     4     @Bean
     5     public Person person()
     6     {
     7         return new Person();
     8     }
     9 
    10     @Bean
    11     @ConditionalOnBean(Person.class)
    12     public Student student(Person person)
    13     {
    14         String name = person.getName();
    15         Student student = new Student();
    16         return student;
    17     }
    18 }

    如果被Person在放Student下面就不会生效了,说明加载顺序是从上至下的。

     1 @Configuration
     2 public class MyConfig
     3 {
     4     @Bean
     5     @ConditionalOnBean(Person.class)
     6     public Student student(Person person)
     7     {
     8         String name = person.getName();
     9         Student student = new Student();
    10         return student;
    11     }
    12 
    13     @Bean
    14     public Person person()
    15     {
    16         return new Person();
    17     }
    18 }

     以上student就不会加载到Ioc容器中。

    4.4、@ConditionalOnMissingBean

    当给定的在bean不存在时,则实例化当前Bean

    4.5 、@ConditionalOnProperty

    spring boot使用@ConditionalOnProperty注解来控制@Configuration是否生效

    源代码:

     1 @Retention(RetentionPolicy.RUNTIME)
     2 @Target({ElementType.TYPE, ElementType.METHOD})
     3 @Documented
     4 @Conditional({OnPropertyCondition.class})
     5 public @interface ConditionalOnProperty {
     6     String[] value() default {};  //数组,获取对应property名称的值,与name不可同时使用
     7 
     8     String prefix() default "";  //property名称的前缀
     9 
    10     String[] name() default {}; //数组,property完整名称或部分名称(可与prefix组合使用,组成完整的property名称),与value不可同时使用
    11 
    12     String havingValue() default "";  //可与name组合使用,比较获取到的属性值与havingValue给定的值是否相同,相同才加载配置
    13 
    14     boolean matchIfMissing() default false;  //缺少该property时是否可以加载。如果为true,没有该property也会正常加载;反之报错
    15 }

    例子:

    创建了TestConfig.class,并设置了ConditionalOnProperty prefix = "context.test"

    1 @Configuration
    2 @ConditionalOnProperty(prefix = "context.test", name = "name", havingValue = "true")
    3 public class TestConfig
    4 {
    5     @Bean
    6     public UserContext userContext(){
    7         return new UserContext();
    8     }
    9 }

    直接执行会报错,因为找不到Property中的context.test.name

    在yml配置中写了

    context.test.name: true

    就可以正常运行了。由name+havingValue进行绑定

    如果将true写成false就会失败。

    如果改写成value属性:

    @ConditionalOnProperty(prefix = "context.test", value = "name")

    也是返回true,加载配置。

     如果将配置去除,加上matchIfMissing = true也是能运行成功加载配置的。

    @ConditionalOnProperty(prefix = "context.test", value = "name", matchIfMissing = true)

    注解说明
    @ConditionalOnSingleCandidate 当给定类型的bean存在并且指定为Primary的给定类型存在时,返回true
    @ConditionalOnMissingBean 当给定的类型、类名、注解、昵称在beanFactory中不存在时返回true.各类型间是or的关系
    @ConditionalOnBean 与上面相反,要求bean存在
    @ConditionalOnMissingClass 当给定的类名在类路径上不存在时返回true,各类型间是and的关系
    @ConditionalOnClass 与上面相反,要求类存在
    @ConditionalOnCloudPlatform 当所配置的CloudPlatform为激活时返回true
    @ConditionalOnExpression spel表达式执行为true
    @ConditionalOnJava 运行时的java版本号是否包含给定的版本号.如果包含,返回匹配,否则,返回不匹配
    @ConditionalOnProperty 要求配置属性匹配条件
    @ConditionalOnJndi 给定的jndi的Location 必须存在一个.否则,返回不匹配
    @ConditionalOnNotWebApplication web环境不存在时
    @ConditionalOnWebApplication web环境存在时
    @ConditionalOnResource 要求制定的资源存在


  • 相关阅读:
    图像、视频等文件类型(拓展名)
    图像、视频等文件类型(拓展名)
    Mstar 编译器的搭建
    microsoft windows network 不允许一个用户使用一个以上用户名与服务器或共享资源的多重连接
    Ubuntu 14.04报“leaking memory”错误
    linux下创建与删除用户详细步骤 ***
    GX 编译器 的搭建
    VMware网络模式介绍
    ubuntu 源更新(sources.list)
    目录的执行权限
  • 原文地址:https://www.cnblogs.com/zjtao/p/12217130.html
Copyright © 2011-2022 走看看