• Spring源码解析之基础应用(四)


    基于注解的容器配置

    spring允许我们用注解来代替XML配置,至于是注解更好还是XML更好视情况而定,一般开发人员喜欢使用注解来进行配置,因为这样更靠近代码;而运维更喜欢XML配置,来决定服务运行的环境,比如:数据库配置。spring不但允许两种不同配置风格的存在,甚至还能混合使用。

    谈到注解,@Autowired一定是spring应用最广泛的注解之一,一般我们是将@Autowired标注在字段上,但这个注解同样可以标注在具有多个参数的方法上,前提是这些参数能在容器内找到类型匹配的bean。

    MovieRecommender一共有3处使用了@Autowired,首先是在MovieFinder字段上标注,这是大家最熟悉的做法;其次在MovieRecommender(MovieCatalog movieCatalog)构造方法上标注,如果这个方法不标注@Autowired,spring默认会调用无参构造方法;最后在setCustomerPreferenceDao(CustomerPreferenceDao customerPreferenceDao)方法上标注,spring会传入这个方法所需要的参数bean。

    package org.example.beans;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    @Component
    public class MovieRecommender {
        @Autowired
        private MovieFinder movieFinder;
        private MovieCatalog movieCatalog;
        private CustomerPreferenceDao customerPreferenceDao;
    
        public MovieRecommender() {
            System.out.println("Construct MovieRecommender()");
        }
    
        @Autowired
        public MovieRecommender(MovieCatalog movieCatalog) {
            this.movieCatalog = movieCatalog;
            System.out.println("Construct MovieRecommender(MovieCatalog movieCatalog) ");
        }
    
        public MovieFinder getMovieFinder() {
            return movieFinder;
        }
    
        public MovieCatalog getMovieCatalog() {
            return movieCatalog;
        }
    
    
        public CustomerPreferenceDao getCustomerPreferenceDao() {
            return customerPreferenceDao;
        }
    
        @Autowired
        public void setCustomerPreferenceDao(CustomerPreferenceDao customerPreferenceDao) {
            this.customerPreferenceDao = customerPreferenceDao;
        }
    }
    
    package org.example.beans;
    
    import org.springframework.stereotype.Component;
    
    @Component
    public class MovieFinder {
    }
    
    package org.example.beans;
    
    import org.springframework.stereotype.Component;
    
    @Component
    public class MovieCatalog {
    }
    
    package org.example.beans;
    
    import org.springframework.stereotype.Component;
    
    @Component
    public class CustomerPreferenceDao {
    }
    

      

    测试用例:

        @Test
        public void test14() {
            AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(MyConfig5.class);
            MovieRecommender recommender = ac.getBean(MovieRecommender.class);
            System.out.println(recommender.getMovieFinder());
            System.out.println(recommender.getMovieCatalog());
            System.out.println(recommender.getCustomerPreferenceDao());
        }
    

      

    运行结果:

    Construct MovieRecommender(MovieCatalog movieCatalog) 
    org.example.beans.MovieFinder@35083305
    org.example.beans.MovieCatalog@8e0379d
    org.example.beans.CustomerPreferenceDao@341b80b2
    

      

    @Autowired还允许我们设置参数required,默认为true,当注入的时候找不到bean则会报错,我们可以设置required为false,当spring没有找到所需的bean,则会跳过标记了@Autowired的方法或字段。

    @Autowired也可以用于标记一个集合,当这个集合所描述的类型包含多个实现。比如下面的例子,Fruit存在Apple和Banana这两个实现,我们在FruitPlate类中分别用数组、List、Set、Map来存放Fruit元素,如果用@Autowired来标注这四个字段,spring容器会把所有实现Fruit的bean数组到这个集合,Map的key为beanName:

    package org.example.beans;
    
    public class Fruit {
    }
    
    package org.example.beans;
    
    import org.springframework.stereotype.Component;
    
    @Component
    public class Apple extends Fruit {
    }
    
    package org.example.beans;
    
    import org.springframework.stereotype.Component;
    
    @Component
    public class Banana extends Fruit {
    }
    
    package org.example.beans;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import java.util.List;
    import java.util.Map;
    import java.util.Set;
    
    @Component
    public class FruitPlate {
        @Autowired
        private Fruit[] fruits;
        @Autowired
        private List<Fruit> fruitList;
        @Autowired
        private Set<Fruit> fruitSet;
        @Autowired
        private Map<String, Fruit> fruitMap;
    
        public Fruit[] getFruits() {
            return fruits;
        }
    
        public List<Fruit> getFruitList() {
            return fruitList;
        }
    
        public Set<Fruit> getFruitSet() {
            return fruitSet;
        }
    
        public Map<String, Fruit> getFruitMap() {
            return fruitMap;
        }
    }
    

      

    测试用例:

        @Test
        public void test15() {
            AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(MyConfig5.class);
            FruitPlate fruitPlate = ac.getBean(FruitPlate.class);
            System.out.println(Arrays.toString(fruitPlate.getFruits()));
            System.out.println(fruitPlate.getFruitList());
            System.out.println(fruitPlate.getFruitSet());
            System.out.println(fruitPlate.getFruitMap());
        }
    

      

    运行结果:

    [org.example.beans.Apple@429bd883, org.example.beans.Banana@4d49af10]
    [org.example.beans.Apple@429bd883, org.example.beans.Banana@4d49af10]
    [org.example.beans.Apple@429bd883, org.example.beans.Banana@4d49af10]
    {apple=org.example.beans.Apple@429bd883, banana=org.example.beans.Banana@4d49af10}
    

      

    由于按照类型注入时可能存在多个候选bean,我们可以在类或方法上标注@Primary注解,当通过类型注入bean时存在多个实现,优先使用标记注@Primary的bean:

    @Primary
    @Component
    public class Apple extends Fruit {
    }
    
    @Component
    public class FruitPlate {
    	……
    	@Autowired
        private Fruit primary;
    	public Fruit getPrimary() {
    		return primary;
    	}
    	……
    }
    

      

    当注入primary时,会优先选用Apple所对应的bean,大家可以在测试用例里面试一下,这里就不再另外试了。

    除了@Autowired,还有另外两个属性能帮助我们完成注入,分别是:JSR-250定义的注解@Resource 和JSR-330定义的注解@Inject。@Autowired和@Inject使用同一套逻辑进行注入,先根据类型进行bean的查找,如果存在多个bean,再根据字段名查找对应的bean。@Resource允许填写name和type,如果同时指定name和type,则注入的时候会找到beanName和类型都能匹配的上的bean进行注入;如果只指定了name,则根据beanName进行查找并注入,找不到则抛出异常;如果只指定了type,则根据类型进行查找,如果找不到或者找到多个,则抛出异常;如果name和type都不指定,则根据字段名查找,找不到再回退到根据类型查找。

    @Autowired虽然是一种装配技术,但不能与之前讲到的自动装配归为一类,即便这二者从表现上来看都会为我们注入所需要的bean,但它们所使用的技术不同,执行注入的时机不同,就像鸡蛋跟鸭蛋煮熟后味道差不多,但我们不能说鸡蛋就是鸭蛋。首先从使用方式上来看@Autowired和自动装配的不同,对于@Autowired我们需要在字段或方法上标记,spring才会为我们注入,这是基于注解的注入,而自动装配我们只要提供setter方法,在<bean/>标记装配类型,spring会根据装配类型来调用setter方法,这是基于XML的注入。当然,等到后续讲到spring源码时,笔者会证明@Autowired和自动装配的处理时机不同,执行逻辑不同。

  • 相关阅读:
    HDU 1301 Jungle Roads (最小生成树)
    POJ 1733 Parity game (并查集)
    HDU 3038 How Many Answers Are Wrong (并查集)
    CentOS用yum安装搭建LAMP
    Linux下php安装Redis扩展
    PHPExcel用法
    利用phpmailer类邮件发送
    Vim编辑器配置
    vhost文件设置
    ThinkPHP验证码类
  • 原文地址:https://www.cnblogs.com/beiluowuzheng/p/13833426.html
Copyright © 2020-2023  润新知