转载

Spring注解开发

  • 标注在类上,表明该类是一个配置类,相当于之前写的spring的xml文件,通常与 @Bean 结合使用
  • 该注解同时拥有了 @Component 的作用,将当前类注入ioc容器
    • 其中有一个 value 属性,指定注入ioc容器的名称,默认是类名首字母小写
    • 源码如下:
      @Target(ElementType.TYPE)
      @Retention(RetentionPolicy.RUNTIME)
      @Documented
      @Component
      public @interface Configuration {}
      

@Bean

  • 在配置类中使用该注解
  • 标注在方法上,默认将方法的返回对象注入到ioc容器,名称默认使用的是方法名
    /**
     * 配置类
     */
    @Configuration(value = "MyConfiguration")
    public class MyConfiguration{
        /**
         * 注入一个User对象,ioc容器中的name是user,类型是User类型
         */
        @Bean
        public User user(){
            return new User(1,"name");
        }
    }
    

属性

  • name :指定注入ioc容器中的名称
  • value :同name
  • autowireCandidate :是否能够自动注入,默认是true,如果指定了属性为false,那么不能使用 @Autowired 或者 @Resource 自动注入
  • initMethod :指定初始化方法,在构造方法之后执行
  • destroyMethod :指定销毁方法,在容器关闭的时候执行

实例

/**
 * 配置类
 */
@Configuration(value = "MyConfiguration")
public class MyConfiguration{

    /**
     * 注入一个User对象,ioc容器中的name是user,类型是User类型
     * init是User类中的init方法,destory是User类中的destory方法
     */
    @Bean(initMethod = "init",destroyMethod = "destory")
    public User user1(){
        return new User(1,"name");
    }
}

/**
* User类
*/
public class Userimplements Serializable{
    private Integer id;
    private String name;

    public User(String name){
        this.name = name;
    }

    public User(Integer id, String name){
        System.out.println("执行构造方法");
        this.id = id;
        this.name = name;
    }

    public void init(){
        System.out.println("初始化方法");
    }

    public void destory(){
        System.out.println("销毁方法");
    }
}

@Scope

  • 指定对象的作用域名,标注在类上或者方法上,默认是单实例

四大作用域

singleton
prototype
request
session
/**
    * 指定多实例,每次用到都会调用
    */
   @Bean(initMethod = "init",destroyMethod = "destory")
   @Scope(value = "prototype")
   public User user1(){
       return new User(1,"name");
   }

@Conditional

  • 标注在方法或者在类上,只有在满足其中的匹配条件才会将对象注入到ioc容器中。
  • 只有一个属性value,是一个 Condition 数组,要向实现相应的功能,可以自定义一个类,实现 Condition 这个接口即可。
  • 这个注解在SpringBoot中将会有很多的扩展,这里就不多说了。

    /**
         * 指定多实例,每次用到都会调用
         * @Conditional 只有里面全部都匹配才会正常注入到容器中
         */
        @Bean(initMethod = "init",destroyMethod = "destory")
        @Conditional(value = {FirstCondition.class})
        public User user1(){
            return new User(1,"name");
        }
    
  • 下面看看 FirstCondition 这个类具体实现

    • 只需要实现接口中的 matches 方法即可,返回true表示符合条件,否则不满足条件,只有满足条件才会注入到ioc容器中
      /**
       * 自定义的条件判断,实现Condition接口
       */
      public class FirstConditionimplements Condition{
      
          /**
           * 如果返回true表示符合条件,反之不符合条件
           * @param context ConditionContext对象,可以获取上下文的信息
           * @param metadata AnnotatedTypeMetadata对象,可以获取标注在该方法上面的注解信息
           * @return
           */
          @Override
          public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata){
              //获取Environment,用来获取运行环境中的一些变量
              Environment environment = context.getEnvironment();
              //获取在properties文件中配置的参数,表示是否注入相关属性
              Boolean isAutowired = environment.<Boolean>getProperty("isAutowired", Boolean.class);
              return isAutowired;
          }
      }
      

ConditionContext

  • 主要的功能是获取上下文的对象,比如BeanFactory
    public interface ConditionContext{
    
    	/**
    	* 获取 BeanDefinitionRegistry,可以自己手动注册对象到ioc容器中
        */
    	BeanDefinitionRegistrygetRegistry();
    
    	/**
    	 * 获取BeanFacotory,操作ioc容器,比如获取对应的Bean,判断ioc中是否已经注入   
    	 */
    	@Nullable
    	ConfigurableListableBeanFactorygetBeanFactory();
    
    	/**
    	 * 返回当前的运行环境,可以获取运行环境中的一下参数,或者一些配置文件中的数据
    	 */
    	EnvironmentgetEnvironment();
    
    	/**
    	 * 获取资源加载器
    	 */
    	ResourceLoadergetResourceLoader();
    
    	/**
    	 * 获取类加载器 
    	 */
    	@Nullable
    	ClassLoadergetClassLoader();
    }
    

AnnotatedTypeMetadata

  • 主要的作用就是获取标注了 @Conditional 这个注解的方法上的注解和对应的参数等信息
    public interface AnnotatedTypeMetadata{
    
    	/**
    	* 判断方法上是否有对应的注解 
        * @param annotationName 注解类的全类名,getName()
        */
    	boolean isAnnotated(String annotationName);
    
    	/**
    	*  获取对应注解的全部属性的值,key是属性,value是属性的值
        * @param annotationName 注解类的全类名,getName()
        */
    	@Nullable
    	Map<String, Object>getAnnotationAttributes(String annotationName);
    
    	@Nullable
    	Map<String, Object>getAnnotationAttributes(String annotationName,boolean classValuesAsString);
    
    	@Nullable
    	MultiValueMap<String, Object>getAllAnnotationAttributes(String annotationName);
    
    	@Nullable
    	MultiValueMap<String, Object>getAllAnnotationAttributes(String annotationName,boolean classValuesAsString);
    }
    

@PostConstruct

  • 标注在方法上,用来在类加载并且属性赋值之后调用,通常用来初始化,和 @Bean 中的 init-menthd 指定的作用相同
    @PostConstruct
        public void init(){
            System.out.println("初始化方法");
        }
    

@PreDestroy

  • 标注在方法上,容器销毁执行,相当于 @Bean 中的 destroy-method 属性
    @PreDestroy
        public void destory(){
            System.out.println("销毁方法");
        }
    

@Import

  • 用来形式化的注入,主要形式有:
    • 直接导入类
    • 导入配置类
    • 指定ImportSelector
    • 使用ImportBeanDefinitionRegistrar手动注册

直接导入类

  • 直接在 value 属性中指定需要导入的类即可,如下:
    @Configuration(value = "MyConfiguration")
    @Import(value = {Person.class})
    public class MyConfiguration{}
    

导入配置类

  • 新建一个配置类,但是不用 @Configuration 标注,使用 @Import 在另外一个配置类上引入即可

    /**
     * 这是一个配置,但是并没有使用@Configuration这个注解,因此不会生效
     */
    public class SecondConfiguration{
        @Bean
        public Person person(){
            return new Person();
        }
    }
    
  • 在另外一个配置类使用 @Import 注解引入上面的配置类,如下:

    @Configuration(value = "MyConfiguration")
    @Import(value = {SecondConfiguration.class})
    public class MyConfiguration{}
    

指定ImportSelector

  • 使用ImportSelector需要自定义一个实现类,如下:

    /**
     * 自定义Selector,需要实现ImportSelector
     */
    public class FirstSelectorimplements ImportSelector{
        /**
         * 筛选逻辑,返回的是String数组(需要注入到容器中的类的全类名)
         * @param importingClassMetadata AnnotationMetadata对象,对标注了@Import这个注解的类中的所有注解信息,比如获取标注指定注解的方法
         * @return 返回的是需要注入的字符串数组(类的全类名)
         */
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            //获取@Import标注的类中被@Bean标注的方法元数据
            Set<MethodMetadata> annotatedMethods = importingClassMetadata.getAnnotatedMethods(Bean.class.getName());
            annotatedMethods.forEach(o->{
                System.out.println(o.getMethodName());
            });
            //将Person类返回去,那么将会自动注入Person
            return new String[]{Person.class.getName()};
        }
    }
    
  • 在配置类上使用 @Import 注解引入即可,如下:

    @Configuration
    @Import(value = {FirstSelector.class})
    public class MyConfiguration{}
    

AnnotationMetadata

public interface AnnotationMetadataextends ClassMetadata,AnnotatedTypeMetadata{

	//拿到Class上标注的所有注解,依赖于Class#getAnnotations
	Set<String>getAnnotationTypes();

	// 拿到所有的元注解信息AnnotatedElementUtils#getMetaAnnotationTypes
	//annotationName:注解类型的全类名
	Set<String>getMetaAnnotationTypes(String annotationName);
	// 是否包含指定注解 (annotationName:全类名)
	boolean hasAnnotation(String annotationName);
	//这个厉害了,依赖于AnnotatedElementUtils#hasMetaAnnotationTypes
	boolean hasMetaAnnotation(String metaAnnotationName);
	// 类里面只有有一个方法标注有指定注解,就返回true
	//getDeclaredMethods获得所有方法, AnnotatedElementUtils.isAnnotated是否标注有指定注解
	boolean hasAnnotatedMethods(String annotationName);
	// 注意返回的是MethodMetadata 原理基本同上
	// .getDeclaredMethods和AnnotatedElementUtils.isAnnotated 最后吧Method转为MethodMetadata
	Set<MethodMetadata>getAnnotatedMethods(String annotationName);
}

使用ImportBeanDefinitionRegistrar手动注册

  • 需要自定义一个类实现 ImportBeanDefinitionRegistrar ,如下:

    /**
     * 自定义的FirstBeanDefinitionRegistrar,需要实现ImportBeanDefinitionRegistrar
     */
    public class FirstBeanDefinitionRegistrarimplements ImportBeanDefinitionRegistrar{
        /**
         * 自己手动注册Bean到ioc容器中
         * @param importingClassMetadata 获取@Import标注的类上的注解信息,比如获取被指定注解标注的方法信息
         * @param registry  注册中心,可以获取指定bean的信息和手动注册bean
         */
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry){
            //如果容器中没有Person这个Bean,就创建一个
            if (!registry.containsBeanDefinition(Person.class.getName())){
                GenericBeanDefinition beanDefinition=new GenericBeanDefinition();
                beanDefinition.setBeanClass(Person.class);
                //手动注册
                registry.registerBeanDefinition("person",beanDefinition);
            }
        }
    }
    
  • 在配置类上使用 @Import 注解引入即可,如下:

    @Configuration
    @Import(value = {FirstBeanDefinitionRegistrar.class})
    public class MyConfiguration{}
    

@Primary

  • 在spring 中使用注解,常使用@Autowired, 默认是根据类型Type来自动注入的。但有些特殊情况,对同一个接口,可能会有几种不同的实现类,而默认只会采取其中一种的情况下 @Primary 的作用就出来了。
  • 有两种使用方式,一种是结合 @Bean ,另外一种是结合 @Service,@Component,@Controller.....
    @Bean
    @Primary
    public User user1(){
        return new User(1,"user1");
    }
    
    //第二种
    @Primary
    @Component
    public class Person{
        private String name;
        private Integer age;
    }
    

@Autowired

  • 标注在方法和属性上,用来自动为成员变量赋值
  • 默认先根据属性的类型从ioc容器中查找,如果ioc容器中存在多个类型相同的属性,将会根据 变量名 从ioc容器中查找。
    @Controller
    public class UserController{
        @Autowired
        private UserService userService;
    

属性

  • required :指定该属性是否是必须的,默认为true,表示一定要为属性赋值,如果ioc容器中没有对应的Bean,那个将会报错,如果为false,会先从ioc容器中查找对应的Bean,如果存在就进行赋值,不存在就不赋值,不会报错。

@Qualifier

  • @Autowired 结合使用,用来从容器中注入指定名字的Bean
  • 使用场景:容器中存在多个类型相同的Bean,那么此时仅仅使用 @Autowired 就不太适用了,此时就要结合该注解,指定需要注入的name。(当然除了 @Autowired 还是可以根据成员变量的名称进行注入的)
    @Controller
    public class UserController{
        @Autowired
        @Qualifier(value = "userService")
        private UserService userService;
    

@PropertySource

  • 从配置文件中读取相关配置注入到指定的成员属性中
    @Component
    @PropertySource(value = {"classpath:user.properties"})
    public class Userimplements Serializable{
        private String name;
        private Integer age;
    
        public User(){}
    
        public User(String name, Integer age){
            this.name = name;
            this.age = age;
        }
    

属性

value
ignoreResourceNotFound

@Value()

  • 有三个典型的使用场景:
    • 获取配置文件中对应的值,为指定属性赋值
    • 使用指定的值为属性赋值
    • 通过表达式计算得到的值为属性赋值

获取配置文件中的值为属性赋值

  • 使用 ${} 的方式获取配置文件中设置的值
    @Value("${name}")
    private String name;
    

使用指定的值

  • 其中的 value 属性可以是自己随便指定的值,如下:
    @Value("陈加兵")
    private String name;
    
原文  https://chenjiabing666.github.io/2019/06/23/Spring注解开发/
正文到此结束
Loading...