/* * Copyright 2007-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.paoding.rose.web; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.List; import java.util.Set; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import net.paoding.rose.web.annotation.Param; import net.paoding.rose.web.impl.thread.AfterCompletion; import net.paoding.rose.web.paramresolver.ParamMetaData; import net.paoding.rose.web.paramresolver.ParamResolver; import net.paoding.rose.web.var.Flash; import net.paoding.rose.web.var.Model; import org.springframework.validation.BindingResult; import org.springframework.web.context.WebApplicationContext; /** * {@link Invocation} 封装框架对控制器方法的一次调用相关的信息:请求和响应对象、目标控制器方法、方法参数值等。 * <p> * 可以在控制器方法参数类似如下声明这个调用对象,获取之:<br> * <code> * public String get(Invocation inv) { * return "@hello, get!"; * } * </code> * <p> * * 当不存在请求转发,一个用户请求只存在一个调用实例;当用户请求可能被转发时,转发前和转发后的控制器方法调用是不同的调用, * 则一个用户请求将不只包含了一个调用实例。可以通过 {@link Invocation#getPreInvocation()} * 获取该调用inv的前一个调用preinv。 * <p> * * 在参数解析器{@link ParamResolver}、验证器{@link ParamValidator}、拦截器 * {@link ControllerInterceptor} 的接口方法实现中可以得到当前调用inv实例,使可以获取此此调用的信息进行控制。 * <p> * * 如果需要在参数验证器、拦截器、控制器之间传递仅在本次调用可见、和视图渲染无关的一些参数时,请使用 * {@link #setAttribute(String, Object)}和 {@link #getAttribute(String)} * 方法。如果某个数据要渲染发送给客户端,请使用 {@link #addModel(String, Object)}。 * <p> * * {@link #addModel(String, Object)}和{@link #setAttribute(String, Object)} * 的区别: * * <ul> * <li>他们各自采用独立容器存储数据,互相不混淆。</li> * <li>addModel的数据可以被后续的调用的getModel方法“看见”,而setAttribute的数据不能被后面调用的 * getAttribute方法“看见”</li> * </ul> * <p> * * WIKI上的说明 <a href= * "http://code.google.com/p/paoding-rose/wiki/Rose_Guide_Invocation" * >http://code.google.com/p/paoding-rose/wiki/Rose_Guide_Invocation</a> * * @author 王志亮 [qieqie.wang@gmail.com] * */ public interface Invocation { /** * 返回rose对本次请求的地址、方法的信息 * <p> * 比如你想得到本次调用的 URL,通过 {@link RequestPath#getUri()} 将是最准确的,而非 * {@link HttpServletRequest#getRequestURI()}。 * * @return */ public RequestPath getRequestPath(); /** * 返回本次调用的 {@link HttpServletRequest}对象 * * @return */ public HttpServletRequest getRequest(); /** * 返回本次调用的 {@link HttpServletResponse}对象 * * @return */ public HttpServletResponse getResponse(); /** * 更改本次调用的请求对象 * * @param request */ public void setRequest(HttpServletRequest request); /** * 本次调用的目标控制器对象。 * <p> * (可能已经是一个Proxy、CGlib等等后的对象,不是原控制器的直接实例对象) * * @return */ public Object getController(); /** * 本次调用的控制器的类名,这个类名就是编写控制器类时的那个类。 * <p> * 他不总是和本次调用的控制器对象的getClass()相同。 * * @return */ public Class<?> getControllerClass(); /** * 本次调用的目标控制器方法 * * @return */ public Method getMethod(); /** * 在该调用所在方法、控制器上上是否标注了该注解对象,如果没有返回false * * @param annotationClass * @return */ public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass); /** * 获取标注在该调用所在方法、控制器上的注解对象,如果没有返回null * * @param <T> * @param annotationClass * @return */ public <T extends Annotation> T getAnnotation(Class<T> annotationClass); /** * 本地调用的目标控制器方法中,各参数的名字。 * <p> * 这个名字不是程序开发中使用的变量名,而是通过 @Param * 声明的名字,比如@Param("userId")用来声明某个参数的名字是userId。 * <p> * 建议,你把参数名和变量定义为一样! * * <p> * 本法方法返回的字符串数组长度和目标控制器方法的参数个数相同,元素的值在下列情况为空: * <ul> * <li>没有标注@Param的基本类型以及封装类型</li> * <li>没有标注@Param的这些类型:时间类型、Map、Collection类型等</li> * </ul> * 对用户对应的实体Bean,一般没有标注@Param,他的参数名即是该实体类名的首字母小写化字符串。<br> * (额外注意:通过@Param标注一个实体类, * 不仅仅改变了默认的参数名,同时也将改变数据绑定规则,即使通过@Param标注的名字和默认的一样)。 * * @see Param * @return */ public String[] getMethodParameterNames(); /** * 本次调用的控制器方法所使用的参数值。 * <p> * 请谨慎对待这个方法的返回值,如果在拦截器等组件中改变这个数组的元素值,表示要使用所设置数据代替原来的数据去调用控制器方法。 * * @return */ public Object[] getMethodParameters(); /** * 获取在请求查询串(即问号后的xx=yyy)、POST中Body中、URI所带的参数值。 * <p> * URI中的参数需要通过在控制器方法中通过类似@Path("user_{name}")进行声明,才可以获取name的参数<br> * 同时因为 Rose 对 {@link HttpServletRequest} * 进行了封装,使得其request的getParameter和inv的getParameter的语义相同。 * * @param name * @return */ public String getParameter(String name); /** * 返回给定名字的方法参数。 * <p> * * @return */ public Object getMethodParameter(String name); /** * 改变调用的方法参数值 * * @param index * @param newValue */ public void changeMethodParameter(int index, Object newValue); /** * 改变调用的方法参数值 * * @param name * @param newValue */ public void changeMethodParameter(String name, Object newValue); /** * * @param paramMeta * @param newValue */ public void changeMethodParameter(ParamMetaData paramMeta, Object newValue); /** * 将对象(object,array,collection等)加入到MVC中的Model中作为一个属性,通过它传递给View * <p> * 将使用该对象的类名头字母小写的字符串作为名字;<br> * 如果对象是数组,去数组元素的类的类名字头字母小写加上"List"作为名字<br> * 如果对象是集合元素,取其第一个元素的类的类名字头字母小写加上"List"作为名字<br> * 如果该值为空或者其集合长度为0的话,将被忽略<br> * * @param value 可以是普通对象,数组对象,集合对象;<br> * 可以为null,如果对象为null或集合长度为0直接被忽略掉 * @see Model#add(Object) * @see #getModel() */ public void addModel(Object value); /** * 将对象(object,array,collection等)加入到MVC中的Model中作为一个属性,通过它传递给View * * @param name 在view中这个字符串将作为该对象的名字被使用;非空 * @param value 可以是普通对象,数组对象,集合对象;<br> * 可以为null,如果对象为null直接被忽略掉 * @see Model#add(String, Object) * @see #getModel() */ public void addModel(String name, Object value); /** * 获取添加到model中的对象 * * @param name * @return */ public Object getModel(String name); /** * 返回Model接口,通过这个设置对象给view渲染 * * @return */ public Model getModel(); /** * 设置一个和本次调用关联的属性。这个属性可以在多个拦截器中共享。 * <p> * 因为所设置的属性值和本次调用有关,所以他与 {@link #getRequest()#setAttribute(String, * Object)}是不相同的。 * * @param name * @param value * @return */ public Invocation setAttribute(String name, Object value); /** * 获取前面拦截器或代码设置的,和本次调用相关的属性 * * @param name * @return */ public Object getAttribute(String name); /** * 删除inv的某一个属性 * * @param name */ public void removeAttribute(String name); /** * 返回本次调用相关的所有属性名字 * * @return */ public Set<String> getAttributeNames(); /** * 用于向重定向跳转后的页面传递参数,比如提示信息 * * @param name * @param msg */ public void addFlash(String name, String msg); /** * 返回本次请求附带的Flash信息,如果上次请求的响应中没有往用户端写入Flash信息,仍旧会返回一个非空的Flash对象, * 只是里面的数据将为空。 * * @return */ public Flash getFlash(); /** * 返回本次请求附带的Flash信息。 * <p> * 如果之前没有获取过这个Flash信息,且create参数为false则返回null,否则创建Flash对象,并填充可能的Flash信息。 * * @param create * @return */ public Flash getFlash(boolean create); /** * 返回本次调用控制器所在模块的 {@link WebApplicationContext} 对象 * * @return */ public WebApplicationContext getApplicationContext(); /** * 返回 {@link ServletContext} 对象 * * @return */ public ServletContext getServletContext(); /** * 返回所有参数绑定名 * * @return */ public List<String> getBindingResultNames(); /** * 返回所有参数绑定结果对象 * * @return */ public List<BindingResult> getBindingResults(); /** * 获取控制器方法普通参数的绑定结果 * * @return * @throws NullPointerException 如果当前线程中没有绑定请求对象时 */ public BindingResult getParameterBindingResult(); /** * 获取控制器方法各个bean的绑定结果 * * @param bean bean实体对象或bindingResult的名字 * @return * @throws NullPointerException 如果当前线程中没有绑定请求对象时 */ public BindingResult getBindingResult(String bean); /** * 获取前一个Invocation对象,如果没有返回null * * @return */ public Invocation getPreInvocation(); /** * 获取最开头的Invocation对象,可能是自己本身 * * @return */ public Invocation getHeadInvocation(); /** * 返回类似 /user/{userId} 这样的字符串 * * @return */ public String getResourceId(); /** * <p> * 通过这个方法,可以在拦截器、控制器等能够拿到inv的地方设置一个“资源回收计划”。 * 在整个页面渲染结束时,或者因为异常导致流程中断时,所设置的“资源回收计划”能够被执行。 * * <p> * 注意:越先加入的afterComletion对象,越靠后执行。 * * @param afterComletion */ public void addAfterCompletion(AfterCompletion afterComletion); }