<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>com.springbootgroupId>
<artifactId>springboot01artifactId>
<version>0.0.1-SNAPSHOTversion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.0.BUILD-SNAPSHOTversion>
parent>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<optional>trueoptional>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
<repositories>
<repository>
<id>spring-snapshotsid>
<url>https://repo.spring.io/snapshoturl>
<snapshots><enabled>trueenabled>snapshots>
repository>
<repository>
<id>spring-milestonesid>
<url>https://repo.spring.io/milestoneurl>
repository>
repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-snapshotsid>
<url>https://repo.spring.io/snapshoturl>
pluginRepository>
<pluginRepository>
<id>spring-milestonesid>
<url>https://repo.spring.io/milestoneurl>
pluginRepository>
pluginRepositories>
project>
创建启动类
@SpringBootApplication
public class SpringBootApp {
public static void main(String[] args) {
SpringApplication.run(SpringBootApp.class, args);
}
}
创建Controller类,Controller类要和启动类同级或者在启动类的下级
@Controller
public class HelloController {
@RequestMapping("/hello.html") //启动启动类在浏览器中即可访问
public @ResponseBody String hello() {
return "hello spring boot" + "! 你好,Spring Boot!";
}
}
控制台Log打印:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.1.0.BUILD-SNAPSHOT)
2018-10-05 18:34:14.845 INFO 2980 --- [ restartedMain] com.springboot.SpringBootApp : Starting SpringBootApp on xiefeideMacBook-Pro.local with PID 2980 (/Users/xiefei/Documents/workspace/eclipse/springboot01/target/classes started by xiefei in /Users/xiefei/Documents/workspace/eclipse/springboot01)
2018-10-05 18:34:14.847 INFO 2980 --- [ restartedMain] com.springboot.SpringBootApp : No active profile set, falling back to default profiles: default
2018-10-05 18:34:14.878 INFO 2980 --- [ restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : Devtools property defaults active! Set 'spring.devtools.add-properties' to 'false' to disable
2018-10-05 18:34:14.878 INFO 2980 --- [ restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : For additional web related logging consider setting the 'logging.level.web' property to 'DEBUG'
2018-10-05 18:34:15.591 INFO 2980 --- [ restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2018-10-05 18:34:15.602 INFO 2980 --- [ restartedMain] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2018-10-05 18:34:15.602 INFO 2980 --- [ restartedMain] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/9.0.12
2018-10-05 18:34:15.605 INFO 2980 --- [ restartedMain] o.a.catalina.core.AprLifecycleListener : The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: [/Users/xiefei/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:.]
2018-10-05 18:34:15.645 INFO 2980 --- [ restartedMain] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2018-10-05 18:34:15.645 INFO 2980 --- [ restartedMain] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 767 ms
2018-10-05 18:34:15.661 INFO 2980 --- [ restartedMain] o.s.b.w.servlet.ServletRegistrationBean : Servlet dispatcherServlet mapped to [/]
2018-10-05 18:34:15.663 INFO 2980 --- [ restartedMain] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'characterEncodingFilter' to: [/*]
2018-10-05 18:34:15.663 INFO 2980 --- [ restartedMain] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2018-10-05 18:34:15.663 INFO 2980 --- [ restartedMain] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'formContentFilter' to: [/*]
2018-10-05 18:34:15.664 INFO 2980 --- [ restartedMain] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'requestContextFilter' to: [/*]
2018-10-05 18:34:15.692 INFO 2980 --- [ restartedMain] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2018-10-05 18:34:15.907 INFO 2980 --- [ restartedMain] o.s.b.d.a.OptionalLiveReloadServer : LiveReload server is running on port 35729
2018-10-05 18:34:15.948 INFO 2980 --- [ restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' # 在8080端口启动
2018-10-05 18:34:15.951 INFO 2980 --- [ restartedMain] com.springboot.SpringBootApp : Started SpringBootApp in 1.297 seconds (JVM running for 1.993)
在浏览器中访问:
地址:http://localhost:8080/hello.html
结果:
hello spring boot! 你好,Spring Boot!
goupId :项目的所属组,一般为公司或组织的名称。例如:org.springframework。
artifactId: 项目名,项目的唯一标识。例如:spring-boot-starter-web。由goupId和artifactId可以唯一定位到项目,二者也被称为项目坐标。
packaging:项目的打包类型,可分为jar,war,pom等。
version:是项目的版本。
SHAPSHOT:开发中的版本
RELEASE:正式发布版本
M1、M2:里程碑,即将发布的版本
RC:发布候选 release candidate
GA:基本可用版本 general availability
modelVersion:Maven的版本。
dependencies:依赖,包含多个dependency,每一个dependency就是一个具体的依赖。
dependency:声明项目中的依赖。
scope:依赖的类库和项目的关系。默认为compile。
compile:编译打包时会带上此依赖
test:只有在单元测试时才会带上此依赖
provided:只在编译阶段使用此类库
runtime:编译和打包时都不需要此类库,只在运行时需要
build:可以添加maven插件
$ mvn -B archetype:generate \
> -DarchetypeGroupID=org.apache.maven.archetypes\
>
xiefeideMacBook-Pro:maven xiefei$ mvn -B archetype:generate \
> -DarchetypeGroupId=org.apache.maven.archetypes \
> -DgroupId=com.mymaven \
> -DartifactId=my-app
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 29.175 s
[INFO] Finished at: 2018-10-05T19:21:19+08:00
[INFO] ------------------------------------------------------------------------
* 当出现如上信息的时候表示,构建成功!
*以下命令均在工程pom.xml所在目录中执行
@Controller、@Service、@Repository、@Component、@Configuration、@Bean注解都能声明一个Spring管理的类。但是每一个注解都有特殊含义。
@PostConstruct:ioc容器创建bean之后执行
@PreDestory:bean被销毁之后执行
// 通过实现InitializingBean,DisposableBean可以对bean实现初始化和销毁操作
@Component
public class ExampleBean implements InitializingBean, DisposableBean{
@PostConstruct // 在构造器执行之后会执行的方法
public void postConstruct() {
System.out.println("初始化之后执行....");
}
@PreDestroy // 在销毁之前会执行的方法
public void preDestroy() {
System.out.println("销毁之前执行....");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean");
System.out.println("执行属性设置之前...");
}
@Override
public void destroy() throws Exception {
System.out.println("DisposableBean");
System.out.println("销毁...");
}
}
通过@Qualifier()指定bean的名字
@Component(value = "exBean") // 也可以使用value属性指定bean的id
//@Qualifier("exBean") //可以通过@Qualifier指定当前bean的id
public class ExampleBean implements InitializingBean, DisposableBean{
@PostConstruct
public void postConstruct() {
System.out.println("初始化之后执行....");
}
}
@Autowired // 注入bean,默认以类型注入
@Qualifier("exBean") // 指定通过id注入
private ExampleBean exampleBean;
在spring boot 中使用aop功能,导入依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-aopartifactId>
<optional>trueoptional>
dependency>
配置AOP
@Component //加入Spring 容器中
@Aspect // 声明这是一个切面类
public class AOPConfig {
// @Auound通知
// @within(org.springframework.stereotype.Controller)表示是作用在Controller注解所在类的方法上的
@Around("@within(org.springframework.stereotype.Controller)")
public Object exampleAop(ProceedingJoinPoint joinPoint) throws Throwable {
Object proceed = joinPoint.proceed();
System.out.println("aop:return -> " + proceed);
return proceed;
}
}
@Controller可以标注在一个类上,表示这个类是用来处理请求的类。
@RequestMapping标注在类上可以限定当前Controller处理的请求路径。
@RequestMapping标注在方法上,可以指定当前方法用来处理指定的请求。
@RequestMapping注解的属性
value:请求URL的路径
method:请求方法
consumes:允许的媒体类型
produces:响应的媒体类型
params:请求的参数
headers:请求头的值
例如:
@Controller
public class HelloController {
@Autowired
@Qualifier("exBean")
private ExampleBean exampleBean;
// 这里规定请求路径中为/**/hello.html,请求参数必须包含token=123才可以访问(**表示可以为n层路径)
@RequestMapping(value = "/**/hello.html", params = "token=123")
public @ResponseBody String hello() {
exampleBean.test();
return "hello spring boot" + "! 你好,Spring Boot!";
}
}
从属性文件中获取匹配的路径:/${ab}/hello.html
${ab}表示读取属性文件中ab的值
@RequestMapping(value = “/{ab}/hello.html”):
{}可以匹配单个路径,{}中的值可以通过@PathVariable(“对应的名字”)取出
支持***Ant***风格的表达式:
通配符 | 说明 |
---|---|
? | 匹配任何单字符 |
* | 匹配0或者任意数量的字符 |
** | 匹配0或者更多的目录 |
例如:/ab/h?ello.html,匹配单个字符,?号处必须由一个字符代替,也不可省略。
/ab/h*ello.html,*号处可以有0个或多个字符。
/ab/**/hello.htm,**处可以有0层或多层路径。
GET:获取资源
POST:新增资源
PUT:对资源的全部更新
PATCH:对资源的局部更新
DELETE:删除一个资源
HEAD:同GET,但不返回资源
除了HEAD请求,都有相应的注解与这对应。
如:@GetMapping、@PostMapping、@PutMapping、@PutMapping、@PatchMapping、@DeleteMapping
HEAD请求,或其他请求也可以采用如下用法:
// 可以通过method指定对应的请求
@RequestMapping(value = "/head", method = RequestMethod.HEAD)
@ResponseBody
public void testHead() {
System.out.println("**");
}
consumes指定请求关Content-Type的值,如果请求中Content-Type的值不匹配将会报错。
// 这个请求,对应的Content-Type的值一定是applicaion/json
@RequestMapping(value = "/consumer", consumes = "applicaion/json")
produces属性对应的是请求头中的Accept的值,只有请求中的Accept字段的值与注解设置的值一致才可以被访问
// 只有当这个对应的请求的请求头中的produces字段的值设置为application/json才能访问
@RequestMapping(value = "/produces", produces = "application/json")
params指定请求参数中必须要带的请求参数,否则不能访问此方法。
// params接收一个数组,参数和值之间不能有空格
@RequestMapping(value = "/params", params = {"action=update"})
header指定请求头中必须有的参数
// headers接收一个数组,指定请求的请求头中要有action且值为header
@RequestMapping(value = "/params", headers = {"action=header"})
在请求的url路径中使用{}表示路径请求参数,对应的使用@PathVariable指定接收路径请求参数的值。
// {}指定路径变量的变量名
@RequestMapping(value = "/{ab}/hello.html")
// @PathVariable指定对应的参数是用来接收路径参数的,参数名要与路径变量名相同。
// 也可以指定@PathVariable的value属性值与路径变量名对应,如@PathVariable("ab"),参数名可以不与路径变量名相同
public @ResponseBody String hello(@PathVariable String ab) {
// ...
}
指定请求参数与方法参数的关系。
@RequestMapping("/test")
@ResponseBody
// 方法中的参数的值会根据请求参数的值来写入,如果请求参数没有对应的值,就为null
public String test(String name, Integer age) {
return "success" + " - " + name + " - " + age;
}
方法参数也可以用@RequestParam来指定
@RequestMapping("/test")
@ResponseBody
// @RequestParam指定一个名字与请求参数的名字进行绑定
public String test(@RequestParam("name")String name,@RequestParam("age") Integer age) {
return "success" + " - " + name + " - " + age;
}
@RequestParam的属性
value 接收一个字符串作为参数名与对应的请求参数绑定,对应的请求参数的值将绑定到注解标注的方法参数上
name 接收一个字符串,功能同value一样
default 接收一个字符串类型,如果请求参数中没有这个参数,default将为这个参数赋值一个默认值
required 布尔类型,如果为true请求中就必须有这个参数,如果为false请求参数中可以不用有这个参数
@RequestMapping(path = "json")
@ResponseBody
public String json(@RequestBody String json) {
return json;
}
MultipartFile用于文件上传
@RequestMapping("upload")
@ResponseBody
public boolean upload(MultipartFile file) throws IllegalStateException, IOException {
}
MultipartFile常用方法
getOriginalFilename:获取上传的文件的名字
getBytes:获取上传文件的内容
getInputStream:获取文件流
isEmpty:判断文件是否为空
getSize:获取文件的大小
transferTo:保存文件
如果上传多个文件,可以用MultiparatFile的数组来接收上传的文件 MultipartFile[]
@ModelAttribute标注的类会在所有处理请求的方法执行之前执行,如果有多个@ModelAttribute,会根据定义的先后顺序依次执行。方法中可以获取到请求参数!
@Controller
public class TestModelAttributeController {
@RequestMapping("/model")
@ResponseBody
public String model(String name, String age) {
return name + " _ " + age;
}
@ModelAttribute // 执行在所有请求方法之前
public void modelAttribute(String name, String age) {
System.out.println(2);
System.out.println(name + " > " + age);
}
public void modelAttribute1(String name, String age) {
System.out.println(name + " " + age);
System.out.println(1);
}
}
@ModelAttribute标注的方法如果有返回值,会自动添加到Model中
@Controller
public class TestModelAttributeController {
@RequestMapping("/model")
@ResponseBody
public String model(Model model) {
System.out.println(model);
return model.containsAttribute("hello") + "";
}
@ModelAttribute
public String modelAttribute1(String name, String age) {
System.out.println(name + " " + age);
// 返回值将存在Model中,key为String值为hello,即key为对应的类名,value为对应的对象
return "hello";
}
// 此时model中的值为{string=hello}
}
@InitBinder
public void initBinder(WebDataBinder webDataBinder) {
// 自定义绑定参数,当用户传入的date类型为yyyy-MM-dd时才能绑定到date类型对应的参数中
// 前端传入的是字符串,字符串要满足这样的格式才能转换成对应的Date类型
webDataBinder.addCustomFormatter(new DateFormatter("yyyy-MM-dd"));
}
@RequestMapping("/date")
@ResponseBody
// 这里的date参数的值的格式必须为 yyyy-MM-dd才可以绑定到date中去
public String date(String name, String age, Date date) {
System.out.println(name + " " + age + " _ " + date);
return "hello";
}
JSR303是Java标准的验证框架,实现产品有hibernate validator。
@Null 验证对象为空,只有为空才能通过验证
@NotNull 验证对象不为空,只有不为空才能通过验证
@NotEmpty 对象不为null,集合不能为空---(字符串用NotBlank验证)
@NotBlank 验证字符串不为空,不为null,不为空串(不可以有空格)
@Size(min= , max= ) 验证字符串和集合的长度,在min和max之间
@Length 验证字符串的长度
@Min 验证数字的大小,最小值
@Max 验证数字的大小,最大值
@Digits(integer = 整数位数, fraction = 小数位数) 验证数字整数位为0-指定的位数,小数位在0-指定位数
@Range(min = , max = ) 验证数字是否在min和max之间
@Enail 验证是否是邮箱
@Pattern 验证String对象是否符合指定的正则表达式
同一个对象,不同的情况可能有不同的校验。例如,当保存一个对象时,对象的id校验为空,当更新一个对象时,对象的id校验不为空。
实现方式,可以通过group组实现。
public class User {
public interface Add{}; // 定义一个类(接口),指定这是一个组,group
public interface Update{}; // 定义一个类,为一个grop
// 两个不同的校验就在两不同的组中去了
@Null(groups = Add.class) // 对应的group属性,指向一个类,即属于这个组
@NotNull(groups = Update.class// 对应的group属性,指向一个类,即属于这个组
private Integer id;
private String name;
@Range(min = 1, max = 100)
private Integer age;
// ...
}
在Controller中校验
@RequestMapping("/user/add")
@ResponseBody
public User addUser(@Validated(value = {User.Add.class}) User user, BindingResult br) {
if (br.hasErrors()) {
br.getAllErrors().forEach(x -> {
System.out.println(x);
});
}
return user;
}
@RequestMapping("/user/add")
@ResponseBody
// @Validated开启校验,使校验注解生效,其value属性指定使用哪个分组的校验
public User updateUser(@Validated(value = {User.Add.class}) User user, BindingResult br) {
// BindingResult中保存了校验结果
if (br.hasErrors()) {
br.getAllErrors().forEach(x -> {
System.out.println(x);
});
}
return user;
}
编写校验注解:
@Documented // 在javaDoc中是否显示
@Target({ElementType.FIELD}) // 指定该注解标注的位置
//注解保留时间,RUNTIME运行时,CLASS编译时,RESOURCE源码
@Retention(RetentionPolicy.RUNTIME)
// 使用的校验类
@Constraint(validatedBy = IsPhoneValidator.class)
public @interface IsPhone {
boolean require() default true; // 是否是必须
String message() default "电话号码不符合规则"; //提示信息
Class<?>[] groups() default {}; // 指定组
Class<? extends Payload>[] payload() default {}; //验证的有效负荷
}
编写校验逻辑:
public class IsPhoneValidator implements ConstraintValidator<IsPhone, String> {
private boolean require = false;
@Override
public void initialize(IsPhone constraintAnnotation) {
// 初始化,获取对应的注解的信息 constraintAnnotation
require = constraintAnnotation.require();
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
// 编写校验规则
if (require && value == null) {
return false;
} else {
String regex = "123456";
return Pattern.matches(regex, value);
}
}
}
可以通过实现WebMvcConfigurer实现全局配置。
@Configuration // 标记这是一个配置类
public class MyWebMvcConfigurer implements WebMvcConfigurer {
@Override // 拦截器配置
public void addInterceptors(InterceptorRegistry registry) {
WebMvcConfigurer.super.addInterceptors(registry);
}
@Override // 跨域访问配置
public void addCorsMappings(CorsRegistry registry) {
WebMvcConfigurer.super.addCorsMappings(registry);
}
// ...
}
自定义拦截器:实现HandlerIntercptor
public class MyInterceptor implements HandlerInterceptor {
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("拦截器执行完成...");
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("拦截器处理之后...");
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("拦截器逻辑...");
return true;
}
}
拦截器执行顺序:preHandle -> postHandle -> afterCompletion
在调用Controller方法之前调用preHandle方法,如果这个方法返回true,继续调用controller方法。如果返回的是false,后续的所有方法都不会被调用了
在Controller方法调用结束之后立即执行postHandle方法。
在视图渲染完成之后,立即调用afterCompletion方法。
在WebMvcConfigurer实现类中配置拦截器:
@Configuration // 标记这是一个配置类
public class MyWebMvcConfigurer implements WebMvcConfigurer {
@Override // 拦截器配置
public void addInterceptors(InterceptorRegistry registry) {
// 配置拦截url和过滤(不拦截)的url
registry.addInterceptor(new MyInterceptor()).addPathPatterns("/user/**")
.excludePathPatterns("/user/mine");
}
}
@Configuration // 标记这是一个配置类
public class MyWebMvcConfigurer implements WebMvcConfigurer {
@Override // 拦截器配置
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor()).addPathPatterns("/user/**").excludePathPatterns("/user/mine");
}
@Override // 跨域访问配置
public void addCorsMappings(CorsRegistry registry) {
// 配置允许/api/下的所有请求不存在跨域(允许跨域访问)
registry.addMapping("/api/**")
// 只允许 http://domain.com跨域访问
.allowedOrigins("http://domain.com")
// 只允许 get和post请求能跨域访问
.allowedMethods("GET", "POST");
// 所以这个配置:只允许http://domain.com域名对/api/下所有接口的get、post请求能跨域访问
}
// ...
}
对于时间(Date类型)的传参,spring没做任何处理,需要自己指定。(可以使用@InitBinder)。
@Override
public void addFormatters(FormatterRegistry registry) {
// 配置Date类型接收参数的格式,这里前端传参的格式必须为yyyy/MM/dd,否则无法入参
registry.addFormatter(new DateFormatter("yyyy/MM/dd"));
// 前端传入的字符串格式要满足yyyy/MM/dd才能把参数写入到指定的时间参数中
}
这种方式的配置全局有效,@InitBinder只针对当前Controller
@Configuration // 标记这是一个配置类
public class MyWebMvcConfigurer implements WebMvcConfigurer {
@Override // 拦截器配置
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor()).addPathPatterns("/user/**").excludePathPatterns("/user/mine");
}
@Override // 跨域访问配置
public void addCorsMappings(CorsRegistry registry) {
// 配置
registry.addMapping("/api/**").allowedOrigins("http://domain.com")
.allowedMethods("GET", "POST");
}
@Override
public void addFormatters(FormatterRegistry registry) {
// 配置Date类型接收参数的格式,这里前端传参的格式必须为yyyy-MM-dd,否则无法入参
registry.addFormatter(new DateFormatter("yyyy/MM/dd"));
}
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// 把index请求跳转到pages/index对象的视图
registry.addViewController("/index").setViewName("/pages/index");
// 把baidu请求重定向到http://www.baidu.com
registry.addRedirectViewController("/baidu", "http://www.baidu.com");
}
// ...
}
添加Freemarker依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-freemarkerartifactId>
dependency>
请求,返回视图:
@GetMapping("/show")
public ModelAndView show() {
ModelAndView mv = new ModelAndView();
mv.addObject("user", "haha");
mv.setViewName("/page/test");
return mv;
}
test.ftl,放在resources/templates/page下
<html>
<head>
<meta charset="UTF-8">
<title>Insert title heretitle>
head>
<body>
${user}
body>
html>
接口返回使用jackson序列化。自定义返回值序列化成json。
@Configuration
public class JacksonConfig {
@Bean // 返回ObjectMapper
public ObjectMapper getObjectMapper() {
ObjectMapper om = new ObjectMapper();
// 设置Date类型序列化格式
om.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm"));
return om;
}
}
接口返回:
@ResponseBody
@RequestMapping("/getDate")
public Date getDate() {
return new Date(); // 接口返回的格式:"2018-10-07 11:36"
}
重定向实现的几种方式:
@RequestMapping("redirect1")
public String redirect1() {
// 返回字符串,字符串前缀加 redirect:
return "redirect:/page/test";
}
@RequestMapping("redirect2")
public ModelAndView redirect2() {
ModelAndView mv = new ModelAndView();
mv.setViewName("redirect:/page/test");
// 返回ModelAndView,在setViewName中加前缀redirect:
return mv;
}
@RequestMapping("redirect3")
public RedirectView redirect3() {
// 直接返回RedirectView,构造器中写对应的重定向地址
RedirectView rv = new RedirectView("/page/test");
return rv;
}
请求转发:
@RequestMapping("foward1")
public String foward1() {
return "foward:/page/test";
}
@RequestMapping("foward2")
public ModelAndView foward2() {
ModelAndView mv = new ModelAndView();
mv.setViewName("foward:/page/test");
return mv;
}
重定向告知浏览器重新请求一个新的地址。请求转发,会把当前请求转发到另一个请求,另一个请求返回的结果才是最后的结果!
@Controller // extends继成AbstracErrorController
public class ErrorController extends AbstractErrorController {
public ErrorController(ErrorAttributes errorAttributes) {
super(errorAttributes);
}
@Override
public String getErrorPath() {
return null;
}
@RequestMapping("/error")
public String err(HttpServletRequest request) {
// getErrorAttributes可以获取异常信息
Map<String, Object> ea = getErrorAttributes(request, true);
Object object = ea.get("status"); // 状态码
return "/page/err";
}
}
getErrorAttribute可以获取异常信息,得到一个Map:
timestamp:错误发生的时间
status:http响应码
error:错误消息
message:详细错误消息
exception:异常信息
path:请求的url
errors:@Validated校验错误信息
如何配置校验信息的通一处理?…
获取真正的异常信息:
protected Throwable getCause(HttpServletRequest request) {
Throwable error = (Throwable) request.getAttribute("javax.servlet.error.exception");
System.out.println("错误信息:" + error);
return error;
}
Controller处理请求,请求业务通过Service处理逻辑。在Service的类或方法上使用@Transactional注解可以开启事务。
@Service
@Transactional // 开启事务,该类中的所有方法都会使用到事务
public class UserService {
public User getUser(Integer id) {
User user = new User();
user.setId(id);
return user;
}
// 开启一个新的事务,不使用之前的事务
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void addUser(User user) {
System.out.println(user);
}
}
// 通过ObjectMapper操作json,ObjectMapper已经被管理在Spring容器中了
@Autowired
private ObjectMapper om;
@RequestMapping("/json")
@ResponseBody
public String json() throws IOException {
// 这是一个json的字符串
String json = "{\n" +
" \"name\":\"小明\",\n" +
" \"age\":18,\n" +
" \"birth\":\"2010-10-01\"\n" +
" }";
// 读取json到字符串中
JsonNode node = om.readTree(json);
// 从node中通过key获取对应的value值 asText()表示把读取到的值以String显示
String str = node.get("name").asText();
// node.get("key"),get方法通过指定key获取对应的值
// asXxx() 方法,把指定的值以对应的数据格式输出
int i = node.get("age").asInt();
return "success";
}
@Autowired
private ObjectMapper om;
@RequestMapping("/json")
@ResponseBody
public String json() throws IOException {
// 这是一个json的字符串
String json = "{\n" +
" \"name\":\"小明\",\n" +
" \"age\":18,\n" +
" \"birth\":\"2010-10-01\"\n" +
" }";
// 读取json,反序列化成一个对象
User user = om.readValue(json, User.class);
return "success";
}
@Autowired
private ObjectMapper om;
@RequestMapping("/json")
@ResponseBody
public String json() throws IOException {
User u = new User();
u.setId(1);
u.setName("Tom");
u.setPhone("1234567");
// 把对象序列化成一个字符串
String json = om.writeValueAsString(u);
System.out.println(json);
return "success";
}
配置数据源:使用HikariCP
<dependency>
<groupId>com.zaxxergroupId>
<artifactId>HikariCPartifactId>
<version>3.1.0version>
dependency>
其他依赖:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jdbcartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
配置数据源相关属性
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/springboot?characterEncoding=utf8&useUnicode=true
username: root
password: mysql123
driver-class-name: com.mysql.jdbc.Driver
spring boot集成了HikariCP,添加了HikariCP依赖,配置了dataSource相关属性就可以自动配置了。
也可以手动配置HikariCP:
@Configuration
public class DataSourceConfig {
@Bean
@Qualifier("hikari")
// Environment可以获取设置环境相关配置,可以配置配置文件中的信息
public DataSource HikariCP(Environment env) {
HikariDataSource dataSource = new HikariDataSource();
dataSource.setUsername(env.getProperty("spring.datasource.username"));
dataSource.setPassword(env.getProperty("spring.datasource.password"));
dataSource.setJdbcUrl(env.getProperty("spring.datasource.url"));
dataSource.setDriverClassName(env.getProperty("spring.datasource.driver-class-name"));
return dataSource;
}
}
在SpringBoot中,配置好数据源后就可以使用JdbcTemplate了。
@Repository
public class UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public Integer getCount() {
String sql = "select count(*) from user";
Integer count = jdbcTemplate.queryForObject(sql, Integer.class);
return count;
}
public Integer getCountForDepartment(Integer departmentId) {
String sql = "select count(*) from user where department_id = ?";
Integer count = jdbcTemplate.queryForObject(sql, Integer.class, 1);
return count;
}
public User getUserById(Integer id) {
String sql = "select * from user where id = ?";
// 传入一个sql,和RowMapper,RowMapper中获取查询结果
User user = jdbcTemplate.queryForObject(sql, new RowMapper<User>() {
@Override
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
System.out.println(rs.getRow());
if (rs.getRow() == 1) {
User user = new User();
user.setId(rs.getInt("id"));
user.setName(rs.getString("name"));
user.setDepartmentId(rs.getInt("department_id"));
user.setCreateTime(rs.getDate("create_time"));
return user;
}
return null;
}
// sql传的参数
}, id);
return user;
}
}
自定义RowMapper
public class UserRowMapper implements RowMapper<User>{
@Override
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
System.out.println(rowNum);
User user = new User();
int id = rs.getInt("id");
user.setId(id);
String name = rs.getString("name");
user.setName(name);
int departmentId = rs.getInt("department_id");
user.setDepartmentId(departmentId);
Date createTime = rs.getDate("create_time");
user.setCreateTime(createTime);
return user;
}
}
查询多个结果用query
public List<User> getUsers() {
String sql = "select * from user";
// query可以获取多个结果
List<User> users = jdbcTemplate.query(sql, new UserRowMapper());
return users;
}
查询结果返回Map(不推存使用,结果数据类型不明确)
public Map<String, Object> getUserMap() {
String sql = "select * from user";
Map<String, Object> map = jdbcTemplate.queryForMap(sql);
return map;
}
使用SpringBoot JPA首先要导入SpringData jpa的 starter
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-jpaartifactId>
dependency>
SpringData JPA基于对象关系模型
@Entity // 标记这是一个对象关系实体
public class User {
@Id // 标记对应的字段 是主键
// 主键生成策略
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column // 标记这是一列
private String name;
// 标记列 并指定列的名称
@Column(name = "create_time")
private Date createTime;
// 多对一映射
@ManyToOne
// 指定当前表要映射的段,并指定一个字段名(外键)
@JoinColumn(name = "department_id")
Department department;
// getter/setter
}
@Entity // 标记实体对象
public class Department {
@Id // 标记主键
// 标记主键生成策略
@GeneratedValue(strategy =GenerationType.IDENTITY)
private Integer id;
// 标记列
@Column
private String name;
// 一对多映射,指定维护关联关系的字段 是user中department属性对应的字段
@OneToMany(mappedBy = "department")
private Set<User> users = new HashSet<>();
// getter/setter
}
想要对数据库操作,还要实现对应的Repository接口(Repository)。
public interface UserDao extends Repository<User, Integer><User, Integer> {
}
也可以通过注解实现:
@RepositoryDefinition(domainClass = User.class, idClass = Integer.class)
public interface UserDao {
}
这两种方式都要通过指定要操作的实体类对象和该对象对应的主键类型。
SpringData Jpa 还提供了其他Repository,并且提供了一些实现。
CrudRepository:提供了基体的增删改查操作。
public interface UserDao extends CrudRepository<User, Integer> {
}
PagingAndSorting:分页和排序相关
public interface UserDao extends PagingAndSortingRepository<User, Integer> {
}
几个Repository接口的关系:
JpaRepository -> PagingAndSortingRepository -> CrudRepository -> Repository
关于save()方法:如果对象实体存在就更新,不存在就添加
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
List<T> findAll(); // 返回所有实体
List<T> findAll(Sort sort); // 返回所有实体并排序
List<T> findAllById(Iterable<ID> ids); // 根据id返回所有的实体
<S extends T> List<S> saveAll(Iterable<S> entities); // 保存/更新所有
void flush(); // 刷新,执行sql
<S extends T> S saveAndFlush(S entity); // 执行sql
void deleteInBatch(Iterable<T> entities); // 批量删除
void deleteAllInBatch(); // 全部删除
T getOne(ID id); // 根据id获取
@Override
<S extends T> List<S> findAll(Example<S> example);
@Override
<S extends T> List<S> findAll(Example<S> example, Sort sort);
}
@Autowired
private UserDao userDao;
public List<User> findAll() {
// 指定排序规则
Sort sort = new Sort(Direction.DESC, "id");
List<User> findAll = userDao.findAll(sort);
return findAll;
}
public Page page() {
// of 方法指定两个参数,分页当前页码,和每页数据条数,页码从0开始
PageRequest pageable = PageRequest.of(1, 1);
// 分页需要传入一个Pageable的实例,PageRequest是Pageable的实现类
Page<User> findAll = userDao.findAll(pageable);
return findAll;
}
@GetMapping("/page")
public Page getCar() {
// 添加一次排序
Sort sort = new Sort(Direction.DESC, "produce");
// 添加第二次排序
sort.and(new Sort(Direction.DESC, "id"));
// 分页并排序
PageRequest pageRequest = PageRequest.of(0, 5, sort);
return carDao.findAll(pageRequest);
}
// 支持原生sql和命名参数,也支持jpql
@Query(value = "select * from car where id > :id", nativeQuery = true)
public List<Car> getCar(int id);
// 结果可以封装成一个Object数组
@Query("select c.name, c.id from Car c where c.id > 1")
public List<Object[]> getObj();
@Query("select c.name, c.id from Car c where c.id > 1")
public Page<Car> getObj(Pageable pr); // 传入一个Pageable参数
@Modifying
@Query("update Car c set c.name = ?1 where id = ?2")
public int update(String name, int id);
public List<Car> getExample() {
Car car = new Car();
// 设置查询对象属性
car.setId(1);
Example<Car> example = Example.of(car);
return carDao.findAll(example);
}
上面这种情况只能固定查询某个特定的值。
public List<Car> getExample() {
Car car = new Car();
car.setName("b"); // name设置为b
ExampleMatcher matcher = ExampleMatcher.matching()
// 匹配name 以b开头,忽略大小写
.withMatcher("name", GenericPropertyMatchers.startsWith().ignoreCase());
Example<Car> example = Example.of(car, matcher);
return carDao.findAll(example);
}
可以直接注入EntityManager
@Autowired
EntityManager entityManager;