使用idea搭建springboot项目

1. 创建项目

idea创建springboot项目非常简单,根据创建指引完成即可。
1.create new project
在这里插入图片描述

2.选择Spring Initializr初始化,选择sdk和default;
在这里插入图片描述
3.自定义group、artifact、name等信息,默认使用maven类型的java8版本;
在这里插入图片描述
4.idea默认的springboot版本为2.2.6,我们搭建的是springboot web项目,所以选择web下的spring web;
此外还需连接数据库,选择mysql和mybatis框架;
一路next下去即可。
在这里插入图片描述

2. 项目结构

在这里插入图片描述

2.1 唯一主入口

默认在包的同级路径下生成一个XXXApplication.java,内部是一个main方法。
在类上使用==@SpringBootApplication注解==,标识该类为唯一主入口!
程序启动方式执行main方法

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}

}

2.2 resource

  1. static:默认的存放css、js、image等静态资源的文件夹;
  2. thymeleaf:默认存放html等页面的文件夹;
  3. application.proprities(.yml):属性配置文件,.properties和.yml只是书写格式不同;

2.3 pom.xml

在创建项目时选择了spring web、mybatis和mysql,idea就根据我们选的这些模块初试化好了相关依赖。依赖如下:

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<!-- 默认的springboot版本:2.2.6 -->
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.2.6.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>
		<!-- spring web模块 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<!-- mybatis框架 -->
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>2.1.2</version>
		</dependency>
		<!-- mysql驱动 -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>

		<!-- test测试 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

3. 基础配置

3.1 指定Tomcat端口号

application.yml

#指定内置Tomcat端口号
server:
  port: 9090

3.2 整合mybatis

mybatis作为持久层框架,传统的SSM整合时需要:
application.xml

1. xml文件配置数据源;
2. xml文件配置事务并开启事务注解;
3. 对mapper接口和xml路径无要求;
4. 实体类设置别名,xml文件要映射;

与springboot整合:

5. application.yml配置;
6. 直接使用@Transactional7. springboot默认有要求,但是可以更改;
3.2.1 数据源
  • SSM:xml文件配置数据源;
  • springboot:application.yml配置;

application.yml
注意冒号后面要空一格

#mysql数据库
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
    url: jdbc:mysql://localhost:3306/db_mis?&characterEncoding=utf8&useSSL=false&allowMultiQueries=true
    username: root
    password: root
多环境配置

application.yml

spring:
  # 运行环境 dev:开发环境|test:测试环境|prod:生产环境
  profiles:
    active: dev

application-dev.yml

server:
  port: 5555

spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://10.20.61.55:3306/netseal_4_data?&characterEncoding=utf8&useSSL=false&allowMultiQueries=true&serverTimezone=UTC
    username: root
    password: root
  mvc:
    pathmatch:
      use-suffix-pattern: true
3.2.3 起别名

指定type-aliases-package,就会自动扫描映射别名,别名就是类名首字母小写。
application.yml

#指定model的位置,自定义扫描映射别名
mybatis:
  type-aliases-package: cn.com.gs.library.model
3.2.4 xml映射

springboot默认mapper接口和mapper.xml需要目录结构一致,才能映射:

  • 可以放在一起,但是不好管理(不推荐);
  • 可以将mapper.xml放在resource目录下
    • 保持和接口路径一致,若包路径很长,也不利于管理(不推荐);
    • 无需保持接口路径一致,需要在application.yml中指定xml的位置(推荐);

application.yml

#指定mapper.xml的位置
mybatis:
  mapper-locations: classpath:/mapper/*.xml
3.2.5 mapper代理
  • 方式一:在每一个mapper接口类上使用@Mapper注解;
  • 方式二:在程序主入口类上使用@MapperScan注解,指定value值为mapper接口路径;
package cn.com.gs.library;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @MapperScan将指定包中的所有接口都标注为DAO层接口,相当于在每一个接口上写@Mapper
 */
@SpringBootApplication
@MapperScan(value = "cn.com.gs.library.dao")
public class LibraryApplication {

	/**
	 * springboot项目程序主入口
	 * 启动main方法相当于启动Tomcat
	 * @param args
	 */
	public static void main(String[] args) {
		SpringApplication.run(LibraryApplication.class, args);
	}

}

3.3 mvc配置

springboot之前的版本认为bookList和bookList.do是同一个请求,从哪一个版本开始就不这样认为了。
我们还是拦截*.do的请求,需要开启use-suffix-pattern,使用后缀匹配功能,springboot就会认为是同一个请求了。
application.yml

spring:
  mvc:
    pathmatch:
      use-suffix-pattern: true

4. 整合其他技术

整合技术就不再使用上述基础demo了,将会使用已经搭建好的libraryMIS图书管理系统。

4.1 Thymeleaf

pom.xml

<!-- 使用thymeleaf引擎 -->
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-thymeleaf -->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-thymeleaf</artifactId>
	<version>2.2.2.RELEASE</version>
</dependency>
<!--热部署,修改代码无需重启,同时yml关闭thymeleaf缓存-->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-devtools</artifactId>
	<!-- optional=true, 依赖不会传递, 该项目依赖devtools; 之后依赖boot项目的项目如果想要使用devtools, 需要重新引入 -->
	<optional>true</optional>
</dependency>

第一步:在标签内引入thymeleaf标签库:xmlns:th=“http://www.thymeleaf.org”

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
	<title>用户登录</title>
</head>
<body>
</body>
</html>

第二步:使用thymeleaf标签

含义标签
取值th:text="${msg}"
4.1.1 引用公共样式文件

目录结构:
|—templates
|—common
|—resource.html
|—main.html
resource.html:相当于是一个通用模板,可以接收传进来的非公共css和script进行渲染。
使用th:fragment定义一个方法,接收title(必须)等信息;
使用th:block和th:replace进行渲染。

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head th:fragment="common_resource(title,links,scripts)">
	<meta charset="UTF-8">
	<title th:replace="${title}">Title</title>
	<link rel="stylesheet" type="text/css" href="/static/h-ui/css/H-ui.min.css" />
	<link rel="stylesheet" type="text/css" href="/static/h-ui.admin/css/H-ui.admin.css" />

	<script type="text/javascript" src="/static/lib/jquery/1.9.1/jquery.min.js"></script>
	<script type="text/javascript" src="/static/lib/layer/2.4/layer.js"></script>
	
	<!--/_footer 作为公共模版分离出去-->
	<th:block th:replace="${links}"></th:block>
	<th:block th:replace="${scripts}"></th:block>
</head>
<body>

</body>
</html>

main.html
在head标签内使用th:replace,
common/resource:指公共模板;
common_resource(传参):~{::title}指将本页面main.html的title值传给模板;语法: ~{::标签}
参数没有要传的值就不写:例如 ~{}

<!DOCTYPE HTML>
<html  xmlns:th="http://www.thymeleaf.org">
<head th:replace="common/resource :: common_resource(~{::title},~{},~{::script})">
	<!--[if lt IE 9]>
	<script type="text/javascript" src="/lib/html5shiv.js"></script>
	<script type="text/javascript" src="/lib/respond.min.js"></script>
	<![endif]-->
	<!--[if IE 6]>
	<script type="text/javascript" src="/lib/DD_belatedPNG_0.0.8a-min.js" ></script>
	<script>DD_belatedPNG.fix('*');</script>
	<![endif]-->
</head>

4.2 shiro

SSM框架整合shiro时,是使用xml配置的方式实现bean注入;在springboot中,没有了xml文件,需要以自定义类+注解的方式完成注入。

  1. 自定义shiro的配置类ShiroConfig.java,使用@Configuration注解标识;
  2. 注入所需的SecurityManager、自定义的SysUserRealm、ShiroFilterFactoryBean;

pom.xml

<properties>
	<shiro.version>1.3.1</shiro.version>
</properties>
<dependencies>
	<!--shiro-->
	<dependency>
		<groupId>org.apache.shiro</groupId>
		<artifactId>shiro-all</artifactId>
		<version>${shiro.version}</version>
	</dependency>
</dependencies>

ShiroConfig.java

package cn.com.gs.library.realm;

import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.mgt.SecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {

	/**
	 * 自定义的认证方法
	 */
	@Bean
	public SysUserRealm sysUserRealm(){
		SysUserRealm sysUserRealm = new SysUserRealm();
		return sysUserRealm;
	}

	/**
	 * 配置securityManager管理器
	 * 指定认证器
	 * 指定缓存器
	 * @return
	 */
	@Bean
	public SecurityManager securityManager(){
		DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
		defaultWebSecurityManager.setRealm(sysUserRealm());
		return defaultWebSecurityManager;
	}

	/**
	 * 开启shiro 注解支持
	 * @param securityManager
	 * @return
	 */
	@Bean
	public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
		AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
		authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
		return authorizationAttributeSourceAdvisor;
	}
	/**
	 * 配置shiro过滤器
	 * 给ShiroFilterFactoryBean指定管理器
	 * 配置过滤器链
	 * @param securityManager
	 * @return
	 */
	@Bean(name = "shiroFilter")
	public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){
		System.out.println("shiroFilter过滤器链");
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
		// 指定管理器
		shiroFilterFactoryBean.setSecurityManager(securityManager);
		// 配置过滤器
		shiroFilterFactoryBean.setLoginUrl("/sysUser/toLogin.do");
		shiroFilterFactoryBean.setSuccessUrl("/sysUser/toMain.do");
		Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
		filterChainDefinitionMap.put("/sysUser/toLogin.do", "anon");
		filterChainDefinitionMap.put("/sysUser/toShiroLogin.do", "anon");
		filterChainDefinitionMap.put("/sysUser/login.do", "anon");
		filterChainDefinitionMap.put("/sysUser/timeout.do", "anon");
		filterChainDefinitionMap.put("/sysUser/toRefuse.do", "anon");
		filterChainDefinitionMap.put("/static/**", "anon");
		filterChainDefinitionMap.put("/2/**", "anon");
		filterChainDefinitionMap.put("/bootstrap/**", "anon");
		filterChainDefinitionMap.put("/css/**", "anon");
		filterChainDefinitionMap.put("/images/**", "anon");
		filterChainDefinitionMap.put("/img/**", "anon");
		filterChainDefinitionMap.put("/js/**", "anon");
		filterChainDefinitionMap.put("/**", "authc");
		shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
		return shiroFilterFactoryBean;
	}
}

SysUserRealm.java

package cn.com.gs.library.realm;

import javax.annotation.Resource;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.authz.permission.PermissionResolver;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

import cn.com.gs.library.common.util.StringUtil;
import cn.com.gs.library.dao.ButtonDao;
import cn.com.gs.library.dao.MenuDao;
import cn.com.gs.library.dao.RoleDao;
import cn.com.gs.library.dao.sysUser.UserDao;
import cn.com.gs.library.model.Button;
import cn.com.gs.library.model.Menu;
import cn.com.gs.library.model.Role;
import cn.com.gs.library.model.User;

public class SysUserRealm extends AuthorizingRealm{
	@Resource
	private UserDao userDao;
	@Resource
	private RoleDao roleDao;
	@Resource
	private MenuDao menuDao;
	@Resource
	private ButtonDao buttonDao;
	
	protected String getRealmName(){
		return "sysUserRealm";
	}
	
	/**
	 * 认证
	 * 
	 * */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		System.out.println("------认证:doGetAuthenticationInfo--------");
		//1.从token中获取用户输入的用户名
		String account = token.getPrincipal().toString();
		System.out.println("输入的账户名:"+account);
		//2.根据用户名去查询数据库,得到账密信息(要求用户名唯一)
		User user = userDao.getUserByAccount(account);
		if(user == null){
			return null;
		}
		/*
		 * 3.将信息封装给SimpleAuthenticationInfo:
		 * Object principal, 	身份信息:可以传入查询出的对象,也可以只传入用户名(传入对象subject就可以获取到)
		 * Object credentials, 	密码信息:传入数据库查询出的密码信息
		 * String realmName		Realm的名字
		 * 
		 * shiro内部将会比对token的密码和传入的数据库密码是否相等,返回SimpleAuthenticationInfo对象
		 * Subject就会得到该SimpleAuthenticationInfo信息,
		 * 最后在controller的登录方法里,由subject判断是否认证通过
		 * 		通过:获取对象存入session
		 * */
		//TODO 密码加密的情况,学习盐值
		SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword(), getRealmName());
		return info;
	}


	/**
	 * 授权
	 * 前台发请求,先走shiro的过滤器链(包含自定义过滤器)
	 * 前台进入页面后,每个被shiro标记的按钮都会发请求来验证权限,效率很低
	 * 请整合redis缓存
	 * 
	 * */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
		Integer menuId = null;
		Integer buttonId = null;
		Menu menu = null;
		Button button = null;
		System.out.println("-----------授权方法------------");
		//1.获取已登录的个人身份信息(认证时传入SimpleAuthenticationInfo的个人信息)
		User user = (User)principal.getPrimaryPrincipal();
		
		//TODO 已集成ehcache 学习整合Redis,先查缓存,缓存中没有再查数据库
		
		//2.获取该用户的已授权按钮信息(菜单级别暂时用NetSeal的写法)
		Role role = roleDao.getRoleById(user.getRoleId());
		//TODO 已实现按钮用shiro标签的实现,基于url的暂未实现
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
		//3.将菜单的code信息(shiro要求的授权规则)传给SimpleAuthorizationInfo
		String menuIds = role.getMenuId();
		String[] menuIdArr = menuIds.split(",");
		for(String tempId : menuIdArr){
			tempId = tempId.trim();
			menuId = Integer.parseInt(tempId);
			menu = menuDao.getMenuById(menuId);
			if(StringUtil.isNotBlank(menu.getMenuCode())) {
				info.addStringPermission(menu.getMenuCode());
			}
			//添加url,在自定义过滤器中控制访问
			info.addStringPermission(menu.getUrl());
		}
		//4.将按钮的code信息(shiro要求的授权规则)传给SimpleAuthorizationInfo
		String buttonIds = role.getButtonId();
		String[] buttonIdArr = buttonIds.split(",");
		for(String tempId : buttonIdArr){
			tempId = tempId.trim();
			buttonId = Integer.parseInt(tempId);
			button = buttonDao.getButtonById(buttonId);
			info.addStringPermission(button.getButtonCode());
		}
		//添加url,在自定义过滤器中控制访问
		info.addStringPermission(button.getUrl());
		return info;
	}

}

4.2.1 Thymeleaf + shiro
  1. 第一步:添加依赖;
    pom.xml
<!--shiro+thymeleaf-->
<dependency>
	<groupId>com.github.theborakompanioni</groupId>
	<artifactId>thymeleaf-extras-shiro</artifactId>
	<version>2.0.0</version>
</dependency>
  1. 第二步:修改ShiroConfig;
    ShiroConfig.java
/**
 * 前台的shiro标签才能生效,注意thymeleaf-extras-shiro版本问题:2.0.0可用
 * @return
 */
@Bean
public ShiroDialect shiroDialect(){
	return new ShiroDialect();
}
4.2.2 配置ehcache缓存

在shiro-all的jar包中,已经包含了ehcache相关类。

ShiroConfig.java

	/**
	 * 配置ehcache缓存
	 */
	@Bean
	public EhCacheManager ehcacheManager(){
		EhCacheManager ehCacheManager = new EhCacheManager();
		return ehCacheManager;
	}
	@Bean
	public SecurityManager securityManager(){
		DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
		defaultWebSecurityManager.setRealm(sysUserRealm());
		defaultWebSecurityManager.setCacheManager(ehcacheManager());
		return defaultWebSecurityManager;
	}

5 bug解决

5.1 There are test failures.

There are test failures.

<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-surefire-plugin</artifactId>
	<configuration>
		<testFailureIgnore>true</testFailureIgnore>            
	</configuration>
</plugin>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值