暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Spring注解驱动开发(三)-属性赋值和自动装配

rocklei123的技术点滴 2018-12-27
359

title: Spring注解驱动开发(三)-属性赋值和自动装配 author: rocklei123 tags:

  • Annotation

  • Spring

  • Java categories: Spring date: 2018-12-01 21:45:44


1.属性赋值

1.1 @Value

使用@Value注解赋值;

  1. 基本数值

  2. 可以写Spring的EL表达式; #{}

  3. 可以写${};取出配置文件【properties】中的值(在运行环境变量里面的值)

1.2 @PropertySource

  • 使用@PropertySource读取外部配置文件中的k/v保存到运行的环境变量中;

  • 加载完外部的配置文件以后使用${}取出配置文件的值

  • @PropertySource(value={"classpath:/person.properties"}) 数组类型,可以支持一次性引入多个文件。

  • 另外@PropertySource 是一个可重复的注解,可以使用@PropertySources注解引入多个@PropertySource标注的properties配置文件

  1. @Target({ElementType.TYPE})

  2. @Retention(RetentionPolicy.RUNTIME)

  3. @Documented

  4. public @interface PropertySources {

  5.    PropertySource[] value();

  6. }

测试代码如下:

  1. //实体类信息

  2. package com.rocklei123.bean;


  3. import org.springframework.beans.factory.annotation.Value;


  4. /**

  5. * @ClassName: Person

  6. * @Author: rocklei123

  7. * @Date: 2018/12/1 21:27

  8. * @Description: TODO

  9. * @Version 1.0

  10. */

  11. public class Person {

  12.    @Value(value = "张三")

  13.    private String name;

  14.    @Value(value = "#{20-2}")

  15.    private Integer age;


  16.    @Value(value = "${person.nickName}")

  17.    private String nickName;


  18.    public Person() {

  19.        System.out.println("Person默认构造方法");

  20.    }


  21.    @Override

  22.    public String toString() {

  23.        return "Person{" +

  24.                "name='" + name + '\'' +

  25.                ", age=" + age +

  26.                ", nickName='" + nickName + '\'' +

  27.                '}';

  28.    }

  29. }


  30. //配置类信息

  31. package com.rocklei123.config;


  32. import com.rocklei123.bean.Person;

  33. import org.springframework.context.annotation.*;


  34. @PropertySource(value = {"classpath:/person.properties"})

  35. @Configuration

  36. public class MainConfigOfPropertyValues {


  37.    @Bean

  38.    public Person person() {

  39.        return new Person();

  40.    }

  41. }

  42. //测试类信息

  43. public class IocTestOfPropertyValues {

  44.    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfPropertyValues.class);


  45.    public void printBean(ApplicationContext applicationContext) {

  46.        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();

  47.        for (String beanDefinitionName : beanDefinitionNames) {

  48.            Object o = applicationContext.getBean(beanDefinitionName);

  49.            System.out.println(o);

  50.        }

  51.    }


  52.    @Test

  53.    public void testValue() {

  54.        printBean(applicationContext);

  55.        Environment environment = applicationContext.getEnvironment();

  56.        String nickName = environment.getProperty("person.nickName");

  57.        System.out.println("环境信息中property=" + nickName);

  58.    }

  59. }

测试结果:

  1. Person{name='张三', age=18, nickName='小李四'}

  2. 环境信息中property=小李四

2.自动装配

自动装配; Spring利用依赖注入(DI),完成对IOC容器中中各个组件的依赖关系赋值;

2.1 @Autowired、@Qualifier、@Primary

2.1.1 @Autowired

2.1.1.1 概念性总结

  • 概念:Spring 2.5 引入了 @Autowired 注释,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。 通过 @Autowired的使用来消除 set ,get方法。

  • 原理:其实在启动spring IOC容器时,容器自动装载了一个AutowiredAnnotationBeanPostProcessor后置处理器,当容器扫描到@Autowied、@Resource或@Inject时,就会在IoC容器自动查找需要的bean,并装配给该对象的属性。

  • 注意事项:

  • 1)、默认优先按照类型去容器中找对应的组件:applicationContext.getBean(BookDao.class);找到就赋值

  • 2)、如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查 applicationContext.getBean("bookDao")所以容器中有多个相同组件时,名称不应该乱起。

  • 3)、自动装配默认一定要将属性赋值好,没有就会报错;可以使用@Autowired(required=false);默认required=true

2.1.1.2 默认注入情况测试

默认注入情况:名字默认是类名首字母小写

  1. //service 层代码,自动注入dao层

  2. @Service

  3. public class BookService {

  4.    @Autowired

  5.    BookDao bookDao;


  6.    @Override

  7.    public String toString() {

  8.        return "BookService{" +

  9.                "bookDao=" + bookDao +

  10.                '}';

  11.    }

  12. }


  13. //dao层代码,加入一个默认名称区别

  14. @Repository

  15. public class BookDao {

  16.    private String nameFlag = "1";


  17.    public BookDao() {

  18.    }


  19.    public BookDao(String nameFlag) {

  20.        this.nameFlag = nameFlag;

  21.    }


  22.    public String getNameFlag() {

  23.        return nameFlag;

  24.    }


  25.    public void setNameFlag(String nameFlag) {

  26.        this.nameFlag = nameFlag;

  27.    }


  28.    @Override

  29.    public String toString() {

  30.        return "BookDao{" +

  31.                "nameFlag='" + nameFlag + '\'' +

  32.                '}';

  33.    }

  34. }


  35. //配置类,向容器中注入另一个bookDao,名称为BookDao2

  36. @ComponentScan(value = {"com.rocklei123.service", "com.rocklei123.controller", "com.rocklei123.repository"})

  37. @Configuration

  38. public class MainConfigOfAutowired {


  39.    @Bean(value = "bookDao2")

  40.    public BookDao bookDao2(BookDao bookDao) {

  41.        BookDao bookDao2 = new BookDao();

  42.        bookDao2.setNameFlag("2");

  43.        return bookDao2;

  44.    }

  45. }

  46. //测试类

  47. public class IocTestOfAutowired {

  48.    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAutowired.class);


  49.    @Test

  50.    public void testValue() {

  51.        BookService bean = applicationContext.getBean(BookService.class);

  52.        System.out.println(bean);

  53.    }

  54. }

测试结果:通过上文注意事项可知,Spring容器注入时会先通过类型找到两个BookDao类型的对象,第二次会通过 默认是类名首字母小写在查找一次。故Service层注入的BookDao对象应该为nameFlag = "1"; 的对象

  1. 信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@4aa8f0b4: startup date [Sat Dec 15 18:56:29 CST 2018]; root of context hierarchy

  2. BookService{bookDao=BookDao{nameFlag='1'}}

如果此时修改测试类使用IOC容器按类型获取BookDao此时,因为容器中有两个BookDao类型的对象,当打印时就会报错。

  1. @Test

  2. public void testValue() {

  3.    BookDao bean = applicationContext.getBean(BookDao.class);

  4.    System.out.println(bean);

  5. }


  6. //异常信息

  7. org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.rocklei123.repository.BookDao' available: expected single matching bean but found 2: bookDao,bookDao2

2.1.1.3 @Autowired(required=false)

自动装配默认一定要将属性赋值好,没有就会报错;可以使用@Autowired(required=false);

  1. @Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})

  2. @Retention(RetentionPolicy.RUNTIME)

  3. @Documented

  4. public @interface Autowired {

  5.    boolean required() default true;

  6. }

  7. 默认required true

测试用例

  1. //业务层代码,自动注入Dao层对象,required = false

  2. public class BookService {

  3.    @Qualifier(value = "bookDao2")

  4.    @Autowired(required = false)

  5.    BookDao bookDao;

  6. }

  7. //将BookDao不注入到容器中


  8. //虽然容器中不含有BookDao,通过测试类打印业务层代码时,也不会报错。

  9. @Test

  10. public void testValue() {

  11.    BookService bean = applicationContext.getBean(BookService.class);

  12.    System.out.println(bean);

  13. }


  14. //测试结果

  15. BookService{bookDao=null}

2.1.1.4 @Autowired:构造器,参数,方法,属性

@Autowired:构造器,参数,方法,属性;都是从容器中获取参数组件的值

默认情况说明:

  • @Bean标注的方法创建对象的时候,方法参数的值从容器中获取

  • 默认加在ioc容器中的组件,容器启动会调用无参构造器创建对象,再进行初始化赋值等操作

@Autowired标注说明:

  • 1)、[标注在方法位置]:@Bean+方法参数;参数从容器中获取;默认不写@Autowired效果是一样的;都能自动装配

  • 2)、[标在构造器上]:如果组件只有一个有参构造器,这个有参构造器的@Autowired可以省略,参数位置的组件还是可以自动从容器中获取.

  • 3)、放在参数位置:

2.1.2 @Qualifier

@Qualifier(“XXX”) Spring的Bean注入配置注解,该注解指定注入的Bean的名称,Spring框架使用byName方式寻找合格的bean,这样就消除了byType方式产生的歧义。

@Qualifier("bookDao"):使用@Qualifier指定需要装配的组件的id,而不是使用属性名。@Qualifier需要和 @Autowired一起使用。

  1. public class BookService {

  2.    @Qualifier(value = "bookDao2")  //指定装配组件的Id为bookDao2

  3.    @Autowired

  4.    BookDao bookDao;

  5. }

  6. //测试结果

  7. BookService{bookDao=BookDao{nameFlag='2'}}

2.1.3 @Primary

@Primary:让Spring进行自动装配的时候,默认使用首选的bean;也可以继续使用@Qualifier指定需要装配的bean的名字。

通过下面的示例也可以看出@Primary和@Qualifier 在一定程度上有所冲突,如果我们service层代码使用@Qualifier指定注入名称,即使在相同类型的其他Bean 上标注@Primary,Spring注入Service层的也是根据@Qualifier标注注入的。

  1. @Service

  2. public class BookService {


  3.    @Qualifier(value = "bookDao")

  4.    @Autowired(required = false)

  5.    BookDao bookDao;


  6.    @Override

  7.    public String toString() {

  8.        return "BookService{" +

  9.                "bookDao=" + bookDao +

  10.                '}';

  11.    }

  12. }


  13. @ComponentScan(value = {"com.rocklei123.service", "com.rocklei123.controller", "com.rocklei123.repository"})

  14. @Configuration

  15. public class MainConfigOfAutowired {


  16.    @Primary

  17.    @Bean(value = "bookDao2")

  18.    public BookDao bookDao2(BookDao bookDao) {

  19.        BookDao bookDao2 = new BookDao();

  20.        bookDao2.setNameFlag("2");

  21.        return bookDao2;

  22.    }

  23. }


  24. //测试代码

  25. public class IocTestOfAutowired {

  26.    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAutowired.class);


  27.    public void printBean(ApplicationContext applicationContext) {

  28.        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();

  29.        for (String beanDefinitionName : beanDefinitionNames) {

  30.            Object o = applicationContext.getBean(beanDefinitionName);

  31.            System.out.println(o);

  32.        }

  33.    }


  34.    @Test

  35.    public void testValue() {

  36.        printBean(applicationContext);

  37.        System.out.println("-----------------------------------");

  38.        BookService bean = applicationContext.getBean(BookService.class);

  39.        System.out.println(bean);

  40.    }

  41. }

测试结果:可以看出Spring容器加载了两个BookDao类型的bean,向Serveice层注入的是 @Qualifier(value = "bookDao")标注的。

  1. com.rocklei123.config.MainConfigOfAutowired$$EnhancerBySpringCGLIB$$7d34398f@37e547da

  2. BookService{bookDao=BookDao{nameFlag='1'}}

  3. com.rocklei123.controller.BookController@5db45159

  4. BookDao{nameFlag='1'}

  5. BookDao{nameFlag='2'}

  6. -----------------------------------

  7. BookService{bookDao=BookDao{nameFlag='1'}}

2.2 @Resource(JSR250)和@Inject(JSR330)

Spring还支持使用@Resource(JSR250)和@Inject(JSR330)[java规范的注解]

2.2.1 @Resource:

  • 可以和@Autowired一样实现自动装配功能;默认是按照组件名称进行装配的;

  • 没有能支持@Primary功能

  • 没有支持@Autowired(reqiured=false);

  1. @Service

  2. public class BookService {

  3.    @Resource(name = "bookDao2")

  4.    BookDao bookDao;

  5. }

2.2.2 @Inject:

@Inject支持构造函数、方法和字段注解,也可能使用于静态实例成员。可注解成员可以是任意修饰符(private,package-private,protected,public)。注入顺序:构造函数、字段,然后是方法。父类的字段和方法注入优先于子类的字段和方法,同一类中的字段和方法是没有顺序的。

  • 需要导入javax.inject的包,和Autowired的功能一样。没有required=false的功能;但是支持@Primary

  1. <!-- https://mvnrepository.com/artifact/javax.inject/javax.inject -->

  2. <dependency>

  3.    <groupId>javax.inject</groupId>

  4.    <artifactId>javax.inject</artifactId>

  5.    <version>1</version>

  6. </dependency>

2.3 @Autowired 和 @Resource、@Inject 的区别

相同点

  1. @Autowired和@Inject基本是一样的,因为两者都是使用AutowiredAnnotationBeanPostProcessor来处理依赖注入。但是@Resource是个例外,它使用的是CommonAnnotationBeanPostProcessor来处理依赖注入。当然,两者都是BeanPostProcessor。

  2. 三个注解都能实现自动装配功能

不同点

  1. 注解定义不同

  • @Autowired:Spring定义的,脱离Spring框架就没有了;

  • @Resource、@Inject都是java规范,其他IOC框架也会支持。

  1. 默认注入方式不同

  • @Autowired和@Inject: 默认 autowired by type,可以通过@Qualifier 显式指定;

  • @Resource:默认 autowired by field name,如果 autowired by field name失败,会退化为 autowired by type;可以 通过@Qualifier 显式指定 autowired by qualifier name;如果 autowired by qualifier name失败,会退化为 autowired by field name。

  1. 功能性差异

  • @Resource 没有能支持@Primary功能;没有支持@Autowired(reqiured=false)功能;

  • @Inject 虽然支持@Primary功能功能,但是不支持@Autowired(reqiured=false)功能;

  • @Autowired支持@Primary功能,@Autowired(reqiured=false)功能;

2.4 自定义组件注入Spring容器底层的一些组件

2.4.1 xxxAware介绍

自定义组件实现xxxAware接口,在创建对象的时候,会调用接口规定的方法注入相关组件;xxxxAware接口把Spring底层一些组件注入到自定义的Bean中;

测试代码:

  1. package com.rocklei123.bean;


  2. import org.springframework.beans.BeansException;

  3. import org.springframework.beans.factory.BeanNameAware;

  4. import org.springframework.beans.factory.config.EmbeddedValueResolver;

  5. import org.springframework.context.ApplicationContext;

  6. import org.springframework.context.ApplicationContextAware;

  7. import org.springframework.context.EmbeddedValueResolverAware;

  8. import org.springframework.context.EnvironmentAware;

  9. import org.springframework.core.env.Environment;

  10. import org.springframework.stereotype.Component;

  11. import org.springframework.util.StringValueResolver;


  12. /**

  13. * @ClassName: Apple

  14. * @Author: rocklei123

  15. * @Date: 2018/12/15 20:46

  16. * @Description: 自定义组件注入Spring容器底层的一些组件

  17. * @Version 1.0

  18. */

  19. @Component

  20. public class Apple implements ApplicationContextAware, BeanNameAware, EnvironmentAware, EmbeddedValueResolverAware {

  21.    private ApplicationContext applicationContext;


  22.    @Override

  23.    public void setBeanName(String s) {

  24.        System.out.println("当前Bean的名称:" + s);

  25.    }


  26.    @Override

  27.    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

  28.        System.out.println("传入的IOC:" + applicationContext);

  29.        this.applicationContext = applicationContext;

  30.    }


  31.    @Override

  32.    public void setEnvironment(Environment environment) {

  33.        System.out.println("当前person.properties文件中的昵称名:" + environment.getProperty("person.nickName"));

  34.    }


  35.    @Override

  36.    public void setEmbeddedValueResolver(StringValueResolver stringValueResolver) {

  37.        String s = stringValueResolver.resolveStringValue("我的操作系统类型${os.name},我的年龄#{26+1}");

  38.        System.out.println(s);

  39.    }

  40. }


  41. //配置类

  42. @PropertySource(value = {"classpath:/person.properties"})

  43. @ComponentScan(value = {"com.rocklei123.bean", "com.rocklei123.service", "com.rocklei123.controller", "com.rocklei123.repository"})

  44. @Configuration

  45. public class MainConfigOfAutowired {


  46.    @Primary

  47.    @Bean(value = "bookDao2")

  48.    public BookDao bookDao2(BookDao bookDao) {

  49.        BookDao bookDao2 = new BookDao();

  50.        bookDao2.setNameFlag("2");

  51.        return bookDao2;

  52.    }

  53. }

  54. //测试结果

  55. 当前Bean的名称:apple

  56. 当前person.properties文件中的昵称名:小李四

  57. 我的操作系统类型Windows 10,我的年龄27

  58. 传入的IOC:org.springframework.context.annotation.AnnotationConfigApplicationContext@1e81f4dc: startup date [Sat Dec 15 20:57:18 CST 2018]; root of context hierarchy

2.4.2 xxxAware底层原理

xxxAware原理:功能使用xxxProcessor,通过后置处理器功能实现; 如: ApplicationContextAware==》ApplicationContextAwareProcessor;

以ApplicationContextAwareProcessor为例源码:

  1. package org.springframework.context.support;


  2. class ApplicationContextAwareProcessor implements BeanPostProcessor {

  3.    private final ConfigurableApplicationContext applicationContext;

  4.    private final StringValueResolver embeddedValueResolver;


  5.    public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {

  6.        AccessControlContext acc = null;

  7.        if (System.getSecurityManager() != null && (bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware || bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware || bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {

  8.            acc = this.applicationContext.getBeanFactory().getAccessControlContext();

  9.        }


  10.        if (acc != null) {

  11.            AccessController.doPrivileged(new PrivilegedAction<Object>() {

  12.                public Object run() {

  13.                    ApplicationContextAwareProcessor.this.invokeAwareInterfaces(bean);

  14.                    return null;

  15.                }

  16.            }, acc);

  17.        } else {

  18.            this.invokeAwareInterfaces(bean);

  19.        }


  20.        return bean;

  21.    }


  22.    private void invokeAwareInterfaces(Object bean) {

  23.        if (bean instanceof Aware) {

  24.            if (bean instanceof EnvironmentAware) {

  25.                ((EnvironmentAware)bean).setEnvironment(this.applicationContext.getEnvironment());

  26.            }


  27.            if (bean instanceof EmbeddedValueResolverAware) {

  28.                ((EmbeddedValueResolverAware)bean).setEmbeddedValueResolver(this.embeddedValueResolver);

  29.            }


  30.            if (bean instanceof ResourceLoaderAware) {

  31.                ((ResourceLoaderAware)bean).setResourceLoader(this.applicationContext);

  32.            }


  33.            if (bean instanceof ApplicationEventPublisherAware) {

  34.                ((ApplicationEventPublisherAware)bean).setApplicationEventPublisher(this.applicationContext);

  35.            }


  36.            if (bean instanceof MessageSourceAware) {

  37.                ((MessageSourceAware)bean).setMessageSource(this.applicationContext);

  38.            }


  39.            if (bean instanceof ApplicationContextAware) {

  40.                ((ApplicationContextAware)bean).setApplicationContext(this.applicationContext);

  41.            }

  42.        }


  43.    }


  44.    public Object postProcessAfterInitialization(Object bean, String beanName) {

  45.        return bean;

  46.    }

  47. }

3 希望大家手动敲一遍代码,会收获颇丰!

4.欢迎关注米宝窝,持续更新中,谢谢!

米宝窝 https://rocklei123.github.io/


文章转载自rocklei123的技术点滴,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论