接上一篇https://blog.csdn.net/m0_46128962/article/details/109174205
五.Docker
1.简介
Docker是一个开源的应用容器引擎;是一个轻量级容器技术;
Docker支持将软件编译成一个镜像;然后在镜像中各种软件做好配置,将镜像发布出去,其他使用者可以直接使 用这个镜像;
运行中的这个镜像称为容器,容器启动是非常快速的。
2.核心概念
docker主机(Host):安装了Docker程序的机器(Docker直接安装在操作系统之上);
docker客户端(Client):连接docker主机进行操作;
docker仓库(Registry):用来保存各种打包好的软件镜像;
docker镜像(Images):软件打包好的镜像;放在docker仓库中;
docker容器(Container):镜像启动后的实例称为一个容器;容器是独立运行的一个或一组应用
使用Docker的步骤:
1)、安装Docker
2)、去Docker仓库找到这个软件对应的镜像;
3)、使用Docker运行这个镜像,这个镜像就会生成一个Docker容器; 4)、对容器的启动停止就是对软件的启动停止;
3.安装Docker
1)、安装linux虚拟机
1、VMWare、VirtualBox(安装);
2、导入虚拟机文件centos7-atguigu.ova;
3、双击启动linux虚拟机;使用 root/ 123456登陆
4、使用客户端连接linux服务器进行命令操作;
5、设置虚拟机网络;
桥接网络=选好网卡==接入网线;
6、设置好网络以后使用命令重启虚拟机的网络
service network restart
7、查看linux的ip地址
ip addr
8、查看IP后将IP地址填入连接的输入栏,使用客户端连接linux; (当你的WiFi连接的校园网可能造成无法分配IP的问题,造成后面的docker安装有问题,最好使用稳定的网络重新建立一个连接)
2)、在linux虚拟机上安装docker
1、检查内核版本必须是3.10及以上
uname -r
2、安装docker(这里出问题的可以去看看https://blog.csdn.net/qq_39720249/article/details/84104646)
yum install docker
3、输入y确认安装
4、启动docker
[root@localhost ~]# systemctl start docker
[root@localhost ~]# docker -v
5、设置打开虚拟机开机启动docker
[root@localhost ~]# systemctl enable docker
6、停止docker
[root@localhost ~]# systemctl stop docker
4.Docker常用命令&操作
1)、镜像操作
操作 | 命令 | 说明 |
---|---|---|
检索 | docker search 关键字 eg:docker search redis | 我们经常去docker hub上检索镜像的详细信息,如镜 像的TAG。 |
拉取 | docker pull 镜像名:tag | :tag是可选的,tag表示标签,多为软件的版本,后面可以接版本号docker pull:(版本号),默认 是latest |
列表 | docker images | 查看所有本地镜像 |
删除 | docker rmi image-id | 删除指定的本地镜像 |
在下载MySQL的时候最好配置阿里云的镜像服务,不然会下载的很慢。地址:https://www.cnblogs.com/360minitao/p/11960655.html
2)、容器操作
软件镜像(QQ安装程序)—运行镜像—产生一个容器(正在运行的软件,运行的QQ);
步骤:
- 搜索镜像
docker search tomcat
- 拉取镜像
docker pull tomcat
- 根据镜像启动容器(后面的版本号可加可不加)
docker run --name mytomcat -d tomcat:latest
- 查看运行中的容器
docker ps
- 停止运行中的容器(这里后面的mytomcat这个位置可以用docker ps中的names也可以用CONTAINER ID)
docker stop mytomcat
- 查看所有的容器(包括运行过的)
docker ps -a
- 启动容器(跟run的区别是run是创建一个没有启动过的,而start是启动一个停止了的)
docker start 容器ID或者name
- 删除容器(一定要停止状态)
docker rm a2b1cb16d8c9(或者name)
- 启动一个做了端口映射的Tomcat
docker run -d -p 8888:8080 tomcat
-d:后台运行
-p将主机的一个端口映射到容器的一个端口 主机端口:容器内部端口
- 防火墙
service firewalld status(查看防火墙状态)
service firewalld stop (关闭防火墙)
- 查看容器的日志
docker logs 2cf186bfb462(容器ID或者容器名)
更多命令参考docker官方文档中每一个镜像的文档
https://docs.docker.com/engine/reference/commandline/docker/
3)、安装mysql示例
https://hub.docker.com/到docker hub中寻找镜像及镜像操作
先
- docker pull mysql
再
- docker run ‐‐name mysql01 ‐d mysql
但是这是错误的:通过docker ps我们看不到MySQL正在运行而docker ps -a
可以看到MySQL运行过了
通过:
docker logs MySQLId//查看MySQL的日志可以看到我们没有配置密码。
在docker hub 中查找正确的命令。
[root@localhost ~]# docker run --name mysql01 -e MYSQL_ROOT_PASSWORD=123456 -d mysql
2d4a7c0bed264f4a5c3dead38287ba5e67b7d706bf6a47211440222172137c28
[root@localhost ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2d4a7c0bed26 mysql "docker-entrypoint..." 11 seconds ago Up 11 seconds 3306/tcp, 33060/tcp mysql01
然后我们还应该做端口映射。
[root@localhost ~]# docker run -p 3306:3306 --name mysql01 -e MYSQL_ROOT_PASSWORD=123456 -d mysql
6759e7219f3e2f5b282e090d38a69b38971ef9d17b10d30fdccdfaeaad188910
几个其他的高级操作:
1、用客户端的配置文件
$ docker run --name some-mysql -v /conf/mysql:/etc/mysql/conf.d -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag
把主机的/conf/mysql文件夹挂载到 mysqldocker容器的/etc/mysql/conf.d文件夹里面
改mysql的配置文件就只需要把mysql配置文件放在自定义的文件夹下(/conf/mysql)
2、不用配置文件直接配置来制定MySQL的一些配置参数。
$ docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
六.Spring Boot 与数据访问
1.jdbc
先导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://192.168.43.243:3306/jdbc
driver-class-name: com.mysql.cj.jdbc.Driver
效果:
默认是用class com.zaxxer.hikari.HikariDataSource作为数据源;
数据源的相关配置都在DataSourceProperties里面;(点击yml中的DataSource下的任意一个)
自动配置原理:
org\springframework\boot\autoconfigure\jdbc
1)、参考DataSourceConfiguration,根据配置创建数据源,默认使用HikariDataSource数据源;可以使用spring.datasource.type指定自定义的数据源类型
2)、spring boot默认可以支持:
BasicDataSource、HikariDataSource、org.apache.tomcat.jdbc.pool.DataSource
3)、自定义数据源类型
@ConditionalOnMissingBean({DataSource.class})
@ConditionalOnProperty(
name = {"spring.datasource.type"}
)
static class Generic {
Generic() {
}
@Bean
DataSource dataSource(DataSourceProperties properties) {
//使用DataSourceBuilder创建数据源,查看build()可以发现利用反射创建响应type的数据源,并且绑定相关属性
return properties.initializeDataSourceBuilder().build();
}
}
4)、同包下的DataSourceInitializerInvoker继承ApplicationListener;可以看到它调用同包下的DataSourceInitializer中的各种schema操作,我们查看DataSourceInitializer
作用:
1、runScripts(scripts, username, password);运行建表语句;
2、runDataScripts();运行插入数据的sql语句;
默认只需要将文件命名为:
schema‐*.sql、data‐*.sql
默认规则:schema.sql,schema‐all.sql;//这两种可以直接使用
在yml中可以使用
schema:
‐ classpath:department.sql
指定位置
在2.0以后必须加上一句
spring.datasource.initialization-mode=always
这里是原理
private List<Resource> getScripts(String propertyName, List<String> resources, String fallback) {
if (resources != null) {
return this.getResources(propertyName, resources, true);
} else {
String platform = this.properties.getPlatform();
List<String> fallbackResources = new ArrayList();
fallbackResources.add("classpath*:" + fallback + "-" + platform + ".sql");
fallbackResources.add("classpath*:" + fallback + ".sql");
return this.getResources(propertyName, fallbackResources, false);
}
}
这是我的几种尝试,亲测可行
5)、操作数据库:自动配置了JdbcTemplate操作数据库
@Controller
public class QueryController {
@Autowired
JdbcTemplate jdbcTemplate;
@ResponseBody
@GetMapping("/find")
public Map<String,Object> a(){
List<Map<String, Object>> maps = jdbcTemplate.queryForList("select * FROM department");
return maps.get(0);
}
}
2.整合Druid数据源
Arrays.asList()
该方法是将数组转化成List集合的方法
//导入Druid数据源
@Configuration
public class DruidConfig {
@ConfigurationProperties(prefix = "spring.datasource")
@Bean
public DataSource druid(){
return new DruidDataSource();
}
//配置Druid的监控
//1、配置一个管理后台的Servlet
@Bean
public ServletRegistrationBean statViewServlet(){
ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
Map<String,String> initParams = new HashMap<>();
initParams.put("loginUsername","admin");
initParams.put("loginPassword","123456");
initParams.put("allow","");//默认就是允许所有访问
initParams.put("deny"," 192.168.43.213");
bean.setInitParameters(initParams);
return bean;
}
//2、配置一个web监控的filter
@Bean
public FilterRegistrationBean webStatFilter(){
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setFilter(new WebStatFilter());
Map<String,String> initParams = new HashMap<>();
initParams.put("exclusions","*.js,*.css,/druid/*");
bean.setInitParameters(initParams);
bean.setUrlPatterns(Arrays.asList("/*"));
return bean;
}
}
3.整合mybatis
<!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
步骤:
1)、配置数据源相关属性(见上一节Druid)
2)、给数据库建表
#spring.datasource.schema=classpath:sql/department.sql,classpath:sql/employee.sql
#spring.datasource.initialization-mode=always
3)、创建JavaBean(表中的bean)
4)、注解版
//指定这是一个操作数据库的mapper
@Mapper
public interface DepartmentMapper {
@Select("select * from department where id=#{id}")
public Department getDeptById(Integer id);
@Delete("delete from department where id=#{id}")
public int deleteDeptById(Integer id);
@Options(useGeneratedKeys = true,keyProperty = "id")
@Insert("insert into department(department-name) values(#{departmentName})")
public int insertDept(Department department);
@Update("update department set departmentName=#{departmentName} where id=#{id}")
public int updateDept(Department department);
}
问题:在上面的查找中会出现sql语句的错误,我们应该开启驼峰匹配:
可以在配置文件中加入
mybatis.configuration.map-underscore-to-camel-case=true
#开启驼峰匹配
也可以自定义MyBatis的配置规则;给容器中添加一个ConfigurationCustomizer;
@org.springframework.context.annotation.Configuration
public class MybatisConfig {
@Bean
public ConfigurationCustomizer configurationCustomizer(){
return new ConfigurationCustomizer(){
@Override
public void customize(Configuration configuration) {
configuration.setMapUnderscoreToCamelCase(true);
}
};
}
}
除了在mapper类上注解@Mapper之外我们还可以在启动类的上面加上下面代码来批量扫描所有接口。
@MapperScan(value = "com.nyb.springboot04web.mapper")
@SpringBootApplication
public class SpringBoot04WebRestfulcrudApplication {
5)、配置文件版
mybatis.config-location=classpath:mybatis/mybatis-config.xml
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml
但是这里出现了问题,原来是在主程序类上还是要加上@MapperScan这个注解,否则不会把mapper注入容器中。
总结:这两个XML只是起一个将mapper和xml文件绑定的作用,加入容器中还是要我们自己来。
4.整合SpringData JPA
1)、SpringData JPA简介
2)、整合SpringData JPA
JPA:ORM(Object Relational Mapping);它也是基于ORM思想
1、编写一个实体类Bean和数据表进行映射,并且配置好映射关系;
//使用jpa注解配置映射关系
@Entity//告诉jpa这是一个实体类
@Table(name = "tb_user")//@Table来指定和那个数据表对应;如果省略就是默认和user对应
public class User {
@Id//这是一个主键
@GeneratedValue(strategy = GenerationType.IDENTITY)//主键自增
private Integer id;
@Column(name = "last_name" ,length = 50)//这是和数据表对应的一个列
private String lastName;
@Column//省略默认列名就是属性名
private String email;
2、编写一个Dao接口来操作实体类对应的数据表(Repository)
//继承JpaRepository来完成对数据库的操作
public interface UserRepository extends JpaRepository<User,Integer> {
}
3、基本的配置jpaProperties
#更新或者创建数据表结构,当你没有这个表示它自己会帮你创建
spring.jpa.hibernate.ddl-auto=update
#控制台显示sql
spring.jpa.show-sql=true
4、直接写controller就可以了,它那个UserRepository里面有很多的数据访问操作方法。
@RestController
public class JpaUserController {
@Autowired
UserRepository userRepository;
@GetMapping("/user/{id}")
public User getUser(@PathVariable("id") Integer id){
return userRepository.findById(id).orElse(null);
}
}
七.自动配置原理
几个重要的事件回调机制
配置在META-INF/spring.factories
ApplicationContextInitializer
SpringApplicationRunListener
只需要放在ioc容器中
ApplicationRunner
CommandLineRunner
启动流程:
1.创建SpringApplication对象
对主应用的函数debug发现调用类中这个方法
public SpringApplication(Class<?>... primarySources) {
this((ResourceLoader)null, primarySources);
}
我们看这个方法
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.sources = new LinkedHashSet();
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.addConversionService = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = new HashSet();
this.isCustomEnvironment = false;
this.lazyInitialization = false;
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
//保存主配置类
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
//判断当前是否为一个web应用,点进去发现如果是它的值为SERVLET
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//从类路径下找到META‐INF/spring.factories配置的所有ApplicationContextInitializer;然后保存起来通过debug中两次stepinto然后stepover可以看到如下图七个initializer
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
//从类路径下找到META‐INF/spring.factories配置的所有ApplicationListener
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
//从多个配置类中找到有main方法的主配置类,可以有多个,因为可以给run方法传入数组
this.mainApplicationClass = this.deduceMainApplicationClass();
}
2.运行run方法
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
this.configureHeadlessProperty();
//获取SpringApplicationRunListeners;从类路径下META‐INF/spring.factories
SpringApplicationRunListeners listeners = this.getRunListeners(args);
//回调所有的获取SpringApplicationRunListener.starting()方法
listeners.starting();
Collection exceptionReporters;
try {
//封装命令行参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//准备环境
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
//创建环境完成后回调SpringApplicationRunListener.environmentPrepared();(在prepareEnvironment函数中)表示环境准备完成
this.configureIgnoreBeanInfo(environment);
Banner printedBanner = this.printBanner(environment);
//创建ApplicationContext;决定创建web(如果type参数是servlet就是web)的ioc还是普通的ioc
context = this.createApplicationContext();
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
//准备上下文环境;将environment保存到ioc中;而且applyInitializers();
//applyInitializers():回调之前保存的所有的ApplicationContextInitializer的initialize方法
//回调所有的SpringApplicationRunListener的contextPrepared();
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//prepareContext运行完成以后回调所有的SpringApplicationRunListener的contextLoaded();
//刷新容器;ioc容器初始化(如果是web应用还会创建嵌入式的Tomcat);Spring注解版
//扫描,创建,加载所有组件的地方;(配置类,组件,自动配置)
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
//所有的SpringApplicationRunListener回调started方法
listeners.started(context);
//从ioc容器中获取所有的ApplicationRunner和CommandLineRunner进行回调
//ApplicationRunner先回调,CommandLineRunner再回调
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
//所有的SpringApplicationRunListener回调running方法
listeners.running(context);
//整个SpringBoot应用启动完成以后返回启动的ioc容器;
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}