在编写非web的的服务处理程序时,需要根据某个字段值作分发,这样便于分离不同的服务类和方法,也便于以后增加和扩展服务处理逻辑。有了Spring的容器,最简单的方式是一个服务一个类,用相同的接口,然后用字段映射到beanName。
但是有时候需要分发的服务变多,而且重用逻辑也不好分得太散。这时需要类似Spring MVC的分发映射到处理方法的方式。这样灵活性很高,扩展性也好,编写扩展服务起来也方便。其实Spring MVC已经实现了,只要借用这套机制,调用其辅助工具类,很容易搭建自己的服务分发。
本文用一个例子来抛砖引玉。
首先定义我们的映射器,用个类似RequestMapping的注解。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Mapping {
String value() default "";
}
然后,抽象一组请求和返回对象:
public interface Req {
String getMappingValue();
}
public class Res {
}
当然你也把请求参数和返回对象定义更贴合自己的业务特点,可以用上包括泛型等等的特性。
先看一个扩展服务,也就是最终分发到的方法样例:
@Service
@Mapping("/demo")
public class DemoServiceImpl implements DemoService {
@Override
@Mapping("/test")
public Res testHandle(Req req) {
return new Res();
}
@Override
@Mapping("/test2")
public Res test2Handle(Req req) {
return new Res();
}
}
和Spring MVC类似,这非常容易理解。最后是靠Req.getMappingValue()返回的值与具体方法的注解中定义的value相匹配而实现分发的。
/demo/test -> DemoServiceImpl.testHandle(Req req)
/demo/test2 -> DemoServiceImpl.test2Handle(Req req)
最后来看分发器,也很简单明了,基本工具类Spring里面都有了:
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ApplicationObjectSupport;
import org.springframework.core.BridgeMethodResolver;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Component
public class Dispatcher extends ApplicationObjectSupport implements InitializingBean {
private Map<String, ReqHandlerMethod> eventHandlerMap = new ConcurrentHashMap<>();
@Override
public void afterPropertiesSet() throws Exception {
initHandlerMethods();
}
public Res dispatch(Req req) {
String mappingValue = req.getMappingValue();
if (eventHandlerMap.containsKey(mappingValue)) {
ReqHandlerMethod reqHandlerMethod = eventHandlerMap.get(mappingValue);
ReflectionUtils.makeAccessible(reqHandlerMethod.bridgeMethod);
try {
return (Res) reqHandlerMethod.bridgeMethod.invoke(reqHandlerMethod.handler, req);
} catch (IllegalAccessException | InvocationTargetException e) {
// 这里处理调用时发生的异常, 返回出错信息
// return new Res();
e.printStackTrace();
throw new RuntimeException(e);
}
} else {
throw new IllegalArgumentException("未找到处理方法,mappingValue:" + mappingValue);
}
}
private void initHandlerMethods() {
ApplicationContext ctx = getApplicationContext();
String[] beanNames = ctx.getBeanNamesForType(Object.class);
for (String beanName : beanNames) {
Class<?> handlerType = ctx.getType(beanName);
// 这里是透过AOP和各种产生的代理类拿到真正用户定义的类
Class<?> userType = ClassUtils.getUserClass(handlerType);
if (isHandler(userType)) {
Map<Method, String> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<String>) method -> {
try {
return getMappingForMethod(method, userType);
} catch (Throwable ex) {
throw new IllegalStateException(
"Invalid mapping on handler class [" + userType.getName() + "]: " + method, ex);
}
});
Object bean = ctx.getBean(beanName);
methods.forEach(((method, s) -> {
String mapping = getMappingForMethod(method, userType);
registerHandlerMethod(mapping, bean, method);
}));
}
}
}
private boolean isHandler(Class<?> userType) {
return AnnotationUtils.findAnnotation(userType, Mapping.class) != null;
}
private String getMapping(AnnotatedElement element) {
Mapping mapping =
AnnotatedElementUtils.findMergedAnnotation(element, Mapping.class);
return mapping.value();
}
private String getMappingForMethod(Method method, Class<?> handlerType) {
String info = getMapping(method);
if (info != null) {
String typeInfo = getMapping(handlerType);
if (typeInfo != null) {
info = typeInfo + info;
}
}
return info;
}
private void registerHandlerMethod(String eventName, Object handler, Method method) {
ReqHandlerMethod reqHandlerMethod = new ReqHandlerMethod();
reqHandlerMethod.handler = handler;
reqHandlerMethod.method = method;
reqHandlerMethod.bridgeMethod = BridgeMethodResolver.findBridgedMethod(method);
eventHandlerMap.put(eventName, reqHandlerMethod);
}
static class ReqHandlerMethod {
Object handler;
Method method;
Method bridgeMethod;
Class<? extends Req> reqType;
}
}
当然可以不实现InitializingBean接口,用@PostConstruct也一样,在容器初始化后调用。
@PostConstruct
private void initHandlerMethods() {
// ...
}
在实际使用的可以扩展请求参数的转换,典型的请求参数可能来自非HTTP协议的,需要自己定义消息转换器来转换请求和返回对象。另外还有集中的处理包装和服务调用期间处理公用异常。
本文介绍如何利用Spring框架创建自定义服务分发器,通过定义映射注解和请求响应对象,实现类似SpringMVC的灵活服务分发机制,便于服务扩展和维护。
136

被折叠的 条评论
为什么被折叠?



