SpringBoot一统江湖
一 SpringBoot简介
SpringBoot是Spring框架的一个新子项目 用于创建Spring4.0项目 它的开发始于2013年 2014年4月发布1.0.0版本 它可以自动配置Spring的各种组件 并不依赖代码生成和XML配置文件 SpringBoot也提供了对于常见场景的推荐组件配置 SpringBoot可以大大提升使用Spring框架时的开发效率 使用SpringBoot可以轻松创建独立运行的程序 非常容易构建独立的服务组件 是实现分布式架构 微服务架构利器
二 SpringBoot优点
1. 轻松创建独立的Spring应用程序
2. 内嵌Tomcat Jetty等web容器 不需要部署WAR文件
3. 提供一系列的starter 来简化的Maven配置 不需要添加很多依赖
4. 开箱即用 尽可能自动配置Spring
三 SpringBoot初体验
1. 新建一个Maven项目 注意不需要webapp文件夹及子文件夹和web.xml
pom.xml
<?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> <groupId>com.hy.springboot</groupId> <artifactId>springboot-demo</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.4.RELEASE</version> </parent> <!-- 定义依赖版本号 --> <properties> <mysql-connector-java.version>8.0.11</mysql-connector-java.version> <druid.version>1.1.10</druid.version> <mybatis-spring-boot-starter.version>2.1.3</mybatis-spring-boot-starter.version> <pagehelper-spring-boot-starter.version>1.3.0</pagehelper-spring-boot-starter.version> </properties> <!-- 管理jar版本号 --> <dependencyManagement> <dependencies> <!-- mysql --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql-connector-java.version}</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>${druid.version}</version> </dependency> <!-- mybatis --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>${mybatis-spring-boot-starter.version}</version> </dependency> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>1.3.0</version> </dependency> </dependencies> </dependencyManagement> <dependencies> <!-- web --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- aop --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <!-- mysql --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> </dependency> <!-- mybatis --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> </dependency> </dependencies> <build> <!-- 允许mybatis的mapper.java和mapper.xml在同一目录 --> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.yml</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.yml</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> </resources> <!-- 打包名称 --> <finalName>one</finalName> <plugins> <!-- 编译 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> <!-- spring boot --> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <mainClass>com.hy.springboot.Application</mainClass> </configuration> </plugin> </plugins> </build> </project>
sql
-- 用户表 CREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT '用户id', username VARCHAR(32) COMMENT '用户名', money DOUBLE COMMENT '用户余额' ); INSERT INTO user VALUES (1, '曹操', 8000); INSERT INTO user VALUES (2, '孙权', 8000); INSERT INTO user VALUES (3, '刘备', 8000); INSERT INTO user VALUES (4, '诸葛亮', 5000); INSERT INTO user VALUES (5, '司马懿', 5000); INSERT INTO user VALUES (6, '张飞', 0); INSERT INTO user VALUES (7, '关羽', 0); INSERT INTO user VALUES (8, '马超', 1000); INSERT INTO user VALUES (9, '黄忠', 1000); INSERT INTO user VALUES (10, '赵云', 3000);
2. 创建配置文件 resources/application.yml
spring: datasource: url: jdbc:mysql://localhost:3306/demo_hy?characterEncoding=utf8&useSSL=false driver-class-name: com.mysql.cj.jdbc.Driver username: root password: root type: com.alibaba.druid.pool.DruidDataSource max-active: 20 min-idle: 5 servlet: multipart: max-file-size: 5Mb max-request-size: 10MB mybatis: type-aliases-package: com.hy.springboot.model configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl pagehelper: helper-dialect: mysql reasonable: true support-methods-arguments: true params: count=countSql server: port: 8081 servlet: context-path: /
3. 创建实体类 com.hy.springboot.model.User com.hy.springboot.model.PageWrapper com.hy.springboot.model.HttpError
public class User implements Serializable { private Integer id; private String username; private Double money; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public Double getMoney() { return money; } public void setMoney(Double money) { this.money = money; } @Override public String toString() { return "User{" + "id=" + id + ", username='" + username + '\'' + ", money=" + money + '}'; } }
public class PageWrapper implements Serializable { private Long total; private List list; public PageWrapper() {} public PageWrapper(Long total, List list) { this.total = total; this.list = list; } public Long getTotal() { return total; } public void setTotal(Long total) { this.total = total; } public List getList() { return list; } public void setList(List list) { this.list = list; } }
public class HttpError extends Exception { private int code; private String message; public HttpError() {} public HttpError(int code, String message) { this.code = code; this.message = message; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } @Override public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } @Override public String toString() { return "HttpError{" + "code=" + code + ", message='" + message + '\'' + '}'; } }
4. 自定义文件上传实现类 com.hy.springboot.resolver.PostAndPutMultipartResolver 解决客户端(android ios...) PUT提交文件表单异常
public class PostAndPutMultipartResolver extends StandardServletMultipartResolver { @Override public boolean isMultipart(HttpServletRequest request) { if ("POST".equalsIgnoreCase(request.getMethod()) || "PUT".equalsIgnoreCase(request.getMethod())) { return StringUtils.startsWithIgnoreCase(request.getContentType(), "multipart/"); } return false; } }
5. 配置文件上传实现类 com.hy.springboot.config.MultipartResolverConfig
@Configuration public class MultipartResolverConfig { @Bean(name = "multipartResolver") public MultipartResolver multipartResolver() { return new PostAndPutMultipartResolver(); } }
6. 创建拦截器 com.hy.springboot.interceptor.Interceptor
public class Interceptor implements HandlerInterceptor { // Controller执行前调用此方法 // 返回true继续执行 返回false中止执行 // 这里可加入登录校验 权限拦截等 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println(" ========== preHandle ========== "); // 放行 return true; } // Controller执行后 且未返回视图前 调用此方法 // 这里可在返回用户前对模型数据进行加工处理 比如这里加入公用信息以便页面显示 @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {} // Controller执行后 且视图返回后 调用此方法 // 这里可得到执行Controller时的异常信息 // 这里可记录操作日志 @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {} }
7. 配置拦截器 com.hy.springboot.config.InterceptorConfig
@Configuration public class InterceptorConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new Interceptor()).addPathPatterns("/**"); //拦截所有 } }
8. 配置异常处理器 com.hy.springboot.exception.HttpErrorResolver
@ControllerAdvice public class HttpErrorResolver { @ExceptionHandler(RuntimeException.class) @ResponseBody public HttpError resolveException(Exception e) { HttpError error = e instanceof HttpError ? (HttpError) e : new HttpError(-1, "未知异常"); return error; } }
9. 配置数据源 com.hy.springboot.config.DruidConfig
@Configuration public class DruidConfig { @Bean @ConfigurationProperties(prefix = "spring.datasource") //将application.yml配置文件中前缀为spring.datasource的属性注入到DruidDataSource同名参数中 public DataSource dataSource() { return new DruidDataSource(); } }
10. 配置AOP com.hy.springboot.aop.LogAspect
@Component @Aspect public class LogAspect { /** * 声明公共切入点 * expression(表达式): 切点表达式 * * com.hy.springboot.controller..*.*(..) * * = 任意返回值 * com.hy.springboot.controller. = com.hy.springboot.controller包和所有子包 * .*.* = .任意类.任意方法名 * (..) = 任意参数 */ @Pointcut("execution(* com.hy.springboot.controller..*.*(..))") public void pointcut() {} /** * 前置通知 在方法执行前执行 如果通知抛出异常 阻止方法运行 * @param joinPoint 连接点 */ @Before("pointcut()") public void before(JoinPoint joinPoint) { System.out.println("前置通知 JoinPoint = " + joinPoint.toString()); // 请求内容 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); String url = request.getRequestURL().toString(); String method = request.getMethod(); System.out.println("url = " + url); System.out.println("method = " + method); Enumeration<String> enu = request.getParameterNames(); while (enu.hasMoreElements()) { String name = enu.nextElement(); System.out.println(name + "=" + request.getParameter(name)); } } /** * 后置通知 方法正常返回后执行 可以获得方法返回值 如果方法中抛出异常 通知无法执行 * @param joinPoint 连接点 * @param ret 方法返回值 */ @AfterReturning(value = "pointcut()", returning = "ret") public void afterReturning(JoinPoint joinPoint, Object ret) { System.out.println("后置通知 ret = " + ret + " JoinPoint = " + joinPoint.toString()); } }
11. 配置启动器 com.hy.springboot.Application
@SpringBootApplication @MapperScan("com.hy.springboot.mapper") public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
12. 创建mapper接口 com.hy.springboot.mapper.UserMapper
public interface UserMapper { List<User> selectUserList(); Integer updateUserMoney(Map map); }
13. 创建mapper映射文件 com.hy.springboot.mapper.UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.hy.springboot.mapper.UserMapper"> <select id="selectUserList" resultType="User"> SELECT * FROM user </select> <update id="updateUserMoney" parameterType="HashMap"> UPDATE user SET money = money + #{money} WHERE id = #{id} </update> </mapper>
14. 创建service接口 com.hy.springboot.service.IUserService
public interface IUserService { List<User> selectUserList(); PageWrapper selectUserList(Integer pageNum, Integer pageSize); Boolean updateUserMoney(Map map1, Map map2); }
15. 创建service实现类 com.hy.springboot.service.imp.UserService
@Service @Transactional public class UserService implements IUserService { @Autowired private UserMapper userMapper; @Override public List<User> selectUserList() { return userMapper.selectUserList(); } @Override public PageWrapper selectUserList(Integer pageNum, Integer pageSize) { PageHelper.startPage(pageNum, pageSize); List<User> userList = userMapper.selectUserList(); PageInfo<User> pageInfo = new PageInfo<>(userList); return new PageWrapper(pageInfo.getTotal(), userList); } @Override public Boolean updateUserMoney(Map map1, Map map2) { Integer integer1 = userMapper.updateUserMoney(map1); int i = 3 / 0; //模拟异常 Integer integer2 = userMapper.updateUserMoney(map2); return 0 != integer1 && 0 != integer2; } }
16. 创建控制器 com.hy.springboot.controller.TestController
@RestController @RequestMapping("/test") public class TestController { @Autowired private IUserService userService; @RequestMapping("/tm") public Boolean tm() { Map<String, Object> map1 = new HashMap<>(); map1.put("id", 6); map1.put("money", -1000.0); Map<String, Object> map2 = new HashMap<>(); map2.put("id", 7); map2.put("money", +1000.0); return userService.updateUserMoney(map1, map2); } @RequestMapping("/selectUserList") public List<User> selectUserList() { return userService.selectUserList(); } @GetMapping("/selectUserList/{page}/{size}") public PageWrapper selectUserList(@PathVariable Integer page, @PathVariable Integer size) { return userService.selectUserList(page, size); } /** RESTful 只进行演示 没有做数据持久化 **/ @PostMapping("/insert") public Map insert(String name) { Map<String, Object> map = new HashMap<>(); map.put("name", name); return map; } @PostMapping("/insertX") public Map insertX(String name, MultipartFile file) throws Exception { Map<String, Object> map = new HashMap<>(); map.put("name", name); map.put("fileSize", file.getSize()); //建议使用文件服务器保存 return map; } @DeleteMapping("/delete/{id}") public Map delete(@PathVariable Integer id, String name) { Map<String, Object> map = new HashMap<>(); map.put("id", id); map.put("name", name); return map; } @PutMapping("/update/{id}") public Map update(@PathVariable Integer id, String name) { Map<String, Object> map = new HashMap<>(); map.put("id", id); map.put("name", name); return map; } @PutMapping("/updateX/{id}") public Map updateX(@PathVariable Integer id, String name, MultipartFile file) throws Exception { Map<String, Object> map = new HashMap<>(); map.put("id", id); map.put("name", name); map.put("fileSize", file.getSize()); //建议使用文件服务器保存 return map; } @GetMapping("/select/{id}") public Map select(@PathVariable Integer id, String name) { Map<String, Object> map = new HashMap<>(); map.put("id", id); map.put("name", name); return map; } }
启动项目 访问 http://localhost:8081/test/selectUserList
android客户端核心代码
public interface Api { @POST("/test/insert") @FormUrlEncoded Call<ResponseBody> insert(@Field("name") String name); @POST("/test/insertX") @Multipart Call<ResponseBody> insertX(@Part MultipartBody.Part name, @Part MultipartBody.Part file); @DELETE("/test/delete/{id}") Call<ResponseBody> delete(@Path("id") Integer id, @Query("name") String name); @PUT("/test/update/{id}") @FormUrlEncoded Call<ResponseBody> update(@Path("id") Integer id, @Field("name") String name); @PUT("/test/updateX/{id}") @Multipart Call<ResponseBody> updateX(@Path("id") Integer id, @Part MultipartBody.Part name, @Part MultipartBody.Part file); @GET("/test/select/{id}") Call<ResponseBody> select(@Path("id") Integer id, @Query("name") String name); }
private void insert() { Call<ResponseBody> call = mApi.insert("祎哥哥"); call.enqueue(new Callback<ResponseBody>() { @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {} @Override public void onFailure(Call<ResponseBody> call, Throwable t) {} }); } private void insertX() { MultipartBody.Part name = MultipartBody.Part.createFormData("name", "祎哥哥"); RequestBody body = RequestBody.create(MediaType.parse("application/octet-stream"), card("text.txt")); MultipartBody.Part file = MultipartBody.Part.createFormData("file", "text.txt", body); Call<ResponseBody> call = mApi.insertX(name, file); call.enqueue(new Callback<ResponseBody>() { @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {} @Override public void onFailure(Call<ResponseBody> call, Throwable t) {} }); } private void delete() { Call<ResponseBody> call = mApi.delete(3, "黄祎"); call.enqueue(new Callback<ResponseBody>() { @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {} @Override public void onFailure(Call<ResponseBody> call, Throwable t) {} }); } private void update() { Call<ResponseBody> call = mApi.update(3, "黄祎"); call.enqueue(new Callback<ResponseBody>() { @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {} @Override public void onFailure(Call<ResponseBody> call, Throwable t) {} }); } private void updateX() { MultipartBody.Part name = MultipartBody.Part.createFormData("name", "黄祎"); RequestBody body = RequestBody.create(MediaType.parse("application/octet-stream"), card("text.txt")); MultipartBody.Part file = MultipartBody.Part.createFormData("file", "text.txt", body); Call<ResponseBody> call = mApi.updateX(3, name, file); call.enqueue(new Callback<ResponseBody>() { @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {} @Override public void onFailure(Call<ResponseBody> call, Throwable t) {} }); } private void select() { Call<ResponseBody> call = mApi.select(3, "黄祎"); call.enqueue(new Callback<ResponseBody>() { @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {} @Override public void onFailure(Call<ResponseBody> call, Throwable t) {} }); }