📄 servicelocatorfactorybean.java
字号:
* Set the exception class that the service locator should throw if service
* lookup failed. The specified exception class must have a constructor
* with one of the following parameter types: <code>(String, Throwable)</code>
* or <code>(Throwable)</code> or <code>(String)</code>.
* <p>If not specified, subclasses of Spring's BeansException will be thrown,
* for example NoSuchBeanDefinitionException. As those are unchecked, the
* caller does not need to handle them, so it might be acceptable that
* Spring exceptions get thrown as long as they are just handled generically.
* @see #determineServiceLocatorExceptionConstructor(Class)
* @see #createServiceLocatorException(java.lang.reflect.Constructor, org.springframework.beans.BeansException)
* @see org.springframework.beans.BeansException
* @see org.springframework.beans.factory.NoSuchBeanDefinitionException
*/
public void setServiceLocatorExceptionClass(Class serviceLocatorExceptionClass) {
if (serviceLocatorExceptionClass != null && !Exception.class.isAssignableFrom(serviceLocatorExceptionClass)) {
throw new IllegalArgumentException(
"serviceLocatorException [" + serviceLocatorExceptionClass.getName() + "] is not a subclass of Exception");
}
this.serviceLocatorExceptionConstructor =
determineServiceLocatorExceptionConstructor(serviceLocatorExceptionClass);
}
/**
* Set mappings between service ids (passed into the service locator)
* and bean names (in the bean factory). Service ids that are not defined
* here will be treated as bean names as-is.
* <p>The empty string as service id key defines the mapping for <code>null</code> and
* empty string, and for factory methods without parameter. If not defined,
* a single matching bean will be retrieved from the bean factory.
* @param serviceMappings mappings between service ids and bean names,
* with service ids as keys as bean names as values
*/
public void setServiceMappings(Properties serviceMappings) {
this.serviceMappings = serviceMappings;
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (!(beanFactory instanceof ListableBeanFactory)) {
throw new FatalBeanException(
"ServiceLocatorFactoryBean needs to run in a BeanFactory that is a ListableBeanFactory");
}
this.beanFactory = (ListableBeanFactory) beanFactory;
}
public void afterPropertiesSet() {
if (this.serviceLocatorInterface == null) {
throw new IllegalArgumentException("serviceLocatorInterface is required");
}
// Create service locator proxy.
this.proxy = Proxy.newProxyInstance(
this.serviceLocatorInterface.getClassLoader(),
new Class[] {this.serviceLocatorInterface},
new ServiceLocatorInvocationHandler());
}
/**
* Determine the constructor to use for the given service locator exception
* class. Only called in case of a custom service locator exception.
* <p>The default implementation looks for a constructor with one of the
* following parameter types: <code>(String, Throwable)</code>
* or <code>(Throwable)</code> or <code>(String)</code>.
* @param exceptionClass the exception class
* @return the constructor to use
* @see #setServiceLocatorExceptionClass
*/
protected Constructor determineServiceLocatorExceptionConstructor(Class exceptionClass) {
try {
return exceptionClass.getConstructor(new Class[] {String.class, Throwable.class});
}
catch (NoSuchMethodException ex) {
try {
return exceptionClass.getConstructor(new Class[] {Throwable.class});
}
catch (NoSuchMethodException ex2) {
try {
return exceptionClass.getConstructor(new Class[] {String.class});
}
catch (NoSuchMethodException ex3) {
throw new IllegalArgumentException(
"serviceLocatorException [" + exceptionClass.getName() +
"] neither has a (String, Throwable) constructor nor a (String) constructor");
}
}
}
}
/**
* Create a service locator exception for the given cause.
* Only called in case of a custom service locator exception.
* <p>The default implementation can handle all variations of
* message and exception arguments.
* @param exceptionConstructor the constructor to use
* @param cause the cause of the service lookup failure
* @return the service locator exception to throw
* @see #setServiceLocatorExceptionClass
*/
protected Exception createServiceLocatorException(Constructor exceptionConstructor, BeansException cause) {
Class[] paramTypes = exceptionConstructor.getParameterTypes();
Object[] args = new Object[paramTypes.length];
for (int i = 0; i < paramTypes.length; i++) {
if (paramTypes[i].equals(String.class)) {
args[i] = cause.getMessage();
}
else if (paramTypes[i].isInstance(cause)) {
args[i] = cause;
}
}
return (Exception) BeanUtils.instantiateClass(exceptionConstructor, args);
}
public Object getObject() {
return this.proxy;
}
public Class getObjectType() {
return this.serviceLocatorInterface;
}
public boolean isSingleton() {
return true;
}
/**
* Invocation handler that delegates service locator calls to the bean factory.
*/
private class ServiceLocatorInvocationHandler implements InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object service;
if (Object.class.equals(method.getDeclaringClass())) {
service = invokeStandardObjectMethod(method, args);
} else {
service = invokeServiceLocatorMethod(method, args);
}
return service;
}
public String toString() {
return "Service locator: " + serviceLocatorInterface.getName();
}
private Object invokeServiceLocatorMethod(Method method, Object[] args) throws Exception {
Class serviceLocatorMethodReturnType = getServiceLocatorMethodReturnType(method);
try {
String beanName = tryGetBeanName(args);
if (StringUtils.hasLength(beanName)) {
// Service locator for a specific bean name.
return beanFactory.getBean(beanName, serviceLocatorMethodReturnType);
}
else {
// Service locator for a bean type.
return BeanFactoryUtils.beanOfTypeIncludingAncestors(beanFactory, serviceLocatorMethodReturnType);
}
}
catch (BeansException ex) {
if (serviceLocatorExceptionConstructor != null) {
throw createServiceLocatorException(serviceLocatorExceptionConstructor, ex);
}
throw ex;
}
}
/**
* Check whether a service id was passed in.
*/
private String tryGetBeanName(Object[] args) {
String beanName = "";
if (args != null && args.length == 1 && args[0] != null) {
beanName = args[0].toString();
}
// Look for explicit serviceId-to-beanName mappings.
if (serviceMappings != null) {
String mappedName = serviceMappings.getProperty(beanName);
if (mappedName != null) {
beanName = mappedName;
}
}
return beanName;
}
private Class getServiceLocatorMethodReturnType(Method method) throws NoSuchMethodException {
Class[] paramTypes = method.getParameterTypes();
Method interfaceMethod = serviceLocatorInterface.getMethod(method.getName(), paramTypes);
Class serviceLocatorReturnType = interfaceMethod.getReturnType();
// Check whether the method is a valid service locator.
if (paramTypes.length > 1 || void.class.equals(serviceLocatorReturnType)) {
throw new UnsupportedOperationException(
"May only call methods with signature '<type> xxx()' or '<type> xxx(<idtype> id)' " +
"on factory interface, but tried to call: " + interfaceMethod);
}
return serviceLocatorReturnType;
}
/**
* It's normal to get here for non service locator interface method calls
*(toString, equals, etc.). Simply apply the call to the invocation handler object.
*/
private Object invokeStandardObjectMethod(Method method, Object[] args) throws Throwable {
try {
return method.invoke(this, args);
}
catch (InvocationTargetException invEx) {
throw invEx.getTargetException();
}
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -