001/* 002 * Copyright 2002-2016 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.IOException; 020import java.io.ObjectInputStream; 021import java.io.Serializable; 022 023import org.aspectj.lang.annotation.Aspect; 024import org.aspectj.lang.reflect.AjType; 025import org.aspectj.lang.reflect.AjTypeSystem; 026import org.aspectj.lang.reflect.PerClauseKind; 027 028import org.springframework.aop.Pointcut; 029import org.springframework.aop.aspectj.AspectJExpressionPointcut; 030import org.springframework.aop.aspectj.TypePatternClassFilter; 031import org.springframework.aop.framework.AopConfigException; 032import org.springframework.aop.support.ComposablePointcut; 033 034/** 035 * Metadata for an AspectJ aspect class, with an additional Spring AOP pointcut 036 * for the per clause. 037 * 038 * <p>Uses AspectJ 5 AJType reflection API, enabling us to work with different 039 * AspectJ instantiation models such as "singleton", "pertarget" and "perthis". 040 * 041 * @author Rod Johnson 042 * @author Juergen Hoeller 043 * @since 2.0 044 * @see org.springframework.aop.aspectj.AspectJExpressionPointcut 045 */ 046@SuppressWarnings("serial") 047public class AspectMetadata implements Serializable { 048 049 /** 050 * The name of this aspect as defined to Spring (the bean name) - 051 * allows us to determine if two pieces of advice come from the 052 * same aspect and hence their relative precedence. 053 */ 054 private final String aspectName; 055 056 /** 057 * The aspect class, stored separately for re-resolution of the 058 * corresponding AjType on deserialization. 059 */ 060 private final Class<?> aspectClass; 061 062 /** 063 * AspectJ reflection information (AspectJ 5 / Java 5 specific). 064 * Re-resolved on deserialization since it isn't serializable itself. 065 */ 066 private transient AjType<?> ajType; 067 068 /** 069 * Spring AOP pointcut corresponding to the per clause of the 070 * aspect. Will be the Pointcut.TRUE canonical instance in the 071 * case of a singleton, otherwise an AspectJExpressionPointcut. 072 */ 073 private final Pointcut perClausePointcut; 074 075 076 /** 077 * Create a new AspectMetadata instance for the given aspect class. 078 * @param aspectClass the aspect class 079 * @param aspectName the name of the aspect 080 */ 081 public AspectMetadata(Class<?> aspectClass, String aspectName) { 082 this.aspectName = aspectName; 083 084 Class<?> currClass = aspectClass; 085 AjType<?> ajType = null; 086 while (currClass != Object.class) { 087 AjType<?> ajTypeToCheck = AjTypeSystem.getAjType(currClass); 088 if (ajTypeToCheck.isAspect()) { 089 ajType = ajTypeToCheck; 090 break; 091 } 092 currClass = currClass.getSuperclass(); 093 } 094 if (ajType == null) { 095 throw new IllegalArgumentException("Class '" + aspectClass.getName() + "' is not an @AspectJ aspect"); 096 } 097 if (ajType.getDeclarePrecedence().length > 0) { 098 throw new IllegalArgumentException("DeclarePrecendence not presently supported in Spring AOP"); 099 } 100 this.aspectClass = ajType.getJavaClass(); 101 this.ajType = ajType; 102 103 switch (this.ajType.getPerClause().getKind()) { 104 case SINGLETON: 105 this.perClausePointcut = Pointcut.TRUE; 106 return; 107 case PERTARGET: 108 case PERTHIS: 109 AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut(); 110 ajexp.setLocation(aspectClass.getName()); 111 ajexp.setExpression(findPerClause(aspectClass)); 112 ajexp.setPointcutDeclarationScope(aspectClass); 113 this.perClausePointcut = ajexp; 114 return; 115 case PERTYPEWITHIN: 116 // Works with a type pattern 117 this.perClausePointcut = new ComposablePointcut(new TypePatternClassFilter(findPerClause(aspectClass))); 118 return; 119 default: 120 throw new AopConfigException( 121 "PerClause " + ajType.getPerClause().getKind() + " not supported by Spring AOP for " + aspectClass); 122 } 123 } 124 125 /** 126 * Extract contents from String of form {@code pertarget(contents)}. 127 */ 128 private String findPerClause(Class<?> aspectClass) { 129 String str = aspectClass.getAnnotation(Aspect.class).value(); 130 str = str.substring(str.indexOf('(') + 1); 131 str = str.substring(0, str.length() - 1); 132 return str; 133 } 134 135 136 /** 137 * Return AspectJ reflection information. 138 */ 139 public AjType<?> getAjType() { 140 return this.ajType; 141 } 142 143 /** 144 * Return the aspect class. 145 */ 146 public Class<?> getAspectClass() { 147 return this.aspectClass; 148 } 149 150 /** 151 * Return the aspect name. 152 */ 153 public String getAspectName() { 154 return this.aspectName; 155 } 156 157 /** 158 * Return a Spring pointcut expression for a singleton aspect. 159 * (e.g. {@code Pointcut.TRUE} if it's a singleton). 160 */ 161 public Pointcut getPerClausePointcut() { 162 return this.perClausePointcut; 163 } 164 165 /** 166 * Return whether the aspect is defined as "perthis" or "pertarget". 167 */ 168 public boolean isPerThisOrPerTarget() { 169 PerClauseKind kind = getAjType().getPerClause().getKind(); 170 return (kind == PerClauseKind.PERTARGET || kind == PerClauseKind.PERTHIS); 171 } 172 173 /** 174 * Return whether the aspect is defined as "pertypewithin". 175 */ 176 public boolean isPerTypeWithin() { 177 PerClauseKind kind = getAjType().getPerClause().getKind(); 178 return (kind == PerClauseKind.PERTYPEWITHIN); 179 } 180 181 /** 182 * Return whether the aspect needs to be lazily instantiated. 183 */ 184 public boolean isLazilyInstantiated() { 185 return (isPerThisOrPerTarget() || isPerTypeWithin()); 186 } 187 188 189 private void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException { 190 inputStream.defaultReadObject(); 191 this.ajType = AjTypeSystem.getAjType(this.aspectClass); 192 } 193 194}