|
2009年11月16日
什么话该说,什么话不该说,该说的话该怎么说。谁能告诉我。
2009年12月31日,站在2009年的尾巴上,不禁感到时间飞逝。2009年,匆匆而过。在过去的2009年,收获何多,失去何多。2009年,对我来说,是一个重要的转折点。渐渐的发现自己应该长大了,也发现自己确实长大了,更发现自己实际上还是需要继续长大。 每年的这个时候,各个新闻媒体都会评出什么十大之类的,我也落一会俗(当然自己本身就很俗),来看看今年发生在我身上的几大事件: 先大体罗列一下吧: - 订婚
- 进京
- 工作
- 毕业
- 读研
- 买笔记本
从时间跨度上来说, 1月到2月在家过年,2月和3月在青岛某公司实习,4月和5月在北京某公司实习,6月在中国石油大学享受毕业之前的时光,7月到8月继续在北京某公司实习,9月到12月在上学+实习。2009年最幸福的事情发生在1到2月,尽快也有不快;最平常的日子在2月和3月,到了4月和5月或许是我最纠结的日子吧;6月或许是最快乐的日子的吧;7月和8月让我体会到了工作的滋味;而9月到12月,整天在公司与学校之间奔跑,知道了工作+上课两者要做到兼顾的滋味,虽然并没有做到兼顾。 2009年大体经历如此。2009年对我最大的关键字或许就是“改变”,这一年我订婚了,在这一点上,改变了我的准非单身状态;在这一年,我实习了,而且大量的时间都在于此,改变了我仅仅是学生的状态;在这一年,我毕业了,我离开了生活学习四年的中国石油大学,离开了让我毕生难忘的日子;在这一年,我来北京了,从对北京的一无所知,到开始的彷徨,然后渐渐熟悉和适应;在这一年,我的经济渐渐独立,尽管每个月只有不到1000的收入,但能满足的我的基本需求;在这一年,我买了笔记本,虽然对其他人来说,这不是一件特别的事,对我来说,因采用了分期付款,而用接下来一年中近半个月的工资来还,但我不觉得后悔,在这个过程中,我的经济观念和理财观念开始渐渐改变;在这一年,工作成了我的核心,工作教会我很多东西,在与人交流、在工作态度、在技术上,都有一定的提高。 回忆2009年,收获颇多,也失去不少。但日子总是向前的,有得必有失。希望在以后的2009年,自己能够渐渐提高自己的能力。无论是在生活上,在工作上,还是在心理上。 2009年10大大事: 与老婆订婚: 2009年,
上次的文章中介绍了ModelAndView对象中的view对象,可以使用字符串来让Spring框架进行解析获得适合的视图。而解析View的就是ViewResolver技术。
ViewResolver的定义如下:
public interface ViewResolver {
View resolveViewName(String viewName, Locale locale) throws Exception;
}
在[spring-dispatcher-name]-servlet.xml中,可以定义viewResolver:
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/jsp/"/>
<property
name="suffix" value=".jsp"/>
</bean>
来让DispacherServlet进行加载默认的viewResolver,如果没有设置viewResolver,spring使用InternalResourceViewResolver进行解析。
Spring实现ViewResolver的非抽象类且我们经常使用的viewResolver有以下四种:
InternalResourceViewResolver |
将逻辑视图名字解析为一个路径 |
BeanNameViewResolver |
将逻辑视图名字解析为bean的Name属性,从而根据name属性,找定义View的bean |
ResourceBundleResolver |
和BeanNameViewResolver一样,只不过定义的view-bean都在一个properties文件中,用这个类进行加载这个properties文件 |
XmlViewResolver |
和ResourceBundleResolver一样,只不过定义的view-bean在一个xml文件中,用这个类来加载xml文件 |
使用多视图解析器:
我们不想只使用一种视图解析器的话,可以在[spring-dispatcher-name]-servlet.xml定义多个viewResolver:
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<bean id=”beanNameViewResolver” class=”...BeanNameViewResolver”>
<property name="order" value="1"></property>
</bean>
<bean id=”beanNameViewResolver” class=”...XmlViewResolver”>
<property name="order" value="0"></property>
</bean>
DispatcherServlet会加载所有的viewResolver到一个list中,并按照优先级进行解析。注意order中的值越小,优先级越高。而id为viewResolver
的viewResolver的优先级是最低的。
以前,我们详细介绍了Spring的Controller技术。Spring的面向接口编程,使Controller的实现多种多样。View技术也一样。今天的分析先从在Controller中的ModelAndView开始。 public class ModelAndView {
private Object view; //View实例或者view的字符串
/** Model Map */
private ModelMap model; //model
/* * Convenient constructor when there is no model data to expose. * Can also be used in conjunction with <code>addObject</code>.
* @param view View object to render
* @see #addObject */
public ModelAndView(View view) {
this.view = view;
}
public ModelAndView(String viewName){
this.view = viewName;
}
可以看到view实例可以指向一个View对象或者字符串。现在先看看View接口: public interface View {
/**
* Return the content type of the view, if predetermined.
* <p>Can be used to check the content type upfront,
* before the actual rendering process.
* @return the content type String (optionally including a character set),
* or <code>null</code> if not predetermined.
*/
String getContentType();
/**
* 绘制视图 * 绘制视图的第一步是准备请求: 如果是JSP的视图技术 * 首先会把model设为request的属性。 * 第二步则是真正的绘制视图 * @param model Map with name Strings as keys and corresponding model
* objects as values (Map can also be <code>null</code> in case of empty model)
* @param request current HTTP request
* @param response HTTP response we are building
* @throws Exception if rendering failed
*/
void render(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception;
}
在这之后,我们再来看看View的实现继承类图,可以看到Spring支持多种类型视图:
org.springframework.web.servlet.view.AbstractView (implements org.springframework.beans.factory.BeanNameAware, org.springframework.web.servlet.View)
- org.springframework.web.servlet.view.document.AbstractExcelView
- org.springframework.web.servlet.view.document.AbstractJExcelView
- org.springframework.web.servlet.view.document.AbstractPdfView
- org.springframework.web.servlet.view.AbstractUrlBasedView (implements org.springframework.beans.factory.InitializingBean)
- org.springframework.web.servlet.view.jasperreports.AbstractJasperReportsView
- org.springframework.web.servlet.view.jasperreports.AbstractJasperReportsSingleFormatView
- org.springframework.web.servlet.view.jasperreports.ConfigurableJasperReportsView
- org.springframework.web.servlet.view.jasperreports.JasperReportsCsvView
- org.springframework.web.servlet.view.jasperreports.JasperReportsHtmlView
- org.springframework.web.servlet.view.jasperreports.JasperReportsPdfView
- org.springframework.web.servlet.view.jasperreports.JasperReportsXlsView
- org.springframework.web.servlet.view.jasperreports.JasperReportsMultiFormatView
- org.springframework.web.servlet.view.document.AbstractPdfStamperView
- org.springframework.web.servlet.view.AbstractTemplateView
- org.springframework.web.servlet.view.freemarker.FreeMarkerView
- org.springframework.web.servlet.view.velocity.VelocityView
- org.springframework.web.servlet.view.velocity.VelocityToolboxView
- org.springframework.web.servlet.view.velocity.VelocityLayoutView
- org.springframework.web.servlet.view.InternalResourceView
- org.springframework.web.servlet.view.JstlView
- org.springframework.web.servlet.view.tiles.TilesView
- org.springframework.web.servlet.view.tiles.TilesJstlView
- org.springframework.web.servlet.view.RedirectView
- org.springframework.web.servlet.view.tiles2.TilesView
- org.springframework.web.servlet.view.xslt.XsltView
- org.springframework.web.servlet.view.xslt.AbstractXsltView
和Controller一样,View的第一个实现也是AbstractView。所以先让我们看看AbstractView对render函数的实现:
public void render(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception { if (logger.isTraceEnabled()) { logger.trace("Rendering view with name '" + this.beanName + "' with model " + model + " and static attributes " + this.staticAttributes); }
// Consolidate static and dynamic model attributes. Map mergedModel = new HashMap(this.staticAttributes.size() + (model != null ? model.size() : 0)); mergedModel.putAll(this.staticAttributes); if (model != null) { mergedModel.putAll(model); }
// Expose RequestContext? if (this.requestContextAttribute != null) { mergedModel.put(this.requestContextAttribute, createRequestContext(request, mergedModel)); }
prepareResponse(request, response); renderMergedOutputModel(mergedModel, request, response); }
第一步,将静态属性和model的属性都添加到mergedModel里面。如果需要请求上下文,则将请求上下文添加到model中。
静态属性是继承AbstractView进行设置的属性。而请求上下文如果设置的名字就会创建一个request上下文。在requestContext中定义了一些包括本地化和主题的处理工具。
第二步,对响应进行预处理。最后调用子类需要实现的函数renderMergedOutputModel。
对PDF和EXCEL格式我们暂且不管,且Spring支持多种视图技术,这里我们主要关注JSTL技术,
接着我们来看AbstractUrlBasedView 类。在AbstractUrlBasedView 只定义了一个url属性。别的没有什么特殊处理。
接着继承AbstractUrlBasedView 的是InternalResourceView。他对renderMergedOutputModel进行实现,实现如下:
/** * Render the internal resource given the specified model. * This includes setting the model as request attributes. */ protected void renderMergedOutputModel( Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {
// 获取要暴露的request,一般都是传入的参数request HttpServletRequest requestToExpose = getRequestToExpose(request);
// 将model的数据添加到request属性中
exposeModelAsRequestAttributes(model, requestToExpose);
// 设置helper,如果存在的话 exposeHelpers(requestToExpose);
// 对绘制进行预处理,从而获得到要分发的url String dispatcherPath = prepareForRendering(requestToExpose, response);
// 获取请求分发对象 RequestDispatcher rd = requestToExpose.getRequestDispatcher(dispatcherPath); if (rd == null) { throw new ServletException( "Could not get RequestDispatcher for [" + getUrl() + "]: check that this file exists within your WAR"); }
// 决定使用RequestDispatcher的include方法还是forward方法 if (useInclude(requestToExpose, response)) { response.setContentType(getContentType()); if (logger.isDebugEnabled()) { logger.debug("Including resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'"); } rd.include(requestToExpose, response); }
else { // Note: The forwarded resource is supposed to determine the content type itself. exposeForwardRequestAttributes(requestToExpose); if (logger.isDebugEnabled()) { logger.debug("Forwarding to resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'"); } rd.forward(requestToExpose, response); } }
可以看到InternalResourceView对请求进行了转发。转发到url上。最后我们看看JSTLView的实现:
public class JstlView extends InternalResourceView {
private MessageSource messageSource;
public JstlView() {
}
public JstlView(String url) {
super(url);
}
public JstlView(String url, MessageSource messageSource) {
this(url);
this.messageSource = messageSource;
}
protected void initServletContext(ServletContext servletContext) {
if (this.messageSource != null) {
this.messageSource = JstlUtils.getJstlAwareMessageSource(servletContext, this.messageSource);
}
super.initServletContext(servletContext);
}
protected void exposeHelpers(HttpServletRequest request) throws Exception {
if (this.messageSource != null) {
JstlUtils.exposeLocalizationContext(request, this.messageSource);
}
else {
JstlUtils.exposeLocalizationContext(new RequestContext(request, getServletContext()));
}
}
}
在InternalResourceView 中,基本上所有的处理都差不多了。在JSTLView对两个方法进行了覆盖。第一个initServletContext,主要初始化了MessageResource
第二个exposeHelpers将messageSource放在了request里面。
这样view的解析就结束了。接下来容器对jsp进行解析,并进行tag等的处理。然后将生成的页面返回给客户端。
org.springframework.web.servlet.mvc.AbstractController (implements org.springframework.web.servlet.mvc.Controller) Spring MVC框架中的Controller对请求进行处理:所有的Controller都实现接口Controller: public interface Controller {
/**
* Process the request and return a ModelAndView object which the DispatcherServlet
* will render. A <code>null</code> return value is not an error: It indicates that
* this object completed request processing itself, thus there is no ModelAndView
* to render.
* @param request current HTTP request
* @param response current HTTP response
* @return a ModelAndView to render, or <code>null</code> if handled directly
* @throws Exception in case of errors
*/
ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;
}
上面的doc表明Controller返回的modelandview可以使空,表明请求都是该函数中处理完成了,不需要modeland来进行渲染。 在继续之前先介绍一个有用的工具类:WebUtils。用这个可以简化session,request的处理。具体的内容可以参考文档。 Controller的第一个实现是:AbstractController。他是一个Abstract类,除了实现了Controller接口,它还继承了WebContentGenerator。 WebContentGenerator的作用是什么?参考文档可以发现,该类主要对Cache和Session进行管理。
cacheSeconds |
指定内容缓存的时间,默认为1 |
requireSession |
是否需要会话,默认支持 |
supportedMethods |
支持的方法,默认是GET\post\Head |
useCacheControlHeader |
指定是否使用http1.1的cache控制头信息,默认使用 |
useCacheControlNoStore |
指定是否设置http1.1的cache控制头信息为no-store。默认使用 |
useExpiresHeader |
指定是否使用http1.0的expire头信息。默认使用 | 用户可以对这些参数进行测试,cache和expire信息涉及到了http协议信息,更多信息可以参考http协议文档。这里不再说明。 再看AbstractController的代码:
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
// Delegate to WebContentGenerator for checking and preparing. checkAndPrepare(request, response, this instanceof LastModified);
// Execute handleRequestInternal in synchronized block if required. if (this.synchronizeOnSession) { HttpSession session = request.getSession(false); if (session != null) { Object mutex = WebUtils.getSessionMutex(session); synchronized (mutex) { return handleRequestInternal(request, response); } } } return handleRequestInternal(request, response); } checkandPrepare的目的就是使用用于进行的配置来对request进行预处理和准备。
他会检查支持的方法,和会话,然后应用cache设置。
如果需要session同步,就进行同步处理。session同步应用于有session的情况下。如果没有session,session同步是没有用的。
AbstractController会调用handleRequestInternal方法进行处理,继承AbstractController的类需要实现该方法。
下面我们再看看AbstractUrlViewController 的代码实现和文档,先看handleRequestInternal的实现:
/** * Retrieves the URL path to use for lookup and delegates to * {@link #getViewNameForRequest}. */ protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) { String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); String viewName = getViewNameForRequest(request); if (logger.isDebugEnabled()) { logger.debug("Returning view name '" + viewName + "' for lookup path [" + lookupPath + "]"); } return new ModelAndView(viewName); }
可以看到,它使用了getViewNameForRequest获取需要的viewName。而getViewNameForRequest是一个抽象函数,需要子类实现。lookupPath就是我们请求的URL中的一部分。如我们使用UrlFilenameViewController来进行如下的配置:
<bean name="/index.do" class="org.springframework.web.servlet.mvc.UrlFilenameViewController"></bean>、
09-11-25 11:56:06 - DEBUG [http-8200-1] - Returning view name 'index' for lookup path [/index.do]
该Controller对/index.do解析成index,然后再通过viewResolver对index进行扩展为/jsp/index.jsp。从而找到该页面。
可以看到这个类的主要是用于对url进行解析,然后转到合适的页面上,而在转到这个页面之前不需要进行特别的处理。
明白了该类的作用自然也就知道了UrlFilenameViewController的作用。这里不再进行详细分析。
在看完BaseCommandController和AbstractCommandController之后,我们再看BaseCommandController的另一个实现AbstractFormController,以及AbstractFormController的具体实现SimpleFormController。
先看看AbstractFormController对handleRequestInternal的实现:
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
throws Exception {
// Form submission or new form to show?
if (isFormSubmission(request)) {
// Fetch form object from HTTP session, bind, validate, process submission.
try {
Object command = getCommand(request);
ServletRequestDataBinder binder = bindAndValidate(request, command);
BindException errors = new BindException(binder.getBindingResult());
return processFormSubmission(request, response, command, errors);
}
catch (HttpSessionRequiredException ex) {
// Cannot submit a session form if no form object is in the session.
if (logger.isDebugEnabled()) {
logger.debug("Invalid submit detected: " + ex.getMessage());
}
return handleInvalidSubmit(request, response);
}
}
else {
// New form to show: render form view.
return showNewForm(request, response);
}
}
这个方法,首先判断是不是Form提交,判断方法是:
protected boolean isFormSubmission(HttpServletRequest request) {
return "POST".equals(request.getMethod());
}
如果是form提交的话,系统首先创建一个Command,然后对数据进行绑定和验证,之后调用processFormSubmission方法。showNewForm则调用showForm。
在AbstractFormController中里面有两个抽象方法:
protected abstract ModelAndView processFormSubmission(
HttpServletRequest request, HttpServletResponse response, Object command, BindException errors)
throws Exception;
protected abstract ModelAndView showForm(
HttpServletRequest request, HttpServletResponse response, BindException errors)
throws Exception;
好了,看完AbstractFormController之后,再看看SimpleFormController是如何实现:
protected ModelAndView processFormSubmission(
HttpServletRequest request, HttpServletResponse response, Object command, BindException errors)
throws Exception {
if (errors.hasErrors()) {
if (logger.isDebugEnabled()) {
logger.debug("Data binding errors: " + errors.getErrorCount());
}
return showForm(request, response, errors);
}
else if (isFormChangeRequest(request, command)) {
logger.debug("Detected form change request -> routing request to onFormChange");
onFormChange(request, response, command, errors);
return showForm(request, response, errors);
}
else {
logger.debug("No errors -> processing submit");
return onSubmit(request, response, command, errors);
}
}
在上面的方法中,如果有错误,调用showForm,来显示form。没有错误的话,则调用onSubmit方法。
protected final ModelAndView showForm(
HttpServletRequest request, BindException errors, String viewName, Map controlModel)
throws Exception {
// In session form mode, re-expose form object as HTTP session attribute.
// Re-binding is necessary for proper state handling in a cluster,
// to notify other nodes of changes in the form object.
if (isSessionForm()) {
String formAttrName = getFormSessionAttributeName(request);
if (logger.isDebugEnabled()) {
logger.debug("Setting form session attribute [" + formAttrName + "] to: " + errors.getTarget());
}
request.getSession().setAttribute(formAttrName, errors.getTarget());
}
// Fetch errors model as starting point, containing form object under
// "commandName", and corresponding Errors instance under internal key.
Map model = errors.getModel();
// Merge reference data into model, if any.
Map referenceData = referenceData(request, errors.getTarget(), errors);
if (referenceData != null) {
model.putAll(referenceData);
}
// Merge control attributes into model, if any.
if (controlModel != null) {
model.putAll(controlModel);
}
// Trigger rendering of the specified view, using the final model.
return new ModelAndView(viewName, model);
}
在showForm中,设置属性,放在model中,然后在viewName进行设置。
FormController就是上面的过程。具体的执行过程和详细信息会在以后的博客中具体介绍。
Spring的BaseCommandController继承自AbstractController。在看BaseCommandController之前先看他的继承类AbstractCommandController是如何实现
AbstractController的handleInternalRequest方法的:
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
throws Exception {
Object command = getCommand(request);
ServletRequestDataBinder binder = bindAndValidate(request, command);
BindException errors = new BindException(binder.getBindingResult());
return handle(request, response, command, errors);
}
getCommand就是BaseCommandController中的方法。
protected Object getCommand(HttpServletRequest request) throws Exception {
return createCommand();
}
protected final Object createCommand() throws Exception {
if (this.commandClass == null) {
throw new IllegalStateException("Cannot create command without commandClass being set - " +
"either set commandClass or (in a form controller) override formBackingObject");
}
if (logger.isDebugEnabled()) {
logger.debug("Creating new command of class [" + this.commandClass.getName() + "]");
}
return BeanUtils.instantiateClass(this.commandClass);
}
createCommand创建了一个CommandClass的对象。
然后再看bindAndValidate方法:
protected final ServletRequestDataBinder bindAndValidate(HttpServletRequest request, Object command)
throws Exception {
ServletRequestDataBinder binder = createBinder(request, command);
BindException errors = new BindException(binder.getBindingResult());
if (!suppressBinding(request)) {
binder.bind(request);
onBind(request, command, errors);
if (this.validators != null && isValidateOnBinding() && !suppressValidation(request, command, errors)) {
for (int i = 0; i < this.validators.length; i++) {
ValidationUtils.invokeValidator(this.validators[i], command, errors);
}
}
onBindAndValidate(request, command, errors);
}
return binder;
}
这个方法首先创建了
DataBinder对象,然后,获取创建绑定对象时发生的错误。报错在errors。接下来绑定对象,调用onBind处理绑定事件;接下来应用Validator。然后调用onBindAndValidate来处理绑定和验证事件。最后返回binder。
处理完之后调用handle方法进行处理。
综上所述,AbstractCommandController具有两个功能:
1、将请求参数转换为Command对象。在该Controller中,我们设置一个object对象。然后BaseCommandController将请求的参数进行转换。如果请求参数有value值,就会调用object的的setValue对象来设置对象里的值。如果请求参数中有address.city.就会调用object中getAddress().setCity()方法来赋值。这个object可以是任意的object,唯一的要求就是这个object类没有参数。
2、对数据进行验证。在转换和验证时发生错误时,需要在handle(request, response, command,
errors)中进行处理。
1、Spring web 框架的核心:DispatcherServlet DispatcherServlet 用于接收请求。是使用Spring框架的入口。在web.xml中,需要配置该servlet。在配置该Servlet的时候url-pattern你可以使用你自己想使用的形式,如*.aspx,*.do,*.htm,*.action,用以混淆客户端对服务器架构的认识。 另外,该Servlet在容器中还会加载一个APPlicationContext的xml文件。默认加载的是[servlet-name]-servlet.xml。例如,你在web.xml中配置的servlet如下: <web-app>
<servlet>
<servlet-name>example</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>example</servlet-name>
<url-pattern>*.form</url-pattern>
</servlet-mapping>
</web-app>
该Servlet就会在服务器启动时,加载example-servlet.xml。当然,你也可以自己来指定加载文件。 要看看DispatcherServlet真面目,打开源文件,发现定义了很多BeanName的常量,如本地化解析器beanname,主题解析器beanname,视图解析器beanname,上传文件解析的multipart解析器beanname。 等: public static final String MULTIPART_RESOLVER_BEAN_NAME = "multipartResolver";
public static final String LOCALE_RESOLVER_BEAN_NAME = "localeResolver";
public static final String THEME_RESOLVER_BEAN_NAME = "themeResolver";
public static final String HANDLER_MAPPING_BEAN_NAME = "handlerMapping";
public static final String HANDLER_ADAPTER_BEAN_NAME = "handlerAdapter";
public static final String HANDLER_EXCEPTION_RESOLVER_BEAN_NAME = "handlerExceptionResolver";
public static final String REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME = "viewNameTranslator";
public static final String VIEW_RESOLVER_BEAN_NAME = "viewResolver";
这个类怎么使用这些bean呢?以上面的exexample-servlet.xml为例,我们定义的名字为example的DispatcherServlet使用example-servlet.xml的配置。在example-servlet.xml里,我们可以配置名字上面的beanName的bean,来让servlet加载这些bean。 这下明白了,servlet.xml该怎么配置,该配置什么了。 好了,看完了最重要的servlet的配置问题,我们再看下一个重要的接口:Controller。至于上面servlet要使用的什么什么解析器啦,我们稍后在分析。 2、Controller 我们的请求提交到DispacherServlet后,会转给Controller。怎么找Controller?通过使用handlerMapping。如果没有设置handlerMapping,spring使用默认的BeanNameUrlHandlerMapping来找Controller。 BeanNameUrlHandlerMapping?顾名思义,就是通过bean的name属性来映射controller。bean的name请求是一个url,如果请求的是logout.do,在example-servlet.xml中定义一个名字(name)为login.do的Controller.
<bean name="/logout.do" class="com.jy.bookshop.web.spring.LogoutController"> </bean>
再插一句话,在handlerMapping中,我们可以使用请求拦截器来对请求进行拦截处理。该拦截器怎么使用这里暂且不表,有机会再讨论。
ok,现在我们创建一个LogoutController来让他处理请求,让他实现Controller吧: public class LogOutController implements Controller {
public ModelAndView handleRequest(HttpServletRequest req,
HttpServletResponse res) throws Exception {
…
return new ModelAndView(new RedirectView("login.do"));
}
} 看看这个Controller接口的定义,发现这个接口只定义了一个handleRequest方法。在这个方法中,返回一个ModelAndView。 先说ModelAndView。我们知道MVC,那么ModelAndView就是 MV了。Controller就是C。这样MVC全了。呵呵。 继续说ModelAndView,要了解他的结构,那自然要看看他的源代码了: /** View instance or view name String */
private Object view;
/** Model Map */
private ModelMap model;
只关注我们关注的,里面包含了一个View对象和model对象。model对象是一个Map,这里不再说了。关键看看view,奇怪,怎么是一个Object,太抽象了。再继续看源代码的话,会更加明白:
public ModelAndView(String viewName, String modelName, Object modelObject) {
this.view = viewName;
addObject(modelName, modelObject);
}
public void setViewName(String viewName) {
this.view = viewName;
}
原来这个view可以指向一个View对象,也可以指向String对象啊。View一个接口,如果看doc的话,他的实现类有AbstractExcelView, AbstractJasperReportsSingleFormatView, AbstractJasperReportsView, AbstractJExcelView, AbstractPdfStamperView, AbstractPdfView,AbstractTemplateView, AbstractUrlBasedView, AbstractView, AbstractXsltView, ConfigurableJasperReportsView, FreeMarkerView, InternalResourceView,JasperReportsCsvView, JasperReportsHtmlView, JasperReportsMultiFormatView, JasperReportsPdfView, JasperReportsXlsView, JstlView, RedirectView,TilesJstlView, TilesView, TilesView, VelocityLayoutView, VelocityToolboxView, VelocityView, XsltView(诚实的说,这些View是拷doc的)。
现在可以知道,我们的Controller返回一个View和Model,来让Spring框架来创建一个回应。
现在奇怪的还有一点,我吧view设置为字符串,Spring框架怎么处理?在ModelAndView中,如果view是一个字符串,则会将这个值交给DispatcherServlet的viewResovler来处理。记得上面提到的viewResovler了吗?呵呵,派上用场了。
view的问题解决了,然后再说model吧。在ModelAndView添加了一些对象,Spring是怎么处理的呢?总应该把这些对象给弄到request对象里,让jsp页面来使用吧。让View使用?那么看看View接口吧: public interface View {
String getContentType();
void render(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception;
} render函数需要带一个model变量。再找找view的实现类,看看是怎么工作的。不过view有那么多,对于一些像什么pdf啦,excel了他们都不需要在request中添加这个model。 最终呢,我们在jstlView的父类InternalResourceView中的renderMergedOutputModel函数发现他把model放在了request里面了。 OK,现在我们明白了,controller返回的modelandview交给Servlet进行处理,来生成一个页面。 最简单的Controller介绍完毕。现在看看Spring提供的一些controller的实现,Spring提供了很多controller的实现,继承的结构如下:
org.springframework.web.servlet.mvc.AbstractController (implements org.springframework.web.servlet.mvc.Controller)
- org.springframework.web.servlet.mvc.AbstractUrlViewController
- org.springframework.web.servlet.mvc.UrlFilenameViewController
- org.springframework.web.servlet.mvc.BaseCommandController
- org.springframework.web.servlet.mvc.AbstractCommandController
- org.springframework.web.servlet.mvc.AbstractFormController
- org.springframework.web.servlet.mvc.AbstractWizardFormController
- org.springframework.web.servlet.mvc.SimpleFormController
- org.springframework.web.servlet.mvc.CancellableFormController
- org.springframework.web.servlet.mvc.ParameterizableViewController
- org.springframework.web.servlet.mvc.ServletForwardingController (implements org.springframework.beans.factory.BeanNameAware)
- org.springframework.web.servlet.mvc.ServletWrappingController (implements org.springframework.beans.factory.BeanNameAware, org.springframework.beans.factory.DisposableBean, org.springframework.beans.factory.InitializingBean)
AbstractController是Controller的第一个实现。其他的Controller都是继承这个Controller的。我们先看比较重要的Controller。 先说UrlFilenameViewController。不如我们自己来部署一个吧。 在example-servlet.xml中增加如下的配置:
<bean name="/error.do" class="org.springframework.web.servlet.mvc.UrlFilenameViewController"> </bean> OK,当/error.do的请求上来后,urlFileNameViewController将他变成/error。然后返回个View,这个view的name就是/error,然后使用viewresolver来进行解析,可以解析成/jsp/error.jsp。 另外还有比较重要的controller是baseCommandController,将请求参数转换为一个对象,并对对象参数合法性进行验证。另外,SimpleFormController可以对表单进行处理。 关于各个controller的分析。未完待续。。
原文:http://www.blogjava.net/orangewhy/archive/2007/06/26/126371.html
java.beans.PropertyEditor的从字义来看是一个属性编辑器,但总觉得它的作用更像一个转换器--从字符串转换为类对象的属性。
java.beans.PropertyEditor接口定义的方法有好几个,但是最重要为下面两个:
void setValue(Object value)
void setAsText(String text) throws java.lang.IllegalArgumentException;
一般地,我们要使用PropertyEditor时,并不直接实现此接口,而是通过继承实现此接口的java.beans.PropertyEditorSupport来简化我们的工作,在子类覆盖setAsText方法就可以了,setValue方法一般不直接使用,在setAsText方法中将字符串进行转换并产生目标对象以后,由调setAsText调用setValue来把目标对象注入到编辑器中。当然,你可用覆盖更多的方法来满足你的特殊要求。JavaBean的类和接口,被大部分spring包使用,可以从spring中学习更成熟的JavaBean使用方法。
简单的例子:
实体类Person:
public class Person
{
private String name;
private String sex;
private int age;
public Person(String name, String sex, int age)
{
this.name = name;
this.sex = sex;
this.age = age;
}
public int getAge()
{
return age;
}
public void setAge(int age)
{
this.age = age;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public String getSex()
{
return sex;
}
public void setSex(String sex)
{
this.sex = sex;
}
@Override
public String toString()
{
return "Person["+name+", "+sex+", "+age+"]";
}
}
Person的属性编辑器:
public class PersonPropertyEditor extends PropertyEditorSupport
{
public void setAsText(String text)
{
setValue(parseString(text));
}
private Object parseString(String text)
{
String[] parts = tokenizeToStringArray(text, ", ", false, false);
String name = (parts.length > 0 ? parts[0] : "undefine");
String sex = (parts.length > 1 ? parts[1] : "undefine");
int age = (parts.length > 2 ? Integer.valueOf(parts[2]) : 0);
return (text.length() > 0 ? new Person(name, sex, age) : null);
}
private String[] tokenizeToStringArray(String str, String delimiters, boolean trimTokens,
boolean ignoreEmptyTokens)
{
StringTokenizer st = new StringTokenizer(str, delimiters);
List tokens = new ArrayList();
while(st.hasMoreTokens())
{
String token = st.nextToken();
if(trimTokens)
{
token = token.trim();
}
if(!ignoreEmptyTokens || token.length() > 0)
{
tokens.add(token);
}
}
return toStringArray(tokens);
}
private String[] toStringArray(Collection collection)
{
if(collection == null)
{
return null;
}
return (String[])collection.toArray(new String[collection.size()]);
}
}
测试代码:
public static void main(String[] args)
{
PersonPropertyEditor editor = new PersonPropertyEditor();
editor.setAsText("aSam,man,22");
System.out.println(editor.getValue());
}
结果输出:
Person[aSam, man, 22]
以下Tip和学习路线从自己身上出发进行总结,仅代表个人观点。你可以留言进行讨论。
1.有计划的学习
学习是一个循序渐进的过程。如果没有一个计划,学习将变得没有规律,我们也无法提高自己的能力。想起上学的时候,学校每个学期都会制定一个教学大纲来指导老师的教学和我们的学习。是的,如果没有计划,今天突然想学这个,明天突然想学那个,朝三暮四,我们永远也无法学到自己想学的东西。所以我们需要制定一个学习计划。有计划的学习才能提高自己的能力。Java web项目的开发是需要很多知识的积累的,包括Java SE,数据库,JDBC,Linux,Log4j,Html/CSS/Javascript,持久层框架,JUNIT及其他测试框架,IOC框架,web MVC框架等等,如果我们没有一个良好的计划,今天学习Log4j,明天学习Junit,这些东西都不会掌握好并学习好。
如果给自己做计划。计划可以按照时间段来进行。例如本年度的工作,本季度要达到的水平,本月要学习的东西,本周学习的计划安排,以及每一天的安排。每天晚上睡觉前,想想今天的计划安排是否完成,明天该学习什么;每周到结束的时候,总结一下本周完成了什么,下周要学习什么。根据自己对计划的实行情况可以改变自己的计划。总之要有计划的学习。可以使用google 日历和 qq mail 邮箱等来管理自己的计划。
2. 同一段时间只学习一种技术
我是一个什么都想学的人。我不想把自己的时间都用在学习Java上,我还想学习C++,还想学习 web 设计,还想学好windows编程,想学Linux编程,想学习计算机网络编程,想学习路由器、网络的配置……。于是,今天看了VC++深入详解,明天学习Linux shell编程。计算机技术包含了太多技术。我们无法一一将他们都掌握。所以不要想什么都学会。至少在一段时间内学习一种技术。我给自己制定了这样的计划,今年要把所有的精力都致力为 java EE 开发技术上。一年后,努力学习C/C++编程。
是的。我们学习的东西可以广一点,但一定要有自己专的方面。学专了一个方面,你就可以接着学习其他的技术。一个什么都会的人,很可能什么都不会;所以,精于一,而博于广。
3.学会休息
我们都很忙,上学的时候学好各科,至少不能挂科,然后在课外学习自己喜欢的java 编程;工作的时候,需要做好工作,然后在工作之余多学一些东西;时间长了,我们就可能倦了,累了。所以我们需要学会休息来改变自己的精神状态。
整天在电脑前进行软件开发的我们,要学会放松自己和休息。作为程序员整天在电脑前,极容易养成工作和休息都离不开电脑的习惯。休息的时候,也是在电脑前看电影,玩游戏。我想,在我们工作累了之后,应该离开电脑,走向户外来放松和休息。或到大街上转转,或到商场里购物,或去游泳馆游泳,或去健身房健身,或和朋友一起打台球。等等等等。总之要学会放松自己,走出户外,不要整天在电脑前。
以上3点是自己对自己工作学习的总结和提醒,特别写出来和大家一起分享。
感谢!分享自己的观点。值得学习。
提高开发技术->如何学习,这个转换并不完全对等。对于学习来说,最重要的不是计划和过程,而是结果,没有成果的学习等于白费时间。对于提高技术来说,必须要有笑傲江湖唯我独尊的气势,以及持之以恒的定力。
|
感谢CoderCream分享自己的观点,没错,执行和结果更加重要!
1、Interface Comparable<T> 只有实现该接口的对象的列表或数组才能调用Collections.sort()方法。 在实现 int compareTo(T o)时,需要注意: 1、如果两个对象相等,返回为0; 2、如果同一个null对象进行比较,应抛出NullPointerException。 3、实现必须保证sgn(x.compareTo(y)) == -sgn(y.compareTo(x))、(x.compareTo(y)==0) == (x.equals(y)) 、(x.compareTo(y)>0 && y.compareTo(z)>0) impliesx.compareTo(z)>0 。如果 x.compareTo(y)抛出异常,y.compareTo(x)也必须抛出异常。 2、Interface Iterable<T> Iterator<T> iterator() 对于链表等对象应实现该接口来允许一个对象可以使用foreach语句。
上面的方法返回java.util.Interface Iterator<E>,该接口的主要方法有: hasNext();next();remove(); 3、Interface Readable java.lang.Interface Readable 一个Readable 是一个字符串的来源。实现这个接口需要实现的方法是: int read(CharBuffer cb)
4、 java.lang Interface Runnable 不用说,这个谁都知道。如果想在一个单独的线程中执行,就需要实现这个接口。 5、java.lang Interface Thread.UncaughtExceptionHandler 从名字就可以判断出来,当线程抛出未捕获的异常时,实现这个接口的类的对象可以对一场进行处理。 官方文档:当线程被一个未捕获的异常打断时,这个接口被调用。 当线程要被为捕获异常打断是,JVM使用Thread.getUncaughtExceptionHandler(),查询异常处理器,如果线程没有这个接口的设置,则查询该线程的ThreadGroup的UncaughtExceptionHandler,如果县城没有处理异常,他就会抛出这个异常。 void uncaughtException(Thread t, Throwable e) Method invoked when the given thread terminates due to the given uncaught exception.
6、包装型对象:Boolean Byte Character Double Float Long Short Integer 这些类就不用了说了,主要会使用里面的静态方法和一些常量就可以了。 7、Class Character.Subset 这个类的实例代表了Unicode字符集的特殊的子集。定义在Character中的唯一子集族类是UnicodeBlock.其他的Java API或许因为自己的用户定义了其他的子集。 static Character.UnicodeBlock
AEGEAN_NUMBERS Constant for the "Aegean Numbers" Unicode character block.
static Character.UnicodeBlock
ALPHABETIC_PRESENTATION_FORMS Constant for the "Alphabetic Presentation Forms" Unicode character block.
static Character.UnicodeBlock
ARABIC Constant for the "Arabic" Unicode character block.
static Character.UnicodeBlock
ARABIC_PRESENTATION_FORMS_A Constant for the "Arabic Presentation Forms-A" Unicode character block.
static Character.UnicodeBlock
ARABIC_PRESENTATION_FORMS_B Constant for the "Arabic Presentation Forms-B" Unicode character block.
static Character.UnicodeBlock
ARMENIAN Constant for the "Armenian" Unicode character block.
static Character.UnicodeBlock
ARROWS Constant for the "Arrows" Unicode character block.
static Character.UnicodeBlock
BASIC_LATIN Constant for the "Basic Latin" Unicode character block.
static Character.UnicodeBlock
BENGALI Constant for the "Bengali" Unicode character block.
static Character.UnicodeBlock
BLOCK_ELEMENTS Constant for the "Block Elements" Unicode character block.
static Character.UnicodeBlock
BOPOMOFO Constant for the "Bopomofo" Unicode character block.
static Character.UnicodeBlock
BOPOMOFO_EXTENDED Constant for the "Bopomofo Extended" Unicode character block.
static Character.UnicodeBlock
BOX_DRAWING Constant for the "Box Drawing" Unicode character block.
………… 具体参见java.lang Class Character.UnicodeBlock里面的定义。 8 、java.langClass Class<T> 这个类的实力代表了Java运行程序中的类和接口。Enum是类,而Annotation是一个接口。Every array also belongs to a class that is reflected as a Class object that is shared by all arrays with the same element type and number of dimensions. Class的对象在程序中可以获取类的详细信息。 9、Java.lang.Class ClassLoader ClassLoader是个不错的东西,下面是官方文档的简单翻译和注解: 1、ClassLoader用于加载类对象。ClassLoader是一个抽象类。给出类的二进制名字(如“ "java.lang.String"
"javax.swing.JSpinner$DefaultEditor"
"java.security.KeyStore$Builder$FileBuilder$1"
"java.net.URLClassLoader$3$1"
”),ClassLoader会使用定位和生成类。一个典型的策略就是将二进制名字转化为文件名,然后从文件系统中读取这个类文件。
每一个Class对象都包含了一个创建它的引用。
数组的Class对象不能由ClassLoader创建,但是可以由Java运行时动态创建。一个数组类的ClassLoader,和他的元素的ClassLoader是一样的;如果元素是基本类型,则数组类没有ClassLoader。
应用程序可以实现ClassLoader的子类,来扩展行为。这样可以在JVM动态的创建类。
ClassLoader主要由安全管理器来使用,用于保证安全区域。
ClassLoader 使用一个delegation(委托)模型来搜索类和资源。每一个ClassLoader有一个相关的父类ClassLoader。当请求来查找一个资源或者类的时候,ClassLoader 实例会委托搜索类和资源。
内建的ClassLoader,叫做bootstrap class loader,没有父类。
正常的,ClassLoader从本地文件系统中加载数据。通过CLassPath。
当然,也可以通过NetWork从服务器上下载字节码。来加载类: ClassLoader loader = new NetworkClassLoader(host, port);
Object main = loader.loadClass("Main", true).newInstance(); Network ClassLoader 子类必须定义方法FindClass 和loadClassData来加载来自互联网上的类。一旦或得到字节码,它使用defineClass方法来创建类实例。 class NetworkClassLoader extends ClassLoader {
String host;
int port;
public Class findClass(String name) {
byte[] b = loadClassData(name);
return defineClass(name, b, 0, b.length);
}
private byte[] loadClassData(String name) {
// load the class data from the connection . . .
}
}
个人理解:
ClassLoader是一个类加载器,除了可以从ClassPath加载类之外,还可以从ClassPath中加载资源:
InputStream
getResourceAsStream(String name) Returns an input stream for reading the specified resource.
Enumeration<URL>
getResources(String name) Finds all the resources with the given name.
static URL
getSystemResource(String name) Find a resource of the specified name from the search path used to load classes.
static InputStream
getSystemResourceAsStream(String name) Open for reading, a resource of the specified name from the search path used to load classes.
static Enumeration<URL>
getSystemResources(String name) Finds all resources of the specified name from the search path used to load classes.
protected Class<?>
findClass(String name) Finds the class with the specified binary name.
10、Compiler类3
编译类是提供给支持Java到本地代码编译器和相关服务。根据设计,编译器类什么都不做;它作为一个占位符来为运行时编译执行的技术。
当JVM第一次启动时,他判断java.compiler是否存在。如果存在,他3
1、background相关: 属性 描述 background 简写属性,作用是将背景属性设置在一个声明中。 background-attachment 背景图像是否固定或者随着页面的其余部分滚动。 background-color 设置元素的背景颜色。 background-image 把图像设置为背景。 background-position 设置背景图像的起始位置。 background-repeat 设置背景图像是否及如何重复。 (1)background background-color: 值 描述 color_name 规定颜色值为颜色名称的背景颜色(比如 red)。 hex_number 规定颜色值为十六进制值的背景颜色(比如 #ff0000)。 rgb_number 规定颜色值为 rgb 代码的背景颜色(比如 rgb(255,0,0))。 transparent 默认。背景颜色为透明。 inherit 规定应该从父元素继承 background-color 属性的设置。 background-image : 值 描述 url('URL') 指向图像的路径。 none 默认值。不显示背景图像。 inherit 规定应该从父元素继承 background-image 属性的设置。 background-repeat : repeat | 默认。背景图像将在垂直方向和水平方向重复。 | repeat-x | 背景图像将在水平方向重复。 | repeat-y | 背景图像将在垂直方向重复。 | no-repeat | 背景图像将仅显示一次。 | background-position :center top buttom right left XX% XX% 值 描述 - top left
- top center
- top right
- center left
- center center
- center right
- bottom left
- bottom center
- bottom right
如果您仅规定了一个关键词,那么第二个值将是"center"。 默认值:0% 0%。 x% y% 第一个值是水平位置,第二个值是垂直位置。 左上角是 0% 0%。右下角是 100% 100%。 如果您仅规定了一个值,另一个值将是 50%。 xpos ypos 第一个值是水平位置,第二个值是垂直位置。 左上角是 0 0。单位是像素 (0px 0px) 或任何其他的 CSS 单位。 如果您仅规定了一个值,另一个值将是50%。 您可以混合使用 % 和 position 值。 background-attachment: fixed 2、文本相关 text-indent :缩进元素中的首行文本。 值 描述 length 定义固定的缩进。默认值:0。 % 定义基于父元素宽度的百分比的缩进。 text-align: 文本对应方式 您可能会认为 text-align:center 与 <CENTER> 元素的作用一样,但实际上二者大不相同。 <CENTER> 不仅影响文本,还会把整个元素居中。text-align 不会控制元素的对齐,而只影响内部内容。元素本身不会从一段移到另一端,只是其中的文本受影响。 值 描述 left 把文本排列到左边。默认值:由浏览器决定。 right 把文本排列到右边。 center 把文本排列到中间。 justify 实现两端对齐文本效果。 word-spacing :以改变字(单词)之间的标准间隔 值 描述 normal 默认。定义单词间的标准空间。 length 定义单词间的固定空间。 letter-spacing:改变字母之间的距离 值 描述 normal 默认。定义字符间的标准空间。 length 定义字符间的固定空间。 text-transform:处理文本的大小写 值 描述 none 默认。定义带有小写字母和大写字母的标准的文本。 capitalize 文本中的每个单词以大写字母开头。 uppercase 定义仅有大写字母。 lowercase 定义无大写字母,仅有小写字母。 text-decoration : 值 描述 none 默认。定义标准的文本。 underline 定义文本下的一条线。 overline 定义文本上的一条线。 line-through 定义穿过文本下的一条线。 blink 定义闪烁的文本(无法运行在 IE 和 Opera 中)。 white-space : 值 描述 normal 默认。空白会被浏览器忽略。 pre 空白会被浏览器保留。其行为方式类似 HTML 中的 <pre> 标签。 nowrap 文本不会换行,文本会在在同一行上继续,直到遇到 <br> 标签为止。 pre-wrap 保留空白符序列,但是正常地进行换行。 pre-line 合并空白符序列,但是保留换行符。 值 | 空白 | 换行 | 自动换行 | pre-line | 合并 | 保留 | 允许 | normal | 合并 | 忽略 | 允许 | nowrap | 合并 | 忽略 | 不允许 | pre | 保留 | 保留 | 不允许 | pre-wrap | 保留
| 保留 | 允许 | direction :文本的方向属性 ltr 默认。文本方向从左到右。 rtl 文本方向从右到左。 属性 描述 color 设置文本颜色 direction 设置文本方向。 line-height 设置行高。 letter-spacing 设置字符间距。 text-align 对齐元素中的文本。 text-decoration 向文本添加修饰。 text-indent 缩进元素中文本的首行。 text-shadow 设置文本阴影。CSS2 包含该属性,但是 CSS2.1 没有保留该属性。 text-transform 控制元素中的字母。 unicode-bidi 设置文本方向。 white-space 设置元素中空白的处理方式。 word-spacing 设置字间距。 3、CSS字体相关 font 简写属性。作用是把所有针对字体的属性设置在一个声明中。 font-family 设置字体系列。 font-size 设置字体的尺寸。 font-size-adjust 当首选字体不可用时,对替换字体进行智能缩放。(CSS2.1 已删除该属性。) font-stretch 对字体进行水平拉伸。(CSS2.1 已删除该属性。) font-style 设置字体风格。 font-variant 以小型大写字体或者正常字体显示文本。 font-weight 设置字体的粗细。 CSS 列表属性(list) 属性 描述 list-style 简写属性。用于把所有用于列表的属性设置于一个声明中。 list-style-image 将图象设置为列表项标志。 - url
图像的路径。 - none
默认。无图形被显示。 list-style-position 设置列表中列表项标志的位置。 - inside
列表项目标记放置在文本以内,且环绕文本根据标记对齐。 - outside
默认。保持标记位于文本的左侧。列表项目标记放置在文本以外,且环绕文本不根据标记对齐。 list-style-type 设置列表项标志的类型。 - none
无标记。 - disc
默认。标记是实心圆。 - circle
标记是空心圆。 - square
标记是实心方块。 - decimal
标记是数字。 - decimal-leading-zero
0开头的数字标记。(01, 02, 03, 等。) - lower-roman
小写罗马数字(i, ii, iii, iv, v, 等。) - upper-roman
大写罗马数字(I, II, III, IV, V, 等。) - lower-alpha
小写英文字母The marker is lower-alpha (a, b, c, d, e, 等。) - upper-alpha
大写英文字母The marker is upper-alpha (A, B, C, D, E, 等。) - lower-greek
小写希腊字母(alpha, beta, gamma, 等。) - lower-latin
小写拉丁字母(a, b, c, d, e, 等。) - upper-latin
大写拉丁字母(A, B, C, D, E, 等。) - hebrew
传统的希伯来编号方式 - armenian
传统的亚美尼亚编号方式 - georgian
传统的乔治亚编号方式(an, ban, gan, 等。) - cjk-ideographic
简单的表意数字 - hiragana
标记是:a, i, u, e, o, ka, ki, 等。(日文片假名) - katakana
标记是:A, I, U, E, O, KA, KI, 等。(日文片假名) - hiragana-iroha
标记是:i, ro, ha, ni, ho, he, to, 等。(日文片假名) - katakana-iroha
标记是:I, RO, HA, NI, HO, HE, TO, 等。(日文片假名) marker-offset CSS Table 属性 CSS 表格属性允许你设置表格的布局。(请注意,本节介绍的不是如何使用表来建立布局,而是要介绍 CSS 中表本身如何布局。) 属性 描述 border-collapse 设置是否把表格边框合并为单一的边框。 border-spacing 设置分隔单元格边框的距离。(仅用于 "separated borders" 模型) caption-side 设置表格标题的位置。 empty-cells 设置是否显示表格中的空单元格。(仅用于 "separated borders" 模型) table-layout 设置显示单元、行和列的算法。 CSS 边框属性 "CSS" 列中的数字指示哪个 CSS 版本定义了该属性。 轮廓(outline)是绘制于元素周围的一条线,位于边框边缘的外围,可起到突出元素的作用。 CSS outline 属性规定元素轮廓的样式、颜色和宽度。 outline 在一个声明中设置所有的轮廓属性。 2 outline-color 设置轮廓的颜色。 2 outline-style 设置轮廓的样式。 2 outline-width 设置轮廓的宽度。 2 CSS 框模型概述 CSS 框模型 (Box Model) 规定了元素框处理元素内容、内边距、边框 和 外边距 的方式。 - element : 元素。
- padding : 内边距,也有资料将其翻译为填充。
- border : 边框。
- margin : 外边距,也有资料将其翻译为空白或空白边。
CSS 内边距属性 属性 描述 padding 简写属性。作用是在一个声明中设置元素的所内边距属性。 padding-bottom 设置元素的下内边距。 padding-left 设置元素的左内边距。 padding-right 设置元素的右内边距。 padding-top 设置元素的上内边距。 CSS 边框属性 属性 描述 border 简写属性,用于把针对四个边的属性设置在一个声明。 border-style 用于设置元素所有边框的样式,或者单独地为各边设置边框样式。 - none
定义无边框。 - hidden
与 "none" 相同。不过应用于表时除外,对于表,hidden 用于解决边框冲突。 - dotted
定义点状边框。在大多数浏览器中呈现为实线。 - dashed
定义虚线。在大多数浏览器中呈现为实线。 - solid
定义实线。 - double
定义双线。双线的宽度等于 border-width 的值。 - groove
定义 3D 凹槽边框。其效果取决于 border-color 的值。 - ridge
定义 3D 垄状边框。其效果取决于 border-color 的值。 - inset
定义 3D inset 边框。其效果取决于 border-color 的值。 - outset
定义 3D outset 边框。其效果取决于 border-color 的值。 border-width 简写属性,用于为元素的所有边框设置宽度,或者单独地为各边边框设置宽度。 - thin
定义细的边框。 - medium
默认。定义中等的边框。 - thick
定义粗的边框。 - length
允许您自定义边框的宽度。 border-color 简写属性,设置元素的所有边框中可见部分的颜色,或为 4 个边分别设置颜色。 border-bottom 简写属性,用于把下边框的所有属性设置到一个声明中。 border-bottom-color 设置元素的下边框的颜色。 border-bottom-style 设置元素的下边框的样式。 border-bottom-width 设置元素的下边框的宽度。 border-left 简写属性,用于把左边框的所有属性设置到一个声明中。 border-left-color 设置元素的左边框的颜色。 border-left-style 设置元素的左边框的样式。 border-left-width 设置元素的左边框的宽度。 border-right 简写属性,用于把右边框的所有属性设置到一个声明中。 border-right-color 设置元素的右边框的颜色。 border-right-style 设置元素的右边框的样式。 border-right-width 设置元素的右边框的宽度。 border-top 简写属性,用于把上边框的所有属性设置到一个声明中。 border-top-color 设置元素的上边框的颜色。 border-top-style 设置元素的上边框的样式。 border-top-width 设置元素的上边框的宽度。 CSS 外边距属性 属性 描述 margin 简写属性。在一个声明中设置所有外边距属性。 margin-bottom 设置元素的下外边距。 margin-left 设置元素的左外边距。 margin-right 设置元素的右外边距。 margin-top 设置元素的上外边距。 CSS 定位属性 CSS 定位属性允许你对元素进行定位。 详细参见http://www.w3school.com.cn/css/css_positioning.asp CSS 定位和浮动 CSS 为定位和浮动提供了一些属性,利用这些属性,可以建立列式布局,将布局的一部分与另一部分重叠,还可以完成多年来通常需要使用多个表格才能完成的任务。 定位的基本思想很简单,它允许你定义元素框相对于其正常位置应该出现的位置,或者相对于父元素、另一个元素甚至浏览器窗口本身的位置。显然,这个功能非常强大,也很让人吃惊。要知道,用户代理对 CSS2 中定位的支持远胜于对其它方面的支持,对此不应感到奇怪。 另一方面,CSS1 中首次提出了浮动,它以 Netscape 在 Web 发展初期增加的一个功能为基础。浮动不完全是定位,不过,它当然也不是正常流布局。我们会在后面的章节中明确浮动的含义。 一切皆为框 div、h1 或 p 元素常常被称为块级元素。这意味着这些元素显示为一块内容,即“块框”。与之相反,span 和 strong 等元素称为“行内元素”,这是因为它们的内容显示在行中,即“行内框”。 您可以使用 display 属性改变生成的框的类型。这意味着,通过将 display 属性设置为 block,可以让行内元素(比如 <a> 元素)表现得像块级元素一样。还可以通过把 display 设置为 none,让生成的元素根本没有框。这样的话,该框及其所有内容就不再显示,不占用文档中的空间。 但是在一种情况下,即使没有进行显式定义,也会创建块级元素。这种情况发生在把一些文本添加到一个块级元素(比如 div)的开头。即使没有把这些文本定义为段落,它也会被当作段落对待: <div>
some text
<p>Some more text.</p>
</div>
在这种情况下,这个框称为无名块框,因为它不与专门定义的元素相关联。
块级元素的文本行也会发生类似的情况。假设有一个包含三行文本的段落。每行文本形成一个无名框。无法直接对无名块或行框应用样式,因为没有可以应用样式的地方(注意,行框和行内框是两个概念)。但是,这有助于理解在屏幕上看到的所有东西都形成某种框。
CSS 定位机制
CSS 有三种基本的定位机制:普通流、浮动和绝对定位。
除非专门指定,否则所有框都在普通流中定位。也就是说,普通流中的元素的位置由元素在 X(HTML) 中的位置决定。
块级框从上到下一个接一个地排列,框之间的垂直距离是由框的垂直外边距计算出来。
行内框在一行中水平布置。可以使用水平内边距、边框和外边距调整它们的间距。但是,垂直内边距、边框和外边距不影响行内框的高度。由一行形成的水平框称为行框(Line Box),行框的高度总是足以容纳它包含的所有行内框。不过,设置行高可以增加这个框的高度。
在下面的章节,我们会为您详细讲解相对定位、绝对定位和浮动。
CSS position 属性
通过使用 position 属性,我们可以选择 4 中不同类型的定位,这会影响元素框生成的方式。
position 属性值的含义:
- static
- 元素框正常生成。块级元素生成一个矩形框,作为文档流的一部分,行内元素则会创建一个或多个行框,置于其父元素中。
- relative
- 元素框偏移某个距离。元素仍保持其未定位前的形状,它原本所占的空间仍保留。
- absolute
- 元素框从文档流完全删除,并相对于其包含块定位。包含块可能是文档中的另一个元素或者是初始包含块。元素原先在正常文档流中所占的空间会关闭,就好像元素原来不存在一样。元素定位后生成一个块级框,而不论原来它在正常流中生成何种类型的框。
- fixed
- 元素框的表现类似于将 position 设置为 absolute,不过其包含块是视窗本身。
提示:相对定位实际上被看作普通流定位模型的一部分,因为元素的位置相对于它在普通流中的位置。
属性 描述
position 把元素放置到一个静态的、相对的、绝对的、或固定的位置中。
- static
默认。位置设置为 static 的元素,它始终会处于页面流给予的位置(static 元素会忽略任何 top、bottom、left 或 right 声明)。
- relative
位置被设置为 relative 的元素,可将其移至相对于其正常位置的地方,因此 "left:20" 将向元素的 LEFT 位置添加 20 个像素。
- absolute
位置设置为 absolute 的元素,可定位于相对于包含它的元素的指定坐标。此元素的位置可通过 "left"、"top"、"right" 以及"bottom" 属性来规定。
- fixed
位置被设置为 fixed 的元素,可定位于相对于浏览器窗口的指定坐标。此元素的位置可通过 "left"、"top"、"right" 以及"bottom" 属性来规定。不论窗口滚动与否,元素都会留在那个位置。工作于 IE7(strict 模式)。
top 定义了一个定位元素的上外边距边界与其包含块上边界之间的偏移。
- auto
默认。通过浏览器来计算顶部的位置。
- %
设置元素的顶部到最近一个具有定位设置父元素的上边缘的百分比位置。
- length
使用 px、cm 等单位设置元素的顶部到最近一个具有定位设置上边缘的顶部的位置。可使用负值。
right 定义了定位元素右外边距边界与其包含块右边界之间的偏移。
bottom 定义了定位元素下外边距边界与其包含块下边界之间的偏移。
left 定义了定位元素左外边距边界与其包含块左边界之间的偏移。
overflow 设置当元素的内容溢出其区域时发生的事情。
- visible
默认。内容不会被修剪,会呈现在元素之外。
- hidden
内容会被修剪,但是浏览器不会显示供查看内容的滚动条。
- scroll
内容会被修剪,但是浏览器会显示滚动条以便查看其余的内容。
- auto
如果内容被修剪,则浏览器会显示滚动条以便查看其余的内容。
clip 设置元素的形状。元素被剪入这个形状之中,然后显示出来。
- shape
设置元素的形状。合法的形状值是:rect (top, right, bottom, left)
- auto
默认。浏览器可设置元素的形状。
vertical-align 设置元素的垂直对齐方式。
- baseline
默认。元素放置在父元素的基线上。
- sub
垂直对齐文本的下标。
- super
垂直对齐文本的上标
- top
把元素的顶端与行中最高元素的顶端对齐
- text-top
把元素的顶端与父元素字体的顶端对齐
- middle
把此元素放置在父元素的中部。
- bottom
把元素的顶端与行中最低的元素的顶端对齐。
- text-bottom
把元素的底端与父元素字体的底端对齐。
- length
- %
使用 "line-height" 属性的百分比值来排列此元素。允许使用负值。
z-index 设置元素的堆叠顺序。
- auto
默认。堆叠顺序与父元素相等。
- number
设置元素的堆叠顺序。
CSS 尺寸属性
CSS 尺寸属性允许你控制元素的高度和宽度。同样,还允许你增加行间距。
属性 描述
height 设置元素的高度。
line-height 设置行高。
max-height 设置元素的最大高度。
max-width 设置元素的最大宽度。
min-height 设置元素的最小高度。
min-width 设置元素的最小宽度。
width 设置元素的宽度。
CSS 分类属性 (Classification)
CSS 分类属性允许你控制如何显示元素,设置图像显示于另一元素中的何处,相对于其正常位置来定位元素,使用绝对值来定位元素,以及元素的可见度。
属性 描述
clear 设置一个元素的侧面是否允许其他的浮动元素。
cursor 规定当指向某元素之上时显示的指针类型。
display 设置是否及如何显示元素。
float 定义元素在哪个方向浮动。
position 把元素放置到一个静态的、相对的、绝对的、或固定的位置中。
visibility 设置元素是否可见或不可见。
伪类
浏览器支持:IE Internet Explorer, F: Firefox, N: Netscape。
W3C:“W3C” 列的数字显示出伪类属性由哪个 CSS 标准定义(CSS1 还是 CSS2)。
伪类 作用 IE F N W3C
:active 将样式添加到被激活的元素 4 1 8 1
:focus 将样式添加到被选中的元素 - - - 2
:hover 当鼠标悬浮在元素上方时,向元素添加样式 4 1 7 1
:link 将特殊的样式添加到未被访问过的链接 3 1 4 1
:visited 将特殊的样式添加到被访问过的链接 3 1 4 1
:first-child 将特殊的样式添加到元素的第一个子元素 1 7 2
:lang 允许创作者来定义指定的元素中使用的语言 1 8 2
伪元素 作用 IE F N W3C
:first-letter 将特殊的样式添加到文本的首字母 5 1 8 1
:first-line 将特殊的样式添加到文本的首行 5 1 8 1
:before 在某元素之前插入某些内容 1.5 8 2
:after 在某元素之后插入某些内容 1.5 8 2
不同的媒介类型
注释:媒介类型名称对大小写不敏感。
浏览器支持:IE: Internet Explorer, F: Firefox, N: Netscape。
W3C:“W3C” 列的数字显示出属性背景由哪个 CSS 标准定义(CSS1 还是 CSS2)。
媒介类型 描述
all 用于所有的媒介设备。
aural 用于语音和音频合成器。
braille 用于盲人用点字法触觉回馈设备。
embossed 用于分页的盲人用点字法打印机。
handheld 用于小的手持的设备。
print 用于打印机。
projection 用于方案展示,比如幻灯片。
screen 用于电脑显示器。
tty 用于使用固定密度字母栅格的媒介,比如电传打字机和终端。
tv 用于电视机类型的设备。
1、可滚动的结果集(Scrollable Result Sets) (1)创建可滚动的结果集: Statement stmt = con.createStatement(
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY); ResultSet.TYPE_FORWARD_ONLY:结果集是不能滚动的;他的游标只能向前移动; ResultSet.TYPE_SCROLL_INSENSITIVE :结果是可滚动的;游标可以向前也可以后退。也可以移动到一个绝对位置。
ResultSet. TYPE_SCROLL_SENSITIVE:结果是可滚动的;游标可以向前也可以后退。也可以移动到一个绝对位置。
ResultSet.CONCUR_READ_ONLY:结果集是只读的。 ResultSet.ResultSet.CONCUR_UPDATABLE :结果集是可以更新的。
ResultSet srs = stmt.executeQuery("SELECT COF_NAME,
PRICE FROM COFFEES");
尽管我们可以在这里设置创建的是可滚动结果集,但是如果厂商的JDBC实现不支持,我们获取到的结果将不具有可滚动属性。 可以使用ResultSet.getType()方法来获取是否支持滚动: int type = rs.getType();
The variable type will be one of the following:
1003 to indicate ResultSet.TYPE_FORWARD_ONLY
1004 to indicate ResultSet.TYPE_SCROLL_INSENSITIVE
1005 to indicate ResultSet.TYPE_SCROLL_SENSITIVE
TYPE_SCROLL_INSENSITIVE和TYPE_SCROLL_SENSITIVE的主要区别是在如果发生改变他们的敏感度。前一个将不会很敏感后一个则会。
(2)移动游标,使用以下方法可以移动游标:
rs.next();
rs.previous();
rs.absolute();
rs.relative();
rs.first();
rs.last();
rs.beforeFirst();
rs.afterLast();
使用rs.getRow()获取游标当前行。
rs.isAfterLast(); rs.isBeforeFirst(); rs.isLast(); rs.isFirst(); rs.hasNext(); 等等方法。 2、更新结果集 (1)创建可以更新的结果集 Statement stmt = con.createStatement(
ResultSet.TYPE_SCROLL_SENSITIVE,
ResultSet.CONCUR_UPDATABLE);
ResultSet uprs = stmt.executeQuery(
"SELECT COF_NAME,
PRICE FROM COFFEES");
在JDBC 2.0中,我们可以向可以更新的结果集中插入行或删除行,或者修改其中的行。
下面的方法用于判断结果集是否可以更新: int concurrency = uprs.getConcurrency();
The variable concurrency will be one of the following:
1007 to indicate ResultSet.CONCUR_READ_ONLY
1008 to indicate ResultSet.CONCUR_UPDATABLE
(2)更新结果集 JDBC 1.0中可以这样更新: stmt.executeUpdate(
"UPDATE COFFEES SET PRICE = 10.99 " +
"WHERE COF_NAME = 'French_Roast_Decaf'"); 在JDBC2.0中。则可以: uprs.last(); uprs.updateFloat("PRICE", 10.99f); uprs.updateRow(); 在移动游标前,必须先调用updateRow方法。否则更新信息会丢失。调用cancelRowUpdates可以取消对行的更新。 (3)向结果集中插入或者删除行 Connection con = DriverManager.getConnection(
"jdbc:mySubprotocol:mySubName");
Statement stmt = con.createStatement(
ResultSet.TYPE_SCROLL_SENSITIVE,
ResultSet.CONCUR_UPDATABLE);
ResultSet uprs = stmt.executeQuery(
"SELECT * FROM COFFEES"); uprs.moveToInsertRow();
uprs.updateString("COF_NAME", "Kona");
uprs.updateInt("SUP_ID", 150);
uprs.updateFloat("PRICE", 10.99f);
uprs.updateInt("SALES", 0);
uprs.updateInt("TOTAL", 0);
uprs.insertRow(); 在移动游标前,必须要先调用insertRow否则插入的信息将丢失。 uprs.absolute(4);
uprs.deleteRow(); 删除行。 (4)查看结果集中的变化(其实就是说了一个意思,用TYPE_SCROLL_SENSITIVE 对数据很敏感,一旦数据变化就会反映在ResultSet中)
Result sets vary greatly in their ability to reflect changes made in their underlying data. If you modify data in a ResultSet object, the change will always be visible if you close it and then reopen it during a transaction. In other words, if you re-execute the same query after changes have been made, you will produce a new result set based on the new data in the target table. This new result set will naturally reflect changes you made earlier. You will also see changes made by others when you reopen a result set if your transaction isolation level makes them visible.
So when can you see visible changes you or others made while the ResultSet object is still open? (Generally, you will be most interested in the changes made by others because you know what changes you made yourself.) The answer depends on the type of ResultSet object you have.
With a ResultSet object that is TYPE_SCROLL_SENSITIVE , you can always see visible updates made to existing column values. You may see inserted and deleted rows, but the only way to be sure is to use DatabaseMetaData methods that return this information. ("New JDBC 2.0 Core API Features" on page 371 explains how to ascertain the visibility of changes.)
You can, to some extent, regulate what changes are visible by raising or lowering the transaction isolation level for your connection with the database. For example, the following line of code, wherecon is an active Connection object, sets the connection's isolation level to TRANSACTION_READ_COMMITTED : con.setTransactionIsolation(
Connection.TRANSACTION_READ_COMMITTED);
With this isolation level, a TYPE_SCROLL_SENSITIVE result set will not show any changes before they are committed, but it can show changes that may have other consistency problems. To allow fewer data inconsistencies, you could raise the transaction isolation level to TRANSACTION_REPEATABLE_READ . The problem is that, in most cases, the higher the isolation level, the poorer the performance is likely to be. And, as is always true of JDBC drivers, you are limited to the levels your driver actually provides. Many programmers find that the best choice is generally to use their database's default transaction isolation level. You can get the default with the following line of code, where con is a newly-created connection: int level = con.getTransactionIsolation();
The explanation of Connection fields, beginning on page 347, gives the transaction isolation levels and their meanings.
If you want more information about the visibility of changes and transaction isolation levels, see "What Is Visible to Transactions" on page 597.
In a ResultSet object that is TYPE_SCROLL_INSENSITIVE , you cannot see changes made to it by others while it is still open, but you may be able to see your own changes with some implementations. This is the type of ResultSet object to use if you want a consistent view of data and do not want to see changes made by others.
昨天晚上看了关于“都市信息网”项目开发视频,给人总体感觉差强人意,学到了一些知识,记录如下: 1、页面结构: 在页面结构的定义上,将页面分成多部分,例如页头,页尾,左侧栏和右主栏。在每个栏中导入需要的JSP文件。 2、关于controller: 要让controller实现RequestAware和ResponseAware。然后使用继承。 3、关于DAO层的设计 遗憾的是都市信息在DAO层的设计上,是一个败笔。在controller里面使用sql语句。不利于各个层次的独立。 4、关于TreeMap 在该项目中,使用map多使用TreeMap,查一下TreeMap是什么东西吧: A Red-Black tree based NavigableMap implementation. The map is sorted according to the natural ordering of its keys, or by aComparator provided at map creation time, depending on which constructor is used. 哦,是一个基于红黑树的Map。 什么是红黑树。 红黑树(Red-Black Tree)是二叉搜索树(Binary Search Tree)的一种改进。我们知道二叉搜索树在最坏的情况下可能会变成一个链表(当所有节点按从小到大的顺序依次插入后)。而红黑树在每一次插入或删除节点之后都会花O(log N)的时间来对树的结构作修改,以保持树的平衡。也就是说,红黑树的查找方法与二叉搜索树完全一样;插入和删除节点的的方法前半部分节与二叉搜索树完全一样,而后半部分添加了一些修改树的结构的操作。 红黑树的每个节点上的属性除了有一个key、3个指针:parent、lchild、rchild以外,还多了一个属性:color。它只能是两种颜色:红或黑。而红黑树除了具有二叉搜索树的所有性质之外,还具有以下4点性质: 1. 根节点是黑色的。 2. 空节点是黑色的(红黑树中,根节点的parent以及所有叶节点lchild、rchild都不指向NULL,而是指向一个定义好的空节点)。 3. 红色节点的父、左子、右子节点都是黑色。 4. 在任何一棵子树中,每一条从根节点向下走到空节点的路径上包含的黑色节点数量都相同。 有了这几条规则,就可以保证整棵树的平衡,也就等于保证了搜索的时间为O(log N)。 但是在插入、删除节点后,就有可能破坏了红黑树的性质。所以我们要做一些操作来把整棵树修补好。下面我就来介绍一下。
|