隨著 Spring Boot 逐步全面覆蓋到我們的項目之中,我們已經基本忘卻當年經典的 Servlet + Spring MVC 的組合,那讓人熟悉的 web.xml 配置。而本文,我們想先拋開 Spring Boot 到一旁,回到從前,一起來看看 Servlet 是怎么和 Spring MVC 集成,怎么來初始化 Spring 容器的。
MVC:Model + View + Controller(數據模型 + 視圖 + 控制器)
三層架構:Presentation tier + Application tier + Data tier(展示層 + 應用層 + 數據訪問層)
MVC和三層架構的關系, MVC只存在三層架構的展示層。
M實際是數據模型,是包含數據的對象。在Spring MVC里,有一個專門的類叫Model,用來和V之間的數據交互、傳值。
V指的是視圖界面,包含JSP、freeMarker、Velocity、Thymeleaf、Tile等。
C就是控制器(Spring MVC的注解@Controller的類)。
三層架構是整個應用的的架構,是由Spring框架負責管理的,一般項目結構中都由Service層、Dao層,這兩個反饋在應用層和數據訪問層。
Spring MVC框架圍繞DispatcherServlet這個核心展開,它負責截獲請求并將其分派給相應的處理器處理。Spring MVC框架包括注解驅動控制器、請求及響應的信息處理、視圖解析、本地化解析、上傳文件解析、異常處理以及表單標簽綁定等內容。
Spring MVC是基于Model 2實現的技術框架。Spring MVC通過一個DispatcherServlet接收所有請求,并將具體工作委托給其他組件進行處理。
可以在web.xml中配置一個Servlet,并通過指定其處理的URL。
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
<!-- (1)從類路徑下加載Spring配置文件-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:application-context.xml</param-value>
</context-param>
<!-- (2)負責啟動 Spring 容器的監聽器,它將引用(1)處的上下文參數獲得Spring配置文件的地址 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- (3)配置DispatcherServlet -->
<servlet>
<servlet-name>web</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- (4)指定處理的URL路徑 -->
<servlet-mapping>
<servlet-name>web</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
復制代碼
可以通過的屬性指定配置參數:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
<context:component-scan base-package="com.ankeetc.web"/>
<!-- 會自動注冊RequestMappingHandlerMapping與RequestMappingHandlerAdapter兩個Bean,這是SpringMVC為@Controllers分發請求所必需的 -->
<!-- 并提供了數據綁定支持、@NumberFormatannotation支持、 @DateTimeFormat支持、@Valid支持、讀寫XML的支持和讀寫JSON的支持等功能。 -->
<mvc:annotation-driven />
</beans>
復制代碼
Spring 4.0已經全面支持Servlet 3.0,可以使用編程的方式配置Servlet容器。在Servlet 3.0環境中,容器會在類路徑中查找實現javax.servlet.ServletContainerInitializer接口的類,如果發現實現類,就會用它來配置Servlet容器。Spring提供了這個接口的實現,名為SpringServletContainerInitializer,這個類反過來又查找實現WebApplicationInitializer的類并將配置的任務交給它們來完成。Spring還提供了一個WebApplicationInitializer基礎實現類AbstractAnnotationConfigDispatcherServletInitializer,使得它在注冊DispatcherServlet時只需要簡單地指定它的Servlet映射即可。
public class WebApplicationInitilalizer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
ServletRegistration.Dynamic registration = servletContext.addServlet("web", new DispatcherServlet());
registration.setLoadOnStartup(1);
registration.addMapping("/");
}
}
復制代碼
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
復制代碼
DispatcherServlet#initStrategies()方法將在WebApplicationContext初始化后執行,此時Spring上下文中的Bean已經初始化完畢,該方法通過反射查找并裝配Spring容器中用戶自定義的Bean,如果找不到就裝配默認的組件實例。
在DispatcherServlet.properties配置文件里邊,指定了DispatcherServlet所使用的默認組件。如果用戶希望采用非默認的組件,只需在Spring配置文件中配置自定義的組件Bean即可。
# 本地化解析器
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
# 主題解析器
org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
# 處理器解析器
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
# 處理器適配器
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
# 異常處理器
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
# 視圖名稱處理器
org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
# 視圖解析器
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
復制代碼
org.springframework.web.context.request
HttpMessageConverter接口可以將請求信息轉換為一個對象(類型為T),并將對象(類型為T)綁定到請求方法的參數中或輸出為響應信息。DispatcherServlet默認已經安裝了RequestMethodHandlerAdapter作為HandlerAdapter組件的實現類,HttpMessageConverter即由RequestMethodHandlerAdapter使用,將請求信息轉換為對象,或將對象轉換為響應信息。
Spring為HttpMessageConverter提供了眾多的實現類:
RequestMappingHandlerAdapter已經默認裝配了以下的HttpMessageConverter:
如果需要裝配其他類型的HttpMessageConverter,可以在Spring的Web容器上下文中自行定義一個RequestMappingHandlerAdapter,注冊若干HttpMessageConverter。如果在Spring web容器中顯式定義了一個RequestMappingHandlerAdapter,則Spring MVC將使用它 覆蓋 默認的RequestMappingHandlerAdapter。
RestTemplate是Spring的模板類,可以使用該類調用Web服務端的服務,它支持Rest風格的URL。
Spring MVC提供了幾個處理XML和JSON格式的請求、響應消息的HttpMessageConverter:
只要在Spring Web容器中為RequestMappingHandlerAdapter裝配好相應的HttpMessageConverter,并在交互中通過請求的Accept指定MIME類型,Spring MVC就可以是服務器端的處理方法和客戶端透明的通過XML或JSON格式進行通信。
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.StringHttpMessageConverter"/>
<bean class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter"/>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
</list>
</property>
</bean>
復制代碼
@RestController已經標注了@ResponseBody和@Controller,可以直接在控制器上標注該注解,就不用在每個@RequestMapping方法上添加@ResponseBody了。
Spring 4.0提供了AsyncRestTemplate用于以異步無阻塞的方式進行服務訪問。
public class WebApplicationInitilalizer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
ServletRegistration.Dynamic registration = servletContext.addServlet("web", new DispatcherServlet());
registration.setLoadOnStartup(1);
// 此處要設置為true
registration.setAsyncSupported(true);
registration.addMapping("/");
}
}
@RestController
public class AsyncController {
@RequestMapping(value = "/async", method = RequestMethod.GET)
public Callable<String> async() {
System.out.println("hello!");
return new Callable<String>() {
@Override
public String call() throws Exception {
TimeUnit.SECONDS.sleep(5);
return "ASYNC";
}
};
}
}
public class Main {
public static void main(String[] args) {
AsyncRestTemplate asyncRestTemplate = new AsyncRestTemplate();
ListenableFuture<ResponseEntity<String>> future = asyncRestTemplate.getForEntity("http://localhost:8080/async", String.class);
System.out.println("return");
future.addCallback(new ListenableFutureCallback<ResponseEntity<String>>() {
@Override
public void onFailure(Throwable ex) {
System.out.println("Failure");
}
@Override
public void onSuccess(ResponseEntity<String> result) {
System.out.println("Success");
}
});
}
}
復制代碼
Spring MVC提供了多種途徑輸出模型數據:
Spring會根據請求方法簽名的不同,將請求中的信息以一定方式轉換并綁定到請求方法的入參中,還會進行數據轉換、數據格式化及數據校驗等。
Spring MVC通過反射對目標簽名進行分析,將請求消息綁定到處理方法的入參中。數據綁定的核心部件是DataBinder。Spring MVC主框架將ServletRequest對象及處理方法的入參對象實例傳遞給 DataBinder,DataBinder 首先調用裝配在 Spring Web 上下文中的 ConversionService 組件進行數據類型轉換、數據格式化等工作,將ServletRequest中的消息填充到入參對象中, 然后調用Validator組件對已經綁定了請求消息數據的入參對象進行數據合法性校驗,最 終生成數據綁定結果BindingResult對象。BindingResult包含了已完成數據綁定的入參 對象,還包含相應的校驗錯誤對象。Spring MVC抽取BindingResult中的入參對象及校驗錯誤對象,將它們賦給處理方法的相應入參。
類型轉換模塊位于org.framework.core.convert包中,同時由于歷史原因,Spring還支持JDK的PropertyEditor。
ConversionService 是 Spring 類型轉換體系的核心接口,它定義了以下4個方法:
第一個和第三個接口方法類似于PmpertyEditor,它們不關注類型對象所在的上下文 信息,只簡單地完成兩個類型對象的轉換,唯一的區別在于這兩個方法支持任意兩個類型的轉換。而第二個和第四個接口方法會參考類型對象所在宿主類的上下文信息,并利用這些信息進行類型轉換。
可以利用 org.springframework.context.support.ConversionServiceFactoryBean 在 Spring 的 上下文中定義一個ConversionService。Spring將自動識別出上下文中的ConversionService, 并在Bean屬性配置及Spring MVC處理方法入參綁定等場合使用它進行數據轉換。該FactoryBean創建ConversionService內建了很多轉換器,可完成大多數Java類型的轉換工作。除了包括將String對象轉換為各種基礎類型的對象外,還包括String、 Number、Array、Collection、Map、Properties 及 Object 之間的轉換器。可通過ConversionServiceFactoryBean的converters屬性注冊自定義的類型轉換器:
<bean class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="com.ankeetc.MyConverter"/>
</list>
</property>
</bean>
復制代碼
Spring 在 org.springframework.core.convert.converter 包中定義了3種類型的轉換器接口,實現任意一個轉換器接口都可以作為自定義轉換器注冊到ConversionServiceFactoryBean中。這3種類型的轉換器接口分別為:
ConversionServiceFactoryBean 的 converters 屬性可接受 Converter、ConverterFactory、 GenericConverter或ConditionalGenericConverter接口的實現類,并把這些轉換器的轉換邏輯統一封裝到一個 ConversionService 實例對象中(GenericConversionService)。Spring 在Bean屬性配置及Spring MVC請求消息綁定時將利用這個ConversionService實例完成類型轉換工作。
Spring也支持JavaBeans的PropertyEditor。可以在控制器中使用@InitBinder添加自定義的編輯器,也可以通過 WebBindingInitializer 裝配在全局范圍內使用的編輯器。
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(User.class, new PropertyEditorSupport() {
@Override
public void setAsText(String text) throws IllegalArgumentException {
User user = new User();
user.setName(text);
this.setValue(user);
}
});
}
復制代碼
如果希望在全局范圍內使用,則可實現WebBindingInitializer接口并在該實現類中注冊。
對于同一個類型對象來說,如果既在ConversionService中裝配了自定義轉換器,又通過WebBindinglnitializer裝配了自定義編輯器,同時還在控制器中通過@InitBinder裝 配了自定義編輯器,那么Spring MVC將按以下優先順序查找對應類型的編輯器:
Spring的轉換器并不提供輸入及輸出信息格式化的工作,一般需要轉換的源類型數據(一般是字符串)都是具有一定格式的,在不同的本地化環境中, 同一類型的數據還會相應地呈現不同的顯示格式。Spring引入了一個新的格式化框架,這個框架位于org.springframework.format類包中。
為了讓注解和格式化的屬性類型關聯起來,Spring在Formatter所在的包中還提供了一個 AnnotationFormatterFactory 接口。
對屬性對象的輸入/輸出進行格式化,從本質上講依然屬于“類型轉換”的范疇。 Spring就是基于對象轉換框架植入“格式化”功能的。Spring 在格式化模塊中定義了一個實現 ConversionService 接口的 FormattingConversionService實現類,該實現類擴展了 GenericConversionService,因此它既具有類型轉換功能,又具有格式化功能。
FormattingConversionService 也擁有一個對應的 FormattingConversionServiceFactoryBean 工廠類,后者用于在Spring上下文中構造一個FormattingConversionService。通過這個工廠類,既可以注冊自定義的轉換器,還可以注冊自定義的注解驅動邏輯。由于 FormattingConversionServiceFactoryBean 在內部會自動注冊 NumberFormatAnnotationFormatterFactory 和 JodaDateTimeFormatAnnotationFormatterFactory,因此裝配了 FormattingConversionServiceFactoryBean 后,就可以在 Spring MVC 入參綁定及模型數據輸出時使用注解驅動的格式化功能。
值得注意的是,
mvc:annotation-driven/ 標簽內部默認創建的ConversionService實例就是一個 FormattingConversionServiceFactoryBean。
Spring擁有自己獨立的數據校驗框架,同時支持JSR-303標準的校驗框架。Spring 的DataBinder在進行數據綁定時,可同時調用校驗框架完成數據校驗工作。在Spring MVC中,則可直接通過注解驅動的方式進行數據校驗。
LocalValidatorFactoryBean 既實現了 Spring 的 Validator 接口,又實現了 JSR-303 的 Validator 接口。只要在 Spring 容器中定義了一個 LocalValidatorFactoryBean,即可將其注入需要數據校驗的Bean中。值得注意的是,Spring本身沒有提供JSR-303的實現,所以必須將JSR-303的實現 者(如Hibernate Validator)的JAR文件放到類路徑下,Spring將自動加載并裝配好 JSR-303的實現者。
mvc:annotation-driven/
會默認裝配一個 LocalValidatorFactoryBean,通過在處理方法的入參上標注@Valid注解,即可讓Spring MVC在完成數據綁定后執行數據校驗工作。
Spring提供了以下4個本地化解析器。
Spring MVC為文件上傳提供了直接支持,這種支持是通過即插即用的MultipartResolver 實現的。Spring 使用 Jakarta Commons FileUpload 技術實現了一個 MultipartResolver 實現 類:CommonsMultipartResolver。
在Spring MVC上下文中默認沒有裝配MultipartResolver,因此默認情況下不能 處理文件的上傳工作。如果想使用Spring的文件上傳功能,則需要先在上下文中配置 MultipartResolver。
當收到請求時,DispatcherServlet將請求交給處理器映射(HandlerMapping),讓它找出對應該請求的HandlerExecutionChain對象。在講解HandlerMapping之前,有必要 認識一下這個 HandlerExecutionChain 對象。
HandlerExecutionChain負責處理請求并返回ModelAndView的處理執行鏈,它包含一個處理該請求的處理器 (Handler),同時包括若干個對該請求實施攔截的攔截器(HandlerInterceptor)。當 HandlerMapping 返回 HandlerExecutionChain 后,DispatcherServlet 將請求交給定義在 HandlerExecutionChain中的攔截器和處理器一并處理。
位于處理器鏈末端的是一個 Handler,DispatcherServlet通過 Handler Adapter適配器對 Handler進行封裝,并按統一的適配器接口對 Handler處理方法進行調用。可以在 web-servlet.xml 中配置多個攔截器,每個攔截器都可以指定一個匹配的映射路徑,以限制攔截器的作用范圍。
Spring MVC通過 HandlerExceptionResolver處理程序的異常,包括處理器映射、數據綁定及處理器執行時發生的異常。 HandlerExceptionResolver僅有一個接口方法:Modelandview resolveException(HttpServletRequest request HttpServletResponse response Object handler, Exception ex)。當發生異常時,Spring MVC將調用 resolveException方法,并轉到 ModelAndView 對應的視圖中,作為一個異常報告頁面反饋給用戶。
實現類
小編這里總結一份springMVC的思維導圖,想了解的小伙伴可以看看呢
喜歡小編分享的技術文章可以點贊關注哦!
小編這邊整理了一些spring MVC的技術資料以及面試題
原文鏈接: https://juejin.cn/post/6902238540466225159
如果覺得本文對你有幫助,可以轉發關注支持一下
我們的開發工程中經常會使用到各種圖,所謂的圖就是由節點和節點之間的連接所形成的系統,數學上專門有一個分支叫圖論(Graph Theroy)。利用圖我們可以做很多工具,比如思維導圖,流程圖,狀態機,組織架構圖,等等。今天我要做的是用開源的HTML5工具來快速構造一個做圖的工具。
工預善其事,必先利其器。第一件事是選擇一件合適的工具,開源時代,程序員還是很幸福的,選擇很多。
最終,我選擇了jsPlumb,因為它完全開源,使用很簡單,用D3的話可能會多花很多功夫。joint.js也不錯。大家可以根據自己的需要選擇。
下面我們一步一步的來使用jsPlumb來創建我們的流程圖工具。
第一步是等待DOM和jsPlumb初始化完畢,類似document.ready()和jquery.ready(), 要使用jsPlumb, 需要把代碼放在這個函數里:
jsPlumb.ready(function()?{ ????//?...?your?code?goes?here?... }
創建一個jsPlumb的實例,并初始化jsPlumb的配置參數:
//Initialize?JsPlumb var?color?=?"#E8C870"; var?instance?=?jsPlumb.getInstance({ ????//?notice?the?'curviness'?argument?to?this?Bezier?curve.??the?curves?on?this?page?are?far?smoother ????//?than?the?curves?on?the?first?demo,?which?use?the?default?curviness?value.?????? ????Connector?:?[?"Bezier",?{?curviness:50?}?], ????DragOptions?:?{?cursor:?"pointer",?zIndex:2000?}, ????PaintStyle?:?{?strokeStyle:color,?lineWidth:2?}, ????EndpointStyle?:?{?radius:5,?fillStyle:color?}, ????HoverPaintStyle?:?{strokeStyle:"#7073EB"?}, ????EndpointHoverStyle?:?{fillStyle:"#7073EB"?}, ????Container:"container-id" ?});
這里給給出了一些配置包括,連接線(這里配置了一個貝塞爾曲線),線的風格,連接點得風格。Container需要配置一個對應的DIV容器的id。(這里也可以使用setContainer的方法)
下面我們要創建一個節點(node),每一個節點可以用一個DIV來實現。我這里提供了一個函數來創建節點。
function?addNode(parentId,?nodeId,?nodeLable,?position)?{ ??var?panel?=?d3.select("#"?+?parentId); ??panel.append('div').style('width','120px').style('height','50px') ????.style('position','absolute') ????.style('top',position.y).style('left',position.x) ????.style('border','2px?#9DFFCA?solid').attr('align','center') ????.attr('id',nodeId).classed('node',true) ????.text(nodeLable); ??return?jsPlumb.getSelector('#'?+?nodeId)[0]; }
這里做的事情就是創建了一個DIV元素,并放在對應的容器的制定位置上,注意為了支持拖拽的功能,必須使用position:absolute 。
我使用D3來操作DOM,大家可能會更習慣JQuery,這純屬個人喜好的問題。
最后返回創建節點的實例引用,這是的selector使用了jsPlumb.getSelector()方法,它和JQuery的selector是一樣的,這樣用的好處是你可以使用不同的DOM操作庫,例如Vanilla
下面我使用一個函數來創建端點/錨點(anchor),錨點就是節點上的連接點,用于連接不同的節點。
function?addPorts(instance,?node,?ports,?type)?{ ??//Assume?horizental?layout ??var?number_of_ports?=?ports.length; ??var?i?=?0; ??var?height?=?$(node).height();??//Note,?jquery?does?not?include?border?for?height ??var?y_offset?=?1?/?(?number_of_ports?+?1); ??var?y?=?0; ??for?(?;?i?<?number_of_ports;?i++?)?{ ????var?anchor?=?[0,0,0,0]; ????var?paintStyle?=?{?radius:5,?fillStyle:'#FF8891'?}; ????var?isSource?=?false,?isTarget?=?false; ????if?(?type?===?'output'?)?{ ??????anchor[0]?=?1; ??????paintStyle.fillStyle?=?'#D4FFD6'; ??????isSource?=?true; ????}?else?{ ??????isTarget?=true; ????} ????anchor[1]?=?y?+?y_offset; ????y?=?anchor[1]; ????instance.addEndpoint(node,?{ ??????uuid:node.getAttribute("id")?+?"-"?+?ports[i], ??????paintStyle:?paintStyle, ??????anchor:anchor, ??????maxConnections:-1, ??????isSource:isSource, ??????isTarget:isTarget ????}); ??} }
instance是jsPlumb的實例
node是我們用addNode方法創建的Node實例
ports,是一個string的數組,指定端點的個數和名字
type,可能是output或者input,指定端點的種類,一個節點的輸出端口可以連接另一個節點的輸入端口。
這里anchor是一個四維數組,0維和1維分別是錨點在節點x軸和y軸的偏移百分比。我這里希望把端口畫在節點的左右兩側,并按照端口的數量均勻分布。
最后使用instance.addEndpoint來創建端點。注意這里只要指定isSource和isTarget就可以用drag&drop的方式來連接端點,非常方便。
下面一步我們提供一個函數來連接端點:
function?connectPorts(instance,?node1,?port1,?node2?,?port2)?{ ??//?declare?some?common?values: ??var?color?=?"gray"; ??var?arrowCommon?=?{?foldback:0.8,?fillStyle:color,?width:5?}, ??//?use?three-arg?spec?to?create?two?different?arrows?with?the?common?values: ??overlays?=?[ ????[?"Arrow",?{?location:0.8?},?arrowCommon?], ????[?"Arrow",?{?location:0.2,?direction:-1?},?arrowCommon?] ??]; ??var?uuid_source?=?node1.getAttribute("id")?+?"-"?+?port1; ??var?uuid_target?=?node2.getAttribute("id")?+?"-"?+?port2; ??instance.connect({uuids:[uuid_source,?uuid_target]}); }
node1和node2是源節點和目標節點的引用,port1和port2是源端口和目標端口的名字。
使用instance.connect方法來創建連接。 overlays用來添加連接線的箭頭效果或者其他風格,我這里沒有使用,因為覺得都不是很好看。大家如果要用,只要把overlays加入到instance.connect的方法參數就可以了。
調用以上方法來創建節點,端點和連接線。
var?node1?=?addNode('container-id','node1',?'node1',?{x:'80px',y:'20px'}); var?node2?=?addNode('container-id','node2',?'node2',?{x:'280px',y:'20px'}); addPorts(instance,?node1,?['out1','out2'],'output'); addPorts(instance,?node2,?['in','in1','in2'],'input'); connectPorts(instance,?node1,?'out2',?node2,?'in');
這里我們創建了兩個節點,第一個節點有兩個輸出端口,第二個節點有三個輸入端口,然后把第一個節點的out2端口連接到第二個端點的in端口。效果如下:
最后我們給節點增加drag&drop的功能,這樣我們就可以拖動這些節點來改變圖的布局了。
instance.draggable($('.node'));
這里似乎依賴于JQuery-UI,我還不是很清楚。
我們已經初步具有了創建圖的功能,可是節點的創建必須通過程序,我們希望用交互的方式來創建節點。
通常我們希望有一個tree view的控件,讓后通過拖拽來創建對應類型的節點。這里我使用了這個開源的tree view,基于bootstrap https://github.com/jonmiles/bootstrap-treeview
我們先創建一個tree view:
function?getTreeData()?{ ??var?tree?=?[ ????{ ??????text:?"Nodes", ??????nodes:?[ ????????{ ??????????text:?"Node1", ????????}, ????????{ ??????????text:?"Node2" ????????} ??????] ????} ??];? ??return?tree; } //Initialize?Control?Tree?View $('#control-panel').treeview({data:?getTreeData()});
樹上有兩個節點:
然后我實現從樹上拖拽對應的節點,到流程圖上的邏輯。
//Handle?drag?and?drop $('.list-group-item').attr('draggable','true').on('dragstart',?function(ev){ ??//ev.dataTransfer.setData("text",?ev.target.id); ??ev.originalEvent.dataTransfer.setData('text',ev.target.textContent); ??console.log('drag?start'); }); $('#container-id').on('drop',?function(ev){ ??//avoid?event?conlict?for?jsPlumb ??if?(ev.target.className.indexOf('_jsPlumb')?>=?0?)?{ ????return; ??} ??ev.preventDefault(); ??var?mx?=?''?+?ev.originalEvent.offsetX?+?'px'; ??var?my?=?''?+?ev.originalEvent.offsetY?+?'px'; ??console.log('on?drop?:?'?+?ev.originalEvent.dataTransfer.getData('text')); ??var?uid?=?new?Date().getTime(); ??var?node?=?addNode('flow-panel','node'?+?uid,?'node',?{x:mx,y:my}); ??addPorts(instance,?node,?['out'],'output'); ??addPorts(instance,?node,?['in1','in2'],'input'); ??instance.draggable($(node)); }).on('dragover',?function(ev){ ??ev.preventDefault(); ??console.log('on?drag?over'); });
這里要注意的是要避免和jsPlumb拖拽端點的邏輯沖突,當檢測到target是jsPlumb對象是需要直接從drop方法中退出以執行對應的jsPlumb的drop邏輯。
好了,一個繪制流程圖的軟件工具初步完工。
我把代碼放在oschina的代碼托管服務上了, 大家有興趣可以去試試。
ervlet容器主要是JavaWeb應用提供運行時環境,所以也可以稱之為JavaWeb應用容器,或者Servlet/JSP容器。Servlet容器主要負責管理Servlet、JSP的生命周期以及它們的共享數據。
Servlet容器有哪些:
目前最流行的Servlet容器軟件包括: Tomcat、Jetty、Jboss等。
Tomcat
Tomcat和IIS等Web服務器一樣,具有處理HTML頁面的功能,另外它還是一個Servlet和JSP容器,獨立的Servlet容器是Tomcat的默認模式。不過,Tomcat處理靜態HTML的能力不如Apache服務器。
Jetty
Jetty 是一個開源的servlet容器,它為基于Java的web容器,例如JSP和servlet提供運行環境。Jetty是使用Java語言編寫的,它的API以一組JAR包的形式發布。開發人員可以將Jetty容器實例化成一個對象,可以迅速為一些獨立運行(stand-alone)的Java應用提供網絡和web連接。
Jboss
Jboss是一個基于J2EE的開放源代碼的應用服務器。 JBoss代碼遵循LGPL許可,可以在任何商業應用中免費使用。JBoss是一個管理EJB的容器和服務器,支持EJB 1.1、EJB 2.0和EJB3的規范。但JBoss核心服務不包括支持servlet/JSP的WEB容器,一般與Tomcat或Jetty綁定使用。
Servlet是和平臺無關的服務器端組件(java編寫的,跨平臺),它運行在Servlet容器中。
Servlet容器負責Servlet和客戶的通信以及調用Servlet的方法,Servlet和客戶的通信采用“請求/響應”的模式 Servlet可完成如下功能:
1、創建并返回基于客戶請求的動態HTML頁面
2、創建可嵌入到現有HTML 頁面中的部分HTML 頁面(HTML 片段)
3、與其它服務器資源(如數據庫或基于Java的應用程序)進行通信
Servlet容器響應客戶請求過程:
*請認真填寫需求信息,我們會在24小時內與您取得聯系。