JEE6 CDI 扩展实现 MVC (一)

自作多情 提交于 2020-03-11 13:16:19

        就目前来说,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 是怎么处理的朋友,希望不吝赐教。







标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!