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.target;
018:
019: import java.io.NotSerializableException;
020: import java.io.ObjectStreamException;
021: import java.io.Serializable;
022:
023: import org.apache.commons.logging.Log;
024: import org.apache.commons.logging.LogFactory;
025:
026: import org.springframework.aop.TargetSource;
027: import org.springframework.beans.BeansException;
028: import org.springframework.beans.factory.BeanFactory;
029: import org.springframework.beans.factory.BeanFactoryAware;
030: import org.springframework.util.ClassUtils;
031: import org.springframework.util.ObjectUtils;
032:
033: /**
034: * Base class for {@link org.springframework.aop.TargetSource} implementations
035: * that are based on a Spring {@link org.springframework.beans.factory.BeanFactory},
036: * delegating to Spring-managed bean instances.
037: *
038: * <p>Subclasses can create prototype instances or lazily access a
039: * singleton target, for example. See {@link LazyInitTargetSource} and
040: * {@link AbstractPrototypeBasedTargetSource}'s subclasses for concrete strategies.
041: *
042: * <p>BeanFactory-based TargetSources are serializable. This involves
043: * disconnecting the current target and turning into a {@link SingletonTargetSource}.
044: *
045: * @author Juergen Hoeller
046: * @author Rod Johnson
047: * @since 1.1.4
048: * @see org.springframework.beans.factory.BeanFactory#getBean
049: * @see LazyInitTargetSource
050: * @see PrototypeTargetSource
051: * @see ThreadLocalTargetSource
052: * @see CommonsPoolTargetSource
053: */
054: public abstract class AbstractBeanFactoryBasedTargetSource implements
055: TargetSource, BeanFactoryAware, Serializable {
056:
057: /** use serialVersionUID from Spring 1.2.7 for interoperability */
058: private static final long serialVersionUID = -4721607536018568393L;
059:
060: /** Logger available to subclasses */
061: protected final Log logger = LogFactory.getLog(getClass());
062:
063: /** Name of the target bean we will create on each invocation */
064: private String targetBeanName;
065:
066: /** Class of the target */
067: private Class targetClass;
068:
069: /**
070: * BeanFactory that owns this TargetSource. We need to hold onto this
071: * reference so that we can create new prototype instances as necessary.
072: */
073: private BeanFactory beanFactory;
074:
075: /**
076: * Set the name of the target bean in the factory.
077: * <p>The target bean should not be a singleton, else the same instance will
078: * always be obtained from the factory, resulting in the same behavior as
079: * provided by {@link SingletonTargetSource}.
080: * @param targetBeanName name of the target bean in the BeanFactory
081: * that owns this interceptor
082: * @see SingletonTargetSource
083: */
084: public void setTargetBeanName(String targetBeanName) {
085: this .targetBeanName = targetBeanName;
086: }
087:
088: /**
089: * Return the name of the target bean in the factory.
090: */
091: public String getTargetBeanName() {
092: return this .targetBeanName;
093: }
094:
095: /**
096: * Specify the target class explicitly, to avoid any kind of access to the
097: * target bean (for example, to avoid initialization of a FactoryBean instance).
098: * <p>Default is to detect the type automatically, through a <code>getType</code>
099: * call on the BeanFactory (or even a full <code>getBean</code> call as fallback).
100: */
101: public void setTargetClass(Class targetClass) {
102: this .targetClass = targetClass;
103: }
104:
105: /**
106: * Set the owning BeanFactory. We need to save a reference so that we can
107: * use the <code>getBean</code> method on every invocation.
108: */
109: public void setBeanFactory(BeanFactory beanFactory)
110: throws BeansException {
111: if (this .targetBeanName == null) {
112: throw new IllegalStateException(
113: "Property'targetBeanName' is required");
114: }
115: this .beanFactory = beanFactory;
116: }
117:
118: /**
119: * Return the owning BeanFactory.
120: */
121: public BeanFactory getBeanFactory() {
122: return this .beanFactory;
123: }
124:
125: public synchronized Class getTargetClass() {
126: if (this .targetClass == null && this .beanFactory != null) {
127: // Determine type of the target bean.
128: this .targetClass = this .beanFactory
129: .getType(this .targetBeanName);
130: if (this .targetClass == null) {
131: if (logger.isTraceEnabled()) {
132: logger.trace("Getting bean with name '"
133: + this .targetBeanName
134: + "' in order to determine type");
135: }
136: this .targetClass = this .beanFactory.getBean(
137: this .targetBeanName).getClass();
138: }
139: }
140: return this .targetClass;
141: }
142:
143: public boolean isStatic() {
144: return false;
145: }
146:
147: public void releaseTarget(Object target) throws Exception {
148: // Nothing to do here.
149: }
150:
151: /**
152: * Copy configuration from the other AbstractBeanFactoryBasedTargetSource object.
153: * Subclasses should override this if they wish to expose it.
154: * @param other object to copy configuration from
155: */
156: protected void copyFrom(AbstractBeanFactoryBasedTargetSource other) {
157: this .targetBeanName = other.targetBeanName;
158: this .targetClass = other.targetClass;
159: this .beanFactory = other.beanFactory;
160: }
161:
162: /**
163: * Replaces this object with a SingletonTargetSource on serialization.
164: * Protected as otherwise it won't be invoked for subclasses.
165: * (The <code>writeReplace()</code> method must be visible to the class
166: * being serialized.)
167: * <p>With this implementation of this method, there is no need to mark
168: * non-serializable fields in this class or subclasses as transient.
169: */
170: protected Object writeReplace() throws ObjectStreamException {
171: if (logger.isDebugEnabled()) {
172: logger.debug("Disconnecting TargetSource [" + this + "]");
173: }
174: try {
175: // Create disconnected SingletonTargetSource.
176: return new SingletonTargetSource(getTarget());
177: } catch (Exception ex) {
178: logger.error(
179: "Cannot get target for disconnecting TargetSource ["
180: + this + "]", ex);
181: throw new NotSerializableException(
182: "Cannot get target for disconnecting TargetSource ["
183: + this + "]: " + ex);
184: }
185: }
186:
187: public boolean equals(Object other) {
188: if (this == other) {
189: return true;
190: }
191: if (other == null || !getClass().equals(other.getClass())) {
192: return false;
193: }
194: AbstractBeanFactoryBasedTargetSource otherTargetSource = (AbstractBeanFactoryBasedTargetSource) other;
195: return (ObjectUtils.nullSafeEquals(this .beanFactory,
196: otherTargetSource.beanFactory) && ObjectUtils
197: .nullSafeEquals(this .targetBeanName,
198: otherTargetSource.targetBeanName));
199: }
200:
201: public int hashCode() {
202: int hashCode = getClass().hashCode();
203: hashCode = 13 * hashCode
204: + ObjectUtils.nullSafeHashCode(this .beanFactory);
205: hashCode = 13 * hashCode
206: + ObjectUtils.nullSafeHashCode(this .targetBeanName);
207: return hashCode;
208: }
209:
210: public String toString() {
211: StringBuffer sb = new StringBuffer();
212: sb.append(ClassUtils.getShortName(getClass()));
213: sb.append(" for target bean '").append(this .targetBeanName)
214: .append("'");
215: if (this .targetClass != null) {
216: sb.append(" of type [").append(this .targetClass.getName())
217: .append("]");
218: }
219: return sb.toString();
220: }
221:
222: }
|