[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[List Home]
|
Re: [aspectj-users] Advice on Constructors With Subclasses
|
- From: "James Elliott" <jvelliott@xxxxxxxxx>
- Date: Mon, 8 Sep 2008 12:08:25 -0700
- Delivered-to: aspectj-users@eclipse.org
- Dkim-signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:received:received:message-id:date:from:to :subject:in-reply-to:mime-version:content-type :content-transfer-encoding:content-disposition:references; bh=mvMLNz+Hi3U1eWcqlnouUq0Hc4YZBFb4JvAtjIivgn0=; b=GLeiwOUXhsA10PFVHN35ILRIz1a14IdI/WxUsjpby4V27Hs4jNXiYCcGCV75q7/3H5 593Kio4wXYKkPzLEhuPEwRdpl4WFhmcJ7mzQg/EJ8nrDl2sVwkJqdUjcOMXuIMpk+OqD SitsJi99nVutelemMpLZuQWcj+9BlU2XyEXog=
- Domainkey-signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=message-id:date:from:to:subject:in-reply-to:mime-version :content-type:content-transfer-encoding:content-disposition :references; b=SDaIRK8CyNjUfEOjWGmUDkrFDb3NRoPRpncfQ6YBXWhPlhuP+23RUh+I+3ruWLJUbW 9S1nX3kGv4o8FuP66yH2unDjVRCwCyaA8zpDsgXczZrzZFUleEDCwy7Quo4MdLNtgxAx 32ERD5tcdqCpKnUNcolNpKW9Pw9LYE11EY5mQ=
I played with Simone's code a bit, and it does seem to work as described.
Here's the solution I came up with for when the constructor call is
made within a constructor call.
This solution works assuming that the only places where A or it's
subclasses are instantiated within the constructor of A or it's
subclasses are in code that can be woven, which, for my project, is a
safe assumption.
I made the class abstract so that it can be easily reused whenever
this pattern is appropriate.
I don't like having the getMyClass() method, but I don't know of a
good way to get the generic type without it.
I am also caching the return value from isAssignableFrom, since this
call appears to be relatively expensive in some versions of java.
It's still a bit of a hack, but it seems to fulfill my needs.
public abstract aspect InitializationAspect<T> {
/* Cache to avoid reflective calls */
private Map<String, Boolean> isAssignableMap = new HashMap<String, Boolean>();
/**
* Returns the class highest in the hierarchy. Should be T.
*/
protected abstract Class<T> getMyClass();
/**
* The Advice to perform after an object has been instantiated.
* @param t The object that was created.
*/
protected abstract void myAdvice(T t);
/**
* Checks to make sure that the constructor that is executing
* is the type of the object to be created (i.e. lowest on the stack)
* and calls myAdvice() if it is. Does nothing if not.
* @param t The object that was created
*/
after(T t) : execution(T+.new(..)) && this(t) {
if (isLastConstructorInChain(getMyClass())) {
myAdvice(t);
}
}
/**
* Captures any calls to create a new object of type T or it's subtypes
* that happen within type T or it's subtypes, and calls myAdvice().
*/
after() returning(T t): call(T+.new()) && withincode(T+.new(..)) {
myAdvice(t);
}
/**
* Returns true if a class' constructor the lowest method call in a
* chain of constructor calls. If it isn't, return true if the next lowest
* call is not type T or a subclass of type T, false otherwise.
* @param clazz The class to look for.
*/
public boolean isLastConstructorInChain(Class<?> clazz) {
StackTraceElement[] stackTrace =
Thread.currentThread().getStackTrace();
// find the first constructor, cause there are a few AspectJ
internal calls before it
int acpos = 0;
while (acpos < stackTrace.length &&
!stackTrace[acpos].getMethodName().equals("<init>")) {
acpos++;
}
// Check if we run out, should never happen
if (acpos >= stackTrace.length) {
return false;
}
// Now we have the last call to <init>
// if the following one is not a constructor (or is not present),
// we can skip all the checks and return true
if (acpos + 1 == stackTrace.length ||
!stackTrace[acpos + 1].getMethodName().equals("<init>")) {
return true;
}
// Otherwise we have to check it the next constructor in the
// chain is a proper subclass of the current class
String callingClass = stackTrace[acpos + 1].getClassName();
Boolean isAssignable = isAssignableMap.get(callingClass);
if(isAssignable == null) {
Class<?> forname = null;
try {
forname = Class.forName(callingClass);
}
catch (ClassNotFoundException e) {
e.printStackTrace();
isAssignable = false;
}
isAssignable = !clazz.isAssignableFrom(forname);
isAssignableMap.put(callingClass, isAssignable);
}
return isAssignable;
}
}
public aspect AAspect extends InitializationAspect<A> {
protected Class<A> getMyClass() {
return A.class;
}
protected void myAdvice(A a) {
System.out.println("Done:" + a.getClass());
}
}