springmvc功能的自动配置类,WebMvcAutoConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
全面接管springmvc
***webmvc配置(重点)要么重写方法;要么@Bean,方法里要么set,要么直接返回类;要么返回某个接口,接口实现方法
***定制化原理- @Configuration + @Bean
- WebMvcConfigure
- @EnableWebMvc + WebMvcConfigure 全面接管SpirngMVC
- xxxCustomizer
@EnableWebMvc导入了 DelegatingWebMvcConfiguration 的作用:把所有的WebMvcConfigure拿过来,所有的功能定制都来自这个WebMvcConfigure 全部合起来
@FunctionalInterface public interface WebServerFactoryCustomizer{ void customize(T factory); }
@Configuration
public class Beans {
@Bean
public WebServerFactoryCustomizer webServerFactoryCustomizer(){
//这个factory就相当于Servlet容器
return new WebServerFactoryCustomizer() {
@Override
public void customize(ConfigurableWebServerFactory factory) {
factory.setPort(8081);
}
};
}
// 或者
@Bean
public WebServerFactoryCustomizer webServerFactoryCustomizer(){
return factory -> factory.setPort(8081);
}
}
config包下
LoginInterceptor
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession();
Integer uid = (Integer) session.getAttribute("uid");
if (uid == null) {
response.sendRedirect("/eBusiness/user/toLogin");
return false;
}
return true;
}
}
需要自己扩展mvc ,则该配置类不能加上@EnableWebMvc
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
// 拦截器
public void addInterceptors(InterceptorRegistry registry) {
LoginInterceptor loginInterceptor = new LoginInterceptor();
// 将自己写的拦截器注入进来
InterceptorRegistration interceptorRegistration = registry.addInterceptor(loginInterceptor);
// 添加 白名单
List list = new ArrayList<>();
list.add("/user/reg");
list.add("/user/doReg");
list.add("/user/toLogin");
list.add("/user/doLogin");
list.add("/user/checkEmail");
list.add("/user/sendEmail");
list.add("/getCode");
list.add("/");
list.add("/goods/goodsDetail");
list.add("/css
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
}
@Override
public void addArgumentResolvers(List
resolvers) {
}
@Override
public void
addReturnValueHandlers(List handlers) {
}
@Override
public void configureMessageConverters(List>
converters) {
}
}
Rest风格
- GET 获取
- POST 保存,创建
- PUT 整体更新
- DELETe 删除
- PATCH 部分更新
yml
spirng:
mvc:
hiddenmethod:
filter:
enabled: true
@RestController
public class TestController2 {
@RequestMapping(value = "/usr", method = RequestMethod.GET)
public String getUsr() {
return "GET";
}
@RequestMapping(value = "/usr", method = RequestMethod.POST)
public String postUsr() {
return "POST";
}
// @PutMapping
@RequestMapping(value = "/usr", method = RequestMethod.PUT)
public String putUsr() {
return "PUT";
}
// @DeleteMapping
@RequestMapping(value = "/usr", method = RequestMethod.DELETE)
public String deleteUsr() {
return "DELETE";
}
//@PatchMapping
}
postman可以直接发送 put 和 delete请求
MyWebMvcConig
@Configuration
public class MyWebMvcConfig implements WebMvcConfigurer {
// 改变_method的名字
@Bean
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
OrderedHiddenHttpMethodFilter orderedHiddenHttpMethodFilter = new OrderedHiddenHttpMethodFilter();
orderedHiddenHttpMethodFilter.setMethodParam("_m");
return orderedHiddenHttpMethodFilter;
}
}
参数注解
- @PathVariable
- @RequestBody
- @RequestParam
- @RequestHeader
- @cookievalue
- @RequestAttribute
- @MatrixVariable 矩阵变量
- @RequestMapping
@RestController
// @RestController = @ResponseBody + @Controller,某个类使用了此注解,那么这个类下按带所有方法的返回值都会转成JSON串
public class TestController2 {
@RequestMapping("/car/{id}/{name}")
public Map car(@PathVariable("id") Integer id,
@PathVariable("name") String name,
@PathVariable Map pv,
@RequestHeader("User-Agent") String userAgent,
@RequestHeader Map header,
@cookievalue("_ga") String _ga,
@cookievalue("_ga") cookie cookie) {
Map map = new HashMap<>();
map.put("id", id);
map.put("name", name);
map.put("pv", pv);
map.put("userAgent", userAgent);
map.put("header", header);
return map;
}
}
// @PathVariable("xx") xx要和 请求参数相对应,后面的是别名
@RequestAttribute
方式一
@Controller
public class TestController3 {
@GetMapping("/goto")
public String goToPage(HttpServletRequest request) {
request.setAttribute("msg", 200);
return "forward:/success";// 转发
}
@GetMapping("/success")
@ResponseBody
public String success(@RequestAttribute("msg") String msg) {
return msg;
}
}
// @RequestAttribute 后面的是别名
方式二
@Controller
public class TestController3 {
@GetMapping("/goto")
public String goToPage(HttpServletRequest request) {
request.setAttribute("msg", "200");
return "forward:/success";
}
@GetMapping("/success")
@ResponseBody
public String success(HttpServletRequest request) {
String msg = (String) request.getAttribute("msg");
return msg;
}
}
@RequestMapping注解的params属性通过请求的请求参数匹配请求映射
@RequestMapping注解的params属性是一个字符串类型的数组,可以通过四种表达式设置请求参数和请求映射的匹配关系
“param”:要求请求映射所匹配的请求必须携带param请求参数
“!param”:要求请求映射所匹配的请求必须不能携带param请求参数
“param=value”:要求请求映射所匹配的请求必须携带param请求参数且param=value
“param!=value”:要求请求映射所匹配的请求必须携带param请求参数但是param!=value
测试@RequestMapping的params属性-->/test
@RequestMapping(
value = {"/testRequestMapping", "/test"}
,method = {RequestMethod.GET, RequestMethod.POST}
,params = {"username","password!=123456"}
)
public String testRequestMapping(){
return "success";
}
注:
若当前请求满足@RequestMapping注解的value和method属性,但是不满足params属性,此时页面回报错400:Parameter conditions “username, password!=123456” not met for actual request parameters: username={admin}, password={123456}
@RequestMapping注解的headers属性
@RequestMapping注解的headers属性通过请求的请求头信息匹配请求映射
@RequestMapping注解的headers属性是一个字符串类型的数组,可以通过四种表达式设置请求头信息和请求映射的匹配关系
“header”:要求请求映射所匹配的请求必须携带header请求头信息
“!header”:要求请求映射所匹配的请求必须不能携带header请求头信息
“header=value”:要求请求映射所匹配的请求必须携带header请求头信息且header=value
“header!=value”:要求请求映射所匹配的请求必须携带header请求头信息且header!=value
若当前请求满足@RequestMapping注解的value和method属性,但是不满足headers属性,此时页面显示404错误,即资源未找到
3、@RequestParam
@RequestParam是将请求参数和控制器方法的形参创建映射关系
@RequestParam注解一共有三个属性:
value:指定为形参赋值的请求参数的参数名
required:设置是否必须传输此请求参数,默认值为true
若设置为true时,则当前请求必须传输value所指定的请求参数,若没有传输该请求参数,且没有设置defaultValue属性,则页面报错400:Required String parameter ‘xxx’ is not present;若设置为false,则当前请求不是必须传输value所指定的请求参数,若没有传输,则注解所标识的形参的值为null
defaultValue:不管required属性值为true或false,当value所指定的请求参数没有传输或传输的值为""(空字符串)时,则使用默认值为形参赋值
4、@RequestHeader
@RequestHeader是将请求头信息和控制器方法的形参创建映射关系
@RequestHeader注解一共有三个属性:value、required、defaultValue,用法同@RequestParam
5、@cookievalue
@cookievalue是将cookie数据和控制器方法的形参创建映射关系
@cookievalue注解一共有三个属性:value、required、defaultValue,用法同@RequestParam
RequestEntity
RequestEntity封装请求报文的一种类型,需要在控制器方法的形参中设置该类型的形参,当前请求的请求报文就会赋值给该形参,可以通过getHeaders()获取请求头信息,通过getBody()获取请求体信息
@RequestMapping("/testRequestEntity")
public String testRequestEntity(RequestEntity requestEntity){ System.out.println("requestHeader:"+requestEntity.getHeaders()); System.out.println("requestBody:"+requestEntity.getBody());
return "success";
}
输出结果:
requestHeader:[host:“localhost:8080”, connection:“keep-alive”, content-length:“27”, cache-control:“max-age=0”, sec-ch-ua:"" Not A;Brand";v=“99”, “Chromium”;v=“90”, “Google Chrome”;v=“90"”, sec-ch-ua-mobile:"?0", upgrade-insecure-requests:“1”, origin:“http://localhost:8080”, user-agent:“Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36”]
requestBody:username=admin&password=123
@RequestMapping("/test1")
@ResponseBody
public String selectGenerator1(Model model, RequestEntity r) {
HttpMethod method = r.getMethod();
System.out.println("method = " + method);
Type type = r.getType();
System.out.println("type = " + type);
URI url = r.getUrl();
System.out.println("url = " + url);
HttpHeaders headers = r.getHeaders();
System.out.println("headers = " + headers);
Object body = r.getBody();
System.out.println("body = " + body);
return "a";
}
@RequestBody
GET没有请求体,所以用@RequestBody接收数据时,前端不能用GET请求方式提交数据,用POST提交
一个方法里面。@RequestParam可以有多个,@RequsetBody只能有一个
将json对象变为java对象
数据响应与内容协商数据响应
-
响应页面
跳转到某个页面 -
响应数据
JSON,XML,xls,图片、音乐视频、自定义协商数据
使用
- @ResponseBody
- @ResController
postman中设置Accept为 application/json,则返回json
application/xml,则返回xml
- 调用RequestResponseBodyMethodProcessor处理
- Processor处理方法返回值,通过MessageConverter处理
- 所有MessageConverter和起来可支持各种媒体类型数据的操作
- 内容协商找到最终的messageConverter
springboot雷神36-42
自定义MessageConverter jacksondata-bind fastjson HttpClient JSR303 Swagger @MapperScan主程序类扫包,要扫mapper包
@MapperScan("com.demo.example.mapper")
绑定数据
四种方式
// 使用不同的对象绑定数据
@RequestMapping("/t7")
public String t7(HttpServletRequest request,
HttpSession session,
Model model) {
request.setAttribute("name", "猴子");
session.setAttribute("address", "森林");
model.addAttribute("age", 10);
ServletContext servletContext = request.getServletContext();
servletContext.setAttribute("context", "666");
return "pages/test04";
}
HttpServletRequest: HttpSession: Model: ServletContext:提交数据到服务端 表单 链接
删除数据
绝对路径![]()
绝对路径异常
@ControllerAdvice
@ExceptionHandler
assignableTypes指定某个controller类
@ControllerAdvice(assignableTypes = {ExceptionController.class})
处理方式
templates 创建一个error目录
1、异常页面
出现异常 跳转到此页面
5XX.html , 4XX.html
2、使用异常类、异常方法处理异常(从根本上解决问题)
优雅的处理异常 验证 QQ邮箱验证码注册qq邮箱,设置-》账户-》开启POP3服务-》获取STMP码
javax.mail mail1.4.7
加工具类 Email
package com.ldk.ebusiness.utils;
import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.util.Properties;
import java.util.Random;
public class Email {
private static final String EMAIL = "1046606977@qq.com"; // QQ邮箱
private static final String STMP = "cplsybbczwpkbbfj"; // STMP验证码
private static final String USERNAME = "1046606977"; // QQ 账号
// toEmail 是收件人的 email
public static String sendEmail(String toEmail) {
String result = null;
try {
// 创建Properties 类用于记录邮箱的一些属性
Properties props = new Properties();
// 表示SMTP发送邮件,必须进行身份验证
props.put("mail.smtp.auth", "true");
//此处填写SMTP服务器
props.put("mail.smtp.host", "smtp.qq.com");
//端口号,QQ邮箱端口587
props.put("mail.smtp.port", "587");
// 此处填写,写信人的账号
props.put("mail.user", Email.EMAIL);
// 构建授权信息,用于进行SMTP进行身份验证
Authenticator authenticator = new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
// 用户名、密码
return new PasswordAuthentication(Email.USERNAME, Email.STMP);
}
};
// 使用环境属性和授权信息,创建邮件会话
Session mailSession = Session.getInstance(props, authenticator);
// 创建邮件消息
MimeMessage message = new MimeMessage(mailSession);
// 设置发件人
InternetAddress form = new InternetAddress(props.getProperty("mail.user"));
message.setFrom(form);
// 设置收件人的邮箱
InternetAddress to = new InternetAddress(toEmail);
message.setRecipient(MimeMessage.RecipientType.TO, to);
// 设置邮件标题
message.setSubject("系统邮箱验证");
StringBuffer stringBuffer = new StringBuffer();
Random random = new Random();
for (int i = 0; i < 6; i++) {
int nextInt = random.nextInt(9);
stringBuffer.append(nextInt);
}
// 设置邮件的内容体
message.setContent("验证码:" + stringBuffer.toString(), "text/html;charset=UTF-8");
// 最后当然就是发送邮件啦
Transport.send(message);
result = stringBuffer.toString();
} catch (MessagingException e) {
e.printStackTrace();
} finally {
return result;
}
}
}
String result = Email.sendEmail(email);
session.setAttribute("code", result);
验证码登录
code
package com.ldk.ebusiness.utils;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Random;
// 封装生成验证码的工具类
public class Code {
private static char code[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
'k', 'm', 'n', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y',
'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M',
'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '2',
'3', '4', '5', '6', '7', '8', '9' };
private static final int WIDTH = 50;
private static final int HEIGHT = 20;
private static final int LENGTH = 4;
public static void getCode(HttpServletRequest request, HttpServletResponse response) {
// TODO Auto-generated method stub
// 设置响应报头信息
response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
// 设置响应的MIME类型
response.setContentType("image/jpeg");
BufferedImage image = new BufferedImage(WIDTH, HEIGHT,
BufferedImage.TYPE_INT_RGB);
Font mFont = new Font("Arial", Font.TRUETYPE_FONT, 18);
Graphics g = image.getGraphics();
Random rd = new Random();
// 设置背景颜色
g.setColor(new Color(rd.nextInt(55) + 200, rd.nextInt(55) + 200, rd
.nextInt(55) + 200));
g.fillRect(0, 0, WIDTH, HEIGHT);
// 设置字体
g.setFont(mFont);
// 画边框
g.setColor(Color.black);
g.drawRect(0, 0, WIDTH - 1, HEIGHT - 1);
// 随机产生的验证码
String result = "";
for (int i = 0; i < LENGTH; ++i) {
result += code[rd.nextInt(code.length)];
}
HttpSession se = request.getSession();
se.setAttribute("code", result);
// 画验证码
for (int i = 0; i < result.length(); i++) {
g.setColor(new Color(rd.nextInt(200), rd.nextInt(200), rd
.nextInt(200)));
g.drawString(result.charAt(i) + "", 12 * i + 1, 16);
}
// 随机产生2个干扰线
for (int i = 0; i < 2; i++) {
g.setColor(new Color(rd.nextInt(200), rd.nextInt(200), rd
.nextInt(200)));
int x1 = rd.nextInt(WIDTH);
int x2 = rd.nextInt(WIDTH);
int y1 = rd.nextInt(HEIGHT);
int y2 = rd.nextInt(HEIGHT);
g.drawLine(x1, y1, x2, y2);
}
// 释放图形资源
g.dispose();
try {
OutputStream os = response.getOutputStream();
// 输出图像到页面
ImageIO.write(image, "JPEG", os);
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 封装验证码
@Controller
public class CodeController {
@RequestMapping("/getCode")
public void validateCode(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
Code.getCode(request, response); // io流输出
}
}
方式一
看不清换一张
或者
![]()
看不清换一张
// 刷新验证码
function refreshCode() {
var time = new Date();
time = time.getTime();
// 要加上根访问路径
$("#mycode").attr("src", "/eBusiness/getCode?time=" + time);
}
方式二
拦截器![]()
- 拦截器 是 Servlet的
- 过滤器 是 Tomcat的
拦截器配置好后,地址栏不能直接访问,点击跳转的话 要自己写 跳转
configInterceptorConfiguration
/eBusiness 不用写
/static 不用写
// 收藏function focus(goodsId) { $.post("/eBusiness/goods/focus", {"goodsId": goodsId}, function(data){ if (data.state == 200) { alert(data.msg); } else if(data.state == null) { // 拦截器拦截,回调函数,让用户登录 location.href = "/eBusiness/user/toLogin"; } });};
上传/下载
yml
spring: servlet: multipart: max-file-size: 10MB #单次上传文件最大不超过10MB max-request-size: 100MB #文件总上传大小不超过100MB上传文件
file.getOriginalFilename(); // 文件名 file.getContentType(); // 文件类型 file.getSize(); // 文件大小
和ssm一样
@RequestMapping("/toUpload")
public String toUpload(MultipartFile fileData, HttpServletRequest request) throws IOException {
if (fileData == null) {
request.setAttribute(“msg”, “请选择文件”);
return “upload”;
}
String fileName = fileData.getOriginalFilename();
System.out.println("fileName = " + fileName);
int index = fileName.lastIndexOf(".");
String suffix = fileName.substring(index);
long prefix = System.currentTimeMillis();
// String uuid = UUID.randomUUID().toString();
String newFileName = prefix + suffix;
// 上传商品图片
public int addProduct(Product product, MultipartFile file, HttpSession session) {
if (file == null) {
// "请选择上传文件";
}
// 文件大小***************************
if (file.getSize() > 1024 * 1024 * 50) {
// "上传的文件大于50MB"
}
JsonResult jsonResult = new JsonResult();
String fileName = file.getOriginalFilename();
int index = fileName.lastIndexOf(".");
String suffix = fileName.substring(index);
long prefix = System.currentTimeMillis();
String newFileName = prefix + suffix;
String path = session.getServletContext().getRealPath("images/product");
String path2 = "E:\Download\idea\idea_project\ssm\git\ssm_store\src\main\webapp\images\product";
File file1 = new File(path);
if (!file1.exists()) {
file1.mkdir();
}
File file00 = new File(path2);
if (file00.exists()) {
file00.mkdir();
}
String filePath = path + File.separator + newFileName;
System.out.println("filePath = " + filePath);
File file2 = new File(filePath);
File file3 = new File(path2 + File.separator + newFileName);
try {
file.transferTo(file2);
FileUtils.copyFile(file2, file3);
System.out.println(product.getProName());
product.setProPic(newFileName);
int row = productMapper.addProduct(product);
return row;
} catch (IOException e) {
e.printStackTrace();
return 0;
}
}
下载文件
和ssm一样
@RequestMapping(value="/download")
public String downloads(HttpServletResponse response , String filename, HttpServletRequest request) throws Exception{
//要下载的图片地址
String path = request.getSession().getServletContext().getRealPath("/upload");
//1、设置response 响应头
response.reset(); //设置页面不缓存,清空buffer
response.setCharacterEncoding("UTF-8"); //字符编码
response.setContentType("multipart/form-data"); //二进制传输数据
//设置响应头
response.setHeader("Content-Disposition",
"attachment;fileName="+ URLEncoder.encode(filename, "UTF-8").replaceAll("\+", "%20"));
File file = new File(path,filename);
//2、 读取文件--输入流
InputStream input=new FileInputStream(file);
//3、 写出文件--输出流
OutputStream out = response.getOutputStream();
byte[] buff =new byte[1024];
int index=0;
//4、执行 写出操作
while((index= input.read(buff))!= -1){
out.write(buff, 0, index);
out.flush();
}
out.close();
input.close();
return null;
}
使用工具类上传和下载
FileUploadUtils
public class FileUploadUtils {
public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024;
public static final int FILE_NAME_MAX = 100;
private static String DEFAULT_base_FILE = "E:\upload";
public static final String upload(MultipartFile file) throws IOException {
try {
return upload(FileUploadUtils.DEFAULT_base_FILE, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
} catch (Exception e) {
throw new IOException(e.getMessage(), e);
}
}
public static final String upload(String baseDir, MultipartFile file) throws IOException {
try {
return upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
} catch (Exception e) {
throw new IOException(e.getMessage(), e);
}
}
public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension)
throws Exception {
//合法性校验
assertAllowed(file, allowedExtension);
String fileName = encodingFileName(file);
File desc = getAbsoluteFile(baseDir, fileName);
file.transferTo(desc);
return desc.getAbsolutePath();
}
private static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException {
File desc = new File(uploadDir + File.separator + fileName);
if (!desc.getParentFile().exists()) {
desc.getParentFile().mkdirs();
}
if (!desc.exists()) {
desc.createNewFile();
}
return desc;
}
private static String encodingFileName(MultipartFile file) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
String datePath = simpleDateFormat.format(new Date());
return datePath + "-" + UUID.randomUUID().toString() + "." + getExtension(file);
}
public static final void assertAllowed(MultipartFile file, String[] allowedExtension)
throws Exception {
if (file.getOriginalFilename() != null) {
int fileNamelength = file.getOriginalFilename().length();
if (fileNamelength > FILE_NAME_MAX) {
throw new Exception("文件名过长");
}
}
long size = file.getSize();
if (size > DEFAULT_MAX_SIZE) {
throw new Exception("文件过大");
}
String extension = getExtension(file);
if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)) {
throw new Exception("请上传指定类型的文件!");
}
}
public static final boolean isAllowedExtension(String extension, String[] allowedExtension) {
for (String str : allowedExtension) {
if (str.equalsIgnoreCase(extension)) {
return true;
}
}
return false;
}
public static final String getExtension(MultipartFile file) {
String fileName = file.getOriginalFilename();
String extension = null;
if (fileName == null) {
return null;
} else {
int index = indexOfExtension(fileName);
extension = index == -1 ? "" : fileName.substring(index + 1);
}
if (ObjectUtils.isEmpty(extension)) {
extension = MimeTypeUtils.getExtension(file.getContentType());
}
return extension;
}
public static int indexOfLastSeparator(String filename) {
if (filename == null) {
return -1;
} else {
int lastUnixPos = filename.lastIndexOf(47);
int lastWindowsPos = filename.lastIndexOf(92);
return Math.max(lastUnixPos, lastWindowsPos);
}
}
public static int indexOfExtension(String filename) {
if (filename == null) {
return -1;
} else {
int extensionPos = filename.lastIndexOf(46);
int lastSeparator = indexOfLastSeparator(filename);
return lastSeparator > extensionPos ? -1 : extensionPos;
}
}
// 设置默认上传路径
public void setDEFAULT_base_FILE(String DEFAULT_base_FILE) {
FileUploadUtils.DEFAULT_base_FILE = DEFAULT_base_FILE;
}
// 获取默认上传路径
public String getDEFAULT_base_FILE() {
return DEFAULT_base_FILE;
}
}
FileUtils
public class FileUtils {
//文件名正则校验
public static String FILENAME_PATTERN = "[a-zA-Z0-9_\-\|\.\u4e00-\u9fa5]+";
public static void writeBytes(String filePath, OutputStream os) {
FileInputStream fi = null;
try {
File file = new File(filePath);
if (!file.exists()) {
throw new FileNotFoundException(filePath);
}
fi = new FileInputStream(file);
byte[] b = new byte[1024];
int length;
while ((length = fi.read(b)) > 0) {
os.write(b, 0, length);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if(os != null) {
try {
os.close();
}catch (IOException e) {
e.printStackTrace();
}
}
if(fi != null) {
try {
fi.close();
}catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static boolean deleteFile(String filePath) {
boolean flag = false;
File file = new File(filePath);
if (file.isFile() && file.exists()) {
file.delete();
flag = true;
}
return flag;
}
public static boolean isValidName(String fileName) {
return fileName.matches(FILENAME_PATTERN);
}
public static String setFileDownloadHeader(HttpServletRequest request, String fileName)
throws UnsupportedEncodingException
{
final String agent = request.getHeader("USER-AGENT");
String filename = fileName;
if (agent.contains("MSIE"))
{
// IE浏览器
filename = URLEncoder.encode(filename, "utf-8");
filename = filename.replace("+", " ");
}
else if (agent.contains("Firefox"))
{
// 火狐浏览器
filename = new String(fileName.getBytes(), "ISO8859-1");
}
else if (agent.contains("Chrome"))
{
// google浏览器
filename = URLEncoder.encode(filename, "utf-8");
}
else
{
// 其它浏览器
filename = URLEncoder.encode(filename, "utf-8");
}
return filename;
}
}
MimeTypeUtils
public class MimeTypeUtils {
public static final String IMAGE_PNG = "image/png";
public static final String IMAGE_JPG = "image/jpg";
public static final String IMAGE_JPEG = "image/jpeg";
public static final String IMAGE_BMP = "image/bmp";
public static final String IMAGE_GIF = "image/gif";
// 图片 扩展
public static final String[] IMAGE_EXTENSION = {"bmp", "gif", "jpg", "jpeg", "png"};
// 视频动画 扩展
public static final String[] FLASH_EXTENSION = {"swf", "flv"};
// 媒体 扩展
public static final String[] MEDIA_EXTENSION = {"swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg", "asf", "rm", "rmvb"};
// 默认允许的扩展
public static final String[] DEFAULT_ALLOWED_EXTENSION = {
// 图片
"bmp", "gif", "jpg", "jpeg", "png",
// word excel powerpoint
"doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt",
// 压缩文件
"rar", "zip", "gz", "bz2",
// pdf
"pdf"};
public static String getExtension(String prefix) {
switch (prefix) {
case IMAGE_PNG:
return "png";
case IMAGE_JPG:
return "jpg";
case IMAGE_JPEG:
return "jpeg";
case IMAGE_BMP:
return "bmp";
case IMAGE_GIF:
return "gif";
default:
return "";
}
}
}
FileUploadController
@RestController
public class FileUploadController {
@Autowired
FileUploadService fileUploadService;
// 使用默认路径
@RequestMapping("/upload")
public String upload(MultipartFile file) throws Exception {
fileUploadService.upload(file, null);
return null;
}
// 自定义路径
@RequestMapping("/upload/template")
public String uploadPlace(MultipartFile file) throws Exception {
fileUploadService.upload(file, "H:\upload");
return null;
}
// 下载
@GetMapping("/download/file")
public String downloadFile(HttpServletResponse response) throws IOException {
fileUploadService.download(response, "上传模板");
return null;
}
}
entity
@TableName("tb_upload")
@Data
public class UploadEntity {
@TableId(type = IdType.AUTO)
private Long id;
//存在本地的地址
private String location;
//名称,业务中用到的名称,比如 ”档案模板“、”用户信息“、”登录记录“等等
private String name;
//保留文件原来的名字
private String oldName;
//描述(可以为空)
private String description;
private Date createTime;
private Date updateTime;
}
mapper
public interface UploadMapper extends baseMapperservice{ }
public interface FileUploadService {
void upload(MultipartFile file, String baseDir) throws Exception;
void download(HttpServletResponse response, String newName) throws IOException;
}
serviceimpl
@Service
public class FileUploadServiceImpl implements FileUploadService {
@Autowired(required = false)
UploadMapper uploadMapper;
@Override
public void upload(MultipartFile file, String baseDir) throws Exception {
if (file == null) {
throw new Exception( "上传文件不能为空");
}
String originalFilename = file.getOriginalFilename();
// 检测是否上传过同样的文件,如果有的话就删除。(这边可根据个人的情况修改逻辑)
QueryWrapper queryWrapper = new QueryWrapper<>();
queryWrapper.eq("old_name", originalFilename);
UploadEntity oldEntity = uploadMapper.selectOne(queryWrapper);
// 新的文件
UploadEntity uploadEntity = new UploadEntity();
uploadEntity.setCreateTime(new Date());
uploadEntity.setUpdateTime(new Date());
uploadEntity.setOldName(originalFilename);
uploadEntity.setName("上传模板");
// 文件上传位置
String fileLocation = null;
if (baseDir != null) {
fileLocation = FileUploadUtils.upload(baseDir, file);
} else {
fileLocation = FileUploadUtils.upload(file);
}
uploadEntity.setLocation(fileLocation);
uploadMapper.insert(uploadEntity);
if(oldEntity != null) {
// 删除原有的同名文件(实体文件 and 数据库文件)
FileUtils.deleteFile(oldEntity.getLocation());
uploadMapper.deleteById(oldEntity.getId());
}
}
@Override
public void download(HttpServletResponse response, String newName) throws IOException {
// 可以根据具体业务修改
QueryWrapper queryWrapper = new QueryWrapper<>();
queryWrapper.eq("name", newName);
UploadEntity uploadEntity = uploadMapper.selectOne(queryWrapper);
// 设置页面不缓存,清空buffer
response.reset();
// 二进制传输数据
response.setContentType("application/octet-stream");
// 设置响应头
response.setHeader("Content-Disposition", "attachment;filename="
+ URLEncoder.encode(uploadEntity.getOldName(), "UTF-8"));
// 使用HttpServletResponse的 getOutputSteam进行下载文件
FileUtils.writeBytes(uploadEntity.getLocation(), response.getOutputStream());
}
}
sql文件
DROP TABLE IF EXISTS `tb_upload`; CREATE TABLE `db_upload` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `location` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `old_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `create_time` datetime(0) NULL DEFAULT NULL, `update_time` datetime(0) NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 34 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;切换servlet容器
- springboot应用启动发现当前是个web应用(导入了web场景启动器)
- web应用会创建一个web版的ioc容器,ServletWebServerApplicationContext
- ServletWebServerApplicationContext启动的时候寻找ServletWebServerFactory
web服务器工厂
TomcatServletWebServerFactory,JettyServletWebServerFactory,Undertow… …
自动配置类ServletWebServerAutoConfiguration
默认Tomcat
web原生组件注入org.springframework.boot spring-boot-starter-weborg.springframework.boot spring-boot-starter-weborg.springframework.boot spring-boot-starter-tomcatorg.springframework.boot spring-boot-starter-jettyorg.springframework.boot spring-boot-starter-undertow
效果注解响应,没有spring拦截器
servlet@WebServlet(urlPatterns = "/my")
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException {
resp.getWriter().write("66666");
}
}
主启动类
@SpringBootApplication
@MapperScan("com.example.demo.mapper")
@ServletComponentScan("com.example.demo")
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
filter
@WebFilter(urlPatterns = {"/css
开发
vo继承 entity,entity实现序列化接口
controller 传参是 VO,DTO
不用Lombok- 一个jar用了Lombok,则所有依赖这个jar的应用都要安装Lombok,侵入性很强
- 破坏封装性
- 用了@Data,而不使用@EqualsAndHashCode(callSuper=true)的话,会默认是@EqualsAndHashCode(callSuper=false),这时候生成的equals()方法只会比较子类的属性,不会考虑从父类继承的属性,无论父类属性访问权限是否开放,这就可能得到意想不到的结果
- 升级到某个新版本的JDK,若其中的特性在Lombok中不支持的话就会受到影响
public abstract class CommcauseConstants {
// 序列名称
public static final String SEQ_NAME = "seq_commcause_serial";
public abstract static class CommCauseDicCode {
// 控制类型
public static final String COMPONENT_TYPE = "IDD_COMMCAUSE_COMPONENT_TYPE";
// 方案状态
public static final String SCHEME_STATUS = "IDD_COMMCAUSE_SCHEME_STATUS";
// 群障状态
public static final String COMMCAUSE_STATUS = "IDD_COMMCAUSE_STATUS";
// 运算类型
public static final String OPERATE_TYPE = "IDD_COMMCAUSE_OPERATE_TYPE";
// 障碍类型 + 客户解释口径(播放内容)
public static final String CATEGORY = "IDD_COMMCAUSE_CATEGORY";
// 工程性质
public static final String ENGINEERING_PROPERTY = "IDD_COMMCAUSE_ENGINEERING_PROPERTY";
// 来源系统
public static final String SRC_SYSTEM = "IDD_COMMCAUSE_SRC_SYSTEM";
}
// 方案状态
public abstract static class SchemeStatus {
// 未使用
public static final String UNUSED = "UNUSED";
// 已使用
public static final String USED = "USED";
// 已删除
public static final String DELETED = "DELETED";
}
// 群障状态
public abstract static class CommCauseStatus {
// 已归档
public static final String ARCHIVE = "ARCHIVE";
// 已删除
public static final String DELETED = "DELETED";
// 默认
public static final String DEFAULT = "DEFAULT";
}
}
// 例子: CommcauseConstants.SchemeStatus.UNUSED
// 或者
public final class StandardCharsets {
public static final Charset US_ASCII = Charset.forName("US-ASCII");
}
// 或者
public interface A {
String UNUSED = "UNUSED";
String USED = "USED";
}
mybatis plus
事务
在方法或类上面加 @Transactional
@Transactional事务不要滥用。事务会影响数据库的QPS(每秒查询数),需要考虑各方面的回滚方案,包括缓存回滚、搜索引擎回滚、消息补偿、统计修正等
@Transactional,抛出检查异常时,事务不会回滚,抛出运行时异常才会回滚
@Transactional(rollbackFor = Exception.class),都回滚
不要将注解声明在接口,而是在类的方法上
@Transactional(rollbackFor = MyException.class),自定义异常,回滚
@Transactional失效
- 比如有一个类Test,它的一个方法A,A再调用Test本类的方法B(不管B是否public还是private),但A没有声明注解事务,而B有。则外部调用A之后,B的事务是不会起作用的。(经常在这里出错)
- @Transactional的方法只能是public,否则事务不生效
@Transactional(propagation = Propagation.REQUIRED) 默认的
事务传播行为就是多个事务方法相互调用时,事务如何在这些方法间传播
PROPAGATION_REQUIRED 默认的事务传播行为,如果当前存在事务,则加入该事务;不存在,则新建一个事务
如果外部方法开启事务并且被Propagation.REQUIRED的话,所有Propagation.REQUIRED修饰的内部方法和外部方法均属于同一事务 ,只要一个方法回滚,整个事务均回滚
Class A {
@Transactional(propagation=propagation.PROPAGATION_REQUIRED)
public void aMethod {
//do something
B b = new B();
b.bMethod();
}
}
Class B {
@Transactional(propagation=propagation.PROPAGATION_REQUIRED)
public void bMethod {
//do something
}
}
PROPAGATION_REQUIRES_NEW:不管外部方法有没有开启事务,该注解修饰的内部方法都会开启自己的事务,互不干扰
举个例子:如果我们上面的bMethod()使用PROPAGATION_REQUIRES_NEW事务传播行为修饰,aMethod还是用PROPAGATION_REQUIRED修饰的话。如果aMethod()发生异常回滚,bMethod()不会跟着回滚,因为 bMethod()开启了独立的事务。但是,如果 bMethod()抛出了未被捕获的异常并且这个异常满足事务回滚规则的话,aMethod()同样也会回滚,因为这个异常被 aMethod()的事务管理机制检测到了。
Class A {
@Transactional(propagation=propagation.PROPAGATION_REQUIRED)
public void aMethod {
//do something
B b = new B();
b.bMethod();
}
}
Class B {
@Transactional(propagation=propagation.REQUIRES_NEW)
public void bMethod {
//do something
}
}
PROPAGATION_NESTED
如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。也就是说:
如果外部方法开启事务的话,Propagation.NESTED修饰的内部方法属于外部事务的子事务,外部主事务回滚的话,子事务也会回滚,而内部子事务可以单独回滚而不影响外部主事务和其他子事务。
这里还是简单举个例子:
如果 aMethod() 回滚的话,bMethod()和bMethod2()都要回滚,而bMethod()回滚的话,并不会造成 aMethod() 和bMethod()2回滚
Class A {
@Transactional(propagation=propagation.PROPAGATION_REQUIRED)
public void aMethod {
//do something
B b = new B();
b.bMethod();
b.bMethod2();
}
}
Class B {
@Transactional(propagation=propagation.PROPAGATION_NESTED)
public void bMethod {
//do something
}
@Transactional(propagation=propagation.PROPAGATION_NESTED)
public void bMethod2 {
//do something
}
}
- PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常
- PROPAGATION_NEVER: 以非事务方式运行,如果当前存在事务,则抛出异常
- PROPAGATION_SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行
- PROPAGATION_NOT_SUPPORTED: 以非事务方式运行,如果当前存在事务,则把当前事务挂起
注意:
SpringBoot使用mybatis一级缓存,要加上@Transactional,由于使用了数据库连接池,默认每次查询完之后自动commit,这就导致两次查询使用的不是同一个sqlSessioin
事务超时属性所谓事务超时,就是指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。在 TransactionDefinition 中以 int 的值来表示超时时间,其单位是秒,默认值为-1
事务只读属性@Transactional(readonly = true),默认是false
对于只有读取数据查询的事务,可以指定事务类型为 readonly,即只读事务。只读事务不涉及数据的修改,数据库会提供一些优化手段,适合用在有多条数据库查询操作的方法中。
很多人就会疑问了,为什么我一个数据查询操作还要启用事务支持呢?
MySQL 默认对每一个新建立的连接都启用了autocommit模式。在该模式下,每一个发送到 MySQL 服务器的sql语句都会在一个单独的事务中进行处理,执行结束后会自动提交事务,并开启一个新的事务
但是,如果你给方法加上了Transactional注解的话,这个方法执行的所有sql会被放在一个事务中。如果声明了只读事务的话,数据库就会去优化它的执行,并不会带来其他的什么收益。
如果不加Transactional,每条sql会开启一个单独的事务,中间被其它事务改了数据,都会实时读取到最新值
分享一下关于事务只读属性,其他人的解答:
- 如果你一次执行单条查询语句,则没有必要启用事务支持,数据库默认支持 SQL 执行期间的读一致性;
- 如果你一次执行多条查询语句,例如统计查询,报表查询,在这种场景下,多条查询 SQL 必须保证整体的读一致性,否则,在前条 SQL 查询之后,后条 SQL 查询之前,数据被其他用户改变,则该次整体的统计查询将会出现读数据不一致的状态,此时,应该启用事务支持
@Transactional(isolation=Isolation.DEFAULT)
- Isolation.DEFAULT:为数据源的默认隔离级别,Oracle读已提交,MySQL可重复读
- isolation=READ_UNCOMMITTED
- isolation.READ_COMMITTED
- isolation.REPEATABLE_READ
- isolation.SERIALIZABLE
import org.apache.commons.lang3.StringUtils;
StringUtils.isNotBlank null,空串,空格,换行符,制表符,换页符,回车符都是空,StringUtils.isBlank
StringUtils.isNotEmpty 字符串不为null,也不为空串 ,StringUtils.isEmpty
import java.util.Objects;
Objects.nonNull()
public static boolean nonNull(Object obj) {
return obj != null;
}
Objects.requireNonNull()
public staticT requireNonNull(T obj) { if (obj == null) throw new NullPointerException(); return obj; }
Objeects.isNull()
public static boolean isNull(Object obj) {
return obj == null;
}
CollectionUtils
org.apache.commons commons-collections4 4.3
CollectionUtils.isNotEmpty()
public static boolean isNotEmpty(Collection> coll) {
return !isEmpty(coll);
}
public static boolean isEmpty(Collection> coll) {
return coll == null || coll.isEmpty();
}
相等比较
java.util.Objects
- Objects.equals(o1, o2) 比较对象
import org.apache.commons.lang3.StringUtils;
-
StringUtils.equals(CharSequence cs1, CharSequence cs2) 比较字符串
-
public static boolean equals(CharSequence cs1, CharSequence cs2) { if (cs1 == cs2) { return true; } else if (cs1 != null && cs2 != null) { if (cs1.length() != cs2.length()) { return false; } else if (cs1 instanceof String && cs2 instanceof String) { return cs1.equals(cs2); } else { int length = cs1.length(); for(int i = 0; i < length; ++i) { if (cs1.charAt(i) != cs2.charAt(i)) { return false; } } return true; } } else { return false; } }
是个接口
public interface CharSequence {
int length();
char charAt(int index);
// ... 此处省略其他方法
}
String、StringBuffer、StringBulider 实现了CharSequence接口
java.lang.Void源码
public final
class Void {
// java.lang.Void 是 void的包装类
@SuppressWarnings("unchecked")
public static final Class TYPE = (Class) Class.getPrimitiveClass("void");
private Void() {}
}
不可实例化的占位符类,如果方法返回Void类型,只能返回null
public Void test() {
return null;
}
比如使用 Callable接口,该接口必须返回一个值,但实际执行后没有需要返回的数据,Future
Futuref = pool.submit(new Callable() { @Override public Void call() throws Exception { ...... return null; } });
另外Void也用于无值的Map中,例如 Map
org.apache.commons commons-collections4 4.3
常用方法
CollectionUtils.addIgnoreNull(personList,null); CollectionUtils.collate(Iterable extends O> a, Iterable extends O> b) CollectionUtils.collate(Iterable extends O> a, Iterable extends O> b, Comparator super O> c) CollectionUtils.containsAny(Collection> coll1, T... coll2) CollectionUtils.emptyIfNull(CollectionJWT HTTP错误代码collection) CollectionUtils.isEmpty(Collection> coll) CollectionUtils.isNotEmpty(Collection> coll) CollectionUtils.reverseArray(Object[] array); CollectionUtils.subtract(Iterable extends O> a, Iterable extends O> b) CollectionUtils.union(Iterable extends O> a, Iterable extends O> b) CollectionUtils.intersection(Collection a, Collection b) CollectionUtils.disjunction(Collection a, Collection b)
| 100 | Continue | 继续。客户端应继续其请求 |
|---|---|---|
| 101 | Switching Protocols | 切换协议。服务器根据客户端的请求切换协议。只能切换到更高级的协议,例如,切换到HTTP的新版本协议 |
| 200 | OK | 请求成功。一般用于GET与POST请求 |
| 201 | Created | 已创建。成功请求并创建了新的资源 |
| 202 | Accepted | 已接受。已经接受请求,但未处理完成 |
| 203 | Non-Authoritative Information | 非授权信息。请求成功。但返回的meta信息不在原始的服务器,而是一个副本 |
| 204 | No Content | 无内容。服务器成功处理,但未返回内容。在未更新网页的情况下,可确保浏览器继续显示当前文档 |
| 205 | Reset Content | 重置内容。服务器处理成功,用户终端(例如:浏览器)应重置文档视图。可通过此返回码清除浏览器的表单域 |
| 206 | Partial Content | 部分内容。服务器成功处理了部分GET请求 |
| 300 | Multiple Choices | 多种选择。请求的资源可包括多个位置,相应可返回一个资源特征与地址的列表用于用户终端(例如:浏览器)选择 |
| 301 | Moved Permanently | 永久移动。请求的资源已被永久的移动到新URI,返回信息会包括新的URI,浏览器会自动定向到新URI。今后任何新的请求都应使用新的URI代替 |
| 302 | Found | 临时移动。与301类似。但资源只是临时被移动。客户端应继续使用原有URI |
| 303 | See Other | 查看其它地址。与301类似。使用GET和POST请求查看 |
| 304 | Not Modified | 未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源 |
| 305 | Use Proxy | 使用代理。所请求的资源必须通过代理访问 |
| 306 | Unused | 已经被废弃的HTTP状态码 |
| 307 | Temporary Redirect | 临时重定向。与302类似。使用GET请求重定向 |
| 400 | Bad Request | 客户端请求的语法错误,服务器无法理解 |
| 401 | Unauthorized | 请求要求用户的身份认证 |
| 402 | Payment Required | 保留,将来使用 |
| 403 | Forbidden | 服务器理解请求客户端的请求,但是拒绝执行此请求 |
| 404 | Not Found | 服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置"您所请求的资源无法找到"的个性页面 |
| 405 | Method Not Allowed | 客户端请求中的方法被禁止 |
| 406 | Not Acceptable | 服务器无法根据客户端请求的内容特性完成请求 |
| 407 | Proxy Authentication Required | 请求要求代理的身份认证,与401类似,但请求者应当使用代理进行授权 |
| 408 | Request Time-out | 服务器等待客户端发送的请求时间过长,超时 |
| 409 | Conflict | 服务器完成客户端的 PUT 请求时可能返回此代码,服务器处理请求时发生了冲突 |
| 410 | Gone | 客户端请求的资源已经不存在。410不同于404,如果资源以前有现在被永久删除了可使用410代码,网站设计人员可通过301代码指定资源的新位置 |
| 411 | Length Required | 服务器无法处理客户端发送的不带Content-Length的请求信息 |
| 412 | Precondition Failed | 客户端请求信息的先决条件错误 |
| 413 | Request Entity Too Large | 由于请求的实体过大,服务器无法处理,因此拒绝请求。为防止客户端的连续请求,服务器可能会关闭连接。如果只是服务器暂时无法处理,则会包含一个Retry-After的响应信息 |
| 414 | Request-URI Too Large | 请求的URI过长(URI通常为网址),服务器无法处理 |
| 415 | Unsupported Media Type | 服务器无法处理请求附带的媒体格式 |
| 416 | Requested range not satisfiable | 客户端请求的范围无效 |
| 417 | Expectation Failed | 服务器无法满足Expect的请求头信息 |
| 500 | Internal Server Error | 服务器内部错误,无法完成请求 |
| 501 | Not Implemented | 服务器不支持请求的功能,无法完成请求 |
| 502 | Bad Gateway | 作为网关或者代理工作的服务器尝试执行请求时,从远程服务器接收到了一个无效的响应 |
| 503 | Service Unavailable | 由于超载或系统维护,服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的Retry-After头信息中 |
| 504 | Gateway Time-out | 充当网关或代理的服务器,未及时从远端服务器获取请求 |
| 505 | HTTP Version not supported | 服务器不支持请求的HTTP协议的版本,无法完成处理 |
此功能仅在Ultimate版本中得到支持
- Spring AOP / @AspectJ
- AspectJ
编译器 ajc
AspectJ是面向切面编程的框架



