在SpringAOP初探中,我们大概理解了aop的用法,现在从源码的角度来解析aop的运作原理。很巧的是,在spring源码(一)获取bean中我们分析了怎么获取bean的源码,现在通过xml解析的方式来了解在获取bean之前,这些在xml中定义的bean是怎么被解析、加载的。
基本概念:
IOC: (Inversion of Control) 指控制反转或反向控制。在Spring框架中我们通过配置创建类对象,由Spring容器在运行阶段实例化、组装对象。
DI: (dependency inject ) 依赖注入。向类的属性设置值(设置具体的值,或另一个bean)
IOC和DI关系密切。IOC形容一种状态,DI是形容一个过程。DI是在IOC基础才能实现。
DI分为手动装配和自动装配
-
手动装配:一般进行配置信息都采用手动。基于xml装配:构造方法、setter方法
-
自动装配(基于注解装配)
1、手动装配
<bean> 标签的属性有:
(1)id属性:用于指定配置对象的名称,不能包含特殊符号。
(2)class属性:创建对象所在类的全路径。
(3)name属性:功能同id属性一致。但是在name属性值中可以包含特殊符号。
(4)scope属性
-
singleton:默认值,单例
单例模式下,在程序下只有一个实例。非单态模式下,每次请求该Bean,都会生成一个新的对象。
-
prototype:多例
-
request:创建对象后将对象存放到request域
-
session:创建对象后将对象存放到session域
-
globalSession:创建对象后将对象存放到globalSession域
初始化bean的时候,有多种方式可以设置属性值:
假设现在有一个类,RedisCacheUtil ,现在用xml的方式配置这个bean
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
public class RedisCacheUtil {
private static final Logger logger = LoggerFactory.getLogger(RedisCacheUtil.class);
private String access;
private String secret;
private Map<String, String> map;
private SpringContextUtil springContextUtil;
RedisCacheUtil(){
}
RedisCacheUtil(String access,String secret){
this.access = access;
this.secret = secret;
}
public void init(){
logger.info("this is RedisCacheUtil.init");
logger.info("access",access);
logger.info("secret",secret);
}
public Map<String, String> getMap() {
return map;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public SpringContextUtil getSpringContextUtil() {
return springContextUtil;
}
public void setSpringContextUtil(SpringContextUtil springContextUtil) {
this.springContextUtil = springContextUtil;
}
public String getAccess() {
return access;
}
public void setAccess(String access) {
this.access = access;
}
public String getSecret() {
return secret;
}
public void setSecret(String secret) {
this.secret = secret;
}
public static void main(String[] args) {
}
}
(1)构造方法的方式
可以用索引,也可以用name
<bean id="redisCacheUtil" class="com.cainiao.arrow.arrowcommon.util.RedisCacheUtil" init-method="init">
<constructor-arg index="0">
<value>ddddd</value>
</constructor-arg>
<constructor-arg name="secret">
<value>ccccc</value>
</constructor-arg>
</bean>
(2)使用set方法注入属性
用 <property> 标签代表属性。注意<property> 标签依赖set,所以bean里要有set方法。
对应较为复杂的类型,也可以加对应的标签,赋值。例如map、List都是支持的
<bean id="redisCacheUtil" class="com.cainiao.arrow.arrowcommon.util.RedisCacheUtil" init-method="init">
<property name="access" value="dsdds"/>
<property name="secret" value="33333"/>
<property name="map">
<map>
<entry key="key1" value="Value1"/>
<entry key="key2" value="Value2"/>
<entry key="key3" value="Value3"/>
</map>
</property>
</bean>
(3)注入对象类型属性
有时候引用了别的bean,此时就需要ref指向bean
<bean id="springContextUtil" class="com.cainiao.arrow.arrowcommon.util.SpringContextUtil">
</bean>
<bean id="redisCacheUtil" class="com.cainiao.arrow.arrowcommon.util.RedisCacheUtil" init-method="init">
<property name="access" value="dsdds"/>
<property name="secret" value="33333"/>
<property name="springContextUtil" ref="springContextUtil"/>
</bean>
springContextUtil也可以是注解形式的,只要ref对应的bean名称能对应的上,就可以成功加载。
2.自动装配
自动装配的实现是基于注解装配,常用的@Service @Component 都可以装配一个bean。
详细的注解可以看 https://blog.csdn.net/gq0811/article/details/107876464
3.代码解析
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/beans.xml");
RedisCacheUtil redisCacheUtil = (RedisCacheUtil)context.getBean("redisCacheUtil");
System.out.printf(redisCacheUtil.getAccess());
}
用上述代码来追溯一下,解析的过程。
大概看一下ClassPathXmlApplicationContext的继承关系
BeanFactory是Spring的心脏,ApplicationContext就是完整的躯体。ApplicationContext继承了BeanFactory,ResourceLoader提供了类的管理以及资源的加载,同时又继承了EnvironmentCapable,MessageSource, ApplicationEventPublisher等,将环境、消息、事件等统一管理。
继承关系:
ClassPathXmlApplicationContext --》 AbstractXmlApplicationContext --》AbstractRefreshableConfigApplicationContext
除了ClassPathXmlApplicationContext,FileSystemXmlApplicationContext也继承了AbstractXmlApplicationContext,然后AbstractXmlApplicationContext又继承了AbstractRefreshableConfigApplicationContext。
时序图:
按照这个图的关系,来梳理流程,会更清晰一点。
(一)ClassPathXmlApplicationContext,最终到了下面的这个构造方法里:
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
//省略
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
参数含义:
(1)configLocations,存放着读取的文件地址。
(2)refresh,是否自动刷新上下文、加载所有bean定义并创建所有单例。或者,在进一步配置上下文后手动调用刷新。
(3)parent :父亲上下文。
总的流程,设置父亲上下文并做初始化,然后设置取的文件地址。
(二)AbstractApplicationContext
重点看AbstractApplicationContext中的refresh主流程,在这里初始化BeanFactory,解析XML加载BeanDefinition,注册bean处理器,注册事件添加监听等:
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
。。。
}
finally {
。。。
}
}
}
1 prepareRefresh(),初始化基础属性值,
2 obtainFreshBeanFactory(),
(1)执行refreshBeanFactory时候创建一个默认的BeanFactory:DefaultListableBeanFactory。如果之前已经创建过DefaultListableBeanFactory,则需要两个步骤,
- destroyBeans 这个步骤会删除单例,会把disposableBeans这个map中的对象在三级缓存中清除掉,另外把一些相关的map全部clear掉。例如containedBeanMap、dependentBeanMap等
- closeBeanFactory 这个步骤会设置this.beanFactory = null;
(2)加载BeanDefinition是会创建一个XmlBeanDefinitionReader对象,交由XmlBeanDefinitionReader去进行加载。这个在后面细说。
- XmlBeanDefinitionReader: 用beanFactory作为参数,新建一个XmlBeanDefinitionReader对象。
- initBeanDefinitionReader:对xmlBeanDefinitionReader做初始化,例如设置validating属性,决定是否用XML Validation
- loadBeanDefinitions:加载xmlBeanDefinitionReader所需的资源。
3 prepareBeanFactory(beanFactory),为beanFactory设置一些属性如ClassLoader,BeanExpressionResolver,PropertyEditorRegistrar,BeanPostProcessor等
4 invokeBeanFactoryPostProcessors(beanFactory),为beanFactory注册BeanFactoryPostProcessor
5 registerBeanPostProcessors(beanFactory),注册当Bean创建时候的BeanPostProcessor
6 initMessageSource()初始化上下文的消息源:DelegatingMessageSource
7 initApplicationEventMulticaster()初始化了一个事件传播器:SimpleApplicationEventMulticaster
8 registerListeners()获取ApplicationListener,并在事件传播器中注册他们
9 finishBeanFactoryInitialization(beanFactory),获取LoadTimeWeaverAware并初始化他们,初始化单例并且非懒加载的Bean
10 finishRefresh()完成refresh Context操作,初始化LifecycleProcessor并start,发布ContextRefreshedEvent事件
11 resetCommonCaches() 清理缓存
接下来对bean文件的解析部分重点描述:
上面是XmlBeanDefinitionReader的继承关系,比较简单,主要就在XmlBeanDefinitionReader和AbstractBeanDefinitionReader中实现bean的解析。
这是几个类之间的大致时序图关系,先捋清楚大致的调用关系,再往下看。
在AbstractXmlApplicationContext中的调用:
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
直接进入到AbstractBeanDefinitionReader中:
(三)AbstractBeanDefinitionReader
@Override
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
Assert.notNull(resources, "Resource array must not be null");
int counter = 0;
for (Resource resource : resources) {
counter += loadBeanDefinitions(resource);
}
return counter;
}
@Override
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
return loadBeanDefinitions(location, null);
}
这里的loadBeanDefinitions有多种实现:(1)GroovyBeanDefinitionReader(2)PropertiesBeanDefinitionReader(3)XmlBeanDefinitionReader ,这里走的是XmlBeanDefinitionReader的loadBeanDefinitions方法:
(四)XmlBeanDefinitionReader
//做了优化,先把resourcesCurrentlyBeingLoaded做了初始化,这样代码里就不用判断空了
private final ThreadLocal<Set<EncodedResource>> resourcesCurrentlyBeingLoaded =
new NamedThreadLocal<Set<EncodedResource>>("XML bean definition resources currently being loaded"){
@Override
protected Set<EncodedResource> initialValue() {
return new HashSet<>(4);
}
};
//省略
/**
* Load bean definitions from the specified XML file.
* @param encodedResource the resource descriptor for the XML file,
* allowing to specify an encoding to use for parsing the file
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of loading or parsing errors
*/
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isInfoEnabled()) {
logger.info("Loading XML bean definitions from " + encodedResource.getResource());
}
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
这一部分还只是生成bean definition,做了三件事:
(1)判断当前是否重复加载了resource,在threadLocal里维护了一个set。获取当前正在加载的EncodedResource集合,然后判断!currentResources.add(encodedResource),如果为true,说明添加失败,即当前Resource已经正在加载了,会直接抛异常。
(2)组装inputSource,调用真正加载bean definition的方法。
/**
* Actually load bean definitions from the specified XML file.
* @param inputSource the SAX InputSource to read from
* @param resource the resource descriptor for the XML file
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of loading or parsing errors
* @see #doLoadDocument
* @see #registerBeanDefinitions
*/
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
Document doc = doLoadDocument(inputSource, resource);
return registerBeanDefinitions(doc, resource);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
...
}
在doLoadBeanDefinitions中生成了一个Document对象,然后执行注册:
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
这里新建了一个BeanDefinitionDocumentReader来执行注册,并且返回注册的个数。
(1)使用DefaultBeanDefinitionDocumentReader实例化BeanDefinitionDocumentReader
(2)记录统计前BeanDefinition的加载个数
(3)根据resource创建一个XmlReaderContext
(4)加载及注册Bean定义,返回新增的注册个数
(五)DefaultBeanDefinitionDocumentReader
终于来到真正执行注册的方法了,DefaultBeanDefinitionDocumentReader是BeanDefinitionDocumentReader接口的一个实现类:
/**
* This implementation parses bean definitions according to the "spring-beans" XSD
* (or DTD, historically).
* <p>Opens a DOM Document; then initializes the default settings
* specified at the {@code <beans/>} level; then parses the contained bean definitions.
*/
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
Element root = doc.getDocumentElement();
doRegisterBeanDefinitions(root);
}
在这里,会得到一个Element对象,代表<beans/>节点,然后在doRegisterBeanDefinitions中对这个节点中的内容做具体的注册。
/**
* Register each bean definition within the given root {@code <beans/>} element.
*/
protected void doRegisterBeanDefinitions(Element root) {
// Any nested <beans> elements will cause recursion in this method. In
// order to propagate and preserve <beans> default-* attributes correctly,
// keep track of the current (parent) delegate, which may be null. Create
// the new (child) delegate with a reference to the parent for fallback purposes,
// then ultimately reset this.delegate back to its original (parent) reference.
// this behavior emulates a stack of delegates without actually necessitating one.
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
//判断命名空间是否是"http://www.springframework.org/schema/beans"
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)) {
if (logger.isInfoEnabled()) {
logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
preProcessXml(root);
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);
this.delegate = parent;
}
大致做了三件事:
(1)createDelegate的时候创建了一个新的代理,并且设置了一些默认值
(2)处理bean的profile
beans.xml可以用profile来区分不同的环境,然后在web.xml中设置哪种环境生效。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="...">
<bean id="transferService" class="com.bank.service.internal.DefaultTransferService">
<constructor-arg ref="accountRepository" />
<constructor-arg ref="feePolicy" />
</bean>
<bean id="accountRepository" class="com.bank.repository.internal.JdbcAccountRepository">
<constructor-arg ref="dataSource" />
</bean>
<bean id="feePolicy" class="com.bank.service.internal.ZeroFeePolicy" />
<beans profile="dev">
。。。
</beans>
<beans profile="production">
。。。
</beans>
</beans>
web.xml文件:
<context-param>
<param-name>spring.profiles.active</param-name>
<param-value>dev</param-value>
</context-param>
(3)解析beanDefinitions,
默认值就是我们在头部设置的一些属性,例如:
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"
default-autowire="byName">
...
其中default-autowire="byName"就是设置的一种属性,其他的还有:
DEFAULT_LAZY_INIT_ATTRIBUTE = "default-lazy-init";
DEFAULT_MERGE_ATTRIBUTE = "default-merge";
DEFAULT_AUTOWIRE_ATTRIBUTE = "default-autowire";
DEFAULT_DEPENDENCY_CHECK_ATTRIBUTE = "default-dependency-check";
DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE = "default-autowire-candidates";
DEFAULT_INIT_METHOD_ATTRIBUTE = "default-init-method";
DEFAULT_DESTROY_METHOD_ATTRIBUTE = "default-destroy-method"
如何判断默认命名空间还是自定义命名空间?
默认的命名空间为:http://www.springframework.org/schema/beans,其他都是自定义命名空间。
进入到parseBeanDefinitions方法:
/**
* Parse the elements at the root level in the document:
* "import", "alias", "bean".
* @param root the DOM root element of the document
*/
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
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);
}
}
关键来了,前面铺垫了这么多,主要是梳理了注册bean的过程,在delegate.isDefaultNamespace(ele)这步,才涉及到aop的东西。前面说过,defaultNamespace是"http://www.springframework.org/schema/beans",而aop标签的namespace则是"http://www.springframework.org/schema/aop",所以进入下面的方法,
(六)BeanDefinitionParserDelegate
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
String namespaceUri = getNamespaceURI(ele);
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
在这里,根据不同的nameSpace得到对应的handler,AopNamespaceHandler:
这里提一下,namespace和其对应的handler的关系是在spring.handlers中定义好的:
里面的内容是:http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler
而在spring.schemas中定义了url和本地xsd标签解析文件的对应关系。
http\://www.springframework.org/schema/aop/spring-aop-2.0.xsd=org/springframework/aop/config/spring-aop-2.0.xsd http\://www.springframework.org/schema/aop/spring-aop-2.5.xsd=org/springframework/aop/config/spring-aop-2.5.xsd http\://www.springframework.org/schema/aop/spring-aop-3.0.xsd=org/springframework/aop/config/spring-aop-3.0.xsd
.....
这些xsd文件中定义的是AopNamespaceHandler对于标签处理的规则。
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());
}
}
这里注册了几个parser,用来解析aop相关的标签。
到这里,aop神秘的面纱逐渐被揭开了,接下来继续看下去:
(1)registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
注册了BeanDefinitionParser,会执行相应Parser的parse方法(Q:怎么找到的?)。看一下ConfigBeanDefinitionParser:
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
//生成一个复合组件定义对象,并放到转换器的上下文中。
CompositeComponentDefinition compositeDef =
new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
parserContext.pushContainingComponent(compositeDef);
//proxy-target-class=true时,会强制设置代理类
configureAutoProxyCreator(parserContext, element);
List<Element> childElts = DomUtils.getChildElements(element);
//按照格式去解析,aop的config的二级标签为三种。
for (Element elt: childElts) {
String localName = parserContext.getDelegate().getLocalName(elt);
if (POINTCUT.equals(localName)) {
parsePointcut(elt, parserContext);
}
else if (ADVISOR.equals(localName)) {
parseAdvisor(elt, parserContext);
}
else if (ASPECT.equals(localName)) {
parseAspect(elt, parserContext);
}
}
parserContext.popAndRegisterContainingComponent();
return null;
}
接下来,把三种标签一个个看过去。为了方便对照,把aop(一)中定义的标签拿过来:
<aop:pointcut expression="execution(* com.cainiao.arrow.autoconfig. add(..))" id="pointcut"/>
先看parsePointcut方法:
/**
* Parses the supplied {@code <pointcut>} and registers the resulting
* Pointcut with the BeanDefinitionRegistry.
*/
private AbstractBeanDefinition parsePointcut(Element pointcutElement, ParserContext parserContext) {
//拿到id和expression属性的值
String id = pointcutElement.getAttribute(ID);
String expression = pointcutElement.getAttribute(EXPRESSION);
AbstractBeanDefinition pointcutDefinition = null;
try {
this.parseState.push(new PointcutEntry(id));
pointcutDefinition = createPointcutDefinition(expression);
pointcutDefinition.setSource(parserContext.extractSource(pointcutElement));
String pointcutBeanName = id;
//对bean定义进行注册,切点名称存在时用定义的名字,没有则生成默认的名称
if (StringUtils.hasText(pointcutBeanName)) {
parserContext.getRegistry().registerBeanDefinition(pointcutBeanName, pointcutDefinition);
}
else {
pointcutBeanName = parserContext.getReaderContext().registerWithGeneratedName(pointcutDefinition);
}
//注册一个切点的组件
parserContext.registerComponent(
new PointcutComponentDefinition(pointcutBeanName, pointcutDefinition, expression));
}
finally {
this.parseState.pop();
}
return pointcutDefinition;
}
ParseState这个类有点意思,里面放了一个栈,用来存放当前的切点id名称,最终当前切点id名称出栈。(Q:这个设计是干嘛用的?ParseState的toString() 可以打印树形结构,只是来做展示用?)
parsePointcut这个方法最终返回了一个pointcutDefinition,这个类继承BeanDefinition,aop的定义最终也会被统一作为bean来处理。
接下来是parseAdvisor方法,先上一个advisor配置的例子:
<aop:config>
<aop:advisor advice-ref="myAdvice"
pointcut="execution(* com.nhq.aop.*(..))" order="4"/>
</aop:config>
advisor是一种特殊的acpect,可以在advice-ref属性中直接定义。order定义解析的顺序,不定义则按照自然顺序。
private void parseAdvisor(Element advisorElement, ParserContext parserContext) {
AbstractBeanDefinition advisorDef = createAdvisorBeanDefinition(advisorElement, parserContext);
String id = advisorElement.getAttribute(ID);
//前面部分做的东西和pointcut类似
try {
this.parseState.push(new AdvisorEntry(id));
String advisorBeanName = id;
if (StringUtils.hasText(advisorBeanName)) {
parserContext.getRegistry().registerBeanDefinition(advisorBeanName, advisorDef);
}
else {
advisorBeanName = parserContext.getReaderContext().registerWithGeneratedName(advisorDef);
}
//把advisor中定义的切点取出来。
Object pointcut = parsePointcutProperty(advisorElement, parserContext);
if (pointcut instanceof BeanDefinition) {
advisorDef.getPropertyValues().add(POINTCUT, pointcut);
parserContext.registerComponent(
new AdvisorComponentDefinition(advisorBeanName, advisorDef, (BeanDefinition) pointcut));
}
else if (pointcut instanceof String) {
advisorDef.getPropertyValues().add(POINTCUT, new RuntimeBeanReference((String) pointcut));
parserContext.registerComponent(
new AdvisorComponentDefinition(advisorBeanName, advisorDef));
}
}
finally {
this.parseState.pop();
}
}
进去看看parsePointcutProperty的判断逻辑:
private Object parsePointcutProperty(Element element, ParserContext parserContext) {
if (element.hasAttribute(POINTCUT) && element.hasAttribute(POINTCUT_REF)) {
parserContext.getReaderContext().error(
"Cannot define both 'pointcut' and 'pointcut-ref' on <advisor> tag.",
element, this.parseState.snapshot());
return null;
}
else if (element.hasAttribute(POINTCUT)) {
// Create a pointcut for the anonymous pc and register it.
String expression = element.getAttribute(POINTCUT);
AbstractBeanDefinition pointcutDefinition = createPointcutDefinition(expression);
pointcutDefinition.setSource(parserContext.extractSource(element));
return pointcutDefinition;
}
else if (element.hasAttribute(POINTCUT_REF)) {
String pointcutRef = element.getAttribute(POINTCUT_REF);
if (!StringUtils.hasText(pointcutRef)) {
parserContext.getReaderContext().error(
"'pointcut-ref' attribute contains empty value.", element, this.parseState.snapshot());
return null;
}
return pointcutRef;
}
else {
parserContext.getReaderContext().error(
"Must define one of 'pointcut' or 'pointcut-ref' on <advisor> tag.",
element, this.parseState.snapshot());
return null;
}
}
(a)不能同时定义pointcut' 和'pointcut-ref标签,要不就是advice-ref,pointcut-ref索引形式,要不就是<aop:pointcut><aop:advice>这种标签定义形式。
(b)如果是pointcut新定义的方式,就重新解析生成pointcutDefinition返回。
(c)如果是ref形式就直接返回。如果取定义的时候,这切点还没注册好,会直接报错。所以没定义order的时候注意自己aop标签的定义顺序!
接下来是parseAspect方法,aspect标签中的灵活度比较大,所以要考虑很多情况,稍微复杂一点:
上个例子先:
<!-- pointcut可以定义在aspect内,也可以在外面 -->
<aop:pointcut expression="execution(* com.cainiao.arrow.autoconfig. add(..))" id="pointcut"/>
<aop:aspect ref="LogTest">
<aop:before method="before" pointcut-ref="pointcut"/>
<aop:around method="around" pointcut-ref="pointcut"/>
<aop:after-returning method="afterReturn" pointcut-ref="pointcut"/>
<aop:after-throwing method="afterException" pointcut-ref="pointcut"/>
<aop:around method="around" pointcut-ref="pointcut"/>
</aop:aspect>
<!-- declare-parents标签定义了一种静态代理,例如:为types-matching指定的类指定了代理的接口,并且指定了这个接口的默认实现impl -->
<aop:aspect>
<aop:declare-parents types-matching="com.nhq.aop.declareparent.Man"
implement-interface="com.nhq.aop.declareparent.IronMan"
default-impl="com.nhq.aop.declareparent.IronManImpl"/>
</aop:aspect>
private void parseAspect(Element aspectElement, ParserContext parserContext) {
String aspectId = aspectElement.getAttribute(ID);
String aspectName = aspectElement.getAttribute(REF);
try {
this.parseState.push(new AspectEntry(aspectId, aspectName));
List<BeanDefinition> beanDefinitions = new ArrayList<BeanDefinition>();
List<BeanReference> beanReferences = new ArrayList<BeanReference>();
//把declare-parents标签收集起来,解析之后一起放到beanDefinitions中
List<Element> declareParents = DomUtils.getChildElementsByTagName(aspectElement, DECLARE_PARENTS);
for (int i = METHOD_INDEX; i < declareParents.size(); i++) {
Element declareParentsElement = declareParents.get(i);
beanDefinitions.add(parseDeclareParents(declareParentsElement, parserContext));
}
//如果遇到advice或者advice类型,需要循环去获取正确的顺序语义
NodeList nodeList = aspectElement.getChildNodes();
boolean adviceFoundAlready = false;
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
if (isAdviceNode(node, parserContext)) {
if (!adviceFoundAlready) {
adviceFoundAlready = true;
if (!StringUtils.hasText(aspectName)) {
parserContext.getReaderContext().error(
"<aspect> tag needs aspect bean reference via 'ref' attribute when declaring advices.",
aspectElement, this.parseState.snapshot());
return;
}
beanReferences.add(new RuntimeBeanReference(aspectName));
}
AbstractBeanDefinition advisorDefinition = parseAdvice(
aspectName, i, aspectElement, (Element) node, parserContext, beanDefinitions, beanReferences);
beanDefinitions.add(advisorDefinition);
}
}
AspectComponentDefinition aspectComponentDefinition = createAspectComponentDefinition(
aspectElement, aspectId, beanDefinitions, beanReferences, parserContext);
parserContext.pushContainingComponent(aspectComponentDefinition);
List<Element> pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT);
for (Element pointcutElement : pointcuts) {
parsePointcut(pointcutElement, parserContext);
}
parserContext.popAndRegisterContainingComponent();
}
finally {
this.parseState.pop();
}
}
(a)把declare-parents标签收集起来,解析之后一起放到beanDefinitions中
(b)在isAdviceNode中判断是否有"before","after","after-returning","after-throwing","around"标签。有则为advice类型标签,那么此时aspect中必须得定义ref,表明是定义的哪个bean(advice)中的方法,作为advice类型标签。
(c)parseAdvice用来解析advice类型标签。
(d) parserContext.getReaderContext().error()用到了state的快照。现在可以回答刚才的问题,所以这个parseState确实是打印错误的时候用的,让你知道当前是在那个位置解析的时候报错的。
看看parseAdvice方法:
private AbstractBeanDefinition parseAdvice(
String aspectName, int order, Element aspectElement, Element adviceElement, ParserContext parserContext,
List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences) {
try {
this.parseState.push(new AdviceEntry(parserContext.getDelegate().getLocalName(adviceElement)));
//解析advice节点中的"method"属性,生成MethodLocatingFactoryBean对象
RootBeanDefinition methodDefinition = new RootBeanDefinition(MethodLocatingFactoryBean.class);
methodDefinition.getPropertyValues().add("targetBeanName", aspectName);
methodDefinition.getPropertyValues().add("methodName", adviceElement.getAttribute("method"));
methodDefinition.setSynthetic(true);
//关联aspectName,生成SimpleBeanFactoryAwareAspectInstanceFactory对象
RootBeanDefinition aspectFactoryDef =
new RootBeanDefinition(SimpleBeanFactoryAwareAspectInstanceFactory.class);
aspectFactoryDef.getPropertyValues().add("aspectBeanName", aspectName);
aspectFactoryDef.setSynthetic(true);
//对point-cut属性进行解析,并结合上面的两个bean最终生成AbstractAspectJAdvice通知对象
AbstractBeanDefinition adviceDef = createAdviceDefinition(
adviceElement, parserContext, aspectName, order, methodDefinition, aspectFactoryDef,
beanDefinitions, beanReferences);
//最终生成AspectJPointcutAdvisor对象
RootBeanDefinition advisorDefinition = new RootBeanDefinition(AspectJPointcutAdvisor.class);
advisorDefinition.setSource(parserContext.extractSource(adviceElement));
advisorDefinition.getConstructorArgumentValues().addGenericArgumentValue(adviceDef);
if (aspectElement.hasAttribute(ORDER_PROPERTY)) {
advisorDefinition.getPropertyValues().add(
ORDER_PROPERTY, aspectElement.getAttribute(ORDER_PROPERTY));
}
// register the final advisor
parserContext.getReaderContext().registerWithGeneratedName(advisorDefinition);
return advisorDefinition;
}
finally {
this.parseState.pop();
}
}
最后看看declareParents属性是怎么处理的:
private AbstractBeanDefinition parseDeclareParents(Element declareParentsElement, ParserContext parserContext) {
//设置根bean定义(rootBeanDefinition)。declare-parents标签内部实现方式是创建了一个切面
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(DeclareParentsAdvisor.class);
//添加构造函数的参数 builder.addConstructorArgValue(declareParentsElement.getAttribute(IMPLEMENT_INTERFACE));
builder.addConstructorArgValue(declareParentsElement.getAttribute(TYPE_PATTERN));
//新加接口的默认实现和实现引用,两者取其一即可
String defaultImpl = declareParentsElement.getAttribute(DEFAULT_IMPL);
String delegateRef = declareParentsElement.getAttribute(DELEGATE_REF);
if (StringUtils.hasText(defaultImpl) && !StringUtils.hasText(delegateRef)) {
builder.addConstructorArgValue(defaultImpl);
}
else if (StringUtils.hasText(delegateRef) && !StringUtils.hasText(defaultImpl)) {
builder.addConstructorArgReference(delegateRef);
}
else {
parserContext.getReaderContext().error(
"Exactly one of the " + DEFAULT_IMPL + " or " + DELEGATE_REF + " attributes must be specified",
declareParentsElement, this.parseState.snapshot());
}
//获取bean定义并且注册bean
AbstractBeanDefinition definition = builder.getBeanDefinition();
definition.setSource(parserContext.extractSource(declareParentsElement));
parserContext.getReaderContext().registerWithGeneratedName(definition);
return definition;
}
总结
总的来讲,bean文件的解析就是:
1 、初始化基础属性值
2、 创建一个默认的BeanFactory:DefaultListableBeanFactory
3、创建一个XmlBeanDefinitionReader对象,根据传入的bean文件路径,生成resource对象;然后组装inputSource对象,和resource一起加载bean的定义
4、根据inputSource和resource加载XML文件,并封装成Document对象。并在注册bean的定义时,解析Document中的Element,也就是对应bean文件中的各个标签,判断是否是默认空间分别进行处理。
5、prepareBeanFactory(beanFactory),为beanFactory设置一些属性如ClassLoader,BeanExpressionResolver,PropertyEditorRegistrar,BeanPostProcessor等
6、invokeBeanFactoryPostProcessors(beanFactory),为beanFactory注册BeanFactoryPostProcessor
7、其他的一些注册和初始化事件:
a)注册当Bean创建时候的BeanPostProcessor
b)initMessageSource()初始化上下文的消息源:DelegatingMessageSource
c) initApplicationEventMulticaster()初始化了一个事件传播器:SimpleApplicationEventMulticaster
d) registerListeners()获取ApplicationListener,并在事件传播器中注册他们
e) finishBeanFactoryInitialization(beanFactory),获取LoadTimeWeaverAware并初始化他们,初始化单例并且非懒加载的Bean
f) finishRefresh()完成refresh Context操作,初始化LifecycleProcessor并start,发布ContextRefreshedEvent事件
6、 resetCommonCaches() 清理缓存