带你了解SpringIOC容器底层注解

Spring IOC 容器底层注解使用

本篇简单介绍下Spring常用注解的使用

xml配置文件的形式 VS 配置类的形式

1.xml形式

<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
             xsi:schemaLocation="
             http://www.springframework.org/schema/beans 
             http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="car" class="com.tuling.compent.Car"></bean> 
</beans>

去容器中读取Bean

public static void main( String[] args ) { 
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); 
System.out.println(ctx.getBean("person")); 
}

2.基于读取配置类的形式定义Bean信息

@Configuration 
public class MainConfig { 
    @Bean public Person person(){ 
    return new Person(); 
    } 
}
 注意: 通过@Bean的形式是使用的话,
  bean的默认名称是方法名,
  若@Bean(value="bean的名称") 那么bean的名称是指定的

去容器中读取Bean的信息(传入配置类

public static void main( String[] args ) {
     AnnotationConfigApplicationContext ctx = new 
                 AnnotationConfigApplicationContext(MainConfig.class); 
     System.out.println(ctx.getBean("person")); 
}

在配置类上写@CompentScan注解来进行包扫描

@Configuration 
@ComponentScan(basePackages = {"com.tuling.testcompentscan"}) 
public class MainConfig { 

}

排除用法 excludeFilters(排除@Controller注解的,和TulingService的)
在这里插入图片描述

@Configuration
@ComponentScan(basePackages = {"com.tuling.testcompentscan"},
		       excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Controller.class}),
					             @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,value = {TulingService.class})})
public class MainConfig {

}

包含用法
包含用法 includeFilters ,注意,若使用包含的用法,需要把useDefaultFilters属性设置为false(true表示扫描全部的)

@Configuration 
@ComponentScan(basePackages = {"com.tuling.testcompentscan"},
               includeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Controller.class, Service.class}) },useDefaultFilters = false) 
public class MainConfig { 
}

@ComponentScan.Filter type的类型
a)注解形式的FilterType.ANNOTATION @Controller @Service @Repository @Compent

b)指定类型的 FilterType.ASSIGNABLE_TYPE @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,value = {TulingService.class})

c)aspectj类型的 FilterType.ASPECTJ(不常用)

d)正则表达式的 FilterType.REGEX(不常用)

e)自定义的 FilterType.CUSTOM

public enum FilterType { 
	//注解形式 比如@Controller @Service @Repository @Compent 
	ANNOTATION, 
	//指定的类型 
	ASSIGNABLE_TYPE,
	//aspectJ形式的 
	ASPECTJ, 
	//正则表达式的
	 REGEX, 
	 //自定义的
	 CUSTOM 
}

配置Bean的作用域对象

在不指定@Scope的情况下,所有的bean都是单实例的bean,而且是饿汉加载(容器启动实例就创建 好了)

@Bean public Person person() { 
	return new Person(); 
}

指定@Scope为 prototype 表示为多实例的,而且还是懒汉模式加载(IOC容器启动的时候,并不会创建对象,而是 在第一次使用的时候才会创建)

@Bean @Scope(value = "prototype")
public Person person() { 
	return new Person(); 
}

@Scope指定的作用域方法取值
a) singleton 单实例的(默认)
b) prototype 多实例的
c) request 同一次请求
d) session 同一个会话级别

Bean的懒加载@Lazy

(主要针对单实例的bean 容器启动的时候,不创建对象,在第一次使用的时候才会创建该对象)

@Bean 
@Lazy 
public Person person() { 
	return new Person(); 
}

@Conditional进行条件判断等

场景,有二个组件TulingAspect 和TulingLog
我的TulingLog组件是依赖于TulingAspect的组件
应用:自己创建一个TulingCondition的类 实现Condition接口

在这里插入图片描述

public class TulingAspect {
	private String name;

	public TulingAspect() {
		System.out.println("aspect构造");
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}
@Configuration
public class MainConfig {
	@Bean
	public TulingAspect tulingAspect() {
		return new TulingAspect();
	}

	//当切 容器中有tulingAspect的组件,那么tulingLog才会被实例化.
	@Bean
	@Conditional(value = TulingCondition.class)
	public TulingLog tulingLog() {
		return new TulingLog();
	}
}
public class TulingCondition implements Condition {
	@Override
	public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
		//判断容器中是否有tulingAspect的组件
		if(context.getBeanFactory().containsBean("tulingAspect")){
			return true;
		}
		return false;
	}
}
public class TulingLog {
	public void log(){
		System.out.println("日志方法...");
	}
}

运行:

public class MainStart {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig.class);
		TulingLog tulingLog= (TulingLog) ctx.getBean("tulingLog");
		tulingLog.log();
	}
}

在这里插入图片描述

往IOC 容器中添加组件的方式

  1. 通过@CompentScan +@Controller @Service @Respository @compent
    适用场景: 针对我们自己写的组件可以通过该方式来进行加载到容器中。

  2. 通过@Bean的方式来导入组件(适用于导入第三方组件的类)

  3. 通过@Import来导入组件 (导入组件的id为全类名路径)

     1)导入组件的id为全类名路径
    
@Configuration 
@Import(value = {Person.class, Car.class}) 
public class MainConfig {

}
	2)通过@Import 的`ImportSeletor`类实现组件的导入 (导入组件的id为全类名路径)
@Configuration
@ComponentScan(basePackages = {"com.tuling.testcompentscan"})
@Import(value = {TulingImportSelector.class})
public class MainConfig {

}

TulingImportSelector代码

public class TulingImportSelector implements ImportSelector {
	@Override
	public String[] selectImports(AnnotationMetadata importingClassMetadata) {
		return new String[]{"com.tuling.testcompentscan.Car"};
	}
}
3)通过@Import的 `ImportBeanDefinitionRegister`导入组件 (可以指定bean的名称)
public class TulingBeanDefinitionRegister implements ImportBeanDefinitionRegistrar {
	 @Override
	 public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, 
	                                     BeanDefinitionRegistry registry) {
	     //创建一个bean定义对象 
	     RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Cat.class);  
	     //把bean定义对象导入到容器中 
	     registry.registerBeanDefinition("cat",rootBeanDefinition);   
	 } 
}                   
@Configuration 
@Import(value = {TulingBeanDefinitionRegister.class}) 
public class MainConfig { 

}
  1. 通过实现FacotryBean接口来实现注册组件
public class CarFactoryBean implements FactoryBean<Car> {
	@Override
	public Car getObject() throws Exception {
		return new Car();
	}

	@Override
	public Class<?> getObjectType() {
		return Car.class;
	}

	@Override
	public boolean isSingleton() {
		return true;
	}
}

Bean的初始化方法和销毁方法

什么是bean的生命周期?

bean的创建----->初始化----->销毁方法

由容器管理Bean的生命周期,我们可以通过自己指定bean的初始化方法和bean的销毁方法

@Configuration 
public class MainConfig { 
	//指定了bean的生命周期的初始化方法和销毁方法.
	@Bean(initMethod = "init",destroyMethod = "destroy") 
	public Car car() { 
		return new Car(); 
	} 
}

针对单实例bean的话,容器启动的时候,bean的对象就创建了,而且容器销毁的时候,也会调用Bean的销毁方法

针对多实例bean的话,容器启动的时候,bean是不会被创建的而是在获取bean的时候被创建,而且bean的销毁不受IOC容器的管理.

通过 InitializingBean和DisposableBean 的二个接口实现bean的初始化以及销毁方法

@Component
public class Person implements InitializingBean,DisposableBean {
	public Person() {
		System.out.println("Person的构造方法");
	}
	@Override
	public void destroy() throws Exception {
		System.out.println("DisposableBean的destroy()方法 ");
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		System.out.println("InitializingBean的 afterPropertiesSet方法");
	}
}

通过JSR250规范 提供的注解@PostConstruct 和@ProDestory标注的方法

@Component
public class Book {
	public Book() {
		System.out.println("book 的构造方法");
	}
	@PostConstruct
	public void init() {
		System.out.println("book 的PostConstruct标志的方法");
	}
	@PreDestroy
	public void destory() {
		System.out.println("book 的PreDestory标注的方法");
	}
}

通过Spring的BeanPostProcessor的 bean的后置处理器会拦截所有bean创建过程

 postProcessBeforeInitialization 在init方法之前调用
 postProcessAfterInitialization 在init方法之后调用
@Component
public class TulingBeanPostProcessor implements BeanPostProcessor {
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("TulingBeanPostProcessor...postProcessBeforeInitialization:"+beanName);
		return null;
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("TulingBeanPostProcessor...postProcessAfterInitialization:"+beanName);
		return null;
	}
}

BeanPostProcessor的执行时机

populateBean(beanName, mbd, instanceWrapper) 
initializeBean{ 
	applyBeanPostProcessorsBeforeInitialization() 
	invokeInitMethods{ 
	    isInitializingBean.afterPropertiesSet 
	    自定义的init方法 
	}
	applyBeanPostProcessorsAfterInitialization()方法 
}

通过@Value +@PropertySource来给组件赋值

public class Person { 
	//通过普通的方式 
	@Value("司马") 
	private String firstName; 
	//spel方式来赋值 
	@Value("#{28-8}") 
	private Integer age; 
	//通过读取外部配置文件的值 
	@Value("${person.lastName}") 
   private String lastName;
}

@Configuration 
@PropertySource(value = {"classpath:person.properties"}) 
//指定外部文件的位置 
public class MainConfig { 
	@Bean 
	public Person person() { 
		return new Person();
	} 
}

自动装配

@AutoWired的使用

自动注入:

//一个Dao 
@Repository 
public class TulingDao { 

}
@Service 
public class TulingService { 
	@Autowired
    private TulingDao tulingDao;

结论:
a:自动装配首先时按照类型进行装配,若在IOC容器中发现了多个相同类型的组件,那么就按照 属性名称来进行装配

 @Autowired
 private TulingDao tulingDao;

比如,我容器中有二个TulingDao类型的组件 一个叫tulingDao 一个叫tulingDao2

那么我们通过@AutoWired 来修饰的属性名称时tulingDao,那么拿就加载容器的tulingDao组件,
若属性名称为 tulignDao2 那么他就加载的时tulingDao2组件

b:假设我们需要指定特定的组件来进行装配,我们可以通过使用@Qualifier(“tulingDao”)来指定装配的组件

或者在配置类上的@Bean加上@Primary注解

@Autowired 
@Qualifier("tulingDao") 
private TulingDao tulingDao2;

c:假设我们容器中即没有tulingDao 和tulingDao2,那么在装配的时候就会抛出异常

No qualifying bean of type ‘com.tuling.testautowired.TulingDao’ available

若我们想不抛异常 ,我们需要指定 required为false的时候可以了

@Autowired(required = false) 
@Qualifier("tulingDao") 
private TulingDao tulingDao2;

d:@Resource(JSR250规范)

功能和@AutoWired的功能差不多一样,但是不支持@Primary 和@Qualifier的支持

e:@InJect(JSR330规范)

需要导入jar包依赖 功能和支持@Primary功能 ,但是没有Require=false的功能

<dependency> 
<groupId>javax.inject</groupId> 
<artifactId>javax.inject</artifactId> 
<version>1</version> 
</dependency>

XXXAware接口的使用

我们自己的组件 需要使用spring ioc的底层组件的时候,比如 ApplicationContext等 我们可以通过实现XXXAware接口来实现

@Component
public class TulingCompent implements ApplicationContextAware,BeanNameAware {
	private ApplicationContext applicationContext;
	@Override
	public void setBeanName(String name) {
		System.out.println("current bean name is :【"+name+"】");
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext;
	}
}

通过@Profile注解 来根据环境来激活标识不同的Bean

  • @Profile标识在类上,那么只有当前环境匹配,整个配置类才会生效
  • @Profile标识在Bean上 ,那么只有当前环境的Bean才会被激活
  • 没有标志为@Profile的bean 不管在什么环境都可以被激活
@Configuration
public class MainConfig  {
	//标识为测试环境才会被装配
	@Bean
	@Profile(value = "test")
	public Car testDs() {
		System.out.println("test环境");
		return new Car();
	}

	//标识开发环境才会被激活
	@Bean
	@Profile(value = "dev")
	public Car testDev() {
		System.out.println("开发环境");
		return new Car();
	}

	//标识生产环境才会被激活
	@Bean
	@Profile(value = "prod")
	public Person testProd() {
		System.out.println("生产环境");
		return new Person();
	}
}
public class MainStart {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
		ctx.getEnvironment().setActiveProfiles("test","prod");
		ctx.register(MainConfig.class);
		ctx.refresh();
	}
}
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页