6.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="userForm"
type="com.mkyong.common.form.UserForm"/>
</form-beans>
<action-mappings>
<action
path="/LoginPage"
type="org.apache.struts.actions.ForwardAction"
parameter="/pages/multi-language.jsp"/>
<action
path="/Submit"
type="com.mkyong.common.action.LanguageSelectAction"
name="userForm"
validate="true"
input="/pages/multi-language.jsp"
>
<forward name="success" path="/pages/multi-language.jsp"/>
</action>
<action
path="/Locale"
type="com.mkyong.common.action.LanguageSelectAction"
name="userForm"
parameter="method"
validate="false"
>
<forward name="success" path="/pages/multi-language.jsp"/>
</action>
</action-mappings>
<message-resources
parameter="com.mkyong.common.properties.Common" />
</struts-config>
7 .好的. xml
将 Struts 框架集成到 web 部署描述符文件中。
<!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>
</web-app>
屏幕截图
http://localhost:8080/struts example/log in page . do
您可以通过语言链接更改界面。
英文界面
中文界面
法国界面
德语界面
Tags : multiple languages struts
Struts + Log4j 集成示例
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/struts/struts-log4j-integration-example/
在本教程中,我们将向您展示如何将 log4j 框架与传统的 Struts 1.3.x web 应用程序集成。没有额外的工作,只需包含log4j.jar
并创建一个log4j.xml
或log4j.properties
文件,并将其放入类路径的根目录(对于 Maven,将其放入 resources 文件夹)。
使用的技术和工具:
Log4j 1.2.17
Struts 1.3.10
maven3
tomcat6
日食开普勒 4.3
1.项目目录
审查最终的项目结构。
2.项目相关性
声明 Struts 和 log4j 依赖关系:
pom.xml
<properties>
<struts.version>1.3.10</struts.version>
<log4j.version>1.2.17</log4j.version>
</properties>
<dependencies>
<!-- Struts 1.x -->
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts-core</artifactId>
<version>${struts.version}</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts-taglib</artifactId>
<version>${struts.version}</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts-extras</artifactId>
<version>${struts.version}</version>
</dependency>
<!-- Log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<!-- Need this for web -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
</dependencies>
3.log4j.xml
创建一个 log4j XML 文件,放入resources
文件夹,参考步骤#1。它告诉 log4j 将日志消息重定向到控制台和一个文件。
log4j.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration debug="true"
xmlns:log4j='http://jakarta.apache.org/log4j/'>
<!-- Console -->
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern"
value="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n" />
</layout>
</appender>
<!-- file -->
<appender name="file" class="org.apache.log4j.RollingFileAppender">
<param name="append" value="false" />
<param name="maxFileSize" value="10KB" />
<param name="maxBackupIndex" value="5" />
<param name="file" value="${catalina.home}/logs/myStruts1App.log" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern"
value="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n" />
</layout>
</appender>
<root>
<level value="DEBUG" />
<appender-ref ref="console" />
<appender-ref ref="file" />
</root>
</log4j:configuration>
4.消息记录
一个返回页面的简单操作,并向您展示了如何使用 log4j 进行日志记录。
WelcomeAction.java
package com.mkyong.common.action;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
public class WelcomeAction extends Action{
//Get a logger
private static final Logger logger = Logger.getLogger(WelcomeAction.class);
public ActionForward execute(ActionMapping mapping,ActionForm form,
HttpServletRequest request,HttpServletResponse response)
throws Exception {
//logs debug
if(logger.isDebugEnabled()){
logger.debug("WelcomeAction.execute()");
}
//logs exception
logger.error("This is Error message", new Exception("Testing"));
return mapping.findForward("success");
}
}
5.Struts 1 配置
简单的 Struts 1 配置等。
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>Log4j + Struts Web Application</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>
</web-app>
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="com.mkyong.common.action.WelcomeAction"
>
<forward name="success" path="/pages/welcome.jsp"/>
</action>
</action-mappings>
</struts-config>
pages/welcome.jsp
<%@taglib uri="http://struts.apache.org/tags-bean" prefix="bean"%>
<html>
<head>
</head>
<body>
<h1>
Struts 1.x + Log4j framework
</h1>
</body>
</html>
6.演示
运行 Struts 1 web 应用程序,并访问欢迎操作。
网址:http://localhost:8080/log 4 jandstruts 1/welcome . do
6.1 Eclipse 控制台。
6.2 此外,将在 Tomcat 的 logs 文件夹中创建一个日志文件。
图:D:\ Apache-Tomcat-6 . 0 . 37 \ logs \ mystruts 1 app . log
下载源代码
Download it – Log4jAndStrutsExample.zip (11 KB)
参考
log4j 1.2 官方页面
log4j hello world 示例
Struts 2 异常和日志记录
struts-&示例
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/struts/struts-logic-empty-logic-notempty-example/
Download this example – Struts-Logic-Empty-NotEmpty-Example.zip
Struts 仅在指定属性为空、零长度字符串或不存在时执行;而 Struts 正在做相反的事情。如果条件匹配,标签的主体将被执行。
下面的例子展示了 Struts & 的使用,并用下面的三个列表进行测试。
list msg 0–包含值的列表。
list msg 1–空列表。
list msg 2–一个不存在的列表
LogicExampleAction.java
package com.mkyong.common.action;
import java.util.ArrayList;
import java.util.List;
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;
public class LogicExampleAction extends Action{
public ActionForward execute(ActionMapping mapping,ActionForm form,
HttpServletRequest request,HttpServletResponse response)
throws Exception {
//listMsg0 - A list contains values
List<String> listMsg0 = new ArrayList<String>();
listMsg0.add("Message A");
listMsg0.add("Message B");
listMsg0.add("Message C");
listMsg0.add("Message D");
request.setAttribute("listMsg0", listMsg0);
//listMsg1 - An empty list
List<String> listMsg1 = new ArrayList<String>();
request.setAttribute("listMsg1", listMsg1);
//listMsg2 - A list which is doesn't exists
return mapping.findForward("success");
}
}
LogicExample.jsp
<%@taglib uri="http://struts.apache.org/tags-html" prefix="html"%>
<%@taglib uri="http://struts.apache.org/tags-bean" prefix="bean"%>
<%@taglib uri="http://struts.apache.org/tags-logic" prefix="logic"%>
Struts -测试
## listMag0 为空
## listMag1 为空
## listMag2 为空
Struts -测试
## listMag0 不为空
<iterate name="listMsg0" id="listMsgId">列出消息 0 -</iterate>
## listMag1 不为空
<iterate name="listMsg1" id="listMsgId">列出消息 1 -</iterate>
## listMag2 不为空
<iterate name="listMsg2" id="listMsgId">列出消息 2 -</iterate>
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="/LogicTest"
type="com.mkyong.common.action.LogicExampleAction">
<forward name="success" path="/pages/LogicExample.jsp"/>
</action>
</action-mappings>
</struts-config>
结果
http://localhost:8080/struts example/logictest . do
在 Struts–Test<逻辑:empty > 中,只显示 listMsg1 和 listMsg2,这是因为 listMag1 是一个空列表,而 listMag2 根本不存在。
在 Struts–Test<逻辑中:notEmpty > ,只显示 listMsg0,因为这是唯一包含值的列表。
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/20190213170100/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0 ')
struts-&示例
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/struts/struts-logic-equal-logicnotequal-example/
Download this example – Struts-Logic-Equal-NotEqual-Example.zip
Struts 用于检查给定属性是否等于给定值;而 Struts 用于检查给定属性是否等于给定值,如果条件匹配,标签的主体将被执行。
下面的例子展示了 Struts & 的用法。
User.java
package com.mkyong.common;
public class User{
String email;
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
LogicExampleAction.java
package com.mkyong.common.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.common.User;
public class LogicExampleAction extends Action{
public ActionForward execute(ActionMapping mapping,ActionForm form,
HttpServletRequest request,HttpServletResponse response)
throws Exception {
User user = new User();
user.setEmail("mkyong123456@yahoo.com");
request.setAttribute("user", user);
return mapping.findForward("success");
}
}
LogicExample.jsp
<%@taglib uri="http://struts.apache.org/tags-bean" prefix="bean"%>
<%@taglib uri="http://struts.apache.org/tags-logic" prefix="logic"%>
Struts -测试<equal></equal>
## 用户的电子邮件是平等的
## Struts -测试<notequal></notequal>
<notequal name="user" property="email" value="mkyong654321@yahoo.com">## 用户的电子邮件不相等</notequal>
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="/LogicTest"
type="com.mkyong.common.action.LogicExampleAction">
<forward name="success" path="/pages/LogicExample.jsp"/>
</action>
</action-mappings>
</struts-config>
结果
http://localhost:8080/struts example/logictest . do
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/20190224164320/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0 ')
Struts 示例
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/struts/struts-logic-iterate-example/
在 Struts 中,可以使用 <逻辑:iterate > 标签迭代集合。这里有两个例子:
遍历列表(原始类型)
遍历列表(对象)
1.迭代列表数组(原始类型)
用一些虚拟字符串创建一个普通列表,并存储到HttpServletRequest
,命名为“ listMsg ”。
...
public class PrintMsgAction extends Action{
public ActionForward execute(ActionMapping mapping,ActionForm form,
HttpServletRequest request,HttpServletResponse response)
throws Exception {
List<String> listMsg = new ArrayList<String>();
listMsg.add("Message A");
listMsg.add("Message B");
listMsg.add("Message C");
listMsg.add("Message D");
request.setAttribute("listMsg", listMsg);
return mapping.findForward("success");
}
}
在逻辑标记中,可以使用“name”属性(listMsg)来获取列表值。
<%@taglib uri="http://struts.apache.org/tags-bean" prefix="bean"%>
<%@taglib uri="http://struts.apache.org/tags-logic" prefix="logic"%>
<html>
<head>
</head>
<body>
<h1>Struts <logic:iterate> example</h1>
<logic:iterate name="listMsg" id="listMsgId">
<p>
List Messages <bean:write name="listMsgId"/>
</p>
</logic:iterate>
</body>
</html>
2.遍历列表数组(对象)
创建一个包含少量“用户”对象的普通列表,并将其存储到HttpServletRequest
中,命名为“列表用户 ”。
public class User{
String username;
String url;
//getter and setter methods
}
...
public class PrintMsgAction extends Action{
public ActionForward execute(ActionMapping mapping,ActionForm form,
HttpServletRequest request,HttpServletResponse response)
throws Exception {
List<User> listUsers = new ArrayList<User>();
listUsers.add(new User("user1", "http://www.user1.com"));
listUsers.add(new User("user2", "http://www.user2.com"));
listUsers.add(new User("user3", "http://www.user3.com"));
listUsers.add(new User("user4", "http://www.user4.com"));
request.setAttribute("listUsers", listUsers);
return mapping.findForward("success");
}
}
在逻辑标签内部,可以使用“ name ”属性(listUsers)来获取列表值;而属性 属性则显示对象属性值。
<%@taglib uri="http://struts.apache.org/tags-bean" prefix="bean"%>
<%@taglib uri="http://struts.apache.org/tags-logic" prefix="logic"%>
<html>
<head>
</head>
<body>
<h1>Struts <logic:iterate> example</h1>
<logic:iterate name="listUsers" id="listUserId">
<p>
List Users <bean:write name="listUserId" property="username"/> ,
<bean:write name="listUserId" property="url"/>
</p>
</logic:iterate>
</body>
</html>
## 下载源代码
Download it – Struts-logic-Iterate-example.zip struts
struts-示例
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/struts/struts-logic-messages-present-logic-messages-notpresent-example/
Download this example – Struts-Logic-MessagePresent-NotPresent-Example.zip
Struts 标签用于检查当前请求上存在的给定消息或错误消息。
“消息”是全局键下的操作消息。当前请求中的 MESSAGE_KEY。
“错误消息”是 ActionErrors,在关键字 Globals 下。当前请求中的 ERROR_KEY。
这里有几个例子来展示和的用法。
如果有任何错误消息或消息存在于关键字“Globals”下。ERROR_KEY "或" Globals。MESSAGE_KEY”,标签的主体将被执行。
There are errors on this page!
There are no errors on this page!
如果在关键字“Globals”下存在任何错误消息或名为“common.email.err”的消息。ERROR_KEY”,标签的主体将被执行。
Email address has error messages! Globals.ERROR_KEY
Email address has no error messages! - Globals.ERROR_KEY
如果在关键字“Globals”下存在任何错误消息或名为“common.email.err”的消息。MESSAGE_KEY”,标签的主体将被执行。
Email address has error messages! - Globals.MESSAGE_KEY
Email address has no error messages! - Globals.MESSAGE_KEY
LogicExampleAction.java
package com.mkyong.common.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;
public class LogicExampleAction extends Action{
public ActionForward execute(ActionMapping mapping,ActionForm form,
HttpServletRequest request,HttpServletResponse response)
throws Exception {
//do nothing
return mapping.findForward("success");
}
}
EmailForm.java –返回错误消息的动作表单–动作错误。
package com.mkyong.common.form;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionMessage;
public class EmailForm extends ActionForm{
String email;
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public ActionErrors validate(ActionMapping mapping,
HttpServletRequest request) {
ActionErrors errors = new ActionErrors();
errors.add("common.email.err",
new ActionMessage("error.common.email.required"));
return errors;
}
}
公共属性
#common module error message
error.common.email.required = Email is required.
LogicExample.jsp
<%@taglib uri="http://struts.apache.org/tags-bean" prefix="bean"%>
<%@taglib uri="http://struts.apache.org/tags-logic" prefix="logic"%>
Struts - <messagespresent> & <messagesnotpresent></messagesnotpresent></messagespresent>
There are errors on this page!
There are no errors on this page!
Email address has error messages! Globals.ERROR_KEY
Email address has no error messages! - Globals.ERROR_KEY
Email address has error messages! - Globals.MESSAGE_KEY
Email address has no error messages! - Globals.MESSAGE_KEY
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="emailForm"
type="com.mkyong.common.form.EmailForm"></form-bean>
</form-beans>
<action-mappings>
<action
path="/LogicTest"
type="com.mkyong.common.action.LogicExampleAction"
name="emailForm"
validate="true"
input="/pages/LogicExample.jsp"
>
<forward name="success" path="/pages/LogicExample.jsp"/>
</action>
</action-mappings>
<message-resources
parameter="com.mkyong.common.Common" />
</struts-config>
结果
http://localhost:8080/struts example/logictest . do
Struts - <logic:messagesPresent> & <logic:messagesNotPresent>
There are errors on this page!
Email address has error messages! Globals.ERROR_KEY
Email address has no error messages! - Globals.MESSAGE_KEY
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/20190209023138/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0 ')
struts-示例
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/struts/struts-logic-present-logic-notpresent-example/
Download this example – Struts-Logic-Present-NotPresent-Example.zip
Struts 标签用于检查给定的对象或属性是否存在或存在于当前请求中;而则反其道而行。
这里有一个例子来展示和的用法。
User.java –一个用户类包含一个 url 属性。
package com.mkyong.common;
public class User{
String url;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
LogicExampleAction.java –初始化用户对象,设置 url 属性,并将其存储到请求会话中。
package com.mkyong.common.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.common.User;
public class LogicExampleAction extends Action{
public ActionForward execute(ActionMapping mapping,ActionForm form,
HttpServletRequest request,HttpServletResponse response)
throws Exception {
User user = new User();
user.setUrl("http://www.mkyong.com");
request.setAttribute("user", user);
return mapping.findForward("success");
}
}
LogicExample.jsp
<%@taglib uri="http://struts.apache.org/tags-bean" prefix="bean"%>
<%@taglib uri="http://struts.apache.org/tags-logic" prefix="logic"%>
Struts - <present>&</present>
User object is exists.
User object does not exists.
Abc object is exists.
Abc object does not exists.
User object, url property is exists.
User object, url property does not exists.
User object, email property is exists.
User object, email property does not exists.
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="/LogicTest"
type="com.mkyong.common.action.LogicExampleAction"
>
<forward name="success" path="/pages/LogicExample.jsp"/>
</action>
</action-mappings>
</struts-config>
结果
http://localhost:8080/struts example/logictest . do
Struts - <logic:present> & <logic:notPresent>
User object is exists.
Abc object does not exists.
User object, url property is exists.
User object, email property does not exists.
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/20190224153322/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0 ')
struts-示例
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/struts/struts-logicgreaterthan-logicgreaterequal-logiclessthan-logiclessequal-example/
Download this example – Struts-Logic-Number-Condition-Tag-Example.zip
在 Struts 中,有四个数字条件标签可用…
–检查给定属性是否大于给定值。
–检查给定属性是否大于或等于给定值。
–检查给定属性是否小于给定值。
–检查给定属性是否小于或等于给定值。
如果条件匹配,标签的主体将被执行。
下面的例子展示了 Struts 、、和的用法。
LogicExampleAction.java
package com.mkyong.common.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;
public class LogicExampleAction extends Action{
public ActionForward execute(ActionMapping mapping,ActionForm form,
HttpServletRequest request,HttpServletResponse response)
throws Exception {
request.setAttribute("number", 100);
return mapping.findForward("success");
}
}
LogicExample.jsp
<%@taglib uri="http://struts.apache.org/tags-bean" prefix="bean"%>
<%@taglib uri="http://struts.apache.org/tags-logic" prefix="logic"%>
Struts - <greaterthan></greaterthan>
## 数字 100 > 99 =真
## Struts - <greaterequal></greaterequal>
<greaterequal name="number" value="100">## 数字 100 >= 100 =真</greaterequal>
## Struts - <lessthan></lessthan>
<lessthan name="number" value="101">## 数字 100 < 101 =真</lessthan>
## Struts - <lessequal></lessequal>
<lessequal name="number" value="100">## 数字 100 <= 100 =真</lessequal>
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="/LogicTest"
type="com.mkyong.common.action.LogicExampleAction">
<forward name="success" path="/pages/LogicExample.jsp"/>
</action>
</action-mappings>
</struts-config>
结果
http://localhost:8080/struts example/logictest . do
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/20190209024221/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0 ')
struts-示例
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/struts/struts-logicmatch-logicnotmatch-example/
Download this example – Struts-Logic-Match-NotMatch-Example.zip
Struts 的标签用于检查给定属性包含的给定值为的子串 。例如,一个属性结果“Google 搜索引擎”,值“gle”将匹配,而值“ABC”将不匹配。如果条件匹配,标签的主体将被执行。Struts <逻辑:notMatch >正在做相反的事情。
Struts 的匹配标记有一个必须知道的属性,名为" location ",值为" start 或" end
location = " start " –仅当给定值作为给定属性的起始子串出现时才匹配。例如“谷歌搜索引擎”——匹配“Goog”,不匹配“gine”。
location = " end " –仅当给定值作为给定属性的结束子串出现时才匹配。例如“谷歌搜索引擎”——不匹配“Goog”,匹配“gine”。
无位置定义 –如果给定值作为给定属性的子串出现,则匹配。例如“谷歌搜索引擎”——匹配“Goog”,匹配“gine”。
下面的例子展示了 & 的用法。
LogicExampleAction.java
package com.mkyong.common.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;
public class LogicExampleAction extends Action{
public ActionForward execute(ActionMapping mapping,ActionForm form,
HttpServletRequest request,HttpServletResponse response)
throws Exception {
request.setAttribute("email", "mkyong123456@yahoo.com");
return mapping.findForward("success");
}
}
LogicExample.jsp
<%@taglib uri="http://struts.apache.org/tags-bean" prefix="bean"%>
<%@taglib uri="http://struts.apache.org/tags-logic" prefix="logic"%>
Struts - <match> & <notmatch></notmatch></match>
Email - mkyong123456@yahoo.com
1\. Is "yong" is a substring of the email? -
true
false
2\. Is "yongABC" is a substring of the email? -
true
false
3\. Is email start with "mkyong"? -
true
false
4.. Is email start with "yong"? -
true
false
5\. Is email end with "com"? -
true
false
6\. Is email end with "net"? -
true
false
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="/LogicTest"
type="com.mkyong.common.action.LogicExampleAction">
<forward name="success" path="/pages/LogicExample.jsp"/>
</action>
</action-mappings>
</struts-config>
结果
http://localhost:8080/struts example/logictest . do
Struts - <logic:match> & <logic:notMatch>
Email - mkyong123456@yahoo.com
1\. Is "yong" is a substring of the email? - true
2\. Is "yongABC" is a substring of the email? - false
3\. Is email start with "mkyong"? - true
4.. Is email start with "yong"? - false
5\. Is email end with "com"? - true
6\. Is email end with "net"? - false
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/20190209024038/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0 ')
struts–MappingDispatchAction 示例
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/struts/struts-mappingdispatchaction-example/
Struts MappingDispatchAction 类用于将相似的功能分组到单个 Action 类中,并根据相应 ActionMapping 的参数属性执行该功能。下面的示例展示了 MappingDispatchAction 的用法。
Download this example – Struts-MappingDispatchAction-Example.zip
1.MappingDispatchAction 类
扩展 MappingDispatchAction 类,并声明两个方法—generate XML() 和 generateExcel() 。
MyCustomDispatchAction.java
package com.mkyong.common.action;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.actions.MappingDispatchAction;
public class MyCustomDispatchAction extends MappingDispatchAction{
public ActionForward generateXML(ActionMapping mapping,ActionForm form,
HttpServletRequest request,HttpServletResponse response)
throws Exception {
request.setAttribute("method", "generateXML is called");
return mapping.findForward("success");
}
public ActionForward generateExcel(ActionMapping mapping,ActionForm form,
HttpServletRequest request,HttpServletResponse response)
throws Exception {
request.setAttribute("method", "generateExcel is called");
return mapping.findForward("success");
}
}
2.Struts 配置
声明两个操作映射,每个映射指向具有不同参数属性的同一个 MyCustomDispatchAction 类。
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="/CustomDispatchActionXML"
type="com.mkyong.common.action.MyCustomDispatchAction"
parameter="generateXML"
>
<forward name="success" path="/pages/DispatchExample.jsp"/>
</action>
<action
path="/CustomDispatchActionExcel"
type="com.mkyong.common.action.MyCustomDispatchAction"
parameter="generateExcel"
>
<forward name="success" path="/pages/DispatchExample.jsp"/>
</action>
<action
path="/Test"
type="org.apache.struts.actions.ForwardAction"
parameter="/pages/TestForm.jsp"
>
</action>
</action-mappings>
</struts-config>
3.查看页面
在 JSP 页面中,链接的工作方式如下:
/CustomDispatchActionXML 将执行 generateXML() 方法。
2。/CustomDispatchActionExcel 将执行 generateExcel() 方法。
TestForm.jsp
<%@taglib uri="http://struts.apache.org/tags-html" prefix="html"%>
Struts - DispatchAction 示例
html:链接
<link action="/CustomDispatchActionXML">生成 XML 文件| <link action="/CustomDispatchActionExcel">生成 Excel 文件
a href
[生成 XML 文件](CustomDispatchActionXML.do) | [生成 Excel 文件](CustomDispatchActionExcel.do)
DispatchExample.jsp
<%@taglib uri="http://struts.apache.org/tags-bean" prefix="bean"%>
<%@taglib uri="http://struts.apache.org/tags-logic" prefix="logic"%>
Struts - DispatchAction 示例
4.测试一下
http://localhost:8080/struts example/test . do
如果点击“生成 XML 文件 链接,会转发到http://localhost:8080/struts example/customdispatchactionxml . do
如果点击“生成 Excel 文件 链接,会转发到http://localhost:8080/struts example/customdispatchactionexcel . do
struts
struts–多个配置文件示例
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/struts/struts-multiple-configuration-files-example/
许多开发人员喜欢将所有与 Struts 相关的东西(动作、表单)放在一个 Struts 配置文件中。这对于最初的开发来说很快,但是对于将来的维护来说却很糟糕,而且可能那些开发人员并没有意识到 Struts 允许多个配置文件的特性。
6 years ago, I had joined a large Struts development project which involve 20+ modules. Unfortunately, the prior developers put all the Struts related stuff (action, form and etc) into a single Struts configuration file (struts-config.xml ). The struts-config.xml just keep growing extremely fast and finally hit 20++mb, every update to this configuration file will take few minutes, and even wait half an hour for a single debugging deployment in Eclipse IDE. This is a serious performance issue and causing the project keep delay the production date. What a good Struts development experience.
请将 Struts 配置细节拆分到不同的模块中,Struts 可以轻松完成。
Struts 多配置文件示例
这是用于演示的示例项目结构。
## 1.单一模块
单个模块支持多个 Struts 配置文件。
page1.jsp
这是第一页
page2.jsp
这是第二页
struts-config-1.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="/Page1"
type="org.apache.struts.actions.ForwardAction"
parameter="/pages/page1.jsp"/>
</action-mappings>
</struts-config>
struts-config-2.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="/Page2"
type="org.apache.struts.actions.ForwardAction"
parameter="/pages/page2.jsp"/>
</action-mappings>
</struts-config>
在 web.xml 中,可以用逗号“、 ”分隔多个 Struts 配置文件。
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-1.xml, /WEB-INF/struts-config-2.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>
</web-app>
测试一下
http://localhost:8080/struts example/page 1 . do
将显示 Page1.do
http://localhost:8080/struts example/common/welcome . do
它将显示 page2.jsp
两种 Struts 配置都是 loaded 属性。
2.多个模块
多个模块,每个模块都有自己的 Struts 配置文件。
admin/welcome.jsp
欢迎来到管理页面
common/welcome.jsp
欢迎来到公共页面
“ struts-config-admin.xml ”和“ struts-config-admin.xml ”两个文件包含相同的设置,struts 能够通过 web.xml 中的“ config 参数值对其进行区分
In Struts 2, the “Namespace ” is a more efficient way to replace this “config parameter ” setting.
struts-config-admin.xml,struts-config-admin.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="/welcome.jsp"/>
</action-mappings>
</struts-config>
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-1.xml, /WEB-INF/struts-config-2.xml
</param-value>
</init-param>
<init-param>
<param-name>config/admin</param-name>
<param-value>
/WEB-INF/struts-config-admin.xml
</param-value>
</init-param>
<init-param>
<param-name>config/common</param-name>
<param-value>
/WEB-INF/struts-config-common.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>
</web-app>
测试一下
“ config/admin ”将匹配此 URL 模式-http://localhost:8080/struts example/admin/
“config/common ”将匹配此 URL 模式-http://localhost:8080/struts example/common/
http://localhost:8080/struts example/admin/welcome . do
它会显示 admin/welcome.jsp
http://localhost:8080/struts example/common/welcome . do
它会显示 common/welcome.jsp
每个模块都有自己的 Struts 配置文件。
下载源代码
Download it – Struts-Mutiple-Config-File-Example.zip struts
Struts 1 + Quartz 2 调度器集成示例
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/struts/struts-quartz-scheduler-integration-example/
在本教程中,我们将向您展示如何使用 Apache Struts 1.x 创建一个简单的 web 应用程序项目,并与 Quartz scheduler 框架集成。在项目启动期间,它将启动 Quartz 引擎并触发定义的调度程序任务。
总体思路非常简单,您只需要为集成工作创建一个 Quartz Struts 插件 。
Struts <--> Quartz Struts Plug-in <--> Quartz
使用的工具:
Apache Struts 1.3.10
石英 2.1.5
Eclipse IDE
专家
Note
Please visit this Quartz 2 scheduler example to learn the basic usage of the Quartz framework.
1.属国
通过 Maven 获取 Struts 和 Quartz 依赖项。
文件:pom.xml
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>servlet-api</artifactId>
<version>6.0.35</version>
</dependency>
<!-- Struts 1.3 framework -->
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts-core</artifactId>
<version>1.3.10</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts-taglib</artifactId>
<version>1.3.10</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts-extras</artifactId>
<version>1.3.10</version>
</dependency>
<!-- Quartz framework -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.1.5</version>
</dependency>
2.Quartz 调度程序作业
创建一个 Quartz 作业来打印消息。
文件:SchedulerJob.java
package com.mkyong.common.quartz;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class SchedulerJob implements Job
{
public void execute(JobExecutionContext context)
throws JobExecutionException {
System.out.println("Print Print Struts 1.3 + Quartz 2.1.5 integration example ~");
}
}
3.Struts 插件
创建一个简单的 Struts 插件并集成 Quartz 框架。在这个插件中,在 Struts 初始化期间,init()
方法将被调用,并每 5 秒启动一次 Quartz 调度程序作业。
文件:QuartzPlugin.java
package com.mkyong.common.plugin;
import javax.servlet.ServletException;
import org.apache.struts.action.ActionServlet;
import org.apache.struts.action.PlugIn;
import org.apache.struts.config.ModuleConfig;
import org.quartz.CronScheduleBuilder;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
import com.mkyong.common.quartz.SchedulerJob;
public class QuartzPlugin implements PlugIn {
@Override
public void destroy() {
// null
}
@Override
public void init(ActionServlet servlet, ModuleConfig config)
throws ServletException {
JobDetail job = JobBuilder.newJob(SchedulerJob.class)
.withIdentity("anyJobName", "group1").build();
try {
Trigger trigger = TriggerBuilder
.newTrigger()
.withIdentity("anyTriggerName", "group1")
.withSchedule(
CronScheduleBuilder.cronSchedule("0/5 * * * * ?"))
.build();
Scheduler scheduler = new StdSchedulerFactory().getScheduler();
scheduler.start();
scheduler.scheduleJob(job, trigger);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
}
4.声明插件
将上面的 Struts 插件 包含到 Struts 配置文件(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>
<action-mappings>
<action path="/Welcome" type="org.apache.struts.actions.ForwardAction"
parameter="/pages/quartz_started.jsp" />
</action-mappings>
<plug-in className="com.mkyong.common.plugin.QuartzPlugin" />
</struts-config>
5.演示
在 Struts 启动期间,触发 Quartz 调度程序任务。
下载源代码
Download it – Struts-Quartz-2-Example.zip (24 KB)
参考
石英官网
Quartz 2 调度器示例
quartz scheduler struts
Struts + Spring + Hibernate 集成示例
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/struts/struts-spring-hibernate-integration-example/
Download this Struts 1.x + Spring + Hibernate example – Struts-Spring-Hibernate-Example.zip
在本教程中,您将学习如何创建一个简单的客户管理(添加和选择)web 应用程序,Maven 作为项目管理工具,Struts 1.x 作为 web 框架,Spring 作为依赖注入框架,Hibernate 作为数据库 ORM 框架。
整体集成架构如下所示:
Struts (Web page) <---> Spring DI <--> Hibernate (DAO) <---> Database
要将所有这些技术集成在一起,您应该..
用 Spring 的" LocalSessionFactoryBean "类集成 Spring 和 Hibernate。
通过 Spring 的 ready make Struts 插件将 Spring 与 Struts 集成起来。
1.项目结构
这是最终的项目结构。
## 2.表格脚本
创建一个客户表来存储客户详细信息。
DROP TABLE IF EXISTS `mkyong`.`customer`;
CREATE TABLE `mkyong`.`customer` (
`CUSTOMER_ID` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`NAME` varchar(45) NOT NULL,
`ADDRESS` varchar(255) NOT NULL,
`CREATED_DATE` datetime NOT NULL,
PRIMARY KEY (`CUSTOMER_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;
3.Maven 详细信息
在 pom.xml.
pom.xml 中定义所有的 Struts、Spring 和 Hibernate 依赖库
<project
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mkyong.common</groupId>
<artifactId>StrutsSpringExample</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>StrutsExample Maven Webapp</name>
<url>http://maven.apache.org</url>
<repositories>
<repository>
<id>Java.Net</id>
<url>http://download.java.net/maven/2/</url>
</repository>
<repository>
<id>JBoss repository</id>
<url>http://repository.jboss.com/maven2/</url>
</repository>
</repositories>
<dependencies>
<!-- Spring framework -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
<version>2.5.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>2.5.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-struts</artifactId>
<version>2.0.8</version>
</dependency>
<!-- J2EE library -->
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>6.0</version>
</dependency>
<!-- Unit Test -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!-- Struts 1.3 framework -->
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts-core</artifactId>
<version>1.3.10</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts-taglib</artifactId>
<version>1.3.10</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts-extras</artifactId>
<version>1.3.10</version>
</dependency>
<!-- MySQL database driver -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.9</version>
</dependency>
<!-- Hibernate core -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate</artifactId>
<version>3.2.7.ga</version>
</dependency>
<!-- Hibernate core library dependecy start -->
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2</version>
</dependency>
<!-- Hibernate core library dependecy end -->
<!-- Hibernate query library dependecy start -->
<dependency>
<groupId>antlr</groupId>
<artifactId>antlr</artifactId>
<version>2.7.7</version>
</dependency>
<!-- Hibernate query library dependecy end -->
</dependencies>
<build>
<finalName>StrutsExample</finalName>
</build>
</project>
4.冬眠
在 Hibernate 中不需要太多的配置,只需要声明一个客户 XML 映射文件和模型。
Customer.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.mkyong.customer.model.Customer"
table="customer" catalog="mkyong">
<id name="customerId" type="long">
<column name="CUSTOMER_ID" />
<generator class="identity" />
</id>
<property name="name" type="string">
<column name="NAME" length="45" not-null="true" />
</property>
<property name="address" type="string">
<column name="ADDRESS" not-null="true" />
</property>
<property name="createdDate" type="timestamp">
<column name="CREATED_DATE" length="19" not-null="true" />
</property>
</class>
</hibernate-mapping>
Customer.java
package com.mkyong.customer.model;
import java.util.Date;
public class Customer implements java.io.Serializable {
private long customerId;
private String name;
private String address;
private Date createdDate;
//getter and setter methods
}
5.春天
Spring 的业务对象(BO)和数据访问对象(DAO)的 beans 声明。DAO 类(CustomerDaoImpl.java)是扩展 Spring 的" HibernateDaoSupport "类来方便地访问休眠功能。
customer bean . XML
<?xml version="1.0" encoding="UTF-8"?>
<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="customerBo"
class="com.mkyong.customer.bo.impl.CustomerBoImpl" >
<property name="customerDao" ref="customerDao" />
</bean>
<bean id="customerDao"
class="com.mkyong.customer.dao.impl.CustomerDaoImpl" >
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
</beans>
CustomerBo.java
package com.mkyong.customer.bo;
import java.util.List;
import com.mkyong.customer.model.Customer;
public interface CustomerBo{
void addCustomer(Customer customer);
List<Customer> findAllCustomer();
}
CustomerBoImpl.java
package com.mkyong.customer.bo.impl;
import java.util.List;
import com.mkyong.customer.bo.CustomerBo;
import com.mkyong.customer.dao.CustomerDao;
import com.mkyong.customer.model.Customer;
public class CustomerBoImpl implements CustomerBo{
CustomerDao customerDao;
public void setCustomerDao(CustomerDao customerDao) {
this.customerDao = customerDao;
}
public void addCustomer(Customer customer){
customerDao.addCustomer(customer);
}
public List<Customer> findAllCustomer(){
return customerDao.findAllCustomer();
}
}
CustomerDao.java
package com.mkyong.customer.dao;
import java.util.List;
import com.mkyong.customer.model.Customer;
public interface CustomerDao{
void addCustomer(Customer customer);
List<Customer> findAllCustomer();
}
CustomerDaoImpl.java
package com.mkyong.customer.dao.impl;
import java.util.Date;
import java.util.List;
import com.mkyong.customer.dao.CustomerDao;
import com.mkyong.customer.model.Customer;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
public class CustomerDaoImpl extends
HibernateDaoSupport implements CustomerDao{
public void addCustomer(Customer customer){
customer.setCreatedDate(new Date());
getHibernateTemplate().save(customer);
}
public List<Customer> findAllCustomer(){
return getHibernateTemplate().find("from Customer");
}
}
6.春天+冬眠
声明数据库细节,并通过“ LocalSessionFactoryBean ”将 Spring 和 Hibernate 集成在一起。
T3
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mkyong
jdbc.username=root
jdbc.password=password
DataSource.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
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>WEB-INF/classes/config/database/properties/database.properties</value>
</property>
</bean>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
</beans>
hibernate session factory . XML
<?xml version="1.0" encoding="UTF-8"?>
<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">
<!-- Hibernate session factory -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
<property name="mappingResources">
<list>
<value>com/mkyong/customer/hibernate/Customer.hbm.xml</value>
</list>
</property>
</bean>
</beans>
SpringBeans.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">
<!-- Database Configuration -->
<import resource="config/database/spring/DataSource.xml"/>
<import resource="config/database/spring/HibernateSessionFactory.xml"/>
<!-- Beans Declaration -->
<import resource="com/mkyong/customer/spring/CustomerBean.xml"/>
</beans>
7.支柱+弹簧
要集成 Spring 和 Struts,需要在 struts-config.xml 文件中注册一个 Spring 的内置 Struts 插件" ContextLoaderPlugIn "。在 Action 类中,它必须扩展 Spring 的“ ActionSupport ”类,您可以通过getWebApplicationContext() 获得 Spring bean。
AddCustomerAction.java
package com.mkyong.customer.action;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.springframework.web.struts.ActionSupport;
import com.mkyong.customer.bo.CustomerBo;
import com.mkyong.customer.form.CustomerForm;
import com.mkyong.customer.model.Customer;
public class AddCustomerAction extends ActionSupport{
public ActionForward execute(ActionMapping mapping,ActionForm form,
HttpServletRequest request,HttpServletResponse response)
throws Exception {
CustomerBo customerBo =
(CustomerBo) getWebApplicationContext().getBean("customerBo");
CustomerForm customerForm = (CustomerForm)form;
Customer customer = new Customer();
//copy customerform to model
BeanUtils.copyProperties(customer, customerForm);
//save it
customerBo.addCustomer(customer);
return mapping.findForward("success");
}
}
ListCustomerAction.java
package com.mkyong.customer.action;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.DynaActionForm;
import org.springframework.web.struts.ActionSupport;
import com.mkyong.customer.bo.CustomerBo;
import com.mkyong.customer.model.Customer;
public class ListCustomerAction extends ActionSupport{
public ActionForward execute(ActionMapping mapping,ActionForm form,
HttpServletRequest request,HttpServletResponse response)
throws Exception {
CustomerBo customerBo =
(CustomerBo) getWebApplicationContext().getBean("customerBo");
DynaActionForm dynaCustomerListForm = (DynaActionForm)form;
List<Customer> list = customerBo.findAllCustomer();
dynaCustomerListForm.set("customerList", list);
return mapping.findForward("success");
}
}
CustomerForm.java
package com.mkyong.customer.form;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionMessage;
public class CustomerForm extends ActionForm {
private String name;
private String address;
//getter and setter, basic validation
}
客户属性
#customer module label message
customer.label.name = Name
customer.label.address = Address
customer.label.button.submit = Submit
customer.label.button.reset = Reset
#customer module error message
customer.err.name.required = Name is required
customer.err.address.required = Address is required
add_customer.jsp
<%@taglib uri="http://struts.apache.org/tags-html" prefix="html"%>
<%@taglib uri="http://struts.apache.org/tags-bean" prefix="bean"%>
Struts + Spring + Hibernate 示例
添加客户
Struts + Spring 集成示例
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/struts/struts-spring-integration-example/
下面的教程展示了如何在用 Apache Struts 1.x 开发的 web 应用程序中访问 Spring Ioc 容器中声明的 beans。
Download this Struts 1.x + Spring example – Struts-Spring-Hibernate-Example.zip
Spring 为 Spring Ioc 容器中声明的访问 beans 提供了“特定于 Struts”的解决方案。
在 Struts 配置文件中注册一个 Spring 的现成 Struts 插件。
更改您的 Struts action 类来扩展 Spring 的 ActionSupport 类,它是 Struts Action 类的子类。
ActionSupport 为您访问 Spring Ioc 容器中声明的 beans 提供了一个方便的getWebApplicationContext() 方法。
1.Struts + Spring 依赖项
为了与 Struts 1.x 集成,Spring 需要" spring-web.jar "和" spring-struts.jar "库。你可以从 Spring 网站或者 Maven 下载。
POM . XML
<!-- Spring framework -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
<version>2.5.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>2.5.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-struts</artifactId>
<version>2.0.8</version>
</dependency>
2.注册 Struts 插件
在您的 struts 配置文件(struts-config.xml)中,注册 Spring 的现成 Struts 插件—“ContextLoaderPlugIn ”。
struts-config.xml
<struts-config>
<!-- Spring Struts plugin -->
<plug-in className="org.springframework.web.struts.ContextLoaderPlugIn">
<set-property property="contextConfigLocation"
value="/WEB-INF/classes/SpringBeans.xml" />
</plug-in>
</struts-config>
“ ContextLoaderPlugIn ”将处理 Struts 和 Spring 之间的所有集成工作。您可以将 Spring 的 bean xml 文件加载到“ contextConfigLocation 属性中。
SpringBeans.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">
<!-- Beans Declaration -->
<import resource="com/mkyong/customer/spring/CustomerBean.xml"/>
</beans>
3.春天的行动支持
在 Struts Action 类中,扩展了 Spring " ActionSupport "类,通过"getWebApplicationContext() "方法获取 Spring 的 bean。
CustomerBean.xml
<bean id="customerBo" class="com.mkyong.customer.bo.impl.CustomerBoImpl" >
<property name="customerDao" ref="customerDao" />
</bean>
Struts 动作
package com.mkyong.customer.action;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.springframework.web.struts.ActionSupport;
import com.mkyong.customer.bo.CustomerBo;
import com.mkyong.customer.model.Customer;
public class ListCustomerAction extends ActionSupport{
public ActionForward execute(ActionMapping mapping,ActionForm form,
HttpServletRequest request,HttpServletResponse response)
throws Exception {
CustomerBo customerBo =
(CustomerBo) getWebApplicationContext().getBean("customerBo");
...
return mapping.findForward("success");
}
}
完成了。
integration spring struts
Struts 1 + Spring 2.5.6 + Quartz 1.6 调度器示例
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/struts/struts-spring-quartz-scheduler-integration-example/
在本教程中,您将集成 Struts + Spring + Quartz 框架来执行调度任务。Spring 提供了现成的解决方案来轻松集成支柱和石英。关系如下:
Struts <--(Plug-In)--> Spring <--(Spring-Helper)--> Quartz <---> Scheduler task
使用的工具:
Struts 1.3.10
弹簧 2.5.6
石英
1.调度程序任务
创建一个调度器任务,printMessage()
就是你想要调度的方法。
文件:SchedulerTask.java
package com.mkyong.common.quartz;
public class SchedulerTask
{
public void printMessage() {
System.out.println("Struts + Spring + Quartz integration example ~");
}
}
2.调度程序作业
要集成 Spring 和 Quartz,创建一个扩展 Spring 的QuartzJobBean
的 SchedulerJob,而不是 Quartz Job 类。
文件:SchedulerJob.java
package com.mkyong.common.quartz;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
public class SchedulerJob extends QuartzJobBean
{
private SchedulerTask schedulerTask;
public void setSchedulerTask(SchedulerTask schedulerTask) {
this.schedulerTask = schedulerTask;
}
protected void executeInternal(JobExecutionContext context)
throws JobExecutionException {
schedulerTask.printMessage();
}
}
3.春天的石英助手
Spring 附带了许多 Quartz 助手类来简化整个 Quartz 调度程序流程——调度程序、Trigget、作业和作业细节。
文件:spring-scheduler.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">
<!-- Scheduler task -->
<bean name="schedulerTask" class="com.mkyong.common.quartz.SchedulerTask" />
<!-- Scheduler job -->
<bean name="schedulerJob"
class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass"
value="com.mkyong.common.quartz.SchedulerJob" />
<property name="jobDataAsMap">
<map>
<entry key="schedulerTask" value-ref="schedulerTask" />
</map>
</property>
</bean>
<!-- Cron Trigger -->
<bean id="cronTrigger"
class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="schedulerJob" />
<property name="cronExpression" value="0/5 * * * * ?" />
</bean>
<!-- Scheduler -->
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="jobDetails">
<list>
<ref bean="schedulerJob" />
</list>
</property>
<property name="triggers">
<list>
<ref bean="cronTrigger" />
</list>
</property>
</bean>
</beans>
4.支杆
要集成 Spring 和 Struts,您需要将 Spring 的ContextLoaderPlugIn
包含到 Struts 配置文件中。
文件: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/quartz_started.jsp"/>
</action-mappings>
<!-- Spring Struts plugin -->
<plug-in className="org.springframework.web.struts.ContextLoaderPlugIn">
<set-property property="contextConfigLocation"
value="/WEB-INF/spring-scheduler.xml" />
</plug-in>
</struts-config>
5.它是如何工作的
在 Struts 初始化期间,它将通过 Spring 的ContextLoaderPlugIn
Struts 插件启动 Spring Ioc 容器;当 Spring 初始化时,它将自动启动 Quartz 调度任务。
在本例中,printMessage()
方法将每 5 秒执行一次。
下载源代码
Download it – Struts-Spring-Quartz-Example.zip
参考
更多的细节解释,你可以参考下面的教程
Struts + Spring 集成示例
Spring + Quartz 调度器集成示例
integration quartz scheduler spring struts
Struts Tiles 框架示例
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/struts/struts-tiles-framework-example/
Struts Tiles 框架是一个布局框架,它允许用户在所有网页上有效地维护页眉、页脚和菜单的标准外观。
Download this example – Struts-Tile-Framework-Example.zip
瓷砖模板示例
下面是一个创建 tiles 模板的示例,用于维护 Struts 中所有网页的页眉和页脚细节。
首先,看看这个 Struts tiles 框架关系。
[
](http://web.archive.org/web/20210128124529/http://www.mkyong.com/wp-content/uploads/2010/05/struts-tiles.png )freestar.config.enabled_slots.push({ placementName: "mkyong_incontent_1", slotId: "mkyong_incontent_1" });
1.获取 Struts 图块库
从 struts 分发文件夹或通过 Maven central repository 获取 struts tiles 库
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts-tiles</artifactId>
<version>1.3.10</version>
</dependency>
并将其包含在项目依赖关系库中。
2.创建模板
为页眉和页脚详细信息创建红色模板和绿色模板。这两个模板只是背景颜色不同的纯 HTML 代码..
Template-Red color
/Template-Red/header . JSP
# [此处为标志]这是红色标题模板
/template-red/footer.jsp
# 这是红色页脚模板
Template-Green color
/Template-Green/header . JSP
# [此处有标志]这是绿色标题模板
/template-green/footer . JSP
# 这是绿色页脚模板
3.瓷砖布局
为所有网页创建标准网页布局。
common-layout.jsp
<%@ taglib uri="http://struts.apache.org/tags-tiles" prefix="tiles" %>
4.主体模板
在 body 模板中,您应该始终为 body 细节创建两个页面“user-form.jsp 和 user-form-body.jsp ”,以打破与 tiles 框架的耦合。“user-form.jsp”用于获取 tiles 定义,并将真实的正文内容(user-form-body.jsp)作为正文模板。
user-form.jsp
<%@ taglib uri="http://struts.apache.org/tags-tiles" prefix="tiles" %>
user-form-body.jsp
# 这是正文内容
5.瓷砖定义
所有的模板都做好了,创建一个“tiles-defs.xml”文件并声明一个“company-template”定义为红色的模板。
tiles-defs.xml
<!DOCTYPE tiles-definitions PUBLIC
"-//Apache Software Foundation//DTD Tiles Configuration 1.3//EN"
"http://struts.apache.org/dtds/tiles-config_1_3.dtd">
<tiles-definitions>
<definition name="company-template" path="/pages/tiles/common-layout.jsp">
<put name="header" value="/pages/tiles/template-red/header.jsp" />
<put name="footer" value="/pages/tiles/template-red/footer.jsp" />
</definition>
</tiles-definitions>
6.包括 TilesPlugin
要使用 Struts tiles 框架,您必须在 Struts 配置文件中声明" TilesPlugin "插件类。
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="/User"
type="org.apache.struts.actions.ForwardAction"
parameter="/pages/user/user-form.jsp"/>
</action-mappings>
<plug-in className="org.apache.struts.tiles.TilesPlugin" >
<set-property property="definitions-config"
value="/WEB-INF/tiles-defs.xml"/>
</plug-in>
</struts-config>
7.演示
在上述情况下,使用红色模板。
http://localhost:8080/struts example/user . do
要将其更改为 template green,只需更新“tiles-defs.xml”文件。
tiles-defs.xml
<!DOCTYPE tiles-definitions PUBLIC
"-//Apache Software Foundation//DTD Tiles Configuration 1.3//EN"
"http://struts.apache.org/dtds/tiles-config_1_3.dtd">
<tiles-definitions>
<definition name="company-template" path="/pages/tiles/common-layout.jsp">
<put name="header" value="/pages/tiles/template-green/header.jsp" />
<put name="footer" value="/pages/tiles/template-green/footer.jsp" />
</definition>
</tiles-definitions>
再次访问它
http://localhost:8080/struts example/user . do
页眉和页脚的颜色发生了变化(模板为绿色),在 tiles 配置文件中仅有微小的变化。
参考
Struts Tiles 文档—http://struts.apache.org/1.x/struts-tiles/index.html
Tags : struts template tiles freestar.config.enabled_slots.push({ placementName: "mkyong_leaderboard_btf", slotId: "mkyong_leaderboard_btf" });
Struts 教程
原文:http://web.archive.org/web/20230101150211/https://mkyong.com/tutorials/struts-tutorials/
Struts 1.x 是最著名、最经典、最成熟的模型-视图-控制器(MVC)框架。很多时候,你会听到这样的话,学习 Struts 1.x 毫无意义,它是一个死框架。然而,尽管 Struts 1.x 在早期取得了巨大的成功,仍然有成千上万的公司在实施 Struts 1.x,并且从来没有考虑过升级,所以 Struts 1.x 仍然存在许多可维护性问题。
Struts 1.x 是一个完整的 web 框架,提供了完整的 web 表单组件、验证器、内部化、错误处理、tiles 布局,学习曲线低且易于实现。在本教程中,它提供了许多关于使用 Struts 1.x MVC 框架的分步示例和解释。
快乐学习 Struts。🙂
Struts 快速入门
让我们快速了解一下 Struts 1.x 框架。
Struts hello world 示例
通过一个 hello world 示例来理解 Struts MVC 是如何工作的。
Struts 配置
所有关于 Struts 配置的东西。
Struts 动作和动作表单
Action 和 ActionForm 实现类。
Struts Web 表单组件
Struts 完全支持所有标准的 web 表单组件。
Struts 逻辑标记
Struts 附带了许多逻辑标记,以简化 bean 组件迭代或条件处理。
Struts 错误和日志记录
异常处理和错误记录。
Struts 本地化
Struts 在国际化或本地化方面有很好的支持。
Struts 验证程序框架
在 Struts 验证器框架中,它提供了许多泛型方法(required、maxlength、minlength..)来验证表单组件,这使您的验证代码更加标准化,也更容易维护。
Struts Tiles 框架
Struts tiles 框架是一个强大的布局框架,用于维护所有网页的页眉、页脚或菜单细节的标准外观。
Struts 与其他框架集成
任何关于 Struts 与其他框架集成的信息。
Struts 杂项
其他 Struts 示例。
Struts 常见错误
一些 Struts 常见的错误消息。
Struts 参考
Struts 验证器框架示例
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/struts/struts-validator-framework-example/
Struts 验证器框架提供了许多通用的验证方法,使验证工作更加容易和可维护。使用 Struts validator,您需要将验证函数声明到一个 xml 文件中,而不是 ActionForm validate()方法,这可以使 Struts 验证更加标准化、可重用和减少重复代码。
Download this example – Struts-Validator-Example.zip
使用 Struts 验证器框架
下面是使用 Struts 验证器框架的快速指南。
1.验证程序插件
要使用 Struts 验证器插件,您需要将" ValidatorPlugIn "类包含到 struts-config.xml 文件中。
...
<plug-in className="org.apache.struts.validator.ValidatorPlugIn" >
<set-property property="pathnames"
value="/WEB-INF/validator-rules.xml, /WEB-INF/validator-user.xml"/>
</plug-in>
...
" validator-rules.xml "文件包含了所有的通用验证器名称,你可以在 Struts 分发库中获得这个文件,(不要自己创建这个文件)。“validator-user.xml”包含所有表单域验证。
2.验证器表单
对于需要使用验证器框架的表单 bean,它必须扩展验证器表单 ,而不是动作表单 。
import org.apache.struts.validator.ValidatorForm;
public class UserForm extends ValidatorForm{
..
3.validator-user.xml
用户表单 bean、电子邮件属性,并将“必需”和“电子邮件”验证器附加到电子邮件属性。“必需”验证器将确保该字段不为空,而“电子邮件”验证器用于检查电子邮件格式是否正确。“必需”和“电子邮件”验证器都在“validator-rules.xml”文件中声明。
<form-validation>
<formset>
<form name="userForm">
<field property="email" depends="required,email">
<msg name="required" key="err.user.email.required" />
<msg name="email" key="err.user.email.invalid" />
</field>
</form>
</formset>
</form-validation>
Struts 验证器框架示例
理解 Struts validator 框架的最佳方式是创建一个简单的应用程序并完成验证工作。这里有一个简单的用户注册表单,使用 Struts validator 框架来检查用户名、密码和电子邮件。
1.行动
动作类,只是转发一个成功请求。
user action . Java
package com.mkyong.user.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;
public class UserAction extends Action{
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
return mapping.findForward("success");
}
}
2.用户表单
UserForm 扩展了 ValidatorForm 类。
UserForm.java
package com.mkyong.user.form;
import org.apache.struts.validator.ValidatorForm;
public class UserForm extends ValidatorForm{
String username;
String pwd;
String pwd2;
String email;
//getter and setter methods
}
3.属性文件
属性文件包含所有标签和错误消息。
用户属性
#user module label message
label.user.name = Name
label.user.username = UserName
label.user.pwd = Password
label.user.pwd2 = Confirm Password
label.user.email = Email
label.user.button.submit = Submit
#Error message
err.user.username.required = Username is required.<br/>
err.user.username.length = Username length should be between {0} and {1}.<br/>
err.user.username.invalid =
Username is invalid , it should be a-z, A-Z, 0-9, dash "-" or underscore "_".<br/>
err.user.pwd.required = Password is required.<br/>
err.user.pwd.length = Password length should be between {0} and {1}.<br/>
err.user.pwd.invalid = Password is invalid , it should be a-z, A-Z, 0-9.<br/>
err.user.pwd2.notmatch = Confirm password is not match.<br/>
err.user.email.required = Email is required.<br/>
err.user.email.invalid = Email address is invalid.<br/>
4.查看页面
一个简单的 jsp 页面创建所有需要的文本字段,和一个简单的感谢页面
RegisterUser.jsp
<%@taglib uri="http://struts.apache.org/tags-bean" prefix="bean"%>
<%@taglib uri="http://struts.apache.org/tags-logic" prefix="logic"%>
<%@taglib uri="http://struts.apache.org/tags-html" prefix="html"%>
Struts -验证器示例
RegisterUser.jsp
User Registeration Form
: : : :
ThanksYou.jsp
Struts -验证器示例
Thanks you for the registration
5.Struts 配置
配置动作、表单映射并注册“ValidatorPlugIn”插件。
struts-connfig.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="userForm" type="com.mkyong.user.form.UserForm" />
</form-beans>
<action-mappings>
<action
path="/Register"
type="com.mkyong.user.action.UserAction"
name="userForm"
input="/pages/RegisterUser.jsp"
>
<forward name="success" path="/pages/ThanksYou.jsp"/>
</action>
<action
path="/RegisterUserPage"
type="org.apache.struts.actions.ForwardAction"
parameter="/pages/RegisterUser.jsp"/>
</action-mappings>
<message-resources
parameter="com.mkyong.user.properties.user" />
<plug-in className="org.apache.struts.validator.ValidatorPlugIn" >
<set-property property="pathnames"
value="/WEB-INF/validator-rules.xml, /WEB-INF/validator-user.xml"/>
</plug-in>
</struts-config>
6.validator-user.xml
为用户表单的用户名、密码和电子邮件属性定义验证器。
validator-user.xml
<!DOCTYPE form-validation PUBLIC
"-//Apache Software Foundation
//DTD Commons Validator Rules Configuration 1.3.0//EN"
"http://jakarta.apache.org/commons/dtds/validator_1_3_0.dtd">
<form-validation>
<formset>
<form name="userForm">
<field property="username" depends="required,maxlength,minlength,mask">
<msg name="required" key="err.user.username.required" />
<msg name="maxlength" key="err.user.username.length" />
<msg name="minlength" key="err.user.username.length" />
<msg name="mask" key="err.user.username.invalid" />
<arg name="maxlength" key="${var:minlength}" position="0" resource="false"/>
<arg name="maxlength" key="${var:maxlength}" position="1" resource="false"/>
<arg name="minlength" key="${var:minlength}" position="0" resource="false"/>
<arg name="minlength" key="${var:maxlength}" position="1" resource="false"/>
<var>
<var-name>minlength</var-name>
<var-value>3</var-value>
</var>
<var>
<var-name>maxlength</var-name>
<var-value>15</var-value>
</var>
<var>
<var-name>mask</var-name>
<var-value>^[a-zA-Z0-9-_]*$</var-value>
</var>
</field>
<field property="pwd" depends="required,maxlength,minlength,mask">
<msg name="required" key="err.user.pwd.required" />
<msg name="maxlength" key="err.user.pwd.length" />
<msg name="minlength" key="err.user.pwd.length" />
<msg name="mask" key="err.user.pwd.invalid" />
<arg name="maxlength" key="${var:minlength}" position="0" resource="false"/>
<arg name="maxlength" key="${var:maxlength}" position="1" resource="false"/>
<arg name="minlength" key="${var:minlength}" position="0" resource="false"/>
<arg name="minlength" key="${var:maxlength}" position="1" resource="false"/>
<var>
<var-name>minlength</var-name>
<var-value>7</var-value>
</var>
<var>
<var-name>maxlength</var-name>
<var-value>15</var-value>
</var>
<var>
<var-name>mask</var-name>
<var-value>^[a-zA-Z0-9]*$</var-value>
</var>
</field>
<field property="pwd2" depends="validwhen">
<msg name="validwhen" key="err.user.pwd2.notmatch" />
<var>
<var-name>test</var-name>
<var-value>
(pwd == *this*)
</var-value>
</var>
</field>
<field property="email" depends="required,email">
<msg name="required" key="err.user.email.required" />
<msg name="email" key="err.user.email.invalid" />
</field>
</form>
</formset>
</form-validation>
7.演示
http://localhost:8080/struts example/register user page . do
http://localhost:8080/struts example/register . do
验证码是描述性的,足以知道它是如何工作的,如果你想知道细节,以及其他可用的验证器,请检查下面的参考网站。
参考
Struts 验证器文档—http://struts.apache.org/1.2.4/userGuide/dev_validator.html
struts validation
struts–通配符示例
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/struts/struts-wildcards-example/
Download this example – Struts-Wildcards-Example.zip
struts 通配符可以帮助减少 struts-config.xml 文件中的重复,只要您的 Struts 项目遵循一些常规的文件结构。例如,在用户模块中,为了实现 CRUD 函数,您的 struts-config.xml 可能如下所示
1.没有通配符
你需要为每个列表创建四个动作映射,添加、删除和更新函数,以及大量的重复。
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="/ListUserAction"
type="com.mkyong.common.action.UserAction"
parameter="ListUser"
>
<forward name="success" path="/pages/ListUser.jsp"/>
</action>
<action
path="/AddUserAction"
type="com.mkyong.common.action.UserAction"
parameter="AddUser"
>
<forward name="success" path="/pages/AddUser.jsp"/>
</action>
<action
path="/EditUserAction"
type="com.mkyong.common.action.UserAction"
parameter="EditUser"
>
<forward name="success" path="/pages/EditUser.jsp"/>
</action>
<action
path="/DeleteUserAction"
type="com.mkyong.common.action.UserAction"
parameter="DeleteUser"
>
<forward name="success" path="/pages/DeleteUser.jsp"/>
</action>
</action-mappings>
</struts-config>
2.带通配符
使用 Struts 通配符特性,您的 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>
<action-mappings>
<action
path="/*UserAction"
type="com.mkyong.common.action.UserAction"
parameter="{1}User"
>
<forward name="success" path="/pages/{1}User.jsp"/>
</action>
</action-mappings>
</struts-config>
让我们来看一个用例,通过http://localhost:8080/struts example/edit user action . do 尝试访问。“ EditUserAction.do ”将匹配“ /*UserAction ”模式, ***** 匹配字符串“ Edit ”用 {1} 表示,以备后用。
在上述情况下,通配符操作映射将从
<action
path="/*UserAction"
type="com.mkyong.common.action.UserAction"
parameter="{1}User"
>
<forward name="success" path="/pages/{1}User.jsp"/>
</action>
到
<action
path="/EditUserAction"
type="com.mkyong.common.action.UserAction"
parameter="EditUser"
>
<forward name="success" path="/pages/EditUser.jsp"/>
</action>
结论
两个 struts-config.xml 示例具有相同的功能,但是在通配符支持方面重复较少。然而,不要 在你的项目中过度使用这个通配符特性,它比普通的声明更难管理。
struts
用 ExpressionParser 测试弹簧 el
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/spring3/test-spring-el-with-expressionparser/
Spring expression language (SpEL)支持许多功能,您可以使用这个特殊的“ ExpressionParser ”接口来测试那些表达式特性。
这里有两个代码片段,展示了使用 Spring EL 的基本用法。
SpEL 来计算文字字符串表达式。
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("'put spel expression here'");
String msg = exp.getValue(String.class);
SpEL 来评估 bean 属性“item.name”。
Item item = new Item("mkyong", 100);
StandardEvaluationContext itemContext = new StandardEvaluationContext(item);
//display the value of item.name property
Expression exp = parser.parseExpression("name");
String msg = exp.getValue(itemContext, String.class);
测试 SpEL 的几个例子。代码和注释应该是自我探索的。
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
public class App {
public static void main(String[] args) {
ExpressionParser parser = new SpelExpressionParser();
//literal expressions
Expression exp = parser.parseExpression("'Hello World'");
String msg1 = exp.getValue(String.class);
System.out.println(msg1);
//method invocation
Expression exp2 = parser.parseExpression("'Hello World'.length()");
int msg2 = (Integer) exp2.getValue();
System.out.println(msg2);
//Mathematical operators
Expression exp3 = parser.parseExpression("100 * 2");
int msg3 = (Integer) exp3.getValue();
System.out.println(msg3);
//create an item object
Item item = new Item("mkyong", 100);
//test EL with item object
StandardEvaluationContext itemContext = new StandardEvaluationContext(item);
//display the value of item.name property
Expression exp4 = parser.parseExpression("name");
String msg4 = exp4.getValue(itemContext, String.class);
System.out.println(msg4);
//test if item.name == 'mkyong'
Expression exp5 = parser.parseExpression("name == 'mkyong'");
boolean msg5 = exp5.getValue(itemContext, Boolean.class);
System.out.println(msg5);
}
}
public class Item {
private String name;
private int qty;
public Item(String name, int qty) {
super();
this.name = name;
this.qty = qty;
}
//...
}
输出
Hello World
11
200
mkyong
true
Note
This article is demonstrates few basic usages of Spring expression parser, and you should visit this official Spring expression documentation for hundred of useful SpEL examples.
下载源代码
Download It – Spring3-EL-Parser-Example.zip (6 KB)spring el 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/20190225101849/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0 ')
TestNG–分组测试
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/unittest/testng-groups-example/
在本教程中,我们将向您展示如何在 TestNG 中进行分组测试。
1.方法组
查看测试组示例。
runSelenium()和 runSelenium1()属于组selenium-test
。
testConnectOracle()和 testConnectMsSQL()属于组database
。
如果组selenium-test
和database
通过,将执行 runFinal()。
TestGroup.java
package com.mkyong.testng.examples.group;
import org.testng.annotations.AfterGroups;
import org.testng.annotations.BeforeGroups;
import org.testng.annotations.Test;
public class TestGroup {
@BeforeGroups("database")
public void setupDB() {
System.out.println("setupDB()");
}
@AfterGroups("database")
public void cleanDB() {
System.out.println("cleanDB()");
}
@Test(groups= "selenium-test")
public void runSelenium() {
System.out.println("runSelenium()");
}
@Test(groups= "selenium-test")
public void runSelenium1() {
System.out.println("runSelenium()1");
}
@Test(groups = "database")
public void testConnectOracle() {
System.out.println("testConnectOracle()");
}
@Test(groups = "database")
public void testConnectMsSQL() {
System.out.println("testConnectMsSQL");
}
@Test(dependsOnGroups = {"database","selenium-test"})
public void runFinal() {
System.out.println("runFinal");
}
}
输出
//group = selenium-test
runSelenium()
runSelenium()1
//group = database
setupDB()
testConnectMsSQL
testConnectOracle()
cleanDB()
//dependsOnGroups = database, selenium-test
runFinal
PASSED: runSelenium
PASSED: runSelenium1
PASSED: testConnectMsSQL
PASSED: testConnectOracle
PASSED: runFinal
2.课堂上的小组
“组”可以应用于类级别。在下面的例子中,这个类“TestSelenium”的每个公共方法都属于组selenium-test
。
TestSelenium.java
package com.mkyong.testng.examples.group;
import org.testng.annotations.Test;
@Test(groups= "selenium-test")
public class TestSelenium {
public void runSelenium() {
System.out.println("runSelenium()");
}
public void runSelenium1() {
System.out.println("runSelenium()1");
}
}
创建一个 XML 文件来运行 2 个测试类。
testng.xml
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="TestAll">
<test name="final">
<classes>
<class name="com.mkyong.testng.examples.group.TestSelenium" />
<class name="com.mkyong.testng.examples.group.TestGroup" />
</classes>
</test>
<!-- Run test method on group "selenium" only -->
<test name="selenium">
<groups>
<run>
<include name="selenium-test" />
</run>
</groups>
<classes>
<class name="com.mkyong.testng.examples.group.TestSelenium" />
<class name="com.mkyong.testng.examples.group.TestGroup" />
</classes>
</test>
</suite>
输出
//test name = final
runSelenium()
runSelenium()1
setupDB()
testConnectMsSQL
testConnectOracle()
cleanDB()
runFinal
//test name = selenium
runSelenium()
runSelenium()1
===============================================
TestAll
Total tests run: 7, Failures: 0, Skips: 0
===============================================
3.杂项示例
3.1 一种试验方法可以属于多个组。
@Test(groups = {"mysql","database"})
public void testConnectMsSQL() {
System.out.println("testConnectMsSQL");
}
3.2 以上结果是通过 Eclipse TestNG 插件执行的。
参考
TestNG : @Test
TestNG Eclipse 插件
group test testng
TestNG Hello World 示例
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/unittest/testng-hello-world-example/
一个经典的例子,向你展示如何开始使用 TestNG 单元测试框架。
使用的工具:
测试 6.8.7
maven3
Eclipse IDE
1.测试依赖关系
在pom.xml
中添加 TestNG 库。
pom.xml
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.8.7</version>
<scope>test</scope>
</dependency>
2.测试示例
复习一个简单的类,有一个方法返回一个固定的邮箱“feedback@yoursite.com”。
RandomEmailGenerator.java
package com.mkyong.testng.project.service.email;
import org.springframework.stereotype.Service;
public class RandomEmailGenerator {
public String generate() {
return "feedback@yoursite.com";
}
}
创建一个这样的测试用例:
TestHelloWorld.java
package com.mkyong.testng.examples.helloworld;
import org.testng.Assert;
import org.testng.annotations.Test;
import com.mkyong.testng.project.service.email.RandomEmailGenerator;
public class TestHelloWorld {
@Test()
public void testEmailGenerator() {
RandomEmailGenerator obj = new RandomEmailGenerator();
String email = obj.generate();
Assert.assertNotNull(email);
Assert.assertEquals(email, "feedback@yoursite.com");
}
}
完成后,一个简单的 TestNG 测试用例就创建了,这个测试确保RandomEmailGenerator.generate()
总是返回“feedback@yoursite.com”。
3.测试 Eclipse 插件
要在 Eclipse IDE 中运行上述测试,您需要安装 TestNG Eclipse 插件。按照这个官方测试 Eclipse 插件指南 进行安装。
要运行 TestNG 测试,右键单击测试类并作为“TestNG test”运行。
结果
参考
TestNG 官方文档
hello world testng
TestNG 参数测试示例
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/unittest/testng-parameter-testing-example/
又一个 TestNG 参数测试例子,用@DataProvider
。
1.CharUtil 类
比方说,一个将字符转换为 ASCII 或相反的类,如何用 TestNG 对它进行单元测试?
CharUtils.java
package com.mkyong.testng.examples.parameter;
/**
* Character Utility class
*
* @author mkyong
*
*/
public class CharUtils {
/**
* Convert the characters to ASCII value
*
* @param character character
* @return ASCII value
*/
public static int CharToASCII(final char character) {
return (int) character;
}
/**
* Convert the ASCII value to character
*
* @param ascii ascii value
* @return character value
*/
public static char ASCIIToChar(final int ascii) {
return (char) ascii;
}
}
2.TestNG @DataProvider 示例
为了测试它,创建一个接受两个参数(字符和期望的 ASCII)的@Test
方法,测试数据从数据提供者传递。
CharUtilsTest.java
package com.mkyong.testng.examples.parameter;
import org.testng.Assert;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
/**
* Character Utils Testing
* @author mkyong
*
*/
public class CharUtilsTest {
@DataProvider
public Object[][] ValidDataProvider() {
return new Object[][]{
{ 'A', 65 },{ 'a', 97 },
{ 'B', 66 },{ 'b', 98 },
{ 'C', 67 },{ 'c', 99 },
{ 'D', 68 },{ 'd', 100 },
{ 'Z', 90 },{ 'z', 122 },
{ '1', 49 },{ '9', 57 }
};
}
@Test(dataProvider = "ValidDataProvider")
public void CharToASCIITest(final char character, final int ascii) {
int result = CharUtils.CharToASCII(character);
Assert.assertEquals(result, ascii);
}
@Test(dataProvider = "ValidDataProvider")
public void ASCIIToCharTest(final char character, final int ascii) {
char result = CharUtils.ASCIIToChar(ascii);
Assert.assertEquals(result, character);
}
}
结果
PASSED: CharToASCIITest(A, 65)
PASSED: CharToASCIITest(a, 97)
PASSED: CharToASCIITest(B, 66)
PASSED: CharToASCIITest(b, 98)
PASSED: CharToASCIITest(C, 67)
PASSED: CharToASCIITest(c, 99)
PASSED: CharToASCIITest(D, 68)
PASSED: CharToASCIITest(d, 100)
PASSED: CharToASCIITest(Z, 90)
PASSED: CharToASCIITest(z, 122)
PASSED: CharToASCIITest(1, 49)
PASSED: CharToASCIITest(9, 57)
PASSED: ASCIIToCharTest(A, 65)
PASSED: ASCIIToCharTest(a, 97)
PASSED: ASCIIToCharTest(B, 66)
PASSED: ASCIIToCharTest(b, 98)
PASSED: ASCIIToCharTest(C, 67)
PASSED: ASCIIToCharTest(c, 99)
PASSED: ASCIIToCharTest(D, 68)
PASSED: ASCIIToCharTest(d, 100)
PASSED: ASCIIToCharTest(Z, 90)
PASSED: ASCIIToCharTest(z, 122)
PASSED: ASCIIToCharTest(1, 49)
PASSED: ASCIIToCharTest(9, 57)
===============================================
com.mkyong.common.CharUtilsTest
Tests run: 24, Failures: 0, Skips: 0
===============================================
More Parameter Examples.
For more examples, please refer to this TestNG parameter test with XML and DataProvider .parameter test testng (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/20190227120414/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0 ')
TestNG+Selenium–负载测试示例
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/unittest/testng-selenium-load-testing-example/
在本教程中,我们将向您展示如何使用@Test 属性invocationCount
和threadPoolSize
在网站上执行负载测试或压力测试。
使用的工具:
测试 6.8.7
硒
maven3
我们正在使用 Selenium 库来自动化浏览器访问网站。
1.项目依赖性
获取 TestNG 和 Selenium 库。
pom.xml
<properties>
<testng.version>6.8.7</testng.version>
<selenium.version>2.39.0</selenium.version>
</properties>
<dependencies>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>${testng.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>${selenium.version}</version>
</dependency>
<dependencies>
2.@Test(invocationCount=?)
这个invocationCount
决定了 TestNG 应该运行这个测试方法多少次。
例 2.1
@Test(invocationCount = 10)
public void repeatThis() {
//...
}
输出—repeatThis()
方法将运行 10 次。
PASSED: repeatThis
PASSED: repeatThis
PASSED: repeatThis
PASSED: repeatThis
PASSED: repeatThis
PASSED: repeatThis
PASSED: repeatThis
PASSED: repeatThis
PASSED: repeatThis
PASSED: repeatThis
示例 2.2 –使用 Selenium 打开 Firefox 浏览器并加载“Google.com”。这个测试是为了确保页面标题始终是“Google”。
package com.mkyong.testng.examples.loadtest;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.testng.Assert;
import org.testng.annotations.Test;
public class TestMultipleThreads {
@Test(invocationCount = 5)
public void loadTestThisWebsite() {
WebDriver driver = new FirefoxDriver();
driver.get("http://www.google.com");
System.out.println("Page Title is " + driver.getTitle());
Assert.assertEquals("Google", driver.getTitle());
driver.quit();
}
}
输出–你会注意到 Firefox 浏览器会提示退出并关闭 5 次。
Page Title is Google
Page Title is Google
Page Title is Google
Page Title is Google
Page Title is Google
PASSED: loadTestThisWebsite
PASSED: loadTestThisWebsite
PASSED: loadTestThisWebsite
PASSED: loadTestThisWebsite
PASSED: loadTestThisWebsite
3.@Test(invocationCount =?,threadPoolSize =?)
threadPoolSize
属性告诉 TestNG 创建一个线程池,通过多线程运行测试方法。有了线程池,将大大减少测试方法的运行时间。
例 3.1 –启动一个线程池,包含 3 个线程,运行测试方法 3 次。
@Test(invocationCount = 3, threadPoolSize = 3)
public void testThreadPools() {
System.out.printf("Thread Id : %s%n", Thread.currentThread().getId());
}
输出——测试方法运行 3 次,每次都接收到自己的线程。
[ThreadUtil] Starting executor timeOut:0ms workers:3 threadPoolSize:3
Thread Id : 10
Thread Id : 12
Thread Id : 11
PASSED: testThreadPools
PASSED: testThreadPools
PASSED: testThreadPools
例 3.2 –启动一个线程池,包含 3 个线程,运行测试方法 10 次。
@Test(invocationCount = 10, threadPoolSize = 3)
public void testThreadPools() {
System.out.printf("Thread Id : %s%n", Thread.currentThread().getId());
}
输出–测试方法运行 10 次,线程被重用。
[ThreadUtil] Starting executor timeOut:0ms workers:10 threadPoolSize:3
Thread Id : 10
Thread Id : 11
Thread Id : 12
Thread Id : 10
Thread Id : 11
Thread Id : 12
Thread Id : 10
Thread Id : 11
Thread Id : 12
Thread Id : 10
PASSED: testThreadPools
PASSED: testThreadPools
PASSED: testThreadPools
PASSED: testThreadPools
PASSED: testThreadPools
PASSED: testThreadPools
PASSED: testThreadPools
PASSED: testThreadPools
PASSED: testThreadPools
PASSED: testThreadPools
4.负载测试示例
通过结合 TestNG 多线程和 Selenium 强大的浏览器自动化。您可以创建一个简单而强大的负载测试,如下所示:
TestMultipleThreads.java
package com.mkyong.testng.examples.loadtest;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.testng.Assert;
import org.testng.annotations.Test;
public class TestMultipleThreads {
@Test(invocationCount = 100, threadPoolSize = 5)
public void loadTest() {
System.out.printf("%n[START] Thread Id : %s is started!",
Thread.currentThread().getId());
WebDriver driver = new FirefoxDriver();
driver.get("http://yourwebsite.com");
//perform whatever actions, like login, submit form or navigation
System.out.printf("%n[END] Thread Id : %s",
Thread.currentThread().getId());
driver.quit();
}
}
输出–上面的测试将启动一个 5 线程池,并向一个指定的网站发送 100 个 URL 请求。
[ThreadUtil] Starting executor timeOut:0ms workers:100 threadPoolSize:5
[START] Thread Id : 11 is started!
[START] Thread Id : 14 is started!
[START] Thread Id : 10 is started!
[START] Thread Id : 12 is started!
[START] Thread Id : 13 is started!
[END] Thread Id : 11
[START] Thread Id : 11 is started!
[END] Thread Id : 10
[START] Thread Id : 10 is started!
[END] Thread Id : 13
[START] Thread Id : 13 is started!
[END] Thread Id : 14
[START] Thread Id : 14 is started!
[END] Thread Id : 12
[START] Thread Id : 12 is started!
[END] Thread Id : 13
......
FAQs
Q : For load testing with Selenium and TestNG, why only one browser is prompts out at a time?
A : Your test method is completed too fast, try putting a Thread.sleep(5000)
to delay the execution, now, you should notice multiple browsers prompt out simultaneously. (For demonstration purpose only).
下载源代码
Download – TestNG-LoadTest-Example.zip (15 kb)
参考
硒网站
维基百科:负载测试
load test selenium testng
TestNG + Spring 集成示例
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/unittest/testng-spring-integration-example/
在本教程中,我们将向您展示如何用 TestNG 测试 Spring 的组件。
使用的工具:
测试 6.8.7
弹簧 3.2.2 释放
maven3
Eclipse IDE
1.项目相关性
要将 Spring 与 TestNG 集成,您需要spring-test.jar
,添加以下内容:
pom.xml
<properties>
<spring.version>3.2.2.RELEASE</spring.version>
<testng.version>6.8.7</testng.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>${testng.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
2.弹簧组件
创建一个简单的 Spring 组件,稍后我们将使用 TestNG 测试这个组件。
EmailGenerator.java
package com.mkyong.testng.project.service.email;
public interface EmailGenerator {
String generate();
}
RandomEmailGenerator.java
package com.mkyong.testng.project.service.email;
import org.springframework.stereotype.Service;
@Service
public class RandomEmailGenerator implements EmailGenerator {
@Override
public String generate() {
return "feedback@yoursite.com";
}
}
3.测试+弹簧
在测试文件夹中创建一个 Spring 配置文件,用于 Spring 组件扫描。
${project}/src/test/resources/spring-test-config.xml
<beans
xmlns:context="http://www.springframework.org/schema/context"
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.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
">
<context:component-scan base-package="com.mkyong.testng" />
</beans>
要访问 TestNG 中的弹簧组件,请扩展AbstractTestNGSpringContextTests
,参见以下示例:
${project}/src/test/java/com/mkyong/testng/examples/spring/TestSpring.java
package com.mkyong.testng.examples.spring;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.testng.Assert;
import org.testng.annotations.Test;
import com.mkyong.testng.project.service.email.EmailGenerator;
@Test
@ContextConfiguration(locations = { "classpath:spring-test-config.xml" })
public class TestSpring extends AbstractTestNGSpringContextTests {
@Autowired
EmailGenerator emailGenerator;
@Test()
void testEmailGenerator() {
String email = emailGenerator.generate();
System.out.println(email);
Assert.assertNotNull(email);
Assert.assertEquals(email, "feedback@yoursite.com");
}
}
输出
feedback@yoursite.com
PASSED: testEmailGenerator
===============================================
Default test
Tests run: 1, Failures: 0, Skips: 0
===============================================
下载源代码
Download it – TestNG-Spring-Example.zip (35 KB)
参考
Spring–TestNG 支持类
Spring AbstractTestNGSpringContextTests JavaDoc
spring testng
TestNG–配置注释示例
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/unittest/testng-tutorial-1-basic-usage/
在 TestNG 中,我们可以使用以下注释来为您的测试类进行配置,比如设置/清理数据库、准备虚拟数据、部署/关闭服务器等。
@BeforeSuite - For suite test, run before all tests in this suite have run.
@AfterSuite - For suite test, run after all tests in this suite have run.
@BeforeTest - For suite test, run before any test method belonging to the classes inside the <test> tag is run.
@AfterTest - For suite test, run after all the test methods belonging to the classes inside the <test> tag have run.
@BeforeGroups: Run before the first test method that belongs to the group is invoked.
@AfterGroups: Run after the last test method that belongs to the groups is invoked.
@BeforeClass - Run before the first test method in the current class is invoked.
@AfterClass - Run after all the test methods in the current class have been run.
@BeforeMethod - Run before each test method.
@AfterMethod - Run after each test method.
P.S Suite 测试–一起运行多个测试类。
回顾下面的例子,看看执行顺序——首先调用哪个方法,然后调用哪个方法。
1.单一测试类别
运行单个测试用例,展示之前/之后group
、class
和method
的使用。
TestConfiguration.java
package com.mkyong.testng.examples.configuration;
import org.testng.annotations.AfterClass;
import org.testng.annotations.AfterGroups;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeGroups;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
public class TestConfiguration {
@BeforeGroups("shopping")
public void beforeGroups() {
System.out.println("@BeforeGroups");
}
@AfterGroups("shopping")
public void afterGroups() {
System.out.println("@AfterGroups");
}
@BeforeClass
public void beforeClass() {
System.out.println("@BeforeClass");
}
@AfterClass
public void afterClass() {
System.out.println("@AfterClass");
}
@BeforeMethod
public void beforeMethod() {
System.out.println("@BeforeMethod");
}
@AfterMethod
public void afterMethod() {
System.out.println("@AfterMethod");
}
@Test(groups = "shopping")
public void runTest1() {
System.out.println("@Test - runTest1");
}
@Test
public void runTest2() {
System.out.println("@Test - runTest2");
}
}
输出
@BeforeClass
@BeforeGroups
@BeforeMethod
@Test - runTest1
@AfterMethod
@AfterGroups
@BeforeMethod
@Test - runTest2
@AfterMethod
@AfterClass
PASSED: runTest1
PASSED: runTest2
===============================================
Default test
Tests run: 2, Failures: 0, Skips: 0
===============================================
2.套件测试类
再创建 2 个测试类来展示 before/after suite
和test
的用法。
DBConfig.java
package com.mkyong.testng.examples.configuration;
import org.testng.annotations.AfterSuite;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.BeforeTest;
public class DBConfig {
@BeforeSuite()
public void beforeSuite() {
System.out.println("@BeforeSuite");
}
@AfterSuite()
public void afterSuite() {
System.out.println("@AfterSuite");
}
@BeforeTest()
public void beforeTest() {
System.out.println("@BeforeTest");
}
@AfterTest()
public void afterTest() {
System.out.println("@AfterTest");
}
}
TestDBConnection.java
package com.mkyong.testng.examples.configuration;
import org.testng.annotations.Test;
public class TestDBConnection {
@Test
public void runOtherTest1() {
System.out.println("@Test - runOtherTest1");
}
@Test
public void runOtherTest2() {
System.out.println("@Test - runOtherTest2");
}
}
创建一个 XML 文件来一起运行多个测试用例。阅读 XML 注释,这是不言自明的。
testng.xml
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<!-- @BeforeSuite -->
<suite name="TestAll">
<!-- @BeforeTest -->
<test name="case1">
<classes>
<class name="com.mkyong.testng.examples.configuration.TestConfiguration" />
<class name="com.mkyong.testng.examples.configuration.TestDBConnection" />
<class name="com.mkyong.testng.examples.configuration.DBConfig" />
</classes>
</test>
<!-- @AfterTest -->
<!-- @BeforeTest -->
<test name="case2">
<classes>
<class name="com.mkyong.testng.examples.configuration.TestDBConnection" />
<class name="com.mkyong.testng.examples.configuration.DBConfig" />
</classes>
</test>
<!-- @AfterTest -->
</suite>
<!-- @AfterSuite -->
输出
@BeforeSuite
@BeforeTest //Start {case1}
@BeforeClass
@BeforeGroups
@BeforeMethod
@Test - runTest1
@AfterMethod
@AfterGroups
@BeforeMethod
@Test - runTest2
@AfterMethod
@AfterClass
@Test - runOtherTest1
@Test - runOtherTest2
@AfterTest //End {case1}
@BeforeTest //Start {case2}
@Test - runOtherTest1
@Test - runOtherTest2
@AfterTest //End {case2}
@AfterSuite
===============================================
TestAll
Total tests run: 6, Failures: 0, Skips: 0
===============================================
完成了。
参考
测试文档
TestNG–运行多个测试类(套件测试)
suite test testng testng config
TestNG–预期异常测试
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/unittest/testng-tutorial-2-expected-exception-test/
在本教程中,我们将向您展示如何使用 TestNG expectedExceptions
来测试代码中预期的异常抛出。
1.运行时异常
这个例子向你展示了如何测试一个运行时异常。如果方法divisionWithException ()
抛出一个运行时异常——ArithmeticException
,它将被传递。
TestRuntime.java
package com.mkyong.testng.examples.exception;
import org.testng.annotations.Test;
public class TestRuntime {
@Test(expectedExceptions = ArithmeticException.class)
public void divisionWithException() {
int i = 1 / 0;
}
}
上述单元测试将通过。
2.检查异常
检查一个简单的业务对象,保存和更新方法,如果出错,抛出自定义的检查异常。
OrderBo.java
package com.mkyong.testng.project.order;
public class OrderBo {
public void save(Order order) throws OrderSaveException {
if (order == null) {
throw new OrderSaveException("Order is empty!");
}
// persist it
}
public void update(Order order) throws OrderUpdateException, OrderNotFoundException {
if (order == null) {
throw new OrderUpdateException("Order is empty!");
}
// If order is not available in the database
throw new OrderNotFoundException("Order is not exists");
}
}
测试预期异常的示例。
TestCheckedException.java
package com.mkyong.testng.examples.exception;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
import com.mkyong.testng.project.order.Order;
import com.mkyong.testng.project.order.OrderBo;
import com.mkyong.testng.project.order.OrderNotFoundException;
import com.mkyong.testng.project.order.OrderSaveException;
import com.mkyong.testng.project.order.OrderUpdateException;
public class TestCheckedException {
OrderBo orderBo;
Order data;
@BeforeTest
void setup() {
orderBo = new OrderBo();
data = new Order();
data.setId(1);
data.setCreatedBy("mkyong");
}
@Test(expectedExceptions = OrderSaveException.class)
public void throwIfOrderIsNull() throws OrderSaveException {
orderBo.save(null);
}
/*
* Example : Multiple expected exceptions
* Test is success if either of the exception is thrown
*/
@Test(expectedExceptions = { OrderUpdateException.class, OrderNotFoundException.class })
public void throwIfOrderIsNotExists() throws OrderUpdateException, OrderNotFoundException {
orderBo.update(data);
}
}
上述单元测试将通过。
下载源代码
Download it – TestNG-Example-Excepted-Exception.zip (11 kb)
参考
TestNG 预期异常 JavaDoc
expected exception testng
TestNG–如何忽略一个测试方法
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/unittest/testng-tutorial-3-ignore-test/
在本教程中,我们将向您展示如何用@Test(enabled = false)
忽略一个测试方法。
TestIgnore.java
package com.mkyong.testng.examples.ignore;
import org.testng.Assert;
import org.testng.annotations.Test;
public class TestIgnore {
@Test //default enable=true
public void test1() {
Assert.assertEquals(true, true);
}
@Test(enabled = true)
public void test2() {
Assert.assertEquals(true, true);
}
@Test(enabled = false)
public void test3() {
Assert.assertEquals(true, true);
}
}
输出
[TestNG] Running:
PASSED: test1
PASSED: test2
===============================================
Default test
Tests run: 2, Failures: 0, Skips: 0
===============================================
在上面的例子中,test3 ()
测试方法被忽略。
ignore testng (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/20190227120518/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0 ')
TestNG–超时测试
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/unittest/testng-tutorial-4-time-test/
在本教程中,我们将向您展示如何在 TestNG 中执行timeout
测试。“超时”意味着如果一个单元测试花费的时间超过了指定的毫秒数,TestNG 将会中止它,并将其作为失败处理。
这个“超时”也可以用于性能测试,以确保方法在合理的时间内返回。
TestTimeout.java
package com.mkyong.testng.examples.timeout;
import org.testng.annotations.Test;
public class TestTimeout {
@Test(timeOut = 5000) // time in mulliseconds
public void testThisShouldPass() throws InterruptedException {
Thread.sleep(4000);
}
@Test(timeOut = 1000)
public void testThisShouldFail() {
while (true);
}
}
输出
[TestNG] Running:
PASSED: testThisShouldPass
FAILED: testThisShouldFail
org.testng.internal.thread.ThreadTimeoutException: Method org.testng.internal.TestNGMethod.testThisShouldFail() didn't finish within the time-out 1000
at com.mkyong.testng.examples.timeout.TestTimeout.testThisShouldFail(TestTimeout.java:14)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:84)
at org.testng.internal.InvokeMethodRunnable.runOne(InvokeMethodRunnable.java:46)
at org.testng.internal.InvokeMethodRunnable.run(InvokeMethodRunnable.java:37)
at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.util.concurrent.FutureTask.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
===============================================
Default test
Tests run: 2, Failures: 1, Skips: 0
===============================================
testng timeout (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/20190227120240/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0 ')
TestNG–运行多个测试类(套件测试)
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/unittest/testng-tutorial-5-suite-test/
在本教程中,我们将向您展示如何一起运行多个 TestNG 测试用例(类),也称为 suite test。
1.测试类
复习以下三个测试类。
TestConfig.java
package com.mkyong.testng.examples.suite;
import org.testng.annotations.AfterSuite;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.BeforeTest;
//show the use of @BeforeSuite and @BeforeTest
public class TestConfig {
@BeforeSuite
public void testBeforeSuite() {
System.out.println("testBeforeSuite()");
}
@AfterSuite
public void testAfterSuite() {
System.out.println("testAfterSuite()");
}
@BeforeTest
public void testBeforeTest() {
System.out.println("testBeforeTest()");
}
@AfterTest
public void testAfterTest() {
System.out.println("testAfterTest()");
}
}
TestDatabase.java
package com.mkyong.testng.examples.suite;
import org.testng.annotations.Test;
public class TestDatabase {
@Test(groups = "db")
public void testConnectOracle() {
System.out.println("testConnectOracle()");
}
@Test(groups = "db")
public void testConnectMsSQL() {
System.out.println("testConnectMsSQL");
}
@Test(groups = "db-nosql")
public void testConnectMongoDB() {
System.out.println("testConnectMongoDB");
}
@Test(groups = { "db", "brokenTests" })
public void testConnectMySQL() {
System.out.println("testConnectMySQL");
}
}
TestOrder.java
package com.mkyong.testng.examples.suite;
import org.testng.annotations.Test;
public class TestOrder {
@Test(groups={"orderBo", "save"})
public void testMakeOrder() {
System.out.println("testMakeOrder");
}
@Test(groups={"orderBo", "save"})
public void testMakeEmptyOrder() {
System.out.println("testMakeEmptyOrder");
}
@Test(groups="orderBo")
public void testUpdateOrder() {
System.out.println("testUpdateOrder");
}
@Test(groups="orderBo")
public void testFindOrder() {
System.out.println("testFindOrder");
}
}
2.Testng.xml
要运行上面的测试类,创建一个 XML 文件-testng.xml
(可以是任何文件名)文件,并如下定义细节:
testng.xml
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="TestAll">
<test name="order">
<classes>
<class name="com.mkyong.testng.examples.suite.TestConfig" />
<class name="com.mkyong.testng.examples.suite.TestOrder" />
</classes>
</test>
<test name="database">
<classes>
<class name="com.mkyong.testng.examples.suite.TestConfig" />
<class name="com.mkyong.testng.examples.suite.TestDatabase" />
</classes>
</test>
</suite>
输出
[TestNG] Running:
C:\mkyong_projects\TestNG\src\test\resources\testng-all.xml
testBeforeSuite()
testBeforeTest()
testFindOrder
testMakeEmptyOrder
testMakeOrder
testUpdateOrder
testAfterTest()
testBeforeTest()
testConnectMongoDB
testConnectMsSQL
testConnectMySQL
testConnectOracle()
testAfterTest()
testAfterSuite()
3.其他示例
下面是一些常用的例子。
3.1 指定包名而不是类名:
testng.xml
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="TestAll">
<test name="order">
<packages>
<package name="com.mkyong.testng.examples.suite.*" />
</packages>
</test>
</suite>
3.2 指定包含或排除的方法:
testng.xml
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="TestAll">
<test name="order">
<classes>
<class name="com.mkyong.testng.examples.suite.TestConfig" />
<class name="com.mkyong.testng.examples.suite.TestOrder">
<methods>
<include name="testMakeOrder" />
<include name="testUpdateOrder" />
<!--
<exclude name="testMakeOrder" />
-->
</methods>
</class>
</classes>
</test>
</suite>
输出
[TestNG] Running:
C:\mkyong_projects\TestNG\src\test\resources\testng.xml
testBeforeSuite()
testBeforeTest()
testMakeOrder
testUpdateOrder
testAfterTest()
testAfterSuite()
3.3 指定要包括或排除的组:
testng.xml
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="TestAll">
<test name="database">
<groups>
<run>
<exclude name="brokenTests" />
<include name="db" />
</run>
</groups>
<classes>
<class name="com.mkyong.testng.examples.suite.TestDatabase" />
</classes>
</test>
</suite>
输出
[TestNG] Running:
C:\mkyong_projects\TestNG\src\test\resources\testng.xml
testConnectMsSQL
testConnectOracle()
参考
TestNG–TestNG . XML 文件
suite test testng
TestNG–参数测试(XML 和@DataProvider)
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/unittest/testng-tutorial-6-parameterized-test/
在本教程中,我们将向您展示如何通过 XML @Parameters
或@DataProvider
将参数传递给@Test
方法。
1.用 XML 传递参数
在这个例子中,属性 filename 从testng.xml
传递,并通过@Parameters
注入到方法中。
TestParameterXML.java
package com.mkyong.testng.examples.parameter;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Properties;
import org.testng.annotations.Parameters;
import org.testng.annotations.Test;
public class TestParameterXML {
Connection con;
@Test
@Parameters({ "dbconfig", "poolsize" })
public void createConnection(String dbconfig, int poolsize) {
System.out.println("dbconfig : " + dbconfig);
System.out.println("poolsize : " + poolsize);
Properties prop = new Properties();
InputStream input = null;
try {
//get properties file from project classpath
input = getClass().getClassLoader().getResourceAsStream(dbconfig);
prop.load(input);
String drivers = prop.getProperty("jdbc.driver");
String connectionURL = prop.getProperty("jdbc.url");
String username = prop.getProperty("jdbc.username");
String password = prop.getProperty("jdbc.password");
System.out.println("drivers : " + drivers);
System.out.println("connectionURL : " + connectionURL);
System.out.println("username : " + username);
System.out.println("password : " + password);
Class.forName(drivers);
con = DriverManager.getConnection(connectionURL, username, password);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (input != null) {
try {
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
db.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mkyongserver
jdbc.username=mkyong
jdbc.password=password
testng.xml
<!DOCTYPE suite SYSTEM "http://beust.com/testng/testng-1.0.dtd" >
<suite name="test-parameter">
<test name="example1">
<parameter name="dbconfig" value="db.properties" />
<parameter name="poolsize" value="10" />
<classes>
<class name="com.mkyong.testng.examples.parameter.TestParameterXML" />
</classes>
</test>
</suite>
输出
dbconfig : db.properties
poolsize : 10
drivers : com.mysql.jdbc.Driver
connectionURL : jdbc:mysql://localhost:3306/mkyongserver
username : mkyong
password : password
2.用@DataProvider 传递参数
2.1 回顾一个简单的@DataProvider
例子,传递一个int
参数。
TestParameterDataProvider.java
package com.mkyong.testng.examples.parameter;
import org.testng.Assert;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class TestParameterDataProvider {
@Test(dataProvider = "provideNumbers")
public void test(int number, int expected) {
Assert.assertEquals(number + 10, expected);
}
@DataProvider(name = "provideNumbers")
public Object[][] provideData() {
return new Object[][] {
{ 10, 20 },
{ 100, 110 },
{ 200, 210 }
};
}
}
输出
PASSED: test(10, 20)
PASSED: test(100, 110)
PASSED: test(200, 210)
2.2 @DataProvider
是传递一个object
参数的支持。下面的例子展示了如何传递一个Map
对象作为参数。
TestParameterDataProvider.java
package com.mkyong.testng.examples.parameter;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import org.testng.Assert;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class TestParameterDataProvider {
@Test(dataProvider = "dbconfig")
public void testConnection(Map<String, String> map) {
for (Map.Entry<String, String> entry : map.entrySet()) {
System.out.println("[Key] : " + entry.getKey()
+ " [Value] : " + entry.getValue());
}
}
@DataProvider(name = "dbconfig")
public Object[][] provideDbConfig() {
Map<String, String> map = readDbConfig();
return new Object[][] { { map } };
}
public Map<String, String> readDbConfig() {
Properties prop = new Properties();
InputStream input = null;
Map<String, String> map = new HashMap<String, String>();
try {
input = getClass().getClassLoader().getResourceAsStream("db.properties");
prop.load(input);
map.put("jdbc.driver", prop.getProperty("jdbc.driver"));
map.put("jdbc.url", prop.getProperty("jdbc.url"));
map.put("jdbc.username", prop.getProperty("jdbc.username"));
map.put("jdbc.password", prop.getProperty("jdbc.password"));
} catch (Exception e) {
e.printStackTrace();
} finally {
if (input != null) {
try {
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return map;
}
}
输出
[Key] : jdbc.url [Value] : jdbc:mysql://localhost:3306/mkyongserver
[Key] : jdbc.username [Value] : mkyong
[Key] : jdbc.driver [Value] : com.mysql.jdbc.Driver
[Key] : jdbc.password [Value] : password
PASSED: testConnection({jdbc.url=jdbc:mysql://localhost:3306/mkyongserver,
jdbc.username=mkyong, jdbc.driver=com.mysql.jdbc.Driver, jdbc.password=password})
3.@DataProvider +方法
这个例子向您展示了如何根据测试方法的名称来传递不同的参数。
TestParameterDataProvider.java
package com.mkyong.testng.examples.parameter;
import java.lang.reflect.Method;
import org.testng.Assert;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class TestParameterDataProvider {
@Test(dataProvider = "dataProvider")
public void test1(int number, int expected) {
Assert.assertEquals(number, expected);
}
@Test(dataProvider = "dataProvider")
public void test2(String email, String expected) {
Assert.assertEquals(email, expected);
}
@DataProvider(name = "dataProvider")
public Object[][] provideData(Method method) {
Object[][] result = null;
if (method.getName().equals("test1")) {
result = new Object[][] {
{ 1, 1 }, { 200, 200 }
};
} else if (method.getName().equals("test2")) {
result = new Object[][] {
{ "test@gmail.com", "test@gmail.com" },
{ "test@yahoo.com", "test@yahoo.com" }
};
}
return result;
}
}
输出
PASSED: test1(1, 1)
PASSED: test1(200, 200)
PASSED: test2("test@gmail.com", "test@gmail.com")
PASSED: test2("test@yahoo.com", "test@yahoo.com")
4. @DataProvider + ITestContext
在 TestNG 中,我们可以使用org.testng.ITestContext
来确定调用当前测试方法的运行时参数。在最后一个例子中,我们将向您展示如何根据包含的组名来传递参数。
TestParameterDataProvider.java
package com.mkyong.testng.examples.parameter;
import org.testng.Assert;
import org.testng.ITestContext;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class TestParameterDataProvider {
@Test(dataProvider = "dataProvider", groups = {"groupA"})
public void test1(int number) {
Assert.assertEquals(number, 1);
}
@Test(dataProvider = "dataProvider", groups = "groupB")
public void test2(int number) {
Assert.assertEquals(number, 2);
}
@DataProvider(name = "dataProvider")
public Object[][] provideData(ITestContext context) {
Object[][] result = null;
//get test name
//System.out.println(context.getName());
for (String group : context.getIncludedGroups()) {
System.out.println("group : " + group);
if ("groupA".equals(group)) {
result = new Object[][] { { 1 } };
break;
}
}
if (result == null) {
result = new Object[][] { { 2 } };
}
return result;
}
}
testng.xml
<!DOCTYPE suite SYSTEM "http://beust.com/testng/testng-1.0.dtd" >
<suite name="test-parameter">
<test name="example1">
<groups>
<run>
<include name="groupA" />
</run>
</groups>
<classes>
<class
name="com.mkyong.testng.examples.parameter.TestParameterDataProvider" />
</classes>
</test>
</suite>
输出
group : groupA
完成了。
参考
TestNG @DataProvider
TestNG ITestContext JavaDoc
使用 JDBC 驱动程序连接 MySQL】
parameter test testng
TestNG–依赖性测试
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/unittest/testng-tutorial-7-dependency-test/
在 TestNG 中,我们使用dependOnMethods
和dependsOnGroups
来实现依赖测试。如果一个从属方法失败,所有后续的测试方法将被跳过,而不是失败。
1.dependOnMethods 示例
一个简单的例子,“方法 2()”依赖于“方法 1()”。
1.1 如果method1()
通过,将执行method2()
。
App.java
package com.mkyong.testng.examples.dependency;
import org.testng.annotations.Test;
public class App {
@Test
public void method1() {
System.out.println("This is method 1");
}
@Test(dependsOnMethods = { "method1" })
public void method2() {
System.out.println("This is method 2");
}
}
输出
This is method 1
This is method 2
PASSED: method1
PASSED: method2
===============================================
Default test
Tests run: 2, Failures: 0, Skips: 0
===============================================
1.2 如果method1()
失败,method2()
将被跳过。
App.java
package com.mkyong.testng.examples.dependency;
import org.testng.annotations.Test;
public class App {
//This test will be failed.
@Test
public void method1() {
System.out.println("This is method 1");
throw new RuntimeException();
}
@Test(dependsOnMethods = { "method1" })
public void method2() {
System.out.println("This is method 2");
}
}
输出
This is method 1
FAILED: method1
java.lang.RuntimeException
at com.mkyong.testng.examples.dependency.App.method1(App.java:10)
//...
SKIPPED: method2
===============================================
Default test
Tests run: 2, Failures: 1, Skips: 1
===============================================
2.dependsOnGroups 示例
让我们创建几个测试用例来演示dependsOnMethods
和dependsOnGroups
的混合使用。不言自明的见注释。
TestServer.java
package com.mkyong.testng.examples.dependency;
import org.testng.annotations.Test;
//all methods of this class are belong to "deploy" group.
@Test(groups="deploy")
public class TestServer {
@Test
public void deployServer() {
System.out.println("Deploying Server...");
}
//Run this if deployServer() is passed.
@Test(dependsOnMethods="deployServer")
public void deployBackUpServer() {
System.out.println("Deploying Backup Server...");
}
}
TestDatabase.java
package com.mkyong.testng.examples.dependency;
import org.testng.annotations.Test;
public class TestDatabase {
//belong to "db" group,
//Run if all methods from "deploy" group are passed.
@Test(groups="db", dependsOnGroups="deploy")
public void initDB() {
System.out.println("This is initDB()");
}
//belong to "db" group,
//Run if "initDB" method is passed.
@Test(dependsOnMethods = { "initDB" }, groups="db")
public void testConnection() {
System.out.println("This is testConnection()");
}
}
TestApp.java
package com.mkyong.testng.examples.dependency;
import org.testng.annotations.Test;
public class TestApp {
//Run if all methods from "deploy" and "db" groups are passed.
@Test(dependsOnGroups={"deploy","db"})
public void method1() {
System.out.println("This is method 1");
//throw new RuntimeException();
}
//Run if method1() is passed.
@Test(dependsOnMethods = { "method1" })
public void method2() {
System.out.println("This is method 2");
}
}
创建一个 XML 文件并一起测试它们。
testng.xml
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="TestDependency">
<test name="TestCase1">
<classes>
<class
name="com.mkyong.testng.examples.dependency.TestApp">
</class>
<class
name="com.mkyong.testng.examples.dependency.TestDatabase">
</class>
<class
name="com.mkyong.testng.examples.dependency.TestServer">
</class>
</classes>
</test>
</suite>
输出
Deploying Server...
Deploying Backup Server...
This is initDB()
This is testConnection()
This is method 1
This is method 2
===============================================
TestDependency
Total tests run: 6, Failures: 0, Skips: 0
===============================================
## 参考
测试依赖方法
dependency test testng
TestNG 教程
原文:http://web.archive.org/web/20230101150211/https://mkyong.com/tutorials/testng-tutorials/
TestNG(下一代)是一个测试框架,受 JUnit 和 NUnit 的启发,但引入了许多新的创新功能,如依赖测试、分组概念,使测试更强大、更容易。它旨在涵盖所有类别的测试:单元、功能、端到端、集成等…
1.测试教程
参考
TestNG 官网
Struts TextArea 示例
原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/struts/struts-htmltextarea-textarea-example/
Download this Struts text area example – Struts-TextArea-Example.zip
在这个 Struts 示例中,您将学习如何使用 Struts 标记创建一个 HTML 文本区域输入字段。
1.文件夹结构
这是 Maven 创建的最终项目结构。请创建相应的文件夹。
2.动作类
创建一个 Action 类,除了转发请求什么也不做。
html text reaction . Java
package com.mkyong.common.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.common.form.HtmlTextAreaForm;
public class HtmlTextAreaAction extends Action{
public ActionForward execute(ActionMapping mapping,ActionForm form,
HttpServletRequest request,HttpServletResponse response)
throws Exception {
HtmlTextAreaForm htmlTextAreaForm = (HtmlTextAreaForm)form;
return mapping.findForward("success");
}
}
3.属性文件
创建一个属性文件,并声明错误和标签消息。
公共属性
#error message
error.common.html.textarea.required = "Address" field is required.
#label message
label.common.html.textarea.address = Address
label.common.html.textarea.button.submit = Submit
label.common.html.textarea.button.reset = Reset
4.动作形式
创建一个 ActionForm,包含一个地址变量来保存文本区域的输入值。
HtmlTextAreaForm.java
package com.mkyong.common.form;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionMessage;
public class HtmlTextAreaForm extends ActionForm{
String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public ActionErrors validate(ActionMapping mapping,
HttpServletRequest request) {
ActionErrors errors = new ActionErrors();
if( getAddress() == null || ("".equals(getAddress())))
{
errors.add("common.textarea.err",
new ActionMessage("error.common.html.textarea.required"));
}
return errors;
}
@Override
public void reset(ActionMapping mapping, HttpServletRequest request) {
// reset properties
address = "";
}
}
5.JSP 页面
使用 Struts HTML 标签创建一个 HTML 文本区域输入字段。
textarea.jsp
<%@taglib uri="http://struts.apache.org/tags-html" prefix="html"%>
<%@taglib uri="http://struts.apache.org/tags-bean" prefix="bean"%>
Struts html:textarea 示例
: </div> <div style="padding:16px"> <div style="float:left;padding-right:8px;"> <submit> <message key="label.common.html.textarea.button.submit"/> </submit> </div> <reset> <message key="label.common.html.textarea.button.reset"/> </reset> </div> </form>
```
<p>从 htmlTextAreaForm 表单中获取文本区域输入值并显示它</p>
<p>display.jsp</p>
<pre><code class="language-java"><%@taglib uri="http://struts.apache.org/tags-bean" prefix="bean"%> <h1>您的地址是:<write name="htmlTextAreaForm" property="address"/></h1>
</code></pre>
<h2 id="6struts-configxml-5">6.struts-config.xml</h2>
<p>创建一个 Struts 配置文件,并将它们链接在一起。</p>
<pre><code class="language-java"><?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="htmlTextAreaForm" type="com.mkyong.common.form.HtmlTextAreaForm"/> </form-beans> <action-mappings> <action path="/TextAreaPage" type="org.apache.struts.actions.ForwardAction" parameter="/pages/textarea.jsp"/> <action path="/TextArea" type="com.mkyong.common.action.HtmlTextAreaAction" name="htmlTextAreaForm" validate="true" input="/pages/textarea.jsp" > <forward name="success" path="/pages/display.jsp"/> </action> </action-mappings> <message-resources parameter="com.mkyong.common.properties.Common" /> </struts-config>
</code></pre>
<h2 id="7webxml-4">7.web.xml</h2>
<p>最后一步,创建一个 web.xml 并集成 Struts 框架</p>
<pre><code class="language-java"><!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> </web-app>
</code></pre>
<p>访问它</p>
<blockquote>
<p><a href="http://localhost:8080/struts" target="_blank">http://localhost:8080/struts</a> example/textarea page . do</p>
</blockquote>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/e3f64ef1dd33aabb2476f8bcd66ffd5e.png" alt="" loading="lazy"></p>
<p>填写地址并按下提交按钮,它将转发到</p>
<blockquote>
<p><a href="http://localhost:8080/struts" target="_blank">http://localhost:8080/struts</a> example/textarea . do</p>
</blockquote>
<p>并在地址中显示您的密钥。</p>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/d7def17456a40eb268e1e8297d8d3083.png" alt="" loading="lazy"></p>
<h1 id="绝对-urihttpstrutsapacheorgtags-bean-无法在-webxml-或与此应用程序一起部署的-jar-文件中解析">绝对 uri:<a href="http://struts.apache.org/tags-bean" target="_blank">http://struts.apache.org/tags-bean</a> 无法在 web.xml 或与此应用程序一起部署的 jar 文件中解析。</h1>
<blockquote>
<p>原文:<a href="http://web.archive.org/web/20230101150211/http://www.mkyong.com/struts/the-absolute-uri-httpstruts-apache-orgtags-bean-cannot-be-resolved-in-either-web-xml-or-the-jar-files-deployed-with-this-application/" target="_blank">http://web.archive.org/web/20230101150211/http://www.mkyong.com/struts/the-absolute-uri-httpstruts-apache-orgtags-bean-cannot-be-resolved-in-either-web-xml-or-the-jar-files-deployed-with-this-application/</a></p>
</blockquote>
<h2 id="问题-2">问题</h2>
<p>访问 Struts 标记库 JSP 文件时的 Struts 常见错误消息。</p>
<pre><code class="language-java"><%@taglib uri="http://struts.apache.org/tags-bean" prefix="bean"%>
</code></pre>
<pre><code class="language-java"> org.apache.jasper.JasperException: The absolute uri:
http://struts.apache.org/tags-bean
org.apache.jasper.JasperException: The absolute uri:
http://struts.apache.org/tags-html
org.apache.jasper.JasperException: The absolute uri:
http://struts.apache.org/tags-logic
org.apache.jasper.JasperException: The absolute uri:
http://struts.apache.org/tags-tiles
cannot be resolved in either web.xml or
the jar files deployed with this application
</code></pre>
<h2 id="解决办法-2">解决办法</h2>
<p>这是因为您没有配置 Struts 标记库属性,在您的项目依赖项中找不到 tld 文件。</p>
<h2 id="1支柱标记库手动配置">1.支柱标记库手动配置</h2>
<p>如果你是手动配置 Struts 标签库,用在 <strong>Struts 版本< = 1.1 和 Servlet < 2.3 容器</strong>中。请确保将下面的“ <strong>tld</strong> 文件复制到 <strong>WEB-INF</strong> 文件夹中,您可以在您的 Struts 库文件夹中找到这些文件。</p>
<ul>
<li>struts-bean.tld</li>
<li>struts-html.tld</li>
<li>struts-logic.tld</li>
<li>struts-tiles.tld</li>
</ul>
<p>并在 web.xml<br>
<strong>web.xml</strong> 中适当定义</p>
<pre><code class="language-java"> ...
<taglib>
<taglib-uri>
http://struts.apache.org/tags-bean
</taglib-uri>
<taglib-location>
/WEB-INF/struts-bean.tld
</taglib-location>
</taglib>
...
</code></pre>
<h2 id="2支柱标签库自动配置">2.支柱标签库自动配置</h2>
<p>如果你是自动配置 Struts 标签库,用在<strong>Servlet 2.3/2.4 版和 Struts 1.2 或 1.3</strong> 框架中。确保 <strong>struts-taglib.jar</strong> 在你的 <strong>/WEB-INF/lib</strong> 目录中。</p>
<p>你可以在这里查看 <a href="http://web.archive.org/web/20190220115711/http://www.mkyong.com/struts/configure-the-struts-tag-libraries/" target="_blank">Struts 标签库的配置细节。</a></p>
<h2 id="3eclipse-ide-调试会话">3.Eclipse IDE 调试会话</h2>
<p>如果这是在 Eclipse IDE 调试会话期间发生的,只需确保您的项目依赖项被部署到正确的文件夹。解决方案请查看<a href="http://web.archive.org/web/20190220115711/http://www.mkyong.com/maven/maven-dependency-libraries-not-deploy-in-eclipse-ide/" target="_blank">这篇文章。</a></p>
<h2 id="结论-2">结论</h2>
<p>问题的原因可能因项目而异,但解决方案总是相同的</p>
<ul>
<li>检查 WEB-INF 中的 tld 文件是否可用(旧的 Struts 样式)。</li>
<li>请检查标记库 uri 没有打字错误。</li>
<li>检查 <strong>struts-taglib.jar</strong> 在 <strong>/WEB-INF/lib</strong> 或项目依赖(新的 struts 样式)中是否可用。</li>
</ul>
<p><a href="http://web.archive.org/web/20190220115711/http://www.mkyong.com/tag/struts/" target="_blank">struts</a></p>
<h1 id="没有为命名空间和操作名youractionname映射的操作">没有为命名空间/和操作名“yourActionName”映射的操作</h1>
<blockquote>
<p>原文:<a href="http://web.archive.org/web/20230101150211/http://www.mkyong.com/struts2/there-is-no-action-mapped-for-namespace-and-action-name-youractionname/" target="_blank">http://web.archive.org/web/20230101150211/http://www.mkyong.com/struts2/there-is-no-action-mapped-for-namespace-and-action-name-youractionname/</a></p>
</blockquote>
<h2 id="问题-3">问题</h2>
<p>许多 Struts 2 开发人员声称他们已经正确配置了 action 类,但是在访问 action 类时遇到了 actions 错误消息。</p>
<p>查看两种模式下的错误信息:</p>
<h2 id="1strutsdevmode处于关闭状态默认">1.“struts.devMode”处于关闭状态(默认)</h2>
<pre><code class="language-java"> HTTP Status 404 -
There is no Action mapped for namespace / and action name "yourActionName".
type Status report
message There is no Action mapped for namespace / and action name "yourActionName".
description The requested resource
(There is no Action mapped for namespace / and action name "yourActionName".) is not available.
</code></pre>
<h2 id="2strutsdevmode-已打开">2.struts.devMode 已打开</h2>
<p>在<code>struts.xml</code></p>
<pre><code class="language-java"> <constant name="struts.devMode" value="true" />
</code></pre>
<pre><code class="language-java"> Struts Problem Report
Struts has detected an unhandled exception:
Messages:
There is no Action mapped for namespace / and action name "yourActionName".
Stacktraces
There is no Action mapped for namespace / and action name "yourActionName". - [unknown location]
com.opensymphony.xwork2.DefaultActionProxy.prepare(DefaultActionProxy.java:178)
org.apache.struts2.impl.StrutsActionProxy.prepare(StrutsActionProxy.java:61)
...
You are seeing this page because development mode is enabled. Development mode, or devMode,
enables extra debugging behaviors and reports to assist developers. To disable this mode, set:
struts.devMode=false
in your WEB-INF/classes/struts.properties file.
</code></pre>
<h2 id="解决办法-3">解决办法</h2>
<p>上面的错误信息是说 action 类不可用,这意味着您在您的 action 类配置中做错了什么,可能是一个<strong>名称空间</strong>或<strong>错别字</strong>错误,只是仔细检查名称。</p>
<p>这里有一个工作动作类配置在<code>struts.xml</code>文件中,可能会供你参考使用。</p>
<pre><code class="language-java"> <?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="abcAction"
class="com.mkyong.common.action.AbcAction" >
<result name="success">pages/abc.jsp</result>
</action>
</package>
</struts>
</code></pre>
<p>假设项目的根上下文是" <strong>Struts2Example</strong> ",那么你可以通过这个 URL 访问上面的动作——<br>
T3【<a href="http://localhost:8080/struts" target="_blank">http://localhost:8080/struts</a> 2 example/ABC action . action</p>
<p><a href="http://web.archive.org/web/20190304030444/http://www.mkyong.com/tag/struts2/" target="_blank">struts2</a></p>
<h1 id="单元测试什么是嘲讽为什么呢">单元测试——什么是嘲讽?为什么呢?</h1>
<blockquote>
<p>原文:<a href="http://web.archive.org/web/20230101150211/http://www.mkyong.com/unittest/unit-test-what-is-mocking-and-why/" target="_blank">http://web.archive.org/web/20230101150211/http://www.mkyong.com/unittest/unit-test-what-is-mocking-and-why/</a></p>
</blockquote>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/e659af74b9be8f511d87e5f06ca15ef9.png" alt="mockito-logo" loading="lazy"></p>
<p>简单地说,模仿就是创建模仿真实对象行为的对象。请参考以下案例研究:</p>
<p>测试:</p>
<ol>
<li>Java 1.8</li>
<li>JUnit 4.12</li>
<li>Mockito 2.0.73-beta</li>
</ol>
<p><strong>Mock Object</strong><br>
Read this <a href="http://web.archive.org/web/20221024153706/https://en.wikipedia.org/wiki/Mock_object" target="_blank">Wikipedia Mock object</a>.</p>
<h2 id="1java-示例">1.Java 示例</h2>
<p>一个简单的作者和书籍的例子。</p>
<p>1.1 <code>BookService</code>按作者姓名返回图书列表。</p>
<p>BookService.java</p>
<pre><code class="language-java"> package com.mkyong.examples.mock;
import java.util.List;
public interface BookService {
List<Book> findBookByAuthor(String author);
}
</code></pre>
<p>BookServiceImpl.java</p>
<pre><code class="language-java"> package com.mkyong.examples.mock;
import java.util.List;
public class BookServiceImpl implements BookService {
private BookDao bookDao;
public BookDao getBookDao() {
return bookDao;
}
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
@Override
public List<Book> findBookByAuthor(String name) {
return bookDao.findBookByAuthor(name);
}
}
</code></pre>
<p>BookDao.java</p>
<pre><code class="language-java"> package com.mkyong.examples.mock;
import java.util.List;
public interface BookDao {
List<Book> findBookByAuthor(String author);
}
</code></pre>
<p>BookDaoImpl.java</p>
<pre><code class="language-java"> package com.mkyong.examples.mock;
import java.util.List;
public class BookDaoImpl implements BookDao {
@Override
public List<Book> findBookByAuthor(String name) {
// init database
// Connect to DB for data
// return data
}
}
</code></pre>
<p>1.2 图书验证器。</p>
<p>BookValidatorService.java</p>
<pre><code class="language-java"> package com.mkyong.examples.mock;
public interface BookValidatorService {
boolean isValid(Book book);
}
</code></pre>
<p>FakeBookValidatorService.java</p>
<pre><code class="language-java"> package com.mkyong.examples.mock;
public class FakeBookValidatorService implements BookValidatorService {
@Override
public boolean isValid(Book book) {
if (book == null)
return false;
if ("bot".equals(book.getName())) {
return false;
} else {
return true;
}
}
}
</code></pre>
<p>1.3 回顾了<code>AuthorServiceImpl</code>,它依赖于<code>BookService</code>(依赖于<code>BookDao</code>)和<code>BookValidatorService</code>,这使得单元测试有点难写。</p>
<p>AuthorService.java</p>
<pre><code class="language-java"> package com.mkyong.examples.mock;
public interface AuthorService {
int getTotalBooks(String author);
}
</code></pre>
<p>AuthorServiceImpl.java</p>
<pre><code class="language-java"> package com.mkyong.examples.mock;
import java.util.List;
import java.util.stream.Collectors;
public class AuthorServiceImpl implements AuthorService {
private BookService bookService;
private BookValidatorService bookValidatorService;
public BookValidatorService getBookValidatorService() {
return bookValidatorService;
}
public void setBookValidatorService(BookValidatorService bookValidatorService) {
this.bookValidatorService = bookValidatorService;
}
public BookService getBookService() {
return bookService;
}
public void setBookService(BookService bookService) {
this.bookService = bookService;
}
//How to test this method ???
@Override
public int getTotalBooks(String author) {
List<Book> books = bookService.findBookByAuthor(author);
//filters some bot writers
List<Book> filtered = books.stream().filter(
x -> bookValidatorService.isValid(x))
.collect(Collectors.toList());
//other business logic
return filtered.size();
}
}
</code></pre>
<h2 id="2单元测试">2.单元测试</h2>
<p>为<code>AuthorServiceImpl.getTotalBooks()</code>创建一个单元测试</p>
<p>2.1<code>AuthorServiceImpl</code>有两个依赖项,你需要确保两者都配置正确。</p>
<p>AuthorServiceTest.java</p>
<pre><code class="language-java"> package com.mkyong.mock;
import com.mkyong.examples.mock.AuthorServiceImpl;
import com.mkyong.examples.mock.BookDaoImpl;
import com.mkyong.examples.mock.BookServiceImpl;
import com.mkyong.examples.mock.FakeBookValidatorService;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
public class AuthorServiceTest {
@Test
public void test_total_book_by_mock() {
//1\. Setup
AuthorServiceImpl obj = new AuthorServiceImpl();
BookServiceImpl bookService = new BookServiceImpl();
bookService.setBookDao(new BookDaoImpl()); //Where Dao connect to?
obj.setBookService(bookService);
obj.setBookValidatorService(new FakeBookValidatorService());
//2\. Test method
int qty = obj.getTotalBooks("mkyong");
//3\. Verify result
assertThat(qty, is(2));
}
}
</code></pre>
<p>要通过上面的单元测试,你需要在 DAO 层建立一个数据库,否则<code>bookService</code>将不返回任何东西。</p>
<p>2.3 执行上述测试的一些缺点:</p>
<ol>
<li>这个单元测试很慢,因为您需要启动一个数据库来从 DAO 获取数据。</li>
<li>这个单元测试不是孤立的,它总是依赖于外部资源,比如数据库。</li>
<li>这种单元测试不能确保测试条件总是相同的,数据库中的数据可能会随时间变化。</li>
<li>测试一个简单的方法工作量太大,导致开发人员跳过测试。</li>
</ol>
<p>2.4 <strong>解决方案</strong><br>
解决方案很明显,你需要一个<code>BookServiceImpl</code>类的修改版本——它将总是返回相同的数据进行测试,一个<strong>模拟对象</strong>!</p>
<p><strong>What is mocking?</strong><br>
Again, mocking is creating objects that mimic the behavior of real objects.</p>
<h2 id="3单元测试模拟对象">3.单元测试–模拟对象</h2>
<p>3.1 创建一个新的<code>MockBookServiceImpl</code>类,并总是为作者“mkyong”返回相同的图书集合。</p>
<p>MockBookServiceImpl.java</p>
<pre><code class="language-java"> package com.mkyong.mock;
import com.mkyong.examples.mock.Book;
import com.mkyong.examples.mock.BookService;
import java.util.ArrayList;
import java.util.List;
//I am a mock object!
public class MockBookServiceImpl implements BookService {
@Override
public List<Book> findBookByAuthor(String author) {
List<Book> books = new ArrayList<>();
if ("mkyong".equals(author)) {
books.add(new Book("mkyong in action"));
books.add(new Book("abc in action"));
books.add(new Book("bot"));
}
return books;
}
//implements other methods...
}
</code></pre>
<p>3.2 再次更新单元测试。</p>
<p>AuthorServiceTest.java</p>
<pre><code class="language-java"> package com.mkyong.mock;
import com.mkyong.examples.mock.AuthorServiceImpl;
import com.mkyong.examples.mock.FakeBookValidatorService;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
public class AuthorServiceTest {
@Test
public void test_total_book_by_mock() {
//1\. Setup
AuthorServiceImpl obj = new AuthorServiceImpl();
/*BookServiceImpl bookService = new BookServiceImpl();
bookService.setBookDao(new BookDaoImpl());
obj.setBookService(bookService);*/
obj.setBookService(new MockBookServiceImpl());
obj.setBookValidatorService(new FakeBookValidatorService());
//2\. Test method
int qty = obj.getTotalBooks("mkyong");
//3\. Verify result
assertThat(qty, is(2));
}
}
</code></pre>
<p>上面的单元测试要好得多,快速,隔离(没有更多的数据库)并且测试条件(数据)总是相同的。</p>
<p>3.3 但是,像上面这样手动创建模拟对象有一些缺点:</p>
<ol>
<li>最后,您可以创建许多模拟对象(类),只是为了单元测试的目的。</li>
<li>如果接口包含许多方法,您需要覆盖它们中的每一个。</li>
<li>还是工作量太大,而且乱七八糟!</li>
</ol>
<p>3.4 <strong>解决方案</strong><br>
试试 <a href="http://web.archive.org/web/20221024153706/http://mockito.org/" target="_blank">Mockito</a> ,一个简单而强大的嘲讽框架。</p>
<h2 id="4单元测试mock-ito">4.单元测试–mock ITO</h2>
<p>4.1 再次更新单元测试,这一次,通过 Mockito 框架创建模拟对象。</p>
<p>pom.xml</p>
<pre><code class="language-java"> <dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.0.73-beta</version>
</dependency>
</code></pre>
<p>AuthorServiceTest.java</p>
<pre><code class="language-java"> package com.mkyong.mock;
import com.mkyong.examples.mock.AuthorServiceImpl;
import com.mkyong.examples.mock.Book;
import com.mkyong.examples.mock.BookServiceImpl;
import com.mkyong.examples.mock.FakeBookValidatorService;
import org.junit.Test;
import java.util.Arrays;
import java.util.List;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class AuthorServiceTest {
@Test
public void test_total_book_by_mockito() {
//1\. Setup
List<Book> books = Arrays.asList(
new Book("mkyong in action"),
new Book("abc in action"),
new Book("bot"));
BookServiceImpl mockito = mock(BookServiceImpl.class);
//if the author is "mkyong", then return a 'books' object.
when(mockito.findBookByAuthor("mkyong")).thenReturn(books);
AuthorServiceImpl obj = new AuthorServiceImpl();
obj.setBookService(mockito);
obj.setBookValidatorService(new FakeBookValidatorService());
//2\. Test method
int qty = obj.getTotalBooks("mkyong");
//3\. Verify result
assertThat(qty, is(2));
}
}
</code></pre>
<p>完成了。感谢您的反馈</p>
<h2 id="参考-29">参考</h2>
<ol>
<li><a href="http://web.archive.org/web/20221024153706/http://mockito.org/" target="_blank">莫奇托官方网站</a></li>
<li><a href="http://web.archive.org/web/20221024153706/https://dzone.com/refcardz/mockito" target="_blank">DZone – Mockito</a></li>
<li><a href="http://web.archive.org/web/20221024153706/https://en.wikipedia.org/wiki/Mock_object" target="_blank">维基百科–模拟对象</a></li>
<li><a href="http://web.archive.org/web/20221024153706/http://www.vogella.com/tutorials/Mockito/article.html" target="_blank">使用 Mockito 的单元测试</a></li>
</ol>
<input type="hidden" id="mkyong-current-postId" value="14010">
<h1 id="警告jsf1063警告将不可序列化的属性值设置到-httpsession-中">警告:JSF1063:警告!将不可序列化的属性值设置到 HttpSession 中</h1>
<blockquote>
<p>原文:<a href="http://web.archive.org/web/20230101150211/http://www.mkyong.com/jsf2/warning-jsf1063-warning-setting-non-serializable-attribute-value-into-httpsession/" target="_blank">http://web.archive.org/web/20230101150211/http://www.mkyong.com/jsf2/warning-jsf1063-warning-setting-non-serializable-attribute-value-into-httpsession/</a></p>
</blockquote>
<h2 id="问题-4">问题</h2>
<p>在 JSF 2.0 web 应用程序中,在服务器初始化期间,它会显示以下警告消息</p>
<blockquote>
<p>警告:JSF1063:警告!将不可序列化的属性值设置到 HttpSession<br>
(key: user,value class: com.mkyong.UserBean)。</p>
</blockquote>
<p><strong>UserBean.java</strong></p>
<pre><code class="language-java"> package com.mkyong;
@ManagedBean(name="user")
@SessionScoped
public class UserBean{
//...
}
</code></pre>
<h2 id="解决办法-4">解决办法</h2>
<p>“UserBean”不可序列化。要消除这个警告消息,只需让这个 bean 实现 <strong>java.io.Serializable</strong> 接口。</p>
<p><strong>UserBean.java</strong></p>
<pre><code class="language-java"> package com.mkyong;
import java.io.Serializable;
@ManagedBean(name="user")
@SessionScoped
public class UserBean implements Serializable{
//...
}
</code></pre>
<p><a href="http://web.archive.org/web/20190116170620/http://www.mkyong.com/tag/jsf2/" target="_blank">jsf2</a><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/6a55ff97b6ecc1d6bfc25f29a165d377.png" alt="" loading="lazy"> (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=<mark>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]<a href="h,i%5Bl%5D%5Bq+y%5D(h)+'&'+dt" target="_blank">w+y</a>;}catch(er){i[h]=dt;} } else if(typeof i[c]!</mark>'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','<a href="//web.archive.org/web/20190116170620/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0" target="_blank">//web.archive.org/web/20190116170620/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0</a>')<input type="hidden" id="mkyong-postId" value="7274"></p>
<h1 id="java-11-的新特性是什么">Java 11 的新特性是什么</h1>
<blockquote>
<p>原文:<a href="http://web.archive.org/web/20230101150211/https://mkyong.com/java/what-is-new-in-java-11/" target="_blank">http://web.archive.org/web/20230101150211/https://mkyong.com/java/what-is-new-in-java-11/</a></p>
</blockquote>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/f3a2fce1e221693e84ee93f6722fa585.png" alt="Java 11 logo" loading="lazy"></p>
<p><a href="http://web.archive.org/web/20221230032428/https://openjdk.java.net/projects/jdk/11/" target="_blank">Java 11</a> 于 2018 年 9 月 25 日正式发布,这是一个长期支持(LTS)版本,<a href="http://web.archive.org/web/20221230032428/https://jdk.java.net/java-se-ri/11" target="_blank">在此下载 Java 11</a>或此 <a href="http://web.archive.org/web/20221230032428/https://jdk.java.net/archive/" target="_blank">openJDK 存档</a>。</p>
<p>Java 11 的特性。</p>
<ul>
<li><a href="#jep-181-nest-based-access-control">1。JEP 181:嵌套式访问控制</a></li>
<li><a href="#jep-309-dynamic-class-file-constants">2。JEP 309:动态类文件常量</a></li>
<li><a href="#jep-315-improve-aarch64-intrinsics">3。JEP 315:改进 Aarch64 内部函数</a></li>
<li><a href="#jep-318-epsilon-a-no-op-garbage-collector-experimental">4。JEP 318:ε:一个无操作垃圾收集器(实验)</a></li>
<li><a href="#jep-320-remove-the-java-ee-and-corba-modules">5。JEP 320:移除 Java EE 和 CORBA 模块</a></li>
<li><a href="#jep-321-http-client-standard">6。JEP 321: HTTP 客户端(标准)</a></li>
<li><a href="#jep-323-local-variable-syntax-for-lambda-parameters">7。JEP 323:Lambda 参数的局部变量语法</a></li>
<li><a href="#jep-324-key-agreement-with-curve25519-and-curve448">8。JEP 324:与曲线 25519 和曲线 448</a> 的关键协议</li>
<li><a href="#jep-327-unicode-10">9。JEP 327: Unicode 10</a></li>
<li>10。JEP 328:飞行记录器</li>
<li><a href="#jep-329-chacha20-and-poly1305-cryptographic-algorithms">11。JEP 329: ChaCha20 和 Poly1305 密码算法</a></li>
<li><a href="#jep-330-launch-single-file-source-code-programs">12。JEP 330:启动单文件源代码程序</a></li>
<li>13。JEP 331:低开销堆分析</li>
<li><a href="#jep-332-transport-layer-security-tls-13">14。JEP 332:传输层安全性(TLS) 1.3</a></li>
<li>15。JEP 333: ZGC:一个可扩展的低延迟垃圾收集器(实验)</li>
<li>16。JEP 335:反对 Nashorn JavaScript 引擎</li>
<li><a href="#jep-336-deprecate-the-pack200-tools-and-api">17。JEP 336:反对 Pack200 工具和 API</a></li>
</ul>
<p><em>Java 11 开发者特性。</em></p>
<p>新增 HTTP 客户端 API<code>java.net.http.*</code>,lambda 参数中的<code>var</code>,Java fright recorder (JFR),启动单文件程序<code>java ClassName.java</code>,支持 ChaCha20 密码算法。</p>
<p><strong>最新 JDK 发布 Java 16</strong><br>
最新 Java 16 有什么新内容。</p>
<h2 id="1jep-181嵌套式访问控制">1。JEP 181:嵌套式访问控制</h2>
<p>它直接支持嵌套成员内部的私有访问,不再需要通过自动生成的桥方法<code>access$000</code>。此外,新的验证嵌套 API 允许嵌套成员内部的私有反射访问。</p>
<p>Java 11 之前。</p>
<p>.java</p>
<pre><code class="language-java"> public class Alphabet {
private String name = "I'm Alphabet!";
public class A {
public void printName() {
System.out.println(name); // access Alphabet's private member!
}
}
}
</code></pre>
<p>如果我们编译上面的类,它将生成两个类,<code>Alphabet</code>和<code>Alphabet$A</code>,即使是嵌套类也是一个典型的具有唯一名称的类。JVM 访问规则不允许不同类中的私有访问。然而,Java 允许嵌套成员内部的私有访问,所以 Java 编译器创建了一个桥方法<code>access$000</code>来应用于 JVM 访问规则。</p>
<pre><code class="language-java"> // After javac Alphabet.java, Java compiler created something similar to this.
public class Alphabet {
private String name = "I'm Alphabet!";
String access$000(){
return name;
}
}
public class Alphabet$A {
final Alphabet obj;
public void printName(){
System.out.println(obj.access$000());
}
}
</code></pre>
<p>在 Java 11 中,Java 编译器不会为嵌套成员内的私有访问生成任何桥方法<code>access$000</code>。这个新的 JVM 访问规则,基于嵌套的访问控制允许嵌套成员内部的私有访问。</p>
<p><em>延伸阅读</em></p>
<ul>
<li>JEP 181:基于嵌套的访问控制</li>
<li><a href="/web/20221230032428/https://mkyong.com/java/java-11-nest-based-access-control/" target="_blank">Java 11–基于嵌套的访问控制</a></li>
</ul>
<h2 id="2jep-309动态类文件常量">2。JEP 309:动态类文件常量</h2>
<p>扩展了类文件格式,以支持新的常量池形式,<code>CONSTANT_Dynamic</code>,目标语言设计者和编译器实现者。</p>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221230032428/https://openjdk.java.net/jeps/309" target="_blank">JEP 309:动态类文件常量</a></li>
</ul>
<h2 id="3jep-315改进-aarch64-内部函数">3。JEP 315:改进 Aarch64 内部函数</h2>
<p>优化了现有的字符串和数组内函数,在 Arm64 或 Aarch64 处理器上为<code>Math.sin()</code>、<code>Math.cos()</code>和<code>Match.log()</code>实现了新的内函数。意味着更好的性能。</p>
<p><em>P.S 一个<a href="http://web.archive.org/web/20221230032428/https://en.wikipedia.org/wiki/Intrinsic_function" target="_blank">内在</a>用于利用 CPU 架构特定的汇编代码来提高性能。</em></p>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221230032428/https://openjdk.java.net/jeps/315" target="_blank">JEP 315:改进 Aarch64 的内部函数</a></li>
</ul>
<h2 id="4jep-318ε一个无操作垃圾收集器实验">4。JEP 318:ε:一个无操作垃圾收集器(实验)</h2>
<p>一个新的 No-Op(无操作)垃圾收集器,它分配内存但不会收集任何垃圾(内存分配),一旦 Java 堆耗尽,JVM 就会关闭。</p>
<p>一些使用案例:</p>
<ul>
<li>性能试验</li>
<li>虚拟机接口测试</li>
<li>短暂的工作</li>
</ul>
<p>这个 GC 是一个实验性的特性;我们需要使用以下选项来启用新的 Epsilon GC。</p>
<pre><code class="language-java"> -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC
</code></pre>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221230032428/https://openjdk.java.net/jeps/318" target="_blank">JEP 318:ε:一个无操作垃圾收集器(实验)</a></li>
</ul>
<h2 id="5jep-320移除-java-ee-和-corba-模块">5。JEP 320:移除 Java EE 和 CORBA 模块</h2>
<p>Java 9 弃用了下面的 Java EE 和 CORBA 模块,现在在 Java 11 中被删除了。如果您想迁移到 Java 11,请确保您的项目没有使用以下任何包或工具。</p>
<p>移除的包:</p>
<ul>
<li>java.xml.ws (JAX)</li>
<li>java.xml.bind</li>
<li>java.activation (JAF)</li>
<li>java.xml.ws.annotation(通用注释)</li>
<li>java.corba</li>
<li>java .事务(JTA)</li>
<li>java.se.ee(上述六个模块的聚合器模块)</li>
</ul>
<p>移除的工具:</p>
<ul>
<li>wsgen 和 wsimport(来自 jdk.xml.ws)</li>
<li>schemagen 和 xjc(来自 jdk.xml.bind)</li>
<li>idlj、orbd、servertool 和 tnamesrv(来自 java.corba)</li>
</ul>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221230032428/https://openjdk.java.net/jeps/320" target="_blank">JEP 320:移除 Java EE 和 CORBA 模块</a></li>
</ul>
<h2 id="6jep-321-http-客户端标准">6。JEP 321: HTTP 客户端(标准)</h2>
<p><code>java.net.http</code>包中的这个<code>HTTP Client API</code>在 Java 9 中引入,在 Java 10 中更新,现在是 Java 11 中的标准特性。</p>
<p>Java 11 <code>HttpClient</code>发送一个简单的<code>GET</code>请求。</p>
<pre><code class="language-java"> HttpClient httpClient = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_1_1)
.connectTimeout(Duration.ofSeconds(10))
.build();
HttpRequest request = HttpRequest.newBuilder()
.GET()
.uri(URI.create("https://httpbin.org/get"))
.setHeader("User-Agent", "Java 11 HttpClient Bot")
.build();
HttpResponse<String> response =
httpClient.send(request, HttpResponse.BodyHandlers.ofString());
HttpHeaders headers = response.headers();
headers.map().forEach((k, v) -> System.out.println(k + ":" + v));
System.out.println(response.statusCode());
System.out.println(response.body());
</code></pre>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221230032428/https://openjdk.java.net/jeps/321" target="_blank">JEP 321: HTTP 客户端(标准)</a></li>
<li><a href="/web/20221230032428/https://mkyong.com/java/java-11-httpclient-examples/" target="_blank">Java 11 HttpClient 示例</a></li>
</ul>
<h2 id="7jep-323lambda-参数的局部变量语法">7。JEP 323:Lambda 参数的局部变量语法</h2>
<p>这个 JEP 增加了对 lambda 参数中关键字<code>var</code>的支持。</p>
<pre><code class="language-java"> List<String> list = Arrays.asList("a", "b", "c");
String result = list.stream()
.map((var x) -> x.toUpperCase())
.collect(Collectors.joining(","));
System.out.println(result2);
</code></pre>
<p>然而,lambda 可以进行类型推断;上面的例子相当于这个:</p>
<pre><code class="language-java"> List<String> list = Arrays.asList("a", "b", "c");
String result = list.stream()
.map(x -> x.toUpperCase())
.collect(Collectors.joining(","));
</code></pre>
<p>那么,这个 JEP 为什么要在 lambda 参数中加入<code>var</code>?好处是现在我们可以向 lambda 参数添加注释,参见以下示例:</p>
<pre><code class="language-java"> import org.jetbrains.annotations.NotNull;
List<String> list = Arrays.asList("a", "b", "c", null);
String result = list.stream()
.map((@NotNull var x) -> x.toUpperCase())
.collect(Collectors.joining(","));
System.out.println(result3);
</code></pre>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221230032428/https://openjdk.java.net/jeps/323" target="_blank">JEP 323:Lambda 参数的局部变量语法</a></li>
</ul>
<h2 id="8jep-324与曲线-25519-和曲线-448">8。JEP 324:与曲线 25519 和曲线 448</h2>
<p>的关键协议</p>
<p>Java 密码学相关项目。它用 <a href="http://web.archive.org/web/20221230032428/https://en.wikipedia.org/wiki/Curve25519" target="_blank">Curve25519</a> 和 <a href="http://web.archive.org/web/20221230032428/https://en.wikipedia.org/wiki/Curve448" target="_blank">Curve448</a> 算法取代了现有的椭圆曲线 Diffie-Hellman (ECDH)方案,这是一种在 <a href="http://web.archive.org/web/20221230032428/https://tools.ietf.org/html/rfc7748" target="_blank">RFC 7748</a> 中定义的密钥协商方案。</p>
<p>使用<code>Curve25519</code>算法生成密钥对的简单<code>KeyPairGenerator</code>示例。</p>
<p>GenerateKeyPairs.java</p>
<pre><code class="language-java"> package com.mkyong.java11.jep324;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.NamedParameterSpec;
public class GenerateKeyPairs {
public static void main(String[] args) throws NoSuchAlgorithmException,
InvalidAlgorithmParameterException {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("XDH");
NamedParameterSpec paramSpec = new NamedParameterSpec("X25519");
kpg.initialize(paramSpec); // equivalent to kpg.initialize(255)
// alternatively: kpg = KeyPairGenerator.getInstance("X25519")
KeyPair kp = kpg.generateKeyPair();
System.out.println("--- Public Key ---");
PublicKey publicKey = kp.getPublic();
System.out.println(publicKey.getAlgorithm()); // XDH
System.out.println(publicKey.getFormat()); // X.509
// save this public key
byte[] pubKey = publicKey.getEncoded();
System.out.println("---");
System.out.println("--- Private Key ---");
PrivateKey privateKey = kp.getPrivate();
System.out.println(privateKey.getAlgorithm()); // XDH
System.out.println(privateKey.getFormat()); // PKCS#8
// save this private key
byte[] priKey = privateKey.getEncoded();
}
}
</code></pre>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221230032428/https://openjdk.java.net/jeps/324" target="_blank">JEP 324:与 Curve25519 和 Curve448 的密钥协议</a></li>
<li><a href="http://web.archive.org/web/20221230032428/https://docs.oracle.com/en/java/javase/11/docs/specs/security/standard-names.html#keypairgenerator-algorithms" target="_blank">Oracle–Java 安全标准算法名称</a></li>
</ul>
<h2 id="9jep-327-unicode-10">9。JEP 327: Unicode 10</h2>
<p>这意味着更多的代码点,更多的表情图标🙂下面的例子打印了一个 Unicode 码位。</p>
<p>PrintUnicode.java</p>
<pre><code class="language-java"> package com.mkyong.java11;
public class PrintUnicode {
public static void main(String[] args) {
String codepoint = "U+1F92A"; // crazy face
System.out.println(convertCodePoints(codepoint));
}
// Java, UTF-16
// Convert code point to unicode
static char[] convertCodePoints(String codePoint) {
Integer i = Integer.valueOf(codePoint.substring(2), 16);
char[] chars = Character.toChars(i);
return chars;
}
}
</code></pre>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221230032428/https://openjdk.java.net/jeps/327" target="_blank">JEP 327: Unicode 10</a></li>
<li><a href="http://web.archive.org/web/20221230032428/https://unicode.org/versions/Unicode10.0.0/" target="_blank">Unicode 10</a></li>
<li><a href="http://web.archive.org/web/20221230032428/https://blog.emojipedia.org/whats-new-in-unicode-10/" target="_blank">Unicode 10.0 的新功能</a></li>
<li><a href="http://web.archive.org/web/20221230032428/https://www.codetab.org/post/java-unicode-basics/" target="_blank">Java 中的 Unicode 乐趣</a></li>
</ul>
<h2 id="10jep-328飞行记录器">10。JEP 328:飞行记录器</h2>
<p>Java 飞行记录器(JFR)是甲骨文 JDK 公司的商业产品,现在它在 OpenJDK 11 中是开源的。这个 JFR 是一个分析工具,用于诊断正在运行的 Java 应用程序。下面的命令在一个 Java 应用程序上开始一个 60 秒的 JFR 记录,将记录的数据转储到一个。jfr 的文件。</p>
<p>Termianl</p>
<pre><code class="language-java"> $ java -XX:StartFlightRecording=duration=60s,settings=profile,filename=app.jfr MyHelloWorldApp
</code></pre>
<p>那么如何处理<code>.jfr</code>文件呢?我们可以使用 <a href="http://web.archive.org/web/20221230032428/https://openjdk.java.net/projects/jmc/" target="_blank">Java Mission Control (JMC)</a> 对<code>.jfr</code>文件进行分析和可视化。</p>
<p><em>延伸阅读</em></p>
<ul>
<li>JEP 328:飞行记录器</li>
<li><a href="/web/20221230032428/https://mkyong.com/java/java-11-java-flight-recorder/" target="_blank">Java 飞行记录器示例</a></li>
</ul>
<h2 id="11jep-329-chacha20-和-poly1305-密码算法">11。JEP 329: ChaCha20 和 Poly1305 密码算法</h2>
<p><code>ChaCha20</code>是一种高速流密码,一种加密解密算法。<code>ChaCha20-Poly1305</code>是指<code>ChaCha20</code>在 AEAD 模式下运行,与<code>Poly1305</code>认证器、加密和认证一起,二者都在 <a href="http://web.archive.org/web/20221230032428/https://tools.ietf.org/html/rfc7539" target="_blank">RFC 7539</a> 中定义。这个 JEP 更新的<code>ChaCha20</code>密码算法是不安全的 <a href="http://web.archive.org/web/20221230032428/https://en.wikipedia.org/wiki/RC4" target="_blank">RC4</a> 流密码的替代品。</p>
<p><code>ChaCha20</code>的输入是:</p>
<ul>
<li>256 位密钥(32 字节)</li>
<li>96 位随机数(12 字节)</li>
<li>32 位初始计数(4 字节)</li>
</ul>
<pre><code class="language-java"> Cipher cipher = Cipher.getInstance("ChaCha20");
ChaCha20ParameterSpec param = new ChaCha20ParameterSpec(nonce, counter);
cipher.init(Cipher.ENCRYPT_MODE, key, param);
byte[] encryptedText = cipher.doFinal(pText);
</code></pre>
<p>请参考本<a href="/web/20221230032428/https://mkyong.com/java/java-11-chacha20-stream-cipher-examples/" target="_blank">Java 11–chacha 20 流密码示例</a></p>
<p><code>ChaCha20-Poly1305</code>的输入是:</p>
<ul>
<li>256 位密钥(32 字节)</li>
<li>96 位随机数(12 字节)</li>
</ul>
<pre><code class="language-java"> Cipher cipher = Cipher.getInstance("ChaCha20-Poly1305");
// IV, initialization value with nonce
IvParameterSpec iv = new IvParameterSpec(nonce);
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
byte[] encryptedText = cipher.doFinal(pText);
</code></pre>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221230032428/https://openjdk.java.net/jeps/329" target="_blank">JEP 329: ChaCha20 和 Poly1305 密码算法</a></li>
<li><a href="/web/20221230032428/https://mkyong.com/java/java-11-chacha20-poly1305-encryption-examples/" target="_blank">Java 11–chacha 20-poly 1305 加密示例</a></li>
</ul>
<h2 id="12jep-330启动单文件源代码程序">12。JEP 330:启动单文件源代码程序</h2>
<p>这个单文件源代码程序意味着整个 Java 程序都在一个源代码<code>.java</code>文件中。这个 JEP 在学习 Java 的初期是一个友好的特性,但是在 Java 开发中没有太大的好处,我们都用 IDE。</p>
<p>查看单个文件源代码。</p>
<p>HelloJava.java</p>
<pre><code class="language-java"> public class HelloJava {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
</code></pre>
<p>Java 11 之前。</p>
<p>Termianl</p>
<pre><code class="language-java"> $ javac HelloJava.java
$ java HelloJava
Hello World!
</code></pre>
<p>现在 Java 11。</p>
<p>Termianl</p>
<pre><code class="language-java"> $ java HelloJava.java
Hello World!
</code></pre>
<p><strong>Shebang #!</strong><br>
现在,单个 Java 程序可以使用 Linux Shebang 作为脚本运行。</p>
<p>run.sh</p>
<pre><code class="language-java"> #!/opt/java/openjdk/bin/java --source 11
public class SheBang {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
</code></pre>
<p><em>延伸阅读</em></p>
<ul>
<li>JEP 330:启动单文件源代码程序</li>
<li><a href="/web/20221230032428/https://mkyong.com/java/java-11-shebang-example-in-docker/" target="_blank">Docker 中的 Java 11 shebang 示例</a></li>
</ul>
<h2 id="13jep-331低开销堆分析">13。JEP 331:低开销堆分析</h2>
<p>Java 虚拟机工具接口(JVMTI)是在 J2SE 5、JDK 5 (Tiger)中引入的,它为分析或监控工具提供了访问 JVM 状态的 API。这个 JEP 在 JVMIT 中添加了新的低开销堆分析 API。</p>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221230032428/https://openjdk.java.net/jeps/331" target="_blank">JEP 331:低开销堆分析</a></li>
<li><a href="http://web.archive.org/web/20221230032428/https://docs.oracle.com/javase/8/docs/platform/jvmti/jvmti.html" target="_blank">Oracle–JVM 工具接口</a></li>
</ul>
<h2 id="14jep-332传输层安全性tls-13">14。JEP 332:传输层安全性(TLS) 1.3</h2>
<p>Java 11 支持 <a href="http://web.archive.org/web/20221230032428/https://tools.ietf.org/html/rfc8446" target="_blank">RFC 8446</a> 传输层安全(TLS) 1.3 协议。然而,并不是所有的 TLS 1.3 特性都实现了,详情请参考这个 <a href="http://web.archive.org/web/20221230032428/https://openjdk.java.net/jeps/332" target="_blank">JEP 332</a> 。</p>
<p>Java 安全套接字扩展(JSSE) + TLS 1.3 示例。</p>
<pre><code class="language-java"> import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
SSLSocketFactory factory =
(SSLSocketFactory) SSLSocketFactory.getDefault();
socket =
(SSLSocket) factory.createSocket("google.com", 443);
socket.setEnabledProtocols(new String[]{"TLSv1.3"});
socket.setEnabledCipherSuites(new String[]{"TLS_AES_128_GCM_SHA256"});
</code></pre>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221230032428/https://openjdk.java.net/jeps/332" target="_blank">JEP 332:传输层安全性(TLS) 1.3</a></li>
<li><a href="/web/20221230032428/https://mkyong.com/java/java-sslsocket-tls-1-3-example/" target="_blank">Java SSLSocket TLS 1.3 示例</a></li>
</ul>
<h2 id="15jep-333-zgc一个可扩展的低延迟垃圾收集器实验">15。JEP 333: ZGC:一个可扩展的低延迟垃圾收集器(实验)</h2>
<p>Z 垃圾收集器(ZGC)是一个实验性的垃圾收集器;它具有不超过 10ms 低暂停时间。这种 ZCG 只在 Linux/64 上支持。</p>
<ul>
<li>在 Java 14 中,这个 ZCG 扩展了对 macOS <a href="http://web.archive.org/web/20221230032428/https://openjdk.java.net/jeps/364" target="_blank">JEP 364</a> 和 Windows <a href="http://web.archive.org/web/20221230032428/https://openjdk.java.net/jeps/365" target="_blank">JEP 365</a> 的支持。</li>
<li>这个 ZGC 垃圾收集器是 Java 15 <a href="http://web.archive.org/web/20221230032428/https://openjdk.java.net/jeps/377" target="_blank">JEP 377</a> 中的一个产品特性。</li>
</ul>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221230032428/https://openjdk.java.net/jeps/333" target="_blank">JEP 333: ZGC:一个可扩展的低延迟垃圾收集器(实验)</a></li>
<li><a href="http://web.archive.org/web/20221230032428/https://wiki.openjdk.java.net/display/zgc/Main" target="_blank">open JDK Wiki–ZCG</a></li>
<li><a href="http://web.archive.org/web/20221230032428/https://docs.oracle.com/en/java/javase/11/gctuning/z-garbage-collector1.html" target="_blank">Oracle–Z 垃圾收集器</a></li>
</ul>
<h2 id="16jep-335反对-nashorn-javascript-引擎">16。JEP 335:反对 Nashorn JavaScript 引擎</h2>
<p>Nashorn JavaScript 脚本引擎和<code>jjs</code>工具已被弃用,可能会在未来的版本中删除。</p>
<p><em>历史</em></p>
<ul>
<li>Java 8 <a href="http://web.archive.org/web/20221230032428/https://openjdk.java.net/jeps/174" target="_blank">JEP 174</a> 引入了 Nashorn,作为 Rhino Javascript 引擎的替代品。</li>
<li>Java 11 <a href="http://web.archive.org/web/20221230032428/https://openjdk.java.net/jeps/335" target="_blank">JEP 335</a> 弃用了 Nashorn。</li>
<li>Java 15 <a href="http://web.archive.org/web/20221230032428/https://openjdk.java.net/jeps/372" target="_blank">JEP 372</a> 移除了 Nashorn JavaScript 引擎和 jjs 工具。</li>
</ul>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221230032428/https://openjdk.java.net/jeps/335" target="_blank">JEP 335:反对 Nashorn JavaScript 引擎</a></li>
</ul>
<h2 id="17jep-336反对-pack200-工具和-api">17。JEP 336:反对 Pack200 工具和 API</h2>
<p>这个 JEP 弃用了<code>pack200</code>和<code>unpack200</code>工具,以及<code>java.util.jar</code>包中的<code>Pack200</code> API,它可能会在未来的版本中移除。</p>
<p>JEP 367 移除了 Pack200 工具和 API。</p>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221230032428/https://openjdk.java.net/jeps/336" target="_blank">JEP 336:反对 Pack200 工具和 API</a></li>
</ul>
<h2 id="下载源代码-9">下载源代码</h2>
<p>$ git 克隆<a href="http://web.archive.org/web/20221230032428/https://github.com/mkyong/core-java" target="_blank">https://github.com/mkyong/core-java</a></p>
<p>$ cd java-11</p>
<h2 id="参考文献">参考文献</h2>
<ul>
<li><a href="http://web.archive.org/web/20221230032428/https://openjdk.java.net/projects/jdk/11/" target="_blank">OpenJDK 11 项目</a></li>
<li><a href="http://web.archive.org/web/20221230032428/https://en.wikipedia.org/wiki/Java_version_history" target="_blank">Java 版本历史</a></li>
<li><a href="http://web.archive.org/web/20221230032428/https://unicode.org/versions/Unicode10.0.0/" target="_blank">Unicode 10</a></li>
<li><a href="http://web.archive.org/web/20221230032428/https://en.wikipedia.org/wiki/ARM_architecture" target="_blank">维基百科–AAR ch 64 处理器</a></li>
<li><a href="http://web.archive.org/web/20221230032428/https://en.wikipedia.org/wiki/Intrinsic_function" target="_blank">维基百科–内在</a></li>
<li>Java 安全性有什么新特性?</li>
<li><a href="http://web.archive.org/web/20221230032428/https://docs.oracle.com/en/java/javase/11/docs/specs/security/standard-names.html#keypairgenerator-algorithms" target="_blank">Oracle–Java 安全标准算法名称</a></li>
<li><a href="http://web.archive.org/web/20221230032428/https://tools.ietf.org/html/rfc7748" target="_blank">RFC 7748</a></li>
<li><a href="http://web.archive.org/web/20221230032428/https://en.wikipedia.org/wiki/Curve25519" target="_blank">维基百科–curve 25519</a></li>
<li><a href="http://web.archive.org/web/20221230032428/https://en.wikipedia.org/wiki/Curve448" target="_blank">维基百科–curve 448</a></li>
<li><a href="http://web.archive.org/web/20221230032428/https://mkyong.com/java/java-11-java-flight-recorder/" target="_blank">Java 飞行记录器</a></li>
<li><a href="http://web.archive.org/web/20221230032428/https://openjdk.java.net/projects/jmc/" target="_blank">Java 任务控制(JMC)</a></li>
<li><a href="http://web.archive.org/web/20221230032428/https://tools.ietf.org/html/rfc7539" target="_blank">RFC 7539</a></li>
<li><a href="http://web.archive.org/web/20221230032428/https://docs.oracle.com/en/java/javase/11/docs/api/java.base/javax/crypto/Cipher.html" target="_blank">Java 11–Chipher</a></li>
<li><a href="http://web.archive.org/web/20221230032428/https://en.wikipedia.org/wiki/Salsa20#ChaCha_variant" target="_blank">ChaCha20</a></li>
<li><a href="http://web.archive.org/web/20221230032428/https://en.wikipedia.org/wiki/RC4" target="_blank">RC4</a></li>
<li><a href="/web/20221230032428/https://mkyong.com/java/java-11-chacha20-stream-cipher-examples/" target="_blank">Java 11–chacha 20 流密码示例</a></li>
<li><a href="/web/20221230032428/https://mkyong.com/java/java-11-chacha20-poly1305-encryption-examples/" target="_blank">Java 11–chacha 20-poly 1305 加密示例</a></li>
<li><a href="/web/20221230032428/https://mkyong.com/java/java-11-shebang-example-in-docker/" target="_blank">Docker 中的 Java 11 shebang 示例</a></li>
<li><a href="http://web.archive.org/web/20221230032428/https://en.wikipedia.org/wiki/Java_Virtual_Machine_Tools_Interface" target="_blank">维基百科–Java 虚拟机工具接口</a></li>
<li><a href="http://web.archive.org/web/20221230032428/https://docs.oracle.com/javase/8/docs/platform/jvmti/jvmti.html" target="_blank">Oracle–JVM 工具接口</a></li>
<li><a href="/web/20221230032428/https://mkyong.com/java/java-sslsocket-tls-1-3-example/" target="_blank">Java SSLSocket TLS 1.3 示例</a></li>
<li><a href="http://web.archive.org/web/20221230032428/https://tools.ietf.org/html/rfc8446" target="_blank">RFC 8446</a></li>
<li><a href="http://web.archive.org/web/20221230032428/https://docs.oracle.com/javase/10/security/toc.htm" target="_blank">Oracle–Java 安全开发人员指南</a></li>
<li><a href="http://web.archive.org/web/20221230032428/https://docs.oracle.com/en/java/javase/11/gctuning/z-garbage-collector1.html" target="_blank">Oracle–Z 垃圾收集器</a></li>
<li><a href="http://web.archive.org/web/20221230032428/https://wiki.openjdk.java.net/display/zgc/Main" target="_blank">open JDK Wiki–ZCG</a></li>
<li><a href="http://web.archive.org/web/20221230032428/https://docs.oracle.com/en/java/javase/11/gctuning/z-garbage-collector1.html" target="_blank">Oracle–Z 垃圾收集器</a></li>
<li><a href="/web/20221230032428/https://mkyong.com/java/java-11-httpclient-examples/" target="_blank">Java 11 HttpClient 示例</a></li>
</ul>
<input type="hidden" id="mkyong-current-postId" value="15725">
<h1 id="java-12-的新特性是什么">Java 12 的新特性是什么</h1>
<blockquote>
<p>原文:<a href="http://web.archive.org/web/20230101150211/https://mkyong.com/java/what-is-new-in-java-12/" target="_blank">http://web.archive.org/web/20230101150211/https://mkyong.com/java/what-is-new-in-java-12/</a></p>
</blockquote>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/96e330d54ef57b30b57d586bcdccbf3d.png" alt="Java 12 logo" loading="lazy"></p>
<p><a href="http://web.archive.org/web/20221124003700/https://openjdk.java.net/projects/jdk/12/" target="_blank">Java 12</a> 于 2019 年 3 月 19 日正式发布,<a href="http://web.archive.org/web/20221124003700/https://jdk.java.net/java-se-ri/12" target="_blank">在此下载 Java 12</a>或此 <a href="http://web.archive.org/web/20221124003700/https://jdk.java.net/archive/" target="_blank">openJDK 存档</a>。</p>
<p>Java 12 特性。</p>
<ul>
<li><a href="#jep-189-shenandoah-a-low-pause-time-garbage-collector-experimental">1。JEP 189: Shenandoah:一个低停顿时间的垃圾收集器(实验)</a></li>
<li><a href="#jep-230-microbenchmark-suite">2。JEP 230:微基准测试套件</a></li>
<li><a href="#jep-325-switch-expressions-preview">3。JEP 325:切换表情(预览)</a></li>
<li><a href="#jep-334-jvm-constants-api">4。JEP 334: JVM 常量 API</a></li>
<li><a href="#jep-340-one-aarch64-port-not-two">5。JEP 340:一个 AArch64 端口,而不是两个</a></li>
<li><a href="#jep-341-default-cds-archives">6。JEP 341:默认 CDS 档案</a></li>
<li><a href="#jep-344-abortable-mixed-collections-for-g1">7。JEP 344:G1 可接受的混合收藏</a></li>
<li><a href="#jep-346-promptly-return-unused-committed-memory-from-g1">8。JEP 346:及时从 G1 返回未使用的提交内存</a></li>
</ul>
<p><em>Java 12 开发者特性。</em></p>
<p>切换表达式(预览)</p>
<h2 id="1jep-189-shenandoah一个低停顿时间的垃圾收集器实验">1。JEP 189: Shenandoah:一个低停顿时间的垃圾收集器(实验)</h2>
<p>Shenandoah 是一个新的低暂停和并发垃圾收集器,阅读这篇<a href="http://web.archive.org/web/20221124003700/https://www.researchgate.net/publication/306112816_Shenandoah_An_open-source_concurrent_compacting_garbage_collector_for_OpenJDK" target="_blank">研究论文</a>,它减少了 GC 暂停时间并且独立于 Java 堆大小(5M 或 5G 的堆大小有相同的暂停时间,对大型堆应用有用。)</p>
<p>这个 GC 是一个实验性的特性,我们需要使用以下选项来启用新的 Shenandoah GC。</p>
<pre><code class="language-java"> -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC
</code></pre>
<p>然而, <a href="http://web.archive.org/web/20221124003700/http://mail.openjdk.java.net/pipermail/shenandoah-dev/2018-December/008673.html" target="_blank">Oracle JDK 和 OpenJDK 都不包含这个新的 Shenandoah GC</a> ,也请阅读这个<a href="http://web.archive.org/web/20221124003700/https://developers.redhat.com/blog/2019/04/19/not-all-openjdk-12-builds-include-shenandoah-heres-why/" target="_blank">并非所有 OpenJDK 12 版本都包含 Shenandoah:下面是原因</a>。</p>
<pre><code class="language-java"> C:\Users\mkyong> java -version
java version "12" 2019-03-19
Java(TM) SE Runtime Environment (build 12+33)
Java HotSpot(TM) 64-Bit Server VM (build 12+33, mixed mode, sharing)
C:\Users\mkyong> java -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC
Error occurred during initialization of VM
Option -XX:+UseShenandoahGC not supported
</code></pre>
<p>为了尝试 Shenandoah GC,我们需要其他 JDK 版本,如 <a href="http://web.archive.org/web/20221124003700/https://adoptopenjdk.net/" target="_blank">AdoptOpenJDK</a> 。</p>
<p>这个 Shenandoah GC 成为了 Java 15 <a href="http://web.archive.org/web/20221124003700/https://openjdk.java.net/jeps/379" target="_blank">JEP 379</a> 的一个产品特性。</p>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221124003700/https://openjdk.java.net/jeps/189" target="_blank">JEP 189:谢南多厄:一个低停顿时间的垃圾收集器(实验)</a></li>
<li><a href="http://web.archive.org/web/20221124003700/https://openjdk.java.net/projects/shenandoah/" target="_blank">Shenandoah OpenJDK 首页</a></li>
</ul>
<h2 id="2jep-230微基准测试套件">2。JEP 230:微基准测试套件</h2>
<p>向 JDK 源代码添加了一系列 Java 微基准测试工具(JMH ),对于那些有兴趣添加或修改 JDK 源代码本身的人来说,现在他们有了一种比较性能的方法。</p>
<p><strong>注</strong><br>
抱歉,不知道它是如何工作的,如果你知道如何运行 JDK 的基准测试,请在下面评论。</p>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221124003700/https://openjdk.java.net/jeps/230" target="_blank">JEP 230:微基准测试套件</a></li>
</ul>
<h2 id="3jep-325切换表情预览">3。JEP 325:切换表情(预览)</h2>
<p>这个 JEP 增强了现有的 switch 语句(不返回任何内容)以支持 switch 表达式(返回某些内容)。</p>
<p>传统的 switch 语句,我们可以通过将值赋给变量来返回值:</p>
<pre><code class="language-java"> private static String getText(int number) {
String result = "";
switch (number) {
case 1, 2:
result = "one or two";
break;
case 3:
result = "three";
break;
case 4, 5, 6:
result = "four or five or six";
break;
default:
result = "unknown";
break;
};
return result;
}
</code></pre>
<p>在 Java 12 中,我们可以使用<code>break</code>或<code>case L -></code>从开关返回值。</p>
<pre><code class="language-java"> private static String getText(int number) {
String result = switch (number) {
case 1, 2:
break "one or two";
case 3:
break "three";
case 4, 5, 6:
break "four or five or six";
default:
break "unknown";
};
return result;
}
</code></pre>
<p><code>case L -></code>语法。</p>
<pre><code class="language-java"> private static String getText(int number) {
return switch (number) {
case 1, 2 -> "one or two";
case 3 -> "three";
case 4, 5, 6 -> "four or five or six";
default -> "unknown";
};
}
</code></pre>
<p><strong>注意</strong><br>
这个 switch 表达式在 Java 13 中有了第二个预览版(去掉了<code>break</code>取而代之的是<code>yield</code>),并且这个 switch 表达式在 <a href="http://web.archive.org/web/20221124003700/https://mkyong.com/java/what-is-new-in-java-14/" target="_blank">Java 14</a> 中成为了一个标准特性。</p>
<p><em>进一步阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221124003700/https://openjdk.java.net/jeps/325" target="_blank">JEP 325:开关表情(预览)</a></li>
<li><a href="http://web.archive.org/web/20221124003700/https://mkyong.com/java/java-12-switch-expressions/" target="_blank">Java 12 开关表达式</a></li>
</ul>
<p>要启用 Java 12 预览功能:</p>
<pre><code class="language-java"> javac --enable-preview --release 12 Example.java
java --enable-preview Example
</code></pre>
<h2 id="4jep-334-jvm-常量-api">4。JEP 334: JVM 常量 API</h2>
<p>一个新的包<code>java.lang.constant</code>,一系列新的类和接口来建模关键的类文件和运行时工件,例如<a href="http://web.archive.org/web/20221124003700/https://docs.oracle.com/javase/specs/jvms/se10/html/jvms-4.html#jvms-4.4" target="_blank">常量池</a>。</p>
<p><em>延伸阅读</em></p>
<ul>
<li>JEP 334: JVM 常量 API</li>
<li><a href="http://web.archive.org/web/20221124003700/https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/lang/constant/package-summary.html" target="_blank">包<code>java.lang.constant</code></a></li>
</ul>
<h2 id="5jep-340一个-aarch64-端口而不是两个">5。JEP 340:一个 AArch64 端口,而不是两个</h2>
<p>在 Java 12 之前,64 位 <a href="http://web.archive.org/web/20221124003700/https://en.wikipedia.org/wiki/ARM_architecture" target="_blank">ARM 架构</a>有两种不同的源代码或端口。</p>
<ul>
<li>甲骨文—<code>src/hotspot/cpu/arm</code></li>
<li>red hat?<code>src/hotspot/cpu/aarch64</code></li>
</ul>
<p>Java 12 移除了 Oracle <code>src/hotspot/cpu/arm</code>端口,只保留了一个端口<code>src/hotspot/cpu/aarch64</code>,并让这个<code>aarch64</code>成为 64 位 ARM 架构的默认构建。</p>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221124003700/https://openjdk.java.net/jeps/340" target="_blank">JEP 340:一个 AArch64 端口,而不是两个</a></li>
</ul>
<h2 id="6jep-341默认-cds-档案">6。JEP 341:默认 CDS 档案</h2>
<p><a href="http://web.archive.org/web/20221124003700/https://docs.oracle.com/javase/8/docs/technotes/guides/vm/class-data-sharing.html" target="_blank">类数据共享(CDS)</a> 通过重用现有的归档文件来缩短启动时间。</p>
<p>在 Java 12 之前,我们需要使用<code>-Xshare: dump</code>为 JDK 类生成 CDS 归档文件。在 Java 12 中,<code>/bin/server/</code>目录中有一个新的<code>classes.jsa</code>文件,这是 JDK 类的默认 CDS 归档文件。</p>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/e3cfca4ff60744b751f31786b4464325.png" alt="default cds" loading="lazy"></p>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221124003700/https://openjdk.java.net/jeps/341" target="_blank">JEP 341:默认 CDS 档案</a></li>
</ul>
<h2 id="7jep-344g1-可接受的混合收藏">7。JEP 344:G1 可接受的混合收藏</h2>
<p>这个 JEP 通过将有问题的收集组分成两部分——强制的和可选的,提高了垃圾优先(G1)收集器的性能。如果没有足够的时间来处理可选部分,G1 将中止它。</p>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221124003700/https://openjdk.java.net/jeps/334" target="_blank">JEP 344:G1 可接受的混合收藏</a></li>
</ul>
<h2 id="8jep-346及时从-g1-返回未使用的提交内存">8。JEP 346:及时从 G1 返回未使用的提交内存</h2>
<p>这个 JEP 提高了垃圾优先(G1)收集器的性能。如果应用程序活动较少或空闲,G1 会定期触发一个并发周期来确定总体 Java 堆使用情况,并将未使用的 Java 堆内存返回给操作系统。</p>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221124003700/https://openjdk.java.net/jeps/346" target="_blank">JEP 346:及时从 G1 返回未使用的提交内存</a></li>
</ul>
<h2 id="下载源代码-10">下载源代码</h2>
<p>$ git 克隆<a href="http://web.archive.org/web/20221124003700/https://github.com/mkyong/core-java" target="_blank">https://github.com/mkyong/core-java</a></p>
<p>$ cd java-12</p>
<h2 id="参考文献-1">参考文献</h2>
<ul>
<li><a href="http://web.archive.org/web/20221124003700/https://openjdk.java.net/projects/jdk/12/" target="_blank">OpenJDK 12 项目</a></li>
<li><a href="http://web.archive.org/web/20221124003700/https://en.wikipedia.org/wiki/Java_version_history" target="_blank">Java 版本历史</a></li>
<li><a href="http://web.archive.org/web/20221124003700/https://openjdk.java.net/projects/shenandoah/" target="_blank">Shenandoah OpenJDK 首页</a></li>
<li><a href="http://web.archive.org/web/20221124003700/https://www.researchgate.net/publication/306112816_Shenandoah_An_open-source_concurrent_compacting_garbage_collector_for_OpenJDK" target="_blank">Shenandoah 研究论文</a></li>
<li><a href="http://web.archive.org/web/20221124003700/https://mkyong.com/java/java-12-switch-expressions/" target="_blank">Java 12 开关表达式</a></li>
<li><a href="http://web.archive.org/web/20221124003700/https://www.mkyong.com/java/java-13-switch-expressions/" target="_blank">Java 13 开关表达式</a></li>
<li><a href="http://web.archive.org/web/20221124003700/https://openjdk.java.net/projects/code-tools/jmh/" target="_blank">Java 微基准测试工具(JMH)</a></li>
<li><a href="http://web.archive.org/web/20221124003700/https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/lang/constant/package-summary.html" target="_blank">java.lang.constant</a></li>
<li><a href="http://web.archive.org/web/20221124003700/https://en.wikipedia.org/wiki/ARM_architecture" target="_blank">ARM 架构</a></li>
<li><a href="http://web.archive.org/web/20221124003700/https://docs.oracle.com/javase/8/docs/technotes/guides/vm/class-data-sharing.html" target="_blank">类数据共享</a></li>
<li><a href="http://web.archive.org/web/20221124003700/https://www.azul.com/39-new-features-and-apis-in-jdk-12/" target="_blank">JDK 发布 39 项新功能(和 API)12</a></li>
<li><a href="http://web.archive.org/web/20221124003700/https://en.wikipedia.org/wiki/Garbage-first_collector" target="_blank">维基百科——垃圾优先收集器</a></li>
<li><a href="http://web.archive.org/web/20221124003700/https://www.oracle.com/technetwork/tutorials/tutorials-1876574.html" target="_blank">开始使用 G1 垃圾收集器</a></li>
<li>Java 安全性有什么新特性?</li>
</ul>
<input type="hidden" id="mkyong-current-postId" value="15738">
<h1 id="java-13-的新特性是什么">Java 13 的新特性是什么</h1>
<blockquote>
<p>原文:<a href="http://web.archive.org/web/20230101150211/https://mkyong.com/java/what-is-new-in-java-13/" target="_blank">http://web.archive.org/web/20230101150211/https://mkyong.com/java/what-is-new-in-java-13/</a></p>
</blockquote>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/97933e4d2b6bd29c2744d61e3ff2f47a.png" alt="Java 13 logo" loading="lazy"></p>
<p><a href="http://web.archive.org/web/20221124003658/https://openjdk.java.net/projects/jdk/13/" target="_blank">Java 13</a> 于 2019 年 9 月 17 日正式发布,<a href="http://web.archive.org/web/20221124003658/https://jdk.java.net/java-se-ri/13" target="_blank">在此下载 Java 13</a>或此 <a href="http://web.archive.org/web/20221124003658/https://jdk.java.net/archive/" target="_blank">openJDK 存档</a>。</p>
<p>Java 13 特性。</p>
<ul>
<li><a href="#jep-350-dynamic-cds-archives">1。JEP 350 动态光盘档案</a></li>
<li><a href="#jep-351-zgc-uncommit-unused-memory">2。JEP 351 ZGC:取消未使用内存的提交</a></li>
<li><a href="#jep-353-reimplement-the-legacy-socket-api">3。JEP-353 重新实现传统套接字 API</a></li>
<li><a href="#jep-354-switch-expressions-preview">4。JEP-354 开关表情(预览)</a></li>
<li><a href="#jep-355-text-blocks-preview">5。JEP-355 文本块(预览)</a></li>
</ul>
<p><em>Java 13 开发者特性。</em></p>
<p>切换表达式(预览)、文本块或多行(预览)</p>
<h2 id="1jep-350-动态光盘档案">1。JEP 350 动态光盘档案</h2>
<p>Java 10 引入了 <a href="http://web.archive.org/web/20221124003658/https://openjdk.java.net/jeps/310" target="_blank">JEP 310 应用类——数据共享</a>。这种 JEP 简化了创建光盘档案的过程。</p>
<p>该命令创建一个<code>.jar</code>的 CDS 存档文件。</p>
<pre><code class="language-java"> $ java -XX:ArchiveClassesAtExit=hello.jsa -cp hello.jar Hello
</code></pre>
<p>该命令对现有的 CDS 档案运行<code>.jar</code>。</p>
<pre><code class="language-java"> $ bin/java -XX:SharedArchiveFile=hello.jsa -cp hello.jar Hello
</code></pre>
<p>类数据共享(CDS)通过创建一次类数据档案并重用它来提高启动性能,这样 JVM 就不需要重新创建它了。</p>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221124003658/https://openjdk.java.net/jeps/350" target="_blank">JEP 350 动态光盘档案</a></li>
<li><a href="http://web.archive.org/web/20221124003658/https://simonis.github.io/cl4cds/" target="_blank">cl4cds</a></li>
<li><a href="http://web.archive.org/web/20221124003658/https://blog.codefx.org/java/application-class-data-sharing/" target="_blank">通过应用程序类数据共享缩短 Java 13 的启动时间</a></li>
</ul>
<h2 id="2jep-351-zgc取消未使用内存的提交">2。JEP 351 ZGC:取消未使用内存的提交</h2>
<p>Java 11 推出了 <a href="http://web.archive.org/web/20221124003658/https://openjdk.java.net/jeps/333" target="_blank">JEP 333: Z 垃圾收集器(实验)</a>;在清理堆内存时,它提供了短暂的暂停时间。然而,它没有将未使用的堆内存返回给操作系统,即使它长时间未使用。</p>
<p>这个 JEP 通过将未使用的堆内存返回给操作系统来增强 ZGC。</p>
<p><em>延伸阅读</em></p>
<ul>
<li>JEP 351 ZGC:取消未使用内存的提交</li>
</ul>
<h2 id="3jep-353-重新实现传统套接字-api">3。JEP-353 重新实现传统套接字 API</h2>
<p><code>java.net.Socket</code>和<code>java.net.ServerSocket</code>的底层实现非常古老,可以追溯到 JDK 1.0,这是一个混合了遗留 Java 和 C 代码的版本,很难维护和调试。这个 JEP 为 Socket APIs 引入了新的底层实现,这是 Java 13 中的默认实现。</p>
<p>在 Java 13 之前,它使用<code>PlainSocketImpl</code>作为<code>SocketImpl</code></p>
<p>ServerSocket.java</p>
<pre><code class="language-java"> public class ServerSocket implements java.io.Closeable {
/**
* The implementation of this Socket.
*/
private SocketImpl impl;
}
</code></pre>
<p>Java 13 引入了一个新的<code>NioSocketImpl</code>类作为<code>PlainSocketImpl</code>的替代。然而,如果出现问题,我们仍然可以通过设置<code>jdk.net.usePlainSocketImpl</code>系统属性切换回旧的实现<code>PlainSocketImpl</code>。</p>
<p>回顾一个简单的套接字示例。</p>
<p>JEP353.java</p>
<pre><code class="language-java"> import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class JEP353 {
public static void main(String[] args) {
try (ServerSocket serverSocket = new ServerSocket(8888)){
boolean running = true;
while(running){
Socket clientSocket = serverSocket.accept();
//do something with clientSocket
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
</code></pre>
<p>在 Java 13 中,默认实现是<code>NioSocketImpl</code></p>
<p>Terminal</p>
<pre><code class="language-java"> D:\test>javac JEP353.java
D:\test>java JEP353
D:\test>java -XX:+TraceClassLoading JEP353 | findStr Socket
[0.040s][info ][class,load] java.net.ServerSocket source: jrt:/java.base
[0.040s][info ][class,load] jdk.internal.access.JavaNetSocketAccess source: jrt:/java.base
[0.040s][info ][class,load] java.net.ServerSocket$1 source: jrt:/java.base
[0.040s][info ][class,load] java.net.SocketOptions source: jrt:/java.base
[0.040s][info ][class,load] java.net.SocketImpl source: jrt:/java.base
[0.044s][info ][class,load] java.net.SocketImpl$$Lambda$1/0x0000000800ba0840 source: java.net.SocketImpl
[0.047s][info ][class,load] sun.net.PlatformSocketImpl source: jrt:/java.base
[0.047s][info ][class,load] sun.nio.ch.NioSocketImpl source: jrt:/java.base
[0.047s][info ][class,load] sun.nio.ch.SocketDispatcher source: jrt:/java.base
[0.052s][info ][class,load] java.net.SocketAddress source: jrt:/java.base
[0.052s][info ][class,load] java.net.InetSocketAddress source: jrt:/java.base
[0.052s][info ][class,load] java.net.InetSocketAddress$InetSocketAddressHolder source: jrt:/java.base
[0.053s][info ][class,load] sun.net.ext.ExtendedSocketOptions source: jrt:/java.base
[0.053s][info ][class,load] jdk.net.ExtendedSocketOptions source: jrt:/jdk.net
[0.053s][info ][class,load] java.net.SocketOption source: jrt:/java.base
[0.053s][info ][class,load] jdk.net.ExtendedSocketOptions$ExtSocketOption source: jrt:/jdk.net
[0.053s][info ][class,load] jdk.net.SocketFlow source: jrt:/jdk.net
[0.053s][info ][class,load] jdk.net.ExtendedSocketOptions$PlatformSocketOptions source: jrt:/jdk.net
[0.053s][info ][class,load] jdk.net.ExtendedSocketOptions$PlatformSocketOptions$1 source: jrt:/jdk.net
[0.054s][info ][class,load] jdk.net.ExtendedSocketOptions$1 source: jrt:/jdk.net
[0.054s][info ][class,load] sun.nio.ch.NioSocketImpl$FileDescriptorCloser source: jrt:/java.base
[0.055s][info ][class,load] java.net.Socket source: jrt:/java.base
</code></pre>
<p>我们可以通过设置<code>Djdk.net.usePlainSocketImpl</code>系统属性切换回<code>PlainSocketImpl</code>。</p>
<p>Terminal</p>
<pre><code class="language-java"> D:\test>java -Djdk.net.usePlainSocketImpl -XX:+TraceClassLoading JEP353 | findStr Socket
[0.041s][info ][class,load] java.net.ServerSocket source: jrt:/java.base
[0.041s][info ][class,load] jdk.internal.access.JavaNetSocketAccess source: jrt:/java.base
[0.041s][info ][class,load] java.net.ServerSocket$1 source: jrt:/java.base
[0.041s][info ][class,load] java.net.SocketOptions source: jrt:/java.base
[0.041s][info ][class,load] java.net.SocketImpl source: jrt:/java.base
[0.045s][info ][class,load] java.net.SocketImpl$$Lambda$1/0x0000000800ba0840 source: java.net.SocketImpl
[0.048s][info ][class,load] sun.net.PlatformSocketImpl source: jrt:/java.base
[0.048s][info ][class,load] java.net.AbstractPlainSocketImpl source: jrt:/java.base
[0.048s][info ][class,load] java.net.PlainSocketImpl source: jrt:/java.base
[0.048s][info ][class,load] java.net.AbstractPlainSocketImpl$1 source: jrt:/java.base
[0.050s][info ][class,load] sun.net.ext.ExtendedSocketOptions source: jrt:/java.base
[0.050s][info ][class,load] jdk.net.ExtendedSocketOptions source: jrt:/jdk.net
[0.050s][info ][class,load] java.net.SocketOption source: jrt:/java.base
[0.051s][info ][class,load] jdk.net.ExtendedSocketOptions$ExtSocketOption source: jrt:/jdk.net
[0.051s][info ][class,load] jdk.net.SocketFlow source: jrt:/jdk.net
[0.051s][info ][class,load] jdk.net.ExtendedSocketOptions$PlatformSocketOptions source: jrt:/jdk.net
[0.051s][info ][class,load] jdk.net.ExtendedSocketOptions$PlatformSocketOptions$1 source: jrt:/jdk.net
[0.051s][info ][class,load] jdk.net.ExtendedSocketOptions$1 source: jrt:/jdk.net
[0.051s][info ][class,load] java.net.StandardSocketOptions source: jrt:/java.base
[0.051s][info ][class,load] java.net.StandardSocketOptions$StdSocketOption source: jrt:/java.base
[0.053s][info ][class,load] sun.net.ext.ExtendedSocketOptions$$Lambda$2/0x0000000800ba1040 source: sun.net.ext.ExtendedSocketOptions
[0.056s][info ][class,load] java.net.SocketAddress source: jrt:/java.base
[0.056s][info ][class,load] java.net.InetSocketAddress source: jrt:/java.base
[0.058s][info ][class,load] java.net.InetSocketAddress$InetSocketAddressHolder source: jrt:/java.base
[0.059s][info ][class,load] java.net.SocketCleanable source: jrt:/java.base
</code></pre>
<p><em>P.S Java 15 <a href="http://web.archive.org/web/20221124003658/https://openjdk.java.net/jeps/373" target="_blank">JEP 373:重新实现遗留的 DatagramSocket API</a> ,替换<code>java.net.DatagramSocket</code>和<code>java.net.MulticastSocket</code>的底层实现。</em></p>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221124003658/https://openjdk.java.net/jeps/353" target="_blank">JEP-353 重新实现传统套接字 API</a></li>
</ul>
<h2 id="4jep-354-开关表情预览">4。JEP-354 开关表情(预览)</h2>
<p>Java 12 引入了 <a href="http://web.archive.org/web/20221124003658/https://openjdk.java.net/jeps/325" target="_blank">JEP 325 开关表达式</a>。这个 JEP 放弃了<code>break value</code>而选择了<code>yield</code>关键字,从<code>switch</code>表达式中返回一个值。</p>
<p>这是 Java 13 中的一个预览语言特性</p>
<p>传统的<code>switch</code>语句,我们可以这样返回值:</p>
<pre><code class="language-java"> private static String getNumber(int number) {
String result = "";
switch (number) {
case 1:
case 2:
result = "one or two";
break;
case 3:
result = "three";
break;
case 4:
case 5:
case 6:
result = "four or five or six";
break;
default:
result = "unknown";
}
;
return result;
}
</code></pre>
<p>在 Java 12 中,我们可以使用<code>break</code>从<code>switch</code>返回一个值。</p>
<pre><code class="language-java"> private static String getNumberViaBreak(int number) {
String result = switch (number) {
case 1, 2:
break "one or two";
case 3:
break "three";
case 4, 5, 6:
break "four or five or six";
default:
break "unknown";
};
return result;
}
</code></pre>
<p>在 Java 13 中,上面的 Java 12 <code>value break</code>被删除,取而代之的是<code>yield</code>关键字来返回值。</p>
<pre><code class="language-java"> private static String getNumberViaYield(int number) {
return switch (number) {
case 1, 2:
yield "one or two";
case 3:
yield "three";
case 4, 5, 6:
yield "four or five or six";
default:
yield "unknown";
};
}
</code></pre>
<p>或者像这样</p>
<pre><code class="language-java"> private static String getNumberViaYield2(int number) {
return switch (number) {
case 1, 2:
yield "one or two";
case 3:
yield "three";
case 4, 5, 6:
int i = 0;
i++;
yield "four or five or six : " + i;
default:
yield "unknown";
};
}
</code></pre>
<p>Java 13 中仍然支持规则标签或箭头或<code>case L</code>。</p>
<pre><code class="language-java"> private static String getNumberViaCaseL(int number) {
return switch (number) {
case 1, 2 -> "one or two";
case 3 -> "three";
case 4, 5, 6 -> "four or five or six";
default -> "unknown";
};
}
</code></pre>
<p>或者像这样,混合使用箭头语法和<code>yield</code>。</p>
<pre><code class="language-java"> private static String getNumberViaCaseL2(int number) {
return switch (number) {
case 1, 2 -> "one or two";
case 3 -> "three";
case 4, 5, 6 -> {
int i = 0;
i++;
yield "four or five or six :" + 1;
}
default -> "unknown";
};
}
</code></pre>
<p>这个开关表达式成为了 Java 14 <a href="http://web.archive.org/web/20221124003658/https://openjdk.java.net/jeps/361" target="_blank">JEP 361</a> 的标准特性。</p>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221124003658/https://openjdk.java.net/jeps/354" target="_blank">JEP-354 开关表情(预览)</a></li>
<li><a href="http://web.archive.org/web/20221124003658/https://www.mkyong.com/java/java-13-switch-expressions/" target="_blank">Java 13 开关表达式</a></li>
</ul>
<h2 id="5jep-355-文本块预览">5。JEP-355 文本块(预览)</h2>
<p>这个 JEP 最后引入了一个多行字符串文字,一个文本块。</p>
<p>这个文本块在 Java 15 中是一个永久的特性。</p>
<p>Java 13 之前</p>
<pre><code class="language-java"> String html ="<html>\n" +
" <body>\n" +
" <p>Hello, World</p>\n" +
" </body>\n" +
"</html>\n";
String json ="{\n" +
" \"name\":\"mkyong\",\n" +
" \"age\":38\n" +
"}\n";
</code></pre>
<p>现在是 Java 13</p>
<pre><code class="language-java"> String html = """
<html>
<body>
<p>Hello, World</p>
</body>
</html>
""";
String json = """
{
"name":"mkyong",
"age":38
}
""";
</code></pre>
<p><strong>注意</strong><br>
这个文本块在<a href="http://web.archive.org/web/20221124003658/https://openjdk.java.net/jeps/368" target="_blank">Java 14–JEP 368</a>中有了第二个预览,增加了两个新的转义序列:</p>
<ul>
<li><code>\<end-of-line></code>取消线路终止。</li>
<li><code>\s</code>翻译成单个空格。</li>
</ul>
<p>要启用 Java 13 预览功能:</p>
<pre><code class="language-java"> javac --enable-preview --release 13 Example.java
java --enable-preview Example
</code></pre>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221124003658/https://openjdk.java.net/jeps/355" target="_blank">JEP-355 文本块(预览)</a></li>
<li><a href="/web/20221124003658/https://mkyong.com/java/java-multi-line-string-text-blocks/" target="_blank">多行字符串、文本块示例</a></li>
</ul>
<h2 id="下载源代码-11">下载源代码</h2>
<p>$ git 克隆<a href="http://web.archive.org/web/20221124003658/https://github.com/mkyong/core-java" target="_blank">https://github.com/mkyong/core-java</a></p>
<p>$ cd java-13</p>
<h2 id="参考文献-2">参考文献</h2>
<ul>
<li><a href="http://web.archive.org/web/20221124003658/https://openjdk.java.net/projects/jdk/13/" target="_blank">OpenJDK 13 项目</a></li>
<li>Oracle–Java 13 的到来!</li>
<li><a href="http://web.archive.org/web/20221124003658/https://simonis.github.io/cl4cds/" target="_blank">cl4cds</a></li>
<li><a href="http://web.archive.org/web/20221124003658/https://en.wikipedia.org/wiki/Java_version_history" target="_blank">Java 版本历史</a></li>
<li><a href="http://web.archive.org/web/20221124003658/https://www.mkyong.com/java/java-13-switch-expressions/" target="_blank">Java 13 开关表达式</a></li>
<li><a href="http://web.archive.org/web/20221124003658/https://blog.codefx.org/java/application-class-data-sharing/" target="_blank">Java 13 与应用程序类-数据共享</a></li>
<li><a href="http://web.archive.org/web/20221124003658/https://blog.codefx.org/java/text-blocks/" target="_blank">Java 13 文本块</a></li>
<li>Java 安全性有什么新特性?</li>
<li><a href="/web/20221124003658/https://mkyong.com/java/java-multi-line-string-text-blocks/" target="_blank">Java 多行字符串、文本块示例</a></li>
</ul>
<input type="hidden" id="mkyong-current-postId" value="15190">
<h1 id="java-14-的新特性是什么">Java 14 的新特性是什么</h1>
<blockquote>
<p>原文:<a href="http://web.archive.org/web/20230101150211/https://mkyong.com/java/what-is-new-in-java-14/" target="_blank">http://web.archive.org/web/20230101150211/https://mkyong.com/java/what-is-new-in-java-14/</a></p>
</blockquote>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/a0c2c22c5dd44cd53a1e348dc99ae33b.png" alt="Java 14 logo" loading="lazy"></p>
<p><a href="http://web.archive.org/web/20221124003654/https://openjdk.java.net/projects/jdk/14/" target="_blank">Java 14</a> 于 2020 年 3 月 17 日正式上市,<a href="http://web.archive.org/web/20221124003654/https://jdk.java.net/14/" target="_blank">点击此处</a>下载 Java 14。</p>
<p>Java 14 特性。</p>
<ul>
<li><a href="#jep-305-pattern-matching-for-instanceof-preview">1。JEP 305:实例的模式匹配(预览)</a></li>
<li><a href="#jep-343-packaging-tool-incubator">2。JEP 343:包装工具(保温箱)</a></li>
<li><a href="#jep-345numa-aware-memory-allocation-for-g1">3。JEP 345:G1 的 NUMA 感知内存分配</a></li>
<li><a href="#jep-349-jfr-event-streaming">4。JEP 349: JFR 事件流</a></li>
<li><a href="#jep-352non-volatile-mapped-byte-buffers">5。JEP 352:非易失映射字节缓冲区</a></li>
<li><a href="#jep-358-helpful-nullpointerexceptions">6。JEP 358:有用的空指针异常</a></li>
<li><a href="#jep-359records-preview">7。JEP 359:记录(预览)</a></li>
<li><a href="#jep-361-switch-expressions-standard">8。JEP 361:开关表达式(标准)</a></li>
<li><a href="#jep-362-deprecate-the-solaris-and-sparc-ports">9。JEP 362:反对 Solaris 和 SPARC 端口</a></li>
<li>10。JEP 363:移除并发标记清除(CMS)垃圾收集器</li>
<li><a href="#jep-364-zgc-on-macos-experimental">11。JEP 364:苹果电脑上的 ZGC(实验版)</a></li>
<li><a href="#jep-365-zgc-on-windows-experimental">12。JEP 365:Windows 上的 ZGC(实验)</a></li>
<li>13。JEP 366:反对 ParallelScavenge + SerialOld GC 组合</li>
<li><a href="#jep-367-remove-the-pack200-tools-and-api">14。JEP 367:移除 Pack200 工具和 API</a></li>
<li>15。JEP 368:文本块(第二次预览)</li>
<li>16。JEP 370:外部内存访问 API(孵化器)</li>
</ul>
<p><em>Java 14 开发者特性。</em></p>
<p>切换表达式(标准)、记录(预览)、模式匹配(预览)、文本块或多行(第二次预览)、外部内存访问 API(孵化器)</p>
<h2 id="1jep-305实例的模式匹配预览">1。JEP 305:实例的模式匹配(预览)</h2>
<p>在 Java 14 之前,我们使用<code>instanceof-and-cast</code>来检查对象的类型并转换为变量。</p>
<pre><code class="language-java"> if (obj instanceof String) { // instanceof
String s = (String) obj; // cast
if("jdk14".equalsIgnoreCase(s)){
//...
}
}else {
System.out.println("not a string");
}
</code></pre>
<p>现在 Java 14,我们可以像这样重构上面的代码:</p>
<pre><code class="language-java"> if (obj instanceof String s) { // instanceof, cast and bind variable in one line.
if("jdk4".equalsIgnoreCase(s)){
//...
}
}else {
System.out.println("not a string");
}
</code></pre>
<p>如果<code>obj</code>是<code>String</code>的一个实例,那么它被转换为<code>String</code>,并被赋给绑定变量<code>s</code>。</p>
<p><em>历史</em><br>
这种模式匹配是 Java 16 中的标准特性, <a href="http://web.archive.org/web/20221124003654/https://mkyong.com/java/what-is-new-in-java-16/#jep-394-pattern-matching-for-instanceof" target="_blank">JEP 394</a> 。</p>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221124003654/https://openjdk.java.net/jeps/305" target="_blank">JEP 305:实例的模式匹配(预览)</a></li>
</ul>
<h2 id="2jep-343包装工具保温箱">2。JEP 343:包装工具(保温箱)</h2>
<p>新的<code>jpackage</code>工具,用于将 Java 应用打包成特定于平台的包,例如:</p>
<ul>
<li>Linux: deb 和 rpm</li>
<li>macOS: pkg 和 dmg</li>
<li>Windows: msi 和 exe</li>
</ul>
<p><em>P.S 这个打包工具是 Java 16 的标准特性,<a href="http://web.archive.org/web/20221124003654/https://mkyong.com/java/what-is-new-in-java-16/#jep-392-packaging-tool" target="_blank">访问这个<code>jpackage</code>例子</a>。</em></p>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221124003654/https://openjdk.java.net/jeps/343" target="_blank">JEP 343:包装工具(保温箱)</a></li>
</ul>
<h2 id="3jep-345g1-的-numa-感知内存分配">3。JEP 345:G1 的 NUMA 感知内存分配</h2>
<p>新的 <a href="http://web.archive.org/web/20221124003654/https://en.wikipedia.org/wiki/Non-uniform_memory_access" target="_blank">NUMA 感知的内存</a>分配模式,提高了大型机器上的 G1 性能。添加<code>+XX:+UseNUMA</code>选项将其启用。</p>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221124003654/https://openjdk.java.net/jeps/345" target="_blank">JEP 345:G1 的 NUMA 感知内存分配</a></li>
</ul>
<h2 id="4jep-349-jfr-事件流">4。JEP 349: JFR 事件流</h2>
<p>改进了现有的 JFR 以支持事件流,这意味着现在我们可以实时传输 JFR 事件,而无需将记录的事件转储到磁盘并手动解析。</p>
<p>JDK 飞行记录器(JFR)是一个工具,用于收集关于正在运行的 Java 应用程序的诊断和分析数据。通常,我们开始记录,停止记录,将记录的事件转储到磁盘进行解析,这对于剖析、分析或调试非常有用。</p>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221124003654/https://openjdk.java.net/jeps/349" target="_blank">JEP 349: JFR 事件流</a></li>
<li>JEP 328:飞行记录器</li>
</ul>
<h2 id="5jep-352非易失映射字节缓冲区">5。JEP 352:非易失映射字节缓冲区</h2>
<p>改进的<code>FileChannel</code> API 创建了访问<a href="http://web.archive.org/web/20221124003654/https://en.wikipedia.org/wiki/Non-volatile_memory" target="_blank">非易失性存储器(NVM)</a> 的<code>MappedByteBuffer</code>,这是一种即使经过电源循环也能检索存储数据的存储器。例如,该特性确保了可能仍在缓存中的任何更改都被写回到内存中。</p>
<p><em>P.S 只有 Linux/x64 和 Linux/AArch64 OS 支持这个!</em></p>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221124003654/https://openjdk.java.net/jeps/352" target="_blank">JEP 352:非易失性映射字节缓冲器</a></li>
</ul>
<h2 id="6jep-358有用的空指针异常">6。JEP 358:有用的空指针异常</h2>
<p>通过告知哪个变量为空,改进了对<code>NullPointerExceptions</code>的描述。添加<code>-XX:+ShowCodeDetailsInExceptionMessages</code>选项以启用该功能。</p>
<p>抛出<code>NullPointerException</code>的简单 Java 文件。</p>
<p>Test.java</p>
<pre><code class="language-java"> import java.util.Locale;
public class Test {
public static void main(String[] args) {
String input = null;
String result = showUpperCase(input); // NullPointerException
System.out.println(result);
}
public static String showUpperCase(String str){
return str.toUpperCase(Locale.US);
}
}
</code></pre>
<p>Java 14 之前。</p>
<pre><code class="language-java"> $ /usr/lib/jvm/jdk-14/bin/java Test
Exception in thread "main" java.lang.NullPointerException
at Test.showUpperCase(Test.java:15)
at Test.main(Test.java:9)
</code></pre>
<p>带<code>-XX:+ShowCodeDetailsInExceptionMessages</code>选项的 Java 14。</p>
<pre><code class="language-java"> $ /usr/lib/jvm/jdk-14/bin/java -XX:+ShowCodeDetailsInExceptionMessages Test
Exception in thread "main" java.lang.NullPointerException:
Cannot invoke "String.toUpperCase(java.util.Locale)" because "<parameter1>" is null
at Test.showUpperCase(Test.java:15)
at Test.main(Test.java:9)
</code></pre>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221124003654/https://openjdk.java.net/jeps/358" target="_blank">JEP 358:有用的空指针异常</a></li>
</ul>
<h2 id="7jep-359记录预览">7。JEP 359:记录(预览)</h2>
<p>一个名为<code>record</code>(又名数据类)的新类,它是一个最终类,不是抽象的,它的所有字段也是最终的。<code>record</code>会在编译时自动生成繁琐的<code>constructors</code>、<code>public get</code>、<code>equals()</code>、<code>hashCode()</code>、<code>toString()</code>。</p>
<p>没有设置者,所有字段都是最终的。</p>
<p>一个<code>record</code>或数据类,创建类名和变量,我们就可以开始使用它了。</p>
<p>Point.java</p>
<pre><code class="language-java"> record Point(int x, int y) { }
</code></pre>
<p>Test.java</p>
<pre><code class="language-java"> public class Test {
public static void main(String[] args) {
Point p1 = new Point(10, 20);
System.out.println(p1.x()); // 10
System.out.println(p1.y()); // 20
Point p2 = new Point(11, 22);
System.out.println(p2.x()); // 11
System.out.println(p2.y()); // 22
Point p3 = new Point(10, 20);
System.out.println(p3.x()); // 10
System.out.println(p3.y()); // 20
System.out.println(p1.hashCode()); // 330
System.out.println(p2.hashCode()); // 363
System.out.println(p3.hashCode()); // 330
System.out.println(p1.equals(p2)); // false
System.out.println(p1.equals(p3)); // true
System.out.println(p2.equals(p3)); // false
}
}
</code></pre>
<p>用<code>--enable-preview</code>测试一下。</p>
<pre><code class="language-java"> $ /usr/lib/jvm/jdk-14/bin/javac --enable-preview --release 14 Point.java
$ /usr/lib/jvm/jdk-14/bin/javac --enable-preview --release 14 Test.java
$ /usr/lib/jvm/jdk-14/bin/java --enable-preview Test
</code></pre>
<p>输出</p>
<pre><code class="language-java"> 10
20
11
22
10
20
330
363
330
false
true
false
</code></pre>
<p><em>历史</em></p>
<ul>
<li>Java 15 <a href="http://web.archive.org/web/20221124003654/https://openjdk.java.net/jeps/384" target="_blank">JEP 384</a> 对记录进行了第二次预览,增加了新的特性,比如密封类型、本地记录、记录注释和反射 API。</li>
<li>Java 16 <a href="http://web.archive.org/web/20221124003654/https://openjdk.java.net/jeps/395" target="_blank">JEP 395</a> ,记录或数据类是标准特性。</li>
</ul>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221124003654/https://openjdk.java.net/jeps/359" target="_blank">JEP 359:记录(预览)</a></li>
<li><a href="/web/20221124003654/https://mkyong.com/java/java-14-record-data-class/" target="_blank">Java 14 记录数据类</a></li>
</ul>
<h2 id="8jep-361开关表达式标准">8。JEP 361:开关表达式(标准)</h2>
<p>switch 表达式是 Java 12 和 Java 13 中的预览特性;现在它正式成为了 JDK 的标准特性,这意味着从 Java 14 开始,我们可以通过 switch 表达式返回值,而不需要指定<code>--enable-preview</code>选项。</p>
<p>查看摘要;我们可以使用<code>yield</code>从<code>switch</code>返回一个值。</p>
<pre><code class="language-java"> private static int getValueViaYield(String mode) {
int result = switch (mode) {
case "a", "b":
yield 1;
case "c":
yield 2;
case "d", "e", "f":
// do something here...
System.out.println("Supports multi line block!");
yield 3;
default:
yield -1;
};
return result;
}
</code></pre>
<p>或者通过标签规则或箭头。</p>
<pre><code class="language-java"> private static int getValueViaArrow(String mode) {
int result = switch (mode) {
case "a", "b" -> 1;
case "c" -> 2;
case "d", "e", "f" -> {
// do something here...
System.out.println("Supports multi line block!");
yield 3;
}
default -> -1;
};
return result;
}
</code></pre>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221124003654/https://openjdk.java.net/jeps/361" target="_blank">JEP 361:开关表达式(标准)</a></li>
<li><a href="/web/20221124003654/https://mkyong.com/java/java-13-switch-expressions/" target="_blank">Java 13 开关表达式</a></li>
</ul>
<h2 id="9jep-362反对-solaris-和-sparc-端口">9。JEP 362:反对 Solaris 和 SPARC 端口</h2>
<p>放弃对 Solaris/SPARC、Solaris/x64 和 Linux/SPARC 端口的支持,更少的平台支持意味着新功能的交付速度更快。</p>
<p><em>P.S Java 15 <a href="http://web.archive.org/web/20221124003654/https://openjdk.java.net/jeps/381" target="_blank">JEP 381</a> 移除上述端口。</em></p>
<p><em>延伸阅读</em></p>
<ul>
<li>JEP 362:反对索拉里斯和 SPARC 港口</li>
</ul>
<h2 id="10jep-363移除并发标记清除cms垃圾收集器">10。JEP 363:移除并发标记清除(CMS)垃圾收集器</h2>
<p>Java 9 <a href="http://web.archive.org/web/20221124003654/https://openjdk.java.net/jeps/291" target="_blank">JEP 291</a> 弃用了这个并发标记清除(CMS)垃圾收集器,现在它被正式移除了。</p>
<pre><code class="language-java"> /usr/lib/jvm/jdk-14/bin/java -XX:+UseConcMarkSweepGC Test
OpenJDK 64-Bit Server VM warning: Ignoring option UseConcMarkSweepGC; support was removed in 14.0
</code></pre>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221124003654/https://openjdk.java.net/jeps/363" target="_blank">JEP 363:移除并发标记清除(CMS)垃圾收集器</a></li>
</ul>
<h2 id="11jep-364苹果电脑上的-zgc实验版">11。JEP 364:苹果电脑上的 ZGC(实验版)</h2>
<p>Java 11 <a href="http://web.archive.org/web/20221124003654/https://openjdk.java.net/jeps/333" target="_blank">JEP 333</a> 在 Linux 上引入了 Z 垃圾收集器(ZGC ),现在它可以移植到 macOS。</p>
<p>这个 ZGC 是 Java 15 <a href="http://web.archive.org/web/20221124003654/https://openjdk.java.net/jeps/377" target="_blank">JEP 377</a> 的一个产品特性。</p>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221124003654/https://openjdk.java.net/jeps/364" target="_blank">JEP 364:苹果电脑上的 ZGC(实验版)</a></li>
</ul>
<h2 id="12jep-365windows-上的-zgc实验">12。JEP 365:Windows 上的 ZGC(实验)</h2>
<p>Java 11 <a href="http://web.archive.org/web/20221124003654/https://openjdk.java.net/jeps/333" target="_blank">JEP 333</a> 在 Linux 上引入了 Z 垃圾收集器(ZGC),现在移植到 Windows 版本> = 1803。</p>
<p>这个 ZGC 是 Java 15 <a href="http://web.archive.org/web/20221124003654/https://openjdk.java.net/jeps/377" target="_blank">JEP 377</a> 的一个产品特性。</p>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221124003654/https://openjdk.java.net/jeps/365" target="_blank">JEP 365:Windows 上的 ZGC(实验)</a></li>
</ul>
<h2 id="13jep-366反对-parallelscavenge--serialold-gc-组合">13。JEP 366:反对 ParallelScavenge + SerialOld GC 组合</h2>
<p>由于较少使用和大量维护工作,Java 14 不赞成并行年轻一代和串行老一代 GC 算法的组合。</p>
<pre><code class="language-java"> /usr/lib/jvm/jdk-14/bin/java -XX:-UseParallelOldGC Test
OpenJDK 64-Bit Server VM warning: Option UseParallelOldGC was deprecated in version 14.0 and will likely be removed in a future release.
</code></pre>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221124003654/https://openjdk.java.net/jeps/366" target="_blank">JEP 366:反对 ParallelScavenge + SerialOld GC 组合</a></li>
</ul>
<h2 id="14jep-367移除-pack200-工具和-api">14。JEP 367:移除 Pack200 工具和 API</h2>
<p>Java 11 <a href="http://web.archive.org/web/20221124003654/https://openjdk.java.net/jeps/336" target="_blank">JEP 336</a> 弃用了<code>pack200</code>和<code>unpack200</code>工具,以及<code>java.util.jar</code>包中的<code>Pack200</code> API,现在正式移除。</p>
<p><em>延伸阅读</em></p>
<ul>
<li>JEP 367:移除 Pack200 工具和 API</li>
</ul>
<h2 id="15jep-368文本块第二次预览">15。JEP 368:文本块(第二次预览)</h2>
<p>第一个预览版出现在 Java 13 <a href="http://web.archive.org/web/20221124003654/https://openjdk.java.net/jeps/354" target="_blank">JEP 354</a> 中,现在增加了两个新的转义序列:</p>
<ul>
<li><code>\<end-of-line></code>取消线路终止。</li>
<li><code>\s</code>翻译成单个空格。</li>
</ul>
<p>Test.java</p>
<pre><code class="language-java"> public class Test {
public static void main(String[] args) {
String html = "<html>\n" +
" <body>\n" +
" <p>Hello, World</p>\n" +
" </body>\n" +
"</html>\n";
String java13 = """
<html>
<body>
<p>Hello, World</p>
</body>
</html>
""";
String java14 = """
<html>
<body>\
<p>Hello, '\s' World</p>\
</body>
</html>
""";
System.out.println(html);
System.out.println(java13);
System.out.println(java14);
}
}
</code></pre>
<pre><code class="language-java"> $ /usr/lib/jvm/jdk-14/bin/javac --enable-preview --release 14 Test.java
$ /usr/lib/jvm/jdk-14/bin/java --enable-preview Test
</code></pre>
<p>输出</p>
<pre><code class="language-java"> html>
<body>
<p>Hello, World</p>
</body>
</html>
<html>
<body>
<p>Hello, World</p>
</body>
</html>
<html>
<body> <p>Hello, ' ' World</p> </body>
</html>
</code></pre>
<p>这个文本块在 Java 15 中是一个永久的特性。</p>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221124003654/https://openjdk.java.net/jeps/368" target="_blank">JEP 368:文本块(第二次预览)</a></li>
<li><a href="/web/20221124003654/https://mkyong.com/java/java-multi-line-string-text-blocks/" target="_blank">Java 多行字符串,文本块</a></li>
</ul>
<h2 id="16jep-370外部内存访问-api孵化器">16。JEP 370:外部内存访问 API(孵化器)</h2>
<p>一个<a href="http://web.archive.org/web/20221124003654/https://openjdk.java.net/jeps/11" target="_blank">孵化器模块</a>,允许 Java API 访问 Java 堆之外的外来内存。</p>
<p>Test.java</p>
<pre><code class="language-java"> import jdk.incubator.foreign.*;
import java.lang.invoke.VarHandle;
import java.nio.ByteOrder;
public class Test {
public static void main(String[] args) {
VarHandle intHandle = MemoryHandles.varHandle(
int.class, ByteOrder.nativeOrder());
try (MemorySegment segment = MemorySegment.allocateNative(1024)) {
MemoryAddress base = segment.baseAddress();
// print memory address
System.out.println(base);
// set value 999 into the foreign memory
intHandle.set(base, 999);
// get the value from foreign memory
System.out.println(intHandle.get(base));
}
}
}
</code></pre>
<p>用孵化器模块<code>jdk.incubator.foreign</code>编译运行。</p>
<pre><code class="language-java"> $ /usr/lib/jvm/jdk-14/bin/javac --add-modules jdk.incubator.foreign Test.java
warning: using incubating module(s): jdk.incubator.foreign
1 warning
$ /usr/lib/jvm/jdk-14/bin/java --add-modules jdk.incubator.foreign Test
WARNING: Using incubator modules: jdk.incubator.foreign
MemoryAddress{ region: MemorySegment{ id=0x4aac6dca limit: 1024 } offset=0x0 }
999
</code></pre>
<p><em>历史</em></p>
<ul>
<li>Java 15 <a href="http://web.archive.org/web/20221124003654/https://openjdk.java.net/jeps/383" target="_blank">JEP 383</a> 拥有第二个孵化器。</li>
<li>Java 16 <a href="http://web.archive.org/web/20221124003654/https://openjdk.java.net/jeps/393" target="_blank">JEP 393</a> 拥有第三个孵化器。</li>
</ul>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221124003654/https://openjdk.java.net/jeps/370" target="_blank">JEP 370:外来内存访问 API(孵化器)</a></li>
<li><a href="http://web.archive.org/web/20221124003654/https://download.java.net/java/GA/jdk14/docs/api/jdk.incubator.foreign/jdk/incubator/foreign/package-summary.html" target="_blank">jdk .孵化器. foreign Javadoc</a></li>
</ul>
<h2 id="下载源代码-12">下载源代码</h2>
<p>$ git 克隆<a href="http://web.archive.org/web/20221124003654/https://github.com/mkyong/core-java" target="_blank">https://github.com/mkyong/core-java</a></p>
<p>$ cd java-14</p>
<h2 id="参考文献-3">参考文献</h2>
<ul>
<li><a href="http://web.archive.org/web/20221124003654/https://openjdk.java.net/projects/jdk/14/" target="_blank">OpenJDK 14 项目</a></li>
<li>Java 14 带来了一系列新特性</li>
<li><a href="http://web.archive.org/web/20221124003654/https://en.wikipedia.org/wiki/Java_version_history" target="_blank">Java 版本历史</a></li>
<li><a href="http://web.archive.org/web/20221124003654/https://en.wikipedia.org/wiki/Non-volatile_memory" target="_blank">维基百科–非易失性存储器(NVM)</a></li>
<li><a href="http://web.archive.org/web/20221124003654/https://en.wikipedia.org/wiki/Non-uniform_memory_access" target="_blank">维基百科–非统一内存访问(NUMA)</a></li>
<li>【Java 的数据类和密封类型</li>
<li><a href="http://web.archive.org/web/20221124003654/https://download.java.net/java/GA/jdk14/docs/api/jdk.incubator.foreign/jdk/incubator/foreign/package-summary.html" target="_blank">jdk .孵化器.外国</a></li>
<li><a href="http://web.archive.org/web/20221124003654/https://www.mkyong.com/java/java-13-switch-expressions/" target="_blank">Java 13 开关表达式</a></li>
<li><a href="http://web.archive.org/web/20221124003654/https://blog.codefx.org/java/text-blocks/" target="_blank">Java 13 文本块</a></li>
</ul>
<input type="hidden" id="mkyong-current-postId" value="15592">
<h1 id="java-15-的新特性是什么">Java 15 的新特性是什么</h1>
<blockquote>
<p>原文:<a href="http://web.archive.org/web/20230101150211/https://mkyong.com/java/what-is-new-in-java-15/" target="_blank">http://web.archive.org/web/20230101150211/https://mkyong.com/java/what-is-new-in-java-15/</a></p>
</blockquote>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/badc0719bd97275b32431283d33e266a.png" alt="Java 15 logo" loading="lazy"></p>
<p><a href="http://web.archive.org/web/20221225035437/https://openjdk.java.net/projects/jdk/15/" target="_blank">Java 15</a> 于 2020 年 9 月 15 日正式上市,<a href="http://web.archive.org/web/20221225035437/https://jdk.java.net/15/" target="_blank">点击此处</a>下载 Java 15。</p>
<p>Java 15 特性。</p>
<ul>
<li><a href="#jep-339-edwards-curve-digital-signature-algorithm-eddsa">1。JEP 339:爱德华兹曲线数字签名算法(EdDSA)</a></li>
<li><a href="#jep-360-sealed-classes-preview">2。JEP 360:密封类(预览)</a></li>
<li><a href="#jep-371hidden-classes">3。JEP 371:隐藏类</a></li>
<li><a href="#jep-372remove-the-nashorn-javascript-engine">4。JEP 372:移除 Nashorn JavaScript 引擎</a></li>
<li><a href="#jep-373reimplement-the-legacy-datagramsocket-api">5。JEP 373:重新实现传统的 DatagramSocket API</a></li>
<li><a href="#jep-374disable-and-deprecate-biased-locking">6。JEP 374:禁用和反对偏向锁</a></li>
<li>7 .<a href="#jep-375pattern-matching-for-instanceof-second-preview">。JEP 375:实例的模式匹配(第二次预览)</a></li>
<li><a href="#jep-377zgc-a-scalable-low-latency-garbage-collector">8。JEP 377:ZGC:可扩展的低延迟垃圾收集器</a></li>
<li><a href="#jep-378text-blocks">9。JEP 378:文本块</a></li>
<li>10。JEP 379:Shenandoah:一个低暂停时间的垃圾收集器</li>
<li><a href="#jep-381remove-the-solaris-and-sparc-ports">11。JEP 381:移除 Solaris 和 SPARC 端口</a></li>
<li><a href="#jep-383foreign-memory-access-api-second-incubator">12。JEP 383:外部存储器访问 API(第二孵化器)</a></li>
<li>13。JEP 384:记录(第二次预览)</li>
<li><a href="#jep-385deprecate-rmi-activation-for-removal">14。JEP 385:反对为移除激活 RMI</a></li>
</ul>
<p><em>Java 15 开发者特性。</em></p>
<p>密封类型(预览)、记录(第二次预览)、模式匹配(第二次预览)、隐藏类、文本块或多行(标准)、外部内存访问 API(第二个孵化器)。</p>
<p><strong>注</strong><br>
<a href="http://web.archive.org/web/20221225035437/https://mkyong.com/java/what-is-new-in-java-16/" target="_blank">最新 Java 16</a> 有什么新内容。</p>
<h2 id="1jep-339爱德华兹曲线数字签名算法eddsa">1。JEP 339:爱德华兹曲线数字签名算法(EdDSA)</h2>
<p>密码学相关的东西,Java 15 使用 <a href="http://web.archive.org/web/20221225035437/https://en.wikipedia.org/wiki/EdDSA" target="_blank">Edwards-Curve 数字签名算法(EdDSA)</a> 实现了一个额外的数字签名方案,如 <a href="http://web.archive.org/web/20221225035437/https://tools.ietf.org/html/rfc8032" target="_blank">RFC 8032</a> 所述。EdDSA 签名方案因其比其他签名方案具有更高的安全性和性能(更快)而广受欢迎,也是 <a href="http://web.archive.org/web/20221225035437/https://www.cloudflare.com/learning-resources/tls-1-3/" target="_blank">TLS 1.3</a> 中允许的签名方案之一。</p>
<p>回顾 Java 15 <a href="http://web.archive.org/web/20221225035437/https://docs.oracle.com/en/java/javase/15/docs/specs/security/standard-names.html#signature-algorithms" target="_blank">签名算法</a>。</p>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/ad990c4a5f0fb9c83b07f305c7c50d25.png" alt="EdDSA digital schema" loading="lazy"></p>
<p>示例代码。</p>
<pre><code class="language-java"> package com.mkyong.java15.jep339;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.Base64;
public class JEP339 {
public static void main(String[] args)
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("Ed25519");
KeyPair kp = kpg.generateKeyPair();
byte[] msg = "abc".getBytes(StandardCharsets.UTF_8);
Signature sig = Signature.getInstance("Ed25519");
sig.initSign(kp.getPrivate());
sig.update(msg);
byte[] s = sig.sign();
System.out.println(Base64.getEncoder().encodeToString(s));
}
}
</code></pre>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221225035437/https://openjdk.java.net/jeps/339" target="_blank">JEP 339:爱德华兹曲线数字签名算法(EdDSA)</a></li>
</ul>
<h2 id="2jep-360密封类预览">2。JEP 360:密封类(预览)</h2>
<p>这个 JEP 引入了几个新的关键字,<code>sealed</code>,<code>non-seal</code>,<code>permits</code>来支持<a href="http://web.archive.org/web/20221225035437/https://cr.openjdk.java.net/~briangoetz/amber/datum.html" target="_blank">密封类和接口</a>。密封的类和接口限制了谁可以成为子类型。</p>
<p>2.1 下面的<code>sealed</code>接口允许三个指定的子类来实现它。</p>
<pre><code class="language-java"> public sealed interface Command
permits LoginCommand, LogoutCommand, PluginCommand{
//...
}
</code></pre>
<p>2.2 对于不允许的类,它会抛出编译时错误:</p>
<pre><code class="language-java"> public final class UnknownCommand implements Command {
//...
}
</code></pre>
<pre><code class="language-java"> class is not allowed to extend sealed class: Command
</code></pre>
<p>2.3 密封类必须有子类,每个允许的子类必须选择一个修饰符(密封的、非密封的、最终的)来描述它如何继续由它的超类发起的密封</p>
<p><code>final</code></p>
<pre><code class="language-java"> // close, dun extends me
public final class LoginCommand implements Command{
}
</code></pre>
<p><code>sealed</code></p>
<pre><code class="language-java"> // another sealed class
// sealed class must have subclasses
public sealed class LogoutCommand implements Command
permits LogoutAndDeleteCachedCommand {
}
</code></pre>
<pre><code class="language-java"> // Sealed this class again if you want
public final class LogoutAndDeleteCachedCommand extends LogoutCommand {
}
</code></pre>
<p><code>non-sealed</code></p>
<pre><code class="language-java"> // open...up to you to play this
// Create custom plugin by extending this class
public non-sealed class PluginCommand implements Command {
}
</code></pre>
<p><strong>注意</strong>你注意到关键词<code>non-sealed</code>了吗?我认为这是 Java 中第一个连字符关键字。但是,这是一个预览功能;在未来的版本中,该关键字可能会发生变化。</p>
<p>2.4 该密封类或允许子类与<a href="http://web.archive.org/web/20221225035437/https://cr.openjdk.java.net/~briangoetz/amber/pattern-match.html" target="_blank">模式匹配</a>。</p>
<pre><code class="language-java"> switch (command) {
case LoginCommand: // login
case LogoutCommand: // logout
case PluginCommand: // custom plugin
// no default needed, only permits 3 sub-classes
}
</code></pre>
<p><em>P.S 密封类在 Java 16 中有第二个预览, <a href="http://web.archive.org/web/20221225035437/https://openjdk.java.net/jeps/397" target="_blank">JEP 397</a> 。</em></p>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221225035437/https://openjdk.java.net/jeps/360" target="_blank">JEP 360:密封类(预览)</a></li>
<li>为什么我们允许一个密封类扩展到一个非密封类?</li>
</ul>
<h2 id="3jep-371隐藏类">3。JEP 371:隐藏类</h2>
<p>3.1 这个 JEP 引入了隐藏类,它们不可被发现,并且具有有限的生命周期(较短的生命周期),对于在运行时动态生成类的开发人员来说很好。现在我们可以使用这个新的<a href="http://web.archive.org/web/20221225035437/https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/invoke/MethodHandles.Lookup.html#defineHiddenClass(byte%5B%5D,boolean,java.lang.invoke.MethodHandles.Lookup.ClassOption...)" target="_blank">Lookup::defineHiddenClass</a>API 从字节创建一个隐藏的类或接口。</p>
<p>3.2 示例代码使用<code>defineHiddenClass</code>从 Base64 编码的类创建一个隐藏类,并手动启动静态<code>lookup</code>方法。</p>
<p>LookupProxyTest.java</p>
<pre><code class="language-java"> package com.mkyong.java15.jep371;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Base64;
public class LookupProxyTest {
//Here is the Base64 encoded class.
/*
package com.mkyong.java15.jep371;
public class LookUpProxy{
public static Integer lookup() {
return 1;
}
}*/
static final String CLASS_IN_BASE64 =
"yv66vgAAADcAFQoABAANCgAOAA8HABAHABEBAAY8aW5pdD4BAAMoKV" +
"YBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQAGbG9va3VwAQAVKClM" +
"amF2YS9sYW5nL0ludGVnZXI7AQAKU291cmNlRmlsZQEAEExvb2tVcF" +
"Byb3h5LmphdmEMAAUABgcAEgwAEwAUAQAkY29tL21reW9uZy9qYXZh" +
"MTUvamVwMzcxL0xvb2tVcFByb3h5AQAQamF2YS9sYW5nL09iamVjdA" +
"EAEWphdmEvbGFuZy9JbnRlZ2VyAQAHdmFsdWVPZgEAFihJKUxqYXZh" +
"L2xhbmcvSW50ZWdlcjsAIQADAAQAAAAAAAIAAQAFAAYAAQAHAAAAHQ" +
"ABAAEAAAAFKrcAAbEAAAABAAgAAAAGAAEAAAADAAkACQAKAAEABwAA" +
"AB0AAQAAAAAABQS4AAKwAAAAAQAIAAAABgABAAAABgABAAsAAAACAAw=";
public static void main(String[] args) throws Throwable {
//byte[] array = Files.readAllBytes(
// Paths.get("/home/mkyong/test/LookUpProxy.class"));
//String s = Base64.getEncoder().encodeToString(array);
//System.out.println(s);
testHiddenClass();
}
// create a hidden class and run its static method
public static void testHiddenClass() throws Throwable {
byte[] classInBytes = Base64.getDecoder().decode(CLASS_IN_BASE64);
Class<?> proxy = MethodHandles.lookup()
.defineHiddenClass(classInBytes,
true, MethodHandles.Lookup.ClassOption.NESTMATE)
.lookupClass();
// output: com.mkyong.java15.jep371.LookUpProxy/0x0000000800b94440
System.out.println(proxy.getName());
MethodHandle mh = MethodHandles.lookup().findStatic(proxy,
"lookup",
MethodType.methodType(Integer.class));
Integer status = (Integer) mh.invokeExact();
System.out.println(status);
}
}
</code></pre>
<p>输出</p>
<p>Terminal</p>
<pre><code class="language-java"> com.mkyong.java15.jep371.LookUpProxy/0x0000000800b94440
1
</code></pre>
<p>3.3 这个 JEP 也弃用了<code>Unsafe.defineAnonymousClass</code> API,并将其标记为将来移除。请不要再使用这个 API 了。</p>
<pre><code class="language-java"> byte[] classInBytes = Base64.getDecoder().decode(CLASS_IN_BASE64);
Field theUnsafe = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
sun.misc.Unsafe unsafe = (sun.misc.Unsafe)theUnsafe.get(null);
// @Deprecated(since = "15", forRemoval = false)
Class<?> proxy = unsafe.defineAnonymousClass(
LookupProxyTest.class, classInBytes, null);
</code></pre>
<p><em>延伸阅读</em></p>
<ul>
<li>JEP 371:隐藏类</li>
</ul>
<h2 id="4jep-372移除-nashorn-javascript-引擎">4。JEP 372:移除 Nashorn JavaScript 引擎</h2>
<ul>
<li>Java 8 <a href="http://web.archive.org/web/20221225035437/https://openjdk.java.net/jeps/174" target="_blank">JEP 174</a> 引入了 Nashorn 作为 Rhino Javascript 引擎的替代品。</li>
<li>Java 11 <a href="http://web.archive.org/web/20221225035437/https://openjdk.java.net/jeps/335" target="_blank">JEP 335</a> 弃用了 Nashorn JavaScript 引擎和<code>jjs</code>工具。</li>
<li>现在,Java 15 永久移除了 Nashorn JavaScript 引擎和<code>jjs</code>工具。</li>
</ul>
<p>这个 JEP 还去掉了下面两个模块:</p>
<ul>
<li><code>jdk.scripting.nashorn</code>–包含<code>jdk.nashorn.api.scripting</code>和<code>jdk.nashorn.api.tree</code>包。</li>
<li><code>jdk.scripting.nashorn.shell</code>–包含 jjs 工具。</li>
</ul>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221225035437/https://openjdk.java.net/jeps/372" target="_blank">JEP 372:移除 Nashorn JavaScript 引擎</a></li>
</ul>
<h2 id="5jep-373重新实现传统的-datagramsocket-api">5。JEP 373:重新实现传统的 DatagramSocket API</h2>
<ul>
<li>Java 13 <a href="http://web.archive.org/web/20221225035437/https://openjdk.java.net/jeps/353" target="_blank">JEP 353</a> 重新实现了遗留的套接字 API——<code>java.net.Socket</code>和<code>java.net.ServerSocket</code>。</li>
<li>这一次,Java 15 重新实现了遗留的 datagram socket API—<code>java.net.DatagramSocket</code>和<code>java.net.MulticastSocket</code>。</li>
</ul>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221225035437/https://openjdk.java.net/jeps/373" target="_blank">JEP 373:重新实现传统的 DatagramSocket API</a></li>
</ul>
<h2 id="6jep-374禁用和反对偏向锁">6。JEP 374:禁用和反对偏向锁</h2>
<p>默认情况下,该 JEP 禁用并取消了<a href="http://web.archive.org/web/20221225035437/https://blogs.oracle.com/dave/biased-locking-in-hotspot" target="_blank">偏向锁定</a>。在 Java 15 之前,缺省情况下总是启用偏置锁定,从而提高同步内容的性能。</p>
<p>旧的或遗留的 Java 应用程序使用类似于<code>Hashtable</code>和<code>Vector</code>的同步集合 API,有偏向的锁定可能会提高性能。现在,较新的 Java 应用程序一般使用非同步集合<code>HashMap</code>和<code>ArrayList</code>,偏向锁的性能增益现在一般没那么有用了。</p>
<p>然而,对于 Java 15,我们仍然可以通过使用<code>-XX:+UseBiasedLocking</code>来启用偏置锁定,但是它会提示 VM 警告不推荐使用的 API。</p>
<p>Terminal</p>
<pre><code class="language-java"> # Java 15
$ java -XX:+UseBiasedLocking name
OpenJDK 64-Bit Server VM warning: Option UseBiasedLocking was deprecated
in version 15.0 and will likely be removed in a future release.
</code></pre>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221225035437/https://openjdk.java.net/jeps/374" target="_blank">JEP 374:禁用和反对偏向锁</a></li>
<li><a href="http://web.archive.org/web/20221225035437/https://stackoverflow.com/questions/9439602/biased-locking-in-java" target="_blank">Java 中的堆栈溢出偏置锁定</a></li>
</ul>
<h2 id="7-jep-375实例的模式匹配第二次预览">7 .。JEP 375:实例的模式匹配(第二次预览)</h2>
<p>Java 14 <a href="http://web.archive.org/web/20221225035437/https://openjdk.java.net/jeps/305" target="_blank">JEP 305</a> 引入了<a href="http://web.archive.org/web/20221225035437/https://cr.openjdk.java.net/~briangoetz/amber/pattern-match.html" target="_blank">模式匹配</a>作为预览特性。这个 JEP 是模式匹配的第二次预览,以获得额外的反馈,对 API 没有改变。</p>
<p>一个典型的<code>instanceof-and-cast</code>来检查对象的类型并转换它。</p>
<pre><code class="language-java"> private static void print(Object obj) {
if (obj instanceof String) { // instanceof
String s = (String) obj; // cast
if ("java15".equalsIgnoreCase(s)) {
System.out.println("Hello Java 15");
}
} else {
System.out.println("not a string");
}
}
</code></pre>
<p>对于模式匹配,我们可以在一行中进行检查、造型和绑定。</p>
<pre><code class="language-java"> private static void printWithPatternMatching(Object obj) {
// instanceof, cast and bind variable in one line.
if (obj instanceof String s) {
if ("java15".equalsIgnoreCase(s)) {
System.out.println("Hello Java 15");
}
} else {
System.out.println("not a string");
}
}
</code></pre>
<p>模式匹配是 Java 16 的标准特性, <a href="http://web.archive.org/web/20221225035437/https://openjdk.java.net/jeps/394" target="_blank">JEP 394</a> 。</p>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221225035437/https://openjdk.java.net/jeps/375" target="_blank">JEP 375:实例的模式匹配(第二次预览)</a></li>
</ul>
<h2 id="8jep-377zgc可扩展的低延迟垃圾收集器">8。JEP 377:ZGC:可扩展的低延迟垃圾收集器</h2>
<p>Java 11 <a href="http://web.archive.org/web/20221225035437/https://openjdk.java.net/jeps/333" target="_blank">JEP 333</a> 引入了 <a href="http://web.archive.org/web/20221225035437/https://wiki.openjdk.java.net/display/zgc/Main" target="_blank">ZGC 垃圾收集器</a>作为实验特性。</p>
<ul>
<li>这个 JEP 修复了一些错误,增加了一些功能和增强,现在支持主要平台,如 Linux/x86_64、Linux/aarch64、Windows 和 macOS。</li>
<li>这个 JEP 还将 Z 垃圾收集器从一个实验性特性变成了一个产品特性。然而,默认的垃圾收集器仍然是 <a href="http://web.archive.org/web/20221225035437/https://www.oracle.com/technetwork/tutorials/tutorials-1876574.html" target="_blank">G1</a> 。</li>
</ul>
<p>下面的命令启用 ZGC 垃圾收集器。</p>
<p>Terminal</p>
<pre><code class="language-java"> $ java -XX:+UseZGC className
</code></pre>
<p><em>延伸阅读</em></p>
<ul>
<li>JEP 377:ZGC:一个可扩展的低延迟垃圾收集器</li>
</ul>
<h2 id="9jep-378文本块">9。JEP 378:文本块</h2>
<p>最后,多行字符串或文本块是 Java 15 的一个永久特性。</p>
<p>历史:</p>
<ul>
<li>Java 12 <a href="http://web.archive.org/web/20221225035437/https://openjdk.java.net/jeps/326" target="_blank">JEP 326 原始字符串文字</a></li>
<li>Java 13 <a href="http://web.archive.org/web/20221225035437/https://openjdk.java.net/jeps/355" target="_blank">JEP 355:文本块(预览)</a></li>
<li>Java 14 <a href="http://web.archive.org/web/20221225035437/https://openjdk.java.net/jeps/368" target="_blank">JEP 368:文本块(第二次预览)</a></li>
<li>Java 15,永久特性。</li>
</ul>
<p>示例代码。</p>
<pre><code class="language-java"> String html = "<html>\n" +
" <body>\n" +
" <p>Hello, World</p>\n" +
" </body>\n" +
"</html>\n";
String java15 = """
<html>
<body>
<p>Hello, World</p>
</body>
</html>
""";
</code></pre>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221225035437/https://openjdk.java.net/jeps/378" target="_blank">JEP 378:文本块</a></li>
</ul>
<h2 id="10jep-379shenandoah一个低暂停时间的垃圾收集器">10。JEP 379:Shenandoah:一个低暂停时间的垃圾收集器</h2>
<p>Java 12 <a href="http://web.archive.org/web/20221225035437/https://openjdk.java.net/jeps/189" target="_blank">JEP 189</a> 引入了 <a href="http://web.archive.org/web/20221225035437/https://wiki.openjdk.java.net/display/shenandoah/Main" target="_blank">Shenandoah 垃圾收集器</a>作为实验特性,现在成为 Java 15 中的产品特性。</p>
<p>在 Java 15 之前,我们需要<code>-XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC</code>来启用 Shenandoah GC。</p>
<p>Terminal</p>
<pre><code class="language-java"> java -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC
</code></pre>
<p>在 Java 15 中,我们只需要<code>-XX:+UseShenandoahGC</code>来启用 Shenandoah GC。</p>
<p>Terminal</p>
<pre><code class="language-java"> java -XX:+UseShenandoahGC
</code></pre>
<p>然而,官方的 <a href="http://web.archive.org/web/20221225035437/https://jdk.java.net/15/" target="_blank">OpenJDK 15</a> 构建并不包括 Shenandoah GC(就像 Java 12 中发生的一样)。阅读这个故事——<a href="http://web.archive.org/web/20221225035437/https://developers.redhat.com/blog/2019/04/19/not-all-openjdk-12-builds-include-shenandoah-heres-why/" target="_blank">不是所有的 OpenJDK 12 版本都包含 Shenandoah:下面是原因</a>。</p>
<p>Terminal</p>
<pre><code class="language-java"> $ java -XX:+UseShenandoahGC ClassName
Error occurred during initialization of VM
Option -XX:+UseShenandoahGC not supported
$ java -version
openjdk version "15" 2020-09-15
OpenJDK Runtime Environment (build 15+36-1562)
OpenJDK 64-Bit Server VM (build 15+36-1562, mixed mode, sharing)
</code></pre>
<p>为了尝试 Shenandoah GC,我们需要其他 JDK 版本,如 <a href="http://web.archive.org/web/20221225035437/https://adoptopenjdk.net/" target="_blank">AdoptOpenJDK</a> 。</p>
<p>Terminal</p>
<pre><code class="language-java"> $ java -XX:+UseShenandoahGC ClassName
$ java -version
openjdk version "15" 2020-09-15
OpenJDK Runtime Environment AdoptOpenJDK (build 15+36)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 15+36, mixed mode, sharing)
</code></pre>
<p>默认的垃圾收集器仍然是 G1 T2。</p>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221225035437/https://openjdk.java.net/jeps/379" target="_blank">JEP 379:谢南多厄:一个低暂停时间的垃圾收集器</a></li>
</ul>
<h2 id="11jep-381移除-solaris-和-sparc-端口">11。JEP 381:移除 Solaris 和 SPARC 端口</h2>
<p>Java 14 <a href="http://web.archive.org/web/20221225035437/https://openjdk.java.net/jeps/362" target="_blank">JEP 362</a> 弃用了 Solaris/SPARC、Solaris/x64 和 Linux/SPARC 端口,现在它在 Java 15 中被正式删除。</p>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221225035437/https://openjdk.java.net/jeps/381" target="_blank">JEP 381:移除 Solaris 和 SPARC 端口</a></li>
</ul>
<h2 id="12jep-383外部存储器访问-api第二孵化器">12。JEP 383:外部存储器访问 API(第二孵化器)</h2>
<p>Java 14 <a href="http://web.archive.org/web/20221225035437/https://openjdk.java.net/jeps/370" target="_blank">JEP 370</a> 引入了新的外来内存访问 API 作为<a href="http://web.archive.org/web/20221225035437/https://openjdk.java.net/jeps/11" target="_blank">孵化器模块</a>。这个 JEP 对 API 做了一些修改,它仍然会在孵化器模块中。</p>
<p>示例代码</p>
<p>HelloForeignMemory.java</p>
<pre><code class="language-java"> import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryHandles;
import jdk.incubator.foreign.MemorySegment;
import java.lang.invoke.VarHandle;
import java.nio.ByteOrder;
public class HelloForeignMemory {
public static void main(String[] args) {
VarHandle intHandle = MemoryHandles.varHandle(
int.class, ByteOrder.nativeOrder());
try (MemorySegment segment = MemorySegment.allocateNative(1024)) {
MemoryAddress base = segment.baseAddress();
// print memory address
System.out.println(base);
// set value 999 into the foreign memory
intHandle.set(base, 999);
// get the value from foreign memory
System.out.println(intHandle.get(base));
}
}
}
</code></pre>
<p>我们需要添加<code>--add-modules jdk.incubator.foreign</code>来启用孵化器模块<code>jdk.incubator.foreign</code>。</p>
<p>Terminal</p>
<pre><code class="language-java"> $ javac --add-modules jdk.incubator.foreign HelloForeignMemory.java
warning: using incubating module(s): jdk.incubator.foreign
1 warning
$ java --add-modules jdk.incubator.foreign HelloForeignMemory
WARNING: Using incubator modules: jdk.incubator.foreign
MemoryAddress{ region: MemorySegment{ id=0x27c908f5 limit: 1024 } offset=0x0 }
999
</code></pre>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221225035437/https://openjdk.java.net/jeps/383" target="_blank">JEP 383:外来存储器访问 API(第二孵化器)</a></li>
</ul>
<h2 id="13jep-384记录第二次预览">13。JEP 384:记录(第二次预览)</h2>
<p>Java 14 <a href="http://web.archive.org/web/20221225035437/https://openjdk.java.net/jeps/384" target="_blank">JEP 359</a> 引入了记录作为预览功能。这个 JEP 用支持密封类型、本地记录、记录注释和记录反射 API 等特性增强了记录。</p>
<p>13.1 记录和密封类型</p>
<p>Fruit.java</p>
<pre><code class="language-java"> public sealed interface Fruit permits Apple, Orange {}
record Apple() implements Fruit{}
record Orange() implements Fruit{}
</code></pre>
<p>13.2 本地记录-方法内部声明的记录。</p>
<p>下面的代码片段使用本地记录来提高流操作的可读性。</p>
<pre><code class="language-java"> List<Merchant> findTopMerchants(List<Merchant> merchants, int month) {
// Local record
record MerchantSales(Merchant merchant, double sales) {
}
return merchants.stream()
.map(merchant -> new MerchantSales(merchant, computeSales(merchant, month)))
.sorted((m1, m2) -> Double.compare(m2.sales(), m1.sales()))
.map(MerchantSales::merchant)
.collect(toList());
}
private double computeSales(Merchant merchant, int month) {
// some business logic to get the sales...
return ThreadLocalRandom.current().nextDouble(100, 10000);
}
</code></pre>
<p>13.3 记录注释</p>
<pre><code class="language-java"> public record Game(@CustomAnno Rank rank) { ... }
</code></pre>
<p>13.4 反射 API</p>
<p><code>java.lang.Class</code>增加了两个公共方法:</p>
<ul>
<li><code>RecordComponent[] getRecordComponents()</code></li>
<li><code>boolean isRecord()</code></li>
</ul>
<p>记录是 Java 16 中的标准特性, <a href="http://web.archive.org/web/20221225035437/https://openjdk.java.net/jeps/384" target="_blank">JEP 395</a> 。</p>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221225035437/https://openjdk.java.net/jeps/384" target="_blank">JEP 384:记录(第二次预览)</a></li>
</ul>
<h2 id="14jep-385反对为移除激活-rmi">14。JEP 385:反对为移除激活 RMI</h2>
<p>这个 JEP 反对过时的 RMI 激活机制。这不会影响马绍尔群岛的其他部分。</p>
<p><em>延伸阅读</em></p>
<ul>
<li><a href="http://web.archive.org/web/20221225035437/https://openjdk.java.net/jeps/385" target="_blank">JEP 385:反对为移除激活 RMI</a></li>
</ul>
<h2 id="下载源代码-13">下载源代码</h2>
<p>$ git 克隆<a href="http://web.archive.org/web/20221225035437/https://github.com/mkyong/core-java" target="_blank">https://github.com/mkyong/core-java</a></p>
<p>$ cd java-15</p>
<h2 id="参考文献-4">参考文献</h2>
<ul>
<li><a href="http://web.archive.org/web/20221225035437/https://openjdk.java.net/projects/jdk/15/" target="_blank">OpenJDK 15 项目</a></li>
<li><a href="http://web.archive.org/web/20221225035437/https://www.cloudflare.com/learning-resources/tls-1-3/" target="_blank">cloud flare–TLS 1.3 有什么新功能?</a></li>
<li><a href="http://web.archive.org/web/20221225035437/https://en.wikipedia.org/wiki/EdDSA" target="_blank">维基百科–爱德华兹曲线数字签名算法(EdDSA)</a></li>
<li><a href="http://web.archive.org/web/20221225035437/https://tools.ietf.org/html/rfc8032" target="_blank">RFC 8032</a></li>
<li>【Java 的模式匹配</li>
<li><a href="http://web.archive.org/web/20221225035437/https://blogs.oracle.com/dave/biased-locking-in-hotspot" target="_blank">热点偏锁</a></li>
<li><a href="http://web.archive.org/web/20221225035437/https://wiki.openjdk.java.net/display/zgc/Main" target="_blank">open JDK Wiki–ZCG</a></li>
<li><a href="http://web.archive.org/web/20221225035437/https://docs.oracle.com/en/java/javase/11/gctuning/z-garbage-collector1.html" target="_blank">Oracle–Z 垃圾收集器</a></li>
<li><a href="http://web.archive.org/web/20221225035437/https://www.oracle.com/technetwork/tutorials/tutorials-1876574.html" target="_blank">G1 垃圾收集工</a></li>
<li><a href="http://web.archive.org/web/20221225035437/https://wiki.openjdk.java.net/display/shenandoah/Main" target="_blank">谢南多厄垃圾收集器</a></li>
<li><a href="http://web.archive.org/web/20221225035437/https://openjdk.java.net/jeps/11" target="_blank">JEP 11 号:孵化器模块</a></li>
<li><a href="http://web.archive.org/web/20221225035437/https://en.wikipedia.org/wiki/Java_version_history" target="_blank">Java 版本历史</a></li>
</ul>
<input type="hidden" id="mkyong-current-postId" value="16189">
<h1 id="java-16-的新特性是什么">Java 16 的新特性是什么</h1>
<blockquote>
<p>原文:<a href="http://web.archive.org/web/20230101150211/https://mkyong.com/java/what-is-new-in-java-16/" target="_blank">http://web.archive.org/web/20230101150211/https://mkyong.com/java/what-is-new-in-java-16/</a></p>
</blockquote>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/cdeecfe3aafadd386816cbc526eed129.png" alt="Java 16 logo" loading="lazy"></p>
<p><a href="http://web.archive.org/web/20221225035521/https://openjdk.java.net/projects/jdk/16/" target="_blank">Java 16</a> 于 2021 年 3 月 16 日正式上市,<a href="http://web.archive.org/web/20221225035521/https://jdk.java.net/16/" target="_blank">点击此处</a>下载 Java 16。</p>
<p>Java 16 有 17 个 JEP 项目。</p>
<ul>
<li><a href="#jep-338-vector-api-incubator">1。JEP 338:载体 API(孵化器)</a></li>
<li><a href="#jep-347enable-c14-language-features">2。JEP 347:启用 C++14 语言特性</a></li>
<li><a href="#jep-357-migrate-from-mercurial-to-git">3。JEP 357:从 Mercurial 迁移到 Git</a></li>
<li><a href="#jep-369-migrate-to-github">4。JEP 369:迁移到 GitHub</a></li>
<li><a href="#jep-376-zgc-concurrent-thread-stack-processing">5。JEP 376: ZGC:并发线程堆栈处理</a></li>
<li><a href="#jep-380-unix-domain-socket-channels">6。JEP 380: Unix 域套接字通道</a></li>
<li><a href="#jep-386-alpine-linux-port">7。JEP 386:阿尔卑斯 Linux 端口</a></li>
<li><a href="#jep-387-elastic-metaspace">8。JEP 387:弹性元空间</a></li>
<li><a href="#jep-388-windowsaarch64-port">9。jep 388:windows/aach 64 端口</a></li>
<li>10。JEP 389:外来链接器 API(孵化器)</li>
<li><a href="#jep-390-warnings-for-value-based-classes">11。JEP 390:基于价值的类的警告</a></li>
<li><a href="#jep-392-packaging-tool">12。JEP 392:包装工具</a></li>
<li>13。JEP 393:外部内存访问 API(第三个孵化器)</li>
<li><a href="#jep-394-pattern-matching-for-instanceof">14。JEP 394:实例的模式匹配</a></li>
<li>15。JEP 395:记录</li>
<li>16。JEP 396:默认强封装 JDK 内部构件</li>
<li><a href="#jep-397-sealed-classes-second-preview">17。JEP 397:密封类(第二次预览)</a></li>
<li><a href="#download-source-code">下载源代码</a></li>
<li><a href="#references">参考文献</a></li>
</ul>
<p><em>Java 16 开发者特性。</em></p>
<p>jpackage、记录(标准)、模式匹配(标准)、密封类型(第二次预览)、外部内存访问 API(第三个孵化器)、替换 JNI 的外部链接器 API(孵化器)、vector APIs(孵化器)</p>
<h2 id="1jep-338载体-api孵化器">1。JEP 338:载体 API(孵化器)</h2>
<p>Java 支持<a href="http://web.archive.org/web/20221225035521/https://en.wikipedia.org/wiki/Automatic_vectorization" target="_blank">自动向量化</a>来优化算术算法,这意味着 Java ( <a href="http://web.archive.org/web/20221225035521/https://en.wikipedia.org/wiki/Just-in-time_compilation" target="_blank">JIT 编译器</a>)会自动将一些标量操作(一次一项)转化为向量操作(一次多项);然而,开发人员无法控制这种向量操作转换,它完全依赖于 JIT 编译器来优化代码,而且,并不是所有的标量操作都是可转换的。</p>
<p>这个 JEP 引入了新的 Vector APIs,允许开发人员显式地执行 Vector 操作。</p>
<ul>
<li>一个<a href="http://web.archive.org/web/20221225035521/https://en.wikipedia.org/wiki/Vector_processor" target="_blank">向量处理器</a>一次处理多个数据,称为<a href="http://web.archive.org/web/20221225035521/https://en.wikipedia.org/wiki/SIMD" target="_blank">单指令多数据(SIMD)</a> 。</li>
<li>一个<a href="http://web.archive.org/web/20221225035521/https://en.wikipedia.org/wiki/Scalar_processor" target="_blank">标量处理器</a>一次只处理一个数据,称为<a href="http://web.archive.org/web/20221225035521/https://en.wikipedia.org/wiki/SISD" target="_blank">单指令单数据(SISD)</a> 。</li>
</ul>
<p><strong>延伸阅读</strong></p>
<ul>
<li><a href="http://web.archive.org/web/20221225035521/https://openjdk.java.net/jeps/338" target="_blank">JEP 338:载体 API(培养箱)</a></li>
<li><a href="http://web.archive.org/web/20221225035521/http://daniel-strecker.com/blog/2020-01-14_auto_vectorization_in_java/" target="_blank">Java 中的自动矢量化</a></li>
<li><a href="http://web.archive.org/web/20221225035521/https://mkyong.com/java/what-is-new-in-java-17/#jep-414-vector-api-second-incubator" target="_blank">Java 17 JEP 414: Vector API(第二孵化器)</a></li>
</ul>
<h2 id="2jep-347启用-c14-语言特性">2。JEP 347:启用 C++14 语言特性</h2>
<p>这个 JEP 允许在 JDK 内的 C++源代码中使用[C++ 14 特性]((<a href="https://en" target="_blank">https://en</a> . Wikipedia . org/wiki/C % 2B % 2b 14)。</p>
<p><strong>延伸阅读</strong></p>
<ul>
<li><a href="http://web.archive.org/web/20221225035521/https://openjdk.java.net/jeps/347" target="_blank">JEP 347:启用 C++14 语言特性</a></li>
</ul>
<h2 id="3jep-357从-mercurial-迁移到-git">3。JEP 357:从 Mercurial 迁移到 Git</h2>
<p>本 JEP 将 OpenJDK 源代码从 Mercurial 迁移到 Git 或 GitHub,下面涉及到 <a href="#">JEP 369</a></p>
<p>迁移到 Git 的原因:</p>
<ol>
<li>版本控制系统元数据(Mercurial)文件太大。</li>
<li>可用工具</li>
<li>可用主机</li>
</ol>
<p><strong>延伸阅读</strong></p>
<ul>
<li><a href="http://web.archive.org/web/20221225035521/https://openjdk.java.net/jeps/357" target="_blank">JEP 357:从 Mercurial 迁移到 Git</a></li>
</ul>
<h2 id="4jep-369迁移到-github">4。JEP 369:迁移到 GitHub</h2>
<p>此次 JEP 加入上述 <a href="#jep-357-migrate-from-mercurial-to-git">JEP 357</a> ,将 OpenJDK 源代码从 Mercurial 迁移到 <a href="http://web.archive.org/web/20221225035521/https://github.com/openjdk/" target="_blank">GitHub</a> 。</p>
<p><strong>延伸阅读</strong></p>
<ul>
<li><a href="http://web.archive.org/web/20221225035521/https://openjdk.java.net/jeps/369" target="_blank">JEP 369:迁移到 GitHub</a></li>
</ul>
<h2 id="5jep-376-zgc并发线程堆栈处理">5。JEP 376: ZGC:并发线程堆栈处理</h2>
<p>这个 JEP 通过将 ZGC 线程堆栈处理从安全点转移到并发阶段,改进了 Z 垃圾收集器(ZGC)。</p>
<p><em>历史</em></p>
<ul>
<li>Java 11 <a href="http://web.archive.org/web/20221225035521/https://openjdk.java.net/jeps/333" target="_blank">JEP 333</a> 引入了 Z 垃圾收集器(ZGC)作为实验性的垃圾收集器。</li>
<li>Java 15 <a href="http://web.archive.org/web/20221225035521/https://openjdk.java.net/jeps/377" target="_blank">JEP 377</a> ,ZGC 成为了产品的一大特色。</li>
</ul>
<p><strong>延伸阅读</strong></p>
<ul>
<li>JEP 376: ZGC:并发线程堆栈处理</li>
</ul>
<h2 id="6jep-380-unix-域套接字通道">6。JEP 380: Unix 域套接字通道</h2>
<p><a href="http://web.archive.org/web/20221225035521/https://en.wikipedia.org/wiki/Unix_domain_socket" target="_blank">Unix 域套接字</a>用于同一主机上的进程间通信(IPC ),这意味着在同一主机上执行的进程之间交换数据。Unix 域套接字类似于 TCP/IP 套接字,只是它们由文件系统路径名寻址,而不是由互联网协议(IP)地址和端口号寻址。大多数 Unix 平台,Windows 10 和 Windows Server 2019,也支持 Unix 域套接字。</p>
<p>此次 JEP 在现有的 <a href="http://web.archive.org/web/20221225035521/https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/nio/channels/SocketChannel.html" target="_blank">SocketChannel</a> 和 <a href="http://web.archive.org/web/20221225035521/https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/nio/channels/ServerSocketChannel.html" target="_blank">ServerSocketChannel</a> 的基础上增加了<a href="http://web.archive.org/web/20221225035521/https://en.wikipedia.org/wiki/Unix_domain_socket" target="_blank">Unix-domain(AF _ Unix)socket</a>支持。</p>
<p>新的 Unix 域套接字类或 API:</p>
<ul>
<li>新套接字地址类,<code>java.net.UnixDomainSocketAddress</code></li>
<li>新增<code>enum</code>,<code>java.net.StandardProtocolFamily.UNIX</code></li>
</ul>
<p><strong>延伸阅读</strong></p>
<ul>
<li><a href="http://web.archive.org/web/20221225035521/https://openjdk.java.net/jeps/380" target="_blank">JEP 380: Unix 域套接字通道</a></li>
</ul>
<h2 id="7jep-386阿尔卑斯-linux-端口">7。JEP 386:阿尔卑斯 Linux 端口</h2>
<p>这个 JEP 把 JDK 移植到了 Alpine Linux 和其他使用 musl 实现的 Linux 发行版上。这个 JDK 端口使 Java 能够在 Alpine Linux 中开箱即用,这有利于那些依赖于 Java 的框架或工具,如 Tomcat 和 Spring。</p>
<p>Alpine Linux 包含较小的映像大小,广泛用于云部署、微服务和容器环境。</p>
<p><strong>延伸阅读</strong></p>
<ul>
<li><a href="http://web.archive.org/web/20221225035521/https://openjdk.java.net/jeps/386" target="_blank">JEP 386:阿尔卑斯 Linux 端口</a></li>
</ul>
<h2 id="8jep-387弹性元空间">8。JEP 387:弹性元空间</h2>
<p>Java 8 <a href="http://web.archive.org/web/20221225035521/https://openjdk.java.net/jeps/122" target="_blank">JEP 122</a> 移除了 PermGen(永久生成),并引入了<a href="http://web.archive.org/web/20221225035521/https://wiki.openjdk.java.net/display/HotSpot/Metaspace" target="_blank">元空间</a>,这是 hotspot 中的一个原生堆外内存管理器。</p>
<p>该 JEP 通过更迅速地将未使用的热点类元数据或元空间内存返回给操作系统,减少了元空间占用空间,并简化了元空间代码,从而改进了元空间内存管理。</p>
<p><strong>延伸阅读</strong></p>
<ul>
<li>JEP 387:弹性元空间</li>
</ul>
<h2 id="9jep-388windowsaach-64-端口">9。jep 388:windows/aach 64 端口</h2>
<p>该 JEP 将 JDK 移植到 Windows/AArch64,在 ARM 硬件、服务器或基于 ARM 的笔记本电脑上运行 JDK + Windows。</p>
<p>Windows/AArch64 是最终用户市场的普遍需求。</p>
<p><strong>延伸阅读</strong></p>
<ul>
<li><a href="http://web.archive.org/web/20221225035521/https://openjdk.java.net/jeps/388" target="_blank">jep 388:windows/aach 64 端口</a></li>
</ul>
<h2 id="10jep-389外来链接器-api孵化器">10。JEP 389:外来链接器 API(孵化器)</h2>
<p>该 JEP 使 Java 代码能够调用或被用其他语言编写的本机代码调用,如 C 或 C++,取代 <a href="http://web.archive.org/web/20221225035521/https://en.wikipedia.org/wiki/Java_Native_Interface" target="_blank">Java 本机接口(JNI)</a></p>
<p>这是一个孵化的特征;需要添加<code>--add-modules jdk.incubator.foreign</code>来编译和运行 Java 代码。</p>
<p>下面的例子显示了如何使用外部链接器 API 来调用标准 C 库<code>strlen</code>以返回字符串的长度。</p>
<pre><code class="language-java"> size_t strlen(const char *s);
</code></pre>
<p>CStrLen.java</p>
<pre><code class="language-java"> import jdk.incubator.foreign.*;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import static jdk.incubator.foreign.CLinker.C_LONG;
import static jdk.incubator.foreign.CLinker.C_POINTER;
// Java call standard C library
// size_t strlen(const char *s);
public class CStrLen {
public static void main(String[] args) throws Throwable {
if (args.length == 0) {
throw new IllegalArgumentException("Please provide an argument.");
}
String input = args[0];
MethodHandle strlen = CLinker.getInstance().downcallHandle(
LibraryLookup.ofDefault().lookup("strlen").get(),
MethodType.methodType(long.class, MemoryAddress.class),
FunctionDescriptor.of(C_LONG, C_POINTER)
);
try (MemorySegment str = CLinker.toCString(input)) {
long len = (long) strlen.invokeExact(str.address()); // 5
System.out.println(len);
}
}
}
</code></pre>
<p>启用孵化器模块进行编译。</p>
<p>Terminal</p>
<pre><code class="language-java"> $ javac --add-modules jdk.incubator.foreign CStrLen.java
warning: using incubating module(s): jdk.incubator.foreign
1 warning
</code></pre>
<p>启用保育箱模块运行。</p>
<p>Terminal</p>
<pre><code class="language-java"> $ java --add-modules jdk.incubator.foreign -Dforeign.restricted=permit CStrLen mkyong
WARNING: Using incubator modules: jdk.incubator.foreign
6
</code></pre>
<p>下面的 10.2 是另一个调用 C 代码中定义的函数的例子。</p>
<p>打印 hello world 的简单 C 函数。</p>
<pre><code class="language-java"> #include <stdio.h>
void printHello() {
printf("hello world!\n");
}
</code></pre>
<p>编译上面的 C 代码,输出到共享库<code>hello.so</code>。</p>
<p>Terminal</p>
<pre><code class="language-java"> $ gcc -c -fPIC hello.c
$ gcc -shared -o hello.so hello.o
</code></pre>
<p>在 Java 程序下面,找到<code>hello.so</code>并调用它的方法<code>printHello</code>。</p>
<p>JEP389.java</p>
<pre><code class="language-java"> import jdk.incubator.foreign.CLinker;
import jdk.incubator.foreign.FunctionDescriptor;
import jdk.incubator.foreign.LibraryLookup;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.nio.file.Path;
import java.util.Optional;
public class JEP389 {
public static void main(String[] args) throws Throwable {
Path path = Path.of("/home/mkyong/projects/core-java/java-16/hello.so");
LibraryLookup libraryLookup = LibraryLookup.ofPath(path);
Optional<LibraryLookup.Symbol> optionalSymbol = libraryLookup.lookup("printHello");
if (optionalSymbol.isPresent()) {
LibraryLookup.Symbol symbol = optionalSymbol.get();
FunctionDescriptor functionDescriptor = FunctionDescriptor.ofVoid();
MethodType methodType = MethodType.methodType(Void.TYPE);
MethodHandle methodHandle = CLinker.getInstance().downcallHandle(
symbol.address(),
methodType,
functionDescriptor);
methodHandle.invokeExact();
}
}
}
</code></pre>
<p>编译。</p>
<p>Terminal</p>
<pre><code class="language-java"> $ javac --add-modules jdk.incubator.foreign JEP389.java
warning: using incubating module(s): jdk.incubator.foreign
1 warning
</code></pre>
<p>快跑。</p>
<p>Terminal</p>
<pre><code class="language-java"> $ java --add-modules jdk.incubator.foreign -Dforeign.restricted=permit JEP389
WARNING: Using incubator modules: jdk.incubator.foreign
hello world!
</code></pre>
<p><em>历史</em></p>
<ul>
<li>Java 14 <a href="http://web.archive.org/web/20221225035521/https://openjdk.java.net/jeps/370" target="_blank">JEP 370</a> 引入了外来内存访问 API(孵化器)。</li>
<li>Java 15 <a href="http://web.archive.org/web/20221225035521/https://openjdk.java.net/jeps/383" target="_blank">JEP 383</a> 引入了外来内存访问 API(第二孵化器)。</li>
<li>Java 16 <a href="http://web.archive.org/web/20221225035521/https://openjdk.java.net/jeps/389" target="_blank">JEP 389</a> 引入了国外的链接器 API(孵化器)。</li>
<li>Java 16 <a href="http://web.archive.org/web/20221225035521/https://openjdk.java.net/jeps/393" target="_blank">JEP 393</a> 引入了外来内存访问 API(第三孵化器)。</li>
<li>Java 17 <a href="http://web.archive.org/web/20221225035521/https://openjdk.java.net/jeps/412" target="_blank">JEP 412</a> 引入了外来函数&内存 API(孵化器)。</li>
</ul>
<p><strong>延伸阅读</strong></p>
<ul>
<li><a href="http://web.archive.org/web/20221225035521/https://openjdk.java.net/jeps/389" target="_blank">JEP 389:外来链接器 API(孵化器)</a></li>
</ul>
<h2 id="11jep-390基于价值的类的警告">11。JEP 390:基于价值的类的警告</h2>
<p>如果我们同步<a href="http://web.archive.org/web/20221225035521/https://download.java.net/java/early_access/jdk16/docs/api/java.base/java/lang/doc-files/ValueBased.html" target="_blank">基于值的类</a>的实例,这个 JEP 提供了一个新的警告;也不赞成移除原始包装类(基于值的)构造函数。</p>
<p>11.1 如何识别基于价值的类?<br>
注释<code>@jdk.internal.ValueBased</code>告诉我们一个类是否是基于值的类。回顾下面的两个类,我们可以看出原始包装类<code>Double</code>和<code>Optional</code>都是基于值的类。</p>
<p>Optional.java</p>
<pre><code class="language-java"> package java.util;
@jdk.internal.ValueBased
public final class Optional<T> {
//...
}
</code></pre>
<p>Double.java</p>
<pre><code class="language-java"> package java.lang;
@jdk.internal.ValueBased
public final class Double extends Number
implements Comparable<Double>, Constable, ConstantDesc {
//...
}
</code></pre>
<p><strong>注</strong><br>
参考 <a href="http://web.archive.org/web/20221225035521/https://openjdk.java.net/jeps/390" target="_blank">JEP 390:基于价值类的警告</a>了解基于价值类的完整列表。</p>
<p>11.2 下面的例子尝试<code>synchronized</code>一个基于值的类。</p>
<p>JEP390.java</p>
<pre><code class="language-java"> public class JEP390 {
public static void main(String[] args) {
Double d = 20.0;
synchronized (d) {} // javac warning & HotSpot warning
}
}
</code></pre>
<p>编译上面的代码,点击新的警告。</p>
<p>Terminal</p>
<pre><code class="language-java"> $ javac JEP390.java
JEP390.java:7: warning: [synchronization] attempt to synchronize on an instance of a value-based class
synchronized (d) {} // javac warning & HotSpot warning
^
1 warning
</code></pre>
<p>11.3 Java 9 弃用了原始包装类(基于值的)构造函数,现已标记为移除。</p>
<p>Double.java</p>
<pre><code class="language-java"> package java.lang;
//...
@jdk.internal.ValueBased
public final class Double extends Number
implements Comparable<Double>, Constable, ConstantDesc {
@Deprecated(since="9", forRemoval = true)
public Double(double value) {
this.value = value;
}
@Deprecated(since="9", forRemoval = true)
public Double(String s) throws NumberFormatException {
value = parseDouble(s);
}
//...
}
</code></pre>
<p><strong>延伸阅读</strong></p>
<ul>
<li><a href="http://web.archive.org/web/20221225035521/https://openjdk.java.net/jeps/390" target="_blank">JEP 390:对基于价值的类的警告</a></li>
</ul>
<p>这是关于未来版本中的值类型吗?</p>
<ul>
<li><a href="http://web.archive.org/web/20221225035521/https://nipafx.dev/java-value-based-classes/" target="_blank">基于值的类</a></li>
<li><a href="http://web.archive.org/web/20221225035521/https://jaxenter.com/java-value-type-163446.html" target="_blank">Java 中的新值类型</a></li>
</ul>
<h2 id="12jep-392包装工具">12。JEP 392:包装工具</h2>
<p>JEP 将<code>jpackage</code>工具从<code>jdk.incubator.jpackage</code>移至<code>jdk.jpackage</code>,并成为 Java 16 中的标准或产品特性。这个<code>jpackage</code>是一个打包工具,用于将 Java 应用打包成特定于平台的包,例如:</p>
<ul>
<li>Linux: deb 和 rpm</li>
<li>macOS: pkg 和 dmg</li>
<li>Windows: msi 和 exe</li>
</ul>
<p><em>历史</em></p>
<ul>
<li>Java 14 <a href="http://web.archive.org/web/20221225035521/https://openjdk.java.net/jeps/343" target="_blank">JEP 343</a> 引入了一个<code>jpackage</code>孵化工具,它在 Java 15 中仍然是一个孵化工具。</li>
</ul>
<p>12.1 下面的例子展示了在 Linux 系统(Ubuntu)中使用<code>jpackage</code>将一个简单的 Java Swing 程序打包成<code>deb</code>格式。</p>
<p>一个简单的 Java Swing 程序来显示 hello world。</p>
<p>JEP392.java</p>
<pre><code class="language-java"> import javax.swing.*;
import java.awt.*;
public class JEP392 {
public static void main(String[] args) {
JFrame frame = new JFrame("Hello World Java Swing");
// display frame site
frame.setMinimumSize(new Dimension(800, 600));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// center the JLabel
JLabel lblText = new JLabel("Hello World!", SwingConstants.CENTER);
// add JLabel to JFrame
frame.getContentPane().add(lblText);
// display it
frame.pack();
frame.setVisible(true);
}
}
</code></pre>
<p>创建一个<code>jar</code>文件并将它<code>jpackage</code>到一个特定于平台的包中;因为这是在 Ubuntu 系统中测试的,所以它会创建一个<code>.deb</code>文件。</p>
<p>Terminal</p>
<pre><code class="language-java"> # compile
$ javac JEP392.java
# create a jar file
$ jar cvf hello.jar JEP392.class
# package the jar file into platform-specific package
$ /opt/jdk-16/bin/jpackage -i . -n JEP392 --main-jar hello.jar --main-class JEP392
# The jpackage created this jep392_1.0-1_amd64.deb
$ ls -lsah
4.0K -rw-rw-r-- 1 mkyong mkyong 994 Mac 15 13:52 hello.jar
30M -rw-r--r-- 1 mkyong mkyong 30M Mac 15 14:01 jep392_1.0-1_amd64.deb
</code></pre>
<p><a href="http://web.archive.org/web/20221225035521/https://unix.stackexchange.com/questions/159094/how-to-install-a-deb-file-by-dpkg-i-or-by-apt" target="_blank">安装。deb 文件</a>;</p>
<p>Terminal</p>
<pre><code class="language-java"> sudo dpkg -i jep392_1.0-1_amd64.deb
Selecting previously unselected package jep392.
(Reading database ... 225576 files and directories currently installed.)
Preparing to unpack jep392_1.0-1_amd64.deb ...
Unpacking jep392 (1.0-1) ...
Setting up jep392 (1.0-1) ...
$ ls -lsah /opt/jep392/bin/JEP392
1.6M -rwxr-xr-x 1 root root 1.6M Mac 15 14:01 /opt/jep392/bin/JEP392
</code></pre>
<p>上的默认安装目录</p>
<ul>
<li>Linux 是<code>/opt</code></li>
<li>macOS 是<code>/Applications</code></li>
<li>Windows 是<code>C:\Program Files\</code></li>
</ul>
<p><em>P.S 这可以通过<code>jpackage --install-dir</code>选项覆盖。</em></p>
<p>运行已安装的 Java Swing 程序。</p>
<p>Terminal</p>
<pre><code class="language-java"> $ /opt/jep392/bin/JEP392
</code></pre>
<p>输出</p>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/4c423581bc484eb8fd4e68d94cf270f4.png" alt="hello world swing" loading="lazy"></p>
<p><code>jpackage -h</code>永远是你最好的朋友:</p>
<p>Terminal</p>
<pre><code class="language-java"> $ /opt/jdk-16/bin/jpackage -h
Usage: jpackage <options>
Sample usages:
--------------
Generate an application package suitable for the host system:
For a modular application:
jpackage -n name -p modulePath -m moduleName/className
For a non-modular application:
jpackage -i inputDir -n name \
--main-class className --main-jar myJar.jar
From a pre-built application image:
jpackage -n name --app-image appImageDir
Generate an application image:
For a modular application:
jpackage --type app-image -n name -p modulePath \
-m moduleName/className
For a non-modular application:
jpackage --type app-image -i inputDir -n name \
--main-class className --main-jar myJar.jar
To provide your own options to jlink, run jlink separately:
jlink --output appRuntimeImage -p modulePath -m moduleName \
--no-header-files [<additional jlink options>...]
jpackage --type app-image -n name \
-m moduleName/className --runtime-image appRuntimeImage
Generate a Java runtime package:
jpackage -n name --runtime-image <runtime-image>
//...
</code></pre>
<p><strong>延伸阅读</strong></p>
<ul>
<li><a href="http://web.archive.org/web/20221225035521/https://openjdk.java.net/jeps/392" target="_blank">JEP 392:包装工具</a></li>
</ul>
<h2 id="13jep-393外部内存访问-api第三个孵化器">13。JEP 393:外部内存访问 API(第三个孵化器)</h2>
<p>外来内存访问 API 允许 Java API 访问 Java 堆外的外来内存,如 <a href="http://web.archive.org/web/20221225035521/https://github.com/dustin/java-memcached-client" target="_blank">memcached</a> 、 <a href="http://web.archive.org/web/20221225035521/https://lucene.apache.org/" target="_blank">Lucene</a> 等。</p>
<p>这个 JEP 更新了外部内存访问 API,并保留了<a href="http://web.archive.org/web/20221225035521/https://openjdk.java.net/jeps/11" target="_blank">孵化器模块</a>。</p>
<p><em>历史</em></p>
<ul>
<li>Java 14 <a href="http://web.archive.org/web/20221225035521/https://openjdk.java.net/jeps/370" target="_blank">JEP 370</a> 引入了外来内存访问 API(孵化器)。</li>
<li>Java 15 <a href="http://web.archive.org/web/20221225035521/https://openjdk.java.net/jeps/383" target="_blank">JEP 383</a> 引入了外来内存访问 API(第二孵化器)。</li>
<li>Java 16 <a href="http://web.archive.org/web/20221225035521/https://openjdk.java.net/jeps/389" target="_blank">JEP 389</a> 引入了国外的链接器 API(孵化器)。</li>
<li>Java 16 <a href="http://web.archive.org/web/20221225035521/https://openjdk.java.net/jeps/393" target="_blank">JEP 393</a> 引入了外来内存访问 API(第三孵化器)。</li>
<li>Java 17 <a href="http://web.archive.org/web/20221225035521/https://openjdk.java.net/jeps/412" target="_blank">JEP 412</a> 引入了外来函数&内存 API(孵化器)。</li>
</ul>
<p><strong>延伸阅读</strong></p>
<ul>
<li><a href="http://web.archive.org/web/20221225035521/https://openjdk.java.net/jeps/393" target="_blank">JEP 393:外来存储器访问 API(第三孵化器)</a></li>
</ul>
<h2 id="14jep-394实例的模式匹配">14。JEP 394:实例的模式匹配</h2>
<p><code>instanceof</code>的模式匹配是 Java 16 中的标准或产品特性。</p>
<p>在模式匹配之前,我们检查对象的类型并手动转换为变量。</p>
<pre><code class="language-java"> if (obj instanceof String) {
String s = (String) obj; // cast
}
</code></pre>
<p>现在我们可以检查对象的类型并自动转换它</p>
<pre><code class="language-java"> if (obj instanceof String s) {
//... s is a string
}
</code></pre>
<p>例如,下面是一个常见的检查和强制转换示例。</p>
<pre><code class="language-java"> if (obj instanceof String) {
String s = (String) obj;
if (s.length() > 5) {
if (s.equalsIgnoreCase("java16")) {
//...
}
}
}
</code></pre>
<p>并且,我们可以使用新的<code>instanceof</code>重构上面的代码。</p>
<pre><code class="language-java"> if (obj instanceof String s && s.length() > 5) {
if (s.equalsIgnoreCase("java16")) {
//...
}
}
</code></pre>
<p><em>历史</em></p>
<ul>
<li>Java 14 <a href="http://web.archive.org/web/20221225035521/https://openjdk.java.net/jeps/305" target="_blank">JEP 305</a> ,首次预告。</li>
<li>Java 15 <a href="http://web.archive.org/web/20221225035521/https://openjdk.java.net/jeps/375" target="_blank">JEP 375</a> ,第二次预告。</li>
<li>Java 16,标准特性。</li>
</ul>
<p><strong>延伸阅读</strong></p>
<ul>
<li><a href="http://web.archive.org/web/20221225035521/https://openjdk.java.net/jeps/394" target="_blank">JEP 394:实例的模式匹配</a></li>
<li><a href="http://web.archive.org/web/20221225035521/https://mkyong.com/java/what-is-new-in-java-17/#jep-406-pattern-matching-for-switch-preview" target="_blank">Java 17 JEP 406:开关的模式匹配(预览)</a></li>
</ul>
<h2 id="15jep-395记录">15。JEP 395:记录</h2>
<p><code>record</code>最终确定并成为标准特征。</p>
<p>JEP395.java</p>
<pre><code class="language-java"> package com.mkyong.java16.jep395;
public class JEP395 {
record Point(int x, int y) { }
public static void main(String[] args) {
Point p1 = new Point(10, 20);
System.out.println(p1.x()); // 10
System.out.println(p1.y()); // 20
Point p2 = new Point(11, 22);
System.out.println(p2.x()); // 11
System.out.println(p2.y()); // 22
Point p3 = new Point(10, 20);
System.out.println(p3.x()); // 10
System.out.println(p3.y()); // 20
System.out.println(p1.hashCode()); // 330
System.out.println(p2.hashCode()); // 363
System.out.println(p3.hashCode()); // 330
System.out.println(p1.equals(p2)); // false
System.out.println(p1.equals(p3)); // true
System.out.println(p2.equals(p3)); // false
}
}
</code></pre>
<p><em>历史</em></p>
<ul>
<li>Java 14 <a href="http://web.archive.org/web/20221225035521/https://openjdk.java.net/jeps/359" target="_blank">JEP 359</a> ,首次预告。</li>
<li>Java 15 <a href="http://web.archive.org/web/20221225035521/https://openjdk.java.net/jeps/384" target="_blank">JEP 384</a> ,第二次预告。</li>
<li>Java 16,标准特性。</li>
</ul>
<p><strong>延伸阅读</strong></p>
<ul>
<li><a href="http://web.archive.org/web/20221225035521/https://openjdk.java.net/jeps/395" target="_blank">JEP 395:记录</a></li>
</ul>
<h2 id="16jep-396默认强封装-jdk-内部构件">16。JEP 396:默认强封装 JDK 内部构件</h2>
<p>Java 9 <a href="http://web.archive.org/web/20221225035521/https://openjdk.java.net/jeps/261#Relaxed-strong-encapsulation" target="_blank">JEP 261</a> 引入了<code>--illegal-access</code>选项来控制对内部 API 的访问,并打包成 JDK。</p>
<p>该 JEP 将<code>--illegal-access</code>选项的默认模式从允许改为拒绝。随着这一改变,JDK 的内部包和 API(除了<a href="http://web.archive.org/web/20221225035521/https://openjdk.java.net/jeps/260#Description" target="_blank">关键内部 API</a>)将不再默认打开。</p>
<p>这个 JEP 的动机是阻止第三方库、框架和工具使用 JDK 的内部 API 和包。</p>
<p><strong>延伸阅读</strong></p>
<ul>
<li><a href="http://web.archive.org/web/20221225035521/https://openjdk.java.net/jeps/396" target="_blank">JEP 396:默认强封装 JDK 内部构件</a></li>
<li>Java 17 JEP 403:强封装 JDK 内部</li>
</ul>
<h2 id="17jep-397密封类第二次预览">17。JEP 397:密封类(第二次预览)</h2>
<p>Java 15 <a href="http://web.archive.org/web/20221225035521/https://openjdk.java.net/jeps/360" target="_blank">JEP 360</a> 引入了<a href="http://web.archive.org/web/20221225035521/https://cr.openjdk.java.net/~briangoetz/amber/datum.html" target="_blank">密封类和接口</a>来限制哪个类可以扩展或实现它们。这个 JEP 是第二个预览版,有一些改进。</p>
<p><strong>延伸阅读</strong></p>
<ul>
<li><a href="http://web.archive.org/web/20221225035521/https://openjdk.java.net/jeps/397" target="_blank">JEP 397:密封类(第二次预览)</a></li>
</ul>
<p>这个密封类是 Java 17 中的一个标准特性。</p>
<h2 id="下载源代码-14">下载源代码</h2>
<p>$ git 克隆<a href="http://web.archive.org/web/20221225035521/https://github.com/mkyong/core-java" target="_blank">https://github.com/mkyong/core-java</a></p>
<p>$ cd java-16</p>
<h2 id="参考文献-5">参考文献</h2>
<ul>
<li><a href="http://web.archive.org/web/20221225035521/https://openjdk.java.net/projects/jdk/16/" target="_blank">OpenJDK 16 项目</a></li>
<li><a href="http://web.archive.org/web/20221225035521/https://github.com/openjdk/" target="_blank">OpenJDK 源代码</a></li>
<li><a href="http://web.archive.org/web/20221225035521/https://en.wikipedia.org/wiki/Automatic_vectorization" target="_blank">维基百科–自动矢量化</a></li>
<li><a href="http://web.archive.org/web/20221225035521/http://daniel-strecker.com/blog/2020-01-14_auto_vectorization_in_java/" target="_blank">Java 中的自动矢量化</a></li>
<li><a href="http://web.archive.org/web/20221225035521/https://en.wikipedia.org/wiki/Vector_processor" target="_blank">矢量处理器</a></li>
<li><a href="http://web.archive.org/web/20221225035521/https://en.wikipedia.org/wiki/Scalar_processor" target="_blank">标量处理器</a></li>
<li><a href="http://web.archive.org/web/20221225035521/https://en.wikipedia.org/wiki/C%2B%2B14" target="_blank">C++14 的语言特性</a></li>
<li><a href="http://web.archive.org/web/20221225035521/https://en.wikipedia.org/wiki/Unix_domain_socket" target="_blank">维基百科–Unix 域套接字</a></li>
<li><a href="http://web.archive.org/web/20221225035521/https://blog.jetbrains.com/idea/2021/01/intellij-idea-2021-1-eap-1/" target="_blank">IntelliJ IDEA 2021.1 EAP 1:支持 Java 16</a></li>
<li><a href="http://web.archive.org/web/20221225035521/https://wiki.openjdk.java.net/display/HotSpot/Metaspace" target="_blank">open JDK–什么是元空间</a></li>
<li><a href="http://web.archive.org/web/20221225035521/https://download.java.net/java/early_access/jdk16/docs/api/java.base/java/lang/doc-files/ValueBased.html" target="_blank">基于值的类</a></li>
<li>【Java 的数据类和密封类型</li>
<li>【Java 的模式匹配</li>
<li><a href="http://web.archive.org/web/20221225035521/https://wiki.openjdk.java.net/display/zgc/Main" target="_blank">open JDK Wiki–ZCG</a></li>
<li><a href="http://web.archive.org/web/20221225035521/https://en.wikipedia.org/wiki/Java_version_history" target="_blank">Java 版本历史</a></li>
</ul>
<input type="hidden" id="mkyong-current-postId" value="16646">
<h1 id="java-17-的新特性是什么">Java 17 的新特性是什么</h1>
<blockquote>
<p>原文:<a href="http://web.archive.org/web/20230101150211/https://mkyong.com/java/what-is-new-in-java-17/" target="_blank">http://web.archive.org/web/20230101150211/https://mkyong.com/java/what-is-new-in-java-17/</a></p>
</blockquote>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/b7c5f15a0b521dfc9999c9cbddd44eb7.png" alt="Java 17 logo" loading="lazy"></p>
<p><a href="http://web.archive.org/web/20221230032425/https://openjdk.java.net/projects/jdk/17/" target="_blank">Java 17</a> 是一个长期支持(LTS)版本,于 2021 年 9 月 14 日全面上市,<a href="http://web.archive.org/web/20221230032425/https://jdk.java.net/17/" target="_blank">点击此处</a>下载 Java 17。</p>
<p>Java 17 有 14 个 JEP 项目。</p>
<ul>
<li><a href="#jep-306-restore-always-strict-floating-point-semantics">1。JEP 306:恢复总是严格的浮点语义</a></li>
<li><a href="#jep-356-enhanced-pseudo-random-number-generators">2。JEP 356:增强型伪随机数发生器</a></li>
<li><a href="#jep-382-new-macos-rendering-pipeline">3。JEP 382:新的 macOS 渲染管道</a></li>
<li><a href="#jep-391-macosaarch64-port">4。JEP 391: macOS/AArch64 端口</a></li>
<li><a href="#jep-398-deprecate-the-applet-api-for-removal">5。JEP 398:反对删除小程序 API</a></li>
<li><a href="#jep-403-strongly-encapsulate-jdk-internals">6。JEP 403:强力封装 JDK 内部构件</a></li>
<li>7 .<a href="#jep-406-pattern-matching-for-switch-preview">。JEP 406:开关模式匹配(预览)</a>
<ul>
<li><a href="#ifelse-chain">7.1 如果…否则链</a></li>
<li><a href="#pattern-matching-and-null">7.2 模式匹配和空值</a></li>
<li><a href="#refining-patterns-in-switch">7.3 开关中的细化模式</a></li>
</ul>
</li>
<li><a href="#jep-407-remove-rmi-activation">8。JEP 407:移除 RMI 激活</a></li>
<li><a href="#jep-409-sealed-classes">9。JEP 409:密封类</a></li>
<li>10。JEP 410:移除实验性的 AOT 和 JIT 编译器</li>
<li><a href="#jep-411-deprecate-the-security-manager-for-removal">11。JEP 411:反对移除安全管理器</a></li>
<li><a href="#jep-412-foreign-function-memory-api-incubator">12。JEP 412:外来函数&内存 API(孵化器)</a></li>
<li>13。JEP 414: Vector API(第二个孵化器)</li>
<li><a href="#jep-415-context-specific-deserialization-filters">14。JEP 415:特定于上下文的反序列化过滤器</a></li>
<li><a href="#download-source-code">下载源代码</a></li>
<li><a href="#references">参考文献</a></li>
</ul>
<p><em>Java 17 开发者特性。</em><br>
伪随机数发生器,用于切换的模式匹配(预览),密封类(标准特性),外来函数&内存 API(孵化器),动态反序列化过滤器。</p>
<h2 id="1jep-306恢复总是严格的浮点语义">1。JEP 306:恢复总是严格的浮点语义</h2>
<p>这个 JEP 用于数字敏感的程序,主要用于科学目的;它再次使默认浮点运算变得严格或<code>Strictfp</code>,确保每个平台上的浮点计算结果相同。</p>
<p><em>简史</em></p>
<ol>
<li>在 Java 1.2 之前,所有的浮点计算都是严格的;它还会导致基于 x87 的硬件过热。</li>
<li>从 Java 1.2 开始,我们需要关键字<code>strictfp</code>来启用严格的浮点计算。默认的浮点计算从严格的浮点计算更改为略微不同的浮点计算(避免过热问题)。</li>
<li>现在,由于 Intel 和 AMD 都支持 <a href="http://web.archive.org/web/20221230032425/https://en.wikipedia.org/wiki/SSE2" target="_blank">SSE2(流 SIMD 扩展 2)</a> 扩展,这可以支持严格的 JVM 浮点操作而没有过热,所以,以前(Java 1.2 之前)基于 x87 的硬件上的过热问题在现在的硬件中是不敬的。</li>
<li>Java 17 将 Java 1.2 之前的严格浮点计算恢复为默认,这意味着关键字<code>strictfp</code>现在是可选的。</li>
</ol>
<p><strong>延伸阅读</strong></p>
<ul>
<li>JEP 306:恢复总是严格的浮点语义</li>
<li><a href="http://web.archive.org/web/20221230032425/https://en.wikipedia.org/wiki/Strictfp" target="_blank">维基百科–strictfp</a></li>
</ul>
<h2 id="2jep-356增强型伪随机数发生器">2。JEP 356:增强型伪随机数发生器</h2>
<p>这个 JEP 引入了一个名为<code>RandomGenerator</code>的新接口,使未来的<a href="http://web.archive.org/web/20221230032425/https://en.wikipedia.org/wiki/Pseudorandom_number_generator" target="_blank">伪随机数发生器(PRNG)</a> 算法更容易实现或使用。</p>
<p>RandomGenerator.java</p>
<pre><code class="language-java"> package java.util.random;
public interface RandomGenerator {
//...
}
</code></pre>
<p>下面的例子使用新的 Java 17 <code>RandomGeneratorFactory</code>来获得著名的<code>Xoshiro256PlusPlus</code> PRNG 算法,以生成特定范围(0–10)内的随机整数。</p>
<p>JEP356.java</p>
<pre><code class="language-java"> package com.mkyong.java17.jep356;
import java.util.random.RandomGenerator;
import java.util.random.RandomGeneratorFactory;
public class JEP356 {
public static void main(String[] args) {
// legacy
// RandomGeneratorFactory.of("Random").create(42);
// default L32X64MixRandom
// RandomGenerator randomGenerator = RandomGeneratorFactory.getDefault().create();
// Passing the same seed to random, and then calling it will give you the same set of numbers
// for example, seed = 999
RandomGenerator randomGenerator = RandomGeneratorFactory.of("Xoshiro256PlusPlus").create(999);
System.out.println(randomGenerator.getClass());
int counter = 0;
while(counter<=10){
// 0-10
int result = randomGenerator.nextInt(11);
System.out.println(result);
counter++;
}
}
}
</code></pre>
<p>输出</p>
<p>Terminal</p>
<pre><code class="language-java"> class jdk.random.Xoshiro256PlusPlus
4
6
9
5
7
6
5
0
6
10
4
</code></pre>
<p>下面的代码生成了所有的 Java 17 PRNG 算法。</p>
<pre><code class="language-java"> RandomGeneratorFactory.all()
.map(fac -> fac.group()+ " : " +fac.name())
.sorted()
.forEach(System.out::println);
</code></pre>
<p>输出</p>
<p>Terminal</p>
<pre><code class="language-java"> LXM : L128X1024MixRandom
LXM : L128X128MixRandom
LXM : L128X256MixRandom
LXM : L32X64MixRandom
LXM : L64X1024MixRandom
LXM : L64X128MixRandom
LXM : L64X128StarStarRandom
LXM : L64X256MixRandom
Legacy : Random
Legacy : SecureRandom
Legacy : SplittableRandom
Xoroshiro : Xoroshiro128PlusPlus
Xoshiro : Xoshiro256PlusPlus
</code></pre>
<p>Java 17 还重构了传统的随机类,如<code>java.util.Random</code>、<code>SplittableRandom</code>和<code>SecureRandom</code>,以扩展新的<code>RandomGenerator</code>接口。</p>
<p><strong>延伸阅读</strong></p>
<ul>
<li><a href="http://web.archive.org/web/20221230032425/https://openjdk.java.net/jeps/356" target="_blank">JEP 356:增强型伪随机数发生器</a></li>
<li><a href="http://web.archive.org/web/20221230032425/https://mbien.dev/blog/entry/enhanced-pseudo-random-number-generators" target="_blank">Java 17 的增强伪随机数生成器</a></li>
</ul>
<h2 id="3jep-382新的-macos-渲染管道">3。JEP 382:新的 macOS 渲染管道</h2>
<p>苹果在 <a href="http://web.archive.org/web/20221230032425/https://developer.apple.com/documentation/macos-release-notes/macos-mojave-10_14-release-notes#Open-GL-and-Open-CL" target="_blank">macOS 10.14 版本</a>(2018 年 9 月)中弃用了 OpenGL 渲染库,转而支持新的<a href="http://web.archive.org/web/20221230032425/https://developer.apple.com/metal/" target="_blank">金属框架</a>以获得更好的性能。</p>
<p>这款 JEP 将 macOS 的 Java 2D(类似 Swing GUI)内部渲染管道从苹果 OpenGL API 改为苹果 Metal API 这是一种内在的变化;没有新的 Java 2D API,也没有改变任何现有的 API。</p>
<p><strong>延伸阅读</strong></p>
<ul>
<li><a href="http://web.archive.org/web/20221230032425/https://openjdk.java.net/jeps/382" target="_blank">JEP 382:新的 macOS 渲染管道</a></li>
</ul>
<h2 id="4jep-391-macosaarch64-端口">4。JEP 391: macOS/AArch64 端口</h2>
<p>苹果有一个长期的计划,将它的 Mac 从 x64 过渡到 AArch64(例如,苹果 M1 处理器)。</p>
<p>这个 JEP 港 JDK 运行在 macOS 上的 AArch64 平台。</p>
<p><strong>延伸阅读</strong></p>
<ul>
<li><a href="http://web.archive.org/web/20221230032425/https://openjdk.java.net/jeps/391" target="_blank">JEP 391: macOS/AArch64 端口</a></li>
</ul>
<h2 id="5jep-398反对删除小程序-api">5。JEP 398:反对删除小程序 API</h2>
<p><a href="http://web.archive.org/web/20221230032425/https://en.wikipedia.org/wiki/Java_applet" target="_blank">Java Applet API</a> 是不相关的,因为大多数网络浏览器已经移除了对 Java 浏览器插件的支持。</p>
<p>Java 9 弃用了 Applet API。</p>
<pre><code class="language-java"> @Deprecated(since = "9")
public class Applet extends Panel {
//...
}
</code></pre>
<p>此 JEP 将 Applet API 标记为删除。</p>
<pre><code class="language-java"> @Deprecated(since = "9", forRemoval = true)
@SuppressWarnings("removal")
public class Applet extends Panel {
//...
}
</code></pre>
<p><strong>延伸阅读</strong></p>
<ul>
<li><a href="http://web.archive.org/web/20221230032425/https://openjdk.java.net/jeps/398" target="_blank">JEP 398:反对删除小应用程序 API</a></li>
</ul>
<h2 id="6jep-403强力封装-jdk-内部构件">6。JEP 403:强力封装 JDK 内部构件</h2>
<p>许多第三方库、框架和工具正在访问 JDK 的内部 API 和包。Java 16、 <a href="http://web.archive.org/web/20221230032425/https://mkyong.com/java/what-is-new-in-java-16/#jep-396-strongly-encapsulate-jdk-internals-by-default" target="_blank">JEP 396</a> 默认做强封装(我们不允许轻易访问内部 API)。然而,我们仍然可以使用<code>--illegal-access</code>切换到简单封装,仍然可以访问内部 API。</p>
<p>这个 JEP 是上述 Java 16 JEP 396 的继任者,它通过移除<code>--illegal-access</code>选项多了一步,这意味着我们没有办法访问内部 API,除了像<code>sun.misc.Unsafe</code>这样的关键内部 API。</p>
<p>试试 Java 17 中的<code>--illegal-access=warn</code>。</p>
<p>Terminal</p>
<pre><code class="language-java"> java --illegal-access=warn
OpenJDK 64-Bit Server VM warning: Ignoring option --illegal-access=warn; support was removed in 17.0
</code></pre>
<p><strong>延伸阅读</strong></p>
<ul>
<li><a href="http://web.archive.org/web/20221230032425/https://openjdk.java.net/jeps/403" target="_blank">JEP 403:强力封装 JDK 内部构件</a></li>
</ul>
<h2 id="7jep-406开关模式匹配预览">7。JEP 406:开关模式匹配(预览)</h2>
<p>这个 JEP 为<code>switch</code>语句和表达式添加了模式匹配。由于这是一个预览功能,我们需要使用<code>--enable-preview</code>选项来启用它。</p>
<h3 id="71-如果否则链">7.1 如果…否则链</h3>
<p>在 Java 17 之前,我们通常会针对几种可能性使用一系列的<code>if...else</code>测试。</p>
<p>JEP406.java</p>
<pre><code class="language-java"> package com.mkyong.java17.jep406;
public class JEP406 {
public static void main(String[] args) {
System.out.println(formatter("Java 17"));
System.out.println(formatter(17));
}
static String formatter(Object o) {
String formatted = "unknown";
if (o instanceof Integer i) {
formatted = String.format("int %d", i);
} else if (o instanceof Long l) {
formatted = String.format("long %d", l);
} else if (o instanceof Double d) {
formatted = String.format("double %f", d);
} else if (o instanceof String s) {
formatted = String.format("String %s", s);
}
return formatted;
}
}
</code></pre>
<p>在 Java 17 中,我们可以这样重写上面的代码:</p>
<p>JEP406.java</p>
<pre><code class="language-java"> package com.mkyong.java17.jep406;
public class JEP406 {
public static void main(String[] args) {
System.out.println(formatterJava17("Java 17"));
System.out.println(formatterJava17(17));
}
static String formatterJava17(Object o) {
return switch (o) {
case Integer i -> String.format("int %d", i);
case Long l -> String.format("long %d", l);
case Double d -> String.format("double %f", d);
case String s -> String.format("String %s", s);
default -> o.toString();
};
}
}
</code></pre>
<h3 id="72-模式匹配和空值">7.2 模式匹配和空值</h3>
<p>现在我们可以直接测试<code>switch</code>中的<code>null</code>。</p>
<p>旧抄本</p>
<p>JEP406.java</p>
<pre><code class="language-java"> package com.mkyong.java17.jep406;
public class JEP406 {
public static void main(String[] args) {
testString("Java 16"); // Ok
testString("Java 11"); // LTS
testString(""); // Ok
testString(null); // Unknown!
}
static void testString(String s) {
if (s == null) {
System.out.println("Unknown!");
return;
}
switch (s) {
case "Java 11", "Java 17" -> System.out.println("LTS");
default -> System.out.println("Ok");
}
}
}
</code></pre>
<p>新代码。</p>
<p>JEP406.java</p>
<pre><code class="language-java"> package com.mkyong.java17.jep406;
public class JEP406 {
public static void main(String[] args) {
testStringJava17("Java 16"); // Ok
testStringJava17("Java 11"); // LTS
testStringJava17(""); // Ok
testStringJava17(null); // Unknown!
}
static void testStringJava17(String s) {
switch (s) {
case null -> System.out.println("Unknown!");
case "Java 11", "Java 17" -> System.out.println("LTS");
default -> System.out.println("Ok");
}
}
}
</code></pre>
<h3 id="73-开关中的细化模式">7.3 开关中的细化模式</h3>
<p>查看下面的代码片段。为了测试<code>Triangle t</code>和<code>t.calculateArea()</code>,我们需要创建一个额外的<code>if</code>条件。</p>
<pre><code class="language-java"> class Shape {}
class Rectangle extends Shape {}
class Triangle extends Shape {
int calculateArea(){
//...
} }
static void testTriangle(Shape s) {
switch (s) {
case null:
break;
case Triangle t:
if (t.calculateArea() > 100) {
System.out.println("Large triangle");
break;
}else{
System.out.println("Triangle");
}
default:
System.out.println("Unknown!");
}
}
</code></pre>
<p>Java 17 允许所谓的重新定义模式或<code>guarded patterns</code>如下:</p>
<pre><code class="language-java"> static void testTriangle2(Shape s) {
switch (s) {
case null ->
{}
case Triangle t && (t.calculateArea() > 100) ->
System.out.println("Large triangle");
case Triangle t ->
System.out.println("Triangle");
default ->
System.out.println("Unknown!");
}
}
</code></pre>
<p><strong>延伸阅读</strong></p>
<p>欲了解更多示例和解释,请访问此 <a href="http://web.archive.org/web/20221230032425/https://openjdk.java.net/jeps/406" target="_blank">JEP 406:开关模式匹配(预览)</a></p>
<h2 id="8jep-407移除-rmi-激活">8。JEP 407:移除 RMI 激活</h2>
<p>Java 15, <a href="http://web.archive.org/web/20221230032425/https://mkyong.com/java/what-is-new-in-java-15/#jep-385deprecate-rmi-activation-for-removal" target="_blank">JEP385</a> 不赞成删除 <a href="http://web.archive.org/web/20221230032425/https://docs.oracle.com/en/java/javase/14/docs/specs/rmi/activation.html" target="_blank">RMI 激活</a>。</p>
<p>这个 JEP 移除了 RMI 激活或<code>java.rmi.activation</code>包。</p>
<p><strong>延伸阅读</strong></p>
<ul>
<li><a href="http://web.archive.org/web/20221230032425/https://openjdk.java.net/jeps/407" target="_blank">JEP 407:移除 RMI 激活</a></li>
</ul>
<h2 id="9jep-409密封类">9。JEP 409:密封类</h2>
<p>Java 15、 <a href="#https://mkyong.com/java/what-is-new-in-java-15/#jep-360-sealed-classes-preview">JEP 360</a> 和 Java 16、 <a href="#https://mkyong.com/java/what-is-new-in-java-16/#jep-397-sealed-classes-second-preview">JEP 397</a> 引入了【密封类(<a href="https://Cr" target="_blank">https://Cr</a> . open JDK . Java . net/~ briangoetz/amber/datum . html)作为预览功能。</p>
<p>这个 JEP 最终将密封类作为 Java 17 的标准特性,与 Java 16 没有任何变化。</p>
<p>密封的类和接口控制或限制谁可以成为子类型。</p>
<pre><code class="language-java"> public sealed interface Command
permits LoginCommand, LogoutCommand, PluginCommand{
//...
}
</code></pre>
<p><strong>延伸阅读</strong></p>
<ul>
<li><a href="http://web.archive.org/web/20221230032425/https://openjdk.java.net/jeps/409" target="_blank">JEP 409:密封类</a></li>
</ul>
<h2 id="10jep-410移除实验性的-aot-和-jit-编译器">10。JEP 410:移除实验性的 AOT 和 JIT 编译器</h2>
<p>Java 9, <a href="http://web.archive.org/web/20221230032425/https://openjdk.java.net/jeps/295" target="_blank">JEP 295</a> 引入了超前编译(<code>jaotc</code>工具)作为一个实验特性。后来的 Java 10, <a href="http://web.archive.org/web/20221230032425/https://openjdk.java.net/jeps/317" target="_blank">JEP 317</a> 再次提出它作为实验性的 JIT 编译器。</p>
<p>然而,这个特性自从被引入以来几乎没有什么用处,并且需要大量的工作来维护它,所以这个 JEP 移除了实验性的基于 Java 的<a href="http://web.archive.org/web/20221230032425/https://en.wikipedia.org/wiki/Ahead-of-time_compilation" target="_blank">提前(AOT)</a> 和<a href="http://web.archive.org/web/20221230032425/https://en.wikipedia.org/wiki/Just-in-time_compilation" target="_blank">实时(JIT)</a> 编译器</p>
<p>下列 AOT 软件包、类、工具和代码被删除:</p>
<ul>
<li><code>jdk.aot</code>—jaotc 工具</li>
<li><code>jdk.internal.vm.compiler</code>—Graal 编译器</li>
<li><code>jdk.internal.vm.compiler.management</code> —圣杯的 MBean</li>
<li><code>src/hotspot/share/aot</code> —转储和加载 AOT 代码</li>
<li>由<code>#if INCLUDE_AOT</code>保护的附加代码</li>
</ul>
<p><strong>延伸阅读</strong></p>
<ul>
<li><a href="http://web.archive.org/web/20221230032425/https://openjdk.java.net/jeps/410" target="_blank">JEP 410:移除实验性的 AOT 和 JIT 编译器</a></li>
</ul>
<h2 id="11jep-411反对移除安全管理器">11。JEP 411:反对移除安全管理器</h2>
<p>Java 1.0 引入了<a href="http://web.archive.org/web/20221230032425/https://docs.oracle.com/javase/tutorial/essential/environment/security.html" target="_blank">安全管理器</a>来保护客户端 Java 代码,现在已经无关紧要了。</p>
<p>这个 JEP 反对安全管理器被删除。</p>
<p>SecurityManager.java</p>
<pre><code class="language-java"> package java.lang;
* @since 1.0
* @deprecated The Security Manager is deprecated and subject to removal in a
* future release. There is no replacement for the Security Manager.
* See <a href="https://openjdk.java.net/jeps/411">JEP 411</a> for
* discussion and alternatives.
*/
@Deprecated(since="17", forRemoval=true)
public class SecurityManager {
//...
}
</code></pre>
<p><strong>延伸阅读</strong></p>
<ul>
<li><a href="http://web.archive.org/web/20221230032425/https://openjdk.java.net/jeps/411" target="_blank">JEP 411:反对安全经理被免职</a></li>
</ul>
<h2 id="12jep-412外来函数内存-api孵化器">12。JEP 412:外来函数&内存 API(孵化器)</h2>
<p>这个外来函数和内存 API 允许开发人员访问 JVM 外部的代码(外来函数)、存储在 JVM 外部的数据(堆外数据),以及访问不受 JVM 管理的内存(外来内存)。</p>
<p>这是一个孵化的特征;需要添加<code>--add-modules jdk.incubator.foreign</code>来编译和运行 Java 代码。</p>
<p><em>历史</em></p>
<ul>
<li>Java 14 <a href="http://web.archive.org/web/20221230032425/https://openjdk.java.net/jeps/370" target="_blank">JEP 370</a> 引入了外来内存访问 API(孵化器)。</li>
<li>Java 15 <a href="http://web.archive.org/web/20221230032425/https://openjdk.java.net/jeps/383" target="_blank">JEP 383</a> 引入了外来内存访问 API(第二孵化器)。</li>
<li>Java 16 <a href="http://web.archive.org/web/20221230032425/https://openjdk.java.net/jeps/389" target="_blank">JEP 389</a> 引入了国外的链接器 API(孵化器)。</li>
<li>Java 16 <a href="http://web.archive.org/web/20221230032425/https://openjdk.java.net/jeps/393" target="_blank">JEP 393</a> 引入了外来内存访问 API(第三孵化器)。</li>
<li>Java 17 <a href="http://web.archive.org/web/20221230032425/https://openjdk.java.net/jeps/412" target="_blank">JEP 412</a> 引入了外来函数&内存 API(孵化器)。</li>
</ul>
<p>请参考之前 Java 16 中的<a href="http://web.archive.org/web/20221230032425/https://mkyong.com/java/what-is-new-in-java-16/#jep-389-foreign-linker-api-incubator" target="_blank">外来链接器 API 示例。</a></p>
<p><strong>延伸阅读</strong></p>
<ul>
<li><a href="http://web.archive.org/web/20221230032425/https://openjdk.java.net/jeps/412" target="_blank">JEP 412:外来函数&内存 API(孵化器)</a></li>
</ul>
<h2 id="13jep-414-vector-api第二个孵化器">13。JEP 414: Vector API(第二个孵化器)</h2>
<p>Java 16, <a href="#">JEP 414</a> 引入新的 Vector API 作为<a href="http://web.archive.org/web/20221230032425/https://openjdk.java.net/jeps/11" target="_blank">孵化 API</a> 。</p>
<p>这个 JEP 改进了 Vector API 性能和其他增强功能,比如支持字符操作、将字节向量与布尔数组相互转换等。</p>
<p><strong>延伸阅读</strong></p>
<ul>
<li><a href="http://web.archive.org/web/20221230032425/https://openjdk.java.net/jeps/414" target="_blank">JEP 414:载体 API(第二孵育器)</a></li>
<li><a href="http://web.archive.org/web/20221230032425/https://en.wikipedia.org/wiki/Automatic_vectorization" target="_blank">维基百科–自动矢量化</a></li>
<li><a href="http://web.archive.org/web/20221230032425/http://daniel-strecker.com/blog/2020-01-14_auto_vectorization_in_java/" target="_blank">Java 中的自动矢量化</a></li>
</ul>
<h2 id="14jep-415特定于上下文的反序列化过滤器">14。JEP 415:特定于上下文的反序列化过滤器</h2>
<p>在 Java 中,反序列化不受信任的数据是危险的,阅读<a href="http://web.archive.org/web/20221230032425/https://owasp.org/www-community/vulnerabilities/Deserialization_of_untrusted_data" target="_blank">OWASP-不受信任数据的反序列化</a>和<a href="http://web.archive.org/web/20221230032425/https://cr.openjdk.java.net/~briangoetz/amber/serialization.html" target="_blank">Brian Goetz-走向更好的序列化</a>。</p>
<p>Java 9, <a href="http://web.archive.org/web/20221230032425/https://openjdk.java.net/jeps/290" target="_blank">JEP 290</a> 引入了序列化过滤来帮助防止反序列化漏洞。</p>
<p>14.1 以下示例使用模式创建了一个自定义过滤器。</p>
<p>DdosExample.java</p>
<pre><code class="language-java"> package com.mkyong.java17.jep415;
import java.io.Serializable;
public class DdosExample implements Serializable {
@Override
public String toString() {
return "running ddos...!";
}
}
</code></pre>
<p>JEP290.java</p>
<pre><code class="language-java"> package com.mkyong.java17.jep415;
import java.io.*;
public class JEP290 {
public static void main(String[] args) throws IOException {
byte[] bytes = convertObjectToStream(new DdosExample());
InputStream is = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(is);
// Setting a Custom Filter Using a Pattern
// need full package path
// the maximum number of bytes in the input stream = 1024
// allows classes in com.mkyong.java17.jep415.*
// allows classes in the java.base module
// rejects all other classes !*
ObjectInputFilter filter1 =
ObjectInputFilter.Config.createFilter(
"maxbytes=1024;com.mkyong.java17.jep415.*;java.base/*;!*");
ois.setObjectInputFilter(filter1);
try {
Object obj = ois.readObject();
System.out.println("Read obj: " + obj);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private static byte[] convertObjectToStream(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();
}
}
</code></pre>
<p>输出</p>
<p>Terminal</p>
<pre><code class="language-java"> Read obj: running ddos...!
</code></pre>
<p>下面的例子将拒绝包<code>com.mkyong.java17.jep415.*</code>中的所有类:</p>
<pre><code class="language-java"> byte[] bytes = convertObjectToStream(new DdosExample());
ObjectInputFilter filter1 =
ObjectInputFilter.Config.createFilter(
"!com.mkyong.java17.jep415.*;java.base/*;!*");
</code></pre>
<p>重新运行;这一次,我们将无法反序列化该对象。</p>
<p>Terminal</p>
<pre><code class="language-java"> Exception in thread "main" java.io.InvalidClassException: filter status: REJECTED
at java.base/java.io.ObjectInputStream.filterCheck(ObjectInputStream.java:1412)
</code></pre>
<p>14.2 下面的例子创建了一个反序列化过滤器来拒绝所有扩展了<code>JComponent</code>的类。</p>
<p>JComponentExample.java</p>
<pre><code class="language-java"> package com.mkyong.java17.jep415;
import javax.swing.*;
import java.io.Serializable;
public class JComponentExample extends JComponent implements Serializable {
}
</code></pre>
<p>JEP290_B.java</p>
<pre><code class="language-java"> package com.mkyong.java17.jep415;
import javax.swing.*;
import java.io.*;
public class JEP290_B {
public static void main(String[] args) throws IOException {
byte[] bytes = convertObjectToStream(new JComponentExample());
InputStream is = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(is);
ois.setObjectInputFilter(createObjectFilter());
try {
Object obj = ois.readObject();
System.out.println("Read obj: " + obj);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
// reject all JComponent classes
private static ObjectInputFilter createObjectFilter() {
return filterInfo -> {
Class<?> clazz = filterInfo.serialClass();
if (clazz != null) {
return (JComponent.class.isAssignableFrom(clazz))
? ObjectInputFilter.Status.REJECTED
: ObjectInputFilter.Status.ALLOWED;
}
return ObjectInputFilter.Status.UNDECIDED;
};
}
private static byte[] convertObjectToStream(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();
}
}
</code></pre>
<p>输出</p>
<p>Terminal</p>
<pre><code class="language-java"> Exception in thread "main" java.io.InvalidClassException: filter status: REJECTED
at java.base/java.io.ObjectInputStream.filterCheck(ObjectInputStream.java:1412)
at java.base/java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:2053)
at java.base/java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1907)
at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2209)
at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1742)
at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:514)
at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:472)
at com.mkyong.java17.jep415.JEP290_B.main(JEP290_B.java:17)
</code></pre>
<p>14.3 Java 17 为<code>ObjectInputFilter</code>接口增加了<code>allowFilter</code>和<code>rejectFilter</code>,可以更快地创建反序列化过滤器。</p>
<pre><code class="language-java"> allowFilter(Predicate<Class<?>>, ObjectInputFilter.Status)
rejectFilter(Predicate<Class<?>>, ObjectInputFilter.Status)
</code></pre>
<p>对于上面 14.2 中的例子,现在我们可以像下面这样重构代码:</p>
<pre><code class="language-java"> // Java 9
private static ObjectInputFilter createObjectFilter() {
return filterInfo -> {
Class<?> clazz = filterInfo.serialClass();
if (clazz != null) {
return (JComponent.class.isAssignableFrom(clazz))
? ObjectInputFilter.Status.REJECTED
: ObjectInputFilter.Status.ALLOWED;
}
return ObjectInputFilter.Status.UNDECIDED;
};
}
// Java 17
// reject all JComponent classes
ObjectInputFilter jComponentFilter = ObjectInputFilter.rejectFilter(
JComponent.class::isAssignableFrom,
ObjectInputFilter.Status.UNDECIDED);
ois.setObjectInputFilter(jComponentFilter);
</code></pre>
<p>14.4 回到 Java 17,这个 JEP 415 引入了一个过滤器工厂的概念,一个<code>BinaryOperator</code>,来动态地或上下文相关地选择不同的反序列化过滤器。工厂决定如何组合两个过滤器或更换过滤器。</p>
<p>下面是结合两个反序列化过滤器的 Java 17 过滤器工厂示例。</p>
<p>JEP415_B.java</p>
<pre><code class="language-java"> package com.mkyong.java17.jep415;
import java.io.*;
import java.util.function.BinaryOperator;
public class JEP415_B {
static class PrintFilterFactory implements BinaryOperator<ObjectInputFilter> {
@Override
public ObjectInputFilter apply(
ObjectInputFilter currentFilter, ObjectInputFilter nextFilter) {
System.out.println("Current filter: " + currentFilter);
System.out.println("Requested filter: " + nextFilter);
// Returns a filter that merges the status of a filter and another filter
return ObjectInputFilter.merge(nextFilter, currentFilter);
// some logic and return other filters
// reject all JComponent classes
/*return filterInfo -> {
Class<?> clazz = filterInfo.serialClass();
if (clazz != null) {
if(JComponent.class.isAssignableFrom(clazz)){
return ObjectInputFilter.Status.REJECTED;
}
}
return ObjectInputFilter.Status.ALLOWED;
};*/
}
}
public static void main(String[] args) throws IOException {
// Set a filter factory
PrintFilterFactory filterFactory = new PrintFilterFactory();
ObjectInputFilter.Config.setSerialFilterFactory(filterFactory);
// create a maxdepth and package filter
ObjectInputFilter filter1 =
ObjectInputFilter.Config.createFilter(
"com.mkyong.java17.jep415.*;java.base/*;!*");
ObjectInputFilter.Config.setSerialFilter(filter1);
// Create a filter to allow String.class only
ObjectInputFilter intFilter = ObjectInputFilter.allowFilter(
cl -> cl.equals(String.class), ObjectInputFilter.Status.REJECTED);
// if pass anything other than String.class, hits filter status: REJECTED
//byte[] byteStream =convertObjectToStream(99);
// Create input stream
byte[] byteStream =convertObjectToStream("hello");
InputStream is = new ByteArrayInputStream(byteStream);
ObjectInputStream ois = new ObjectInputStream(is);
ois.setObjectInputFilter(intFilter);
try {
Object obj = ois.readObject();
System.out.println("Read obj: " + obj);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private static byte[] convertObjectToStream(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();
}
}
</code></pre>
<p>输出</p>
<p>Terminal</p>
<pre><code class="language-java"> Current filter: null
Requested filter: com.mkyong.java17.jep415.*;java.base/*;!*
Current filter: com.mkyong.java17.jep415.*;java.base/*;!*
Requested filter: predicate(
com.mkyong.java17.jep415.JEP415_B$$Lambda$22/0x0000000800c01460@15aeb7ab,
ifTrue: ALLOWED, ifFalse:REJECTED)
Read obj: hello
</code></pre>
<p><strong>延伸阅读</strong></p>
<p>请阅读以下链接,了解更多反序列化过滤器示例:</p>
<ul>
<li><a href="http://web.archive.org/web/20221230032425/https://openjdk.java.net/jeps/415" target="_blank">JEP 415:上下文相关的反序列化过滤器</a></li>
<li><a href="http://web.archive.org/web/20221230032425/https://openjdk.java.net/jeps/290" target="_blank">JEP 290:过滤输入的串行化数据</a></li>
<li><a href="http://web.archive.org/web/20221230032425/https://docs.oracle.com/en/java/javase/17/core/serialization-filtering1.html" target="_blank">序列化过滤</a></li>
</ul>
<h2 id="下载源代码-15">下载源代码</h2>
<p>$ git 克隆<a href="http://web.archive.org/web/20221230032425/https://github.com/mkyong/core-java" target="_blank">https://github.com/mkyong/core-java</a></p>
<p>$ cd java-17</p>
<h2 id="参考文献-6">参考文献</h2>
<ul>
<li><a href="http://web.archive.org/web/20221230032425/https://openjdk.java.net/projects/jdk/17/" target="_blank">OpenJDK 17 项目</a></li>
<li><a href="http://web.archive.org/web/20221230032425/https://github.com/openjdk/" target="_blank">OpenJDK 源代码</a></li>
<li><a href="http://web.archive.org/web/20221230032425/https://en.wikipedia.org/wiki/SSE2" target="_blank">维基百科–SSE2(流媒体 SIMD 扩展 2)</a></li>
<li><a href="http://web.archive.org/web/20221230032425/https://en.wikipedia.org/wiki/Strictfp" target="_blank">维基百科–strictfp</a></li>
<li><a href="http://web.archive.org/web/20221230032425/https://en.wikipedia.org/wiki/Pseudorandom_number_generator" target="_blank">维基百科–伪随机数发生器(PRNG)</a></li>
<li><a href="http://web.archive.org/web/20221230032425/https://mbien.dev/blog/entry/enhanced-pseudo-random-number-generators" target="_blank">Java 17 的增强伪随机数生成器</a></li>
<li><a href="http://web.archive.org/web/20221230032425/https://developer.apple.com/metal/" target="_blank">苹果金属框架</a></li>
<li><a href="http://web.archive.org/web/20221230032425/https://en.wikipedia.org/wiki/Apple_M1" target="_blank">维基百科–苹果 M1</a></li>
<li>【Java 的数据类和密封类型</li>
<li><a href="http://web.archive.org/web/20221230032425/https://inside.java/2021/03/12/simpler-serilization-with-records/" target="_blank">更简单的记录序列化</a></li>
<li><a href="http://web.archive.org/web/20221230032425/https://inside.java/2020/07/20/record-serialization/" target="_blank">记录序列化</a></li>
<li><a href="http://web.archive.org/web/20221230032425/http://daniel-strecker.com/blog/2020-01-14_auto_vectorization_in_java/" target="_blank">Java 中的自动矢量化</a></li>
<li><a href="http://web.archive.org/web/20221230032425/https://en.wikipedia.org/wiki/Vector_processor" target="_blank">矢量处理器</a></li>
<li><a href="http://web.archive.org/web/20221230032425/https://en.wikipedia.org/wiki/Scalar_processor" target="_blank">标量处理器</a></li>
<li><a href="http://web.archive.org/web/20221230032425/https://cr.openjdk.java.net/~briangoetz/amber/serialization.html" target="_blank">迈向更好的系列化</a></li>
<li>【Java 的模式匹配</li>
<li><a href="http://web.archive.org/web/20221230032425/https://en.wikipedia.org/wiki/Java_version_history" target="_blank">Java 版本历史</a></li>
<li><a href="http://web.archive.org/web/20221230032425/https://docs.oracle.com/en/java/javase/17/core/serialization-filtering1.html" target="_blank">序列化过滤</a></li>
<li><a href="http://web.archive.org/web/20221230032425/https://owasp.org/www-community/vulnerabilities/Deserialization_of_untrusted_data" target="_blank">OWASP–不可信数据的反序列化</a></li>
<li><a href="http://web.archive.org/web/20221230032425/https://cr.openjdk.java.net/~briangoetz/amber/serialization.html" target="_blank">Brian Goetz——迈向更好的序列化</a></li>
<li><a href="http://web.archive.org/web/20221230032425/https://mkyong.com/java/java-serialization-examples/" target="_blank">Java 序列化和反序列化示例</a></li>
</ul>
<input type="hidden" id="mkyong-current-postId" value="17013">
<h1 id="jsf-2-taglib-javadoc-在哪里">JSF 2 taglib JavaDoc 在哪里?</h1>
<blockquote>
<p>原文:<a href="http://web.archive.org/web/20230101150211/http://www.mkyong.com/jsf2/where-is-jsf-2-taglib-javadoc/" target="_blank">http://web.archive.org/web/20230101150211/http://www.mkyong.com/jsf2/where-is-jsf-2-taglib-javadoc/</a></p>
</blockquote>
<h2 id="问题-5">问题</h2>
<p>很多时候,你需要知道 JSF HTML 表单标签的细节属性,例如 f:inputText。JSF 2 标签库 JavaDoc 很难找到,甚至 Google 都没有返回任何结果?</p>
<h2 id="解决办法-5">解决办法</h2>
<p>其实 JavaServer(TM) Faces,无论是 1.x 还是 2.x 在这个 <a href="http://web.archive.org/web/20190113095117/https://javaserverfaces.dev.java.net/" target="_blank">JSF 官网</a>都有。不幸的是,JSF 官方网站正在使用“SSL”连接,“T2”https 对所有页面进行访问,导致谷歌爬虫无法索引所有页面。</p>
<p>以下是 3 个最常用的 JavaServer Faces 2.0 JavaDoc,您可能需要将其加入书签以供参考:</p>
<ol>
<li><a href="http://web.archive.org/web/20190113095117/https://javaserverfaces.dev.java.net/nonav/docs/2.0/javadocs/" target="_blank">JSF 2 JavaDoc</a></li>
<li><a href="http://web.archive.org/web/20190113095117/https://javaserverfaces.dev.java.net/nonav/docs/2.0/managed-bean-javadocs/" target="_blank">JSF 2 托管豆注解</a></li>
<li><a href="http://web.archive.org/web/20190113095117/https://javaserverfaces.dev.java.net/nonav/docs/2.0/jsdocs/" target="_blank">JSF 2 JavaScript&Ajax API</a></li>
</ol>
<p><strong>Note</strong><br>
For other JSF 2 JavaDocs, please access this <a href="http://web.archive.org/web/20190113095117/https://javaserverfaces.dev.java.net/users.html" target="_blank">JSF documentation page</a>.<a href="http://web.archive.org/web/20190113095117/http://www.mkyong.com/tag/jsf2/" target="_blank">jsf2</a><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/d3a26adc385b0e1bbb10f577442916f6.png" alt="" loading="lazy"> (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=<mark>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]<a href="h,i%5Bl%5D%5Bq+y%5D(h)+'&'+dt" target="_blank">w+y</a>;}catch(er){i[h]=dt;} } else if(typeof i[c]!</mark>'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','<a href="//web.archive.org/web/20190113095117/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0" target="_blank">//web.archive.org/web/20190113095117/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0</a>')<input type="hidden" id="mkyong-postId" value="7117"></p>
<h1 id="maven-中央存储库在哪里">Maven 中央存储库在哪里?</h1>
<blockquote>
<p>原文:<a href="http://web.archive.org/web/20230101150211/https://www.mkyong.com/maven/where-is-maven-central-repository/" target="_blank">http://web.archive.org/web/20230101150211/https://www.mkyong.com/maven/where-is-maven-central-repository/</a></p>
</blockquote>
<p>默认情况下,Maven 将从 <a href="http://web.archive.org/web/20210814055540/https://www.mkyong.com/maven/where-is-maven-local-repository/" target="_blank">Maven 本地库</a>获取项目依赖关系,如果没有找到,Maven 将从 <a href="http://web.archive.org/web/20210814055540/https://search.maven.org/" target="_blank">Maven 中央库</a>获取</p>
<ol>
<li>Maven 中央存储库 URL-<a href="http://web.archive.org/web/20210814055540/https://repo.maven.apache.org/maven2" target="_blank">https://repo.maven.apache.org/maven2</a></li>
<li>Maven 中央存储库搜索-<a href="http://web.archive.org/web/20210814055540/https://search.maven.org/" target="_blank">https://search.maven.org/</a></li>
</ol>
<p>这是 Maven 中央存储库搜索网站的样子:</p>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/516e51e530c8cc17c24c3320a05ca21b.png" alt="" loading="lazy"></p>
<h2 id="历史">历史</h2>
<p>这是过去 Maven 中央存储库网站的样子:</p>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/569e3ec257368cb7f6017d85945e36ec.png" alt="Maven center repository" title="maven-center-repository" loading="lazy"><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/2f5980f271e021771a1eabcaca5332b3.png" alt="maven center repository search" title="maven-center-repository-search" loading="lazy">Tags : <a href="http://web.archive.org/web/20210814055540/https://mkyong.com/tag/maven/" target="_blank">maven</a> <a href="http://web.archive.org/web/20210814055540/https://mkyong.com/tag/maven-faq/" target="_blank">maven-faq</a> <a href="http://web.archive.org/web/20210814055540/https://mkyong.com/tag/maven-repo/" target="_blank">maven-repo</a><input type="hidden" id="mkyong-current-postId" value="965"></p>
<h1 id="maven-本地存储库在哪里">Maven 本地存储库在哪里?</h1>
<blockquote>
<p>原文:<a href="http://web.archive.org/web/20230101150211/https://www.mkyong.com/maven/where-is-maven-local-repository/" target="_blank">http://web.archive.org/web/20230101150211/https://www.mkyong.com/maven/where-is-maven-local-repository/</a></p>
</blockquote>
<p>默认情况下,Maven 本地存储库默认为<code>${user.home}/.m2/repository</code>文件夹:</p>
<ol>
<li>UNIX/Mac OS X-<code>~/.m2/repository</code></li>
<li>windows—<code>C:\Users\{your-username}\.m2\repository</code></li>
</ol>
<p>当我们编译一个 Maven 项目时,Maven 会将项目的所有依赖项和插件 jar 下载到 Maven 本地存储库中,为下一次编译节省时间。</p>
<h2 id="1查找-maven-本地存储库">1.查找 Maven 本地存储库</h2>
<p>1.1 如果默认<code>.m2</code>找不到,可能有人改变了默认路径。发出以下命令,找出 Maven 本地存储库在哪里:</p>
<pre><code class="language-java"> mvn help:evaluate -Dexpression=settings.localRepository
</code></pre>
<p>1.2 示例:</p>
<p>Terminal</p>
<pre><code class="language-java"> D:\> mvn help:evaluate -Dexpression=settings.localRepository
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------< org.apache.maven:standalone-pom >-------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] --------------------------------[ pom ]---------------------------------
[INFO]
[INFO] --- maven-help-plugin:3.1.0:evaluate (default-cli) @ standalone-pom ---
[INFO] No artifact parameter specified, using 'org.apache.maven:standalone-pom:pom:1' as project.
[INFO]
C:\opt\maven-repository
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.598 s
[INFO] Finished at: 2018-10-24T16:44:18+08:00
[INFO] ------------------------------------------------------------------------
</code></pre>
<p>在上面的输出中,Maven 本地存储库被重新定位到<code>C:\opt\maven-repository</code></p>
<h2 id="2更新-maven-本地存储库">2.更新 Maven 本地存储库</h2>
<p>2.1 找到这个文件<code>{MAVEN_HOME}\conf\settings.xml</code>并更新<code>localRepository</code>。</p>
<p>{MAVEN_HOME}\conf\settings.xml</p>
<pre><code class="language-java"> <settings>
<!-- localRepository
| The path to the local repository maven will use to store artifacts.
|
| Default: ~/.m2/repository
<localRepository>/path/to/local/repo</localRepository>
-->
<localRepository>D:/maven_repo</localRepository>
</code></pre>
<p><strong>Note</strong><br>
Issue <code>mvn -version</code> to find out where is Maven installed.</p>
<p>2.2 保存文件,完成,Maven 本地库现在改为<code>D:/maven_repo</code>。</p>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/88b2f13f8c3ac92fc21c516efe3b930d.png" alt="" loading="lazy"></p>
<h2 id="参考-30">参考</h2>
<ol>
<li><a href="http://web.archive.org/web/20210815110733/https://maven.apache.org/repository/index.html" target="_blank">Maven 中央储存库</a></li>
<li><a href="http://web.archive.org/web/20210815110733/https://maven.apache.org/guides/introduction/introduction-to-repositories.html" target="_blank">存储库简介</a></li>
</ol>
<p>Tags : <a href="http://web.archive.org/web/20210815110733/https://mkyong.com/tag/maven/" target="_blank">maven</a> <a href="http://web.archive.org/web/20210815110733/https://mkyong.com/tag/maven-repository/" target="_blank">maven repository</a> <a href="http://web.archive.org/web/20210815110733/https://mkyong.com/tag/maven-faq/" target="_blank">maven-faq</a> <a href="http://web.archive.org/web/20210815110733/https://mkyong.com/tag/maven-repo/" target="_blank">maven-repo</a><input type="hidden" id="mkyong-current-postId" value="805"></p>
<h1 id="三星-galaxy-s2-usb-驱动哪里下载">三星 Galaxy S2 USB 驱动哪里下载?</h1>
<blockquote>
<p>原文:<a href="http://web.archive.org/web/20230101150211/http://www.mkyong.com/android/where-to-download-samsung-galaxy-s2-usb-driver/" target="_blank">http://web.archive.org/web/20230101150211/http://www.mkyong.com/android/where-to-download-samsung-galaxy-s2-usb-driver/</a></p>
</blockquote>
<p>在像<em>三星 Galaxy S2</em> 这样的真实设备上进行 Android 开发,需要安装<strong>三星 OEM 驱动或者 USB 驱动</strong>。这份 <a href="http://web.archive.org/web/20190220131458/http://developer.android.com/sdk/oem-usb.html" target="_blank">Android OEM 驱动文档</a>会指引你去哪里下载它……但是这个指引并不容易遵循。</p>
<p>其实“<strong>三星 USB 驱动</strong>是包含在名为“<strong>三星 Kies 或者 PC Sync</strong> 的软件中的。下面的指南将告诉您如何以及在哪里获得它。</p>
<p>这个例子使用的是 Windows 7 和三星 Galaxy S2。</p>
<h2 id="1三星手机型号">1.三星手机型号</h2>
<p>大多数三星网站会询问您的手机型号来下载软件。您可以在手机背面找到手机型号,见下图:</p>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/feaae626261ba943513c1ad0de0763ce.png" alt="samsung phone model" title="android-samsung-model" loading="lazy"></p>
<p>上图摘自三星网站。</p>
<p><strong>Note</strong><br>
The phone model number of my Samsung galaxy s2 is “<strong>GT-19100</strong>“. ## 2.三星网站</p>
<p>进入三星官网:<a href="http://web.archive.org/web/20190220131458/http://www.samsung.com/" target="_blank">http://www.samsung.com</a>,会自动重定向到您当地的三星网站。找到“<strong>支持</strong>——>”<strong>下载</strong>,或者类似的东西。</p>
<h2 id="3下载三星-kies-usb-驱动程序">3.下载三星 Kies (USB 驱动程序)</h2>
<p>输入您的手机型号或手机名称,找到合适的“ <strong>Samsung Kies</strong> ”进行下载。见下图:</p>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/1a2c7b620b17a2ec36bc54b2a9162378.png" alt="samsung usb driver download" title="android-samsung-USB-driver" loading="lazy"></p>
<p>获取“ <strong>Samsung Kies</strong> ”并安装在您的 Windows 上,“ <strong>Samsung USB 驱动程序</strong>”将一起安装。</p>
<p>完成了。现在你可以在<em>三星 Galaxy S2</em> 上调试你的安卓应用了。</p>
<p><a href="http://web.archive.org/web/20190220131458/http://www.mkyong.com/tag/android/" target="_blank">android</a> <a href="http://web.archive.org/web/20190220131458/http://www.mkyong.com/tag/samsung/" target="_blank">samsung</a> <a href="http://web.archive.org/web/20190220131458/http://www.mkyong.com/tag/usb/" target="_blank">usb</a></p>
<h1 id="为什么我的项目选择-hibernate">为什么我的项目选择 Hibernate?</h1>
<blockquote>
<p>原文:<a href="http://web.archive.org/web/20230101150211/http://www.mkyong.com/hibernate/why-i-choose-hibernate-for-my-project/" target="_blank">http://web.archive.org/web/20230101150211/http://www.mkyong.com/hibernate/why-i-choose-hibernate-for-my-project/</a></p>
</blockquote>
<p>目前我的公司使用 IBATIS 和纯 SQL 作为数据库持久化机制。我非常喜欢 SQL 查询,尤其是在调优方面,但我不喜欢在 Java 应用程序中编写所有的 SQL 语句,这很容易出现打字错误,这是一项多么愚蠢和繁琐的工作?</p>
<p>最后,我的公司有了一个新项目,这是提出 Hibernate 作为我们新的 java 数据库持久化机制工具的恰当时机。为了说服我的老板接受 Hibernate 作为新项目的考虑因素,我必须强调 Hibernate 的好处和优势。</p>
<h2 id="为什么是-or-映射">为什么是 O/R 映射?</h2>
<h2 id="1生产力">1.生产力</h2>
<p>它帮助开发人员摆脱编写复杂繁琐的 SQL 语句,不再需要 JDBC API 进行结果集或数据处理。它使开发人员更专注于业务逻辑,并提高了项目的生产率。</p>
<h2 id="2可维护性">2.可维护性</h2>
<p>它有助于减少代码行,使系统更容易理解,更强调业务逻辑而不是持久性工作(SQL)。更重要的是,代码更少的系统更容易重构。</p>
<h2 id="3轻便">3.轻便</h2>
<p>它将我们的应用程序从底层 sql 数据库和 SQL 方言中抽象出来。切换到其他 SQL 数据库只需对 Hibernate 配置文件进行少量修改(一次编写/随处运行)。</p>
<blockquote>
<p>老板:嗯,听起来是个有趣的工具。(事实上,我不知道你在说什么)有没有其他的 O/R 映射工具或者数据库持久化机制可以做和 Hibernate 一样的事情?<br>
<strong>我:</strong>是的,老板,它确实存在,但是我需要告诉你,为什么我选择冬眠。</p>
</blockquote>
<h2 id="为什么选择-hibernate-而不是其他">为什么选择 Hibernate 而不是其他?</h2>
<p>Java 中流行的开源持久性框架</p>
<p>1.<strong>冬眠</strong>–【<a href="http://www.hibernate.org/%E3%80%91" target="_blank">http://www.hibernate.org/】</a><br>
2<a href="http://web.archive.org/web/20201109025833/http://www.hibernate.org/" target="_blank">。<strong>EJB 3</strong>–</a><a href="http://web.archive.org/web/20201109025833/http://java.sun.com/products/ejb/index.jsp" target="_blank">http://java.sun.com/products/ejb/index.jsp</a><br>
3。<strong>甲骨文热门链接</strong>–<a href="http://web.archive.org/web/20201109025833/http://www.oracle.com/technology/products/ias/toplink/index.html" target="_blank">http://www . Oracle . com/technology/products/IAS/toplink/index . html</a>–<br>
4 .<strong>卡宴</strong>–<a href="http://web.archive.org/web/20201109025833/http://cayenne.apache.org/" target="_blank">http://cayenne.apache.org/</a><br>
5。<strong>开 JPA【<a href="http://openjpa.apache.org/%E3%80%91%E2%80%93" target="_blank">http://openjpa.apache.org/】–</a><br>
6。伊巴蒂斯</strong>–<a href="http://web.archive.org/web/20201109025833/http://ibatis.apache.org/javadownloads.cgi" target="_blank">http://ibatis.apache.org/javadownloads.cgi</a><br>
7。<strong>JPOX</strong>–<a href="http://web.archive.org/web/20201109025833/http://www.jpox.org/" target="_blank">http://www.jpox.org/</a></p>
<p>我不想单独比较每个 O/R 或非 O/R 持久性机制,这可能需要花费一年多的时间进行研究和实践比较。基于我个人的拙见,我将选择 Hibernate 作为最好的 O/R 映射持久化机制。</p>
<h2 id="选择-hibernate-的原因">选择 Hibernate 的原因</h2>
<h2 id="1生产力可维护性可移植性">1.生产力、可维护性、可移植性</h2>
<p>Hibernate 提供了以上所有的 O/R 优势。(生产力、可维护性、可移植性)。</p>
<h2 id="2免费经济高效">2.免费–经济高效</h2>
<p>Hibernate 是免费和开源的——性价比高</p>
<h2 id="3学习曲线很短">3.学习曲线很短</h2>
<p>因为我们都有使用 Hibernate 的工作经验,而且 Hibernate 是完全面向对象的概念,这将缩短我们的学习曲线。</p>
<h2 id="4代码生成工具">4.代码生成工具</h2>
<p>社区提供的 Hibernate 工具可以帮助开发者快速简单地生成或开发 hibernate 应用程序。(Eclipse 的插件和代码生成工具)</p>
<h2 id="5流行的">5.流行的</h2>
<p>Hibernate 很受欢迎,当我们用 Hibernate 出错时,我们可以很容易地从 Google 找到答案。此外,还有很多关于 Hibernate 的书籍、社区和论坛。</p>
<blockquote>
<p><strong>我:</strong>嗨老板,嗨,嗨,你在听吗?<br>
<strong>老板:</strong>……对,对,我明白你的意思。<br>
<strong>我:</strong> …。(真的吗?)</p>
<p><strong>我:</strong> ……(免费…发货日期…这是老板)谢谢老板…你太棒了!</p>
</blockquote>
<p>这里还有一个使用 Hibernate 的好处是我没有通知老板的。可能是我加的 6 号。</p>
<h2 id="6市场需求">6)市场需求</h2>
<p>Java 市场需要 Hibernate 开发人员,与其他工具相比,Hibernate 开发人员的需求正在稳步增长。Hibernate 的工作经验无疑为我的下一次跳跃增加了优势。你认为我应该通知我的老板吗?🙂</p>
<h2 id="参考-31">参考</h2>
<p>Hibernate tools (Eclipse 插件&代码生成)<br>
<a href="http://web.archive.org/web/20201109025833/http://www.hibernate.org/255.html" target="_blank">http://www.hibernate.org/255.html</a><br>
<a href="http://web.archive.org/web/20201109025833/http://www.hibernate.org/hib_docs/tools/reference/en/html_single/" target="_blank">http://www . hibernate . org/Hib _ docs/tools/reference/en/html _ single/</a></p>
<p>1.Hibernate 是最好的选择吗?<br>
<a href="http://web.archive.org/web/20201109025833/http://java.dzone.com/news/hibernate-best-choice" target="_blank">http://java.dzone.com/news/hibernate-best-choice</a></p>
<p>2.休眠 VS TopLink VS CMP<br>
<a href="http://www.theserverside.com/discussions/thread.tss?thread_id=27037" target="_blank">http://www.theserverside.com/discussions/thread.tss?thread_id=27037</a></p>
<p>3.<a href="http://www.theserverside.com/discussions/thread.tss" target="_blank">http://www.theserverside.com/discussions/thread.tss</a>? hibernate vs EJB 3.0 持久性<br>
<a href="http://web.archive.org/web/20201109025833/http://www.theserverside.com/discussions/thread.tss?thread_id=38800" target="_blank">thread_id=38800</a></p>
<p>4.基于 Hibernate vs . JDO vs . EJB 3 的持久性的优缺点新的 J2EE 应用程序来构建<br>
<a href="http://web.archive.org/web/20201109025833/http://saloon.javaranch.com/cgi-bin/ubb/ultimatebb.cgi?ubb=get_topic&f=78&t=000927" target="_blank">http://saloon.javaranch.com/cgi-bin/ubb/ultimatebb.cgi?ubb = get _ topic&f = 78&t = 000927</a></p>
<p>5.iBATIS 与 Hibernate——是什么原因导致了二者的选择?<br>
<a href="http://web.archive.org/web/20201109025833/http://www.javalobby.org/java/forums/t16496.html" target="_blank">http://www.javalobby.org/java/forums/t16496.html</a></p>
<p>6.Java 中的开源持久性框架<br>
<a href="http://web.archive.org/web/20201109025833/http://java-source.net/open-source/persistence" target="_blank">http://java-source.net/open-source/persistence</a></p>
<p>Tags : <a href="http://web.archive.org/web/20201109025833/https://mkyong.com/tag/hibernate/" target="_blank">hibernate</a> <a href="http://web.archive.org/web/20201109025833/https://mkyong.com/tag/java/" target="_blank">java</a><input type="hidden" id="mkyong-current-postId" value="508"></p>
<h3 id="相关文章">相关文章</h3>
<ul>
<li>
<p><a href="/web/20201109025833/https://www.mkyong.com/java/check-duplicated-value-in-array/" target="_blank">检查数组</a>中的重复值</p>
</li>
<li>
<p><a href="/web/20201109025833/https://www.mkyong.com/java/how-to-install-java-jdk-on-fedora-core-linux/" target="_blank">如何在 fedora core (linux)上安装 Java JDK</a></p>
</li>
<li>
<p><a href="/web/20201109025833/https://www.mkyong.com/java/open-browser-in-java-windows-or-linux/" target="_blank">在 Java windows 或 Linux 中打开浏览器</a></p>
</li>
<li>
<p>【Eclipse IDE 的 Java 反编译器插件</p>
</li>
<li>
<p><a href="/web/20201109025833/https://www.mkyong.com/java/how-to-remove-whitespace-between-string-java/" target="_blank">如何去除字符串- Java 之间的空格</a></p>
</li>
<li>
<p>If 语句小心!!!</p>
</li>
<li>
<p><a href="/web/20201109025833/https://www.mkyong.com/java/javalangunsupportedclassversionerror-bad-version-number-in-class-file/" target="_blank">Java . lang . unsupportedclassversionerror:错误的版本</a></p>
</li>
<li>
<p><a href="/web/20201109025833/https://www.mkyong.com/java/how-to-delete-temporary-file-in-java/" target="_blank">如何在 Java 中删除临时文件</a></p>
</li>
<li>
<p><a href="/web/20201109025833/https://www.mkyong.com/java/java-web-start-jnlp-tutorial-unofficial-guide/" target="_blank">Java Web Start (Jnlp)教程</a></p>
</li>
<li>
<p><a href="/web/20201109025833/https://www.mkyong.com/java/how-to-export-data-to-csv-file-java/" target="_blank">如何将数据导出到 CSV 文件- Java</a></p>
</li>
</ul>
<h1 id="wicket-复选框示例">Wicket 复选框示例</h1>
<blockquote>
<p>原文:<a href="http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/wicket-checkbox-example/" target="_blank">http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/wicket-checkbox-example/</a></p>
</blockquote>
<p>Wicket 示例创建一个复选框,并自动选中该复选框。</p>
<pre><code class="language-java"> //Java
import org.apache.wicket.markup.html.form.CheckBox;
...
final CheckBox chk0 = new CheckBox("checkbox0", Model.of(Boolean.TRUE));
final CheckBox chk1 = new CheckBox("checkbox1", new PropertyModel<Boolean>(this, "checkbox1"));
form.add(address);
//HTML
<input type="checkbox" wicket:id="checkbox0" />
<input type="checkbox" wicket:id="checkbox1" />
</code></pre>
<h2 id="1wicket-复选框示例">1.Wicket 复选框示例</h2>
<p>呈现复选框的不同方式,默认勾选了“chk0”和“chk2”。</p>
<p><strong>Checked by default ?</strong><br>
To checked a checkbox, you just need to assign a “true” to the component. This concept applied to other web application frameworks as well.</p>
<p><em>文件:CheckBoxPage.java</em></p>
<pre><code class="language-java"> package com.mkyong.user;
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.form.CheckBox;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;
public class CheckBoxPage extends WebPage {
private boolean checkbox1 = false; // uncheck
private boolean checkbox2 = true; // checked by default
public CheckBoxPage(final PageParameters parameters) {
add(new FeedbackPanel("feedback"));
final CheckBox chk0 = new CheckBox("checkbox0", Model.of(Boolean.TRUE));
final CheckBox chk1 = new CheckBox("checkbox1",
new PropertyModel<Boolean>(this, "checkbox1"));
final CheckBox chk2 = new CheckBox("checkbox2",
new PropertyModel<Boolean>(this, "checkbox2"));
Form<?> form = new Form<Void>("userForm") {
@Override
protected void onSubmit() {
info("checkbox0 : " + chk0.getModelObject().toString());
info("checkbox1 : " + Boolean.toString(checkbox1));
info("checkbox2 : " + Boolean.toString(checkbox2));
}
};
add(form);
form.add(chk0);
form.add(chk1);
form.add(chk2);
}
}
</code></pre>
<h2 id="2wicket-html-页面">2.Wicket HTML 页面</h2>
<p>呈现 3 个复选框的页面。</p>
<p><em>文件:CheckBoxPage.html</em></p>
<pre><code class="language-java"> <html>
<head>
<style>
label {
background-color: #eee;
padding: 4px;
float:left;
}
.feedbackPanelINFO {
color: green;
}
</style>
</head>
<body>
<h1>Wicket Checkbox Example</h1>
<div wicket:id="feedback"></div>
<form wicket:id="userForm">
<p>
<label>CheckBox 0 :</label>
<input type="checkbox" wicket:id="checkbox0" />
</p>
<p>
<label>CheckBox 1 :</label>
<input type="checkbox" wicket:id="checkbox1" />
</p>
<p>
<label>CheckBox 2 :</label>
<input type="checkbox" wicket:id="checkbox2" />
</p>
<input type="submit" value="Register" />
</form>
</body>
</html>
</code></pre>
<h2 id="3演示">3.演示</h2>
<p>开始并访问—<em><a href="http://localhost:8080/wicket" target="_blank">http://localhost:8080/wicket</a> examples/</em></p>
<p>默认情况下,“chk0”和“chk2”处于选中状态。</p>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/bf9f6960ca8d62160e64d36b41ca583a.png" alt="wicket checkbox" title="wicket-checkbox-example1" loading="lazy"><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/1df3d8bd16fa7f17d69036332303074c.png" alt="wicket checkbox example" title="wicket-checkbox-example2" loading="lazy">Download it – <a href="http://web.archive.org/web/20190306164345/http://www.mkyong.com/wp-content/uploads/2011/05/Wicket-Checkbox-Example.zip" target="_blank">Wicket-Checkbox-Example.zip</a> (7KB)</p>
<h2 id="参考-32">参考</h2>
<ol>
<li><a href="http://web.archive.org/web/20190306164345/http://wicket.apache.org/apidocs/1.4/org/apache/wicket/markup/html/form/CheckBox.html" target="_blank">Wicket 复选框 Javadoc</a></li>
</ol>
<p><a href="http://web.archive.org/web/20190306164345/http://www.mkyong.com/tag/checkbox/" target="_blank">checkbox</a> <a href="http://web.archive.org/web/20190306164345/http://www.mkyong.com/tag/wicket/" target="_blank">wicket</a></p>
<h1 id="wicket-compoundpropertymodel-示例">Wicket CompoundPropertyModel 示例</h1>
<blockquote>
<p>原文:<a href="http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/wicket-compoundpropertymodel-example/" target="_blank">http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/wicket-compoundpropertymodel-example/</a></p>
</blockquote>
<p>在 Wicket 中, <strong>CompoundPropertyModel</strong> 类似于 <a href="http://web.archive.org/web/20190114230013/http://www.mkyong.com/wicket/wicket-propertymodel-example/" target="_blank">PropertyModel</a> ,是将表单组件绑定到对象属性最常用的模型。</p>
<p><strong>Note</strong><br>
For detail, read this <a href="http://web.archive.org/web/20190114230013/https://cwiki.apache.org/WICKET/working-with-wicket-models.html#WorkingwithWicketmodels-CompoundPropertyModels" target="_blank">CompoundPropertyModel article</a></p>
<p>请看下面的例子来展示如何在 Wicket 中使用 CompoundPropertyModel。</p>
<h2 id="1用户级">1.用户级</h2>
<p>一个用户类,有两个属性——“姓名”和“年龄”。</p>
<pre><code class="language-java"> package com.mkyong.user;
import java.io.Serializable;
public class User implements Serializable{
private String name;
private int age;
//setter and getter methods
}
</code></pre>
<h2 id="2compoundpropertymodel-示例">2.CompoundPropertyModel 示例</h2>
<p>使用“ <strong>CompoundPropertyModel</strong> ”将 textbox 组件绑定到“<strong>用户</strong>对象。</p>
<pre><code class="language-java"> package com.mkyong.user;
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.model.CompoundPropertyModel;
public class UserPage extends WebPage {
private User user = new User();
@SuppressWarnings("serial")
public UserPage(final PageParameters parameters) {
add(new FeedbackPanel("feedback"));
final TextField<String> tName = new TextField<String>("name");
final TextField<Integer> tAge = new TextField<Integer>("age");
Form<User> form = new Form<User>("userForm",
new CompoundPropertyModel<User>(user)) {
@Override
protected void onSubmit() {
PageParameters pageParameters = new PageParameters();
pageParameters.add("name", user.getName());
pageParameters.add("age", Integer.toString(user.getAge()));
setResponsePage(SuccessPage.class, pageParameters);
}
};
add(form);
form.add(tName);
form.add(tAge);
}
}
</code></pre>
<p>在这种情况下,</p>
<ol>
<li><code>new TextField<String>("name")</code>将绑定到用户对象,<strong>名称属性</strong></li>
<li><code>new TextField<Integer>("age")</code>绑定到用户对象,<strong>年龄属性</strong></li>
</ol>
<p>Download It – <a href="http://web.archive.org/web/20190114230013/http://www.mkyong.com/wp-content/uploads/2011/05/Wicket-CompoundPropertyModel-Examples.zip" target="_blank">Wicket-CompoundPropertyModel-Examples.zip</a> (8KB) ## 参考</p>
<ol>
<li><a href="http://web.archive.org/web/20190114230013/http://wicket.apache.org/apidocs/1.4/org/apache/wicket/model/CompoundPropertyModel.html" target="_blank">CompoundPropertyModel Javadoc</a></li>
<li><a href="http://web.archive.org/web/20190114230013/https://cwiki.apache.org/WICKET/working-with-wicket-models.html#WorkingwithWicketmodels-CompoundPropertyModels" target="_blank">使用复合属性模型</a></li>
</ol>
<p><a href="http://web.archive.org/web/20190114230013/http://www.mkyong.com/tag/wicket/" target="_blank">wicket</a></p>
<h1 id="wicket-下拉框示例drop-down-choice">Wicket 下拉框示例–drop down choice</h1>
<blockquote>
<p>原文:<a href="http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/wicket-dropdown-box-example-dropdownchoice/" target="_blank">http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/wicket-dropdown-box-example-dropdownchoice/</a></p>
</blockquote>
<p>在 Wicket 中,可以使用“ <strong>DropDownChoice</strong> 来呈现一个下拉框组件。</p>
<pre><code class="language-java"> //Java
import org.apache.wicket.markup.html.form.DropDownChoice;
...
//choices in dropdown box
private static final List<String> SEARCH_ENGINES = Arrays.asList(new String[] {
"Google", "Bing", "Baidu" });
//variable to hold the selected value from dropdown box,
//and also make "Google" is selected by default
private String selected = "Google";
DropDownChoice<String> listSites = new DropDownChoice<String>(
"sites", new PropertyModel<String>(this, "selected"), SEARCH_ENGINES);
//HTML for dropdown box
<select wicket:id="sites"></select>
</code></pre>
<h2 id="1wicket-dropdownchoice-示例">1.Wicket DropDownChoice 示例</h2>
<p>示例通过“<code>DropDownChoice</code>”显示下拉框,并默认一个选定值。</p>
<pre><code class="language-java"> import java.util.Arrays;
import java.util.List;
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.form.DropDownChoice;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.model.PropertyModel;
public class DropDownChoicePage extends WebPage {
private static final List<String> SEARCH_ENGINES = Arrays.asList(new String[] {
"Google", "Bing", "Baidu" });
//make Google selected by default
private String selected = "Google";
public DropDownChoicePage(final PageParameters parameters) {
add(new FeedbackPanel("feedback"));
DropDownChoice<String> listSites = new DropDownChoice<String>(
"sites", new PropertyModel<String>(this, "selected"), SEARCH_ENGINES);
Form<?> form = new Form<Void>("form") {
@Override
protected void onSubmit() {
info("Selected search engine : " + selected);
}
};
add(form);
form.add(listSites);
}
}
</code></pre>
<h2 id="2wicket-html-页面-1">2.Wicket HTML 页面</h2>
<p>用于呈现下拉框的页面。</p>
<pre><code class="language-java"> <html>
<head>
<style>
.feedbackPanelINFO {
color: green;
}
</style>
</head>
<body>
<h1>Wicket DropDownChoice example</h1>
<div wicket:id="feedback"></div>
<form wicket:id="form">
<p>
<label>Select your search engine </label>
<br />
<select wicket:id="sites"></select>
</p>
<input type="submit" value="Display" />
</form>
</body>
</html>
</code></pre>
<h2 id="3演示-1">3.演示</h2>
<p>开始并访问—<em><a href="http://localhost:8080/wicket" target="_blank">http://localhost:8080/wicket</a> examples/</em></p>
<p>默认选择“谷歌”。</p>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/c78973bbfff58fdb8fe1033af6c20216.png" alt="wicket dropdown box" title="wicket-dropdownchoice-example1" loading="lazy"></p>
<p>选择“百度”并点击显示按钮。</p>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/445fe949c1ad3e514286861a8e3e8654.png" alt="wicket dropdownbox example" title="wicket-dropdownchoice-example2" loading="lazy">Download it – <a href="http://web.archive.org/web/20190221190526/http://www.mkyong.com/wp-content/uploads/2011/05/Wicket-DropDownChoice-Examples.zip" target="_blank">Wicket-DropDownChoice-Example.zip</a> (7KB)</p>
<h2 id="参考-33">参考</h2>
<ol>
<li><a href="http://web.archive.org/web/20190221190526/http://wicket.apache.org/apidocs/1.4/org/apache/wicket/markup/html/form/DropDownChoice.html" target="_blank">Wicket drop down choice Javadoc</a></li>
</ol>
<p><a href="http://web.archive.org/web/20190221190526/http://www.mkyong.com/tag/dropdown/" target="_blank">dropdown</a> <a href="http://web.archive.org/web/20190221190526/http://www.mkyong.com/tag/wicket/" target="_blank">wicket</a></p>
<h1 id="wicket-文件上传示例">Wicket 文件上传示例</h1>
<blockquote>
<p>原文:<a href="http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/wicket-file-upload-example/" target="_blank">http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/wicket-file-upload-example/</a></p>
</blockquote>
<p>这个例子展示了如何创建一个 Wicket <strong>FileUploadField</strong> 组件,让用户从本地驱动器选择一个文件并上传到服务器。</p>
<pre><code class="language-java"> //Java
import org.apache.wicket.markup.html.form.upload.FileUploadField;
form.setMultiPart(true);
form.add(fileUpload = new FileUploadField("fileUpload"));
//HTML
<input wicket:id="fileUpload" type="file"/>
</code></pre>
<p>要上传文件,您必须在 Wicket 表单组件中启用“<strong>多部分模式</strong>”。</p>
<h2 id="1文件上传示例">1.文件上传示例</h2>
<p>示例呈现 fileupload 组件,并将<strong>上传的文件大小限制为 10k</strong> 。并且新上传的文件将被保存到预定义的位置。</p>
<pre><code class="language-java"> package com.mkyong.user;
import java.io.File;
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.upload.FileUpload;
import org.apache.wicket.markup.html.form.upload.FileUploadField;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.util.lang.Bytes;
public class FileUploadPage extends WebPage {
private FileUploadField fileUpload;
private String UPLOAD_FOLDER = "C:\\";
public FileUploadPage(final PageParameters parameters) {
add(new FeedbackPanel("feedback"));
Form<?> form = new Form<Void>("form") {
@Override
protected void onSubmit() {
final FileUpload uploadedFile = fileUpload.getFileUpload();
if (uploadedFile != null) {
// write to a new file
File newFile = new File(UPLOAD_FOLDER
+ uploadedFile.getClientFileName());
if (newFile.exists()) {
newFile.delete();
}
try {
newFile.createNewFile();
uploadedFile.writeTo(newFile);
info("saved file: " + uploadedFile.getClientFileName());
} catch (Exception e) {
throw new IllegalStateException("Error");
}
}
}
};
// Enable multipart mode (need for uploads file)
form.setMultiPart(true);
// max upload size, 10k
form.setMaxSize(Bytes.kilobytes(10));
form.add(fileUpload = new FileUploadField("fileUpload"));
add(form);
}
}
</code></pre>
<h2 id="2html-页面">2.HTML 页面</h2>
<p>通过 HTML 输入标签呈现组件。</p>
<pre><code class="language-java"> <html>
<head>
<style>
.feedbackPanelINFO {
color: green;
}
.feedbackPanelERROR {
color: red;
}
</style>
</head>
<body>
<h1>Wicket file upload example</h1>
<div wicket:id="feedback"></div>
<form wicket:id="form">
<p>
<label>Select file :</label>
<input wicket:id="fileUpload" size="40" type="file"/>
<input type="submit" value="Upload"/>
</p>
</form>
</body>
</html>
</code></pre>
<h2 id="3演示-2">3.演示</h2>
<p>开始并访问—<em><a href="http://localhost:8080/wicket" target="_blank">http://localhost:8080/wicket</a> examples/</em></p>
<p>如果文件大小超过 10k,显示错误</p>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/0ef1f9c17bee40a785fe87f466cae6dd.png" alt="wicket file upload error" title="wicket-file-upload-example-error" loading="lazy"></p>
<p>一切正常,显示保存的文件名。</p>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/3da66f23766c818c634c30207230db3f.png" alt="wicket file upload" title="wicket-file-upload-example-done" loading="lazy">Download it – <a href="http://web.archive.org/web/20190310100746/http://www.mkyong.com/wp-content/uploads/2011/05/Wicket-FileUpload-Example.zip" target="_blank">Wicket-FileUpload-Example.zip</a> (7KB)</p>
<h2 id="参考-34">参考</h2>
<ol>
<li><a href="http://web.archive.org/web/20190310100746/http://wicket.apache.org/apidocs/1.4/org/apache/wicket/markup/html/form/upload/FileUpload.html" target="_blank">Wicket 文件上传 Javadoc</a></li>
<li><a href="http://web.archive.org/web/20190310100746/http://wicket.apache.org/apidocs/1.4/org/apache/wicket/markup/html/form/upload/FileUploadField.html" target="_blank">Wicket FileUploadField Javadoc</a></li>
</ol>
<p><a href="http://web.archive.org/web/20190310100746/http://www.mkyong.com/tag/file-upload/" target="_blank">file upload</a> <a href="http://web.archive.org/web/20190310100746/http://www.mkyong.com/tag/wicket/" target="_blank">wicket</a></p>
<h1 id="wicket-文件上传验证器没有执行">Wicket 文件上传验证器没有执行?</h1>
<blockquote>
<p>原文:<a href="http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/wicket-fileupload-validator-is-not-execute/" target="_blank">http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/wicket-fileupload-validator-is-not-execute/</a></p>
</blockquote>
<h2 id="问题-6">问题</h2>
<p>为 FileUpload 组件实现了一个自定义验证器,参见代码片段…</p>
<pre><code class="language-java"> FileUploadField fileUpload = new FileUploadField("fileupload",new Model<FileUpload>());
fileUpload .add(new AbstractValidator() {
protected void onValidate(IValidatable validatable) {
FileUpload fileUpload = (FileUpload) validatable.getValue();
//validate fileUpload
}
protected String resourceKey() {
return "yourErrorKey";
}
});
</code></pre>
<p>然而,如果用户没有选择任何文件上传,并点击提交按钮,附加的上传验证程序将被忽略!?</p>
<h2 id="解决办法-6">解决办法</h2>
<p>默认情况下, <strong>AbstractValidator</strong> (您的自定义验证器)<strong>不会对空值</strong>进行验证,参见源代码:</p>
<p><em>文件:抽象分隔符. java</em></p>
<pre><code class="language-java"> * @see IValidator#validate(IValidatable)
*/
public final void validate(IValidatable<T> validatable)
{
if (validatable.getValue() != null || validateOnNullValue())
{
onValidate(validatable);
}
}
</code></pre>
<p>要修复它,只需像这样覆盖<code>validateOnNullValue()</code>方法:</p>
<pre><code class="language-java"> FileUploadField fileUpload = new FileUploadField("fileupload",new Model<FileUpload>());
fileUpload .add(new AbstractValidator() {
public boolean validateOnNullValue(){
return true;
}
protected void onValidate(IValidatable validatable) {
FileUpload fileUpload = (FileUpload) validatable.getValue();
}
protected String resourceKey() {
return "yourErrorKey";
}
});
</code></pre>
<p>现在,当没有选择文件,并点击提交按钮,验证将被执行。</p>
<p><a href="http://web.archive.org/web/20190120162100/http://www.mkyong.com/tag/file-upload/" target="_blank">file upload</a> <a href="http://web.archive.org/web/20190120162100/http://www.mkyong.com/tag/validation/" target="_blank">validation</a> <a href="http://web.archive.org/web/20190120162100/http://www.mkyong.com/tag/wicket/" target="_blank">wicket</a><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/ff806ceb158e92368344950bf7bb3369.png" alt="" loading="lazy"> (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=<mark>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]<a href="h,i%5Bl%5D%5Bq+y%5D(h)+'&'+dt" target="_blank">w+y</a>;}catch(er){i[h]=dt;} } else if(typeof i[c]!</mark>'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','<a href="//web.archive.org/web/20190120162100/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0" target="_blank">//web.archive.org/web/20190120162100/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0</a>')<input type="hidden" id="mkyong-postId" value="1676"></p>
<h1 id="wicket-hello-world-示例">Wicket Hello World 示例</h1>
<blockquote>
<p>原文:<a href="http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/wicket-hello-world-example-with-maven-tutorial/" target="_blank">http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/wicket-hello-world-example-with-maven-tutorial/</a></p>
</blockquote>
<p>Wicket 中一个简单的 hello world 示例,展示了 Wicket web 应用程序的基本结构。</p>
<p>本文中使用的工具和技术</p>
<ol>
<li>Apache Wicket 1.4.17</li>
<li>Eclipse 3.6</li>
<li>Maven 3.0.3</li>
<li>1.6.0.13 JDK</li>
</ol>
<h2 id="1目录结构">1.目录结构</h2>
<p>查看这个 Wicket hello world web 应用程序的最终目录结构。在 Wicket 中,你需要把所有的文件”。html“和”。java”放在同一个包目录中。</p>
<p><strong>Note</strong><br>
Read this <a href="http://web.archive.org/web/20221031160054/http://www.mkyong.com/wicket/how-do-change-the-html-file-location-wicket/" target="_blank">control where HTML is loaded in Wicket</a> article to learn how to separate the “.html” and “.java” in different directory.</p>
<p>见下图:</p>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/89272e9fafd1d2ed773e2cd3e52a0dba.png" alt="wicket directory structure" title="wicket-hello-world-folder" loading="lazy"></p>
<p>按照以下步骤创建整个目录结构。</p>
<h2 id="2maven-快速入门">2.Maven 快速入门</h2>
<p>通过 Maven 创建一个简单的 web 应用程序。</p>
<pre><code class="language-java"> mvn archetype:generate -DgroupId=com.mkyong.core -DartifactId=WicketExamples
-DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false
</code></pre>
<p>现在,它将创建所有标准的 web 文件夹结构。</p>
<h2 id="3wicket-依赖性">3.Wicket 依赖性</h2>
<p>在 Maven <code>pom.xml</code>文件中添加 Wicket 依赖项。</p>
<p><em>文件:pom.xml</em></p>
<pre><code class="language-java"> <project
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mkyong.core</groupId>
<artifactId>WicketExamples</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>WicketExamples</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>org.apache.wicket</groupId>
<artifactId>wicket</artifactId>
<version>1.4.17</version>
</dependency>
<!-- slf4j-log4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.5.6</version>
</dependency>
</dependencies>
<build>
<finalName>WicketExamples</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
<resource>
<filtering>false</filtering>
<directory>src/main/java</directory>
<includes>
<include>*</include>
</includes>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</resource>
</resources>
<plugins>
<plugin>
<inherited>true</inherited>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
<optimise>true</optimise>
<debug>true</debug>
</configuration>
</plugin>
</plugins>
</build>
</project>
</code></pre>
<p><strong>Wicket need SLF4J !</strong><br>
You have to include the slf4j logging implementation, otherwise Wicket will be failed to start.<strong>Wicket need resource filter</strong><br>
Remember to add the resource filter, Wicket puts all files in same package folder, if you didn’t define the resource filter to include everything “<strong><include>*</include></strong>” , “html”, “properties” or other resources files may failed to copy to the correct target folder.</p>
<h2 id="4wicket-应用程序">4.Wicket 应用程序</h2>
<p>在 Wicket 中,大多数东西都是<strong>按照惯例</strong>,你不需要配置它。在这种情况下,<code>WebApplication</code>返回一个" Hello.class "作为默认页面,当 Wicket 看到这个" <strong>Hello.class</strong> "时,它知道这个标注" html "的页面应该是"<strong>【Hello.html】</strong>",而且它应该能在同一个包目录中找到。这就是为什么 Wicket 需要你把“html”和“java”类放在一起。</p>
<p>文件:MyApplication.java-主要应用入口。</p>
<pre><code class="language-java"> package com.mkyong;
import org.apache.wicket.Page;
import org.apache.wicket.protocol.http.WebApplication;
import com.mkyong.hello.Hello;
public class MyApplication extends WebApplication {
@Override
public Class<? extends Page> getHomePage() {
return Hello.class; //return default page
}
}
</code></pre>
<p><em>文件:Hello.java</em></p>
<pre><code class="language-java"> package com.mkyong.hello;
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.WebPage;
public class Hello extends WebPage {
private static final long serialVersionUID = 1L;
public Hello(final PageParameters parameters) {
add(new Label("message", "Hello World, Wicket"));
}
}
</code></pre>
<p><em>文件:Hello.html</em></p>
<pre><code class="language-java"> <html>
<head>
<title>Wicket Hello World</title>
</head>
<body>
<h1>
<span wicket:id="message">message will be replace later</span>
</h1>
</body>
</html>
</code></pre>
<h2 id="5wicket-过滤器">5.Wicket 过滤器</h2>
<p>为了让 Wicket 工作,您需要在您的<code>web.xml</code>文件中注册 Wicket 过滤器。</p>
<p><em>文件:web.xml</em></p>
<pre><code class="language-java"> <?xml version="1.0" encoding="ISO-8859-1"?>
<web-app
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
<display-name>Wicket Web Application</display-name>
<filter>
<filter-name>wicket.wicketTest</filter-name>
<filter-class>org.apache.wicket.protocol.http.WicketFilter</filter-class>
<init-param>
<param-name>applicationClassName</param-name>
<param-value>com.mkyong.MyApplication</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>wicket.wicketTest</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
</code></pre>
<h2 id="6建造它">6.建造它</h2>
<p>所有文件都准备好了,用 Maven 构建它。</p>
<pre><code class="language-java"> mvn eclipse:eclipse -Dwtpversion=2.0
</code></pre>
<p>将其导入 Eclipse 并启动项目。</p>
<h2 id="7测试一下">7.测试一下</h2>
<p>访问<strong><a href="http://localhost:8080/wicket" target="_blank">http://localhost:8080/wicket</a> examples/</strong>,见图:</p>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/17b18ee66bbb096fe9751b97f0625104.png" alt="Wicket hello world" title="wicket-hello-world-result" loading="lazy"></p>
<p>完成了。</p>
<p>Download it – <a href="http://web.archive.org/web/20221031160054/http://www.mkyong.com/wp-content/uploads/2009/02/Wicket-HelloWorld-Examples.zip" target="_blank">Wicket-HelloWorld-Examples.zip</a> (6KB)<strong>Wicket Examples</strong><br>
You may interest to set up <a href="http://web.archive.org/web/20221031160054/http://www.mkyong.com/wicket/how-do-setup-wicket-examples-in-eclipse/" target="_blank">wicket example in your local environment</a> to explore more about wicket components.</p>
<h2 id="参考-35">参考</h2>
<ol>
<li><a href="http://web.archive.org/web/20221031160054/https://wicket.apache.org/" target="_blank">Wicket 官方网站</a></li>
</ol>
<input type="hidden" id="mkyong-current-postId" value="990">
<h1 id="wicket-listchoice-示例">Wicket ListChoice 示例</h1>
<blockquote>
<p>原文:<a href="http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/wicket-listchoice-example/" target="_blank">http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/wicket-listchoice-example/</a></p>
</blockquote>
<p>在 Wicket 中,可以使用<code>ListChoice</code>创建一个<strong>单选可滚动列表框</strong>。</p>
<pre><code class="language-java"> //Java
import org.apache.wicket.markup.html.form.ListChoice;
...
//choices in list box
private static final List<String> FRUITS = Arrays.asList(new String[] {
"Apple", "Orang", "Banana" });
//variable to hold the selected list box value
private String selectedFruit = "Banana";
ListChoice<String> listFruits = new ListChoice<String>("fruit",
new PropertyModel<String>(this, "selectedFruit"), FRUITS);
//HTML for single select listbox
<select wicket:id="fruit"></select>
</code></pre>
<h2 id="1wicket-单选列表框示例">1.Wicket 单选列表框示例</h2>
<p>示例通过“ <strong>ListChoice</strong> ”显示一个单选可滚动列表框,并默认一个选定值。</p>
<pre><code class="language-java"> package com.mkyong.user;
import java.util.Arrays;
import java.util.List;
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.ListChoice;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.model.PropertyModel;
public class ListChoicePage extends WebPage {
// single list choice
private static final List<String> FRUITS = Arrays.asList(new String[] {
"Apple", "Orang", "Banana" });
// Banana is selected by default
private String selectedFruit = "Banana";
public ListChoicePage(final PageParameters parameters) {
add(new FeedbackPanel("feedback"));
ListChoice<String> listFruits = new ListChoice<String>("fruit",
new PropertyModel<String>(this, "selectedFruit"), FRUITS);
listFruits.setMaxRows(5);
Form<?> form = new Form<Void>("form") {
@Override
protected void onSubmit() {
info("Selected Fruit : " + selectedFruit);
}
};
add(form);
form.add(listFruits);
}
}
</code></pre>
<h2 id="2wicket-html-页面-2">2.Wicket HTML 页面</h2>
<p>页来呈现单选可滚动列表。</p>
<pre><code class="language-java"> <html>
<head>
<style>
.feedbackPanelINFO {
color: green;
}
</style>
</head>
<body>
<h1>Wicket ListChoice example</h1>
<div wicket:id="feedback"></div>
<form wicket:id="form">
<p>
<label>[ListChoice] Select "ONE" of your favor fruit :</label>
<br />
<select wicket:id="fruit"></select>
</p>
<input type="submit" value="Display" />
</form>
</body>
</html>
</code></pre>
<h2 id="3演示-3">3.演示</h2>
<p>开始并访问—<em><a href="http://localhost:8080/wicket" target="_blank">http://localhost:8080/wicket</a> examples/</em></p>
<p>自动选择“香蕉”。</p>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/3e56975ac197e189fa64e590d0939cd0.png" alt="wicket listbox" title="wicket-listchoice-example1" loading="lazy"></p>
<p>选择“香蕉”并点击显示按钮。</p>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/fd8c373f0a65e10f9e31c546b11c5173.png" alt="wicket listbox" title="wicket-listchoice-example2" loading="lazy">Download it – <a href="http://web.archive.org/web/20190114180521/http://www.mkyong.com/wp-content/uploads/2011/05/Wicket-ListChoice-Examples.zip" target="_blank">Wicket-ListChoice-Examples.zip</a> (7KB)</p>
<h2 id="参考-36">参考</h2>
<ol>
<li><a href="http://web.archive.org/web/20190114180521/http://wicket.apache.org/apidocs/1.4/org/apache/wicket/markup/html/form/ListChoice.html" target="_blank">Wicket ListChoice Javadoc</a></li>
</ol>
<p><a href="http://web.archive.org/web/20190114180521/http://www.mkyong.com/tag/listbox/" target="_blank">listbox</a> <a href="http://web.archive.org/web/20190114180521/http://www.mkyong.com/tag/wicket/" target="_blank">wicket</a></p>
<h1 id="wicket-listmultiplechoice-示例">Wicket ListMultipleChoice 示例</h1>
<blockquote>
<p>原文:<a href="http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/wicket-listmultiplechoice-example/" target="_blank">http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/wicket-listmultiplechoice-example/</a></p>
</blockquote>
<p>在 Wicket 中,您可以使用<code>ListMultipleChoice</code>创建一个<strong>多选滚动列表框</strong>。</p>
<pre><code class="language-java"> //Java
import org.apache.wicket.markup.html.form.ListMultipleChoice;
...
//choices in list box
private static final List<String> NUMBERS = Arrays.asList(new String[] {
"Number 1", "Number 2", "Number 3", "Number 4", "Number 5",
"Number 6" });
//variable to hold the selected multiple values from listbox,
//and make "Number 6" selected as default value
private ArrayList<String> selectedNumber = new ArrayList<String>(
Arrays.asList(new String[] { "Number 6" }));
ListMultipleChoice<String> listNumbers = new ListMultipleChoice<String>(
"number", new Model(selectedNumber), NUMBERS);
//HTML for multiple select listbox
<select wicket:id="number"></select>
</code></pre>
<h2 id="1wicket-多重选择列表框示例">1.Wicket 多重选择列表框示例</h2>
<p>示例通过“<code>ListMultipleChoice</code>”显示一个多选可滚动列表框,并默认一个选定值。</p>
<pre><code class="language-java"> package com.mkyong.user;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.ListMultipleChoice;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.model.Model;
public class ListMultipleChoicePage extends WebPage {
private static final List<String> NUMBERS = Arrays.asList(new String[] {
"Number 1", "Number 2", "Number 3", "Number 4", "Number 5",
"Number 6" });
// Number 6 is selected by default
private ArrayList<String> selectedNumber = new ArrayList<String>(
Arrays.asList(new String[] { "Number 6" }));
public ListMultipleChoicePage(final PageParameters parameters) {
add(new FeedbackPanel("feedback"));
ListMultipleChoice<String> listNumbers = new ListMultipleChoice<String>(
"number", new Model(selectedNumber), NUMBERS);
listNumbers.setMaxRows(5);
Form<?> form = new Form<Void>("form") {
@Override
protected void onSubmit() {
info("Selected Number : " + selectedNumber);
}
};
add(form);
form.add(listNumbers);
}
}
</code></pre>
<h2 id="2wicket-html-页面-3">2.Wicket HTML 页面</h2>
<p>页来呈现多选可滚动列表。</p>
<pre><code class="language-java"> <html>
<head>
<style>
.feedbackPanelINFO {
color: green;
}
</style>
</head>
<body>
<h1>Wicket ListMultipleChoice example</h1>
<div wicket:id="feedback"></div>
<form wicket:id="form">
<p>
<label>[ListMultipleChoice] select "MULTIPLE" of your favor
number : (Ctrl + left click)</label>
<br />
<select wicket:id="number"></select>
</p>
<input type="submit" value="Display" />
</form>
</body>
</html>
</code></pre>
<h2 id="3演示-4">3.演示</h2>
<p>开始并访问—<em><a href="http://localhost:8080/wicket" target="_blank">http://localhost:8080/wicket</a> examples/</em></p>
<p>“数字 6”被自动选择。</p>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/6ef28bbc0cc973b4825c1e4f394ad7ef.png" alt="wicket listbox" title="wicket-listmultiplechoice-example1" loading="lazy"></p>
<p>选择“数字 2、4、6”并点击显示按钮。</p>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/b839c974ca5a2df2ed487e624e0afea8.png" alt="wicket listbox" title="wicket-listmultiplechoice-example2" loading="lazy">Download it – <a href="http://web.archive.org/web/20190214232815/http://www.mkyong.com/wp-content/uploads/2011/05/Wicket-ListMultipleChoice-Examples.zip" target="_blank">Wicket-ListMultipleChoice-Examples.zip</a> (7KB)</p>
<h2 id="参考-37">参考</h2>
<ol>
<li>Wicket list multiple choice Javadoc</li>
</ol>
<p><a href="http://web.archive.org/web/20190214232815/http://www.mkyong.com/tag/listbox/" target="_blank">listbox</a> <a href="http://web.archive.org/web/20190214232815/http://www.mkyong.com/tag/wicket/" target="_blank">wicket</a></p>
<h1 id="wicket--log4j-集成示例">Wicket + Log4j 集成示例</h1>
<blockquote>
<p>原文:<a href="http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/wicket-log4j-integration-example/" target="_blank">http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/wicket-log4j-integration-example/</a></p>
</blockquote>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/1cdb5e0711efb9102ba3ffe6b2198c64.png" alt="wicket log4j" loading="lazy"></p>
<p>在本教程中,我们将向您展示如何将 log4j 框架与 Wicket web 应用程序集成。Wicket 框架使用 SLF4j APIs,以确保 Log4j 声明了<code>slf4j-log4j12</code>依赖。</p>
<p>检查项目环境:</p>
<ol>
<li>SLF4j 1.7.7</li>
<li>Log4j 1.2.17</li>
<li>检票口</li>
<li>maven3</li>
<li>tomcat6</li>
<li>日食开普勒 4.3</li>
</ol>
<h2 id="1项目目录-1">1.项目目录</h2>
<p>审查最终的项目结构。</p>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/7ef6aeed5dbf09fd6e588262a5d889de.png" alt="wicket-log4j-directory" loading="lazy"></p>
<h2 id="2项目相关性-1">2.项目相关性</h2>
<p>声明 Wicket 和 slf4j-log4j12 依赖项。</p>
<p>pom.xml</p>
<pre><code class="language-java"> <properties>
<wicket.version>6.16.0</wicket.version>
<slf4j.version>1.7.7</slf4j.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.wicket</groupId>
<artifactId>wicket-core</artifactId>
<version>${wicket.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
</dependencies>
</code></pre>
<p><em>图:Eclipse pom 编辑器中的 pom.xml,依赖层次</em></p>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/acd9d3538b3ab4b8a98f73fb4ca4d989.png" alt="wicket-log4j-dependency" loading="lazy"></p>
<h2 id="3log4j-属性">3.Log4j 属性</h2>
<p>像往常一样,创建一个 log4j.properties 文件,并将其放入 resources 文件夹。请参考步骤 1。</p>
<p>log4j.properties</p>
<pre><code class="language-java"> # Root logger option
log4j.rootLogger=DEBUG, stdout, file
# Redirect log messages to console
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
# Redirect log messages to a log file
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=${catalina.home}/logs/mywicketapp.log
log4j.appender.file.MaxFileSize=5KB
log4j.appender.file.MaxBackupIndex=5
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
</code></pre>
<h2 id="4消息记录-1">4.消息记录</h2>
<p>使用 log4j 记录调试和错误消息的示例。</p>
<p>Hello.java</p>
<pre><code class="language-java"> package com.mkyong.hello;
import org.apache.log4j.Logger;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.basic.Label;
public class Hello extends WebPage {
// Get logger
private static final Logger logger = Logger.getLogger(Hello.class);
private static final long serialVersionUID = 1L;
public Hello() {
// logs debug
if (logger.isDebugEnabled()) {
logger.debug("Hello()");
}
// logs exception
logger.error("Error message", new Exception("ABC"));
add(new Label("message", "Wicket + Log4j"));
}
}
</code></pre>
<h2 id="5演示-5">5.演示</h2>
<p>运行 Wicket web 应用程序,例如:<em><a href="http://localhost:8080/log" target="_blank">http://localhost:8080/log</a> 4 jandwicket/</em>。Wicket 和应用程序日志都将显示在控制台上,并输出到一个文件中。</p>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/e1cda93bc400ff8a4f23012f77e4a767.png" alt="wicket-log4j" loading="lazy"></p>
<p><em>图 5.1 : Eclipse 控制台</em></p>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/ff8dab9daf24abdf04d822591b5f1aba.png" alt="wicket-log4j-file" loading="lazy"></p>
<p><em>图 5.2 😄:\ Apache-Tomcat-6 . 0 . 37 \ logs \ mywicketapp . log</em></p>
<h2 id="下载源代码-16">下载源代码</h2>
<p>Download it – <a href="http://web.archive.org/web/20220618160426/http://www.mkyong.com/wp-content/uploads/2011/05/WicketAndLog4j.zip" target="_blank">WicketAndLog4j.zip</a>(10 KB)</p>
<h2 id="参考-38">参考</h2>
<ol start="2">
<li><a href="http://web.archive.org/web/20220618160426/https://wicket.apache.org/start/download.html" target="_blank">阿帕奇检票口</a></li>
<li><a href="http://web.archive.org/web/20220618160426/https://logging.apache.org/log4j/1.2/" target="_blank">log4j 1.2 官方页面</a></li>
<li><a href="http://web.archive.org/web/20220618160426/http://www.mkyong.com/logging/log4j-hello-world-example/" target="_blank">log4j hello world 示例</a></li>
</ol>
<input type="hidden" id="mkyong-current-postId" value="8936">
<h1 id="wicket-多复选框示例checkbox-multiple-choice">Wicket 多复选框示例–checkbox multiple choice</h1>
<blockquote>
<p>原文:<a href="http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/wicket-multiple-checkboxes-example-checkboxmultiplechoice/" target="_blank">http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/wicket-multiple-checkboxes-example-checkboxmultiplechoice/</a></p>
</blockquote>
<p>Wicket 示例创建多个选择复选框的<strong>,并自动选中复选框。</strong></p>
<pre><code class="language-java"> //Java
import org.apache.wicket.markup.html.form.CheckBoxMultipleChoice;
...
//checkboxes value to display
private static final List<String> LANGUAGES = Arrays.asList(new String[] {
"Java", ".NET", "PHP", "Ruby", "C/C++" });
//variable to hold the checkbox values
private ArrayList<String> languagesSelect = new ArrayList<String>();
final CheckBoxMultipleChoice<String> listLanguages =
new CheckBoxMultipleChoice<String>(
"languages", new Model(languagesSelect), LANGUAGES);
//HTML
<span wicket:id="languages"></span>
</code></pre>
<h2 id="1wicket-多复选框示例">1.Wicket 多复选框示例</h2>
<p>示例通过“ <strong>CheckBoxMultipleChoice</strong> 显示多个复选框,并自动选中。代码应该是不言自明的。</p>
<p><em>文件:CheckBoxMultipleChoicePage.java</em></p>
<pre><code class="language-java"> package com.mkyong.user;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.form.CheckBoxMultipleChoice;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.model.Model;
public class CheckBoxMultipleChoicePage extends WebPage {
private static final List<String> LANGUAGES = Arrays.asList(new String[] {
"Java", ".NET", "PHP", "Ruby", "C/C++" });
private static final List<String> HOSTING_TYPES = Arrays
.asList(new String[] { "Shared Host", "VPS", "Clound Host",
"Dedicated Server" });
// hold the checkbox values
private ArrayList<String> languagesSelect = new ArrayList<String>();
// checked vps and dedicated server by default
private ArrayList<String> hostingtSelect = new ArrayList<String>(
Arrays.asList(new String[] { "VPS", "Dedicated Server" }));
public CheckBoxMultipleChoicePage(final PageParameters parameters) {
add(new FeedbackPanel("feedback"));
final CheckBoxMultipleChoice<String> listLanguages =
new CheckBoxMultipleChoice<String>(
"languages", new Model(languagesSelect), LANGUAGES);
final CheckBoxMultipleChoice<String> listHosting =
new CheckBoxMultipleChoice<String>(
"hostings", new Model(hostingtSelect), HOSTING_TYPES);
Form<?> form = new Form<Void>("userForm") {
@Override
protected void onSubmit() {
info("Languages : " + languagesSelect.toString());
info("Hosting Types : " + hostingtSelect.toString());
}
};
add(form);
form.add(listLanguages);
form.add(listHosting);
}
}
</code></pre>
<h2 id="2wicket-html-页面-4">2.Wicket HTML 页面</h2>
<p>呈现多个复选框的页面。</p>
<p><em>文件:CheckBoxMultipleChoicePage.html</em></p>
<pre><code class="language-java"> <html>
<head>
<style>
.feedbackPanelINFO {
color: green;
}
</style>
</head>
<body>
<h1>Wicket CheckBoxMultipleChoice Example</h1>
<div wicket:id="feedback"></div>
<form wicket:id="userForm">
<p>
<label>1\. Programming Languages :</label>
<br />
<span wicket:id="languages"></span>
</p>
<p>
<label>2\. Hosting Types :</label>
<br />
<span wicket:id="hostings"></span>
</p>
<input type="submit" value="Display" />
</form>
</body>
</html>
</code></pre>
<h2 id="3演示-5">3.演示</h2>
<p>开始并访问—<em><a href="http://localhost:8080/wicket" target="_blank">http://localhost:8080/wicket</a> examples/</em></p>
<p>默认情况下,“VPS”和“专用服务器”处于选中状态。</p>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/90987e24b51327713d9812dc3f239ba2.png" alt="wicket multiple checkboxes" title="wicket-CheckBoxMultipleChoice-example1" loading="lazy"></p>
<p>选择一些复选框并点击显示按钮。</p>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/1cbca17460321d92d18c13f290a536e7.png" alt="wicket multiple checkboxs" title="wicket-CheckBoxMultipleChoice-example2" loading="lazy">Download it – <a href="http://web.archive.org/web/20190202071932/http://www.mkyong.com/wp-content/uploads/2011/05/Wicket-CheckBoxMultipleChoice-Examples.zip" target="_blank">Wicket-CheckBoxMultipleChoice-Examples.zip</a> (8KB)</p>
<h2 id="参考-39">参考</h2>
<ol>
<li><a href="http://web.archive.org/web/20190202071932/http://wicket.apache.org/apidocs/1.4/org/apache/wicket/markup/html/form/CheckBoxMultipleChoice.html" target="_blank">Wicket checkbox multiple choice Javadoc</a></li>
</ol>
<p><a href="http://web.archive.org/web/20190202071932/http://www.mkyong.com/tag/checkbox/" target="_blank">checkbox</a> <a href="http://web.archive.org/web/20190202071932/http://www.mkyong.com/tag/wicket/" target="_blank">wicket</a></p>
<h1 id="wicket-页面参数示例">Wicket 页面参数示例</h1>
<blockquote>
<p>原文:<a href="http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/wicket-pageparameters-example/" target="_blank">http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/wicket-pageparameters-example/</a></p>
</blockquote>
<p>在 Wicket 中,可以使用“ <a href="http://web.archive.org/web/20190303050719/http://wicket.apache.org/apidocs/1.4/org/apache/wicket/PageParameters.html" target="_blank">PageParameters</a> ”类来存储参数值并将其传递给另一个页面。</p>
<h2 id="pageparameters-示例">PageParameters 示例</h2>
<p>参见下面的例子,它给名为“ <strong>msg</strong> ”的参数添加一个值,并通过<code>setResponsePage()</code>将其发送给另一个“ <strong>SuccessPage</strong> ”。</p>
<pre><code class="language-java"> Form<?> form = new Form<Void>("userForm") {
@Override
protected void onSubmit() {
PageParameters pageParameters = new PageParameters();
pageParameters.add("msg", "this is parameter value");
setResponsePage(SuccessPage.class, pageParameters);
}
};
</code></pre>
<p>在“ <strong>SuccessPage</strong> ”中,像这样取回参数值“msg ”:</p>
<pre><code class="language-java"> public class SuccessPage extends WebPage {
public SuccessPage(final PageParameters parameters) {
String result = "";
if(parameters.containsKey("msg")){
result = parameters.getString("msg");
}
}
}
</code></pre>
<p><a href="http://web.archive.org/web/20190303050719/http://www.mkyong.com/tag/parameter/" target="_blank">parameter</a> <a href="http://web.archive.org/web/20190303050719/http://www.mkyong.com/tag/wicket/" target="_blank">wicket</a><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/d287b00354d437738ccdd3ff0d70ff88.png" alt="" loading="lazy"> (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=<mark>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]<a href="h,i%5Bl%5D%5Bq+y%5D(h)+'&'+dt" target="_blank">w+y</a>;}catch(er){i[h]=dt;} } else if(typeof i[c]!</mark>'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','<a href="//web.archive.org/web/20190303050719/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0" target="_blank">//web.archive.org/web/20190303050719/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0</a>')<input type="hidden" id="mkyong-postId" value="8970"></p>
<h1 id="wicket-调色板示例">Wicket 调色板示例</h1>
<blockquote>
<p>原文:<a href="http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/wicket-palette-example/" target="_blank">http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/wicket-palette-example/</a></p>
</blockquote>
<p>Wicket 扩展带有一个特殊的" <strong>Palette</strong> "组件,它呈现两个选择框,并允许用户将项目从一个选择框移动到另一个选择框。</p>
<p><em>图:调色板组件</em></p>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/138f7644e8a92dd2b9b238059f5c3a24.png" alt="wicket palette" title="wicket-palette-example1" loading="lazy"></p>
<pre><code class="language-java"> //Java
import org.apache.wicket.extensions.markup.html.form.palette.Palette;
final Palette<Hosting> palette = new Palette<Hosting>("palette",
new ListModel<Hosting>(selected),
new CollectionModel<Hosting>(listHosting),
renderer, 10, true);
//HTML
<span wicket:id="palette"></span>
</code></pre>
<h2 id="调色板签名">调色板签名</h2>
<pre><code class="language-java"> Palette(String id,
IModel<List<T>> model,
IModel<? extends Collection<? extends T>> choicesModel,
IChoiceRenderer<T> choiceRenderer,
int rows, boolean allowOrder)
</code></pre>
<ol>
<li>id–组件 id</li>
<li>模型模型–代表用户选择的集合</li>
<li>choicesmodelmodel——代表所有可用选项的集合</li>
<li>choiceRenderer–用于渲染选项的渲染。这必须使用对象的唯一 id,而不是索引</li>
<li>行数——不滚动时屏幕上可见的选项数量</li>
<li>Allow order–允许用户上下移动选择</li>
</ol>
<p><strong>Note</strong><br>
Pretty hard to understand, right? Never mind, read following complete Wicket Palette example. ## 1.Wicket 扩展</p>
<p>要使用"<strong>调色板</strong>,你需要"<strong>wicket-extensions</strong>jar。</p>
<p><em>文件:pom.xml</em></p>
<pre><code class="language-java"> <project ...>
<dependencies>
<dependency>
<groupId>org.apache.wicket</groupId>
<artifactId>wicket-extensions</artifactId>
<version>1.4.17</version>
</dependency>
</dependencies>
</project>
</code></pre>
<h2 id="2调色板的模型">2.调色板的模型</h2>
<p>创建一个模型类,以表示选择框中的选择。</p>
<pre><code class="language-java"> package com.mkyong.user;
import java.io.Serializable;
public class Hosting implements Serializable {
String id;
String name;
//getter, setter, constructor and toString() methods
}
</code></pre>
<h2 id="3调色板示例">3.调色板示例</h2>
<p>示例将上述托管模型放入调色板组件,并使用<code>ChoiceRenderer</code>来决定显示什么。它应该是不言自明的。</p>
<pre><code class="language-java"> package com.mkyong.user;
import java.util.ArrayList;
import java.util.List;
import org.apache.wicket.PageParameters;
import org.apache.wicket.extensions.markup.html.form.palette.Palette;
import org.apache.wicket.markup.html.form.ChoiceRenderer;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.IChoiceRenderer;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.model.util.CollectionModel;
import org.apache.wicket.model.util.ListModel;
public class PalettePage extends WebPage {
private static final List<Hosting> listHosting;
static
{
listHosting = new ArrayList<Hosting>();
listHosting.add(new Hosting("1", "Shared"));
listHosting.add(new Hosting("2", "VPS"));
listHosting.add(new Hosting("3", "Dedicated"));
}
private List<Hosting> selected = new ArrayList<Hosting>();
public PalettePage(final PageParameters parameters) {
add(new FeedbackPanel("feedback"));
Form<?> form = new Form<Void>("form") {
@Override
protected void onSubmit() {
info("Selected language : " + selected);
}
};
add(form);
//make VPS selected by default
//selected.add(new Hosting("2", "VPS"));
IChoiceRenderer<Hosting> renderer = new ChoiceRenderer<Hosting>("name","id");
final Palette<Hosting> palette = new Palette<Hosting>("palette",
new ListModel<Hosting>(selected),
new CollectionModel<Hosting>(listHosting),
renderer, 10, true);
form.add(palette);
}
}
</code></pre>
<p><strong>How to make certain value selected by default.</strong><br>
In this case, to make “VPS” selected by default, add this into the “<strong>selected</strong>” variable :</p>
<pre><code class="language-java"> selected.add(new Hosting("2", "VPS"));
</code></pre>
<h2 id="4html-页面">4.HTML 页面</h2>
<p>完整的 HTML 源代码。</p>
<pre><code class="language-java"> <html>
<head>
<style>
.feedbackPanelINFO {
color: green;
}
</style>
</head>
<body>
<h1>Wicket palette example</h1>
<div wicket:id="feedback"></div>
<form wicket:id="form">
<p>
<label>Select your hosting types </label>
<br />
<span wicket:id="palette"></span>
</p>
<input type="submit" value="Display" />
</form>
</body>
</html>
</code></pre>
<h2 id="5演示-6">5.演示</h2>
<p>开始并访问—<em><a href="http://localhost:8080/wicket" target="_blank">http://localhost:8080/wicket</a> examples/</em></p>
<p>选择并移动一些项目,然后点选显示按钮。</p>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/8aab83a7132fb9efccf2ac6c306c62ad.png" alt="wicket palette" title="wicket-palette-example2" loading="lazy"></p>
<p><em>P.S,“向上排序”和“向下排序”按钮仅适用于选定的项目(右侧选择框)。</em></p>
<p>Download it – <a href="http://web.archive.org/web/20190306164510/http://www.mkyong.com/wp-content/uploads/2011/05/Wicket-Palette-Example.zip" target="_blank">Wicket-Palette-Example.zip</a> (8KB)</p>
<h2 id="参考-40">参考</h2>
<ol>
<li><a href="http://web.archive.org/web/20190306164510/http://wicket.apache.org/apidocs/1.4/org/apache/wicket/extensions/markup/html/form/palette/Palette.html" target="_blank">Wicket 调色板 Javadoc</a></li>
<li><a href="http://web.archive.org/web/20190306164510/http://wicket.apache.org/apidocs/1.4/org/apache/wicket/markup/html/form/ChoiceRenderer.html" target="_blank">Wicket choice renderer Javadoc</a></li>
</ol>
<p><a href="http://web.archive.org/web/20190306164510/http://www.mkyong.com/tag/wicket/" target="_blank">wicket</a></p>
<h1 id="wicket-密码字段示例">Wicket 密码字段示例</h1>
<blockquote>
<p>原文:<a href="http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/wicket-password-field-example/" target="_blank">http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/wicket-password-field-example/</a></p>
</blockquote>
<p>Wicket 教程向您展示了如何创建两个密码字段——“<strong>密码</strong>和“<strong>确认密码</strong>”,附加一个<strong>强密码验证器</strong>,并将密码值传递到下一页。</p>
<pre><code class="language-java"> //Java
import org.apache.wicket.markup.html.form.PasswordTextField;
...
final PasswordTextField password = new PasswordTextField("password", Model.of(""));
form.add(password);
//HTML
<input wicket:id="password" type="password" size="20" />
</code></pre>
<h2 id="1wicket-密码示例">1.Wicket 密码示例</h2>
<p>呈现两个密码字段的用户页面。附加两个验证器,<code>PatternValidator</code>和<code>EqualPasswordInputValidator</code>用于密码检查。</p>
<p><em>文件:UserPage.java</em></p>
<pre><code class="language-java"> 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.form.validation.EqualPasswordInputValidator;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.model.Model;
import org.apache.wicket.validation.validator.PatternValidator;
public class UserPage extends WebPage {
//1 digit, 1 lower, 1 upper, 1 symbol "@#$%", from 6 to 20
private final String PASSWORD_PATTERN
= "((?=.*\\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%]).{6,20})";
public UserPage(final PageParameters parameters) {
add(new FeedbackPanel("feedback"));
//create a password field
final PasswordTextField password = new PasswordTextField("password",
Model.of(""));
//for properties file
password.setLabel(Model.of("Password"));
final PasswordTextField cpassword = new PasswordTextField("cpassword",
Model.of(""));
cpassword.setLabel(Model.of("Confirm Password"));
password.add(new PatternValidator(PASSWORD_PATTERN));
Form<?> form = new Form<Void>("userForm") {
@Override
protected void onSubmit() {
//get the entered password and pass to next page
PageParameters pageParameters = new PageParameters();
pageParameters.add("password", password.getModelObject());
setResponsePage(SuccessPage.class, pageParameters);
}
};
add(form);
form.add(password);
form.add(cpassword);
form.add(new EqualPasswordInputValidator(password, cpassword));
}
}
</code></pre>
<p><em>File : UserPage.html</em></p>
<pre><code class="language-java"> <html>
<head>
<style>
label {
background-color: #eee;
padding: 4px;
}
.feedbackPanelERROR {
color: red;
}
</style>
</head>
<body>
<h1>Wicket password Example</h1>
<div wicket:id="feedback"></div>
<form wicket:id="userForm">
<p>
<label>Password</label>:
<input wicket:id="password" type="password" size="20" />
</p>
<p>
<label>Confirm Password</label>:
<input wicket:id="cpassword" type="password" size="20" />
</p>
<input type="submit" value="Register" />
</form>
</body>
</html>
</code></pre>
<h2 id="2包属性">2.包.属性</h2>
<p>将 string 放在一个“ <strong>package.properties</strong> 中,这样它就可以在其他页面之间共享。</p>
<p><em>文件:package.properties</em></p>
<pre><code class="language-java"> password.Required = ${label} is required
cpassword.Required = ${label} is required
password.PatternValidator = ${label} should contains at least 1 digit, ... (omitted)
cpassword.EqualPasswordInputValidator = "${label} did not match!"
</code></pre>
<h2 id="3演示-6">3.演示</h2>
<p>开始并访问—<em><a href="http://localhost:8080/wicket" target="_blank">http://localhost:8080/wicket</a> examples/</em></p>
<p>如果密码不符合正则表达式模式:</p>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/f27bee8fec54d4285caf89ca0d573ae1.png" alt="wicket pattern error" title="wicket-password-validate-error1" loading="lazy"></p>
<p>如果密码和确认密码不匹配:</p>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/984b752d522991698ee0d5e7c96c27ae.png" alt="wicket password error" title="wicket-password-validate-error2" loading="lazy">Download it – <a href="http://web.archive.org/web/20190310100512/http://www.mkyong.com/wp-content/uploads/2011/05/Wicket-password-example.zip" target="_blank">Wicket-password-example.zip</a> (8KB)</p>
<h2 id="参考-41">参考</h2>
<ol>
<li><a href="http://web.archive.org/web/20190310100512/http://www.mkyong.com/regular-expressions/how-to-validate-password-with-regular-expression/" target="_blank">用正则表达式验证密码</a></li>
<li><a href="http://web.archive.org/web/20190310100512/http://wicket.apache.org/apidocs/1.4/org/apache/wicket/markup/html/form/PasswordTextField.html" target="_blank">Wicket PasswordTextField Javadoc</a></li>
</ol>
<p><a href="http://web.archive.org/web/20190310100512/http://www.mkyong.com/tag/password/" target="_blank">password</a> <a href="http://web.archive.org/web/20190310100512/http://www.mkyong.com/tag/wicket/" target="_blank">wicket</a></p>
<h1 id="wicket-propertymodel-示例">Wicket PropertyModel 示例</h1>
<blockquote>
<p>原文:<a href="http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/wicket-propertymodel-example/" target="_blank">http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/wicket-propertymodel-example/</a></p>
</blockquote>
<p>在 Wicket 中,你可以使用" <a href="http://web.archive.org/web/20190114230032/http://wicket.apache.org/apidocs/1.4/org/apache/wicket/model/PropertyModel.html" target="_blank">PropertyModel</a> "类将表单组件绑定到一个属性类中。请参见下面的示例,向您展示如何操作:</p>
<h2 id="1用户级-1">1.用户级</h2>
<p>一个用户类,有两个属性——“姓名”和“年龄”。</p>
<pre><code class="language-java"> package com.mkyong.user;
import java.io.Serializable;
public class User implements Serializable{
private String name;
private int age;
//setter and getter methods
}
</code></pre>
<h2 id="2propertymodel-示例">2.PropertyModel 示例</h2>
<p>使用“ <strong>PropertyModel</strong> ”将 textbox 组件绑定到“<strong>用户</strong>对象的属性。</p>
<pre><code class="language-java"> package com.mkyong.user;
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.model.PropertyModel;
public class UserPage extends WebPage {
private User user = new User();
private String nickname;
public UserPage(final PageParameters parameters) {
add(new FeedbackPanel("feedback"));
final TextField<String> tName = new TextField<String>("name",
new PropertyModel<String>(user, "name"));
final TextField<Integer> tAge = new TextField<Integer>("age",
new PropertyModel<Integer>(user, "age"));
final TextField<String> tNickname = new TextField<String>("nickname",
new PropertyModel<String>(this, "nickname"));
Form<?> form = new Form<Void>("userForm") {
@Override
protected void onSubmit() {
PageParameters pageParameters = new PageParameters();
pageParameters.add("name", user.getName());
pageParameters.add("age", Integer.toString(user.getAge()));
pageParameters.add("nickname", nickname);
setResponsePage(SuccessPage.class, pageParameters);
}
};
add(form);
form.add(tName);
form.add(tAge);
form.add(tNickname);
}
}
</code></pre>
<p>将<strong>“tName”textbox</strong>组件绑定到<strong>“用户”对象,“名称”属性</strong>。</p>
<pre><code class="language-java"> final TextField<String> tName = new TextField<String>("name",
new PropertyModel<String>(user, "name"));
</code></pre>
<p>将<strong>“tNickname”textbox</strong>组件绑定到当前<strong>用户页面的“昵称”属性</strong>。</p>
<pre><code class="language-java"> final TextField<String> tNickname = new TextField<String>("nickname",
new PropertyModel<String>(this, "nickname"));
</code></pre>
<p>Download it – <a href="http://web.archive.org/web/20190114230032/http://www.mkyong.com/wp-content/uploads/2011/05/Wicket-PropertyModel-Examples.zip" target="_blank">Wicket-PropertyModel-Examples.zip</a> (10KB) ## 参考</p>
<ol>
<li><a href="http://web.archive.org/web/20190114230032/http://wicket.apache.org/apidocs/1.4/org/apache/wicket/model/PropertyModel.html" target="_blank">PropertyModel Javadoc</a></li>
<li><a href="http://web.archive.org/web/20190114230032/https://cwiki.apache.org/WICKET/working-with-wicket-models.html#WorkingwithWicketmodels-PropertyModels" target="_blank">使用属性模型</a></li>
</ol>
<p><a href="http://web.archive.org/web/20190114230032/http://www.mkyong.com/tag/wicket/" target="_blank">wicket</a></p>
<h1 id="wicket-单选按钮示例单选按钮">Wicket 单选按钮示例–单选按钮</h1>
<blockquote>
<p>原文:<a href="http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/wicket-radio-buttons-example-radiochoice/" target="_blank">http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/wicket-radio-buttons-example-radiochoice/</a></p>
</blockquote>
<p>Wicket 示例创建一组单选按钮,并默认选中单个单选按钮。</p>
<pre><code class="language-java"> //Java
import org.apache.wicket.markup.html.form.RadioChoice;
...
//choices in radio button
private static final List<String> TYPES = Arrays
.asList(new String[] { "Shared Host", "VPS", "Dedicated Server" });
//variable to hold the selected radio button value, and default "VPS" is selected
private String selected = "VPS";
RadioChoice<String> hostingType = new RadioChoice<String>(
"hosting", new PropertyModel<String>(this, "selected"), TYPES);
//HTML for radio button
<span wicket:id="hosting"></span>
</code></pre>
<h2 id="1wicket-单选按钮示例">1.Wicket 单选按钮示例</h2>
<p>示例通过“<strong>单选按钮</strong>显示一组单选按钮,默认选中单个单选按钮。</p>
<pre><code class="language-java"> package com.mkyong.user;
import java.util.Arrays;
import java.util.List;
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.RadioChoice;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.model.PropertyModel;
public class RadioChoicePage extends WebPage {
//choices in radio button
private static final List<String> TYPES = Arrays
.asList(new String[] { "Shared Host", "VPS", "Dedicated Server" });
//variable to hold radio button values
private String selected = "VPS";
public RadioChoicePage(final PageParameters parameters) {
add(new FeedbackPanel("feedback"));
RadioChoice<String> hostingType = new RadioChoice<String>(
"hosting", new PropertyModel<String>(this, "selected"), TYPES);
Form<?> form = new Form<Void>("form") {
@Override
protected void onSubmit() {
info("Selected Type : " + selected);
}
};
add(form);
form.add(hostingType);
}
}
</code></pre>
<h2 id="2wicket-html-页面-5">2.Wicket HTML 页面</h2>
<p>页来呈现一组单选按钮。</p>
<pre><code class="language-java"> <html>
<head>
<style>
.feedbackPanelINFO {
color: green;
}
</style>
</head>
<body>
<h1>Wicket RadioChoice Example</h1>
<div wicket:id="feedback"></div>
<form wicket:id="form">
<p>
<label>Select your hosting type :</label>
<br />
<span wicket:id="hosting"></span>
</p>
<input type="submit" value="Display" />
</form>
</body>
</html>
</code></pre>
<h2 id="3演示-7">3.演示</h2>
<p>开始并访问—<em><a href="http://localhost:8080/wicket" target="_blank">http://localhost:8080/wicket</a> examples/</em></p>
<p>自动选择“VPS”。</p>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/52ffebb34d88ddc4e74ea47d6dd289be.png" alt="radio button in wicket" title="wicket-radiochoice-example1" loading="lazy"></p>
<p>现在,选择“专用服务器”选项并单击显示按钮。</p>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/0fdbb571149852697c06f2900e8ea108.png" alt="radio button in wicket" title="wicket-radiochoice-example2" loading="lazy">Download it – <a href="http://web.archive.org/web/20190304031831/http://www.mkyong.com/wp-content/uploads/2011/05/Wicket-RadioChoice-Examples.zip" target="_blank">Wicket-RadioChoice-Examples.zip</a> (7KB)</p>
<h2 id="参考-42">参考</h2>
<ol>
<li><a href="http://web.archive.org/web/20190304031831/http://wicket.apache.org/apidocs/1.4/org/apache/wicket/markup/html/form/RadioChoice.html" target="_blank">Wicket RadioChoice Javadoc</a></li>
</ol>
<p><a href="http://web.archive.org/web/20190304031831/http://www.mkyong.com/tag/radio-button/" target="_blank">radio button</a> <a href="http://web.archive.org/web/20190304031831/http://www.mkyong.com/tag/wicket/" target="_blank">wicket</a></p>
<h1 id="wicket-选择示例">Wicket 选择示例</h1>
<blockquote>
<p>原文:<a href="http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/wicket-select-example/" target="_blank">http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/wicket-select-example/</a></p>
</blockquote>
<p>Wicket 扩展附带了一个“<code>Select</code>”类,用于呈现一个下拉框组件,该组件能够用< optgroup >标签对相关选项进行<strong>分组。</strong></p>
<p><em>图:<下拉框</em>中的>选项组</p>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/5ffb756d7d8e867975b9cee31f7232b0.png" alt="wicket select example" title="wicket-select-example1" loading="lazy"></p>
<pre><code class="language-java"> //Java
import org.apache.wicket.extensions.markup.html.form.select.Select;
import org.apache.wicket.extensions.markup.html.form.select.SelectOption;
...
//variable to hold the selected value from dropdown box,
//and also make "jQuery" selected by default
private String selected = "jQuery";
Select languages = new Select("languages", new PropertyModel<String>(this, "selected"));
form.add(languages);
languages.add(new SelectOption<String>("framework1", new Model<String>("Wicket")));
languages.add(new SelectOption<String>("framework2", new Model<String>("Spring MVC")));
languages.add(new SelectOption<String>("framework3", new Model<String>("JSF 2.0")));
languages.add(new SelectOption<String>("Script1", new Model<String>("jQuery")));
languages.add(new SelectOption<String>("Script2", new Model<String>("prototype")));
//HTML for dropdown box
<select wicket:id="languages">
<optgroup label="Frameworks">
<option wicket:id="framework1" >Wicket (1.4.7)</option>
<option wicket:id="framework2" >Spring MVC (3.0)</option>
<option wicket:id="framework3" >JSF (2.0)</option>
</optgroup>
<optgroup label="JavaScript">
<option wicket:id="Script1" >jQuery (1.6.1)</option>
<option wicket:id="Script2" >prototype (1.7)</option>
</optgroup>
</select>
</code></pre>
<h2 id="-1wicket-extension">## 1。Wicket Extension</h2>
<p>要使用“<code>Select</code>”标签,您需要获得“<strong>wicket-extensions</strong>jar。</p>
<p>文件:pom.xml</p>
<pre><code class="language-java"> <project ...>
<dependencies>
<dependency>
<groupId>org.apache.wicket</groupId>
<artifactId>wicket</artifactId>
<version>1.4.17</version>
</dependency>
<dependency>
<groupId>org.apache.wicket</groupId>
<artifactId>wicket-extensions</artifactId>
<version>1.4.17</version>
</dependency>
<!-- slf4j-log4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.5.6</version>
</dependency>
</dependencies>
</project>
</code></pre>
<h2 id="2wicket-选择示例">2.Wicket 选择示例</h2>
<p>显示下拉框的示例,使用 <strong>< optgroup ></strong> 标签,通过“<code>Select</code>”和“<code>SelectOption</code>”标签对相关选项进行分组。</p>
<pre><code class="language-java"> import org.apache.wicket.PageParameters;
import org.apache.wicket.extensions.markup.html.form.select.Select;
import org.apache.wicket.extensions.markup.html.form.select.SelectOption;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;
public class SelectPage extends WebPage {
private String selected = "jQuery";
public SelectPage(final PageParameters parameters) {
add(new FeedbackPanel("feedback"));
Form<?> form = new Form<Void>("form") {
@Override
protected void onSubmit() {
info("Selected language : " + selected);
}
};
add(form);
Select languages = new Select("languages", new PropertyModel<String>(
this, "selected"));
form.add(languages);
languages.add(new SelectOption<String>("framework1", new Model<String>(
"Wicket")));
languages.add(new SelectOption<String>("framework2", new Model<String>(
"Spring MVC")));
languages.add(new SelectOption<String>("framework3", new Model<String>(
"JSF 2.0")));
languages.add(new SelectOption<String>("Script1", new Model<String>(
"jQuery")));
languages.add(new SelectOption<String>("Script2", new Model<String>(
"prototype")));
}
}
</code></pre>
<h2 id="3wicket-html-页面">3.Wicket HTML 页面</h2>
<p>匹配上述 Wicket 代码的 HTML 代码。</p>
<pre><code class="language-java"> <html>
<head>
<style>
.feedbackPanelINFO {
color: green;
}
</style>
</head>
<body>
<h1>Wicket Select example</h1>
<div wicket:id="feedback"></div>
<form wicket:id="form">
<p>
<label>Select your favor language </label>
<br />
<select wicket:id="languages">
<optgroup label="Frameworks">
<option wicket:id="framework1" >Wicket (1.4.7)</option>
<option wicket:id="framework2" >Spring MVC (3.0)</option>
<option wicket:id="framework3" >JSF (2.0)</option>
</optgroup>
<optgroup label="JavaScript">
<option wicket:id="Script1" >jQuery (1.6.1)</option>
<option wicket:id="Script2" >prototype (1.7)</option>
</optgroup>
</select>
</p>
<input type="submit" value="Display" />
</form>
</body>
</html>
</code></pre>
<h2 id="4演示-5">4.演示</h2>
<p>开始并访问—<em><a href="http://localhost:8080/wicket" target="_blank">http://localhost:8080/wicket</a> examples/</em></p>
<p>默认情况下选择“jQuery”。</p>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/5ffb756d7d8e867975b9cee31f7232b0.png" alt="wicket select example" title="wicket-select-example1" loading="lazy"></p>
<p>选择“Spring MVC”并点击显示按钮。</p>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/ff4ae1a3ab2d6a8a021dad195931f484.png" alt="wicket select example" title="wicket-select-example2" loading="lazy">Download it – <a href="http://web.archive.org/web/20190202032227/http://www.mkyong.com/wp-content/uploads/2011/05/Wicket-Select-Example.zip" target="_blank">Wicket-Select-Example.zip</a> (7KB)</p>
<h2 id="参考-43">参考</h2>
<ol>
<li><a href="http://web.archive.org/web/20190202032227/http://wicket.apache.org/apidocs/1.4/org/apache/wicket/extensions/markup/html/form/select/Select.html" target="_blank">Wicket Select Javadoc</a></li>
<li><a href="http://web.archive.org/web/20190202032227/http://wicket.apache.org/apidocs/1.4/org/apache/wicket/extensions/markup/html/form/select/SelectOption.html" target="_blank">Wicket select option Javadoc</a></li>
<li><a href="http://web.archive.org/web/20190202032227/http://www.w3schools.com/tags/tag_optgroup.asp" target="_blank">HTML optgroup 标签</a></li>
</ol>
<p><a href="http://web.archive.org/web/20190202032227/http://www.mkyong.com/tag/wicket/" target="_blank">wicket</a><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/9e519e48c42a1fb7372c36c8642924ba.png" alt="" loading="lazy"> (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=<mark>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]<a href="h,i%5Bl%5D%5Bq+y%5D(h)+'&'+dt" target="_blank">w+y</a>;}catch(er){i[h]=dt;} } else if(typeof i[c]!</mark>'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','<a href="//web.archive.org/web/20190202032227/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0" target="_blank">//web.archive.org/web/20190202032227/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0</a>')<input type="hidden" id="mkyong-postId" value="9067"></p>
<h1 id="wicket--spring-集成示例">Wicket + Spring 集成示例</h1>
<blockquote>
<p>原文:<a href="http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/wicket-spring-integration-example/" target="_blank">http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/wicket-spring-integration-example/</a></p>
</blockquote>
<p>本教程演示了如何将 Wicket 与 Spring 框架集成在一起。</p>
<p>本文中的库:</p>
<ol>
<li>Wicket v1.4.17</li>
<li>小门弹簧 v1.4.17</li>
<li>Spring v3.0.5.RELEASE</li>
</ol>
<h2 id="1项目结构-4">1.项目结构</h2>
<p>本教程的最终项目目录结构,没什么特别的,只是一个标准的 Maven 项目。</p>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/fa27945bc4173afa430b696ca1ce7419.png" alt="wicket spring example" title="wicket-spring-example" loading="lazy"></p>
<h2 id="2项目依赖性">2.项目依赖性</h2>
<p>获取 Wicket 和 Spring 的依赖关系,要集成两者,需要" <strong>wicket-spring.jar</strong> "。</p>
<pre><code class="language-java"> <project ..>
<dependencies>
<!-- Wicket framework-->
<dependency>
<groupId>org.apache.wicket</groupId>
<artifactId>wicket</artifactId>
<version>1.4.17</version>
</dependency>
<!-- Integrate Wicket with Spring -->
<dependency>
<groupId>org.apache.wicket</groupId>
<artifactId>wicket-spring</artifactId>
<version>1.4.17</version>
</dependency>
<!-- Spring framework -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>3.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>3.0.5.RELEASE</version>
</dependency>
<!-- slf4j-log4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.5.6</version>
</dependency>
</dependencies>
</project>
</code></pre>
<h2 id="3春豆">3.春豆</h2>
<p>创建一个 Spring bean,用 <strong>@Service</strong> 对其进行注释。</p>
<pre><code class="language-java"> package com.mkyong.user;
public interface HelloService {
String getHelloWorldMsg();
}
</code></pre>
<pre><code class="language-java"> package com.mkyong.user;
import org.springframework.stereotype.Service;
@Service
public class HelloServiceImpl implements HelloService {
public String getHelloWorldMsg() {
return "Spring : hello world";
}
}
</code></pre>
<h2 id="4注入弹簧容器">4.注入弹簧容器</h2>
<p>创建一个标准的 Spring<strong>application context . XML</strong>文件,启用自动组件扫描特性。</p>
<p><em>文件:applicationContext.xml</em></p>
<pre><code class="language-java"> <beans
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
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">
<context:component-scan base-package="com.mkyong.user" />
</beans>
</code></pre>
<h2 id="5带弹簧的集成小门">5.带弹簧的集成小门</h2>
<p>用这个“<code>addComponentInstantiationListener(new SpringComponentInjector(this));</code>”覆盖 Wicket 应用程序 <strong>init()</strong> 方法。</p>
<p><em>文件:Wicket 应用程序类</em></p>
<pre><code class="language-java"> package com.mkyong;
import org.apache.wicket.protocol.http.WebApplication;
import org.apache.wicket.spring.injection.annot.SpringComponentInjector;
import com.mkyong.user.SimplePage;
public class WicketApplication extends WebApplication {
@Override
public Class<SimplePage> getHomePage() {
return SimplePage.class; // return default page
}
@Override
protected void init() {
super.init();
addComponentInstantiationListener(new SpringComponentInjector(this));
}
}
</code></pre>
<p>现在,您可以通过 <strong>@SpringBean</strong> 将 Spring bean 注入 Wicket 组件。</p>
<pre><code class="language-java"> package com.mkyong.user;
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.spring.injection.annot.SpringBean;
public class SimplePage extends WebPage {
@SpringBean
private HelloService helloService;
public SimplePage(final PageParameters parameters) {
add(new Label("msg", helloService.getHelloWorldMsg()));
}
}
</code></pre>
<pre><code class="language-java"> <html>
<body>
<h1>Wicket + Spring integration example</h1>
<h2 wicket:id="msg"></h2>
</body>
</html>
</code></pre>
<h2 id="6webxml">6.web.xml</h2>
<p>最后一步,让你的 web 项目知道什么是 Wicket 和 Spring。在<code>web.xml</code>中声明两者。</p>
<p><em>文件:web.xml</em></p>
<pre><code class="language-java"> <?xml version="1.0" encoding="ISO-8859-1"?>
<web-app
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
<display-name>Wicket Web Application</display-name>
<filter>
<filter-name>wicket.wicketTest</filter-name>
<filter-class>org.apache.wicket.protocol.http.WicketFilter</filter-class>
<init-param>
<param-name>applicationClassName</param-name>
<param-value>com.mkyong.WicketApplication</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>wicket.wicketTest</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
</web-app>
</code></pre>
<h2 id="7演示-2">7.演示</h2>
<p>开始并访问-<em><a href="http://localhost:8080/wicket" target="_blank">http://localhost:8080/wicket</a> examples/</em>。</p>
<p>一个简单的 Wicket 页面,消息从 Spring 返回。</p>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/122b885c5081da89a0fd1e750efdd2f3.png" alt="wicket spring demo" title="wicket-spring-example-demo" loading="lazy">Download it – <a href="http://web.archive.org/web/20210110024944/http://www.mkyong.com/wp-content/uploads/2011/06/Wicket-Spring-Integration-Example.zip" target="_blank">Wicket-Spring-Integration-Example.zip</a> (8KB)</p>
<h2 id="参考-44">参考</h2>
<ol>
<li><a href="http://web.archive.org/web/20210110024944/https://cwiki.apache.org/WICKET/spring.html" target="_blank">小门+弹簧详细说明</a></li>
<li><a href="http://web.archive.org/web/20210110024944/http://blog.springsource.com/2009/12/02/obtaining-spring-3-artifacts-with-maven/" target="_blank">Spring Maven 详情</a></li>
<li><a href="http://web.archive.org/web/20210110024944/http://www.mkyong.com/spring/spring-auto-scanning-components/" target="_blank">弹簧自动组件扫描</a></li>
</ol>
<p>Tags : <a href="http://web.archive.org/web/20210110024944/https://mkyong.com/tag/integration/" target="_blank">integration</a> <a href="http://web.archive.org/web/20210110024944/https://mkyong.com/tag/spring/" target="_blank">spring</a> <a href="http://web.archive.org/web/20210110024944/https://mkyong.com/tag/wicket/" target="_blank">wicket</a><input type="hidden" id="mkyong-current-postId" value="9121"></p>
<h3 id="相关文章-1">相关文章</h3>
<ul>
<li>
<p><a href="/web/20210110024944/https://www.mkyong.com/wicket/wicket-log4j-integration-example/" target="_blank">Wicket + Log4j 集成示例</a></p>
</li>
<li>
<p><a href="/web/20210110024944/https://www.mkyong.com/spring/maven-spring-jdbc-example/" target="_blank">春天+ JDBC 的例子</a></p>
</li>
<li>
<p><a href="/web/20210110024944/https://www.mkyong.com/spring/spring-jdbctemplate-jdbcdaosupport-examples/" target="_blank">Spring+JDBC template+JdbcDaoSupport 示例</a></p>
</li>
<li>
<p><a href="/web/20210110024944/https://www.mkyong.com/spring/spring-jdk-timer-scheduler-example/" target="_blank">Spring + JDK 定时器调度器示例</a></p>
</li>
<li>
<p><a href="/web/20210110024944/https://www.mkyong.com/spring/spring-quartz-scheduler-example/" target="_blank">Spring 3 + Quartz 1.8.6 调度器示例</a></p>
</li>
<li>
<p><a href="/web/20210110024944/https://www.mkyong.com/struts/struts-spring-integration-example/" target="_blank">Struts + Spring 集成示例</a></p>
</li>
<li>
<p><a href="/web/20210110024944/https://www.mkyong.com/struts/struts-spring-hibernate-integration-example/" target="_blank">Struts + Spring + Hibernate 集成示例</a></p>
</li>
<li>
<p><a href="/web/20210110024944/https://www.mkyong.com/struts/struts-spring-quartz-scheduler-integration-example/" target="_blank">Struts 1+Spring 2 . 5 . 6+Quartz 1.6 调度器 exa</a></p>
</li>
<li>
<p><a href="/web/20210110024944/https://www.mkyong.com/struts2/struts-2-spring-integration-example/" target="_blank">Struts 2 + Spring 集成示例</a></p>
</li>
<li>
<p><a href="/web/20210110024944/https://www.mkyong.com/struts2/struts-2-spring-quartz-scheduler-integration-example/" target="_blank">Struts 2+Spring 2 . 5 . 6+Quartz 1.6 调度器 int</a></p>
</li>
</ul>
<h1 id="wicket-textarea-示例">Wicket textarea 示例</h1>
<blockquote>
<p>原文:<a href="http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/wicket-textarea-example/" target="_blank">http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/wicket-textarea-example/</a></p>
</blockquote>
<p>Wicket 教程向您展示了如何创建一个 textarea,多行文本编辑组件,通常用于地址字段。</p>
<pre><code class="language-java"> //Java
import org.apache.wicket.markup.html.form.TextArea;
...
final TextArea<String> address = new TextArea<String>("address",Model.of(""));
form.add(address);
//HTML
<textarea wicket:id="address" rows="6" cols="40"></textarea>
</code></pre>
<h2 id="1wicket-textarea-示例">1.Wicket textarea 示例</h2>
<p>为地址呈现文本区域字段的代码。</p>
<p><em>文件:UserPage.java</em></p>
<pre><code class="language-java"> package com.mkyong.user;
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.TextArea;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.model.Model;
public class UserPage extends WebPage {
public UserPage(final PageParameters parameters) {
add(new FeedbackPanel("feedback"));
//create a textarea field for address
final TextArea<String> address = new TextArea<String>("address",Model.of(""));
address.setRequired(true);
address.setLabel(Model.of("Address"));
Form<?> form = new Form<Void>("userForm") {
@Override
protected void onSubmit() {
PageParameters pageParameters = new PageParameters();
pageParameters.add("address", address.getModelObject());
setResponsePage(SuccessPage.class, pageParameters);
}
};
add(form);
form.add(address);
}
}
</code></pre>
<h2 id="2wicket-html-页面-6">2.Wicket HTML 页面</h2>
<p>呈现 textarea 字段的页面。</p>
<p><em>File : UserPage.html</em></p>
<pre><code class="language-java"> <html>
<head>
<style>
label {
background-color: #eee;
padding: 4px;
float:left;
}
.feedbackPanelERROR {
color: red;
}
</style>
</head>
<body>
<h1>Wicket TextArea Example</h1>
<div wicket:id="feedback"></div>
<form wicket:id="userForm">
<p>
<label>Address :</label>
<textarea wicket:id="address" rows="6" cols="40"></textarea>
</p>
<input type="submit" value="Register" />
</form>
</body>
</html>
</code></pre>
<h2 id="3演示-8">3.演示</h2>
<p>开始并访问—<em><a href="http://localhost:8080/wicket" target="_blank">http://localhost:8080/wicket</a> examples/</em></p>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/844dc010651cb6e4fe091f243a5b39f5.png" alt="wicket textarea address field" title="wicket-textarea-example" loading="lazy">Download it – <a href="http://web.archive.org/web/20190301152104/http://www.mkyong.com/wp-content/uploads/2011/05/Wicket-textarea-example.zip" target="_blank">Wicket-textarea-example.zip</a> (8KB)</p>
<h2 id="参考-45">参考</h2>
<ol>
<li><a href="http://web.archive.org/web/20190301152104/http://wicket.apache.org/apidocs/1.4/org/apache/wicket/markup/html/form/TextArea.html" target="_blank">Wicket TextArea Javadoc</a></li>
</ol>
<p><a href="http://web.archive.org/web/20190301152104/http://www.mkyong.com/tag/textarea/" target="_blank">textarea</a> <a href="http://web.archive.org/web/20190301152104/http://www.mkyong.com/tag/wicket/" target="_blank">wicket</a></p>
<h1 id="wicket-文本框示例">Wicket 文本框示例</h1>
<blockquote>
<p>原文:<a href="http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/wicket-textbox-example/" target="_blank">http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/wicket-textbox-example/</a></p>
</blockquote>
<p>Wicket 教程向你展示了如何创建一个文本框,验证它,并将文本框的值传递到下一页。</p>
<pre><code class="language-java"> //Java
import org.apache.wicket.markup.html.form.TextField;
...
final TextField<String> username = new TextField<String>("username", Model.of(""));
form.add(username);
//HTML
<input wicket:id="username" type="text" size="20" />
</code></pre>
<h2 id="1wicket-文本框示例">1.Wicket 文本框示例</h2>
<p>通过 Wicket 的<code>TextField</code>呈现文本框的用户页面。</p>
<p><em>文件:UserPage.java</em></p>
<pre><code class="language-java"> package com.mkyong.user;
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.model.Model;
public class UserPage extends WebPage {
public UserPage(final PageParameters parameters) {
add(new FeedbackPanel("feedback"));
final TextField<String> username = new TextField<String>("username",
Model.of(""));
username.setRequired(true);
username.add(new UsernameValidator());
Form<?> form = new Form<Void>("userForm") {
@Override
protected void onSubmit() {
final String usernameValue = username.getModelObject();
PageParameters pageParameters = new PageParameters();
pageParameters.add("username", usernameValue);
setResponsePage(SuccessPage.class, pageParameters);
}
};
add(form);
form.add(username);
}
}
</code></pre>
<p><em>File : UserPage.html</em></p>
<pre><code class="language-java"> <html>
<head>
<style>
label {
background-color: #eee;
padding: 4px;
}
.feedbackPanelERROR {
color: red;
}
</style>
</head>
<body>
<h1>Wicket TextBox Example - UserPage.html</h1>
<div wicket:id="feedback"></div>
<form wicket:id="userForm">
<p>
<label>Username</label>:
<input wicket:id="username" type="text" size="20" />
</p>
<input type="submit" value="Register" />
</form>
</body>
</html>
</code></pre>
<h2 id="2用户名验证器">2.用户名验证器</h2>
<p>一个用户名验证器,以确保用户名的长度在 5 到 15 之间,在字符或符号 a-z,0-9,下划线,连字符。</p>
<p><em>文件:UsernameValidator.java</em></p>
<pre><code class="language-java"> package com.mkyong.user;
import org.apache.wicket.validation.CompoundValidator;
import org.apache.wicket.validation.validator.PatternValidator;
import org.apache.wicket.validation.validator.StringValidator;
public class UsernameValidator extends CompoundValidator<String> {
private static final long serialVersionUID = 1L;
public UsernameValidator() {
add(StringValidator.lengthBetween(5, 15));
add(new PatternValidator("[a-z0-9_-]+"));
}
}
</code></pre>
<h2 id="3显示文本框值">3.显示文本框值</h2>
<p>显示来自<strong>UserPage.html</strong>的文本框值的页面。</p>
<p><em>文件:SuccessPage.java</em></p>
<pre><code class="language-java"> package com.mkyong.user;
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.WebPage;
public class SuccessPage extends WebPage {
public SuccessPage(final PageParameters parameters) {
String username = "";
if(parameters.containsKey("username")){
username = parameters.getString("username");
}
final Label result = new Label("result", "Username : " + username);
add(result);
}
}
</code></pre>
<p><em>文件:SuccessPage.html</em></p>
<pre><code class="language-java"> <html>
<body>
<h1>Wicket TextBox Example - SuccessPage.html</h1>
<label wicket:id="result"></label>
</body>
</html>
</code></pre>
<h2 id="4演示-6">4.演示</h2>
<p>开始并访问—<em><a href="http://localhost:8080/wicket" target="_blank">http://localhost:8080/wicket</a> examples/</em></p>
<p>如果文本框有错误:</p>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/a9fee054551a3eeba8179110831eafd7.png" alt="wicket textbox example" title="wicket-textbox-example1" loading="lazy"></p>
<p>如果文本框没有错误,导航到下一页并显示文本框值。</p>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/a857756edda29e30434f2bab3bed1740.png" alt="wicket textbox" title="wicket-textbox-example2" loading="lazy">Download it – <a href="http://web.archive.org/web/20190304030558/http://www.mkyong.com/wp-content/uploads/2011/05/Wicket-textbox-examples.zip" target="_blank">Wicket-textbox-examples.zip</a> (8KB)</p>
<h2 id="参考-46">参考</h2>
<ol>
<li><a href="http://web.archive.org/web/20190304030558/http://www.mkyong.com/regular-expressions/how-to-validate-username-with-regular-expression/" target="_blank">用正则表达式验证用户名</a></li>
<li><a href="http://web.archive.org/web/20190304030558/http://wicket.apache.org/apidocs/1.4/org/apache/wicket/markup/html/form/TextField.html" target="_blank">Wicket TextField Javadoc</a></li>
</ol>
<p><a href="http://web.archive.org/web/20190304030558/http://www.mkyong.com/tag/textbox/" target="_blank">textbox</a> <a href="http://web.archive.org/web/20190304030558/http://www.mkyong.com/tag/wicket/" target="_blank">wicket</a></p>
<h1 id="wicket-教程">Wicket 教程</h1>
<blockquote>
<p>原文:<a href="http://web.archive.org/web/20230101150211/https://mkyong.com/tutorials/wicket-tutorials/" target="_blank">http://web.archive.org/web/20230101150211/https://mkyong.com/tutorials/wicket-tutorials/</a></p>
</blockquote>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/cf2dbc2f55dc7f08242fefdd4e76eff7.png" alt="wicket tutorial" title="wicket-tutorials" loading="lazy"></p>
<p>Apache Wicket 是一个简单且功能丰富的基于组件的 web 框架,真正的可重用组件是这个框架的主要卖点。然而,由于基于组件和 MVC 架构之间的巨大差异,这使得 Wicket 很难学习,尤其是对于那些经典的 MVC 开发人员。</p>
<p>在本教程中,它提供了许多关于使用 Apache Wicket 1.4 的分步示例和解释。</p>
<h2 id="基本和配置">基本和配置</h2>
<p>Wicket hello world,基本的东西和 URL 配置。</p>
<ul>
<li><a href="http://web.archive.org/web/20190213133126/http://www.mkyong.com/wicket/wicket-hello-world-example-with-maven-tutorial/" target="_blank">Wicket Hello World 示例</a><br>
一个简单的 Hello World 探索 Wicket web 应用的基本结构。</li>
<li><a href="http://web.archive.org/web/20190213133126/http://www.mkyong.com/wicket/how-do-setup-wicket-examples-in-eclipse/" target="_blank">在本地环境中设置 Wicket 示例</a><br>
在您的本地开发环境中设置一个克隆的 Wicket 示例站点以供快速参考。</li>
<li><a href="http://web.archive.org/web/20190213133126/http://www.mkyong.com/wicket/wicket-pageparameters-example/" target="_blank">PageParameters 示例</a><br>
学习如何将参数传递给另一个页面。</li>
<li><a href="http://web.archive.org/web/20190213133126/http://www.mkyong.com/wicket/how-do-change-wicket-url-bookmarkablepage-structure-url-mounting/" target="_blank">使 Wicket URL bookmarkablePage 结构友好</a><br>
默认情况下,Wicket 生成的 URL 很难看,这里有一种方法可以使 URL 更加整洁友好。</li>
<li><a href="http://web.archive.org/web/20190213133126/http://www.mkyong.com/wicket/how-do-encrypt-encode-url-in-wicket/" target="_blank">在 Wicket 中加密 URL</a><br>
在 Wicket 中加密 URL 的简单方法。</li>
</ul>
<h2 id="模型">模型</h2>
<p>Wicket 模型示例,从组件绑定到对象属性,反之亦然。</p>
<ul>
<li><a href="http://web.archive.org/web/20190213133126/http://www.mkyong.com/wicket/wicket-propertymodel-example/" target="_blank">PropertyModel 示例</a><br>
使用 PropertyModel 将表单组件绑定到对象属性。</li>
<li><a href="http://web.archive.org/web/20190213133126/http://www.mkyong.com/wicket/wicket-compoundpropertymodel-example/" target="_blank">CompoundPropertyModel 示例</a><br>
使用 CompoundPropertyModel 将表单组件绑定到对象属性。</li>
</ul>
<h2 id="表单组件">表单组件</h2>
<p>Wicket 表单组件示例。</p>
<ul>
<li><a href="http://web.archive.org/web/20190213133126/http://www.mkyong.com/wicket/wicket-textbox-example/" target="_blank">Textbox 示例</a><br>
使用 TextField 来呈现一个用户名文本框,并附加了一个简单的用户名验证器。</li>
<li><a href="http://web.archive.org/web/20190213133126/http://www.mkyong.com/wicket/wicket-password-field-example/" target="_blank">密码字段示例</a><br>
使用 PasswordTextField 来呈现密码字段,并附加了一个强大的密码验证器。</li>
<li><a href="http://web.archive.org/web/20190213133126/http://www.mkyong.com/wicket/wicket-textarea-example/" target="_blank">Textarea 示例</a><br>
使用 Textarea 呈现多行地址栏。</li>
<li><a href="http://web.archive.org/web/20190213133126/http://www.mkyong.com/wicket/wicket-checkbox-example/" target="_blank">复选框示例</a><br>
使用 Checkbox 渲染复选框,默认选中。</li>
<li><a href="http://web.archive.org/web/20190213133126/http://www.mkyong.com/wicket/wicket-multiple-checkboxes-example-checkboxmultiplechoice/" target="_blank">多个复选框示例</a><br>
使用 CheckBoxMultipleChoice 呈现多个复选框。</li>
<li><a href="http://web.archive.org/web/20190213133126/http://www.mkyong.com/wicket/wicket-radio-buttons-example-radiochoice/" target="_blank">单选按钮示例</a><br>
使用 RadioChoice 呈现一组单选按钮。</li>
<li><a href="http://web.archive.org/web/20190213133126/http://www.mkyong.com/wicket/wicket-listchoice-example/" target="_blank">单选列表框示例</a><br>
使用 ListChoice 呈现单选可滚动列表。</li>
<li><a href="http://web.archive.org/web/20190213133126/http://www.mkyong.com/wicket/wicket-listmultiplechoice-example/" target="_blank">多选列表框示例</a><br>
使用 ListMultipleChoice 呈现多选可滚动列表。</li>
<li><a href="http://web.archive.org/web/20190213133126/http://www.mkyong.com/wicket/wicket-dropdown-box-example-dropdownchoice/" target="_blank">下拉框示例</a><br>
使用 DropDownChoice 来呈现下拉框选项。</li>
<li><a href="http://web.archive.org/web/20190213133126/http://www.mkyong.com/wicket/wicket-file-upload-example/" target="_blank">文件上传示例</a><br>
使用 FileUploadField 来呈现文件上传组件。</li>
<li><a href="http://web.archive.org/web/20190213133126/http://www.mkyong.com/wicket/wicket-select-example/" target="_blank">Select 和 SelectOption 示例</a><br>
Wicket 扩展——使用“Select”和“SelectOption”呈现一个下拉框,该下拉框用< optgroup >标签将相关选项分组。</li>
<li><a href="http://web.archive.org/web/20190213133126/http://www.mkyong.com/wicket/wicket-palette-example/" target="_blank">调色板示例</a><br>
Wicket 扩展——使用“调色板”来呈现两个选择框,并允许用户将项目从一个选择框移动到另一个选择框。</li>
</ul>
<h2 id="验证器">验证器</h2>
<p>Wicket 中的自定义验证器。</p>
<ul>
<li><a href="http://web.archive.org/web/20190213133126/http://www.mkyong.com/wicket/create-custom-validator-in-wicket/" target="_blank">创建自定义验证器</a><br>
示例创建自定义验证器。</li>
<li><a href="http://web.archive.org/web/20190213133126/http://www.mkyong.com/wicket/notequalinputvalidator-in-wicket/" target="_blank">自定义 notequilinputvalidator</a><br>
另一个自定义验证器来确保两个组件不相等。</li>
</ul>
<h2 id="wicket-集成">Wicket 集成</h2>
<p>将 Wicket 与其他人集成。</p>
<ul>
<li><a href="http://web.archive.org/web/20190213133126/http://www.mkyong.com/wicket/wicket-spring-integration-example/" target="_blank">Wicket + Spring 集成示例</a><br>
将 Wicket 与 Spring 框架轻松集成的示例。</li>
<li><a href="http://web.archive.org/web/20190213133126/http://www.mkyong.com/wicket/how-do-integrate-kaptcha-in-wicket-solution/" target="_blank">Wicket + Kaptcha 集成示例</a><br>
通过 Spring 将 Kaptcha 与 Wicket 框架集成的示例。</li>
<li><a href="http://web.archive.org/web/20190213133126/http://www.mkyong.com/wicket/wicket-log4j-integration-example/" target="_blank">Wicket + Log4j 集成实例</a><br>
如何将 Log4j 与 Wicket 集成,通过 slf4j-log4j 桥绑定。</li>
</ul>
<h2 id="常见问题">常见问题</h2>
<p>Wicket 中的一些常见问题。</p>
<ul>
<li><a href="http://web.archive.org/web/20190213133126/http://www.mkyong.com/wicket/how-do-change-the-html-file-location-wicket/" target="_blank">改变 html 文件在 Wicket</a><br>
中的位置由于某些原因,你想把 java 和 html 文件分开放在不同的文件夹中,这里有几种方法可以做到。</li>
<li><a href="http://web.archive.org/web/20190213133126/http://www.mkyong.com/wicket/how-do-get-servletcontext-in-wicket/" target="_blank">在 Wicket 中获取 servlet context</a><br>
servlet context 对于提供 web 信息很有用,向你展示如何在 Wicket 中获取它。</li>
<li><a href="http://web.archive.org/web/20190213133126/http://www.mkyong.com/wicket/how-to-change-wicket-to-deployment-mode/" target="_blank">将 Wicket 更改为部署模式</a><br>
将 Wicket 从开发模式更改为部署模式的两种方法。</li>
<li><a href="http://web.archive.org/web/20190213133126/http://www.mkyong.com/wicket/how-to-call-javscript-after-ajax-update-wicket/" target="_blank">AJAX 更新后调用 Javascript</a><br>
向你展示如何在 AJAX 更新后运行 Javascript 代码。</li>
<li><a href="http://web.archive.org/web/20190213133126/http://www.mkyong.com/wicket/wicket-fileupload-validator-is-not-execute/" target="_blank">文件上传验证器被忽略</a><br>
如果没有选择文件,文件上传验证器将不执行!解决方案。</li>
<li><a href="http://web.archive.org/web/20190213133126/http://www.mkyong.com/wicket/how-do-configure-404-error-page-in-wicket-solution/" target="_blank">在 Wicket</a><br>
中配置 404 错误页面如何为找不到的 url 配置 404 错误页面。</li>
<li><a href="http://web.archive.org/web/20190213133126/http://www.mkyong.com/wicket/how-do-get-root-context-of-a-web-application-in-wicket/" target="_blank">获取 web 应用的根上下文</a><br>
获取 Wicket 项目根上下文的常用方法。</li>
<li><a href="http://web.archive.org/web/20190213133126/http://www.mkyong.com/wicket/how-do-detect-browser-javascript-or-ajax-disabled-in-wicket/" target="_blank">检测浏览器是否支持 JavaScript 或 Ajax</a><br>
Wicket 方式检测浏览器是否支持 JavaScript 或 Ajax</li>
<li><a href="http://web.archive.org/web/20190213133126/http://www.mkyong.com/wicket/how-to-dynamic-add-attribute-to-a-html-tag-in-wicket/" target="_blank">向 HTML 标签中动态添加属性</a><br>
示例动态操作 HTML 标签中的属性。</li>
<li><a href="http://web.archive.org/web/20190213133126/http://www.mkyong.com/wicket/how-do-use-ajaxlazyloadpanel-in-wicket/" target="_blank">Wicket</a><br>
中的 AjaxLazyLoadPanel 示例使用 AjaxLazyLoadPanel 来启用惰性加载效果。</li>
</ul>
<h2 id="常见错误">常见错误</h2>
<p>Wicket 中的一些常见错误消息。</p>
<ul>
<li><a href="http://web.archive.org/web/20190213133126/http://www.mkyong.com/wicket/java-lang-classnotfoundexception-org-slf4j-impl-staticloggerbinder/" target="_blank">Java . lang . classnotfoundexception:org . slf4j . impl . staticloggerbinder</a></li>
<li><a href="http://web.archive.org/web/20190213133126/http://www.mkyong.com/wicket/enable-debug-messages-for-org-apache-wicket-util-resource/" target="_blank">启用 org . Apache . wicket . util . resource 的调试消息</a></li>
</ul>
<h2 id="wicket-参考">Wicket 参考</h2>
<ul>
<li><a href="http://web.archive.org/web/20190213133126/http://wicket.apache.org/" target="_blank">Apache Wicket 官网</a></li>
<li><a href="http://web.archive.org/web/20190213133126/http://wicketstuff.org/wicket14/" target="_blank">Apache Wicket 示例</a></li>
<li><a href="http://web.archive.org/web/20190213133126/https://cwiki.apache.org/WICKET/framework-documentation.html" target="_blank">Apache Wicket Wiki 文档</a></li>
<li><a href="http://web.archive.org/web/20190213133126/http://wicket.apache.org/apidocs/1.4/index.html?index-all.html" target="_blank">Apache Wicket API JavaDocs</a></li>
</ul>
<h2 id="下一步是什么">下一步是什么?</h2>
<p>为了研究更多关于 Wicket 框架的话题,我推荐下面两本很棒的 Wicket 书籍。</p>
<p><a href="http://web.archive.org/web/20190213133126/http://www.amazon.com/gp/product/1932394982/ref=as_li_tf_tl?ie=UTF8&tag=mkyong-recommend-20&linkCode=as2&camp=217153&creative=399353&creativeASIN=1932394982" title="Wicket in Action" target="_blank"><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/55e6c274be845d90bd268b22a64f4b7d.png" alt="Wicket in Action" loading="lazy"></a><a href="http://web.archive.org/web/20190213133126/http://www.amazon.com/gp/product/1849511608/ref=as_li_tf_tl?ie=UTF8&tag=mkyong-recommend-20&linkCode=as2&camp=217153&creative=399701&creativeASIN=1849511608" title="Apache Wicket Cookbook" target="_blank"><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/f73b4808872c2f3c1171e3782048ba8e.png" alt="Apache Wicket Cookbook" loading="lazy"></a><a href="http://web.archive.org/web/20190213133126/http://www.mkyong.com/tag/tutorials/" target="_blank">tutorials</a> <a href="http://web.archive.org/web/20190213133126/http://www.mkyong.com/tag/wicket/" target="_blank">wicket</a><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/46f2dab6a653435cc16bd4aaebc909ac.png" alt="" loading="lazy"> (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=<mark>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]<a href="h,i%5Bl%5D%5Bq+y%5D(h)+'&'+dt" target="_blank">w+y</a>;}catch(er){i[h]=dt;} } else if(typeof i[c]!</mark>'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','<a href="//web.archive.org/web/20190213133126/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0" target="_blank">//web.archive.org/web/20190213133126/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0</a>')<input type="hidden" id="mkyong-postId" value="9134"></p>
<h1 id="使用-struts-2-操作">使用 Struts 2 操作</h1>
<blockquote>
<p>原文:<a href="http://web.archive.org/web/20230101150211/http://www.mkyong.com/struts2/working-with-struts-2-actions/" target="_blank">http://web.archive.org/web/20230101150211/http://www.mkyong.com/struts2/working-with-struts-2-actions/</a></p>
</blockquote>
<p>在 Struts 2 中,您将花费大部分时间处理动作。action 类包含业务逻辑、检索资源包、保存数据、验证和选择应该发送回用户的查看结果页面。这是 Struts 2 的核心,所以您必须理解动作的基本概念。</p>
<h2 id="1行动-7">1.行动</h2>
<p>Struts 2 动作不强迫你实现任何接口或扩展类,它只要求你实现一个 <strong>execute()</strong> 方法,该方法返回一个字符串来指示哪个结果页面应该返回。</p>
<pre><code class="language-java"> package com.mkyong.user.action;
public class LoginAction{
//business logic
public String execute() {
return "success";
}
}
</code></pre>
<p>在 <strong>struts.xml</strong> 中,用<strong>动作标签</strong>和<strong>类属性</strong>配置动作类。使用<strong>结果标签</strong>定义哪个结果页面应该返回给用户,并使用<strong>名称属性</strong>定义可以用来访问这个操作类的操作名称。</p>
<pre><code class="language-java"> <package name="user" namespace="/User" extends="struts-default">
<action name="validateUser" class="com.mkyong.user.action.LoginAction">
<result name="success">pages/welcome.jsp</result>
</action>
<package>
</code></pre>
<p>现在,您可以通过后缀来访问该操作。动作扩展。</p>
<pre><code class="language-java"> http://localhost:8080/Struts2Example/User/validateUser.action
</code></pre>
<p>The default .action is configurable, just change the “<a href="http://web.archive.org/web/20190225092953/http://www.mkyong.com/struts2/how-to-remove-the-action-suffix-extension-in-struts-2/" target="_blank">struts.action.extension</a>” value to suit your need. ## 2.可选操作界面</p>
<p>Struts 2 自带可选的动作接口(<strong>com . open symphony . xwork 2 . action</strong>)。通过实现这个接口,它带来了一些方便的好处,参见源代码:</p>
<pre><code class="language-java"> package com.opensymphony.xwork2;
public interface Action {
public static final String SUCCESS = "success";
public static final String NONE = "none";
public static final String ERROR = "error";
public static final String INPUT = "input";
public static final String LOGIN = "login";
public String execute() throws Exception;
}
</code></pre>
<p>这个接口非常简单,有 5 个常用的常量:<strong>成功、错误、无、输入和逻辑</strong>。现在 action 类可以直接使用常量值了。</p>
<pre><code class="language-java"> package com.mkyong.user.action;
import com.opensymphony.xwork2.Action;
public class LoginAction{
//business logic
public String execute() {
return SUCCESS;
}
}
</code></pre>
<p>I don’t understand why many Struts developers like to implement this Action interface, it better to extend the ActionSupport. ## 3.行动支持</p>
<p>Support class, a common practice to provide default implementations of interfaces.</p>
<p>action support(<strong>com . open symphony . xwork 2 . action support</strong>),一个非常强大和方便的类,它提供了一些重要接口的默认实现:</p>
<pre><code class="language-java"> public class ActionSupport implements Action, Validateable,
ValidationAware, TextProvider, LocaleProvider, Serializable {
...
}
</code></pre>
<p><strong>ActionSupport</strong> 类让您能够:</p>
<p><strong>1。验证</strong>–声明了一个 validate()方法并将验证代码放入其中。<br>
2<strong>。文本本地化</strong>–使用 GetText()方法从资源包中获取消息。</p>
<pre><code class="language-java"> package com.mkyong.user.action;
import com.opensymphony.xwork2.ActionSupport;
public class LoginAction extends ActionSupport{
private String username;
private String password;
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
//business logic
public String execute() {
return "SUCCESS";
}
//simple validation
public void validate(){
if("".equals(getUsername())){
addFieldError("username", getText("username.required"));
}
if("".equals(getPassword())){
addFieldError("password", getText("password.required"));
}
}
}
</code></pre>
<p>In most cases, you should extends this class for the ready convenient features, unless you have reason not to. This is also a very good learning class to understand how to do the implementation of some of the important Struts 2 interfaces.</p>
<h2 id="4动作注释">4.动作注释</h2>
<p>Struts 2 对注释有很好的支持,你可以去掉 XML 文件,在你的 action 类中用 <strong>@action</strong> 替换。</p>
<pre><code class="language-java"> package com.mkyong.user.action;
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.ResultPath;
import com.opensymphony.xwork2.ActionSupport;
@Namespace("/User")
@ResultPath(value="/")
public class ValidateUserAction extends ActionSupport{
@Action(value="Welcome", results={
@Result(name="success",location="pages/welcome_user.jsp")
})
public String execute() {
return SUCCESS;
}
}
</code></pre>
<p>If you want to know more about the Struts 2 annotation, please download this <a href="http://web.archive.org/web/20190225092953/http://www.mkyong.com/struts2/struts-2-hello-world-annotation-example/" target="_blank">Struts 2 annotation example</a> for practice.</p>
<h2 id="结论-3">结论</h2>
<p>不用动脑筋,只是扩展了 <strong>ActionSupport</strong> 类,它适合大多数情况。</p>
<p><a href="http://web.archive.org/web/20190225092953/http://www.mkyong.com/tag/struts2/" target="_blank">struts2</a></p>
<h1 id="使用-struts-2-主题和模板">使用 Struts 2 主题和模板</h1>
<blockquote>
<p>原文:<a href="http://web.archive.org/web/20230101150211/http://www.mkyong.com/struts2/working-with-struts-2-theme-template/" target="_blank">http://web.archive.org/web/20230101150211/http://www.mkyong.com/struts2/working-with-struts-2-theme-template/</a></p>
</blockquote>
<p>Download It – <a href="http://web.archive.org/web/20190304032055/http://www.mkyong.com/wp-content/uploads/2010/06/Struts2-Theme-Example.zip" target="_blank">Struts2-Theme-Example.zip</a></p>
<p>在 Struts2 中,当您在 HTML 页面中放置一个“<strong>s:textfield</strong>”UI 标签时</p>
<pre><code class="language-java"> <s:textfield key="global.username" name="username" />
</code></pre>
<p>以下 2 列 HTML 表格布局设计将自动生成</p>
<pre><code class="language-java"> <tr>
<td class="tdLabel">
<label for="validateUser_username" class="label">Username:</label>
</td>
<td>
<input type="text" name="username" value="" id="validateUser_username"/>
</td>
</tr>
</code></pre>
<p>Struts 2 使用“主题和模板”特性为您生成预先设计好的表格布局。</p>
<p>让我们看看序列:</p>
<ol>
<li>Struts 2 看到一个" <strong>s:textfield</strong> "标签。</li>
<li>搜索声明的主题(如果没有声明主题,将选择默认的 xhtml 主题)。</li>
<li>搜索对应主题的模板,如“ <strong>s:textfield - > text.ftl</strong> ”、“<strong>s:password->password . ftl</strong>”。所有预定义的 HTML 布局都在 ftl 文件中定义。</li>
<li>将值绑定到模板文件中。</li>
<li>显示最终的 HTML 标记。</li>
</ol>
<p>Struts 2 tags + Theme’s template file (ftl) = Final HTML markup code.Struts 2 is using the freemaker framework as the default template engine, all ftl files are written in freemaker script. No worry, the freemarker syntax is almost in human explains words, it’s quite easy to learn.</p>
<h2 id="使用-struts-2-主题和模板-1">使用 Struts 2 主题和模板</h2>
<p>在本文中,您将创建一个新的主题来定制错误消息的位置。在默认的“xhtml”主题中,错误消息将出现在字段名称的上方。</p>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/af789219fa28e9020d0f0616c15f0803.png" alt="Struts2 xhtml theme" title="Struts2-theme-example-1" loading="lazy"></p>
<p>在新主题中,错误信息将显示在输入字段旁边,并用红色突出显示。</p>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/dd0de85dd7a1a8d3dbc023191f78157c.png" alt="Struts2 mkyong theme" title="Struts2-theme-example-2" loading="lazy"> ## 1.提取主题</p>
<p>所有的主题和模板文件都在模板文件夹 <strong>struts2-core.jar</strong> 中。将其解压缩到您的本地驱动器。</p>
<p>The ftl file is just a normal text file, you can open it with any prefer text editors. ## 2.创建新主题</p>
<p>创建一个新文件夹,复制所有现有的 xhtml 模板文件(ftl)并将新文件夹放入项目资源文件夹。</p>
<p><img src="https://github.com/OpenDocCN/geekdoc-java-zh/raw/master/mkyong-blog/img/a5711c69eb7935e3cc2576a8bf9e90ae.png" alt="Struts 2 theme folder " title="Struts2-theme-folder-structure" loading="lazy">To understand how Struts 2 find the theme template folder, read <a href="http://web.archive.org/web/20190304032055/http://struts.apache.org/2.0.14/docs/selecting-template-directory.html" target="_blank">Struts 2 template directory</a> selection for more detail.Often times, you just need to copy and modify the existing “xhtml” theme, unless you have reason not to.</p>
<h2 id="3定义新主题">3.定义新主题</h2>
<p>定义“ <strong>struts.ui.theme</strong> ”和“ <strong>struts.ui.templateDir</strong> ”来告诉 Struts 2 在哪里可以找到你的新主题和模板文件夹。</p>
<p><strong>struts.xml</strong></p>
<pre><code class="language-java"> <struts>
...
<constant name="struts.ui.theme" value="mkyong" />
<constant name="struts.ui.templateDir" value="template" />
...
</struts>
</code></pre>
<p>Now, when Struts 2 see a “<strong>s:textfield</strong>“, it will find the “mkyong” theme instead of the default “xhtml” theme. Read <a href="http://web.archive.org/web/20190304032055/http://struts.apache.org/2.0.14/docs/selecting-themes.html" target="_blank">Struts 2 select a theme</a> for more detail.</p>
<h2 id="4修改主题">4.修改主题</h2>
<p>要修改现有的模板(ftl)文件,您可能需要了解一点 <a href="http://web.archive.org/web/20190304032055/http://freemarker.sourceforge.net/" target="_blank">freemarker 语法</a>。</p>
<p>1.创建一个新的 <strong>error-message.ftl</strong> 文件来显示错误消息。</p>
<p><strong>错误消息. ftl</strong></p>
<pre><code class="language-java"> <#--
Only show message if errors are available.
This will be done if ActionSupport is used.
-->
<#assign hasFieldErrors = parameters.name?? && fieldErrors?? && fieldErrors[parameters.name]??/>
<#if hasFieldErrors>
<#list fieldErrors[parameters.name] as error>
<span class="errorMessage" errorFor="${parameters.id}">${error?html}</span><#t/>
</#list>
</#if>
</code></pre>
<p>2.如果存在错误,通过向“td”标签添加一个新的“errorsBg”类来修改 <strong>controlheader.ftl</strong> 。</p>
<p><strong>controlheader.ftl</strong></p>
<pre><code class="language-java"> <#include "/${parameters.templateDir}/mkyong/controlheader-core.ftl" />
<td
<#if hasFieldErrors>
class="errorsBg" <#t/>
</#if>
<#if parameters.align??>
align="${parameters.align?html}"<#t/>
</#if>
><#t/>
</code></pre>
<p>3.通过删除许多不必要的标签来修改 <strong>controlheader-core.ftl</strong> ,如果存在错误,则向“td”标签添加一个新的“errorsBg”类。</p>
<p><strong>controlheader-core.ftl</strong></p>
<pre><code class="language-java"> <#--
Only show message if errors are available.
This will be done if ActionSupport is used.
-->
<#assign hasFieldErrors = parameters.name?? && fieldErrors?? && fieldErrors[parameters.name]??/>
<#--
if the label position is top,
then give the label its own row in the table
-->
<tr>
<td class="tdLabel <#t/>
<#--
<#if hasFieldErrors>
errorsBg"<#t/>
</#if>
-->
><#rt/>
<#if parameters.label??>
<label <#t/>
<#if parameters.id??>
for="${parameters.id?html}" <#t/>
</#if>
<#if hasFieldErrors>
class="errorLabel"<#t/>
<#else>
class="label"<#t/>
</#if>
><#t/>
<#if parameters.required?default(false) && parameters.requiredposition?default("right") != 'right'>
<span class="required">*</span><#t/>
</#if>
${parameters.label?html}<#t/>
<#if parameters.required?default(false) && parameters.requiredposition?default("right") == 'right'>
<span class="required">*</span><#t/>
</#if>
</label><#t/>
</#if>
</td><#lt/>
</code></pre>
<p>4.修改 <strong>text.ftl</strong> ,在“ <strong>simple/text.ftl</strong> ”之后添加一个新的模板文件“ <strong>error-message.ftl</strong> ”。</p>
<p><strong>text.ftl</strong></p>
<pre><code class="language-java"> <#include "/${parameters.templateDir}/${parameters.theme}/controlheader.ftl" />
<#include "/${parameters.templateDir}/simple/text.ftl" />
<#include "/${parameters.templateDir}/mkyong/error-message.ftl" />
<#include "/${parameters.templateDir}/xhtml/controlfooter.ftl" />
</code></pre>
<p>5.将 CSS 放在视图页面中以格式化错误消息。</p>
<pre><code class="language-java"> <style type="text/css">
.errorsBg{
background-color:#fdd;
color:red;
border: 1px solid;
}
.errorMessage{
padding:0px 8px;
}
table{
border-spacing: 4px;
}
td{
padding:4px;
}
</style>
</code></pre>
<p>5.完成,保存,现在错误信息将显示在输入字段旁边,并用红色突出显示。</p>
<p>Hope this article can give you a general concept about how to create or modify a theme in Struts2.</p>
<h2 id="参考-47">参考</h2>
<ol>
<li><a href="http://web.archive.org/web/20190304032055/http://freemarker.sourceforge.net/" target="_blank">http://freemarker.sourceforge.net/</a></li>
<li><a href="http://web.archive.org/web/20190304032055/http://www.vitarara.org/cms/struts_2_cookbook/creating_a_theme" target="_blank">http://www . vita rara . org/CMS/struts _ 2 _ cookbook/creating _ a _ theme</a></li>
<li><a href="http://web.archive.org/web/20190304032055/http://struts.apache.org/2.0.14/docs/themes-and-templates.html" target="_blank">http://struts . Apache . org/2 . 0 . 14/docs/themes-and-templates . html</a></li>
<li><a href="http://web.archive.org/web/20190304032055/http://www.packtpub.com/article/themes-and-templates-with-apache-struts2" target="_blank">http://www . packtpub . com/article/themes-and-templates-with-Apache-struts 2</a></li>
</ol>
<p><a href="http://web.archive.org/web/20190304032055/http://www.mkyong.com/tag/struts2/" target="_blank">struts2</a> <a href="http://web.archive.org/web/20190304032055/http://www.mkyong.com/tag/template/" target="_blank">template</a> <a href="http://web.archive.org/web/20190304032055/http://www.mkyong.com/tag/theme/" target="_blank">theme</a></p>