HttpHeaders源码
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.http;
import java.io.Serializable;
import java.net.URI;
import java.nio.charset.Charset;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import java.util.Map.Entry;
import org.springframework.util.Assert;
import org.springframework.util.LinkedCaseInsensitiveMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
public class HttpHeaders implements MultiValueMap<String, String>, Serializable {
private static final long serialVersionUID = -8578554704772377436L;
public static final String ACCEPT = "Accept";
public static final String ACCEPT_CHARSET = "Accept-Charset";
public static final String ACCEPT_ENCODING = "Accept-Encoding";
public static final String ACCEPT_LANGUAGE = "Accept-Language";
public static final String ACCEPT_RANGES = "Accept-Ranges";
public static final String AGE = "Age";
public static final String ALLOW = "Allow";
public static final String AUTHORIZATION = "Authorization";
public static final String CACHE_CONTROL = "Cache-Control";
public static final String CONNECTION = "Connection";
public static final String CONTENT_ENCODING = "Content-Encoding";
public static final String CONTENT_DISPOSITION = "Content-Disposition";
public static final String CONTENT_LANGUAGE = "Content-Language";
public static final String CONTENT_LENGTH = "Content-Length";
public static final String CONTENT_LOCATION = "Content-Location";
public static final String CONTENT_RANGE = "Content-Range";
public static final String CONTENT_TYPE = "Content-Type";
public static final String COOKIE = "Cookie";
public static final String DATE = "Date";
public static final String ETAG = "ETag";
public static final String EXPECT = "Expect";
public static final String EXPIRES = "Expires";
public static final String FROM = "From";
public static final String HOST = "Host";
public static final String IF_MATCH = "If-Match";
public static final String IF_MODIFIED_SINCE = "If-Modified-Since";
public static final String IF_NONE_MATCH = "If-None-Match";
public static final String IF_RANGE = "If-Range";
public static final String IF_UNMODIFIED_SINCE = "If-Unmodified-Since";
public static final String LAST_MODIFIED = "Last-Modified";
public static final String LINK = "Link";
public static final String LOCATION = "Location";
public static final String MAX_FORWARDS = "Max-Forwards";
public static final String ORIGIN = "Origin";
public static final String PRAGMA = "Pragma";
public static final String PROXY_AUTHENTICATE = "Proxy-Authenticate";
public static final String PROXY_AUTHORIZATION = "Proxy-Authorization";
public static final String RANGE = "Range";
public static final String REFERER = "Referer";
public static final String RETRY_AFTER = "Retry-After";
public static final String SERVER = "Server";
public static final String SET_COOKIE = "Set-Cookie";
public static final String SET_COOKIE2 = "Set-Cookie2";
public static final String TE = "TE";
public static final String TRAILER = "Trailer";
public static final String TRANSFER_ENCODING = "Transfer-Encoding";
public static final String UPGRADE = "Upgrade";
public static final String USER_AGENT = "User-Agent";
public static final String VARY = "Vary";
public static final String VIA = "Via";
public static final String WARNING = "Warning";
public static final String WWW_AUTHENTICATE = "WWW-Authenticate";
private static final String[] DATE_FORMATS = new String[]{"EEE, dd MMM yyyy HH:mm:ss zzz", "EEE, dd-MMM-yy HH:mm:ss zzz", "EEE MMM dd HH:mm:ss yyyy"};
private static TimeZone GMT = TimeZone.getTimeZone("GMT");
private final Map<String, List<String>> headers;
public HttpHeaders() {
this(new LinkedCaseInsensitiveMap(8, Locale.ENGLISH), false);
}
private HttpHeaders(Map<String, List<String>> headers, boolean readOnly) {
Assert.notNull(headers, "'headers' must not be null");
if (readOnly) {
Map<String, List<String>> map = new LinkedCaseInsensitiveMap(headers.size(), Locale.ENGLISH);
Iterator var4 = headers.entrySet().iterator();
while(var4.hasNext()) {
Entry<String, List<String>> entry = (Entry)var4.next();
List<String> values = Collections.unmodifiableList((List)entry.getValue());
map.put(entry.getKey(), values);
}
this.headers = Collections.unmodifiableMap(map);
} else {
this.headers = headers;
}
}
public void setAccept(List<MediaType> acceptableMediaTypes) {
this.set("Accept", MediaType.toString(acceptableMediaTypes));
}
public List<MediaType> getAccept() {
String value = this.getFirst("Accept");
List<MediaType> result = value != null ? MediaType.parseMediaTypes(value) : Collections.emptyList();
if (result.size() == 1) {
List<String> acceptHeader = this.get("Accept");
if (acceptHeader.size() > 1) {
value = StringUtils.collectionToCommaDelimitedString(acceptHeader);
result = MediaType.parseMediaTypes(value);
}
}
return result;
}
public void setAcceptCharset(List<Charset> acceptableCharsets) {
StringBuilder builder = new StringBuilder();
Iterator iterator = acceptableCharsets.iterator();
while(iterator.hasNext()) {
Charset charset = (Charset)iterator.next();
builder.append(charset.name().toLowerCase(Locale.ENGLISH));
if (iterator.hasNext()) {
builder.append(", ");
}
}
this.set("Accept-Charset", builder.toString());
}
public List<Charset> getAcceptCharset() {
List<Charset> result = new ArrayList();
String value = this.getFirst("Accept-Charset");
if (value != null) {
String[] tokens = value.split(",\\s*");
String[] var4 = tokens;
int var5 = tokens.length;
for(int var6 = 0; var6 < var5; ++var6) {
String token = var4[var6];
int paramIdx = token.indexOf(59);
String charsetName;
if (paramIdx == -1) {
charsetName = token;
} else {
charsetName = token.substring(0, paramIdx);
}
if (!charsetName.equals("*")) {
result.add(Charset.forName(charsetName));
}
}
}
return result;
}
public void setAllow(Set<HttpMethod> allowedMethods) {
this.set("Allow", StringUtils.collectionToCommaDelimitedString(allowedMethods));
}
public Set<HttpMethod> getAllow() {
String value = this.getFirst("Allow");
if (StringUtils.isEmpty(value)) {
return EnumSet.noneOf(HttpMethod.class);
} else {
List<HttpMethod> allowedMethod = new ArrayList(5);
String[] tokens = value.split(",\\s*");
String[] var4 = tokens;
int var5 = tokens.length;
for(int var6 = 0; var6 < var5; ++var6) {
String token = var4[var6];
allowedMethod.add(HttpMethod.valueOf(token));
}
return EnumSet.copyOf(allowedMethod);
}
}
public void setCacheControl(String cacheControl) {
this.set("Cache-Control", cacheControl);
}
public String getCacheControl() {
return this.getFirst("Cache-Control");
}
public void setConnection(String connection) {
this.set("Connection", connection);
}
public void setConnection(List<String> connection) {
this.set("Connection", this.toCommaDelimitedString(connection));
}
public List<String> getConnection() {
return this.getFirstValueAsList("Connection");
}
public void setContentDispositionFormData(String name, String filename) {
Assert.notNull(name, "'name' must not be null");
StringBuilder builder = new StringBuilder("form-data; name=\"");
builder.append(name).append('"');
if (filename != null) {
builder.append("; filename=\"");
builder.append(filename).append('"');
}
this.set("Content-Disposition", builder.toString());
}
public void setContentLength(long contentLength) {
this.set("Content-Length", Long.toString(contentLength));
}
public long getContentLength() {
String value = this.getFirst("Content-Length");
return value != null ? Long.parseLong(value) : -1L;
}
public void setContentType(MediaType mediaType) {
Assert.isTrue(!mediaType.isWildcardType(), "'Content-Type' cannot contain wildcard type '*'");
Assert.isTrue(!mediaType.isWildcardSubtype(), "'Content-Type' cannot contain wildcard subtype '*'");
this.set("Content-Type", mediaType.toString());
}
public MediaType getContentType() {
String value = this.getFirst("Content-Type");
return StringUtils.hasLength(value) ? MediaType.parseMediaType(value) : null;
}
public void setDate(long date) {
this.setDate("Date", date);
}
public long getDate() {
return this.getFirstDate("Date");
}
public void setETag(String eTag) {
if (eTag != null) {
Assert.isTrue(eTag.startsWith("\"") || eTag.startsWith("W/"), "Invalid eTag, does not start with W/ or \"");
Assert.isTrue(eTag.endsWith("\""), "Invalid eTag, does not end with \"");
}
this.set("ETag", eTag);
}
public String getETag() {
return this.getFirst("ETag");
}
public void setExpires(long expires) {
this.setDate("Expires", expires);
}
public long getExpires() {
try {
return this.getFirstDate("Expires");
} catch (IllegalArgumentException var2) {
return -1L;
}
}
public void setIfModifiedSince(long ifModifiedSince) {
this.setDate("If-Modified-Since", ifModifiedSince);
}
/** @deprecated */
@Deprecated
public long getIfNotModifiedSince() {
return this.getIfModifiedSince();
}
public long getIfModifiedSince() {
return this.getFirstDate("If-Modified-Since");
}
public void setIfNoneMatch(String ifNoneMatch) {
this.set("If-None-Match", ifNoneMatch);
}
public void setIfNoneMatch(List<String> ifNoneMatchList) {
this.set("If-None-Match", this.toCommaDelimitedString(ifNoneMatchList));
}
protected String toCommaDelimitedString(List<String> list) {
StringBuilder builder = new StringBuilder();
Iterator iterator = list.iterator();
while(iterator.hasNext()) {
String ifNoneMatch = (String)iterator.next();
builder.append(ifNoneMatch);
if (iterator.hasNext()) {
builder.append(", ");
}
}
return builder.toString();
}
public List<String> getIfNoneMatch() {
return this.getFirstValueAsList("If-None-Match");
}
protected List<String> getFirstValueAsList(String header) {
List<String> result = new ArrayList();
String value = this.getFirst(header);
if (value != null) {
String[] tokens = value.split(",\\s*");
String[] var5 = tokens;
int var6 = tokens.length;
for(int var7 = 0; var7 < var6; ++var7) {
String token = var5[var7];
result.add(token);
}
}
return result;
}
public void setLastModified(long lastModified) {
this.setDate("Last-Modified", lastModified);
}
public long getLastModified() {
return this.getFirstDate("Last-Modified");
}
public void setLocation(URI location) {
this.set("Location", location.toASCIIString());
}
public URI getLocation() {
String value = this.getFirst("Location");
return value != null ? URI.create(value) : null;
}
public void setOrigin(String origin) {
this.set("Origin", origin);
}
public String getOrigin() {
return this.getFirst("Origin");
}
public void setPragma(String pragma) {
this.set("Pragma", pragma);
}
public String getPragma() {
return this.getFirst("Pragma");
}
public void setUpgrade(String upgrade) {
this.set("Upgrade", upgrade);
}
public String getUpgrade() {
return this.getFirst("Upgrade");
}
public long getFirstDate(String headerName) {
String headerValue = this.getFirst(headerName);
if (headerValue == null) {
return -1L;
} else {
String[] var3 = DATE_FORMATS;
int var4 = var3.length;
int var5 = 0;
while(var5 < var4) {
String dateFormat = var3[var5];
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat, Locale.US);
simpleDateFormat.setTimeZone(GMT);
try {
return simpleDateFormat.parse(headerValue).getTime();
} catch (ParseException var9) {
++var5;
}
}
throw new IllegalArgumentException("Cannot parse date value \"" + headerValue + "\" for \"" + headerName + "\" header");
}
}
public void setDate(String headerName, long date) {
SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMATS[0], Locale.US);
dateFormat.setTimeZone(GMT);
this.set(headerName, dateFormat.format(new Date(date)));
}
public String getFirst(String headerName) {
List<String> headerValues = (List)this.headers.get(headerName);
return headerValues != null ? (String)headerValues.get(0) : null;
}
public void add(String headerName, String headerValue) {
List<String> headerValues = (List)this.headers.get(headerName);
if (headerValues == null) {
headerValues = new LinkedList();
this.headers.put(headerName, headerValues);
}
((List)headerValues).add(headerValue);
}
public void set(String headerName, String headerValue) {
List<String> headerValues = new LinkedList();
headerValues.add(headerValue);
this.headers.put(headerName, headerValues);
}
public void setAll(Map<String, String> values) {
Iterator var2 = values.entrySet().iterator();
while(var2.hasNext()) {
Entry<String, String> entry = (Entry)var2.next();
this.set((String)entry.getKey(), (String)entry.getValue());
}
}
public Map<String, String> toSingleValueMap() {
LinkedHashMap<String, String> singleValueMap = new LinkedHashMap(this.headers.size());
Iterator var2 = this.headers.entrySet().iterator();
while(var2.hasNext()) {
Entry<String, List<String>> entry = (Entry)var2.next();
singleValueMap.put(entry.getKey(), ((List)entry.getValue()).get(0));
}
return singleValueMap;
}
public int size() {
return this.headers.size();
}
public boolean isEmpty() {
return this.headers.isEmpty();
}
public boolean containsKey(Object key) {
return this.headers.containsKey(key);
}
public boolean containsValue(Object value) {
return this.headers.containsValue(value);
}
public List<String> get(Object key) {
return (List)this.headers.get(key);
}
public List<String> put(String key, List<String> value) {
return (List)this.headers.put(key, value);
}
public List<String> remove(Object key) {
return (List)this.headers.remove(key);
}
public void putAll(Map<? extends String, ? extends List<String>> map) {
this.headers.putAll(map);
}
public void clear() {
this.headers.clear();
}
public Set<String> keySet() {
return this.headers.keySet();
}
public Collection<List<String>> values() {
return this.headers.values();
}
public Set<Entry<String, List<String>>> entrySet() {
return this.headers.entrySet();
}
public boolean equals(Object other) {
if (this == other) {
return true;
} else if (!(other instanceof HttpHeaders)) {
return false;
} else {
HttpHeaders otherHeaders = (HttpHeaders)other;
return this.headers.equals(otherHeaders.headers);
}
}
public int hashCode() {
return this.headers.hashCode();
}
public String toString() {
return this.headers.toString();
}
public static HttpHeaders readOnlyHttpHeaders(HttpHeaders headers) {
return new HttpHeaders(headers, true);
}
}
JAVA反射中的getFields()方法和getDeclaredFields ()方法的区别
关于获取类的字段有两种方式:getFields()和getDeclaredFields()。我们先来看看这两者的区别吧:
getFields():获得某个类的所有的公共(public)的字段,包括父类中的字段。 getDeclaredFields():获得某个类的所有声明的字段,即包括public、private和proteced,但是不包括父类的申明字段。
同样类似的还有getConstructors()和getDeclaredConstructors()、getMethods()和getDeclaredMethods(),这两者分别表示获取某个类的方法、构造函数。
具体编码如下:
我们先创建一个POJO
public class User {
private long id;
private String name;
public void setId(long id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public long getId() {
return id;
}
public String getName() {
return name;
}
}
再来获取此类中的所有字段 Field[] fields = User.class.getDeclaredFields(); 获取字段的名称
String fieldName = field.getName();
获取字段的修饰符
int fieldValue = field.getModifiers();//如:private、static、final等
与某个具体的修饰符进行比较
Modifier.isStatic(fieldValue)//看此修饰符是否为静态(static)
获取字段的声明类型
field.getType();//返回的是一个class
与某个类型进行比较
field.getType() == Timestamp.class
获取指定对象中此字段的值
Object fieldObject= field.get(user);//user可以看做是从数据库中查找出来的对象
如果POJO类中有一个集合类型的字段,我们该如何获取字段中中的值呢?
首先创建一个POJO类
public class Bean {
private String[] love;
public String[] getLove() {
return love;
}
public void setLove(String[] love) {
this.love = love;
}
}
创建一个操作类
import java.lang.reflect.Method;
public class Admin {
public static void main(String[] args) {
try {
// 赋值
Object obj = Bean.class.newInstance();
Class paramClass = Class.forName("[Ljava.lang.String;");
String[] param = { "吃", "喝", "玩", "乐" };
Method method = Bean.class.getMethod("setLove", paramClass);
method.invoke(obj, (Object) param);
// 取值
Bean bean = (Bean) obj;
for (int i = 0; i < bean.getLove().length; i++) {
System.out.println(bean.getLove()[i]);
}
System.out.println();
} catch (Exception e) {
e.printStackTrace();
}
}
}
得到的结果为: 吃 喝 玩 乐
关于field还有一种情况我们需要注意,就是当字段修饰符为private时,我们需要加上:
field.setAccessible(true);
判断方法是:if (!field.isAccessible())
更详细的解释请点击
接下来补充一下在Java中使用正则表达式
先创建一个正则表达式对象
Pattern pattern = Pattern.compile(“[A-Z][a-z]* “);
指定为字符串的正则表达式必须首先被编译为此类的实例
Matcher matcher = pattern.matcher(fieldName);
尝试查找与该模式匹配的输入序列的下一个子序列
boolean isMatcher = matcher.find();
返回由以前匹配操作所匹配的输入子序列
String str = matcher.group();
实现非终端添加和替换步骤,以’_ ‘连接各个字段
matcher.appendReplacement(sb, str + “_ “);
Valid介绍及相关注解
主要用途
主要用于表单验证,减轻了代码量
相关依赖
implementation 'org.springframework.boot:spring-boot-starter-web'
在Springboot启动器的web包下包含了javax.validation.Valid
所以无需添加多余的依赖
Valid相关注解的使用方式
1.在相关的实体类的相关字段添加用于充当验证条件的注解
示例:
- 字段:
(ps:这里只是为了演示,大家要注意命名规范)
@NotNull(message = "用户名不能为空")
@Email(message = "邮箱不正确")
private String username;
- 实体类
@Data @AllArgsConstructor @NoArgsConstructor public class RegisterUser { @Min(value = 1000000) @NotNull(message = "ID不能为空") private Long userId; @NotNull(message = "用户名不能为空") @Email(message = "邮箱不正确") private String username; /** * 教师职称 */ private String position; /** * 教师所属教研室 */ private String office; }
2.在controller层的方法的要校验的参数上添加@Valid注解
@PostMapping("/action/register") public Result registerByForm(@Valid @RequestBody RegisterUser registerUser){ return userService.register(registerUser); }
3.编写全局异常捕捉类
@Slf4j @ControllerAdvice public class GlobalExceptionHandler { private final static String EXCEPTION_MSG_KEY = "Exception message : "; @ResponseBody @ExceptionHandler(MethodArgumentNotValidException.class) public Result handleValidException(MethodArgumentNotValidException e){ //日志记录错误信息 log.error(Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage()); //将错误信息返回给前台 return Result.error(103, Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage()); } }
相关代码类
- Result类
@Data @JsonInclude(JsonInclude.Include.NON_NULL) public class Result<T> { private Integer code; private String message; private T data; public Result() { this.code = 200; this.message = "操作成功"; } /** * 用于错误处理 * @param code 错误码 * @param message 错误提示信息 */ public Result(Integer code,String message){ this.code = code; this.message = message; } public static Result<String> error(Integer code,String msg){ return new Result<>(code,msg); } }
非法字段验证结果
{
"code":103,
"message":"邮箱不正确"
}
相关注解
@Null 限制只能为null
@NotNull 限制必须不为null
@AssertFalse 限制必须为false
@AssertTrue 限制必须为true
@DecimalMax(value) 限制必须为一个不大于指定值的数字
@DecimalMin(value) 限制必须为一个不小于指定值的数字
@Digits(integer,fraction) 限制必须为一个小数,且整数部分的位数不能超过integer,小数部分的位数不能超过fraction
@Future 限制必须是一个将来的日期
@Max(value) 限制必须为一个不大于指定值的数字
@Min(value) 限制必须为一个不小于指定值的数字
@Past 限制必须是一个过去的日期
@Pattern(value) 限制必须符合指定的正则表达式
@Size(max,min) 限制字符长度必须在min到max之间
@Past 验证注解的元素值(日期类型)比当前时间早
@NotEmpty 验证注解的元素值不为null且不为空(字符串长度不为0、集合大小不为0)
@NotBlank 验证注解的元素值不为空(不为null、去除首位空格后长度为0),不同于@NotEmpty,@NotBlank只应用于字符串且在比较时会去除字符串的空格
@Email 验证注解的元素值是Email,也可以通过正则表达式和flag指定自定义的email格式
需要注意每个注解对应的数据类型
Controller 数据处理示例(Model ModelAndView redirect)
直接返回所需要的数据
//删除用户地址
$(".delAddresslBtn").click(function () {
var addressId = $(this).attr("addressId");
$.ajax({
url: "/user/userAddress/delete/" + addressId,
success: function (result) {
if(result.status=="SUCCESS"){
$("tr[addressId="+addressId+"]").remove();
toastr.info("地址信息删除成功...");
} else {
toastr.error(result.message);
}
},
error: function () {
toastr.error("发生错误...");
}
})
});
@RequestMapping(value = "/userAddress/delete/{id}", method = RequestMethod.GET)
@ResponseBody
public String delUserAddress(@PathVariable Integer id) {
userAddressService.deleteById(id);
log.debug("收货地址删除成功...");
return "success";
}
返回数据通过JsonResult封装
//保存用户地址
$("#addAddressBtn").click(function () {
var addressId = $(this).attr("addressId");
$.ajax({
url: "/user/userAddress/add/",
method:"post",
data:{
"id":$("#id").val(),
"address":$("#address").val(),
"phone":$("#phone").val(),
"zipcode":$("#zipcode").val(),
"consignee":$("#consignee").val()
},
success: function (result) {
if(result.status=="SUCCESS"){
$('#addAddressSuccess').show();
toastr.info("添加成功");
} else {
toastr.error(result.message);
}
},
error: function () {
toastr.error("发生错误...");
}
})
});
@RequestMapping(value = "/userAddress/add", method = RequestMethod.POST)
@ResponseBody
public JsonResult doAddUserAddress(HttpSession session, UserAddress userAddress) {
userAddress.setUser(getUserFromSession(session));
userAddressService.save(userAddress);
log.debug("地址信息保存成功.");
JsonResult result = new JsonResult();
result.setToSuccess();
return result;
}
redirect 重定向到其他 controller 继续执行
@RequestMapping(value = "/reg", method = RequestMethod.POST)
public String doReg(Admin admin) {
adminService.save(admin);
return "redirect:/";
}
通过controller 实现页面跳转
<ul class="nav navbar-nav navbar-right" th:unless="${session.login_user != null}">
<li><a href="/user/login">登陆</a></li>
<li><a href="/user/reg">注册</a></li>
</ul>
@RequestMapping(value = "/login", method = RequestMethod.GET)
public String login() {
return "admin/adminLogin";
}
跳转到 adminLogin.html 页面
通过 redirect 后面携带参数,返回数据到前端
@RequestMapping(value = "/login", method = RequestMethod.POST)
public String doLogin(Admin admin, HttpSession session) {
if (adminService.checkLogin(admin)) {
AdminUtil.saveAdminToSession(session, adminService.findByUsernameAndPassword(admin.getUsername(), admin.getPassword()));
log.debug("管理员[{}]登陆成功", admin.getUsername());
return "redirect:/admin/product";
}
return "redirect:/admin/login?errorPwd=true";
}
<form action="#" th:action="@{/user/login}" method="post"
id="loginForm" class="form-signin">
<h2 class="form-signin-heading">系统登录</h2>
<div th:unless="${#httpServletRequest.getParameter('errorPwd')==null}">
<div id="errorPwd" class="alert alert-success">用户密码错误...</div>
<script type="text/javascript">
setTimeout(function () {
$('#errorPwd').hide('slow');
}, 3000);
</script>
</div>
通过 Model 传递数据到前端渲染
- html 触发请求 ```html
- controller 接收请求,并将数据传递到前端
1.通过model传递数据 userAddressList 到前端 userAddress.html 渲染
2."userAddressList" 对应 html 中的 ${userAddressList}
```java
@RequestMapping(value = "/userAddress", method = RequestMethod.GET)
public String userAddress(Model model, HttpSession session) {
model.addAttribute("title", "地址管理");
List<UserAddress> userAddressList = userAddressService.findByUserId(UserUtil.getUserFromSession(session).getId());
model.addAttribute("userAddressList", userAddressList);
return "user/userAddress";
}
- html前端渲染 ```html
### 通过 ModelAndView 传递数据到前端渲染
```html
<ul class="nav nav-tabs">
<!--<li id="sub-nav-admin"><a href="/user/profile">个人信息</a></li>-->
<li id="sub-nav-product"><a href="/admin/product">商品管理</a></li>
<li id="sub-nav-order"><a href="/admin/order">订单管理</a></li>
<li id="sub-nav-news"><a href="/admin/news">公告管理</a></li>
</ul>
@RequestMapping(method = RequestMethod.GET)
public ModelAndView index(ModelAndView model, HttpServletRequest request) {
Page<News> page = new Page<News>(request);
newsService.findNews(page);
model.addObject("page",page);
model.setViewName("admin/news/newsAdmin");
return model;
}
<tbody>
<tr th:each="news:${page.result}" th:pid="${news.id}">
<td th:text="${news.createTime}"></td>
<td th:text="${news.title}"></td>
<td><a class="btn btn-info btn-xs">查看</a> <a class="btn btn-info btn-xs">修改 <a
class="btn btn-info btn-xs">删除</a></a></td>
</tr>
</tbody>
Springboot 使用BindingResult校验参数
1、创建一个参数对象
import java.util.List;
import javax.validation.constraints.Min;
import javax.validation.constraints.Size;
import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotBlank;
import org.hibernate.validator.constraints.NotEmpty;
public class Parameter {
@NotEmpty(message="姓名不能为空")
private String name;
@Min(value = 18, message = "年龄必须大于18岁")
private int age;
@NotEmpty(message="hobbies不能为空")
private List<String> hobbies;
@NotBlank(message="账号不能为空")
private String account;
@Size(min=5,max=10,message="密码的长度应该在5和10之间")
private String password;
@Email(message="邮箱格式错误")
private String email;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public List<String> getHobbies() {
return hobbies;
}
public void setHobbies(List<String> hobbies) {
this.hobbies = hobbies;
}
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
2、controller控制层写参数接收的入口,需要注意的是@Valid 和 BindingResult 是一 一对应的,如果有多个@Valid,那么每个@Valid后面都需要添加BindingResult用于接收bean中的校验信息
@RequestMapping(value = "/test", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public @ResponseBody ResponseEntity<Pesponsibles> testBindingResult(@Valid @RequestBody Parameter parameter,BindingResult bindingResult)
{
log.info("test start");
Pesponsibles pesponsibles=new Pesponsibles();
if(bindingResult.hasErrors()){
List<FieldError> fieldErrors = bindingResult.getFieldErrors();
fieldErrors.forEach(fieldError -> {
//日志打印不符合校验的字段名和错误提示
log.error("error field is : {} ,message is : {}", fieldError.getField(), fieldError.getDefaultMessage());
});
for(int i=0;i<fieldErrors.size();i++){
//控制台打印不符合校验的字段名和错误提示
System.out.println("error field is :"+fieldErrors.get(i).getField()+",message is :"+fieldErrors.get(i).getDefaultMessage());
}
// pesponsibles.setError_msg(fieldErrors);
return new ResponseEntity<>(pesponsibles, HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<>(pesponsibles, HttpStatus.OK);
}
3、常用校验注解
@Null 只能是null
@NotNull 不能为null 注意用在基本类型上无效,基本类型有默认初始值
@AssertFalse 必须为false
@AssertTrue 必须是true
字符串/数组/集合检查:(字符串本身就是个数组)
@Pattern(regexp="reg") 验证字符串满足正则
@Size(max, min) 验证字符串、数组、集合长度范围
@NotEmpty 验证字符串不为空或者null
@NotBlank 验证字符串不为null或者trim()后不为空
数值检查:同时能验证一个字符串是否是满足限制的数字的字符串
@Max 规定值得上限int
@Min 规定值得下限
@DecimalMax("10.8") 以传入字符串构建一个BigDecimal,规定值要小于这个值
@DecimalMin 可以用来限制浮点数大小
@Digits(int1, int2) 限制一个小数,整数精度小于int1;小数部分精度小于int2
@Digits 无参数,验证字符串是否合法
@Range(min=long1,max=long2) 检查数字是否在范围之间
这些都包括边界值
日期检查:Date/Calendar
@Post 限定一个日期,日期必须是过去的日期
@Future 限定一个日期,日期必须是未来的日期
其他验证:
@Vaild 递归验证,用于对象、数组和集合,会对对象的元素、数组的元素进行一一校验
@Email 用于验证一个字符串是否是一个合法的右键地址,空字符串或null算验证通过
@URL(protocol=,host=,port=,regexp=,flags=) 用于校验一个字符串是否是合法UR
控制类中,return的几种形态
redirect重定向流程
客户发送一个请求到服务器,服务器匹配servlet,这都和请求转发一样,servlet处理完之后调用了sendRedirect()这个方法,这个方法是response的方法,所以,当这个servlet处理完之后,看到response.senRedirect()方法,立即向客户端返回这个响应,响应行告诉客户端你必须要再发送一个请求,去访问test.jsp,紧接着客户端受到这个请求后,立刻发出一个新的请求,去请求test.jsp,这里两个请求互不干扰,相互独立,在前面request里面setAttribute()的任何东西,在后面的request里面都获得不了。可见,在sendRedirect()里面是两个请求,两个响应。
重定向场景
Spring MVC中做form表单功能提交时,防止用户客户端后退或者刷新时重复提交问题,需要在服务端进行重定向跳转,其中redirect是直接跳转到其他页面,有以下3种方法进行重定向。
1. response.sendRedirect重定向跳转
@RequestMapping(value="/testredirect",method = { RequestMethod.POST, RequestMethod.GET })
public ModelAndView testredirect(HttpServletResponse response){
response.sendRedirect("/index");
return null;
}
2. ViewResolver直接跳转
不带参数
@RequestMapping(value="/testredirect",method = { RequestMethod.POST, RequestMethod.GET })
public String testredirect(HttpServletResponse response){
return "redirect:/index";
}
带参数
@RequestMapping("/testredirect")
public String testredirect(Model model, RedirectAttributes attr) {
attr.addAttribute("test", "51gjie");//跳转地址带上test参数
attr.addFlashAttribute("u2", "51gjie");//跳转地址不带上u2参数
return "redirect:/user/users";
}
3. ModelAndView重定向
不带参数
@RequestMapping(value="/restredirect",method = { RequestMethod.POST, RequestMethod.GET })
public ModelAndView restredirect(String userName){
ModelAndView model = new ModelAndView("redirect:/main/index");
return model;
}
带参数
@RequestMapping(value="/toredirect",method = { RequestMethod.POST, RequestMethod.GET })
public ModelAndView toredirect(String userName){
ModelAndView model = new ModelAndView("redirect:/main/index");
model.addObject("userName", userName); //把userName参数带入到controller的RedirectAttributes
return model;
}
其他return 形态
带参数,跳转到admin/news/newsDetail页面
@RequestMapping(value = "/news/{id}")
public ModelAndView newsView(@PathVariable("id") Integer id,ModelAndView model) {
News news = newsService.findById(id);
model.addObject("news", news);
model.setViewName("admin/news/newsDetail");
return model;
}
不带参数,跳转到 admin/adminReg 页面
@RequestMapping(value = "/reg", method = RequestMethod.GET)
public String reg() {
return "admin/adminReg";
}
返回指定数据到前端,JsonResult
@RequestMapping(value = "/news/delete/{id}")
@ResponseBody
public JsonResult newsDelete(@PathVariable("id") Integer id) {
newsService.delNews(id);
JsonResult result = new JsonResult();
result.setToSuccess();
return result;
}
———2020/03/01 更新 ModelAndView 和 Map 及 Model
ModelAndView
概述
控制器处理方法的返回值如果为 ModelAndView, 则其既包含视图信息,也包含模型数据信息。
添加模型数据:
- MoelAndView addObject(String attributeName, Object attributeValue)
- ModelAndView addAllObject(Map< String, ?> modelMap )
####设置视图:
- void setView(View view )
- void setViewName(String viewName)
示例
- register_form.jsp ```html <!DOCTYPE HTML>
- ModelController.java
```java
package com.ricky.codelab.webapp.ch3;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import com.ricky.codelab.webapp.ch2.model.User;
@Controller
@RequestMapping("model")
public class ModelController {
@RequestMapping("register")
public ModelAndView register(User user){
ModelAndView mv = new ModelAndView("home");
mv.addObject("user", user);
return mv;
}
}
- home.jsp ```html <%@ page language=”java” contentType=”text/html; charset=UTF-8” pageEncoding=”UTF-8”%> <!DOCTYPE html PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN” “http://www.w3.org/TR/html4/loose.dtd”>
Welcome ${requestScope.user.username }!
## Map 及 Model
### 概述
Spring MVC 在内部使用了一个org.springframework.ui.Model 接口存储模型数据。
Spring MVC 在调用方法前会创建一个–隐含的模型对象作为模型数据的存储容器。如果方法的入参为 Map 或 Model 类型,Spring MVC 会隐含模型的引用传递给这些入参。在方法体内,开发者可以通过这个入参对象访问到模型中的所有数据,也可以向模型中添加新的属性数据。
#### 示例
- 1、Map
```java
package com.ricky.codelab.webapp.ch3;
import java.util.Map;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import com.ricky.codelab.webapp.ch2.model.User;
@Controller
@RequestMapping("model")
public class ModelController {
@RequestMapping("register")
public String register(User user, Map<String, Object> map){
map.put("user", user);
return "home";
}
}
- 2、Model ```java package com.ricky.codelab.webapp.ch3;
import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping;
import com.ricky.codelab.webapp.ch2.model.User;
@Controller @RequestMapping(“model”) public class ModelController {
@RequestMapping("register")
public String register(User user, Model model){
model.addAttribute("user", user);
return "home";
} } ```
Controller和@RestController的区别?
知识点:@RestController
注解相当于@ResponseBody + @Controller
合在一起的作用。
1) 如果只是使用@RestController注解Controller,则Controller中的方法无法返回jsp页面,或者html,配置的视图解析器 InternalResourceViewResolver不起作用,返回的内容就是Return 里的内容。
2) 如果需要返回到指定页面,则需要用 @Controller配合视图解析器InternalResourceViewResolver才行。如果需要返回JSON,XML或自定义mediaType内容到页面,则需要在对应的方法上加上@ResponseBody注解。
例如:
1.使用@Controller 注解,在对应的方法上,视图解析器可以解析return 的jsp,html页面,并且跳转到相应页面
若返回json等内容到页面,则需要加@ResponseBody注解
@CrossOrigin
@Controller
public class FileUploadController {
//跳转到上传文件的页面
@RequestMapping(value="/gouploadimg", method = RequestMethod.GET)
public String goUploadImg() {
//跳转到 templates 目录下的 uploadimg.html
return "uploadimg";
}
//处理文件上传
@RequestMapping(value="/testuploadimg", method = RequestMethod.POST)
public @ResponseBody String uploadImg(@RequestParam("file") MultipartFile file,
HttpServletRequest request) {
System.out.println("调用文件上传方法");
String contentType = file.getContentType();
String fileName = file.getOriginalFilename();
。。。
}
}
2.@RestController注解,相当于@Controller+@ResponseBody两个注解的结合,返回json数据不需要在方法前面加@ResponseBody注解了,但使用@RestController这个注解,就不能返回jsp,html页面,视图解析器无法解析jsp,html页面
@CrossOrigin
@RestController /* @Controller + @ResponseBody*/
public class HospitalController {
//注入Service服务对象
@Autowired
private HospitalService hospitalService;
/**
* 查询所有医院信息(未分页)
*/
@RequestMapping(value = "findAllHospital",method = RequestMethod.GET)
public List<Hospital> findAllHospital(){
List<Hospital> hospitalList= hospitalService.findAllHospital();
return hospitalList;
}
建造模式
@Builder
###1. @Builder 实战使用
如果对建造者设计模式不太清楚的,可以先了解一下:建造这模式
@Builder注释为你的类生成相对略微复杂的构建器API。@Builder可以让你以下面显示的那样调用你的代码,来初始化你的实例对象:
Person.builder()
.name("Adam Savage")
.city("San Francisco")
.job("Mythbusters")
.job("Unchained Reaction")
.build();
@Builder可以放在类,构造函数或方法上。 虽然放在类上和放在构造函数上这两种模式是最常见的用例,但@Builder最容易用放在方法的用例来解释。
使用@Builder注释的方法可以帮助我们完成一下一些事情:
- 一个名为FooBuilder的内部静态类,并具有和实体类形同的属性(称为构建器)。
- 在构建器中:对于目标类中的所有的属性和未初始化的final字段,都会在构建器中创建对应属性;
- 在构建器中:创建一个无参的default构造函数。
- 在构建器中:对于实体类中的每个参数,都会对应创建类似于“setter”的方法,只不多方法名与该参数名相同。 并且返回值是构建器本身(便于链式调用),如上例所示。
- 在构建器中:一个build()方法,调用此方法,就会根据设置的值进行创建实体对象。
- 在构建器中:同时也会生成一个toString()方法。
- 在实体类中:会创建一个builder()方法,它的目的是用来创建构建器。
说的多了有点绕,我们还是先来使用一下试试,然后再详细说明:
@Builder
public class User {
private String username;
private String password;
}
// 编译后:
public class User {
private String username;
private String password;
User(String username, String password) {
this.username = username; this.password = password;
}
public static User.UserBuilder builder() {
return new User.UserBuilder();
}
public static class UserBuilder {
private String username;
private String password;
UserBuilder() {}
public User.UserBuilder username(String username) {
this.username = username;
return this;
}
public User.UserBuilder password(String password) {
this.password = password;
return this;
}
public User build() {
return new User(this.username, this.password);
}
public String toString() {
return "User.UserBuilder(username=" + this.username + ", password=" + this.password + ")";
}
}
}
2. @Builder 中使用 @Singular 注释集合
2.1 @Singular 实战使用
@Builder也可以为集合类型的参数或字段生成一种特殊的方法。 它采用修改列表中一个元素而不是整个列表的方式,可以是增加一个元素,也可以是删除一个元素。 例如:
Person.builder()
.job("Mythbusters")
.job("Unchained Reaction")
.build();
这样就可以轻松地将List
在使用@Singular注释注释一个集合字段(使用@Builder注释类),lombok会将该构建器节点视为一个集合,并生成两个adder方法而不是setter方法。
- 一个向集合添加单个元素,
-
一个将另一个集合的所有元素添加到集合中。 将不生成仅设置集合(替换已添加的任何内容)的setter。 还生成了clear方法。 这些singular构建器相对而言是有些复杂的,主要是来保证以下特性:
- 在调用build()时,生成的集合将是不可变的。
- 在调用build()之后调用其中一个adder方法或clear方法不会修改任何已经生成的对象。如果对集合修改之后,再调用build(),则会创建一个基于上一个对象创建的对象实体。
-
生成的集合将被压缩到最小的可行格式,同时保持高效。 @Singular只能应用于lombok已知的集合类型。目前,支持的类型有:
- java.util:
- Iterable, Collection, 和List (一般情况下,由压缩的不可修改的ArrayList支持).
- Set, SortedSet, and NavigableSet (一般情况下,生成可变大小不可修改的HashSet或者TreeSet).
- Map, SortedMap, and NavigableMap (一般情况下,生成可变大小不可修改的HashMap或者TreeMap).
- Guava’s com.google.common.collect:
- ImmutableCollection and ImmutableList
- ImmutableSet and ImmutableSortedSet
- ImmutableMap, ImmutableBiMap, and ImmutableSortedMap
- ImmutableTable
再来看看使用了@Singular注解之后的编译情况:
```java
@Builder
public class User {
private final Integer id;
private final String zipCode = “215500”;
private String username;
private String password;
@Singular
private List
hobbies; }
// 编译后:
public class User {
private final Integer id;
private final String zipCode = “215500”;
private String username;
private String password;
private List
public static User.UserBuilder builder() {return new User.UserBuilder();}
public static class UserBuilder {
private Integer id;
private String username;
private String password;
private ArrayList<String> hobbies;
UserBuilder() {}
public User.UserBuilder id(Integer id) { this.id = id; return this; }
public User.UserBuilder username(String username) { this.username = username; return this; }
public User.UserBuilder password(String password) { this.password = password; return this; }
public User.UserBuilder hobby(String hobby) {
if (this.hobbies == null) {
this.hobbies = new ArrayList();
}
this.hobbies.add(hobby);
return this;
}
public User.UserBuilder hobbies(Collection<? extends String> hobbies) {
if (this.hobbies == null) {
this.hobbies = new ArrayList();
}
this.hobbies.addAll(hobbies);
return this;
}
public User.UserBuilder clearHobbies() {
if (this.hobbies != null) {
this.hobbies.clear();
}
return this;
}
public User build() {
List hobbies;
switch(this.hobbies == null ? 0 : this.hobbies.size()) {
case 0:
hobbies = Collections.emptyList();
break;
case 1:
hobbies = Collections.singletonList(this.hobbies.get(0));
break;
default:
hobbies = Collections.unmodifiableList(new ArrayList(this.hobbies));
}
return new User(this.id, this.username, this.password, hobbies);
}
public String toString() {
return "User.UserBuilder(id=" + this.id + ", username=" + this.username + ", password=" + this.password + ", hobbies=" + this.hobbies + ")";
}
} } ```
其实,lombok的创作者还是很用心的,在进行build()来床建实例对象时,并没有直接使用Collections.unmodifiableList(Collection)此方法来床架实例,而是分为三种情况,
- 第一种,当集合中没有元素时,创建一个空list;
- 第二种情况,当集合中存在一个元素时,创建一个不可变的单元素list;
-
第三种情况,根据当前集合的元素数量创建对应合适大小的list; 当然我们看编译生成的代码,创建了三个关于集合操作的方法:
- hobby(String hobby):向集合中添加一个元素
- hobbies(Collection<? extends String> hobbies):添加一个集合所有的元素
- clearHobbies():清空当前集合数据 2.2 @Singular 注解配置详解
我们先来看看 @Singular 注解的详情:
@Target({FIELD, PARAMETER})
@Retention(SOURCE)
public @interface Singular {
// 修改添加集合元素的方法名
String value() default "";
}
测试如何使用注解属性vlaue
@Builder
public class User {
private final Integer id;
private final String zipCode = "215500";
private String username;
private String password;
@Singular(value = "testHobbies")
private List<String> hobbies;
}
// 测试类
public class BuilderTest {
public static void main(String[] args) {
User user = User.builder()
.testHobbies("reading")
.testHobbies("chatting")
.id(1)
.password("jdkong")
.username("jdkong")
.build();
System.out.println(user);
}
}
说明,当我们使用了注解属性value之后,我们在使用添加集合元素时的方法名发生相应的改变。但是,同时生成的添加整个集合的方法名发生改变了吗?我们再来看看编译后的代码:
// 编译后:
public class User {
// 省略部分代码,只看关键部分
public static class UserBuilder {
public User.UserBuilder testHobbies(String testHobbies) {
if (this.hobbies == null) {
this.hobbies = new ArrayList();
}
this.hobbies.add(testHobbies);
return this;
}
public User.UserBuilder hobbies(Collection<? extends String> hobbies) {
if (this.hobbies == null) {
this.hobbies = new ArrayList();
}
this.hobbies.addAll(hobbies);
return this;
}
public User.UserBuilder clearHobbies() {
if (this.hobbies != null) {
this.hobbies.clear();
}
return this;
}
}
}
可以看到,只有添加一个元素的方法名发生了改变。
3. @Builder.Default 使用
比如有这样一个实体类:
@Builder
@ToString
public class User {
@Builder.Default
private final String id = UUID.randomUUID().toString();
private String username;
private String password;
@Builder.Default
private long insertTime = System.currentTimeMillis();
}
在类中我在id和insertTime上都添加注解@Builder.Default,当我在使用这个实体对象时,我就不需要在为这两个字段进行初始化值,如下面这样:
public class BuilderTest {
public static void main(String[] args) {
User user = User.builder()
.password("jdkong")
.username("jdkong")
.build();
System.out.println(user);
}
}
// 输出内容:
User(id=416219e1-bc64-43fd-b2c3-9f8dc109c2e8, username=jdkong, password=jdkong, insertTime=1546869309868)
lombok在实例化对象时就为我们初始化了这两个字段值。
当然,你如果再对这两个字段进行设值的话,那么默认定义的值将会被覆盖掉,如下面这样:
public class BuilderTest {
public static void main(String[] args) {
User user = User.builder()
.id("jdkong")
.password("jdkong")
.username("jdkong")
.build();
System.out.println(user);
}
}
// 输出内容
User(id=jdkong, username=jdkong, password=jdkong, insertTime=1546869642151)
4. @Builder 详细配置
下面我们再来详细看看@Builder这个注解类地详细实现:
@Target({TYPE, METHOD, CONSTRUCTOR})
@Retention(SOURCE)
public @interface Builder {
// 如果@Builder注解在类上,可以使用 @Builder.Default指定初始化表达式
@Target(FIELD)
@Retention(SOURCE)
public @interface Default {}
// 指定实体类中创建 Builder 的方法的名称,默认为: builder (个人觉得没必要修改)
String builderMethodName() default "builder";
// 指定 Builder 中用来构件实体类的方法的名称,默认为:build (个人觉得没必要修改)
String buildMethodName() default "build";
// 指定创建的建造者类的名称,默认为:实体类名+Builder
String builderClassName() default "";
// 使用toBuilder可以实现以一个实例为基础继续创建一个对象。(也就是重用原来对象的值)
boolean toBuilder() default false;
@Target({FIELD, PARAMETER})
@Retention(SOURCE)
public @interface ObtainVia {
// 告诉lombok使用表达式获取值
String field() default "";
// 告诉lombok使用表达式获取值
String method() default "";
boolean isStatic() default false;
}
}
以上注解属性,我只测试一个比较常用的toBuilder,因为我们在对实体对象进行操作时,往往会存在对某些实体对象的某个字段进行二次赋值,这个时候就会用到这一属性。但是,这会创建一个新的对象,而不是原来的对象,原来的对象属性是不可变的,除非你自己想要给这个实体类再添加上@Data或者@setter方法(我是这么干过,哈哈)。下面就来测试一下:
@Builder(toBuilder = true)
@ToString
public class User {
private String username;
private String password;
}
// 测试类
public class BuilderTest {
public static void main(String[] args) {
User user1 = User.builder()
.password("jdkong")
.username("jdkong")
.build();
System.out.println(user1);
User user2 = user1.toBuilder().username("jdkong2").build();
// 验证user2是否是基于user1的现有属性创建的
System.out.println(user2);
// 验证对象是否是同一对象
System.out.println(user1 == user2);
}
}
// 输出内容
User(username=jdkong, password=jdkong)
User(username=jdkong2, password=jdkong)
false
- @Builder 全局配置
# 是否禁止使用@Builder lombok.builder.flagUsage = [warning | error] (default: not set) #是否使用Guaua lombok.singular.useGuava = [true | false] (default: false) # 是否自动使用singular,默认是使用 lombok.singular.auto = [true | false] (default: true)
339 post articles, 43 pages.