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="//maven.apache.org/POM/4.0.0" xmlns:xsi="//www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="//maven.apache.org/POM/4.0.0 //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" "//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; } }
啟動項目 訪問 //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) {} }); }