Spring注解开发

栏目: Java · 发布时间: 6年前

  • 标注在类上,表明该类是一个配置类,相当于之前写的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;
    

以上所述就是小编给大家介绍的《Spring注解开发》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Just My Type

Just My Type

Simon Garfield / Profile Books / 2010-10-21 / GBP 14.99

What's your type? Suddenly everyone's obsessed with fonts. Whether you're enraged by Ikea's Verdanagate, want to know what the Beach Boys have in common with easy Jet or why it's okay to like Comic Sa......一起来看看 《Just My Type》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

SHA 加密
SHA 加密

SHA 加密工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具