WenLiangBaoBlog

个人博客


  • 首页

  • 标签7

  • 归档62

  • 搜索

PC计算机常识

发表于 2021-12-06 | 更新于 2020-03-18

计算机是如何记录时间的

名词:RTC

RTC (real time clock)实时时钟

计算机断电期间,RTC芯片由锂电池供电;

RTC在内部或外部晶体振荡器驱动下以固定节拍增加内部计数器的值;

计算机开机后,CPU通过scl/sda串行线读取RTC的计数值;

RTC计数值表从格林尼治时间1970年1月1日零时起;

Docker

发表于 2021-12-06 | 更新于 2019-05-17

Docker

学习目标:

  • 掌握Docker基础知识,能够理解Docker镜像与容器的概念

  • 完成Docker安装与启动

  • 掌握Docker镜像与容器相关命令

  • 掌握Tomcat Nginx 等软件的常用应用的安装

  • 掌握docker迁移与备份相关命令

  • 能够运用Dockerfile编写创建容器的脚本

  • 能够搭建与使用docker私有仓库

    ​

1 Docker简介

1.1 什么是虚拟化

​ 在计算机中,虚拟化(英语:Virtualization)是一种资源管理技术,是将计算机的各种实体资源,如服务器、网络、内存及存储等,予以抽象、转换后呈现出来,打破实体结构间的不可切割的障碍,使用户可以比原本的组态更好的方式来应用这些资源。这些资源的新虚拟部份是不受现有资源的架设方式,地域或物理组态所限制。一般所指的虚拟化资源包括计算能力和资料存储。

​ 在实际的生产环境中,虚拟化技术主要用来解决高性能的物理硬件产能过剩和老的旧的硬件产能过低的重组重用,透明化底层物理硬件,从而最大化的利用物理硬件 对资源充分利用

​ 虚拟化技术种类很多,例如:软件虚拟化、硬件虚拟化、内存虚拟化、网络虚拟化(vip)、桌面虚拟化、服务虚拟化、虚拟机等等。

1.2 什么是Docker

​ Docker 是一个开源项目,诞生于 2013 年初,最初是 dotCloud 公司内部的一个业余项目。它基于 Google 公司推出的 Go 语言实现。 项目后来加入了 Linux 基金会,遵从了 Apache 2.0 协议,项目代码在 GitHub 上进行维护。

​

​ Docker 自开源后受到广泛的关注和讨论,以至于 dotCloud 公司后来都改名为 Docker Inc。Redhat 已经在其 RHEL6.5 中集中支持 Docker;Google 也在其 PaaS 产品中广泛应用。

​ Docker 项目的目标是实现轻量级的操作系统虚拟化解决方案。 Docker 的基础是 Linux 容器(LXC)等技术。

​ 在 LXC 的基础上 Docker 进行了进一步的封装,让用户不需要去关心容器的管理,使得操作更为简便。用户操作 Docker 的容器就像操作一个快速轻量级的虚拟机一样简单。

为什么选择Docker?

(1)上手快。

​ 用户只需要几分钟,就可以把自己的程序“Docker化”。Docker依赖于“写时复制”(copy-on-write)模型,使修改应用程序也非常迅速,可以说达到“随心所致,代码即改”的境界。

随后,就可以创建容器来运行应用程序了。大多数Docker容器只需要不到1秒中即可启动。由于去除了管理程序的开销,Docker容器拥有很高的性能,同时同一台宿主机中也可以运行更多的容器,使用户尽可能的充分利用系统资源。

(2)职责的逻辑分类

​ 使用Docker,开发人员只需要关心容器中运行的应用程序,而运维人员只需要关心如何管理容器。Docker设计的目的就是要加强开发人员写代码的开发环境与应用程序要部署的生产环境一致性。从而降低那种“开发时一切正常,肯定是运维的问题(测试环境都是正常的,上线后出了问题就归结为肯定是运维的问题)”

(3)快速高效的开发生命周期

​ Docker的目标之一就是缩短代码从开发、测试到部署、上线运行的周期,让你的应用程序具备可移植性,易于构建,并易于协作。(通俗一点说,Docker就像一个盒子,里面可以装很多物件,如果需要这些物件的可以直接将该大盒子拿走,而不需要从该盒子中一件件的取。)

(4)鼓励使用面向服务的架构

​ Docker还鼓励面向服务的体系结构和微服务架构。Docker推荐单个容器只运行一个应用程序或进程,这样就形成了一个分布式的应用程序模型,在这种模型下,应用程序或者服务都可以表示为一系列内部互联的容器,从而使分布式部署应用程序,扩展或调试应用程序都变得非常简单,同时也提高了程序的内省性。(当然,可以在一个容器中运行多个应用程序)

1.3 容器与虚拟机比较

​ 下面的图片比较了 Docker 和传统虚拟化方式的不同之处,可见容器是在操作系统层面上实现虚拟化,直接复用本地主机的操作系统,而传统方式则是在硬件层面实现。

与传统的虚拟机相比,Docker优势体现为启动速度快、占用体积小。

1.4 Docker 组件

1.4.1 Docker服务器与客户端

​ Docker是一个客户端-服务器(C/S)架构程序。Docker客户端只需要向Docker服务器或者守护进程发出请求,服务器或者守护进程将完成所有工作并返回结果。Docker提供了一个命令行工具Docker以及一整套RESTful API。你可以在同一台宿主机上运行Docker守护进程和客户端,也可以从本地的Docker客户端连接到运行在另一台宿主机上的远程Docker守护进程。

1.4.2 Docker镜像与容器

​ 镜像是构建Docker的基石。用户基于镜像来运行自己的容器。镜像也是Docker生命周期中的“构建”部分。镜像是基于联合文件系统的一种层式结构,由一系列指令一步一步构建出来。例如:

添加一个文件;

执行一个命令;

打开一个窗口。

也可以将镜像当作容器的“源代码”。镜像体积很小,非常“便携”,易于分享、存储和更新。

​ Docker可以帮助你构建和部署容器,你只需要把自己的应用程序或者服务打包放进容器即可。容器是基于镜像启动起来的,容器中可以运行一个或多个进程。我们可以认为,镜像是Docker生命周期中的构建或者打包阶段,而容器则是启动或者执行阶段。 容器基于镜像启动,一旦容器启动完成后,我们就可以登录到容器中安装自己需要的软件或者服务。

所以Docker容器就是:

​ 一个镜像格式;

​ 一些列标准操作;

​ 一个执行环境。

​ Docker借鉴了标准集装箱的概念。标准集装箱将货物运往世界各地,Docker将这个模型运用到自己的设计中,唯一不同的是:集装箱运输货物,而Docker运输软件。

和集装箱一样,Docker在执行上述操作时,并不关心容器中到底装了什么,它不管是web服务器,还是数据库,或者是应用程序服务器什么的。所有的容器都按照相同的方式将内容“装载”进去。

Docker也不关心你要把容器运到何方:我们可以在自己的笔记本中构建容器,上传到Registry,然后下载到一个物理的或者虚拟的服务器来测试,在把容器部署到具体的主机中。像标准集装箱一样,Docker容器方便替换,可以叠加,易于分发,并且尽量通用。

1.4.3 Registry(注册中心)

​ Docker用Registry来保存用户构建的镜像。Registry分为公共和私有两种。Docker公司运营公共的Registry叫做Docker Hub。用户可以在Docker Hub注册账号,分享并保存自己的镜像(说明:在Docker Hub下载镜像巨慢,可以自己构建私有的Registry)。

​ https://hub.docker.com/

2 Docker安装与启动

2.1 安装Docker

​ Docker官方建议在Ubuntu中安装,因为Docker是基于Ubuntu发布的,而且一般Docker出现的问题Ubuntu是最先更新或者打补丁的。在很多版本的CentOS中是不支持更新最新的一些补丁包的。

​ 由于我们学习的环境都使用的是CentOS,因此这里我们将Docker安装到CentOS上。注意:这里建议安装在CentOS7.x以上的版本,在CentOS6.x的版本中,安装前需要安装其他很多的环境而且Docker很多补丁不支持更新。

​ 请直接挂载课程配套的Centos7.x镜像

(1)yum 包更新到最新

1
sudo yum update

(2)安装需要的软件包, yum-util 提供yum-config-manager功能,另外两个是devicemapper驱动依赖的

1
sudo yum install -y yum-utils device-mapper-persistent-data lvm2

(3)设置yum源为阿里云

1
sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

(4)安装docker

1
sudo yum install docker-ce

(5)安装后查看docker版本

1
docker -v

2.2 设置ustc的镜像

ustc是老牌的linux镜像服务提供者了,还在遥远的ubuntu 5.04版本的时候就在用。ustc的docker镜像加速器速度很快。ustc docker mirror的优势之一就是不需要注册,是真正的公共服务。

https://lug.ustc.edu.cn/wiki/mirrors/help/docker

编辑该文件:

1
vi /etc/docker/daemon.json

在该文件中输入如下内容:

1
2
3
{
"registry-mirrors": ["https://docker.mirrors.ustc.edu.cn"]
}

2.3 Docker的启动与停止

systemctl命令是系统服务管理器指令

启动docker:

1
systemctl start docker

停止docker:

1
systemctl stop docker

重启docker:

1
systemctl restart docker

查看docker状态:

1
systemctl status docker

开机启动:

1
systemctl enable docker

查看docker概要信息

1
docker info

查看docker帮助文档

1
docker --help

3 常用命令

3.1 镜像相关命令

3.1.1 查看镜像

1
docker images

REPOSITORY:镜像名称

TAG:镜像标签

IMAGE ID:镜像ID

CREATED:镜像的创建日期(不是获取该镜像的日期)

SIZE:镜像大小

这些镜像都是存储在Docker宿主机的/var/lib/docker目录下

3.1.2 搜索镜像

如果你需要从网络中查找需要的镜像,可以通过以下命令搜索

1
docker search 镜像名称

NAME:仓库名称

DESCRIPTION:镜像描述

STARS:用户评价,反应一个镜像的受欢迎程度

OFFICIAL:是否官方

AUTOMATED:自动构建,表示该镜像由Docker Hub自动构建流程创建的

3.1.3 拉取镜像

拉取镜像就是从中央仓库中下载镜像到本地

1
docker pull 镜像名称

例如,我要下载centos7镜像

1
docker pull centos:7

3.1.4 删除镜像

按镜像ID删除镜像

1
docker rmi 镜像ID

删除所有镜像

1
docker rmi `docker images -q`

3.2 容器相关命令

3.2.1 查看容器

查看正在运行的容器

1
docker ps

查看所有容器

1
docker ps –a

查看最后一次运行的容器

1
docker ps –l

查看停止的容器

1
docker ps -f status=exited

3.2.2 创建与启动容器

创建容器常用的参数说明:

创建容器命令:docker run

-i:表示运行容器

-t:表示容器启动后会进入其命令行。加入这两个参数后,容器创建就能登录进去。即分配一个伪终端。

–name :为创建的容器命名。

-v:表示目录映射关系(前者是宿主机目录,后者是映射到宿主机上的目录),可以使用多个-v做多个目录或文件映射。注意:最好做目录映射,在宿主机上做修改,然后共享到容器上。

-d:在run后面加上-d参数,则会创建一个守护式容器在后台运行(这样创建容器后不会自动登录容器,如果只加-i -t两个参数,创建后就会自动进去容器)。

-p:表示端口映射,前者是宿主机端口,后者是容器内的映射端口。可以使用多个-p做多个端口映射

(1)交互式方式创建容器

1
docker run -it --name=容器名称 镜像名称:标签 /bin/bash

这时我们通过ps命令查看,发现可以看到启动的容器,状态为启动状态

退出当前容器

1
exit

(2)守护式方式创建容器:

1
docker run -di --name=容器名称 镜像名称:标签

登录守护式容器方式:

1
docker exec -it 容器名称 (或者容器ID)  /bin/bash

3.2.3 停止与启动容器

停止容器:

1
docker stop 容器名称(或者容器ID)

启动容器:

1
docker start 容器名称(或者容器ID)

3.2.4 文件拷贝

如果我们需要将文件拷贝到容器内可以使用cp命令

1
docker cp 需要拷贝的文件或目录 容器名称:容器目录

也可以将文件从容器内拷贝出来

1
docker cp 容器名称:容器目录 需要拷贝的文件或目录

3.2.5 目录挂载

我们可以在创建容器的时候,将宿主机的目录与容器内的目录进行映射,这样我们就可以通过修改宿主机某个目录的文件从而去影响容器。
创建容器 添加-v参数 后边为 宿主机目录:容器目录,例如:

1
docker run -di -v /usr/local/myhtml:/usr/local/myhtml --name=mycentos3 centos:7

如果你共享的是多级的目录,可能会出现权限不足的提示。

这是因为CentOS7中的安全模块selinux把权限禁掉了,我们需要添加参数 –privileged=true 来解决挂载的目录没有权限的问题

3.2.6 查看容器IP地址

我们可以通过以下命令查看容器运行的各种数据

1
docker inspect 容器名称(容器ID)

也可以直接执行下面的命令直接输出IP地址

1
docker inspect --format='{{.NetworkSettings.IPAddress}}' 容器名称(容器ID)

3.2.7 删除容器

删除指定的容器:

1
docker rm 容器名称(容器ID)

4 应用部署

4.1 MySQL部署

(1)拉取mysql镜像

1
docker pull centos/mysql-57-centos7

(2)创建容器

1
docker run -di --name=tensquare_mysql -p 33306:3306 -e MYSQL_ROOT_PASSWORD=123456 mysql(镜像名称或ID)

-p 代表端口映射,格式为 宿主机映射端口:容器运行端口

-e 代表添加环境变量 MYSQL_ROOT_PASSWORD 是root用户的登陆密码

(3)远程登录mysql

连接宿主机的IP ,指定端口为33306

4.2 tomcat部署

(1)拉取镜像

1
docker pull tomcat:7-jre7

(2)创建容器

创建容器 -p表示地址映射

1
2
docker run -di --name=mytomcat -p 9000:8080 
-v /usr/local/webapps:/usr/local/tomcat/webapps tomcat:7-jre7(镜像名称或ID)

4.3 Nginx部署

(1)拉取镜像

1
docker pull nginx

(2)创建Nginx容器

1
docker run -di --name=mynginx -p 80:80 nginx

4.4 Redis部署

(1)拉取镜像

1
docker pull redis

(2)创建容器

1
docker run -di --name=myredis -p 6379:6379 redis

5 迁移与备份

5.1 容器保存为镜像

我们可以通过以下命令将容器保存为镜像

1
docker commit mynginx mynginx_i

5.2 镜像备份

我们可以通过以下命令将镜像保存为tar 文件

1
docker  save -o mynginx.tar mynginx_i

5.3 镜像恢复与迁移

首先我们先删除掉mynginx_img镜像 然后执行此命令进行恢复

1
docker load -i mynginx.tar

-i 输入的文件

执行后再次查看镜像,可以看到镜像已经恢复

6 Dockerfile

6.1 什么是Dockerfile

Dockerfile是由一系列命令和参数构成的脚本,这些命令应用于基础镜像并最终创建一个新的镜像。

1、对于开发人员:可以为开发团队提供一个完全一致的开发环境;
2、对于测试人员:可以直接拿开发时所构建的镜像或者通过Dockerfile文件构建一个新的镜像开始工作了;
3、对于运维人员:在部署时,可以实现应用的无缝移植。

6.2 常用命令

命令 作用
FROM image_name:tag 定义了使用哪个基础镜像启动构建流程
MAINTAINER user_name 声明镜像的创建者
ENV key value 设置环境变量 (可以写多条)
RUN command 是Dockerfile的核心部分(可以写多条)
ADD source_dir/file dest_dir/file 将宿主机的文件复制到容器内,如果是一个压缩文件,将会在复制后自动解压
COPY source_dir/file dest_dir/file 和ADD相似,但是如果有压缩文件并不能解压
WORKDIR path_dir 设置工作目录

6.3 使用脚本创建镜像

步骤:

(1)创建目录

1
mkdir –p /usr/local/dockerjdk8

(2)下载jdk-8u171-linux-x64.tar.gz并上传到服务器(虚拟机)中的/usr/local/dockerjdk8目录

(3)创建文件Dockerfile vi Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#依赖镜像名称和ID
FROM centos:7
#指定镜像创建者信息
MAINTAINER ITCAST
#切换工作目录
WORKDIR /usr
RUN mkdir /usr/local/java
#ADD 是相对路径jar,把java添加到容器中
ADD jdk-8u171-linux-x64.tar.gz /usr/local/java/

#配置java环境变量
ENV JAVA_HOME /usr/local/java/jdk1.8.0_171
ENV JRE_HOME $JAVA_HOME/jre
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib:$CLASSPATH
ENV PATH $JAVA_HOME/bin:$PATH

(4)执行命令构建镜像

1
docker build -t='jdk1.8' .

注意后边的空格和点,不要省略

(5)查看镜像是否建立完成

1
docker images

7 Docker私有仓库

7.1 私有仓库搭建与配置

(1)拉取私有仓库镜像(此步省略)

1
docker pull registry

(2)启动私有仓库容器

1
docker run -di --name=registry -p 5000:5000 registry

(3)打开浏览器 输入地址http://192.168.184.141:5000/v2/_catalog看到`{"repositories":[]}` 表示私有仓库搭建成功并且内容为空

(4)修改daemon.json

1
vi /etc/docker/daemon.json

添加以下内容,保存退出。

1
{"insecure-registries":["192.168.184.141:5000"]}

此步用于让 docker信任私有仓库地址

(5)重启docker 服务

1
systemctl restart docker

7.2 镜像上传至私有仓库

(1)标记此镜像为私有仓库的镜像

1
docker tag jdk1.8 192.168.184.141:5000/jdk1.8

(2)再次启动私服容器

1
docker start registry

(3)上传标记的镜像

1
docker push 192.168.184.141:5000/jdk1.8

8.Docker如何查看容器日志

8.1 Docker logs

​ 使用命令docker logs 容器名即可查看

1
docker logs 容器名

​ -f 查看实时日志

1
docker logs -f 容器名

​ -t查看日志产生的日期

1
docker logs -t 容器名

​ –since : 此参数指定了输出日志开始日期,即只输出指定日期之后的日志。

1
docker logs -f -t --since="2017-05-31" --tail=10 容器名

​ –tail=10 : 查看最后的10条日志。

9.Docker的生命周期

10.进入Docker

1
docker exce -ti 容器名称 /bin/bash

vim篇

发表于 2021-12-06 | 更新于 2019-08-26

vim文本编辑器

vim常用操作

​ 1554102941025

vim是荷兰一位名叫 Bram Moolenaar 的工程师开发并分享的开源编辑器插件

1554103011275

1554103333245

1554103479882

1554103628208

1554104114402

vim区域复制,进入Ctrl+V 选择要复制的区域后 按y复制 p粘贴

1554104294187

1554104229157

1554104607078

vim使用技巧

1554105915669

上面连续行注释中 /表示隔离符 \表示转移符

个人用户下编辑添加 .vimrc 文件可以保存一些关于vim的配置和快捷键的使用。

树莓派-apt篇

发表于 2021-12-06 | 更新于 2019-11-22

添加apt source 资源路径

主要修改/etc/apt/sources.list 文件

添加阿里镜像

1
2
3
4
sudo vim /etc/apt/sources.list
#添加如下内容
deb http://mirrors.aliyun.com/raspbian/raspbian/ wheezy main non-free contrib
deb-src http://mirrors.aliyun.com/raspbian/raspbian/ wheezy main non-free contrib

python-入门篇

发表于 2021-12-06 | 更新于 2020-04-17

python的来源

​ python是著名的“Guido van Rossum”在1989年圣诞节期间开发的编程语言

现在,全世界差不多有600多种编程语言,但流行的编程语言也就那么20来种。如果你听说过TIOBE排行榜,你就能知道编程语言的大致流行程度。tiobe的官方数据链接如下:

https://www.tiobe.com/tiobe-index/

总的来说,这几种编程语言各有千秋。C语言是可以用来编写操作系统的贴近硬件的语言,所以,C语言适合开发那些追求运行速度、充分发挥硬件性能的程序。而Python是用来编写应用程序的高级编程语言。

当你用一种语言开始作真正的软件开发时,你除了编写代码外,还需要很多基本的已经写好的现成的东西,来帮助你加快开发进度。比如说,要编写一个电子邮件客户端,如果先从最底层开始编写网络协议相关的代码,那估计一年半载也开发不出来。高级编程语言通常都会提供一个比较完善的基础代码库,让你能直接调用,比如,针对电子邮件协议的SMTP库,针对桌面环境的GUI库,在这些已有的代码库的基础上开发,一个电子邮件客户端几天就能开发出来。

Python就为我们提供了非常完善的基础代码库,覆盖了网络、文件、GUI、数据库、文本等大量内容,被形象地称作“内置电池(batteries included)”。用Python开发,许多功能不必从零编写,直接使用现成的即可。

除了内置的库外,Python还有大量的第三方库,也就是别人开发的,供你直接使用的东西。当然,如果你开发的代码通过很好的封装,也可以作为第三方库给别人使用。

许多大型网站就是用Python开发的,例如YouTube、Instagram,还有国内的豆瓣。很多大公司,包括Google、Yahoo等,甚至NASA(美国航空航天局)都大量地使用Python。

python 的优点

life is short,you need Python –Bruce Eckel

龟叔给Python的定位是“优雅”、“明确”、“简单”,所以Python程序看上去总是简单易懂,初学者学Python,不但入门容易,而且将来深入下去,可以编写那些非常非常复杂的程序。

总的来说,Python的哲学就是简单优雅,尽量写容易看明白的代码,尽量写少的代码。如果一个资深程序员向你炫耀他写的晦涩难懂、动不动就几万行的代码,你可以尽情地嘲笑他。

那Python适合开发哪些类型的应用呢?

首选是网络应用,包括网站、后台服务等等;

其次是许多日常需要的小工具,包括系统管理员需要的脚本任务等等;

另外就是把其他语言开发的程序再包装起来,方便使用。

Python的缺点

任何编程语言都有缺点,Python也不例外。优点说过了,那Python有哪些缺点呢?

第一个缺点就是运行速度慢,和C程序相比非常慢,因为Python是解释型语言,你的代码在执行时会一行一行地翻译成CPU能理解的机器码,这个翻译过程非常耗时,所以很慢。而C程序是运行前直接编译成CPU能执行的机器码,所以非常快。

但是大量的应用程序不需要这么快的运行速度,因为用户根本感觉不出来。例如开发一个下载MP3的网络应用程序,C程序的运行时间需要0.001秒,而Python程序的运行时间需要0.1秒,慢了100倍,但由于网络更慢,需要等待1秒,你想,用户能感觉到1.001秒和1.1秒的区别吗?这就好比F1赛车和普通的出租车在北京三环路上行驶的道理一样,虽然F1赛车理论时速高达400公里,但由于三环路堵车的时速只有20公里,因此,作为乘客,你感觉的时速永远是20公里。

python交互模式

使用当前系统的命令模式,输入python3即可进入交互模式,输入exit()可退出python交互式模式。
交互式模式可以直接显示代码运行的结果。但是之前输入的代码退出交互式模式后不能存到硬盘中

命令行执行.py文件

命令行模式下,进入要执行的.py文件同目录下,输入python3 文件名即可运行

变量的命名规则(标识符)

变量名只能包含字母,数字和下划线,且不能以数字打头;

变量名不能包含空格;

不能将Python 关键字和函数名作为变量名使用;

慎用小写字母i和大写字母O,容易看成数字1和数字0;

Python 的命名是大小写敏感的,也就是说a和A对于解释器而言,是两个不同的名字;

占位符(place holder)

%d %.2f %.1f %s

%d 表示十进制整数占位符
%.2f 表示浮点数保留到2位小数点后
%.1f 表示浮点数保留到1位小数点后
%s 表示字符串

进制

二进制 (binary)

1
2
3
a = 0b111 #7
#二进制的形式表示7
a = bin(7)

十进制 (decimal)
十六进制 (hexadecimal)

1
2
3
a = 0xff #255
#十六进制的形式表示255
hex(255)

占位符表示进制

1
print("%x" %(255)) #使用16进制的形式表示255

注释

多行注释 “””
单行注释 #

列表 list

list 列表类似Java中的列表list都是类构成

下标的使用如: list[1]、list[-1]

列表的追加如:list.append()

列表的删除如:

del list[x] 直接删除指定索引处的元素
list.pop(x) 弹出指定索引处的元素并返回
list.remove(x) 删除首个指定元素

列表的排序:
list.sort()
列表拷贝
list.copy() 此拷贝不是深层拷贝,如果想要深层拷贝使用copy.deepcopy

数值列表

1
2
3
print("output of range(10):")
for i in range(5):
print(i)

列表的快速生成

1
2
3
matrix = [[0]*8]*10
cubes = [x**3 + 100 for x in range(1,11)]
variables = [x+y for x in 'abc' for y in '0123']

切片(slice)

1
2
3
numbers = [x for x in range(10)]

print("numbers[3:9]:",numbers[3:9])

切片会生成一个新的list返回,并不是直接操作原有的list
切片可以对str类型的数据使用

妙用切片

1
2
3
4
5
6
7
numbers = [x for x in range(10)]

numbersCopy = numbers[:]

numbersReversed = numbers[::-1]
print("numbers[:3:-2] is:",numbers[:3:2])
print("numbers[:3:-2] is:",numbers[:3:-2])

将切片的参数划分为[x:y:z]
当z为正数时 x默认为0,y默认为最后一个元素不包含、
当z为负数时 x默认为最后一个值,y默认为第一个值并且包含

数据类型

元组(tuple)

元组是只读的列表
当只有一个数时需要加,如下

1
print((1,))

byte字节

字节的作用:用来存储数据方便数据的交流,如字节流

字节的存储Python:

little 高位字节存高地址
big 高位字节存低地址

Intel ARM little endian

Motorola big endian

将数据以二进制字节的方式存储

example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import struct
import matplotlib.pyplot as plt

# write
lData = [11,18.234,10.342,3.2342,13]
with open('secondData.dat','wb') as d:
for i in lData:
d.write(struct.pack("<f",i))

# read
data = None
with open('secondData.dat','rb') as d:
data = d.read()

iSampleCount = len(data)//4 #解包的时候是按照4个字节的大小来进行的,所以要求4的倍数,因为Python使用/的结果是flot所以使用//。另外数据的总长度len(data)肯定是能被4整除。
print(len(data))
lCurveData = []
for i in range(iSampleCount):
fValue, = struct.unpack('<f',data[i*4:i*4+4])
lCurveData.append(fValue)

print(lCurveData)
plt.plot(lCurveData)
plt.show()

bytearray

1
2
3
4

buffer = bytearray(b'asdajhskfahs')
buffer[1] = ord('B')
print(buffer)

可以修改的byte 数组

序列 (sequence)

拥有的共同点:都可以使用索引、切片,并且拥有顺序

如 list tuple str byte bytearray

只读类型

int float str bytes boolean tuple

可修改类型

list bytearray dict

名字绑定 (name binding)

概念名词: 名字(name) 对象(object) 绑定(binding)

例子:q=2

解释就是 将 name q 绑定到 object 2 上

== is

1
2
3
4
5
6
7
8
a = 3
b = 3.0

print("a==b:",a==b)

print("a is b:",a is b)

print("a:",id(a),"b:",id(b))

序列解包(sequence unpack)

x,y,z = ‘x’,’y’,’z’
print(x,y,z)

x,y = y,x

print(x,y)

numbers = 1,2,3
print(type(numbers),numbers)

a,b,c = numbers
print(a,b,c)

d,e,f = 4,5,6
print(d,e,f)

g,h,i = ‘789’
print(g,h,i)

j,k,l = b’\x10\x20\x30’
print(j,k,l)

序列解包,首先解包的对象必须是序列,其次解包时两边数据必须保持一致,不然会报错如下
j,k= b’\x10\x20\x30’
number1,number2 = 1,2,3
print(j,k)

解包元素赋值序列
lstr = “Lenoardo di ser Piero Da Vinci”.split()
first,*middle,last = lstr
print(‘first:’,first,”- middle:”,middle,”- last:”,last)
print(‘first id is:’,id(first),”lstr[1] id is:”,id(lstr[1]))

链式赋值

x = y = 2

布尔型

非空即真,非零即真

for 可迭代对象 iterable object

1
2
3
4
s = ""
for x in "asdafasg":
s += x +"-"
print(s)

for else 当for 条件不成立时会触发else 的代码块并执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
for x in names:
if x.endswith("Bach"):
print(" I found a Bach:",x)
break
else:
print("No Bach been found.")

matrix = [[x+1+y for x in range(5)] for y in range(6)]

for r in range(6):
for c in range(5):
matrix[r][c] *= 2

for r in matrix:
print(r)

while loop

1
2
3
4
5
6
7
8
sum = i = 0

while i<=100:
sum += i
i += 1
print("sum of (1,2...,100) = ",sum)

names = ["Peter Anderson","Frank Bush","Tom Henry","Jack Lee","Dorothy Henry"]

带下标的遍历 enumerate

1
2
3
4
5
names = ['Tom','Andy','Alex','Dorothy']
print(list(enumerate(names)))

for idx,name in enumerate(names):
print(name +" is %s,id is %s" % (name,idx))

反向遍历

1
2
3
4
5
for x in reversed(names):
print(x)
print(names)
# reversed会生成一个新的序列并将原有的值反向存储
# list本身的reverse会将当前序列的值反向存储

del

1
2
3
4
5
6
x = "Anything"

y = x

del y
print(x)

exec 执行代码无返回

1
2
3
4
5
6
7
exec("print('This  string was print in exec function')")

scopeTemp = {}
scopeTemp['x'] = 30
scopeTemp['y'] = 20
exec("sum = x+y",scopeTemp)
print("sum = ",scopeTemp["sum"])

eval evaluation 评估 执行代码有返回

1
2
r = eval("3+2-5")
print(r)

字符串格式化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import os
sText = "Mary have {} lambs,they are {n1},{n2} and {}.".format(3,'cot',n2='happy',n1='nauty')
print(sText)

dora = {"name":"Dora","id":17,"age":32,"gender":"mate","title":"hello"}
sDoraHtml = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Employee {name}'s information</title>
</head>
<body>

<h1> Employee {name}'s Information</h1>
<table border="1" width="100%">
<tr>
<td>ID:</td>
<td>{id}</td>
<td>Name:</td>
<td>{name}</td>
<td>Age:</td>
<td>{age}</td>
</tr>
<tr>
<td>Gender:</td>
<td>{gender}</td>
<td>Title:</td>
<td>{title}</td>
</tr>
</table>
</body>
</html>
"""
t = open("dora.html",'w')
t.write(sDoraHtml.format_map(dora))
t.close()

os.system("google-chrome dora.html")

dict

key 必须是可哈希类型 hashable Type
可哈希类型 int float str tuple
不可哈希类型 dict list

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
doroth = dict(name="Dorothy",id = "10003",age = 26)
print(doroth)

d = dict.fromkeys(['id','name','age'],"unknown")
d1 = {}.fromkeys(['id','name','age'],"unknown")

print(d)
print(d1)



dora = {"name":"Dora","id":17,"age":32,"gender":"mate","title":"hello"}
# print(dora['salary'])
print(dora.get("salary",1000))

sTitle = dora.pop('title')
print(sTitle)
print(dora)


dora = {'id':10003,'age':32,'title':'Salas'}
dora2 = {'id':10004,'title':'CEO','gender':'female'}

dora.update(dora2)
print(dora)

for i in dora:
print(i,end=",")

for i in dora.keys():
print(i,end=",")

for key,value in dora.items():
print(key,"-",value)

函数与抽象

Python实现使用函数的案例

1
2
3
4
5
6
7
8
9
10
11
def costCompute(iStart,iEnd):
"""
计算客户电费,每度电10元
:param iStart: 起始度数
:param iEnd: 最终度数
:return: 电费
"""
iCousume = iEnd - iStart
return iCousume * 10

print(costCompute(100,230))

默认参数(default parameter)

1
2
3
4
5
6
7
def greeting(n,gender="male"):
n = n.title()
s = "Mr " if gender == 'male' else "Miss"
print("Hi,",s,n)

sName = "alan turning"
greeting(sName)

非只读类型的参数

1
2
3
4
5
6
7
8
9
10
def initPerson(person,id,name,age,gender,title):
assert type(person) == dict
person['id'] = id
person['name'] = name
person['age'] = age
person['gender'] = gender
person['title'] = title

dora = {}
initPerson(dora,'10003','dora chen',32,'female','sales')

关键字参数调用

1
2
3
4
5
6
7
8
9
10
def initPerson(person,id,name,age,gender,title):
assert type(person) == dict
person['id'] = id
person['name'] = name
person['age'] = age
person['gender'] = gender
person['title'] = title

initPerson(person=dora,id='10003',name='dora chen',age=32,gender='female',title='sales')
print(dora)

任意数量参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

def myPrint(title,*contents):
print(title,":")
for i in contents:
print("\t",i)

myPrint("Read-only data type:","int","float",'tuple','str','bytes')

def myprint(title,**contents):
print(title,":")
for k,v in contents.items():
print("\t",k+":",v)
myprint("dora",name="Dora chen",age=32)


def initPerson(person,id,name,age,gender,title):
assert type(person) == dict
person['id'] = id
person['name'] = name
person['age'] = age
person['gender'] = gender
person['title'] = title

dora = {}
tDora = ('10003','dora chen',32,'female','sales')
initPerson(dora,*tDora)
print(dora)

dDora = {'id':"10003",'name':'dora chen','age':32,'gender':'female','title':'sales'}
initPerson(dora,**dDora)
print(dora)

python 全局作用域实现方式

1
2
3
4
5
6
7
import pprint
x =1
y =2
scope = vars()
pprint.pprint(scope)
scope['x'] = 3
print(x)

递归 阶乘(factorial)总结不要在大规模的计算中使用递归

1
2
3
4
5
6
7
def factorial(n):
if n==1:
return 1
else:
return n*(factorial(n-1))

print("6!=",factorial(6))

面向对象

程序设计语言
1990年前
结构化编程 structured programming
1990年后
面向对象程序语言 object oriented programming

面向对象关键术语:

类型 type or class

对象 object or instance

属性 attribute or data member

方法 method or function

example:

1
2
3
4
5
6
7
8
9
10
11

from enum import Enum

class Person:
pass

class Gender(Enum):
mate = 1
female = 0

print(range.__doc__)

文件的读写

example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# write
f = open('datafile.txt','w')
f.write("This is a file which is writable in text mode.\n")
f.close()

# read
f = open('datafile.txt','r')
sLine1 = f.readline()
print(sLine1)
f.close()

# 安全使用流的简洁方式
with open('datafile.txt','r') as f:
sLine1 = f.readline()
print(sLine1)

标准输入 标准输出 错误流

example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import sys

iInput = open('standardInput.txt','w')
iInput.write('Alas\n')
iInput.write('27\n')
iInput.close()

sys.stdout = open('standardOut.txt','w')
sys.stdin = open('standardInput.txt','r')
sys.stderr = open('standardError.txt','w')

name = input('Water are you Name?')
age = input('How old are you')
print('This is %s she %s old' %(name,age))

raise Exception("Exception infomatoin")

管道重定向

1
cat title.txt | python wordCount.py

结构化文本文件 ini json

example:

1
2
3
4
5
6
7
8
9
10
import json

dora = "{'name':'Dora','no':'2018173','age':26,'married':false,'scores':[{'c++':76},{'Data structure':99.5}]}"
# write
iDora = open('dora.json','w')
json.dump(dora,iDora)

# read
dora = json.load(open('dora.json','r'))
print(dora)

异常、警告

异常勾子 :当系统发生异常时会执行一个指定的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import sys,traceback
from datetime import datetime


def userExceptHook(exceptType,value,traceBack):
fError = open('except_error.log', 'w')
traceList = traceback.format_tb(traceBack)
html = str(datetime.now())+'\n'
html += repr(exceptType)+'\n'
html += repr(value) + '\n'
for i in traceList:
html += i+"\n"

print(html,file=sys.stderr)
print(html,file=fError)
fError.close()

sys.excepthook = userExceptHook

sFirst = input("first number")
sSecond = input("second number")
try:
print(int(sFirst)/int(sSecond))
except Exception as e:
raise

类的序列化 迭代器和生成器函数

类的序列化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
class Fibonacci:

def __init__(self):
self.seq = [0,1,1]
self.maxKey = 1000

def computeTo(self,key):
for i in range(len(self.seq),key +1):
v = self.seq[i-1]+self.seq[i-2]
self.seq.append(v)

def __getitem__(self, item):
if not isinstance(item, int):
raise TypeError('type of item need int')
if not(item>=0 and item<self.maxKey):
raise IndexError("sequence max length is 1000,you's item > max length")
if item>len(self.seq):
self.computeTo(item)
return self.seq[item]

def __setitem__(self, key, value):
if not isinstance(key, int):
raise TypeError('type of key need int')
if not(key>=0 and key<self.maxKey):
raise IndexError("sequence max length is 1000,you's key > max length")
if key > len(self.seq):
self.computeTo(key)
self.seq[key] = value

def __len__(self):
return self.maxKey

f = Fibonacci()
print("f[20]:",f[20])
f[10] = "熊孩子"
for i in range(1,21):
print(f[i],end=",")

print("----------------------------------------------------------------------------------")
class Fibonacci(list):

def __init__(self,*arg):
super().__init__(*arg)
self.iCounter = 0

def __getitem__(self, item):
self.iCounter += 1
return super().__getitem__(item)

f = Fibonacci(range(100))
print('f[20]',f[20])
f[19] = "熊孩子"
print('f[19]',f[19])
print(f)

创建可迭代类

迭代器和序列的对比

迭代器,由于是当系统调用next方法时才会生成相应的数据所以在系统资源占用损耗节省方面优于序列,但是不能像序列那样可以随意指定下标取值,并且只能迭代一次.推荐需要依次获取一次数据时使用

序列,由于系统执行后会在系统内部一次性创建完成序列所以系统资源占用损耗节省方面劣于迭代器,但是它可以获取指定下标的值.推荐在重复或指定获取数据的情况下使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Fibonacci:

def __init__(self,arg):
self.a = 1
self.b = 1
self.index = 0
self.length = arg

def __iter__(self):
return self

def __next__(self):
result = None
if not isinstance(self.index,int):
raise TypeError
elif self.index >= self.length or self.index<0:
raise StopIteration
if self.index in(0,1):
result = 1
else:
result = self.a + self.b
self.a,self.b = self.b,result
self.index += 1

return result
f = Fibonacci(10)
print(list(f))
for i in f:
print(i,end=",")

生成器函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def fibonacciGenerator(len):

if not isinstance(len,int):
raise TypeError

a,b =1,1
for i in range(1,len+1):
if i in (1,2):
yield 1
else:
c = a + b
a,b = b,c
yield c
f = fibonacciGenerator(10)
print(list(f))
for i in f:
print(i,end=",")

f = [i**2 for i in range(10)] #列表推导
f2 = (i**2 for i in range(10))
print(type(f),f)
print(type(f2),list(f))

modelAttribute注解

发表于 2021-12-06 | 更新于 2019-04-29

@modelAttribute

moelAttribute 是springmvc提供的注解。

1.运用在参数上,会将客户端传递过来的参数按名称注入到指定对象中,并且会将这个对象自动加入ModelMap中,便于View层使用;

2.运用在方法上,会在每一个@RequestMapping标注的方法前执行,如果有返回值,则自动将该返回值加入到ModelMap中;

运用在方法上

实现案例,抽取request 和response、modelMap将其注入在baseController中。

beseController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import lombok.Data;
import org.springframework.web.bind.annotation.ModelAttribute;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Data
public class BaseController {

protected HttpServletRequest request;

protected HttpServletResponse response;

@ModelAttribute
public void initParams(HttpServletResponse response,HttpServletRequest request){
this.request = request;
this.response = response;
}
}

基础controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import com.healthengine.medpro.common.controller.BaseController;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/file")
@CrossOrigin
public class FileBaseController extends BaseController {


@GetMapping("/modelAttributeTest")
public String modelAttributeTest(){
request.getAuthType();
response.getStatus();
return "OK";
}

}

注意:其中的response是多例线程不安全的,如果想要时其线程安全可以使用ThreadLocal

spring4 读后笔记

发表于 2021-12-06 | 更新于 2020-02-03

spring

作者

spring 的作者叫做 Rob johnson

最初创建的目标

spring 创建的初期主要是为了解决企业级应用开发的复杂性

spring 的根本使命

简化JAVA开发

支撑spring 使命实现的4个关键策略

基于pojo的轻量级和最小侵入性编程
通过依赖注入和面相接口实现松耦合
基于切面和惯例进行声明式编程
通过切面和模板减少样板式代码

pojo示例代码

1
2
3
4
5
public class HelloWordBean{
public String sayHello(){
return "hello word!";
}
}

构造器模式

将一个事物运行的流程确定,用一个类表示,如上面的braveKnight类
将事物中多变的部分抽取出来,如执行的任务quest

这样做的好处

代码将松耦合
这样一来我们不用发愁骑士每次执行一个新的任务就需要重新创建一个骑士,而只需要新建一个需要执行的任务类并且实现quest接口即可

DI(dependency injection)

自动装配

github 上查看 https://github.com/slothjun/Spring4BookProject/tree/master/src/main/java/dome4

Java注解声明式装配

github 上查看 https://github.com/slothjun/Spring4BookProject/tree/master/src/main/java/dome5

xml装配bean

C命名空间中的标签无法装配集合。

代码:github 上查看 https://github.com/slothjun/Spring4BookProject/tree/master/src/main/java/dome6

导入和混合装配

代码:github 上查看 https://github.com/slothjun/Spring4BookProject/tree/master/src/main/java/dome7

注解混合xml或注解

@import 将别的配置引入当前配置中
@importResource 将xml配置引入当前配置中

xml混合注解或xml

标签可将别的xml配置导入当前配置中

标签可以将JavaConfig配置以bean的方式导入

profile条件化装配配置

使用profile 可以实现程序在不同配置下切换并运行

基于注解下的使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Configuration
@Profile("MagicExists")
public class MagicExistsConfig {

}
```
##### 基于xml下的使用
```xml
<?xml version="1.0" encoding="utf-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd" profile="MagicExists">
激活并使用条件配置下的配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = MagicExistsConfig.class)
public class Dome8Test {

@ActiveProfiles("MagicExists")
public static class MagicExistsTest{

@Autowired
private MagicBean magicBean;

@Test
public void test1(){
Assert.assertNotNull(magicBean);
}

}
}

关于条件化配置装配dome可到如下网址查看 https://github.com/slothjun/Spring4BookProject/tree/master/src/main/java/dome8

条件化bean

基于注解实现(@conditional)

1.在bean声明上加入@condition注解

1
2
3
4
5
@Bean
@Conditional(MagicExistsCondition.class)
public MagicBean magicBean(){
return new MagicBean();
}

2.编写MagicExistsCondition条件判定类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* @className MagicExistsCondition
* @Descirption 判断Magic存在
* @Author WengLiangBao
* @Date 2020/1/8 上午10:22
* @Vsersion 1.0
*/
public class MagicExistsCondition implements Condition {

public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
Environment env = conditionContext.getEnvironment();
return env.containsProperty("magic");
}
}

3.测试
@conditional注解会根据条件判定类中的matches方法返回的结果来装配当前被声明的bean
代码 https://github.com/slothjun/Spring4BookProject/tree/master/src/main/java/dome8

处理自动装配歧义

Primary

指定每次都首选的bean(个人不太推荐,因为它不够灵活)

1
2
3
4
@Component
@Primary //指定首选bean解决方案
public class Cake implements Dessert {
}

Qualifier

使用限定符指定要装配的bean

根据bean的ID装配
1
2
3
@Autowired
@Qualifier("cake")//限定符解决方案
private Dessert dessert;
根据自定义限定符注解装配

1.创建自定义限定符注解

1
2
3
4
5
6
7
8
9
10
11
12
/**
* @className Cold
* @Descirption
* @Author WengLiangBao
* @Date 2020/1/14 下午1:07
* @Vsersion 1.0
*/
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Cold {
}

2.在bean的声明处使用注解

1
2
3
4
@Component
@Cold
public class IceCream implements Dessert {
}

3.自动注入处使用注解

1
2
3
@Autowired
@Cold //自定义限定符注解
private Dessert dessert;

代码:github 上查看 https://github.com/slothjun/Spring4BookProject/tree/master/src/main/java/dome9

spring 是通过什么装载这些配置后的类的

spring通过应用上下文来实现应用装载配置
spring提供了多种上下文的实现,也就是说spring可以通过多种不同的方式来实现应用类的装载和配置

关于dependency injection 的更过信息可以查看《dependency injection》

总结 关于依赖注入和面相接口编程,本事阐述的核心思想是基于 装饰器模式+spring的多样化配置方式+spring 装载配置的核心接口(application context)

AOP面向切面

spring实现AOP的的方式

1.spring 本身提供的aop方案是基于代理实现,这种方案只能做到方法级别的把控无法对类的创建构造函数等操作
2.可以引入aspectJ方案替代,但是学习成本比较高。此方案可以做到细粒度的把控

注解实现

主要操控的内容有
1.5种通知(前置通知,后置通知,返回通知,异常通知,环绕通知)
其中环绕通知可以通过ProceedingJoinPoint 参数类实现前后返异四中通知。
2.带参通知
3.为接口添加新的接口
代码案例 :https://github.com/slothjun/Spring4BookProject/tree/master/src/main/java/dome12
https://github.com/slothjun/Spring4BookProject/tree/master/src/main/java/dome13
https://github.com/slothjun/Spring4BookProject/tree/master/src/main/java/dome14

xml实现

基于XML配置

sping 容器(基于IOC思想)

spring 容器负责创建,装配,配置bean。并管理bean的整个生命周期

spring 自带的容器实现

总体可归纳为2种类型

bean工厂(由org.springframework.beans.factory.BeanFactory定义)

提供基本的DI支持

应用上下文(由org.springframework.context.ApplicationContext定义)

基于BeanFactory构建,提供应用框架级别的服务
AnnotationConfigApplicationContext 从一个或多个基于java配置类中加载spring应用上下文
AnnotationConfigWebApplicationContext 从一个或多个基于java的配置类中加载SpringWeb应用上下文
ClassPathXmlApplicationContext 从类路径下的一个或多个xml配置文件中加载spring应用上下文
FileSystemXmlapplicationContext 从文件系统中的一个或多个xml配置文件中加载spring应用上下文
XmlWebApplicationContext 从Web应用下的一个或多个xml配置文件中加载应用上下文

bean的生命周期

bean 的作用域

spring默认bean的作用域是单例的。

scope的4大作用域

Singleton 单例:在整个应用中只创建一次bean
Prototype 原型:每次注入或根据spring上下文获取时,都会创建新的bean实例
Session 会话:在web应用中为每次会话创建一个bean的实例
Request 请求:在web应用中为每次请求创建一个bean的实例

使用Scope注解指定作用域

传统项目
1
2
3
4
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PrototypeCart implements ShoppingCart {
}
1
<bean id="prototypeCart" class="dome10.PrototypeCart" scope="Prototype"/>
web项目

在为web项目指定bean的作用域时需要注意bean构建时依赖传递的问题
问题举例:
如有一个购物车bean需的作用域需要被指定为Session级别。当spring的上下文创建时发现购物车bean是的作用域是购物车级别的所以不能被创建。
如何解决此问题,答案是通过代理的手段解决

对于bean是以接口的方式注入 (基于java代理)

1
2
3
4
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE,proxyMode = ScopedProxyMode.INTERFACES)
public class PrototypeCart implements ShoppingCart {
}

1
2
3
<bean id="prototypeCart" class="dome10.PrototypeCart" scope="Session">
<aop:scoped-proxy proxy-target-class="false"/>
</bean>

对于bean是以类的方式注入(基于CGLib代理)

1
2
3
4
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE,proxyMode = ScopedProxyMode.TARGET_CLASS)
public class PrototypeCart implements ShoppingCart {
}

1
2
3
<bean id="prototypeCart" class="dome10.PrototypeCart" scope="Session">
<aop:scoped-proxy/>
</bean>

springMVC

​ 运行流程图

dispatcher Servlet 前端控制器(front Controller)核心

快速尝鲜:

基于spring 搭建spring mvc

1.将DispatcherServlet 注册到web容器中

在 Servlet 3. 0 环境 中, 容器 会在 类 路径 中 查找 实现 javax. servlet. ServletContainerInitializer 接口 的 类, 如果 能 发现 的 话, 就会 用 它来 配置 Servlet 容器。

​ Spring 提供 了 这个 接口 的 实现, 名为 SpringServletContainerInitializer。

1
2
@HandlesTypes({WebApplicationInitializer.class})
public class SpringServletContainerInitializer implements ServletContainerInitializer

​ 其中的HandlesTypes 是什么?

1.Tomcat的主机容器在添加子容器时,会通过解析.xml并通过类加载器加载@HandlesTypes注解的类

2.读取@HandlesTypes注解值值。并放入ServletContainerInitializers对应的Set集合中

3.在ApplicationContext内部启动时会通知ServletContainerInitializers的onStart方法()。这个onStart方法的第一个参数就是@HandlesTypes注解的值值指定的类集合

4.在Spring应用中,对ServletContainerInitializers的实现就是SpringServletContainerInitializer,注解指定的类就是WebApplicationInitializer。

通过上面解读过程我们知道,如果要想使用基于servlet3.0支持注解申明servlet。使用spring框架时的关注点就在WebApplicationInitializer接口中。

如果个人有能力或者有兴趣的化可以自己通过实现WebApplicationInitializer接口来注册servlet。但是一般我们只需要使用spring进行过一些封装的AbstractAnnotationConfigDispatcherServletInitializer抽象类即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package org.springframework.web.servlet.support;

import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;

public abstract class AbstractAnnotationConfigDispatcherServletInitializer extends AbstractDispatcherServletInitializer {
public AbstractAnnotationConfigDispatcherServletInitializer() {
}

@Nullable
protected WebApplicationContext createRootApplicationContext() {
Class<?>[] configClasses = this.getRootConfigClasses();
if (!ObjectUtils.isEmpty(configClasses)) {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(configClasses);
return context;
} else {
return null;
}
}

protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
Class<?>[] configClasses = this.getServletConfigClasses();
if (!ObjectUtils.isEmpty(configClasses)) {
context.register(configClasses);
}

return context;
}

@Nullable
protected abstract Class<?>[] getRootConfigClasses();

@Nullable
protected abstract Class<?>[] getServletConfigClasses();
}

使用 AbstractAnnotationConfigDispatcherServletInitializer注册容器并初始化上下文

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.healthengine.medpro.common.config;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class ServletConfigAchieve extends AbstractAnnotationConfigDispatcherServletInitializer {

//用来配置ContextLoaderListener 的上下文
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[0];
}

//用来配置springMvc 中Dispatcher Servlet 上下文
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[0];
}

//配置容器请求的映射路径
@Override
protected String[] getServletMappings() {
return new String[0];
}
}

配置Filter

方式1:基于Disparther Servlet 配置

​

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.healthengine.medpro.common.config;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

import javax.servlet.Filter;
import javax.servlet.MultipartConfigElement;
import javax.servlet.ServletContext;
import javax.servlet.ServletRegistration;

public class ServletConfigAchieve extends AbstractAnnotationConfigDispatcherServletInitializer {

//用来配置ContextLoaderListener 的上下文
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[0];
}

//用来配置springMvc 中Dispatcher Servlet 上下文
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[0];
}

//配置容器请求的映射路径
@Override
protected String[] getServletMappings() {
return new String[0];
}

//设置filter
@Override
protected Filter[] getServletFilters() {
return //自己定义的filter 类(数组)
}

}
方式2:自行实现

处理Multipart格式的内容信息

​ 1.配置类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package com.healthengine.medpro.common.config;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

import javax.servlet.Filter;
import javax.servlet.MultipartConfigElement;
import javax.servlet.ServletContext;
import javax.servlet.ServletRegistration;

public class ServletConfigAchieve extends AbstractAnnotationConfigDispatcherServletInitializer {

//用来配置ContextLoaderListener 的上下文
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[0];
}

//用来配置springMvc 中Dispatcher Servlet 上下文
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[0];
}

//配置容器请求的映射路径
@Override
protected String[] getServletMappings() {
return new String[0];
}

//自定义注册
@Override
protected void customizeRegistration(ServletRegistration.Dynamic registration) {
//Multipart 配置设置
registration.setMultipartConfig(new MultipartConfigElement("temp/medpro/"));
}

}

2.配置MultipartResolver Bean

MultipartResolver 是springframework.web提供用于处理Multipart的接口规范

​ 1.CommonsMultipartResolver: 使用 Jakarta Commons FileUpload 解析 multipart 请求;

​ 2.StandardServletMultipartResolver: 依赖于 Servlet 3. 0 对 multipart 请求 的 支持( 始于 Spring 3. 1)。

1
2
3
@Bean public MultipartResolver multipartResolver() throws IOException {
return new StandardServletMultipartResolver();
}

3.测试代码

1
2
3
4
5
6
7
8
@GetMapping
public void multipartHandle(MultipartFile multipartFile) throws IOException {
String originalFilename = multipartFile.getOriginalFilename();
byte[] bytes = multipartFile.getBytes();
String contentType = multipartFile.getContentType();
InputStream inputStream = multipartFile.getInputStream();
String name = multipartFile.getName();
}

Linux-系统结构

发表于 2021-12-06 | 更新于 2019-08-27

简单的说Linux系统可分为 Linux内核 + 应用程序

对于Linux系统的细致划分,可以分为

Linux 内核

系统调用

库函数

shell

应用程序

内核模式与系统调用

内核模式

计算机的硬件资源又系统内核来操控,应用程序要想调用系统硬件资源需要通过访问或调用内核接口来实现指定的功能。
这时系统的内核就是操控硬件的基础操作。我们把内核程序的活动称为内核模式,应用程序的活动称为用户模式。

系统调用

应用程序通过内核接口调用内核程序的过程称为系统调用。

Linux中的系统调用接口大约有200多种,我们可以通过shell查看Linux下的所有系统调用

1
man 2 syscalls

man 中的参数2表示系统调用,不理解的可通过 man man 查看系统帮助文档.

系统调用常见的有

read 文件读取

write 文件写入

fork 复制当前进程

wait 等待某个进程完成

chdir 改变工作目录

通过系统调用,Linux用户可以体验到内核封装的好处。使得我们不必关系硬件驱动使用的一些底层细节。我更专注与程序的功能和要实现的目的。

库函数

库函数是什么,个人认为库函数就是系统调用的更上一层的封装。将系统调用的一些指令结合起来写成函数的形式,以方便调用。就像是C语言中的C标准库一样,有了他们程序开发起来更便捷。

除了C标准库,Linux还有一些其他的库比如 POSIX等

shell

shell 是一个交互程序,他是为了普通用户通过shell在不需要自己编程的情况下使用系统的一些资源。

用户程序

用户程序是基于编程语言开发后的程序,它也是通过其语言调用系统内核功能来使用系统的一些硬件资源。和库函数不同的是1它是面向用户的,2它针对的功能更专一,使用更方便。

早期由于Linux程序中大部分用户程序是用C编写的,然而Linux系统内核也是由C编写的。所以我觉得我们有必要了解下C中的函数调用与进程空间

springBoot-MultipartAutoConfiguration

发表于 2021-12-06 | 更新于 2019-04-29

前提:

​ 今天本人想实现一个文件上传的功能。

​ 文件上传的难点在以什么样的传输格式上传文件。

​ 一般 表单 提交 所 形成 的 请求 结果是 很 简单 的, 就是 以“&” 符 分割 的 多个 name- value 对。 例如

1
firstName= Charles& lastName= Xavier& email= professorx% 40xmen. org &username= professorx& password= letmein01

尽管 这种 编码 形式 很 简单, 并且 对于 典型的 基于 文本 的 表单 提交 也 足够 满足要求, 但是 对于 传送 二进制 数据, 如上 传 图片等文件, 就 显得 力不从心 了。

​ 那么对应二进制数据的上传依托Http请求要怎么去实现呢?

​ multipart 格式 的 数据 会 将 一个 表单 拆分 为多 个 部分( part), 每个 部分 对应 一个 输入 域。 在 一般 的 表单 输入 域 中, 它 所 对应 的 部分 中会 放置 文本 型 数据, 但是 如果 上传 文件 的 话, 它 所 对应 的 部分 可以 是 二进制, 下面 展现 了 multipart 的 请求 体:

1
2
3
4
5
6
------WebKitFormBoundaryqgkaBn8IHJCuNmiW Content- Disposition: form- data; name=" firstName" Charles 
------WebKitFormBoundaryqgkaBn8IHJCuNmiW Content- Disposition: form- data; name=" lastName" Xavier ------WebKitFormBoundaryqgkaBn8IHJCuNmiW Content- Disposition: form- data; name=" email" charles@ xmen. com
------WebKitFormBoundaryqgkaBn8IHJCuNmiW Content- Disposition: form- data; name=" username" professorx
------WebKitFormBoundaryqgkaBn8IHJCuNmiW Content- Disposition: form- data; name=" password" letmein01
------WebKitFormBoundaryqgkaBn8IHJCuNmiW Content- Disposition: form- data; name=" profilePicture"; filename=" me. jpg" Content- Type: image/ jpeg [[ Binary image data goes here ]]
------WebKitFormBoundaryqgkaBn8IHJCuNmiW--

在 这个 multipart 的 请求 中, 我们 可以 看到 profilePicture 部分 与其 他 部分 明显 不同。 除了 其他 内容 以外, 它 还有 自己的 Content- Type 头, 表明 它是 一个 JPEG 图片。 尽管 不一定 那么 明显, 但 profilePicture 部分 的 请求 体 是 二进制 数据, 而 不是 简单 的 文本。

尽管 multipart 请求 看起来 很复杂, 但通过 Spring MVC 处理 它们 却 很容易。 在 编写 控制器 方法 处理 文件 上传 之前, 我们 必须 要 配置 一个 multipart 解析 器, 通过 它来 告诉 DispatcherServlet 该 如何 读取 multipart 请求。

​ spring mvc 对multipart 解析器定义了 multipart Resolver 接口规范。 并提供了CommonsMultipartResolver: 使用 Jakarta Commons FileUpload 解析 multipart 请求的实现和StandardServletMultipartResolver: 依赖于 Servlet 3. 0 对 multipart 请求 的 支持( 始于 Spring 3. 1)。

使用springboot 如何操作multipart Resolver 接下来请看MultipartAutoConfiguration装配类都做了什么

MultipartAutoConfiguration

系统安全案例及设计理念

发表于 2021-12-06 | 更新于 2019-04-29

RBAC模型

什么是RBAC

RBAC(全称:Role-Based Access Control)基于角色的权限访问控制,作为传统访问控制(自主访问,强制访
问)的有前景的代替受到广泛的关注。在RBAC中,权限与角色相关联,用户通过成为适当角色的成员而得到这些
角色的权限。这就极大地简化了权限的管理。在一个组织中,角色是为了完成各种工作而创造,用户则依据它的责
任和资格来被指派相应的角色,用户可以很容易地从一个角色被指派到另一个角色。角色可依新的需求和系统的合
并而赋予新的权限,而权限也可根据需要而从某角色中回收。角色与角色的关系可以建立起来以囊括更广泛的客观
情况。
访问控制是针对越权使用资源的防御措施,目的是为了限制访问主体(如用户等) 对访问客体(如数据库资源等)
的访问权限。企业环境中的访问控制策略大部分都采用基于角色的访问控制(RBAC)模型,是目前公认的解决大
型企业的统一资源访问控制的有效方法

基于RBAC实现的设计思路

基于角色的访问控制基本原理是在用户和访问权限之间加入角色这一层,实现用户和权限的分离,用户只有通过激
活角色才能获得访问权限。通过角色对权限分组,大大简化了用户权限分配表,间接地实现了对用户的分组,提高
了权限的分配效率。且加入角色层后,访问控制机制更接近真实世界中的职业分配,便于权限管理。

表结构模型图

需求1

前端

​ 动态加载用户的功能模块

后端

​ 根据用户权限使用API

基于RBAC dome 案例分析并实现

后台无状态安全校验实现

​ 底层数据

​ 问题:数据如何存储便于后期的使用与管理。

​ 方案:使用RBAC管理模型,根据用户、角色、权限三种概念。来实现数据的访问、使用等相关权限的合理分配。

​ 数据传输

​ 问题:数据传输依托于互联网,如何保证其安全。

​ 方案:认证+校验

​ 认证 :依托web认证机制中的方案可实现

​ 校验 :

​ 实现逻辑 在请求到达controller 时拦截并校验。

​ 实现方式有:filter intercept aop 他们三者的关系如下图

​

​ 本次案例使用的的过滤方式为,filter 过滤一些不存在token的请求,由intercept拦截校验接下来的数据信息。

后台有状态安全校验实现

1…4567
WenLiangBao

WenLiangBao

个人技术总结。(*^_^*)温故而知新,可以为师矣

62 日志
7 标签
Creative Commons
© 2024 WenLiangBao
由 Hexo 强力驱动 v3.8.0
|
主题 – NexT.Mist v7.0.0