/* * Copyright 2002-2018 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.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.HashMap; import java.util.Map; import java.util.StringTokenizer; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.AjType; import org.aspectj.lang.reflect.AjTypeSystem; import org.aspectj.lang.reflect.PerClauseKind; import org.springframework.aop.framework.AopConfigException; import org.springframework.core.ParameterNameDiscoverer; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.lang.Nullable; import org.springframework.util.StringUtils; /** * Abstract base class for factories that can create Spring AOP Advisors * given AspectJ classes from classes honoring the AspectJ 5 annotation syntax. * *
This class handles annotation parsing and validation functionality.
* It does not actually generate Spring AOP Advisors, which is deferred to subclasses.
*
* @author Rod Johnson
* @author Adrian Colyer
* @author Juergen Hoeller
* @since 2.0
*/
public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFactory {
private static final String AJC_MAGIC = "ajc$";
/** Logger available to subclasses. */
protected final Log logger = LogFactory.getLog(getClass());
protected final ParameterNameDiscoverer parameterNameDiscoverer = new AspectJAnnotationParameterNameDiscoverer();
/**
* We consider something to be an AspectJ aspect suitable for use by the Spring AOP system
* if it has the @Aspect annotation, and was not compiled by ajc. The reason for this latter test
* is that aspects written in the code-style (AspectJ language) also have the annotation present
* when compiled by ajc with the -1.5 flag, yet they cannot be consumed by Spring AOP.
*/
@Override
public boolean isAspect(Class> clazz) {
return (hasAspectAnnotation(clazz) && !compiledByAjc(clazz));
}
private boolean hasAspectAnnotation(Class> clazz) {
return (AnnotationUtils.findAnnotation(clazz, Aspect.class) != null);
}
/**
* We need to detect this as "code-style" AspectJ aspects should not be
* interpreted by Spring AOP.
*/
private boolean compiledByAjc(Class> clazz) {
// The AJTypeSystem goes to great lengths to provide a uniform appearance between code-style and
// annotation-style aspects. Therefore there is no 'clean' way to tell them apart. Here we rely on
// an implementation detail of the AspectJ compiler.
for (Field field : clazz.getDeclaredFields()) {
if (field.getName().startsWith(AJC_MAGIC)) {
return true;
}
}
return false;
}
@Override
public void validate(Class> aspectClass) throws AopConfigException {
// If the parent has the annotation and isn't abstract it's an error
if (aspectClass.getSuperclass().getAnnotation(Aspect.class) != null &&
!Modifier.isAbstract(aspectClass.getSuperclass().getModifiers())) {
throw new AopConfigException("[" + aspectClass.getName() + "] cannot extend concrete aspect [" +
aspectClass.getSuperclass().getName() + "]");
}
AjType> ajType = AjTypeSystem.getAjType(aspectClass);
if (!ajType.isAspect()) {
throw new NotAnAtAspectException(aspectClass);
}
if (ajType.getPerClause().getKind() == PerClauseKind.PERCFLOW) {
throw new AopConfigException(aspectClass.getName() + " uses percflow instantiation model: " +
"This is not supported in Spring AOP.");
}
if (ajType.getPerClause().getKind() == PerClauseKind.PERCFLOWBELOW) {
throw new AopConfigException(aspectClass.getName() + " uses percflowbelow instantiation model: " +
"This is not supported in Spring AOP.");
}
}
/**
* Find and return the first AspectJ annotation on the given method
* (there should only be one anyway...).
*/
@SuppressWarnings("unchecked")
@Nullable
protected static AspectJAnnotation> findAspectJAnnotationOnMethod(Method method) {
Class>[] classesToLookFor = new Class>[] {
Before.class, Around.class, After.class, AfterReturning.class, AfterThrowing.class, Pointcut.class};
for (Class> clazz : classesToLookFor) {
AspectJAnnotation> foundAnnotation = findAnnotation(method, (Class