SPRING02_核心注解BeanprimaryDependsOnLazyScopeComponentScan详解ImportLookup注解

Posted 所得皆惊喜

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SPRING02_核心注解BeanprimaryDependsOnLazyScopeComponentScan详解ImportLookup注解相关的知识,希望对你有一定的参考价值。

①. Spring核心注解概览

  • ①. Spring核心注解概述
  • ②. 注意:@Indexed 需要引入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-indexer</artifactId>
<optional>true</optional>
</dependency>

②. @Bean、@Primary、@DependsOn

  • ①. @Bean:它是可以标注在一个方法上,并且方法返回的这个对象就会注册到Spring容器中

  • ②. @Primary:同样的这些组件,比如两个方法都返回了数据源DataSource,可以给某一个方法上面标注一个@Primary这样的话,就能够标识这是一个主要组件,那么就默认使用这个标注了@Primary这个方法的组件

  • ③. @DependsOn :这个是声明组件之间的依赖关系的,比如A要创建,@DependsOn B跟C,那么就是创建A这个组件的时候,就会把B跟C先提前准备好

③. @Lazy、@Scope 、@Configuration

  • ①. @Lazy:组件的懒加载,也就是说在用的时候才进行创建,不用的话不创建。因为默认的组件,Spring容器一启动,容器中的所有组件都会进行创建

  • ②. @Scope

  1. 声明组件的作用范围,常用的作用范围是:SCOPE_PROTOTYPE(原型), SCOPE_SING LETON(单例)
  2. 单例就是获取到的东西跟他原本的是一模一样的,独一份的
  3. 而原型就是有一个本体,但你获取的都是这个本体的克隆对象,就跟这个本体不是一个对象,但是内容是一样的
  4. 如果学过原型设计模式和单例设计模式就知道这两者的用途
  • ③. @Configuration:这个注解在SpringBoot大量使用到,这个注解用来标注一个类,就说明这个类是一个配置类来替换以前的Spring的Xml配置文件

④. @Component、@Indexed、@Order

  • ①. @Component:这个用来编写Spring/WEB应用的时候,标注在一个类上,那么这个类就自动添加到容器中

  • ②. @Indexed:这个是Spring5.0里面新加入的一个注解,它是一个加速注解,也就是说默认Spring容器一启动的时候,所有的组件才会进行加载创建对象,如果有大量的组件经常要用到,又不想等Spring容器启动的时候才加载并创建对象,那么就可以标注@Indexed 这个注解,这样的话就相当于给组件直接生成相关的class缓存并全部放好,那么以后Spring容器一旦启动就直接能用了,就不用再走整个Spring容器的启动流程

  • ③. @Order:这个是组件之间的顺序,设置的数字越小,优先级就会越高,一旦优先级越高就优先创建这个组件

⑤. @ComponentScan、@Conditional、@Import

  • ①. @ComponentScan:这个大家估计都知道了,就是包扫描,可以批量扫描某一个包下的所有组件(该包和下面的子包)

  • ②. @Conditional :这个在SpringBoot里面的核心注解,他是根据有没有满足相关条件,再来进行组件注入

  • ③. @Import:这个是用于导入第三方的jar的,如果自己来写注解,那么这些自己写的注解只能标注在自己的类上,但是引入别人开发好的第三方包,并且这个第三方包这个源码在包里面已经压缩好了,所以就可以使用@Import 这个注解来把第三方的包里面的组件导入到容器中

⑥. @ImportResource、@Profile、@PropertySource、@PropertySources

  • ①. @ImportResource:这个是用于导入以前的Xml配置文件

  • ②. @Profile:这个是在SpringBoot里面的多环境激活

  • ③. @PropertySource:这个是导入外部的properties配置文件

  • ④. @PropertySources :跟@PropertySource的组件注解

⑦. @Autowired、@Qualifier、@Value

  • ①. @Autowired:都是组件的装配功能

  • ②. @Qualifier:都是组件的装配功能

  • ③. @Value
    获取环境变量里面的值或者是获取配置文件中的值 @Value("#{xxx}") @Value("${xxx}")
    #:可以对象调用方法
    $:代表的是一个取值

⑧. @Lookup

  • ①. 这个注解只能标注在方法上,这个注解用于有时候这个单实例组件就想使用原型模式引用另外的原型的组件,使其能够获取不一样的对象。 这个注解标注在方法上以后,它会去容器中找相关组件

  • ②. A是单实例组件,A要使用B这个原型组件,在A获取B还是获取的是单实例的,如果用了这个@Lookup注解,那么A获取B,就是获取的是原型的B

  • ③. 注意:@Lookup若标注在get()的方法上,使用@Bean的这种方式注册的组件,那么@Lookup这个注解就不不生效

  • ④. 案列代码搭建

  1. 在Person类上自动注入Cat类,在Person类上标注@Component注解
  2. 在Cat类上标上@Scope注解以及@Component注解
  3. 重新获取到Person对象,调用两次getCat()方法获取到cat1、cat2
@Component
public class Person{
	private String name;
	@Autowired //依赖的组件是多实例就不能Autowired
	private Cat cat;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public void setCat(Cat cat) {
		this.cat = cat;
	}

	public Cat getCat() {
		return cat;
	}

	@Override
	public String toString() {
		return "Person{" +
				"name='" + name + '\\'' +
				'}';
	}
}
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class Cat {
	private String name;

	public void setName(String name) {
		this.name = name;
	}

	public String getName() {
		return name;
	}
}
@ComponentScan(value = "com.xiaozhi")
@Configuration
// @Import(Person.class)
//如果要导入两个用下面的方式
//@Import({Person.class, MyConfig.MyImportBeanDefinitionRegistrar.class})
public class MyConfig {

//	@Bean
//	public Person person(){
//		Person person=new Person();
//		person.setName("TANGZHI");
//		return person;
//	}

	/**
	 * BeanDefinitionRegistry:Bean定义信息注册中心,图纸中心
	 * 		它里面都是BeanDefinition
	 */
	static class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
		@Override
		public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
											BeanDefinitionRegistry registry) {

			//BeanDefinition
			RootBeanDefinition catDefinition = new RootBeanDefinition();
			catDefinition.setBeanClass(Cat.class);
			//Spring 这个实例的类型
			registry.registerBeanDefinition("tomcat",catDefinition);

		}
	}
}
public class AnnotationMainTest {
	public static void main(String[] args) {
		ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
//		Person person = applicationContext.getBean(Person.class);
//		System.out.println("person = " + person);
//		String[] names = applicationContext.getBeanDefinitionNames();
//		/**
//		 * 如果使用的是import方式导入的,那么这个bean的名字就是全类名
//		 * com.xiaozhi.bean.Person
//		 * tomcat
//		 */
//		for (String name : names) {
//			System.out.println(name);
//		}
		Person person = applicationContext.getBean(Person.class);
		Cat cat1 = person.getCat();
		Cat cat2 = person.getCat();
		//这里的结果是true,原因是Person单实例的,导致获取的Cat就是第一次创建
		//person对象的时候,容器初始化的Cat
		System.out.println(cat1==cat2);
	}
}

  • ⑤. 使用@Lookup注解解决问题
// 加上ComponentScan进行包扫描 一定要加上
@Configuration
// @Import(Person.class)
//如果要导入两个用下面的方式
//@Import({Person.class, MyConfig.MyImportBeanDefinitionRegistrar.class})
@ComponentScan(value = "com.xiaozhi") //这里一定要进行包扫描
public class MyConfig {
//@Autowired //依赖的组件是多实例就不能Autowired
@Component
public class Person{
	private String name;
	@Autowired //依赖的组件是多实例就不能Autowired
	private Cat cat;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public void setCat(Cat cat) {
		this.cat = cat;
	}
	@Lookup//去容器中找
	public Cat getCat() {
		return cat;
	}

	@Override
	public String toString() {
		return "Person{" +
				"name='" + name + '\\'' +
				'}';
	}
}
public class AnnotationMainTest {
	public static void main(String[] args) {
		ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
//		Person person = applicationContext.getBean(Person.class);
//		System.out.println("person = " + person);
//		String[] names = applicationContext.getBeanDefinitionNames();
//		/**
//		 * 如果使用的是import方式导入的,那么这个bean的名字就是全类名
//		 * com.xiaozhi.bean.Person
//		 * tomcat
//		 */
//		for (String name : names) {
//			System.out.println(name);
//		}
		Person person = applicationContext.getBean(Person.class);
		Cat cat1 = person.getCat();
		Cat cat2 = person.getCat();
		//false,这里是false,表示获取到的是多列的
		System.out.println(cat1==cat2);
		String[] names = applicationContext.getBeanDefinitionNames();
		/**
		 * 这里我们在配置文件中配置了@ComponentScan(value = "com.xiaozhi")
		 * 进行扫描,可以看到这里的person和cat的名称
		 * cat
		 * person
		 */
		for (String name : names) {
			System.out.println(name);
		}
	}
}

⑨. 详解@Import注解

  • ①. 新建配置类MyConfig,进行测试(这里是引入注解的方式创建)
package com.xiaozhi.config;
@Configuration
public class MyConfig {
	@Bean
	public Person person(){
		Person person=new Person();
		person.setName("TANGZHI");
		return person;
	}
}
package com.xiaozhi;
public class AnnotationMainTest {
	public static void main(String[] args) {
		ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
		Person person = applicationContext.getBean(Person.class);
		System.out.println("person = " + person);
	}
}
  • ②. 使用@Import的方式注入
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {

	/**
	 * 1.直接写class person = Person{name='null'}:利用无参构造创建出对象放在容器中
	 * 2.ImportSelector
	 * 3.ImportBeanDefinitionRegistrar
	 * {@link Configuration @Configuration}, {@link ImportSelector},
	 * {@link ImportBeanDefinitionRegistrar}, or regular component classes to import.
	 */
	Class<?>[] value();
}
@Configuration
   @Import(Person.class)
public class MyConfig {
}
  • ③. 新建Cat类,使用Import的方式导入,我们使用ImportBeanDefinitionRegistrar的方式给Cat进行注册
    BeanDefinitionRegistry:这个相当于档案库
    BeanDefinition:这个一个个的实例图纸对象
package com.xiaozhi.bean;
public class Cat {
	private String name;
	public void setName(String name) {
		this.name = name;
	}
	public String getName() {
		return name;
	}
}
@Configuration
// @Import(Person.class)
//如果要导入两个用下面的方式
@Import({Person.class, MyConfig.MyImportBeanDefinitionRegistrar.class})
public class MyConfig {

//	@Bean
//	public Person person(){
//		Person person=new Person();
//		person.setName("TANGZHI");
//		return person;
//	}

	/**
	 * BeanDefinitionRegistry:Bean定义信息注册中心,图纸中心
	 * 		它里面都是BeanDefinition
	 */
	static class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
		@Override
		public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
											BeanDefinitionRegistry registry) {

			//BeanDefinition
			RootBeanDefinition catDefinition = new RootBeanDefinition();
			catDefinition.setBeanClass(Cat.class);
			//Spring 这个实例的类型
			registry.registerBeanDefinition("tomcat",catDefinition);
		}
	}
}
public class AnnotationMainTest {
	public static void main(String[] args) {
		ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
//		Person person = applicationContext.getBean(Person.class);
//		System.out.println("person = " + person);
		String[] names = applicationContext.getBeanDefinitionNames();
		/**
		 * 如果使用的是import方式导入的,那么这个bean的名字就是全类名
		 * com.xiaozhi.bean.Person
		 * tomcat
		 */
		for (String name : names) {
			System.out.println(name);
		}
	}
}

以上是关于SPRING02_核心注解BeanprimaryDependsOnLazyScopeComponentScan详解ImportLookup注解的主要内容,如果未能解决你的问题,请参考以下文章

SRPING02_配置数据源原始注解开发新注解开发集成Junit代码实现

《02.Spring Boot连载:Spring Boot实战.Spring Boot核心原理剖析》

阶段3 2.Spring_06.Spring的新注解_6 Qualifier注解的另一种用法

SPRING_应用程序框架源码解析目录

Spring4-自动装配Beans-通过注解@Autowired在构造方法上

Spring Boot 运行原理 - 核心注解