Mkyong-中文博客翻译-一-
Mkyong 中文博客翻译(一)
原文:Mkyong
从 JSF 页面向后台 bean 传递参数的 4 种方法
据我所知,从 JSF 页面向后台 bean 传递参数值有 4 种方式:
- 方法表达式(JSF 2.0)
- 问:我的钱
- f:属性
- f:setPropertyActionListener
让我们一个一个地看例子:
1.方法表达式
从 JSF 2.0 开始,允许在方法表达式中像这样传递参数值 #{bean.method(param)} 。
JSF 页……
<h:commandButton action="#{user.editAction(delete)}" />
背豆……
@ManagedBean(name="user")
@SessionScoped
public class UserBean{
public String editAction(String id) {
//id = "delete"
}
}
Note
If you are deploy JSF application in servlet container like Tomcat, make sure you include the “el-impl-2.2.jar” properly. For detail, please read this article – JSF 2.0 method expression caused error in Tomcat. ## 第二,我的钱
通过f:param
标签传递参数值,并通过 backing bean 中的请求参数取回。
JSF 页……
<h:commandButton action="#{user.editAction}">
<f:param name="action" value="delete" />
</h:commandButton>
背豆……
@ManagedBean(name="user")
@SessionScoped
public class UserBean{
public String editAction() {
Map<String,String> params =
FacesContext.getExternalContext().getRequestParameterMap();
String action = params.get("action");
//...
}
}
点击这里查看完整的 f:param 示例。
3.女:属性
通过f:atribute
标签传递参数值,并通过 backing bean 中的动作监听器获取它。
JSF 页……
<h:commandButton action="#{user.editAction}" actionListener="#{user.attrListener}">
<f:attribute name="action" value="delete" />
</h:commandButton>
背豆……
@ManagedBean(name="user")
@SessionScoped
public class UserBean{
String action;
//action listener event
public void attrListener(ActionEvent event){
action = (String)event.getComponent().getAttributes().get("action");
}
public String editAction() {
//...
}
}
在这里可以看到完整的 f:attribute 示例。
4. f:setPropertyActionListener
通过f:setPropertyActionListener
标签传递参数值,它会将值直接设置到您的后台 bean 属性中。
JSF 页……
<h:commandButton action="#{user.editAction}" >
<f:setPropertyActionListener target="#{user.action}" value="delete" />
</h:commandButton>
背豆……
@ManagedBean(name="user")
@SessionScoped
public class UserBean{
public String action;
public void setAction(String action) {
this.action = action;
}
public String editAction() {
//now action property contains "delete"
}
}
请在此查看完整的 f:setPropertyActionListener 示例。
P.S 请分享你的想法,如果你有任何其他方法:)
404 错误代码在 Spring MVC 中不起作用
问题
在 Spring MVC 应用程序中,404 错误代码配置正确。请参见下面的 web.xml 片段。
文件:web.xml
<web-app ...>
<servlet>
<servlet-name>mvc-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>
//...
<error-page>
<error-code>404</error-code>
<location>/WEB-INF/pages/404.htm</location>
</error-page>
</web-app>
然而,当用户访问任何不存在的资源时,它将显示一个空白页面,而不是 404.htm。
解决办法
404 错误代码配置正确,但会导致“”。“ servlet 容器和 Spring 的“ DispatcherServlet ”之间的扩展处理冲突。要解决这个问题,请尝试将 404.htm 更改为其他文件扩展名,例如 404.jsp。
文件:web.xml
<web-app ...>
<servlet>
<servlet-name>mvc-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>
//...
<error-page>
<error-code>404</error-code>
<location>/WEB-INF/pages/404.jsp</location>
</error-page>
</web-app>
现在,当用户访问任何不存在的资源时,它会转到 404.jsp 页面。
404 spring mvc (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','//web.archive.org/web/20190225092406/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0')
从事件监听器-JSF 访问受管 bean
问题
JSF 事件侦听器类如何访问另一个受管 bean?请参见下面的场景:
JSF 页……
<h:selectOneMenu value="#{country.localeCode}" onchange="submit()">
<f:valueChangeListener type="com.mkyong.CountryValueListener" />
<f:selectItems value="#{country.countryInMap}" />
</h:selectOneMenu>
国家管理的豆……
package com.mkyong;
import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
@ManagedBean(name="country")
@SessionScoped
public class CountryBean implements Serializable{
private String localeCode;
public void setLocaleCode(String localeCode) {
this.localeCode = localeCode;
}
//...
}
ValueChangeListener…
package com.mkyong;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.ValueChangeEvent;
import javax.faces.event.ValueChangeListener;
public class CountryValueListener implements ValueChangeListener{
@Override
public void processValueChange(ValueChangeEvent event)
throws AbortProcessingException {
//how to access the existing country managed bean?
//country.setLocaleCode(event.getNewValue().toString());
}
}
解决办法
实际上,有许多方法可以从事件侦听器类或另一个受管 bean 访问现有的受管 bean。参见示例:
1.getApplicationMap()
如果在应用程序范围内声明了国家管理的 bean。
CountryBean country = (CountryBean) FacesContext.getCurrentInstance().
getExternalContext().getApplicationMap().get("country");
2.getRequestMap()
如果在请求范围内声明了国家管理的 bean。
CountryBean country = (CountryBean) FacesContext.getCurrentInstance().
getExternalContext().getRequestMap().get("country");
3.getSessionMap()
如果在会话范围内声明了国家管理的 bean。
CountryBean country = (CountryBean) FacesContext.getCurrentInstance().
getExternalContext().getSessionMap().get("country");
4.ELResolver()
使用 ELResolver。
FacesContext context = FacesContext.getCurrentInstance();
CountryBean country = (CountryBean) context.
getELContext().getELResolver().getValue(context.getELContext(), null,"country");
5.ValueExpression()
使用 ValueExpression。
FacesContext context = FacesContext.getCurrentInstance();
CountryBean country = (CountryBean) context.getApplication().getExpressionFactory()
.createValueExpression(context.getELContext(), "#{country}", CountryBean.class)
.getValue(context.getELContext());
6.evaluateExpressionGet()
使用 evaluateexprrecessionet。
FacesContext context = FacesContext.getCurrentInstance();
CountryBean country = (CountryBean)context.getApplication()
.evaluateExpressionGet(context, "#{country}", CountryBean.class);
参考
- getApplicationMap()Java Doc
- getRequestMap() JavaDoc
- getSessionMap() JavaDoc
- getELResolver() JavaDoc
- createValueExpression()JavaDoc
- evaluateExpressionGet()JavaDoc
Android 活动–从一个屏幕到另一个屏幕
在 Android 中,一个活动代表一个屏幕。大多数应用程序都有多个活动来表示不同的屏幕,例如,一个活动显示应用程序设置列表,另一个活动显示应用程序状态。
Note
Refer to this official Android activity article to understand more about Android activity.
在本教程中,我们将向您展示如何与活动交互,当单击一个按钮时,从当前屏幕(当前活动)导航到另一个屏幕(另一个活动)。
P.S 这个项目是在 Eclipse 3.7 中开发的,用 Android 2.3.3 测试过。
1.XML 布局
在“res/layout/”文件夹中创建以下两个 XML 布局文件:
res/layout/main.xml
–代表屏幕 1res/layout/main2.xml
–代表屏幕 2
文件:res/layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/linearLayout1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="I'm screen 1 (main.xml)"
android:textAppearance="?android:attr/textAppearanceLarge" />
<Button
android:id="@+id/button1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Click me to another screen" />
</LinearLayout>
文件:res/layout/main2.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/linearLayout1"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="I'm screen 2 (main2.xml)"
android:textAppearance="?android:attr/textAppearanceLarge" />
</LinearLayout>
2.活动
创建两个活动类别:
- AppActivity.java –> main . XML
- App2Activity.java –> main 2 . XML
要从一个屏幕导航到另一个屏幕,请使用以下代码:
Intent intent = new Intent(context, anotherActivity.class);
startActivity(intent);
文件:AppActivity.java
package com.mkyong.android;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.widget.Button;
import android.view.View;
import android.view.View.OnClickListener;
public class AppActivity extends Activity {
Button button;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
addListenerOnButton();
}
public void addListenerOnButton() {
final Context context = this;
button = (Button) findViewById(R.id.button1);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
Intent intent = new Intent(context, App2Activity.class);
startActivity(intent);
}
});
}
}
文件:App2Activity.java
package com.mkyong.android;
import android.app.Activity;
import android.os.Bundle;
import android.widget.Button;
public class App2Activity extends Activity {
Button button;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main2);
}
}
3.AndroidManifest.xml
在AndroidManifest.xml
中声明以上两个活动类。
文件:AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mkyong.android"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="10" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<activity
android:label="@string/app_name"
android:name=".AppActivity" >
<intent-filter >
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:label="@string/app_name"
android:name=".App2Activity" >
</activity>
</application>
</manifest>
4.演示
运行应用程序。
显示AppActivity.java
(main.xml)画面。
当点击上述按钮时,它将导航到另一个屏幕App2Activity.java
(main2.xml)。
下载源代码
Download it – Android-From-Screen-To-Screen-Acticity-Example.zip (16 KB)
参考
Tags : android android activity
Android 警告对话框示例
在本教程中,我们将向您展示如何在 Android 中显示一个警告框。参见流程步骤:
- 首先,使用
AlertDialog.Builder
创建警告框界面,如标题、要显示的消息、按钮和按钮 onclick 功能 - 稍后将上述生成器附加到
AlertDialog
并显示。 - 完成了。
P.S 这个项目是在 Eclipse 3.7 中开发的,用 Android 2.3.3 测试过。
1 个 Android 布局文件
简单布局文件,在屏幕上显示一个按钮。
文件:res/layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<Button
android:id="@+id/buttonAlert"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Show Alert Box" />
</LinearLayout>
2.活动
当用户点击此按钮时,显示警告框,带有您预先定义的警告对话框界面。
文件:MainActivity.java
package com.mkyong.android;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends Activity {
final Context context = this;
private Button button;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
button = (Button) findViewById(R.id.buttonAlert);
// add button listener
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(
context);
// set title
alertDialogBuilder.setTitle("Your Title");
// set dialog message
alertDialogBuilder
.setMessage("Click yes to exit!")
.setCancelable(false)
.setPositiveButton("Yes",new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,int id) {
// if this button is clicked, close
// current activity
MainActivity.this.finish();
}
})
.setNegativeButton("No",new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,int id) {
// if this button is clicked, just close
// the dialog box and do nothing
dialog.cancel();
}
});
// create alert dialog
AlertDialog alertDialog = alertDialogBuilder.create();
// show it
alertDialog.show();
}
});
}
}
3.演示
启动它,显示一个按钮。
单击按钮时,显示警告框
如果点击“是”按钮,关闭活动并返回到您的 Android 主屏幕。
下载源代码
Download it – Android-Alert-Dialogl-Example.zip (16 KB)
参考
Android 模拟时钟和数字时钟示例
在安卓系统中, AnalogClock 是一个双手时钟,一个用于小时指示器,另一个用于分钟指示器。数字时钟看起来就像你手上的普通数字手表,以数字格式显示小时、分钟和秒。
AnalogClock
和DigitalClock
都不能修改时间,如果要修改时间,请使用“时间选择器”。
P.S 这个项目是在 Eclipse 3.7 中开发的,用 Android 2.3.3 测试过。
1.模拟时钟和数字时钟
打开“ res/layout/main.xml 文件,在 xml 中添加AnalogClock
和DigitalClock
。
文件:res/layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Analog Clock"
android:textAppearance="?android:attr/textAppearanceLarge" />
<AnalogClock
android:id="@+id/analogClock1"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Digital Clock"
android:textAppearance="?android:attr/textAppearanceLarge" />
<DigitalClock
android:id="@+id/digitalClock1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="DigitalClock" />
</LinearLayout>
2.代码代码
不知道我能用AnalogClock
或DigitalClock
做什么。
文件:MyAndroidAppActivity.java
package com.mkyong.android;
import android.app.Activity;
import android.os.Bundle;
import android.widget.AnalogClock;
import android.widget.DigitalClock;
public class MyAndroidAppActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
AnalogClock ac = (AnalogClock) findViewById(R.id.analogClock1);
//what can i do with AnalogClock?
DigitalClock dc = (DigitalClock) findViewById(R.id.digitalClock1);
//what can i do with DigitalClock also? for display only
}
}
3.演示
运行应用程序。
1.这是模拟时钟和数字时钟的样子:
下载源代码
Download it – Android-AnalogClock-DigitalClock-Example.zip (15 KB)
参考
Android 按钮示例
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/android/android-button-example/
在 Android 中,只需使用“ android.widget.Button ”类显示一个普通按钮即可。
在本教程中,我们向你展示如何显示一个普通的按钮,添加一个点击监听器,当用户点击按钮时,在你的 Android 的互联网浏览器中打开一个 URL。
P.S 这个项目是在 Eclipse 3.7 中开发的,用 Android 2.3.3 测试过。
Note
For more advance function, like image, please refer to this ImageButton example and also this ImageButton selector example.
1.添加按钮
打开" res/layout/main.xml 文件,添加一个按钮。
文件:res/layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button - Go to mkyong.com" />
</LinearLayout>
2.代码代码
给按钮附加一个点击监听器。
当用户点击它时,打开手机浏览器并显示网址:http://www.mkyong.com。
文件:MyAndroidAppActivity.java
package com.mkyong.android;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.widget.Button;
import android.view.View;
import android.view.View.OnClickListener;
public class MyAndroidAppActivity extends Activity {
Button button;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
addListenerOnButton();
}
public void addListenerOnButton() {
button = (Button) findViewById(R.id.button1);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
Intent browserIntent =
new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.mkyong.com"));
startActivity(browserIntent);
}
});
}
}
3.演示
运行应用程序。
1.结果,一个正常的按钮。
2.点击按钮,在浏览器中显示网址。
下载源代码
Download it – Android-Button-Example.zip (15 KB)
参考
Android 复选框示例
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/android/android-checkbox-example/
在 Android 中,可以使用“ android.widget.CheckBox ”类来渲染一个复选框。
在本教程中,我们将向您展示如何在 XML 文件中创建 3 个复选框,并演示如何使用监听器来检查复选框的状态-选中或未选中。
P.S 这个项目是在 Eclipse 3.7 中开发的,用 Android 2.3.3 测试过。
1.自定义字符串
打开" res/values/strings.xml 文件,添加一些用户自定义的字符串。
文件:res/values/strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="hello">Hello World, MyAndroidAppActivity!
<string name="app_name">MyAndroidApp
<string name="chk_ios">IPhone
<string name="chk_android">Android
<string name="chk_windows">Windows Mobile
<string name="btn_display">Display
</resources>
2.检验盒
打开“ res/layout/main.xml 文件,在LinearLayout
里面添加 3 个“复选框和一个按钮。
文件:res/layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<CheckBox
android:id="@+id/chkIos"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/chk_ios" />
<CheckBox
android:id="@+id/chkAndroid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/chk_android"
android:checked="true" />
<CheckBox
android:id="@+id/chkWindows"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/chk_windows" />
<Button
android:id="@+id/btnDisplay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/btn_display" />
</LinearLayout>
Make CheckBox is checked by default
Put android:checked="true"
inside checkbox element to make it checked bu default. In this case, “Android” option is checked by default. ## 3.代码代码
在 activity " onCreate()
"方法中附加侦听器,以监视以下事件:
- 如果复选框 id : " chkIos "被选中,则显示一个浮动框,显示消息"兄弟,试试 Android "。
- 如果按钮被单击,则显示一个浮动框并显示复选框状态。
文件:MyAndroidAppActivity.java
package com.mkyong.android;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.Toast;
public class MyAndroidAppActivity extends Activity {
private CheckBox chkIos, chkAndroid, chkWindows;
private Button btnDisplay;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
addListenerOnChkIos();
addListenerOnButton();
}
public void addListenerOnChkIos() {
chkIos = (CheckBox) findViewById(R.id.chkIos);
chkIos.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//is chkIos checked?
if (((CheckBox) v).isChecked()) {
Toast.makeText(MyAndroidAppActivity.this,
"Bro, try Android :)", Toast.LENGTH_LONG).show();
}
}
});
}
public void addListenerOnButton() {
chkIos = (CheckBox) findViewById(R.id.chkIos);
chkAndroid = (CheckBox) findViewById(R.id.chkAndroid);
chkWindows = (CheckBox) findViewById(R.id.chkWindows);
btnDisplay = (Button) findViewById(R.id.btnDisplay);
btnDisplay.setOnClickListener(new OnClickListener() {
//Run when button is clicked
@Override
public void onClick(View v) {
StringBuffer result = new StringBuffer();
result.append("IPhone check : ").append(chkIos.isChecked());
result.append("\nAndroid check : ").append(chkAndroid.isChecked());
result.append("\nWindows Mobile check :").append(chkWindows.isChecked());
Toast.makeText(MyAndroidAppActivity.this, result.toString(),
Toast.LENGTH_LONG).show();
}
});
}
}
4.演示
运行应用程序。
1.结果:
2.如果选中了“IPhone ”:
3.勾选了“IPhone”和“Windows Mobile”,之后,点击“显示”按钮:
下载源代码
Download it – Android-Checkbox-Example.zip (15 KB)
参考
Android 自定义对话框示例
在本教程中,我们将向您展示如何在 Android 中创建自定义对话框。请参见以下步骤:
- 创建自定义对话框布局(XML 文件)。
- 将布局附在
Dialog
上。 - 显示
Dialog
。 - 完成了。
P.S 这个项目是在 Eclipse 3.7 中开发的,用 Android 2.3.3 测试过。
Note
You may also interest to read this custom AlertDialog example.
1 个 Android 布局文件
两个 XML 文件,一个用于主屏幕,一个用于自定义对话框。
文件:res/layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<Button
android:id="@+id/buttonShowCustomDialog"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Show Custom Dialog" />
</LinearLayout>
文件:res/layout/custom.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<ImageView
android:id="@+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="5dp" />
<TextView
android:id="@+id/text"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textColor="#FFF"
android:layout_toRightOf="@+id/image"/>/>
<Button
android:id="@+id/dialogButtonOK"
android:layout_width="100px"
android:layout_height="wrap_content"
android:text=" Ok "
android:layout_marginTop="5dp"
android:layout_marginRight="5dp"
android:layout_below="@+id/image"
/>
</RelativeLayout>
2.活动
阅读下一步的评论和演示,这应该是自我探索。
文件:MainActivity.java
package com.mkyong.android;
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
public class MainActivity extends Activity {
final Context context = this;
private Button button;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
button = (Button) findViewById(R.id.buttonShowCustomDialog);
// add button listener
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
// custom dialog
final Dialog dialog = new Dialog(context);
dialog.setContentView(R.layout.custom);
dialog.setTitle("Title...");
// set the custom dialog components - text, image and button
TextView text = (TextView) dialog.findViewById(R.id.text);
text.setText("Android custom dialog example!");
ImageView image = (ImageView) dialog.findViewById(R.id.image);
image.setImageResource(R.drawable.ic_launcher);
Button dialogButton = (Button) dialog.findViewById(R.id.dialogButtonOK);
// if button is clicked, close the custom dialog
dialogButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
dialog.dismiss();
}
});
dialog.show();
}
});
}
}
3.演示
启动它,显示“main.xml
”布局。
点击按钮,显示自定义对话框"custom.xml
"布局,如果点击"确定"按钮,对话框将被关闭。
下载源代码
Download it – Android-Custom-Dialog-Example.zip (16 KB)
参考
Android 日期选择器示例
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/android/android-date-picker-example/
在 Android 中,您可以使用“Android . widget . date picker”类来呈现一个日期选择器组件,以便在预定义的用户界面中选择日、月和年。
在本教程中,我们将向您展示如何通过Android . widget . date picker在当前页面中呈现日期选择器组件,以及通过Android . app . datepickerdialog在对话框中呈现日期选择器组件。此外,我们还向您展示了如何在日期选择器组件中设置日期。
P.S 这个项目是在 Eclipse 3.7 中开发的,用 Android 2.3.3 测试过。
1.日期选择器
打开“ res/layout/main.xml ”文件,添加日期选择器、标签和按钮进行演示。
文件:res/layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<Button
android:id="@+id/btnChangeDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Change Date" />
<TextView
android:id="@+id/lblDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Current Date (M-D-YYYY): "
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView
android:id="@+id/tvDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""
android:textAppearance="?android:attr/textAppearanceLarge" />
<DatePicker
android:id="@+id/dpResult"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
"DatePickerDialog
"是在代码中声明,而不是在 XML 中。
2.代码代码
阅读代码的注释,应该是不言自明的。
文件:MyAndroidAppActivity.java
package com.mkyong.android;
import java.util.Calendar;
import android.app.Activity;
import android.app.DatePickerDialog;
import android.app.Dialog;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.DatePicker;
import android.widget.TextView;
public class MyAndroidAppActivity extends Activity {
private TextView tvDisplayDate;
private DatePicker dpResult;
private Button btnChangeDate;
private int year;
private int month;
private int day;
static final int DATE_DIALOG_ID = 999;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
setCurrentDateOnView();
addListenerOnButton();
}
// display current date
public void setCurrentDateOnView() {
tvDisplayDate = (TextView) findViewById(R.id.tvDate);
dpResult = (DatePicker) findViewById(R.id.dpResult);
final Calendar c = Calendar.getInstance();
year = c.get(Calendar.YEAR);
month = c.get(Calendar.MONTH);
day = c.get(Calendar.DAY_OF_MONTH);
// set current date into textview
tvDisplayDate.setText(new StringBuilder()
// Month is 0 based, just add 1
.append(month + 1).append("-").append(day).append("-")
.append(year).append(" "));
// set current date into datepicker
dpResult.init(year, month, day, null);
}
public void addListenerOnButton() {
btnChangeDate = (Button) findViewById(R.id.btnChangeDate);
btnChangeDate.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
showDialog(DATE_DIALOG_ID);
}
});
}
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case DATE_DIALOG_ID:
// set date picker as current date
return new DatePickerDialog(this, datePickerListener,
year, month,day);
}
return null;
}
private DatePickerDialog.OnDateSetListener datePickerListener
= new DatePickerDialog.OnDateSetListener() {
// when dialog box is closed, below method will be called.
public void onDateSet(DatePicker view, int selectedYear,
int selectedMonth, int selectedDay) {
year = selectedYear;
month = selectedMonth;
day = selectedDay;
// set selected date into textview
tvDisplayDate.setText(new StringBuilder().append(month + 1)
.append("-").append(day).append("-").append(year)
.append(" "));
// set selected date into datepicker also
dpResult.init(year, month, day, null);
}
};
}
上面的“DatePickerDialog”例子是从谷歌 Android 日期选择器例子 引用的,有一些小的变化。
3.演示
运行应用程序。
1.结果,“日期选择器”和“文本视图”被设置为当前日期。
2.点击“更改日期”按钮,将通过DatePickerDialog
在对话框中提示一个日期选择器组件。
3.“日期选择器”和“文本视图”都用选定的日期更新。
下载源代码
Download it – Android-DatePicker-Example.zip (16 KB)
参考
Tags : android date date picker
Android 在真实设备上的调试
本教程将向你展示如何在一个真正的 Android 设备(手机)上调试 Android 应用程序。
本教程中的工具和环境:
- Eclipse IDE 3.7 + ADT 插件
- 三星银河 S2
- Windows 7
在设备上调试的总结步骤:
- 下载 Google USB 驱动程序(如果使用 Android 开发者手机(ADP))
- 下载 OEM USB 驱动程序(如果使用其他 Android 驱动的设备,三星、宏基、HTC…)
- 在您的设备中,打开 USB 调试。
- 将您的设备连接到 PC。
- 使用“adb 设备”验证您的设备是否连接成功。
- 将 Eclipse 的“部署目标选择模式”改为“手动”,在运行时选择设备。
- 完成了。
在这个例子中,我们将使用前面的“ Hello World Android 示例”,并在一个真正的 Android 驱动的设备上调试或运行,三星 Galaxy S2 。
1.下载 OEM USB 驱动程序
参考这个安卓 USB 驱动指南。如果你正在使用像 Nexus One 或 Nexus S 这样的 Android 开发者手机(ADP),你应该通过“ Android SDK 管理器安装谷歌 USB 驱动程序。
使用三星 Galaxy S2,您需要安装 OEM USB 驱动程序,或三星 USB 驱动程序,它包含在三星 Kies 软件中。
请参阅此“何处下载三星 Galaxy S2 USB 驱动程序”指南,在您的电脑上安装 USB 驱动程序。
## 2.启用 USB 调试
在您的设备中,要打开 USB 调试:“设置”->“应用程序”->“开发”->“USB 调试”。
见下图:
## 3.将设备连接到 PC
将三星 Galaxy S2 连接到 PC,并通过命令“adb devices
”进行验证。
在命令提示符下,将路径更改为“Android SDK/platform-tools,输入命令“adb devices
”,如果看到类似“某个号码奇怪的设备”的东西,说明你的设备已经成功连接到 PC。
图——“304d 19665059 df6e 设备”是三星 Galaxy S2。
4.Eclipse -> Android
Note
Most people are stuck in this stage, beware.
之前,您可能创建了几个“ Android 虚拟设备(AVD) 用于测试,并将“部署目标选择模式”设置为“自动”,但是,这将导致应用程序无法在您连接的设备上调试,并继续启动 AVD 仿真器。
2 个解决方案:
- 在 Eclipse 中右击 Android 项目,选择"运行 " - > " 运行配置 " - > " Android 应用 " - > " 目标 " tab - > " 部署目标选择模式 " - >设置为"手动",就可以在运行时选择设备了。
- 或者,在部署目标选择模式中,取消选择所有选中的 AVD 即可。
图:部署目标选择模式
图:运行时选择您的设备
5.启动它
在 Eclipse 中,将您的项目作为 Android 项目运行或调试,在运行时选择您的设备,项目将复制到您的三星 Galaxy S2 并自动启动。
图:HelloWorldApp 在三星 galaxy S2 上调试。
参考
Android GridView 示例
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/android/android-gridview-example/
在 Android 中,GridView
让你在二维滚动网格中排列组件。有关详细的属性解释,请参见 GridView 参考。
在本教程中,我们将向您展示两个常见的GridView
示例:
- 正常方式,只是在
GridView
布局中显示文本。 - 创建自定义适配器以在
GridView
布局中显示图像和文本。
P.S 这个项目是在 Eclipse 3.7 中开发的,用 Android 2.3.3 测试过。
1.普通 GridView 示例
在GridView
布局中显示从 A 到 Z 的字符。很简单,应该是不言自明的。
1.1 Android 布局文件–RES/Layout/main . XML
<?xml version="1.0" encoding="utf-8"?>
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/gridView1"
android:numColumns="auto_fit"
android:gravity="center"
android:columnWidth="50dp"
android:stretchMode="columnWidth"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
</GridView>
1.2 活动
package com.mkyong.android;
import android.app.Activity;
import android.os.Bundle;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.GridView;
import android.widget.TextView;
import android.widget.Toast;
import android.view.View;
import android.widget.AdapterView.OnItemClickListener;
public class GridViewActivity extends Activity {
GridView gridView;
static final String[] numbers = new String[] {
"A", "B", "C", "D", "E",
"F", "G", "H", "I", "J",
"K", "L", "M", "N", "O",
"P", "Q", "R", "S", "T",
"U", "V", "W", "X", "Y", "Z"};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
gridView = (GridView) findViewById(R.id.gridView1);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, numbers);
gridView.setAdapter(adapter);
gridView.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v,
int position, long id) {
Toast.makeText(getApplicationContext(),
((TextView) v).getText(), Toast.LENGTH_SHORT).show();
}
});
}
}
1.3 演示
2.自定义适配器示例
在这个例子中,扩展一个BaseAdapter
以在GridView
布局中显示一组图像和文本。
2.1 两个 Android 布局文件
File–RES/layout/main . XML
<?xml version="1.0" encoding="utf-8"?>
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/gridView1"
android:numColumns="auto_fit"
android:gravity="center"
android:columnWidth="100dp"
android:stretchMode="columnWidth"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
</GridView>
File–RES/layout/mobile . XML
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp" >
<ImageView
android:id="@+id/grid_item_image"
android:layout_width="50px"
android:layout_height="50px"
android:layout_marginRight="10px"
android:src="@drawable/android_logo" >
</ImageView>
<TextView
android:id="@+id/grid_item_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@+id/label"
android:layout_marginTop="5px"
android:textSize="15px" >
</TextView>
</LinearLayout>
2.2 定制适配器
package com.mkyong.android.adapter;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import com.mkyong.android.R;
public class ImageAdapter extends BaseAdapter {
private Context context;
private final String[] mobileValues;
public ImageAdapter(Context context, String[] mobileValues) {
this.context = context;
this.mobileValues = mobileValues;
}
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View gridView;
if (convertView == null) {
gridView = new View(context);
// get layout from mobile.xml
gridView = inflater.inflate(R.layout.mobile, null);
// set value into textview
TextView textView = (TextView) gridView
.findViewById(R.id.grid_item_label);
textView.setText(mobileValues[position]);
// set image based on selected text
ImageView imageView = (ImageView) gridView
.findViewById(R.id.grid_item_image);
String mobile = mobileValues[position];
if (mobile.equals("Windows")) {
imageView.setImageResource(R.drawable.windows_logo);
} else if (mobile.equals("iOS")) {
imageView.setImageResource(R.drawable.ios_logo);
} else if (mobile.equals("Blackberry")) {
imageView.setImageResource(R.drawable.blackberry_logo);
} else {
imageView.setImageResource(R.drawable.android_logo);
}
} else {
gridView = (View) convertView;
}
return gridView;
}
@Override
public int getCount() {
return mobileValues.length;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
}
2.3 活动
package com.mkyong.android;
import com.mkyong.android.adapter.ImageAdapter;
import android.app.Activity;
import android.os.Bundle;
import android.widget.AdapterView;
import android.widget.GridView;
import android.widget.TextView;
import android.widget.Toast;
import android.view.View;
import android.widget.AdapterView.OnItemClickListener;
public class GridViewActivity extends Activity {
GridView gridView;
static final String[] MOBILE_OS = new String[] {
"Android", "iOS","Windows", "Blackberry" };
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
gridView = (GridView) findViewById(R.id.gridView1);
gridView.setAdapter(new ImageAdapter(this, MOBILE_OS));
gridView.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v,
int position, long id) {
Toast.makeText(
getApplicationContext(),
((TextView) v.findViewById(R.id.grid_item_label))
.getText(), Toast.LENGTH_SHORT).show();
}
});
}
}
2.4 演示
下载源代码
Download it – Android-GridView-Example.zip (21 KB)
参考
Android hello world 示例
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/android/android-hello-world-example/
在本教程中,我们将向您展示如何在 Eclipse IDE + ADT 插件中创建一个简单的“hello world”Android项目,并使用 Android 虚拟设备(AVD) 运行它。Eclipse ADT 插件提供了简单的 Android 项目创建和管理、组件拖放、自动完成和许多有用的特性来加速您的 Android 开发周期。
开发 Android 应用程序的步骤概述:
- 安装 Android SDK
- 安装 ADT Eclipse 插件
- 创建一个 Android 虚拟设备(AVD)
- 用 Eclipse 创建 Android 项目(向导)
- 编码它…
- 在 Android 虚拟设备(AVD)中启动
本教程中使用的工具:
- JDK 1.6
- Eclipse IDE 3.7,Indigo
- Android SDK
1.安装 Android SDK
访问这个 Android SDK 页面,选择哪个平台并安装。
在 Android SDK 安装文件夹中,运行“Android SDK 管理器”,选择你要开发的 Android 版本。
2.安装 ADT Eclipse 插件
要将 Android SDK 与 Eclipse IDE 集成,需要安装 Eclipse ADT 插件。请参考本官方指南-“安装 ADT 插件”。
在 Eclipse IDE 中,选择“帮助”->“安装新软件……”,并输入以下 URL:
https://dl-ssl.google.com/android/eclipse/
Note
In my case, above ADT plugin is taking years to download, no idea why. If you are facing the similar problem, just download and install the ADT plugin manually, refer to this ADT plugin troubleshooting guide.
3.创建一个 Android 虚拟设备(AVD)
在 Eclipse 中,可以访问 Eclipse 工具栏中的“ Android 虚拟设备(AVD) ”。单击“新建”创建一个 AVD。
稍后,Eclipse 会将应用程序部署到这个 AVD 中。
4.创建 Android 项目
在 Eclipse 中,选择“文件->新建->项目…”,“Android 项目”,并输入您的应用程序详细信息。Eclipse 将创建所有必要的 Android 项目文件和配置。
5.你好世界
定位生成的活动文件,修改一点输出一个字符串“Hello World”。
文件:HelloWorldActivity.java
package com.mkyong.android;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
public class HelloWorldActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TextView text = new TextView(this);
text.setText("Hello World, Android - mkyong.com");
setContentView(text);
}
}
6.演示
作为“ Android 应用运行,见输出。
按下“主页”按钮(在右手边),你会注意到“HelloWorld”应用程序已成功部署在 Android 虚拟设备上。
Note
This tutorial is more to example driven, not much explanation. For detail and complete explanation, please refer to the official Android developer hello world tutorial.Firewall…
In my computer, the COMODO firewall is installed and blocked the deploying process, caused the connection between Eclipse ADT and AVD is blocked and failed to deploy. Just make sure your firewall is configured properly, or just turn it off for Android development 🙂Debug Android application on real device
Sometime, “Android Virtual Device” is not enough to test some real phone functionalities, like led light, sensor and etc. Then you should refer to this how to debug Android application on real device.
下载源代码
Download it – Android-HelloWorld.zip (15 KB)
参考
Tags : android hello world
Android——如何将按钮置于屏幕中央
一个小的 Android 提示,告诉你如何在屏幕上居中按钮。将按钮包裹在RelativeLayout
中,并将以下属性设置为“真”。
android:layout_centerVertical="true"
android:layout_centerHorizontal="true"
下面是一个完整的例子,演示了如何使用上面的提示在屏幕上居中一个按钮。
1.Android 布局
布局中的按钮。
文件:res/layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/relativeLayout1"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true"
android:text="Button" />
</RelativeLayout>
2.活动
简单的活动类,什么也不做,只显示上面的布局文件。
package com.mkyong.android;
import android.app.Activity;
import android.os.Bundle;
public class HelloWorldActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}
3.演示
启动它,并查看输出:
下载源代码
Download it – Android-Center-Button-On-Screen-Example.zip (16 KB)
参考
Android:如何检查设备是否有摄像头
在 Android 中,您可以使用PackageManager
hasSystemFeature()
方法来检查设备是否具有摄像头、gps 或其他功能。
查看在活动类中使用PackageManager
的完整示例。
package com.mkyong.android;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.Log;
public class FlashLightActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.main);
Context context = this;
PackageManager packageManager = context.getPackageManager();
// if device support camera?
if (packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
//yes
Log.i("camera", "This device has camera!");
}else{
//no
Log.i("camera", "This device has no camera!");
}
}
}
Camera flashlight example
You may interest on this example – How to turn on/off camera LED/flashlight in Android.
参考
android camera (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','//web.archive.org/web/20190214233023/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0')
Android ImageButton 示例
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/android/android-imagebutton-example/
在 Android 中,您可以使用“Android . widget . imagebutton”来显示一个普通的“Button
”,并带有自定义的背景图像。
在本教程中,我们将向您展示如何显示一个带有背景图片的按钮,名为“ android_button.png ”,当用户点击它时,会显示一条短消息。就这么简单。
Note
You may also like this Android ImageButton selector example, which allow to change button’s images depends on button states.
P.S 这个项目是在 Eclipse 3.7 中开发的,用 Android 2.3.3 测试过。
1.将图像添加到资源
将 image "Android _ button . png"放入" res/drawable-?dpi 文件夹。以便 Android 知道在哪里可以找到您图像。
2.添加 ImageButton
打开“ res/layout/main.xml 文件,添加一个“ImageButton
标签,通过“android:src
定义背景图片。
文件:res/layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<ImageButton
android:id="@+id/imageButton1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/android_button" />
</LinearLayout>
3.代码代码
下面是代码,在图像按钮上添加一个点击监听器。
文件:MyAndroidAppActivity.java
package com.mkyong.android;
import android.app.Activity;
import android.os.Bundle;
import android.widget.ImageButton;
import android.widget.Toast;
import android.view.View;
import android.view.View.OnClickListener;
public class MyAndroidAppActivity extends Activity {
ImageButton imageButton;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
addListenerOnButton();
}
public void addListenerOnButton() {
imageButton = (ImageButton) findViewById(R.id.imageButton1);
imageButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
Toast.makeText(MyAndroidAppActivity.this,
"ImageButton is clicked!", Toast.LENGTH_SHORT).show();
}
});
}
}
4.演示
运行应用程序。
1.结果,带有自定义背景图像的按钮。
2.点击按钮,显示一条短消息。
下载源代码
Download it – Android-ImageButton-Example.zip (28 KB)
参考
Android 线性布局示例
在 Android 中, LinearLayout 是一种常见的布局,通过orientation
属性将“组件”按垂直或水平顺序排列。此外,最高的weight
组件将填充LinearLayout
中的剩余空间。
在本教程中,我们将向你展示如何使用LinearLayout
以垂直和水平顺序显示 3 个按钮,以及“权重”是如何工作的。
P.S 这个项目是在 Eclipse 3.7 中开发的,用 Android 2.3.3 测试过。
1.线性布局–水平
打开“ res/layout/main.xml ”文件,在LinearLayout
内添加 3 个按钮,方向为“水平”。在这种情况下,权重最高的是“button3”,因此它将填满布局中的剩余空间。
文件:res/layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal" >
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button 1" />
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button 2" />
<Button
android:id="@+id/button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button 3"
android:layout_weight="1"/>
</LinearLayout>
见图:
## 2.线性布局–垂直
现在,将LinearLayout
改为垂直方向。
文件:res/layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button 1" />
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button 2" />
<Button
android:id="@+id/button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button 3"
android:layout_weight="1"/>
</LinearLayout>
见图:
## 下载源代码
Download it – Android-LinearLayout-Example.zip (15 KB)
参考
Android ListView 示例
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/android/android-listview-example/
在 Android 中, ListView 允许你在一个垂直的可滚动列表中排列组件。
在本教程中,我们将向您展示 2 个ListView
示例:
- 在
ListView
中显示组件的正常方式。 - 自定义数组适配器,用于自定义
ListView
中的项目显示。
P.S 这个项目是在 Eclipse 3.7 中开发的,用 Android 2.3.3 测试过。
1.普通 ListView 示例
在这个例子中,我们向你展示了如何通过ListView
来显示水果名称的列表,这应该是简单明了的。
1.1 Android 布局文件
文件:res/layout/list_fruit.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="10dp"
android:textSize="20sp" >
</TextView>
1.2 ListView
package com.mkyong.android;
import android.app.ListActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.AdapterView.OnItemClickListener;
public class ListFruitActivity extends ListActivity {
static final String[] FRUITS = new String[] { "Apple", "Avocado", "Banana",
"Blueberry", "Coconut", "Durian", "Guava", "Kiwifruit",
"Jackfruit", "Mango", "Olive", "Pear", "Sugar-apple" };
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// no more this
// setContentView(R.layout.list_fruit);
setListAdapter(new ArrayAdapter<String>(this, R.layout.list_fruit,FRUITS));
ListView listView = getListView();
listView.setTextFilterEnabled(true);
listView.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
// When clicked, show a toast with the TextView text
Toast.makeText(getApplicationContext(),
((TextView) view).getText(), Toast.LENGTH_SHORT).show();
}
});
}
}
1.3 演示
## 2.自定义数组适配器示例
在这个例子中,我们展示了如何在ListView
中创建 4 个项目,并使用一个自定义的ArrayAdapter
来根据列表中的“项目名称”显示不同的图像。
2.1 图像
获取 4 幅图像进行演示。
2.2 Android 布局文件
文件:res/layout/list_mobile.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp" >
<ImageView
android:id="@+id/logo"
android:layout_width="50px"
android:layout_height="50px"
android:layout_marginLeft="5px"
android:layout_marginRight="20px"
android:layout_marginTop="5px"
android:src="@drawable/windowsmobile_logo" >
</ImageView>
<TextView
android:id="@+id/label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@+id/label"
android:textSize="30px" >
</TextView>
</LinearLayout>
2.3 自定义 ArrayAdapter
创建一个类扩展ArrayAdapter
并在getView()
方法中自定义项目显示。
package com.mkyong.android.adaptor;
import com.mkyong.android.R;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
public class MobileArrayAdapter extends ArrayAdapter<String> {
private final Context context;
private final String[] values;
public MobileArrayAdapter(Context context, String[] values) {
super(context, R.layout.list_mobile, values);
this.context = context;
this.values = values;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rowView = inflater.inflate(R.layout.list_mobile, parent, false);
TextView textView = (TextView) rowView.findViewById(R.id.label);
ImageView imageView = (ImageView) rowView.findViewById(R.id.logo);
textView.setText(values[position]);
// Change icon based on name
String s = values[position];
System.out.println(s);
if (s.equals("WindowsMobile")) {
imageView.setImageResource(R.drawable.windowsmobile_logo);
} else if (s.equals("iOS")) {
imageView.setImageResource(R.drawable.ios_logo);
} else if (s.equals("Blackberry")) {
imageView.setImageResource(R.drawable.blackberry_logo);
} else {
imageView.setImageResource(R.drawable.android_logo);
}
return rowView;
}
}
2.4 ListView
ListView
,但是使用上面的自定义适配器来显示列表。
package com.mkyong.android;
import com.mkyong.android.adaptor.MobileArrayAdapter;
import android.app.ListActivity;
import android.os.Bundle;
import android.widget.ListView;
import android.widget.Toast;
import android.view.View;
public class ListMobileActivity extends ListActivity {
static final String[] MOBILE_OS =
new String[] { "Android", "iOS", "WindowsMobile", "Blackberry"};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setListAdapter(new MobileArrayAdapter(this, MOBILE_OS));
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
//get selected items
String selectedValue = (String) getListAdapter().getItem(position);
Toast.makeText(this, selectedValue, Toast.LENGTH_SHORT).show();
}
}
2.5 演示
## 下载源代码
Download both examples – Android-ListView-Example.zip (21 KB)
参考
Android 密码字段示例
在 Android 中,可以使用“ android.widget.EditText ”,配合inputType="textPassword"
渲染一个密码组件。
在本教程中,我们将向您展示如何使用 XML 来创建密码字段、标签字段和普通按钮。当您点击按钮时,密码值将显示为浮动消息(toast 消息)。
P.S 这个项目是在 Eclipse 3.7 中开发的,用 Android 2.3.3 测试过。
1.自定义字符串
打开" res/values/strings.xml 文件,添加一些自定义字符串进行演示。
文件:res/values/strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">MyAndroidApp
<string name="lblPassword">Enter Your Password :
<string name="btn_submit">Submit
</resources>
2.密码
打开“ res/layout/main.xml 文件,添加密码组件,EditText
+ inputType="textPassword"
。
文件:res/layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/lblPassword"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/lblPassword"
android:textAppearance="?android:attr/textAppearanceLarge" />
<EditText
android:id="@+id/txtPassword"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPassword" >
<requestFocus />
</EditText>
<Button
android:id="@+id/btnSubmit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/btn_submit" />
</LinearLayout>
3.代码代码
在 activity " onCreate()
"方法中,附加一个 click listener on 按钮,以显示密码值。
文件:MyAndroidAppActivity.java
package com.mkyong.android;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class MyAndroidAppActivity extends Activity {
private EditText password;
private Button btnSubmit;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
addListenerOnButton();
}
public void addListenerOnButton() {
password = (EditText) findViewById(R.id.txtPassword);
btnSubmit = (Button) findViewById(R.id.btnSubmit);
btnSubmit.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MyAndroidAppActivity.this, password.getText(),
Toast.LENGTH_SHORT).show();
}
});
}
}
4.演示
运行应用程序。
1.结果,显示密码字段。
2.键入密码“mkyong123 ”,然后单击提交按钮。
下载源代码
Download it – Android-Password-Example.zip (15 KB)
参考
Android 进度条示例
在 Android 中,进度条可以告诉用户任务需要更长的时间来完成。
在本教程中,我们将向您展示如何显示一个进度条对话框来告诉用户您的任务正在运行,以及如何增加进度条状态直到任务完成。
Note
Refer to this Android ProgressBar JavaDoc for detail explanation.
P.S 这个项目是在 Eclipse 3.7 中开发的,用 Android 2.3.3 测试过。
1.添加按钮
打开“ res/layout/main.xml 文件,只需添加普通按钮进行演示即可。
文件:res/layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<Button
android:id="@+id/btnStartProgress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Download File" />
</LinearLayout>
2.代码代码
使用进度条的关键是使用“Thread
”来运行您的耗时任务,并使用另一个“Thread
”来相应地更新进度条状态。阅读代码的注释,应该是不言自明的。
文件:MyAndroidAppActivity.java
package com.mkyong.android;
import android.app.Activity;
import android.app.ProgressDialog;
import android.os.Bundle;
import android.os.Handler;
import android.widget.Button;
import android.view.View;
import android.view.View.OnClickListener;
public class MyAndroidAppActivity extends Activity {
Button btnStartProgress;
ProgressDialog progressBar;
private int progressBarStatus = 0;
private Handler progressBarHandler = new Handler();
private long fileSize = 0;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
addListenerOnButton();
}
public void addListenerOnButton() {
btnStartProgress = (Button) findViewById(R.id.btnStartProgress);
btnStartProgress.setOnClickListener(
new OnClickListener() {
@Override
public void onClick(View v) {
// prepare for a progress bar dialog
progressBar = new ProgressDialog(v.getContext());
progressBar.setCancelable(true);
progressBar.setMessage("File downloading ...");
progressBar.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressBar.setProgress(0);
progressBar.setMax(100);
progressBar.show();
//reset progress bar status
progressBarStatus = 0;
//reset filesize
fileSize = 0;
new Thread(new Runnable() {
public void run() {
while (progressBarStatus < 100) {
// process some tasks
progressBarStatus = doSomeTasks();
// your computer is too fast, sleep 1 second
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// Update the progress bar
progressBarHandler.post(new Runnable() {
public void run() {
progressBar.setProgress(progressBarStatus);
}
});
}
// ok, file is downloaded,
if (progressBarStatus >= 100) {
// sleep 2 seconds, so that you can see the 100%
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// close the progress bar dialog
progressBar.dismiss();
}
}
}).start();
}
});
}
// file download simulator... a really simple
public int doSomeTasks() {
while (fileSize <= 1000000) {
fileSize++;
if (fileSize == 100000) {
return 10;
} else if (fileSize == 200000) {
return 20;
} else if (fileSize == 300000) {
return 30;
}
// ...add your own
}
return 100;
}
}
“doSomeTasks”方法只是一个文件大小下载模拟器,只是用你的长时间运行的任务替换这个方法。
3.演示
运行应用程序。
1.结果,一个按钮。
2.点击按钮,会提示一个“进度条对话框”显示当前下载进度。
3.任务完成后,进度条会显示 100%,并自动关闭。
下载源代码
Download it - Android-ProgressBar-Example.zip (15 KB)
参考
Android 提示用户输入对话框示例
在本教程中,我们将增强前面的 AlertDialog
示例,使其能够接受用户输入,就像一个提示对话框。更具体地说,这是一个自定义的AlertDialog
示例。
请参见以下步骤:
- 创建提示对话框布局(XML 文件)。
- 将提示对话框布局附加到
AlertDialog.Builder
。 - 将
AlertDialog.Builder
连接到AlertDialog
上。 - 完成了。
P.S 这个项目是在 Eclipse 3.7 中开发的,用 Android 2.3.3 测试过。
Note
You may interest to read this custom dialog example.
1 个 Android 布局文件
两个 XML 文件,一个用于主屏幕,一个用于提示对话框。
文件:res/layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<Button
android:id="@+id/buttonPrompt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Show Prompt Dialog" />
<EditText
android:id="@+id/editTextResult"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</EditText>
</LinearLayout>
文件:res/layout/prompts.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/layout_root"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:padding="10dp" >
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Type Your Message : "
android:textAppearance="?android:attr/textAppearanceLarge" />
<EditText
android:id="@+id/editTextDialogUserInput"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<requestFocus />
</EditText>
</LinearLayout>
2.活动
阅读下一步的评论和演示,这应该是自我探索。
文件:MainActivity.java
package com.mkyong.android;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
public class MainActivity extends Activity {
final Context context = this;
private Button button;
private EditText result;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// components from main.xml
button = (Button) findViewById(R.id.buttonPrompt);
result = (EditText) findViewById(R.id.editTextResult);
// add button listener
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
// get prompts.xml view
LayoutInflater li = LayoutInflater.from(context);
View promptsView = li.inflate(R.layout.prompts, null);
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(
context);
// set prompts.xml to alertdialog builder
alertDialogBuilder.setView(promptsView);
final EditText userInput = (EditText) promptsView
.findViewById(R.id.editTextDialogUserInput);
// set dialog message
alertDialogBuilder
.setCancelable(false)
.setPositiveButton("OK",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,int id) {
// get user input and set it to result
// edit text
result.setText(userInput.getText());
}
})
.setNegativeButton("Cancel",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,int id) {
dialog.cancel();
}
});
// create alert dialog
AlertDialog alertDialog = alertDialogBuilder.create();
// show it
alertDialog.show();
}
});
}
}
3.演示
启动它,“main.xml
”布局显示一个按钮和 edittext(结果)。
点击按钮,显示提示对话框“prompts.xml
”布局,键入消息“ mkyong ”,并点击“确定”按钮。
用户输入“ mkyong ”将传递到“main.xml
”布局,编辑文本(结果),并显示它。
下载源代码
Download it – Android-Prompt-Dialog-Example.zip (16 KB)
参考
Android 单选按钮示例
在 Android 中,可以使用“Android . widget . radio button类来渲染单选按钮,而那些单选按钮通常是由Android . widget . radio group分组的。如果RadioButtons
在组中,当一个组中的一个RadioButton
被选中时,所有其他的都被自动取消选中。
在本教程中,我们将向您展示如何使用 XML 创建两个单选按钮,并分组到一个单选按钮组中。单击按钮时,显示选中的单选按钮。
P.S 这个项目是在 Eclipse 3.7 中开发的,用 Android 2.3.3 测试过。
1.自定义字符串
打开" res/values/strings.xml "文件,为单选按钮添加一些自定义字符串。
文件:res/values/strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="hello">Hello World, MyAndroidAppActivity!
<string name="app_name">MyAndroidApp
<string name="radio_male">Male
<string name="radio_female">Female
<string name="btn_display">Display
</resources>
2.单选按钮
打开“ res/layout/main.xml 文件,在LinearLayout
里面添加“ RadioGroup ”、“ RadioButton 和一个按钮。
文件:res/layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<RadioGroup
android:id="@+id/radioSex"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<RadioButton
android:id="@+id/radioMale"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/radio_male"
android:checked="true" />
<RadioButton
android:id="@+id/radioFemale"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/radio_female" />
</RadioGroup>
<Button
android:id="@+id/btnDisplay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/btn_display" />
</LinearLayout>
Radio button selected by default.
To make a radio button is selected by default, put android:checked="true"
within the RadioButton
element. In this case, radio option “Male” is selected by default.
3.代码代码
在 activity " onCreate()
"方法中,附加一个 click listener on 按钮。
文件:MyAndroidAppActivity.java
package com.mkyong.android;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.Toast;
public class MyAndroidAppActivity extends Activity {
private RadioGroup radioSexGroup;
private RadioButton radioSexButton;
private Button btnDisplay;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
addListenerOnButton();
}
public void addListenerOnButton() {
radioSexGroup = (RadioGroup) findViewById(R.id.radioSex);
btnDisplay = (Button) findViewById(R.id.btnDisplay);
btnDisplay.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// get selected radio button from radioGroup
int selectedId = radioSexGroup.getCheckedRadioButtonId();
// find the radiobutton by returned id
radioSexButton = (RadioButton) findViewById(selectedId);
Toast.makeText(MyAndroidAppActivity.this,
radioSexButton.getText(), Toast.LENGTH_SHORT).show();
}
});
}
}
4.演示
运行应用程序。
1.结果,选择了单选选项“男性”。
2.选择"女性"并点击"显示"按钮,所选择的单选按钮值被显示。
下载源代码
Download it – Android-RadioButton-Example.zip (15 KB)
参考
Tags : android radio button
Android 评级栏示例
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/android/android-rating-bar-example/
在 Android 中,您可以使用“ android.widget.RatingBar ”在星星图标中显示评级栏组件。用户可以触摸、拖动或点击星星来轻松设置评级值。
在本教程中,我们向您展示了如何使用 XML 来显示一个评级栏,几个文本视图和一个按钮。当用户点击评级栏的星号时,所选的评级值将显示在文本视图中。并且,如果用户点击该按钮,所选择的评级值将显示为浮动消息(toast 消息)。
P.S 这个项目是在 Eclipse 3.7 中开发的,用 Android 2.3.3 测试过。
1.分级栏
打开" res/layout/main.xml "文件,添加一个评级栏组件,几个 textviews 和一个按钮。
Note
The rating bar contains many configurable values. In this case, the rating bar contains 4 stars, each increase 1.0 value, so, it contains the minimum of 1.0 (1 star) and maximum value of 4.0 (4 stars). In addition, it made the 2nd star (2.0) selected by default.
文件:res/layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/lblRateMe"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Rate Me"
android:textAppearance="?android:attr/textAppearanceMedium" />
<RatingBar
android:id="@+id/ratingBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:numStars="4"
android:stepSize="1.0"
android:rating="2.0" />
<Button
android:id="@+id/btnSubmit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Submit" />
<LinearLayout
android:id="@+id/linearLayout1"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:id="@+id/lblResult"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Result : "
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView
android:id="@+id/txtRatingValue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""
android:textAppearance="?android:attr/textAppearanceSmall" />
</LinearLayout>
</LinearLayout>
2.代码代码
在 activity " onCreate()
"方法中,在评级栏上附加一个监听器,当评级值改变时触发。按钮上的另一个侦听器,在按钮被单击时触发。阅读代码的注释,应该是不言自明的。
文件:MyAndroidAppActivity.java
package com.mkyong.android;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.RatingBar;
import android.widget.RatingBar.OnRatingBarChangeListener;
import android.widget.TextView;
import android.widget.Toast;
public class MyAndroidAppActivity extends Activity {
private RatingBar ratingBar;
private TextView txtRatingValue;
private Button btnSubmit;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
addListenerOnRatingBar();
addListenerOnButton();
}
public void addListenerOnRatingBar() {
ratingBar = (RatingBar) findViewById(R.id.ratingBar);
txtRatingValue = (TextView) findViewById(R.id.txtRatingValue);
//if rating value is changed,
//display the current rating value in the result (textview) automatically
ratingBar.setOnRatingBarChangeListener(new OnRatingBarChangeListener() {
public void onRatingChanged(RatingBar ratingBar, float rating,
boolean fromUser) {
txtRatingValue.setText(String.valueOf(rating));
}
});
}
public void addListenerOnButton() {
ratingBar = (RatingBar) findViewById(R.id.ratingBar);
btnSubmit = (Button) findViewById(R.id.btnSubmit);
//if click on me, then display the current rating value.
btnSubmit.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MyAndroidAppActivity.this,
String.valueOf(ratingBar.getRating()),
Toast.LENGTH_SHORT).show();
}
});
}
}
3.演示
运行应用程序。
1.结果,默认选择第二颗星。
2.触摸第三颗星,额定值改变,在结果中显示当前选择的值(textview)。
3.触摸第一个星号,点击提交按钮,当前选择值显示为浮动信息。
下载源代码
Download it – Android-RatingBar-Example.zip (15 KB)
参考
Android RelativeLayout 示例
在 Android 中, RelativeLayout 允许你根据附近(相对或兄弟)组件的位置来定位你的组件。这是最灵活的布局,允许你将你的组件放置在任何你想显示的地方(如果你知道如何“相对”它)。
在RelativeLayout
中,可以使用上方的、下方的、左侧的和右侧的来排列组件的位置,例如在“按钮 2”下方显示一个“按钮 1”,或者在“按钮 1”的右侧显示“按钮 3”。
Note
The RelativeLayout
is very flexible, but hard to master it. Suggest you use Eclipse IDE to drag the component, then view study the Eclipse generated XML layout code to understand how to code “relative” components.
在本教程中,我们将向您展示如何通过RelativeLayout
来排列/定位button
、textview
和editbox
。
P.S 这个项目是在 Eclipse 3.7 中开发的,用 Android 2.3.3 测试过。
1.相对布局
打开“ res/layout/main.xml 文件,添加组件,通过“RelativeLayout
定位。阅读下面的 XML 代码,非常详细地告诉你在哪里显示组件。
文件:res/layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<Button
android:id="@+id/btnButton1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button 1"/>
<Button
android:id="@+id/btnButton2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button 2"
android:layout_toRightOf="@+id/btnButton1"/>
<Button
android:id="@+id/btnButton3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button 3"
android:layout_below="@+id/btnButton1"/>
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/btnButton3"
android:layout_marginTop="94dp"
android:text="User :"
android:textAppearance="?android:attr/textAppearanceLarge" />
<EditText
android:id="@+id/editText1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignTop="@+id/textView1"
android:layout_toRightOf="@+id/btnButton3" />
<Button
android:id="@+id/btnSubmit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_below="@+id/editText1"
android:text="Submit" />
</RelativeLayout>
2.演示
请参见结果,上面的 XML 代码将生成以下输出。
## 下载源代码
Download it – Android-RelativeLayout-Example.zip (15 KB)
参考
Android 旋转器(下拉列表)示例
在 Android 中,可以使用“ android.widget.Spinner ”类来呈现下拉框选择列表。
Note
Spinner is a widget similar to a drop-down list for selecting items.
在本教程中,我们将向您展示如何执行以下任务:
- 用 XML 呈现一个微调器,并通过 XML 文件加载选择项。
- 用 XML 呈现另一个 Spinner,并通过代码动态加载选择项。
- 在 Spinner 上附加一个监听器,当用户在 Spinner 中选择一个值时触发。
- 在一个普通按钮上渲染并附加一个监听器,当用户点击它时触发,它将显示微调器的选定值。
P.S 这个项目是在 Eclipse 3.7 中开发的,用 Android 2.3.3 测试过。
1.微调器中的项目列表
打开" res/values/strings.xml "文件,定义将在微调器中显示的项目列表(下拉列表)。
文件:res/values/strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">MyAndroidApp
<string name="country_prompt">Choose a country
<string-array name="country_arrays">
<item>Malaysia</item>
<item>United States</item>
<item>Indonesia</item>
<item>France</item>
<item>Italy</item>
<item>Singapore</item>
<item>New Zealand</item>
<item>India</item>
</string-array>
</resources>
2.微调器(下拉列表)
打开" res/layout/main.xml "文件,添加两个微调器组件和一个按钮。
- 在“spinner1”中,“
android:entries
”代表微调器中的选择项。 - 在“spinner2”中,选择项将在后面的代码中定义。
文件:res/layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<Spinner
android:id="@+id/spinner1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:entries="@array/country_arrays"
android:prompt="@string/country_prompt" />
<Spinner
android:id="@+id/spinner2"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:id="@+id/btnSubmit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Submit" />
</LinearLayout>
3.代码代码
阅读代码和代码的注释,它应该是不言自明的。
文件:MyAndroidAppActivity.java
package com.mkyong.android;
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.Spinner;
import android.widget.Toast;
public class MyAndroidAppActivity extends Activity {
private Spinner spinner1, spinner2;
private Button btnSubmit;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
addItemsOnSpinner2();
addListenerOnButton();
addListenerOnSpinnerItemSelection();
}
// add items into spinner dynamically
public void addItemsOnSpinner2() {
spinner2 = (Spinner) findViewById(R.id.spinner2);
List<String> list = new ArrayList<String>();
list.add("list 1");
list.add("list 2");
list.add("list 3");
ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item, list);
dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner2.setAdapter(dataAdapter);
}
public void addListenerOnSpinnerItemSelection() {
spinner1 = (Spinner) findViewById(R.id.spinner1);
spinner1.setOnItemSelectedListener(new CustomOnItemSelectedListener());
}
// get the selected dropdown list value
public void addListenerOnButton() {
spinner1 = (Spinner) findViewById(R.id.spinner1);
spinner2 = (Spinner) findViewById(R.id.spinner2);
btnSubmit = (Button) findViewById(R.id.btnSubmit);
btnSubmit.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MyAndroidAppActivity.this,
"OnClickListener : " +
"\nSpinner 1 : "+ String.valueOf(spinner1.getSelectedItem()) +
"\nSpinner 2 : "+ String.valueOf(spinner2.getSelectedItem()),
Toast.LENGTH_SHORT).show();
}
});
}
}
文件:CustomOnItemSelectedListener.java
package com.mkyong.android;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.Toast;
public class CustomOnItemSelectedListener implements OnItemSelectedListener {
public void onItemSelected(AdapterView<?> parent, View view, int pos,long id) {
Toast.makeText(parent.getContext(),
"OnItemSelectedListener : " + parent.getItemAtPosition(pos).toString(),
Toast.LENGTH_SHORT).show();
}
@Override
public void onNothingSelected(AdapterView<?> arg0) {
// TODO Auto-generated method stub
}
}
4.演示
运行应用程序。
1.结果,显示两个微调器:
2.从 spinner1 中选择“法国”,项目选择监听器被触发:
3.从微调器 2 中选择“list2 ”,然后单击提交按钮:
下载源代码
Download it – Android-Spinner-DropDownList-Example.zip (16 KB)
参考
Android 表格布局示例
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/android/android-tablelayout-example/
在 Android 中, TableLayout 让你按行和列排列组件,就像 HTML 中的标准表格布局,<tr>
和<td>
。
在本教程中,我们将向您展示如何使用TableLayout
来排列按钮、文本视图和以行和列的格式编辑文本,还将演示如何使用android:layout_span
在两个单元格中显示视图,以及使用android:layout_column
在指定的列中显示视图。
Note
In Eclipse 3.7, XML code assist will not prompts the attribute “android:layout_span
“, “android:layout_column
” and many other useful TableLayout
attributes, no idea why, may be bug. Just put the attribute inside, it’s still compile and run.
P.S 这个项目是在 Eclipse 3.7 中开发的,用 Android 2.3.3 测试过。
1.表格布局
打开" res/layout/main.xml "文件,添加以下视图进行演示。阅读 XML 注释,它应该是不言自明的。
文件:res/layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/tableLayout1"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<!-- 2 columns -->
<TableRow
android:id="@+id/tableRow1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dip" >
<TextView
android:id="@+id/textView1"
android:text="Column 1"
android:textAppearance="?android:attr/textAppearanceLarge" />
<Button
android:id="@+id/button1"
android:text="Column 2" />
</TableRow>
<!-- edittext span 2 column -->
<TableRow
android:id="@+id/tableRow2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dip" >
<EditText
android:id="@+id/editText1"
android:layout_span="2"
android:text="Column 1 & 2" />
</TableRow>
<!-- just draw a red line -->
<View
android:layout_height="2dip"
android:background="#FF0000" />
<!-- 3 columns -->
<TableRow
android:id="@+id/tableRow3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dip" >
<TextView
android:id="@+id/textView2"
android:text="Column 1"
android:textAppearance="?android:attr/textAppearanceLarge" />
<Button
android:id="@+id/button2"
android:text="Column 2" />
<Button
android:id="@+id/button3"
android:text="Column 3" />
</TableRow>
<!-- display this button in 3rd column via layout_column(zero based) -->
<TableRow
android:id="@+id/tableRow4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dip" >
<Button
android:id="@+id/button4"
android:layout_column="2"
android:text="Column 3" />
</TableRow>
<!-- display this button in 2nd column via layout_column(zero based) -->
<TableRow
android:id="@+id/tableRow5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dip" >
<Button
android:id="@+id/button5"
android:layout_column="1"
android:text="Column 2" />
</TableRow>
</TableLayout>
2.演示
见结果。
## 下载源代码
Download it – Android-TableLayout-Example.zip (15 KB)
参考
Android 文本框示例
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/android/android-textbox-example/
在 Android 中,你可以使用" EditText "类来创建一个可编辑的 textbox 来接受用户输入。
这个教程向你展示了如何在 XML 文件中创建一个 textbox,并演示了如何使用 key listener 来显示在 textbox 中输入的消息。
这个项目是在 Eclipse 中开发的,在 Android 2.3.3 中测试过。
1. EditText
打开“ res/layout/main.xml 文件,添加一个“ EditText 组件。
文件:res/layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<EditText
android:id="@+id/editText"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<requestFocus />
</EditText>
</LinearLayout>
2.编辑文本监听器
在 activity " onCreate()
"方法中附加一个关键监听器,以监视以下事件:
- 如果按下“enter ”,显示一个浮动框,并在“EditText”框中键入消息。
- 如果按下“数字 9”,显示一个浮动框,并显示消息“数字 9 已按下!”。
文件:MyAndroidAppActivity.java
package com.mkyong.android;
import android.app.Activity;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnKeyListener;
import android.widget.EditText;
import android.widget.Toast;
public class MyAndroidAppActivity extends Activity {
private EditText edittext;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
addKeyListener();
}
public void addKeyListener() {
// get edittext component
edittext = (EditText) findViewById(R.id.editText);
// add a keylistener to keep track user input
edittext.setOnKeyListener(new OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) {
// if keydown and "enter" is pressed
if ((event.getAction() == KeyEvent.ACTION_DOWN)
&& (keyCode == KeyEvent.KEYCODE_ENTER)) {
// display a floating message
Toast.makeText(MyAndroidAppActivity.this,
edittext.getText(), Toast.LENGTH_LONG).show();
return true;
} else if ((event.getAction() == KeyEvent.ACTION_DOWN)
&& (keyCode == KeyEvent.KEYCODE_9)) {
// display a floating message
Toast.makeText(MyAndroidAppActivity.this,
"Number 9 is pressed!", Toast.LENGTH_LONG).show();
return true;
}
return false;
}
});
}
}
3.演示
运行应用程序。
1.在文本框中键入一些内容,然后按“enter”键:
2.如果按下“数字 9”键:
下载源代码
Download it – Android-EditText-Example.zip (15 KB)
参考
Android:与 adb 的连接中断,出现严重错误。
问题
使用 Eclipse IDE 进行 Android 开发时,有时会出现以下“ adb ”错误消息,导致应用程序无法启动?
[2011-11-22 15:17:07 - HelloWorld] ------------------------------
[2011-11-22 15:17:07 - HelloWorld] Android Launch!
[2011-11-22 15:17:07 - HelloWorld] The connection to adb is down, and a severe error has occured.
[2011-11-22 15:17:07 - HelloWorld] You must restart adb and Eclipse.
[2011-11-22 15:17:07 - HelloWorld] Please ensure that adb is correctly located at
'C:\Android\android-sdk\platform-tools\adb.exe' and can be executed.
P.S 重启 Eclipse 不会摆脱错误信息。
解决办法
在某些情况下,Android 项目可能会崩溃或异常关闭,并导致“adb.exe功能异常。
要解决这个问题,只需重启“adb.exe”进程。重启 Eclipse 不会自动关闭“【adb.exe】”进程,你需要手动杀死它。
比如,在 Windows 中,只需使用任务管理器来终止进程。
android (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','//web.archive.org/web/20190310101843/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0')
Android 时间选择器示例
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/android/android-time-picker-example/
在 Android 中,您可以使用"Android . widget . time picker"类来呈现时间选择器组件,以便在预定义的用户界面中选择小时和分钟。
在本教程中,我们将向您展示如何通过Android . widget . time picker在当前页面中呈现时间选择器组件,以及通过Android . app . time picker dialog在对话框中呈现时间选择器组件。此外,我们还向您展示了如何设置时间选择器组件中的小时和分钟。
P.S 这个项目是在 Eclipse 3.7 中开发的,用 Android 2.3.3 测试过。
1.时间选择器
打开“ res/layout/main.xml ”文件,添加时间选择器、标签和按钮进行演示。
文件:res/layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<Button
android:id="@+id/btnChangeTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Change Time" />
<TextView
android:id="@+id/lblTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Current Time (H:M): "
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView
android:id="@+id/tvTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""
android:textAppearance="?android:attr/textAppearanceLarge" />
<TimePicker
android:id="@+id/timePicker1"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
"TimePickerDialog
"是在代码中声明,而不是在 XML 中。
2.代码代码
阅读代码的注释,应该是不言自明的。
文件:MyAndroidAppActivity.java
package com.mkyong.android;
import java.util.Calendar;
import android.app.Activity;
import android.app.Dialog;
import android.app.TimePickerDialog;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.TimePicker;
public class MyAndroidAppActivity extends Activity {
private TextView tvDisplayTime;
private TimePicker timePicker1;
private Button btnChangeTime;
private int hour;
private int minute;
static final int TIME_DIALOG_ID = 999;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
setCurrentTimeOnView();
addListenerOnButton();
}
// display current time
public void setCurrentTimeOnView() {
tvDisplayTime = (TextView) findViewById(R.id.tvTime);
timePicker1 = (TimePicker) findViewById(R.id.timePicker1);
final Calendar c = Calendar.getInstance();
hour = c.get(Calendar.HOUR_OF_DAY);
minute = c.get(Calendar.MINUTE);
// set current time into textview
tvDisplayTime.setText(
new StringBuilder().append(pad(hour))
.append(":").append(pad(minute)));
// set current time into timepicker
timePicker1.setCurrentHour(hour);
timePicker1.setCurrentMinute(minute);
}
public void addListenerOnButton() {
btnChangeTime = (Button) findViewById(R.id.btnChangeTime);
btnChangeTime.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
showDialog(TIME_DIALOG_ID);
}
});
}
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case TIME_DIALOG_ID:
// set time picker as current time
return new TimePickerDialog(this,
timePickerListener, hour, minute,false);
}
return null;
}
private TimePickerDialog.OnTimeSetListener timePickerListener =
new TimePickerDialog.OnTimeSetListener() {
public void onTimeSet(TimePicker view, int selectedHour,
int selectedMinute) {
hour = selectedHour;
minute = selectedMinute;
// set current time into textview
tvDisplayTime.setText(new StringBuilder().append(pad(hour))
.append(":").append(pad(minute)));
// set current time into timepicker
timePicker1.setCurrentHour(hour);
timePicker1.setCurrentMinute(minute);
}
};
private static String pad(int c) {
if (c >= 10)
return String.valueOf(c);
else
return "0" + String.valueOf(c);
}
}
另外,上面的“时间选择器对话框”的例子是从谷歌安卓时间选择器的例子、中引用的,有一些小的变化。
3.演示
运行应用程序。
1.结果,“时间选择器”和“文本视图”被设置为当前时间。
2.点击“更改时间”按钮,将通过TimePickerDialog
在对话框中提示一个时间选择器组件。
3.“时间选择器”和“文本视图”都用选定的时间更新。
下载源代码
Download it – Android-TimePicker-Example.zip (16 KB)
参考
Android 吐司示例
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/android/android-toast-example/
在 Android 中,Toast
是一个弹出通知消息,显示一定的时间,并自动淡入淡出,大多数人只是用它来调试。
创建Toast
消息的代码片段:
//display in short period of time
Toast.makeText(getApplicationContext(), "msg msg", Toast.LENGTH_SHORT).show();
//display in long period of time
Toast.makeText(getApplicationContext(), "msg msg", Toast.LENGTH_LONG).show();
在本教程中,我们将向您展示两个Toast
示例:
- 普通吐司视图。
- 自定义吐司视图。
P.S 这个项目是在 Eclipse 3.7 中开发的,用 Android 2.3.3 测试过。
1.普通吐司视图
简单的Toast
例子。
文件:res/layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<Button
android:id="@+id/buttonToast"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Show Toast" />
</LinearLayout>
文件:MainActivity.java
package com.mkyong.android;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends Activity {
private Button button;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
button = (Button) findViewById(R.id.buttonToast);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
Toast.makeText(getApplicationContext(),
"Button is clicked", Toast.LENGTH_LONG).show();
}
});
}
}
看到演示,当一个按钮被点击,显示一个正常的祝酒辞信息。
2.自定义吐司视图
通过自定义原来的Toast
视图来增强上面的例子。
File:RES/layout/custom _ toast . XML–这是自定义 Toast 视图。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/custom_toast_layout_id"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#FFF"
android:orientation="horizontal"
android:padding="5dp" >
<ImageView
android:id="@+id/image"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_marginRight="5dp" />
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:textColor="#000" />
</LinearLayout>
文件:MainActivity.java–读取注释,获取上面的自定义视图并附加到Toast
。
package com.mkyong.android;
import android.app.Activity;
import android.os.Bundle;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends Activity {
private Button button;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
button = (Button) findViewById(R.id.buttonToast);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
// get your custom_toast.xml ayout
LayoutInflater inflater = getLayoutInflater();
View layout = inflater.inflate(R.layout.custom_toast,
(ViewGroup) findViewById(R.id.custom_toast_layout_id));
// set a dummy image
ImageView image = (ImageView) layout.findViewById(R.id.image);
image.setImageResource(R.drawable.ic_launcher);
// set a message
TextView text = (TextView) layout.findViewById(R.id.text);
text.setText("Button is clicked!");
// Toast...
Toast toast = new Toast(getApplicationContext());
toast.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
toast.setDuration(Toast.LENGTH_LONG);
toast.setView(layout);
toast.show();
}
});
}
}
参见演示,当一个按钮被点击时,显示自定义的 Toast 消息。
下载源代码
Download it – Android-Toast-Example.zip (16 KB)
参考
Android ToggleButton 示例
在 Android 中,“Android . widget . toggle button是一个特殊的类,用于呈现只有两种状态的按钮,例如“开”和“关”。它是单选按钮打开或关闭功能的最佳选择。
在本教程中,我们将向您展示如何使用 XML 创建两个切换按钮和一个普通按钮,当用户单击普通按钮时,它将显示两个切换按钮的当前状态。
P.S 这个项目是在 Eclipse 3.7 中开发的,用 Android 2.3.3 测试过。
1.自定义字符串
打开" res/values/strings.xml "文件,为切换按钮添加一些自定义字符串。
文件:res/values/strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">MyAndroidApp
<string name="toggle_turn_on">Turn On
<string name="toggle_turn_off">Turn Off
<string name="btn_display">Display
</resources>
2.切换按钮
打开“ res/layout/main.xml 文件,在LinearLayout
里面添加两个“ ToggleButton 和一个普通按钮。
文件:res/layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<ToggleButton
android:id="@+id/toggleButton1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="ToggleButton" />
<ToggleButton
android:id="@+id/toggleButton2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textOn="@string/toggle_turn_on"
android:textOff="@string/toggle_turn_off"
android:checked="true" />
<Button
android:id="@+id/btnDisplay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/btn_display" />
</LinearLayout>
Note
Review the “togglebutton2”, we did customized the togglebutton2’s display text on and off and made it checked by default. ## 3.代码代码
在 activity " onCreate()
"方法内部,在一个普通按钮上附加一个 click 监听器,以显示切换按钮的当前状态。
文件:MyAndroidAppActivity.java
package com.mkyong.android;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
import android.widget.ToggleButton;
public class MyAndroidAppActivity extends Activity {
private ToggleButton toggleButton1, toggleButton2;
private Button btnDisplay;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
addListenerOnButton();
}
public void addListenerOnButton() {
toggleButton1 = (ToggleButton) findViewById(R.id.toggleButton1);
toggleButton2 = (ToggleButton) findViewById(R.id.toggleButton2);
btnDisplay = (Button) findViewById(R.id.btnDisplay);
btnDisplay.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
StringBuffer result = new StringBuffer();
result.append("toggleButton1 : ").append(toggleButton1.getText());
result.append("\ntoggleButton2 : ").append(toggleButton2.getText());
Toast.makeText(MyAndroidAppActivity.this, result.toString(),
Toast.LENGTH_SHORT).show();
}
});
}
}
4.演示
运行应用程序。
1.结果,toggleButton2 使用自定义字符串,并且默认选中。
2.选中 toggleButton1 和未选中的 toggleButton2,并单击显示按钮,将显示两个切换按钮的当前状态。
下载源代码
Download it – Android-ToggleButton-Example.zip (15 KB)
参考
Android 教程
原文:http://web.archive.org/web/20230101150211/https://mkyong.com/tutorials/android-tutorial/
Android ,移动设备(智能手机和平板电脑)开源操作系统,由谷歌领衔。Android SDK 提供了一套使用 Java 开发 Android 应用程序的工具和 API。所以,如果你懂 Java,Android 编程很容易🙂
在这一系列教程中,我们向你展示了基础教程的列表,让你轻松地开始 Android 程序。
Note
All Android tutorials are developed in Eclipse 3.7, and tested with Android 2.3.3.
这只是 Android 教程的初始版本,以后会继续发布更多。
1.快速启动
让您开始 Android 编程。
- Android hello world 示例
开发 Android 应用的工具和 SDK。
2.基本原则
一些安卓基础的东西。
- Android 活动示例
了解 Android 的活动,从一个屏幕(活动)导航到另一个屏幕(活动)的简单示例。 - Android wrap_content 和 fill_parent 示例
wrap _ content 和 fill_parent 的不同之处在于控制组件的宽度和高度。 - 将 Android 源代码附加到 Eclipse IDE
Android 源代码对于理解 Android 如何工作很重要,一本将 Android 源代码附加到 Eclipse IDE 的指南。
3.用户界面控件
玩 Android UI 控件。
- Android 按钮示例
使用“按钮”来显示一个简单的按钮。 - Android textbox 示例
使用“EditText”呈现一个可编辑的 textbox 组件。 - 安卓密码示例
使用“EditText”+input type = " text password "渲染一个密码组件。 - Android checkbox 示例
使用“checkbox”呈现 CheckBox 组件。 - Android 单选按钮示例
使用“RadioButton”和“RadioGroup”在组中呈现单选按钮组件。 - Android 切换按钮示例
使用“toggle button”呈现一个只有两种状态(开和关)的按钮。 - Android 评级栏示例
使用“评级栏”在星星图标中呈现一个评级栏。 - Android spinner(下拉列表)示例
使用“spinner”呈现一个下拉框,用于选择项目。 - Android 日期选择器示例
使用“date picker”和“DatePickerDialog”呈现一个日期选择器组件。 - Android 时间选择器示例
使用“time picker”和“TimePickerDialog”呈现一个时间选择器组件。 - Android analogclock 和 digitalclock 示例
使用“analogclock”和“DigitalClock”渲染一个类似时钟的组件,支持时、分、秒。 - Android 进度条示例
使用“ProgressDialog”在对话框中显示一个进度条,告诉我们你的任务需要时间来完成。 - Android 警告对话框示例
如何显示警告框。 - Android 提示对话框示例
自定义 AlertDialog 示例。 - Android 自定义对话框示例
自定义对话框示例。 - 安卓吐司示例
自定义吐司查看示例。 - Android ImageView 示例
使用“ImageView”显示一个图像文件。 - Android ImageButton 示例
使用“ImageButton”显示带有自定义背景图像的按钮。 - Android ImageButton 选择器示例
根据按钮状态,使用“按钮”和“选择器”标签显示按钮的图像。
4.布局
玩 Android 布局控件。
- Android LinearLayout 示例
最常见的布局,以水平或垂直顺序排列组件。 - Android RelativeLayout 示例
最灵活的布局,基于“相对”或兄弟组件排列组件。 - Android TableLayout 示例
最灵活的布局,以行列格式排列组件,就像 HTML 表格,< tr >和< td >一样。 - Android ListView 示例
在垂直可滚动列表中显示组件。 - Android GridView 示例
在二维滚动网格中显示组件。 - Android WebView 示例
允许你打开一个自己的窗口来查看 URL 或自定义 html 标记页面。
5.常见问题
Android 中的一些常见问题。
- 如何在安卓网络浏览器中打开网址
- 如何设置安卓应用的默认活动
- 如何在 Android 中打电话
- 三星 Galaxy S2 USB 驱动哪里下载?
- Android 在真实设备上调试
- Android–如何将按钮置于屏幕中央
- 如何在 Android 中打开/关闭相机 LED/闪光灯
- Android:如何检查设备是否有摄像头
- 如何在安卓系统中发送短信
- 如何在 Android 中发送电子邮件
- Android:与 adb 的连接中断,出现严重错误。
参考
Android WebView 示例
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/android/android-webview-example/
Android 的 WebView 允许你打开自己的窗口来查看 URL 或自定义 html 标记页面。
在本教程中,您将创建两个页面,一个页面有一个按钮,当您单击它时,它将导航到另一个页面,并在 WebView 组件中显示 URL“google.com”。
P.S 这个项目是在 Eclipse 3.7 中开发的,用 Android 2.3.3 测试过。
1.Android 布局文件
创建两个 Android 布局文件——“RES/layout/main . XML”和“ res/layout/webview.xml ”。
文件:res/layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<Button
android:id="@+id/buttonUrl"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Go to http://www.google.com" />
</LinearLayout>
文件:RES/layout/main . XML–WebView 示例
<?xml version="1.0" encoding="utf-8"?>
<WebView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/webView1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
2.活动
两个活动类,一个活动显示一个按钮,另一个活动显示带有预定义 URL 的WebView
。
文件:MainActivity.java
package com.mkyong.android;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends Activity {
private Button button;
public void onCreate(Bundle savedInstanceState) {
final Context context = this;
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
button = (Button) findViewById(R.id.buttonUrl);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
Intent intent = new Intent(context, WebViewActivity.class);
startActivity(intent);
}
});
}
}
文件:WebViewActivity.java
package com.mkyong.android;
import android.app.Activity;
import android.os.Bundle;
import android.webkit.WebView;
public class WebViewActivity extends Activity {
private WebView webView;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.webview);
webView = (WebView) findViewById(R.id.webView1);
webView.getSettings().setJavaScriptEnabled(true);
webView.loadUrl("http://www.google.com");
}
}
3.Android 清单
WebView
所需的上网权限,添加到下面的AndroidManifest.xml
中。
<uses-permission android:name="android.permission.INTERNET" />
文件:Android manifest . XML–参见完整示例。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mkyong.android"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="10" />
<uses-permission android:name="android.permission.INTERNET" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<activity
android:name=".WebViewActivity"
android:theme="@android:style/Theme.NoTitleBar" />
<activity
android:label="@string/app_name"
android:name=".MainActivity" >
<intent-filter >
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
4.演示
默认情况下,只显示一个按钮。
点击按钮,显示 WebView。
5.再次演示
WebView
允许您通过webView.loadData()
手动加载自定义 HTML 标记,参见修改后的版本:
package com.mkyong.android;
import android.app.Activity;
import android.os.Bundle;
import android.webkit.WebView;
public class WebViewActivity extends Activity {
private WebView webView;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.webview);
webView = (WebView) findViewById(R.id.webView1);
webView.getSettings().setJavaScriptEnabled(true);
//webView.loadUrl("http://www.google.com");
String customHtml = "<html><body><h1>Hello, WebView</h1></body></html>";
webView.loadData(customHtml, "text/html", "UTF-8");
}
}
现在,当点击按钮时,会显示一个定制的 html 页面。
下载源代码
Download it – Android-WebView-Example.zip (16 KB)
参考
Android wrap_content 和 fill_parent 示例
在 Android 中,你总是在组件的属性“layout_width
”和“【T3””上加上“wrap_content
”或“fill_parent
”,你不知道这有什么不同吗?
参见以下定义:
- wrap _ content–组件只想显示足够大的内容。
- fill _ parent–组件想要显示得和它的父组件一样大,并填充剩余的空间。(在 API 级别 8 中重命名为 match_parent)
以上术语现在可能没有意义,让我们看看下面的演示:
1.包装内容
一个按钮组件,在宽度和高度属性上都设置了“wrap_content
”。它告诉 Android 只显示足够大的按钮来包含它的内容“按钮 ABC ”。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<Button
android:id="@+id/btnButton1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button ABC"/>
</RelativeLayout>
2.填充 _ 父级–宽度
将“layout_width
”改为“【T1”),现在,按钮的宽度将填充剩余的空间,就像它的父“T2”一样大,但按钮的高度仍然足够大,只能包含它的内容。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<Button
android:id="@+id/btnButton1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Button ABC"/>
</RelativeLayout>
3.填充 _ 父级–高度
将“layout_height
”改为“【T1”),现在,按钮的高度将填充剩余的空间,就像它的父“【T2”一样大,但按钮的宽度仍然足够大,只能包含它的内容。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<Button
android:id="@+id/btnButton1"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:text="Button ABC"/>
</RelativeLayout>
4.fill _ parent–宽度、高度
将“layout_width
”和“layout_height
”都改为“【T2”,按钮将显示为整个设备屏幕那么大,正好填满整个屏幕空间。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<Button
android:id="@+id/btnButton1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:text="Button ABC"/>
</RelativeLayout>
Note
Actually, you can specifying an exact width and height, but it’s not recommended, due to Android variety of devices screen size. You just do not know what size of Android device is running your fantasy application.
参考
Ant 和 jUnit 任务示例
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/ant/ant-and-junit-task-example/
在本教程中,我们将向您展示如何在 Ant build 中运行 junit 测试。
1.运行单元测试
build.xml
<target name="junit" depends="compile">
<junit printsummary="yes" haltonfailure="no">
<!-- Project classpath, must include junit.jar -->
<classpath refid="test.path" />
<!-- test class -->
<classpath location="${test.classes.dir}" />
<test name="com.mkyong.test.TestMessage"
haltonfailure="no" todir="${report.dir}">
<formatter type="plain" />
<formatter type="xml" />
</test>
</junit>
</target>
2.运行一批单元测试
build.xml
<target name="junit" depends="compile">
<junit printsummary="yes" haltonfailure="no">
<!--
<classpath location="lib/junit-4.11.jar" />
<classpath location="lib/hamcrest-core-1.3.jar" />
-->
<classpath refid="test.path" />
<classpath location="${test.classes.dir}" />
<formatter type="xml" />
<formatter type="plain" />
<batchtest fork="yes" todir="${report.dir}">
<fileset dir="${test.dir}">
<include name="**/*Test*.java" />
</fileset>
</batchtest>
</junit>
</target>
3.例子
一个 web 应用程序示例,向您展示如何运行 junit 测试。
3.1 返回消息
MessageGenerator.java
package com.mkyong.message;
import org.springframework.stereotype.Component;
@Component
public class MessageGenerator {
public String getWelcomeMessage() {
return "welcome";
}
}
3.2 测试上述类的两个 junit 测试用例。
TestMessage.java
package com.mkyong.test;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import com.mkyong.message.MessageGenerator;
public class TestMessage {
@Test
public void test_welcome_message() {
MessageGenerator obj = new MessageGenerator();
assertEquals("welcome", obj.getWelcomeMessage());
}
}
TestMessage2.java
package com.mkyong.test;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import com.mkyong.message.MessageGenerator;
public class TestMessage2 {
@Test
public void test_welcome_message_2() {
MessageGenerator obj = new MessageGenerator();
assertEquals("welcome", obj.getWelcomeMessage());
}
}
3.3 使用 ivy 获取项目依赖项,并声明项目范围。
ivy.xml
<ivy-module version="2.0">
<info organisation="org.apache" module="WebProject" />
<configurations>
<conf name="compile" description="Required to compile application"/>
<conf name="runtime" description="Additional run-time dependencies" extends="compile"/>
<conf name="test" description="Required for test only" extends="runtime"/>
</configurations>
<dependencies>
<dependency org="junit" name="junit" rev="4.11" conf="test->default" />
</dependencies>
</ivy-module>
3.4 运行单元测试
build.xml
<project xmlns:ivy="antlib:org.apache.ivy.ant"
name="HelloProject" default="main" basedir=".">
<description>
Running junit Test
</description>
<!-- Project Structure -->
<property name="jdk.version" value="1.7" />
<property name="projectName" value="WebProject" />
<property name="src.dir" location="src" />
<property name="test.dir" location="src" />
<property name="report.dir" location="report" />
<property name="web.dir" value="war" />
<property name="web.classes.dir" location="${web.dir}/WEB-INF/classes" />
<!-- ivy start -->
<target name="resolve" description="retrieve dependencies with ivy">
<echo message="Getting dependencies..." />
<ivy:retrieve />
<ivy:cachepath pathid="compile.path" conf="compile" />
<ivy:cachepath pathid="runtime.path" conf="runtime" />
<ivy:cachepath pathid="test.path" conf="test" />
</target>
<!-- Compile Java source from ${src.dir} and output it to ${web.classes.dir} -->
<target name="compile" depends="init, resolve" description="compile source code">
<mkdir dir="${web.classes.dir}" />
<javac destdir="${web.classes.dir}" source="${jdk.version}"
target="${jdk.version}" debug="true"
includeantruntime="false" classpathref="compile.path">
<src path="${src.dir}" />
</javac>
</target>
<!-- Run jUnit -->
<target name="junit" depends="compile">
<junit printsummary="yes" haltonfailure="no">
<classpath refid="test.path" />
<classpath location="${web.classes.dir}" />
<formatter type="xml" />
<batchtest fork="yes" todir="${report.dir}">
<fileset dir="${test.dir}">
<include name="**/*Test*.java" />
</fileset>
</batchtest>
</junit>
</target>
<!-- Create folders -->
<target name="init">
<mkdir dir="${src.dir}" />
<mkdir dir="${web.classes.dir}" />
<mkdir dir="${report.dir}" />
</target>
<!-- Delete folders -->
<target name="clean" description="clean up">
<delete dir="${web.classes.dir}" />
<delete dir="${report.dir}" />
</target>
<target name="main" depends="junit" />
</project>
Run it
<pre><code class="language-bash">
$ ant junit
完成了。
下载源代码
Download It - AntSpringMVC-Junit-Example (42 KB)
参考
Ant 和 TestNG 任务示例
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/ant/ant-and-testng-task-example/
在本教程中,我们将向您展示如何在 Ant build 中运行 TestNG 测试。
1.按类运行
build.xml
<taskdef name="testng" classname="org.testng.TestNGAntTask">
<classpath location="lib/testng-6.8.14.jar" />
</taskdef>
<target name="testng" depends="compile">
<!-- Assume test.path contains the project library dependencies -->
<testng classpathref="test.path"
outputDir="${report.dir}"
haltOnFailure="true">
<!-- Extra project classpath, which is not included in above "test.path" -->
<!-- Tell Ant where is the project and test classes -->
<classpath location="${test.classes.dir}" />
<classpath location="${src.classes.dir}" />
<!-- Tell Ant what test classes need to run -->
<classfileset dir="${test.classes.dir}" includes="**/*Test*.class" />
</testng>
</target>
2.由 XML 运行
${resources.dir}/testng.xml
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="TestAll">
<test name="anyname">
<classes>
<class name="com.mkyong.test.TestMessage" />
</classes>
</test>
</suite>
build.xml
<taskdef name="testng" classname="org.testng.TestNGAntTask">
<classpath location="lib/testng-6.8.14.jar" />
</taskdef>
<target name="testng" depends="compile">
<testng classpathref="test.path"
outputDir="${report.dir}"
haltOnFailure="true">
<classpath location="${test.classes.dir}" />
<!-- Tell Ant where is testng.xml -->
<xmlfileset dir="${resources.dir}" includes="testng.xml"/>
</testng>
</target>
3.例子
一个 web 应用程序示例,向您展示如何运行一组 TestNG 测试。
3.1 返回消息
MessageGenerator.java
package com.mkyong.message;
import org.springframework.stereotype.Component;
@Component
public class MessageGenerator {
public String getWelcomeMessage() {
return "welcome";
}
}
3.2 两次测试。
TestMessage.java
package com.mkyong.test;
import org.testng.Assert;
import org.testng.annotations.Test;
import com.mkyong.message.MessageGenerator;
public class TestMessage {
@Test
public void test_welcome_message() {
MessageGenerator obj = new MessageGenerator();
Assert.assertEquals(obj.getWelcomeMessage(), "welcome");
}
}
TestMessage2.java
package com.mkyong.test;
import org.testng.Assert;
import org.testng.annotations.Test;
import com.mkyong.message.MessageGenerator;
public class TestMessage2 {
@Test
public void test_welcome_message_2() {
MessageGenerator obj = new MessageGenerator();
Assert.assertEquals(obj.getWelcomeMessage(), "welcome");
}
}
3.3 使用 ivy 获取项目依赖关系,并声明项目范围。
ivy.xml
<ivy-module version="2.0">
<info organisation="org.apache" module="WebProject" />
<configurations>
<conf name="compile" description="Required to compile application"/>
<conf name="runtime" description="Additional run-time dependencies" extends="compile"/>
<conf name="test" description="Required for test only" extends="runtime"/>
</configurations>
<dependencies>
<dependency org="org.testng" name="testng" rev="6.8.14" conf="test->default" />
</dependencies>
</ivy-module>
3.4 运行单元测试
build.xml
<project xmlns:ivy="antlib:org.apache.ivy.ant"
name="HelloProject" default="main" basedir=".">
<description>
Running TestNG Test
</description>
<!-- Project Structure -->
<property name="jdk.version" value="1.7" />
<property name="projectName" value="WebProject" />
<property name="src.dir" location="src" />
<property name="test.dir" location="src" />
<property name="report.dir" location="report" />
<property name="web.dir" value="war" />
<property name="web.classes.dir" location="${web.dir}/WEB-INF/classes" />
<!-- ivy start -->
<target name="resolve" description="retrieve dependencies with ivy">
<echo message="Getting dependencies..." />
<ivy:retrieve />
<ivy:cachepath pathid="compile.path" conf="compile" />
<ivy:cachepath pathid="runtime.path" conf="runtime" />
<ivy:cachepath pathid="test.path" conf="test" />
</target>
<!-- Compile Java source from ${src.dir} and output it to ${web.classes.dir} -->
<target name="compile" depends="init, resolve" description="compile source code">
<mkdir dir="${web.classes.dir}" />
<javac destdir="${web.classes.dir}" source="${jdk.version}"
target="${jdk.version}" debug="true" includeantruntime="false" classpathref="compile.path">
<src path="${src.dir}" />
</javac>
</target>
<!-- Run TestNG -->
<target name="testng" depends="compile">
<testng classpathref="test.path"
outputDir="${report.dir}"
haltOnFailure="true">
<classpath location="${web.classes.dir}" />
<xmlfileset dir="${resources.dir}" includes="testng.xml"/>
<!--
<classfileset dir="${web.classes.dir}" includes="**/*Test*.class" />
-->
</testng>
</target>
<!-- Create folders -->
<target name="init">
<mkdir dir="${src.dir}" />
<mkdir dir="${web.classes.dir}" />
<mkdir dir="${report.dir}" />
</target>
<!-- Delete folders -->
<target name="clean" description="clean up">
<delete dir="${web.classes.dir}" />
<delete dir="${report.dir}" />
</target>
<target name="main" depends="testng" />
</project>
${resources.dir}/testng.xml
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="TestAll">
<test name="example1">
<classes>
<class name="com.mkyong.test.TestMessage" />
<class name="com.mkyong.test.TestMessage2" />
</classes>
</test>
</suite>
运行它
$ ant testng
输出
testng:
[testng] [TestNG] Running:
[testng] /Users/mkyong/Documents/workspace/AntSpringMVC/resources/testng.xml
[testng]
[testng]
[testng] ===============================================
[testng] TestAll
[testng] Total tests run: 2, Failures: 0, Skips: 0
[testng] ===============================================
[testng]
BUILD SUCCESSFUL
Total time: 3 seconds
完成了。
下载源代码
Download It – AntSpringMVC-TestNG-Example (90 KB)
参考
如果不在 Ant 自己的类路径中,则必须包含 junit.jar
在 Ant 中声明一个 junit 任务,如下所示
build.xml
<!-- Run jUnit -->
<target name="junit" depends="resolve">
<junit printsummary="yes" haltonfailure="no">
<classpath refid="test.path" />
<classpath location="${build.dir}" />
<test name="com.mkyong.test.TestMessage"
haltonfailure="no" todir="${report.dir}" outfile="result">
<formatter type="plain" />
<formatter type="xml" />
</test>
</junit>
</target>
运行ant junit
,但出现以下错误信息:
BUILD FAILED
build.xml:86: The <classpath> for <junit> must include junit.jar if not in Ant's own classpath
解决办法
要在 Ant 中运行 junit 任务,请确保在类路径中定义了junit.jar
。
build.xml
<!-- Run jUnit -->
<target name="junit" depends="resolve">
<junit printsummary="yes" haltonfailure="no">
<classpath refid="test.path" />
<classpath location="${build.dir}" />
<!-- Make sure these two libraries are included -->
<classpath location="lib/junit-4.11.jar" />
<classpath location="lib/hamcrest-core-1.3.jar" />
<test name="com.mkyong.test.TestMessage"
haltonfailure="no" todir="${report.dir}" outfile="result">
<formatter type="plain" />
<formatter type="xml" />
</test>
</junit>
</target>
参考
- 蚂蚁 jUnit 任务
- Ant 常见问题:junit 忽略了我的类路径
ant——如何用外部库创建 Jar 文件
在本教程中,我们将向您展示如何使用 Ant 构建脚本来创建 Jar 文件,以及如何使用项目的外部库/依赖项。
使用的技术:
- Eclipse 4.2
- JDK 1.7
- Ant 1.9.4
- 蚂蚁常春藤 2.4
- 回溯 1.1.2
- joda-时间 2.5
P.S 之前的 Ant Java 项目将被重用。
1.项目结构
图 1.1:最终的项目目录结构,在 Eclipse IDE 中。
2.Java 项目+外部库
在 Eclipse IDE 中,重新打开之前的 Java 项目 AntDateUtils ,更新源代码使用logback
和joda-time
。
src/com/mkyong/core/utils/DateUtils.java
package com.mkyong.core.utils;
import org.joda.time.LocalDate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DateUtils {
private static final Logger logger = LoggerFactory.getLogger(DateUtils.class);
public static void main(String[] args) {
logger.debug("[MAIN] Current Date : {}", getLocalCurrentDate());
System.out.println(getLocalCurrentDate());
}
private static String getLocalCurrentDate() {
LocalDate date = new LocalDate();
return date.toString();
}
}
创建一个logback.xml
并把它放在项目src
文件夹中。参见图 1.1
src/logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>
ANT + LogBack : %-5level %logger{36} - %msg%n
</Pattern>
</layout>
</appender>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>
3.ivy–获取外部库
我们使用 Apache Ivy 来获取项目的外部库/依赖项。
3.1 创建该文件ivy.xml
:
ivy.xml
<ivy-module version="2.0">
<info organisation="org.apache" module="dateUtilsProject" />
<dependencies>
<dependency org="joda-time" name="joda-time" rev="2.5" />
<dependency org="org.slf4j" name="slf4j-api" rev="1.7.6" />
<dependency org="ch.qos.logback" name="logback-classic" rev="1.1.2" />
</dependencies>
</ivy-module>
3.2 更新build.xml
,在顶部添加 ivy 名称空间,以及下载 ivy 模块的“ivy”任务,以及要求 Ivy 模块下载外部库的“resolve”任务。
build.xml
<project xmlns:ivy="antlib:org.apache.ivy.ant"
name="dateUtilsProject" default="main" basedir=".">
<!-- ivy start -->
<!-- ivy to get dependencies and copy to project lib folder automatically -->
<target name="resolve" description="retrieve dependencies with ivy">
<ivy:retrieve />
</target>
<!-- install ivy -->
<target name="ivy" description="Install ivy">
<mkdir dir="${user.home}/.ant/lib" />
<get dest="${user.home}/.ant/lib/ivy.jar"
src="http://search.maven.org/remotecontent?filepath=org/apache/ivy/ivy/2.4.0-rc1/ivy-2.4.0-rc1.jar" />
</target>
<!-- ivy end -->
</project>
第一次,从 Maven center 资源库下载 ivy 模块到本地${user.home}/.ant/lib/ivy.jar
。
$ ant ivy
要下载外部库,请运行任务“解决”。声明的库将被下载到项目lib
文件夹。
$ ant resolve
4.build.xml
查看更新后的build.xml
脚本,阅读注释以了解自明性。
要点:
- 使用 Apache Ivy 管理项目外部库,检查顶层的 Ivy 名称空间,并执行“resolve”任务。
- 要编译源代码,您需要声明类路径。查看任务“编译”和“classpathref”属性。
- 在“jar”任务中,构建外部库的完整列表,并将其放入
manifest.mf
文件中。 - 在“jar”任务中,项目 jar 将被打包到文件夹“dist ”,整个外部库将被从“lib”复制到“dist/lib”。
build.xml
<project xmlns:ivy="antlib:org.apache.ivy.ant"
name="dateUtilsProject" default="main" basedir=".">
<description>
Create a Java Project (JAR) with Ant build script
</description>
<property name="projectName" value="DateUtils" />
<property name="src.dir" location="src" />
<property name="build.dir" location="bin" />
<property name="dist.dir" location="dist" />
<property name="dist.lib.dir" location="dist/lib" />
<property name="lib.dir" value="lib" />
<property name="main-class" value="com.mkyong.core.utils.DateUtils" />
<!-- ivy start -->
<!-- ivy to get dependencies and copy to project lib folder automatically -->
<target name="resolve" description="retrieve dependencies with ivy">
<ivy:retrieve />
</target>
<!-- install ivy -->
<target name="ivy" description="Install ivy">
<mkdir dir="${user.home}/.ant/lib" />
<get dest="${user.home}/.ant/lib/ivy.jar" src="http://search.maven.org/remotecontent?filepath=org/apache/ivy/ivy/2.4.0-rc1/ivy-2.4.0-rc1.jar" />
</target>
<!-- ivy end -->
<target name="init">
<mkdir dir="${build.dir}" />
</target>
<!-- external libraries classpath, we don't need sources and javadoc -->
<path id="classpath">
<fileset dir="${basedir}/">
<include name="${lib.dir}/*.jar" />
<exclude name="${lib.dir}/*sources.jar"/>
<exclude name="${lib.dir}/*javadoc.jar"/>
</fileset>
</path>
<!-- To work with external libraries, need classpath to compile -->
<target name="compile" depends="init" description="compile the source ">
<javac includeantruntime="false" srcdir="${src.dir}" destdir="${build.dir}" classpathref="classpath" />
</target>
<!-- constructs the external libraries classpath name -->
<pathconvert property="classpath.name" pathsep=" ">
<path refid="classpath" />
<mapper>
<chainedmapper>
<flattenmapper />
<globmapper from="*.jar" to="lib/*.jar" />
</chainedmapper>
</mapper>
</pathconvert>
<target name="copy-dependencies">
<copy todir="${dist.lib.dir}">
<fileset dir="${lib.dir}" includes="**/*.jar" excludes="**/*sources.jar, **/*javadoc.jar" />
</copy>
</target>
<!-- jar it, and declares the ext libraries in manifest.mf file -->
<target name="jar" depends="compile, copy-dependencies" description="package, output to JAR">
<echo message="classpath.name : ${classpath.name} " />
<mkdir dir="${dist.dir}" />
<mkdir dir="${dist.lib.dir}" />
<jar jarfile="${dist.dir}/${projectName}.jar" basedir="${build.dir}">
<manifest>
<attribute name="Main-Class" value="${main-class}" />
<attribute name="Class-Path" value="${classpath.name}" />
</manifest>
</jar>
</target>
<target name="clean" description="clean up">
<delete dir="${build.dir}" />
<delete dir="${dist.dir}" />
</target>
<!-- Default, run this -->
<target name="main" depends="clean, compile, jar" />
</project>
5.试验
用 Ant 构建脚本测试 Java 项目。
5.1 装罐。
$ pwd
/Users/mkyong/Documents/workspace/AntDateUtils
$ ant
Buildfile: /Users/mkyong/Documents/workspace/AntDateUtils/build.xml
clean:
[delete] Deleting directory /Users/mkyong/Documents/workspace/AntDateUtils/bin
[delete] Deleting directory /Users/mkyong/Documents/workspace/AntDateUtils/dist
init:
[mkdir] Created dir: /Users/mkyong/Documents/workspace/AntDateUtils/bin
compile:
[javac] Compiling 1 source file to /Users/mkyong/Documents/workspace/AntDateUtils/bin
copy-dependencies:
[copy] Copying 12 files to /Users/mkyong/Documents/workspace/AntDateUtils/dist/lib
jar:
[echo] classpath.name : ... lib/joda-time-2.5.jar lib/logback-classic-1.1.2.jar lib/logback-core-1.1.2.jar lib/mail-1.4.jar ...
[jar] Building jar: /Users/mkyong/Documents/workspace/AntDateUtils/dist/DateUtils.jar
main:
BUILD SUCCESSFUL
Total time: 1 second
5.2 检查生成的 jar 文件。
$ jar -tf dist/DateUtils.jar
META-INF/
META-INF/MANIFEST.MF
com/
com/mkyong/
com/mkyong/core/
com/mkyong/core/utils/
com/mkyong/core/utils/DateUtils.class
META-INF/MANIFEST.MF
Manifest-Version: 1.0
Ant-Version: Apache Ant 1.9.4
Created-By: 1.7.0_05-b05 (Oracle Corporation)
Main-Class: com.mkyong.core.utils.DateUtils
Class-Path: lib/activation-1.1.jar lib/commons-compiler-2.6.1.jar lib/
geronimo-jms_1.1_spec-1.0.jar lib/groovy-all-2.0.7.jar lib/janino-2.6
.1.jar lib/joda-convert-1.2.jar lib/joda-time-2.5.jar lib/logback-cla
ssic-1.1.2.jar lib/logback-core-1.1.2.jar lib/mail-1.4.jar lib/servle
t-api-2.5.jar lib/slf4j-api-1.7.6.jar
5.3 运行 Jar 文件。
$ pwd
/Users/mkyong/Documents/workspace/AntDateUtils
$ java -jar dist/DateUtils.jar
16:28:43.957 [main] DEBUG com.mkyong.core.utils.DateUtils - [MAIN] Current Date : 2014-11-21
2014-11-21
5.4 用logback.xml
再次运行 Jar 文件。
$ pwd
/Users/mkyong/Documents/workspace/AntDateUtils
$ java -jar -Dlogback.configurationFile=src/logback.xml dist/DateUtils.jar
16:34:43,746 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Found resource [src/logback.xml] at [file:/Users/mkyong/Documents/workspace/AntDateUtils/src/logback.xml]
//...
ANT + LogBack : DEBUG com.mkyong.core.utils.DateUtils - [MAIN] Current Date : 2014-11-21
2014-11-21
下载源代码
Download it – AntDateUtils-External-Libraries.zip (8 KB)
参考
ant——如何创建 Java 项目
在本教程中,我们将向您展示如何使用 Ant 构建工具来管理 Java 项目、编译并将其打包到 Jar 文件中。
使用的技术:
- Eclipse 4.2
- Ant 1.9.4
- JDK 1.7
1.创建一个 Java 项目
在 Eclipse IDE 中,创建一个名为“AntDateUtils”的新 Java 项目。
2.Java 源代码
创建一个新的 Java 类来打印当前日期:
src/com/mkyong/core/utils/DateUtils.java
package com.mkyong.core.utils;
import java.util.Date;
public class DateUtils {
public static void main(String[] args) {
System.out.println(getLocalCurrentDate());
}
private static Date getLocalCurrentDate() {
return new Date();
}
}
3.build.xml
在项目根文件夹中创建一个新的build.xml
,阅读注释进行自我解释。
build.xml
<project name="AntJavaProject" default="main" basedir=".">
<description>
Create a Java Project (JAR) with Ant build script
</description>
<property name="projectName" value="DateUtils" />
<!-- Java sources -->
<property name="src.dir" location="src" />
<!-- Java classes -->
<property name="build.dir" location="bin" />
<!-- Output, Jar -->
<property name="dist.dir" location="dist" />
<target name="init">
<!-- Create the time stamp -->
<tstamp />
<!-- Create the build directory structure used by compile -->
<mkdir dir="${build.dir}" />
</target>
<target name="compile" depends="init" description="compile the source ">
<!-- Compile the java code from ${src.dir} into ${build.dir} -->
<javac includeantruntime="false" srcdir="${src.dir}" destdir="${build.dir}" />
</target>
<target name="dist" depends="compile" description="package, output to JAR">
<!-- Create the distribution directory -->
<mkdir dir="${dist.dir}" />
<!-- Put everything in ${build} into the {$projectName}-${DSTAMP}.jar file -->
<jar jarfile="${dist.dir}/${projectName}-${DSTAMP}.jar" basedir="${build.dir}" >
<manifest>
<!-- create an executable Jar -->
<attribute name="Main-Class" value="com.mkyong.core.utils.DateUtils" />
</manifest>
</jar>
</target>
<target name="clean" description="clean up">
<delete dir="${build.dir}" />
<delete dir="${dist.dir}" />
</target>
<!-- Default, run this -->
<target name="main" depends="clean, compile, dist" />
</project>
4.Ant 构建脚本
完成后,尝试几个 Ant 的命令
4.1 编译源代码
$ ant compile
build.xml
<target name="compile" depends="init" description="compile the source ">
<javac includeantruntime="false" srcdir="${src.dir}" destdir="${build.dir}" />
</target>
4.2 将项目打包成一个可执行的 Jar 文件
$ ant dist
build.xml
<target name="dist" depends="compile" description="package, output to JAR">
<mkdir dir="${dist.dir}" />
<jar jarfile="${dist.dir}/${projectName}-${DSTAMP}.jar" basedir="${build.dir}">
<manifest>
<attribute name="Main-Class" value="com.mkyong.core.utils.DateUtils" />
</manifest>
</jar>
</target>
4.3 删除文件夹
$ ant clean
build.xml
<target name="clean" description="clean up">
<delete dir="${build.dir}" />
<delete dir="${dist.dir}" />
</target>
4.4 如果没有选项,默认目标将被执行,在本例中,默认目标是main
build.xml
<project name="AntJavaProject" default="main" basedir=".">
...
<target name="main" depends="clean, compile, dist" />
$ ant
输出
Buildfile: /Users/mkyong/Documents/workspace/AntDateUtils/build.xml
clean:
[delete] Deleting directory /Users/mkyong/Documents/workspace/AntDateUtils/bin
[delete] Deleting directory /Users/mkyong/Documents/workspace/AntDateUtils/dist
init:
[mkdir] Created dir: /Users/mkyong/Documents/workspace/AntDateUtils/bin
compile:
[javac] Compiling 1 source file to /Users/mkyong/Documents/workspace/AntDateUtils/bin
dist:
[mkdir] Created dir: /Users/mkyong/Documents/workspace/AntDateUtils/dist
[jar] Building jar: /Users/mkyong/Documents/workspace/AntDateUtils/dist/DateUtils-20141030.jar
main:
BUILD SUCCESSFUL
Total time: 1 second
最终目录结构
5.试验
5.1 在 Jar 文件中运行一个类。
$ pwd
/Users/mkyong/Documents/workspace/AntDateUtils
$ java -cp dist/DateUtils-20141030.jar com.mkyong.core.utils.DateUtils
Thu Oct 30 17:39:21 MYT 2014
5.2 运行可执行的 Jar 文件
$ pwd
/Users/mkyong/Documents/workspace/AntDateUtils
$ java -jar dist/DateUtils-20141030.jar
Thu Oct 30 17:40:21 MYT 2014
下载源代码
Download It – AntDateUtils.zip (6 KB)
参考
ant——如何从路径 id 打印类路径
在 Ant 中,可以使用pathconvert
任务从 path 中打印出类路径:
build.xml
<path id="project.web.classpath">
<pathelement location="test/lib/junit-4.11.jar"/>
<pathelement location="test/lib/hamcrest-core-1.3.jar"/>
<fileset dir="${lib.web.dir}">
<include name="**/*.jar" />
</fileset>
</path>
<target name="print-classpath">
<pathconvert property="classpathInName" refid="project.web.classpath" />
<echo>Classpath is ${classpathInName}</echo>
</target>
测试:
$ ant print-classpath
Buildfile: /Users/mkyong/Documents/workspace/AntSpringMVC/build.xml
print-classpath:
[echo] Classpath is
/Users/mkyong/Documents/workspace/AntSpringMVC/test/lib/junit-4.11.jar:
/Users/mkyong/Documents/workspace/AntSpringMVC/testlib/hamcrest-core-1.3.jar:
......
BUILD SUCCESSFUL
Total time: 0 seconds
参考
ant (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','//web.archive.org/web/20190115040926/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0')
ant-Spring MVC 和 WAR 文件示例
在本教程中,我们将向您展示如何使用 Ant 构建脚本来管理 Spring MVC web 应用程序项目,创建 WAR 文件并部署到 Tomcat。
使用的技术:
- Eclipse 4.2
- JDK 1.7
- Ant 1.9.4
- 蚂蚁常春藤 2.4
- 回溯 1.1.2
- jstl 1.2
- 弹簧 4.1.3 .释放
- Tomcat 7
1.项目目录
审查最终项目结构:
- 资源——放置 Java 源代码所需的 xml 和属性文件。比如 log4j.properties,logback.xml,db.properties 等等。
- src–放 Java 源代码。
- 目标–最终的 WAR 文件将存储在这里。
- 战争——与网络相关的东西。
- WAR/WEB-INF–WAR 文件的标准 we b-INF 目录结构。
- war/resources–用于 css 和 js 文件
2.项目相关性
我们使用 Ivy 下载项目依赖关系:
ivy.xml
<ivy-module version="2.0">
<info organisation="org.apache" module="WebProject" />
<!-- Classpath management, thanks Maven -->
<configurations>
<conf name="compile" description="compile dependencies"/>
<conf name="runtime" description="runtime dependencies" extends="compile"/>
<conf name="test" description="test dependencies" extends="runtime"/>
</configurations>
<dependencies>
<dependency org="org.slf4j" name="slf4j-api" rev="1.7.6" conf="compile->default"/>
<dependency org="jstl" name="jstl" rev="1.2" conf="compile->default"/>
<dependency org="ch.qos.logback" name="logback-classic" rev="1.1.2" conf="compile->default"/>
<dependency org="org.springframework" name="spring-core" rev="4.1.3.RELEASE" conf="compile->default"/>
<dependency org="org.springframework" name="spring-beans" rev="4.1.3.RELEASE" conf="compile->default"/>
<dependency org="org.springframework" name="spring-context" rev="4.1.3.RELEASE" conf="compile->default"/>
<dependency org="org.springframework" name="spring-web" rev="4.1.3.RELEASE" conf="compile->default"/>
<dependency org="org.springframework" name="spring-webmvc" rev="4.1.3.RELEASE" conf="compile->default"/>
</dependencies>
</ivy-module>
3.Ant 构建脚本
Ant 脚本来管理这个 Spring MVC 项目,不言自明。
build.xml
<project xmlns:ivy="antlib:org.apache.ivy.ant" name="HelloProject" default="main" basedir=".">
<description>
Create a Spring MVC (WAR) with Ant build script
</description>
<!-- Project Structure -->
<property name="jdk.version" value="1.7" />
<property name="projectName" value="WebProject" />
<property name="src.dir" location="src" />
<property name="resources.dir" location="resources" />
<property name="web.dir" value="war" />
<property name="web.classes.dir" location="${web.dir}/WEB-INF/classes" />
<property name="target.dir" location="target" />
<property name="target.temp.dir" location="target/temp" />
<property name="lib.dir" value="lib" />
<!-- ivy start -->
<target name="resolve" description="retrieve dependencies with ivy">
<echo message="Getting dependencies..." />
<ivy:retrieve />
<ivy:cachepath pathid="compile.path" conf="compile" />
<ivy:cachepath pathid="runtime.path" conf="runtime" />
<ivy:cachepath pathid="test.path" conf="test" />
</target>
<!-- install ivy if you don't have ivyide-->
<target name="ivy" description="Install ivy">
<mkdir dir="${user.home}/.ant/lib" />
<get dest="${user.home}/.ant/lib/ivy.jar"
src="http://search.maven.org/remotecontent?filepath=org/apache/ivy/ivy/2.4.0-rc1/ivy-2.4.0-rc1.jar" />
</target>
<!-- ivy end -->
<!-- Compile Java source from ${src.dir} and output it to ${web.classes.dir} -->
<target name="compile" depends="init, resolve" description="compile source code">
<mkdir dir="${web.classes.dir}" />
<javac destdir="${web.classes.dir}" source="${jdk.version}" target="${jdk.version}"
debug="true" includeantruntime="false" classpathref="compile.path">
<src path="${src.dir}" />
</javac>
</target>
<!-- Copy *.xml or *.properties from ${resources.dir} to ${web.classes.dir} -->
<target name="copy-resources"
description="copy Java resources like xml and properties files">
<copy todir="${web.classes.dir}">
<fileset dir="${resources.dir}">
<include name="**/*.xml" />
<include name="**/*.properties" />
</fileset>
</copy>
</target>
<!-- Create the final WAR file for deployment -->
<target name="package" depends="clean, compile, copy-resources"
description="create a war file">
<!-- ivy get dependencies and put it in ${lib.dir} -->
<ivy:retrieve pattern="${lib.dir}/[artifact]-[revision].[ext]" conf="runtime" />
<war destfile="${target.dir}/helloproject.war" webxml="${web.dir}/WEB-INF/web.xml">
<webinf dir="${web.dir}/WEB-INF" />
<lib dir="${lib.dir}" />
<zipfileset dir="${web.dir}/resources" prefix="resources" />
</war>
</target>
<!-- Create folders -->
<target name="init">
<mkdir dir="${src.dir}" />
<mkdir dir="${target.dir}" />
<mkdir dir="${web.classes.dir}" />
</target>
<!-- Delete folders -->
<target name="clean" description="clean up">
<delete dir="${web.classes.dir}" />
<delete dir="${target.dir}" />
</target>
<target name="main" depends="package" />
</project>
4.Spring MVC、JSP 和 XML 文件
如果您感兴趣,这里有完整的 Java、XML 和 JSP 文件。
4.1 Spring MVC 控制器类。
WelcomeController.java
package com.mkyong.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class WelcomeController {
private static final String VIEW_INDEX = "index";
private final static Logger logger = LoggerFactory.getLogger(WelcomeController.class);
@RequestMapping(value = "/", method = RequestMethod.GET)
public String welcome(ModelMap model) {
model.addAttribute("message", "");
logger.debug("[welcome]");
return VIEW_INDEX;
}
@RequestMapping(value = "/{name}", method = RequestMethod.GET)
public String welcomeName(@PathVariable String name, ModelMap model) {
model.addAttribute("message", "Welcome " + name);
logger.debug("[welcomeName] name : {}", name);
return VIEW_INDEX;
}
}
4.2 简单的日志返回配置文件,用于日志记录。
logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>
ANT + LogBack : %-5level %logger{36} - %msg%n
</Pattern>
</layout>
</appender>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>
4.3 JSP 和 CSS 文件。
war/WEB-INF/pages/index.jsp
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<c:url var="resources" value="/resources/theme1" scope="request" />
<html>
<head>
<link href="${resources}/css/core.css" rel="stylesheet">
</head>
<body>
<h1>Ant + Spring MVC Web Project Example</h1>
<p>Message : ${message}</p>
</body>
</html>
war/resources/theme1/css/core.css
h1 {
font-size: 50px;
}
p {
font-size: 20px;
border: 5px solid red;
padding:10px;
}
4.4 弹簧配置。
mvc-dispatcher-servlet.xml
<beans
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.mkyong.controller" />
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/WEB-INF/pages/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
<!-- for web resources like images, css and js files -->
<mvc:resources mapping="/resources/**" location="/resources/" />
<mvc:annotation-driven />
</beans>
4.5 一个标准的 web.xml,与 Spring 容器集成。
web.xml
<web-app
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_2_5.xsd"
version="2.5">
<display-name>Ant + Spring MVC Web Application</display-name>
<servlet>
<servlet-name>mvc-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/mvc-dispatcher-servlet.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
5.运行和测试
5.1 默认情况下,它会运行package
任务。
$ pwd
/Users/mkyong/Documents/workspace/AntSpringMVC
$ ant
Buildfile: /Users/mkyong/Documents/workspace/AntSpringMVC/build.xml
clean:
[delete] Deleting directory /Users/mkyong/Documents/workspace/AntSpringMVC/war/WEB-INF/classes
[delete] Deleting directory /Users/mkyong/Documents/workspace/AntSpringMVC/target
init:
[mkdir] Created dir: /Users/mkyong/Documents/workspace/AntSpringMVC/target
[mkdir] Created dir: /Users/mkyong/Documents/workspace/AntSpringMVC/war/WEB-INF/classes
resolve:
[echo] Getting dependencies...
[ivy:retrieve] :: Apache Ivy 2.4.0-rc1 - 20140315220245 :: http://ant.apache.org/ivy/ ::
[ivy:retrieve] :: loading settings :: url = jar:file:/Users/mkyong/.ant/lib/ivy.jar!/org/apache/ivy/core/settings/ivysettings.xml
[ivy:retrieve] :: resolving dependencies :: org.apache#WebProject;working@mkyong-mac
[ivy:retrieve] confs: [compile, runtime, test]
[ivy:retrieve] found org.slf4j#slf4j-api;1.7.6 in public
[ivy:retrieve] found jstl#jstl;1.2 in public
[ivy:retrieve] found ch.qos.logback#logback-classic;1.1.2 in public
[ivy:retrieve] found ch.qos.logback#logback-core;1.1.2 in public
[ivy:retrieve] found org.springframework#spring-core;4.1.3.RELEASE in public
[ivy:retrieve] found commons-logging#commons-logging;1.2 in public
[ivy:retrieve] found org.springframework#spring-beans;4.1.3.RELEASE in public
[ivy:retrieve] found org.springframework#spring-context;4.1.3.RELEASE in public
[ivy:retrieve] found org.springframework#spring-aop;4.1.3.RELEASE in public
[ivy:retrieve] found aopalliance#aopalliance;1.0 in public
[ivy:retrieve] found org.springframework#spring-expression;4.1.3.RELEASE in public
[ivy:retrieve] found org.springframework#spring-web;4.1.3.RELEASE in public
[ivy:retrieve] found org.springframework#spring-webmvc;4.1.3.RELEASE in public
[ivy:retrieve] :: resolution report :: resolve 974ms :: artifacts dl 19ms
---------------------------------------------------------------------
| | modules || artifacts |
| conf | number| search|dwnlded|evicted|| number|dwnlded|
---------------------------------------------------------------------
| compile | 13 | 0 | 0 | 0 || 13 | 0 |
| runtime | 13 | 0 | 0 | 0 || 13 | 0 |
| test | 13 | 0 | 0 | 0 || 13 | 0 |
---------------------------------------------------------------------
[ivy:retrieve] :: retrieving :: org.apache#WebProject
[ivy:retrieve] confs: [compile, runtime, test]
[ivy:retrieve] 2 artifacts copied, 11 already retrieved (682kB/31ms)
compile:
[javac] Compiling 1 source file to /Users/mkyong/Documents/workspace/AntSpringMVC/war/WEB-INF/classes
copy-resources:
[copy] Copying 1 file to /Users/mkyong/Documents/workspace/AntSpringMVC/war/WEB-INF/classes
package:
[ivy:retrieve] :: retrieving :: org.apache#WebProject
[ivy:retrieve] confs: [runtime]
[ivy:retrieve] 0 artifacts copied, 13 already retrieved (0kB/11ms)
[war] Building war: /Users/mkyong/Documents/workspace/AntSpringMVC/target/helloproject.war
main:
BUILD SUCCESSFUL
Total time: 3 seconds
根据您开发环境,上述结果可能会有所不同。如果构建成功,最终的 WAR 文件将被创建在target
文件夹中。
5.2 查看生成的 WAR 文件的内容—helloproject.war
mkyong-mac:AntSpringMVC mkyong$ tar -tvf target/helloproject.war
drwxrwxrwx 0 0 0 0 Dec 26 16:55 META-INF/
-rwxrwxrwx 0 0 0 103 Dec 26 16:55 META-INF/MANIFEST.MF
drwxrwxrwx 0 0 0 0 Dec 26 16:55 WEB-INF/
-rwxrwxrwx 0 0 0 897 Dec 24 15:05 WEB-INF/web.xml
drwxrwxrwx 0 0 0 0 Dec 26 16:55 WEB-INF/classes/
drwxrwxrwx 0 0 0 0 Dec 26 16:55 WEB-INF/classes/com/
drwxrwxrwx 0 0 0 0 Dec 26 16:55 WEB-INF/classes/com/mkyong/
drwxrwxrwx 0 0 0 0 Dec 26 16:55 WEB-INF/classes/com/mkyong/controller/
drwxrwxrwx 0 0 0 0 Dec 24 17:05 WEB-INF/lib/
drwxrwxrwx 0 0 0 0 Dec 24 15:04 WEB-INF/pages/
-rwxrwxrwx 0 0 0 1903 Dec 26 16:55 WEB-INF/classes/com/mkyong/controller/WelcomeController.class
-rwxrwxrwx 0 0 0 372 Dec 26 16:55 WEB-INF/classes/logback.xml
-rwxrwxrwx 0 0 0 1030 Dec 25 13:41 WEB-INF/mvc-dispatcher-servlet.xml
-rwxrwxrwx 0 0 0 311 Dec 25 13:44 WEB-INF/pages/index.jsp
-rwxrwxrwx 0 0 0 4467 Aug 1 2005 WEB-INF/lib/aopalliance-1.0.jar
-rwxrwxrwx 0 0 0 61829 Jul 6 02:12 WEB-INF/lib/commons-logging-1.2.jar
-rwxrwxrwx 0 0 0 414240 Jul 22 2006 WEB-INF/lib/jstl-1.2.jar
-rwxrwxrwx 0 0 0 270750 Apr 3 2014 WEB-INF/lib/logback-classic-1.1.2.jar
-rwxrwxrwx 0 0 0 427729 Apr 3 2014 WEB-INF/lib/logback-core-1.1.2.jar
-rwxrwxrwx 0 0 0 28688 Feb 6 2014 WEB-INF/lib/slf4j-api-1.7.6.jar
-rwxrwxrwx 0 0 0 359018 Dec 9 18:48 WEB-INF/lib/spring-aop-4.1.3.RELEASE.jar
-rwxrwxrwx 0 0 0 707820 Dec 9 18:48 WEB-INF/lib/spring-beans-4.1.3.RELEASE.jar
-rwxrwxrwx 0 0 0 1026840 Dec 9 18:48 WEB-INF/lib/spring-context-4.1.3.RELEASE.jar
-rwxrwxrwx 0 0 0 1006709 Dec 9 18:48 WEB-INF/lib/spring-core-4.1.3.RELEASE.jar
-rwxrwxrwx 0 0 0 259164 Dec 9 18:48 WEB-INF/lib/spring-expression-4.1.3.RELEASE.jar
-rwxrwxrwx 0 0 0 713531 Dec 9 18:48 WEB-INF/lib/spring-web-4.1.3.RELEASE.jar
-rwxrwxrwx 0 0 0 781676 Dec 9 18:48 WEB-INF/lib/spring-webmvc-4.1.3.RELEASE.jar
drwxrwxrwx 0 0 0 0 Dec 26 16:48 resources/
drwxrwxrwx 0 0 0 0 Dec 25 13:30 resources/theme1/
drwxrwxrwx 0 0 0 0 Dec 25 13:27 resources/theme1/css/
-rwxrwxrwx 0 0 0 88 Dec 25 13:45 resources/theme1/css/core.css
所有文件都是放置属性,要部署它只需复制helloproject.war
并将其放入$Tomcat/webapps
文件夹。
5.3 http://localhost:8080/hello project/
5.4 http://localhost:8080/hello project/mkyong
Note
Follow up : how to debug this ant-ivy project in Eclipse IDE.
下载源代码
Download It – Ant-SpringMVC-Example.zip (26 KB)
参考
- 蚂蚁常春藤检索模式
- 蚂蚁类型 zipfileset
- 蚂蚁大战任务
- 蚂蚁复制任务
- Stackoverflow : Ivy 类路径管理
- W3schools CSS Border
- Ant–如何使用外部库创建 Jar 文件
- 伊夫德 WTP 集成
- Maven:如何创建 web 项目
- Spring IO : Spring MVC 步步为营
构建 Java 项目的 Ant 模板文件
这里有一个 Apache Ant 模板文件,最适合用来从头开始一个项目。
文件:build.xml
<project name="ProjectName" default="dist" basedir=".">
<description>
Project Description
</description>
<!-- set global properties for this build -->
<property name="projectName" value="ProjectName"/>
<property name="src" location="src"/>
<property name="build" location="build"/>
<property name="dist" location="dist"/>
<property name="webcontent" location="WebContent"/>
<target name="init">
<!-- Create the time stamp -->
<tstamp/>
<!-- Create the build directory structure used by compile -->
<mkdir dir="${build}"/>
</target>
<target name="compile" depends="init"
description="compile the source " >
<!-- Compile the java code from ${src} into ${build} -->
<javac srcdir="${src}" destdir="${build}"/>
</target>
<target name="dist" depends="compile"
description="generate the distribution" >
<!-- Create the distribution directory -->
<mkdir dir="${dist}/lib"/>
<!-- Put everything in ${build} into the {$projectName}-${DSTAMP}.jar file -->
<jar jarfile="${dist}/lib/${projectName}-${DSTAMP}.jar" basedir="${build}"/>
</target>
<target name="war" depends="compile"
description="generate the distribution war" >
<!-- Create the war distribution directory -->
<mkdir dir="${dist}/war"/>
<!-- Follow standard WAR structure -->
<copydir dest="${dist}/war/build/WEB-INF/" src="${webcontent}/WEB-INF/" />
<copydir dest="${dist}/war/build/WEB-INF/classes/" src="${build}" />
<jar jarfile="${dist}/war/${projectName}-${DSTAMP}.war" basedir="${dist}/war/build/"/>
</target>
<target name="clean"
description="clean up" >
<!-- Delete the ${build} and ${dist} directory trees -->
<delete dir="${build}"/>
<delete dir="${dist}"/>
</target>
</project>
Apache Ant 教程
原文:http://web.archive.org/web/20230101150211/https://mkyong.com/tutorials/apache-ant-tutorial/
Apache Ant ,是一个经典的 Java 项目构建工具。
P.S Apache Ant 版本:1.9.4
1.教程
- 如何在 Windows 上安装 Ant
- 如何在 Mac OSX 上安装 Ant
- Ant–如何创建 Java 项目
- Ant–如何使用外部库创建 Jar 文件
- Ant–创建一个 fat jar 文件
- Ant-Spring MVC 和 WAR 文件示例
- 如何在 Eclipse IDE 中调试 Ant Ivy 项目
- Ant 和 jUnit 任务示例
- Ant 和 TestNG 任务示例
常见问题解答
ant tutorials (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','//web.archive.org/web/20190214234113/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0')
将 Android 源代码附加到 Eclipse IDE
默认情况下, Android SDK 或 Eclipse ADT 插件不捆绑任何 Android 的源代码进行调试。在 Eclipse IDE 中,进入任何 Android 类都将提示没有附加源代码,请参见下面的屏幕:
解决办法
根据这个官方的 Android 源代码文章,震惊的是我们需要使用repo
下载然后构建完整的源代码来获得android.jar
的源代码。
如果你认为上面的工作太多,或者,你可以安装名为“ Android Source 的 Eclipse 插件来获得“android.jar
的源代码。阅读这篇文章“【Android 的附加 Eclipse 插件”。
在安装了" Android source "插件后,假设现有项目以及新创建的针对 Android 的项目将自动附加源代码 jar。然而,我现有的 Android 项目仍然没有附加到正确的源,我必须手动附加它。
找到" Android Source "插件文件夹,它应该在以下目录中:
ECLIPSE_PATH\plugins\com.android.ide.eclipse.source_MAY_BE_VARY
文件夹:
- 14–安卓 4.0.1
- 10–安卓 2.3.4
- 9–安卓 2.3
- 8–安卓 2.2
- 7–安卓 2.1
- 6–安卓 2.0.1
- 4–安卓 1.6
- 3–安卓 1.5
每个文件夹包含一个“ sources.zip ”,它针对特定的 Android 版本。比如你开发的是 Android 2.3 ,那么从文件夹“ 10 ”中获取“ sources.zip ,手动将其附加到 Eclipse IDE 中。
再次踏入 Android 类,源代码显示。
参考
- 【Android 的附加 Eclipse 插件
- Android 开源项目
Tags : android eclipse source code
相关文章
-
【Eclipse IDE 的 Java 反编译器插件
无法更改 HTTP accept 标头-使用不同的区域设置解析策略
问题
在 Spring MVC 应用程序中,当用"org . Spring framework . web . servlet . i18n . localechangeinterceptor"更改区域设置时,遇到以下错误
java.lang.UnsupportedOperationException:
Cannot change HTTP accept header - use a different locale resolution strategy
...AcceptHeaderLocaleResolver.setLocale(AcceptHeaderLocaleResolver.java:45)
解决办法
在 Spring MVC 应用程序中,如果不配置 Spring 的 LocaleResolver,它将使用默认的AcceptHeaderLocaleResolver,这不允许更改区域设置。要解决这个问题,请尝试在 Spring bean 配置文件中声明一个session locale resolverbean,它应该适合大多数情况。
<beans ...
<bean id="localeResolver"
class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
<property name="defaultLocale" value="en" />
</bean>
<bean id="localeChangeInterceptor"
class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
<property name="paramName" value="language" />
</bean>
<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping" >
<property name="interceptors">
<list>
<ref bean="localeChangeInterceptor" />
</list>
</property>
</bean>
</beans>
参考
在 org . Apache . struts . action . message 项下找不到消息资源
问题
Struts 框架中一个常见的资源包错误,它通常是由系统找不到相应的消息资源引起的。
javax.servlet.jsp.JspException: Cannot find message resources under key org.apache.struts.action.MESSAGE
org.apache.struts.taglib.TagUtils.retrieveMessageResources(TagUtils.java:1112)
org.apache.struts.taglib.TagUtils.present(TagUtils.java:1055)
org.apache.struts.taglib.html.ErrorsTag.doStartTag(ErrorsTag.java:200)
org.apache.jsp.pages.login_jsp._jspx_meth_html_005ferrors_005f0(login_jsp.java:160)
org.apache.jsp.pages.login_jsp._jspx_meth_html_005fform_005f0(login_jsp.java:111)
org.apache.jsp.pages.login_jsp._jspService(login_jsp.java:77)
org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
javax.servlet.http.HttpServlet.service(HttpServlet.java:831)
org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:377)
org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:313)
org.apache.jasper.servlet.JspServlet.service(JspServlet.java:260)
javax.servlet.http.HttpServlet.service(HttpServlet.java:831)
org.apache.struts.chain.commands.servlet.PerformForward.handleAsForward(PerformForward.java:113)
org.apache.struts.chain.commands.servlet.PerformForward.perform(PerformForward.java:96)
org.apache.struts.chain.commands.AbstractPerformForward.execute(AbstractPerformForward.java:54)
org.apache.struts.chain.commands.ActionCommandBase.execute(ActionCommandBase.java:51)
org.apache.commons.chain.impl.ChainBase.execute(ChainBase.java:191)
org.apache.commons.chain.generic.LookupCommand.execute(LookupCommand.java:305)
org.apache.commons.chain.impl.ChainBase.execute(ChainBase.java:191)
org.apache.struts.chain.ComposableRequestProcessor.process(ComposableRequestProcessor.java:283)
org.apache.struts.action.ActionServlet.process(ActionServlet.java:1913)
org.apache.struts.action.ActionServlet.doGet(ActionServlet.java:449)
javax.servlet.http.HttpServlet.service(HttpServlet.java:718)
javax.servlet.http.HttpServlet.service(HttpServlet.java:831)
解决办法
只需包含相应的消息资源。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts-config PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 1.3//EN"
"http://jakarta.apache.org/struts/dtds/struts-config_1_3.dtd">
<struts-config>
<message-resources
parameter="com.mkyong.common.properties.Common" />
</struts-config>
struts (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','//web.archive.org/web/20190209024201/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0')
找不到基本名称 xxx,区域设置 en_US 的包
问题
在 JSF web 应用中,在应用层加载一个消息包,如下:
faces-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<faces-config
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-facesconfig_2_0.xsd"
version="2.0">
<application>
<message-bundle>
com.mkyong.payment_error
</message-bundle>
</application>
</faces-config>
页面渲染时,遇到“找不到 base name com . mkyong . payment _ error,locale en_US 的 bundle”?
解决办法
显然,包或属性文件(com . mkyong . payment _ error . properties)丢失了,请确保名称匹配并正确放置在资源文件夹中。
For Eclipse User
This problem is usually happened in the Eclipse IDE environment, where it doesn’t copy the “.properties” file extension by default. So, just make sure the properties file is existed in the run time “classes” folder and can be located by your web application.jsf2 (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','//web.archive.org/web/20190224212715/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0')
cascade–JPA & Hibernate 注释常见错误
很多时候,开发人员混合使用 JPA 和 Hibernate 注释,这将导致一个非常常见的错误——JPA 级联类型注释在 Hibernate 中不起作用?
在代码审查阶段,我发现许多 Java 开发人员没有意识到这个错误,导致程序无法对相关实体执行级联操作。我将以这个一对多 hibernate 示例为例进行演示。
错误
在一对多示例中,许多开发人员声明 JPA 级联选项如下:
...
@Entity
@Table(name = "stock", catalog = "mkyong", uniqueConstraints = {
@UniqueConstraint(columnNames = "STOCK_NAME"),
@UniqueConstraint(columnNames = "STOCK_CODE") })
public class Stock implements java.io.Serializable {
...
private Set<StockDailyRecord> stockDailyRecords =
new HashSet<StockDailyRecord>(0);
@OneToMany(fetch = FetchType.LAZY,
cascade = {CascadeType.PERSIST,CascadeType.MERGE },
mappedBy = "stock")
public Set<StockDailyRecord> getStockDailyRecords() {
return this.stockDailyRecords;
}
public void setStockDailyRecords(Set<StockDailyRecord> stockDailyRecords) {
this.stockDailyRecords = stockDailyRecords;
}
...
用 Hibernate 会话保存它。
stockDailyRecords.setStock(stock);
stock.getStockDailyRecords().add(stockDailyRecords);
session.save(stock);
session.getTransaction().commit();
这段代码试图做的是,当您保存一个“股票”时,它也会保存相关的 stockDailyRecords。一切看起来都很好,但是这不起作用,级联选项将不会执行和保存 stockDailyRecords。你能发现这个错误吗?
说明
在代码中, @OneToMany 来自 JPA,它期望一个 JPA 级联-javax . persistence . cascadetype。然而,当你用 Hibernate 会话保存它时,org . Hibernate . engine . cascade会做如下检查…
if ( style.doCascade( action ) ) {
cascadeProperty(
persister.getPropertyValue( parent, i, entityMode ),
types[i],
style,
anything,
false
);
}
Hibernate save 进程会导致一个 ACTION_SAVE_UPDATE 动作,但是 JPA 会传递一个 ACTION_PERSIST 和 ACTION_MERGE ,它们不匹配,导致级联执行失败。
@见源代码
- org . hibernate . engine . cascade
- org . hibernate . engine . cascade style
- org . hibernate . engine . cascading action
解决办法
删除 JPA cascade-javax . persistence . cascade type,替换为 Hibernate cascade-org . Hibernate . annotations . cascade,替换为 CascadeType。保存 _ 更新。
...
@Entity
@Table(name = "stock", catalog = "mkyong", uniqueConstraints = {
@UniqueConstraint(columnNames = "STOCK_NAME"),
@UniqueConstraint(columnNames = "STOCK_CODE") })
public class Stock implements java.io.Serializable {
...
private Set<StockDailyRecord> stockDailyRecords =
new HashSet<StockDailyRecord>(0);
@OneToMany(fetch = FetchType.LAZY, mappedBy = "stock")
@Cascade({CascadeType.SAVE_UPDATE})
public Set<StockDailyRecord> getStockDailyRecords() {
return this.stockDailyRecords;
}
public void setStockDailyRecords(Set<StockDailyRecord> stockDailyRecords) {
this.stockDailyRecords = stockDailyRecords;
}
...
现在,它如你所料地工作了。
结论
JPA 和 Hibernate cascade 注释之间似乎存在不兼容的问题,如果 Hibernate 是 JPA 的实现,是什么导致了两者之间的误解?
处理@Configuration 类需要 CGLIB
问题
使用 Spring3 @Configuration
创建如下所示的应用程序配置文件:
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
//...
}
但是,当运行它时,它会显示以下错误消息:
org.springframework.context.support.AbstractApplicationContext prepareRefresh
//...
Exception in thread "main" java.lang.IllegalStateException:
CGLIB is required to process @Configuration classes.
Either add CGLIB to the classpath or remove the following
@Configuration bean definitions: [appConfig]
//...
at com.mkyong.core.App.main(App.java:12)
解决办法
要在 Spring 3 中使用@Configuration
,需要手动包含 CGLIB 库,只需在 Maven pom.xml
文件中声明即可。
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
notfounindexception:com . opensymphony . xwork 2 . util . finder . test
问题
一个启用了 Struts 2 注释的项目,在服务器启动期间点击了以下错误消息。
Caused by: java.lang.NoClassDefFoundError: com/opensymphony/xwork2/util/finder/Test
at java.lang.Class.getDeclaredConstructors0(Native Method)
at java.lang.Class.privateGetDeclaredConstructors(Unknown Source)
at java.lang.Class.getDeclaredConstructors(Unknown Source)
... 24 more
Caused by: java.lang.ClassNotFoundException: com.opensymphony.xwork2.util.finder.Test
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1516)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1361)
... 28 more
解决办法
缺少" xwork.jar "库,这是 Struts 2 开发中所需要的。从 Maven 中央存储库下载它。
<dependency>
<groupId>com.opensymphony</groupId>
<artifactId>xwork</artifactId>
<version>2.1.3</version>
</dependency>
struts2 (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','//web.archive.org/web/20190225092427/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0')
ClassNotFoundException:com . sun . syndication . feed . wire feed
问题
用 Spring MVC 开发 RSS,扩展了“AbstractRssFeedView
”,在应用程序启动时出现以下错误信息。
Caused by: java.lang.NoClassDefFoundError: com/sun/syndication/feed/WireFeed
at java.lang.Class.getDeclaredConstructors0(Native Method)
at java.lang.Class.privateGetDeclaredConstructors(Class.java:2389)
at java.lang.Class.getDeclaredConstructors(Class.java:1836)
//...
Caused by: java.lang.ClassNotFoundException: com.sun.syndication.feed.WireFeed
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1516)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1361)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
... 41 more
解决办法
Spring MVC 使用"罗马"来生成 RSS 提要。对于 Maven,在pom.xml
文件中包含下面的依赖关系。
<dependency>
<groupId>net.java.dev.rome</groupId>
<artifactId>rome</artifactId>
<version>1.0.0</version>
</dependency>
spring mvc (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','//web.archive.org/web/20190225093251/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0')
ClassNotFoundException:com . thoughtworks . xstream . io . hierarchical streamreader
问题
Spring MVC 中的 XML 开发,通过 oxm,命中“HierarchicalStreamReader
”类未发现异常?
Caused by: java.lang.ClassNotFoundException: com.thoughtworks.xstream.io.HierarchicalStreamReader
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1516)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1361)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
... 49 more
解决办法
类“HierarchicalStreamReader
”属于“ xstream.jar ”。如果您使用的是 Maven,那么在您的pom.xml
文件中声明以下依赖关系。
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.3.1</version>
</dependency>
Note
For Ant user, just download the “xstream.jar” from http://xstream.codehaus.org/ directly.spring mvc (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','//web.archive.org/web/20190306164910/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0')
ClassNotFoundException:DefaultSavedRequest
问题
与 Spring Security 合作,哪个 jar 包含DefaultSavedRequest
?
SEVERE: Exception loading sessions from persistent storage
java.lang.ClassNotFoundException:
org.springframework.security.web.savedrequest.DefaultSavedRequest
at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
解决办法
DefaultSavedRequest
在 spring-security-web.jar 里面。访问这个 Spring Security hello world 示例获得依赖库列表。
<!-- Spring Security & dependencies -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>3.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>3.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>3.0.5.RELEASE</version>
</dependency>
spring security (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','//web.archive.org/web/20190306155926/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0')
ClassNotFoundException:org . Apache . XML . serialize . XML serializer
问题
使用 Spring OXM + Castor 绑定,添加了 Castor 库,但是仍然会出现下面的错误消息?
Exception in thread "main" java.lang.RuntimeException:
Could not instantiate serializer org.apache.xml.serialize.XMLSerializer:
java.lang.ClassNotFoundException: org.apache.xml.serialize.XMLSerializer
at org.exolab.castor.xml.XercesSerializer.<init>(XercesSerializer.java:50)
//...
Maven 中的 Castor 依赖。
<dependency>
<groupId>org.codehaus.castor</groupId>
<artifactId>castor</artifactId>
<version>1.2</version>
</dependency>
解决办法
如果没有弄错的话, Castor 需要 Xerces 来工作,所以,您还需要添加 Xerces 依赖项。
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
<version>3.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.codehaus.castor</groupId>
<artifactId>castor</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>xerces</groupId>
<artifactId>xercesImpl</artifactId>
<version>2.8.1</version>
</dependency>
</dependencies>
ClassNotFoundException:org . exolab . castor . XML . XML exception
问题
在 Spring OXM(对象 XML 映射)中,当将一个对象转换为 XML 文件时,会遇到以下错误消息:
Caused by: java.lang.ClassNotFoundException: org.exolab.castor.xml.XMLException
at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
... 29 more
Spring oxm 中是否包含 castor 数据绑定框架?
解决办法
castor 是 spring-oxm.jar 中的一个可选依赖项,要在 Spring OXM 中使用 castor 来编组和解组 XML,将这个 castor 依赖项添加到您的 Maven pom.xml
文件中。
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
<version>3.0.5.RELEASE</version>
</dependency>
<!-- Uses Castor for XML -->
<dependency>
<groupId>org.codehaus.castor</groupId>
<artifactId>castor</artifactId>
<version>1.2</version>
</dependency>
<!-- Castor need this -->
<dependency>
<groupId>xerces</groupId>
<artifactId>xercesImpl</artifactId>
<version>2.8.1</version>
</dependency>
</dependencies>
oxm spring3 (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','//web.archive.org/web/20190121170502/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0')
Struts 2 组合框示例
Download It – Struts-ComboBox-Example.zip
在 Struts 2 中, < s:combobox > 标签基本上是一个与单行文本框组合在一起的下拉列表,允许用户直接在文本框中键入值或从下拉列表中选择值,所选值将自动填充到文本框中。
In case you are confuse between drop down list and combo box list, read the combo box definition from Wiki.
<s:combobox label="What's your favor fruit"
headerKey="-1" headerValue="--- Select ---"
list="fruits"
name="yourFruits" />
产生以下 HTML 代码…
<td class="tdLabel">
<label for="resultAction_yourFruits" class="label">
What's your favor fruit:
</label>
</td>
<td>
<script type="text/javascript">
function autoPopulate_resultAction_yourFruits(targetElement) {
if (targetElement.options[targetElement.selectedIndex].value == '-1') {
return;
}
targetElement.form.elements['yourFruits'].value=
targetElement.options[targetElement.selectedIndex].value;
}
</script>
<input type="text" name="yourFruits" value="" id="resultAction_yourFruits"/>
<br />
<select onChange="autoPopulate_resultAction_yourFruits(this);">
<option value="-1">--- Select ---</option>
<option value="Apple">Apple</option>
<option value="Banana">Banana</option>
<option value="Orange">Orange</option>
<option value="Watermelon">Watermelon</option>
</select>
</td>
< s:combobox > 标签会生成一个输入文本框,下拉列表带有一个“ onChange() ”行为来调用生成的 JavaScript 函数,自动将下拉列表中选择的值填充到生成的文本框中。
To create a drop down list , you should use <s:select> tag instead.
struts 2<s:combobox>示例
一个完整的 Struts 2 示例,通过 < s:combobox > 展示组合框的使用
1.行动
Action 类来生成并保存选定的组合框选项。
ComboBoxAction.java
package com.mkyong.common.action;
import java.util.ArrayList;
import java.util.List;
import com.opensymphony.xwork2.ActionSupport;
public class ComboBoxAction extends ActionSupport{
private List<String> fruits;
private String yourFruits;
private String yourMonth;
public String getYourMonth() {
return yourMonth;
}
public void setYourMonth(String yourMonth) {
this.yourMonth = yourMonth;
}
public List<String> getFruits() {
return fruits;
}
public void setFruits(List<String> fruits) {
this.fruits = fruits;
}
public String getYourFruits() {
return yourFruits;
}
public void setYourFruits(String yourFruits) {
this.yourFruits = yourFruits;
}
public ComboBoxAction(){
fruits = new ArrayList<String>();
fruits.add("Apple");
fruits.add("Banana");
fruits.add("Orange");
fruits.add("Watermelon");
}
public String execute() {
return SUCCESS;
}
public String display() {
return NONE;
}
}
2.结果页面
通过“ < s:combobox > ”标签呈现组合框,通过 Java list 和 OGNL list 填充选择选项
combobox.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
</head>
<body>
<h1>Struts 2 <s:combobox> example</h1>
<s:form action="resultAction" namespace="/">
<h2>
<s:combobox label="What's your favor fruit"
headerKey="-1" headerValue="--- Select ---"
list="fruits"
name="yourFruits" />
</h2>
<h2>
<s:combobox label="Select a month"
headerKey="-1" headerValue="--- Select ---"
list="#{'1':'Jan', '2':'Feb', '3':'Mar', '4':'Apr'}"
name="yourMonth" />
</h2>
<s:submit value="submit" name="submit" />
</s:form>
</body>
</html>
result.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<body>
<h1>Struts 2 <s:combobox> example</h1>
<h2>
Favor fruit : <s:property value="yourFruits"/>
</h2>
<h2>
Selected month : <s:property value="yourMonth"/>
</h2>
</body>
</html>
3.struts.xml
链接在一起~
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.devMode" value="true" />
<package name="default" namespace="/" extends="struts-default">
<action name="comboBoxAction"
class="com.mkyong.common.action.ComboBoxAction" method="display">
<result name="none">pages/combobox.jsp</result>
</action>
<action name="resultAction" class="com.mkyong.common.action.ComboBoxAction">
<result name="success">pages/result.jsp</result>
</action>
</package>
</struts>
5.演示
http://localhost:8080/struts 2 example/comboboxation . action
http://localhost:8080/struts 2 example/result action . action
参考
JSF 2.0 中的复合组件
从 JSF 2.0 开始,创建一个可重用的组件变得非常容易,被称为复合组件。在本教程中,我们将向您展示如何创建一个简单的复合组件(存储为“ register.xhtml ”),这是一个用户注册表单,包括姓名和电子邮件文本字段(h:inputText
)和一个提交按钮(h:commandButton
)。此外,我们还向您展示如何使用它。
创建一个复合组件
下面是创建复合组件的步骤:
freestar.config.enabled_slots.push({ placementName: "mkyong_incontent_1", slotId: "mkyong_incontent_1" });
1.复合名称空间
创建一个。xhtml 文件,并声明了复合名称空间。
<html
//...
xmlns:composite="http://java.sun.com/jsf/composite">
</html>
2.接口和实施
使用复合标签composite:interface
、composite:attribute
和composite:implementation
,定义复合组件的内容。举个例子,
<html
//...
xmlns:composite="http://java.sun.com/jsf/composite">
<composite:interface>
<composite:attribute name="anything" />
</composite:interface>
<composite:implementation>
#{cc.attrs.anything}
</composite:implementation>
</html>
composite:interface
标签用于声明可配置的值,这些值向使用它的开发人员公开。而composite:implementation
标签声明了所有的 XHTML 标记,也就是复合组件的内容,在composite:implementation
标签里面,你可以用表达式#{cc.attrs.attributeName}
访问composite:interface
属性。
3.资源文件夹
把复合组件(“。xhtml "文件)放到 JSF 的资源文件夹中,见图 1:
图 1:这个例子的目录结构。
在本例中,您将“ register.xhtml ”复合组件放入名为“mkyong”的文件夹中。
4.完整示例
做完了,让我们来看一个完整的“register.xhtml”的例子。
文件:register.xhtml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:composite="http://java.sun.com/jsf/composite"
>
<composite:interface>
<composite:attribute name="nameLable" />
<composite:attribute name="nameValue" />
<composite:attribute name="emailLable" />
<composite:attribute name="emailValue" />
<composite:attribute name="registerButtonText" />
<composite:attribute name="registerButtonAction"
method-signature="java.lang.String action()" />
</composite:interface>
<composite:implementation>
<h:form>
<h:message for="textPanel" style="color:red;" />
<h:panelGrid columns="2" id="textPanel">
#{cc.attrs.nameLable} :
<h:inputText id="name" value="#{cc.attrs.nameValue}" />
#{cc.attrs.emailLable} :
<h:inputText id="email" value="#{cc.attrs.emailValue}" />
</h:panelGrid>
<h:commandButton action="#{cc.attrs.registerButtonAction}"
value="#{cc.attrs.registerButtonText}"
/>
</h:form>
</composite:implementation>
</html>
使用复合组件
您刚刚创建了一个复合组件“register.xhtml ”,现在我们将向您展示如何使用它。
1.复合组件访问路径
参考上面的图 1;“register.xhtml”文件在“mkyong”文件夹下。以下是您访问它的方式:
<html
///...
xmlns:mkyong="http://java.sun.com/jsf/composite/mkyong"
>
<mkyong:register />
</html>
http://java.sun.com/jsf/composite/folder-name-in-resources-folder
The folder name of the composite components is defined the component access path, for example, if you put your “register.xhtml” file under folder named “abc”, then you should access it like this :
<html
///...
xmlns:mkyong="http://java.sun.com/jsf/composite/abc"
>
<mkyong:register />
</html>
2.完整示例
让我们看一个完整的例子来展示“register.xhtml”复合组件的用法。
文件:default.xhtml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html
xmlns:h="http://java.sun.com/jsf/html"
xmlns:mkyong="http://java.sun.com/jsf/composite/mkyong"
>
<h:body>
<h1>Composite Components in JSF 2.0</h1>
<mkyong:register
nameLable="Name"
nameValue="#{user.name}"
emailLable="E-mail"
emailValue="#{user.email}"
registerButtonText="Register"
registerButtonAction="#{user.registerAction}"
/>
</h:body>
</html>
您可以通过暴露的属性将硬编码值或支持方法或属性传递到复合组件中,当提交表单时,JSF 将自动完成所有支持 bean 绑定。
对那些感兴趣的人来说,这是“用户”管理或支持的 bean。
package com.mkyong;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
@ManagedBean(name="user")
@SessionScoped
public class UserBean{
public String name;
public String email;
//getter and setter methods for name and email
public String registerAction(){
return "result";
}
}
演示
这是结果。
URL:http://localhost:8080/Java server faces/default . XHTML
下载源代码
Download It – JSF-2-Composite-Components-Example.zip (11KB)
参考
Tags : component jsf2freestar.config.enabled_slots.push({ placementName: "mkyong_leaderboard_btf", slotId: "mkyong_leaderboard_btf" });
JSF 2.0 中的条件导航规则
JSF 2 附带了一个非常灵活的条件导航规则来解决复杂的页面导航流程,参见下面的条件导航规则示例:
1.JSF·佩奇
一个简单的 JSF 页面,有一个按钮从这个页面移动到支付页面。
start.xhtml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html
xmlns:h="http://java.sun.com/jsf/html">
<h:body>
<h2>This is start.xhtml</h2>
<h:form>
<h:commandButton action="payment" value="Payment" />
</h:form>
</h:body>
</html>
2.受管 Bean
一个受管 bean,提供示例数据以在导航规则中执行条件检查。
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import java.io.Serializable;
@ManagedBean
@SessionScoped
public class PaymentController implements Serializable {
private static final long serialVersionUID = 1L;
public boolean registerCompleted = true;
public int orderQty = 99;
//getter and setter methods
}
3.条件导航规则
通常,您在“faces-config.xml”中声明简单的导航规则,如下所示:
<navigation-rule>
<from-view-id>start.xhtml</from-view-id>
<navigation-case>
<from-outcome>payment</from-outcome>
<to-view-id>payment.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
在 JSF 新协议中,您可以在进入支付页面之前添加一些条件检查,请参见以下内容:
faces-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<faces-config
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-facesconfig_2_0.xsd"
version="2.0">
<navigation-rule>
<from-view-id>start.xhtml</from-view-id>
<navigation-case>
<from-outcome>payment</from-outcome>
<if>#{paymentController.orderQty < 100}</if>
<to-view-id>ordermore.xhtml</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>payment</from-outcome>
<if>#{paymentController.registerCompleted}</if>
<to-view-id>payment.xhtml</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>payment</from-outcome>
<to-view-id>register.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
</faces-config>
这相当于下面的 Java 代码:
if (from-view-id == "start.xhtml"){
if(from-outcome == "payment"){
if(paymentController.orderQty < 100){
return "ordermore";
}else if(paymentController.registerCompleted){
return "payment";
}else{
return "register";
}
}
}
代码应该足够简单明了。
Note
In the conditional navigation rule, the sequence of the navigation rule does affect the navigation flow, always put the highest checking priority in the top.
4.测试
用于测试的不同数据集:
例 1
public class PaymentController implements Serializable {
public boolean registerCompleted = true;
public int orderQty = 99;
...
当这个按钮被点击时,它达到了"payment controller . order qty<100"标准,并移动到" ordermore.xhtml "页面。
例 2
public class PaymentController implements Serializable {
public boolean registerCompleted = true;
public int orderQty = 200;
...
当按钮被点击时,它点击"payment controller . register completed"标准并移动到" payment.xhtml "页面。
例 3
public class PaymentController implements Serializable {
public boolean registerCompleted = false;
public int orderQty = 200;
...
当按钮被单击时,它没有通过所有的检查标准,并移动到“register.xhtml”页面。
建议
在 JSF 2.0 中,条件导航规则中没有“ else 标签,希望 JSF 团队能在未来的版本中包含“else”标签。举个例子,
<navigation-rule>
<from-view-id>start.xhtml</from-view-id>
<navigation-case>
<from-outcome>payment</from-outcome>
<if>#{paymentController.orderQty < 100}</if>
<to-view-id>ordermore.xhtml</to-view-id>
<else if>#{paymentController.registerCompleted}</else if>
<to-view-id>payment.xhtml</to-view-id>
<else>
<to-view-id>register.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
此外,它还应该包括多重条件检查,就像这样
<navigation-rule>
<from-view-id>start.xhtml</from-view-id>
<navigation-case>
<from-outcome>payment</from-outcome>
<if>#{paymentController.orderQty < 100} && #{paymentController.xxx}</if>
<to-view-id>ordermore.xhtml</to-view-id>
<else if>#{paymentController.registerCompleted}</else if>
<to-view-id>payment.xhtml</to-view-id>
<else>
<to-view-id>register.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
Thought…
JSF 2 conditional navigation rule, … quite similar with the Spring Web Flow, right? 🙂
下载源代码
Download it – JSF-2-Conditional-Navigation-Example.zip (11KB)Tags : jsf2 navigation rule
相关文章
在 Struts 中配置欢迎页面
每个网站都需要一个欢迎页面或默认页面作为入口点。这里有 3 种在 Struts 中配置欢迎页面的方法。
Download this Struts welcome file example – Struts-Welcome-File-Example.zip
1.index.jsp
最简单的方法是创建一个“index.jsp页面,并把它与 WEB-INF 文件夹,项目根文件夹放在同一层。
访问项目根目录
http://localhost:8080/StrutsExample/
它将默认为 index.jsp 内部。
http://localhost:8080/StrutsExample/index.jsp
2.web.xml 欢迎文件
在 web.xml 文件中声明一个欢迎文件。
<welcome-file-list>
<welcome-file>
/pages/Welcome.jsp
</welcome-file>
</welcome-file-list>
访问项目根目录
http://localhost:8080/StrutsExample/
它将在内部重定向到 welcome.jsp 文件。
http://localhost:8080/StrutsExample/pages/Welcome.jsp
web.xml
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Maven Struts Examples</display-name>
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>
org.apache.struts.action.ActionServlet
</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>
/WEB-INF/struts-config.xml
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>
/pages/Welcome.jsp
</welcome-file>
</welcome-file-list>
</web-app>
3.JSP 转发
如方法 1 所述,创建一个“index.jsp”文件,并定义一个 JSP forward 标记,将其重定向到另一个 Struts 动作。
index.jsp
声明一个 /Welcome web 路径,用一个 ForwardAction 类型将其转发到另一个 JSP 文件。
struts-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts-config PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 1.3//EN"
"http://jakarta.apache.org/struts/dtds/struts-config_1_3.dtd">
<struts-config>
<action-mappings>
<action
path="/Welcome"
type="org.apache.struts.actions.ForwardAction"
parameter="/pages/Welcome.jsp"/>
</action-mappings>
</struts-config>
在 Google App Engine 中配置日志记录
Google App Engine for Java 正在使用 java.util.logging.Logger 来执行日志记录。在本教程中,我们将向您展示如何在 GAE 环境中进行日志记录。
1.日志示例
在MovieController
中定义一个记录器,并记录不同记录级别(信息、警告和错误)的消息的示例
import java.util.logging.Logger;
//...
@Controller
@RequestMapping("/movie")
public class MovieController {
private static final Logger log = Logger.getLogger(MovieController.class.getName());
@RequestMapping(value="/{name}", method = RequestMethod.GET)
public String getMovie(@PathVariable String name, ModelMap model) {
log.info("Information log message.");
log.warning("Warning log message.");
log.severe("Error log message.");
return "page";
}
}
2.日志示例
创建一个文件 logging.properties,设置日志级别,并将其放入" $project/war/WEB-INF "
文件:$ project/war/we b-INF/logging . properties
# Set the default logging level for all loggers to WARNING
.level = WARNING
3.appengine-web.xml
更新 appengine-web.xml ,定义系统属性标签,将日志指向上面的“we b-INF/logging . properties”。
文件:$ project/war/we b-INF/app engine-WEB . XML
<?xml version="1.0" encoding="utf-8"?>
<appengine-web-app >
<application>mkyong-springmvc</application>
<version>1</version>
<!-- Configure java.util.logging -->
<system-properties>
<property name="java.util.logging.config.file" value="WEB-INF/logging.properties"/>
</system-properties>
</appengine-web-app>
4.完成的
在 GAE 本地开发环境中,所有记录的消息都将显示在控制台上。在 GAE 生产环境中,您可以在应用程序的管理员页面中访问记录的消息。
参考
在 JSF 2.0 中配置受管 Beans
在 JSF 2.0 中,可以从 JSF 页面访问的 Java bean 被称为托管 Bean 。受管 bean 可以是一个普通的 Java bean,它包含 getter 和 setter 方法、业务逻辑,甚至是一个后备 bean(一个 bean 包含所有的 HTML 表单值)。
有两种方法可以配置受管 bean:
1.使用注释配置受管 Bean
在 JSF 2.0 中,可以用新的 @ManagedBean 注释来注释受管 Bean。
package com.mkyong.common;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import java.io.Serializable;
@ManagedBean
@SessionScoped
public class HelloBean implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
2.用 XML 配置受管 Bean
使用 XML 配置,您可以使用旧的 JSF 1.x 机制在普通的 faces-config.xml 文件中定义受管 bean。
<?xml version="1.0" encoding="UTF-8"?>
<faces-config
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-facesconfig_2_0.xsd"
version="2.0">
<managed-bean>
<managed-bean-name>helloBean</managed-bean-name>
<managed-bean-class>com.mkyong.common.HelloBean</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
</faces-config>
Best Practice
It’s recommended to put the managed beans in a separate XML file because the faces-config.xml is used to set the application level configurations.
因此,您应该创建一个新的 XML 文件并将受管 beans 细节放入其中,并在javax . faces . config _ FILESinitialize 参数中声明该 XML 文件,该参数位于 WEB-INF/web.xml 文件中。
web.xml
...
<context-param>
<param-name>javax.faces.CONFIG_FILES</param-name>
<param-value>WEB-INF/manage-beans.xml</param-value>
</context-param>
...
下载源代码
Download it – JSF-2-Managed-Beans-Example.zip (10KB)Tags : jsf2 managed bean
相关文章
在 Spring MVC 中配置多视图解析器优先级
问题
在 Spring MVC 应用程序中,你经常会应用一些视图解析器策略来解析视图名。例如,将三个视图解析器组合在一起:InternalResourceViewResolver、ResourceBundleViewResolver和 XmlViewResolver 。
<beans ...>
<bean class="org.springframework.web.servlet.view.XmlViewResolver">
<property name="location">
<value>/WEB-INF/spring-views.xml</value>
</property>
</bean>
<bean class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
<property name="basename" value="spring-views" />
</bean>
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
<property name="prefix">
<value>/WEB-INF/pages/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
</beans>
但是,如果返回一个视图名,将使用哪个视图解析器策略呢?
解决办法
如果应用了多视图解析器策略,您必须通过“ order 属性声明优先级,其中较低的顺序值具有较高的优先级,例如:
<beans ...>
<bean class="org.springframework.web.servlet.view.XmlViewResolver">
<property name="location">
<value>/WEB-INF/spring-views.xml</value>
</property>
<property name="order" value="0" />
</bean>
<bean class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
<property name="basename" value="spring-views" />
<property name="order" value="1" />
</bean>
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
<property name="prefix">
<value>/WEB-INF/pages/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
<property name="order" value="2" />
</bean>
</beans>
现在,如果返回视图名称,视图解析策略按以下顺序工作:
XmlViewResolver --> ResourceBundleViewResolver --> InternalResourceViewResolver
Note
The InternalResourceViewResolver must always assign with the lowest priority (largest order number), because it will resolve the view no matter what view name is returned. It caused other view resolvers have no chance to resolve the view if they have lower priority.
下载源代码
Download it – SpringMVC-ViewResolver-Priority-Example.zip (7KB)
参考
- Spring MVC InternalResourceViewResolver 示例
- Spring MVC XmlViewResolver 示例
- Spring MVC ResourceBundleViewResolver 示例
Tags : spring mvc
在 Spring MVC 中配置处理程序映射优先级
通常,在 Spring MVC 开发中,您可能会混合使用多个处理程序映射策略。
例如,使用ControllerClassNameHandlerMapping映射所有的约定处理程序映射,使用 SimpleUrlHandlerMapping 显式映射其他特殊的处理程序映射。
<beans ...>
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<value>
/index.htm=welcomeController
/welcome.htm=welcomeController
/main.htm=welcomeController
/home.htm=welcomeController
</value>
</property>
<property name="order" value="0" />
</bean>
<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping" >
<property name="caseSensitive" value="true" />
<property name="order" value="1" />
</bean>
<bean id="welcomeController"
class="com.mkyong.common.controller.WelcomeController" />
<bean class="com.mkyong.common.controller.HelloGuestController" />
</beans>
在上述情况下,指定处理程序映射优先级很重要,这样就不会导致冲突。您可以通过“ order 属性设置优先级,其中顺序值越低优先级越高。
下载源代码
Download it – SpringMVC-HandlerMapping-Priority-Example.zip (8 KB) ## 参考
spring mvc (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','//web.archive.org/web/20190214230856/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0')
配置 Struts 标记库
在 Struts 框架中,您总是需要配置 Struts 标记库,以便在视图页面(JSP)中访问它。有两种方法可以配置它。
1.支柱标记库手动配置
手动配置是古老而经典的方式,在 Struts 版本< = 1.1 和 Servlet < 2.3 容器中使用。下载所有的 Struts 依赖项,确保下面的“ tld 文件被复制到 WEB-INF 文件夹,你可以在下载的 Struts 库中找到这些文件。
- struts-bean.tld
- struts-html.tld
- struts-logic.tld
- struts-tiles.tld
在 web.xml
web.xml 中声明标记库 uri
...
<taglib>
<taglib-uri>
http://struts.apache.org/tags-bean
</taglib-uri>
<taglib-location>
/WEB-INF/struts-bean.tld
</taglib-location>
</taglib>
<taglib>
<taglib-uri>
http://struts.apache.org/tags-html
</taglib-uri>
<taglib-location>
/WEB-INF/struts-html.tld
</taglib-location>
</taglib>
<taglib>
<taglib-uri>
http://struts.apache.org/tags-logic
</taglib-uri>
<taglib-location>
/WEB-INF/struts-logic.tld
</taglib-location>
</taglib>
<taglib>
<taglib-uri>
http://struts.apache.org/tags-tiles
</taglib-uri>
<taglib-location>
/WEB-INF/struts-tiles.tld
</taglib-location>
</taglib>
<taglib>
<taglib-uri>
http://struts.apache.org/tags-nested
</taglib-uri>
<taglib-location>
/WEB-INF/struts-nested.tld
</taglib-location>
</taglib>
...
现在您可以在 JSP 页面中访问它。JSP 的 @taglib uri 必须与 web . XML
<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
<%@ taglib uri="http://struts.apache.org/tags-tiles" prefix="tiles" %>
实际上,您可以定义自己的 taglib uri 名称,例如
web.xml
...
<taglib>
<taglib-uri>
customer-anything/tags-bean
</taglib-uri>
<taglib-location>
/WEB-INF/struts-bean.tld
</taglib-location>
</taglib>
...
然后通过您的自定义 taglib uri 名称访问它。
<%@ taglib uri="customer-anything/tags-bean" prefix="bean" %>
2.支柱标签库自动配置
这是一种简单的方法,只在 Struts、1.3 和 Servlet 2.3/2.4 容器中使用。您不再需要在 web.xml 中定义“ tlds ”细节,只需要在您的项目类路径中包含 struts-taglib.jar 或者将其复制到 WEB-INF/lib 文件夹中。
所有的“ tld ”细节都在“struts-taglib . jar \ META-INF \ TLD”文件夹中定义。部署过程中, struts-bean.tld 、 struts-html.tld 、 struts-logic.tld 和 struts-tiles.tld 会自动部署。但是,您只能通过下面的“预先固定的 uri ”名称来访问它。在此方法中,不允许您更改“ taglib uri ”名称。
<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
<%@ taglib uri="http://struts.apache.org/tags-tiles" prefix="tiles" %>
常见问题解答
问:看起来“taglib uri”指向 Apache 网站,客户端无法访问互联网怎么办?
A:taglib uri 定义在“struts-taglib . jar \ META-INF \ TLD”文件夹中,它只是一个项目 uri 名称,并不指向 Apache 网站,即使在没有互联网访问的环境下也可以访问。
问:手动配置可以在最新的 Struts 1.2 或 1.3 中工作吗?
A :是的,Struts 是向后兼容的,老办法还是在 Struts 1.2 和 1.3 中支持。
问:哪种方法最好?
A :靠,自动配置只在 Servlet 2.3/2.4 容器中有效。如果让你选择,请转到自动方式,为什么要手动复制 tld 文件?
参考
通过 JDBC 驱动程序连接到 Oracle 数据库
下面的示例向您展示了如何通过 JDBC 驱动程序连接到 Oracle 数据库。
1.下载 Oracle JDBC 驱动程序
访问甲骨文网站获取甲骨文 JDBC 驱动ojdbc6.jar
或ojdbc7.jar
另外,您需要创建一个 Oracle 帐户(免费)来下载 JDBC 驱动程序。
2.Java JDBC 连接示例
通过 JDBC 驱动程序连接 Oracle 数据库的代码片段。
Class.forName("oracle.jdbc.driver.OracleDriver");
Connection connection = null;
connection = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:SID","username","password");
connection.close();
请参见下面的完整示例:
OracleJDBCExample.java
package com.mkyong;
import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.SQLException;
public class OracleJDBCExample {
public static void main(String[] argv) {
System.out.println("-------- Oracle JDBC Connection Testing ------");
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
} catch (ClassNotFoundException e) {
System.out.println("Where is your Oracle JDBC Driver?");
e.printStackTrace();
return;
}
System.out.println("Oracle JDBC Driver Registered!");
Connection connection = null;
try {
connection = DriverManager.getConnection(
"jdbc:oracle:thin:@localhost:1521:xe", "system", "password");
} catch (SQLException e) {
System.out.println("Connection Failed! Check output console");
e.printStackTrace();
return;
}
if (connection != null) {
System.out.println("You made it, take control your database now!");
} else {
System.out.println("Failed to make connection!");
}
}
}
3.运行它
假设OracleJDBCExample.java
与 Oracle JDBC 驱动程序ojdbc7.jar
一起存储在C:\jdbc-test
文件夹中
Terminal
C:\jdbc-test>javac OracleJDBCExample.java
C:\jdbc-test>java -cp c:\jdbc-test\ojdbc7.jar;c:\jdbc-test OracleJDBCExample
-------- Oracle JDBC Connection Testing ------------
Oracle JDBC Driver Registered!
You made it, take control your database now!
完成了。
参考
Spring 中的构造函数注入类型不明确
在 Spring 框架中,当你的类包含多个参数个数相同的构造函数时,总是会导致构造函数注入参数类型歧义的问题。
问题
让我们看看这个客户 bean 的例子。它包含两个构造函数方法,都接受 3 个不同数据类型的参数。
package com.mkyong.common;
public class Customer
{
private String name;
private String address;
private int age;
public Customer(String name, String address, int age) {
this.name = name;
this.address = address;
this.age = age;
}
public Customer(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
//getter and setter methods
public String toString(){
return " name : " +name + "\n address : "
+ address + "\n age : " + age;
}
}
在 Spring bean 配置文件中,为名称传递“mkyong”,为地址传递“188”,为年龄传递“28”。
<!--Spring-Customer.xml-->
<beans
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="CustomerBean" class="com.mkyong.common.Customer">
<constructor-arg>
<value>mkyong</value>
</constructor-arg>
<constructor-arg>
<value>188</value>
</constructor-arg>
<constructor-arg>
<value>28</value>
</constructor-arg>
</bean>
</beans>
运行它,你的预期结果是什么?
package com.mkyong.common;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App
{
public static void main( String[] args )
{
ApplicationContext context =
new ClassPathXmlApplicationContext(new String[] {"Spring-Customer.xml"});
Customer cust = (Customer)context.getBean("CustomerBean");
System.out.println(cust);
}
}
输出
name : mkyong
address : 28
age : 188
结果并不是我们所期望的,运行的是第二个构造函数,而不是第一个构造函数。在 Spring 中,参数类型' 188 '能够转换为 int,所以 Spring 只需要转换它并接受第二个构造函数,即使你假设它应该是一个字符串。
此外,如果 Spring 不能解析使用哪个构造函数,它将提示以下错误消息
constructor arguments specified but no matching constructor
found in bean 'CustomerBean' (hint: specify index and/or
type arguments for simple parameters to avoid type ambiguities)
解决办法
要解决这个问题,您应该始终通过 type 属性为构造函数指定确切的数据类型,如下所示:
<beans
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="CustomerBean" class="com.mkyong.common.Customer">
<constructor-arg type="java.lang.String">
<value>mkyong</value>
</constructor-arg>
<constructor-arg type="java.lang.String">
<value>188</value>
</constructor-arg>
<constructor-arg type="int">
<value>28</value>
</constructor-arg>
</bean>
</beans>
再次运行,现在你得到了你所期望的。
输出
name : mkyong
address : 188
age : 28
Note
It’s always a good practice to explicitly declared the data type for each constructor argument, to avoid constructor injection type ambiguities issue above.spring (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','//web.archive.org/web/20190309054054/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0')
在 Java 中将 InputStream 转换为 BufferedReader
BufferedReader
不能直接读取InputStream
;所以,我们需要使用像 InputStreamReader 这样的适配器将字节转换成字符格式。例如:
// BufferedReader -> InputStreamReader -> InputStream
BufferedReader br = new BufferedReader(
new InputStreamReader(inputStream, StandardCharsets.UTF_8));
1。从资源文件夹中读取文件。
这个例子从 resources 文件夹中读取一个文件作为InputStream
;而且我们可以用BufferedReader + InputStreamReader
逐行读取。
InputStreamToReaderExample.java
package com.mkyong.io.howto;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
public class InputStreamToReaderExample {
public static void main(String[] args) throws IOException {
// loads a file from a resources folder
InputStream is = InputStreamToReaderExample.class
.getClassLoader()
.getResourceAsStream("file/abc.txt");
// BufferedReader -> InputStreamReader -> InputStream
// try-with-resources, auto close
String line;
try (BufferedReader br = new BufferedReader(
new InputStreamReader(is, StandardCharsets.UTF_8))) {
// read line by line
while ((line = br.readLine()) != null) {
System.out.println(line);
}
}
}
}
2。向 Cloudflare 端点发送 Http post 请求。
这个示例使用 Apache HttpClient 向 Cloudflare 端点发送 POST 请求,以阻止一个 IP 地址。在InputStream
中返回的是一个 JSON 字符串,我们可以使用同一个BufferedReader + InputStreamReader
来读取字节流。
pom.xml
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
CloudFlareBanIP.java
package com.mkyong.security.action;
import com.mkyong.security.util.PropertyUtils;
import org.apache.http.Header;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicHeader;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.concurrent.TimeUnit;
public class CloudFlareBanIP {
private static final String CF_AUTH_EMAIL = PropertyUtils.getInstance().getValue("cf_auth_email");
private static final String CF_AUTH_TOKEN = PropertyUtils.getInstance().getValue("cf_auth_token");
private static final String JSON_TYPE = "application/json";
private static Header[] HTTP_HEADERS = {
new BasicHeader("X-Auth-Email", CF_AUTH_EMAIL),
new BasicHeader("X-Auth-Key", CF_AUTH_TOKEN),
new BasicHeader("content-type", JSON_TYPE)
};
private HttpClient httpClient = HttpClientBuilder.create()
.setConnectionTimeToLive(10, TimeUnit.SECONDS)
.build();
public HttpClient getHttpClient() {
return httpClient;
}
public static void main(String[] args) throws IOException {
CloudFlareBanIP obj = new CloudFlareBanIP();
String response = obj.banIp("52.249.189.81", "bad ip");
System.out.println(response);
}
public String banIp(String ip, String note) throws IOException {
StringBuilder json = new StringBuilder();
json.append("{");
json.append("\"mode\":\"block\",");
json.append("\"configuration\":" + "{\"target\":\"ip\",\"value\":\"" + ip + "\"}" + ",");
json.append("\"notes\":\"" + note + "\"");
json.append("}");
StringBuilder result = new StringBuilder();
HttpPost post = new HttpPost("https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules");
post.setHeaders(HTTP_HEADERS);
post.setEntity(new StringEntity(json.toString()));
// read response from the POST request
try (BufferedReader br = new BufferedReader(
new InputStreamReader(getHttpClient().execute(post).getEntity().getContent()))) {
String line;
while ((line = br.readLine()) != null) {
result.append(line);
}
}
return result.toString();
}
}
下载源代码
$ git 克隆https://github.com/mkyong/core-java
$ CD Java-io/操作方法
参考文献
在 Java 中将对象转换为 byte[]
原文:http://web.archive.org/web/20230101150211/https://mkyong.com/java/convert-object-to-byte-in-java/
这篇文章展示了如何在 Java 中将一个对象转换成byte[]
或字节数组,反之亦然。
1。将对象转换为字节[]
下面的例子展示了如何使用ByteArrayOutputStream
和ObjectOutputStream
将一个对象转换为byte[]
。
// Convert object to byte[]
public static byte[] convertObjectToBytes(Object obj) {
ByteArrayOutputStream boas = new ByteArrayOutputStream();
try (ObjectOutputStream ois = new ObjectOutputStream(boas)) {
ois.writeObject(obj);
return boas.toByteArray();
} catch (IOException ioe) {
ioe.printStackTrace();
}
throw new RuntimeException();
}
// Convert object to byte[]
public static byte[] convertObjectToBytes2(Object obj) throws IOException {
ByteArrayOutputStream boas = new ByteArrayOutputStream();
try (ObjectOutputStream ois = new ObjectOutputStream(boas)) {
ois.writeObject(obj);
return boas.toByteArray();
}
}
2。将字节[]转换为对象
下面的例子展示了如何使用ByteArrayInputStream
和ObjectInputStream
将byte[]
转换回一个对象。
// Convert byte[] to object
public static Object convertBytesToObject(byte[] bytes) {
InputStream is = new ByteArrayInputStream(bytes);
try (ObjectInputStream ois = new ObjectInputStream(is)) {
return ois.readObject();
} catch (IOException | ClassNotFoundException ioe) {
ioe.printStackTrace();
}
throw new RuntimeException();
}
// Convert byte[] to object
public static Object convertBytesToObject2(byte[] bytes)
throws IOException, ClassNotFoundException {
InputStream is = new ByteArrayInputStream(bytes);
try (ObjectInputStream ois = new ObjectInputStream(is)) {
return ois.readObject();
}
}
// Convert byte[] to object with filter
public static Object convertBytesToObjectWithFilter(byte[] bytes, ObjectInputFilter filter) {
InputStream is = new ByteArrayInputStream(bytes);
try (ObjectInputStream ois = new ObjectInputStream(is)) {
// add filter before readObject
ois.setObjectInputFilter(filter);
return ois.readObject();
} catch (IOException | ClassNotFoundException ioe) {
ioe.printStackTrace();
}
throw new RuntimeException();
}
延伸阅读
下载源代码
$ git 克隆https://github.com/mkyong/core-java.git
$ CD Java-io/com/mkyong/io/object
参考文献
- 文件到字节[]
- JavaDoc 对象输入流
- JavaDoc object output stream
- JavaDoc ByteArrayOutputStream
- JavaDoc ByteArrayInputStream
- OWASP–不可信数据的反序列化
- Brian Goetz——迈向更好的序列化
在 Wicket 中创建自定义验证器
在本教程中,您将创建一个自定义密码验证器,并将其附加到密码字段。
请参见创建自定义验证程序的总结步骤:
1.实现IValidator
。
import org.apache.wicket.validation.IValidator;
public class StrongPasswordValidator implements IValidator<String>{
...
}
2.超越validate(IValidatable <string>validatable)
。
public class StrongPasswordValidator implements IValidator<String>{
...
@Override
public void validate(IValidatable<String> validatable) {
//get input from attached component
final String field = validatable.getValue();
}
}
3.将自定义验证程序附加到表单组件。
public class CustomValidatorPage extends WebPage {
public CustomValidatorPage(final PageParameters parameters) {
final PasswordTextField password = new PasswordTextField("password",Model.of(""));
//attached custom validator to password field
password.add(new StrongPasswordValidator());
//...
}
}
完整示例
参见下面的 Wicket 示例,创建一个定制的密码验证器,并在密码与预定义的模式不匹配时显示一条错误消息。
1.StrongPasswordValidator
自定义密码验证程序。
package com.mkyong.user;
import java.util.regex.Pattern;
import org.apache.wicket.validation.IValidatable;
import org.apache.wicket.validation.IValidator;
import org.apache.wicket.validation.ValidationError;
public class StrongPasswordValidator implements IValidator<String> {
private final String PASSWORD_PATTERN
= "((?=.*\\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%]).{6,20})";
private final Pattern pattern;
StrongPasswordValidator() {
pattern = Pattern.compile(PASSWORD_PATTERN);
}
@Override
public void validate(IValidatable<String> validatable) {
final String password = validatable.getValue();
// validate password
if (pattern.matcher(password).matches() == false) {
//Message from key "StrongPasswordValidator.not-strong-password"
error(validatable, "not-strong-password");
}
}
private void error(IValidatable<String> validatable, String errorKey) {
ValidationError error = new ValidationError();
error.addMessageKey(getClass().getSimpleName() + "." + errorKey);
validatable.error(error);
}
}
文件:package.properties
StrongPasswordValidator.not-strong-password = Password required at least ... (omitted)
2.附加到组件
将上述自定义验证程序附加到密码字段。
package com.mkyong.user;
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.PasswordTextField;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.model.Model;
public class CustomValidatorPage extends WebPage {
public CustomValidatorPage(final PageParameters parameters) {
add(new FeedbackPanel("feedback"));
final PasswordTextField password
= new PasswordTextField("password",Model.of(""));
//attached custom validator to password field
password.add(new StrongPasswordValidator());
Form<?> form = new Form<Void>("form") {
@Override
protected void onSubmit() {
info("Done");
}
};
add(form);
form.add(password);
}
}
<html>
<head>
<style>
.feedbackPanelERROR {
color: red;
}
</style>
</head>
<body>
<h1>Wicket custom validator example</h1>
<div wicket:id="feedback"></div>
<form wicket:id="form">
<p>
<label>Password</label>:
<input wicket:id="password" type="password" size="20" />
</p>
<input type="submit" value="Register" />
</form>
</body>
</html>
3.演示
开始并访问—http://localhost:8080/wicket examples/
键入弱密码,则从自定义验证程序返回并显示错误消息。
Download it – Wicket-Custom-Validator-Example.zip (9KB)
参考
JSF 2.0 中的自定义转换器
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/jsf2/custom-converter-in-jsf-2-0/
在本文中,我们将向您展示如何在 JSF 2.0 中创建自定义转换器。
步骤
1。通过实现javax . faces . convert . converter接口创建一个转换器类。
2。重写 getAsObject() 和 getAsString() 方法。
3。使用 @FacesConverter 注释分配唯一的转换器 ID。
4。通过 f:converter 标签将您的自定义转换器类链接到 JSF 组件。
自定义转换器示例
创建一个名为“URLConverter”的 JSF 2 自定义转换器的详细指南,该转换器用于将字符串转换为 URL 格式(仅在前面添加 HTTP 协议:),并将其存储到一个对象中。
freestar.config.enabled_slots.push({ placementName: "mkyong_incontent_1", slotId: "mkyong_incontent_1" });
1.文件夹结构
这个例子的文件夹结构。
2.转换器类别
通过实现javax . faces . convert . converter接口创建一个自定义转换器类。
package com.mkyong;
import javax.faces.convert.Converter;
public class URLConverter implements Converter{
//...
}
覆盖以下两种方法:
1。,将给定的字符串值转换成一个对象。
2。getAsString() ,将给定对象转换成字符串。
public class URLConverter implements Converter{
@Override
public Object getAsObject(FacesContext context, UIComponent component,
String value) {
//...
}
@Override
public String getAsString(FacesContext context, UIComponent component,
Object value) {
//...
}
使用 @FacesConverter 注释分配转换器 ID。
package com.mkyong;
import javax.faces.convert.Converter;
@FacesConverter("com.mkyong.URLConverter")
public class URLConverter implements Converter{
//...
}
查看完整的自定义转换器源代码:
URLConverter.java
package com.mkyong;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;
import javax.faces.convert.FacesConverter;
import org.apache.commons.validator.UrlValidator;
@FacesConverter("com.mkyong.URLConverter")
public class URLConverter implements Converter{
@Override
public Object getAsObject(FacesContext context, UIComponent component,
String value) {
String HTTP = "http://";
StringBuilder url = new StringBuilder();
//if not start with http://, then add it
if(!value.startsWith(HTTP, 0)){
url.append(HTTP);
}
url.append(value);
//use Apache common URL validator to validate URL
UrlValidator urlValidator = new UrlValidator();
//if URL is invalid
if(!urlValidator.isValid(url.toString())){
FacesMessage msg =
new FacesMessage("URL Conversion error.",
"Invalid URL format.");
msg.setSeverity(FacesMessage.SEVERITY_ERROR);
throw new ConverterException(msg);
}
URLBookmark urlBookmark = new URLBookmark(url.toString());
return urlBookmark;
}
@Override
public String getAsString(FacesContext context, UIComponent component,
Object value) {
return value.toString();
}
}
在这个自定义的转换器类中,它被赋予一个转换器 id 作为“ com.mkyong.URLConverter ”,并将任何给定的字符串(通过在前面添加“http”)转换成“ URLBookmark 对象。
此外,如果 URL 验证失败,返回一个带有声明的错误消息的 FacesMessage 对象。
URLBookmark.java
package com.mkyong;
public class URLBookmark{
String fullURL;
public URLBookmark(String fullURL) {
this.fullURL = fullURL;
}
public String getFullURL() {
return fullURL;
}
public void setFullURL(String fullURL) {
this.fullURL = fullURL;
}
public String toString(){
return fullURL;
}
}
3.受管 Bean
一个名为“user”的普通托管 bean,这里没什么特别的。
package com.mkyong;
import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
@ManagedBean(name="user")
@SessionScoped
public class UserBean implements Serializable{
String bookmarkURL;
public String getBookmarkURL() {
return bookmarkURL;
}
public void setBookmarkURL(String bookmarkURL) {
this.bookmarkURL = bookmarkURL;
}
}
4.JSF·佩奇
通过“ f:converter ”标记中的“ converterId ”属性将上述自定义转换器链接到 JSF 组件。
default.xhtml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
>
<h:body>
<h1>Custom converter in JSF 2.0</h1>
<h:form>
<h:panelGrid columns="3">
Enter your bookmark URL :
<h:inputText id="bookmarkURL" value="#{user.bookmarkURL}"
size="20" required="true" label="Bookmark URL">
<f:converter converterId="com.mkyong.URLConverter" />
</h:inputText>
<h:message for="bookmarkURL" style="color:red" />
</h:panelGrid>
<h:commandButton value="Submit" action="result" />
</h:form>
</h:body>
</html>
result.xhtml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:c="http://java.sun.com/jsp/jstl/core"
>
<h:body>
<h1>Custom converter in JSF 2.0</h1>
<h:panelGrid columns="2">
Bookmark URL :
<h:outputText value="#{user.bookmarkURL}" />
</h:panelGrid>
</h:body>
</html>
5.演示
输入一个有效的 URL,不带“http”。
在有效的 URL 前面添加一个“http”并显示它。
如果提供了无效的 URL,则返回声明的错误消息。
下载源代码
Download It – JSF-2-Custom-Converter-Example.zip (11KB)
参考
Tags : jsf2freestar.config.enabled_slots.push({ placementName: "mkyong_leaderboard_btf", slotId: "mkyong_leaderboard_btf" });
JSF 2.0 中的自定义标签
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/jsf2/custom-tags-in-jsf-2-0/
在 JSF 2.0 中,您可以创建自定义标签来呈现预定义内容。定制标签看起来像普通的 JSF 标签,使用“ ui:composition ”将内容插入页面。
以下是在 JSF 2.0 中创建自定义标签的步骤概要。
- 使用:ui:composition "标记在 XHTML 页面中创建预定义的内容。
- 在标记库描述符中声明自定义标记。
- 在 web.xml 中注册标记库描述符。
自定义标签示例
创建自定义标签的指南,它将在页面中插入两个预定义的提交和重置按钮。
1.自定义标签
创建一个普通的 XHTML 文件来实现定制标记,它使用“ui:composition”标记将提交和重置按钮组合在一起。
we b-INF/tags/com/mkyong/button . XHTML
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
>
<h:body>
<ui:composition>
<h:commandButton type="submit" value="#{buttonSubmitLabel}" />
<h:commandButton type="reset" value="#{buttonResetLabel}" />
</ui:composition>
</h:body>
</html>
2.标签库
在标记库描述符文件中定义自定义标记细节。
- 命名空间–该标签库的命名空间,创建一个唯一的名称以避免冲突。
- 标记名–自定义标记名。
- 源–自定义标签的实现。
we b-INF \ mkyong . taglib . XML
<?xml version="1.0"?>
<!DOCTYPE facelet-taglib PUBLIC
"-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN"
"http://java.sun.com/dtd/facelet-taglib_1_0.dtd">
<facelet-taglib>
<namespace>http://mkyong.com/facelets</namespace>
<tag>
<tag-name>button</tag-name>
<source>tags/com/mkyong/button.xhtml</source>
</tag>
</facelet-taglib>
3.在 web.xml 中注册
在 web.xml 文件中注册标记库。
<!-- Load custom tag into JSF web application -->
<context-param>
<param-name>facelets.LIBRARIES</param-name>
<param-value>/WEB-INF/mkyong.taglib.xml</param-value>
</context-param>
4.使用自定义标签
要使用自定义标签,您必须在顶部声明它的名称空间,并像普通的 JSF 标签一样使用它。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:mkyong="http://mkyong.com/facelets"
>
<h:body>
<h1>Custome Tags in JSF 2.0</h1>
<mkyong:button
buttonSubmitLabel="Submit"
buttonResetLabel="Reset" />
</h:body>
</html>
“mkyong:button”自定义标记将呈现一个提交按钮和一个重置按钮。
下载源代码
Download It – JSF-2-Custom-Tag-Example.zip (11KB)Tags : custom tag jsf2
JSF 2.0 中的自定义验证程序
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/jsf2/custom-validator-in-jsf-2-0/
在本文中,我们将向您展示如何在 JSF 2.0 中创建一个定制的验证器
步骤
- 通过实现
javax.faces.validator.Validator
接口创建一个验证器类。 - 覆盖
validate()
方法。 - 通过
@FacesValidator
注释分配一个唯一的验证器 ID。 - 通过
f:validator
标签引用自定义验证器类到 JSF 组件。
创建自定义验证器名称“EmailValidator”的详细指南——通过 Java 正则表达式验证电子邮件地址。
1.文件夹结构
该项目的目录结构。
freestar.config.enabled_slots.push({ placementName: "mkyong_incontent_1", slotId: "mkyong_incontent_1" });
2.验证程序类
创建一个自定义验证器类并实现javax.faces.validator.Validator
接口。
package com.mkyong;
import javax.faces.validator.Validator;
public class EmailValidator implements Validator{
//...
}
覆盖validate()
方法。
public class EmailValidator implements Validator{
public void validate(FacesContext context, UIComponent component,
Object value) throws ValidatorException {
//...
}
}
用@FacesValidator
分配一个唯一的验证器 ID。
package com.mkyong;
import javax.faces.validator.Validator;
@FacesValidator("com.mkyong.EmailValidator")
public class EmailValidator implements Validator{
//...
}
查看完整的自定义验证器类:
EmailValidator.java
package com.mkyong;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.FacesValidator;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;
@FacesValidator("com.mkyong.EmailValidator")
public class EmailValidator implements Validator{
private static final String EMAIL_PATTERN = "^[_A-Za-z0-9-]+(\\." +
"[_A-Za-z0-9-]+)*@[A-Za-z0-9]+(\\.[A-Za-z0-9]+)*" +
"(\\.[A-Za-z]{2,})$";
private Pattern pattern;
private Matcher matcher;
public EmailValidator(){
pattern = Pattern.compile(EMAIL_PATTERN);
}
@Override
public void validate(FacesContext context, UIComponent component,
Object value) throws ValidatorException {
matcher = pattern.matcher(value.toString());
if(!matcher.matches()){
FacesMessage msg =
new FacesMessage("E-mail validation failed.",
"Invalid E-mail format.");
msg.setSeverity(FacesMessage.SEVERITY_ERROR);
throw new ValidatorException(msg);
}
}
}
上面是一个定制的验证器类,id 为com.mkyong.EmailValidator
。如果电子邮件无效,返回FacesMessage
错误消息。
Note
For detail explanation about the email regular expression pattern, please refer to this “Validate E-mail with Java regular expression” article.
3.受管 Bean
一个名为“user”的普通托管 bean,这里没什么特别的。
package com.mkyong;
import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
@ManagedBean(name="user")
@SessionScoped
public class UserBean implements Serializable{
String email;
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
4.JSF·佩奇
通过f:validator
标签中的validatorId
属性将上述自定义验证器引用到 JSF 组件。
Spring DI into JSF custom validator
If you need @Autowired
into JSF custom validator, uses binding
, instead of validatorId
. Read this post – Spring @Autowired into JSF custom validator.default.xhtml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
>
<h:body>
<h1>Custom validator in JSF 2.0</h1>
<h:form>
<h:panelGrid columns="3">
Enter your email :
<h:inputText id="email" value="#{user.email}"
size="20" required="true" label="Email Address">
<f:validator validatorId="com.mkyong.EmailValidator" />
</h:inputText>
<h:message for="email" style="color:red" />
</h:panelGrid>
<h:commandButton value="Submit" action="result" />
</h:form>
</h:body>
</html>
result.xhtml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html
xmlns:h="http://java.sun.com/jsf/html"
>
<h:body>
<h1>Custom validator in JSF 2.0</h1>
<h:panelGrid columns="2">
Email Address :
<h:outputText value="#{user.email}" />
</h:panelGrid>
</h:body>
</html>
5.演示
通过自定义验证器验证电子邮件地址,如果电子邮件地址无效,返回错误消息。
下载源代码
Download It – JSF-2-Custom-Validator-Example (10KB)
参考
Tags : jsf2 validationfreestar.config.enabled_slots.push({ placementName: "mkyong_leaderboard_btf", slotId: "mkyong_leaderboard_btf" });
Spring Security:自定义 403 拒绝访问页面
在 Spring Security 中,如果非授权用户试图访问受保护的页面,将显示默认的“ http 403 access denied ”:
在本教程中,我们将向您展示如何在 Spring Security 中自定义 403 访问被拒绝页面。
1.Spring 安全配置
查看配置,如果“alex”试图访问/admin
页面,将显示上面的 403 访问被拒绝页面。
Spring-Security.xml
<http auto-config="true">
<access-denied-handler error-page="/403" />
<intercept-url pattern="/admin**" access="ROLE_ADMIN" />
</http>
<authentication-manager>
<authentication-provider>
<user-service>
<user name="alex" password="123456" authorities="ROLE_USER" />
<user name="mkyong" password="123456" authorities="ROLE_USER, ROLE_ADMIN" />
</user-service>
</authentication-provider>
</authentication-manager>
2.解决方案–自定义 403 页面
2.1 创建一个新的 403 页面。
403.jsp
<html>
<body>
<h1>HTTP Status 403 - Access is denied</h1>
<h2>${msg}</h2>
</body>
</html>
2.2.要显示以上页面,添加一个如下所示的error-page
:
Spring-Security.xml
<http auto-config="true">
<access-denied-handler error-page="/403" />
<intercept-url pattern="/admin**" access="ROLE_ADMIN" />
</http>
2.3 在控制器类中,添加“/403”URL 的映射:
HelloController.java
package com.mkyong.web.controller;
import java.security.Principal;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class HelloController {
// for 403 access denied page
@RequestMapping(value = "/403", method = RequestMethod.GET)
public ModelAndView accesssDenied(Principal user) {
ModelAndView model = new ModelAndView();
if (user != null) {
model.addObject("msg", "Hi " + user.getName()
+ ", you do not have permission to access this page!");
} else {
model.addObject("msg",
"You do not have permission to access this page!");
}
model.setViewName("403");
return model;
}
}
完成了。
对于注释用户,使用此.exceptionHandling().accessDeniedPage("/403")
。
SecurityConfig.java
package com.mkyong.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
.and().formLogin()
.loginPage("/login").failureUrl("/login?error")
.usernameParameter("username")
.passwordParameter("password")
.and().logout().logoutSuccessUrl("/login?logout")
.and()
.exceptionHandling().accessDeniedPage("/403")
}
}
3.AccessDeniedHandler
另外,您可以创建一个自定义的AccessDeniedHandler
来执行一些业务逻辑,然后将 URL 传递给/403
映射。
MyAccessDeniedHandler.java
package com.mkyong.web.exception;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
public class MyAccessDeniedHandler implements AccessDeniedHandler {
private String errorPage;
public MyAccessDeniedHandler() {
}
public MyAccessDeniedHandler(String errorPage) {
this.errorPage = errorPage;
}
public String getErrorPage() {
return errorPage;
}
public void setErrorPage(String errorPage) {
this.errorPage = errorPage;
}
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException accessDeniedException)
throws IOException, ServletException {
//do some business logic, then redirect to errorPage url
response.sendRedirect(errorPage);
}
}
给 http 标签添加一个ref
。
Spring-Security.xml
<http auto-config="true">
<access-denied-handler ref="my403" />
<intercept-url pattern="/admin**" access="ROLE_ADMIN" />
</http>
<beans:bean id="my403"
class="com.mkyong.web.exception.MyAccessDeniedHandler">
<beans:property name="errorPage" value="403" />
</beans:bean>
完成了。
4.演示
当“alex”试图访问/admin
页面时,将显示上述自定义 403 访问被拒绝页面。
4.1 如果使用error-page
,url 将显示如下:
http://localhost:8080/spring-security-403-access-denied/admin
4.2 如果使用自定义拒绝访问处理程序ref
,url 将显示如下:
http://localhost:8080/spring-security-403-access-denied/403
下载源代码
Download it – spring-security-403-access-denied.zip (26 KB)
参考
在 JSF 2.0 中自定义验证错误消息
标准 JSF 转换和验证错误消息太详细、太专业,有时不太容易让人理解。在本文中,它向您展示了如何在 JSF 2.0 中定制标准转换或验证错误消息。
概要指南
- 从 jsf-api-2.x.jar 的“Messages.properties”文件中找到您的消息密钥。
- 创建您自己的属性文件,放置您在上面的“Messages.properties”文件中找到的相同消息键,并用您的自定义错误消息覆盖它。
- 在“faces-config.xml”中注册您的属性文件,将其作为应用程序级别。
- 完成了。
1.消息.属性
所有 JSF 标准转换和验证错误消息都存储在“Messages.properties”文件中,该文件可以从 jsf-api-2.x.jar 、javax \ faces \ messages . properties中找到,见下图:
请参见“ Messages.properties ”文件的一部分
...
# ===================================================================
# Converter Errors
# ===================================================================
javax.faces.converter.DateTimeConverter.DATE={2}: ''{0}'' could not be understood as a date.
javax.faces.converter.DateTimeConverter.DATE_detail={2}: ''{0}'' could not be understood as a date. Example: {1}
...
# ====================================================================
# Validator Errors
# ====================================================================
javax.faces.validator.LengthValidator.MAXIMUM={1}: Validation Error: Length is greater than allowable maximum of ''{0}''
javax.faces.validator.LengthValidator.MINIMUM={1}: Validation Error: Length is less than allowable minimum of ''{0}''
...
比如
1。<f:validate length minimum = " 5 " maximum = " 10 "/>如果最大长度验证失败,JSF 得到“javax . faces . validator . length validator . maximum”。
如果最小长度验证失败,JSF 得到“javax . faces . validator . length validator . minimum”。
2。<f:convert datetime pattern = " d-M-yyyy "/>
如果日期验证失败,JSF 得到“javax . faces . converter . datetime converter . date _ detail”。
Note
If you do not sure which key match to which validator tag, just display the error message once and compare it with “Messages.properties”, then you will know which key you want to override.freestar.config.enabled_slots.push({ placementName: "mkyong_incontent_1", slotId: "mkyong_incontent_1" });
2.自定义错误消息
创建一个名为“ MyMessage.properties ”的属性文件(可以是您喜欢的任何名称),将消息键和自定义错误消息放入其中。稍后,将该属性文件放入项目资源文件夹中。
MyMessage.properties
javax.faces.converter.DateTimeConverter.DATE={2}: ''{0}'' could not be understood as a date.
javax.faces.converter.DateTimeConverter.DATE_detail=Invalid date format.
javax.faces.validator.LengthValidator.MINIMUM=Minimum length of ''{0}'' is required.
现在,您将自定义“javax . faces . validator . length validator . minimum”的验证错误消息和“javax . faces . converter . datetime converter . date _ detail”的转换错误消息。
Note
For the XXX_detail message key, you have to override its parent key (summary message) as well, which is XXX without the “_detail” behind; Otherwise, JSF will ignore your new custom error message and keep getting the standard error message from “Messages.properties”, may be this is a bug in JSF 2.0?
3.注册消息包
在“faces-config.xml”中注册您的自定义属性文件,将其作为应用程序级别。
faces-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<faces-config
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-facesconfig_2_0.xsd"
version="2.0">
<application>
<message-bundle>
com.mkyong.MyMessage
</message-bundle>
</application>
</faces-config>
4.演示
一个 JSF 页面,同时添加<f:validate length/>和<f:convert datetime/>验证。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
>
<h:body>
<h1>Customize validation error message in JSF 2.0</h1>
<h:form>
<h:panelGrid columns="3">
Enter your username :
<h:inputText id="username" value="#{user.username}"
size="20" required="true" label="Username">
<f:validateLength minimum="5" maximum="10" />
</h:inputText>
<h:message for="username" style="color:red" />
Enter your DOB :
<h:inputText id="dob" value="#{user.dob}"
size="20" required="true" label="Date of Birth">
<f:convertDateTime />
</h:inputText>
<h:message for="dob" style="color:red" />
</h:panelGrid>
<h:commandButton value="Submit" action="result" />
</h:form>
</h:body>
</html>
当验证失败时,现在显示您的自定义错误消息。
下载源代码
Download It – JSF-2-Custom-Validation-Error-Message-Example.zip (11KB)
参考
Tags : error message jsf2 validationfreestar.config.enabled_slots.push({ placementName: "mkyong_leaderboard_btf", slotId: "mkyong_leaderboard_btf" });
在 Tomcat + SSL 连接上部署 JAX-WS web 服务
在本文中,我们将向您展示如何在启用了 TLS / SSL 或 https 安全连接的 Tomcat 上部署 JAX-WS web 服务。实际上,答案很简单,只需将它部署为普通的 web 服务,并在 Tomcat 服务器上正确配置 SSL 连接🙂
Note
This article is just a combination of my last few posts on developing web service in SSL connection environment.
1.配置 Tomcat + SSL
有关详细信息,请参见本指南—让 Tomcat 支持 SSL 或 https 连接。
基本上,只需从可信的证书提供商那里购买一个证书,或者使用 JDK 的keytool
命令为本地主机测试生成一个伪证书。并将以下部分放入您的 Tomcat server.xml
文件中。
File:$ Tomcat \ conf \ server . XML
//...
<!-- Define a SSL HTTP/1.1 Connector on port 8443
This connector uses the JSSE configuration, when using APR, the
connector should be using the OpenSSL style configuration
described in the APR documentation -->
<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS"
keystoreFile="c:\your keystore file"
keystorePass="your keystore password" />
//...
重启 Tomcat,现在,你的 Tomcat 支持 SSL 连接,例如 https://localhost:8443
2.部署 Web 服务
像部署一个普通的 web 服务一样部署它,参见本指南—在 Tomcat servlet 容器上部署 JAX-WS web 服务。
3.测试一下
配置完成;您可以通过使用普通的 web 服务客户端,在 SSL 连接中访问部署的 web 服务。
举个例子,
URL url = new URL("https://localhost:8443/HelloWorld/hello?wsdl");
QName qname = new QName("http://ws.mkyong.com/", "HelloWorldImplService");
Service service = Service.create(url, qname);
HelloWorld hello = service.getPort(HelloWorld.class);
System.out.println(hello.getHelloWorldAsString());
Note
For localhost SSL testing environment, the client will hit following exceptions, please read the problem and solution below :
4.完成的
你的 web 服务是在 SSL 保护下的,相当简单,web 服务站点上没有变化;只需将您的 Tomcat 配置为只支持 SSL 连接。
参考
jQuery 中 filter()和 find()的区别
filter() 和 find() 方法非常相似,只是前者适用于所有元素,而后者只搜索子元素。
太简单了
- filter()–搜索所有元素。
- find()–仅搜索所有子元素。
jQuery filter()与 find()示例
<html>
<head>
<script type="text/javascript" src="jquery-1.3.2.min.js"></script>
<style type="text/css">
div{
padding:8px;
border:1px solid;
}
</style>
</head>
<body>
<h1>jQuery find() vs filter() example</h1>
<script type="text/javascript">
$(document).ready(function(){
$("#filterClick").click(function () {
$('div').css('background','white');
$('div').filter('#Fruits').css('background','red');
});
$("#findClick").click(function () {
$('div').css('background','white');
$('div').find('#Fruits').css('background','red');
});
});
</script>
</head><body>
<div id="Fruits">
Fruits
<div id="Apple">Apple</div>
<div id="Banana">Banana</div>
</div>
<div id="Category">
Category
<div id="Fruits">Fruits</div>
<div id="Animals">Animals</div>
</div>
<br/>
<br/>
<br/>
<input type='button' value='filter(Fruits)' id='filterClick'>
<input type='button' value='find(Fruits)' id='findClick'>
</body>
</html>
Try DemoTags : jquery jquery traversing
jQuery 中 find()和 children()的区别
find() 和 children() 两种方法都用来过滤匹配元素的子元素,只不过前者是向下遍历任意一级,后者是向下遍历单级。
太简单了
- find()–搜索匹配元素的子元素、孙元素、曾孙元素…任何下一层。
- children()–仅搜索匹配元素的子元素(向下一级)。
参见下面的例子来理解 find()和 children() 之间的区别。
jQuery find() vs children()示例
<html>
<head>
<script type="text/javascript" src="jquery-1.3.2.min.js"></script>
<style type="text/css">
div{
padding:8px;
border:1px solid;
}
</style>
</head>
<body>
<h1>jQuery find() vs children() example</h1>
<script type="text/javascript">
$(document).ready(function(){
$("#testChildren").click(function () {
$('div').css('background','white');
$('.B1').children('.child').css('background','red');
});
$("#testFind").click(function () {
$('div').css('background','white');
$('.B1').find('.child').css('background','red');
});
});
</script>
</head><body>
<div class="B1">
<div class="child">B1-1</div>
<div class="child">B1-2</div>
<div class="orphan">B1-3 - Orphan</div>
<div class="child">B1-4</div>
<div class="B2">
<div class="child">B2-1</div>
<div class="child">B2-2</div>
<div class="orphan">B2-2 - Orphan</div>
<div class="B3">
<div class="child">B3-1</div>
<div class="orphan">B3-2 - Orphan</div>
<div class="child">B3-3</div>
</div>
</div>
</div>
<br/>
<br/>
<br/>
<input type='button' value='.B1 children(child)' id='testChildren'>
<input type='button' value='.B1 find(child)' id='testFind'>
</body>
</html>
Struts 2 FilterDispatcher 和 StrutsPrepareAndExecuteFilter 的区别?
问题
在 Struts2 开发中,很多人问为什么有些将 filter 类声明为“filter dispatcher”;还有人声明“strutsprepareendexecutefilter”?但是两者都工作得很好,有什么不同呢?
1。FilterDispatcher 示例
...
<filter>
<filter-name>struts2</filter-name>
<filter-class>
org.apache.struts2.dispatcher.FilterDispatcher
</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
...
2。StrutsPrepareAndExecuteFilter 示例
...
<filter>
<filter-name>struts2</filter-name>
<filter-class>
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
...
回答
FilterDispatcher ( org.apache.struts2.dispatcher.FilterDispatcher
)用于早期的 Struts2 开发中,从 Struts 2.1.3 开始就不再使用了。
如果您使用的是 Struts 版本> = 2.1.3,那么总是建议升级新的过滤器类—strutsprepareendexecutefilter(org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
)。
参考
级联和逆之间的不同
许多 Hibernate 开发人员对 cascade 选项和 inverse 关键字感到困惑。在某些方面..它们一开始看起来真的很相似,两者都与关系有关。
级联与反向
然而,级联和逆之间没有关系,两者是完全不同的概念。
1.相反的
这用于决定哪一方是管理关系的关系所有者(插入或更新外键列)。
例子
在此示例中,关系所有者属于 stockDailyRecords (inverse=true)。
<!-- Stock.hbm.xml -->
<hibernate-mapping>
<class name="com.mkyong.common.Stock" table="stock" ...>
...
<set name="stockDailyRecords" table="stock_daily_record" inverse="true">
<key>
<column name="STOCK_ID" not-null="true" />
</key>
<one-to-many class="com.mkyong.common.StockDailyRecord" />
</set>
...
当您保存或更新股票对象时
session.save(stock);
session.update(stock);
Hibernate 只会插入或更新股票表,不会更新外键列。此处有更多详细示例……
2.串联
在 cascade 中,在一个操作(保存、更新和删除)完成后,它决定是否需要在另一个有关系实体上调用其他操作(保存、更新和删除)。
例子
在本例中,在 stockDailyRecords 上声明了 cascade="save-update"。
<!-- Stock.hbm.xml -->
<hibernate-mapping>
<class name="com.mkyong.common.Stock" table="stock" ...>
...
<set name="stockDailyRecords" table="stock_daily_record"
cascade="save-update" inverse="true">
<key>
<column name="STOCK_ID" not-null="true" />
</key>
<one-to-many class="com.mkyong.common.StockDailyRecord" />
</set>
...
当您保存或更新股票对象时
session.save(stock);
session.update(stock);
它会将记录插入或更新到股票表中,并在 StockDailyRecord 上调用另一个 insert 或 update 语句(cascade="save-update ")。此处有更多详细示例……
结论
简而言之,“逆向”是决定哪一方将更新外键,而“级联”是决定应该执行什么样的后续操作。两者在关系上看起来很相似,但这完全是两码事。Hibernate 开发人员值得花时间去研究它,因为误解这个概念或误用它会给应用程序带来严重的性能或数据完整性问题。
参考
jQuery 中 mouseout()和 mouseleave()之间的不同
在 jQuery 中,当鼠标离开匹配的元素时,触发 mouseout() 和 mouseleave() 事件。唯一不同的是在子元素中处理“事件冒泡的方式,让我们看两个场景:
1.没有子元素
如果匹配的元素没有子元素,那么 mouseout() 和 mouseleave() 事件的工作方式完全相同。参见下面的“亲自尝试”。
2.内部有子元素
如果匹配的元素有子元素,那么 mouseout() 和 mouseleave() 事件的工作方式是不同的,其方式是“事件冒泡”:
例如,“outerBox”包含一个子元素“innerBox”。
<div id="outerBox">OuterBox
<div id="innerBox">InnerBox
</div>
</div>
请确保外部盒子和内部盒子都与特定事件相关联。
mouseout()
- 当鼠标进入“外部盒子”时,不会触发任何事件。
- 当鼠标离开“外部盒子”并进入“内部盒子”时,触发“外部盒子”事件。
- 当鼠标离开“innerBox”并进入“outerBox”时,触发“innerBox”事件,然后触发“outerBox”事件。
- 当鼠标离开“outerBox”时,触发“outerBox”事件。
鼠标离开()
- 当鼠标进入“外部盒子”时,不会触发任何事件。
- 当鼠标离开“外部盒子”并进入“内部盒子”时,不会触发任何事件。
- 当鼠标离开“内盒”并进入“外盒”时,触发“内盒”事件。
- 当鼠标离开“outerBox”时,触发“outerBox”事件。
让我们通过播放下面的例子来理解它:
你自己试试
<html>
<head>
<script type="text/javascript" src="jquery-1.4.2.min.js"></script>
<style type="text/css">
#mouseout-outerBox1, #mouseleave-outerBox1,
#mouseout-outerBox2, #mouseleave-outerBox2{
margin:8px;
border:1px groove #999966;
background-color : #999966;
width:150px;
height:150px;
color:white;
}
#mouseout-innerBox2, #mouseleave-innerBox2{
margin:8px 8px 8px 16px;
border:1px groove #0000FF;
background-color : #0000FF;
width:100px;
height:100px;
color:white;
}
span{
padding:8px;
}
.content{
width:500px;
height:250px;
}
.container1{
float:left;
padding-right:16px;
}
</style>
</head>
<body>
<h1>jQuery mouseout() vs mouseleave() example</h1>
<div class="content">
<div class="container1">
<span>mouseout() - no child element</span>
<div id="mouseout-outerBox1">OuterBox
</div>
<span id="mouseout-msg1">#mouseout is fired : 0</span>
</div>
<div class="container1">
<span>mouseleave() - no child element</span>
<div id="mouseleave-outerBox1">OuterBox
</div>
<span id="mouseleave-msg1">#mouseleave is fired : 0</span>
</div>
</div>
<div class="content">
<div class="container1">
<span>mouseout() - with child elements</span>
<div id="mouseout-outerBox2">OuterBox
<div id="mouseout-innerBox2">InnerBox
</div>
</div>
<span id="mouseout-outer-msg2">#mouseout outer is fired : 0</span>
<br/>
<span id="mouseout-inner-msg2">#mouseout inner is fired : 0</span>
</div>
<div class="container1">
<span>mouseleave() - with child elements</span>
<div id="mouseleave-outerBox2">OuterBox
<div id="mouseleave-innerBox2">InnerBox
</div>
</div>
<span id="mouseleave-outer-msg2">#mouseleave outer is fired : 0</span>
<br/>
<span id="mouseleave-inner-msg2">#mouseleave inner is fired : 0</span>
</div>
</div>
<script type="text/javascript">
//example 1
var mouseout1=1;
$('#mouseout-outerBox1').mouseout(function(event) {
$('#mouseout-msg1').text('#mouseout is fired : ' + mouseout1++)
});
var mouseleave1=1;
$('#mouseleave-outerBox1').mouseleave(function(event) {
$('#mouseleave-msg1').text('#mouseleave is fired : ' + mouseleave1++)
});
//example 2
var mouseoutouter2=1;
$('#mouseout-outerBox2').mouseout(function(event) {
$('#mouseout-outer-msg2').text('#mouseout outer is fired : ' + mouseoutouter2++)
});
var mouseoutinner2=1;
$('#mouseout-innerBox2').mouseout(function(event) {
$('#mouseout-inner-msg2').text('#mouseout inner is fired : ' + mouseoutinner2++)
});
var mouseleaveouter2=1;
$('#mouseleave-outerBox2').mouseleave(function(event) {
$('#mouseleave-outer-msg2')
.text('#mouseleave outer is fired : ' + mouseleaveouter2++)
});
var mouseleaveinner2=1;
$('#mouseleave-innerBox2').mouseleave(function(event) {
$('#mouseleave-inner-msg2')
.text('#mouseleave inner is fired : ' + mouseleaveinner2++)
});
</script>
</body>
</html>
jQuery 中的 mouseover()和 mouseenter()不同
在 jQuery 中,当鼠标进入匹配的元素时,触发 mouseover() 和 mouseenter() 事件。唯一不同的是在子元素中处理“事件冒泡的方式,让我们看两个场景:
1.没有子元素
如果匹配的元素没有子元素,那么 mouseover() 和 mouseenter() 事件的工作方式完全相同。参见下面的“亲自尝试”。
2.内部有子元素
如果匹配的元素有子元素,那么 mouseover() 和 mouseenter() 事件的工作方式是不同的,就是“事件冒泡”:
例如,“outerBox”包含一个子元素“innerBox”。
<div id="outerBox">OuterBox
<div id="innerBox">InnerBox
</div>
</div>
请确保外部盒子和内部盒子都与特定事件相关联。
鼠标悬停()
- 当鼠标进入“outerBox”时,触发“outerBox”事件。
- 当鼠标进入“innerBox”时,触发“innerBox”事件,然后触发“outerBox”事件。
- 当鼠标返回到“外部盒子”时,触发“外部盒子”事件。
滑鼠 enter()
- 当鼠标进入“outerBox”时,触发“outerBox”事件。
- 当鼠标进入“innerBox”时,触发“innerBox”事件。
- 当鼠标回到“外部盒子”时,不会触发任何事件。
雅,这是相当混乱和理解在“话”,让理解它通过播放下面的例子:
你自己试试
<html>
<head>
<script type="text/javascript" src="jquery-1.4.2.min.js"></script>
<style type="text/css">
#mouseover-outerBox1, #mouseenter-outerBox1,
#mouseover-outerBox2, #mouseenter-outerBox2{
margin:8px;
border:1px groove #999966;
background-color : #999966;
width:150px;
height:150px;
color:white;
}
#mouseover-innerBox2, #mouseenter-innerBox2{
margin:8px 8px 8px 16px;
border:1px groove #0000FF;
background-color : #0000FF;
width:100px;
height:100px;
color:white;
}
span{
padding:8px;
}
.content{
width:500px;
height:250px;
}
.container1{
float:left;
padding-right:16px;
}
</style>
</head>
<body>
<h1>jQuery mouseover() vs mouseenter() example</h1>
<div class="content">
<div class="container1">
<span>mouseover() - no child element</span>
<div id="mouseover-outerBox1">OuterBox
</div>
<span id="mouseover-msg1">#mouseover is fired : 0</span>
</div>
<div class="container1">
<span>mouseenter() - no child element</span>
<div id="mouseenter-outerBox1">OuterBox
</div>
<span id="mouseenter-msg1">#mouseenter is fired : 0</span>
</div>
</div>
<div class="content">
<div class="container1">
<span>mouseover() - with child elements</span>
<div id="mouseover-outerBox2">OuterBox
<div id="mouseover-innerBox2">InnerBox
</div>
</div>
<span id="mouseover-outer-msg2">#mouseover outer is fired : 0</span>
<br/>
<span id="mouseover-inner-msg2">#mouseover inner is fired : 0</span>
</div>
<div class="container1">
<span>mouseenter() - with child elements</span>
<div id="mouseenter-outerBox2">OuterBox
<div id="mouseenter-innerBox2">InnerBox
</div>
</div>
<span id="mouseenter-outer-msg2">#mouseenter outer is fired : 0</span>
<br/>
<span id="mouseenter-inner-msg2">#mouseenter inner is fired : 0</span>
</div>
</div>
<script type="text/javascript">
//example 1
var mouseover1=1;
$('#mouseover-outerBox1').mouseover(function(event) {
$('#mouseover-msg1').text('#mouseover is fired : ' + mouseover1++)
});
var mouseenter1=1;
$('#mouseenter-outerBox1').mouseenter(function(event) {
$('#mouseenter-msg1').text('#mouseenter is fired : ' + mouseenter1++)
});
//example 2
var mouseoverouter2=1;
$('#mouseover-outerBox2').mouseover(function(event) {
$('#mouseover-outer-msg2')
.text('#mouseover outer is fired : ' + mouseoverouter2++)
});
var mouseoverinner2=1;
$('#mouseover-innerBox2').mouseover(function(event) {
$('#mouseover-inner-msg2')
.text('#mouseover inner is fired : ' + mouseoverinner2++)
});
var mouseenterouter2=1;
$('#mouseenter-outerBox2').mouseenter(function(event) {
$('#mouseenter-outer-msg2')
.text('#mouseenter outer is fired : ' + mouseenterouter2++)
});
var mouseenterinner2=1;
$('#mouseenter-innerBox2').mouseenter(function(event) {
$('#mouseenter-inner-msg2')
.text('#mouseenter inner is fired : ' + mouseenterinner2++)
});
</script>
</body>
</html>
session.get()和 session.load()不同
很多时候,你会注意到 Hibernate 开发人员混合使用 session.get() 和 session load() ,你想知道有什么不同吗?什么时候你应该使用其中的一个?
session.get()和 session.load()不同
当然,实际上,这两个函数都是用不同的机制来检索一个对象。
1.session.load()
- 它将总是返回一个“代理”(Hibernate 术语),而不会命中数据库。在 Hibernate 中,代理是一个具有给定标识符值的对象,它的属性还没有初始化,看起来只是一个临时的假对象。
- 如果没有找到行,它将抛出一个 ObjectNotFoundException 。
2.session.get()
- 它总是命中数据库并返回真实对象,一个代表数据库行的对象,而不是代理。
- 如果没有找到行,它返回空值。
这是性能的问题
Hibernate 创建任何东西都是有原因的,当你做关联的时候,正常的做法是从数据库获取检索一个对象(持久实例)并把它作为一个引用分配给另一个对象,只是为了维护关系。下面我们通过一些例子来了解在什么情况下应该使用 session.load() 。
1.session.get()
例如,在一个股票应用程序中,股票和 StockTransactions 应该有一个“一对多”的关系,当您想要保存一个股票交易时,通常会声明如下所示的内容
Stock stock = (Stock)session.get(Stock.class, new Integer(2));
StockTransaction stockTransactions = new StockTransaction();
//set stockTransactions detail
stockTransactions.setStock(stock);
session.save(stockTransactions);
输出
Hibernate:
select ... from mkyong.stock stock0_
where stock0_.STOCK_ID=?
Hibernate:
insert into mkyong.stock_transaction (...)
values (?, ?, ?, ?, ?, ?)
在 session.get()中,Hibernate 会命中数据库检索 Stock 对象,并把它作为 StockTransaction 的引用。但是,这个保存过程要求极高,每小时可能有上千或上百万笔交易,你认为有必要打数据库检索股票对象的所有内容保存一条股票交易记录吗?毕竟,您只需要股票的 Id 作为 StockTransaction 的参考。
2.session.load()
在上面的场景中, session.load() 将是您的好解决方案,让我们来看看这个例子。
Stock stock = (Stock)session.load(Stock.class, new Integer(2));
StockTransaction stockTransactions = new StockTransaction();
//set stockTransactions detail
stockTransactions.setStock(stock);
session.save(stockTransactions);
输出
Hibernate:
insert into mkyong.stock_transaction (...)
values (?, ?, ?, ?, ?, ?)
在 session.load()中,Hibernate 不会命中数据库(输出中没有 select 语句)来检索股票对象,它将返回一个股票代理对象——一个具有给定标识值的假对象。在这种情况下,一个代理对象足以保存股票交易记录。
例外
在例外情况下,请参见示例
session.load()
Stock stock = (Stock)session.load(Stock.class, new Integer(100)); //proxy
//initialize proxy, no row for id 100, throw ObjectNotFoundException
System.out.println(stock.getStockCode());
它将始终返回一个具有给定标识值的代理对象,即使该标识值在数据库中不存在。但是,当您试图通过从数据库中检索代理的属性来初始化代理时,它将使用 select 语句来访问数据库。如果没有找到行,将抛出一个 ObjectNotFoundException 。
org.hibernate.ObjectNotFoundException: No row with the given identifier exists:
[com.mkyong.common.Stock#100]
session.get()
//return null if not found
Stock stock = (Stock)session.get(Stock.class, new Integer(100));
System.out.println(stock.getStockCode()); //java.lang.NullPointerException
如果在数据库中找不到标识值,它将始终返回 null。
结论
没有永远正确的解决方案,您必须了解它们之间的差异,并决定哪种方法最适合您的应用程序。
Tags : hibernate
相关文章
-
为什么我的项目选择 Hibernate?
JavaScript 和 JQuery 中函数调用的不同
这是一个简单的例子,演示了如何在 JavaScript 和 jQuery 中调用一个函数来创建页面加载后的动态内容。
1.Java Script 语言
为了在页面加载后调用函数,JavaScript 使用“ window.onload ”和“ innerHTML ”来动态创建内容。
window.onload = function(){
document.getElementById('msgid3')
.innerHTML = "This is Hello World by JavaScript";
}
2.jQuery
为了在页面加载后调用函数,jQuery 使用了" $(document)。准备好,和 html() 来动态创建内容。
$(document).ready(function(){
$("#msgid1").html("This is Hello World by JQuery 1<BR>");
});
例子
<html>
<head>
<title>jQuery Hello World</title>
</head>
<script type="text/javascript" src="jquery-1.2.6.min.js"></script>
<body>
<script type="text/javascript">
$(document).ready(function(){
$("#msgid1").html("This is Hello World by JQuery 1<BR>");
});
$(function(){
$("#msgid2").html("This is Hello World by JQuery 2<BR>");
});
window.onload = function(){
document.getElementById('msgid3').innerHTML = "This is Hello World by JavaScript";
}
</script>
This is Hello World by HTML
<div id="msgid1">
</div>
<div id="msgid2">
</div>
<div id="msgid3">
</div>
</body>
</html>
在 Spring Security 中显示自定义错误消息
在 Spring Security 中,当身份验证失败时,将显示以下预定义的错误消息:
Spring display : Bad credentials
在本文中,我们将向您展示如何覆盖上述错误消息并显示您自定义的错误消息。举个例子,
Spring display : Bad credentials
You want override it with this message : Invalid username or password
解决办法
Spring Security 将消息存储在“ spring-security-core.jar 内的“ messages.properties ”中,见下图:
要覆盖它,在 spring securitymessage . properties文件中找到哪个键生成了什么错误消息,并用您自己的属性文件重新定义它。
1.覆盖密钥和消息
创建一个新的属性文件,将其放在项目类路径中,并用您的自定义错误消息覆盖 Spring 的“key”。在这种情况下,只需覆盖“AbstractUserDetailsAuthenticationProvider.badCredentials
”。
文件:mymessages.properties
AbstractUserDetailsAuthenticationProvider.badCredentials=Invalid username or password
2.注册资源 BundleMessageSource
要加载上述属性文件,请在 Spring bean 配置文件中定义ResourceBundleMessageSource
。
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>mymessages</value>
</list>
</property>
</bean>
现在,当认证失败时,它将显示您自定义的错误消息“无效用户名或密码”,而不是默认的“错误凭证”。
Note
With this trick, you can override any Spring Security messages easily.
下载源代码
Download it – Spring-Security-Display-Custom-Error-Msg.zip (9 KB)spring security
下载/导出 Google 应用程序引擎日志、Java 应用程序
在 GAE,Java App,你可以使用命令" appcfg request_logs "从 GAE 下载或导出日志到你的电脑,见下面签名:
AppCfg [options] request_logs <app-dir> <output-file>
您可以输入“ appcfg request_logs ”来查看所有可用选项。
1.什么是<app-dir>
?
这不是您的 app_id,也不是来自 GAE 的任何已部署的 web 路径,这是您的本地项目文件夹,包含文件:
- WEB-INF/appengine-web.xml
- WEB-INF/web.xml
GAE Java SDK,命令“ appcfg ”是硬编码的,可以找到上面两个文件。要从 GAE 下载日志,您必须在“ WEB-INF ”文件夹中提供上述两个 XML 文件,这是没有意义的,但您别无选择,只能遵循 GAE 的做法。
在我看来,更好的方法应该是
AppCfg [options] request_logs <app-id-on-GAE> <output-file>
Note
If you don’t have the XML files but still want to download the logs, you can issue appcfg download_app to download the deployed application which included the XML files. ## 2.例子
以下是一些从 GAE 下载日志的常见例子。
1.从 GAE 下载今天的日志到“c:\gae.log”。
C:\appengine-java-sdk-1.6.3.1\bin>
appcfg request_logs C:\workspace-3.7\SpringGAEProject\war\ c:\gae.log
2.将 GAE 的所有日志下载到“c:\gae.log”。
C:\appengine-java-sdk-1.6.3.1\bin>
appcfg --num_days=0 request_logs C:\workspace-3.7\SpringGAEProject\war\ c:\gae.log
默认情况下,–天数=1
3.将严重性=4 的所有日志从 GAE 下载到“c:\gae.log”。
C:\appengine-java-sdk-1.6.3.1\bin>
appcfg --num_days=0 --severity=4 request_logs C:\workspace-3.7\SpringGAEProject\war\ c:\gae.log
P.S Severity 是日志中的记录级别。范围是 0(调试)到 4(关键)。
4.从 GAE 下载所有日志并附加到“c:\gae.log”。
C:\appengine-java-sdk-1.6.3.1\bin>
appcfg --num_days=0 --append request_logs C:\workspace-3.7\SpringGAEProject\war\ c:\gae.log
参考
从谷歌应用引擎(GAE)下载上传的应用程序
您可以使用命令“ appcfg download_app ”从 GAE 下载上传的应用程序,请参见下面的签名:
AppCfg [options] -A app_id [ -V version ] download_app <out-dir>
AppCfg 下载示例
假设您已经将 app_id 为" mkyong-springmvc "的应用程序上传到 GAE,要将其下载回您的计算机,请使用以下命令:
D:\appengine-java-sdk-1.6.3.1\bin>appcfg -A mkyong-springmvc download_app c:\testing
参见输出:
D:\appengine-java-sdk-1.6.3.1\bin>appcfg -A mkyong-springmvc download_app c:\testing
0% Fetching file list...
0% Fetching files...
0% [1/43] WEB-INF/lib/repackaged-appengine-tomcat-juli-6.0.29.jar
2% [2/43] WEB-INF/lib/spring-context-3.1.1.RELEASE.jar
4% [3/43] WEB-INF/classes/org/apache/jsp/pages/list_jsp.java
6% [4/43] WEB-INF/classes/META-INF/jdoconfig.xml
9% [5/43] WEB-INF/classes/org/apache/jsp/pages/list_jsp.class
11% [6/43] WEB-INF/lib/repackaged-appengine-jasper-el-6.0.29.jar
13% [7/43] WEB-INF/lib/spring-context-support-3.1.1.RELEASE.jar
16% [8/43] WEB-INF/logging.properties
18% [9/43] WEB-INF/lib/jdo2-api-2.3-eb.jar
20% [10/43] WEB-INF/lib/spring-webmvc-3.1.1.RELEASE.jar
23% [11/43] WEB-INF/lib/repackaged-appengine-jasper-6.0.29.jar
25% [12/43] WEB-INF/lib/repackaged-appengine-ant-1.7.1.jar
27% [13/43] WEB-INF/appengine-web.xml
30% [14/43] WEB-INF/classes/log4j.properties
32% [15/43] WEB-INF/lib/datanucleus-jpa-1.1.5.jar
34% [16/43] WEB-INF/lib/appengine-jsr107cache-1.6.3.1.jar
37% [17/43] WEB-INF/lib/geronimo-jpa_3.0_spec-1.1.1.jar
39% [18/43] WEB-INF/web.xml
41% [19/43] WEB-INF/lib/commons-logging-1.1.1.jar
44% [20/43] WEB-INF/lib/appengine-api-labs-1.6.3.1.jar
46% [21/43] WEB-INF/lib/datanucleus-appengine-1.0.10.final.jar
48% [22/43] WEB-INF/lib/repackaged-appengine-ant-launcher-1.7.1.jar
51% [23/43] WEB-INF/lib/aopalliance-1.0.jar
53% [24/43] favicon.ico
55% [25/43] WEB-INF/lib/repackaged-appengine-jasper-jdt-6.0.29.jar
58% [26/43] WEB-INF/lib/repackaged-appengine-jakarta-jstl-1.1.2.jar
60% [27/43] WEB-INF/lib/spring-expression-3.1.1.RELEASE.jar
62% [28/43] WEB-INF/lib/spring-web-3.1.1.RELEASE.jar
65% [29/43] WEB-INF/lib/spring-aop-3.1.1.RELEASE.jar
67% [30/43] WEB-INF/lib/spring-core-3.1.1.RELEASE.jar
69% [31/43] index.html
72% [32/43] WEB-INF/mvc-dispatcher-servlet.xml
74% [33/43] WEB-INF/lib/repackaged-appengine-jakarta-standard-1.1.2.jar
76% [34/43] WEB-INF/lib/spring-asm-3.1.1.RELEASE.jar
79% [35/43] WEB-INF/lib/datanucleus-core-1.1.5.jar
81% [36/43] WEB-INF/appengine-generated/app.yaml
83% [37/43] pages/list.jsp
86% [38/43] WEB-INF/lib/geronimo-jta_1.1_spec-1.1.1.jar
88% [39/43] WEB-INF/lib/spring-beans-3.1.1.RELEASE.jar
90% [40/43] WEB-INF/lib/jsr107cache-1.1.jar
93% [41/43] WEB-INF/classes/com/mkyong/controller/MovieController.class
95% [42/43] __static__/index.html
97% [43/43] __static__/favicon.ico
download_app completed successfully.
上面的命令将从 GAE,app_id " mkyong-springmvc "下载所有东西(类,库,jsp)到你的计算机文件夹" c:\testing "。
参考
gae (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','//web.archive.org/web/20190226092206/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0')
Eclipse IDE:编辑器中不支持的内容类型
问题
使用 Eclipse Ganymede (3.4),开发 JSF 2.0 web 应用程序。然而,当用 Eclipse web 页面编辑器打开 xhtml 文件时,它会提示一个对话框并说…
Unsupported content type in editor
To associate file extension with a supported content type, please see Content Types Preference Page.
P . S The。xhtml 文件扩展名是在“内容类型”首选项页面中配置和关联的属性。
解决办法
上千次的尝试和错误,但仍然无法摆脱“不支持的内容类型错误,这可能是由 Eclipse IDE 本身引起的吗?放弃吧,干脆升级到最新日食太阳神 (3.6),马上解决问题😃
Note
Not sure what’s the root cause of it, may be i had installed too many plugins, and some caused the conflicts. Just get a clean copy of the Eclipse IDE will get rid of the annoying error message immediately.eclipse (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','//web.archive.org/web/20190301233143/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0')
Eclipse IDE:。xhtml 代码辅助不适用于 JSF 标签
问题
使用 Eclipse Helios (3.6)开发 JSF 2.0 web 应用程序。在里。xhtml 文件,当我按下“ Ctrl + Space 组合键调用 JSF 标签的代码助手时,没有任何提示?看起来 JSF 标签的代码助手在。xhtml 文件扩展名?
解决办法
在 Eclipse 项目中,你必须确保项目支持 WTP 和 JSF 功能。
1.右键单击项目,选择 properties,选择“项目 Faces”,确保“ JavaServer Faces ”被选中。稍后,点击进一步配置… 链接来配置 JSF 功能。
2.创建一个用户库,包括 JSF API 和实现库, jsf-api-xxx.jar 和 jsf-impl-xxx.jar 。这将为您的项目添加 JSF 功能。
3.完成了。xhtml 文件,再次点击" Ctrl +空格键键,现在它提示 JSF 标签代码辅助正确。此外,它增加了 JSF 可视化编辑器的网页编辑器。
为 org . Apache . wicket . util . resource 启用调试消息
问题
在 Wicket 中,当一个 html 页面没有被找到时,它将为 org . Apache . Wicket . util . resource 发出" Enable debug messages,以获得所有被尝试的文件名的列表。想知道如何为 Wicket 资源启用调试消息吗?
Root cause:
org.apache.wicket.markup.MarkupNotFoundException:
Markup of type 'html' for component 'com.mkyong.hello.Hello' not found.
Enable debug messages for org.apache.wicket.util.resource to get a list of all filenames tried.:
[Page class = com.mkyong.hello.Hello, id = 0, version = 0]
at org.apache.wicket.markup.MarkupCache.getMarkupStream(MarkupCache.java:227)
...
解决办法
我不知道如何在“org.apache.wicket.util.resource
”上启用调试,Wicket 错误消息应该更清楚!
或者,您可以在调试模式下启用日志记录,并通过日志文件跟踪 Wicket 如何找到资源。例如,集成 log4j 和 Wicket 。
log4j 输出的示例…
DEBUG MarkupCache:300 - Load markup: cacheKey=com.mkyong.hello.Helloen_US.html
DEBUG ResourceStreamLocator:216 - Attempting to locate resource
'com/mkyong/hello/Hello_en_US.html' on path [folders = [], webapppaths: [/pages/]]
DEBUG ResourceStreamLocator:186 - Attempting to locate resource
'com/mkyong/hello/Hello_en_US.html' using classloader WebappClassLoader
delegate: false
repositories:
/WEB-INF/classes/
...
DEBUG ResourceStreamLocator:216 - Attempting to locate resource
'com/mkyong/hello/Hello.html' on path [folders = [], webapppaths: [/pages/]]
DEBUG ResourceStreamLocator:186 - Attempting to locate resource
'com/mkyong/hello/Hello.html' using classloader WebappClassLoader
delegate: false
repositories:
/WEB-INF/classes/
...
wicket (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','//web.archive.org/web/20190225092432/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0')
在 Quartz 中运行多个作业的示例
在这个例子中,我们展示了如何通过 Quartz APIs、Quartz XML 和 Spring 声明多个 Quartz 作业。在 Quartz 调度框架中,每个作业都将被附加到一个唯一的触发器上,并由调度程序运行。
在 Quartz 中,一个触发器不能用于多个任务。(如果这是错误的,请纠正我。)
1.石英原料药
创建 3 个 Quartz 的作业,JobA,JobB 和 JobC。
package com.mkyong.quartz;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class JobA implements Job {
@Override
public void execute(JobExecutionContext context)
throws JobExecutionException {
System.out.println("Job A is runing");
}
}
package com.mkyong.quartz;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class JobB implements Job {
@Override
public void execute(JobExecutionContext context)
throws JobExecutionException {
System.out.println("Job B is runing");
}
}
package com.mkyong.quartz;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class JobC implements Job {
@Override
public void execute(JobExecutionContext context)
throws JobExecutionException {
System.out.println("Job C is runing");
}
}
使用 Quartz APIs 来声明上述 3 个作业,分配给 3 个特定的触发器并调度它。
package com.mkyong.quartz;
import org.quartz.CronScheduleBuilder;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
public class CronTriggerExample {
public static void main( String[] args ) throws Exception
{
JobKey jobKeyA = new JobKey("jobA", "group1");
JobDetail jobA = JobBuilder.newJob(JobA.class)
.withIdentity(jobKeyA).build();
JobKey jobKeyB = new JobKey("jobB", "group1");
JobDetail jobB = JobBuilder.newJob(JobB.class)
.withIdentity(jobKeyB).build();
JobKey jobKeyC = new JobKey("jobC", "group1");
JobDetail jobC = JobBuilder.newJob(JobC.class)
.withIdentity(jobKeyC).build();
Trigger trigger1 = TriggerBuilder
.newTrigger()
.withIdentity("dummyTriggerName1", "group1")
.withSchedule(
CronScheduleBuilder.cronSchedule("0/5 * * * * ?"))
.build();
Trigger trigger2 = TriggerBuilder
.newTrigger()
.withIdentity("dummyTriggerName2", "group1")
.withSchedule(
CronScheduleBuilder.cronSchedule("0/5 * * * * ?"))
.build();
Trigger trigger3 = TriggerBuilder
.newTrigger()
.withIdentity("dummyTriggerName3", "group1")
.withSchedule(
CronScheduleBuilder.cronSchedule("0/5 * * * * ?"))
.build();
Scheduler scheduler = new StdSchedulerFactory().getScheduler();
scheduler.start();
scheduler.scheduleJob(jobA, trigger1);
scheduler.scheduleJob(jobB, trigger2);
scheduler.scheduleJob(jobC, trigger3);
}
}
输出
Job A is runing //every 5 seconds
Job B is runing
Job C is runing
Job A is runing //every 5 seconds
Job B is runing
Job C is runing
2.Quartz XML 示例
XML 文件中的等效版本。确保“quartz.properties”和“quartz-config.xml”位于项目类路径中。
文件-quartz . properties
org.quartz.scheduler.instanceName = MyScheduler
org.quartz.threadPool.threadCount = 3
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
org.quartz.plugin.jobInitializer.class =org.quartz.plugins.xml.XMLSchedulingDataProcessorPlugin
org.quartz.plugin.jobInitializer.fileNames = quartz-config.xml
org.quartz.plugin.jobInitializer.failOnFileNotFound = true
文件–quartz-config . XML
<?xml version="1.0" encoding="UTF-8"?>
<job-scheduling-data
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.quartz-scheduler.org/xml/JobSchedulingData
http://www.quartz-scheduler.org/xml/job_scheduling_data_1_8.xsd"
version="1.8">
<schedule>
<job>
<name>JobA</name>
<group>GroupDummy</group>
<description>This is Job A</description>
<job-class>com.mkyong.quartz.JobA</job-class>
</job>
<trigger>
<cron>
<name>dummyTriggerNameA</name>
<job-name>JobA</job-name>
<job-group>GroupDummy</job-group>
<!-- It will run every 5 seconds -->
<cron-expression>0/5 * * * * ?</cron-expression>
</cron>
</trigger>
</schedule>
<schedule>
<job>
<name>JobB</name>
<group>GroupDummy</group>
<description>This is Job B</description>
<job-class>com.mkyong.quartz.JobB</job-class>
</job>
<trigger>
<cron>
<name>dummyTriggerNameB</name>
<job-name>JobB</job-name>
<job-group>GroupDummy</job-group>
<!-- It will run every 5 seconds -->
<cron-expression>0/5 * * * * ?</cron-expression>
</cron>
</trigger>
</schedule>
</job-scheduling-data>
文件:web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app ...>
<listener>
<listener-class>
org.quartz.ee.servlet.QuartzInitializerListener
</listener-class>
</listener>
</web-app>
3.春天的例子
春天的对等版本。
package com.mkyong.job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
public class JobA extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext arg0)
throws JobExecutionException {
System.out.println("Job A is runing");
}
}
package com.mkyong.job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
public class JobB extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext arg0)
throws JobExecutionException {
System.out.println("Job B is runing");
}
}
package com.mkyong.job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
public class JobC extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext arg0)
throws JobExecutionException {
System.out.println("Job C is runing");
}
}
文件:Spring-quartz . XML–在 Spring XML bean 配置文件中声明作业和触发器。
<beans
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="jobA" class="com.mkyong.job.JobA" />
<bean id="jobB" class="com.mkyong.job.JobB" />
<bean id="jobC" class="com.mkyong.job.JobC" />
<!-- Quartz Job -->
<bean name="JobA" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass" value="com.mkyong.job.JobA" />
</bean>
<bean name="JobB" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass" value="com.mkyong.job.JobB" />
</bean>
<bean name="JobC" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass" value="com.mkyong.job.JobC" />
</bean>
<!-- Cron Trigger, run every 5 seconds -->
<bean id="cronTriggerJobA"
class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="JobA" />
<property name="cronExpression" value="0/5 * * * * ?" />
</bean>
<bean id="cronTriggerJobB"
class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="JobB" />
<property name="cronExpression" value="0/5 * * * * ?" />
</bean>
<bean id="cronTriggerJobC"
class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="JobC" />
<property name="cronExpression" value="0/5 * * * * ?" />
</bean>
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="cronTriggerJobA" />
<ref bean="cronTriggerJobB" />
<ref bean="cronTriggerJobC" />
</list>
</property>
</bean>
</beans>
运行它
package com.mkyong.common;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) throws Exception {
new ClassPathXmlApplicationContext("Spring-Quartz.xml");
}
}
输出。
INFO: Starting beans in phase 2147483647
Jul 30, 2012 10:38:13 PM org.springframework.scheduling.quartz.SchedulerFactoryBean startScheduler
INFO: Starting Quartz Scheduler now
Job A is runing
Job B is runing
Job C is runing
Job A is runing
Job B is runing
Job C is runing
下载源代码
Download it – Multiple-Jobs-in-Quartz-Spring-Example.zip (25kb)
参考
fast Json–将 Java 对象转换成 JSON 或从 JSON 转换过来
FastJson 提供了简单的 API 来将 Java 对象与 Json 相互转换
JSON.toJSONString
–Java 对象到 JSONJSON.parseObject
–JSON 到 Java 对象JSON.parseArray
–Java 对象列表的 JSON 数组
Note
You may have interest to read this How to parse JSON with Jackson
总的来说,FastJson 非常简单,很容易将 Json 转换成对象,但是,它缺乏直接的File
支持,尤其是JSON.parseArray
方法,它需要额外的努力来读取 JSON 文件。希望将来像parseObject
和parseArray
这样的 API 能够直接支持像File
或URL
这样的源。
PS 用 FastJson 1.2.57 测试
pom.xml
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.57</version>
</dependency>
1.波乔
一个简单的 POJO,用于 JSON 转换。
Staff.java
package com.mkyong;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
public class Staff {
private String name;
private int age;
private String[] position;
private List<String> skills;
private Map<String, BigDecimal> salary;
//getters, setters, toString, constructor
}
2.JSON 的 Java 对象
FastJsonExample1.java
package com.mkyong;
import com.alibaba.fastjson.JSON;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
public class FastJsonExample1 {
public static void main(String[] args) {
Staff staff = createStaff();
// Java objects to JSON
String json = JSON.toJSONString(staff);
System.out.println(json);
// Java objects to JSON, pretty-print
String json2 = JSON.toJSONString(staff, true);
System.out.println(json2);
// Java objects to JSON, with formatted date
String json3 = JSON.toJSONStringWithDateFormat(staff, "dd/MM/yyyy HH:mm:ss");
System.out.println(json3);
// List of Java objects to JSON Array
List<Staff> list = Arrays.asList(createStaff(), createStaff());
String json4 = JSON.toJSONStringWithDateFormat(list, "dd/MM/yyyy HH:mm:ss");
System.out.println(json4);
try {
// can't find fastjson api to write files, np, just use the standard java.nio Files.write
Files.write(Paths.get("c:\\projects\\staff.json"), json4.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
private static Staff createStaff() {
Staff staff = new Staff();
staff.setName("mkyong");
staff.setAge(38);
staff.setPosition(new String[]{"Founder", "CTO", "Writer"});
Map<String, BigDecimal> salary = new HashMap() {{
put("2010", new BigDecimal(10000));
put("2012", new BigDecimal(12000));
put("2018", new BigDecimal(14000));
}};
staff.setSalary(salary);
staff.setSkills(Arrays.asList("java", "python", "node", "kotlin"));
staff.setJoinDate(new Date());
return staff;
}
}
输出
// json
{"age":38,"joinDate":1556870430099,"name":"mkyong","position":["Founder","CTO","Writer"],
"salary":{"2018":14000,"2012":12000,"2010":10000},"skills":["java","python","node","kotlin"]}
// json2
{
"age":38,
"joinDate":1556870430099,
"name":"mkyong",
"position":["Founder","CTO","Writer"],
"salary":{
"2018":14000,
"2012":12000,
"2010":10000
},
"skills":[
"java",
"python",
"node",
"kotlin"
]
}
// json3 - format date
{"age":38,"joinDate":"03/05/2019 16:00:30","name":"mkyong","position":["Founder","CTO","Writer"],
"salary":{"2018":14000,"2012":12000,"2010":10000},"skills":["java","python","node","kotlin"]}
// json4 - JSON Array
[
{
"age":38,
"joinDate":1556870630615,
"name":"mkyong",
"position":["Founder","CTO","Writer"],
"salary":{
"2018":14000,
"2012":12000,
"2010":10000
},
"skills":[
"java",
"python",
"node",
"kotlin"
]
},
{
"age":38,
"joinDate":1556870630615,
"name":"mkyong",
"position":["Founder","CTO","Writer"],
"salary":{
"2018":14000,
"2012":12000,
"2010":10000
},
"skills":[
"java",
"python",
"node",
"kotlin"
]
}
]
3.JSON 到 Java 对象
FastJsonExample2.java
package com.mkyong;
import com.alibaba.fastjson.JSON;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class FastJsonExample2 {
public static void main(String[] args) {
// JSON string to Java object
String jsonString = "{\"name\":38,\"name\":\"mkyong\"}";
Staff staff = JSON.parseObject(jsonString, Staff.class);
System.out.println(staff);
// JSON array to Java object
String jsonArray = "[{\"name\":38,\"name\":\"mkyong\"}, {\"name\":39,\"name\":\"mkyong2\"}]";
List<Staff> staff1 = JSON.parseArray(jsonArray, Staff.class);
System.out.println(staff1);
// JSON array in File to Java object
// staff.json contain JSON array
try (Stream<String> lines = Files.lines(Paths.get("c:\\projects\\staff.json"))) {
String content = lines.collect(Collectors.joining());
// Hope parseArray() will support File or Reader in future.
List<Staff> list = JSON.parseArray(content, Staff.class);
System.out.println(list);
} catch (IOException e) {
e.printStackTrace();
}
}
}
参考
- FastJson 主页
- FastJson 用户指南
- FastJson 数据绑定示例
- FastJson 最佳实践(中文)
- 【FastJson 为什么这么快?(中文)
- FastJson 流 API
使用 Java 中的 SFTP 进行文件传输(JSch)
本文展示了如何使用 Java 中的 SSH 文件传输协议(SFTP) 在远程服务器和本地系统之间进行文件传输。
PS 用JSch 0 . 1 . 55测试
1.JSch 依赖性
pom.xml
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.55</version>
</dependency>
2.文件传输–JSch 示例
2.1 在JSch
中,我们可以使用put
和get
在服务器之间进行文件传输。
我们使用put
将文件从本地系统传输到远程服务器。
channelSftp.put(localFile, remoteFile);
我们使用get
将文件从远程服务器下载到本地系统。
channelSftp.get(remoteFile, localFile);
2.2 密码认证。
JSch jsch = new JSch();
jsch.setKnownHosts("/home/mkyong/.ssh/known_hosts");
jschSession = jsch.getSession(USERNAME, REMOTE_HOST, REMOTE_PORT);
jschSession.setPassword(PASSWORD);
2.3 公钥和私钥认证,阅读此使用 SSH 的公钥认证
- 本地私钥-
/home/mkyong/.ssh/id_rsa
- 远程公钥-
~/.ssh/authorized_keys
JSch jsch = new JSch();
jsch.setKnownHosts("/home/mkyong/.ssh/known_hosts");
jschSession = jsch.getSession(USERNAME, REMOTE_HOST, REMOTE_PORT);
jsch.addIdentity("/home/mkyong/.ssh/id_rsa");
2.4 查看这个完整的JSch
示例,将文件从本地系统传输到远程服务器1.2.3.4
,使用 SSH 密码进行身份验证。
SFTPFileTransfer.java
package com.mkyong.io.howto;
import com.jcraft.jsch.*;
public class SFTPFileTransfer {
private static final String REMOTE_HOST = "1.2.3.4";
private static final String USERNAME = "";
private static final String PASSWORD = "";
private static final int REMOTE_PORT = 22;
private static final int SESSION_TIMEOUT = 10000;
private static final int CHANNEL_TIMEOUT = 5000;
public static void main(String[] args) {
String localFile = "/home/mkyong/local/random.txt";
String remoteFile = "/home/mkyong/remote/afile.txt";
Session jschSession = null;
try {
JSch jsch = new JSch();
jsch.setKnownHosts("/home/mkyong/.ssh/known_hosts");
jschSession = jsch.getSession(USERNAME, REMOTE_HOST, REMOTE_PORT);
// authenticate using private key
// jsch.addIdentity("/home/mkyong/.ssh/id_rsa");
// authenticate using password
jschSession.setPassword(PASSWORD);
// 10 seconds session timeout
jschSession.connect(SESSION_TIMEOUT);
Channel sftp = jschSession.openChannel("sftp");
// 5 seconds timeout
sftp.connect(CHANNEL_TIMEOUT);
ChannelSftp channelSftp = (ChannelSftp) sftp;
// transfer file from local to remote server
channelSftp.put(localFile, remoteFile);
// download file from remote server to local
// channelSftp.get(remoteFile, localFile);
channelSftp.exit();
} catch (JSchException | SftpException e) {
e.printStackTrace();
} finally {
if (jschSession != null) {
jschSession.disconnect();
}
}
System.out.println("Done");
}
}
3.JSch 异常
一些常见的例外。
3.1 对于 UnknownHostKey 异常,将远程 IP 地址添加到known_hosts
文件中。
Terminal
$ ssh-keyscan -t rsa 1.2.3.4 >> ~/.ssh/known_hosts
3.2 对于无效的私钥,将私钥转换为另一种格式。
Terminal
$ ssh-keygen -p -f ~/.ssh/id_rsa -m pem
3.3 对于Auth fail
,确保提供的密码正确。
Terminal
com.jcraft.jsch.JSchException: Auth fail
at com.jcraft.jsch.Session.connect(Session.java:519)
at com.mkyong.io.howto.SFTPFileTransfer.main(SFTPFileTransfer.java:34)
下载源代码
$ git 克隆https://github.com/mkyong/core-java
$ cd java-io
参考
GAE:如何将日志信息输出到文件中
默认情况下,所有日志记录消息都将输出到日志控制台。要更改日志设置,找到这个文件—{ Google App Engine SDK directory } \ Google \ App Engine \ tools `dev_appserver_main.py`。
File:dev _ appserver _ main . py–查找以下模式
#...
import getopt
import logging
import os
import signal
import sys
import tempfile
import traceback
logging.basicConfig(
level=logging.INFO,
format='%(levelname)-8s %(asctime)s %(filename)s:%(lineno)s] %(message)s')
#...
输出到文件
为了将日志信息输出到一个文件中,我们可以在dev_appserver_main.py
中更改日志记录的配置,如下所示:
#...
import getopt
import logging
import os
import signal
import sys
import tempfile
import traceback
# default , comment out
#logging.basicConfig(
# level=logging.INFO,
# format='%(levelname)-8s %(asctime)s %(filename)s:%(lineno)s] %(message)s')
# new log settings , output to a file
logging.basicConfig(
filename='/Users/lokjack/gae.log',
filemode='a',
level=logging.DEBUG,
format='%(levelname)-8s %(asctime)s %(filename)s:%(lineno)s] %(message)s')
#...
更改dev_appserver_main.py
后重启dev_appserver.py
。
现在,日志控制台不会显示任何日志消息,而是将所有日志消息输出到一个文件中(在本例中,所有日志消息都将输出到" /Users/lokjack/gae.log ")。
Note
This hacks only works on local GAE development environment.
下载源代码
Download it – gae-logging-to-file.zip (11 kb)
参考
- 在 Python 中记录到文件
- Google App Engine 允许在服务器上创建文件和文件夹吗?
gae logs (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','//web.archive.org/web/20190226091841/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0')
杰克洛克
Co-founder of penefit.com.
GAE+Java——整合谷歌用户账户
在本教程中,我们将向您展示如何通过 Google Java SDKUserService类将 Google 用户帐户集成到 GAE + Java 项目中。
使用的工具:
- JDK 1.6
- Eclipse 3.7+Eclipse 的 Google 插件
- 谷歌应用引擎 Java SDK 1.6.3.1
1.GAE 用户服务示例
如果用户使用他们的 Google 帐户登录,显示一条欢迎消息和一个“ Logout 链接;否则显示一个“登录链接。
package com.mkyong.user;
import java.io.IOException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.google.appengine.api.users.User;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;
@SuppressWarnings("serial")
public class LoginExampleServlet extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
UserService userService = UserServiceFactory.getUserService();
User user = userService.getCurrentUser();
resp.setContentType("text/html");
resp.getWriter().println("<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script><h2>GAE - Integrating Google user account</h2>");
if (user != null) {
resp.getWriter().println("Welcome, " + user.getNickname());
resp.getWriter().println(
"<a href='"
+ userService.createLogoutURL(req.getRequestURI())
+ "'> LogOut </a>");
} else {
resp.getWriter().println(
"Please <a href='"
+ userService.createLoginURL(req.getRequestURI())
+ "'> LogIn </a>");
}
}
}
Note
Both login or logout page are handle by GAE automatically, but the workflow is different :
- 在本地运行-它将模拟谷歌帐户登录页面(无密码认证)。
- 在 GAE 上运行-它将重定向到实际的谷歌帐户登录屏幕。
2.在本地运行
右键单击项目并作为“Web 应用程序”运行。默认情况下,它在 post 8888 上运行。
图 2.1 :访问 URL:http://localhost:8888/log in example
图 2.2 :一个模拟谷歌的登录界面,输入一些东西,没有认证。
图 2.3 :欢迎使用,显示注销链接。
3.部署在 GAE
使用应用程序 ID“mkyong-Java”部署 Google App Engine。
图 3.1–访问网址:http://mkyong-java.appspot.com/loginexample
图 3.2–重定向至实际的 Google 帐户登录屏幕。
图 3.3–如果登录成功,重定向回 http://mkyong-java.appspot.com/loginexample
下载源代码
由于文件很大,所有 GAE SDK 依赖库都被排除在外。
Download it – GAE-UserService-LoginExample.zip (8 KB)
参考
- 使用用户服务
- 用户 Java API
- GAE 用户服务 JavaDoc
- GAE Java hello world 示例
gae java (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','//web.archive.org/web/20190226114540/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0')
GAE+JSF:javax . naming . initial context 是一个受限类
问题
使用 Google App Engine (1.6.3)开发 JSF (2.1.7) web 应用程序时,出现以下错误消息:
java.lang.NoClassDefFoundError: javax.naming.InitialContext is a restricted class.
Please see the Google App Engine developer's guide for more details.
at com.google.appengine.tools.development.agent.runtime.Runtime.reject(Runtime.java:51)
at com.sun.faces.config.WebConfiguration.processJndiEntries(WebConfiguration.java:687)
at com.sun.faces.config.WebConfiguration.<init>(WebConfiguration.java:134)
at com.sun.faces.config.WebConfiguration.getInstance(WebConfiguration.java:194)
at com.sun.faces.config.ConfigureListener.contextInitialized(ConfigureListener.java:163)
//...
解决办法
谷歌应用引擎不支持 Java clsss-javax.naming.InitialContext
,但 JSF 正在WebConfiguration.java
中使用它。获得 JSF 源代码的副本,审查方法—processJndiEntries()
和canProcessJndiEntries()
。
文件:WebConfiguration.java
private void processJndiEntries(String contextName) {
Context initialContext = null;
try {
initialContext = new InitialContext();
} catch (NamingException ne) {
if (LOGGER.isLoggable(Level.WARNING)) {
LOGGER.log(Level.WARNING, ne.toString(), ne);
}
}
if (initialContext != null) {
// process environment entries
for (WebEnvironmentEntry entry : WebEnvironmentEntry.values()) {
String entryName = entry.getQualifiedName();
String value = null;
try {
value = (String) initialContext.lookup(entryName);
} catch (NamingException root) {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.fine(root.toString());
}
}
if (value != null) {
if (LOGGER.isLoggable(Level.INFO)) {
// special logic for ClientStateSavingPassword
if (!entry
.equals(WebEnvironmentEntry.ClientStateSavingPassword)) {
if (LOGGER
.isLoggable(loggingLevel)) {
LOGGER.log(loggingLevel,
"jsf.config.webconfig.enventryinfo",
new Object[]{contextName,
entryName,
value});
}
} else {
if (LOGGER
.isLoggable(loggingLevel)) {
LOGGER.log(loggingLevel,
"jsf.config.webconfig.enventry.clientencrypt",
contextName);
}
}
}
envEntries.put(entry, value);
}
}
}
}
private boolean canProcessJndiEntries() {
try {
Util.getCurrentLoader(this).loadClass("javax.naming.InitialContext");
} catch (Exception e) {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.fine(
"javax.naming is unavailable.
JNDI entries related to Mojarra configuration will not be processed.");
}
return false;
}
return true;
}
以上两种方法与 Google App Engine 不兼容。
要解决这个问题,创建一个新的WebConfiguration.java
文件,注释上面的方法,并将这个文件放在您的源文件夹中,这样它将重载原来的 JSF WebConfiguration.java
。这是一个临时的解决方案,但它很有效,希望 GAE 团队或 JSF 团队将在未来的版本中解决这个问题。
文件:见 WebConfiguration.java 全文
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package com.sun.faces.config;
import com.sun.faces.lifecycle.HttpMethodRestrictionsPhaseListener;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import javax.faces.application.ResourceHandler;
import javax.faces.application.ViewHandler;
import javax.faces.application.StateManager;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletContext;
import com.sun.faces.util.FacesLogger;
import com.sun.faces.util.Util;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import javax.faces.FactoryFinder;
import javax.faces.component.UIInput;
import javax.faces.event.PhaseListener;
import javax.faces.lifecycle.Lifecycle;
import javax.faces.lifecycle.LifecycleFactory;
import javax.faces.validator.BeanValidator;
import javax.faces.view.facelets.ResourceResolver;
/** Class Documentation */
public class WebConfiguration {
// Log instance for this class
private static final Logger LOGGER = FacesLogger.CONFIG.getLogger();
// A Simple regular expression of allowable boolean values
private static final Pattern ALLOWABLE_BOOLEANS =
Pattern.compile("true|false", Pattern.CASE_INSENSITIVE);
// Key under which we store our WebConfiguration instance.
private static final String WEB_CONFIG_KEY =
"com.sun.faces.config.WebConfiguration";
// Logging level. Defaults to FINE
private Level loggingLevel = Level.FINE;
private Map<BooleanWebContextInitParameter, Boolean> booleanContextParameters =
new EnumMap<BooleanWebContextInitParameter, Boolean>(BooleanWebContextInitParameter.class);
private Map<WebContextInitParameter, String> contextParameters =
new EnumMap<WebContextInitParameter, String>(WebContextInitParameter.class);
private Map<WebContextInitParameter, Map<String, String>> facesConfigParameters =
new EnumMap<WebContextInitParameter, Map<String, String>>(WebContextInitParameter.class);
private Map<WebEnvironmentEntry, String> envEntries =
new EnumMap<WebEnvironmentEntry, String>(WebEnvironmentEntry.class);
private Map<WebContextInitParameter, String []> cachedListParams;
private Set<String> setParams = new HashSet<String>();
private ServletContext servletContext;
private ArrayList<DeferredLoggingAction> deferredLoggingActions;
private FaceletsConfiguration faceletsConfig;
// ------------------------------------------------------------ Constructors
private WebConfiguration(ServletContext servletContext) {
this.servletContext = servletContext;
String contextName = getServletContextName();
initSetList(servletContext);
processBooleanParameters(servletContext, contextName);
processInitParameters(servletContext, contextName);
if (canProcessJndiEntries()) {
processJndiEntries(contextName);
}
// build the cache of list type params
cachedListParams = new HashMap<WebContextInitParameter, String []>(3);
getOptionValue(WebContextInitParameter.ResourceExcludes, " ");
getOptionValue(WebContextInitParameter.DefaultSuffix, " ");
getOptionValue(WebContextInitParameter.FaceletsViewMappings, ";");
}
// ---------------------------------------------------------- Public Methods
/**
* Return the WebConfiguration instance for this application passing
* the result of FacesContext.getCurrentInstance().getExternalContext()
* to {@link #getInstance(javax.faces.context.ExternalContext)}.
* @return the WebConfiguration for this application or <code>null</code>
* if no FacesContext is available.
*/
public static WebConfiguration getInstance() {
FacesContext facesContext = FacesContext.getCurrentInstance();
return getInstance(facesContext.getExternalContext());
}
/**
* Return the WebConfiguration instance for this application.
* @param extContext the ExternalContext for this request
* @return the WebConfiguration for this application
*/
public static WebConfiguration getInstance(ExternalContext extContext) {
WebConfiguration config = (WebConfiguration) extContext.getApplicationMap()
.get(WEB_CONFIG_KEY);
if (config == null) {
return getInstance((ServletContext) extContext.getContext());
} else {
return config;
}
}
/**
* Return the WebConfiguration instance for this application.
* @param servletContext the ServletContext
* @return the WebConfiguration for this application or <code>null</code>
* if no WebConfiguration could be located
*/
public static WebConfiguration getInstance(ServletContext servletContext) {
WebConfiguration webConfig = (WebConfiguration)
servletContext.getAttribute(WEB_CONFIG_KEY);
if (webConfig == null) {
webConfig = new WebConfiguration(servletContext);
servletContext.setAttribute(WEB_CONFIG_KEY, webConfig);
}
return webConfig;
}
/**
* @return The <code>ServletContext</code> originally used to construct
* this WebConfiguration instance
*/
public ServletContext getServletContext() {
return servletContext;
}
/**
* Obtain the value of the specified boolean parameter
* @param param the parameter of interest
* @return the value of the specified boolean parameter
*/
public boolean isOptionEnabled(BooleanWebContextInitParameter param) {
if (booleanContextParameters.get(param) != null) {
return booleanContextParameters.get(param);
} else {
return param.getDefaultValue();
}
}
/**
* Obtain the value of the specified parameter
* @param param the parameter of interest
* @return the value of the specified parameter
*/
public String getOptionValue(WebContextInitParameter param) {
String result = contextParameters.get(param);
if (null == result) {
WebContextInitParameter alternate = param.getAlternate();
if (null != alternate) {
result = contextParameters.get(alternate);
}
}
return result;
}
public FaceletsConfiguration getFaceletsConfiguration() {
if (null == faceletsConfig) {
faceletsConfig = new FaceletsConfiguration(this);
}
return faceletsConfig;
}
public Map<String, String> getFacesConfigOptionValue(WebContextInitParameter param, boolean create) {
Map<String, String> result = null;
assert(null != facesConfigParameters);
result = facesConfigParameters.get(param);
if (null == result) {
if (create) {
result = new ConcurrentHashMap<String, String>(3);
facesConfigParameters.put(param, result);
} else {
result = Collections.emptyMap();
}
}
return result;
}
public Map<String, String> getFacesConfigOptionValue(WebContextInitParameter param) {
return getFacesConfigOptionValue(param, false);
}
public String[] getOptionValue(WebContextInitParameter param, String sep) {
String [] result;
assert(null != cachedListParams);
if (null == (result = cachedListParams.get(param))) {
String value = getOptionValue(param);
if (null == value) {
result = new String[0];
} else {
result = Util.split(value, sep);
}
cachedListParams.put(param, result);
}
return result;
}
/**
* Obtain the value of the specified env-entry
* @param entry the env-entry of interest
* @return the value of the specified env-entry
*/
public String getEnvironmentEntry(WebEnvironmentEntry entry) {
return envEntries.get(entry);
}
/**
* @param param the init parameter of interest
* @return <code>true</code> if the parameter was explicitly set,
* otherwise, <code>false</code>
*/
public boolean isSet(WebContextInitParameter param) {
return isSet(param.getQualifiedName());
}
/**
* @param param the init parameter of interest
* @return <code>true</code> if the parameter was explicitly set,
* otherwise, <code>false</code>
*/
public boolean isSet(BooleanWebContextInitParameter param) {
return isSet(param.getQualifiedName());
}
/**
* @return the name of this application
*/
public String getServletContextName() {
if (servletContext.getMajorVersion() == 2
&& servletContext.getMinorVersion() <= 4) {
return servletContext.getServletContextName();
} else {
return servletContext.getContextPath();
}
}
public void overrideContextInitParameter(BooleanWebContextInitParameter param, boolean value) {
if (param == null) {
return;
}
boolean oldVal = booleanContextParameters.put(param, value);
if (LOGGER.isLoggable(Level.FINE) && oldVal != value) {
LOGGER.log(Level.FINE,
"Overriding init parameter {0}. Changing from {1} to {2}.",
new Object[] { param.getQualifiedName(), oldVal, value});
}
}
public void overrideContextInitParameter(WebContextInitParameter param, String value) {
if (param == null || value == null || value.length() == 0) {
return;
}
value = value.trim();
String oldVal = contextParameters.put(param, value);
cachedListParams.remove(param);
if (oldVal != null) {
if (LOGGER.isLoggable(Level.FINE) && !(oldVal.equals(value))) {
LOGGER.log(Level.FINE,
"Overriding init parameter {0}. Changing from {1} to {2}.",
new Object[]{param.getQualifiedName(),
oldVal,
value});
}
}
}
public void doPostBringupActions() {
if (deferredLoggingActions != null) {
for (DeferredLoggingAction loggingAction : deferredLoggingActions) {
loggingAction.log();
}
}
// add the HttpMethodRestrictionPhaseListener if the parameter is enabled.
boolean enabled = this.isOptionEnabled(BooleanWebContextInitParameter.EnableHttpMethodRestrictionPhaseListener);
if (enabled) {
LifecycleFactory factory = (LifecycleFactory) FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
Iterator<String> ids = factory.getLifecycleIds();
PhaseListener listener = null;
Lifecycle cur;
while (ids.hasNext()) {
cur = factory.getLifecycle(ids.next());
boolean foundExistingListenerInstance = false;
for (PhaseListener curListener : cur.getPhaseListeners()) {
if (curListener instanceof HttpMethodRestrictionsPhaseListener) {
foundExistingListenerInstance = true;
break;
}
}
if (!foundExistingListenerInstance) {
if (null == listener) {
listener = new HttpMethodRestrictionsPhaseListener();
}
cur.addPhaseListener(listener);
}
}
}
}
// ------------------------------------------------- Package Private Methods
static void clear(ServletContext servletContext) {
servletContext.removeAttribute(WEB_CONFIG_KEY);
}
// --------------------------------------------------------- Private Methods
/**
* <p>Is the configured value valid against the default boolean pattern.</p>
* @param param the boolean parameter
* @param value the configured value
* @return <code>true</code> if the value is valid,
* otherwise <code>false</code>
*/
private boolean isValueValid(BooleanWebContextInitParameter param,
String value) {
if (!ALLOWABLE_BOOLEANS.matcher(value).matches()) {
if (LOGGER.isLoggable(Level.WARNING)) {
LOGGER.log(Level.WARNING,
"jsf.config.webconfig.boolconfig.invalidvalue",
new Object[]{
value,
param.getQualifiedName(),
"true|false",
"true|false",
new Boolean(param.getDefaultValue()).toString()
});
}
return false;
}
return true;
}
/**
* <p>Process all boolean context initialization parameters.</p>
* @param servletContext the ServletContext of interest
* @param contextName the context name
*/
private void processBooleanParameters(ServletContext servletContext,
String contextName) {
// process boolean contxt parameters
for (BooleanWebContextInitParameter param : BooleanWebContextInitParameter
.values()) {
String strValue =
servletContext.getInitParameter(param.getQualifiedName());
boolean value;
if (strValue != null && strValue.length() > 0 && param.isDeprecated()) {
BooleanWebContextInitParameter alternate = param.getAlternate();
if (LOGGER.isLoggable(Level.WARNING)) {
if (alternate != null) {
queueLoggingAction(new DeferredBooleanParameterLoggingAction(
param,
Level.WARNING,
"jsf.config.webconfig.param.deprecated",
new Object[]{
contextName,
param.getQualifiedName(),
alternate.getQualifiedName()}));
} else {
queueLoggingAction(new DeferredBooleanParameterLoggingAction(
param,
Level.WARNING,
"jsf.config.webconfig.param.deprecated.no_replacement",
new Object[]{
contextName,
param.getQualifiedName()}));
}
}
if (alternate != null) {
if (isValueValid(param, strValue)) {
value = Boolean.valueOf(strValue);
} else {
value = param.getDefaultValue();
}
if (LOGGER.isLoggable(Level.INFO) && alternate != null) {
queueLoggingAction(new DeferredBooleanParameterLoggingAction(
param,
Level.INFO,
((value)
? "jsf.config.webconfig.configinfo.reset.enabled"
: "jsf.config.webconfig.configinfo.reset.disabled"),
new Object[]{
contextName,
alternate.getQualifiedName()}));
}
booleanContextParameters.put(alternate, value);
}
continue;
}
if (!param.isDeprecated()) {
if (strValue == null) {
value = param.getDefaultValue();
} else {
if (isValueValid(param, strValue)) {
value = Boolean.valueOf(strValue);
} else {
value = param.getDefaultValue();
}
}
// first param processed should be
// com.sun.faces.displayConfiguration
if (BooleanWebContextInitParameter.DisplayConfiguration.equals(param) && value) {
loggingLevel = Level.INFO;
}
if (LOGGER.isLoggable(loggingLevel)) {
LOGGER.log(loggingLevel,
((value)
? "jsf.config.webconfig.boolconfiginfo.enabled"
: "jsf.config.webconfig.boolconfiginfo.disabled"),
new Object[]{contextName,
param.getQualifiedName()});
}
booleanContextParameters.put(param, value);
}
}
}
/**
* Adds all com.sun.faces init parameter names to a list. This allows
* callers to determine if a parameter was explicitly set.
* @param servletContext the ServletContext of interest
*/
private void initSetList(ServletContext servletContext) {
for (Enumeration e = servletContext.getInitParameterNames();
e.hasMoreElements(); ) {
String name = e.nextElement().toString();
if (name.startsWith("com.sun.faces") ||
name.startsWith("javax.faces")) {
setParams.add(name);
}
}
}
/**
* @param name the param name
* @return <code>true</code> if the name was explicitly specified
*/
private boolean isSet(String name) {
return setParams.contains(name);
}
/**
* <p>Process all non-boolean context initialization parameters.</p>
* @param servletContext the ServletContext of interest
* @param contextName the context name
*/
private void processInitParameters(ServletContext servletContext,
String contextName) {
for (WebContextInitParameter param : WebContextInitParameter.values()) {
String value =
servletContext.getInitParameter(param.getQualifiedName());
if (value != null && value.length() > 0 && param.isDeprecated()) {
WebContextInitParameter alternate = param.getAlternate();
DeprecationLoggingStrategy strategy = param.getDeprecationLoggingStrategy();
if (strategy == null || strategy.shouldBeLogged(this)) {
if (LOGGER.isLoggable(Level.WARNING)) {
if (alternate != null) {
queueLoggingAction(new DeferredParameterLoggingAction(param, Level.WARNING,
"jsf.config.webconfig.param.deprecated",
new Object[]{
contextName,
param.getQualifiedName(),
alternate.getQualifiedName()}));
} else {
queueLoggingAction(new DeferredParameterLoggingAction(
param,
Level.WARNING,
"jsf.config.webconfig.param.deprecated.no_replacement",
new Object[]{
contextName,
param.getQualifiedName()}));
}
}
}
if (alternate != null) {
queueLoggingAction(
new DeferredParameterLoggingAction(param,
Level.INFO,
"jsf.config.webconfig.configinfo.reset",
new Object[]{
contextName,
alternate.getQualifiedName(),
value}));
contextParameters.put(alternate, value);
}
continue;
}
if ((value == null || value.length() == 0) && !param.isDeprecated()) {
value = param.getDefaultValue();
}
if (value == null || value.length() == 0) {
continue;
}
if (value.length() > 0) {
if (LOGGER.isLoggable(loggingLevel)) {
LOGGER.log(loggingLevel,
"jsf.config.webconfig.configinfo",
new Object[]{contextName,
param.getQualifiedName(),
value});
}
contextParameters.put(param, value);
} else {
if (LOGGER.isLoggable(loggingLevel)) {
LOGGER.log(loggingLevel,
"jsf.config.webconfig.option.notconfigured",
new Object[]{contextName,
param.getQualifiedName()});
}
}
}
}
/**
* <p>Process all JNDI entries.</p>
* @param contextName the context name
*/
private void processJndiEntries(String contextName) {
/* incompatible with Google App Engine
*
* Context initialContext = null;
try {
initialContext = new InitialContext();
} catch (NamingException ne) {
if (LOGGER.isLoggable(Level.WARNING)) {
LOGGER.log(Level.WARNING, ne.toString(), ne);
}
}
if (initialContext != null) {
// process environment entries
for (WebEnvironmentEntry entry : WebEnvironmentEntry.values()) {
String entryName = entry.getQualifiedName();
String value = null;
try {
value = (String) initialContext.lookup(entryName);
} catch (NamingException root) {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.fine(root.toString());
}
}
if (value != null) {
if (LOGGER.isLoggable(Level.INFO)) {
// special logic for ClientStateSavingPassword
if (!entry
.equals(WebEnvironmentEntry.ClientStateSavingPassword)) {
if (LOGGER
.isLoggable(loggingLevel)) {
LOGGER.log(loggingLevel,
"jsf.config.webconfig.enventryinfo",
new Object[]{contextName,
entryName,
value});
}
} else {
if (LOGGER
.isLoggable(loggingLevel)) {
LOGGER.log(loggingLevel,
"jsf.config.webconfig.enventry.clientencrypt",
contextName);
}
}
}
envEntries.put(entry, value);
}
}
}
*/
}
private boolean canProcessJndiEntries() {
/* incompatible with Google App Engine
* try {
Util.getCurrentLoader(this).loadClass("javax.naming.InitialContext");
} catch (Exception e) {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.fine(
"javax.naming is unavailable. JNDI entries related to Mojarra configuration will not be processed.");
}
return false;
}
return true;*/
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.fine(
"javax.naming is unavailable. JNDI entries related to Mojarra configuration will not be processed.");
}
return false;
}
private void queueLoggingAction(DeferredLoggingAction loggingAction) {
if (deferredLoggingActions == null) {
deferredLoggingActions = new ArrayList<DeferredLoggingAction>();
}
deferredLoggingActions.add(loggingAction);
}
// ------------------------------------------------------------------- Enums
/**
* <p>An <code>enum</code> of all non-boolean context initalization parameters
* recognized by the implementation.</p>
*/
public enum WebContextInitParameter {
// implementation note:
// if a parameter is to be deprecated,
// then the <name>Deprecated enum element
// *must* appear after the one that is taking
// its place. The reporting logic depends on this
ManagedBeanFactoryDecorator(
"com.sun.faces.managedBeanFactoryDecoratorClass",
""
),
StateSavingMethod(
"javax.faces.STATE_SAVING_METHOD",
"server"
),
FaceletsSuffix(
ViewHandler.FACELETS_SUFFIX_PARAM_NAME,
ViewHandler.DEFAULT_FACELETS_SUFFIX
),
DefaultSuffix(
ViewHandler.DEFAULT_SUFFIX_PARAM_NAME,
ViewHandler.DEFAULT_SUFFIX
),
JavaxFacesConfigFiles(
"javax.faces.CONFIG_FILES",
""
),
JavaxFacesProjectStage(
"javax.faces.PROJECT_STAGE",
"Production"
),
AlternateLifecycleId(
"javax.faces.LIFECYCLE_ID",
""
),
ResourceExcludes(
ResourceHandler.RESOURCE_EXCLUDES_PARAM_NAME,
ResourceHandler.RESOURCE_EXCLUDES_DEFAULT_VALUE + " .groovy"
),
NumberOfViews(
"com.sun.faces.numberOfViewsInSession",
"15"
),
NumberOfViewsDeprecated(
"com.sun.faces.NUMBER_OF_VIEWS_IN_SESSION",
"15",
true,
NumberOfViews
),
NumberOfLogicalViews(
"com.sun.faces.numberOfLogicalViews",
"15"
),
NumberOfLogicalViewsDeprecated(
"com.sun.faces.NUMBER_OF_VIEWS_IN_LOGICAL_VIEW_IN_SESSION",
"15",
true,
NumberOfLogicalViews
),
NumberOfConcurrentFlashUsers(
"com.sun.faces.numberOfConcerrentFlashUsers",
"5000"
),
NumberOfFlashesBetweenFlashReapings(
"com.sun.faces.numberOfFlashesBetweenFlashReapings",
"5000"
),
InjectionProviderClass(
"com.sun.faces.injectionProvider",
""
),
SerializationProviderClass(
"com.sun.faces.serializationProvider",
""
),
ResponseBufferSize(
"com.sun.faces.responseBufferSize",
"1024"
),
FaceletsBufferSize(
"javax.faces.FACELETS_BUFFER_SIZE",
"1024"
),
FaceletsBufferSizeDeprecated(
"facelets.BUFFER_SIZE",
"1024",
true,
FaceletsBufferSize,
new FaceletsConfigParamLoggingStrategy()
),
ClientStateWriteBufferSize(
"com.sun.faces.clientStateWriteBufferSize",
"8192"
),
ResourceBufferSize(
"com.sun.faces.resourceBufferSize",
"2048"
),
ExpressionFactory(
"com.sun.faces.expressionFactory",
"com.sun.el.ExpressionFactoryImpl"
),
ClientStateTimeout(
"com.sun.faces.clientStateTimeout",
""
),
DefaultResourceMaxAge(
"com.sun.faces.defaultResourceMaxAge",
"604800000" // 7 days
),
ResourceUpdateCheckPeriod(
"com.sun.faces.resourceUpdateCheckPeriod",
"5" // in minutes
),
CompressableMimeTypes(
"com.sun.faces.compressableMimeTypes",
""
),
DisableUnicodeEscaping(
"com.sun.faces.disableUnicodeEscaping",
"auto"
),
FaceletsDefaultRefreshPeriod(
"javax.faces.FACELETS_REFRESH_PERIOD",
"2"
),
FaceletsDefaultRefreshPeriodDeprecated(
"facelets.REFRESH_PERIOD",
"2",
true,
FaceletsDefaultRefreshPeriod,
new FaceletsConfigParamLoggingStrategy()
),
FaceletsResourceResolver(
ResourceResolver.FACELETS_RESOURCE_RESOLVER_PARAM_NAME,
""
),
FaceletsResourceResolverDeprecated(
"facelets.RESOURCE_RESOLVER",
"",
true,
FaceletsResourceResolver,
new FaceletsConfigParamLoggingStrategy()
),
FaceletsViewMappings(
ViewHandler.FACELETS_VIEW_MAPPINGS_PARAM_NAME,
""
),
FaceletsViewMappingsDeprecated(
"facelets.VIEW_MAPPINGS",
"",
true,
FaceletsViewMappings,
new FaceletsConfigParamLoggingStrategy()
),
FaceletsLibraries(
"javax.faces.FACELETS_LIBRARIES",
""
),
FaceletsLibrariesDeprecated(
"facelets.LIBRARIES",
"",
true,
FaceletsLibraries,
new FaceletsConfigParamLoggingStrategy()
),
FaceletsDecorators(
"javax.faces.FACELETS_DECORATORS",
""
),
FaceletsDecoratorsDeprecated(
"facelets.DECORATORS",
"",
true,
FaceletsDecorators,
new FaceletsConfigParamLoggingStrategy()
),
DuplicateJARPattern(
"com.sun.faces.duplicateJARPattern",
""
),
ValidateEmptyFields(
UIInput.VALIDATE_EMPTY_FIELDS_PARAM_NAME,
"auto"
),
FullStateSavingViewIds(
StateManager.FULL_STATE_SAVING_VIEW_IDS_PARAM_NAME,
""
),
AnnotationScanPackages(
"com.sun.faces.annotationScanPackages",
""
),
FaceletFactory(
"com.sun.faces.faceletFactory",
""
),
FaceletCache(
"com.sun.faces.faceletCache",
""
),
FaceletsProcessingFileExtensionProcessAs(
"",
""
);
private String defaultValue;
private String qualifiedName;
private WebContextInitParameter alternate;
private boolean deprecated;
private DeprecationLoggingStrategy loggingStrategy;
// ---------------------------------------------------------- Public Methods
public String getDefaultValue() {
return defaultValue;
}
public String getQualifiedName() {
return qualifiedName;
}
DeprecationLoggingStrategy getDeprecationLoggingStrategy() {
return loggingStrategy;
}
// ------------------------------------------------- Package Private Methods
WebContextInitParameter(String qualifiedName,
String defaultValue) {
this(qualifiedName, defaultValue, false, null);
}
WebContextInitParameter(String qualifiedName,
String defaultValue,
boolean deprecated,
WebContextInitParameter alternate) {
this.qualifiedName = qualifiedName;
this.defaultValue = defaultValue;
this.deprecated = deprecated;
this.alternate = alternate;
}
WebContextInitParameter(String qualifiedName,
String defaultValue,
boolean deprecated,
WebContextInitParameter alternate,
DeprecationLoggingStrategy loggingStrategy) {
this(qualifiedName, defaultValue, deprecated, alternate);
this.loggingStrategy = loggingStrategy;
}
// --------------------------------------------------------- Private Methods
private WebContextInitParameter getAlternate() {
return alternate;
}
private boolean isDeprecated() {
return deprecated;
}
}
/**
* <p>An <code>enum</code> of all boolean context initalization parameters
* recognized by the implementation.</p>
*/
public enum BooleanWebContextInitParameter {
// implementation note:
// if a parameter is to be deprecated,
// then the <name>Deprecated enum element
// *must* appear after the one that is taking
// its place. The reporting logic depends on this
DisplayConfiguration(
"com.sun.faces.displayConfiguration",
false
),
ValidateFacesConfigFiles(
"com.sun.faces.validateXml",
false
),
VerifyFacesConfigObjects(
"com.sun.faces.verifyObjects",
false
),
ForceLoadFacesConfigFiles(
"com.sun.faces.forceLoadConfiguration",
false
),
DisableArtifactVersioning(
"com.sun.faces.disableVersionTracking",
false,
true,
null
),
EnableHtmlTagLibraryValidator(
"com.sun.faces.enableHtmlTagLibValidator",
false
),
EnableCoreTagLibraryValidator(
"com.sun.faces.enableCoreTagLibValidator",
false
),
PreferXHTMLContentType(
"com.sun.faces.preferXHTML",
false
),
PreferXHTMLContextTypeDeprecated(
"com.sun.faces.PreferXHTML",
false,
true,
PreferXHTMLContentType
),
CompressViewState(
"com.sun.faces.compressViewState",
true
),
CompressViewStateDeprecated(
"com.sun.faces.COMPRESS_STATE",
true,
true,
CompressViewState
),
CompressJavaScript(
"com.sun.faces.compressJavaScript",
true
),
ExternalizeJavaScriptDeprecated(
"com.sun.faces.externalizeJavaScript",
true,
true,
null
),
SendPoweredByHeader(
"com.sun.faces.sendPoweredByHeader",
true
),
EnableJSStyleHiding(
"com.sun.faces.enableJSStyleHiding",
false
),
EnableScriptInAttributeValue(
"com.sun.faces.enableScriptsInAttributeValues",
true
),
WriteStateAtFormEnd(
"com.sun.faces.writeStateAtFormEnd",
true
),
EnableLazyBeanValidation(
"com.sun.faces.enableLazyBeanValidation",
true
),
EnableLoadBundle11Compatibility(
"com.sun.faces.enabledLoadBundle11Compatibility",
false
),
EnableRestoreView11Compatibility(
"com.sun.faces.enableRestoreView11Compatibility",
false
),
SerializeServerState(
"com.sun.faces.serializeServerState",
false
),
EnableViewStateIdRendering(
"com.sun.faces.enableViewStateIdRendering",
true
),
RegisterConverterPropertyEditors(
"com.sun.faces.registerConverterPropertyEditors",
false
),
EnableGroovyScripting(
"com.sun.faces.enableGroovyScripting",
false
),
DisableFaceletJSFViewHandler(
"javax.faces.DISABLE_FACELET_JSF_VIEWHANDLER",
false
),
DisableDefaultBeanValidator(
BeanValidator.DISABLE_DEFAULT_BEAN_VALIDATOR_PARAM_NAME,
false),
DateTimeConverterUsesSystemTimezone(
"javax.faces.DATETIMECONVERTER_DEFAULT_TIMEZONE_IS_SYSTEM_TIMEZONE",
false
),
EnableHttpMethodRestrictionPhaseListener(
"com.sun.faces.ENABLE_HTTP_METHOD_RESTRICTION_PHASE_LISTENER",
false
),
FaceletsSkipComments(
"javax.faces.FACELETS_SKIP_COMMENTS",
false
),
FaceletsSkipCommentsDeprecated(
"facelets.SKIP_COMMENTS",
false,
true,
FaceletsSkipComments,
new FaceletsConfigParamLoggingStrategy()
),
PartialStateSaving(
StateManager.PARTIAL_STATE_SAVING_PARAM_NAME,
true
),
GenerateUniqueServerStateIds(
"com.sun.faces.generateUniqueServerStateIds",
true
),
AutoCompleteOffOnViewState(
"com.sun.faces.autoCompleteOffOnViewState",
true
),
EnableThreading(
"com.sun.faces.enableThreading",
false
),
AllowTextChildren(
"com.sun.faces.allowTextChildren",
false
),
CacheResourceModificationTimestamp(
"com.sun.faces.cacheResourceModificationTimestamp",
false
),
EnableAgressiveSessionDirtying(
"com.sun.faces.enableAgressiveSessionDirtying",
false
),
EnableMissingResourceLibraryDetection(
"com.sun.faces.enableMissingResourceLibraryDetection",
false
);
private BooleanWebContextInitParameter alternate;
private String qualifiedName;
private boolean defaultValue;
private boolean deprecated;
private DeprecationLoggingStrategy loggingStrategy;
// ---------------------------------------------------------- Public Methods
public boolean getDefaultValue() {
return defaultValue;
}
public String getQualifiedName() {
return qualifiedName;
}
DeprecationLoggingStrategy getDeprecationLoggingStrategy() {
return loggingStrategy;
}
// ------------------------------------------------- Package Private Methods
BooleanWebContextInitParameter(String qualifiedName,
boolean defaultValue) {
this(qualifiedName, defaultValue, false, null);
}
BooleanWebContextInitParameter(String qualifiedName,
boolean defaultValue,
boolean deprecated,
BooleanWebContextInitParameter alternate) {
this.qualifiedName = qualifiedName;
this.defaultValue = defaultValue;
this.deprecated = deprecated;
this.alternate = alternate;
}
BooleanWebContextInitParameter(String qualifiedName,
boolean defaultValue,
boolean deprecated,
BooleanWebContextInitParameter alternate,
DeprecationLoggingStrategy loggingStrategy) {
this(qualifiedName, defaultValue, deprecated, alternate);
this.loggingStrategy = loggingStrategy;
}
// --------------------------------------------------------- Private Methods
private BooleanWebContextInitParameter getAlternate() {
return alternate;
}
private boolean isDeprecated() {
return deprecated;
}
}
/**
* <p>An <code>enum</code> of all environment entries (specified in the
* web.xml) recognized by the implemenetation.</p>
*/
public enum WebEnvironmentEntry {
ClientStateSavingPassword("ClientStateSavingPassword"),
ProjectStage(javax.faces.application.ProjectStage.PROJECT_STAGE_JNDI_NAME);
private static final String JNDI_PREFIX = "java:comp/env/";
private String qualifiedName;
// ---------------------------------------------------------- Public Methods
public String getQualifiedName() {
return qualifiedName;
}
// ------------------------------------------------- Package Private Methods
WebEnvironmentEntry(String qualifiedName) {
if (qualifiedName.startsWith(JNDI_PREFIX)) {
this.qualifiedName = qualifiedName;
} else {
this.qualifiedName = JNDI_PREFIX + qualifiedName;
}
}
}
/**
* <p>An <code>enum</code> of all possible values for the <code>disableUnicodeEscaping</code>
* configuration parameter.</p>
*/
public enum DisableUnicodeEscaping {
True("true"),
False("false"),
Auto("auto");
private final String value;
DisableUnicodeEscaping(String value) {
this.value = value;
}
public static DisableUnicodeEscaping getByValue(String value)
{
for (DisableUnicodeEscaping disableUnicodeEscaping : DisableUnicodeEscaping.values()) {
if (disableUnicodeEscaping.value.equals(value)) {
return disableUnicodeEscaping;
}
}
return null;
}
}
// ----------------------------------------------------------- Inner Classes
private interface DeprecationLoggingStrategy {
boolean shouldBeLogged(WebConfiguration configuration);
}
private static class FaceletsConfigParamLoggingStrategy implements DeprecationLoggingStrategy {
public boolean shouldBeLogged(WebConfiguration configuration) {
return !configuration.isOptionEnabled(BooleanWebContextInitParameter.DisableFaceletJSFViewHandler);
}
} // END FaceletsConfigParamLoggingStrategy
private interface DeferredLoggingAction {
void log();
} // END DeferredLogginAction
private class DeferredParameterLoggingAction implements DeferredLoggingAction {
private WebContextInitParameter parameter;
private Level loggingLevel;
private String logKey;
private Object[] params;
DeferredParameterLoggingAction(WebContextInitParameter parameter,
Level loggingLevel,
String logKey,
Object[] params) {
this.parameter = parameter;
this.loggingLevel = loggingLevel;
this.logKey = logKey;
this.params = params;
}
public void log() {
if (WebConfiguration.LOGGER.isLoggable(loggingLevel)) {
DeprecationLoggingStrategy strategy = parameter.getDeprecationLoggingStrategy();
if (strategy != null && strategy.shouldBeLogged(WebConfiguration.this)) {
WebConfiguration.LOGGER.log(loggingLevel, logKey, params);
}
}
}
} // END DeferredParameterLogginAction
private class DeferredBooleanParameterLoggingAction implements DeferredLoggingAction {
private BooleanWebContextInitParameter parameter;
private Level loggingLevel;
private String logKey;
private Object[] params;
DeferredBooleanParameterLoggingAction(BooleanWebContextInitParameter parameter,
Level loggingLevel,
String logKey,
Object[] params) {
this.parameter = parameter;
this.loggingLevel = loggingLevel;
this.logKey = logKey;
this.params = params;
}
public void log() {
if (WebConfiguration.LOGGER.isLoggable(loggingLevel)) {
DeprecationLoggingStrategy strategy = parameter.getDeprecationLoggingStrategy();
if (strategy != null && strategy.shouldBeLogged(WebConfiguration.this)) {
WebConfiguration.LOGGER.log(loggingLevel, logKey, params);
}
}
}
} // END DeferredBooleanParameterLoggingAction
} // END WebConfiguration
Credit : Source code is copied from here ## 参考
GAE + JSF:无法实例化 expression factory“com . sun . El . expression factory impl”
问题
JSF 应用程序能够部署在本地 GAE 环境中,开发环境如下:
- JSF 2.1.7
- 谷歌应用引擎 SDK 1.6.3
但是在 GAE 生产环境中部署时出现错误消息。下面是 GAE 记录的错误消息。
com.sun.faces.config.ConfigureListener installExpressionFactory: Unable to instantiate ExpressionFactory 'com.sun.el.ExpressionFactoryImpl'
E 2012-04-24 03:37:37.989
com.sun.faces.config.ConfigureListener contextInitialized: Critical error during deployment:
com.sun.faces.config.ConfigurationException:
It appears the JSP version of the container is older than 2.1 and unable to locate the EL RI expression factory,
com.sun.el.ExpressionFactoryImpl. If not using JSP or the EL RI, make sure the context initialization parameter,
com.sun.faces.expressionFactory, is properly set.
at com.sun.faces.config.ConfigureListener.registerELResolverAndListenerWithJsp(ConfigureListener.java:662)
at com.sun.faces.config.ConfigureListener.contextInitialized(ConfigureListener.java:243)
at org.mortbay.jetty.handler.ContextHandler.startContext(ContextHandler.java:548)
at org.mortbay.jetty.servlet.Context.startContext(Context.java:136)
at org.mortbay.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1250)
at org.mortbay.jetty.handler.ContextHandler.doStart(ContextHandler.java:517)
at org.mortbay.jetty.webapp.WebAppContext.doStart(WebAppContext.java:467)
at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:50)
at com.google.apphosting.runtime.jetty.AppVersionHandlerMap.createHandler(AppVersionHandlerMap.java:202)
at com.google.apphosting.runtime.jetty.AppVersionHandlerMap.getHandler(AppVersionHandlerMap.java:171)
at com.google.apphosting.runtime.jetty.JettyServletEngineAdapter.serviceRequest(JettyServletEngineAdapter.java:123)
at com.google.apphosting.runtime.JavaRuntime$RequestRunnable.run(JavaRuntime.java:446)
at com.google.tracing.TraceContext$TraceContextRunnable.runInContext(TraceContext.java:449)
at com.google.tracing.TraceContext$TraceContextRunnable$1.run(TraceContext.java:455)
at com.google.tracing.TraceContext.runInContext(TraceContext.java:695)
at com.google.tracing.TraceContext$AbstractTraceContextCallback.runInInheritedContextNoUnref(TraceContext.java:333)
at com.google.tracing.TraceContext$AbstractTraceContextCallback.runInInheritedContext(TraceContext.java:325)
at com.google.tracing.TraceContext$TraceContextRunnable.run(TraceContext.java:453)
at com.google.apphosting.runtime.ThreadGroupPool$PoolEntry.run(ThreadGroupPool.java:251)
at java.lang.Thread.run(Thread.java:679)
解决办法
GAE + JSF 需要 el-ri.jar 。要解决它,找到并获取 el-ri.jar ,并将其复制到您的“ /war/lib ”文件夹中。
Where to get el-ri.jar?
The faster way is get the el-ri.jar from Maven repository. ## 参考
GAE + JSF:无法恢复 View /hello.xhtml
问题
部署在 GAE 生产环境中,当从一个页面/视图导航到另一个页面/视图时,GAE 显示错误消息“视图 xxx 无法恢复”?
- JSF 2.1.7
- 谷歌应用引擎 SDK 1.6.3
在 GAE 当地发展没有问题。
解决办法
默认情况下,JSF 2 使用服务器进行会话管理,GAE 不支持(在 1.6.3 中测试)。要解决这个问题,更新 web.xml 文件,定义“javax.faces.STATE_SAVING_METHOD
,把值设为“客户端”。
文件:web.xml
<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>client</param-value>
</context-param>
参考
Mac OS X 上的 GAE + Python hello world
在本教程中,我们将向您展示如何在 Mac OS X 上使用 Python 创建一个简单的 GAE hello world web 项目,并通过 Google App Engine Launcher 运行它。
使用的工具:
- 用于 Python 的谷歌应用引擎 SDK(Mac OS X)-1 . 7 . 0
- 苹果 OS X 10.8
- Python 2.7
Note
By default, Mac OS X 10.8, has Python 2.7 installed, which makes Google App Engine development more easier.
1.谷歌应用引擎 SDK
访问这个Google App Engine SDK for Python,选择 Mac OS X 并开始下载。
1.1 安装 Google App Engine SDK
双击下载的GoogleAppEngineLauncher-version.dmg
文件,会提取出“ GoogleAppEngineLauncher ”图标,拖到你希望 GAE SDK 安装的文件夹中。
1.2 再次运行 Google App Engine Launcher
,双击“ GoogleAppEngineLauncher ”图标,按照向导提示完成安装。
图:Google appengine launcher–这个 GAE 启动器帮助你运行、部署和管理你的应用程序。
2.Python Hello World
File:hello . py–创建一个简单的 python 文件来显示 hello world 消息。
import webapp2
class MainPage(webapp2.RequestHandler):
def get(self):
self.response.headers['Content-Type'] = 'text/plain'
self.response.out.write('Hello World, GAE + Python')
app = webapp2.WSGIApplication([('/', MainPage)], debug=True)
文件:app . YAML–创建一个简单的 GAE 配置文件。
application: helloworld
version: 1
runtime: python27
api_version: 1
threadsafe: true
handlers:
- url: /.*
script: hello.app
完成了。
3.导入、运行和演示
在 GAE 启动程序中,两个手指点击表格网格->选择“添加已有… ”,找到上面包含 Python 文件的文件夹。
运行它并点击浏览以查看部署的 web 应用程序。
下载源代码
Download it – gae-python-hello-world.zip (3 kb)
参考
在 Spring Security 中获取当前登录的用户名
在本文中,我们将向您展示在 Spring Security 中获取当前登录用户名的三种方法。
1.security context holder+authentic ation . getname()
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class LoginController {
@RequestMapping(value="/login", method = RequestMethod.GET)
public String printUser(ModelMap model) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String name = auth.getName(); //get logged in username
model.addAttribute("username", name);
return "hello";
}
//...
2.security context holder+user . get username()
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class LoginController {
@RequestMapping(value="/login", method = RequestMethod.GET)
public String printUser(ModelMap model) {
User user = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
String name = user.getUsername(); //get logged in username
model.addAttribute("username", name);
return "hello";
}
//...
3.usernamepasswordtauthenticationtoken
这是更优雅的解决方案,在运行时,Spring 会将UsernamePasswordAuthenticationToken
注入到Principal
接口中。
import java.security.Principal;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class LoginController {
@RequestMapping(value="/login", method = RequestMethod.GET)
public String printWelcome(ModelMap model, Principal principal ) {
String name = principal.getName(); //get logged in username
model.addAttribute("username", name);
return "hello";
}
//...
下载源代码
Download it – Spring-Security-Get-Logged-In-Username.zip (9 KB)
参考
Tags : spring security
相关文章
使用 Eclipse 的 Google app engine Java hello world 示例
在本教程中,我们将向您展示如何使用 Eclipse 创建一个Google App Engine(GAE)Java项目(hello world 示例),在本地运行它,并将其部署到 Google App Engine 帐户。
使用的工具:
- JDK 1.6
- Eclipse 3.7+Eclipse 的 Google 插件
- 谷歌应用引擎 Java SDK 1.6.3.1
Note
GAE supports Java 1.5 and 1.6.
P.S 假设安装了 JDK1.6 和 Eclipse 3.7。
1.为 Eclipse 安装谷歌插件
阅读本指南-如何为 Eclipse 安装谷歌插件。如果你同时安装了 Google App Engine Java SDK 和“ Google Plugin for Eclipse ”,那么转到步骤 2,否则,获取 Google App Engine Java SDK 并解压。
2.创建新的 Web 应用程序项目
在 Eclipse 工具栏中,点击 Google 图标,选择“新建 Web 应用项目… ”
图-新的 Web 应用项目
图–取消选择“ Google Web ToolKit ”,通过“ configure SDK ”链接链接您的 GAE Java SDK。
单击 finished,Google Plugin for Eclipse 将自动生成一个示例项目。
3.你好世界
查看生成的项目目录。
没什么特别的,标准的 Java web 项目结构。
HelloWorld/
src/
...Java source code...
META-INF/
...other configuration...
war/
...JSPs, images, data files...
WEB-INF/
...app configuration...
lib/
...JARs for libraries...
classes/
...compiled classes...
额外的是这个文件“appengine-web.xml
”,Google App Engine 需要这个来运行和部署应用程序。
文件:appengine-web.xml
<?xml version="1.0" encoding="utf-8"?>
<appengine-web-app >
<application></application>
<version>1</version>
<!-- Configure java.util.logging -->
<system-properties>
<property name="java.util.logging.config.file" value="WEB-INF/logging.properties"/>
</system-properties>
</appengine-web-app>
4.在本地运行
右键单击项目并作为“ Web 应用程序”运行。
Eclipse 控制台:
//...
INFO: The server is running at http://localhost:8888/
30 Mac 2012 11:13:01 PM com.google.appengine.tools.development.DevAppServerImpl start
INFO: The admin console is running at http://localhost:8888/_ah/admin
访问 URL http://localhost:8888/
,参见输出
还有 hello world servlet–http://localhost:8888/helloworld
5.部署到 Google 应用引擎
在https://appengine.google.com/上注册一个帐户,并为您的网络应用程序创建一个应用程序 ID。
在本演示中,我创建了一个名为“mkyong123”的应用程序 ID,并将其放在appengine-web.xml
中。
文件:appengine-web.xml
<?xml version="1.0" encoding="utf-8"?>
<appengine-web-app >
<application>mkyong123</application>
<version>1</version>
<!-- Configure java.util.logging -->
<system-properties>
<property name="java.util.logging.config.file" value="WEB-INF/logging.properties"/>
</system-properties>
</appengine-web-app>
要部署,请参见以下步骤:
图 1.1–点击工具栏上的 GAE 部署按钮。
图 1.2–使用您的 Google 帐户登录并点击部署按钮。
图 1.3–如果一切正常,hello world web 应用程序将被部署到这个 URL——http://mkyong123.appspot.com/
完成了。
参考
谷歌应用引擎+ JDO + Spring MVC,CRUD 例子
Note
This tutorial is more on practice guide, please refer to this official Using JDO in datastore for detail explanation.
参见下面的代码片段,使用 Java 数据对象(JDO) 在 GAE 数据存储上执行 CRUD。只需用 JDO 注释对客户进行注释,并通过 PersistenceManager 执行 CRUD。
增加
Customer c = new Customer();
c.setName(name);
PersistenceManager pm = PMF.get().getPersistenceManager();
try {
pm.makePersistent(c);
} finally {
pm.close();
}
搜索
搜索“客户”,其中 name = =“mkyong”。
PersistenceManager pm = PMF.get().getPersistenceManager();
Query q = pm.newQuery(Customer.class);
q.setFilter("name == nameParameter");
q.setOrdering("date desc");
q.declareParameters("String nameParameter");
try {
List<Customer> results = (List<Customer>) q.execute("mkyong");
//...
} finally {
q.closeAll();
pm.close();
}
搜索“客户”,其中 name = =“mkyong”,email = =“test @ Gmail . com”。
Query q = pm.newQuery(Customer.class);
q.setOrdering("date desc");
q.setFilter("name == nameParameter && email == emailParameter");
q.declareParameters("String nameParameter, String emailParameter");
try {
List<Customer> results = (List<Customer>) q.execute("mkyong", "test@gmail.com");
//...
} finally {
q.closeAll();
pm.close();
}
客户记录返回列表。
PersistenceManager pm = PMF.get().getPersistenceManager();
Query q = pm.newQuery(Customer.class);
q.setOrdering("date desc");
List<Customer> results = null;
try {
results = (List<Customer>) q.execute();
if (!results.isEmpty()) {
// good for listing
}
} finally {
q.closeAll();
pm.close();
}
更新
若要更新,请获取现有对象并修改它。
PersistenceManager pm = PMF.get().getPersistenceManager();
try {
Customer c = pm.getObjectById(Customer.class, key);
c.setName(name);
c.setEmail(email);
c.setDate(new Date());
} finally {
pm.close();
}
删除
PersistenceManager pm = PMF.get().getPersistenceManager();
try {
Customer c = pm.getObjectById(Customer.class, key);
pm.deletePersistent(c);
} finally {
pm.close();
}
GAE + Spring MVC + CRUD 示例
好了,现在我们将向您展示一个使用 Spring MVC 以 REST 风格开发的简单 web 应用程序,它使用 JDO 在 datastore 中存储数据。
- 谷歌应用引擎 Java SDK 1.6.3.1,JDO 2.3
- 弹簧 3.1.1
- JDK 1.6
- Eclipse 3.7+Eclipse 的 Google 插件
另外,使用 Google Plugin for Eclipse 创建一个 web 应用程序项目模板,它会自动为您创建和配置jdoconfig.xml
。
Note
This example is keep into as simple as possible, to show you how to use JDO to perform CRUD only, no layers like DAO or BO, no validation or message notification of the success or failed action.
1.顾客
用 JDO 注释来注释客户对象。
package com.mkyong.model;
import java.util.Date;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;
import com.google.appengine.api.datastore.Key;
@PersistenceCapable
public class Customer {
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
@Persistent
private String name;
@Persistent
private String email;
@Persistent
private Date date;
//getter and setter methods
}
2.持久性管理器
创建一个 singleton PersistenceManager 类。
package com.mkyong;
import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManagerFactory;
public final class PMF {
private static final PersistenceManagerFactory pmfInstance = JDOHelper
.getPersistenceManagerFactory("transactions-optional");
private PMF() {
}
public static PersistenceManagerFactory get() {
return pmfInstance;
}
}
3.弹簧控制器
弹簧控制器,REST 风格,执行 CRUD 操作。代码应该是不言自明的。
文件:CustomerController.java
package com.mkyong.controller;
import java.util.Date;
import java.util.List;
import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import com.mkyong.PMF;
import com.mkyong.model.Customer;
@Controller
@RequestMapping("/customer")
public class CustomerController {
@RequestMapping(value = "/add", method = RequestMethod.GET)
public String getAddCustomerPage(ModelMap model) {
return "add";
}
@RequestMapping(value = "/add", method = RequestMethod.POST)
public ModelAndView add(HttpServletRequest request, ModelMap model) {
String name = request.getParameter("name");
String email = request.getParameter("email");
Customer c = new Customer();
c.setName(name);
c.setEmail(email);
c.setDate(new Date());
PersistenceManager pm = PMF.get().getPersistenceManager();
try {
pm.makePersistent(c);
} finally {
pm.close();
}
return new ModelAndView("redirect:list");
}
@RequestMapping(value = "/update/{name}", method = RequestMethod.GET)
public String getUpdateCustomerPage(@PathVariable String name,
HttpServletRequest request, ModelMap model) {
PersistenceManager pm = PMF.get().getPersistenceManager();
Query q = pm.newQuery(Customer.class);
q.setFilter("name == nameParameter");
q.setOrdering("date desc");
q.declareParameters("String nameParameter");
try {
List<Customer> results = (List<Customer>) q.execute(name);
if (results.isEmpty()) {
model.addAttribute("customer", null);
} else {
model.addAttribute("customer", results.get(0));
}
} finally {
q.closeAll();
pm.close();
}
return "update";
}
@RequestMapping(value = "/update", method = RequestMethod.POST)
public ModelAndView update(HttpServletRequest request, ModelMap model) {
String name = request.getParameter("name");
String email = request.getParameter("email");
String key = request.getParameter("key");
PersistenceManager pm = PMF.get().getPersistenceManager();
try {
Customer c = pm.getObjectById(Customer.class, key);
c.setName(name);
c.setEmail(email);
c.setDate(new Date());
} finally {
pm.close();
}
// return to list
return new ModelAndView("redirect:list");
}
@RequestMapping(value = "/delete/{key}", method = RequestMethod.GET)
public ModelAndView delete(@PathVariable String key,
HttpServletRequest request, ModelMap model) {
PersistenceManager pm = PMF.get().getPersistenceManager();
try {
Customer c = pm.getObjectById(Customer.class, key);
pm.deletePersistent(c);
} finally {
pm.close();
}
// return to list
return new ModelAndView("redirect:../list");
}
// get all customers
@RequestMapping(value = "/list", method = RequestMethod.GET)
public String listCustomer(ModelMap model) {
PersistenceManager pm = PMF.get().getPersistenceManager();
Query q = pm.newQuery(Customer.class);
q.setOrdering("date desc");
List<Customer> results = null;
try {
results = (List<Customer>) q.execute();
if (results.isEmpty()) {
model.addAttribute("customerList", null);
} else {
model.addAttribute("customerList", results);
}
} finally {
q.closeAll();
pm.close();
}
return "list";
}
}
4.JSP 页面
显示客户并执行添加、更新和删除的 JSP 页面。
文件:list.jsp
<%@ page import="java.util.List" %>
<%@ page import="com.mkyong.model.Customer" %>
<%@ page import="com.google.appengine.api.datastore.KeyFactory" %>
<html>
<body>
<h1>GAE + Spring 3 MVC REST + CRUD Example with JDO</h1>
Function : <a href="add">Add Customer</a>
<hr />
<h2>All Customers</h2>
<table border="1">
<thead>
<tr>
<td>Name</td>
<td>Email</td>
<td>Created Date</td>
<td>Action</td>
</tr>
</thead>
<%
if(request.getAttribute("customerList")!=null){
List<Customer> customers =
(List<Customer>)request.getAttribute("customerList");
if(!customers.isEmpty()){
for(Customer c : customers){
%>
<tr>
<td><%=c.getName() %></td>
<td><%=c.getEmail() %></td>
<td><%=c.getDate() %></td>
<td><a href="update/<%=c.getName()%>">Update</a> |
<a href="delete/<%=KeyFactory.keyToString(c.getKey()) %>">
Delete</a>
</td>
</tr>
<%
}
}
}
%>
</tr>
</table>
</body>
</html>
文件:add.jsp
<html>
<body>
<h1>Add Customer</h1>
<form method="post" action="add">
<table>
<tr>
<td>UserName :</td>
<td><input type="text" style="width: 185px;" maxlength="30"
name="name" id="name" /></span></td>
</tr>
<tr>
<td>Email :</td>
<td><input type="text" style="width: 185px;" maxlength="30"
name="email" id="email" /></span></td>
</tr>
</table>
<input type="submit" class="save" title="Save" value="Save" />
</form>
</body>
</html>
文件:update.jsp
<%@ page import="com.mkyong.model.Customer" %>
<%@ page import="com.google.appengine.api.datastore.KeyFactory" %>
<html>
<body>
<h1>Update Customer</h1>
<%
Customer customer = new Customer();
if(request.getAttribute("customer")!=null){
customer = (Customer)request.getAttribute("customer");
}
%>
<form method="post" action="../update" >
<input type="hidden" name="key" id="key"
value="<%=KeyFactory.keyToString(customer.getKey()) %>" />
<table>
<tr>
<td>
UserName :
</td>
<td>
<input type="text" style="width: 185px;"
maxlength="30" name="name" id="name"
value="<%=customer.getName() %>" />
</td>
</tr>
<tr>
<td>
Email :
</td>
<td>
<input type="text" style="width: 185px;"
maxlength="30" name="email" id="email"
value="<%=customer.getEmail() %>" />
</td>
</tr>
</table>
<input type="submit" class="update" title="Update" value="Update" />
</form>
</body>
</html>
5.演示
完成,看 demo 向你展示 web 应用的工作流程。
1.列表页面,显示现有客户列表。
网址:http://localhost:8888/customer/list
2.在列表页面中,点击“添加客户”链接,显示添加客户页面,填写新客户,姓名= " mkyong ",邮箱= "【test@yahoo.com】",点击“添加”按钮。
网址:http://localhost:8888/customer/add
3.保存了客户,就会返回到列表页面。
网址:http://localhost:8888/customer/list
4.尝试更新链接,它将显示所选客户的数据,更新电子邮件至“mkyong@yahoo.com”,并点击更新按钮。
网址:http://localhost:8888/customer/update/mkyong
5.电子邮件被更新,并重定向回列表页面。
网址:http://localhost:8888/customer/list
6.要删除客户,只需点击“删除”链接。
下载源代码
由于文件很大,所有 Spring MVC 和 GAE jar 都被排除在外。
Download – GAE-SpringMVC-JDO-example.zip (22 KB)
参考
谷歌应用引擎+ JSF 2 的例子
在本教程中,我们将向您展示如何在谷歌应用引擎(GAE)环境中开发和部署 JSF 2.0 web 应用程序。
使用的工具和技术:
- JDK 1.6
- Eclipse 3.7+Eclipse 的 Google 插件
- 谷歌应用引擎 Java SDK 1.6.3.1
- JSF 2.1.7
Note
This example is going to reuse this JSF 2.0 hello world example, modify it and merge it with this GAE + Java example.
1.新建 Web 应用程序项目
在 Eclipse 中,创建一个新的 Web 应用项目,命名为“ JSFGoogleAppEngine ”。
“谷歌 Eclipse 插件”将生成一个 GAE 项目结构的样本。
2.JSF 新协议的附属协议
要在 GAE 使用 JSF 2,你需要以下罐子
- jsf-api-2.1.7.jar
- jsf-impl-2.1.7.jar
- 埃尔里-1.0.jar
复制后放入“ war/WEB-INF/lib ”文件夹。
右击项目文件夹,选择“属性”。选择“ Java 构建路径->-库”选项卡,点击“添加 Jars 按钮,选择上面的 Jars。
Note
You need to put this el-ri-1.0.jar
, otherwise, you will hit error message – Unable to instantiate ExpressionFactory ‘com.sun.el.ExpressionFactoryImpl’.
3.JSF 管理比恩
3.1 删除插件生成的JSFGoogleAppEngineServlet.java
,不需要这个。
3.2 创建一个受管理的 bean。
文件:src/com/mkyong/hello bean . Java
package com.mkyong;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import java.io.Serializable;
@ManagedBean
@SessionScoped
public class HelloBean implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
3.3 创建一个新的 WebConfiguration.java。
JSF 2 号正在使用“javax.naming.InitialContext
”这在 GAE 是不支持的。
要解决这个问题,你需要得到一份 JSF 的源代码,克隆出WebConfiguration.java
,注释正在使用javax.naming.InitialContext
类的方法,把它放在src/com/sun/faces/com fig/web configuration . Java。现在,您新创建的WebConfiguration.java
类将重载原来的WebConfiguration.java
。
Note
Get the full source code of the WebConfiguration.java.
我不认为 GAE 团队会白名单这个罐子,只是希望 JSF 的团队可以在未来的版本中修复这个问题。
4.JSF 页面
4.1 创建hello.xhtml
页面,接受用户输入并将其传递给 helloBean。
文件:war/hello.xhtml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html">
<h:head>
<title>GAE + JSF</title>
</h:head>
<h:body>
<h1>Google App Engine + JSF 2.0 example - hello.xhtml</h1>
<h:form>
<h:inputText value="#{helloBean.name}"></h:inputText>
<h:commandButton value="Welcome Me" action="welcome"></h:commandButton>
</h:form>
</h:body>
</html>
4.2 创建welcome.xhtml
页面,显示来自 hellobean 的用户输入。
文件:war/welcome.xhtml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html">
<h:head>
<title>GAE + JSF</title>
</h:head>
<h:body bgcolor="white">
<h1>Google App Engine + JSF 2.0 example - welcome.xhtml</h1>
<h2>Welcome #{helloBean.name}</h2>
</h:body>
</html>
4.3 删除插件生成的index.html
文件,你不需要这个。
5.web.xml
更新 web.xml,集成 JSF 2。
文件:web.xml
<?xml version="1.0" encoding="utf-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<display-name>JavaServerFaces</display-name>
<!-- GAE 1.6.3 cannot handle server side (JSF default) state management. -->
<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>client</param-value>
</context-param>
<!-- Change to "Production" when you are ready to deploy -->
<context-param>
<param-name>javax.faces.PROJECT_STAGE</param-name>
<param-value>Development</param-value>
</context-param>
<!-- Welcome page -->
<welcome-file-list>
<welcome-file>faces/hello.xhtml</welcome-file>
</welcome-file-list>
<!-- JSF mapping -->
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Map these files with JSF -->
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.jsf</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.faces</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.xhtml</url-pattern>
</servlet-mapping>
</web-app>
Note
GAE do not support server side state management, so, you need to define “javax.faces.STATE_SAVING_METHOD
” to “client
“, to avoid of this “View /hello.xhtml could not be restored” error message in GAE production environment.
6.在 GAE 启用会话
更新appengine-web.xml
,启用会话支持,JSF 需要这个。
文件:appengine-web.xml
<?xml version="1.0" encoding="utf-8"?>
<appengine-web-app >
<application></application>
<version>1</version>
<sessions-enabled>true</sessions-enabled>
</appengine-web-app>
7.目录结构
审查最终目录结构。
8.在本地运行
右键单击项目,作为“Web 应用程序”运行。
网址:http://localhost:8888/hello . JSF
点击按钮。
10.部署在 GAE
更新appengine-web.xml
文件,添加您的 App Engine 应用 ID。
文件:appengine-web.xml
<?xml version="1.0" encoding="utf-8"?>
<appengine-web-app >
<application>mkyong-jsf2gae</application>
<version>1</version>
<sessions-enabled>true</sessions-enabled>
</appengine-web-app>
选择项目,点击谷歌图标,“部署到应用引擎”。
网址:http://mkyong-jsf2gae.appspot.com/hello.jsf
下载源代码
由于文件很大,所有 JSF 和 GAE 的罐子都被排除在外。
Download – JSF2-GoogleAppEngine-Example.zip (42 KB)
参考
- JSF 和 GAE 兼容问题
- 谷歌应用上的 JSF 2 配置
- 带 JSF 2 + CDI 的谷歌应用引擎
- 开始使用谷歌应用和 JSF
- JSF 2.0 hello world 示例
- Oracle : JavaServer Faces 开发教程
相关文章
使用 Eclipse 的 Google app engine Python hello world 示例
在本教程中,我们将向您展示如何使用 Eclipse 创建一个Google App Engine(GAE)Pythonweb 项目(hello world 示例),在本地运行它,并将其部署到 Google App Engine 帐户。
使用的工具:
- Python 2.7
- Eclipse 3.7 + PyDev 插件
- 用于 Python 1.6.4 的谷歌应用引擎 SDK
P.S 假设安装了 Python 2.7 和 Eclipse 3.7。
1.为 Eclipse 安装 PyDev 插件
使用下面的 URL 安装 PyDev 作为 Eclipse 插件。
http://pydev.org/updates
图 1——在 Eclipse 菜单中,“帮助—>安装新软件。”并放在上面的网址。选择“ PyDev for Eclipse ”选项,按照步骤操作,并在完成后重启 Eclipse。
2.验证 PyDev
Eclipse 重启后,确保 PyDev 的解释器指向你的python.exe
。
图 2-Eclipse->-Windows->首选项,确保“解释器-Python配置正确。
3.谷歌应用引擎 SDK Python
下载并安装Google App Engine SDK for Python。
4.Eclipse 中的 Python Hello World
下面的步骤向你展示了如何通过 Pydev 插件创建一个 GAE 项目。
图 4.1–Eclipse 菜单,文件- >新建- >其他…,PyDev 文件夹,选择“ PyDev Google App Engine 项目”。
图 4.2–键入项目名称,如果解释器尚未配置(在步骤 2 中),您现在可以这样做。并选择此选项—“创建‘src’文件夹并将其添加到 PYTHONPATH ”。
图 4.3–点击“浏览”按钮,指向 Google App Engine 安装目录(在步骤 3 中)。
图 4.4–在 GAE 命名您的应用程序 id,键入任何内容,您可以稍后更改。并选择“ Hello Webapp World 模板生成示例文件。
图 4.5–完成,生成 4 个文件,.pydevproject
、.project
都是 Eclipse 项目文件,忽略。
查看生成的 Python 文件:
File:helloworld . py——只输出一个 hello world。
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
class MainPage(webapp.RequestHandler):
def get(self):
self.response.headers['Content-Type'] = 'text/plain'
self.response.out.write('Hello, webapp World!')
application = webapp.WSGIApplication([('/', MainPage)], debug=True)
def main():
run_wsgi_app(application)
if __name__ == "__main__":
main()
文件:app . YAML–GAE 需要这个文件来运行和部署你的 Python 项目,它非常简单明了,详细的语法和配置,请访问 yaml 和 app.yaml 参考。
application: mkyong-python
version: 1
runtime: python
api_version: 1
handlers:
- url: /.*
script: helloworld.py
5.在本地运行
要在本地运行它,右击helloworld.py
,选择“运行方式”—>“运行配置”,创建一个新的“ PyDev Google App 运行”。
图 5.1-在主选项卡- >主模块中,手动键入“ dev_appserver.py 的目录路径。“浏览”按钮不能够帮助你,手动输入。
图 5.2–在“参数”选项卡- >程序参数中,填入“ ${project_loc}/src ”。
图 5.3–运行它。默认情况下,它会部署到 http://localhost:8080 。
图 5.4–完成。
5.部署到 Google 应用引擎
在https://appengine.google.com/上注册一个账户,并为你的网络应用创建一个应用 ID。再次回顾“app.yaml
”,这个 web 应用程序将被部署到 GAE,应用程序 ID 为“ mkyong-python ”。
文件:app.yaml
application: mkyong-python
version: 1
runtime: python
api_version: 1
handlers:
- url: /.*
script: helloworld.py
要部署到 GAE,请参见以下步骤:
图 5.1-新建一个“PyDev Google App Run”,在主选项卡- >主模块中,手动键入“ appcfg.py 的目录路径。
图 5.2–在参数选项卡- >程序参数中,放入“更新${project_loc}/src ”。
图 5.3–在部署过程中,您需要输入您的 GAE 电子邮件和密码进行验证。
图 5.4-如果成功,web app 将部署到 http://mkyong-python.appspot.com/。
*
完成了。
参考
- 用于 Eclipse 的 PyDev 插件
- Yaml 官网
- GAE 开始使用 Python
- 为 Eclipse 安装 PyDev】
- GAE Java hello world 使用 Eclipse 的例子
*
Google App Engine + Spring 3 MVC REST 示例
在本教程中,我们将向您展示如何在 Google App Engine()环境中开发和部署一个 Spring 3.0 MVC REST web 应用程序。
**使用的工具和技术:
- 谷歌应用引擎 Java SDK 1.6.3.1
- 弹簧 3.1.1
- JDK 1.6
- Eclipse 3.7+Eclipse 的 Google 插件
Note
This example is going to reuse this Spring 3 MVC REST example, modify it and integrate with Google App Engine, you may also interest to read this – GAE + Java + Eclipse example
1.新建 Web 应用程序项目
在 Eclipse 中,创建一个新的 Web 应用项目,命名为“ SpringMVCGoogleAppEngine ”。
Eclipse 的“ Google 插件将生成一个 GAE 项目结构的样本。
2.Spring 3.0 依赖项
要在 GAE 使用 Spring MVC + REST ,您需要以下 jar
- aopalliance-1.0.jar
- commons-logging-1.1.1.jar
- spring-aop-3.1.1
- spring-asm-3.1.1 .版本. jar
- spring-beans-3.1.1.RELEASE.jar
- spring 上下文 3.1.1.RELEASE.jar
- spring-context-support-3.1.1
- 3.1.1.RELEASE.jar
- spring-表达式-3.1.1.RELEASE.jar
- spring-web-3.1.1.RELEASE.jar
- spring-webmvc-3.1.1
复制后放入“ war/WEB-INF/lib ”文件夹。
将其添加到项目的构建路径中——右键单击项目文件夹,选择“ Properties ”。选择“ Java 构建路径->-库”选项卡,点击“添加 Jars 按钮,选择上面的 Jars。
## 3.弹簧控制器
3.1 删除自动生成的SpringMVCGoogleAppEngineServlet.java
,不需要这个。
3.2 创建一个 bean,在 REST 结构中充当控制器。此外,阿迪的消息进入了“message
”属性。
文件:src/com/mkyong/movie controller . Java
package com.mkyong.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping("/movie")
public class MovieController {
//DI via Spring
String message;
@RequestMapping(value="/{name}", method = RequestMethod.GET)
public String getMovie(@PathVariable String name, ModelMap model) {
model.addAttribute("movie", name);
model.addAttribute("message", this.message);
//return to jsp page, configured in mvc-dispatcher-servlet.xml, view resolver
return "list";
}
public void setMessage(String message) {
this.message = message;
}
}
4.JSP 页面
创建一个list.jsp
页面,显示结果。
文件:war/list.jsp
<html>
<body>
<h1>GAE + Spring 3 MVC REST example</h1>
<h2>Movie : ${movie} , DI message : ${message}</h2>
</body>
</html>
5.弹簧配置
创建一个 Spring XML bean 配置文件,定义 bean 并查看解析器。
文件:war/we b-INF/MVC-dispatcher-servlet . XML
<beans
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<!--
Need DI a message into controller, so auto component scan is disabled,
to avoid double create the movieController bean.
Only controller need this hack.
-->
<context:component-scan base-package="com.mkyong.controller">
<context:exclude-filter type="regex"
expression="com.mkyong.controller.Movie.*" />
</context:component-scan>
<mvc:annotation-driven />
<!-- Bean to show you Di in GAE, via Spring, also init the MovieController -->
<bean class="com.mkyong.controller.MovieController">
<property name="message">
<value>Hello World</value>
</property>
</bean>
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/pages/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
</beans>
6.web.xml
更新web.xml
,集成 Spring 框架。
文件:war/WEB-INF/web.xml
<?xml version="1.0" encoding="utf-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
<servlet>
<servlet-name>mvc-dispatcher</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/mvc-dispatcher-servlet.xml</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>
7.目录结构
查看最终的目录结构。
8.在本地运行
右键点击项目,运行为“ Web 应用”。
网址:http://localhost:8888/movie/Avengers
9.部署在 GAE
更新appengine-web.xml
文件,添加您的 App Engine 应用 ID。
文件:war/we b-INF/app engine-WEB . XML
<?xml version="1.0" encoding="utf-8"?>
<appengine-web-app >
<application>mkyong-springmvc</application>
<version>1</version>
<system-properties>
<property name="java.util.logging.config.file"
value="WEB-INF/logging.properties"/>
</system-properties>
</appengine-web-app>
选择项目,点击谷歌图标,“部署到应用引擎”。
网址:http://mkyong-springmvc.appspot.com/movie/forrest gump
下载源代码
由于文件较大,所有 Spring 和 GAE jar 都被排除在外。
Download – SpringMVC-GoogleAppEngine.zip (12 KB)
参考
- 春天 3.0 豆子参考
- REST 解释维基百科
- Google App Engine+Java+Google Plugin for eclipse 示例
- Spring 3 MVC hello world 示例
- 春天 3 休息你好世界示例
- Google 添加引擎 Java doc
gae rest spring mvc spring rest
Google App Engine + Spring MVC,带有数据存储低级 api 的 CRUD 示例
GAE datastore
Refer to this official “Using datstore guide” to understand what is GAE datastore.
参见以下代码片段,使用低级 API 为 Google App Engine datastore,Java 执行 CRUD。
增加
将客户存储到数据存储中,以“name”作为键。
Key customerKey = KeyFactory.createKey("Customer", "your name");
Entity customer = new Entity("Customer", customerKey);
customer.setProperty("name", "your name");
customer.setProperty("email", "your email");
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
datastore.put(customer); //save it
搜索
返回 10 个客户作为列表。
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
Query query = new Query("Customer").addSort("date", Query.SortDirection.DESCENDING);
List<Entity> customers = datastore.prepare(query).asList(FetchOptions.Builder.withLimit(10));
用匹配的过滤器找到并返回一个客户。
Query query = new Query("Customer");
query.addFilter("name", FilterOperator.EQUAL, "your name");
PreparedQuery pq = datastore.prepare(query);
Entity customer = pq.asSingleEntity();
FilterOperator.EQUAL
Play this filters, it has few condition options, like less than, great than and etc.
更新
要更新,只需修改现有实体并再次保存即可。
Query query = new Query("Customer");
query.addFilter("name", FilterOperator.EQUAL, "your name");
PreparedQuery pq = datastore.prepare(query);
Entity customer = pq.asSingleEntity();
customer.setProperty("name", name);
customer.setProperty("email", email);
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
datastore.put(customer); //GAE will know save or update
删除
要删除,需要实体键。
Query query = new Query("Customer");
query.addFilter("name", FilterOperator.EQUAL, name);
PreparedQuery pq = datastore.prepare(query);
Entity customer = pq.asSingleEntity();
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
datastore.delete(customer.getKey()); //delete it
GAE + Spring MVC + CRUD 示例
好了,现在我们将向您展示一个使用 Spring MVC 以 REST 风格开发的简单 web 应用程序,操作 Google App Engine datastore 中的数据,使用上面的低级 API。
- 谷歌应用引擎 Java SDK 1.6.3.1
- 弹簧 3.1.1
- JDK 1.6
- Eclipse 3.7+Eclipse 的 Google 插件
Note
This example is keep into as simple as possible, to show you how to perform CRUD only, no layers like DAO or BO, no validation or message notification of the success or failed action.
1.弹簧控制器
Spring 控制器,REST 风格,用于显示网页和执行 CRUD。代码应该是不言自明的。
文件:CustomerController.java
package com.mkyong.controller;
import java.util.Date;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.FetchOptions;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.PreparedQuery;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Query.FilterOperator;
@Controller
@RequestMapping("/customer")
public class CustomerController {
@RequestMapping(value="/addCustomerPage", method = RequestMethod.GET)
public String getAddCustomerPage(ModelMap model) {
return "add";
}
@RequestMapping(value="/add", method = RequestMethod.POST)
public ModelAndView add(HttpServletRequest request, ModelMap model) {
String name = request.getParameter("name");
String email = request.getParameter("email");
Key customerKey = KeyFactory.createKey("Customer", name);
Date date = new Date();
Entity customer = new Entity("Customer", customerKey);
customer.setProperty("name", name);
customer.setProperty("email", email);
customer.setProperty("date", date);
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
datastore.put(customer);
return new ModelAndView("redirect:list");
}
@RequestMapping(value="/update/{name}", method = RequestMethod.GET)
public String getUpdateCustomerPage(@PathVariable String name,
HttpServletRequest request, ModelMap model) {
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
Query query = new Query("Customer");
query.addFilter("name", FilterOperator.EQUAL, name);
PreparedQuery pq = datastore.prepare(query);
Entity e = pq.asSingleEntity();
model.addAttribute("customer", e);
return "update";
}
@RequestMapping(value="/update", method = RequestMethod.POST)
public ModelAndView update(HttpServletRequest request, ModelMap model) {
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
String name = request.getParameter("name");
String email = request.getParameter("email");
String originalName = request.getParameter("originalName");
Query query = new Query("Customer");
query.addFilter("name", FilterOperator.EQUAL, originalName);
PreparedQuery pq = datastore.prepare(query);
Entity customer = pq.asSingleEntity();
customer.setProperty("name", name);
customer.setProperty("email", email);
customer.setProperty("date", new Date());
datastore.put(customer);
//return to list
return new ModelAndView("redirect:list");
}
@RequestMapping(value="/delete/{name}", method = RequestMethod.GET)
public ModelAndView delete(@PathVariable String name,
HttpServletRequest request, ModelMap model) {
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
Query query = new Query("Customer");
query.addFilter("name", FilterOperator.EQUAL, name);
PreparedQuery pq = datastore.prepare(query);
Entity customer = pq.asSingleEntity();
datastore.delete(customer.getKey());
//return to list
return new ModelAndView("redirect:../list");
}
//get all customers
@RequestMapping(value="/list", method = RequestMethod.GET)
public String listCustomer(ModelMap model) {
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
Query query =
new Query("Customer").addSort("date", Query.SortDirection.DESCENDING);
List<Entity> customers =
datastore.prepare(query).asList(FetchOptions.Builder.withLimit(10));
model.addAttribute("customerList", customers);
return "list";
}
}
2.JSP 页面
3 个 JSP 页面来显示客户并执行添加和更新。
文件:list.jsp
<%@ page import="java.util.List" %>
<%@ page import="com.google.appengine.api.datastore.Entity" %>
<html>
<body>
<h1>GAE + Spring 3 MVC REST + CRUD Example</h1>
Function : <a href="addCustomerPage">Add Customer</a>
<hr />
<h2>All Customers</h2>
<table border="1">
<thead>
<tr>
<td>Name</td>
<td>Email</td>
<td>Created Date</td>
<td>Action</td>
</tr>
</thead>
<%
List<Entity> customers = (List<Entity>)request.getAttribute("customerList");
for(Entity e : customers){
%>
<tr>
<td><%=e.getProperty("name") %></td>
<td><%=e.getProperty("email") %></td>
<td><%=e.getProperty("date") %></td>
<td><a href="update/<%=e.getProperty("name")%>">Update</a>
| <a href="delete/<%=e.getProperty("name")%>">Delete</a></td>
</tr>
<%
}
%>
</table>
</body>
</html>
文件:add.jsp
<html>
<body>
<h1>Add Customer</h1>
<form method="post" action="add" >
<table>
<tr>
<td>
UserName :
</td>
<td>
<input type="text" style="width: 185px;"
maxlength="30" name="name" id="name" />
</td>
</tr>
<tr>
<td>
Email :
</td>
<td>
<input type="text" style="width: 185px;"
maxlength="30" name="email" id="email" />
</td>
</tr>
</table>
<input type="submit" class="save" title="Save" value="Save" />
</form>
</body>
</html>
文件:update.jsp
<%@ page import="com.google.appengine.api.datastore.Entity" %>
<html>
<body>
<h1>Update Customer</h1>
<%
Entity customer = (Entity)request.getAttribute("customer");
%>
<form method="post" action="../update" >
<input type="hidden" name="originalName" id="originalName"
value="<%=customer.getProperty("name") %>" />
<table>
<tr>
<td>
UserName :
</td>
<td>
<input type="text" style="width: 185px;"
maxlength="30" name="name" id="name"
value="<%=customer.getProperty("name") %>" />
</td>
</tr>
<tr>
<td>
Email :
</td>
<td>
<input type="text" style="width: 185px;"
maxlength="30" name="email" id="email"
value="<%=customer.getProperty("email") %>" />
</td>
</tr>
</table>
<input type="submit" class="update" title="Update" value="Update" />
</form>
</body>
</html>
3.弹簧配置
扫描 Spring 控制器并配置视图解析器,以便它可以将视图重定向到 jsp 页面。
文件:mvc-dispatcher-servlet.xml
<beans
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<context:component-scan base-package="com.mkyong.controller" />
<mvc:annotation-driven />
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/pages/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
</beans>
4.集成弹簧
将 Spring 集成到 web 应用程序中。
文件:web.xml
<?xml version="1.0" encoding="utf-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
<servlet>
<servlet-name>mvc-dispatcher</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/mvc-dispatcher-servlet.xml</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>
5.演示
完成,看 demo,向你展示 web 应用的工作流程。
1.列表页面,显示现有客户列表。
网址:http://localhost:8888/customer/list
2.在列表页面中,点击“添加客户”链接以显示添加客户页面,填写新客户,并点击“添加”按钮。
URL : http://localhost:8888/customer/addCustomerPage
3.保存了客户后,它会返回到列表页面。
网址:http://localhost:8888/customer/list
4.尝试更新链接,它将显示所选客户的数据,更新电子邮件地址并点击更新按钮。
网址:http://localhost:8888/customer/update/mkyong
5.电子邮件被更新,并重定向回列表页面。
网址:http://localhost:8888/customer/list
6.要删除客户,只需点击“删除”链接。
下载源代码
由于文件很大,所有 Spring MVC 和 GAE jar 都被排除在外。
Download – GoogleAppEngine-SpringMVC-datastore.zip (17 KB)
参考
- GAE:使用数据存储
- GAE:实体
- GAE:数据存储低层 api
- GAE:JDO 和 Spring MVC 的 CRUD 操作
- 使用 Google App Engine、Spring MVC 和 Flex 的体验
Google 应用引擎+ Struts 1 示例
经典 Struts 1 框架万岁,在本教程中,我们将向您展示如何在 Google App Engine (GAE)环境下开发一个 Struts 1.x web 应用。
使用的工具和技术:
- JDK 1.6
- Eclipse 3.7+Eclipse 的 Google 插件
- 谷歌应用引擎 Java SDK 1.6.3.1
- Struts 1.3.10
Note
You may also interest at this Google App Engine + Struts 2 example.
这个例子将把 Struts 1.x hello world 例子与这个 GAE + Java 例子合并。
1.新建 Web 应用程序项目
在 Eclipse 中,创建一个新的 Web 应用程序项目,命名为“StrutsGoogleAppEngine”。
Google Plugin for Eclipse 将生成一个样本 GAE 项目结构。稍后将把 Struts 1 集成到这个 GAE 结构中。
2.集成 Struts 1.x 库
访问此链接下载 Struts 1.x 。需要以下罐子:
- antlr-2.7.2.jar
- commons-beanutils-1.8.0.jar
- 公共链 1.2.jar
- 1.8.jar
- commons-logging-1.0.4.jar
- 公共验证器 1.3.1.jar
- 奥罗-2.0.8.jar
- struts-core-1.3.10.jar
- struts-taglib-1.3.10.jar
复制后放入“ war/WEB-INF/lib ”文件夹。
右击项目文件夹,选择属性。选择" Java 构建路径 " - > " 库"选项卡,点击"添加 Jars "按钮,从" war/WEB-INF/lib "文件夹中选择上面 9 个 Jars 进入构建路径。
## 3.集成 Struts 1.x 动作和表单
3.1 删除StrutsGoogleAppEngineServlet.java
,你不需要这个。
3.2 创建新的操作类。
文件:src/com/mkyong/action/hello world action . Java
package com.mkyong.action;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import com.mkyong.form.HelloWorldForm;
public class HelloWorldAction extends Action {
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
HelloWorldForm helloWorldForm = (HelloWorldForm) form;
helloWorldForm.setMessage("Hello World!");
return mapping.findForward("success");
}
}
3.3 创建一个新的表单类。
文件:src/com/mkyong/form/hello world form . Java
package com.mkyong.form;
import org.apache.struts.action.ActionForm;
public class HelloWorldForm extends ActionForm {
String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
4.集成 Struts 1.x JSP 页面
4.1 创建HelloWorld.jsp
页面,放入“war/User/pages/hello world . JSP”。
文件:HelloWorld.jsp
<%@taglib uri="http://struts.apache.org/tags-bean" prefix="bean"%>
<html>
<head>
</head>
<body>
<h1>
Google App Engine + Struts 1.x example
</h1>
<h2><bean:write name="helloWorldForm" property="message" /></h2>
</body>
</html>
5.Struts XML 配置
创建一个struts-config.xml
文件,放在“war/we b-INF/struts-config . XML”中。
文件:struts-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts-config PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 1.3//EN"
"http://jakarta.apache.org/struts/dtds/struts-config_1_3.dtd">
<struts-config>
<form-beans>
<form-bean name="helloWorldForm" type="com.mkyong.form.HelloWorldForm" />
</form-beans>
<action-mappings>
<action path="/helloWorld" type="com.mkyong.action.HelloWorldAction"
name="helloWorldForm">
<forward name="success" path="/HelloWorld.jsp" />
</action>
</action-mappings>
</struts-config>
6.web.xml
更新web.xml
,整合 Struts。
文件:web.xml
<?xml version="1.0" encoding="utf-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>
/WEB-INF/struts-config.xml
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>
7.在 GAE 启用会话
更新appengine-web.xml
,启用会话支持,Struts 需要这个。
文件:appengine-web.xml
<?xml version="1.0" encoding="utf-8"?>
<appengine-web-app >
<application></application>
<version>1</version>
<sessions-enabled>true</sessions-enabled>
</appengine-web-app>
Note
If you don’t enable session in GAE, you will hit error “java.lang.RuntimeException: Session support is not enabled in appengine-web.xml“.
8.目录结构
查看最终的目录结构。
9.在本地运行
右键点击项目,运行为“ Web 应用”。
网址:http://localhost:8888/hello world . do
10.部署在 GAE
更新appengine-web.xml
文件,添加您的 App Engine 应用 ID。
文件:appengine-web.xml
<?xml version="1.0" encoding="utf-8"?>
<appengine-web-app >
<application>mkyong-strutsgae</application>
<version>1</version>
<sessions-enabled>true</sessions-enabled>
</appengine-web-app>
选择项目,点击谷歌图标,“部署到应用引擎”。
网址:http://mkyong-strutsgae.appspot.com/helloWorld.do
Note
Not much problem, just follow GAE directory structure, at least integrate Struts 1 is more easily than Struts 2.
下载源代码
由于文件较大,所有 Struts1 jars 都被排除在外,需要手动下载。
Download – StrutsGoogleAppEngine (13 KB)
参考
Google 应用引擎+ Struts 2 示例
在本教程中,我们将向您展示如何在 Google App Engine (GAE)环境下开发 Struts 2 web 应用程序。
使用的工具和技术:
- JDK 1.6
- Eclipse 3.7+Eclipse 的 Google 插件
- 谷歌应用引擎 Java SDK 1.6.3.1
- struts 2.3.1.2
Note
Before proceed on this tutorial, make sure you read this – GAE + Java example and Struts 2 hello world example.
1.新建 Web 应用程序项目
在 Eclipse 中,创建一个新的 Web 应用程序项目,命名为“Struts2GoogleAppEngine”。
Eclipse 的谷歌插件将生成一个 GAE 项目结构的样本。稍后,我们将向您展示如何将 Struts2 与这个生成的 GAE 项目集成。
## 2.集成 Struts 2 库
获取以下 Struts 2 依赖库,在此下载 Struts 2。
- asm-3.3.jar
- asm-commons-3.3
- 组件树 3.3.jar
- 文件上传-1.2.2.jar
- commons-io-2.0.1.jar
- 2.5.jar
- 自由标记-2.3.18.jar
- javassist-3.11.0.GA.jar
- ognl-3.0.4.jar
- 支柱 2-核心-2.3.1.2.jar
- xwork-core-2.3.1.2.jar
全部放入“ war/WEB-INF/lib ”文件夹。
右键点击项目文件夹,选择"属性 " - > " Java 构建路径 " - > " 库"选项卡,点击"添加 Jars 按钮,从" war/WEB-INF/lib "文件夹中选择以上 11 个 Jars 进入构建路径。
## 3.集成 Struts 2 代码
3.1 删除生成的Struts2GoogleAppEngineServlet.java
,不需要这个。
3.2 创建一个新的 Struts 2 操作类。
文件:src/com/mkyong/user/action/welcome user action . Java
package com.mkyong.user.action;
public class WelcomeUserAction {
private String username;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String execute() {
return "SUCCESS";
}
}
3.3 创建一个监听器类,并将 ognl 安全设置为空。
Note
Struts 2 need this listener to run in GAE environment. Read this – Issues when deploying Struts 2 on GAE and Error: result ‘null’ not found
文件:src/com/mkyong/listener/struts 2 listenerongae . Java
package com.mkyong.listener;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import ognl.OgnlRuntime;
public class Struts2ListenerOnGAE implements ServletContextListener,
HttpSessionListener, HttpSessionAttributeListener {
public void contextInitialized(ServletContextEvent sce) {
OgnlRuntime.setSecurityManager(null);
}
@Override
public void contextDestroyed(ServletContextEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void sessionCreated(HttpSessionEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void sessionDestroyed(HttpSessionEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void attributeAdded(HttpSessionBindingEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void attributeRemoved(HttpSessionBindingEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void attributeReplaced(HttpSessionBindingEvent arg0) {
// TODO Auto-generated method stub
}
}
3.4 要在本地 GAE 环境中运行 Struts2 项目,必须创建一个TextBlock
类,并重载原来的TextBlok
类,否则会出现“javax.swing.tree.TreeNode is a restricted class
错误信息。希望 Struts2 团队可以在未来的版本中解决这个问题。
TextBlock Source Code
Go this URL to download TextBlock source code.
3.5 审查项目目录结构。
4.集成 Struts 2 页
4.1 创建一个login.jsp
页面,接受用户输入。
文件:war/User/pages/login.jsp
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head></head>
<body>
<h1>GAE + Struts 2 Example</h1>
<s:form action="Welcome">
<s:textfield name="username" label="Username"/>
<s:password name="password" label="Password"/>
<s:submit/>
</s:form>
</body>
</html>
4.2 创建一个welcome_user.jsp
页面。
文件:war/User/pages/welcome _ User . JSP
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head></head>
<body>
<h1>GAE + Struts 2 Example</h1>
<h2>Hello <s:property value="username"/></h2>
</body>
</html>
4.3 再次审查项目目录结构。
5.Struts XML 配置
创建一个struts.xml
文件,放入“ src/struts.xml ”。
文件:struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<package name="user" namespace="/User" extends="struts-default">
<action name="Login">
<result>pages/login.jsp</result>
</action>
<action name="Welcome" class="com.mkyong.user.action.WelcomeUserAction">
<result name="SUCCESS">pages/welcome_user.jsp</result>
</action>
</package>
</struts>
6.web.xml
更新web.xml
,集成 Struts2,配置 ognl 安全监听器。
文件:web.xml
<?xml version="1.0" encoding="utf-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<filter>
<filter-name>struts2</filter-name>
<filter-class>
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>
com.mkyong.listener.Struts2ListenerOnGAE
</listener-class>
</listener>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>
7.目录结构
查看最终的目录结构。
8.在本地运行
完成,是时候进行测试了。右键点击项目,运行为“ Web 应用”。
网址:http://localhost:8888/User/log in . action
9.部署在 GAE
更新appengine-web.xml
,放上你的 App Engine 应用 ID。
<?xml version="1.0" encoding="utf-8"?>
<appengine-web-app >
<application>mkyong-struts2gae</application>
<version>1</version>
<!-- Configure java.util.logging -->
<system-properties>
<property name="java.util.logging.config.file" value="WEB-INF/logging.properties"/>
</system-properties>
</appengine-web-app>
选择项目,点击谷歌图标,“部署到应用引擎”。谷歌 Eclipse 插件将自动部署所有必要的文件到 GAE 生产。
在部署期间,Eclipse 控制台视图中将显示以下类似的消息。
------------ Deploying frontend ------------
Preparing to deploy:
Created staging directory at: 'C:\Users\mkyong\AppData\Local\Temp\appcfg7432687551.tmp'
Scanning for jsp files.
Compiling jsp files.
Scanning files on local disk.
Initiating update.
Cloning 2 static files.
Cloning 46 application files.
Deploying:
Uploading 12 files.
Uploaded 3 files.
Uploaded 6 files.
Uploaded 9 files.
Uploaded 12 files.
Initializing precompilation...
Sending batch containing 11 file(s) totaling 44KB.
Sending batch containing 1 blob(s) totaling 1KB.
Deploying new version.
Verifying availability:
Will check again in 1 seconds.
Will check again in 2 seconds.
Will check again in 4 seconds.
Closing update: new version is ready to start serving.
Updating datastore:
Uploading index definitions.
Deployment completed successfully
网址:http://mkyong-struts2gae.appspot.com/User/Login.action
Note
Finally, finished this long article. The overall integration is not much difficult, just need to fix Struts2 ognl security and TextBlock issues, hope Struts2’s team can fix this in future.
下载源代码
由于文件很大,所有 Struts2 jars 都被排除在外,需要手动下载。
Download – Struts2GoogleAppEngine.zip (23 KB)
参考
谷歌应用引擎教程
原文:http://web.archive.org/web/20230101150211/https://mkyong.com/tutorials/google-app-engine-tutorial/
Google App Engine ,一个云计算平台,用于在现有的 Google 基础设施中托管 web 应用程序,它易于扩展、管理和自由使用多达预定义的消耗资源,并且它支持 Java。如需额外收费,请参考此 GAE 计费。
在这一系列教程中,我们将向您展示一系列教程,帮助您开始使用 Java 编程 Google App Engine。
Note
All GAE tutorials are developed with “Eclipse 3.7 + Google Plugin for Eclipse” and “Google App Engine Java SDK 1.6.3.1“.
1.快速启动
向谷歌应用引擎问好。
- Google App Engine+Java hello world 使用 Eclipse 的例子
GAE+Eclipse+Java hello world 例子。 - Google App Engine+Python hello world 使用 Eclipse 的例子
GAE+Eclipse+PyDev+Python web hello world 例子。 - GAE+Python hello world on Mac OS X
GAE+Python web hello world 示例,使用 GAE launcher。
2.GAE +支柱 1 和 2
Struts 1 & 2 框架集成和你可能遇到的一些常见错误。
- Google App Engine + Struts 1.x 示例
将经典的 Struts 1.x 框架与 Google App Engine 集成。 - Google App Engine + Struts 2 示例
将 Struts 2 框架与 Google App Engine 集成。 - GAE 上的 Struts 2–错误:未找到结果‘null’
- GAE 上的 Struts 2–Java . security . accesscontrolexception:访问被拒绝
- javax.swing.tree.TreeNode 是受限类
3.GAE + JSF 2
JSF 新协议框架集成和你可能遇到的一些常见错误。
- Google App Engine + JSF 2 例
将 JSF 2 框架与 Google App Engine 整合。 - 无法实例化 expression factory ' com . sun . El . expression factory impl '
- 无法恢复 View/hello . XHTML
- javax.naming.InitialContext 是一个受限类
4.GAE +春天
Spring 框架集成。
- Google App Engine+Spring 3 MVC REST 实例
将 Spring MVC 框架与 Google App Engine 集成。
5.数据存储
在 Java 的 GAE 中,可以通过数据存储低级 api、JDO 或 JPA 将数据存储在“数据存储”中。在这个例子中,我们将使用 Spring MVC 进行演示。
6.常见问题
谷歌应用引擎中的一些常见问题。
-
Cron job on Google App Engine for Java
创建“cron.xml”并放入“WEB-INF”文件夹。 -
GAE + Java–集成谷歌用户账户
示例使用userService
在 GAE+Java 项目中集成谷歌用户账户。 -
下载/导出 Google App 引擎日志,Java App
App CFG request _ Logs 从 GAE 下载日志。 -
从谷歌应用引擎
下载上传的应用 AppCfg download_app 从 GAE 下载上传的应用。 -
如何安装谷歌 Eclipse 插件
步骤展示指导你在 Eclipse 3.7 中安装“谷歌 Eclipse 插件” -
Google Eclipse 插件–jar 已被篡改!
-
GAE:如何将日志信息输出到文件
将日志信息输出到文件,而不是日志控制台。
参考
Gradle 和 JUnit 示例
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/unittest/gradle-and-junit-example/
在 Gradle 中,您可以像这样声明 JUnit 依赖关系:
build.gradle
apply plugin: 'java'
dependencies {
testCompile 'junit:junit:4.12'
}
默认情况下,JUnit 附带了一个hamcrest-core
的捆绑副本
$ gradle dependencies --configuration testCompile
testCompile - Compile classpath for source set 'test'.
\--- junit:junit:4.12
\--- org.hamcrest:hamcrest-core:1.3
1.Gradle + JUnit + Hamcrest
通常,我们需要有用的hamcrest-library
库,所以,最好排除hamcrest-core
的 JUnit 捆绑副本,包括原始的hamcrest-core
库。再次回顾更新后的pom.xml
。
build.gradle
apply plugin: 'java'
dependencies {
testCompile('junit:junit:4.12'){
exclude group: 'org.hamcrest'
}
testCompile 'org.hamcrest:hamcrest-library:1.3'
}
再次检查依赖性。
$ gradle dependencies --configuration testCompile
testCompile - Compile classpath for source set 'test'.
+--- junit:junit:4.12
\--- org.hamcrest:hamcrest-library:1.3
\--- org.hamcrest:hamcrest-core:1.3
参考
gradle hamcrest junit (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','//web.archive.org/web/20190223082648/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0')
gradle–Spring 4 MVC Hello World 示例–注释
在本教程中,我们将以之前的 Gradle + Spring MVC XML 示例为例,将其重写为支持@JavaConfig 注释配置,不再需要 XML 文件。
这个例子只能在 Servlet 3.0+容器中运行,比如 Tomcat 7 或 Jetty 9。
使用的技术:
- Gradle 2.0
- 弹簧 4.1.6 释放
- Tomcat 7 或 Jetty 9
- Eclipse 4.4
- JDK 1.7
- 回溯 1.1.3
- 助推器 3
1.项目结构
下载项目源代码并查看项目文件夹结构:
P.S 不再有类似web.xml
或 Spring XML 配置文件的 XML 文件。
2. Gradle
2.1 查看build.gradle
文件,这应该是不言自明的。
build.gradle
apply plugin: 'java'
apply plugin: 'war'
apply plugin: 'eclipse-wtp'
//apply plugin: 'jetty' //too old, Jetty 6, use gretty plugin
apply plugin: 'org.akhikhl.gretty'
// JDK 7
sourceCompatibility = 1.7
targetCompatibility = 1.7
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
compile 'ch.qos.logback:logback-classic:1.1.3'
compile 'org.springframework:spring-webmvc:4.1.6.RELEASE'
compile 'javax.servlet:jstl:1.2'
//include in compile only, exclude in the war
providedCompile 'javax.servlet:servlet-api:2.5'
}
//Gretty Embedded Jetty
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'org.akhikhl.gretty:gretty:+'
}
}
// Don't use Jetty8, even it's a servlet 3.0+ container,
// but not support non-jar WebApplicationInitializer scanning.
// It will cause "No Spring WebApplicationInitializer types detected on classpath"
gretty {
port = 8080
contextPath = 'spring4'
servletContainer = 'jetty9' //tomcat7 or tomcat8
}
//For Eclipse IDE only
eclipse {
wtp {
component {
//define context path, default to project folder name
contextPath = 'spring4'
}
}
}
2.2 使这个项目支持 Eclipse IDE。现在,您可以将项目导入到 Eclipse IDE 中。
your-project$ gradle eclipse
3.弹簧@配置
Spring @Configuration 及其 XML 等价物。
3.1 Spring annotation 配置扫描服务类。
SpringRootConfig.java
package com.mkyong.helloworld.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan({ "com.mkyong.helloworld.service" })
public class SpringRootConfig {
}
XML 等价物。
spring-core-config.xml
<beans
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd ">
<context:component-scan base-package="com.mkyong.helloworld.service" />
</beans>
3.2 扩展抽象类WebMvcConfigurerAdapter
。
SpringWebConfig.java
package com.mkyong.helloworld.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;
@EnableWebMvc //<mvc:annotation-driven />
@Configuration
@ComponentScan({ "com.mkyong.helloworld.web" })
public class SpringWebConfig extends WebMvcConfigurerAdapter {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**")
.addResourceLocations("/resources/");
}
@Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver viewResolver
= new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/jsp/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}
XML 等价物。
spring-web-config.xml
<beans
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd ">
<context:component-scan base-package="com.mkyong.helloworld.web" />
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/views/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
<mvc:resources mapping="/resources/**" location="/resources/" />
<mvc:annotation-driven />
</beans>
4.Servlet 3.0+容器
创建一个ServletInitializer
类,Servlet 3.0+容器会自动拾取这个类并运行它。这是web.xml
的替代类
package com.mkyong.helloworld.servlet3;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import com.mkyong.helloworld.config.SpringRootConfig;
import com.mkyong.helloworld.config.SpringWebConfig;
public class MyWebInitializer extends
AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { SpringRootConfig.class };
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { SpringWebConfig.class };
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
XML 等价物。
web.xml
<web-app
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_2_5.xsd"
version="2.5">
<display-name>Gradle + Spring MVC Hello World</display-name>
<description>Spring MVC web application</description>
<!-- For web context -->
<servlet>
<servlet-name>hello-dispatcher</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-mvc-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>hello-dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- For root context -->
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-core-config.xml</param-value>
</context-param>
</web-app>
Note
There is no change in the Spring controller, logback and JSP files, so, the source code will not repeat here, please refer to the previous Gradle + Spring MVC XML example for complete source code.
5.演示
5.1 运行本项目。启动嵌入式 Jetty 容器的问题。
Terminal
your-project$ gradle jettyRun
21:56:34 INFO Jetty 9.2.10.v20150310 started and listening on port 8080
21:56:34 INFO spring4 runs at:
21:56:34 INFO http://localhost:8080/spring4
Press any key to stop the server.
> Building 87% > :jettyRun
5.2 http://localhost:8080/spring 4/
5.3 http://localhost:8080/spring 4/hello/mkyong . com
下载源代码
Download It – spring4-mvc-gradle-annotation-hello-world.zip (39 KB)GitHub link – spring4-mvc-gradle-annotation-hello-world.git
参考
- 维基百科–Java servlet
- Spring Web MVC 参考资料
- Gradle–eclipse WTP
- Gradle–Eclipse 插件
- Gradle–Jetty 插件
- Gradle–Gretty 插件
- 格雷迪–格雷迪配置
gradle hello world servlet 3 spring config spring mvc
Spring MVC hello world 示例(Gradle 和 JSP)
本教程向您展示了如何使用 Jakarta 服务器页面(JSP;以前的 JavaServer Pages)模板。
使用的技术和工具:
- Java 11
- 释放弹簧
- JSP
- JSTL 1.2
- Servlet API 4.0.1
- Bootstrap 5.2.0 (webjars)
- 智能理念
- Gradle 7.5.1
- 用于嵌入式 servlet 容器的 Gradle Gretty 插件 3 . 0 . 9(Tomcat 9 和 Jetty 9.4)
- 弹簧测试 5.2.22 .释放
- 哈姆克雷斯特 2.2
- JUnit 5.9
目录:
- 1。目录结构
- 2。项目依赖性
- 3。项目依赖关系——树形格式
- 4。弹簧控制器
- 5。弹簧配置
- 6。Spring DispatcherServlet
- 7 .。Spring MVC 和单元测试
- 8。JSP 和 JSTL
- 9。演示
- 10。下载源代码
- 10。参考文献
注
本教程不是 Spring Boot 应用,只是纯 Spring Web MVC!
1。目录结构
下面是这个项目的标准 Java 目录结构。
2。项目依赖性
以下是该项目的核心依赖项;spring-webmvc
依赖是必须的,但是其他的依赖依赖于你的项目需求。
spring-webmvc
–用于 Spring core 相关的 web 组件。jstl
–对于雅加达标准标签库(JSTL;原名 JavaServer Pages 标准标签库)。org.webjars.bootstrap
–用于 WebJars 管理客户端 web 库,例如 bootstrap。javax.servlet-api
–我们需要servlet-api
来编译 web 应用程序,设置为compileOnly
范围;通常,嵌入式服务器会提供这种功能。org.gretty
–使用嵌入式 servlet 容器运行这个项目,比如 Tomcat 9 或 Jetty 9.4。
为什么是 Gretty 3 插件?
最新的 Gretty 4 插件只支持 Tomcat 10,而 Spring MVC 5 我们需要 Tomcat 8.5 或 9,所以我们会坚持使用更老的 Gretty 3。
为什么 Spring Web MVC 5 不运行 o Tomcat 10?
- Tomcat 10 在包
jakarta.*
下实现 Servlet 5 规范(Jakarta EE 9 的一部分)。 - Spring Web MVC 5.x 在包
javax.*
下实现 Servlet 4 规范(Jakarta EE 8 的一部分)。
Spring 5 还是靠javax.servlet.*
,最新的 Tomcat 10 靠jakarta.servlet
。简而言之,Spring 5 无法与 Tomcat 10 兼容,因为软件包从javax.*
重命名为jakarta.*
。
延伸阅读
build.gradle
plugins {
id 'org.gretty' version '3.0.9'
}
apply plugin: 'java'
apply plugin: 'war'
apply plugin: 'idea'
// JDK 11
sourceCompatibility = 11
targetCompatibility = 11
repositories {
mavenCentral()
}
dependencies {
compileOnly 'javax.servlet:javax.servlet-api:4.0.1'
implementation 'javax.servlet:jstl:1.2'
implementation 'org.springframework:spring-webmvc:5.2.22.RELEASE'
implementation 'org.webjars:bootstrap:5.2.0'
testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.9.0'
testImplementation 'org.springframework:spring-test:5.2.22.RELEASE'
testImplementation 'org.hamcrest:hamcrest-core:2.2'
}
gretty {
httpPort = 8080
contextPath = '/'
//servletContainer = 'jetty9.4'
servletContainer = 'tomcat9'
}
test {
useJUnitPlatform()
}
3。项目依赖关系——树形格式
再次检查树结构中的项目依赖关系。
Terminal
gradle dependencies --configuration compileClasspath
> Task :dependencies
------------------------------------------------------------
Root project 'spring-mvc-hello-world-jsp'
------------------------------------------------------------
compileClasspath - Compile classpath for source set 'main'.
+--- javax.servlet:javax.servlet-api:4.0.1
+--- javax.servlet:jstl:1.2
+--- org.springframework:spring-webmvc:5.2.22.RELEASE
| +--- org.springframework:spring-aop:5.2.22.RELEASE
| | +--- org.springframework:spring-beans:5.2.22.RELEASE
| | | \--- org.springframework:spring-core:5.2.22.RELEASE
| | | \--- org.springframework:spring-jcl:5.2.22.RELEASE
| | \--- org.springframework:spring-core:5.2.22.RELEASE (*)
| +--- org.springframework:spring-beans:5.2.22.RELEASE (*)
| +--- org.springframework:spring-context:5.2.22.RELEASE
| | +--- org.springframework:spring-aop:5.2.22.RELEASE (*)
| | +--- org.springframework:spring-beans:5.2.22.RELEASE (*)
| | +--- org.springframework:spring-core:5.2.22.RELEASE (*)
| | \--- org.springframework:spring-expression:5.2.22.RELEASE
| | \--- org.springframework:spring-core:5.2.22.RELEASE (*)
| +--- org.springframework:spring-core:5.2.22.RELEASE (*)
| +--- org.springframework:spring-expression:5.2.22.RELEASE (*)
| \--- org.springframework:spring-web:5.2.22.RELEASE
| +--- org.springframework:spring-beans:5.2.22.RELEASE (*)
| \--- org.springframework:spring-core:5.2.22.RELEASE (*)
\--- org.webjars:bootstrap:5.2.0
4。弹簧控制器
下面是一个 Spring Web MVC 控制器,用于处理对/
和/hello/{name}
的 Web 请求并显示消息。
HelloController.java
package com.mkyong.web;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class HelloController {
//@RequestMapping(value = "/", method = RequestMethod.GET)
@GetMapping("/")
public String defaultPage(ModelMap model) {
return "hello";
}
//@RequestMapping(value = "/hello/{name:.+}", method = RequestMethod.GET)
@GetMapping("/hello/{name:.+}")
public ModelAndView hello(@PathVariable("name") String name) {
ModelAndView model = new ModelAndView();
model.setViewName("hello");
model.addObject("message", name);
return model;
}
}
5。弹簧配置
Spring 需要一个用于 JSP 页面的viewResolver
bean。
SpringWebConfig.java
package com.mkyong.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;
@EnableWebMvc
@Configuration
@ComponentScan({ "com.mkyong.web" })
public class SpringWebConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("/webjars/");
}
@Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/jsp/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}
6。Spring DispatcherServlet
这个MyServletInitializer
将由 Servlet 容器(例如 Jetty 或 Tomcat)自动检测。
MyServletInitializer.java
package com.mkyong;
import com.mkyong.config.SpringWebConfig;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class MyServletInitializer
extends AbstractAnnotationConfigDispatcherServletInitializer {
// services and data sources
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[0];
}
// controller, view resolver, handler mapping
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringWebConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
7 .。Spring MVC 和单元测试
Spring MVC 控制器的简单单元测试。
HelloControllerTest.java
package com.mkyong;
import com.mkyong.config.SpringWebConfig;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.ModelAndView;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ExtendWith(SpringExtension.class)
@WebAppConfiguration
@ContextConfiguration(classes = {SpringWebConfig.class})
public class HelloControllerTest {
@Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
@BeforeEach
void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
@Test
public void testDefaultPage() throws Exception {
MvcResult result = this.mockMvc.perform(get("/"))
/*.andDo(print())*/
.andExpect(status().isOk())
.andReturn();
ModelAndView modelAndView = result.getModelAndView();
assertEquals("hello", modelAndView.getViewName());
assertNull(modelAndView.getModel().get("message"));
}
@Test
public void testHelloPage() throws Exception {
MvcResult result = this.mockMvc.perform(get("/hello/mkyong"))
.andExpect(status().isOk())
.andReturn();
ModelAndView modelAndView = result.getModelAndView();
assertEquals("hello", modelAndView.getViewName());
assertEquals("mkyong", modelAndView.getModel().get("message"));
}
}
8。JSP 和 JSTL
一个显示 hello world 消息的简单 JSP + JSTL 模板也展示了如何集成 webjars 的引导和定制 CSS 和 JS 文件。
webapp/WEB-INF/views/jsp/hello.jsp
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html lang="en">
<head>
<title>Spring Web MVC and JSP</title>
<spring:url value="/resources/core/css/main.css" var="coreCss" />
<spring:url value="/webjars/bootstrap/5.2.0/css/bootstrap.min.css" var="bootstrapCss" />
<link href="${bootstrapCss}" rel="stylesheet" />
<link href="${coreCss}" rel="stylesheet" />
</head>
<nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top">
<a class="navbar-brand" href="#">Mkyong.com</a>
</nav>
<main role="main" class="container">
<div class="starter-template">
<h1>Spring Web MVC JSP Example</h1>
<h2>
<c:if test="${not empty message}">
Hello ${message}
</c:if>
<c:if test="${empty message}">
Welcome!
</c:if>
</h2>
</div>
</main>
<spring:url value="/resources/core/js/main.js" var="coreJs" />
<spring:url value="/webjars/bootstrap/5.2.0/js/bootstrap.min.js" var="bootstrapJs" />
<script src="${coreJs}"></script>
<script src="${bootstrapJs}"></script>
</body>
</html>
9。演示
转到终端,项目文件夹,运行gradle tomcatRunWar
。
Terminal
gradle tomcatRunWar
//...
INFO: Completed initialization in 628 ms
17:19:14 INFO Tomcat 9.0.58 started and listening on port 8080
17:19:14 INFO runs at:
17:19:14 INFO http://localhost:8080
> Task :tomcatRunWar
Press any key to stop the server.
另外,试着用命令gradle jettyRunWar
在嵌入式 Jetty 上运行 Spring Web MVC。
http://localhost:8080/
http://localhost:8080/hello/mkyong
10。下载源代码
$ git 克隆https://github.com/mkyong/spring-mvc/
$ cd spring-mvc-hello-world-jsp
$ gradle tomcatRunWar
或者
$度喷气式滑水道
请访问 http://localhost:8080/hello/mkyong
10。参考文献
- Spring MVC hello world 示例(Maven 和百里香叶)
- Apache Tomcat 版本
- 格雷尔·格雷蒂插件
- 格雷蒂·吉图布
- 格雷蒂文档
- 构建 Java 应用程序示例
- 维基百科–模型–视图–控制器
- 维基百科–雅加达服务器页面
- Spring Boot Hello World 示例–百里香叶
- Spring 5,Embedded Tomcat 8,和 Gradle:快速教程
- 弹簧启动不适用于 Tomcat 10
gson——以流的形式读写 JSON
从 Gson 版本 1.6 开始,引入了两个新的类JsonReader
和JsonWriter
来提供对 JSON 数据的流处理。阅读此 Gson 流媒体文档以了解使用它的好处。
JsonWriter
–将 JSON 写成一个流。JsonReader
–以流的形式读取 JSON。
1.JsonWriter
GsonExample1.java
package com.mkyong;
import com.google.gson.stream.JsonWriter;
import java.io.FileWriter;
import java.io.IOException;
public class GsonExample1 {
public static void main(String[] args) {
try (JsonWriter writer = new JsonWriter(new FileWriter("c:\\projects\\user.json"))) {
writer.beginObject(); // {
writer.name("name").value("mkyong"); // "name" : "mkyong"
writer.name("age").value(29); // "age" : 29
writer.name("messages"); // "messages" :
writer.beginArray(); // [
writer.value("msg 1"); // "msg 1"
writer.value("msg 2"); // "msg 2"
writer.value("msg 3"); // "msg 3"
writer.endArray(); // ]
writer.endObject(); // }
} catch (IOException e) {
e.printStackTrace();
}
}
}
输出
c:\projects\user.json
{"name":"mkyong","age":29,"messages":["msg 1","msg 2","msg 3"]}
2.JsonReader
令牌在流模式下,每一个 JSON 数据都被认为是一个单独的令牌。当你使用JsonReader
来处理它的时候,每个令牌都会被顺序处理。举个例子,
{
"url":"www.mkyong.com"
}
- 令牌 1 = {
- 令牌 2 = url
- 令牌 3 = www.mkyong.com
- 令牌 4 = }
GsonExample2.java
package com.mkyong;
import com.google.gson.stream.JsonReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class GsonExample2 {
public static void main(String[] args) {
try (JsonReader reader = new JsonReader(new FileReader("c:\\projects\\user.json"))) {
reader.beginObject();
while (reader.hasNext()) {
String name = reader.nextName();
if (name.equals("name")) {
System.out.println(reader.nextString());
} else if (name.equals("age")) {
System.out.println(reader.nextInt());
} else if (name.equals("messages")) {
// read array
reader.beginArray();
while (reader.hasNext()) {
System.out.println(reader.nextString());
}
reader.endArray();
} else {
reader.skipValue(); //avoid some unhandle events
}
}
reader.endObject();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
输出
mkyong
29
msg 1
msg 2
msg 3
Note
Read more Gson examples
参考
在 Spring MVC 中处理重复的表单提交
在最后一个 Spring MVC 表单处理的例子中,如果你刷新表单成功视图,大多数浏览器会提示一个弹出对话框来确认表单的重新提交。如果您单击“是”,表单将再次被重新提交,这种情况就是众所周知的重复表单提交。
图:提交重复表单的示例。
常见的解决方案是使用" Post/Redirect/Get "设计模式。如果表单提交成功,它将重定向到另一个 URL,而不是直接返回网页。
Note
Check the details explanation of Post/Redirect/Get Design Pattern in Wiki.
Spring MVC 中的 post/重定向/Get 设计模式
在本教程中,我们将向您展示如何应用 Spring MVC 中的" Post/Redirect/Get "设计模式来解决最后一个表单处理示例中的重复表单提交问题。
1.重复表单提交
见下面正常的表单声明,会遇到重复表单提交的问题。
文件:mvc-dispatcher-servlet.xml
<bean
class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping" />
<bean class="com.mkyong.customer.controller.CustomerController">
<property name="formView" value="CustomerForm" />
<property name="successView" value="CustomerSuccess" />
</bean>
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
<property name="prefix">
<value>/WEB-INF/pages/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
在上面的代码片段中,CustomerController
直接返回一个" CustomerSuccess "视图,而这个视图应该用重定向 URL 替换为。
2.重定向视图
声明了一个评论视图,命名为“customersuccessredict”,并返回一个 URL“【CustomerSuccess.htm】T2”。
文件:spring-views.xml
<beans ...>
<!-- Redirect view -->
<bean id="customerSuccessRedirect"
class="org.springframework.web.servlet.view.RedirectView">
<property name="url" value="CustomerSuccess.htm" />
</bean>
</beans>
3.弹簧配置
更新MVC-dispatcher-servlet . XML设置,将所有 Spring 的配置链接在一起。
- 将“成功视图”更新为新的重定向视图,命名为“客户成功重定向”。
- 声明一个“ XmlViewResolver ”来加载重定向视图。
- 为“InternalResourceViewResolver”和“ XmlViewResolver ”设置一个优先级顺序,否则“InternalResourceViewResolver”将始终匹配,并且不会让您的应用程序有机会调用“ XmlViewResolver ”。
- 声明一个"ParameterizableViewController"控制器来匹配重定向 URL 并向用户返回一个视图。由于“ControllerClassNameHandlerMapping”不会为任何内置 Spring 的控制器生成映射,所以您必须在“ SimpleUrlHandlerMapping ”中定义显式映射。
文件:mvc-dispatcher-servlet.xml
<bean
class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping" />
<bean class="com.mkyong.customer.controller.CustomerController">
<property name="formView" value="CustomerForm" />
<property name="successView" value="customerSuccessRedirect" />
<!-- it was
<property name="successView" value="CustomerSuccess" />
-->
</bean>
<!-- Redirect Controller -->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/CustomerSuccess.htm">customerSuccessController</prop>
</props>
</property>
</bean>
<bean id="customerSuccessController"
class="org.springframework.web.servlet.mvc.ParameterizableViewController">
<property name="viewName" value="CustomerSuccess" />
</bean>
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
<property name="prefix">
<value>/WEB-INF/pages/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
<property name="order" value="1" />
</bean>
<bean class="org.springframework.web.servlet.view.XmlViewResolver">
<property name="location">
<value>/WEB-INF/spring-views.xml</value>
</property>
<property name="order" value="0" />
</bean>
4.它是如何工作的?
1.访问网址:http://localhost:8080/spring MVC/customer . htm。
2.填写并提交表单。
3.返回“成功视图”,即“客户成功重定向”。
<bean class="com.mkyong.customer.controller.CustomerController">
<property name="formView" value="CustomerForm" />
<property name="successView" value="customerSuccessRedirect" />
</bean>
4.“XmlViewResolver”匹配它并返回一个 URL 为“CustomerSuccess.htm”的“RedirectView”。
<bean id="customerSuccessRedirect"
class="org.springframework.web.servlet.view.RedirectView">
<property name="url" value="CustomerSuccess.htm" />
</bean>
5.“SimpleUrlHandlerMapping”匹配它并返回一个 ParameterizableViewController,“customerSuccessController”,返回视图名“ CustomerSuccess ”。
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/CustomerSuccess.htm">customerSuccessController</prop>
</props>
</property>
</bean>
<bean id="customerSuccessController"
class="org.springframework.web.servlet.mvc.ParameterizableViewController">
<property name="viewName" value="CustomerSuccess" />
</bean>
6.“InternalResourceViewResolver”匹配它并返回最终视图“/we b-INF/pages/customer success . JSP”。
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
<property name="prefix">
<value>/WEB-INF/pages/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
<property name="order" value="1" />
</bean>
7.网址改为http://localhost:8080/spring MVC/customer success . htm。
8.尝试刷新成功表单页面,表单重新提交对话框将不再提示。
Note
The overall concept is return a redirect URL instead of a direct page.
下载源代码
Download it – SpringMVC-Duplicated-Form-Submission-Solution.zip (12KB)
参考
duplicated form submission spring mvc
休眠–找不到 C3P0ConnectionProvider
问题
已将 Hibernate 配置为使用" c3p0 "连接池,但遇到以下警告:
//...
2011-04-25_12:18:37.190 WARN o.h.c.ConnectionProviderFactory -
c3p0 properties is specificed, but could not find
org.hibernate.connection.C3P0ConnectionProvider from the classpath,
these properties are going to be ignored.
2011-04-25_12:18:37.191 INFO o.h.c.DriverManagerConnectionProvider -
Using Hibernate built-in connection pool (not for production use!)
//...
貌似“org.hibernate.connection.C3P0ConnectionProvider
”不见了?
解决办法
从 Hibernate v3.3 开始(如果没弄错的话),“ C3P0ConnectionProvider ”被移到另一个 jar 文件“ hibernate-c3p0.jar ”。你需要包含它,以使 Hibernate 支持 c3p0 连接池。
可以从 JBoss 公共资源库下载“ hibernate-c3p0.jar ”。
文件:pom.xml
<project ...>
<repositories>
<repository>
<id>JBoss repository</id>
<url>http://repository.jboss.org/nexus/content/groups/public/</url>
</repository>
</repositories>
<dependencies>
<!-- Hibernate c3p0 connection pool -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-c3p0</artifactId>
<version>3.6.3.Final</version>
</dependency>
</dependencies>
</project>
Note
See this detail guide – “How to configure c3p0 connection pool in Hibernate“. ## 参考
- http://docs . JBoss . org/hibernate/core/3.3/API/org/hibernate/connection/C3 P0 connection provider . html
- http://sourceforge.net/projects/c3p0/
休眠标准示例
Hibernate Criteria API 是 Hibernate 查询语言(HQL)的一个更加面向对象和优雅的替代品。对于有许多可选搜索标准的应用程序来说,这总是一个好的解决方案。
HQL 的例子和标准
这里有一个案例研究,使用可选的搜索标准(开始日期、结束日期和数量、按日期排序)来检索一个 StockDailyRecord 的列表。
1.HQL 的例子
在 HQL,您需要比较这是否是附加“where”语法的第一个标准,并将日期格式化为合适的格式。它的工作,但长代码是丑陋的,笨重的和容易出错的字符串连接可能会引起安全问题,如 SQL 注入。
public static List getStockDailtRecord(Date startDate,Date endDate,
Long volume,Session session){
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
boolean isFirst = true;
StringBuilder query = new StringBuilder("from StockDailyRecord ");
if(startDate!=null){
if(isFirst){
query.append(" where date >= '" + sdf.format(startDate) + "'");
}else{
query.append(" and date >= '" + sdf.format(startDate) + "'");
}
isFirst = false;
}
if(endDate!=null){
if(isFirst){
query.append(" where date <= '" + sdf.format(endDate) + "'");
}else{
query.append(" and date <= '" + sdf.format(endDate) + "'");
}
isFirst = false;
}
if(volume!=null){
if(isFirst){
query.append(" where volume >= " + volume);
}else{
query.append(" and volume >= " + volume);
}
isFirst = false;
}
query.append(" order by date");
Query result = session.createQuery(query.toString());
return result.list();
}
2.标准示例
在条件中,您不需要比较这是否是附加“where”语法的第一个条件,也不需要设置日期格式。代码行减少了,一切都以更优雅和面向对象方式处理。
public static List getStockDailyRecordCriteria(Date startDate,Date endDate,
Long volume,Session session){
Criteria criteria = session.createCriteria(StockDailyRecord.class);
if(startDate!=null){
criteria.add(Expression.ge("date",startDate));
}
if(endDate!=null){
criteria.add(Expression.le("date",endDate));
}
if(volume!=null){
criteria.add(Expression.ge("volume",volume));
}
criteria.addOrder(Order.asc("date"));
return criteria.list();
}
标准 API
让我们来看看一些流行的标准 API 函数。
1.标准基本查询
创建一个 criteria 对象,并从数据库中检索所有“StockDailyRecord”记录。
Criteria criteria = session.createCriteria(StockDailyRecord.class);
2.标准排序查询
结果按“日期”升序排序。
Criteria criteria = session.createCriteria(StockDailyRecord.class)
.addOrder( Order.asc("date") );
结果按“日期”降序排序。
Criteria criteria = session.createCriteria(StockDailyRecord.class)
.addOrder( Order.desc("date") );
3.标准限制查询
Restrictions 类提供了许多方法来进行比较操作。
限制. eq
确保值等于 10000。
Criteria criteria = session.createCriteria(StockDailyRecord.class)
.add(Restrictions.eq("volume", 10000));
限制。lt,le,gt,ge
确保音量小于 10000。
Criteria criteria = session.createCriteria(StockDailyRecord.class)
.add(Restrictions.lt("volume", 10000));
请确保音量小于或等于 10000。
Criteria criteria = session.createCriteria(StockDailyRecord.class)
.add(Restrictions.le("volume", 10000));
确保音量大于 10000。
Criteria criteria = session.createCriteria(StockDailyRecord.class)
.add(Restrictions.gt("volume", 10000));
请确保音量大于或等于 10000。
Criteria criteria = session.createCriteria(StockDailyRecord.class)
.add(Restrictions.ge("volume", 10000));
限制,比如
确保股票名称以“MKYONG”开头,后跟任意字符。
Criteria criteria = session.createCriteria(StockDailyRecord.class)
.add(Restrictions.like("stockName", "MKYONG%"));
限制。介于
请确保该日期在开始日期和结束日期之间。
Criteria criteria = session.createCriteria(StockDailyRecord.class)
.add(Restrictions.between("date", startDate, endDate));
Restrictions.isNull,isNotNull
请确保卷为空。
Criteria criteria = session.createCriteria(StockDailyRecord.class)
.add(Restrictions.isNull("volume"));
请确保卷不为空。
Criteria criteria = session.createCriteria(StockDailyRecord.class)
.add(Restrictions.isNotNull("volume"));
许多其他的限制函数可以在这里找到。
https://www . hibernate . org/Hib _ docs/v3/API/org/hibernate/criterion/restrictions . html
3.结果分页标准
Criteria 提供了很少的函数来使分页变得非常容易。从第 20 条记录开始,从数据库中检索接下来的 10 条记录。
Criteria criteria = session.createCriteria(StockDailyRecord.class);
criteria.setMaxResults(10);
criteria.setFirstResult(20);
为什么不是标准!?
标准 API 确实带来了一些缺点。
1.性能问题
您无法控制 Hibernate 生成的 SQL 查询,如果生成的查询很慢,您很难调优查询,并且您的数据库管理员可能不喜欢它。
1.维护问题
所有的 SQL 查询都分散在 Java 代码中,当一个查询出错时,您可能要花时间在您的应用程序中查找问题查询。另一方面,存储在 Hibernate 映射文件中的命名查询更容易维护。
结论
没有什么是完美的,请考虑您的项目需求并明智地使用它。
Hibernate 数据过滤器示例–XML 和注释
Hibernate 数据过滤器是一种创新的方式来过滤从数据库中检索的数据,以一种更可重用的方式和“可见性”规则。数据过滤器有一个唯一的名称,全局访问并接受过滤器规则的参数化值,您可以在 Hibernate 会话中启用和禁用它。
休眠数据过滤器示例
在本例中,它定义了一个数据过滤器,过滤指定日期的收集数据。Hibernate 数据过滤器可以在 XML 映射文件和注释中实现。
1.XML 映射文件中的 Hibernate 数据过滤器
用' filter-def '关键字定义一个数据过滤器,并接受一个日期参数。
<filter-def name="stockRecordFilter">
<filter-param name="stockRecordFilterParam" type="date"/>
</filter-def>
XML 映射示例
一个 XML 映射文件示例,用于声明并将其分配给收集组。
<hibernate-mapping>
<class name="com.mkyong.common.Stock" table="stock" catalog="mkyong">
...
<set name="stockDailyRecords" inverse="true" table="stock_daily_record">
<key>
<column name="STOCK_ID" not-null="true" />
</key>
<one-to-many class="com.mkyong.common.StockDailyRecord" />
<filter name="stockRecordFilter" condition="date >= :stockRecordFilterParam"/>
</set>
</class>
<filter-def name="stockRecordFilter">
<filter-param name="stockRecordFilterParam" type="date"/>
</filter-def>
</hibernate-mapping>
在condition = " date>=:stockRecordFilterParam "中,“日期”是属于“StockDailyRecord”的一个属性。
2.批注中的休眠数据过滤器
用' @FilterDef '关键字定义一个数据过滤器,用 @ParamDef 接受一个日期参数。
@FilterDef(name="stockRecordFilter",
parameters=@ParamDef( name="stockRecordFilterParam", type="date" ) )
注释示例
要声明并将其分配给收集组的批注文件示例。
...
@Entity
@FilterDef(name="stockRecordFilter",
parameters=@ParamDef( name="stockRecordFilterParam", type="date" ) )
@Table(name = "stock", catalog = "mkyong")
public class Stock implements java.io.Serializable {
...
@OneToMany(fetch = FetchType.LAZY, mappedBy = "stock")
@Filter(
name = "stockRecordFilter",
condition="date >= :stockRecordFilterParam"
)
public Set<StockDailyRecord> getStockDailyRecords() {
return this.stockDailyRecords;
}
在condition = " date>=:stockRecordFilterParam "中,“日期”是属于“StockDailyRecord”的一个属性。
如何启用和禁用数据过滤器
启用数据过滤器。
Filter filter = session.enableFilter("stockRecordFilter");
filter.setParameter("stockRecordFilterParam", new Date());
禁用数据过滤器。
session.disableFilter("stockRecordFilter");
应用和实现日期过滤器
下面的代码片段展示了如何应用和实现数据过滤器。
Session session = HibernateUtil.getSessionFactory().openSession();
System.out.println("****** Enabled Filter ******");
Filter filter = session.enableFilter("stockRecordFilter");
filter.setParameter("stockRecordFilterParam", new Date());
Stock stock = (Stock)session.get(Stock.class, 2);
Set<StockDailyRecord> sets = stock.getStockDailyRecords();
for(StockDailyRecord sdr : sets){
System.out.println(sdr.getDailyRecordId());
System.out.println(sdr.getDate());
}
System.out.println("****** Disabled Filter ******");
session.disableFilter("stockRecordFilter");
//clear the loaded instance and get Stock again, for demo only
session.evict(stock);
Stock stock2 = (Stock)session.get(Stock.class, 2);
Set<StockDailyRecord> sets2 = stock2.getStockDailyRecords();
for(StockDailyRecord sdr : sets2){
System.out.println(sdr.getDailyRecordId());
System.out.println(sdr.getDate());
}
输出
****** Enabled Filter ******
58
2010-01-31
****** Disabled Filter ******
60
2010-01-02
58
2010-01-31
63
2010-01-23
61
2010-01-03
...
在本例中(包括 XML 和注释),在启用过滤器后,它的所有“StockDailyRecord”集合都将根据参数 date 进行过滤。
P . S filter . set parameter(" stockRecordFilterParam ",new Date());,当前的新日期是 2010-01-27。
Hibernate SQL 方言集合
Hibernate SQL 方言告诉您的 Hibernate 应用程序应该使用哪种 SQL 语言与您的数据库进行对话。
1.DB2
org.hibernate.dialect.DB2Dialect
2.DB2 AS/400
org.hibernate.dialect.DB2400Dialect
3.DB2 OS390
org.hibernate.dialect.DB2390Dialect
4.一种数据库系统
org.hibernate.dialect.PostgreSQLDialect
5.关系型数据库
org.hibernate.dialect.MySQLDialect
6.带有 InnoDB 的 MySQL
org.hibernate.dialect.MySQLInnoDBDialect
7.带有 MyISAM 的 MySQL
org.hibernate.dialect.MySQLMyISAMDialect
8.Oracle 8
org.hibernate.dialect.OracleDialect
9.Oracle 9i/10g
org.hibernate.dialect.Oracle9Dialect
10.赛贝斯
org.hibernate.dialect.SybaseDialect
11.Sybase Anywhere
org.hibernate.dialect.SybaseAnywhereDialect
12.Microsoft SQL Server
org.hibernate.dialect.SQLServerDialect
13.SAP 数据库
org.hibernate.dialect.SAPDBDialect
14.Informix
org.hibernate.dialect.InformixDialect
15.HypersonicSQL
org.hibernate.dialect.HSQLDialect
16.安格尔
org.hibernate.dialect.IngresDialect
17.进步
org.hibernate.dialect.ProgressDialect
18.Mckoi SQL
org.hibernate.dialect.MckoiDialect
19.Interbase
org.hibernate.dialect.InterbaseDialect
20.点基础
org.hibernate.dialect.PointbaseDialect
21. FrontBase
org.hibernate.dialect.FrontbaseDialect
22.火鸟
org.hibernate.dialect.FirebirdDialect
Note
Refer to this Hibernate SQL dialects documentation for further information.hibernate
向控制台显示 Hibernate SQL–show _ SQL、format_sql 和 use_sql_comments
Hibernate 有一个内置函数,可以将所有生成的 SQL 语句记录到控制台。您可以通过在 Hibernate 配置文件"hibernate.cfg.xml
"中添加一个" show_sql "属性来启用它。这个函数对于基本的故障排除很有用,并且可以查看 Hibernate 在后面做了什么。
1.显示 sql
允许将所有生成的 SQL 语句记录到控制台
<!--hibernate.cfg.xml -->
<property name="show_sql">true</property>
输出
Hibernate: insert into mkyong.stock_transaction
(CHANGE, CLOSE, DATE, OPEN, STOCK_ID, VOLUME)
values (?, ?, ?, ?, ?, ?)
2.格式 _sql
格式化生成的 SQL 语句,使其更具可读性,但会占用更多的屏幕空间。😃
<!--hibernate.cfg.xml -->
<property name="format_sql">true</property>
输出
Hibernate:
insert
into
mkyong.stock_transaction
(CHANGE, CLOSE, DATE, OPEN, STOCK_ID, VOLUME)
values
(?, ?, ?, ?, ?, ?)
3.使用 sql 注释
Hibernate 将把注释放在所有生成的 SQL 语句中,以提示生成的 SQL 试图做什么
<!--hibernate.cfg.xml -->
<property name="use_sql_comments">true</property>
输出
Hibernate:
/* insert com.mkyong.common.StockTransaction
*/ insert
into
mkyong.stock_transaction
(CHANGE, CLOSE, DATE, OPEN, STOCK_ID, VOLUME)
values
(?, ?, ?, ?, ?, ?)
休眠配置文件
hibernate.cfg.xml
完整的例子。
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.bytecode.use_reflection_optimizer">false</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.password">password</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/mkyong</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="show_sql">true</property>
<property name="format_sql">true</property>
<property name="use_sql_comments">true</property>
</session-factory>
</hibernate-configuration>
Hibernate SQL 参数值怎么样?
这个基本的 SQL 日志对于正常的调试来说已经足够好了,但是它不能显示 Hibernate SQL 参数值。需要一些第三方库集成来将 Hibernate SQL 参数值显示到控制台或文件中。检查以下两篇文章:
hibernate–动态插入属性示例
什么是动态插入
dynamic-insert 属性告诉 Hibernate 是否在 SQL INSERT 语句中包含空属性。让我们探讨一些例子来更清楚地了解它。
动态插入示例
1.动态插入=假
dynamic-insert 的默认值是 false,这意味着在 Hibernate 的 SQL INSERT 语句中包含空属性。
例如,尝试为一个对象属性设置一些空值并保存它。
StockTransaction stockTran = new StockTransaction();
//stockTran.setPriceOpen(new Float("1.2"));
//stockTran.setPriceClose(new Float("1.1"));
//stockTran.setPriceChange(new Float("10.0"));
stockTran.setVolume(2000000L);
stockTran.setDate(new Date());
stockTran.setStock(stock);
session.save(stockTran);
打开 Hibernate "show_sql "为 true,你会看到下面的 insert SQL 语句。
Hibernate:
insert
into
mkyong.stock_transaction
(DATE, PRICE_CHANGE, PRICE_CLOSE, PRICE_OPEN, STOCK_ID, VOLUME)
values
(?, ?, ?, ?, ?, ?)
Hibernate 将为插入生成不必要的列 (PRICE_CHANGE,PRICE_CLOSE,PRICE_OPEN)。
2.动态插入=真
如果将 dynamic-insert 设置为 true,这意味着在 Hibernate 的 SQL INSERT 语句中排除空属性值。
例如,尝试将一些空值设置到一个对象属性并再次保存它。
StockTransaction stockTran = new StockTransaction();
//stockTran.setPriceOpen(new Float("1.2"));
//stockTran.setPriceClose(new Float("1.1"));
//stockTran.setPriceChange(new Float("10.0"));
stockTran.setVolume(2000000L);
stockTran.setDate(new Date());
stockTran.setStock(stock);
session.save(stockTran);
将 Hibernate“show _ SQL”设置为 true。您将看到不同的 insert SQL 语句。
Hibernate:
insert
into
mkyong.stock_transaction
(DATE, STOCK_ID, VOLUME)
values
(?, ?, ?)
Hibernate 将只为插入生成必要的列(日期、股票 ID、数量)。
性能问题
在某些情况下,比如一个有数百列的非常大的表(遗留设计),或者一个表包含非常大的数据量,插入一些不必要的东西肯定会降低系统性能。
如何配置
您可以通过注释或 XML 映射文件配置动态插入属性值。
1.注释
@Entity
@Table(name = "stock_transaction", catalog = "mkyong")
@org.hibernate.annotations.Entity(
dynamicInsert = true
)
public class StockTransaction implements java.io.Serializable {
2.XML 映射
<class ... table="stock_transaction" catalog="mkyong" dynamic-insert="true">
<id name="tranId" type="java.lang.Integer">
<column name="TRAN_ID" />
<generator class="identity" />
</id>
结论
这个小小的"动态插入"调整可能会提高您的系统性能,强烈建议这样做。但是,我脑子里的一个问题是,Hibernate 为什么默认设置为 false?
追踪
1.hibernate-动态更新属性示例
hibernate–动态更新属性示例
什么是动态更新
动态更新属性告诉 Hibernate 是否在 SQL UPDATE 语句中包含未修改的属性。
动态更新示例
1.动态更新=假
dynamic-update 的默认值是 false,这意味着在 Hibernate 的 SQL update 语句中包含未修改的属性。
例如,获取一个对象并尝试修改它的值和更新它。
Query q = session.createQuery("from StockTransaction where tranId = :tranId ");
q.setParameter("tranId", 11);
StockTransaction stockTran = (StockTransaction)q.list().get(0);
stockTran.setVolume(4000000L);
session.update(stockTran);
Hibernate 将生成下面的更新 SQL 语句。
Hibernate:
update
mkyong.stock_transaction
set
DATE=?,
PRICE_CHANGE=?,
PRICE_CLOSE=?,
PRICE_OPEN=?,
STOCK_ID=?,
VOLUME=?
where
TRAN_ID=?
Hibernate 将更新所有未修改的列。
2.动态更新=真
如果将 dynamic-insert 设置为 true,这意味着在 Hibernate 的 SQL update 语句中排除未修改的属性。
例如,获取一个对象,尝试修改它的值并再次更新它。
Query q = session.createQuery("from StockTransaction where tranId = :tranId ");
q.setParameter("tranId", 11);
StockTransaction stockTran = (StockTransaction)q.list().get(0);
stockTran.setVolume(4000000L);
session.update(stockTran);
Hibernate 会生成不同的更新 SQL 语句。
Hibernate:
update
mkyong.stock_transaction
set
VOLUME=?
where
TRAN_ID=?
Hibernate 将只更新修改过的列。
Performance issue
In a large table with many columns (legacy design) or contains large data volumes, update some unmodified columns are absolutely unnecessary and great impact on the system performance.
如何配置
您可以通过注释或 XML 映射文件配置“dynamic-update
”属性。
1.注释
@Entity
@Table(name = "stock_transaction", catalog = "mkyong")
@org.hibernate.annotations.Entity(
dynamicUpdate = true
)
public class StockTransaction implements java.io.Serializable {
2.XML 映射
<class ... table="stock_transaction" catalog="mkyong" dynamic-update="true">
<id name="tranId" type="java.lang.Integer">
<column name="TRAN_ID" />
<generator class="identity" />
</id>
结论
这个小小的"动态更新"调整肯定会提高您的系统性能,强烈推荐这样做。
追踪
1.hibernate-动态插入属性示例
Tags : hibernate
相关文章
-
为什么我的项目选择 Hibernate?
休眠错误–需要使用 AnnotationConfiguration 实例
Hibernate 注释需要“AnnotationConfiguration”而不是普通的“Configuration()”来构建会话工厂。
INFO: Configuration resource: /hibernate.cfg.xml
Initial SessionFactory creation failed.org.hibernate.MappingException:
An AnnotationConfiguration instance is required to use <mapping class="com.mkyong.common.Stock"/>
Exception in thread "main" java.lang.ExceptionInInitializerError
at com.mkyong.persistence.HibernateUtil.buildSessionFactory(HibernateUtil.java:19)
at com.mkyong.persistence.HibernateUtil.<clinit>(HibernateUtil.java:8)
at com.mkyong.common.App.main(App.java:11)
Caused by: org.hibernate.MappingException: An AnnotationConfiguration instance is required to use <mapping class="com.mkyong.common.Stock"/>
at org.hibernate.cfg.Configuration.parseMappingElement(Configuration.java:1600)
at org.hibernate.cfg.Configuration.parseSessionFactory(Configuration.java:1555)
at org.hibernate.cfg.Configuration.doConfigure(Configuration.java:1534)
at org.hibernate.cfg.Configuration.doConfigure(Configuration.java:1508)
at org.hibernate.cfg.Configuration.configure(Configuration.java:1428)
at org.hibernate.cfg.Configuration.configure(Configuration.java:1414)
at com.mkyong.persistence.HibernateUtil.buildSessionFactory(HibernateUtil.java:13)
... 2 more
解决办法
1.下载 Hibernate 注释库
可以从 Hibernate 官网下载该库
或者
在 Maven 的 pom.xml 中添加依赖项
<!-- Hibernate annotation -->
<dependency>
<groupId>hibernate-annotations</groupId>
<artifactId>hibernate-annotations</artifactId>
<version>3.3.0.GA</version>
</dependency>
页(page 的缩写)为了下载 Hibernate 注释库,您可能需要包含 JBoss 存储库。
<repositories>
<repository>
<id>JBoss repository</id>
<url>http://repository.jboss.com/maven2/</url>
</repository>
</repositories>
2.使用 AnnotationConfiguration 构建会话工厂
正常的 Hibernate XML 文件映射使用配置()
return new Configuration().configure().buildSessionFactory();
对于 Hibernate 注释,您必须将其更改为“AnnotationConfiguration”
return new AnnotationConfiguration().configure().buildSessionFactory();
HibernateUtil.java
使用“AnnotationConfiguration”进行 Hibernate 注释应用的“HibernateUtil.java”的完整示例。
package com.mkyong.persistence;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
public class HibernateUtil {
private static final SessionFactory sessionFactory = buildSessionFactory();
private static SessionFactory buildSessionFactory() {
try {
// Create the SessionFactory from hibernate.cfg.xml
return new AnnotationConfiguration().configure().buildSessionFactory();
}
catch (Throwable ex) {
// Make sure you log the exception, as it might be swallowed
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
public static void shutdown() {
// Close caches and connection pools
getSessionFactory().close();
}
}
休眠错误–线程“main”Java . lang . noclassdeffounderror 中的异常:antlr/antl Exception
这是由于缺少 antlr 库造成的。这通常发生在你调用 Hibernate 的查询语句的时候。
Exception in thread "main" java.lang.NoClassDefFoundError: antlr/ANTLRException
at org.hibernate.hql.ast.ASTQueryTranslatorFactory.createQueryTranslator(ASTQueryTranslatorFactory.java:35)
at org.hibernate.engine.query.HQLQueryPlan.<init>(HQLQueryPlan.java:74)
at org.hibernate.engine.query.HQLQueryPlan.<init>(HQLQueryPlan.java:56)
at org.hibernate.engine.query.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:72)
at org.hibernate.impl.AbstractSessionImpl.getHQLQueryPlan(AbstractSessionImpl.java:133)
at org.hibernate.impl.AbstractSessionImpl.createQuery(AbstractSessionImpl.java:112)
at org.hibernate.impl.SessionImpl.createQuery(SessionImpl.java:1623)
at com.mkyong.common.App.main(App.java:23)
Caused by: java.lang.ClassNotFoundException: antlr.ANTLRException
at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
... 8 more
解决办法
可以从 Antlr 官网下载该库
或者
在 Maven 的 pom.xml 中添加依赖项
<dependency>
<groupId>antlr</groupId>
<artifactId>antlr</artifactId>
<version>2.7.7</version>
</dependency>
hibernate (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','//web.archive.org/web/20190308011741/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0')
休眠错误-初始会话工厂创建失败。Java . lang . noclassdeffounderror:net/SF/cglib/proxy/CallbackFilter
一个常见的 Hibernate 错误,这是由于缺少依赖库 cglib 造成的。
Initial SessionFactory creation failed.java.lang.NoClassDefFoundError: net/sf/cglib/proxy/CallbackFilter
Exception in thread "main" java.lang.ExceptionInInitializerError
at com.mkyong.persistence.HibernateUtil.buildSessionFactory(HibernateUtil.java:18)
at com.mkyong.persistence.HibernateUtil.<clinit>(HibernateUtil.java:8)
at com.mkyong.common.App.main(App.java:17)
Caused by: java.lang.NoClassDefFoundError: net/sf/cglib/proxy/CallbackFilter
at org.hibernate.bytecode.cglib.BytecodeProviderImpl.getProxyFactoryFactory(BytecodeProviderImpl.java:33)
at org.hibernate.tuple.entity.PojoEntityTuplizer.buildProxyFactoryInternal(PojoEntityTuplizer.java:182)
at org.hibernate.tuple.entity.PojoEntityTuplizer.buildProxyFactory(PojoEntityTuplizer.java:160)
at org.hibernate.tuple.entity.AbstractEntityTuplizer.<init>(AbstractEntityTuplizer.java:135)
at org.hibernate.tuple.entity.PojoEntityTuplizer.<init>(PojoEntityTuplizer.java:55)
at org.hibernate.tuple.entity.EntityEntityModeToTuplizerMapping.<init>(EntityEntityModeToTuplizerMapping.java:56)
at org.hibernate.tuple.entity.EntityMetamodel.<init>(EntityMetamodel.java:295)
at org.hibernate.persister.entity.AbstractEntityPersister.<init>(AbstractEntityPersister.java:434)
at org.hibernate.persister.entity.SingleTableEntityPersister.<init>(SingleTableEntityPersister.java:109)
at org.hibernate.persister.PersisterFactory.createClassPersister(PersisterFactory.java:55)
at org.hibernate.impl.SessionFactoryImpl.<init>(SessionFactoryImpl.java:226)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1294)
at com.mkyong.persistence.HibernateUtil.buildSessionFactory(HibernateUtil.java:13)
... 2 more
Caused by: java.lang.ClassNotFoundException: net.sf.cglib.proxy.CallbackFilter
at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
... 15 more
解决办法
你可以在这里下载图书馆-http://cglib.sourceforge.net/
或者
在 Maven 的 pom.xml 中添加依赖项
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2</version>
</dependency>
hibernate (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','//web.archive.org/web/20190110062218/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0')
休眠错误-初始会话工厂创建失败。Java . lang . noclassdeffounderror:org/Apache/commons/collections/sequenced hashmap
一个常见的 Hibernate 错误,这是由于缺少依赖库——Apache Common Collection 造成的。
Initial SessionFactory creation failed.java.lang.NoClassDefFoundError: org/apache/commons/collections/SequencedHashMap
Exception in thread "main" java.lang.ExceptionInInitializerError
at com.mkyong.persistence.HibernateUtil.buildSessionFactory(HibernateUtil.java:18)
at com.mkyong.persistence.HibernateUtil.<clinit>(HibernateUtil.java:8)
at com.mkyong.common.App.main(App.java:17)
Caused by: java.lang.NoClassDefFoundError: org/apache/commons/collections/SequencedHashMap
at org.hibernate.mapping.Table.<init>(Table.java:33)
at org.hibernate.cfg.Mappings.addTable(Mappings.java:165)
at org.hibernate.cfg.HbmBinder.bindRootPersistentClassCommonValues(HbmBinder.java:290)
at org.hibernate.cfg.HbmBinder.bindRootClass(HbmBinder.java:273)
at org.hibernate.cfg.HbmBinder.bindRoot(HbmBinder.java:144)
at org.hibernate.cfg.Configuration.add(Configuration.java:669)
at org.hibernate.cfg.Configuration.addInputStream(Configuration.java:504)
at org.hibernate.cfg.Configuration.addResource(Configuration.java:566)
at org.hibernate.cfg.Configuration.parseMappingElement(Configuration.java:1587)
at org.hibernate.cfg.Configuration.parseSessionFactory(Configuration.java:1555)
at org.hibernate.cfg.Configuration.doConfigure(Configuration.java:1534)
at org.hibernate.cfg.Configuration.doConfigure(Configuration.java:1508)
at org.hibernate.cfg.Configuration.configure(Configuration.java:1428)
at org.hibernate.cfg.Configuration.configure(Configuration.java:1414)
at com.mkyong.persistence.HibernateUtil.buildSessionFactory(HibernateUtil.java:13)
... 2 more
Caused by: java.lang.ClassNotFoundException: org.apache.commons.collections.SequencedHashMap
at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
... 17 more
解决办法
你可以在这里下载图书馆-http://commons.apache.org/collections/
或者
在 Maven 的 pom.xml 中添加依赖项
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
hibernate (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','//web.archive.org/web/20190302163458/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0')
休眠错误-初始会话工厂创建失败。Java . lang . noclassdeffounderror:org/Apache/commons/logging/log factory
一个常见的 Hibernate 错误,这是由于缺少依赖库——公共日志记录造成的。
Initial SessionFactory creation failed.java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory
Exception in thread "main" java.lang.ExceptionInInitializerError
at com.mkyong.persistence.HibernateUtil.buildSessionFactory(HibernateUtil.java:18)
at com.mkyong.persistence.HibernateUtil.<clinit>(HibernateUtil.java:8)
at com.mkyong.common.App.main(App.java:17)
Caused by: java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory
at org.hibernate.cfg.Configuration.<clinit>(Configuration.java:120)
at com.mkyong.persistence.HibernateUtil.buildSessionFactory(HibernateUtil.java:13)
... 2 more
Caused by: java.lang.ClassNotFoundException: org.apache.commons.logging.LogFactory
at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
... 4 more
解决办法
你可以在这里下载图书馆-http://commons.apache.org/logging/
或者
在 Maven 的 pom.xml 中添加依赖项
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
</dependency>
hibernate (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','//web.archive.org/web/20190305140402/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0')
休眠错误-初始会话工厂创建失败。Java . lang . noclassdeffounderror:org/dom4j/document exception
一个常见的 Hibernate 错误,这是由于缺少依赖库 dom4j 造成的。
Initial SessionFactory creation failed.java.lang.NoClassDefFoundError: org/dom4j/DocumentException
Exception in thread "main" java.lang.ExceptionInInitializerError
at com.mkyong.persistence.HibernateUtil.buildSessionFactory(HibernateUtil.java:18)
at com.mkyong.persistence.HibernateUtil.<clinit>(HibernateUtil.java:8)
at com.mkyong.common.App.main(App.java:17)
Caused by: java.lang.NoClassDefFoundError: org/dom4j/DocumentException
at com.mkyong.persistence.HibernateUtil.buildSessionFactory(HibernateUtil.java:13)
... 2 more
Caused by: java.lang.ClassNotFoundException: org.dom4j.DocumentException
at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
... 3 more
解决办法
你可以在这里下载图书馆-http://sourceforge.net/projects/dom4j/files/dom4j/
或者
在 Maven 的 pom.xml 中添加依赖项
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
hibernate (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','//web.archive.org/web/20190301211220/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0')
Hibernate 错误-初始会话工厂创建失败。Java . lang . noclassdeffounderror:org/hibernate/annotations/common/reflection/reflection manager
这是由于缺少 Hibernate commons 注释库造成的。
Initial SessionFactory creation failed.java.lang.NoClassDefFoundError:
org/hibernate/annotations/common/reflection/ReflectionManager
Exception in thread "main" java.lang.ExceptionInInitializerError
at com.mkyong.persistence.HibernateUtil.buildSessionFactory(HibernateUtil.java:19)
at com.mkyong.persistence.HibernateUtil.<clinit>(HibernateUtil.java:8)
at com.mkyong.common.App.main(App.java:11)
Caused by: java.lang.NoClassDefFoundError: org/hibernate/annotations/common/reflection/ReflectionManager
at com.mkyong.persistence.HibernateUtil.buildSessionFactory(HibernateUtil.java:13)
... 2 more
Caused by: java.lang.ClassNotFoundException: org.hibernate.annotations.common.reflection.ReflectionManager
at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
... 3 more
解决办法
可以从 Hibernate 官网下载该库
或者
在 Maven 的 pom.xml 中添加依赖项
<dependency>
<groupId>hibernate-commons-annotations</groupId>
<artifactId>hibernate-commons-annotations</artifactId>
<version>3.0.0.GA</version>
</dependency>
页(page 的缩写)为了下载 Hibernate 公共注释库,您可能需要包含 JBoss 存储库。
<repositories>
<repository>
<id>JBoss repository</id>
<url>http://repository.jboss.com/maven2/</url>
</repository>
</repositories>
hibernate (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','//web.archive.org/web/20190203090236/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0')
休眠错误–Java . lang . noclasdeffounderror:javax/transaction/synchron ization
问题
这是由于缺少“jta.jar
”造成的,通常发生在 Hibernate 事务开发中。
java.lang.NoClassDefFoundError: javax/transaction/Synchronization
at org.hibernate.impl.SessionImpl.<init>(SessionImpl.java:213)
at org.hibernate.impl.SessionFactoryImpl.openSession(SessionFactoryImpl.java:473)
at org.hibernate.impl.SessionFactoryImpl.openSession(SessionFactoryImpl.java:497)
at org.hibernate.impl.SessionFactoryImpl.openSession(SessionFactoryImpl.java:505)
at com.mkyong.common.App.main(App.java:13)
Caused by: java.lang.ClassNotFoundException: javax.transaction.Synchronization
at java.net.URLClassLoader$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClassInternal(Unknown Source)
... 5 more
解决办法
你可以从默认的 Maven central、JBoss 或 Java.net 库下载“jta.jar
”。
1.Maven 中央存储库
<dependencies>
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>jta</artifactId>
<version>1.1</version>
</dependency>
</dependencies>
2.JBoss Maven 资源库
添加 JBoss Maven 存储库
<repositories>
<repository>
<id>JBoss repository</id>
<url>http://repository.jboss.com/maven2/</url>
</repository>
</repositories>
并定义“jta.jar
”明细。
<dependencies>
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>jta</artifactId>
<version>1.1</version>
</dependency>
</dependencies>
3.Java net Maven 知识库
添加 Java net Maven 资源库
<repositories>
<repository>
<id>Java 2</id>
<url>http://download.java.net/maven/2/</url>
</repository>
</repositories>
并定义“ jta1.0.1B.jar
”明细。
<dependencies>
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>jta</artifactId>
<version>1.0.1B</version>
</dependency>
</dependencies>
Note
Alternately, you can include the “javaee.jar“, which can be found under J2EE SDK’s folder. The “javaee.jar” contains “javax/transaction/Synchronization
” class as well.hibernate