/* * Copyright 2002-2007 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.aop.aspectj.annotation; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.LinkedList; import java.util.List; import org.aopalliance.aop.Advice; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.DeclareParents; import org.aspectj.lang.annotation.Pointcut; import org.springframework.aop.Advisor; import org.springframework.aop.MethodBeforeAdvice; import org.springframework.aop.aspectj.AbstractAspectJAdvice; import org.springframework.aop.aspectj.AspectJAfterAdvice; import org.springframework.aop.aspectj.AspectJAfterReturningAdvice; import org.springframework.aop.aspectj.AspectJAfterThrowingAdvice; import org.springframework.aop.aspectj.AspectJAroundAdvice; import org.springframework.aop.aspectj.AspectJExpressionPointcut; import org.springframework.aop.aspectj.AspectJMethodBeforeAdvice; import org.springframework.aop.aspectj.DeclareParentsAdvisor; import org.springframework.aop.framework.AopConfigException; import org.springframework.aop.support.DefaultPointcutAdvisor; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; /** * Factory that can create Spring AOP Advisors given AspectJ classes from * classes honoring the AspectJ 5 annotation syntax, using reflection to * invoke the corresponding advice methods. * * @author Rod Johnson * @author Adrian Colyer * @author Juergen Hoeller * @author Ramnivas Laddad * @since 2.0 */ public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFactory { public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory maaif) { final Class<?> aspectClass = maaif.getAspectMetadata().getAspectClass(); final String aspectName = maaif.getAspectMetadata().getAspectName(); validate(aspectClass); // We need to wrap the MetadataAwareAspectInstanceFactory with a decorator // so that it will only instantiate once. final MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory = new LazySingletonAspectInstanceFactoryDecorator(maaif); final List<Advisor> advisors = new LinkedList<Advisor>(); ReflectionUtils.doWithMethods(aspectClass, new ReflectionUtils.MethodCallback() { public void doWith(Method method) throws IllegalArgumentException { // Exclude pointcuts if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) { Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName); if (advisor != null) { advisors.add(advisor); } } } }); // If it's a per target aspect, emit the dummy instantiating aspect. if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) { Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory); advisors.add(0, instantiationAdvisor); } // Find introduction fields. for (Field field : aspectClass.getDeclaredFields()) { Advisor advisor = getDeclareParentsAdvisor(field); if (advisor != null) { advisors.add(advisor); } } return advisors; } /** * Build a {@link org.springframework.aop.aspectj.DeclareParentsAdvisor} * for the given introduction field. * <p>Resulting Advisors will need to be evaluated for targets. * @param introductionField the field to introspect * @return <code>null</code> if not an Advisor */ private Advisor getDeclareParentsAdvisor(Field introductionField) { DeclareParents declareParents = (DeclareParents) introductionField.getAnnotation(DeclareParents.class); if (declareParents == null) { // Not an introduction field return null; } if (DeclareParents.class.equals(declareParents.defaultImpl())) { // This is what comes back if it wasn't set. This seems bizarre... // TODO this restriction possibly should be relaxed throw new IllegalStateException("defaultImpl must be set on DeclareParents"); } return new DeclareParentsAdvisor( introductionField.getType(), declareParents.value(), declareParents.defaultImpl()); } public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aif, int declarationOrderInAspect, String aspectName) { validate(aif.getAspectMetadata().getAspectClass()); AspectJExpressionPointcut ajexp = getPointcut(candidateAdviceMethod, aif.getAspectMetadata().getAspectClass()); if (ajexp == null) { return null; } return new InstantiationModelAwarePointcutAdvisorImpl( this, ajexp, aif, candidateAdviceMethod, declarationOrderInAspect, aspectName); } private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) { AspectJAnnotation<?> aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod); if (aspectJAnnotation == null) { return null; } AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class[0]); ajexp.setExpression(aspectJAnnotation.getPointcutExpression()); return ajexp; } public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut ajexp, MetadataAwareAspectInstanceFactory aif, int declarationOrderInAspect, String aspectName) { Class<?> candidateAspectClass = aif.getAspectMetadata().getAspectClass(); validate(candidateAspectClass); AspectJAnnotation<?> aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod); if (aspectJAnnotation == null) { return null; } // If we get here, we know we have an AspectJ method. // Check that it's an AspectJ-annotated class if (!isAspect(candidateAspectClass)) { throw new AopConfigException("Advice must be declared inside an aspect type: " + "Offending method '" + candidateAdviceMethod + "' in class [" + candidateAspectClass.getName() + "]"); } if (logger.isDebugEnabled()) { logger.debug("Found AspectJ method: " + candidateAdviceMethod); } AbstractAspectJAdvice springAdvice; switch (aspectJAnnotation.getAnnotationType()) { case AtBefore: springAdvice = new AspectJMethodBeforeAdvice(candidateAdviceMethod, ajexp, aif); break; case AtAfter: springAdvice = new AspectJAfterAdvice(candidateAdviceMethod, ajexp, aif); break; case AtAfterReturning: springAdvice = new AspectJAfterReturningAdvice(candidateAdviceMethod, ajexp, aif); AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation(); if (StringUtils.hasText(afterReturningAnnotation.returning())) { springAdvice.setReturningName(afterReturningAnnotation.returning()); } break; case AtAfterThrowing: springAdvice = new AspectJAfterThrowingAdvice(candidateAdviceMethod, ajexp, aif); AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation(); if (StringUtils.hasText(afterThrowingAnnotation.throwing())) { springAdvice.setThrowingName(afterThrowingAnnotation.throwing()); } break; case AtAround: springAdvice = new AspectJAroundAdvice(candidateAdviceMethod, ajexp, aif); break; case AtPointcut: if (logger.isDebugEnabled()) { logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'"); } return null; default: throw new UnsupportedOperationException( "Unsupported advice type on method " + candidateAdviceMethod); } // Now to configure the advice... springAdvice.setAspectName(aspectName); springAdvice.setDeclarationOrder(declarationOrderInAspect); String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod); if (argNames != null) { springAdvice.setArgumentNamesFromStringArray(argNames); } springAdvice.calculateArgumentBindings(); return springAdvice; } /** * Synthetic advisor that instantiates the aspect. * Triggered by per-clause pointcut on non-singleton aspect. * The advice has no effect. */ protected static class SyntheticInstantiationAdvisor extends DefaultPointcutAdvisor { public SyntheticInstantiationAdvisor(final MetadataAwareAspectInstanceFactory aif) { super(aif.getAspectMetadata().getPerClausePointcut(), new MethodBeforeAdvice() { public void before(Method method, Object[] args, Object target) { // Simply instantiate the aspect aif.getAspectInstance(); } }); } } }