专栏首页国产程序员快速搭建Spring Boot项目及常用技术整合

快速搭建Spring Boot项目及常用技术整合

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

本文分享自微信公众号 - 国产程序员(Monday_lida),作者:看似无限透明的你

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-12-06

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 使用Spring Cloud搭建服务注册中心

    创建一个普通的Spring Boot工程.首先我们需要创建一个普通的Spring Boot工程,命名为eureka-server,普通到什么程度呢?就是一个st...

    一觉睡到小时候
  • 谈谈final、finally、finalize的区别

    这是一道再经典不过的面试题了,我们在各个公司的面试题中几乎都能看到它的身影。final、finally和finalize虽然长得像孪生三兄弟一样,但是它们的含义...

    一觉睡到小时候
  • HashMap实现原理及源码分析

    HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。 HashMap 继承于AbstractMap,实现了Map、Cloneable、...

    一觉睡到小时候
  • springCloud --- 初级篇(1)

    本系列笔记涉及到的代码在GitHub上,地址:https://github.com/zsllsz/cloud

    贪挽懒月
  • Feign Client without Eureka

    十毛
  • Java接口测试之ExtentReport测试报告

    ExtentReport是由Anshoo Arora创造的一个基于HTML5报告,它提供了Java与.NET类库,非常容易使用并且创建出漂亮的自动化测试报告。

    用户5521279
  • Java接口测试之ExtentReport测试报告

    ExtentReport是由Anshoo Arora创造的一个基于HTML5报告,它提供了Java与.NET类库,非常容易使用并且创建出漂亮的自动化测试报告。

    软测小生
  • mybatis3.2.8 与 hibernate4.3.6 混用

    mybatis、hibernate这二个框架各有特色,对于复杂的查询,利用mybatis直接手写sql控制起来更灵活,而一般的insert/update,hib...

    菩提树下的杨过
  • java 使用grpc步骤

    1、配置grpc maven依赖 <dependency> <groupId>io.grpc</groupId> ...

    杉枫
  • web项目常见错误和解决方案

    剽悍一小兔

扫码关注云+社区

领取腾讯云代金券