001: /*
002: * Copyright 2002-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.aop.aspectj.annotation;
018:
019: import java.util.HashMap;
020: import java.util.List;
021: import java.util.Map;
022:
023: import org.aspectj.lang.reflect.PerClauseKind;
024:
025: import org.springframework.aop.Advisor;
026: import org.springframework.aop.aspectj.AspectJProxyUtils;
027: import org.springframework.aop.framework.AopConfigException;
028: import org.springframework.aop.framework.ProxyCreatorSupport;
029: import org.springframework.aop.support.AopUtils;
030: import org.springframework.util.Assert;
031: import org.springframework.util.ClassUtils;
032:
033: /**
034: * AspectJ-based proxy factory, allowing for programmatic building
035: * of proxies which include AspectJ aspects (code style as well
036: * Java 5 annotation style).
037: *
038: * @author Rob Harrop
039: * @author Juergen Hoeller
040: * @author Ramnivas Laddad
041: * @since 2.0
042: * @see #addAspect(Object)
043: * @see #addAspect(Class)
044: * @see #getProxy()
045: * @see #getProxy(ClassLoader)
046: * @see org.springframework.aop.framework.ProxyFactory
047: */
048: public class AspectJProxyFactory extends ProxyCreatorSupport {
049:
050: /** Cache for singleton aspect instances */
051: private static final Map aspectCache = new HashMap();
052:
053: private final AspectJAdvisorFactory aspectFactory = new ReflectiveAspectJAdvisorFactory();
054:
055: /**
056: * Create a new AspectJProxyFactory.
057: */
058: public AspectJProxyFactory() {
059: }
060:
061: /**
062: * Create a new AspectJProxyFactory.
063: * <p>Will proxy all interfaces that the given target implements.
064: * @param target the target object to be proxied
065: */
066: public AspectJProxyFactory(Object target) {
067: Assert.notNull(target, "Target object must not be null");
068: setInterfaces(ClassUtils.getAllInterfaces(target));
069: setTarget(target);
070: }
071:
072: /**
073: * Create a new <code>AspectJProxyFactory</code>.
074: * No target, only interfaces. Must add interceptors.
075: */
076: public AspectJProxyFactory(Class[] interfaces) {
077: setInterfaces(interfaces);
078: }
079:
080: /**
081: * Add the supplied aspect instance to the chain. The type of the aspect instance
082: * supplied must be a singleton aspect. True singleton lifecycle is not honoured when
083: * using this method - the caller is responsible for managing the lifecycle of any
084: * aspects added in this way.
085: * @param aspectInstance the AspectJ aspect instance
086: */
087: public void addAspect(Object aspectInstance) {
088: Class aspectClass = aspectInstance.getClass();
089: String aspectName = aspectClass.getName();
090: AspectMetadata am = createAspectMetadata(aspectClass,
091: aspectName);
092: if (am.getAjType().getPerClause().getKind() != PerClauseKind.SINGLETON) {
093: throw new IllegalArgumentException("Aspect class ["
094: + aspectClass.getName()
095: + "] does not define a singleton aspect");
096: }
097: addAdvisorsFromAspectInstanceFactory(new SingletonMetadataAwareAspectInstanceFactory(
098: aspectInstance, aspectName));
099: }
100:
101: /**
102: * Add an aspect of the supplied type to the end of the advice chain.
103: * @param aspectClass the AspectJ aspect class
104: */
105: public void addAspect(Class aspectClass) {
106: String aspectName = aspectClass.getName();
107: AspectMetadata am = createAspectMetadata(aspectClass,
108: aspectName);
109: MetadataAwareAspectInstanceFactory instanceFactory = createAspectInstanceFactory(
110: am, aspectClass, aspectName);
111: addAdvisorsFromAspectInstanceFactory(instanceFactory);
112: }
113:
114: /**
115: * Add all {@link Advisor Advisors} from the supplied {@link MetadataAwareAspectInstanceFactory}
116: * to the current chain. Exposes any special purpose {@link Advisor Advisors} if needed.
117: * @see #makeAdvisorChainAspectJCapableIfNecessary()
118: */
119: private void addAdvisorsFromAspectInstanceFactory(
120: MetadataAwareAspectInstanceFactory instanceFactory) {
121: List advisors = this .aspectFactory.getAdvisors(instanceFactory);
122: advisors = AopUtils.findAdvisorsThatCanApply(advisors,
123: getTargetClass());
124: addAllAdvisors((Advisor[]) advisors
125: .toArray(new Advisor[advisors.size()]));
126: makeAdvisorChainAspectJCapableIfNecessary();
127: }
128:
129: /**
130: * Create an {@link AspectMetadata} instance for the supplied aspect type.
131: */
132: private AspectMetadata createAspectMetadata(Class aspectClass,
133: String aspectName) {
134: AspectMetadata am = new AspectMetadata(aspectClass, aspectName);
135: if (!am.getAjType().isAspect()) {
136: throw new IllegalArgumentException("Class ["
137: + aspectClass.getName()
138: + "] is not a valid aspect type");
139: }
140: return am;
141: }
142:
143: /**
144: * Create a {@link MetadataAwareAspectInstanceFactory} for the supplied aspect type. If the aspect type
145: * has no per clause, then a {@link SingletonMetadataAwareAspectInstanceFactory} is returned, otherwise
146: * a {@link PrototypeAspectInstanceFactory} is returned.
147: */
148: private MetadataAwareAspectInstanceFactory createAspectInstanceFactory(
149: AspectMetadata am, Class aspectClass, String aspectName) {
150:
151: MetadataAwareAspectInstanceFactory instanceFactory = null;
152: if (am.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
153: // Create a shared aspect instance.
154: Object instance = getSingletonAspectInstance(aspectClass);
155: instanceFactory = new SingletonMetadataAwareAspectInstanceFactory(
156: instance, aspectName);
157: } else {
158: // Create a factory for independent aspect instances.
159: instanceFactory = new SimpleMetadataAwareAspectInstanceFactory(
160: aspectClass, aspectName);
161: }
162: return instanceFactory;
163: }
164:
165: /**
166: * Add any special-purpose {@link Advisor Advisors} needed for AspectJ support
167: * to the chain. {@link #updateAdvisorArray() Updates} the {@link Advisor} array
168: * and fires {@link #adviceChanged events}.
169: */
170: private void makeAdvisorChainAspectJCapableIfNecessary() {
171: if (AspectJProxyUtils
172: .makeAdvisorChainAspectJCapableIfNecessary(getAdvisorsInternal())) {
173: updateAdvisorArray();
174: adviceChanged();
175: }
176: }
177:
178: /**
179: * Get the singleton aspect instance for the supplied aspect type. An instance
180: * is created if one cannot be found in the instance cache.
181: */
182: private Object getSingletonAspectInstance(Class aspectClass) {
183: synchronized (aspectCache) {
184: Object instance = aspectCache.get(aspectClass);
185: if (instance != null) {
186: return instance;
187: }
188: try {
189: instance = aspectClass.newInstance();
190: aspectCache.put(aspectClass, instance);
191: return instance;
192: } catch (InstantiationException ex) {
193: throw new AopConfigException(
194: "Unable to instantiate aspect class ["
195: + aspectClass.getName() + "]", ex);
196: } catch (IllegalAccessException ex) {
197: throw new AopConfigException(
198: "Cannot access aspect class ["
199: + aspectClass.getName() + "]", ex);
200: }
201: }
202: }
203:
204: /**
205: * Create a new proxy according to the settings in this factory.
206: * <p>Can be called repeatedly. Effect will vary if we've added
207: * or removed interfaces. Can add and remove interceptors.
208: * <p>Uses a default class loader: Usually, the thread context class loader
209: * (if necessary for proxy creation).
210: * @return the new proxy
211: */
212: public <T> T getProxy() {
213: return (T) createAopProxy().getProxy();
214: }
215:
216: /**
217: * Create a new proxy according to the settings in this factory.
218: * <p>Can be called repeatedly. Effect will vary if we've added
219: * or removed interfaces. Can add and remove interceptors.
220: * <p>Uses the given class loader (if necessary for proxy creation).
221: * @param classLoader the class loader to create the proxy with
222: * @return the new proxy
223: */
224: public <T> T getProxy(ClassLoader classLoader) {
225: return (T) createAopProxy().getProxy(classLoader);
226: }
227:
228: }
|