RESTful中使用HTTP协议作为应用层协议。本文主要介绍http协议的 GET、POST、PUT、PATCH、DELETE请求的语义。

HTTP GET: 主要用于获取URI所代表的资源的表述,但不应该改变资源,是安全且幂等的。

HTTP POST: 用于创建资源,POST对应的URI是一个表述的类型,比如创建一篇文章应该是

POST http://www.climbran.com/article

多次POST请求会创建多篇文章。

HTTP PUT: 用于创建或更新资源,创建资源时与POST相比,区别在于PUT是幂等的,例如:

PUT http://www.climbran.com/article/1

表示创建或更新id为1的文章。

HTTP PATCH: 用于更新资源,与PUT相比,区别在于PUT应带有URI对应表述的全部信息,PATCH只带有部分信息,可用于表述信息量非常大,但只希望修改其中一小部分的时候。PATCH同样是幂等的。

HTTP DELETE: 用于删除资源,应该是幂等的。

例子:

GET /zoos:列出所有动物园
POST /zoos:新建一个动物园
GET /zoos/ID:获取某个指定动物园的信息
PUT /zoos/ID:更新某个指定动物园的信息(提供该动物园的全部信息)
PATCH /zoos/ID:更新某个指定动物园的信息(提供该动物园的部分信息)
DELETE /zoos/ID:删除某个动物园
GET /zoos/ID/animals:列出某个指定动物园的所有动物
DELETE /zoos/ID/animals/ID:删除某个指定动物园的指定动物

各种请求的返回结果一般是:

GET /collection:返回资源对象的列表(数组)
GET /collection/resource:返回单个资源对象
POST /collection:返回新生成的资源对象
PUT /collection/resource:返回完整的资源对象
PATCH /collection/resource:返回完整的资源对象
DELETE /collection/resource:返回一个空文档

最早通过在网上资料学习restful时,,都是说每个url代表一个资源(resource),在项目设计时,会发现有时候为一个资源设计url时会发生冲突。


比如一个教育系统中有老师和学生两种类型账号,一个班级的url为/class,而在老师账号中还有一个班级管理模块,同样使用的班级这一资源,但/class这一url已被使用。


后来突然想到我们是不是能把浏览的班级和老师的班级管理模块当成两种不同资源?于是设计成了


/class // 班级资源
/classmgr //班级管理资源

最近看《restful web APIs》时看到表述(representation of the resource)一词,突然顿悟到到其实上面两个url对应的是同一个资源的两个不同表述。每个url代表一个资源的说法也并没错,每个资源可以有多个表述,url和资源是多对一关系。在客户端看来,不同url获取的是不同表述,但最终定位的可能是同一资源。

一般通过xml文件配置站点地图时每个页面生成的面包屑都是固定的,现在需要动态生成面包屑(eg:首页-文章-文章名,其中文章名需要动态生成),结合网上生成静态sitemap的代码做出以下实现:

com.climbran.tag.SiteMapTag.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
package com.climbran.tag;  

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

/**
* 站点导航标签的实现
* @author swe
*
*/

public class SiteMapTag extends TagSupport {

private static final long serialVersionUID = -3531938467909884528L;
private String currentFilePath;
private Element target;
private String dynamicTitle;
private String dynamicTitle1;
private String dynamicUrl;
private String dynamicUrl1;
private final static String dynamic = "{dynamic}";
private final static String dynamic_1 = "{dynamic1}";

@Override
public int doStartTag() throws JspException {
HttpServletRequest request = (HttpServletRequest) this.pageContext.getRequest();
currentFilePath = request.getRequestURI().replaceFirst(request.getContextPath(), "");
try {
Element root = (Element)pageContext.getServletContext().getAttribute("webSiteMapSet");
if(root==null){
SAXReader reader = new SAXReader();
InputStream inputStream = SiteMapTag.class.getClassLoader().getResourceAsStream("sitemap.xml");
Document document = reader.read(inputStream);
root = document.getRootElement();
pageContext.getServletContext().setAttribute("webSiteMapSet", root);
}
parseParent(root);
StringBuffer content = new StringBuffer("");
List<String> titles = new ArrayList<String>();
List<String> hrefs = new ArrayList<String>();

while(target!=null){
Attribute attTitle = target.attribute("title");
if(attTitle!=null){
titles.add(attTitle.getText());
}

Attribute attHref = target.attribute("href");
if(attHref!=null){
hrefs.add(attHref.getText());
}else{
hrefs.add("");
}

target = target.getParent();
}
content.append("<div class='breadMenu'><ol class='breadcrumb clearfix'>");
for (int i = titles.size()-1; i >=0; i--) {
String href = hrefs.get(i);
if(dynamicUrl!=null)
href=href.replace(dynamic, dynamicUrl);
if(dynamicUrl1!=null)
href=href.replace(dynamic_1, dynamicUrl1);
if(href.equals("")||i==0){
if(titles.get(i).equals(dynamic))
content.append("<li>" + dynamicTitle +"</li>");
else if(titles.get(i).equals(dynamic_1))
content.append("<li>" + dynamicTitle1 +"</li>");
else
content.append("<li>" + titles.get(i)+"</li>");
}else{
if(titles.get(i).equals(dynamic))
content.append("<li><a href='"+href+"'>"+dynamicTitle+"</a></li>");
else if(titles.get(i).equals(dynamic_1))
content.append("<li><a href='"+href+"'>"+dynamicTitle1+"</a></li>");
else
content.append("<li><a href='"+href+"'>"+titles.get(i)+"</a></li>");
}

}
content.append("</ol></div>");
if(content.length()>0){
this.pageContext.getOut().println(content.delete(content.length()-0, content.length()));
}


} catch (Exception e) {
e.printStackTrace();
throw new JspException(e);
}


return super.doStartTag();
}

private void parseParent(Element parent){
Iterator<Element> it = parent.elementIterator();
while(it.hasNext()){
Element temp = it.next();
Attribute attr = temp.attribute("path");
if(attr!=null){
if(attr.getText().equals(currentFilePath)){
target = temp;
return;
}
}
parseParent(temp);
}
}

public String getDynamicTitle() {
return dynamicTitle;
}

public void setDynamicTitle(String dynamicTitle) {
this.dynamicTitle = dynamicTitle;
}

public String getDynamicTitle1() {
return dynamicTitle1;
}

public void setDynamicTitle1(String dynamicTitle1) {
this.dynamicTitle1 = dynamicTitle1;
}

public String getDynamicUrl() {
return dynamicUrl;
}

public void setDynamicUrl(String dynamicUrl) {
this.dynamicUrl = dynamicUrl;
}

public String getDynamicUrl1() {
return dynamicUrl1;
}

public void setDynamicUrl1(String dynamicUrl1) {
this.dynamicUrl1 = dynamicUrl1;
}
}

src/main/resources/sitemap.xml
1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8"?>

<!-- 配置文件说明, sitemap根节点,title是页面上显示的内容,
href是超链接,path是该请求页面的物理路径
-->


<sitemap title="首页" href="/home">
<node path="/WEB-INF/views/article/index.jsp" href="/article" title="全部文章">
<node path="/WEB-INF/views/article/new.jsp" href="/article/new" title="发表文章"/>
<node path="/WEB-INF/views/article/show.jsp" href="/article/{dynamic}" title="{dynamic}"/><!-- 文章名称 -->
</node>
</sitemap>
WEB-INF/tlds/sitemap.tld
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<?xml version="1.0" encoding="UTF-8"?>
<taglib xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
version="2.1">


<description>站点导航标签</description>
<display-name>myTaglib siteMap</display-name>
<tlib-version>1.1</tlib-version>
<short-name>tagUtil</short-name>
<uri>http://tag.climbran.com/jsp/tagutil</uri>

<tag>
<description>面包屑标签</description>
<name>siteMap</name>
<tag-class>com.climbran.tag.SiteMapTag</tag-class>
<body-content>empty</body-content>
<attribute>
<description>title变量</description>
<name>dynamicTitle</name>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<description>url变量</description>
<name>dynamicUrl</name>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<description>title1变量</description>
<name>dynamicTitle1</name>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<description>url1变量</description>
<name>dynamicUrl1</name>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>

</taglib>
WEB-INF/views/article/show.jsp
1
2
3
4
5
6
7
8
9
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@taglib uri="http://tag.climbran.com/jsp/tagutil" prefix="tagUtil" %>
<head>
<title>article</title>
</head>
<body>
<tagUtil:siteMap dynamicUrl="${article.id}" dynamicTitle="${article.title}"/>
</body>

foreach循环中无法直接对Iterable中的元素进行remove,可以通过下面方式实现:

1
2
3
4
for(String s : new ArrayList<String>().addAll(list)){ //list为一个List<String>
if(s.equals("need remove"))
list.remove(s);
}

看springside的showcase发现static块的直接使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package org.springside.examples.showcase.web;

import java.util.List;
.....

@Controller
@RequestMapping(value = "/account/user")
public class UserController {

private static Map<String, String> allStatus = Maps.newHashMap();

static {
allStatus.put("enabled", "有效");
allStatus.put("disabled", "无效");
}

......

}



static{},会在类被加载的时候执行且仅会被执行一次,一般用来初始化静态变量和调用静态方法。


虚拟机的生命周期中一个类只被加载一次;又因为static{}是伴随类加载执行的,所以,不管你new多少次对象实例,static{}都只执行一次。


类会在以下情况加载:

1、用Class.forName()显示加载的时候,如上面的示例一;

2、实例化一个类的时候,如将main()函数的内容改为:Test t=new Test();//这种形式其实和1相比,原理是相同的,都是显示的加载这个类,读者可以验证Test t=new Test();和Test t=(Test)Class.forName().newInstance();这两条语句效果相同。

3、调用类的静态方法的时候,如将main()函数的内容改为:Test.display();

4、调用类的静态变量的时候,如将main()函数的内容改为:System.out.println(Test.X);




以下两种情况类不会加载:

1、调用类的静态常量的时候,是不会加载类的,即不会执行static{}语句块,读者可以自己验证一下(将main()函数的内容改为System.out.println(Test.Y);),你会发现程序只输出了一个200;(这是java虚拟机的规定,当访问类的静态常量时,如果编译器可以计算出常量的值,则不会加载类,否则会加载类)

2、用Class.forName()形式的时候,我们也可以自己设定要不要加载类,如将Class.forName(“Test”)改为 Class.forName(“Test”,false,StaticBlockTest.class.getClassLoader()),你会发现程序什么都没有输出,即Test没有被加载,static{}没有被执行。


当带satic的静态初始化块与不带staic的非静态初始化块同时需要执行时,按照以下顺序执行:

所有类的静态初始化块(从父类自顶向下) –> 所有类的普通初始化块(从父类自顶向下) –> 类的构选器(从父类自顶向下)

Spring MVC请求时经常出现中文乱码,查看配置文件,web.xml中已配置:

1
2
3
4
5
6
7
8
9
10
11
12
<filter>  
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

度娘后发现该配置只对post请求有效,对于get请求还要对tomcat的service.xml进行配置,具体配置如下:
1
2
<Connector connectionTimeout="20000" port="8080" 
protocol="HTTP/1.1" redirectPort="8443" useBodyEncodingForURI="true"/>


上面配置对ajax的get请求无效,如果要同时对ajax请求有效则改为
1
2
<Connector connectionTimeout="20000" 
port="8080" protocol="HTTP/1.1" redirectPort="8443" URIEncoding="UTF-8"/>

SpringCtx.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public final class SpringCtx {
private SpringCtx() {
}

// Spring应用上下文环境
private static ApplicationContext context = new ClassPathXmlApplicationContext("/applicationContext.xml"); //new FileSystemXmlApplicationContext("classpath:applicationContext.xml");

public static <T> T getBean(Class<T> requiredType) {
T t = null;
try {
t = context.getBean(requiredType);
} catch (Exception e) {
e.printStackTrace();
}
return t;
}
}

C语言中回调函数由指针实现,java 中没有指针,可以通过以下方法实现:

1
2
3
public interface MyCallInterface{
public void method();
}
1
2
3
4
5
6
public Client implements MyCallInterface{
@Override
public void method(){
//TODO: 回调内容
}
}

调用时:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Caller{
private MyCallInterface callInterface;

public Caller(){
}

public void setCallFunc(MyCallInterface callInterface){
this.callInterface = callInterface;
}

public void call(){
callInterface.method();
}
}

1
2
3
4
5
6
7
public class Test{
public static void main(String args){
Caller caller = new Caller();
caller.setCallFunc(new Client());
caller.call();
}
}

或者
1
2
3
4
5
public class Caller{
public void call(Client client){
client.method();
}
}

1
2
3
4
5
6
public class Test{
public static void main(String args){
Caller caller = new Caller();
caller.call(new Client());
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
git config --global user.name "name"
git config --global user.email email@email.com
git config --global alias.co checkout
git init
git add -A
git commit -m "message"

git remote add origin git@gitservicename:username/reponame.git

git remote rm origin

git push -u origin master 推送master分支
git push -u origin --all 推送所有分支
-u表示追踪分支,下次可无参使用git push

git clone url

ssh-keygen 三次空格,.ssh/id_rsa.pub下为key