大家好!我是,就是那个“请你跟我这样做,我就跟你这样做!”的村长?????!
||To Do||???
正推出一系列【To Do】文章,该系列文章重要是对Java开发方法的整理,便于在开发项目过程中及时回顾,提升个人开发实践能力。主要面向Java开发,总结常用的框架或中间件的使用方法。
文章目录
一、开发工具或技巧??
1、Lombok
“Never write another getter or equals method again, with one annotation.”
Lombok是一个Java库,可以通过注解的方式添加构造器、getter、setter或equals方法,提升开发人员的工作效率。
(1)添加依赖和插件
使用Lombok需要添加相应的依赖,除此还需要在IDEA中的Plugins搜索lombok插件进行安装。
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
<scope>provided</scope>
</dependency>
(2)常用注解
注解
说明
@Data
注解在类,生成setter/getter、equals、canEqual、hashCode、toString方法
@AllArgsConstructor
注解在类,生成包含类中所有字段的构造方法。
@NoArgsConstructor
注解在类,生成无参的构造方法。
@EqualsAndHashCode
注解在类,生成hashCode和equals方法。
@Setter
注解在类或字段,注解在类时为所有字段生成setter方法,注解在字段上时只为该字段生成setter方法。
@Getter
使用方法同上,区别在于生成的是getter方法。
@ToString
注解在类,添加toString方法。
@Slf4j
注解在类,生成log变量,严格意义来说是常量。private static final Logger log = LoggerFactory.getLogger(UserController.class);
(3)工作原理
Java注解的解析分为两种方式,一是运行时解析,二是编译时解析。
Lombok注解使用的是编译时解析:
- 在javac之后,针对源代码会生成一个AST语法树,此时会启动Lombok进程对AST树进行修改
- 找到对应的地方增加getter、setter方法的相应节点,根据该树生成新的字节码文件。
2、dev-tools
基于springboot进行开发,开发过程中,如果每次修改代码都将项目重启,将耗费大量的时间成本。
使用spring-boot-devtools
,可以实现指定目录(默认为classpath路径)下的文件进行更改后,项目自动重启,更改后的代码自动生效。
重启快捷键:Ctrl + F9
(1)添加依赖和配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
devtools依赖此配置,否则不生效。
<plugin> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork>
</configuration>
</plugin>
(2)原理
spring-boot-devtools使用了两个类加载器
ClassLoader,一个ClassLoader加载不会发生更改的类(第三方jar包),另一个ClassLoader(restart ClassLoader)加载已更改的类(自定义的类)。
依旧是重启应用,但是第三方jar包不会进行再次加载,只会加载我们自定义的且被修改的类,加载的类变少则减少了重启加载时间。
3、API测试—Apifox
百度搜索Apifox进行下载,下载完成后应用类就有相应的教程。官方对Apifor定位,Apifox = Postman + Swagger + Mock + JMeter,即对现有Api调试产品的集成。
相关使用文档:接口调试 / 接口用例 | Apifox 使用文档
该应用对标Postman,提供了更细致的Api测试服务(Apifox打钱!!!)。
4、配置文件
(1)yaml格式配置文件
YAML(YAML Ain’t Markup Language)以数据为中心,比json、xml更适合做配置文件。
YAML都是key: value配置,:后必须有空格,换行可代替空格进行嵌套配置,以左对齐为属性的附属设置。
# 这是一个注解
#1、对象或Map
friend:
name: zhangsan
age: 20
#行内写法
friend: {name:zhangsan,age:20}
#2、数组、List或Set
pets:
- cat
- dog
- pig
#行内写法
pets: [cat,dog,pig]
(2)配置文件属性绑定
我们可以通过注解类的方式,将配置文件的内容注入到相应的Bean中。当Bean被实例化时,@ConfigurationProperties会将对应前缀的后面的属性与Bean对象的属性匹配。符合条件则进行赋值。
@ConfigurationProperties(prefix="属性名")
(3)配置提示工具
Configuration-Processor是用于配置文件时使用的配置提示工具,可在编码时进行Coding提示。需要添加依赖如下。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
这个Java库只用于代码编写时进行提示,我们打包SpringBoot应用时不需要将其打包,则需要添加以下Maven插件配置,这样在项目打包时就不会将spring-boot-configuration-processor中的类进行打包。
<plugin> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<execuldes>
<execulde>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<execulde>
<execuldes>
</configuration>
</plugin>
二、请求与响应??
1、调用层级与注解
(1)调用层级
SpringBoot开发我们通常分为Dao、Service、Controller、Entity四个层级。
- Dao:编写与数据库交互相应接口(一般结合Mybatis)。
- Service:编写业务处理逻辑,一般先写接口,再通过实现impl类进行代码的编写。
- Controller:编写请求处理与响应返回逻辑。
- Entity:实体类,用于建立系统程序的实体对应POJO类(对应数据库的相关表段)或对请求响应相关数据的封装。
(2)相关注解
@Controller、@Repository、@Service、@Component四大注解可代表不同的层级,其中@Controller、@Repository、@Service都是@Component注解的衍生注解。在启动类中配置的ComponentScan
@Controller
:注解控制器类@Repository
:注解Dao类或接口@Service
::注解Service类或接口@Component
:注解普通类
2、请求接收
(1)普通类型参数
当请求路径url附带的key-value值的参数名称与请求参数的名称一致时,会自动映射匹配。
http://localhost:8080/arg?username=chief&age=18
@RequestMapping(value = "arg")
public void sout(String username, int age) {
System.out.println(username);
System.out.println(age);
}
(2)POJO类型
Controller中的业务方法的POJO参数的属性名与请求参数的name一致,参数值会自动映射匹配。
http://localhost:8080/arg?username=chief&age=18
@Data
public class User {
private String username;
private int age;
}
@RequestMapping(value = "/arg")
public void sout(User user) {
System.out.println(user);
}
(3)数组类型参数
Controller中的业务方法数组名称与请求参数的name一致,参数值会自动映射匹配。
http://localhost:8080/arg?strs=1&strs=2&strs=3
@RequestMapping(value = "/arg")
public void sout(String[] strs) {
System.out.println(Arrays.asList(strs));
}
(4)集合类型参数
获得集合参数时,要将集合参数包装到一个POJO中才可以,即当通过浏览器提交form表单时。
<form action="${pageContext.request.contextPath}/arg" method="post">
<input type="text" name = "username"><br/>
<input type="text" name = "age"><br/>
<input type="submit" value="提交">
</form>
@Data
public class UserVo {
private String username;
private int age;
}
@RequestMapping(value = "/arg")
public void sout(UserVo vo) {
System.out.println(vo);
}
(5)相关注解的使用
① RequestBody
@RequestBody主要用来接收前端传递给后端的json字符串数据,接收的参数是来自请求体,@RequestBody最多只能有一个。不能用于GET请求,因为GET请求没有请求体。
会根据json字符串中的key来匹配对应@RequestBody实体类的属性,@RequestBody的作用就是自动将json格式的数据转java对象。
$.ajax({
url:"/arg",
type:“POST”,
data:’{“username”:“admin”,“age”,“18”}’,
content-type:“application/json charset=utf-8”,
success:function(data){
alert("request success !");
}
});
@requestMapping("/arg")
public void sout(@requestBody User user){
System.out.println(user);
}
② RequestParam
RequestParam作用是将请求参数绑定到对应Controller的方法参数上,用于接收普通请求参数,可以是url请求,也可以是请求体中的参数。
不加RequestParam也能接收url后的参数,如果不加@RequestParam,则url请求可以带参数也可以不带,如果加了该注解则必须带参数【可设置required为false,这样也可以不带参数】。
@RequestParam(value="参数名",required="true/false",defaultValue="")
//- value:参数名
//- required:是否包含该参数,默认为true
//- defaultValue:默认参数值,设置以后required设置为false
http://localhost:8080/arg?name=chief
@RequestMapping("/arg")
public void sout(@RequestParam("name") String name){
System.out.println(name);
}
③ PathVariable
作用也是接收普通参数,区别是@PathVariable通过"/"的方式来获取参数值,即实现RESTful风格,现在开发常用PathVariable代替RequestParam。
@RequestMapping("/arg/{name}")
public void sout(@PathVariable("name") String name){
System.out.println(name);
}
④ RequestAttribute
作用是从request请求中取对应的属性值。
@GetMapping("/arg")
public void sout(@RequestAttribute("requestArg") String name) {
System.out.println(name);
}
等同于:
request.getAttribute("name");
(6)通过HttpServletRequest
HttpServletRequest可用于获取用户提交的表单数据,相关方法如下:
方法
说明
String getParameter(String name)
该方法用于获取某个指定名称的参数值,如果请求消息中没有包含指定名称的参数,getParameter()方法返回null;如果指定名称的参数存在但没有设置值,则返回一个空串;如果请求消息中包含有多个该指定名称的参数,getParameter()方法返回第一个出现的参数值
String[] getParameterValues(String name)
HTTP请求消息中可以有多个相同名称的参数(通常由一个包含有多个同名的字段元素的FORM表单生成),如果要获得HTTP请求消息中的同一个参数名所对应的所有参数值,那么就应该使用getParameterValues()方法,该方法用于返回一个String类型的数组
Map getParameterMap()
用于将请求消息中的所有参数名和值装入进一个Map对象中返回
<form action="/login" method="post">
用户名:<input type="text" name="username">
密码:<input type="password" name="password">
<input type="submit" value="提交">
</form>
@RequestMapping("/login")
public void login(HttpServletRequest request){
String username = request.getParameter("username");
String password = request.getParameter("password");
}
3、Cookie与Session
(1)自定义Cookie
我们以登录为例。
<form action="login" method="post">
用户:<input type="text" name="username" /><br/>
密码:<input type="text" name="password" /><br/>
<input type="submit" value="登录">
</form>
在自定义Cookie时,我们需要使用HttpServletResponse对象,将其加入到HttpServletResponse,作为响应返回的一部分。
@RequestMapping("/login")
public void login(String username, String password, HttpServletResponse response) {
System.out.println(username+":"+password );
//创建cookie信息,在构造函数中设置名字和值,第一个参数是名字,第二个参数是值。
Cookie userCookie = new Cookie("username", username);
Cookie pwdCookie = new Cookie("password", password);
//将cookie信息放到回应response中
response.addCookie(userCookie);
response.addCookie(pwdCookie);
}
加入到HttpServletResponse后可在服务端Controller任意方法中进行获取,在SpringMVC中使用注解@CookieValue获取
注解中name为cookie的名字,required设置为false。
@RequestMapping(value="/student", method = RequestMethod.GET)
public void get(@CookieValue(name = "JSESSIONID", required = false)String jid, @CookieValue(name = "username", required = false)String username, @CookieValue(name = "password", required = false)String password) {
System.out.println("session id 是:" + jid);
System.out.println("当前登录用户名为:" + username + ",密码为: " + password );
}
(2)Session的使用
由于cookie的数量有限制,安全性也较低,为了满足更多需求,可以使用session。当服务器创建完session对象后,会把session对象的id以cookie形式返回给客户端。session是在第一次访问网站时创建出来的,我们使用时不需要再创建。Session的获取有以下两种方式
//方式一
public void get(HttpServletRequest request){
HttpSession session = request.getSession();
}
//方式二
public void get(HttpSession session){
}
Session提供了以下方法对其进行操作。
public void setAttribute(String name,Object value)
//将value对象以name名称绑定到会话
public object getAttribute(String name)
//获取指定name的属性值,如果属性不存在则返回null
public void removeAttribute(String name)
//从会话中删除name属性,如果不存在不会执行,也不会抛处错误
public Enumeration getAttributeNames()
//返回和会话有关的枚举值
public void invalidate()
//使会话失效,同时删除属性对象
public Boolean isNew()
//用于检测当前客户是否为新的会话
public long getCreationTime()
//返回会话创建时间
public long getLastAccessedTime()
//返回在会话时间内web容器接收到客户最后发出的请求的时间
public int getMaxInactiveInterval()
//返回在会话期间内客户请求的最长时间.秒
public void setMasInactiveInterval(int seconds)
//允许客户客户请求的最长时间
ServletContext getServletContext()
//返回当前会话的上下文环境,ServletContext对象可以使Servlet与web容器进行通信
public String getId()
//返回会话期间的识别号
4、响应方式
(1)统一封装返回json对象[建议]
在SpringBoot实际开放中,我们需要通过定义统一的响应对象,以便前端处理。SpringBoot框架默认添加了Jackson依赖,我们只需要通过@ResponseBody或@RestController说明返回的值为对象即可。
通常我们将需要返回的对象封装为以下形式,code表示响应码,info表示响应信息,data表示我们需要传入的对象数据。
@Data
public class Response<T> {
private Integer code;
private String info;
private T data;
}
在返回响应值时,我们只需要将属性封装到一个对象中进行返回即可。
(2)返回ModelAndView
返回ModelAndView,即将返回的数据和界面名称都给ModelAndView。
@RequestMapping(value="/login")
public ModelAndView WelcomeSeven(){
//创建ModelAndView对象
ModelAndView mav = new ModelAndView("index");
mav.addObject("name", "张三");
mav.addObject("role", "管理员");
return mav;
}
@RequestMapping(value="/login")
public String WelcomeEight(Model model){
model.addAttribute("name", "张三");
model.addAttribute("role", "管理员");
return "index";
}
(3)返回String
直接返回字符串的话,返回的是界面的名称。
(4)通过Map对象返回
@RequestMapping(value="/login")
public String WelcomeNine(Map<String,String> map){
map.put("name", "张三");
map.put("role", "管理员");
return "index";
}
(5)通过Request域返回
@RequestMapping("/login")
public String index(HttpServletRequest request){
request.setAttribute("name", "张三");
request.setAttribute("role", "管理员");
return "index";
}
5、重定向方式
(1)通过String
@GetMapping(value = "test")
public String test(){
return "redirect:https://www.baidu.com";
}
(2)通过ModelAndView
@GetMapping(value = "test")
public ModelAndView test(){
return new ModelAndView("redirect:https://www.baidu.com");
}
6、SpringMVC请求处理原理
SpringMVC的请求处理过程的时序图【图源网络】:
DispacherServlet会先进行初始化,初始化过程分为两步,一是初始化WebApplicationContext容器,然后再初始化HandlerMapping、HandlerAdapter、ViewResolver等Web组件。
对于请求的处理分为以下步骤:
-
DispatcherServlet通过doService调用doDispatch,对容器封装的Request和Response进行处理。[doDispatch(request,reponse)]
-
通过getHandler获取到HandlerExecutionChina对象mappedHandler[HandlerExecutonChina mappedHandler = getHandler(processedRequest)]
- getHandler方法通过遍历HandlerMapping,找到对应映射关系的handler并进行返回
-
handlerExecutorChina 执行链对象获取到对应的handlerAdapter,handler适配器[handlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler())]
-
根据适配器真正执行handler ,并且返回一个modeAndView,在执行该方法前后会通过preHandler和postHandler执行相关的拦截方法。[mv=ha.handler(xxx)]
-
最后通过获取的ModelAndView,解析渲染页面并返回结果[processDispatchResult(processedRequest,response,mappedHAndler,mv,dispatchException)]
- process方法通过render()完成页面的渲染
三、文件上传??
1、文件上传例子
浏览器文件通过表单进行上传。
<form method:"post" enctype="multipart/form-data">
<input type="file" name="img"> 单文件上传
<input type="file" name="imgs" multiple> 多文件上传
<button type="submit">提交</button>
<form/>
对应编写的Controller如下。
@PostMapping("/upload")
public void upload(
@RequestParam("img") MutipartFile file,
@RequestParam("imgs") MutipartFile[] files){
if(!file.isEmpty()){
file.transferTo(new File("文件存放路径\"+"文件名"))
}
if(files.length>0){
for(MutipartFile f : files){
if(!f.isEmpty()){
f.transferTo(new File("文件存放路径\"+"文件名"))
}
}
2、文件上传配置
SpringBoot默认设置了文件上传的参数,其中最大文件上传大小默认为1M,一次请求上传的文件大小不超过10M,我们可以通过修改配置文件来修改相关设定。
# 开启multipart上传功能
spring.servlet.multipart.enabled=true
# 文件写入磁盘的阈值
spring.servlet.multipart.file-size-threshold=2KB
# 最大文件大小
spring.servlet.multipart.max-file-size=200MB
# 最大请求大小
spring.servlet.multipart.max-request-size=215MB
# 文件存储所需参数
# 所有通过 REST API 上传的文件都将存储在此目录下
file.upload.path=文件上传路径
四、拦截器??
配置拦截器可分为两个步骤:自定义拦截器器和配置拦截器。
1、自定义配置器
自定义配置器需要我们实现HandlerInterceptor接口,一般只需要拦截Controller方法执行前的操作即可。
@Component
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("controller执行前");
return true;//true表示放行
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("渲染前-controller执行后");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("渲染后处理");
}
}
2、配置拦截器
我们通过配置类的编写来进行具体配置,这个类不仅配置了相应的类,并且
@Configuration//定义此类为配置类
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
//addPathPatterns拦截的路径
String[] addPathPatterns = {
"/path/**"
};
//excludePathPatterns排除的路径
String[] excludePathPatterns = {
"/path/xxx1","/path/xxx2"
};
//添加自定义拦截器对象并指定其拦截的路径和排除的路径
registry.addInterceptor(new MyInterceptor()).addPathPatterns(addPathPatterns).excludePathPatterns(excludePathPatterns);
}
}
也可以写在一行:
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor()).addPathPatterns("/path/**").excludePathPatterns("/path/xxx1","/path/xxx2");
}
3、实现例子—登录拦截
(1)拦截器
public class UserInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//从session中获取user的信息
User user =(User)request.getSession().getAttribute("user");
//判断用户是否登录
if (null==user){
//重定向
response.sendRedirect(request.getContextPath()+"/user/error");
//不放行
return false;
}
return true;
}
}
(2)拦截器配置类
@Configuration//定义此类为配置类
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
//addPathPatterns拦截的路径
String[] addPathPatterns = {
"/user/**"
};
//excludePathPatterns排除的路径
String[] excludePathPatterns = {
"/user/login","/user/noLg","/user/error"
};
//创建用户拦截器对象并指定其拦截的路径和排除的路径
registry.addInterceptor(new UserInterceptor()).addPathPatterns(addPathPatterns).excludePathPatterns(excludePathPatterns);
}
}
五、事务??
1、事务开启方式
(1)在启动类上开启事务支持
@EnableTransactionManagement
该注解代替了以下xml配置:
<!--事务的注解驱动-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
(2)在业务逻辑接口实现类声明事务
@Transactional(propagation=Propagation.REQUIRED,readOnly=false)
public void tranSerciveImpl(){}
2、事务补充说明
(1)提示
Transactional注解只能应用到public方法上,@Transactional注解的事物所管理的方法中,如果方法抛出运行时异常,那么会进行事务回滚;如果方法抛出的是非运行时异常,那么不会回滚。
我们可以通过更改rollbackFor = {Exception.class} 设置当Exception异常或Exception的所有任意子类异常时事物会进行回滚,且被catch处理了的异常,不会被事务作为回滚判断依据。
(2)事务处理原理
Spring事务处理主要依靠以下几个核心类:
- TransactionInterceptor:使用AOP实现的声明式事务处理拦截器,封装了Spring对事务处理的基本过程。
- TransactionInfo和TransactionStatus:存放事务处理信息的主要数据对象,TransactionInfo持有TransactionStatus对象。
- TransactionInfo本身是一个栈,对应着每一次事务方法的调用时保存的事务处理信息
- TransactionStatus掌管事务执行的详细信息,包括事务对象、事务执行状态、事务设置状态等信息
- TransactionManager:具体进行事务处理的事务处理器,负责最底层事务的创建、挂起、提交、回滚操作。
- DataSourceTransactionManager:AbstractPlatformTransactionManager的子类,AbstractPlatformTransactionManager已经设计好了事务创建、挂起、提交、回滚操作的模板方法。DataSourceTransactionManager通过doBegin方法创建事务,该方法会得到相应的Connection对象,事务的配置、提交、回滚都是通过直接调用Connection来完成。
- HibernateTransactionManager在调用doBegin创建事务时,会打开一个Session,该类是Hibernate管理数据对象生命周期的核心类,HibernateTransactionManager通过对Session的管理来完成事务处理实现。
六、异常处理??
1、全局异常捕获
SpringBoot中提供了@ControllerAdvice
和@ExceptionHandler
两个注解来实现专门对服务器500异常进行自定义处理。
定义一个类,使用 @ControllerAdvice
注解该类,使用 @ExceptionHandler
注解方法,定义了一个 GlobalExceptionHandler
类表示来处理全局异常,代码如下:
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(value = {xxx1Exception.class,xxx2Exception.class})
public String arithmeticExceptionHandle(Exception e) {
log.error("捕获异常:"+e);
return "error";//返回视图地址
}
}
@ControllerAdvice
注解表示我们定义的是一个控制器增强类,当其他任何控制器发生异常且异常类型符合@ExceptionHandler
注解中指定的异常类时,原请求将会被拦截到这个我们自定义的控制器方法中。
2、自定义错误页
SpringBoot默认的错误处理机制可以自动返回相应的错误信息(以json或页面的形式),我们也可以自定义错误页面。只需要将该页面放在resource/public/error/或resource/templates/error下
页面名称为相应的响应码,例如404,400等。响应码可以定义为4xx,5xx,xx相当于通配符,可以返回以4开头或5开头的错误页面。
——————————————————————
作者:
参考:文章出于整理目的出发,参考了多篇博文,侵删
个人网站:www.76pl.com
???还有人不关注我的话,我真的会谢???
——————————————————————