快速搭建Spring Boot項目及常用技術整合
- 2019 年 12 月 13 日
- 筆記
Spring Boot簡介
Spring Boot是由Pivotal團隊提供的全新框架,其設計目的是用來簡化新Spring應用的初始搭建以及開發過程。該框架使用了特定的方式來進行配置,從而使開發人員不再需要定義樣板化的配置。
Spring Boot特點
- 創建獨立的Spring應用程式
- 嵌入的Tomcat,無需部署WAR文件
- 簡化Maven配置
- 自動配置Spring
- 提供生產就緒型功能,如指標,健康檢查和外部配置
- 絕對沒有程式碼生成並且對XML也沒有配置要求
快速入門
1、訪問http://start.spring.io/構建項目,也可在idea創建如下圖:




2、 springboot默認生成三個文件
2.1 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> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.4.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>demo</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </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>
重點就一個gav:spring-boot-starter-web,其他可以刪除。
2.2 application.properties
該文件默認為空,springboot的默認啟動埠號:8080,可以在改文件修改。建議用yml的格式
server: port: 8080
2.3 啟動類文件
public class JxcApplication { public static void main(String[] args) { SpringApplication.run(JxcApplication.class, args); } }
2.4 驗證springboot
在項目包路徑下創建一個Controller,寫一個HelloController
@Controller public class HelloController { @RequestMapping("/") @ResponseBody public String getHello() { return "hello"; } }
瀏覽器查看效果

完成項目
完整項目目錄

1、項目依賴
- web
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.0</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>${aspectjweaver.version}</version> </dependency>
- mysql
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.version}</version> </dependency>
- lombok(可選)
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency>
- pagehelper(可選)
<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>${pagehelper.version}</version> </dependency>
- JWT(可選)
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>${jwt.version}</version> </dependency>
- mybatis
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.2</version> </dependency>
- shiro
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.0</version> </dependency>
- hutool(可選)
<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.0.7</version> </dependency>
- druid
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.10</version> </dependency>
- jdbc
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency>
- fastjson
<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>${fastjson.version}</version> </dependency>
- tomcat
<dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-core</artifactId> </dependency>
附上properties
<properties> <project.version>1.0</project.version> <java.version>1.8</java.version> <mysql.version>5.1.25</mysql.version> <pagehelper.version>1.2.12</pagehelper.version> <jwt.version>0.9.1</jwt.version> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <commons.lang.version>3.9</commons.lang.version> <aspectjweaver.version>1.9.4</aspectjweaver.version> <fastjson.version>1.2.62</fastjson.version> </properties>
2、配置文件
2.1修改`application.properties`為`application.yml`
配置埠,項目根路徑,spring配置,mybatis配置,分頁插件配置
server: port: 8100 servlet: context-path: /api spring: profiles: active: dev http: encoding: charset: UTF-8 force: true enabled: true mybatis: mapper-locations: classpath:/mapper/*.xml type-aliases-package: com.example.jxc.domain.entity.* configuration: cache-enabled: true lazy-loading-enabled: true multiple-result-sets-enabled: true use-column-label: true call-setters-on-nulls: true local-cache-scope: session map-underscore-to-camel-case: true default-executor-type: BATCH auto-mapping-behavior: PARTIAL pagehelper: helperDialect: mysql reasonable: true supportMethodsArguments: true params: count=countSql
2.2 新建`application-dev.yml`
配置資料庫資訊,通過application.yml
中的active來啟用dev配置文件
spring: profiles: active: dev
application-dev.yml
完整配置
spring: datasource: # 數據源基本配置 username: root password: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/fhshgl type: com.alibaba.druid.pool.DruidDataSource # 數據源其他配置 initialSize: 5 minIdle: 5 maxActive: 20 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQuery: SELECT 1 FROM DUAL testWhileIdle: true testOnBorrow: false testOnReturn: false poolPreparedStatements: true # 配置監控統計攔截的filters,去掉後監控介面sql無法統計,'wall'用於防火牆 filters: stat,wall maxPoolPreparedStatementPerConnectionSize: 20 useGlobalDataSourceStat: true connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
3、資料庫連接池

@Configuration public class DruidConfig { @ConfigurationProperties(prefix = "spring.datasource") @Bean public DataSource druid(){ return new DruidDataSource(); } /** * 配置Druid的監控 * @return */ @Bean public ServletRegistrationBean statViewServlet(){ ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*"); Map<String,String> initParams = new HashMap<>(); initParams.put("loginUsername","admin"); initParams.put("loginPassword","123456"); //默認就是允許所有訪問 initParams.put("allow",""); initParams.put("deny","192.168.15.21"); bean.setInitParameters(initParams); return bean; } /** * 配置一個web監控的filter * @return */ @Bean public FilterRegistrationBean webStatFilter(){ FilterRegistrationBean bean = new FilterRegistrationBean(); bean.setFilter(new WebStatFilter()); Map<String,String> initParams = new HashMap<>(); initParams.put("exclusions","*.js,*.css,/druid/*"); bean.setInitParameters(initParams); bean.setUrlPatterns(Arrays.asList("/*")); return bean; } }
4、shiro
4.1自定義realm

public class MyRealm extends AuthorizingRealm{ @Autowired private UserService userService; @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { return null; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { //拿到封裝好賬戶密碼的token UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken; String userName = token.getUsername(); //用戶校驗 User user = this.userService.getUser(userName); if (user == null) { throw new AuthenticationException("用戶名或密碼錯誤!"); } //加鹽 計算鹽值 保證每個加密後的 MD5 不一樣 ByteSource credentialsSalt = ByteSource.Util.bytes(user.getUsername()); SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword(), credentialsSalt, this.getName()); return info; } }
4.2shiro配置

@Configuration public class ShiroConfig { /** * 主要配置一些相應的URL的規則和訪問許可權 */ @Bean public ShiroFilterFactoryBean shiroFilter() { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager()); //攔截器. Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>(); //配置退出 過濾器,其中的具體的退出程式碼Shiro已經替我們實現了 filterChainDefinitionMap.put("/system/logout", "anon"); //過濾鏈定義,從上向下順序執行,一般將/**放在最為下邊 //authc:所有url都必須認證通過才可以訪問; anon:所有url都都可以匿名訪問 // filterChainDefinitionMap.put("/static/**", "anon"); shiroFilterFactoryBean.setLoginUrl("/system/login"); filterChainDefinitionMap.put("/**", "authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } /** * 注入 securityManager */ @Bean public DefaultWebSecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); // 設置realm. securityManager.setRealm(customRealm()); return securityManager; } /** * 自定義身份認證 realm; * <p> * 必須寫這個類,並加上 @Bean 註解,目的是注入 MyRealm, * 否則會影響 MyRealm 中其他類的依賴注入 */ @Bean public MyRealm customRealm() { return new MyRealm(); } /** * 開啟Shiro的註解(如@RequiresRoles,@RequiresPermissions),需藉助SpringAOP掃描使用Shiro註解的類,並在必要時進行安全邏輯驗證 * 配置以下兩個bean(DefaultAdvisorAutoProxyCreator(可選)和AuthorizationAttributeSourceAdvisor)即可實現此功能 * * @return */ @Bean @DependsOn({"lifecycleBeanPostProcessor"}) public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator(); advisorAutoProxyCreator.setProxyTargetClass(true); return advisorAutoProxyCreator; } @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager()); return authorizationAttributeSourceAdvisor; } /** * Shiro生命周期處理器 ---可以自定的來調用配置在 Spring IOC 容器中 shiro bean 的生命周期方法. * * @return */ @Bean public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } }
5、過濾器-跨域過濾
5.1跨域過濾

public class CostFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse resp = (HttpServletResponse) response; String origin = req.getHeader("Origin"); if (origin == null) { origin = req.getHeader("Referer"); } // 允許指定域訪問跨域資源 resp.setHeader("Access-Control-Allow-Origin", origin); // 允許客戶端攜帶跨域cookie,此時origin值不能為「*」,只能為指定單一域名 resp.setHeader("Access-Control-Allow-Credentials", "true"); if ("OPTIONS".equals(req.getMethod())) { String allowMethod = req.getHeader("Access-Control-Request-Method"); String allowHeaders = req.getHeader("Access-Control-Request-Headers"); // 瀏覽器快取預檢請求結果時間,單位:秒 resp.setHeader("Access-Control-Max-Age", "86400"); // 允許瀏覽器在預檢請求成功之後發送的實際請求方法名 resp.setHeader("Access-Control-Allow-Methods", allowMethod); // 允許瀏覽器發送的請求消息頭 resp.setHeader("Access-Control-Allow-Headers", allowHeaders); resp.setHeader("Content-Type", "application/json;charset=utf-8"); return; } chain.doFilter(request, response); } }
5.2 過濾器配置

@Configuration public class FilterConfig { @Bean public FilterRegistrationBean configureFilter(){ FilterRegistrationBean bean = new FilterRegistrationBean<>(); bean.setName("costFilter"); CostFilter costFilter = new CostFilter(); bean.setFilter(costFilter); bean.setOrder(1); List<String> urlList = new ArrayList<String>(); urlList.add("/*"); bean.setUrlPatterns(urlList); return bean; } }
6、token攔截
6.1JWT
jwt工具類
public class JwtUtils { public static SecretKey getBase64Key() { String stringKey = "MyJwtSecret"; byte[] encodeKey = Base64.getDecoder().decode(stringKey); SecretKey key = new SecretKeySpec(encodeKey, 0, encodeKey.length, "AES"); return key; } /** * 簽發token * * @param userName 用戶名 * @return token */ public static String create(String userName) { Date now = new Date(System.currentTimeMillis()); String token = Jwts.builder() .setIssuedAt(now) .setSubject(userName) .setExpiration(new Date(System.currentTimeMillis() + 60 * 60 * 1000)) .signWith(SignatureAlgorithm.HS256, getBase64Key()) .compact(); return token; } /** * 解析token * * @param token token * @return 用戶名 */ public static String parse(String token) { String username = null; try { username = Jwts.parser() .setSigningKey(getBase64Key()) .parseClaimsJws(token.replace("Bearer ", "")) .getBody() .getSubject(); } catch (Exception e) { e.printStackTrace(); } return username; } /** * 檢驗token是否過期 * * @param token * @return */ public static boolean verify(String token) { Date expiraDate = null; Date currentDate = new Date(); try { expiraDate = Jwts.parser() .setSigningKey(getBase64Key()) .parseClaimsJws(token.replace("Bearer ", "")) .getBody() .getExpiration(); if (currentDate.before(expiraDate)) { return true; } else { return false; } } catch (Exception e) { return false; } } }
6.2token攔截器

@Component public class TokenInterceptor implements HandlerInterceptor { public Log log = LogFactory.getLog(TokenInterceptor.class); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler){ if (request.getMethod().equals("OPTIONS")) { response.setStatus(HttpServletResponse.SC_OK); return true; } response.setCharacterEncoding("utf-8"); String token = request.getHeader("Authorization"); if (token != null) { boolean result = JwtUtils.verify(token); if (result) { return true; } } log.error("認證失敗"); response.setStatus(HttpServletResponse.SC_NON_AUTHORITATIVE_INFORMATION); return false; } }
6.3配置攔截器

@Configuration public class InterceptorConfig extends WebMvcConfigurationSupport { @Autowired private TokenInterceptor tokenInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(tokenInterceptor) .addPathPatterns("/**") .excludePathPatterns("/**/login") .excludePathPatterns("/**/logOut"); } }
7、完成一個登錄介面`LoginController`
@RestController @RequestMapping("/system") public class LoginController extends BaseController { @Autowired private UserService userService; /** * 瀏覽器點擊登錄 * * @param user * @return */ @PostMapping("/login") public R login(@RequestBody User user) { log.debug("------瀏覽器點擊登錄------"); String userName = user.getUsername(); String passWord = user.getPassword(); UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(userName, MD5.md5Salt(passWord, userName)); Subject subject = SecurityUtils.getSubject(); try { subject.login(usernamePasswordToken); String token = JwtUtils.create(userName); return R.ok(R.SUCCESS, R.MSG_SUCCESS, token); } catch (AuthenticationException e) { e.printStackTrace(); return R.error(R.MSG_LOGIN_ERROR); } } }