title: Spring注解驱动开发(三)-属性赋值和自动装配 author: rocklei123 tags:
Annotation
Spring
Java categories: Spring date: 2018-12-01 21:45:44
1.属性赋值
1.1 @Value
使用@Value注解赋值;
基本数值
可以写Spring的EL表达式; #{}
可以写${};取出配置文件【properties】中的值(在运行环境变量里面的值)
1.2 @PropertySource
使用@PropertySource读取外部配置文件中的k/v保存到运行的环境变量中;
加载完外部的配置文件以后使用${}取出配置文件的值
@PropertySource(value={"classpath:/person.properties"}) 数组类型,可以支持一次性引入多个文件。
另外@PropertySource 是一个可重复的注解,可以使用@PropertySources注解引入多个@PropertySource标注的properties配置文件
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PropertySources {
PropertySource[] value();
}
测试代码如下:
//实体类信息
package com.rocklei123.bean;
import org.springframework.beans.factory.annotation.Value;
/**
* @ClassName: Person
* @Author: rocklei123
* @Date: 2018/12/1 21:27
* @Description: TODO
* @Version 1.0
*/
public class Person {
@Value(value = "张三")
private String name;
@Value(value = "#{20-2}")
private Integer age;
@Value(value = "${person.nickName}")
private String nickName;
public Person() {
System.out.println("Person默认构造方法");
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", nickName='" + nickName + '\'' +
'}';
}
}
//配置类信息
package com.rocklei123.config;
import com.rocklei123.bean.Person;
import org.springframework.context.annotation.*;
@PropertySource(value = {"classpath:/person.properties"})
@Configuration
public class MainConfigOfPropertyValues {
@Bean
public Person person() {
return new Person();
}
}
//测试类信息
public class IocTestOfPropertyValues {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfPropertyValues.class);
public void printBean(ApplicationContext applicationContext) {
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
Object o = applicationContext.getBean(beanDefinitionName);
System.out.println(o);
}
}
@Test
public void testValue() {
printBean(applicationContext);
Environment environment = applicationContext.getEnvironment();
String nickName = environment.getProperty("person.nickName");
System.out.println("环境信息中property=" + nickName);
}
}
测试结果:
Person{name='张三', age=18, nickName='小李四'}
环境信息中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 默认注入情况测试
默认注入情况:名字默认是类名首字母小写
//service 层代码,自动注入dao层
@Service
public class BookService {
@Autowired
BookDao bookDao;
@Override
public String toString() {
return "BookService{" +
"bookDao=" + bookDao +
'}';
}
}
//dao层代码,加入一个默认名称区别
@Repository
public class BookDao {
private String nameFlag = "1";
public BookDao() {
}
public BookDao(String nameFlag) {
this.nameFlag = nameFlag;
}
public String getNameFlag() {
return nameFlag;
}
public void setNameFlag(String nameFlag) {
this.nameFlag = nameFlag;
}
@Override
public String toString() {
return "BookDao{" +
"nameFlag='" + nameFlag + '\'' +
'}';
}
}
//配置类,向容器中注入另一个bookDao,名称为BookDao2
@ComponentScan(value = {"com.rocklei123.service", "com.rocklei123.controller", "com.rocklei123.repository"})
@Configuration
public class MainConfigOfAutowired {
@Bean(value = "bookDao2")
public BookDao bookDao2(BookDao bookDao) {
BookDao bookDao2 = new BookDao();
bookDao2.setNameFlag("2");
return bookDao2;
}
}
//测试类
public class IocTestOfAutowired {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAutowired.class);
@Test
public void testValue() {
BookService bean = applicationContext.getBean(BookService.class);
System.out.println(bean);
}
}
测试结果:通过上文注意事项可知,Spring容器注入时会先通过类型找到两个BookDao类型的对象,第二次会通过 默认是类名首字母小写在查找一次。故Service层注入的BookDao对象应该为nameFlag = "1"; 的对象
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@4aa8f0b4: startup date [Sat Dec 15 18:56:29 CST 2018]; root of context hierarchy
BookService{bookDao=BookDao{nameFlag='1'}}
如果此时修改测试类使用IOC容器按类型获取BookDao此时,因为容器中有两个BookDao类型的对象,当打印时就会报错。
@Test
public void testValue() {
BookDao bean = applicationContext.getBean(BookDao.class);
System.out.println(bean);
}
//异常信息
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);
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
boolean required() default true;
}
默认required 为true
测试用例
//业务层代码,自动注入Dao层对象,required = false
public class BookService {
@Qualifier(value = "bookDao2")
@Autowired(required = false)
BookDao bookDao;
}
//将BookDao不注入到容器中
//虽然容器中不含有BookDao,通过测试类打印业务层代码时,也不会报错。
@Test
public void testValue() {
BookService bean = applicationContext.getBean(BookService.class);
System.out.println(bean);
}
//测试结果
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一起使用。
public class BookService {
@Qualifier(value = "bookDao2") //指定装配组件的Id为bookDao2
@Autowired
BookDao bookDao;
}
//测试结果
BookService{bookDao=BookDao{nameFlag='2'}}
2.1.3 @Primary
@Primary:让Spring进行自动装配的时候,默认使用首选的bean;也可以继续使用@Qualifier指定需要装配的bean的名字。
通过下面的示例也可以看出@Primary和@Qualifier 在一定程度上有所冲突,如果我们service层代码使用@Qualifier指定注入名称,即使在相同类型的其他Bean 上标注@Primary,Spring注入Service层的也是根据@Qualifier标注注入的。
@Service
public class BookService {
@Qualifier(value = "bookDao")
@Autowired(required = false)
BookDao bookDao;
@Override
public String toString() {
return "BookService{" +
"bookDao=" + bookDao +
'}';
}
}
@ComponentScan(value = {"com.rocklei123.service", "com.rocklei123.controller", "com.rocklei123.repository"})
@Configuration
public class MainConfigOfAutowired {
@Primary
@Bean(value = "bookDao2")
public BookDao bookDao2(BookDao bookDao) {
BookDao bookDao2 = new BookDao();
bookDao2.setNameFlag("2");
return bookDao2;
}
}
//测试代码
public class IocTestOfAutowired {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAutowired.class);
public void printBean(ApplicationContext applicationContext) {
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
Object o = applicationContext.getBean(beanDefinitionName);
System.out.println(o);
}
}
@Test
public void testValue() {
printBean(applicationContext);
System.out.println("-----------------------------------");
BookService bean = applicationContext.getBean(BookService.class);
System.out.println(bean);
}
}
测试结果:可以看出Spring容器加载了两个BookDao类型的bean,向Serveice层注入的是 @Qualifier(value = "bookDao")标注的。
com.rocklei123.config.MainConfigOfAutowired$$EnhancerBySpringCGLIB$$7d34398f@37e547da
BookService{bookDao=BookDao{nameFlag='1'}}
com.rocklei123.controller.BookController@5db45159
BookDao{nameFlag='1'}
BookDao{nameFlag='2'}
-----------------------------------
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);
@Service
public class BookService {
@Resource(name = "bookDao2")
BookDao bookDao;
}
2.2.2 @Inject:
@Inject支持构造函数、方法和字段注解,也可能使用于静态实例成员。可注解成员可以是任意修饰符(private,package-private,protected,public)。注入顺序:构造函数、字段,然后是方法。父类的字段和方法注入优先于子类的字段和方法,同一类中的字段和方法是没有顺序的。
需要导入javax.inject的包,和Autowired的功能一样。没有required=false的功能;但是支持@Primary
<!-- https://mvnrepository.com/artifact/javax.inject/javax.inject -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
2.3 @Autowired 和 @Resource、@Inject 的区别
相同点
@Autowired和@Inject基本是一样的,因为两者都是使用AutowiredAnnotationBeanPostProcessor来处理依赖注入。但是@Resource是个例外,它使用的是CommonAnnotationBeanPostProcessor来处理依赖注入。当然,两者都是BeanPostProcessor。
三个注解都能实现自动装配功能
不同点
注解定义不同
@Autowired:Spring定义的,脱离Spring框架就没有了;
@Resource、@Inject都是java规范,其他IOC框架也会支持。
默认注入方式不同
@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。
功能性差异
@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中;
测试代码:
package com.rocklei123.bean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.config.EmbeddedValueResolver;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import org.springframework.util.StringValueResolver;
/**
* @ClassName: Apple
* @Author: rocklei123
* @Date: 2018/12/15 20:46
* @Description: 自定义组件注入Spring容器底层的一些组件
* @Version 1.0
*/
@Component
public class Apple implements ApplicationContextAware, BeanNameAware, EnvironmentAware, EmbeddedValueResolverAware {
private ApplicationContext applicationContext;
@Override
public void setBeanName(String s) {
System.out.println("当前Bean的名称:" + s);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("传入的IOC:" + applicationContext);
this.applicationContext = applicationContext;
}
@Override
public void setEnvironment(Environment environment) {
System.out.println("当前person.properties文件中的昵称名:" + environment.getProperty("person.nickName"));
}
@Override
public void setEmbeddedValueResolver(StringValueResolver stringValueResolver) {
String s = stringValueResolver.resolveStringValue("我的操作系统类型${os.name},我的年龄#{26+1}");
System.out.println(s);
}
}
//配置类
@PropertySource(value = {"classpath:/person.properties"})
@ComponentScan(value = {"com.rocklei123.bean", "com.rocklei123.service", "com.rocklei123.controller", "com.rocklei123.repository"})
@Configuration
public class MainConfigOfAutowired {
@Primary
@Bean(value = "bookDao2")
public BookDao bookDao2(BookDao bookDao) {
BookDao bookDao2 = new BookDao();
bookDao2.setNameFlag("2");
return bookDao2;
}
}
//测试结果
当前Bean的名称:apple
当前person.properties文件中的昵称名:小李四
我的操作系统类型Windows 10,我的年龄27
传入的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为例源码:
package org.springframework.context.support;
class ApplicationContextAwareProcessor implements BeanPostProcessor {
private final ConfigurableApplicationContext applicationContext;
private final StringValueResolver embeddedValueResolver;
public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
AccessControlContext acc = null;
if (System.getSecurityManager() != null && (bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware || bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware || bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {
acc = this.applicationContext.getBeanFactory().getAccessControlContext();
}
if (acc != null) {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
ApplicationContextAwareProcessor.this.invokeAwareInterfaces(bean);
return null;
}
}, acc);
} else {
this.invokeAwareInterfaces(bean);
}
return bean;
}
private void invokeAwareInterfaces(Object bean) {
if (bean instanceof Aware) {
if (bean instanceof EnvironmentAware) {
((EnvironmentAware)bean).setEnvironment(this.applicationContext.getEnvironment());
}
if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware)bean).setEmbeddedValueResolver(this.embeddedValueResolver);
}
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware)bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware)bean).setApplicationEventPublisher(this.applicationContext);
}
if (bean instanceof MessageSourceAware) {
((MessageSourceAware)bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware)bean).setApplicationContext(this.applicationContext);
}
}
}
public Object postProcessAfterInitialization(Object bean, String beanName) {
return bean;
}
}
3 希望大家手动敲一遍代码,会收获颇丰!
4.欢迎关注米宝窝,持续更新中,谢谢!
米宝窝 https://rocklei123.github.io/




