001/* 002 * Copyright 2002-2020 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.aop.aspectj.annotation; 018 019import java.io.Serializable; 020import java.lang.annotation.Annotation; 021import java.lang.reflect.Field; 022import java.lang.reflect.Method; 023import java.util.ArrayList; 024import java.util.Collections; 025import java.util.Comparator; 026import java.util.List; 027 028import org.aopalliance.aop.Advice; 029import org.aspectj.lang.annotation.After; 030import org.aspectj.lang.annotation.AfterReturning; 031import org.aspectj.lang.annotation.AfterThrowing; 032import org.aspectj.lang.annotation.Around; 033import org.aspectj.lang.annotation.Before; 034import org.aspectj.lang.annotation.DeclareParents; 035import org.aspectj.lang.annotation.Pointcut; 036 037import org.springframework.aop.Advisor; 038import org.springframework.aop.MethodBeforeAdvice; 039import org.springframework.aop.aspectj.AbstractAspectJAdvice; 040import org.springframework.aop.aspectj.AspectJAfterAdvice; 041import org.springframework.aop.aspectj.AspectJAfterReturningAdvice; 042import org.springframework.aop.aspectj.AspectJAfterThrowingAdvice; 043import org.springframework.aop.aspectj.AspectJAroundAdvice; 044import org.springframework.aop.aspectj.AspectJExpressionPointcut; 045import org.springframework.aop.aspectj.AspectJMethodBeforeAdvice; 046import org.springframework.aop.aspectj.DeclareParentsAdvisor; 047import org.springframework.aop.framework.AopConfigException; 048import org.springframework.aop.support.DefaultPointcutAdvisor; 049import org.springframework.beans.factory.BeanFactory; 050import org.springframework.core.annotation.AnnotationUtils; 051import org.springframework.core.convert.converter.Converter; 052import org.springframework.core.convert.converter.ConvertingComparator; 053import org.springframework.util.ReflectionUtils; 054import org.springframework.util.StringUtils; 055import org.springframework.util.comparator.CompoundComparator; 056import org.springframework.util.comparator.InstanceComparator; 057 058/** 059 * Factory that can create Spring AOP Advisors given AspectJ classes from 060 * classes honoring the AspectJ 5 annotation syntax, using reflection to 061 * invoke the corresponding advice methods. 062 * 063 * @author Rod Johnson 064 * @author Adrian Colyer 065 * @author Juergen Hoeller 066 * @author Ramnivas Laddad 067 * @author Phillip Webb 068 * @since 2.0 069 */ 070@SuppressWarnings("serial") 071public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFactory implements Serializable { 072 073 private static final Comparator<Method> METHOD_COMPARATOR; 074 075 static { 076 CompoundComparator<Method> comparator = new CompoundComparator<Method>(); 077 comparator.addComparator(new ConvertingComparator<Method, Annotation>( 078 new InstanceComparator<Annotation>( 079 Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class), 080 new Converter<Method, Annotation>() { 081 @Override 082 public Annotation convert(Method method) { 083 AspectJAnnotation<?> ann = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(method); 084 return (ann != null ? ann.getAnnotation() : null); 085 } 086 })); 087 comparator.addComparator(new ConvertingComparator<Method, String>( 088 new Converter<Method, String>() { 089 @Override 090 public String convert(Method method) { 091 return method.getName(); 092 } 093 })); 094 METHOD_COMPARATOR = comparator; 095 } 096 097 098 private final BeanFactory beanFactory; 099 100 101 /** 102 * Create a new {@code ReflectiveAspectJAdvisorFactory}. 103 */ 104 public ReflectiveAspectJAdvisorFactory() { 105 this(null); 106 } 107 108 /** 109 * Create a new {@code ReflectiveAspectJAdvisorFactory}, propagating the given 110 * {@link BeanFactory} to the created {@link AspectJExpressionPointcut} instances, 111 * for bean pointcut handling as well as consistent {@link ClassLoader} resolution. 112 * @param beanFactory the BeanFactory to propagate (may be {@code null}} 113 * @since 4.3.6 114 * @see AspectJExpressionPointcut#setBeanFactory 115 * @see org.springframework.beans.factory.config.ConfigurableBeanFactory#getBeanClassLoader() 116 */ 117 public ReflectiveAspectJAdvisorFactory(BeanFactory beanFactory) { 118 this.beanFactory = beanFactory; 119 } 120 121 122 @Override 123 public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) { 124 Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass(); 125 String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName(); 126 validate(aspectClass); 127 128 // We need to wrap the MetadataAwareAspectInstanceFactory with a decorator 129 // so that it will only instantiate once. 130 MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory = 131 new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory); 132 133 List<Advisor> advisors = new ArrayList<Advisor>(); 134 for (Method method : getAdvisorMethods(aspectClass)) { 135 Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName); 136 if (advisor != null) { 137 advisors.add(advisor); 138 } 139 } 140 141 // If it's a per target aspect, emit the dummy instantiating aspect. 142 if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) { 143 Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory); 144 advisors.add(0, instantiationAdvisor); 145 } 146 147 // Find introduction fields. 148 for (Field field : aspectClass.getDeclaredFields()) { 149 Advisor advisor = getDeclareParentsAdvisor(field); 150 if (advisor != null) { 151 advisors.add(advisor); 152 } 153 } 154 155 return advisors; 156 } 157 158 private List<Method> getAdvisorMethods(Class<?> aspectClass) { 159 final List<Method> methods = new ArrayList<Method>(); 160 ReflectionUtils.doWithMethods(aspectClass, new ReflectionUtils.MethodCallback() { 161 @Override 162 public void doWith(Method method) throws IllegalArgumentException { 163 // Exclude pointcuts 164 if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) { 165 methods.add(method); 166 } 167 } 168 }); 169 Collections.sort(methods, METHOD_COMPARATOR); 170 return methods; 171 } 172 173 /** 174 * Build a {@link org.springframework.aop.aspectj.DeclareParentsAdvisor} 175 * for the given introduction field. 176 * <p>Resulting Advisors will need to be evaluated for targets. 177 * @param introductionField the field to introspect 178 * @return the Advisor instance, or {@code null} if not an Advisor 179 */ 180 private Advisor getDeclareParentsAdvisor(Field introductionField) { 181 DeclareParents declareParents = introductionField.getAnnotation(DeclareParents.class); 182 if (declareParents == null) { 183 // Not an introduction field 184 return null; 185 } 186 187 if (DeclareParents.class == declareParents.defaultImpl()) { 188 throw new IllegalStateException("'defaultImpl' attribute must be set on DeclareParents"); 189 } 190 191 return new DeclareParentsAdvisor( 192 introductionField.getType(), declareParents.value(), declareParents.defaultImpl()); 193 } 194 195 196 @Override 197 public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory, 198 int declarationOrderInAspect, String aspectName) { 199 200 validate(aspectInstanceFactory.getAspectMetadata().getAspectClass()); 201 202 AspectJExpressionPointcut expressionPointcut = getPointcut( 203 candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass()); 204 if (expressionPointcut == null) { 205 return null; 206 } 207 208 return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod, 209 this, aspectInstanceFactory, declarationOrderInAspect, aspectName); 210 } 211 212 private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) { 213 AspectJAnnotation<?> aspectJAnnotation = 214 AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod); 215 if (aspectJAnnotation == null) { 216 return null; 217 } 218 219 AspectJExpressionPointcut ajexp = 220 new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]); 221 ajexp.setExpression(aspectJAnnotation.getPointcutExpression()); 222 ajexp.setBeanFactory(this.beanFactory); 223 return ajexp; 224 } 225 226 227 @Override 228 public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut, 229 MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) { 230 231 Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass(); 232 validate(candidateAspectClass); 233 234 AspectJAnnotation<?> aspectJAnnotation = 235 AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod); 236 if (aspectJAnnotation == null) { 237 return null; 238 } 239 240 // If we get here, we know we have an AspectJ method. 241 // Check that it's an AspectJ-annotated class 242 if (!isAspect(candidateAspectClass)) { 243 throw new AopConfigException("Advice must be declared inside an aspect type: " + 244 "Offending method '" + candidateAdviceMethod + "' in class [" + 245 candidateAspectClass.getName() + "]"); 246 } 247 248 if (logger.isDebugEnabled()) { 249 logger.debug("Found AspectJ method: " + candidateAdviceMethod); 250 } 251 252 AbstractAspectJAdvice springAdvice; 253 254 switch (aspectJAnnotation.getAnnotationType()) { 255 case AtPointcut: 256 if (logger.isDebugEnabled()) { 257 logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'"); 258 } 259 return null; 260 case AtAround: 261 springAdvice = new AspectJAroundAdvice( 262 candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); 263 break; 264 case AtBefore: 265 springAdvice = new AspectJMethodBeforeAdvice( 266 candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); 267 break; 268 case AtAfter: 269 springAdvice = new AspectJAfterAdvice( 270 candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); 271 break; 272 case AtAfterReturning: 273 springAdvice = new AspectJAfterReturningAdvice( 274 candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); 275 AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation(); 276 if (StringUtils.hasText(afterReturningAnnotation.returning())) { 277 springAdvice.setReturningName(afterReturningAnnotation.returning()); 278 } 279 break; 280 case AtAfterThrowing: 281 springAdvice = new AspectJAfterThrowingAdvice( 282 candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); 283 AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation(); 284 if (StringUtils.hasText(afterThrowingAnnotation.throwing())) { 285 springAdvice.setThrowingName(afterThrowingAnnotation.throwing()); 286 } 287 break; 288 default: 289 throw new UnsupportedOperationException( 290 "Unsupported advice type on method: " + candidateAdviceMethod); 291 } 292 293 // Now to configure the advice... 294 springAdvice.setAspectName(aspectName); 295 springAdvice.setDeclarationOrder(declarationOrder); 296 String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod); 297 if (argNames != null) { 298 springAdvice.setArgumentNamesFromStringArray(argNames); 299 } 300 springAdvice.calculateArgumentBindings(); 301 302 return springAdvice; 303 } 304 305 306 /** 307 * Synthetic advisor that instantiates the aspect. 308 * Triggered by per-clause pointcut on non-singleton aspect. 309 * The advice has no effect. 310 */ 311 @SuppressWarnings("serial") 312 protected static class SyntheticInstantiationAdvisor extends DefaultPointcutAdvisor { 313 314 public SyntheticInstantiationAdvisor(final MetadataAwareAspectInstanceFactory aif) { 315 super(aif.getAspectMetadata().getPerClausePointcut(), new MethodBeforeAdvice() { 316 @Override 317 public void before(Method method, Object[] args, Object target) { 318 // Simply instantiate the aspect 319 aif.getAspectInstance(); 320 } 321 }); 322 } 323 } 324 325}