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