目录
@Autowired & @Qualifier & @Primary 与 @Resource,@Inject
@Configuration
告诉spring这是一个配置类,配置类 = 配置文件
@Configuration
public class MainConfig {
}
@Bean
给容器注入一个bean
1).bean的类型为返回值类型,bean的名称默认是方法名
2).可以自定义bean的名称:@Bean("person")
3).下面的例子中,给容器MainConfig 注入一个Person,自定义名称为person
@Configuration
public class MainConfig {
@Bean("person") //自定义bean的名称为person
public Person person01() {
return new Person("lbl",35);
}
}
@Test
public void test() {
ApplicationContext app = new AnnotationConfigApplicationContext(MainConfig.class);
Arrays.stream(app.getBeanDefinitionNames()).forEach(System.out::println);
}
输入结果:
mainConfig
person
----------------------------------------------
小提示:@Configuratiion标注的配置类同时也是一个组件(@Configuratiion里面标注了@Component),所以也在输出结果中.
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
String value() default "";
}
@ComponentScan
定义扫描规则
例子:
@Configuration
@ComponentScan(value = "com.atguigu", useDefaultFilters = false,
includeFilters = {
// @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class,Repository.class}),
// @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {BookService.class}),
@ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyFilterType.class})
}
)
public class MainConfig {
@Bean("person")
public Person person01() {
return new Person("lbl",35);
}
}
@ComponentScan的属性说明
属性 | 返回类型 | 说明 |
value | String[] | 指定要扫描的包 |
useDefaultFilters | boolean | 指定过滤规则,默认的过滤规则是扫描所有(即会把标注了@Controller,@Service,@Repository,@Component 等等全部扫描),所以一般是设置为false |
excludeFilters | Filter[] | 指定扫描的时候按照什么规则排除那些组件 |
includeFilters | Filter[] | 指定扫描的时候只需要包含哪些组件 |
Filter[]类型和说明
类型 | 说明 |
FilterType.ANNOTATION | 按照注解 |
FilterType.ASSIGNABLE_TYPE | 按照给定的类型 |
FilterType.ASPECTJ | 使用aspectj表达式 |
FilterType.REGEX | 使用正则 |
FilterType.CUSTOM | 使用自定义规则 (当前扫描包下的所有类都会进行匹配) |
自定义TypeFilter指定过滤规则
自定义的类实现TypeFilter,重写match方法,在里面自定义过滤规则。
例子:
import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;
import java.io.IOException;
public class MyFilterType implements TypeFilter {
//metadataReader: 读取到当前正在扫描的类的信息
//metadataReaderFactory:可以获取到其他任何的类的信息
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
//获取当前类注解的信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
//获取当前正在扫描的类的类信息(比如类的类型是什么,实现什么接口)
ClassMetadata classMetadata = metadataReader.getClassMetadata();
String className = classMetadata.getClassName();
//获取当前类资源(类的路径)
Resource resource = metadataReader.getResource();
//自定义过滤规则
if (className.contains("BookDao")) {
return true;
}
return false;
}
}
另外:
java8中,@ComponentScan是一个重复注解,可以写多个,定义多种扫描策略;
也可以使用@ComponentScans,里面是ComponentScan数组, 这样也可以定义多个扫描规则。
@Scope
设置组件作用域
singleton: 单例的(默认值)。ioc容器启动会调用方法创建对象放到ioc容器中,以后每次获取就是直接从容器中拿
prototype:多实例的。ioc容器启动并不会去调用方法创建对象放在容器中,每次获取的时候才会调用方法创建对象
request: 同一次请求创建一个实例
session: 同一个session创建一个实例
例子:
@Scope("prototype")
@Bean("person")
public Person person01() {
return new Person("lbl",35);
}
@Lazy
懒加载
单实例bean:默认在容器启动的时候创建对象;
加上@Lazy:容器启动不创建对象。第一次使用(获取)Bean创建对象,并初始化;
例子:
@Lazy
@Bean("person")
public Person person(){
return new Person("lbl", 35);
}
@Conditional
按照条件注册bean
@Conditional 放在类上,表示满足当前条件,这个类上配置的所有bean注册才能生效。
例子:
判断当前操作系统是linux才把名称为linus的Person注册到容器中。
@Bean("linus")
@Conditional(LinuxCondition.class)
public Person person02(){
return new Person("linus", 48);
}
自定义的LinuxCondition类实现Condition
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
import java.util.Arrays;
public class LinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
Environment env = conditionContext.getEnvironment();
//获取操作系统名称
String osName = env.getProperty("os.name");
if (osName.contains("linux")) {
return true;
}
return false;
}
}
@PropertySource
读取外部配置文件中的key/value,保存到运行的环境变量中
例子:
@Configuration
@PropertySource("classpath:conf/person.properties")
public class MainConfigOfPropertyValue {
@Bean
public Person person() {
return new Person();
}
}
@Value
属性赋值
方式:
1).基本数值
2).SpEl(Spring表达式):#{}
3).取配置文件的值:${} (在运行环境变量里面的值)
例子:@Value("${des}") 中的des为person.properties配置文件中的key
import org.springframework.beans.factory.annotation.Value;
public class Person {
@Value("勒布朗")
private String name;
@Value("#{25-2}")
private int age;
@Value("${des}")
private String des;
}
@Autowired & @Qualifier & @Primary 与 @Resource,@Inject
@Autowired: 自动注入
a.默认是优先按照类型去容器中找对应的组件
例子:
使用@Repository向容器中注入BookDao,名称为bookDao,
使用@Autowired在bookService中自动注入BookDao,名称为bookDao3,
输出bookService中的bookDao和根据类型获取的bookDao,结果一样,说明是优先按照类型去容器中找对应的组件。
@Configuration
@ComponentScan({"com.atguigu"})
public class MainConifgOfAutowired {
}
@Repository("bookDao")
public class BookDao {
}
@Service
public class BookService {
@Autowired
private BookDao bookDao3;
public void print(){
System.out.println(bookDao3);
}
}
@Test
public void test5() {
AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(MainConifgOfAutowired.class);
BookService bookService = app.getBean(BookService.class);
bookService.print();
BookDao bookDao = app.getBean(BookDao.class);
System.out.println(bookDao);
}
输出结果:
com.atguigu.dao.BookDao@2ea227af
com.atguigu.dao.BookDao@2ea227af
------------------------------------------------------------
b.若找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找。
例子:
使用@Repository向容器中注入一个BookDao,名称为bookDao,属性label值为1,
使用@Bean向容器中注入一个BookDao,名称为bookDao2,属性label值为2,
向BookService中注入BookDao,名称为bookDao,
输出bookService中的bookDao,结果label值为1,说明获取的是名称为bookDao的bean,
说明相同类型时,再根据名称去容器中查找。
@Configuration
@ComponentScan({"com.atguigu"})
public class MainConifgOfAutowired {
@Bean("bookDao2")
public BookDao bookDao(){
BookDao bookDao = new BookDao();
bookDao.setLabel("2");
return bookDao;
}
}
@Service
public class BookService {
@Autowired
private BookDao bookDao;
public void print(){
System.out.println(bookDao);
}
}
@Repository("bookDao")
public class BookDao {
private String label = "1";
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
@Override
public String toString() {
return "BookDao{" + "label='" + label + '\'' + '}';
}
}
@Test
public void test5() {
AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(MainConifgOfAutowired.class);
BookService bookService = app.getBean(BookService.class);
bookService.print();
}
输出结果:
BookDao{label='1'}
-----------------------------------------------------
若把BookService中的BookDao改为
@Autowired
private BookDao bookDao2;
输出结果为:BookDao{label='2'}
-----------------------------------------------------
c.@Autowired 默认是一定要装配成功的,否则报错。
可以通过required=false更改为 有就装配,没有也行:@Autowired(required=false)
@Qualifier: 明确指定要装配的组件id,而不是使用属性名
例子:虽然注入的bean名称为bookDao2,但明确指定使用bookDao
@Qualifier("bookDao")
@Autowired
private BookDao bookDao2;
@Primary:让Spring进行自动装配时,默认使用首选的bean
例子:当不使用@Qualifier明确指定时,可以使用@Primary标注在某个bean上,这样默认就会使用该bean
import com.atguigu.dao.BookDao;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
@Configuration
@ComponentScan({"com.atguigu"})
public class MainConfigOfAutowired {
@Primary
@Bean("bookDao2")
public BookDao bookDao(){
BookDao bookDao = new BookDao();
bookDao.setLabel("2");
return bookDao;
}
}
@Service
public class BookService {
@Autowired
private BookDao bookDao;
public void print(){
System.out.println(bookDao);
}
@Override
public String toString() {
return "BookService [bookDao=" + bookDao + "]";
}
}
@Repository("bookDao")
public class BookDao {
private String label = "1";
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
@Override
public String toString() {
return "BookDao{" + "label='" + label + '\'' + '}';
}
}
@Test
public void test5() {
AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(MainConfigOfAutowired.class);
BookService bookService = app.getBean(BookService.class);
bookService.print();
}
输出结果:
BookDao{label='2'}
----------------------------------------------
@Resource
它不是Spring的注解,是java规范中的,是JSR250定义的注解(JSR是Java Specification Requests——java 规范要求)可以和@Autowired一样实现自动装配功能;默认是按照组件名称进行装配的,也可以指定名称:
@Resource(name="bookDao");
private BookDao bookDao3;
但没有@Primary功能,也没有@Autowired(reqiured=false),即一定要装配上,否则报错。
注意:
此处说没有@Primary功能,是说当@Resource标注的bean装配上时,另外一个同样类型的bean标注了@Primary也不会使用;
但当@Resource标注的bean装配不上时,另外一个同样类型且标注了@Primary的bean则可以装配上。
例如:
容器里有bookDao 和 bookDao2,
当使用
@Resource
private BookDao bookDao;
@Primary
@Bean("bookDao2")
public BookDao bookDao(){
BookDao bookDao = new BookDao();
bookDao.setLabel("2");
return bookDao;
}
装配的是bookDao,说明@Primary不起作用;
但当
@Resource
private BookDao bookDao3;
@Primary
@Bean("bookDao2")
public BookDao bookDao(){
BookDao bookDao = new BookDao();
bookDao.setLabel("2");
return bookDao;
}
装配的是bookDao2,说明@Primary起作用了;若没@Primary,则会找不到,报错。
@Inject
是JSR330定义的注解,需要导入javax.inject的包,依赖为:
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
和Autowired一样可以实现自动装配功能。若想指定名称,使用@Named
@Inject
@Named("bookDao")
private BookDao bookDao3;
但没有@Primary功能,也没有@Autowired(reqiured=false)。
注意:此处说没有@Primary功能,和@Resource的情况一样。
另外:
@Autowired不仅作用在属性上,还可以作用在 构造器,参数,方法 上,作用都是从容器中获取参数组件的值。
标注在方法上:
@Autowired //方法参数的值从ioc容器中获取
public void setCourse(Course course) {
this.course = course;
}
@Bean+方法参数;参数从容器中获取;默认不写@Autowired效果是一样的,都能自动装配;
@Bean
public Student student(@Autowired Course course) { //这里写不写@Autowired都可以
return new Student(course);
}
标注在构造器上:如果组件只有一个有参构造器,这个有参构造器的@Autowired可以省略,参数位置的组件还是可以自动从容器中获取。
@Autowired //这里写不写@Autowired都可以
public Student(Course course) {
this.course = course;
}
标注在参数上:
public void setCourse(@Autowired Course course) {
this.course = course;
}
// =================或者========================
public Student(@Autowired Course course) {
this.course = course;
}
综上:自动注入时,推荐使用@AutoWired