git仓库地址:服务注册
前言
本文章是阐述通过注解和扫描完成对服务包里面的功能实现注册,不需要为每一个service类,提供不同的映射方法,尤其是供前台获取数据的接口。
- 使用场景
对项目进行封装,规范接口的调用,比如post方法的参数都使用json对象,对返回数据也进行规范,必须返回ResultVo,数据放在vo的data字段中。
一般可以称为:对SpringMVC进行封装;对服务进行注册 - 最终效果
访问对应的服务,只需要找到对应的Controller类url,后面的路径使用service中方法拼接上Auto即可,最终会有整个封装好的demo。 - 涉及的主要类
- AutoDispatch注解
作用:贴在Controller类上面,根据参数serviceInterface指定该Controller对哪些service类提供的服务进行注册。 - LoadPackageClasses工具类
作用:类似Spring的自动扫描机制进行对指定包中的类进行扫描,并根据传入的注解进行过滤未被该注解标注的类。(扫描指定包里所有被某注解标注的类,并放入同一个集合)
在本demo中就是扫描controller包中所有标注了AutoDispatch的注解的Controller类 - MetaDataManager
作用:利用LoadPackageClasses进行获取注册服务的Controller类集合,然后通过反射获取每个controller的AutoDispatch注解,获取注解的serviceInterface参数(class集合即controller需要注册的服务是哪个service中的服务),再通过service的class对象进行反射将每个service的所有public方法进行缓存起来。
4.WelcomeController
作用:提供一个供子类继承的方法,所有的注册服务的Controller都是这个类的子类,对于该唯一的方法我们还进行了扩展,就是服务的前置环境检查和服务执行后的处理。一般情况下,执行前检查和执行后的处理是没有提供的,只是为了拓展性,进行了添加该功能,相信大家看了方法后会动的
使用方法和一些细节
待更新
代码
- WelcomeController
@Component
@Slf4j
public class WelcomeController {
@Autowired
MetaDataManager metaDataManager;
protected Object autoDispatch(HttpServletRequest request, HttpServletResponse response, String methodName, Map<String, ?> model) {
if(request.getMethod().equalsIgnoreCase("GET")) {
//model = request.getParameterMap();
}
AutoDispatch annotation = getClass().getAnnotation(AutoDispatch.class);
Class<?>[] services = annotation.serviceInterface();
String preKey = null, postKey = null;
String key = null;
Class<?> service = null;
Map<String, Method> methodCache = metaDataManager.getMethodCache();
for (Class<?> cls : services) {
if (metaDataManager.getMethodCache().containsKey(key = cls.getName()
.concat(methodName))) {
preKey = key.replace(methodName, "pre" + methodName);
postKey = key.replace(methodName, "post" + methodName);
service = cls;
break;
}
}
if(key != null) {
Object o = ServiceLocator.getBeanByName(CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, service.getSimpleName()));
Object ret = null;
//1. preCondition process
ResultVo preCheck = null;
Method pre = metaDataManager.getMethodCache().get(preKey);
if (pre != null) {
try {
int pCount = pre.getParameterTypes().length;
if(pCount == 1)
preCheck = (ResultVo) pre.invoke(o, model);
else
preCheck = (ResultVo) pre.invoke(o);
}catch (Exception e) {
log.debug("preCondition check error:" + e.getCause().getMessage());
}
if (!ResultVo.isSuccess(preCheck))
throw new RuntimeException((String)preCheck.getMessage());
}
//2. business process
Method m = metaDataManager.getMethodCache().get(key);
try {
int pCount = m.getParameterTypes().length;
if(pCount == 1)
ret = m.invoke(o, model);
else
ret = m.invoke(o);
} catch (Exception e) {
e.printStackTrace();
} finally {
//3. postCondition process
Method post = metaDataManager.getMethodCache().get(postKey);
if(post != null) {
try {
post.invoke(o, ret);
} catch (Exception e) {
e.printStackTrace();
}
}
}
return ret;
}
else
throw new UnsupportedOperationException();
}
}
- AutoDispatch注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
// 自动启用处理激活器
public @interface AutoDispatch {
/** 默认开启 */
boolean enable() default true;
/** 需要注入的服务 */
Class<?>[] serviceInterface();
}
*LoadPackageClasses,借鉴了Spring
public class LoadPackageClasses {
protected final Log logger = LogFactory.getLog(getClass());
public static final String RESOURCE_PATTERN = "/**/*.class";
private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
private List<String> packagesList = new LinkedList<String>();
private List<TypeFilter> typeFilters = new LinkedList<TypeFilter>();
private Set<Class<?>> classSet = new HashSet<Class<?>>();
/**
* 构造函数
*
* @param packagesToScan
* 指定哪些包需要被扫描,支持多个包"package.a,package.b"并对每个包都会递归搜索
* @param annotationFilter
* 指定扫描包中含有特定注解标记的bean,支持多个注解
*/
@SuppressWarnings("unchecked")
public LoadPackageClasses(String[] packagesToScan,
Class<? extends Annotation>... annotationFilter) {
if (packagesToScan != null) {
for (String packagePath : packagesToScan) {
this.packagesList.add(packagePath);
}
}
if (annotationFilter != null) {
for (Class<? extends Annotation> annotation : annotationFilter) {
typeFilters.add(new AnnotationTypeFilter(annotation, false));
}
}
}
/**
* 将符合条件的Bean以Class集合的形式返回
*
* @return
* @throws IOException
* @throws ClassNotFoundException
*/
public Set<Class<?>> getClassSet() throws IOException,
ClassNotFoundException {
this.classSet.clear();
if (!this.packagesList.isEmpty()) {
for (String pkg : this.packagesList) {
String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
+ ClassUtils.convertClassNameToResourcePath(pkg)
+ RESOURCE_PATTERN;
Resource[] resources = this.resourcePatternResolver
.getResources(pattern);
MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(
this.resourcePatternResolver);
for (Resource resource : resources) {
if (resource.isReadable()) {
MetadataReader reader = readerFactory
.getMetadataReader(resource);
String className = reader.getClassMetadata()
.getClassName();
if (matchesEntityTypeFilter(reader, readerFactory)) {
this.classSet.add(Class.forName(className));
}
}
}
}
}
// 输出日志
if (logger.isInfoEnabled()) {
for (Class<?> clazz : this.classSet) {
logger.info(String.format("Found class:%s", clazz.getName()));
}
}
return this.classSet;
}
/**
* 检查当前扫描到的Bean含有任何一个指定的注解标记
*
* @param reader
* @param readerFactory
* @return
* @throws IOException
*/
private boolean matchesEntityTypeFilter(MetadataReader reader,
MetadataReaderFactory readerFactory) throws IOException {
if (!this.typeFilters.isEmpty()) {
for (TypeFilter filter : this.typeFilters) {
if (filter.match(reader, readerFactory)) {
return true;
}
}
}
return false;
}
}
- MetaDataManager
public class MetaDataManager {
@Getter
public Map<String, Method> methodCache = Maps.newHashMap();
@Getter
public Map<String, Class<?>[]> cache = Maps.newHashMap();
public MetaDataManager() {}
public MetaDataManager(String[] pkgs) {
try {
scan(pkgs);
}catch(Exception e) {
;
}
}
@SuppressWarnings("unchecked")
public MetaDataManager scan(String[] pkgs) throws Exception {
//MetaDataManager manager = new MetaDataManager();
if (pkgs == null || pkgs.length == 0)
return null;
LoadPackageClasses loader = new LoadPackageClasses(pkgs, AutoDispatch.class);
Set<Class<?>> sets = loader.getClassSet();
Set<Class<?>> serviceInterfaces = Sets.newHashSet();
for (Class<?> cls : sets) {
AutoDispatch autoDispatch = cls.getAnnotation(AutoDispatch.class);
for (Class<?> service : autoDispatch.serviceInterface()) {
if (!serviceInterfaces.contains(service))
serviceInterfaces.add(service);
}
cache.put(cls.getName(), autoDispatch.serviceInterface());
}
for (Class<?> cls : serviceInterfaces) {
for (Method m : cls.getMethods())
methodCache.put(cls.getName().concat(m.getName()), m);
}
methodCache = Collections.unmodifiableMap(methodCache);
cache = Collections.unmodifiableMap(cache);
return this;
}
}
流程及注意点看下面的博客:利用Spring扫描机制,实现服务的注册(二)