-- 1 -- springboot
框架或工具:Lombok
项目地址:https://github.com/h837272998/next-springboot
一、Maven依赖
<?xml version="1.0" encoding="UTF-8"?>
<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.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.hjh.myspringboot</groupId>
<artifactId>myspringboot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>myspringboot</name>
<description>自学习springboot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
二、配置文件
springboot 提供了两种配置文件格式。分别是properties和yml。选择一种使用,结果都一样。这里选择properties。(为了看懂其他项目两者最好都掌握,喜欢用哪个看个人习惯)
server.port t端口
server.servlet.context-path 上下文路径
spring.datasource.* 数据库连接配置
server.port=8080
server.servlet.context-path=
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/spring?serverTimezone=GMT&useSSL=false
spring.datasource.username=root
spring.datasource.password=123456
三、RESTful API
常见的API
操作 | url | method |
---|---|---|
增加 | /user/add?name=xx | POST |
删除 | /user/delete?id=x | GET |
修改 | /user/update?id=x&name=xxx | POST |
获取 | /user/get?id=x | GET |
查询 | /user/list?name=xx | GET |
采用RESTful API
操作 | url | method |
---|---|---|
增加 | /user | POST |
删除 | /user/x | DELETE |
修改 | /user/x | PUT |
获取 | /user/x | GET |
查询 | /user?name=xx | GET |
RESTful架构:
遵循统一接口原则,统一接口包含了一组受限的预定义操作,不论什么资源,都是通过使用相同的接口进行资源访问
RESTful 是一种风格,不是强制标准。
四、编写RESTful和测试用例。
实现对用户表的增删改查
User.java
@Data
public class User {
@JsonView(View.Summary.class)
private long id;
@JsonView(View.Summary.class)
private String username;
@JsonView(View.SummaryWithDetail.class)
private String password;
@JsonView(View.SummaryWithDetail.class)
private Date createDate;
}
View.java
public class View {
public interface Summary{}
public interface SummaryWithDetail extends Summary{}
}
UserController.java
/**
* @Description:
* @Author: HJH
* @Date: 2019-08-16 21:32
*/
@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {
@PostMapping
public User add(@RequestBody User user){
log.info("增加用户;"+user.toString());
user.setId(1);
return user;
}
//正则匹配 id只能为数字
@DeleteMapping("/{id:\\d+}")
public void delete(@PathVariable long id){
log.info("删除用户id:"+id);
}
@PutMapping("/{id:\\d+}")
public User update(@RequestBody User user){
log.info("修改用户:"+user);
return user;
}
@GetMapping("/{id:\\d+}")
public User get(@PathVariable long id){
User user = new User();
user.setId(1);
user.setUsername("hjh");
user.setPassword("123");
return user;
}
@GetMapping
@JsonView(View.Summary.class)
public List<User> list(User user){
log.info("查询用户名:" + user);
ArrayList<User> users = new ArrayList<>();
users.add(new User());
users.add(new User());
users.add(new User());
return users;
}
}
测试用例
UserControllerTest.java
// 引入静态对象。简化
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
/**
* @Description:
* @Author: HJH
* @Date: 2019-08-16 21:38
*/
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserControllerTest {
@Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
@Before
public void before(){
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
@Test
public void whenAddSuccess() throws Exception {
String content = "{\"username\":\"hjh\",\"password\":null,\"createDate\":"+new Date().getTime()+"}";
String result = mockMvc.perform(post("/user")
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(content)) //请求
.andExpect(status().isOk()) //断言响应结果
.andExpect(jsonPath("$.id").value(1))
.andReturn().getResponse().getContentAsString();
log.info("增加结果;"+result);
}
@Test
public void whenUpdateSuccess() throws Exception {
String content = "{\"username\":\"hjh\",\"password\":null,\"createDate\":"+new Date().getTime()+"}";
String result = mockMvc.perform(put("/user/1")
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(content)) //请求
.andExpect(status().isOk()) //断言响应结果
.andReturn().getResponse().getContentAsString();
log.info("修改结果;"+result);
}
@Test
public void whenDeleteSuccess() throws Exception {
String result = mockMvc.perform(delete("/user/1")
.contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(status().isOk()) //断言响应结果
.andReturn().getResponse().getContentAsString();
}
@Test
public void whenGetSuccess() throws Exception {
String result = mockMvc.perform(get("/user/1")
.contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(status().isOk()) //响应结果
.andReturn().getResponse().getContentAsString();
log.info("获得结果;"+result);
}
@Test
public void whenListSuccess() throws Exception {
String result = mockMvc.perform(get("/user").param("username","hjh")
.contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(status().isOk()) //断言响应结果
.andReturn().getResponse().getContentAsString();
log.info("查询结果;"+result);
}
}
Jackson @JsonVIew
@JsonView可以过滤序列化对象的字段属性,使有选择的序列化对象
可以将View类理解为一组标识,Summary只是其中一种标识,其中DetailSummary继承了Summary
当使用@JsonView序列化User对象的时候,就只会序列化选择的属性,可以隐藏一些不想序列化的字段属性。
简单的说就是可以控制控制器层某个方法输出对象的属性。例如在查看用户详细信息的时候可以看到用户的所有信息。当查询所有用户时就不显示密码。
五、数据验证
数据验证:对前台传输的数据进行验证。
- 在entity定义对应成员变量的验证
@Past:过去的时间
@NotBlank 不能为空
- 需要验证的方法中添加@Valid注解
再通过BindingResult捕获错误
1. 常见的验证
2. 自定义消息
通过重写message
3. 自定义校检注解
定义注解MyConstraint
在注解里面的注解称为元注解,例如Target...
Target 作用目标:作用在METHOD(方法)和FIELD(字段)
Retention 保留位置:RetentionPolicy.RUNTIME : 注解保留在程序运行期间,此时可以通过反射获得定义在某个类上的所有注解。
MyConstraint.java
@Target({ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = MyConstraintValidate.class)
public @interface MyConstraint {
String message() default "测试验证";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
注解实现类
MyConstraintValidate.java
@Slf4j
public class MyConstraintValidate implements ConstraintValidator<MyConstraint, Object> {
@Override
public void initialize(MyConstraint constraintAnnotation) {
//初始化
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
log.info("MyConstraintValidate: ",value);
return false;
}
}
通过isValid 判断值是否通过验证
六、异常处理
1. springboot原生异常
覆盖默认的处理方式
- 自定义一个bean,实现ErrorController接口,那么默认的错误处理机制将不再生效。
- 自定义一个bean,继承BasicErrorController类,使用一部分现成的功能,自己也可以添加新的public方法,使用@RequestMapping及其produces属性指定新的地址映射。
- 自定义一个ErrorAttribute类型的bean,那么还是默认的两种响应方式,只不过改变了内容项而已。
- 继承AbstractErrorController
BasicErrorController.class
SpringBoot在页面 发生异常的时候会自动把请求转到/error,SpringBoot内置了一个BasicErrorController对异常进行统一的处理.
浏览器404
应用程序404(postMan)
分析
修改浏览器响应的404
在templates添加error/404
404.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>404</title>
</head>
<body>
<h1>访问的页面不存在</h1>
<p th:text="'状态码:'+${status}"></p>
<p th:text="'错误:'+${error}"></p>
<p th:text="'错误信息:'+${message}"></p>
<p th:text="'路径:'+${path}"></p>
<p th:text="'时间戳:'+${timestamp}"></p>
</body>
</html>
结果:
error/.. 如果需要模板渲染需要放在渲染路径。像thymeleaf,如果放在 classpath:static/errror/. 就不会被渲染,从而无法获得status等数据。
2. 自定义异常类和全局异常
- 自定义异常
UserNotExistException.java
public class UserNotExistException extends RuntimeException {
private long id;
public UserNotExistException(long id){
super("the user is not exist...");
this.id = id;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
}
进行测试
@GetMapping("/exceptionTest")
public void test(){
throw new UserNotExistException(1);
}
结果测试:
添加Controller全局异常捕获
ControllrExceptionhandler.java
/**
* @Description:全局控制器异常处理器
* @Author: HJH
* @Date: 2019-08-17 15:43
*/
@ControllerAdvice
public class ControllerExceptionHandler {
/**
* @Description:处理UserNotExistException,返回的是json对象
* @Author: HJH
* @Date: 2019-08-17 16:46
* @Param: [ex]
* @Return: java.util.Map<java.lang.String,java.lang.Object>
*/
@ExceptionHandler(UserNotExistException.class)
@ResponseBody
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public Map<String,Object> handleUserNotExistException(UserNotExistException ex){
Map<String,Object> result = new HashMap<>();
result.put("id",ex.getId());
result.put("message", ex.getMessage());
return result;
}
/**
* @Description:全局Exception处理。返回的是html页面
* @Author: HJH
* @Date: 2019-08-17 15:56
* @Param: [req, e]
* @Return: ModelAndView
*/
@ExceptionHandler(value = Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception ex) throws Exception {
ModelAndView mav = new ModelAndView();
mav.addObject("exception", ex);
mav.addObject("url", req.getRequestURL());
mav.setViewName("error/errorPage");
return mav;
}
}
测试控制器
@GetMapping("/exceptionTest")
public void test(@RequestParam String ex){
if("runtime".equals(ex)){
throw new RuntimeException("this is a RuntimeException");
}
throw new UserNotExistException(1);
}
测试结果
/user/exceptionTest?ex=user
使用浏览器和app(PostMan)
/user/exceptionTest?ex=runtime
使用浏览器和app(PostMan)
通过上面就可以对控制器层的异常进行捕获
弊端,只能返回页面或者json格式。在需要json格式异常时也只是返回页面
改进
@ExceptionHandler(value = Exception.class)
@ResponseBody
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public Object defaultErrorHandler(HttpServletRequest req, Exception ex) throws Exception {
if ("application/json".equals(req.getHeader("Content-Type"))){
Map<String, Object> result = new HashMap<>();
result.put("message", ex.getMessage());
return result;
}else {
ModelAndView mav = new ModelAndView();
mav.addObject("exception", ex);
mav.addObject("url", req.getRequestURL());
mav.setViewName("error/errorPage");
return mav;
}
}
测试结果:还是ok的。但没有真正的测试 ,当是ajax请求抛出错误是否能够判断。但思路应该是可行的
虽然加了@ResponseBody但是返回是ModelAndView时还是可以解析成页面
七、对API的拦截
1. 过滤器(Filter)
定义一个时间过滤器,实现对API访问进行计时
@Slf4j
@Component
public class TimeFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("TimeFilter Init");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
log.info("time filter start");
long begin = System.currentTimeMillis();
filterChain.doFilter(servletRequest,servletResponse);
log.info("the request spent :"+(System.currentTimeMillis()-begin));
log.info("time filter finish");
}
@Override
public void destroy() {
log.info("TimeFilter Destroy");
}
}
使用@Component 就可以使过滤器生效
调用一个API,结果
自定义的拦截器可以利用注解实现注入使过滤器生效。但是其他第三方过滤器时该如何配置。
添加配置文件。将timeFilter的@Component去掉使用下面也可以实现添加过滤器。
WebConfig.java
@Configuration
public class WebConfig {
@Bean
public FilterRegistrationBean timeFilter(){
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
TimeFilter timeFilter = new TimeFilter();
registrationBean.setFilter(timeFilter);
List<String> urls = new ArrayList<>();
urls.add("/*"); //拦截url
registrationBean.setUrlPatterns(urls);
return registrationBean;
}
}
2. 拦截器(Interceptor)
在过滤器中。使用的Filter
是在包 javax.servlet中定义的。并不知道spring的那一套
使用拦截器实现运行时间。但可以获取执行函数
TimeInterceptor.java
/**
* @Description:
* @Author: HJH
* @Date: 2019-08-17 19:31
*/
@Slf4j
@Component
public class TimeInterceptor implements HandlerInterceptor {
/**
* @Description:控制器方法调用之前
* @Author: HJH
* @Date: 2019-08-17 19:35
* @Param: [request, response, handler]
* @Return: boolean
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
log.info("Interceptor perHandle");
HandlerMethod handlerMethod = (HandlerMethod) handler;
log.info("Interceptor Class Name: "+handlerMethod.getBean().getClass().getName());
log.info("Interceptor Method Name: "+handlerMethod.getMethod().getName());
request.setAttribute("startTime",System.currentTimeMillis());
return true;
}
/**
* @Description:控制器完成之后。但当控制器抛出错误时就不会进入该函数
* @Author: HJH
* @Date: 2019-08-17 19:36
* @Param: [request, response, handler, modelAndView]
* @Return: void
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
log.info("Interceptor postHandle");
long start = (long) request.getAttribute("startTime");
log.info("Time Interceptor Spent:"+(System.currentTimeMillis()-start));
}
/**
* @Description:类似try-catch 的finally
* @Author: HJH
* @Date: 2019-08-17 19:37
* @Param: [request, response, handler, ex]
* @Return: void
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable Exception ex) throws Exception {
log.info("Interceptor afterCompletion");
long start = (long) request.getAttribute("startTime");
log.info("Time Interceptor Spent:"+(System.currentTimeMillis()-start));
log.info("Exception is "+ex);
}
}
WebInterceptorConfig.java
@Configuration
public class WebInterceptorConfig implements WebMvcConfigurer {
@Autowired
private TimeInterceptor timeInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(timeInterceptor);
}
}
运行结果
可以看到 afterCompletion一定会执行的
3. 切片(Aspect)
spring aop
切片实现
要点:切入点 (通过注解)1. 在什么方法上起作用。2. 在什么时候起作用。
要点:增强(通过方法):起作用时执行的业务逻辑
TimeAspect.java
@Slf4j
@Aspect
@Component
public class TimeAspect {
@Around("execution(* com.hjh.myspringboot.myspringboot.web.controller.UserController.*(..))")
public Object handleControllerMethod(ProceedingJoinPoint pjp) throws Throwable {
log.info("Time Aspect Start");
//获取传输args
Object[] args = pjp.getArgs();
for (Object arg:args){
log.info("arg is:" +arg);
}
long start = System.currentTimeMillis();
Object o = pjp.proceed();
log.info("Time Aspect spent:"+(new Date().getTime()-start));
log.info("Time Aspect end");
return o;
}
}
结果
总结
拦截顺序
七、文件上传下载
比较简单的实现和单元测试
增加单元测试
@Test
public void whenUploadSuccess() throws Exception {
String result = mockMvc.perform(multipart("/file")
.file(new MockMultipartFile("file","test.txt","multipart/form-data","hello".getBytes("utf-8"))))
.andExpect(status().isOk())
.andReturn().getResponse().getContentAsString();
log.info("上传结果"+result);
}
fileupload在springboot2.0x过时。
FileController
@Slf4j
@RestController
@RequestMapping("/file")
public class FileController {
public static final String FOLDER = "D:/U";
@PostMapping
public Map upload(MultipartFile file) throws IOException {
log.info("上传文件名"+file.getName());
log.info("上传文件原始名"+file.getOriginalFilename());
log.info("上传文件大小:"+file.getSize());
File file1 = new File(FOLDER,System.currentTimeMillis()+".txt");
file.transferTo(file1);
Map<String, String> map = new HashMap<>();
map.put("path",file1.getAbsolutePath());
return map;
}
@GetMapping("/{id}")
public void download(@PathVariable String id, HttpServletResponse response, HttpServletRequest request) throws IOException {
try(FileInputStream inputStream = new FileInputStream(new File(FOLDER, id + ".txt"));
OutputStream outputStream = response.getOutputStream()) {
response.setContentType("application/x-download");
response.addHeader("Content-Disposition","attachment;filename=test.txt");
IOUtils.copy(inputStream,outputStream);
outputStream.flush();
}
}
}
八、异步处理
当同时访问一个需要处理10s的方法时
结果
通过输出:
第一个访问过来10s后第二个访问才被处理。也就是说在当前方法中。同一时间只能处理一个请求。只有当前请求处理完后才能处理下一个。
1、使用Runable异步
@GetMapping("async2")
public Callable<String> async2(){
log.info("主线程开始");
Callable<String> result = new Callable<String>() {
@Override
public String call() throws Exception {
log.info("副线程开始");
for (int i=0;i<10;i++){
Thread.sleep(1000);
}
log.info("副线程返回");
return "success";
}
};
log.info("主线程结束");
return result;
}
在同一时间可以处理同一类请求,提高了服务器的吞吐量
场景比较单一,添加一个副线程。
2. 使用DeferredResult异步处理请求
使用消息队列解决请求丢失等问题,可以用于订单处理等,重要的,高可用,高并发的请求。
下面使用异步模拟处理订单
使用RibbitMq作为消息队列
添加maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
添加mq配置
spring.application.name=spring-boot
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.username=admin
spring.rabbitmq.password=123456
添加springboot配置
使用Exchange类型的Direct。添加两条队列。一个用于接收下单,一个接收订单处理结果。
RabbitMQConfig.java
Configuration
public class RabbitMQConfig {
@Bean
public Queue queue1(){
return new Queue("order");
}
@Bean
public Queue queue2(){
return new Queue("finish");
}
}
DeferredResultHolder.java
@Component
public class DeferredResultHolder {
private Map<String , DeferredResult<String>> map = new HashMap<>();
public Map<String, DeferredResult<String>> getMap() {
return map;
}
public void setMap(Map<String, DeferredResult<String>> map) {
this.map = map;
}
}
- 线程1:
@Autowired
private AmqpTemplate rabbitTemplate;
@GetMapping("async4")
public DeferredResult<String> async4() throws InterruptedException {
log.info("主线程开始");
String orderId = RandomStringUtils.randomNumeric(8);
log.info("2.发送下单请求:"+orderId);
rabbitTemplate.convertAndSend("order",orderId);
DeferredResult<String> result = new DeferredResult();
deferredResultHolder.getMap().put(orderId,result);
return result;
}
- 应用2:监听请求并处理
@Slf4j
@Component
@RabbitListener(queues = "order")
public class OrderReceiver {
@Autowired
private RabbitTemplate rabbitTemplate;
@RabbitHandler
public void process(String re) {
new Thread(()->{
log.info("3.监听到下单请求:"+re);
//处理
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//处理完成
log.info("4.订单处理完成:"+re);
rabbitTemplate.convertAndSend("finish",re);
}).start();
}
- 线程2:接收完成订单,并响应请求。
@Slf4j
@Component
@RabbitListener(queues = "finish")
public class OrderFinishThread {
@Autowired
private DeferredResultHolder deferredResultHolder;
@RabbitHandler
public void process(String re) {
new Thread(()->{
log.info("5.监听到完成的订单2:"+re);
deferredResultHolder.getMap().get(re).setResult("订单成功");
}).start();
}
}
使用jmeter对Callable和DeferredResult异步进行简单的测试
并发100个请求
Callable
DeferredResult
-- 1 -- springboot的更多相关文章
- 解决 Springboot Unable to build Hibernate SessionFactory @Column命名不起作用
问题: Springboot启动报错: Caused by: org.springframework.beans.factory.BeanCreationException: Error creati ...
- 【微框架】Maven +SpringBoot 集成 阿里大鱼 短信接口详解与Demo
Maven+springboot+阿里大于短信验证服务 纠结点:Maven库没有sdk,需要解决 Maven打包找不到相关类,需要解决 ps:最近好久没有写点东西了,项目太紧,今天来一篇 一.本文简介 ...
- Springboot搭建web项目
最近因为项目需要接触了springboot,然后被其快速零配置的特点惊呆了.关于springboot相关的介绍我就不赘述了,大家自行百度google. 一.pom配置 首先,建立一个maven项目,修 ...
- Java——搭建自己的RESTful API服务器(SpringBoot、Groovy)
这又是一篇JavaWeb相关的博客,内容涉及: SpringBoot:微框架,提供快速构建服务的功能 SpringMVC:Struts的替代者 MyBatis:数据库操作库 Groovy:能与Java ...
- 解决 SpringBoot 没有主清单属性
问题:SpringBoot打包成jar后运行提示没有主清单属性 解决:补全maven中的bulid信息 <plugin> <groupId>org.springframewor ...
- SpringBoot中yaml配置对象
转载请在页首注明作者与出处 一:前言 YAML可以代替传统的xx.properties文件,但是它支持声明map,数组,list,字符串,boolean值,数值,NULL,日期,基本满足开发过程中的所 ...
- springboot 学习资源推荐
springboot 是什么?对于构建生产就绪的Spring应用程序有一个看法. Spring Boot优先于配置的惯例,旨在让您尽快启动和运行.(这是springboot的官方介绍) 我们为什么要学 ...
- Springboot框架
本片文章主要分享一下,Springboot框架为什么那么受欢迎以及如何搭建一个Springboot框架. 我们先了解一下Springboot是个什么东西,它是干什么用的.我是刚开始接触,查了很多资料, ...
- 如何在SpringBoot中使用JSP ?但强烈不推荐,果断改Themeleaf吧
做WEB项目,一定都用过JSP这个大牌.Spring MVC里面也可以很方便的将JSP与一个View关联起来,使用还是非常方便的.当你从一个传统的Spring MVC项目转入一个Spring Boot ...
- 5分钟创建一个SpringBoot + Themeleaf的HelloWord应用
第一步:用IDE创建一个普通maven工程,我用的eclipse. 第二步:修改pom.xml,加入支持SpringBoot和Themeleaf的依赖,文件内容如下: <?xml version ...
随机推荐
- openpyxl模块(excel操作)
openpyxl模块介绍 openpyxl模块是一个读写Excel 2010文档的Python库,如果要处理更早格式的Excel文档,需要用到额外的库,openpyxl是一个比较综合的工具,能够同时读 ...
- Jmeter linux 运行
一.在Linux服务器先安装sdk 1.先从客户端下载jdk1.8.0_144.tar.gz,再上传到服务器 2.解压:tar -xzf jdk1.8.0_144.tar.gz,生成文件夹 jdk1. ...
- git修改下载地址
git查看远程地址git remote -v修改git remote set-url origin [url]
- mysql 常见面试题
附录: https://mp.weixin.qq.com/s/pC0_Y7M7BkoUmlRwneZZdA 一.为什么用自增列作为主键 1.如果我们定义了主键(PRIMARY KEY),那么InnoD ...
- 重读APUE(9)-SIG_ERR、SIG_DFL、SIG_IGN定义无参数
下面这几个函数定义,每次看到都会纠结一阵子,奇怪的是为什么没有参数? #define SIG_ERR (void (*)())-1 #define SIG_DFL (void (*)())0 #def ...
- postgresql interval 字段拼接
无拼接时: SELECT scan_time + '5 day' FROM tbl_temp_record SELECT scan_time + '-5 day' FROM tbl_temp_reco ...
- qt 创建及调用QT的 DLL
先讲一下对QT动态链接库的调用方法,主要包括: 1.显式链接DLL,调用DLL的全局函数,采用Qt的QLibrary方法 2.显示链接DLL,调用DLL中类对象.成员函数.(通过对象即可实现类成员函数 ...
- [Java复习] Spring 常见面试问题
1. 什么是 Spring 框架?Spring 框架有哪些主要模块? 轻量级实现IoC和AOP的JavaEE框架. Core模块: bean(bean定义创建解析), context(环境, IoC容 ...
- Dynatrace
1.概述 过去,企业的IT部门在测量系统性能时,一般重点测量为最终用户提供服务的硬件组件的利用率,如CPU利用率以及通过网络传输的字节数.虽然这种方法也提供了一些宝贵的信息,但却忽视了最重要的因素-- ...
- 阿里内部分享:我们是如何?深度定制高性能MySQL的
阿里云资深数据库工程师赵建伟在“云栖大会上海峰会”的分享.核心是阿里云的数据库服务和MySQL分支的深度定制实践分享. 阿里巴巴MySQL在全球都是有名的.不仅是因为其性能,还因为其是全世界少数拥有M ...