我的網站原文:chenmingyu.top/spring-sour…java
本文首先提供了一個實現了spring aop的demo,經過demo進行源碼分析node
經過讀源碼咱們能夠學習到spring是如何解析xml的,如何加載bean的,如何建立bean的,又是如何實現aop操做的,及其中各類操做的細節是如何實現的git
講源碼的時候我會進行一些取捨,根據上面的問題結合demo對主要流程進行講解,爭取能把上述的問題說明白github
源碼地址:github.com/mingyuHub/s…spring
代碼:數組
UserController
,提供一個方法login
UserAspect
,切入點爲login
方法spring-aop.xml
將類加載到spring容器中建立UserController
類緩存
public class UserController {
public void login(){
System.out.println("登陸");
}
}
複製代碼
定義一個切面UserAspect
,不瞭解aop概念的能夠看一下:chenmingyu.top/springboot-…springboot
/** * @author: chenmingyu * @date: 2019/3/19 18:29 * @description: */
@Aspect
public class UserAspect {
/** * 切入點 */
@Pointcut("execution(public * com.my.spring.*.*(..))")
public void execute(){
}
/** * 前置通知 * @param joinPoint */
@Before(value ="execute()")
public void Before(JoinPoint joinPoint) {
System.out.println("執行方法以前");
}
/** * 後置通知 * @param joinPoint */
@After(value ="execute()")
public void After(JoinPoint joinPoint) {
System.out.println("執行方法以後");
}
}
複製代碼
自定義一個xml文件,名爲spring-aop.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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
">
<aop:aspectj-autoproxy proxy-target-class="true"/>
<bean id="userController" class="com.my.spring.UserController"/>
<bean id="userAspect" class="com.my.spring.UserAspect"/>
</beans>
複製代碼
調試的代碼已經準備好,首先寫一個測試類測一下app
@Test
public void test(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-aop.xml");
UserController userController = (UserController) applicationContext.getBean("userController");
userController.login();
}
複製代碼
正常狀況下的輸出以下:
開始學習spring的源碼以前,有必要先了解一下spring中幾個較爲核心的類
先大概瞭解一下這些類是幹什麼的,沒必要深究,後續讀源碼的時候碰到會着重講解一下
先看一個spring容器的類圖:
BeanFactory
工廠類的頂級接口,用於獲取bean及bean的各類屬性,提供了ioc容器最基本的形式,給具體的IOC容器的實現提供了規範
ListableBeanFactory、HierarchicalBeanFactory 和 AutowireCapableBeanFactory是BeanFactory接口的子接口,最終的默認實現類是 DefaultListableBeanFactory,定義這多接口主要是爲了區分在 Spring 內部在操做過程當中對象的傳遞和轉化過程當中,對對象的數據訪問所作的限制
DefaultListableBeanFactory
ioc容器的實現,DefaultListableBeanFactory做爲一個能夠獨立使用的ioc容器,是整個Bean加載的核心部分,是spring註冊及加載bean的默認實現
xmlBeanFactory
xmlBeanFactory繼承DefaultListableBeanFactory,對其進行了擴展,增長了自定義的xml讀取器XmlBeanDefinitionReader
,實現了個性化的BeanDefinitionReader讀取,主要做用就是將xml配置解析成BeanDefinition
ApplicationContext
ApplicationContext繼承自BeanFactory,包含BeanFactory的全部功能,狀況下都使用這個
BeanDefinition
Spring中用於包裝Bean的數據結構
BeanDefinitionRegistory
定義對BeanDefinition的各類增刪操做
BeanDefinitionReader
定義了讀取BeanDefinition的接口,主要做用是從資源文件中讀取Bean定義,XmlBeanDefinitionReader是其具體的實現類
SingletonBeanRegistry
定義對單例的註冊及獲取
AliasRegistry
定義對alias的簡單增刪改操做
瞭解一些核心類以後咱們就要開始讀源碼了
咱們的源碼以測試類爲入口開始分析
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-aop.xml");
複製代碼
ApplicationContext
和BeanFactory
都是用於加載Bean的,相比之下ApplicationContext
提供了更多的擴展功能
ClassPathXmlApplicationContext
最終調用下面這個構造函數
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {
super(parent);
//設置配置文件
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
複製代碼
refresh()
是ApplicationContext
的核心方法,這個方法基本包含了ApplicationContext
提供的所有功能
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 準備刷新此上下文,不重要.
prepareRefresh();
// 初始化bean工廠,加載xml,解析默認標籤,解析自定義標籤,註冊BeanDefinitions
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 設置bean工廠的屬性,進行功能填充.
prepareBeanFactory(beanFactory);
try {
// 子類覆蓋方法作額外的處理.
postProcessBeanFactory(beanFactory);
// 激活bean處理器.
invokeBeanFactoryPostProcessors(beanFactory);
// 註冊攔截bean建立的bean處理器,只是註冊
registerBeanPostProcessors(beanFactory);
// 初始化message源.
initMessageSource();
// 初始化應用消息廣播器
initApplicationEventMulticaster();
// 留給子類加載其餘bean.
onRefresh();
// 註冊Listeners bean,到消息廣播器
registerListeners();
// 實例化全部剩餘的(非lazy init)單例.
finishBeanFactoryInitialization(beanFactory);
// 刷新通知.
finishRefresh();
}
catch (BeansException ex) {
logger.warn("Exception encountered during context initialization - cancelling refresh attempt", ex);
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
}
}
複製代碼
加載解析xml和註冊BeanDefinition的邏輯都在obtainFreshBeanFactory()
方法中,這個方法的做用是初始化bean工廠,加載xml,解析默認標籤和自定義標籤,將解析出來的BeanDefinition
註冊到容器中
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
// 核心邏輯:建立bean工廠,解析xml,註冊BeanDefinition
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
------------------調用下面這個方法------------------
//調用AbstractRefreshableApplicationContext的refreshBeanFactory()
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
//建立bean工廠,類型爲DefaultListableBeanFactory
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
//核心邏輯:加載BeanDefinitions,進入這個方法
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
複製代碼
createBeanFactory()
方法邏輯特別簡單,咱們詳細說一下loadBeanDefinitions(beanFactory)
方法
在loadBeanDefinitions()
這個方法實例化了一個XmlBeanDefinitionReader,介紹XmlBeanDefinitionReader
以前須要介紹一下什麼是BeanDefinition
BeanDefinition:Bean的定義主要由BeanDefinition
來描述的。做爲Spring中用於包裝Bean的數據結構
BeanDefinition
做爲頂級接口 ,擁有三種實現:RootBeanDefinition
,ChildBeanDefinition
,GenericBeanDefinition
,三種BeanDefinition
均繼承了AbstractBeanDefinition
spring經過BeanDefinition
將配置文件中的標籤轉換爲容器的內部表示,並將BeanDefinition
註冊到BeandefinitionRegistry
中,spring中的容器主要以map的形式進行存儲BeanDefinition
再介紹一下BeanDefinitionReader:
BeanDefinitionReader解決的是從資源文件(xml,propert)解析到BeanDefinition
的過程,因此XmlBeanDefinitionReader的做用就很明顯了,將xml轉爲BeanDefinition
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// 根據beanFactory建立XmlBeanDefinitionReader
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// 加載環境變量啥的
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// initBeanDefinitionReader
initBeanDefinitionReader(beanDefinitionReader);
//核心邏輯在這個方法裏,加載beanDefinitions
loadBeanDefinitions(beanDefinitionReader);
}
複製代碼
在loadBeanDefinitions(beanDefinitionReader);
方法中咱們層層調用,發現最終解析xml調用的方法是doLoadBeanDefinitions(InputSource inputSource, Resource resource)
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
try {
//將xml轉成document對象
Document doc = doLoadDocument(inputSource, resource);
//核心邏輯在這個方法裏,解析Document,註冊beanDefinition
return registerBeanDefinitions(doc, resource);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
......省略其餘異常信息
}
複製代碼
registerBeanDefinitions(doc, resource);
方法中最終調用doRegisterBeanDefinitions(Element root)
將由xml
轉出來的Document
(經過doc.getDocumentElement()
取到Element 元素)解析成beandefinition
並註冊
protected void doRegisterBeanDefinitions(Element root) {
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
return;
}
}
}
//解析前置操做,留給子類實現
preProcessXml(root);
// 核心邏輯:解析並註冊BeanDefinition
parseBeanDefinitions(root, this.delegate);
//解析後置操做,留給子類實現
postProcessXml(root);
this.delegate = parent;
}
複製代碼
經歷層層調用咱們終於找到了核心方法,解析beanDefinition
,咱們看看它是如何進行解析的
spring中的標籤包括默認標籤和自定義標籤兩種,可是兩種標籤的解析方式有很大的區別
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
//根據root元素的namespace是否等於http://www.springframework.org/schema/beans
//經過上面的判斷,肯定是不是屬於spring的默認標籤
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)) {
//默認標籤解析
parseDefaultElement(ele, delegate);
}
else {
//自定義標籤解析
delegate.parseCustomElement(ele);
}
}
}
}
else {
//自定義標籤解析
delegate.parseCustomElement(root);
}
}
複製代碼
parseDefaultElement()
方法提供了對import,alias,bean,beans標籤的解析
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
//解析<import>標籤
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
//解析<alias>標籤
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
//解析<bean>標籤
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
//解析<beans>標籤
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
複製代碼
詳細的講解一下對bean
標籤的解析
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//步驟一,將Element解析成BeanDefinitionHolder.
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
// 對 bdHolder 進行裝飾,針對自定義的屬性進行解析,根據自定義標籤找到對應的處理器,進行解析(自定義解析方式下面會細說)
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
//步驟二,註冊解析出來的BeanDefinition.
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
複製代碼
步驟一:先介紹下BeanDefinitionHolder:這個類是一個工具類,做用是承載BeanDefinition數據的
public class BeanDefinitionHolder implements BeanMetadataElement {
private final BeanDefinition beanDefinition;
private final String beanName;
private final String[] aliases;
...
}
複製代碼
delegate.parseBeanDefinitionElement(ele);
方法中將Element
轉化爲BeanDefinitionHolder,而且識別出bean的beanName和aliases(別名),獲得BeanDefinitionHolder
其實默認標籤的解析就已經結束了
這個方法沒有啥複雜邏輯,挺清晰的
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}
----------------------調用下面這個方法------------------------------
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
//獲取id屬性
String id = ele.getAttribute(ID_ATTRIBUTE);
//獲取name屬性
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
List<String> aliases = new ArrayList<String>();
//若是name屬性配置的不爲空
if (StringUtils.hasLength(nameAttr)) {
//按,; 分割成字符串數組
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
//加到別名的集合裏
aliases.addAll(Arrays.asList(nameArr));
}
//把id屬性賦值給beanName,若是id爲空就在aliases別名的集合裏取第一個
String beanName = id;
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
beanName = aliases.remove(0);
if (logger.isDebugEnabled()) {
logger.debug("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}
if (containingBean == null) {
//檢查beanName和aliases是否已經使用,若是使用了就報異常,沒使用就加到一個
checkNameUniqueness(beanName, aliases, ele);
}
//建立了一個GenericBeanDefinition類型的BeanDefinition,並對個屬性進行填充
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
beanName = this.readerContext.generateBeanName(beanDefinition);
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Neither XML 'id' nor 'name' specified - " +
"using generated bean name [" + beanName + "]");
}
}
catch (Exception ex) {
error(ex.getMessage(), ele);
return null;
}
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
//建立一個BeanDefinitionHolder返回
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
複製代碼
獲得BeanDefinitionHolder
後,剩下的就是註冊BeanDefinition
了
步驟二,調用BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry())
方法,註冊BeanDifinition
public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException {
// 使用beanName作惟一標識註冊
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// 使用全部別名進行註冊
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
複製代碼
經過beanName進行註冊:definitionHolder.getBeanName()
,默認標籤和自定義標籤都使用這個方法進行BeanDefinition
的註冊
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");
// beanDefinition是否屬於AbstractBeanDefinition的實例
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
// 進行校驗,主要是校驗methodOverrides與工程方法是否存在以及methodOverrides對應的方法存不存在
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
BeanDefinition oldBeanDefinition;
// 經過beanName獲取BeanDefinition是否已經註冊
oldBeanDefinition = this.beanDefinitionMap.get(beanName);
//若是已經註冊而且不容許覆蓋就拋出異常
if (oldBeanDefinition != null) {
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
"': There is already [" + oldBeanDefinition + "] bound.");
}
else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
if (this.logger.isWarnEnabled()) {
this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
"' with a framework-generated bean definition: replacing [" +
oldBeanDefinition + "] with [" + beanDefinition + "]");
}
}
else {
if (this.logger.isInfoEnabled()) {
this.logger.info("Overriding bean definition for bean '" + beanName +
"': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
}
}
}
else {
//註冊BeanDefinition
this.beanDefinitionNames.add(beanName);
this.manualSingletonNames.remove(beanName);
this.frozenBeanDefinitionNames = null;
}
this.beanDefinitionMap.put(beanName, beanDefinition);
// 若是oldBeanDefinition經過上述校驗沒拋出異常或者beanName是單例
if (oldBeanDefinition != null || containsSingleton(beanName)) {
//則更新對應的緩存
resetBeanDefinition(beanName);
}
}
複製代碼
經過this.beanDefinitionMap.put(beanName, beanDefinition)
這行代碼咱們就知道,spring使用一個叫beanDefinitionMap
的ConcurrentHashMap來存儲解析出來的beanDefinition
Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(64);
經過別名註冊的方式跟經過beanName註冊的區別不大,仔細看下registry.registerAlias(beanName, alias);
方法應該就能瞭解
spring默認標籤的解析大體流程就是這樣,細枝末節並無特別詳細的講解,不過這並不會對咱們理解spring的總體流程有阻礙,你們可自行看一下,代碼邏輯也不是特別複雜
自定義標籤解析的時候會先根據從Element
獲取到的namespaceUri
獲取到對應的NamespaceHandler
,根據NamespaceHandler
進行自定義的解析,以aop爲例,咱們在配置文件中配置了<aop:aspectj-autoproxy proxy-target-class="true"/>
,解析會根據aspectj-autoproxy
找到對應的處理器,而後調用其parse
方法建立
delegate.parseCustomElement(root);
定義了自定義標籤解析的流程
public BeanDefinition parseCustomElement(Element ele) {
return parseCustomElement(ele, null);
}
-----------------------調用下面這個方法-----------------------
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
//獲取命名空間
String namespaceUri = getNamespaceURI(ele);
//步驟一,根據命名空間找到對應的NamespaceHandler
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
//步驟二,根據自定義的NamespaceHandler進行解析
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
複製代碼
步驟一:有了namespaceUri
咱們就能夠根據this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri)
方法獲取對應NamespaceHandler
的實例
//DefaultNamespaceHandlerResolver.java
public NamespaceHandler resolve(String namespaceUri) {
//1,獲取到全部的解析器
Map<String, Object> handlerMappings = getHandlerMappings();
//2,根據 namespaceUri 獲取到對應的handle
Object handlerOrClassName = handlerMappings.get(namespaceUri);
if (handlerOrClassName == null) {
return null;
}
else if (handlerOrClassName instanceof NamespaceHandler) {
//3,強轉返回
return (NamespaceHandler) handlerOrClassName;
}
else {
//4,根據handlerOrClassName實例化NamespaceHandler
String className = (String) handlerOrClassName;
try {
Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
"] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
}
NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
//調用自定義的NamespaceHandler的初始化方法
namespaceHandler.init();
//添加到緩存裏
handlerMappings.put(namespaceUri, namespaceHandler);
return namespaceHandler;
}
catch (ClassNotFoundException ex) {
throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" +
namespaceUri + "] not found", ex);
}
catch (LinkageError err) {
throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" +
namespaceUri + "]: problem with handler class file or dependent class", err);
}
}
}
複製代碼
這個過程仍是比較清晰的,debug一下:
這個handlerMappings.get(namespaceUri)
取到是字符串:org.springframework.aop.config.AopNamespaceHandler
,接下來按流程走調用BeanUtils.instantiateClass(handlerClass)
就是實例化這個類,而後調用它的init
方法,而後加到緩存裏,而後返回這個NamespaceHandler
的實例
public class AopNamespaceHandler extends NamespaceHandlerSupport {
/** * Register the {@link BeanDefinitionParser BeanDefinitionParsers} for the * '{@code config}', '{@code spring-configured}', '{@code aspectj-autoproxy}' * and '{@code scoped-proxy}' tags. */
@Override
public void init() {
// In 2.0 XSD as well as in 2.1 XSD.
registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
// Only in 2.0 XSD: moved to context namespace as of 2.1
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
}
}
複製代碼
註冊BeanDefinitionParser
,就是放到一個叫parsers
的HashMap裏,總共4個config
,aspectj-autoproxy
,scoped-proxy
,spring-configured
protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
this.parsers.put(elementName, parser);
}
複製代碼
步驟二:返回NamespaceHandler
實例以後調用它的parse
方法
handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
咱們的配置文件中只有一個自定義標籤:<aop:aspectj-autoproxy proxy-target-class="true"/>
因此findParserForElement(element, parserContext)
這個方法根據標籤aspectj-autoproxy
取到的是取到的BeanDefinition
是:AspectJAutoProxyBeanDefinitionParser
//NamespaceHandlerSupport.java
//獲取到對應解析器的BeanDefinition,調用其parse方法
//好比aspectj-autoproxy標籤對應AspectJAutoProxyBeanDefinitionParser
public BeanDefinition parse(Element element, ParserContext parserContext) {
return findParserForElement(element, parserContext).parse(element, parserContext);
}
--------------------調用下面這個方法-----------------------
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
String localName = parserContext.getDelegate().getLocalName(element);
BeanDefinitionParser parser = this.parsers.get(localName);
if (parser == null) {
parserContext.getReaderContext().fatal(
"Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
}
return parser;
}
複製代碼
AspectJAutoProxyBeanDefinitionParser
實現BeanDefinitionParser
接口
BeanDefinitionParser
接口中只定義了一個parse
方法,全部自定義處理器都須要實現BeanDefinitionParser
接口進行自定標籤的解析
接下來咱們看下AspectJAutoProxyBeanDefinitionParser
類的parse
方法
//AspectJAutoProxyBeanDefinitionParser.java
public BeanDefinition parse(Element element, ParserContext parserContext) {
//註冊 AspectJAnnotationAutoProxyCreator
AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
extendBeanDefinition(element, parserContext);
return null;
}
複製代碼
主要的邏輯就在註冊AspectJAnnotationAutoProxyCreator
這個方法上
public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary( ParserContext parserContext, Element sourceElement) {
//核心邏輯:註冊或升級AutoProxyCreator定義beanName爲org.springframework.aop.config.internalAutoProxyCreator的BeanDefinition
BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
parserContext.getRegistry(), parserContext.extractSource(sourceElement));
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
registerComponentIfNecessary(beanDefinition, parserContext);
}
複製代碼
這個方法調用了registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source)
方法
調用的這個方法邏輯也特別清晰
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
-----------------------調用下面這個方法---------------------
private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
//判斷BeanDefinitionRegistry是否包含AUTO_PROXY_CREATOR_BEAN_NAME這個靜態變量
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
//包含說明就註冊過,將這個BeanDefinition取出來,而後判斷BeanClassName若是不相等,從新BeanClassName爲cls.getName()
BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
int requiredPriority = findPriorityForClass(cls);
if (currentPriority < requiredPriority) {
apcDefinition.setBeanClassName(cls.getName());
}
}
return null;
}
//若是不包含就建立一個RootBeanDefinition,填充屬性而後註冊
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
//這個註冊方法咱們前面講默認標籤註冊BeanDefinition的時候講過,用的一個方法
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}
複製代碼
自定義標籤解析註冊BeanDefinition
的過程咱們也講解完了
如今咱們知道了spring是若是解析默認標籤和自定義標籤的了,總體流程仍是比較清晰的
總結一下spring如何加載xml及註冊BeanDefinition:
首先將xml文件轉化爲Element對象,獲取命名空間,根據命名空間判斷是spring的默認標籤仍是自定義標籤
默認標籤:使用spring的流程進行處理,遇到默認標籤首先判斷是哪一種標籤,import,alias,bean,beans標籤都有着不一樣的解析處理邏輯,解析成BeanDefinition以後進行註冊,註冊的過程就是放到一個ConcurrentHashMap
裏
自定義標籤:使用自定義的命名空間處理器(實現了NamespaceHandler
接口)進行解析註冊處理
首先根據namespaceUri
找到對應的NamespaceHandler
處理器
而後調用它的init方法,註冊對應自定義標籤的解析器(好比aspectj-autoproxy
對應AspectJAutoProxyBeanDefinitionParser
)
調用NamespaceHandler
的parse
方法,在這個方法里根據自定義標籤找到對應的解析器,調用對應的解析器的parse
方法進行註冊BeanDefinition
本想一篇文章把全部的問題都說明白,發現寫完一個問題篇幅就比較長了
那關於spring是如何加載bean的,如何建立bean的,又是如何實現aop操做的,咱們下篇分解
以爲寫得還能夠記得點關注,下次更新文章直接就能看到了