KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > aop > framework > JdkDynamicAopProxy


1 /*
2  * Copyright 2002-2007 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.springframework.aop.framework;
18
19 import java.io.Serializable JavaDoc;
20 import java.lang.reflect.InvocationHandler JavaDoc;
21 import java.lang.reflect.Method JavaDoc;
22 import java.lang.reflect.Proxy JavaDoc;
23 import java.util.List JavaDoc;
24
25 import org.aopalliance.intercept.MethodInvocation;
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.logging.LogFactory;
28
29 import org.springframework.aop.TargetSource;
30 import org.springframework.aop.support.AopUtils;
31 import org.springframework.util.Assert;
32 import org.springframework.util.ClassUtils;
33
34 /**
35  * JDK-based {@link AopProxy} implementation for the Spring AOP framework,
36  * based on JDK 1.3+ {@link java.lang.reflect.Proxy dynamic proxies}.
37  *
38  * <p>Creates a dynamic proxy, implementing the interfaces exposed by
39  * the AopProxy. Dynamic proxies <i>cannot</i> be used to proxy methods
40  * defined in classes, rather than interfaces.
41  *
42  * <p>Objects of this type should be obtained through proxy factories,
43  * configured by an {@link AdvisedSupport} class. This class is internal
44  * to Spring's AOP framework and need not be used directly by client code.
45  *
46  * <p>Proxies created using this class will be thread-safe if the
47  * underlying (target) class is thread-safe.
48  *
49  * <p>Proxies are serializable so long as all Advisors (including Advices
50  * and Pointcuts) and the TargetSource are serializable.
51  *
52  * @author Rod Johnson
53  * @author Juergen Hoeller
54  * @author Rob Harrop
55  * @see java.lang.reflect.Proxy
56  * @see AdvisedSupport
57  * @see ProxyFactory
58  */

59 final class JdkDynamicAopProxy implements AopProxy, InvocationHandler JavaDoc, Serializable JavaDoc {
60
61     /** use serialVersionUID from Spring 1.2 for interoperability */
62     private static final long serialVersionUID = 5531744639992436476L;
63
64
65     /*
66      * NOTE: We could avoid the code duplication between this class and the CGLIB
67      * proxies by refactoring "invoke" into a template method. However, this approach
68      * adds at least 10% performance overhead versus a copy-paste solution, so we sacrifice
69      * elegance for performance. (We have a good test suite to ensure that the different
70      * proxies behave the same :-)
71      * This way, we can also more easily take advantage of minor optimizations in each class.
72      */

73
74     /** We use a static Log to avoid serialization issues */
75     private static Log logger = LogFactory.getLog(JdkDynamicAopProxy.class);
76
77     /** Config used to configure this proxy */
78     private final AdvisedSupport advised;
79
80     /**
81      * Is the {@link #equals} method defined on the proxied interfaces?
82      */

83     private boolean equalsDefined;
84
85     /**
86      * Is the {@link #hashCode} method defined on the proxied interfaces?
87      */

88     private boolean hashCodeDefined;
89
90
91     /**
92      * Construct a new JdkDynamicAopProxy for the given AOP configuration.
93      * @param config the AOP configuration as AdvisedSupport object
94      * @throws AopConfigException if the config is invalid. We try to throw an informative
95      * exception in this case, rather than let a mysterious failure happen later.
96      */

97     public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
98         Assert.notNull(config, "AdvisedSupport must not be null");
99         if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
100             throw new AopConfigException("No advisors and no TargetSource specified");
101         }
102         this.advised = config;
103     }
104
105
106     public Object JavaDoc getProxy() {
107         return getProxy(ClassUtils.getDefaultClassLoader());
108     }
109
110     public Object JavaDoc getProxy(ClassLoader JavaDoc classLoader) {
111         if (logger.isDebugEnabled()) {
112             logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
113         }
114         Class JavaDoc[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
115         findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
116         return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
117     }
118
119     /**
120      * Finds any {@link #equals} or {@link #hashCode} method that may be defined
121      * on the supplied set of interfaces.
122      * @param proxiedInterfaces the interfaces to introspect
123      */

124     private void findDefinedEqualsAndHashCodeMethods(Class JavaDoc[] proxiedInterfaces) {
125         for (int i = 0; i < proxiedInterfaces.length; i++) {
126             Class JavaDoc proxiedInterface = proxiedInterfaces[i];
127             Method JavaDoc[] methods = proxiedInterface.getDeclaredMethods();
128             for (int j = 0; j < methods.length; j++) {
129                 Method JavaDoc method = methods[j];
130                 if (AopUtils.isEqualsMethod(method)) {
131                     this.equalsDefined = true;
132                 }
133                 if (AopUtils.isHashCodeMethod(method)) {
134                     this.hashCodeDefined = true;
135                 }
136                 if (this.equalsDefined && this.hashCodeDefined) {
137                     return;
138                 }
139             }
140         }
141     }
142
143
144     /**
145      * Implementation of <code>InvocationHandler.invoke</code>.
146      * <p>Callers will see exactly the exception thrown by the target,
147      * unless a hook method throws an exception.
148      */

149     public Object JavaDoc invoke(Object JavaDoc proxy, Method JavaDoc method, Object JavaDoc[] args) throws Throwable JavaDoc {
150         MethodInvocation invocation = null;
151         Object JavaDoc oldProxy = null;
152         boolean setProxyContext = false;
153
154         TargetSource targetSource = this.advised.targetSource;
155         Class JavaDoc targetClass = null;
156         Object JavaDoc target = null;
157
158         try {
159             if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
160                 // The target does not implement the equals(Object) method itself.
161
return (equals(args[0]) ? Boolean.TRUE : Boolean.FALSE);
162             }
163             if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
164                 // The target does not implement the hashCode() method itself.
165
return new Integer JavaDoc(hashCode());
166             }
167             if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
168                     method.getDeclaringClass().isAssignableFrom(Advised.class)) {
169                 // Service invocations on ProxyConfig with the proxy config...
170
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
171             }
172
173             Object JavaDoc retVal = null;
174
175             if (this.advised.exposeProxy) {
176                 // Make invocation available if necessary.
177
oldProxy = AopContext.setCurrentProxy(proxy);
178                 setProxyContext = true;
179             }
180
181             // May be <code>null</code>. Get as late as possible to minimize the time we "own" the target,
182
// in case it comes from a pool.
183
target = targetSource.getTarget();
184             if (target != null) {
185                 targetClass = target.getClass();
186             }
187
188             // Get the interception chain for this method.
189
List JavaDoc chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
190
191             // Check whether we have any advice. If we don't, we can fallback on direct
192
// reflective invocation of the target, and avoid creating a MethodInvocation.
193
if (chain.isEmpty()) {
194                 // We can skip creating a MethodInvocation: just invoke the target directly
195
// Note that the final invoker must be an InvokerInterceptor so we know it does
196
// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
197
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
198             }
199             else {
200                 // We need to create a method invocation...
201
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
202                 // Proceed to the joinpoint through the interceptor chain.
203
retVal = invocation.proceed();
204             }
205
206             // Massage return value if necessary.
207
if (retVal != null && retVal == target && method.getReturnType().isInstance(proxy)) {
208                 // Special case: it returned "this" and the return type of the method
209
// is type-compatible. Note that we can't help if the target sets
210
// a reference to itself in another returned object.
211
retVal = proxy;
212             }
213             return retVal;
214         }
215         finally {
216             if (target != null && !targetSource.isStatic()) {
217                 // Must have come from TargetSource.
218
targetSource.releaseTarget(target);
219             }
220             if (setProxyContext) {
221                 // Restore old proxy.
222
AopContext.setCurrentProxy(oldProxy);
223             }
224         }
225     }
226
227
228     /**
229      * Equality means interfaces, advisors and TargetSource are equal.
230      * <p>The compared object may be a JdkDynamicAopProxy instance itself
231      * or a dynamic proxy wrapping a JdkDynamicAopProxy instance.
232      */

233     public boolean equals(Object JavaDoc other) {
234         if (other == this) {
235             return true;
236         }
237         if (other == null) {
238             return false;
239         }
240
241         JdkDynamicAopProxy otherProxy = null;
242         if (other instanceof JdkDynamicAopProxy) {
243             otherProxy = (JdkDynamicAopProxy) other;
244         }
245         else if (Proxy.isProxyClass(other.getClass())) {
246             InvocationHandler JavaDoc ih = Proxy.getInvocationHandler(other);
247             if (!(ih instanceof JdkDynamicAopProxy)) {
248                 return false;
249             }
250             otherProxy = (JdkDynamicAopProxy) ih;
251         }
252         else {
253             // Not a valid comparison...
254
return false;
255         }
256
257         // If we get here, aopr2 is the other AopProxy.
258
return AopProxyUtils.equalsInProxy(this.advised, otherProxy.advised);
259     }
260
261     /**
262      * Proxy uses the hash code of the TargetSource.
263      */

264     public int hashCode() {
265         return JdkDynamicAopProxy.class.hashCode() * 13 + this.advised.getTargetSource().hashCode();
266     }
267
268 }
269
Popular Tags