項目說明:
1.前後端分離的web項目-後臺管理系統
2.外置tomcat,保留web.xml
3.打包方式:war
4.yml配置
應用場景:
1.支持多數據源
2.文件上傳
3.定時
4.異步
5.shiro 權限整合
6.log4j
二.案例
項目結構:
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 https://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.0.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>demo</groupId>
<artifactId>demo</artifactId>
<version>1.0.0</version>
<packaging>war</packaging>
<name>demo</name>
<description>demo</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<jackson.version>2.9.5</jackson.version>
<log4j2.version>2.6.2</log4j2.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<finalName>demo##${project.version}</finalName>
</build>
<dependencies>
<!-- JDBC -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- web項目 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- 剔除掉springBoot內嵌的tomcat-->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- tomcat -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<!-- 日誌 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<!-- 數據庫連接 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.18</version>
</dependency>
<!-- 測試 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 針對tomcat的版本問題而導入包 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.4.3.Final</version>
</dependency>
<!-- 文件上傳 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.2</version>
</dependency>
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- spring session + redis -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<!-- jedis -->
<!-- <dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>-->
<!-- alibaba的druid數據庫連接池 -->
<!-- <dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.9</version>
</dependency>-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.16</version>
</dependency>
<!-- shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.1</version>
</dependency>
<!-- <dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>-->
</dependencies>
</project>
2.application.yml
#切換不同運行環境的配置
spring:
profiles:
active: local
---
#開發配置
spring:
profiles: local
# double mysql datasource
datasource:
# 數據源1
db1:
jdbc-url:
username:
password:
driver-class-name: com.mysql.cj.jdbc.Driver
# type: com.alibaba.druid.pool.DruidDataSource
type: org.apache.commons.dbcp2.BasicDataSource
dbcp2:
initialSize: 2
maxTotal: 4
minIdle: 2
maxWaitMillis: 5000
validationQuery: select 1
maxConnLifetimeMillis: 43200000
timeBetweenEvictionRunsMillis: 14400000
numTestsPerEvictionRun: 500
# 數據源2
#db2:
# session
session:
store-type: none
#停用jmx監控,tomcat多個項目使用相同數據源名時會部署失敗;
jmx:
enabled: false
# mybatis backpage
mybatis:
typeAliasesPackage: demo.model
#mapperLocations: classpath:mapper/*.xml
---
#test environment
spring:
profiles: test
datasource:
# db1
db1:
jdbc-url:
username:
password:
driver-class-name: com.mysql.cj.jdbc.Driver
# type: com.alibaba.druid.pool.DruidDataSource
type: org.apache.commons.dbcp2.BasicDataSource
dbcp2:
initialSize: 2
maxTotal: 4
minIdle: 2
maxWaitMillis: 5000
validationQuery: select 1
maxConnLifetimeMillis: 43200000
timeBetweenEvictionRunsMillis: 14400000
numTestsPerEvictionRun: 500
# db2
#db2:
# session
session:
store-type: none
jmx:
enabled: false
# mybatis backpage
mybatis:
typeAliasesPackage: demo.model
#config-location:
3.web.xml
依舊在webapp->WEB-INF下
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID"
version="3.0">
<display-name>demo</display-name>
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/</url-pattern>
</filter-mapping>
<session-config>//session的有效時間
<session-timeout>300</session-timeout>
<cookie-config>
<name>BACKEND</name>
<path>/</path>
</cookie-config>
</session-config>
</web-app>
4.應用主程序: SpringBootApplication
** * 啓動類 */ // controller,service等掃描路徑 : 默認掃描和啓動類同個包下文件 @EnableScheduling // 啓用spring定時 @EnableTransactionManagement//數據庫事務 @EnableAsync //異步 @SpringBootApplication public class SpringBootBackApplication extends SpringBootServletInitializer { public static void main(String[] args) { SpringApplication.run(SpringBootBackApplication.class, args); } @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { return application.sources(SpringBootBackApplication.class); } /** * 去除multipartResolver 衝突,上傳文件配置 * * @return */ @Bean(name = "multipartResolver") public CommonsMultipartResolver multipartResolver() { CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(); multipartResolver.setDefaultEncoding("UTF-8"); // resolveLazily屬性啓用是爲了推遲文件解析,以在在UploadAction中捕獲文件大小異常 // multipartResolver.setResolveLazily(true); multipartResolver.setMaxInMemorySize(5120); // 上傳文件大小 30M 30*1024*1024 multipartResolver.setMaxUploadSize(30 * 1024 * 1024); return multipartResolver; } /** * 文件上傳配置 * * @return */ // @Bean // public MultipartConfigElement multipartConfigElement() { // MultipartConfigFactory factory = new MultipartConfigFactory(); // // 單個文件最大 // factory.setMaxFileSize("30MB"); // KB,MB // /// 設置總上傳數據總大小 // factory.setMaxRequestSize("30MB"); // return factory.createMultipartConfig(); // } /** * redis * * @return */ // @Bean // public static ConfigureRedisAction configureRedisAction() { // return ConfigureRedisAction.NO_OP; // } }
5.config 配置類
5.1數據源註冊
/* * db1的數據源,事務,工廠等配置 */ @Configuration @MapperScan(basePackages = "hotkidclub.mapper", sqlSessionTemplateRef = "db1SqlSessionTemplate") public class MasterDataSourceConfig { @Bean @ConfigurationProperties(prefix = "spring.datasource.db1") @Primary public DataSource db1DataSource() { return DataSourceBuilder.create().build(); } @Bean @Primary public SqlSessionFactory db1SqlSessionFactory(@Qualifier("db1DataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(dataSource); bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:hotkidclub/mapper/*.xml")); return bean.getObject(); } @Bean @Primary public DataSourceTransactionManager db1TransactionManager(@Qualifier("db1DataSource") DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } @Bean @Primary public SqlSessionTemplate db1SqlSessionTemplate(@Qualifier("db1SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception { return new SqlSessionTemplate(sqlSessionFactory); } }
5.2 Mvc 可根據具體應用場景配置
/** * Mvc相關配置 * * Interceptors:攔截器 * * ArgumentResolvers: 參數綁定解析器 * * ViewControllers:頁面跳轉 * * ResourceHandlers:靜態資源 * * configureDefaultServletHandling:默認靜態資源處理器 * * configureViewResolvers:視圖解析器 * * configureContentNegotiation:配置內容裁決的一些參數 * * addCorsMappings:跨域 * * configureMessageConverters:信息轉換器 */ @Configuration public class WebConfigurer implements WebMvcConfigurer { @Autowired private LoggedInInterceptor loggedInInterceptor; @Autowired private LoginUserInfoResolver loginUserInfoResolver; /** * 配置靜態資源:html,js,css,等等 * * @param registry */ @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { } /** * 註冊攔截器:自定義的攔截器需要通過這裏添加註冊才能生效 * * * @param registry */ // @Override // public void addInterceptors(InterceptorRegistry registry) { // registry.addInterceptor(loggedInInterceptor).addPathPatterns("/**").excludePathPatterns("/login.ctrl");// 添加請求路徑攔截,以及不包含的路徑(不攔截) // registry.addInterceptor(loggedInInterceptor);// 在註冊時不指定攔截路徑,通過攔截器中判斷是否函數帶有isLogin(自定義)的註解來攔截 // } /** * 註冊:參數綁定解析器 * * @param argumentResolvers */ @Override public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) { //argumentResolvers.add(loginUserInfoResolver); } }
6.shiro 相關
6.1 自定義realm
/** * shiro 核心組件-realm : 處理用戶資源權限,實現認證和授權 */ public class ShiroRealm extends AuthorizingRealm { @Autowired RoleService roleService; /** * 認證器 : * <p> * 本平臺用戶登入接口調用-subject.login() : 回調該函數 * </p> * * @param authcToken 憑證信息:可存放用戶賬號密碼 * @return 認證信息:包含當前請求登入的用戶信息 * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException { Subject subject = SecurityUtils.getSubject(); // 1.獲取認證的主體信息:賬戶密碼 UsernamePasswordToken token = (UsernamePasswordToken) authcToken; // 2.可校驗數據庫存儲的賬號密碼(本系統在登入接口的service層已處理) // 3.創建簡單認證信息對象可存放相關信息 : (object, object, 字符串);可傳遞自定義對象,字符內容; // Session session = subject.getSession(); // session.setAttribute("user", token.getPrincipal()); SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(token.getPrincipal(), token.getCredentials(), getName()); return info; } /** * 授權器 : 校驗用戶的角色和權限 * <p> * 本平臺接口配置角色/權限的授權註解時 : 回調該函數 * </p> * * @param principalCollection * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { String principal = (String) principalCollection.getPrimaryPrincipal(); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); // 1.通過用戶名去數據庫查詢該用戶的角色和權限 Map<String, Object> verifyInfo = roleService.getAuthorizationInfo(principal); Set<String> rs = (Set) verifyInfo.get("roleNames"); Set<String> ps = (Set) verifyInfo.get("permissionCodes"); // 2.設置角色信息 if (rs != null && rs.size() > 0) { info.addRoles(rs); } // 3.設置權限信息 if (ps != null && ps.size() > 0) { info.addStringPermissions(ps); } return info; } }
6.2自定義異常
public class AuthenticationFilter extends FormAuthenticationFilter { //未登入 @Override protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { response.setContentType("application/json; charset=utf-8"); response.setCharacterEncoding("UTF-8"); response.getWriter().print("{\"errorCode\":444,\"errorMsg\":\"未登入\"}"); response.flushBuffer(); return false; } }
/** * 全局未授權異常-捕獲&處理 * * 本項目使用場景: shiro鑑權時拋出的未授權異常未處理直接響應客戶端,使用全局異常註解@ControllerAdvice 捕獲處理 * */ @RestControllerAdvice public class UnauthorizedExceptionHandler { /** * 處理UnauthorizedException * * @param req * @param e * @return */ @ExceptionHandler(value = UnauthorizedException.class) public Map<?, ?> defaultExceptionHandler(HttpServletRequest req, Exception e) { Map<String, Object> exception_response = new HashMap<>(); exception_response.put("errorCode", 445); exception_response.put("errorMsg", "拒絕訪問-權限不足"); return exception_response; } }
後期持續更新springBoot的各種應用場景...