Moved over initial version of aop bundle
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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;
|
||||
|
||||
import org.aopalliance.aop.Advice;
|
||||
|
||||
/**
|
||||
* Base interface holding AOP <b>advice</b> (action to take at a joinpoint)
|
||||
* and a filter determining the applicability of the advice (such as
|
||||
* a pointcut). <i>This interface is not for use by Spring users, but to
|
||||
* allow for commonality in support for different types of advice.</i>
|
||||
*
|
||||
* <p>Spring AOP is based around <b>around advice</b> delivered via method
|
||||
* <b>interception</b>, compliant with the AOP Alliance interception API.
|
||||
* The Advisor interface allows support for different types of advice,
|
||||
* such as <b>before</b> and <b>after</b> advice, which need not be
|
||||
* implemented using interception.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
*/
|
||||
public interface Advisor {
|
||||
|
||||
/**
|
||||
* Return the advice part of this aspect. An advice may be an
|
||||
* interceptor, a before advice, a throws advice, etc.
|
||||
* @return the advice that should apply if the pointcut matches
|
||||
* @see org.aopalliance.intercept.MethodInterceptor
|
||||
* @see BeforeAdvice
|
||||
* @see ThrowsAdvice
|
||||
* @see AfterReturningAdvice
|
||||
*/
|
||||
Advice getAdvice();
|
||||
|
||||
/**
|
||||
* Return whether this advice is associated with a particular instance
|
||||
* (for example, creating a mixin) or shared with all instances of
|
||||
* the advised class obtained from the same Spring bean factory.
|
||||
* <p><b>Note that this method is not currently used by the framework.</b>
|
||||
* Typical Advisor implementations always return <code>true</code>.
|
||||
* Use singleton/prototype bean definitions or appropriate programmatic
|
||||
* proxy creation to ensure that Advisors have the correct lifecycle model.
|
||||
* @return whether this advice is associated with a particular target instance
|
||||
*/
|
||||
boolean isPerInstance();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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;
|
||||
|
||||
import org.aopalliance.aop.Advice;
|
||||
|
||||
/**
|
||||
* Common marker interface for after advice,
|
||||
* such as {@link AfterReturningAdvice} and {@link ThrowsAdvice}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0.3
|
||||
* @see BeforeAdvice
|
||||
*/
|
||||
public interface AfterAdvice extends Advice {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* After returning advice is invoked only on normal method return, not if an
|
||||
* exception is thrown. Such advice can see the return value, but cannot change it.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @see MethodBeforeAdvice
|
||||
* @see ThrowsAdvice
|
||||
*/
|
||||
public interface AfterReturningAdvice extends AfterAdvice {
|
||||
|
||||
/**
|
||||
* Callback after a given method successfully returned.
|
||||
* @param returnValue the value returned by the method, if any
|
||||
* @param method method being invoked
|
||||
* @param args arguments to the method
|
||||
* @param target target of the method invocation. May be <code>null</code>.
|
||||
* @throws Throwable if this object wishes to abort the call.
|
||||
* Any exception thrown will be returned to the caller if it's
|
||||
* allowed by the method signature. Otherwise the exception
|
||||
* will be wrapped as a runtime exception.
|
||||
*/
|
||||
void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2002-2006 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;
|
||||
|
||||
import org.springframework.core.NestedRuntimeException;
|
||||
|
||||
/**
|
||||
* Exception that gets thrown when an AOP invocation failed
|
||||
* because of misconfiguration or unexpected runtime issues.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
public class AopInvocationException extends NestedRuntimeException {
|
||||
|
||||
/**
|
||||
* Constructor for AopInvocationException.
|
||||
* @param msg the detail message
|
||||
*/
|
||||
public AopInvocationException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for AopInvocationException.
|
||||
* @param msg the detail message
|
||||
* @param cause the root cause
|
||||
*/
|
||||
public AopInvocationException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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;
|
||||
|
||||
import org.aopalliance.aop.Advice;
|
||||
|
||||
/**
|
||||
* Common marker interface for before advice, such as {@link MethodBeforeAdvice}.
|
||||
*
|
||||
* <p>Spring supports only method before advice. Although this is unlikely to change,
|
||||
* this API is designed to allow field advice in future if desired.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @see AfterAdvice
|
||||
*/
|
||||
public interface BeforeAdvice extends Advice {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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;
|
||||
|
||||
/**
|
||||
* Filter that restricts matching of a pointcut or introduction to
|
||||
* a given set of target classes.
|
||||
*
|
||||
* <p>Can be used as part of a {@link Pointcut} or for the entire
|
||||
* targeting of an {@link IntroductionAdvisor}.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @see Pointcut
|
||||
* @see MethodMatcher
|
||||
*/
|
||||
public interface ClassFilter {
|
||||
|
||||
/**
|
||||
* Should the pointcut apply to the given interface or target class?
|
||||
* @param clazz the candidate target class
|
||||
* @return whether the advice should apply to the given target class
|
||||
*/
|
||||
boolean matches(Class clazz);
|
||||
|
||||
|
||||
/**
|
||||
* Canonical instance of a ClassFilter that matches all classes.
|
||||
*/
|
||||
ClassFilter TRUE = TrueClassFilter.INSTANCE;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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;
|
||||
|
||||
import org.aopalliance.aop.Advice;
|
||||
|
||||
/**
|
||||
* Subinterface of AOP Alliance Advice that allows additional interfaces
|
||||
* to be implemented by an Advice, and available via a proxy using that
|
||||
* interceptor. This is a fundamental AOP concept called <b>introduction</b>.
|
||||
*
|
||||
* <p>Introductions are often <b>mixins</b>, enabling the building of composite
|
||||
* objects that can achieve many of the goals of multiple inheritance in Java.
|
||||
*
|
||||
* <p>Compared to {qlink IntroductionInfo}, this interface allows an advice to
|
||||
* implement a range of interfaces that is not necessarily known in advance.
|
||||
* Thus an {@link IntroductionAdvisor} can be used to specify which interfaces
|
||||
* will be exposed in an advised object.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @since 1.1.1
|
||||
* @see IntroductionInfo
|
||||
* @see IntroductionAdvisor
|
||||
*/
|
||||
public interface DynamicIntroductionAdvice extends Advice {
|
||||
|
||||
/**
|
||||
* Does this introduction advice implement the given interface?
|
||||
* @param intf the interface to check
|
||||
* @return whether the advice implements the specified interface
|
||||
*/
|
||||
boolean implementsInterface(Class intf);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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;
|
||||
|
||||
/**
|
||||
* Superinterface for advisors that perform one or more AOP <b>introductions</b>.
|
||||
*
|
||||
* <p>This interface cannot be implemented directly; subinterfaces must
|
||||
* provide the advice type implementing the introduction.
|
||||
*
|
||||
* <p>Introduction is the implementation of additional interfaces
|
||||
* (not implemented by a target) via AOP advice.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @since 04.04.2003
|
||||
* @see IntroductionInterceptor
|
||||
*/
|
||||
public interface IntroductionAdvisor extends Advisor, IntroductionInfo {
|
||||
|
||||
/**
|
||||
* Return the filter determining which target classes this introduction
|
||||
* should apply to.
|
||||
* <p>This represents the class part of a pointcut. Note that method
|
||||
* matching doesn't make sense to introductions.
|
||||
* @return the class filter
|
||||
*/
|
||||
ClassFilter getClassFilter();
|
||||
|
||||
/**
|
||||
* Can the advised interfaces be implemented by the introduction advice?
|
||||
* Invoked before adding an IntroductionAdvisor.
|
||||
* @throws IllegalArgumentException if the advised interfaces can't be
|
||||
* implemented by the introduction advice
|
||||
*/
|
||||
void validateInterfaces() throws IllegalArgumentException;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* A specialized type of MethodMatcher that takes into account introductions when
|
||||
* matching methods. If there are no introductions on the target class, a method
|
||||
* matcher may be able to optimize matching more effectively for example.
|
||||
*
|
||||
* @author Adrian Colyer
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface IntroductionAwareMethodMatcher extends MethodMatcher {
|
||||
|
||||
/**
|
||||
* Perform static checking whether the given method matches. This may be invoked
|
||||
* instead of the 2-arg {@link #matches(java.lang.reflect.Method, Class)} method
|
||||
* if the caller supports the extended IntroductionAwareMethodMatcher interface.
|
||||
* @param method the candidate method
|
||||
* @param targetClass the target class (may be <code>null</code>, in which case
|
||||
* the candidate class must be taken to be the method's declaring class)
|
||||
* @param hasIntroductions <code>true</code> if the object on whose behalf we are
|
||||
* asking is the subject on one or more introductions; <code>false</code> otherwise
|
||||
* @return whether or not this method matches statically
|
||||
*/
|
||||
boolean matches(Method method, Class targetClass, boolean hasIntroductions);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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;
|
||||
|
||||
/**
|
||||
* Interface supplying the information necessary to describe an introduction.
|
||||
*
|
||||
* <p>{@link IntroductionAdvisor IntroductionAdvisors} must implement this
|
||||
* interface. If an {@link org.aopalliance.aop.Advice} implements this,
|
||||
* it may be used as an introduction without an {@link IntroductionAdvisor}.
|
||||
* In this case, the advice is self-describing, providing not only the
|
||||
* necessary behavior, but describing the interfaces it introduces.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @since 1.1.1
|
||||
*/
|
||||
public interface IntroductionInfo {
|
||||
|
||||
/**
|
||||
* Return the additional interfaces introduced by this Advisor or Advice.
|
||||
* @return the introduced interfaces
|
||||
*/
|
||||
Class[] getInterfaces();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
|
||||
/**
|
||||
* Subinterface of AOP Alliance MethodInterceptor that allows additional interfaces
|
||||
* to be implemented by the interceptor, and available via a proxy using that
|
||||
* interceptor. This is a fundamental AOP concept called <b>introduction</b>.
|
||||
*
|
||||
* <p>Introductions are often <b>mixins</b>, enabling the building of composite
|
||||
* objects that can achieve many of the goals of multiple inheritance in Java.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @see DynamicIntroductionAdvice
|
||||
*/
|
||||
public interface IntroductionInterceptor extends MethodInterceptor, DynamicIntroductionAdvice {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2002-2005 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;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Advice invoked before a method is invoked. Such advices cannot
|
||||
* prevent the method call proceeding, unless they throw a Throwable.
|
||||
*
|
||||
* @see AfterReturningAdvice
|
||||
* @see ThrowsAdvice
|
||||
*
|
||||
* @author Rod Johnson
|
||||
*/
|
||||
public interface MethodBeforeAdvice extends BeforeAdvice {
|
||||
|
||||
/**
|
||||
* Callback before a given method is invoked.
|
||||
* @param method method being invoked
|
||||
* @param args arguments to the method
|
||||
* @param target target of the method invocation. May be <code>null</code>.
|
||||
* @throws Throwable if this object wishes to abort the call.
|
||||
* Any exception thrown will be returned to the caller if it's
|
||||
* allowed by the method signature. Otherwise the exception
|
||||
* will be wrapped as a runtime exception.
|
||||
*/
|
||||
void before(Method method, Object[] args, Object target) throws Throwable;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Part of a {@link Pointcut}: Checks whether the target method is eligible for advice.
|
||||
*
|
||||
* <p>A MethodMatcher may be evaluated <b>statically</b> or at <b>runtime</b> (dynamically).
|
||||
* Static matching involves method and (possibly) method attributes. Dynamic matching
|
||||
* also makes arguments for a particular call available, and any effects of running
|
||||
* previous advice applying to the joinpoint.
|
||||
*
|
||||
* <p>If an implementation returns <code>false</code> from its {@link #isRuntime()}
|
||||
* method, evaluation can be performed statically, and the result will be the same
|
||||
* for all invocations of this method, whatever their arguments. This means that
|
||||
* if the {@link #isRuntime()} method returns <code>false</code>, the 3-arg
|
||||
* {@link #matches(java.lang.reflect.Method, Class, Object[])} method will never be invoked.
|
||||
*
|
||||
* <p>If an implementation returns <code>true</code> from its 2-arg
|
||||
* {@link #matches(java.lang.reflect.Method, Class)} method and its {@link #isRuntime()} method
|
||||
* returns <code>true</code>, the 3-arg {@link #matches(java.lang.reflect.Method, Class, Object[])}
|
||||
* method will be invoked <i>immediately before each potential execution of the related advice</i>,
|
||||
* to decide whether the advice should run. All previous advice, such as earlier interceptors
|
||||
* in an interceptor chain, will have run, so any state changes they have produced in
|
||||
* parameters or ThreadLocal state will be available at the time of evaluation.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @since 11.11.2003
|
||||
* @see Pointcut
|
||||
* @see ClassFilter
|
||||
*/
|
||||
public interface MethodMatcher {
|
||||
|
||||
/**
|
||||
* Perform static checking whether the given method matches. If this
|
||||
* returns <code>false</code> or if the {@link #isRuntime()} method
|
||||
* returns <code>false</code>, no runtime check (i.e. no.
|
||||
* {@link #matches(java.lang.reflect.Method, Class, Object[])} call) will be made.
|
||||
* @param method the candidate method
|
||||
* @param targetClass the target class (may be <code>null</code>, in which case
|
||||
* the candidate class must be taken to be the method's declaring class)
|
||||
* @return whether or not this method matches statically
|
||||
*/
|
||||
boolean matches(Method method, Class targetClass);
|
||||
|
||||
/**
|
||||
* Is this MethodMatcher dynamic, that is, must a final call be made on the
|
||||
* {@link #matches(java.lang.reflect.Method, Class, Object[])} method at
|
||||
* runtime even if the 2-arg matches method returns <code>true</code>?
|
||||
* <p>Can be invoked when an AOP proxy is created, and need not be invoked
|
||||
* again before each method invocation,
|
||||
* @return whether or not a runtime match via the 3-arg
|
||||
* {@link #matches(java.lang.reflect.Method, Class, Object[])} method
|
||||
* is required if static matching passed
|
||||
*/
|
||||
boolean isRuntime();
|
||||
|
||||
/**
|
||||
* Check whether there a runtime (dynamic) match for this method,
|
||||
* which must have matched statically.
|
||||
* <p>This method is invoked only if the 2-arg matches method returns
|
||||
* <code>true</code> for the given method and target class, and if the
|
||||
* {@link #isRuntime()} method returns <code>true</code>. Invoked
|
||||
* immediately before potential running of the advice, after any
|
||||
* advice earlier in the advice chain has run.
|
||||
* @param method the candidate method
|
||||
* @param targetClass the target class (may be <code>null</code>, in which case
|
||||
* the candidate class must be taken to be the method's declaring class)
|
||||
* @param args arguments to the method
|
||||
* @return whether there's a runtime match
|
||||
* @see MethodMatcher#matches(Method, Class)
|
||||
*/
|
||||
boolean matches(Method method, Class targetClass, Object[] args);
|
||||
|
||||
|
||||
/**
|
||||
* Canonical instance that matches all methods.
|
||||
*/
|
||||
MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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;
|
||||
|
||||
/**
|
||||
* Core Spring pointcut abstraction.
|
||||
*
|
||||
* <p>A pointcut is composed of a {@link ClassFilter} and a {@link MethodMatcher}.
|
||||
* Both these basic terms and a Pointcut itself can be combined to build up combinations
|
||||
* (e.g. through {@link org.springframework.aop.support.ComposablePointcut}).
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @see ClassFilter
|
||||
* @see MethodMatcher
|
||||
* @see org.springframework.aop.support.Pointcuts
|
||||
* @see org.springframework.aop.support.ClassFilters
|
||||
* @see org.springframework.aop.support.MethodMatchers
|
||||
*/
|
||||
public interface Pointcut {
|
||||
|
||||
/**
|
||||
* Return the ClassFilter for this pointcut.
|
||||
* @return the ClassFilter (never <code>null</code>)
|
||||
*/
|
||||
ClassFilter getClassFilter();
|
||||
|
||||
/**
|
||||
* Return the MethodMatcher for this pointcut.
|
||||
* @return the MethodMatcher (never <code>null</code>)
|
||||
*/
|
||||
MethodMatcher getMethodMatcher();
|
||||
|
||||
|
||||
/**
|
||||
* Canonical Pointcut instance that always matches.
|
||||
*/
|
||||
Pointcut TRUE = TruePointcut.INSTANCE;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2002-2005 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;
|
||||
|
||||
/**
|
||||
* Superinterface for all Advisors that are driven by a pointcut.
|
||||
* This covers nearly all advisors except introduction advisors,
|
||||
* for which method-level matching doesn't apply.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
*/
|
||||
public interface PointcutAdvisor extends Advisor {
|
||||
|
||||
/**
|
||||
* Get the Pointcut that drives this advisor.
|
||||
*/
|
||||
Pointcut getPointcut();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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;
|
||||
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
/**
|
||||
* Extension of the AOP Alliance {@link org.aopalliance.intercept.MethodInvocation}
|
||||
* interface, allowing access to the proxy that the method invocation was made through.
|
||||
*
|
||||
* <p>Useful to be able to substitute return values with the proxy,
|
||||
* if necessary, for example if the invocation target returned itself.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Adrian Colyer
|
||||
* @since 1.1.3
|
||||
* @see org.springframework.aop.framework.ReflectiveMethodInvocation
|
||||
* @see org.springframework.aop.support.DelegatingIntroductionInterceptor
|
||||
*/
|
||||
public interface ProxyMethodInvocation extends MethodInvocation {
|
||||
|
||||
/**
|
||||
* Return the proxy that this method invocation was made through.
|
||||
* @return the original proxy object
|
||||
*/
|
||||
Object getProxy();
|
||||
|
||||
/**
|
||||
* Create a clone of this object. If cloning is done before <code>proceed()</code>
|
||||
* is invoked on this object, <code>proceed()</code> can be invoked once per clone
|
||||
* to invoke the joinpoint (and the rest of the advice chain) more than once.
|
||||
* @return an invocable clone of this invocation.
|
||||
* <code>proceed()</code> can be called once per clone.
|
||||
*/
|
||||
MethodInvocation invocableClone();
|
||||
|
||||
/**
|
||||
* Create a clone of this object. If cloning is done before <code>proceed()</code>
|
||||
* is invoked on this object, <code>proceed()</code> can be invoked once per clone
|
||||
* to invoke the joinpoint (and the rest of the advice chain) more than once.
|
||||
* @param arguments the arguments that the cloned invocation is supposed to use,
|
||||
* overriding the original arguments
|
||||
* @return an invocable clone of this invocation.
|
||||
* <code>proceed()</code> can be called once per clone.
|
||||
*/
|
||||
MethodInvocation invocableClone(Object[] arguments);
|
||||
|
||||
/**
|
||||
* Set the arguments to be used on subsequent invocations in the any advice
|
||||
* in this chain.
|
||||
* @param arguments the argument array
|
||||
*/
|
||||
void setArguments(Object[] arguments);
|
||||
|
||||
/**
|
||||
* Add the specified user attribute with the given value to this invocation.
|
||||
* <p>Such attributes are not used within the AOP framework itself. They are
|
||||
* just kept as part of the invocation object, for use in special interceptors.
|
||||
* @param key the name of the attribute
|
||||
* @param value the value of the attribute, or <code>null</code> to reset it
|
||||
*/
|
||||
void setUserAttribute(String key, Object value);
|
||||
|
||||
/**
|
||||
* Return the value of the specified user attribute.
|
||||
* @param key the name of the attribute
|
||||
* @return the value of the attribute, or <code>null</code> if not set
|
||||
* @see #setUserAttribute
|
||||
*/
|
||||
Object getUserAttribute(String key);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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;
|
||||
|
||||
/**
|
||||
* Marker for AOP proxy interfaces (in particular: introduction interfaces)
|
||||
* that explicitly intend to return the raw target object (which would normally
|
||||
* get replaced with the proxy object when returned from a method invocation).
|
||||
*
|
||||
* <p>Note that this is a marker interface in the style of {@link java.io.Serializable},
|
||||
* semantically applying to a declared interface rather than to the full class
|
||||
* of a concrete object. In other words, this marker applies to a particular
|
||||
* interface only (typically an introduction interface that does not serve
|
||||
* as the primary interface of an AOP proxy), and hence does not affect
|
||||
* other interfaces that a concrete AOP proxy may implement.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0.5
|
||||
* @see org.springframework.aop.scope.ScopedObject
|
||||
*/
|
||||
public interface RawTargetAccess {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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;
|
||||
|
||||
/**
|
||||
* Marker interface implemented by all AOP proxies. Used to detect
|
||||
* whether or not objects are Spring-generated proxies.
|
||||
*
|
||||
* @author Rob Harrop
|
||||
* @since 2.0.1
|
||||
* @see org.springframework.aop.support.AopUtils#isAopProxy(Object)
|
||||
*/
|
||||
public interface SpringProxy {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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;
|
||||
|
||||
/**
|
||||
* Minimal interface for exposing the target class behind a proxy.
|
||||
*
|
||||
* <p>Implemented by AOP proxy objects and proxy factories
|
||||
* (via {@link org.springframework.aop.framework.Advised}}
|
||||
* as well as by {@link TargetSource TargetSources}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0.3
|
||||
* @see org.springframework.aop.support.AopUtils#getTargetClass(Object)
|
||||
*/
|
||||
public interface TargetClassAware {
|
||||
|
||||
/**
|
||||
* Return the target class behind the implementing object
|
||||
* (typically a proxy configuration or an actual proxy).
|
||||
* @return the target Class, or <code>null</code> if not known
|
||||
*/
|
||||
Class getTargetClass();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*<
|
||||
* Copyright 2002-2007 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;
|
||||
|
||||
/**
|
||||
* A <code>TargetSource</code> is used to obtain the current "target" of
|
||||
* an AOP invocation, which will be invoked via reflection if no around
|
||||
* advice chooses to end the interceptor chain itself.
|
||||
*
|
||||
* <p>If a <code>TargetSource</code> is "static", it will always return
|
||||
* the same target, allowing optimizations in the AOP framework. Dynamic
|
||||
* target sources can support pooling, hot swapping, etc.
|
||||
*
|
||||
* <p>Application developers don't usually need to work with
|
||||
* <code>TargetSources</code> directly: this is an AOP framework interface.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
*/
|
||||
public interface TargetSource extends TargetClassAware {
|
||||
|
||||
/**
|
||||
* Return the type of targets returned by this {@link TargetSource}.
|
||||
* <p>Can return <code>null</code>, although certain usages of a
|
||||
* <code>TargetSource</code> might just work with a predetermined
|
||||
* target class.
|
||||
* @return the type of targets returned by this {@link TargetSource}
|
||||
*/
|
||||
Class getTargetClass();
|
||||
|
||||
/**
|
||||
* Will all calls to {@link #getTarget()} return the same object?
|
||||
* <p>In that case, there will be no need to invoke
|
||||
* {@link #releaseTarget(Object)}, and the AOP framework can cache
|
||||
* the return value of {@link #getTarget()}.
|
||||
* @return <code>true</code> if the target is immutable
|
||||
* @see #getTarget
|
||||
*/
|
||||
boolean isStatic();
|
||||
|
||||
/**
|
||||
* Return a target instance. Invoked immediately before the
|
||||
* AOP framework calls the "target" of an AOP method invocation.
|
||||
* @return the target object, which contains the joinpoint
|
||||
* @throws Exception if the target object can't be resolved
|
||||
*/
|
||||
Object getTarget() throws Exception;
|
||||
|
||||
/**
|
||||
* Release the given target object obtained from the
|
||||
* {@link #getTarget()} method.
|
||||
* @param target object obtained from a call to {@link #getTarget()}
|
||||
* @throws Exception if the object can't be released
|
||||
*/
|
||||
void releaseTarget(Object target) throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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;
|
||||
|
||||
/**
|
||||
* Tag interface for throws advice.
|
||||
*
|
||||
* <p>There are not any methods on this interface, as methods are invoked by
|
||||
* reflection. Implementing classes must implement methods of the form:
|
||||
*
|
||||
* <pre class="code">void afterThrowing([Method, args, target], ThrowableSubclass);</pre>
|
||||
*
|
||||
* <p>Some examples of valid methods would be:
|
||||
*
|
||||
* <pre class="code">public void afterThrowing(Exception ex)</pre>
|
||||
* <pre class="code">public void afterThrowing(RemoteException)</pre>
|
||||
* <pre class="code">public void afterThrowing(Method method, Object[] args, Object target, Exception ex)</pre>
|
||||
* <pre class="code">public void afterThrowing(Method method, Object[] args, Object target, ServletException ex)</pre>
|
||||
*
|
||||
* The first three arguments are optional, and only useful if we want further
|
||||
* information about the joinpoint, as in AspectJ <b>after-throwing</b> advice.
|
||||
*
|
||||
* <p><b>Note:</b> If a throws-advice method throws an exception itself, it will
|
||||
* override the original exception (i.e. change the exception thrown to the user).
|
||||
* The overriding exception will typically be a RuntimeException; this is compatible
|
||||
* with any method signature. However, if a throws-advice method throws a checked
|
||||
* exception, it will have to match the declared exceptions of the target method
|
||||
* and is hence to some degree coupled to specific target method signatures.
|
||||
* <b>Do not throw an undeclared checked exception that is incompatible with
|
||||
* the target method's signature!</b>
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @see AfterReturningAdvice
|
||||
* @see MethodBeforeAdvice
|
||||
*/
|
||||
public interface ThrowsAdvice extends AfterAdvice {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2002-2006 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;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Canonical ClassFilter instance that matches all classes.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
*/
|
||||
class TrueClassFilter implements ClassFilter, Serializable {
|
||||
|
||||
public static final TrueClassFilter INSTANCE = new TrueClassFilter();
|
||||
|
||||
/**
|
||||
* Enforce Singleton pattern.
|
||||
*/
|
||||
private TrueClassFilter() {
|
||||
}
|
||||
|
||||
public boolean matches(Class clazz) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Required to support serialization. Replaces with canonical
|
||||
* instance on deserialization, protecting Singleton pattern.
|
||||
* Alternative to overriding <code>equals()</code>.
|
||||
*/
|
||||
private Object readResolve() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "ClassFilter.TRUE";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2002-2006 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;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Canonical MethodMatcher instance that matches all methods.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
*/
|
||||
class TrueMethodMatcher implements MethodMatcher, Serializable {
|
||||
|
||||
public static final TrueMethodMatcher INSTANCE = new TrueMethodMatcher();
|
||||
|
||||
/**
|
||||
* Enforce Singleton pattern.
|
||||
*/
|
||||
private TrueMethodMatcher() {
|
||||
}
|
||||
|
||||
public boolean isRuntime() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean matches(Method method, Class targetClass) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean matches(Method method, Class targetClass, Object[] args) {
|
||||
// Should never be invoked as isRuntime returns false.
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Required to support serialization. Replaces with canonical
|
||||
* instance on deserialization, protecting Singleton pattern.
|
||||
* Alternative to overriding <code>equals()</code>.
|
||||
*/
|
||||
private Object readResolve() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "MethodMatcher.TRUE";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2002-2006 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;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Canonical Pointcut instance that always matches.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
*/
|
||||
class TruePointcut implements Pointcut, Serializable {
|
||||
|
||||
public static final TruePointcut INSTANCE = new TruePointcut();
|
||||
|
||||
/**
|
||||
* Enforce Singleton pattern.
|
||||
*/
|
||||
private TruePointcut() {
|
||||
}
|
||||
|
||||
public ClassFilter getClassFilter() {
|
||||
return ClassFilter.TRUE;
|
||||
}
|
||||
|
||||
public MethodMatcher getMethodMatcher() {
|
||||
return MethodMatcher.TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Required to support serialization. Replaces with canonical
|
||||
* instance on deserialization, protecting Singleton pattern.
|
||||
* Alternative to overriding <code>equals()</code>.
|
||||
*/
|
||||
private Object readResolve() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Pointcut.TRUE";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,691 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.aopalliance.aop.Advice;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.weaver.tools.JoinPointMatch;
|
||||
import org.aspectj.weaver.tools.PointcutParameter;
|
||||
|
||||
import org.springframework.aop.AopInvocationException;
|
||||
import org.springframework.aop.MethodMatcher;
|
||||
import org.springframework.aop.Pointcut;
|
||||
import org.springframework.aop.ProxyMethodInvocation;
|
||||
import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
|
||||
import org.springframework.aop.support.ComposablePointcut;
|
||||
import org.springframework.aop.support.MethodMatchers;
|
||||
import org.springframework.aop.support.StaticMethodMatcher;
|
||||
import org.springframework.core.JdkVersion;
|
||||
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.core.PrioritizedParameterNameDiscoverer;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Base class for AOP Alliance {@link org.aopalliance.aop.Advice} classes
|
||||
* wrapping an AspectJ aspect or an AspectJ-annotated advice method.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Adrian Colyer
|
||||
* @author Juergen Hoeller
|
||||
* @author Ramnivas Laddad
|
||||
* @since 2.0
|
||||
*/
|
||||
public abstract class AbstractAspectJAdvice implements Advice, AspectJPrecedenceInformation {
|
||||
|
||||
/**
|
||||
* Key used in ReflectiveMethodInvocation userAtributes map for the current joinpoint.
|
||||
*/
|
||||
protected static final String JOIN_POINT_KEY = JoinPoint.class.getName();
|
||||
|
||||
|
||||
/**
|
||||
* Lazily instantiate joinpoint for the current invocation.
|
||||
* Requires MethodInvocation to be bound with ExposeInvocationInterceptor.
|
||||
* <p>Do not use if access is available to the current ReflectiveMethodInvocation
|
||||
* (in an around advice).
|
||||
* @return current AspectJ joinpoint, or through an exception if we're not in a
|
||||
* Spring AOP invocation.
|
||||
*/
|
||||
public static JoinPoint currentJoinPoint() {
|
||||
MethodInvocation mi = ExposeInvocationInterceptor.currentInvocation();
|
||||
if (!(mi instanceof ProxyMethodInvocation)) {
|
||||
throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
|
||||
}
|
||||
ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
|
||||
JoinPoint jp = (JoinPoint) pmi.getUserAttribute(JOIN_POINT_KEY);
|
||||
if (jp == null) {
|
||||
jp = new MethodInvocationProceedingJoinPoint(pmi);
|
||||
pmi.setUserAttribute(JOIN_POINT_KEY, jp);
|
||||
}
|
||||
return jp;
|
||||
}
|
||||
|
||||
|
||||
protected final Method aspectJAdviceMethod;
|
||||
|
||||
/** The total number of arguments we have to populate on advice dispatch */
|
||||
private final int adviceInvocationArgumentCount;
|
||||
|
||||
private final AspectJExpressionPointcut pointcut;
|
||||
|
||||
private final AspectInstanceFactory aspectInstanceFactory;
|
||||
|
||||
/**
|
||||
* The name of the aspect (ref bean) in which this advice was defined (used
|
||||
* when determining advice precedence so that we can determine
|
||||
* whether two pieces of advice come from the same aspect).
|
||||
*/
|
||||
private String aspectName;
|
||||
|
||||
/**
|
||||
* The order of declaration of this advice within the aspect.
|
||||
*/
|
||||
private int declarationOrder;
|
||||
|
||||
/**
|
||||
* This will be non-null if the creator of this advice object knows the argument names
|
||||
* and sets them explicitly
|
||||
*/
|
||||
private String[] argumentNames = null;
|
||||
|
||||
/** Non-null if after throwing advice binds the thrown value */
|
||||
private String throwingName = null;
|
||||
|
||||
/** Non-null if after returning advice binds the return value */
|
||||
private String returningName = null;
|
||||
|
||||
private Class discoveredReturningType = Object.class;
|
||||
|
||||
private Class discoveredThrowingType = Object.class;
|
||||
|
||||
/**
|
||||
* Index for thisJoinPoint argument (currently only
|
||||
* supported at index 0 if present at all)
|
||||
*/
|
||||
private int joinPointArgumentIndex = -1;
|
||||
|
||||
/**
|
||||
* Index for thisJoinPointStaticPart argument (currently only
|
||||
* supported at index 0 if present at all)
|
||||
*/
|
||||
private int joinPointStaticPartArgumentIndex = -1;
|
||||
|
||||
private Map argumentBindings = null;
|
||||
|
||||
private boolean argumentsIntrospected = false;
|
||||
|
||||
// The actual type is java.lang.reflect.Type,
|
||||
// but for JDK 1.4 compatibility we use Object as the static type.
|
||||
private Object discoveredReturningGenericType;
|
||||
// Note: Unlike return type, no such generic information is needed for the throwing type,
|
||||
// since Java doesn't allow exception types to be parameterized.
|
||||
|
||||
|
||||
/**
|
||||
* Create a new AbstractAspectJAdvice for the given advice method.
|
||||
* @param aspectJAdviceMethod the AspectJ-style advice method
|
||||
* @param pointcut the AspectJ expression pointcut
|
||||
* @param aspectInstanceFactory the factory for aspect instances
|
||||
*/
|
||||
public AbstractAspectJAdvice(
|
||||
Method aspectJAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aspectInstanceFactory) {
|
||||
|
||||
Assert.notNull(aspectJAdviceMethod, "Advice method must not be null");
|
||||
this.aspectJAdviceMethod = aspectJAdviceMethod;
|
||||
this.adviceInvocationArgumentCount = this.aspectJAdviceMethod.getParameterTypes().length;
|
||||
this.pointcut = pointcut;
|
||||
this.aspectInstanceFactory = aspectInstanceFactory;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the AspectJ-style advice method.
|
||||
*/
|
||||
public final Method getAspectJAdviceMethod() {
|
||||
return this.aspectJAdviceMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the AspectJ expression pointcut.
|
||||
*/
|
||||
public final AspectJExpressionPointcut getPointcut() {
|
||||
calculateArgumentBindings();
|
||||
return this.pointcut;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a 'safe' pointcut that excludes the AspectJ advice method itself.
|
||||
* @return a composable pointcut that builds on the original AspectJ expression pointcut
|
||||
* @see #getPointcut()
|
||||
*/
|
||||
public final Pointcut buildSafePointcut() {
|
||||
Pointcut pc = getPointcut();
|
||||
MethodMatcher safeMethodMatcher = MethodMatchers.intersection(
|
||||
new AdviceExcludingMethodMatcher(this.aspectJAdviceMethod), pc.getMethodMatcher());
|
||||
return new ComposablePointcut(pc.getClassFilter(), safeMethodMatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the factory for aspect instances.
|
||||
*/
|
||||
public final AspectInstanceFactory getAspectInstanceFactory() {
|
||||
return this.aspectInstanceFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ClassLoader for aspect instances.
|
||||
*/
|
||||
public final ClassLoader getAspectClassLoader() {
|
||||
return this.aspectInstanceFactory.getAspectClassLoader();
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return this.aspectInstanceFactory.getOrder();
|
||||
}
|
||||
|
||||
|
||||
public void setAspectName(String name) {
|
||||
this.aspectName = name;
|
||||
}
|
||||
|
||||
public String getAspectName() {
|
||||
return this.aspectName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the <b>declaration order</b> of this advice within the aspect
|
||||
*/
|
||||
public void setDeclarationOrder(int order) {
|
||||
this.declarationOrder = order;
|
||||
}
|
||||
|
||||
public int getDeclarationOrder() {
|
||||
return this.declarationOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set by creator of this advice object if the argument names are known.
|
||||
* <p>This could be for example because they have been explicitly specified in XML,
|
||||
* or in an advice annotation.
|
||||
* @param argNames comma delimited list of arg names
|
||||
*/
|
||||
public void setArgumentNames(String argNames) {
|
||||
String[] tokens = StringUtils.commaDelimitedListToStringArray(argNames);
|
||||
setArgumentNamesFromStringArray(tokens);
|
||||
}
|
||||
|
||||
public void setArgumentNamesFromStringArray(String[] args) {
|
||||
this.argumentNames = new String[args.length];
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
this.argumentNames[i] = StringUtils.trimWhitespace(args[i]);
|
||||
if (!isVariableName(this.argumentNames[i])) {
|
||||
throw new IllegalArgumentException(
|
||||
"'argumentNames' property of AbstractAspectJAdvice contains an argument name '" +
|
||||
this.argumentNames[i] + "' that is not a valid Java identifier");
|
||||
}
|
||||
}
|
||||
if (argumentNames != null) {
|
||||
if (aspectJAdviceMethod.getParameterTypes().length == argumentNames.length + 1) {
|
||||
// May need to add implicit join point arg name...
|
||||
Class firstArgType = aspectJAdviceMethod.getParameterTypes()[0];
|
||||
if (firstArgType == JoinPoint.class ||
|
||||
firstArgType == ProceedingJoinPoint.class ||
|
||||
firstArgType == JoinPoint.StaticPart.class) {
|
||||
String[] oldNames = argumentNames;
|
||||
argumentNames = new String[oldNames.length + 1];
|
||||
argumentNames[0] = "THIS_JOIN_POINT";
|
||||
System.arraycopy(oldNames, 0, argumentNames, 1, oldNames.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setReturningName(String name) {
|
||||
throw new UnsupportedOperationException("Only afterReturning advice can be used to bind a return value");
|
||||
}
|
||||
|
||||
/**
|
||||
* We need to hold the returning name at this level for argument binding calculations,
|
||||
* this method allows the afterReturning advice subclass to set the name.
|
||||
*/
|
||||
protected void setReturningNameNoCheck(String name) {
|
||||
// name could be a variable or a type...
|
||||
if (isVariableName(name)) {
|
||||
this.returningName = name;
|
||||
}
|
||||
else {
|
||||
// assume a type
|
||||
try {
|
||||
this.discoveredReturningType = ClassUtils.forName(name, getAspectClassLoader());
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new IllegalArgumentException("Returning name '" + name +
|
||||
"' is neither a valid argument name nor the fully-qualified name of a Java type on the classpath. " +
|
||||
"Root cause: " + ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Class getDiscoveredReturningType() {
|
||||
return this.discoveredReturningType;
|
||||
}
|
||||
|
||||
protected Object getDiscoveredReturningGenericType() {
|
||||
return this.discoveredReturningGenericType;
|
||||
}
|
||||
|
||||
public void setThrowingName(String name) {
|
||||
throw new UnsupportedOperationException("Only afterThrowing advice can be used to bind a thrown exception");
|
||||
}
|
||||
|
||||
/**
|
||||
* We need to hold the throwing name at this level for argument binding calculations,
|
||||
* this method allows the afterThrowing advice subclass to set the name.
|
||||
*/
|
||||
protected void setThrowingNameNoCheck(String name) {
|
||||
// name could be a variable or a type...
|
||||
if (isVariableName(name)) {
|
||||
this.throwingName = name;
|
||||
}
|
||||
else {
|
||||
// assume a type
|
||||
try {
|
||||
this.discoveredThrowingType = ClassUtils.forName(name, getAspectClassLoader());
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new IllegalArgumentException("Throwing name '" + name +
|
||||
"' is neither a valid argument name nor the fully-qualified name of a Java type on the classpath. " +
|
||||
"Root cause: " + ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Class getDiscoveredThrowingType() {
|
||||
return this.discoveredThrowingType;
|
||||
}
|
||||
|
||||
private boolean isVariableName(String name) {
|
||||
char[] chars = name.toCharArray();
|
||||
if (!Character.isJavaIdentifierStart(chars[0])) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 1; i < chars.length; i++) {
|
||||
if (!Character.isJavaIdentifierPart(chars[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Do as much work as we can as part of the set-up so that argument binding
|
||||
* on subsequent advice invocations can be as fast as possible.
|
||||
* <p>If the first argument is of type JoinPoint or ProceedingJoinPoint then we
|
||||
* pass a JoinPoint in that position (ProceedingJoinPoint for around advice).
|
||||
* <p>If the first argument is of type <code>JoinPoint.StaticPart</code>
|
||||
* then we pass a <code>JoinPoint.StaticPart</code> in that position.
|
||||
* <p>Remaining arguments have to be bound by pointcut evaluation at
|
||||
* a given join point. We will get back a map from argument name to
|
||||
* value. We need to calculate which advice parameter needs to be bound
|
||||
* to which argument name. There are multiple strategies for determining
|
||||
* this binding, which are arranged in a ChainOfResponsibility.
|
||||
*/
|
||||
public synchronized final void calculateArgumentBindings() {
|
||||
// The simple case... nothing to bind.
|
||||
if (this.argumentsIntrospected || this.adviceInvocationArgumentCount == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int numUnboundArgs = this.adviceInvocationArgumentCount;
|
||||
Class[] parameterTypes = this.aspectJAdviceMethod.getParameterTypes();
|
||||
if (maybeBindJoinPoint(parameterTypes[0]) || maybeBindProceedingJoinPoint(parameterTypes[0])) {
|
||||
numUnboundArgs--;
|
||||
}
|
||||
else if (maybeBindJoinPointStaticPart(parameterTypes[0])) {
|
||||
numUnboundArgs--;
|
||||
}
|
||||
|
||||
if (numUnboundArgs > 0) {
|
||||
// need to bind arguments by name as returned from the pointcut match
|
||||
bindArgumentsByName(numUnboundArgs);
|
||||
}
|
||||
|
||||
this.argumentsIntrospected = true;
|
||||
}
|
||||
|
||||
private boolean maybeBindJoinPoint(Class candidateParameterType) {
|
||||
if (candidateParameterType.equals(JoinPoint.class)) {
|
||||
this.joinPointArgumentIndex = 0;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean maybeBindProceedingJoinPoint(Class candidateParameterType) {
|
||||
if (candidateParameterType.equals(ProceedingJoinPoint.class)) {
|
||||
if (!supportsProceedingJoinPoint()) {
|
||||
throw new IllegalArgumentException("ProceedingJoinPoint is only supported for around advice");
|
||||
}
|
||||
this.joinPointArgumentIndex = 0;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean supportsProceedingJoinPoint() {
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean maybeBindJoinPointStaticPart(Class candidateParameterType) {
|
||||
if (candidateParameterType.equals(JoinPoint.StaticPart.class)) {
|
||||
this.joinPointStaticPartArgumentIndex = 0;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void bindArgumentsByName(int numArgumentsExpectingToBind) {
|
||||
if (this.argumentNames == null) {
|
||||
this.argumentNames = createParameterNameDiscoverer().getParameterNames(this.aspectJAdviceMethod);
|
||||
}
|
||||
if (this.argumentNames != null) {
|
||||
// We have been able to determine the arg names.
|
||||
bindExplicitArguments(numArgumentsExpectingToBind);
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("Advice method [" + this.aspectJAdviceMethod.getName() + "] " +
|
||||
"requires " + numArgumentsExpectingToBind + " arguments to be bound by name, but " +
|
||||
"the argument names were not specified and could not be discovered.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ParameterNameDiscoverer to be used for argument binding.
|
||||
* <p>The default implementation creates a {@link PrioritizedParameterNameDiscoverer}
|
||||
* containing a {@link LocalVariableTableParameterNameDiscoverer} and an
|
||||
* {@link AspectJAdviceParameterNameDiscoverer}.
|
||||
*/
|
||||
protected ParameterNameDiscoverer createParameterNameDiscoverer() {
|
||||
// We need to discover them, or if that fails, guess,
|
||||
// and if we can't guess with 100% accuracy, fail.
|
||||
PrioritizedParameterNameDiscoverer discoverer = new PrioritizedParameterNameDiscoverer();
|
||||
discoverer.addDiscoverer(new LocalVariableTableParameterNameDiscoverer());
|
||||
AspectJAdviceParameterNameDiscoverer adviceParameterNameDiscoverer =
|
||||
new AspectJAdviceParameterNameDiscoverer(this.pointcut.getExpression());
|
||||
adviceParameterNameDiscoverer.setReturningName(this.returningName);
|
||||
adviceParameterNameDiscoverer.setThrowingName(this.throwingName);
|
||||
// Last in chain, so if we're called and we fail, that's bad...
|
||||
adviceParameterNameDiscoverer.setRaiseExceptions(true);
|
||||
discoverer.addDiscoverer(adviceParameterNameDiscoverer);
|
||||
return discoverer;
|
||||
}
|
||||
|
||||
private void bindExplicitArguments(int numArgumentsLeftToBind) {
|
||||
this.argumentBindings = new HashMap();
|
||||
|
||||
int numExpectedArgumentNames = this.aspectJAdviceMethod.getParameterTypes().length;
|
||||
if (this.argumentNames.length != numExpectedArgumentNames) {
|
||||
throw new IllegalStateException("Expecting to find " + numExpectedArgumentNames
|
||||
+ " arguments to bind by name in advice, but actually found " +
|
||||
this.argumentNames.length + " arguments.");
|
||||
}
|
||||
|
||||
// So we match in number...
|
||||
int argumentIndexOffset = this.adviceInvocationArgumentCount - numArgumentsLeftToBind;
|
||||
for (int i = argumentIndexOffset; i < this.argumentNames.length; i++) {
|
||||
this.argumentBindings.put(this.argumentNames[i],new Integer(i));
|
||||
}
|
||||
|
||||
// Check that returning and throwing were in the argument names list if
|
||||
// specified, and find the discovered argument types.
|
||||
if (this.returningName != null) {
|
||||
if (!this.argumentBindings.containsKey(this.returningName)) {
|
||||
throw new IllegalStateException("Returning argument name '"
|
||||
+ this.returningName + "' was not bound in advice arguments");
|
||||
}
|
||||
else {
|
||||
Integer index = (Integer) this.argumentBindings.get(this.returningName);
|
||||
this.discoveredReturningType = this.aspectJAdviceMethod.getParameterTypes()[index.intValue()];
|
||||
if (JdkVersion.isAtLeastJava15()) {
|
||||
this.discoveredReturningGenericType =
|
||||
this.aspectJAdviceMethod.getGenericParameterTypes()[index.intValue()];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.throwingName != null) {
|
||||
if (!this.argumentBindings.containsKey(this.throwingName)) {
|
||||
throw new IllegalStateException("Throwing argument name '"
|
||||
+ this.throwingName + "' was not bound in advice arguments");
|
||||
}
|
||||
else {
|
||||
Integer index = (Integer) this.argumentBindings.get(this.throwingName);
|
||||
this.discoveredThrowingType = this.aspectJAdviceMethod.getParameterTypes()[index.intValue()];
|
||||
}
|
||||
}
|
||||
|
||||
// configure the pointcut expression accordingly.
|
||||
configurePointcutParameters(argumentIndexOffset);
|
||||
}
|
||||
|
||||
/**
|
||||
* All parameters from argumentIndexOffset onwards are candidates for
|
||||
* pointcut parameters - but returning and throwing vars are handled differently
|
||||
* and must be removed from the list if present.
|
||||
*/
|
||||
private void configurePointcutParameters(int argumentIndexOffset) {
|
||||
int numParametersToRemove = argumentIndexOffset;
|
||||
if (this.returningName != null) {
|
||||
numParametersToRemove++;
|
||||
}
|
||||
if (this.throwingName != null) {
|
||||
numParametersToRemove++;
|
||||
}
|
||||
String[] pointcutParameterNames = new String[this.argumentNames.length - numParametersToRemove];
|
||||
Class[] pointcutParameterTypes = new Class[pointcutParameterNames.length];
|
||||
Class[] methodParameterTypes = this.aspectJAdviceMethod.getParameterTypes();
|
||||
|
||||
int index = 0;
|
||||
for (int i = 0; i < this.argumentNames.length; i++) {
|
||||
if (i < argumentIndexOffset) {
|
||||
continue;
|
||||
}
|
||||
if (this.argumentNames[i].equals(this.returningName) ||
|
||||
this.argumentNames[i].equals(this.throwingName)) {
|
||||
continue;
|
||||
}
|
||||
pointcutParameterNames[index] = this.argumentNames[i];
|
||||
pointcutParameterTypes[index] = methodParameterTypes[i];
|
||||
index++;
|
||||
}
|
||||
|
||||
this.pointcut.setParameterNames(pointcutParameterNames);
|
||||
this.pointcut.setParameterTypes(pointcutParameterTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Take the arguments at the method execution join point and output a set of arguments
|
||||
* to the advice method
|
||||
* @param jp the current JoinPoint
|
||||
* @param jpMatch the join point match that matched this execution join point
|
||||
* @param returnValue the return value from the method execution (may be null)
|
||||
* @param ex the exception thrown by the method execution (may be null)
|
||||
* @return the empty array if there are no arguments
|
||||
*/
|
||||
protected Object[] argBinding(JoinPoint jp, JoinPointMatch jpMatch, Object returnValue, Throwable ex) {
|
||||
calculateArgumentBindings();
|
||||
|
||||
// AMC start
|
||||
Object[] adviceInvocationArgs = new Object[this.adviceInvocationArgumentCount];
|
||||
int numBound = 0;
|
||||
|
||||
if (this.joinPointArgumentIndex != -1) {
|
||||
adviceInvocationArgs[this.joinPointArgumentIndex] = jp;
|
||||
numBound++;
|
||||
}
|
||||
else if (this.joinPointStaticPartArgumentIndex != -1) {
|
||||
adviceInvocationArgs[this.joinPointStaticPartArgumentIndex] = jp.getStaticPart();
|
||||
numBound++;
|
||||
}
|
||||
|
||||
if (!CollectionUtils.isEmpty(this.argumentBindings)) {
|
||||
// binding from pointcut match
|
||||
if (jpMatch != null) {
|
||||
PointcutParameter[] parameterBindings = jpMatch.getParameterBindings();
|
||||
for (int i = 0; i < parameterBindings.length; i++) {
|
||||
PointcutParameter parameter = parameterBindings[i];
|
||||
String name = parameter.getName();
|
||||
Integer index = (Integer) this.argumentBindings.get(name);
|
||||
adviceInvocationArgs[index.intValue()] = parameter.getBinding();
|
||||
numBound++;
|
||||
}
|
||||
}
|
||||
// binding from returning clause
|
||||
if (this.returningName != null) {
|
||||
Integer index = (Integer) this.argumentBindings.get(this.returningName);
|
||||
adviceInvocationArgs[index.intValue()] = returnValue;
|
||||
numBound++;
|
||||
}
|
||||
// binding from thrown exception
|
||||
if (this.throwingName != null) {
|
||||
Integer index = (Integer) this.argumentBindings.get(this.throwingName);
|
||||
adviceInvocationArgs[index.intValue()] = ex;
|
||||
numBound++;
|
||||
}
|
||||
}
|
||||
|
||||
if (numBound != this.adviceInvocationArgumentCount) {
|
||||
throw new IllegalStateException("Required to bind " + this.adviceInvocationArgumentCount
|
||||
+ " arguments, but only bound " + numBound + " (JoinPointMatch " +
|
||||
(jpMatch == null ? "was NOT" : "WAS") +
|
||||
" bound in invocation)");
|
||||
}
|
||||
|
||||
return adviceInvocationArgs;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Invoke the advice method.
|
||||
* @param jpMatch the JoinPointMatch that matched this execution join point
|
||||
* @param returnValue the return value from the method execution (may be null)
|
||||
* @param ex the exception thrown by the method execution (may be null)
|
||||
* @return the invocation result
|
||||
* @throws Throwable in case of invocation failure
|
||||
*/
|
||||
protected Object invokeAdviceMethod(JoinPointMatch jpMatch, Object returnValue, Throwable ex) throws Throwable {
|
||||
return invokeAdviceMethodWithGivenArgs(argBinding(getJoinPoint(), jpMatch, returnValue, ex));
|
||||
}
|
||||
|
||||
// As above, but in this case we are given the join point.
|
||||
protected Object invokeAdviceMethod(JoinPoint jp, JoinPointMatch jpMatch, Object returnValue, Throwable t)
|
||||
throws Throwable {
|
||||
|
||||
return invokeAdviceMethodWithGivenArgs(argBinding(jp, jpMatch, returnValue, t));
|
||||
}
|
||||
|
||||
protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {
|
||||
Object[] actualArgs = args;
|
||||
if (this.aspectJAdviceMethod.getParameterTypes().length == 0) {
|
||||
actualArgs = null;
|
||||
}
|
||||
try {
|
||||
ReflectionUtils.makeAccessible(this.aspectJAdviceMethod);
|
||||
// TODO AopUtils.invokeJoinpointUsingReflection
|
||||
return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs);
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
throw new AopInvocationException("Mismatch on arguments to advice method [" +
|
||||
this.aspectJAdviceMethod + "]; pointcut expression [" +
|
||||
this.pointcut.getPointcutExpression() + "]", ex);
|
||||
}
|
||||
catch (InvocationTargetException ex) {
|
||||
throw ex.getTargetException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridden in around advice to return proceeding join point.
|
||||
*/
|
||||
protected JoinPoint getJoinPoint() {
|
||||
return currentJoinPoint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current join point match at the join point we are being dispatched on.
|
||||
*/
|
||||
protected JoinPointMatch getJoinPointMatch() {
|
||||
MethodInvocation mi = ExposeInvocationInterceptor.currentInvocation();
|
||||
if (!(mi instanceof ProxyMethodInvocation)) {
|
||||
throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
|
||||
}
|
||||
return getJoinPointMatch((ProxyMethodInvocation) mi);
|
||||
}
|
||||
|
||||
// Note: We can't use JoinPointMatch.getClass().getName() as the key, since
|
||||
// Spring AOP does all the matching at a join point, and then all the invocations.
|
||||
// Under this scenario, if we just use JoinPointMatch as the key, then
|
||||
// 'last man wins' which is not what we want at all.
|
||||
// Using the expression is guaranteed to be safe, since 2 identical expressions
|
||||
// are guaranteed to bind in exactly the same way.
|
||||
protected JoinPointMatch getJoinPointMatch(ProxyMethodInvocation pmi) {
|
||||
return (JoinPointMatch) pmi.getUserAttribute(this.pointcut.getExpression());
|
||||
}
|
||||
|
||||
|
||||
public String toString() {
|
||||
return getClass().getName() + ": advice method [" + this.aspectJAdviceMethod + "]; " +
|
||||
"aspect name '" + this.aspectName + "'";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* MethodMatcher that excludes the specified advice method.
|
||||
* @see AbstractAspectJAdvice#buildSafePointcut()
|
||||
*/
|
||||
private static class AdviceExcludingMethodMatcher extends StaticMethodMatcher {
|
||||
|
||||
private final Method adviceMethod;
|
||||
|
||||
public AdviceExcludingMethodMatcher(Method adviceMethod) {
|
||||
this.adviceMethod = adviceMethod;
|
||||
}
|
||||
|
||||
public boolean matches(Method method, Class targetClass) {
|
||||
return !this.adviceMethod.equals(method);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
/**
|
||||
* Interface implemented to provide an instance of an AspectJ aspect.
|
||||
* Decouples from Spring's bean factory.
|
||||
*
|
||||
* <p>Extends the {@link org.springframework.core.Ordered} interface
|
||||
* to express an order value for the underlying aspect in a chain.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see org.springframework.beans.factory.BeanFactory#getBean
|
||||
*/
|
||||
public interface AspectInstanceFactory extends Ordered {
|
||||
|
||||
/**
|
||||
* Create an instance of this factory's aspect.
|
||||
* @return the aspect instance (never <code>null</code>)
|
||||
*/
|
||||
Object getAspectInstance();
|
||||
|
||||
/**
|
||||
* Expose the aspect class loader that this factory uses.
|
||||
* @return the aspect class loader (never <code>null</code>)
|
||||
*/
|
||||
ClassLoader getAspectClassLoader();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,826 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.weaver.tools.PointcutParser;
|
||||
import org.aspectj.weaver.tools.PointcutPrimitive;
|
||||
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link ParameterNameDiscoverer} implementation that tries to deduce parameter names
|
||||
* for an advice method from the pointcut expression, returning, and throwing clauses.
|
||||
* If an unambiguous interpretation is not available, it returns <code>null</code>.
|
||||
*
|
||||
* <p>This class interprets arguments in the following way:
|
||||
* <ol>
|
||||
* <li>If the first parameter of the method is of type {@link JoinPoint}
|
||||
* or {@link ProceedingJoinPoint}, it is assumed to be for passing
|
||||
* <code>thisJoinPoint</code> to the advice, and the parameter name will
|
||||
* be assigned the value <code>"thisJoinPoint"</code>.</li>
|
||||
* <li>If the first parameter of the method is of type
|
||||
* <code>JoinPoint.StaticPart</code>, it is assumed to be for passing
|
||||
* <code>"thisJoinPointStaticPart"</code> to the advice, and the parameter name
|
||||
* will be assigned the value <code>"thisJoinPointStaticPart"</code>.</li>
|
||||
* <li>If a {@link #setThrowingName(String) throwingName} has been set, and
|
||||
* there are no unbound arguments of type <code>Throwable+</code>, then an
|
||||
* {@link IllegalArgumentException} is raised. If there is more than one
|
||||
* unbound argument of type <code>Throwable+</code>, then an
|
||||
* {@link AmbiguousBindingException} is raised. If there is exactly one
|
||||
* unbound argument of type <code>Throwable+</code>, then the corresponding
|
||||
* parameter name is assigned the value <throwingName>.</li>
|
||||
* <li>If there remain unbound arguments, then the pointcut expression is
|
||||
* examined. Let <code>a</code> be the number of annotation-based pointcut
|
||||
* expressions (@annotation, @this, @target, @args,
|
||||
* @within, @withincode) that are used in binding form. Usage in
|
||||
* binding form has itself to be deduced: if the expression inside the
|
||||
* pointcut is a single string literal that meets Java variable name
|
||||
* conventions it is assumed to be a variable name. If <code>a</code> is
|
||||
* zero we proceed to the next stage. If <code>a</code> > 1 then an
|
||||
* <code>AmbiguousBindingException</code> is raised. If <code>a</code> == 1,
|
||||
* and there are no unbound arguments of type <code>Annotation+</code>,
|
||||
* then an <code>IllegalArgumentException</code> is raised. if there is
|
||||
* exactly one such argument, then the corresponding parameter name is
|
||||
* assigned the value from the pointcut expression.</li>
|
||||
* <li>If a returningName has been set, and there are no unbound arguments
|
||||
* then an <code>IllegalArgumentException</code> is raised. If there is
|
||||
* more than one unbound argument then an
|
||||
* <code>AmbiguousBindingException</code> is raised. If there is exactly
|
||||
* one unbound argument then the corresponding parameter name is assigned
|
||||
* the value <returningName>.</li>
|
||||
* <li>If there remain unbound arguments, then the pointcut expression is
|
||||
* examined once more for <code>this</code>, <code>target</code>, and
|
||||
* <code>args</code> pointcut expressions used in the binding form (binding
|
||||
* forms are deduced as described for the annotation based pointcuts). If
|
||||
* there remains more than one unbound argument of a primitive type (which
|
||||
* can only be bound in <code>args</code>) then an
|
||||
* <code>AmbiguousBindingException</code> is raised. If there is exactly
|
||||
* one argument of a primitive type, then if exactly one <code>args</code>
|
||||
* bound variable was found, we assign the corresponding parameter name
|
||||
* the variable name. If there were no <code>args</code> bound variables
|
||||
* found an <code>IllegalStateException</code> is raised. If there are
|
||||
* multiple <code>args</code> bound variables, an
|
||||
* <code>AmbiguousBindingException</code> is raised. At this point, if
|
||||
* there remains more than one unbound argument we raise an
|
||||
* <code>AmbiguousBindingException</code>. If there are no unbound arguments
|
||||
* remaining, we are done. If there is exactly one unbound argument
|
||||
* remaining, and only one candidate variable name unbound from
|
||||
* <code>this</code>, <code>target</code>, or <code>args</code>, it is
|
||||
* assigned as the corresponding parameter name. If there are multiple
|
||||
* possibilities, an <code>AmbiguousBindingException</code> is raised.</li>
|
||||
* </ol>
|
||||
*
|
||||
* <p>The behavior on raising an <code>IllegalArgumentException</code> or
|
||||
* <code>AmbiguousBindingException</code> is configurable to allow this discoverer
|
||||
* to be used as part of a chain-of-responsibility. By default the condition will
|
||||
* be logged and the <code>getParameterNames(..)</code> method will simply return
|
||||
* <code>null</code>. If the {@link #setRaiseExceptions(boolean) raiseExceptions}
|
||||
* property is set to <code>true</code>, the conditions will be thrown as
|
||||
* <code>IllegalArgumentException</code> and <code>AmbiguousBindingException</code>,
|
||||
* respectively.
|
||||
*
|
||||
* <p>Was that perfectly clear? ;)
|
||||
*
|
||||
* <p>Short version: If an unambiguous binding can be deduced, then it is.
|
||||
* If the advice requirements cannot possibly be satisfied, then <code>null</code>
|
||||
* is returned. By setting the {@link #setRaiseExceptions(boolean) raiseExceptions}
|
||||
* property to <code>true</code>, descriptive exceptions will be thrown instead of
|
||||
* returning <code>null</code> in the case that the parameter names cannot be discovered.
|
||||
*
|
||||
* @author Adrian Colyer
|
||||
* @since 2.0
|
||||
*/
|
||||
public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscoverer {
|
||||
|
||||
private static final String ANNOTATION_CLASS_NAME = "java.lang.annotation.Annotation";
|
||||
|
||||
private static final String THIS_JOIN_POINT = "thisJoinPoint";
|
||||
private static final String THIS_JOIN_POINT_STATIC_PART = "thisJoinPointStaticPart";
|
||||
|
||||
// Steps in the binding algorithm...
|
||||
private static final int STEP_JOIN_POINT_BINDING = 1;
|
||||
private static final int STEP_THROWING_BINDING = 2;
|
||||
private static final int STEP_ANNOTATION_BINDING = 3;
|
||||
private static final int STEP_RETURNING_BINDING = 4;
|
||||
private static final int STEP_PRIMITIVE_ARGS_BINDING = 5;
|
||||
private static final int STEP_THIS_TARGET_ARGS_BINDING = 6;
|
||||
private static final int STEP_REFERENCE_PCUT_BINDING = 7;
|
||||
private static final int STEP_FINISHED = 8;
|
||||
|
||||
private static final Set singleValuedAnnotationPcds = new HashSet();
|
||||
private static final Set nonReferencePointcutTokens = new HashSet();
|
||||
|
||||
private static Class annotationClass;
|
||||
|
||||
|
||||
static {
|
||||
singleValuedAnnotationPcds.add("@this");
|
||||
singleValuedAnnotationPcds.add("@target");
|
||||
singleValuedAnnotationPcds.add("@within");
|
||||
singleValuedAnnotationPcds.add("@withincode");
|
||||
singleValuedAnnotationPcds.add("@annotation");
|
||||
|
||||
Set pointcutPrimitives = PointcutParser.getAllSupportedPointcutPrimitives();
|
||||
for (Iterator iterator = pointcutPrimitives.iterator(); iterator.hasNext();) {
|
||||
PointcutPrimitive primitive = (PointcutPrimitive) iterator.next();
|
||||
nonReferencePointcutTokens.add(primitive.getName());
|
||||
}
|
||||
nonReferencePointcutTokens.add("&&");
|
||||
nonReferencePointcutTokens.add("!");
|
||||
nonReferencePointcutTokens.add("||");
|
||||
nonReferencePointcutTokens.add("and");
|
||||
nonReferencePointcutTokens.add("or");
|
||||
nonReferencePointcutTokens.add("not");
|
||||
|
||||
try {
|
||||
annotationClass = ClassUtils.forName(ANNOTATION_CLASS_NAME,
|
||||
AspectJAdviceParameterNameDiscoverer.class.getClassLoader());
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
// Running on < JDK 1.5, this is OK...
|
||||
annotationClass = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private boolean raiseExceptions;
|
||||
|
||||
/**
|
||||
* If the advice is afterReturning, and binds the return value, this is the parameter name used.
|
||||
*/
|
||||
private String returningName;
|
||||
|
||||
/**
|
||||
* If the advice is afterThrowing, and binds the thrown value, this is the parameter name used.
|
||||
*/
|
||||
private String throwingName;
|
||||
|
||||
/**
|
||||
* The pointcut expression associated with the advice, as a simple String.
|
||||
*/
|
||||
private String pointcutExpression;
|
||||
|
||||
private Class[] argumentTypes;
|
||||
|
||||
private String[] parameterNameBindings;
|
||||
|
||||
private int numberOfRemainingUnboundArguments;
|
||||
|
||||
private int algorithmicStep = STEP_JOIN_POINT_BINDING;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new discoverer that attempts to discover parameter names
|
||||
* from the given pointcut expression.
|
||||
*/
|
||||
public AspectJAdviceParameterNameDiscoverer(String pointcutExpression) {
|
||||
this.pointcutExpression = pointcutExpression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate whether {@link IllegalArgumentException} and {@link AmbiguousBindingException}
|
||||
* must be thrown as appropriate in the case of failing to deduce advice parameter names.
|
||||
* @param raiseExceptions <code>true</code> if exceptions are to be thrown
|
||||
*/
|
||||
public void setRaiseExceptions(boolean raiseExceptions) {
|
||||
this.raiseExceptions = raiseExceptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* If <code>afterReturning</code> advice binds the return value, the
|
||||
* returning variable name must be specified.
|
||||
* @param returningName the name of the returning variable
|
||||
*/
|
||||
public void setReturningName(String returningName) {
|
||||
this.returningName = returningName;
|
||||
}
|
||||
|
||||
/**
|
||||
* If <code>afterThrowing</code> advice binds the thrown value, the
|
||||
* throwing variable name must be specified.
|
||||
* @param throwingName the name of the throwing variable
|
||||
*/
|
||||
public void setThrowingName(String throwingName) {
|
||||
this.throwingName = throwingName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deduce the parameter names for an advice method.
|
||||
* <p>See the {@link AspectJAdviceParameterNameDiscoverer class level javadoc}
|
||||
* for this class for details of the algorithm used.
|
||||
* @param method the target {@link Method}
|
||||
* @return the parameter names
|
||||
*/
|
||||
public String[] getParameterNames(Method method) {
|
||||
this.argumentTypes = method.getParameterTypes();
|
||||
this.numberOfRemainingUnboundArguments = this.argumentTypes.length;
|
||||
this.parameterNameBindings = new String[this.numberOfRemainingUnboundArguments];
|
||||
this.algorithmicStep = STEP_JOIN_POINT_BINDING;
|
||||
|
||||
int minimumNumberUnboundArgs = 0;
|
||||
if (this.returningName != null) {
|
||||
minimumNumberUnboundArgs++;
|
||||
}
|
||||
if (this.throwingName != null) {
|
||||
minimumNumberUnboundArgs++;
|
||||
}
|
||||
if (this.numberOfRemainingUnboundArguments < minimumNumberUnboundArgs) {
|
||||
throw new IllegalStateException(
|
||||
"Not enough arguments in method to satisfy binding of returning and throwing variables");
|
||||
}
|
||||
|
||||
try {
|
||||
while ((this.numberOfRemainingUnboundArguments > 0) && (this.algorithmicStep < STEP_FINISHED)) {
|
||||
switch (this.algorithmicStep++) {
|
||||
case STEP_JOIN_POINT_BINDING:
|
||||
if (!maybeBindThisJoinPoint()) {
|
||||
maybeBindThisJoinPointStaticPart();
|
||||
}
|
||||
break;
|
||||
case STEP_THROWING_BINDING:
|
||||
maybeBindThrowingVariable();
|
||||
break;
|
||||
case STEP_ANNOTATION_BINDING:
|
||||
maybeBindAnnotationsFromPointcutExpression();
|
||||
break;
|
||||
case STEP_RETURNING_BINDING:
|
||||
maybeBindReturningVariable();
|
||||
break;
|
||||
case STEP_PRIMITIVE_ARGS_BINDING:
|
||||
maybeBindPrimitiveArgsFromPointcutExpression();
|
||||
break;
|
||||
case STEP_THIS_TARGET_ARGS_BINDING:
|
||||
maybeBindThisOrTargetOrArgsFromPointcutExpression();
|
||||
break;
|
||||
case STEP_REFERENCE_PCUT_BINDING:
|
||||
maybeBindReferencePointcutParameter();
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unknown algorithmic step: " + (this.algorithmicStep - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (AmbiguousBindingException ambigEx) {
|
||||
if (this.raiseExceptions) {
|
||||
throw ambigEx;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
if (this.raiseExceptions) {
|
||||
throw ex;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.numberOfRemainingUnboundArguments == 0) {
|
||||
return this.parameterNameBindings;
|
||||
}
|
||||
else {
|
||||
if (this.raiseExceptions) {
|
||||
throw new IllegalStateException("Failed to bind all argument names: " +
|
||||
this.numberOfRemainingUnboundArguments + " argument(s) could not be bound");
|
||||
}
|
||||
else {
|
||||
// convention for failing is to return null, allowing participation in a chain of responsibility
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An advice method can never be a constructor in Spring.
|
||||
* @return <code>null</code>
|
||||
* @throws UnsupportedOperationException if
|
||||
* {@link #setRaiseExceptions(boolean) raiseExceptions} has been set to <code>true</code>
|
||||
*/
|
||||
public String[] getParameterNames(Constructor ctor) {
|
||||
if (this.raiseExceptions) {
|
||||
throw new UnsupportedOperationException("An advice method can never be a constructor");
|
||||
}
|
||||
else {
|
||||
// we return null rather than throw an exception so that we behave well
|
||||
// in a chain-of-responsibility.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void bindParameterName(int index, String name) {
|
||||
this.parameterNameBindings[index] = name;
|
||||
this.numberOfRemainingUnboundArguments--;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the first parameter is of type JoinPoint or ProceedingJoinPoint,bind "thisJoinPoint" as
|
||||
* parameter name and return true, else return false.
|
||||
*/
|
||||
private boolean maybeBindThisJoinPoint() {
|
||||
if ((this.argumentTypes[0] == JoinPoint.class) || (this.argumentTypes[0] == ProceedingJoinPoint.class)) {
|
||||
bindParameterName(0, THIS_JOIN_POINT);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void maybeBindThisJoinPointStaticPart() {
|
||||
if (this.argumentTypes[0] == JoinPoint.StaticPart.class) {
|
||||
bindParameterName(0, THIS_JOIN_POINT_STATIC_PART);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If a throwing name was specified and there is exactly one choice remaining
|
||||
* (argument that is a subtype of Throwable) then bind it.
|
||||
*/
|
||||
private void maybeBindThrowingVariable() {
|
||||
if (this.throwingName == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// So there is binding work to do...
|
||||
int throwableIndex = -1;
|
||||
for (int i = 0; i < this.argumentTypes.length; i++) {
|
||||
if (isUnbound(i) && isSubtypeOf(Throwable.class, i)) {
|
||||
if (throwableIndex == -1) {
|
||||
throwableIndex = i;
|
||||
}
|
||||
else {
|
||||
// Second candidate we've found - ambiguous binding
|
||||
throw new AmbiguousBindingException("Binding of throwing parameter '" +
|
||||
this.throwingName + "' is ambiguous: could be bound to argument " +
|
||||
throwableIndex + " or argument " + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (throwableIndex == -1) {
|
||||
throw new IllegalStateException("Binding of throwing parameter '" + this.throwingName
|
||||
+ "' could not be completed as no available arguments are a subtype of Throwable");
|
||||
}
|
||||
else {
|
||||
bindParameterName(throwableIndex, this.throwingName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If a returning variable was specified and there is only one choice remaining, bind it.
|
||||
*/
|
||||
private void maybeBindReturningVariable() {
|
||||
if (this.numberOfRemainingUnboundArguments == 0) {
|
||||
throw new IllegalStateException(
|
||||
"Algorithm assumes that there must be at least one unbound parameter on entry to this method");
|
||||
}
|
||||
|
||||
if (this.returningName != null) {
|
||||
if (this.numberOfRemainingUnboundArguments > 1) {
|
||||
throw new AmbiguousBindingException("Binding of returning parameter '" + this.returningName +
|
||||
"' is ambiguous, there are " + this.numberOfRemainingUnboundArguments + " candidates.");
|
||||
}
|
||||
|
||||
// We're all set... find the unbound parameter, and bind it.
|
||||
for (int i = 0; i < this.parameterNameBindings.length; i++) {
|
||||
if (this.parameterNameBindings[i] == null) {
|
||||
bindParameterName(i, this.returningName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse the string pointcut expression looking for:
|
||||
* @this, @target, @args, @within, @withincode, @annotation.
|
||||
* If we find one of these pointcut expressions, try and extract a candidate variable
|
||||
* name (or variable names, in the case of args).
|
||||
* <p>Some more support from AspectJ in doing this exercise would be nice... :)
|
||||
*/
|
||||
private void maybeBindAnnotationsFromPointcutExpression() {
|
||||
List varNames = new ArrayList();
|
||||
String[] tokens = StringUtils.tokenizeToStringArray(this.pointcutExpression, " ");
|
||||
for (int i = 0; i < tokens.length; i++) {
|
||||
String toMatch = tokens[i];
|
||||
int firstParenIndex = toMatch.indexOf("(");
|
||||
if (firstParenIndex != -1) {
|
||||
toMatch = toMatch.substring(0, firstParenIndex);
|
||||
}
|
||||
if (singleValuedAnnotationPcds.contains(toMatch)) {
|
||||
PointcutBody body = getPointcutBody(tokens, i);
|
||||
i += body.numTokensConsumed;
|
||||
String varName = maybeExtractVariableName(body.text);
|
||||
if (varName != null) {
|
||||
varNames.add(varName);
|
||||
}
|
||||
}
|
||||
else if (tokens[i].startsWith("@args(") || tokens[i].equals("@args")) {
|
||||
PointcutBody body = getPointcutBody(tokens, i);
|
||||
i += body.numTokensConsumed;
|
||||
maybeExtractVariableNamesFromArgs(body.text, varNames);
|
||||
}
|
||||
}
|
||||
|
||||
bindAnnotationsFromVarNames(varNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* Match the given list of extracted variable names to argument slots.
|
||||
*/
|
||||
private void bindAnnotationsFromVarNames(List varNames) {
|
||||
if (!varNames.isEmpty()) {
|
||||
// we have work to do...
|
||||
int numAnnotationSlots = countNumberOfUnboundAnnotationArguments();
|
||||
if (numAnnotationSlots > 1) {
|
||||
throw new AmbiguousBindingException("Found " + varNames.size() +
|
||||
" potential annotation variable(s), and " +
|
||||
numAnnotationSlots + " potential argument slots");
|
||||
}
|
||||
else if (numAnnotationSlots == 1) {
|
||||
if (varNames.size() == 1) {
|
||||
// it's a match
|
||||
findAndBind(annotationClass, (String) varNames.get(0));
|
||||
}
|
||||
else {
|
||||
// multiple candidate vars, but only one slot
|
||||
throw new IllegalArgumentException("Found " + varNames.size() +
|
||||
" candidate annotation binding variables" +
|
||||
" but only one potential argument binding slot");
|
||||
}
|
||||
}
|
||||
else {
|
||||
// no slots so presume those candidate vars were actually type names
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If the token starts meets Java identifier conventions, it's in.
|
||||
*/
|
||||
private String maybeExtractVariableName(String candidateToken) {
|
||||
if (candidateToken == null || candidateToken.equals("")) {
|
||||
return null;
|
||||
}
|
||||
if (Character.isJavaIdentifierStart(candidateToken.charAt(0)) &&
|
||||
Character.isLowerCase(candidateToken.charAt(0))) {
|
||||
char[] tokenChars = candidateToken.toCharArray();
|
||||
for (int i = 0; i < tokenChars.length; i++) {
|
||||
if (!Character.isJavaIdentifierPart(tokenChars[i])) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return candidateToken;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an args pointcut body (could be <code>args</code> or <code>at_args</code>),
|
||||
* add any candidate variable names to the given list.
|
||||
*/
|
||||
private void maybeExtractVariableNamesFromArgs(String argsSpec, List varNames) {
|
||||
if (argsSpec == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String[] tokens = StringUtils.tokenizeToStringArray(argsSpec, ",");
|
||||
for (int i = 0; i < tokens.length; i++) {
|
||||
tokens[i] = StringUtils.trimWhitespace(tokens[i]);
|
||||
String varName = maybeExtractVariableName(tokens[i]);
|
||||
if (varName != null) {
|
||||
varNames.add(varName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the string pointcut expression looking for this(), target() and args() expressions.
|
||||
* If we find one, try and extract a candidate variable name and bind it.
|
||||
*/
|
||||
private void maybeBindThisOrTargetOrArgsFromPointcutExpression() {
|
||||
if (this.numberOfRemainingUnboundArguments > 1) {
|
||||
throw new AmbiguousBindingException("Still " + this.numberOfRemainingUnboundArguments
|
||||
+ " unbound args at this(),target(),args() binding stage, with no way to determine between them");
|
||||
}
|
||||
|
||||
List varNames = new ArrayList();
|
||||
String[] tokens = StringUtils.tokenizeToStringArray(this.pointcutExpression, " ");
|
||||
for (int i = 0; i < tokens.length; i++) {
|
||||
if (tokens[i].equals("this") ||
|
||||
tokens[i].startsWith("this(") ||
|
||||
tokens[i].equals("target") ||
|
||||
tokens[i].startsWith("target(")) {
|
||||
PointcutBody body = getPointcutBody(tokens, i);
|
||||
i += body.numTokensConsumed;
|
||||
String varName = maybeExtractVariableName(body.text);
|
||||
if (varName != null) {
|
||||
varNames.add(varName);
|
||||
}
|
||||
}
|
||||
else if (tokens[i].equals("args") || tokens[i].startsWith("args(")) {
|
||||
PointcutBody body = getPointcutBody(tokens, i);
|
||||
i += body.numTokensConsumed;
|
||||
List candidateVarNames = new ArrayList();
|
||||
maybeExtractVariableNamesFromArgs(body.text, candidateVarNames);
|
||||
// we may have found some var names that were bound in previous primitive args binding step,
|
||||
// filter them out...
|
||||
for (Iterator iter = candidateVarNames.iterator(); iter.hasNext();) {
|
||||
String varName = (String) iter.next();
|
||||
if (!alreadyBound(varName)) {
|
||||
varNames.add(varName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (varNames.size() > 1) {
|
||||
throw new AmbiguousBindingException("Found " + varNames.size() +
|
||||
" candidate this(), target() or args() variables but only one unbound argument slot");
|
||||
}
|
||||
else if (varNames.size() == 1) {
|
||||
for (int j = 0; j < this.parameterNameBindings.length; j++) {
|
||||
if (isUnbound(j)) {
|
||||
bindParameterName(j, (String) varNames.get(0));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// else varNames.size must be 0 and we have nothing to bind.
|
||||
}
|
||||
|
||||
private void maybeBindReferencePointcutParameter() {
|
||||
if (this.numberOfRemainingUnboundArguments > 1) {
|
||||
throw new AmbiguousBindingException("Still " + this.numberOfRemainingUnboundArguments
|
||||
+ " unbound args at reference pointcut binding stage, with no way to determine between them");
|
||||
}
|
||||
|
||||
List varNames = new ArrayList();
|
||||
String[] tokens = StringUtils.tokenizeToStringArray(this.pointcutExpression, " ");
|
||||
for (int i = 0; i < tokens.length; i++) {
|
||||
String toMatch = tokens[i];
|
||||
if (toMatch.startsWith("!")) {
|
||||
toMatch = toMatch.substring(1);
|
||||
}
|
||||
int firstParenIndex = toMatch.indexOf("(");
|
||||
if (firstParenIndex != -1) {
|
||||
toMatch = toMatch.substring(0, firstParenIndex);
|
||||
}
|
||||
else {
|
||||
if (tokens.length < i + 2) {
|
||||
// no "(" and nothing following
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
String nextToken = tokens[i + 1];
|
||||
if (nextToken.charAt(0) != '(') {
|
||||
// next token is not "(" either, can't be a pc...
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// eat the body
|
||||
PointcutBody body = getPointcutBody(tokens, i);
|
||||
i += body.numTokensConsumed;
|
||||
|
||||
if (!nonReferencePointcutTokens.contains(toMatch)) {
|
||||
// then it could be a reference pointcut
|
||||
String varName = maybeExtractVariableName(body.text);
|
||||
if (varName != null) {
|
||||
varNames.add(varName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (varNames.size() > 1) {
|
||||
throw new AmbiguousBindingException("Found " + varNames.size() +
|
||||
" candidate reference pointcut variables but only one unbound argument slot");
|
||||
}
|
||||
else if (varNames.size() == 1) {
|
||||
for (int j = 0; j < this.parameterNameBindings.length; j++) {
|
||||
if (isUnbound(j)) {
|
||||
bindParameterName(j, (String) varNames.get(0));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// else varNames.size must be 0 and we have nothing to bind.
|
||||
}
|
||||
|
||||
/*
|
||||
* We've found the start of a binding pointcut at the given index into the
|
||||
* token array. Now we need to extract the pointcut body and return it.
|
||||
*/
|
||||
private PointcutBody getPointcutBody(String[] tokens, int startIndex) {
|
||||
int numTokensConsumed = 0;
|
||||
String currentToken = tokens[startIndex];
|
||||
int bodyStart = currentToken.indexOf('(');
|
||||
if (currentToken.charAt(currentToken.length() - 1) == ')') {
|
||||
// It's an all in one... get the text between the first (and the last)
|
||||
return new PointcutBody(0, currentToken.substring(bodyStart + 1, currentToken.length() - 1));
|
||||
}
|
||||
else {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
if (bodyStart >= 0 && bodyStart != (currentToken.length() - 1)) {
|
||||
sb.append(currentToken.substring(bodyStart + 1));
|
||||
sb.append(" ");
|
||||
}
|
||||
numTokensConsumed++;
|
||||
int currentIndex = startIndex + numTokensConsumed;
|
||||
while (currentIndex < tokens.length) {
|
||||
if (tokens[currentIndex].equals("(")) {
|
||||
currentIndex++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tokens[currentIndex].endsWith(")")) {
|
||||
sb.append(tokens[currentIndex].substring(0, tokens[currentIndex].length() - 1));
|
||||
return new PointcutBody(numTokensConsumed, sb.toString().trim());
|
||||
}
|
||||
|
||||
String toAppend = tokens[currentIndex];
|
||||
if (toAppend.startsWith("(")) {
|
||||
toAppend = toAppend.substring(1);
|
||||
}
|
||||
sb.append(toAppend);
|
||||
sb.append(" ");
|
||||
currentIndex++;
|
||||
numTokensConsumed++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// We looked and failed...
|
||||
return new PointcutBody(numTokensConsumed, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Match up args against unbound arguments of primitive types
|
||||
*/
|
||||
private void maybeBindPrimitiveArgsFromPointcutExpression() {
|
||||
int numUnboundPrimitives = countNumberOfUnboundPrimitiveArguments();
|
||||
if (numUnboundPrimitives > 1) {
|
||||
throw new AmbiguousBindingException("Found '" + numUnboundPrimitives +
|
||||
"' unbound primitive arguments with no way to distinguish between them.");
|
||||
}
|
||||
if (numUnboundPrimitives == 1) {
|
||||
// Look for arg variable and bind it if we find exactly one...
|
||||
List varNames = new ArrayList();
|
||||
String[] tokens = StringUtils.tokenizeToStringArray(this.pointcutExpression, " ");
|
||||
for (int i = 0; i < tokens.length; i++) {
|
||||
if (tokens[i].equals("args") || tokens[i].startsWith("args(")) {
|
||||
PointcutBody body = getPointcutBody(tokens, i);
|
||||
i += body.numTokensConsumed;
|
||||
maybeExtractVariableNamesFromArgs(body.text, varNames);
|
||||
}
|
||||
}
|
||||
if (varNames.size() > 1) {
|
||||
throw new AmbiguousBindingException("Found " + varNames.size() +
|
||||
" candidate variable names but only one candidate binding slot when matching primitive args");
|
||||
}
|
||||
else if (varNames.size() == 1) {
|
||||
// 1 primitive arg, and one candidate...
|
||||
for (int i = 0; i < this.argumentTypes.length; i++) {
|
||||
if (isUnbound(i) && this.argumentTypes[i].isPrimitive()) {
|
||||
bindParameterName(i, (String) varNames.get(0));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true if the parameter name binding for the given parameter
|
||||
* index has not yet been assigned.
|
||||
*/
|
||||
private boolean isUnbound(int i) {
|
||||
return this.parameterNameBindings[i] == null;
|
||||
}
|
||||
|
||||
private boolean alreadyBound(String varName) {
|
||||
for (int i = 0; i < this.parameterNameBindings.length; i++) {
|
||||
if (!isUnbound(i) && varName.equals(this.parameterNameBindings[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return <code>true</code> if the given argument type is a subclass
|
||||
* of the given supertype.
|
||||
*/
|
||||
private boolean isSubtypeOf(Class supertype, int argumentNumber) {
|
||||
return supertype.isAssignableFrom(this.argumentTypes[argumentNumber]);
|
||||
}
|
||||
|
||||
private int countNumberOfUnboundAnnotationArguments() {
|
||||
if (annotationClass == null) {
|
||||
// We're running on a JDK < 1.5
|
||||
return 0;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
for (int i = 0; i < this.argumentTypes.length; i++) {
|
||||
if (isUnbound(i) && isSubtypeOf(annotationClass, i)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private int countNumberOfUnboundPrimitiveArguments() {
|
||||
int count = 0;
|
||||
for (int i = 0; i < this.argumentTypes.length; i++) {
|
||||
if (isUnbound(i) && this.argumentTypes[i].isPrimitive()) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the argument index with the given type, and bind the given
|
||||
* <code>varName</code> in that position.
|
||||
*/
|
||||
private void findAndBind(Class argumentType, String varName) {
|
||||
for (int i = 0; i < this.argumentTypes.length; i++) {
|
||||
if (isUnbound(i) && isSubtypeOf(argumentType, i)) {
|
||||
bindParameterName(i, varName);
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("Expected to find an unbound argument of type '" +
|
||||
argumentType.getName() + "'");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Simple struct to hold the extracted text from a pointcut body, together
|
||||
* with the number of tokens consumed in extracting it.
|
||||
*/
|
||||
private static class PointcutBody {
|
||||
|
||||
private int numTokensConsumed;
|
||||
|
||||
private String text;
|
||||
|
||||
public PointcutBody(int tokens, String text) {
|
||||
this.numTokensConsumed = tokens;
|
||||
this.text = text;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Thrown in response to an ambiguous binding being detected when
|
||||
* trying to resolve a method's parameter names.
|
||||
*/
|
||||
public static class AmbiguousBindingException extends RuntimeException {
|
||||
|
||||
/**
|
||||
* Construct a new AmbiguousBindingException with the specified message.
|
||||
* @param msg the detail message
|
||||
*/
|
||||
public AmbiguousBindingException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import org.springframework.aop.AfterAdvice;
|
||||
|
||||
/**
|
||||
* Spring AOP advice wrapping an AspectJ after advice method.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @since 2.0
|
||||
*/
|
||||
public class AspectJAfterAdvice extends AbstractAspectJAdvice implements MethodInterceptor, AfterAdvice {
|
||||
|
||||
public AspectJAfterAdvice(
|
||||
Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {
|
||||
|
||||
super(aspectJBeforeAdviceMethod, pointcut, aif);
|
||||
}
|
||||
|
||||
public Object invoke(MethodInvocation mi) throws Throwable {
|
||||
try {
|
||||
return mi.proceed();
|
||||
}
|
||||
finally {
|
||||
invokeAdviceMethod(getJoinPointMatch(), null, null);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isBeforeAdvice() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isAfterAdvice() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import org.springframework.aop.AfterAdvice;
|
||||
import org.springframework.aop.AfterReturningAdvice;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.TypeUtils;
|
||||
|
||||
/**
|
||||
* Spring AOP advice wrapping an AspectJ after-returning advice method.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @author Ramnivas Laddad
|
||||
* @since 2.0
|
||||
*/
|
||||
public class AspectJAfterReturningAdvice extends AbstractAspectJAdvice implements AfterReturningAdvice, AfterAdvice {
|
||||
|
||||
public AspectJAfterReturningAdvice(
|
||||
Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {
|
||||
|
||||
super(aspectJBeforeAdviceMethod, pointcut, aif);
|
||||
}
|
||||
|
||||
public boolean isBeforeAdvice() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isAfterAdvice() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setReturningName(String name) {
|
||||
setReturningNameNoCheck(name);
|
||||
}
|
||||
|
||||
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
|
||||
if (shouldInvokeOnReturnValueOf(method, returnValue)) {
|
||||
invokeAdviceMethod(getJoinPointMatch(), returnValue, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Following AspectJ semantics, if a returning clause was specified, then the
|
||||
* advice is only invoked if the returned value is an instance of the given
|
||||
* returning type and generic type parameters, if any, match the assignment
|
||||
* rules. If the returning type is Object, the advice is *always* invoked.
|
||||
* @param returnValue the return value of the target method
|
||||
* @return whether to invoke the advice method for the given return value
|
||||
*/
|
||||
private boolean shouldInvokeOnReturnValueOf(Method method, Object returnValue) {
|
||||
Class type = getDiscoveredReturningType();
|
||||
Object genericType = getDiscoveredReturningGenericType();
|
||||
// If we aren't dealing with a raw type, check if generic parameters are assignable.
|
||||
return (ClassUtils.isAssignableValue(type, returnValue) &&
|
||||
(genericType == null || genericType == type || GenericTypeMatcher.isAssignable(genericType, method)));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inner class to avoid a static JDK 1.5 dependency for generic type matching.
|
||||
*/
|
||||
private static class GenericTypeMatcher {
|
||||
|
||||
public static boolean isAssignable(Object genericType, Method method) {
|
||||
return TypeUtils.isAssignable((Type) genericType, method.getGenericReturnType());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import org.springframework.aop.AfterAdvice;
|
||||
|
||||
/**
|
||||
* Spring AOP advice wrapping an AspectJ after-throwing advice method.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @since 2.0
|
||||
*/
|
||||
public class AspectJAfterThrowingAdvice extends AbstractAspectJAdvice implements MethodInterceptor, AfterAdvice {
|
||||
|
||||
public AspectJAfterThrowingAdvice(
|
||||
Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {
|
||||
|
||||
super(aspectJBeforeAdviceMethod, pointcut, aif);
|
||||
}
|
||||
|
||||
public boolean isBeforeAdvice() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isAfterAdvice() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setThrowingName(String name) {
|
||||
setThrowingNameNoCheck(name);
|
||||
}
|
||||
|
||||
public Object invoke(MethodInvocation mi) throws Throwable {
|
||||
try {
|
||||
return mi.proceed();
|
||||
}
|
||||
catch (Throwable t) {
|
||||
if (shouldInvokeOnThrowing(t)) {
|
||||
invokeAdviceMethod(getJoinPointMatch(), null, t);
|
||||
}
|
||||
throw t;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* In AspectJ semantics, after throwing advice that specifies a throwing clause
|
||||
* is only invoked if the thrown exception is a subtype of the given throwing type.
|
||||
*/
|
||||
private boolean shouldInvokeOnThrowing(Throwable t) {
|
||||
Class throwingType = getDiscoveredThrowingType();
|
||||
return throwingType.isAssignableFrom(t.getClass());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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;
|
||||
|
||||
import org.aopalliance.aop.Advice;
|
||||
|
||||
import org.springframework.aop.Advisor;
|
||||
import org.springframework.aop.AfterAdvice;
|
||||
import org.springframework.aop.BeforeAdvice;
|
||||
|
||||
/**
|
||||
* Utility methods for dealing with AspectJ advisors.
|
||||
*
|
||||
* @author Adrian Colyer
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
public abstract class AspectJAopUtils {
|
||||
|
||||
/**
|
||||
* Return <code>true</code> if the advisor is a form of before advice.
|
||||
*/
|
||||
public static boolean isBeforeAdvice(Advisor anAdvisor) {
|
||||
AspectJPrecedenceInformation precedenceInfo = getAspectJPrecedenceInformationFor(anAdvisor);
|
||||
if (precedenceInfo != null) {
|
||||
return precedenceInfo.isBeforeAdvice();
|
||||
}
|
||||
return (anAdvisor.getAdvice() instanceof BeforeAdvice);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return <code>true</code> if the advisor is a form of after advice.
|
||||
*/
|
||||
public static boolean isAfterAdvice(Advisor anAdvisor) {
|
||||
AspectJPrecedenceInformation precedenceInfo = getAspectJPrecedenceInformationFor(anAdvisor);
|
||||
if (precedenceInfo != null) {
|
||||
return precedenceInfo.isAfterAdvice();
|
||||
}
|
||||
return (anAdvisor.getAdvice() instanceof AfterAdvice);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the AspectJPrecedenceInformation provided by this advisor or its advice.
|
||||
* If neither the advisor nor the advice have precedence information, this method
|
||||
* will return <code>null</code>.
|
||||
*/
|
||||
public static AspectJPrecedenceInformation getAspectJPrecedenceInformationFor(Advisor anAdvisor) {
|
||||
if (anAdvisor instanceof AspectJPrecedenceInformation) {
|
||||
return (AspectJPrecedenceInformation) anAdvisor;
|
||||
}
|
||||
Advice advice = anAdvisor.getAdvice();
|
||||
if (advice instanceof AspectJPrecedenceInformation) {
|
||||
return (AspectJPrecedenceInformation) advice;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.weaver.tools.JoinPointMatch;
|
||||
|
||||
import org.springframework.aop.ProxyMethodInvocation;
|
||||
|
||||
/**
|
||||
* Spring AOP around advice (MethodInterceptor) that wraps
|
||||
* an AspectJ advice method. Exposes ProceedingJoinPoint.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
public class AspectJAroundAdvice extends AbstractAspectJAdvice implements MethodInterceptor {
|
||||
|
||||
public AspectJAroundAdvice(
|
||||
Method aspectJAroundAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {
|
||||
|
||||
super(aspectJAroundAdviceMethod, pointcut, aif);
|
||||
}
|
||||
|
||||
public boolean isBeforeAdvice() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isAfterAdvice() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean supportsProceedingJoinPoint() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public Object invoke(MethodInvocation mi) throws Throwable {
|
||||
if (!(mi instanceof ProxyMethodInvocation)) {
|
||||
throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
|
||||
}
|
||||
ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
|
||||
ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
|
||||
JoinPointMatch jpm = getJoinPointMatch(pmi);
|
||||
return invokeAdviceMethod(pjp, jpm, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ProceedingJoinPoint for the current invocation,
|
||||
* instantiating it lazily if it hasn't been bound to the thread already.
|
||||
* @param rmi the current Spring AOP ReflectiveMethodInvocation,
|
||||
* which we'll use for attribute binding
|
||||
* @return the ProceedingJoinPoint to make available to advice methods
|
||||
*/
|
||||
protected ProceedingJoinPoint lazyGetProceedingJoinPoint(ProxyMethodInvocation rmi) {
|
||||
return new MethodInvocationProceedingJoinPoint(rmi);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,530 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.aspectj.weaver.BCException;
|
||||
import org.aspectj.weaver.patterns.NamePattern;
|
||||
import org.aspectj.weaver.reflect.ReflectionWorld;
|
||||
import org.aspectj.weaver.tools.ContextBasedMatcher;
|
||||
import org.aspectj.weaver.tools.FuzzyBoolean;
|
||||
import org.aspectj.weaver.tools.JoinPointMatch;
|
||||
import org.aspectj.weaver.tools.MatchingContext;
|
||||
import org.aspectj.weaver.tools.PointcutDesignatorHandler;
|
||||
import org.aspectj.weaver.tools.PointcutExpression;
|
||||
import org.aspectj.weaver.tools.PointcutParameter;
|
||||
import org.aspectj.weaver.tools.PointcutParser;
|
||||
import org.aspectj.weaver.tools.PointcutPrimitive;
|
||||
import org.aspectj.weaver.tools.ShadowMatch;
|
||||
|
||||
import org.springframework.aop.ClassFilter;
|
||||
import org.springframework.aop.IntroductionAwareMethodMatcher;
|
||||
import org.springframework.aop.MethodMatcher;
|
||||
import org.springframework.aop.ProxyMethodInvocation;
|
||||
import org.springframework.aop.framework.autoproxy.ProxyCreationContext;
|
||||
import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
|
||||
import org.springframework.aop.support.AbstractExpressionPointcut;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Spring {@link org.springframework.aop.Pointcut} implementation
|
||||
* that uses the AspectJ weaver to evaluate a pointcut expression.
|
||||
*
|
||||
* <p>The pointcut expression value is an AspectJ expression. This can
|
||||
* reference other pointcuts and use composition and other operations.
|
||||
*
|
||||
* <p>Naturally, as this is to be processed by Spring AOP's proxy-based model,
|
||||
* only method execution pointcuts are supported.
|
||||
*
|
||||
* @author Rob Harrop
|
||||
* @author Adrian Colyer
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @author Ramnivas Laddad
|
||||
* @since 2.0
|
||||
*/
|
||||
public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
implements ClassFilter, IntroductionAwareMethodMatcher, BeanFactoryAware {
|
||||
|
||||
private static final Set DEFAULT_SUPPORTED_PRIMITIVES = new HashSet();
|
||||
|
||||
static {
|
||||
DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.EXECUTION);
|
||||
DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.ARGS);
|
||||
DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.REFERENCE);
|
||||
DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.THIS);
|
||||
DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.TARGET);
|
||||
DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.WITHIN);
|
||||
DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ANNOTATION);
|
||||
DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_WITHIN);
|
||||
DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ARGS);
|
||||
DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_TARGET);
|
||||
}
|
||||
|
||||
|
||||
private static final Log logger = LogFactory.getLog(AspectJExpressionPointcut.class);
|
||||
|
||||
private final Map shadowMapCache = new HashMap();
|
||||
|
||||
private PointcutParser pointcutParser;
|
||||
|
||||
private Class pointcutDeclarationScope;
|
||||
|
||||
private String[] pointcutParameterNames = new String[0];
|
||||
|
||||
private Class[] pointcutParameterTypes = new Class[0];
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
private PointcutExpression pointcutExpression;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new default AspectJExpressionPointcut.
|
||||
*/
|
||||
public AspectJExpressionPointcut() {
|
||||
this(DEFAULT_SUPPORTED_PRIMITIVES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new AspectJExpressionPointcut with the given supported primitives.
|
||||
* @param supportedPrimitives Set of {@link org.aspectj.weaver.tools.PointcutPrimitive}
|
||||
* instances
|
||||
*/
|
||||
public AspectJExpressionPointcut(Set supportedPrimitives) {
|
||||
this.pointcutParser =
|
||||
PointcutParser.getPointcutParserSupportingSpecifiedPrimitivesAndUsingContextClassloaderForResolution(
|
||||
supportedPrimitives);
|
||||
this.pointcutParser.registerPointcutDesignatorHandler(new BeanNamePointcutDesignatorHandler());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new AspectJExpressionPointcut with the given settings.
|
||||
* @param declarationScope the declaration scope for the pointcut
|
||||
* @param paramNames the parameter names for the pointcut
|
||||
* @param paramTypes the parameter types for the pointcut
|
||||
*/
|
||||
public AspectJExpressionPointcut(Class declarationScope, String[] paramNames, Class[] paramTypes) {
|
||||
this(DEFAULT_SUPPORTED_PRIMITIVES);
|
||||
this.pointcutDeclarationScope = declarationScope;
|
||||
if (paramNames.length != paramTypes.length) {
|
||||
throw new IllegalStateException(
|
||||
"Number of pointcut parameter names must match number of pointcut parameter types");
|
||||
}
|
||||
this.pointcutParameterNames = paramNames;
|
||||
this.pointcutParameterTypes = paramTypes;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the declaration scope for the pointcut.
|
||||
*/
|
||||
public void setPointcutDeclarationScope(Class pointcutDeclarationScope) {
|
||||
this.pointcutDeclarationScope = pointcutDeclarationScope;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the parameter names for the pointcut.
|
||||
*/
|
||||
public void setParameterNames(String[] names) {
|
||||
this.pointcutParameterNames = names;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the parameter types for the pointcut.
|
||||
*/
|
||||
public void setParameterTypes(Class[] types) {
|
||||
this.pointcutParameterTypes = types;
|
||||
}
|
||||
|
||||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
|
||||
public ClassFilter getClassFilter() {
|
||||
checkReadyToMatch();
|
||||
return this;
|
||||
}
|
||||
|
||||
public MethodMatcher getMethodMatcher() {
|
||||
checkReadyToMatch();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check whether this pointcut is ready to match,
|
||||
* lazily building the underlying AspectJ pointcut expression.
|
||||
*/
|
||||
private void checkReadyToMatch() {
|
||||
if (getExpression() == null) {
|
||||
throw new IllegalStateException("Must set property 'expression' before attempting to match");
|
||||
}
|
||||
if (this.pointcutExpression == null) {
|
||||
this.pointcutExpression = buildPointcutExpression();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the underlying AspectJ pointcut expression.
|
||||
*/
|
||||
private PointcutExpression buildPointcutExpression() {
|
||||
PointcutParameter[] pointcutParameters = new PointcutParameter[this.pointcutParameterNames.length];
|
||||
for (int i = 0; i < pointcutParameters.length; i++) {
|
||||
pointcutParameters[i] = this.pointcutParser.createPointcutParameter(
|
||||
this.pointcutParameterNames[i], this.pointcutParameterTypes[i]);
|
||||
}
|
||||
return this.pointcutParser.parsePointcutExpression(
|
||||
replaceBooleanOperators(getExpression()), this.pointcutDeclarationScope, pointcutParameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* If a pointcut expression has been specified in XML, the user cannot
|
||||
* write <code>and</code> as "&&" (though && will work).
|
||||
* We also allow <code>and</code> between two pointcut sub-expressions.
|
||||
* <p>This method converts back to <code>&&</code> for the AspectJ pointcut parser.
|
||||
*/
|
||||
private String replaceBooleanOperators(String pcExpr) {
|
||||
pcExpr = StringUtils.replace(pcExpr," and "," && ");
|
||||
pcExpr = StringUtils.replace(pcExpr, " or ", " || ");
|
||||
pcExpr = StringUtils.replace(pcExpr, " not ", " ! ");
|
||||
return pcExpr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the underlying AspectJ pointcut expression.
|
||||
*/
|
||||
public PointcutExpression getPointcutExpression() {
|
||||
checkReadyToMatch();
|
||||
return this.pointcutExpression;
|
||||
}
|
||||
|
||||
|
||||
public boolean matches(Class targetClass) {
|
||||
checkReadyToMatch();
|
||||
try {
|
||||
return this.pointcutExpression.couldMatchJoinPointsInType(targetClass);
|
||||
}
|
||||
catch (BCException ex) {
|
||||
logger.debug("PointcutExpression matching rejected target class", ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean matches(Method method, Class targetClass, boolean beanHasIntroductions) {
|
||||
checkReadyToMatch();
|
||||
Method targetMethod = AopUtils.getMostSpecificMethod(method, targetClass);
|
||||
ShadowMatch shadowMatch = null;
|
||||
try {
|
||||
shadowMatch = getShadowMatch(targetMethod, method);
|
||||
}
|
||||
catch (ReflectionWorld.ReflectionWorldException ex) {
|
||||
// Could neither introspect the target class nor the proxy class ->
|
||||
// let's simply consider this method as non-matching.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Special handling for this, target, @this, @target, @annotation
|
||||
// in Spring - we can optimize since we know we have exactly this class,
|
||||
// and there will never be matching subclass at runtime.
|
||||
if (shadowMatch.alwaysMatches()) {
|
||||
return true;
|
||||
}
|
||||
else if (shadowMatch.neverMatches()) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
// the maybe case
|
||||
return (beanHasIntroductions || matchesIgnoringSubtypes(shadowMatch) || matchesTarget(shadowMatch, targetClass));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean matches(Method method, Class targetClass) {
|
||||
return matches(method, targetClass, false);
|
||||
}
|
||||
|
||||
public boolean isRuntime() {
|
||||
checkReadyToMatch();
|
||||
return this.pointcutExpression.mayNeedDynamicTest();
|
||||
}
|
||||
|
||||
public boolean matches(Method method, Class targetClass, Object[] args) {
|
||||
checkReadyToMatch();
|
||||
ShadowMatch shadowMatch = null;
|
||||
ShadowMatch originalShadowMatch = null;
|
||||
try {
|
||||
shadowMatch = getShadowMatch(AopUtils.getMostSpecificMethod(method, targetClass), method);
|
||||
originalShadowMatch = getShadowMatch(method, method);
|
||||
}
|
||||
catch (ReflectionWorld.ReflectionWorldException ex) {
|
||||
// Could neither introspect the target class nor the proxy class ->
|
||||
// let's simply consider this method as non-matching.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Bind Spring AOP proxy to AspectJ "this" and Spring AOP target to AspectJ target,
|
||||
// consistent with return of MethodInvocationProceedingJoinPoint
|
||||
ProxyMethodInvocation pmi = null;
|
||||
Object targetObject = null;
|
||||
Object thisObject = null;
|
||||
try {
|
||||
MethodInvocation mi = ExposeInvocationInterceptor.currentInvocation();
|
||||
targetObject = mi.getThis();
|
||||
if (!(mi instanceof ProxyMethodInvocation)) {
|
||||
throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
|
||||
}
|
||||
pmi = (ProxyMethodInvocation) mi;
|
||||
thisObject = pmi.getProxy();
|
||||
}
|
||||
catch (IllegalStateException ex) {
|
||||
// No current invocation...
|
||||
// TODO: Should we really proceed here?
|
||||
logger.debug("Couldn't access current invocation - matching with limited context: " + ex);
|
||||
}
|
||||
|
||||
JoinPointMatch joinPointMatch = shadowMatch.matchesJoinPoint(thisObject, targetObject, args);
|
||||
|
||||
/*
|
||||
* Do a final check to see if any this(TYPE) kind of residue match. For
|
||||
* this purpose, we use the original method's (proxy method's) shadow to
|
||||
* ensure that 'this' is correctly checked against. Without this check,
|
||||
* we get incorrect match on this(TYPE) where TYPE matches the target
|
||||
* type but not 'this' (as would be the case of JDK dynamic proxies).
|
||||
* <p>See SPR-2979 for the original bug.
|
||||
*/
|
||||
if (pmi != null) { // there is a current invocation
|
||||
RuntimeTestWalker originalMethodResidueTest = new RuntimeTestWalker(originalShadowMatch);
|
||||
if (!originalMethodResidueTest.testThisInstanceOfResidue(thisObject.getClass())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (joinPointMatch.matches() && pmi != null) {
|
||||
bindParameters(pmi, joinPointMatch);
|
||||
}
|
||||
return joinPointMatch.matches();
|
||||
}
|
||||
|
||||
|
||||
protected String getCurrentProxiedBeanName() {
|
||||
return ProxyCreationContext.getCurrentProxiedBeanName();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A match test returned maybe - if there are any subtype sensitive variables
|
||||
* involved in the test (this, target, at_this, at_target, at_annotation) then
|
||||
* we say this is not a match as in Spring there will never be a different
|
||||
* runtime subtype.
|
||||
*/
|
||||
private boolean matchesIgnoringSubtypes(ShadowMatch shadowMatch) {
|
||||
return !(new RuntimeTestWalker(shadowMatch).testsSubtypeSensitiveVars());
|
||||
}
|
||||
|
||||
private boolean matchesTarget(ShadowMatch shadowMatch, Class targetClass) {
|
||||
return new RuntimeTestWalker(shadowMatch).testTargetInstanceOfResidue(targetClass);
|
||||
}
|
||||
|
||||
private void bindParameters(ProxyMethodInvocation invocation, JoinPointMatch jpm) {
|
||||
// Note: Can't use JoinPointMatch.getClass().getName() as the key, since
|
||||
// Spring AOP does all the matching at a join point, and then all the invocations
|
||||
// under this scenario, if we just use JoinPointMatch as the key, then
|
||||
// 'last man wins' which is not what we want at all.
|
||||
// Using the expression is guaranteed to be safe, since 2 identical expressions
|
||||
// are guaranteed to bind in exactly the same way.
|
||||
invocation.setUserAttribute(getExpression(), jpm);
|
||||
}
|
||||
|
||||
private ShadowMatch getShadowMatch(Method targetMethod, Method originalMethod) {
|
||||
synchronized (this.shadowMapCache) {
|
||||
ShadowMatch shadowMatch = (ShadowMatch) this.shadowMapCache.get(targetMethod);
|
||||
if (shadowMatch == null) {
|
||||
try {
|
||||
shadowMatch = this.pointcutExpression.matchesMethodExecution(targetMethod);
|
||||
}
|
||||
catch (ReflectionWorld.ReflectionWorldException ex) {
|
||||
// Failed to introspect target method, probably because it has been loaded
|
||||
// in a special ClassLoader. Let's try the original method instead...
|
||||
if (targetMethod == originalMethod) {
|
||||
throw ex;
|
||||
}
|
||||
shadowMatch = this.pointcutExpression.matchesMethodExecution(originalMethod);
|
||||
}
|
||||
this.shadowMapCache.put(targetMethod, shadowMatch);
|
||||
}
|
||||
return shadowMatch;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof AspectJExpressionPointcut)) {
|
||||
return false;
|
||||
}
|
||||
AspectJExpressionPointcut otherPc = (AspectJExpressionPointcut) other;
|
||||
return ObjectUtils.nullSafeEquals(this.getExpression(), otherPc.getExpression()) &&
|
||||
ObjectUtils.nullSafeEquals(this.pointcutDeclarationScope, otherPc.pointcutDeclarationScope) &&
|
||||
ObjectUtils.nullSafeEquals(this.pointcutParameterNames, otherPc.pointcutParameterNames) &&
|
||||
ObjectUtils.nullSafeEquals(this.pointcutParameterTypes, otherPc.pointcutParameterTypes);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int hashCode = ObjectUtils.nullSafeHashCode(this.getExpression());
|
||||
hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.pointcutDeclarationScope);
|
||||
hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.pointcutParameterNames);
|
||||
hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.pointcutParameterTypes);
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append("AspectJExpressionPointcut: ");
|
||||
if (this.pointcutParameterNames != null && this.pointcutParameterTypes != null) {
|
||||
sb.append("(");
|
||||
for (int i = 0; i < this.pointcutParameterTypes.length; i++) {
|
||||
sb.append(this.pointcutParameterTypes[i].getName());
|
||||
sb.append(" ");
|
||||
sb.append(this.pointcutParameterNames[i]);
|
||||
if ((i+1) < this.pointcutParameterTypes.length) {
|
||||
sb.append(", ");
|
||||
}
|
||||
}
|
||||
sb.append(")");
|
||||
}
|
||||
sb.append(" ");
|
||||
if (getExpression() != null) {
|
||||
sb.append(getExpression());
|
||||
}
|
||||
else {
|
||||
sb.append("<pointcut expression not set>");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handler for the Spring-specific <code>bean()</code> pointcut designator
|
||||
* extension to AspectJ.
|
||||
* <p>This handler must be added to each pointcut object that needs to
|
||||
* handle the <code>bean()</code> PCD. Matching context is obtained
|
||||
* automatically by examining a thread local variable and therefore a matching
|
||||
* context need not be set on the pointcut.
|
||||
*/
|
||||
private class BeanNamePointcutDesignatorHandler implements PointcutDesignatorHandler {
|
||||
|
||||
private static final String BEAN_DESIGNATOR_NAME = "bean";
|
||||
|
||||
public String getDesignatorName() {
|
||||
return BEAN_DESIGNATOR_NAME;
|
||||
}
|
||||
|
||||
public ContextBasedMatcher parse(String expression) {
|
||||
return new BeanNameContextMatcher(expression);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Matcher class for the BeanNamePointcutDesignatorHandler.
|
||||
*
|
||||
* <p>Dynamic match tests for this matcher always return true,
|
||||
* since the matching decision is made at the proxy creation time.
|
||||
* For static match tests, this matcher abstains to allow the overall
|
||||
* pointcut to match even when negation is used with the bean() poitncut.
|
||||
*/
|
||||
private class BeanNameContextMatcher implements ContextBasedMatcher {
|
||||
|
||||
private final NamePattern expressionPattern;
|
||||
|
||||
public BeanNameContextMatcher(String expression) {
|
||||
this.expressionPattern = new NamePattern(expression);
|
||||
}
|
||||
|
||||
public boolean couldMatchJoinPointsInType(Class someClass) {
|
||||
return (contextMatch(someClass) == FuzzyBoolean.YES);
|
||||
}
|
||||
|
||||
public boolean couldMatchJoinPointsInType(Class someClass, MatchingContext context) {
|
||||
return (contextMatch(someClass) == FuzzyBoolean.YES);
|
||||
}
|
||||
|
||||
public boolean matchesDynamically(MatchingContext context) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public FuzzyBoolean matchesStatically(MatchingContext context) {
|
||||
return contextMatch(null);
|
||||
}
|
||||
|
||||
public boolean mayNeedDynamicTest() {
|
||||
return false;
|
||||
}
|
||||
|
||||
private FuzzyBoolean contextMatch(Class targetType) {
|
||||
String advisedBeanName = getCurrentProxiedBeanName();
|
||||
if (advisedBeanName == null) { // no proxy creation in progress
|
||||
// abstain; can't return YES, since that will make pointcut with negation fail
|
||||
return FuzzyBoolean.MAYBE;
|
||||
}
|
||||
if (BeanFactoryUtils.isGeneratedBeanName(advisedBeanName)) {
|
||||
return FuzzyBoolean.NO;
|
||||
}
|
||||
if (targetType != null) {
|
||||
boolean isFactory = FactoryBean.class.isAssignableFrom(targetType);
|
||||
return FuzzyBoolean.fromBoolean(
|
||||
matchesBeanName(isFactory ? BeanFactory.FACTORY_BEAN_PREFIX + advisedBeanName : advisedBeanName));
|
||||
}
|
||||
else {
|
||||
return FuzzyBoolean.fromBoolean(matchesBeanName(advisedBeanName) ||
|
||||
matchesBeanName(BeanFactory.FACTORY_BEAN_PREFIX + advisedBeanName));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean matchesBeanName(String advisedBeanName) {
|
||||
if (this.expressionPattern.matches(advisedBeanName)) {
|
||||
return true;
|
||||
}
|
||||
if (beanFactory != null) {
|
||||
String[] aliases = beanFactory.getAliases(advisedBeanName);
|
||||
for (int i = 0; i < aliases.length; i++) {
|
||||
if (this.expressionPattern.matches(aliases[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2002-2006 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;
|
||||
|
||||
import org.springframework.aop.Pointcut;
|
||||
import org.springframework.aop.support.AbstractGenericPointcutAdvisor;
|
||||
|
||||
/**
|
||||
* Spring AOP Advisor that can be used for any AspectJ pointcut expression.
|
||||
*
|
||||
* @author Rob Harrop
|
||||
* @since 2.0
|
||||
*/
|
||||
public class AspectJExpressionPointcutAdvisor extends AbstractGenericPointcutAdvisor {
|
||||
|
||||
private final AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
|
||||
|
||||
|
||||
public Pointcut getPointcut() {
|
||||
return this.pointcut;
|
||||
}
|
||||
|
||||
public void setExpression(String expression) {
|
||||
this.pointcut.setExpression(expression);
|
||||
}
|
||||
|
||||
public void setLocation(String location) {
|
||||
this.pointcut.setLocation(location);
|
||||
}
|
||||
|
||||
public void setParameterTypes(Class[] types) {
|
||||
this.pointcut.setParameterTypes(types);
|
||||
}
|
||||
|
||||
public void setParameterNames(String[] names) {
|
||||
this.pointcut.setParameterNames(names);
|
||||
}
|
||||
|
||||
public String getLocation() {
|
||||
return this.pointcut.getLocation();
|
||||
}
|
||||
|
||||
public String getExpression() {
|
||||
return this.pointcut.getExpression();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2002-2006 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;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.aop.MethodBeforeAdvice;
|
||||
|
||||
/**
|
||||
* Spring AOP advice that wraps an AspectJ before method.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Adrian Colyer
|
||||
* @since 2.0
|
||||
*/
|
||||
public class AspectJMethodBeforeAdvice extends AbstractAspectJAdvice implements MethodBeforeAdvice {
|
||||
|
||||
public AspectJMethodBeforeAdvice(
|
||||
Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {
|
||||
|
||||
super(aspectJBeforeAdviceMethod, pointcut, aif);
|
||||
}
|
||||
|
||||
public void before(Method method, Object[] args, Object target) throws Throwable {
|
||||
invokeAdviceMethod(getJoinPointMatch(), null, null);
|
||||
}
|
||||
|
||||
public boolean isBeforeAdvice() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isAfterAdvice() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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;
|
||||
|
||||
import org.aopalliance.aop.Advice;
|
||||
|
||||
import org.springframework.aop.Pointcut;
|
||||
import org.springframework.aop.PointcutAdvisor;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* AspectJPointcutAdvisor that adapts an {@link AbstractAspectJAdvice}
|
||||
* to the {@link org.springframework.aop.PointcutAdvisor} interface.
|
||||
*
|
||||
* @author Adrian Colyer
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
public class AspectJPointcutAdvisor implements PointcutAdvisor, Ordered {
|
||||
|
||||
private final AbstractAspectJAdvice advice;
|
||||
|
||||
private final Pointcut pointcut;
|
||||
|
||||
private Integer order;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new AspectJPointcutAdvisor for the given advice
|
||||
* @param advice the AbstractAspectJAdvice to wrap
|
||||
*/
|
||||
public AspectJPointcutAdvisor(AbstractAspectJAdvice advice) {
|
||||
Assert.notNull(advice, "Advice must not be null");
|
||||
this.advice = advice;
|
||||
this.pointcut = advice.buildSafePointcut();
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = new Integer(order);
|
||||
}
|
||||
|
||||
|
||||
public boolean isPerInstance() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public Advice getAdvice() {
|
||||
return this.advice;
|
||||
}
|
||||
|
||||
public Pointcut getPointcut() {
|
||||
return this.pointcut;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
if (this.order != null) {
|
||||
return this.order.intValue();
|
||||
}
|
||||
else {
|
||||
return this.advice.getOrder();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof AspectJPointcutAdvisor)) {
|
||||
return false;
|
||||
}
|
||||
AspectJPointcutAdvisor otherAdvisor = (AspectJPointcutAdvisor) other;
|
||||
return (ObjectUtils.nullSafeEquals(this.advice, otherAdvisor.advice));
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return AspectJPointcutAdvisor.class.hashCode();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2002-2006 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;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
/**
|
||||
* Interface to be implemented by types that can supply the information
|
||||
* needed to sort advice/advisors by AspectJ's precedence rules.
|
||||
*
|
||||
* @author Adrian Colyer
|
||||
* @since 2.0
|
||||
* @see org.springframework.aop.aspectj.autoproxy.AspectJPrecedenceComparator
|
||||
*/
|
||||
public interface AspectJPrecedenceInformation extends Ordered {
|
||||
|
||||
// Implementation note:
|
||||
// We need the level of indirection this interface provides as otherwise the
|
||||
// AspectJPrecedenceComparator must ask an Advisor for its Advice in all cases
|
||||
// in order to sort advisors. This causes problems with the
|
||||
// InstantiationModelAwarePointcutAdvisor which needs to delay creating
|
||||
// its advice for aspects with non-singleton instantiation models.
|
||||
|
||||
/**
|
||||
* The name of the aspect (bean) in which the advice was declared.
|
||||
*/
|
||||
String getAspectName();
|
||||
|
||||
/**
|
||||
* The declaration order of the advice member within the aspect.
|
||||
*/
|
||||
int getDeclarationOrder();
|
||||
|
||||
/**
|
||||
* Return whether this is a before advice.
|
||||
*/
|
||||
boolean isBeforeAdvice();
|
||||
|
||||
/**
|
||||
* Return whether this is an after advice.
|
||||
*/
|
||||
boolean isAfterAdvice();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.aop.Advisor;
|
||||
import org.springframework.aop.PointcutAdvisor;
|
||||
import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
|
||||
|
||||
/**
|
||||
* Utility methods for working with AspectJ proxies.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Ramnivas Laddad
|
||||
* @since 2.0
|
||||
*/
|
||||
public abstract class AspectJProxyUtils {
|
||||
|
||||
/**
|
||||
* Add special advisors if necessary to work with a proxy chain that contains AspectJ advisors.
|
||||
* This will expose the current Spring AOP invocation (necessary for some AspectJ pointcut matching)
|
||||
* and make available the current AspectJ JoinPoint. The call will have no effect if there are no
|
||||
* AspectJ advisors in the advisor chain.
|
||||
* @param advisors Advisors available
|
||||
* @return <code>true</code> if any special {@link Advisor Advisors} were added, otherwise <code>false</code>.
|
||||
*/
|
||||
public static boolean makeAdvisorChainAspectJCapableIfNecessary(List advisors) {
|
||||
// Don't add advisors to an empty list; may indicate that proxying is just not required
|
||||
if (!advisors.isEmpty()) {
|
||||
boolean foundAspectJAdvice = false;
|
||||
for (Iterator it = advisors.iterator(); it.hasNext() && !foundAspectJAdvice; ) {
|
||||
Advisor advisor = (Advisor) it.next();
|
||||
// Be careful not to get the Advice without a guard, as
|
||||
// this might eagerly instantiate a non-singleton AspectJ aspect
|
||||
if (isAspectJAdvice(advisor)) {
|
||||
foundAspectJAdvice = true;
|
||||
}
|
||||
}
|
||||
if (foundAspectJAdvice && !advisors.contains(ExposeInvocationInterceptor.ADVISOR)) {
|
||||
advisors.add(0, ExposeInvocationInterceptor.ADVISOR);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the given Advisor contains an AspectJ advice.
|
||||
* @param advisor the Advisor to check
|
||||
*/
|
||||
private static boolean isAspectJAdvice(Advisor advisor) {
|
||||
return (advisor instanceof InstantiationModelAwarePointcutAdvisor ||
|
||||
advisor.getAdvice() instanceof AbstractAspectJAdvice ||
|
||||
(advisor instanceof PointcutAdvisor &&
|
||||
((PointcutAdvisor) advisor).getPointcut() instanceof AspectJExpressionPointcut));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.aspectj.bridge.AbortException;
|
||||
import org.aspectj.bridge.IMessage;
|
||||
import org.aspectj.bridge.IMessage.Kind;
|
||||
import org.aspectj.bridge.IMessageHandler;
|
||||
|
||||
/**
|
||||
* Implementation of AspectJ's {@link IMessageHandler} interface that
|
||||
* routes AspectJ weaving messages through the same logging system as the
|
||||
* regular Spring messages.
|
||||
*
|
||||
* <p>Pass the option...
|
||||
*
|
||||
* <p><code class="code">-XmessageHandlerClass:org.springframework.aop.aspectj.AspectJWeaverMessageHandler</code>
|
||||
*
|
||||
* <p>to the weaver; for example, specifying the following in a
|
||||
* "<code>META-INF/aop.xml</code> file:
|
||||
*
|
||||
* <p><code class="code"><weaver options="..."/></code>
|
||||
*
|
||||
* @author Adrian Colyer
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
public class AspectJWeaverMessageHandler implements IMessageHandler {
|
||||
|
||||
private static final String AJ_ID = "[AspectJ] ";
|
||||
|
||||
private static final Log LOGGER = LogFactory.getLog("AspectJ Weaver");
|
||||
|
||||
|
||||
public boolean handleMessage(IMessage message) throws AbortException {
|
||||
Kind messageKind = message.getKind();
|
||||
|
||||
if (LOGGER.isDebugEnabled() || LOGGER.isTraceEnabled()) {
|
||||
if (messageKind == IMessage.DEBUG) {
|
||||
LOGGER.debug(makeMessageFor(message));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (LOGGER.isInfoEnabled()) {
|
||||
if ((messageKind == IMessage.INFO) || (messageKind == IMessage.WEAVEINFO)) {
|
||||
LOGGER.info(makeMessageFor(message));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (LOGGER.isWarnEnabled()) {
|
||||
if (messageKind == IMessage.WARNING) {
|
||||
LOGGER.warn(makeMessageFor(message));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (LOGGER.isErrorEnabled()) {
|
||||
if (messageKind == IMessage.ERROR) {
|
||||
LOGGER.error(makeMessageFor(message));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (LOGGER.isFatalEnabled()) {
|
||||
if (messageKind == IMessage.ABORT) {
|
||||
LOGGER.fatal(makeMessageFor(message));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private String makeMessageFor(IMessage aMessage) {
|
||||
return AJ_ID + aMessage.getMessage();
|
||||
}
|
||||
|
||||
public boolean isIgnoring(Kind messageKind) {
|
||||
// We want to see everything, and allow configuration of log levels dynamically.
|
||||
return false;
|
||||
}
|
||||
|
||||
public void dontIgnore(Kind messageKind) {
|
||||
// We weren't ignoring anything anyway...
|
||||
}
|
||||
|
||||
public void ignore(Kind kind) {
|
||||
// We weren't ignoring anything anyway...
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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;
|
||||
|
||||
import org.aopalliance.aop.Advice;
|
||||
|
||||
import org.springframework.aop.ClassFilter;
|
||||
import org.springframework.aop.IntroductionAdvisor;
|
||||
import org.springframework.aop.support.ClassFilters;
|
||||
import org.springframework.aop.support.DelegatePerTargetObjectIntroductionInterceptor;
|
||||
import org.springframework.aop.support.DelegatingIntroductionInterceptor;
|
||||
|
||||
/**
|
||||
* Introduction advisor delegating to the given object.
|
||||
* Implements AspectJ annotation-style behavior for the DeclareParents annotation.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Ramnivas Laddad
|
||||
* @since 2.0
|
||||
*/
|
||||
public class DeclareParentsAdvisor implements IntroductionAdvisor {
|
||||
|
||||
private final Class introducedInterface;
|
||||
|
||||
private final ClassFilter typePatternClassFilter;
|
||||
|
||||
private final Advice advice;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new advisor for this DeclareParents field.
|
||||
* @param interfaceType static field defining the introduction
|
||||
* @param typePattern type pattern the introduction is restricted to
|
||||
* @param defaultImpl the default implementation class
|
||||
*/
|
||||
public DeclareParentsAdvisor(Class interfaceType, String typePattern, Class defaultImpl) {
|
||||
this(interfaceType, typePattern, defaultImpl,
|
||||
new DelegatePerTargetObjectIntroductionInterceptor(defaultImpl, interfaceType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new advisor for this DeclareParents field.
|
||||
* @param interfaceType static field defining the introduction
|
||||
* @param typePattern type pattern the introduction is restricted to
|
||||
* @param delegateRef the delegate implementation object
|
||||
*/
|
||||
public DeclareParentsAdvisor(Class interfaceType, String typePattern, Object delegateRef) {
|
||||
this(interfaceType, typePattern, delegateRef.getClass(),
|
||||
new DelegatingIntroductionInterceptor(delegateRef));
|
||||
}
|
||||
|
||||
/**
|
||||
* Private constructor to share common code between impl-based delegate and reference-based delegate
|
||||
* (cannot use method such as init() to share common code, due the the use of final fields)
|
||||
* @param interfaceType static field defining the introduction
|
||||
* @param typePattern type pattern the introduction is restricted to
|
||||
* @param implementationClass implementation class
|
||||
* @param advice delegation advice
|
||||
*/
|
||||
private DeclareParentsAdvisor(Class interfaceType, String typePattern, Class implementationClass, Advice advice) {
|
||||
this.introducedInterface = interfaceType;
|
||||
ClassFilter typePatternFilter = new TypePatternClassFilter(typePattern);
|
||||
|
||||
// Excludes methods implemented.
|
||||
ClassFilter exclusion = new ClassFilter() {
|
||||
public boolean matches(Class clazz) {
|
||||
return !(introducedInterface.isAssignableFrom(clazz));
|
||||
}
|
||||
};
|
||||
|
||||
this.typePatternClassFilter = ClassFilters.intersection(typePatternFilter, exclusion);
|
||||
this.advice = advice;
|
||||
}
|
||||
|
||||
|
||||
public ClassFilter getClassFilter() {
|
||||
return this.typePatternClassFilter;
|
||||
}
|
||||
|
||||
public void validateInterfaces() throws IllegalArgumentException {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
public boolean isPerInstance() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public Advice getAdvice() {
|
||||
return this.advice;
|
||||
}
|
||||
|
||||
public Class[] getInterfaces() {
|
||||
return new Class[] {this.introducedInterface};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2002-2006 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;
|
||||
|
||||
import org.springframework.aop.PointcutAdvisor;
|
||||
|
||||
/**
|
||||
* Interface to be implemented by Spring AOP Advisors wrapping AspectJ
|
||||
* aspects that may have a lazy initialization strategy. For example,
|
||||
* a perThis instantiation model would mean lazy initialization of the advice.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface InstantiationModelAwarePointcutAdvisor extends PointcutAdvisor {
|
||||
|
||||
/**
|
||||
* Return whether this advisor is lazily initializing its underlying advice.
|
||||
*/
|
||||
boolean isLazy();
|
||||
|
||||
/**
|
||||
* Return whether this advisor has already instantiated its advice.
|
||||
*/
|
||||
boolean isAdviceInstantiated();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.Signature;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.aspectj.lang.reflect.SourceLocation;
|
||||
import org.aspectj.runtime.internal.AroundClosure;
|
||||
|
||||
import org.springframework.aop.ProxyMethodInvocation;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Implementation of AspectJ ProceedingJoinPoint interface
|
||||
* wrapping an AOP Alliance MethodInvocation.
|
||||
*
|
||||
* <p><b>Note</b>: the <code>getThis()</code> method returns the current Spring AOP proxy.
|
||||
* The <code>getTarget()</code> method returns the current Spring AOP target (which may be
|
||||
* <code>null</code> if there is no target), and is a plain POJO without any advice.
|
||||
* <b>If you want to call the object and have the advice take effect, use
|
||||
* <code>getThis()</code>.</b> A common example is casting the object to an
|
||||
* introduced interface in the implementation of an introduction.
|
||||
*
|
||||
* <p>Of course there is no such distinction between target and proxy in AspectJ.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @author Adrian Colyer
|
||||
* @since 2.0
|
||||
*/
|
||||
public class MethodInvocationProceedingJoinPoint implements ProceedingJoinPoint, JoinPoint.StaticPart {
|
||||
|
||||
private final ProxyMethodInvocation methodInvocation;
|
||||
|
||||
private Object[] defensiveCopyOfArgs;
|
||||
|
||||
/** Lazily initialized signature object */
|
||||
private Signature signature;
|
||||
|
||||
/** Lazily initialized source location object */
|
||||
private SourceLocation sourceLocation;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new MethodInvocationProceedingJoinPoint, wrapping the given
|
||||
* Spring ProxyMethodInvocation object.
|
||||
* @param methodInvocation the Spring ProxyMethodInvocation object
|
||||
*/
|
||||
public MethodInvocationProceedingJoinPoint(ProxyMethodInvocation methodInvocation) {
|
||||
Assert.notNull(methodInvocation, "MethodInvocation must not be null");
|
||||
this.methodInvocation = methodInvocation;
|
||||
}
|
||||
|
||||
public void set$AroundClosure(AroundClosure aroundClosure) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public Object proceed() throws Throwable {
|
||||
return this.methodInvocation.invocableClone().proceed();
|
||||
}
|
||||
|
||||
public Object proceed(Object[] arguments) throws Throwable {
|
||||
Assert.notNull(arguments, "Argument array passed to proceed cannot be null");
|
||||
if (arguments.length != this.methodInvocation.getArguments().length) {
|
||||
throw new IllegalArgumentException("Expecting " +
|
||||
this.methodInvocation.getArguments().length + " arguments to proceed, " +
|
||||
"but was passed " + arguments.length + " arguments");
|
||||
}
|
||||
this.methodInvocation.setArguments(arguments);
|
||||
return this.methodInvocation.invocableClone(arguments).proceed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Spring AOP proxy. Cannot be <code>null</code>.
|
||||
*/
|
||||
public Object getThis() {
|
||||
return this.methodInvocation.getProxy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Spring AOP target. May be <code>null</code> if there is no target.
|
||||
*/
|
||||
public Object getTarget() {
|
||||
return this.methodInvocation.getThis();
|
||||
}
|
||||
|
||||
public Object[] getArgs() {
|
||||
if (this.defensiveCopyOfArgs == null) {
|
||||
Object[] argsSource = this.methodInvocation.getArguments();
|
||||
this.defensiveCopyOfArgs = new Object[argsSource.length];
|
||||
System.arraycopy(argsSource, 0, this.defensiveCopyOfArgs, 0, argsSource.length);
|
||||
}
|
||||
return this.defensiveCopyOfArgs;
|
||||
}
|
||||
|
||||
public Signature getSignature() {
|
||||
if (this.signature == null) {
|
||||
this.signature = new MethodSignatureImpl();
|
||||
}
|
||||
return signature;
|
||||
}
|
||||
|
||||
public SourceLocation getSourceLocation() {
|
||||
if (this.sourceLocation == null) {
|
||||
this.sourceLocation = new SourceLocationImpl();
|
||||
}
|
||||
return this.sourceLocation;
|
||||
}
|
||||
|
||||
public String getKind() {
|
||||
return ProceedingJoinPoint.METHOD_EXECUTION;
|
||||
}
|
||||
|
||||
public JoinPoint.StaticPart getStaticPart() {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public String toShortString() {
|
||||
return "execution(" + this.methodInvocation.getMethod().getName() + ")";
|
||||
}
|
||||
|
||||
public String toLongString() {
|
||||
return getClass().getName() + ": execution: [" + this.methodInvocation + "]";
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return getClass().getName() + ": " + toShortString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Lazily initialized MethodSignature.
|
||||
*/
|
||||
private class MethodSignatureImpl implements Signature, MethodSignature {
|
||||
|
||||
public String toShortString() {
|
||||
return methodInvocation.getMethod().getName();
|
||||
}
|
||||
|
||||
public String toLongString() {
|
||||
return methodInvocation.getMethod().toString();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return methodInvocation.getMethod().getName();
|
||||
}
|
||||
|
||||
public int getModifiers() {
|
||||
return methodInvocation.getMethod().getModifiers();
|
||||
}
|
||||
|
||||
public Class getDeclaringType() {
|
||||
return methodInvocation.getMethod().getDeclaringClass();
|
||||
}
|
||||
|
||||
public String getDeclaringTypeName() {
|
||||
return methodInvocation.getMethod().getDeclaringClass().getName();
|
||||
}
|
||||
|
||||
public Class getReturnType() {
|
||||
return methodInvocation.getMethod().getReturnType();
|
||||
}
|
||||
|
||||
public Method getMethod() {
|
||||
return methodInvocation.getMethod();
|
||||
}
|
||||
|
||||
public Class[] getParameterTypes() {
|
||||
return methodInvocation.getMethod().getParameterTypes();
|
||||
}
|
||||
|
||||
public String[] getParameterNames() {
|
||||
// TODO consider allowing use of ParameterNameDiscoverer, or tying into
|
||||
// parameter names exposed for argument binding...
|
||||
throw new UnsupportedOperationException(
|
||||
"Parameter names cannot be determined unless compiled by AspectJ compiler");
|
||||
}
|
||||
|
||||
public Class[] getExceptionTypes() {
|
||||
return methodInvocation.getMethod().getExceptionTypes();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Lazily initialized SourceLocation.
|
||||
*/
|
||||
private class SourceLocationImpl implements SourceLocation {
|
||||
|
||||
public Class getWithinType() {
|
||||
if (methodInvocation.getThis() == null) {
|
||||
throw new UnsupportedOperationException("No source location joinpoint available: target is null");
|
||||
}
|
||||
return methodInvocation.getThis().getClass();
|
||||
}
|
||||
|
||||
public String getFileName() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public int getLine() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public int getColumn() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import org.aspectj.weaver.ResolvedType;
|
||||
import org.aspectj.weaver.ast.And;
|
||||
import org.aspectj.weaver.ast.Call;
|
||||
import org.aspectj.weaver.ast.FieldGetCall;
|
||||
import org.aspectj.weaver.ast.HasAnnotation;
|
||||
import org.aspectj.weaver.ast.ITestVisitor;
|
||||
import org.aspectj.weaver.ast.Instanceof;
|
||||
import org.aspectj.weaver.ast.Literal;
|
||||
import org.aspectj.weaver.ast.Not;
|
||||
import org.aspectj.weaver.ast.Or;
|
||||
import org.aspectj.weaver.ast.Test;
|
||||
import org.aspectj.weaver.internal.tools.MatchingContextBasedTest;
|
||||
import org.aspectj.weaver.reflect.ReflectionVar;
|
||||
import org.aspectj.weaver.reflect.ShadowMatchImpl;
|
||||
import org.aspectj.weaver.tools.ShadowMatch;
|
||||
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* This class encapsulates some AspectJ internal knowledge that should be
|
||||
* pushed back into the AspectJ project in a future release.
|
||||
*
|
||||
* <p>It relies on implementation specific knowledge in AspectJ to break
|
||||
* encapsulation and do something AspectJ was not designed to do: query
|
||||
* the types of runtime tests that will be performed. The code here should
|
||||
* migrate to <code>ShadowMatch.getVariablesInvolvedInRuntimeTest()</code>
|
||||
* or some similar operation.
|
||||
*
|
||||
* <p>See <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=151593"/>.
|
||||
*
|
||||
* @author Adrian Colyer
|
||||
* @author Ramnivas Laddad
|
||||
* @since 2.0
|
||||
*/
|
||||
class RuntimeTestWalker {
|
||||
|
||||
private final Test runtimeTest;
|
||||
|
||||
|
||||
public RuntimeTestWalker(ShadowMatch shadowMatch) {
|
||||
ShadowMatchImpl shadowMatchImplementation = (ShadowMatchImpl) shadowMatch;
|
||||
try {
|
||||
Field testField = shadowMatchImplementation.getClass().getDeclaredField("residualTest");
|
||||
ReflectionUtils.makeAccessible(testField);
|
||||
this.runtimeTest = (Test) testField.get(shadowMatch);
|
||||
}
|
||||
catch (NoSuchFieldException noSuchFieldEx) {
|
||||
throw new IllegalStateException("The version of aspectjtools.jar / aspectjweaver.jar " +
|
||||
"on the classpath is incompatible with this version of Spring: Expected field " +
|
||||
"'runtimeTest' is not present on ShadowMatchImpl class.");
|
||||
}
|
||||
catch (IllegalAccessException illegalAccessEx) {
|
||||
// Famous last words... but I don't see how this can happen given the
|
||||
// makeAccessible call above
|
||||
throw new IllegalStateException("Unable to access ShadowMatchImpl.runtimeTest field.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If the test uses any of the this, target, at_this, at_target, and at_annotation vars,
|
||||
* then it tests subtype sensitive vars.
|
||||
*/
|
||||
public boolean testsSubtypeSensitiveVars() {
|
||||
return new SubtypeSensitiveVarTypeTestVisitor().testsSubtypeSensitiveVars(this.runtimeTest);
|
||||
}
|
||||
|
||||
public boolean testThisInstanceOfResidue(Class thisClass) {
|
||||
return new ThisInstanceOfResidueTestVisitor(thisClass).thisInstanceOfMatches(this.runtimeTest);
|
||||
}
|
||||
|
||||
public boolean testTargetInstanceOfResidue(Class targetClass) {
|
||||
return new TargetInstanceOfResidueTestVisitor(targetClass).targetInstanceOfMatches(this.runtimeTest);
|
||||
}
|
||||
|
||||
|
||||
private static class TestVisitorAdapter implements ITestVisitor {
|
||||
|
||||
protected static final int THIS_VAR = 0;
|
||||
protected static final int TARGET_VAR = 1;
|
||||
protected static final int AT_THIS_VAR = 3;
|
||||
protected static final int AT_TARGET_VAR = 4;
|
||||
protected static final int AT_ANNOTATION_VAR = 8;
|
||||
|
||||
public void visit(And e) {
|
||||
e.getLeft().accept(this);
|
||||
e.getRight().accept(this);
|
||||
}
|
||||
|
||||
public void visit(Or e) {
|
||||
e.getLeft().accept(this);
|
||||
e.getRight().accept(this);
|
||||
}
|
||||
|
||||
public void visit(Not e) {
|
||||
e.getBody().accept(this);
|
||||
}
|
||||
|
||||
public void visit(Instanceof i) {
|
||||
}
|
||||
|
||||
public void visit(Literal literal) {
|
||||
}
|
||||
|
||||
public void visit(Call call) {
|
||||
}
|
||||
|
||||
public void visit(FieldGetCall fieldGetCall) {
|
||||
}
|
||||
|
||||
public void visit(HasAnnotation hasAnnotation) {
|
||||
}
|
||||
|
||||
public void visit(MatchingContextBasedTest matchingContextTest) {
|
||||
}
|
||||
|
||||
protected int getVarType(ReflectionVar v) {
|
||||
try {
|
||||
Field varTypeField = ReflectionVar.class.getDeclaredField("varType");
|
||||
ReflectionUtils.makeAccessible(varTypeField);
|
||||
Integer varTypeValue = (Integer) varTypeField.get(v);
|
||||
return varTypeValue.intValue();
|
||||
}
|
||||
catch (NoSuchFieldException noSuchFieldEx) {
|
||||
throw new IllegalStateException("the version of aspectjtools.jar / aspectjweaver.jar " +
|
||||
"on the classpath is incompatible with this version of Spring:- expected field " +
|
||||
"'varType' is not present on ReflectionVar class");
|
||||
}
|
||||
catch (IllegalAccessException illegalAccessEx) {
|
||||
// Famous last words... but I don't see how this can happen given the
|
||||
// makeAccessible call above
|
||||
throw new IllegalStateException("Unable to access ReflectionVar.varType field.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static abstract class InstanceOfResidueTestVisitor extends TestVisitorAdapter {
|
||||
|
||||
private Class matchClass;
|
||||
private boolean matches;
|
||||
private int matchVarType;
|
||||
|
||||
public InstanceOfResidueTestVisitor(Class matchClass, boolean defaultMatches, int matchVarType) {
|
||||
this.matchClass = matchClass;
|
||||
this.matches = defaultMatches;
|
||||
this.matchVarType = matchVarType;
|
||||
}
|
||||
|
||||
public boolean instanceOfMatches(Test test) {
|
||||
test.accept(this);
|
||||
return matches;
|
||||
}
|
||||
|
||||
public void visit(Instanceof i) {
|
||||
ResolvedType type = (ResolvedType) i.getType();
|
||||
int varType = getVarType((ReflectionVar) i.getVar());
|
||||
if (varType != this.matchVarType) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Class typeClass = ClassUtils.forName(type.getName(), this.matchClass.getClassLoader());
|
||||
// Don't use ReflectionType.isAssignableFrom() as it won't be aware of (Spring) mixins
|
||||
this.matches = typeClass.isAssignableFrom(this.matchClass);
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
this.matches = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if residue of target(TYPE) kind. See SPR-3783 for more details.
|
||||
*/
|
||||
private static class TargetInstanceOfResidueTestVisitor extends InstanceOfResidueTestVisitor {
|
||||
|
||||
public TargetInstanceOfResidueTestVisitor(Class targetClass) {
|
||||
super(targetClass, false, TARGET_VAR);
|
||||
}
|
||||
|
||||
public boolean targetInstanceOfMatches(Test test) {
|
||||
return instanceOfMatches(test);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if residue of this(TYPE) kind. See SPR-2979 for more details.
|
||||
*/
|
||||
private static class ThisInstanceOfResidueTestVisitor extends InstanceOfResidueTestVisitor {
|
||||
|
||||
public ThisInstanceOfResidueTestVisitor(Class thisClass) {
|
||||
super(thisClass, true, THIS_VAR);
|
||||
}
|
||||
|
||||
// TODO: Optimization: Process only if this() specifies a type and not an identifier.
|
||||
public boolean thisInstanceOfMatches(Test test) {
|
||||
return instanceOfMatches(test);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class SubtypeSensitiveVarTypeTestVisitor extends TestVisitorAdapter {
|
||||
|
||||
private final Object thisObj = new Object();
|
||||
private final Object targetObj = new Object();
|
||||
private final Object[] argsObjs = new Object[0];
|
||||
private boolean testsSubtypeSensitiveVars = false;
|
||||
|
||||
public boolean testsSubtypeSensitiveVars(Test aTest) {
|
||||
aTest.accept(this);
|
||||
return this.testsSubtypeSensitiveVars;
|
||||
}
|
||||
|
||||
public void visit(Instanceof i) {
|
||||
ReflectionVar v = (ReflectionVar) i.getVar();
|
||||
Object varUnderTest = v.getBindingAtJoinPoint(thisObj,targetObj,argsObjs);
|
||||
if ((varUnderTest == thisObj) || (varUnderTest == targetObj)) {
|
||||
this.testsSubtypeSensitiveVars = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void visit(HasAnnotation hasAnn) {
|
||||
// If you thought things were bad before, now we sink to new levels of horror...
|
||||
ReflectionVar v = (ReflectionVar) hasAnn.getVar();
|
||||
int varType = getVarType(v);
|
||||
if ((varType == AT_THIS_VAR) || (varType == AT_TARGET_VAR) || (varType == AT_ANNOTATION_VAR)) {
|
||||
this.testsSubtypeSensitiveVars = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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;
|
||||
|
||||
import org.springframework.aop.framework.AopConfigException;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Implementation of {@link AspectInstanceFactory} that creates a new instance
|
||||
* of the specified aspect class for every {@link #getAspectInstance()} call.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0.4
|
||||
*/
|
||||
public class SimpleAspectInstanceFactory implements AspectInstanceFactory {
|
||||
|
||||
private final Class aspectClass;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new SimpleAspectInstanceFactory for the given aspect class.
|
||||
* @param aspectClass the aspect class
|
||||
*/
|
||||
public SimpleAspectInstanceFactory(Class aspectClass) {
|
||||
Assert.notNull(aspectClass, "Aspect class must not be null");
|
||||
this.aspectClass = aspectClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the specified aspect class (never <code>null</code>).
|
||||
*/
|
||||
public final Class getAspectClass() {
|
||||
return this.aspectClass;
|
||||
}
|
||||
|
||||
|
||||
public final Object getAspectInstance() {
|
||||
try {
|
||||
return this.aspectClass.newInstance();
|
||||
}
|
||||
catch (InstantiationException ex) {
|
||||
throw new AopConfigException("Unable to instantiate aspect class [" + this.aspectClass.getName() + "]", ex);
|
||||
}
|
||||
catch (IllegalAccessException ex) {
|
||||
throw new AopConfigException("Cannot access element class [" + this.aspectClass.getName() + "]", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public ClassLoader getAspectClassLoader() {
|
||||
return this.aspectClass.getClassLoader();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the order for this factory's aspect instance,
|
||||
* either an instance-specific order expressed through implementing
|
||||
* the {@link org.springframework.core.Ordered} interface,
|
||||
* or a fallback order.
|
||||
* @see org.springframework.core.Ordered
|
||||
* @see #getOrderForAspectClass
|
||||
*/
|
||||
public int getOrder() {
|
||||
return getOrderForAspectClass(this.aspectClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine a fallback order for the case that the aspect instance
|
||||
* does not express an instance-specific order through implementing
|
||||
* the {@link org.springframework.core.Ordered} interface.
|
||||
* <p>The default implementation simply returns <code>Ordered.LOWEST_PRECEDENCE</code>.
|
||||
* @param aspectClass the aspect class
|
||||
*/
|
||||
protected int getOrderForAspectClass(Class aspectClass) {
|
||||
return Ordered.LOWEST_PRECEDENCE;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Implementation of {@link AspectInstanceFactory} that is backed by a
|
||||
* specified singleton object, returning the same instance for every
|
||||
* {@link #getAspectInstance()} call.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see SimpleAspectInstanceFactory
|
||||
*/
|
||||
public class SingletonAspectInstanceFactory implements AspectInstanceFactory {
|
||||
|
||||
private final Object aspectInstance;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new SingletonAspectInstanceFactory for the given aspect instance.
|
||||
* @param aspectInstance the singleton aspect instance
|
||||
*/
|
||||
public SingletonAspectInstanceFactory(Object aspectInstance) {
|
||||
Assert.notNull(aspectInstance, "Aspect instance must not be null");
|
||||
this.aspectInstance = aspectInstance;
|
||||
}
|
||||
|
||||
|
||||
public final Object getAspectInstance() {
|
||||
return this.aspectInstance;
|
||||
}
|
||||
|
||||
public ClassLoader getAspectClassLoader() {
|
||||
return this.aspectInstance.getClass().getClassLoader();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the order for this factory's aspect instance,
|
||||
* either an instance-specific order expressed through implementing
|
||||
* the {@link org.springframework.core.Ordered} interface,
|
||||
* or a fallback order.
|
||||
* @see org.springframework.core.Ordered
|
||||
* @see #getOrderForAspectClass
|
||||
*/
|
||||
public int getOrder() {
|
||||
if (this.aspectInstance instanceof Ordered) {
|
||||
return ((Ordered) this.aspectInstance).getOrder();
|
||||
}
|
||||
return getOrderForAspectClass(this.aspectInstance.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine a fallback order for the case that the aspect instance
|
||||
* does not express an instance-specific order through implementing
|
||||
* the {@link org.springframework.core.Ordered} interface.
|
||||
* <p>The default implementation simply returns <code>Ordered.LOWEST_PRECEDENCE</code>.
|
||||
* @param aspectClass the aspect class
|
||||
*/
|
||||
protected int getOrderForAspectClass(Class aspectClass) {
|
||||
return Ordered.LOWEST_PRECEDENCE;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright 2002-2006 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;
|
||||
|
||||
import org.aspectj.weaver.tools.PointcutParser;
|
||||
import org.aspectj.weaver.tools.TypePatternMatcher;
|
||||
|
||||
import org.springframework.aop.ClassFilter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Spring AOP {@link ClassFilter} implementation using AspectJ type matching.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @since 2.0
|
||||
*/
|
||||
public class TypePatternClassFilter implements ClassFilter {
|
||||
|
||||
private String typePattern;
|
||||
|
||||
private TypePatternMatcher aspectJTypePatternMatcher;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new instance of the {@link TypePatternClassFilter} class.
|
||||
* <p>This is the JavaBean constructor; be sure to set the
|
||||
* {@link #setTypePattern(String) typePattern} property, else a
|
||||
* no doubt fatal {@link IllegalStateException} will be thrown
|
||||
* when the {@link #matches(Class)} method is first invoked.
|
||||
*/
|
||||
public TypePatternClassFilter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a fully configured {@link TypePatternClassFilter} using the
|
||||
* given type pattern.
|
||||
* @param typePattern the type pattern that AspectJ weaver should parse
|
||||
* @throws IllegalArgumentException if the supplied <code>typePattern</code> is <code>null</code>
|
||||
* or is recognized as invalid
|
||||
*/
|
||||
public TypePatternClassFilter(String typePattern) {
|
||||
setTypePattern(typePattern);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the AspectJ type pattern to match.
|
||||
* <p>Examples include:
|
||||
* <code class="code">
|
||||
* org.springframework.beans.*
|
||||
* </code>
|
||||
* This will match any class or interface in the given package.
|
||||
* <code class="code">
|
||||
* org.springframework.beans.ITestBean+
|
||||
* </code>
|
||||
* This will match the <code>ITestBean</code> interface and any class
|
||||
* that implements it.
|
||||
* <p>These conventions are established by AspectJ, not Spring AOP.
|
||||
* @param typePattern the type pattern that AspectJ weaver should parse
|
||||
* @throws IllegalArgumentException if the supplied <code>typePattern</code> is <code>null</code>
|
||||
* or is recognized as invalid
|
||||
*/
|
||||
public void setTypePattern(String typePattern) {
|
||||
Assert.notNull(typePattern);
|
||||
this.typePattern = typePattern;
|
||||
this.aspectJTypePatternMatcher =
|
||||
PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution().
|
||||
parseTypePattern(replaceBooleanOperators(typePattern));
|
||||
}
|
||||
|
||||
public String getTypePattern() {
|
||||
return typePattern;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should the pointcut apply to the given interface or target class?
|
||||
* @param clazz candidate target class
|
||||
* @return whether the advice should apply to this candidate target class
|
||||
* @throws IllegalStateException if no {@link #setTypePattern(String)} has been set
|
||||
*/
|
||||
public boolean matches(Class clazz) {
|
||||
if (this.aspectJTypePatternMatcher == null) {
|
||||
throw new IllegalStateException("No 'typePattern' has been set via ctor/setter.");
|
||||
}
|
||||
return this.aspectJTypePatternMatcher.matches(clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* If a type pattern has been specified in XML, the user cannot
|
||||
* write <code>and</code> as "&&" (though && will work).
|
||||
* We also allow <code>and</code> between two sub-expressions.
|
||||
* <p>This method converts back to <code>&&</code> for the AspectJ pointcut parser.
|
||||
*/
|
||||
private String replaceBooleanOperators(String pcExpr) {
|
||||
pcExpr = StringUtils.replace(pcExpr," and "," && ");
|
||||
pcExpr = StringUtils.replace(pcExpr, " or ", " || ");
|
||||
pcExpr = StringUtils.replace(pcExpr, " not ", " ! ");
|
||||
return pcExpr;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,341 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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.aspectj.AspectJExpressionPointcut;
|
||||
import org.springframework.aop.framework.AopConfigException;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.core.PrioritizedParameterNameDiscoverer;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
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.
|
||||
*
|
||||
* <p>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 {
|
||||
|
||||
protected static final ParameterNameDiscoverer ASPECTJ_ANNOTATION_PARAMETER_NAME_DISCOVERER =
|
||||
new AspectJAnnotationParameterNameDiscoverer();
|
||||
|
||||
private static final String AJC_MAGIC = "ajc$";
|
||||
|
||||
|
||||
/**
|
||||
* Find and return the first AspectJ annotation on the given method
|
||||
* (there <i>should</i> only be one anyway...)
|
||||
*/
|
||||
protected static AspectJAnnotation findAspectJAnnotationOnMethod(Method aMethod) {
|
||||
Class<? extends Annotation>[] classesToLookFor = (Class<? extends Annotation>[]) new Class[] {
|
||||
Before.class,
|
||||
Around.class,
|
||||
After.class,
|
||||
AfterReturning.class,
|
||||
AfterThrowing.class,
|
||||
Pointcut.class
|
||||
};
|
||||
for (Class<? extends Annotation> c : classesToLookFor) {
|
||||
AspectJAnnotation foundAnnotation = findAnnotation(aMethod, c);
|
||||
if (foundAnnotation != null) {
|
||||
return foundAnnotation;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static <A extends Annotation> AspectJAnnotation<A> findAnnotation(Method method, Class<A> toLookFor) {
|
||||
A result = AnnotationUtils.findAnnotation(method, toLookFor);
|
||||
if (result != null) {
|
||||
return new AspectJAnnotation<A>(result);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Logger available to subclasses */
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
protected final ParameterNameDiscoverer parameterNameDiscoverer;
|
||||
|
||||
|
||||
protected AbstractAspectJAdvisorFactory() {
|
||||
PrioritizedParameterNameDiscoverer prioritizedParameterNameDiscoverer = new PrioritizedParameterNameDiscoverer();
|
||||
prioritizedParameterNameDiscoverer.addDiscoverer(ASPECTJ_ANNOTATION_PARAMETER_NAME_DISCOVERER);
|
||||
this.parameterNameDiscoverer = prioritizedParameterNameDiscoverer;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public boolean isAspect(Class<?> clazz) {
|
||||
return (AjTypeSystem.getAjType(clazz).isAspect() &&
|
||||
hasAspectAnnotation(clazz) && !compiledByAjc(clazz));
|
||||
}
|
||||
|
||||
private boolean hasAspectAnnotation(Class<?> clazz) {
|
||||
return clazz.isAnnotationPresent(Aspect.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
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.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The pointcut and advice annotations both have an "argNames" member which contains a
|
||||
* comma-separated list of the argument names. We use this (if non-empty) to build the
|
||||
* formal parameters for the pointcut.
|
||||
*/
|
||||
protected AspectJExpressionPointcut createPointcutExpression(
|
||||
Method annotatedMethod, Class declarationScope, String[] pointcutParameterNames) {
|
||||
|
||||
Class<?> [] pointcutParameterTypes = new Class<?>[0];
|
||||
if (pointcutParameterNames != null) {
|
||||
pointcutParameterTypes = extractPointcutParameterTypes(pointcutParameterNames,annotatedMethod);
|
||||
}
|
||||
|
||||
AspectJExpressionPointcut ajexp =
|
||||
new AspectJExpressionPointcut(declarationScope,pointcutParameterNames,pointcutParameterTypes);
|
||||
ajexp.setLocation(annotatedMethod.toString());
|
||||
return ajexp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the pointcut parameters needed by aspectj based on the given argument names
|
||||
* and the argument types that are available from the adviceMethod. Needs to take into
|
||||
* account (ignore) any JoinPoint based arguments as these are not pointcut context but
|
||||
* rather part of the advice execution context (thisJoinPoint, thisJoinPointStaticPart)
|
||||
*/
|
||||
private Class<?>[] extractPointcutParameterTypes(String[] argNames, Method adviceMethod) {
|
||||
Class<?>[] ret = new Class<?>[argNames.length];
|
||||
Class<?>[] paramTypes = adviceMethod.getParameterTypes();
|
||||
if (argNames.length > paramTypes.length) {
|
||||
throw new IllegalStateException("Expecting at least " + argNames.length +
|
||||
" arguments in the advice declaration, but only found " + paramTypes.length);
|
||||
}
|
||||
// Make the simplifying assumption for now that all of the JoinPoint based arguments
|
||||
// come first in the advice declaration.
|
||||
int typeOffset = paramTypes.length - argNames.length;
|
||||
for (int i = 0; i < ret.length; i++) {
|
||||
ret[i] = paramTypes[i+typeOffset];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
protected enum AspectJAnnotationType {
|
||||
AtPointcut,
|
||||
AtBefore,
|
||||
AtAfter,
|
||||
AtAfterReturning,
|
||||
AtAfterThrowing,
|
||||
AtAround
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Class modelling an AspectJ annotation, exposing its type enumeration and
|
||||
* pointcut String.
|
||||
*/
|
||||
protected static class AspectJAnnotation<A extends Annotation> {
|
||||
|
||||
private static Map<Class,AspectJAnnotationType> annotationTypes = new HashMap<Class,AspectJAnnotationType>();
|
||||
|
||||
private static final String[] EXPRESSION_PROPERTIES = new String[]{"value", "pointcut"};
|
||||
|
||||
static {
|
||||
annotationTypes.put(Pointcut.class,AspectJAnnotationType.AtPointcut);
|
||||
annotationTypes.put(After.class,AspectJAnnotationType.AtAfter);
|
||||
annotationTypes.put(AfterReturning.class,AspectJAnnotationType.AtAfterReturning);
|
||||
annotationTypes.put(AfterThrowing.class,AspectJAnnotationType.AtAfterThrowing);
|
||||
annotationTypes.put(Around.class,AspectJAnnotationType.AtAround);
|
||||
annotationTypes.put(Before.class,AspectJAnnotationType.AtBefore);
|
||||
}
|
||||
|
||||
private final A annotation;
|
||||
private AspectJAnnotationType annotationType;
|
||||
private final String expression;
|
||||
private final String argNames;
|
||||
|
||||
public AspectJAnnotation(A aspectjAnnotation) {
|
||||
this.annotation = aspectjAnnotation;
|
||||
for (Class type : annotationTypes.keySet()) {
|
||||
if (type.isInstance(this.annotation)) {
|
||||
this.annotationType = annotationTypes.get(type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (this.annotationType == null) {
|
||||
throw new IllegalStateException("Unknown annotation type: " + this.annotation.toString());
|
||||
}
|
||||
|
||||
// We know these methods exist with the same name on each object,
|
||||
// but need to invoke them reflectively as there isn't a common interface.
|
||||
try {
|
||||
this.expression = resolveExpression();
|
||||
this.argNames = (String)
|
||||
this.annotation.getClass().getMethod("argNames", (Class[]) null).invoke(this.annotation);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalArgumentException(aspectjAnnotation + " cannot be an AspectJ annotation", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private String resolveExpression() throws Exception {
|
||||
String expression = null;
|
||||
for (int i = 0; i < EXPRESSION_PROPERTIES.length; i++) {
|
||||
String methodName = EXPRESSION_PROPERTIES[i];
|
||||
Method method;
|
||||
try {
|
||||
method = this.annotation.getClass().getDeclaredMethod(methodName);
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
method = null;
|
||||
}
|
||||
if (method != null) {
|
||||
String candidate = (String) method.invoke(this.annotation);
|
||||
if (StringUtils.hasText(candidate)) {
|
||||
expression = candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
return expression;
|
||||
}
|
||||
|
||||
public AspectJAnnotationType getAnnotationType() {
|
||||
return this.annotationType;
|
||||
}
|
||||
|
||||
public A getAnnotation() {
|
||||
return this.annotation;
|
||||
}
|
||||
|
||||
public String getPointcutExpression() {
|
||||
return this.expression;
|
||||
}
|
||||
|
||||
public String getArgNames() {
|
||||
return this.argNames;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return this.annotation.toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ParameterNameDiscoverer implementation that analyzes the arg names
|
||||
* specified at the AspectJ annotation level.
|
||||
*/
|
||||
private static class AspectJAnnotationParameterNameDiscoverer implements ParameterNameDiscoverer {
|
||||
|
||||
public String[] getParameterNames(Method method) {
|
||||
if (method.getParameterTypes().length == 0) {
|
||||
return new String[0];
|
||||
}
|
||||
AspectJAnnotation annotation = findAspectJAnnotationOnMethod(method);
|
||||
if (annotation == null) {
|
||||
return null;
|
||||
}
|
||||
StringTokenizer strTok = new StringTokenizer(annotation.getArgNames(), ",");
|
||||
if (strTok.countTokens() > 0) {
|
||||
String[] names = new String[strTok.countTokens()];
|
||||
for (int i = 0; i < names.length; i++) {
|
||||
names[i] = strTok.nextToken();
|
||||
}
|
||||
return names;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public String[] getParameterNames(Constructor ctor) {
|
||||
throw new UnsupportedOperationException("Spring AOP cannot handle constructor advice");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link AspectJAwareAdvisorAutoProxyCreator} subclass that processes all AspectJ
|
||||
* annotation aspects in the current application context, as well as Spring Advisors.
|
||||
*
|
||||
* <p>Any AspectJ annotated classes will automatically be recognized, and their
|
||||
* advice applied if Spring AOP's proxy-based model is capable of applying it.
|
||||
* This covers method execution joinpoints.
|
||||
*
|
||||
* <p>If the <aop:include> element is used, only @AspectJ beans with names matched by
|
||||
* an include pattern will be considered as defining aspects to use for Spring auto-proxying.
|
||||
*
|
||||
* <p>Processing of Spring Advisors follows the rules established in
|
||||
* {@link org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator}.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see org.springframework.aop.aspectj.annotation.AspectJAdvisorFactory
|
||||
*/
|
||||
public class AnnotationAwareAspectJAutoProxyCreator extends AspectJAwareAdvisorAutoProxyCreator {
|
||||
|
||||
private List<Pattern> includePatterns;
|
||||
|
||||
private AspectJAdvisorFactory aspectJAdvisorFactory = new ReflectiveAspectJAdvisorFactory();
|
||||
|
||||
private BeanFactoryAspectJAdvisorsBuilder aspectJAdvisorsBuilder;
|
||||
|
||||
|
||||
/**
|
||||
* Set a list of regex patterns, matching eligible @AspectJ bean names.
|
||||
* <p>Default is to consider all @AspectJ beans as eligible.
|
||||
*/
|
||||
public void setIncludePatterns(List<String> patterns) {
|
||||
this.includePatterns = new ArrayList<Pattern>(patterns.size());
|
||||
for (String patternText : patterns) {
|
||||
this.includePatterns.add(Pattern.compile(patternText));
|
||||
}
|
||||
}
|
||||
|
||||
public void setAspectJAdvisorFactory(AspectJAdvisorFactory aspectJAdvisorFactory) {
|
||||
Assert.notNull(this.aspectJAdvisorFactory, "AspectJAdvisorFactory must not be null");
|
||||
this.aspectJAdvisorFactory = aspectJAdvisorFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {
|
||||
super.initBeanFactory(beanFactory);
|
||||
this.aspectJAdvisorsBuilder =
|
||||
new BeanFactoryAspectJAdvisorsBuilderAdapter(beanFactory, this.aspectJAdvisorFactory);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected List findCandidateAdvisors() {
|
||||
// Add all the Spring advisors found according to superclass rules.
|
||||
List advisors = super.findCandidateAdvisors();
|
||||
// Build Advisors for all AspectJ aspects in the bean factory.
|
||||
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
|
||||
return advisors;
|
||||
}
|
||||
|
||||
protected boolean isInfrastructureClass(Class beanClass) {
|
||||
// Previously we setProxyTargetClass(true) in the constructor, but that has too
|
||||
// broad an impact. Instead we now override isInfrastructureClass to avoid proxying
|
||||
// aspects. I'm not entirely happy with that as there is no good reason not
|
||||
// to advise aspects, except that it causes advice invocation to go through a
|
||||
// proxy, and if the aspect implements e.g the Ordered interface it will be
|
||||
// proxied by that interface and fail at runtime as the advice method is not
|
||||
// defined on the interface. We could potentially relax the restriction about
|
||||
// not advising aspects in the future.
|
||||
return (super.isInfrastructureClass(beanClass) || this.aspectJAdvisorFactory.isAspect(beanClass));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given aspect bean is eligible for auto-proxying.
|
||||
* <p>If no <aop:include> elements were used then "includePatterns" will be
|
||||
* <code>null</code> and all beans are included. If "includePatterns" is non-null,
|
||||
* then one of the patterns must match.
|
||||
*/
|
||||
protected boolean isEligibleAspectBean(String beanName) {
|
||||
if (this.includePatterns == null) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
for (Pattern pattern : this.includePatterns) {
|
||||
if (pattern.matcher(beanName).matches()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Subclass of BeanFactoryAspectJAdvisorsBuilderAdapter that delegates to
|
||||
* surrounding AnnotationAwareAspectJAutoProxyCreator facilities.
|
||||
*/
|
||||
private class BeanFactoryAspectJAdvisorsBuilderAdapter extends BeanFactoryAspectJAdvisorsBuilder {
|
||||
|
||||
public BeanFactoryAspectJAdvisorsBuilderAdapter(
|
||||
ListableBeanFactory beanFactory, AspectJAdvisorFactory advisorFactory) {
|
||||
super(beanFactory, advisorFactory);
|
||||
}
|
||||
|
||||
protected boolean isEligibleBean(String beanName) {
|
||||
return AnnotationAwareAspectJAutoProxyCreator.this.isEligibleAspectBean(beanName);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
import org.aopalliance.aop.Advice;
|
||||
|
||||
import org.springframework.aop.Advisor;
|
||||
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
|
||||
import org.springframework.aop.framework.AopConfigException;
|
||||
|
||||
/**
|
||||
* Interface for factories that can create Spring AOP Advisors from classes
|
||||
* annotated with AspectJ annotation syntax.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see AspectMetadata
|
||||
* @see org.aspectj.lang.reflect.AjTypeSystem
|
||||
*/
|
||||
public interface AspectJAdvisorFactory {
|
||||
|
||||
/**
|
||||
* Determine whether or not the given class is an aspect, as reported
|
||||
* by AspectJ's {@link org.aspectj.lang.reflect.AjTypeSystem}.
|
||||
* <p>Will simply return <code>false</code> if the supposed aspect is
|
||||
* invalid (such as an extension of a concrete aspect class).
|
||||
* Will return true for some aspects that Spring AOP cannot process,
|
||||
* such as those with unsupported instantiation models.
|
||||
* Use the {@link #validate} method to handle these cases if necessary.
|
||||
* @param clazz the supposed annotation-style AspectJ class
|
||||
* @return whether or not this class is recognized by AspectJ as an aspect class
|
||||
*/
|
||||
boolean isAspect(Class<?> clazz);
|
||||
|
||||
/**
|
||||
* Is the given class a valid AspectJ aspect class?
|
||||
* @param aspectClass the supposed AspectJ annotation-style class to validate
|
||||
* @throws AopConfigException if the class is an invalid aspect
|
||||
* (which can never be legal)
|
||||
* @throws NotAnAtAspectException if the class is not an aspect at all
|
||||
* (which may or may not be legal, depending on the context)
|
||||
*/
|
||||
void validate(Class<?> aspectClass) throws AopConfigException;
|
||||
|
||||
/**
|
||||
* Build Spring AOP Advisors for all annotated At-AspectJ methods
|
||||
* on the specified aspect instance.
|
||||
* @param aif the aspect instance factory (not the aspect instance itself
|
||||
* in order to avoid eager instantiation)
|
||||
* @return a list of advisors for this class
|
||||
*/
|
||||
List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aif);
|
||||
|
||||
/**
|
||||
* Build a Spring AOP Advisor for the given AspectJ advice method.
|
||||
* @param candidateAdviceMethod the candidate advice method
|
||||
* @param aif the aspect instance factory
|
||||
* @param declarationOrderInAspect the declaration order within the aspect
|
||||
* @param aspectName the name of the aspect
|
||||
* @return <code>null</code> if the method is not an AspectJ advice method
|
||||
* or if it is a pointcut that will be used by other advice but will not
|
||||
* create a Spring advice in its own right
|
||||
*/
|
||||
Advisor getAdvisor(Method candidateAdviceMethod,
|
||||
MetadataAwareAspectInstanceFactory aif, int declarationOrderInAspect, String aspectName);
|
||||
|
||||
/**
|
||||
* Build a Spring AOP Advice for the given AspectJ advice method.
|
||||
* @param candidateAdviceMethod the candidate advice method
|
||||
* @param pointcut the corresponding AspectJ expression pointcut
|
||||
* @param aif the aspect instance factory
|
||||
* @param declarationOrderInAspect the declaration order within the aspect
|
||||
* @param aspectName the name of the aspect
|
||||
* @return <code>null</code> if the method is not an AspectJ advice method
|
||||
* or if it is a pointcut that will be used by other advice but will not
|
||||
* create a Spring advice in its own right
|
||||
* @see org.springframework.aop.aspectj.AspectJAroundAdvice
|
||||
* @see org.springframework.aop.aspectj.AspectJMethodBeforeAdvice
|
||||
* @see org.springframework.aop.aspectj.AspectJAfterAdvice
|
||||
* @see org.springframework.aop.aspectj.AspectJAfterReturningAdvice
|
||||
* @see org.springframework.aop.aspectj.AspectJAfterThrowingAdvice
|
||||
*/
|
||||
Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut pointcut,
|
||||
MetadataAwareAspectInstanceFactory aif, int declarationOrderInAspect, String aspectName);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.aspectj.lang.reflect.PerClauseKind;
|
||||
|
||||
import org.springframework.aop.Advisor;
|
||||
import org.springframework.aop.aspectj.AspectJProxyUtils;
|
||||
import org.springframework.aop.framework.AopConfigException;
|
||||
import org.springframework.aop.framework.ProxyCreatorSupport;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* AspectJ-based proxy factory, allowing for programmatic building
|
||||
* of proxies which include AspectJ aspects (code style as well
|
||||
* Java 5 annotation style).
|
||||
*
|
||||
* @author Rob Harrop
|
||||
* @author Juergen Hoeller
|
||||
* @author Ramnivas Laddad
|
||||
* @since 2.0
|
||||
* @see #addAspect(Object)
|
||||
* @see #addAspect(Class)
|
||||
* @see #getProxy()
|
||||
* @see #getProxy(ClassLoader)
|
||||
* @see org.springframework.aop.framework.ProxyFactory
|
||||
*/
|
||||
public class AspectJProxyFactory extends ProxyCreatorSupport {
|
||||
|
||||
/** Cache for singleton aspect instances */
|
||||
private static final Map aspectCache = new HashMap();
|
||||
|
||||
private final AspectJAdvisorFactory aspectFactory = new ReflectiveAspectJAdvisorFactory();
|
||||
|
||||
|
||||
/**
|
||||
* Create a new AspectJProxyFactory.
|
||||
*/
|
||||
public AspectJProxyFactory() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new AspectJProxyFactory.
|
||||
* <p>Will proxy all interfaces that the given target implements.
|
||||
* @param target the target object to be proxied
|
||||
*/
|
||||
public AspectJProxyFactory(Object target) {
|
||||
Assert.notNull(target, "Target object must not be null");
|
||||
setInterfaces(ClassUtils.getAllInterfaces(target));
|
||||
setTarget(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new <code>AspectJProxyFactory</code>.
|
||||
* No target, only interfaces. Must add interceptors.
|
||||
*/
|
||||
public AspectJProxyFactory(Class[] interfaces) {
|
||||
setInterfaces(interfaces);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add the supplied aspect instance to the chain. The type of the aspect instance
|
||||
* supplied must be a singleton aspect. True singleton lifecycle is not honoured when
|
||||
* using this method - the caller is responsible for managing the lifecycle of any
|
||||
* aspects added in this way.
|
||||
* @param aspectInstance the AspectJ aspect instance
|
||||
*/
|
||||
public void addAspect(Object aspectInstance) {
|
||||
Class aspectClass = aspectInstance.getClass();
|
||||
String aspectName = aspectClass.getName();
|
||||
AspectMetadata am = createAspectMetadata(aspectClass, aspectName);
|
||||
if (am.getAjType().getPerClause().getKind() != PerClauseKind.SINGLETON) {
|
||||
throw new IllegalArgumentException(
|
||||
"Aspect class [" + aspectClass.getName() + "] does not define a singleton aspect");
|
||||
}
|
||||
addAdvisorsFromAspectInstanceFactory(
|
||||
new SingletonMetadataAwareAspectInstanceFactory(aspectInstance, aspectName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an aspect of the supplied type to the end of the advice chain.
|
||||
* @param aspectClass the AspectJ aspect class
|
||||
*/
|
||||
public void addAspect(Class aspectClass) {
|
||||
String aspectName = aspectClass.getName();
|
||||
AspectMetadata am = createAspectMetadata(aspectClass, aspectName);
|
||||
MetadataAwareAspectInstanceFactory instanceFactory = createAspectInstanceFactory(am, aspectClass, aspectName);
|
||||
addAdvisorsFromAspectInstanceFactory(instanceFactory);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add all {@link Advisor Advisors} from the supplied {@link MetadataAwareAspectInstanceFactory}
|
||||
* to the current chain. Exposes any special purpose {@link Advisor Advisors} if needed.
|
||||
* @see #makeAdvisorChainAspectJCapableIfNecessary()
|
||||
*/
|
||||
private void addAdvisorsFromAspectInstanceFactory(MetadataAwareAspectInstanceFactory instanceFactory) {
|
||||
List advisors = this.aspectFactory.getAdvisors(instanceFactory);
|
||||
advisors = AopUtils.findAdvisorsThatCanApply(advisors, getTargetClass());
|
||||
addAllAdvisors((Advisor[]) advisors.toArray(new Advisor[advisors.size()]));
|
||||
makeAdvisorChainAspectJCapableIfNecessary();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link AspectMetadata} instance for the supplied aspect type.
|
||||
*/
|
||||
private AspectMetadata createAspectMetadata(Class aspectClass, String aspectName) {
|
||||
AspectMetadata am = new AspectMetadata(aspectClass, aspectName);
|
||||
if (!am.getAjType().isAspect()) {
|
||||
throw new IllegalArgumentException("Class [" + aspectClass.getName() + "] is not a valid aspect type");
|
||||
}
|
||||
return am;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link MetadataAwareAspectInstanceFactory} for the supplied aspect type. If the aspect type
|
||||
* has no per clause, then a {@link SingletonMetadataAwareAspectInstanceFactory} is returned, otherwise
|
||||
* a {@link PrototypeAspectInstanceFactory} is returned.
|
||||
*/
|
||||
private MetadataAwareAspectInstanceFactory createAspectInstanceFactory(
|
||||
AspectMetadata am, Class aspectClass, String aspectName) {
|
||||
|
||||
MetadataAwareAspectInstanceFactory instanceFactory = null;
|
||||
if (am.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
|
||||
// Create a shared aspect instance.
|
||||
Object instance = getSingletonAspectInstance(aspectClass);
|
||||
instanceFactory = new SingletonMetadataAwareAspectInstanceFactory(instance, aspectName);
|
||||
}
|
||||
else {
|
||||
// Create a factory for independent aspect instances.
|
||||
instanceFactory = new SimpleMetadataAwareAspectInstanceFactory(aspectClass, aspectName);
|
||||
}
|
||||
return instanceFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add any special-purpose {@link Advisor Advisors} needed for AspectJ support
|
||||
* to the chain. {@link #updateAdvisorArray() Updates} the {@link Advisor} array
|
||||
* and fires {@link #adviceChanged events}.
|
||||
*/
|
||||
private void makeAdvisorChainAspectJCapableIfNecessary() {
|
||||
if (AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(getAdvisorsInternal())) {
|
||||
updateAdvisorArray();
|
||||
adviceChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the singleton aspect instance for the supplied aspect type. An instance
|
||||
* is created if one cannot be found in the instance cache.
|
||||
*/
|
||||
private Object getSingletonAspectInstance(Class aspectClass) {
|
||||
synchronized (aspectCache) {
|
||||
Object instance = aspectCache.get(aspectClass);
|
||||
if (instance != null) {
|
||||
return instance;
|
||||
}
|
||||
try {
|
||||
instance = aspectClass.newInstance();
|
||||
aspectCache.put(aspectClass, instance);
|
||||
return instance;
|
||||
}
|
||||
catch (InstantiationException ex) {
|
||||
throw new AopConfigException("Unable to instantiate aspect class [" + aspectClass.getName() + "]", ex);
|
||||
}
|
||||
catch (IllegalAccessException ex) {
|
||||
throw new AopConfigException("Cannot access aspect class [" + aspectClass.getName() + "]", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a new proxy according to the settings in this factory.
|
||||
* <p>Can be called repeatedly. Effect will vary if we've added
|
||||
* or removed interfaces. Can add and remove interceptors.
|
||||
* <p>Uses a default class loader: Usually, the thread context class loader
|
||||
* (if necessary for proxy creation).
|
||||
* @return the new proxy
|
||||
*/
|
||||
public <T> T getProxy() {
|
||||
return (T) createAopProxy().getProxy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new proxy according to the settings in this factory.
|
||||
* <p>Can be called repeatedly. Effect will vary if we've added
|
||||
* or removed interfaces. Can add and remove interceptors.
|
||||
* <p>Uses the given class loader (if necessary for proxy creation).
|
||||
* @param classLoader the class loader to create the proxy with
|
||||
* @return the new proxy
|
||||
*/
|
||||
public <T> T getProxy(ClassLoader classLoader) {
|
||||
return (T) createAopProxy().getProxy(classLoader);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright 2002-2006 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 org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.reflect.AjType;
|
||||
import org.aspectj.lang.reflect.AjTypeSystem;
|
||||
import org.aspectj.lang.reflect.PerClauseKind;
|
||||
|
||||
import org.springframework.aop.Pointcut;
|
||||
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
|
||||
import org.springframework.aop.aspectj.TypePatternClassFilter;
|
||||
import org.springframework.aop.framework.AopConfigException;
|
||||
import org.springframework.aop.support.ComposablePointcut;
|
||||
|
||||
/**
|
||||
* Metadata for an AspectJ aspect class, with an additional Spring AOP pointcut
|
||||
* for the per clause.
|
||||
*
|
||||
* <p>Uses AspectJ 5 AJType reflection API, so is only supported on Java 5.
|
||||
* Enables us to work with different AspectJ instantiation models such as
|
||||
* "singleton", "pertarget" and "perthis".
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see org.springframework.aop.aspectj.AspectJExpressionPointcut
|
||||
*/
|
||||
public class AspectMetadata {
|
||||
|
||||
/**
|
||||
* AspectJ reflection information (AspectJ 5 / Java 5 specific).
|
||||
*/
|
||||
private final AjType ajType;
|
||||
|
||||
/**
|
||||
* Spring AOP pointcut corresponding to the per clause of the
|
||||
* aspect. Will be the Pointcut.TRUE canonical instance in the
|
||||
* case of a singleton, otherwise an AspectJExpressionPointcut.
|
||||
*/
|
||||
private final Pointcut perClausePointcut;
|
||||
|
||||
/**
|
||||
* The name of this aspect as defined to Spring (the bean name) -
|
||||
* allows us to determine if two pieces of advice come from the
|
||||
* same aspect and hence their relative precedence.
|
||||
*/
|
||||
private String aspectName;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new AspectMetadata instance for the given aspect class.
|
||||
* @param aspectClass the aspect class
|
||||
* @param aspectName the name of the aspect
|
||||
*/
|
||||
public AspectMetadata(Class aspectClass, String aspectName) {
|
||||
this.aspectName = aspectName;
|
||||
this.ajType = AjTypeSystem.getAjType(aspectClass);
|
||||
|
||||
if (!this.ajType.isAspect()) {
|
||||
throw new IllegalArgumentException("Class '" + aspectClass.getName() + "' is not an @AspectJ aspect");
|
||||
}
|
||||
if (this.ajType.getDeclarePrecedence().length > 0) {
|
||||
throw new IllegalArgumentException("DeclarePrecendence not presently supported in Spring AOP");
|
||||
}
|
||||
|
||||
switch (this.ajType.getPerClause().getKind()) {
|
||||
case SINGLETON :
|
||||
this.perClausePointcut = Pointcut.TRUE;
|
||||
return;
|
||||
case PERTARGET : case PERTHIS :
|
||||
AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut();
|
||||
ajexp.setLocation("@Aspect annotation on " + aspectClass.getName());
|
||||
ajexp.setExpression(findPerClause(aspectClass));
|
||||
this.perClausePointcut = ajexp;
|
||||
return;
|
||||
case PERTYPEWITHIN :
|
||||
// Works with a type pattern
|
||||
this.perClausePointcut = new ComposablePointcut(new TypePatternClassFilter(findPerClause(aspectClass)));
|
||||
return;
|
||||
default :
|
||||
throw new AopConfigException(
|
||||
"PerClause " + ajType.getPerClause().getKind() + " not supported by Spring AOP for " + aspectClass);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract contents from String of form <code>pertarget(contents)</code>.
|
||||
*/
|
||||
private String findPerClause(Class<?> aspectClass) {
|
||||
// TODO when AspectJ provides this, we can remove this hack. Hence we don't
|
||||
// bother to make it elegant. Or efficient. Or robust :-)
|
||||
String str = aspectClass.getAnnotation(Aspect.class).value();
|
||||
str = str.substring(str.indexOf("(") + 1);
|
||||
str = str.substring(0, str.length() - 1);
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return AspectJ reflection information.
|
||||
*/
|
||||
public AjType getAjType() {
|
||||
return this.ajType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the aspect class.
|
||||
*/
|
||||
public Class getAspectClass() {
|
||||
return this.ajType.getJavaClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the aspect class.
|
||||
*/
|
||||
public String getAspectName() {
|
||||
return this.aspectName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Spring pointcut expression for a singleton aspect.
|
||||
* (e.g. <code>Pointcut.TRUE</code> if it's a singleton).
|
||||
*/
|
||||
public Pointcut getPerClausePointcut() {
|
||||
return this.perClausePointcut;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the aspect is defined as "perthis" or "pertarget".
|
||||
*/
|
||||
public boolean isPerThisOrPerTarget() {
|
||||
PerClauseKind kind = getAjType().getPerClause().getKind();
|
||||
return (kind == PerClauseKind.PERTARGET || kind == PerClauseKind.PERTHIS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the aspect is defined as "pertypewithin".
|
||||
*/
|
||||
public boolean isPerTypeWithin() {
|
||||
PerClauseKind kind = getAjType().getPerClause().getKind();
|
||||
return (kind == PerClauseKind.PERTYPEWITHIN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the aspect needs to be lazily instantiated.
|
||||
*/
|
||||
public boolean isLazilyInstantiated() {
|
||||
return (isPerThisOrPerTarget() || isPerTypeWithin());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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 org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* AspectInstanceFactory backed by a Spring
|
||||
* {@link org.springframework.beans.factory.BeanFactory}.
|
||||
*
|
||||
* <p>Note that this may instantiate multiple times if using a prototype,
|
||||
* which probably won't give the semantics you expect.
|
||||
* Use a {@link LazySingletonAspectInstanceFactoryDecorator}
|
||||
* to wrap this to ensure only one new aspect comes back.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see org.springframework.beans.factory.BeanFactory
|
||||
* @see LazySingletonAspectInstanceFactoryDecorator
|
||||
*/
|
||||
public class BeanFactoryAspectInstanceFactory implements MetadataAwareAspectInstanceFactory {
|
||||
|
||||
private final BeanFactory beanFactory;
|
||||
|
||||
private final String name;
|
||||
|
||||
private final AspectMetadata aspectMetadata;
|
||||
|
||||
|
||||
/**
|
||||
* Create a BeanFactoryAspectInstanceFactory. AspectJ will be called to
|
||||
* introspect to create AJType metadata using the type returned for the
|
||||
* given bean name from the BeanFactory.
|
||||
* @param beanFactory BeanFactory to obtain instance(s) from
|
||||
* @param name name of the bean
|
||||
*/
|
||||
public BeanFactoryAspectInstanceFactory(BeanFactory beanFactory, String name) {
|
||||
this(beanFactory, name, beanFactory.getType(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a BeanFactoryAspectInstanceFactory, providing a type that AspectJ should
|
||||
* introspect to create AJType metadata. Use if the BeanFactory may consider the type
|
||||
* to be a subclass (as when using CGLIB), and the information should relate to a superclass.
|
||||
* @param beanFactory BeanFactory to obtain instance(s) from
|
||||
* @param name the name of the bean
|
||||
* @param type the type that should be introspected by AspectJ
|
||||
*/
|
||||
public BeanFactoryAspectInstanceFactory(BeanFactory beanFactory, String name, Class type) {
|
||||
this.beanFactory = beanFactory;
|
||||
this.name = name;
|
||||
this.aspectMetadata = new AspectMetadata(type, name);
|
||||
}
|
||||
|
||||
|
||||
public Object getAspectInstance() {
|
||||
return this.beanFactory.getBean(this.name);
|
||||
}
|
||||
|
||||
public ClassLoader getAspectClassLoader() {
|
||||
if (this.beanFactory instanceof ConfigurableBeanFactory) {
|
||||
return ((ConfigurableBeanFactory) this.beanFactory).getBeanClassLoader();
|
||||
}
|
||||
else {
|
||||
return ClassUtils.getDefaultClassLoader();
|
||||
}
|
||||
}
|
||||
|
||||
public AspectMetadata getAspectMetadata() {
|
||||
return this.aspectMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the order for this factory's target aspect, either
|
||||
* an instance-specific order expressed through implementing the
|
||||
* {@link org.springframework.core.Ordered} interface (only
|
||||
* checked for singleton beans), or an order expressed through the
|
||||
* {@link org.springframework.core.annotation.Order} annotation
|
||||
* at the class level.
|
||||
* @see org.springframework.core.Ordered
|
||||
* @see org.springframework.core.annotation.Order
|
||||
*/
|
||||
public int getOrder() {
|
||||
Class type = this.beanFactory.getType(this.name);
|
||||
if (type != null) {
|
||||
if (Ordered.class.isAssignableFrom(type) && this.beanFactory.isSingleton(this.name)) {
|
||||
return ((Ordered) this.beanFactory.getBean(this.name)).getOrder();
|
||||
}
|
||||
Order order = (Order) type.getAnnotation(Order.class);
|
||||
if (order != null) {
|
||||
return order.value();
|
||||
}
|
||||
}
|
||||
return Ordered.LOWEST_PRECEDENCE;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + ": bean name '" + this.name + "'";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.aspectj.lang.reflect.PerClauseKind;
|
||||
|
||||
import org.springframework.aop.Advisor;
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Helper for retrieving @AspectJ beans from a BeanFactory and building
|
||||
* Spring Advisors based on them, for use with auto-proxying.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0.2
|
||||
* @see AnnotationAwareAspectJAutoProxyCreator
|
||||
*/
|
||||
public class BeanFactoryAspectJAdvisorsBuilder {
|
||||
|
||||
private final ListableBeanFactory beanFactory;
|
||||
|
||||
private final AspectJAdvisorFactory advisorFactory;
|
||||
|
||||
private List<String> aspectBeanNames;
|
||||
|
||||
private final Map<String, List<Advisor>> advisorsCache = new HashMap<String, List<Advisor>>();
|
||||
|
||||
private final Map<String, MetadataAwareAspectInstanceFactory> aspectFactoryCache =
|
||||
new HashMap<String, MetadataAwareAspectInstanceFactory>();
|
||||
|
||||
|
||||
/**
|
||||
* Create a new BeanFactoryAspectJAdvisorsBuilder for the given BeanFactory.
|
||||
* @param beanFactory the ListableBeanFactory to scan
|
||||
*/
|
||||
public BeanFactoryAspectJAdvisorsBuilder(ListableBeanFactory beanFactory) {
|
||||
this(beanFactory, new ReflectiveAspectJAdvisorFactory());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new BeanFactoryAspectJAdvisorsBuilder for the given BeanFactory.
|
||||
* @param beanFactory the ListableBeanFactory to scan
|
||||
* @param advisorFactory the AspectJAdvisorFactory to build each Advisor with
|
||||
*/
|
||||
public BeanFactoryAspectJAdvisorsBuilder(ListableBeanFactory beanFactory, AspectJAdvisorFactory advisorFactory) {
|
||||
Assert.notNull(beanFactory, "ListableBeanFactory must not be null");
|
||||
Assert.notNull(advisorFactory, "AspectJAdvisorFactory must not be null");
|
||||
this.beanFactory = beanFactory;
|
||||
this.advisorFactory = advisorFactory;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Look for AspectJ-annotated aspect beans in the current bean factory,
|
||||
* and return to a list of Spring AOP Advisors representing them.
|
||||
* <p>Creates a Spring Advisor for each AspectJ advice method.
|
||||
* @return the list of {@link org.springframework.aop.Advisor} beans
|
||||
* @see #isEligibleBean
|
||||
*/
|
||||
public List<Advisor> buildAspectJAdvisors() {
|
||||
List<String> aspectNames = null;
|
||||
|
||||
synchronized (this) {
|
||||
aspectNames = this.aspectBeanNames;
|
||||
if (aspectNames == null) {
|
||||
List<Advisor> advisors = new LinkedList<Advisor>();
|
||||
aspectNames = new LinkedList<String>();
|
||||
String[] beanNames =
|
||||
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);
|
||||
for (String beanName : beanNames) {
|
||||
if (!isEligibleBean(beanName)) {
|
||||
continue;
|
||||
}
|
||||
// We must be careful not to instantiate beans eagerly as in this
|
||||
// case they would be cached by the Spring container but would not
|
||||
// have been weaved
|
||||
Class beanType = this.beanFactory.getType(beanName);
|
||||
if (beanType == null) {
|
||||
continue;
|
||||
}
|
||||
if (this.advisorFactory.isAspect(beanType)) {
|
||||
aspectNames.add(beanName);
|
||||
AspectMetadata amd = new AspectMetadata(beanType, beanName);
|
||||
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
|
||||
MetadataAwareAspectInstanceFactory factory =
|
||||
new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
|
||||
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
|
||||
if (this.beanFactory.isSingleton(beanName)) {
|
||||
this.advisorsCache.put(beanName, classAdvisors);
|
||||
}
|
||||
else {
|
||||
this.aspectFactoryCache.put(beanName, factory);
|
||||
}
|
||||
advisors.addAll(classAdvisors);
|
||||
}
|
||||
else {
|
||||
// Per target or per this.
|
||||
if (this.beanFactory.isSingleton(beanName)) {
|
||||
throw new IllegalArgumentException("Bean with name '" + beanName +
|
||||
"' is a singleton, but aspect instantiation model is not singleton");
|
||||
}
|
||||
MetadataAwareAspectInstanceFactory factory =
|
||||
new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
|
||||
this.aspectFactoryCache.put(beanName, factory);
|
||||
advisors.addAll(this.advisorFactory.getAdvisors(factory));
|
||||
}
|
||||
}
|
||||
}
|
||||
this.aspectBeanNames = aspectNames;
|
||||
return advisors;
|
||||
}
|
||||
}
|
||||
|
||||
if (aspectNames.isEmpty()) {
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
List<Advisor> advisors = new LinkedList<Advisor>();
|
||||
for (Iterator it = aspectNames.iterator(); it.hasNext();) {
|
||||
String aspectName = (String) it.next();
|
||||
List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
|
||||
if (cachedAdvisors != null) {
|
||||
advisors.addAll(cachedAdvisors);
|
||||
}
|
||||
else {
|
||||
MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
|
||||
advisors.addAll(this.advisorFactory.getAdvisors(factory));
|
||||
}
|
||||
}
|
||||
return advisors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the aspect bean with the given name is eligible.
|
||||
* @param beanName the name of the aspect bean
|
||||
* @return whether the bean is eligible
|
||||
*/
|
||||
protected boolean isEligibleBean(String beanName) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* Copyright 2002-2006 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.reflect.Method;
|
||||
|
||||
import org.aopalliance.aop.Advice;
|
||||
import org.aspectj.lang.reflect.PerClauseKind;
|
||||
|
||||
import org.springframework.aop.Pointcut;
|
||||
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
|
||||
import org.springframework.aop.aspectj.AspectJPrecedenceInformation;
|
||||
import org.springframework.aop.aspectj.InstantiationModelAwarePointcutAdvisor;
|
||||
import org.springframework.aop.aspectj.annotation.AbstractAspectJAdvisorFactory.AspectJAnnotation;
|
||||
import org.springframework.aop.support.DynamicMethodMatcherPointcut;
|
||||
import org.springframework.aop.support.Pointcuts;
|
||||
|
||||
/**
|
||||
* Internal implementation of AspectJPointcutAdvisor.
|
||||
* Note that there will be one instance of this advisor for each target method.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
class InstantiationModelAwarePointcutAdvisorImpl
|
||||
implements InstantiationModelAwarePointcutAdvisor, AspectJPrecedenceInformation {
|
||||
|
||||
private final AspectJExpressionPointcut declaredPointcut;
|
||||
|
||||
private Pointcut pointcut;
|
||||
|
||||
private final MetadataAwareAspectInstanceFactory aspectInstanceFactory;
|
||||
|
||||
private final Method method;
|
||||
|
||||
private final boolean lazy;
|
||||
|
||||
private final AspectJAdvisorFactory atAspectJAdvisorFactory;
|
||||
|
||||
private Advice instantiatedAdvice;
|
||||
|
||||
private int declarationOrder;
|
||||
|
||||
private String aspectName;
|
||||
|
||||
private Boolean isBeforeAdvice = null;
|
||||
|
||||
private Boolean isAfterAdvice = null;
|
||||
|
||||
|
||||
public InstantiationModelAwarePointcutAdvisorImpl(
|
||||
AspectJAdvisorFactory af,
|
||||
AspectJExpressionPointcut ajexp,
|
||||
MetadataAwareAspectInstanceFactory aif,
|
||||
Method method,
|
||||
int declarationOrderInAspect,
|
||||
String aspectName) {
|
||||
|
||||
this.declaredPointcut = ajexp;
|
||||
this.method = method;
|
||||
this.atAspectJAdvisorFactory = af;
|
||||
this.aspectInstanceFactory = aif;
|
||||
this.declarationOrder = declarationOrderInAspect;
|
||||
this.aspectName = aspectName;
|
||||
|
||||
if (aif.getAspectMetadata().isLazilyInstantiated()) {
|
||||
// Static part of the pointcut is a lazy type.
|
||||
Pointcut preInstantiationPointcut =
|
||||
Pointcuts.union(aif.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut);
|
||||
|
||||
// Make it dynamic: must mutate from pre-instantiation to post-instantiation state.
|
||||
// If it's not a dynamic pointcut, it may be optimized out
|
||||
// by the Spring AOP infrastructure after the first evaluation.
|
||||
this.pointcut = new PerTargetInstantiationModelPointcut(this.declaredPointcut, preInstantiationPointcut, aif);
|
||||
this.lazy = true;
|
||||
}
|
||||
else {
|
||||
// A singleton aspect.
|
||||
this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
|
||||
this.pointcut = declaredPointcut;
|
||||
this.lazy = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The pointcut for Spring AOP to use. Actual behaviour of the pointcut will change
|
||||
* depending on the state of the advice.
|
||||
*/
|
||||
public Pointcut getPointcut() {
|
||||
return this.pointcut;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is only of interest for Spring AOP: AspectJ instantiation semantics
|
||||
* are much richer. In AspectJ terminology, all a return of <code>true</code>
|
||||
* means here is that the aspect is not a SINGLETON.
|
||||
*/
|
||||
public boolean isPerInstance() {
|
||||
return (getAspectMetadata().getAjType().getPerClause().getKind() != PerClauseKind.SINGLETON);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the AspectJ AspectMetadata for this advisor.
|
||||
*/
|
||||
public AspectMetadata getAspectMetadata() {
|
||||
return this.aspectInstanceFactory.getAspectMetadata();
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazily instantiate advice if necessary.
|
||||
*/
|
||||
public synchronized Advice getAdvice() {
|
||||
if (this.instantiatedAdvice == null) {
|
||||
this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
|
||||
}
|
||||
return this.instantiatedAdvice;
|
||||
}
|
||||
|
||||
public boolean isLazy() {
|
||||
return this.lazy;
|
||||
}
|
||||
|
||||
public synchronized boolean isAdviceInstantiated() {
|
||||
return (this.instantiatedAdvice != null);
|
||||
}
|
||||
|
||||
|
||||
private Advice instantiateAdvice(AspectJExpressionPointcut pcut) {
|
||||
return this.atAspectJAdvisorFactory.getAdvice(
|
||||
this.method, pcut, this.aspectInstanceFactory, this.declarationOrder, this.aspectName);
|
||||
}
|
||||
|
||||
public MetadataAwareAspectInstanceFactory getAspectInstanceFactory() {
|
||||
return this.aspectInstanceFactory;
|
||||
}
|
||||
|
||||
public AspectJExpressionPointcut getDeclaredPointcut() {
|
||||
return this.declaredPointcut;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return this.aspectInstanceFactory.getOrder();
|
||||
}
|
||||
|
||||
public String getAspectName() {
|
||||
return this.aspectName;
|
||||
}
|
||||
|
||||
public int getDeclarationOrder() {
|
||||
return this.declarationOrder;
|
||||
}
|
||||
|
||||
public boolean isBeforeAdvice() {
|
||||
if (this.isBeforeAdvice == null) {
|
||||
determineAdviceType();
|
||||
}
|
||||
return this.isBeforeAdvice;
|
||||
}
|
||||
|
||||
public boolean isAfterAdvice() {
|
||||
if (this.isAfterAdvice == null) {
|
||||
determineAdviceType();
|
||||
}
|
||||
return this.isAfterAdvice;
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicates some logic from getAdvice, but importantly does not force
|
||||
* creation of the advice.
|
||||
*/
|
||||
private void determineAdviceType() {
|
||||
AspectJAnnotation<?> aspectJAnnotation =
|
||||
AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(this.method);
|
||||
if (aspectJAnnotation == null) {
|
||||
this.isBeforeAdvice = false;
|
||||
this.isAfterAdvice = false;
|
||||
}
|
||||
else {
|
||||
switch (aspectJAnnotation.getAnnotationType()) {
|
||||
case AtAfter:
|
||||
case AtAfterReturning:
|
||||
case AtAfterThrowing:
|
||||
this.isAfterAdvice = true;
|
||||
this.isBeforeAdvice = false;
|
||||
break;
|
||||
case AtAround:
|
||||
case AtPointcut:
|
||||
this.isAfterAdvice = false;
|
||||
this.isBeforeAdvice = false;
|
||||
break;
|
||||
case AtBefore:
|
||||
this.isAfterAdvice = false;
|
||||
this.isBeforeAdvice = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "InstantiationModelAwarePointcutAdvisor: expression [" + getDeclaredPointcut().getExpression() +
|
||||
"]; advice method [" + this.method + "]; perClauseKind=" +
|
||||
this.aspectInstanceFactory.getAspectMetadata().getAjType().getPerClause().getKind();
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Pointcut implementation that changes its behaviour when the advice is instantiated.
|
||||
* Note that this is a <i>dynamic</i> pointcut. Otherwise it might
|
||||
* be optimized out if it does not at first match statically.
|
||||
*/
|
||||
private class PerTargetInstantiationModelPointcut extends DynamicMethodMatcherPointcut {
|
||||
|
||||
private final AspectJExpressionPointcut declaredPointcut;
|
||||
|
||||
private final Pointcut preInstantiationPointcut;
|
||||
|
||||
private LazySingletonAspectInstanceFactoryDecorator aspectInstanceFactory;
|
||||
|
||||
private PerTargetInstantiationModelPointcut(AspectJExpressionPointcut declaredPointcut,
|
||||
Pointcut preInstantiationPointcut, MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
|
||||
this.declaredPointcut = declaredPointcut;
|
||||
this.preInstantiationPointcut = preInstantiationPointcut;
|
||||
if (aspectInstanceFactory instanceof LazySingletonAspectInstanceFactoryDecorator) {
|
||||
this.aspectInstanceFactory = (LazySingletonAspectInstanceFactoryDecorator) aspectInstanceFactory;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(Method method, Class targetClass) {
|
||||
// We're either instantiated and matching on declared pointcut, or uninstantiated matching on either pointcut
|
||||
return (isAspectMaterialized() && this.declaredPointcut.matches(method, targetClass)) ||
|
||||
this.preInstantiationPointcut.getMethodMatcher().matches(method, targetClass);
|
||||
}
|
||||
|
||||
public boolean matches(Method method, Class targetClass, Object[] args) {
|
||||
// This can match only on declared pointcut.
|
||||
return (isAspectMaterialized() && this.declaredPointcut.matches(method, targetClass));
|
||||
}
|
||||
|
||||
private boolean isAspectMaterialized() {
|
||||
return (this.aspectInstanceFactory == null || this.aspectInstanceFactory.isMaterialized());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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 org.springframework.core.Ordered;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Decorator to cause a MetadataAwareAspectInstanceFactory to instantiate only once.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
public class LazySingletonAspectInstanceFactoryDecorator implements MetadataAwareAspectInstanceFactory {
|
||||
|
||||
private final MetadataAwareAspectInstanceFactory maaif;
|
||||
|
||||
private Object materialized;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new lazily initializing decorator for the given AspectInstanceFactory.
|
||||
* @param maaif the MetadataAwareAspectInstanceFactory to decorate
|
||||
*/
|
||||
public LazySingletonAspectInstanceFactoryDecorator(MetadataAwareAspectInstanceFactory maaif) {
|
||||
Assert.notNull(maaif, "AspectInstanceFactory must not be null");
|
||||
this.maaif = maaif;
|
||||
}
|
||||
|
||||
public synchronized Object getAspectInstance() {
|
||||
if (this.materialized == null) {
|
||||
this.materialized = this.maaif.getAspectInstance();
|
||||
}
|
||||
return this.materialized;
|
||||
}
|
||||
|
||||
public ClassLoader getAspectClassLoader() {
|
||||
return this.maaif.getAspectClassLoader();
|
||||
}
|
||||
|
||||
public boolean isMaterialized() {
|
||||
return (this.materialized != null);
|
||||
}
|
||||
|
||||
public AspectMetadata getAspectMetadata() {
|
||||
return this.maaif.getAspectMetadata();
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
if (this.maaif instanceof Ordered) {
|
||||
return ((Ordered) this.maaif).getOrder();
|
||||
}
|
||||
return Ordered.LOWEST_PRECEDENCE;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LazySingletonAspectInstanceFactoryDecorator: decorating " + this.maaif;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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 org.springframework.aop.aspectj.AspectInstanceFactory;
|
||||
|
||||
/**
|
||||
* Subinterface of {@link org.springframework.aop.aspectj.AspectInstanceFactory}
|
||||
* that returns {@link AspectMetadata} associated with AspectJ-annotated classes.
|
||||
*
|
||||
* <p>Ideally, AspectInstanceFactory would include this method itself, but because
|
||||
* AspectMetadata uses Java-5-only {@link org.aspectj.lang.reflect.AjType},
|
||||
* we need to split out this subinterface.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @since 2.0
|
||||
* @see AspectMetadata
|
||||
* @see org.aspectj.lang.reflect.AjType
|
||||
*/
|
||||
public interface MetadataAwareAspectInstanceFactory extends AspectInstanceFactory {
|
||||
|
||||
/**
|
||||
* Return the AspectJ AspectMetadata for this factory's aspect.
|
||||
* @return the aspect metadata
|
||||
*/
|
||||
AspectMetadata getAspectMetadata();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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 org.springframework.aop.framework.AopConfigException;
|
||||
|
||||
/**
|
||||
* Extension of AopConfigException thrown when trying to perform
|
||||
* an advisor generation operation on a class that is not an
|
||||
* AspectJ annotation-style aspect.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @since 2.0
|
||||
*/
|
||||
public class NotAnAtAspectException extends AopConfigException {
|
||||
|
||||
private Class<?> nonAspectClass;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new NotAnAtAspectException for the given class.
|
||||
* @param nonAspectClass the offending class
|
||||
*/
|
||||
public NotAnAtAspectException(Class<?> nonAspectClass) {
|
||||
super(nonAspectClass.getName() + " is not an @AspectJ aspect");
|
||||
this.nonAspectClass = nonAspectClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the offending class.
|
||||
*/
|
||||
public Class<?> getNonAspectClass() {
|
||||
return this.nonAspectClass;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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 org.springframework.beans.factory.BeanFactory;
|
||||
|
||||
/**
|
||||
* AspectInstanceFactory backed by a BeanFactory-provided prototype,
|
||||
* enforcing prototype semantics.
|
||||
*
|
||||
* <p>Note that this may instantiate multiple times, which probably won't give the
|
||||
* semantics you expect. Use a {@link LazySingletonAspectInstanceFactoryDecorator}
|
||||
* to wrap this to ensure only one new aspect comes back.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see org.springframework.beans.factory.BeanFactory
|
||||
* @see LazySingletonAspectInstanceFactoryDecorator
|
||||
*/
|
||||
public class PrototypeAspectInstanceFactory extends BeanFactoryAspectInstanceFactory {
|
||||
|
||||
/**
|
||||
* Create a PrototypeAspectInstanceFactory. AspectJ will be called to
|
||||
* introspect to create AJType metadata using the type returned for the
|
||||
* given bean name from the BeanFactory.
|
||||
* @param beanFactory the BeanFactory to obtain instance(s) from
|
||||
* @param name the name of the bean
|
||||
*/
|
||||
public PrototypeAspectInstanceFactory(BeanFactory beanFactory, String name) {
|
||||
super(beanFactory, name);
|
||||
if (!beanFactory.isPrototype(name)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Cannot use PrototypeAspectInstanceFactory with bean named '" + name + "': not a prototype");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.aopalliance.aop.Advice;
|
||||
import org.aspectj.lang.annotation.AfterReturning;
|
||||
import org.aspectj.lang.annotation.AfterThrowing;
|
||||
import org.aspectj.lang.annotation.DeclareParents;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
|
||||
import org.springframework.aop.Advisor;
|
||||
import org.springframework.aop.MethodBeforeAdvice;
|
||||
import org.springframework.aop.aspectj.AbstractAspectJAdvice;
|
||||
import org.springframework.aop.aspectj.AspectJAfterAdvice;
|
||||
import org.springframework.aop.aspectj.AspectJAfterReturningAdvice;
|
||||
import org.springframework.aop.aspectj.AspectJAfterThrowingAdvice;
|
||||
import org.springframework.aop.aspectj.AspectJAroundAdvice;
|
||||
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
|
||||
import org.springframework.aop.aspectj.AspectJMethodBeforeAdvice;
|
||||
import org.springframework.aop.aspectj.DeclareParentsAdvisor;
|
||||
import org.springframework.aop.framework.AopConfigException;
|
||||
import org.springframework.aop.support.DefaultPointcutAdvisor;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Factory that can create Spring AOP Advisors given AspectJ classes from
|
||||
* classes honoring the AspectJ 5 annotation syntax, using reflection to
|
||||
* invoke the corresponding advice methods.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Adrian Colyer
|
||||
* @author Juergen Hoeller
|
||||
* @author Ramnivas Laddad
|
||||
* @since 2.0
|
||||
*/
|
||||
public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFactory {
|
||||
|
||||
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory maaif) {
|
||||
final Class<?> aspectClass = maaif.getAspectMetadata().getAspectClass();
|
||||
final String aspectName = maaif.getAspectMetadata().getAspectName();
|
||||
validate(aspectClass);
|
||||
|
||||
// We need to wrap the MetadataAwareAspectInstanceFactory with a decorator
|
||||
// so that it will only instantiate once.
|
||||
final MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
|
||||
new LazySingletonAspectInstanceFactoryDecorator(maaif);
|
||||
|
||||
final List<Advisor> advisors = new LinkedList<Advisor>();
|
||||
ReflectionUtils.doWithMethods(aspectClass, new ReflectionUtils.MethodCallback() {
|
||||
public void doWith(Method method) throws IllegalArgumentException {
|
||||
// Exclude pointcuts
|
||||
if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) {
|
||||
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
|
||||
if (advisor != null) {
|
||||
advisors.add(advisor);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// If it's a per target aspect, emit the dummy instantiating aspect.
|
||||
if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
|
||||
Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
|
||||
advisors.add(0, instantiationAdvisor);
|
||||
}
|
||||
|
||||
// Find introduction fields.
|
||||
for (Field field : aspectClass.getDeclaredFields()) {
|
||||
Advisor advisor = getDeclareParentsAdvisor(field);
|
||||
if (advisor != null) {
|
||||
advisors.add(advisor);
|
||||
}
|
||||
}
|
||||
|
||||
return advisors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a {@link org.springframework.aop.aspectj.DeclareParentsAdvisor}
|
||||
* for the given introduction field.
|
||||
* <p>Resulting Advisors will need to be evaluated for targets.
|
||||
* @param introductionField the field to introspect
|
||||
* @return <code>null</code> if not an Advisor
|
||||
*/
|
||||
private Advisor getDeclareParentsAdvisor(Field introductionField) {
|
||||
DeclareParents declareParents = (DeclareParents) introductionField.getAnnotation(DeclareParents.class);
|
||||
if (declareParents == null) {
|
||||
// Not an introduction field
|
||||
return null;
|
||||
}
|
||||
|
||||
if (DeclareParents.class.equals(declareParents.defaultImpl())) {
|
||||
// This is what comes back if it wasn't set. This seems bizarre...
|
||||
// TODO this restriction possibly should be relaxed
|
||||
throw new IllegalStateException("defaultImpl must be set on DeclareParents");
|
||||
}
|
||||
|
||||
return new DeclareParentsAdvisor(
|
||||
introductionField.getType(), declareParents.value(), declareParents.defaultImpl());
|
||||
}
|
||||
|
||||
|
||||
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aif,
|
||||
int declarationOrderInAspect, String aspectName) {
|
||||
|
||||
validate(aif.getAspectMetadata().getAspectClass());
|
||||
|
||||
AspectJExpressionPointcut ajexp =
|
||||
getPointcut(candidateAdviceMethod, aif.getAspectMetadata().getAspectClass());
|
||||
if (ajexp == null) {
|
||||
return null;
|
||||
}
|
||||
return new InstantiationModelAwarePointcutAdvisorImpl(
|
||||
this, ajexp, aif, candidateAdviceMethod, declarationOrderInAspect, aspectName);
|
||||
}
|
||||
|
||||
private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
|
||||
AspectJAnnotation<?> aspectJAnnotation =
|
||||
AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
|
||||
if (aspectJAnnotation == null) {
|
||||
return null;
|
||||
}
|
||||
AspectJExpressionPointcut ajexp =
|
||||
new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class[0]);
|
||||
ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
|
||||
return ajexp;
|
||||
}
|
||||
|
||||
|
||||
public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut ajexp,
|
||||
MetadataAwareAspectInstanceFactory aif, int declarationOrderInAspect, String aspectName) {
|
||||
|
||||
Class<?> candidateAspectClass = aif.getAspectMetadata().getAspectClass();
|
||||
validate(candidateAspectClass);
|
||||
|
||||
AspectJAnnotation<?> aspectJAnnotation =
|
||||
AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
|
||||
if (aspectJAnnotation == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// If we get here, we know we have an AspectJ method.
|
||||
// Check that it's an AspectJ-annotated class
|
||||
if (!isAspect(candidateAspectClass)) {
|
||||
throw new AopConfigException("Advice must be declared inside an aspect type: " +
|
||||
"Offending method '" + candidateAdviceMethod + "' in class [" +
|
||||
candidateAspectClass.getName() + "]");
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Found AspectJ method: " + candidateAdviceMethod);
|
||||
}
|
||||
|
||||
AbstractAspectJAdvice springAdvice;
|
||||
|
||||
switch (aspectJAnnotation.getAnnotationType()) {
|
||||
case AtBefore:
|
||||
springAdvice = new AspectJMethodBeforeAdvice(candidateAdviceMethod, ajexp, aif);
|
||||
break;
|
||||
case AtAfter:
|
||||
springAdvice = new AspectJAfterAdvice(candidateAdviceMethod, ajexp, aif);
|
||||
break;
|
||||
case AtAfterReturning:
|
||||
springAdvice = new AspectJAfterReturningAdvice(candidateAdviceMethod, ajexp, aif);
|
||||
AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
|
||||
if (StringUtils.hasText(afterReturningAnnotation.returning())) {
|
||||
springAdvice.setReturningName(afterReturningAnnotation.returning());
|
||||
}
|
||||
break;
|
||||
case AtAfterThrowing:
|
||||
springAdvice = new AspectJAfterThrowingAdvice(candidateAdviceMethod, ajexp, aif);
|
||||
AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
|
||||
if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
|
||||
springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
|
||||
}
|
||||
break;
|
||||
case AtAround:
|
||||
springAdvice = new AspectJAroundAdvice(candidateAdviceMethod, ajexp, aif);
|
||||
break;
|
||||
case AtPointcut:
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
|
||||
}
|
||||
return null;
|
||||
default:
|
||||
throw new UnsupportedOperationException(
|
||||
"Unsupported advice type on method " + candidateAdviceMethod);
|
||||
}
|
||||
|
||||
// Now to configure the advice...
|
||||
springAdvice.setAspectName(aspectName);
|
||||
springAdvice.setDeclarationOrder(declarationOrderInAspect);
|
||||
String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
|
||||
if (argNames != null) {
|
||||
springAdvice.setArgumentNamesFromStringArray(argNames);
|
||||
}
|
||||
springAdvice.calculateArgumentBindings();
|
||||
return springAdvice;
|
||||
}
|
||||
|
||||
/**
|
||||
* Synthetic advisor that instantiates the aspect.
|
||||
* Triggered by per-clause pointcut on non-singleton aspect.
|
||||
* The advice has no effect.
|
||||
*/
|
||||
protected static class SyntheticInstantiationAdvisor extends DefaultPointcutAdvisor {
|
||||
|
||||
public SyntheticInstantiationAdvisor(final MetadataAwareAspectInstanceFactory aif) {
|
||||
super(aif.getAspectMetadata().getPerClausePointcut(), new MethodBeforeAdvice() {
|
||||
public void before(Method method, Object[] args, Object target) {
|
||||
// Simply instantiate the aspect
|
||||
aif.getAspectInstance();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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 org.springframework.aop.aspectj.SimpleAspectInstanceFactory;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
|
||||
/**
|
||||
* Implementation of {@link MetadataAwareAspectInstanceFactory} that
|
||||
* creates a new instance of the specified aspect class for every
|
||||
* {@link #getAspectInstance()} call.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0.4
|
||||
*/
|
||||
public class SimpleMetadataAwareAspectInstanceFactory extends SimpleAspectInstanceFactory
|
||||
implements MetadataAwareAspectInstanceFactory {
|
||||
|
||||
private final AspectMetadata metadata;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new SimpleMetadataAwareAspectInstanceFactory for the given aspect class.
|
||||
* @param aspectClass the aspect class
|
||||
* @param aspectName the aspect name
|
||||
*/
|
||||
public SimpleMetadataAwareAspectInstanceFactory(Class aspectClass, String aspectName) {
|
||||
super(aspectClass);
|
||||
this.metadata = new AspectMetadata(aspectClass, aspectName);
|
||||
}
|
||||
|
||||
|
||||
public final AspectMetadata getAspectMetadata() {
|
||||
return this.metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine a fallback order for the case that the aspect instance
|
||||
* does not express an instance-specific order through implementing
|
||||
* the {@link org.springframework.core.Ordered} interface.
|
||||
* <p>The default implementation simply returns <code>Ordered.LOWEST_PRECEDENCE</code>.
|
||||
* @param aspectClass the aspect class
|
||||
*/
|
||||
protected int getOrderForAspectClass(Class aspectClass) {
|
||||
Order order = (Order) aspectClass.getAnnotation(Order.class);
|
||||
if (order != null) {
|
||||
return order.value();
|
||||
}
|
||||
return Ordered.LOWEST_PRECEDENCE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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 org.springframework.aop.aspectj.SingletonAspectInstanceFactory;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
|
||||
/**
|
||||
* Implementation of {@link MetadataAwareAspectInstanceFactory} that is backed
|
||||
* by a specified singleton object, returning the same instance for every
|
||||
* {@link #getAspectInstance()} call.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see SimpleMetadataAwareAspectInstanceFactory
|
||||
*/
|
||||
public class SingletonMetadataAwareAspectInstanceFactory extends SingletonAspectInstanceFactory
|
||||
implements MetadataAwareAspectInstanceFactory {
|
||||
|
||||
private final AspectMetadata metadata;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new SingletonMetadataAwareAspectInstanceFactory for the given aspect.
|
||||
* @param aspectInstance the singleton aspect instance
|
||||
* @param aspectName the name of the aspect
|
||||
*/
|
||||
public SingletonMetadataAwareAspectInstanceFactory(Object aspectInstance, String aspectName) {
|
||||
super(aspectInstance);
|
||||
this.metadata = new AspectMetadata(aspectInstance.getClass(), aspectName);
|
||||
}
|
||||
|
||||
|
||||
public final AspectMetadata getAspectMetadata() {
|
||||
return this.metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the aspect class carries an
|
||||
* {@link org.springframework.core.annotation.Order} annotation,
|
||||
* falling back to <code>Ordered.LOWEST_PRECEDENCE</code>.
|
||||
* @see org.springframework.core.annotation.Order
|
||||
*/
|
||||
protected int getOrderForAspectClass(Class aspectClass) {
|
||||
Order order = (Order) aspectClass.getAnnotation(Order.class);
|
||||
if (order != null) {
|
||||
return order.value();
|
||||
}
|
||||
return Ordered.LOWEST_PRECEDENCE;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<html>
|
||||
<body>
|
||||
|
||||
Classes enabling AspectJ 5 @Annotated classes to be used in Spring AOP.
|
||||
|
||||
<p>Normally to be used through an AspectJAutoProxyCreator rather than directly.
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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.autoproxy;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.aopalliance.aop.Advice;
|
||||
import org.aspectj.util.PartialOrder;
|
||||
import org.aspectj.util.PartialOrder.PartialComparable;
|
||||
|
||||
import org.springframework.aop.Advisor;
|
||||
import org.springframework.aop.aspectj.AbstractAspectJAdvice;
|
||||
import org.springframework.aop.aspectj.AspectJPointcutAdvisor;
|
||||
import org.springframework.aop.aspectj.AspectJProxyUtils;
|
||||
import org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator;
|
||||
import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator}
|
||||
* subclass that exposes AspectJ's invocation context and understands AspectJ's rules
|
||||
* for advice precedence when multiple pieces of advice come from the same aspect.
|
||||
*
|
||||
* @author Adrian Colyer
|
||||
* @author Juergen Hoeller
|
||||
* @author Ramnivas Laddad
|
||||
* @since 2.0
|
||||
*/
|
||||
public class AspectJAwareAdvisorAutoProxyCreator extends AbstractAdvisorAutoProxyCreator {
|
||||
|
||||
private static final Comparator DEFAULT_PRECEDENCE_COMPARATOR = new AspectJPrecedenceComparator();
|
||||
|
||||
|
||||
/**
|
||||
* Sort the rest by AspectJ precedence. If two pieces of advice have
|
||||
* come from the same aspect they will have the same order.
|
||||
* Advice from the same aspect is then further ordered according to the
|
||||
* following rules:
|
||||
* <ul>
|
||||
* <li>if either of the pair is after advice, then the advice declared
|
||||
* last gets highest precedence (runs last)</li>
|
||||
* <li>otherwise the advice declared first gets highest precedence (runs first)</li>
|
||||
* </ul>
|
||||
* <p><b>Important:</b> Advisors are sorted in precedence order, from highest
|
||||
* precedence to lowest. "On the way in" to a join point, the highest precedence
|
||||
* advisor should run first. "On the way out" of a join point, the highest precedence
|
||||
* advisor should run last.
|
||||
*/
|
||||
protected List sortAdvisors(List advisors) {
|
||||
// build list for sorting
|
||||
List partiallyComparableAdvisors = new LinkedList();
|
||||
for (Iterator it = advisors.iterator(); it.hasNext();) {
|
||||
Advisor element = (Advisor) it.next();
|
||||
PartiallyComparableAdvisorHolder advisor =
|
||||
new PartiallyComparableAdvisorHolder(element, DEFAULT_PRECEDENCE_COMPARATOR);
|
||||
partiallyComparableAdvisors.add(advisor);
|
||||
}
|
||||
|
||||
// sort it
|
||||
List sorted = PartialOrder.sort(partiallyComparableAdvisors);
|
||||
if (sorted == null) {
|
||||
// TODO: work much harder to give a better error message here.
|
||||
throw new IllegalArgumentException("Advice precedence circularity error");
|
||||
}
|
||||
|
||||
// extract results again
|
||||
List result = new LinkedList();
|
||||
for (Iterator it = sorted.iterator(); it.hasNext();) {
|
||||
PartiallyComparableAdvisorHolder pcAdvisor = (PartiallyComparableAdvisorHolder) it.next();
|
||||
result.add(pcAdvisor.getAdvisor());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an {@link ExposeInvocationInterceptor} to the beginning of the advice chain.
|
||||
* These additional advices are needed when using AspectJ expression pointcuts
|
||||
* and when using AspectJ-style advice.
|
||||
*/
|
||||
protected void extendAdvisors(List candidateAdvisors) {
|
||||
AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(candidateAdvisors);
|
||||
}
|
||||
|
||||
protected boolean shouldSkip(Class beanClass, String beanName) {
|
||||
// TODO: Consider optimization by caching the list of the aspect names
|
||||
List candidtate = findCandidateAdvisors();
|
||||
for (Iterator it = candidtate.iterator(); it.hasNext();) {
|
||||
Advisor advisor = (Advisor) it.next();
|
||||
if (advisor instanceof AspectJPointcutAdvisor) {
|
||||
if(((AbstractAspectJAdvice) advisor.getAdvice()).getAspectName().equals(beanName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.shouldSkip(beanClass, beanName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements AspectJ PartialComparable interface for defining partial orderings.
|
||||
*/
|
||||
private static class PartiallyComparableAdvisorHolder implements PartialComparable {
|
||||
|
||||
private final Advisor advisor;
|
||||
|
||||
private final Comparator comparator;
|
||||
|
||||
public PartiallyComparableAdvisorHolder(Advisor advisor, Comparator comparator) {
|
||||
this.advisor = advisor;
|
||||
this.comparator = comparator;
|
||||
}
|
||||
|
||||
public int compareTo(Object obj) {
|
||||
Advisor otherAdvisor = ((PartiallyComparableAdvisorHolder) obj).advisor;
|
||||
return this.comparator.compare(this.advisor, otherAdvisor);
|
||||
}
|
||||
|
||||
public int fallbackCompareTo(Object obj) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public Advisor getAdvisor() {
|
||||
return this.advisor;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
Advice advice = this.advisor.getAdvice();
|
||||
sb.append(ClassUtils.getShortName(advice.getClass()));
|
||||
sb.append(": ");
|
||||
if (this.advisor instanceof Ordered) {
|
||||
sb.append("order " + ((Ordered) this.advisor).getOrder() + ", ");
|
||||
}
|
||||
if (advice instanceof AbstractAspectJAdvice) {
|
||||
AbstractAspectJAdvice ajAdvice = (AbstractAspectJAdvice) advice;
|
||||
sb.append(ajAdvice.getAspectName());
|
||||
sb.append(", declaration order ");
|
||||
sb.append(ajAdvice.getDeclarationOrder());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* Copyright 2002-2006 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.autoproxy;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
import org.springframework.aop.Advisor;
|
||||
import org.springframework.aop.aspectj.AspectJAopUtils;
|
||||
import org.springframework.aop.aspectj.AspectJPrecedenceInformation;
|
||||
import org.springframework.core.OrderComparator;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Orders AspectJ advice/advisors by precedence (<i>not</i> invocation order).
|
||||
*
|
||||
* <p>Given two pieces of advice, <code>a</code> and <code>b</code>:
|
||||
* <ul>
|
||||
* <li>if <code>a</code> and <code>b</code> are defined in different
|
||||
* aspects, then the advice in the aspect with the lowest order
|
||||
* value has the highest precedence</li>
|
||||
* <li>if <code>a</code> and <code>b</code> are defined in the same
|
||||
* aspect, then if one of <code>a</code> or <code>b</code> is a form of
|
||||
* after advice, then the advice declared last in the aspect has the
|
||||
* highest precedence. If neither <code>a</code> nor <code>b</code> is a
|
||||
* form of after advice, then the advice declared first in the aspect has
|
||||
* the highest precedence.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Important: Note that unlike a normal comparator a return of 0 means
|
||||
* we don't care about the ordering, not that the two elements must be sorted
|
||||
* identically. Used with AspectJ PartialOrder class.
|
||||
*
|
||||
* @author Adrian Colyer
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
class AspectJPrecedenceComparator implements Comparator {
|
||||
|
||||
private static final int HIGHER_PRECEDENCE = -1;
|
||||
private static final int SAME_PRECEDENCE = 0;
|
||||
private static final int LOWER_PRECEDENCE = 1;
|
||||
private static final int NOT_COMPARABLE = 0;
|
||||
|
||||
private final Comparator advisorComparator;
|
||||
|
||||
|
||||
/**
|
||||
* Create a default AspectJPrecedenceComparator.
|
||||
*/
|
||||
public AspectJPrecedenceComparator() {
|
||||
this.advisorComparator = new OrderComparator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a AspectJPrecedenceComparator, using the given Comparator
|
||||
* for comparing {@link org.springframework.aop.Advisor} instances.
|
||||
* @param advisorComparator the Comparator to use for Advisors
|
||||
*/
|
||||
public AspectJPrecedenceComparator(Comparator advisorComparator) {
|
||||
Assert.notNull(advisorComparator, "Advisor comparator must not be null");
|
||||
this.advisorComparator = advisorComparator;
|
||||
}
|
||||
|
||||
|
||||
public int compare(Object o1, Object o2) {
|
||||
if (!(o1 instanceof Advisor && o2 instanceof Advisor)) {
|
||||
throw new IllegalArgumentException(
|
||||
"AspectJPrecedenceComparator can only compare the order of Advisors, " +
|
||||
"but was passed [" + o1 + "] and [" + o2 + "]");
|
||||
}
|
||||
|
||||
Advisor advisor1 = (Advisor) o1;
|
||||
Advisor advisor2 = (Advisor) o2;
|
||||
|
||||
boolean oneOrOtherIsAfterAdvice =
|
||||
(AspectJAopUtils.isAfterAdvice(advisor1) || AspectJAopUtils.isAfterAdvice(advisor2));
|
||||
boolean oneOrOtherIsBeforeAdvice =
|
||||
(AspectJAopUtils.isBeforeAdvice(advisor1) || AspectJAopUtils.isBeforeAdvice(advisor2));
|
||||
if (oneOrOtherIsAfterAdvice && oneOrOtherIsBeforeAdvice) {
|
||||
return NOT_COMPARABLE;
|
||||
}
|
||||
else {
|
||||
int advisorPrecedence = this.advisorComparator.compare(advisor1, advisor2);
|
||||
if (advisorPrecedence == SAME_PRECEDENCE && declaredInSameAspect(advisor1, advisor2)) {
|
||||
advisorPrecedence = comparePrecedenceWithinAspect(advisor1, advisor2);
|
||||
}
|
||||
return advisorPrecedence;
|
||||
}
|
||||
}
|
||||
|
||||
private int comparePrecedenceWithinAspect(Advisor advisor1, Advisor advisor2) {
|
||||
boolean oneOrOtherIsAfterAdvice =
|
||||
(AspectJAopUtils.isAfterAdvice(advisor1) || AspectJAopUtils.isAfterAdvice(advisor2));
|
||||
int adviceDeclarationOrderDelta = getAspectDeclarationOrder(advisor1) - getAspectDeclarationOrder(advisor2);
|
||||
|
||||
if (oneOrOtherIsAfterAdvice) {
|
||||
// the advice declared last has higher precedence
|
||||
if (adviceDeclarationOrderDelta < 0) {
|
||||
// advice1 was declared before advice2
|
||||
// so advice1 has lower precedence
|
||||
return LOWER_PRECEDENCE;
|
||||
}
|
||||
else if (adviceDeclarationOrderDelta == 0) {
|
||||
return SAME_PRECEDENCE;
|
||||
}
|
||||
else {
|
||||
return HIGHER_PRECEDENCE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// the advice declared first has higher precedence
|
||||
if (adviceDeclarationOrderDelta < 0) {
|
||||
// advice1 was declared before advice2
|
||||
// so advice1 has higher precedence
|
||||
return HIGHER_PRECEDENCE;
|
||||
}
|
||||
else if (adviceDeclarationOrderDelta == 0) {
|
||||
return SAME_PRECEDENCE;
|
||||
}
|
||||
else {
|
||||
return LOWER_PRECEDENCE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean declaredInSameAspect(Advisor advisor1, Advisor advisor2) {
|
||||
if (!(hasAspectName(advisor1) && hasAspectName(advisor2))) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return getAspectName(advisor1).equals(getAspectName(advisor2));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasAspectName(Advisor anAdvisor) {
|
||||
return (anAdvisor instanceof AspectJPrecedenceInformation ||
|
||||
anAdvisor.getAdvice() instanceof AspectJPrecedenceInformation);
|
||||
}
|
||||
|
||||
// pre-condition is that hasAspectName returned true
|
||||
private String getAspectName(Advisor anAdvisor) {
|
||||
return AspectJAopUtils.getAspectJPrecedenceInformationFor(anAdvisor).getAspectName();
|
||||
}
|
||||
|
||||
private int getAspectDeclarationOrder(Advisor anAdvisor) {
|
||||
AspectJPrecedenceInformation precedenceInfo =
|
||||
AspectJAopUtils.getAspectJPrecedenceInformationFor(anAdvisor);
|
||||
if (precedenceInfo != null) {
|
||||
return precedenceInfo.getDeclarationOrder();
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<html>
|
||||
<body>
|
||||
|
||||
Base classes enabling auto-proxying based on AspectJ.
|
||||
Support for AspectJ annotation aspects resides in the "aspectj.annotation" package.
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,14 @@
|
||||
<html>
|
||||
<body>
|
||||
|
||||
AspectJ integration package. Includes Spring AOP advice implementations for AspectJ 5
|
||||
annotation-style methods, and an AspectJExpressionPointcut: a Spring AOP Pointcut
|
||||
implementation that allows use of the AspectJ pointcut expression language with the Spring AOP
|
||||
runtime framework.
|
||||
|
||||
<p>Note that use of this package does <i>not</i> require the use of the <code>ajc</code> compiler
|
||||
or AspectJ load-time weaver. It is intended to enable the use of a valuable subset of AspectJ
|
||||
functionality, with consistent semantics, with the proxy-based Spring AOP framework.
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright 2002-2006 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.config;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.w3c.dom.Node;
|
||||
|
||||
import org.springframework.aop.framework.ProxyFactoryBean;
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.ManagedList;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionDecorator;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Base implementation for {@link org.springframework.beans.factory.xml.BeanDefinitionDecorator BeanDefinitionDecorators} wishing
|
||||
* to add an {@link org.aopalliance.intercept.MethodInterceptor interceptor} to the resulting
|
||||
* bean.
|
||||
*
|
||||
* <p>This base class controls the creation of the {@link ProxyFactoryBean} bean definition
|
||||
* and wraps the original as an inner-bean definition for the <code>target</code> property of
|
||||
* {@link ProxyFactoryBean}.
|
||||
*
|
||||
* <p>Chaining is correctly handled, ensuring that only one {@link ProxyFactoryBean} definition
|
||||
* is created. If a previous {@link org.springframework.beans.factory.xml.BeanDefinitionDecorator} already created the {@link org.springframework.aop.framework.ProxyFactoryBean}
|
||||
* then the interceptor is simply added to the existing definition.
|
||||
*
|
||||
* <p>Subclasses have only to create the <code>BeanDefinition</code> to the interceptor they
|
||||
* wish to add.
|
||||
*
|
||||
* @author Rob Harrop
|
||||
* @since 2.0
|
||||
* @see org.aopalliance.intercept.MethodInterceptor
|
||||
*/
|
||||
public abstract class AbstractInterceptorDrivenBeanDefinitionDecorator implements BeanDefinitionDecorator {
|
||||
|
||||
public final BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definitionHolder, ParserContext parserContext) {
|
||||
BeanDefinitionRegistry registry = parserContext.getRegistry();
|
||||
|
||||
// get the root bean name - will be the name of the generated proxy factory bean
|
||||
String existingBeanName = definitionHolder.getBeanName();
|
||||
BeanDefinition existingDefinition = definitionHolder.getBeanDefinition();
|
||||
|
||||
// delegate to subclass for interceptor def
|
||||
BeanDefinition interceptorDefinition = createInterceptorDefinition(node);
|
||||
|
||||
// generate name and register the interceptor
|
||||
String interceptorName = existingBeanName + "." + getInterceptorNameSuffix(interceptorDefinition);
|
||||
BeanDefinitionReaderUtils.registerBeanDefinition(new BeanDefinitionHolder(interceptorDefinition, interceptorName), registry);
|
||||
|
||||
BeanDefinitionHolder result = definitionHolder;
|
||||
|
||||
if (!isProxyFactoryBeanDefinition(existingDefinition)) {
|
||||
|
||||
// create the proxy definitionHolder
|
||||
RootBeanDefinition proxyDefinition = new RootBeanDefinition();
|
||||
// create proxy factory bean definitionHolder
|
||||
proxyDefinition.setBeanClass(ProxyFactoryBean.class);
|
||||
|
||||
// set up property values
|
||||
MutablePropertyValues mpvs = new MutablePropertyValues();
|
||||
proxyDefinition.setPropertyValues(mpvs);
|
||||
|
||||
// set the target
|
||||
mpvs.addPropertyValue("target", existingDefinition);
|
||||
|
||||
// create the interceptor names list
|
||||
ManagedList interceptorList = new ManagedList();
|
||||
mpvs.addPropertyValue("interceptorNames", interceptorList);
|
||||
|
||||
result = new BeanDefinitionHolder(proxyDefinition, existingBeanName);
|
||||
}
|
||||
|
||||
addInterceptorNameToList(interceptorName, result.getBeanDefinition());
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
private void addInterceptorNameToList(String interceptorName, BeanDefinition beanDefinition) {
|
||||
List list = (List) beanDefinition.getPropertyValues().getPropertyValue("interceptorNames").getValue();
|
||||
list.add(interceptorName);
|
||||
}
|
||||
|
||||
private boolean isProxyFactoryBeanDefinition(BeanDefinition existingDefinition) {
|
||||
return existingDefinition.getBeanClassName().equals(ProxyFactoryBean.class.getName());
|
||||
}
|
||||
|
||||
protected String getInterceptorNameSuffix(BeanDefinition interceptorDefinition) {
|
||||
return StringUtils.uncapitalize(ClassUtils.getShortName(interceptorDefinition.getBeanClassName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses should implement this method to return the <code>BeanDefinition</code>
|
||||
* for the interceptor they wish to apply to the bean being decorated.
|
||||
*/
|
||||
protected abstract BeanDefinition createInterceptorDefinition(Node node);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2002-2006 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.config;
|
||||
|
||||
import org.springframework.beans.factory.parsing.ParseState;
|
||||
|
||||
/**
|
||||
* {@link ParseState} entry representing an advice element.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @since 2.0
|
||||
*/
|
||||
public class AdviceEntry implements ParseState.Entry {
|
||||
|
||||
private final String kind;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new instance of the {@link AdviceEntry} class.
|
||||
* @param kind the kind of advice represented by this entry (before, after, around, etc.)
|
||||
*/
|
||||
public AdviceEntry(String kind) {
|
||||
this.kind = kind;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Advice (" + this.kind + ")";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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.config;
|
||||
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanReference;
|
||||
import org.springframework.beans.factory.parsing.AbstractComponentDefinition;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.beans.factory.parsing.ComponentDefinition}
|
||||
* that bridges the gap between the advisor bean definition configured
|
||||
* by the <code><aop:advisor></code> tag and the component definition
|
||||
* infrastructure.
|
||||
*
|
||||
* @author Rob Harrop
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
public class AdvisorComponentDefinition extends AbstractComponentDefinition {
|
||||
|
||||
private final String advisorBeanName;
|
||||
|
||||
private final BeanDefinition advisorDefinition;
|
||||
|
||||
private String description;
|
||||
|
||||
private BeanReference[] beanReferences;
|
||||
|
||||
private BeanDefinition[] beanDefinitions;
|
||||
|
||||
|
||||
public AdvisorComponentDefinition(String advisorBeanName, BeanDefinition advisorDefinition) {
|
||||
this(advisorBeanName, advisorDefinition, null);
|
||||
}
|
||||
|
||||
public AdvisorComponentDefinition(
|
||||
String advisorBeanName, BeanDefinition advisorDefinition, BeanDefinition pointcutDefinition) {
|
||||
|
||||
Assert.notNull(advisorBeanName, "'advisorBeanName' must not be null");
|
||||
Assert.notNull(advisorDefinition, "'advisorDefinition' must not be null");
|
||||
this.advisorBeanName = advisorBeanName;
|
||||
this.advisorDefinition = advisorDefinition;
|
||||
unwrapDefinitions(advisorDefinition, pointcutDefinition);
|
||||
}
|
||||
|
||||
|
||||
private void unwrapDefinitions(BeanDefinition advisorDefinition, BeanDefinition pointcutDefinition) {
|
||||
MutablePropertyValues pvs = advisorDefinition.getPropertyValues();
|
||||
BeanReference adviceReference = (BeanReference) pvs.getPropertyValue("adviceBeanName").getValue();
|
||||
|
||||
if (pointcutDefinition != null) {
|
||||
this.beanReferences = new BeanReference[] {adviceReference};
|
||||
this.beanDefinitions = new BeanDefinition[] {advisorDefinition, pointcutDefinition};
|
||||
this.description = buildDescription(adviceReference, pointcutDefinition);
|
||||
}
|
||||
else {
|
||||
BeanReference pointcutReference = (BeanReference) pvs.getPropertyValue("pointcut").getValue();
|
||||
this.beanReferences = new BeanReference[] {adviceReference, pointcutReference};
|
||||
this.beanDefinitions = new BeanDefinition[] {advisorDefinition};
|
||||
this.description = buildDescription(adviceReference, pointcutReference);
|
||||
}
|
||||
}
|
||||
|
||||
private String buildDescription(BeanReference adviceReference, BeanDefinition pointcutDefinition) {
|
||||
return new StringBuffer("Advisor <advice(ref)='").
|
||||
append(adviceReference.getBeanName()).append("', pointcut(expression)=[").
|
||||
append(pointcutDefinition.getPropertyValues().getPropertyValue("expression").getValue()).
|
||||
append("]>").toString();
|
||||
}
|
||||
|
||||
private String buildDescription(BeanReference adviceReference, BeanReference pointcutReference) {
|
||||
return new StringBuffer("Advisor <advice(ref)='").
|
||||
append(adviceReference.getBeanName()).append("', pointcut(ref)='").
|
||||
append(pointcutReference.getBeanName()).append("'>").toString();
|
||||
}
|
||||
|
||||
|
||||
public String getName() {
|
||||
return this.advisorBeanName;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
public BeanDefinition[] getBeanDefinitions() {
|
||||
return this.beanDefinitions;
|
||||
}
|
||||
|
||||
public BeanReference[] getBeanReferences() {
|
||||
return this.beanReferences;
|
||||
}
|
||||
|
||||
public Object getSource() {
|
||||
return this.advisorDefinition.getSource();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2002-2006 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.config;
|
||||
|
||||
import org.springframework.beans.factory.parsing.ParseState;
|
||||
|
||||
/**
|
||||
* {@link ParseState} entry representing an advisor.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @since 2.0
|
||||
*/
|
||||
public class AdvisorEntry implements ParseState.Entry {
|
||||
|
||||
private final String name;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new instance of the {@link AdvisorEntry} class.
|
||||
* @param name the bean name of the advisor
|
||||
*/
|
||||
public AdvisorEntry(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Advisor '" + this.name + "'";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator;
|
||||
import org.springframework.aop.framework.autoproxy.InfrastructureAdvisorAutoProxyCreator;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.core.JdkVersion;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Utility class for handling registration of AOP auto-proxy creators.
|
||||
*
|
||||
* <p>Only a single auto-proxy creator can be registered yet multiple concrete
|
||||
* implementations are available. Therefore this class wraps a simple escalation
|
||||
* protocol, allowing classes to request a particular auto-proxy creator and know
|
||||
* that class, <code>or a subclass thereof</code>, will eventually be resident
|
||||
* in the application context.
|
||||
*
|
||||
* @author Rob Harrop
|
||||
* @author Juergen Hoeller
|
||||
* @author Mark Fisher
|
||||
* @since 2.5
|
||||
* @see AopNamespaceUtils
|
||||
*/
|
||||
public abstract class AopConfigUtils {
|
||||
|
||||
/**
|
||||
* The bean name of the internally managed auto-proxy creator.
|
||||
*/
|
||||
public static final String AUTO_PROXY_CREATOR_BEAN_NAME =
|
||||
"org.springframework.aop.config.internalAutoProxyCreator";
|
||||
|
||||
/**
|
||||
* The class name of the <code>AnnotationAwareAspectJAutoProxyCreator</code> class.
|
||||
* Only available with AspectJ and Java 5.
|
||||
*/
|
||||
private static final String ASPECTJ_ANNOTATION_AUTO_PROXY_CREATOR_CLASS_NAME =
|
||||
"org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator";
|
||||
|
||||
|
||||
/**
|
||||
* Stores the auto proxy creator classes in escalation order.
|
||||
*/
|
||||
private static final List APC_PRIORITY_LIST = new ArrayList();
|
||||
|
||||
/**
|
||||
* Setup the escalation list.
|
||||
*/
|
||||
static {
|
||||
APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class.getName());
|
||||
APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class.getName());
|
||||
APC_PRIORITY_LIST.add(ASPECTJ_ANNOTATION_AUTO_PROXY_CREATOR_CLASS_NAME);
|
||||
}
|
||||
|
||||
|
||||
public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
|
||||
return registerAutoProxyCreatorIfNecessary(registry, null);
|
||||
}
|
||||
|
||||
public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
|
||||
return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);
|
||||
}
|
||||
|
||||
public static BeanDefinition registerAspectJAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
|
||||
return registerAspectJAutoProxyCreatorIfNecessary(registry, null);
|
||||
}
|
||||
|
||||
public static BeanDefinition registerAspectJAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
|
||||
return registerOrEscalateApcAsRequired(AspectJAwareAdvisorAutoProxyCreator.class, registry, source);
|
||||
}
|
||||
|
||||
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
|
||||
return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, null);
|
||||
}
|
||||
|
||||
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
|
||||
Class cls = getAspectJAnnotationAutoProxyCreatorClassIfPossible();
|
||||
return registerOrEscalateApcAsRequired(cls, registry, source);
|
||||
}
|
||||
|
||||
public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) {
|
||||
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
|
||||
BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
|
||||
definition.getPropertyValues().addPropertyValue("proxyTargetClass", Boolean.TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static BeanDefinition registerOrEscalateApcAsRequired(Class cls, BeanDefinitionRegistry registry, Object source) {
|
||||
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
|
||||
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
|
||||
BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
|
||||
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
|
||||
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
|
||||
int requiredPriority = findPriorityForClass(cls.getName());
|
||||
if (currentPriority < requiredPriority) {
|
||||
apcDefinition.setBeanClassName(cls.getName());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
|
||||
beanDefinition.setSource(source);
|
||||
beanDefinition.getPropertyValues().addPropertyValue("order", new Integer(Ordered.HIGHEST_PRECEDENCE));
|
||||
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
|
||||
return beanDefinition;
|
||||
}
|
||||
|
||||
private static Class getAspectJAnnotationAutoProxyCreatorClassIfPossible() {
|
||||
if (JdkVersion.getMajorJavaVersion() < JdkVersion.JAVA_15) {
|
||||
throw new IllegalStateException(
|
||||
"AnnotationAwareAspectJAutoProxyCreator is only available on Java 1.5 and higher");
|
||||
}
|
||||
try {
|
||||
return ClassUtils.forName(
|
||||
ASPECTJ_ANNOTATION_AUTO_PROXY_CREATOR_CLASS_NAME, AopConfigUtils.class.getClassLoader());
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new IllegalStateException("Unable to load Java 1.5 dependent class [" +
|
||||
ASPECTJ_ANNOTATION_AUTO_PROXY_CREATOR_CLASS_NAME + "]", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static int findPriorityForClass(String className) {
|
||||
Assert.notNull(className, "Class name must not be null");
|
||||
for (int i = 0; i < APC_PRIORITY_LIST.size(); i++) {
|
||||
String str = (String) APC_PRIORITY_LIST.get(i);
|
||||
if (className.equals(str)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException(
|
||||
"Class name [" + className + "] is not a known auto-proxy creator class");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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.config;
|
||||
|
||||
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
|
||||
|
||||
/**
|
||||
* <code>NamespaceHandler</code> for the <code>aop</code> namespace.
|
||||
*
|
||||
* <p>Provides a {@link org.springframework.beans.factory.xml.BeanDefinitionParser} for the
|
||||
* <code><aop:config></code> tag. A <code>config</code> tag can include nested
|
||||
* <code>pointcut</code>, <code>advisor</code> and <code>aspect</code> tags.
|
||||
*
|
||||
* <p>The <code>pointcut</code> tag allows for creation of named
|
||||
* {@link AspectJExpressionPointcut} beans using a simple syntax:
|
||||
* <pre class="code">
|
||||
* <aop:pointcut id="getNameCalls" expression="execution(* *..ITestBean.getName(..))"/>
|
||||
* </pre>
|
||||
*
|
||||
* <p>Using the <code>advisor</code> tag you can configure an {@link org.springframework.aop.Advisor}
|
||||
* and have it applied to all relevant beans in you {@link org.springframework.beans.factory.BeanFactory}
|
||||
* automatically. The <code>advisor</code> tag supports both in-line and referenced
|
||||
* {@link org.springframework.aop.Pointcut Pointcuts}:
|
||||
*
|
||||
* <pre class="code">
|
||||
* <aop:advisor id="getAgeAdvisor"
|
||||
* pointcut="execution(* *..ITestBean.getAge(..))"
|
||||
* advice-ref="getAgeCounter"/>
|
||||
*
|
||||
* <aop:advisor id="getNameAdvisor"
|
||||
* pointcut-ref="getNameCalls"
|
||||
* advice-ref="getNameCounter"/></pre>
|
||||
*
|
||||
* @author Rob Harrop
|
||||
* @author Adrian Colyer
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
public class AopNamespaceHandler extends NamespaceHandlerSupport {
|
||||
|
||||
/**
|
||||
* Register the {@link BeanDefinitionParser BeanDefinitionParsers} for the
|
||||
* '<code>config</code>', '<code>spring-configured</code>', '<code>aspectj-autoproxy</code>'
|
||||
* and '<code>scoped-proxy</code>' tags.
|
||||
*/
|
||||
public void init() {
|
||||
// In 2.0 XSD as well as in 2.1 XSD.
|
||||
registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
|
||||
registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
|
||||
registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
|
||||
|
||||
// Only in 2.0 XSD: moved to context namespace as of 2.1
|
||||
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
|
||||
/**
|
||||
* Utility class for handling registration of auto-proxy creators used internally
|
||||
* by the '<code>aop</code>' namespace tags.
|
||||
*
|
||||
* <p>Only a single auto-proxy creator can be registered and multiple tags may wish
|
||||
* to register different concrete implementations. As such this class delegates to
|
||||
* {@link AopConfigUtils} which wraps a simple escalation protocol. Therefore classes
|
||||
* may request a particular auto-proxy creator and know that class, <i>or a subclass
|
||||
* thereof</i>, will eventually be resident in the application context.
|
||||
*
|
||||
* @author Rob Harrop
|
||||
* @author Juergen Hoeller
|
||||
* @author Mark Fisher
|
||||
* @since 2.0
|
||||
* @see AopConfigUtils
|
||||
*/
|
||||
public abstract class AopNamespaceUtils {
|
||||
|
||||
/**
|
||||
* The <code>proxy-target-class</code> attribute as found on AOP-related XML tags.
|
||||
*/
|
||||
public static final String PROXY_TARGET_CLASS_ATTRIBUTE = "proxy-target-class";
|
||||
|
||||
|
||||
public static void registerAutoProxyCreatorIfNecessary(
|
||||
ParserContext parserContext, Element sourceElement) {
|
||||
|
||||
BeanDefinition beanDefinition = AopConfigUtils.registerAutoProxyCreatorIfNecessary(
|
||||
parserContext.getRegistry(), parserContext.extractSource(sourceElement));
|
||||
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
|
||||
registerComponentIfNecessary(beanDefinition, parserContext);
|
||||
}
|
||||
|
||||
public static void registerAspectJAutoProxyCreatorIfNecessary(
|
||||
ParserContext parserContext, Element sourceElement) {
|
||||
|
||||
BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAutoProxyCreatorIfNecessary(
|
||||
parserContext.getRegistry(), parserContext.extractSource(sourceElement));
|
||||
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
|
||||
registerComponentIfNecessary(beanDefinition, parserContext);
|
||||
}
|
||||
|
||||
public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
|
||||
ParserContext parserContext, Element sourceElement) {
|
||||
|
||||
BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
|
||||
parserContext.getRegistry(), parserContext.extractSource(sourceElement));
|
||||
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
|
||||
registerComponentIfNecessary(beanDefinition, parserContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since Spring 2.5, in favor of
|
||||
* {@link #registerAutoProxyCreatorIfNecessary(ParserContext, Element)} and
|
||||
* {@link AopConfigUtils#registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry, Object)}
|
||||
*/
|
||||
public static void registerAutoProxyCreatorIfNecessary(ParserContext parserContext, Object source) {
|
||||
BeanDefinition beanDefinition = AopConfigUtils.registerAutoProxyCreatorIfNecessary(
|
||||
parserContext.getRegistry(), source);
|
||||
registerComponentIfNecessary(beanDefinition, parserContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since Spring 2.5, in favor of
|
||||
* {@link AopConfigUtils#forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry)}
|
||||
*/
|
||||
public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) {
|
||||
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
|
||||
}
|
||||
|
||||
|
||||
private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, Element sourceElement) {
|
||||
if (sourceElement != null) {
|
||||
boolean proxyTargetClass = Boolean.valueOf(
|
||||
sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE)).booleanValue();
|
||||
if (proxyTargetClass) {
|
||||
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void registerComponentIfNecessary(BeanDefinition beanDefinition, ParserContext parserContext) {
|
||||
if (beanDefinition != null) {
|
||||
BeanComponentDefinition componentDefinition =
|
||||
new BeanComponentDefinition(beanDefinition, AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME);
|
||||
parserContext.registerComponent(componentDefinition);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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.config;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanReference;
|
||||
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.beans.factory.parsing.ComponentDefinition}
|
||||
* that holds an aspect definition, including its nested pointcuts.
|
||||
*
|
||||
* @author Rob Harrop
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see #getNestedComponents()
|
||||
* @see PointcutComponentDefinition
|
||||
*/
|
||||
public class AspectComponentDefinition extends CompositeComponentDefinition {
|
||||
|
||||
private final BeanDefinition[] beanDefinitions;
|
||||
|
||||
private final BeanReference[] beanReferences;
|
||||
|
||||
|
||||
public AspectComponentDefinition(
|
||||
String aspectName, BeanDefinition[] beanDefinitions, BeanReference[] beanReferences, Object source) {
|
||||
|
||||
super(aspectName, source);
|
||||
this.beanDefinitions = (beanDefinitions != null ? beanDefinitions : new BeanDefinition[0]);
|
||||
this.beanReferences = (beanReferences != null ? beanReferences : new BeanReference[0]);
|
||||
}
|
||||
|
||||
|
||||
public BeanDefinition[] getBeanDefinitions() {
|
||||
return this.beanDefinitions;
|
||||
}
|
||||
|
||||
public BeanReference[] getBeanReferences() {
|
||||
return this.beanReferences;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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.config;
|
||||
|
||||
import org.springframework.beans.factory.parsing.ParseState;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link ParseState} entry representing an aspect.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
public class AspectEntry implements ParseState.Entry {
|
||||
|
||||
private final String id;
|
||||
|
||||
private final String ref;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new AspectEntry.
|
||||
* @param id the id of the aspect element
|
||||
* @param ref the bean name referenced by this aspect element
|
||||
*/
|
||||
public AspectEntry(String id, String ref) {
|
||||
this.id = id;
|
||||
this.ref = ref;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Aspect: " + (StringUtils.hasLength(this.id) ? "id='" + this.id + "'" : "ref='" + this.ref + "'");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.TypedStringValue;
|
||||
import org.springframework.beans.factory.support.ManagedList;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
|
||||
/**
|
||||
* {@link BeanDefinitionParser} for the <code>aspectj-autoproxy</code> tag,
|
||||
* enabling the automatic application of @AspectJ-style aspects found in
|
||||
* the {@link org.springframework.beans.factory.BeanFactory}.
|
||||
*
|
||||
* @author Rob Harrop
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
class AspectJAutoProxyBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
public BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||
AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
|
||||
extendBeanDefinition(element, parserContext);
|
||||
return null;
|
||||
}
|
||||
|
||||
private void extendBeanDefinition(Element element, ParserContext parserContext) {
|
||||
BeanDefinition beanDef =
|
||||
parserContext.getRegistry().getBeanDefinition(AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME);
|
||||
if (element.hasChildNodes()) {
|
||||
addIncludePatterns(element, parserContext, beanDef);
|
||||
}
|
||||
}
|
||||
|
||||
private void addIncludePatterns(Element element, ParserContext parserContext, BeanDefinition beanDef) {
|
||||
ManagedList includePatterns = new ManagedList();
|
||||
NodeList childNodes = element.getChildNodes();
|
||||
for (int i = 0; i < childNodes.getLength(); i++) {
|
||||
Node node = childNodes.item(i);
|
||||
if (node instanceof Element) {
|
||||
Element includeElement = (Element) node;
|
||||
TypedStringValue valueHolder = new TypedStringValue(includeElement.getAttribute("name"));
|
||||
valueHolder.setSource(parserContext.extractSource(includeElement));
|
||||
includePatterns.add(valueHolder);
|
||||
}
|
||||
}
|
||||
if (!includePatterns.isEmpty()) {
|
||||
includePatterns.setSource(parserContext.extractSource(element));
|
||||
beanDef.getPropertyValues().addPropertyValue("includePatterns", includePatterns);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,546 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import org.springframework.aop.aspectj.AspectJAfterAdvice;
|
||||
import org.springframework.aop.aspectj.AspectJAfterReturningAdvice;
|
||||
import org.springframework.aop.aspectj.AspectJAfterThrowingAdvice;
|
||||
import org.springframework.aop.aspectj.AspectJAroundAdvice;
|
||||
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
|
||||
import org.springframework.aop.aspectj.AspectJMethodBeforeAdvice;
|
||||
import org.springframework.aop.aspectj.AspectJPointcutAdvisor;
|
||||
import org.springframework.aop.aspectj.DeclareParentsAdvisor;
|
||||
import org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanReference;
|
||||
import org.springframework.beans.factory.config.ConstructorArgumentValues;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanNameReference;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
|
||||
import org.springframework.beans.factory.parsing.ParseState;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
|
||||
/**
|
||||
* {@link BeanDefinitionParser} for the <code><aop:config></code> tag.
|
||||
*
|
||||
* @author Rob Harrop
|
||||
* @author Juergen Hoeller
|
||||
* @author Adrian Colyer
|
||||
* @author Mark Fisher
|
||||
* @author Ramnivas Laddad
|
||||
* @since 2.0
|
||||
*/
|
||||
class ConfigBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
private static final String ASPECT = "aspect";
|
||||
|
||||
private static final String EXPRESSION = "expression";
|
||||
|
||||
private static final String ID = "id";
|
||||
|
||||
private static final String POINTCUT = "pointcut";
|
||||
|
||||
private static final String ADVICE_BEAN_NAME = "adviceBeanName";
|
||||
|
||||
private static final String ADVISOR = "advisor";
|
||||
|
||||
private static final String ADVICE_REF = "advice-ref";
|
||||
|
||||
private static final String POINTCUT_REF = "pointcut-ref";
|
||||
|
||||
private static final String REF = "ref";
|
||||
|
||||
private static final String BEFORE = "before";
|
||||
|
||||
private static final String DECLARE_PARENTS = "declare-parents";
|
||||
|
||||
private static final String TYPE_PATTERN = "types-matching";
|
||||
|
||||
private static final String DEFAULT_IMPL = "default-impl";
|
||||
|
||||
private static final String DELEGATE_REF = "delegate-ref";
|
||||
|
||||
private static final String IMPLEMENT_INTERFACE = "implement-interface";
|
||||
|
||||
private static final String AFTER = "after";
|
||||
|
||||
private static final String AFTER_RETURNING_ELEMENT = "after-returning";
|
||||
|
||||
private static final String AFTER_THROWING_ELEMENT = "after-throwing";
|
||||
|
||||
private static final String AROUND = "around";
|
||||
|
||||
private static final String RETURNING = "returning";
|
||||
|
||||
private static final String RETURNING_PROPERTY = "returningName";
|
||||
|
||||
private static final String THROWING = "throwing";
|
||||
|
||||
private static final String THROWING_PROPERTY = "throwingName";
|
||||
|
||||
private static final String ARG_NAMES = "arg-names";
|
||||
|
||||
private static final String ARG_NAMES_PROPERTY = "argumentNames";
|
||||
|
||||
private static final String ASPECT_NAME_PROPERTY = "aspectName";
|
||||
|
||||
private static final String DECLARATION_ORDER_PROPERTY = "declarationOrder";
|
||||
|
||||
private static final String ORDER_PROPERTY = "order";
|
||||
|
||||
private static final int METHOD_INDEX = 0;
|
||||
|
||||
private static final int POINTCUT_INDEX = 1;
|
||||
|
||||
private static final int ASPECT_INSTANCE_FACTORY_INDEX = 2;
|
||||
|
||||
|
||||
private ParseState parseState = new ParseState();
|
||||
|
||||
|
||||
public BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||
CompositeComponentDefinition compositeDef =
|
||||
new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
|
||||
parserContext.pushContainingComponent(compositeDef);
|
||||
|
||||
configureAutoProxyCreator(parserContext, element);
|
||||
|
||||
NodeList childNodes = element.getChildNodes();
|
||||
for (int i = 0; i < childNodes.getLength(); i++) {
|
||||
Node node = childNodes.item(i);
|
||||
if (node.getNodeType() == Node.ELEMENT_NODE) {
|
||||
String localName = node.getLocalName();
|
||||
if (POINTCUT.equals(localName)) {
|
||||
parsePointcut((Element) node, parserContext);
|
||||
}
|
||||
else if (ADVISOR.equals(localName)) {
|
||||
parseAdvisor((Element) node, parserContext);
|
||||
}
|
||||
else if (ASPECT.equals(localName)) {
|
||||
parseAspect((Element) node, parserContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parserContext.popAndRegisterContainingComponent();
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the auto proxy creator needed to support the {@link BeanDefinition BeanDefinitions}
|
||||
* created by the '<code><aop:config/></code>' tag. Will force class proxying if the
|
||||
* '<code>proxy-target-class</code>' attribute is set to '<code>true</code>'.
|
||||
* @see AopNamespaceUtils
|
||||
*/
|
||||
private void configureAutoProxyCreator(ParserContext parserContext, Element element) {
|
||||
AopNamespaceUtils.registerAspectJAutoProxyCreatorIfNecessary(parserContext, element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the supplied <code><advisor></code> element and registers the resulting
|
||||
* {@link org.springframework.aop.Advisor} and any resulting {@link org.springframework.aop.Pointcut}
|
||||
* with the supplied {@link BeanDefinitionRegistry}.
|
||||
*/
|
||||
private void parseAdvisor(Element advisorElement, ParserContext parserContext) {
|
||||
AbstractBeanDefinition advisorDef = createAdvisorBeanDefinition(advisorElement, parserContext);
|
||||
String id = advisorElement.getAttribute(ID);
|
||||
|
||||
try {
|
||||
this.parseState.push(new AdvisorEntry(id));
|
||||
String advisorBeanName = id;
|
||||
if (StringUtils.hasText(advisorBeanName)) {
|
||||
parserContext.getRegistry().registerBeanDefinition(advisorBeanName, advisorDef);
|
||||
}
|
||||
else {
|
||||
advisorBeanName = parserContext.getReaderContext().registerWithGeneratedName(advisorDef);
|
||||
}
|
||||
|
||||
Object pointcut = parsePointcutProperty(advisorElement, parserContext);
|
||||
if (pointcut instanceof BeanDefinition) {
|
||||
advisorDef.getPropertyValues().addPropertyValue(POINTCUT, pointcut);
|
||||
parserContext.registerComponent(
|
||||
new AdvisorComponentDefinition(advisorBeanName, advisorDef, (BeanDefinition) pointcut));
|
||||
}
|
||||
else if (pointcut instanceof String) {
|
||||
advisorDef.getPropertyValues().addPropertyValue(POINTCUT, new RuntimeBeanReference((String) pointcut));
|
||||
parserContext.registerComponent(
|
||||
new AdvisorComponentDefinition(advisorBeanName, advisorDef));
|
||||
}
|
||||
}
|
||||
finally {
|
||||
this.parseState.pop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link RootBeanDefinition} for the advisor described in the supplied. Does <strong>not</strong>
|
||||
* parse any associated '<code>pointcut</code>' or '<code>pointcut-ref</code>' attributes.
|
||||
*/
|
||||
private AbstractBeanDefinition createAdvisorBeanDefinition(Element advisorElement, ParserContext parserContext) {
|
||||
RootBeanDefinition advisorDefinition = new RootBeanDefinition(DefaultBeanFactoryPointcutAdvisor.class);
|
||||
advisorDefinition.setSource(parserContext.extractSource(advisorElement));
|
||||
|
||||
String adviceRef = advisorElement.getAttribute(ADVICE_REF);
|
||||
if (!StringUtils.hasText(adviceRef)) {
|
||||
parserContext.getReaderContext().error(
|
||||
"'advice-ref' attribute contains empty value.", advisorElement, this.parseState.snapshot());
|
||||
}
|
||||
else {
|
||||
advisorDefinition.getPropertyValues().addPropertyValue(
|
||||
ADVICE_BEAN_NAME, new RuntimeBeanNameReference(adviceRef));
|
||||
}
|
||||
|
||||
if (advisorElement.hasAttribute(ORDER_PROPERTY)) {
|
||||
advisorDefinition.getPropertyValues().addPropertyValue(
|
||||
ORDER_PROPERTY, advisorElement.getAttribute(ORDER_PROPERTY));
|
||||
}
|
||||
|
||||
return advisorDefinition;
|
||||
}
|
||||
|
||||
private void parseAspect(Element aspectElement, ParserContext parserContext) {
|
||||
String aspectId = aspectElement.getAttribute(ID);
|
||||
String aspectName = aspectElement.getAttribute(REF);
|
||||
|
||||
try {
|
||||
this.parseState.push(new AspectEntry(aspectId, aspectName));
|
||||
List beanDefinitions = new ArrayList();
|
||||
List beanReferences = new ArrayList();
|
||||
|
||||
List declareParents = DomUtils.getChildElementsByTagName(aspectElement, DECLARE_PARENTS);
|
||||
for (int i = METHOD_INDEX; i < declareParents.size(); i++) {
|
||||
Element declareParentsElement = (Element) declareParents.get(i);
|
||||
beanDefinitions.add(parseDeclareParents(declareParentsElement, parserContext));
|
||||
}
|
||||
|
||||
// We have to parse "advice" and all the advice kinds in one loop, to get the
|
||||
// ordering semantics right.
|
||||
NodeList nodeList = aspectElement.getChildNodes();
|
||||
boolean adviceFoundAlready = false;
|
||||
for (int i = 0; i < nodeList.getLength(); i++) {
|
||||
Node node = nodeList.item(i);
|
||||
if (isAdviceNode(node)) {
|
||||
if (!adviceFoundAlready) {
|
||||
adviceFoundAlready = true;
|
||||
if (!StringUtils.hasText(aspectName)) {
|
||||
parserContext.getReaderContext().error(
|
||||
"<aspect> tag needs aspect bean reference via 'ref' attribute when declaring advices.",
|
||||
aspectElement, this.parseState.snapshot());
|
||||
return;
|
||||
}
|
||||
beanReferences.add(new RuntimeBeanReference(aspectName));
|
||||
}
|
||||
AbstractBeanDefinition advisorDefinition = parseAdvice(
|
||||
aspectName, i, aspectElement, (Element) node, parserContext, beanDefinitions, beanReferences);
|
||||
beanDefinitions.add(advisorDefinition);
|
||||
}
|
||||
}
|
||||
|
||||
AspectComponentDefinition aspectComponentDefinition = createAspectComponentDefinition(
|
||||
aspectElement, aspectId, beanDefinitions, beanReferences, parserContext);
|
||||
parserContext.pushContainingComponent(aspectComponentDefinition);
|
||||
|
||||
List pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT);
|
||||
for (int i = 0; i < pointcuts.size(); i++) {
|
||||
Element pointcutElement = (Element) pointcuts.get(i);
|
||||
parsePointcut(pointcutElement, parserContext);
|
||||
}
|
||||
|
||||
parserContext.popAndRegisterContainingComponent();
|
||||
}
|
||||
finally {
|
||||
this.parseState.pop();
|
||||
}
|
||||
}
|
||||
|
||||
private AspectComponentDefinition createAspectComponentDefinition(
|
||||
Element aspectElement, String aspectId, List beanDefs, List beanRefs, ParserContext parserContext) {
|
||||
|
||||
BeanDefinition[] beanDefArray = (BeanDefinition[]) beanDefs.toArray(new BeanDefinition[beanDefs.size()]);
|
||||
BeanReference[] beanRefArray = (BeanReference[]) beanRefs.toArray(new BeanReference[beanRefs.size()]);
|
||||
Object source = parserContext.extractSource(aspectElement);
|
||||
return new AspectComponentDefinition(aspectId, beanDefArray, beanRefArray, source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return <code>true</code> if the supplied node describes an advice type. May be one of:
|
||||
* '<code>before</code>', '<code>after</code>', '<code>after-returning</code>',
|
||||
* '<code>after-throwing</code>' or '<code>around</code>'.
|
||||
*/
|
||||
private boolean isAdviceNode(Node aNode) {
|
||||
if (!(aNode instanceof Element)) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
String name = aNode.getLocalName();
|
||||
return (BEFORE.equals(name) || AFTER.equals(name) || AFTER_RETURNING_ELEMENT.equals(name) ||
|
||||
AFTER_THROWING_ELEMENT.equals(name) || AROUND.equals(name));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a '<code>declare-parents</code>' element and register the appropriate
|
||||
* DeclareParentsAdvisor with the BeanDefinitionRegistry encapsulated in the
|
||||
* supplied ParserContext.
|
||||
*/
|
||||
private AbstractBeanDefinition parseDeclareParents(Element declareParentsElement, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(DeclareParentsAdvisor.class);
|
||||
builder.addConstructorArgValue(declareParentsElement.getAttribute(IMPLEMENT_INTERFACE));
|
||||
builder.addConstructorArgValue(declareParentsElement.getAttribute(TYPE_PATTERN));
|
||||
|
||||
String defaultImpl = declareParentsElement.getAttribute(DEFAULT_IMPL);
|
||||
String delegateRef = declareParentsElement.getAttribute(DELEGATE_REF);
|
||||
|
||||
if (StringUtils.hasText(defaultImpl) && !StringUtils.hasText(delegateRef)) {
|
||||
builder.addConstructorArgValue(defaultImpl);
|
||||
}
|
||||
else if (StringUtils.hasText(delegateRef) && !StringUtils.hasText(defaultImpl)) {
|
||||
builder.addConstructorArgReference(delegateRef);
|
||||
}
|
||||
else {
|
||||
parserContext.getReaderContext().error(
|
||||
"Exactly one of the " + DEFAULT_IMPL + " or " + DELEGATE_REF + " attributes must be specified",
|
||||
declareParentsElement, this.parseState.snapshot());
|
||||
}
|
||||
|
||||
AbstractBeanDefinition definition = builder.getBeanDefinition();
|
||||
definition.setSource(parserContext.extractSource(declareParentsElement));
|
||||
parserContext.getReaderContext().registerWithGeneratedName(definition);
|
||||
return definition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses one of '<code>before</code>', '<code>after</code>', '<code>after-returning</code>',
|
||||
* '<code>after-throwing</code>' or '<code>around</code>' and registers the resulting
|
||||
* BeanDefinition with the supplied BeanDefinitionRegistry.
|
||||
* @return the generated advice RootBeanDefinition
|
||||
*/
|
||||
private AbstractBeanDefinition parseAdvice(
|
||||
String aspectName, int order, Element aspectElement, Element adviceElement, ParserContext parserContext,
|
||||
List beanDefinitions, List beanReferences) {
|
||||
|
||||
try {
|
||||
this.parseState.push(new AdviceEntry(adviceElement.getLocalName()));
|
||||
|
||||
// create the method factory bean
|
||||
RootBeanDefinition methodDefinition = new RootBeanDefinition(MethodLocatingFactoryBean.class);
|
||||
methodDefinition.getPropertyValues().addPropertyValue("targetBeanName", aspectName);
|
||||
methodDefinition.getPropertyValues().addPropertyValue("methodName", adviceElement.getAttribute("method"));
|
||||
methodDefinition.setSynthetic(true);
|
||||
|
||||
// create instance factory definition
|
||||
RootBeanDefinition aspectFactoryDef =
|
||||
new RootBeanDefinition(SimpleBeanFactoryAwareAspectInstanceFactory.class);
|
||||
aspectFactoryDef.getPropertyValues().addPropertyValue("aspectBeanName", aspectName);
|
||||
aspectFactoryDef.setSynthetic(true);
|
||||
|
||||
// register the pointcut
|
||||
AbstractBeanDefinition adviceDef = createAdviceDefinition(
|
||||
adviceElement, parserContext, aspectName, order, methodDefinition, aspectFactoryDef,
|
||||
beanDefinitions, beanReferences);
|
||||
|
||||
// configure the advisor
|
||||
RootBeanDefinition advisorDefinition = new RootBeanDefinition(AspectJPointcutAdvisor.class);
|
||||
advisorDefinition.setSource(parserContext.extractSource(adviceElement));
|
||||
advisorDefinition.getConstructorArgumentValues().addGenericArgumentValue(adviceDef);
|
||||
if (aspectElement.hasAttribute(ORDER_PROPERTY)) {
|
||||
advisorDefinition.getPropertyValues().addPropertyValue(
|
||||
ORDER_PROPERTY, aspectElement.getAttribute(ORDER_PROPERTY));
|
||||
}
|
||||
|
||||
// register the final advisor
|
||||
parserContext.getReaderContext().registerWithGeneratedName(advisorDefinition);
|
||||
|
||||
return advisorDefinition;
|
||||
}
|
||||
finally {
|
||||
this.parseState.pop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the RootBeanDefinition for a POJO advice bean. Also causes pointcut
|
||||
* parsing to occur so that the pointcut may be associate with the advice bean.
|
||||
* This same pointcut is also configured as the pointcut for the enclosing
|
||||
* Advisor definition using the supplied MutablePropertyValues.
|
||||
*/
|
||||
private AbstractBeanDefinition createAdviceDefinition(
|
||||
Element adviceElement, ParserContext parserContext, String aspectName, int order,
|
||||
RootBeanDefinition methodDef, RootBeanDefinition aspectFactoryDef, List beanDefinitions, List beanReferences) {
|
||||
|
||||
RootBeanDefinition adviceDefinition = new RootBeanDefinition(getAdviceClass(adviceElement));
|
||||
adviceDefinition.setSource(parserContext.extractSource(adviceElement));
|
||||
|
||||
adviceDefinition.getPropertyValues().addPropertyValue(
|
||||
ASPECT_NAME_PROPERTY, aspectName);
|
||||
adviceDefinition.getPropertyValues().addPropertyValue(
|
||||
DECLARATION_ORDER_PROPERTY, new Integer(order));
|
||||
|
||||
if (adviceElement.hasAttribute(RETURNING)) {
|
||||
adviceDefinition.getPropertyValues().addPropertyValue(
|
||||
RETURNING_PROPERTY, adviceElement.getAttribute(RETURNING));
|
||||
}
|
||||
if (adviceElement.hasAttribute(THROWING)) {
|
||||
adviceDefinition.getPropertyValues().addPropertyValue(
|
||||
THROWING_PROPERTY, adviceElement.getAttribute(THROWING));
|
||||
}
|
||||
if (adviceElement.hasAttribute(ARG_NAMES)) {
|
||||
adviceDefinition.getPropertyValues().addPropertyValue(
|
||||
ARG_NAMES_PROPERTY, adviceElement.getAttribute(ARG_NAMES));
|
||||
}
|
||||
|
||||
ConstructorArgumentValues cav = adviceDefinition.getConstructorArgumentValues();
|
||||
cav.addIndexedArgumentValue(METHOD_INDEX, methodDef);
|
||||
|
||||
Object pointcut = parsePointcutProperty(adviceElement, parserContext);
|
||||
if (pointcut instanceof BeanDefinition) {
|
||||
cav.addIndexedArgumentValue(POINTCUT_INDEX, pointcut);
|
||||
beanDefinitions.add(pointcut);
|
||||
}
|
||||
else if (pointcut instanceof String) {
|
||||
RuntimeBeanReference pointcutRef = new RuntimeBeanReference((String) pointcut);
|
||||
cav.addIndexedArgumentValue(POINTCUT_INDEX, pointcutRef);
|
||||
beanReferences.add(pointcutRef);
|
||||
}
|
||||
|
||||
cav.addIndexedArgumentValue(ASPECT_INSTANCE_FACTORY_INDEX, aspectFactoryDef);
|
||||
|
||||
return adviceDefinition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the advice implementation class corresponding to the supplied {@link Element}.
|
||||
*/
|
||||
private Class getAdviceClass(Element adviceElement) {
|
||||
String elementName = adviceElement.getLocalName();
|
||||
if (BEFORE.equals(elementName)) {
|
||||
return AspectJMethodBeforeAdvice.class;
|
||||
}
|
||||
else if (AFTER.equals(elementName)) {
|
||||
return AspectJAfterAdvice.class;
|
||||
}
|
||||
else if (AFTER_RETURNING_ELEMENT.equals(elementName)) {
|
||||
return AspectJAfterReturningAdvice.class;
|
||||
}
|
||||
else if (AFTER_THROWING_ELEMENT.equals(elementName)) {
|
||||
return AspectJAfterThrowingAdvice.class;
|
||||
}
|
||||
else if (AROUND.equals(elementName)) {
|
||||
return AspectJAroundAdvice.class;
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Unknown advice kind [" + elementName + "].");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the supplied <code><pointcut></code> and registers the resulting
|
||||
* Pointcut with the BeanDefinitionRegistry.
|
||||
*/
|
||||
private AbstractBeanDefinition parsePointcut(Element pointcutElement, ParserContext parserContext) {
|
||||
String id = pointcutElement.getAttribute(ID);
|
||||
String expression = pointcutElement.getAttribute(EXPRESSION);
|
||||
|
||||
AbstractBeanDefinition pointcutDefinition = null;
|
||||
|
||||
try {
|
||||
this.parseState.push(new PointcutEntry(id));
|
||||
pointcutDefinition = createPointcutDefinition(expression);
|
||||
pointcutDefinition.setSource(parserContext.extractSource(pointcutElement));
|
||||
|
||||
String pointcutBeanName = id;
|
||||
if (StringUtils.hasText(pointcutBeanName)) {
|
||||
parserContext.getRegistry().registerBeanDefinition(pointcutBeanName, pointcutDefinition);
|
||||
}
|
||||
else {
|
||||
pointcutBeanName = parserContext.getReaderContext().registerWithGeneratedName(pointcutDefinition);
|
||||
}
|
||||
|
||||
parserContext.registerComponent(
|
||||
new PointcutComponentDefinition(pointcutBeanName, pointcutDefinition, expression));
|
||||
}
|
||||
finally {
|
||||
this.parseState.pop();
|
||||
}
|
||||
|
||||
return pointcutDefinition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the <code>pointcut</code> or <code>pointcut-ref</code> attributes of the supplied
|
||||
* {@link Element} and add a <code>pointcut</code> property as appropriate. Generates a
|
||||
* {@link org.springframework.beans.factory.config.BeanDefinition} for the pointcut if necessary
|
||||
* and returns its bean name, otherwise returns the bean name of the referred pointcut.
|
||||
*/
|
||||
private Object parsePointcutProperty(Element element, ParserContext parserContext) {
|
||||
if (element.hasAttribute(POINTCUT) && element.hasAttribute(POINTCUT_REF)) {
|
||||
parserContext.getReaderContext().error(
|
||||
"Cannot define both 'pointcut' and 'pointcut-ref' on <advisor> tag.",
|
||||
element, this.parseState.snapshot());
|
||||
return null;
|
||||
}
|
||||
else if (element.hasAttribute(POINTCUT)) {
|
||||
// Create a pointcut for the anonymous pc and register it.
|
||||
String expression = element.getAttribute(POINTCUT);
|
||||
AbstractBeanDefinition pointcutDefinition = createPointcutDefinition(expression);
|
||||
pointcutDefinition.setSource(parserContext.extractSource(element));
|
||||
return pointcutDefinition;
|
||||
}
|
||||
else if (element.hasAttribute(POINTCUT_REF)) {
|
||||
String pointcutRef = element.getAttribute(POINTCUT_REF);
|
||||
if (!StringUtils.hasText(pointcutRef)) {
|
||||
parserContext.getReaderContext().error(
|
||||
"'pointcut-ref' attribute contains empty value.", element, this.parseState.snapshot());
|
||||
return null;
|
||||
}
|
||||
return pointcutRef;
|
||||
}
|
||||
else {
|
||||
parserContext.getReaderContext().error(
|
||||
"Must define one of 'pointcut' or 'pointcut-ref' on <advisor> tag.",
|
||||
element, this.parseState.snapshot());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link BeanDefinition} for the {@link AspectJExpressionPointcut} class using
|
||||
* the supplied pointcut expression.
|
||||
*/
|
||||
protected AbstractBeanDefinition createPointcutDefinition(String expression) {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(AspectJExpressionPointcut.class);
|
||||
beanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
|
||||
beanDefinition.setSynthetic(true);
|
||||
beanDefinition.getPropertyValues().addPropertyValue(EXPRESSION, expression);
|
||||
return beanDefinition;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2002-2006 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.config;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link FactoryBean} implementation that locates a {@link Method} on a specified bean.
|
||||
*
|
||||
* @author Rob Harrop
|
||||
* @since 2.0
|
||||
*/
|
||||
public class MethodLocatingFactoryBean implements FactoryBean, BeanFactoryAware {
|
||||
|
||||
private String targetBeanName;
|
||||
|
||||
private String methodName;
|
||||
|
||||
private Method method;
|
||||
|
||||
|
||||
/**
|
||||
* Set the name of the bean to locate the {@link Method} on.
|
||||
* <p>This property is required.
|
||||
* @param targetBeanName the name of the bean to locate the {@link Method} on
|
||||
*/
|
||||
public void setTargetBeanName(String targetBeanName) {
|
||||
this.targetBeanName = targetBeanName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the {@link Method} to locate.
|
||||
* <p>This property is required.
|
||||
* @param methodName the name of the {@link Method} to locate
|
||||
*/
|
||||
public void setMethodName(String methodName) {
|
||||
this.methodName = methodName;
|
||||
}
|
||||
|
||||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
if (!StringUtils.hasText(this.targetBeanName)) {
|
||||
throw new IllegalArgumentException("Property 'targetBeanName' is required");
|
||||
}
|
||||
if (!StringUtils.hasText(this.methodName)) {
|
||||
throw new IllegalArgumentException("Property 'methodName' is required");
|
||||
}
|
||||
|
||||
Class beanClass = beanFactory.getType(this.targetBeanName);
|
||||
if (beanClass == null) {
|
||||
throw new IllegalArgumentException("Can't determine type of bean with name '" + this.targetBeanName + "'");
|
||||
}
|
||||
this.method = BeanUtils.resolveSignature(this.methodName, beanClass);
|
||||
|
||||
if (this.method == null) {
|
||||
throw new IllegalArgumentException("Unable to locate method [" + this.methodName +
|
||||
"] on bean [" + this.targetBeanName + "]");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Object getObject() throws Exception {
|
||||
return this.method;
|
||||
}
|
||||
|
||||
public Class getObjectType() {
|
||||
return Method.class;
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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.config;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.parsing.AbstractComponentDefinition;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.beans.factory.parsing.ComponentDefinition}
|
||||
* implementation that holds a pointcut definition.
|
||||
*
|
||||
* @author Rob Harrop
|
||||
* @since 2.0
|
||||
*/
|
||||
public class PointcutComponentDefinition extends AbstractComponentDefinition {
|
||||
|
||||
private final String pointcutBeanName;
|
||||
|
||||
private final BeanDefinition pointcutDefinition;
|
||||
|
||||
private final String description;
|
||||
|
||||
|
||||
public PointcutComponentDefinition(String pointcutBeanName, BeanDefinition pointcutDefinition, String expression) {
|
||||
Assert.notNull(pointcutBeanName, "Bean name must not be null");
|
||||
Assert.notNull(pointcutDefinition, "Pointcut definition must not be null");
|
||||
Assert.notNull(expression, "Expression must not be null");
|
||||
this.pointcutBeanName = pointcutBeanName;
|
||||
this.pointcutDefinition = pointcutDefinition;
|
||||
this.description = "Pointcut <name='" + pointcutBeanName + "', expression=[" + expression + "]>";
|
||||
}
|
||||
|
||||
|
||||
public String getName() {
|
||||
return this.pointcutBeanName;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
public BeanDefinition[] getBeanDefinitions() {
|
||||
return new BeanDefinition[] {this.pointcutDefinition};
|
||||
}
|
||||
|
||||
public Object getSource() {
|
||||
return this.pointcutDefinition.getSource();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2002-2006 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.config;
|
||||
|
||||
import org.springframework.beans.factory.parsing.ParseState;
|
||||
|
||||
/**
|
||||
* {@link ParseState} entry representing a pointcut.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @since 2.0
|
||||
*/
|
||||
public class PointcutEntry implements ParseState.Entry {
|
||||
|
||||
private final String name;
|
||||
|
||||
/**
|
||||
* Creates a new instance of the {@link PointcutEntry} class.
|
||||
* @param name the bean name of the pointcut
|
||||
*/
|
||||
public PointcutEntry(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Pointcut '" + this.name + "'";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
|
||||
import org.springframework.aop.scope.ScopedProxyUtils;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionDecorator;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
|
||||
/**
|
||||
* {@link BeanDefinitionDecorator} responsible for parsing the
|
||||
* <code><aop:scoped-proxy/></code> tag.
|
||||
*
|
||||
* @author Rob Harrop
|
||||
* @author Juergen Hoeller
|
||||
* @author Mark Fisher
|
||||
* @since 2.0
|
||||
*/
|
||||
class ScopedProxyBeanDefinitionDecorator implements BeanDefinitionDecorator {
|
||||
|
||||
private static final String PROXY_TARGET_CLASS = "proxy-target-class";
|
||||
|
||||
|
||||
public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext parserContext) {
|
||||
boolean proxyTargetClass = true;
|
||||
if (node instanceof Element) {
|
||||
Element ele = (Element) node;
|
||||
if (ele.hasAttribute(PROXY_TARGET_CLASS)) {
|
||||
proxyTargetClass = Boolean.valueOf(ele.getAttribute(PROXY_TARGET_CLASS)).booleanValue();
|
||||
}
|
||||
}
|
||||
|
||||
// Register the original bean definition as it will be referenced by the scoped proxy and is relevant for tooling (validation, navigation).
|
||||
String targetBeanName = ScopedProxyUtils.getTargetBeanName(definition.getBeanName());
|
||||
parserContext.getReaderContext().fireComponentRegistered(new BeanComponentDefinition(definition.getBeanDefinition(), targetBeanName));
|
||||
|
||||
return ScopedProxyUtils.createScopedProxy(definition, parserContext.getRegistry(), proxyTargetClass);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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.config;
|
||||
|
||||
import org.springframework.aop.aspectj.AspectInstanceFactory;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Implementation of {@link AspectInstanceFactory} that locates the aspect from the
|
||||
* {@link org.springframework.beans.factory.BeanFactory} using a configured bean name.
|
||||
*
|
||||
* @author Rob Harrop
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
public class SimpleBeanFactoryAwareAspectInstanceFactory implements AspectInstanceFactory, BeanFactoryAware {
|
||||
|
||||
private String aspectBeanName;
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Set the name of the aspect bean. This is the bean that is returned when calling
|
||||
* {@link #getAspectInstance()}.
|
||||
*/
|
||||
public void setAspectBeanName(String aspectBeanName) {
|
||||
this.aspectBeanName = aspectBeanName;
|
||||
}
|
||||
|
||||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
if (!StringUtils.hasText(this.aspectBeanName)) {
|
||||
throw new IllegalArgumentException("'aspectBeanName' is required");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Look up the aspect bean from the {@link BeanFactory} and returns it.
|
||||
* @see #setAspectBeanName
|
||||
*/
|
||||
public Object getAspectInstance() {
|
||||
return this.beanFactory.getBean(this.aspectBeanName);
|
||||
}
|
||||
|
||||
public ClassLoader getAspectClassLoader() {
|
||||
if (this.beanFactory instanceof ConfigurableBeanFactory) {
|
||||
return ((ConfigurableBeanFactory) this.beanFactory).getBeanClassLoader();
|
||||
}
|
||||
else {
|
||||
return ClassUtils.getDefaultClassLoader();
|
||||
}
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
if (this.beanFactory.isSingleton(this.aspectBeanName) &&
|
||||
this.beanFactory.isTypeMatch(this.aspectBeanName, Ordered.class)) {
|
||||
return ((Ordered) this.beanFactory.getBean(this.aspectBeanName)).getOrder();
|
||||
}
|
||||
return Ordered.LOWEST_PRECEDENCE;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
|
||||
/**
|
||||
* {@link BeanDefinitionParser} responsible for parsing the
|
||||
* <code><aop:spring-configured/></code> tag.
|
||||
*
|
||||
* <p><b>NOTE:</b> This is essentially a duplicate of Spring 2.5's
|
||||
* {@link org.springframework.context.config.SpringConfiguredBeanDefinitionParser}
|
||||
* for the <code><context:spring-configured/></code> tag, mirrored here
|
||||
* for compatibility with Spring 2.0's <code><aop:spring-configured/></code>
|
||||
* tag (avoiding a direct dependency on the context package).
|
||||
*
|
||||
* @author Rob Harrop
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
class SpringConfiguredBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
/**
|
||||
* The bean name of the internally managed bean configurer aspect.
|
||||
*/
|
||||
public static final String BEAN_CONFIGURER_ASPECT_BEAN_NAME =
|
||||
"org.springframework.context.config.internalBeanConfigurerAspect";
|
||||
|
||||
private static final String BEAN_CONFIGURER_ASPECT_CLASS_NAME =
|
||||
"org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect";
|
||||
|
||||
|
||||
public BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||
if (!parserContext.getRegistry().containsBeanDefinition(BEAN_CONFIGURER_ASPECT_BEAN_NAME)) {
|
||||
RootBeanDefinition def = new RootBeanDefinition();
|
||||
def.setBeanClassName(BEAN_CONFIGURER_ASPECT_CLASS_NAME);
|
||||
def.setFactoryMethodName("aspectOf");
|
||||
def.setSource(parserContext.extractSource(element));
|
||||
parserContext.registerBeanComponent(new BeanComponentDefinition(def, BEAN_CONFIGURER_ASPECT_BEAN_NAME));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<html>
|
||||
<body>
|
||||
|
||||
Support package for declarative AOP configuration,
|
||||
with XML schema being the primary configuration format.
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,406 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
|
||||
<xsd:schema xmlns="http://www.springframework.org/schema/aop"
|
||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
xmlns:tool="http://www.springframework.org/schema/tool"
|
||||
targetNamespace="http://www.springframework.org/schema/aop"
|
||||
elementFormDefault="qualified"
|
||||
attributeFormDefault="unqualified">
|
||||
|
||||
<xsd:import namespace="http://www.springframework.org/schema/beans"/>
|
||||
<xsd:import namespace="http://www.springframework.org/schema/tool"/>
|
||||
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Defines the configuration elements for the Spring Framework's AOP support.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
|
||||
<xsd:element name="config">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
A section (compartmentalization) of AOP-specific configuration (including
|
||||
aspects, pointcuts, etc).
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="pointcut" type="pointcutType" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
A named pointcut definition.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="advisor" type="advisorType" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:org.springframework.aop.Advisor"><![CDATA[
|
||||
A named advisor definition.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="aspect" type="aspectType" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
A named aspect definition.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="proxy-target-class" type="xsd:boolean" default="false">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Are class-based (CGLIB) proxies to be created? By default, standard
|
||||
Java interface-based proxies are created.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="aspectj-autoproxy">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator"><![CDATA[
|
||||
Enables the use of the @AspectJ style of Spring AOP.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="include" type="includeType" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Indicates that only @AspectJ beans with names matched by the (regex)
|
||||
pattern will be considered as defining aspects to use for Spring autoproxying.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="proxy-target-class" type="xsd:boolean" default="false">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Are class-based (CGLIB) proxies to be created? By default, standard
|
||||
Java interface-based proxies are created.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="scoped-proxy">
|
||||
<xsd:complexType>
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:org.springframework.aop.scope.ScopedProxyFactoryBean"><![CDATA[
|
||||
Marks a bean definition as being a scoped proxy.
|
||||
|
||||
A bean marked as such will be exposed via a proxy, with the 'real'
|
||||
bean instance being retrieved from some other source (such as a
|
||||
HttpSession) as and when required.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:attribute name="proxy-target-class" type="xsd:boolean" default="true">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Are class-based (CGLIB) proxies to be created? This is the default; in order to
|
||||
switch to standard Java interface-based proxies, turn this flag to "false".
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="spring-configured">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect"><![CDATA[
|
||||
Signals the current application context to apply dependency injection
|
||||
to non-managed objects that are instantiated outside of the Spring bean
|
||||
factory (typically classes annotated with the @Configurable annotation).
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:simpleType>
|
||||
<xsd:restriction base="xsd:string"/>
|
||||
</xsd:simpleType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:complexType name="aspectType">
|
||||
<xsd:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:element name="pointcut" type="pointcutType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
A named pointcut definition.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="declare-parents" type="declareParentsType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Allows this aspect to introduce additional interfaces that the advised
|
||||
object will transparently implement.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="before" type="basicAdviceType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
A before advice definition.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="after" type="basicAdviceType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
An after advice definition.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="after-returning" type="afterReturningAdviceType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
An after-returning advice definition.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="after-throwing" type="afterThrowingAdviceType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
An after-throwing advice definition.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="around" type="basicAdviceType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
An around advice definition.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
<xsd:attribute name="id" type="xsd:ID">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The unique identifier for an aspect.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="ref" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The name of the (backing) bean that encapsulates the aspect.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="order" type="xsd:int">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:org.springframework.core.Ordered"><![CDATA[
|
||||
Controls the ordering of the execution of this aspect when multiple
|
||||
advice executes at a specific joinpoint.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="includeType">
|
||||
<xsd:attribute name="name" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:java.util.regex.Pattern"><![CDATA[
|
||||
The regular expression defining which beans are to be included in the
|
||||
list of @AspectJ beans; beans with names matched by the pattern will
|
||||
be included.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="pointcutType">
|
||||
<xsd:annotation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation>
|
||||
<tool:exports type="org.springframework.aop.Pointcut"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
<xsd:attribute name="id" type="xsd:ID">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The unique identifier for a pointcut.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="expression" use="required" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The pointcut expression.
|
||||
|
||||
For example : 'execution(* com.xyz.myapp.service.*.*(..))'
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="type" default="aspectj">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The type of pointcut expression (see the associated enumeration).
|
||||
DEPRECATED: Only AspectJ expression pointcuts are supported.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:simpleType>
|
||||
<xsd:restriction base="xsd:string">
|
||||
<xsd:enumeration value="aspectj"/>
|
||||
<xsd:enumeration value="regex"/>
|
||||
</xsd:restriction>
|
||||
</xsd:simpleType>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="declareParentsType">
|
||||
<xsd:attribute name="types-matching" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:org.springframework.aop.aspectj.TypePatternClassFilter"><![CDATA[
|
||||
The AspectJ type expression that defines what types (classes) the
|
||||
introduction is restricted to.
|
||||
|
||||
An example would be 'org.springframework.beans.ITestBean+'.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="implement-interface" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:java.lang.Class"><![CDATA[
|
||||
The fully qualified name of the interface that will be introduced.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="default-impl" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:java.lang.Class"><![CDATA[
|
||||
The fully qualified name of the class that will be instantiated to serve
|
||||
as the default implementation of the introduced interface.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="basicAdviceType">
|
||||
<xsd:attribute name="pointcut" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The associated pointcut expression.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="pointcut-ref" type="pointcutRefType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The name of an associated pointcut definition.
|
||||
]]></xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.aop.Pointcut"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="method" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The name of the method that defines the logic of the advice.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="arg-names" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The comma-delimited list of advice method argument (parameter) names
|
||||
that will be matched from pointcut parameters.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="afterReturningAdviceType">
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="basicAdviceType">
|
||||
<xsd:attribute name="returning" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The name of the method parameter to which the return value must
|
||||
be passed.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="afterThrowingAdviceType">
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="basicAdviceType">
|
||||
<xsd:attribute name="throwing" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The name of the method parameter to which the thrown exception must
|
||||
be passed.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="advisorType">
|
||||
<xsd:annotation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation>
|
||||
<tool:exports type="org.springframework.aop.Advisor"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
<!-- Rick : ID type written explicitly as opposed to being imported (c.f. [SPR-2290] -->
|
||||
<xsd:attribute name="id" type="xsd:ID"/>
|
||||
<xsd:attribute name="advice-ref" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
A reference to an advice bean.
|
||||
]]></xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.aopalliance.aop.Advice"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="pointcut" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
A pointcut expression.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="pointcut-ref" type="pointcutRefType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
A reference to a pointcut definition.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="order" type="xsd:int">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:org.springframework.core.Ordered"><![CDATA[
|
||||
Controls the ordering of the execution of this advice when multiple
|
||||
advice executes at a specific joinpoint.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:simpleType name="pointcutRefType">
|
||||
<xsd:annotation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.aop.Pointcut"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
<xsd:union memberTypes="xsd:string"/>
|
||||
</xsd:simpleType>
|
||||
|
||||
</xsd:schema>
|
||||
@@ -0,0 +1,390 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
|
||||
<xsd:schema xmlns="http://www.springframework.org/schema/aop"
|
||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
xmlns:tool="http://www.springframework.org/schema/tool"
|
||||
targetNamespace="http://www.springframework.org/schema/aop"
|
||||
elementFormDefault="qualified"
|
||||
attributeFormDefault="unqualified">
|
||||
|
||||
<xsd:import namespace="http://www.springframework.org/schema/beans"/>
|
||||
<xsd:import namespace="http://www.springframework.org/schema/tool"/>
|
||||
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Defines the configuration elements for the Spring Framework's AOP support.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
|
||||
<xsd:element name="config">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
A section (compartmentalization) of AOP-specific configuration (including
|
||||
aspects, pointcuts, etc).
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="pointcut" type="pointcutType" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
A named pointcut definition.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="advisor" type="advisorType" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:org.springframework.aop.Advisor"><![CDATA[
|
||||
A named advisor definition.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="aspect" type="aspectType" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
A named aspect definition.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="proxy-target-class" type="xsd:boolean" default="false">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Are class-based (CGLIB) proxies to be created? By default, standard
|
||||
Java interface-based proxies are created.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="aspectj-autoproxy">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator"><![CDATA[
|
||||
Enables the use of the @AspectJ style of Spring AOP.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="include" type="includeType" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Indicates that only @AspectJ beans with names matched by the (regex)
|
||||
pattern will be considered as defining aspects to use for Spring autoproxying.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="proxy-target-class" type="xsd:boolean" default="false">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Are class-based (CGLIB) proxies to be created? By default, standard
|
||||
Java interface-based proxies are created.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="scoped-proxy">
|
||||
<xsd:complexType>
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:org.springframework.aop.scope.ScopedProxyFactoryBean"><![CDATA[
|
||||
Marks a bean definition as being a scoped proxy.
|
||||
|
||||
A bean marked as such will be exposed via a proxy, with the 'real'
|
||||
bean instance being retrieved from some other source (such as a
|
||||
HttpSession) as and when required.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:attribute name="proxy-target-class" type="xsd:boolean" default="true">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Are class-based (CGLIB) proxies to be created? This is the default; in order to
|
||||
switch to standard Java interface-based proxies, turn this flag to "false".
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:complexType name="aspectType">
|
||||
<xsd:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:element name="pointcut" type="pointcutType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
A named pointcut definition.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="declare-parents" type="declareParentsType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Allows this aspect to introduce additional interfaces that the advised
|
||||
object will transparently implement.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="before" type="basicAdviceType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
A before advice definition.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="after" type="basicAdviceType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
An after advice definition.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="after-returning" type="afterReturningAdviceType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
An after-returning advice definition.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="after-throwing" type="afterThrowingAdviceType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
An after-throwing advice definition.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="around" type="basicAdviceType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
An around advice definition.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
<xsd:attribute name="id" type="xsd:ID">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The unique identifier for an aspect.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="ref" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The name of the (backing) bean that encapsulates the aspect.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="order" type="xsd:int">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:org.springframework.core.Ordered"><![CDATA[
|
||||
Controls the ordering of the execution of this aspect when multiple
|
||||
advice executes at a specific joinpoint.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="includeType">
|
||||
<xsd:attribute name="name" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:java.util.regex.Pattern"><![CDATA[
|
||||
The regular expression defining which beans are to be included in the
|
||||
list of @AspectJ beans; beans with names matched by the pattern will
|
||||
be included.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="pointcutType">
|
||||
<xsd:annotation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation>
|
||||
<tool:exports type="org.springframework.aop.Pointcut"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
<xsd:attribute name="id" type="xsd:ID" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The unique identifier for a pointcut.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="expression" use="required" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The pointcut expression.
|
||||
|
||||
For example : 'execution(* com.xyz.myapp.service.*.*(..))'
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="declareParentsType">
|
||||
<xsd:attribute name="types-matching" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:org.springframework.aop.aspectj.TypePatternClassFilter"><![CDATA[
|
||||
The AspectJ type expression that defines what types (classes) the
|
||||
introduction is restricted to.
|
||||
|
||||
An example would be 'org.springframework.beans.ITestBean+'.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="implement-interface" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:java.lang.Class"><![CDATA[
|
||||
The fully qualified name of the interface that will be introduced.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="default-impl" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:java.lang.Class"><![CDATA[
|
||||
The fully qualified name of the class that will be instantiated to serve
|
||||
as the default implementation of the introduced interface.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="delegate-ref" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
A reference to the bean that will serve
|
||||
as the default implementation of the introduced interface.
|
||||
]]></xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref"/>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="basicAdviceType">
|
||||
<xsd:attribute name="pointcut" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The associated pointcut expression.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="pointcut-ref" type="pointcutRefType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The name of an associated pointcut definition.
|
||||
]]></xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.aop.Pointcut"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="method" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The name of the method that defines the logic of the advice.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="arg-names" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The comma-delimited list of advice method argument (parameter) names
|
||||
that will be matched from pointcut parameters.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="afterReturningAdviceType">
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="basicAdviceType">
|
||||
<xsd:attribute name="returning" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The name of the method parameter to which the return value must
|
||||
be passed.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="afterThrowingAdviceType">
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="basicAdviceType">
|
||||
<xsd:attribute name="throwing" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The name of the method parameter to which the thrown exception must
|
||||
be passed.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="advisorType">
|
||||
<xsd:annotation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation>
|
||||
<tool:exports type="org.springframework.aop.Advisor"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
<!-- Rick : ID type written explicitly as opposed to being imported (c.f. [SPR-2290] -->
|
||||
<xsd:attribute name="id" type="xsd:ID"/>
|
||||
<xsd:attribute name="advice-ref" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
A reference to an advice bean.
|
||||
]]></xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.aopalliance.aop.Advice"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="pointcut" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
A pointcut expression.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="pointcut-ref" type="pointcutRefType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
A reference to a pointcut definition.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="order" type="xsd:int">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:org.springframework.core.Ordered"><![CDATA[
|
||||
Controls the ordering of the execution of this advice when multiple
|
||||
advice executes at a specific joinpoint.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:simpleType name="pointcutRefType">
|
||||
<xsd:annotation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.aop.Pointcut"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
<xsd:union memberTypes="xsd:string"/>
|
||||
</xsd:simpleType>
|
||||
|
||||
</xsd:schema>
|
||||
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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.framework;
|
||||
|
||||
import org.springframework.aop.TargetSource;
|
||||
import org.springframework.aop.framework.adapter.AdvisorAdapterRegistry;
|
||||
import org.springframework.aop.framework.adapter.GlobalAdvisorAdapterRegistry;
|
||||
import org.springframework.aop.target.SingletonTargetSource;
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.FactoryBeanNotInitializedException;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Convenient proxy factory bean superclass for proxy factory
|
||||
* beans that create only singletons.
|
||||
*
|
||||
* <p>Manages pre- and post-interceptors (references, rather than
|
||||
* interceptor names, as in {@link ProxyFactoryBean}) and provides
|
||||
* consistent interface management.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
public abstract class AbstractSingletonProxyFactoryBean extends ProxyConfig
|
||||
implements FactoryBean, BeanClassLoaderAware, InitializingBean {
|
||||
|
||||
private Object target;
|
||||
|
||||
private Class[] proxyInterfaces;
|
||||
|
||||
private Object[] preInterceptors;
|
||||
|
||||
private Object[] postInterceptors;
|
||||
|
||||
/** Default is global AdvisorAdapterRegistry */
|
||||
private AdvisorAdapterRegistry advisorAdapterRegistry = GlobalAdvisorAdapterRegistry.getInstance();
|
||||
|
||||
private transient ClassLoader proxyClassLoader;
|
||||
|
||||
private Object proxy;
|
||||
|
||||
|
||||
/**
|
||||
* Set the target object, that is, the bean to be wrapped with a transactional proxy.
|
||||
* <p>The target may be any object, in which case a SingletonTargetSource will
|
||||
* be created. If it is a TargetSource, no wrapper TargetSource is created:
|
||||
* This enables the use of a pooling or prototype TargetSource etc.
|
||||
* @see org.springframework.aop.TargetSource
|
||||
* @see org.springframework.aop.target.SingletonTargetSource
|
||||
* @see org.springframework.aop.target.LazyInitTargetSource
|
||||
* @see org.springframework.aop.target.PrototypeTargetSource
|
||||
* @see org.springframework.aop.target.CommonsPoolTargetSource
|
||||
*/
|
||||
public void setTarget(Object target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the set of interfaces being proxied.
|
||||
* <p>If not specified (the default), the AOP infrastructure works
|
||||
* out which interfaces need proxying by analyzing the target,
|
||||
* proxying all the interfaces that the target object implements.
|
||||
*/
|
||||
public void setProxyInterfaces(Class[] proxyInterfaces) {
|
||||
this.proxyInterfaces = proxyInterfaces;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set additional interceptors (or advisors) to be applied before the
|
||||
* implicit transaction interceptor, e.g. a PerformanceMonitorInterceptor.
|
||||
* <p>You may specify any AOP Alliance MethodInterceptors or other
|
||||
* Spring AOP Advices, as well as Spring AOP Advisors.
|
||||
* @see org.springframework.aop.interceptor.PerformanceMonitorInterceptor
|
||||
*/
|
||||
public void setPreInterceptors(Object[] preInterceptors) {
|
||||
this.preInterceptors = preInterceptors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set additional interceptors (or advisors) to be applied after the
|
||||
* implicit transaction interceptor.
|
||||
* <p>You may specify any AOP Alliance MethodInterceptors or other
|
||||
* Spring AOP Advices, as well as Spring AOP Advisors.
|
||||
*/
|
||||
public void setPostInterceptors(Object[] postInterceptors) {
|
||||
this.postInterceptors = postInterceptors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the AdvisorAdapterRegistry to use.
|
||||
* Default is the global AdvisorAdapterRegistry.
|
||||
* @see org.springframework.aop.framework.adapter.GlobalAdvisorAdapterRegistry
|
||||
*/
|
||||
public void setAdvisorAdapterRegistry(AdvisorAdapterRegistry advisorAdapterRegistry) {
|
||||
this.advisorAdapterRegistry = advisorAdapterRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ClassLoader to generate the proxy class in.
|
||||
* <p>Default is the bean ClassLoader, i.e. the ClassLoader used by the
|
||||
* containing BeanFactory for loading all bean classes. This can be
|
||||
* overridden here for specific proxies.
|
||||
*/
|
||||
public void setProxyClassLoader(ClassLoader classLoader) {
|
||||
this.proxyClassLoader = classLoader;
|
||||
}
|
||||
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
if (this.proxyClassLoader == null) {
|
||||
this.proxyClassLoader = classLoader;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
if (this.target == null) {
|
||||
throw new IllegalArgumentException("Property 'target' is required");
|
||||
}
|
||||
if (this.target instanceof String) {
|
||||
throw new IllegalArgumentException("'target' needs to be a bean reference, not a bean name as value");
|
||||
}
|
||||
if (this.proxyClassLoader == null) {
|
||||
this.proxyClassLoader = ClassUtils.getDefaultClassLoader();
|
||||
}
|
||||
|
||||
ProxyFactory proxyFactory = new ProxyFactory();
|
||||
|
||||
if (this.preInterceptors != null) {
|
||||
for (int i = 0; i < this.preInterceptors.length; i++) {
|
||||
proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(this.preInterceptors[i]));
|
||||
}
|
||||
}
|
||||
|
||||
// Add the main interceptor (typically an Advisor).
|
||||
proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(createMainInterceptor()));
|
||||
|
||||
if (this.postInterceptors != null) {
|
||||
for (int i = 0; i < this.postInterceptors.length; i++) {
|
||||
proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(this.postInterceptors[i]));
|
||||
}
|
||||
}
|
||||
|
||||
proxyFactory.copyFrom(this);
|
||||
|
||||
TargetSource targetSource = createTargetSource(this.target);
|
||||
proxyFactory.setTargetSource(targetSource);
|
||||
|
||||
if (this.proxyInterfaces != null) {
|
||||
proxyFactory.setInterfaces(this.proxyInterfaces);
|
||||
}
|
||||
else if (!isProxyTargetClass()) {
|
||||
// Rely on AOP infrastructure to tell us what interfaces to proxy.
|
||||
proxyFactory.setInterfaces(
|
||||
ClassUtils.getAllInterfacesForClass(targetSource.getTargetClass(), this.proxyClassLoader));
|
||||
}
|
||||
|
||||
this.proxy = getProxy(proxyFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine a TargetSource for the given target (or TargetSource).
|
||||
* @param target target. If this is an implementation of TargetSource it is
|
||||
* used as our TargetSource; otherwise it is wrapped in a SingletonTargetSource.
|
||||
* @return a TargetSource for this object
|
||||
*/
|
||||
protected TargetSource createTargetSource(Object target) {
|
||||
if (target instanceof TargetSource) {
|
||||
return (TargetSource) target;
|
||||
}
|
||||
else {
|
||||
return new SingletonTargetSource(target);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the proxy object to expose.
|
||||
* <p>The default implementation uses a <code>getProxy</code> call with
|
||||
* the factory's bean class loader. Can be overridden to specify a
|
||||
* custom class loader.
|
||||
* @param aopProxy the prepared AopProxy instance to get the proxy from
|
||||
* @return the proxy object to expose
|
||||
* @see AopProxy#getProxy(ClassLoader)
|
||||
*/
|
||||
protected Object getProxy(AopProxy aopProxy) {
|
||||
return aopProxy.getProxy(this.proxyClassLoader);
|
||||
}
|
||||
|
||||
|
||||
public Object getObject() {
|
||||
if (this.proxy == null) {
|
||||
throw new FactoryBeanNotInitializedException();
|
||||
}
|
||||
return this.proxy;
|
||||
}
|
||||
|
||||
public Class getObjectType() {
|
||||
if (this.proxy != null) {
|
||||
return this.proxy.getClass();
|
||||
}
|
||||
if (this.proxyInterfaces != null && this.proxyInterfaces.length == 1) {
|
||||
return this.proxyInterfaces[0];
|
||||
}
|
||||
if (this.target instanceof TargetSource) {
|
||||
return ((TargetSource) this.target).getTargetClass();
|
||||
}
|
||||
if (this.target != null) {
|
||||
return this.target.getClass();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public final boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create the "main" interceptor for this proxy factory bean.
|
||||
* Typically an Advisor, but can also be any type of Advice.
|
||||
* <p>Pre-interceptors will be applied before, post-interceptors
|
||||
* will be applied after this interceptor.
|
||||
*/
|
||||
protected abstract Object createMainInterceptor();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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.framework;
|
||||
|
||||
import org.aopalliance.aop.Advice;
|
||||
|
||||
import org.springframework.aop.Advisor;
|
||||
import org.springframework.aop.TargetClassAware;
|
||||
import org.springframework.aop.TargetSource;
|
||||
|
||||
/**
|
||||
* Interface to be implemented by classes that hold the configuration
|
||||
* of a factory of AOP proxies. This configuration includes the
|
||||
* Interceptors and other advice, and Advisors, and the proxied interfaces.
|
||||
*
|
||||
* <p>Any AOP proxy obtained from Spring can be cast to this interface to
|
||||
* allow manipulation of its AOP advice.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @since 13.03.2003
|
||||
* @see org.springframework.aop.framework.AdvisedSupport
|
||||
*/
|
||||
public interface Advised extends TargetClassAware {
|
||||
|
||||
/**
|
||||
* Return whether the Advised configuration is frozen,
|
||||
* in which case no advice changes can be made.
|
||||
*/
|
||||
boolean isFrozen();
|
||||
|
||||
/**
|
||||
* Are we proxying the full target class instead of specified interfaces?
|
||||
*/
|
||||
boolean isProxyTargetClass();
|
||||
|
||||
/**
|
||||
* Return the interfaces proxied by the AOP proxy. Will not
|
||||
* include the target class, which may also be proxied.
|
||||
*/
|
||||
Class[] getProxiedInterfaces();
|
||||
|
||||
/**
|
||||
* Determine whether the given interface is proxied.
|
||||
* @param intf the interface to check
|
||||
*/
|
||||
boolean isInterfaceProxied(Class intf);
|
||||
|
||||
|
||||
/**
|
||||
* Change the TargetSource used by this Advised object.
|
||||
* Only works if the configuration isn't frozen.
|
||||
* @param targetSource new TargetSource to use
|
||||
*/
|
||||
void setTargetSource(TargetSource targetSource);
|
||||
|
||||
/**
|
||||
* Return the TargetSource used by this Advised object.
|
||||
*/
|
||||
TargetSource getTargetSource();
|
||||
|
||||
/**
|
||||
* Set whether the proxy should be exposed by the AOP framework as a
|
||||
* ThreadLocal for retrieval via the AopContext class. This is useful
|
||||
* if an advised object needs to call another advised method on itself.
|
||||
* (If it uses <code>this</code>, the invocation will not be advised).
|
||||
* <p>Default is "false", for optimal performance.
|
||||
*/
|
||||
void setExposeProxy(boolean exposeProxy);
|
||||
|
||||
/**
|
||||
* Return whether the factory should expose the proxy as a ThreadLocal.
|
||||
* This can be necessary if a target object needs to invoke a method on itself
|
||||
* benefitting from advice. (If it invokes a method on <code>this</code> no advice
|
||||
* will apply.) Getting the proxy is analogous to an EJB calling getEJBObject().
|
||||
* @see AopContext
|
||||
*/
|
||||
boolean isExposeProxy();
|
||||
|
||||
/**
|
||||
* Set whether this proxy configuration is pre-filtered so that it only
|
||||
* contains applicable advisors (matching this proxy's target class).
|
||||
* <p>Default is "false". Set this to "true" if the advisors have been
|
||||
* pre-filtered already, meaning that the ClassFilter check can be skipped
|
||||
* when building the actual advisor chain for proxy invocations.
|
||||
* @see org.springframework.aop.ClassFilter
|
||||
*/
|
||||
void setPreFiltered(boolean preFiltered);
|
||||
|
||||
/**
|
||||
* Return whether this proxy configuration is pre-filtered so that it only
|
||||
* contains applicable advisors (matching this proxy's target class).
|
||||
*/
|
||||
boolean isPreFiltered();
|
||||
|
||||
|
||||
/**
|
||||
* Return the advisors applying to this proxy.
|
||||
* @return a list of Advisors applying to this proxy (never <code>null</code>)
|
||||
*/
|
||||
Advisor[] getAdvisors();
|
||||
|
||||
/**
|
||||
* Add an advisor at the end of the advisor chain.
|
||||
* <p>The Advisor may be an {@link org.springframework.aop.IntroductionAdvisor},
|
||||
* in which new interfaces will be available when a proxy is next obtained
|
||||
* from the relevant factory.
|
||||
* @param advisor the advisor to add to the end of the chain
|
||||
* @throws AopConfigException in case of invalid advice
|
||||
*/
|
||||
void addAdvisor(Advisor advisor) throws AopConfigException;
|
||||
|
||||
/**
|
||||
* Add an Advisor at the specified position in the chain.
|
||||
* @param advisor the advisor to add at the specified position in the chain
|
||||
* @param pos position in chain (0 is head). Must be valid.
|
||||
* @throws AopConfigException in case of invalid advice
|
||||
*/
|
||||
void addAdvisor(int pos, Advisor advisor) throws AopConfigException;
|
||||
|
||||
/**
|
||||
* Remove the given advisor.
|
||||
* @param advisor the advisor to remove
|
||||
* @return <code>true</code> if the advisor was removed; <code>false</code>
|
||||
* if the advisor was not found and hence could not be removed
|
||||
*/
|
||||
boolean removeAdvisor(Advisor advisor);
|
||||
|
||||
/**
|
||||
* Remove the advisor at the given index.
|
||||
* @param index index of advisor to remove
|
||||
* @throws AopConfigException if the index is invalid
|
||||
*/
|
||||
void removeAdvisor(int index) throws AopConfigException;
|
||||
|
||||
/**
|
||||
* Return the index (from 0) of the given advisor,
|
||||
* or -1 if no such advisor applies to this proxy.
|
||||
* <p>The return value of this method can be used to index into the advisors array.
|
||||
* @param advisor the advisor to search for
|
||||
* @return index from 0 of this advisor, or -1 if there's no such advisor
|
||||
*/
|
||||
int indexOf(Advisor advisor);
|
||||
|
||||
/**
|
||||
* Replace the given advisor.
|
||||
* <p><b>Note:</b> If the advisor is an {@link org.springframework.aop.IntroductionAdvisor}
|
||||
* and the replacement is not or implements different interfaces, the proxy will need
|
||||
* to be re-obtained or the old interfaces won't be supported and the new interface
|
||||
* won't be implemented.
|
||||
* @param a the advisor to replace
|
||||
* @param b the advisor to replace it with
|
||||
* @return whether it was replaced. If the advisor wasn't found in the
|
||||
* list of advisors, this method returns <code>false</code> and does nothing.
|
||||
* @throws AopConfigException in case of invalid advice
|
||||
*/
|
||||
boolean replaceAdvisor(Advisor a, Advisor b) throws AopConfigException;
|
||||
|
||||
|
||||
/**
|
||||
* Add the given AOP Alliance advice to the tail of the advice (interceptor) chain.
|
||||
* <p>This will be wrapped in a DefaultPointcutAdvisor with a pointcut that always
|
||||
* applies, and returned from the <code>getAdvisors()</code> method in this wrapped form.
|
||||
* <p>Note that the given advice will apply to all invocations on the proxy,
|
||||
* even to the <code>toString()</code> method! Use appropriate advice implementations
|
||||
* or specify appropriate pointcuts to apply to a narrower set of methods.
|
||||
* @param advice advice to add to the tail of the chain
|
||||
* @throws AopConfigException in case of invalid advice
|
||||
* @see #addAdvice(int, Advice)
|
||||
* @see org.springframework.aop.support.DefaultPointcutAdvisor
|
||||
*/
|
||||
void addAdvice(Advice advice) throws AopConfigException;
|
||||
|
||||
/**
|
||||
* Add the given AOP Alliance Advice at the specified position in the advice chain.
|
||||
* <p>This will be wrapped in a {@link org.springframework.aop.support.DefaultPointcutAdvisor}
|
||||
* with a pointcut that always applies, and returned from the {@link #getAdvisors()}
|
||||
* method in this wrapped form.
|
||||
* <p>Note: The given advice will apply to all invocations on the proxy,
|
||||
* even to the <code>toString()</code> method! Use appropriate advice implementations
|
||||
* or specify appropriate pointcuts to apply to a narrower set of methods.
|
||||
* @param pos index from 0 (head)
|
||||
* @param advice advice to add at the specified position in the advice chain
|
||||
* @throws AopConfigException in case of invalid advice
|
||||
*/
|
||||
void addAdvice(int pos, Advice advice) throws AopConfigException;
|
||||
|
||||
/**
|
||||
* Remove the Advisor containing the given advice.
|
||||
* @param advice the advice to remove
|
||||
* @return <code>true</code> of the advice was found and removed;
|
||||
* <code>false</code> if there was no such advice
|
||||
*/
|
||||
boolean removeAdvice(Advice advice);
|
||||
|
||||
/**
|
||||
* Return the index (from 0) of the given AOP Alliance Advice,
|
||||
* or -1 if no such advice is an advice for this proxy.
|
||||
* <p>The return value of this method can be used to index into
|
||||
* the advisors array.
|
||||
* @param advice AOP Alliance advice to search for
|
||||
* @return index from 0 of this advice, or -1 if there's no such advice
|
||||
*/
|
||||
int indexOf(Advice advice);
|
||||
|
||||
|
||||
/**
|
||||
* As <code>toString()</code> will normally be delegated to the target,
|
||||
* this returns the equivalent for the AOP proxy.
|
||||
* @return a string description of the proxy configuration
|
||||
*/
|
||||
String toProxyConfigString();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,590 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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.framework;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.aopalliance.aop.Advice;
|
||||
|
||||
import org.springframework.aop.Advisor;
|
||||
import org.springframework.aop.DynamicIntroductionAdvice;
|
||||
import org.springframework.aop.IntroductionAdvisor;
|
||||
import org.springframework.aop.IntroductionInfo;
|
||||
import org.springframework.aop.TargetSource;
|
||||
import org.springframework.aop.support.DefaultIntroductionAdvisor;
|
||||
import org.springframework.aop.support.DefaultPointcutAdvisor;
|
||||
import org.springframework.aop.target.EmptyTargetSource;
|
||||
import org.springframework.aop.target.SingletonTargetSource;
|
||||
import org.springframework.core.CollectionFactory;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Base class for AOP proxy configuration managers.
|
||||
* These are not themselves AOP proxies, but subclasses of this class are
|
||||
* normally factories from which AOP proxy instances are obtained directly.
|
||||
*
|
||||
* <p>This class frees subclasses of the housekeeping of Advices
|
||||
* and Advisors, but doesn't actually implement proxy creation
|
||||
* methods, which are provided by subclasses.
|
||||
*
|
||||
* <p>This class is serializable; subclasses need not be.
|
||||
* This class is used to hold snapshots of proxies.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @see org.springframework.aop.framework.AopProxy
|
||||
*/
|
||||
public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
|
||||
/** use serialVersionUID from Spring 2.0 for interoperability */
|
||||
private static final long serialVersionUID = 2651364800145442165L;
|
||||
|
||||
|
||||
/**
|
||||
* Canonical TargetSource when there's no target, and behavior is
|
||||
* supplied by the advisors.
|
||||
*/
|
||||
public static final TargetSource EMPTY_TARGET_SOURCE = EmptyTargetSource.INSTANCE;
|
||||
|
||||
|
||||
/** Package-protected to allow direct access for efficiency */
|
||||
TargetSource targetSource = EMPTY_TARGET_SOURCE;
|
||||
|
||||
/** Whether the Advisors are already filtered for the specific target class */
|
||||
private boolean preFiltered = false;
|
||||
|
||||
/** The AdvisorChainFactory to use */
|
||||
AdvisorChainFactory advisorChainFactory = new DefaultAdvisorChainFactory();
|
||||
|
||||
/** Cache with Method as key and advisor chain List as value */
|
||||
private transient Map methodCache;
|
||||
|
||||
/**
|
||||
* Interfaces to be implemented by the proxy. Held in List to keep the order
|
||||
* of registration, to create JDK proxy with specified order of interfaces.
|
||||
*/
|
||||
private List interfaces = new ArrayList();
|
||||
|
||||
/**
|
||||
* List of Advisors. If an Advice is added, it will be wrapped
|
||||
* in an Advisor before being added to this List.
|
||||
*/
|
||||
private List advisors = new LinkedList();
|
||||
|
||||
/**
|
||||
* Array updated on changes to the advisors list, which is easier
|
||||
* to manipulate internally.
|
||||
*/
|
||||
private Advisor[] advisorArray = new Advisor[0];
|
||||
|
||||
|
||||
/**
|
||||
* No-arg constructor for use as a JavaBean.
|
||||
*/
|
||||
public AdvisedSupport() {
|
||||
initMethodCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a AdvisedSupport instance with the given parameters.
|
||||
* @param interfaces the proxied interfaces
|
||||
*/
|
||||
public AdvisedSupport(Class[] interfaces) {
|
||||
this();
|
||||
setInterfaces(interfaces);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the method cache.
|
||||
*/
|
||||
private void initMethodCache() {
|
||||
this.methodCache = CollectionFactory.createConcurrentMapIfPossible(32);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the given object as target.
|
||||
* Will create a SingletonTargetSource for the object.
|
||||
* @see #setTargetSource
|
||||
* @see org.springframework.aop.target.SingletonTargetSource
|
||||
*/
|
||||
public void setTarget(Object target) {
|
||||
setTargetSource(new SingletonTargetSource(target));
|
||||
}
|
||||
|
||||
public void setTargetSource(TargetSource targetSource) {
|
||||
this.targetSource = (targetSource != null ? targetSource : EMPTY_TARGET_SOURCE);
|
||||
}
|
||||
|
||||
public TargetSource getTargetSource() {
|
||||
return this.targetSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a target class to be proxied, indicating that the proxy
|
||||
* should be castable to the given class.
|
||||
* <p>Internally, an {@link org.springframework.aop.target.EmptyTargetSource}
|
||||
* for the given target class will be used. The kind of proxy needed
|
||||
* will be determined on actual creation of the proxy.
|
||||
* <p>This is a replacement for setting a "targetSource" or "target",
|
||||
* for the case where we want a proxy based on a target class
|
||||
* (which can be an interface or a concrete class) without having
|
||||
* a fully capable TargetSource available.
|
||||
* @see #setTargetSource
|
||||
* @see #setTarget
|
||||
*/
|
||||
public void setTargetClass(Class targetClass) {
|
||||
this.targetSource = EmptyTargetSource.forClass(targetClass);
|
||||
}
|
||||
|
||||
public Class getTargetClass() {
|
||||
return this.targetSource.getTargetClass();
|
||||
}
|
||||
|
||||
public void setPreFiltered(boolean preFiltered) {
|
||||
this.preFiltered = preFiltered;
|
||||
}
|
||||
|
||||
public boolean isPreFiltered() {
|
||||
return this.preFiltered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the advisor chain factory to use.
|
||||
* <p>Default is a {@link DefaultAdvisorChainFactory}.
|
||||
*/
|
||||
public void setAdvisorChainFactory(AdvisorChainFactory advisorChainFactory) {
|
||||
Assert.notNull(advisorChainFactory, "AdvisorChainFactory must not be null");
|
||||
this.advisorChainFactory = advisorChainFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the advisor chain factory to use (never <code>null</code>).
|
||||
*/
|
||||
public AdvisorChainFactory getAdvisorChainFactory() {
|
||||
return this.advisorChainFactory;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the interfaces to be proxied.
|
||||
*/
|
||||
public void setInterfaces(Class[] interfaces) {
|
||||
Assert.notNull(interfaces, "Interfaces must not be null");
|
||||
this.interfaces.clear();
|
||||
for (int i = 0; i < interfaces.length; i++) {
|
||||
addInterface(interfaces[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new proxied interface.
|
||||
* @param intf the additional interface to proxy
|
||||
*/
|
||||
public void addInterface(Class intf) {
|
||||
Assert.notNull(intf, "Interface must not be null");
|
||||
if (!intf.isInterface()) {
|
||||
throw new IllegalArgumentException("[" + intf.getName() + "] is not an interface");
|
||||
}
|
||||
if (!this.interfaces.contains(intf)) {
|
||||
this.interfaces.add(intf);
|
||||
adviceChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a proxied interface.
|
||||
* <p>Does nothing if the given interface isn't proxied.
|
||||
* @param intf the interface to remove from the proxy
|
||||
* @return <code>true</code> if the interface was removed; <code>false</code>
|
||||
* if the interface was not found and hence could not be removed
|
||||
*/
|
||||
public boolean removeInterface(Class intf) {
|
||||
return this.interfaces.remove(intf);
|
||||
}
|
||||
|
||||
public Class[] getProxiedInterfaces() {
|
||||
return (Class[]) this.interfaces.toArray(new Class[this.interfaces.size()]);
|
||||
}
|
||||
|
||||
public boolean isInterfaceProxied(Class intf) {
|
||||
for (Iterator it = this.interfaces.iterator(); it.hasNext();) {
|
||||
Class proxyIntf = (Class) it.next();
|
||||
if (intf.isAssignableFrom(proxyIntf)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public final Advisor[] getAdvisors() {
|
||||
return this.advisorArray;
|
||||
}
|
||||
|
||||
public void addAdvisor(Advisor advisor) {
|
||||
int pos = this.advisors.size();
|
||||
addAdvisor(pos, advisor);
|
||||
}
|
||||
|
||||
public void addAdvisor(int pos, Advisor advisor) throws AopConfigException {
|
||||
if (advisor instanceof IntroductionAdvisor) {
|
||||
validateIntroductionAdvisor((IntroductionAdvisor) advisor);
|
||||
}
|
||||
addAdvisorInternal(pos, advisor);
|
||||
}
|
||||
|
||||
public boolean removeAdvisor(Advisor advisor) {
|
||||
int index = indexOf(advisor);
|
||||
if (index == -1) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
removeAdvisor(index);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void removeAdvisor(int index) throws AopConfigException {
|
||||
if (isFrozen()) {
|
||||
throw new AopConfigException("Cannot remove Advisor: Configuration is frozen.");
|
||||
}
|
||||
if (index < 0 || index > this.advisors.size() - 1) {
|
||||
throw new AopConfigException("Advisor index " + index + " is out of bounds: " +
|
||||
"This configuration only has " + this.advisors.size() + " advisors.");
|
||||
}
|
||||
|
||||
Advisor advisor = (Advisor) this.advisors.get(index);
|
||||
if (advisor instanceof IntroductionAdvisor) {
|
||||
IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
|
||||
// We need to remove introduction interfaces.
|
||||
for (int j = 0; j < ia.getInterfaces().length; j++) {
|
||||
removeInterface(ia.getInterfaces()[j]);
|
||||
}
|
||||
}
|
||||
|
||||
this.advisors.remove(index);
|
||||
updateAdvisorArray();
|
||||
adviceChanged();
|
||||
}
|
||||
|
||||
public int indexOf(Advisor advisor) {
|
||||
Assert.notNull(advisor, "Advisor must not be null");
|
||||
return this.advisors.indexOf(advisor);
|
||||
}
|
||||
|
||||
public boolean replaceAdvisor(Advisor a, Advisor b) throws AopConfigException {
|
||||
Assert.notNull(a, "Advisor a must not be null");
|
||||
Assert.notNull(b, "Advisor b must not be null");
|
||||
int index = indexOf(a);
|
||||
if (index == -1) {
|
||||
return false;
|
||||
}
|
||||
removeAdvisor(index);
|
||||
addAdvisor(index, b);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all of the given advisors to this proxy configuration.
|
||||
* @param advisors the advisors to register
|
||||
*/
|
||||
public void addAllAdvisors(Advisor[] advisors) {
|
||||
if (isFrozen()) {
|
||||
throw new AopConfigException("Cannot add advisor: Configuration is frozen.");
|
||||
}
|
||||
if (!ObjectUtils.isEmpty(advisors)) {
|
||||
for (int i = 0; i < advisors.length; i++) {
|
||||
Advisor advisor = advisors[i];
|
||||
if (advisor instanceof IntroductionAdvisor) {
|
||||
validateIntroductionAdvisor((IntroductionAdvisor) advisor);
|
||||
}
|
||||
Assert.notNull(advisor, "Advisor must not be null");
|
||||
this.advisors.add(advisor);
|
||||
}
|
||||
updateAdvisorArray();
|
||||
adviceChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void validateIntroductionAdvisor(IntroductionAdvisor advisor) {
|
||||
advisor.validateInterfaces();
|
||||
// If the advisor passed validation, we can make the change.
|
||||
Class[] ifcs = advisor.getInterfaces();
|
||||
for (int i = 0; i < ifcs.length; i++) {
|
||||
addInterface(ifcs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private void addAdvisorInternal(int pos, Advisor advisor) throws AopConfigException {
|
||||
Assert.notNull(advisor, "Advisor must not be null");
|
||||
if (isFrozen()) {
|
||||
throw new AopConfigException("Cannot add advisor: Configuration is frozen.");
|
||||
}
|
||||
if (pos > this.advisors.size()) {
|
||||
throw new IllegalArgumentException(
|
||||
"Illegal position " + pos + " in advisor list with size " + this.advisors.size());
|
||||
}
|
||||
this.advisors.add(pos, advisor);
|
||||
updateAdvisorArray();
|
||||
adviceChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Bring the array up to date with the list.
|
||||
*/
|
||||
protected final void updateAdvisorArray() {
|
||||
this.advisorArray = (Advisor[]) this.advisors.toArray(new Advisor[this.advisors.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows uncontrolled access to the {@link List} of {@link Advisor Advisors}.
|
||||
* <p>Use with care, and remember to {@link #updateAdvisorArray() refresh the advisor array}
|
||||
* and {@link #adviceChanged() fire advice changed events} when making any modifications.
|
||||
*/
|
||||
protected final List getAdvisorsInternal() {
|
||||
return this.advisors;
|
||||
}
|
||||
|
||||
|
||||
public void addAdvice(Advice advice) throws AopConfigException {
|
||||
int pos = this.advisors.size();
|
||||
addAdvice(pos, advice);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cannot add introductions this way unless the advice implements IntroductionInfo.
|
||||
*/
|
||||
public void addAdvice(int pos, Advice advice) throws AopConfigException {
|
||||
Assert.notNull(advice, "Advice must not be null");
|
||||
if (advice instanceof IntroductionInfo) {
|
||||
// We don't need an IntroductionAdvisor for this kind of introduction:
|
||||
// It's fully self-describing.
|
||||
addAdvisor(pos, new DefaultIntroductionAdvisor(advice, (IntroductionInfo) advice));
|
||||
}
|
||||
else if (advice instanceof DynamicIntroductionAdvice) {
|
||||
// We need an IntroductionAdvisor for this kind of introduction.
|
||||
throw new AopConfigException("DynamicIntroductionAdvice may only be added as part of IntroductionAdvisor");
|
||||
}
|
||||
else {
|
||||
addAdvisor(pos, new DefaultPointcutAdvisor(advice));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean removeAdvice(Advice advice) throws AopConfigException {
|
||||
int index = indexOf(advice);
|
||||
if (index == -1) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
removeAdvisor(index);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public int indexOf(Advice advice) {
|
||||
Assert.notNull(advice, "Advice must not be null");
|
||||
for (int i = 0; i < this.advisors.size(); i++) {
|
||||
Advisor advisor = (Advisor) this.advisors.get(i);
|
||||
if (advisor.getAdvice() == advice) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the given advice included in any advisor within this proxy configuration?
|
||||
* @param advice the advice to check inclusion of
|
||||
* @return whether this advice instance is included
|
||||
*/
|
||||
public boolean adviceIncluded(Advice advice) {
|
||||
Assert.notNull(advice, "Advice must not be null");
|
||||
for (int i = 0; i < this.advisors.size(); i++) {
|
||||
Advisor advisor = (Advisor) this.advisors.get(i);
|
||||
if (advisor.getAdvice() == advice) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count advices of the given class.
|
||||
* @param adviceClass the advice class to check
|
||||
* @return the count of the interceptors of this class or subclasses
|
||||
*/
|
||||
public int countAdvicesOfType(Class adviceClass) {
|
||||
Assert.notNull(adviceClass, "Advice class must not be null");
|
||||
int count = 0;
|
||||
for (int i = 0; i < this.advisors.size(); i++) {
|
||||
Advisor advisor = (Advisor) this.advisors.get(i);
|
||||
if (advisor.getAdvice() != null &&
|
||||
adviceClass.isAssignableFrom(advisor.getAdvice().getClass())) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine a list of {@link org.aopalliance.intercept.MethodInterceptor} objects
|
||||
* for the given method, based on this configuration.
|
||||
* @param method the proxied method
|
||||
* @param targetClass the target class
|
||||
* @return List of MethodInterceptors (may also include InterceptorAndDynamicMethodMatchers)
|
||||
*/
|
||||
public List getInterceptorsAndDynamicInterceptionAdvice(Method method, Class targetClass) {
|
||||
MethodCacheKey cacheKey = new MethodCacheKey(method);
|
||||
List cached = (List) this.methodCache.get(cacheKey);
|
||||
if (cached == null) {
|
||||
cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
|
||||
this, method, targetClass);
|
||||
this.methodCache.put(cacheKey, cached);
|
||||
}
|
||||
return cached;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when advice has changed.
|
||||
*/
|
||||
protected void adviceChanged() {
|
||||
synchronized (this.methodCache) {
|
||||
this.methodCache.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this method on a new instance created by the no-arg constructor
|
||||
* to create an independent copy of the configuration from the given object.
|
||||
* @param other the AdvisedSupport object to copy configuration from
|
||||
*/
|
||||
protected void copyConfigurationFrom(AdvisedSupport other) {
|
||||
copyConfigurationFrom(other, other.targetSource, new ArrayList(other.advisors));
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the AOP configuration from the given AdvisedSupport object,
|
||||
* but allow substitution of a fresh TargetSource and a given interceptor chain.
|
||||
* @param other the AdvisedSupport object to take proxy configuration from
|
||||
* @param targetSource the new TargetSource
|
||||
* @param advisors the Advisors for the chain
|
||||
*/
|
||||
protected void copyConfigurationFrom(AdvisedSupport other, TargetSource targetSource, List advisors) {
|
||||
copyFrom(other);
|
||||
this.targetSource = targetSource;
|
||||
this.advisorChainFactory = other.advisorChainFactory;
|
||||
this.interfaces = new ArrayList(other.interfaces);
|
||||
for (Iterator it = advisors.iterator(); it.hasNext();) {
|
||||
Advisor advisor = (Advisor) it.next();
|
||||
if (advisor instanceof IntroductionAdvisor) {
|
||||
validateIntroductionAdvisor((IntroductionAdvisor) advisor);
|
||||
}
|
||||
Assert.notNull(advisor, "Advisor must not be null");
|
||||
this.advisors.add(advisor);
|
||||
}
|
||||
updateAdvisorArray();
|
||||
adviceChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a configuration-only copy of this AdvisedSupport,
|
||||
* replacing the TargetSource
|
||||
*/
|
||||
AdvisedSupport getConfigurationOnlyCopy() {
|
||||
AdvisedSupport copy = new AdvisedSupport();
|
||||
copy.copyFrom(this);
|
||||
copy.targetSource = EmptyTargetSource.forClass(getTargetClass(), getTargetSource().isStatic());
|
||||
copy.advisorChainFactory = this.advisorChainFactory;
|
||||
copy.interfaces = this.interfaces;
|
||||
copy.advisors = this.advisors;
|
||||
copy.updateAdvisorArray();
|
||||
return copy;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Serialization support
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
|
||||
// Rely on default serialization; just initialize state after deserialization.
|
||||
ois.defaultReadObject();
|
||||
|
||||
// Initialize transient fields.
|
||||
initMethodCache();
|
||||
}
|
||||
|
||||
|
||||
public String toProxyConfigString() {
|
||||
return toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* For debugging/diagnostic use.
|
||||
*/
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer(getClass().getName() + ": ");
|
||||
sb.append(this.interfaces.size()).append(" interfaces ");
|
||||
sb.append(ClassUtils.classNamesToString(this.interfaces)).append("; ");
|
||||
sb.append(this.advisors.size()).append(" advisors ");
|
||||
sb.append(this.advisors).append("; ");
|
||||
sb.append("targetSource [").append(this.targetSource).append("]; ");
|
||||
sb.append(super.toString());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Simple wrapper class around a Method. Used as the key when
|
||||
* caching methods, for efficient equals and hashCode comparisons.
|
||||
*/
|
||||
private static class MethodCacheKey {
|
||||
|
||||
private final Method method;
|
||||
|
||||
private final int hashCode;
|
||||
|
||||
public MethodCacheKey(Method method) {
|
||||
this.method = method;
|
||||
this.hashCode = method.hashCode();
|
||||
}
|
||||
|
||||
public boolean equals(Object other) {
|
||||
if (other == this) {
|
||||
return true;
|
||||
}
|
||||
MethodCacheKey otherKey = (MethodCacheKey) other;
|
||||
return (this.method == otherKey.method);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return this.hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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.framework;
|
||||
|
||||
/**
|
||||
* Listener to be registered on {@link ProxyCreatorSupport} objects
|
||||
* Allows for receiving callbacks on activation and change of advice.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @see ProxyCreatorSupport#addListener
|
||||
*/
|
||||
public interface AdvisedSupportListener {
|
||||
|
||||
/**
|
||||
* Invoked when the first proxy is created.
|
||||
* @param advised the AdvisedSupport object
|
||||
*/
|
||||
void activated(AdvisedSupport advised);
|
||||
|
||||
/**
|
||||
* Invoked when advice is changed after a proxy is created.
|
||||
* @param advised the AdvisedSupport object
|
||||
*/
|
||||
void adviceChanged(AdvisedSupport advised);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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.framework;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Factory interface for advisor chains.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public interface AdvisorChainFactory {
|
||||
|
||||
/**
|
||||
* Determine a list of {@link org.aopalliance.intercept.MethodInterceptor} objects
|
||||
* for the given advisor chain configuration.
|
||||
* @param config the AOP configuration in the form of an Advised object
|
||||
* @param method the proxied method
|
||||
* @param targetClass the target class
|
||||
* @return List of MethodInterceptors (may also include InterceptorAndDynamicMethodMatchers)
|
||||
*/
|
||||
List getInterceptorsAndDynamicInterceptionAdvice(Advised config, Method method, Class targetClass);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2002-2006 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.framework;
|
||||
|
||||
import org.springframework.core.NestedRuntimeException;
|
||||
|
||||
/**
|
||||
* Exception that gets thrown on illegal AOP configuration arguments.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @since 13.03.2003
|
||||
*/
|
||||
public class AopConfigException extends NestedRuntimeException {
|
||||
|
||||
/**
|
||||
* Constructor for AopConfigException.
|
||||
* @param msg the detail message
|
||||
*/
|
||||
public AopConfigException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for AopConfigException.
|
||||
* @param msg the detail message
|
||||
* @param cause the root cause
|
||||
*/
|
||||
public AopConfigException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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.framework;
|
||||
|
||||
import org.springframework.core.NamedThreadLocal;
|
||||
|
||||
/**
|
||||
* Class containing static methods used to obtain information about the current AOP invocation.
|
||||
*
|
||||
* <p>The <code>currentProxy()</code> method is usable if the AOP framework is configured to
|
||||
* expose the current proxy (not the default). It returns the AOP proxy in use. Target objects
|
||||
* or advice can use this to make advised calls, in the same way as <code>getEJBObject()</code>
|
||||
* can be used in EJBs. They can also use it to find advice configuration.
|
||||
*
|
||||
* <p>Spring's AOP framework does not expose proxies by default, as there is a performance cost
|
||||
* in doing so.
|
||||
*
|
||||
* <p>The functionality in this class might be used by a target object that needed access
|
||||
* to resources on the invocation. However, this approach should not be used when there is
|
||||
* a reasonable alternative, as it makes application code dependent on usage under AOP and
|
||||
* the Spring AOP framework in particular.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @since 13.03.2003
|
||||
*/
|
||||
public abstract class AopContext {
|
||||
|
||||
/**
|
||||
* ThreadLocal holder for AOP proxy associated with this thread.
|
||||
* Will contain <code>null</code> unless the "exposeProxy" property on
|
||||
* the controlling proxy configuration has been set to "true".
|
||||
* @see ProxyConfig#setExposeProxy
|
||||
*/
|
||||
private static final ThreadLocal currentProxy = new NamedThreadLocal("Current AOP proxy");
|
||||
|
||||
|
||||
/**
|
||||
* Try to return the current AOP proxy. This method is usable only if the
|
||||
* calling method has been invoked via AOP, and the AOP framework has been set
|
||||
* to expose proxies. Otherwise, this method will throw an IllegalStateException.
|
||||
* @return Object the current AOP proxy (never returns <code>null</code>)
|
||||
* @throws IllegalStateException if the proxy cannot be found, because the
|
||||
* method was invoked outside an AOP invocation context, or because the
|
||||
* AOP framework has not been configured to expose the proxy
|
||||
*/
|
||||
public static Object currentProxy() throws IllegalStateException {
|
||||
Object proxy = currentProxy.get();
|
||||
if (proxy == null) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available.");
|
||||
}
|
||||
return proxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the given proxy available via the <code>currentProxy()</code> method.
|
||||
* <p>Note that the caller should be careful to keep the old value as appropriate.
|
||||
* @param proxy the proxy to expose (or <code>null</code> to reset it)
|
||||
* @return the old proxy, which may be <code>null</code> if none was bound
|
||||
* @see #currentProxy()
|
||||
*/
|
||||
static Object setCurrentProxy(Object proxy) {
|
||||
Object old = currentProxy.get();
|
||||
currentProxy.set(proxy);
|
||||
return old;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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.framework;
|
||||
|
||||
/**
|
||||
* Marker interface that indicates a bean that is part of Spring's
|
||||
* AOP infrastructure. In particular, this implies that any such bean
|
||||
* is not subject to auto-proxying, even if a pointcut would match.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0.3
|
||||
* @see org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator
|
||||
* @see org.springframework.aop.scope.ScopedProxyFactoryBean
|
||||
*/
|
||||
public interface AopInfrastructureBean {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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.framework;
|
||||
|
||||
/**
|
||||
* Delegate interface for a configured AOP proxy, allowing for the creation
|
||||
* of actual proxy objects.
|
||||
*
|
||||
* <p>Out-of-the-box implementations are available for JDK dynamic proxies
|
||||
* and for CGLIB proxies, as applied by {@link DefaultAopProxyFactory}.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @see DefaultAopProxyFactory
|
||||
*/
|
||||
public interface AopProxy {
|
||||
|
||||
/**
|
||||
* Create a new proxy object.
|
||||
* <p>Uses the AopProxy's default class loader (if necessary for proxy creation):
|
||||
* usually, the thread context class loader.
|
||||
* @return the new proxy object (never <code>null</code>)
|
||||
* @see java.lang.Thread#getContextClassLoader()
|
||||
*/
|
||||
Object getProxy();
|
||||
|
||||
/**
|
||||
* Create a new proxy object.
|
||||
* <p>Uses the given class loader (if necessary for proxy creation).
|
||||
* <code>null</code> will simply be passed down and thus lead to the low-level
|
||||
* proxy facility's default, which is usually different from the default chosen
|
||||
* by the AopProxy implementation's {@link #getProxy()} method.
|
||||
* @param classLoader the class loader to create the proxy with
|
||||
* (or <code>null</code> for the low-level proxy facility's default)
|
||||
* @return the new proxy object (never <code>null</code>)
|
||||
*/
|
||||
Object getProxy(ClassLoader classLoader);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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.framework;
|
||||
|
||||
/**
|
||||
* Interface to be implemented by factories that are able to create
|
||||
* AOP proxies based on {@link AdvisedSupport} configuration objects.
|
||||
*
|
||||
* <p>Proxies should observe the following contract:
|
||||
* <ul>
|
||||
* <li>They should implement all interfaces that the configuration
|
||||
* indicates should be proxied.
|
||||
* <li>They should implement the {@link Advised} interface.
|
||||
* <li>They should implement the equals method to compare proxied
|
||||
* interfaces, advice, and target.
|
||||
* <li>They should be serializable if all advisors and target
|
||||
* are serializable.
|
||||
* <li>They should be thread-safe if advisors and target
|
||||
* are thread-safe.
|
||||
* </ul>
|
||||
*
|
||||
* <p>Proxies may or may not allow advice changes to be made.
|
||||
* If they do not permit advice changes (for example, because
|
||||
* the configuration was frozen) a proxy should throw an
|
||||
* {@link AopConfigException} on an attempted advice change.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public interface AopProxyFactory {
|
||||
|
||||
/**
|
||||
* Create an {@link AopProxy} for the given AOP configuration.
|
||||
* @param config the AOP configuration in the form of an
|
||||
* AdvisedSupport object
|
||||
* @return the corresponding AOP proxy
|
||||
* @throws AopConfigException if the configuration is invalid
|
||||
*/
|
||||
AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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.framework;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.springframework.aop.SpringProxy;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Utility methods for AOP proxy factories.
|
||||
* Mainly for internal use within the AOP framework.
|
||||
*
|
||||
* <p>See {@link org.springframework.aop.support.AopUtils} for a collection of
|
||||
* generic AOP utility methods which do not depend on AOP framework internals.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @see org.springframework.aop.support.AopUtils
|
||||
*/
|
||||
public abstract class AopProxyUtils {
|
||||
|
||||
/**
|
||||
* Determine the target class of the given bean instance,
|
||||
* which might be an AOP proxy.
|
||||
* <p>Returns the target class for an AOP proxy and the plain class else.
|
||||
* @param candidate the instance to check (might be an AOP proxy)
|
||||
* @return the target class (or the plain class of the given object as fallback)
|
||||
* @deprecated as of Spring 2.0.3, in favor of <code>AopUtils.getTargetClass</code>
|
||||
* @see org.springframework.aop.support.AopUtils#getTargetClass(Object)
|
||||
*/
|
||||
public static Class getTargetClass(Object candidate) {
|
||||
Assert.notNull(candidate, "Candidate object must not be null");
|
||||
if (AopUtils.isCglibProxy(candidate)) {
|
||||
return candidate.getClass().getSuperclass();
|
||||
}
|
||||
if (candidate instanceof Advised) {
|
||||
return ((Advised) candidate).getTargetSource().getTargetClass();
|
||||
}
|
||||
return candidate.getClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the complete set of interfaces to proxy for the given AOP configuration.
|
||||
* <p>This will always add the {@link Advised} interface unless the AdvisedSupport's
|
||||
* {@link AdvisedSupport#setOpaque "opaque"} flag is on. Always adds the
|
||||
* {@link org.springframework.aop.SpringProxy} marker interface.
|
||||
* @return the complete set of interfaces to proxy
|
||||
* @see Advised
|
||||
* @see org.springframework.aop.SpringProxy
|
||||
*/
|
||||
public static Class[] completeProxiedInterfaces(AdvisedSupport advised) {
|
||||
Class[] specifiedInterfaces = advised.getProxiedInterfaces();
|
||||
if (specifiedInterfaces.length == 0) {
|
||||
// No user-specified interfaces: check whether target class is an interface.
|
||||
Class targetClass = advised.getTargetClass();
|
||||
if (targetClass != null && targetClass.isInterface()) {
|
||||
specifiedInterfaces = new Class[] {targetClass};
|
||||
}
|
||||
}
|
||||
boolean addSpringProxy = !advised.isInterfaceProxied(SpringProxy.class);
|
||||
boolean addAdvised = !advised.isOpaque() && !advised.isInterfaceProxied(Advised.class);
|
||||
int nonUserIfcCount = 0;
|
||||
if (addSpringProxy) {
|
||||
nonUserIfcCount++;
|
||||
}
|
||||
if (addAdvised) {
|
||||
nonUserIfcCount++;
|
||||
}
|
||||
Class[] proxiedInterfaces = new Class[specifiedInterfaces.length + nonUserIfcCount];
|
||||
System.arraycopy(specifiedInterfaces, 0, proxiedInterfaces, 0, specifiedInterfaces.length);
|
||||
if (addSpringProxy) {
|
||||
proxiedInterfaces[specifiedInterfaces.length] = SpringProxy.class;
|
||||
}
|
||||
if (addAdvised) {
|
||||
proxiedInterfaces[proxiedInterfaces.length - 1] = Advised.class;
|
||||
}
|
||||
return proxiedInterfaces;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the user-specified interfaces that the given proxy implements,
|
||||
* i.e. all non-Advised interfaces that the proxy implements.
|
||||
* @param proxy the proxy to analyze (usually a JDK dynamic proxy)
|
||||
* @return all user-specified interfaces that the proxy implements,
|
||||
* in the original order (never <code>null</code> or empty)
|
||||
* @see Advised
|
||||
*/
|
||||
public static Class[] proxiedUserInterfaces(Object proxy) {
|
||||
Class[] proxyInterfaces = proxy.getClass().getInterfaces();
|
||||
int nonUserIfcCount = 0;
|
||||
if (proxy instanceof SpringProxy) {
|
||||
nonUserIfcCount++;
|
||||
}
|
||||
if (proxy instanceof Advised) {
|
||||
nonUserIfcCount++;
|
||||
}
|
||||
Class[] userInterfaces = new Class[proxyInterfaces.length - nonUserIfcCount];
|
||||
System.arraycopy(proxyInterfaces, 0, userInterfaces, 0, userInterfaces.length);
|
||||
Assert.notEmpty(userInterfaces, "JDK proxy must implement one or more interfaces");
|
||||
return userInterfaces;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check equality of the proxies behind the given AdvisedSupport objects.
|
||||
* Not the same as equality of the AdvisedSupport objects:
|
||||
* rather, equality of interfaces, advisors and target sources.
|
||||
*/
|
||||
public static boolean equalsInProxy(AdvisedSupport a, AdvisedSupport b) {
|
||||
return (a == b ||
|
||||
(equalsProxiedInterfaces(a, b) && equalsAdvisors(a, b) && a.getTargetSource().equals(b.getTargetSource())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check equality of the proxied interfaces behind the given AdvisedSupport objects.
|
||||
*/
|
||||
public static boolean equalsProxiedInterfaces(AdvisedSupport a, AdvisedSupport b) {
|
||||
return Arrays.equals(a.getProxiedInterfaces(), b.getProxiedInterfaces());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check equality of the advisors behind the given AdvisedSupport objects.
|
||||
*/
|
||||
public static boolean equalsAdvisors(AdvisedSupport a, AdvisedSupport b) {
|
||||
return Arrays.equals(a.getAdvisors(), b.getAdvisors());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,935 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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.framework;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.UndeclaredThrowableException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
import net.sf.cglib.core.CodeGenerationException;
|
||||
import net.sf.cglib.proxy.Callback;
|
||||
import net.sf.cglib.proxy.CallbackFilter;
|
||||
import net.sf.cglib.proxy.Dispatcher;
|
||||
import net.sf.cglib.proxy.Enhancer;
|
||||
import net.sf.cglib.proxy.Factory;
|
||||
import net.sf.cglib.proxy.MethodInterceptor;
|
||||
import net.sf.cglib.proxy.MethodProxy;
|
||||
import net.sf.cglib.proxy.NoOp;
|
||||
import net.sf.cglib.transform.impl.UndeclaredThrowableStrategy;
|
||||
import org.aopalliance.aop.Advice;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.aop.Advisor;
|
||||
import org.springframework.aop.PointcutAdvisor;
|
||||
import org.springframework.aop.RawTargetAccess;
|
||||
import org.springframework.aop.TargetSource;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.core.SmartClassLoader;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* CGLIB2-based {@link AopProxy} implementation for the Spring AOP framework.
|
||||
*
|
||||
* <p><i>Requires CGLIB 2.1+ on the classpath.</i>.
|
||||
* As of Spring 2.0, earlier CGLIB versions are not supported anymore.
|
||||
*
|
||||
* <p>Objects of this type should be obtained through proxy factories,
|
||||
* configured by an {@link AdvisedSupport} object. This class is internal
|
||||
* to Spring's AOP framework and need not be used directly by client code.
|
||||
*
|
||||
* <p>{@link DefaultAopProxyFactory} will automatically create CGLIB2-based
|
||||
* proxies if necessary, for example in case of proxying a target class
|
||||
* (see the {@link DefaultAopProxyFactory attendant javadoc} for details).
|
||||
*
|
||||
* <p>Proxies created using this class are thread-safe if the underlying
|
||||
* (target) class is thread-safe.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Rob Harrop
|
||||
* @author Juergen Hoeller
|
||||
* @author Ramnivas Laddad
|
||||
* @see net.sf.cglib.proxy.Enhancer
|
||||
* @see AdvisedSupport#setProxyTargetClass
|
||||
* @see DefaultAopProxyFactory
|
||||
*/
|
||||
final class Cglib2AopProxy implements AopProxy, Serializable {
|
||||
|
||||
// Constants for CGLIB callback array indices
|
||||
private static final int AOP_PROXY = 0;
|
||||
private static final int INVOKE_TARGET = 1;
|
||||
private static final int NO_OVERRIDE = 2;
|
||||
private static final int DISPATCH_TARGET = 3;
|
||||
private static final int DISPATCH_ADVISED = 4;
|
||||
private static final int INVOKE_EQUALS = 5;
|
||||
private static final int INVOKE_HASHCODE = 6;
|
||||
|
||||
|
||||
/** Logger available to subclasses; static to optimize serialization */
|
||||
protected final static Log logger = LogFactory.getLog(Cglib2AopProxy.class);
|
||||
|
||||
/** Keeps track of the Classes that we have validated for final methods */
|
||||
private static final Map validatedClasses = new WeakHashMap();
|
||||
|
||||
|
||||
/** The configuration used to configure this proxy */
|
||||
protected final AdvisedSupport advised;
|
||||
|
||||
private Object[] constructorArgs;
|
||||
|
||||
private Class[] constructorArgTypes;
|
||||
|
||||
/** Dispatcher used for methods on Advised */
|
||||
private final transient AdvisedDispatcher advisedDispatcher;
|
||||
|
||||
private transient Map fixedInterceptorMap;
|
||||
|
||||
private transient int fixedInterceptorOffset;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new Cglib2AopProxy for the given AOP configuration.
|
||||
* @param config the AOP configuration as AdvisedSupport object
|
||||
* @throws AopConfigException if the config is invalid. We try to throw an informative
|
||||
* exception in this case, rather than let a mysterious failure happen later.
|
||||
*/
|
||||
public Cglib2AopProxy(AdvisedSupport config) throws AopConfigException {
|
||||
Assert.notNull(config, "AdvisedSupport must not be null");
|
||||
if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
|
||||
throw new AopConfigException("No advisors and no TargetSource specified");
|
||||
}
|
||||
this.advised = config;
|
||||
this.advisedDispatcher = new AdvisedDispatcher(this.advised);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set constructor arguments to use for creating the proxy.
|
||||
* @param constructorArgs the constructor argument values
|
||||
* @param constructorArgTypes the constructor argument types
|
||||
*/
|
||||
public void setConstructorArguments(Object[] constructorArgs, Class[] constructorArgTypes) {
|
||||
if (constructorArgs == null || constructorArgTypes == null) {
|
||||
throw new IllegalArgumentException("Both 'constructorArgs' and 'constructorArgTypes' need to be specified");
|
||||
}
|
||||
if (constructorArgs.length != constructorArgTypes.length) {
|
||||
throw new IllegalArgumentException("Number of 'constructorArgs' (" + constructorArgs.length +
|
||||
") must match number of 'constructorArgTypes' (" + constructorArgTypes.length + ")");
|
||||
}
|
||||
this.constructorArgs = constructorArgs;
|
||||
this.constructorArgTypes = constructorArgTypes;
|
||||
}
|
||||
|
||||
|
||||
public Object getProxy() {
|
||||
return getProxy(null);
|
||||
}
|
||||
|
||||
public Object getProxy(ClassLoader classLoader) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Creating CGLIB2 proxy: target source is " + this.advised.getTargetSource());
|
||||
}
|
||||
|
||||
try {
|
||||
Class rootClass = this.advised.getTargetClass();
|
||||
Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");
|
||||
|
||||
Class proxySuperClass = rootClass;
|
||||
if (AopUtils.isCglibProxyClass(rootClass)) {
|
||||
proxySuperClass = rootClass.getSuperclass();
|
||||
Class[] additionalInterfaces = rootClass.getInterfaces();
|
||||
for (int i = 0; i < additionalInterfaces.length; i++) {
|
||||
Class additionalInterface = additionalInterfaces[i];
|
||||
this.advised.addInterface(additionalInterface);
|
||||
}
|
||||
}
|
||||
|
||||
// Validate the class, writing log messages as necessary.
|
||||
validateClassIfNecessary(proxySuperClass);
|
||||
|
||||
// Configure CGLIB Enhancer...
|
||||
Enhancer enhancer = createEnhancer();
|
||||
if (classLoader != null) {
|
||||
enhancer.setClassLoader(classLoader);
|
||||
if (classLoader instanceof SmartClassLoader &&
|
||||
((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
|
||||
enhancer.setUseCache(false);
|
||||
}
|
||||
}
|
||||
enhancer.setSuperclass(proxySuperClass);
|
||||
enhancer.setStrategy(new UndeclaredThrowableStrategy(UndeclaredThrowableException.class));
|
||||
enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
|
||||
enhancer.setInterceptDuringConstruction(false);
|
||||
|
||||
Callback[] callbacks = getCallbacks(rootClass);
|
||||
enhancer.setCallbacks(callbacks);
|
||||
enhancer.setCallbackFilter(new ProxyCallbackFilter(
|
||||
this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
|
||||
|
||||
Class[] types = new Class[callbacks.length];
|
||||
for (int x = 0; x < types.length; x++) {
|
||||
types[x] = callbacks[x].getClass();
|
||||
}
|
||||
enhancer.setCallbackTypes(types);
|
||||
|
||||
// Generate the proxy class and create a proxy instance.
|
||||
Object proxy;
|
||||
if (this.constructorArgs != null) {
|
||||
proxy = enhancer.create(this.constructorArgTypes, this.constructorArgs);
|
||||
}
|
||||
else {
|
||||
proxy = enhancer.create();
|
||||
}
|
||||
|
||||
return proxy;
|
||||
}
|
||||
catch (CodeGenerationException ex) {
|
||||
throw new AopConfigException("Could not generate CGLIB subclass of class [" +
|
||||
this.advised.getTargetClass() + "]: " +
|
||||
"Common causes of this problem include using a final class or a non-visible class",
|
||||
ex);
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
throw new AopConfigException("Could not generate CGLIB subclass of class [" +
|
||||
this.advised.getTargetClass() + "]: " +
|
||||
"Common causes of this problem include using a final class or a non-visible class",
|
||||
ex);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// TargetSource.getTarget() failed
|
||||
throw new AopConfigException("Unexpected AOP exception", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the CGLIB {@link Enhancer}. Subclasses may wish to override this to return a custom
|
||||
* {@link Enhancer} implementation.
|
||||
*/
|
||||
protected Enhancer createEnhancer() {
|
||||
return new Enhancer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see whether the supplied <code>Class</code> has already been validated and
|
||||
* validates it if not.
|
||||
*/
|
||||
private void validateClassIfNecessary(Class proxySuperClass) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
synchronized (validatedClasses) {
|
||||
if (!validatedClasses.containsKey(proxySuperClass)) {
|
||||
doValidateClass(proxySuperClass);
|
||||
validatedClasses.put(proxySuperClass, Boolean.TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for final methods on the <code>Class</code> and writes warnings to the log
|
||||
* for each one found.
|
||||
*/
|
||||
private void doValidateClass(Class proxySuperClass) {
|
||||
Method[] methods = proxySuperClass.getMethods();
|
||||
for (int i = 0; i < methods.length; i++) {
|
||||
Method method = methods[i];
|
||||
if (!Object.class.equals(method.getDeclaringClass()) && Modifier.isFinal(method.getModifiers())) {
|
||||
logger.warn("Unable to proxy method [" + method + "] because it is final: " +
|
||||
"All calls to this method via a proxy will be routed directly to the proxy.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Callback[] getCallbacks(Class rootClass) throws Exception {
|
||||
// Parameters used for optimisation choices...
|
||||
boolean exposeProxy = this.advised.isExposeProxy();
|
||||
boolean isFrozen = this.advised.isFrozen();
|
||||
boolean isStatic = this.advised.getTargetSource().isStatic();
|
||||
|
||||
// Choose an "aop" interceptor (used for AOP calls).
|
||||
Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
|
||||
|
||||
// Choose a "straight to target" interceptor. (used for calls that are
|
||||
// unadvised but can return this). May be required to expose the proxy.
|
||||
Callback targetInterceptor = null;
|
||||
|
||||
if (exposeProxy) {
|
||||
targetInterceptor = isStatic ?
|
||||
(Callback) new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()) :
|
||||
(Callback) new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource());
|
||||
}
|
||||
else {
|
||||
targetInterceptor = isStatic ?
|
||||
(Callback) new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) :
|
||||
(Callback) new DynamicUnadvisedInterceptor(this.advised.getTargetSource());
|
||||
}
|
||||
|
||||
// Choose a "direct to target" dispatcher (used for
|
||||
// unadvised calls to static targets that cannot return this).
|
||||
Callback targetDispatcher = isStatic ?
|
||||
(Callback) new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp();
|
||||
|
||||
Callback[] mainCallbacks = new Callback[]{
|
||||
aopInterceptor, // for normal advice
|
||||
targetInterceptor, // invoke target without considering advice, if optimized
|
||||
new SerializableNoOp(), // no override for methods mapped to this
|
||||
targetDispatcher, this.advisedDispatcher,
|
||||
new EqualsInterceptor(this.advised),
|
||||
new HashCodeInterceptor(this.advised)
|
||||
};
|
||||
|
||||
Callback[] callbacks;
|
||||
|
||||
// If the target is a static one and the advice chain is frozen,
|
||||
// then we can make some optimisations by sending the AOP calls
|
||||
// direct to the target using the fixed chain for that method.
|
||||
if (isStatic && isFrozen) {
|
||||
Method[] methods = rootClass.getMethods();
|
||||
Callback[] fixedCallbacks = new Callback[methods.length];
|
||||
this.fixedInterceptorMap = new HashMap(methods.length);
|
||||
|
||||
// TODO: small memory optimisation here (can skip creation for
|
||||
// methods with no advice)
|
||||
for (int x = 0; x < methods.length; x++) {
|
||||
List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(methods[x], rootClass);
|
||||
fixedCallbacks[x] = new FixedChainStaticTargetInterceptor(
|
||||
chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass());
|
||||
this.fixedInterceptorMap.put(methods[x].toString(), new Integer(x));
|
||||
}
|
||||
|
||||
// Now copy both the callbacks from mainCallbacks
|
||||
// and fixedCallbacks into the callbacks array.
|
||||
callbacks = new Callback[mainCallbacks.length + fixedCallbacks.length];
|
||||
|
||||
for (int x = 0; x < mainCallbacks.length; x++) {
|
||||
callbacks[x] = mainCallbacks[x];
|
||||
}
|
||||
|
||||
for (int x = 0; x < fixedCallbacks.length; x++) {
|
||||
callbacks[x + mainCallbacks.length] = fixedCallbacks[x];
|
||||
}
|
||||
|
||||
this.fixedInterceptorOffset = mainCallbacks.length;
|
||||
}
|
||||
else {
|
||||
callbacks = mainCallbacks;
|
||||
}
|
||||
return callbacks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap a return of this if necessary to be the proxy
|
||||
*/
|
||||
private static Object massageReturnTypeIfNecessary(Object proxy, Object target, Method method, Object retVal) {
|
||||
// Massage return value if necessary
|
||||
if (retVal != null && retVal == target &&
|
||||
!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
|
||||
// Special case: it returned "this".
|
||||
// Note that we can't help if the target sets a reference
|
||||
// to itself in another returned object.
|
||||
retVal = proxy;
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
public boolean equals(Object other) {
|
||||
return (this == other || (other instanceof Cglib2AopProxy &&
|
||||
AopProxyUtils.equalsInProxy(this.advised, ((Cglib2AopProxy) other).advised)));
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return Cglib2AopProxy.class.hashCode() * 13 + this.advised.getTargetSource().hashCode();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializable replacement for CGLIB's NoOp interface.
|
||||
* Public to allow use elsewhere in the framework.
|
||||
*/
|
||||
public static class SerializableNoOp implements NoOp, Serializable {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Method interceptor used for static targets with no advice chain. The call
|
||||
* is passed directly back to the target. Used when the proxy needs to be
|
||||
* exposed and it can't be determined that the method won't return
|
||||
* <code>this</code>.
|
||||
*/
|
||||
private static class StaticUnadvisedInterceptor implements MethodInterceptor, Serializable {
|
||||
|
||||
private final Object target;
|
||||
|
||||
public StaticUnadvisedInterceptor(Object target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
|
||||
Object retVal = methodProxy.invoke(this.target, args);
|
||||
return massageReturnTypeIfNecessary(proxy, this.target, method, retVal);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Method interceptor used for static targets with no advice chain, when the
|
||||
* proxy is to be exposed.
|
||||
*/
|
||||
private static class StaticUnadvisedExposedInterceptor implements MethodInterceptor, Serializable {
|
||||
|
||||
private final Object target;
|
||||
|
||||
public StaticUnadvisedExposedInterceptor(Object target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
|
||||
Object oldProxy = null;
|
||||
try {
|
||||
oldProxy = AopContext.setCurrentProxy(proxy);
|
||||
Object retVal = methodProxy.invoke(this.target, args);
|
||||
return massageReturnTypeIfNecessary(proxy, this.target, method, retVal);
|
||||
}
|
||||
finally {
|
||||
AopContext.setCurrentProxy(oldProxy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Interceptor used to invoke a dynamic target without creating a method
|
||||
* invocation or evaluating an advice chain. (We know there was no advice
|
||||
* for this method.)
|
||||
*/
|
||||
private static class DynamicUnadvisedInterceptor implements MethodInterceptor, Serializable {
|
||||
|
||||
private final TargetSource targetSource;
|
||||
|
||||
public DynamicUnadvisedInterceptor(TargetSource targetSource) {
|
||||
this.targetSource = targetSource;
|
||||
}
|
||||
|
||||
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
|
||||
Object target = this.targetSource.getTarget();
|
||||
try {
|
||||
Object retVal = methodProxy.invoke(target, args);
|
||||
return massageReturnTypeIfNecessary(proxy, target, method, retVal);
|
||||
}
|
||||
finally {
|
||||
this.targetSource.releaseTarget(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Interceptor for unadvised dynamic targets when the proxy needs exposing.
|
||||
*/
|
||||
private static class DynamicUnadvisedExposedInterceptor implements MethodInterceptor, Serializable {
|
||||
|
||||
private final TargetSource targetSource;
|
||||
|
||||
public DynamicUnadvisedExposedInterceptor(TargetSource targetSource) {
|
||||
this.targetSource = targetSource;
|
||||
}
|
||||
|
||||
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
|
||||
Object oldProxy = null;
|
||||
Object target = this.targetSource.getTarget();
|
||||
try {
|
||||
oldProxy = AopContext.setCurrentProxy(proxy);
|
||||
Object retVal = methodProxy.invoke(target, args);
|
||||
return massageReturnTypeIfNecessary(proxy, target, method, retVal);
|
||||
}
|
||||
finally {
|
||||
AopContext.setCurrentProxy(oldProxy);
|
||||
this.targetSource.releaseTarget(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Dispatcher for a static target. Dispatcher is much faster than
|
||||
* interceptor. This will be used whenever it can be determined that a
|
||||
* method definitely does not return "this"
|
||||
*/
|
||||
private static class StaticDispatcher implements Dispatcher, Serializable {
|
||||
|
||||
private Object target;
|
||||
|
||||
public StaticDispatcher(Object target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
public Object loadObject() {
|
||||
return this.target;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Dispatcher for any methods declared on the Advised class.
|
||||
*/
|
||||
private static class AdvisedDispatcher implements Dispatcher, Serializable {
|
||||
|
||||
private final AdvisedSupport advised;
|
||||
|
||||
public AdvisedDispatcher(AdvisedSupport advised) {
|
||||
this.advised = advised;
|
||||
}
|
||||
|
||||
public Object loadObject() throws Exception {
|
||||
return this.advised;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Dispatcher for the <code>equals</code> method.
|
||||
* Ensures that the method call is always handled by this class.
|
||||
*/
|
||||
private static class EqualsInterceptor implements MethodInterceptor, Serializable {
|
||||
|
||||
private final AdvisedSupport advised;
|
||||
|
||||
public EqualsInterceptor(AdvisedSupport advised) {
|
||||
this.advised = advised;
|
||||
}
|
||||
|
||||
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) {
|
||||
Object other = args[0];
|
||||
if (proxy == other) {
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
AdvisedSupport otherAdvised = null;
|
||||
if (other instanceof Factory) {
|
||||
Callback callback = ((Factory) other).getCallback(INVOKE_EQUALS);
|
||||
if (!(callback instanceof EqualsInterceptor)) {
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
otherAdvised = ((EqualsInterceptor) callback).advised;
|
||||
}
|
||||
else {
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
return (AopProxyUtils.equalsInProxy(this.advised, otherAdvised) ? Boolean.TRUE : Boolean.FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Dispatcher for the <code>hashCode</code> method.
|
||||
* Ensures that the method call is always handled by this class.
|
||||
*/
|
||||
private static class HashCodeInterceptor implements MethodInterceptor, Serializable {
|
||||
|
||||
private final AdvisedSupport advised;
|
||||
|
||||
public HashCodeInterceptor(AdvisedSupport advised) {
|
||||
this.advised = advised;
|
||||
}
|
||||
|
||||
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) {
|
||||
return new Integer(Cglib2AopProxy.class.hashCode() * 13 + this.advised.getTargetSource().hashCode());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Interceptor used specifically for advised methods on a frozen, static proxy.
|
||||
*/
|
||||
private static class FixedChainStaticTargetInterceptor implements MethodInterceptor, Serializable {
|
||||
|
||||
private final List adviceChain;
|
||||
|
||||
private final Object target;
|
||||
|
||||
private final Class targetClass;
|
||||
|
||||
public FixedChainStaticTargetInterceptor(List adviceChain, Object target, Class targetClass) {
|
||||
this.adviceChain = adviceChain;
|
||||
this.target = target;
|
||||
this.targetClass = targetClass;
|
||||
}
|
||||
|
||||
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
|
||||
Object retVal = null;
|
||||
MethodInvocation invocation = new CglibMethodInvocation(proxy, this.target, method, args,
|
||||
this.targetClass, this.adviceChain, methodProxy);
|
||||
// If we get here, we need to create a MethodInvocation.
|
||||
retVal = invocation.proceed();
|
||||
retVal = massageReturnTypeIfNecessary(proxy, this.target, method, retVal);
|
||||
return retVal;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* General purpose AOP callback. Used when the target is dynamic or when the
|
||||
* proxy is not frozen.
|
||||
*/
|
||||
private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {
|
||||
|
||||
private AdvisedSupport advised;
|
||||
|
||||
public DynamicAdvisedInterceptor(AdvisedSupport advised) {
|
||||
this.advised = advised;
|
||||
}
|
||||
|
||||
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
|
||||
MethodInvocation invocation = null;
|
||||
Object oldProxy = null;
|
||||
boolean setProxyContext = false;
|
||||
Class targetClass = null;
|
||||
Object target = null;
|
||||
try {
|
||||
Object retVal = null;
|
||||
if (this.advised.exposeProxy) {
|
||||
// Make invocation available if necessary.
|
||||
oldProxy = AopContext.setCurrentProxy(proxy);
|
||||
setProxyContext = true;
|
||||
}
|
||||
// May be <code>null</code>. Get as late as possible to minimize the time we
|
||||
// "own" the target, in case it comes from a pool.
|
||||
target = getTarget();
|
||||
if (target != null) {
|
||||
targetClass = target.getClass();
|
||||
}
|
||||
List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
|
||||
// Check whether we only have one InvokerInterceptor: that is,
|
||||
// no real advice, but just reflective invocation of the target.
|
||||
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
|
||||
// We can skip creating a MethodInvocation: just invoke the target directly.
|
||||
// Note that the final invoker must be an InvokerInterceptor, so we know
|
||||
// it does nothing but a reflective operation on the target, and no hot
|
||||
// swapping or fancy proxying.
|
||||
retVal = methodProxy.invoke(target, args);
|
||||
}
|
||||
else {
|
||||
// We need to create a method invocation...
|
||||
invocation = new CglibMethodInvocation(proxy, target, method, args,
|
||||
targetClass, chain, methodProxy);
|
||||
// If we get here, we need to create a MethodInvocation.
|
||||
retVal = invocation.proceed();
|
||||
}
|
||||
|
||||
retVal = massageReturnTypeIfNecessary(proxy, target, method, retVal);
|
||||
return retVal;
|
||||
}
|
||||
finally {
|
||||
if (target != null) {
|
||||
releaseTarget(target);
|
||||
}
|
||||
if (setProxyContext) {
|
||||
// Restore old proxy.
|
||||
AopContext.setCurrentProxy(oldProxy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean equals(Object other) {
|
||||
return (this == other ||
|
||||
(other instanceof DynamicAdvisedInterceptor &&
|
||||
this.advised.equals(((DynamicAdvisedInterceptor) other).advised)));
|
||||
}
|
||||
|
||||
/**
|
||||
* CGLIB uses this to drive proxy creation.
|
||||
*/
|
||||
public int hashCode() {
|
||||
return this.advised.hashCode();
|
||||
}
|
||||
|
||||
protected Object getTarget() throws Exception {
|
||||
return this.advised.getTargetSource().getTarget();
|
||||
}
|
||||
|
||||
protected void releaseTarget(Object target) throws Exception {
|
||||
this.advised.getTargetSource().releaseTarget(target);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Implementation of AOP Alliance MethodInvocation used by this AOP proxy.
|
||||
*/
|
||||
private static class CglibMethodInvocation extends ReflectiveMethodInvocation {
|
||||
|
||||
private final MethodProxy methodProxy;
|
||||
|
||||
private boolean protectedMethod;
|
||||
|
||||
public CglibMethodInvocation(Object proxy, Object target, Method method, Object[] arguments,
|
||||
Class targetClass, List interceptorsAndDynamicMethodMatchers, MethodProxy methodProxy) {
|
||||
super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers);
|
||||
this.methodProxy = methodProxy;
|
||||
this.protectedMethod = Modifier.isProtected(method.getModifiers());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives a marginal performance improvement versus using reflection to
|
||||
* invoke the target when invoking public methods.
|
||||
*/
|
||||
protected Object invokeJoinpoint() throws Throwable {
|
||||
if (this.protectedMethod) {
|
||||
return super.invokeJoinpoint();
|
||||
}
|
||||
else {
|
||||
return this.methodProxy.invoke(this.target, this.arguments);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* CallbackFilter to assign Callbacks to methods.
|
||||
*/
|
||||
private static class ProxyCallbackFilter implements CallbackFilter {
|
||||
|
||||
private final AdvisedSupport advised;
|
||||
|
||||
private final Map fixedInterceptorMap;
|
||||
|
||||
private final int fixedInterceptorOffset;
|
||||
|
||||
public ProxyCallbackFilter(AdvisedSupport advised, Map fixedInterceptorMap, int fixedInterceptorOffset) {
|
||||
this.advised = advised;
|
||||
this.fixedInterceptorMap = fixedInterceptorMap;
|
||||
this.fixedInterceptorOffset = fixedInterceptorOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of CallbackFilter.accept() to return the index of the
|
||||
* callback we need.
|
||||
* <p>The callbacks for each proxy are built up of a set of fixed callbacks
|
||||
* for general use and then a set of callbacks that are specific to a method
|
||||
* for use on static targets with a fixed advice chain.
|
||||
* <p>The callback used is determined thus:
|
||||
* <dl>
|
||||
* <dt>For exposed proxies</dt>
|
||||
* <dd>Exposing the proxy requires code to execute before and after the
|
||||
* method/chain invocation. This means we must use
|
||||
* DynamicAdvisedInterceptor, since all other interceptors can avoid the
|
||||
* need for a try/catch block</dd>
|
||||
* <dt>For Object.finalize():</dt>
|
||||
* <dd>No override for this method is used.</dd>
|
||||
* <dt>For equals():</dt>
|
||||
* <dd>The EqualsInterceptor is used to redirect equals() calls to a
|
||||
* special handler to this proxy.</dd>
|
||||
* <dt>For methods on the Advised class:</dt>
|
||||
* <dd>the AdvisedDispatcher is used to dispatch the call directly to
|
||||
* the target</dd>
|
||||
* <dt>For advised methods:</dt>
|
||||
* <dd>If the target is static and the advice chain is frozen then a
|
||||
* FixedChainStaticTargetInterceptor specific to the method is used to
|
||||
* invoke the advice chain. Otherwise a DyanmicAdvisedInterceptor is
|
||||
* used.</dd>
|
||||
* <dt>For non-advised methods:</dt>
|
||||
* <dd>Where it can be determined that the method will not return <code>this</code>
|
||||
* or when <code>ProxyFactory.getExposeProxy()</code> returns <code>false</code>,
|
||||
* then a Dispatcher is used. For static targets, the StaticDispatcher is used;
|
||||
* and for dynamic targets, a DynamicUnadvisedInterceptor is used.
|
||||
* If it possible for the method to return <code>this</code> then a
|
||||
* StaticUnadvisedInterceptor is used for static targets - the
|
||||
* DynamicUnadvisedInterceptor already considers this.</dd>
|
||||
* </dl>
|
||||
*/
|
||||
public int accept(Method method) {
|
||||
if (AopUtils.isFinalizeMethod(method)) {
|
||||
logger.debug("Found finalize() method - using NO_OVERRIDE");
|
||||
return NO_OVERRIDE;
|
||||
}
|
||||
if (!this.advised.isOpaque() && method.getDeclaringClass().isInterface() &&
|
||||
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Method is declared on Advised interface: " + method);
|
||||
}
|
||||
return DISPATCH_ADVISED;
|
||||
}
|
||||
// We must always proxy equals, to direct calls to this.
|
||||
if (AopUtils.isEqualsMethod(method)) {
|
||||
logger.debug("Found 'equals' method: " + method);
|
||||
return INVOKE_EQUALS;
|
||||
}
|
||||
// We must always calculate hashCode based on the proxy.
|
||||
if (AopUtils.isHashCodeMethod(method)) {
|
||||
logger.debug("Found 'hashCode' method: " + method);
|
||||
return INVOKE_HASHCODE;
|
||||
}
|
||||
Class targetClass = this.advised.getTargetClass();
|
||||
// Proxy is not yet available, but that shouldn't matter.
|
||||
List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
|
||||
boolean haveAdvice = !chain.isEmpty();
|
||||
boolean exposeProxy = this.advised.isExposeProxy();
|
||||
boolean isStatic = this.advised.getTargetSource().isStatic();
|
||||
boolean isFrozen = this.advised.isFrozen();
|
||||
if (haveAdvice || !isFrozen) {
|
||||
// If exposing the proxy, then AOP_PROXY must be used.
|
||||
if (exposeProxy) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Must expose proxy on advised method: " + method);
|
||||
}
|
||||
return AOP_PROXY;
|
||||
}
|
||||
String key = method.toString();
|
||||
// Check to see if we have fixed interceptor to serve this method.
|
||||
// Else use the AOP_PROXY.
|
||||
if (isStatic && isFrozen && this.fixedInterceptorMap.containsKey(key)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Method has advice and optimisations are enabled: " + method);
|
||||
}
|
||||
// We know that we are optimising so we can use the
|
||||
// FixedStaticChainInterceptors.
|
||||
int index = ((Integer) this.fixedInterceptorMap.get(key)).intValue();
|
||||
return (index + this.fixedInterceptorOffset);
|
||||
}
|
||||
else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Unable to apply any optimisations to advised method: " + method);
|
||||
}
|
||||
return AOP_PROXY;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// See if the return type of the method is outside the class hierarchy
|
||||
// of the target type. If so we know it never needs to have return type
|
||||
// massage and can use a dispatcher.
|
||||
// If the proxy is being exposed, then must use the interceptor the
|
||||
// correct one is already configured. If the target is not static cannot
|
||||
// use a Dispatcher because the target can not then be released.
|
||||
if (exposeProxy || !isStatic) {
|
||||
return INVOKE_TARGET;
|
||||
}
|
||||
Class returnType = method.getReturnType();
|
||||
if (targetClass == returnType) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Method " + method +
|
||||
"has return type same as target type (may return this) - using INVOKE_TARGET");
|
||||
}
|
||||
return INVOKE_TARGET;
|
||||
}
|
||||
else if (returnType.isPrimitive() || !returnType.isAssignableFrom(targetClass)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Method " + method +
|
||||
" has return type that ensures this cannot be returned- using DISPATCH_TARGET");
|
||||
}
|
||||
return DISPATCH_TARGET;
|
||||
}
|
||||
else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Method " + method +
|
||||
"has return type that is assignable from the target type (may return this) - " +
|
||||
"using INVOKE_TARGET");
|
||||
}
|
||||
return INVOKE_TARGET;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean equals(Object other) {
|
||||
if (other == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof ProxyCallbackFilter)) {
|
||||
return false;
|
||||
}
|
||||
ProxyCallbackFilter otherCallbackFilter = (ProxyCallbackFilter) other;
|
||||
AdvisedSupport otherAdvised = otherCallbackFilter.advised;
|
||||
if (this.advised == null || otherAdvised == null) {
|
||||
return false;
|
||||
}
|
||||
if (this.advised.isFrozen() != otherAdvised.isFrozen()) {
|
||||
return false;
|
||||
}
|
||||
if (this.advised.isExposeProxy() != otherAdvised.isExposeProxy()) {
|
||||
return false;
|
||||
}
|
||||
if (this.advised.getTargetSource().isStatic() != otherAdvised.getTargetSource().isStatic()) {
|
||||
return false;
|
||||
}
|
||||
if (!AopProxyUtils.equalsProxiedInterfaces(this.advised, otherAdvised)) {
|
||||
return false;
|
||||
}
|
||||
// Advice instance identity is unimportant to the proxy class:
|
||||
// All that matters is type and ordering.
|
||||
Advisor[] thisAdvisors = this.advised.getAdvisors();
|
||||
Advisor[] thatAdvisors = otherAdvised.getAdvisors();
|
||||
if (thisAdvisors.length != thatAdvisors.length) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < thisAdvisors.length; i++) {
|
||||
Advisor thisAdvisor = thisAdvisors[i];
|
||||
Advisor thatAdvisor = thatAdvisors[i];
|
||||
if (!equalsAdviceClasses(thisAdvisor, thatAdvisor)) {
|
||||
return false;
|
||||
}
|
||||
if (!equalsPointcuts(thisAdvisor, thatAdvisor)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean equalsAdviceClasses(Advisor a, Advisor b) {
|
||||
Advice aa = a.getAdvice();
|
||||
Advice ba = b.getAdvice();
|
||||
if (aa == null || ba == null) {
|
||||
return (aa == ba);
|
||||
}
|
||||
return aa.getClass().equals(ba.getClass());
|
||||
}
|
||||
|
||||
private boolean equalsPointcuts(Advisor a, Advisor b) {
|
||||
// If only one of the advisor (but not both) is PointcutAdvisor, then it is a mismatch.
|
||||
// Takes care of the situations where an IntroductionAdvisor is used (see SPR-3959).
|
||||
if (a instanceof PointcutAdvisor ^ b instanceof PointcutAdvisor) {
|
||||
return false;
|
||||
}
|
||||
// If both are PointcutAdvisor, match their pointcuts.
|
||||
if (a instanceof PointcutAdvisor && b instanceof PointcutAdvisor) {
|
||||
return ObjectUtils.nullSafeEquals(((PointcutAdvisor) a).getPointcut(), ((PointcutAdvisor) b).getPointcut());
|
||||
}
|
||||
// If neither is PointcutAdvisor, then from the pointcut matching perspective, it is a match.
|
||||
return true;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int hashCode = 0;
|
||||
Advisor[] advisors = this.advised.getAdvisors();
|
||||
for (int i = 0; i < advisors.length; i++) {
|
||||
Advice advice = advisors[i].getAdvice();
|
||||
if (advice != null) {
|
||||
hashCode = 13 * hashCode + advice.getClass().hashCode();
|
||||
}
|
||||
}
|
||||
hashCode = 13 * hashCode + (this.advised.isFrozen() ? 1 : 0);
|
||||
hashCode = 13 * hashCode + (this.advised.isExposeProxy() ? 1 : 0);
|
||||
hashCode = 13 * hashCode + (this.advised.isOptimize() ? 1 : 0);
|
||||
hashCode = 13 * hashCode + (this.advised.isOpaque() ? 1 : 0);
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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.framework;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.aopalliance.intercept.Interceptor;
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
|
||||
import org.springframework.aop.Advisor;
|
||||
import org.springframework.aop.IntroductionAdvisor;
|
||||
import org.springframework.aop.MethodMatcher;
|
||||
import org.springframework.aop.PointcutAdvisor;
|
||||
import org.springframework.aop.framework.adapter.AdvisorAdapterRegistry;
|
||||
import org.springframework.aop.framework.adapter.GlobalAdvisorAdapterRegistry;
|
||||
import org.springframework.aop.support.MethodMatchers;
|
||||
|
||||
/**
|
||||
* A simple but definitive way of working out an advice chain for a Method,
|
||||
* given an {@link Advised} object. Always rebuilds each advice chain;
|
||||
* caching can be provided by subclasses.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Rod Johnson
|
||||
* @author Adrian Colyer
|
||||
* @since 2.0.3
|
||||
*/
|
||||
public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializable {
|
||||
|
||||
public List getInterceptorsAndDynamicInterceptionAdvice(Advised config, Method method, Class targetClass) {
|
||||
// This is somewhat tricky... we have to process introductions first,
|
||||
// but we need to preserve order in the ultimate list.
|
||||
List interceptorList = new ArrayList(config.getAdvisors().length);
|
||||
boolean hasIntroductions = hasMatchingIntroductions(config, targetClass);
|
||||
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
|
||||
Advisor[] advisors = config.getAdvisors();
|
||||
for (int i = 0; i < advisors.length; i++) {
|
||||
Advisor advisor = advisors[i];
|
||||
if (advisor instanceof PointcutAdvisor) {
|
||||
// Add it conditionally.
|
||||
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
|
||||
if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass)) {
|
||||
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
|
||||
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
|
||||
if (MethodMatchers.matches(mm, method, targetClass, hasIntroductions)) {
|
||||
if (mm.isRuntime()) {
|
||||
// Creating a new object instance in the getInterceptors() method
|
||||
// isn't a problem as we normally cache created chains.
|
||||
for (int j = 0; j < interceptors.length; j++) {
|
||||
interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptors[j], mm));
|
||||
}
|
||||
}
|
||||
else {
|
||||
interceptorList.addAll(Arrays.asList(interceptors));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (advisor instanceof IntroductionAdvisor) {
|
||||
IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
|
||||
if (config.isPreFiltered() || ia.getClassFilter().matches(targetClass)) {
|
||||
Interceptor[] interceptors = registry.getInterceptors(advisor);
|
||||
interceptorList.addAll(Arrays.asList(interceptors));
|
||||
}
|
||||
}
|
||||
else {
|
||||
Interceptor[] interceptors = registry.getInterceptors(advisor);
|
||||
interceptorList.addAll(Arrays.asList(interceptors));
|
||||
}
|
||||
}
|
||||
return interceptorList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the Advisors contain matching introductions.
|
||||
*/
|
||||
private static boolean hasMatchingIntroductions(Advised config, Class targetClass) {
|
||||
for (int i = 0; i < config.getAdvisors().length; i++) {
|
||||
Advisor advisor = config.getAdvisors()[i];
|
||||
if (advisor instanceof IntroductionAdvisor) {
|
||||
IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
|
||||
if (ia.getClassFilter().matches(targetClass)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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.framework;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.springframework.aop.SpringProxy;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Default {@link AopProxyFactory} implementation,
|
||||
* creating either a CGLIB proxy or a JDK dynamic proxy.
|
||||
*
|
||||
* <p>Creates a CGLIB proxy if one the following is true
|
||||
* for a given {@link AdvisedSupport} instance:
|
||||
* <ul>
|
||||
* <li>the "optimize" flag is set
|
||||
* <li>the "proxyTargetClass" flag is set
|
||||
* <li>no proxy interfaces have been specified
|
||||
* </ul>
|
||||
*
|
||||
* <p>Note that the CGLIB library classes have to be present on
|
||||
* the class path if an actual CGLIB proxy needs to be created.
|
||||
*
|
||||
* <p>In general, specify "proxyTargetClass" to enforce a CGLIB proxy,
|
||||
* or specify one or more interfaces to use a JDK dynamic proxy.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @since 12.03.2004
|
||||
* @see AdvisedSupport#setOptimize
|
||||
* @see AdvisedSupport#setProxyTargetClass
|
||||
* @see AdvisedSupport#setInterfaces
|
||||
*/
|
||||
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
|
||||
|
||||
/** Whether the CGLIB2 library is present on the classpath */
|
||||
private static final boolean cglibAvailable =
|
||||
ClassUtils.isPresent("net.sf.cglib.proxy.Enhancer", DefaultAopProxyFactory.class.getClassLoader());
|
||||
|
||||
|
||||
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
|
||||
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
|
||||
Class targetClass = config.getTargetClass();
|
||||
if (targetClass == null) {
|
||||
throw new AopConfigException("TargetSource cannot determine target class: " +
|
||||
"Either an interface or a target is required for proxy creation.");
|
||||
}
|
||||
if (targetClass.isInterface()) {
|
||||
return new JdkDynamicAopProxy(config);
|
||||
}
|
||||
if (!cglibAvailable) {
|
||||
throw new AopConfigException(
|
||||
"Cannot proxy target class because CGLIB2 is not available. " +
|
||||
"Add CGLIB to the class path or specify proxy interfaces.");
|
||||
}
|
||||
return CglibProxyFactory.createCglibProxy(config);
|
||||
}
|
||||
else {
|
||||
return new JdkDynamicAopProxy(config);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the supplied {@link AdvisedSupport} has only the
|
||||
* {@link org.springframework.aop.SpringProxy} interface specified
|
||||
* (or no proxy interfaces specified at all).
|
||||
*/
|
||||
private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
|
||||
Class[] interfaces = config.getProxiedInterfaces();
|
||||
return (interfaces.length == 0 || (interfaces.length == 1 && SpringProxy.class.equals(interfaces[0])));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inner factory class used to just introduce a CGLIB2 dependency
|
||||
* when actually creating a CGLIB proxy.
|
||||
*/
|
||||
private static class CglibProxyFactory {
|
||||
|
||||
public static AopProxy createCglibProxy(AdvisedSupport advisedSupport) {
|
||||
return new Cglib2AopProxy(advisedSupport);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user