您的位置:首页 > 编程语言 > Java开发

Spring系列之-Spring AOP设计原理(一)

2017-12-25 14:10 393 查看
Spring AOP作为Spring中两大最要特性跟IOC一样重要,看了很多书籍,都没有把这个东西的来龙去脉讲清楚,网上很多文章标题也都是一知半解,甚至很多直接就切入到动态代理这块来讲。本文参考了网上相关分析以及《深入理解Spring技术设计原理》一书,从源码级别来分析Spring AOP的设计过程,用一个例子来从头到尾分析Spring AOP实现过程。

我们先上一个简单的例子:

public interface ShopService {

public String getShopName();

}

public class ShopServiceImpl implements ShopService{

public String getShopName() {
System.out.println("nike shop");
return "Nike";
}

}

public class TimeHandler {

public void printTime() {
System.out.println("当前时间:" + System.currentTimeMillis());
}

}

XML配置:

<bean id="shopService" class="com.dianping.aop.ShopServiceImpl"/>
<bean id="timeHandler" class="com.dianping.aop.TimeHandler"/>

<aop:config proxy-target-class="true">
<aop:aspect id="time" ref="timeHandler">
<aop:pointcut id="point" expression="execution(* com.dianping.aop.ShopService.*(..))"/>
<aop:before method="printTime" pointcut-ref="point"/>
<aop:after method="printTime" pointcut-ref="point"/>
</aop:aspect>
</aop:config>

测试类:

public class Main {
public static void main(String[] args) {
BeanFactory beanFactory = new FileSystemXmlApplicationContext("classpath:appcontext.xml");
ShopService shopService = (ShopService) beanFactory.getBean("shopService");
shopService.getShopName();
}
}

输出:

当前时间:1514042119262
nike shop
当前时间:1514042119283

可以看见在getShopName()方法执行前后都打印了当前时间,也就是执行了TimeHandler中的printTime()方法。

这是一个简单的AOP使用,下面将从这个例子触发解析Spring AOP设计实现。

可以发现打印时间是在getBean()方法之后执行的,说明AOP被Spring 容器处理过了,那么只有两个地方处理:

1.在Spring IOC容器初始化过程中处理。

2.在获取bean的过程中被处理。

前几篇文章分析过Spring IOC的初始化过程,在Spring IOC出事化过程中会调用XmlBeanDefinitionReader对XML文件中的Bean定义进行读取获取到Dom文档后进行解析,最终的解析BeanDefinition在DefaultBeanDefinitionDocumentReader的下述方法中。(本文源码均基于Spring 4.3.9)

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);
}
}

在Spring IOC容器初始化过程中调用的是这句代码parseDefaultElement(ele, delegate),因为我们的bean都是通过<bean id="">声明的,这属于Spring默认命名,从上面配置Spring AOP XML中可以看出AOP的配置为<aop:>,这种配置不属于Spring默认命名,故不会执行parseDefaultElement(ele, delegate),而会执行delegate.parseCustomElement(ele),这句代码是解析自定义命名bean定义,<aop:>就属于自定义命名。

进一步进去:

public BeanDefinition parseCustomElement(Element ele) {
return parseCustomElement(ele, null);
}

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));
}

其中这句代码NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); 获得了命名空间处理Handler,而String namespaceUri = getNamespaceURI(ele);这个获得的值就是http://www.springframework.org/schema/aop

NamespaceHandlerResolver用于根据命名空间解析得到命名空间处理Handler,NamespaceHandlerResolver通过resolve方法来根据http://www.springframework.org/schema/aop过去AOP命名空间处理Handler,NamespaceHandlerResolver有个默认实现DefaultNamespaceHandlerResolver,内部resolve实现如下:

@Override
public NamespaceHandler resolve(String namespaceUri) {
//加载handler map映射
Map<String, Object> handlerMappings = getHandlerMappings();
Object handlerOrClassName = handlerMappings.get(namespaceUri);
if (handlerOrClassName == null) {
return null;
}
else if (handlerOrClassName instanceof NamespaceHandler) {
return (NamespaceHandler) handlerOrClassName;
}
else {
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.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);
}
}
}

其中Map<String, Object> handlerMappings = getHandlerMappings();获取到了url所对应的handler对应对象。getHandlerMappings()方法如下:

private Map<String, Object> getHandlerMappings() {
if (this.handlerMappings == null) {
synchronized (this) {
if (this.handlerMappings == null) {
try {
Properties mappings =
PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
if (logger.isDebugEnabled()) {
logger.debug("Loaded NamespaceHandler mappings: " + mappings);
}
Map<String, Object> handlerMappings = new ConcurrentHashMap<String, Object>(mappings.size());
CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
this.handlerMappings = handlerMappings;
}
catch (IOException ex) {
throw new IllegalStateException(
"Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
}
}
}
}
return this.handlerMappings;
}

getHandlerMappings会在第一次调用时加载handler与url映射关系,并且会把class路径下的所有META-INF/spring.handlers下文件下的内容加载到Properties文件中然后转成map,其中Spring 每个Jar包下都有spring.handlers文件夹,在Spring-aop Jar包中该文件夹下内容是http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler

故Map<String, Object>维护了http://www.springframework.org/schema/aop与org.springframework.aop.config.AopNamespaceHandler之间的映射关系,即Aop的handler为AopNamespaceHandler。

故NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);这句代码得到了AopNamespaceHandler。

return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));

上面这行代码对aop定义进行解析,具体解析过程是先得到BeanDefinitionParser然后再调用BeanDefinitionParser中的BeanDefinition parse(Element element, ParserContext parserContext);方法

解析得到BeanDefinition。具体过程在NamespaceHandlerSupport中,其中AopNamespaceHandler继承了NamespaceHandlerSupport,而NamespaceHandlerSupport实现了NamespaceHandler接口。

@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
return findParserForElement(element, parserContext).parse(element, parserContext);
}

/**
* Locates the {@link BeanDefinitionParser} from the register implementations using
* the local name of the supplied {@link Element}.
*/
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;
}

其中String localName = parserContext.getDelegate().getLocalName(element),因为当前标签是<aop:config>,故得到的值为config,故获取到的BeanDefinitionParser为parser中key为config的对象,其中在AopNamespaceHandler初始化了下面几个BeanDefinitionParser,这个init方法是在 public NamespaceHandler resolve(String namespaceUri) {}即获取到AopNamespaceHandler后立即对其进行了初始化。

@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());
}

故<aop:config>得到的BeanDefinitionParser为ConfigBeanDefinitionParser,其中ConfigBeanDefinitionParser的parser方法如下:

@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
CompositeComponentDefinition compositeDef =
new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
parserContext.pushContainingComponent(compositeDef);

configureAutoProxyCreator(parserContext, element);

List<Element> childElts = DomUtils.getChildElements(element);
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;
}

其中configureAutoProxyCreator(parserContext, element)向Spring容器中注册了一个beanName为org.springframework.aop.config.internalAutoProxyCreator的bean,这个bean的默认实现为AspectJAwareAdvisorAutoProxyCreator。这个类是Spring AOP中非常重要的类,本文主要介绍AOP的初始化XML解析过程,这个类留在后续做处理。

if (POINTCUT.equals(localName)) {
parsePointcut(elt, parserContext);
}
else if (ADVISOR.equals(localName)) {
parseAdvisor(elt, parserContext);
}
else if (ASPECT.equals(localName)) {
parseAspect(elt, parserContext);
}

从名字很容易看出上面代码分别处理了<aop:config>下的<aop:pointcut> <aop:advisor>以及<aop:aspect>,从我们的例子使用中我们分别介绍:

<aop:aspect>标签解析:

private void parseAspect(Element aspectElement, ParserContext parserContext) {
//得到 <aop:aspect id="time" ref="timeHandler">中的time
String aspectId = aspectElement.getAttribute(ID);
//得到timeHandler
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>();
//获取<aop:aspect>节点为declare-parents的元素,本次不会涉及到,这个有时间再研究
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));
}

// We have to parse "advice" and all the advice kinds in one loop, to get the
// ordering semantics right.
//获取<aop:aspect>下所有的节点
NodeList nodeList = aspectElement.getChildNodes();
boolean adviceFoundAlready = false;
//循环遍历所有节点
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
if (isAdviceNode(node, parserContext)) {
//如果节点为为通知类型即<aop:before>、<aop:after>、<aop:after-returning>、<aop:after-throwing method="">、<aop:around method=""> 5个节点
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();
}
}


AbstractBeanDefinition advisorDefinition = parseAdvice( aspectName, i, aspectElement, (Element) node, parserContext, beanDefinitions, beanReferences);

这句代码对通知节点进行了解析,其实现如下。

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)));

// create the method factory bean
RootBeanDefinition methodDefinition = new RootBeanDefinition(MethodLocatingFactoryBean.class);
methodDefinition.getPropertyValues().add("targetBeanName", aspectName);
methodDefinition.getPropertyValues().add("methodName", adviceElement.getAttribute("method"));
methodDefinition.setSynthetic(true);

// create instance factory definition
RootBeanDefinition aspectFactoryDef =
new RootBeanDefinition(SimpleBeanFactoryAwareAspectInstanceFactory.class);
aspectFactoryDef.getPropertyValues().add("aspectBeanName", aspectName);
aspectFactoryDef.setSynthetic(true);

// 注册切点
AbstractBeanDefinition adviceDef = createAdviceDefinition(
adviceElement, parserContext, aspectName, order, methodDefinition, aspectFactoryDef,
beanDefinitions, beanReferences);

// 配置通知
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));
}

// 注册到beanFactory中
parserContext.getReaderContext().registerWithGeneratedName(advisorDefinition);

return advisorDefinition;
}
finally {
this.parseState.pop();
}
}

private AbstractBeanDefinition createAdviceDefinition(
Element adviceElement, ParserContext parserContext, String aspectName, int order,
RootBeanDefinition methodDef, RootBeanDefinition aspectFactoryDef,
List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences) {

RootBeanDefinition adviceDefinition = new RootBeanDefinition(getAdviceClass(adviceElement, parserContext));
adviceDefinition.setSource(parserContext.extractSource(adviceElement));

adviceDefinition.getPropertyValues().add(ASPECT_NAME_PROPERTY, aspectName);
adviceDefinition.getPropertyValues().add(DECLARATION_ORDER_PROPERTY, order);

if (adviceElement.hasAttribute(RETURNING)) {
adviceDefinition.getPropertyValues().add(
RETURNING_PROPERTY, adviceElement.getAttribute(RETURNING));
}
if (adviceElement.hasAttribute(THROWING)) {
adviceDefinition.getPropertyValues().add(
THROWING_PROPERTY, adviceElement.getAttribute(THROWING));
}
if (adviceElement.hasAttribute(ARG_NAMES)) {
adviceDefinition.getPropertyValues().add(
ARG_NAMES_PROPERTY, adviceElement.getAttribute(ARG_NAMES));
}

ConstructorArgumentValues cav = adviceDefinition.getConstructorArgumentValues();
cav.addIndexedArgumentValue(METHOD_INDEX, methodDef);

Object pointcut = parsePointcutProperty(adviceElement, parserContext);
if (pointcut instanceof BeanDefinition) {
cav.addIndexedArgumentValue(POINTCUT_INDEX, pointcut);
beanDefinitions.add((BeanDefinition) pointcut);
}
else if (pointcut instanceof String) {
RuntimeBeanReference pointcutRef = new RuntimeBeanReference((String) pointcut);
cav.addIndexedArgumentValue(POINTCUT_INDEX, pointcutRef);
beanReferences.add(pointcutRef);
}

cav.addIndexedArgumentValue(ASPECT_INSTANCE_FACTORY_INDEX, aspectFactoryDef);

return adviceDefinition;
}

其中createAdviceDefinition创建了通知定义是一种Spring Bean定义RootBeanDefinition,其中针对每个通知有对应的bean与之对应。

private Class<?> getAdviceClass(Element adviceElement, ParserContext parserContext) {
String elementName = parserContext.getDelegate().getLocalName(adviceElement);
if (BEFORE.equals(elementName)) {
return AspectJMethodBeforeAdvice.class;
}
else if (AFTER.equals(elementName)) {
return AspectJAfterAdvice.class;
}
else if (AFTER_RETURNING_ELEMENT.equals(elementName)) {
return AspectJAfterReturningAdvice.class;
}
else if (AFTER_THROWING_ELEMENT.equals(elementName)) {
return AspectJAfterThrowingAdvice.class;
}
else if (AROUND.equals(elementName)) {
return AspectJAroundAdvice.class;
}
else {
throw new IllegalArgumentException("Unknown advice kind [" + elementName + "].");
}
}

从上面方法可以看出5种通知对应的bean定义分别为

before对应AspectJMethodBeforeAdvice

After对应AspectJAfterAdvice

after-returning对应AspectJAfterReturningAdvice

after-throwing对应AspectJAfterThrowingAdvice

around对应AspectJAroundAdvice

故我们例子中<aop:before>、<aop:after>两个标签的Spring Bean定义都创建完成,具体创建Bean定义跟Spring IOC一样,只不过这里直接指定了Bean实现。

获取通知的BeanDefinition后会会实例化一个

RootBeanDefinition advisorDefinition = new RootBeanDefinition(AspectJPointcutAdvisor.class);并将通知Bean封装起来,这个bean是AspectJPointcutAdvisor。

回到上面节点循环中,其中解析了通知节点后会解析横切点,代码如下:

//获取横切点的元素节点
List<Element> pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT);
for (Element pointcutElement : pointcuts) {
parsePointcut(pointcutElement, parserContext);
}

private AbstractBeanDefinition parsePointcut(Element pointcutElement, ParserContext parserContext) {
//获取<aop:pointcut id="point" expression="execution(* com.dianping.aop.ShopService.*(..))"/>中id和expression的值
String id = pointcutElement.getAttribute(ID);
String expression = pointcutElement.getAttribute(EXPRESSION);

AbstractBeanDefinition pointcutDefinition = null;

try {
this.parseState.push(new PointcutEntry(id));
//创建切点bean定义
pointcutDefinition = createPointcutDefinition(expression);
pointcutDefinition.setSource(parserContext.extractSource(pointcutElement));

String pointcutBeanName = id;
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;
}

pointcutDefinition = createPointcutDefinition(expression);方法如下:

protected AbstractBeanDefinition createPointcutDefinition(String expression) {
RootBeanDefinition beanDefinition = new RootBeanDefinition(AspectJExpressionPointcut.class);
beanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
beanDefinition.setSynthetic(true);
beanDefinition.getPropertyValues().add(EXPRESSION, expression);
return beanDefinition;
}

即创建了一个AspectJExpressionPointcut Bean定义。

创建完成后无非就是将bean注册到BeanFactory中。

这样整个Spring AOP初始化就基本完成,AOP初始化过程中就是解析各种标签创建Spring IOC中bean定义。

回顾下AOP初始化主要创建了一下Bean

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息