目录
Spring的核心是提供了一个容器(container),通常称为Spring应用上下文(Spring application context),它们会创建和管理应用组件。这些组件也可以称为bean,会在Spring应用上下文中装配在一起,从而形成一个完整的应用程序。 将bean装配在一起的行为是通过一种基于依赖注入(dependency injection,DI)的模式实现的。此时,组件不会再去创建它所依赖的组件并管理它们的生命周期,使用依赖注入的应用依赖于单独的实体(容器)来创建和维护所有的组件,并将其注入到需要它们的bean中。通常,这是通过构造器参数和属性访问方法来实现的。
Spring的核心是提供了一个容器(container),通常称为Spring应用上下文(Spring application context),它们会创建和管理应用组件。这些组件也可以称为bean,会在Spring应用上下文中装配在一起,从而形成一个完整的应用程序。
将bean装配在一起的行为是通过一种基于依赖注入(dependency injection,DI)的模式实现的。此时,组件不会再去创建它所依赖的组件并管理它们的生命周期,使用依赖注入的应用依赖于单独的实体(容器)来创建和维护所有的组件,并将其注入到需要它们的bean中。通常,这是通过构造器参数和属性访问方法来实现的。
在历史上,一般通过两种配置方式为Spring应用上下文提供Bean
随着Spring Boot 2.x的引入,Spring自动配置的能力已经大大加强,Spring Boot能够基于类路径中的条目、环境变量和其他因素合理猜测需要配置的组件并将它们装配在一起。Java程序员尽可能多地使用Spring Boot,只有在必要的时候才使用显式配置。
在IntelliJ IDEA中创建新项目
通过地址为https://start.spring.io/初始化项目;
指定项目通用信息:
选择项目Starter:
生成的项目结构:
maven规范
<?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.3.1.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> <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>
可参见本人博客《Maven POM( Project Object Model,项目对象模型 )》及《一图说清maven常见要素》这两篇文章。
/** * SpringBoot应用 */ @SpringBootApplication public class DemoApplication { public static void main(String[] args) { // 运行应用 SpringApplication.run(DemoApplication.class, args); } }
@SpringBootApplication是一个组合注解,它组合了3个其他的注解。
* @SpringBootConfiguration:将该类声明为配置类。尽管这个类目前还没有太多的配置,但是后续我们可以按需添加基于Java的Spring框架配置。这个注解实际上是@Configuration注解的特殊形式。 @EnableAutoConfiguration:启用Spring Boot的自动配置。我们随后会介绍自动配置的更多功能。就现在来说,我们只需要知道这个注解会告诉SpringBoot自动配置它认为我们会用到的组件。 * @ComponentScan:启用组件扫描。这样我们能够通过@Component@Controller、@Service这样的注解声明其他类,Spring会自动发现它们并将它们注册为Spring应用上下文中的组件。
* @SpringBootConfiguration:将该类声明为配置类。尽管这个类目前还没有太多的配置,但是后续我们可以按需添加基于Java的Spring框架配置。这个注解实际上是@Configuration注解的特殊形式。
8. 测试类
// SpringBoot测试 @SpringBootTest class DemoApplicationTests { // 测试方法 @Test void contextLoads() { } }
index()是一个简单的控制器方法。它带有@GetMapping注解,表明如果针对“/”发送HTTP GET请求,那么这个方法将会处理请求。该方法所做的只是返回String类型的index值,该控制器方法中还通过Spring自动注入IndexService服务组件,及调用服务组件方法。
/** * 第一个SpringMVC程序--Controller层 * * @author zhuhuix * @date 2020-07-02 */ @Controller public class IndexController { // Spring注入服务组件 @Autowired private IndexService indexService; @GetMapping("/") public String index(Model model) { String index = indexService.getIndex(); model.addAttribute("index", index); // 返回视图 即index.html return "index"; } }
getIndex()是一个简单的服务方法。该方法所做的只是返回String类型的index值,该服务组件供控制层调用。
/** * 第一个SpringMVC程序--Service层 * * @author zhuhuix * @date 2020-07-02 */ @Service public class IndexService { public String getIndex() { return "hello world!!!"; } }
-- 使用Thymeleaf模板引擎
### application.properties ###ThymeLeaf配置 spring: thymeleaf: #模板的模式,支持 HTML, XML TEXT JAVASCRIPT mode: HTML5 #编码 可不用配置 encoding: UTF-8 #内容类别,可不用配置 content-type: text/html #开发配置为false,避免修改模板还要重启服务器 cache: false #配置模板路径,默认是templates,可以不用配置 prefix: classpath:/templates
-- 定义index.html视图层
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org" > <head> <meta charset="UTF-8"/> <title>Title</title> </head> <body> <p th:text="${index}" /> </body> </html>
•代码变更后应用会自动重启; •当面向浏览器的资源(如模板、JavaScript、样式表)等发生变化时,会自动刷新浏览器
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> <scope>runtime</scope> </dependency> ... </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <!--fork : devtools生效必须设置成true --> <fork>true</fork> </configuration> </plugin> </plugins> </build>
. 在上一小节中创建了第一个DEMO,本章将继续基于SpringMVC框架构建我们的web应用,该应用需要实现用户登记,具体实现步骤如下:
/** * 基于SpringMVC框架开发web应用--用户类 * * @author zhuhuix * @date 2020-07-03 */ public class User implements Serializable { // 用户id private Long id; // 用户名 private String name; // 邮箱 private String email; User(){ } public User(Long id, String name, String email) { this.id = id; this.name = name; this.email = email; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } }
/** * 基于SpringMVC框架开发web应用--用户服务类 * * @author zhuhuix * @date 2020-07-03 */ @Service public class UserService { public static ArrayList<User> users = new ArrayList<>(); // mock数据 public UserService() { users.add(new User(1L, "Mike", "mike@gmail.com")); users.add(new User(2L, "Jack", "jack@gmail.com")); users.add(new User(3L, "Kate", "kate@gmail.com")); users.add(new User(4L, "Mary", "mary@gmail.com")); users.add(new User(5L, "Rose", "rose@gmail.com")); } // 返回所有的用户 public List<User> listUsers() { return users; } // 增加用户 public User saveUser(User user) { user.setId(users.size() + 1L); users.add(user); return user; } }
在Spring MVC框架中,控制器是重要的参与者。它们的主要职责是处理HTTP请求传递给视图以便于渲染HTML(浏览器展现)。
/** * 基于SpringMVC框架开发web应用--用户控制器 * * @author zhuhuix * @date 2020-07-03 */ @RestController @RequestMapping("/user") public class UserController { @Autowired private UserService userService; // 保存用户并返回到用户列表页面 @PostMapping public ModelAndView saveUser(User user) { userService.saveUser(user); return new ModelAndView("redirect:/user");//重定向到list页面 } // 获取创建用户表单页面 @GetMapping("/form") public ModelAndView createForm(Model model) { model.addAttribute("user",new User()); return new ModelAndView("register","userModel",model); } // 获取用户列表页面 @GetMapping public ModelAndView list(Model model) { model.addAttribute("userList", userService.listUsers()); return new ModelAndView("userlist", "userModel", model); } }
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultrag.net.nz/thymeleaf/layout" > <head> <meta charset="UTF-8"> </head> <body> <h3>用户列表</h3> <div> <a th:href="@{/user/form}">创建用户</a> </div> <table border="1"> <thead> <tr> <td>ID</td> <td>Email</td> <td>Name</td> </tr> </thead> <tbody> <tr th:if="${userModel.userList.size()} eq 0"> <td colspan="3">没有用户信息!</td> </tr> <tr th:each="user:${userModel.userList}"> <td th:text="${user.id}"></td> <td th:text="${user.email}"></td> <td th:text="${user.name}"></td> </tr> </tbody> </table> </body> </html>
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultrag.net.nz/thymeleaf/layout" > <head> <meta charset="UTF-8"> </head> <body> <h3>登记用户</h3> <form action="/users" th:action="@{/user}" method="POST" th:object="${userModel.user}"> <input type="hidden" name="id" th:value="*{id}"> 名称:<br> <input type="text" name="name" th:value="*{name}"> <br> 邮箱:<br> <input type="text" name="email" th:value="*{email}"> <input type="submit" value="提交" > </form> </body> </html>
虽然我们已经实现了用户列表与登记新用户,但视图层还存在漏洞,比如用户名称为空的时候不能保存,邮箱输入格式要符合规则,所以程序要对表单输入的内容进行校验。
<dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> <version>6.0.13.Final</version> </dependency> <dependency> <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> <version>2.0.1.Final</version> </dependency>
public class User implements Serializable { // 用户id @NotNull private Long id; // 用户名 @NotBlank(message = "用户名称不能为空") private String name; // 邮箱 @Pattern(message ="邮箱格式不符", regexp = "^[A-Za-z0-9\\u4e00-\\u9fa5]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$") private String email; ... }
@RestController @RequestMapping("/user") public class UserController { @Autowired private UserService userService; // 保存用户并返回到用户列表页面 @PostMapping public ModelAndView saveUser(@Valid User user, Errors errors,Model model) { if (errors.hasErrors()){ model.addAttribute("user",user); if (errors.getFieldError("name")!=null) { model.addAttribute("nameError", errors.getFieldError("name").getDefaultMessage()); } if (errors.getFieldError("email")!=null) { model.addAttribute("emailError", errors.getFieldError("email").getDefaultMessage()); } return new ModelAndView("register","userModel",model); } userService.saveUser(user); //重定向到list页面 return new ModelAndView("redirect:/user"); } ... }
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> </head> <body> <h3>登记用户</h3> <form action="/users" th:action="@{/user}" method="POST" th:object="${userModel.user}"> <input type="hidden" name="id" th:value="*{id}"> 名称:<br> <input type="text" name="name" th:value="*{name}" > <br> 邮箱:<br> <input type="text" name="email" th:value="*{email}"> <br> <input type="submit" value="提交" > <div style="color:red" th:text="${nameError}"></div> <div style="color:red" th:text="${emailError}"></div> </form> </body> </html>
. 在上一小节中基于SpringMVC框架构建了我们的web应用,并在视图层运用模板引擎展示数据及校验表单输入,本章将使用JdbcTemplate及Spring Data实现数据持久化的操作。
MySQL 最流行的关系型数据库管理系统,MySQL所使用的 SQL 语言是用于访问数据库的最常用标准化语言,MySQL由于性能高、成本低、可靠性好,已经成为最流行的开源数据库,因此被广泛地应用在互联网领域中。
DROP DATABASE IF EXISTS user_info; CREATE DATABASE user_info DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci; use user_info; SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for user -- ---------------------------- DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `email` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; SET FOREIGN_KEY_CHECKS = 1;
<!--Mysql依赖包--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> <scope>runtime</scope> </dependency> <!-- 数据库连接池:druid数据源驱动 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency>
#配置数据源 spring.datasource.type=com.alibaba.druid.pool.DruidDataSource spring.datasource.username=root spring.datasource.password=root spring.datasource.url=jdbc:mysql://user_info?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
>
Spring对JDBC的支持要归功于JdbcTemplate类。JdbcTemplate提供了一种特殊的方式,通过这种方式,开发人员在对关系型数据库执行SQL操作的时候能够避免使用JDBC时常见的繁文缛节和样板式代码。
<!-- JDBC --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency>
/** * 基于SpringMVC框架开发web应用--用户服务类 * * @author zhuhuix * @date 2020-07-03 * @date 2020-07-04 增加通过jdbcTemplate处理数据 */ @Service public class UserService { @Autowired private JdbcTemplate jdbcTemplate; // 返回所有的用户 public List<User> listUsers() { return jdbcTemplate.query("select id,name,email from user;", new Object[]{}, new BeanPropertyRowMapper<>(User.class)); } // 增加用户 public int saveUser(User user) { return jdbcTemplate.update("insert into user(name,email) values(?,?);" , user.getName(), user.getEmail()); } }
我们只修改了UserService用户服务类;控制器、视图模板都未做任何改变,接下来我们可以再次尝试运行一下。打开浏览器并访问http://localhost:8080/user。
输入用户信息并提交
查看数据库用户信息表
相对于普通的JDBC,Spring的JdbcTemplate能够极大地简化关系型数据库的使用。但是,你会发现使用JPA会更加简单。接下来我们会继续通过Spring Data框架让数据持久化变得更简单。
在上篇文章中,我们使用mysql数据库与JdbcTemplate简单实现了数据持久化操作,并对web程序进行了测试,本篇文章将继续通过Spring Data框架让数据持久化变得更简单。
Spring Data JDBC -数据访问组件对JDBC的支持。 Spring Data JPA:-基于关系型数据库进行JPA持久化。 Spring Data MongoDB - 持久化到Mongo文档数据库。 Spring Data Redis-持久化到Redis key-value内存数据库。 Spring Data REST:通过Spring Data数据访问组件导出为RESTful资源。 Spring Data Cassandra:持久化到Cassandra数据库。
Spring Data JDBC -数据访问组件对JDBC的支持。
Spring Data JPA:-基于关系型数据库进行JPA持久化。
Spring Data MongoDB - 持久化到Mongo文档数据库。
Spring Data Redis-持久化到Redis key-value内存数据库。
Spring Data REST:通过Spring Data数据访问组件导出为RESTful资源。
Spring Data Cassandra:持久化到Cassandra数据库。
<!--pom.xml--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency>
ALTER TABLE user ADD COLUMN create_time DATETIME; ALTER TABLE user ADD COLUMN update_time DATETIME;
/** * 基于SpringMVC框架开发web应用--用户类 * * @author zhuhuix * @date 2020-07-03 * @date 2020-07-07 添加JPA映射注解 */ @Entity public class User implements Serializable { // 用户id @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; // 用户名 @NotBlank(message = "用户名称不能为空") @Column(name="name") private String name; // 邮箱 @Column(name="email") @Pattern(message ="邮箱格式不符", regexp = "^[A-Za-z0-9\\u4e00-\\u9fa5]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$") private String email; // 创建时间 @Column(name = "create_time") @CreationTimestamp private Timestamp createTime; // 更新时间 @Column(name = "update_time") @UpdateTimestamp private Timestamp updateTime; public User(Long id, String name, String email) { this.id = id; this.name = name; this.email = email; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } }
/** * 基于SpringMVC框架开发web应用--数据操作层 * * @author zhuhuix * @date 2020-07-07 */ public interface UserRepository extends CrudRepository<User,Long> { }
/** * 基于SpringMVC框架开发web应用--用户服务类 * * @author zhuhuix * @date 2020-07-03 * @date 2020-07-04 增加通过jdbcTemplate处理数据 * @date 2020-07-07 将jdbcTemplate处理数据程序改为Spring Data JPA的处理方式 */ @Service public class UserService { @Autowired private UserRepository userRepository; // 返回所有的用户 public List<User> listUsers() { return (List<User>) userRepository.findAll(); } // 增加用户 public User saveUser(User user) { return userRepository.save(user); } }
我们增加了数据层并修改了UserService服务层;控制器、视图模板都未做任何改变,接下来我们可以再次尝试运行一下。打开浏览器并访问http://localhost:8080/user。
测试结论:通过将JdbcTemplate替换成Spring Data JPA,同样实现了用户信息的查询与增加,而且通过JPA CrudRepository接口,我们没有书写一行SQL语句,也实现了数据的增加与查询。
public interface UserRepository extends CrudRepository<User,Long> { // 自定义添加通过用户名称查找用户信息 List<User> findByName(String name); }
按照Spring Data的规范的规定,查询方法以find | read | get开头(比如 find、findBy、read、readBy、get、getBy),涉及查询条件时,条件的属性用条件关键字连接,要注意的是:条件属性以首字母大写。框架在进行方法名解析时,会先把方法名多余的前缀截取掉,然后对剩下部分进行解析。 直接在接口中定义查询方法,如果是符合规范的,可以不用写实现,即不用写SQL。
@Service public class UserService { @Autowired private UserRepository userRepository; ... // 根据名称查找用户 public List<User> searchUser(String name){ return userRepository.findByName(name); } }
/** * 基于SpringMVC框架开发web应用--用户控制器 * * @author zhuhuix * @date 2020-07-03 * @date 2020-07-07 增加用户查找 */ @RestController @RequestMapping("/user") public class UserController { @Autowired private UserService userService; ... // 查找输入页面 @GetMapping("/index") public ModelAndView index(Model model) { model.addAttribute("user", new User()); return new ModelAndView("index", "userModel", model); } // 查找提交并跳转用户列表 @PostMapping("/search") public ModelAndView search(@ModelAttribute User user, Model model) { model.addAttribute("userList", userService.searchUser(user.getName())); return new ModelAndView("userlist", "userModel", model); } }
<!-- index.html--> <!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org" > <head> <meta charset="UTF-8"/> <title>Title</title> </head> <body> <h3>查找用户</h3> <form action="/users" th:action="@{/user/search}" method="POST" th:object="${userModel.user}"> 名称:<br> <input type="text" name="name" th:value="*{name}" > <br> <input type="submit" value="查询" > </form> </body> </html>
Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架。它是用于保护基于Spring的应用程序的标准。
本文将通过Spring Security配置实现web应用的如下功能:
<!-- Spring Security--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security </artifactId> </dependency>
通过将security starter添加到项目的构建文件中,我们得到了如下的安全特性:
我们试着启动一下应用,并访问用户列表页面,发现程序竟然跳转到了一个login登录页面,并要求输入用户和密码。 这是一个HTTP basic认证对话框,提示进行认证。要想通过这个认证,需要一个用户名和密码。用户名为user,而密码则是随机生成的,可以在日志监控界面上查到: 我们试着在登录界面上输入用户名user与图中的密码,进行认证登录: 程序顺利通过了密码认证,并进入到用户列表界面。但显然以上的方式是不符合需求的,我们需要的是:
-- ---------------------------- -- Table structure for admin -- ---------------------------- DROP TABLE IF EXISTS `admin`; CREATE TABLE `admin` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `user_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `user_password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; SET FOREIGN_KEY_CHECKS = 1; /** * 基于SpringMVC框架开发web应用--管理员用户类,用于登录认证 * * @author zhuhuix * @date 2020-07-08 */ @Entity public class Admin implements UserDetails { // 管理员id @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(name="user_name") private String userName; @Column(name="user_password") private String userPassword; @Override public Collection<? extends GrantedAuthority> getAuthorities() { return Arrays.asList(new SimpleGrantedAuthority("ROLE_USER")); } @Override public String getPassword() { return userPassword; } @Override public String getUsername() { return userName; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public void setUserName(String userName) { this.userName = userName; } public void setUserPassword(String userPassword) { this.userPassword = userPassword; } }
/** * 基于SpringMVC框架开发web应用--管理员数据操作层 * * @author zhuhuix * @date 2020-07-08 */ public interface AdminRepository extends CrudRepository<Admin,Long> { // 根据用户名查找管理员 Admin findByUserName(String username); }
/** * 基于SpringMVC框架开发web应用--管理员服务层 * * @author zhuhuix * @date 2020-07-08 */ @Service public class AdminService implements UserDetailsService { private final Logger logger = LoggerFactory.getLogger(Logger.class); @Autowired private AdminRepository adminRepository; @Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { Admin admin = adminRepository.findByUserName(s); if (admin == null) { logger.error("管理员" + s + "未找到"); throw new UsernameNotFoundException("User " + s + "not found"); } return admin; } // 保存管理员 public Admin save(Admin admin){ return adminRepository.save(admin); } }
/** * Spring Security配置类 * * @author zhuhuix * @date 2020-07-08 */ @Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private AdminService adminService; @Override protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception { authenticationManagerBuilder .userDetailsService(adminService) .passwordEncoder(new BCryptPasswordEncoder()); } }
以上通过建立管理员信息表,及通过JPA定义数据处理层,编写获取管理员信息的服务实现,最后配置Spring Security Web安全类,实现了自定义的登录验证方法,下面具体来测试一下: web应用程序已经实现了自定义的用户登录验证。
/** * 基于SpringMVC框架开发web应用--管理员注册控制器 * * @author zhuhuix * @date 2020-07-08 */ @RestController @RequestMapping("/admin") public class AdminController { @Autowired private AdminService adminService; // 管理注册页 @GetMapping public ModelAndView registerForm(Model model) { model.addAttribute("admin", new Admin()); return new ModelAndView("registration", "adminModel", model); } // 提交注册信息 @PostMapping("/register") public ModelAndView save(Admin admin) { BCryptPasswordEncoder bCryptPasswordEncoder =new BCryptPasswordEncoder(); admin.setUserPassword(bCryptPasswordEncoder.encode(admin.getPassword())); if (adminService.save(admin) != null) { return new ModelAndView("redirect:/login "); } else { return null; } } }
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> </head> <body> <h3>管理员注册</h3> <form action="/admin" th:action="@{/admin/register}" method="POST" th:object="${adminModel.admin}"> 名称:<br> <input type="text" name="userName" th:value="*{userName}" > <br> 输入密码:<br> <input type="password" name="userPassword" th:value="*{userPassword}"> <br> <input type="submit" value="注册" > </form> </body> </html>
/** * Spring Security配置类 * * @author zhuhuix * @date 2020-07-08 */ @Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private AdminService adminService; // 自定义用户验证 @Override protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception { authenticationManagerBuilder .userDetailsService(adminService) .passwordEncoder(new BCryptPasswordEncoder()); } // 保护web请求的安全性规则 @Override protected void configure(HttpSecurity http) throws Exception{ http.authorizeRequests() .antMatchers("/user/**").access("hasRole('ROLE_USER')") .antMatchers("/admin/**").permitAll() .and().formLogin().defaultSuccessUrl("/user") .and().httpBasic(); } }
访问管理员注册页面进行注册
使用注册的管理员帐户进行登录验证
Spring的环境抽象是各种配置属性的一站式服务。它抽取了原始的属性,这样需要这些属性的bean就可以从Spring本身中获取了。Spring环境会拉取多个属性源,包括: JVM系统属性; 操作系统环境变量; 命令行参数; 应用属性配置文件。
Spring的环境抽象是各种配置属性的一站式服务。它抽取了原始的属性,这样需要这些属性的bean就可以从Spring本身中获取了。Spring环境会拉取多个属性源,包括:
###数据源配置 spring.datasource.type=com.alibaba.druid.pool.DruidDataSource spring.datasource.username=root spring.datasource.password=root spring.datasource.driver-class-name=net.sf.log4jdbc.sql.jdbcapi.DriverSpy spring.datasource.url=jdbc:log4jdbc:mysql://localhost:3306/user_info?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true #thymelea模板配置 spring.thymeleaf.prefix=classpath:/templates/ spring.thymeleaf.suffix=.html spring.thymeleaf.mode=HTML5 spring.thymeleaf.encoding=UTF-8 spring.thymeleaf.content-type=text/html spring.thymeleaf.cache=false spring.resources.chain.strategy.content.enabled=true spring.resources.chain.strategy.content.paths=/**
spring: port:8080
显然具有层次关系的配置文件更易于理解与书写,接下来我们将使用application.yml取代application.properties完成各种属性配置。
###数据源配置 spring: datasource: type: com.alibaba.druid.pool.DruidDataSource username: root password: root driver-class-name: net.sf.log4jdbc.sql.jdbcapi.DriverSpy url: jdbc:log4jdbc:mysql://localhost:3306/user_info?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
#thymelea模板配置 thymeleaf: prefix: classpath:/templates/ suffix: .html mode: HTML5 encoding: UTF-8 content-type: text/html cache: false
默认情况下,Spring Boot通过Logback配置日志,日志会以INFO级别写入到控制台中,我们希望重新配置显示日志的格式; 同时我们希望通过监控sql日志输出到控制台,并将输出的信息进行筛选打印;
<dependency> <groupId>org.bgee.log4jdbc-log4j2</groupId> <artifactId>log4jdbc-log4j2-jdbc4.1</artifactId> <version>1.16</version> </dependency>
log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator log4jdbc.auto.load.popular.drivers=false log4jdbc.drivers=com.mysql.cj.jdbc.Driver
<?xml version="1.0" encoding="UTF-8"?> <configuration scan="true" scanPeriod="30 seconds" debug="false"> <contextName>web demo</contextName> <property name="log.charset" value="utf-8" /> <property name="log.pattern" value="%black(%contextName-) %red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}) - %gray(%msg%n)" /> <!--输出到控制台--> <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>${log.pattern}</pattern> <charset>${log.charset}</charset> </encoder> </appender> <!--普通日志输出到控制台--> <root level="info"> <appender-ref ref="console" /> </root> <!--监控sql日志输出 --> <logger name="jdbc.sqlonly" level="INFO" additivity="false"> <appender-ref ref="console" /> </logger> <logger name="jdbc.resultset" level="ERROR" additivity="false"> <appender-ref ref="console" /> </logger> <!-- 如想看到表格数据,将OFF改为INFO --> <logger name="jdbc.resultsettable" level="OFF" additivity="false"> <appender-ref ref="console" /> </logger> <logger name="jdbc.connection" level="OFF" additivity="false"> <appender-ref ref="console" /> </logger> <logger name="jdbc.sqltiming" level="OFF" additivity="false"> <appender-ref ref="console" /> </logger> <logger name="jdbc.audit" level="OFF" additivity="false"> <appender-ref ref="console" /> </logger> </configuration>
为了支持配置属性的注入,Spring Boot提供了@ConfigurationProperties注解。将它放到Spring 应用上下文的 bean之后,它就会为该bean中那些能够根据Spring环境注入值的属性赋值。
#管理员默认密码 admin: password: 123456
/** * 基于SpringMVC框架开发web应用--管理员用户默认密钥 * * @author zhuhuix * @date 2020-07-09 */ @Component @ConfigurationProperties(prefix = "admin") public class DefaultPasswordProperties { private String password; public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
/** * 基于SpringMVC框架开发web应用--管理员注册控制器 * * @author zhuhuix * @date 2020-07-08 * @date 2020-07-09 注册密码为空时使用自定义的默认密码属性 */ @RestController @RequestMapping("/admin") public class AdminController { @Autowired private AdminService adminService; @Autowired private DefaultPasswordProperties defaultPasswordProperties; // 管理注册页 @GetMapping public ModelAndView registerForm(Model model) { model.addAttribute("admin", new Admin()); return new ModelAndView("registration", "adminModel", model); } // 提交注册信息 @PostMapping("/register") public ModelAndView save(Admin admin) { // 如果注册密码为空,则赋值默认密码 if (StringUtils.isEmpty(admin.getPassword())) { admin.setUserPassword(defaultPasswordProperties.getPassword()); } BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder(); admin.setUserPassword(bCryptPasswordEncoder.encode(admin.getPassword())); if (adminService.save(admin) != null) { return new ModelAndView("redirect:/login "); } else { return null; } } }
注册admin2管理员,密码保持为空,并提交
用admin2管理员,密码为123456进行登录
登录成功
本节将进入到新的单元:Spring与应用的集成,今天先实现集成REST API服务。
微服务架构,前后端分离目前已成为互联网项目开发的业界标准,其核心思想就是前端(APP、小程序、H5页面等)通过调用后端的API接口,提交及返回JSON数据进行交互。
在前几篇文章中我们用了模板引擎开发了多页应用(MultiPage Application,MPA),我们将在原有基础上按以下步骤实现集成API服务:
创建用户管理的Restful Api(UserRestfulApi.java)的接口,实现增加、删除、修改、查找用户信息的API交互服务;
集成Swagger2,对上述API接口进行测试。
/** * 基于SpringMVC框架开发web应用--用户restful api * 增加、删除、修改、查找用户信息的API交互服务 * * @author zhuhuix * @date 2020-07-10 */ @RestController @RequestMapping("/user/api") public class UserRestfulApi { @Autowired private UserService userService; // 增加用户信息 @PostMapping public ResponseEntity<User> addUser(User user) { return ResponseEntity.ok(userService.saveUser(user)); } // 根据id删除用户 @DeleteMapping("/{id}") public ResponseEntity deleteUser(@PathVariable Long id) { userService.deleteUser(id); return ResponseEntity.ok(HttpStatus.OK); } // 根据id修改用户 @PutMapping("/{id}") public ResponseEntity<User> updateUser(@RequestBody User user) { return ResponseEntity.ok(userService.saveUser(user)); } // 根据id查找用户 @GetMapping("/{id}") public ResponseEntity<User> findUser(@PathVariable Long id) { return ResponseEntity.ok(userService.findUser(id)); } }
/** * 基于SpringMVC框架开发web应用--用户服务类 * * @author zhuhuix * @date 2020-07-03 * @date 2020-07-04 增加通过jdbcTemplate处理数据 * @date 2020-07-07 将jdbcTemplate处理数据程序改为Spring Data JPA的处理方式 * @date 2020-07-10 增加 */ @Service public class UserService { @Autowired private UserRepository userRepository; // 返回所有的用户 public List<User> listUsers() { return (List<User>) userRepository.findAll(); } // 保存用户 public User saveUser(User user) { return userRepository.save(user); } // 删除用户 public void deleteUser(Long id){ userRepository.deleteById(id); } // 查找用户 public User findUser(Long id){ return userRepository.findById(id).get(); } // 根据名称查找用户 public List<User> searchUser(String name){ return userRepository.findByName(name); } }
/** * 基于SpringMVC框架开发web应用--数据操作层 * * @author zhuhuix * @date 2020-07-07 */ public interface UserRepository extends CrudRepository<User,Long> { // 自定义添加通过用户名称查找用户信息 List<User> findByName(String name); }
Swagger2 作为一个规范和完整的框架,可以用于生成、描述、调用和可视化 RESTful 风格的 Web 服务: 1、 接口文档在线自动生成,文档随接口变动实时更新,节省维护成本 2、 支持在线接口测试,不依赖第三方工具
<!-- RESTful APIs swagger2 --> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.9.2</version> <exclusions> <exclusion> <groupId>io.swagger</groupId> <artifactId>swagger-annotations</artifactId> </exclusion> <exclusion> <groupId>io.swagger</groupId> <artifactId>swagger-models</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.9.2</version> </dependency> <dependency> <groupId>io.swagger</groupId> <artifactId>swagger-annotations</artifactId> <version>1.5.21</version> </dependency> <dependency> <groupId>io.swagger</groupId> <artifactId>swagger-models</artifactId> <version>1.5.21</version> </dependency>
/** * Swagger2配置类 * * @author zhuhuix * @date 2020-07-10 */ @Configuration @EnableSwagger2 public class SwaggerConfig { @Bean @SuppressWarnings("all") public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .enable(true) .apiInfo(apiInfo()) .select() .paths(Predicates.not(PathSelectors.regex("/error.*"))) .build(); } private ApiInfo apiInfo() { return new ApiInfoBuilder() .title("Restful Api接口测试") .version("1.0") .build(); } }
/** * Spring Security配置类 * * @author zhuhuix * @date 2020-07-08 * @date 2020-07-10 禁用跨域请求的安全验证 */ @Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private AdminService adminService; // 自定义用户验证 @Override protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception { authenticationManagerBuilder .userDetailsService(adminService) .passwordEncoder(new BCryptPasswordEncoder()); } // 保护web请求的安全性规则 @Override protected void configure(HttpSecurity http) throws Exception{ http.authorizeRequests() .antMatchers("/user/**").access("hasRole('ROLE_USER')") .antMatchers("/admin/**").permitAll() .and().formLogin().defaultSuccessUrl("/user") .and().httpBasic() // 禁用 CSRF .and().csrf().disable(); } }
至此我们完成了Spring集成restful api服务,并通过集成Swagger2,简单直观地对http的各种请求进行了完整地测试,下面做个总结:
原文链接:https://www.cnblogs.com/zhuhuix/p/13280080.html