JAVA早期,Web框架都使用XML文件来管理接口,比如Struts,spring。其组件基本都是定义在XML文件中,然后系统启动时通过XML文件来加载。这种模式开发出的系统,文件臃肿不堪,非常难以维护和管理。后来Java带来了Annotation,再加上Java的反射机制。为我们提供了非常便利的方式来管理接口,搭建出的系统就非常的简洁和优雅。来看看其具体应用。

首先我们定义一个作用于方法上的Annotation

package webfunc;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestFunction {
    String action();

    boolean cached() default false;

    boolean isSynchronize() default false;

    String explain() default "Request function";
}


//  @RequestFunction (action = "personVerify")
//  public ResponseData personVerifyMethod(WebSystemContext context) {
     //..............
//  }

这个Annotation里的ElementType.METHOD属性代表本注解是作用在方法上的。还可以根据系统需要给他定义其他功能,比如缓存,异步操作等等。注解定义好,就可以像上面的业务代码这样去使用,来看看系统是如何加载这个注解的。

    在继续之前,简要说明一下,注解的内容是这样的:@RequestFunction (action = “personVerify”)。

    需要执行的代码是这样的:public ResponseData personVerifyMethod(WebSystemContext context)。

    这就需要有一个对应关系,分别代表请求指令和方法,通过请求指令来执行对应的方法。

    先定义一个实际的执行方法的调用类。里面有需要被调用的class和method,只需要调用method就可以。

    package webfunc;
    
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    public class InvokedFunction {
        public Method method;
        public Class<?> clazz;
        public boolean single;
        public boolean cached;
        public boolean voided;
    
        public Object execute(WebSystemContext context) {
            Object refer = null;
            try {
                if (this.single != true) {
                    refer = clazz.newInstance();
                }
                Object[] args = new Object[]{context};
                Object result = method.invoke(refer, args);
    
                return voided == true ? null : result;
            } catch (InstantiationException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            } catch (InvocationTargetException e) {
                throw new RuntimeException(e.getTargetException());
            }
        }
    }
    

    这个类用到了Java反射机制,其目的是根据参数,返回值,来执行需要被执行的方法。而这些内容的定义,就是事先加载好的,类的实例,方法名以及其他属性。

    下面在定义一个指令类,来管理请求指令和调用方法之间的关系

    package webfunc;
    
    import java.util.HashMap;
    import java.util.Map;
    
    public class InvokeCommand {
    
        Map<String, InvokedFunction> funcMap;
    
        public InvokeCommand() {
            funcMap = new HashMap<String, InvokedFunction>();
        }
    
        public boolean hasFunction(String func) {
            return funcMap.containsKey(func);
        }
    
        public InvokedFunction getFunction(String func) {
            return InvokeCommand.this.funcMap.get(func);
        }
    
        public void put(String func, InvokedFunction invFunc) {
            this.funcMap.put(func, invFunc);
        }
    
    }
    

    在这个类中我们维护一个map,这个map管理请求指令与被调用对象的关系。比如指令为personVerify,其对应的被调用方法就是public ResponseData personVerifyMethod(WebSystemContext context)。

    小零件都准备好了,我们就要在系统启动的时候加载这些注解和方法,等待被系统调用

    package webfunc;
    
    import java.lang.reflect.Method;
    import java.lang.reflect.Modifier;
    import java.util.HashMap;
    import java.util.Map;
    
    public class ServiceContext {
    
        public static Map<String, InvokeCommand> cmmdMap;
        private static ServiceContext context;
    
        private ServiceContext() {
            cmmdMap = new HashMap<String, InvokeCommand>();
            try{
                //...preload the Commands from xml file...
                //the business class
                String classStr = "com.test.example.Business.UserAction";
                //Request Command
                String commandStr = "personVerify";
                this.parseWebModule(Class.forName(classStr), commandStr);
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    
        public static ServiceContext getContext() {
            if (context == null) {
                context = new ServiceContext();
            }
            return ServiceContext.context;
        }
    
        public boolean hasCommand(String cmmd) {
            return cmmdMap.containsKey(cmmd);
        }
    
        public InvokeCommand getCommand(String cmmd) {
            return this.cmmdMap.get(cmmd);
        }
    
        public void parseWebModule(Class<?> clz, String code) {
            Class<RequestFunction> antClass = RequestFunction.class;
            for (Method method : clz.getMethods()) {
                int modify = method.getModifiers();
                if (Modifier.isPublic(modify)) {
                    RequestFunction webfunc = null;
                    if (method.isAnnotationPresent(antClass)) {
                        webfunc = method.getAnnotation(antClass);
                        if (code.equals(webfunc.action())) {
                            InvokeCommand proxy = null;
                            if (!cmmdMap.containsKey(code)) {
                                proxy = new InvokeCommand();
                                cmmdMap.put(code, proxy);
                            }
                            InvokedFunction webmethod = null;
                            webmethod = new InvokedFunction();
                            webmethod.clazz = clz;
                            webmethod.method = method;
                            webmethod.cached = webfunc.cached();
                            webmethod.single = Modifier.isStatic(modify);
                            Class<?> returnType = method.getReturnType();
                            webmethod.voided = returnType.equals(Void.class);
    
                            proxy = cmmdMap.get(code);
                            proxy.put(code, webmethod);
    
                        }
                    }
                }
            }
    
        }
    }
    

    这个类中,我们通过配置文件加载了业务执行类和业务执行方法。把他们提前加载到内存,等请求到达时,就可以判断该请求命令是否合法。如果合法,就可以调用与其对应的方法。然后返回响应给客户端,这里也用到了JAVA的反射机制。

    这里的业务执行类和调用命令应该全部配置在文件中统一加载。现在大多数的框架已经改变了模式,这样也会造成配置文件的臃肿。现在大多数框架都是检索Annotation,利用Java的反射机制解析其内容。这里只是用例子说明Annotation和反射机制是如果帮助我们开发Java框架的。

    这一套东西既可以用在Web框架中,也可以用在C/S结构的接口平台中,非常实用。


    发表回复

    Thanks for your support to bet365fans!