rose初始化

BruceZhang發表於2015-08-05
rose封裝了spring框架,集合spring IOC和AOP所構建的一個MVC框架

rose載體為RoseFilter
在web.xml配置檔案,如filter進行配置即可,如下:
	< filter>
             < filter-name> roseFilter </filter-name >
             < filter-class> net.paoding.rose.RoseFilter </filter-class >
       </ filter>

       < filter-mapping>
             < filter-name> roseFilter </filter-name >
             < url-pattern> /*</ url-pattern >
             < dispatcher> REQUEST </dispatcher >
             < dispatcher> FORWARD </dispatcher >
             < dispatcher> INCLUDE </dispatcher >
       </ filter-mapping>

RoseFilter 
初始化函式:initFilterBean

初始化 rose的 context 容器
WebApplicationContext rootContext = prepareRootApplicationContext();
rose通過繼承XmlWebApplicationContext,構建自己的context RoseWebAppContext 作為最根級別的 ApplicationContext 物件
RoseWebAppContext rootContext = new RoseWebAppContext(getServletContext(), load , false);
        rootContext.setConfigLocation(contextConfigLocation);
        rootContext.setId( "rose.root");
        rootContext.refresh();
上述的 refresh 方法和spring的 context 的初始化過程一樣,即IOC物件的初始化
呼叫refresh 方法後,就會把配置檔案中的 bean 載入到記憶體,成為 BeanDefinition

rose下 會有如下配置檔案的約定 /WEB-INF/applicationContext*.xml
所有形如該形式的.xml檔案將會被rose所識別,並載入到IOC容器中


// 識別 Rose 程式模組
this.modules = prepareModules(rootContext);

rose 以 controllers 作為一個模組,controllers 下的package也會作為一個模組
模組物件 ModuleResource 封裝了 controllers 相關的資源
包括模組路徑,匹配的xml資源等 
初始化過程通過 provider.findModuleResources( load) 進行資源的載入,構建 List<ModuleResource>

初始化過程會把模組裡的 beandefinition 類載入到記憶體,並儲存在ModuleResource 

module.addModuleClass(Class. forName(className));

完成模組資源掃描後,就開始為每個具體的模組進行具體的資源搭配構建了
List<Module> modules = modulesBuilder. build(moduleResources, rootContext)
構建一個模組list,包括了rose的各個模組,即對rose的各個模組的初始化

單獨的模組 Module 會構建自己的 context 物件
ServletContext servletContext = parent.getServletContext();
        Assert. notNull(servletContext);
        ModuleAppContext wac = new ModuleAppContext();
        wac.setParent(parent);
        wac.setServletContext(servletContext);
        wac.setContextResources( toResources(contextResources));
        wac.setId(uniqueId);
        wac.setNamespace(namespace);
        wac.setMessageBaseNames(messageBasenames);
        wac.refresh();
又看到 refresh 方法了,即初始化 Module 的context IOC容器
然後會把模組相關的bean 註冊到模組的 IOC容器裡
registerBeanDefinitions (moduleContext, moduleResource.getModuleClasses());


然後 rose 會把模組相關的 resolver interceptor等資源進行載入
// 從Spring應用環境中找出本web模組要使用的ParamValidator,ParamResolver, ControllerInterceptor, ControllerErrorHandler

List<ParamResolver> customerResolvers = findContextResolvers(moduleContext);
List<InterceptorDelegate> interceptors = findContextInterceptors(moduleContext);
List<ParamValidator> validators = findContextValidators(moduleContext);
ControllerErrorHandler errorHandler = getContextErrorHandler(moduleContext);

找出關聯的資源,然後會載入到module 中
module.addCustomerResolver(resolver);
module.addControllerInterceptor(interceptor);
module.addValidator(validator);
module.setErrorHandler(errorHandler);
對於interceptor 的載入如下
for ( int i = 0; i < interceptors .size(); i++) {
            // 先判斷是否有"名字"一樣的攔截器
            InterceptorDelegate temp = interceptors.get(i);
            if (temp.getName().equals(interceptor.getName())) {
                // rose內部要求interceptor要有一個唯一的標識
                // 請這兩個類的提供者商量改類名,不能同時取一樣的類名
                // 如果是通過@Component等設定名字的,則不要設定一樣
                ControllerInterceptor duplicated1 = InterceptorDelegate
                        . getMostInnerInterceptor(temp);
                ControllerInterceptor duplicated2 = InterceptorDelegate
                        . getMostInnerInterceptor(interceptor);

                throw new IllegalArgumentException(
                        "duplicated interceptor name for these two interceptors: '"
                                + duplicated1.getClass() + "' and '" + duplicated2.getClass() + "'" );
            }
            // 加入到這個位置?
            if (!added && interceptor.getPriority() > temp.getPriority()) {
                this.interceptors .add(i, interceptor);
                added = true;
            }
        }
攔截器的優先順序即通過 if (!added && interceptor.getPriority() > temp.getPriority()) 這個語句進行判斷
然後把攔截器載入到適當的位置


載入完相關的資源後,rose才對controller進行初始化,對模組裡的各個 controller 進行初始化

for (String beanName : beanFactory.getBeanDefinitionNames()) {
                checkController(moduleContext, beanName, module);
            }

在初始化過程中,可以看到以下的語句:
Path reqMappingAnnotation = clazz.getAnnotation(Path.class);
        if (reqMappingAnnotation != null) {
            controllerPaths = reqMappingAnnotation.value();
        }

我們在 controller 上標註的@Path 註解,在這裡得到了解析,作為controller 的新路徑

另外可以看到如下語句,rose 約定,controller 的命名規範
/ TODO: 這個程式碼是為了使從0.9到1.0比較順暢而做的判斷,201007之後可以考慮刪除掉
            if (controllerName.equals("index" ) || controllerName.equals("home" )
                    || controllerName.equals( "welcome")) {
                // 這個異常的意思是讓大家在IndexController/HomeController/WelcomeController上明確標註@Path("")
                throw new IllegalArgumentException("please add @Path(\"\") to " + clazz.getName());
            } else {
                controllerPaths = new String[] { "/" + controllerName };
            }


然後 rose 從context 裡得到 controller 例項,沒有對 controller 進行屬性的標註,就是單例了
Object controller = context.getBean(beanName);
        module. addController(//
                controllerPaths, clazz, controllerName, controller);
新增 controller 例項到module 裡
這樣一個 module 就填充完成了

完成了資源的載入後就到了 另一個關鍵的步驟了,即rose的匹配樹的構建過程
// 建立匹配樹以及各個結點的上的執行邏輯(Engine)
            this.mappingTree = prepareMappingTree(modules);

rose構建一個根節點,然後從根節點進行枝葉的新增
Mapping rootMapping = new ConstantMapping( "");
        MappingNode mappingTree = new MappingNode(rootMapping);
        LinkedEngine rootEngine = new LinkedEngine(null, new RootEngine(instructionExecutor ),
                mappingTree);
        mappingTree.getMiddleEngines().addEngine(ReqMethod. ALL, rootEngine);

        TreeBuilder treeBuilder = new TreeBuilder();
        treeBuilder.create(mappingTree, modules);

構建過程由 create 方法開始
整個構建過程為先從 module 開始,一個一個路徑新增到匹配樹上
for (Module module : modules) {
            addModule(rootNode, module);
        }
然後把controllers 的路徑新增到匹配樹上
for (ControllerRef controller : controllers) {
            addController(module, parent, moduleEngine, controller);
        }
最後把 action 方法的路徑新增到匹配樹上
for (MethodRef action : actions) {
                addAction(module, controller, action, target, controllerEngine);
            }

在把 controllers 的action 方法新增到匹配樹上前,rose會對 controllers 進行初始化操作

如下,標註了 @Get @Post的方法會得到處理,解析並獲取標註的路徑值
for (Annotation annotation : annotations) {
            if (annotation instanceof Delete) {
                restMethods.put(ReqMethod. DELETE, ((Delete ) annotation).value());
            } else if (annotation instanceof Get) {
                restMethods.put(ReqMethod. GET, ((Get ) annotation).value());
            } else if (annotation instanceof Head) {
                restMethods.put(ReqMethod. HEAD, ((Head ) annotation).value());
            } else if (annotation instanceof Options) {
                restMethods.put(ReqMethod. OPTIONS, ((Options ) annotation).value());
            } else if (annotation instanceof Post) {
                restMethods.put(ReqMethod. POST, ((Post ) annotation).value());
            } else if (annotation instanceof Put) {
                restMethods.put(ReqMethod. PUT, ((Put ) annotation).value());
            } else if (annotation instanceof Trace) {
                restMethods.put(ReqMethod. TRACE, ((Trace ) annotation).value());
            } else {}
        }
        for (String[] paths : restMethods.values()) {
            for (int i = 0; i < paths.length; i++) {
                String path = paths[i];
                if (path.length() > 0 && path.charAt(0) != '/') {
                    paths[i] = "/" + path;
                }
            }
        }

如下,rose 做了約定,凡是使用index/get等的方法名的必須新增註解
否則rose會丟擲異常
// TODO: 這個程式碼是為了使從0.9到1.0比較順暢而做的判斷,201007之後可以考慮刪除掉
                        if ("get" .equals(method.getName()) || "index".equals(method.getName())) {
                            // 這個異常的意思是讓大家在get/index上明確標註@Get,請注意@Get的意思
                            throw new IllegalArgumentException("please add @Get to "
                                    + controllerClass.getName() + "#" + method.getName());
                        }
                        if ("post" .equals(method.getName()) || "delete".equals(method.getName())
                                || "put".equals(method.getName())) {
                            // 這個異常的意思是讓大家在post/delete/put上明確標註@Get/@Delete/@Put,請注意@Get的意思
                            throw new IllegalArgumentException("please add @"
                                    + StringUtils.capitalize(method.getName()) + " to "
                                    + controllerClass.getName() + "#" + method.getName());
                        }


如果方法沒有標註任何註解,則 rose會使用預設操作
方法會預設可以處理get、post請求,請求路徑名為方法名
shotcutMappings.put(ReqMethod. GET, new String[] { "/" + method.getName() });
                        shotcutMappings
                                .put(ReqMethod. POST, new String[] { "/" + method.getName() });

另外,對於標註了 @AsSuperController 註解的 controller 類,其子類可以使用父類的action方法
其處理過程如下,rose 會解析註解,標註該註解則會 解析父類的action方法,並新增到匹配樹中
clz = clz.getSuperclass();
            if (clz == null || clz.getAnnotation(AsSuperController .class) == null) {
                break;
            }
// 初始化完成後,就可以通過addAction 新增 controller的方法了
//  而addAction方法裡有下面的邏輯
Engine actionEngine = new ActionEngine(module, controller.getControllerClass(),//
                controller.getControllerObject(), action.getMethod());

// 初始化actionEngine物件
public ActionEngine(Module module, Class<?> controllerClass, Object controller, Method method) {
        this .module = module;
        this .controllerClass = controllerClass;
        this .controller = controller;
        this .method = method;
        this .interceptors = compileInterceptors ();
        this .methodParameterResolver = compileParamResolvers();
        this .validators = compileValidators();
        this .acceptedCheckers = compileAcceptedCheckers();
    }

// 這裡可以看到這該方法下的聯結器的初始化
private InterceptorDelegate[] compileInterceptors () {
        List<InterceptorDelegate> interceptors = module .getInterceptors();
        List<InterceptorDelegate> registeredInterceptors = new ArrayList<InterceptorDelegate>(
                interceptors.size());
        for (InterceptorDelegate interceptor : interceptors) {
            // 獲取@Intercepted註解 (@Intercepted註解配置於控制器或其方法中,決定一個攔截器是否應該攔截之。沒有配置按“需要”處理)
            Intercepted intercepted = method .getAnnotation(Intercepted . class);
            if (intercepted == null) {
                // 對於標註@Inherited的annotation,class.getAnnotation可以保證:如果本類沒有,自動會從父類判斷是否具有
                intercepted = this .controllerClass .getAnnotation( Intercepted. class );
            }
            // 通過@Intercepted註解的allow和deny排除攔截器
            if (intercepted != null) {
                // 3.1 先排除deny禁止的
                if (ArrayUtils.contains(intercepted.deny(), "*" )
                        || ArrayUtils. contains(intercepted.deny(), interceptor.getName())) {
                    continue ;
                }
                // 3.2 確認最大的allow允許
                else if (!ArrayUtils.contains (intercepted.allow(), "*" )
                        && !ArrayUtils. contains(intercepted.allow(), interceptor.getName())) {
                    continue ;
                }
            }
            // 取得攔截器同意後,註冊到這個控制器方法中
            if (interceptor.isForAction(controllerClass , method )) {
                registeredInterceptors.add(interceptor);
            }
        }
        //
        return registeredInterceptors
                .toArray( new InterceptorDelegate[registeredInterceptors.size()]);
    }

// 我們知道攔截器可以通過使用註解來標註是否攔截一個請求的處理的
// 具體是否需要攔截,這個判斷就是通過 interceptor.isForAction 這個方法來實現的
public final boolean isForAction(Class<?> controllerClazz, Method actionMethod) {
        // 返回false,表示控制器或其方法沒有標註本攔截器所要求的註解
        if (!checkRequiredAnnotations(controllerClazz, actionMethod)) {
            return false;
        }
        // 返回true,表示控制器或其方法標註了“拒絕”註解
        if (checkDenyAnnotations(controllerClazz, actionMethod)) {
            return false;
        }
        return isForAction(actionMethod, controllerClazz);
    }








相關文章