|
2008年9月13日
什么話該說,什么話不該說,該說的話該怎么說。誰能告訴我。
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.tkk7.com/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 設置是否把表格邊框合并為單一的邊框。 caption-side 設置表格標題的位置。 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)。 但是在插入、刪除節點后,就有可能破壞了紅黑樹的性質。所以我們要做一些操作來把整棵樹修補好。下面我就來介紹一下。
你時常被客戶抱怨JSP頁面響應速度很慢嗎?你想過當客戶訪問次數劇增時,你的WEB應用能承受日益增加的訪問量嗎?本文講述了調整JSP和servlet的一些非常實用的方法,它可使你的servlet和JSP頁面響應更快,擴展性更強。而且在用戶數增加的情況下,系統負載會呈現出平滑上長的趨勢。在本文中,我將通過一些實際例子和配置方法使得你的應用程序的性能有出人意料的提升。其中,某些調優技術是在你的編程工作中實現的。而另一些技術是與應用服務器的配置相關的。在本文中,我們將詳細地描述怎樣通過調整servlet和JSP頁面,來提高你的應用程序的總體性能。在閱讀本文之前,假設你有基本的servlet和JSP的知識。
方法一:在servlet的init()方法中緩存數據
當應用服務器初始化servlet實例之后,為客戶端請求提供服務之前,它會調用這個servlet的init()方法。在一個servlet的生命周期中,init()方法只會被調用一次。通過在init()方法中緩存一些靜態的數據或完成一些只需要執行一次的、耗時的操作,就可大大地提高系統性能。
例如,通過在init()方法中建立一個JDBC連接池是一個最佳例子,假設我們是用jdbc2.0的DataSource接口來取得數據庫連接,在通常的情況下,我們需要通過JNDI來取得具體的數據源。我們可以想象在一個具體的應用中,如果每次SQL請求都要執行一次JNDI查詢的話,那系統性能將會急劇下降。解決方法是如下代碼,它通過緩存DataSource,使得下一次SQL調用時仍然可以繼續利用它:
<table borderColor=#ffcc66 width="90%" align=center bgColor=#e6e4dd border=1>
<tr>
<td>public class ControllerServlet extends HttpServlet
{
private javax.sql.DataSource testDS = null;
public void init(ServletConfig config) throws ServletException
{
super.init(config);
Context ctx = null;
try
{
ctx = new InitialContext();
testDS = (javax.sql.DataSource)ctx.lookup("jdbc/testDS");
}
catch(NamingException ne)
{
ne.printStackTrace();
}
catch(Exception e)
{
e.printStackTrace();
}
}
public javax.sql.DataSource getTestDS()
{
return testDS;
}
...
...
}</td></tr></table>
方法 2:禁止servlet和JSP 自動重載(auto-reloading)
Servlet/JSP提供了一個實用的技術,即自動重載技術,它為開發人員提供了一個好的開發環境,當你改變servlet和JSP頁面后而不必重啟應用服務器。然而,這種技術在產品運行階段對系統的資源是一個極大的損耗,因為它會給JSP引擎的類裝載器(classloader)帶來極大的負擔。因此關閉自動重載功能對系統性能的提升是一個極大的幫助。
方法 3: 不要濫用HttpSession
在很多應用中,我們的程序需要保持客戶端的狀態,以便頁面之間可以相互聯系。但不幸的是由于HTTP具有天生無狀態性,從而無法保存客戶端的狀態。因此一般的應用服務器都提供了session來保存客戶的狀態。在JSP應用服務器中,是通過HttpSession對像來實現session的功能的,但在方便的同時,它也給系統帶來了不小的負擔。因為每當你獲得或更新session時,系統者要對它進行費時的序列化操作。你可以通過對HttpSession的以下幾種處理方式來提升系統的性能:
· 如果沒有必要,就應該關閉JSP頁面中對HttpSession的缺省設置: 如果你沒有明確指定的話,每個JSP頁面都會缺省地創建一個HttpSession。如果你的JSP中不需要使用session的話,那可以通過如下的JSP頁面指示符來禁止它:
<table borderColor=#ffcc66 width="90%" align=center bgColor=#e6e4dd border=1>
<tr>
<td><%@ page session="false"%> </td></tr></table>
· 不要在HttpSession中存放大的數據對像:如果你在HttpSession中存放大的數據對像的話,每當對它進行讀寫時,應用服務器都將對其進行序列化,從而增加了系統的額外負擔。你在HttpSession中存放的數據對像越大,那系統的性能就下降得越快。
· 當你不需要HttpSession時,盡快地釋放它:當你不再需要session時,你可以通過調用HttpSession.invalidate()方法來釋放它。
· 盡量將session的超時時間設得短一點:在JSP應用服務器中,有一個缺省的session的超時時間。當客戶在這個時間之后沒有進行任何操作的話,系統會將相關的session自動從內存中釋放。超時時間設得越大,系統的性能就會越低,因此最好的方法就是盡量使得它的值保持在一個較低的水平。
方法 4: 將頁面輸出進行壓縮
壓縮是解決數據冗余的一個好的方法,特別是在網絡帶寬不夠發達的今天。有的瀏覽器支持gzip(GNU zip)進行來對HTML文件進行壓縮,這種方法可以戲劇性地減少HTML文件的下載時間。因此,如果你將servlet或JSP頁面生成的HTML頁面進行壓縮的話,那用戶就會覺得頁面瀏覽速度會非常快。但不幸的是,不是所有的瀏覽器都支持gzip壓縮,但你可以通過在你的程序中檢查客戶的瀏覽器是否支持它。下面就是關于這種方法實現的一個代碼片段:
<table borderColor=#ffcc66 width="90%" align=center bgColor=#e6e4dd border=1>
<tr>
<td>public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
OutputStream out = null
String encoding = request.getHeader("Accept-Encoding");
if (encoding != null && encoding.indexOf("gzip") != -1)
{
request.setHeader("Content-Encoding" , "gzip");
out = new GZIPOutputStream(request.getOutputStream());
}
else if (encoding != null && encoding.indexOf("compress") != -1)
{
request.setHeader("Content-Encoding" , "compress");
out = new ZIPOutputStream(request.getOutputStream());
}
else
{
out = request.getOutputStream();
}
...
...
} </td></tr></table>
方法 5: 使用線程池
應用服務器缺省地為每個不同的客戶端請求創建一個線程進行處理,并為它們分派service()方法,當service()方法調用完成后,與之相應的線程也隨之撤消。由于創建和撤消線程會耗費一定的系統資源,這種缺省模式降低了系統的性能。但所幸的是我們可以通過創建一個線程池來改變這種狀況。另外,我們還要為這個線程池設置一個最小線程數和一個最大線程數。在應用服務器啟動時,它會創建數量等于最小線程數的一個線程池,當客戶有請求時,相應地從池從取出一個線程來進行處理,當處理完成后,再將線程重新放入到池中。如果池中的線程不夠地話,系統會自動地增加池中線程的數量,但總量不能超過最大線程數。通過使用線程池,當客戶端請求急劇增加時,系統的負載就會呈現的平滑的上升曲線,從而提高的系統的可伸縮性。
方法 6: 選擇正確的頁面包含機制
在JSP中有兩種方法可以用來包含另一個頁面:1、使用include指示符(<%@ includee file=”test.jsp” %>)。2、使用jsp指示符(<jsp:includee page=”test.jsp” flush=”true”/>)。在實際中我發現,如果使用第一種方法的話,可以使得系統性能更高。
方法 7:正確地確定javabean的生命周期
JSP的一個強大的地方就是對javabean的支持。通過在JSP頁面中使用<jsp:useBean>標簽,可以將javabean直接插入到一個JSP頁面中。它的使用方法如下:
<table borderColor=#ffcc66 width="90%" align=center bgColor=#e6e4dd border=1>
<tr>
<td><jsp:useBean id="name" scope="page|request|session|application" class=
"package.className" type="typeName">
</jsp:useBean></td></tr></table>
其中scope屬性指出了這個bean的生命周期。缺省的生命周期為page。如果你沒有正確地選擇bean的生命周期的話,它將影響系統的性能。
舉例來說,如果你只想在一次請求中使用某個bean,但你卻將這個bean的生命周期設置成了session,那當這次請求結束后,這個bean將仍然保留在內存中,除非session超時或用戶關閉瀏覽器。這樣會耗費一定的內存,并無謂的增加了JVM垃圾收集器的工作量。因此為bean設置正確的生命周期,并在bean的使命結束后盡快地清理它們,會使用系統性能有一個提高。
其它一些有用的方法
· 在字符串連接操作中盡量不使用“+”操作符:在java編程中,我們常常使用“+”操作符來將幾個字符串連接起來,但你或許從來沒有想到過它居然會對系統性能造成影響吧?由于字符串是常量,因此JVM會產生一些臨時的對像。你使用的“+”越多,生成的臨時對像就越多,這樣也會給系統性能帶來一些影響。解決的方法是用StringBuffer對像來代替“+”操作符。
· 避免使用System.out.println()方法:由于System.out.println()是一種同步調用,即在調用它時,磁盤I/O操作必須等待它的完成,因此我們要盡量避免對它的調用。但我們在調試程序時它又是一個必不可少的方便工具,為了解決這個矛盾,我建議你最好使用Log4j工具(http://jakarta.apache.org ),它既可以方便調試,而不會產生System.out.println()這樣的方法。
· ServletOutputStream 與 PrintWriter的權衡:使用PrintWriter可能會帶來一些小的開銷,因為它將所有的原始輸出都轉換為字符流來輸出,因此如果使用它來作為頁面輸出的話,系統要負擔一個轉換過程。而使用ServletOutputStream作為頁面輸出的話就不存在一個問題,但它是以二進制進行輸出的。因此在實際應用中要權衡兩者的利弊。
總結
本文的目的是通過對servlet和JSP的一些調優技術來極大地提高你的應用程序的性能,并因此提升整個J2EE應用的性能。通過這些調優技術,你可以發現其實并不是某種技術平臺(比如J2EE和.NET之爭)決定了你的應用程序的性能,重要是你要對這種平臺有一個較為深入的了解,這樣你才能從根本上對自己的應用程序做一個優化!
要過得健康充實,還得靠自立。要學會自立,必須得掌握一些技巧。可能和你在學校里學到的不同,但全才遠遠要比專門只會一樣的人更能夠適應生活。
這里是每個人都該學會的50件事情。(不完全統計)
1. 生火——火能產生光和熱。這兩件是生存的必需品。也許在某個時候,這項技能可以救你的命。
2. 操作電腦——基本的電腦常識是現代生活之必需。必要的時候,請幫助他人。
3。有效使用Google——Google無所不知。如果你用Google找不到你想找的東西,那就是你的問題了。
4。人工呼吸和處理喉嚨被塞住的急救措施——有一天你的妻子、丈夫、兒子或女兒,可能正是需要你幫助的那個人。
5。開手排檔的車——也許有時候你會需要這項技能,要是不會就糟了。
6。簡單地做飯——如果你連雞蛋或牛排都不會煎,那就麻煩了。
7。講一個能吸引人們注意力的故事——如果你不能吸引別人的注意力,那還是別廢話了。
8。不與人打架,除非你能贏——兩種情況下,你都贏了。
9。告訴別人壞消息——總得有人去做這件事。不幸的是,有一天這個重任也會落到你肩上。
10。換輪胎——輪胎里面有氣體,只要有氣體的東西總有一天會爆掉。
11。從容應對面試——要是緊張得渾身是汗,你肯定得不到那份工作。
12。管理好自己的時間——否則你就是在浪費時間。偶爾浪費時間也沒關系,但不能一直這樣。
13。快速閱讀——很多時候你只要讀要點就可以了。(看到這里就覺得沒早點學會這條了吧)
14。記住名字——你會喜歡別人管你叫“喂,那個誰。。。”嗎?
15。整理房間——真正做起來總比想象的要難一點。
16。輕裝上陣——旅行時只帶最必需的東西。這樣更便宜、更簡單、更靈活。
17。和警察打交道——蹲監獄可不是好玩的,監獄里面的大漢就更不用說了。
18。給別人指路——沒人喜歡迷路,所以最好第一次就指對。
19。進行基本的急救——會正確處理傷口,不是只有醫生或天才才能做到的。
20。游泳——地球表面71%的地方都是水,所以學會游泳絕對是個好主意。
21。路邊停車——大多數標準駕照考試都要求考路邊停車,然而大多數人還是不知道該怎么做。很奇怪啊。
22。了解自己的酒量——不然你一不小心就會很難看。
23。選擇好的產品——腐壞的水果和蔬菜只能成為惡作劇的道具用來嚇人。
24。會用錘子、斧子和手鋸——不是只有木匠才用得著這些工具的。每個人都得有點使用這些工具的基本常識。
25。進行簡單的財務規劃——負債累累可不好,一個簡單地預算計劃可以幫你解決這些問題。
26.至少會說兩種通用的語言——世界上只有25%的人說英語。如果能跟剩下75%的人溝通也挺好的。
27.正確地做俯臥撐和仰臥起坐——姿勢不正確的話只會傷害身體,也是浪費時間。
28.多贊美別人——贊美是世上最好的禮物之一,更何況是免費的。
29.談判——再多問一兩個問題就會得到更好的價錢。
30.仔細聽別人說話——多聽少講,學到的多,失去的少。
31.復習基本的地理知識——如果你不知道你生活圈以外的環境,大部分的人只會認為你什么都不知道(而且通常他們是對的)。
32.粉刷房間——粉刷房間的價格百分之九十是人工費用,簡單的油漆活自己做比付給別人9倍的工資要來得劃算。
33.簡單明了又內容豐富的演說——如果下次公司開會時你的上司要你解釋你上個月做了什么活,這樣的演講能讓你過關。“嗯。。。不知道”的話就慘了。
34.照相的時候微笑——硬是不笑的人最糟了!
35.不出丑的搭訕——搭訕成功跟失敗只是一念之間。如果太刻意的話會失敗,不夠用心的話也會失敗。
36.做有用的筆記——因為沒用的筆記和不做筆記一樣都是沒用的。
37.禮待住在自己家里的客人——要不然的話哪天你需要借住他們家的時候就只能睡旅館了。
38.給人好的第一次印象——亞里士多德曾經說過,“好的開始是成功的一半。”
39.會看地圖和使用指南針——如果你的衛星導航器在一個鳥不生蛋的地方壞了,你要怎么辦?
40.縫鈕扣——比買新衣便宜。
41.自己裝家里的視聽系統——又不是多難,付錢給別人裝只是因為偷懶。
42.打字——打字打得好的話會節省很多時間,一生受用。
43.保護私人資料——除非你是那個小偷,不然個人資料被偷可不是好玩的。
44.設定基本的電腦系統防護措施——設定密碼跟防火墻并不需要計算機本科畢業,但可以預防將來倒大霉。
45。識別謊言——人們會對你說謊,此乃人生之一大不幸。
46。有禮貌地結束約會,而不承諾下一次——千萬別給別人一個你不愿給的承諾。你也不用急著決定和一個剛認識沒多久的人有什么未來。
47。除去污漬——總比再買一件便宜。
48。保持房間整潔——整潔的房間是健康、規律生活的開始。
49。正確地抱嬰兒——你可不想無意弄傷一個小寶寶。
50。啟動汽車電瓶——這總比走路或叫拖車好得多。
風雨雷電---MP3MP4專賣;
喜歡聽戴佩妮的那首"小小",歌詞簡單,卻能讓人安靜下來,心里不在浮躁。就像剛才,心里正浮躁的不知道干什么的時候,就聽了那首歌,讓我靜了下來,開始寫我的博客。 關于保研的問題仍然很尷尬,我也不知道自己到底想不想上。想吧,害怕白上了三年,不想吧,可這是個好的機會啊。而且是上學還不掏錢。多好啊。或許自己給自己的要求太高了,我想自己以后畢業一定要去一個大公司。可今天上課的時候,給我們講課的老師,那么厲害都不去那些好的公司,何況是我們這些畢業的社會上一大把的本科生呢。而且,我們的專業和二本學校的專業是一樣的。 就是上面的考慮,讓我猶豫了。我不知道自己究竟作何選擇。我的想法是要上的話就上個好的,比如上北京的學校,多好啊。要不上的話,就好好的努力工作。如果以后有機會的話,再去上。但畢業卻是另一種情況。 現在問題是如果我工作了。我做什么工作,我都不知道呢。我可以做網絡安全,可以做java編程,可以做嵌入式開發等等。但我卻沒有一個確定的目標。目標是什么啊?阿晶。我也不知道。 就拿上研究生,我也不知道自己上什么方向呢。所以很迷茫啊。以前就想著給自己做職業規劃,但從來就沒有規劃好。覺得到時候就知道了,知道怎么選擇了。到現在也不知道該怎么辦。如果有一天,自己真的研究生畢業了,我還是不知道自己要干什么。 語無倫次。我沒有什么特別的特點。所以,這才導致了我不知道作何選擇了。哎,我究竟是什么樣的人。 我真不知道。或許這是最重要的。不如做一個測試吧,看看自己到底適合做什么啊。測試的網站是: http://www.psytopic.com/mag/post/mbti-career-personality-test-psytopic-special-edition.html 測試結果是: Psytopic分析:您的性格類型是“INTP”(內向+直覺+思維+知覺) 對任何感興趣的事物,都要探索一個合理的解釋。喜歡理論和抽象的事情,喜歡理念思維多于社交活動。沉靜,滿足,有彈性,適應力強。在他們感興趣的范疇內,有非凡的能力去專注而深入地解決問題。有懷疑精神,有 時喜歡批判,常常善于分析。 INTP型的人是解決理性問題者。他們很有才智和條理性,以及創造才華的突出表現。INTP型的人外表平靜、緘默、超然,內心卻專心致志于分析問題。他們苛求精細、慣于懷疑。他們努力尋找和利用原則以理解許多想法。 他們喜歡有條理和有目的的交談,而且可能會僅僅為了高興,爭論一些無益而瑣細的問題。只有有條理的推理才會使他們信服。通常INTP型的人是足智多謀、有獨立見解的思考者。他們重視才智,對于個人能力有強烈的欲 望,有能力也很感興趣向他人挑戰。 INTP型的人最主要的興趣在于理解明顯的事物之外的可能性。他們樂于為了改進事物的目前狀況或解決難題而進行思考。他們的思考方式極端復雜,而且他們能很好地組織概念和想法。 偶爾,他們的想法非常復雜,以致于很難向別人表達和被他人理解。 INTP型的人十分獨立,喜歡冒險和富有想象力的活動。他們靈活易變、思維開闊,更感興趣的是發現有創見而且合理的解決方法,而不是僅僅看到成為事 實的解決方式。 您適合的領域有:計算機技術 理論研究、學術領域 專業領域 創造性領域等 您適合的職業有: · 電腦軟件設計師 · 系統分析人員 · 計算機程序員 · 研究開發專業人員 · 數據庫管理 · 故障排除專家 · 戰略規劃師 · 金融規劃師 · 信息服務開發商 · 變革管理顧問 · 企業金融律師 · 大學教授 · 科研機構研究人員 · 數學家 · 物理學家 · 經濟學家 · 考古學家 · 歷史學家 · 證券分析師 · 金融投資顧問 · 律師 · 法律顧問 · 財務專家 · 偵探 · 各類發明家 · 作家 · 設計師 · 音樂家 · 藝術家 · 藝術鑒賞 呵呵,最適合的領域是計算機技術,剛好,我就是學計算機的。通過測試,我知道我該從事做什么了。的確如此。但But,仍然迷茫的是下一步該做什么。繼續先迷茫著把。
中秋時節月兒圓,皎潔的月光,云在月光下顯的那樣潔白和安詳。 最近一直都在考慮關于自己的抉擇。下面是具體的分析: 1、上研究生 上研究生,理論上不錯,但限制是你只能上本校的研究生。 優點如下: 1、能夠獲得碩士學位證書; 2、在一個好的導師的情況下,能夠學到更多的東西; 3、有獎學金上,免費的研究生。 4、研究生畢業后,可以別的學校任教。這個工作也不錯。呵呵; 5、研究生畢業后,可以有好的工作。。。。 缺點可能如下: 1、在上研究生過程中,除了給學校的導師干活之外,沒有其他的工作經驗,因為學校周圍沒有好的實習機會; 2、上完3年,研究生不值錢,還是不好找工作; 3、學校的研究生不好。 總而言之,就是學校的研究生不好。尤其是我們學校的研究生。怕自己的三年什么也沒有學到。
摘要: 在JSP 的各種Web框架中,據我看來Struts框架最為簡單。下面從Struts框架開始來研究JSP的web框架。 1、MVC 在研究這個框架之前,先要對MVC模式有所了解。幸好以前學習過設計模式,對MVC編程和模式了解一些,即將模型、視圖、控制器分開。使三者可以各負其責,處理自己的邏輯。 2、Struts框架的介紹: Struts框架也采用了MVC結構: Model:在Struts框架中,Mo... 閱讀全文
借助Writer的強大的插件功能,可以在博客里插入各種元素,下面我們在博客里添加文件。 是不是很方便?呵呵。。
安裝Writer完畢,發現寫hello,world這樣的源代碼沒有源代碼標簽,查閱其他博客,看到需要增加插件。在右邊的工具欄中有一個添加插件選項,點擊后,彈出網頁。在網頁中找到Insert Code插件。下載安裝,OK,就可以寫如下的hello,world源代碼了: public class HelloWorld{ public static void main(String[] str){ System.out.println("hello,world"); } }
不錯吧,遺憾的是這個插件不支持java源代碼,但C#的語法和Java的差不多。那么選擇語言為C#也可以由java的效果。如上。呵呵。。。
以后就可以方便的寫自己的讀書筆記了。呵呵。。
呵呵,終于可以離線博客了。我裝上了windows live writer之后,費了九牛二虎之力終于可以寫離線博客了。下面把方法貼出來: 在輸入自己的blog的URL、用戶名、密碼之后,要求選擇服務類型。在blogjava中選擇metaweblog API,然后在上傳URL上填寫: http://www.tkk7.com/wangajing/services/metaweblog.aspx 其中,wangajing應換為自己的ID號。 點擊下一步,OK,配置成功了。 下面就開始寫自己的hello,world了。呵呵。。。
|