Spring源码解读
一、Spring 以XML方式加载bean的过程
1. 代码入口
public class DemoApplication {
public static void main(String[] args) {
// 通过XmlBeanFactory加载bean.xml文件。
BeanFactory factory = new XmlBeanFactory(new ClassPathResource("BeanFactoryTest.xml"));
// 读取bean的对象。
Person person = (Person) factory.getBean("person");
System.out.println(person);
}
}
2. 封装配置文件
上面时序图的第2步就是完成将配置文件BeanFactoryTest.xml封装为Resource的实例。
Resource接口抽象了所有Spring内部使用到的底层资源:File、URL、ClassPath等。
下面是Resource接口的实现类。
ContextResource (org.springframework.core.io)
HeadMethodResource in ResourceHandlerFunction (org.springframework.web.servlet.function)
HttpResource (org.springframework.web.servlet.resource)
WritableResource (org.springframework.core.io)
AbstractResource (org.springframework.core.io)
3. 初始化XmlBeanFactory
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);// 初始化1
this.reader = new XmlBeanDefinitionReader(this);//初始化2
this.reader.loadBeanDefinitions(resource);
}
- 初始化1主要初始化其继承的DefaultListableBeanFactory,父类中的操作主要是初始化一些后面加载bean所用到的容器。
SimpleAliasRegistry (org.springframework.core)
DefaultSingletonBeanRegistry (org.springframework.beans.factory.support)
FactoryBeanRegistrySupport (org.springframework.beans.factory.support)
AbstractBeanFactory (org.springframework.beans.factory.support)
AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
DefaultListableBeanFactory (org.springframework.beans.factory.support)
XmlBeanFactory (org.springframework.beans.factoy.xml)
此处的beanDefinitionMap就是存放bena的容器。
public DefaultListableBeanFactory(@Nullable BeanFactory parentBeanFactory) {
super(parentBeanFactory);
this.autowireCandidateResolver = SimpleAutowireCandidateResolver.INSTANCE;
this.resolvableDependencies = new ConcurrentHashMap(16);
this.beanDefinitionMap = new ConcurrentHashMap(256);
this.mergedBeanDefinitionHolders = new ConcurrentHashMap(256);
this.allBeanNamesByType = new ConcurrentHashMap(64);
this.singletonBeanNamesByType = new ConcurrentHashMap(64);
this.beanDefinitionNames = new ArrayList(256);
this.manualSingletonNames = new LinkedHashSet(16);
}
- 初始化2 就是new 一个处理加载bean的对象。其loadBeanDefinitions(resource); 方法就是加载bean的核心方法,具体加载的逻辑都封装在该方法中。
4. 加载操作的切入点
(简单点:doLoadBeanDefinitions(inputSource, encodedResource.getResource());方法进行加载操作。)
此处进行的操作主要有三个:
- 封装资源文件
- 获取输入流。
- 使用前两步准备的数据作为参数调用doLoadBeanDefinitions。该步骤之前的所有操作都是为了准备需要的InputResource对象和Resource对象。
// 1.封装资源文件。 此处将Resource实现进行再次封装为EncodedResource实现,进行编码设置。
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (this.logger.isTraceEnabled()) {
this.logger.trace("Loading XML bean definitions from " + encodedResource);
}
// 将当前加载的所有Resource实现都放到一个Set容器中。并且该Set是一个ThreadLocal对象。
// private final ThreadLocal<Set<EncodedResource>> resourcesCurrentlyBeingLoaded;
Set<EncodedResource> currentResources = (Set)this.resourcesCurrentlyBeingLoaded.get();
if (!currentResources.add(encodedResource)) {//当该set中以存在该encodedResource,则说明其已被加载,存在循环加载的错误。
throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");
} else {
int var6;
try {
// 2. 获取输入流。从EncodedResource中获取封装的Resource对象得到输入流,构造下面的InputSource。
InputStream inputStream = encodedResource.getResource().getInputStream();
Throwable var4 = null;
try {
doLoadBeanDefinitions inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//3. 此处是加载Bean的真正的方法。
var6 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());
} catch (Throwable var24) {
var4 = var24;
throw var24;
} finally {
if (inputStream != null) {
if (var4 != null) {
try {
inputStream.close();
} catch (Throwable var23) {
var4.addSuppressed(var23);
}
} else {
inputStream.close();
}
}
}
} catch (IOException var26) {
throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), var26);
} finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
return var6;
}
}
5. doLoadBeanDefinitions方法的加载分析(加载XML文件的核心)
主要操作步骤:
- 获取XML文件的验证模式。
- 加载XML文件得到将其封装的Document对象。
- 根据其返回的Document对象注册Bean信息到上面一开始初始化的beanDefinitionMap中。
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
try {
// 1. 验证并获取Document对象
Document doc = this.doLoadDocument(inputSource, resource);
// 2. 注册xml对象到BeanDefinitionMap容器中。
int count = this.registerBeanDefinitions(doc, resource);
return count;
}
(1)获取验证模式,并封装返回Doc
xml的验证模式分为DTD和XSD。
DTD主要校验文档的格式,使用规则,标签,元素使用是否正确。XSD主要校验文档的内容和结果是否正确。
<!-- XSD模式 -->
<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">
<!-- DTD模式 -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
return this.documentLoader.loadDocument(inputSource, this.getEntityResolver(), this.errorHandler, this.getValidationModeForResource(resource), this.isNamespaceAware());
}
// 获取资源的验证模式
protected int getValidationModeForResource(Resource resource) {
int validationModeToUse = this.getValidationMode();
if (validationModeToUse != 1) {// 检测是否手动指定了验证模式
return validationModeToUse;
} else {
int detectedMode = this.detectValidationMode(resource);
return detectedMode != 1 ? detectedMode : 3;
}
}
// Spring默认的验证模式。
protected int detectValidationMode(Resource resource) {
if (resource.isOpen()) {
throw new BeanDefinitionStoreException("Passed-in Resource [" + resource + "] contains an open stream: cannot determine validation mode automatically. Either pass in a Resource that is able to create fresh streams, or explicitly specify the validationMode on your XmlBeanDefinitionReader instance.");
} else {
InputStream inputStream;
try {
inputStream = resource.getInputStream();
} catch (IOException var5) {
throw new BeanDefinitionStoreException("Unable to determine validation mode for [" + resource + "]: cannot open InputStream. Did you attempt to load directly from a SAX InputSource without specifying the validationMode on your XmlBeanDefinitionReader instance?", var5);
}
try {
//该方法会获xml文件的头部进行校验。
return this.validationModeDetector.detectValidationMode(inputStream);
} catch (IOException var4) {
throw new BeanDefinitionStoreException("Unable to determine validation mode for [" + resource + "]: an error occurred whilst reading from the InputStream.", var4);
}
}
}
获取XML文档的校验模式,包含DOCTYPE就是DTD,不包含就是XSD.
// 返回 3 代表XSD模式,2代表DTD模式。
public int detectValidationMode(InputStream inputStream) throws IOException {
// 读取xml文档的读取器。
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
byte var4;
try {
boolean isDtdValidated = false;
while(true) {
String content;
// 读取一行文本=》 <beans xmlns="http://www.springframework.org/schema/beans"
if ((content = reader.readLine()) != null) {
content = this.consumeCommentTokens(content);
if (this.inComment || !StringUtils.hasText(content)) {//如果读取的行是注释或空则跳过。
continue;
}
// 对比是否包含校验字符。
if (this.hasDoctype(content)) {
isDtdValidated = true;
} else if (!this.hasOpeningTag(content)) {
continue;
}
}
int var5 = isDtdValidated ? 2 : 3;
return var5;
}
} catch (CharConversionException var9) {
var4 = 1;
} finally {
reader.close();
}
return var4;
}
// 对比是否包含校验字符。
private boolean hasDoctype(String content) {
return content.contains("DOCTYPE");
}
(2)注册bean
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader();
int countBefore = this.getRegistry().getBeanDefinitionCount();
// 注册bean
documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource));
return this.getRegistry().getBeanDefinitionCount() - countBefore;
}
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
// 注册bean的具体方法
this.doRegisterBeanDefinitions(doc.getDocumentElement());
}
protected void doRegisterBeanDefinitions(Element root) {
//专门处理解析
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = this.createDelegate(this.getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute("profile");
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, ",; ");
if (!this.getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + this.getReaderContext().getResource());
}
return;
}
}
}
// 解析前处理,留给子类实现
this.preProcessXml(root);
// 解析当前bean对象的方法。
this.parseBeanDefinitions(root, this.delegate);
// 解析后处理,留给子类实现
this.postProcessXml(root);
this.delegate = parent;
}
[1]解析bean
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
// Spring默认命名空间的xml进行解析。对beans处理
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for(int i = 0; i < nl.getLength(); ++i) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element)node;
if (delegate.isDefaultNamespace(ele)) {
// 对bean处理
this.parseDefaultElement(ele, delegate);
} else {
delegate.parseCustomElement(ele);//对自定义命名空间进行解析
}
}
}
} else {
delegate.parseCustomElement(root);
}
}
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
// 对不同的标签进行解析
if (delegate.nodeNameEquals(ele, "import")) {
this.importBeanDefinitionResource(ele);
} else if (delegate.nodeNameEquals(ele, "alias")) {
this.processAliasRegistration(ele);
} else if (delegate.nodeNameEquals(ele, "bean")) {
this.processBeanDefinition(ele, delegate);// 主要看对bean的解析
} else if (delegate.nodeNameEquals(ele, "beans")) {
this.doRegisterBeanDefinitions(ele);
}
}
//解析bean标签
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 将注册bean的任务委托给BeanDefinitionReaderUtils。
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.getReaderContext().getRegistry());
} catch (BeanDefinitionStoreException var5) {
this.getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, var5);
}
this.getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
[2] 封装BeanDefinition对象
BeanDefinitionHolder创建的BeanDefinition是xml中标签属性对象。
public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException {
String beanName = definitionHolder.getBeanName();
// 执行注册的方法 将bean封装为可以
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
String[] var4 = aliases;
int var5 = aliases.length;
for(int var6 = 0; var6 < var5; ++var6) {
String alias = var4[var6];
registry.registerAlias(beanName, alias);
}
}
}
[3] 将BeanDefinition装入Spring的beanDefinitionMap容器中。
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition)beanDefinition).validate();
} catch (BeanDefinitionValidationException var8) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", var8);
}
}
BeanDefinition existingDefinition = (BeanDefinition)this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
if (!this.isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
if (existingDefinition.getRole() < beanDefinition.getRole()) {
if (this.logger.isInfoEnabled()) {
this.logger.info("Overriding user-defined bean definition for bean '" + beanName + "' with a framework-generated bean definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]");
}
} else if (!beanDefinition.equals(existingDefinition)) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Overriding bean definition for bean '" + beanName + "' with a different definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]");
}
} else if (this.logger.isTraceEnabled()) {
this.logger.trace("Overriding bean definition for bean '" + beanName + "' with an equivalent definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]");
}
// 终点:将bean放入Spring容器中。此处
this.beanDefinitionMap.put(beanName, beanDefinition);
} else {
if (this.hasBeanCreationStarted()) {
synchronized(this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
this.removeManualSingletonName(beanName);
}
} else {
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
this.removeManualSingletonName(beanName);
}
this.frozenBeanDefinitionNames = null;
}
if (existingDefinition == null && !this.containsSingleton(beanName)) {
if (this.isConfigurationFrozen()) {
this.clearByTypeCache();
}
} else {
this.resetBeanDefinition(beanName);
}
}
6 总结
- Spring加载xml文件就通过一系列的准备工作,将xml中个标签对象封装为一个BeanDefinition对象,以beanName为key放入到Spring的beanDefinitionMap容器中。
二、Spring 通过注解加载bean
1.加载注解标注的类。MainConfig是通过注解注册在IOC容器中。
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
2. 执行register方法,注册bean到BeanDefinitionRegistry。
此步只注册了bean的定义信息,还没有创建对象。
后续对MainConfig的注册处理都是由AnnotatedBeanDefinitionReader类进行处理。最终注册到BeanDefinitionRegistry的实现类DefaultListableBeanFactory中。
在doRegisterBean方法做四件事
1)、将bean封装为AnnotatedGenvericBeanDefinition对象。
2)、获取该bean的名称MainCofig。此时是将bean的全路径引入的,通过以点进行分割获取末尾的字符串就是bean的名称。
3)、判断设置bean的作用域,采用代理模式进行判断设置,默认是Singlne单例。
4)、注册DefaultListableBeanFactory容器中beanDefinitionMap和beanDefinitionNames,以beanName为key,封装的BeanDefinition对象为Value注册进beanDefinitionMap。将beanName放入beanDefinitionNames中。
3.执行Refresh方法 创建对象 到ConfigurableListableBeanFactory容器中。
下面是refresh方法中需要执行的核心操作方法。
this.postProcessBeanFactory(beanFactory);
this.invokeBeanFactoryPostProcessors(beanFactory);
this.registerBeanPostProcessors(beanFactory);// 注册bean相关的后置处理器
this.initMessageSource();
this.initApplicationEventMulticaster();
this.onRefresh();
this.registerListeners();
this.finishBeanFactoryInitialization(beanFactory);// 该方法时进行bean对象的初始化创建。让面都是在创建bean对象前的一些用来扩展的操作。
this.finishRefresh();
(1)、注册bean的后置处理器到ConfigurableListableBeanFactory容器中
registerBeanPostProcessors(beanFactory):
- 获取BeanFactory工厂总所有类型为BeanPostProcessor的bean对象。
通过beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false)方法获取bean工厂中注册的所有BeanPostProcessor对象名称。
- 根据排序方式分类的先后顺序注册到ConfigurableListableBeanFactory容器中()。
优先注册实现PriorityOrdered接口的BeanPostProcessor、其次是实现Ordered接口的BeanPostProcessor、最后是没有实现优先级接口的BeanPostProcessor。
- 注册BeanPostProcessor,实际上就是创建BeanPostProcessor对象,保存到容器中。
- 创建BeanPostProcessor对象的过程就是和下面创建bean对象的过程一致。
(2)、创建(剩余的单实例)bean对象。
finishBeanFactoryInitialization:
-
先判断单例和多例。
- 要创建的对象是单例对象,则需要先判断容器中是否有该对象,没有则创建。
- 多例则直接创建。
-
创建Bean实例(doCreateBean方法其说明看 五–(七)-- 1)
-
populateBean给bean的各种属性赋值。
-
initializeBean 调用bean的初始化方法。(查看五–(七)-- 2)
// 创建bean的操作 { invokeAwareMethods(); //处理Aware接口的回调方法。 applyBeanPostProcessorsBeforeInitialization(bean, beanName);// 执行初始化前的后置处理器,这里面会进行遍历执行所有的postProcessBeforeInitialization后置处理器。 invokeInitMethods(beanName, wrappedBean, mbd);//调用初始化方法 applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);//调用初始化后的后置处理器,这里上面一样会遍历执行所有。 }
总结 我们可以在下面阶段接入自己的业务逻辑
//可以在下面这几个阶段加入自己的业务逻辑。可以IOC容器中bean对象。
this.postProcessBeanFactory(beanFactory);
this.invokeBeanFactoryPostProcessors(beanFactory);
this.registerBeanPostProcessors(beanFactory);
三、Spring IOC 解析
-
在准备阶段做的操作,是为了提高框架的可扩展性。
-
Spring提供的框架可扩展性?
- 在对象创建之前添加某些功能。
- 在容器初始化之前添加某些功能。
- 通过监听器。在不同的阶段发生不同的事件,完成一些功能。(观察者模式)
- 抽象出一堆接口,进行扩展。
- 面向接口编程。
IOC与DI的关系
IOC (Inversion Of Control)是控制反转,也被称为DI(Dependency Injection)依赖注入。他是对象定义其依赖关系的过程。对象只
和构造参数,工厂方法参数,对象实例属性或工厂方法返回相关。
(一)重要接口
- BeanFactory: 能管理任何类型的对象。提供了配置框架和基本功能。
- ApplicationContext:表示Spring IoC容器,并负责实例化、配置和组装bean。
(二)配置元数据(Configuration Metadata)
定义:程序员告诉Spring容器在应用程序中怎样实例化、配置和组装对象。即配置文件。
配置元数据有三种形式:XML形式、注解形式、Java配置类(需要借助 @Configuration
, @Bean
, @Import
, 和 @DependsOn
这几个注解)。
四、Spring重要接口
(一)BeanFactoryPostProcessor
-
允许自定义配置文件修改应用程序上下文的bean定义,调整上下文的底层bean工厂的bean属性值,但从不使用bean 的instance。
-
并且Spring的context会自动检测在加载bean之前先加载BeanFactoryPostProcessor中的beans。
-
可参阅PropertyResourceConfigurer及其具体实现,为满足这种配置需求的开箱即用的解决方案。
缺点:这样做可能会导致过早实例化一些bean,破坏容器并导致意外的副作用。
应用场景:可以在定时任务中使用该接口。对于某些定时任务进行配置。
在公司系统中:采用实现该接口,来完成对定时任务的调度任务工作,对于配置文件中设置的某些需要取消定时任务的进行调度处理。
五、重要注解/组件
(一)@Conditional
- 用在方法上:按照一定的条件进行判断,满足条件给容器中注册bean。
- 用在类上:按照一定的条件进行判断,满足条件才会加载这个类中的所有信息。
@Conditional(WindowCondition.class)
@Bean("maotao")
public Person test01() {
return new Person("毛涛",21);
}
// 判断条件
public class WindowCondition implements Condition {
/**
*
* @param conditionContext 条件上下文
* @param annotatedTypeMetadata 注解信息
* @return
*/
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
// 可以使用的组件
ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
ClassLoader classLoader = conditionContext.getClassLoader();
BeanDefinitionRegistry registry = conditionContext.getRegistry();
ResourceLoader resourceLoader = conditionContext.getResourceLoader();
Environment environment = conditionContext.getEnvironment();
// 获取操作系统名称
String environmentName = environment.getProperty("os.name");
// 判断容器中是否有bean,也可以注册bean
boolean zhangsan = registry.containsBeanDefinition("zhangsan");
if (!zhangsan) {
System.out.println("容器中没有张三");
}else{
// 注册张三
//registry.registerBeanDefinition("zhangsan",new B);
}
// 判断环境变量
if (environmentName.contains("Windows")) {
return true;
}
return false;
}
(二)@Lazy
懒加载,将单例的bean实例化方式改为懒加载,只有在第一次调用时才加载。
@Lazy
@Bean("person")
public Person test(){
System.out.println("正在创建:::::");
return new Person("wagnsan",20);
}
(三)@Scope
Spring设置bean在IOC容器中的作用域,默认是单例。
// prototype :多实例
// singleton : 单实例
// request :同一次请求创建一个实例
// session : 同一个session创建一个实例
//@Scope(value = "prototype")
@Lazy
@Bean("person")
public Person test(){
System.out.println("正在创建:::::");
return new Person("wagnsan",20);
}
(四)@ComponentScan
指定要扫描的包。
-
value是指定要扫描的包路径。
-
使用includeFilters,需要更改useDefaultFilters=false,禁用默认的过滤。
-
type 是选择过滤类型。
-
ANNOTATION, 注解类型 ASSIGNABLE_TYPE,指定类型 ASPECTJ,ASPECTJ表达式 REGEX,正则表达式 CUSTOM;自定义类型
-
@ComponentScan(value = "com.mt",includeFilters = {
//@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class, Service.class})}, //注解类型
@ComponentScan.Filter(type = FilterType.CUSTOM,classes = {MyFilter.class})},//自定义注册类型
useDefaultFilters = false
)
// 自己定义的过滤条件 需要实现Spring的TypeFilter
public class MyFilter implements TypeFilter {
/**
*
* @param metadataReader 读取到当前正在扫描的类信息
* @param metadataReaderFactory 可以获取到其他任何类的信息
* @return
* @throws IOException
*/
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
// 获取当前类注解的信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
// 获取当前正在扫描类的信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
// 获取类路径相关信息
Resource resource = metadataReader.getResource();
String className = classMetadata.getClassName();
System.out.println("->>>"+className);
return false;
}
}
(五)@Import给容器中注册组件的方式
-
包扫描(@ComponentScan)+组件标注注解(@Controller/@Service/@Repository/@Component)[适用于自己写的类]
-
@Bean[导入的第三方包组件]
-
@Import[快速给容器中导入一个组件]
-
@Import (要导入到容器中的组件),容器中就会自动注册这个组件,id默认是组件的全类名。
-
ImportSelector 返回需要导入组件的全类名数组。
-
ImportBeanDefinitionRegistrar 自己注册beanDefinition.
直接在类上使用该注解,引入需要的组件。
方式一 @Import({Color.class, Red.class}) public class MainConfig2 {
方式二 @Import({Color.class, Red.class, MyImportSelector.class}) public class MainConfig2 {
// 自定义逻辑需要导入的组件。 public class MyImportSelector implements ImportSelector { /** * * @param annotationMetadata 当前标注@Improt注解的类的所有注解信息 * @return 返回值,就是导入到容器中的组件全类名 */ @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { return new String[]{"com.mt.domain.Blue","com.mt.domain.Yellow"}; } }
方式三 @Import({Color.class, Red.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class}) public class MainConfig2 {
// 自定义要注册的组件。 public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { /** * * @param importingClassMetadata 当前类的注解信息 * @param registry BeanDefinitation注册类 所有需要添加到容器中的bean都需要在此注册 * 通过registry.registryregisterBeanDefinition手工注册。 */ @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(RainRow.class); registry.registerBeanDefinition("RainRow",rootBeanDefinition); } }
-
-
使用Spring提供的FactoryBean (工厂bean)
-
默认获取的是工厂bean提供的getObject()方法创建的对象。
-
要获取工厂bean本身,我们需要给id前面加一个&。
-
// Spring 提供的FactoryBean(工厂bean)
public class ColorFactory implements FactoryBean<Color>{
@Override
public Color getObject() throws Exception {
return new Color();
}
@Override
public Class<?> getObjectType() {
return Color.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
/**
* 注册bean
*/
@Bean
public ColorFactory ColorFactory() {
return new ColorFactory();
}
@Test
public void test06() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
Object colorFactory = applicationContext.getBean("ColorFactory");
System.out.println(colorFactory);// 返回的是getObject创建的Color对象
Object colorFactory = applicationContext.getBean("&ColorFactory");
System.out.println(colorFactory);// 返回的是ColorFactory对象
}
(六)bean的生命周期
实现自定义初始化和销毁:
- 指定初始化和销毁方法。
/**
* @ClassName: MainConfigLifeCycle
* @Description: bean生命周期的配置
* @Author: mao-tao
* @Date: 2020-10-21 18:38
* @Version: 1.0
* 容器管理bean的生命周期: 创建 -------- 初始化 ------- 销毁
* 我们可以自定义初始化和销毁方法。
* 构造(对象创建):
* 单实例:在容器启动时创建对象(默认)使用懒加载则与多实例一样。
* 多实例:在每次调用时创建对象。
* 初始化:
* 对象创建好,并赋值好,调用初始化方法。
* 销毁:
* 单实例,容器关闭时。
* 多实例,容器不会管理bean,不会调用销毁方法。
* 1)、指定初始化和销毁方法
* 使用@Bean的initMethod和destoryMethod指定。
**/
@Configuration
public class MainConfigLifeCycle {
//@Scope(value = "prototype")
//@Lazy
@Bean(value = "car",initMethod = "init",destroyMethod = "destroy")
public Car test01() {
return new Car();
}
}
// 自定义初始化和销毁方法
public class Car {
public Car(){
System.out.println("car ------ constructor ------");
}
public void init() {
System.out.println("car ------ init ------");
}
public void destroy() {
System.out.println("cr ------ destory ------");
}
}
- 实现接口方式
通过让bean实现InitializingBean(定义初始化逻辑)、DisposableBean(定义销毁逻辑)
public class Cat implements InitializingBean, DisposableBean {
public Cat() {
System.out.println("cat ------ contructor ------");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("cat ------ init ------");
}
@Override
public void destroy() throws Exception {
System.out.println("cat ------ destroy ------");
}
}
(七)后置处理器 BeanPostProcessor
1.在(doCreateBean方法)创建bean前后调用的后置处理器
BeanPostProcessor其子接口InstantiationAwareBeanPostProcessor。
@Nullable
default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
return null;
}
default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
return true;
}
// 会在doCreateBean之前调用,并且当该方法返回的beanInstance不为空则不会执行下面的doCreateBean方法,直接返回。
beanInstance = this.resolveBeforeInstantiation(beanName, mbdToUse);
beanInstance = this.doCreateBean(beanName, mbdToUse, args);
2. 在(initializeBean方法)调用bean的初始化方法前后调用的处理器。
initializeBean方法在doCreateBean方法中调用。
可以实现该接口,在调用bean的初始化方法前后添加自己的业务逻辑。
1)、简单例子
@Component
public class MyPostProcessor implements BeanPostProcessor {
/**
* 在bean初始化之前执行
* @param bean
* @param beanName
* @return
* @throws BeansException
*/
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println(beanName + "初始化之前" + bean);
return bean;
}
/**
* 在bean初始化之后执行
* @param bean
* @param beanName
* @return
* @throws BeansException
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println(beanName + "初始化之后" + bean);
return bean;
}
}
1)、底层实现原理
-
初始化bean前需要先创建bean,给bean赋值。
-
然后初始化bean,调用bean初始化前执行的后置处理器,
-
调用初始化方法,
-
调用bean初始化后执行的后置处理器。
populateBean(beanName, mbd, instanceWrapper);给bean进行属性赋值。
initializeBean(beanName, exposedObject, mbd);初始化bean ;下面时具体初始化bean内部逻辑。
下面申请后置处理器BeanPostProcessor时遍历获取所有的后置处理器,挨个执行,返回null则跳出循环。
{
invokeAwareMethods(); //处理Aware接口的回调方法。
applyBeanPostProcessorsBeforeInitialization(bean, beanName);// 执行初始化方法前的后置处理器,这里面会进行遍历执行所有的postProcessBeforeInitialization后置处理器。
invokeInitMethods(beanName, wrappedBean, mbd);//调用初始化方法
applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);//调用初始化方法后的后置处理器,这里上面一样会遍历执行所有。
}
Spring底层对BeanPostProcessor的应用
bean赋值、注入其他组件、@Autowired、生命周期注解功能,@Async等都应用后置处理器。
(八)@Autowired
/**@Autowired和@Qualifier一块使用可以指定注入的指定bean,当该接口的实现有多个时。
@Autowired可以标注在:构造器、参数、方法、属性上。都是从容器中获取参数组件的值。
(1)标注在方法位置: @Bean+方法参数;参数从容器中获取,默认@Autowired可以不写。
(2)标注在构造器上:若组件只有一个有参构造器,参数从容器中获取,当只有一个有参构造器,默认@Autowired可以不写。
(3)标注在参数位置
*/
Component
public class Boss {
private Car car;
//标注在有参构造器上 car从容器中获取。
//@Autowired
public Boss(@Autowierd Car car){// 标注在参数上
this.car = car;
System.out.println("执行有参构造器");
}
public Car getCar() {
return car;
}
// 标注在方法上 car从容器中获取。
//@Autowired
public void setCar(Car car) {
this.car = car;
System.out.println("booss.... 方法");
}
}
(九)自定义组件使用Spring底层组件
(Spring的ApplicationContext、beanFactory、等组件);
自定义组件实现xxxAware,在创建对象时,会调用组件规定的方法注入相关组件;
@Component
public class Red implements ApplicationContextAware {
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("获取到IOC容器"+applicationContext);
}
}
底层实现:
xxxAware 都借助xxxAwareProcessor后置处理器实现。
eg: ApplicationContextAware ====> ApplicationContextAwareProcessor。
(十)@profile
/**
* @Profile:
* Spring为我们提供的根据当前环境,动态的激活和切换一系列组件的功能。
* 开发环境、测试环境、生产环境
* 此处从配置文件中获取配置信息,分别通过三种方式:
* 1)、@Value 用在变量上。
* 2)、@Value 用在参数上。
* 3)、通过StringValueResolver,借助EmbeddedValueResolverAware接口
* 加了环境标识的类,只有这个环境被激活的情况下才能注册到容器中。默认是default环境。
*/
//PropertySource("classpath:/com/myco/app.properties") 引入外部配置文件.properties;这是源码给的例子
@PropertySource(value = {"classpath:/dbconfig.properties"})
@Configuration
public class MainConfigProfile implements EmbeddedValueResolverAware {
@Value("${db.user}")
private String user;
private StringValueResolver valueResolver;
String driveClass;
@Profile(value = "test") //设置生效的环境
@Bean(value = "testDataSource")
public DataSource dataSourceTest(@Value("${db.password}") String pwd) throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(pwd);
dataSource.setDriverClass(driveClass);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
return dataSource;
}
@Profile(value = "dev")
@Bean(value = "devDataSource")
public DataSource dataSourceDev(@Value("${db.password}") String pwd) throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(pwd);
dataSource.setDriverClass(driveClass);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/dev");
return dataSource;
}
@Profile(value = "pro")
@Bean(value = "proDataSource")
public DataSource dataSourcePro(@Value("${db.password}") String pwd) throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(pwd);
dataSource.setDriverClass(driveClass);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/pro");
return dataSource;
}
@Override
public void setEmbeddedValueResolver(StringValueResolver stringValueResolver) {
this.valueResolver = stringValueResolver;
driveClass = valueResolver.resolveStringValue("${db.driveClass}");
}
}
// 代码设置激活环境
@Test
public void test01(){
// 因为需要配置激活的环境,则需要applicationContext的无参构造器对象;显示设置激活环境
// AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigProfile.class);
// 1. 创建容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 2. 设置激活环境
applicationContext.getEnvironment().setActiveProfiles("test");// 设置激活的环境
// 3. 注册配置类
applicationContext.register(MainConfigProfile.class);
// 4. 启动刷新容器
applicationContext.refresh();
}
(十一)@Aspect
标注该注解的类代表切面类。
1. AOP切面的使用
// 业务类
@Controller
public class UserController {
public Integer MathCalculator(int i, int j) {
return (i/j);
}
}
/**
* @ClassName: MainConfigAOP
* @Description: AOP切面配置类,将业务类和切面类加入到容器中
* @Author: mao-tao
* @Date: 2020-10-23 12:47
* @Version: 1.0
* AOP 、【动态代理】 指在程序运行过程中动态的将某段代码切入到指定位置进行运行的编码方式。
* 1. 导入AOP模块,Spring AOP,(spring-aspect)
* 2. 定义一个业务逻辑类
* 3. 定义一个日志切面类 (LogAspect):切面类里面的方法需要动态感知目标方法 UserController.MathCalculator方法
* 通知方式:
* 前置通知(@Before):在目标方法执行前通知
* 后置通知(@After): 在目标方法执行结束后通知
* 返回通知(@AfterReturning): 在目标方法正常返回后通知
* 异常通知(@AfterThrowing): 在目标方法出现异常以后通知
* 环绕通知(@Around): 动态代理,手动进行目标方法运行(joinPoint.procced())
*
* 4. 给切面类的目标方法标注何时何地运行。
* 5. 将切面和业务类加入到容器中。
* 6. 给切面类加@Aspect注解。
* 7. 给配置类加@EnableAspectJAutoProxy (开启基于注解的AOP模式)
*
* 概括三步:
* 第一步:将业务逻辑和切面类加入容器中。
* 第二步:给切面的每一个通知方法上加通知注解。告诉Spring何时何地运行。
* 第三步:开启基于注解的AOP模式@EnableAspectJAutoProxy
*
**/
@EnableAspectJAutoProxy
@Configuration
public class MainConfigAOP {
// 注册业务
@Bean
public UserController userController(){
return new UserController();
}
// 注册切面
@Bean
public LogAspect logAspect() {
return new LogAspect();
}
}
@Aspect // 声明一个AOP切面类
public class LogAspect {
// 抽取公共的切入点表达式
@Pointcut("execution(public Integer com.mt.controller.UserController.*(..))")
public void pointCut(){
}
// JoinPoint参数必须卸载参数列表第一个
// 当外部类需要引用该切面可以如下
// 目标方法之前切入
@Before(value = "com.mt.aop.LogAspect.pointCut()")
public void logStart(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
String name = joinPoint.getSignature().getName();
System.out.println("log ---- 开始运行 ---- "+name+"参数列表---{"+ Arrays.asList(args)+"}");
}
// 目标发方法之后切入
// 内部类引用则可以直接写方法名
@After(value = "pointCut()")
public void logEnd() {
System.out.println("log---- 运行结束");
}
// 异常返回
@AfterThrowing(value = "pointCut()", throwing = "exception")
public void logExceptReturn(JoinPoint joinPoint,Exception exception) {
System.out.println("log ---"+joinPoint.getSignature().getName()+" 异常返回 ---- {"+exception+"}");
}
//正常返回
@AfterReturning(value = "pointCut()", returning = "result")
public void logReturn(JoinPoint joinPoint,Object result) {
System.out.println("log ---"+joinPoint.getSignature().getName()+" 正常返回 --- {"+result+"}");
}
}
2. AOP切面原理
关键在@EnableAspectJAutoProxy注解。
-
该注解主要为了将自动代理创建器AnnotationAwareAspectJAutoProxyCreator注册到容器中BeanDefinitionRegistry中,以internalAutoProxyCreator为key。
自动代理创建器的作用:
// AnnotationAwareAspectJAutoProxyCreator的继承父类
AnnotationAwareAspectJAutoProxyCreator
-- AspectJAwareAdvisorAutoProxyCreator
-- AbstractAdvisorAutoProxyCreator
--- AbstractAutoProxyCreator
** implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware
//重点关注后置处理器(bean初始化前后做的操作)和 自动装配 Beanfactory
从下往上分析我们关注的后置处理器和BeanFactory相关处理:
- AbstractAutoProxyCreator.setBeanFactory();
AbstractAutoProxyCreator.后置处理器的逻辑(before和after)
- AbstractAdvisorAutoProxyCreator.setBeanFactory() ====> initBeanFactory(); // 子类重写了父类的方法。
- AnnotationAwareAspectJAutoProxyCreator.initBeanFactory(); 子类重写了父类的方法
分别在上面这几个方法体中断点调试。
AnnotationAwareAspectJAutoProxyCreator间接实现了InstantiationAwareBeanPostProcessor。
每一个bean创建之前,调用postProcessBeforeInstantiation()方法。
- 判断当前bean是否在adviseBeans增强器中,该容器中存在这所有需要加强的类。
- 判断当前类是否是切面(@Aspect)。
- 获取切面中的通知方法(@Before、@After等标注的方法)。
,Object result) {
System.out.println(“log —”+joinPoint.getSignature().getName()+" 正常返回 — {"+result+"}");
}
}
##### 2. AOP切面原理
关键在@EnableAspectJAutoProxy注解。
- 该注解主要为了将自动代理创建器AnnotationAwareAspectJAutoProxyCreator注册到容器中BeanDefinitionRegistry中,以internalAutoProxyCreator为key。
-
自动代理创建器的作用:
```java
// AnnotationAwareAspectJAutoProxyCreator的继承父类
AnnotationAwareAspectJAutoProxyCreator
-- AspectJAwareAdvisorAutoProxyCreator
-- AbstractAdvisorAutoProxyCreator
--- AbstractAutoProxyCreator
** implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware
//重点关注后置处理器(bean初始化前后做的操作)和 自动装配 Beanfactory
从下往上分析我们关注的后置处理器和BeanFactory相关处理:
- AbstractAutoProxyCreator.setBeanFactory();
AbstractAutoProxyCreator.后置处理器的逻辑(before和after)
- AbstractAdvisorAutoProxyCreator.setBeanFactory() ====> initBeanFactory(); // 子类重写了父类的方法。
- AnnotationAwareAspectJAutoProxyCreator.initBeanFactory(); 子类重写了父类的方法
分别在上面这几个方法体中断点调试。
AnnotationAwareAspectJAutoProxyCreator间接实现了InstantiationAwareBeanPostProcessor。
每一个bean创建之前,调用postProcessBeforeInstantiation()方法。
- 判断当前bean是否在adviseBeans增强器中,该容器中存在这所有需要加强的类。
- 判断当前类是否是切面(@Aspect)。
- 获取切面中的通知方法(@Before、@After等标注的方法)。
3.总结
1)、 使用@EnableAspectJAutoProxy开启AOP功能。
2)、该注解给容器中注册一个AnnotationAwareAspectJAutoProxyCreator后置处理器。
3)、执行容器的创建流程:
(1)、registerBeanPostProcessors()注册后置处理器;创建AnnotationAwareAspectJAutoProxyCreator对象。
(2)、finishBeanFactoryInitialization()注册剩下的单例bean;
- 创建业务组件和切面组件。
- AnnotationAwareAspectJAutoProxyCreator拦截组件的创建。
- 拦截组件创建完成,将切面的通知方法,包装成增强器(Advisor)给业务组件创建代理对象(jdk代理和Cjlib代理)。
3)、执行目标方法。
(1)、代理对象执行目标方法。