1 | /* |
2 | * Copyright 2006-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.batch.core.scope.util; |
18 | |
19 | import java.lang.reflect.Modifier; |
20 | |
21 | import org.springframework.aop.framework.AopInfrastructureBean; |
22 | import org.springframework.aop.framework.ProxyConfig; |
23 | import org.springframework.aop.framework.ProxyFactory; |
24 | import org.springframework.aop.framework.autoproxy.AutoProxyUtils; |
25 | import org.springframework.aop.scope.DefaultScopedObject; |
26 | import org.springframework.aop.scope.ScopedObject; |
27 | import org.springframework.aop.scope.ScopedProxyFactoryBean; |
28 | import org.springframework.aop.support.DelegatingIntroductionInterceptor; |
29 | import org.springframework.beans.factory.BeanFactory; |
30 | import org.springframework.beans.factory.BeanFactoryAware; |
31 | import org.springframework.beans.factory.FactoryBean; |
32 | import org.springframework.beans.factory.FactoryBeanNotInitializedException; |
33 | import org.springframework.beans.factory.config.BeanDefinition; |
34 | import org.springframework.beans.factory.config.BeanDefinitionHolder; |
35 | import org.springframework.beans.factory.config.ConfigurableBeanFactory; |
36 | import org.springframework.beans.factory.support.BeanDefinitionRegistry; |
37 | import org.springframework.beans.factory.support.RootBeanDefinition; |
38 | import org.springframework.util.ClassUtils; |
39 | |
40 | /** |
41 | * Factory bean for proxies that can replace placeholders in their target. Just |
42 | * a specialisation of {@link ScopedProxyFactoryBean}, with a different target |
43 | * source type. |
44 | * |
45 | * @author Dave Syer |
46 | * |
47 | */ |
48 | public class PlaceholderProxyFactoryBean extends ProxyConfig implements FactoryBean, BeanFactoryAware { |
49 | |
50 | /** The TargetSource that manages scoping */ |
51 | private final PlaceholderTargetSource scopedTargetSource = new PlaceholderTargetSource(); |
52 | |
53 | /** The name of the target bean */ |
54 | private String targetBeanName; |
55 | |
56 | /** The cached singleton proxy */ |
57 | private Object proxy; |
58 | |
59 | private final ContextFactory contextFactory; |
60 | |
61 | /** |
62 | * Create a new FactoryBean instance. |
63 | */ |
64 | public PlaceholderProxyFactoryBean(ContextFactory contextFactory) { |
65 | this.contextFactory = contextFactory; |
66 | // setProxyTargetClass(false); |
67 | } |
68 | |
69 | /** |
70 | * Set the name of the bean that is to be scoped. |
71 | */ |
72 | public void setTargetBeanName(String targetBeanName) { |
73 | this.targetBeanName = targetBeanName; |
74 | this.scopedTargetSource.setTargetBeanName(targetBeanName); |
75 | } |
76 | |
77 | public void setBeanFactory(BeanFactory beanFactory) { |
78 | if (!(beanFactory instanceof ConfigurableBeanFactory)) { |
79 | throw new IllegalStateException("Not running in a ConfigurableBeanFactory: " + beanFactory); |
80 | } |
81 | ConfigurableBeanFactory cbf = (ConfigurableBeanFactory) beanFactory; |
82 | |
83 | this.scopedTargetSource.setBeanFactory(beanFactory); |
84 | this.scopedTargetSource.setContextFactory(contextFactory); |
85 | |
86 | ProxyFactory pf = new ProxyFactory(); |
87 | pf.copyFrom(this); |
88 | pf.setTargetSource(this.scopedTargetSource); |
89 | |
90 | Class<?> beanType = beanFactory.getType(this.targetBeanName); |
91 | if (beanType == null) { |
92 | throw new IllegalStateException("Cannot create scoped proxy for bean '" + this.targetBeanName |
93 | + "': Target type could not be determined at the time of proxy creation."); |
94 | } |
95 | if (!isProxyTargetClass() || beanType.isInterface() || Modifier.isPrivate(beanType.getModifiers())) { |
96 | pf.setInterfaces(ClassUtils.getAllInterfacesForClass(beanType, cbf.getBeanClassLoader())); |
97 | } |
98 | |
99 | // Add an introduction that implements only the methods on ScopedObject. |
100 | ScopedObject scopedObject = new DefaultScopedObject(cbf, this.scopedTargetSource.getTargetBeanName()); |
101 | pf.addAdvice(new DelegatingIntroductionInterceptor(scopedObject)); |
102 | |
103 | // Add the AopInfrastructureBean marker to indicate that the scoped |
104 | // proxy itself is not subject to auto-proxying! Only its target bean |
105 | // is. |
106 | pf.addInterface(AopInfrastructureBean.class); |
107 | |
108 | this.scopedTargetSource.afterPropertiesSet(); |
109 | |
110 | this.proxy = pf.getProxy(cbf.getBeanClassLoader()); |
111 | } |
112 | |
113 | public Object getObject() { |
114 | if (this.proxy == null) { |
115 | throw new FactoryBeanNotInitializedException(); |
116 | } |
117 | return this.proxy; |
118 | } |
119 | |
120 | public Class<?> getObjectType() { |
121 | if (this.proxy != null) { |
122 | return this.proxy.getClass(); |
123 | } |
124 | if (this.scopedTargetSource != null) { |
125 | return this.scopedTargetSource.getTargetClass(); |
126 | } |
127 | return null; |
128 | } |
129 | |
130 | public boolean isSingleton() { |
131 | return true; |
132 | } |
133 | |
134 | /** |
135 | * Convenience method to create a {@link BeanDefinition} for a target |
136 | * wrapped in a placeholder tarrget source, able to defer binding of |
137 | * placeholders until the bean is used. |
138 | * |
139 | * @param definition a target bean definition |
140 | * @param registry a {@link BeanDefinitionRegistry} |
141 | * @param proxyTargetClass true if we need to use CGlib to create the |
142 | * proxies |
143 | * @return a {@link BeanDefinitionHolder} for a |
144 | * {@link PlaceholderProxyFactoryBean} |
145 | */ |
146 | public static BeanDefinitionHolder createScopedProxy(BeanDefinitionHolder definition, |
147 | BeanDefinitionRegistry registry, boolean proxyTargetClass) { |
148 | |
149 | String originalBeanName = definition.getBeanName(); |
150 | BeanDefinition targetDefinition = definition.getBeanDefinition(); |
151 | |
152 | // Create a proxy definition for the original bean name, |
153 | // "hiding" the target bean in an internal target definition. |
154 | RootBeanDefinition proxyDefinition = new RootBeanDefinition(PlaceholderProxyFactoryBean.class); |
155 | proxyDefinition.getConstructorArgumentValues().addGenericArgumentValue(new StepContextFactory()); |
156 | proxyDefinition.setOriginatingBeanDefinition(definition.getBeanDefinition()); |
157 | proxyDefinition.setSource(definition.getSource()); |
158 | proxyDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); |
159 | |
160 | String targetBeanName = "lazyBindingProxy." + originalBeanName; |
161 | proxyDefinition.getPropertyValues().addPropertyValue("targetBeanName", targetBeanName); |
162 | |
163 | if (proxyTargetClass) { |
164 | targetDefinition.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE); |
165 | // ProxyFactoryBean's "proxyTargetClass" default is FALSE, so we |
166 | // need to set it explicitly here. |
167 | proxyDefinition.getPropertyValues().addPropertyValue("proxyTargetClass", Boolean.TRUE); |
168 | } |
169 | else { |
170 | proxyDefinition.getPropertyValues().addPropertyValue("proxyTargetClass", Boolean.FALSE); |
171 | } |
172 | |
173 | proxyDefinition.setAutowireCandidate(targetDefinition.isAutowireCandidate()); |
174 | // The target bean should be ignored in favor of the proxy. |
175 | targetDefinition.setAutowireCandidate(false); |
176 | |
177 | // Register the target bean as separate bean in the factory. |
178 | registry.registerBeanDefinition(targetBeanName, targetDefinition); |
179 | |
180 | // Return the scoped proxy definition as primary bean definition |
181 | // (potentially an inner bean). |
182 | return new BeanDefinitionHolder(proxyDefinition, originalBeanName, definition.getAliases()); |
183 | } |
184 | |
185 | } |