File -> new project 选择springboot项目初始化。
在项目中建一个controller,代码如下:
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* hello world测试类
* @author Alone
*/
@RestController
public class HelloController {
@RequestMapping("/hello")
public String Hello() {
return "Hello,world!";
}
}
将方法的返回值,以特定的格式写入到response的body区域,进而将数据返回给客户端。当方法上面没有写ResponseBody,底层会将方法的返回值封装为ModelAndView对象。如果返回值是字符串,那么直接将字符串写到客户端;如果是一个对象,会将对象转化为json串,然后写到客户端。
@RequestMapping、@ResponseBody 等这些注解是干什么的? - 果真真的回答 - 知乎
前置知识-SpringMVC请求流程
DispatcherServlet#doDispatch
有个loggingApplicationListener的监听器,监听了spring的事件,读取了spring容器中的日志配置,进行了日志的初始化。
(springboot-actuator可以实现动态调整日志级别,待研究)
to be continue
拦截器是一种动态拦截方法调用的机制。可以在指定方法前后执行预先设定的代码以及阻止方法调用。
拦截器的实现需要实现HandlerInterceptor
接口:
/**
* 拦截器测试
* @author Alone
*/
@Component
public class InterceptConfiguration implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle...");
// 此处返回false导致后面不再执行
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle...");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion");
}
}
将拦截器添加入SpringMVC的配置类:
/**
* @author Alone
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private InterceptConfiguration interceptConfiguration;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(interceptConfiguration);
}
}
preHandler(HttpServletRequest request, HttpServletResponse response, Object handler)
: 方法在请求处理之前调用,在这个方法中进行一些前置预处理或初始化操作,也可以决定是否终止请求。当该方法返回false时,后续的interceptor和controller都不会执行。拦截器可以注册多个,调用时依据声明顺序依次执行。handler
参数本质上是一个方法对象,有了它就可以操作原始执行的方法。postHandler(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
: 在当前请求处理之后,DispatcherServlet进行视图渲染之前调用。此时可以对controller处理之后的modelAndView对象进行操作。afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex)
: 该方法在整个请求结束后进行调用,主要用于清理资源。多拦截器的执行顺序:
to be continue
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-jpaartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<scope>runtimescope>
dependency>
配置文件application.yml:
spring:
jpa:
show-sql: true
hibernate:
ddl-auto: update
datasource:
url: jdbc:mysql://localhost:3306/gotest?useUnicode=true&characterEncoding=UTF-8
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 我是密码
spring.jpa.hibernate.ddl-auto
属性的选项说明:
create
: 无论数据是否改变,每次hibernate
加载时都删除上一次生成的表create-drop
: 每次加载hibernate
时都根据实体类生成表,但是sessionFactory一关闭,表就自动删除。update
: 每次加载hibernate时根据model类会自动更新表的结构,不存在则新建。不会删除旧数据。validate
: 每次加载hibernate时,验证创建数据库表结构,只会和数据库中的表进行比较,不会创建新表,但是会插入新值。none
: 不做任何操作实体类:
package com.alone.simpleusage.database.datajpa.entity;
import lombok.Data;
import javax.persistence.*;
/**
* 学生实体类
* @Entity 表示这是一个实体类
* @Table 定义数据库表名,jpa在操作的时候会先去寻找这张表,没有这张表的话就创建
*
* @author Alone
*/
@Data
@Entity
@Table(name = "student")
public class Student {
/**
* @Id 表示这是主键
* @GeneratedValue 主键生成策略
* @Column 配置属性和数据库字段的对应
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer urid;
@Column(name = "code")
private String code;
@Column
private String name;
}
dao接口:
package com.alone.simpleusage.database.datajpa.dao;
import com.alone.simpleusage.database.datajpa.entity.Student;
import org.springframework.data.jpa.repository.JpaRepository;
public interface StudentRepository extends JpaRepository<Student, Integer> {
}
运行测试:
@SpringBootTest
public class RepositoryTest {
@Autowired
private StudentRepository studentRepository;
@Test
public void testInsert() {
Student student = new Student();
student.setCode("1");
student.setName("顶不住了");
studentRepository.save(student);
}
}
运行结果:
Spring JPA实现的默认方法:
Spring jpa 动态查询:
public interface StudentRepository extends JpaRepository<Student, Integer>, JpaSpecificationExecutor<Student> {
}
@Test
public void testQuery() {
List<Student> userList = studentRepository.findAll(((root, criteriaQuery, criteriaBuilder) -> {
// 定义集合,用于存放动态查询条件
List<Predicate> predicateList = Lists.newArrayList();
predicateList.add(criteriaBuilder.like(root.get("code").as(String.class), "%1%"));
predicateList.add(criteriaBuilder.like(root.get("name").as(String.class), "%顶%"));
return criteriaBuilder.and(predicateList.toArray(new Predicate[predicateList.size()]));
}));
userList.forEach(System.out::println);
}
分页查询:
@Test
public void testPageQuery() {
// 构造查询条件
Specification<Student> spec = new Specification<Student>() {
@Override
public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
return null;
}
};
// 分页查询接口
PageRequest pageRequest = PageRequest.of(0, 1);
Page<Student> students = studentRepository.findAll(spec, pageRequest);
System.out.println("查询总页数:" + students.getTotalPages());
System.out.println("查询总记录数:" + students.getTotalElements());
System.out.println("数据集合列表:" + students.getContent());
}
@Test
public void testSortQuery() {
Sort sort = Sort.by(Sort.Direction.DESC, "urid");
Specification<Student> spec = new Specification<Student>() {
@Override
public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
return null;
}
};
List<Student> studentList = studentRepository.findAll(spec, sort);
for (Student student : studentList) {
System.out.println(student);
}
}
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jdbcartifactId>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.2.4version>
dependency>
spring:
datasource:
url: jdbc:mysql://localhost:3306/gotest?useUnicode=true&characterEncoding=UTF-8
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 密码
type: com.alibaba.druid.pool.DruidDataSource
/**
* Druid数据源配置类
* @author Alone
*/
@Configuration
public class DruidConfig {
/**
* 配置绑定
* @return
*/
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DruidDataSource druid() {
return new DruidDataSource();
}
}
此处的前缀prefix要和application.yml中的前缀对应,不然会报java.sql.SQLException: url not set
的异常。
且该注解一定要标在对应bean上。
@Test
public void testGetDruidDataBase() throws SQLException {
System.out.println(dataSource.getClass());
Connection connection = dataSource.getConnection();
System.out.println(connection);
connection.close();
}
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.4.2version>
dependency>
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 开启sql语句打印
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Long id;
private String name;
private Integer age;
private String email;
private Long managerId;
private LocalDateTime createTime;
}
此处简单说一下LocalDateTime相较于Date的优点:
SimpleDateFormat
进行格式化,该对象线程不安全getYear()
getDay()
等方法均已被弃用public interface UserMapper extends BaseMapper<User> {
}
@SpringBootApplication
@MapperScan("com.alone.simpleusage.database.mpp")
public class SimpleusageApplication {
public static void main(String[] args) {
SpringApplication.run(SimpleusageApplication.class, args);
}
}
@Test
public void testUserMapper() {
DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
User user = new User(6L, "test", 10, "[email protected]", null, LocalDateTime.parse("2022-11-08 15:44:00", df));
userMapper.insert(user);
}
【Java】Spring事务相关笔记
AOP(Aspect Oriented Programming)面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。主要用于分离系统的业务逻辑和系统服务。
通知:指切面要完成的工作,定义了切面是什么以及何时使用。
共有5种类型:
springboot切面实现打印当前系统时间:
pom.xml:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-aopartifactId>
dependency>
切面类:
package com.alone.simpleusage.aop.aspect;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
/**
* 切面类
* @author Alone
*/
@Aspect
@Component
@Slf4j
public class SysOutTimeAspect {
/**
* 切入点方法
*/
@Pointcut("execution(public * com.alone.simpleusage.aop.controller.*.*(..))")
public void printTime() {
}
/**
* 前置通知
* 这里的方法名和入参都不能写错,不然会进不去需要被增强的方法
* @param joinPoint
*/
@Before("printTime()")
public void doBefore(JoinPoint joinPoint) {
log.info("开始打印");
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
log.info(df.format(System.currentTimeMillis()));
}
}
测试类:
package com.alone.simpleusage.aop.controller;
import org.springframework.web.bind.annotation.*;
/**
* 切面测试controller
* @author Alone
*/
@RestController
@RequestMapping("/aoptest")
public class AopController {
@RequestMapping("/hello")
public String hello(@RequestParam(value = "name", required = false) String name) {
System.out.println("进入hello方法");
return "hello" + name;
}
}
结果:
以类名描述:
execution (访问修饰符 返回值 包名.类/接口名.方法名 (参数) 异常名)
通配符:
*
:单个独立的任意符号
..
: 多个连续的任意符号
+
: 匹配子类
如果是环绕通知,被增强方法的返回值是void时执行proceedingJoinPoint.proceed()
后得到的返回值是null
主要用于操作解耦。
事件定义:
package com.alone.simpleusage.event;
import org.springframework.context.ApplicationEvent;
/**
* 事件定义
* @author Alone
*/
public class MailEvent extends ApplicationEvent {
private String message;
public MailEvent(Object source, String message) {
super(source);
this.message = message;
}
public String sendMail() {
return message;
}
}
事件发布:
package com.alone.simpleusage.event;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
/**
* 事件发布
* @author Alone
*/
@Component
@Slf4j
public class MailEventPublisher {
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
public void publish(String message) {
log.info("开始发送");
MailEvent mailEvent = new MailEvent(this, message);
applicationEventPublisher.publishEvent(mailEvent);
}
}
事件监听:
package com.alone.simpleusage.event;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import java.time.LocalTime;
/**
* 事件监听
* @author Alone
*/
@Component
@Slf4j
public class MailEventListener {
@EventListener
public void receiveMail(MailEvent mailEvent) {
String message = mailEvent.sendMail();
log.info("receive message: " + message + "time: " + LocalTime.now());
}
}
测试:
package com.alone.simpleusage.event;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
@SpringBootTest
public class EventTest {
@Resource
private MailEventPublisher sendEmailEventPublisher;
@Test
public void publish() {
sendEmailEventPublisher.publish("hello world");
}
}