1 /* 2 * Copyright 2002-2007 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.aspectj.annotation; 18 19 import java.lang.reflect.Field; 20 import java.lang.reflect.Method; 21 import java.util.LinkedList; 22 import java.util.List; 23 24 import org.aopalliance.aop.Advice; 25 import org.aspectj.lang.annotation.AfterReturning; 26 import org.aspectj.lang.annotation.AfterThrowing; 27 import org.aspectj.lang.annotation.DeclareParents; 28 import org.aspectj.lang.annotation.Pointcut; 29 30 import org.springframework.aop.Advisor; 31 import org.springframework.aop.MethodBeforeAdvice; 32 import org.springframework.aop.aspectj.AbstractAspectJAdvice; 33 import org.springframework.aop.aspectj.AspectJAfterAdvice; 34 import org.springframework.aop.aspectj.AspectJAfterReturningAdvice; 35 import org.springframework.aop.aspectj.AspectJAfterThrowingAdvice; 36 import org.springframework.aop.aspectj.AspectJAroundAdvice; 37 import org.springframework.aop.aspectj.AspectJExpressionPointcut; 38 import org.springframework.aop.aspectj.AspectJMethodBeforeAdvice; 39 import org.springframework.aop.aspectj.DeclareParentsAdvisor; 40 import org.springframework.aop.framework.AopConfigException; 41 import org.springframework.aop.support.DefaultPointcutAdvisor; 42 import org.springframework.core.annotation.AnnotationUtils; 43 import org.springframework.util.ReflectionUtils; 44 import org.springframework.util.StringUtils; 45 46 /** 47 * Factory that can create Spring AOP Advisors given AspectJ classes from 48 * classes honoring the AspectJ 5 annotation syntax, using reflection to 49 * invoke the corresponding advice methods. 50 * 51 * @author Rod Johnson 52 * @author Adrian Colyer 53 * @author Juergen Hoeller 54 * @author Ramnivas Laddad 55 * @since 2.0 56 */ 57 public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFactory { 58 getAdvisors(MetadataAwareAspectInstanceFactory maaif)59 public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory maaif) { 60 final Class<?> aspectClass = maaif.getAspectMetadata().getAspectClass(); 61 final String aspectName = maaif.getAspectMetadata().getAspectName(); 62 validate(aspectClass); 63 64 // We need to wrap the MetadataAwareAspectInstanceFactory with a decorator 65 // so that it will only instantiate once. 66 final MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory = 67 new LazySingletonAspectInstanceFactoryDecorator(maaif); 68 69 final List<Advisor> advisors = new LinkedList<Advisor>(); 70 ReflectionUtils.doWithMethods(aspectClass, new ReflectionUtils.MethodCallback() { 71 public void doWith(Method method) throws IllegalArgumentException { 72 // Exclude pointcuts 73 if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) { 74 Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName); 75 if (advisor != null) { 76 advisors.add(advisor); 77 } 78 } 79 } 80 }); 81 82 // If it's a per target aspect, emit the dummy instantiating aspect. 83 if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) { 84 Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory); 85 advisors.add(0, instantiationAdvisor); 86 } 87 88 // Find introduction fields. 89 for (Field field : aspectClass.getDeclaredFields()) { 90 Advisor advisor = getDeclareParentsAdvisor(field); 91 if (advisor != null) { 92 advisors.add(advisor); 93 } 94 } 95 96 return advisors; 97 } 98 99 /** 100 * Build a {@link org.springframework.aop.aspectj.DeclareParentsAdvisor} 101 * for the given introduction field. 102 * <p>Resulting Advisors will need to be evaluated for targets. 103 * @param introductionField the field to introspect 104 * @return <code>null</code> if not an Advisor 105 */ getDeclareParentsAdvisor(Field introductionField)106 private Advisor getDeclareParentsAdvisor(Field introductionField) { 107 DeclareParents declareParents = (DeclareParents) introductionField.getAnnotation(DeclareParents.class); 108 if (declareParents == null) { 109 // Not an introduction field 110 return null; 111 } 112 113 if (DeclareParents.class.equals(declareParents.defaultImpl())) { 114 // This is what comes back if it wasn't set. This seems bizarre... 115 // TODO this restriction possibly should be relaxed 116 throw new IllegalStateException("defaultImpl must be set on DeclareParents"); 117 } 118 119 return new DeclareParentsAdvisor( 120 introductionField.getType(), declareParents.value(), declareParents.defaultImpl()); 121 } 122 123 getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aif, int declarationOrderInAspect, String aspectName)124 public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aif, 125 int declarationOrderInAspect, String aspectName) { 126 127 validate(aif.getAspectMetadata().getAspectClass()); 128 129 AspectJExpressionPointcut ajexp = 130 getPointcut(candidateAdviceMethod, aif.getAspectMetadata().getAspectClass()); 131 if (ajexp == null) { 132 return null; 133 } 134 return new InstantiationModelAwarePointcutAdvisorImpl( 135 this, ajexp, aif, candidateAdviceMethod, declarationOrderInAspect, aspectName); 136 } 137 getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass)138 private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) { 139 AspectJAnnotation<?> aspectJAnnotation = 140 AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod); 141 if (aspectJAnnotation == null) { 142 return null; 143 } 144 AspectJExpressionPointcut ajexp = 145 new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class[0]); 146 ajexp.setExpression(aspectJAnnotation.getPointcutExpression()); 147 return ajexp; 148 } 149 150 getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut ajexp, MetadataAwareAspectInstanceFactory aif, int declarationOrderInAspect, String aspectName)151 public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut ajexp, 152 MetadataAwareAspectInstanceFactory aif, int declarationOrderInAspect, String aspectName) { 153 154 Class<?> candidateAspectClass = aif.getAspectMetadata().getAspectClass(); 155 validate(candidateAspectClass); 156 157 AspectJAnnotation<?> aspectJAnnotation = 158 AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod); 159 if (aspectJAnnotation == null) { 160 return null; 161 } 162 163 // If we get here, we know we have an AspectJ method. 164 // Check that it's an AspectJ-annotated class 165 if (!isAspect(candidateAspectClass)) { 166 throw new AopConfigException("Advice must be declared inside an aspect type: " + 167 "Offending method '" + candidateAdviceMethod + "' in class [" + 168 candidateAspectClass.getName() + "]"); 169 } 170 171 if (logger.isDebugEnabled()) { 172 logger.debug("Found AspectJ method: " + candidateAdviceMethod); 173 } 174 175 AbstractAspectJAdvice springAdvice; 176 177 switch (aspectJAnnotation.getAnnotationType()) { 178 case AtBefore: 179 springAdvice = new AspectJMethodBeforeAdvice(candidateAdviceMethod, ajexp, aif); 180 break; 181 case AtAfter: 182 springAdvice = new AspectJAfterAdvice(candidateAdviceMethod, ajexp, aif); 183 break; 184 case AtAfterReturning: 185 springAdvice = new AspectJAfterReturningAdvice(candidateAdviceMethod, ajexp, aif); 186 AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation(); 187 if (StringUtils.hasText(afterReturningAnnotation.returning())) { 188 springAdvice.setReturningName(afterReturningAnnotation.returning()); 189 } 190 break; 191 case AtAfterThrowing: 192 springAdvice = new AspectJAfterThrowingAdvice(candidateAdviceMethod, ajexp, aif); 193 AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation(); 194 if (StringUtils.hasText(afterThrowingAnnotation.throwing())) { 195 springAdvice.setThrowingName(afterThrowingAnnotation.throwing()); 196 } 197 break; 198 case AtAround: 199 springAdvice = new AspectJAroundAdvice(candidateAdviceMethod, ajexp, aif); 200 break; 201 case AtPointcut: 202 if (logger.isDebugEnabled()) { 203 logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'"); 204 } 205 return null; 206 default: 207 throw new UnsupportedOperationException( 208 "Unsupported advice type on method " + candidateAdviceMethod); 209 } 210 211 // Now to configure the advice... 212 springAdvice.setAspectName(aspectName); 213 springAdvice.setDeclarationOrder(declarationOrderInAspect); 214 String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod); 215 if (argNames != null) { 216 springAdvice.setArgumentNamesFromStringArray(argNames); 217 } 218 springAdvice.calculateArgumentBindings(); 219 return springAdvice; 220 } 221 222 /** 223 * Synthetic advisor that instantiates the aspect. 224 * Triggered by per-clause pointcut on non-singleton aspect. 225 * The advice has no effect. 226 */ 227 protected static class SyntheticInstantiationAdvisor extends DefaultPointcutAdvisor { 228 SyntheticInstantiationAdvisor(final MetadataAwareAspectInstanceFactory aif)229 public SyntheticInstantiationAdvisor(final MetadataAwareAspectInstanceFactory aif) { 230 super(aif.getAspectMetadata().getPerClausePointcut(), new MethodBeforeAdvice() { 231 public void before(Method method, Object[] args, Object target) { 232 // Simply instantiate the aspect 233 aif.getAspectInstance(); 234 } 235 }); 236 } 237 } 238 239 } 240