就目前来说,CDI 基本还是和 JSF 结合使用的比较多,CDI 的扩展能力非常的出色,Seam3 就是完全基于 weld 的。当然我们也可以扩展 CDI 实现和 Spring MVC, .Net MVC 差不多的功能结合 JSP 一起使用。这里我是看到了老外的这篇文章,然后对其已经实现的MVC功能做了一些扩展,添加了页面像 Controller 传值的功能,后面我还准备尝试添加表单实体的提交,还有 Controller 返回 JSon,Freemarker 等一些功能,这些功能都是借鉴于 RestEasy 。
对于MVC,我就不去介绍了,网上有很多。
添加 MVC 注解
JDK5 以后新增了自定义注解的功能,我们先为MVC添加好需要的一些注解,在 CDI 中的限定词都是通过扩展注解来做的。基本取消了配置文件用注解来代替,Spring 3 MVC 也是。下面是目前我们使用到的几个注解。
添加 @Controller 注解,这个注解表示这个类为 MVC 中的 Controller。
@Target({ ElementType.TYPE,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
}
添加 @RequestMapping 注解,它有两个参数,第一个表示映射路径,第二个表示其对应的Http不同的提交 GET POST DELETE。
@Target({ ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {
String[] value() default {};
RequestMethod[] method() default {};
}
定义一下 RequestMethod 的枚举
public enum RequestMethod {
POST,GET,DELETE
}
添加 @Param 注解,这个注解只能在 Controller 的参数中使用,他的值表示你请求该 Controller 值的名称。如 controller?xxx=123 ,当你为一个参数添加了 @Param("xxx") 注解,那调用这个这个Controller的时候就这个参数就会被传入 123 值。
@Qualifier
@Target({ElementType.FIELD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface Param {
String value();
}
当我们定义一个 Controller的时候,就可以这样去写:
@Controller
@RequestMapping("/person/")
public class PersonController {
public Person createPerson() {
return new Person(null, "new", "person");
}
@RequestMapping("list")
public String doListPeople() {
return "listPeople";
}
@RequestMapping("view")
public String doViewPerson() {
return "viewPerson";
}
@RequestMapping(value = "edit", method = RequestMethod.GET)
public String doEditPerson() {
return "editPerson";
}
@RequestMapping(value = "edit", method = RequestMethod.POST)
public String doPostUpdatePerson(@Param("first") String first,@Param("last") int last) {
return "updatedPerson";
}
MVC 的初始化
在容器启动的时候,我们这里需要讲工程路径下面所有的 Controller 的基本信息初始化好,然后存在内存中。这里每一个Controller 包括其类的映射路径,方法的路径,Http请求的方式,所执行的方法,已经方法的参数信息。我们这里作为 MVC 的元数据初始化好放在一个 List 里面。然后我们定义一个 Servlet 来拦截所有请求,然后遍历这个List找出匹配的 Controller 然后执行其方法以后,根据返回的 View 的路径然后开打这个页面。我没有去研究过 Spring MVC 是怎么做的,有知道的朋友可以在评论中介绍一下。
定义 MVC 的基本信息对象
public class ControllerMethod {
private final String prefix;
private final String suffix;
private final RequestMethod[] requestMethod;
private final Method method;
private final List<Map<String,Class<?>>> args;
// gets , sets
}
我们通过添加一个 CDI 的 ApplicationScoped 的 Bean 来完成我们的初始化工作,我们添加一个@PostConstruct的方法,这个方法会在工程第一次被访问的时候执行该方法。
@ApplicationScoped
public class ControllerInfo {
private final List<ControllerMethod> controllerMethods = new ArrayList<ControllerMethod>();
private static final String[] DEFAULT_MAPPING_PATHS = new String[] { "" };
public static final AnnotationLiteral<Controller> CONTROLLER_LITERAL = new AnnotationLiteral<Controller>() {
private static final long serialVersionUID = -3226395594698453241L;
};
@Inject
private BeanManager beanManager;
@PostConstruct
public void initialize() {
logger.debug("Initializing controller info");
Set<Bean<?>> controllers = beanManager.getBeans(Object.class,
CONTROLLER_LITERAL);
for (Bean<?> bean : controllers) {
add(bean.getBeanClass());
}
sortControllerMethods();
listMethods();
}
... ...
}
在这个方法中,先通过 CDI 的 BeanManager 拿到所有具有 @Controller 注解的类,然后调用 add 方法,解析这个类里面的方法 具有@RequestMapping 的方法。初始化 ControllerMethod 对象放到 List 中。
private void add(Class<?> clazz) {
logger.debug("Adding class {}", clazz);
if(clazz == null){
throw new NullPointerException();
}
if (clazz.isAnnotationPresent(Controller.class)) {
logger.debug("Found controller on class {}", clazz);
RequestMapping rm = clazz.getAnnotation(RequestMapping.class);
Method[] methods = clazz.getMethods();
String[] controllerPaths = null;
if (rm != null) {
controllerPaths = rm.value();
}
// if no paths are specified, then default to one blank path
if (controllerPaths == null || controllerPaths.length == 0) {
controllerPaths = DEFAULT_MAPPING_PATHS;
}
// add methods for each controller level paths
for (String prefix : controllerPaths) {
for (Method m : methods) {
addMethod(prefix, m);
}
}
}
}
private void addMethod(String prefix, Method javaMethod) {
if(javaMethod == null){
throw new NullPointerException();
}
RequestMapping mapping = javaMethod.getAnnotation(RequestMapping.class);
Annotation[][] annotations = javaMethod.getParameterAnnotations();
Class<?>[] types = javaMethod.getParameterTypes();
// 这里对方法的参数处理,形成 名称:类型 的键值对
List<Map<String,Class<?>>> args = new ArrayList<Map<String,Class<?>>>();
for(int i = 0 , size = types.length; i<size; i ++){
Map<String,Class<?>> m = new HashMap<String,Class<?>>();
for(Annotation a : annotations[i]){
if(a instanceof Param){
m.put(((Param)a).value(),types[i]);
}
}
args.add(m);
}
if (mapping != null) {
logger.debug("Found request mapping on method {}", javaMethod
.getName());
String[] paths = mapping.value();
// if these are blank, fill with defaults
if (paths.length == 0) {
paths = DEFAULT_MAPPING_PATHS;
}
for (String path : paths) {
controllerMethods.add(new ControllerMethod(javaMethod, prefix,
path, mapping.method(),args));
}
}
// check for other annotations in the future
}
这样,我们就完成对MVC 的Controller 的基本信息的初始化,目前功能还比较简单。下面会给出处理请求的功能实现。不过,有知道Spring 3 MVC 是怎么处理的朋友,希望不吝赐教。
来源:oschina
链接:https://my.oschina.net/u/254662/blog/73519