@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
String name() default "";// 指定映射的名称
@AliasFor("path")
String[] value() default {}; // 指定请求路径的地址
@AliasFor("value")
String[] path() default {}; // 指定请求路径的地址
// 指定请求的方式,是一个RequsetMethod数组,可以配置多个方法
RequestMethod[] method() default {};
// 指定参数的类型
String[] params() default {};
// 指定请求头内容
String[] headers() default {};
// 指定数据请求的格式
String[] consumes() default {};
// 指定返回的内容类型
String[] produces() default {};
}
* 如上源码所示,在@Target中有两个属性,分别为 ElementType.METHOD 和 ElementType.TYPE ,也就是说@RequestMapping 可以在方法和类的声明中使用
* 可以看到注解中的属性除了 name() 返回的字符串,其它的方法均返回数组,也就是可以定义多个属性值,例如 value() 和 path() 都可以同时定义多个字符串值来接收多个URL请求
`RequestMapping属性介绍`
**1、name**
此处name属性,相当于方法的注释,使方法更易理解
@RequestMapping(value = “login”,name = “用户登录”)
@ResponseBody
public String login() {
return "success";
}
官方文档说:它能让你非常方便的在JSP页面上使用它,形如这样子可以直接通过静态页面调用:
<%@ taglib uri=“http://www.springframework.org/tags” prefix=“s” %>
…
Get Address
**2、value**
@Controller
@RequestMapping(“user”) //此处如果不省略,则为@RequestMapping(value=“user”)
public class UserController {
@RequestMapping("login")
@ResponseBody
public String login() {
return "success";
}
}
指定请求的实际地址,指定的地址可以是URI 模板模式(Template Pattern);
由于value属性是@RequestMapping注释的默认属性,因此如果只有唯一的属性,则可以省略该属性名,如果有超过一个属性,则必须写上value属性名称。即如下两个标注含义一样
@RequestMapping(value=“login”)
@RequestMapping(“login”)
value属性支持通配符匹配:
@RequestMapping(value=“login/*”);
即:http://localhost:8080/login/1或者http://localhost:8080/login/hahaha都能够正常访问该接口
此处通过URL:http://localhost:8080/user/login进行访问,将由login()方法进行处理
**3、path**
与value同义,path(value)(path 和 value 互相引用,参见RequestMapping接口源码)
path属性,和1中的value属性使用一致,两者都是用来作为映射使用的。
@RequestMapping(value=“login”)、@RequestMapping(path=“login”),这两种情况都能对login()方法进行访问
path属性支持通配符匹配:
@RequestMapping(path=“login/*”);
即:http://localhost:8080/login/1或者http://localhost:8080/login/abc都能够正常访问
**4、method**
指定请求类型, 如GET、POST、PUT、DELETE等;
@RequestMapping(value = “login”,method = RequestMethod.GET)
@ResponseBody
public String login() {
return "success";
}
以上方法表示,该方法只支持GET请求。
此处也可以为
@RequestMapping(value = “login”,method = {RequestMethod.GET,RequestMethod.POST})
说明该方法可以同时支持GET和POST请求。
如果没有method属性,则说明该方法支持全部的HTTP请求。
**5、params**
该属性指定,请求中必须包含params属性规定的参数时,才能执行该请求
@RequestMapping(value = “login”,params = “flag”)
@ResponseBody
public String login() {
return "success";
}
以上方法,说明请求中必须包含flag参数才能执行该请求,flag参数值不做要求
* http://localhost:8080/login?flag=xxx // 正常访问
* http://localhost:8080/login // 无法访问
@RequestMapping(value = “login”,params = “flag=true”)
@ResponseBody
public String login() {
return "success";
}
以上方法,说明请求中必须包含flag参数,而且参数值必须为true才能执行该请求
* http://localhost:8080/login?flag=true // 正常访问
* http://localhost:8080/login?flag=false // 无法访问
* http://localhost:8080/login // 无法访问
**6、headers**
* 用于HTTP协义交互的信息被称为HTTP报文,客户端发送的HTTP报文被称为请求报文,服务器发回给客户端的HTTP报文称为响应报文,报文由报文头部和报文体组成。
* 请求头部(Request Headers):请求头包含许多有关客户端环境和请求正文的信息,例如浏览器支持的语言、请求的服务器地址、客户端的操作系统等。
* 响应头部(Rsponse Headers):响应头也包含许多有用的信息,包括服务器类型、日期、响应内容的类型及编码,响应内容的长度等等。
该属性指定,请求中必须包含某些指定的header值,才能够让该方法处理请求
`如Chrome浏览器:`
可以通过点击F12(进入开发者模式)---->Network---->Name中点击网页---->右侧查看Headers即可,下边是我电脑中的一个请求头部示例:
Request Headers
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding:gzip, deflate, sdch
Accept-Language:zh-CN,zh;q=0.8
Cache-Control:max-age=0
Connection:keep-alive
Cookie:JSESSIONID=210075B5E521CWE3CDE938076295A57A
Host:localhost:8080
Upgrade-Insecure-Requests:1
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.93
// 表示只接收本机发来的请求
@RequestMapping(path = "/login", headers="Referer=http://localhost:8080")
public String login() {
return "success";
}
```
以上方法 ,必须满足请求的header中包含了指定的"Referer"请求头和值为"http://localhost:8080"时,才能执行该请求
**7、consumes**
指定处理请求的提交内容类型(Content-Type),例如:application/json、text/html时,才能够让该方法处理请求
```
@RequestMapping(value = "login",consumes = "application/json")
@ResponseBody
public String login() {
return "success";
}
```
**8、produces**
指定返回的内容类型,返回的内容类型必须是request请求头(Accept)中所包含的类型
```
@RequestMapping(value = "login",produces = "application/json")
@ResponseBody
public String login() {
return "success";
}
```
此外,produces属性还可以指定返回值的编码
```
@RequestMapping(value = "login",produces = "application/json,charset=utf-8")
```
如上,则指明返回utf-8编码
* * *
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210615200004665.png?x-oss-process=image,size_16,color_FFFFFF,t_70#pic_center)
课间休息,又来秀一下来自咱们群里同学的搬砖工地,坐标:**上海**。
* * *
[](
)面试题2:SpringBoot 打成的 jar 包和普通的 jar 包有什么区别
=======================================================================================================
[](
)正经回答:
--------------------------------------------------------------------
Spring Boot 中默认打包成的 jar 叫做`可执行 jar`,这种jar包可以通过可以通过命令(java -jar xxx.jar)来运行的,但这种jar包不能被其他项目所依赖,因为它和普通 jar 的结构不同,即使被依赖了也不能直接使用其中的类。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210615170501913.png#pic_center)
普通的jar包,解压后直接就是包名,包里就是我们的代码,而 Spring Boot 打包成的可执行 jar 解压后,`在 \BOOT-INF\classes 目录下才是我们的代码,因此无法被直接引用。如果非要引用`,可以在 pom.xml 文件中增加配置,将 Spring Boot 项目打包成两个 jar ,一个可执行,一个可引用。
![在这里插入图片描述](https://img-blog.csdnimg.cn/2021061517044514.png#pic_center)
[](
)深入追问:
--------------------------------------------------------------------
### [](
)追问1:如何让SpringBoot打的jar包可依赖?
在pom文件中增加以下配置:
```
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<!--可以把依赖的包都打包到生成的Jar包中 -->
<goal>repackage</goal>
</goals>
<!--可以生成不含依赖包的不可执行Jar包 -->
<configuration>
<classifier>exec</classifier>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
```
* 如下图,一次性打包生成两个jar,其中XXX.jar可被其它工程依赖,XXX-exec.jar可执行。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210616174551579.png?x-oss-process=image,size_16,color_FFFFFF,t_70#pic_center)
* * *
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210615172239223.gif#pic_center)
今天没有群里老铁们投稿插图,难受~
* * *
[](
)面试题3:CORS跨域问题是怎么引起的呢?
====================================================================================
[](
)正经回答:
--------------------------------------------------------------------
> Springboot跨域问题,是当前主流web开发人员都绕不开的难题。但我们首先要明确以下几点
>
> * 跨域只存在于浏览器端,不存在于安卓/ios/Node.js/python/ java等其它环境
> * 跨域请求能发出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了。
> * 之所以会跨域,是因为受到了同源策略的限制,同源策略要求源相同才能正常进行通信,即协议、域名、端口号都完全一致。
浏览器出于安全的考虑,使用 XMLHttpRequest对象发起 HTTP请求时必须遵守同源策略,否则就是跨域的HTTP请求,默认情况下是被禁止的。换句话说,浏览器安全的基石是同源策略。
同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210615170337921.png#pic_center)
**报错内容:**
```
Access to XMLHttpRequest at 'http://192.168.1.1:8080/app/easypoi/importExcelFile'
from origin 'http://localhost:8080' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
```
> **什么是CORS?**
>
> CORS是一个W3C标准,全称是”跨域资源共享”(Cross-origin resource sharing),允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
>
> 它通过服务器增加一个特殊的Header\[Access-Control-Allow-Origin\]来告诉客户端跨域的限制,如果浏览器支持CORS、并且判断Origin通过的话,就会允许XMLHttpRequest发起跨域请求。
**CORS Header**
* Access-Control-Allow-Origin: http://www.xxx.com
* Access-Control-Max-Age:86400
* Access-Control-Allow-Methods:GET, POST, OPTIONS, PUT, DELETE
* Access-Control-Allow-Headers: content-type
* Access-Control-Allow-Credentials: true
含义解释:
| CORS Header属性 | 解释 |
| --- | --- |
| Access-Control-Allow-Origin | 允许http://www.xxx.com域(自行设置,这里只做示例)发起跨域请求 |
| Access-Control-Max-Age | 设置在86400秒不需要再发送预校验请求 |
| Access-Control-Allow-Methods | 设置允许跨域请求的方法 |
| Access-Control-Allow-Headers | 允许跨域请求包含content-type |
| Access-Control-Allow-Credentials | 设置允许Cookie |
[](
)深入追问:
--------------------------------------------------------------------
### [](
)追问1:处理过Springboot的CORS跨域问题么?怎么解决的?
**方法一、直接采用SpringBoot的注解@CrossOrigin(也支持SpringMVC)**
简单粗暴的方式,Controller层在需要跨域的类或者方法上加上该注解即可
```
/**
* Created with IDEA
*
* @Author Chensj
* @Date 2020/5/8 10:28
* @Description xxxx控制层
* @Version 1.0
*/
@RestController
@CrossOrigin
@RequestMapping("/situation")
public class SituationController extends PublicUtilController {
@Autowired
private SituationService situationService;
// log日志信息
private static Logger LOGGER = Logger.getLogger(SituationController.class);
}
```
但每个Controller都得加,太麻烦了,怎么办呢,加在Controller公共父类(PublicUtilController)中,所有Controller继承即可。
```
/**
* Created with IDEA
*
* @Author Chensj
* @Date 2020/5/6 10:01
* @Description
* @Version 1.0
*/
@CrossOrigin
public class PublicUtilController {
/**
* 公共分页参数整理接口
*
* @param currentPage
* @param pageSize
* @return
*/
public PageInfoUtil proccedPageInfo(String currentPage, String pageSize) {
/* 分页 */
PageInfoUtil pageInfoUtil = new PageInfoUtil();
try {
/*
* 将字符串转换成整数,有风险, 字符串为a,转换不成整数
*/
pageInfoUtil.setCurrentPage(Integer.valueOf(currentPage));
pageInfoUtil.setPageSize(Integer.valueOf(pageSize));
} catch (NumberFormatException e) {
}
return pageInfoUtil;
}
}
```
当然,这里虽然指SpringBoot,SpringMVC也是同样的,但要求在Spring4.2及以上的版本。另外,如果SpringMVC框架版本不方便修改,也可以通过修改tomcat的web.xml配置文件来处理,请参照另一篇博文(nginx同理)
> SpringMVC使用@CrossOrigin使用场景要求
>
> * jdk1.8+
> * Spring4.2+
**方法二、处理跨域请求的Configuration**
增加一个配置类,CrossOriginConfig.java。继承WebMvcConfigurerAdapter或者实现WebMvcConfigurer接口,其他都不用管,项目启动时,会自动读取配置。
```
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/**
* AJAX请求跨域
* @author Mr.W
* @time 2018-08-13
*/
@Configuration
public class CorsConfig extends WebMvcConfigurerAdapter {
static final String ORIGINS[] = new String[] { "GET", "POST", "PUT", "DELETE" };
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*").allowCredentials(true).allowedMethods(ORIGINS).maxAge(3600);
}
```
**方法三、采用过滤器(filter)的方式**
同方法二加配置类,增加一个CORSFilter 类,并实现Filter接口即可,其他都不用管,接口调用时,会过滤跨域的拦截。
```
@Component
public class CORSFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse res = (HttpServletResponse) response;
res.addHeader("Access-Control-Allow-Credentials", "true");
res.addHeader("Access-Control-Allow-Origin", "*");
res.addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
res.addHeader("Access-Control-Allow-Headers", "Content-Type,X-CAF-Authorization-Token,sessionToken,X-TOKEN");
if (((HttpServletRequest) request).getMethod().equals("OPTIONS")) {
response.getWriter().println("ok");
return;
}
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
}
```
[](
)每日小结
===================================================================
今天我们复习了面试中常考的Springboot相关问题,你做到心中有数了么?`对了,如果你的朋友也在准备面试,请将这个系列扔给他,如果他认真对待,肯定会感谢你的!!`好了,今天就到这里,学废了的同学,`记得在评论区留言:打卡。`,给同学们以激励。
_本篇参考资料:
https://www.zhihu.com/question/284488830/answer/439068110
https://www.cnblogs.com/wudimanong/p/10457211.html_