整理一下之前Spring的学习笔记,大致有一下几种Spring注入到容器中的方法:

1)、配置在xml的方式。

2)、开启包扫描@ComponentScan使用@Component,@Service,@Controller,@Repository(其实后三个都继承@Component)注册组件到spring容器里面

3)、使用@Bean注入

4)、使用@Import快速导入组件

@Configuration

声明为配置类,与bean.xml一致

XML方式注入

Sping最开始的用法,先定义好xml文件,使用ClassPathXmlApplicationContext()加载指定xml文件就创建好容器了,当Bean过多的时候,使用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="user" class="com.test.SpringCoreTest.test00.bean.User">
		<property name="name" value="Tom"></property>
		<property name="age" value="12"></property>
	</bean>
	
</beans>

@ComponentScan

需和@Configuration注解一起使用,与xml当中的<context:component-scan base-package="" />一致,表示扫描指定包下的类将带有@Component注解的类全部扫描到容器当中

有几个常用的参数需了解一下:

basePackages: 扫描指定包下的类并且注入到spring容器里面

useDefaultFilters:是否使用默认过滤器,和excludeFilters、includeFilters配置一起使用

includeFilters:包含过滤器为过滤的内容,当useDefaultFilters为false的时候才生效

excludeFilters:去除指定过滤器过滤的内容,当useDefaultFilters为true的时候才能生效

过滤器为@ComponentScan的内部注解类@Filter:

      classes:指定类,与type搭配使用

       type:过滤器的类型org.springframework.context.annotation.FilterType.class枚举类中(共有五种,仅记录三种经常使用的类型)

            ANNOTATION:指定过滤哪些注解,例如@Controller

            ASSIGNABLE_TYPE:指定过滤哪些类

            CUSTOM:自定义过滤器,可继承TypeFilter接口,实现match方法

示例:

public class SpringTest01Filter implements TypeFilter {
	/**
	 * @param metadataReader 读取当前扫描类的信息
	 * @param metadataReaderFactory 可以获取其他任何类的信息
	 */
	public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
			throws IOException {
		//获取当前注解信息
		ClassMetadata classMetadata = metadataReader.getClassMetadata();
		//获取当前扫描类的信息
		String className = classMetadata.getClassName();
		System.out.println("====>>>>"+className);
		if(className.contains("er"))//如果当前扫描类信息包含er的时候,注入到Spring容器
			return true;
		else
			return false;
	}

}

//配置类
@ComponentScan(basePackages="com.test.SpringCoreTest.test01.config",includeFilters= {
		@Filter(type = FilterType.CUSTOM,classes= {SpringTest01Filter.class})
},useDefaultFilters=true)
@Configuration

这里会有个问题,为什么在使用过滤器的时候对useDefaultFilters有要求?源码中解析:

ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
				componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
//该段代码中获取了useDefaultFilters的值
//进入该类中
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,Environment environment, @Nullable ResourceLoader resourceLoader) {

		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
		this.registry = registry;

		if (useDefaultFilters) {
			registerDefaultFilters();//useDefaultFilters为true时,该方法开启了注册默认过滤器的方法
		}
		setEnvironment(environment);
		setResourceLoader(resourceLoader);
	}
	
//进入到registerDefaultFilters方法里面
protected void registerDefaultFilters() {
		//执行了当前方法重置了includeFilters包含的过滤器,导致带有Component注解的类都会加载到容器里面,所以在useDefaultFilters为true的情况下includeFilters失效的原因在此
		this.includeFilters.add(new AnnotationTypeFilter(Component.class));
		ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
		try {
			this.includeFilters.add(new AnnotationTypeFilter(
					((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
			logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
		}
		catch (ClassNotFoundException ex) {
			// JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
		}
		try {
			this.includeFilters.add(new AnnotationTypeFilter(
					((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
			logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
		}
		catch (ClassNotFoundException ex) {
			// JSR-330 API not available - simply skip.
		}
	}

@Bean

需和@Configuration一起使用,容器中的key为方法名,bean为返回的对象,默认为单例。示例:

@Configuration
public class SpringConfig01 {
	
	@Bean
	public User user() {
		return new User();
	}

    //....
}

以下三个注解可以和@Bean一起使用:

@Scope

指定容器组件类型

prototype:多例模式,当容器创建时,并不会创建对象,而是在调用时创建一个新的对象

singleton:单例模式,容器创建时,对象也会创建

request:主要是针对web应用,每提交一次请求,都回去创建一个对象

session:针对web应用,创建一个session创建一个对象

@Lazy

懒加载只有当前组件第一次被调用的时候才会去创建对象,针对单例模式

@Conditional

可指定在某些条件下,才能将当前组件注入到容器,

参数为继承org.springframework.context.annotation.Condition.class的实现类

/**
	 * @param context 可以使用ApplicationContext(上下文)
	 * @param metadata 可以获取到注解信息
	 */
	public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
		//获取到BeanFactory
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
		//可获取环境参数,如jvm环境,spring环境等等
		Environment environment = context.getEnvironment();
		
		String osName = environment.getProperty("os.name");
		if(osName.contains("Windows"))//如果当前环境为window,则创建当前组件并加载到容器中
			return true;
		return false;
	}

@Import

 快速导入组件,共三种形式,这个算是一个比较主要的注入方式吧,在Spring中,需要做一些扩展的时候都会需要用到这个,比如:mybatis-spring融合,开启AOP功能等使用的就是这里的第三种方式

1)、@Import(value = { Dog.class,Cat.class })//快速导入到容器中,以类的全路径作为bean的ID

2)、@Import(value = { SpringTest03ImportSelector.class })//实现ImportSelector接口

public class SpringTest03ImportSelector implements ImportSelector {
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
			//返回为类的全类名
              return new String [] {};//注意一下,这边必须返回一个空的数组,不然启动报错空指针异常
         }
      }
            //在源码org.springframework.context.annotation.ConfigurationClassParser.class类中
private Collection<SourceClass> asSourceClasses(String... classNames) throws IOException {
		List<SourceClass> annotatedClasses = new ArrayList<>(classNames.length);//这里的classNames为之前ImportSelector返回的数组,为null的话即报错
		for (String className : classNames) {
			annotatedClasses.add(asSourceClass(className));
		}
		return annotatedClasses;
	}

3)、@Import(value = { SpringTest03ImportBeanDefinitionRegistrar.class })//实现ImportBeanDefinitionRegistrar接口,所有的bean注册都会使用到该接口进行注册

/**
            * @param importingClassMetadata 当前类的注解信息 
            * @param registry BeanDefinition的注册类
            * 可做条件修改
            */
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		boolean b1 = registry.containsBeanDefinition("user");
		if(b1) {
			registry.registerBeanDefinition("pig", new RootBeanDefinition(Pig.class));
		}