快速搭建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創建如下圖:

step1.png
step2.png
step3.png
step4.png

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";      }  }

瀏覽器查看效果

HelloController.png

完成項目

完整項目目錄

project.png

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
realm.png
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);          }      }  }