• 学习过SpringData Jpa课程
• 学习过SpringData Redis课程
• 学习过 SpringBoot 初级阶段
Spring Boot是一个简化Spring开发的框架。用来监护spring应用开发,约定大于配置,去繁就简,just run 就能创建一个独立的,产品级的应用。我们在使用Spring Boot时只需要配置相应的Spring Boot就可以用所有的Spring组件,简单的说,spring boot就是整合了很多优秀的框架,不用我们自己手动的去写一堆xml配置然后进行配置。从本质上来说,Spring Boot就是Spring,它做了那些没有它你也会去做的Spring Bean配置。
搭建一个简单的环境 ,实现表单的校验 .包括对用户名 ,密码 ,年龄 ,邮箱等进行校验.
见本人上篇博文 SpringBoot初级阶段之SpringBoot 整合SpringMVC+MyBatis
注意 : th:errors 会获取响应的数据 .有,会将数据取出,没有会报异常
<font color="red" th:errors="${users.name}">font>
add.html
<html>
<head>
<meta charset="UTF-8">
<title>Insert title heretitle>
head>
<body>
<form th:action="@{/save}" method="post">
用户姓名:<input type="text" name="name" />
<font color="red" th:errors="${users.name}">font><br />
用户密码:<input type="password" name="password" />
<font color="red" th:errors="${users.password}">font><br />
用户年龄:<input type="text" name="age" />
<font color="red" th:errors="${users.age}">font><br />
用户邮箱:<input type="text" name="email" />
<font color="red" th:errors="${users.email}">font><br />
<input type="submit" value="添加" />
form>
body>
html>
步骤
1.在页面的跳转的Controller方法中注入需要校验的对象
2.在添加对象的Controller方法中在该对象的参数旁加 @Valid注解
3.在添加对象的Controller方法中加入BindingResult的对象 result ,用于封装校验结果
4.对结果进行判断 ,没有有错误就重新返回该页面if (result.hasErrors()) {return "add"; }
@Controller
public class UsersController {
/**
* 解决异常的方式。可以在跳转页面的方法中注入一个Uesrs 对象。
* 注意:由于springmvc 会将该对象放入到Model 中传递。key 的名称会使用
* 该对象的驼峰式的命名规则来作为key。 参数的变量名需要与对象的名称相同。将首字母小写。
*
* @param users
* @return
*/
@RequestMapping("/addUser")
public String addUser(Users users) {
return "add";
}
/**
* 完成用户添加
*
* @Valid 开启对Users 对象的数据校验
* @param result 封装了校验的结果
* @return
*/
@RequestMapping("/save")
public String saveUser(@Valid Users users, BindingResult result) {
if (result.hasErrors()) {
return "add";
}
System.out.println(users);
return "ok";
}
}
步骤
1.在页面的跳转的Controller方法中注入需要校验的对象 并加入@ModelAttribute()注解
2.在添加对象的Controller方法中在该对象的参数旁加 @Valid注解 ,在其前方加 @ModelAttribute()注解
3.在添加对象的Controller方法中加入BindingResult的对象 result ,用于封装校验结果
4.对结果进行判断 ,没有有错误就重新返回该页面if (result.hasErrors()) {return "add"; }
5.总结 :@ModelAttribute() 注解的作用相当于为 users 对象修改别名 ,使用后,前端获取方式应该同样改变,类似下面这样
用户姓名:<input type="text" name="name" />
<font color="red" th:errors="${aa.name}">font><br />
@Controller
public class UsersController {
/**
* 解决异常的方式。可以在跳转页面的方法中注入一个Uesrs 对象。
* 注意:由于springmvc 会将该对象放入到Model 中传递。key 的名称会使用
* 该对象的驼峰式的命名规则来作为key。 参数的变量名需要与对象的名称相同。将首字母小写。
*
* @param users
* @return
*/
@RequestMapping("/addUser")
public String addUser(@ModelAttribute("aa")Users users) {
return "add";
}
/**
* 完成用户添加
*
* @Valid 开启对Users 对象的数据校验
* @param result 封装了校验的结果
* @return
*/
@RequestMapping("/save")
public String saveUser(@ModelAttribute("aa")@Valid Users users, BindingResult result) {
if (result.hasErrors()) {
return "add";
}
System.out.println(users);
return "ok";
}
}
实现表单校验最根本的一步就是要在实体类上加入相应的注解
public class Users {
@NotBlank(message="用户名不能为空") //非空校验
@Length(min=3,max=6)
private String name;
@NotBlank(message="密码不能为空") //密码非空校验
private String password;
@Min(value=15,message="年龄不能低于15")
private Integer age;
//相关方法省略
}
注解名称 | 作用 |
---|---|
@NotBlank | 判断字符串是否为null 或者是空串(去掉首尾空格)。 |
@NotEmpty: | 判断字符串是否null 或者是空串。 |
@Length | 判断字符的长度(最大或者最小) |
@Min | 判断数值最小值 |
@Max | 判断数值最大值 |
判断邮箱是否合法 |
注意 :
SpringBoot 默认的处理异常的机制:
SpringBoot 默认的已经提供了一套处理异常的机制。一旦程序中出现了异常SpringBoot 会向 /error 的url 发送请求。在springBoot 中提供了一个叫BasicExceptionController 来处理 /error 请求,然后跳转到默认显示异常的页面来展示异常信息。
如果我们需要将所有的异常同一跳转到自定义的错误页面, 需要在 src/main/resources/templates 目录下创建error.html 页面。注意:名称必须叫error
error.html
<html>
<head>
<meta charset="UTF-8">
<title>Insert title heretitle>
head>
<body>
错误了...请自我检讨~~~
body>
html>
Controller层
@Controller
public class ThymeleafController {
@RequestMapping("/show")
public String showOther() {
int i=0;
System.out.println(i/0);
return "index";//index 为任意html页面
}
}
它会捕获注解里面的异常 ,然后通过Controller跳转到相应的异常页面 ,例如下面一行代码就会捕获算术异常
@ExceptionHandler(value = { java.lang.ArithmeticException.class })
error1.html
<html>
<head>
<meta charset="UTF-8">
<title>错误提示页面-ArithmeticExceptiontitle>
head>
<body>
出错了,请与管理员联系。。。
<span th:text="${error}">span>
body>
html>
error2.html
<html>
<head>
<meta charset="UTF-8">
<title>错误提示页面-NullPointerExceptiontitle>
head>
<body>
出错了,请与管理员联系。。。
<span th:text="${error}">span>
body>
html>
controller
@Controller
public class ThymeleafController {
@RequestMapping("/show")
public String showInfo() {
String str = null;
str.length();
return "index";
}
@RequestMapping("/show2")
public String showOther() {
int i = 0;
System.out.println(i / 0);
return "index";
}
/**
* java.lang.ArithmeticException 该方法需要返回一个ModelAndView:
* 目的是可以让我们封装异常信息以及视 图的指定
* 参数Exception e:会将产生异常对象注入到方法中
*/
@ExceptionHandler(value = { java.lang.ArithmeticException.class })
public ModelAndView arithmeticExceptionHandler(Exception e) {
ModelAndView mv = new ModelAndView();
mv.addObject("error", e.toString());
mv.setViewName("error1");
return mv;
}
/**
* java.lang.NullPointerException
* 该方法需要返回一个ModelAndView:目的是可以让我们封装异常信息以及视图的指定
* 参数Exception e:会将产生异常对象注入到方法中
*/
@ExceptionHandler(value={java.lang.NullPointerException.class})
public ModelAndView nullPointerExceptionHandler(Exception e){
ModelAndView mv = new ModelAndView();
mv.addObject("error", e.toString());
mv.setViewName("error2");
return mv;
}
}
这一种方式是对前两种方式的升华 ,但是问题仍是代码量过多 ,可以实现错误页面的映射 ,以及所有对应异常的页面的跳转
/**
* 全局异常处理类
* 使用@ControllerAdvice配合@ExceptionHandler
* 用于在一个类中定义全局异常
*
* @author chy
*
*/
@ControllerAdvice
public class GlobalException {
/**
* java.lang.ArithmeticException 该方法需要返回一个ModelAndView:
* 目的是可以让我们封装异常信息以及视 图的指定
* 参数Exception e:会将产生异常对象注入到方法中
*/
@ExceptionHandler(value = { java.lang.ArithmeticException.class })
public ModelAndView arithmeticExceptionHandler(Exception e) {
ModelAndView mv = new ModelAndView();
mv.addObject("error", e.toString());
mv.setViewName("error1");
return mv;
}
/**
* java.lang.NullPointerException
* 该方法需要返回一个ModelAndView:目的是可以让我们封装异常信息以及视图的指定
* 参数Exception e:会将产生异常对象注入到方法中
*/
@ExceptionHandler(value={java.lang.NullPointerException.class})
public ModelAndView nullPointerExceptionHandler(Exception e){
ModelAndView mv = new ModelAndView();
mv.addObject("error", e.toString());
mv.setViewName("error2");
return mv;
}
}
/**
* SimpleMappingExceptionResolver处理异常
* 在全局异常类中添加一个方法@Bean 完成异常的统一处理
* 但是缺陷和第二种一样,无法对每个Controller的异常都进行对应的跳转
*
* @author chy
*
*/
@Configuration
public class GlobalException {
/**
* 该方法必须要有返回值。返回值类型必须是: SimpleMappingExceptionResolver
*/
@Bean
public SimpleMappingExceptionResolver getSimpleMappingExceptionResolver() {
SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
Properties mappings = new Properties();
/**
* 参数一:异常的类型,注意必须是异常类型的全名 参数二:视图名称
*/
mappings.put("java.lang.ArithmeticException", "error1");
mappings.put("java.lang.NullPointerException", "error2");
// 设置异常与视图信息的映射
resolver.setExceptionMappings(mappings);
return resolver;
}
}
/**
* 自定义HandlerExceptionResolver 类处理异常
* 需要再全局异常处理类中实现HandlerExceptionResolver 接口
* 同样配合 @Configuration实现全局异常的页面映射与跳转 这种方式是对第四种的优化 ,
* 弥补了第四种只建立了映射关系 ,没有显示对应异常的现象 ,强烈推荐使用!!!
*
* @author chy
*
*/
@Configuration
public class GlobalException implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) {
ModelAndView mv = new ModelAndView();
// 判断不同异常类型,做不同视图跳转
if (ex instanceof ArithmeticException) {
mv.setViewName("error1");
}
if (ex instanceof NullPointerException) {
mv.setViewName("error2");
}
//将异常信息封装成相应作用域对象,发送给视图层
mv.addObject("error", ex.toString());
return mv;
}
}
,继承父启动器 ,添加web启动器之后 ,需要额外添加用于支持一个单元测试的启动器
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>1.5.10.RELEASEversion>
parent>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
dependency>
1. 在测试类头部需要加上 ,下面两个注解 ,并创建启动类
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = { AppForJunit.class })
2. 在被测试的方法上加上@Test注解
/**
* SpringBoot 测试类
*
* @RunWith:启动器 SpringJUnit4ClassRunner.class:让junit 与spring 环境进行整合
* @SpringBootTest(classes={AppForJunit.class}) 注解的两个功能
* 1. 当前类为springBoot 的测试类
* 2. 加载SpringBoot 启动类。启动 springBoot
*
* junit 与spring
* 整合 @Contextconfiguartion("classpath:applicationContext.xml")
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = { AppForJunit.class })
public class TestJunit {
@Autowired
private UsersService usersService;
@Test
public void addUser() {
this.usersService.adduser();
}
}
@SpringBootApplication
public class AppForJunit {
public static void main(String[] args) {
SpringApplication.run(AppForJunit.class, args);
}
}
复制一个SpringBoot的web项目
添加SpringLoader 插件
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>springloadedartifactId>
<version>1.2.5.RELEASEversion>
dependency>
dependencies>
plugin>
plugins>
build>
右击项目 run as->Maven Builder…输入以下命令
spring-boot:run
3. 也可以通过进程的名称来结束进程,输入taskkill /im 进程名称(带上后缀) /f
就可以关闭进程了,
启动命令:
-javaagent:.\lib\springloaded-1.2.5.RELEASE.jar -noverify
SpringLoader:SpringLoader 在部署项目时使用的是热部署的方式。
DevTools:DevTools 在部署项目时使用的是重新部署的方式
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<optional>trueoptional>
dependency>
在进行热部署时 ,推荐使用 DevTools 工具 ,这样在前端和后端代码都改变时都能及时看到结果
Spring Data:其实Spring Data 就是spring 提供了一个操作数据的框架。而Spring Data JPA只是Spring Data 框架下的一个基于JPA 标准操作数据的模块。
Spring Data JPA:基于JPA 的标准对数据进行操作。简化操作持久层的代码。只需要编
写接口就可以。
<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.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>1.5.10.RELEASEversion>
parent>
<groupId>ah.szxy.SpringBootgroupId>
<artifactId>22-SpringBoot-JpaartifactId>
<version>0.0.1-SNAPSHOTversion>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-jpaartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.0.9version>
dependency>
dependencies>
project>
#连接数据库
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/ssm?useSSL=false
spring.datasource.username=root
spring.datasource.password=root
#指定连接池
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
#开启正向工程
spring.jpa.hibernate.ddl-auto=update
#运行时,打印sql语句
spring.jpa.show-sql=true
注意相关注解的使用
@Entity
@Table(name="t_users")
public class Users {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="id")
private Integer id;
@Column(name="name")
private String name;
@Column(name="age")
private Integer age;
@Column(name="address")
private String address;
//相关方法省略
}
/**
* 参数一T :当前需要映射的实体
* 参数二ID :当前映射的实体中的OID 的类型
*
*/
public interface UsersRepository extends JpaRepository<Users, Integer>{
}
@SpringBootApplication
public class AppForSpringBootJap {
public static void main(String[] args) {
SpringApplication.run(AppForSpringBootJap.class, args);
}
}
测试类所导入的所有的包,(因为会出现许多相同的包)
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.Order;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
import ah.szxy.AppForSpringBootJap;
import ah.szxy.dao.UsersRepository;
import ah.szxy.dao.UsersRepositoryByName;
import ah.szxy.dao.UsersRepositoryCrudRepository;
import ah.szxy.dao.UsersRepositoryPagingAndSorting;
import ah.szxy.dao.UsersRepositoryQueryAnnotation;
import ah.szxy.pojo.Users;
所注入的接口
@Autowired
private UsersRepository userDao;
@Autowired
private UsersRepositoryByName usersRepositoryByName;
@Autowired
private UsersRepositoryQueryAnnotation usersRepositoryQueryAnnotation;
@Autowired
private UsersRepositoryCrudRepository usersRepositoryCrudRepository;
@Autowired
private UsersRepositoryPagingAndSorting usersRepositoryPagingAndSorting;
测试代码
@Test
public void addUser() {
Users users = new Users();
users.setName("王晓红");
users.setAge(20);
users.setAddress("北京大兴区");
this.userDao.save(users);
}
//方法的名称必须要遵循驼峰式命名规则。findBy(关键字)+属性名称(首字母要大写)+查询条件(首字母大写)
/**
* 根据姓名查询
* @param name
* @return
*/
List<Users> findByName(String name);
/**
* 根据姓名和年龄查询
* @param name
* @param age
* @return
*/
List<Users> findByNameAndAge(String name,Integer age);
/**
* 姓名模糊查询
* @param name
* @return
*/
List<Users> findByNameLike(String name);
}
@Test
public void findUserByName() {
List<Users> list = this.usersRepositoryByName.findByName("周杰伦");
for (Users users : list) {
System.out.println(users);
}
}
@Test
public void findUserByNameAndAge() {
List<Users> list = this.usersRepositoryByName.findByNameAndAge("周杰伦", 23);
for (Users users : list) {
System.out.println(users);
}
}
@Test
public void findUserByNamelike() {
List<Users> list = this.usersRepositoryByName.findByNameLike("周%");
for (Users users : list) {
System.out.println(users);
}
}
注意 :
@Test
public void findUserByNameHQL() {
List<Users> list = this.usersRepositoryQueryAnnotation.queryByNameUseHQL("周杰伦");
for (Users users : list) {
System.out.println(users);
}
}
@Test
public void findUserByNameSQL() {
List<Users> list = this.usersRepositoryQueryAnnotation.queryByNameUseSQL("周杰伦");
for (Users users : list) {
System.out.println(users);
}
}
/**
* 再进行更新操作时,需要开启事务 ,关闭自动回滚
*/
@Test
@Transactional
@Rollback(false)
public void updateUserByid() {
this.usersRepositoryQueryAnnotation.updateUsersNameById("王小王", 4);
}
CrudRepository 接口,主要是完成一些增删改查的操作。
注意:CredRepository 接口继承Repository 接口, 所能调用的方法如下
public interface UsersRepositoryCrudRepository extends CrudRepository<Users, Integer>{
}
/**
* CrudRepository 测试 save 方法中,若指定id ,则相当于更新操作, nice!!!
*/
@Test
public void testCrudRepositorySave() {
Users user = new Users();
user.setId(7);
user.setAddress("天津");
user.setAge(20);
user.setName("王小妹");
this.usersRepositoryCrudRepository.save(user);
}
/**
* CrudRepository 测试-更新
*
*/
@Test
public void testCrudRepositoryUpdate() {
Users user = new Users();
user.setId(7);
user.setAddress("阿拉德");
user.setAge(40);
user.setName("王大强");
this.usersRepositoryCrudRepository.save(user);
}
/**
* CrudRepository 测试-查询(通过主键)
*
*/
@Test
public void testCrudRepositoryQuery() {
Users user = this.usersRepositoryCrudRepository.findOne(4);
System.out.println(user);
}
/**
* CrudRepository 测试-删除(通过主键)
*
*/
@Test
public void testCrudRepositoryDelete() {
this.usersRepositoryCrudRepository.delete(6);
}
该接口提供了分页与排序的操作。
注意: 该接口继承了CrudRepository 接口
public interface UsersRepositoryPagingAndSorting extends PagingAndSortingRepository<Users, Integer> {
}
/**
* PagingAndSortingRepository 排序测试
*/
@Test
public void testPagingAndSortingRepositorySort() {
// Order 定义排序规则
Order order = new Order(Direction.DESC, "id");
// Sort 对象封装了排序规则
Sort sort = new Sort(order);
List<Users> list = (List<Users>) this.usersRepositoryPagingAndSorting.findAll(sort);
for (Users users : list) {
System.out.println(users);
}
}
/**
* PagingAndSortingRepository 分页测试
*/
@Test
public void testPagingAndSortingRepositoryPaging() {
// Pageable:封装了分页的参数,当前页,每页显示的条数。注意:他的当前页是从0 开始。
// PageRequest(page,size) page:当前页。size:每页显示的条数
Pageable pageable = new PageRequest(1, 2);
Page<Users> page = this.usersRepositoryPagingAndSorting.findAll(pageable);
System.out.println("总条数:" + page.getTotalElements());
System.out.println("总页数" + page.getTotalPages());
List<Users> list = page.getContent();
for (Users users : list) {
System.out.println(users);
}
}
/**
* PagingAndSortingRepository 排序+分页
*/
@Test
public void testPagingAndSortingRepositorySortAndPaging() {
Sort sort = new Sort(new Order(Direction.DESC, "id"));
Pageable pageable = new PageRequest(1, 2, sort);
Page<Users> page = this.usersRepositoryPagingAndSorting.findAll(pageable);
System.out.println("总条数:" + page.getTotalElements());
System.out.println("总页数" + page.getTotalPages());
List<Users> list = page.getContent();
for (Users users : list) {
System.out.println(users);
}
}
该接口继承了PagingAndSortingRepository 接口。对继承的父接口中的方法的返回值进行适配。
对比PagingAndSortingRepository ,在进行查询时无需对返回结果进行强转 ,方便我们编程
/**
* 参数一T :当前需要映射的实体
* 参数二ID :当前映射的实体中的OID 的类型
*
*/
public interface UsersRepository extends JpaRepository<Users, Integer>{
}
/**
* PagingAndSortingRepository 排序测试
*/
@Test
public void testPagingAndSortingRepositorySort() {
// Order 定义排序规则
Order order = new Order(Direction.DESC, "id");
// Sort 对象封装了排序规则
Sort sort = new Sort(order);
List<Users> list = this.userDao.findAll(sort);
for (Users users : list) {
System.out.println(users);
}
}
相关代码见SpringData Jpa课程第九部分
pom文件
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-jpaartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.0.9version>
dependency>
dependencies>
application.properties
#连接数据库
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/ssm?useSSL=false
spring.datasource.username=root
spring.datasource.password=root
#指定连接池
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
#开启正向工程
spring.jpa.hibernate.ddl-auto=update
#运行时,打印sql语句
spring.jpa.show-sql=true
启动类
@SpringBootApplication
public class AppForSpringBootJap {
public static void main(String[] args) {
SpringApplication.run(AppForSpringBootJap.class, args);
}
}
需求:角色与用户的一对多的关联关系。
角色:一方
用户:多方
注意编写一对多关系时,“一” 的实体类的toString()写法
@Entity
@Table(name="tb_roles")
public class Roles {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="role_id")
private Integer roleid;
@Column(name="role_name")
private String rolename;
@OneToMany(mappedBy="roles")
private Set<Users> users=new HashSet<Users>();
//取值赋值方法,构造方法略
//注意整理不能有Users对象,会出异常
@Override
public String toString() {
return "Roles [roleid=" + roleid + ", rolename=" + rolename +"]";
}
}
@Entity
@Table(name="tb_users")
public class Users {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="id")
private Integer id;
@Column(name="name")
private String name;
@Column(name="age")
private Integer age;
@Column(name="address")
private String address;
@ManyToOne(cascade=CascadeType.PERSIST,fetch=FetchType.EAGER)
@JoinColumn(name="role_id")
private Roles roles;
//取值赋值方法,构造方法略
}
public interface UserRepository extends JpaRepository<Users, Integer>{
}
注意相关的注解!!!
/**
* 测试一对多关系
*
* @author chy
*
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes={AppForSpringBootJap.class})
public class TestOneToMany {
@Autowired
private UserRepository userDao;
/**
* 添加用户同时添加角色
*/
@Test
public void test1() {
// 创建角色
Roles roles = new Roles();
roles.setRolename("管理员");
// 创建用户
Users users = new Users();
users.setName("小辣椒");
users.setAge(22);
users.setAddress("纽约");
// 建立关系
roles.getUsers().add(users); // 为users这个set集合添加属性
users.setRoles(roles); // 为user保存roles相关信息 ,如外键roles_id
// 保存数据
this.userDao.save(users);
}
/**
* 根据用户ID 查询用户信息,同时查询角色
*/
@Test
public void test2() {
Users users = this.userDao.findOne(2);
System.out.println("用户姓名:" + users.getName());
Roles roles = users.getRoles();
System.out.println(roles);
}
}
需求:角色与菜单多对多关联关系
角色:多方
菜单:多方
@Entity
@Table(name="tb_roles")
public class Roles {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="role_id")
private Integer roleid;
@Column(name="role_name")
private String rolename;
@OneToMany(mappedBy="roles")
private Set<Users> users=new HashSet<Users>();
@ManyToMany(cascade=CascadeType.PERSIST,fetch=FetchType.EAGER)
@JoinTable(name="tb_roles_menus",joinColumns=@JoinColumn(name="role_id"),inverseJoinColumns=@JoinColumn(name="menu_id"))
private Set<Menus>meuns=new HashSet<Menus>();
//注意整理不能有Users对象,会出异常
@Override
public String toString() {
return "Roles [roleid=" + roleid + ", rolename=" + rolename +"]";
}
//取值赋值,构造方法略
}
注意 toString()方法的修改!!!
@Entity
@Table(name="tb_menus")
public class Menus {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="menuId")
private Integer menuId;
@Column(name="menuName")
private String menuName;
@Column(name="menuUrl")
private String menuUrl;
@Column(name="fatherId")
private Integer fatherId;
@ManyToMany(mappedBy="menus")
private Set<Roles> roles=new HashSet<Roles>();
//不打印roles的信息,因为查不到(没有session)
@Override
public String toString() {
return "Menus [menuId=" + menuId + ", menuName=" + menuName + ", menuUrl=" + menuUrl + ", fatherId=" + fatherId
+ "]";
}
//取值赋值,构造方法略
}
public interface RolesRepository extends JpaRepository<Roles, Integer>{
}
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes= {AppForSpringBootJap.class})
public class TestManyToMany {
@Autowired
private RolesRepository roleDao;
/**
* 添加角色同时添加菜单
*/
@Test
public void test1() {
// 创建角色
Roles roles = new Roles();
roles.setRolename("超管");;
// 创建菜单
Menus menus = new Menus();
menus.setFatherId(-1);
menus.setMenuName("CSND博客平台");
menus.setMenuUrl("www.csdn.com");
Menus menus2 = new Menus();
menus2.setFatherId(1);
menus2.setMenuName("博客园博客平台");
menus2.setMenuUrl("www.cnblogs.com");
// 添加关系
roles.getMeuns().add(menus);
roles.getMeuns().add(menus2);
menus.getRoles().add(roles);
menus2.getRoles().add(roles);
// 保存数据
this.roleDao.save(roles);
}
/**
* 查询Roles
*/
@Test
public void test2() {
Roles roles = this.roleDao.findOne(4);
System.out.println("角色信息:" + roles);
Set<Menus> menus = roles.getMeuns();
for (Menus menus2 : menus) {
System.out.println("菜单信息:" + menus2);
}
}
}
通过 Ehcache实现对数据本地的缓存 , 用于单体架构的项目中
<properties>
<thymeleaf.version>3.0.2.RELEASEthymeleaf.version>
<thymeleaf-layout-dialect.version>2.0.4thymeleaf-layout-dialect.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-jpaartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.0.9version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-cacheartifactId>
dependency>
<dependency>
<groupId>net.sf.ehcachegroupId>
<artifactId>ehcacheartifactId>
dependency>
dependencies>
文件名:ehcache.xml
位置:src/main/resources/ehcache.xml
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<diskStore path="java.io.tmpdir" />
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
maxElementsOnDisk="10000000"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
<persistence strategy="localTempSwap" />
defaultCache>
<cache name="users"
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
maxElementsOnDisk="10000000"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
<persistence strategy="localTempSwap" />
cache>
ehcache>
#连接数据库
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/ssm?useSSL=false
spring.datasource.username=root
spring.datasource.password=root
#指定连接池
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
#开启正向工程
spring.jpa.hibernate.ddl-auto=update
spring.jpa.hibernate.generate-ddl=true
#运行时,打印sql语句
spring.jpa.show-sql=true
#使用缓存时需要在启动类上添加@EnableCaching注解 在需要开启注解的方法上添加@Cacheable
#开启ehcache的缓存配置
spring.cache.ehcache.config=ehcache.xml
加上@EnableCaching注解表示开启本地缓存
@SpringBootApplication
@EnableCaching
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
public interface UserRepository extends JpaRepository<Users, Integer>{
}
public interface UsersService {
void save(Users users);
List<Users>findALl();
Users findUserById(Integer id);
Page<Users>findUserByPage(Pageable pageable);
}
@CacheEvict(value=“users”,allEntries=true) 清除缓存中以users 存策略缓存的对象
@Cacheable(“users”) 这个注解是选择缓存的类型,需要在启动类开启@EnableCaching
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import ah.szxy.dao.UserRepository;
import ah.szxy.pojo.Users;
@Service
public class UsersServiceImpl implements UsersService{
@Autowired
private UserRepository userRepository;
@Override
public List<Users> findALl() {
return this.userRepository.findAll();
}
/**
* 没有方法参数时,会默认以0为key,当有两个无参方法使用缓存时mkey会冲突。可以使用数字作为key,不能直接使用字符串
*/
@Override
//@Cacheable:对当前查询的对象做缓存处理
@Cacheable("users") //这个注解是选择缓存的类型,需要在启动类开启@EnableCaching
public Users findUserById(Integer id) {
return this.userRepository.findOne(id);
}
@Override
@Cacheable(value="users",key="#pageable.pageSize")
public Page<Users> findUserByPage(Pageable pageable) {
return this.userRepository.findAll(pageable);
}
//@CacheEvict(value="users",allEntries=true) 清除缓存中以users 存策略缓存的对象
@Override //实现了在在添加用户时清除本地缓存 ,防止数据的脏读
@CacheEvict(value="users",allEntries=true)
public void save(Users users) {
this.userRepository.save(users);
}
}
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes=App.class)
public class UsersServiceTest {
@Autowired
private UsersService usersService;
@Test
public void testfindUserById() {
Users user = usersService.findUserById(1);
System.out.println(user);
Users user2 = usersService.findUserById(1);
System.out.println(user2);
// //第一次查询
// System.out.println(this.usersService.findUserById(1));
// //第二次查询
// System.out.println(this.usersService.findUserById(1));
}
@Test
public void TestPageable() {
//第一次
Pageable pageable=new PageRequest(0, 2);
Page<Users> page = this.usersService.findUserByPage(pageable);
System.out.println("总条数"+page.getTotalElements());
//第二次
Page<Users> page2 = this.usersService.findUserByPage(pageable);
System.out.println("总条数"+page2.getTotalElements());
//第三次 ,在实现类@Cacheable(value="users",key="#pageable.pageSize")
//表示以page的size作为key ,只要key一样,就会默认使用本地缓存而不会执行对数据库的查询
Pageable pageable2=new PageRequest(1, 2);
Page<Users> page3 = this.usersService.findUserByPage(pageable2);
System.out.println("总条数"+page3.getTotalElements());
}
@Test
public void test3() {
Users user = usersService.findUserById(1);
System.out.println(user);
Users users = new Users();
users.setName("chy");
users.setAge(24);
users.setAddress("szxt");
this.usersService.save(users);
Users user2 = usersService.findUserById(1);
System.out.println(user2);
}
}
点击查询常用注解
创建maven项目
启动器的父类
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>1.5.10.RELEASEversion>
parent>
其他依赖支持
<properties>
<thymeleaf.version>3.0.2.RELEASEthymeleaf.version>
<thymeleaf-layout-dialect.version>2.0.4thymeleaf-layout-dialect.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
dependency>
dependencies>
文件名:application.properties
位置:src/main/resources/application.properties
spring.redis.pool.max-idle=10
spring.redis.pool.min-idle=5
spring.redis.pool.max-total=20
spring.redis.hostName=192.168.179.131
spring.redis.port=6379
/**
* 完成对Redis的整合的一些配置
*
* @author chy
*
*/
@Configuration
public class RedisConfig {
/**
* 1.创建JedisPoolConfig 对象。在该对象中完成一些链接池配置
* @ConfigurationProperties:会将前缀相同的内容创建一个实体。
*/
@Bean
@ConfigurationProperties(prefix="spring.redis.pool")
public JedisPoolConfig jedisPoolConfig(){
JedisPoolConfig config = new JedisPoolConfig();
//最大空闲数
//config.setMaxIdle(10);
//最小空闲数
//config.setMinIdle(5);
//最大链接数
//config.setMaxTotal(20);
System.out.println("默认值:"+config.getMaxIdle());
System.out.println("默认值:"+config.getMinIdle());
System.out.println("默认值:"+config.getMaxTotal());
return config;
}
/**
* 2.创建JedisConnectionFactory:配置redis链接信息
*/
@Bean
@ConfigurationProperties(prefix="spring.redis")
public JedisConnectionFactory jedisConnectionFactory(JedisPoolConfig config){
System.out.println("配置完毕:"+config.getMaxIdle());
System.out.println("配置完毕:"+config.getMinIdle());
System.out.println("配置完毕:"+config.getMaxTotal());
JedisConnectionFactory factory = new JedisConnectionFactory();
//关联链接池的配置对象
factory.setPoolConfig(config);
//配置链接Redis的信息
//主机地址
//factory.setHostName("192.168.179.131");
//端口
//factory.setPort(6379);
return factory;
}
/**
* 3.创建RedisTemplate:用于执行Redis操作的方法
*/
@Bean
public RedisTemplate<String,Object> redisTemplate(JedisConnectionFactory factory){
RedisTemplate<String, Object> template = new RedisTemplate<>();
//关联
template.setConnectionFactory(factory);
//为key设置序列化器
template.setKeySerializer(new StringRedisSerializer());
//为value设置序列化器
template.setValueSerializer(new StringRedisSerializer());
return template;
}
}
public class Users implements Serializable{
private Integer id;
private String name;
private Integer age;
private String address;
//洽谈方法省略
}
/**
* SpringBoot整合Mybatis
* @author chy
*
*/
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes=App.class)
public class TestRedis {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 插入一个字符串
*/
@Test
public void TestSet() {
this.redisTemplate.opsForValue().set("name", "乔鲁诺乔斯达");
}
/**
* 从redis中取值
*/
@Test
public void TestGet() {
String name = (String) this.redisTemplate.opsForValue().get("name");
System.out.println(name);
}
/**
* 设置Users对象
*注意通过: JdkSerializationRedisSerializer序列化器所占内存较大
*/
@Test
public void TestObjectSet() {
Users users = new Users();
users.setId(1);
users.setName("乔鲁诺乔巴纳");
users.setAge(16);
users.setAddress("意大利罗马");
this.redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
this.redisTemplate.opsForValue().set("Users", users);
}
/**
* 获取Users对象
*注意通过: JdkSerializationRedisSerializer序列化器所占内存较大
*/
@Test
public void TestObjectGet() {
this.redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
Users users = (Users) this.redisTemplate.opsForValue().get("Users");
System.out.println(users);
}
/**
* 设置Users对象,通过Json序列化器
*占用内存较小
*/
@Test
public void TestObjectSetJson() {
Users users = new Users();
users.setId(1);
users.setName("乔鲁诺乔巴纳");
users.setAge(16);
users.setAddress("意大利罗马");
this.redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(Users.class));
this.redisTemplate.opsForValue().set("Users_Json", users);
}
/**
* 获取Users对象,通过Json序列化器
*占用内存较小
*/
@Test
public void TestObjectGetJson() {
this.redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(Users.class));
Users users = (Users) this.redisTemplate.opsForValue().get("Users_Json");
System.out.println(users);
}
}
引入maven依赖
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-context-supportartifactId>
dependency>
创建定时器
//不属于控制层、服务层、数据访问层。所以使用@Component
@Component
public class ScheduledDemo {
/**
* 每两秒打印一次时间
* cron="0/2 * * * * *"
*
* 每分钟的第二秒打印一次时间
* cron="2 * * * * *"
*/
@Scheduled(cron="0/2 * * * * ?")
public void doScheduled() {
System.out.println("定时器:"+new Date());
}
}
启动器(注意:使用了@EnableScheduling注解):
/**
* SpringBoot整合定时器任务
* 在开启启动类时,需要额外加@EnableScheduling注解
*
* @author chy
*
*/
@SpringBootApplication
@EnableScheduling
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
Cron 表达式是一个字符串,分为6 或7 个域,每一个域代表一个含义
Cron 有如下两种语法格式:
(1) Seconds Minutes Hours Day Month Week Year
(2) Seconds Minutes Hours Day Month Week(推荐使用)
一、结构
corn 从左到右(用空格隔开):秒分小时月份中的日期月份星期中的日期年份
二、各字段的含义
Cron 表达式的时间字段除允许设置数值外,还可使用一些特殊的字符,提供列表、范围、通配符等功能,细说如下:
特殊字符名称 | 作用 |
---|---|
星号(*) | 可用在所有字段中,表示对应时间域的每一个时刻,例如,*在分钟字段时,表示“每分钟”; |
问号(?) | 该字符只在日期和星期字段中使用,它通常指定为“无意义的值”,相当于占位符; |
减号(-) | 表达一个范围,如在小时字段中使用“10-12”,则表示从10 到12 点,即10,11,12; |
逗号(,): | 表达一个列表值,如在星期字段中使用“MON,WED,FRI”,则表示星期一,星期三和星期五; |
斜杠(/) | x/y 表达一个等步长序列,x 为起始值,y 为增量步长值。如在分钟字段中使用0/15, 则表示为0,15,30 和45 秒,而5/15 在分钟字段中表示5,20,35,50,你也可以使用*/y,它等同于0/y; |
LW 组合 | 在日期字段可以组合使用LW,它的意思是当月的最后一个工作日; |
井号(#) | 该字符只能在星期字段中使用,表示当月某个工作日。如6#3 表示当月的第三个星期五(6表示星期五,#3 表示当前的第三个),而4#5 表示当月的第五个星期三,假设当月没有第五个星期三,忽略不触发; |
C | 该字符只在日期和星期字段中使用,代表“Calendar”的意思。它的意思是计划所关联的日期,如果日期没有被关联,则相当于日历中所有日期。例如5C 在日期字段中就相当于日历5 日以后的第一天。 |
1C | 在星期字段中相当于星期日后的第一天。 |
L | 该字符只在日期和星期字段中使用,代表“Last”的意思,但它在两个字段中意思不同。 L 在日期字段中,表示这个月份的最后一天,如一月的31 号,非闰年二月的28 号; 如果L 用在星期中,则表示星期六,等同于7。但是,如果L 出现在星期字段里,而且在前面有一个数值X, 则表示“这个月的最后X 天”,例如,6L 表示该月的最后星期五; |
W | 该字符只能出现在日期字段里,是对前导日期的修饰,表示离该日期最近的工作日。 例如15W表示离该月15 号最近的工作日,如果该月15 号是星期六,则匹配14 号星期五;如果15 日是星期日,则匹配16 号星期一; 如果15 号是星期二,那结果就是15 号星期二。但必须注意关联的匹配日期不能够跨月,如你指定1W,如果1 号是星期六,结果匹配的是3 号星期一,而非上个月最后的那天。W 字符串只能指定单一日期,而不能指定日期范围; |
注 : Cron 表达式对特殊字符的大小写不敏感,对代表星期的缩写英文大小写也不敏感。
例子 | 作用 |
---|---|
@Scheduled(cron = “0 0 1 1 1 ?”) | 每年一月的一号的1:00:00 执行一次 |
@Scheduled(cron = “0 0 1 1 1,6 ?”) | 一月和六月的一号的1:00:00 执行一次 |
@Scheduled(cron = “0 0 1 1 1,4,7,10 ?”) | 每个季度的第一个月的一号的1:00:00 执行一次 |
@Scheduled(cron = “0 0 1 1 * ?”) | 每月一号1:00:00 执行一次 |
@Scheduled(cron=“0 0 1 * * *”) | 每天凌晨1 点执行一次 |
Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用。Quartz可以用来创建简单或为运行十个,百个,甚至是好几万个Jobs这样复杂的程序。Jobs可以做成标准的Java组件或 EJBs。Quartz的最新版本为Quartz 2.3.0。
创建项目(简单的javase的maven项目)
导入依赖:
<dependency>
<groupId>org.quartz-schedulergroupId>
<artifactId>quartzartifactId>
<version>2.2.1version>
dependency>
创建定时任务:
//需要实现job接口
public class QuartzJob implements Job{
@Override
public void execute(JobExecutionContext arg0) throws JobExecutionException {
System.out.println("定时任务:"+new Date());
}
}
启动定时任务:
public class QuartzStart {
public static void main(String[] args) throws Exception {
//创建定时任务 job 做什么
JobDetail job = JobBuilder.newJob(QuartzJob.class).build();
//创建定时器 trigger 什么时候做
//两种定时方式
//SimpleScheduleBuilder简单定时:通过Quartz 提供一个方法来完成简单的重复调用
//CronScheduleBuilder 表达式定时:cron表达式
//Trigger trigger=TriggerBuilder.newTrigger().withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(2)).build();
Trigger trigger=TriggerBuilder.newTrigger().withSchedule(CronScheduleBuilder.cronSchedule("0/2 * * * * ?")).build();
//定时任务与定时器绑定Scheduler 在什么时候做什么
Scheduler scheduler=StdSchedulerFactory.getDefaultScheduler();
scheduler.scheduleJob(job, trigger);
//启动
scheduler.start();
}
}
创建项目
导入依赖:
<properties>
<thymeleaf.version>3.0.2.RELEASEthymeleaf.version>
<thymeleaf-layout-dialect.version>2.0.4thymeleaf-layout-dialect.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-context-supportartifactId>
dependency>
<dependency>
<groupId>org.quartz-schedulergroupId>
<artifactId>quartzartifactId>
<version>2.2.1version>
<exclusions>
<exclusion>
<artifactId>slf4j-apiartifactId>
<groupId>org.slf4jgroupId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-txartifactId>
dependency>
dependencies>
定时任务:
public class JobDemo implements Job{
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println("定时任务:"+new Date());
}
}
配置类:
/**
* Quartz配置类
* @author Administrator
*
*/
@Configuration
public class QuartzConfig {
@Bean
public JobDetailFactoryBean jobDetailFactoryBean() {
JobDetailFactoryBean factoryBean=new JobDetailFactoryBean();
factoryBean.setJobClass(JobDemo.class);
return factoryBean;
}
// @Bean
// public SimpleTriggerFactoryBean simpleTriggerFactoryBean(JobDetailFactoryBean jobDetailFactoryBean) {
// SimpleTriggerFactoryBean factoryBean=new SimpleTriggerFactoryBean();
// factoryBean.setJobDetail(jobDetailFactoryBean.getObject());
// //间隔时间
// factoryBean.setRepeatInterval(2000);//ms
// //执行次数
// factoryBean.setRepeatCount(4);
// return factoryBean;
// }
@Bean
public CronTriggerFactoryBean cronTriggerFactoryBean(JobDetailFactoryBean jobDetailFactoryBean) {
CronTriggerFactoryBean factoryBean=new CronTriggerFactoryBean();
factoryBean.setJobDetail(jobDetailFactoryBean.getObject());
factoryBean.setCronExpression("0/2 * * * * ?");
return factoryBean;
}
// @Bean
// public SchedulerFactoryBean schedulerFactoryBean(SimpleTriggerFactoryBean simpleTriggerFactoryBean) {
//
// SchedulerFactoryBean factoryBean=new SchedulerFactoryBean();
// factoryBean.setTriggers(simpleTriggerFactoryBean.getObject());
// return factoryBean;
// }
@Bean
public SchedulerFactoryBean schedulerFactoryBean(CronTriggerFactoryBean cronTriggerFactoryBean) {
SchedulerFactoryBean factoryBean=new SchedulerFactoryBean();
factoryBean.setTriggers(cronTriggerFactoryBean.getObject());
return factoryBean;
}
}
启动类:
@SpringBootApplication
@EnableScheduling//开启定时器
public class StartApp {
public static void main(String[] args) {
SpringApplication.run(StartApp.class, args);
}
}
问题:在定时任务中无法调用其他对象,spring无法注入对象。
原因:在quartz中定时任务的创建时通过AdaptableJobFactory类中的createJobInstance方法创建,使用的是反射,没有使用spring,所以无法注入对象。
解决:复写createJobInstance方法,将创建的定时任务加入到spring容器中。
package ah.szxy.config;
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.scheduling.quartz.AdaptableJobFactory;
import org.springframework.stereotype.Component;
@Component("myAdaptableJobFactory")
public class MyAdaptableJobFactory extends AdaptableJobFactory {
// AutowireCapableBeanFactory 可以将一个对象添加到SpringIOC 容器中,并且完成该对象注入
@Autowired
private AutowireCapableBeanFactory autowireCapableBeanFactory;
/**
* 该方法需要将实例化的任务对象手动的添加到springIOC 容器中并且完成对 象的注入
*/
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
Object obj = super.createJobInstance(bundle);
// 将obj 对象添加Spring IOC 容器中,并完成注入
this.autowireCapableBeanFactory.autowireBean(obj);
return obj;
}
}
在配置类中设置
/**
* 3.创建Scheduler对象
*/
@Bean
public SchedulerFactoryBean schedulerFactoryBean(CronTriggerFactoryBean cronTriggerFactoryBean,
MyAdaptableJobFactory myAdaptableJobFactory){
SchedulerFactoryBean factory = new SchedulerFactoryBean();
//关联trigger
factory.setTriggers(cronTriggerFactoryBean.getObject());
factory.setJobFactory(myAdaptableJobFactory);
return factory;
}
在实际开发中关于定时器的应用:
https://blog.csdn.net/weixin_39723544/article/details/83382000
https://www.cnblogs.com/ealenxie/p/9134602.html