`
earls
  • 浏览: 69877 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Wicket如何通过OSGi框架注入Jetty

阅读更多
       Wicket框架的优点就不多说了,总之第一次看到她就让人耳目一新。我的框架就是采用OSGi、Spring、Hibernate、Wicket、Jetty等开源技术搭建的,所以这篇文章就是我在将Wicket通过OSGi框架注入Jetty中的一点心得体会。目前wicket刚刚推出1.3Beta3版,由于1.3版和2.0版比较接近,所以采用1.3版来测试。
       首先介绍一下我的整体思路,将Jetty和wicket分别作为OSGi的Bundle,对外提供服务;自己的基于wicket的web应用也作为一个Bundle。另外因为web应用中要用到其它Bundle提供的服务,当然采用IOC方式最方便,所以需要wicket和spring整合一下。
       先说Jetty和Wicket这两个Bundle,其实Jetty6.1.5已经兼容OSGi,它的jar包已经可以直接作为Bundle使用,我们目前涉及到的就是其安装目录下的lib/jetty-6.1.5.jar,lib/jetty-util-6.1.5.jar和lib/servlet-api-2.5.6.1.5.jar这三个jar包。前两个是jetty作为嵌入式服务器所必需的,最后一个是jetty的servlet实现。
       因为我们采用编程式处理web应用,所以需要将web应用的信息注入到Jetty中。那么为什么不使用配置文件如web.xml呢?因为如果使用配置文件就要解决两个问题:
       第一是绝对路径问题,OSGi的Bundle并没有自己固定部署的位置,所以部署时绝对路径可以任选,这样如果采用配置文件声明web应用,那么配置文件就要随着Bundle位置变更而变更,一般Bundle都是jar包的形式保存,势必导致修改的困难。
       第二是类加载问题,因为我们要使用IOC和其他Bundle声明的服务,如果采用配置文件方式,类的初始化都由ClassLoader直接处理,因此上面所说的很难办到。   
       采用编程式处理web应用,就需要建一个Bundle(wanged_core_jetty),用来处理其他Bundle提供的Handler(这个jetty中的主要部件,可以参看Jetty 6.1.5的配置一文)。先看配置文件bean.xml:
xml 代码
 
  1. <bean id="jettyHandlerManager" class="wanged.core.jetty.JettyHandlerManager">  
  2.   <property name="handlers" ref="handlers" />  
  3. </bean>  
  4.   
  5. <bean name="jettyServer" class="wanged.core.jetty.JettyServer" init-method="start" destroy-method="stop" />  
以及osgi-service.xml:
xml 代码
 
  1. <!-- 服务 和 引用 -->  
  2. <osgi:reference id="handlers" interface="org.mortbay.jetty.Handler" cardinality="1..n">  
  3.   <osgi:listener ref="jettyServer" />  
  4. </osgi:reference>  
这里引用了其他Bundle提供的接口为org.mortbay.jetty.Handler的服务,并注册一个监听器jettyServer来处理Handler的动态绑定与删除问题,以便可以动态添加/删除web应用。下面看看具体代码:
java 代码
 
  1. package wanged.core.jetty;  
  2.   
  3. import java.util.Dictionary;  
  4.   
  5. import org.mortbay.jetty.Handler;  
  6. import org.mortbay.jetty.Server;  
  7. import org.mortbay.jetty.handler.HandlerCollection;  
  8. import org.springframework.osgi.service.TargetSourceLifecycleListener;  
  9.   
  10. /** 
  11.  * 启动Jetty服务器,监听Handler的添加与删除,并动态更新服务器的Handler 
  12.  * @author wanged 
  13.  * 
  14.  */  
  15.   
  16. @SuppressWarnings("unchecked")  
  17. public class JettyServer implements TargetSourceLifecycleListener{  
  18.       
  19.     private Server server = new Server(8080);  
  20.       
  21.     private HandlerCollection handlers = new HandlerCollection();  
  22.       
  23.     public void start(){  
  24.         this.server.setHandler(this.handlers);  
  25.         try {  
  26.             this.server.start();  
  27.         } catch (Exception e) {  
  28.             e.printStackTrace();  
  29.         }  
  30.     }  
  31.       
  32.     /** 
  33.      * 绑定新的Handler,并启动之 
  34.      */  
  35.     public void bind(Object obj, Dictionary dict) throws Exception {  
  36.         Handler h = (Handler)obj;  
  37.         this.handlers.addHandler(h);  
  38.         h.start();  
  39.     }  
  40.       
  41.     /** 
  42.      * 删除Handler,并停止之 
  43.      */  
  44.     public void unbind(Object obj, Dictionary dict) throws Exception {  
  45.         Handler h = (Handler)obj;  
  46.         this.handlers.removeHandler(h);  
  47.         h.stop();  
  48.     }  
  49.       
  50.     public void stop(){  
  51.         try {  
  52.             this.server.stop();  
  53.         } catch (Exception e) {  
  54.             e.printStackTrace();  
  55.         }  
  56.     }  
  57.   
  58. }  
实现接口TargetSourceLifecycleListener,监听服务的绑定与删除。另一个类如下:
java 代码
  1. package wanged.core.jetty;  
  2.   
  3. import java.util.Collection;  
  4.   
  5. import org.mortbay.jetty.Handler;  
  6.   
  7. /** 
  8.  * Jetty服务器Handler的管理器,用来动态管理作为服务注册的Handler 
  9.  * @author wanged 
  10.  * 
  11.  */  
  12. public class JettyHandlerManager {  
  13.     private Collection<Handler> handlers;  
  14.   
  15.     public void setHandlers(Collection<Handler> handlers) {  
  16.         this.handlers = handlers;  
  17.     }  
  18.   
  19. }  
这个类只是简单声明了一个
setHandlers方法,功能可以扩展。到此为止Jetty的相关工作已经处理完了。
       下面该说说web应用了,我的web应用放在一个叫wanged_wicket_app的Bundle中。涉及到的类比较多,所以现将类列出再一一解说。首先看SpringWicketFilter:
java 代码
  1. package wanged.web.wicket;  
  2.   
  3. import org.apache.wicket.protocol.http.IWebApplicationFactory;  
  4. import org.apache.wicket.protocol.http.WicketFilter;  
  5.   
  6. public class SpringWicketFilter extends WicketFilter{  
  7.     private IWebApplicationFactory factory ;  
  8.       
  9.     public IWebApplicationFactory getApplicationFactory(){  
  10.         return factory;  
  11.     }  
  12.           
  13.     public void setApplicationFactory(IWebApplicationFactory factory){  
  14.         this.factory = factory;  
  15.     }  
  16.       
  17. }  
为什么不直接用WicketFilter呢,因为
WicketFilter需要有一个applicationClass的参数需要定义在init-param中,需要解析web.xml才能得到,而且生成的WebApplication不能使用IOC功能。熟悉Wicket的朋友会问,Wicket不是已经和Spring整合了么?是的,确实整合了,但这种整合无法用在OSGi环境下,所以我们需要自定义一个WicketFilter的子类来声明一个注入了Spring上下文的IWebApplicationFactory的实现SpringWebApplicationFactory
java 代码
 
  1. package wanged.web.wicket;  
  2.   
  3. import org.apache.wicket.protocol.http.IWebApplicationFactory;  
  4. import org.apache.wicket.protocol.http.WebApplication;  
  5. import org.apache.wicket.protocol.http.WicketFilter;  
  6. import org.springframework.beans.BeansException;  
  7. import org.springframework.context.ApplicationContext;  
  8. import org.springframework.context.ApplicationContextAware;  
  9.   
  10. public class SpringWebApplicationFactory implements IWebApplicationFactory, ApplicationContextAware {  
  11.     private WebApplication webApplication;  
  12.   
  13.     private ApplicationContext context;  
  14.   
  15.     private String diskStorePath;  
  16.   
  17.     public void init() {  
  18.         DefaultApplication app = new DefaultApplication();  
  19.         app.setApplicationContext(context);  
  20.         app.setDiskStorePath(this.diskStorePath);  
  21.         this.webApplication = app;  
  22.     }  
  23.   
  24.     public WebApplication createApplication(WicketFilter filter) {  
  25.         return webApplication;  
  26.     }  
  27.   
  28.     public void setApplicationContext(ApplicationContext context) throws BeansException {  
  29.         this.context = context;  
  30.     }  
  31.       
  32.     public void setDiskStorePath(String path){  
  33.         this.diskStorePath = path;  
  34.     }  
  35. }  
这里引用了一个
DefaultApplication,这是WebApplication的子类,在Wicket应用中有至关重要的作用,每一个wicket的web应用都需要自定义一个:
java 代码
 
  1. package wanged.web.wicket;  
  2.   
  3. import java.io.File;  
  4.   
  5. import org.apache.wicket.protocol.http.SecondLevelCacheSessionStore;  
  6. import org.apache.wicket.protocol.http.WebApplication;  
  7. import org.apache.wicket.protocol.http.pagestore.DiskPageStore;  
  8. import org.apache.wicket.session.ISessionStore;  
  9. import org.apache.wicket.spring.injection.annot.SpringComponentInjector;  
  10. import org.apache.wicket.util.lang.Bytes;  
  11. import org.springframework.beans.BeansException;  
  12. import org.springframework.context.ApplicationContext;  
  13. import org.springframework.context.ApplicationContextAware;  
  14.   
  15. import wanged.web.wicket.login.LoginPage;  
  16.   
  17. public class DefaultApplication extends WebApplication implements ApplicationContextAware {  
  18.     private ApplicationContext context;  
  19.     private String diskStorePath;  
  20.   
  21.       
  22.     @SuppressWarnings("unchecked")  
  23.     @Override  
  24.     public Class getHomePage() {  
  25.         return LoginPage.class;  
  26.     }  
  27.   
  28.     @Override  
  29.     public void init() {  
  30.         super.init();  
  31.         addComponentInstantiationListener(new SpringComponentInjector(thisthis.context));  
  32.     }  
  33.   
  34.     public void setApplicationContext(ApplicationContext context) throws BeansException {  
  35.         this.context = context;  
  36.     }  
  37.   
  38.     protected ISessionStore newSessionStore() {  
  39.         return new SecondLevelCacheSessionStore(thisnew DiskPageStore(new File(this.diskStorePath), (int) Bytes.megabytes(10).bytes(), (int) Bytes.megabytes(  
  40.                         100).bytes(), 25));  
  41.     }  
  42.       
  43.     public void setDiskStorePath(String path){  
  44.         this.diskStorePath = path;  
  45.     }  
  46. }  
由于Wicket1.3开始采用Filter处理Request,不再使用Servlet,所以我们的环境中只需要配置Filter的相关信息即可。为了能采用IOC方式配置Filter及其映射,引入一个新类FilterMapping:
java 代码
 
  1. package wanged.web.wicket;  
  2.   
  3. import javax.servlet.Filter;  
  4.   
  5. public class FilterMapping {  
  6.     private Class<Filter> filterClass;  
  7.   
  8.     private Filter filter;  
  9.   
  10.     private String urlPattern = "/*";  
  11.   
  12.     public Class<Filter> getFilterClass() {  
  13.         return filterClass;  
  14.     }  
  15.   
  16.     public void setFilterClass(Class<Filter> filterClass) {  
  17.         this.filterClass = filterClass;  
  18.     }  
  19.   
  20.     public Filter getFilter() {  
  21.         return filter;  
  22.     }  
  23.   
  24.     public void setFilter(Filter filter) {  
  25.         this.filter = filter;  
  26.     }  
  27.   
  28.     public String getUrlPattern() {  
  29.         return urlPattern;  
  30.     }  
  31.   
  32.     public void setUrlPattern(String urlPattern) {  
  33.         this.urlPattern = urlPattern;  
  34.     }  
  35.   
  36. }  
内容很简单,不多废话了。到目前为止,我们只是定义了Wicket相关的类文件,如何注入到Jetty中呢?接下来就是最主要的了:
java 代码
  1. package wanged.web;  
  2.   
  3. import java.util.HashMap;  
  4. import java.util.List;  
  5. import java.util.Map;  
  6.   
  7. import org.mortbay.jetty.Handler;  
  8. import org.mortbay.jetty.servlet.Context;  
  9. import org.mortbay.jetty.servlet.DefaultServlet;  
  10. import org.mortbay.jetty.servlet.FilterHolder;  
  11. import org.mortbay.jetty.servlet.SessionHandler;  
  12.   
  13. import wanged.web.wicket.FilterMapping;  
  14.   
  15. public class ContextFactory {  
  16.   
  17.     private HashMap<String, String> attributes;  
  18.   
  19.     private List<FilterMapping> filterMappings;  
  20.   
  21.     private Context ctx = new Context();;  
  22.   
  23.     public Context newInstance() {  
  24.         return ctx;  
  25.     }  
  26.   
  27.     public void init() {  
  28.         if (this.attributes != null) {  
  29.             for (Map.Entry<String, String> entry : this.attributes.entrySet()) {  
  30.                 ctx.setAttribute(entry.getKey(), entry.getValue());  
  31.             }  
  32.         }  
  33.         ctx.setSessionHandler(new SessionHandler());  
  34.         ctx.addServlet(DefaultServlet.class"/");  
  35.   
  36.         for (FilterMapping fm : filterMappings) {  
  37.             ctx.addFilter(new FilterHolder(fm.getFilter()), fm.getUrlPattern(), Handler.REQUEST);  
  38.         }  
  39.     }  
  40.   
  41.     public void setFilterMappings(List<FilterMapping> filterMappings) {  
  42.         this.filterMappings = filterMappings;  
  43.     }  
  44.     public void setAttributes(HashMap<String, String> attributes) {  
  45.         this.attributes = attributes;  
  46.     }  
  47.   
  48.     public void setContextPath(String contextPath) {  
  49.         this.ctx.setContextPath(contextPath);  
  50.     }  
  51.   
  52. }  
这个类负责收集我们配置文件中的bean,然后打包成一个Context作为一个对外声明的Handler服务(
注意org.mortbay.jetty.servlet.Context实现了org.mortbay.jetty.Handler接口)。
下面看配置文件web-bean.xml:
xml 代码
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  3.   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">  
  4.   
  5.   <!-- 定义服务实现 -->  
  6.   <bean id="handler" factory-bean="contextFactory" factory-method="newInstance" />  
  7.   
  8.   <!-- Jetty的处理器工厂,用来向Jetty添加处理器 -->  
  9.   <bean id="contextFactory" class="wanged.web.ContextFactory" init-method="init">  
  10.     <property name="contextPath" value="/" />  
  11.     <property name="filterMappings">  
  12.       <!-- Filter执行顺序预定顺序相关 -->  
  13.       <list>  
  14.         <ref bean="gzipFilterMapping" />  
  15.         <ref bean="wicketFilterMapping" />  
  16.       </list>  
  17.     </property>  
  18.   </bean>  
  19.   
  20.   <bean id="gzipFilter" class="org.mortbay.servlet.GzipFilter" />  
  21.   
  22.   <bean id="wicketFilter" class="wanged.web.wicket.SpringWicketFilter">  
  23.     <property name="applicationFactory" ref="applicationFactory" />  
  24.   </bean>  
  25.   
  26.   <bean id="wicketFilterMapping" class="wanged.web.wicket.FilterMapping">  
  27.     <property name="filter" ref="wicketFilter" />  
  28.   </bean>  
  29.   
  30.   <bean id="gzipFilterMapping" class="wanged.web.wicket.FilterMapping">  
  31.     <property name="filter" ref="gzipFilter" />  
  32.   </bean>  
  33.   
  34.   <bean id="applicationFactory" class="wanged.web.wicket.SpringWebApplicationFactory" init-method="init" >  
  35.     <property name="diskStorePath" value="F:/TMP" />  
  36.   </bean>  
  37.   
  38. </beans>  
这里定义了我们所使用到的各种类,同时
SpringWebApplicationFactory在此获得Spring上下文信息。
另一个声明服务的配置文件osgi-service.xml:
xml 代码
 
  1. <osgi:service interface="org.mortbay.jetty.Handler" ref="handler" />  
  2.   
  3. <osgi:reference id="roleService" interface="wanged.security.service.RoleService" />  
这里的roleService是引用了外部的一个服务,用来测试Sping注入的(可以用一个在web-bean.xml中定义的bean来代替测试)。
以上已经完成了wicket的web应用注入jetty的工作,最后就是写一个LoginPage.java完成整个Bundle:
java 代码
 
  1. package wanged.web.wicket.login;  
  2.   
  3. import org.apache.wicket.ajax.AjaxRequestTarget;  
  4. import org.apache.wicket.ajax.markup.html.form.AjaxButton;  
  5. import org.apache.wicket.markup.html.WebPage;  
  6. import org.apache.wicket.markup.html.form.Form;  
  7. import org.apache.wicket.markup.html.form.PasswordTextField;  
  8. import org.apache.wicket.markup.html.form.TextField;  
  9. import org.apache.wicket.model.CompoundPropertyModel;  
  10. import org.apache.wicket.model.IModel;  
  11.   
  12. import wanged.security.entity.Role;  
  13. import wanged.security.service.RoleService;  
  14. import org.apache.wicket.spring.injection.annot.SpringBean;  
  15. import org.mortbay.jetty.Handler;  
  16.   
  17. public class LoginPage extends WebPage {  
  18.   
  19.     private LoginUser user;  
  20.   
  21.     public LoginPage() {  
  22.         super();  
  23.         this.user = new LoginUser();  
  24.   
  25.         Form form = new AutoSaveForm("loginForm"new CompoundPropertyModel(user));  
  26.         add(form);  
  27.         form.add(new TextField("username"));  
  28.         form.add(new PasswordTextField("password"));  
  29.     }  
  30.   
  31. }  
  32.   
  33. class AutoSaveForm extends Form {  
  34.   
  35.     /* 这里就是使用IOC的地方, 
  36.         试想一下如果没有IOC,我们如何让自定义的类来获得其他Bean呢 
  37.     */  
  38.     @SpringBean  
  39.     RoleService roleService;  
  40.       
  41.     AutoSaveForm(String id, IModel m) {  
  42.         super(id, m);  
  43.         AjaxButton button = new AjaxButton("button"this) {  
  44.             @Override  
  45.             protected void onSubmit(AjaxRequestTarget target, Form form) {  
  46.                 LoginUser user = (LoginUser)form.getModelObject();  
  47.                 System.out.println("un: " + user.getUsername());  
  48.                 System.out.println("pd: " + user.getPassword());  
  49.             }  
  50.         };  
  51.         this.add(button);  
  52.     }  
  53. }  

        运行一下一切OK。感觉好繁琐呀!确实如此,OSGi带给我们的是基于组件的面向服务的开发,用来做小项目确实有点大炮打蚊子的感觉,但如果是做大型项目,就能充分体现出组件和SOA的优势。
分享到:
评论
4 楼 shaucle 2007-08-24  
谢谢,感觉刚入手好烦。

至少要比它看起来要烦得多。
3 楼 earls 2007-08-24  
图见附件
2 楼 earls 2007-08-24  
无法贴图,只好这样了<br/>
wanged_wicket_app<br/>
       classes<br/>
        META-INF<br/>
                spring<br/>
                        osgi-service.xml<br/>
                        web-bean.xml<br/>
                MANIFEST.MF<br/>
        src<br/>
                wanged<br/>
                        web<br/>
                                wicket<br/>
                                        login<br/>
                                                LoginPage.java<br/>
                                                LoginPage.html<br/>
                                        DefaultApplication.java<br/>
                                       FilterMapping.java<br/>
                                        SpringWebApplicationFactory.java<br/>
                                        SpringWicketFilter.java<br/>
                                ContextFactory.java
1 楼 shaucle 2007-08-24  
能否提供个目录结构?

相关推荐

    Wicket开发指南

    Wicket框架是一个基于Java的开源Web应用程序框架,它的设计目标是提供一种简单、声明式和组件化的编程模型,让开发者能够更高效地构建动态、交互式的Web应用。Wicket的核心理念是将UI逻辑与HTML模板分离,从而实现更...

    Wicket 入门

    在当今的 Web 开发领域,Java 框架扮演着举足轻重的角色,其中 Apache Wicket 是一个强大的、面向组件的 Java Web 应用框架,它简化了 Web 应用的开发过程。本文将详细介绍如何在 Eclipse 中使用 Maven 创建一个 ...

    wicket指南

    1. **部署**:Wicket 应用可以部署在任何Servlet容器,如Tomcat、Jetty等。 2. **配置**:通过`wicket.properties`文件设置应用属性,如session超时时间、调试模式等。 3. **资源管理**:Wicket 使用`...

    wicket1.4的jar文件

    9. **依赖注入**:Wicket支持依赖注入框架,如Spring,这使得组件之间的依赖关系更加清晰,代码更易于测试和管理。 10. **版本兼容性**:Wicket 1.4的jar文件可能还包含了对不同JVM版本的支持,确保了在多种环境下...

    wicket+spring

    在Java Web开发领域,Wicket和Spring都是非常流行的框架,它们分别在用户界面和依赖注入方面有着出色的表现。本文将深入探讨如何将这两个框架整合,创建一个高效、可维护的Web应用程序。 **Wicket 框架** Wicket是...

    org.ops4j.pax.wicket.service-0.8.6.zip

    标题 "org.ops4j.pax.wicket.service-...了解并掌握这些知识点,开发者可以有效地利用OPS4J Pax Wicket构建OSGi应用,同时通过WiQuery Highcharts为这些应用添加数据可视化功能,创建出既美观又功能强大的Web应用程序。

    Wicket1.40.war包+开发指南.rar

    通过这本书,开发者可以从零开始学习Wicket,逐步掌握其精髓。 **布置Wicket应用** `wicket-examples-1.4.0.war`文件是一个可部署的Web应用档案,可以直接部署到支持Servlet 2.4或更高版本的Web服务器上,如Tomcat...

    wicket1.4.7中的jar包

    5. **wicket-ioc.jar**:提供了依赖注入(Dependency Injection, DI)的功能,使得对象之间的依赖关系可以通过配置文件或注解来管理,降低了代码的耦合度,提高了可维护性。 6. **wicket-datetime.jar**:包含了...

    Wicket 开发指南

    通过学习并实践以上内容,你将能够熟练掌握Wicket框架,创建出高效且易于维护的Web应用程序。不断探索Wicket的高级特性和社区提供的各种扩展,你会发现这个框架具有极高的灵活性和可扩展性。在实际开发中,结合最佳...

    wicket7教程

    Apache Wicket是一个为Java Web开发者提供的组件导向的框架,它能够帮助开发者在不编写大量JavaScript代码的情况下快速...通过学习本教程,Java开发者可以有效地掌握Wicket框架,开发出结构清晰、易于维护的Web应用。

    apacke-wicket-mvn编译

    Apache Wicket是一款开源的Java Web应用框架,它以组件为基础,提供了丰富的功能来帮助开发者创建动态、交互式的Web应用程序。 【描述】:“自用,在线转移资源,使用带mvn插件的eclipse直接导入即可。”这说明这个...

    wicket开发指南 pdf

    Wicket 是一个开源的、基于Java的Web应用程序框架,它以其组件化、模型-视图-控制器(MVC)架构和轻量级特性而受到开发者们的青睐。本指南将深入探讨Wicket的核心概念、工作原理以及如何有效地利用它来构建动态、...

    apache-wicket-7

    7. **依赖注入**:Wicket 7集成了Spring和其他依赖注入框架,方便组件间的协作和解耦。 8. **性能优化**:Wicket通过页面版本控制和组件缓存策略来提升性能。了解如何正确设置这些选项对于大型、高流量的应用尤为...

    wicket_mvn库

    Wicket Maven 库是一个针对 Apache Wicket 框架的 Maven 存储库,它包含了构建和运行 Wicket 应用程序所需的各种依赖项。Apache Wicket 是一个用于创建 Web 应用程序的开源 Java 框架,它采用模型-视图-控制器(MVC...

    前端项目-wicket.zip

    6. **版本控制系统**:提及的“Wicket-master”暗示项目使用了Git进行版本控制,这意味着代码的历史记录、协作和分支管理都是通过Git完成的。 7. **开发流程**:此项目可能遵循敏捷开发或持续集成/持续部署(CI/CD)...

    wicket 官方JAVA教程

    Wicket是一款开源的Java Web应用程序框架,它强调组件化和声明式编程模型,使得开发人员可以构建出用户界面更加灵活且易于维护的Web应用。本教程将深入探讨Wicket的核心概念、特性以及如何利用它进行实际开发。 1. ...

    Wicket开发指南-简体版

    - **Wicket概述**:Wicket是一个轻量级框架,其设计旨在降低开发门槛,并通过一系列特性提升开发体验。 **1.4 Wicket的主要特性** - **更加纯粹的Java和面向对象**:Wicket的设计遵循Java的最佳实践,使得开发者...

    wicket的helloworld

    这个“Wicket的HelloWorld”程序是初学者入门Wicket的典型例子,它展示了如何利用Wicket框架创建一个简单的Web应用。 在Wicket中,组件是构建Web页面的基本单元,可以是HTML元素,如按钮、文本框,也可以是更复杂的...

Global site tag (gtag.js) - Google Analytics