Spring5源码 - Spring IOC  注解复习_Spring5


Pre

为了更好地学习源码,我们有必要对基础知识进行一次简单的复习,只有在知道如何使用的基础上,再去阅读源码才能明白spring这些源码是对哪些功能的支持。

这里简单的梳理一下


xml配置文件

【配置文件 】

<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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
	<bean id="artisan"  class="com.artisan.base.Artisan"/>
</beans>

【读取Bean】

ClassPathXmlApplicationContext cx = new ClassPathXmlApplicationContext("classpath:spring.xml");
System.out.println(cx.getBean("artisan").getClass().getSimpleName());
	}

【输出】
Spring5源码 - Spring IOC  注解复习_Spring5_02


JavaConfig

【POJO】

public class Bean1 {
}
    

【配置文件 】

@Configuration
public class MainConfig {

	@Bean
	public Bean1 bean1(){
		return new Bean1();
	}
}

【读取Bean— 传入配置类】

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

@Bean的形式, bean的默认名称是方法名,若@Bean(value=“bean的名称”) ,那么bean的名称是指定的名称。

【测试结果】

Spring5源码 - Spring IOC  注解复习_Spring5源码_03


@CompentScan

Spring5源码 - Spring IOC  注解复习_Spring5_04

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

【配置类】

package com.artisan.base.componentscan.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("com.artisan.base.componentscan")
public class CSMainConfig {

}
    

【c - s -d 】

package com.artisan.base.componentscan.controller;

import org.springframework.stereotype.Controller;

@Controller
public class ArtisanInfoController {
}
    
package com.artisan.base.componentscan.service;

import org.springframework.stereotype.Service;

@Service
public class ArtisanService {
}
    
package com.artisan.base.componentscan.dao;

import org.springframework.stereotype.Repository;

@Repository
public class ArtisanDao {
}
    

【测试类】

package com.artisan.base.componentscan;

import com.artisan.base.componentscan.config.CSMainConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;


public class CMTest {

	public static void main(String[] args) {

		AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(CSMainConfig.class);
		// 输出 单例池中的单例bean的名字
		for (String beanDefinitionName : ac.getBeanDefinitionNames()) {
			System.out.println("bdName:" + beanDefinitionName);
		}

	}
}

【输出】

Spring5源码 - Spring IOC  注解复习_Spring5_05


excludeFilters

【配置类】

import com.artisan.base.componentscan.service.ArtisanService;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;

@Configuration
@ComponentScan(
		basePackages = {"com.artisan.base.componentscan"},
		excludeFilters = {
			@ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Controller.class}),
			@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,value = {ArtisanService.class})
})
public class CSMainConfig {

}

重点配置

@ComponentScan(
		basePackages = {"com.artisan.base.componentscan"},
		excludeFilters = {
			@ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Controller.class}),
			@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,value = {ArtisanService.class})
})

excludeFilters(排除@Controller注解的,和ArtisanService的)

【测试结果】

Spring5源码 - Spring IOC  注解复习_Spring5源码_06


includeFilters

@Configuration
@ComponentScan(
		basePackages = {"com.artisan.base.componentscan"},
		includeFilters =
				{@ComponentScan.Filter(type = FilterType.ANNOTATION, value = {Controller.class})},
		useDefaultFilters = false)public class CSMainConfig {

}

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

【测试结果】

Spring5源码 - Spring IOC  注解复习_Spring5源码_07


@ComponentScan.Filter type的类型

看下源码

public enum FilterType {

	/**
	 * Filter candidates marked with a given annotation.
	 * @see org.springframework.core.type.filter.AnnotationTypeFilter
	 */
	ANNOTATION,

	/**
	 * Filter candidates assignable to a given type.
	 * @see org.springframework.core.type.filter.AssignableTypeFilter
	 */
	ASSIGNABLE_TYPE,

	/**
	 * Filter candidates matching a given AspectJ type pattern expression.
	 * @see org.springframework.core.type.filter.AspectJTypeFilter
	 */
	ASPECTJ,

	/**
	 * Filter candidates matching a given regex pattern.
	 * @see org.springframework.core.type.filter.RegexPatternTypeFilter
	 */
	REGEX,

	/** Filter candidates using a given custom
	 * {@link org.springframework.core.type.filter.TypeFilter} implementation.
	 */
	CUSTOM

}
  • ANNOTATION 注解形式 举个例子比如 @Compent @Controller @Service @Repository .......
  • ASSIGNABLE_TYPE 指定类型的 FilterType.ASSIGNABLE_TYPE 举个例子
 @ComponentScan.Filter(type =
FilterType.ASSIGNABLE_TYPE,value = {ArtisanService.class})
  • ASPECTJ 类型的 FilterType.ASPECTJ 不常用
  • REGEX 正则表达式的 FilterType.REGEX 不常用
  • CUSTOM 自定义类型

使用自定义过滤器CUSTOM

package com.artisan.base.componentscan.customFilterType;


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 ArtisanFilterType  implements TypeFilter {


	@Overridez
	public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
		//获取当前类的注解源信息
		AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
		//获取当前类的class的源信息
		ClassMetadata classMetadata = metadataReader.getClassMetadata();
		//获取当前类的资源信息
		Resource resource =  metadataReader.getResource();
		System.out.println("类的路径:"+classMetadata.getClassName());
		// 排除包含Artisan的Bean
		if(classMetadata.getClassName().contains("Artisan")) {
			return true;
		}
		return false;
	}
}
    

【配置类】

@Configuration
@ComponentScan(basePackages = {"com.artisan.base.componentscan"},
		excludeFilters = {@ComponentScan.Filter(type = FilterType.CUSTOM, value = {ArtisanFilterType.class})})
public class CSMainConfig {

}

【测试结果】

Spring5源码 - Spring IOC  注解复习_Spring5_08


@Scope

https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-factory-scopes
Spring5源码 - Spring IOC  注解复习_Spring5源码_09


public class Bean2 {

	public Bean2() {
		System.out.println("Bean2 Created");
	}
}
public class Bean3 {

	public Bean3() {

		System.out.println("Bean3 Created");
	}
}
    

【config】

package com.artisan.base.scope;


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

@Configuration
public class Config {


	@Bean()
	public Bean2 bean2(){
		return new Bean2();
	}

 
	@Bean()
	@Scope(value = "prototype")
	public Bean3 bean3(){
		return new Bean3();
	}


}

【测试bean的加载】

package com.artisan.base.scope;


import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class TestScope {

	public static void main(String[] args) {
		AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);

		System.out.println("========================");
		// 每次调用都会实例化一个新的Bean3+
		System.out.println(ac.getBean("bean3"));
		System.out.println(ac.getBean("bean3"));
	}
}

【输出】

Spring5源码 - Spring IOC  注解复习_Spring5_10


【结论】

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

  • 指定@Scope为 prototype 表示为原型bean,而且还是懒汉模式加载 , 即IOC容器启动的时候,并不会创建对象,而是 在第一次使用的时候才会创建 ,并且每次调用,都会实例化一个新的对象


@Lazy

Bean的懒加载@Lazy 主要针对单实例的bean 容器启动的时候,不创建对象,在第一次使用的时候才会创建该对象 ,后续调用不会新建对象,而是从单例池中获取缓存的bean。

继续使用上面的例子 ,给Bean2 加上Lazy注解

	@Bean()
	@Lazy
	public Bean2 bean2(){
		return new Bean2();
	}

【测试】

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class TestScope {

	public static void main(String[] args) {
		AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);

		System.out.println(ac.getBean("bean2"));
		System.out.println(ac.getBean("bean2"));


	}
}
    

【结果】

Spring5源码 - Spring IOC  注解复习_Spring5_11


@Conditional 条件判断

Spring5源码 - Spring IOC  注解复习_Spring5源码_12

需求: 只有容器中有bean5 才装载bean6


Spring5源码 - Spring IOC  注解复习_Spring5_13

value 是一个class数组, 需要实现Condition 接口 , 那我们也弄个呗

【自定义Condition 】

package com.artisan.base.condition;

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;


public class ArtisanCondition implements Condition {

	@Override
	public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
		//判断容器中是否有Bean5 ,有的话 返回true
		if(context.getBeanFactory().containsBean("bean5")) {
			return true;
		}
		return false;
	}
}
    

【config 】

package com.artisan.base.condition;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;

@Configuration
public class CDConfig {

	@Bean
	public Bean5 bean5(){
		return new Bean5();
	}


	// Conditional的条件返回true,才装载Bean6
	@Bean
	@Conditional(value = ArtisanCondition.class)
	public Bean6 bean6(){
		return new Bean6();
	}

}
    

【beans】

public class Bean5 {

	public Bean5() {
		System.out.println("Bean5 Created");
	}
}

public class Bean6 {

	public Bean6() {
		System.out.println("Bean6 Created");
	}
}
    

【正常情况】

package com.artisan.base.condition;


import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class CDTest {

	public static void main(String[] args) {
		AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(CDConfig.class);

		for (String beanDefinitionName : ac.getBeanDefinitionNames()) {
			System.out.println(beanDefinitionName);
		}
	}
}
    
    

我们看到了 CDConfig中 bean5 上标注了@Bean

Spring5源码 - Spring IOC  注解复习_Spring5源码_14

现在我们把Bean5的@Bean去掉

Spring5源码 - Spring IOC  注解复习_Spring5源码_15