JEE6 CDI 擴展實現 MVC (一)

        就目前來講,CDI 基本仍是和 JSF 結合使用的比較多,CDI 的擴展能力很是的出色,Seam3 就是徹底基於 weld 的。固然咱們也能夠擴展 CDI 實現和 Spring MVC, .Net MVC 差很少的功能結合 JSP 一塊兒使用。這裏我是看到了老外的這篇文章,而後對其已經實現的MVC功能作了一些擴展,添加了頁面像 Controller 傳值的功能,後面我還準備嘗試添加表單實體的提交,還有 Controller 返回 JSon,Freemarker 等一些功能,這些功能都是借鑑於 RestEasy 。java

        對於MVC,我就不去介紹了,網上有不少。
spring

      添加 MVC 註解

        JDK5 之後新增了自定義註解的功能,咱們先爲MVC添加好須要的一些註解,在 CDI 中的限定詞都是經過擴展註解來作的。基本取消了配置文件用註解來代替,Spring 3 MVC 也是。下面是目前咱們使用到的幾個註解。
spring-mvc

        添加 @Controller 註解,這個註解表示這個類爲 MVC 中的 Controller。
mvc

@Target({ ElementType.TYPE,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
}

        添加 @RequestMapping 註解,它有兩個參數,第一個表示映射路徑,第二個表示其對應的Http不一樣的提交 GET POST  DELETE。app

@Target({ ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {
    String[] value() default {};
    RequestMethod[] method() default {};
}

        定義一下 RequestMethod 的枚舉java-ee

public enum RequestMethod {
    POST,GET,DELETE
}

        添加 @Param 註解,這個註解只能在 Controller 的參數中使用,他的值表示你請求該 Controller 值的名稱。如 controller?xxx=123 ,當你爲一個參數添加了 @Param("xxx") 註解,那調用這個這個Controller的時候就這個參數就會被傳入 123 值。spa

@Qualifier
@Target({ElementType.FIELD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface Param {	
	String value();
}

        當咱們定義一個 Controller的時候,就能夠這樣去寫:.net

@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 是怎麼作的,有知道的朋友能夠在評論中介紹一下。
debug

        定義 MVC 的基本信息對象code

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 是怎麼處理的朋友,但願不吝賜教。
相關文章
相關標籤/搜索