HowToDoInJava-其它教程-1-二-
HowToDoInJava 其它教程 1(二)
原文:HowToDoInJava
协议:CC BY-NC-SA 4.0
[已解决] IllegalStateException
:无法初始化插件MockMaker
原文: https://howtodoinjava.com/mockito/plugin-mockmaker-error/
如果您正在使用 Spring boot 2.x 应用,它们自动包含 Mockito Core 依赖项,那么您将遇到此错误,那么您 可以尝试建议的解决方案。
1. 问题
Mockito 核心依赖于称为字节伙伴的库,而当 mocito 找不到匹配的字节伙伴 jar 版本时,通常会出现此问题。 错误看起来像这样:
java.lang.IllegalStateException: Could not initialize plugin: interface org.mockito.plugins.MockMaker (alternate: null)
at org.mockito.internal.configuration.plugins. PluginLoader$1.invoke(PluginLoader.java:74) ~[mockito-core-2.23.4.jar:na]
at com.sun.proxy.$Proxy61.getHandler(Unknown Source) ~[na:na]
at org.mockito.internal.util. MockUtil.isMock(MockUtil.java:81) ~[mockito-core-2.23.4.jar:na]
...
...
Caused by: java.lang.NoClassDefFoundError: net/bytebuddy/dynamic/loading/ ClassInjector$UsingReflection
at org.mockito.internal.creation.bytebuddy. SubclassInjectionLoader.<init>(SubclassInjectionLoader.java:28) ~[mockito-core-2.23.4.jar:na]
at org.mockito.internal.creation.bytebuddy. SubclassByteBuddyMockMaker.<init>(SubclassByteBuddyMockMaker.java:33) ~[mockito-core-2.23.4.jar:na]
at org.mockito.internal.creation.bytebuddy. ByteBuddyMockMaker.<init>(ByteBuddyMockMaker.java:21) ~[mockito-core-2.23.4.jar:na]
...
...
[spring-test-5.1.4.RELEASE.jar:5.1.4.RELEASE]
... 19 common frames omitted
Caused by: java.lang.ClassNotFoundException: net.bytebuddy.dynamic. loading.ClassInjector$UsingReflection
at java.net.URLClassLoader.findClass(Unknown Source) ~[na:1.8.0_171]
at java.lang.ClassLoader.loadClass(Unknown Source) ~[na:1.8.0_171]
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source) ~[na:1.8.0_171]
at java.lang.ClassLoader.loadClass(Unknown Source) ~[na:1.8.0_171]
... 42 common frames omitted
2. 解决方案
-
Find out the mockito core version your project is using. In eclipse, you can check in project build path by navigating to
右键单击“项目 -> 属性 -> Java 构建路径 -> 库选项卡”
Mockito 核心依赖项
-
在 Maven 仓库中搜索该版本的 Mockito Core。 对我来说是: https://mvnrepository.com/artifact/org.mockito/mockito-core/2.23.4
-
Look at the Compile Dependencies section. Note down the correct dependent version of byte-buddy and include in the project. If the jar is already included with some other version, override the version with this correct version.
正确的 jar 版本
生成应用,然后再次运行测试。 您与 MockMaker 有关的问题必须立即解决。
学习愉快!
使用 PowerMock 进行模拟测试(带有 JUnit 和 Mockito)
原文: https://howtodoinjava.com/library/mock-testing-using-powermock-with-junit-and-mockito/
PowerMock 是 Java 世界的开源模拟库。 它扩展了现有的 Mockito 框架,例如 EasyMock 和 Mockito,为它们添加了更强大的功能。 PowerMock 使我们能够为最不可测试的代码编写良好的单元测试。 Java 中的大多数 Mockito 框架都不能模拟静态方法或最终类。 但是使用 PowerMock,我们可以模拟几乎任何类。
PowerMock 当前扩展了 EasyMock 和 Mockito 模拟框架。 根据首选的扩展名,编写任何单元测试的语法略有不同。 在本教程中,我将 PowerMock 与 Mockito 结合使用。
本教程将演示一个使用 PowerMock 的非常简单的模拟示例。 它将向我们展示创建模拟和验证方法调用的基本语法。
PowerMock 和依赖项的安装
PowerMock 是一个正在积极开发的开源 Mockito 框架。 您可以按照以下步骤在计算机中进行设置。
1)首先下载 PowerMock 1.5。 通过 http://code.google.com/p/powermock/ 访问 PowerMock 主页。
2)点击页面上的下载选项卡,您应该看到如下内容:
3)由于我们将使用 Mockito 扩展和 JUnit 测试框架来开发所有示例,因此请下载powermock-mockito-junit-1.6.zip
文件。
4)将 ZIP 文件解压缩到某个文件夹中。 该 ZIP 文件包含使用 PowerMock 编写单元测试所需的所有从属 JAR 文件。
5)将所有 jar 文件复制到项目的lib
文件夹中,并将其添加为依赖项。
你完成了!
如果您还没有的话,您可能需要另外添加“hamcrest-core-1.3.jar
”。
使用的类
为了完整起见,让我首先在下面的类中写下我们将在测试示例中使用的类。
Employee.java
public class Employee {
private String fName;
public String getfName() {
return fName;
}
public void setfName(String fName) {
this.fName = fName;
}
}
EmployeeService.java
public class EmployeeService
{
public int getEmployeeCount() {
throw new UnsupportedOperationException();
}
public void saveEmployee(Employee employee) {
//return nothing
}
}
EmployeeController.java
public class EmployeeController
{
private EmployeeService employeeService;
public EmployeeController(EmployeeService employeeService) {
this.employeeService = employeeService;
}
public int getProjectedEmployeeCount()
{
final int actualEmployeeCount = employeeService.getEmployeeCount();
return actualEmployeeCount * 2;
}
public void saveEmployee(Employee employee) {
employeeService.saveEmployee(employee);
}
}
不带 PowerMock 的首次测试
让我们编写一个简单的测试来获得不使用模拟的员工人数。 如您所见EmployeeService.getEmployeeCount()
方法抛出UnsupportedOperationException
,则测试应失败。
@Test
public void shouldGetCountOfEmployees()
{
EmployeeController employeeController =new EmployeeController(new EmployeeService());
Assert.assertEquals(10,employeeController.getProjectedEmployeeCount());
}
在运行测试时,它肯定会失败,并带有以下异常。
java.lang.UnsupportedOperationException
at com.howtodoinjava.powermock.examples.service.EmployeeService.getEmployeeCount(EmployeeService.java:8)
at com.howtodoinjava.powermock.examples.controller.EmployeeController.getProjectedEmployeeCount(EmployeeController.java:16)
at com.howtodoinjava.powermock.examples.test.EmployeeControllerTestOne.shouldGetCountOfEmployees(EmployeeControllerTestOne.java:15)
在不允许您使用某种方法的任何应用中都可能出现这种情况,并且原因可以是无限的。 在这种情况下,您可能希望模拟上述方法,以便可以测试应用的其他部分。
使用 PowerMock 模拟一种简单的方法
在上面的示例中,不支持getEmployeeCount()
方法,但我们想使用它。 在这种情况下,我们可以使用 powermock 模拟它。
@Test
public void firstMockTest()
{
//Creating a mock using the PowerMockito.mock
//method for the EmployeeService class.
EmployeeService mock =PowerMockito.mock(EmployeeService.class);
//Next statement essentially says that when getProjectedEmployeeCount method
//is called on the mocked EmployeeService instance, return 8.
PowerMockito.when(mock.getEmployeeCount()).thenReturn(8);
EmployeeController employeeController = new EmployeeController(mock);
Assert.assertEquals(16, employeeController.getProjectedEmployeeCount());
}
以上测试将成功执行。 在此,当调用employeeController.getProjectedEmployeeCount()
时,它依次从模拟对象中调用方法getEmployeeCount()
,该方法返回值 8。控制器将其乘以 2,返回值为 16。该返回值等于assert
语句中的期望值, 因此测试通过了。
验证是否真的调用了模拟方法?
有时,单元测试只需要调用一个方法而忘记它。 主要是因为方法不返回任何值。 您肯定可以通过再次从数据源获取值来测试 DB 中是否存在值。 但是,如果您的测试仅需要验证方法是否已被调用,就可以使用 powermock 进行。
@Test
public void verifyMethodInvokationTest()
{
EmployeeService mock =PowerMockito.mock(EmployeeService.class);
EmployeeController employeeController = new EmployeeController(mock);
Employee employee = new Employee();
employeeController.saveEmployee(employee);
//Verifying that controller did call the
//saveEmployee() method on the mocked service instance.
Mockito.verify(mock).saveEmployee(employee);
}
在上面的测试示例中,我们使用verify(mock)
方法验证了saveEmployee(employee)
是否确实已被调用。 如果您通过测试,则将通过。
要验证上述代码是否正常运行,请在EmployeeController.java
的下面一行中注解掉。
public void saveEmployee(Employee employee) {
//employeeService.saveEmployee(employee); //Comment this line
}
现在,如果您再次运行测试,它将失败并显示此错误。
Wanted but not invoked:
employeeService.saveEmployee(
com.howtodoinjava.powermock.examples.model.Employee@7808b9
);
-> at com.howtodoinjava.powermock.examples.test.EmployeeControllerTestOne.verifyMethodInvokationTest(EmployeeControllerTestOne.java:47)
Actually, there were zero interactions with this mock.
您可以在单元测试中拥有非常好的功能。
使用模拟设置获取有关测试运行的更多信息
这些模拟设置很少使用,但在某些情况下很有用。 如果您想为模拟命名,以供将来调试之用,请使用它们。 或者您想启用详细日志记录以获取更多信息。 当您要注册一个监听器来通知该模拟方法的调用时,可以使用它。 甚至在尚未实现实际对象的模拟对象上实现一些额外的接口。
@Test
public void MockettingsTest() {
EmployeeService mock =PowerMockito.mock(EmployeeService.class, Mockito
.withSettings()
.name("EmployeeServiceMock")
.verboseLogging());
EmployeeController employeeController = new EmployeeController(mock);
Employee employee = new Employee();
employeeController.saveEmployee(employee);
//Verifying that controller did call the
//saveEmployee method on the mocked service
//instance.
Mockito.verify(mock).saveEmployee(employee);
}
运行以上测试以在控制台中获得以下结果:
############ Logging method invocation #1 on mock/spy ########
employeeService.saveEmployee(
com.howtodoinjava.powermock.examples.model.Employee@c9131c
);
invoked: -> at com.howtodoinjava.powermock.examples.controller.EmployeeController.saveEmployee(EmployeeController.java:21)
has returned: "null"
############ Logging method invocation #2 on mock/spy ########
employeeService.saveEmployee(
com.howtodoinjava.powermock.examples.model.Employee@c9131c
);
invoked: -> at com.howtodoinjava.powermock.examples.test.EmployeeControllerTestOne.MockettingsTest(EmployeeControllerTestOne.java:64)
has returned: "null"
这就是有关 powermock 的初学者教程的全部内容,可帮助您入门。 我将在下一组教程中介绍一些复杂的主题。
祝您学习愉快!
TypeScript 教程
TypeScript 教程
原文: https://howtodoinjava.com/typescript/typescript-tutorial/
TypeScript 是一种开放源代码编程语言,由 Microsoft 在 2012 年开发和维护。TypeScript 将“类型”(或数据类型)引入 JavaScript。
通常,类型在程序存储或操纵所提供的值之前会检查其有效性。 这样可以确保代码按预期方式运行,并防止您不小心破坏程序。 这有助于使 JavaScript 更接近其他强类型语言,例如 Java 和 C#。
在本教程中,我们将在开始使用语言之前,全面了解 TypeScript 的全部知识。
Table of Contents
TypeScript vs JavaScript
TypeScript Compiler
Install TypeScript
Run TypeScript
TypeScript 与 JavaScript
TypeScript vs JavaScript
- TypeScript 是 JavaScript 的 ES6 版本,还有其他一些 TypeScript 仅具有的东西,而 Angular 需要这些才能工作。
- TypeScript 是 JavaScript 的超集。 它通过数据类型支持扩展 JavaScript。
- 现有的 JavaScript 程序也是有效的 TypeScript 程序。
- TypeScript 支持可以包含现有 JavaScript 库的类型信息的定义文件。
- TypeScript 仅用于开发。 要在浏览器中运行,必须将其转换为 ES6 或 ES5 版本的 JavaScript。
TypeScript 编译器
浏览器不支持 TypeScript。 因此,必须使用支持的 JavaScript 源代码重写用 TypeScript 编写的程序源代码。 为此,TypeScript 发行版附带了名为tsc
的 TypeScript 编译器。
默认情况下,当前版本的编译器支持 ES 5。 TypeScript 可以将源代码编译为任何模块模式 - AMD
,CommonJS
,ES 6
,SystemJS
等。
与任何npm
包一样,您可以在本地或全局安装它,或同时在这两者中安装,并通过在命令行上运行tsc
来编译 TS 文件。
$ tsc helloworld.ts //It compile the file into helloworld.js
编译器配置
TypeScipt 编译器选项在tsconfig.js
中给出。 示例配置文件如下所示:
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"target": "es5",
"typeRoots": [
"node_modules/@types"
],
"lib": [
"es2017",
"dom"
]
}
}
安装 TypeScript
由于 TypeScript 仅用于开发目的,而不是在运行时使用,因此应将其安装为dev
依赖项。
$ npm install typescript --save-dev //As dev dependency
$ npm install typescript -g //Install as global module
$ npm install typescript@latest -g //Install latest if you have older version
运行 TypeScript
在工作区中创建文件helloworld.ts
。 在文件下面添加控制台日志语句。
console.log("Welcome to TypeScript !!");
要将 TypeScript 编译为 javascript ,请使用命令tsc filename
。
$ tsc helloworld.ts //Generates file helloworld.js
要执行文件,请使用node
命令运行。
$ node helloworld.ts //Output "Welcome to TypeScript !!"
从控制台运行 TypeScript
以上就是对 TypeScript 的介绍。
将我的问题放在评论部分。
学习愉快!
TypeScript 类型
原文: https://howtodoinjava.com/typescript/typescript-types/
TypeScript 支持对基本类型(原始类型和对象类型)进行类型检查。 它还支持泛型,装饰器和 ES6 模块类型。 重要的是要知道 TypeScript 中的类型系统设计为可选。 因此,所有 javascript 程序也都是 TypeScript 程序。
Table of Contents
Static types
- Primitive Types
- Object Types
Generics
Decorators
1)静态类型
在语言语义和类型系统的上下文中,静态类型通常表示“在编译时”或“不运行程序”,而动态表示“在运行时”。 在静态类型语言中,变量,参数和对象成员具有编译器在编译时就知道的类型。 编译器可以使用该信息执行类型检查并优化编译的代码。
TypeScript 中的静态类型可以分为两个子类型:
1.1)基本类型
TypeScript 具有 5 种主要基本类型,即number
,string
,boolean
,void
和any
。
number
用于定义类型为number
的变量。
let num: number = 123;
num = 123.456;
num = '123'; // Error
string
用于定义string
类型的变量。
let str: string = 'hello';
str = 'world';
str = 123; // Error
boolean
用于定义boolean
类型的变量。
let bool: boolean = false;
bool = true;
bool = 123; // Error
void
-
用于函数返回类型以表示非返回函数。
function warnUser(): void { alert("This is my warning message"); }
-
声明
void
类型的变量没有用,因为只能将undefined
或null
分配给它们。let tempVar: void = undefined; tempVar = null; tempVar = 123; //Error
any
当您想退出类型检查并让值通过编译时检查时,请使用any
。
let val: any = 'hello';
val = 123; // OK
val = true; // OK
|
1.2)对象类型
TypeScript 支持以下对象类型。
数组
数组是相同数据类型的值的集合。
var names:string[]; //declaration
names = ["how","to","do","in","java"]; //initialization
元组
元组就像数组; 但存储“不同数据类型”的值。 就像数组一样,它也是基于索引的数据结构。
var items; //declaration
items = ["how", 2, "do", 1 , true]; //initialization
接口
接口定义派生成员类必须实现的属性,方法和事件。
interface ICalc {
add (first: number, second: number): any;
}
let Calculator: ICalc = {
add(first: number, second: number) {
return first + second;
}
}
类
类是用于创建对象的模板。 Typescript 从 ES6 获得对类的支持。
class Person {
//field
name:string;
//constructor
constructor(name:string) {
this.name = name;
}
//function
speakName():void {
console.log("Name is : "+this.name)
}
}
枚举
与其他编程语言一样,枚举是由一组命名值组成的数据类型。 名称通常是充当常量的标识符。 ES6 中引入了枚举。
enum Direction {
Up,
Down,
Left,
Right
}
let go: Direction;
go = Direction.Up;
函数
在 TypeScript 中,我们可以声明仅在函数生命期内指向函数的变量。
let fun: Function = () => console.log("Hello");
fun = 123; //Error
2)泛型
泛型允许创建一个可以在多种类型而不是单个类型上工作的组件。 例如:
function throwBack<T>(arg: T): T { //Function return the parameter as it is
return arg;
}
let outputStr = identity<string>("myString"); //OK
let outputNum = identity<number>( 100 ); //OK
3)装饰器
一般而言,装饰器是注解。 它们与'@'
符号一起使用。 它允许我们修饰类和函数,类似于 java 中的注解和 python 中的装饰器。
装饰器是一项新功能,很可能会纳入 JavaScript 的 ES7 版本。 但是该功能在 TypeScript(实验性)中可用,因此我们已经可以使用它了。
如何使用装饰器
了解每个装饰器都是 javascript 函数,这一点很重要。 要创建装饰器,请创建如下函数:
function printable ( target ) {
Object.defineProperty(target.prototype, 'print', {value: () => "Lokesh Gupta"});
}
@printable
class Person {
}
let admin = new Person();
console.log(admin.print()); // Lokesh Gupta
TypeScript 中的类型系统就这些了。 将我的问题放在评论部分。
学习愉快!
TypeScript 联合类型
原文: https://howtodoinjava.com/typescript/union-types/
在 TypeScript 中,您可以定义一个变量,该变量可以具有多种类型的值(即数字,字符串等)。 这称为联合类型。 联合类型允许我们定义具有多个类型的变量。 这是通过使用类型之间的竖线('|'
)符号实现的。
从 JavaScript 代码迁移到 TypeScript 代码时,联合类型在某些情况下会有所帮助。
联合类型语法
如前所述,在变量可以支持的多种类型之间使用管道符号。
let myVar : string | number; //myVar can store string and number types
联合类型示例
让我们看一个 TypeScript 联合类型的例子。
let myVar : string | number; //Variable with union type declaration
myVar = 100; //OK
myVar = 'Lokesh'; //OK
myVar = true; //Error - boolean not allowed
在这里,myVar
变量可以同时包含number
和string
,这使我们能够灵活使用两种数据类型。
如果我们尝试分配未定义的值类型,则 TypeScript 编译器可确保警告我们。
将我的问题放在评论部分。
学习愉快!
字符串字面值类型
原文: https://howtodoinjava.com/typescript/string-literal-types/
在 TypeScript 中,字符串字面值可让您指定string
在其生命周期中必须具有的确切值。 您可以将其假定为“基于字符串的枚举”的形式,也称为string
常量组。
语法
在允许的不同string
值之间使用“竖线”符号。
type myVar = "value1" | "value2" | "value3" | "value4"; //upto N values
//For example
type AppStatus = "ACTIVE" | "INACTIVE" | "ONHOLD";
字符串字面值类型示例
让我们看看如何使用string
字面值,以及如何无法使用。
-
变量赋值
您只能将允许的值分配给字面值类型变量。 否则将是编译时错误。
type AppStatus = "ACTIVE" | "INACTIVE" | "ONHOLD"; let currStatus: AppStatus; currStatus = "ACTIVE"; //OK currStatus = "DELETED"; //Error - Type '"DELETED"' is not //assignable to type 'AppStatus'
-
函数参数
您只能将允许的值传递给字面值类型参数。 否则将是编译时错误。
type AppStatus = "ACTIVE" | "INACTIVE" | "ONHOLD"; function showMe(currentStatus: AppStatus): void { console.log(currentStatus); } showMe('ACTIVE'); //OK - Print 'ACTIVE' showMe('DELETED'); //Compile time Error
将我的问题放在评论部分。
学习愉快!
TypeScript 变量 – var
,let
和const
原文: https://howtodoinjava.com/typescript/var-let-const/
在原始 JavaScript 中,变量是使用“var
”关键字声明的。 现在,在 ES6 中,您还可以使用let
和const
关键字定义变量。 这三个关键字都具有用于变量声明和初始化的标准语法,但是它们的范围和用法不同。 让我们了解这三个关键字之间的区别。
var
关键字
关键字var
具有传统的变量定义语法。 (可选)您可以使用值初始化变量。 如果您没有在var
语句中初始化变量,则会自动为其分配 JavaScript 值undefined
。
var value ; //value is 'undefined'
var data = 100; //initialized with number 100
var blogName = 'howtodoinjava.com'; //initialized with string value
1)范围
-
用
var
关键字声明的变量具有函数范围(即声明它们的函数内部的全局范围); 这意味着它们可以被共享同一作用域的任何函数访问。function fun() { var dataX = 10; //Globally available inside function fun() if(true) { var dataY = 20; //Globally available inside function fun() console.log(dataX); //Output 10 console.log(dataY); //Output 20 } console.log(dataX); //Output 10 console.log(dataY); //Output 20 } fun(); console.log(dataX); //Not available outside function; dataX is undefined console.log(dataY); //Not available outside function; dataY is undefined
-
可以在声明或初始化变量之前使用它们。 在这种情况下,它们的值为
undefined
,但不会出现任何运行时错误。function fun() { console.log(dataX); //Output 'undefined' var dataX = 10; } fun();
2)var
声明的提升
请注意,用var
关键字声明的变量将被提升。 提升意味着,如果我们在函数末尾声明一个变量(已声明但未初始化),则运行时会将其提升到顶部,并且如果在声明之前使用该变量,则不会有任何错误。
阅读更多: JavaScript 提升
3)如果您不使用“var
”关键字
在 JavaScript 中,如果不使用var
关键字进行变量声明(隐式声明),则会在全局范围内创建变量。 例如:
for(index=0; index< array.length; index++){ //index is in global scope
//code
}
在for
循环上方,将在全局范围内创建一个名为index
的变量。 如果其他人碰巧也在使用全局index
变量,那么您刚刚覆盖了他们的变量。
为避免赋予变量全局范围,必须在变量声明中使用var
关键字。
let
关键字
let
关键字与var
关键字非常相似 – 范围界定上有更多限制。
-
当“作用域必须限制在声明它的块中”时,使用“
let
”语句声明一个变量。function fun() { let dataX = 10; if(true) { let dataY = 20; console.log(dataX); //Output 10 console.log(dataY); //Output 20 } console.log(dataX); //Output 10 console.log(dataY); //dataY is 'undefined' } fun();
参见上面突出显示的行。 如果您使用了“
var
”关键字,则由于dataY
在函数中具有全局作用域,因此该名称已经可用。 由于let
关键字,dataY
在if
块之外不可见。 -
使用
let
声明的变量不能在声明之前使用,否则将导致错误。function fun() { console.log(x); //Output 'undefined' console.log(y); //Error - "Uncaught ReferenceError: y is not defined" var x = 10; let y = 11; } fun();
const
关键字
const
声明一个具有常量值的块范围变量。 它基本上是带有“var
”关键字的变量声明,其中变量值是常量,不能更改。const
遵循与let
关键字相同的作用域原则。- 如果您知道所声明的变量不能且不应被允许重新分配,则使用
const
对其进行声明,否则请使用let
关键字。
语法和用法
const PI = "3.14";
PI = 12; //Uncaught TypeError: Assignment to constant variable.
将我的问题放在评论部分。
学习愉快!
已解决:Java 编译器级别与已安装的 Java 项目方面的版本不匹配
原文: https://howtodoinjava.com/maven/solved-java-compiler-level-does-not-match-the-version-of-the-installed-java-project-facet/
很长一段时间以来,我一直没有解决这个问题。 每次遇到此问题时,我都会去修改 Eclipse 中项目方面菜单的项目编译器级别。 今天,我决定结束一切。
eclipse 中的此错误如下所示:
Maven 编译器级别不匹配错误
原因:
此错误是由于默认的 Maven 编译器插件。 目前是 1.5。 因此,如果您使用 Java 1.6 构建项目,则每次运行此命令时都会遇到此问题:
mvn eclipse:eclipse -Dwtpversion=2.0
根据 maven 文档:“目前,默认源设置为 1.5,默认目标设置为 1.5,与运行 Maven 的 JDK 无关。 如果要更改这些默认值,则应按照 Java 编译器的-source
和-target
设置所述设置源和目标。”
我的情况更糟,因为当时默认值为 Java 1.4。🙁
解决方案
要解决此问题,您需要在pom.xml
文件中进行一次更新。 此更新用于覆盖 Maven 编译器插件中的默认编译器级别。
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
使用所需的 Java 版本更新上述编译器属性。 并将此配置放置在pom.xml
文件的build
节点中,如下所示:
<build>
<finalName>JerseyHelloWorld</finalName>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
以上更改将纠正错误。
祝您学习愉快!
参考: http://maven.apache.org/plugins/maven-compiler-plugin/
TypeScript 模板字符串
原文: https://howtodoinjava.com/typescript/template-strings/
模板字符串是常规 JavaScript 字符串,其中包含反引号字符(```
java),这些字符带有以${ }
表示的占位符。 在运行时,您的代码将占位符替换为实际值。
模板字符串示例
我们来看一个非常基本的模板字符串示例。 在此示例中,在运行时,代码将占位符替换为变量的实际值。 称为变量替换。
const name: String = 'Lokesh';
const age: number = 35;
let message: string = `My name is ${ name } and my age is ${ age }`;
console.log(message); //My name is Lokesh and my age is 35
模板字符串可以是多行
与使用加号(+
)来连接多行字符串的常规字符串不同,模板字符串不需要加号。
const name: String = 'Lokesh';
const age: number = 35;
let message: string = `My name is
${ name }
and
my age is
${ age }`;
//Output
My name is
Lokesh
and
my age is
35
请注意,多行模板字符串输出以新行字符格式化的字符串。
将我的问题放在评论部分。
学习愉快!
TypeScript 算术运算符
原文: https://howtodoinjava.com/typescript/arithmetic-operators/
运算符是代表特定动作的符号。 算术运算符可以与一个或多个值一起使用以产生单个值。
在 TypeScript 中,我们可以使用下面给出的 10 个运算符执行算术运算。
算术运算符
x + y
,加法,相加两个操作数。
let x = 10;
let y = 20;
let z = x + y;
console.log( z ); //Output 30
x - y
,减法,相减两个操作数。
let x = 10;
let y = 20;
let z = x - y;
console.log( z ); //Output -10
-x
,取反,翻转标志,求相反数。
let x = 10;
let z = -x;
console.log( z ); //Output -10
x * y
,乘法,相乘两个运算符。
let x = 10;
let y = 20;
let z = x * y;
console.log( z ); //Output 200
x ** y
,求幂,将第一个操作数乘以自己,重复第二个操作数指示的次数。
let x = 10;
let y = 20;
let z = x ** y;
console.log( z ); //Output 100000000000000000000 or 1e+20
x / y
,除法,分子除以分母。
let x = 10;
let y = 20;
let z = x / y;
console.log( z ); //Output 2
x % y
,模量,返回整数除法后的余数。
let x = 10;
let y = 20;
let z = x % y;
console.log( z ); //Output 10
x++
,递增,将整数值增加一。
let x = 10;
x++;
console.log( x ); //Output 11
x--
,递减,将整数值减一。
let x = 10;
x--;
console.log( x ); //Output 9
以上是 TypeScript 中使用的算术运算符。
将我的问题放在评论部分。
学习愉快!
TypeScript 逻辑运算符
原文: https://howtodoinjava.com/typescript/logical-operators/
TypeScript 逻辑运算符类似于您在 JavaScript 逻辑运算符中学习的内容。 这些运算符有助于比较布尔表达式并产生单个布尔值作为结果。
逻辑运算符
逻辑 AND 运算符 – &&
如果两个操作数(或表达式)均为true
,则结果将为true
,否则为false
。
let firstVar = true;
let secondVar = false;
console.log( firstVar && secondVar ); //false
console.log( firstVar && true ); //true
console.log( firstVar && 10 ); //10 which is also 'true'
console.log( firstVar && '10' ); //'10' which is also 'true'
阅读更多:真值和假值
逻辑或运算符 – ||
如果两个操作数(或表达式)中的任何一个为true
,则结果将为true
,否则为false
。
let firstVar = true;
let secondVar = false;
console.log( firstVar || secondVar ); //true
console.log( firstVar || true ); //true
console.log( firstVar || 10 ); //true
console.log( firstVar || '10' ); //true
逻辑非运算符 – !
它用于反转其操作数的逻辑状态。 它将true
转换为false
,反之亦然。
let firstVar = 10;
let secondVar = 20;
console.log( !true ); //false
console.log( !false ); //true
console.log( !firstVar ); //false
console.log( !null ); //true
将我的问题放在评论部分。
学习愉快!
TypeScript 比较运算符
原文: https://howtodoinjava.com/typescript/comparison-operators/
TypeScript 比较运算符与 JavaScript 相同。 比较运算符可帮助比较两个变量的值。
请注意,有些运算符在比较值时会使用类型转换,而有些则不会。 请小心使用它们。
类型转换表示,当一个运算符的操作数为不同类型时,其中一个将转换为另一个操作数类型的“等效”值。
例如:
100 == "100" //true 100 === "100" //false
比较运算符
==
- 检查两个操作数的值是否相等。
- 该运算符使用强制转换。
let firstVar = 10;
let secondVar = 20;
console.log( firstVar == secondVar ); //false
console.log( firstVar == '10' ); //true
console.log( 10 == '10' ); //true
===
- 检查两个操作数的值和类型是否相等。
- 该运算符不使用强制转换。
let firstVar = 10;
let secondVar = 20;
console.log( firstVar === secondVar ); //false
console.log( firstVar === 10 ); //true
console.log( firstVar === '10' ); //false
console.log( 10 === '10' ); //false
!=
- 检查两个操作数的值是否相等。
- 该运算符使用强制转换。
let firstVar = 10;
let secondVar = 20;
console.log( firstVar != secondVar ); //true
console.log( firstVar != 10 ); //false
console.log( firstVar != '10' ); //false
!==
- 检查两个操作数的值和类型是否相等。
- 该运算符使用强制转换。
let firstVar = 10;
let secondVar = 20;
console.log( firstVar !== secondVar ); //true
console.log( firstVar !== 10 ); //false
console.log( firstVar !== '10' ); //true
>
检查左操作数的值是否大于右操作数的值。 该运算符使用强制转换。
let firstVar = 10;
let secondVar = 20;
console.log( firstVar > secondVar ); //false
console.log( firstVar > 10 ); //false
console.log( firstVar > '10' ); //false
<
检查左操作数的值是否小于右操作数的值。 该运算符使用强制转换。
let firstVar = 10;
let secondVar = 20;
console.log( firstVar < secondVar ); //true
console.log( firstVar < 10 ); //false
console.log( firstVar < '10' ); //false
>=
检查左操作数的值是否大于等于右操作数的值。 该运算符使用强制转换。
let firstVar = 10;
let secondVar = 20;
console.log( firstVar >= secondVar ); //false
console.log( firstVar >= 10 ); //true
console.log( firstVar >= '10' ); //true
<=
检查左操作数的值是否小于等于右操作数的值。 该运算符使用强制转换。
let firstVar = 10;
let secondVar = 20;
console.log( firstVar <= secondVar ); //true
console.log( firstVar <= 10 ); //true
console.log( firstVar <='10' ); //true
将我的问题放在评论部分。
学习愉快!
TypeScript for…of
循环
原文: https://howtodoinjava.com/typescript/for-of-loop/
在 TypeScript 中,可以使用for...of
循环遍历可迭代对象(包括array
,map
,set
,string
和arguments
对象等)。 要进行迭代,对象必须实现@@iterator
方法。
遍历数组
使用'for...of'
迭代array
元素的示例。
let myArray = [10, 20, 30];
for (let value of myArray) {
console.log(value); //10 20 30
}
遍历映射
使用'for...of'
迭代map
条目的示例。
let map = new Map();
map.set("A",1);
map.set("B",2);
map.set("C",3);
//Iterate over map keys
for (let key of map.keys()) {
console.log(key); //A B C
}
//Iterate over map values
for (let value of map.values()) {
console.log(value); //1 2 3
}
//Iterate over map entries
for (let entry of map.entries()) {
console.log(entry[0], entry[1]); //"A" 1 "B" 2 "C" 3
}
//Using object destructuring
for (let [key, value] of map) {
console.log(key, value); //"A" 1 "B" 2 "C" 3
}
遍历集合
使用'for...of'
迭代set
条目的示例。
let mySet = new Set();
mySet.add('A');
mySet.add('B');
mySet.add('C');
//Iterate over set
for (let entry of mySet) {
console.log(entry); //A B C
}
遍历字符串
使用'for...of'
迭代string
的示例。 在迭代期间,您将在每个循环周期中从string
中获得一个字符。
let blogName:string = "howtodoinjava.com";
//Iterate over set
for (let character of blogName) {
console.log(character); //howtodoinjava.com
}
将我的问题放在评论部分。
学习愉快!
TypeScript 中的展开运算符
原文: https://howtodoinjava.com/typescript/spread-operator/
在 TypeScript 中,可以使用展开运算符(以省略号的形式)来初始化另一个数组或对象中的数组和对象。 您还可以使用展开运算符进行对象分解。
展开运算符与apply()
方法
JavaScript 的apply()
方法调用具有给定this
值的函数,并以array
提供参数。 例如,在下面的示例中,两个突出显示的函数调用是等效的。 它们都打印相同的输出。
var numbers = [1, 2, 3, 4, 5];
console.log.apply(console, numbers); //1 2 3 4 5
console.log("1", "2", "3", "4", "5"); //1 2 3 4 5
展开运算符(ECMAScript 6 的一部分)是apply()
方法的更好版本。 使用展开运算符,您可以像上面这样写上面的语句。
var numbers = [1, 2, 3, 4, 5];
console.log(...argsArray); //1 2 3 4 5
展开运算符示例
-
从另一个数组初始化数组
您可以使用展开运算符以给定的方式从现有数组创建数组。
let origArrayOne = [ 1, 2, 3]; //1,2,3 let origArrayTwo = [ 4, 5, 6]; //4,5,6 //Create new array from existing array let copyArray = [...origArrayOne]; //1,2,3 //Create new array from existing array + more elements let newArray = [...origArrayOne, 7, 8]; //1,2,3,7,8 //Create array by merging two arrays let mergedArray = [...origArrayOne, ...origArrayTwo]; //1,2,3,4,5,6
-
从另一个对象初始化对象
您还可以使用展开运算符以给定的方式从现有对象创建对象。
let origObjectOne = {a: 1, b: 2, c: 3}; //{a: 1, b: 2, c: 3} let origObjectTwo = {d: 4, e: 5, f: 6}; //{d: 4, e: 5, f: 6} //Create new object from existing object let copyObject = {...origObjectOne}; //{a: 1, b: 2, c: 3} //Create new object from existing object + more elements let newObject = {...origObjectOne, g: 7, h: 8}; //{a: 1, b: 2, c: 3, g: 7, h: 8} //Create object by merging two objects let mergedObject = {...origObjectOne, ...origObjectTwo}; //{a: 1, b: 2, c: 3, d: 4, e: 5, f: 6}
-
对象解构
解构赋值语法是一个 JavaScript 表达式,可以将数组中的值或对象中的属性解压缩为不同的变量。
function myFunction(x, y, z) { console.log( x ); console.log( y ); console.log( z ); } var parametersArray = [0, 1, 2]; myFunction(...parametersArray); //0, 1, 2
将我的问题放在评论部分。
学习愉快!
参考: Mozilla Docs
TypeScript 中的数组
原文: https://howtodoinjava.com/typescript/arrays/
通过简单易学的示例学习在 TypeScript 中创建数组,克隆数组,合并数组以及遍历数组元素。
Table of Contents
Create Array
Iterate Through Array
Clone Array
Merge Arrays
创建数组
像 JavaScript 一样,TypeScript 具有数组类型以允许分配多个值。 通过在类型之后添加方括号来指定数组。
每次将新值添加到数组时,编译器都会检查类型兼容性,并在类型不匹配时发出警报。
//array declaration and initialization in separate lines
let myArr1: boolean[];
let myArr2: boolean[] = [];
let myArr3: boolean[] = new Array();
let myArr4: boolean[] = Array();
myArr1 = [false, false, true];
//array inline declaration and initialization
//array of booleans
let flags1: boolean[] = [false, false, true];
//or
let flags2: Array<boolean> =[false, false, true];
//array of numbers
let scores1: number[] = [10, 20, 30, 40];
//or
let scores2: Array<number> = [10, 20, 30, 40];
向数组添加元素
要向数组添加更多元素,请使用push()
方法。
//array of numbers
let scores: number[] = [10, 20, 30, 40];
scores.push( 50 ); //[10, 20, 30, 40, 50]
scores.push( 'abc' ); //data.ts(24,14): error TS2345: Argument of type '"abc"' is not
//assignable to parameter of type 'number'.
遍历数组
您可以使用for...of
循环或传统的for
循环来迭代数组元素。
使用“for…of
”循环
let scores :number[] = [10, 20, 30, 40];
for (var score of scores) {
console.log(score); //Outputs 10 20 30 40
}
请勿使用用于循环访问对象属性的“for…in
”循环。
使用传统的for
循环
let scores :number[] = [10, 20, 30, 40];
for (var i = 0; i < scores.length; i++) {
console.log(scores[i]); //Outputs 10 20 30 40
}
克隆数组
使用展开运算符克隆数组。 这是最简单和推荐的方法。
let origScores :number[] = [10, 20, 30];
let clonedScores = [...origScores];
console.log(clonedScores); //[10, 20, 30]
origScores.push( 40 );
console.log(origScores); //[10, 20, 30, 40] is "changed"
console.log(clonedScores); //[10, 20, 30] is "unchanged"
合并数组
也可以使用展开运算符合并数组。 这是最简单推荐的方法。
let scores1 :number[] = [10, 20, 30];
let scores2 :number[] = [40, 50, 60];
let mergedScore = [...scores1, ...scores2];
console.log(mergedScore); //[ 10, 20, 30, 40, 50, 60 ]
将我的问题放在评论部分。
学习愉快!
TypeScript 中的枚举
原文: https://howtodoinjava.com/typescript/enums/
在 TypeScript 中,枚举是一组命名常量。 尽管它是可选的,但枚举应为一组命名的相关常量。 TypeScript 支持传统的枚举和基于字符串的枚举。
1)基于字符串的枚举
让我们从基于字符串的枚举开始,因为与传统枚举相比,它们是推荐的方法。 始终使用基于字符串的枚举,直到您有充分的理由选择传统的枚举为止。
1.1)语法
枚举语法与其他语言非常相似,例如 Java。
enum AppStatus {
ACTIVE = 'ACTIVE',
INACTIVE = 'INACTIVE',
ONHOLD = 'ONHOLD'
}
查看 JavaScript 中生成的代码。 它具有一个生成的查找表,如下所示。
var AppStatus;
(function (AppStatus) {
AppStatus["ACTIVE"] = "ACTIVE";
AppStatus["INACTIVE"] = "INACTIVE";
AppStatus["ONHOLD"] = "ONHOLD";
})(AppStatus || (AppStatus = {}));
1.2)访问枚举成员值
使用Enum.member
或Enum['member']
格式访问枚举成员值。
enum AppStatus {
ACTIVE = 'ACT',
INACTIVE = 'INACT',
ONHOLD = 'HLD'
}
AppStatus.ACTIVE //ACT
AppStatus.INACTIVE //INACT
AppStatus['ACTIVE'] //ACT
//Never use numbers with string based enums
AppStatus[0] //undefined
1.3)作为函数参数的枚举
要在函数中传递枚举,请声明枚举本身的参数类型。
enum AppStatus {
ACTIVE = 'ACT',
INACTIVE = 'INACT',
ONHOLD = 'HLD'
}
function checkStatus(status: AppStatus): void {
console.log(status);
}
checkStatus(AppStatus.ONHOLD);
2)传统的基于数字的枚举
尽管不建议这样做,但您可能会遇到需要的情况。 因此,让我们快速学习。
2.1)语法
语法很简单,很老套。 由于尚未初始化值,因此,转译器会生成查找表,并以数组索引的方式分配值(从零开始,然后为每个成员加一)。
enum AppStatus {
ACTIVE, //0
INACTIVE, //1
ONHOLD //2
}
如果要以其他任何数字开头,则将其分配给第一个枚举成员。 接下来的所有成员将通过一个一个的递增来获取值。
enum AppStatus {
ACTIVE = 5, //5
INACTIVE, //6
ONHOLD //7
}
2.2)访问枚举成员值
由于这些是基于数字的枚举,因此您也可以使用enum[index_number]
格式以及Enum.member
或Enum['member']
格式。
enum AppStatus {
ACTIVE,
INACTIVE,
ONHOLD
}
console.log(AppStatus.ACTIVE); //0
console.log(AppStatus['INACTIVE']); //1
console.log(AppStatus[0]); //ACTIVE
2.3)作为函数参数的枚举
要在函数中传递枚举,请声明枚举本身的参数类型。
enum AppStatus {
ACTIVE,
INACTIVE,
ONHOLD
}
function checkStatus(status: AppStatus): void {
console.log(status);
}
checkStatus(AppStatus.ONHOLD); //2
请在最后一个语句中查看打印的'2'
值。 在大多数情况下,它不是很有用,因此,首选并推荐基于字符串的枚举。
将我的问题放在评论部分。
学习愉快!
TypeScript 映射
原文: https://howtodoinjava.com/typescript/maps/
映射是 ES6 中引入的新数据结构。 它允许您存储类似于其他编程语言的键值对,例如 Java,C#。
建立映射
使用Map
类型和new
关键字在 TypeScript 中创建Map
。
let myMap = new Map();
从映射添加/检索/删除条目
map.set()
– 在Map
中添加条目的方法。map.get()
– 从Map
检索条目。map.has()
– 在Map
中存在条目。map.delete()
– 从Map
删除条目。map.size
– 将返回Map
的大小。
let nameAgeMapping = new Map();
//Set entries
nameAgeMapping.set("Lokesh", 37);
nameAgeMapping.set("Raj", 35);
nameAgeMapping.set("John", 40);
//Get entries
nameAgeMapping.get("John"); //40
//Check entry is present or not
nameAgeMapping.has("Lokesh"); //true
nameAgeMapping.has("Brian"); //false
//Size of Map with
nameAgeMapping.size; //3
//Delete an entry
nameAgeMapping.delete("Lokesh"); // true
//Clear whole Map
nameAgeMapping.clear(); //Clear all entries
遍历Map
条目
使用'for...of'
循环可迭代映射键,值或条目。
let nameAgeMapping = new Map();
nameAgeMapping.set("Lokesh", 37);
nameAgeMapping.set("Raj", 35);
nameAgeMapping.set("John", 40);
//Iterate over map keys
for (let key of nameAgeMapping.keys()) {
console.log(key); //Lokesh Raj John
}
//Iterate over map values
for (let value of nameAgeMapping.values()) {
console.log(value); //37 35 40
}
//Iterate over map entries
for (let entry of nameAgeMapping.entries()) {
console.log(entry[0], entry[1]); //"Lokesh" 37 "Raj" 35 "John" 40
}
//Using object destructuring
for (let [key, value] of nameAgeMapping) {
console.log(key, value); //"Lokesh" 37 "Raj" 35 "John" 40
}
将我的问题放在评论部分。
学习愉快!
TypeScript 集合
原文: https://howtodoinjava.com/typescript/sets/
Set
是 ES6 中引入的新数据结构,类似于Map
。 它允许您将不同的值存储到类似于其他编程语言的列表中 Java,C#。
创建集合
使用Set
类型和new
关键字在 TypeScript 中创建Set
。
let mySet = new Set();
从集合中添加/检索/删除值
set.add()
– 在Set
中添加值的方法。set.has()
– 检查Set
中是否存在值。set.delete()
– 从Set
中删除一个值。set.size
– 将返回Set
的大小。
let diceEntries = new Set();
//Add Values
diceEntries.add(1);
diceEntries.add(2);
diceEntries.add(3);
diceEntries.add(4).add(5).add(6); //Chaining of add() method is allowed
//Check value is present or not
diceEntries.has(1); //true
diceEntries.has(10); //false
//Size of Set
diceEntries.size; //6
//Delete a value from set
diceEntries.delete(6); // true
//Clear whole Set
diceEntries.clear(); //Clear all entries
遍历集合
使用'for...of'
循环迭代Set
值。
let diceEntries = new Set();
diceEntries.add(1).add(2).add(3).add(4).add(5).add(6);
//Iterate over set entries
for (let currentNumber of diceEntries) {
console.log(currentNumber); //1 2 3 4 5 6
}
// Iterate set entries with forEach
diceEntries.forEach(function(value) {
console.log(value); //1 2 3 4 5 6
});
将我的问题放在评论部分。
学习愉快!
Maven ant 插件 – 从pom.xml
生成build.xml
原文: https://howtodoinjava.com/maven/maven-ant-plugin/
假设您已经从 maven 创建并维护了您的项目很长时间,但是现在您有了将其移至 Ant 构建环境的说明。 好吧,这看起来并不容易,但是确实很多。
仅用于此目的的 maven ant 插件即可使用。
$ mvn ant:ant
让我们看一个详细的示例,以使用 maven ant 插件从 maven pom 文件生成 ant 构建文件。
1. 创建示例 Maven 项目
$ mvn archetype:generate
-DgroupId=com.howtodoinjava
-DartifactId=antBuildScriptDemo
-DarchetypeArtifactId=maven-archetype-quickstart
-DinteractiveMode=false
2. 从pom.xml
生成 ant 脚本
$ cd antBuildScriptDemo
$ mvn ant:ant
3. 从pom.xml
生成build.xml
的演示
上面的命令将在项目根文件夹中生成 3 个文件。
build.xml
maven-build.properies
maven-build.xml
要运行上面生成的 ant 文件,请在系统中安装 ant 并执行如下所示的build.xml
文件:
ant build.xml
学习愉快!
参考: http://maven.apache.org/plugins/maven-ant-plugin/index.html
TypeScript 函数 – 剩余,可选和默认参数
原文: https://howtodoinjava.com/typescript/functions-rest-optional-default-params/
学习使用示例创建函数,函数类型声明,可选参数,默认参数和剩余参数。
Table of Contents
Create function
Function Types
Optional Parameters
Default Parameters
Rest Parameters
创建函数
在 TypeScript 中,可以通过两种方式创建函数。
-
函数声明
这些是以传统 JavaScript 风格编写的命名函数。
console.log( showMyName("Lokesh") ); // Hi! Lokesh function showMyName (name: string): string { return `Hi! ${name}`; }
-
函数表达式
这些函数没有名称。 他们被分配给:
console.log(showMyName("Lokesh")); //Error - Define functional expression first. let showMyName = function(name: string): string { return `Hi! ${name}`; }; console.log(showMyName("Lokesh")); //Hi! Lokesh
请注意,这两个函数声明看上去都相似,但两者却不相同。 JavaScript 解释器可以在解析函数声明时对其进行求值(变量提升)。 另一方面,函数表达式是赋值的一部分,直到赋值完成后才进行计算。
函数类型
在 TypeScript 中,一切都是类型,函数也是类型。 您可以将变量的类型声明为Function
,如下所示。
let showMyName: Function = function(name: string): string {
return `Hi! ${name}`;
};
在上面的示例中,showMyName
是只能指向Function
类型的变量。
如果您未指定变量类型,TypeScript 会自动推断类型。
可选参数
与 JavaScript 不同,如果我们尝试调用一个函数而不提供其签名声明的确切数量和参数类型,则 TypeScript 编译器将引发错误。
要解决此问题,可以使用带问号(?
)的可选参数。 在下面的示例中,message
被标记为可选参数。
let showMyName = function(name: string, message?: string): string {
return `Hi! ${name} {message}`;
};
showMyName(); //Error
showMyName('Lokesh'); //Hi! Lokesh
showMyName('Lokesh', 'How are you?'); //Hi! Lokesh How are you?
重要的是要注意,可选参数必须始终位于函数的参数列表中的必需参数之后。
默认参数
可选参数是一个很棒的功能,但让我们有逻辑去实现其值可以为undefined
的方案。 它需要很多null
检查和if-else
块。
更好的方法是使这些参数具有默认值,而不是将它们声明为可选值。 如果这样做将导致更简洁的代码和易于维护的代码。
let showMyName = function(name: string, message: string = 'How are you?'): string {
return `Hi! ${name} {message}`;
};
showMyName('Lokesh'); //Hi! Lokesh How are you?
showMyName('Lokesh', 'How are you buddy?'); //Hi! Lokesh How are you buddy?
1)请注意,与可选参数一样,默认参数也必须位于函数参数列表中的必需参数之后。
2)您不能使任何参数可选并且默认。 只允许一种类型。
剩余参数
有时,您可能希望创建可以具有不确定数量参数的函数。 对于一个或两个参数,可以使用可选参数或默认参数。 但是,如果参数数量未知,或者在运行时可能有所不同,该怎么办。
例如您正在读取输入字段值,并且该值可能会因动态 UI 而有所不同。 此处剩余参数将为您提供帮助。 剩余参数语法使我们可以将不确定数量的参数表示为数组。
- 要创建剩余参数,请在变量名称前使用省略号,即三个点(
'...'
)。 - 剩余参数必须为数组类型,否则我们将收到编译错误。
- 从理论上讲,对最大参数个数没有具体限制。
let addInputValues = function( ...values: number[] ): number {
let result = 0;
for (let val of values) {
result += val;
}
return result;
};
addInputValues(); //OK - You can choose not to pass anything as well
addInputValues(1, 1); //OK
addInputValues(1, 2, 3); //OK
addInputValues(1, 2, 3, 4, 5, 6); //OK
将我的问题放在评论部分。
学习愉快!
TypeScript 函数或方法重载
原文: https://howtodoinjava.com/typescript/function-overloading/
在 TypeScript 中,函数重载或方法重载是创建具有相同名称和不同数量参数或类型的多个方法的能力。
因此从本质上讲,在以下情况下允许方法重载:
- 函数名称相同
- 每个重载中参数的数量不同
- 如果参数数量相同,则它们的类型必须不同
- 所有重载必须具有相同的返回类型
还应注意,函数实现必须与所有重载签名兼容。 它应该始终是列表中的最后一个,并以any
类型或联合类型作为其参数的类型。
函数重载示例
假设我们正在创建一个函数,该函数将根据不同的参数(例如 id,email,名称或其组合)返回Employee
类型。 让我们为此要求创建重载函数。
class Employee {}
function getEmployee(id: number): Employee; //Overload 1
function getEmployee(email: string): Employee; //Overload 2
function getEmployee(email: number, name: string): Employee; //Overload 3
//function getEmployee(name: string): Employee; //Error - Conflict with Overload 2
//Implement the function
function getEmployee (paramOne: string | number, paramTwo?: string ): Employee {
let employee: Employee;
if( typeof paramOne === 'number') {
//Logic for overload 1
} else if( typeof paramTwo != 'undefined') {
//Logic for overload 3
} else {
//Logic for overload 2
}
return employee;
}
将我的问题放在评论部分。
学习愉快!
转译器(Transpiler)与编译器
原文: https://howtodoinjava.com/typescript/transpiler-vs-compiler/
转译器(Transpiler)或源到源编译器是读取用一种编程语言编写的源代码,并生成具有相似抽象级别的另一种语言的等效代码的工具。 转译器的一个很好的例子是 Typescript 转译器,它可以将 Typescript 代码转换为 JavaScript。 Babel 编译器也可以用于 ES6 JS 代码到 ES5 JS 代码。
编译器也将代码从一种语言转换为另一种语言,但是两种语言的抽象级别却大不相同。 例如.java
至.class
文件编译。
ES6 和 ES5
要了解转译器,您必须首先了解 ES6 和 ES5 JavaScript 之间的区别。 ES6( ECMAScript 6 )是下一版本 JavaScript 的规范。 它的一些主要增强功能包括模块,类声明,词法块作用域,迭代器和生成器,对异步编程的承诺,解构模式以及适当的尾部调用。
功能很棒,但是大多数浏览器直到现在都不支持该规范。 因此,用 ES6 编写的任何 UI 应用规范都无法在大多数浏览器中运行。 要运行这些应用,必须将此 ES6 源代码转换为受支持的 JavaScript 版本 ES5。 几乎所有浏览器都支持 ES5,并且是迄今为止最稳定的版本。
ES6 – 为 JavaScript 带来“类型”。 使它更接近强类型语言,例如 Java,C#。 到目前为止,大多数浏览器都不支持它。 必须将其转换为 ES5 才能在浏览器中执行。
ES5 – 这些年来,我们一直在编写朴素的 JavaScript。
转译器
编译器是类似于程序的编译器,它将 ES6 JavaScript 代码转换为 ES5 JavaScript 代码,以便可以在浏览器中运行。 当编译器看到使用需要翻译的语言功能的表达式时,它将生成逻辑上等效的表达式。 产生的表达式与源表达式可以非常相似,也可以非常不同。
转译器做什么?
ES6 代码 => ES5 代码(甚至还有 ES4,ES3)
tsconfig.json
文件中提供了 TypeScript 的转译器配置。
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"target": "es5",
"typeRoots": [
"node_modules/@types"
],
"lib": [
"es2017",
"dom"
]
}
}
转译器演示
让我们看看 ES6 到 ES5 转换的工作原理。 创建一个文件helloworld.ts
并在其中写入此代码。
ES6 代码
export class HelloWorld {
constructor() {
console.log('welcome');
}
};
ES5 代码
要将 TypeScript 代码编译为 javascript 代码,请使用命令“tsc helloworld.ts
”。 它将在同一文件夹中生成helloworld.js
文件。
"use strict";
exports.__esModule = true;
var HelloWorld = /** @class */ (function () {
function HelloWorld() {
console.log('welcome');
}
return HelloWorld;
}());
exports.HelloWorld = HelloWorld;
将我的问题放在评论部分。
学习愉快!
JavaScript 中的真值和假值
原文: https://howtodoinjava.com/typescript/truthy-and-falsy/
在 JavaScript 中,真值是等于布尔值true
的表达式,而假值则是等于布尔值false
的表达式。 与其他语言不同,true
和false
值不限于boolean
数据类型和比较。 它可以具有许多其他形式。
让我们了解什么使 JavaScript 中的真假表达式成为事实。
假值
javascript 中共有 6 个假值/表达式。 我将使用此函数来检查值是真值还是假值。
function testTruthyFalsy (val)
{
return val ? console.log('truthy') : console.log('falsy');
}
布尔‘false
’
显然布尔值false
是false
。
testTruthyFalsy (false); //Prints 'falsy'
空字符串,即''
任何空字符串都将被求值为false
。
testTruthyFalsy (''); //Prints 'falsy'
undefined
任何undefined
变量将等于false
。
var my_var = undefined;
testTruthyFalsy (my_var); //Prints 'falsy'
null
任何null
变量将等于false
。
var my_var = null;
testTruthyFalsy (my_var); //Prints 'falsy'
NaN
结果为NaN
(不是数字)的任何数值表达式都将等于false
。
testTruthyFalsy (NaN); //Prints 'falsy'
数字零,即 +0 或 -0
任何结果为零(+0
或-0
)的数值表达式都将等于false
。
testTruthyFalsy ( 2 - 2 ); //Prints 'falsy'
testTruthyFalsy ( 0 ); //Prints 'falsy'
真值
除了上面列出的假值以外的任何表达式或值 - 被认为是真值。 例如:
function testTruthyFalsy (val)
{
return val ? console.log('truthy') : console.log('falsy');
}
testTruthy(true); // truthy
testTruthy(false); // falsy
testTruthy(new Boolean(false)); // truthy (object is always true)
testTruthy(''); // falsy
testTruthy('Packt'); // truthy
testTruthy(new String('')); // true (object is always true)
testTruthy(1); // truthy
testTruthy(-1); // truthy
testTruthy(NaN); // falsy
testTruthy(new Number(NaN)); // truthy (object is always true)
testTruthy({}); // truthy (object is always true)
var obj = { name: 'John' };
testTruthy(obj); // truthy
testTruthy(obj.name); // truthy
将我的问题放在评论部分。
学习愉快!
相等运算符(==
)与严格相等运算符(===
)
原文: https://howtodoinjava.com/typescript/equals-vs-strict-equals/
在 TypeScript(和 JavaScript)中,您可以与相等运算符('=='
)或严格相等运算符('==='
)比较。 两者似乎几乎相似; 但是他们比较两个值的方式却大不相同。
相等运算符(==
)
使用相等运算符比较 x == y
(其中x
和y
是值)会产生true
或false
。 要知道的重要一点是,在比较两个值时,JavaScript 运行时将执行类型转换以使两个值都具有相同的类型。
例如,如果尝试将string
值与number
值进行比较,则string
值将首先转换为number
类型,然后进行比较。
"10" == 10
//becomes
ToNumber("10") === 10
阅读更多:完全相等比较算法
让我们看一个例子。
let a = 10;
a == 10 //true
a == '10' //true
查看上面示例中的最后两个语句。 变量'a'
如何等于number 10
和string '10'
。
严格相等运算符(===
)
使用相等运算符比较 x === y
,其中x
和y
是值,仅在以下情况下才产生true
或false
:
x
和y
的类型相同x
和y
的值相等
让我们看一个例子。
let a = 10;
a === 10 //true
a === '10' //false
在这种情况下,比较存储number 10
的变量不等于string 10
。
使用哪个运算符?
作为建议,建议始终使用严格相等运算符。 这很有帮助,因为
- 您无需在检查相等性时记住类型转换规则。
- 如预期的那样,比较不同的数据类型应为
false
。 - 由于上述事实,源代码不太容易出错。
将我的问题放在评论部分。
学习愉快!
JavaScript 中的undefined
vs null
原文: https://howtodoinjava.com/typescript/undefined-vs-null/
如果变量已经声明但尚未初始化,则称其为“undefined
”。 而“null
”被分配给当时没有值的变量。
简单来说,当您不为变量赋值时,JavaScript 引擎会将其视为undefined
。 null
值是由程序员分配的,以指示变量内部没有变量,但打算在以后的程序执行中使用该值。
undefined
请参阅下面的 JavaScript 语句以了解undefined
。
var myVar; //Variable declaration without assigning any value to it
console.log( myVar ); //undefined
console.log( typeof(myVar) ); //undefined
console.log( undeclaredVar ); //Uncaught ReferenceError: undeclaredVar is not defined
如果看到一个值undefined
的变量,则说明:
- 该变量已在程序中声明。
- 变量没有赋值。
- 如果检查变量
type
,它将是undefined
。 请注意,这是 JavaScript 中的原始类型之一:字符串,数字,布尔值,null
,undefined
,符号(ES6 中的新增功能)。 - 如果函数返回
undefined
,则未返回任何值。
undefined
在 JavaScript 中不是保留关键字。
null
null
表示故意缺乏的值。 从概念上讲,它与 Java,C# 等其他编程语言中的null
非常相似。null
表示缺少标识,表示变量指向没有对象。
var myVar = null; //Variable declared and assigned to null
console.log( myVar ); //null
console.log( typeof(myVar) ); //object
请注意,null
的类型为object
,具有有效值,无属性,不可更改,并且系统中始终只存在相同的单个实例。
将相等运算符与null
和undefined
一起使用
当您想清楚地区分null
和undefined
变量时,请始终记住使用严格相等运算符(===
)将变量与null
和undefined
进行比较。
了解更多:JavaScript 中的
==
vs===
var myUndefinedVar; //undefined
var myNullVar = null; //null
myUndefinedVar == myNullVar; //true - which not correct as both are different
myUndefinedVar === myNullVar; //false - correct behavior
null
等于零
null
和undefined
之间的主要区别在于将它们转换为原始类型的方式。
null
在执行基本操作时会转换为零(0)。undefined
转换为NaN
。
var myUndefinedVar; //undefined
var myNullVar = null; //null
myUndefinedVar + 100; //NaN
myNullVar + 100; //100
将我的问题放在评论部分。
学习愉快!
JavaScript 变量提升
原文: https://howtodoinjava.com/typescript/javascript-variable-hoisting/
JavaScript 中的变量提升意味着将所有变量声明移到函数的顶部。 这意味着,如果我们在函数末尾声明一个变量,则运行时会将其提升到顶部,并且如果在声明之前使用了该变量,则不会有任何错误。
请注意,用
var
关键字声明的变量将被提升。let
和const
(在 ES6 中引入)没有提升效果。
什么是提升?
如前所述,提升意味着将变量声明移到顶部。 重要的是要注意,如果初始化变量,则不会发生提升。
变量声明(发生提升)
在下面的代码中,在声明之前使用data
。
function fun()
{
data = 1;
console.log(data); //Outputs 1
var data;
}
fun();
在运行时,提升以上代码后,代码将如下所示:
function fun()
{
var data; /*** moved to top ***/
data = 1;
console.log(data); //Outputs 1
}
fun();
变量初始化(不会发生提升)
在下面的代码中,data
也进行了声明和初始化。 在这种情况下,将不发生提升并且不会向上移动。 因此,data
的值只有在声明和初始化后才可用。
function fun()
{
console.log(data); //Outputs 'undefined'
var data = 1;
console.log(data); //Outputs 1
}
fun();
在评论部分中,将您与 Javascript 中的变量提升有关的问题放到我这里。
学习愉快!
tsconfig.json
– TypeScript 编译器配置
原文: https://howtodoinjava.com/typescript/tsconfig-json/
TypeScript 编译器使用tsconfig.json
获取用于从 TypeScript 源代码生成 JavaScript 代码的配置选项。 使用'$ tsc'
命令编译 TypeScript 代码时,编译器将搜索tsconfig.json
中加载的配置。
创建tsconfig.json
在开始使用 TypeScript 之前,请确保已安装它。 为了方便使用,请将其安装为全局依赖项,以便您可以从控制台窗口使用tsc
命令。
然后使用'tsc --init'
命令在项目的根文件夹中创建tsconfig.json
文件。
$ npm install typescript -g //Install typescript
$ cd project_root //Go to project's root folder
$ tsc --init //Create tsconfig.json in project's root folder
将创建默认的tsconfig.json
。
{
"compilerOptions":{
"target":"es6",
"moduleResolution":"node",
"module":"commonjs",
"declaration":false,
"noLib":false,
"emitDecoratorMetadata":true,
"experimentalDecorators":true,
"sourceMap":true,
"pretty":true,
"allowUnreachableCode":true,
"allowUnusedLabels":true,
"noImplicitAny":true,
"noImplicitReturns":false,
"noImplicitUseStrict":false,
"outDir":"dist/",
"baseUrl":"src/",
"listFiles":false,
"noEmitHelpers":true
},
"include":[
"src/**/*"
],
"exclude":[
"node_modules"
],
"compileOnSave":false
}
编译器选项
您必须了解一些重要的编译器选项。
选项 | 描述 |
---|---|
allowJs |
允许编译 JavaScript 文件。 默认值为false 。 |
alwaysStrict |
以严格模式解析并为每个源文件发出“use strict ”。 默认值为false 。 |
module |
输出模块类型例如 “CommonJS”,“AMD ”,“System ”,“ES6 ”,“ES2015 ”或“ESNext ”。如果target 属性为ES3 或ES5 ,则默认值为CommonJS ; 否则默认为ES6 。 |
target |
指定 ECMAScript 目标版本。 默认值为ES3 。 |
moduleResolution |
确定如何解决模块。 对于module ,键入ES6 ,AMD 或System - 默认值为 classic ; 其他Node 。 |
sourceMap |
指示是否生成源映射。 源映射有助于调试。 |
outDir |
转储文件的存放位置。 |
baseUrl 或paths |
指示 TypeScript 在哪里可以找到类型文件。 |
watch |
用于livereload 。 这意味着无论何时更改任何源文件,都将重新触发编译过程以再次生成已编译的文件。 |
experimentalDecorators |
此选项允许在 TypeScript 项目中使用装饰器。 ES 尚未引入装饰器,因此默认情况下将其禁用。 |
请遵循官方页面以获得编译器选项的完整列表。
包含和排除选项
这些选项采用一系列全局模式,这些模式需要包含在编译路径中,以便在编译过程中添加或删除文件。
"include":[
"src/**/*",
"src/app/shared/**/*",
"typings/*.d.ts"
],
"exclude":[
"node_modules",
"jspm_packages",
"application",
"system",
"dist"
]
将我的问题放在评论部分。
学习愉快!
Angular(2.x)教程
Angular 开发工作区设置
原文: https://howtodoinjava.com/angular/dev-workspace-setup/
学习在本地工作站中为 Angular,Node 和 TypeScript 设置开发环境,以开发下一代 UI 应用。
Table of Contents
Install Node
Install Git
Install Packages
Create Angular HelloWorld
Demo
安装 Node
-
转到 Node 下载页面,然后根据您的机器配置下载安装程序。
-
执行安装程序。 如果要在任何备用位置上安装 Node,请选择路径。 我建议使用默认选项。
-
验证是否已使用此命令安装了 Node。
$ node --version v8.11.3 //output
安装 Git
-
转到 Git 下载页面,然后根据您的机器配置下载安装程序。
-
执行安装程序。 如果要在任何备用位置上安装 Git,请选择路径。 我建议使用默认选项。
-
验证是否已使用此命令安装了 Git。
$ git --version git version 2.17.1.windows.2 //Output
另外,运行以下命令来设置您的 git 身份。
$ git config --global user.email "you@example.com" //Use your git email id
$ git config --global user.name "Your Name" //Use your git name
安装包
现在,在安装了 node 和 git 之后,该安装必要的npm
包了。
$ npm install -g rxjs
$ npm install -g typescript
$ npm install -g webpack
$ npm install -g webpack-cli
$ npm install -g @angular/core
$ npm install -g @angular/cli
$ npm install -g gulp
创建 Angular HelloWorld
在命令提示符下转到您的工作区,然后运行此命令。 此命令将在helloworld
文件夹中创建一个新的 Angular 应用 – 其中包含所有必需的文件。
$ ng new helloworld
Angular CLI 生成文件
演示
要运行上面创建的应用,请从应用文件夹中运行命令ng serve
。
$ cd helloworld
$ ng serve
** Angular Live Development Server is listening on localhost:4200, open your browser on http://localhost:4200/ **
Date: 2018-06-18T17:10:50.638Z
Hash: 832b348b3a35e13efaf8
Time: 6365ms
chunk {main} main.js, main.js.map (main) 10.7 kB [initial] [rendered]
chunk {polyfills} polyfills.js, polyfills.js.map (polyfills) 227 kB [initial] [rendered]
chunk {runtime} runtime.js, runtime.js.map (runtime) 5.22 kB [entry] [rendered]
chunk {styles} styles.js, styles.js.map (styles) 15.6 kB [initial] [rendered]
chunk {vendor} vendor.js, vendor.js.map (vendor) 3.06 MB [initial] [rendered]
i 「wdm」: Compiled successfully.
打开浏览器,然后在http://localhost:4200/
中验证正在运行的应用。
浏览器中的 Angular HelloWorld
您的 Angular 开发环境已启动并正在运行。
将我的问题放在评论部分。
学习愉快!
Maven IntelliJ IDEA 项目
原文: https://howtodoinjava.com/maven/how-to-convert-maven-java-project-to-intellij-idea-project/
我们已经了解了如何在 maven 中创建一个简单的 Java 项目,以及如何将其转换为支持 Eclipse IDE 。 有时,开发人员环境由 IntelliJ IDE 而不是 Eclipse 组成。
因此,让我们学习如何将其转换为也支持 IntelliJ。
$ mvn idea:idea
1)创建 Maven 项目
$ mvn archetype:generate
-DgroupId=com.howtodoinjava
-DartifactId=intelliJDemo
-DarchetypeArtifactId=maven-archetype-quickstart
-DinteractiveMode=false
2)IntelliJ 支持
$ mvn idea:idea
上面的命令将下载 IntelliJ 的项目插件。 并且,上述命令还将创建项目文件(.ipr
),模块文件(.iml
)和工作区文件(.iws
)。
学习愉快!
参考: http://maven.apache.org/plugins/maven-idea-plugin/index.html
[已解决] Npm 安装挂起或时间过长
原文: https://howtodoinjava.com/angular/npm-install-hung/
在使用 Node 时,您可能正在下载各种npm
包。 有时下载过程只是挂起而无法继续进行。 您可能还会看到与不可访问的registry.npmjs.org
URL 相关的错误。
错误
通常,错误日志如下所示:
E:\ngexamples\helloworld>npm install -g webpack
npm ERR! code ETIMEDOUT
npm ERR! errno ETIMEDOUT
npm ERR! network request to https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz failed, reason: connect ETIMEDOUT 104.18.95.96:443
npm ERR! network This is a problem related to network connectivity.
npm ERR! network In most cases you are behind a proxy or have bad network settings.
npm ERR! network
npm ERR! network If you are behind a proxy, please make sure that the
npm ERR! network 'proxy' config is set properly. See: 'npm help config'
npm ERR! A complete log of this run can be found in:
npm ERR! C:\Users\Lokesh\AppData\Roaming\npm-cache\_logs\2018-06-18T16_50_17_569Z-debug.log
解决方案
大多数情况下(也是我自己),此错误是由于代理设置不正确引起的。 验证网络代理设置正确。
否则,请尝试使用以下命令删除代理设置,然后重试。
$ npm config rm proxy
$ npm config rm https-proxy
希望以上命令也将为您解决此错误。
学习愉快!
模拟 REST 服务器来伪造在线 API
原文: https://howtodoinjava.com/angular/mock-rest-server/
学习在本地创建模拟 REST 服务器,该服务器将模拟在线 REST API,并在线生成所需的 JSON 响应。 拥有这样的 REST API 模拟功能非常方便,可以缩短开发时间。
安装 JSON 服务器
在 Node 和 Angular 开发环境中,让我们导入json-server
依赖项。
$ npm install -g json-server
//Console Output
C:\Users\admin\AppData\Roaming\npm\json-server -> C:\Users\admin\AppData\Roaming\npm\node_modules\json-server\bin\index.js
+ json-server@0.14.0
added 229 packages from 130 contributors in 24.844s
创建模拟 JSON 响应并提供在线服务
-
关于这种方法的最好的事情是,您不会被迫消耗任何意味着更少数据的虚拟对象。 相反,您可以创建要使用的 JSON 数据,并将其放入文件
'db.json'
中并开始提供它。{ "employees": [ { "id": 1, "name": "Lokesh", "status": "active" }, { "id": 2, "name": "Andy", "status": "Inactive" }, { "id": 3, "name": "Brian", "status": "active" }, { "id": 4, "name": "Charles", "status": "Inactive" } ] }
-
并使用简单的命令开始在
db.json
文件上方投放。$ json-server --watch 'E:\ngexamples\db.json' \{^_^}/ hi! Loading E:\ngexamples\db.json Done Resources http://localhost:3000/employees Home http://localhost:3000 Type s + enter at any time to create a snapshot of the database Watching...
-
现在使用命令在浏览器中测试 REST API
'http://localhost:3000/employees'
.JSON 服务器运行中
请注意,由 JSON 服务器提供的 REST 端点由
db.json
中的数据节点确定。 它是如此简单而强大。
将我的问题放在评论部分。
学习愉快!
参考: json 服务器 github 页面
{% raw %}
Angular 插值
原文: https://howtodoinjava.com/angular/angular-interpolation/
1. 什么是 Angular 插值?
Angular 插值用于在具有双花括号语法的各个视图模板中显示组件属性。 我们可以在视图中显示所有类型的属性数据字符串,数字,日期,数组,列表或映射。
数据绑定由单向数据绑定和双向数据绑定组成。 插值用于单向数据绑定。 插值会将数据从我们的组件向 HTML 元素沿一个方向移动。
2. Angular 插值语法
在视图模板中显示的属性名称应括在双花括号中,也称为胡子语法。 即:
class AppComponent
{
propertyName: string;
object: DomainObject;
}
{{ propertyName }}
{{ object.propertyName }}
Angular 会自动从组件中提取propertyName
和object.propertyName
的值,并将这些值插入浏览器。 这些属性更改时,Angular 会更新显示。
3. Angular 插值用法
-
显示简单属性 – 插值可用于显示和求值 HTML 元素标签之间以及属性分配内的文本字符串。
<h1>Greetings {{ name }}! </h1> <h4><img src="{{ backgroundImgUrl }}" style="height:40px"></h4>
-
求值算术表达式 – 插值的另一种用法是求值花括号内的算术表达式。
<h6>{{3 + 5}}</h6> //outputs 8 on HTML browser
-
调用方法并显示返回值 – 我们还可以在插值表达式内的宿主组件视图上调用/调用方法。
import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-greet', template: ` <h1>Greetings {{ name }}! </h1> <h2>Have a good {{ getTime() }}!</h2> `, styleUrls: ['./greet.component.css'] }) export class GreetComponent implements OnInit { name: string = "John Doe"; getTime(): string { return 'morning'; } }
-
显示数组项 – 我们可以将插值与
ngFor
指令一起使用以显示数组项。export class DomainObject { constructor(public id: number, public name: string) { //code } }
import { DomainObject } from './domain'; @Component({ selector: 'app-root', template: ` <h1>{{title}}</h1> <h2>The name is : {{domainObjectItem.name}}</h2> <p>Data Items:</p> <ul> <li *ngFor="let d of domainObjects"> {{ d.name }} </li> </ul> ` }) export class AppComponent { title = 'App Title'; domainObjects = [ new DomainObject(1, 'A'), new DomainObject(2, 'B'), new DomainObject(3, 'C'), new DomainObject(4, 'D') ]; domainObjectItem = this.domainObjects[0]; }
我们使用内联模板或单独的 HTML 文件进行组件视图,模板数据绑定都具有对组件属性的相同访问权限。
4. Angular 插值示例
通过以下命令,使用@angular/cli
创建一个新组件。
//with inline template using '-it' flag
ng generate component greet -it
上面的命令将使用内联模板生成“greet.component.ts
”。 让我们将属性name
和time
添加到问候组件中,如下所示:
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-greet',
template: `
<h1>Greetings {{name}}! </h1>
<h2>Have a good {{time}}!</h2>
`,
styleUrls: ['./greet.component.css']
})
export class GreetComponent implements OnInit {
name: string = "John Doe";
time: string = "morning";
}
Angular 会自动从“问候”组件中提取name
和time
属性的值,并将这些值插入浏览器。 这些属性更改时,Angular 会更新显示。
5. 插值和属性绑定之间的区别
插值是 Angular 转换为属性绑定(一对方括号)的一种特殊语法。 这是属性绑定的便捷替代方法。
另一个主要区别是,要将元素属性设置为非字符串数据值,我们必须使用属性绑定。
在此示例中,将基于'isDisabled'
的值禁用或启用OK
按钮。 另一方面,无论属性值如何,Cancel
按钮将始终被禁用。
export class AppComponent {
isDisabled: boolean = true;
}
<button [disabled]='isDisabled'>OK</button> //Data binding
<button disabled='{{isDisabled}}'>Cancel</button> //Interpolation
学习愉快!
{% endraw %}
Angular 组件
原文: https://howtodoinjava.com/angular/angular-component/
在本 Angular2 组件教程中,学习 Angular 中的组件是什么,如何创建 Angular 组件,Angular 组件元数据的示例。
1. 什么是 Angular 组件
Angular 组件控制屏幕的称为视图的部分。 支持视图中各种功能(例如数据绑定,事件绑定等)的应用逻辑被编写在一个类文件中,该文件通常被称为“app.component.ts
”。
2. 何时使用 Angular 组件
当应用基于基于组件的架构且用户界面分为较小的 Web 组件(每个组件提供不同的功能)时,应使用 Angular 组件。
例如,一个网站可能有一个组件用于捕获反馈,而另一个则用于社交媒体跟踪。
3. 如何创建 Angular 组件
我们可以使用 angular CLI(命令行界面)或手动创建 Angular 组件。
3.1 使用@angular/cli
创建组件
可以使用以下命令,使用@angular/cli
创建新的 Angular 组件(例如“邮政编码”)组件:
// command: ng generate component [name]
$ ng generate component zipcode
上面的命令将在src
下名为zipcode
的新文件夹中创建以下文件:
-
zipcode.component.html
:此 html 文件包含与组件关联的代码/模板,例如:<div><strong>zipcode works!</strong></div>
-
zipcode.component.spec.ts
:此文件包含与单元测试相关的代码,例如:import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { ZipcodeComponent } from './zipcode.component'; describe('ZipcodeComponent', () => { let component: ZipcodeComponent; let fixture: ComponentFixture<ZipcodeComponent>; beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [ ZipcodeComponent ] }) .compileComponents(); })); beforeEach(() => { fixture = TestBed.createComponent(ZipcodeComponent); component = fixture.componentInstance; fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); }); });
-
zipcode.component.ts
:包含支持视图功能的逻辑的组件类。import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-zipcode', templateUrl: './zipcode.component.html', styleUrls: ['./zipcode.component.css'] }) export class ZipcodeComponent implements OnInit { constructor() { } ngOnInit() { } }
-
zipcode.component.css
:CSS 文件包含与组件关联的样式表,例如:/* ZipcodeComponent's private CSS styles */ h1 { font-size: 1.2em; color: #999; margin-bottom: 0; } h2 { font-size: 2em; margin-top: 0; padding-top: 0; }
3.2 手动创建组件
我们可以根据需要手动创建上述文件,但是要创建组件,我们只需要在文件夹zipcode
内定义必需文件zipcode.component.ts
。
4. 如何导入 Angular 组件
创建完组件后,我们需要通过将其导入文件“app.module.ts
”中来告知 angular 来加载组件,如下所示:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api';
import { InMemoryDataService } from './in-memory-data.service';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { DashboardComponent } from './dashboard/dashboard.component';
import { HeroDetailComponent } from './hero-detail/hero-detail.component';
import { HeroesComponent } from './heroes/heroes.component';
import { HeroSearchComponent } from './hero-search/hero-search.component';
import { MessagesComponent } from './messages/messages.component';
import { ZipcodeComponent } from './zipcode/zipcode.component';
@NgModule({
imports: [
BrowserModule,
FormsModule,
AppRoutingModule,
HttpClientModule,
// The HttpClientInMemoryWebApiModule module intercepts HTTP requests
// and returns simulated server responses.
// Remove it when a real server is ready to receive requests.
HttpClientInMemoryWebApiModule.forRoot(
InMemoryDataService, { dataEncapsulation: false }
)
],
declarations: [
AppComponent,
DashboardComponent,
HeroesComponent,
HeroDetailComponent,
MessagesComponent,
HeroSearchComponent,
ZipcodeComponent
],
bootstrap: [AppComponent]
})
export class AppModule { }
如果使用
@angular/cli
命令ng generate component zipcode
来生成组件,则它将自动进行上述突出显示的更改,否则我们必须手动进行。
5. Angular 组件元数据
组件元数据是指在@Component
装饰器中定义的属性。 @Component
装饰器将紧随其后的类标识为组件类。
元数据直接通过内联代码或通过引用将模板与组件关联。 组件及其模板一起描述了视图。
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-zipcode',
templateUrl: './zipcode.component.html',
styleUrls: ['./zipcode.component.css']
})
export class ZipcodeComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
以下是元数据中定义的最常用的属性:
selector
:CSS 选择器可帮助 Angular 在模板 HTML 中找到的相应标记的位置创建并插入组件的实例。templateUrl
:组件的 HTML 模板的模块相对地址。template
:在//html stuff goes here
中定义的内联 HTML 模板。styleUrls
:一个或多个 URL,用于包含 CSS 样式表的文件,仅特定于此组件。style
:特定于此组件的一个或多个内联 CSS 样式表。
学习愉快!
阅读更多: Angular Docs
Angular 模板和视图
原文: https://howtodoinjava.com/angular/angular-templates-and-views/
1. 什么是 Angular 模板和视图
模板是 HTML 代码段,它告诉 Angular 如何在 angular 应用中渲染组件。
模板立即与组件相关联,以定义该组件的视图。
2. Angular 视图层次结构
该组件还可以包含视图层次结构,这些层次结构具有嵌入的视图,这些视图定义或与其他组件相关联。
视图层次结构可以包含来自同一 Ng 模块中组件的视图,但是它也可以包含来自不同 Ng 模块中定义的组件的视图。
关于视图层次结构的要点如下:
- 它是相关视图的树,可以充当一个独立的单元。
- 根视图通常称为组件的宿主视图。
- 它在 Angular 变化检测中起着重要作用。
2.1 查看层次结构示例
下图显示了管理超级英雄和危机中心的应用的视图层次结构。
- 应用组件位于根级别,称为主机视图,其中包含英雄和危机中心组件。
- 英雄组件充当其子组件英雄列表和英雄详细信息的宿主视图,它们将具有各自的视图。
- 同样,危机中心组件还托管两个子组件,分别包含针对危机列表和危机详细信息组件的视图。
在此,层次结构中的每个组件都可以具有与其关联的视图。
3. 模板类型
有两种方法可以在 Angular 组件中定义模板。
3.1 内联模板
内联模板是通过将 HTML 代码放在“反引号”中定义的,并使用@Component
装饰器的模板属性链接到组件元数据。
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-zipcode',
template: `
<p>
zipcode inline template
</p>
`,
styleUrls: ['./zipcode.component.css']
})
export class ZipcodeComponent implements OnInit {
constructor() { }
ngOnInit() { }
}
要使用@angular/cli
定义内联模板,请使用以下命令:
ng generate component zipcode -it
3.2 模板文件
模板是在单独的 HTML 文件中定义的,并使用@Component
装饰器的templateUrl
属性链接到组件元数据,例如:
<p>
zipcode separate HTML template
</p>
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-zipcode',
templateUrl: './zipcode.component.html',
styleUrls: ['./zipcode.component.css']
})
export class ZipcodeComponent implements OnInit
{
constructor() { }
ngOnInit() {
}
}
学习愉快!
{% raw %}
Angular 服务示例
原文: https://howtodoinjava.com/angular/angular2-service/
通过示例学习创建 Angular 服务并将服务作为全局依赖项(通过模块)与局部依赖项(通过组件)导入。
Table of Contents
Create Service
Global service vs Local Service Injection
Demo
创建服务
在所需位置创建一个新文件calc.service.ts
,并将以下代码放入其中。 CalcService
是服务名称。
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class CalcService {
constructor() { }
}
以上服务非常完整,可以在应用组件中使用。
使用 Angular CLI
如果使用 Angular CLI,创建服务很容易。 这只是一个命令,可以完成所有工作。 它将像上面的示例一样生成服务代码。
$ ng g s service/calc --flat
//Other useful options
--force = override
--spec=false = dont generate spec file
--dry-run = dont touch code if it has been updated already.
全局服务与本地服务注入
要注入服务,您有两个选择。
1)注入为“全局服务”
要作为全局服务注入,请将服务注入到根模块中。
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { CalcService } from './service/calc.service';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule
],
providers: [CalcService],
bootstrap: [AppComponent]
})
export class AppModule { }
2)注入为“本地服务”
要作为本地服务注入,请直接将服务注入组件。
import { Component } from '@angular/core';
import { CalcService } from './service/calc.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
providers: [CalcService]
})
export class AppComponent {
title = 'app';
constructor(calc:CalcService){
//Use calc
}
}
演示
1)添加服务方法
为了演示CalcService
的用法,我们在服务中定义一个新方法,并在app.component.ts
中使用它。
我添加了方法add()
,该方法将剩余参数用作数字数组。 然后,将所有数字相加并返回总和。
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class CalcService {
constructor() { }
public add(...params: number[]): number {
let result = 0;
for (let val of params) {
result += val;
}
return result;
}
}
2)将服务导入到组件并使用该方法更新模型
我创建了一个新的模型属性'sum'
,我们将使用服务方法add()
的返回值进行更新。
import { Component } from '@angular/core';
import { CalcService } from './service/calc.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'app';
sum: number = 0;
constructor(calc:CalcService){
this.sum = calc.add(1,2,3,4);
}
}
3)更新视图 HTML
现在,最后,使用新添加的模型属性sum
更新视图html
文件。
<h1>
Welcome to {{ title }}!
Sum is {{sum}}
</h1>
4)运行应用
现在,使用命令'ng serve'
运行该应用,并检查'http://localhost:4200/'
的输出。
Angular 2 服务输出
将我的问题放在评论部分。
学习愉快!
源码下载
{% endraw %}
{% raw %}
带有 RxJS Observable
的 Angular HttpClient
示例
原文: https://howtodoinjava.com/angular/rxjs-observable-httpclient/
了解如何使用 Angular HttpClient
服务从在线 REST API 获取数据,并将其作为Observable
对象/数组返回。 在发生任何数据事件时,observable
的订户将做出反应。
Table of Contents
HTTPClient Setup
Create service which return Observable
Create observer which subscribe to Observable
View HTML Template
Demo
HTTPClient
设置
要使用HTTPClient
服务,您需要执行两个步骤:
-
在根模块中导入
HttpClientModule
从
@angular/common/http
包中导入HttpClientModule
模块,并将其条目添加到@NgModule
的imports
属性中。import { BrowserModule } from '@angular/platform-browser'; import { HttpClientModule } from '@angular/common/http'; import { NgModule } from '@angular/core'; import { AppComponent } from './app.component'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, HttpClientModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
-
在服务构造器中注入
HttpClient
现在,在开始使用它时,在服务代码中注入实际的
HttpClient
服务。import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; @Injectable({ providedIn: 'root' }) export class EmployeeService { constructor(private http: HttpClient) { } }
创建返回Observable
的服务
我们将使用通过 REST 模拟服务器创建的 REST API。 让我们编辑员工服务类别的代码,并从中返回Observable
。
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Employee } from '../model/employee';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class EmployeeService {
constructor(private http: HttpClient) { }
public getEmployees(): Observable<Employee[]>
{
const url = 'http://localhost:3000/employees';
return this.http.get<Employee[]>(url);
}
}
上面的代码点击 REST API "/employees"
并获取employee
数组。 然后,它返回employee
数组作为可观察的集合。 任何方法都可以订阅它来监听此数组上的数据事件。
仅供参考,Employee
是用于存储数据的模型类。
export class Employee {
public id: number;
public name: string;
public status: string;
constructor(id:number, name:string, status:string) {
this.id = id;
this.name = name;
this.status = status;
}
}
创建订阅了Observable
的观察者
我们将在组件文件中创建订户。 它将从可观察数组中读取数据并分配给模型属性。 模型属性可用于映射来自 UI 的数据。
import { Component } from '@angular/core';
import { EmployeeService } from './service/employee.service';
import { Employee } from './model/employee';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'app';
employees = new Array<Employee>();
constructor( empService:EmployeeService ) {
empService.getEmployees().subscribe(response =>
{
this.employees = response.map(item =>
{
return new Employee(
item.id,
item.name,
item.status
);
});
});
}
}
查看 HTML 模板
是时候更新视图 HTML 了,该 HTML 将尽快提供employee
数组数据。
<h1>
Angular HTTP Service Example
</h1>
<table border="1" style="width: 33%">
<tr>
<th>Id</th>
<th>Name</th>
<th>Status</th>
</tr>
<tr *ngFor="let emp of employees">
<td>{{ emp.id }}</td>
<td>{{ emp.name }}</td>
<td>{{ emp.status }}</td>
</tr>
</table>
演示
要测试以上编写的代码,您将启动模拟 REST 服务器以及 angular 应用。
-
使用此命令启动模拟服务器。
$ json-server --watch 'E:\ngexamples\db.json'
-
使用命令启动 Angular 应用。
$ ng serve
在浏览器中检查应用。
带有 RxJS Observable
的 Angular HttpClient
示例
将您的评论放在源代码中。
学习愉快!
源码下载
{% endraw %}
AngularJS(1.x)教程
{% raw %}
AngularJS 教程 – HelloWorld 示例
原文: https://howtodoinjava.com/angularjs/angularjs-tutorial-helloworld-example/
本 AngularJS 教程涵盖以下主题,这些主题将帮助您详细学习 AngularJS。
Table Of Contents
Why AngularJS?
Advantages of using AngularJS?
Understanding AngularJS Components
Building AngularJS HelloWorld application
为什么选择 AngularJS?
最初建立网站时,它们纯粹是在完整的请求 - 响应模型上工作。 流程就像–首先在浏览器中呈现了一个网页–然后用户在网页中进行了任何互动,例如,表单提交或单击按钮–将向服务器发送新请求,然后从服务器返回整个新页面以在浏览器中呈现响应。
在 Microsoft 引入XMLHttpRequest
之前,一直使用该模型,这使开发人员无需实际刷新页面即可对服务器进行异步调用,并使用服务器响应来部分刷新页面。 这使用户体验更加连贯,因为用户可以执行需要远程调用的任务,并且在进行和处理呼叫时仍可以与应用进行交互。
仅在此时间段内,大多数 JS 框架(如 jQuery,原型等)才出现。jQuery 是该列表中最引人注目的框架–可能仍然是 UI 开发的最佳和最简单的选择。
如果您从事过在 UI 组件中使用 jQuery 的相当大的项目,那么您可以在一段时间内轻松地将其关联起来 - 代码维护变得有些麻烦。 一段时间后,您的项目中将有 100 多个 JS 文件,甚至更多–所有这些文件都是相互引用的,而无需执行设计。 jQuery 确实是使编码变得容易的绝佳框架,但是它缺乏任何可以在代码库增长时为我们提供帮助的结构性指导。 因此,我们最终得到了难以维护和测试的意大利面条代码。
这是诸如模型视图控制器(MVC)之类的架构模式和诸如 AngularJS 之类的框架出现的地方。 Google 的 AngularJS 是一个全面的 JavaScript 模型 - 视图 - 控制器(MVC)框架,可让您轻松轻松地快速构建可在任何台式机或移动平台上正常运行的应用。 AngularJS 完全支持单元测试,以帮助减少质量保证(QA)时间。
AngularJS 中的模型和视图比大多数 JavaScript 客户端框架中的模型和视图简单得多。 在其他 JavaScript 客户端框架中经常缺少的控制器是 AngularJS 中的关键功能组件。
使用 AngularJS 的优势?
让我们记下 Angular 与其他框架相比提供的其他好处:
- Angular 直接修改页面 DOM,而不添加内部 HTML 代码。 它更快。
- Angular 基于页面中特定于 Angular 的元素属性构建绑定。 这有助于减少编写工作,使代码更简洁,更易于理解并且不易出错。
- Angular 具有一些扩展功能,例如依赖项注入,路由,动画,视图编排等。 它们有助于编写更健壮和可维护的代码,并鼓励良好的设计实践。
- AngularJS 完全支持单元测试。
- Angular 中的 Pub-sub 系统支持上下文感知通信。
broadcast()
将向所有子控制器发送一条消息,而emit()
将向所有祖先发送一条消息。 - 它得到了 Google 和一个强大的开发社区的支持。
了解 AngularJS 组件
我们已经知道 AngularJS 是基于 MVC 的结构化框架,因此我们将需要编写许多组件和工具以使您的应用代码结构化并得到良好维护。 让我们简短地学习这些组件。 我们将在下一个教程中详细讨论。
-
模块
模块是控制器,指令,过滤器,服务和其他配置信息的集合。 所有这一切的主要参与者是
angular.module()
,因为它是进入模块 API(用于配置 Angular 模块的机制)的网关。 它用于注册,创建和检索以前创建的 AngularJS 模块。例如,将以下代码添加到新的 JavaScript 文件中,您可以将其命名为
myAppModule.js
。// Create a new module var myAppModule = angular.module('myAppModule', []);
您刚刚创建了一个模块。 很简单,不是吗?
在
module()
中传递的数组参数可用于传递依赖项列表。 即该模块所依赖的其他模块。 我们没有任何依赖项,因此我们只需传递一个空数组即可。现在,您可以使用
myAppModule
变量来定义模块特定的其他组件,例如控制器或过滤器。 例如:// configure the module with a controller myAppModule.controller('MyFilterDemoCtrl', function ($scope) { // controller code would go here } );
-
作用域
在 AngularJS 中,
$scope
是具有可用属性和要使用方法的对象。 请注意,作用域可用于视图和控制器。
在 AngularJS 中创建控制器时,会将$scope
对象作为参数传递。var myAppModule = angular.module('MyFilterDemoCtrl', []); myAppModule.controller('myController', function($scope) { $scope.firstName = "Lokesh"; });
将属性添加到控制器中的
$scope
时,视图(HTML)可以访问这些属性。<div ng-app="myAppModule" ng-controller="myController"> <h1>{{firstName}}</h1> </div>
-
指令
AngularJS 指令是扩展 HTML 属性,其前缀为
"ng-"
。 有一些预定义的指令,例如ng-app
,ng-init
和ng-controller
。 此外,您也可以定义自己的指令。例如,使用
ng-init
指令,您可以将应用数据初始化为某个默认值。<div ng-app="myAppModule" ng-init="firstName='Lokesh'"> <h1>{{firstName}}</h1> </div>
-
表达式
视图中使用的范围变量实际上是表达式。 这些表达式用大括号括起来:
{{ expression }}
。 您可以编写简单表达式以及复杂表达式,例如{{ firstName + " " + lastName }}
或{{ "id = " + 0 }}
。 -
控制器
在 Angular 中,控制器通过在范围内设置初始状态或值以及向范围添加行为来扩展范围。 例如,您可以添加一个对范围内的值求和的函数,以提供总计,以便如果范围后的模型数据发生更改,则总值始终会更改。
您必须使用“
ng-controller
”将控制器添加到 HTML 元素,然后在后台将其实现为 JavaScript 代码。<div ng-app="myApp" ng-controller="sumController"> First Value: <input type="text" ng-model="firstValue"><br> Second Value: <input type="text" ng-model="secondValue"><br> <br /> Sum is = {{sum()}} </div> <script> var app = angular.module('myApp', []); app.controller('sumController', function($scope) { $scope.firstValue = 5; $scope.secondValue = 10; $scope.sum = function() { return $scope.firstValue + $scope.secondValue; }; }); </script>
-
数据绑定或模型
这是 AngularJS 的最佳功能。 数据绑定是将来自模型的数据与网页中显示的内容链接起来的过程。 AngularJS 提供了一个非常干净的界面,可以将模型数据链接到网页中的元素。
在 AngularJS 中,数据绑定是一个双向过程:在网页上更改数据时,将更新模型,而在模型中更改数据时,将自动更新网页。 这样,模型始终是表示用户的唯一数据源,视图只是模型的投影。
上面“控制器”部分中的示例也是数据绑定的示例。 因此,当您在文本框中更改“第一个值”或“第二个值”时,求和字段将自动更新,而无需付出额外的努力。 太棒了。
-
服务
服务是 AngularJS 环境中的主要工作。 服务是为 Web 应用提供功能的单例对象。 例如,Web 应用的常见任务是执行对 Web 服务器的 AJAX 请求。 AngularJS 提供了一个 HTTP 服务,其中包含访问 Web 服务器的所有功能。
服务功能完全独立于上下文或状态,因此可以轻松地从应用的组件中使用它。 AngularJS 提供了许多用于基本用途的内置服务组件,例如 HTTP 请求,日志记录,解析和动画。 您还可以创建自己的服务,并在整个代码中重复使用它们。
例如,
$http
是用于从 Web 服务器读取数据的核心服务。 我们可以按以下方式使用此服务。var app = angular.module('myApp', []); app.controller('customersCtrl', function($scope, $http) { $http.get("some HTTP URL") .then(function(response) { //process the response here }); });
-
编译器
AngularJS 提供了一个 HTML 编译器,它将在 AngularJS 模板中发现指令,并使用 JavaScript 指令代码构建扩展的 HTML 元素。
引导 AngularJS 库时,会将 AngularJS 编译器加载到浏览器中。 加载后,编译器将在浏览器中搜索 HTML DOM,并将任何后端 JavaScript 代码链接到 HTML 元素,然后最终的应用视图将呈现给用户。
构建 AngularJS HelloWorld 应用
到目前为止,我们已经了解了 AngularJS 中涉及的一些重要概念。 让我们利用所有这些知识来构建我们的第一个 HelloWorld 应用。
在此示例中,我们将尝试使用所有组件而不增加复杂性,以便于理解。 以后,如果需要,您可以尝试添加功能,从而在此应用中增加更多的复杂性。
该应用显示两个文本框,您可以在其中输入任何数字,然后单击求和按钮以添加值并更新显示在它们下面的消息。 看起来像这样的图像:
AngularJS HelloWorld 应用界面
让我们看一下 HTML 视图和 Angular 控制器 JS 文件的代码,以了解其工作原理。
helloWorld.html
<!doctype html>
<html ng-app="helloWorldApp">
<head>
<title>Hello World - AngularJS</title>
</head>
<body>
<div ng-controller="MyController">
<span>First Value:</span> <input type="text" ng-model="first"> <br />
<span>Second Value:</span> <input type="text" ng-model="second"> <br /><br />
<button ng-click='updateSum()'>Sum</button> <br /><br />
{{heading}} {{total}}
</div>
<script src="angular.min.js"></script>
<script src="app.js"></script>
</body>
</html>
app.js
var firstApp = angular.module('helloWorldApp', []);
firstApp.controller('MyController', function($scope) {
$scope.first = 5;
$scope.second = 10;
$scope.heading = 'Sum is : ';
$scope.total = 15;
$scope.updateSum = function() {
$scope.total = parseInt($scope.first) + parseInt($scope.second);
};
});
当您执行上面的 Angular 示例时,您将看到输出作为附加图像作为工作应用。
学习愉快!
{% endraw %}
AngularJS – jQueryLite(jqLite)教程
原文: https://howtodoinjava.com/angularjs/angular-jquery-lite-jqlite-tutorial/
AngularJS 和 jQuery 都是非常强大的 JS 框架。 正如 Angular 介绍中所讨论的那样,jQuery 从很长时间以来一直是最引人注目的框架。 AngularJS 增加了 MVC 模式的功能以及许多其他内置服务,但是它确实尊重了 jquery 易于使用的语法(jquery 选择器),并且可以通过简单的函数调用对多个元素进行操作。
虽然,可以将 jQuery 库和 angular 一起使用,但建议使用 jQueryLite (默认情况下打包在 AngularJS 中)。
它是 jQuery 的精简版,即,它缺少 jQuery 的某些功能,但包含其中的大部分功能。 让我们看看如何在 AngularJS 应用中使用 jQueryLite。
访问 jQuery 或 jQueryLite
对于大多数 AngularJS 应用来说,内置在 AngularJS 中的 jQueryLite 库就足够了。 但是,如果您需要完整版 jQuery 的其他功能,只需在加载 AngularJS 库之前加载 jQuery 库即可。 例如:
<script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
<script src="http://code.angularjs.org/1.2.9/angular.min.js"></script>
无论是否加载 jQueryLite 或完整的 jQuery 库,都可以使用 AngularJS 引导时可用的angular
变量的element
属性从 AngularJS 代码访问 jQuery。 本质上, angular.element
将是jQuery
变量的别名,该变量通常在 jQuery 应用中使用。 您可以考虑如下:
angular.element() === jQuery() === $()
jQuery 示例
<div ng-click="clicked($event)">Click Me</div>
//You can access a jQuery version of the object using the following AngularJS code:
$scope.clicked = function(event){
var jQueryElement = angular.element(event.target);
//Now perform jQuery actions on jQueryElement
};
倾向于使用“Angular 方式”
您会经常听到“仅在指令中进行 DOM 操作”。 这是必须的。 在前进之前,请尽量避免使用 jQuery。 总是想办法避免使用 jQuery/jqLite 来操作 DOM 对象。
AngularJS 附带了一整套工具,使这一过程变得非常容易。 使用ngClass
,我们可以动态更新类; ngModel
允许双向数据绑定; ngShow
和ngHide
以编程方式显示或隐藏元素; 还有更多–包括我们自己编写的内容。
换句话说,我们可以在没有 DOM 操作的情况下进行各种出色的工作。 DOM 操作越少,指令的测试就越容易,指令的样式就越容易,将来就越容易更改,它们的可重用性和可分发性就越高。
这是可切换按钮的快速示例:
.directive( 'myDirective', function () {
return {
template: '<a class="btn">Toggle me!</a>',
link: function ( scope, element, attrs ) {
var on = false;
$(element).click( function () {
on = !on;
$(element).toggleClass('active', on);
});
}
};
});
可以更简单地重写此指令,如下所示:
.directive( 'myDirective', function () {
return {
scope: true,
template: '<a class="btn" ng-class="{active: on}" ng-click="toggle()">Toggle me!</a>',
link: function ( scope, element, attrs ) {
scope.on = false;
scope.toggle = function () {
scope.on = !scope.on;
};
}
};
});
与以前的版本相比,它非常清晰,易于维护且易于测试,在任何不使用 jQuery 的环境中,它都很容易被破坏。
参考:
https://docs.angularjs.org/api/ng/function/angular.element
https://stackoverflow.com/questions/14994391/thinking-in-angularjs-if-i-have-a-jquery-background
学习愉快!
Spring MVC JSTL 配置示例
原文: https://howtodoinjava.com/spring-mvc/how-to-add-jstl-support-in-spring-3-using-maven/
学习使用 maven 构建工具为 Spring MVC 应用配置 JSTL 支持。 学习在 Spring MVC 应用中启用 JSTL 标签。
1. JSTL maven 依赖项
<dependency>
<groupid>javax.servlet</groupid>
<artifactid>jstl</artifactid>
<version>1.2</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupid>taglibs</groupid>
<artifactid>standard</artifactid>
<version>1.1.2</version>
<scope>runtime</scope>
</dependency>
2. 配置InternalResourceViewResolver
来解析 JSTL 视图
2.1 Spring JSTL Java 配置
@Bean
public ViewResolver configureViewResolver()
{
InternalResourceViewResolver viewResolve = new InternalResourceViewResolver();
viewResolve.setPrefix("/WEB-INF/jsp/");
viewResolve.setSuffix(".jsp");
return viewResolve;
}
2.2 Spring JSTL XML 配置
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"></property>
<property name="prefix">
<value>/WEB-INF/jsp/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
3. 在 JSP 文件中使用 JSTL 标记
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<h1>Welcome message : <c:out value="${message}"></c:out></h1>
学习愉快!
阅读更多:
JSTL 库
{% raw %}
AngularJS 服务(内置和自定义)
原文: https://howtodoinjava.com/angularjs/angularjs-services-built-in-and-custom/
正如我们在 Angular 介绍中了解到的,服务是无状态对象和单例对象,它们为 Web 应用提供功能。 例如,$http
是用于对 Web 服务器进行 HTTP 调用的核心服务。 简单来说,您可以将 Angular 服务假定为可重用代码块,它执行一个或多个相关任务(例如 Java 中带有静态方法的工具类)。 在 AngularJS 中,有几个内置服务 – 您也可以创建自己的自定义服务。
Table of Contents
Built-in Services
Creating Custom Services
How to use Services
内置 Angular 服务
如前所述,Angular 提供了几种内置服务,这些服务可以即时使用。 在运行时,这些服务会自动向依赖项注入器注册,因此您可以通过使用依赖项注入轻松地将它们合并到您的 angular 应用中。 例如,要在控制器中使用$http
服务,请按以下步骤注入服务:
module.controller('DemoController', function( $http ){
//...
});
让我们列出有 Angular 的内置服务。
服务名称 | 描述 |
---|---|
$anchorScroll | 提供滚动到$location.hash() 中指定的页面锚点的功能 |
|
$animate |
该服务公开了一系列 DOM 工具方法,这些方法提供对动画挂钩的支持。 |
$animateCss |
默认情况下,仅当包含ngAnimate 时,此服务才会执行动画。 |
$cacheFactory |
构造缓存对象,放置和检索键值对并为其提供对其他服务的访问权限的工厂。 |
$templateCache |
首次使用模板时,会将其加载到模板缓存中以便快速检索。 |
$compile |
将 HTML 字符串或 DOM 编译到模板中,并生成模板函数,然后可以使用该函数将范围和模板链接在一起。 |
$controller |
这负责实例化 Angular 控制器组件。 |
$document |
指定对window.document 元素的 jQuery 包的引用。 |
$exceptionHandler | Angular 表达式中任何未捕获的异常都委托给此服务。 默认实现只是委派给$log.error ,它将其记录到浏览器控制台中。 |
|
$filter |
过滤器用于格式化显示给用户的数据。 |
$httpParamSerializer | 默认的$http 参数序列化程序,将对象转换为字符串。 |
|
$httpParamSerializerJQLike | 替代$http 参数序列化器,它遵循 jQuery 的param() 方法逻辑。 序列化程序还将按字母顺序对参数进行排序。 |
|
$http |
此服务有助于通过浏览器的XMLHttpRequest 对象或JSONP 与远程 HTTP 服务器进行通信。 |
$xhrFactory |
用于创建XMLHttpRequest 对象的工厂函数。 |
$httpBackend |
用于处理浏览器不兼容。 |
$interpolate | 将带有标记的字符串编译为插值函数。 HTML $compile 服务使用此服务进行数据绑定。 |
|
$interval |
Angular 的window.setInterval 包装器。 |
$locale |
提供各种 Angular 组件的本地化规则。 |
$location | 它解析浏览器地址栏中的 URL,并使该 URL 可用于您的应用。 对地址栏中 URL 的更改将反映到$location 服务中,对$location 的更改将反映到浏览器地址栏中。 |
|
$log |
控制台日志。 |
$parse |
将 Angular 表达式转换为函数。 |
$q |
帮助您异步运行函数,并在完成处理后使用它们的返回值(或错误)。 |
$rootElement |
Angular 应用的根元素。 |
$rootScope |
范围提供了模型和视图之间的分离。 这是针对根范围的。 每个应用都有一个根范围。 |
$sceDelegate | 由后端$sce 使用。 |
|
$sce |
为 AngularJS 提供严格的上下文转义服务。 |
$templateRequest | 它运行安全检查,然后使用$http 下载提供的模板,并在成功后将内容存储在$templateCache 中。 |
|
$timeout |
Angular 的window.setTimeout() 包装器 |
$window | 对浏览器的window 对象的引用。 尽管window 在 JavaScript 中全局可用,但由于它是全局变量,因此会引起可测试性问题。 在 Angular 上,我们始终通过$window 服务来引用它,因此可以对其进行覆盖,删除或模拟以进行测试。 |
参考: https://docs.angularjs.org/api/ng/service
创建自定义 Angular 服务
将应用的业务逻辑和通用操作放在一个地方总是一个好的设计。 这样可以提高代码的可重用性,并避免代码重复。 您应将所有此类逻辑放入自定义 Angular 服务中。 这使代码模块化并且更易于维护。
此外,您的代码变得更具可测试性。 请记住,Angular 为单元测试提供了一流的支持。 因此,我们可以为这些服务快速编写测试,并避免许多可能的缺陷。
声明 angularjs 服务的方式主要有两种。 让我们了解两种方式:
使用module.service('serviceName', function(){})
当您使用module.service()
和创建服务时,作为第二个参数传递的function()
的实例成为 AngularJS 注册并随后在需要时注入到其他服务/控制器的服务对象。
使用module.service()
在自定义服务对象中声明方法的语法为:
module.service('DemoService', function() {
//"this" will be used as service instance
this.firstMethod = function() {
//..
}
this.someOtherMethod = function() {
//..
}
});
使用module.factory('factoryName', function(){})
当您使用module.factory()
创建服务时,作为第二个参数传递的function()
的返回值将成为 AngularJS 注册并稍后在需要时注入到其他服务/控制器的服务对象。
使用module.factory()
在自定义服务对象中声明方法的语法为:
module.factory('DemoService', function() {
//"factory" will be used as service instance
var factory = {};
factory.firstMethod = function() {
//..
}
factory.someOtherMethod = function() {
//..
}
return factory;
});
如何使用 AngularJS 服务
此示例演示了自定义服务的用法,该服务具有用于显示不同时区的当前时间的逻辑。 首先创建自定义服务。
var app = angular.module('myApp', []);
//Create a function
function TimeObj() {
var cities = {
'Los Angeles': -8,
'New York': -5,
'London': 0
};
this.getTZDate = function(city) {
var localDate = new Date();
var utcTime = localDate.getTime() + localDate.getTimezoneOffset() * 60 * 1000;
return new Date(utcTime + (60 * 60 * 1000 * cities[city]));
};
this.getCities = function() {
var cList = [];
for (var key in cities) {
cList.push(key);
}
return cList;
};
}
//Register as service 'TimeService'
app.service('TimeService', [TimeObj]);
现在要使用此服务,请使用如下控制器:
app.controller('LAController', ['$scope', 'TimeService',
function($scope, timeS) {
$scope.myTime = timeS.getTZDate("Los Angeles").toLocaleTimeString();
}
]);
app.controller('NYController', ['$scope', 'TimeService',
function($scope, timeS) {
$scope.myTime = timeS.getTZDate("New York").toLocaleTimeString();
}
]);
app.controller('LondonController', ['$scope', 'TimeService',
function($scope, timeS) {
$scope.myTime = timeS.getTZDate("London").toLocaleTimeString();
}
]);
现在,您可以在网页中使用控制器显示不同的时间。
<html ng-app="myApp">
<head>
<title>AngularJS Custom Time Service</title>
<style>
span {
color: lightgreen; background-color: black;
border: 3px ridge; padding: 2px;
font: 14px/18px arial, serif;
}
</style>
</head>
<body>
<h2>Custom Time Service:</h2><hr>
<div ng-controller="LAController">
Los Angeles Time:
<span>{{myTime}}</span>
</div><hr>
<div ng-controller="NYController">
New York Time:
<span>{{myTime}}</span>
</div><hr>
<div ng-controller="LondonController">
London Time:
<span>{{myTime}}</span>
</div>
</body>
</html>
输出将如下所示:
请参阅 CodePen 上的 Angular 服务演示 – 时区示例,作者为 Lokesh(@howtodoinjava)。
这就是 AngularJS 服务入门教程的全部内容。 将我的问题放在评论部分。
学习愉快!
{% endraw %}
{% raw %}
AngularJS Spring MVC Rest 示例
原文: https://howtodoinjava.com/angularjs/angularjs-http-restful-api-example/
在这个 angularjs spring mvc crud 示例中,我们将学习使用 AngularJS $http
服务来调用 RESTful API(HTTP GET,PUT,POST,DELETE)操作。 此外,我们将使用 RESTFul API 的响应来刷新此示例中使用的屏幕数据。
Table of Contents
1\. Overview of example
2\. $http usage - for impatient
3\. RESTFul APIs used in example
4\. Client source code
5\. How example works?
1. 示例概述
在这个 angularjs spring mvc 示例应用中,我们将构建一个用于员工管理的屏幕。 您将可以使用各种链接和按钮从此屏幕中获取/添加/编辑/删除员工。 屏幕看起来像这样:
angular http 服务示例
屏幕选项非常简单。 您可以使用以下表格添加员工。 所有员工都列在上表中。 您可以通过单击该行中的“删除”链接来删除员工。 同样,单击编辑链接将在下面的表格中填充员工详细信息,您可以通过按提交按钮保存更改。
2. AngularJS $http
用法 – 不耐烦
尽管我将在本教程的后面部分进行详细介绍,但是如果您急于阅读本节以了解对 REST API 的$http
调用。
-
HTTP GET 操作
Angular
$http
可以以下方式用于调用 HTTP GET api。 在此示例中,此代码用于从服务器获取所有员工。$http({ method : 'GET', url : 'employees' }).then(function successCallback(response) { $scope.employees = response.data.employees; }, function errorCallback(response) { console.log(response.statusText); });
GET 调用上方使用相对 URL
/employees
。 如果当前位置为HTTP GET http://localhost:8080/myapplication
,则会调用HTTP GET http://localhost:8080/myapplication/employees
URL。 您也可以使用完整的应用网址,例如 “http://localhost:8080/myapplication/employees
”。 两种 URL 模式都可以使用。默认情况下,angular 使用异步 HTTP 调用。 因此,我使用了两个函数
successCallback()
和errorCallback()
,它们将从服务器返回响应后由 angular 调用。 -
HTTP POST 操作
Angular
$http
可以以下方式用于调用 HTTP POST api。 在此示例中,此代码用于将员工添加到系统中。$http({ method : "POST", url : "employees", data : angular.toJson($scope.form), headers : { 'Content-Type' : 'application/json' } }).then( _success, _error );
在上面的方法调用中,我已使用
angular.toJson()
方法以 JSON 格式传递了请求有效负载,然后将content-type
标头参数设置为application/json
。 -
HTTP PUT 操作
Angular
$http
可以以下方式用于调用 HTTP PUT api。 在此示例中,此代码用于将员工更新到系统中。$http({ method : "PUT", url : "employees/" + $scope.form.id, data : angular.toJson($scope.form), headers : { 'Content-Type' : 'application/json' } }).then( _success, _error );
-
HTTP DELETE 操作
Angular
$http
可以以下方式用于调用 HTTP DETELE api。 在此示例中,此代码用于将员工删除到系统中。$http({ method : "DELETE", url : "employees/" + employee.id }).then( _success, _error );
3. 示例中使用的 Spring REST API
现在让我们看一下本示例中使用的 RESTful API。 这些是使用 Spring REST JSON 示例的源代码创建的。
package com.howtodoinjava.demo.controller;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.howtodoinjava.demo.model.EmployeeListVO;
import com.howtodoinjava.demo.model.EmployeeVO;
@Controller
public class EmployeeRESTController
{
//Local storage of employees for demo; You will use database here
private static EmployeeListVO employees = new EmployeeListVO();
//add some employees here
public EmployeeRESTController()
{
EmployeeVO empOne = new EmployeeVO(1,"Lokesh","Gupta","howtodoinjava@gmail.com");
EmployeeVO empTwo = new EmployeeVO(2,"Amit","Singhal","asinghal@yahoo.com");
EmployeeVO empThree = new EmployeeVO(3,"Kirti","Mishra","kmishra@gmail.com");
employees.getEmployees().add(empOne);
employees.getEmployees().add(empTwo);
employees.getEmployees().add(empThree);
}
//Utility methods for getting employee by id
private EmployeeVO _getEmployeeById(int id){
for(EmployeeVO e : employees.getEmployees()){
if(e.getId() == id){
return e;
}
}
return null;
}
/**
* HTTP GET - Get all employees
* */
@RequestMapping(value = "/employees", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
public ResponseEntity<EmployeeListVO> getAllEmployeesJSON()
{
return new ResponseEntity<EmployeeListVO>(employees, HttpStatus.OK);
}
/**
* HTTP POST - Create new Employee
* */
@RequestMapping(value = "/employees", consumes = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.POST)
public ResponseEntity<String> createEmployee(@RequestBody EmployeeVO employee)
{
employee.setId(employees.getEmployees().size() + 1);
employees.getEmployees().add(employee);
return new ResponseEntity<String>(HttpStatus.CREATED);
}
/**
* HTTP PUT - Update employee
* */
@RequestMapping(value = "/employees/{id}", consumes = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.PUT)
public ResponseEntity<EmployeeVO> updateEmployee(@PathVariable("id") int id, @RequestBody EmployeeVO employee)
{
EmployeeVO emp = _getEmployeeById(id);
if(emp != null){
emp.setFirstName(employee.getFirstName());
emp.setLastName(employee.getLastName());
emp.setEmail(employee.getEmail());
return new ResponseEntity<EmployeeVO>(emp, HttpStatus.OK);
}
return new ResponseEntity<EmployeeVO>(HttpStatus.NOT_FOUND);
}
/**
* HTTP DELETE - Delete employee
* */
@RequestMapping(value = "/employees/{id}", method = RequestMethod.DELETE)
public ResponseEntity<String> deleteEmployee(@PathVariable("id") int id)
{
EmployeeVO employee = _getEmployeeById(id);
if(employee != null){
employees.getEmployees().remove(employee);
return new ResponseEntity<String>(HttpStatus.OK);
}
return new ResponseEntity<String>(HttpStatus.NOT_FOUND);
}
}
4. 使用 angularjs 的 Spring MVC 视图代码
现在,让我们看看运行此示例的完整版本的客户端代码(HTML + AngularJS)。
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>
AngularJS - REST Demo using $http service
</title>
<!-- Load AngularJS -->
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<script type="text/javascript">
var app = angular.module("UserManagement", []);
//Controller Part
app.controller("UserManagementController", function($scope, $http) {
//Initialize page with default data which is blank in this example
$scope.employees = [];
$scope.form = {
id : -1,
firstName : "",
lastName : "",
email : ""
};
//Now load the data from server
_refreshPageData();
//HTTP POST/PUT methods for add/edit employee
$scope.submitEmployee = function() {
var method = "";
var url = "";
if ($scope.form.id == -1) {
//Id is absent so add employee - POST operation
method = "POST";
url = 'employees';
} else {
//If Id is present, it's edit operation - PUT operation
method = "PUT";
url = 'employees/' + $scope.form.id;
}
$http({
method : method,
url : url,
data : angular.toJson($scope.form),
headers : {
'Content-Type' : 'application/json'
}
}).then( _success, _error );
};
//HTTP DELETE- delete employee by Id
$scope.removeEmployee = function(employee) {
$http({
method : 'DELETE',
url : 'employees/' + employee.id
}).then(_success, _error);
};
//In case of edit employee, populate form with employee data
$scope.editEmployee = function(employee) {
$scope.form.firstName = employee.firstName;
$scope.form.lastName = employee.lastName;
$scope.form.email = employee.email;
$scope.form.id = employee.id;
};
/* Private Methods */
//HTTP GET- get all employees collection
function _refreshPageData() {
$http({
method : 'GET',
url : 'employees'
}).then(function successCallback(response) {
$scope.employees = response.data.employees;
}, function errorCallback(response) {
console.log(response.statusText);
});
}
function _success(response) {
_refreshPageData();
_clearForm()
}
function _error(response) {
console.log(response.statusText);
}
//Clear the form
function _clearForm() {
$scope.form.firstName = "";
$scope.form.lastName = "";
$scope.form.email = "";
$scope.form.id = -1;
};
});
</script>
<style>
.button {
cursor: pointer;
color: blue;
}
td,th{
border: 1px solid gray;
width: 25%;
text-align: left;
}
table {
width: 600px;
}
</style>
<head>
<body ng-app="UserManagement" ng-controller="UserManagementController">
<h1>
AngularJS - Use $http to invoke RESTful APIs
</h1>
<table>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Email</th>
<th>Actions</th>
</tr>
<tr ng-repeat="employee in employees">
<td>{{ employee.firstName }}</td>
<td>{{ employee.lastName }}</td>
<td>{{ employee.email }}</td>
<td><a ng-click="editEmployee( employee )" class="button">Edit</a> | <a ng-click="removeEmployee( employee )" class="button">Remove</a></td>
</tr>
</table>
<h2>Add/Edit Employee</h2>
<form ng-submit="submitEmployee()">
<table>
<tr>
<td>First Name</td>
<td><input type="text" ng-model="form.firstName" size="60" /></td>
</tr>
<tr>
<td>Last Name</td>
<td><input type="text" ng-model="form.lastName" size="60" /></td>
</tr>
<tr>
<td>Email</td>
<td><input type="text" ng-model="form.email" size="60" /></td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="Submit" /></td>
</tr>
</table>
</form>
</body>
</html>
5. Spring MVC angularjs 示例如何工作?
尽管我添加了源代码注解以使代码易于理解,但让我们逐步了解一些要点。
-
参见
app.controller("UserManagementController", function($scope, $http)
行。 它创建 Angular 控制器组件,并传递$http
服务和$scope
变量的相关性。$http
用于进行 REST 调用,$scope
用于与页面数据进行交互。 -
$scope
具有两个数据元素。$scope.employees
引用页面中的所有员工集合,$scope.form
映射到页面中的表单元素字段。 -
加载页面后,将调用
_refreshPageData()
,该调用将调用 HTTP GET api,以 JSON 格式从服务器获取所有员工数据。 检索到数据后,将使用$scope.employees = response.data.employees
将其映射到$scope.employees
。 该调用将自动刷新 UI,并使用员工数据填充表格。 -
使用
ng-click="removeEmployee( employee )"
将页面中的删除链接绑定到removeEmployee()
函数。 该调用具有附加参数employee
,该参数用于标识需要从表中删除哪个雇员(employee.id
用于获取雇员 ID)。 -
类似地,编辑链接与
ng-click="editEmployee( employee )"
绑定。 在editEmployee()
函数内部,我们通过下面的映射简单地使用现有员工数据填充表单文本字段。$scope.editEmployee = function(employee) { $scope.form.firstName = employee.firstName; $scope.form.lastName = employee.lastName; $scope.form.email = employee.email; $scope.form.id = employee.id; };
使用修改过的员工数据更新页面后,我们通过为表单字段分配空白值来清除表单。
function _clearForm() { $scope.form.firstName = ""; $scope.form.lastName = ""; $scope.form.email = ""; $scope.form.id = -1; };
-
对于 PUT 和 POST 方法,由于代码相似,我们使用了相同的函数来避免代码重复。 我们仅根据用户操作更改
method
和url
参数。 -
为了显示从服务器获取的用户集合,我们使用了
ng-repeat="employee in employees"
循环。
其余的事情几乎可以自我解释。 如果您有任何疑问或疑问,请在下面给我留言。
学习愉快!
{% endraw %}
JavaScript / jQuery 教程
Ajax 教程 – 面向初学者的 Ajax 指南
原文: https://howtodoinjava.com/ajax/complete-ajax-tutorial/
异步 JavaScript 和 XML( AJAX )是一种与服务器交换数据并更新网页的某些部分的技术,而无需重新加载整个网页。 换句话说, AJAX 允许通过与幕后服务器交换少量数据来异步更新网页。 如果应用未使用 AJAX,则必须在用户提出的每个请求上重新加载网页。
在本面向初学者的 ajax 教程中,我将介绍在开发基于 ajax 的应用之前,您应该了解的所有基本和重要事实,以充分利用 ajax 的功能。
Table of Contents
1\. How ajax works?
2\. Ajax XMLHttpRequest object
3\. XMLHttpRequest methods (open(), setRequestHeader(), send(), abort())
4\. Synchronous and Asynchronous requests
- onreadystatechange event, Handling server response
5\. Ajax demo
- Asynchronous example, Synchronous example
6\. Popular ajax libraries
- JQuery, Prototype
7\. 下载源码
1. ajax 如何工作?
重要的是要了解 Ajax 不是单一技术,而是一组技术,例如 HTML,CSS,DOM 和 JavaScript 等。 HTML 和 CSS 可以组合使用以标记和样式信息。 通过 JavaScript 访问 DOM ,以动态显示所呈现的信息,并允许用户与之交互。 JavaScript 和XMLHttpRequest
对象提供了一种在浏览器和服务器之间异步交换数据以避免重新加载整个页面的方法。
近年来,XML 的本质已经减少。 JSON (JavaScript 对象表示法)通常用作数据交换的替代格式,尽管其他格式(例如预格式化的 HTML 或纯文本)也可以用于数据目的。
1.1 Ajax 生命周期
通常,对服务器的 ajax 调用和从服务器获取响应(生命周期事件)涉及以下步骤:
- 您在浏览器的地址栏中键入网页的 URL,然后按
Enter
。 页面已加载到浏览器窗口中。 - 某些操作会触发事件,例如用户单击按钮。
- 事件触发 ajax 调用,并使用 xml 或 json 向服务器发送请求。
- 服务器服务从 ajax / http 请求获取输入,并处理该请求。 如果需要,它还会准备响应数据。
- 服务器将数据发送回发出请求的原始网页。
- 另一个 JavaScript 函数称为回调函数,该函数接收数据并更新网页。
很容易,对吧? 让我们在下面的图片中查看所有动作。
AJAX 如何工作?
2. Ajax XMLHttpRequest
对象
AJAX 的核心是XMLHttpRequest
对象(可在客户端脚本语言(如 javascript)中使用)。 XMLHttpRequest
对象用于与后台的服务器交换数据。 所有现代浏览器(IE7+,Firefox,Chrome,Safari 和 Opera)都具有内置的XMLHttpRequest
对象。
如果您使用的是 IE5 或 IE6(我想知道是否有人仍在使用它),则ActiveXObject
将用于服务器通信以发送 Ajax 请求。
2.1 创建XMLHttpRequest
对象
这样创建一个XMLHttpRequest
的新对象:
//Creating a new XMLHttpRequest object
var xmlhttp;
if (window.XMLHttpRequest)
{
xmlhttp = new XMLHttpRequest(); //for IE7+, Firefox, Chrome, Opera, Safari
}
else
{
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); //for IE6, IE5
}
可以重新使用此xmlhttp
变量发送多个 ajax 请求,而无需创建新对象。 出于安全原因,XMLHttpRequest
受浏览器的相同来源策略约束。 这意味着只有在向原始网页提供服务的同一服务器上发出请求时,请求才会成功。
3. XMLHttpRequest
方法
为了发送请求和设置请求属性,XMLHttpRequest
对象具有一些方法。 让我们来看看他们:
3.1 open(method, url, isAsync, userName, password)
必须通过打开方法初始化XMLHttpRequest
对象的 HTTP 和 HTTPS 请求。 此方法指定请求的类型(GET,POST 等),URL,以及是否应异步处理请求。 我将在下一部分中介绍第三个参数。
第四个和第五个参数分别是用户名和密码。 如果服务器需要此参数,则可以提供这些参数或仅提供用户名以进行身份验证和授权。
例:
xmlhttp.open("GET","report_data.xml",true);
xmlhttp.open("GET","sensitive_data.xml",false);
xmlhttp.open("POST","saveData",true,"myUserName","somePassord");
3.2 setRequestHeader(name, value)
成功初始化请求后,可以调用XMLHttpRequest
对象的setRequestHeader
方法来发送带有请求的 HTTP 标头。
示例:
//Tells server that this call is made for ajax purposes.
xmlhttp.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
3.3 send(payload)
要发送 HTTP 请求,必须调用XMLHttpRequest
的send
方法。 此方法接受单个参数,该参数包含要与请求一起发送的内容。
在 POST 请求中,该内容是必需的。 对于 GET 方法,隐式传递null
作为参数。
示例:
xmlhttp.send(null); //Request with no data in request body; Mostly used in GET requests.
xmlhttp.send( {"id":"23423"} ); //Request with data in request body; Mostly used in POST/ PUT requests.
3.4 abort()
如果XMLHttpRequest
对象的readyState
尚未变为 4 (请求完成),则此方法将中止请求。 abort
方法确保回调方法不会在异步请求中被调用。
语法:
//Abort the processing
xmlhttp.abort();
除上述方法外,onreadystatechange
事件监听器非常重要,我们将在下一部分中进行讨论。
4. 同步和异步 ajax 请求
XMLHttpRequest
对象能够根据网页中的要求发送同步和异步请求。 该行为由打开方法的第三个参数控制。 对于异步请求,此参数设置为true
,对于同步请求,此参数设置为false
。
xmlhttp.open("GET", "report_data.xml", true); //Asynchrnonos request as third parameter is true
xmlhttp.open("GET", "report_data.xml", false); Synchrnonos request as third parameter is false
如果未提供,则此参数的默认值为“true
”。
异步 Ajax 请求不会阻止网页,并且在服务器上处理请求时,用户可以继续与页面上的其他元素进行交互。 您应该始终使用异步 Ajax 请求,因为同步 Ajax 请求会使 UI(浏览器)无响应。 这意味着在请求完成之前,用户将无法与网页进行交互。
在极少数情况下,应格外小心地使用同步请求。 例如,如果您要使用 ajax 在客户端上嵌入新的 JavaScript 文件,然后再引用该 JavaScript 文件中的类型和/或对象,则应使用同步 Ajax 请求。 然后,应该通过使用同步 Ajax 请求来包括对该新 JS 文件的提取。
4.1 同步 ajax 请求示例
var request = new XMLHttpRequest();
request.open('GET', '/bar/foo.txt', false); //"false" makes the request synchronous
request.send(null);
if(request.status === 200)
{
//request successful; handle the response
}
else
{
//Request failed; Show error message
}
4.2 异步 Ajax 请求示例
var request = new XMLHttpRequest();
request.open('GET', '/bar/foo.txt', true); //"true" makes the request asynchronous
request.onreadystatechange = function() {
if (request.readyState == 4) {
if (request.status == 200)
{
//request succeed
}
else
{
//request failed
}
}
};
request.send(null)
4.3 onreadystatechange
事件
在上面的示例中,onreadystatechange
是向XMLHttpRequest
请求注册的事件监听器。 onreadystatechange
存储一个函数,该函数将处理从服务器返回的响应。 在请求的生命周期中,所有重要事件都将被调用。 每次在请求处理中完成一个步骤时,readyState
的值都会更改并设置为其他值。 让我们看一下可能的值:
0:未初始化请求
1:建立服务器连接
2:接收请求
3:处理请求
4:请求已完成,响应已准备就绪
4.4 处理来自服务器的响应
要从服务器获取响应,请使用XMLHttpRequest
对象的responseText
或responseXML
属性。 如果来自服务器的响应是 XML,并且您要将其解析为 XML 对象,请使用responseXML
属性。 如果来自服务器的响应不是 XML,请使用responseText
属性。
responseText
:从服务器获取响应作为字符串
responseXML
:从服务器获取 XML 响应
示例代码:
if (xmlhttp.readyState == 4) {
if (xmlhttp.status == 200)
{
document.getElementById("message").innerHTML = xmlhttp.responseText;
}
else {
alert('Something is wrong !!');
}
}
5. Ajax 教程 – 演示
出于演示目的,我创建了一个非常简单的 HelloWorld 应用。 在此应用中,网页发送 ajax GET 请求以查询当前服务器的系统时间。 作为响应,服务器发送当前时间。 很简单。
5.1 异步请求示例
为了使网页能够发送此类请求,我在 JSP 页面中编写了以下 javascript 代码:
function ajaxAsyncRequest(reqURL)
{
//Creating a new XMLHttpRequest object
var xmlhttp;
if (window.XMLHttpRequest){
xmlhttp = new XMLHttpRequest(); //for IE7+, Firefox, Chrome, Opera, Safari
} else {
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); //for IE6, IE5
}
//Create a asynchronous GET request
xmlhttp.open("GET", reqURL, true);
//When readyState is 4 then get the server output
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4) {
if (xmlhttp.status == 200)
{
document.getElementById("message").innerHTML = xmlhttp.responseText;
//alert(xmlhttp.responseText);
}
else
{
alert('Something is wrong !!');
}
}
};
xmlhttp.send(null);
}
并触发 ajax 请求,应单击一个按钮,其内容为:
<input type="button" value="Show Server Time" onclick='ajaxAsyncRequest("get-current-time")'/>
为了处理服务器端的请求,我编写了一个像这样的 servlet:
public class GetTimeServlet extends HttpServlet
{
private static final long serialVersionUID = 1L;
public void doGet (HttpServletRequest request,HttpServletResponse response)
throws ServletException, IOException
{
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
PrintWriter out = response.getWriter();
Date currentTime= new Date();
String message = String.format("Currently time is %tr on %tD.",currentTime, currentTime);
out.print(message);
}
}
上面的代码将以文本形式返回当前服务器时间,以客户端代码的形式接收并打印在网页上。
5.2 同步请求示例
要发送同步 ajax 请求,请使用以下命令更改index.jsp
文件中的 javascript 代码:
function ajaxSyncRequest(reqURL)
{
//Creating a new XMLHttpRequest object
var xmlhttp;
if (window.XMLHttpRequest){
xmlhttp = new XMLHttpRequest(); //for IE7+, Firefox, Chrome, Opera, Safari
} else {
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); //for IE6, IE5
}
//Create a asynchronous GET request
xmlhttp.open("GET", reqURL, false);
xmlhttp.send(null);
//Execution blocked till server send the response
if (xmlhttp.readyState == 4) {
if (xmlhttp.status == 200)
{
document.getElementById("message").innerHTML = xmlhttp.responseText;
}
else
{
alert('Something is wrong !!');
}
}
}
您无需在同步请求中检查就绪状态,因为仅当请求完成时响应才可用。 到此为止,页面将被阻止。
6. 流行的 ajax 库
显而易见,如今,ajax 是使网页具有高度交互性和用户友好性的非常流行的技术。 为了简化与 Ajax 相关组件的开发,当今市场上的开发人员可以使用各种框架。 好消息是它们都可以免费使用。
6.1 jQuery
jQuery 可能在其替代品中很受欢迎。 它拥有自己的开发者社区,该社区也非常活跃。 使用 jquery 发送 ajax 请求的示例代码如下:
//Current request reference; can be used else where
var request;
/* attach a submit handler to the form */
$("#buttonId").submit(function(event) {
// abort any pending request
if (request) {
request.abort();
}
/* stop form from submitting normally */
event.preventDefault();
/*clear result div*/
$("#result").html('');
/* get some values from elements on the page: */
var values = $(this).serialize();
/* Send the data using post and put the results in a div */
request =$.ajax({
url: "ajaxRequest",
type: "post",
data: values,
success: function(){
$("#result").html('submitted successfully');
},
error:function(){
$("#result").html('there is error while submit');
}
});
});
6.2 Prototype
Prototype 是另一个用于相同目的的流行框架。 但是,请注意,已知 Prototype 与某些其他框架不兼容。 使用 Prototype 发送 ajax 请求的示例代码如下所示:
new Ajax.Request('/some_url',
{
method:'get',
onSuccess: function(transport)
{
var response = transport.responseText || "no response text";
},
onFailure: function()
{
alert('Something went wrong...');
}
});
这就是 ajax 教程的全部内容。 将来我会在 ajax 上写更多文章。 您可能想注册您的电子邮件 ID,以获取更新通知。
Ajax 教程源代码下载
源码下载
学习愉快!
完整的 jQuery Ajax($.ajax
)教程
原文: https://howtodoinjava.com/jquery/jquery-ajax-tutorial/
在本 Ajax 教程中,我们已经学习了 Ajax(异步 JavaScript 和 XML)。 我们了解了有关 ajax 的工作原理和基本组成部分的基本但重要的概念。 让我们继续进行讨论,看看如何利用 jQuery 充分利用 ajax 的功能来使应用开发变得简单,快速和有效。
Table of Contents
$.ajax() Method
jqXHR (jQuery XMLHttpRequest) vs. XHR (XMLHttpRequest)
Invoking jQuery Ajax HTTP Methods
Synchronous vs. Asynchronous Communication
jQuery Ajax Global Event Handlers
$.ajaxSend()
$.ajaxStart()
$.ajaxStop()
$.ajaxSuccess()
$.ajaxError()
$.ajaxComplete()
Using $.ajaxSetup() to Globalize Parameters
Using $.ajaxPrefilter() to filter Ajax Requests
Other Ajax Powered Functions in jQuery
$.get() and $.post() Functions
$.load() Function
$.getScript()
$.ajax()
方法
jQuery Ajax 的根是ajax()
函数。 此函数用于执行默认情况下异步的 HTTP 请求。 使用此函数的语法是:
$.ajax({name:value, name:value, ... })
参数为 AJAX 请求指定一个或多个名称/值对。 下表中可能的名称/值:
名称 | 值/说明 |
---|---|
async |
一个布尔值,指示是否应异步处理请求。 默认为true 。 请注意,从 jQuery 1.8 开始,async: false 的使用已被弃用。 您必须使用success / error / complete 回调选项,而不要使用jqXHR 对象的相应方法,例如jqXHR.done() 或已弃用的jqXHR.success() 。 |
beforeSend(xhr) |
发送请求之前运行的函数 |
cache |
一个布尔值,指示浏览器是否应缓存请求的页面。 默认为true |
complete(xhr,status) |
请求完成后要运行的函数(成功和错误函数之后) |
contentType |
将数据发送到服务器时使用的内容类型。 默认值为:“application/x-www-form-urlencoded ” |
context |
为所有与 AJAX 相关的回调函数指定“this ”值 |
data |
指定要发送到服务器的数据 |
dataFilter(data,type) |
用于处理XMLHttpRequest 的原始响应数据的函数 |
dataType |
服务器响应期望的数据类型。 |
error(xhr,status,error) |
如果请求失败,则运行的函数。 |
global |
一个布尔值,指定是否触发请求的全局 AJAX 事件句柄。 默认为true |
ifModified |
一个布尔值,指定是否仅在自上一个请求以来响应已更改的情况下,请求才成功。 默认值为:false 。 |
jsonp |
覆盖 jsonp 请求中的回调函数的字符串 |
jsonpCallback |
为 jsonp 请求中的回调函数指定名称 |
password |
指定在 HTTP 访问认证请求中使用的密码。 |
processData |
一个布尔值,指定是否应将与请求一起发送的数据转换为查询字符串。 默认为true |
scriptCharset |
指定请求的字符集 |
success(result,status,xhr) |
请求成功时要运行的函数 |
timeout |
请求的本地超时(以毫秒为单位) |
traditional |
一个布尔值,指定是否使用传统的参数序列化样式 |
type |
指定请求的类型。 (获取或发布) |
url |
指定将请求发送到的 URL。 默认为当前页面 |
username |
指定在 HTTP 访问认证请求中使用的用户名 |
xhr |
用于创建XMLHttpRequest 对象的函数 |
例如,示例 ajax 请求可以像这样(直到 jQuery 1.7 版本):
$.ajax({
url: "/app-url/relative-url",
data: {
name : "The name",
desc : "The description"
},
success: function(data, textStatus, jqXHR)
{
alert("Success: " + response);
},
error: function(jqXHR, textStatus, errorThrown)
{
alert("Error");
}
});
在 jQuery 1.8 之后,您可以编写如下的 ajax 调用:
$.ajax({
url: "/app-url/relative-url",
data: {
name : "The name",
desc : "The description"
}
})
.done (function(data, textStatus, jqXHR) {
alert("Success: " + response);
})
.fail (function(jqXHR, textStatus, errorThrown) {
alert("Error");
})
.always (function(jqXHROrData, textStatus, jqXHROrErrorThrown) {
alert("complete");
});
jqXHR(jQuery XMLHttpRequest
)与 XHR(XMLHttpRequest
)
jQuery 1.8 在 jQuery 模式方面带来了重大变化。 此更改是$.ajax()
方法的返回类型。 在 1.7 版之前,返回类型为XHR
,即XMLHttpRequest
,但从 1.8 版开始,其返回类型为jqXHR
,即 jQuery XMLHttpRequest
。
在 jQuery 1.8 中,库使用超集 API 包浏览器本机XMLHttpRequest
对象,并返回jqXHR
对象。 jqXHR
对象可模拟本地XHR
功能,并提供更多功能,例如:
- 它处理 HTTP 请求标头(
Last-Modified
,etag,Content-Type
,MIME 类型等) - 它处理请求的回调(包括
Promise
回调.done()
、.fail()
等) - 它处理为请求设置的所有预过滤器
- 它处理为请求设置的任何超时
- 它处理任何跨域调用(包括 jsonp)
$.ajax()
返回的 jqXHR 对象实现Promise
接口。 该对象具有一个Promise
对象的所有属性,方法和行为。
jQuery 库作者已努力使其向后兼容,但不支持onreadystatechange()
方法。
调用 jQuery Ajax HTTP 方法
让我们看看 jQuery ajax 如何调用不同的 HTTP 方法。 我只是在编写代码框架,希望您根据需要更改代码。
jQuery Ajax HTTP GET 或 DELETE 方法
$.ajax({
url: "/app-url/relative-url",
type: "GET", //Or "DELETE" for http delete calls
dataType: 'json',
})
.done (function(data, textStatus, jqXHR) {
alert("Success: " + response);
})
.fail (function(jqXHR, textStatus, errorThrown) {
alert("Error");
})
.always (function(jqXHROrData, textStatus, jqXHROrErrorThrown) {
alert("complete");
});
jQuery Ajax HTTP POST 或 PUT 方法
$.ajax({
url: "/app-url/relative-url",
type: "POST", //Use "PUT" for HTTP PUT methods
dataType: 'json',
data: {
key : "value",
}
})
.done (function(data, textStatus, jqXHR) {
alert("Success: " + response);
})
.fail (function(jqXHR, textStatus, errorThrown) {
alert("Error");
})
.always (function(jqXHROrData, textStatus, jqXHROrErrorThrown) {
alert("complete");
});
同步与异步通信
默认情况下,所有通过 jQuery 发送的 ajax 请求都是异步的。 如果要进行同步调用(完全不建议这样做,因为它可能导致浏览器冻结,这会导致一些非常不满意的用户),请在函数调用中使用“async : false
”参数,如下所示:
$.ajax({
url: "/app-url/relative-url",
type: "POST",
async: false,
dataType: 'json',
data: {
key : "value",
}
})
.done (function(data, textStatus, jqXHR) {
alert("Success: " + response);
})
.fail (function(jqXHR, textStatus, errorThrown) {
alert("Error");
})
.always (function(jqXHROrData, textStatus, jqXHROrErrorThrown) {
alert("complete");
});
在 jQuery 1.8 及更高版本中,不赞成使用“async:false
”选项。
jQuery Ajax 全局事件处理器
除了上述三种方法(即done()
,fail()
或always()
)之外,jQuery 还具有一组全局 AJAX 函数,您可以使用这些函数来监听通过 jQuery 发送的所有 AJAX 请求中的 AJAX 事件。 让我们来看看它们:
$.ajaxSend()
在通过 jQuery 发送 AJAX 请求之前,总是会调用在ajaxSend()
函数中注册的回调函数。
$(document).ajaxSend(function() {
console.log("called before each send");
});
$.ajaxStart()
每当即将发送 Ajax 请求时,jQuery 都会检查是否还有其他未完成的 Ajax 请求。 如果没有正在进行中,则 jQuery 触发ajaxStart
事件。
如果在全局选项设置为false
的情况下调用$.ajax()
或$.ajaxSetup()
,则将不会触发ajaxStart()
方法。
$( document ).ajaxStart(function() {
$( "#loading" ).show();
});
$.ajaxStop()
每当 Ajax 请求完成时,jQuery 都会检查是否还有其他未完成的 Ajax 请求。 如果没有剩余的内容,jQuery 将触发ajaxStop
事件。
如果在全局选项设置为false
的情况下调用$.ajax()
或$.ajaxSetup()
,则不会触发.ajaxStop()
方法。
$( document ).ajaxStop(function() {
$( "#loading" ).hide();
});
$.ajaxSuccess()
每当 Ajax 请求成功完成时,jQuery 就会触发ajaxSuccess
事件。
$( document ).ajaxSuccess(function( event, xhr, settings ) {
$( "#msg" ).append( "<li>Successful Request!</li>" );
});
$.AjaxError()
只要 Ajax 请求完成并出现错误,jQuery 就会触发ajaxError
事件。
$( document ).ajaxError(function( event, xhr, settings ) {
$( "#msg" ).append( "<li>Failed Request!</li>" );
});
$.ajaxComplete()
每当 Ajax 请求完成时,jQuery 都会触发ajaxComplete
事件。
$( document ).ajaxComplete(function( event, xhr, settings ) {
$( "#msg" ).append( "<li>Request Completed !!</li>" );
});
使用$.ajaxSetup()
全局化参数
您是否认为将一组通用参数传递给所有 ajax 请求很无聊,您可以使用$.ajaxSetup()
函数对其进行一次注册,然后在所有 ajax 调用中重复使用它们。 $.ajaxSetup()
函数可用于设置所有 AJAX 调用使用的选项,包括通过$.ajax()
,$.load()
,$.get()
等执行的选项。
您可以在$.ajaxSetup()
中设置所有选项,这些选项可以设置为$.ajax()
调用。 例如:
$.ajaxSetup({
type : "POST"
});
上面的函数默认会将来自应用的所有 jQuery ajax 请求设为 HTTP POST 方法。 因此,无论何时,您想发送 HTTP GET 方法,都必须在特定的$.ajax()
调用中对其进行显式设置,如下所示:
$.ajax({
url : "/app-url/relative-url",
type : "GET"
});
使用$.ajaxPrefilter()
过滤 Ajax 请求
如果您曾经在服务器端进行 Web 开发,那么您会认识到过滤器是实现某些目标(如输入值清洁等)的好方法。现在 jQuery 也使用ajaxPrefilter
事件在客户端提供了此功能。 使用此功能,您可以在所有 AJAX 调用发送到服务器之前对其进行过滤。
发送请求之前,可以在$.ajaxPrefilter()
中更改传递给$.ajax()
函数的所有 Ajax 选项/参数(“过滤”)。 例如,如果您希望发送到以“/add
”结尾的 URL 的所有 HTTP 请求都必须是 HTTP POST 调用,则可以在此处应用逻辑。
$.ajaxPrefilter(function(options, originalOptions, jqXHR){
if(options.url.indexOf("/add") != -1)
{
options.type = "POST";
}
});
这里的参数是:
options
– 是请求选项originalOptions
– 是$.ajax()
方法提供的选项,未经修改,因此没有ajaxSettings
的默认值jqXHR
– 是请求的 jqXHR 对象
jQuery 中其他由 Ajax 支持的函数
让我们在内部使用 ajax 浏览 jQuery 库提供的其他实用函数,这些函数可以直接使用。
$.get()
和$.post()
函数
jQuery 具有这些函数,可用于发送简化的 HTTP GET 和 HTTP POST 请求。 这是显示如何使用 jQuery 的$.get()
函数的示例:
var parameters = { p1 : "val1", p2 : "val2"};
$.get( "/app/url", parameters )
.done(function(data) {
$("#label").html(data);
})
.fail(function() {
alert( "error" );
})
.always(function() {
alert( "finished" );
});
同样,您可以使用$.post()
函数发出 HTTP POST 请求。
var parameters = { p1 : "val1", p2 : "val2"};
$.post( "/app/url", parameters )
.done(function(data) {
$("#label").html(data);
})
.fail(function() {
alert( "error" );
})
.always(function() {
alert( "finished" );
});
$.load()
函数
jQuery load()
函数通过 AJAX 加载一些 HTML,并将其插入到选定的元素中。 它是后台的简单 HTTP GET 方法,可从服务器获取一些 HTML 并插入元素的 DOM 中。
$("#loadTarget").load("html-response.html");
您也可以只插入一部分已加载的 HTML。 如果在url
后面附加space + jQuery selector
字符串,则load()
将仅插入与选择器匹配的部分已加载 HTML。
$("#loadTarget").load("html-response.html #someDiv");
在上面的示例中,ajax 调用将从 URL html-response.html
加载 HTML 响应,然后对id=someDiv
执行响应的 jQuery ID 选择器,然后将结果 HTML 插入loadTarget
的innerHTML
中。
如果加载的 HTML 包含任何 JavaScript,则将 HTML 插入目标 HTML 元素时将执行该 JavaScript。 但是,如果加载片段(URL + jQuery 选择器),则在插入 HTML 之前,将删除在加载的文件中找到的所有 JavaScript。
$.getScript()
函数
jQuery 中的$.getScript()
函数加载一个 JavaScript 文件并执行它。 此函数使用 jQuery 的基础 AJAX 函数,因此由于跨域限制,$.getScript()
函数无法从其他域加载脚本。 例如:
$.getScript("js/myscript.js");
这就是使用 jQuery 处理 ajax 调用的全部内容。 随时在下面的评论部分中发表您的评论和建议。
学习愉快!
jQuery 深度克隆示例
原文: https://howtodoinjava.com/jquery/jquery-deep-cloning-example/
jQuery 深度克隆意味着独立更改原始对象或克隆对象不会对其他对象产生影响。 换句话说,两个对象(原始对象和克隆对象)都彼此完全独立。 您可以在对象克隆的指南中阅读有关克隆的更多信息。
使用方法:
var clonedObject = jQuery.extend({}, originalObject);
jQuery 深度克隆示例
在 jquery 克隆示例代码中,我创建了一个User
对象并创建了两个属性,即名字和姓氏。 然后,我为这些属性创建了两个设置器函数,还使用了原型属性添加了另一种方法。
实际上,使用“extend
”关键字的 jquery 克隆机制用于将两个或多个对象的属性和属性复制/合并到第三个或完全新的对象中。 可以在中找到更多详细信息。
示例代码如下所示:
//Create the object
var user = new User($("#fname").val(),$("#lname").val());
//Check the original object values
$("#origUser").html(user.getUserName());
//Cloning is done here
var cloned = $.extend({}, user);
//Change the firstname property
user.fname = 'Noname';
//Let's check the original and cloned object again
$("#origAfterUser").html(user.getUserName());
//Verify cloned is same as original in starting
$("#clonedUser").html(cloned.getUserName());
示例应用
<HTML>
<HEAD>
<TITLE> jQuery Cloning Example </TITLE>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<SCRIPT LANGUAGE="JAVASCRIPT">
var User = function(fname,lname){
this.fname = fname,
this.lname = lname,
this.getFName = function(){
return this.fname;
},
this.getLName = function(){
return this.lname;
}
};
User.prototype.getUserName = function() {
return (this.getFName() + " " + this.getLName());
}
function cloneDemo(){
//Create the object
var user = new User($("#fname").val(),$("#lname").val());
//Check the original object values
$("#origUser").html(user.getUserName());
//Cloning is done here
var cloned = $.extend({}, user);
//Change the firstname property
user.fname = 'Noname';
//Let's check the original and cloned object again
$("#origAfterUser").html(user.getUserName());
//Verify cloned is same as original in starting
$("#clonedUser").html(cloned.getUserName());
}
</SCRIPT>
</HEAD>
<BODY>
<h3>jQuery Cloning Example</h3>
To clone an object, you can directly use clone() method.
<p>
First Name : <input type='text' id='fname'/>
</p>
<p>
Last Name : <input type='text' id='lname'/>
</p>
<p>
<input type='button' value='Create above User and Clone it' onclick="cloneDemo();"/>
</p>
<p>I have changed the first name of orginal Object to 'Noname'</p>
<p>
Original User : <span id='origUser'></span>
</p>
<p>
After Cloning Original User : <span id='origAfterUser'></span>
</p>
<p>
Cloned User Still is : <span id='clonedUser'></span>
</p>
</BODY>
</HTML>
祝您学习愉快!
jQuery 选择器 – 完整列表
原文: https://howtodoinjava.com/jquery/jquery-selectors/
jQuery 选择器也许是 jQuery 库中最重要的方面。 这些选择器使用类似于 CSS 的语法,使开发人员可以轻松选择任何页面元素集并对其执行操作。 了解 jQuery 选择器是最有效地使用 jQuery 库的关键。
jQuery 语句通常遵循以下语法模式:
$(selector).methodName();
selector
是一个字符串表达式,用于标识将被收集到要操作的匹配集合中的 DOM 元素集合。 jQuery 选择器返回 jQuery 对象,而不是从 JavaScript 选择器返回的 DOM 对象。
如果要使用任何元字符(例如.#()
)作为类 / id / 名称的一部分,则需要使用\\
两个反斜杠对字符进行转义。 例如,如果您的元素带有id="my.element"
,则可以使用选择器$("my\\.element")
。
jQuery 提供了许多应用这些选择器的方法。 大致来说,您可以将它们分为以下几类:
Basic Selectors
Attribute Selectors
Content Selectors
Hierarchy Selectors
Form Selectors
Visibility Selectors
Filtered Selectors
基本的 jQuery 选择器
基本选择器集中于 HTML 元素的 id 属性,类属性和标签名称。
语法/范例 | 描述 |
---|---|
所有选择器("*") |
选择页面中的所有元素 |
类选择器(".className") |
选择具有给定类的所有元素。 |
元素选择器("element") |
选择具有给定标签名称的所有元素。 |
ID 选择器("#id") |
选择具有给定 id 属性的单个元素。 |
多重选择器("selector1, selector2, selectorN") |
选择所有指定选择器的合并结果。 |
jQuery 选择器属性
使用 jQuery 选择器的另一种方法是根据 HTML 元素的属性值来选择它们。 它可以是默认属性或任何自定义属性。 在选择器语法中,属性值用[]
括号括起来,例如$("a[rel='nofollow']")
。
这些选择器将属性值视为单个字符串。 例如,$("a[rel='nofollow']")
将选择<a href=”example.html” rel=”nofollow”>Some text</a>
,但不会<a href=”example.html” rel=”nofollow otherValue”>Some text</a>
。
语法/示例 | 描述 |
---|---|
[attributeName] |
选择具有指定属性且具有任何值的元素。 |
[attributeName = "value"] |
选择具有指定属性且其值与特定值完全相等的元素。 |
[attributeName != "value"] |
选择具有指定属性的元素,该元素的值恰好以给定字符串开头。 |
[attributeName ^= "value"] |
选择具有指定属性的元素,该元素的值恰好以给定字符串开头。 |
[attributeName $= "value"] |
选择具有指定属性且其值完全以给定字符串结尾的元素。 比较是区分大小写的。 |
[attributeName ~= "value"] |
选择具有指定属性的元素,该元素的值包含以空格分隔的给定单词。 |
[attributeName *= "value"] |
选择具有指定属性且其值包含给定子字符串的元素。 |
[attributeName |= "value"] |
选择具有指定属性的元素,该元素的值等于给定字符串或以该字符串开头,后跟连字符(- )。 |
[attributeName = "value"][attributeName2="value2"] |
匹配与所有指定的属性过滤器匹配的元素。 |
内容选择器
内容选择器允许您基于 HTML 元素内的内容选择 HTML 元素。
语法/示例 | 描述 |
---|---|
:contains() |
选择所有包含指定文本的元素。 |
:empty |
选择所有没有子元素的元素(包括文本节点)。 |
:has() |
选择包含至少一个与指定选择器匹配的元素的元素。 |
:parent |
:empty 的逆。 选择具有至少一个子节点的所有元素(元素或文本)。 |
层次选择器
层次选择器允许您基于 DOM 层次选择 HTML 元素。 这使您能够通过仅基于 DOM 树中的父项,子项或围绕它们的其他元素来选择内容,从而编写具有更多内容意识的动态代码。
语法/示例 | 描述 |
---|---|
子选择器("parent > child") |
选择parent 指定的元素的child 指定的所有直接子元素。 |
后代选择器("ancestor descendant") |
选择作为给定祖先的后代的所有元素。 |
下一个相邻选择器("prev + next") |
选择所有与“next ”匹配的next 元素,并紧随其后的是“prev ”。 |
下一兄弟姐妹选择器("prev ~ siblings") |
选择“prev ”元素之后的所有兄弟元素,它们具有相同的父元素,并与过滤“siblings ”选择器匹配。 |
表单选择器
表单选择器使您可以根据表单元素的状态选择表单中的元素。
语法/示例 | 描述 |
---|---|
:button 选择器 |
选择所有按钮元素和按钮类型的元素。 |
:checkbox 选择器 |
选择所有类型的元素复选框。 |
:checked 选择器 |
匹配所有选中或选中的元素。 |
:disabled 选择器 |
选择所有禁用的元素。 |
:enabled 选择器 |
选择所有启用的元素。 |
:file 选择器 |
选择文件类型的所有元素。 |
:focus 选择器 |
选择当前处于焦点状态的元素。 |
:image 选择器 |
选择类型为图像的所有元素。 |
:input 选择器 |
选择所有输入,文本区域,选择和按钮元素。 |
:password 选择器 |
选择类型为密码的所有元素。 |
:radio 选择器 |
选择单选类型的所有元素。 |
:reset 选择器 |
选择所有类型为重置的元素。 |
:selected 选择器 |
选择所有选定的元素。 |
:submit 选择器 |
选择类型为提交的所有元素。 |
:text 选择器 |
选择文本类型的所有输入元素。 |
可见性选择器
如果使用可见性来控制网页组件的流程和交互,则使用可见性 jQuery 选择器可以轻松选择隐藏或可见的 HTML 元素。
语法/示例 | 描述 |
---|---|
:hidden 选择器 |
选择所有隐藏的元素。 |
:visible 选择器 |
选择所有可见的元素。 |
筛选选择器
通常,您将需要将 jQuery 选择器优化为更具体的子集。 一种实现方法是使用过滤的选择器。 筛选的选择器在选择器语句的末尾附加一个筛选器,以限制选择器返回的结果。
语法/示例 | 描述 |
---|---|
:animated 选择器 |
运行选择器时,选择动画进行中的所有元素。 |
:eq() 选择器 |
选择匹配集中索引 n 处的元素。 |
:even 选择器 |
选择零索引的偶数元素。 |
:odd 选择器 |
选择零索引的奇数元素。 |
:first 选择器 |
选择第一个匹配的元素。 |
:last 选择器 |
选择最后一个匹配的元素。 |
:focus 选择器 |
选择当前处于焦点状态的元素。 |
:gt() 选择器 |
选择索引大于匹配集中索引的所有元素。 |
:lt() 选择器 |
选择索引小于匹配集中索引的所有元素。 |
:header 选择器 |
选择所有作为标头的元素,例如h1 ,h2 ,h3 等。 |
:lang() 选择器 |
选择指定语言的所有元素。 |
:not() 选择器 |
选择与给定选择器不匹配的所有元素。 |
:root 选择器 |
选择作为文档根目录的元素。 |
:target 选择器 |
选择由文档 URI 的片段标识符指示的目标元素。 |
不可能在上面给出所有 jQuery 选择器的示例,因此我们将在单独的文章中了解这些选择器。
学习愉快!
jQuery – 所有选择器(“*
”) – 通用选择器
原文: https://howtodoinjava.com/jquery/jquery-all-or-universal-selector/
jQuery ("*")
所有选择器选择文档中的所有元素,包括html
,head
和body
。
Syntax : $("*")
- 所有选择器非常慢,因此请小心使用。
- 如果您的浏览器启用了将
script
或link
元素插入 DOM 的扩展/附加组件,则该元素也将被计入。
请参见 CodePen 上的 Lokesh 的 jQuery –所有选择器(@howtodoinjava)。
如果("*")
选择器与另一个元素一起使用,它将选择指定元素中的所有子元素。
例如,在所有选择器下方,将选择#ul
中的所有元素。
Syntax : $("ul *")
请参见 CodePen 上的 jQuery – Lokesh 元素中的所有选择器(@howtodoinjava)。
学习愉快!
jQuery – 检测剪切,复制或粘贴事件
原文: https://howtodoinjava.com/jquery/jquery-detect-cut-copy-or-paste-events/
如果您从事的项目涉及具有客户端输入验证的动态 UI,那么您可以回想起 QA / 测试人员非常普遍的做法,即他们尝试复制大量垃圾文本以创建无效的方案并将它们记录为错误。 我不会评论这类问题的有效性。 在本文的这篇文章中,我将为您提供一个解决方案,如果遇到此问题,您可以使用该解决方案。
请注意,这将始终有效。 无论您使用键盘还是鼠标生成这些事件。
阅读更多:按键事件和击键事件之间的差异和检测ENTER
键
检测剪切复制或粘贴事件
要检测这些事件,您需要以给定的方式为任何输入区域绑定以下事件。
$(document).ready(function() {
$("#textA").bind({
copy : function(){
$('#message').text('copy behaviour detected!');
},
paste : function(){
$('#message').text('paste behaviour detected!');
},
cut : function(){
$('#message').text('cut behaviour detected!');
}
});
});
尝试一下
<html>
<head>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.10.2.min.js"></script>
<style type="text/css">
span{
color:green;
font-weight:bold;
}
</style>
</head>
<body>
<h1>Detect Cut, Copy and Paste with jQuery</h1>
<form action="#">
<label>Text Box : </label>
<input id="textA" type="text" size="50" value="Copy, paste or cut any text here" />
</form>
<span id="message"></span>
<script type="text/javascript">
$(document).ready(function() {
$("#textA").bind({
copy : function(){
$('#message').text('copy behaviour detected!');
},
paste : function(){
$('#message').text('paste behaviour detected!');
},
cut : function(){
$('#message').text('cut behaviour detected!');
}
});
});
</script>
</body>
</html>
祝您学习愉快!
jQuery 检测ENTER
键按下事件
原文: https://howtodoinjava.com/jquery/jquery-detect-if-enter-key-is-pressed/
学习在 jQuery 中检测ENTER
键示例。 这是常识,如果您必须检测浏览器中按下的键,则必须检入键码(ascii)值。 简单容易。 当您不知道如何正确使用此功能时,就会出现问题。 例如,您是否需要绑定按键或按键? ENTER
键的 ascii 值是多少?
1. jQuery 检测ENTER
键
注意,“ENTER
”键由 ASCII 代码“13” 表示。 检查此 ASCII 图表。
要检查用户是否在网页或任何输入元素上按下ENTER
键,可以将keypress
或keydown
事件绑定到该元素或文档对象本身。 然后在bind()
函数中检查所按下键的键码是否为 13。
如果按键的 ASCII 码为 13,则按“ENTER
”键; 否则,将按下其他键。
阅读更多:
keypress
事件和keydown
事件之间的区别
2. jQuery 检测在文本框中按下的ENTER
键
$('#someTextBox').keypress(function(event){
var keycode = (event.keyCode ? event.keyCode : event.which);
if(keycode == '13'){
alert('You pressed a "enter" key in textbox');
}
});
3. jQuery 检测在文档对象上按下的ENTER
键
$(document).keypress(function(event){
var keycode = (event.keyCode ? event.keyCode : event.which);
if(keycode == '13'){
alert('You pressed a "enter" key in somewhere');
}
});
在 Firefox 中,您必须使用event.which
来获取键码; IE 支持“event.keyCode
”和“event.which
”。
4. jQuery 检测ENTER
键 – 尝试一下
<html>
<head>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.10.2.min.js"></script>
</head>
<body>
<h1>Detect ENTER key with jQuery</h1>
<label>TextBox Area: </label>
<input id="someTextBox" type="text" size="40" />
<script type="text/javascript">
//Bind keypress event to textbox
$('#someTextBox').keypress(function(event){
var keycode = (event.keyCode ? event.keyCode : event.which);
if(keycode == '13'){
alert('You pressed a "enter" key in textbox');
}
//Stop the event from propogation to other handlers
//If this line will be removed, then keypress event handler attached
//at document level will also be triggered
event.stopPropagation();
});
//Bind keypress event to document
$(document).keypress(function(event){
var keycode = (event.keyCode ? event.keyCode : event.which);
if(keycode == '13'){
alert('You pressed a "enter" key in somewhere');
}
});
</script>
</body>
</html>
学习愉快!
Tomcat Maven 插件示例
原文: https://howtodoinjava.com/maven/tomcat-maven-plugin-example/
在本 maven 教程中,学习将 tomcat 插件添加并配置到pom.xml
并使用它部署 Web 应用,而无需在机器上安装任何 tomcat。
当您想在由于某些原因无法安装实际 tomcat 的开发人员的机器上测试应用时,此功能非常有用。
插件的最新版本是“2.2”。 它具有 Apache Tomcat7 支持。
如何添加 tomcat Maven 插件
编辑项目的pom.xml
文件和build
标签内的插件条目。
<build>
<sourceDirectory>src/main/java</sourceDirectory>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<!-- Tomcat plugin-->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>9000</port> //Configure port number
<path>/spring5-webmvc-demo</path> //Configure application root URL
</configuration>
</plugin>
</plugins>
</build>
Tomcat Maven 插件配置
您可以在configuration
标签内以各种方式添加 tomcat 插件。 一些有用的配置选项是:
address
– 此 IP 地址将在所有端口上使用contextFile
– Tomcat 上下文 XML 文件的路径。hostName
– 配置主机名httpsPort
– 用于运行 Tomcat 服务器的 https 端口。keystoreFile
– 覆盖 HTTPS 连接器的默认密钥库文件(如果启用)keystorePass
– 覆盖 HTTPS 连接器的默认keystorePass
(如果启用)mainClass
– 用于启动独立 jar 的主类。systemProperties
– 要传递给 Tomcat 服务器的系统属性的列表。port
– 自定义端口号path
– 用于 Web 应用的 WebApp 上下文路径warFile
– 要部署的 WAR 文件的路径。
使用 tomcat 插件运行应用
要使用 tomcat maven 插件运行该应用,请将 maven 目标用作:
mvn tomcat7:run
当您运行在 Maven 目标之上时,您会看到 tomcat 在控制台日志中使用默认端口8080
启动。
[INFO] >>> tomcat7-maven-plugin:2.2:run (default-cli) > process-classes @ spring5-webmvc-demo >>>
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ spring5-webmvc-demo ---
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.5.1:compile (default-compile) @ spring5-webmvc-demo ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] <<< tomcat7-maven-plugin:2.2:run (default-cli) < process-classes @ spring5-webmvc-demo <<<
[INFO]
[INFO] --- tomcat7-maven-plugin:2.2:run (default-cli) @ spring5-webmvc-demo ---
[INFO] Running war on http://localhost:8080/spring5-webmvc-demo
[INFO] Using existing Tomcat server configuration at D:\java9\workspace\spring5-webmvc-demo\target\tomcat
[INFO] create webapp with contextPath: /spring5-webmvc-demo
---
---
INFO: Starting ProtocolHandler ["http-bio-8080"]
将我的问题放在评论部分。
学习愉快!
参考: Tomcat Maven 插件文档
jQuery – Keypress
和Keydown
事件之间的区别
原文: https://howtodoinjava.com/jquery/jquery-difference-between-keypress-and-keydown-events/
jQuery 支持 3 种类型的键盘事件,而我们是:
keyup()
:释放键盘上的某个键时会触发该事件。keydown()
:按下键盘上的某个键时触发事件。keypress()
:按下键盘上的某个键时会触发该事件。
根据以上定义,看起来keydown()
和keypress()
是同一回事。 但是,实际上它们并不完全相同。
让我们看看它们有何不同。
1)特殊键(例如CTRL
或ALT
或SHIFT
)
如果您按下任何特殊键,浏览器将仅触发keydown()
事件,而不触发keypress()
事件。
尝试一下
尝试按一些常规键,然后按一些特殊键。
<html>
<head>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.10.2.min.js"></script>
<script type="text/javascript">
var keydownCounter = 0;
var keypressCounter = 0;
$( document ).ready(function(){
$('#textbox').keydown(function(event){
$('#keydownCounter').html(++keydownCounter);
});
$('#textbox').keypress(function(event){
$('#keypressCounter').html(++keypressCounter);
});
});
</script>
</head>
<body>
<h4>jQuery keydown() and keypress() difference</h4>
<label>TextBox : </label><input id="textbox" type="text" size="50" />
<div>
<label>keydown() event fired : </label><span id="keydownCounter">0</span> times.
</div>
<div>
<label>keypress() event fired : </label><span id="keypressCounter">0</span> times.
</div>
</body>
</html>
2)捕获键码或键 ascii 值
如果您打字母a
和A
(大写字母),则会发现以下事件行为。
keydown()
将显示a = 65
,A = 65
(不区分大小写)。keypresss()
将显示a = 97
,A = 65
(区分大小写)。
如果要捕获实际字符键入,请始终使用keypress()
事件。
试一试
尝试在[Caps Lock]
打开的情况下按一些键,然后再将其关闭。
<html>
<head>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.10.2.min.js"></script>
<script type="text/javascript">
$( document ).ready(function(){
$('#textbox').keydown(function(event){
var keycode = (event.keyCode ? event.keyCode : event.which);
$('#keydownCode').html(keycode);
});
$('#textbox').keypress(function(event){
var keycode = (event.keyCode ? event.keyCode : event.which);
$('#keypressCode').html(keycode);
});
});
</script>
</head>
<body>
<h4>jQuery keydown() and keypress() difference</h4>
<label>TextBox : </label><input id="textbox" type="text" size="50" />
<div>
<label>keydown() detected keycode : </label><span id="keydownCode">0</span>.
</div>
<div>
<label>keypress() detected keycode : </label><span id="keypressCode">0</span>.
</div>
</body>
</html>
event.keyCode
在 FireFox 中无法运行,但在 IE 中可以完美运行。 要在 Firefox 中获取event.keyCode
,您应该改用event.which
,jQuery 也会推荐它。 所以更好的方法应该是:
var keycode =(event.keyCode? event.keyCode: event.which);
3)长按任意键
在这种情况下,根据文档,keydown()
事件被触发一次,但是keypress()
事件将一直触发直到释放键为止。 无论如何,这是浏览器特定的行为,我不会建议您使用此功能。 这应该仅限于您的知识。
试一试
按任意键并保持按下状态。
<html>
<head>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.10.2.min.js"></script>
<script type="text/javascript">
var keydownCounter = 0;
var keypressCounter = 0;
$( document ).ready(function(){
$('#textbox').keydown(function(event){
$('#keydownCounter').html(++keydownCounter);
});
$('#textbox').keypress(function(event){
$('#keypressCounter').html(++keypressCounter);
});
});
</script>
</head>
<body>
<h4>jQuery keydown() and keypress() difference</h4>
<label>TextBox : </label><input id="textbox" type="text" size="50" />
<div>
<label>keydown() event fired : </label><span id="keydownCounter">0</span> times.
</div>
<div>
<label>keypress() event fired : </label><span id="keypressCounter">0</span> times.
</div>
</body>
</html>
如果以上演示在您的浏览器中不起作用,请不要担心。 你不是一个人。 请参阅 http://unixpapa.com/js/key.html 中的 2.2 节(自动重复触发的事件)。
如果您有任何疑问或建议,请告诉我。
学习愉快!
关于 StackOverflow 的最佳 jQuery 讨论
原文: https://howtodoinjava.com/for-fun-only/best-jquery-discussion-ever-on-stackoverflow/
阅读这篇文章后,我真的笑了起来。
JavaScript – 相等(==
)与身份(===
)运算符
原文: https://howtodoinjava.com/javascript/javascript-equality-vs-identity-operators/
如果您使用过 JavaScript,那么您一定已经注意到这两个运算符可以比较值。 许多开发人员不了解他们在特定情况下使用的正确版本。 正确的决定是基于对它们实际工作原理的了解。 让我们来了解一下。
请记住,在执行比较时,相等运算符(==
)将在继续进行操作之前尝试使数据类型相同。 另一方面,身份运算符(===
)要求两种数据类型相同,作为前提条件。
让我们看一个例子。 参见下面的代码:
var valueOne = 3;
var valueTwo = "3";
if (valueOne == valueTwo) {
console.log("ValueOne and ValueTwo are the same");
} else {
console.log("ValueOne and ValueTwo are NOT the same");
}
你能猜出输出吗? 您可能会感到惊讶,也可能不会感到惊讶,但是这些值被认为是相同的。
Output is: ValueOne and ValueTwo are the same
==
运算符之所以认为"3"
和3
相同,是因为在进行比较之前,它实际上将操作数(==
运算符两侧的值)覆盖为相同的类型。
但是,如果我们将运算符更改为身份运算符,如此处所示,我们将看到截然不同的输出:
var valueOne = 3;
var valueTwo = "3";
if (valueOne === valueTwo) {
console.log("ValueOne and ValueTwo are the same");
} else {
console.log("ValueOne and ValueTwo are NOT the same");
}
现在有了身份运算符,输出为:
Output is: ValueOne and ValueTwo are NOT the same
由于我们在这种情况下使用了===
运算符,并且由于该运算符未执行任何类型转换,因此我们看到字符串值"3"
和数字3
毕竟是不同的。
如有疑问,相对安全的选择只是出于习惯而使用身份运算符(===
)。 当然,最安全的选择是使自己熟悉这些差异,以便您了解引擎盖下实际发生的事情。
学习愉快!
您必须知道的 JavaScript 变量范围规则
原文: https://howtodoinjava.com/javascript/javascript-variable-scope-rules/
每种编程语言都有变量,它们只能在特定范围内访问。 使用 JavaScript 时,作用域并不是一个棘手的功能。 它们看起来很简单,但是如果您不知道 javascript 变量的范围是多少,可能会出错。 让我们更详细地了解 javascript 的作用域。
在 JavaScript 中,变量仅通过两种方式限定范围:
- 全局范围
- 函数范围
只需记住,当您使用var
关键字定义变量时,就声明了 JavaScript 中的作用域。 让我们从简单的一个开始 - Java 中的全局范围变量。
全局范围
在以下情况下,用 javascript 声明的变量将具有全局作用域:
a)在任何函数之外定义的变量
//'foo' is global variable
var foo = 'I am GLOBAL foo';
//'foo' is function local variable
function test() {
var foo = 'I am LOCAL foo';
}
console.log(foo); //I am GLOBAL foo
在任何函数中定义的变量 - 不使用“var
”关键字
//'foo' is function local variable
function test() {
foo = 'I am GLOBAL foo';
}
console.log(foo); //I am GLOBAL foo
console.log(window.foo); //I am GLOBAL foo
所有全局作用域变量均作为window
对象的属性可见。
函数范围
从全局作用域详细信息可以明显看出,函数作用域变量是在函数内部使用“var
”关键字声明的。
//'foo' is function local variable
function test() {
var foo = 'I am LOCAL foo';
console.log(foo); //I am LOCAL foo
}
console.log(foo); //foo is not defined
请注意, javascript 函数具有自己的作用域,但是块(例如while
,if
和for
语句)则没有。
混合全局变量和函数范围变量
现在,在基本理解之后,我们来看一个示例,在该示例中,我们将在单个页面的不同范围中使用变量foo
,然后我们将在其上明确说明 javascript 范围的工作原理。
//Define global scoped 'foo'
var foo = 'I am GLOBAL foo';
//Inside if block foo will refer to global foo
if ( true ) {
var foo = 'I am GLOBAL foo TOO';
console.log( foo ); //I am GLOBAL foo TOO
}
//As blocks do not have their own scope
//So foo in if block referred to global scope foo
//foo refer to new value
console.log( foo ); //I am GLOBAL foo TOO
//Inside function - foo has it's own declaration
function test() {
var foo = 'I am LOCAL foo';
console.log( foo ); //I am LOCAL foo
}
test();
//Ouside function foo is still globally declared foo
console.log( foo ); //I am GLOBAL foo TOO
了解仅在函数内部声明的foo
如何具有自己的作用域,否则所有foo
变量都引用全局作用域foo
。
提示:无论范围如何,都应始终使用var
初始化变量。 这样,您的变量将具有您期望的范围,并且可以避免意外的全局变量。
我希望当我们谈论 javascript 变量的范围时能够使我更加清楚。 将我的问题放在评论部分。
学习愉快!
JavaScript:定义全局变量的正确方法
原文: https://howtodoinjava.com/javascript/javascript-correct-way-to-define-global-variables/
我们知道什么是全局变量或常量,它们是可在应用范围内访问的字段。 在 Java 中,通常是通过定义“public static
”字段来完成的。 在这里,通过添加final
关键字,我们可以将全局变量更改为全局常量。 很容易,对吧?
但是 javascript 呢? 这些脚本语言没有这些访问修饰符等,那么我们有什么选择呢?
我在正常工作时间遇到了类似的情况,并且知道以传统的 javascript 方式定义全局变量会破坏生产代码。 传统方式是:
var iAmGlobal = "some val"; //Global variable declaration
//Any place in other part of code
function doSomething()
{
//iAmGlobal = "changed value";
alert(iAmGlobal); //I am accessible here too !!
}
这些类型实际上是在很长一段时间内声明的,对于一些不幸的伙伴来说,它会导致 Java 脚本中出现著名的“对象不支持此属性或方法”错误。 如果您还记得一个 IE 错误,它带有“var a = foo
”,则仅声明了文件范围的全局变量。 这是 IE 臭名昭著的解释器的问题。
那么,在 JavaScript 中声明全局变量的正确方法是什么?
在 JavaScript 中声明全局变量的正确方法
正确的方法是使用窗口对象。 并使用如下语法:
var window.iAmGlobal = "some val"; //Global variable declaration with window.
//Any place in other part of code
function doSomething()
{
alert(window.iAmGlobal); //I am accessible here too !!
//OR
alert(iAmGlobal); //I am accessible here too !!
}
通过以这种方式定义全局变量,您将使 JavaScript 更加健壮和可靠。
要点:
- 使用
window
关键字以“window.VAR_NAME
”的形式定义全局变量 - 您可以使用“
window.VAR_NAME
”或直接“VAR_NAME
”访问变量 - 不要使用同名的其他变量,否则可能会导致不良结果。
所有人都在这个话题上。
进一步阅读: http://stackoverflow.com/questions/4862193/javascript-global-variables
学习愉快!
在 JavaScript 中实现 MVC 和 PubSub
原文: https://howtodoinjava.com/javascript/implement-mvc-and-pubsub-in-javascript/
我们知道什么是 MVC? MVC 代表模型 - 视图 - 控制器。 简而言之,MVC 是一种设计技术,其中将应用组件分为 3 组,以便可以独立开发它们而无需考虑它们将如何交互。 如果构建正确,则很少有配置代码可以绑定它们,并且可以立即使用。
PubSub(发布者订阅者) 模型是设计范式,其中多个订阅者正在监听源上的更改事件,并且一旦发生任何更改,便会立即通知监听器。 在用户交互影响屏幕上多个部分的大型系统中,此模式消除了许多硬编码,并提供了设计灵活性。
JavaScript 中的 PubSub + MVC
在本教程中,我们将学习以下概念:
Building Model-View-Controller components
Building Publisher Subscriber infrastructure
Understanding Event Notification mechanism
Demo application
让我们从构建 MVC 组件开始。
构建模型视图控制器组件
在 JavaScript 中,如果必须开发 MVC 结构,则至少需要编写 3 个对象。 我只花 3 个使例子更加关注概念。
例如,我以媒体播放器为例。 此媒体播放器附有一个播放列表,用户可以使用按键事件在此播放列表上向前和向后移动。
模型:存储当前视图状态
playlist
– 数组对象将所有曲目存储在当前可用的播放列表中。
currentIndex
– 当前播放的曲目
模型还包含帮助用户在用户交互后保持其当前状态更改的函数。
var Model = {
playlist: new Array(),
currentIndex : 0,
reLoad: function() {
currentIndex = 0;
var tracks = document.getElementById("playListSelector").options;
for(var i=0; i<tracks.length; i++)
{
this.playlist[i] = tracks[i].value;
}
},
next: function () {
if(this.currentIndex < (this.playlist.length-1))
this.currentIndex++;
publish(this);
},
prev: function () {
if(this.currentIndex > 0)
this.currentIndex--;
publish(this);
},
current: function () {
publish(this);
}
};
视图:表示用户与之交互的屏幕
该对象只有一种方法可以在屏幕上呈现用户事件的结果。
var View = {
notify: function(model) {
document.getElementById("playListSelector").selectedIndex = model.currentIndex;
}
};
控制器:视图调用控制器以更改模型
控制器具有在用户交互期间将被调用的函数。
var Controller = {
model: Model,
moveNext: function () {
this.model.next();
return this;
},
movePrev: function () {
this.model.prev();
return this;
},
getCurrent: function () {
this.model.current();
return this;
}
};
构建发布者订阅服务器基础结构
到目前为止,一切都很好。 现在,我们将添加一些 pub-sub 逻辑,以便无论何时触发任何用户事件,都会通知所有已注册的视图,并且它们可以进行所需的视觉更改。
//All subscribers for a event
var subscribers = [];
function publish(event) {
for (i in subscribers) {
subscribers[i].notify(event);
}
};
上面的代码声明了一个数组,该数组可用于存储所有感兴趣的视图以将其自身注册为事件监听器。 每当任何事件作为用户交互触发时,都会通知他们该事件。
要将视图注册为事件监听器,将使用以下代码:
//Subscribe for updates
subscribers.push(View);
了解事件通知机制
事件处理按以下顺序执行:
视图触发事件 -> 控制器触发模型更新 -> 模型将通知发送到 pubsub -> pubsub 通知所有有关事件的视图,以便它们可以更新用户屏幕
在上面的代码段中,假设用户按下了播放列表中的下一首曲目。 这是控制流:
- 用户按下“下一首”按钮
- 控制器的
moveNext()
方法调用 moveNext()
触发模型的next()
方法next()
方法增加当前正在播放曲目的currentIndex
next()
方法使用publish()
方法发布事件publish()
方法调用notify()
方法是所有注册的订户- 视图
notify()
方法根据模型的当前状态更新用户屏幕
这样,所有可能的事件都将从控制器处理到视图层。 最后,我们一直都处于模型的当前状态。
演示应用
我已经在一个文件中使用了上述所有代码段,并使用 HTML select
元素进行了虚拟播放列表行为。 select
的当前选定选项代表媒体播放器中当前播放的曲目。
让我们看一下完整的演示代码:
<html>
<head>
<meta charset="utf-8">
<script language="javascript">
// PubSub
var subscribers = [];
function publish(event) {
for (i in subscribers) {
subscribers[i].notify(event);
}
};
// MVC
var Model = {
playlist: new Array(),
currentIndex : 0,
reLoad: function() {
currentIndex = 0;
var tracks = document.getElementById("playListSelector").options;
for(var i=0; i<tracks.length; i++)
{
this.playlist[i] = tracks[i].value;
}
},
next: function () {
if(this.currentIndex < (this.playlist.length-1))
this.currentIndex++;
publish(this);
},
prev: function () {
if(this.currentIndex > 0)
this.currentIndex--;
publish(this);
},
current: function () {
publish(this);
}
};
var View = {
notify: function(model) {
document.getElementById("output").innerHTML = JSON.stringify(model);
document.getElementById("playListSelector").selectedIndex = model.currentIndex;
}
};
var Controller = {
model: Model,
moveNext: function () {
this.model.next();
return this;
},
movePrev: function () {
this.model.prev();
return this;
},
getCurrent: function () {
this.model.current();
return this;
}
};
subscribers.push(View); // Subscribe for updates
function initializeModel()
{
Model.reLoad();
}
</script>
</head>
<body onload="initializeModel()">
<input type="button" onclick="Controller.getCurrent();" value="Current Track">
<input type="button" onclick="Controller.moveNext();" value="Next Track">
<input type="button" onclick="Controller.movePrev();" value="Previous Track">
<select id="playListSelector" multiple readonly>
<option value="0">Track 1</option>
<option value="1">Track 2</option>
<option value="2">Track 3</option>
<option value="3">Track 4</option>
</select>
<span id="output" />
</body>
</html>
上面的代码还有另外一个方法initializeModel()
,该方法用于在页面加载时使用播放列表项初始化模型对象。 现在,当我们按“下一个曲目”时,选择元素中的下一个选项被选中。 同样,按下“上一曲目”按钮,则在选择列表中选择了上一个选项。
您将看到如下运行代码:
JavaScript 中的 MVC + PubSub 的示例界面
如果不清楚或您有任何建议/查询,请发表评论。
————————————————————————————————————
更新:
经过简短的邮件讨论后,Brook Monroe 向我发送了类似示例的更好的代码示例。 尽管本教程的目的不是更好的代码实践,而是详细介绍了概念。 我在下面共享更新的代码以供参考。 它可能会帮助您。
<html>
<head>
<meta charset="utf-8">
<script src="./pubsub.js"></script>
</head>
<body>
<button id="btnCurrent">Current Track</button>
<button id="btnNext">Next Track</button>
<button id="btnPrev">Previous Track</button>
<select id="playListSelector" multiple readonly>
<option value="0" selected>Track 1</option>
<option value="1">Track 2</option>
<option value="2">Track 3</option>
<option value="3">Track 4</option>
</select>
<span id="output"></span>
</body>
</html>
//pubsub.js
// PubSub
( function () {
"use strict";
var subscribers = [],
elCache = {},
Model = {
playlist : [],
currentIndex : 0,
reLoad : function()
{
var tracks = Array.prototype.slice.call(elCache.get("playListSelector").options);
this.playlist = [];
tracks.forEach( function (e,i) { this.playlist.push(tracks[i].value); }, Model);
this.currentIndex = 0;
},
next : function ()
{
if (this.currentIndex < (this.playlist.length-1)) {
this.currentIndex++;
}
subscribers.publish(this);
},
prev : function ()
{
if (this.currentIndex > 0) {
this.currentIndex--;
}
subscribers.publish(this);
},
current : function ()
{
subscribers.publish(this);
}
},
// MVC
View = {
notify : function(model)
{
elCache.get("output").innerHTML = JSON.stringify(model);
elCache.get("playListSelector").selectedIndex = model.currentIndex;
}
},
Controller = {
moveNext: function ()
{
Model.next();
return this;
},
movePrev: function ()
{
Model.prev();
return this;
},
getCurrent: function ()
{
Model.current();
return this;
}
};
function start()
{
elCache.get = function (elId)
{
return this[elId] || ( this[elId] = document.getElementById(elId) );
};
subscribers.publish = function (event)
{
this.forEach( function (e) { e.notify(event); } );
};
subscribers.push(View); // Subscribe for updates
elCache.get("btnCurrent").addEventListener("click", Controller.getCurrent.bind(Model));
elCache.get("btnNext").addEventListener("click", Controller.moveNext.bind(Model));
elCache.get("btnPrev").addEventListener("click", Controller.movePrev.bind(Model));
Model.reLoad.bind(Model)();
}
window.addEventListener("load",start,false);
} )();
祝您学习愉快!
JavaScript DOM 对象与 jQuery 对象
原文: https://howtodoinjava.com/jquery/javascript-dom-objects-vs-jquery-objects/
用一句话,DOM 对象是 Web 浏览器用来在网页上呈现元素的对象,而 jQuery 对象基本上是围绕一组 DOM 元素的包对象。 如果您想详细了解这些对象以及如何使用它们,并相互转换,然后继续阅读本教程。
Table of Contents
What are JavaScript DOM objects?
What are jQuery objects?
How to determine Whether an Object is DOM or jQuery?
Convert an Object from DOM to jQuery and back
什么是 JavaScript DOM 对象?
如前所述,浏览器直接使用 DOM 对象在浏览器窗口中呈现网页。 浏览器从 Web 服务器接收 HTML 文档,它只是文本。 浏览器继续将此文本解析为一个内部结构,该结构实际上可以用来可视化呈现页面。 DOM 表示浏览器具有 HTML 文档的内部结构。 DOM 对象表示页面上的视觉或功能对象,该对象是从原始 HTML 文档创建的。
即使浏览器完全渲染了网页,您也可以使用 JavaScript 更改 DOM 对象, 属性和值。 以这种方式进行的任何更改都会自动刷新浏览器窗口中显示的视觉表示。
使用 DOM 对象的优势在于,您可以直接访问处理 HTML 元素所需的所有内容。 DOM 对象的缺点是,大多数附加的函数和属性都是浏览器需要的,在使用 JavaScript 时不一定有用。 至少对于经验不足的开发人员而言,这会使与他们的合作变慢一些。
例如,要更改段落或标签的内容,可以使用如下所示的 javascript:
document.getElementById("label_firstname").innerHTML = "First Name";
什么是 jQuery 对象?
jQuery 对象是围绕单个或多个 DOM 元素的包对象。 jQuery 对象(尽管从技术上讲还是 JavaScript 对象)提供对包的 DOM 元素的访问 - 但是,这是一种非常不同,更容易且通常更有效的方式。
请记住,jQuery 对象可以表示单个 DOM 对象,也可以表示许多 DOM 对象的集合。 因此,如果将操作应用于 jQuery 对象,则该操作可能适用于许多 DOM 对象。
使用 jquery 对象有其自身的优势。 例如,jQuery 提供了许多有用的库方法来搜索它所表示的 DOM 元素内的元素,并对所搜索的元素执行批量操作,而无需在代码中对其进行迭代。
例如,要更改段落或标签的内容,可以使用 jQuery,如下所示:
("#label_firstname").html("First Name");
如何确定对象是 DOM 还是 jQuery?
很多时候,在任何复杂的应用上工作时,您都可以在单个代码中找到 jQuery 对象和 javascript DOM 对象。 现在您不确定是 jQuery 对象,DOM 对象还是其他 JavaScript 对象。 有一个简单的方法可以分辨出差异。
要确认对象是否为 jQuery 对象,请查看该对象是否具有jquery
属性:
if( obj.jquery ) {
//other operation
}
类似地,要确认对象是否为 DOM 对象,请查看该对象是否具有nodeType
属性:
if( obj.nodeType ) {
//other operation
}
将对象从 DOM 转换为 jQuery 并返回
在上述情况下,如果要将对象从 DOM 转换为 jQuery 或将 jQuery 转换为 DOM,可以使用以下技术来实现。
将 DOM 对象转换为 jQuery 对象
$()
或jquery()
方法从 DOM 对象创建一个新的 jQuery 对象。
var jqueryObj = $(domObj);
将 jQuery 对象转换为 DOM 对象
.get()
方法返回包在 jQuery 对象中的 DOM 对象。
var domObj = jqueryObj.get();
这就是有关 javascript DOM 对象和 jQuery 对象的全部内容。 如果有任何问题或建议,请留下我评论。
学习愉快!
Jasmine 单元测试教程及示例
原文: https://howtodoinjava.com/javascript/jasmine-unit-testing-tutorial/
Jasmine 是流行的 JavaScript 单元测试框架之一,能够测试同步和异步 JavaScript 代码。 它用于 BDD(行为驱动的开发)编程中,该编程更多地侧重于业务价值而不是技术细节。 在此 Jasmine 教程中,我们将从设置说明到了解测试用例的输出来详细学习 Jasmine 框架。
Table of Contents
1\. Jasmine Setup Configuration
2\. Writing Suite and Specs
3\. Setup and Teardown
4\. Jasmine Describe Blocks
5\. Jasmine Matchers
6\. Disable Suites and Specs
7\. Working with Jasmine Spy
8\. Final Thoughts
1. Jasmine 起步配置
首先下载 Jasmine 框架并将其解压缩到您的项目文件夹中。 我建议在应用中可能已经存在的/js
或/javascript
文件夹下创建一个单独的文件夹/jasmine
。
您将在分发捆绑包中获得以下四个文件夹/文件:
/src
:包含您要测试的 JavaScript 源文件/lib
:包含框架文件/spec
:包含 JavaScript 测试文件SpecRunner.html
:是测试用例运行器 HTML 文件
您可以删除/src
文件夹; 并从SpecRunner.html
文件中的当前位置引用源文件。 默认文件如下所示,您将需要更改/src
和/spec
文件夹中包含的文件。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Jasmine Spec Runner v2.4.1</title>
<link rel="shortcut icon" type="image/png" href="lib/jasmine-2.4.1/jasmine_favicon.png">
<link rel="stylesheet" href="lib/jasmine-2.4.1/jasmine.css">
<script src="lib/jasmine-2.4.1/jasmine.js"></script>
<script src="lib/jasmine-2.4.1/jasmine-html.js"></script>
<script src="lib/jasmine-2.4.1/boot.js"></script>
<!-- include source files here... -->
<script src="src/Player.js"></script>
<script src="src/Song.js"></script>
<!-- include spec files here... -->
<script src="spec/SpecHelper.js"></script>
<script src="spec/PlayerSpec.js"></script>
</head>
<body></body>
</html>
在此演示中,我删除了/src
文件夹,并将引用其当前位置的文件。 当前的文件夹结构如下:
Jasmine 文件夹结构
为了专注于 Jasmine 的功能,我将创建一个具有一些基本操作的简单 JS 文件MathUtils.js
,我们将对这些功能进行单元测试。
MathUtils = function() {};
MathUtils.prototype.sum = function(number1, number2) {
return number1 + number2;
}
MathUtils.prototype.substract = function(number1, number2) {
return number1 - number2;
}
MathUtils.prototype.multiply = function(number1, number2) {
return number1 * number2;
}
MathUtils.prototype.divide = function(number1, number2) {
return number1 / number2;
}
MathUtils.prototype.average = function(number1, number2) {
return (number1 + number2) / 2;
}
MathUtils.prototype.factorial = function(number) {
if (number < 0) {
throw new Error("There is no factorial for negative numbers");
} else if (number == 1 || number == 0) {
return 1;
} else {
return number * this.factorial(number - 1);
}
}
在SpecRunner.html
中添加文件引用后,文件内容将为:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Jasmine Spec Runner v2.4.1</title>
<link rel="shortcut icon" type="image/png"
href="lib/jasmine-2.4.1/jasmine_favicon.png">
<link rel="stylesheet" href="lib/jasmine-2.4.1/jasmine.css">
<script src="lib/jasmine-2.4.1/jasmine.js"></script>
<script src="lib/jasmine-2.4.1/jasmine-html.js"></script>
<script src="lib/jasmine-2.4.1/boot.js"></script>
<!-- include source files here... -->
<script src="../MathUtils.js"></script>
<!-- include spec files here... -->
<script src="spec/MathUtils.js"></script>
</head>
<body></body>
</html>
2. Jasmine 套件和规范
在 Jasmine 中,有两个重要术语 – 套件和规范。
2.1 套件
Jasmine 套件是一组测试用例,可用于测试 JavaScript 代码(JavaScript 对象或函数)的特定行为。 首先使用两个参数调用 Jasmine 全局函数describe
- 第一个参数代表测试套件的标题,第二个参数代表实现测试套件的功能。
//This is test suite
describe("Test Suite", function() {
//.....
});
2.2 规范
Jasmine 规范表示测试套件中的一个测试用例。 首先使用两个参数调用 Jasmine 全局函数it
- 第一个参数代表规范的标题,第二个参数代表实现测试用例的函数。
实际上,规范包含一个或多个期望。 每个期望代表一个可以为true
或false
的断言。 为了通过规范,规范内的所有期望都必须为true
。 如果规范中的一个或多个期望是false
,则规范失败。
//This is test suite
describe("Test Suite", function() {
it("test spec", function() {
expect( expression ).toEqual(true);
});
});
让我们开始为MathUtils.js
编写单元测试,以更好地了解套件和规范。 我们将在spec/MathUtils.js
中编写这些规范。
describe("MathUtils", function() {
var calc;
//This will be called before running each spec
beforeEach(function() {
calc = new MathUtils();
});
describe("when calc is used to peform basic math operations", function(){
//Spec for sum operation
it("should be able to calculate sum of 3 and 5", function() {
expect(calc.sum(3,5)).toEqual(8);
});
//Spec for multiply operation
it("should be able to multiply 10 and 40", function() {
expect(calc.multiply(10, 40)).toEqual(400);
});
//Spec for factorial operation for positive number
it("should be able to calculate factorial of 9", function() {
expect(calc.factorial(9)).toEqual(362880);
});
//Spec for factorial operation for negative number
it("should be able to throw error in factorial operation when the number is negative", function() {
expect(function() {
calc.factorial(-7)
}).toThrowError(Error);
});
});
});
在浏览器中打开SpecRunner.html
文件时,将运行规范并在浏览器中呈现结果,如下所示:
Jasmine 输出
3. 安装和拆卸
为了进行安装和拆卸,Jasmine 在套件级别提供了两个全局函数,即beforeEach()
和afterEach()
。
3.1 beforeEach()
在调用describe()
的每个规范之前,会先调用beforeEach
函数。
3.2 afterEach()
每个规范后都会调用一次afterEach
函数。
实际上,规范变量(任何变量)在顶级范围describe
块中定义,并且初始化代码被移到beforeEach
函数中。 afterEach
函数在继续之前重置变量。 这有助于开发人员不要为每个规范重复设置和完成代码。
4. Jasmine 描述块
在 Jasmine 中,describe
函数用于对相关规范进行分组。 字符串参数用于命名规范集合,并将其与规范连接在一起以形成规范的全名。 这有助于在大型套件中查找规范。
好消息是,您也可以嵌套describe
块。 在嵌套describe
的情况下,Jasmine 在执行规范之前先按顺序执行每个beforeEach
函数,然后执行规范,最后逐步执行每个afterEach
函数。
让我们通过一个例子来理解它。 将MathUtilSpecs.js
中的内容替换为以下代码:
describe("Nested Describe Demo", function() {
beforeEach(function() {
console.log("beforeEach level 1");
});
describe("MyTest level2", function() {
beforeEach(function() {
console.log("beforeEach level 2");
});
describe("MyTest level3", function() {
beforeEach(function() {
console.log("beforeEach level 3");
});
it("is a simple spec in level3", function() {
console.log("A simple spec in level 3");
expect(true).toBe(true);
});
afterEach(function() {
console.log("afterEach level 3");
});
});
afterEach(function() {
console.log("afterEach level 2");
});
});
afterEach(function() {
console.log("afterEach level 1");
});
});
现在,通过在浏览器中打开SpecRunner.html
执行此文件。 观察控制台输出,它写为:
beforeEach level 1
beforeEach level 2
beforeEach level 3
A simple spec in level 3
afterEach level 3
afterEach level 2
afterEach level 1
我建议您在上面的代码中放置更多规范,并查看执行流程以更好地理解。
5. Jasmine 匹配器
在第一个示例中,我们看到了toEqual
和toThrow
函数的用法。 它们是匹配器,用于比较任何 Jasmine 测试的实际输出和预期输出。 您就像 Java 断言一样,如果有帮助的话。
让我们列出所有这些 Jasmine 匹配器,它们可以帮助您制定更强大,更有意义的测试规范。
匹配器 | 目的 |
---|---|
toBe() |
如果实际值与期望值的类型和值相同,则通过。 它与=== 运算符进行比较 |
toEqual() |
适用于简单的文字和变量;也应适用于对象 |
toMatch() |
检查值是否匹配字符串或正则表达式 |
toBeDefined() |
确保定义了属性或值 |
toBeUndefined() |
确保未定义属性或值 |
toBeNull() |
确保属性或值为空。 |
toBeTruthy() |
确保属性或值是true |
toBeFalsy() |
确保属性或值是false |
toContain() |
检查字符串或数组是否包含子字符串或项目。 |
toBeLessThan() |
用于小于的数学比较 |
toBeGreaterThan() |
用于大于的数学比较 |
toBeCloseTo() |
用于精确数学比较 |
toThrow() |
用于测试函数是否引发异常 |
toThrowError() |
用于测试引发的特定异常 |
Jasmine not
关键字可以与每个匹配器的条件一起使用,以求将结果取反。 例如:
expect(actual).not.toBe(expected);
expect(actual).not.toBeDefined(expected);
6. 禁用套件和规范
很多时候,出于各种原因,您可能需要禁用套件 - 一段时间。 在这种情况下,您无需删除代码 - 只需在describe
的开头添加char x
以使if
为xdescribe
。
这些套件及其内部的任何规范在运行时都会被跳过,因此其结果将不会出现在结果中。
xdescribe("MathUtils", function() {
//code
});
如果您不想禁用整个套件,而是只想禁用某个规范测试,然后将x
放在该规范本身之前,这一次将仅跳过该规范。
describe("MathUtils", function() {
//Spec for sum operation
xit("should be able to calculate the sum of two numbers", function() {
expect(10).toBeSumOf(7, 3);
});
});
7. 与 Jasmine 间谍一起工作
Jasmine 具有测试替身功能,称为间谍。间谍可以对任何函数进行存根,并跟踪对该函数和所有参数的调用。 间谍仅存在于定义它的describe
或it
块中,并且在每个规范后都会被删除。 要使用任何方法创建间谍,请使用spyOn(object, 'methodName')
调用。
有两个匹配器toHaveBeenCalled
和toHaveBeenCalledWith
应该与间谍一起使用。 如果调用了间谍,则toHaveBeenCalled
匹配器将返回true
;否则,将返回true
。 如果参数列表与对间谍的任何记录调用匹配,则toHaveBeenCalledWith
匹配器将返回true
。
describe("MathUtils", function() {
var calc;
beforeEach(function() {
calc = new MathUtils();
spyOn(calc, 'sum');
});
describe("when calc is used to peform basic math operations", function(){
//Test for sum operation
it("should be able to calculate sum of 3 and 5", function() {
//call any method
calc.sum(3,5);
//verify it got executed
expect(calc.sum).toHaveBeenCalled();
expect(calc.sum).toHaveBeenCalledWith(3,5);
});
});
});
上面的示例本质上是最基本的,您也可以使用间谍来验证对内部方法的调用。 例如。 如果在任何对象上调用方法calculateInterest()
,则可能需要检查是否必须在该对象内调用getPrincipal()
,getROI()
和getTime()
。 间谍将帮助您验证这些假设。
当没有要监视的函数时,jasmine.createSpy
可以创建裸露的间谍。 该间谍的行为与其他任何间谍一样 - 跟踪调用,参数等。但是它背后没有实现。 间谍是 JavaScript 对象,可以这样使用。 通常,这些间谍在需要时用作其他函数的回调函数。
var callback = jasmine.createSpy('callback');
//Use it for testing
expect(object.callback).toHaveBeenCalled();
如果需要定义多个此类方法,则可以使用快捷方式jasmine.createSpyObj
。 例如:
tape = jasmine.createSpyObj('tape', ['play', 'pause', 'stop', 'rewind']);
tape.play();
//Use it for testing
expect(tape.play.calls.any()).toEqual(true);
在calls
属性上跟踪并公开了对间谍的每次调用。 让我们看看如何使用这些属性来跟踪间谍。
追踪属性 | 目的 |
---|---|
.calls.any() |
如果根本没有调用过该间谍,则返回false ;如果至少发生一次调用,则返回true 。 |
.calls.count() |
返回间谍被调用的次数 |
.calls.argsFor(index) |
返回传递给电话号码索引的参数 |
.calls.allArgs() |
返回所有调用的参数 |
.calls.all() |
返回上下文(this ),并且参数传递了所有调用 |
.calls.mostRecent() |
返回上下文(this )和最近一次调用的参数 |
.calls.first() |
返回第一次调用的上下文(this )和参数 |
.calls.reset() |
清除所有跟踪以发现间谍 |
8. Jasmine 教程 – 最终想法
Jasmine 是用于测试 javascript 函数的非常强大的框架,但是学习曲线有点困难。 在使用 Jasmine 进行有效测试之前,需要编写大量实际的 javascript 代码。
请记住,Jasmine 旨在用于以 BDD(行为驱动的开发)样式编写测试。 不要通过测试无关的东西来滥用它。
让我知道您对本初学者 Jasmine 教程的想法。
学习愉快!
参考文献:
Jasmine Github 仓库
JavaScript 日志 – 在 JSON 中屏蔽敏感信息
原文: https://howtodoinjava.com/javascript/mask-sensitive-info-json-logs/
屏蔽敏感信息是处理敏感客户数据的应用的非常实际的需求。 例如,银行应用。 当这些应用在组织内部网中运行时,会多次生成 UI 日志并将其存储在用户的计算机上以进行调试。
在上述情况下,监管要求屏蔽所有此类敏感信息,以使它们不存储在用户的计算机中。 在本教程中,我们将学习对此类敏感信息应用屏蔽。
我假设数据通知 JSON 对象,而敏感信息是以其属性的形式出现的。
结合使用JSON.stringify()
和replacer
函数
解决方案在于使用函数JSON.stringify()
。 此函数将 JavaScript / JSON 对象转换为 JavaScript 对象表示法(JSON)字符串表示形式。 此 JSON 字符串用于记录目的。
JSON.stringify( value [, replacer] [, space] )
这里value
是要进行字符串化的对象,replacer
是可以转换结果的函数。 在遍历完整对象时,将为 JSON 对象的每个属性调用此replacer
函数。 在replacer
函数中,您可以在键值对中获得属性(即,属性名称和属性值)。
使用此替换器函数可屏蔽敏感信息。
例如,在下面的示例中,我屏蔽了accountNumber
字段。 您可以根据项目需要自定义函数maskInfo
。
var unmaskedData = { "name":"Lokesh", "accountNumber":"3044444444", "city":"New York"};
var maskedData = JSON.stringify( unmaskedData, maskInfo );
function maskInfo (key, value) {
var maskedValue = value;
if (key == "accountNumber")
{
if(value && value.length > 5) {
maskedValue = "*" + maskedValue.substring(value.length - 4, value.length);
} else {
maskedValue = "****";
}
}
return maskedValue;
}
Output:
{
"name": "Lokesh",
"accountNumber": "*4444", //Masked account number
"city": "New York"
}
数据屏蔽实时演示
https://howtodoinjava.com/wp-content/downloads/maskingDemo.html
源代码
<!DOCTYPE html>
<html>
<body>
<h2>JavaScript - Mask Sensitive Information Demo</h2>
<p>Input JSON payload in left box and press submit button. Masked data will appear on right box.</p>
<table>
<tr>
<th>Unmasked Data</th>
<th>Masked Data</th>
</tr>
<tr>
<td><textarea id="unmasked" rows="10" cols="30">{ "name":"Lokesh", "accountNumber":"3044444444", "city":"New York"}</textarea></td>
<td><textarea id="masked" rows="10" cols="30"></textarea></td>
</tr>
<tr><td colspan="2"><input type="button" name="submit" value="Submit" onclick="submit()"></td></tr>
</table>
<script>
function submit() {
var unmaskedData = JSON.parse(document.getElementById("unmasked").value);
var maskedData = JSON.stringify(unmaskedData, maskInfo, ' ');
document.getElementById("masked").value = maskedData;
}
function maskInfo(key, value)
{
//Default value is same as original
var maskedValue = value;
if (key == "accountNumber")
{
if(value && value.length > 5) {
maskedValue = "*" + maskedValue.substring(value.length - 4, value.length);
} else {
maskedValue = "****";
}
}
return maskedValue;
}
</script>
</body>
</html>
将我的问题放在评论部分。
学习愉快!
Android 教程
Maven – Spring Boot 胖/Uber Jar
原文: https://howtodoinjava.com/maven/create-fat-jar-spring-boot-applications/
在本 Maven 教程中,我们将学习使用 maven 插件为 SpringBoot 应用创建胖 jar 或 uber jar。 它非常简单,几乎不需要pom.xml
文件中的配置更改。
步骤 1 – 在pom.xml
中添加 Spring boot maven 插件
第一步,也许只是所需的第一步,是在pom.xml
文件中具有spring-boot-maven-plugin
插件条目。
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
阅读更多:使用 maven shade 插件创建胖 JAR
第 2 步 – 运行 Maven 构建
现在,当您使用简单的命令mvn clean install
运行 maven 构建时,它将为您的项目生成胖 jar,其中包含所有已编译的应用代码及其所有依赖项 – 打包在一个大 jar 文件中。
mvn clean install
演示
您可以将 Spring Boot REST API 示例的源代码下载到您的计算机中以运行演示。 如果希望查看项目代码内部,则可以选择将此项目作为 Maven 项目导入 eclipse。
导入的项目结构
Spring boot 应用
生成的胖 jar 文件
生成的 Maven 胖 JAR 文件
演示中使用的完整pom.xml
文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd;
<modelVersion>4.0.0</modelVersion>
<groupId>com.howtodoinjava.demo</groupId>
<artifactId>springbootdemo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>SpringBootDemo</name>
<description>Spring Boot Demo for https://howtodoinjava.com</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
这就是这个快速提示。 将我的问题放在评论部分。
学习愉快!
Android 教程:关键概念
原文: https://howtodoinjava.com/android/android-tutorial-key-concepts/
让我们从我们中有些人可能已经知道的大多数基本 android 概念开始。
Android 应用
用户可以从 Google Play 商店安装 Android 应用,也可以从互联网上的任何远程位置(甚至也可以从本地系统)将其下载到设备上。 该应用应该具有一些用户界面,并且可能还有一些其他代码旨在在该应用的后台工作。 在这里,我假设您对 Android 设备有一定的动手经验,因此您熟悉诸如 HOME 和 BACK 之类的按钮,内置设置,主屏幕和启动器的概念,等等。 如果您从未使用过 Android 设备,我强烈建议您购买一台并使用该设备和已安装在设备上的应用/或从应用商店下载的应用。 这将帮助您了解经过如此艰苦工作后将要构建的内容。
允许的编程语言
绝大多数 Android 应用都是用 Java 编写的。 但是,还有其他选择:
- 您可以使用 C/C++ 编写应用的各个部分。 通常这样做是为了提高性能或移植到现有应用的代码库等上。
- 您可以使用 C/C++ 编写整个应用。 这主要是针对使用 OpenGL 进行 3D 动画的游戏完成的。
- 您还可以使用 HTML,CSS 和 JavaScript 编写 android 应用的一部分。 有些工具会将它们打包到 Android 应用中。
但事实仍然是,Java 是构建 Android 应用最常用和流行的语言。 如果您想深入研究 android 应用开发,那么没有学习 Java 的任何借口。
主要 Android 组件
请记住,当您开始学习 Java 时,您的第一个程序是“HelloWorld”应用。 您编写了main()
方法和一些打印语句; 然后发生了一些魔术,并且输出是在控制台中编写的。 同样,当您进入 Web 编程区域时,通常您将首先学习/编写 http servlet。 您扩展一个类并在其中编写一些代码; 然后某些东西将控制权传递给您的 servlet,然后它开始执行。
Android 采用第二种方法,即,您扩展了一些特定的类,并在一些 XML 文件中定义了配置,因此很好地启动了第一个 android 应用。 通过扩展 Android 提供的基类而创建的子类称为组件。 以下是您应该事先了解的 4 个主要组成部分:
1)活动
用户界面的主要构建块称为活动。 您可以将活动视为在经典 Windows 应用中看到的用户界面。 就像在窗口中,应用将大部分屏幕从工具栏上移开一样,活动也会在移动设备屏幕上留下区域,仅用于顶部的条上包含设备时钟,信号强度指示器等。请记住此术语,您将在每个步骤中使用它 您的应用开发。
Android 活动
2)服务
活动是短暂的,可以随时关闭,例如当用户按下 BACK 按钮或 HOME 按钮时。 另一方面,服务的设计目的是,在需要时与应用内部的任何活动无关地在短时间内保持运行。 即使控制活动(即媒体播放器)不再在前屏幕上运行,您也可以使用一项服务来检查 RSS Feed 的更新或播放音乐。
3)内容供应器
内容供应器为“多个”应用可访问的设备中存储的任何数据提供抽象级别。 Android 开发模型鼓励您将自己的数据提供给其他应用。 建立内容供应器可以使您做到这一点,同时可以控制同一设备上其他应用如何访问您的数据。
4)广播接收器
该系统和/或其他应用将不时发送广播/通知,用于所有相关的内容,例如,当电池电量不足时,屏幕将关闭,或者连接性将从 WiFi 更改为移动数据等。您应用中的广播接收器将能够收听这些广播/通知,并以所需的方式做出相应的响应 。
Android 开发中使用的关键术语
a)小部件
用 Android 术语来说,小部件是用户界面的“微型”单元。 字段,按钮,标签,列表等都是小部件。 因此,您活动的用户界面由这些小部件中的一个或多个组成。 您可以想到普通网页中的所有文本框,下拉菜单和其他 HTML UI 元素。 在 Android 中,它们称为小部件。 很容易记住。
b)容器/布局管理器
如果您有多个小部件(这很典型),则需要告诉 Android 这些小部件在屏幕上的组织方式。 为此,您将使用各种称为“布局管理器”的容器类。 这些将使您根据需要以行,列或更复杂的方式放置内容。 为了描述容器和小部件的连接方式,通常将创建一个布局资源文件,并将其放入项目的资源文件夹中,Android 将从中直接将其拾取并自动为您呈现整个 UI。
用更熟悉的术语来说,它们等效于 HTML 中的DIV
,SPAN
或Table
标签。
c)资源
android 中的资源指的是图像,字符串以及应用在运行时使用的其他类似内容。 在 android 编程中,您将创建大量此类资源文件,以在应用运行时提供数据; 更像是普通 Java 应用中的属性文件。
d)片段
通常,您将以以下方式设计应用: UI 可以在各种设备上运行:手机,平板电脑,电视等。例如,平板电脑上的 Gmail 应用将显示标签列表, 平板电脑中的一个活动(屏幕)中所有选定标签中的对话列表以及选定对话中的消息列表。 但是,电话上的同一个 Gmail 应用无法执行此操作,因为屏幕空间不足,因此会在单独的活动(屏幕)中显示每个(标签,对话,消息)。 Android 提供了一个称为片段的构造,以帮助您更轻松地实现这些效果。 我们将在以后的文章中详细学习它们。
e)应用和包
给定一堆源代码和一揽子资源,Android 构建工具将为您提供一个应用。 该应用以 APK 文件的形式出现。 它是 APK 文件,您将上传到 Play 商店或通过其他方式分发。
要了解的重要一点是,每个 android 应用都有一个唯一的包名称,并且必须满足三个要求:
- 它必须是有效的 Java 包名称,因为此包中的 android 构建工具将生成一些 Java 源代码。
- 使用同一包的设备上不能同时存在两个应用。
- 具有相同包的两个应用都不能上传到 Play 商店。
因此,您将遵循“反向域名”约定选择一个包名称(例如com.howtodoinjava.android.app
)。 这样,域名系统将确保您的程序包名称前缀(com.howtodoinjava
)是唯一的,并且由您来确保其余程序包名称将您的应用与其他应用区分开。
Android 设备类型
Android 设备具有各种形状,大小和颜色。 但是,存在三个主要的“形式因素”:
- 电话
- 平板
- 电视
但是,重要的是要了解 android 没有内置的概念,即设备是“电话”,“平板电脑”或“电视”。 而是,Android 根据功能和特征来区分设备。 因此,尽管您可以询问 android,但您不会在任何地方看到isPhone()
方法:
- 屏幕尺寸是多少?
- 设备是否具有电话功能? 等等
同样,在构建应用时,无需考虑这三个形状因素,而是将重点放在所需的功能上。 这不仅可以帮助您更好地与 android 希望您构建应用的方式保持一致,而且还可以使您更轻松地适应即将出现的其他形状因素,例如:
- 手表和其他类型的可穿戴设备
- 飞机靠背娱乐中心
- 车载导航和娱乐设备等
Android 版本和 API 级别
自 2007 年下半年发布早期 Beta 版以来,Android 已经走了很长一段路。每个新的 Android OS 版本都为该平台增加了更多功能,开发人员可以利用这些功能做更多的事情。 而且,核心的 Android 开发团队会尽力确保向前和向后的兼容性。 您今天编写的应用在将来的 Android 版本(向前兼容)上应该可以保持不变,尽管它可能会丢失某些功能或以某种“兼容模式”运行。
为了帮助我们跟踪与开发人员相关的所有不同操作系统版本,Android 具有 API 级别。 当 Android 版本包含影响开发人员的更改时,会定义一个新的 API 级别。 创建仿真器 AVD(某种 VM)以测试您的应用时,您将指出仿真器应仿真的 API 级别。 分发应用时,您将指出应用支持的最旧 API 级别,因此该应用未安装在较旧的设备上。
以下是 android 版本和 API 级别的列表。 当您想要在至少具有某些功能的设备上安装应用时,将需要此信息。
- API 级别 3(Android 1.5)
- API 级别 4(Android 1.6)
- API 级别 7(Android 2.1)
- API 级别 8(Android 2.2)
- API 级别 9(Android 2.3)
- API 级别 11(Android 3.0)
- API 级别 15(Android 4.0.3)
- API 级别 16(Android 4.1)
- API 级别 17(Android 4.2)
- API 级别 18(Android 4.3)
- API 级别 19(Android 4.4)
Dalvik 虚拟机
虚拟机被许多编程语言使用,例如 Java,Perl 和 Smalltalk。 Dalvik VM 的工作原理类似于 Java VM,但针对嵌入式 Linux 环境进行了优化。 在开发环境中,您将使用此 VM 来测试您的代码,以查看其在实际 android 设备上的外观。
现在让我们看一下,当有人编写并运行一个 android 应用时真正发生了什么:
- 开发人员利用 Android 项目和第三方发布的类库编写 Java 语法源代码。
- 构建工具使用 Java SDK 随附的 javac 编译器将源代码编译为 Java VM 字节码。
- 生成工具将 Java VM 字节码转换为 Dalvik VM 字节码,该 Dalvik VM 字节码与其他文件一起打包成扩展名为
.apk
的 ZIP 存档(APK 文件)。 - Android 设备或模拟器运行 APK 文件,您将以实时应用的形式获取构建的内容。
从开发人员的角度来看,大多数构建工具都将其隐藏了。 您只需从顶部倒入 Java 源代码,APK 文件就会出现在漏斗的底部。
这是简短的 Android 入门教程的全部内容。 我将在以后的文章中再次回顾上述所有术语,以对整个图片和幕后事物有更深入的了解。
学习愉快!
Android 教程:在 Windows 上安装 Android
原文: https://howtodoinjava.com/android/android-tutorial-install-android-on-windows/
在之前的 android 教程中,我们学习了 android 开发中使用的常用术语,以及一些概念,这些内容将在您开发任何 android 应用时为您提供帮助。 在未来的教程中,我将讨论您需要针对 android 应用进行开发设置的概念。 因此,在进入实际的 android 开发概念之前,我们先准备一下开发机器。 我按顺序列出了所需的步骤。 按照给定的所有步骤,您还应该能够准备环境并开始进行实际的 android 应用开发。
Table of Contents
1) Download ADT Bundle
2) Add SDK Packages
3) Configure Virtual Devices
4) Test your development environment
1)下载 ADT 捆绑包
我遵循为您的机器配置 android 开发环境的最短根目录。 您只需要下载 ADT(Android 开发者工具包)并开始使用它即可。 无需配置 IDE,环境变量或其他任何东西。 只需下载并开始开发。
在下载之前,仅需检查一下您的计算机是 32 位还是 64 位。 对于较麻烦的计算机版本,有单独的 ADT 下载。
为 32 位 Windows 下载 ADT
为 64 位 Windows 下载 ADT
从上述任何位置下载 zip 文件后,将其解压缩到计算机中的任何文件夹。 请确保不在原始位置中移动分布式 ADT 中的任何文件。 它可能会破坏开发环境。 只需复制,提取和开发应用即可。
该 ADT 包随附 Android SDK,eclipse IDE 和 SDK Manager 的快捷方式,您将使用该快捷方式将新功能更新/导入开发环境。
2)添加 SDK 包
将 ADT 提取到计算机中的某个位置后,单击“SDK Manager.exe
”。 现在,选择要在计算机中安装的一组所需包。 接受所有条款和条件; 然后单击确定。 它将下载计算机中所有必需的包并自动配置。 您可以一次使用它们,设置完成。
在我的机器中,导入的包如下(截至日期):
SDK-Manager 预览
除了上述包外,您还应该从“其他”选项中下载“ Android 支持库”和“ Android 支持库”,这些选项隐藏在上图中的滚动条下方。
3)配置虚拟设备
您是开发测试应用还是生产类应用。 您需要先进行测试的设备,对吗? AVD 管理器将帮助您执行此操作,并创建一些测试应用的虚拟设备。
- 单击“ AVD 管理器”
配置 Android 虚拟设备 - 图标
- 创建新的虚拟设备
配置 Android 虚拟设备 - 创建按钮
- 填写 AVD 详细信息
配置 Android 虚拟设备 - 创建选项
- 点击完成
配置 Android 虚拟设备 - AVD 已创建
4)测试您的开发环境
与实际在环境中创建和运行应用相比,这是测试环境的更好方法。 请按照下面列出的屏幕快照来创建应用并在我们在第三步中创建的 AVD 中进行部署。
- 创建新的 Android 项目
创建 Android 项目 – Android 应用项目文件选项
- 向导选项 1:填写项目详细信息
创建 Android 项目 – 新项目向导 – 步骤 1
- 选择项目配置
包
创建 Android 项目 – 新项目向导 – 步骤 2
- 选择图标
创建 Android 项目 – 新项目向导 – 步骤 3
- 选择活动
创建 Android 项目 – 新项目向导 – 步骤 4
- 填写其他详细信息并完成
创建 Android 项目 – 新项目向导 – 完成
- 将该应用运行为 Android 应用
运行为 – Android 应用
- 查看在模拟器中运行的应用
应用运行
仅此而已。 您的 android 开发环境已经准备就绪,您还可以准备使用一些应用开发工具来弄脏您的手。 开始探索,我很快就会发布新内容。
祝您学习愉快!
Android 教程:如何创建 Android 应用/项目
原文: https://howtodoinjava.com/android/android-tutorial-how-to-create-android-app-project/
在我以前的 Android 教程中,我们首先学习设置 Android 开发环境 ,然后学习提高虚拟设备的性能,以便我们可以更快地开发和测试代码。 在本教程中,我列出了每次都会创建一个 Android 应用的步骤。 在这两个步骤之间,我将详细说明细节和一些提示,以便您在有疑问时希望可以为您提供帮助。
以下连续步骤将使用我们在上一篇文章中创建的开发设置,创建名为DemoApp
的 android 应用。 让我们开始乐趣吧。
步骤 1:创建 Android 项目
从 Eclipse 主菜单中,选择“文件 -> Android 应用项目”,然后单击“下一步”进入向导的下一页。
文件 – Android 应用项目
这将为您带来一个空白向导,用于创建新的 android 应用,如下所示:
空白的新 Android 应用屏幕
在此填写以下项目。
- 对于“应用名称”和“项目名称”,请填写
DemoApp
- 对于“包名称”,填写
com.howtodoinjava.demoapp
- 对于“最低要求的 SDK”,请选择“
API 11: Android 3.0 (Honeycomb)
” - 对于“目标 SDK”和“编译方式”,请选择“
API 19: Android 4.4 (Kitkat)
” - 选择主题“
Holo Dark
”
填写好的新 Android 应用屏幕 – 向导一
然后,单击“下一步”以转到向导的下一页:
空白的新 Android 应用屏幕 – 向导二
现在将所有选项保留为默认值,然后单击“下一步”。 在这一步,您将必须为您的应用选择启动器图标。 您只需要选择一个图像,向导就会完成剩下的工作,例如为不同的屏幕级别调整其大小并将其存储在项目中的适当位置。 酷!
我选择了如下图像。 您可以选择任何一个。
Android 创建加载器图标
现在单击下一步。 在这里,您选择要用作起点的模板项目。 保持“创建活动”复选框处于选中状态,然后从模板列表中选择“空白活动”。
Android 创建活动
然后,单击“下一步”移至向导的下一页,并填写以下详细信息:
- 对于“活动名称”,填写
DemoAppMainActivity
- 对于“布局名称”,请填写
main
Android 填写活动详细信息
此时,您可以单击“完成”按钮以完成向导。 您的新DemoApp
项目应出现在 Eclipse 包浏览器视图中,如下所示。
项目浏览器视图
步骤 2:执行一些次要清理
Android 开发者工具插件将为您提供适合您的应用的启动文件。 有时是正确的,有时是错误的。 就我们而言,它可能已经添加了一个我们不需要直接使用的库,我们现在也可以摆脱它。
查看项目的libs/
目录。 如果该目录存在,并且其中存在 JAR(可能名为android-support-v4.jar
),请将其删除。 但是,将空libs/
留在那里。
步骤 3:运行项目
现在,我们可以通过在设备或仿真器上运行项目来确认我们的项目已正确设置。
按下运行工具栏按钮(通常显示为绿色圆圈中的白色“播放”三角形)。 首次运行项目时,将看到“运行方式”对话框,提示您声明要如何运行应用:
项目作为 Android App 运行
单击“Android 应用”,然后单击“确定”继续。
此时,如果您有兼容的正在运行的仿真器或设备,则将安装该应用并在其上运行。 否则,Eclipse 将根据您在上一教程中创建的 AVD 启动合适的仿真器,然后在其上安装并运行该应用。
Android 正在运行的应用
请注意,您必须解锁设备或仿真器才能真正看到该应用在运行 - 它不会自动为您解锁。
在下一个教程中,我将详细讨论创建的项目中的内容。
祝您学习愉快!
Android 教程:Android 项目结构,文件和资源
原文: https://howtodoinjava.com/android/android-tutorial-android-project-structure-files-and-resources/
在上一个教程中,我们了解了创建示例 android 应用并在 android 虚拟设备(AVD)中运行。 在本教程中,我将简要介绍 android 应用的项目结构,在 android 应用中创建的文件和文件夹及其用法。 这些信息将在以后的教程中进行更详细的讨论,但是目前,仅进行少量介绍将为进一步发展打下基础。
Android 应用项目结构
1)项目根目录/文件夹
当您创建新的 Android 项目(例如,通过 android 创建项目)时,您会在项目的根目录中找到几项,包括:
AndroidManifest.xml
是描述正在构建的应用以及该应用正在提供哪些组件(活动,服务等)的 XML 文件。bin/
文件夹,用于在应用编译后保存该应用(请注意:此目录将在您首次构建应用时创建。通常,在 Eclipse 中启用了“自动构建”功能,因此您将在获取后直接获得它 项目已创建。)res/
文件夹,其中包含“资源”,例如图标,GUI 布局等,将与已编译的应用打包在一起src/
文件夹,其中包含应用的 Java 源代码lib/
文件夹,其中包含运行时所需的额外 jar 文件(如果有)asset/
文件夹,其中包含您希望与应用打包在一起以部署到设备上的其他静态文件gen/
文件夹包含 Android 构建工具生成的源代码
2)Java 源代码
创建项目时(例如,通过 android 创建项目),您为应用(例如com.howtodoinjava.DemoApp
)提供了“主”活动的全限定类名。 然后,您会发现项目的src/
树已经安装了包的目录树,还有代表您的主要活动的存根Activity
子类(例如src/com/howtodoinjava/DemoAppMainActivity.java
)。 欢迎您修改此文件,并根据需要将其他文件添加到src/
树中,以实现您的应用。
首次在项目包的目录中编译该项目时,Android 构建链将创建R.java
。 它包含许多与您在res/
目录树中放置的各种资源相关联的常量。
您不应该自己修改R.java
,让 Android 工具为您处理它。
3)Android 应用资源
您还将发现您的项目具有res/
目录树。 它包含“资源” - 与您的应用打包在一起的静态文件,它们可以是原始形式,也可以是预处理形式。 在res/
下可以找到或创建的一些子目录包括:
res/drawable/
用于图像(PNG,JPEG 等)res/layout/
用于基于 XML 的 UI 布局规范res/menu/
用于基于 XML 的菜单规范res/raw/
用于通用文件(例如,音频片段,帐户信息的 CSV 文件)res/values/
用于字符串,尺寸和类似内容res/xml/
用于您希望随应用一起提供的其他通用 XML 文件
一些目录名称可能带有后缀,例如res/drawable-hdpi/
。 这表明资源目录仅应在某些情况下使用 - 在这种情况下,可绘制资源应仅在具有高密度屏幕的设备上使用。
在我们的初始项目中,您将找到以下文件:
res/drawable-hdpi/icon.png
,res/drawable-ldpi/icon.png
和res/drawable-mdpi/icon.png
, 是您的高,低和中密度屏幕的应用的占位符图标的三种表示形式res/layout/main.xml
,其中包含一个 XML 文件,该文件描述了用户界面的非常简单的布局res/values/strings.xml
,其中包含外部化的字符串,尤其是应用的占位符名称
4)当我们编译 android 项目时会发生什么
当您编译项目(通过 ant 或 IDE)时,结果进入项目根目录下的bin/
目录。
Android bin
文件夹内容
特别:
bin/classes/
保存已编译的 Java 类bin/classes.dex
包含从那些编译的 Java 类创建的可执行文件bin/resources.ap_
保存您应用的资源,打包为 ZIP 文件(其中yourapp
是应用的名称)bin/DemoApp.apk
是实际的 Android 应用
.apk
文件是一个 ZIP 存档,其中包含.dex
文件,资源的编译版本,任何未编译的资源(例如,放入res/raw/
的资源)和AndroidManifest.xml
文件。
如果您构建应用的调试版本 - 您将DemoApp-debug.apk
和DemoApp-debug-aligned.apk
作为 APK 的两个版本。 后者已使用 zipalign 工具进行了优化,以使其运行更快。
这就是这篇小介绍性文章的全部内容,讨论了默认情况下每个 android 应用创建的各种文件和文件夹。 我们将在以后的教程中根据需要更深入地讨论它们。
祝您学习愉快!
Android 清单:指定 Android 应用和 SDK 版本
原文: https://howtodoinjava.com/android/android-manifest-specifying-android-app-and-sdk-versions/
在上一教程中,我们通过讨论默认情况下使用 Android 应用创建的项目结构,文件和文件夹,开始建立 android 知识库的基础。 让我们花更多的时间来理解一些我认为您必须事先知道的基本知识。
任何 Android 应用的基础都是项目根目录中的清单文件AndroidManifest.xml
。 在这里,您可以声明应用内部的内容 - 活动,服务等。 您还将指出这些部分如何将它们附加到整个 Android 系统上; 例如,您指出哪些活动应显示在设备的主菜单(即启动器)上。
在AndroidManifest.xml
文件中,您将声明各种版本属性,您将在本简短教程中学习这些属性。
android 清单文件的根是“manifest
”元素。 一个小清单如下所示:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.howtodoinjava.demoapp"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="11"
android:targetSdkVersion="18"
android:maxSdkVersion="21"/>
...
...
</manifest>
您需要在元素上提供的最大信息是package
属性。 在这里,您可以提供将被视为应用“基础”的 Java 包的名称。 您的包是应用的唯一标识符。 一台设备只能安装一个带有给定包的应用,而 Play 商店只会列出一个带有给定包的项目。 我们已经详细讨论了在 Android 开发中使用的关键术语和概念 。
指定应用版本
Android 清单还指定了android:versionName
和android:versionCode
属性。 这些代表您的应用的版本。 android:versionName
值是用户在其设置的“应用详细信息”屏幕中看到的版本指示符。 另外,如果您以这种方式分发应用,则 Play 商店列表会使用版本名称。 版本名称可以是您想要的任何字符串值。
另一方面,android:versionCode
必须为整数,并且较新版本必须具有比旧版本更高的版本代码。 Android 和 Play 商店会将新.apk
文件的版本代码与已安装.apk
文件的版本代码进行比较,以确定新 APK 是否确实是更新。 通常的方法是将版本代码从 1 开始,并在应用的每个生产版本中都将其递增,尽管您可以根据需要选择其他约定。 在开发过程中,您可以不理会这些,但是当您投入生产时,这些属性将非常重要。
指定 SDK / API 版本
Android 清单还包含一个元素作为该元素的子元素,以指定您支持的 Android 版本。 您的元素最重要的属性是android:minSdkVersion
。 这表示您正在与应用一起测试的最旧的 Android 版本。 该属性的值是代表 Android API 级别的整数。 因此,如果仅在 Android 2.1 和更高版本的 Android 上测试您的应用,则可以将android:minSdkVersion
设置为 7。请注意,您的应用将无法安装在较旧的 android 设备上。
您还应该指定android:targetSdkVersion
属性。 这表明您在编写代码时正在考虑的 Android 版本。 如果您的应用在更新版本的 Android 上运行,则 Android 可能会做一些事情来尝试针对更新的 Android 所做的更改来提高代码的兼容性。
通常,您不想要它,但是如果您不希望将您的应用安装在 API 级别大于一定数量的 android 设备上,则可以使用android:maxSdkVersion
属性设置此限制。 在大多数情况下,您根本不需要此属性,因为 android SDK 始终向后兼容,并且可以确保几乎始终确保您的应用可以在将来的版本中运行而不会出现任何问题。
这是简短的 android 教程的全部内容,清单文件中包含与版本参数相关的信息。 在以后的讨论中,我们将讨论可从AndroidManifest.xml
控制的其他概念/功能。
祝您学习愉快!
如何加快缓慢的 Android AVD / 模拟器
原文: https://howtodoinjava.com/android/how-to-speed-up-a-slow-android-avdemulator/
在上一教程中,我分享了 Windows 机器中 Android ADT 工具包的安装步骤。 然后,我们学习了有关创建 AVD(虚拟设备或仿真器)的知识,并将 HelloWorld 应用部署到了仿真器上。 您必须注意到一件事,默认情况下的仿真器非常慢。 我的意思是,它们是如此之慢,以至于您可以在此期间享用午餐,而且它们将一直处于加载状态。 我们可以改善这种慢速模拟器吗? 是的,我们可以。 让我们看看如何。
加快缓慢的 Android 模拟器
步骤 1)使用 SDK Manager 下载 HAXM 驱动
要提高仿真器的性能,您首先需要从 Android ADT 获得的 SDK Manager 中安装“英特尔硬件加速执行管理器驱动”。 查看下面的屏幕截图。
仿真器加速器选项
步骤 2)执行IntelHaxm.exe
以安装 HAXM 驱动
这将在您的 android ADT 包的“extra
”文件夹中创建IntelHaxm.exe
。 执行此文件以安装负责系统中虚拟化的 HAXM 驱动。
intelhaxm exe 文件
步骤 3)如果驱动安装失败,请更新 BIOS
上述安装过程可能会失败,建议您更新与“Intel Virtualization Technology
”相关的 BIOS 配置。 继续做吧。 有关此步骤的相关信息,请阅读购买时随主板附带的手册。
通常,在引导过程期间通过按下“DEL
”键进入 BIOS 选项时,可以在第三个选项卡中轻松找到菜单选项“Intel Virtualization Technology
”。 启用此选项并重新启动机器。
步骤 4)使用主机 GPU 选项
启用虚拟化技术后,您可以在创建仿真器时使用此功能,方法是选中“使用主机 GPU”复选框。 这将启用系统对模拟器虚拟化的本地支持。
配置 Android 虚拟设备主机 GPU
步骤 5)测试配置更改
现在启动仿真器,并查看通过上述步骤获得的主要性能提升。
祝您学习愉快!
Hadoop 教程
Hadoop – 大数据教程
原文: https://howtodoinjava.com/hadoop/hadoop-big-data-tutorial/
在这个 hadoop 教程中,我将讨论大数据技术的需求,他们打算解决的问题以及有关所涉及的技术和框架的一些信息。
Table of Contents
How really big is Big Data?
Characteristics Of Big Data Systems
How Google solved the Big Data problem?
Evolution of Hadoop
Apache Hadoop Distribution Bundle
Apache Hadoop Ecosystem
大数据到底有多大?
让我们从一些事实开始。 从开始到 2003 年,我们产生的数据量为 50 亿千兆字节。 2011 年每两天和 2013 年每十分钟创建一次相同的数量。此速度仍在极大地增长。 统计数据显示,每天有 500 TB 以上的新数据被摄入社交媒体网站 Facebook 的数据库中。 这些数据主要以照片和视频上传,消息,评论等形式生成(参考)。 Google 每天处理 20 PB 的信息。
参见下面的信息图。 它将帮助您认识到生成了这些源的数据量与数千个源相似。
大数据增长
图片: Erik Fitzpatrick CC BY 2.0
现在,您知道正在生成的数据量。 尽管如此大量的数据本身就是一个很大的挑战,但由于该数据没有固定格式,因此带来了更大的挑战。 它具有图像,视频,线路记录,GPS 跟踪详细信息,传感器记录以及许多其他形式。 简而言之,它是非结构化数据。 传统系统擅长处理结构化数据(也受限制),但它们无法处理如此大量的非结构化数据。
有人可能会问这个问题,为什么还要关心存储和处理数据呢? 出于什么目的? 答案是,我们需要这些数据,以便在我们正在研究的任何领域中做出更明智和可计算的决策。 业务预测并不是什么新鲜事物。 过去也已完成,但数据非常有限。 为了领先于竞争对手,企业必须使用这些数据,然后做出更明智的决策。 这些决定的范围从猜测消费者的喜好到提前预防欺诈活动。 每个领域的专业人员都可能找到他们分析此数据的原因。
大数据系统的特征
当您想确定下一个项目需要使用任何大数据系统时,请查看应用将产生的数据,并尝试寻找这些特征。 这些特性称为 4V 大数据。
-
容量
容量无疑是使大数据变得庞大的一部分。 互联网移动革命带来了大量社交媒体更新,来自设备的传感器数据以及电子商务的爆炸式增长,这意味着每个行业都充斥着数据,如果您知道如何使用它,那将是非常有价值的数据 。
您的数据是否正在扩展/或可能像上述统计数据一样呈爆炸式增长?
-
种类
存储在 SQL 表中的结构化数据已成为过去。 如今,从地理空间数据到可以分析内容和情感的推文,再到视觉数据(如照片和视频),各种形式和形式的 90% 生成的数据都是“非结构化”的。
您的数据是否始终保持结构化? 还是半结构化还是非结构化?
-
速度
每天每一分钟,全局用户在 YouTube 上上传 100 个小时的视频,发送超过 2 亿封电子邮件,以及发送 30 万条推文。 并且速度正在迅速增加。
您的数据速度如何,或者将来会怎样?
-
真实性
这是指营销商可获得的数据的不确定性(或可变性)。 这也可能适用于可能不一致的数据流的可变性,这使得组织更难以快速,更适当地做出反应。
您是否总是以一致的形式获取数据?
Google 如何解决大数据问题?
这个问题可能首先是因为搜索引擎数据使 Google 感到头疼,但随着互联网行业的革命而爆炸(尽管没有任何证据)。 他们使用并行处理的概念巧妙地解决了这个问题。 他们创建了一种称为 MapReduce 的算法。 该算法将任务分为小部分,并将这些部分分配给通过网络连接的许多计算机,然后收集结果以形成最终结果数据集。
当您意识到 I/O 是数据处理中最昂贵的操作时,这似乎合乎逻辑。 传统上,数据库系统将数据存储到单台计算机中,并且当您需要数据时,可以通过 SQL 查询的形式向它们发送一些命令。 这些系统从存储中获取数据,将其放入本地内存区域,进行处理并发送回给您。 这是最好的事情,您可以处理有限的数据和有限的处理能力。
但是,当您获得大数据时,就无法将所有数据存储在一台计算机中。 您必须将其存储到多台计算机(可能是数千台计算机)中。 而且,当您需要运行查询时,由于高 I/O 成本,您无法将数据聚合到一个位置。 那么 MapReduce 算法的作用是什么? 它会将您的查询独立运行到存在数据的所有节点中,然后汇总结果并返回给您。
它带来了两个主要的改进,即 I/O 成本非常低,因为数据移动很少。 因为您的工作并行地在多台计算机中分解成较小的数据集,所以时间减少了第二秒。
Hadoop 的演变
这一切始于 1997 年,当时 Doug Cutting 开始编写 Lucene(全文搜索库)以对整个 Web 进行索引(就像 Google 一样)。 后来 Lucene 被 Apache 社区改编,Cutting 与华盛顿大学研究生 Mike Cafarella 一起创建了一个 Lucene 子项目“Apache Nutch”。 Nutch 现在被称为网络爬虫。 Nutch 抓取网站,并在其获取页面时,Nutch 使用 Lucene 对页面内容进行索引(使其“可搜索”)。
最初,他们将应用部署在具有 1GB RAM 和 8 个硬盘驱动器,总容量 8 TB 的单台计算机中,索引速率约为每秒 1000 页。 但是,一旦应用数据增长,就会出现限制。 不能将整个互联网数据存储到一台机器中是可以理解的。 因此,他们又增加了 3 台计算机(主要用于存储数据)。 但这是一个挑战,因为现在他们需要将数据从一台机器手动移动到另一台机器。 他们希望使应用易于扩展,因为即使 4 台计算机也将很快装满。
因此,他们开始研究一种系统,该系统可以是没有模式的,没有预定义的结构,经久耐用,能够处理组件故障的系统。 硬盘故障并自动重新平衡,以平衡整个计算机群集中的磁盘空间消耗。 幸运的是,2003 年 10 月,Google 发表了他们的 Google 文件系统论文。 本文旨在解决他们面临的完全相同的问题。 太好了!
他们出色地在 Java 中实现了该解决方案,并将其称为 Nutch 分布式文件系统(NDFS)。 根据 GFS 论文,Cutting 和 Cafarella 通过将每个文件分成 64MB 块并将每个块存储在 3 个不同的节点(复制因子设置为 3)上,解决了持久化和容错性问题。 如果发生组件故障,系统将自动发现缺陷,并通过使用其他两个副本的数据重新复制故障节点上的数据块。 因此,发生故障的节点对 NDFS 的整体状态没有任何作用。
NDFS 解决了他们的一个问题,即存储,但带来了另一个问题“如何处理此数据”? 至关重要的是,新算法应具有与 NDFS 相同的可扩展性。 该算法必须能够达到尽可能高的并行度(能够同时在多个节点上运行的能力)。 幸运再次使勇敢者受益。 2004 年 12 月,Google 发表了另一篇关于类似算法“MapReduce”的论文。 太棒了!!
MapReduce 论文解决的三个主要问题是并行化,分布和容错。 这些是切特和卡法雷利亚面临的确切问题。 MapReduce 的主要见解之一是,不应强迫他人为了处理数据而移动数据。 而是将程序发送到数据所在的位置。 与传统的数据仓库系统和关系数据库相比,这是一个关键的区别。 在 2005 年 7 月,Cutting 报告说 MapReduce 已集成到 Nutch 中,作为其基础计算引擎。
2006 年 2 月,Cutting 从 Nutch 代码库中删除了 NDFS 和 MapReduce,并创建了 Hadoop。 它由 Hadoop Common(核心库),HDFS 和 MapReduce 组成。 这就是 hadoop 诞生的方式。
此后发生了许多事情,导致 Yahoo 在 MapReduce “Pig”之上贡献了他们的高级编程语言,而 Facebook 在“Hive”之上贡献了 SQL,这是 SQL 的第一个化身。 MapReduce 的顶部。
Apache Hadoop 分发套件
开源 Hadoop 由 Apache 软件基金会维护,网站位置为 http://hadoop.apache.org/。 当前的 Apache Hadoop 项目(版本 2.7)包括以下模块:
- Hadoop 通用:支持其他 Hadoop 模块的通用工具
- Hadoop 分布式文件系统(HDFS):提供对应用数据的高吞吐量访问的分布式文件系统
- Hadoop YARN:用于作业调度和集群资源管理的框架
- Hadoop MapReduce:基于 YARN 的系统,用于并行处理大型数据集
可以通过以下三种模式部署 Apache Hadoop:
- 独立:用于简单分析或调试到单机环境中。
- 伪分布式:它可帮助您模拟单个节点上的多节点安装。 在伪分布式模式下,每个组件进程都在单独的 JVM 中运行。
- 分布式:具有数十个,数百个或数千个多个节点的群集。
Apache Hadoop 生态系统
除了上述随 hadoop 分发的核心组件之外,还有许多组件可以补充基本的 Hadoop 框架,并为公司提供获得所需 Hadoop 结果所需的特定工具。
Hadoop 生态系统
- 数据存储层:这是将数据存储在分布式文件系统中的位置,该文件系统由 HDFS 和 HBase ColumnDB 存储组成。 HBase 是可扩展的分布式数据库,支持大型表的结构化数据存储。
- 数据处理层:此处要计算的调度,资源管理和集群管理。 使用 Map Reduce 的 YARN 作业调度和群集资源管理位于此层。
- 数据访问层:这是将来自管理层的请求发送到数据处理层的层。 Hive,一种数据仓库基础结构,提供数据汇总和临时查询; Pig,一种用于并行计算的高级数据流语言和执行框架; Mahout,可扩展的机器学习和数据挖掘库; Avro,数据序列化系统。
- 管理层:这是满足用户需求的层。 用户通过该层访问系统,该层具有以下组件:Chukwa,一个用于管理大型分布式系统的数据收集系统,以及 ZooKeeper,用于分布式应用的高性能协调服务。
在下一组文章中,我将详细介绍 hadoop 集群中涉及的编程概念。
祝您学习愉快!
参考文献
https://zh.wikipedia.org/wiki/Doug_Cutting
http://hadoop.apache.org/
http://lucene.apache.org/
http://nutch.apache.org/
http://hive.apache.org/
http://pig.apache.org/
http://research.google.com/archive/gfs.html
http://research.google.com/archive/mapreduce.html
https://medium.com/@markobonaci/the-history-of-hadoop-68984a11704(推荐阅读)
https://www.linkedin.com/pulse/100-open-source-big-data-architecture-papers-anil-madan(必须阅读)
Hadoop MapReduce 初学者教程
原文: https://howtodoinjava.com/hadoop/hadoop-mapreduce-tutorial-for-beginners/
Hadoop MapReduce 是一种软件框架,可轻松以可靠,容错的方式,编写可在商用硬件的大型集群(数千个节点)上并行处理大量数据(多 TB 数据集)的应用。 与 HDFS 相似,Hadoop MapReduce 甚至可以在商用硬件中执行,并且假定节点可以随时发生故障并且仍在处理作业。 通过将一个任务划分为多个独立的子任务,MapReduce 可以并行处理大量数据。 与 HDFS 相似,MapReduce 也具有主从结构。
MapReduce 作业通常将输入数据集分成独立的块,这些块由映射任务以完全并行的方式处理。 框架对映射的输出进行排序,然后将其输入到简化任务中。 通常,作业的输入和输出都存储在文件系统中。
输入和输出,甚至是 MapReduce 作业中的中间输出,都是<key, value>
对的形式。 键和值必须是可序列化的,并且不使用 Java 序列化程序包,而是具有一个接口,该接口必须实现并且可以有效地序列化,因为数据处理必须从一个节点移动到另一个节点。
MapReduce 应用中的步骤
在大数据中,您希望将大数据集分解为许多较小的部分,并使用相同的算法并行处理它们。 使用 HDFS,文件已经被分割成一口大小的片段。 MapReduce 可帮助您处理所有这些数据。
MapReduce 作业很复杂,涉及多个步骤。 某些步骤是由 Hadoop 以默认行为执行的,可以在需要时将其覆盖。 以下是在 MapReduce 中依次执行的必需步骤:
-
映射器
在 MapReduce 中,映射器代码应具有逻辑,该逻辑可以独立于其他块数据。 映射器逻辑应利用算法中所有可能的并行步骤。 在特定的
InputFormat
类型和必须在其上运行映射器进程的文件的驱动中设置对映射器的输入。 映射器的输出将是一个映射<key, value>
,在映射器输出中设置的键和值不会保存在 HDFS 中,而是会在 OS 空间路径中创建一个中间文件,并且该文件会被读取,随机播放和排序。 -
随机排序(合并)
随机排序和排序是映射器和归约器之间 MapReduce 中的中间步骤,由 Hadoop 处理,可以根据需要覆盖。 随机播放过程通过对映射器输出的键值进行分组来聚合所有映射器输出,并且该值将附加在值列表中。 因此,随机播放输出格式将为映射
<key, List<list of values>>
。 -
归约器
归约器是聚合器过程,将经过随机排序和排序后的数据发送到具有
<key, List<list of values>>
的归约器,然后归约器将在值列表上进行处理。 每个键可以发送到不同的归约器。 归约器可以设置该值,并将其合并到 MapReduce 作业的最终输出中,并且该值将作为最终输出保存在 HDFS 中。
MapReduce 流程
让我们来看看序列图的所有动作信息。 这将有助于更好地可视化整个过程。
MapReduce 序列图
让我们来看一个上述步骤的示例。 考虑一下,我们将一个文件分为两个节点,文件内容为:
file1
内容:“hello world hello moon
”file2
内容:“goodbye world goodnight moon
”
现在,在每个步骤之后,MapReduce 的结果如下:
MapReduce 步骤输出
没有归约器的应用称为“只有映射的作业”,当不需要合并映射任务的结果集时,它会很有用。
用 Java 编写 MapReduce 应用
要对 HDFS 中存储的大数据使用 MapReduce,必须提供以下输入:
- 指定输入/输出位置
- 通过实现适当的接口和/或抽象类来提供映射并简化功能
映射和归约函数必须与Serializable
键和值一起使用,因为它们将在不同的节点之间移动。
对于序列化,Hadoop 使用以下两个接口:
-
Writable
接口(用于值)Writable
接口用于序列化和反序列化的值。 一些实现Writable
接口的类是ArrayWritable
,BooleanWritable
和ByteWritable
等。我们可以创建自己的自定义Writable
类,该类可在 MapReduce 中使用。 为了创建自定义类,我们必须实现Writable
类并实现以下两种方法:void write (DataOutput out):
:序列化对象。void readFields (DataInput in)
:这将读取输入流并将其转换为对象。 -
WritableComparable
接口(用于键)WritableComparable
用于键,键是从Writable
接口继承的,并实现可比较的接口以提供值对象的比较。 其中的一些实现是BooleanWritable
,BytesWritable
和ByteWritable
等。为了创建自定义WritableComparable
类,我们必须实现WritableComparable
类并实现以下三种方法:void write (DataPutput out)
:序列化对象void readFields (DataInput in)
:读取输入流并将其转换为对象Int compareTo (Object obj)
:比较排序键所需的值
将以上输入合并到 MapReduce 作业中后,可以将其提交到JobTracker
。
JobTracker
和TaskTracker
流程
为了执行上面讨论的所有事情,MapReduce 具有以下两个守护进程:
-
JobTracker
(主进程)JobTracker
的主要功能是资源管理,跟踪资源可用性和任务处理周期。JobTracker
识别TaskTracker
来执行某些任务,并监视任务的进度和状态。JobTracker
是 MapReduce 流程的单点故障。 -
TaskTracker
(从属进程)TaskTracker
是从属守护程序进程,它执行JobTracker
分配的任务。TaskTracker
定期将心跳消息发送到JobTracker
,以通知有关可用插槽的信息,并将有关任务的状态发送给JobTracker
,并检查是否必须执行任何任务。
择时执行
MapReduce 作业分为多个映射器和归约器流程以及一些中间任务,因此一个作业可以产生数百或数千个任务,并且某些任务或节点可能需要很长时间才能完成任务。 Hadoop 监视和检测任务何时运行得比预期的慢,并且如果该节点的执行历史较慢,则它将在另一个节点中启动相同的任务作为备份,这称为推测性执行任务。 Hadoop 不会尝试修复或诊断节点或进程,因为该进程不会出错,但是速度很慢,并且由于硬件降级,软件配置错误,网络拥塞等原因会导致速度变慢。
一旦标记了执行缓慢的任务,JobTracker
就会在另一个节点上启动该任务,并获取该任务的结果,该结果首先完成并杀死其他任务并记录情况。 如果某个节点始终落后,则JobTracker
对该节点的优先级会降低。
可以启用或禁用推测性执行,并且默认情况下将其启用,因为它是一个有用的过程。 推测执行必须监视每个任务,在某些情况下会影响性能和资源。 不建议在执行某些任务的情况下执行推测执行,在这些任务中,特别是归约任务由于特定归约器上的数据偏斜而可能获得数百万个值,这比其他任务需要更长的时间,而启动另一个任务也无济于事。
MapReduce 的局限性?
- 您无法控制映射或缩小的运行顺序。
- 为了获得最大的并行度,您需要映射和归约不依赖于同一 MapReduce 作业中生成的数据(即两者都应该是无状态的)。
- 在所有映射都完成之前,不会进行归约操作。
- 一般的假设,归约的输出小于映射的输入。
- 由于带有索引的数据库将始终比未索引数据上的 MapReduce 作业快,因此性能可能下降。
祝您学习愉快!
HDFS – Hadoop 分布式文件系统架构教程
原文: https://howtodoinjava.com/hadoop/hdfs-hadoop-distributed-file-system-architecture-tutorial/
HDFS(Hadoop 分布式文件系统)是大数据的存储位置。 HDFS 的主要目标是即使在存在名称节点故障,数据节点故障和/或网络分区(CAP 定理中的“P”)等故障的情况下,也能可靠地存储数据。 本教程旨在研究将 HDFS 实现到分布式集群环境中涉及的不同组件。
Table of Contents
HDFS Architecture
NameNode
DataNodes
CheckpointNode
BackupNode
File System Snapshots
File Operations
Read/Write operation
Block Placement
Replication management
Balancer
Block Scanner
HDFS 架构
HDFS 是 Hadoop 的文件系统组件。 您可以可视化普通文件系统(例如 FAT 和 NTFS),但可以处理非常大的数据集/文件。 默认块大小为 64 MB(HDFS 2 中为 128 MB)。 因此,当您在其中存储大文件时,HDFS 的性能最佳。 小文件实际上会导致内存浪费。
在 HDFS 中,数据存储为两种形式,即实际数据和元数据(文件大小,块位置,时间戳等)。 元数据存储在名称节点中,应用数据存储在数据节点中。 所有服务器均已完全连接,并使用基于 TCP 的协议相互通信。 通过在多个数据节点之间分布存储和计算,集群可以水平增长,同时保持成本有限,因为这些节点是简单的商业硬件。
名称节点
名称节点存储元数据。 当您将大文件存储到 HDFS 中时,它会分成多个块(通常为 128 MB,但用户可以覆盖它)。 然后,将每个块分别存储到多个数据节点中(默认值为 3)。 名称节点是在称为 inode 的数据结构中存储文件块到数据节点的映射(文件数据的物理位置),文件权限,修改和访问时间戳,名称空间和磁盘空间配额的组件。
任何想要读取文件的客户端都将首先与名称节点联系以获取数据块的位置,然后再从距离该客户端最近的数据节点中读取块内容。 类似地,在写入数据时,客户端请求名称节点提名一组三个数据节点来写入块副本。 然后,客户端以管道方式(我们将看到如何完成)将数据写入数据节点。
任何群集一次只能具有一个名称节点。 HDFS 将整个名称空间保留在 RAM 中,以便更快地访问客户端程序。 并定期将这些数据以映像,检查点和日志的形式同步到持久化文件系统中。
内存中索引节点数据和属于每个文件的块列表组成了命名空间的元数据,称为映像。 图像的持久记录(存储在本机文件系统中)称为检查点。 本地主机的本机文件系统中称为日志的映像的修改日志。 在重新启动期间,名称节点通过读取检查点并重播日志来还原名称空间。
数据节点
数据节点存储实际的应用数据。 数据节点中的每个数据块在单独的文件中都有两部分,即数据本身及其元数据,包括块数据的校验和和块的生成标记。
在启动期间,每个数据节点都连接到名称节点并执行握手以验证名称空间 ID 和数据节点的软件版本。 如果其中一个与名称节点中存在的记录不匹配,则数据节点将自动关闭。 命名空间 ID 是整个集群的单个唯一 ID,当将节点格式化为包含在集群中时,命名空间 ID 将存储到所有节点中。 软件版本是 HDFS 的版本,其经过验证可防止由于新版本功能的更改而导致任何数据丢失。
允许新初始化的,没有任何名称空间 ID 的数据节点加入群集并接收群集的名称空间 ID。 也就是它拥有自己的唯一存储 ID。
握手后,数据节点向名称节点注册并发送其块报告。 块报告包含数据节点托管的每个块副本的块 ID,生成标记和长度。 第一个阻止报告在数据节点注册后立即发送。 随后的块报告每小时发送一次,并为数据节点提供有关块副本在群集上的位置的最新视图。
为了通知其实时状态,所有数据节点都会向名称节点发送心跳。 默认的心跳间隔为三秒。 如果名称节点在十分钟内未从数据节点接收到心跳,则名称节点会认为数据节点停止服务,并且该数据节点托管的块副本不可用。 然后,名称节点计划在其他数据节点上创建这些块的新副本。
来自数据节点的心跳还携带有关总存储容量,正在使用的存储部分以及当前正在进行的数据传输数量的信息。 这些统计信息用于名称节点的空间分配和负载平衡决策。
名称节点从不直接调用数据节点。 它使用对心跳的答复将指令发送到数据节点,例如,将块复制到其他节点,删除本地块副本,重新注册或关闭该节点或发送即时块报告。 这些命令对于维护整个系统的完整性非常重要,因此即使在大型集群上也要保持心跳频繁是至关重要的。 名称节点每秒可以处理数千个心跳,而不会影响其他名称节点操作。
检查点节点
检查点节点通常是群集中的另一个名称节点(托管在另一台计算机中,并且不直接为客户端提供服务)。 检查点节点从名称节点下载当前的检查点和日志文件,在本地合并它们,并将新的检查点返回给名称节点。 此时,名称节点存储此新检查点并清空其拥有的现有日志。
创建定期检查点是保护文件系统元数据的一种方法。 如果名称空间映像或日志的所有其他持久化副本在名称节点中不可用,则系统可以从最近的检查点开始。
备份节点
备份节点能够创建定期检查点,但除此之外,它还维护文件系统名称空间的内存中最新映像,该映像始终与名称节点的状态同步。 名称节点以更改流的形式通知备份节点所有事务。 由于名称节点和备份节点都将所有信息都存储在内存中,因此两者的内存要求相似。
如果名称节点发生故障,则备份节点在内存中的映像和磁盘上的检查点将记录最新的名称空间状态。 可以将备份节点视为只读名称节点。 它包含除块位置之外的所有文件系统元数据信息。 它可以执行常规名称节点的所有操作,这些操作不涉及名称空间的修改或块位置的知识。 这意味着当名称节点发生故障时,备份节点不会立即保留到目前为止尚未保留的所有最新更新。
请注意, 备份节点不是辅助名称节点(如果活动名称节点失败,则是备用名称节点),它不会受理客户端对读/写文件的请求。 辅助节点是 HDFS 2.x(Apache YARN)的功能,我们将在单独的教程中介绍。
文件系统快照
快照是整个群集的映像,将其保存以防止在系统升级时丢失任何数据。 实际的数据文件不是该映像的一部分,因为它将导致整个群集的存储区域加倍。
当管理员请求快照时,名称节点选择退出的检查点文件并将所有日志日志合并到该文件中并存储到持久文件系统中。 同样,所有数据节点复制其目录信息,并将硬链接链接到数据块并存储到其中。 群集故障时将使用此信息。
文件操作
首先,所有组件将构建 HDFS 文件系统,并以连续的方式发挥其作用,以保持群集的健康。 现在,让我们尝试了解在 HDFS 集群环境中如何进行文件读/写操作。
读/写操作
如果要将数据写入 HDFS,则只需创建一个文件,然后将数据写入该文件。 HDFS 为您管理了上述所有复杂性。 在读取数据时,给它一个文件名; 并开始从中读取数据。
要学习的重要一件事是一旦在 HDFS 中写入,就无法修改数据。 您只能向其附加新数据。 或者,只需删除该文件并写入一个新文件来代替它。
想要写入数据的客户端应用首先需要询问“写租约”(类似于写锁)。 客户端获得写租约后,其他客户端无法写入该文件,直到完成第一个程序或租约到期为止。 编写器客户端通过将心跳发送到名称节点来定期续订租约。 文件关闭后,租约将被撤销,其他程序可以请求对此文件进行写租约。
租约期限受软限制和硬限制的约束。 在软限制到期之前,编写者可以确定对该文件的独占访问权限。 如果软限制到期并且客户端无法关闭文件或续订租约,则另一个客户端可以抢占该租约。 如果硬限制到期后(一小时)并且客户端未能续订租约,则 HDFS 会假定客户端已退出,并将代表编写者自动关闭文件并恢复租约。
编写者的租约不会阻止其他客户读取文件; 一个文件可能有许多并发读取器。 将数据写入 HDFS 文件后,HDFS 不提供任何保证,除非关闭文件,否则新读取器可以看到该数据。 如果用户应用需要可见性保证,则可以显式调用“hflush
”操作。
数据以管道方式写入,如下所示。
HDFS 中的编写管道
上图描绘了三个数据节点(DN)的流水线和五个数据包的块。 客户端仅写入第一个数据节点,然后该数据节点将数据传递给第二个数据节点,依此类推。 这样,所有节点都参与数据写入。
客户端打开要读取的文件时,它将从名称节点获取块列表和每个块副本的位置。 每个块的位置按它们与阅读器的距离排序。 当读取块的内容时,客户端首先尝试最接近的副本。 如果读取尝试失败,则客户端将依次尝试下一个副本。
块放置
选择用于写入数据的节点时,名称节点遵循副本管理策略。 默认的 HDFS 副本放置策略如下:
- 没有数据节点包含任何块的一个以上副本。
- 如果集群上有足够的机架,则没有一个机架包含同一块的两个以上副本。
复制管理
名称节点的一项主要职责是,确保所有数据块均具有适当数量的副本。 在汇总来自数据节点的块报告期间,名称节点会检测到某个块的复制不足或过度。 当块变得过度复制时,名称节点选择要删除的副本。
当块复制不足时,会将其放入复制优先级队列中,以创建该数据块的更多副本。 只有一个副本的块具有最高优先级。 后台线程定期扫描复制队列的头部,以根据上述块放置策略决定将新副本放置在何处。
平衡器
HDFS 块放置策略未考虑数据节点磁盘空间利用率。 将新节点添加到群集时,也会发生不平衡。 平衡器是平衡 HDFS 群集上磁盘空间使用情况的工具。 该工具被部署为可以由集群管理员运行的应用。 它将副本从利用率较高的数据节点移到利用率较低的数据节点。
在选择要移动的副本并确定其目的地时,平衡器保证该决定不会减少副本数量或机架数量。
平衡器通过最小化机架间数据复制来优化平衡过程。 如果平衡器确定需要将副本 A 移动到其他机架,并且目标机架恰好具有相同块的副本 B,则将从副本 B 复制数据,而不是副本 A。
区块扫描器
每个数据节点运行一个块扫描器,该扫描器定期扫描其块副本并验证存储的校验和与块数据相匹配。 另外,如果客户端读取了完整的块并且校验和验证成功,它将通知数据节点。 数据节点将其视为副本的验证。
每当读取客户端或块扫描器检测到损坏的块时,它都会通知名称节点。 名称节点将副本标记为已损坏,但不会立即计划删除副本。 相反,它开始复制该块的良好副本。 仅当良好的副本数达到块的复制因子时,才计划删除损坏的副本。
这就是 HDFS 非常复杂的介绍。 让我知道你的问题。
祝您学习愉快!
Maven Shade 插件 – UberJar/胖 Jar 示例
原文: https://howtodoinjava.com/maven/maven-shade-plugin-create-uberfat-jar-example/
在此示例中,我们将学习使用 Maven Shade 插件将 Java 项目及其依赖项打包到胖 JAR 或超级 JAR 中。
Maven Shade 插件语法
让我们先了解一下 Maven Shade 插件的基本语法,然后再学习如何在项目中使用它。
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Main-Class>com.howtodoinjava.demo.App</Main-Class>
<Build-Number>1.0</Build-Number>
</manifestEntries>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
<goal>shade</goal>
告知应在程序包阶段中运行。ManifestResourceTransformer
在MANIFEST.MF
文件中创建条目,作为<manifestEntries>
中的键值对。- 您可以根据需要使用更多可用的传输。
示例 Maven 项目
让我们创建一个示例 maven 项目,并向其中添加一些依赖项。 这是它的pom.xml
文件。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd;
<modelVersion>4.0.0</modelVersion>
<groupId>com.howtodoinjava.demo</groupId>
<artifactId>MavenShadeExample</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>MavenShadeExample</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
</dependencies>
<build>
<finalName>MavenShadeExample-uber</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Main-Class>com.howtodoinjava.demo.App</Main-Class>
<Build-Number>1.0</Build-Number>
</manifestEntries>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
项目结构如下所示。
Maven Shade 插件结构
运行 Maven 包目标
> mvn package
在项目的根目录中运行package
目标时,您将获得两个生成的 jar 文件,即一个名为dependency-reduced-pom.xml
的额外pom.xml
文件。
MavenShadeExample-uber.jar
:这是胖/超级 jar,其中包含所有依赖项。- 依赖项减少的
pom.xml
:此生成的 Maven 文件是您的pom.xml
文件减去所有依赖项。 original-MavenShadeExample-uber.jar
:该 jar 是通过执行dependency-reduced-pom.xml
生成的。
通过运行以下命令验证生成的所有类。
jar -tvf MavenShadeExample-uber.jar
输出将在命令提示符中列出,其中包含所有类。
清单文件的内容也可以被验证。
Manifest-Version: 1.0
Build-Number: 1.0
Build-Jdk: 1.6.0_33
Created-By: Apache Maven
Main-Class: com.howtodoinjava.demo.App
将我的问题放在评论部分。
学习愉快!
Brewer 的 CAP 定理简述
原文: https://howtodoinjava.com/hadoop/brewers-cap-theorem-in-simple-words/
当您开始讨论分布式架构时,很可能会遇到 CAP 理论(或 Brewer 定理)。 CAP 代表一致性,可用性和分区容错性。 它希望系统设计师在最终设计中在以上三个竞争保证之间做出选择。 据说不可能在系统中实现全部三项,并且您必须在系统的三项保证中最多选择两项。
好。 定义似乎简单快捷。 可是等等 !! 您所说的一致性,可用性和分区容错性是什么意思? 让我们在分布式计算环境中定义这些术语。
-
一致性
一致的服务将完全运行或根本不运行。 一致性在非分布式系统中的 ACID 属性中为“C”,适用于数据库事务的理想属性,这意味着数据将永远不会持久保存(回滚),从而打破了某些预设约束。
在分布式环境中,一致性意味着,正在从集群读取数据的所有客户端程序在任何给定时间点都会看到相同的数据。 从两个节点获取数据的两个客户端应该根本看不到不同的数据。
-
可用性
可用性意味着,无论群集内部发生什么,您都应该能够检索存储在分布式系统中的数据。 如果发出请求,则必须从系统得到响应; 即使集群中的一个节点(或多个节点)发生故障。
-
分区容错性
分区容错性意味着即使两个节点(两个节点都在上,但无法通信)之间存在“分区”(通信中断),群集(作为一个整体)仍可以继续运行。
不允许出现少于总网络故障的一组故障,从而导致系统无法正确响应。
因此,CAP 定理使您可以选择想要添加到系统中的 3 个保证中的任意两个。 您不能保证单个分布式系统中的全部 3 个,例如,为了获得可用性和分区容错性,您必须放弃一致性。
CAP 定理例子
不管您读过什么,大部分都是定义部分,对于初学者来说有点复杂。 让我们用更简单的英语来了解
您需要设计一个由 4 个数据节点组成的分布式集群。 复制因子为 2,即任何写入群集的数据都必须写入 2 个节点; 因此,当发生故障时,一秒钟即可提供数据。 现在,尝试对此要求应用 CAP 定理。 在分布式系统中,任何时候都可能发生两件事,即节点故障(硬盘崩溃)或网络故障(两个节点之间的连接断开)(分布式计算的谬误)。
我们将尝试根据这些事实/假设来设计我们的系统。
CP(一致性/分区容错性)系统
在分布式系统中,在读取数据时,一致性是由一种投票机制决定的,在该机制中,所有拥有数据副本的节点都同意他们拥有所请求数据的“相同副本”。 现在,假设我们请求的数据存在于两个节点 N1 和 N2 中。 客户端尝试读取数据; 而且我们的 CP 系统也可以容忍分区,因此发生了预期的网络故障,并且将 N2 检测为宕机。 现在,系统无法确定 N1 的数据副本是否为最新; 它也可能是陈旧的。 因此,系统决定将错误事件发送给客户端。 在这里,系统选择了数据一致性而不是数据可用性。
同样,如果复制因子为 2,则在写入数据时,系统可能会拒绝写入请求,直到找到两个运行状况良好的节点以一致的方式完全写入数据为止。
AP(可用性/分区容错性)系统
如果在上述情况下系统而不是发送错误(如果 N2 处于关闭状态)怎么办? 它发送从 N1 接收的数据。 客户端得到了数据,但是过去存储在系统中的是最新数据副本吗? 你不能决定。 您选择可用性而不是一致性。 这些是 AP 系统。
CA(一致性/可用性)系统
在分布式环境中,我们无法避免 CAP 的“P”。 因此,我们必须在 CP 或 AP 系统之间进行选择。 如果我们希望拥有一个一致且可用的系统,那么我们就必须忽略分区容错性,只有在非分布式系统(例如 oracle 和 SQL Server)中才有可能。
CAP 理论示例
在当今世界中,我们可以在分布式系统中实现这三个目标(如果不是全部,则至少是部分)。 例如 Cassandra 中的可调一致性。
CAP 定理就像关于软件项目的古老玩笑一样:您可以在时间,预算或正确性中选择任意两个🙂
祝您学习愉快!
参考:http://www.julianbrowne.com/article/viewer/brewers-cap-theorem
Java 云开发简介和工具
原文: https://howtodoinjava.com/cloud/java-cloud-development-introduction-and-tools/
如今,“云”已成为业界最热门的话题。 每个人似乎都在朝着它冲来。 所有财大气粗的大公司都在投资或利用它。 同时,对于大多数开发人员来说,这似乎是一个难题。 让我们今天解决这个难题,并了解其确切含义。 什么使云如此有趣和有用? 还有更重要的问题,当我们谈论云开发时,对我们的开发人员有什么帮助。
Table of Contents
What is Cloud Computing
Advantages of Cloud Computing
Cloud Service Delivery Models
- Infrastructure as a Service (IaaS)
- Platform as a Service (PaaS)
- Software as a Service (SaaS)
Cloud Deployment Models
- Public Cloud
- Private Cloud
- Hybrid Cloud
Java Cloud Development Tools
Oracle Java Cloud Service
AWS SDK for Java
Google App Engine
Cloudfoundry
Heroku Java
Jelastic
IBM SmartCloud
Openshift
什么是云计算
简而言之,云计算就是基于互联网的计算。 通常,术语“云”用于指代用于分布式计算的平台。 如 Wiki 中所述,“云计算,也是按需计算,是一种基于互联网的计算,可根据需要向计算机和其他设备提供共享的处理资源和数据。”
作为普通用户,当您检查电子邮件或在互联网上搜索某些内容时,您会从很少的云功能中受益。 在这些示例中,使用了处理技术的功能,该功能存在于遥远的位置,并且用户不知道。 还有许多其他这样的示例,例如在 Dropbox 中存储或在 Google 驱动器中保存文档。 所有这些都是由云驱动的。 当我们开始研究云的 5 个基本特征时,您将获得更多见解。
-
按需功能
用户能够在不需要人工干预的情况下配置云计算资源,这通常是通过基于 Web 的自助服务门户完成的。 从 SMTP 服务器到以 GB 为单位的存储限制,这些资源可以是任何东西。 您应该要做的就是 – 登录控制台并添加/删除所需的服务。
-
广泛的网络访问
所有云服务都必须可以通过网络访问,并且对可以连接到它们的客户端应用没有任何限制。 一个人应该能够使用办公台式机,笔记本电脑或他的手机/智能手机连接到云接口。 这种移动性对企业特别有吸引力,因此在上班时间或下班时间,无论是在路上还是在办公室中,员工都可以随时随地关注项目,契约和客户。
-
资源池
使用多租户模型将提供商的计算资源集中起来为多个消费者提供服务,并根据消费者需求动态分配和重新分配不同的物理和虚拟资源。 资源包括存储,处理,内存,网络带宽,虚拟机和电子邮件服务等。
-
快速扩容
资源是根据触发器或参数按需配置和/或自动分配和释放的。 这将确保您的应用在任何时间点都具有所需的容量。 例如,在为您的应用提供最高负载时,云将为您分配资源来处理所有任务 - 但是当负载较低时,这些资源将为其他一些客户端提供服务。
-
实测服务
根据使用情况透明地监视,测量和报告(计费)资源使用情况。 如果您已经使用或知道亚马逊云基础架构,那么它会为您提供数百种服务 - 但是您只需要为每月使用的服务和消耗的资源付费。 用简单的英语来说,是按使用付费。
云计算的优势
今天,如果您在 Facebook 上发布身份,预订机票或通过移动设备转帐资金,则很有可能正在使用后端云提供的某些服务。 越来越多的组织以非常快的速度向云迁移,为什么呢? 这是因为云计算可以提高效率,帮助改善现金流并提供更多好处 - 同时有效解决小型企业每天面临的一些最复杂的问题。 让我们列出一些好处,云提供:
- 高可用性:大多数云提供商在提供服务方面都非常可靠,其中许多维护正常运行时间为 99.99% 。 连接始终处于打开状态,这意味着您的应用始终处于运行状态。
- 可扩展性和性能:迁移到云使所有人都能使用企业级技术。 它还允许小型企业比大型,成熟的竞争对手采取行动更快。 随用随付服务和云业务应用意味着小型公司可以与大型公司一起运行,扰乱市场,同时保持精简和灵活。
- 自动化备份和恢复:云提供商为您提供了计划备份的功能,因此您不会由于端到或任何其他因素的错误而丢失数据。 您已经有了备份,只需很少的鼠标单击就可以回滚到最小的工作量。
- 最新及时更新:云提供商会为您照顾软件,并定期发布软件更新(包括安全更新),因此您不必担心浪费时间自己维护系统。 让您可以自由地专注于重要的事情,例如发展业务。
- 具有成本效益:也许,最显着的云计算优势在于节省 IT 成本。 借助云计算,您可以在内部服务器存储和应用需求为零的情况下节省大量的资金成本。 缺乏内部部署基础架构还消除了相关的运营成本,包括电力,空调和管理成本。 您需要为使用的东西付费,并可以随时删除。
云服务交付模型
云基础架构的交付模型可以大致分为基础架构即服务(IaaS),平台即服务(PaaS)或软件即服务(SaaS)。 让我们详细看看它们。
基础架构即服务(IaaS)
IaaS 提供物理计算机或(通常)虚拟机和其他物理资源。 如果您在博客世界中,或者对服务器基础结构感到好奇,则可以将其与托管服务器,内存和网络相关联。 所有基础结构服务,例如计算机,存储设备和路由器,都属于此类别。 它们主要是基于虚拟机的解决方案,其中根据您的成本向您分配了一定数量的资源。 亚马逊,Rackspace,微软,Bluehost 和 Godaddy 等公司是 IaaS 的领先提供商。
请注意,IaaS 可能不附带捆绑软件,您可以自行安装和配置所需的软件和数据库,因为云供应商很可能会提供适当的工具来帮助您这样做。
此外,云提供商可能会在收取象征性费用后向您出售预配置的资源和基础架构。
平台即服务(PaaS)
PaaS 供应商为应用开发人员提供了开发环境。 在 PaaS 模型中,云提供商提供了一个计算平台,通常包括操作系统,编程语言执行环境,数据库和 Web 服务器。 应用开发人员可以在云平台上开发和运行其软件解决方案,而无需购买和管理基础硬件和软件层的成本和复杂性。
平台即服务(PaaS)用户不管理或控制底层的云基础架构,包括网络,服务器,操作系统或存储,但是可以控制已部署的应用以及应用托管环境的可能配置设置。
软件即服务(SaaS)
在 SaaS 模型中,用户可以访问特定的应用软件和数据库。 云提供商管理运行应用的基础架构和平台。 SaaS 有时被称为“按需软件”,通常按使用付费或使用订阅费定价,例如,每月或每年。
提供的服务包括:
- 企业服务,例如工作流管理,群件和协作,供应链,通信,数字签名,客户关系管理(CRM),桌面软件,财务管理,地理空间和搜索。
- Web 2.0 应用,例如元数据管理,社交网络,博客,Wiki 服务和门户网站服务。
云部署模型
部署模型主要是指为单个或多个组织提供的服务。 它们分为 3 个主要类别:
公有云
当通过开放供公众使用的网络提供服务时,云被称为“公共云”。 通常,诸如 Amazon AWS,Microsoft 和 Google 之类的公共云服务提供商在其数据中心拥有并运营基础架构,并且通常通过互联网进行访问。
资源使用可以是收费的还是免费的 - 取决于提供给供应器的服务和基于服务的服务。
私有云
私有云是专门为单个组织运营的云基础架构,无论是内部管理还是第三方管理。 这需要大量的参与才能虚拟化整个业务环境。 如果经常提高业务能力,但是项目的每个步骤都会引发安全问题,必须解决这些问题以防止出现严重的漏洞。
私有云可以托管在 IT 组织内部,或者像 Rackspace 这样的云供应商可以为特定公司自定义其基础架构的一部分。
混合云
混合云是由两个或更多云(私有云,社区云或公共云)组成的,这些云仍然是不同的实体,但被捆绑在一起,提供了多种部署模型的优势。
例如,组织可以将敏感的客户端数据存储在私有云应用的内部,但可以将该应用与作为软件服务在公共云上提供的商业智能应用互连。
Java 云开发工具
-
Oracle Java 云服务
Oracle Java 云服务是 Oracle 云中平台服务产品的一部分。 通过使用 Oracle Java 云服务,您可以快速创建和配置 Oracle WebLogic 服务器域并设置 Java EE 应用环境,而不必担心自己设置任何基础架构或平台详细信息。 您创建的所有 Oracle Java 云服务实例也都进行了预配置,以使用 Oracle 数据库云(数据库即服务)中的实例以及在 Oracle 存储云服务中创建的对象存储容器。
创建 Oracle Java 云服务实例时,可以选择专为更高可用性需求而设计的环境,例如用户验收测试,登台和生产,或者专为开发和测试而设计的环境。
您可以请求 Oracle Java 云服务的试用订阅或购买订阅。
-
适用于 Java 的 AWS 开发工具包
Amazon 提供了在 AWS 云上开发安全,可靠和可扩展的 Java 应用所需的所有工具,文档和示例代码。 Eclipse Java IDE 用户可以使用适用于 Eclipse 的 AWS 工具包轻松地开始使用 SDK。 用于 Eclipse 的 AWS 工具包是 Eclipse Java IDE 的插件,使开发人员可以更轻松地使用 Amazon Web 服务开发,部署和调试 Java 应用。
Amazon 还为许多 AWS 服务(包括 Amazon S3,Amazon EC2,DynamoDB 等)提供了一组专用的 API。 单个可下载的包包括 AWS Java 库,代码示例和文档。
-
Google 应用引擎
Google 应用引擎应用易于创建,易于维护,并且可以随着流量和数据存储需求的变化而轻松扩展。 使用应用引擎,无需维护任何服务器。 您只需上传您的应用即可使用。
通过应用引擎,您可以使用 Servlet 或监听端口
8080
的服务器代码轻松部署和运行标准 Java Web 应用。 应用引擎应用会根据传入流量自动扩展。 负载平衡,微服务,授权,SQL 和 noSQL 数据库,内存缓存,流量拆分,日志记录,搜索,版本控制,推出和回滚以及安全扫描均受本机支持,并且可以高度自定义。要在应用引擎上运行应用,您需要:
- 一个
app.yaml
文件,描述您的应用的运行时配置,其中包括实例的最小和最大数量以及所需的 CPU 和 RAM。 - 一个
pom.xml
文件,该文件描述了应用的程序包依赖项。 - 您的 Java 应用代码。
除了这些要求之外,应用引擎通常与您要使用的开发工具,框架和库无关。
- 一个
-
Cloudfoundry
CloudFoundry 是一个开源的云计算平台即服务(PaaS),最初由 VMware 开发,现在归 Pivotal Software 所有,后者是由 EMC,VMware 和 General Electric 合资成立的。
CloudFoundry 支持从初始开发到所有测试阶段再到部署的整个生命周期。 因此,它非常适合连续交付策略。 用户可以访问一个或多个空间,这些空间通常对应于生命周期阶段。 例如,可以将准备好进行质量检查的应用推送(部署)到其项目的质量检查空间。 可以将不同的用户限制在具有不同访问权限的不同空间中。
CloudFoundry 有三种口味。
CloudFoundry 开源软件(OSS):任何人都可以使用。 部署此版本的 CloudFoundry 涉及使用 CloudFoundry BOSH(高级外壳)部署脚本语言(Pivotal 的另一种开源工具)与基础架构进行接口。 百度网站在 OSS CloudFoundry 上实现。
Pivotal CloudFoundry(Pivotal CF):可从 Pivotal 获得的商业产品。 它提供了 OSS 产品中未包含的用于安装和管理的额外工具。
Pivotal Web 服务(PWS):托管在 Amazon Web 服务(AWS)上的 Pivotal CloudFoundry 实例。
-
Heroku Java
Heroku 是基于托管容器系统的云平台即服务(PaaS)平台,具有集成的数据服务和强大的生态系统,用于部署和运行现代应用。 Heroku 开发人员的经验是一种以应用为中心的软件交付方法,并与当今最流行的开发人员工具和工作流集成在一起。
Heroku 支持多种编程语言,例如 Java,Node.js,Scala,Clojure,Python,PHP 和 Go。 Heroku 使在云中轻松部署和扩展 Java 应用变得容易。 无论您是喜欢将标准库与 Tomcat 或 Jetty 之类的应用服务器一起使用,还是与 Spring 或 Play 之类的框架一起使用,Heroku 都不会妨碍您 - 允许您使用自己喜欢的工具构建自己的东西。
-
Jelastic
Jelastic (Java Elastic 的缩写)是在单个平台内不受限制的基于 PaaS 和容器的 IaaS,可提供高可用性的应用,自动垂直和水平缩放。 Jelastic 提供了复杂的 PaaS(如 Heroku)的所有功能,但还集成了基础架构即服务(如 Amazon)的关键元素。 他们的托管客户需要一个完整的垂直栈,该栈可以安装在裸机上,并可以通过计费集成等交钥匙功能轻松进行管理。
如今,Jelastic 的“基础架构平台”正迅速成为全局托管服务提供商的标准,并通过以仅现有虚拟化解决方案的一小部分成本提供卓越的统包式云环境来渗透企业市场。
Jelastic 的独特之处在于它没有限制或代码更改要求,并且提供了自动化的垂直扩展,应用生命周期管理以及来自世界各地多个托管提供商的可用性。 Jelastic 通过插件支持 IntelliJ IDEA 和 Eclipse。
Jelastic 团队的顾问包括 PHP Rasmus Lerdorf 的创建者,Java James Gosling 和巴西 JavaMan Bruno Souza 的父亲。
-
IBM SmartCloud
IBM SmartCloud 是用于构建和使用私有,公共和混合云的一系列企业级云计算技术和服务。 SmartCloud 产品可以作为自助服务或托管服务购买。
除了构成云的组件之外,IBM 云还包括通过公共,私有和混合云交付模型提供的基础架构即服务(IaaS),软件即服务(SaaS)和平台即服务(PaaS)。 IBM 将这些产品归为三类:SmartCloud 基础,SmartCloud 服务和 SmartCloud 解决方案。
SmartCloud 基础由基础架构,硬件,供应,管理,集成和安全性组成,它们是私有云或混合云的基础。 PaaS,IaaS 和备份服务使用这些基本组件构建而成,构成了 SmartCloud 服务。 SmartCloud 解决方案在此云平台和基础架构上运行,由许多协作,分析和营销 SaaS 应用组成。
IBM 提供了五种云配置模型:
- 由客户拥有和运营的私有云
- 客户拥有但由 IBM(或其他提供商)运营的私有云
- 由 IBM(或其他提供商)拥有和运营的私有云
- 虚拟私有云服务(基于对单个企业的多租户支持)
- 公共云服务(基于向个人提供的功能)
-
Openshift
OpenShift 是 RedHat 的平台即服务(PaaS),允许开发人员在云环境中快速开发,托管和扩展应用。 借助 OpenShift,您可以选择提供的产品,包括在线,本地和开源项目选项。
借助 OpenShift,您可以使用自己喜欢的应用服务器和框架轻松地部署和运行 Java 应用。 想要运行由 MySQL 支持的 Spring 的 JBoss 服务器吗? 或者,您是否正在寻找具有 Scala 和 MongoDB 的 GlassFish? 如果它可以在 Red Hat Enterprise Linux 64 位上运行,那么它可以在 OpenShift 上运行。
OpenShift 支持源和 WAR 文件部署以及
server.xml
的修改。
作为开发人员,还有许多其他事情可能会让您感兴趣。 我将鼓励您在云上进行更多探索。
学习愉快!
MongoDB 教程
MongoDB 简介:为什么选择 MongoDB?
原文: https://howtodoinjava.com/mongodb/introduction-to-mongodb-why-mongodb/
在直接进入 MongoDB 的基础知识之前,我们应该尝试了解 No-SQL 数据库本身的需要。 为什么传统的关系数据库失去了与新竞争对手(如 MongoDB)的战斗。 为什么它们在当今如此受欢迎。 为什么?
为什么使用 NoSQL?
过去 15 年中,交互式应用发生了巨大变化,这些应用的数据管理需求也发生了变化。 数据变得更加易于通过 Facebook,D & B 等第三方进行捕获和访问。 个人用户信息,地理位置数据,社交图谱,用户生成的内容,机器记录数据和传感器生成的数据只是捕获的数据不断扩展的一些示例。 并且对数据的使用正在迅速改变通信,购物,广告,娱乐和关系管理的性质。 无法快速利用它的应用将很快落后。
开发人员需要一个非常灵活的数据库,该数据库可以轻松容纳新的数据类型,并且不会因第三方数据提供商的内容结构更改而受到干扰。 许多新数据都是非结构化和半结构化的,因此开发人员还需要一个能够有效存储数据的数据库。 不幸的是,关系数据库使用的严格定义的,基于模式的方法使不可能快速合并新类型的数据,并且不适用于非结构化和半结构化数据。 NoSQL 提供了可以更好地映射这些需求的数据模型。
为什么选择 MongoDB?
MongoDB 是开源文档数据库,可在一组用作存储节点的可配置系统上提供高性能,高可用性和自动扩展。
MongoDB 之所以大放异彩,是因为它易于使用,无论开发人员是在跨越数百或数千个节点的大型应用中使用它,还是在无需扩展的单服务器应用中使用它。
如果您还记得在 JSON 中,我们将信息存储在键值对中,如下所示:
{
name : "lorem",
address : "ipsum
}
MongoDB 将所有数据存储在文档中,该文档是由字段和值对组成的 JSON 样式的数据结构。 MongoDB 以 BSON 序列化格式将文档存储在磁盘上。 BSON 是 JSON 文档的二进制表示形式,尽管它包含比 JSON 更多的数据类型。 这些文档可以是上面的简单文档,也可以是下面的复杂文档:
{
id: x,
name: y,
other: z,
multipleArray: [
{lab1: "A", lab2: "B", lab3:"C"},
{lab1: "AB", lab2: "BB", lab3:"CB"},
{lab1: "AC", lab2: "BC", lab3:"CC"}
]
}
MongoDB 中大多数用户可访问的数据结构是文档,包括:
- 所有数据库记录。
- 查询选择器,用于定义要选择进行读取,更新和删除操作的记录。
- 更新定义,它们定义在更新期间要修改的字段。
- 索引规范,用于定义要索引的字段。
- MongoDB 用于报告和配置的数据输出,例如服务器状态和副本集配置文档的输出。
请注意,与关系数据库比较时,MongoDB 也有一些限制:
a)不支持连接
b)不支持事务
这就是这篇简短的介绍性文章。 当我们开始学习 MongoDB 时,将学习数百种概念。
学习愉快!
参考文献:
http://www.mongodb.com/leading-nosql-database
http://docs.mongodb.org/manual/core/introduction/
如何在 Windows 上安装 MongoDB
原文: https://howtodoinjava.com/mongodb/how-to-install-mongodb-on-windows/
在本 MongoDB 教程中,我列出了在 Windows 计算机上安装 MongoDB 的步骤。 另外,我们还将学习与 MongoDB 的启动和关闭有关的一些基本操作。
Table of Contents
1) Download MongoDB
2) Install MongoDB
3) Create mongo.config Configuration File
4) Start/Shutdown the MongoDB Server
5) MongoDB Windows Service
6) Download/Use MongoDB Java Driver
7) Verify MongoDB Installation
1. 下载 MongoDB
Windows 有 MongoDB 的三种构建:
1)Windows Server 2008 R2 版的 MongoDB(下载链接)
2)Windows 版 64 位 MongoDB(下载链接)
3)Windows 版 32 位 MongoDB(下载链接)
要查找您正在运行的 Windows 版本,请在命令提示符中输入以下命令:c:\> wmic os get osarchitecture
更多下载选项在 MongoDB 的官方网站上提供。
2. 安装 MongoDB
上面给定的链接将下载 zip 文件,您可以将其直接提取到所选系统中的任何位置。 我已经将它们提取到“d:/mongodb
”中。 因此,本文中的所有代码示例以及以后的文章都将引用此位置。
建议在 Windows 环境变量中添加d:/mongodb/bin
,以便您可以直接在命令提示符下访问 MongoDB 的命令。
另外,请在d:/mongodb
中创建以下目录
D:\mongodb\data
D:\mongodb\log
3. 创建mongo.config
配置文件
这是前进之前的重要一步。 在位置d:/mongodb
中创建一个普通文本文件,并将其保存为名称mongo.config
。
现在将以下配置选项放置在文件中。 您可以随意更改选项的值。
##Which IP address(es) mongod should bind to.
bind_ip = 127.0.0.1
##Which port mongod should bind to.
port = 27017
##I set this to true, so that only critical events and errors are logged.
quiet = true
##store data here
dbpath=D:\mongodb\data
##The path to the log file to which mongod should write its log messages.
logpath=D:\mongodb\log\mongo.log
##I set this to true so that the log is not overwritten upon restart of mongod.
logappend = true
##log read and write operations
diaglog=3
##It ensures write durability and data consistency much as any journaling scheme would be expected to do.
##Only set this to false if you don’t really care about your data (or more so, the loss of it).
journal = true
4. 启动/关闭 MongoDB 服务器
要启动 MongoDB 服务器,请在命令提示符下使用以下命令:
mongod.exe –config d:\mongodb\mongo.config
D:\mongodb\bin>mongod --config D:\mongodb\mongo.config --journal
2014-05-25T16:51:18.433+0530 warning: --diaglog is deprecated and will be removed in a future release
2014-05-25T16:51:18.434+0530 diagLogging level=3
2014-05-25T16:51:18.435+0530 diagLogging using file D:\mongodb\data/diaglog.5381d22e
要在命令提示符下将连接到 MongoDB ,请使用以下命令:
d:\mongodb\bin>mongo
D:\mongodb\bin>mongo
MongoDB shell version: 2.6.1
connecting to: test
Welcome to the MongoDB shell.
For interactive help, type "help".
For more comprehensive documentation, see http://docs.mongodb.org/
Questions? Try the support group http://groups.google.com/group/mongodb-user
Server has startup warnings:
2014-05-25T16:52:09.158+0530 [initandlisten]
2014-05-25T16:52:09.158+0530 [initandlisten] ** NOTE: This is a 32 bit MongoDB binary.
2014-05-25T16:52:09.158+0530 [initandlisten] ** 32 bit builds are limited to less than 2GB of data (or less with --jour
nal).
2014-05-25T16:52:09.158+0530 [initandlisten] ** See http://dochub.mongodb.org/core/32bit
2014-05-25T16:52:09.158+0530 [initandlisten]
要关闭 MongoDB 服务器,您必须是授权用户。 因此,完成认证后,在命令提示符下使用以下命令:
db.shutdownServer()
> use admin
switched to db admin
> db.shutdownServer()
2014-05-25T19:55:25.221+0530 DBClientCursor::init call() failed
server should be down...
2014-05-25T19:55:25.224+0530 trying reconnect to 127.0.0.1:27017 (127.0.0.1) failed
2014-05-25T19:55:26.225+0530 warning: Failed to connect to 127.0.0.1:27017, reason: errno:10061 No connection could be made b
ecause the target machine actively refused it.
2014-05-25T19:55:26.225+0530 reconnect 127.0.0.1:27017 (127.0.0.1) failed failed couldn't connect to server 127.0.0.1:27017 (
127.0.0.1), connection attempt failed
> quit()
D:\mongodb\bin>
5. MongoDB Windows 服务
要安装窗口服务,请使用以下命令:
mongod –config D:\mongodb\mongo.config –install
从命令提示符启动 Windows 服务:
net start MongoDB
从命令提示符处停止 Windows 服务:
net stop MongoDB
删除 Windows 服务
mongod –delete
以下是上述所有四个命令的示例运行:
D:\mongodb\bin>mongod --config D:\mongodb\mongo.config --install
2014-05-25T20:07:41.191+0530 warning: --diaglog is deprecated and will be removed in a future release
2014-05-25T20:07:41.192+0530 diagLogging level=3
2014-05-25T20:07:41.193+0530 diagLogging using file D:\mongodb\data/diaglog.53820035
D:\mongodb\bin>net start MongoDB
The MongoDB service was started successfully.
D:\mongodb\bin>net stop MongoDB
System error 109 has occurred.
The pipe has been ended.
D:\mongodb\bin>mongod --remove
2014-05-25T20:09:32.514+0530
2014-05-25T20:09:32.515+0530 warning: 32-bit servers don't have journaling enabled by default. Please use --journal if you wa
nt durability.
2014-05-25T20:09:32.515+0530
2014-05-25T20:09:32.518+0530 Trying to remove Windows service 'MongoDB'
2014-05-25T20:09:32.520+0530 Service 'MongoDB' removed
6. 下载/使用 MongoDB Java 驱动
从此下载链接下载 MongoDB Java 驱动(mongo-java-driver-2.9.3.jar
)。 这是一个 jar 文件,您需要在要使用 MongoDB 的项目的libpath
文件夹中的classpath/copy
中包含该文件。
7. 验证 MongoDB 安装
要验证是否已安装 MongoDB 并正常工作,请执行以下程序:
package examples.mongodb.install;
import java.net.UnknownHostException;
import java.util.List;
import com.mongodb.MongoClient;
public class VerifyMongoDBInstallation {
public static void main(String[] args) throws UnknownHostException {
MongoClient mongo = new MongoClient("localhost", 27017);
List<String> dbs = mongo.getDatabaseNames();
for (String db : dbs) {
System.out.println(db);
}
}
}
Output:
local
admin
全部完成 MongoDB Windows 安装,启动和关闭操作。 接下来,我们将学习一些 CRUD 操作。 跟着我保持关注。
学习愉快!
Java MongoDB:使用 GridFS API 获取/保存图像
原文: https://howtodoinjava.com/mongodb/java-mongodb-getsave-image-using-gridfs-apis/
在先前的教程中,我们了解了 MongoDB 基础知识和在 Windows 中安装 MongoDB 的知识,以及有关在 MongoDB 中插入文档和从 MongoDB 选择文档的知识。 在本教程中,我仅提供一些代码示例,这些代码示例将在您必须使用图像或其他二进制数据类型来从 MongoDB 存储/检索/删除此类数据时提供帮助。
对于 CRUD 操作,我们将使用 MongoDB 的 GridFS API 。 在理想情况下,GridFS 是规范,用于存储和检索超出 BSON 文档大小限制 16MB 的文件。 而不是将文件存储在单个文档中, GridFS 将文件划分为多个部分或大块,并将这些大块中的每个存储为单独的文档。 默认情况下,GridFS 将块大小限制为 255k。 GridFS 使用两个集合来存储文件。 一个集合存储文件块,另一个集合存储文件元数据。
当您查询 GridFS 存储中的文件时,驱动或客户端将根据需要重新组装块。 您可以对通过 GridFS 存储的文件执行范围查询。 您还可以从文件的任意部分访问信息,从而可以“跳入”视频或音频文件的中间。
GridFS 不仅可用于存储超过 16MB 的文件,而且还可用于存储要访问的任何文件,而不必将整个文件加载到内存中。
Code Examples List
Save an image into MongoDB
Get/Retrieve an image from MongoDB
Get/Retrieve all images from MongoDB
Save the retrieved image into local file-system
Delete an image from MongoDB
为了进行测试,我将图像存储在名为DemoImage.png
的临时位置(c:
驱动器)中。 我将此图像存储到 MongoDB 中。
将图像保存到 MongoDB
private static void saveImageIntoMongoDB(DB db) throws IOException {
String dbFileName = "DemoImage";
File imageFile = new File("c:\\DemoImage.png");
GridFS gfsPhoto = new GridFS(db, "photo");
GridFSInputFile gfsFile = gfsPhoto.createFile(imageFile);
gfsFile.setFilename(dbFileName);
gfsFile.save();
}
从 MongoDB 获取/检索图像
private static void getSingleImageExample(DB db) {
String newFileName = "c:/DemoImage";
GridFS gfsPhoto = new GridFS(db, "photo");
GridFSDBFile imageForOutput = gfsPhoto.findOne(newFileName);
System.out.println(imageForOutput);
}
Output:
{ "_id" : { "$oid" : "53cff8d736414e8af4a4f0b8"} , "chunkSize" : 262144 , "length" : 138855 ,
"md5" : "b75f77c16c3ac6472365c06cde15d0da" , "filename" : "DemoImage" , "contentType" : null ,
"uploadDate" : { "$date" : "2014-07-23T18:03:03.403Z"} , "aliases" : null }
从 MongoDB 获取/检索所有图像
private static void listAllImages(DB db) {
GridFS gfsPhoto = new GridFS(db, "photo");
DBCursor cursor = gfsPhoto.getFileList();
while (cursor.hasNext()) {
System.out.println(cursor.next());
}
}
Output:
{ "_id" : { "$oid" : "53cff8d736414e8af4a4f0b8"} , "chunkSize" : 262144 , "length" : 138855 ,
"md5" : "b75f77c16c3ac6472365c06cde15d0da" , "filename" : "DemoImage" , "contentType" : null ,
"uploadDate" : { "$date" : "2014-07-23T18:03:03.403Z"} , "aliases" : null }
将检索到的图像保存到本地文件系统中
private static void saveToFileSystem(DB db) throws IOException {
String dbFileName = "DemoImage";
GridFS gfsPhoto = new GridFS(db, "photo");
GridFSDBFile imageForOutput = gfsPhoto.findOne(dbFileName);
imageForOutput.writeTo("c:/DemoImageNew.png");
}
从 MongoDB 删除图像
private static void deleteImageFromMongoDB(DB db) {
String dbFileName = "DemoImage";
GridFS gfsPhoto = new GridFS(db, "photo");
gfsPhoto.remove(gfsPhoto.findOne(dbFileName));
}
完整的示例代码
以下是构建以上简短示例的完整信息。 随意玩。
package examples.mongodb.crud;
import java.io.File;
import java.io.IOException;
import com.mongodb.DB;
import com.mongodb.DBCursor;
import com.mongodb.MongoClient;
import com.mongodb.gridfs.GridFS;
import com.mongodb.gridfs.GridFSDBFile;
import com.mongodb.gridfs.GridFSInputFile;
public class MongoDBBinaryExample
{
public static void main(String[] args) throws IOException
{
MongoClient mongo = new MongoClient("localhost", 27017);
DB db = mongo.getDB("howtodoinjava");
//Save a image in DB
saveImageIntoMongoDB(db);
//Get a image from DB
getSingleImageExample(db);
//Get all images from DB
listAllImages(db);
saveToFileSystem(db);
//Delete images from DB
deleteImageFromMongoDB(db);
//Verifying if image was deleted or not
getSingleImageExample(db);
}
private static void saveImageIntoMongoDB(DB db) throws IOException {
String dbFileName = "DemoImage";
File imageFile = new File("c:\\DemoImage.png");
GridFS gfsPhoto = new GridFS(db, "photo");
GridFSInputFile gfsFile = gfsPhoto.createFile(imageFile);
gfsFile.setFilename(dbFileName);
gfsFile.save();
}
private static void getSingleImageExample(DB db) {
String newFileName = "c:/DemoImage";
GridFS gfsPhoto = new GridFS(db, "photo");
GridFSDBFile imageForOutput = gfsPhoto.findOne(newFileName);
System.out.println(imageForOutput);
}
private static void listAllImages(DB db) {
GridFS gfsPhoto = new GridFS(db, "photo");
DBCursor cursor = gfsPhoto.getFileList();
while (cursor.hasNext()) {
System.out.println(cursor.next());
}
}
private static void saveToFileSystem(DB db) throws IOException {
String dbFileName = "DemoImage";
GridFS gfsPhoto = new GridFS(db, "photo");
GridFSDBFile imageForOutput = gfsPhoto.findOne(dbFileName);
imageForOutput.writeTo("c:/DemoImageNew.png");
}
private static void deleteImageFromMongoDB(DB db) {
String dbFileName = "DemoImage";
GridFS gfsPhoto = new GridFS(db, "photo");
gfsPhoto.remove(gfsPhoto.findOne(dbFileName));
}
}
Output:
{ "_id" : { "$oid" : "53cff8d736414e8af4a4f0b8"} , "chunkSize" : 262144 , "length" : 138855 , "md5" : "b75f77c16c3ac6472365c06cde15d0da" , "filename" : "DemoImage" , "contentType" : null , "uploadDate" : { "$date" : "2014-07-23T18:03:03.403Z"} , "aliases" : null }
{ "_id" : { "$oid" : "53cff8d736414e8af4a4f0b8"} , "chunkSize" : 262144 , "length" : 138855 , "md5" : "b75f77c16c3ac6472365c06cde15d0da" , "filename" : "DemoImage" , "contentType" : null , "uploadDate" : { "$date" : "2014-07-23T18:03:03.403Z"} , "aliases" : null }
null
这就是本简单教程的全部内容。 希望它能帮助有需要的人。
祝您学习愉快!
参考:http://docs.mongodb.org/manual/core/gridfs/
Java MongoDB:在集合中插入文档的示例
原文: https://howtodoinjava.com/mongodb/java-mongodb-insert-documents-in-collection-examples/
在 MongoDB 学习系列中,我们已经介绍了 MongoDB 基础知识, 在 Windows 中安装 MongoDB 和如何从集合中查询/选择文档。 在本教程中,我列出了 4 种可用于在 MongoDB 中向集合中插入或添加文档的方法。
List of examples in this tutorial
1) Insert BasicDBObject in collection
2) Use DBObjectBuilder to insert document in collection
3) Use java.util.HashMap to build BasicDBObject and insert into collection
4) Parse JSON to build DBObject and insert into collection
我们将插入集合的示例文档
{
"name":"lokesh",
"website":"howtodoinjava.com",
"address":{
"addressLine1":"Some address",
"addressLine2":"Karol Bagh",
"addressLine3":"New Delhi, India"
}
}
1)在集合中插入BasicDBObject
这是最简单的。 创建BasicDBObject
的实例,填充数据并调用collection.insert()
方法。
BasicDBObject document = new BasicDBObject();
document.put("name", "lokesh");
document.put("website", "howtodoinjava.com");
BasicDBObject documentDetail = new BasicDBObject();
documentDetail.put("addressLine1", "Sweet Home");
documentDetail.put("addressLine2", "Karol Bagh");
documentDetail.put("addressLine3", "New Delhi, India");
document.put("address", documentDetail);
collection.insert(document);
Output:
{ "_id" : { "$oid" : "538d56a3364192189d4f98fe"} , "name" : "lokesh" , "website" : "howtodoinjava.com" ,
"address" : { "addressLine1" : "Sweet Home" , "addressLine2" : "Karol Bagh" , "addressLine3" : "New Delhi, India"}}
2)使用DBObjectBuilder
在集合中插入文档
与上面的示例非常相似,仅使用DBObjectBuilder
来构建DBObject
。
BasicDBObjectBuilder documentBuilder = BasicDBObjectBuilder.start()
.add("name", "lokesh")
.add("website", "howtodoinjava.com");
BasicDBObjectBuilder documentBuilderDetail = BasicDBObjectBuilder.start()
.add("addressLine1", "Some address")
.add("addressLine2", "Karol Bagh")
.add("addressLine3", "New Delhi, India");
documentBuilder.add("address", documentBuilderDetail.get());
collection.insert(documentBuilder.get());
Output:
{ "_id" : { "$oid" : "538d56a3364192189d4f98ff"} , "name" : "lokesh" , "website" : "howtodoinjava.com" ,
"address" : { "addressLine1" : "Sweet Home" , "addressLine2" : "Karol Bagh" , "addressLine3" : "New Delhi, India"}}
3)使用java.util.HashMap
构建BasicDBObject
并插入到集合中
在这里,您首先将数据放入哈希图中,然后使用该哈希图构建BasicDBObject
。
Map<String, Object> documentMap = new HashMap<String, Object>();
documentMap.put("name", "lokesh");
documentMap.put("website", "howtodoinjava.com");
Map<String, Object> documentMapDetail = new HashMap<String, Object>();
documentMapDetail.put("addressLine1", "Some address");
documentMapDetail.put("addressLine2", "Karol Bagh");
documentMapDetail.put("addressLine3", "New Delhi, India");
documentMap.put("address", documentMapDetail);
collection.insert(new BasicDBObject(documentMap));
Output:
{ "_id" : { "$oid" : "538d56a3364192189d4f98fg"} , "name" : "lokesh" , "website" : "howtodoinjava.com" ,
"address" : { "addressLine1" : "Sweet Home" , "addressLine2" : "Karol Bagh" , "addressLine3" : "New Delhi, India"}}
4)解析 JSON 以构建DBObject
并插入到集合中
String json = "{ 'name' : 'lokesh' , " +
"'website' : 'howtodoinjava.com' , " +
"'address' : { 'addressLine1' : 'Some address' , " +
"'addressLine2' : 'Karol Bagh' , " +
"'addressLine3' : 'New Delhi, India'}" +
"}";
DBObject dbObject = (DBObject)JSON.parse(json);
collection.insert(dbObject);
Output:
{ "_id" : { "$oid" : "538d56a3364192189d4f98fg"} , "name" : "lokesh" , "website" : "howtodoinjava.com" ,
"address" : { "addressLine1" : "Sweet Home" , "addressLine2" : "Karol Bagh" , "addressLine3" : "New Delhi, India"}}
完整的示例和源代码
以上所有示例的完整工作代码如下:
package examples.mongodb.crud;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;
import com.mongodb.BasicDBObject;
import com.mongodb.BasicDBObjectBuilder;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.MongoClient;
import com.mongodb.WriteResult;
import com.mongodb.util.JSON;
public class MongoDBInsertDataExample
{
public static void main(String[] args) throws UnknownHostException
{
MongoClient mongo = new MongoClient("localhost", 27017);
DB db = mongo.getDB("howtodoinjava");
DBCollection collection = db.getCollection("users");
///Delete All documents before running example again
WriteResult result = collection.remove(new BasicDBObject());
System.out.println(result.toString());
basicDBObject_Example(collection);
basicDBObjectBuilder_Example(collection);
hashMap_Example(collection);
parseJSON_Example(collection);
DBCursor cursor = collection.find();
while(cursor.hasNext()) {
System.out.println(cursor.next());
}
}
private static void basicDBObject_Example(DBCollection collection){
BasicDBObject document = new BasicDBObject();
document.put("name", "lokesh");
document.put("website", "howtodoinjava.com");
BasicDBObject documentDetail = new BasicDBObject();
documentDetail.put("addressLine1", "Sweet Home");
documentDetail.put("addressLine2", "Karol Bagh");
documentDetail.put("addressLine3", "New Delhi, India");
document.put("address", documentDetail);
collection.insert(document);
}
private static void basicDBObjectBuilder_Example(DBCollection collection){
BasicDBObjectBuilder documentBuilder = BasicDBObjectBuilder.start()
.add("name", "lokesh")
.add("website", "howtodoinjava.com");
BasicDBObjectBuilder documentBuilderDetail = BasicDBObjectBuilder.start()
.add("addressLine1", "Some address")
.add("addressLine2", "Karol Bagh")
.add("addressLine3", "New Delhi, India");
documentBuilder.add("address", documentBuilderDetail.get());
collection.insert(documentBuilder.get());
}
private static void hashMap_Example(DBCollection collection){
Map<String, Object> documentMap = new HashMap<String, Object>();
documentMap.put("name", "lokesh");
documentMap.put("website", "howtodoinjava.com");
Map<String, Object> documentMapDetail = new HashMap<String, Object>();
documentMapDetail.put("addressLine1", "Some address");
documentMapDetail.put("addressLine2", "Karol Bagh");
documentMapDetail.put("addressLine3", "New Delhi, India");
documentMap.put("address", documentMapDetail);
collection.insert(new BasicDBObject(documentMap));
}
private static void parseJSON_Example(DBCollection collection){
String json = "{ 'name' : 'lokesh' , " +
"'website' : 'howtodoinjava.com' , " +
"'address' : { 'addressLine1' : 'Some address' , " +
"'addressLine2' : 'Karol Bagh' , " +
"'addressLine3' : 'New Delhi, India'}" +
"}";
DBObject dbObject = (DBObject)JSON.parse(json);
collection.insert(dbObject);
}
}
Output:
{ "serverUsed" : "localhost/127.0.0.1:27017" , "connectionId" : 3 , "n" : 4 , "syncMillis" : 0 , "writtenTo" : null , "err" : null , "ok" : 1.0}
{ "_id" : { "$oid" : "538d5b3936417871aa391d20"} , "name" : "lokesh" , "website" : "howtodoinjava.com" , "address" : { "addressLine1" : "Sweet Home" , "addressLine2" : "Karol Bagh" , "addressLine3" : "New Delhi, India"}}
{ "_id" : { "$oid" : "538d5b3936417871aa391d21"} , "name" : "lokesh" , "website" : "howtodoinjava.com" , "address" : { "addressLine1" : "Some address" , "addressLine2" : "Karol Bagh" , "addressLine3" : "New Delhi, India"}}
{ "_id" : { "$oid" : "538d5b3936417871aa391d22"} , "address" : { "addressLine3" : "New Delhi, India" , "addressLine2" : "Karol Bagh" , "addressLine1" : "Some address"} , "website" : "howtodoinjava.com" , "name" : "lokesh"}
{ "_id" : { "$oid" : "538d5b3936417871aa391d23"} , "name" : "lokesh" , "website" : "howtodoinjava.com" , "address" : { "addressLine1" : "Some address" , "addressLine2" : "Karol Bagh" , "addressLine3" : "New Delhi, India"}}
祝您学习愉快!
MongoDB 查找文档示例
原文: https://howtodoinjava.com/mongodb/mongodb-find-documents/
学习在 MongoDB 中找到文档。 此 mongodb 查找文档教程涵盖了多种方法,它们与我们在 SQL 中使用WHERE
子句的条件相同,可以查询单个文档或查找多个文档。
Table of Contents
1) Select all documents from a collection
2) Select first document from a collection
3) Select single document and limited field(s) from a collection
4) Select all documents with unique id from a collection
5) Document ids IN clause example
6) Document ids less than or greater than clause example
7) Document ids NOT IN clause example
8) Documents matching multiple fields example
9) Documents matching field value to some REGEX example
MongoDB 查找文档 – 测试数据准备
为了构建和运行本教程中使用的示例,我在数据库中填充了 20 个员工文档,其中employeeId
从 1 到 20,以及employeeName
从TestEmployee_1
到TestEmployee_10
。
private static void setUpTestData(DBCollection collection){
for (int i=1; i <= 10; i++) {
collection.insert(new BasicDBObject().append("employeeId", i).append("employeeName", "TestEmployee_"+i));
}
}
现在运行我们的示例,并获取不同场景下的数据。
1. MongoDB find()
– 从集合中选择所有文档
private static void selectAllRecordsFromACollection(DBCollection collection)
{
DBCursor cursor = collection.find();
while(cursor.hasNext())
{
System.out.println(cursor.next());
}
}
程序输出。
{ "_id" : { "$oid" : "538782753641d31b0cad0142"} , "employeeId" : 1 , "employeeName" : "TestEmployee_1"}
{ "_id" : { "$oid" : "538782753641d31b0cad0143"} , "employeeId" : 2 , "employeeName" : "TestEmployee_2"}
{ "_id" : { "$oid" : "538782753641d31b0cad0144"} , "employeeId" : 3 , "employeeName" : "TestEmployee_3"}
{ "_id" : { "$oid" : "538782753641d31b0cad0145"} , "employeeId" : 4 , "employeeName" : "TestEmployee_4"}
{ "_id" : { "$oid" : "538782753641d31b0cad0146"} , "employeeId" : 5 , "employeeName" : "TestEmployee_5"}
{ "_id" : { "$oid" : "538782753641d31b0cad0147"} , "employeeId" : 6 , "employeeName" : "TestEmployee_6"}
{ "_id" : { "$oid" : "538782753641d31b0cad0148"} , "employeeId" : 7 , "employeeName" : "TestEmployee_7"}
{ "_id" : { "$oid" : "538782753641d31b0cad0149"} , "employeeId" : 8 , "employeeName" : "TestEmployee_8"}
{ "_id" : { "$oid" : "538782753641d31b0cad014a"} , "employeeId" : 9 , "employeeName" : "TestEmployee_9"}
{ "_id" : { "$oid" : "538782753641d31b0cad014b"} , "employeeId" : 10 , "employeeName" : "TestEmployee_10"}
2. MongoDB findOne()
– 从集合中选择第一个文档
private static void selectFirstRecordInCollection(DBCollection collection)
{
DBObject dbObject = collection.findOne();
System.out.println(dbObject);
}
程序输出:
{ "_id" : { "$oid" : "538782a53641ed9125df86c0"} , "employeeId" : 1 , "employeeName" : "TestEmployee_1"}
3. MongoDB where
子句 – 从集合中选择单个文档和有限的字段
private static void selectSingleRecordAndFieldByRecordNumber(DBCollection collection)
{
BasicDBObject whereQuery = new BasicDBObject();
whereQuery.put("employeeId", 5);
BasicDBObject fields = new BasicDBObject();
fields.put("employeeId", 1);
DBCursor cursor = collection.find(whereQuery, fields);
while (cursor.hasNext()) {
System.out.println(cursor.next());
}
}
程序输出:
{ "_id" : { "$oid" : "53878332364101041fb2c141"} , "employeeId" : 5}
4. MongoDB 通过 ID 查找文档
private static void selectAllRecordByRecordNumber(DBCollection collection)
{
BasicDBObject whereQuery = new BasicDBObject();
whereQuery.put("employeeId", 5);
DBCursor cursor = collection.find(whereQuery);
while(cursor.hasNext()) {
System.out.println(cursor.next());
}
}
程序输出:
{ "_id" : { "$oid" : "538783623641e9b2da299fa7"} , "employeeId" : 5 , "employeeName" : "TestEmployee_5"}
5. MongoDB IN
子句示例
private static void in_Example(DBCollection collection)
{
BasicDBObject inQuery = new BasicDBObject();
List<Integer> list = new ArrayList<Integer>();
list.add(2);
list.add(4);
list.add(5);
inQuery.put("employeeId", new BasicDBObject("$in", list));
DBCursor cursor = collection.find(inQuery);
while(cursor.hasNext()) {
System.out.println(cursor.next());
}
}
程序输出:
{ "_id" : { "$oid" : "5387838d3641024de174c795"} , "employeeId" : 2 , "employeeName" : "TestEmployee_2"}
{ "_id" : { "$oid" : "5387838d3641024de174c797"} , "employeeId" : 4 , "employeeName" : "TestEmployee_4"}
{ "_id" : { "$oid" : "5387838d3641024de174c798"} , "employeeId" : 5 , "employeeName" : "TestEmployee_5"}
6. MongoDB 小于或大于子句示例
private static void lessThan_GreaterThan_Example(DBCollection collection)
{
BasicDBObject getQuery = new BasicDBObject();
getQuery.put("employeeId", new BasicDBObject("$gt", 2).append("$lt", 5));
DBCursor cursor = collection.find(getQuery);
while(cursor.hasNext()) {
System.out.println(cursor.next());
}
}
程序输出:
{ "_id" : { "$oid" : "538783b63641720cd34f98c8"} , "employeeId" : 3 , "employeeName" : "TestEmployee_3"}
{ "_id" : { "$oid" : "538783b63641720cd34f98c9"} , "employeeId" : 4 , "employeeName" : "TestEmployee_4"}
7. MongoDb NOT IN
子句示例
private static void negation_Example(DBCollection collection)
{
BasicDBObject neQuery = new BasicDBObject();
neQuery.put("employeeId", new BasicDBObject("$ne", 4));
DBCursor cursor = collection.find(neQuery);
while(cursor.hasNext()) {
System.out.println(cursor.next());
}
}
程序输出:
{ "_id" : { "$oid" : "538783db3641d3ca9d400510"} , "employeeId" : 1 , "employeeName" : "TestEmployee_1"}
{ "_id" : { "$oid" : "538783db3641d3ca9d400511"} , "employeeId" : 2 , "employeeName" : "TestEmployee_2"}
{ "_id" : { "$oid" : "538783db3641d3ca9d400512"} , "employeeId" : 3 , "employeeName" : "TestEmployee_3"}
{ "_id" : { "$oid" : "538783db3641d3ca9d400514"} , "employeeId" : 5 , "employeeName" : "TestEmployee_5"} //4 is missing
{ "_id" : { "$oid" : "538783db3641d3ca9d400515"} , "employeeId" : 6 , "employeeName" : "TestEmployee_6"}
{ "_id" : { "$oid" : "538783db3641d3ca9d400516"} , "employeeId" : 7 , "employeeName" : "TestEmployee_7"}
{ "_id" : { "$oid" : "538783db3641d3ca9d400517"} , "employeeId" : 8 , "employeeName" : "TestEmployee_8"}
{ "_id" : { "$oid" : "538783db3641d3ca9d400518"} , "employeeId" : 9 , "employeeName" : "TestEmployee_9"}
{ "_id" : { "$oid" : "538783db3641d3ca9d400519"} , "employeeId" : 10 , "employeeName" : "TestEmployee_10"}
8. MongoDB 查找匹配多个字段的文档示例
private static void andLogicalComparison_Example(DBCollection collection)
{
BasicDBObject andQuery = new BasicDBObject();
List<BasicDBObject> obj = new ArrayList<BasicDBObject>();
obj.add(new BasicDBObject("employeeId", 2));
obj.add(new BasicDBObject("employeeName", "TestEmployee_2"));
andQuery.put("$and", obj);
System.out.println(andQuery.toString());
DBCursor cursor = collection.find(andQuery);
while (cursor.hasNext()) {
System.out.println(cursor.next());
}
}
程序输出:
{ "$and" : [ { "employeeId" : 2} , { "employeeName" : "TestEmployee_2"}]}
{ "_id" : { "$oid" : "5387840336418a41167caaa4"} , "employeeId" : 2 , "employeeName" : "TestEmployee_2"}
9. MongoDb 查找匹配REGEX
的文档示例
private static void regex_Example(DBCollection collection) {
BasicDBObject regexQuery = new BasicDBObject();
regexQuery.put("employeeName",
new BasicDBObject("$regex", "TestEmployee_[3]")
.append("$options", "i"));
System.out.println(regexQuery.toString());
DBCursor cursor = collection.find(regexQuery);
while (cursor.hasNext()) {
System.out.println(cursor.next());
}
}
程序输出:
{ "employeeName" : { "$regex" : "TestEmployee_[3]" , "$options" : "i"}}
{ "_id" : { "$oid" : "538784213641917ce7068c57"} , "employeeId" : 3 , "employeeName" : "TestEmployee_3"}
10. 所有 mondodb 查找查询示例
package examples.mongodb.crud;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.MongoClient;
import com.mongodb.WriteResult;
public class MongoDBSelectExample {
private static void setUpTestData(DBCollection collection){
for (int i=1; i <= 10; i++) {
collection.insert(new BasicDBObject().append("employeeId", i).append("employeeName", "TestEmployee_"+i));
}
}
public static void main(String[] args) throws UnknownHostException
{
MongoClient mongo = new MongoClient("localhost", 27017);
DB db = mongo.getDB("howtodoinjava");
DBCollection collection = db.getCollection("users");
//Delete All documents before running example again
WriteResult result = collection.remove(new BasicDBObject());
System.out.println(result.toString());
//Set up test data
setUpTestData(collection);
//Select all document from a collection
selectAllRecordsFromACollection(collection);
//Select first document in collection
selectFirstRecordInCollection(collection);
//Select single document and single field based on record number
selectSingleRecordAndFieldByRecordNumber(collection);
//Select all documents where record number = n
selectAllRecordByRecordNumber(collection);
//In example
in_Example(collection);
//Less than OR greater than example
lessThan_GreaterThan_Example(collection);
//Select document where record number != n
negation_Example(collection);
//And logical comparison query example
andLogicalComparison_Example(collection);
//Select documents based on regex match LIKE example
regex_Example(collection);
}
private static void selectFirstRecordInCollection(DBCollection collection) {
DBObject dbObject = collection.findOne();
System.out.println(dbObject);
}
private static void selectAllRecordsFromACollection(DBCollection collection) {
DBCursor cursor = collection.find();
while(cursor.hasNext()) {
System.out.println(cursor.next());
}
}
private static void selectSingleRecordAndFieldByRecordNumber(DBCollection collection) {
BasicDBObject whereQuery = new BasicDBObject();
whereQuery.put("employeeId", 5);
BasicDBObject fields = new BasicDBObject();
fields.put("employeeId", 1);
DBCursor cursor = collection.find(whereQuery, fields);
while (cursor.hasNext()) {
System.out.println(cursor.next());
}
}
private static void selectAllRecordByRecordNumber(DBCollection collection) {
BasicDBObject whereQuery = new BasicDBObject();
whereQuery.put("employeeId", 5);
DBCursor cursor = collection.find(whereQuery);
while(cursor.hasNext()) {
System.out.println(cursor.next());
}
}
private static void in_Example(DBCollection collection) {
BasicDBObject inQuery = new BasicDBObject();
List<Integer> list = new ArrayList<Integer>();
list.add(2);
list.add(4);
list.add(5);
inQuery.put("employeeId", new BasicDBObject("$in", list));
DBCursor cursor = collection.find(inQuery);
while(cursor.hasNext()) {
System.out.println(cursor.next());
}
}
private static void lessThan_GreaterThan_Example(
DBCollection collection) {
BasicDBObject gtQuery = new BasicDBObject();
gtQuery.put("employeeId", new BasicDBObject("$gt", 2).append("$lt", 5));
DBCursor cursor = collection.find(gtQuery);
while(cursor.hasNext()) {
System.out.println(cursor.next());
}
}
private static void negation_Example(DBCollection collection) {
BasicDBObject neQuery = new BasicDBObject();
neQuery.put("employeeId", new BasicDBObject("$ne", 4));
DBCursor cursor = collection.find(neQuery);
while(cursor.hasNext()) {
System.out.println(cursor.next());
}
}
private static void andLogicalComparison_Example(DBCollection collection) {
BasicDBObject andQuery = new BasicDBObject();
List<BasicDBObject> obj = new ArrayList<BasicDBObject>();
obj.add(new BasicDBObject("employeeId", 2));
obj.add(new BasicDBObject("employeeName", "TestEmployee_2"));
andQuery.put("$and", obj);
System.out.println(andQuery.toString());
DBCursor cursor = collection.find(andQuery);
while (cursor.hasNext()) {
System.out.println(cursor.next());
}
}
private static void regex_Example(DBCollection collection) {
BasicDBObject regexQuery = new BasicDBObject();
regexQuery.put("employeeName",
new BasicDBObject("$regex", "TestEmployee_[3]")
.append("$options", "i"));
System.out.println(regexQuery.toString());
DBCursor cursor = collection.find(regexQuery);
while (cursor.hasNext()) {
System.out.println(cursor.next());
}
}
}
所有这些都是通过从 MongoDB 中获取文件的不同技术。
学习愉快!
参考文献:
MongoDB find()
文档
微服务 – 定义,原理和好处
原文: https://howtodoinjava.com/microservices/microservices-definition-principles-benefits/
微服务是业界最新的流行语,似乎每个人都以一种或另一种方式谈论它。 让我们了解什么是微服务? 在本教程中,我们将尝试了解微服务的定义,概念和原理。
Table of Contents
1\. Definition of Microservices
2\. Principles of Microservices
3\. Benefits of Microservices
4\. Conclusion
1. 微服务的定义
如今,微服务是继 SOA(面向服务的架构)之后越来越流行的架构模式之一。 如果您遵循行业趋势,那么您会意识到,如今的企业不再像几年前那样对开发大型应用来管理其端到端业务功能感兴趣,而是选择了快速而敏捷的应用,而这些应用的成本很高 他们的钱也减少了。
微服务有助于打破大型应用的界限,并在系统内部构建逻辑上独立的小型系统。 例如。 使用 Amazon AWS,您可以毫不费力地构建云应用。 这是微服务可以做什么的一个很好的例子。
整体与微服务架构
如上图所示,每个微服务都有其自己的业务层和数据库。 这样,对一种微服务的更改不会影响其他微服务。
通常,微服务使用广泛采用的轻量协议(例如 HTTP 和 REST)或消息传递协议(例如 JMS 或 AMQP)相互通信。 在特定情况下,它们也可以使用更专业的协议。
2. 微服务的原理
现在,让我们研究微服务的“必备”原则。
-
单一责任原则
单一责任原则是定义为 SOLID 设计模式一部分的原则之一。 这意味着一个单元(一个类,一个函数或一个微服务)应该只承担一个责任。
在任何时候,一项微服务都不应承担一项以上的责任。
-
围绕业务功能构建
微服务应专注于某些业务功能,并确保其有助于完成任务。 微服务绝不能限制自己采用最适合解决业务目的的适当技术栈或后端数据库存储。
当我们设计整体应用时,这通常是约束条件,在这些应用中,我们试图解决多个业务解决方案并在某些方面做出一些妥协。 微服务可让您选择最适合当前问题的方法。
-
您创建它,就拥有它!
这种设计的另一个重要方面与职责的前后发展有关。 在大型组织中,通常会有一个团队来开发应用位置,并且在进行一些知识转移之后,会将项目移交给维护团队。 在微服务中,构建服务的团队拥有它,并负责将来维护它。
您构建它,就拥有它!
这种所有权使开发人员可以接触到其软件的日常操作,并且他们可以更好地了解现实世界中客户如何使用其内置产品。
-
基础设施自动化
为微服务准备和构建基础架构是另一个非常重要的需求。 服务应是可独立部署的,并且应捆绑所有依赖项,包括库依赖项,甚至捆绑抽象物理资源的执行环境,例如 Web 服务器和容器或虚拟机。
微服务和 SOA 之间的主要差异之一在于它们的自治程度。 虽然大多数 SOA 实现都提供服务级别的抽象,但是微服务却走得更远,并且抽象了实现和执行环境。
在传统的应用开发中,我们先构建一个 WAR 或 EAR,然后将其部署到 JEE 应用服务器中,例如使用 JBoss,WebLogic,WebSphere 等。 我们可能将多个应用部署到同一个 JEE 容器中。 在理想情况下,在微服务方法中,每个微服务将构建为胖 JAR,嵌入所有依赖项并作为独立的 Java 进程运行。
-
失败的设计
微服务的设计应考虑到故障情况。 如果服务失败或停顿了一段时间该怎么办。 这些是非常重要的问题,必须在实际编码开始之前解决 - 清楚地估计服务故障将如何影响用户体验。
快速故障是用于构建容错,弹性系统的另一个概念。 这种哲学提倡的系统应该是失败的,而不是构建永远不会失败的系统。 由于服务随时可能发生故障,因此重要的是能够迅速检测到故障,并在可能的情况下自动恢复服务。
微服务应用非常重视应用的实时监视,同时检查架构元素(数据库每秒收到多少请求)和业务相关指标(例如每分钟有多少订单) 收到)。 语义监视可以为发生错误的情况提供预警系统,从而触发开发团队进行跟进和调查。
3. 微服务的好处
与传统的多层单片架构相比,微服务提供了许多优势。 让我们列出它们:
- 借助微服务,架构师和开发人员可以为每种微服务选择适用于特定架构和技术的(多语言架构)。 这样可以灵活地以更具成本效益的方式设计更适合的解决方案。
- 由于服务相当简单且规模较小,因此企业可以承受尝试新流程,算法,业务逻辑等的费用。 它通过提供实验和快速失败的能力,使企业能够进行颠覆性创新。
- 微服务能够实现选择性可伸缩性,即每个服务都可以独立地按比例放大或缩小,并且按比例缩放的成本要比单片方法小。
- 微服务是自包含的独立部署模块,当第二个微服务未按我们的需要运行时,它可以用另一个类似的微服务代替。 它有助于做出正确的买进建造决策,而这通常是许多企业所面临的挑战。
- 微服务可帮助我们构建本质上是有机的系统(有机系统是通过向其添加越来越多的功能而在一段时间内横向增长的系统)。 因为微服务都是关于独立可管理的服务的,所以它可以根据需要添加越来越多的服务,而对现有服务的影响却很小。
- 技术变革是软件开发的障碍之一。 使用微服务,可以单独为每个服务更改或升级技术,而不是升级整个应用。
- 由于微服务将服务运行时环境与服务本身打包在一起,因此可以在同一环境中共存多个版本的服务。
- 最后,微服务还使小型,专注的敏捷团队得以开发。 将基于微服务的边界组织团队。
4 总结
通常,您做出架构决策的真正后果只有在您做出决策后的几年才会显现出来。
在本文中,我仅列举了一些关于微服务的正面评价,而这些知识在我有限的知识中已在许多组织中看到。 具有强大设计和出色编码人员支持的单片应用也可以证明是一个不错的决定,并且产品可以保留足够长的时间来支持该决定。
与微服务类似,糟糕的设计决策将被证明是昂贵的。 它们似乎在简化组件,但它们可能会增加它们之间的通信复杂性,并且更难控制和管理。
最后,好的设计和熟练的团队将为您带来胜利。 技术水平低下的团队总是会创建一个糟糕的系统,很难说微服务在这种情况下是减少混乱还是使其变得更糟。
我建议您从整体应用设计开始,并且当您觉得它使系统变得复杂时,请尝试使用微服务,以检查它们是否使应用变得不太复杂。 这样,您将有一个更明智的决定。
学习愉快!
资源:
Martin Fowler 关于微服务
Apache Kafka 教程
Maven – 删除所有损坏的 jar/依赖项
原文: https://howtodoinjava.com/maven/remove-all-corrupt-jars/
通过从仓库中删除所有损坏的 jar 来学习重建本地仓库,并修复丢失或损坏的 Maven 依赖项的问题。
该解决方案将在需要强制更新 Maven 项目或下载缺少 Maven 依赖项的情况下为您提供帮助。
1. 方法
在 maven 中,您将观察到大多数依赖项都由于损坏的 jar 下载而损坏。 这种中断留下了一个扩展名为".lastUpdated"
的特殊文件。
在此解决方案中,我们创建了一个 Windows 批处理文件(请为 mac/linux 系统编写类似的脚本),该文件将执行以下操作:
- 查找所有包含任何扩展名为
'.lastUpdated'
的文件的文件夹。 - 强制删除文件夹及其内容。
- 记录文件夹位置以供参考。
2. 删除 Maven 损坏的 jar 的批处理文件
以下是执行上述部分列出的所有步骤的批处理文件。
:: Usage
:: C:/M2> delete-corrupt-dependencies.bat > files.txt
@echo off
setlocal EnableDelayedExpansion
set last=?
for /f %%I in ('dir /s /b /o:n /a-d "*.lastUpdated"') do (
if !last! NEQ %%~dpI (
set last=%%~dpI
echo !last!
rd /s /q !last!
)
)
goto end
:end
3. 用法
将其放置在 maven 的 M2 文件夹中 Windows 批处理文件上方。
现在打开命令提示符并运行批处理文件。 您可以在文件中捕获文件的输出(已删除损坏的依赖项列表)。
$ delete-corrupt-dependencies.bat > files.txt
上述批处理文件完成处理后,您可以将丢失的 Maven 依赖项列表(已清除)检查到当前目录中生成的files.txt
文件中。
C:\M2\commons-beanutils\commons-beanutils\1.9.3
C:\M2\io\netty\netty-buffer\4.1.39.Final
...
...
...
and so on
请把关于 maven 本地仓库问题的问题以及上面提供的解决方案与我联系。
学习愉快!
Apache Kafka – 简介
原文: https://howtodoinjava.com/kafka/tutorial-introduction/
从头开始学习下载和设置 Apache Kafka 和内置的 zookeeper。 本教程适用于绝对的初学者,他们可以在长期学习 Kafka 的同时为他们提供一些技巧。
1. 什么是 Apache Kafka?
Apache Kafka 是基于发布/订阅消息传递系统的分布式流平台。 Kafka 使用的另一个术语是“分布式提交日志”。
就像我们将事务性数据存储在数据库中一样,以便以后可以检索它以做出一些业务决策时,Kafka 还以消息形式存储数据。 Kafka 中的数据按顺序持久存储,并可以确定性地读取。
Kafka 的主要特征是其伸缩能力以及使用数据复制来防止故障的能力。
1.1 信息
Kafka 中数据单位称为消息。 将此视为数据库表中的一行。
按摩有两个部分 – 键和主体。 两者都只是一个字节数组,Kafka 并没有做任何神奇的读取和理解这些字节的事情。 它可以是 XML,JSON,字符串或其他任何形式。 许多 Kafka 开发人员赞成使用 Apache Avro ,这是最初为 Hadoop 开发的序列化框架。 Kafka 不在乎并存储所有内容。
键用于以更可控的方式将消息写入分区。 Kafka 只是简单地找到键的哈希值,并使用它来查找必须在其中写入消息的分区号(逻辑并不是那么简单,当然)。
这样可以确保始终将具有相同键的消息写入同一分区。
1.2 批量
批量只是消息的集合,所有消息都针对相同的主题和分区生成。
邮件以批量的形式在网络中移动。 这样做是为了提高网络利用率。
批量通常也被压缩,以一些处理能力为代价提供更有效的数据传输和存储。
1.3 主题
Kafka 主题与数据库表或文件系统中的文件夹非常相似。 主题还分为多个分区。
例如,考虑我们有一个名为“activity-log
”的主题,该主题有 3 个分区,它们的名称分别为:
activity-log-0
activity-log-1
activity-log-2
当源系统将消息发送到activity-log
主题时,可以基于负载和各种其他因素将这些消息(1-n)存储在任一分区中。
此处,message-1
仅存储在一个分区中。 同样,message-2
将存储在相同或另一个分区中。 没有消息将存储在给定主题的多个分区中。
请注意,如果所有消息之间都有顺序,则 Kafka 仅确保将消息顺序存储在单个分区中。 无法保证存储在多个分区中的所有消息的顺序。
流被视为存储特定类别数据的单个主题,而不管其分区数如何。 与其他系统一起使用时,Kafka 将此主题(例如activity-log
)显示为这些消息流的生产者或消费者。
1.4 Broker 和集群
一台 Kafka 服务器称为 Broker。 Broker 从生产者客户端接收消息,分配和维护其偏移量,并将消息存储在存储系统中。
它还为用户提供服务,响应对分区的提取请求并响应已提交到磁盘的消息。
如果硬件支持良好,则单个 Broker 可以轻松处理每秒数千个分区和数百万条消息。
KafkaBroker 旨在作为集群的一部分进行运营。 在 Broker 群集中,一个 Broker 还将充当群集控制器,该控制器负责管理操作,包括为 Broker 分配分区并监视 Broker 故障。
2. 优势
-
Kafka 能够提供高吞吐量,同时处理多个生产者,它们向单个主题或多个主题发送数据集。 这使得 Kafka 可以处理来自前端系统的大量事件/消息,这些事件/消息记录了页面浏览,鼠标跟踪或用户行为。
-
Kafka 多个用户读取任何单个消息流而不会互相干扰。 每个消息可以读取 N 次,因为消息是持久的。
-
持久的消息还意味着消费者可以处理“历史数据”。 不过,Kafka 也支持“实时处理”。
这也意味着,如果某些消费者在一段时间内处于脱机状态,则他们不会丢失任何数据并在重新连接时获取数据。
-
Kafka 具有高度的可伸缩性,可以在运行时添加或删除博克(节点)。 无需停止集群。
-
Kafka 提供出色的性能,并且能够在支持的硬件或基础架构中每秒处理数百万条记录。
3. 总结
这篇文章主要是对 Kafka 的外观进行非常高级别的概述。 当涉及到更详细的细节时,您将阅读很多项目。
在下一篇文章中,我们将学习下载并启动 Kafka Broker,并将运行一些初学者命令。
学习愉快!
参考文献:Kafka 网站
Apache Kafka – Windows 10 入门
原文: https://howtodoinjava.com/kafka/getting-started-windows-10/
学习在 Windows 10 上安装 Apache Kafka 并执行与 Kafka 和 Zookeeper 相关的启动服务器和停止服务器脚本。 我们还将通过创建主题,向其生成少量消息,然后使用用户读取用 Kafka 编写的消息来验证 Kafka 的安装。
1. 先决条件
- 需要 Java8 才能从 Kafka 网站运行最新下载。
- Zookeeper (用于存储有关 Kafka 群集的元数据)也是必需的。 Kafka 首先内置了 Zookeeper。 但是建议在生产环境中单独安装 Zookeeper。 从其官方网站下载它。
- Kafka 可以在任何操作系统上运行。 推荐使用 linux 操作系统。 使用 Windows,Kafka 有一些已知的错误。
本教程适用于初学者,并且不使用单独的 zookeeper 实例 - 保持简单,仅专注于 Kafka。
2. 下载并安装 Kafka
-
从官方网站下载 Kafka。 我今天下载了最新版本 2.5.0,文件名为“
kafka_2.12-2.5.0.tgz
”。 -
将下载的文件复制到某个文件夹,然后使用
tar
命令将其解压缩。> tar -xzf kafka_2.12-2.5.0.tgz
-
将提取的文件夹复制到所需位置。 我把它放在位置“
E:\devsetup\bigdata\kafka2.5
”中。安装几乎完成了!
3. 启动和关闭
要启动 Kafka,我们需要先启动 Zookeeper,然后再启动 Kafka。 我正在编写小的批处理文件,这些文件首先移至 Kafka 安装目录,然后在新的命令提示符窗口中执行命令。
3.1 启动 Zookeeper
要启动 Zookeeper,我们需要运行zookeeper-server-start.bat
脚本并传递 zookeeper 配置文件路径。
cd E:\devsetup\bigdata\kafka2.5
start cmd /k bin\windows\zookeeper-server-start.bat config\zookeeper.properties
3.2 启动 Kafka
要启动 Kafka,我们需要运行kafka-server-start.bat
脚本并传递 Broker 配置文件路径。
cd E:\devsetup\bigdata\kafka2.5
start cmd /k bin\windows\kafka-server-start.bat config\server.properties
3.3 关闭 Kafka
要停止 Kafka,我们需要运行kafka-server-stop.bat
脚本。
cd E:\devsetup\bigdata\kafka2.5
start cmd /k bin\windows\kafka-server-stop.bat
3.4 关闭 Zookeeper
要停止 Zookeeper,我们需要运行zookeeper-server-stop.bat
脚本。
cd E:\devsetup\bigdata\kafka2.5
start cmd /k bin\windows\zookeeper-server-stop.bat
不要使用
CTRL+C
命令停止 zookeeper 和 kafka。 始终使用上面的.bat
文件或命令。 否则可能会发生数据损坏。
4. 验证 Kafka 安装
首先,使用上述脚本启动 Zookeeper 和 Kafka。
-
打开一个新的命令提示符,然后创建一个新的 Kafka 主题。
> bin\windows\kafka-topics.bat --create --bootstrap-server localhost:9092 --replication-factor 1 --partitions 1 --topic test //Output: Created topic test.
-
现在列出所有主题以验证创建的主题是否存在于此列表中。 在这一步,我们只有一个主题。
> bin\windows\kafka-topics.bat --list --bootstrap-server localhost:9092 //Output: test
-
现在列出所有主题,以验证创建的主题是否存在于此列表中。 在这一步,我们只有一个主题。
> bin\windows\kafka-topics.bat --list --bootstrap-server localhost:9092 //Output: test
-
产生一些消息并提交给
test
主题。 我添加了两个消息,即“Hello
”和“Kafka
”。> bin\windows\kafka-console-producer.bat --bootstrap-server localhost:9092 --topic test
-
使用消息并提交到
test
主题。> bin\windows\kafka-console-consumer.bat --bootstrap-server localhost:9092 --topic test --from-beginning //Output: Hello Kafka
验证 Kafka 安装
5. 总结
在本教程中,我们学习了与 Zookeeper 一起安装 Kafka。 我们学会了启动和停止这两个服务器。 此外,我们通过创建主题,发布一些消息然后使用控制台用户脚本进行使用来验证安装。
需要注意的重要一点是,我们永远不要通过杀死进程或CTRL+C
命令来停止服务器。 始终使用脚本来停止服务器。
学习愉快!
参考:
源码下载
Kafka 的 Spring Boot – HelloWorld 示例
原文: https://howtodoinjava.com/kafka/spring-boot-with-kafka/
学习创建一个 SpringBoot 应用,该应用能够连接给定的 Apache Kafka Borker 实例。 另外,从 Kafka 主题中学习产生和消费消息。
我们将遵循的步骤:
- 创建具有 Kafka 依赖项的 Spring Boot 应用
- 在
application.yaml
中配置 kafka Borker 实例 - 使用
KafkaTemplate
将消息发送到主题 - 使用
@KafkaListener
实时收听发送到主题的消息
1. 先决条件
- 请按照本指南在您的机器上设置 Kafka 。
- 我们正在创建一个基于 Maven 的 Spring 引导应用,因此您的计算机应至少安装 Java8 和 Maven 。
2. Spring Boot 应用
打开 spring 初始化器并创建具有以下依赖项的 spring boot 应用:
- Spring Apache Kafka
- Spring Web
创建 Spring boot kafka 应用
生成的项目在pom.xml
中具有以下依赖项。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
在首选的 IDE 中导入项目。
3. 配置 Kafka Borker
在application.yaml
文件中,添加 kafka Borker 地址以及与消费者和生产者相关的配置。
server:
port: 9000
spring:
kafka:
consumer:
bootstrap-servers: localhost:9092
group-id: group-id
auto-offset-reset: earliest
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
producer:
bootstrap-servers: localhost:9092
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
4. KafKaProducerService
和KafKaConsumerService
KafKaProducerService
类使用自动连接的KafkaTemplate
将消息发送到已配置的主题名称。 同样,KafKaConsumerService
类使用@KafkaListener
来接收来自已配置主题名称的消息。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
import com.howtodoinjava.kafka.demo.common.AppConstants;
@Service
public class KafKaProducerService
{
private static final Logger logger =
LoggerFactory.getLogger(KafKaProducerService.class);
@Autowired
private KafkaTemplate<String, String> kafkaTemplate;
public void sendMessage(String message)
{
logger.info(String.format("Message sent -> %s", message));
this.kafkaTemplate.send(AppConstants.TOPIC_NAME, message);
}
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Service;
import com.howtodoinjava.kafka.demo.common.AppConstants;
@Service
public class KafKaConsumerService
{
private final Logger logger =
LoggerFactory.getLogger(KafKaConsumerService.class);
@KafkaListener(topics = AppConstants.TOPIC_NAME,
groupId = AppConstants.GROUP_ID)
public void consume(String message)
{
logger.info(String.format("Message recieved -> %s", message));
}
}
public class AppConstants
{
public static final String TOPIC_NAME = "test";
public static final String GROUP_ID = "group_id";
}
5. 控制器
控制器负责使用 REST API 从用户获取消息,并将消息移交给生产者服务以将其发布到 kafka 主题。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.howtodoinjava.kafka.demo.service.KafKaProducerService;
@RestController
@RequestMapping(value = "/kafka")
public class KafkaProducerController
{
private final KafKaProducerService producerService;
@Autowired
public KafkaProducerController(KafKaProducerService producerService)
{
this.producerService = producerService;
}
@PostMapping(value = "/publish")
public void sendMessageToKafkaTopic(@RequestParam("message") String message)
{
this.producerService.sendMessage(message);
}
}
6. 测试
使用任何 REST API 测试器并将少量消息发布到查询参数"message"
中的 API http://localhost:9000/kafka/publish
。
留言栏:http://localhost:9000/kafka/publish?message=Alphabet
观察控制台日志:
2020-05-24 23:36:47.132 INFO 2092 --- [nio-9000-exec-4]
c.h.k.demo.service.KafKaProducerService : Message sent -> Alphabet
2020-05-24 23:36:47.138 INFO 2092 --- [ntainer#0-0-C-1]
c.h.k.demo.service.KafKaConsumerService : Message recieved -> Alphabet
如果您已经在命令提示符下打开了 Kafka 控制台用户,那么您也会看到该消息。
Kafka 控制台消费者
7. 总结
在 spring boot kafka 教程中,我们学习了如何创建 spring boot 应用和配置 Kafka 服务器。 另外,我们通过使用KafkaTemplate
发布一些消息,然后使用@KafkaListener
使用这些消息来验证该应用。
学习愉快!
源码下载
Spring Boot Kafka JsonSerializer
示例
原文: https://howtodoinjava.com/kafka/spring-boot-jsonserializer-example/
学习使用JsonSerializer
和JsonDeserializer
类从 Apache Kafka 主题存储和检索 JSON,并返回 Java 模型对象。
1. 先决条件
- 请按照本指南在您的机器上设置 Kafka 。
- 我们正在修改 Spring boot 和 Kafka HelloWorld 应用。
- 还要确保您的计算机上至少安装了 Java8 和 Maven 。
2. 应用配置
在application.properties
文件中,我们添加了以下配置。
server.port=9000
spring.kafka.consumer.bootstrap-servers: localhost:9092
spring.kafka.consumer.group-id: group-id
spring.kafka.consumer.auto-offset-reset: earliest
spring.kafka.consumer.key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
spring.kafka.consumer.value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer
spring.kafka.consumer.properties.spring.json.trusted.packages=*
spring.kafka.producer.bootstrap-servers: localhost:9092
spring.kafka.producer.key-serializer: org.apache.kafka.common.serialization.StringSerializer
spring.kafka.producer.value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
spring.kafka.consumer.key-deserializer
指定键的反序列化器类。spring.kafka.consumer.value-deserializer
指定值的反序列化器类。spring.kafka.consumer.properties.spring.json.trusted.packages
指定允许反序列化的程序包模式的逗号分隔列表。'*'
表示反序列化所有程序包。spring.kafka.producer.key-deserializer
指定键的序列化器类。spring.kafka.producer.value-deserializer
指定值的序列化器类。
3. 模型类
我们创建了User
类,该类将发送给 Kafka。 其实例将由JsonSerializer
序列化为字节数组。 这个字节数组最终由 Kafka 存储到给定的分区中。
反序列化期间,JsonDeserializer
用于以字节数组的形式从 Kafka 接收 JSON,并将User
对象返回给应用。
public class User
{
private long userId;
private String firstName;
private String lastName;
public long getUserId() {
return userId;
}
public void setUserId(long userId) {
this.userId = userId;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
@Override
public String toString() {
return "User [userId=" + userId + ", firstName="
+ firstName + ", lastName=" + lastName + "]";
}
}
4. Kafka 生产者
生产者 API 只是在 HTTP POST API 中使用用户信息。 然后,它创建一个新的User
对象,并使用KafkaTemplate
发送给 Kafka。
@PostMapping(value = "/createUser")
public void sendMessageToKafkaTopic(
@RequestParam("userId") long userId,
@RequestParam("firstName") String firstName,
@RequestParam("lastName") String lastName) {
User user = new User();
user.setUserId(userId);
user.setFirstName(firstName);
user.setLastName(lastName);
this.producerService.saveCreateUserLog(user);
}
@Autowired
private KafkaTemplate<String, Object> kafkaTemplate;
public void saveCreateUserLog(User user)
{
logger.info(String.format("User created -> %s", user));
this.kafkaTemplate.send(AppConstants.TOPIC_NAME_USER_LOG, user);
}
5. Kafka 消费者
用户被实现为@KafkaListener
,每次在主题中添加新条目时都会得到通知。
@KafkaListener(topics = AppConstants.TOPIC_NAME_USER_LOG,
groupId = AppConstants.GROUP_ID)
public void consume(User user)
{
logger.info(String.format("User created -> %s", user));
}
6. 测试
使用任何 REST API 测试器,并向 API http://localhost:9000/kafka/createUser
发送一些消息,如下所示。
留言栏:http://localhost:9000/kafka/createUser?userId=1&firstName=Lokesh&lastName=Gupta
观察控制台日志:
2020-05-24 23:36:47.132 INFO 2092 --- [nio-9000-exec-4]
2020-05-26 01:03:52.722 INFO 11924 --- [nio-9000-exec-6] c.h.k.demo.service.KafKaProducerService
: User created -> User [userId=1, firstName=Lokesh, lastName=Gupta]
2020-05-26 01:03:52.729 INFO 11924 --- [ntainer#1-0-C-1] c.h.k.demo.service.KafKaConsumerService
: User created -> User [userId=1, firstName=Lokesh, lastName=Gupta]
7. 总结
在此 SpringBoot kafka JsonSerializer
示例中,我们学习了使用JsonSerializer
对 Java 对象进行序列化和反序列化并存储在 Kafka 中。
学习愉快!
源码下载
如何在 Windows 上安装 Maven
原文: https://howtodoinjava.com/maven/how-to-install-maven-on-windows/
Maven 是用于基于 Java 的应用开发的构建和依赖项管理工具。 与其他基于 Java 的开发工具一样,它不会作为 Windows 服务安装,而是使用 Windows 环境变量进行配置。
在此 maven 教程中,我将在 Windows 7 32 位机器上安装 maven。
Windows 环境变量位置:
控制面板 -> 所有控制面板项目 -> 系统 -> 高级系统设置 -> 环境变量
请按照在 Windows 操作系统上安装 Maven 所需的步骤进行操作。
第 1 步)安装 JDK 并添加“JAVA_HOME
”环境变量
要安装 Java,请下载 JDK 安装程序和将JAVA_HOME
变量添加/更新到 JDK 安装文件夹。
第 2 步)下载 Maven 并添加MAVEN_HOME
和M2_HOME
环境变量
可以从此位置下载 Maven。 我已将其提取到位置 – D:/Latest Setup/apache-maven-3.0.4
。 您可以选择自己的位置。
将M2_HOME
和MAVEN_HOME
变量设置为 maven 安装文件夹。
第 3 步)在PATH
变量中包含maven/bin
目录
要从命令提示符运行 maven,这是必需的。 用'Maven-installation/bin' directory
更新PATH
变量。
步骤 4)在控制台中验证 Maven
Maven 安装完成。 现在让我们从 Windows 命令提示符下对其进行测试。
- 转到开始菜单,然后在应用位置搜索框中键入
cmd
。 - 按
ENTER
。 将打开一个新的命令提示符。 - 在命令提示符下键入
mvn -version
并点击ENTER
。
$ mvn -version
这应该显示已安装的 maven 的版本信息。 如果显示任何错误,请交叉检查上述所有步骤。
在本教程中,我们学习了如何在 Windows 上安装 maven。
学习愉快!
Gradle 教程 – 安装和 HelloWorld 示例
原文: https://howtodoinjava.com/gradle/gradle-tutorial-installation-and-hello-world-example/
Gradle 是类似于 Maven 或 Ant 的构建自动化工具,用于编写构建脚本以在任何环境中按需运行它们。 在本教程中,我们将了解 Gradle 与其他构建自动化工具的区别,然后我们将看到一个简单的示例来安装和使用 gradle。
Table of Contents
Gradle vs. other build tools
Installing Gradle
Writing simple Gradle script
Executing Gradle script
Gradle 与其他构建工具
如果您使用过 maven,则知道您不能从一个构建文件(pom.xml
)生成多个工件(jar 或 war 文件等)。 如果要从单个src
文件夹生成两个 jar 文件,则可能必须在 maven 中创建两个项目。 这是现有构建工具的困难之一。
多年来,其他构建工具已用于编译和打包软件。 但是随着不断变化的行业需求,项目涉及大量多样的软件栈,合并多种编程语言并应用广泛的测试策略。 随着敏捷实践的兴起,构建必须支持代码的早期集成以及频繁且轻松地交付给测试和生产环境。
其他工具不足以实现这些目标,而 Gradle 正是其中之一。 它借鉴了从既有工具中汲取的经验教训,并将其最佳思想带入了一个新的水平。
Gradle 管理依赖项的能力不仅限于外部库。 随着项目规模和复杂性的增长,您将需要将代码组织到职责明确的模块中。 Gradle 为定义和组织多项目构建以及对项目之间的依赖项建模提供了强大的支持。
Gradle 构建脚本具有声明性,可读性,并且清楚地表达了其意图。 用 Groovy 而不是 XML 编写代码,再加上 Gradle 的按惯例构建理念,大大减少了构建脚本的大小,并且可读性更高。
安装 Gradle
- 作为前提条件,请确保您已经安装了 JDK 6 或更高版本。
- Gradle 入门很容易。 您可以直接从 Gradle 主页上的 https://gradle.org/gradle-download/ 下载该发行版。 发行版附带源代码,二进制文件,文档和示例。
- 在某个位置将下载的 zip 文件解压缩到开发计算机中。 现在,将环境变量
GRADLE_HOME
设置为 gradle 目录。 - 将
GRADLE_HOME/bin
文件夹添加到PATH
环境变量。
您应该能够从命令行运行gradle -v
命令。 如果发生这种情况,并且您获得了输出,就可以在项目中使用 gradle 了。 上面命令的输出将如下所示:
------------------------------------------------------------
Gradle 2.14.1
------------------------------------------------------------
Build time: 2016-07-18 06:38:37 UTC
Revision: d9e2113d9fb05a5caabba61798bdb8dfdca83719
Groovy: 2.4.4
Ant: Apache Ant(TM) version 1.9.6 compiled on June 29 2015
JVM: 1.6.0_33 (Sun Microsystems Inc. 20.8-b03)
OS: Windows 7 6.1 x86
编写简单的 Gradle 脚本
现在,让我们编写一个简单的 gradle 脚本并尝试运行它。 我已经在桌面上创建了新文件夹,并以Playground
命名。 然后,我创建了build.gradle
并写下了 HelloWorld 脚本。
task helloWorld {
doLast {
println 'Hello world!'
}
}
上面的脚本是用 Gradle 的 DSL(自我描述性语言)编写的,例如,名为doLast
的动作几乎可以自我表达。 这是为任务执行的最后一个动作。
执行 Gradle 脚本
要运行脚本,请运行gradle
命令并传递任务名称。 输出将如下所示:
> gradle –q helloWorld
Hello world!
在–q
中使用安静选项时,您告诉 Gradle 仅输出任务的输出。
上面的用例本质上是非常基本的,并且其编写目的是使事情尽可能简单 — 因此您首先要熟悉最基本的东西。 在接下来的教程中,我们将学习 Gradle 的更高级功能。
学习愉快!
Log4j2 教程
Log4j2 JSON 配置示例
原文: https://howtodoinjava.com/log4j2/log4j-2-json-configuration-example/
Apache Log4j2 是对 Log4j 1.x 的升级,在性能上进行了重大改进,改进了配置文件的自动重装,Java8 lambda 支持和自定义日志级别,从而大大改进了 Log4j1.x。 除了 XML 和属性文件外,还可以使用 JSON 配置 Log4j。
Log4j2 依赖项
要在项目中包含 Log4j2 ,请在项目中包含以下依赖项。
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.6.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.6.1</version>
</dependency>
Log4j2 使用 Jackson 解析 JSON 文件 - 因此,我们还要添加它的依赖项。
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.7.4</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.7.4</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.7.4</version>
</dependency>
用于控制台记录的log4j2.json
您可以使用下面的src/main/resources/log4j2.json
文件将日志输出到控制台。 请注意,如果找不到配置文件,则将使用DefaultConfiguration
。 这也将日志输出到控制台。
{
"configuration": {
"status": "error",
"name": "JSONConfigDemo",
"packages": "com.howtodoinjava",
"ThresholdFilter": {
"level": "debug"
},
"appenders": {
"Console": {
"name": "STDOUT",
"PatternLayout": {
"pattern": "%d [%t] %-5p %c - %m%n"
}
}
},
"loggers": {
"root": {
"level": "debug",
"AppenderRef": {
"ref": "STDOUT"
}
}
}
}
}
用于滚动文件记录的log4j2.json
您可以使用下面的log4j2.json
文件将日志输出到基于大小的滚动文件中。
{
"configuration": {
"name": "Default",
"appenders": {
"RollingFile": {
"name":"File",
"fileName":"C:/logs/howtodoinjava.log",
"filePattern":"C:/logs/howtodoinjava-backup-%d{MM-dd-yy-HH-mm-ss}-%i.log.gz",
"PatternLayout": {
"pattern":"%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n"
},
"Policies": {
"SizeBasedTriggeringPolicy": {
"size":"10 MB"
}
},
"DefaultRolloverStrategy": {
"max":"10"
}
}
},
"loggers": {
"root": {
"level":"debug",
"appender-ref": {
"ref":"File"
}
}
}
}
}
log4j2.json
文件位置
您应该将log4j2.json
放在应用的类路径中的任何位置。 Log4j2 将扫描所有类路径位置以查找此文件,然后将其加载。
Log4j2.json
文件位置
log4j2.json
示例
让我们写一个 Java 类并写一些日志语句来验证日志是否也出现在控制台和日志文件中。
package com.howtodoinjava.log4j2.examples;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class Log4j2HelloWorldExample
{
private static final Logger LOGGER = LogManager.getLogger(Log4j2HelloWorldExample.class.getName());
public static void main(String[] args)
{
LOGGER.debug("Debug Message Logged !!!");
LOGGER.info("Info Message Logged !!!");
LOGGER.error("Error Message Logged !!!", new NullPointerException("NullError"));
}
}
现在,当您运行上述程序时,您将在控制台中获得以下日志。
2016-06-16 15:06:25 DEBUG Log4j2HelloWorldExample:12 - Debug Message Logged !!!
2016-06-16 15:06:25 INFO Log4j2HelloWorldExample:13 - Info Message Logged !!!
2016-06-16 15:06:25 ERROR Log4j2HelloWorldExample:14 - Error Message Logged !!!
java.lang.NullPointerException: NullError at com.howtodoinjava.log4j2.examples.Log4j2HelloWorldExample.main
(Log4j2HelloWorldExample.java:14) [classes/:?]
学习愉快!
Log4j2 属性文件示例
原文: https://howtodoinjava.com/log4j2/log4j2-properties-example/
学习配置log4j2.properties
文件,以将日志语句输出到控制台,滚动文件等。学习配置 log4j2 附加器,级别和模式。
Apache Log4j2 是 Log4j 1.x 的升级版,对它的前身进行了重大改进,例如性能提高,自动重新加载已修改的配置文件, Java8 lambda 支持和自定义 日志级别。
1. Log4j2 Maven 依赖项
要包含 Log4j2 ,请在项目中包含以下 maven 依赖项。
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.6.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.6.1</version>
</dependency>
2. log4j2.properties
– 控制台日志记录
我们可以使用下面的log4j2.properties
文件将日志输出到控制台。 请注意,如果找不到配置文件,则将使用DefaultConfiguration
。 Log4j2 默认日志记录还将日志输出到控制台。
status = error
name = PropertiesConfig
filters = threshold
filter.threshold.type = ThresholdFilter
filter.threshold.level = debug
appenders = console
appender.console.type = Console
appender.console.name = STDOUT
appender.console.layout.type = PatternLayout
appender.console.layout.pattern = %d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
rootLogger.level = debug
rootLogger.appenderRefs = stdout
rootLogger.appenderRef.stdout.ref = STDOUT
3. log4j2.properties
– 滚动文件附加器
我们可以使用下面的log4j2.properties
文件将日志输出到基于日期的滚动文件。
status = error
name = PropertiesConfig
#Make sure to change log file path as per your need
property.filename = C:\\logs\\debug.log
filters = threshold
filter.threshold.type = ThresholdFilter
filter.threshold.level = debug
appenders = rolling
appender.rolling.type = RollingFile
appender.rolling.name = RollingFile
appender.rolling.fileName = ${filename}
appender.rolling.filePattern = debug-backup-%d{MM-dd-yy-HH-mm-ss}-%i.log.gz
appender.rolling.layout.type = PatternLayout
appender.rolling.layout.pattern = %d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
appender.rolling.policies.type = Policies
appender.rolling.policies.time.type = TimeBasedTriggeringPolicy
appender.rolling.policies.time.interval = 1
appender.rolling.policies.time.modulate = true
appender.rolling.policies.size.type = SizeBasedTriggeringPolicy
appender.rolling.policies.size.size=10MB
appender.rolling.strategy.type = DefaultRolloverStrategy
appender.rolling.strategy.max = 20
loggers = rolling
#Make sure to change the package structure as per your application
logger.rolling.name = com.howtodoinjava
logger.rolling.level = debug
logger.rolling.additivity = false
logger.rolling.appenderRef.rolling.ref = RollingFile
4. log4j2.properties
文件路径
我们应该将log4j2.properties
放在应用的类路径中的任何位置。 Log4j2 将扫描所有类路径位置以查找此文件,然后将其加载。
Log4j2.properties
文件位置
5. log4j2 属性文件示例
让我们写一个 Java 类并写一些日志语句来验证日志是否也出现在控制台和日志文件中。
package com.howtodoinjava.log4j2.examples;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class Log4j2HelloWorldExample
{
private static final Logger LOGGER = LogManager.getLogger(Log4j2HelloWorldExample.class.getName());
public static void main(String[] args)
{
LOGGER.debug("Debug Message Logged !!!");
LOGGER.info("Info Message Logged !!!");
LOGGER.error("Error Message Logged !!!", new NullPointerException("NullError"));
}
}
现在,当您运行上述程序时,您将在控制台中获得以下日志。
2016-06-16 13:41:27 DEBUG Log4j2HelloWorldExample:12 - Debug Message Logged !!!
2016-06-16 13:41:27 INFO Log4j2HelloWorldExample:13 - Info Message Logged !!!
2016-06-16 13:41:27 ERROR Log4j2HelloWorldExample:14 - Error Message Logged !!!
java.lang.NullPointerException: NullError
at com.howtodoinjava.log4j2.examples.Log4j2HelloWorldExample.main(Log4j2HelloWorldExample.java:14)
[classes/:?]
学习愉快!
Log4j2 xml 配置示例
原文: https://howtodoinjava.com/log4j2/log4j-2-xml-configuration-example/
Apache Log4j2 是对 Log4j 1.x 的升级,与以前的版本相比有了显着改进,例如性能提高,自动重新加载已修改的配置文件, Java8 lambda 支持和自定义日志级别。
Log4j2.4 及更高版本需要 Java 7。 版本 2.0-alpha1 至 2.3 需要 Java 6。
1. log4j2 maven 依赖
要将 Log4j2 包含在您的项目中,请在项目中包含以下 maven 依赖项。
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.6.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.6.1</version>
</dependency>
2. 用于控制台日志log4j2.xml
您可以使用下面的log4j2.xml
将日志输出到控制台。 请注意,如果找不到配置文件,则将使用DefaultConfiguration
。 这也将日志输出到控制台。
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO">
<Appenders>
<Console name="console" target="SYSTEM_OUT">
<PatternLayout
pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n" />
</Console>
</Appenders>
<Loggers>
<Root level="debug" additivity="false">
<AppenderRef ref="console" />
</Root>
</Loggers>
</Configuration>
3. 用于滚动文件日志的log4j2.xml
您可以使用下面的log4j2.xml
文件将日志输出到基于日期的滚动文件 – 以及控制台。
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn">
<Properties>
<Property name="basePath">C:\\logs</Property>
</Properties>
<Appenders>
<RollingFile name="fileLogger" fileName="${basePath}/app-info.log" filePattern="${basePath}/app-info-%d{yyyy-MM-dd}.log">
<PatternLayout>
<pattern>[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n</pattern>
</PatternLayout>
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true" />
</Policies>
</RollingFile>
<Console name="console" target="SYSTEM_OUT">
<PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n" />
</Console>
</Appenders>
<Loggers>
<Logger name="com.howtodoinjava" level="debug" additivity="true">
<appender-ref ref="fileLogger" level="debug" />
</Logger>
<Root level="debug" additivity="false">
<appender-ref ref="console" />
</Root>
</Loggers>
</Configuration>
4. log4j2.xml
文件位置
您应该将log4j2.xml
放在应用的类路径中的任何位置。 Log4j 将扫描所有类路径位置以查找此文件,然后加载它。
Log4j2.xml
文件位置
5. log4j2.xml
示例
让我们编写一个 Java 类,并写一些日志语句,以验证控制台和日志文件中的日志是否也在增加。 它将不同的日志级别记录到不同的日志中
package com.howtodoinjava.log4j2.examples;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class Log4j2HelloWorldExample
{
private static final Logger LOGGER = LogManager.getLogger(Log4j2HelloWorldExample.class.getName());
public static void main(String[] args)
{
LOGGER.debug("Debug Message Logged !!!");
LOGGER.info("Info Message Logged !!!");
LOGGER.error("Error Message Logged !!!", new NullPointerException("NullError"));
}
}
现在,当您运行上述程序时,您将在控制台中获得以下日志。
[DEBUG] 2016-06-16 12:17:42.972 [main] Log4j2HelloWorldExample - Debug Message Logged !!!
[INFO ] 2016-06-16 12:17:42.996 [main] Log4j2HelloWorldExample - Info Message Logged !!!
[ERROR] 2016-06-16 12:17:42.997 [main] Log4j2HelloWorldExample - Error Message Logged !!!
java.lang.NullPointerException: NullError
at com.howtodoinjava.log4j2.examples.Log4j2HelloWorldExample.main(Log4j2HelloWorldExample.java:14) [classes/:?]
如果您更改系统日期并再次运行该应用,则会在配置的位置找到两个日志文件,即app-info.log
和app-info-2016-06-15.log
–第二个文件将在该文件上滚动。
6. 带有多文件附加器的log4j2.xml
使用此简单log4j2.xml
作为多个日志文件中的日志语句的快速参考。 它使用LevelRangeFilter
将不同级别的日志(debug
,info
等)记录到不同的文件中,以便您的日志干净且分开,以便于分析。
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" monitorInterval="30">
<!-- Logging Properties -->
<Properties>
<Property name="LOG_PATTERN">%d{yyyy-MM-dd'T'HH:mm:ss.SSSZ} %p %m%n</Property>
<Property name="APP_LOG_ROOT">c:/temp</Property>
</Properties>
<Appenders>
<!-- Console Appender -->
<Console name="Console" target="SYSTEM_OUT" follow="true">
<PatternLayout pattern="${LOG_PATTERN}"/>
</Console>
<!-- File Appenders on need basis -->
<RollingFile name="frameworkLog" fileName="${APP_LOG_ROOT}/app-framework.log"
filePattern="${APP_LOG_ROOT}/app-framework-%d{yyyy-MM-dd}-%i.log">
<LevelRangeFilter minLevel="ERROR" maxLevel="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<SizeBasedTriggeringPolicy size="19500KB" />
</Policies>
<DefaultRolloverStrategy max="10"/>
</RollingFile>
<RollingFile name="debugLog" fileName="${APP_LOG_ROOT}/app-debug.log"
filePattern="${APP_LOG_ROOT}/app-debug-%d{yyyy-MM-dd}-%i.log">
<LevelRangeFilter minLevel="DEBUG" maxLevel="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<SizeBasedTriggeringPolicy size="19500KB" />
</Policies>
<DefaultRolloverStrategy max="10"/>
</RollingFile>
<RollingFile name="infoLog" fileName="${APP_LOG_ROOT}/app-info.log"
filePattern="${APP_LOG_ROOT}/app-info-%d{yyyy-MM-dd}-%i.log" >
<LevelRangeFilter minLevel="INFO" maxLevel="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<SizeBasedTriggeringPolicy size="19500KB" />
</Policies>
<DefaultRolloverStrategy max="10"/>
</RollingFile>
<RollingFile name="errorLog" fileName="${APP_LOG_ROOT}/app-error.log"
filePattern="${APP_LOG_ROOT}/app-error-%d{yyyy-MM-dd}-%i.log" >
<LevelRangeFilter minLevel="ERROR" maxLevel="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<SizeBasedTriggeringPolicy size="19500KB" />
</Policies>
<DefaultRolloverStrategy max="10"/>
</RollingFile>
<RollingFile name="perfLog" fileName="${APP_LOG_ROOT}/app-perf.log"
filePattern="${APP_LOG_ROOT}/app-perf-%d{yyyy-MM-dd}-%i.log" >
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<SizeBasedTriggeringPolicy size="19500KB" />
</Policies>
<DefaultRolloverStrategy max="1"/>
</RollingFile>
<RollingFile name="traceLog" fileName="${APP_LOG_ROOT}/app-trace.log"
filePattern="${APP_LOG_ROOT}/app-trace-%d{yyyy-MM-dd}-%i.log" >
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<SizeBasedTriggeringPolicy size="19500KB" />
</Policies>
<DefaultRolloverStrategy max="1"/>
</RollingFile>
</Appenders>
<Loggers>
<Logger name="com.howtodoinjava.app.somePackage" additivity="false" level="trace">
<AppenderRef ref="traceLog" />
<AppenderRef ref="Console" />
</Logger>
<Logger name="com.howtodoinjava.app" additivity="false" level="debug">
<AppenderRef ref="debugLog" />
<AppenderRef ref="infoLog" />
<AppenderRef ref="errorLog" />
<AppenderRef ref="Console" />
</Logger>
<Logger name="org.framework.package" additivity="false" level="info">
<AppenderRef ref="perfLog" />
<AppenderRef ref="Console"/>
</Logger>
<Root level="warn">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
学习愉快!
Log4j2 RollingFileAppender
示例
原文: https://howtodoinjava.com/log4j2/log4j2-rollingfileappender-example/
Log4j2 RollingFileAppender
是OutputStreamAppender
,它按照有关何时应进行滚动(备份)的已配置触发策略,将日志消息写入文件。 它还具有有关如何转换文件的已配置转换策略。
通常,日志文件的备份是根据文件大小和/或当前日期创建的。
1. Log4j2 Maven 依赖项
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.11.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.11.1</version>
</dependency>
在 maven 仓库中检查最新版本。
2. Log4j2 RollingFileAppender
示例 – 基于日志文件大小的滚动
此给定的配置根据日志文件大小滚动日志文件。 我已将日志文件大小配置为 10 MB。 根据您的要求进行更改。
2.1. log4j2.properties
我们可以按照给定的方式在log4j.properties
中配置滚动文件附加器。
name = PropertiesConfig
appender.rolling.type = RollingFile
appender.rolling.name = RollingFile
appender.rolling.fileName = ${LOG_DIR}/application.log
appender.rolling.filePattern = ${LOG_DIR}/application.%d{dd-MMM}.log.gz
appender.rolling.layout.type = PatternLayout
appender.rolling.layout.pattern = %d{yyyy-MM-dd HH:mm:ss} %-5p %m%n
appender.rolling.policies.type = Policies
appender.rolling.policies.size.type = SizeBasedTriggeringPolicy
appender.rolling.policies.size.size=10MB
appender.rolling.strategy.type = DefaultRolloverStrategy
appender.rolling.strategy.max = 5
logger.rolling.name = rollingFile
logger.rolling.level = debug
logger.rolling.additivity = false
logger.rolling.appenderRef.rolling.ref = RollingFile
2.2 log4j2.xml
<RollingFile
name="rollingFile"
fileName="${LOG_DIR}/application.log"
filePattern="${LOG_DIR}/application.%i.log.gz"
ignoreExceptions="false">
<PatternLayout>
<Pattern>%d{yyyy-MM-dd HH:mm:ss} %-5p %m%n</Pattern>
</PatternLayout>
<Policies>
<SizeBasedTriggeringPolicy size="10MB" />
</Policies>
<DefaultRolloverStrategy max="5" />
</RollingFile>
3. RollingFileAppender
– 基于日期时间的滚动
我们也可以根据日期时间滚动日志文件。
3.1 RollingFileAppender
示例
如果使用RollingFileAppender
,则使用TimeBasedRollingPolicy
来指定何时基于日期时间滚动日志文件。
注意FileNamePattern
属性。 它定义了滚动文件的名称模式。 在给定的示例中,它将在日志文件名中使用date-month
重命名滚动日志文件。
例如,模式'{MM-dd-yyyy-HH}'
将每小时滚动日志文件。
我们还使用.gz
扩展名,因此 log4j 将自动压缩日志文件。
<RollingFile
name="rollingFile"
fileName="${LOG_DIR}/application.log"
filePattern="${LOG_DIR}/application.%d{dd-MMM}.log.gz"
ignoreExceptions="false">
<PatternLayout>
<Pattern>%d{yyyy-MM-dd HH:mm:ss} %-5p %m%n</Pattern>
</PatternLayout>
<Policies>
<TimeBasedTriggeringPolicy filePattern="${LOG_DIR}/application.%d{dd-MMM-hh}.log.gz" />
</Policies>
<DefaultRolloverStrategy max="5" />
</RollingFile>
3.2 每日滚动日志示例
为了启用每日滚动,log4j2 没有DailyRollingFileAppender
,这是较早的 log4j 中存在的。 要每天滚动日志,请在TimeBasedTriggeringPolicy
中将时间间隔设置为 1。
<RollingFile
name="rollingFile"
fileName="${LOG_DIR}/application.log"
ignoreExceptions="false">
<PatternLayout>
<Pattern>%d{yyyy-MM-dd HH:mm:ss} %-5p %m%n</Pattern>
</PatternLayout>
<Policies>
<TimeBasedTriggeringPolicy interval="1"/>
</Policies>
<DefaultRolloverStrategy max="5" />
</RollingFile>
4. RollingFileAppender
– 基于日志大小和日期时间的滚动
如果要同时基于文件大小和日期时间来滚动日志文件,则需要同时使用SizeBasedTriggeringPolicy
和TimeBasedRollingPolicy
。
在给定的示例中,附加器可以使用包含{dd-MMM}
的filePattern
属性引用文件名模式和基于时间的滚动策略。 基于大小的滚动将发生在 10 MB。
<RollingFile
name="rollingFile"
fileName="${LOG_DIR}/application.log"
filePattern="${LOG_DIR}/application.%d{dd-MMM}.log.gz"
ignoreExceptions="false">
<PatternLayout>
<Pattern>%d{yyyy-MM-dd HH:mm:ss} %-5p %m%n</Pattern>
</PatternLayout>
<Policies>
<OnStartupTriggeringPolicy />
<SizeBasedTriggeringPolicy size="10 MB" />
<TimeBasedTriggeringPolicy />
</Policies>
<DefaultRolloverStrategy max="5" />
</RollingFile>
学习愉快!
参考文献:
RollingFileAppender
Java 文档
Log4j2 多个附加器示例
原文: https://howtodoinjava.com/log4j2/multiple-appenders/
给定log4j2.xml
是配置多个附加器的参考,例如控制台附加器和文件附加器。 这还将配置动态日志根路径。
Log4j 多个附加器配置
log4j 配置示例受到打击。 它执行以下操作:
- 使用将在其中创建日志文件的动态日志根路径。 将环境变量作为
-DAPP_LOG_ROOT=c:/temp
进行配置。 - 演示在配置文件中定义的属性常量的用法,例如,以下文件中的
LOG_PATTERN
。 - 它使用
LevelRangeFilter
将不同的日志级别语句记录在不同的文件中,即将调试日志记录在一个文件中并将错误日志记录在单独的文件中。 - 所有日志也将显示在控制台中。
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" monitorInterval="30">
<Properties>
<Property name="LOG_PATTERN">%d{yyyy-MM-dd'T'HH:mm:ss.SSSZ} %p %m%n</Property>
</Properties>
<Appenders>
<Console name="Console" target="SYSTEM_OUT" follow="true">
<PatternLayout pattern="${LOG_PATTERN}"/>
</Console>
<RollingFile name="debugLog" fileName="${sys:APP_LOG_ROOT}/application-debug.log"
filePattern="${sys:APP_LOG_ROOT}/application-debug-%d{yyyy-MM-dd}-%i.log">
<LevelRangeFilter minLevel="DEBUG" maxLevel="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<SizeBasedTriggeringPolicy size="19500KB" />
</Policies>
<DefaultRolloverStrategy max="10"/>
</RollingFile>
<RollingFile name="infoLog" fileName="${sys:APP_LOG_ROOT}/application-info.log"
filePattern="${sys:APP_LOG_ROOT}/application-info-%d{yyyy-MM-dd}-%i.log" >
<LevelRangeFilter minLevel="INFO" maxLevel="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<SizeBasedTriggeringPolicy size="19500KB" />
</Policies>
<DefaultRolloverStrategy max="10"/>
</RollingFile>
<RollingFile name="errorLog" fileName="${sys:APP_LOG_ROOT}/application-error.log"
filePattern="${sys:APP_LOG_ROOT}/application-error-%d{yyyy-MM-dd}-%i.log" >
<LevelRangeFilter minLevel="ERROR" maxLevel="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<SizeBasedTriggeringPolicy size="19500KB" />
</Policies>
<DefaultRolloverStrategy max="10"/>
</RollingFile>
<RollingFile name="springLog" fileName="${sys:APP_LOG_ROOT}/spring.log"
filePattern="${sys:APP_LOG_ROOT}/spring-%d{yyyy-MM-dd}-%i.log" >
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<SizeBasedTriggeringPolicy size="19500KB" />
</Policies>
<DefaultRolloverStrategy max="1"/>
</RollingFile>
<RollingFile name="aopLog" fileName="${sys:APP_LOG_ROOT}/application-aop.log"
filePattern="${sys:APP_LOG_ROOT}/application-aop-%d{yyyy-MM-dd}-%i.log" >
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<SizeBasedTriggeringPolicy size="19500KB" />
</Policies>
<DefaultRolloverStrategy max="1"/>
</RollingFile>
</Appenders>
<Loggers>
<Logger name="com.howtodoinjava.app.aop" additivity="false">
<AppenderRef ref="aopLog" />
<AppenderRef ref="Console" />
</Logger>
<Logger name="com.howtodoinjava.app" additivity="false">
<AppenderRef ref="debugLog" />
<AppenderRef ref="infoLog" />
<AppenderRef ref="errorLog" />
<AppenderRef ref="Console" />
</Logger>
<Logger name="org.springframework" additivity="false">
<AppenderRef ref="springLog" />
<AppenderRef ref="Console"/>
</Logger>
<Root level="all">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
将我的问题放在评论部分。
学习愉快!
Log4j2 LevelRangeFilter
示例
原文: https://howtodoinjava.com/log4j2/levelrangefilter-example/
了解如何使用 log4j LevelRangeFilter
过滤器,如果LogEvent
中的级别在配置的最小和最大级别的范围内,则返回onMatch
结果,否则返回onMismatch
值。
LevelRangeFilter
示例
在给定的log4j2.xml
文件中,我们习惯于LevelRangeFilter
通过以下方式过滤日志级别:
- 所有
info
级别的日志都将进入application-info.log
文件。 - 所有
debug
级别的日志都将进入application-debug.log
文件。 - 所有
error
级别的日志都将进入application-error.log
文件。
可以根据您的项目需求随意更改minLevel
和maxLevel
属性。
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" monitorInterval="30">
<Properties>
<Property name="LOG_PATTERN">%d{yyyy-MM-dd'T'HH:mm:ss.SSSZ} %p %m%n</Property>
</Properties>
<Appenders>
<Console name="Console" target="SYSTEM_OUT" follow="true">
<PatternLayout pattern="${LOG_PATTERN}"/>
</Console>
<RollingFile name="debugLog" fileName="${sys:APP_LOG_ROOT}/application-debug.log"
filePattern="${sys:APP_LOG_ROOT}/application-debug-%d{yyyy-MM-dd}-%i.log">
<!-- Matches only DEBUG level -->
<LevelRangeFilter minLevel="DEBUG" maxLevel="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<SizeBasedTriggeringPolicy size="19500KB" />
</Policies>
<DefaultRolloverStrategy max="10"/>
</RollingFile>
<RollingFile name="infoLog" fileName="${sys:APP_LOG_ROOT}/application-info.log"
filePattern="${sys:APP_LOG_ROOT}/application-info-%d{yyyy-MM-dd}-%i.log" >
<!-- Matches only INFO level -->
<LevelRangeFilter minLevel="INFO" maxLevel="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<SizeBasedTriggeringPolicy size="19500KB" />
</Policies>
<DefaultRolloverStrategy max="10"/>
</RollingFile>
<RollingFile name="errorLog" fileName="${sys:APP_LOG_ROOT}/application-error.log"
filePattern="${sys:APP_LOG_ROOT}/application-error-%d{yyyy-MM-dd}-%i.log">
<!-- Matches only ERROR level -->
<LevelRangeFilter minLevel="ERROR" maxLevel="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<SizeBasedTriggeringPolicy size="19500KB" />
</Policies>
<DefaultRolloverStrategy max="10"/>
</RollingFile>
</Appenders>
<Loggers>
<Logger name="com.howtodoinjava.app" additivity="false">
<AppenderRef ref="debugLog" />
<AppenderRef ref="infoLog" />
<AppenderRef ref="errorLog" />
<AppenderRef ref="Console" />
</Logger>
<Root level="all">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
将我的问题放在评论部分。
学习愉快!
Log4j2 HTMLLayout
配置示例
原文: https://howtodoinjava.com/log4j2/log4j2-htmllayout-configuration-example/
此 Log4j2 示例将帮助您使用HTMLLayout
配置log4j2.xml
文件。 HTMLLayout
生成 HTML 页面,并将每个 log 语句添加到表中的一行。
HTMLLayout
配置
在配置HTMLLayout
时,可以使用以下属性:
locationInfo
– 如果为true
,则将包括位置信息。 默认为false
。title
– 要包含在文件头中的标题。 如果未指定,则使用默认标题。contentType
– 内容类型。 默认为“text/html
”。charset
– 要使用的字符集。 如果未指定,将使用默认值。fontSize
– 文本的字体大小。font
– 用于文本的字体。
我们来看看用于生成基于 HTML 格式的日志文件的不同配置选项。
log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn">
<Properties>
<Property name="basePath">C:\\logs</Property>
</Properties>
<Appenders>
<RollingFile name="fileLogger" fileName="${basePath}/app-info.html"
filePattern="${basePath}/app-info-%d{yyyy-MM-dd}.html">
<HTMLLayout charset="UTF-8" title="Howtodoinjava Info Logs" locationInfo="true" />
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true" />
<SizeBasedTriggeringPolicy size="10 MB" />
</Policies>
</RollingFile>
<Console name="console" target="SYSTEM_OUT">
<PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n" />
</Console>
</Appenders>
<Loggers>
<Logger name="com.howtodoinjava" level="debug" additivity="false">
<appender-ref ref="fileLogger" level="debug" />
</Logger>
<Root level="debug" additivity="false">
<appender-ref ref="console" />
</Root>
</Loggers>
</Configuration>
log4j2.properties
status = error
name = PropertiesConfig
#Make sure to change log file path as per your need
property.filename = C:\\logs\\app-info.html
filters = threshold
filter.threshold.type = ThresholdFilter
filter.threshold.level = debug
appenders = rolling
appender.rolling.type = RollingFile
appender.rolling.name = RollingFile
appender.rolling.fileName = ${filename}
appender.rolling.filePattern = debug-backup-%d{MM-dd-yy-HH-mm-ss}-%i.html.gz
appender.rolling.layout.type = HTMLLayout
appender.rolling.layout.charset = UTF-8
appender.rolling.layout.title = Howtodoinjava Info Logs
appender.rolling.layout.locationInfo = true
appender.rolling.policies.type = Policies
appender.rolling.policies.time.type = TimeBasedTriggeringPolicy
appender.rolling.policies.time.interval = 1
appender.rolling.policies.time.modulate = true
appender.rolling.policies.size.type = SizeBasedTriggeringPolicy
appender.rolling.policies.size.size=10MB
appender.rolling.strategy.type = DefaultRolloverStrategy
appender.rolling.strategy.max = 20
loggers = rolling
#Make sure to change the package structure as per your application
logger.rolling.name = com.howtodoinjava
logger.rolling.level = debug
logger.rolling.additivity = false
logger.rolling.appenderRef.rolling.ref = RollingFile
log4j2.json
{
"configuration": {
"name": "Default",
"appenders": {
"RollingFile": {
"name":"File",
"fileName":"C:/logs/howtodoinjava.html",
"filePattern":"C:/logs/howtodoinjava-backup-%d{MM-dd-yy-HH-mm-ss}-%i.html.gz",
"HTMLLayout": {
"charset":"UTF-8",
"title":"Howtodoinjava Info Logs",
"locationInfo":"true"
},
"Policies": {
"SizeBasedTriggeringPolicy": {
"size":"10 MB"
}
},
"DefaultRolloverStrategy": {
"max":"10"
}
}
},
"loggers": {
"root": {
"level":"debug",
"appender-ref": {
"ref":"File"
}
}
}
}
}
示例日志语句
现在,我们使用圆顶日志语句生成日志文件。
package com.howtodoinjava.log4j2.examples;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class Log4j2HelloWorldExample
{
private static final Logger LOGGER = LogManager.getLogger(Log4j2HelloWorldExample.class.getName());
public static void main(String[] args)
{
LOGGER.debug("Debug Message Logged !!");
LOGGER.info("Info Message Logged !!");
LOGGER.debug("Another Debug Message !!");
}
}
Log4j2 HTMLLayout
输出
在上面的日志文件中运行将生成以下 HTMl 文件。
Log4j2 HTMLLayout
输出
将我的问题放在评论部分。
参考:
http://logging.apache.org/log4j/2.x/manual/layouts.html#HTMLLayout
https://logging.apache.org/log4j/2.x/log4j-core/apidocs/org/apache/logging/log4j/core/layout/HtmlLayout.html
Maven – 设置文件
原文: https://howtodoinjava.com/maven/maven-settings-file/
Maven settings.xml
文件包含并非特定于项目的配置,但实际上是全局的。 它还包含不需要分发的信息(例如密码)。
Maven 设置文件的位置
Maven 可以同时具有两个设置文件:
- Maven 安装目录:
$M2_HOME/conf/settings.xml
[全局设置] - 用户的主目录:
${user.home}/.m2/settings.xml
[用户设置]
这两个文件都是可选的。 如果两个文件都存在,则用户主目录设置文件中的值将覆盖全局设置文件中的值。
Maven 默认setting.xml
默认的 Maven setting.xml
如下所示:
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
http://maven.apache.org/xsd/settings-1.0.0.xsd">
<localRepository/>
<interactiveMode/>
<usePluginRegistry/>
<offline/>
<pluginGroups/>
<servers/>
<mirrors/>
<proxies/>
<profiles/>
<activeProfiles/>
</settings>
下表中对元素进行了简要说明:
元素名称 | 描述 |
---|---|
localRepository |
Maven 在C:\Users\<your_user_name>\.m2\repository 文件夹中本地存储插件和依赖项的副本。 此元素可用于更改本地仓库的路径。 |
interactiveMode |
顾名思义,当此值设置为true 时,默认值是 Maven 与用户交互输入。 |
usePluginRegistry |
它决定 Maven 是否应使用${user.home}/.m2/plugin-registry.xml 文件来管理插件版本。 其默认值为false 。 |
offline |
当设置为true 时,此配置指示 Maven 以脱机模式运行。 默认值为false 。 |
pluginGroups |
它包含pluginGroup 元素的列表,每个元素都包含groupId 。 当使用插件且命令行中未提供groupId 时,将搜索列表。 此列表自动包含org.apache.maven.plugins 和org.codehaus.mojo 。 |
servers |
Maven 可以与各种服务器进行交互,例如 Apache Subversion(SVN)服务器,构建服务器和远程仓库服务器。 该元素允许您指定连接到这些服务器所需的安全凭证,例如用户名和密码。 |
mirrors |
顾名思义,镜像使您可以为仓库指定备用位置。 |
proxies |
代理包含连接到互联网所需的 HTTP 代理信息。 |
profiles |
配置文件允许您对某些配置元素进行分组,例如仓库和pluginRepositories 。 |
activeProfile |
activeProfile 允许您指定默认配置文件以供 Maven 使用。 |
参考: Maven
将我的问题放在评论部分。
学习愉快!
Log4j2 ThreadContext
– 相同事务的鱼标日志
原文: https://howtodoinjava.com/log4j2/threadcontext-fish-tagging/
Log4j2 ThreadContext
允许您使用多个唯一的标签标记日志语句,以分析日志,同时在运行时诊断问题 - 主要是在多线程应用中,其中应用会在短时间内生成大量日志。 例如。 您可能需要扫描所有日志以查找特定的用户事务或完成会话。 此过程也称为鱼标(即,在每个日志语句中添加一些额外的上下文信息)。 鱼标可以帮助使用自动化工具记录日志,例如 Splunk 。
让我们看看如何在 log4j2 中将ThreadContext
类用于鱼标。
在ThreadContext
中添加上下文信息
为了唯一标记每个请求,ThreadContext
提供了put(String key, String value)
方法,该方法接受键及其值。 您可以根据需要添加任意数量的标签以捕获整个上下文信息。 请注意,ThreadContext
类的所有方法都是静态的。
package com.howtodoinjava.log4j2.examples;
import java.util.UUID;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.ThreadContext;
public class Log4j2HelloWorldExample
{
private static final Logger LOGGER = LogManager.getLogger(Log4j2HelloWorldExample.class.getName());
public static void main(String[] args)
{
//Add context information
ThreadContext.put("id", UUID.randomUUID().toString());
ThreadContext.put("ipAddress", "192.168.21.9");
LOGGER.debug("Debug Message Logged !!");
LOGGER.info("Info Message Logged !!");
LOGGER.debug("Another Debug Message !!");
//Clear the map
ThreadContext.clearMap();
LOGGER.debug("Thread Context Cleaned up !!");
LOGGER.debug("Log message with no context information !!");
}
}
或者,您可以将ThreadContext
的Stack
实现与ThreadContext.push(String value)
协作,如下所示:
//Add context information
ThreadContext.push(UUID.randomUUID().toString());
ThreadContext.push("192.168.21.9");
LOGGER.debug("Debug Message Logged !!");
LOGGER.info("Info Message Logged !!");
LOGGER.debug("Another Debug Message !!");
//Clear the map
ThreadContext.clearStack();
LOGGER.debug("Thread Context Cleaned up !!");
LOGGER.debug("Log message with no context information !!");
事务处理结束或不再需要上下文信息后,可以使用ThreadContext.clearMap()
方法清空信息。
默认情况下,ThreadContext
的栈和映射是按线程管理的,并且基于ThreadLocal
。 通过将系统属性isThreadContextMapInheritable
设置为true
,上下文映射的内容将传递给子线程。
修改log4j2.xml
中的转换模式
现在要在日志语句中打印以上标记,您需要在 log4j2 配置文件中修改转换模式。
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO">
<Appenders>
<Console name="console" target="SYSTEM_OUT">
<PatternLayout
pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] [%X{id}] [%X{ipAddress}] %c{1} - %msg%n" />
</Console>
</Appenders>
<Loggers>
<Root level="debug" additivity="false">
<AppenderRef ref="console" />
</Root>
</Loggers>
</Configuration>
- 单独使用
%X
可以包含映射的全部内容。 - 使用
%X{key}
包含指定的键。 - 使用
%x
包含栈的全部内容。
现在,当您运行上面的代码时 – 您将获得以下输出:
[DEBUG] 2016-06-21 13:09:56.485 [main] [7cdd4cf0-2c26-4b81-b374-1adce3781499] [192.168.21.9] Log4j2HelloWorldExample - Debug Message Logged !!
[INFO ] 2016-06-21 13:09:56.487 [main] [7cdd4cf0-2c26-4b81-b374-1adce3781499] [192.168.21.9] Log4j2HelloWorldExample - Info Message Logged !!
[DEBUG] 2016-06-21 13:09:56.487 [main] [7cdd4cf0-2c26-4b81-b374-1adce3781499] [192.168.21.9] Log4j2HelloWorldExample - Another Debug Message !!
[DEBUG] 2016-06-21 13:09:56.487 [main] [] [] Log4j2HelloWorldExample - Thread Context Cleaned up !!
[DEBUG] 2016-06-21 13:09:56.487 [main] [] [] Log4j2HelloWorldExample - Log message with no context information !!
如您所见,前三个日志语句中添加了上下文信息 - 其他两个语句没有此类信息。
学习愉快!
Log4j2 – 有用的转换模式示例
原文: https://howtodoinjava.com/log4j2/useful-conversion-pattern-examples/
下面的 log4j2 转换模式仅供参考,这样您和我就不会在每次创建/编辑 log4j 配置文件时浪费时间来构建这些模式。
我正在使用下面的日志语句来生成日志。
LOGGER.debug("Debug Message Logged !!");
LOGGER.info("Info Message Logged !!");
现在,我将列出不同的日志模式及其各自生成的输出。
%d [%p] %c{1} – %m%n
使用它进行简单的日志记录,即日期,级别,记录器,消息。 它将生成以下输出:
2016-06-20 19:18:02,958 [DEBUG] Log4j2HelloWorldExample - Debug Message Logged !!
2016-06-20 19:18:02,959 [INFO] Log4j2HelloWorldExample - Info Message Logged !!
%d [%-6p] %c{1} – %m%n
使用它可以以漂亮的打印记录器级别进行简单记录。 它将生成以下输出:
2016-06-20 19:21:05,271 [DEBUG ] Log4j2HelloWorldExample - Debug Message Logged !!
2016-06-20 19:21:05,272 [INFO ] Log4j2HelloWorldExample - Info Message Logged !!
%d [%-6p] %c{1} – %m%n
使用它来打印完整的包级别。 它将生成以下输出:
2016-06-20 19:22:05,379 [DEBUG ] com.howtodoinjava.log4j2.examples.Log4j2HelloWorldExample - Debug Message Logged !!
2016-06-20 19:22:05,380 [INFO ] com.howtodoinjava.log4j2.examples.Log4j2HelloWorldExample - Info Message Logged !!
%d [%-6p] %c{3} – %m%n
使用它最多可打印两个级别的包。 它将生成以下输出:
2016-06-20 19:23:48,202 [DEBUG ] log4j2.examples.Log4j2HelloWorldExample - Debug Message Logged !!
2016-06-20 19:23:48,204 [INFO ] log4j2.examples.Log4j2HelloWorldExample - Info Message Logged !!
%d{yyyy/MM/dd HH:mm:ss,SSS} [%-6p] %c{1} – %m%n
用于自定义日期格式。 它将生成以下输出:
2016/06/20 19:24:45,076 [DEBUG ] Log4j2HelloWorldExample - Debug Message Logged !!
2016/06/20 19:24:45,078 [INFO ] Log4j2HelloWorldExample - Info Message Logged !!
%d [%-6p] %C{1}.%M(%F:%L) – %m%n
将其用于调用方类,方法,源文件和行号。 它将生成以下输出:
2016-06-20 19:25:42,249 [DEBUG ] Log4j2HelloWorldExample.methodOne(Log4j2HelloWorldExample.java:14) - Debug Message Logged !!
2016-06-20 19:25:42,251 [INFO ] Log4j2HelloWorldExample.methodOne(Log4j2HelloWorldExample.java:15) - Info Message Logged !!
%sn %d{yyyy/MM/dd HH:mm:ss,SSS} %r [%-6p] [%t] %c{3} %C{3}.%M(%F:%L) – %m%n
使用它来捕获上面讨论的所有内容。 它将生成以下输出:
1 2016/06/20 19:27:03,595 620 [DEBUG ] [main] log4j2.examples.Log4j2HelloWorldExample log4j2.examples.Log4j2HelloWorldExample.main(Log4j2HelloWorldExample.java:14) - Debug Message Logged !!
2 2016/06/20 19:27:03,597 622 [INFO ] [main] log4j2.examples.Log4j2HelloWorldExample log4j2.examples.Log4j2HelloWorldExample.main(Log4j2HelloWorldExample.java:15) - Info Message Logged !!
根据您的需要随意更改和使用任何模式。
学习愉快!
为 JUnit 测试用例配置 Log4j2
原文: https://howtodoinjava.com/log4j2/configure-log4j2-for-junit/
大多数开发人员都希望使用不同的 log4j2 配置文件进行 junit 测试。 让我们学习一些推荐的配置 Log4j2 的推荐方法,这些方法特定于 junit 测试用例,并且与生产环境不同。
Table of Contents
Place log4j2-test.xml in test folder
Use log4j.configurationFile property in @BeforeClass
将log4j2-test.xml
放入测试文件夹
将log4j2-test.xml
文件放置在src/test/resources
文件夹中。 通过将log4j2-test.xml
放入此目录,将导致使用它代替可能存在的log4j2.xml
或log4j2.json
。
用于 JUnit 的 Log4j2 配置
在@BeforeClass
中使用log4j.configurationFile
属性
为 junit 测试引入不同日志文件的另一种方法是 – 在任何测试类的@BeforeClass
注解中设置log4j.configurationFile
属性。
例如。 创建测试特定的日志记录配置文件log4j2-testConfig.xml
,并将其放置在resources
文件夹中。 现在,让我们在 JUnit 测试中使用该文件。
import java.net.MalformedURLException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.BeforeClass;
import org.junit.Test;
public class HelloWorldTest
{
private static Logger LOGGER = null;
@BeforeClass
public static void setLogger() throws MalformedURLException
{
System.setProperty("log4j.configurationFile","log4j2-testConfig.xml");
LOGGER = LogManager.getLogger();
}
@Test
public void testOne()
{
LOGGER.debug("Debug Message Logged !!!");
LOGGER.info("Info Message Logged !!!");
LOGGER.error("Error Message Logged !!!", new NullPointerException("NullError"));
}
}
在评论部分中,将有关 junit 测试的 log4j2 配置的问题交给我。
学习愉快!
Log4j 教程
log4j.properties
示例 – Log4j 属性文件示例
原文: https://howtodoinjava.com/log4j/how-to-configure-log4j-using-properties-file/
Log4j 是一个简单而灵活的日志记录框架。 日志记录为开发人员提供了有关应用故障的详细上下文。 使用 log4j 可以在运行时启用日志记录,而无需修改应用二进制文件。 log4j 包的设计目的是使这些语句可以保留在出厂代码中,而不会造成高昂的性能成本。
这篇 log4j 属性文件教程,我展示了使用log4j.properties
文件配置 log4j 的示例代码。
1. Log4j Maven 依赖项
创建一个 maven java 项目并更新 log4j maven 依赖项。
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
2. log4j.properties
文件
这是主要属性文件,具有 log4j 使用的所有运行时配置。 该文件将具有 log4j 附加器信息,日志级别信息和文件附加器的输出文件名。
log4j.rootLogger=DEBUG, consoleAppender, fileAppender
log4j.appender.consoleAppender=org.apache.log4j.ConsoleAppender
log4j.appender.consoleAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.consoleAppender.layout.ConversionPattern=[%t] %-5p %c %x - %m%n
log4j.appender.fileAppender=org.apache.log4j.RollingFileAppender
log4j.appender.fileAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.fileAppender.layout.ConversionPattern=[%t] %-5p %c %x - %m%n
log4j.appender.fileAppender.File=demoApplication.log
3. log4j.properties
示例
package com.howtodoinjava;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
public class Log4jPropertiesConfigurationExample
{
static Logger logger = Logger.getLogger(Log4jPropertiesConfigurationExample.class);
public static void main(String[] args)
{
//PropertiesConfigurator is used to configure logger from properties file
PropertyConfigurator.configure("log4j.properties");
//Log in console in and log file
logger.debug("Log4j appender configuration is successful !!");
}
}
在控制台中输出,在项目根文件夹中输出demoApplication.log
:
[main] DEBUG com.howtodoinjava.Log4jPropertiesConfigurationExample - Log4j appender configuration is successful !!
现在,我们来看一些log4j.properties
示例,用于将日志消息输出到特定位置。
4. Log4j ConsoleAppender
– 记录到控制台
将日志输出到控制台的 Java 程序。
# Root logger
log4j.rootLogger=INFO, console
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.Target=System.out
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.consoleAppender.layout.ConversionPattern=[%t] %-5p %c %x - %m%n
Log4j RollingFileAppender
– 记录到文件
将日志输出到文件的 Java 程序。
# Root logger
log4j.rootLogger=INFO, file
# Direct log messages to a log file
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=C:\\temp\info.log
log4j.appender.file.MaxFileSize=10MB
log4j.appender.file.MaxBackupIndex=10
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%t] %-5p %c %x - %m%n
让我知道是否有任何问题。
学习愉快!
log4j.xml
示例 – Log4j xml 配置示例
原文: https://howtodoinjava.com/log4j/how-to-configure-log4j-using-xml-configuration/
Log4j 是一个简单而灵活的日志记录框架。 应用日志为开发人员提供了有关应用故障的详细上下文。 使用 log4j,可以在运行时启用日志记录,而无需修改应用二进制文件。 log4j 包的设计目的是使这些语句可以保留在出厂代码中,而不会造成高昂的性能成本。
实际上,两个最常见的配置选项是使用log4j.xml
配置或使用log4j.properties
配置。
在此 log4j xml 配置教程中,我展示了log4j.xml
配置的示例代码。
阅读更多: Log4j 属性文件示例
1. Log4j Maven 依赖项
创建一个 Maven Java 项目并更新 log4j 依赖项。
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
2. log4j.xml
文件
这是主要配置文件,其中包含 log4j 使用的所有运行时配置。 该文件将具有 log4j 附加器信息,日志级别信息和文件附加器的输出文件名。
创建此文件并放入应用类路径。
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration debug="true" xmlns:log4j='http://jakarta.apache.org/log4j/'>
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<param name="Target" value="System.out"/>
<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>
<appender name="fileAppender" class="org.apache.log4j.RollingFileAppender">
<param name="File" value="demoApplication.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>
<priority value ="debug"></priority>
<appender-ref ref="console"></appender>
<appender-ref ref="fileAppender"></appender>
</root>
</log4j:configuration>
3. log4j.xml
示例
package com.howtodoinjava;
import org.apache.log4j.Logger;
import org.apache.log4j.xml.DOMConfigurator;
public class Log4jXmlConfigurationExample
{
static Logger logger = Logger.getLogger(Log4jXmlConfigurationExample.class);
public static void main(String[] args)
{
//DOMConfigurator is used to configure logger from xml configuration file
DOMConfigurator.configure("log4j.xml");
//Log in console in and log file
logger.debug("Log4j appender configuration is successful !!");
}
}
在控制台中输出,在项目根文件夹中输出demoApplication.log
:
[main] DEBUG com.howtodoinjava.Log4jXmlConfigurationExample - Log4j xml configuration is successful !!
现在,让我们看一些log4j.xml
示例,这些示例将日志消息输出到特定位置。
4. Log4j 控制台附加器 – 记录到控制台
使用ConsoleAppender
将日志输出到控制台的 Java 程序。
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration debug="true" xmlns:log4j='http://jakarta.apache.org/log4j/'>
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<param name="Target" value="System.out"/>
<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>
<priority value ="debug"></priority>
<appender-ref ref="console"></appender>
</root>
</log4j:configuration>
4. Log4j 滚动文件附加器 – 记录到文件
使用RollingFileAppender
将日志输出到文件的 Java 程序。
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration debug="true" xmlns:log4j='http://jakarta.apache.org/log4j/'>
<appender name="fileAppender" class="org.apache.log4j.RollingFileAppender">
<param name="File" value="demoApplication.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>
<priority value ="debug"></priority>
<appender-ref ref="fileAppender"></appender>
</root>
</log4j:configuration>
让我知道关于log4j.xml
配置和用法的任何问题。
学习愉快!
Log4j Maven 配置示例
原文: https://howtodoinjava.com/log4j/how-to-configure-log4j-using-maven/
Log4j 是一个简单而灵活的日志记录框架。 日志记录为开发人员提供了有关应用故障的详细上下文。 使用 log4j 可以在运行时启用日志记录,而无需修改应用二进制文件。 log4j 包的设计目的是使这些语句可以保留在出厂代码中,而不会造成高昂的性能成本。
以下是在项目中配置 log4j 日志记录支持的最基本步骤。
1)创建一个 Maven 项目
mvn archetype:generate -DgroupId=com.howtodoinjava -DartifactId=Log4jTestProject
-DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
在 Eclipse 工作区或您正在使用的任何其他 IDE 中运行上述命令。如果您的工作区中已经有一个项目,则直接转到步骤 3。
2)将项目转换为 Eclipse 支持的 Java 项目
mvn eclipse:eclipse
上面的命令会将 maven 项目转换为 Eclipse Java 项目。 现在,将项目导入到 eclipse 中。
3)使用 log4j 依赖项更新pom.xml
文件
在给定的依赖项下面添加到pom.xml
。
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
运行以下命令以下载本地系统中所需的 jar,并更新项目运行时依赖项。
mvn eclipse:eclipse
4)使用BasicConfigurator
测试应用
package com.howtodoinjava;
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Logger;
public class Log4jHelloWorld {
static final Logger logger = Logger.getLogger(Log4jHelloWorld.class);
public static void main(String[] args)
{
//Configure logger
BasicConfigurator.configure();
logger.debug("Hello World!");
}
}
输出:
0 [main] DEBUG com.howtodoinjava.Log4jHelloWorld - Hello World!
如果您在控制台中看到上述消息,那么恭喜您,我们已经成功配置了 log4j。 如果您有任何问题,请再次重复上述所有步骤,或给我留言。
祝您学习愉快!
Log4j 日志级别 – Log4j2 日志级别示例
原文: https://howtodoinjava.com/log4j/logging-levels-in-log4j/
在 log4j 教程中,了解 log4j 日志记录级别。 系统和事件日志中显示的信息量和类型由配置文件中的 log4j 级别设置控制。 请记住,日志中的每个消息都以消息级别为前缀。
在 Log4j 中,级别是org.apache.log4j.Level
类的实例。
1. Log4j 日志级别
Log4j 具有以下级别的日志记录。
日志级别 | 描述 |
---|---|
ALL |
此级别用于打开所有级别的日志记录。 一旦配置完成,就根本不考虑级别。 所有追加程序将开始将日志事件倒入日志文件中。 |
TRACE |
最近在 1.2 版中引入了此功能,并将更多信息添加到调试级别日志中。 |
DEBUG |
您可以在开发时大量使用它们来调试应用。 设置此级别后,每条日志消息都会进入日志文件。 它基本上属于开发人员。 |
INFO |
重要的业务流程已经完成,好消息“如预期”。 系统管理员将实时查看信息日志,以确保当前系统上正在发生的事情以及正常流程中是否存在任何问题。 |
WARN |
它建议继续该应用,但您应格外小心。 该应用可以容忍警告消息,但是应该始终对警告消息进行合理的检查,以确保它们不会证明应用中等待点火的隐藏饼干。 |
ERROR |
它对您大喊大叫,出了什么问题,您必须立即进行调查。 这仅表示您的应用已达到真正不希望的状态。 例如数据库不可用或意外的格式化输入等。 |
FATAL |
在正常情况下,您不会感觉到它们的存在,但是一旦它们出现,就表示非常不好的消息,甚至是应用死亡。 |
OFF |
很简单。 没有记录! |
在 log4j 中,对于根记录器 – 默认日志级别为
DEBUG
。
2. 如何设置日志级别
2.1 在log4j.properties
中设置日志级别
log4j.rootLogger=DEBUG, consoleAppender
log4j.appender.consoleAppender=org.apache.log4j.ConsoleAppender
log4j.appender.consoleAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.consoleAppender.layout.ConversionPattern=[%t] %-5p %c %x - %m%n
#Log info messages for package 'com.howtodoinjava.web.controller'
log4j.logger.com.howtodoinjava.web.controller=INFO, consoleAppender
查看log4j2.properties
配置示例。
2.2 在log4j.xml
中设置日志级别
<log4j:configuration debug="true" xmlns:log4j='http://jakarta.apache.org/log4j/'>
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<param name="Target" value="System.out"/>
<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>
<logger name="com.howtodoinjava.web.controller">
<level value="INFO" />
<appender-ref ref="console" />
</logger>
<root>
<level value="DEBUG" />
<appender-ref ref="console"></appender>
</root>
</log4j:configuration>
查看log4j2.xml
配置示例。
3. 日志级别如何工作?
在 log4j 中,日志级别具有顺序。
ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF
如果我们将日志级别设置为X
,则级别大于等于X
的任何日志请求都会记录在日志文件中。带有更低级别的任何请求都不会被记录。
例如,如果我们将日志记录级别设置为INFO
,则应用可以记录范围为INFO
,WARN
,ERROR
和FATAL
的消息。
在此图中,垂直标题显示LogEvent
的级别,而水平标题显示与适当的日志记录配置关联的级别。 交叉点表明允许LogEvent
通过以便进一步处理(是)还是丢弃(否)。
Log4j 日志级别层次
4. Log4j 日志级别示例
演示日志级别用法的 Java 程序。
import org.apache.log4j.*;
public class LogLevelExample
{
private static Logger logger = Logger.getLogger(LogLevelExample.class);
public static void main(String[] args) {
logger.setLevel(Level.INFO);
logger.trace("Trace Message!");
logger.debug("Debug Message!");
logger.info("Info Message!");
logger.warn("Warn Message!");
logger.error("Error Message!");
logger.fatal("Fatal Message!");
}
}
程序输出。
Info Message!
Warn Message!
Error Message!
Fatal Message!
学习愉快!
参考:
Log4j 文档
Log4j ConsoleAppender
配置示例
原文: https://howtodoinjava.com/log4j/log4j-console-appender-example/
任何旨在将日志记录信息打印到控制台的日志记录应用都应使用此org.apache.log4j.ConsoleAppender
。 ConsoleAppender
是一个非常简单的类,旨在将日志记录信息写入System.out
或System.err
。 可以通过名为target
的属性来配置日志消息的目的地。
ConsoleAppender
的属性
ConsoleAppender
的可配置属性如下所述:
属性 | 描述 |
---|---|
immediateFlush |
设置是否用每个日志记录输出请求刷新控制台流。 |
encoding |
重写默认的字符编码方案。 |
threshold |
级别低于阈值的任何日志记录请求都将被忽略。 |
target |
System.out 或System.err 。 默认值为System.out 。 |
ConsoleAppender
配置
属性文件中的ConsoleAppender
配置
log4j.rootCategory=debug,console
log4j.logger.com.demo.package=debug,console
log4j.additivity.com.demo.package=false
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.target=System.out
log4j.appender.console.immediateFlush=true
log4j.appender.console.encoding=UTF-8
#log4j.appender.console.threshold=warn
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.conversionPattern=%d [%t] %-5p %c - %m%n
XML 文件中的ConsoleAppender
配置
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration>
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<param name="target" value="System.err"/>
<param name="immediateFlush" value="false"/>
<param name="encoding" value="UTF-8"/>
<param name="threshold" value="warn"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="conversionPattern" value="%d [%t] %-5p %c - %m%n"/>
</layout>
</appender>
<logger name="com.demo.package">
<level value="debug"/>
<appender-ref ref="console"/>
</logger>
<root>
<priority value ="debug" />
<appender-ref ref="console"/>
</root>
</log4j:configuration>
测试ConsoleAppender
配置
让我们编写一个快速的 Java 程序,并使用上述配置在控制台中写入日志。
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
public class Demo
{
static Logger logger = Logger.getLogger(Demo.class);
public static void main(String[] args) {
// PropertiesConfigurator is used to configure logger from properties file
PropertyConfigurator.configure("log4j.properties");
// Log in console
logger.debug("Log4j console appender configuration is successful !!");
}
}
现在,根据需要将log4j.properties
文件放在项目根文件夹或资源文件夹中,然后运行该应用。 您将在控制台中获得以下日志消息。
2016-06-14 18:03:13,175 [main] DEBUG Demo - Log4j console appender configuration is successful !!
将我的问题放在评论部分。
学习愉快!
Maven – 依赖管理
原文: https://howtodoinjava.com/maven/maven-dependency-management/
在 Maven 中,依赖项是您当前项目为了编译,构建,测试和/或运行而需要的另一个归档文件(JAR,ZIP 等)。 依赖项收集在<dependency>
标签内的pom.xml
文件中。
当您运行构建或执行 Maven 目标时,将解决这些依赖项,然后从本地仓库加载这些依赖项。 如果它们不存在,那么 Maven 将从远程仓库下载它们并将它们存储在本地仓库中。 您也可以手动安装依赖项。
了解更多:本地仓库路径
Table of Contents
Dependency Example
External Dependency
Dependency Tree
Dependency Exclusion
Artifact Version Ranges
Maven 依赖示例
在深入研究依赖管理之前,让我们看一个简单的示例,其中包含不同的元素,我们将在本文中讨论。
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.5.RELEASE</version>
</dependency>
</dependencies>
如果pom.xml
指向同一groupId
的许多工件,则应使用属性以便分解代码以便于维护。
<properties>
<junit.version>4.12</junit.version>
<spring.version>4.3.5.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
外部依赖
有时,您将必须引用不在 maven 仓库(本地,中央或远程仓库)中的 jar 文件。 您可以通过将这些 jar 放在项目的lib
文件夹中来使用它们,并配置外部依赖项,如下所示:
<dependency>
<groupId>extDependency</groupId>
<artifactId>extDependency</artifactId>
<scope>system</scope>
<version>1.0</version>
<systemPath>${basedir}\war\WEB-INF\lib\extDependency.jar</systemPath>
</dependency>
groupId
和artifactId
都设置为依赖项的名称。scope
元素值设置为system
。systemPath
元素引用 JAR 文件的位置。
Maven 依赖树
使用 maven 的dependency:tree
命令,可以传递地查看项目中所有依赖项的列表。 传递依赖表示如果 A 依赖于 B 而 B 依赖于 C,则 A 依赖于 B 和 C。
当不同依赖项包含相同工件的不同版本时,传递性带来了一个非常严重的问题。 它可能会在运行时导致版本不匹配问题。 在这种情况下,dependency:tree
命令对于处理 JAR 冲突非常有用。
$ mvn dependency:tree
它以给定的格式输出依赖项信息:
[INFO] --- maven-dependency-plugin:2.1:tree (default-cli) @ MavenExamples ---
[INFO] com.howtodoinjava.demo:MavenExamples:jar:0.0.1-SNAPSHOT
[INFO] +- junit:junit:jar:3.8.1:test
[INFO] \- org.springframework:spring-core:jar:4.3.5.RELEASE:compile
[INFO] \- commons-logging:commons-logging:jar:1.2:compile
了解它如何通知 spring 依赖commons-logging
。 同样,您可以使用此命令获取完整的传递依赖项信息。
Maven 依赖排除
除了由传递依赖引起的版本不匹配问题之外,项目工件和部署平台(例如 Tomcat 或其他服务器)之间的工件之间可能存在版本不匹配。
为了解决此类版本不匹配的问题,maven 提供了<exclusion>
标记,以打破传递依赖项。
例如,当您在类路径中具有 JUnit4.12 并包括 DBUnit 依赖项时,则需要删除 JUnit 3.8.2 依赖项。 可以使用exclusion
标签完成。
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.dbunit</groupId>
<artifactId>dbunit</artifactId>
<version>${dbunit.version}</version>
<scope>test</scope>
<exclusions>
<!--Exclude transitive dependency to JUnit-3.8.2 -->
<exclusion>
<artifactId>junit</artifactId>
<groupId>junit</groupId>
</exclusion>
</exclusions>
</dependency>
工件版本范围
在包含依赖项的同时,您可以自由地为任何工件指定版本范围。 要给出版本范围,可以使用以下符号:
- 括号符号
(
和)
表示包含范围 - 括号符号
[
和]
表示排除范围 - 逗号分隔的子集
版本范围示例
让我们看一些示例,以更好地了解有关指定版本范围的信息。
范围 | 含义 |
---|---|
1.2 |
等于 1.2 或以 1.2 开头的版本 |
(,1.2] |
小于 1.2 的任何版本。 包含 1.2 版。 x <= 1.2 |
(,1.2) |
小于 1.2 的任何版本。 1.2 版除外。 x < 1.2 |
[1.2] |
仅限于 1.2 版。 x == 1.0 |
[1.2,) |
任何大于 1.2 的版本。 包含 1.2 版。 x >= 1.2 |
(1.2,) |
任何大于 1.2 的版本。 1.2 版除外。 x > 1.2 |
(1.2,2.2) |
在 1.2 和 2.2 之间的版本。 两者都排除在外。 1.0 < x < 2.0 |
[1.2,2.2] |
在 1.2 和 2.2 之间的版本。 两者都包括在内。 1.2 <= x <= 2.2 |
(,1.2],[2.2,) |
小于 1.2 或大于 2.2 的版本。 两者都包括在内。 x <= 1.2 or x >= 2.2 |
将我的问题放在评论部分。
学习愉快!
Log4jRollingFileAppender
配置示例
原文: https://howtodoinjava.com/log4j/log4j-rolling-file-appender/
Log4j RollingFileAppender
是OutputStreamAppender
,它按照有关何时发生滚动(备份)的已配置触发策略,将日志消息写入文件。 它还具有有关如何转换文件的已配置转换策略。
通常,日志文件的备份是根据文件大小和/或当前日期创建的。
1. Log4j Maven 依赖项
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
2. RollingFileAppender
示例 – 基于日志文件大小的滚动
此给定的配置根据日志文件大小滚动日志文件。 我已将日志文件大小配置为 10 MB。 根据您的要求进行更改。
2.1. log4j.properties
我们可以按照给定的方式在log4j.properties
中配置滚动文件附加器。
log4j.appender.rollingFile=org.apache.log4j.RollingFileAppender
log4j.appender.rollingFile.File=${LOG_DIR}/application.log
log4j.appender.rollingFile.layout=org.apache.log4j.PatternLayout
log4j.appender.rollingFile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %m%n
log4j.appender.rollingFile.MaxFileSize=10MB
log4j.appender.rollingFile.MaxBackupIndex=5
log4j.appender.rollingFile.append=true
log4j.rootCategory=ALL, rollingFile
2.2 log4j.xml
<appender name="rollingFile" class="org.apache.log4j.RollingFileAppender">
<param name="file" value="${LOG_DIR}/application.log" />
<param name="MaxFileSize" value="10MB" />
<param name="MaxBackupIndex" value="5" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5p %m%n" />
</layout>
</appender>
3. RollingFileAppender
– 基于日期时间的滚动
我们也可以根据日期时间滚动日志文件。
3.1 RollingFileAppender
示例
如果使用RollingFileAppender
,则使用TimeBasedRollingPolicy
来指定何时基于日期时间滚动日志文件。
注意FileNamePattern
属性。 它定义了滚动文件的名称模式。 在给定的示例中,它将在日志文件名中使用date-month
重命名滚动日志文件。
例如,模式'{dd-MMM}'
将在一个月的每天中滚动日志文件。 同样,'{MM-dd-yyyy-HH}'
将每小时滚动一次。
我们还使用.gz
扩展名,因此 log4j 将自动压缩日志文件。
<appender name="rollingFile" class="org.apache.log4j.rolling.RollingFileAppender">
<rollingPolicy class="org.apache.log4j.rolling.TimeBasedRollingPolicy">
<param name="FileNamePattern" value="${LOG_DIR}/application.%d{dd-MMM}.log.gz" />
</rollingPolicy>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5p - %m%n" />
</layout>
</appender>
3.2 DailyRollingFileAppender
示例
为了启用每日滚动,log4j 提供了DailyRollingFileAppender
,它扩展了FileAppender
。 如果要每天滚动日志文件,请直接使用它。
<appender name="rollingFile" class="org.apache.log4j.rolling.DailyRollingFileAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5p - %m%n" />
</layout>
</appender>
4. RollingFileAppender
– 基于日志大小和日期时间的滚动
如果要同时基于文件大小和日期时间来滚动日志文件,则需要同时使用SizeBasedTriggeringPolicy
和TimeBasedRollingPolicy
。
<appender name="rollingFile" class="org.apache.log4j.rolling.RollingFileAppender">
<rollingPolicy
class="org.apache.log4j.rolling.TimeBasedRollingPolicy">
<param name="ActiveFileName" value="${LOG_DIR}/application.log" />
<param name="FileNamePattern" value="${LOG_DIR}/application.%d{dd-MMM}.log.gz" />
</rollingPolicy>
<triggeringPolicy
class="org.apache.log4j.rolling.SizeBasedTriggeringPolicy">
<param name="MaxFileSize" value="10MB" />
</triggeringPolicy>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5p - %m%n" />
</layout>
</appender>
参考文献:
Log4j RollingFileAppender
Java 文档
Log4j SocketAppender
和套接字服务器示例
原文: https://howtodoinjava.com/log4j/log4j-socketappender-and-socket-server-example/
Log4j 是一个简单而灵活的日志记录框架。 日志记录为开发人员提供了有关应用故障的详细上下文。 使用 log4j 可以在运行时启用日志记录,而无需修改应用二进制文件。
Log4j 带有多个选项来格式化框架创建的日志文件。 它也可以创建简单的日志文件,html 日志文件或 xml 日志文件。
这篇文章中,我将展示用于配置 log4j 以在网络位置的简单套接字服务器(打包在log4j.jar
本身中)中记录日志事件的示例代码。
步骤 1)创建一个 Maven Java 项目并更新 log4j 依赖项
请遵循与使用 maven 配置 log4j 有关的步骤。
步骤 2)在log4j-server.properties
文件中配置套接字服务器日志记录配置
很少有人知道 log4j 具有与功能齐全的套接字服务器捆绑在一起的功能,该功能可用于监听网络连接并记录从各个网络节点和位置发送到服务器的日志事件。
要配置套接字服务器,请在给定的项目根文件夹中创建一个log4j-server.properties
文件。 此文件配置接收到的日志事件的记录方式和记录位置。
#Define a narrow log category. A category like debug will produce some extra logs also from server itself
log4j.rootLogger=ERROR, file
#Define how the socket server should store the log events
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=application-error.log
log4j.appender.file.MaxFileSize=1MB
log4j.appender.file.MaxBackupIndex=1
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%d] [%t] [%m]%n
步骤 3)将log4j.properties
配置为使用SocketAppender
在此步骤中,让我们指定远程套接字服务器的主机名(即 IP 地址)和它正在监听日志事件的端口。 我们还将日志附加器指定为SocketAppender
,它能够将日志事件发送到网络上连接的套接字。
#Define the log4j configuration for local application
log4j.rootLogger=ERROR, server
#We will use socket appender
log4j.appender.server=org.apache.log4j.net.SocketAppender
#Port where socket server will be listening for the log events
log4j.appender.server.Port=4712
#Host name or IP address of socket server
log4j.appender.server.RemoteHost=localhost
#Define any connection delay before attempting to reconnect
log4j.appender.server.ReconnectionDelay=10000
步骤 4)启动简单套接字服务器
要启动服务器,请在命令提示符下键入以下命令,服务器将启动并运行:
java -classpath c:Users.m2repositorylog4jlog4j1.2.17log4j-1.2.17.jar org.apache.log4j.net.SimpleSocketServer
4712 log4j-server.properties
请不要忘记在系统中指定 log4j.jar 的正确路径。
步骤 5)测试应用
编写一个配置log4j.properties
并发送日志事件的测试类。 我写了这样的测试类,如下所示:
package com.howtodoinjava;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
public class Log4jSocketAppenderExample
{
static Logger logger = Logger.getLogger(Log4jSocketAppenderExample.class);
public static void main(String[] args)
{
//PropertiesConfigurator is used to configure logger from properties file
PropertyConfigurator.configure("log4j.properties");
//These logs will be sent to socket server as configured in log4j.xml
logger.error("Log4j socket appender test run successfully!!");
}
}
上面的代码将在application.log
文件中创建一个日志条目,如下所示:
[2013-04-09 09:00:34,044] [main] [Log4j socket appender test run successfully!!]
祝您学习愉快!
Log4j JDBCAppender
– 在数据库中创建日志
原文: https://howtodoinjava.com/log4j/how-to-create-logs-in-database-using-jdbcappender-in-log4j/
Log4j 是一个简单而灵活的日志记录框架。 日志记录为开发人员提供了有关应用故障的详细上下文。 使用 log4j 可以在运行时启用日志记录,而无需修改应用二进制文件。 log4j 包的设计目的是使这些语句可以保留在出厂代码中,而不会造成高昂的性能成本。
Log4j 带有多个选项来格式化框架创建的日志文件。 它也可以创建简单的日志文件,html 日志文件或 xml 日志文件。 它还使用 mysql 语句将日志语句插入数据库。
这篇文章,我将展示用于配置 log4j 以在数据库表中生成日志的示例代码。
步骤 1)创建一个 Maven Java 项目并更新 log4j 依赖项
请遵循与使用 maven 配置 log4j 有关的步骤。
步骤 2)在log4j.properties
文件中配置JDBCAppender
JDBCAppender
提供了用于将日志事件发送到数据库表的机制。 每个追加调用将添加到ArrayList
缓冲区。 当缓冲区已满时,每个日志事件都将放置在 sql 语句(可配置)中并执行。 缓冲区大小, DB URL,用户和密码是标准 log4j 方式中的可配置选项。
警告:此版本的JDBCAppender
将来很可能会完全替换。 此外,它不会记录异常。
# Define the root logger with file appender
log4j.rootLogger = DEBUG, sql
# Define the file appender
log4j.appender.sql=org.apache.log4j.jdbc.JDBCAppender
log4j.appender.sql.URL=jdbc:mysql://localhost/test
# Set Database Driver
log4j.appender.sql.driver=com.mysql.jdbc.Driver
# Set database user name and password
log4j.appender.sql.user=root
log4j.appender.sql.password=password
# Set the SQL statement to be executed.
log4j.appender.sql.sql=INSERT INTO LOGS VALUES ('%x', now() ,'%C','%p','%m')
# Define the xml layout for file appender
log4j.appender.sql.layout=org.apache.log4j.PatternLayout
步骤 3)在数据库中创建表并测试应用
在模式测试中,创建数据库表LOGS
。
CREATE TABLE LOGS
(
USER_ID VARCHAR(20) NOT NULL,
DATED DATETIME NOT NULL,
LOGGER VARCHAR(50) NOT NULL,
LEVEL VARCHAR(10) NOT NULL,
MESSAGE VARCHAR(1000) NOT NULL
);
现在,使用PropertyConfigurator
配置log4j.properties
文件并调用一些日志事件。
package com.howtodoinjava;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
public class Log4jJDBCExample
{
static Logger log = Logger.getLogger(Log4jJDBCExample.class);
public static void main(String[] args)
{
PropertyConfigurator.configure("log4j.properties");
log.debug("Sample debug message");
log.info("Sample info message");
log.error("Sample error message");
log.fatal("Sample fatal message");
}
}
日志语句将使用 sql 语句插入数据库中。
让我知道是否有任何问题。
祝您学习愉快!
Log4j XMLLayout
– 以 XML 格式创建日志
原文: https://howtodoinjava.com/log4j/how-to-create-logs-in-xml-format-using-log4j/
Log4j 是一个简单而灵活的日志记录框架。 日志记录为开发人员提供了有关应用故障的详细上下文。 使用 log4j 可以在运行时启用日志记录,而无需修改应用二进制文件。 log4j 包的设计目的是使这些语句可以保留在出厂代码中,而不会造成高昂的性能成本。
Log4j 带有多个选项来格式化框架创建的日志文件。 它也可以创建简单的日志文件,html 日志文件或 xml 日志文件。
在这篇文章中,我将展示用于配置 log4j 以生成 xml 格式的日志的示例代码。
步骤 1)创建一个 Maven Java 项目并更新 log4j 依赖项
请遵循与使用 maven 配置 log4j 有关的步骤。
步骤 2)在log4j.properties
文件中配置XMLLayout
XMLLayout
类扩展了抽象的org.apache.log4j.Layout
类,并从其基类覆盖format()
方法以提供 XML 样式的格式。
这在 xml 标记中提供了以下信息:
log4j:event
:该标签包含有关记录器设置的信息,例如记录器名称,时间戳,级别和线程。log4j:message
:它包含 CDATA 格式的实际日志消息。log4j:locationInfo
:它包含类文件中 log 语句的位置。
# Define the root logger with file appender
log4j.rootLogger = DEBUG, XML
# Define the file appender
log4j.appender.XML=org.apache.log4j.FileAppender
log4j.appender.XML.File=application.xml
# Define the xml layout for file appender
log4j.appender.XML.layout=org.apache.log4j.xml.XMLLayout
log4j.appender.XML.layout.LocationInfo=true
log4j.appender.XML.Threshold=DEBUG
步骤 3)配置log4j.properties
并测试应用
package com.howtodoinjava;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
public class Log4jXMLLayoutExample
{
static Logger log = Logger.getLogger(Log4jXMLLayoutExample.class);
public static void main(String[] args)
{
PropertyConfigurator.configure("log4j.properties");
log.debug("Sample debug message");
log.info("Sample info message");
log.error("Sample error message");
log.fatal("Sample fatal message");
}
}
输出将记录在项目根文件夹中的application.xml
文件中。 示例内容将如下所示:
<log4j:event logger="com.howtodoinjava.Log4jXMLLayoutExample" timestamp="1368417841874" level="DEBUG" thread="main">
<log4j:message><![CDATA[Sample debug message]]></log4j:message>
<log4j:locationInfo class="com.howtodoinjava.Log4jXMLLayoutExample" method="main" file="Log4jXMLLayoutExample.java" line="14"/>
</log4j:event>
<log4j:event logger="com.howtodoinjava.Log4jXMLLayoutExample" timestamp="1368417841893" level="INFO" thread="main">
<log4j:message><![CDATA[Sample info message]]></log4j:message>
<log4j:locationInfo class="com.howtodoinjava.Log4jXMLLayoutExample" method="main" file="Log4jXMLLayoutExample.java" line="15"/>
</log4j:event>
<log4j:event logger="com.howtodoinjava.Log4jXMLLayoutExample" timestamp="1368417841893" level="ERROR" thread="main">
<log4j:message><![CDATA[Sample error message]]></log4j:message>
<log4j:locationInfo class="com.howtodoinjava.Log4jXMLLayoutExample" method="main" file="Log4jXMLLayoutExample.java" line="16"/>
</log4j:event>
<log4j:event logger="com.howtodoinjava.Log4jXMLLayoutExample" timestamp="1368417841893" level="FATAL" thread="main">
<log4j:message><![CDATA[Sample fatal message]]></log4j:message>
<log4j:locationInfo class="com.howtodoinjava.Log4jXMLLayoutExample" method="main" file="Log4jXMLLayoutExample.java" line="17"/>
</log4j:event>
要点
如果您尝试在浏览器中查看以上文件,它将显示解析错误:“XML 解析错误:前缀未绑定到名称空间”。 这是预期的,因为日志文件不包含任何根元素。
同样根据XMLLayout
的 Java 文档,“XMLLayout
的输出由一系列log4j.event
元素组成,如log4j.dtd
中所定义。 它不会输出完整的格式正确的 XML 文件。 该输出被设计为作为外部实体包含在单独的文件中,以形成正确的 XML 文件。”
例如,如果abc
是XMLLayout
输出所在的文件的名称,那么格式正确的 XML 文件将是:
<!DOCTYPE log4j:eventSet PUBLIC "-//APACHE//DTD LOG4J 1.2//EN" "log4j.dtd" [<!ENTITY data SYSTEM "abc">]>
<log4j:eventSet version="1.2" xmlns:log4j="http://jakarta.apache.org/log4j/">
<log4j:event logger="com.howtodoinjava.Log4jXMLLayoutExample" timestamp="1368417841874" level="DEBUG" thread="main">
<log4j:message><![CDATA[Sample debug message]]></log4j:message>
<log4j:locationInfo class="com.howtodoinjava.Log4jXMLLayoutExample" method="main" file="Log4jXMLLayoutExample.java" line="14"/>
</log4j:event>
<log4j:event logger="com.howtodoinjava.Log4jXMLLayoutExample" timestamp="1368417841893" level="INFO" thread="main">
<log4j:message><![CDATA[Sample info message]]></log4j:message>
<log4j:locationInfo class="com.howtodoinjava.Log4jXMLLayoutExample" method="main" file="Log4jXMLLayoutExample.java" line="15"/>
</log4j:event>
<log4j:event logger="com.howtodoinjava.Log4jXMLLayoutExample" timestamp="1368417841893" level="ERROR" thread="main">
<log4j:message><![CDATA[Sample error message]]></log4j:message>
<log4j:locationInfo class="com.howtodoinjava.Log4jXMLLayoutExample" method="main" file="Log4jXMLLayoutExample.java" line="16"/>
</log4j:event>
<log4j:event logger="com.howtodoinjava.Log4jXMLLayoutExample" timestamp="1368417841893" level="FATAL" thread="main">
<log4j:message><![CDATA[Sample fatal message]]></log4j:message>
<log4j:locationInfo class="com.howtodoinjava.Log4jXMLLayoutExample" method="main" file="Log4jXMLLayoutExample.java" line="17"/>
</log4j:event>
</log4j:eventSet>
这种方法增强了XMLLayout
和嵌入它的附加器的独立性。
祝您学习愉快!
Log4j HTMLLayout
– 以 HTML 格式创建日志
原文: https://howtodoinjava.com/log4j/how-to-create-logs-in-html-format-using-log4j/
Log4j 是一个简单而灵活的日志记录框架。 日志记录为开发人员提供了有关应用故障的详细上下文。 使用 log4j 可以在运行时启用日志记录,而无需修改应用二进制文件。 log4j 包的设计目的是使这些语句可以保留在出厂代码中,而不会造成高昂的性能成本。
Log4j 带有多个选项来格式化框架创建的日志文件。 它也可以创建简单的日志文件,html 日志文件或 xml 日志文件。
在这篇文章中,我将展示用于配置 log4j 以生成 html 格式的日志的示例代码。
步骤 1)创建一个 Maven Java 项目并更新 log4j 依赖项
请遵循与使用 maven 配置 log4j 有关的步骤。
步骤 2)在log4j.properties
文件中配置HTMLLayout
HTMLLayout
类扩展了抽象的org.apache.log4j.Layout
类,并从其基类覆盖format()
方法以提供 HTML 样式的格式设置。
这提供了以下信息以供显示:
- 从应用启动到生成特定日志事件之前所经过的时间。
- 调用日志记录请求的线程的名称。
- 与此日志记录请求关联的级别。
- 记录器和记录消息的名称。
- 程序文件的可选位置信息以及从中调用此日志记录的行号。
# Define the root logger with file appender
log4j.rootLogger = DEBUG, HTML
# Define the file appender
log4j.appender.HTML=org.apache.log4j.FileAppender
log4j.appender.HTML.File=application.html
# Define the html layout for file appender
log4j.appender.HTML.layout=org.apache.log4j.HTMLLayout
log4j.appender.HTML.layout.Title=Application logs
log4j.appender.HTML.layout.LocationInfo=true
log4j.appender.HTML.Threshold=DEBUG
步骤 3)配置 log4j.properties 并测试应用
package com.howtodoinjava;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
public class Log4jHTMLLayoutExample
{
static Logger log = Logger.getLogger(Log4jHTMLLayoutExample.class);
public static void main(String[] args)
{
PropertyConfigurator.configure("log4j.properties");
log.debug("Sample debug message");
log.info("Sample info message");
log.error("Sample error message");
log.fatal("Sample fatal message");
}
}
输出将记录在项目根文件夹的application.html
中:
包
从 log4j 生成的 HTML 日志
让我知道是否有任何问题。
祝您学习愉快!
Log4j – 在运行时重新加载日志记录级别
原文: https://howtodoinjava.com/log4j/how-to-reload-log4j-levels-on-runtime/
过多的日志记录是导致应用性能下降的常见原因。 它是最佳实践之一,可确保 Java EE 应用实现中的正确日志记录。 但是,请注意在生产环境中启用的日志记录级别。 过多的日志记录将触发服务器上的高 IO,并增加 CPU 使用率。 对于使用较旧硬件的较旧环境或处理大量并发卷的环境而言,这尤其可能成为问题。
A balanced approach is to implement a "reloadable logging level" facility to turn extra logging ON / OFF
when required in your day to day production support.
让我们看看如何使用 Java 7 提供的WatchService
完成此操作。
步骤 1)实现一个WatchService
,它将监听给定 log4j 文件中的更改
下面的给定代码初始化了一个线程,该线程连续监视给定 log4j 文件(StandardWatchEventKinds.ENTRY_MODIFY
)中的修改。 一旦观察到文件更改,就会调用configurationChanged()
方法,并使用 DOMConfigurator.configure()
方法再次重新加载 log4j 配置。
Log4jChangeWatcherService.java
package com.howtodoinjava.demo;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import org.apache.log4j.xml.DOMConfigurator;
public class Log4jChangeWatcherService implements Runnable
{
private String configFileName = null;
private String fullFilePath = null;
public Log4jChangeWatcherService(final String filePath) {
this.fullFilePath = filePath;
}
//This method will be called each time the log4j configuration is changed
public void configurationChanged(final String file)
{
System.out.println("Log4j configuration file changed. Reloading logging levels !!");
DOMConfigurator.configure(file);
}
public void run() {
try {
register(this.fullFilePath);
} catch (IOException e) {
e.printStackTrace();
}
}
private void register(final String file) throws IOException {
final int lastIndex = file.lastIndexOf("/");
String dirPath = file.substring(0, lastIndex + 1);
String fileName = file.substring(lastIndex + 1, file.length());
this.configFileName = fileName;
configurationChanged(file);
startWatcher(dirPath, fileName);
}
private void startWatcher(String dirPath, String file) throws IOException {
final WatchService watchService = FileSystems.getDefault().newWatchService();
//Define the file and type of events which the watch service should handle
Path path = Paths.get(dirPath);
path.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY);
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
try {
watchService.close();
} catch (IOException e) {
e.printStackTrace();
}
}
});
WatchKey key = null;
while (true) {
try {
key = watchService.take();
for (WatchEvent< ?> event : key.pollEvents()) {
if (event.context().toString().equals(configFileName)) {
//From here the configuration change callback is triggered
configurationChanged(dirPath + file);
}
}
boolean reset = key.reset();
if (!reset) {
System.out.println("Could not reset the watch key.");
break;
}
} catch (Exception e) {
System.out.println("InterruptedException: " + e.getMessage());
}
}
}
}
2)添加Log4jConfigurator
,这是您的应用与 log4j 协作的接口
此类是一个工具类,它将 log4j 初始化代码和重载策略与应用代码分开。
Log4jConfigurator.java
package com.howtodoinjava.demo;
public class Log4jConfigurator
{
//This ensures singleton instance of configurator
private final static Log4jConfigurator INSTANCE = new Log4jConfigurator();
public static Log4jConfigurator getInstance()
{
return INSTANCE;
}
//This method will start the watcher service of log4j.xml file and also configure the loggers
public void initilize(final String file) {
try
{
//Create the watch service thread and start it.
//I will suggest to use some logic here which will check if this thread is still alive;
//If thread is killed then restart the thread
Log4jChangeWatcherService listner = new Log4jChangeWatcherService(file);
//Start the thread
new Thread(listner).start();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
3)测试 log4j 重载事件
为了测试代码,我记录了两个两个语句:一个调试级别和一个信息级别。 两个语句在循环 2 秒后都被记录。 我将在一些日志语句之后更改日志记录级别,并且应该重新加载 log4j。
log4j-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<param name="Target" value="System.out"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="[%t] %-5p %c %x - %m%n"/>
</layout>
</appender>
<root>
<priority value ="info" />
<appender-ref ref="console" />
</root>
</log4j:configuration>
Log4jConfigReloadExample.java
package com.howtodoinjava.demo;
import org.apache.log4j.Logger;
public class Log4jConfigReloadExample
{
private static final String LOG_FILE_PATH = "C:/Lokesh/Setup/workspace/Log4jReloadExample/log4j-config.xml";
public static void main(String[] args) throws InterruptedException
{
//Configure logger service
Log4jConfigurator.getInstance().initilize(LOG_FILE_PATH);
//Get logger instance
Logger LOGGER = Logger.getLogger(Log4jConfigReloadExample.class);
//Print the log messages and wait for log4j changes
while(true)
{
//Debug level log message
LOGGER.debug("A debug message !!");
//Info level log message
LOGGER.info("A info message !!");
//Wait between log messages
Thread.sleep(2000);
}
}
}
Output:
[main] INFO com.howtodoinjava.demo.Log4jConfigReloadExample - A info message !!
[main] INFO com.howtodoinjava.demo.Log4jConfigReloadExample - A info message !!
[main] INFO com.howtodoinjava.demo.Log4jConfigReloadExample - A info message !!
[main] INFO com.howtodoinjava.demo.Log4jConfigReloadExample - A info message !!
[main] INFO com.howtodoinjava.demo.Log4jConfigReloadExample - A info message !!
Log4j configuration file changed. Reloading logging levels !!
[main] DEBUG com.howtodoinjava.demo.Log4jConfigReloadExample - A debug message !!
[main] INFO com.howtodoinjava.demo.Log4jConfigReloadExample - A info message !!
[main] DEBUG com.howtodoinjava.demo.Log4jConfigReloadExample - A debug message !!
[main] INFO com.howtodoinjava.demo.Log4jConfigReloadExample - A info message !!
祝您学习愉快!
SLF4j 与 Log4j – 哪个更好?
原文: https://howtodoinjava.com/log4j/slf4j-vs-log4j-which-one-is-better/
我多次被问到这个问题,所以我想写下我的答案作为博客本身的这篇文章,以便其他人在需要时可以不时引用它。
Java 简单日志记录外观(SLF4J)是一种 API,旨在提供对许多日志记录框架的通用访问;log4j 是其中之一。 然后,在部署时(而不是在编写代码时)决定使用哪个。 最佳实践是将 slf4j 用于您自己的日志语句,然后为其选择适当的后端(通过配置使用 log4j 作为日志记录后端,还包括 log4j)。
例如,您可以在下面的代码中编写应用类文件:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HelloWorld
{
public static void main(String[] args)
{
Logger logger = LoggerFactory.getLogger(HelloWorld.class);
logger.info("Hello World");
}
}
现在,您只需选择运行时需要使用的日志记录框架即可。 为此,您将必须包含两个 jar 文件:
- SLF4j 绑定 jar 文件
- 所需的日志记录框架 jar 文件
例如,要在您的项目中使用 log4j,您将必须在给定的 jar 文件中包含以下内容:
slf4j-log4j12-1.7.12.jar
log4j-1.2.17.jar
将两个 jar 文件都放置在应用类路径中后,SLF4j 将自动检测到它,并开始使用 log4j 根据您在 log4j 配置文件中提供的配置来处理日志语句。
将来,如果您想用任何其他日志记录框架替换 log4j – 您要做的就是替换绑定和日志记录 jar 文件(以及配置文件)。 这简单。 无需更改实际的源代码文件。
因此,从本质上讲,SLF4J 不能替代 log4j,它们可以一起工作。 它从您的应用中删除了对 log4j 的依赖项,并在将来可以轻松地用功能更强大的库替换它。
我希望以上讨论对将来的我们有所帮助。
祝您学习愉快!
RESTEasy + Tomcat 7 + Log4j 日志记录示例
原文: https://howtodoinjava.com/resteasy/resteasy-tomcat-7-log4j-logging-example/
RESTEasy 在日志支持方面非常灵活。 它也可以与 log4j,slf4j 和java.util.logging
协作。 用于确定需要使用哪个日志记录框架的算法是:
- 如果 log4j 在应用的类路径中,则将使用 log4j
- 如果 slf4j 在应用的类路径中,则将使用 slf4j
- 如果 log4j 或 slf4j 都不在类路径中,则
java.util.logging
为默认值 - 如果 servlet 上下文参数
resteasy.logger.type
设置为 JUL,LOG4J 或 SLF4J 将覆盖此默认行为
在本文中,我们将学习在 tomcat 服务器中开发应用时使用 RESTEasy 进行 log4j 的开发。
使用的环境:
- Tomcat 7
- Log4j 1.2.17
- RESTEasy JAX-RS 2.3.1.GA
配置 log4j 的步骤
1)在项目中包含依赖项
我正在添加 Maven 依赖项。 如果需要,您可以选择包括 jar 文件。
<!-- core library -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<version>2.3.1.GA</version>
</dependency>
<dependency>
<groupId>net.sf.scannotation</groupId>
<artifactId>scannotation</artifactId>
<version>1.0.2</version>
</dependency>
<!-- Log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
2)在类路径中添加log4j.properties
文件
log4j 文件中的最低配置可以是:
log4j.rootLogger=DEBUG, consoleAppender, fileAppender
log4j.appender.consoleAppender=org.apache.log4j.ConsoleAppender
log4j.appender.consoleAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.consoleAppender.layout.ConversionPattern=[%t] %-5p %c %x - %m%n
log4j.appender.fileAppender=org.apache.log4j.RollingFileAppender
log4j.appender.fileAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.fileAppender.layout.ConversionPattern=[%t] %-5p %c %x - %m%n
log4j.appender.fileAppender.File=C:/logs/demoApplication.log
3)在 API 方法中使用日志语句
始终使用“org.jboss.resteasy.logging.Logger
”,因为它是使用上述给定算法进行配置的,因此完全将日志记录框架的依赖项与应用代码分离。 这意味着,如果您以后决定使用 slf4j 代替 log4j,则只需将 slf4j 放入运行时类路径中,然后从类路径中删除 log4j。 而已 !!
import org.jboss.resteasy.logging.Logger;
@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name = "user-management")
@Path("/user-management")
public class UserService
{
Logger log = Logger.getLogger(UserService.class);
@GET
@Path("/users/{id : \d+}")
public User getUserById(@PathParam("id") Integer id) {
log.info("GET API called for id : " + id);
User user = new User();
user.setId(id);
user.setFirstName("demo");
user.setLastName("user");
return user;
}
}
4)测试日志记录
调用上述 RESTFul API 会记录以下事件:
[http-bio-8080-exec-3] INFO org.jboss.resteasy.plugins.server.servlet.ConfigurationBootstrap - Adding scanned resource: com.howtodoinjava.service.UserService
[http-bio-8080-exec-3] DEBUG org.jboss.resteasy.core.SynchronousDispatcher - PathInfo: /user-management/users/10
[http-bio-8080-exec-3] INFO com.howtodoinjava.service.UserService - GET API called for id : 10
祝您学习愉快!
Dropwizard 教程
Dropwizard 教程
原文: https://howtodoinjava.com/dropwizard-tutorials/
该页面列出了此博客上发布的与 dropwizard 库相关的所有教程。
HelloWorld 示例
Dropwizard – HelloWorld 示例
让我们逐步学习如何使用 dropwizard 构建 REST API。 学习添加 REST 资源,表示形式和验证。
Dropwizard 客户端
Dropwizard – Jersey/HTTP 配置和示例
Dropwizard 包括 Apache HttpClient 和 Jersey 客户端。 让我们构建一个 REST 客户端,以通过网络使用 REST API。
Dropwizard 进阶主题
Dropwizard – 运行状况检查配置示例
DropWizard 运行状况检查是通过扩展HealthCheck
类并在一切正常的情况下返回Result.healthy()
以及在某些情况下无法返回 Result.unhealthy()
来实现的(如预期的那样)。
Dropwizard – BasicAuth 安全性示例
了解如何使用基本认证将基于用户名/密码的认证和基于角色的授权功能添加到 Dropwizard REST API 中。
相关话题
- Dropwizard – 无法解析配置(无法将类型 ID “http”解析为子类型)
- [已解决] Maven Shade 插件 – 缺少所需的类
org/apache/commons/io/IOUtils
参考资料:
https://www.dropwizard.io/en/stable/getting-started.html
Maven 依赖范围
原文: https://howtodoinjava.com/maven/maven-dependency-scopes/
Maven 依赖范围属性用于指定相对于不同生命周期阶段(构建,测试,运行时等)的依赖项可见性。 Maven 提供了六个范围,即compile
,provided
,runtime
,test
,system
和import
。
Table of Contents
1\. Compile Scope
2\. Provided Scope
3\. Runtime Scope
4\. Test Scope
5\. System Scope
6\. Import Scope
7\. Transitivity Resolution
Maven 依赖范围 – compile
这是 maven 默认范围。 构建,测试和运行项目需要具有compile
范围的依赖项。
在大多数情况下,必须使用范围compile
才能将import
语句解析为您的 Java 类源代码。
<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
<!-- You can ommit this because it is default -->
<scope>compile</scope>
</dependency>
</dependencies>
Maven 依赖范围 – provided
Maven 依赖范围provided
在构建和测试期间用于整个项目。 它们也需要运行,但是不应导出,因为依赖项将由运行时提供,例如,由 servlet 容器或应用服务器提供。
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
Maven 依赖范围 – runtime
不需要构建具有 maven 依赖范围runtime
的依赖项,但它们是测试和运行项目的类路径的一部分。
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.4</version>
<scope>runtime</scope>
</dependency>
Maven 依赖范围 – test
不需要使用 maven 依赖范围test
来构建和运行项目。 需要它们来编译和运行单元测试。
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
Maven 依赖范围 – system
具有system
的依赖项与具有范围provided
的依赖项相似。 唯一的区别是system
依赖项不是从远程仓库中检索到的。 它们位于项目的子目录下,并从那里进行引用。 有关更多详细信息,请参见外部依赖项。
<dependency>
<groupId>extDependency</groupId>
<artifactId>extDependency</artifactId>
<scope>system</scope>
<version>1.0</version>
<systemPath>${basedir}\war\WEB-INF\lib\extDependency.jar</systemPath>
</dependency>
Maven 依赖范围 – import
import
范围仅在dependencyManagement
部分中的类型为pom
的依赖项上受支持。 它在指定的 POM 的dependencyManagement
部分中指示要用有效的依赖项列表替换的依赖项。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>other.pom.group.id</groupId>
<artifactId>other-pom-artifact-id</artifactId>
<version>SNAPSHOT</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
Maven 依赖项可传递性解析
当您包含一个 maven 依赖项并且它具有它自己的其他依赖项(即传递性依赖项)时,您可能还想弄清楚这些传递性依赖项的范围。
让我们用一个简单的表来了解 maven 传递依赖项。 在此表中,如果在左列中将范围设置为依赖项,则在顶部行的可传递依赖项将导致在其交点处列出的范围。
依赖项 | 编译 | 提供 | 运行 | 测试 |
---|---|---|---|---|
compile |
compile |
– | runtime |
– |
provided |
provided |
– | provided |
– |
runtime |
runtime |
– | runtime |
– |
test |
test |
– | test |
– |
将我的问题放在评论部分。
学习愉快!
Dropwizard 教程 – HelloWorld 示例
原文: https://howtodoinjava.com/dropwizard/tutorial-and-hello-world-example/
Dropwizard 是用于快速开发 REST API 的开源 Java 框架。 Dropwizard 是一种生态系统,其中包含捆绑到单个程序包中的所有依赖项(例如,Jersey,Jackson 或 Jetty),也可以作为单独的模块添加。 如果不使用 dropwizard,最终将自己收集所有依赖项,由于各种 Java 库之间的版本不匹配,通常会导致类加载问题。 Dropwizard 为您解决了这个问题,并将稳定,成熟的库组合到一个简单,轻巧的程序包中,使您可以专注于完成工作。 让我们逐步使用来学习使用 dropwizard 构建 REST API。
Table of Contents
Libraries included inside dropwizard
Setup dropwizard with maven
Create REST Application Class
Create REST Resource and APIs
Build Resource Representations
Request Validation
Verify REST APIs
您将需要 Java8 来运行此代码中给出的示例,这些示例是使用 dropwizard 1.0.0 版开发的。
dropwizard 中包含的库
将 dropwizard 包含到项目中后,您将获得以下库添加到您的类路径中。
- Jersey – 用于构建 RESTful Web 应用。
- Jetty – Dropwizard 使用 Jetty HTTP 库将 HTTP 服务器直接嵌入到您的项目中。
- Jackson - 用于对象到 JSON 的转换。 它允许直接使用 JAXB 注解导出域模型。
- Guava – 高度优化的不可变数据结构,可加快开发速度。
- Logback 和 SLF4j – 用于高性能和灵活的日志记录。
- Hiberante 验证器 – 一个简单的声明性框架,用于验证用户输入并生成有用且对 i18n 友好的错误消息。
- Apache HTTPClient – 用于与其他 Web 服务的高层和高层交互。
- JDBI - 在 Java 中使用关系数据库的最直接方法。
- Liquidbase – 在整个开发和发布周期中,始终检查数据库模式。
- FreeMarker – 模板系统。
- Mustache – 适用于更多面向用户的应用的简单模板系统。
- Joda Time –非常完整和理智的库,用于处理日期和时间。
用 Maven 设置 dropwizard
我们的项目将基于maven-archetype-quickstart
原型。 您可以使用命令提示符来创建项目,或使用 eclipse 创建简单的 Maven Java 项目。
mvn archetype:generate -DgroupId=com.howtodoinjava.demo -DartifactId=DropWizardExample
-DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
生成的项目也将具有pom.xml
文件。 在此处添加 dropwizard 依赖项。
<properties>
<dropwizard.version>1.0.0</dropwizard.version>
</properties>
<dependencies>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-core</artifactId>
<version>${dropwizard.version}</version>
</dependency>
</dependencies>
这将下载所有 jar 文件并将它们添加到您的类路径中。 为了向我们的项目添加构建和程序包支持,我们将使用 maven-shade 插件,它将允许我们将我们的项目及其依赖项完全打包到一个独立的 JAR 文件中(胖/Uber JAR),可以按原样分发和执行。
完整的pom.xml
文件如下所示。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd;
<modelVersion>4.0.0</modelVersion>
<groupId>com.howtodoinjava.demo</groupId>
<artifactId>DropWizardExample</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>DropWizardExample</name>
<url>http://maven.apache.org</url>
<properties>
<dropwizard.version>1.0.0</dropwizard.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-core</artifactId>
<version>${dropwizard.version}</version>
</dependency>
</dependencies>
<build>
<finalName>DropWizardExample-${version}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.howtodoinjava.rest.App</mainClass>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer">
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
创建 REST 应用类
应用类是任何 dropwizard 应用的入口点。 它需要扩展io.dropwizard.Application
类并实现initialize(Bootstrap<Configuration>)
和run(Configuration, Environment)
方法。 他们准备应用的运行时环境。
要调用run
方法,您需要具有public static void main(String[] args) {}
方法,当您将应用作为 jar 文件运行时,该方法将由java -jar
命令调用。
package com.howtodoinjava.rest;
import io.dropwizard.Application;
import io.dropwizard.Configuration;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.howtodoinjava.rest.controller.EmployeeRESTController;
public class App extends Application<Configuration> {
private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
@Override
public void initialize(Bootstrap<Configuration> b) {
}
@Override
public void run(Configuration c, Environment e) throws Exception {
LOGGER.info("Registering REST resources");
e.jersey().register(new EmployeeRESTController(e.getValidator()));
}
public static void main(String[] args) throws Exception {
new App().run(args);
}
}
为了执行 JAR 文件,我们在命令中添加服务器参数,该参数启动嵌入式 HTTP 服务器(Jetty)以运行我们的服务。
java -jar target\DropWizardExample.jar server
Dropwizard 的嵌入式 Jetty 服务器将默认尝试绑定到端口 8080 和 8081 。 服务器使用端口 8080 来向应用提供传入的 HTTP 请求,而 Dropwizard 的管理界面则使用 8081 端口。
我们还导入了必要的Logger
和LoggerFactory
类,以构造可用于日志记录需求的Logger
实例。
创建 REST 资源和 API
现在,当您添加了应用引导类后,现在可以添加包含 REST API 的 REST 资源。 在此示例中,我创建并创建了员工管理应用 - 因此它具有用于创建/更新/删除员工记录的 API。 此类将负责处理 HTTP 请求并生成 JSON 响应。
由于类路径中包含 Jersey,因此我们将使用它来构建 REST API。
package com.howtodoinjava.rest.controller;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import com.howtodoinjava.rest.dao.EmployeeDB;
import com.howtodoinjava.rest.representations.Employee;
@Path("/employees")
@Produces(MediaType.APPLICATION_JSON)
public class EmployeeRESTController {
private final Validator validator;
public EmployeeRESTController(Validator validator) {
this.validator = validator;
}
@GET
public Response getEmployees() {
return Response.ok(EmployeeDB.getEmployees()).build();
}
@GET
@Path("/{id}")
public Response getEmployeeById(@PathParam("id") Integer id) {
Employee employee = EmployeeDB.getEmployee(id);
if (employee != null)
return Response.ok(employee).build();
else
return Response.status(Status.NOT_FOUND).build();
}
@POST
public Response createEmployee(Employee employee) throws URISyntaxException {
// validation
Set<ConstraintViolation<Employee>> violations = validator.validate(employee);
Employee e = EmployeeDB.getEmployee(employee.getId());
if (violations.size() > 0) {
ArrayList<String> validationMessages = new ArrayList<String>();
for (ConstraintViolation<Employee> violation : violations) {
validationMessages.add(violation.getPropertyPath().toString() + ": " + violation.getMessage());
}
return Response.status(Status.BAD_REQUEST).entity(validationMessages).build();
}
if (e != null) {
EmployeeDB.updateEmployee(employee.getId(), employee);
return Response.created(new URI("/employees/" + employee.getId()))
.build();
} else
return Response.status(Status.NOT_FOUND).build();
}
@PUT
@Path("/{id}")
public Response updateEmployeeById(@PathParam("id") Integer id, Employee employee) {
// validation
Set<ConstraintViolation<Employee>> violations = validator.validate(employee);
Employee e = EmployeeDB.getEmployee(employee.getId());
if (violations.size() > 0) {
ArrayList<String> validationMessages = new ArrayList<String>();
for (ConstraintViolation<Employee> violation : violations) {
validationMessages.add(violation.getPropertyPath().toString() + ": " + violation.getMessage());
}
return Response.status(Status.BAD_REQUEST).entity(validationMessages).build();
}
if (e != null) {
employee.setId(id);
EmployeeDB.updateEmployee(id, employee);
return Response.ok(employee).build();
} else
return Response.status(Status.NOT_FOUND).build();
}
@DELETE
@Path("/{id}")
public Response removeEmployeeById(@PathParam("id") Integer id) {
Employee employee = EmployeeDB.getEmployee(id);
if (employee != null) {
EmployeeDB.removeEmployee(id);
return Response.ok().build();
} else
return Response.status(Status.NOT_FOUND).build();
}
}
为了模仿数据库,我创建了EmployeeDB
类,该类将员工记录和更新存储在内存中。
package com.howtodoinjava.rest.dao;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import com.howtodoinjava.rest.representations.Employee;
public class EmployeeDB {
public static HashMap<Integer, Employee> employees = new HashMap<>();
static{
employees.put(1, new Employee(1, "Lokesh", "Gupta", "India"));
employees.put(2, new Employee(2, "John", "Gruber", "USA"));
employees.put(3, new Employee(3, "Melcum", "Marshal", "AUS"));
}
public static List<Employee> getEmployees(){
return new ArrayList<Employee>(employees.values());
}
public static Employee getEmployee(Integer id){
return employees.get(id);
}
public static void updateEmployee(Integer id, Employee employee){
employees.put(id, employee);
}
public static void removeEmployee(Integer id){
employees.remove(id);
}
}
建立资源表示
表示是保存数据并序列化为 JSON 的内容。 它是 RESTful 应用的模型。 当将 Jersey 与 Jackson 结合使用时,构建资源表示形式所需的全部就是 – 遵循 Java bean 标准的简单 POJO。 Jackson 根据每个类的设置器方法及其返回类型来递归构造 JSON 字符串。
任何java.util.List
类型的实例都将转换为 JSON 数组。
package com.howtodoinjava.rest.representations;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.NotBlank;
public class Employee {
@NotNull
private Integer id;
@NotBlank @Length(min=2, max=255)
private String firstName;
@NotBlank @Length(min=2, max=255)
private String lastName;
@Pattern(regexp=".+@.+\\.[a-z]+")
private String email;
public Employee(){
}
public Employee(Integer id, String firstName, String lastName, String email) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "Emplyee [id=" + id + ", firstName=" + firstName + ", lastName="
+ lastName + ", email=" + email + "]";
}
}
如果在某些情况下需要,可以通过将@JsonIgnore
注解添加到其设置器来防止该属性成为 JSON 表示的一部分。
请求验证
接受PUT
和POST
请求时,您需要在请求正文中验证用户提交的实体内容。 Dropwizard 为此使用 Hiberate 验证器。 添加验证需要执行以下步骤。
-
在表示形式类中添加验证注解
@NotNull private Integer id; @NotBlank @Length(min=2, max=255) private String firstName; @NotBlank @Length(min=2, max=255) private String lastName; @Pattern(regexp=".+@.+\\.[a-z]+") private String email;
-
在应用的 REST 资源中注入
Environment.getValidator()
@Override
public void run(Configuration c, Environment e) throws Exception
{
LOGGER.info("Registering REST resources");
e.jersey().register(new EmployeeRESTController(e.getValidator()));
}
-
在 REST 资源中使用验证器
public class EmployeeRESTController { private final Validator validator; public EmployeeRESTController(Validator validator) { this.validator = validator; } @POST public Response createEmployee(Employee employee) throws URISyntaxException { Set<ConstraintViolation<Employee>> violations = validator.validate(employee); Employee e = EmployeeDB.getEmployee(employee.getId()); if (violations.size() > 0) { ArrayList<String> validationMessages = new ArrayList<String>(); for (ConstraintViolation<Employee> violation : violations) { validationMessages.add(violation.getPropertyPath().toString() + ": " + violation.getMessage()); } return Response.status(Status.BAD_REQUEST).entity(validationMessages).build(); } //Create Employee code here } }
验证 REST API
现在,当我们创建并添加了 REST API 的验证后,让我们对其进行测试。
构建应用 uber jar 文件
> mvn clean package
在 Jetty 服务器中启动应用
> java -jar target\DropWizardExample-0.0.1-SNAPSHOT.jar server
访问 URI http://localhost:8080/employees
这将返回员工集合和相关的响应头。
Dropwizard – GET 请求示例 – 1
访问 URI http://localhost:8080/employees/1
这将返回 ID 为 1 的员工记录。
Dropwizard – GET 请求示例 – 2
使用无效的请求数据发送 HTTP PUT http://localhost:8080/employees/1
您将收到验证消息。
Dropwizard – 验证示例
使用正确的数据发送 HTTP PUT http://localhost:8080/employees/1
员工记录将成功更新。
Dropwizard – PUT 请求示例
同样,您可以测试其他 API 和方案。
源码下载
在评论部分让我知道您的问题。
学习愉快!
Dropwizard – BasicAuth 安全示例
原文: https://howtodoinjava.com/dropwizard/dropwizard-basic-auth-security-example/
使用 dropwizard,我们了解了创建 REST API ,编写客户端代码和添加运行状况检查过滤器的知识。 在本教程中,我们将学习使用基本认证将基于用户名/密码的认证和基于基于角色的授权功能添加到 REST API 中。
Table of Contents
Include Dropwizard Auth Module Maven Dependency
Add Custom Principal Object
Add Custom Authenticator
Add Custom Authorizer
Configure BasicCredentialAuthFilter
Secure REST APIs with @Auth Annotation
Test Dropwizard Basic Auth Code
包含 Dropwizard Auth 模块的 Maven 依赖项
认证功能在 dropwizard 应用中作为单独的模块添加。
<properties>
<dropwizard.version>1.0.0</dropwizard.version>
</properties>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-auth</artifactId>
<version>${dropwizard.version}</version>
</dependency>
添加自定义主体对象
在安全性方面,主体对象表示已为其提供凭据的用户。 它实现了java.security.Principal
接口。
package com.howtodoinjava.rest.auth;
import java.security.Principal;
import java.util.Set;
public class User implements Principal {
private final String name;
private final Set<String> roles;
public User(String name) {
this.name = name;
this.roles = null;
}
public User(String name, Set<String> roles) {
this.name = name;
this.roles = roles;
}
public String getName() {
return name;
}
public int getId() {
return (int) (Math.random() * 100);
}
public Set<String> getRoles() {
return roles;
}
}
添加自定义认证器
Authenticator
类负责验证基本认证标头中包含的用户名/密码凭证。 在企业应用中,您可以从数据库中获取用户密码,如果密码匹配,则将用户角色设置为主体对象。 在 dropwizard 中,您将需要实现io.dropwizard.auth.Authenticator
接口以放置您的应用逻辑。
package com.howtodoinjava.rest.auth;
import io.dropwizard.auth.AuthenticationException;
import io.dropwizard.auth.Authenticator;
import io.dropwizard.auth.basic.BasicCredentials;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
public class AppBasicAuthenticator implements Authenticator<BasicCredentials, User>
{
private static final Map<String, Set<String>> VALID_USERS = ImmutableMap.of(
"guest", ImmutableSet.of(),
"user", ImmutableSet.of("USER"),
"admin", ImmutableSet.of("ADMIN", "USER")
);
@Override
public Optional<User> authenticate(BasicCredentials credentials) throws AuthenticationException
{
if (VALID_USERS.containsKey(credentials.getUsername()) && "password".equals(credentials.getPassword()))
{
return Optional.of(new User(credentials.getUsername(), VALID_USERS.get(credentials.getUsername())));
}
return Optional.empty();
}
}
添加自定义授权器
Authorizer
类负责匹配角色,并确定是否允许用户执行某些操作。
package com.howtodoinjava.rest.auth;
import io.dropwizard.auth.Authorizer;
public class AppAuthorizer implements Authorizer<User>
{
@Override
public boolean authorize(User user, String role) {
return user.getRoles() != null && user.getRoles().contains(role);
}
}
配置BasicCredentialAuthFilter
现在,让我们将自定义类注册到 dropwizard 安全框架中。
package com.howtodoinjava.rest;
import io.dropwizard.Application;
import io.dropwizard.Configuration;
import io.dropwizard.auth.AuthDynamicFeature;
import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.auth.basic.BasicCredentialAuthFilter;
import io.dropwizard.client.JerseyClientBuilder;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;
import javax.ws.rs.client.Client;
import org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature;
import com.howtodoinjava.rest.auth.AppAuthorizer;
import com.howtodoinjava.rest.auth.AppBasicAuthenticator;
import com.howtodoinjava.rest.auth.User;
import com.howtodoinjava.rest.controller.EmployeeRESTController;
import com.howtodoinjava.rest.controller.RESTClientController;
import com.howtodoinjava.rest.healthcheck.AppHealthCheck;
import com.howtodoinjava.rest.healthcheck.HealthCheckController;
public class App extends Application<Configuration> {
@Override
public void initialize(Bootstrap<Configuration> b) {
}
@Override
public void run(Configuration c, Environment e) throws Exception {
e.jersey().register(new EmployeeRESTController(e.getValidator()));
final Client client = new JerseyClientBuilder(e).build("DemoRESTClient");
e.jersey().register(new RESTClientController(client));
// Application health check
e.healthChecks().register("APIHealthCheck", new AppHealthCheck(client));
// Run multiple health checks
e.jersey().register(new HealthCheckController(e.healthChecks()));
//****** Dropwizard security - custom classes ***********/
e.jersey().register(new AuthDynamicFeature(new BasicCredentialAuthFilter.Builder<User>()
.setAuthenticator(new AppBasicAuthenticator())
.setAuthorizer(new AppAuthorizer())
.setRealm("BASIC-AUTH-REALM")
.buildAuthFilter()));
e.jersey().register(RolesAllowedDynamicFeature.class);
e.jersey().register(new AuthValueFactoryProvider.Binder<>(User.class));
}
public static void main(String[] args) throws Exception {
new App().run(args);
}
}
具有@Auth
注解的安全 REST API
添加@Auth
注解将在将其作为参数的任何 API 上触发认证过滤器。
1)用户必须经过验证。 允许所有用户使用 API。
@PermitAll
@GET
public Response getEmployees(@Auth User user) {
return Response.ok(EmployeeDB.getEmployees()).build();
}
2)用户必须经过验证。 仅角色为ADMIN
的所有用户都可以使用 API。
@RolesAllowed({ "ADMIN" })
@GET
@Path("/{id}")
public Response getEmployeeById(@PathParam("id") Integer id, @Auth User user) {
Employee employee = EmployeeDB.getEmployee(id);
if (employee != null)
return Response.ok(employee).build();
else
return Response.status(Status.NOT_FOUND).build();
}
这样,您可以根据需要在所有 API 中添加各种认证方案。
测试 Dropwizard 基本验证代码
让我们测试一下我们的安全 API。
调用任何安全的 API
基本认证屏幕
http://localhost:8080/employees
经过认证并允许所有角色
http://localhost:8080/employees/1
经过认证并仅允许ADMIN
角色
将我的问题放在评论部分。
学习愉快!
源码下载
Dropwizard 运行状况检查配置示例
原文: https://howtodoinjava.com/dropwizard/health-check-configuration-example/
我们已经看到 dropwizard 在开发自包含的 REST API 甚至 REST 客户端服务方面如此有效。 Dropwizard 包含几乎所有必需的包,它们可以非常简单地构建 API,而无需使事情复杂化。 dropwizard 的一项易于实现的功能是运行状况检查服务,该服务可用于在运行时监视正在创建的应用/组件的状态。
实现 Dropwizard 运行状况检查
DropWizard 运行状况检查是通过扩展HealthCheck
类并在一切正常的情况下返回Result.healthy()
以及在某些情况下无效的情况下返回Result.unhealthy()
来实现的 - 符合预期。
运行状况检查配置
public class AppHealthCheck extends HealthCheck
{
@Override
protected Result check() throws Exception
{
if(Check some condition == true){
return Result.healthy();
}
return Result.unhealthy("Error message");
}
}
要在 dropwizard 应用中注册此AppHealthCheck
类,请使用Environment.healthChecks()
注册表。
public void run(Configuration c, Environment e) throws Exception
{
//Application health check
e.healthChecks().register("APIHealthCheck", new AppHealthCheck());
}
验证系统运行状况
Dropwizard 将在管理端口上的/healthcheck
上使用 HTTP 资源终结点(默认为 8081)。 在默认情况下,Dropwizard 还包括对死锁的检查以及您定义的自定义运行状况检查AppHealthCheck
。
http://localhost:8081/healthcheck
上面的运行状况检查网址将返回一些结果,如下所示:
{
"APIHealthCheck": {
"healthy": true
},
"deadlocks": {
"healthy": true
}
}
自定义 REST 资源以运行运行状况检查
如果您不想使用管理端口,则还可以创建一个自定义 REST 资源,该资源将为您运行运行状况检查,并以所需的响应格式返回结果。
要运行所有运行状况检查并获取所有结果,您将在 REST 资源中调用registry.runHealthChecks()
。
@Produces(MediaType.APPLICATION_JSON)
@Path("/status")
public class HealthCheckController
{
private HealthCheckRegistry registry;
public HealthCheckController(HealthCheckRegistry registry) {
this.registry = registry;
}
@GET
public Set<Entry<String, Result>> getStatus(){
return registry.runHealthChecks().entrySet();
}
}
现在,当我们使用http://localhost:8080/status
调用此 REST API 时,我们将得到如下响应:
[
{
"APIHealthCheck": {
"healthy": true,
"message": null,
"error": null
}
},
{
"deadlocks": {
"healthy": true,
"message": null,
"error": null
}
}
]
您可以根据需要自定义消息。
Dropwizard 运行状况检查示例
为了演示上述两种功能,我修改了 dropwizard HelloWorld 应用中给出的代码。
AppHealthCheck.java
package com.howtodoinjava.healthcheck;
import java.util.ArrayList;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import com.codahale.metrics.health.HealthCheck;
public class AppHealthCheck extends HealthCheck {
private final Client client;
public AppHealthCheck(Client client) {
super();
this.client = client;
}
@Override
protected Result check() throws Exception {
WebTarget webTarget = client.target("http://localhost:8080/employees");
Invocation.Builder invocationBuilder = webTarget.request(MediaType.APPLICATION_JSON);
Response response = invocationBuilder.get();
@SuppressWarnings("rawtypes")
ArrayList employees = response.readEntity(ArrayList.class);
if(employees !=null && employees.size() > 0){
return Result.healthy();
}
return Result.unhealthy("API Failed");
}
}
HealthCheckController.java
package com.howtodoinjava.healthcheck;
import java.util.Map.Entry;
import java.util.Set;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import com.codahale.metrics.health.HealthCheck.Result;
import com.codahale.metrics.health.HealthCheckRegistry;
@Produces(MediaType.APPLICATION_JSON)
@Path("/status")
public class HealthCheckController
{
private HealthCheckRegistry registry;
public HealthCheckController(HealthCheckRegistry registry) {
this.registry = registry;
}
@GET
public Set<Entry<String, Result>> getStatus(){
return registry.runHealthChecks().entrySet();
}
}
App.java
package com.howtodoinjava.rest;
import io.dropwizard.Application;
import io.dropwizard.Configuration;
import io.dropwizard.client.JerseyClientBuilder;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;
import javax.ws.rs.client.Client;
import com.howtodoinjava.healthcheck.AppHealthCheck;
import com.howtodoinjava.healthcheck.HealthCheckController;
import com.howtodoinjava.rest.controller.EmployeeRESTController;
import com.howtodoinjava.rest.controller.RESTClientController;
public class App extends Application<Configuration> {
@Override
public void initialize(Bootstrap<Configuration> b) {
}
@Override
public void run(Configuration c, Environment e) throws Exception
{
e.jersey().register(new EmployeeRESTController(e.getValidator()));
final Client client = new JerseyClientBuilder(e).build("DemoRESTClient");
e.jersey().register(new RESTClientController(client));
//Application health check
e.healthChecks().register("APIHealthCheck", new AppHealthCheck(client));
//Run multiple health checks
e.jersey().register(new HealthCheckController(e.healthChecks()));
}
public static void main(String[] args) throws Exception {
new App().run(args);
}
}
验证运行状况检查 URL
1)http://localhost:8081/healthcheck
在管理端口上进行 Dropwizard 运行状况检查
2)http://localhost:8080/status
在应用端口上进行 Dropwizard 运行状况检查
在评论部分让我知道您的问题。
学习愉快!
源码下载
参考:
Dropwizard 文档
Dropwizard 客户端 – Jersey/HTTP 配置和示例
原文: https://howtodoinjava.com/dropwizard/client-configuration-and-examples/
我们已经使用 dropwizard 构建了 REST API。 现在,让我们构建 REST 客户端,以在整个网络上使用 REST API。 Dropwizard 同时包含 Apache HttpClient 和 Jersey 客户端。 让我们来构建它们。
阅读更多: Dropwizard HelloWorld 应用
Maven 依赖
Dropwizard 客户端模块被添加为单独的模块。
<properties>
<dropwizard.version>1.0.0</dropwizard.version>
</properties>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-client</artifactId>
<version>${dropwizard.version}</version>
</dependency>
Dropwizard REST 客户端配置
Dropwizard 提供易于声明和使用的 REST 客户端配置。 您需要创建io.dropwizard.client.JerseyClientBuilder
实例并为其提供io.dropwizard.setup.Environment
参考。
@Override
public void run(Configuration c, Environment e) throws Exception {
//Here we added REST Resource
e.jersey().register(new EmployeeRESTController(e.getValidator()));
//Now we added REST Client Resource named RESTClientController
final Client client = new JerseyClientBuilder(e).build("DemoRESTClient");
e.jersey().register(new RESTClientController(client));
}
要添加 HTTP 客户端,请使用以下类似的步骤:
@Override
public void run(Configuration c, Environment e) throws Exception {
//Here we added REST Resource
e.jersey().register(new EmployeeRESTController(e.getValidator()));
//Now we added REST Client Resource named RESTClientController
final HttpClient client = new HttpClientBuilder(e).build("DemoRESTClient");
e.jersey().register(new RESTClientController(client));
}
HttpClientConfiguration
的默认配置如下:
timeout: 500ms
connectionTimeout: 500ms
timeToLive: 1 hour
cookiesEnabled: false
maxConnections: 1024
maxConnectionsPerRoute: 1024
keepAlive: 0s
JerseyClientConfiguration
的默认配置如下:
minThreads: 1
maxThreads: 128
gzipEnabled: true
gzipEnabledForRequests: true
//same as HttpClientConfiguration
timeout: 500ms
connectionTimeout: 500ms
timeToLive: 1 hour
cookiesEnabled: false
maxConnections: 1024
maxConnectionsPerRoute: 1024
keepAlive: 0s
Dropwizard REST 客户端资源
现在,当您可以访问 REST 客户端资源RESTClientController.java
中的javax.ws.rs.client.Client
或org.apache.http.client.HttpClient
时,您可以使用库方法来照常调用 HTTP URI。
package com.howtodoinjava.rest.controller;
import java.util.ArrayList;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import com.howtodoinjava.rest.representations.Employee;
@Produces(MediaType.TEXT_PLAIN)
@Path("/client/")
public class RESTClientController
{
private Client client;
public RESTClientController(Client client) {
this.client = client;
}
@GET
@Path("/employees/")
public String getEmployees()
{
//Do not hard code in your application
WebTarget webTarget = client.target("http://localhost:8080/employees");
Invocation.Builder invocationBuilder = webTarget.request(MediaType.APPLICATION_JSON);
Response response = invocationBuilder.get();
@SuppressWarnings("rawtypes")
ArrayList employees = response.readEntity(ArrayList.class);
return employees.toString();
}
@GET
@Path("/employees/{id}")
public String getEmployeeById(@PathParam("id") int id)
{
//Do not hard code in your application
WebTarget webTarget = client.target("http://localhost:8080/employees/"+id);
Invocation.Builder invocationBuilder = webTarget.request(MediaType.APPLICATION_JSON);
Response response = invocationBuilder.get();
Employee employee = response.readEntity(Employee.class);
return employee.toString();
}
}
在上面的类中,我访问了在 dropwizard HelloWorld 教程中创建的 REST API。
访问 API 之后,我以纯文本形式返回了响应,如下图所示。
DropWizard REST 客户端
我已将客户端资源类的上下文路径设置为/client/
,以在逻辑上分离客户端端点和服务端点的 URI。
阅读更多 :
Jersey RESTful 客户端示例
Apache HttpClient GET/POST 请求示例
源码下载
将我的问题放在评论部分。
学习愉快!
[已解决] Dropwizard – 无法解析配置(无法将类型 ID “http”解析为子类型)
原文: https://howtodoinjava.com/dropwizard/solved-dropwizard-failed-parse-configuration-not-resolve-type-id-http-subtype/
如果您使用 dropwizard 构建 REST API,并且在启动 Jetty 服务器时遇到此错误,则给定的解决方案可能会对您有所帮助。
问题
启动服务器时,您会收到此错误。
>java -jar target\DropWizardExample-0.0.1-SNAPSHOT.jar server
default configuration has an error:
* Failed to parse configuration at: server.applicationConnectors.[0]; Could not resolve type id 'http' into a subtype of [simple type, class io.dropwizard.jetty.ConnectorFactory]: known type ids = [ConnectorFactory]
at [Source: N/A; line: -1, column: -1] (through reference chain: io.dropwizard.Configuration["server"]->io.dropwizard.server.DefaultServerFactory["applicationConnectors"]->java.util.ArrayList[0])
解决方案
上面的异常是因为您在 Maven shade 插件中没有使用 ServicesResourceTransformer
。 它将META-INF/services
中的类重新放置,并将META-INF/services
资源中的条目追加到单个资源中。
请更正 shade 插件,您会感觉很好。
我的pom.xml
非常适合我。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd;
<modelVersion>4.0.0</modelVersion>
<groupId>com.howtodoinjava.demo</groupId>
<artifactId>DropWizardExample</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>DropWizardExample</name>
<url>http://maven.apache.org</url>
<properties>
<dropwizard.version>1.0.0</dropwizard.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-core</artifactId>
<version>${dropwizard.version}</version>
</dependency>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-client</artifactId>
<version>${dropwizard.version}</version>
</dependency>
</dependencies>
<build>
<finalName>DropWizardExample-${version}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.howtodoinjava.rest.App</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
让我知道它是否可以解决您的问题。
学习愉快!
RESTEasy 教程
JAX-RS 2.0 教程
原文: https://howtodoinjava.com/restful-web-service/
JAX-RS 2.0 是一个旨在帮助您在客户端和服务器端编写 RESTful 应用的框架。 以下教程是使用 RESTEasy 框架构建的。
在 Jersey 和 Dropwizard 页面上获得更多 JAX-RS 教程。
HelloWorld 应用
RESTEasy + JBOSS 7 HelloWorldd 应用
了解在 JBOSS AS7 中构建 RESTful Web 应用所需的最基本配置。
RESTEasy + Tomcat HelloWorld 应用
了解使用 Tomcat 服务器构建 RESTful Web 应用所需的最低配置。
REST 客户端
使用java.net
包的 RESTful 客户端
使用java.net package
构建 Restful 客户端以使用 Restful API。
RESTEasy 客户端示例
使用内置的 jax-rs 功能构建 RESTful 客户端以使用 RESTful API。
Apache HttpClient 示例
使用 apache http 客户端框架构建 Restful 客户端以使用 Restful API。
Ajax/JavaScript 客户端示例
学习构建 resteasy ajax 客户端
JAX-RS 2.0 客户端 API 示例
JAX-RS 2.0 在以前的版本中带来了很多改进。 主要改进之一是客户端 API,它在 JAX-RS 1.0 中完全丢失。 在本教程中学习使用。
XML / JSON 支持
RESTEasy + JAXB xml 示例
学习使用 JAXB 在 XML 文档中封送模型对象,并将其作为服务器响应发送给客户端
RESTEasy + Jettison json 示例
使用 Jettison 学习在 JSON 文档中封送模型对象,并将其作为服务器响应发送给客户端
RESTEasy + Jackson JSON 示例
学习使用 Jackson 封送 JSON 文档中的模型对象,并将其作为服务器响应发送给客户端
文件下载/上传
RESTEasy + 文件下载示例
此示例显示了使用 RESTful API 下载各种文件格式所需的配置和代码
RESTEasy + 文件上传 + HttpClient 纯 Java 客户端示例
使用纯 Java 客户端将文件上传到服务器
RESTEasy + 文件上传 + HTML 表单示例
使用 html 表单提交将文件上传到服务器
注解用法和示例
基于 JAX-RS @Path
正则表达式的 URI 匹配
使用@Path
注解的 API 路径匹配示例。 还包括一些正则表达式示例。
Hateoas 实现
RESTful Web 服务的 HATEOAS 示例
学习启用 HATEOAS 链接来制作 RESTful Web 服务
JAX-RS 安全
使用PreProcessorInterceptor
的基本认证和授权示例
使用PreProcessorInterceptor
实现安全性。
JAX-RS 2.0 RESTEasy 3.0.2.Final 使用ContainerRequestFilter
的安全性示例
PreProcessorInterceptor
和PostProcessorInterceptor
现在已过时。 因此,从现在开始,使用ContainerRequestFilter
和ContainerReponseFilter
。 在本教程中学习使用它们。
REST 安全性指南
保护 RESTful Web 服务的知识与编写它们一样重要。 编写安全的 API 以保护业务非常重要。 但是在开始保护 RESTful API 之前,让我们了解作为开发人员我们有哪些选择? 什么将最适合我们的用例?
请求验证
使用 Ajax 自定义 HTML 表单
为您的网页构建 Ajax 支持的 html 表单验证功能
Hiberate 验证器供应器演示
将 Hiberate bean 验证功能集成到您的 RESTEasy API 中
各种整合
使用 Spring 3 mvc 的 RESTful Web 服务
了解如何使用 Spring 3 框架编写 RESTful Web 服务
日志支持
RESTEasy + Log4j 示例
在 RESTEasy 应用中添加 log4j 日志记录支持的演示配置
RESTEasy + SLF4j 示例
演示配置以在 RESTEasy 应用中添加 slf4j 日志支持
最佳实践
使用ResteasyProviderFactory
共享上下文数据
当您想在应用的各个层中共享某些数据而不将其作为方法参数传递时,ResteasyProviderFacory
可以证明非常方便
使用自定义异常映射器进行异常处理
构建自定义异常映射器以处理各种异常的应用特定处理
启用 gzip 压缩内容编码
JAX-RS Resteasy 具有自动 GZIP 解压缩支持。 学习使用它。
带有 ETag 示例的 JAX-RS RESTEasy 缓存控制
ETags 或实体标签是有用的 HTTP 标头,可通过最小化系统上的服务器负载来帮助构建超快速的应用。 ETag 设置为对客户端的响应,因此客户端可以对条件请求使用各种控制请求标头,例如If-Match
和If-None-Match
。 javax.ws.rs.core.Response.ResponseBuilder#tag
和javax.ws.rs.core.EntityTag
是用于处理 ETags 的有用类。
RESTful Web 服务的资源
- Jboss RESTEasy 主页
- JBoss 服务器
- Tomcat 服务器
- JAX-RS 2 规范
- Jettison 库
- Jackson 库
RESTEasy + JBOSS 7 HelloWorld 应用
原文: https://howtodoinjava.com/resteasy/resteasy-jboss-7-hello-world-application/
RESTEasy 是 JBOSS 提供的 JAX-RS 规范的实现,用于构建 RESTful Web 服务 和 RESTful Java 应用。 尽管这不仅限于仅在 JBOSS 中使用,您还可以与其他服务器协作。 在本文中,我将在 JBOSS AS7 服务器中构建这样的 HelloWorld 应用。
使用的环境:
- RESTEasy 2.3.1.GA
- Jboss AS7
- JDK 1.6
请按照以下步骤构建演示应用。
1)创建一个 Maven 项目并转换为 Eclipse Web 项目
mvn archetype:generate -DgroupId=com.howtodoinjava -DartifactId=RESTfulDemoApplication
-DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false
mvn eclipse:eclipse -Dwtpversion=2.0
2)更新pom.xml
中的运行时依赖项
您只需要定义编译时间相关性。 更好的是,如果您可以识别并包含 jboss 分发包中的 jar。
<project xmlns="http://maven.apache.org/POM/4.0.0" 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.howtodoinjava</groupId>
<artifactId>RESTfulDemoApplication</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>RESTfulDemoApplication Maven Webapp</name>
<url>http://maven.apache.org</url>
<repositories>
<repository>
<id>jboss</id>
<url>http://repository.jboss.org/maven2</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!-- core library -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<version>2.3.1.GA</version>
<scope>compile</scope>
</dependency>
<!-- JAXB support -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxb-provider</artifactId>
<version>2.3.1.GA</version>
<scope>compile</scope>
</dependency>
<!-- multipart/form-data and multipart/mixed support -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-multipart-provider</artifactId>
<version>2.3.1.GA</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.sf.scannotation</groupId>
<artifactId>scannotation</artifactId>
<version>1.0.2</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<finalName>RESTfulDemoApplication</finalName>
</build>
</project>
3)创建一个空白的web.xml
文件
JBOSS 的内置支持 RESTeasy 使它成为 RESTFul Web 应用的完美组合。 构建此类应用的最低配置为无。 是的,一个空白的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>Restful Web Application</display-name>
</web-app>
5)注册应用路径
您将需要扩展javax.ws.rs.core.Application
类并提供@ApplicationPath
注解。
<img src="//howtodoinjava.com/wp-content/uploads/jboss+resteasy1.png" alt="JBOSS 7+ RESTEasy demo application" width="453" height="225" class="size-full wp-image-2033" /> JBOSS 7+ RESTEasy demo applicationpackage com.howtodoinjava;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
import com.howtodoinjava.service.UserService;
@ApplicationPath("/")
public class ApplicationConfig extends Application {
@SuppressWarnings("unchecked")
public Set<Class<?>> getClasses() {
return new HashSet<Class<?>>(Arrays.asList(UserService.class));
}
}
4)编写具有@Path
注解的服务类
package com.howtodoinjava.service;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import com.howtodoinjava.model.User;
import com.howtodoinjava.model.Users;
@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name = "user-management")
@Path("/user-management")
public class UserService {
@XmlElement(name = "users")
private String uri1 = "/user-management/users";
@XmlElement(name = "report")
private String uri2 = "/user-managemet/generate-report";
public String getUri1() {
return uri1;
}
public void setUri1(String uri1) {
this.uri1 = uri1;
}
public String getUri2() {
return uri2;
}
public void setUri2(String uri2) {
this.uri2 = uri2;
}
@GET
@Path("/")
@Produces("application/vnd.com.demo.user-management+xml;charset=UTF-8;version=1")
public UserService getServiceInfo() {
return new UserService();
}
@GET
@Path("/users")
@Produces("application/vnd.com.demo.user-management.users+xml;charset=UTF-8;version=1")
public Users getAllUsers() {
User user1 = new User();
user1.setId(1);
user1.setFirstName("demo");
user1.setLastName("user");
user1.setUri("/user-management/users/1");
User user2 = new User();
user2.setId(2);
user2.setFirstName("demo");
user2.setLastName("user");
user2.setUri("/user-management/users/2");
Users users = new Users();
users.setUsers(new ArrayList<User>());
users.getUsers().add(user1);
users.getUsers().add(user2);
return users;
}
@GET
@Path("/users/{id}")
@Produces("application/vnd.com.demo.user-management.user+xml;charset=UTF-8;version=1")
public User getUserById(@PathParam("id") int id) {
User user = new User();
user.setId(id);
user.setFirstName("demo");
user.setLastName("user");
user.setUri("/user-management/users/" + id);
return user;
}
@POST
@Path("/users")
@Consumes("application/vnd.com.demo.user-management.user+xml;charset=UTF-8;version=1")
public Response createUser(User user,
@DefaultValue("false") @QueryParam("allow-admin") boolean allowAdmin)
throws URISyntaxException {
System.out.println(user.getFirstName());
System.out.println(user.getLastName());
return Response.status(201)
.contentLocation(new URI("/user-management/users/123")).build();
}
@PUT
// @Path("/users/{id: [0-9]*}")
@Path("/users/{id}")
@Consumes("application/vnd.com.demo.user-management.user+xml;charset=UTF-8;version=1")
@Produces("application/vnd.com.demo.user-management.user+xml;charset=UTF-8;version=1")
public User updateUser(@PathParam("id") int id, User user)
throws URISyntaxException {
user.setId(id);
user.setFirstName(user.getFirstName() + "updated");
return user;
}
@DELETE
@Path("/users/{id}")
public Response deleteUser(@PathParam("id") int id)
throws URISyntaxException {
return Response.status(200).build();
}
}
5)运行应用
当我们在 jboss 中部署以上构建的应用并单击 URL:“http://localhost:8080/RESTfulDemoApplication/user-management/users
”时,以下是响应。
JBOSS 7+ RESTEasy 示例应用
要下载以上示例的源代码,请单击下面的链接。
下载源代码
祝您学习愉快!
面向初学者的 RESTEasy 示例教程
原文: https://howtodoinjava.com/resteasy/resteasy-tomcat-hello-world-application/
RESTEasy 是 JBOSS 提供的 JAX-RS 规范的实现,用于构建 REST API 和 RESTful Java 应用。 尽管 RESTEasy 不限于仅在 JBOSS 中使用,我们也可以与其他服务器协作。 在此 RESTEasy 示例中,学习使用 eclipse 和 tomcat 在 Java 中创建静态的 Web 服务
1. 开发环境
- RESTEasy 2.3.1.GA
- Tomcat 7
- JDK 1.6
请按照以下步骤构建演示应用。
2. 创建 Maven Eclipse Web 项目
运行这些命令来创建一个 Maven 项目,并且可以转换为 Eclipse 项目。
$ mvn archetype:generate -DgroupId=com.howtodoinjava -DartifactId=RESTfulDemoApplication
-DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false
$ mvn eclipse:eclipse -Dwtpversion=2.0
3. RESTEasy Maven 依赖项
<project xmlns="http://maven.apache.org/POM/4.0.0" 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.howtodoinjava</groupId>
<artifactId>RESTfulDemoApplication</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>RESTfulDemoApplication Maven Webapp</name>
<url>http://maven.apache.org</url>
<repositories>
<repository>
<id>jboss</id>
<url>http://repository.jboss.org/maven2</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!-- core library -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<version>2.3.1.GA</version>
</dependency>
<dependency>
<groupId>net.sf.scannotation</groupId>
<artifactId>scannotation</artifactId>
<version>1.0.2</version>
</dependency>
</dependencies>
<build>
<finalName>RESTfulDemoApplication</finalName>
</build>
</project>
4. 注册HttpServletDispatcher
RESTeasy 实现为 Servlet,并部署在 WAR 文件中。 HttpServletDispatcher
类负责初始化 RESTeasy 的一些基本组件。
resteasy.scan
属性会自动扫描WEB-INF/lib
jar 和WEB-INF/classes
目录中的@Provider
和 JAX-RS 资源类(@Path
,@GET
,@POST
等)并进行注册。
<!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>Archetype Created Web Application</display-name>
<!-- Auto scan REST service -->
<context-param>
<param-name>resteasy.scan</param-name>
<param-value>true</param-value>
</context-param>
<servlet>
<servlet-name>resteasy-servlet</servlet-name>
<servlet-class>
org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>resteasy-servlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
5. 创建 REST 控制器
package com.howtodoinjava.restful;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;
@Path("/user-management")
public class UserManagementModule
{
@GET
@Path("/users")
public Response getAllUsers()
{
String result = "<h1>RESTful Demo Application</h1>In real world application, a collection of users will be returned !!";
return Response.status(200).entity(result).build();
}
}
6. RESTEasy 示例应用演示
当我们在 tomcat 中部署以上构建的应用并单击 URL:“http://localhost:8080/RESTfulDemoApplication/user-management/users
”时,以下是响应。
JAX-RS + Tomcat 示例
要下载以上示例的源代码,请单击下面的链接。
下载源码
学习愉快!
JAX-RS @Path
URI 匹配 – 静态和正则 URI
原文: https://howtodoinjava.com/resteasy/jaxrs-path-uri-matching/
学习使用 JAX-RS @Path
注解,并在其中为 URI 匹配的内容写资源路径。 路径可以以简单形式(静态或路径参数)或复杂形式(基于正则表达式)编写。
1. JAX-RS @Path
URI 匹配类型
JAX-RS @Path
注解用于指定其他应用或客户端可在其上访问资源的 URI。 @javax.ws.rs.Path
注解必须存在于类和/或资源方法上。 如果它同时存在于类和方法上,则资源方法的相对路径是类和方法的连接。
@Path
中的 URI 以两种方式定义。
- 简单的 URI 匹配
- 基于正则表达式的 URI 匹配
2. JAX-RS @Path
– 简单的 URI 匹配
以最简单的形式,URI 由静态路径以及一些必要的参数组成。
@Path ("users")
– 与application/user-management/users
匹配。@Path ("users/{id}")
– 与application/user-management/users/12
匹配。
3. JAX-RS @Path
– 基于正则表达式的 URI 匹配
以复杂的形式,我们可以使用基于参数匹配的正则表达式。
@Path("/users/{id: [0-9]*}")
(仅支持数字)–与http://localhost:8080/RESTfulDemoApplication/user-management/users/123456
匹配。@Path("/users/{id: [a-zA-Z][a-zA-Z_0-9]}")
(第一个字符字母,然后是字母数字)–与http:// localhost:8080/RESTfulDemoApplication/user-management/users/abc123
匹配。
下载源码
学习愉快!
Maven - POM 文件
原文: https://howtodoinjava.com/maven/maven-pom-files/
POM(项目对象模型)是一个 XML 文件,其中包含有关项目的信息以及 Maven 用于构建项目的配置详细信息,例如源代码位置,项目依赖项等。此文件必须命名为pom.xml
,并放置在项目的根文件夹下 。 执行任务或目标时,maven 读取 POM,获取所需的配置信息,然后执行目标。
Table of Contents
Super POM
Minimal POM Configuration
Default POM Configuration
POM Hierarchy
超级 POM
POM 文件在它们之间保持父子关系。 您为项目创建的任何 pom 文件最终都将扩展此超级 pom 文件。 从 maven 2.2 开始,这是超级 pom 文件内容。
<project>
<modelVersion>4.0.0</modelVersion>
<name>Maven Default Project</name>
<repositories>
<repository>
<id>central</id>
<name>Maven Repository Switchboard</name>
<layout>default</layout>
<url>http://repo1.maven.org/maven2</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>central</id>
<name>Maven Plugin Repository</name>
<url>http://repo1.maven.org/maven2</url>
<layout>default</layout>
<snapshots>
<enabled>false</enabled>
</snapshots>
<releases>
<updatePolicy>never</updatePolicy>
</releases>
</pluginRepository>
</pluginRepositories>
<build>
<directory>target</directory>
<outputDirectory>target/classes</outputDirectory>
<finalName>${artifactId}-${version}</finalName>
<testOutputDirectory>target/test-classes</testOutputDirectory>
<sourceDirectory>src/main/java</sourceDirectory>
<scriptSourceDirectory>src/main/scripts</scriptSourceDirectory>
<testSourceDirectory>src/test/java</testSourceDirectory>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
<testResources>
<testResource>
<directory>src/test/resources</directory>
</testResource>
</testResources>
</build>
<reporting>
<outputDirectory>target/site</outputDirectory>
</reporting>
<profiles>
<profile>
<id>release-profile</id>
<activation>
<property>
<name>performRelease</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<inherited>true</inherited>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<inherited>true</inherited>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<inherited>true</inherited>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<updateReleaseInfo>true</updateReleaseInfo>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
值得注意的是:
- 默认仓库是 Maven 仓库。
- 默认执行目标是“ jar”。
- 默认源代码位置为
src/main/java
。 - 默认测试代码位置为
src/test/java
。
最小的 POM 配置
任何 Maven 项目的最小 POM 配置都非常简单,如下所示:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.howtodoinjava</groupId>
<artifactId>MavenExample</artifactId>
<version>1.0.0</version>
</project>
这个需要:
project
根modelVersion
– 应设置为 4.0.0groupId
– 项目组的 ID。artifactId
– 工件的 ID(项目)version
– 指定组下工件的版本
您需要在此最小 pom 的基础上添加项目特定的配置。 对于其余配置,如果未指定,则 maven 将使用超级 pom 文件中的默认设置。
默认 POM 配置
这取决于您选择的原型类型。 例如,对于快速启动项目,这是默认生成的pom.xml
文件。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd;
<modelVersion>4.0.0</modelVersion>
<groupId>com.howtodoinjava.demo</groupId>
<artifactId>MavenExamples</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>MavenExamples</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
POM 层次结构
如前所述,POM 文件在它们之间保持父子关系。 子 POM 文件从其父 POM 文件继承所有配置元素。 这就是 Maven 坚持其设计理念的方式,即相对于配置而言是约定。
项目使用的 pom 文件是通过合并项目 pom 文件,父 pom 文件(如果有)和超级 pom 文件而获得的。 此 pom 文件称为有效 pom 文件。
要获取项目使用的有效 pom 文件,请在项目的根文件夹中输入以下命令:
$ mvn help:effective-pom
例如,有效的 pom 文件默认快速启动项目为:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd;
<modelVersion>4.0.0</modelVersion>
<groupId>com.howtodoinjava.demo</groupId>
<artifactId>MavenExamples</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>MavenExamples</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<repositories>
<repository>
<snapshots>
<enabled>false</enabled>
</snapshots>
<id>central</id>
<name>Central Repository</name>
<url>https://repo.maven.apache.org/maven2</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<releases>
<updatePolicy>never</updatePolicy>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
<id>central</id>
<name>Central Repository</name>
<url>https://repo.maven.apache.org/maven2</url>
</pluginRepository>
</pluginRepositories>
<build>
<sourceDirectory>c:\temp\MavenExamples\src\main\java</sourceDirectory>
<scriptSourceDirectory>c:\temp\MavenExamples\src\main\scripts</scriptSourceDirectory>
<testSourceDirectory>c:\temp\MavenExamples\src\test\java</testSourceDirectory>
<outputDirectory>c:\temp\MavenExamples\target\classes</outputDirectory>
<testOutputDirectory>c:\temp\MavenExamples\target\test-classes</testOutputDirectory>
<resources>
<resource>
<directory>c:\temp\MavenExamples\src\main\resources</directory>
</resource>
</resources>
<testResources>
<testResource>
<directory>c:\temp\MavenExamples\src\test\resources</directory>
</testResource>
</testResources>
<directory>c:\temp\MavenExamples\target</directory>
<finalName>MavenExamples-0.0.1-SNAPSHOT</finalName>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.3</version>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2-beta-5</version>
</plugin>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.8</version>
</plugin>
<plugin>
<artifactId>maven-release-plugin</artifactId>
<version>2.3.2</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>2.5</version>
<executions>
<execution>
<id>default-clean</id>
<phase>clean</phase>
<goals>
<goal>clean</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>2.6</version>
<executions>
<execution>
<id>default-testResources</id>
<phase>process-test-resources</phase>
<goals>
<goal>testResources</goal>
</goals>
</execution>
<execution>
<id>default-resources</id>
<phase>process-resources</phase>
<goals>
<goal>resources</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<executions>
<execution>
<id>default-jar</id>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<executions>
<execution>
<id>default-compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>default-testCompile</id>
<phase>test-compile</phase>
<goals>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.12.4</version>
<executions>
<execution>
<id>default-test</id>
<phase>test</phase>
<goals>
<goal>test</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.4</version>
<executions>
<execution>
<id>default-install</id>
<phase>install</phase>
<goals>
<goal>install</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.7</version>
<executions>
<execution>
<id>default-deploy</id>
<phase>deploy</phase>
<goals>
<goal>deploy</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.3</version>
<executions>
<execution>
<id>default-site</id>
<phase>site</phase>
<goals>
<goal>site</goal>
</goals>
<configuration>
<outputDirectory>c:\temp\MavenExamples\target\site</outputDirectory>
<reportPlugins>
<reportPlugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-project-info-reports-plugin</artifactId>
</reportPlugin>
</reportPlugins>
</configuration>
</execution>
<execution>
<id>default-deploy</id>
<phase>site-deploy</phase>
<goals>
<goal>deploy</goal>
</goals>
<configuration>
<outputDirectory>c:\temp\MavenExamples\target\site</outputDirectory>
<reportPlugins>
<reportPlugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-project-info-reports-plugin</artifactId>
</reportPlugin>
</reportPlugins>
</configuration>
</execution>
</executions>
<configuration>
<outputDirectory>c:\temp\MavenExamples\target\site</outputDirectory>
<reportPlugins>
<reportPlugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-project-info-reports-plugin</artifactId>
</reportPlugin>
</reportPlugins>
</configuration>
</plugin>
</plugins>
</build>
<reporting>
<outputDirectory>c:\temp\MavenExamples\target\site</outputDirectory>
</reporting>
</project>
将我的问题放在评论部分。
学习愉快!
Java REST HATEOAS 示例
原文: https://howtodoinjava.com/resteasy/writing-restful-webservices-with-hateoas-using-jax-rs-and-jaxb-in-java/
代表性状态转移( REST )是一种设计习语,它使用 Web 的无状态客户端 - 服务器架构将 REST Web 服务表示为 URL 标识的资源。 REST 风格的架构由客户端和服务器组成。 客户端向服务器发起请求; 服务器处理请求并返回适当的响应。 请求和响应围绕“资源”的“表示”的传递而构建。 资源可以是可以解决的任何连贯且有意义的概念。 资源的表示形式通常是捕获资源当前或预期状态的文档。
来源: http://en.wikipedia.org/wiki/Representational_State_Transfer 。
请注意,此示例应用应部署在 JBOSS 7 服务器上。 如果您使用的是其他服务器,则需要更新这篇文章中提到的pom.xml
和web.xml
文件。
Table of Contents
What is HATEOAS?
Java REST HATEOAS Example
Creating maven blank project
Adding required dependencies in pom.xml
Registering a new module or service
Defining GET,PUT,POST and DELETE methods
Annotating model classes
Analyze the result
1. 什么是 HATEOAS?
HATEOAS 是对 REST 的约束,它表示 REST 应用的客户端只需要知道一个固定的 URL 即可访问它。 应该通过返回的资源表示中包含的超链接从该 URL 动态发现任何资源。
理想情况下,您应该仅向最终用户提供您的服务根 URI。 从此以后,用户必须能够发现您服务中的所有其他 URI。 可以使用当前资源表示中的“链接”来发现这些 URI。 在下面给出的示例应用中,我们将看到 HATEOAS 的演示。
请记住,给定示例项目中的 HATEOAS 实现仅用于演示。 在企业级应用中,建议使用任何第三方 API 或某些自定义实现(最好使用注解)。
2. Java REST HATEOAS 示例
让我们创建一个 Java REST 应用,并在其响应中添加 HATEOAS 链接。
2.1 创建 Maven 项目
创建 Maven 项目就像在命令提示符下执行以下命令一样简单。 我假设您已经在系统中安装了 maven。
mvn archetype:generate -DgroupId=com.demo.rest -DartifactId=sampleRestApp -DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false
如果您尚未安装 maven,请转到 maven 的主页并下载最新版本。
现在将上述项目转换为 Eclipse 支持的项目。 下面的命令将生成.project
文件和其他 Eclipse 依赖项。
mvn eclipse:eclipse -Dwtpversion=2.0
2.2 更新pom.xml
中的 Maven 依赖项
现在该为新创建的 Maven 项目提供所需的依赖项了。 以下是必需的依赖项。 将它们添加到pom.xml
文件中。
<project xmlns="http://maven.apache.org/POM/4.0.0" 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.demo.rest</groupid>
<artifactid>demoResteasyApplication</artifactid>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>demoResteasyApplication Maven Webapp</name>
<url>http://maven.apache.org</url>
<repositories>
<repository>
<id>jboss</id>
<url>http://repository.jboss.org/maven2</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupid>junit</groupid>
<artifactid>junit</artifactid>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!-- core library -->
<dependency>
<groupid>org.jboss.resteasy</groupid>
<artifactid>resteasy-jaxrs</artifactid>
<version>2.3.1.GA</version>
<scope>provided</scope>
</dependency>
<!-- JAXB support -->
<dependency>
<groupid>org.jboss.resteasy</groupid>
<artifactid>resteasy-jaxb-provider</artifactid>
<version>2.3.1.GA</version>
</dependency>
<!-- multipart/form-data and multipart/mixed support -->
<dependency>
<groupid>org.jboss.resteasy</groupid>
<artifactid>resteasy-multipart-provider</artifactid>
<version>2.3.1.GA</version>
</dependency>
<dependency>
<groupid>net.sf.scannotation</groupid>
<artifactid>scannotation</artifactid>
<version>1.0.2</version>
</dependency>
</dependencies>
<build>
<finalname>demoResteasyApplication</finalname>
</build>
</project>
2.3 注册新的模块或服务
随着 jax-rs 2.x 的发布,我们无需在web.xml
中指定任何内容。 Jax-rs 现在扫描@ApplicationPath
注解以注册新的应用模块。
package com.demo.rest;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
import com.demo.rest.service.UserService;
@ApplicationPath("/")
public class ApplicationConfig extends Application
{
@SuppressWarnings("unchecked")
public Set<class <?>> getClasses()
{
return new HashSet<class <?>>(Arrays.asList(UserService.class));
}
}
我们的模块类如下所示:
@Path("/user-management")
public class UserService
{
//Some code
}
上面的模块注册代码将注册一个新的应用"/user-management"
,并将所有相关的相对资源请求转发到此应用/模块。
2.4 定义 REST 方法 - GET,PUT,POST 和 DELETE
如上所述,REST 服务映射了资源表示形式和将更改其内部表示形式的操作。 这些动作应被视为等同于数据库的SELECT
,INSERT
,UPDATE
和DELETE
操作。
如果我们谈论 HTTP 协议,则可以将它们映射到 GET,PUT,POST 和 DELETE 方法。 其中:
- GET 方法将返回资源表示形式
- PUT 将修改资源的内部状态
- POST 通常用于添加新资源,但本质上不是
- DELETE 用于删除资源
现在,让他们了解user-management
模块。
- GET 应该返回所有用户或单个用户表示。
- PUT 方法应用于修改单个用户的表示。
- POST 方法应用于创建新的用户资源。
- 同样,应该使用 DELETE 方法从系统中删除用户。
package com.demo.rest.service;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;
import com.demo.rest.model.User;
import com.demo.rest.model.Users;
@Path("/user-management")
public class UserService {
@GET
@Path("/")
@Produces("application/vnd.com.demo.user-management+xml;charset=UTF-8;version=1")
public UserService getServiceInfo() {
return new UserService();
}
@GET
@Path("/users")
@Produces("application/vnd.com.demo.user-management.users+xml;charset=UTF-8;version=1")
public Users getAllUsers() {
User user1 = new User();
user1.setId(1);
user1.setFirstName("demo");
user1.setLastName("user");
user1.setUri("/user-management/users/1");
User user2 = new User();
user2.setId(2);
user2.setFirstName("demo");
user2.setLastName("user");
user2.setUri("/user-management/users/2");
Users users = new Users();
users.setUsers(new ArrayList());
users.getUsers().add(user1);
users.getUsers().add(user2);
return users;
}
@GET
@Path("/users/{id}")
@Produces("application/vnd.com.demo.user-management.user+xml;charset=UTF-8;version=1")
public User getUserById(@PathParam("id") int id) {
User user = new User();
user.setId(id);
user.setFirstName("demo");
user.setLastName("user");
user.setUri("/user-management/users/" + id);
return user;
}
@POST
@Path("/users")
@Consumes("application/vnd.com.demo.user-management.user+xml;charset=UTF-8;version=1")
public Response createUser(User user,
@DefaultValue("false") @QueryParam("allow-admin") boolean allowAdmin)
throws URISyntaxException {
System.out.println(user.getFirstName());
System.out.println(user.getLastName());
return Response.status(201)
.contentLocation(new URI("/user-management/users/123")).build();
}
@PUT
// @Path("/users/{id: [0-9]*}")
@Path("/users/{id}")
@Consumes("application/vnd.com.demo.user-management.user+xml;charset=UTF-8;version=1")
@Produces("application/vnd.com.demo.user-management.user+xml;charset=UTF-8;version=1")
public User updateUser(@PathParam("id") int id, User user)
throws URISyntaxException {
user.setId(id);
user.setFirstName(user.getFirstName() + "updated");
return user;
}
@DELETE
@Path("/users/{id}")
public Response deleteUser(@PathParam("id") int id)
throws URISyntaxException {
return Response.status(200).build();
}
}
2.5 注解模型类
到目前为止,我们已经创建了我们的服务类。 现在该创建资源表示形式了,用户可以使用它。
如果您还记得,HATEOAS 坚持您的应用应该有一个起点,此后,用户与应用的每次交互都应该是状态转移。 状态传输所需的信息应来自当前资源表示,即每次重新表示应提供嵌套状态传输的机制。
让我们用 JAXB 注解来注解我们的服务和模型类,然后我们将看到在什么程度上遵循了 HATEOAS 准则。
Users.java
(用户集合的表示形式)
package com.demo.rest.model;
import java.util.ArrayList;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name = "users")
public class Users {
@XmlElement(name="user")
private ArrayList users;
public ArrayList getUsers() {
return users;
}
public void setUsers(ArrayList users) {
this.users = users;
}
}
User.java
(单个用户的表示形式)
package com.demo.rest.model;
import java.io.Serializable;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name = "user")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
@XmlAttribute(name = "id")
private int id;
@XmlAttribute(name="uri")
private String uri;
@XmlElement(name = "firstName")
private String firstName;
@XmlElement(name = "lastName")
private String lastName;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getUri() {
return uri;
}
public void setUri(String uri) {
this.uri = uri;
}
}
添加了 JAXB 注解的UserService.java
(服务根的表示)
package com.demo.rest.service;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import com.demo.rest.model.User;
import com.demo.rest.model.Users;
@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name = "user-management")
@Path("/user-management")
public class UserService {
@XmlElement(name = "users")
private String uri1 = "/user-management/users";
@XmlElement(name = "report")
private String uri2 = "/user-managemet/generate-report";
public String getUri1() {
return uri1;
}
public void setUri1(String uri1) {
this.uri1 = uri1;
}
public String getUri2() {
return uri2;
}
public void setUri2(String uri2) {
this.uri2 = uri2;
}
@GET
@Path("/")
@Produces("application/vnd.com.demo.user-management+xml;charset=UTF-8;version=1")
public UserService getServiceInfo() {
return new UserService();
}
@GET
@Path("/users")
@Produces("application/vnd.com.demo.user-management.users+xml;charset=UTF-8;version=1")
public Users getAllUsers() {
User user1 = new User();
user1.setId(1);
user1.setFirstName("demo");
user1.setLastName("user");
user1.setUri("/user-management/users/1");
User user2 = new User();
user2.setId(2);
user2.setFirstName("demo");
user2.setLastName("user");
user2.setUri("/user-management/users/2");
Users users = new Users();
users.setUsers(new ArrayList());
users.getUsers().add(user1);
users.getUsers().add(user2);
return users;
}
@GET
@Path("/users/{id}")
@Produces("application/vnd.com.demo.user-management.user+xml;charset=UTF-8;version=1")
public User getUserById(@PathParam("id") int id) {
User user = new User();
user.setId(id);
user.setFirstName("demo");
user.setLastName("user");
user.setUri("/user-management/users/" + id);
return user;
}
@POST
@Path("/users")
@Consumes("application/vnd.com.demo.user-management.user+xml;charset=UTF-8;version=1")
public Response createUser(User user,
@DefaultValue("false") @QueryParam("allow-admin") boolean allowAdmin)
throws URISyntaxException {
System.out.println(user.getFirstName());
System.out.println(user.getLastName());
return Response.status(201)
.contentLocation(new URI("/user-management/users/123")).build();
}
@PUT
// @Path("/users/{id: [0-9]*}")
@Path("/users/{id}")
@Consumes("application/vnd.com.demo.user-management.user+xml;charset=UTF-8;version=1")
@Produces("application/vnd.com.demo.user-management.user+xml;charset=UTF-8;version=1")
public User updateUser(@PathParam("id") int id, User user)
throws URISyntaxException {
user.setId(id);
user.setFirstName(user.getFirstName() + "updated");
return user;
}
@DELETE
@Path("/users/{id}")
public Response deleteUser(@PathParam("id") int id)
throws URISyntaxException {
return Response.status(200).build();
}
}
2.6 验证 HATEOAS 链接
因此,我们为一个简单的演示编写了许多代码。 现在,该测试我们的代码了。
我正在使用 RESTClient 来验证 API 输出。 您可以选择自己的验证方式。
我已经在 eclipse juno 上运行的 JBOSS 7.1 服务器运行时环境中部署了以上应用。 如果要部署在某些独立的 jboss 实例上,也可以尝试。
让我们一一点击应用 URL:
-
根服务 API
此 API 返回服务根的表示形式。 它具有用于用户收集的 uri 和用于 API 的一个附加链接来生成用户报告。
-
获取所有用户集合
此表示具有用户数据和 uri 的快照,可在其中提取特定用户的所有其他信息。
-
根据 ID 获取用户
此表示应提供用户资源和其他链接(如果有)的每个相关细节。
-
不带媒体类型添加用户
添加用户资源后,应将其添加到用户集合中。 因此,在几乎所有集合类型表示中, POST 都应该隐式地可用。
在这里,用户将被添加到用户集合中,因此我们会将其发布在“
/user-management/users
”上。添加帖子时,我们必须声明我们要发布的媒体类型。 如果未指定,则会发生以下错误。
响应代码 415(不支持的媒体类型)
-
在请求标头中添加正确的媒体类型
让我们向请求标头添加正确的媒体类型。
-
使用正确的媒体类型创建
现在,应该创建一个新用户,并返回到创建资源的“链接”。
-
删除用户
删除资源时,使用“HTTP DELETE”。 不得使用任何媒体类型或请求正文。
资源应在资源链接本身上删除。
因此,这就是在通过 REST HATEOAS 进行的连续 API 调用之间,应用状态应如何更改,而其下一个状态应从当前状态表示中进行定向的方式。
我在这里完成了写作,剩下的工作就是让您对这个例子更加满意。 从下面给定的下载链接,下载上面的源代码文件并使用它。
源码下载
学习愉快!
RESTEasy + Tomcat 7 + SLF4J 日志示例
原文: https://howtodoinjava.com/resteasy/resteasy-tomcat-7-slf4j-logging-example/
RESTEasy 在日志支持方面非常灵活。 它也可以与 log4j,slf4j 和java.util.logging
协作。 用于确定需要使用哪个日志记录框架的算法是:
- 如果 log4j 在应用的类路径中,则将使用 log4j
- 如果 slf4j 在应用的类路径中,则将使用 slf4j
- 如果 log4j 或 slf4j 都不在类路径中,则
java.util.logging
为默认值 - 如果 servlet 上下文参数
resteasy.logger.type
设置为 JUL,LOG4J 或 SLF4J 将覆盖此默认行为
在这篇文章中,我们将学习如何在 tomcat 服务器中开发应用时使用 RESTEasy 来学习 slf4j 。 这篇文章是我上一篇关于 log4j 与 RESTEasy 集成的继续。 在这里,我要添加 slf4j 依赖项(JCL 绑定)并删除 log4j 依赖项。 我没有触碰应用代码,来显示“org.jboss.resteasy.logging.Logger
”提供的灵活性。
使用的环境:
- Tomcat 7
- SLF4J 1.7.5
- RESTEasy JAX-RS 2.3.1.GA
配置 SLF4j 的步骤
1)在项目中包含依赖项
我正在添加 Maven 依赖项。 如果需要,您可以选择包括 jar 文件。
<!-- SLF4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.5</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jcl</artifactId>
<version>1.7.5</version>
</dependency>
还要确保在类路径中存在 Commons Logging jar 文件。 如果您正在使用 maven 进行依赖项管理,则在添加 jax-rs 依赖项时,可能会将其包括在内。 因此,无需单独包含它。
2)在 API 方法中使用日志语句
始终使用“org.jboss.resteasy.logging.Logger
”,因为它是使用上述给定算法配置的,因此完全将日志记录框架的依赖项与应用代码分离。 这意味着,如果您以后决定使用 slf4j 代替 log4j,则只需将 slf4j 放入运行时类路径中,然后从类路径中删除 log4j。 仅此而已 !!
import org.jboss.resteasy.logging.Logger;
@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name = "user-management")
@Path("/user-management")
public class UserService
{
Logger log = Logger.getLogger(UserService.class);
@GET
@Path("/users/{id : \d+}")
public User getUserById(@PathParam("id") Integer id) {
log.info("GET API called for id : " + id);
User user = new User();
user.setId(id);
user.setFirstName("demo");
user.setLastName("user");
return user;
}
}
3)测试日志记录
调用上述 RESTFul API 会记录以下事件:
May 13, 2013 11:39:10 AM org.slf4j.impl.JCLLoggerAdapter info
INFO: Adding scanned resource: com.howtodoinjava.service.UserService
May 13, 2013 11:39:10 AM org.slf4j.impl.JCLLoggerAdapter info
INFO: GET API called for id : 10
学习愉快!
RESTEasy + Tomcat 7 + Log4j 记录示例
原文: https://howtodoinjava.com/resteasy/resteasy-tomcat-7-log4j-logging-example/
RESTEasy 在日志支持方面非常灵活。 它也可以与 log4j,slf4j 和java.util.logging
协作。 用于确定需要使用哪个日志记录框架的算法是:
- 如果 log4j 在应用的类路径中,则将使用 log4j
- 如果 slf4j 在应用的类路径中,则将使用 slf4j
- 如果 log4j 或 slf4j 都不在类路径中,则
java.util.logging
为默认值 - 如果 servlet 上下文参数
resteasy.logger.type
设置为 JUL,LOG4J 或 SLF4J 将覆盖此默认行为
在本文中,我们将学习在 tomcat 服务器中开发应用时使用 RESTEasy 进行 log4j 的开发。
使用的环境:
- Tomcat 7
- Log4j 1.2.17
- RESTEasy JAX-RS 2.3.1.GA
配置 log4j 的步骤
1)在项目中包含依赖项
我正在添加 Maven 依赖项。 如果需要,您可以选择包括 jar 文件。
<!-- core library -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<version>2.3.1.GA</version>
</dependency>
<dependency>
<groupId>net.sf.scannotation</groupId>
<artifactId>scannotation</artifactId>
<version>1.0.2</version>
</dependency>
<!-- Log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
2)在类路径中添加log4j.properties
文件
log4j 文件中的最低配置可以是:
log4j.rootLogger=DEBUG, consoleAppender, fileAppender
log4j.appender.consoleAppender=org.apache.log4j.ConsoleAppender
log4j.appender.consoleAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.consoleAppender.layout.ConversionPattern=[%t] %-5p %c %x - %m%n
log4j.appender.fileAppender=org.apache.log4j.RollingFileAppender
log4j.appender.fileAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.fileAppender.layout.ConversionPattern=[%t] %-5p %c %x - %m%n
log4j.appender.fileAppender.File=C:/logs/demoApplication.log
3)在 API 方法中使用日志语句
始终使用“org.jboss.resteasy.logging.Logger
”,因为它是使用上述给定算法进行配置的,因此完全将日志记录框架的依赖项与应用代码分离。 这意味着,如果您以后决定使用 slf4j 代替 log4j,则只需将 slf4j 放入运行时类路径中,然后从类路径中删除 log4j。 仅此而已 !!
import org.jboss.resteasy.logging.Logger;
@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name = "user-management")
@Path("/user-management")
public class UserService
{
Logger log = Logger.getLogger(UserService.class);
@GET
@Path("/users/{id : \d+}")
public User getUserById(@PathParam("id") Integer id) {
log.info("GET API called for id : " + id);
User user = new User();
user.setId(id);
user.setFirstName("demo");
user.setLastName("user");
return user;
}
}
4)测试日志记录
调用上述 RESTFul API 会记录以下事件:
[http-bio-8080-exec-3] INFO org.jboss.resteasy.plugins.server.servlet.ConfigurationBootstrap - Adding scanned resource: com.howtodoinjava.service.UserService
[http-bio-8080-exec-3] DEBUG org.jboss.resteasy.core.SynchronousDispatcher - PathInfo: /user-management/users/10
[http-bio-8080-exec-3] INFO com.howtodoinjava.service.UserService - GET API called for id : 10
祝您学习愉快!
RESTEasy - 文件下载示例
原文: https://howtodoinjava.com/resteasy/resteasy-file-download-example/
RESTEasy 是 JBOSS 提供的 JAX-RS 规范的实现,用于构建 RESTful Web 服务和 RESTful Java 应用。 RESTEasy 与 HTTP 媒体类型结合使用,以特定格式(例如图像,pdf 或文本)提供响应。
为响应而支持多种媒体类型的配置的核心组件是@Produces
注解。 可以在此链接中找到此类媒体类型的完整列表。
出于演示目的,我将展示下载一个图像,一个文本和一个 pdf 文件的示例。 同样,您可以构建其他媒体类型(文件类型)。
1)创建一个 Maven 项目
mvn archetype:generate -DgroupId=com.howtodoinjava -DartifactId=RESTfulDemoApplication
-DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false
mvn eclipse:eclipse -Dwtpversion=2.0
2)更新pom.xml
文件中的 jax-rs 依赖项
<project xmlns="http://maven.apache.org/POM/4.0.0" 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.howtodoinjava</groupId>
<artifactId>RESTfulDemoApplication</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>RESTfulDemoApplication Maven Webapp</name>
<url>http://maven.apache.org</url>
<repositories>
<repository>
<id>jboss</id>
<url>http://repository.jboss.org/maven2</url>
</repository>
</repositories>
<dependencies>
<!-- core library -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<version>2.3.1.GA</version>
</dependency>
<dependency>
<groupId>net.sf.scannotation</groupId>
<artifactId>scannotation</artifactId>
<version>1.0.2</version>
</dependency>
</dependencies>
<build>
<finalName>RESTfulDemoApplication</finalName>
</build>
</project>
3)在@Produces
注解中创建具有相应媒体类型的服务类和 API
package com.howtodoinjava.service;
import java.io.File;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.Response.Status;
@Path("/file-management")
public class FileServer
{
@GET
@Path("/{fileName}/text")
@Produces("text/plain")
public Response getFileInTextFormat(@PathParam("fileName") String fileName)
{
System.out.println("File requested is : " + fileName);
//Put some validations here such as invalid file name or missing file name
if(fileName == null || fileName.isEmpty())
{
ResponseBuilder response = Response.status(Status.BAD_REQUEST);
return response.build();
}
//Prepare a file object with file to return
File file = new File("c:/demoTxtFile.txt");
ResponseBuilder response = Response.ok((Object) file);
response.header("Content-Disposition", "attachment; filename="howtodoinjava.txt"");
return response.build();
}
@GET
@Path("/{fileName}/pdf")
@Produces("application/pdf")
public Response getFileInPDFFormat(@PathParam("fileName") String fileName)
{
System.out.println("File requested is : " + fileName);
//Put some validations here such as invalid file name or missing file name
if(fileName == null || fileName.isEmpty())
{
ResponseBuilder response = Response.status(Status.BAD_REQUEST);
return response.build();
}
//Prepare a file object with file to return
File file = new File("c:/demoPDFFile.pdf");
ResponseBuilder response = Response.ok((Object) file);
response.header("Content-Disposition", "attachment; filename="howtodoinjava.pdf"");
return response.build();
}
@GET
@Path("/{fileName}/image")
@Produces("image/jpeg")
public Response getFileInJPEGFormat(@PathParam("fileName") String fileName)
{
System.out.println("File requested is : " + fileName);
//Put some validations here such as invalid file name or missing file name
if(fileName == null || fileName.isEmpty())
{
ResponseBuilder response = Response.status(Status.BAD_REQUEST);
return response.build();
}
//Prepare a file object with file to return
File file = new File("c:/demoJpegFile.jpeg");
ResponseBuilder response = Response.ok((Object) file);
response.header("Content-Disposition", "attachment; filename="howtodoinjava.jpeg"");
return response.build();
}
}
4)将要下载的文件放在参考位置
我正在引用“c:/<文件>
”中的文件。 在运行此示例之前,从下载链接中的分发文件夹复制文件,并将其粘贴到c:
中。
5)测试应用
以下是在浏览器中显示的用于请求 URL 的下载窗口的快照:
http://localhost:8080/RESTfulDemoApplication/file-management/demoTxtFile/text
http://localhost:8080/RESTfulDemoApplication/file-management/demoPDFFile/pdf
http://localhost:8080/RESTfulDemoApplication/file-management/demoJpegFile/image
如果要下载以上示例的源代码,请遵循以下给定的链接。
祝您学习愉快!
RESTEasy 文件上传 - HTML 表单示例
原文: https://howtodoinjava.com/resteasy/jax-rs-resteasy-file-upload-html-form-example/
在上一篇文章中,我们了解了有关在 JAX-RS RESTEasy 应用中上传文件的信息,其中使用HttpClient
库构建了客户端来上传文件。 该客户端是纯 Java 客户端,没有任何 UI 关联。 在这篇文章中,我将构建相同的上传功能,但是这次,我们将有一个与之交互的 UI。
我们将文件上传到服务器的 UI 如下所示。
JAX-RS RESTeasy 文件上传示例
让我们逐步制作应用:
1)使用 Maven 创建一个 Eclipse Web 项目进行依赖项管理
在此处学习操作方法
2)更新pom.xml
中的项目依赖项
<project xmlns="http://maven.apache.org/POM/4.0.0" 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.howtodoinjava</groupId>
<artifactId>RESTfulDemoApplication</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>RESTfulDemoApplication Maven Webapp</name>
<url>http://maven.apache.org</url>
<repositories>
<repository>
<id>jboss</id>
<url>http://repository.jboss.org/maven2</url>
</repository>
</repositories>
<dependencies>
<!-- Junit support -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!-- core library -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<version>2.3.1.GA</version>
</dependency>
<dependency>
<groupId>net.sf.scannotation</groupId>
<artifactId>scannotation</artifactId>
<version>1.0.2</version>
</dependency>
<!-- JAXB provider -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxb-provider</artifactId>
<version>2.3.1.GA</version>
</dependency>
<!-- Multipart support -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-multipart-provider</artifactId>
<version>2.3.1.GA</version>
</dependency>
<!-- For better I/O control -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.0.1</version>
</dependency>
</dependencies>
<build>
<finalName>RESTfulDemoApplication</finalName>
</build>
</project>
3)更新web.xml
文件以映射有效的 REST API
<!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>Archetype Created Web Application</display-name>
<!-- Auto scan REST service -->
<context-param>
<param-name>resteasy.scan</param-name>
<param-value>true</param-value>
</context-param>
<listener>
<listener-class>
org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap
</listener-class>
</listener>
<servlet>
<servlet-name>resteasy-servlet</servlet-name>
<servlet-class>
org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>resteasy-servlet</servlet-name>
<url-pattern>/rest-ws/*</url-pattern>
</servlet-mapping>
</web-app>
4)使用所需的用户界面更新index.jsp
文件
<html>
<body>
<h1>JAX-RS File Upload Example</h1>
<form action="rest-ws/upload-file" method="post" enctype="multipart/form-data">
<p>
File name : <input type="text" name="fileName" />
</p>
<p>
Choose the file : <input type="file" name="selectedFile" />
</p>
<input type="submit" value="Upload" />
</form>
https://www.howtodoinjava.com
</body>
</html>
5)创建具有映射到 HTML 表单的字段的FileUploadForm
package com.howtodoinjava.client.upload;
import javax.ws.rs.FormParam;
import org.jboss.resteasy.annotations.providers.multipart.PartType;
public class FileUploadForm {
public FileUploadForm() {
}
private byte[] fileData;
private String fileName;
public String getFileName() {
return fileName;
}
@FormParam("fileName")
public void setFileName(String fileName) {
this.fileName = fileName;
}
public byte[] getFileData() {
return fileData;
}
@FormParam("selectedFile")
@PartType("application/octet-stream")
public void setFileData(byte[] fileData) {
this.fileData = fileData;
}
}
6)创建具有处理上传文件逻辑的 RESTful API
package com.howtodoinjava.client.upload;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;
import org.jboss.resteasy.annotations.providers.multipart.MultipartForm;
@Path("/rest-ws")
public class DemoFileSaver_MultipartForm
{
@POST
@Path("/upload-file")
@Consumes("multipart/form-data")
public Response uploadFile(@MultipartForm FileUploadForm form) {
String fileName = form.getFileName() == null ? "Unknown" : form.getFileName() ;
String completeFilePath = "c:/temp/" + fileName;
try
{
//Save the file
File file = new File(completeFilePath);
if (!file.exists())
{
file.createNewFile();
}
FileOutputStream fos = new FileOutputStream(file);
fos.write(form.getFileData());
fos.flush();
fos.close();
}
catch (IOException e)
{
e.printStackTrace();
}
//Build a response to return
return Response.status(200)
.entity("uploadFile is called, Uploaded file name : " + fileName).build();
}
}
7)测试应用
祝您学习愉快!
RESTEasy 文件上传 - HttpClient
示例
原文: https://howtodoinjava.com/resteasy/jax-rs-resteasy-file-upload-httpclient-example/
在以前的文章中,我们了解了文件下载以及构建 RESTful 客户端的知识。 现在,让我们继续前进。 在这篇文章中,我将提供使用 jax-rs resteasy 上传文件的示例代码。 要上传文件,将使用httpclient
库代替 HTML 表单。
我正在使用MultipartFormDataInput
类,它是resteasy-multipart
插件的一部分。
1)更新项目的 Maven 依赖项
在 maven 依赖项下添加/更新,以添加对项目中多部分文件上传的支持。
<!-- core library -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<version>2.3.1.GA</version>
</dependency>
<dependency>
<groupId>net.sf.scannotation</groupId>
<artifactId>scannotation</artifactId>
<version>1.0.2</version>
</dependency>
<!-- JAXB provider -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxb-provider</artifactId>
<version>2.3.1.GA</version>
</dependency>
<!-- Multipart support -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-multipart-provider</artifactId>
<version>2.3.1.GA</version>
</dependency>
<!-- For better I/O control -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.0.1</version>
</dependency>
还要添加下图给出的 jar 文件。 需要使用它们来构建用于文件上传示例的客户端代码。
HTTP 客户端 jar 文件
2)准备将要在客户端上载文件的 http 客户端
package com.howtodoinjava.client.upload;
import java.io.File;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.DefaultHttpClient;
public class DemoFileUploader
{
public static void main(String args[]) throws Exception
{
DemoFileUploader fileUpload = new DemoFileUploader () ;
File file = new File("C:/Lokesh/Setup/workspace/RESTfulDemoApplication/target/classes/Tulips.jpg") ;
//Upload the file
fileUpload.executeMultiPartRequest("http://localhost:8080/RESTfulDemoApplication/user-management/image-upload",
file, file.getName(), "File Uploaded :: Tulips.jpg") ;
}
public void executeMultiPartRequest(String urlString, File file, String fileName, String fileDescription) throws Exception
{
HttpClient client = new DefaultHttpClient() ;
HttpPost postRequest = new HttpPost (urlString) ;
try
{
//Set various attributes
MultipartEntity multiPartEntity = new MultipartEntity () ;
multiPartEntity.addPart("fileDescription", new StringBody(fileDescription != null ? fileDescription : "")) ;
multiPartEntity.addPart("fileName", new StringBody(fileName != null ? fileName : file.getName())) ;
FileBody fileBody = new FileBody(file, "application/octect-stream") ;
//Prepare payload
multiPartEntity.addPart("attachment", fileBody) ;
//Set to request body
postRequest.setEntity(multiPartEntity) ;
//Send request
HttpResponse response = client.execute(postRequest) ;
//Verify response if any
if (response != null)
{
System.out.println(response.getStatusLine().getStatusCode());
}
}
catch (Exception ex)
{
ex.printStackTrace() ;
}
}
}
3)在应用中编写 RESTful API 以接受多部分请求
package com.howtodoinjava.client.upload;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import org.apache.commons.io.IOUtils;
import org.jboss.resteasy.plugins.providers.multipart.InputPart;
import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput;
@Path("/user-management")
public class DemoFileSaver_MultipartFormDataInput
{
private final String UPLOADED_FILE_PATH = "c:\temp\";
@POST
@Path("/image-upload")
@Consumes("multipart/form-data")
public Response uploadFile(MultipartFormDataInput input) throws IOException
{
//Get API input data
Map<String, List<InputPart>> uploadForm = input.getFormDataMap();
//Get file name
String fileName = uploadForm.get("fileName").get(0).getBodyAsString();
//Get file data to save
List<InputPart> inputParts = uploadForm.get("attachment");
for (InputPart inputPart : inputParts)
{
try
{
//Use this header for extra processing if required
@SuppressWarnings("unused")
MultivaluedMap<String, String> header = inputPart.getHeaders();
// convert the uploaded file to inputstream
InputStream inputStream = inputPart.getBody(InputStream.class, null);
byte[] bytes = IOUtils.toByteArray(inputStream);
// constructs upload file path
fileName = UPLOADED_FILE_PATH + fileName;
writeFile(bytes, fileName);
System.out.println("Success !!!!!");
}
catch (Exception e)
{
e.printStackTrace();
}
}
return Response.status(200)
.entity("Uploaded file name : "+ fileName).build();
}
//Utility method
private void writeFile(byte[] content, String filename) throws IOException
{
File file = new File(filename);
if (!file.exists()) {
file.createNewFile();
}
FileOutputStream fop = new FileOutputStream(file);
fop.write(content);
fop.flush();
fop.close();
}
}
[源码下载](https://docs.google.com/file/d/0B7yo2HclmjI4amhXRE5VMjBRSHM/edit?usp=sharing "multipart rest file upload")
祝您学习愉快!
使用 Ajax 的 JAX-RS 自定义验证示例
原文: https://howtodoinjava.com/resteasy/jax-rs-custom-validation-example-using-ajax/
在此示例中,我将显示ValidatorAdapter
与@ValidateRequest
注解的结合使用。 为了从 UI 发送请求,我将使用 ajax。 您可以在项目中使用表单提交。 在这种情况下,您将需要@FormParam
注解来捕获请求参数。
在下面的功能截图中,我们将在本教程中实现。
JAX-RS + Ajax 验证示例
让我们逐步构建此示例教程。
步骤 1)使用 Maven 创建一个 Eclipse Web 项目
C:LokeshSetupworkspaceRESTfulValidation>mvn archetype:generate -DgroupId=com.howtodoinjava
-DartifactId=RESTfulValidation -DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false
mvn eclipse:eclipse -Dwtpversion=2.0
步骤 2)更新pom.xml
文件中的运行时依赖项
<!-- core library -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<version>2.3.1.GA</version>
</dependency>
<dependency>
<groupId>net.sf.scannotation</groupId>
<artifactId>scannotation</artifactId>
<version>1.0.2</version>
</dependency>
<!-- JAXB provider -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxb-provider</artifactId>
<version>2.3.1.GA</version>
</dependency>
步骤 3)使用路径映射信息更新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>Archetype Created Web Application</display-name>
<!-- Auto scan REST service -->
<context-param>
<param-name>resteasy.scan</param-name>
<param-value>true</param-value>
</context-param>
<listener>
<listener-class>
org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap
</listener-class>
</listener>
<servlet>
<servlet-name>resteasy-servlet</servlet-name>
<servlet-class>
org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>resteasy-servlet</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
</web-app>
步骤 4)创建将在其上执行验证的 RESTful API
该 API 在以下级别可以具有@ValidateRequest
注解:
- 方法级别:它将启用对该特定方法的验证。
- 类级别:它将在该类内的所有方法上启用验证。
我在类上使用此注解。
package com.howtodoinjava.rest;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;
import org.jboss.resteasy.spi.validation.ValidateRequest;
@Path("/rest")
@ValidateRequest
public class UserService {
@Path("/users")
@POST
public Response addUser(@QueryParam("firstName") String firstName, @QueryParam("lastName") String lastName)
{
System.out.println("User added !!");
return Response.ok().build();
}
}
注意:如果已在类级别使用@ValidateRequest
注解,并且想要禁用某些 API 的验证,则可以使用@DoNotValidateRequest
注解。
步骤 5)创建您的自定义验证器,该实现器将实现ValidatorAdapter
类
ValidatorAdapter
的实现在应用启动时由 RESTEasy 自动扫描,并在上下文中注册。
package com.howtodoinjava.validator;
import java.lang.reflect.Method;
import org.jboss.resteasy.spi.BadRequestException;
import org.jboss.resteasy.spi.validation.ValidatorAdapter;
public class CommonValidator implements ValidatorAdapter {
@Override
public void applyValidation(Object resource, Method invokedMethod, Object[] args)
{
/*ValidateRequest classLevelValidateRequest = FindAnnotation.findAnnotation(invokedMethod.getDeclaringClass()
.getAnnotations(), ValidateRequest.class);
ValidateRequest methodLevelValidateRequest = FindAnnotation.findAnnotation(invokedMethod.getAnnotations(), ValidateRequest.class);
boolean applyValidation = (classLevelValidateRequest != null || methodLevelValidateRequest != null);*/
if( invokedMethod.getName().equalsIgnoreCase("addUser"))
{
if(args == null || args.length != 2)
{
throw new BadRequestException("Fill all fields");
}
if(((String) args[0]).isEmpty())
{
throw new BadRequestException("Fill first name");
}
else if(((String) args[1]).isEmpty())
{
throw new BadRequestException("Fill last name");
}
}
}
}
步骤 6)修改index.jsp
文件以与 REST API 交互
该 jsp 文件将包含带有两个输入框的 HTML 表单。 这些文本框将接受用户的名字和姓氏。 我们正在尝试在此表单上启用 ajax 支持的验证功能。
<html>
<head>
<script lang="javascript">
var xmlhttp;
function postRequest(url, cfunc) {
if (window.XMLHttpRequest) {// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp = new XMLHttpRequest();
} else {// code for IE6, IE5
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange = cfunc;
xmlhttp.open("POST", url, true);
xmlhttp.send();
}
function submitForm() {
postRequest(
"./rest/users?firstName="+document.getElementById("firstName").value+"&lastName="+document.getElementById("lastName").value,
function() {
if (xmlhttp.readyState == 4 && xmlhttp.status != 200) {
//alert(xmlhttp.responseText);
document.getElementById("error").innerHTML = "<h2><span style='color:red'>Fill all fields !!</span></h2>";
}
});
}
</script>
</head>
<body>
<h1>JAX-RS Custom Validation</h1>
<div id="error"></div>
<form onclick="submitForm()" method="post">
<p>
First Name : <input type="text" name="firstName" id="firstName"/>
</p>
<p>
LastName : <input type="text" name="lastName" id="lastName"/>
</p>
<input type="button" value="Add User" />
</form>
By :
<b>https://www.howtodoinjava.com</b>
</body>
</html>
步骤 7)测试应用
尝试提交项目或半填表时,您会收到验证错误,如帖子开头的屏幕截图所示。 另外,在服务器日志中,您可以验证错误记录为:
SEVERE: Failed executing POST /rest/users
org.jboss.resteasy.spi.BadRequestException: Fill first name
at com.howtodoinjava.validator.CommonValidator.applyValidation(CommonValidator.java:30)
at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:150)
at org.jboss.resteasy.core.ResourceMethod.invokeOnTarget(ResourceMethod.java:257)
at org.jboss.resteasy.core.ResourceMethod.invoke(ResourceMethod.java:222)
at org.jboss.resteasy.core.ResourceMethod.invoke(ResourceMethod.java:211)
at org.jboss.resteasy.core.SynchronousDispatcher.getResponse(SynchronousDispatcher.java:525)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:502)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:119)
at org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.java:208)
at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:55)
at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:50)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:936)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1004)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
学习愉快!
使用 Hibernate 验证器供应器进行 RESTEasy Bean 验证
原文: https://howtodoinjava.com/resteasy/resteasy-bean-validation-using-hibernate-validator-provider/
Bean 验证 API(JSR-303)基于注解定义了用于 bean 验证的元数据模型和 API。 该功能是通过resteasy-hibernatevalidator-provider
组件实现的。 为了集成,我们需要将resteasy-hibernatevalidator-provider.jar
和hibernate-validator.jar
文件添加到类路径。
如果您使用的是 maven,则只需添加以下依赖项。 随身更改 RESTEasy 的版本号。
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-hibernatevalidator-provider</artifactId>
<version>2.3.1.GA</version>
</dependency>
步骤 1)在 API 参数中添加特定于验证的注解
您可以使用各种注解来验证请求参数和表单输入。 例如,我使用了@NotNull
和@Size
注解。 阅读链接中的所有可用注解。 别忘了在方法 API 上添加@ValidateRequest
注解。@ValidateRequest
启用对其应用方法的验证。
package com.howtodoinjava.rest;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.ws.rs.FormParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;
import org.jboss.resteasy.spi.validation.ValidateRequest;
@Path("/rest")
public class UserService
{
@Path("/users")
@POST
@ValidateRequest
public Response addUser
(
@NotNull
@Size(min=1,max=50)
@FormParam("firstName") String firstName ,
@Size(max=50)
@FormParam("lastName") String lastName
)
{
return Response.ok().entity("User "" + firstName + " " + lastName + "" added through JAX-RS JavaScript API").build();
}
}
步骤 2)添加验证异常处理器
好吧,这很重要,因为 Hiberate 验证器插件没有任何用于异常处理的功能,因此您需要嵌入自己的。 RESTEasy 中的异常处理器通过ExceptionMapper
类使用。
package com.howtodoinjava.exception;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
import org.hibernate.validator.method.MethodConstraintViolationException;
@Provider
public class ValidationExceptionHandler implements ExceptionMapper<MethodConstraintViolationException>
{
@Override
public Response toResponse(MethodConstraintViolationException exception)
{
return Response.status(Status.BAD_REQUEST).entity("Fill all fields").build();
}
}
步骤 3)构建客户端代码并测试应用
我写了一个普通的 HTML 表单,它将使用表单提交为firstName
和lastName
参数发送两个参数。 参数验证自动执行,如果验证失败,则调用异常处理器以返回正确的状态代码。
<html>
<body>
<h1>RESTEasy Hibernate Validator Plugin</h1>
<div id="error"></div>
<form method="post" action="./rest/users">
<p>First Name : <input type="text" name="firstName" id="firstName"/></p>
<p>LastName : <input type="text" name="lastName" id="lastName"/></p>
<input type="submit" value="Add User" />
</form>
Demo by : <b>//howtodoinjava.com</b>
</body>
</html>
您可以在浏览器窗口中测试上述应用,也可以使用一些 REST 测试工具,例如 RESTClient firefox 插件。
使用 Hibernate 验证器的 RESTEasy bean 验证
要下载此演示的源代码,请点击以下链接。
祝您学习愉快!
RESTEasy ContainerRequestFilter
- RESTEasy 安全过滤器示例
原文: https://howtodoinjava.com/resteasy/resteasy-containerrequestfilter-example/
了解如何使用 RESTEasy ContainerRequestFilter
创建安全筛选器,该筛选器能够对基于 RESTEasy 的 Web 应用执行认证和授权。
1. RESTEasy ContainerRequestFilter
和ContainerReponseFilter
最近发布了新的 RESTEasy 版本 3.0.2.Final,并使其与 JAX-RS 2.0 兼容。 如果您还记得以前的 JAX-RS 版本没有关于实现过滤器和拦截器的规范。 这就是所有 JAX-RS 实现都有自己独特风格的原因。 RESTEasy 的PreProcessorInterceptor
和PostProcessorInterceptor
现在已弃用。
现在,JAX-RS 在过滤器和拦截器方面有了自己的规范。 您可以阅读 Bill Burke 的帖子中的详细讨论。
在 resteasy 中,过滤器在调用resource
方法之前和之后运行。 这些过滤器实质上是ContainerRequestFilter
和ContainerReponseFilter
。 ContainerRequestFilter
在调用 JAX-RS 资源方法之前运行。 ContainerResponseFilter
在调用 JAX-RS 资源方法之后运行。 需要注意的是,ContainerRequestFilter
有两种风格:匹配前和匹配后。 预先匹配的ContainerRequestFilter
用@PreMatching
注解指定,并且将在 JAX-RS 资源方法与传入的 HTTP 请求匹配之前执行。 在匹配 Java 资源方法之后,执行匹配后的ContainerRequestFilters
。
过滤器修改请求或响应头时,拦截器处理消息正文。 它们可用于实现特定的内容编码。 它们可用于生成数字签名,或在编组 Java 对象模型之前或之后发布或预处理 Java 对象模型。
2. RESTEasy ContainerRequestFilter
示例
在本文中,我将修改 resteasy 认证和授权教程,该教程最初是使用PreProcessorInterceptor
在 RESTEasy 2.3.1.GA 中编写的。 我已将其更新为基于 JAX-RS 2.0 规范的 RESTEasy 版本 3.0.2.Final。
2.1 更新 Maven 依赖项
在使用 Maven 时,我已经更新了pom.xml
文件,如下所示。 如果您使用的是 ant 或 jar 文件,请相应地更新所需的 jar。
<dependencies>
<!-- core library -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<version>3.0.2.Final</version>
</dependency>
<!-- JAXB support -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxb-provider</artifactId>
<version>3.0.2.Final</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>jaxrs-api</artifactId>
<version>3.0.2.Final</version>
</dependency>
<dependency>
<groupId>net.sf.scannotation</groupId>
<artifactId>scannotation</artifactId>
<version>1.0.3</version>
</dependency>
</dependencies>
2.2 RESTEasy 安全拦截器
由于 JAX-RS 2.0 具有用于处理前后请求的过滤器,因此我们将使用ContainerRequestFilter
接口。 记住PreProcessorInterceptor
现在已弃用。
@Provider
public class SecurityInterceptor implements javax.ws.rs.container.ContainerRequestFilter
{
@Override
public void filter(ContainerRequestContext requestContext)
{
//More code...
}
}
现在,首先我们必须访问资源方法以检查安全性约束及其定义的属性。
ResourceMethodInvoker methodInvoker = (ResourceMethodInvoker)
requestContext.getProperty("org.jboss.resteasy.core.ResourceMethodInvoker");
Method method = methodInvoker.getMethod();
现在我们可以访问资源方法了。 现在,一切将与我们之前所做的相同。 即
- 检查
PermitAll
注解(如果存在),则无需进一步检查任何内容 - 检查
DenyAll
注解(如果存在),然后返回拒绝访问 - 检查
RolesAllowed
注解,然后从注解中获取所需的角色。 从请求中获取授权信息,并根据应用逻辑进行匹配。 如果授权成功,则授予访问权限,否则返回拒绝访问权限。
2.3 RESTEasy SecurityInterceptor
源代码
SecurityInterceptor
的完整代码如下。
package com.howtodoinjava.demo.rest.security;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import javax.annotation.security.DenyAll;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.Provider;
import org.jboss.resteasy.core.Headers;
import org.jboss.resteasy.core.ResourceMethodInvoker;
import org.jboss.resteasy.core.ServerResponse;
import org.jboss.resteasy.util.Base64;
/**
* This interceptor verify the access permissions for a user
* based on username and passowrd provided in request
* */
@Provider
public class SecurityInterceptor implements javax.ws.rs.container.ContainerRequestFilter
{
private static final String AUTHORIZATION_PROPERTY = "Authorization";
private static final String AUTHENTICATION_SCHEME = "Basic";
private static final ServerResponse ACCESS_DENIED = new ServerResponse("Access denied for this resource", 401, new Headers<Object>());;
private static final ServerResponse ACCESS_FORBIDDEN = new ServerResponse("Nobody can access this resource", 403, new Headers<Object>());;
private static final ServerResponse SERVER_ERROR = new ServerResponse("INTERNAL SERVER ERROR", 500, new Headers<Object>());;
@Override
public void filter(ContainerRequestContext requestContext)
{
ResourceMethodInvoker methodInvoker = (ResourceMethodInvoker) requestContext.getProperty("org.jboss.resteasy.core.ResourceMethodInvoker");
Method method = methodInvoker.getMethod();
//Access allowed for all
if( ! method.isAnnotationPresent(PermitAll.class))
{
//Access denied for all
if(method.isAnnotationPresent(DenyAll.class))
{
requestContext.abortWith(ACCESS_FORBIDDEN);
return;
}
//Get request headers
final MultivaluedMap<String, String> headers = requestContext.getHeaders();
//Fetch authorization header
final List<String> authorization = headers.get(AUTHORIZATION_PROPERTY);
//If no authorization information present; block access
if(authorization == null || authorization.isEmpty())
{
requestContext.abortWith(ACCESS_DENIED);
return;
}
//Get encoded username and password
final String encodedUserPassword = authorization.get(0).replaceFirst(AUTHENTICATION_SCHEME + " ", "");
//Decode username and password
String usernameAndPassword = null;
try {
usernameAndPassword = new String(Base64.decode(encodedUserPassword));
} catch (IOException e) {
requestContext.abortWith(SERVER_ERROR);
return;
}
//Split username and password tokens
final StringTokenizer tokenizer = new StringTokenizer(usernameAndPassword, ":");
final String username = tokenizer.nextToken();
final String password = tokenizer.nextToken();
//Verifying Username and password
System.out.println(username);
System.out.println(password);
//Verify user access
if(method.isAnnotationPresent(RolesAllowed.class))
{
RolesAllowed rolesAnnotation = method.getAnnotation(RolesAllowed.class);
Set<String> rolesSet = new HashSet<String>(Arrays.asList(rolesAnnotation.value()));
//Is user valid?
if( ! isUserAllowed(username, password, rolesSet))
{
requestContext.abortWith(ACCESS_DENIED);
return;
}
}
}
}
private boolean isUserAllowed(final String username, final String password, final Set<String> rolesSet)
{
boolean isAllowed = false;
//Step 1\. Fetch password from database and match with password in argument
//If both match then get the defined role for user from database and continue; else return isAllowed [false]
//Access the database and do this part yourself
//String userRole = userMgr.getUserRole(username);
String userRole = "ADMIN";
//Step 2\. Verify user role
if(rolesSet.contains(userRole))
{
isAllowed = true;
}
return isAllowed;
}
}
2.4 RESTEasy 安全过滤器演示
要测试安全代码,请将 Web 应用部署在任何应用服务器(例如 Tomcat)中。 现在,发送以下请求:
-
不带用户名和密码的 HTTP GET
http://localhost:8080/RESTEasyEtagDemo/user-service/users/1
用户能够成功访问 API。
-
不带用户名和密码的 HTTP PUT
http://localhost:8080/RESTEasyEtagDemo/user-service/users/1
用户无法访问 API。
-
添加基本授权凭据
-
带有用户名和密码的 HTTP PUT
http://localhost:8080/RESTEasyEtagDemo/user-service/users/1
用户能够访问受保护的 API
以上就是 resteasy 安全拦截器示例。 如果您有任何疑问或建议,请给我评论。
下载面向 Jboss 的源码
更新:以下是在 tomcat 7 中运行此项目的步骤。
今天,我再次致力于在 tomcat 7 上运行的该项目。要成功运行,我执行了以下步骤:
-
在 eclipse 中导入项目
-
在项目根文件夹中运行提示符
> mvn eclipse:eclipse -Dwtpversion=2.0
(参考) -
更新方法上的
@Produces
和@Consumes
注解 -
启动 tomcat 服务器并测试应用。 您将获得理想的结果。
下载面向 Tomcat 7 的源码
学习愉快!
RESTEasy 基本认证和授权教程
原文: https://howtodoinjava.com/resteasy/jax-rs-resteasy-basic-authentication-and-authorization-tutorial/
安全性是任何企业应用不可或缺的一部分。 安全涉及两个阶段,即认证和授权。 认证可验证您的身份。 授权会验证您有权执行的操作。 在本文中,我们将学习为 REST API 构建基于角色的基本认证/授权安全性。
Sections in this post:
Background information
Important classes/annotations used
Building the security interceptor
Testing authorization and authentication on REST APIs
背景信息
在本文中,我将尝试解决以下安全问题:
- 从 http 请求获取用户名和密码
- 获取适用的方法安全性详细信息
- 验证用户是否有权访问 API
- 如果访问无效,则返回有效的错误代码
另外,请注意,我正在为您实现以下部分:
- 访问数据库以获取密码,以验证请求中提供的密码
- 从数据库中为有效用户获取允许的角色
我正在重用为 ETag 使用演示编写的代码。 该项目有 2 个主要类:
User.java
:代表系统中用户资源的模型类
@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name = "user")
public class User implements Serializable
{
@XmlAttribute(name = "id")
private int id;
@XmlAttribute(name="uri")
private String uri;
@XmlElement(name = "firstName")
private String firstName;
@XmlElement(name = "lastName")
private String lastName;
@XmlElement(name="last-modified")
private Date lastModified;
//Getters and setters
}
UserService.java
:此类具有 GET 和 PUT API,用于获取/修改用户资源。
@Path("/user-service")
public class UserService
{
@GET
@Path("/users/{id}")
public Response getUserById(@PathParam("id") int id, @Context Request req)
{
Response.ResponseBuilder rb = Response.ok(UserDatabase.getUserById(id));
return rb.build();
}
@PUT
@Path("/users/{id}")
public Response updateUserById(@PathParam("id") int id)
{
//Update the User resource
UserDatabase.updateUser(id);
return Response.status(200).build();
}
}
在本教程中,我将使用上述 2 个 API 并确保它们的安全。
使用的重要类/注解
JAX-RS 提供必要的注解,以在基于 RESTEasy 的应用中实现安全性。 重要的注解是:
javax.annotation.security.PermitAll
:此注解应用于 API 时,用于声明任何用户都可以自由访问该 API。 此 API 没有访问限制。javax.annotation.security.DenyAll
:此注解用于声明无论分配给任何用户的角色,都不允许任何人访问此 API。javax.annotation.security.RolesAllowed
:此注解有助于标识访问该 API 的任何用户都需要哪些角色。 如果通过用户/名称和密码验证了用户,但没有此注解指定的角色,则将不允许该用户访问。javax.ws.rs.core.HttpHeaders
:一个接口,提供对与 http 请求一起附加的 HTTP 标头信息的访问。org.jboss.resteasy.util.Base64
:此类有助于在 Base64 表示法之间进行编码和解码。
除上述类外,org.jboss.resteasy.spi.interception.PreProcessInterceptor
将用于创建安全拦截器。 javax.ws.rs.ext.Provider
注解将用于在 resteasy 上下文中注册拦截器。
构建安全拦截器
在构建拦截器之前,请确保 API 的安全。 我在 GET/PUT API 中添加了@PermitAll
和@RolesAllowed
注解。 GET API 向所有人开放,即没有访问限制。 PUT API 需要具有ADMIN
功能的有效用户。
@Path("/user-service")
public class UserService
{
@PermitAll
@GET
@Path("/users/{id}")
public Response getUserById(@PathParam("id") int id, @Context Request req)
{
Response.ResponseBuilder rb = Response.ok(UserDatabase.getUserById(id));
return rb.build();
}
@RolesAllowed("ADMIN")
@PUT
@Path("/users/{id}")
public Response updateUserById(@PathParam("id") int id)
{
//Update the User resource
UserDatabase.updateUser(id);
return Response.status(200).build();
}
}
通过实现org.jboss.resteasy.spi.interception
.PreProcessInterceptor
接口来构建安全拦截器。 该接口有一个实现类的类需要实现的方法。
public ServerResponse preProcess(HttpRequest request, ResourceMethod methodInvoked)
throws Failure, WebApplicationException;
methodInvoked
参数提供对方法的访问,该方法将由于调用 REST API 而被调用。
请求参数提供对客户端传递的请求标头和参数的访问。
拦截器类的定义如下:
@Provider
@ServerInterceptor
public class SecurityInterceptor implements PreProcessInterceptor
{
//more code
}
@Provider
进行 resteasy 上下文扫描,以将该类添加为上下文类。
@ServerInterceptor
注解将此类添加到可用的拦截器列表中。
现在,第一步是检查 API 是否对所有人都打开或对所有人都关闭。 这可以通过检查@PermitAll
和@DenyAll
注解来完成。
Method method = methodInvoked.getMethod();
//Access allowed for all
if(method.isAnnotationPresent(PermitAll.class))
{
return null; //Return null to continue request processing
}
//Access denied for all
if(method.isAnnotationPresent(DenyAll.class))
{
return ACCESS_FORBIDDEN; //Return access denied to user
}
下一步是从请求中获取授权标头。 从授权标头中,我们将检索用户发送的用户名和密码。
//Get request headers
final HttpHeaders headers = request.getHttpHeaders();
//Fetch authorization header
final List<String> authorization = headers.getRequestHeader(AUTHORIZATION_PROPERTY);
//If no authorization information present; block access
if(authorization == null || authorization.isEmpty())
{
return ACCESS_DENIED;
}
//Get encoded username and password
final String encodedUserPassword = authorization.get(0).replaceFirst(AUTHENTICATION_SCHEME + " ", "");
//Decode username and password
String usernameAndPassword;
try {
usernameAndPassword = new String(Base64.decode(encodedUserPassword));
} catch (IOException e) {
return SERVER_ERROR;
}
//Split username and password tokens
final StringTokenizer tokenizer = new StringTokenizer(usernameAndPassword, ":");
final String username = tokenizer.nextToken();
final String password = tokenizer.nextToken();
现在,我们将从@RolesAllowed
注解中获取访问 API 所需的角色。
RolesAllowed rolesAnnotation = method.getAnnotation(RolesAllowed.class);
Set<String> rolesSet = new HashSet<String>(Arrays.asList(rolesAnnotation.value()));
RolesSet
包含所有可以允许 API 访问的角色。
现在,在最后一步中,我们必须做两件事。 首先,从任何数据库服务验证用户名和密码,如果用户有效,则获取分配给他的角色。 该角色将用于检查用户的认证成功。
if(rolesSet.contains(userRole))
{
isAllowed = true;
}
SecurityInterceptor.java
的完整源代码如下所示:
/**
* This interceptor verify the access permissions for a user
* based on username and passowrd provided in request
* */
@Provider
@ServerInterceptor
public class SecurityInterceptor implements PreProcessInterceptor
{
private static final String AUTHORIZATION_PROPERTY = "Authorization";
private static final String AUTHENTICATION_SCHEME = "Basic";
private static final ServerResponse ACCESS_DENIED = new ServerResponse("Access denied for this resource", 401, new Headers<Object>());;
private static final ServerResponse ACCESS_FORBIDDEN = new ServerResponse("Nobody can access this resource", 403, new Headers<Object>());;
private static final ServerResponse SERVER_ERROR = new ServerResponse("INTERNAL SERVER ERROR", 500, new Headers<Object>());;
@Override
public ServerResponse preProcess(HttpRequest request, ResourceMethod methodInvoked) throws Failure, WebApplicationException
{
Method method = methodInvoked.getMethod();
//Access allowed for all
if(method.isAnnotationPresent(PermitAll.class))
{
return null;
}
//Access denied for all
if(method.isAnnotationPresent(DenyAll.class))
{
return ACCESS_FORBIDDEN;
}
//Get request headers
final HttpHeaders headers = request.getHttpHeaders();
//Fetch authorization header
final List<String> authorization = headers.getRequestHeader(AUTHORIZATION_PROPERTY);
//If no authorization information present; block access
if(authorization == null || authorization.isEmpty())
{
return ACCESS_DENIED;
}
//Get encoded username and password
final String encodedUserPassword = authorization.get(0).replaceFirst(AUTHENTICATION_SCHEME + " ", "");
//Decode username and password
String usernameAndPassword;
try {
usernameAndPassword = new String(Base64.decode(encodedUserPassword));
} catch (IOException e) {
return SERVER_ERROR;
}
//Split username and password tokens
final StringTokenizer tokenizer = new StringTokenizer(usernameAndPassword, ":");
final String username = tokenizer.nextToken();
final String password = tokenizer.nextToken();
//Verifying Username and password
System.out.println(username);
System.out.println(password);
//Verify user access
if(method.isAnnotationPresent(RolesAllowed.class))
{
RolesAllowed rolesAnnotation = method.getAnnotation(RolesAllowed.class);
Set<String> rolesSet = new HashSet<String>(Arrays.asList(rolesAnnotation.value()));
//Is user valid?
if( ! isUserAllowed(username, password, rolesSet))
{
return ACCESS_DENIED;
}
}
//Return null to continue request processing
return null;
}
private boolean isUserAllowed(final String username, final String password, final Set<String> rolesSet)
{
boolean isAllowed = false;
//Step 1\. Fetch password from database and match with password in argument
//If both match then get the defined role for user from database and continue; else return isAllowed [false]
//Access the database and do this part yourself
//String userRole = userMgr.getUserRole(username);
String userRole = "ADMIN";
//Step 2\. Verify user role
if(rolesSet.contains(userRole))
{
isAllowed = true;
}
return isAllowed;
}
}
在 REST API 上测试授权和认证
要测试安全代码,请将 Web 应用部署在任何应用服务器(例如 Tomcat)中。 现在,发送以下请求:
HTTP GET http://localhost:8080/RESTEasyEtagDemo/user-service/users/1
,没有用户名和密码
用户能够成功访问 API。
HTTP PUT http://localhost:8080/RESTEasyEtagDemo/user-service/users/1
,没有用户名和密码
用户无法访问 API。
添加基本授权凭证
HTTP PUT http://localhost:8080/RESTEasyEtagDemo/user-service/users/1
,其中添加了用户名和密码
用户能够访问受保护的 API
这就是本教程中的全部内容。 如果您有任何疑问或建议,请给我评论。
下载源代码
祝您学习愉快!
Maven – 父子 POM 示例
原文: https://howtodoinjava.com/maven/maven-parent-child-pom-example/
Maven 父 POM (或超级 POM)用于构造项目,以避免重复或重复使用 pom 文件之间的继承配置。 它有助于长期轻松维护。
如果在父 POM 和子 POM 中都使用不同的值配置了任何依赖项或属性,则子 POM 值将具有优先级。
Table of Contents
Parent POM Contents
Parent POM and Child POM
Parent POM Relative Path
Demo
父 POM 内容
可以使用包pom
声明父 POM。 它不打算分发,因为仅从其他项目中引用了它。
Maven 父 pom 可以包含几乎所有内容,并且可以继承到子 pom 文件中,例如:
- 通用数据 – 开发人员的姓名,SCM 地址,分发管理等
- 常数 – 例如版本号
- 共同的依赖项 – 所有子项共同的。 与在单个 pom 文件中多次写入它们具有相同的效果。
- 属性 – 例如插件,声明,执行和 ID。
- 配置
- 资源
父 POM 和子 POM 示例
为了匹配父 POM,Maven 使用两个规则:
- 在项目的根目录或给定的相对路径中有一个 pom 文件。
- 子 POM 文件中的引用包含与父 POM 文件中所述相同的坐标。
父 POM
此处,父 POM 为 JUnit 和 spring 框架配置了基本项目信息和两个依赖项。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd;
<modelVersion>4.0.0</modelVersion>
<groupId>com.howtodoinjava.demo</groupId>
<artifactId>MavenExamples</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>MavenExamples Parent</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<junit.version>3.8.1</junit.version>
<spring.version>4.3.5.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
</project>
子 POM
现在,子 POM 需要使用parent
标签并指定groupId/artifactId/version
属性来引用父 POM。 这个 pom 文件将从父 POM 继承所有属性和依赖项,并且还可以包括子项目特定的依赖项。
<project xmlns="http://maven.apache.org/POM/4.0.0"
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">
<!--The identifier of the parent POM-->
<parent>
<groupId>com.howtodoinjava.demo</groupId>
<artifactId>MavenExamples</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>MavenExamples</artifactId>
<name>MavenExamples Child POM</name>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-security</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
</project>
父 POM 相对路径
默认情况下,Maven 首先在项目的根目录下查找父 POM,然后在本地仓库中查找,最后在远程仓库中查找。 如果父 POM 文件不在任何其他位置,则可以使用代码标签。 该相对路径应相对于项目根。
如果未明确给出相对路径,则默认为..
,即当前项目的父目录中的 pom。
<project xmlns="http://maven.apache.org/POM/4.0.0"
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">
<!--The identifier of the parent POM-->
<parent>
<groupId>com.howtodoinjava.demo</groupId>
<artifactId>MavenExamples</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../baseapp/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>MavenExamples</artifactId>
<name>MavenExamples Child POM</name>
<packaging>jar</packaging>
</project>
演示
让我们学习创建具有父子关系的 Maven 项目。
1)创建 Maven 父项目
项目创建向导。
Maven 项目创建向导
选择项目原型。
Maven 快速启动原型
填写详细信息并创建项目。
创建 Maven 父项目
现在在pom.xml
中将包从 jar 更改为 pom。
<packaging>jar</packaging> //previous
<packaging>pom</packaging> //New
此外,添加项目属性和依赖项。
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<junit.version>3.8.1</junit.version>
<spring.version>4.2.3.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
2)创建 Maven 子项目
就像创建父项目一样,创建一个新的 Maven 项目。 更改项目的特定详细信息,例如名称等。
创建 Maven 子项目
现在,使用父级参考更新子项目的pom.xml
文件。
<!--The identifier of the parent POM -->
<parent>
<groupId>com.howtodoinjava.demo</groupId>
<artifactId>MavenExamples</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
现在,您可以自由使用父 pom 的元素(例如属性)。 您的子项目已继承了父项目。 要对此进行测试,只需从子项目的pom.xml
中删除所有依赖项。
现在,在 Java 构建路径中检查它的库。 您将在那里看到所有父级的依赖项。
子项目的 Java 构建路径
将我的问题放在评论部分。
学习愉快!
参考: Maven 继承
RESTEasy JAXB XML 示例
原文: https://howtodoinjava.com/resteasy/resteasy-jaxb-xml-example/
RESTEasy 是 JBOSS 提供的 JAX-RS 规范的实现,用于构建 RESTful Web 服务和 RESTful Java 应用。 尽管这不仅限于仅在 JBOSS 中使用,您还可以与其他服务器协作。
另一方面, JAXB 用于将 Java 类映射到等效的 xml 文档,反之亦然。 它是使用 JAXB 的编组和解组功能完成的。
在本文中,我演示了将 JAXB 与 RESTEasy 协作的方式,以将 API 响应转换为 xml 格式。
使用的环境:
- RESTEasy 2.3.1.GA
- RESTEasy JAXB 供应器 2.3.1
- Tomcat 7
- JDK 1.6
请按照以下步骤构建演示应用。
1)创建一个 Maven 项目并转换为 Eclipse Web 项目
mvn archetype:generate -DgroupId=com.howtodoinjava -DartifactId=RESTfulDemoApplication
-DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false
mvn eclipse:eclipse -Dwtpversion=2.0
2)更新pom.xml
中的运行时依赖项
<project xmlns="http://maven.apache.org/POM/4.0.0" 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.howtodoinjava</groupId>
<artifactId>RESTfulDemoApplication</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>RESTfulDemoApplication Maven Webapp</name>
<url>http://maven.apache.org</url>
<repositories>
<repository>
<id>jboss</id>
<url>http://repository.jboss.org/maven2</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!-- core library -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<version>2.3.1.GA</version>
</dependency>
<dependency>
<groupId>net.sf.scannotation</groupId>
<artifactId>scannotation</artifactId>
<version>1.0.2</version>
</dependency>
<!-- JAXB support -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxb-provider</artifactId>
<version>2.3.1.GA</version>
</dependency>
</dependencies>
<build>
<finalName>RESTfulDemoApplication</finalName>
</build>
</project>
3)更新web.xml
文件以实现 Resteasy 特定的 Servlet 映射
<!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>Archetype Created Web Application</display-name>
<!-- Auto scan REST service -->
<context-param>
<param-name>resteasy.scan</param-name>
<param-value>true</param-value>
</context-param>
<servlet>
<servlet-name>resteasy-servlet</servlet-name>
<servlet-class>
org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>resteasy-servlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
4)编写服务类和模型类
User.java
package com.howtodoinjava.model;
import java.io.Serializable;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name = "user")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
@XmlAttribute(name = "id")
private int id;
@XmlElement(name = "firstName")
private String firstName;
@XmlElement(name = "lastName")
private String lastName;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
UserManagementModule.java
package com.howtodoinjava.service;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Response;
import com.howtodoinjava.model.User;
@Path("/user-management")
public class UserManagementModule
{
@GET
@Path("/users/{id}")
public Response getUserById(@PathParam("id") Integer id)
{
User user = new User();
user.setId(id);
user.setFirstName("Lokesh");
user.setLastName("Gupta");
return Response.status(200).entity(user).build();
}
}
5)运行应用
当我们在 tomcat 中部署以上构建的应用并单击 URL:“http://localhost:8080/RESTfulDemoApplication/user-management/users/10
”时,以下是响应。
RESTEasy JAXB 示例
祝您学习愉快!
RESTEasy Jettison JSON 示例
原文: https://howtodoinjava.com/resteasy/resteasy-jettison-json-example/
RESTEasy 是 JBOSS 提供的 JAX-RS 规范的实现,用于构建 RESTful Web 服务和 RESTful Java 应用。 尽管这不仅限于仅在 JBOSS 中使用,您还可以与其他服务器协作。
Jettison 是可读写 JSON 的 Java API(如 STaX 和 DOM)的集合。 它是使用编组和解组功能完成的。
在本文中,我演示了将 Jettison 与 RESTEasy 结合使用的方法,以将 API 响应转换为 json 格式。
使用的环境:
- RESTEasy 2.3.1.GA
- RESTEasy Jettison 供应器 2.3.1
- Tomcat 7
- JDK 1.6
请按照以下步骤构建演示应用。
1)创建一个 Maven 项目并转换为 Eclipse Web 项目
mvn archetype:generate -DgroupId=com.howtodoinjava -DartifactId=RESTfulDemoApplication
-DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false
mvn eclipse:eclipse -Dwtpversion=2.0
2)更新pom.xml
中的运行时依赖项
<project xmlns="http://maven.apache.org/POM/4.0.0" 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.howtodoinjava</groupId>
<artifactId>RESTfulDemoApplication</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>RESTfulDemoApplication Maven Webapp</name>
<url>http://maven.apache.org</url>
<repositories>
<repository>
<id>jboss</id>
<url>http://repository.jboss.org/maven2</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!-- core library -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<version>2.3.1.GA</version>
</dependency>
<dependency>
<groupId>net.sf.scannotation</groupId>
<artifactId>scannotation</artifactId>
<version>1.0.2</version>
</dependency>
<!-- Jettison support -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jettison-provider</artifactId>
<version>2.3.1.GA</version>
</dependency>
</dependencies>
<build>
<finalName>RESTfulDemoApplication</finalName>
</build>
</project>
3)更新web.xml
文件以实现 Resteasy 特定的 Servlet 映射
<!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>Archetype Created Web Application</display-name>
<!-- Auto scan REST service -->
<context-param>
<param-name>resteasy.scan</param-name>
<param-value>true</param-value>
</context-param>
<servlet>
<servlet-name>resteasy-servlet</servlet-name>
<servlet-class>
org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>resteasy-servlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
4)编写服务类和模型类
User.java
package com.howtodoinjava.model;
import java.io.Serializable;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name = "user")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
@XmlAttribute(name = "id")
private int id;
@XmlElement(name = "firstName")
private String firstName;
@XmlElement(name = "lastName")
private String lastName;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
UserManagementModule.java
package com.howtodoinjava.service;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Response;
import com.howtodoinjava.model.User;
@Path("/user-management")
public class UserManagementModule
{
@GET
@Path("/users/{id}")
@Produces("application/json")
public Response getUserById(@PathParam("id") Integer id)
{
User user = new User();
user.setId(id);
user.setFirstName("Lokesh");
user.setLastName("Gupta");
return Response.status(200).entity(user).build();
}
}
5)运行应用
当我们在 tomcat 中部署上述构建的应用并单击 URL:http://localhost:8080/RESTfulDemoApplication/user-management/users/10
时,以下是响应。
RESTEasy + Jettison 示例
学习愉快!
源码下载
Jackson 的 RESTEasy JSON 示例
原文: https://howtodoinjava.com/resteasy/resteasy-jackson-json-example/
RESTEasy 是 JBOSS 提供的 JAX-RS 规范的实现,用于构建 RESTful Web 服务和 RESTful Java 应用。 尽管这不仅限于仅在 JBOSS 中使用,您还可以与其他服务器协作。
Jackson 是用于处理 JSON 数据格式的多功能 Java 库。 Jackson 的目标是为开发人员提供快速,正确,轻巧和人体工程学的最佳组合。
在本文中,我演示了将 Jackson 与 RESTEasy 结合使用的方式,以 JSON 格式转换 API 响应。
使用的环境:
- RESTEasy 2.3.1.GA
- RESTEasy Jackson 供应器 2.3.1
- Tomcat 7
- JDK 1.6
请按照以下步骤构建演示应用。
1)创建一个 Maven 项目并转换为 Eclipse Web 项目
mvn archetype:generate -DgroupId=com.howtodoinjava -DartifactId=RESTfulDemoApplication
-DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false
mvn eclipse:eclipse -Dwtpversion=2.0
2)更新pom.xml
中的运行时依赖项
<project xmlns="http://maven.apache.org/POM/4.0.0" 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.howtodoinjava</groupId>
<artifactId>RESTfulDemoApplication</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>RESTfulDemoApplication Maven Webapp</name>
<url>http://maven.apache.org</url>
<repositories>
<repository>
<id>jboss</id>
<url>http://repository.jboss.org/maven2</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!-- core library -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<version>2.3.1.GA</version>
</dependency>
<dependency>
<groupId>net.sf.scannotation</groupId>
<artifactId>scannotation</artifactId>
<version>1.0.2</version>
</dependency>
<!-- Jackson support -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jackson-provider</artifactId>
<version>2.3.1.GA</version>
</dependency>
</dependencies>
<build>
<finalName>RESTfulDemoApplication</finalName>
</build>
</project>
3)更新web.xml
文件以实现 Resteasy 特定的 Servlet 映射
<!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>Archetype Created Web Application</display-name>
<!-- Auto scan REST service -->
<context-param>
<param-name>resteasy.scan</param-name>
<param-value>true</param-value>
</context-param>
<servlet>
<servlet-name>resteasy-servlet</servlet-name>
<servlet-class>
org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>resteasy-servlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
4)编写服务类和模型类
User.java
package com.howtodoinjava.model;
import java.io.Serializable;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name = "user")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
@XmlAttribute(name = "id")
private int id;
@XmlElement(name = "firstName")
private String firstName;
@XmlElement(name = "lastName")
private String lastName;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
UserManagementModule.java
package com.howtodoinjava.service;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Response;
import com.howtodoinjava.model.User;
@Path("/user-management")
public class UserManagementModule
{
@GET
@Path("/users/{id}")
@Produces("application/json")
public Response getUserById(@PathParam("id") Integer id)
{
User user = new User();
user.setId(id);
user.setFirstName("Lokesh");
user.setLastName("Gupta");
return Response.status(200).entity(user).build();
}
}
5)运行应用
当我们在 tomcat 中部署以上构建的应用并单击 URL:“http://localhost:8080/RESTfulDemoApplication/user-management/users/10
”时,以下是响应。
RESTEasy + Jackson 示例
单击下面的代码下载此示例的源代码。
祝您学习愉快!