posts - 78, comments - 34, trackbacks - 0, articles - 1
  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

2010年2月9日

       我们的课程即将结束,有一种想立即进入公司工作的冲动,也有一种对这种整日在学校学习生活的眷恋。

      

       培训学校都是骗子,这是在网上见得多的传言。在我想找一家培训机构提升自己的技术时,现实和这些话语让我有些的警觉。为了找一家好的培训机构,我使用了两个月的业余时间不停的在网上转悠,不停的对比各培训机构的课程,对比视频,对比网上的评论...。因为我已经有近三年的桌面相关开发经验,我需要的课程内容是提高技术。最终找到了感觉上适合我的传智播客!(价钱也比较合理)

 

       我是2009115日班的,来到北京我不大喜欢学校推介的住宿环境,幸好我提前两天来到这里。第二天便与几个同学在外面找了个租处——唐家岭, 这里的环境还算不错,来到之后才知道唐家岭已经是全国知名的地境(蚂族)。这里吃饭的地方比较多,价格还算合理,多尝试了几家饭店,选择了我们比较喜欢的长吃(6-10元)。这里也有早餐,我一般都在饭店里吃(3元左右),特殊情况我才会在马路边上摆的摊子买点早餐。

 

       上了几天的课程,我已深觉到我的选择是正确的。来之前,家人和朋友还担心学校的可信度,现在他们是放心了。授课内容和方式正如传智播客所倡导的——深度,这是他们的文化,这是正确的道路。

 

       传智播客提倡学生们写学习日志,按要求完成后还有奖金,这是一个非常好的激励学习方式,当然也这是一个非常好的推广学校的方式。这一点也非常吸引我,所以在我来之前就做了写学习日志的决定。因为我知道我不想终生做为一个技术人员,即使做为一个技术人员做到一定的时候写些东西是必须的。可能会写技术文章、项目文档...。其次,我想将学习的每一项技术详细认真的总结,深入他们的原理。这些促使做了写日志的决定。

 

       但来到学校学习了几天才发现,要详细认真的总结所学习的内容那是不可能的。课后的每天晚上我都坚持写学习日志,忙的时候写到凌晨2:00(偶尔)。如果我能详细认真的总结出每天的学习内容,那我已经可以出三四本书籍了。

 

       至于写学习日志这一点,要适力而为。如果想写好学习日志,就要投入时间和精力,这样就减少了动手练习的时间,对于新人来说这是致命的。我一般都是使用休息的时间来动手。所以,写学习日志自己要打量好了,要写到什么程度、要做多少练习。

 

       在来传智之前,也有看到网上骂传智的贴子。这帖子无论是来自真实的学员、还是来自某些竞争对手。强大而美好的东西自然能证实自己。一个再好的人,怎么会得到所有的人认可!在学校里学习的都是好学生吗?都是努力学习的学生吗?肯定不是,似乎在哪里都能看到这一部分人,当然我以前也不那么喜欢学习。既然远远来到这里学习,也想找一份好工作,那就努力学习吧!

 

       在近期课程将要结束时,有到学校来招聘的公司。这些信息是即时在学校的网站上发布。

有的同学已经与公司签订了合同。但来直接来学校招聘的公司一般给的工资都不会太高,也有给的还算理想的,主要看个人能力。我想回大连工作,没有参加他们的面试。课程结束后我就回大连,虽然老师们不建议我回去...

 

       我要感谢传智播客那些认真、负责、可爱的老师,张孝祥、方力勋、冯威、佟刚、汤阳光、徐培成、毛伟、黎活明,他们让我大丰收。感谢他们的殷勤付出,祝他们身体健康,工作愉快!(话外,上老张和老黎的课要注意下课时及时打水和去卫生间,这两人太尿性了。)

 

       现在WEB开发、桌面开发对我来说已经没多大神秘了,但需要我学习的内容仍然很多很多!    

 

       我时而为自己的命运庆幸,我的道路让我深感到了他的正确!

 

posted @ 2010-03-16 21:27 長城 阅读(2968) | 评论 (6)编辑 收藏

    剩下5天的内容是ERP进销存项目和Linux,这几天我将为今后的工作做准备,所以可能不再学习总结了!

posted @ 2010-03-11 21:11 長城 阅读(1514) | 评论 (0)编辑 收藏

       今日继续我们的Axis学习,我只进行一下简单的总结。有机会再补上吧!

 

       昨天我们学习了,使用WebService进行运程调用,传递基本类型数据和类实体数据。在传递基本数据类型时,我们不需要进行任何操作。但在传递类实体类型数据时,我们需要在服务器端和客户端进行序列化和反序列化注册。那还有哪些数据传递是我们需要注意的?

 

       基本数据类型数组和集合、类类型数组和集合、远程异常。在传递基本类型数组或集合时,我们不需要添加任何声明。但在传递类类型数组或集合时,需要添加与传递类实例进行相同的注册。远程异常应该如何传递?

 

       远程异常,需要通过fault元素注册一个 远程异常。

 

       上面我们对基本数据类型和类类型的简单传输有了基本的认识,但是复合类型应该如何传递呢?类套类,这样下去,难道我们需要手动添加注册信息吗?当然不需要,Axis为我们提供了相应的工具,通过WSDL生成客户端和服务器端Java类,通过Java类(接口)生成WSDL文档,在此我就不详细总结了。

 

       除了Axis,老徐又给我们介绍了xfire,它也是WebService的一种实现工具。在此也不做总结了。

 

       有位网友跟我说还有一种WebService——xcf,是目前最流行的,我并未对此进行埋阅。但我想说的是,以后可能还会有其他流行的WebService实现,但无论出什么样的新东西,它的核心思想都是一样的。这也是我来传智学习的一大原因,如果只知道一种工具如何使用,而不知道它的道理,确实不高明。如果掌握了它的道理,那就是以不变应万变了!

 

posted @ 2010-03-10 21:42 長城 阅读(1515) | 评论 (0)编辑 收藏

     摘要:        WebService课程由徐培成老师主讲,依然发扬传智播客的特点——深入理论和实践。今天老徐讲的原理的专业术语比较多,但我只做WebService的应用总结。如果你的Java和JavaWeb基础好些,我想你看到WebService的应用,自然就能想到它的实现原理。      &nbs...  阅读全文

posted @ 2010-03-08 22:40 長城 阅读(3221) | 评论 (1)编辑 收藏

     摘要: 一、Android中的通知          一般手机上边都有一个状态条,显示电池电量、信号强度、未接来电、短信...。Android的屏幕上方也具有状态条。这里所说的通知,就是在这个状态条上显示通知。          发送通知的步骤如下: &n...  阅读全文

posted @ 2010-03-07 12:41 長城 阅读(2251) | 评论 (0)编辑 收藏

     摘要:          今日课程内容较多,我们的课上到晚7:20。   一、创建新的Activity        在进行桌面开发时,我们可以通过一个窗口上的控件事件打开另一个新的窗口。在WEB应用开发时,我们也可以通过一个连接打开一个新的页面。通过...  阅读全文

posted @ 2010-03-07 10:42 長城 阅读(6106) | 评论 (0)编辑 收藏

     摘要:        我们编写的是Andorid的HTTP多线程断点下载应用程序。因为之间我们学习的学习积累,直接使用单线程下载HTTP文件对我们来说是一件非常简单的事。那么,多线程断点下载的难点在哪里?1.多线程下载,2.支持断点。          多线程下载: &...  阅读全文

posted @ 2010-03-04 14:35 長城 阅读(5392) | 评论 (1)编辑 收藏

     摘要:        昨天我们只对Android接收网络数据进行了简单介绍,今天我们完成了Android数据存储网络部分的所有内容。在此我将对这非常重要的内容进行总结。          本篇日志是对Android与WEB应用服务之间进行数据交互的总结,下篇日志是一个经典...  阅读全文

posted @ 2010-03-04 11:34 長城 阅读(6172) | 评论 (0)编辑 收藏

既然是3G开发,网络重要性自然不必多说!Android的网络存储使用HTTP协议,我们编写的Android网络应用就相当于一个浏览器。由于Android的应用是使用Java来开发的,所以网络应用使用的也是J2SE的包。

 

       Android如何与服务器交互数据?我们可以建立一个WEB应用,这对我们来说是一件十分容易的事。在WEB应用的相关请求处理中接收Andorid提交的数据、返回XML数据或JSON数据。Android发送相应的请求并接收服务相应的数据。这就是AndroidWEB应用的数据交互。

 

       Android发送请求和获取数据如下:

String path = "http://www.android.com/images/opensourceproject.gif";

URL url = new URL(path);

HttpURLConnection conn = (HttpURLConnection)url.openConnection();

conn.setConnectTimeout(6 * 1000);

InputStream inStream = conn.getInputStream();

 

       既然获得了InputStream,那么对数据进行操作就比较容易了。

 

       通过上面的代码,可见Android的网络应用是如此容易!

 

       这只是个开始,明天继续学习网络部分!

 

posted @ 2010-03-02 22:53 長城 阅读(1467) | 评论 (0)编辑 收藏

     摘要:        早上我们简要的对SQLite进行回顾,然后将SQLite的事务管理和SQLiteDataBase提供的Insert、Update、Delete、Query方法进行了简单的讲解。          今日的重点内容是ContentProvider(内容提...  阅读全文

posted @ 2010-03-02 22:12 長城 阅读(4449) | 评论 (4)编辑 收藏

     摘要: 前两篇日志我已经总结了本地数据存储的前两种:文件和配置项。还剩下最后一种数据库存储——SQLite。   一、SQLite简介   在Android平台上,集成了一个嵌入式关系型数据库—SQLite,SQLite3支持 NULL、INTEGER、REAL(浮点数字)、TEXT(字符串文本)和BLOB(二进制对象)数据类型,虽然它支持的类型虽然只有五种,但实际上sqli...  阅读全文

posted @ 2010-03-01 22:58 長城 阅读(5797) | 评论 (5)编辑 收藏

     摘要:        在此之前的学习内容是数据存储之一文件存储。在本地存储中常用的有,文件、配置文件、数据库。前面的学习主要是针对本地文件的。我认为可以把SharedPreferences看做是配置文件,虽然它也是采用XML格式存储的。          比如我们使用的桌...  阅读全文

posted @ 2010-03-01 15:01 長城 阅读(2425) | 评论 (0)编辑 收藏

     摘要:        今日继续学习Android中使用Pull的XML解析技术实现对XML文件的解析和创建。由于明天休息,时间比较充裕,所以我也将昨天未总结的SAX解析技术在此做个总结。   一、SAX解析技术        Sax使用的是事件驱动的流式解析技术。事件驱...  阅读全文

posted @ 2010-03-01 12:01 長城 阅读(2932) | 评论 (0)编辑 收藏

     摘要:        今日重点内容是Adnroid的数据存储和访问。Android的数据存储有五种:文件 SharedPreferences、SQLite数据库、内容提供者(Content provider)、网络。今天老黎讲解Android的单元测试、文件存储和访问以及解析XML文件。   一、Android的单元测试 &nb...  阅读全文

posted @ 2010-02-27 21:31 長城 阅读(4763) | 评论 (4)编辑 收藏

 一、创建 Android工程

Project name:SendMessage

BuildTarget:Android2.1

Application name:发送短信

Package name:com.changcheng.Activity

Create Activity:SendMessage

Min SDK Version:7


二、编辑工程

1.编辑strings.xml文件内容为:

<?xml version="1.0" encoding="utf-8"?>

<resources>

<string name="hello">请输入手机号码:</string>

<string name="app_name">发送短信</string>

<string name="content">请输入信息内容:</string>

<string name="send">发送</string>

</resources>


2.编辑main.xml文件内容为:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical" android:layout_width="fill_parent"

android:layout_height="fill_parent">

<!-- 请输入手机号码标签 -->

<TextView android:layout_width="fill_parent"

android:layout_height="wrap_content" android:text="@string/hello" />

<!-- 手机号码编辑框 -->

<EditText android:layout_width="fill_parent"

android:layout_height="wrap_content" android:id="@+id/mobile" />

<!-- 请输入信息内容标签 -->

<TextView android:layout_width="fill_parent"

android:layout_height="wrap_content" android:text="@string/content" />

<!-- 信息内容编辑框 -->

<EditText android:layout_width="fill_parent"

android:layout_height="wrap_content" android:minLines="3"

android:id="@+id/message" />

<!-- 发送按钮 -->

<Button android:layout_width="wrap_content"

android:layout_height="wrap_content" android:text="@string/send"

android:id="@+id/send"/>

</LinearLayout>

注意,我们在电话号码输入框和拨打电话按钮中添加了android:id属性。如电话号码输入框的android:id=”@+id/mobile”@代码R.java+id代码添加id静态内部类,mobile代表向id类中添加一个常量成员。ADT将自动为我们生成常量值。

android:minLines设置信息内容编辑框的最小行数。


3.编辑Call.java内容:

package com.changcheng.activity;


import java.util.List;

import android.app.Activity;

import android.os.Bundle;

import android.telephony.SmsManager;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.Button;

import android.widget.EditText;


public class SendMessage extends Activity {

/** Called when the activity is first created. */

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

// 根据ID获取按钮

Button button = (Button) this.findViewById(R.id.send);

// 注册按钮被单击事件

button.setOnClickListener(new OnClickListener() {


@Override

public void onClick(View v) {

// 根据ID获取手机号码编辑框

EditText mobileText = (EditText) findViewById(R.id.mobile);

// 获取手机号码

String mobile = mobileText.getText().toString();

// 根据ID获取信息内容编辑框

EditText messageText = (EditText) findViewById(R.id.message);

// 获取信息内容

String message = messageText.getText().toString();

// 移动运营商允许每次发送的字节数据有限,我们可以使用Android给我们提供 的短信工具。

if (message != null) {

SmsManager sms = SmsManager.getDefault();

// 如果短信没有超过限制长度,则返回一个长度的List

List<String> texts = sms.divideMessage(message);

for (String text : texts) {

sms.sendTextMessage(mobile, null, text, null, null);

}

}

}

});

}

}

sms.sendTextMessage(destinationAddress, scAddress, text, sentIntent, deliveryIntent)

destinationAddress:接收方的手机号码

scAddress:发送方的手机号码

text:信息内容

sentIntent:发送是否成功的回执,以后会详细介绍。

DeliveryIntent:接收是否成功的回执,以后会详细介绍。


4.编辑AndroidManifest.xml内容:

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

package="com.changcheng.activity" android:versionCode="1"

android:versionName="1.0">

<application android:icon="@drawable/icon" android:label="@string/app_name">

<activity android:name=".SendMessage" android:label="@string/app_name">

<intent-filter>

<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>

</application>

<uses-sdk android:minSdkVersion="7" />

<!-- 注册发送短信的权限 -->

<uses-permission android:name="android.permission.SEND_SMS" />

</manifest>

注册发送短信的权限,如果没有注册这个,将使用不了系统的发送短信功能。以后在我们的应用程序开发中,有使用到系统功能的必须在这个文件中进行注册。我们可以查看Android的帮助手册都有哪些功能。(.../android-sdk-windows/docs/reference/android/Manifest.permission.html


三、启动模拟器

我们给谁发短信?我们可以启动两个模拟器。使用一个模拟器给另一个模拟器发信息。首先我们使用工具栏上的手机图标再添加一个Android2.1的模拟器,另记一个名称。


在启动两个模拟器之前,我们需要模拟器能“接收到信号”。如果我们的机器是联网的,启动模拟器后,主界面显示信号强度的旁边会有一个3G的字样,这说明模拟器已经能接收到信号了。如果我们的机器不能联网,那么将自己的IP地址、网关和DNS服务器都设置为相同的值,比如都设置为192.168.0.100。如果我们的机器是在局域网下,但没有联网,那么将自己的网关和DNS设置为路由的IP即可,一般情况下路由的IP192.168.0.1


OK,现在我们启动两个模拟器!


四、发送短信

我们启动模拟器后,可以看到模拟器窗口的标题栏上有555455556的字样。这是模拟器监听的端口即——127.0.0.15554


在工程上右键,Run As Android Application,选择其中的一个模拟器。比如选择了端口为5554的模拟器。OK,程序被加载到模拟器中了,会被自动运行。


我们在电话号码编辑框中输入5556(接收端模拟器的端口号),点击发送按钮!


OK,你看到效果了吗?5556主界面,显示信号强度的旁边显示收到新短信。


五、使用ADT插件发送短信给模拟器

如果机器太慢,无法启动两个模拟器,我们可以只启动一个模拟器。然后在菜单windows->show view->other->Android->Emulator Control打开Emulator Control面板。


Telephony Actions分组框中,Voice是呼叫,SMS是发送短信。Incoming number是模拟器的端口号,我们也可以使用这个功能给我们的模拟器拨打电话或发送短信。


posted @ 2010-02-27 00:20 長城 阅读(3498) | 评论 (0)编辑 收藏

 

一、创建 Android工程

Project name:Call

BuildTarget:Android2.1

Application name:拨打电话

Package name:com.changcheng.Activity

Create Activity:Call

Min SDK Version:7


二、编辑工程

1.编辑strings.xml文件内容为:

<?xml version="1.0" encoding="utf-8"?>

<resources>

<string name="hello">请输入 手机号码:</string>

<string name="app_name">拨打电话</string>

<string name="button_call">呼叫</string>

</resources>



2.编辑main.xml文件内容为:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical" android:layout_width="fill_parent"

android:layout_height="fill_parent">

<!-- 标题标签 -->

<TextView android:layout_width="fill_parent"

android:layout_height="wrap_content" android:text="@string/hello" />

<!-- 电话号码输入框 -->

<EditText android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:id="@+id/mobile"/>

<!-- 拨打电话按钮 -->

<Button android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="@string/button_call"

android:id="@+id/button_call"/>

</LinearLayout>

注意,我们在电话号码输入框和拨打电话按钮中添加了android:id属性。如电话号码输入框的android:id=”@+id/mobile”@代码R.java+id代码添加id静态内部类,mobile代表向id类中添加一个常量成员。ADT将自动为我们生成常量值。


3.编辑Call.java内容:

package com.changcheng.Activity;


import android.app.Activity;

import android.content.Intent;

import android.net.Uri;

import android.os.Bundle;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.Button;

import android.widget.EditText;


public class Call extends Activity {

/** Called when the activity is first created. */

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

// 根据ID获取按钮

Button button = (Button) this.findViewById(R.id.button_call);

// 为按钮添加被单击事件

button.setOnClickListener(new OnClickListener(){


@Override

public void onClick(View v) {

// 根据ID获取编辑框

EditText editText = (EditText) findViewById(R.id.mobile);

// 获取电话号码

String mobile = editText.getText().toString();

// 生成呼叫意图

Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:"+ mobile));

// 开始呼叫

startActivity(intent);

}

});

}

}



4.编辑AndroidManifest.xml内容:

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

package="com.changcheng.Activity"

android:versionCode="1"

android:versionName="1.0">

<application android:icon="@drawable/icon" android:label="@string/app_name">

<activity android:name=".Call"

android:label="@string/app_name">

<intent-filter>

<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>


</application>

<uses-sdk android:minSdkVersion="7" />

<!-- 注册使用拨打电话功能的权限 -->

<uses-permission android:name="android.permission.CALL_PHONE"/>

</manifest>

注册使用拨打电话功能的权限,如果没有注册这个,将使用不了系统的拨打电话功能。以后在我们的应用程序开发中,有使用到系统功能的必须在这个文件中进行注册。我们可以查看Android的帮助手册都有哪些功能。(.../android-sdk-windows/docs/reference/android/Manifest.permission.html


三、启动模拟器

我们给谁打电话?我们可以启动两个模拟器。使用一个模拟器给另一个模拟器拨打。首先我们使用工具栏上的手机图标再添加一个Android2.1的模拟器,另记一个名称。


在启动两个模拟器之前,我们需要模拟器能“接收到信号”。如果我们的机器是联网的,启动模拟器后,主界面显示信号强度的旁边会有一个3G的字样,这说明模拟器已经能接收到信号了。如果我们的机器不能联网,那么将自己的IP地址、网关和DNS服务器都设置为相同的值,比如都设置为192.168.0.100。如果我们的机器是在局域网下,但没有联网,那么将自己的网关和DNS设置为路由的IP即可,一般情况下路由的IP192.168.0.1


OK,现在我们启动两个模拟器!


四、拨打电话

我们启动模拟器后,可以看到模拟器窗口的标题栏上有555455556的字样。这是模拟器监听的端口即——127.0.0.15554


在工程上右键盘,Run As Android Application,选择其中的一个模拟器。比如选择了端口为5554的模拟器。OK,程序被加载到模拟器中了,会被自动运行。


我们在电话号码编辑框中输入5556(接收端模拟器的端口号),点击呼叫按钮!


OK,你看到效果了吗?5554模拟器显示正在呼叫,5556模拟器有来电显示...


五、使用ADT插件呼叫模拟器

如果机器太慢,无法启动两个模拟器,我们可以只启动一个模拟器。然后在菜单windows->show view->other->Android->Emulator Control打开Emulator Control面板。


Telephony Actions分组框中,Voice是呼叫,SMS是发送短信。Incoming number是模拟器的端口号,我们也可以使用这个功能给我们的模拟器拨打电话或发送短信。

 

posted @ 2010-02-26 23:00 長城 阅读(3704) | 评论 (0)编辑 收藏

     摘要:   万众瞩目,Android终于开课了。本课程有黎活明老师主讲,讲课细致而流畅。想做细他的课程总结,那我没时间和精力了。也是总结个框框,虽然是个框框,但如果看着我的日志跟着我做,也能做起Android开发。 今日的重点内容是,3G简介、Android简介、Android的开发环境搭建、HelloWorld程序、Android程序运行过程、拨打电话小程序、发送短信小程序。我们跳...  阅读全文

posted @ 2010-02-26 21:21 長城 阅读(5326) | 评论 (2)编辑 收藏

 我今天用Struts2Spring整合,但部署到Tomcat(tomcat-6.0.20)时,WabAppClassLoader却给我抛出了一个异常:ClassNotFindException:xxx.ActionServletActionServletStruts1中使用的Action控制器。但我使用的是Struts2啊!

我反复查看自己的配置文件和使用到的Jar包,没有任何问题。然后到网上搜索答案,出现这个问题的人还不少。有的是使用Strut1出现这个问题,那么他可能是没有导入Struts1的核心包导致的。至于使用Struts2也出现这个问题多少就有些奇怪了。

一些网友给出答案,是将Tomcat删除,然后安装一个新的Tomcat这样就可以解决问题。确实如此,我使用这个方法解决了问题。但为什么会出现这个问题呢?我最先想到的是Tomcat可能有缓存功能,于是我查看Tomcat的目录。发现它有一个temp目录和一个work目录,temp目录是做什么用的我不知道,work目录是为我们生成JSPclass文件。

问题就可能出现在这两个目录,所以以后遇见莫名其妙的问题可以删除这两个目录中的内容,然后再试一试。

posted @ 2010-02-25 23:58 長城 阅读(434) | 评论 (0)编辑 收藏

       Struts2第四天,正如预期Struts2的内容还没有讲完,还需要再加一天课。按照正常的授课方式,Struts2在四天内是可以讲完的,但这可能是老张最后一次讲Struts2,所以他要讲得细致些。

 

       今天的重点内容是Struts2的表单错误信息排版、Struts2中的FreeMark、和Struts2中的UI标签。老张计的比较细致,我做总结就不做的那么细致了。

 

一、Struts2的表单错误信息排版

       Struts2的表单错误信息排版是一个比较常见的问题,但网给所见到的解决方案似乎并不正统。老张给出了他的解决方法。

 

       通过之前的学习,我们知道Struts2中的大部分数据交互操作是由ValueStack来完成的。错误信息也是如此。我们在后台使用配置文件校验或硬编码校验,Struts2将错误信息存放在类型为MapfieldErrors对象中。

 

       我们可以在页面表单字段的后边添加此错误信息,比如在user.name表单字段后边添加<s:property value="fieldErrors['user.name'][0]"/>,这样错误信息可以显示在对应的字段后边。这是针对我们使用非struts2ui标签时(使用提htmlui标签)。但这里有一个问题,如果我的表单字段特别的多,难道我要手动为每个字段添加一个这样的错误信息,这多少有些麻烦,而且以后字段有什么变化还需要行动更改...

 

       Struts2在各方面都做的非常细致和人性化,Struts2UI标签等主要是使用FreeMark来实现的。Struts2使用FreeMar来实现模板和主题,那么我们回显表单数据和错误信息也可以使用FreeMark来实现。此时我们的表单需要使用struts-tags提供的标签来定义,然后我们修改它的ftl模板文件。

 

       下面我们看看一下应该如何修改ftl模板文件,我们以UI标签<s:textarea>为例。Struts2的模板文件存在哪?在Struts2的核心包中的template.xhtml中,textarea.ftl文件内容:

<#include "/${parameters.templateDir}/${parameters.theme}/controlheader.ftl" />

<#include "/${parameters.templateDir}/simple/textarea.ftl" />

<#include "/${parameters.templateDir}/xhtml/controlfooter.ftl" />

      

       我们只需修改 controlheader.ftlcontrolfooter.ftl即可,难道我们需要修改Struts2核心包中的内容?当然不用,这一点Struts2已经为我们考虑到了。我们将这两个文件解压缩并放到WebRoot目录下的”template/xhtml”目录,必须是xhtml目录。看default.properties文件中的这断配置:

### Standard UI theme

### Change this to reflect which path should be used for JSP control tag templates by default

struts.ui.theme=xhtml

struts.ui.templateDir=template

#sets the default template type. Either ftl, vm, or jsp

struts.ui.templateSuffix=ftl

Struts2会先到我们的WebRoot目录搜索相关ftl文件,如果没有才到自己的包中找。

 

       我们的目标,要使用Struts2UI标签并将错误信息显示在标签的旁边。所以我们修改这两个文件的内容为:

controlheader.ftl

<#--

       Only show message if errors are available.

       This will be done if ActionSupport is used.

-->

<#assign hasFieldErrors = parameters.name?? && fieldErrors?? && fieldErrors[parameters.name]??/>

<#if parameters.labelposition?default("") == 'top'>

    <td align="left" valign="top" colspan="2"><#rt/>

<#else>

    <td class="tdLabel"><#rt/>

</#if>

<#if parameters.label??>

    <label <#t/>

<#if parameters.id??>

        for="${parameters.id?html}" <#t/>

</#if>

<#if hasFieldErrors>

        class="errorLabel"<#t/>

<#else>

        class="label"<#t/>

</#if>

    ><#t/>

<#if parameters.required?default(false) && parameters.requiredposition?default("right") != 'right'>

        <span class="required">${stack.findValue("getText('requiredmark')")}</span><#t/>

</#if>

${parameters.label?html}<#t/>

<#if parameters.required?default(false) && parameters.requiredposition?default("right") == 'right'>

 <span class="required"><@s.text name="requiredmark"></@s.text></span><#t/>

</#if>

${parameters.labelseparator?default(":")?html}<#t/>

<#include "/${parameters.templateDir}/xhtml/tooltip.ftl" />

</label><#t/>

</#if>

    </td><#lt/>

<#-- add the extra row -->

<#if parameters.labelposition?default("") == 'top'>

</tr>

<tr>

</#if>

 

controlfooter.ftl

${parameters.after?if_exists}<#t/>

    </td><#lt/>

<#assign hasFieldErrors = parameters.name?? && fieldErrors?? && fieldErrors[parameters.name]??/>

<#if hasFieldErrors>

    <td ><#rt/>

<#list fieldErrors[parameters.name] as error>

        <span class="errorMessage">${error?html}</span><#t/>

</#list>       

    </td><#lt/>

</#if>

<#--

       if the label position is top,

       then give the label it's own row in the table

-->

<tr>   

</tr>

 

至于为什么这么修改,一看便知,我就不多做解释了。

 

二、Struts2如何使用Freemarker

       Struts2是如何使用Freemarker的?在struts2的核心包中有一个default.properties配置文件,Struts2的默认配置都在这个文件中。有一些配置是开启的有一些配置是关闭的。我们要想打开被关闭的配置可以在struts.xml中,添加<constant name="配置项名" value="配置项值"></constant>元素。

 

       default.properties中有一个struts.freemarker.manager.classname=org.apache.struts2.views.freemarker.FreemarkerManager配置,Struts2是通过FreemarkerManager类来实现对Freemarker的操作的。

 

       通过查看源代码,我们知道通过调用FreemarkerManager类的buildTemplateModel方法生成一个model对象,然后将这个对象放在ValueStack中提供给Freemarker的引擎使用。model中都包含发哪些数据?Freemark的模板信息自然不用说,它还包含RequestApplicationResponse等这些在WEB应用中常用到的对象。具体我就不详细列出了,大家可以查看源代码。我们在上边两个模板文件中使用到的 parameters.name也是存储在model中的。

 

       在此特别提出一个被叫做UIBean的类型,UIBean就是对应Struts2UI标签的对象实体。比如标签有namethemeid等数据,这些都会被封装到UIBean中。Freemarker也正是使用这个东东给我们生成了相应该的页面文件。    

 

三、Struts2中的UI标签

       关于各UI标签的详细使用方式,在此就不做总结了。

 

       在实际开发中有一个重要的问题需要我们解决,比如有一个选择个人喜好的表单。我们需要通过一个Action的方法(likesUI)将喜好列表提供给页面,可以让用户选择。但在应用提交选择进行表单校验时,用户提交的数据不合法,我们需要重新返回到用户选择的界面。此时,我们需要调用 likesUI,获取列表将数据提供给用户选择的界面。

 

       我们可以在Action中添加”<result name="input" type="chain">likesUI</result>”,使其发生错误时直接跳转到likesUI,我们也需要在likesUI中添加一个 名称为inputresult标签,但这个标签的值不能为likesUI,否则会递归调用,直到缓存溢出。我们应将它的值指定为likesUI.jsp页面。但即使指定了这个页面,Struts2的内部实现方式也不会调用likesUI方法从,不会将数据传递给likesUI.jsp页面,而它直接跳转到likesUI.jsp页面。

 

       看来我们不能这么做,通过张老师对源代码的详细解析,我们只需要将likesUIAction中的likesUI的方法命名为input(),并只在likeUIAction中添加”<result name="input">/WEB-INF/pages/user/likesUI.jsp</result>“即可。为什么呢?

 

       在表单校验发生错误并使用 chain进行跳转时,会被chain拦截器给拦截了(ActionChainResult)。然后又会被拦截器validation给拦截了,它再进行表单校验进还是以前的数据,还是会出错然后它就直接跳转到我们指定的页面了。注意配置文件中的validation

<interceptor-ref name="validation">

       <param name="excludeMethods">input,back,cancel,browse</param>

</interceptor-ref>

它忽略请求为 input,back,cancel,browse请求路径。所以我们需要将我们的方法名定义为 input()

 

       我只是泛泛而结,如果想了解Struts2的更多细节请下载老张的视频看吧!

 

       最后送给大家一个Struts2开发用例参考模式图:

clip_image002
       1.用户申请注册,打开注册页面。

       2.用户提交注册申请,表单校验错误。

       3.表单校验错误不要跳转到regUser.jsp页面,而是应该跳转到RegUserUI这个Action方法。

       4.用户提交注册申请,表单校验通过。

       5.表单校验通过,调用RegUser这个Action方法进行注册。

       6.注册成功后,不要跳转到list.jsp页面。而是应该调用ListAction这个Action方法。

       7.ListAction获取所有用户信息后,跳转到list.jsp

 

       一定要记得老张还有一天的Struts2的课程,下一次课程的重点内容应该是Struts2的文件上传与下载,Struts2的防止表单重复提交,Struts2SpringAJAX整合,Struts2的插件。

 

       接下来的课程内容让我等的好久啊——Android!虽然听学习过的同学们说十分简单,但我还是迫不及待的想Android的一睹真容。我想很多人都是这样吧!那就关注我接下来6天课程的总结日志吧!

posted @ 2010-02-25 21:32 長城 阅读(569) | 评论 (0)编辑 收藏

       Struts2第三天。如果老张他喜欢头发,他肯定会把头上的头发有多少根,一根一根的数出来。他搞的太细了!

 

       今天的重点内容是国际化、表单与Action数据交互、表单校验,不多说了开始总结。

 

一、Struts2国际化

       在我们学习WEB基础的时候就讲到了国际化,Struts2中的国际化实现方式与我们之前所学习到的国际化相同。但是Struts2中对国际化做了强化支持!

 

       我们通过在struts.xml文件中加入”<constant name="struts.custom.i18n.resources" value="*.properties"></constant>”为指定全局国际化资源文件。Struts2会根据浏览器设置的语言信息运行对应的国际化文件,如果没找到就根据操作系统的语言查找指定的文件,还是没有,那就使用默认的。我们必须在struts.xml中使用定义在struts-default.xml文件中的defaultStack拦截器堆栈。使用defaultStack我们必须在package中添加extends="struts-default"属性。如果在我们的Action中没有使用任何拦截器,defaultStack默认被使用。如果有使用任何其他拦截器,我们必须手动加入defaultStack拦截器堆栈。Struts2将使用defaultStack拦截器堆栈中的i18n拦截器,为我们提供国际化支持。

 

       引入多个全局资源包的方法是在上面的value属性中添加以”,”分隔的多个资源文件。排在最后的资源包的优先级别最高(如果有重复的资源名称)。那如果有时我想在JSP页面中使用第一个资源包中的资源怎么办?Struts2为我们提供了一个标签“<s:i18n name="first.properties"></s:i18n>”,在标签体内使用到的国际化资源,全以first.properties为先。

 

       我们可以为每个Action指定一个资源包,资源包的名称与Action的名称相同。Struts2的国际化模块会先找与Action对应的资源包,如果没有就找它上一级的资源包,就这样逐级向上。如果直到类所在包的是后一层目录也没找到就使用全局资源。这有什么好处?我们可能会有多个Action使用同一个或多个相同的资源,难道我们要在每个Action对应的资源包里定义它吗?当然不需要,我们只需要把它定义在这几个Action的上级且同一目录即可。

 

       我们如何在JSP页面有使用国际化资源?Struts2tags为我们提供了“<s:text name=""></s:text>”标签,它是专门用来访问国际化资源的。除此之外我们也可以使用“<s:property value=”getText(key)”>”,这个getText()是哪来的?当然是ValueStack中的某个对象的方法喽!是类型为com.opensymphony.xwork2.DefaultTextProvider对象的。Struts2中使用OGNL操作ValueStackContextMapOGNL看到getText(key)时,它会在ValueStack中从上向上反射每一个对象,如果哪一个对象最先具有这个方法,则OGNL就调用这个对象的getText(key)方法。

 

       除了在JSP页面中需要使用到国际化资源,我们在Action中也需要使用国际化资源。此时,我们需要将我们的Action类继承自ActionSupport类,它实现了专门用于操作国际化的接口TextProviderActionSupport还实现了其他非常有用的接口,我们在下面会总结。此后,我们只需要调用getText()方法,即可获得相应的资源信息。我们的Action也可以继承自DefaultTextProvider类,但它默认情况下去搜索全局资源包,不会找Action的资源包。而ActionSUpport正如我们在上面所说,以Action的资源包优先级最高...

 

       我们知道我们可以为国际化资源指定参数,比如“regNameError={0} is invalid.”。我们在JSP页面中可以通过<s:param></s:param>标签来指定,如:

<s:text name=”regNameError”>

       <s:param></s:param>

</s:text>

<s:text name=”getText('regNameError','xxx')”></s:text>。在Action中直接使用getText(“regNameError”,”xxx”)

 

       我们之所以能够如此方便的使用国际化支持,JDK中的java.util.Locale为此做了很大的贡献。目前我们使用国际化操作,无不基于java.util.Locale。因为OGNL的强大,我们也可以在JSP页面中使用java.util.Locale还记得吗?在OGNL中调用XXX类的静态方法或成员的方式,比如调用java.util.LocalegetCountry()<s:property value=”@java.util.Locale@getDefault()”/>

 

二、表单与Action数据交互

       相比Struts1,在Strut2中的一大变化是我们不需要ActionForm了。我们在使用Struts1时,为了操作表单数据,我们需要额外定义一个继承自ActionFormFormBean。然后再将FormBean中的数据CopyBean中。

 

       Struts2是如何实现对表单数据的操作呢?Struts2ValueStack一直发辉着它的作用,ValueStack像是能完成Struts2的在所在操作,上面的国际化也是使用的ValueStack存储的。在Struts2中操作表单数据,我们需要在我们Action定义属性。在Action添加的属性可以是基本数据类型也可以是复合类型。

 

       进行表单与Action数据交互时,我们的Action类无需实现任何接口或继承自任何类。但是,我们必须在struts.xml中使用定义在struts-default.xml文件中的defaultStack拦截器堆栈。使用defaultStack我们必须在package中添加extends="struts-default"属性。如果在我们的Action中没有使用任何拦截器,defaultStack默认被使用。如果有使用任何其他拦截器,我们必须手动加入defaultStack拦截器堆栈。

 

       Struts2正是使用在defaultStack中的params拦截器,来为我们的Action设置属性值。如何使用基本类型属性和复合类型属性与JSP页面交互呢?比如在我们的RegUserAction中有一个User属性和一个字符串型的password2属性。那么我们在JSP页面中应该这样使用:

<s:form action=”RegUserAction”>

    <s:textfield name=”user.name”/>

    <s:textfield name=”user.password”/>

    <s:textfield name=”user.email”/>

    <s:textfield name=”password2”/>

    <s:submit value=”注册”></s:submit>

</s:form>

 

       点击注册按钮后,Struts2正是使用params拦截器将数据设置到RegUserAction的属性中。

 

       因为这些表单字段数据是被保存在ValueStack中,所以我们在JSP页面我们可以使用EL表达试,如:${user.name},也可以使用OGNL表达式,如:<s:property value=”user.name”/>来获取数据。

 

       除此之外,我们可以让Action实现ModelDriven接口,并将User做为它的泛型参数。如果被ModelDriven接管的实体类型中有与我们的Action属性相同的,ModelDriven的优先级高。Struts2会设置ModelDriven接管的实体类型的是属性,而忽略Action的同名属性。但这并不会引起错误,因为我们的数据全部放在ValueStack中,使用OGNL可以正确获取。我们不推荐使用ModelDriven

 

三、表单校验

       表单校验在Struts2中实现比较简单,表单数据的校验分为前台校验和后台校验。同样我们需要开启defaultStack拦截器堆栈,首先我们来看看后台校验。

 

       后台校验,我们的Action可以继承ValidationAwareSupport类和实现Validateable接口。因为我们同时需要在类中使用到国际化表单校验,所以我们可以直接继承ActionSupport类。Action类需要实现validate方法。

 

       validate方法中进行Action的属性校验,并且我们可以调用ActionSupport类的addActionError(String)方法,添加错误信息。我们可以在这里使用国际化支持,而在JSP页面中需要使用<s:fieldError>取出所有错误信息,或者指定字段名而取出某个字段的错误信息。

 

       但有一个问题,一个Action类可以有多个方法分别用于处理不同的请求。Struts2每当调用其中一个方法时都会进行表单校验,这没必要啊!所以我们在不需要进行表单校验的方法上添加“@SkipValiation”注解,这样就会被Struts2过滤掉。

 

       我们也可以添加”ActionClassName-validation.xml”配置文件,来为指定名称的Action(ActionClassName)添加表单校验。具体的配置可以参看struts2包中所带的例子程序。我们也可以使用“ActionClassName-actionname-validation.xml”来为Action类中的某个具体方法指定表单校验。

 

       前台校验,前台校验的好处不再说明了。前提我们以配置文件的方式为Action指定的表单校验,那么在JSP页面中,我们可以直接引用配置文件中的数据,实现前台校验。Struts2中的struts-tags为我们提供的表单标签中可以引用配置文件中对应的校验数据。在我们访问到某个JSP页面时,Struts2的标签会为我们自动生成相应的JavaScript代码,但Struts2的表单前台校验的这种功能还不够强大。在此就不多做总结了。

 

       老张讲得比较细致,我也仅仅是泛泛而总结。按照正常的进程,我们明天还有一天的Struts2课程,但由于时间不够,老张决定哪天同学们休息时再为我们补一天,太好了!

posted @ 2010-02-23 23:39 長城 阅读(524) | 评论 (0)编辑 收藏

     摘要: Struts2第二天,大家似乎没有从年假中苏醒过来,上课显得有些疲惫。不过还好,听课效果还不错。今日的主要内容是OGNL,Struts2使用OGNL访问contextMap和valueStack。老张很执着,他在研究Struts2时遇到的一些问题,一定要解决,并且要详细解决。通过他在讲课中,我们可以看出这一点。有开发经验的人都知道,我们时而被陌生技术的一个小细节搞的晕头转向,耗费了大半天的时间我们...  阅读全文

posted @ 2010-02-22 16:59 長城 阅读(736) | 评论 (0)编辑 收藏

     摘要:          春节假期终于结束了,假期内在北京只出去转了两天,感觉很好!剩余时间呆在屋子里写了个练习程序收获颇丰。          依然迫不及待,迫不及待的学完课程赶紧回大连。我想除了我,很多同学和网友也都期待着今天由张考祥老师主讲的Strut...  阅读全文

posted @ 2010-02-20 23:56 長城 阅读(569) | 评论 (0)编辑 收藏

     摘要:          今日教育办公系统结束。新内容有:导出数据到PDF文档、生成图表(饼图、柱图...),整体上操作比较简单。   一、创建PDF文档          我们使用iText创建PDF文档,需要到http://itext...  阅读全文

posted @ 2010-02-09 01:25 長城 阅读(470) | 评论 (0)编辑 收藏