1 /* 2 * Copyright 2002-2009 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 org.aspectj.lang.annotation.Aspect; 20 import org.aspectj.lang.reflect.AjType; 21 import org.aspectj.lang.reflect.AjTypeSystem; 22 import org.aspectj.lang.reflect.PerClauseKind; 23 24 import org.springframework.aop.Pointcut; 25 import org.springframework.aop.aspectj.AspectJExpressionPointcut; 26 import org.springframework.aop.aspectj.TypePatternClassFilter; 27 import org.springframework.aop.framework.AopConfigException; 28 import org.springframework.aop.support.ComposablePointcut; 29 30 /** 31 * Metadata for an AspectJ aspect class, with an additional Spring AOP pointcut 32 * for the per clause. 33 * 34 * <p>Uses AspectJ 5 AJType reflection API, so is only supported on Java 5. 35 * Enables us to work with different AspectJ instantiation models such as 36 * "singleton", "pertarget" and "perthis". 37 * 38 * @author Rod Johnson 39 * @author Juergen Hoeller 40 * @since 2.0 41 * @see org.springframework.aop.aspectj.AspectJExpressionPointcut 42 */ 43 public class AspectMetadata { 44 45 /** 46 * AspectJ reflection information (AspectJ 5 / Java 5 specific). 47 */ 48 private final AjType ajType; 49 50 /** 51 * Spring AOP pointcut corresponding to the per clause of the 52 * aspect. Will be the Pointcut.TRUE canonical instance in the 53 * case of a singleton, otherwise an AspectJExpressionPointcut. 54 */ 55 private final Pointcut perClausePointcut; 56 57 /** 58 * The name of this aspect as defined to Spring (the bean name) - 59 * allows us to determine if two pieces of advice come from the 60 * same aspect and hence their relative precedence. 61 */ 62 private String aspectName; 63 64 65 /** 66 * Create a new AspectMetadata instance for the given aspect class. 67 * @param aspectClass the aspect class 68 * @param aspectName the name of the aspect 69 */ AspectMetadata(Class<?> aspectClass, String aspectName)70 public AspectMetadata(Class<?> aspectClass, String aspectName) { 71 this.aspectName = aspectName; 72 73 Class<?> currClass = aspectClass; 74 AjType ajType = null; 75 while (!currClass.equals(Object.class)) { 76 AjType ajTypeToCheck = AjTypeSystem.getAjType(currClass); 77 if (ajTypeToCheck.isAspect()) { 78 ajType = ajTypeToCheck; 79 break; 80 } 81 currClass = currClass.getSuperclass(); 82 } 83 if (ajType == null) { 84 throw new IllegalArgumentException("Class '" + aspectClass.getName() + "' is not an @AspectJ aspect"); 85 } 86 this.ajType = ajType; 87 if (this.ajType.getDeclarePrecedence().length > 0) { 88 throw new IllegalArgumentException("DeclarePrecendence not presently supported in Spring AOP"); 89 } 90 91 switch (this.ajType.getPerClause().getKind()) { 92 case SINGLETON : 93 this.perClausePointcut = Pointcut.TRUE; 94 return; 95 case PERTARGET : case PERTHIS : 96 AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut(); 97 ajexp.setLocation("@Aspect annotation on " + aspectClass.getName()); 98 ajexp.setExpression(findPerClause(aspectClass)); 99 this.perClausePointcut = ajexp; 100 return; 101 case PERTYPEWITHIN : 102 // Works with a type pattern 103 this.perClausePointcut = new ComposablePointcut(new TypePatternClassFilter(findPerClause(aspectClass))); 104 return; 105 default : 106 throw new AopConfigException( 107 "PerClause " + ajType.getPerClause().getKind() + " not supported by Spring AOP for " + aspectClass); 108 } 109 } 110 111 /** 112 * Extract contents from String of form <code>pertarget(contents)</code>. 113 */ findPerClause(Class<?> aspectClass)114 private String findPerClause(Class<?> aspectClass) { 115 // TODO when AspectJ provides this, we can remove this hack. Hence we don't 116 // bother to make it elegant. Or efficient. Or robust :-) 117 String str = aspectClass.getAnnotation(Aspect.class).value(); 118 str = str.substring(str.indexOf("(") + 1); 119 str = str.substring(0, str.length() - 1); 120 return str; 121 } 122 123 124 /** 125 * Return AspectJ reflection information. 126 */ getAjType()127 public AjType getAjType() { 128 return this.ajType; 129 } 130 131 /** 132 * Return the aspect class. 133 */ getAspectClass()134 public Class getAspectClass() { 135 return this.ajType.getJavaClass(); 136 } 137 138 /** 139 * Return the aspect class. 140 */ getAspectName()141 public String getAspectName() { 142 return this.aspectName; 143 } 144 145 /** 146 * Return a Spring pointcut expression for a singleton aspect. 147 * (e.g. <code>Pointcut.TRUE</code> if it's a singleton). 148 */ getPerClausePointcut()149 public Pointcut getPerClausePointcut() { 150 return this.perClausePointcut; 151 } 152 153 /** 154 * Return whether the aspect is defined as "perthis" or "pertarget". 155 */ isPerThisOrPerTarget()156 public boolean isPerThisOrPerTarget() { 157 PerClauseKind kind = getAjType().getPerClause().getKind(); 158 return (kind == PerClauseKind.PERTARGET || kind == PerClauseKind.PERTHIS); 159 } 160 161 /** 162 * Return whether the aspect is defined as "pertypewithin". 163 */ isPerTypeWithin()164 public boolean isPerTypeWithin() { 165 PerClauseKind kind = getAjType().getPerClause().getKind(); 166 return (kind == PerClauseKind.PERTYPEWITHIN); 167 } 168 169 /** 170 * Return whether the aspect needs to be lazily instantiated. 171 */ isLazilyInstantiated()172 public boolean isLazilyInstantiated() { 173 return (isPerThisOrPerTarget() || isPerTypeWithin()); 174 } 175 176 } 177