快速搭建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); } } }