主要详细说了创建maven+webx的工程,其他的技术只是列举了些简单的问题。内容比较多,自以为遇到的问题又怪又没什么技术含量,如果找不到更好的资料的话,大家不妨耐心看下吧……或组就有收获了呢。![大笑](http://static.blog.csdn.net/xheditor/xheditor_emot/default/laugh.gif)
1、maven技术
1.1 创建maven web工程
刚开始使用eclipse 建立,但是总出错
原因:未知。。
但是勾选上create a simple project ,写上版本信息,就可以创建maven工程了。
使用命令行创建:
mvn archetype:create -DgroupId=login -DartifactId=roma.login -Dversion=1.0.0 -DarchetypeGroupId=org.apache.maven.archetypes
这样创建的是java application 。
如果要创建web工程,则需要增减-Dpackaging=war
mvn archetype:create -DgroupId=login-DartifactId=roma.login.web -Dpackaging=war -Dversion=1.0.0-DarchetypeArtifactId=maven-archetype-webapp
这样就是maven web工程。
注意: 1、修改pom文件中的 build 选项。
2、archetype:create中是不可以有空格的,否则会报错如下:
Invalid task 'archetype':you must specify a valid lifecycle phase, or a goal in the format plugin:goalor pluginGroupId:pluginArtifactId:pluginVersion:goal
这样maven web工程就创建成功了。
体会: 直接在cmd中运行上述命令是因为在path环境变量中配置了maven的安装路径, 在创建maven工程时,会用到配置的setting.xml :
l <localRepository>D:\workspaces\maven-repos</localRepository> 设置该maven工程的依赖jar包存放位置。
l interactiveMode 交互模式: 会默认为true 有需要输入的时候会有提示
l offline 默认为false ,需联网下载jar包
l profile这里可以指定从哪里下载jar包,可以设置为自己的私服。指定自己的文件读取等。
建立maven web 工程后,pom文件中注意增加build 节点。
1.2 maven run
在遇到不知道为什么,然后项目中明明已经没有错误的时候,要记得先mvn clean mvn package等。
(ps:插入 project 也要clean )
1.3 maven create
1.3.1 pom文件
可以定义属性,有点程序中变量的味道 :
<properties>
<project.build.sourceEncoding>GB18030</project.build.sourceEncoding> <project.reporting.outputEncoding>GB18030</project.reporting.outputEncoding>
<webx3-version>3.0.0-RC1-fix8</webx3-version>
<java.version>1.6</java.version>
<java.encoding>GBK</java.encoding> <warExplodedDirectory>target/exploded/${artifactId}.war</warExplodedDirectory>
</properties>
这里定义的属性下面可以直接使用。使用${artifactId}这个访问。
<profiles> //这个和上面properties 的不同大概是定义一个读取属性的文件
<profile>
<id>dev</id>
<properties> <filterFile>src/main/filter/dev-filter.properties</filterFile>
<skipTgzPackage>true</skipTgzPackage>
</properties>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
</profiles>
然后添加build节点。
<build> 整个maven工程编译成war包时,应该读取文件的相关东西。
<finalName>weekly</finalName>
<filters>
<filter>${filterFile}</filter>
</filters>
<resources>
<resource>
<directory>src/main/resources</directory>//规定这个目录下的文件都要经过filter下,就是有可能定义一些变量相关的东西
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>${java.encoding}</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration> <webappDirectory>${warExplodedDirectory}</webappDirectory>
<webResources>
<resource>
<filtering>true</filtering> <directory>${basedir}/src/main/webapp/WEB-INF</directory>
<targetPath>WEB-INF</targetPath>
</resource>
</webResources>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.5</version>
<!-- default resource encoding -->
<configuration>
<encoding>GB18030</encoding>
</configuration>
</plugin>
</plugins>
</build>
1.3.2 dev-filter.properties
在src\main\filter 文件夹下定义一些${} 配置的文件
2、Webx 相关编写
加载web工程必须的配置文件:web.xml 这里面的servlet filter 及其相关东西都会在加载到web容器中,然后webx工程会读取webx-default.xml文件,加载里面的service。
2.1 web.xml 相关配置
日志信息,还有Filter的配置。这里没写。
<filter><filter-name></fiter-name><filter-class></fiter-class></filter>
<filter-mapping><filter-name></fiter-name><url-pattern></url-pattern></filter-mapping>
<servlet>
<servlet-name>WebxController</servlet-name>
<servlet-class>com.alibaba.webx.WebxControllerServlet</servlet-class>
<init-param>
<param-name>initAllServices</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>webx.components</param-name>
<param-value>home</param-value>
</init-param>
<init-param>
<param-name>default.component</param-name>
<param-value>home</param-value>
</init-param>
<init-param>
<param-name>log4j.configuration</param-name>
<param-value>/WEB-INF/log4j.properties</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>WebxController</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>WebxController</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
WebxController 中可以指定要加载的car component包过来,如果有子compoent 的话,需要在这里指定,并且指定一个默认寻找的地方。注意:配置后,整个项目启动的时候会先加载父项目的webx-default.xml 然后再加载各个component 中 webx.xml 文件。如果不配置,则会默认加载这个项目中webx.xml 。所以不一定都是webx-default.xml 。
2.2 webx.xml配置(如果不想了解,可以直接复制过去)
ResourceLoaderService:
<service name="ResourceLoaderService" class="com.alibaba.service.resource.DefaultResourceLoaderService">
<property name="resource.descriptors">
<value>/webroot/WEB-INF/resources.xml</value>
</property>
</service>
加载Resource 文件,设置读取resource.xml文件,//这里面主要指定一些路径的对应。
RundataService
<service name="RunDataService"
class="com.alibaba.webx.service.rundata.DefaultRunDataService">
<property name="request.buffered.class" value="com.alibaba.webx.request.context.buffered.BufferedRequestContextFactory"/>
<property name="request.lazycommit.class" value="com.alibaba.webx.request.context.lazycommit.LazyCommitRequestContextFactory"/>
<property name="request.parser.class" value="com.alibaba.webx.request.context.parser.ParserRequestContextFactory"/>
<property name="request.locale">
<property name="class" value="com.alibaba.webx.request.context.locale.SetLocaleRequestContextFactory"/>
<property name="defaultLocale" value="zh_CN"/>
<property name="defaultCharset" value="GBK"/>
</property>
<property name="request.session">
<property name="class" value="com.alibaba.webx.session.request.SessionRequestContextFactory"/>
<property name="session.cookie.domain" value="" />
<property name="session.cookie.path" value="/" />
<property name="session.cookie.maxAge" value="0" />
<property name="session.store.temporary">
<property name="class" value="com.alibaba.webx.session.store.cookie.CookieStore"/>
<property name="match" value="*" />
<property name="cookie.name" value="weekly"/>
<property name="cookie.permanent" value="false"/>
</property>
<property name="cookie.encoder.class" value="com.alibaba.webx.session.store.cookie.encoder.EncryptCookieEncoderImpl"/>
</property>
</service>
//这里没有什么特殊说明,一看就可以明白滴。
周报系统中仍然有,home这个模块,所以同时也会加载home这个组件。
<instance name="component">
//加载全局resource,和上面的ResourceService 是一样的。
piplelineService
<service name="PipelineService" class="com.alibaba.service.pipeline.DefaultPipelineService">
<property name="pipeline.default.descriptor" alue="/weekly/pipeline.xml" />
</service> //指定都要经过那些Valve 相当于过滤器,这里的配置会使该模块的所有请求都经过这样的过滤。
<service name="ModuleLoaderService" class="com.alibaba.turbine.service.moduleloader.DefaultModuleLoaderService">
<property name="module.ref.resolver">
<property name="class" value="com.alibaba.turbine.service.moduleloader.factory.SpringReferenceResolver"/>
</property>
</service> //这个ModuleService 和 home模块里面 webx.xml定义的ModuleService 其实是同一个,只是那里面又去限制一些属性。
VeloctiyService :
<service name="VelocityService" class="com.alibaba.service.velocity.DefaultVelocityService" earlyInit="true">
<property name="input.encoding" value="GBK"/>
<property name="file.resource.loader.path">
<!-- 从每个car自己的目录下面读模板文件 -->
<value>/templates</value>
<!-- 读一些全局的模板文件 -->
<value>/webroot/templates</value>
</property>
<property name="velocimacro.library" value="macros.vm"/>
</service> //解析vm
URIBrokerService :
<service name="URIBrokerService" class="com.alibaba.service.uribroker.DefaultURIBrokerService">
<property name="uri.descriptors">
<value>/weekly/uri.xml</value>
</property>
</service> //uri.xml 中 规定一些全局链接的配置 ?但是没看到用的地方 vm中weeklyServer 用了下~
BeanFactoryService :
<service name="BeanFactoryService" class="com.alibaba.service.spring.DefaultBeanFactoryService">
<property name="bean.descriptors">
<value>/weekly/classes/spring.xml</value>
</property>
</service> //初始化BeanFactoryService 这样这里的配置文件就和src/main/resources中的配置文件联系在一起了,可以把spring 文件加载进去。Spring-dao spring-biz.xml 等。
MappingService :
<service name="MappingService" class="com.alibaba.turbine.service.mapping.DefaultMappingService">
<!-- Target name => Screen module name (*.jsp,*.vm) -->
<property name="mapper.screen">
<property name="matchLastName" value="true"/>
</property>
<!-- Target name => Layout module name -->
<property name="mapper.layout">
<property name="matchLastName" value="true"/>
</property>
<!-- Target name => Layout template name -->
<property name="mapper.layout.template">
<property name="matchLastName" value="true"/>
</property>
<!-- Target name => Control module name(setTemplate method) -->
<property name="mapper.control">
<property name="matchLastName" value="true"/>
</property>
</service> //说明映射规则,不需解释
FormService :
<service name="FormService"class="com.alibaba.service.form.DefaultFormService">
<property name="form.descriptors">
<value>/WEB-INF/form.xml</value>
</property>
</service> //Form加载同样不需解释
2.3 其他相关的配置文件
同目录下:Resource.xml pipeline.xml uri.xml form.xml
在src\main\resources 目录下: spring.xml
//spring.xml 中也会加载其他的xml文件的。
其实spring 文件并不是必须的,只是使用spring会使工程好很多。
Spring.xml :
<importresource="spring/spring_dataSource.xml" />
<importresource="spring/dao/spring_dao.xml" />
<importresource="spring/biz/spring_biz.xml" />
<importresource="tddl/persistence-tddl.xml" />
<importresource="tair/tair.xml" />
<importresource="tfs/tfs.xml" />
<beanid="tairAspectj"class="com.taobao.weekly.base.annotation.TairCacheAspectj"> //tair的AOP实现
<propertyname="tairManager" ref="tairManager" />
<propertyname="tairNameSpace" value="11221" />
</bean>
<aop:aspectj-autoproxyproxy-target-class="true" />
//这样整个工程 maven + webx +spring + velocity 就ok了。
整个工程图的结构:
3、VM文件
之后就开始写VM文件。其实和普通的HTML文件差不多的。
在webapp下创建 templates 文件夹 \macro.vm ---定义所有组件都可能用到的函数
新建home 文件夹
结构应该如下:
其他的choose.html css js 可以不要,但是templates文件夹是必须的,并且最好目录名和上面的一模一样。
Control : top.vm bottom.vm 等
Layout :default.vm 呈现给用户的 布局vm
Screen : 主要页面 主要的显示逻辑等。
在screen 中新建一个VM ,如果VM中有表单,则应该在form.xml中增加相应配置。
如:
<form action=""method="post" enctype="multipart/form-data"> //这里如果有上传文件,则需加上这个 enctype="multipart/form-data"
<input type="hidden"name="action" value="UserAction"/>
#set ($group =$form.createUser.defaultInstance)
<input type="text"name="$group.userName.key"value="$!group.userName.value"/>
<input type="password"name="$group.password.key" value="$!group.password.value"/>
</form>
在jsp中form 以及标签都是一些对象,同样在velocity中也是,form是一个内置对象,#set ($group = $form.createUser.defaultInstance) 这个语句会在form中查找createUser 的group对象, 然后拿过来在页面VM中使用。这个需要在form.xml中配置:
<groupname="createUser">
<field name="userName"displayName="登录名">
</field>
<field name="password"displayName="密码">
</field>
.............
</group>
提交表单时,就可以进行验证了。同时在Action中可以通过Rundata对象得到该Group,获取其中的值。
4、action 、Screen 类的编写
Src/main/java 最重要的程序编写部分。大致可分为三个模块: (M)domain和dto分别对应数据库对象、在业务中用到的DO 模型对象(因为这个还别扭了很久的说);(V)有关展示的 module.home.screen;(C)action 和 service 相关的;如果显示页面前要处理下东西可以用screen 类 ,如果涉及到form表单之类的,就可以用action。
请求report/login.htm过来时,会先在home(默认的)文件夹下,查找templates文件夹,然后在screen 文件夹下查找login.vm ,找到之后 有pipeline 中的valve转发,有Action参数则转发到对应的Action类。找Action类时,会从指定包module.home + .action 过去查找。找到Action那个参数名(如UserAction)。如果没有action参数,则会去找对应的screen类,从指定包module.home + .screen 下开始查找,Login 类。请求是/user/login.htm 会在home \ user\templates \screen login.vm 然后action类: module.home . user .action
Screen 类: module.home. user.screen.Login。 不过一般分的时候user 或者是store 等信息时,会组成不同的component,这样不同的component 就可以指定不同的包路径,如 home 可以指定module.home ,而需要分user的话则会指定module.user等信息。所以尽量不要用绿色字表示部分。(PS蓝色和绿色部分都是猜测的……)
其他的就是一些工具类、有的是全局用到的。
注意一点:在找Screen类时,会去指定包下查找,学习资料上说是
指定包的设置
在report项目中,是在home/webx.xml 中设置的:
<servicename="ModuleLoaderService">---模块加载Service
<propertyname="module.packages">
<value>com.XXX.weekly.web.module.home</value>
</property>
</service>
Src/main/resources
主要放和业务相关的一些文件,ibatis 、 spring \ tddl tfs tair 等之类的.xml 可以使用spring加载应用中的。
再加上一些业务代码。
运行package.bat , 设置jboss server path\service\default\conf\jboss-service.xml 中 <attribute> deploy /, target/report.war 这个路径,运行jboss中的run.bat 文件就可以访问了。
5、ibatis技术
5.1 ibatis结构
上面的类图中左边 SqlMapExecutor 接口主要定义了客户端的操作行为包括 select、insert、update、delete。而右边主要是定义了当前客户端在当前线程的执行环境。当使用者持有了 SqlMapClientImpl 对象就可以使用 iBATIS 来工作了。这里还要提到另外一个类 SqlMapExecutorDelegate 这个类从名字就可以看出他是执行代理类。这个类非常重要,重要是因为他耦合了用户端的执行操作行为和执行的环境,他持有执行操作的所需要的数据,同时提供 管理着执行操作依赖的环境。所以他是一个强耦合的类,也可以看做是个工具类。
<bean id="sqlMapClient"class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
<propertyname="dataSource" ref="weekly_ds" /> ---ref 配置数据源
<propertyname="configLocation"value="/weekly/classes/sqlmap-config.xml" />---- 这里配置具体的sql语句
</bean>
<bean id="sqlMapTemplate"class="org.springframework.orm.ibatis.SqlMapClientTemplate">
<propertyname="sqlMapClient" ref="sqlMapClient" />
</bean>-----sqlMapTemplate
public class SqlMapClientTemplate extends JdbcAccessorimplements SqlMapClientOperations { // JdbcAccessor jdbc接口相关的东西
SqlMapClientOperations---jdbc的 CRUD 操作。
具体实现是:
在初始化SqlMapClientTemplate这个Bean类时,会注入属性sqlMapClient,里面有SqlMapExecutorDelegate 、SqlMapSeesion 两种对象。猜想在注入后,会对SqlMapSession对象进行一定的环境或者是状态设置。
然后实际执行调用 对应的 insertupdate 等方法时,会去调用SqlMapClientTemplate 的 execute 方法。其实最终执行的是 SqlMapExecutorDelegate里的 query select update 方法,同样会先得到Connection 然后再执行语句。
5.2 ibatis sql 语句
上面简单看下ibatis的实现。下面或许更关心些,怎么使用的。配置文件上面也已经讲过,总结sql有关的。
具体ibatis 详细内容:http://www.ibm.com/developerworks/cn/java/j-lo-ibatis-principle/index.html 可以看这里。 这里引用下,谢谢作者啦。
************************************************************************* 从网上查的其他资料结束 ***************************************************************
5.2.1 insert 语句然后返回ID
只是insert语句的话很简单,可以这样写。
<insert id="user-insert"parameterClass="user"> ---- parameterClass 可以写完整包名.类名 也可以写TypeAlias 定义好的。
<![CDATA[
insert into t_user(id,user_name,user_password,organization_id,avatar_name)values(#id#,#userName#,#userPassword#,#organizationId#,#avatarName#)
]]>
</insert>
这样就没返回值了。
返回值,可以添加selectKey
<insertid="organizationInsert">
<![CDATA[
insert intot_organization(title) values(#title#)
]]>
<selectKey resultClass="int" keyProperty="id">
select LAST_INSERT_ID()
//---注意 这里LAST_INSERT_ID()是动态函数所以不可以放在
<![CDATA[ 中
</selectKey>
</insert>
完善点可以如下:
<insertid="organizationInsert" parameterClass=”java.lang.String”resultClass=”java.lang.Integer”>
<![CDATA[
insert intot_organization(title) values(#title#)
]]>
<selectKey resultClass="int" keyProperty="id">
select LAST_INSERT_ID()
</selectKey>
</insert>
Insert 注意地方:
ibatis.xml文件突然出现了错误。说Thereis no statement named user-insert in this SqlMap. 从网上查有以下几点:
检查我自己写的,貌似都一样只有parameterClass没写,然后加上好了。
其中命名空间也容易忘记。
5.2.2 select 语句
刚开始我的select sql 语句居然是这么写的…………悲剧呀
<select id="organizationQueryAll"class="organization">
select * from t_organization
</select>
抛错:Attribute "class" must be declared for element type select .
说是select中并没有这样的class属性,上网查才知道,对于这些sql语句的参数只有如下情况:
parameterClass :入参类型的设置
resultClass :这个要写完整的包名.类名
resultMap : 这个可以不写完整的。
5.2.3 Ibatis 文件中传入参数为list的情况:
<select id="queryUserListByUserIdList" resultMap="userResult" parameterClass="java.util.List">
<![CDATA[
select * from t_userwhere id in (<iterate open="(" close=")" conjunction=","> #userIdList[]#</iterate> )
]]>
</select>
抛出错误 :
原因:是把sql语句写在了<![CDATA[ 中 然后规定不再对这样的xml语句进行解析,所以出现错误。
<select id="queryUserListByUserIdList" resultMap="userResult" parameterClass="java.util.List">
select * from t_userwhere id in (<iterate open="(" close=")" conjunction=","> #userIdList[]#</iterate> )
</select>
如上就好了。还有ibatis中 使用的一些东西其实和dao传入的参数并没有什么联系。在select语句中并不要求和dao传入的参数名字相同。
在DAO调用时,可能传入的参数为organizationId 然后如果在这个sql语句中写入organization_id = # organizationId # 其实并不会把它的值取出……大概也许。正确的说应该传过来一个Integer对象,或者是什么对象 然后##之间应该取出相应的属性。所以这个地方可以写value或者是其他任何值都可以,并不需要和传入的参数名对应。
但是一般情况下,应该指定下resultClass 或者是resultMap这样才能正常进行。
6、vm注意事项
6.1 vm中访问Context里的值
在vm中得到context的值可以直接得到
$allOrganization等. 遍历list 可以如下:
7、tddl注意事项
/**
* 传入只有用户名和密码的UserEntity,验证用户是否合法,是则返回全部信息的UserEntity(部门只有部门ID、没有Report的相关信息),否则返回null
* @param UserEntity 只有用户名和密码的UserEntity
* @return UserEntity 全部信息的UserEntity
* @throws Exception
*/
public UserEntity verifyUser(UserEntity user) throws Exception;
本来判断用户名是这样的,但是分表的时候是用user_id 来分的,所以如果直接在user表中查找,根据分表规则是找不到对应的表的……所以可以先建立一个user_name到user_id的索引表,然后先根据user_name查找到user_id 然后再用use_id 查找到整个user对象。此时有点疑问说如果现在在数据库中队user_name没有什么限制,那user_name岂不是可以重复。
以为是数据库中应该加一个唯一字段,秋年说这样并不好,是以异常的形式抛出才知道用户名已经重复了。在Action中向用户显示的时候还要对异常进行判断……这样不好滴,所以如果要保证user_name唯一,要在插入用户是判断一下。
其实TDDL的三层,不管怎么对于sql语句并不会影响到什么的。
8、svn注意事项
1、svn冲突 : 有可能是已经提交了,但是还没有更新下来。可以选择覆盖下。
2、检出svn项目
Svn服务器那里有过记录,之后删除了就好了。
再注意下,提交文件的时候。要记得先更新,然后使用与资源库同步,看那些文件要提交, 然后下面有加号显示的,就表示从本地同步资源库中。 点击这个
,会看到要从资源库同步到本地中的文件。这样可以防止忘记提交。记得学会使用这个SVN。。。。本地提交的时候要先对比下,看哪里不一样,自己改的有没什么问题,会不会把别人的弄掉。。。等等之类的。记住………………………………
9、jboss注意事项
1、如果在jboss\server\deploy\conf下已经配置好jboss-service.xml文件,在eclipse中就不需要再重新部署上去了,直接debug起来就好。就跟在外面启动起来是一样的。
2、ERROR:invalid console appender config detected, console stream is looping
Log4j.propertities 日志问题可以修改
jboss-4.2.0.GA\server\default\deploy\jboss-web.deployer\META-INF\jboss-service.xml
<attributename="UseJBossWebLoader">false</attribute>
将这个属性改成false就好了。
在多个应用程序部署,共享Jar文件的时候,可以改成true。
如果想每个应用程序都用自己独立的jar文件的,就改为false,用各自的webloader去加载自己的文件,这样不会冲突。
3、 jboss一直在starting 中,项目其实已经启动完,仍显示在starting,最后导致超时启动失败。
原因:eclipse 中判断jboss是否启动,是通过端口监听的,所以要求eclipse 中设置的jboss port端口 和 jbossServerPath\server\default\deploy\jboss-web.deployer下的 server.xml 中的Connector port 端口相同。
eclipse 中设置的jndi port 端口 和 jbossServerPath\server\default\conf\jboss-service.xml 中的jndi 端口相同。
<Connector port="80"address="0.0.0.0"
maxThreads="250" maxHttpHeaderSize="8192"
emptySessionPath="true" protocol="HTTP/1.1"
enableLookups="false" redirectPort="8443"acceptCount="100"
connectionTimeout="20000"disableUploadTimeout="true" URIEncoding="UTF-8"/>
<mbeancode="org.jboss.naming.NamingService"
name="jboss:service=Naming"
xmbean-dd="resource:xmdesc/NamingService-xmbean.xml">
<!--The call by value mode. true if all lookups are unmarshalled using
thecaller's TCL, false if in VM lookups return the value by reference.
-->
<attribute name="CallByValue">false</attribute>
<!--The listening port for the bootstrap JNP service. Set this to -1
to runthe NamingService without the JNP invoker listening port.
-->
<attribute name="Port">1099</attribute>
10、hsf遇到的错误
UndeclaredThrowableException : ---未找到HSFRequest的处理器
原因:在configserver 那里HSF服务并没有发布成功。
UndeclaredThrowableException : ---连接创建失败
原因: 目标HSF服务没起来。
未找到plugins地址。。。
原因: 做HSF服务测试指向本地的HSF plugins 时,路径中不可以有空格,最好不要用中文。