1 /*
2  * Copyright 2002-2008 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package org.springframework.aop.config;
18 
19 import java.util.ArrayList;
20 import java.util.List;
21 
22 import org.w3c.dom.Element;
23 import org.w3c.dom.Node;
24 import org.w3c.dom.NodeList;
25 
26 import org.springframework.aop.aspectj.AspectJAfterAdvice;
27 import org.springframework.aop.aspectj.AspectJAfterReturningAdvice;
28 import org.springframework.aop.aspectj.AspectJAfterThrowingAdvice;
29 import org.springframework.aop.aspectj.AspectJAroundAdvice;
30 import org.springframework.aop.aspectj.AspectJExpressionPointcut;
31 import org.springframework.aop.aspectj.AspectJMethodBeforeAdvice;
32 import org.springframework.aop.aspectj.AspectJPointcutAdvisor;
33 import org.springframework.aop.aspectj.DeclareParentsAdvisor;
34 import org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor;
35 import org.springframework.beans.factory.config.BeanDefinition;
36 import org.springframework.beans.factory.config.BeanReference;
37 import org.springframework.beans.factory.config.ConstructorArgumentValues;
38 import org.springframework.beans.factory.config.RuntimeBeanNameReference;
39 import org.springframework.beans.factory.config.RuntimeBeanReference;
40 import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
41 import org.springframework.beans.factory.parsing.ParseState;
42 import org.springframework.beans.factory.support.AbstractBeanDefinition;
43 import org.springframework.beans.factory.support.BeanDefinitionBuilder;
44 import org.springframework.beans.factory.support.BeanDefinitionRegistry;
45 import org.springframework.beans.factory.support.RootBeanDefinition;
46 import org.springframework.beans.factory.xml.BeanDefinitionParser;
47 import org.springframework.beans.factory.xml.ParserContext;
48 import org.springframework.util.StringUtils;
49 import org.springframework.util.xml.DomUtils;
50 
51 /**
52  * {@link BeanDefinitionParser} for the <code>&lt;aop:config&gt;</code> tag.
53  *
54  * @author Rob Harrop
55  * @author Juergen Hoeller
56  * @author Adrian Colyer
57  * @author Mark Fisher
58  * @author Ramnivas Laddad
59  * @since 2.0
60  */
61 class ConfigBeanDefinitionParser implements BeanDefinitionParser {
62 
63 	private static final String ASPECT = "aspect";
64 	private static final String EXPRESSION = "expression";
65 	private static final String ID = "id";
66 	private static final String POINTCUT = "pointcut";
67 	private static final String ADVICE_BEAN_NAME = "adviceBeanName";
68 	private static final String ADVISOR = "advisor";
69 	private static final String ADVICE_REF = "advice-ref";
70 	private static final String POINTCUT_REF = "pointcut-ref";
71 	private static final String REF = "ref";
72 	private static final String BEFORE = "before";
73 	private static final String DECLARE_PARENTS = "declare-parents";
74 	private static final String TYPE_PATTERN = "types-matching";
75 	private static final String DEFAULT_IMPL = "default-impl";
76 	private static final String DELEGATE_REF = "delegate-ref";
77 	private static final String IMPLEMENT_INTERFACE = "implement-interface";
78 	private static final String AFTER = "after";
79 	private static final String AFTER_RETURNING_ELEMENT = "after-returning";
80 	private static final String AFTER_THROWING_ELEMENT = "after-throwing";
81 	private static final String AROUND = "around";
82 	private static final String RETURNING = "returning";
83 	private static final String RETURNING_PROPERTY = "returningName";
84 	private static final String THROWING = "throwing";
85 	private static final String THROWING_PROPERTY = "throwingName";
86 	private static final String ARG_NAMES = "arg-names";
87 	private static final String ARG_NAMES_PROPERTY = "argumentNames";
88 	private static final String ASPECT_NAME_PROPERTY = "aspectName";
89 	private static final String DECLARATION_ORDER_PROPERTY = "declarationOrder";
90 	private static final String ORDER_PROPERTY = "order";
91 	private static final int METHOD_INDEX = 0;
92 	private static final int POINTCUT_INDEX = 1;
93 	private static final int ASPECT_INSTANCE_FACTORY_INDEX = 2;
94 
95 	private ParseState parseState = new ParseState();
96 
97 
parse(Element element, ParserContext parserContext)98 	public BeanDefinition parse(Element element, ParserContext parserContext) {
99 		CompositeComponentDefinition compositeDef =
100 				new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
101 		parserContext.pushContainingComponent(compositeDef);
102 
103 		configureAutoProxyCreator(parserContext, element);
104 
105 		List<Element> childElts = DomUtils.getChildElements(element);
106 		for (Element elt: childElts) {
107 			String localName = parserContext.getDelegate().getLocalName(elt);
108 			if (POINTCUT.equals(localName)) {
109 				parsePointcut(elt, parserContext);
110 			}
111 			else if (ADVISOR.equals(localName)) {
112 				parseAdvisor(elt, parserContext);
113 			}
114 			else if (ASPECT.equals(localName)) {
115 				parseAspect(elt, parserContext);
116 			}
117 		}
118 
119 		parserContext.popAndRegisterContainingComponent();
120 		return null;
121 	}
122 
123 	/**
124 	 * Configures the auto proxy creator needed to support the {@link BeanDefinition BeanDefinitions}
125 	 * created by the '<code>&lt;aop:config/&gt;</code>' tag. Will force class proxying if the
126 	 * '<code>proxy-target-class</code>' attribute is set to '<code>true</code>'.
127 	 * @see AopNamespaceUtils
128 	 */
configureAutoProxyCreator(ParserContext parserContext, Element element)129 	private void configureAutoProxyCreator(ParserContext parserContext, Element element) {
130 		AopNamespaceUtils.registerAspectJAutoProxyCreatorIfNecessary(parserContext, element);
131 	}
132 
133 	/**
134 	 * Parses the supplied <code>&lt;advisor&gt;</code> element and registers the resulting
135 	 * {@link org.springframework.aop.Advisor} and any resulting {@link org.springframework.aop.Pointcut}
136 	 * with the supplied {@link BeanDefinitionRegistry}.
137 	 */
parseAdvisor(Element advisorElement, ParserContext parserContext)138 	private void parseAdvisor(Element advisorElement, ParserContext parserContext) {
139 		AbstractBeanDefinition advisorDef = createAdvisorBeanDefinition(advisorElement, parserContext);
140 		String id = advisorElement.getAttribute(ID);
141 
142 		try {
143 			this.parseState.push(new AdvisorEntry(id));
144 			String advisorBeanName = id;
145 			if (StringUtils.hasText(advisorBeanName)) {
146 				parserContext.getRegistry().registerBeanDefinition(advisorBeanName, advisorDef);
147 			}
148 			else {
149 				advisorBeanName = parserContext.getReaderContext().registerWithGeneratedName(advisorDef);
150 			}
151 
152 			Object pointcut = parsePointcutProperty(advisorElement, parserContext);
153 			if (pointcut instanceof BeanDefinition) {
154 				advisorDef.getPropertyValues().add(POINTCUT, pointcut);
155 				parserContext.registerComponent(
156 						new AdvisorComponentDefinition(advisorBeanName, advisorDef, (BeanDefinition) pointcut));
157 			}
158 			else if (pointcut instanceof String) {
159 				advisorDef.getPropertyValues().add(POINTCUT, new RuntimeBeanReference((String) pointcut));
160 				parserContext.registerComponent(
161 						new AdvisorComponentDefinition(advisorBeanName, advisorDef));
162 			}
163 		}
164 		finally {
165 			this.parseState.pop();
166 		}
167 	}
168 
169 	/**
170 	 * Create a {@link RootBeanDefinition} for the advisor described in the supplied. Does <strong>not</strong>
171 	 * parse any associated '<code>pointcut</code>' or '<code>pointcut-ref</code>' attributes.
172 	 */
createAdvisorBeanDefinition(Element advisorElement, ParserContext parserContext)173 	private AbstractBeanDefinition createAdvisorBeanDefinition(Element advisorElement, ParserContext parserContext) {
174 		RootBeanDefinition advisorDefinition = new RootBeanDefinition(DefaultBeanFactoryPointcutAdvisor.class);
175 		advisorDefinition.setSource(parserContext.extractSource(advisorElement));
176 
177 		String adviceRef = advisorElement.getAttribute(ADVICE_REF);
178 		if (!StringUtils.hasText(adviceRef)) {
179 			parserContext.getReaderContext().error(
180 					"'advice-ref' attribute contains empty value.", advisorElement, this.parseState.snapshot());
181 		}
182 		else {
183 			advisorDefinition.getPropertyValues().add(
184 					ADVICE_BEAN_NAME, new RuntimeBeanNameReference(adviceRef));
185 		}
186 
187 		if (advisorElement.hasAttribute(ORDER_PROPERTY)) {
188 			advisorDefinition.getPropertyValues().add(
189 					ORDER_PROPERTY, advisorElement.getAttribute(ORDER_PROPERTY));
190 		}
191 
192 		return advisorDefinition;
193 	}
194 
parseAspect(Element aspectElement, ParserContext parserContext)195 	private void parseAspect(Element aspectElement, ParserContext parserContext) {
196 		String aspectId = aspectElement.getAttribute(ID);
197 		String aspectName = aspectElement.getAttribute(REF);
198 
199 		try {
200 			this.parseState.push(new AspectEntry(aspectId, aspectName));
201 			List<BeanDefinition> beanDefinitions = new ArrayList<BeanDefinition>();
202 			List<BeanReference> beanReferences = new ArrayList<BeanReference>();
203 
204 			List<Element> declareParents = DomUtils.getChildElementsByTagName(aspectElement, DECLARE_PARENTS);
205 			for (int i = METHOD_INDEX; i < declareParents.size(); i++) {
206 				Element declareParentsElement = declareParents.get(i);
207 				beanDefinitions.add(parseDeclareParents(declareParentsElement, parserContext));
208 			}
209 
210 			// We have to parse "advice" and all the advice kinds in one loop, to get the
211 			// ordering semantics right.
212 			NodeList nodeList = aspectElement.getChildNodes();
213 			boolean adviceFoundAlready = false;
214 			for (int i = 0; i < nodeList.getLength(); i++) {
215 				Node node = nodeList.item(i);
216 				if (isAdviceNode(node, parserContext)) {
217 					if (!adviceFoundAlready) {
218 						adviceFoundAlready = true;
219 						if (!StringUtils.hasText(aspectName)) {
220 							parserContext.getReaderContext().error(
221 									"<aspect> tag needs aspect bean reference via 'ref' attribute when declaring advices.",
222 									aspectElement, this.parseState.snapshot());
223 							return;
224 						}
225 						beanReferences.add(new RuntimeBeanReference(aspectName));
226 					}
227 					AbstractBeanDefinition advisorDefinition = parseAdvice(
228 							aspectName, i, aspectElement, (Element) node, parserContext, beanDefinitions, beanReferences);
229 					beanDefinitions.add(advisorDefinition);
230 				}
231 			}
232 
233 			AspectComponentDefinition aspectComponentDefinition = createAspectComponentDefinition(
234 					aspectElement, aspectId, beanDefinitions, beanReferences, parserContext);
235 			parserContext.pushContainingComponent(aspectComponentDefinition);
236 
237 			List<Element> pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT);
238 			for (Element pointcutElement : pointcuts) {
239 				parsePointcut(pointcutElement, parserContext);
240 			}
241 
242 			parserContext.popAndRegisterContainingComponent();
243 		}
244 		finally {
245 			this.parseState.pop();
246 		}
247 	}
248 
createAspectComponentDefinition( Element aspectElement, String aspectId, List<BeanDefinition> beanDefs, List<BeanReference> beanRefs, ParserContext parserContext)249 	private AspectComponentDefinition createAspectComponentDefinition(
250 			Element aspectElement, String aspectId, List<BeanDefinition> beanDefs,
251 			List<BeanReference> beanRefs, ParserContext parserContext) {
252 
253 		BeanDefinition[] beanDefArray = beanDefs.toArray(new BeanDefinition[beanDefs.size()]);
254 		BeanReference[] beanRefArray = beanRefs.toArray(new BeanReference[beanRefs.size()]);
255 		Object source = parserContext.extractSource(aspectElement);
256 		return new AspectComponentDefinition(aspectId, beanDefArray, beanRefArray, source);
257 	}
258 
259 	/**
260 	 * Return <code>true</code> if the supplied node describes an advice type. May be one of:
261 	 * '<code>before</code>', '<code>after</code>', '<code>after-returning</code>',
262 	 * '<code>after-throwing</code>' or '<code>around</code>'.
263 	 */
isAdviceNode(Node aNode, ParserContext parserContext)264 	private boolean isAdviceNode(Node aNode, ParserContext parserContext) {
265 		if (!(aNode instanceof Element)) {
266 			return false;
267 		}
268 		else {
269 			String name = parserContext.getDelegate().getLocalName(aNode);
270 			return (BEFORE.equals(name) || AFTER.equals(name) || AFTER_RETURNING_ELEMENT.equals(name) ||
271 					AFTER_THROWING_ELEMENT.equals(name) || AROUND.equals(name));
272 		}
273 	}
274 
275 	/**
276 	 * Parse a '<code>declare-parents</code>' element and register the appropriate
277 	 * DeclareParentsAdvisor with the BeanDefinitionRegistry encapsulated in the
278 	 * supplied ParserContext.
279 	 */
parseDeclareParents(Element declareParentsElement, ParserContext parserContext)280 	private AbstractBeanDefinition parseDeclareParents(Element declareParentsElement, ParserContext parserContext) {
281 		BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(DeclareParentsAdvisor.class);
282 		builder.addConstructorArgValue(declareParentsElement.getAttribute(IMPLEMENT_INTERFACE));
283 		builder.addConstructorArgValue(declareParentsElement.getAttribute(TYPE_PATTERN));
284 
285 		String defaultImpl = declareParentsElement.getAttribute(DEFAULT_IMPL);
286 		String delegateRef = declareParentsElement.getAttribute(DELEGATE_REF);
287 
288 		if (StringUtils.hasText(defaultImpl) && !StringUtils.hasText(delegateRef)) {
289 			builder.addConstructorArgValue(defaultImpl);
290 		}
291 		else if (StringUtils.hasText(delegateRef) && !StringUtils.hasText(defaultImpl)) {
292 			builder.addConstructorArgReference(delegateRef);
293 		}
294 		else {
295 			parserContext.getReaderContext().error(
296 					"Exactly one of the " + DEFAULT_IMPL + " or " + DELEGATE_REF + " attributes must be specified",
297 					declareParentsElement, this.parseState.snapshot());
298 		}
299 
300 		AbstractBeanDefinition definition = builder.getBeanDefinition();
301 		definition.setSource(parserContext.extractSource(declareParentsElement));
302 		parserContext.getReaderContext().registerWithGeneratedName(definition);
303 		return definition;
304 	}
305 
306 	/**
307 	 * Parses one of '<code>before</code>', '<code>after</code>', '<code>after-returning</code>',
308 	 * '<code>after-throwing</code>' or '<code>around</code>' and registers the resulting
309 	 * BeanDefinition with the supplied BeanDefinitionRegistry.
310 	 * @return the generated advice RootBeanDefinition
311 	 */
parseAdvice( String aspectName, int order, Element aspectElement, Element adviceElement, ParserContext parserContext, List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences)312 	private AbstractBeanDefinition parseAdvice(
313 			String aspectName, int order, Element aspectElement, Element adviceElement, ParserContext parserContext,
314 			List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences) {
315 
316 		try {
317 			this.parseState.push(new AdviceEntry(parserContext.getDelegate().getLocalName(adviceElement)));
318 
319 			// create the method factory bean
320 			RootBeanDefinition methodDefinition = new RootBeanDefinition(MethodLocatingFactoryBean.class);
321 			methodDefinition.getPropertyValues().add("targetBeanName", aspectName);
322 			methodDefinition.getPropertyValues().add("methodName", adviceElement.getAttribute("method"));
323 			methodDefinition.setSynthetic(true);
324 
325 			// create instance factory definition
326 			RootBeanDefinition aspectFactoryDef =
327 					new RootBeanDefinition(SimpleBeanFactoryAwareAspectInstanceFactory.class);
328 			aspectFactoryDef.getPropertyValues().add("aspectBeanName", aspectName);
329 			aspectFactoryDef.setSynthetic(true);
330 
331 			// register the pointcut
332 			AbstractBeanDefinition adviceDef = createAdviceDefinition(
333 					adviceElement, parserContext, aspectName, order, methodDefinition, aspectFactoryDef,
334 					beanDefinitions, beanReferences);
335 
336 			// configure the advisor
337 			RootBeanDefinition advisorDefinition = new RootBeanDefinition(AspectJPointcutAdvisor.class);
338 			advisorDefinition.setSource(parserContext.extractSource(adviceElement));
339 			advisorDefinition.getConstructorArgumentValues().addGenericArgumentValue(adviceDef);
340 			if (aspectElement.hasAttribute(ORDER_PROPERTY)) {
341 				advisorDefinition.getPropertyValues().add(
342 						ORDER_PROPERTY, aspectElement.getAttribute(ORDER_PROPERTY));
343 			}
344 
345 			// register the final advisor
346 			parserContext.getReaderContext().registerWithGeneratedName(advisorDefinition);
347 
348 			return advisorDefinition;
349 		}
350 		finally {
351 			this.parseState.pop();
352 		}
353 	}
354 
355 	/**
356 	 * Creates the RootBeanDefinition for a POJO advice bean. Also causes pointcut
357 	 * parsing to occur so that the pointcut may be associate with the advice bean.
358 	 * This same pointcut is also configured as the pointcut for the enclosing
359 	 * Advisor definition using the supplied MutablePropertyValues.
360 	 */
createAdviceDefinition( Element adviceElement, ParserContext parserContext, String aspectName, int order, RootBeanDefinition methodDef, RootBeanDefinition aspectFactoryDef, List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences)361 	private AbstractBeanDefinition createAdviceDefinition(
362 			Element adviceElement, ParserContext parserContext, String aspectName, int order,
363 			RootBeanDefinition methodDef, RootBeanDefinition aspectFactoryDef,
364 			List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences) {
365 
366 		RootBeanDefinition adviceDefinition = new RootBeanDefinition(getAdviceClass(adviceElement, parserContext));
367 		adviceDefinition.setSource(parserContext.extractSource(adviceElement));
368 
369 		adviceDefinition.getPropertyValues().add(ASPECT_NAME_PROPERTY, aspectName);
370 		adviceDefinition.getPropertyValues().add(DECLARATION_ORDER_PROPERTY, order);
371 
372 		if (adviceElement.hasAttribute(RETURNING)) {
373 			adviceDefinition.getPropertyValues().add(
374 					RETURNING_PROPERTY, adviceElement.getAttribute(RETURNING));
375 		}
376 		if (adviceElement.hasAttribute(THROWING)) {
377 			adviceDefinition.getPropertyValues().add(
378 					THROWING_PROPERTY, adviceElement.getAttribute(THROWING));
379 		}
380 		if (adviceElement.hasAttribute(ARG_NAMES)) {
381 			adviceDefinition.getPropertyValues().add(
382 					ARG_NAMES_PROPERTY, adviceElement.getAttribute(ARG_NAMES));
383 		}
384 
385 		ConstructorArgumentValues cav = adviceDefinition.getConstructorArgumentValues();
386 		cav.addIndexedArgumentValue(METHOD_INDEX, methodDef);
387 
388 		Object pointcut = parsePointcutProperty(adviceElement, parserContext);
389 		if (pointcut instanceof BeanDefinition) {
390 			cav.addIndexedArgumentValue(POINTCUT_INDEX, pointcut);
391 			beanDefinitions.add((BeanDefinition) pointcut);
392 		}
393 		else if (pointcut instanceof String) {
394 			RuntimeBeanReference pointcutRef = new RuntimeBeanReference((String) pointcut);
395 			cav.addIndexedArgumentValue(POINTCUT_INDEX, pointcutRef);
396 			beanReferences.add(pointcutRef);
397 		}
398 
399 		cav.addIndexedArgumentValue(ASPECT_INSTANCE_FACTORY_INDEX, aspectFactoryDef);
400 
401 		return adviceDefinition;
402 	}
403 
404 	/**
405 	 * Gets the advice implementation class corresponding to the supplied {@link Element}.
406 	 */
getAdviceClass(Element adviceElement, ParserContext parserContext)407 	private Class getAdviceClass(Element adviceElement, ParserContext parserContext) {
408 		String elementName = parserContext.getDelegate().getLocalName(adviceElement);
409 		if (BEFORE.equals(elementName)) {
410 			return AspectJMethodBeforeAdvice.class;
411 		}
412 		else if (AFTER.equals(elementName)) {
413 			return AspectJAfterAdvice.class;
414 		}
415 		else if (AFTER_RETURNING_ELEMENT.equals(elementName)) {
416 			return AspectJAfterReturningAdvice.class;
417 		}
418 		else if (AFTER_THROWING_ELEMENT.equals(elementName)) {
419 			return AspectJAfterThrowingAdvice.class;
420 		}
421 		else if (AROUND.equals(elementName)) {
422 			return AspectJAroundAdvice.class;
423 		}
424 		else {
425 			throw new IllegalArgumentException("Unknown advice kind [" + elementName + "].");
426 		}
427 	}
428 
429 	/**
430 	 * Parses the supplied <code>&lt;pointcut&gt;</code> and registers the resulting
431 	 * Pointcut with the BeanDefinitionRegistry.
432 	 */
parsePointcut(Element pointcutElement, ParserContext parserContext)433 	private AbstractBeanDefinition parsePointcut(Element pointcutElement, ParserContext parserContext) {
434 		String id = pointcutElement.getAttribute(ID);
435 		String expression = pointcutElement.getAttribute(EXPRESSION);
436 
437 		AbstractBeanDefinition pointcutDefinition = null;
438 
439 		try {
440 			this.parseState.push(new PointcutEntry(id));
441 			pointcutDefinition = createPointcutDefinition(expression);
442 			pointcutDefinition.setSource(parserContext.extractSource(pointcutElement));
443 
444 			String pointcutBeanName = id;
445 			if (StringUtils.hasText(pointcutBeanName)) {
446 				parserContext.getRegistry().registerBeanDefinition(pointcutBeanName, pointcutDefinition);
447 			}
448 			else {
449 				pointcutBeanName = parserContext.getReaderContext().registerWithGeneratedName(pointcutDefinition);
450 			}
451 
452 			parserContext.registerComponent(
453 					new PointcutComponentDefinition(pointcutBeanName, pointcutDefinition, expression));
454 		}
455 		finally {
456 			this.parseState.pop();
457 		}
458 
459 		return pointcutDefinition;
460 	}
461 
462 	/**
463 	 * Parses the <code>pointcut</code> or <code>pointcut-ref</code> attributes of the supplied
464 	 * {@link Element} and add a <code>pointcut</code> property as appropriate. Generates a
465 	 * {@link org.springframework.beans.factory.config.BeanDefinition} for the pointcut if  necessary
466 	 * and returns its bean name, otherwise returns the bean name of the referred pointcut.
467 	 */
parsePointcutProperty(Element element, ParserContext parserContext)468 	private Object parsePointcutProperty(Element element, ParserContext parserContext) {
469 		if (element.hasAttribute(POINTCUT) && element.hasAttribute(POINTCUT_REF)) {
470 			parserContext.getReaderContext().error(
471 					"Cannot define both 'pointcut' and 'pointcut-ref' on <advisor> tag.",
472 					element, this.parseState.snapshot());
473 			return null;
474 		}
475 		else if (element.hasAttribute(POINTCUT)) {
476 			// Create a pointcut for the anonymous pc and register it.
477 			String expression = element.getAttribute(POINTCUT);
478 			AbstractBeanDefinition pointcutDefinition = createPointcutDefinition(expression);
479 			pointcutDefinition.setSource(parserContext.extractSource(element));
480 			return pointcutDefinition;
481 		}
482 		else if (element.hasAttribute(POINTCUT_REF)) {
483 			String pointcutRef = element.getAttribute(POINTCUT_REF);
484 			if (!StringUtils.hasText(pointcutRef)) {
485 				parserContext.getReaderContext().error(
486 						"'pointcut-ref' attribute contains empty value.", element, this.parseState.snapshot());
487 				return null;
488 			}
489 			return pointcutRef;
490 		}
491 		else {
492 			parserContext.getReaderContext().error(
493 					"Must define one of 'pointcut' or 'pointcut-ref' on <advisor> tag.",
494 					element, this.parseState.snapshot());
495 			return null;
496 		}
497 	}
498 
499 	/**
500 	 * Creates a {@link BeanDefinition} for the {@link AspectJExpressionPointcut} class using
501 	 * the supplied pointcut expression.
502 	 */
createPointcutDefinition(String expression)503 	protected AbstractBeanDefinition createPointcutDefinition(String expression) {
504 		RootBeanDefinition beanDefinition = new RootBeanDefinition(AspectJExpressionPointcut.class);
505 		beanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
506 		beanDefinition.setSynthetic(true);
507 		beanDefinition.getPropertyValues().add(EXPRESSION, expression);
508 		return beanDefinition;
509 	}
510 
511 }
512