jdbc结束

This commit is contained in:
yinkanglong
2023-11-26 18:29:38 +08:00
parent f31bf5475d
commit 8090262bff
61 changed files with 3576 additions and 613 deletions

View File

@@ -50,6 +50,19 @@
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.16</version>
</dependency>
</dependencies>
<build>

View File

@@ -1,7 +1,7 @@
package org.example.controller;
import org.apache.commons.lang3.StringUtils;
import org.example.bean.User;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

View File

@@ -1,10 +1,7 @@
package org.example.interceptor;
import lombok.extern.slf4j.Slf4j;
import org.example.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

View File

@@ -0,0 +1,63 @@
package org.example.dao;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public class BaseDAO {
public int executeUpdate(String sql, Object... args) throws SQLException {
Connection conn = JDBCUtils.getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
ps.setString(i, args[i-1].toString());
}
int rows = ps.executeUpdate();
ps.close();
if (!conn.getAutoCommit()) {
conn.close();
}
return rows;
}
public <T> List<T> executeQuery(Class<T> clazz,String sql, Object... args) throws SQLException, InstantiationException, IllegalAccessException, NoSuchFieldException {
Connection conn = JDBCUtils.getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
ps.setString(i, args[i-1].toString());
}
ResultSet resultSet = ps.executeQuery();
//对返回值进行解析
ResultSetMetaData resultMeta = resultSet.getMetaData();
int count = resultMeta.getColumnCount();
List<T> rows = new ArrayList<T>();
while(resultSet.next()){
T t = clazz.newInstance();
for (int i = 1; i < count; i++) {
Object object = resultSet.getObject(i);
String columnName = resultMeta.getColumnName(i);
Field field = clazz.getDeclaredField(columnName);
field.setAccessible(true);
field.set(t,object);
}
rows.add(t);
}
ps.close();
if (!conn.getAutoCommit()) {
conn.close();
}
return rows;
}
}

View File

@@ -0,0 +1,45 @@
package org.example.dao;
import com.mysql.cj.jdbc.Driver;
import org.junit.Test;
import java.sql.*;
public class JDBCTest {
@Test
public void testJDBCConnnection() throws SQLException {
DriverManager.registerDriver(new Driver());
String url = "jdbc:mysql://127.0.0.1/test";
String username= "root";
String password = "long1011";
Connection connection = DriverManager.getConnection(url, username, password);
String sql2 = "INSERT INTO user (name) values ('testname02')";
connection.createStatement().executeUpdate(sql2);
String sql = "select * from user;";
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(sql);
while (resultSet.next()) {
String name = resultSet.getString("name");
int id = resultSet.getInt("id");
System.out.println("name: " + name+" id: " + id);
}
resultSet.close();
statement.close();
connection.close();
}
}

View File

@@ -0,0 +1,56 @@
package org.example.dao;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
public class JDBCUtils {
static DataSource datasource = null;
private static ThreadLocal<Connection> tl = new ThreadLocal<>();
static {
Properties properties = new Properties();
InputStream in = JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties");
try{
properties.load(in);
}catch(IOException e){
throw new RuntimeException(e);
}
try {
datasource = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static Connection getConnection() throws SQLException {
Connection connection = tl.get();
if(connection == null){
connection = datasource.getConnection();
tl.set(connection);
}
return connection;
}
public static void freeConnection() throws SQLException {
Connection connection = tl.get();
if(connection != null){
tl.remove();
connection.setAutoCommit(false);
connection.close();
}
}
}

View File

@@ -1,39 +0,0 @@
## 1 原理说明
类似概念
* 可观察对象、观察者。(观察者模式)
* 发布者、订阅者。(发布订阅机制)
* 事件、响应。(事件驱动、事件监听机制、响应式编程)
### 事件驱动模型和观察者模式
一下名称具有相同的含义,都是观察着模式在不同场景下的实现。
* Spring的事件驱动模型
* 设计模式中的观察者模式
* jdk中的observable和observer
* ui中事件监听机制
* 消息队列的订阅发布机制
先是一种对象间的一对多的关系;最简单的如交通信号灯,信号灯是目标(一方),行人注视着信号灯(多方)。当目标发送改变(发布),观察者(订阅者)就可以接收到改变。 观察者如何处理(如行人如何走,是快走/慢走/不走,目标不会管的), 目标无需干涉;所以就松散耦合了它们之间的关系。
### Spring中实现
![](image/2022-10-18-12-06-37.png)
### 类图关系
* 事件
![](image/2022-10-18-12-07-26.png)
* 发布(动作)
* ApplicationContext 接口继承了 ApplicationEventPublisher并在 AbstractApplicationContext 实现了具体代码实际执行是委托给ApplicationEventMulticaster可以认为是多播
![](image/2022-10-18-12-08-25.png)
* 监听
![](image/2022-10-18-12-08-47.png)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 179 KiB

View File

@@ -9,6 +9,8 @@
> * 核心原理
> 。。。
> 参考文档
> https://blog.csdn.net/lin1214000999/article/details/105468338/
## 1 springboot背景
springboot + springcloud
### 微服务
@@ -127,3 +129,7 @@ Spring Boot 是基于 Spring 框架基础上推出的一个全新的框架, 旨
* 非常多的starter
* 引入了哪些场景这个场景的自动配置才会开启
* SpringBoot所有的自动配置功能都在 spring-boot-autoconfigure 包里面
## 知识体系
![](image/2023-11-18-20-31-33.png)

View File

@@ -1,155 +1,5 @@
## 1 springboot的启动过程
springcontext.run到底干了什么
![](image/2023-01-09-10-47-27.png)
SpringApplication.run()到底干了什么
### 服务构建
调用SpringApplication的静态run方法。通过一系列配置创建SpringApplication类。
1. 初始化资源加载器
2. 初始化服务类型
3. 初始化spring.factories中定义的初始化类。包括Initializer和Listener
4. 找到启动类
![](image/2023-01-09-10-56-25.png)
```java
/**
* Create a new {@link SpringApplication} instance. The application context will load
* beans from the specified primary sources (see {@link SpringApplication class-level}
* documentation for details). The instance can be customized before calling
* {@link #run(String...)}.
* @param resourceLoader the resource loader to use
* @param primarySources the primary bean sources
* @see #run(Class, String[])
* @see #setSources(Set)
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
```
### 环境准备
调用SpringApplicationContext的run方法。
1. 加载bootstrapContext上下文
2. 获取并注册监听器。
3. 加载环境变量,并发布环境变量加载完成的事件。(通过观察者模式)
![](image/2023-01-09-11-25-19.png)
```java
/**
* Run the Spring application, creating and refreshing a new
* {@link ApplicationContext}.
* @param args the application arguments (usually passed from a Java main method)
* @return a running {@link ApplicationContext}
*/
public ConfigurableApplicationContext run(String... args) {
long startTime = System.nanoTime();
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
}
listeners.started(context, timeTakenToStartup);
callRunners(context, applicationArguments);
}
```
### 容器创建
在run方法中创建容器上下文SpringApplicationContext
![](image/2023-01-09-11-39-47.png)
1. 默认创建AnnotationConfigServletWebServerApplicationContext。在该类中调用两个注解处理方法。
```java
public AnnotationConfigServletWebServerApplicationContext() {
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
```
2. 构建conttext。加载Initializer注册启动参数加载postProcess.
```java
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
listeners.contextPrepared(context);
bootstrapContext.close(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {
((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(this.allowCircularReferences);
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
}
```
3. 发布资源监听事件
```java
listeners.contextLoaded(context);
```
### 填充容器——自动装配
![](image/2023-01-09-11-40-17.png)
1. refreshContext(conext)
2. 发布启动完成事件调用自定义实现的runner接口。
## 2 自动配置加载的过程
## 1 自动配置加载的过程
### 加载过程

View File

@@ -0,0 +1,200 @@
## 0 概述
使用的标准方法
```java
@ServletComponentScan(basePackages = "com.atguigu.admin") :指定原生Servlet组件都放在那里
@WebServlet(urlPatterns = "/my")效果直接响应没有经过Spring的拦截器
@WebFilter(urlPatterns={"/css/*","/images/*"})
@WebListener
```
## WebServlet
```java
@WebServlet(urlPatterns = "/path2/*")
public class MyServlet extends HttpServlet {
@Override
protected void doGet (HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
PrintWriter writer = resp.getWriter();
writer.println("response from servlet ");
}
}
```
## WebFilter
```java
@WebFilter(urlPatterns = "/*")
public class MyFilter implements Filter{
@Override
public void init (FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter (ServletRequest request,
ServletResponse response, FilterChain chain)
throws IOException, ServletException {
String url = request instanceof HttpServletRequest ?
((HttpServletRequest) request).getRequestURL().toString() : "N/A";
System.out.println("from filter, processing url: "+url);
chain.doFilter(request, response);
}
@Override
public void destroy () {
}
}
```
## WebListener
```java
@WebListener
public class MyServletListener implements ServletContextListener{
@Override
public void contextInitialized (ServletContextEvent sce) {
System.out.println("from ServletContextListener: " +
" context initialized");
}
@Override
public void contextDestroyed (ServletContextEvent sce) {
}
}
```
### DispatcherServlet
DispatchServlet 如何注册进来
● 容器中自动配置了 DispatcherServlet 属性绑定到 WebMvcProperties对应的配置文件配置项是 spring.mvc。
● 通过 ServletRegistrationBean<DispatcherServlet> 把 DispatcherServlet 配置进来。
● 默认映射的是 / 路径。
![](image/2023-11-18-16-28-24.png)
### 使用RegistrationBean
ServletRegistrationBean, FilterRegistrationBean, and ServletListenerRegistrationBean
```java
@Configuration
public class MyRegistConfig {
@Bean
public ServletRegistrationBean myServlet(){
MyServlet myServlet = new MyServlet();
return new ServletRegistrationBean(myServlet,"/my","/my02");
}
@Bean
public FilterRegistrationBean myFilter(){
MyFilter myFilter = new MyFilter();
// return new FilterRegistrationBean(myFilter,myServlet());
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(myFilter);
filterRegistrationBean.setUrlPatterns(Arrays.asList("/my","/css/*"));
return filterRegistrationBean;
}
@Bean
public ServletListenerRegistrationBean myListener(){
MySwervletContextListener mySwervletContextListener = new MySwervletContextListener();
return new ServletListenerRegistrationBean(mySwervletContextListener);
}
}
```
## 2 嵌入式Servlet容器
### 方法
● 默认支持的webServer
○ Tomcat, Jetty, or Undertow
○ ServletWebServerApplicationContext 容器启动寻找ServletWebServerFactory 并引导创建服务器
● 切换服务器
```java
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
```
### 原理
* SpringBoot应用启动发现当前是Web应用。web场景包-导入tomcat
* web应用会创建一个web版的ioc容器 ServletWebServerApplicationContext
* ServletWebServerApplicationContext 启动的时候寻找 ServletWebServerFactoryServlet 的web服务器工厂---> Servlet 的web服务器
* SpringBoot底层默认有很多的WebServer工厂TomcatServletWebServerFactory, JettyServletWebServerFactory, or UndertowServletWebServerFactory
* 底层直接会有一个自动配置类。ServletWebServerFactoryAutoConfiguration
* ServletWebServerFactoryAutoConfiguration导入了ServletWebServerFactoryConfiguration配置类
* ServletWebServerFactoryConfiguration 配置类 根据动态判断系统中到底导入了那个Web服务器的包。默认是web-starter导入tomcat包容器中就有 TomcatServletWebServerFactory
* TomcatServletWebServerFactory 创建出Tomcat服务器并启动TomcatWebServer 的构造器拥有初始化方法initialize---this.tomcat.start();
* 内嵌服务器就是手动把启动服务器的代码调用tomcat核心jar包存在
## 3 定制Servlet容器
● 实现 WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>
○ 把配置文件的值和ServletWebServerFactory 进行绑定
● 修改配置文件 server.xxx
● 直接自定义 ConfigurableServletWebServerFactory
xxxxxCustomizer定制化器可以改变xxxx的默认规则
```java
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.stereotype.Component;
@Component
public class CustomizationBean implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
@Override
public void customize(ConfigurableServletWebServerFactory server) {
server.setPort(9000);
}
}
```
### 定制化的常见方式
多级定制化方式
● 修改配置文件;
● xxxxxCustomizer
● 编写自定义的配置类 xxxConfiguration+ @Bean替换、增加容器中默认组件;视图解析器
● Web应用 编写一个配置类实现 WebMvcConfigurer 即可定制化web功能+ @Bean给容器中再扩展一些组件
```java
@Configuration
public class AdminWebConfig implements WebMvcConfigurer
```
@EnableWebMvc + WebMvcConfigurer —— @Bean 可以全面接管SpringMVC所有规则全部自己重新配置 实现定制和扩展功能
○ 原理
○ 1、WebMvcAutoConfiguration 默认的SpringMVC的自动配置功能类。静态资源、欢迎页.....
○ 2、一旦使用 @EnableWebMvc 、。会 @Import(DelegatingWebMvcConfiguration.class)
○ 3、DelegatingWebMvcConfiguration 的 作用只保证SpringMVC最基本的使用
■ 把所有系统中的 WebMvcConfigurer 拿过来。所有功能的定制都是这些 WebMvcConfigurer 合起来一起生效
■ 自动配置了一些非常底层的组件。RequestMappingHandlerMapping、这些组件依赖的组件都是从容器中获取
■ public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport
○ 4、WebMvcAutoConfiguration 里面的配置要能生效 必须 @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
○ 5、@EnableWebMvc 导致了 WebMvcAutoConfiguration 没有生效。

View File

@@ -1,7 +1,10 @@
## 1 jdbc配置
> 数据库与数据源不是同一个东西。。。
> 三层关键概念需要理解
> 1. 数据库驱动mysql、hsqldb
> 2. 数据源datasource和数据库连接池Harica、Druid
> 3. 数据库操作工具JDBCTemplates、Mybatis
### 数据源配置
pom.xml
@@ -13,7 +16,7 @@ pom.xml
</dependency>
```
### 嵌入式数据库支持
### 嵌入式数据库驱动
嵌入式数据库支持H2、HSQL、Derby。不需要任何配置被集成到springboot的jar包当中。
```
<dependency>
@@ -23,7 +26,7 @@ pom.xml
</dependency>
```
### 连接mysql数据库
### mysql数据库驱动
* 引入mysql依赖包
@@ -50,7 +53,7 @@ JNDI不需要用户使用java代码与数据库建立连接而是将连接交
spring.datasource.jndi-name=java:jboss/datasources/customers
```
## 2 使用jdbcTemplate操作数据库
## 2 JdbcTemplate操作数据库
### 准备数据库
```
@@ -119,7 +122,7 @@ public interface UserService {
* 通过jdbcTemplate实现Userservice中定义的操作。
```
```java
@Service
public class UserServiceImpl implements UserService {
@@ -165,7 +168,7 @@ public class UserServiceImpl implements UserService {
### 编写单元测试用例
创建对UserService的单元测试用例通过创建、删除和查询来验证数据库操作的正确性。
```
```java
@RunWith(SpringRunner.class)
@SpringBootTest
public class Chapter31ApplicationTests {

View File

@@ -0,0 +1,207 @@
## 1 基本概念
### JDBC
java数据库链接java database connectivity。java语言用来规范客户访问数据库的应用程序接口。提供了查询、更新数据库的方法。java.sql与javax.sql主要包括以下类
* DriverManager:负责加载不同的驱动程序Driver返回相应的数据库连接Connection。
* Driver对应数据库的驱动程序。
* Connection数据库连接负责与数据库进行通信。可以产生SQL的statement.
* Statement:用来执行SQL查询和更新。
* CallableStatement用以调用数据库中的存储过程。
* SQLException代表数据库联机额的建立和关闭和SQL语句中发生的例情况。
### 数据源
1. 封装关于数据库访问的各种参数,实现统一管理。
2. 通过数据库的连接池管理,节省开销并提高效率。
> 简单理解,就是在用户程序与数据库之间,建立新的缓冲地带,用来对用户的请求进行优化,对数据库的访问进行整合。
常见的数据源DBCP、C3P0、Druid、HikariCP。
## 2 HikariCP默认数据源配置
### 通用配置
以spring.datasource.*的形式存在,包括数据库连接地址、用户名、密码。
```
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
```
### 数据源连接配置
以spring.datasource.<数据源名称>.*的形式存在,
```
spring.datasource.hikari.minimum-idle=10//最小空闲连接
spring.datasource.hikari.maximum-pool-size=20//最大连接数
spring.datasource.hikari.idle-timeout=500000//控线连接超时时间
spring.datasource.hikari.max-lifetime=540000//最大存活时间
spring.datasource.hikari.connection-timeout=60000//连接超时时间
spring.datasource.hikari.connection-test-query=SELECT 1//用于测试连接是否可用的查询语句
```
## 3 druid数据源
### pom.xml配置druid依赖
```
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.21</version>
</dependency>
```
### application.properties配置数据库连接信息
以spring.datasource.druid作为前缀
```
spring.datasource.druid.url=jdbc:mysql://localhost:3306/test
spring.datasource.druid.username=root
spring.datasource.druid.password=
spring.datasource.druid.driver-class-name=com.mysql.cj.jdbc.Driver
```
### 配置druid连接池
> 具体的信息可以自己查询相关的内容。
```
spring.datasource.druid.initialSize=10
spring.datasource.druid.maxActive=20
spring.datasource.druid.maxWait=60000
spring.datasource.druid.minIdle=1
spring.datasource.druid.timeBetweenEvictionRunsMillis=60000
spring.datasource.druid.minEvictableIdleTimeMillis=300000
spring.datasource.druid.testWhileIdle=true
spring.datasource.druid.testOnBorrow=true
spring.datasource.druid.testOnReturn=false
spring.datasource.druid.poolPreparedStatements=true
spring.datasource.druid.maxOpenPreparedStatements=20
spring.datasource.druid.validationQuery=SELECT 1
spring.datasource.druid.validation-query-timeout=500
spring.datasource.druid.filters=stat
```
yaml实例
```yaml
spring:
datasource:
url: jdbc:mysql://localhost:3306/db_account
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
druid:
aop-patterns: com.atguigu.admin.* #监控SpringBean
filters: stat,wall # 底层开启功能statsql监控wall防火墙
stat-view-servlet: # 配置监控页功能
enabled: true
login-username: admin
login-password: admin
resetEnable: false
web-stat-filter: # 监控web
enabled: true
urlPattern: /*
exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'
filter:
stat: # 对上面filters里面的stat的详细配置
slow-sql-millis: 1000
logSlowSql: true
enabled: true
wall:
enabled: true
config:
drop-table-allow: false
```
### 配置druid监控
* 在pom.xml中增加依赖
```
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
```
* 在application.properties中添加druid监控配置
```
spring.datasource.druid.stat-view-servlet.enabled=true
spring.datasource.druid.stat-view-servlet.url-pattern=/druid/*
spring.datasource.druid.stat-view-servlet.reset-enable=true
spring.datasource.druid.stat-view-servlet.login-username=admin
spring.datasource.druid.stat-view-servlet.login-password=admin
```
* 在xml中添加监控配置(可选,通过配置文件配置即可)
```xml
需要给数据源中配置如下属性可以允许多个filter多个用分割
<property name="filters" value="stat,slf4j" />
<bean id="stat-filter" class="com.alibaba.druid.filter.stat.StatFilter">
<property name="slowSqlMillis" value="10000" />
<property name="logSlowSql" value="true" />
</bean>
使用 slowSqlMillis 定义慢SQL的时长
```
## 4 原理
### 自动配置的类
● DataSourceAutoConfiguration 数据源的自动配置
○ 修改数据源相关的配置spring.datasource
○ 数据库连接池的配置是自己容器中没有DataSource才自动配置的
○ 底层配置好的连接池是HikariDataSource
```java
@Configuration(proxyBeanMethods = false)
@Conditional(PooledDataSourceCondition.class)
@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
@Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,
DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.OracleUcp.class,
DataSourceConfiguration.Generic.class, DataSourceJmxConfiguration.class })
protected static class PooledDataSourceConfiguration
```
● DataSourceTransactionManagerAutoConfiguration 事务管理器的自动配置
● JdbcTemplateAutoConfiguration JdbcTemplate的自动配置可以来对数据库进行crud
○ 可以修改这个配置项@ConfigurationProperties(prefix = "spring.jdbc") 来修改JdbcTemplate
@Bean@Primary JdbcTemplate容器中有这个组件
● JndiDataSourceAutoConfiguration jndi的自动配置
● XADataSourceAutoConfiguration 分布式事务相关的
### Druid自动配置原理
分析自动配置
● 扩展配置项 spring.datasource.druid
● DruidSpringAopConfiguration.class, 监控SpringBean的配置项spring.datasource.druid.aop-patterns
● DruidStatViewServletConfiguration.class, 监控页的配置spring.datasource.druid.stat-view-servlet默认开启
● DruidWebStatFilterConfiguration.class, web监控配置spring.datasource.druid.web-stat-filter默认开启
● DruidFilterConfiguration.class}) 所有Druid自己filter的配置
```java
private static final String FILTER_STAT_PREFIX = "spring.datasource.druid.filter.stat";
private static final String FILTER_CONFIG_PREFIX = "spring.datasource.druid.filter.config";
private static final String FILTER_ENCODING_PREFIX = "spring.datasource.druid.filter.encoding";
private static final String FILTER_SLF4J_PREFIX = "spring.datasource.druid.filter.slf4j";
private static final String FILTER_LOG4J_PREFIX = "spring.datasource.druid.filter.log4j";
private static final String FILTER_LOG4J2_PREFIX = "spring.datasource.druid.filter.log4j2";
private static final String FILTER_COMMONS_LOG_PREFIX = "spring.datasource.druid.filter.commons-log";
private static final String FILTER_WALL_PREFIX = "spring.datasource.druid.filter.wall";
```

View File

@@ -1,8 +1,22 @@
> 对象关系映射模型Hibernate。用来实现非常轻量级的对象的封装。将对象与数据库建立映射关系。实现增删查改。
> MyBatis与Hibernate非常相似。对象关系映射模型ORG。java对象与关系数据库映射的模型。
## 1 配置MyBatis
### 最佳实践
最佳实战:
● 引入mybatis-starter
● 配置application.yaml中指定mapper-location位置即可
● 编写Mapper接口并标注@Mapper注解
● 简单方法直接注解方式
● 复杂方法编写mapper.xml进行绑定映射
@MapperScan("com.atguigu.admin.mapper") 简化,其他的接口就可以不用标注@Mapper注解
### 在pom.xml中添加MyBatis依赖
```
@@ -21,7 +35,7 @@
### 配置数据库连接
在application.properties中配置mysql的链接配置
```
```sh
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=
@@ -30,7 +44,7 @@ spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
### 创建关系数据表
```
```sql
CREATE TABLE `User` (
`id` bigint NOT NULL AUTO_INCREMENT,
`name` varchar(100) COLLATE utf8mb4_general_ci DEFAULT NULL,
@@ -41,7 +55,7 @@ CREATE TABLE `User` (
### 创建数据表的java对象
```
```java
@Data
@NoArgsConstructor
public class User {
@@ -58,20 +72,6 @@ public class User {
}
```
### 创建数据表的操作接口
```
@Mapper
public interface UserMapper {
@Select("SELECT * FROM USER WHERE NAME = #{name}")
User findByName(@Param("name") String name);
@Insert("INSERT INTO USER(NAME, AGE) VALUES(#{name}, #{age})")
int insert(@Param("name") String name, @Param("age") Integer age);
}
```
## 2 MyBatis参数传递
@@ -99,9 +99,25 @@ userMapper.insertByMap(map);
@Insert("INSERT INTO USER(NAME, AGE) VALUES(#{name}, #{age})")
int insertByUser(User user);
```
## 3 增删查改操作
## 3 注解模式
### 创建数据表的操作接口
```java
@Mapper
public interface UserMapper {
@Select("SELECT * FROM USER WHERE NAME = #{name}")
User findByName(@Param("name") String name);
@Insert("INSERT INTO USER(NAME, AGE) VALUES(#{name}, #{age})")
int insert(@Param("name") String name, @Param("age") Integer age);
}
```
### 增删改查操作
```java
public interface UserMapper {
@Select("SELECT * FROM user WHERE name = #{name}")
@@ -118,7 +134,7 @@ public interface UserMapper {
}
```
对增删查改的调用
```
```java
@Transactional
@RunWith(SpringRunner.class)
@SpringBootTest
@@ -147,7 +163,7 @@ public class ApplicationTests {
}
```
## 4 使用MyBatis的XML方式
## 4 XML方式
### 在应用主类中增加mapper的扫描包配置
```
@@ -215,5 +231,93 @@ public class Chapter36ApplicationTests {
Assert.assertEquals(20, u.getAge().intValue());
}
}
```
## 5 整合 MyBatis-Plus 完成CRUD
### 什么是MyBatis-Plus
MyBatis-Plus简称 MP是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
mybatis plus 官网
建议安装 MybatisX 插件
### 引入
```xml
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
```
### 自动配置
● MybatisPlusAutoConfiguration 配置类MybatisPlusProperties 配置项绑定。mybatis-plusxxx 就是对mybatis-plus的定制
● SqlSessionFactory 自动配置好。底层是容器中默认的数据源
● mapperLocations 自动配置好的。有默认值。classpath*:/mapper/**/*.xml任意包的类路径下的所有mapper文件夹下任意路径下的所有xml都是sql映射文件。 建议以后sql映射文件放在 mapper下
● 容器中也自动配置好了 SqlSessionTemplate
@Mapper 标注的接口也会被自动扫描;建议直接 @MapperScan("com.atguigu.admin.mapper") 批量扫描就行
### 优点
优点:
● 只需要我们的Mapper继承 BaseMapper 就可以拥有crud能力
### CRUD
DAO层
```java
@GetMapping("/user/delete/{id}")
public String deleteUser(@PathVariable("id") Long id,
@RequestParam(value = "pn",defaultValue = "1")Integer pn,
RedirectAttributes ra){
userService.removeById(id);
ra.addAttribute("pn",pn);
return "redirect:/dynamic_table";
}
@GetMapping("/dynamic_table")
public String dynamic_table(@RequestParam(value="pn",defaultValue = "1") Integer pn,Model model){
//表格内容的遍历
// response.sendError
// List<User> users = Arrays.asList(new User("zhangsan", "123456"),
// new User("lisi", "123444"),
// new User("haha", "aaaaa"),
// new User("hehe ", "aaddd"));
// model.addAttribute("users",users);
//
// if(users.size()>3){
// throw new UserTooManyException();
// }
//从数据库中查出user表中的用户进行展示
//构造分页参数
Page<User> page = new Page<>(pn, 2);
//调用page进行分页
Page<User> userPage = userService.page(page, null);
// userPage.getRecords()
// userPage.getCurrent()
// userPage.getPages()
model.addAttribute("users",userPage);
return "table/dynamic_table";
}
```
### Service层
```java
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper,User> implements UserService {
}
public interface UserService extends IService<User> {
}
```

View File

@@ -0,0 +1,68 @@
## 1 简介
### 引入
NoSQL
Redis 是一个开源BSD许可内存中的数据结构存储系统它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串strings 散列hashes 列表lists 集合sets 有序集合sorted sets 与范围查询, bitmaps hyperloglogs 和 地理空间geospatial 索引半径查询。 Redis 内置了 复制replicationLUA脚本Lua scripting LRU驱动事件LRU eviction事务transactions 和不同级别的 磁盘持久化persistence 并通过 Redis哨兵Sentinel和自动 分区Cluster提供高可用性high availability
### 自动配置
```xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
```
![](image/2023-11-18-16-53-56.png)
### 配置原理
自动配置:
● RedisAutoConfiguration 自动配置类。RedisProperties 属性类 --> spring.redis.xxx是对redis的配置
● 连接工厂是准备好的。LettuceConnectionConfiguration、JedisConnectionConfiguration
● 自动注入了RedisTemplate<Object, Object> xxxTemplate
● 自动注入了StringRedisTemplatekv都是String
● keyvalue
● 底层只要我们使用 StringRedisTemplate、RedisTemplate就可以操作redis
### 操作
```java
@Test
void testRedis(){
ValueOperations<String, String> operations = redisTemplate.opsForValue();
operations.set("hello","world");
String hello = operations.get("hello");
System.out.println(hello);
}
```
### 切换jedis
```java
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 导入jedis-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
```
```yml
spring:
redis:
host: r-bp1nc7reqesxisgxpipd.redis.rds.aliyuncs.com
port: 6379
password: lfy:Lfy123456
client-type: jedis
jedis:
pool:
max-active: 10
```

View File

@@ -0,0 +1,491 @@
> springboot with junit4 &junit5 https://segmentfault.com/a/1190000040803747
## 1 概述
### spring-boot-starter-test
SpringBoot中有关测试的框架主要来源于 spring-boot-starter-test。一旦依赖了spring-boot-starter-test下面这些类库将被一同依赖进去
* JUnitjava测试事实上的标准。
* Spring Test & Spring Boot TestSpring的测试支持。
* AssertJ提供了流式的断言方式。
* Hamcrest提供了丰富的matcher。
* Mockitomock框架可以按类型创建mock对象可以根据方法参数指定特定的响应也支持对于mock调用过程的断言。
* JSONassert为JSON提供了断言功能。
* JsonPath为JSON提供了XPATH功能。
### 多种测试模式
* @RunWith(SpringJUnit4ClassRunner.class)启动Spring上下文环境。
* @RunWith(MockitoJUnitRunner.class)mockito方法进行测试。对底层的类进行mock测试速度快。
* @RunWith(PowerMockRunner.class)powermock方法进行测试。对底层类进行mock测试方法更全面。
### junit5 变化
Spring Boot 2.2.0 版本开始引入 JUnit 5 作为单元测试默认库
作为最新版本的JUnit框架JUnit5与之前版本的Junit框架有很大的不同。由三个不同子项目的几个不同模块组成。
JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage
* JUnit Platform: Junit Platform是在JVM上启动测试框架的基础不仅支持Junit自制的测试引擎其他测试引擎也都可以接入。
* JUnit Jupiter: JUnit Jupiter提供了JUnit5的新的编程模型是JUnit5新特性的核心。内部 包含了一个测试引擎用于在Junit Platform上运行。
* JUnit Vintage: 由于JUint已经发展多年为了照顾老的项目JUnit Vintage提供了兼容JUnit4.x,Junit3.x的测试引擎。
![](image/2023-11-18-18-53-47.png)
```java
@SpringBootTest
class Boot05WebAdminApplicationTests {
@Test
void contextLoads() {
}
}
```
SpringBoot 2.4 以上版本移除了默认对 Vintage 的依赖。如果需要兼容junit4需要自行引入不能使用junit4的功能 @Test。JUnit 5s Vintage Engine Removed from spring-boot-starter-test,如果需要继续兼容junit4需要自行引入vintage
```xml
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
</exclusion>
</exclusions>
</dependency>
```
以前:`@SpringBootTest + @RunWith(SpringTest.class)`。SpringBoot整合Junit以后。编写测试方法@Test标注注意需要使用junit5版本的注解。Junit类具有Spring的功能@Autowired、比如 @Transactional 标注测试方法,测试完成后自动回滚
### junit4 & junit5对比
| 功能 | JUnit4 | JUnit5 |
|------------------|--------------|--------------|
| 声明一种测试方法 | @Test | @Test |
| 在当前类中的所有测试方法之前执行 | @BeforeClass | @BeforeAll |
| 在当前类中的所有测试方法之后执行 | @AfterClass | @AfterAll |
| 在每个测试方法之前执行 | @Before | @BeforeEach |
| 在每个测试方法之后执行 | @After | @AfterEach |
| 禁用测试方法/类 | @Ignore | @Disabled |
| 测试工厂进行动态测试 | NA | @TestFactory |
| 嵌套测试 | NA | @Nested |
| 标记和过滤 | @Category | @Tag |
| 注册自定义扩展 | NA | @ExtendWith |
### RunWith 和 ExtendWith
在 JUnit4 版本,在测试类加 @SpringBootTest 注解时,同样要加上 @RunWith(SpringRunner.class)才生效,即:
```
@SpringBootTest
@RunWith(SpringRunner.class)
class HrServiceTest {
...
}
```
但在 JUnit5 中,官网告知 @RunWith 的功能都被 @ExtendWith 替代,即原 @RunWith(SpringRunner.class) 被同功能的 @ExtendWith(SpringExtension.class) 替代。但 JUnit5 中 @SpringBootTest 注解中已经默认包含了 @ExtendWith(SpringExtension.class)。
因此,在 JUnit5 中只需要单独使用 @SpringBootTest 注解即可。其他需要自定义拓展的再用 @ExtendWith,不要再用 @RunWith 了。
### mockito
Mockito 框架中最核心的两个概念就是 Mock 和 Stub。测试时不是真正的操作外部资源而是通过自定义的代码进行模拟操作。我们可以对任何的依赖进行模拟从而使测试的行为不需要任何准备工作或者不具备任何副作用。
1. 当我们在测试时,如果只关心某个操作是否执行过,而不关心这个操作的具体行为,这种技术称为 mock。比如我们测试的代码会执行发送邮件的操作我们对这个操作进行 mock测试的时候我们只关心是否调用了发送邮件的操作而不关心邮件是否确实发送出去了。
2. 另一种情况,当我们关心操作的具体行为,或者操作的返回结果的时候,我们通过执行预设的操作来代替目标操作,或者返回预设的结果作为目标操作的返回结果。这种对操作的模拟行为称为 stub打桩。比如我们测试代码的异常处理机制是否正常我们可以对某处代码进行 stub让它抛出异常。再比如我们测试的代码需要向数据库插入一条数据我们可以对插入数据的代码进行stub让它始终返回1表示数据插入成功。
### powermock
需要手动引入测试类。依赖mockito注意版本的对应关系。
```xml
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
</dependency>
```
### 断言测试
![](image/2023-11-18-18-58-17.png)
```java
@Test
@DisplayName("simple assertion")
public void simple() {
assertEquals(3, 1 + 2, "simple math");
assertNotEquals(3, 1 + 1);
assertNotSame(new Object(), new Object());
Object obj = new Object();
assertSame(obj, obj);
assertFalse(1 > 2);
assertTrue(1 < 2);
assertNull(null);
assertNotNull(new Object());
}
```
数组断言
```java
@Test
@DisplayName("array assertion")
public void array() {
assertArrayEquals(new int[]{1, 2}, new int[] {1, 2});
}
```
组合断言。assertAll 方法接受多个 org.junit.jupiter.api.Executable 函数式接口的实例作为要验证的断言,可以通过 lambda 表达式很容易的提供这些断言
```java
@Test
@DisplayName("assert all")
public void all() {
assertAll("Math",
() -> assertEquals(2, 1 + 1),
() -> assertTrue(1 > 0)
);
}
```
异常断言
在JUnit4时期想要测试方法的异常情况时需要用@Rule注解的ExpectedException变量还是比较麻烦的。而JUnit5提供了一种新的断言方式Assertions.assertThrows() ,配合函数式编程就可以进行使用。
```java
@Test
@DisplayName("异常测试")
public void exceptionTest() {
ArithmeticException exception = Assertions.assertThrows(
//扔出断言异常
ArithmeticException.class, () -> System.out.println(1 % 0));
}
```
超时断言
Junit5还提供了Assertions.assertTimeout() 为测试方法设置了超时时间
```java
@Test
@DisplayName("超时测试")
public void timeoutTest() {
//如果测试方法时间超过1s将会异常
Assertions.assertTimeout(Duration.ofMillis(1000), () -> Thread.sleep(500));
}
```
快速失败
通过 fail 方法直接使得测试失败
```java
@Test
@DisplayName("fail")
public void shouldFail() {
fail("This should fail");
}
```
### 前置条件assumptions
JUnit 5 中的前置条件assumptions【假设】类似于断言不同之处在于不满足的断言会使得测试方法失败而不满足的前置条件只会使得测试方法的执行终止。前置条件可以看成是测试方法执行的前提当该前提不满足时就没有继续执行的必要。
```java
@DisplayName("前置条件")
public class AssumptionsTest {
private final String environment = "DEV";
@Test
@DisplayName("simple")
public void simpleAssume() {
assumeTrue(Objects.equals(this.environment, "DEV"));
assumeFalse(() -> Objects.equals(this.environment, "PROD"));
}
@Test
@DisplayName("assume then do")
public void assumeThenDo() {
assumingThat(
Objects.equals(this.environment, "DEV"),
() -> System.out.println("In DEV")
);
}
}
```
assumeTrue 和 assumFalse 确保给定的条件为 true 或 false不满足条件会使得测试执行终止。assumingThat 的参数是表示条件的布尔值和对应的 Executable 接口的实现对象。只有条件满足时Executable 对象才会被执行;当条件不满足时,测试执行并不会终止。
## 2 使用
### 注解
@RunWith
1. 表示运行方式,@RunWith(JUnit4TestRunner)、@RunWith(SpringRunner.class)、@RunWith(PowerMockRunner.class) 三种运行方式,分别在不同的场景中使用。
2. 当一个类用@RunWith注释或继承一个用@RunWith注释的类时JUnit将调用它所引用的类来运行该类中的测试而不是开发者去在junit内部去构建它。我们在开发过程中使用这个特性
@SpringBootTest
1. 注解制定了一个测试类运行了Spring Boot环境。提供了以下一些特性
1. 当没有特定的ContextConfiguration#loader()@ContextConfiguration(loader=...)被定义那么就是SpringBootContextLoader作为默认的ContextLoader。
2. 自动搜索到SpringBootConfiguration注解的文件。
3. 允许自动注入Environment类读取配置文件。
4. 提供一个webEnvironment环境可以完整的允许一个web环境使用随机的端口或者自定义的端口。
5. 注册了TestRestTemplate类可以去做接口调用。
2. 添加这个就能取到spring中的容器的实例如果配置了@Autowired那么就自动将对象注入
### 基本测试用例
Springboot整合JUnit的步骤
1. 导入测试对应的starter
2. 测试类使用@SpringBootTest修饰
3.使用自动装配的形式添加要测试的对象。
```java
@SpringBootTest(classes = {SpringbootJunitApplication.class})
class SpringbootJunitApplicationTests {
@Autowired
private UsersDao users;
@Test
void contextLoads() {
users.save();
}
}
```
### mock单元测试
因为单元测试不用启动 Spring 容器,则无需加 @SpringBootTest,因为要用到 Mockito只需要自定义拓展 MockitoExtension.class 即可,依赖简单,运行速度更快。
可以明显看到,单元测试写的代码,怎么是被测试代码长度的好几倍?其实单元测试的代码长度比较固定,都是造数据和打桩,但如果针对越复杂逻辑的代码写单元测试,还是越划算的
```java
@ExtendWith(MockitoExtension.class)
class HrServiceTest {
@Mock
private OrmDepartmentDao ormDepartmentDao;
@Mock
private OrmUserDao ormUserDao;
@InjectMocks
private HrService hrService;
@DisplayName("根据部门名称,查询用户")
@Test
void findUserByDeptName() {
Long deptId = 100L;
String deptName = "行政部";
OrmDepartmentPO ormDepartmentPO = new OrmDepartmentPO();
ormDepartmentPO.setId(deptId);
ormDepartmentPO.setDepartmentName(deptName);
OrmUserPO user1 = new OrmUserPO();
user1.setId(1L);
user1.setUsername("001");
user1.setDepartmentId(deptId);
OrmUserPO user2 = new OrmUserPO();
user2.setId(2L);
user2.setUsername("002");
user2.setDepartmentId(deptId);
List<OrmUserPO> userList = new ArrayList<>();
userList.add(user1);
userList.add(user2);
Mockito.when(ormDepartmentDao.findOneByDepartmentName(deptName))
.thenReturn(
Optional.ofNullable(ormDepartmentPO)
.filter(dept -> deptName.equals(dept.getDepartmentName()))
);
Mockito.doReturn(
userList.stream()
.filter(user -> deptId.equals(user.getDepartmentId()))
.collect(Collectors.toList())
).when(ormUserDao).findByDepartmentId(deptId);
List<OrmUserPO> result1 = hrService.findUserByDeptName(deptName);
List<OrmUserPO> result2 = hrService.findUserByDeptName(deptName + "error");
Assertions.assertEquals(userList, result1);
Assertions.assertEquals(Collections.emptyList(), result2);
}
```
### 集成单元测试
还是那个方法如果使用Spring上下文真实的调用方法依赖可直接用下列方式
```java
@SpringBootTest
class HrServiceTest {
@Autowired
private HrService hrService;
@DisplayName("根据部门名称,查询用户")
@Test
void findUserByDeptName() {
List<OrmUserPO> userList = hrService.findUserByDeptName("行政部");
Assertions.assertTrue(userList.size() > 0);
}
}
```
还可以使用@MockBean@SpyBean替换Spring上下文中的对应的Bean
```java
@SpringBootTest
class HrServiceTest {
@Autowired
private HrService hrService;
@SpyBean
private OrmDepartmentDao ormDepartmentDao;
@DisplayName("根据部门名称,查询用户")
@Test
void findUserByDeptName() {
String deptName="行政部";
OrmDepartmentPO ormDepartmentPO = new OrmDepartmentPO();
ormDepartmentPO.setDepartmentName(deptName);
Mockito.when(ormDepartmentDao.findOneByDepartmentName(ArgumentMatchers.anyString()))
.thenReturn(Optional.of(ormDepartmentPO));
List<OrmUserPO> userList = hrService.findUserByDeptName(deptName);
Assertions.assertTrue(userList.size() > 0);
}
}
```
### 嵌套测试
JUnit 5 可以通过 Java 中的内部类和@Nested 注解实现嵌套测试,从而可以更好的把相关的测试方法组织在一起。在内部类中可以使用@BeforeEach@AfterEach 注解,而且嵌套的层次没有限制。
```java
@DisplayName("A stack")
class TestingAStackDemo {
Stack<Object> stack;
@Test
@DisplayName("is instantiated with new Stack()")
void isInstantiatedWithNew() {
new Stack<>();
}
@Nested
@DisplayName("when new")
class WhenNew {
@BeforeEach
void createNewStack() {
stack = new Stack<>();
}
@Test
@DisplayName("is empty")
void isEmpty() {
assertTrue(stack.isEmpty());
}
@Test
@DisplayName("throws EmptyStackException when popped")
void throwsExceptionWhenPopped() {
assertThrows(EmptyStackException.class, stack::pop);
}
@Test
@DisplayName("throws EmptyStackException when peeked")
void throwsExceptionWhenPeeked() {
assertThrows(EmptyStackException.class, stack::peek);
}
@Nested
@DisplayName("after pushing an element")
class AfterPushing {
String anElement = "an element";
@BeforeEach
void pushAnElement() {
stack.push(anElement);
}
@Test
@DisplayName("it is no longer empty")
void isNotEmpty() {
assertFalse(stack.isEmpty());
}
@Test
@DisplayName("returns the element when popped and is empty")
void returnElementWhenPopped() {
assertEquals(anElement, stack.pop());
assertTrue(stack.isEmpty());
}
@Test
@DisplayName("returns the element when peeked but remains not empty")
void returnElementWhenPeeked() {
assertEquals(anElement, stack.peek());
assertFalse(stack.isEmpty());
}
}
}
}
```
### 参数化测试
参数化测试是JUnit5很重要的一个新特性它使得用不同的参数多次运行测试成为了可能也为我们的单元测试带来许多便利。
利用@ValueSource等注解,指定入参,我们将可以使用不同的参数进行多次单元测试,而不需要每新增一个参数就新增一个单元测试,省去了很多冗余代码。
@ValueSource: 为参数化测试指定入参来源支持八大基础类以及String类型,Class类型
@NullSource: 表示为参数化测试提供一个null的入参
@EnumSource: 表示为参数化测试提供一个枚举入参
@CsvFileSource表示读取指定CSV文件内容作为参数化测试入参
@MethodSource:表示读取指定方法的返回值作为参数化测试入参(注意方法返回需要是一个流)
当然如果参数化测试仅仅只能做到指定普通的入参还达不到让我觉得惊艳的地步。让我真正感到他的强大之处的地方在于他可以支持外部的各类入参。如:CSV,YML,JSON 文件甚至方法的返回值也可以作为入参。只需要去实现ArgumentsProvider接口任何外部文件都可以作为它的入参。
```java
@ParameterizedTest
@ValueSource(strings = {"one", "two", "three"})
@DisplayName("参数化测试1")
public void parameterizedTest1(String string) {
System.out.println(string);
Assertions.assertTrue(StringUtils.isNotBlank(string));
}
@ParameterizedTest
@MethodSource("method") //指定方法名
@DisplayName("方法来源参数")
public void testWithExplicitLocalMethodSource(String name) {
System.out.println(name);
Assertions.assertNotNull(name);
}
static Stream<String> method() {
return Stream.of("apple", "banana");
}
```
### 迁移指南
在进行迁移的时候需要注意如下的变化:
● 注解在 org.junit.jupiter.api 包中,断言在 org.junit.jupiter.api.Assertions 类中,前置条件在 org.junit.jupiter.api.Assumptions 类中。
● 把@Before@After 替换成@BeforeEach@AfterEach
● 把@BeforeClass@AfterClass 替换成@BeforeAll@AfterAll
● 把@Ignore 替换成@Disabled
● 把@Category 替换成@Tag
● 把@RunWith@Rule@ClassRule 替换成@ExtendWith

View File

@@ -1,109 +0,0 @@
## 1 基本概念
### JDBC
java数据库链接java database connectivity。java语言用来规范客户访问数据库的应用程序接口。提供了查询、更新数据库的方法。java.sql与javax.sql主要包括以下类
* DriverManager:负责加载不同的驱动程序Driver返回相应的数据库连接Connection。
* Driver对应数据库的驱动程序。
* Connection数据库连接负责与数据库进行通信。可以产生SQL的statement.
* Statement:用来执行SQL查询和更新。
* CallableStatement用以调用数据库中的存储过程。
* SQLException代表数据库联机额的建立和关闭和SQL语句中发生的例情况。
### 数据源
1. 封装关于数据库访问的各种参数,实现统一管理。
2. 通过数据库的连接池管理,节省开销并提高效率。
> 简单理解,就是在用户程序与数据库之间,建立新的缓冲地带,用来对用户的请求进行优化,对数据库的访问进行整合。
常见的数据源DBCP、C3P0、Druid、HikariCP。
## 2 HikariCP默认数据源配置
### 通用配置
以spring.datasource.*的形式存在,包括数据库连接地址、用户名、密码。
```
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
```
### 数据源连接配置
以spring.datasource.<数据源名称>.*的形式存在,
```
spring.datasource.hikari.minimum-idle=10//最小空闲连接
spring.datasource.hikari.maximum-pool-size=20//最大连接数
spring.datasource.hikari.idle-timeout=500000//控线连接超时时间
spring.datasource.hikari.max-lifetime=540000//最大存活时间
spring.datasource.hikari.connection-timeout=60000//连接超时时间
spring.datasource.hikari.connection-test-query=SELECT 1//用于测试连接是否可用的查询语句
```
## 3 druid数据源
### pom.xml配置druid依赖
```
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.21</version>
</dependency>
```
### application.properties配置数据库连接信息
以spring.datasource.druid作为前缀
```
spring.datasource.druid.url=jdbc:mysql://localhost:3306/test
spring.datasource.druid.username=root
spring.datasource.druid.password=
spring.datasource.druid.driver-class-name=com.mysql.cj.jdbc.Driver
```
### 配置druid连接池
> 具体的信息可以自己查询相关的内容。
```
spring.datasource.druid.initialSize=10
spring.datasource.druid.maxActive=20
spring.datasource.druid.maxWait=60000
spring.datasource.druid.minIdle=1
spring.datasource.druid.timeBetweenEvictionRunsMillis=60000
spring.datasource.druid.minEvictableIdleTimeMillis=300000
spring.datasource.druid.testWhileIdle=true
spring.datasource.druid.testOnBorrow=true
spring.datasource.druid.testOnReturn=false
spring.datasource.druid.poolPreparedStatements=true
spring.datasource.druid.maxOpenPreparedStatements=20
spring.datasource.druid.validationQuery=SELECT 1
spring.datasource.druid.validation-query-timeout=500
spring.datasource.druid.filters=stat
```
### 配置druid监控
* 在pom.xml中增加依赖
```
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
```
* 在application.properties中添加druid监控配置
```
spring.datasource.druid.stat-view-servlet.enabled=true
spring.datasource.druid.stat-view-servlet.url-pattern=/druid/*
spring.datasource.druid.stat-view-servlet.reset-enable=true
spring.datasource.druid.stat-view-servlet.login-username=admin
spring.datasource.druid.stat-view-servlet.login-password=admin
```

View File

@@ -0,0 +1,294 @@
## 1 简介
### 概念
未来每一个微服务在云上部署以后我们都需要对其进行监控、追踪、审计、控制等。SpringBoot就抽取了Actuator场景使得我们每个微服务快速引用即可获得生产级别的应用监控、审计等功能。
### 引入
```xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
```
![](image/2023-11-18-19-06-53.png)
## 2 使用
### 简单使用
1. 暴露指标
```xml
management:
endpoints:
enabled-by-default: true #暴露所有端点信息
web:
exposure:
include: '*' #以web方式暴露
```
2. 访问界面
```
http://localhost:8080/actuator/beans
http://localhost:8080/actuator/configprops
http://localhost:8080/actuator/metrics
http://localhost:8080/actuator/metrics/jvm.gc.pause
http://localhost:8080/actuator/endpointName/detailPath
```
### 可视化
https://github.com/codecentric/spring-boot-admin
### 常用指标
| ID | 描述 |
|------------------|--------------------------------------------------------------------------------------|
| auditevents | 暴露当前应用程序的审核事件信息。需要一个AuditEventRepository组件。 |
| beans | 显示应用程序中所有Spring Bean的完整列表。 |
| caches | 暴露可用的缓存。 |
| conditions | 显示自动配置的所有条件信息,包括匹配或不匹配的原因。 |
| configprops | 显示所有@ConfigurationProperties。 |
| env | 暴露Spring的属性ConfigurableEnvironment |
| flyway | 显示已应用的所有Flyway数据库迁移。
需要一个或多个Flyway组件。 |
| health | 显示应用程序运行状况信息。 |
| httptrace | 显示HTTP跟踪信息默认情况下最近100个HTTP请求-响应。需要一个HttpTraceRepository组件。 |
| info | 显示应用程序信息。 |
| integrationgraph | 显示Spring&nbsp;integrationgraph 。需要依赖spring-integration-core。 |
| loggers | 显示和修改应用程序中日志的配置。 |
| liquibase | 显示已应用的所有Liquibase数据库迁移。需要一个或多个Liquibase组件。 |
| metrics | 显示当前应用程序的“指标”信息。 |
| mappings | 显示所有@RequestMapping路径列表。 |
| scheduledtasks | 显示应用程序中的计划任务。 |
| sessions | 允许从Spring Session支持的会话存储中检索和删除用户会话。需要使用Spring Session的基于Servlet的Web应用程序。 |
| shutdown | 使应用程序正常关闭。默认禁用。 |
| startup | 显示由ApplicationStartup收集的启动步骤数据。需要使用SpringApplication进行配置BufferingApplicationStartup。 |
| threaddump | 执行线程转储。 |
#### 最常用的Endpoint
● Health监控状况
● Metrics运行时指标
● Loggers日志记录
#### Health Endpoint
健康检查端点我们一般用于在云平台平台会定时的检查应用的健康状况我们就需要Health Endpoint可以为平台返回当前应用的一系列组件健康状况的集合。
重要的几点:
● health endpoint返回的结果应该是一系列健康检查后的一个汇总报告
● 很多的健康检查默认已经自动配置好了比如数据库、redis等
● 可以很容易的添加自定义的健康检查机制
#### Metrics Endpoint
提供详细的、层级的、空间指标信息这些信息可以被pull主动推送或者push被动获取方式得到
● 通过Metrics对接多种监控系统
● 简化核心Metrics开发
● 添加自定义Metrics或者扩展已有Metrics
#### 管理Endpoints
1、开启与禁用Endpoints
● 默认所有的Endpoint除过shutdown都是开启的。
● 需要开启或者禁用某个Endpoint。配置模式为 management.endpoint.<endpointName>.enabled = true
```java
management:
endpoint:
beans:
enabled: true
```
或者禁用所有的Endpoint然后手动开启指定的Endpoint
```java
management:
endpoints:
enabled-by-default: false
endpoint:
beans:
enabled: true
health:
enabled: true
```
#### 支持的暴露方式
● HTTP默认只暴露health和info Endpoint
● JMX默认暴露所有Endpoint
● 除过health和info剩下的Endpoint都应该进行保护访问。如果引入SpringSecurity则会默认配置安全访问规则
## 3 自定义扩展
#### 定制 Health 信息
```java
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;
@Component
public class MyHealthIndicator implements HealthIndicator {
@Override
public Health health() {
int errorCode = check(); // perform some specific health check
if (errorCode != 0) {
return Health.down().withDetail("Error Code", errorCode).build();
}
return Health.up().build();
}
}
构建Health
Health build = Health.down()
.withDetail("msg", "error service")
.withDetail("code", "500")
.withException(new RuntimeException())
.build();
```
配置文件
```sh
management:
health:
enabled: true
show-details: always #总是显示详细信息。可显示每个模块的状态信息
```
```java
@Component
public class MyComHealthIndicator extends AbstractHealthIndicator {
/**
* 真实的检查方法
* @param builder
* @throws Exception
*/
@Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
//mongodb。 获取连接进行测试
Map<String,Object> map = new HashMap<>();
// 检查完成
if(1 == 2){
// builder.up(); //健康
builder.status(Status.UP);
map.put("count",1);
map.put("ms",100);
}else {
// builder.down();
builder.status(Status.OUT_OF_SERVICE);
map.put("err","连接超时");
map.put("ms",3000);
}
builder.withDetail("code",100)
.withDetails(map);
}
}
```
### 定制info信息
编写配置文件
```
info:
appName: boot-admin
version: 2.0.1
mavenProjectName: @project.artifactId@ #使用@@可以获取maven的pom文件值
mavenProjectVersion: @project.version@
```
编写InfoContributor
```java
import java.util.Collections;
import org.springframework.boot.actuate.info.Info;
import org.springframework.boot.actuate.info.InfoContributor;
import org.springframework.stereotype.Component;
@Component
public class ExampleInfoContributor implements InfoContributor {
@Override
public void contribute(Info.Builder builder) {
builder.withDetail("example",
Collections.singletonMap("key", "value"));
}
}
```
http://localhost:8080/actuator/info
### 定制Metrics信息
1、SpringBoot支持自动适配的Metrics
● JVM metrics, report utilization of:
○ Various memory and buffer pools
○ Statistics related to garbage collection
○ Threads utilization
○ Number of classes loaded/unloaded
● CPU metrics
● File descriptor metrics
● Kafka consumer and producer metrics
● Log4j2 metrics: record the number of events logged to Log4j2 at each level
● Logback metrics: record the number of events logged to Logback at each level
● Uptime metrics: report a gauge for uptime and a fixed gauge representing the applications absolute start time
● Tomcat metrics (server.tomcat.mbeanregistry.enabled must be set to true for all Tomcat metrics to be registered)
● Spring Integration metrics
2、增加定制Metrics
```java
class MyService{
Counter counter;
public MyService(MeterRegistry meterRegistry){
counter = meterRegistry.counter("myservice.method.running.counter");
}
public void hello() {
counter.increment();
}
}
//也可以使用下面的方式
@Bean
MeterBinder queueSize(Queue queue) {
return (registry) -> Gauge.builder("queueSize", queue::size).register(registry);
}
```
### 定制Endpoint
```java
@Component
@Endpoint(id = "container")
public class DockerEndpoint {
@ReadOperation
public Map getDockerInfo(){
return Collections.singletonMap("info","docker started...");
}
@WriteOperation
private void restartDocker(){
System.out.println("docker restarted....");
}
}
```
场景开发ReadinessEndpoint来管理程序是否就绪或者LivenessEndpoint来管理程序是否存活
当然,这个也可以直接使用 https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html#production-ready-kubernetes-probes

View File

@@ -1,216 +0,0 @@
> springboot with junit4 &junit5 https://segmentfault.com/a/1190000040803747
## 1 概述
### 多种测试模式
* @RunWith(SpringJUnit4ClassRunner.class)启动Spring上下文环境。
* @RunWith(MockitoJUnitRunner.class)mockito方法进行测试。对底层的类进行mock测试速度快。
* @RunWith(PowerMockRunner.class)powermock方法进行测试。对底层类进行mock测试方法更全面。
### spring-boot-starter-test
SpringBoot中有关测试的框架主要来源于 spring-boot-starter-test。一旦依赖了spring-boot-starter-test下面这些类库将被一同依赖进去
* JUnitjava测试事实上的标准。
* Spring Test & Spring Boot TestSpring的测试支持。
* AssertJ提供了流式的断言方式。
* Hamcrest提供了丰富的matcher。
* Mockitomock框架可以按类型创建mock对象可以根据方法参数指定特定的响应也支持对于mock调用过程的断言。
* JSONassert为JSON提供了断言功能。
* JsonPath为JSON提供了XPATH功能。
### junit4 & junit5对比
| 功能 | JUnit4 | JUnit5 |
|------------------|--------------|--------------|
| 声明一种测试方法 | @Test | @Test |
| 在当前类中的所有测试方法之前执行 | @BeforeClass | @BeforeAll |
| 在当前类中的所有测试方法之后执行 | @AfterClass | @AfterAll |
| 在每个测试方法之前执行 | @Before | @BeforeEach |
| 在每个测试方法之后执行 | @After | @AfterEach |
| 禁用测试方法/类 | @Ignore | @Disabled |
| 测试工厂进行动态测试 | NA | @TestFactory |
| 嵌套测试 | NA | @Nested |
| 标记和过滤 | @Category | @Tag |
| 注册自定义扩展 | NA | @ExtendWith |
### RunWith 和 ExtendWith
在 JUnit4 版本,在测试类加 @SpringBootTest 注解时,同样要加上 @RunWith(SpringRunner.class)才生效,即:
```
@SpringBootTest
@RunWith(SpringRunner.class)
class HrServiceTest {
...
}
```
但在 JUnit5 中,官网告知 @RunWith 的功能都被 @ExtendWith 替代,即原 @RunWith(SpringRunner.class) 被同功能的 @ExtendWith(SpringExtension.class) 替代。但 JUnit5 中 @SpringBootTest 注解中已经默认包含了 @ExtendWith(SpringExtension.class)。
因此,在 JUnit5 中只需要单独使用 @SpringBootTest 注解即可。其他需要自定义拓展的再用 @ExtendWith,不要再用 @RunWith 了。
### mockito
Mockito 框架中最核心的两个概念就是 Mock 和 Stub。测试时不是真正的操作外部资源而是通过自定义的代码进行模拟操作。我们可以对任何的依赖进行模拟从而使测试的行为不需要任何准备工作或者不具备任何副作用。
1. 当我们在测试时,如果只关心某个操作是否执行过,而不关心这个操作的具体行为,这种技术称为 mock。比如我们测试的代码会执行发送邮件的操作我们对这个操作进行 mock测试的时候我们只关心是否调用了发送邮件的操作而不关心邮件是否确实发送出去了。
2. 另一种情况,当我们关心操作的具体行为,或者操作的返回结果的时候,我们通过执行预设的操作来代替目标操作,或者返回预设的结果作为目标操作的返回结果。这种对操作的模拟行为称为 stub打桩。比如我们测试代码的异常处理机制是否正常我们可以对某处代码进行 stub让它抛出异常。再比如我们测试的代码需要向数据库插入一条数据我们可以对插入数据的代码进行stub让它始终返回1表示数据插入成功。
### powermock
需要手动引入测试类。依赖mockito注意版本的对应关系。
```xml
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
</dependency>
```
## 2 使用
### 注解
@RunWith
1. 表示运行方式,@RunWith(JUnit4TestRunner)、@RunWith(SpringRunner.class)、@RunWith(PowerMockRunner.class) 三种运行方式,分别在不同的场景中使用。
2. 当一个类用@RunWith注释或继承一个用@RunWith注释的类时JUnit将调用它所引用的类来运行该类中的测试而不是开发者去在junit内部去构建它。我们在开发过程中使用这个特性
@SpringBootTest
1. 注解制定了一个测试类运行了Spring Boot环境。提供了以下一些特性
1. 当没有特定的ContextConfiguration#loader()@ContextConfiguration(loader=...)被定义那么就是SpringBootContextLoader作为默认的ContextLoader。
2. 自动搜索到SpringBootConfiguration注解的文件。
3. 允许自动注入Environment类读取配置文件。
4. 提供一个webEnvironment环境可以完整的允许一个web环境使用随机的端口或者自定义的端口。
5. 注册了TestRestTemplate类可以去做接口调用。
2. 添加这个就能取到spring中的容器的实例如果配置了@Autowired那么就自动将对象注入
### 基本测试用例
Springboot整合JUnit的步骤
1. 导入测试对应的starter
2. 测试类使用@SpringBootTest修饰
3.使用自动装配的形式添加要测试的对象。
```java
@SpringBootTest(classes = {SpringbootJunitApplication.class})
class SpringbootJunitApplicationTests {
@Autowired
private UsersDao users;
@Test
void contextLoads() {
users.save();
}
}
```
### mock单元测试
因为单元测试不用启动 Spring 容器,则无需加 @SpringBootTest,因为要用到 Mockito只需要自定义拓展 MockitoExtension.class 即可,依赖简单,运行速度更快。
可以明显看到,单元测试写的代码,怎么是被测试代码长度的好几倍?其实单元测试的代码长度比较固定,都是造数据和打桩,但如果针对越复杂逻辑的代码写单元测试,还是越划算的
```java
@ExtendWith(MockitoExtension.class)
class HrServiceTest {
@Mock
private OrmDepartmentDao ormDepartmentDao;
@Mock
private OrmUserDao ormUserDao;
@InjectMocks
private HrService hrService;
@DisplayName("根据部门名称,查询用户")
@Test
void findUserByDeptName() {
Long deptId = 100L;
String deptName = "行政部";
OrmDepartmentPO ormDepartmentPO = new OrmDepartmentPO();
ormDepartmentPO.setId(deptId);
ormDepartmentPO.setDepartmentName(deptName);
OrmUserPO user1 = new OrmUserPO();
user1.setId(1L);
user1.setUsername("001");
user1.setDepartmentId(deptId);
OrmUserPO user2 = new OrmUserPO();
user2.setId(2L);
user2.setUsername("002");
user2.setDepartmentId(deptId);
List<OrmUserPO> userList = new ArrayList<>();
userList.add(user1);
userList.add(user2);
Mockito.when(ormDepartmentDao.findOneByDepartmentName(deptName))
.thenReturn(
Optional.ofNullable(ormDepartmentPO)
.filter(dept -> deptName.equals(dept.getDepartmentName()))
);
Mockito.doReturn(
userList.stream()
.filter(user -> deptId.equals(user.getDepartmentId()))
.collect(Collectors.toList())
).when(ormUserDao).findByDepartmentId(deptId);
List<OrmUserPO> result1 = hrService.findUserByDeptName(deptName);
List<OrmUserPO> result2 = hrService.findUserByDeptName(deptName + "error");
Assertions.assertEquals(userList, result1);
Assertions.assertEquals(Collections.emptyList(), result2);
}
```
### 集成单元测试
还是那个方法如果使用Spring上下文真实的调用方法依赖可直接用下列方式
```java
@SpringBootTest
class HrServiceTest {
@Autowired
private HrService hrService;
@DisplayName("根据部门名称,查询用户")
@Test
void findUserByDeptName() {
List<OrmUserPO> userList = hrService.findUserByDeptName("行政部");
Assertions.assertTrue(userList.size() > 0);
}
}
```
还可以使用@MockBean@SpyBean替换Spring上下文中的对应的Bean
```java
@SpringBootTest
class HrServiceTest {
@Autowired
private HrService hrService;
@SpyBean
private OrmDepartmentDao ormDepartmentDao;
@DisplayName("根据部门名称,查询用户")
@Test
void findUserByDeptName() {
String deptName="行政部";
OrmDepartmentPO ormDepartmentPO = new OrmDepartmentPO();
ormDepartmentPO.setDepartmentName(deptName);
Mockito.when(ormDepartmentDao.findOneByDepartmentName(ArgumentMatchers.anyString()))
.thenReturn(Optional.of(ormDepartmentPO));
List<OrmUserPO> userList = hrService.findUserByDeptName(deptName);
Assertions.assertTrue(userList.size() > 0);
}
}
```

View File

@@ -0,0 +1,93 @@
## 1 Profile
### Profile功能
为了方便多环境适配springboot简化了profile功能。
application-profile功能
● 默认配置文件 application.yaml任何时候都会加载
● 指定环境配置文件 application-{env}.yaml
● 激活指定环境
○ 配置文件激活
○ 命令行激活java -jar xxx.jar --spring.profiles.active=prod --person.name=haha
■ 修改配置文件的任意值,命令行优先
● 默认配置与环境配置同时生效
● 同名配置项profile配置优先
### @Profile条件装配功能
@Configuration(proxyBeanMethods = false)
@Profile("production")
public class ProductionConfiguration {
// ...
}
### Profile 分组功能
spring.profiles.group.production[0]=proddb
spring.profiles.group.production[1]=prodmq
使用:--spring.profiles.active=production 激活
## 2 外部化配置
### 配置文件优先级
https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-external-config
1. Default properties (specified by setting SpringApplication.setDefaultProperties).
2. @PropertySource annotations on your @Configuration classes. Please note that such property sources are not added to the Environment until the application context is being refreshed. This is too late to configure certain properties such as logging.* and spring.main.* which are read before refresh begins.
3. Config data (such as application.properties files)
4. A RandomValuePropertySource that has properties only in random.*.
5. OS environment variables.
6. Java System properties (System.getProperties()).
7. JNDI attributes from java:comp/env.
8. ServletContext init parameters.
9. ServletConfig init parameters.
10. Properties from SPRING_APPLICATION_JSON (inline JSON embedded in an environment variable or system property).
11. Command line arguments.
12. properties attribute on your tests. Available on @SpringBootTest and the test annotations for testing a particular slice of your application.
13. @TestPropertySource annotations on your tests.
14. Devtools global settings properties in the $HOME/.config/spring-boot directory when devtools is active.
常用Java属性文件、YAML文件、环境变量、命令行参数
### 配置文件的路径
(1) classpath 根路径
(2) classpath 根路径下config目录
(3) jar包当前目录
(4) jar包当前目录的config目录
(5) /config子目录的直接子目录
### 配置文件加载顺序:
1.  当前jar包内部的application.properties和application.yml
2.  当前jar包内部的application-{profile}.properties 和 application-{profile}.yml
3.  引用的外部jar包的application.properties和application.yml
4.  引用的外部jar包的application-{profile}.properties 和 application-{profile}.yml
指定环境优先,外部优先,后面的可以覆盖前面的同名配置项
## 3 自定义starter
![](image/2023-11-18-20-10-32.png)
starter-pom引入 autoconfigurer 包
● autoconfigure包中配置使用 META-INF/spring.factories 中 EnableAutoConfiguration 的值,使得项目启动加载指定的自动配置类
● 编写自动配置类 xxxAutoConfiguration -> xxxxProperties
@Configuration
@Conditional
@EnableConfigurationProperties
@Bean
○ ......
引入starter --- xxxAutoConfiguration --- 容器中放入组件 ---- 绑定xxxProperties ---- 配置项
自定义starter
atguigu-hello-spring-boot-starter启动器
atguigu-hello-spring-boot-starter-autoconfigure自动配置包

View File

@@ -0,0 +1,311 @@
## 1 springboot的启动过程
springcontext.run到底干了什么
![](image/2023-01-09-10-47-27.png)
SpringApplication.run()到底干了什么
### 服务构建
调用SpringApplication的静态run方法。通过一系列配置创建SpringApplication类。
1. 初始化资源加载器
2. 初始化服务类型
3. 初始化spring.factories中定义的初始化类。包括Initializer和Listener
4. 找到启动类
![](image/2023-01-09-10-56-25.png)
```java
/**
* Create a new {@link SpringApplication} instance. The application context will load
* beans from the specified primary sources (see {@link SpringApplication class-level}
* documentation for details). The instance can be customized before calling
* {@link #run(String...)}.
* @param resourceLoader the resource loader to use
* @param primarySources the primary bean sources
* @see #run(Class, String[])
* @see #setSources(Set)
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
```
### 环境准备
调用SpringApplicationContext的run方法。
1. 加载bootstrapContext上下文
2. 获取并注册监听器。
3. 加载环境变量,并发布环境变量加载完成的事件。(通过观察者模式)
![](image/2023-01-09-11-25-19.png)
```java
/**
* Run the Spring application, creating and refreshing a new
* {@link ApplicationContext}.
* @param args the application arguments (usually passed from a Java main method)
* @return a running {@link ApplicationContext}
*/
public ConfigurableApplicationContext run(String... args) {
long startTime = System.nanoTime();
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
}
listeners.started(context, timeTakenToStartup);
callRunners(context, applicationArguments);
}
```
### 容器创建
在run方法中创建容器上下文SpringApplicationContext
![](image/2023-01-09-11-39-47.png)
1. 默认创建AnnotationConfigServletWebServerApplicationContext。在该类中调用两个注解处理方法。
```java
public AnnotationConfigServletWebServerApplicationContext() {
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
```
2. 构建conttext。加载Initializer注册启动参数加载postProcess.
```java
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
listeners.contextPrepared(context);
bootstrapContext.close(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {
((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(this.allowCircularReferences);
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
}
```
3. 发布资源监听事件
```java
listeners.contextLoaded(context);
```
### 填充容器——自动装配
![](image/2023-01-09-11-40-17.png)
1. refreshContext(conext)
2. 发布启动完成事件调用自定义实现的runner接口。
## 2 SpringBoot初始化过程
> Spring原理【Spring注解】、SpringMVC原理、自动配置原理、SpringBoot原理
### 创建 SpringApplication
1. 保存一些信息。
2. 判定当前应用的类型。ClassUtils。Servlet
3. bootstrappers初始启动引导器List<Bootstrapper>去spring.factories文件中找 org.springframework.boot.Bootstrapper
4. 找 ApplicationContextInitializer去spring.factories找 ApplicationContextInitializer
1. List<ApplicationContextInitializer<?>> initializers
5. 找 ApplicationListener 应用监听器。去spring.factories找 ApplicationListener
1. List<ApplicationListener<?>> listeners
### 运行 SpringApplication
1. StopWatch记录应用的启动时间
2. 创建引导上下文Context环境createBootstrapContext()
3. 获取到所有之前的 bootstrappers 挨个执行 intitialize() 来完成对引导启动器上下文环境设置
4. 让当前应用进入headless模式。java.awt.headless
5. 获取所有 RunListener运行监听器【为了方便所有Listener进行事件感知】
1. getSpringFactoriesInstances 去spring.factories找 SpringApplicationRunListener.
6. 遍历 SpringApplicationRunListener 调用 starting 方法;
1. 相当于通知所有感兴趣系统正在启动过程的人,项目正在 starting。
7. 保存命令行参数ApplicationArguments
8. 准备环境 prepareEnvironment;
1. 返回或者创建基础环境信息对象。StandardServletEnvironment
2. 配置环境信息对象。
3. 读取所有的配置源的配置属性值。
4. 绑定环境信息
5. 监听器调用 listener.environmentPrepared();通知所有的监听器当前环境准备完成
9. 创建IOC容器createApplicationContext
1. 根据项目类型Servlet创建容器
2. 当前会创建 AnnotationConfigServletWebServerApplicationContext
10. 准备ApplicationContext IOC容器的基本信息 prepareContext()
1. 保存环境信息
2. IOC容器的后置处理流程。
3. 应用初始化器applyInitializers
1. 遍历所有的 ApplicationContextInitializer 。调用 initialize.。来对ioc容器进行初始化扩展功能
2. 遍历所有的 listener 调用 contextPrepared。EventPublishRunListenr通知所有的监听器contextPrepared
4. 所有的监听器 调用 contextLoaded。通知所有的监听器 contextLoaded
11. 刷新IOC容器。refreshContext
1. 创建容器中的所有组件Spring注解
2. 容器刷新完成后工作afterRefresh
12. 所有监听 器 调用 listeners.started(context); 通知所有的监听器 started
13. 调用所有runnerscallRunners()
1. 获取容器中的 ApplicationRunner
2. 获取容器中的 CommandLineRunner
3. 合并所有runner并且按照@Order进行排序
4. 遍历所有的runner。调用 run 方法
14. 如果以上有异常调用Listener 的 failed
15. 调用所有监听器的 running 方法 listeners.running(context); 通知所有的监听器 running
16. running如果有问题。继续通知 failed 。调用所有 Listener 的 failed通知所有的监听器 failed
## 3 SpringBoot扩展点
### 扩展点原理
扩展点的服务发现机制
1. 利用SPI服务发现机制完成在Context初始化之前的扩展。
2. 利用@注解和接口机制完成IOC容器初始化之后的扩展。
扩展点观察者模式
1. 定义Lisenter。通过回调接口监听SpringBoot启动过程。
主要的扩展点如下
1. ApplicationContextInitializer
1. ApplicationListener
1. SpringApplicationRunListener
1. ApplicationRunner
1. CommandLineRunner
### 扩展点BootStrapper
```java
public interface Bootstrapper {
/**
* Initialize the given {@link BootstrapRegistry} with any required registrations.
* @param registry the registry to initialize
*/
void intitialize(BootstrapRegistry registry);
}
```
### 扩展点ContextInitializer
![](image/2023-11-18-20-24-19.png)
### 扩展点RunListener
![](image/2023-11-18-20-24-27.png)
![](image/2023-11-18-20-24-40.png)
### 扩展点ApplicationRunner
```java
@FunctionalInterface
public interface ApplicationRunner {
/**
* Callback used to run the bean.
* @param args incoming application arguments
* @throws Exception on error
*/
void run(ApplicationArguments args) throws Exception;
}
```
### 扩展点CommandLineRunner
```java
@FunctionalInterface
public interface CommandLineRunner {
/**
* Callback used to run the bean.
* @param args incoming main method arguments
* @throws Exception on error
*/
void run(String... args) throws Exception;
}
```
## 4 springApplication创建的方法
### 1.通过类的静态方法直接创建
```java
SpringApplication.run(ApplicationConfiguration.class,args);
```
### 2.通过自定义SpringApplication创建
```java
SpringApplication springApplication = new SpringApplication(ApplicationConfiguration.class); //这里也是传入配置源,但也可以不传
springApplication.setWebApplicationType(WebApplicationType.NONE); //指定服务类型 可以指定成非Web应用和SERVLET应用以及REACTIVE应用
springApplication.setAdditionalProfiles("prod"); //prodFiles配置
Set<String> sources = new HashSet<>(); //创建配置源
sources.add(ApplicationConfiguration.class.getName()); //指定配置源
springApplication.setSources(sources); //设置配置源,注意配置源可以多个
ConfigurableApplicationContext context = springApplication.run(args); //运行SpringApplication 返回值为服务上下文对象
context.close(); //上下文关闭
```
### 3.通过Builder工厂模式
> 只是一种初始化对象的方法。
```java
ConfigurableApplicationContext context = new SpringApplicationBuilder(ApplicationConfiguration.class)//这里也是传入配置源,但也可以不传
.web(WebApplicationType.REACTIVE)
.profiles("java7")
.sources(ApplicationConfiguration.class) //可以多个Class
.run();
context.close(); //上下文关闭
```

View File

@@ -1,8 +0,0 @@
#include <stdio.h>
int main(int argc, char **argv){
int i = 10;
char *b = &i;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 715 KiB

View File

@@ -1,33 +0,0 @@
# springApplication创建的方法
### 1.通过类的静态方法直接创建
```java
SpringApplication.run(ApplicationConfiguration.class,args);
```
### 2.通过自定义SpringApplication创建
```java
SpringApplication springApplication = new SpringApplication(ApplicationConfiguration.class); //这里也是传入配置源,但也可以不传
springApplication.setWebApplicationType(WebApplicationType.NONE); //指定服务类型 可以指定成非Web应用和SERVLET应用以及REACTIVE应用
springApplication.setAdditionalProfiles("prod"); //prodFiles配置
Set<String> sources = new HashSet<>(); //创建配置源
sources.add(ApplicationConfiguration.class.getName()); //指定配置源
springApplication.setSources(sources); //设置配置源,注意配置源可以多个
ConfigurableApplicationContext context = springApplication.run(args); //运行SpringApplication 返回值为服务上下文对象
context.close(); //上下文关闭
```
### 3.通过Builder工厂模式
> 只是一种初始化对象的方法。
```java
ConfigurableApplicationContext context = new SpringApplicationBuilder(ApplicationConfiguration.class)//这里也是传入配置源,但也可以不传
.web(WebApplicationType.REACTIVE)
.profiles("java7")
.sources(ApplicationConfiguration.class) //可以多个Class
.run();
context.close(); //上下文关闭
```