1 16 17 package org.springframework.beans.factory.config; 18 19 import java.lang.reflect.Constructor ; 20 import java.lang.reflect.InvocationHandler ; 21 import java.lang.reflect.InvocationTargetException ; 22 import java.lang.reflect.Method ; 23 import java.lang.reflect.Proxy ; 24 import java.util.Properties ; 25 26 import org.springframework.beans.BeanUtils; 27 import org.springframework.beans.BeansException; 28 import org.springframework.beans.FatalBeanException; 29 import org.springframework.beans.factory.BeanFactory; 30 import org.springframework.beans.factory.BeanFactoryAware; 31 import org.springframework.beans.factory.BeanFactoryUtils; 32 import org.springframework.beans.factory.FactoryBean; 33 import org.springframework.beans.factory.InitializingBean; 34 import org.springframework.beans.factory.ListableBeanFactory; 35 import org.springframework.util.StringUtils; 36 37 191 public class ServiceLocatorFactoryBean implements FactoryBean, BeanFactoryAware, InitializingBean { 192 193 private Class serviceLocatorInterface; 194 195 private Constructor serviceLocatorExceptionConstructor; 196 197 private Properties serviceMappings; 198 199 private ListableBeanFactory beanFactory; 200 201 private Object proxy; 202 203 204 212 public void setServiceLocatorInterface(Class interfaceType) { 213 this.serviceLocatorInterface = interfaceType; 214 } 215 216 230 public void setServiceLocatorExceptionClass(Class serviceLocatorExceptionClass) { 231 if (serviceLocatorExceptionClass != null && !Exception .class.isAssignableFrom(serviceLocatorExceptionClass)) { 232 throw new IllegalArgumentException ( 233 "serviceLocatorException [" + serviceLocatorExceptionClass.getName() + "] is not a subclass of Exception"); 234 } 235 this.serviceLocatorExceptionConstructor = 236 determineServiceLocatorExceptionConstructor(serviceLocatorExceptionClass); 237 } 238 239 249 public void setServiceMappings(Properties serviceMappings) { 250 this.serviceMappings = serviceMappings; 251 } 252 253 public void setBeanFactory(BeanFactory beanFactory) throws BeansException { 254 if (!(beanFactory instanceof ListableBeanFactory)) { 255 throw new FatalBeanException( 256 "ServiceLocatorFactoryBean needs to run in a BeanFactory that is a ListableBeanFactory"); 257 } 258 this.beanFactory = (ListableBeanFactory) beanFactory; 259 } 260 261 public void afterPropertiesSet() { 262 if (this.serviceLocatorInterface == null) { 263 throw new IllegalArgumentException ("serviceLocatorInterface is required"); 264 } 265 266 this.proxy = Proxy.newProxyInstance( 268 this.serviceLocatorInterface.getClassLoader(), 269 new Class [] {this.serviceLocatorInterface}, 270 new ServiceLocatorInvocationHandler()); 271 } 272 273 274 284 protected Constructor determineServiceLocatorExceptionConstructor(Class exceptionClass) { 285 try { 286 return exceptionClass.getConstructor(new Class [] {String .class, Throwable .class}); 287 } 288 catch (NoSuchMethodException ex) { 289 try { 290 return exceptionClass.getConstructor(new Class [] {Throwable .class}); 291 } 292 catch (NoSuchMethodException ex2) { 293 try { 294 return exceptionClass.getConstructor(new Class [] {String .class}); 295 } 296 catch (NoSuchMethodException ex3) { 297 throw new IllegalArgumentException ( 298 "serviceLocatorException [" + exceptionClass.getName() + 299 "] neither has a (String, Throwable) constructor nor a (String) constructor"); 300 } 301 } 302 } 303 } 304 305 315 protected Exception createServiceLocatorException(Constructor exceptionConstructor, BeansException cause) { 316 Class [] paramTypes = exceptionConstructor.getParameterTypes(); 317 Object [] args = new Object [paramTypes.length]; 318 for (int i = 0; i < paramTypes.length; i++) { 319 if (paramTypes[i].equals(String .class)) { 320 args[i] = cause.getMessage(); 321 } 322 else if (paramTypes[i].isInstance(cause)) { 323 args[i] = cause; 324 } 325 } 326 return (Exception ) BeanUtils.instantiateClass(exceptionConstructor, args); 327 } 328 329 330 public Object getObject() { 331 return this.proxy; 332 } 333 334 public Class getObjectType() { 335 return this.serviceLocatorInterface; 336 } 337 338 public boolean isSingleton() { 339 return true; 340 } 341 342 343 346 private class ServiceLocatorInvocationHandler implements InvocationHandler { 347 348 public Object invoke(Object proxy, Method method, Object [] args) throws Throwable { 349 Object service; 350 if (Object .class.equals(method.getDeclaringClass())) { 351 service = invokeStandardObjectMethod(method, args); 352 } else { 353 service = invokeServiceLocatorMethod(method, args); 354 } 355 return service; 356 } 357 358 public String toString() { 359 return "Service locator: " + serviceLocatorInterface.getName(); 360 } 361 362 363 private Object invokeServiceLocatorMethod(Method method, Object [] args) throws Exception { 364 Class serviceLocatorMethodReturnType = getServiceLocatorMethodReturnType(method); 365 try { 366 String beanName = tryGetBeanName(args); 367 if (StringUtils.hasLength(beanName)) { 368 return beanFactory.getBean(beanName, serviceLocatorMethodReturnType); 370 } 371 else { 372 return BeanFactoryUtils.beanOfTypeIncludingAncestors(beanFactory, serviceLocatorMethodReturnType); 374 } 375 } 376 catch (BeansException ex) { 377 if (serviceLocatorExceptionConstructor != null) { 378 throw createServiceLocatorException(serviceLocatorExceptionConstructor, ex); 379 } 380 throw ex; 381 } 382 } 383 384 387 private String tryGetBeanName(Object [] args) { 388 String beanName = ""; 389 if (args != null && args.length == 1 && args[0] != null) { 390 beanName = args[0].toString(); 391 } 392 if (serviceMappings != null) { 394 String mappedName = serviceMappings.getProperty(beanName); 395 if (mappedName != null) { 396 beanName = mappedName; 397 } 398 } 399 return beanName; 400 } 401 402 private Class getServiceLocatorMethodReturnType(Method method) throws NoSuchMethodException { 403 Class [] paramTypes = method.getParameterTypes(); 404 Method interfaceMethod = serviceLocatorInterface.getMethod(method.getName(), paramTypes); 405 Class serviceLocatorReturnType = interfaceMethod.getReturnType(); 406 407 if (paramTypes.length > 1 || void.class.equals(serviceLocatorReturnType)) { 409 throw new UnsupportedOperationException ( 410 "May only call methods with signature '<type> xxx()' or '<type> xxx(<idtype> id)' " + 411 "on factory interface, but tried to call: " + interfaceMethod); 412 } 413 return serviceLocatorReturnType; 414 } 415 416 420 private Object invokeStandardObjectMethod(Method method, Object [] args) throws Throwable { 421 try { 422 return method.invoke(this, args); 423 } 424 catch (InvocationTargetException invEx) { 425 throw invEx.getTargetException(); 426 } 427 } 428 429 } 430 431 } 432 | Popular Tags |