spring boot的定位
Spring Boot并不是不对Spring功能上的增强,而是提供了一种快速使用Spring的方式
本节转载自spring boot与spring mvc的区别是什么?
Spring Boot与spring及spring mvc的关系
简介
- Spring 框架就像一个家族,有众多衍生产品例如 boot、security、jpa等等。但他们的基础都是Spring 的 IOC(提供了依赖注入的容器)和 AOP(解决了面向横切面的编程) ,然后在此两者的基础上实现了其他延伸产品的高级功能。
- Spring MVC是基于 Servlet 的一个 MVC 框架 主要解决 WEB 开发的问题
- 因为 Spring 的配置非常复杂,各种XML、 JavaConfig、hin处理起来比较繁琐。于是为了简化开发者的使用,从而创造性地推出了Spring boot,
约定优于配置
,简化了spring的配置流程
关系
- Spring 最初利用“工厂模式”(DI)和“代理模式”(AOP)解耦应用组件。
- 大家觉得挺好用,于是按照这种模式搞了一个 MVC框架(一些用Spring 解耦的组件),用开发 web 应用( SpringMVC )。
- 然后有发现每次开发都写很多样板代码,为了简化工作流程,于是开发出了一些“懒人整合包”(starter),这套就是 Spring Boot。
所以,用最简练的语言概括就是:
Spring 是一个“引擎”;
Spring MVC 是基于Spring的一个 MVC 框架 ;
Spring Boot 是基于Spring4的条件注册的一套快速开发整合包。
spring mvc < spring <springboot
为什么用spring boot
功能
Spring Boot实现了自动配置,降低了项目搭建的复杂度。
众所周知Spring框架需要进行大量的配置,Spring Boot引入自动配置的概念,让项目设置变得很容易。Spring Boot本身并不提供Spring框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于Spring框架的应用程序。也就是说,它并不是用来替代Spring的解决方案,而是和Spring框架紧密结合用于提升Spring开发者体验的工具。同时它集成了大量常用的第三方库配置(例如Jackson, JDBC, Mongo, Redis, Mail等等),Spring Boot应用中这些第三方库几乎可以零配置的开箱即用(out-of-the-box),大部分的Spring Boot应用都只需要非常少量的配置代码,开发者能够更加专注于业务逻辑。
Spring Boot只是承载者,辅助你简化项目搭建过程的。如果承载的是WEB项目,使用Spring MVC作为MVC框架,那么工作流程和你上面描述的是完全一样的,因为这部分工作是Spring MVC做的而不是Spring Boot。
对使用者来说,换用Spring Boot以后,项目初始化方法变了,配置文件变了,另外就是不需要单独安装Tomcat这类容器服务器了,maven打出jar包直接跑起来就是个网站,但你最核心的业务逻辑实现与业务流程实现没有任何变化
。
优点
- Spring Boot可以建立独立的
Spring
应用程序 -
内嵌了如
Tomcat,Jetty
和Undertow这样的容器,也就是说可以直接跑起来,用不着再做部署工作了 - 可以自动配置Spring:无需再像Spring那样搞一堆繁琐的
xml文件的配置
- 提供了一些现有的功能,如量度工具,表单数据验证以及一些外部配置这样的一些第三方功能
- 提供的POM可以简化
Maven
的配置
spring Boot实例demo
程序入口
SpringApplication.run(App.class, args)
首先来创建src/main/java/ToutiaoApplication.java
:
@SpringBootApplication
public class ToutiaoApplication {
public static void main(String[] args) {
SpringApplication.run(ToutiaoApplication.class, args);
}
}
程序的入口:SpringApplication.run(Application.class, args)
,SpringApplication是Spring Boot框架中描述Spring应用的类,它的run()方法会创建一个Spring应用上下文(Application Context)。
另一方面它会扫描当前应用类路径上的依赖,如果Spring Boot判断这是一个Web应用,会启动一个内嵌的Servlet容器(默认是Tomcat)用于处理HTTP请求。
@SpringBootApplication
Spring Boot提供一个方便的 @SpringBootApplication 选择。该 @SpringBootApplication 注解等价于以默认属性使用 @Configuration+@EnableAutoConfiguration+@ComponentScan
1. @Configuration
定义一个配置类,用@Configuration注解该类,等价于XML中配置beans
;用@Bean标注方法等价于XML中配置bean
public class SpringConfig {
@Bean
public Piano piano(){
return new Piano();
}
@Bean(name = "counter")
public Counter counter(){
return new Counter(12,"Shake it Off",piano());
}
}
2. @EnableAutoConfiguration
启动Spring MVC
3. @ComponentScan
启用组件扫描
实现Hello World显示
在controller目录下创建一个IndexController 类(之后的方法除特殊说明,否则都是IndexController类中的方法)
@Controller
public class IndexController {
@RequestMapping(path = {"/","/index"})
@ResponseBody
public String index(HttpSession session){
return "Hello World" ;
}
}
运行Application文件,右键Run As -> Java Application,之后打开浏览器输入地址:http://127.0.0.1:8080/(或者http://127.0.0.1:8080/ index) 就可以看到Hello world。
@Controller
基于@Component注解,表明是控制类组件,辅助实现组件扫描,组件扫描会自动找到@Controller注解对应的类,并将其声明为Spring应用上下文的一个bean
@RequestMapping(value=”/”,method)
- 作用
@RequestMapping 注解为控制器指定可以处理那些URL 请求
@RequestMapping (value=”/”,method),value值属性指定了这个方法所要处理的请求路径,method属性细化了它所处理的HTTP方法(GET或者POST),例如
@RequestMapping(value = "/login", method = RequestMethod.POST)
获取请求中信息
- @RequestBody
绑定请求对象,Spring会帮你进行协议转换,将Json、Xml协议转换成你需要的对象。 - @ResponseBody
标注任何对象,由Srping完成对象——协议的转换。
在SpringMVC中,可以使用@RequestBody和@ResponseBody两个注解,分别完成请求报文到对象和对象到响应报文的转换,底层这种灵活的消息转换机制,就是Spring3.x中新引入的HttpMessageConverter即消息转换器机制。
//value与path都是别名,实质上是一样的
@RequestMapping(value = {"/profile/{groupId}/{userId}"})
@ResponseBody
//127.0.0.1:8080/profile/12/33?key=xx&type=33
public String profile(
@PathVariable("groupId") String groupId,
@PathVariable("userId") int userId,
@RequestParam(value = "key",defaultValue = "megustas") String key,
@RequestParam(value = "type",defaultValue = "1") int type) {
return String.format("GID{%s},UID{%d},KEY{%s},TYPE{%d}",groupId,userId,key,type);
}
@RequestParam注解和@PathVariable注解的区别,从字面上可以看出前者是获取请求里边携带的参数;后者是获取请求路径里边的变量参数。
例如:
127.0.0.1/user/{userId}?userName=datiangou
- userId是路径上的变量
- userName才是请求参数信息
@Pathvariable
通过 @PathVariable 可以将 URL 中占位符参数绑定到控制器处理方法的入参中:URL 中的{xxx}
占位符可以通过@PathVariable("xxx") 绑定到操作方法的入参
中。
@RequestMapping("/pathVariable/{name1}")
public String pathVariable(@PathVariable("name1")String name2){
System.out.println("hello "+name2);
return "helloworld";
}
URL 中的{name1}
占位符通过@PathVariable("name1") 绑定到操作方法的String name2入参
中。
@RequestParam
获取请求参数
模板
直接返回HTML代码太复杂
在之前所有的@RequestMapping注解的方法中,返回值字符串都被直接传送到浏览器端并显示给用户。但是为了能够呈现更加丰富、美观的页面,我们需要将HTML代码返回给浏览器,浏览器再进行页面的渲染、显示。
一种很直观的方法是在处理请求的方法中,直接返回HTML代码,但是这样做的问题在于——一个复杂的页面HTML代码往往也非常复杂,并且嵌入在Java代码中十分不利于维护。更好的做法是将页面的HTML代码写在模板文件中(此处使用Velocity模板语言),渲染后再返回给用户。
模板引擎
模板引擎是为了使用户界面与业务数据分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎就会生成一个标准的HTML文档。
在MVC模式中,模板引擎的工作原理基本一样,比如说以freemarker为例,如下图:
可概括为一个公式:
模板 + 数据模型 = 输出
@RequestMapping(value = {"/vm"})
public String news(Model model){
model.addAttribute("value1", "vv1");
List<String> colors = Arrays.asList(new String[]{"RED", "GREEN", "BLUE"});
Map<String, String> map = new HashMap<String, String>();
for (int i = 0; i < 4; ++i) {
map.put(String.valueOf(i), String.valueOf(i * i));
}
model.addAttribute("colors", colors);
model.addAttribute("map", map);
model.addAttribute("user",new User("Megustas"));//传递自定义对象
return "news";//news.vm
}
Model
后端与渲染之间铰链的一个数据模型Model,通过Model向前台视图传递参数,Model中存入的数据在Velocity中直接使用
return "news";
返回值不再是ResponseBody,
在上述例子中,返回值”news”并非直接将字符串返回给浏览器,而是寻找名字为news的模板进行渲染,news.vm文件存放于resources/templates目录下
Velocity简单语法
1.标识Velocity的脚本语句 #
用来标识Velocity的脚本语句,包括#set、#if 、#else、#end、#foreach、#end、#iinclude、#parse、#macro等
2. 用来标识一个变量$
3. 把不存在的变量显示为空白!
4. 注释##
5. 常用指令
常用指令 | 说明 | 例子 |
---|---|---|
#include | include指令用于引入其他的文件,引入的文件将会被当做静态文件来处理 | #include("test1.txt") |
#parse | 引入的文件通常是动态文件,并且parse指令中允许嵌套 | |
#set | 赋值指令:可以用于创建一个新的实例,或者更新一个已经存在的实例。set指令中也支持基本的数据运算 | #set($username="yxd") |
#if #else #end | 条件判断指令 | |
#foreach #end | 循环指令 | #foreach ($item in [1..5]).. #end |
#macro #end | 提供了一个构建模板代码复用的机制,类似于Java中的函数 | #macro ( sayHi $username)Hello $username #end |
request/response
一次网页请求中,通过request获取它的所有数据
@RequestMapping(value = {"/request"})
@ResponseBody
public String request(HttpServletRequest request,
HttpServletResponse response,
HttpSession session){
//获取Http请求的头文件
StringBuilder sb = new StringBuilder();
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()){
String name = headerNames.nextElement();
sb.append(name + ":" + request.getHeader(name) + "<br>");
}
for(Cookie cookie : request.getCookies()){
sb.append("Cookie");
sb.append(cookie.getName());
sb.append(":");
sb.append(cookie.getValue());
sb.append("<br>");
}
sb.append("getMethod:" + request.getMethod()+ "<br>");
sb.append("getPathInfo:" + request.getPathInfo()+ "<br>");
sb.append("getQueryString:" + request.getQueryString()+ "<br>");
sb.append("getRequestURI" + request.getRequestURI());
return sb.toString();
}
Enumeration
Enumeration接口作用和Iterator类似,提供了遍历元素的功能,其中只定义了两种方法:
方法 | 作用 |
---|---|
boolean hasMoreElements() | 测试Enumeration对象中是否包含元素,如果有返回true,则表示至少包含一个元素 |
Object nextElement() | 如果Enumeration对象还有元素,返回对象的下一个元素,否则抛出NoSuchElementException |
request.getQueryString():
request.getQueryString()就是获取查询字符串
即请求?后面的就是QueryString,例如
127.0.0.1:8080/request?type=2&&k=xx
- QueryString为type=2&&k=xx
- RequestURI为/request
response
可以通过HttpServletResponse response将更多数据写回
@RequestMapping(value = {"/response"})
@ResponseBody
public String response(
@CookieValue(value="nowcoderid",defaultValue = "a") String nowcoderId,
@RequestParam(value = "key", defaultValue = "key") String key,
@RequestParam(value = "value", defaultValue = "value") String value,
HttpServletResponse response){
response.addCookie(new Cookie(key,value));
response.addHeader(key,value);
return "NowCoderId From Cookie:" + nowcoderId;
}
访问:127.0.0.1:8080/response
:
输出:NowCoderId From Cookie:a(此时为默认值)
访问127.0.0.1:8080/response?key=nowcoderid&value=22
:
输出:NowCoderId From Cookie:22
重定向
redirect
前缀,跳到首页,默认是302跳转
从一个页面跳到另一个页面,所有的访问都是同一个HttpSession,可以在redirect中添加session的一些特性,返回到首页的时候,把session的信息读取出来,显示在首页。用户体验较好。
301和302的区别
301:永久转移
如果是301,会把信息存入浏览器,下次浏览器访问网址,会直接定位到另一个地方。
302:临时转移
统一的异常处理
@ExceptionHandler
- 统一处理某一类异常,从而能够减少代码重复率和复杂度
- @ExceptionHandler只会是在当前的Controller里面起作用
@RequestMapping("/admin")
@ResponseBody
public String admin(@RequestParam(value = "key",required = false) String key){
if("admin".equals(key)){
return "hello admin";
}
throw new IllegalArgumentException("Key 错误");
}
//好处是可以用统一的页面处理问题
@ExceptionHandler()
@ResponseBody
public String error(Exception e){
return "error:" + e.getMessage();
}
@ControllerAdvice
如果想所有的Controller统一处理异常的话,可以用@ControllerAdvice来创建一个专门处理的类,这样所有控制器的异常可以在一个地方进行处理
@ResponseStatus
- 可以将某种异常映射为HTTP状态码
- 不要轻易把@ResponseStatus修饰目标方法,因为无论它执行方法过程中有没有异常产生,用户都会得到异常的界面,而目标方法正常执行