最近在学习Spring Boot,动手搭建了一个demo。打算作一个公用的工程,方便之后玩得开,目前还在学习,系统还会进一步扩展。
这是个人项目下载路径:github springboot 项目路径
下载:git clone git@github.com:NikolaZhang/SpringBoot.BookSystem.git
下面是从代码层面对项目中内容的介绍。css
<?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> <groupId>com.demo</groupId> <artifactId>booksys</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>booksys</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.0.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <version>2.0.3.RELEASE</version> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <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> <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-autoconfigure --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-autoconfigure</artifactId> </dependency> <!--使用数据源--> <!-- https://mvnrepository.com/artifact/com.oracle/ojdbc7 --> <dependency> <groupId>com.oracle</groupId> <artifactId>ojdbc8</artifactId> <version>12.2.0.1.0</version> </dependency> <!-- 这里使用的是JPA包,也可以使用JDBC包进行 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <!-- 使用mybatis --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.1.1</version> </dependency> <!--热部署--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency> <!-- 使用driuid数据源 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.12</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <fork>true</fork> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> </plugin> </plugins> </build> </project>
能够看到咱们的项目支持thymeleaf模板,druid数据源,oracle数据库,数据持久化能够使用mybatis(注解方式和配置xml方式)、jpa(暂时支持注解),日志,热部署。这里要单独说一下数据库的jar包若是没法下载,须要手动下载后,添加到库中。项目中另外添加了druid的sql监控,拦截器等。html
e.g. 项目结构暂时就是下面这个样子,明显偷晴较重(后台不少,前台几乎没有)。不过这并不影像咱们的开发。只要你有一个postman。。。
java
先来看一下咱们的实体类。git
package com.demo.booksys.domin; import javax.persistence.*; @Entity @Table(name = "SYS_USER_MST") public class UserModel { @Id @Column(name = "CODE", nullable = false, unique = true) private String code; @Column(name = "NAME", nullable = false) private String name; @Column(name = "PASSWORD", nullable = false) private String password; @Column(name = "DESCRIPTION") private String description; public String getCode() { return code; } public void setCode(String code) { this.code = code; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } @Override public String toString() { return "UserModel{" + "code='" + code + '\'' + ", name='" + name + '\'' + ", password='" + password + '\'' + ", description='" + description + '\'' + '}'; } }
这里咱们须要使用@Table
指定实体类对应的数据表名。在JPA中,咱们能够直接使用UserModel
替换sql中的SYS_USER_MST
。github
这里咱们以UserController为例。web
package com.demo.booksys.controller.user; import com.demo.booksys.domin.UserModel; import com.demo.booksys.service.user.UserService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpSession; import java.util.List; import java.util.Map; @Controller @RequestMapping(value = "/UserController") public class UserController { Logger logger = LoggerFactory.getLogger(UserController.class); @Autowired private UserService userService; // 全部请求使用RESTFUL风格,再也不使用?&拼接参数。 Nikola Zhang 【2018/11/22 21:50】 // 使用GET查询数据库 Nikola Zhang 【2018/11/22 21:49】 @RequestMapping(value = "/findUserByCode/{code}", method = RequestMethod.GET) @ResponseBody public UserModel findUserByCode(@PathVariable("code") String code) { logger.info("->findByCode"); UserModel user = userService.findUserByCode(code); return user; } // 使用POST保存用户信息 Nikola Zhang 【2018/11/22 22:00】 @RequestMapping(value = "/saveUser", method = RequestMethod.POST) @ResponseBody public List<UserModel> saveUser(UserModel userModel){ userService.saveUser(userModel); return userService.findAll(); } @RequestMapping(value = "/updateUser", method = RequestMethod.PATCH) @ResponseBody public List<UserModel> updateUser(UserModel userModel){ userService.updateUser(userModel.getName(), userModel.getCode()); return userService.findAll(); } @RequestMapping(value = "/deleteUser/{code}", method = RequestMethod.DELETE) @ResponseBody public int deleteUserByCode(@PathVariable("code") String code){ logger.info("->删除"+code); return userService.deleteUserByCode(code); } @RequestMapping(value = "/queryUserRoleInfo", method = RequestMethod.GET) @ResponseBody public Map queryUserRoleInfo() { return userService.queryUserRoleInfo(); } @RequestMapping("/findAll") @ResponseBody public List<UserModel> findAll() { logger.info("->findAll"); List<UserModel> users = userService.findAll(); logger.info(users.size()+""); return users; } @RequestMapping("/login/{usercode}/{password}") public String toLogin(@PathVariable("usercode") String usercode, @PathVariable("password") String password, HttpSession session) { logger.info("->toLogin"); int res = userService.countByCodeAndPassword(usercode, password); if(res == 1){ session.setAttribute("usercode", usercode); return "Success"; } else { return "Error"; } } }
能够看到咱们的请求和SSM框架中习惯使用的****/*.action?a=1&b=2
是不一样的。查询使用RequestMethod.GET
;增长使用RequestMethod.POST
;修改使用RequestMethod.PATCH
;删除使用RequestMethod.DELETE
。spring
对于/login
请求咱们须要进行登陆验证。数据库存在表单数据,则增长当前用户的code做为登陆成功的标记。sql
这一部分没有什么,咱们仍是用UserService展现。数据库
package com.demo.booksys.service.user; import com.demo.booksys.domin.UserModel; import com.demo.booksys.persistence.user.UserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Example; import org.springframework.stereotype.Service; import java.util.List; import java.util.Map; import java.util.Optional; @Service public class UserService { @Autowired private UserRepository userRepository; public UserModel findUserByCode(String code){ List<UserModel> users = userRepository.findUserModelsByCode(code); if(users != null) { int userSize = users.size(); if(userSize != 0) { return users.get(0); } else { return null; } } else{ return null; } } public List<UserModel> findAll() { return userRepository.findAll(); } public void saveUser(UserModel userModel){ userRepository.save(userModel); } // public void updateUser(UserModel userModel) { // userRepository.updateUser(userModel); // } public void updateUser(String name, String code) { userRepository.updateUser(name, code); } public int deleteUserByCode(String code) { return userRepository.deleteUserByCode(code); } public Map queryUserRoleInfo(){ return userRepository.queryUserRoleInfo(); } public int countByCodeAndPassword(String usercode, String password) { return userRepository.countByCodeAndPassword(usercode, password); } }
package com.demo.booksys.persistence.user; import com.demo.booksys.domin.UserModel; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.transaction.annotation.Transactional; import java.util.List; import java.util.Map; public interface UserRepository extends JpaRepository<UserModel, Long> { @Query("select um from UserModel um where um.code = ?1") List<UserModel> findUserModelsByCode(String code); @Transactional @Modifying @Query("update UserModel set name=?1 where code=?2") int updateUser(String name, String code); @Transactional @Modifying @Query(value = "delete from UserModel where code = ?1") int deleteUserByCode(String code); // 使用JPA进行关联查询最好给查询结果设置别名 Nikola Zhang 【2018/11/24 11:28】 @Query(value = "select sumt.CODE, sumt.NAME, sumt.PASSWORD, srm.NAME rolename from sys_user_mst sumt join sys_user_role sur on sumt.code=sur.usercode join sys_role_mst srm on srm.code=sur.rolecode", nativeQuery = true) Map queryUserRoleInfo(); // 计数 int countByCodeAndPassword(String code, String password); }
@MapperScan("com.demo.booksys.persistence.role")
去扫描咱们的接口加载入上下文。package com.demo.booksys.persistence.role; import com.demo.booksys.domin.RoleModel; import org.apache.ibatis.annotations.Delete; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; import java.util.HashMap; import java.util.List; import java.util.Map; public interface RoleMapper { @Select("select code, name from sys_role_mst where code=#{code}") RoleModel queryRoleByCode(String code); @Insert("insert into sys_role_mst(code, name) values(#{code}, #{name})") // @Options(useGeneratedKeys=true,keyProperty="id", keyColumn="id") void saveRole(RoleModel roleModel); @Update("update sys_role_mst set name=#{name} where code=#{code}") void updateRole(RoleModel roleModel); @Delete("delete from sys_user_role where code=#{code}; " ) void deleteRole(RoleModel roleModel); @Select("select * from sys_role_mst srm join sys_user_role sur on srm.code=sur.rolecode join sys_user_mst sumt on sumt.code=sur.usercode") Map<String, String> queryUserRoleInfo(); List<HashMap> queryRoleByMap(HashMap hashMap); }下面是对应接口中
List<HashMap> queryRoleByMap(HashMap hashMap);
方法的Mapper xml配置。<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace="com.demo.booksys.persistence.role.RoleMapper"> <!--resultMap对应的是表与实体类的映射 - type 数据库表对应的实体类,别名或完整类名均可以--> <resultMap id="BaseResultMap" type="com.demo.booksys.domin.RoleModel" > <result column="CODE" property="code" jdbcType="VARCHAR" /> <result column="NAME" property="name" jdbcType="VARCHAR" /> </resultMap> <select id="queryRoleByMap" parameterType="java.util.HashMap" resultType="java.util.HashMap"> SELECT * FROM SYS_ROLE_MST WHERE 1=1 <if test="code!=null and code!='' " > and CODE = #{code} </if> </select> </mapper>
这个本因该放在前面介绍的。可是因为它和咱们以后说到的拦截器,druid有很大关系。因此放在了较后面的位置。apache
spring: datasource: url: jdbc:oracle:thin:@localhost:1521:orcl driver-class-name: oracle.jdbc.OracleDriver username: C##nikola password: 123654 type: com.alibaba.druid.pool.DruidDataSource initialSize: 5 maxActive: 20 minIdle: 5 maxWait: 60000 poolPreparedStatements: true maxOpenPreparedStatements: 100 testWhileIdle: true timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 filters: commons-log.connection-logger-name: stat,wall,log4j userGlobalDataSource: true connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500 logging: level: org: hibernate: SQL: DEBUG com: demo: booksys: persistence: DEBUG mybatis: typeAliasesPackage: comdemo.booksys.domin mapperLocations: classpath:/mapper/*Mapper.xml
咱们在yml中配置了sql日志,druid数据源,mapper映射。
这里须要注意:
方框中的属性是spring DataSource没有的。须要咱们自定义配置添加。咱们使用@ConfigurationProperties将配置文件中的属性绑定到DataSource(DataSource要使用@Bean注入,毕竟咱们不能再jar中标注@Component)中。
自定义配置以下:
package com.demo.booksys.webconfig; import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.support.http.StatViewServlet; import com.alibaba.druid.support.http.WebStatFilter; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.sql.DataSource; import java.util.Arrays; import java.util.HashMap; import java.util.Map; /************************************************ *@ClassName : DruidConfig *@Description : Druid配置,没有此配置文件yml中的属性不会生效 *@Author : NikolaZhang *@Date : 【2018/11/24 20:06】 *@Version : 1.0.0 *************************************************/ @Configuration public class DruidConfig { @ConfigurationProperties(prefix = "spring.datasource") @Bean public DataSource druid() { return new DruidDataSource(); } // 配置Druid监控 Nikola Zhang 【2018/11/24 20:09】 // 1. 配置一个管理后台的servlet @Bean public ServletRegistrationBean statViewServlet() { ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*"); Map<String, String> initMap = new HashMap<>(); // 设置druid初始化参数 initMap.put("loginUsername","admin"); initMap.put("loginPassword","admin"); servletRegistrationBean.setInitParameters(initMap); return servletRegistrationBean; } // 2. 配置一个监控的filter @Bean public FilterRegistrationBean webStatFilter() { FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); filterRegistrationBean.setFilter(new WebStatFilter()); Map<String, String> initMap = new HashMap<>(); initMap.put("exclusions","*.js,*.css,/druid/*"); filterRegistrationBean.setInitParameters(initMap); filterRegistrationBean.setUrlPatterns(Arrays.asList("/*")); return filterRegistrationBean; } }
package com.demo.booksys.webconfig; import com.demo.booksys.util.datautil.StringUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.PrintWriter; public class InterceptorConfig implements HandlerInterceptor { private static final Logger logger = LoggerFactory.getLogger(InterceptorConfig.class); @Override public boolean preHandle(HttpServletRequest httpServletRequest , HttpServletResponse httpServletResponse, Object o) throws Exception { logger.info("---------------------开始进入请求地址拦截----------------------------"); HttpSession session = httpServletRequest.getSession(); String usercode = (String)session.getAttribute("usercode"); logger.info("从session中获取usercode: "+ usercode); if(!StringUtil.isEmpty(usercode)){ return true; } else { PrintWriter printWriter = httpServletResponse.getWriter(); printWriter.write("{code:0,message:\"session is invalid,please login again!\"}"); return false; } } @Override public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse , Object o, ModelAndView modelAndView) throws Exception { logger.info("--------------处理请求完成后视图渲染以前的处理操做---------------"); } @Override public void afterCompletion(HttpServletRequest httpServletRequest , HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { logger.info("---------------视图渲染以后的操做-------------------------"); } }
上面的拦截只是咱们自定义的类,并无添加到springboot的环境中。方法是继承WebMvcConfigurationSupport
重写addInterceptors
方法,使用注册咱们的拦截器。注意没有@Configuration
咱们的WebAppConfig
也不会注入到上下文中。
package com.demo.booksys.webconfig; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; @Configuration public class WebAppConfig extends WebMvcConfigurationSupport { // 定义拦截器对除进入登陆界面的全部请求进行拦截 Nikola Zhang 【2018/11/24 14:41】 // 使用localhost:8080 登陆系统,经过校验设置session public void addInterceptors(InterceptorRegistry registry) { //注册自定义拦截器,添加拦截路径和排除拦截路径 registry.addInterceptor(new InterceptorConfig()).addPathPatterns("/**") .excludePathPatterns("/") .excludePathPatterns("/UserController/login/**"); } @Override // 定义视图跳转 protected void addViewControllers(ViewControllerRegistry registry) { // 添加无业务跳转 // 直接访问的请求进入登陆界面 Nikola Zhang 【2018/11/24 14:42】 registry.addViewController("/").setViewName("Login"); } }
这里简单说明一下,InterceptorConfig
中的preHandle
咱们进行了登陆用户的session验证。在WebAppConfig
中对除localhost:8080
和localhost:8080/UserController/login/**
这样的请求进行拦截,验证其session。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>登陆界面</title> </head> <body> <h1>Login</h1> </body> </html>
由于我用了postman因此整个开发过程,根本没有理会这玩意,之后再学thymeleft吧。
呵呵~~ o( ̄︶ ̄)o
Okay,总算说完了,好累。。。