Skip to content
sunlet edited this page Jan 10, 2016 · 34 revisions

1.JPlugin基本介绍

软件架构的本质在于模块拆分,这些经过拆分的模块经过某种契约协同满足应用软件的对外需求。在当今,软件的需求都是迭代产生的,易变的。所以,对软件本身来说,如何最大限度的应对变化,“预见未来”,甚至“随需应变”是对架构设计的极致目标。

那么,什么样子的架构才能应对未来尽可能多的可能性呢?在客户端开发工具层面,Eclipse应该是业界最成功的案例。Eclipse最早提供的功能就是一个Java的IDE,但是基于Eclipse,可以扩展出几乎任何桌面式GUI应用;并且既有的Java开发环境也可以进行无限想象的功能扩展。Eclipse之所以能够支持如此“多样性的未来”,是因为Eclipse采用了插件式框架,以及OSGI类加载机制。

JPlugin借鉴Eclipse设计思想,引入到服务端(Server Side),希望能够做到服务端的Eclipse。JPlugin引入插件的思想,插件、扩展点、扩展等基本概念都有实现,插件的生命周期有完整的实现。插件式架构的核心就是,在可能变化的地方定义扩展点,让所有产生变化的地方都扩展来实现;同时,用插件在组织应用软件的模块,最大限度实现松耦合和依赖倒置;还有,由于可以在扩展点方便地管理所有扩展,这对系统的监控以及开发高性能的应用都提供了很多方便。

使用JPlugin,对于应对复杂的应用以及应用未来的变化应该说是非常好的选择;同时,对于一次性的或者较简单的应用来说,JPlugin也提供了现成的核心插件可供使用。这些基本插件提供了诸如MVC、交易、日志、数据持久化(提供了Mybatis和Hiberinate的集成器)、缓存、调度等功能,可以方便地快速开发应用。

2.如何引用

在Maven项目中增加如下依赖:

<dependency>
		<groupId>net.jplugin.mvn</groupId>
		<artifactId>jplugin-core</artifactId>
		<version>1.4.0-SNAPSHOT</version>
	</dependency>
	<dependency>
		<groupId>net.jplugin.mvn</groupId>
		<artifactId>jplugin-web</artifactId>
		<version>1.4.0-SNAPSHOT</version>
	</dependency>
	<dependency>
		<groupId>org.mybatis</groupId>
		<artifactId>mybatis</artifactId>
		<version>3.2.8</version>
	</dependency>
	<dependency>
		<groupId>javax.servlet</groupId>
		<artifactId>servlet-api</artifactId>
		<version>2.4</version>
		<scope>provided</scope>
	</dependency>
	<dependency>
		<groupId>javax.servlet.jsp</groupId>
		<artifactId>jsp-api</artifactId>
		<version>2.1</version>
		<scope>provided</scope>
	</dependency>
	<dependency>
		<groupId>mysql</groupId>
		<artifactId>mysql-connector-java</artifactId>
		<version>5.1.36</version>
		<scope>provided</scope>
	</dependency>
	<dependency>
		<groupId>junit</groupId>
		<artifactId>junit</artifactId>
		<version>3.8.1</version>
		<scope>test</scope>
	</dependency>

#3.相关配置文件 ##数据库配置 数据库配置文件部署以后发布在在WEB-INF/classes/config目录下面。

#jdbc driver class name
driverClassName=com.mysql.jdbc.Driver
#the database url
url=jdbc:mysql://localhost:3306/weapp?useUnicode=true&amp;characterEncoding=utf8

dbuser=root
dbpassword=toor

maxActive=100 
#max idle connections 
maxIdle=50
#max wait seconds till time out
maxWait=2000

##日志配置 日志配置文件在 WEB-INF/classes/config/log4j.properties,默认配置如下,一般无需修改。另外,Jplugin体系支持专用的单独日志文件,请在后面“日志”章节查看。

log4j.rootLogger=ERROR, root,stdout
log4j.appender.root=org.apache.log4j.DailyRollingFileAppender
log4j.appender.root.file=${work-dir}/logs/root
log4j.appender.root.DatePattern='.'yyyy-MM-dd'.log'
log4j.appender.root.layout=org.apache.log4j.PatternLayout
log4j.appender.root.layout.ConversionPattern=%d %-5p %F %L - %m%n

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%p [%c] %m%n

##Plugin注册配置 WEB-INF/classes/config/plugin.cfg文件注册一个或者多个Plugin类。在“Plugin与Plugin注册”章节有详细介绍。

#4.Plugin与Plugin的注册 ##Plugin类及其作用

在JPlugin项目中,理论上所有代码都被分到不同的Plugin当中,在实际使用中,一个Plugin一般为Eclipse中一个Java项目。

每一个Plugin必须包含一个Plugin主类,该类继承自AbstractPlugin父类。Plugin主类为该Plugin在软件体系中的实际标识,在主类注册该Plugin的扩展点、扩展,提供该Plugin的优先级。

##系统级Plugin和用户级Plugin Plugin分两类:系统级Plugin、用户级Plugin。系统级Plugin为框架本身提供的Plugin。用户级Plugin为在应用中开发的Plugin。

##系统级别Plugin的注册和加载 系统级Plugin无需注册,系统启动时自动加载。系统级Plugin的优先级都小于0(负数),所以一般会优先加载。

##用户级Plugin的注册和加载

用户级Plugin需要在WEB-INF/classes/config/plugin.cfg文件中进行注册,这个文件中可以注册一个或者多个类,每一行代表一个类名称。比如:

com.hello.HelloPlugin

#5.Web控制器

Web控制器提供一个类作为Web请求的源。JPlugin提供两种格式的Web控制器,基础Web控制器和扩展Web控制器。

##基础Web控制器

基础Web控制器不需要继承任何父类,但是每个方法都需要传入HttpServletRequest和HttpServletResponse对象作为参数。下例支持 http://xxxxx/custbasic.do 访问:

Plugin.java这样写:
public class Plugin extends AbstractPlugin{
public Plugin(){
		ExtensionWebHelper.addWebControllerExtension(this, "/custbasic", CustomerControllerBasic.class);
}
public void init() {
}
@Override
public int getPrivority() {
	return 0;
}
}

CustomerControllerBasic.java:
public class CustomerControllerBasic {
public void list(HttpServletRequest req,HttpServletResponse res) {
   。。。。。。
}
}

##扩展Web控制器

扩展Web控制器需要继承AbstractExController父类,每个方法都不需要参数。下例支持 http://xxxxx/list.do 访问:

Plugin.java这样写:
public class Plugin extends AbstractPlugin{
public Plugin(){
        ExtensionWebHelper.addWebExControllerExtension(this, "/cust", CustomerController.class);
}
public void init() {
}
@Override
public int getPrivority() {
	return 0;
}
}

CustomerControllerBasic.java:
public class CustomerControllerBasic extends AbstractExController{

public void list() {
     //可以调用getParam, getAttr,setAttr等父类方法
     //可以调用renderJsp、renderJson等方法
}
}

#6.开发和使用服务

如果想发布一些Singleton的服务,并通过ServiceFacotory.getService()方法获取到,可以先在Plugin类中把服务扩展出去,然后所有的代码中都可以使用了:比如在Plugin类构造函数中加入如下代码片段: ExtensionServiceHelper.addServiceExtension(this,”s1” , MyService.class); 然后在需要的地方,就可以用ServiceFactory.getService("s1",MyService.class);

##注意: ServiceFactory.getService(cls)的会最终实现为下面的代码:ServiceFactory.getService(cls.getName(),cls); 所以,鼓励把服务的实现和接口分离。然后,把注册的服务名字定位接口类的全名。如此就可以用 ServiceFactory.getService(接口类) 来获取服务了。

#7.开发和使用业务规则服务 ##关于业务规则服务: 业务规则服务运行过程中被系统拦截,并增加事务和日志等必要内容,日志通过Annotation来标记。 业务规则服务必须有一个接口和实现类,接口中需要对每一个方法用@Rule的标记进行标记。一般有两种用法; 不进行事务控制: @Rule 进行事务控制: @Rule(methodType=TxType.REQUIRED)

##业务规则服务注册: 在Plugin类构造函数中加入代码片段: ExtensionCtxHelper.addRuleExtension(this, ICustomerService.class.getName(), ICustomerService.class, CustomerServiceImpl.class);

##业务规则服务使用: ICustomerService svc = RuleServiceFactory.getRuleService(ICustomerService.class);

##业务规则服务的日志记录: 业务规则服务运行过程中,默认记录在日志:${tomcatdir}/nswork/logs/rulelog.log当中。记录了规则的执行、消耗的事件、事务有无完成等。

#8.使用日志 ##日志文件说明 文件名 日志内容 Root 一般日志 Rulelog.log 记录业务规则日志 Request.log 记录请求日志

##获取Root日志服务 ServiceFactory.getService(ILogService.class).getLogger("com.abc.xxx") #9.使用独立日志文件

如果在应用中由于需要把一些信息记录到单独的日志文件当中,可以通过插件动态配置: ServiceFactory.getService(ILogService.class).getSpecicalLogger ("mylog.log")

#10.使用Mybatis

#11.使用Hiberinate 略,目前不要使用Hiberinate #12.发布和使用Restful服务 ##Resutful服务说明 Restful服务发布机制可以把一个Java类或方法发布为服务,进行跨平台的调用。 ##Restful服务的发布 加入有一个加法计算的类如下,注意需要增加@Para的标记,否则默认为arg0,arg1,…… public class AddService { public String add(@Para(name="a") int a, @Para(name="b") int b){ return a-b +""; } }

在Plugin类的构造函数中加入下面代码,将把整个类的所有方法都发布为Restful服务

ExtensionWebHelper.addRestMethodExtension(this, "/addRest", AddService.class);

在Plugin类的构造函数中加入下面代码,将只把add方法发布为Restful服务,其他方法不发布

ExtensionWebHelper.addRestMethodExtension(this, "/addRest", AddService.class,”add”);

##Restful服务调用地址和参数

调用的URL地址,上面例子中发布的Add服务调用的URL地址为 http://..../addRest/add.do

传入参数规则:每一个参数对应一个http的参数名称,如果是简单类型,直接传值。如果是复杂类型,请把参数值序列化为JSON字符串。 比如上面的服务可以使用 http://xxx/addRest/add.do?a=1&b=2 调用。

返回值结构 返回值为json结构如下:

{success:true,"code":0,"message":null,"content":{"return":"1"}}

  • 其中success=true表示成功,否则表示失败。
  • 档success=false时,code表示错误代码。
  • 如果不成功message域保存错误描述。
  • 如果成功content/return域保存返回结果。

##在Rest方法实现中如何控制各个返回的JSON属性 可以在Restfule方法的实现中用RestMethodState类来控制rest返回的固定属性(success、code、msg):

  • 设置是否成功:RestMethodState. setSuccess()
  • 设置返回编码(错误码):RestMethodState. setCode()
  • 设置错误信息:RestMethodState. setMessage()

复杂类型处理规则 对于复杂类型的参数或者返回值,都会序列化为标准的Json进行传递。比如 {name:”zhangsan”,age:1} #13.发布和使用远程服务 ##远程服务说明 相对于Restfule的服务,远程服务可以通过JpluginClient进行更直接的调用,其体验就像调用本地方法,并且有更高的性能。 ##远程服务的发布 假如有一个减法计算类如下 public class SubService { public int sub (int a,int b){ return a-b; } } ##发布远程服务,在Plugin类的构造函数中增加如下代码: ExtensionWebHelper.addRemoteCallExtension(this, "/subRemote", SubService.class);

##远程服务的调用

调用远程服务需要依赖 jplugin-client 或者 jplugin-core. 并需要开发一个本地接口

public interface ISubService {
public int sub (int a,int b);
}

调用代码如下:

public class SubRemoteClient {
public static void main(String[] args) {
	Client<ISubService> client = ClientFactory.getThreadLocalClient(ISubService.class);
	client.setServiceBaseUrl("http://localhost:8080/jplugin-study/subRemote/");
	ISubService obj = client.getObject();
	int ret = obj.sub(5, 3);
	System.out.println("resuult = "+ret);
}
}

##复杂类型处理规则

对于参数或者返回值是复杂类型的情况,采用Java的序列化处理。

#14.使用HTTP请求过滤器