首页 文章 精选 留言 我的

精选列表

搜索[学习],共10000篇文章
优秀的个人博客,低调大师

day12_JavaWeb设计模式与案例学习笔记

一、JavaWeb开发模式 C/S:客户端 / 服务器 (胖客户端)比如:LOL、CS、魔兽世界。.exe安装文件。 B/S:浏览器 / 服务器(瘦客户端)比如:页游。网页版软件。 JavaBean: 其实就是一个普通类(实体bean:用作封装数据),包含三样标准:一个无参构造方法、私有属性、公共的getter和setter方法。 还有一个业务bean:用于封装业务,比如:登录、注册等。 1、Model1模式(了解即可,早已过时) JSP + JavaBean 在网页.jsp代码中通过jsp:useBean、jsp:setProperty、jsp:getProperty,相当于new出来一个类,即jsp代码中嵌入实体类(java代码),不合适实际开发! 因为这样的话,java代码就太多了,喧宾夺主了!但是,这种方式适合教学用。 一般new一个类(实体bean类),是纯的java代码,就放在专门放java代码的项目的src里面比较好,比如:XxxServlet.java中。 2、Model2模式(模拟MVC) JSP + Servlet + JavaBean 把JavaBean放到3个域对象中。 MVC:开发模式 M:Model模型 相当于 JavaBean(即四种作用域) V:view视图 相当于 JSP C:Controller控制器 相当于 Servlet 分层思想:强内聚、弱耦合。 二、案例:用户的注册和登陆 三、开发步骤 开发时注意事项: 约定优于编码。列名、字段名、表单中的属性名要一致。 1、创建数据库及表 2、开发web应用 a、搭建开发环境:添加jar包,按顺序创建包结构。 b、创建实体类(javaBean),用于封装数据。 c、dao层(数据访问层) 接口 实现类 先添加工具类:DBUtils和数据库配置文件。 以后开发中,dao中的代码,有异常的话就全部往外抛,全部抛到service里面的实现类来处理。 因为service属于业务逻辑层,service里面有日志记录,会把异常写入到日志。 所以业务层中的异常就不要向外抛了,在service里面的实现类来处理。 d、service层(业务层) 接口 实现类 e、表示层(JSP、Servlet) index.jsp log.jsp reg.jsp logServlet.jsp regServlet.jsp logoutServlet.jsp 我的GitHub地址: https://github.com/heizemingjun 我的博客园地址: http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址: http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军 【转载文章务必保留出处和署名,谢谢!】

优秀的个人博客,低调大师

Zabbix proxy分布式监控(学习笔记三十一)

转载:http://www.cnblogs.com/yaoyaojcy/p/8182067.html 自学Zabbix13.1分布式监控proxy介绍 zabbix2.4版本之前,zabbix提供了两种解决方案,分别为:proxy和nodes proxy代替zabbix server在本地检索数据,然后提交给zabbix server Nodes则就是一个完整的zabbix Server(不做介绍) zabbix自从2.4版本之后,分布式监控就移除了 nodes 模式 1. 概述 zabbixproxy可以代替zabbix server检索客户端的数据,然后把数据汇报给zabbix server,并且在一定程度上分担了zabbix server的压力.zabbix proxy可以非常简便的实现了集中式、分布式监控。 zabbix proxy使用场景: 监控远程区域设备 监控本地网络不稳定区域 当zabbix监控上千设备时,使用它来减轻server的压力 简化zabbix的维护 2.zabbix proxy架构图 zabbix proxy仅仅需要一条tcp连接到zabbix server,所以防火墙上仅仅需要加上一条规则即可。 zabbix proxy数据库必须和server分开,否则数据会被破坏,毕竟这两个数据库的表大部分都相同。总之记住,数据库分开即可。 proxy收集到数据之后,首先将数据缓存在本地,然后在一定得时间之后传递给zabbix server。 这个时间由proxy配置文件中参数ProxyLocalBuffer and ProxyOfflineBuffer决定。 3.zabbix proxy功能 zabbix proxy是一个数据收集器,它不计算触发器、不处理事件、不发送报警,如下是proxy的功能. ItemsFunction Supported by proxy Zabbix agent checksYes Zabbix agent checks (active)Yes Simple checksYes Trapper itemsYes SNMP checksYes SNMP trapsYes IPMI checksYes JMX checksYes Log file monitoringYes Internal checksYes SSH checksYes Telnet checksYes External checksYes Built-in web monitoringYes Network discoveryYes Low-level discoveryYes Calculating triggersNo Processing eventsNo Sending alertsNo Remote commandsNo 备注: 使用agent active模式,一定要记住在agent的配置文件参数ServerActive加上proxy的IP地址 分为两部分: 安装proxy 配置proxy 1. 安装proxy 官方文档使用的是源码安装,因为方便我们使用yum安装,因为我们只有2台,所以就用agent当做Proxy 1.1 安装 [root@linux-node2 ~]# yum install -y zabbix-proxy zabbix-proxy-mysql mariadb-server 我们需要启动MySQL [root@linux-node2 ~]# systemctl start mariadb.service 1.2 创建数据库 1mysql2create database zabbix_proxy character set utf8;3grant all on zabbix_proxy.* to zabbix_proxy@localhost identified by'zabbix_proxy'; 1.3 导入数据 [root@linux-node2 ~]# cd /usr/share/doc/zabbix-proxy-mysql-3.0.8/[root@linux-node2 zabbix-proxy-mysql-3.0.8]# zcat schema.sql.gz | mysql -uzabbix_proxy -p zabbix_proxyEnter password: #密码是:zabbix_proxy 是我们数据库授权的密码 1.4 检查数据库 1mysql2show databases;3use zabbix_proxy;4show tables;5#查看是否含有数据 1.5 修改proxy的配置文件 1[root@linux-node2 zabbix-proxy-mysql-3.0.8]# vim /etc/zabbix/zabbix_proxy.conf 2Server=172.18.50.33 3Hostname=Zabbix proxy 4DBName=zabbix_proxy 5#数据库名称 6DBUser=zabbix_proxy 7#用户名 8DBPassword=zabbix_proxy 9#用户密码10配置文件中没有配置的内容如下:(有需要可以配置)11# ProxyLocalBuffer=012#数据保留的时间(小时为单位)13# ProxyOfflineBuffer=114#连不上Server,数据要保留多久(小时为单位,默认1小时)15# DataSenderFrequency=116#数据的发送时间间隔(默认是1秒)17# StartPollers=518#启动的线程数19# StartIPMIPollers=020#启动IPMI的线程数 过滤修改过的配置如下: 1[root@linux-node2 zabbix-proxy-mysql-3.0.8]# grep '^[a-Z]' /etc/zabbix/zabbix_proxy.conf 2Server=172.18.50.33 3Hostname=Zabbix proxy 4LogFile=/var/log/zabbix/zabbix_proxy.log 5LogFileSize=0 6PidFile=/var/run/zabbix/zabbix_proxy.pid 7DBName=zabbix_proxy 8DBUser=zabbix_proxy 9DBPassword=zabbix_proxy10SNMPTrapperFile=/var/log/snmptrap/snmptrap.log11Timeout=412ExternalScripts=/usr/lib/zabbix/externalscripts13LogSlowQueries=3000 1.6 启动, 查看proxy进程 [root@linux-node2 ~]# systemctl start zabbix-proxy 1[root@linux-node2 ~]# netstat -lntup 2Active Internet connections (only servers) 3Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name 4tcp 0 0 0.0.0.0:3306 0.0.0.0:* LISTEN 15685/mysqld 5tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1073/sshd 6tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 2498/master 7tcp 0 0 0.0.0.0:10051 0.0.0.0:* LISTEN 15924/zabbix_proxy 8tcp6 0 0 :::44589 :::* LISTEN 9052/java 9tcp6 0 0 :::8080 :::* LISTEN 9052/java 10tcp6 0 0 :::22 :::* LISTEN 1073/sshd 11tcp6 0 0 :::8888 :::* LISTEN 9052/java 12tcp6 0 0 ::1:25 :::* LISTEN 2498/master 13tcp6 0 0 :::39743 :::* LISTEN 9052/java 14tcp6 0 0 :::10051 :::* LISTEN 15924/zabbix_proxy 15tcp6 0 0 127.0.0.1:8005 :::* LISTEN 9052/java 16tcp6 0 0 :::8009 :::* LISTEN 9052/java Zabbix-proxy 监控10051端口,因为是代理就必须跟Server的端口相同,对于Agent Proxy就是Server 2. proxy配置 2.1 administrator --- proxies---creat proxy 参数描述 Proxy nameproxy名称,必须和proxy配置文件中的hostname一致 Proxy mode选择proxy模式 Activeproxy主动连接到zabbix server并且请求配置文件数据 PassiveZabbix server连接到proxy Hosts哪些主机需要被proxy监控 2.2 encryption选项 连接到代理服务器如何连接到被动代理:没有加密(默认),使用PSK(预共享密钥)或证书。 来自代理的连接选择从活动代理允许什么类型的连接 可以同时选择几种连接类型(用于测试和切换到其他连接类型) 默认没有加密 发行人允许颁发证书。证书首先由CA(证书颁发机构)验证。如果它是有效的,在CA签署,然后发行领域可以用来进一步限制允许约这个字段是可选的,如果你打算使用Zabbix安装使用证书从多个CAS。 科目允许证书。证书首先由CA.验证,如果它是有效的,由CA签名,则主题字段可用于只允许一个主题字符串值。如果此字段为空,则接受配置的CA签署的任何有效证书。 身份认证预共享密钥标识字符串。 预共享密钥最大长度:512进制数(256字节的PSK) 如果Zabbix uses GnuTLS或OpenSSL库,64进制数(32字节的PSK) 如果Zabbix使用MBED TLS(polarssl)。例如:1f87b595725ac58dd977beef14b97461a7c1045b9a1c963065002c5473194952 2.3 配置主机HOST的时候,如果需要被proxy代理,那么都选择对应的proxy名称 。

优秀的个人博客,低调大师

Zabbix 二次开发API(学习笔记三十)

Zabbix API我们可以做很多,自己开发web界面、开发手机端zabbix、获取zabbix指定数据、创建zabbix监控项等等。 1. zabbix API开发库 zabbix API请求和响应都是json,并且还提供了各种语法的lib库,http://zabbix.org/wiki/Docs/api/libraries,包含php、c#、Python、Perl、go等等语言,简单看了下phpzabbixapi,使用非常方便。 2.请求zabbix API post json数据到api接口地址,例如你得zabbix地址是http://company.com/zabbix,那么你得接口地址是:http://company.com/zabbix/api_jsonrpc.php,必须包含content-type头,值为application/json-rpc,application/jsonorapplication/jsonrequest之一。 POSThttp://company.com/zabbix/api_jsonrpc.php HTTP/1.1 Content-Type:application/json-rpc {"jsonrpc":"2.0","method":"apiinfo.version","id":1,"auth":null,"params":{}} 3. zabbix API登陆 获取auth token(登陆) 在操作zabbix之前,我们必须先登陆zabbix,得到token,以后的操作带着这个token即可,要不然肯定没权限。 请求的json如下: { "jsonrpc":"2.0", "method":"user.login", "params":{ "user":"Admin", "password":"zabbix" }, "id":1, "auth":null } 属性说明 jsonrps - JSON-RPC版本,基本上用2.0就行了; method - 调用的API方法,方法列表请上官网; params - 需要传递的参数,这边是user和password; id - 请求标志; auth - 用户token,这边使用null,因为还没通过验证 验证成功,会返回如下json数据 { "jsonrpc":"2.0", "result":"0424bd59b807674191e7d77572075f33", "id":1 } result便是我们要德token数据,id对应请求的id。 4. zabbix api检索主机 通过验证之后,我们带着token使用host.get获取主机列表,请求的json如下: { "jsonrpc":"2.0", "method":"host.get", "params":{ "output":[ "hostid", "host" ], "selectInterfaces":[ "interfaceid", "ip" ] }, "id":2, "auth":"0424bd59b807674191e7d77572075f33" } 获取到如下数据: { "jsonrpc":"2.0", "result":[ { "hostid":"10084", "host":"Zabbix server", "interfaces":[ { "interfaceid":"1", "ip":"127.0.0.1" } ] } ], "id":2 } 请使用你的程序处理一下即可。

优秀的个人博客,低调大师

java学习笔记--简单掌握正则表达式

正则表达式: 主要用于操作字符串的规则 用于检索替换那些符合规定的文本 预定义字符 . 任何字符 . 就单纯代表. \d是 数字 \D 非数字 \s 空白字符 包括\t \n \r \S 非空白字符 \w 单词字符 比如 a—z A-Z _还有0到9 \W 非单词字符 如果预定义字符没有加上数量词 那就只能匹配一个字符 数量词: ? 一次或者一次也没有 * 0次或者多次 + 1次或者多次 {n}恰好n次 {n,} 至少n次 {n,m} 至少n次,但是不超过m次 范围词: 没有数量词就只能匹配一个字符 [abc] 在abc内 [^abc] 除了abc外 [a-zA-Z] a到z或者A到Z 两头字母包括在内 [a-d[m-p]]a到d或者m到p 并集 [a-d&&[def]] 交集 匹配功能 举个例子: String match="1[12a]\\d{2}"; System.out.println(number.matches(match)?"ok":"flase");//number是被匹配的字符串 意思就是第一个为1 第二个为1或2或a 接下来的为数字并且有且只有两个 再举例: String match="a\\d{0,2}[a]{2}"; 第一个为a 第二个开始后0个到2个内必须为数字 剩下的两位必须为a 数量词如{2} 必须跟范围词或者预定义符后面有效 是对这两个的修饰 切割功能 举例:根据空格来对字符串进行切分 String str = "aa.bb.cc"; str = "-1 99 4 23"; String[] arr = str.split(" +"); //+这里代表一个或者多个空格 再举例:根据重叠词进行切割 String str = "sdqqfgkkkhjppppkl"; String[] arr = str.split("(.)\\1+"); 注意:为了提高规则复用,用()进行封装,每一个括号都有一个编号,从1开始,为了复用这个规则。可以通过编号来完成该规则的调用。需要对编号数字进行转义。\1就代表获取1组规则。 等于说 第一次获得s 然后\1引用 变成ss+ 无法切割 然后下一个 替换: String name="wocao141311164sdfsdf1232"; String reg="1[2345]\\d{3}"; String aa=name.replaceAll(reg, "****"); //将满足条件的内容替换成****格式 替换重复的 String name="wo我洗爱你你你"; String reg="(.)\\1+"; String aa=name.replaceAll(reg,"$1"); 如果需要在replaceAll方法正则的时候外部引用组的内容 要使用$组号 查找: 范例: Pattern p = Pattern.compile("a*b"); Matcher m = p.matcher("aaaaab"); boolean b = m.matches(); **注意:步骤: 1,先将正则表达式编译成正则对象。使用的是Pattern类一个静态的方法。compile(regex); 2,让正则对象和要操作的字符串相关联,通过matcher方法完成,并返回匹配器对象(Matcher)。 3,通过匹配器对象的方法将正则模式作用到字符串上对字符串进行针对性的功能操作** 查找三个的单词 String str = "da jia zhu yi le,ming tian bu fang jia,xie xie!"; String reg="\\b[a-zA-Z]{3}\\b"; Pattern pattern=Pattern.compile(reg); Matcher matcher= pattern.matcher(str); while(matcher.find()) { System.out.println(matcher.group()); } 先find(寻找有没有满足的 然后下移位置)才可以group(得到满足条件的字符串) \b是边界词 不匹配 只表示开始和结束 匹配emil: String str = "123sdfsd@sin.com.cn"; true str="1@1.1"; false String reg="[a-zA-Z_0-9]+@[a-zA-Z0-9]+(\\.[a-zA-Z]+)+"; System.out.println(str.matches(reg)); 网络爬虫:emil String content="有事没事联系:112312@12.com 又是没事士大夫似的说的说的1211112312@22.cn1211dfsd1@22.cn.com"; String reg="[a-zA-Z1-9]\\w{5,7}@[a-zA-Z0-9]{2,}(\\.(com|cn|net)){1,2}"; Matcher matcher= pattern.matcher(content); while(matcher.find()) { System.out.println(matcher.group()); } 我认为的重点: \b 熟悉: 匹配的是位置 \b 匹配这样的位置:它的前一个字符和后一个字符不全是(一个是,一个不是或不存在) \w String str = "sshello world"; String reg="hello\\b"; Pattern pattern=Pattern.compile(reg); Matcher matcher= pattern.matcher(str); while(matcher.find()) { String name=matcher.group(); System.out.println(name); } 抽象出来直接看区别: **String str = “sshello world”; String reg=”hello\b”;** 返回hello 分析: 以hello结尾 **String str = “ss hello,world”; String reg=”\bhello”;** 返回hello 分析: 以hello开头 就用 “It’s a nice day today.” 举例说明: 正确的正则:\bnice\b 分析:第一个 \b 前面一个字符是空格,后面一个字符是 ‘n’,不全是 \w, 所以可以匹配出 ‘n’ 是一个单词的开头。第二个 \b 前面一个字符是 ‘e’, 后面一个字符是空格,不全是 \w,可以匹配出 ‘e’ 是一个单词的结尾。 所以,合在一起,就能匹配出以 ‘n’ 开头以 ‘e’ 结尾的单词, 这里就能匹配出 “nice” 这个单词。 举例: String str = “ss shello world”; String reg=”hello\b w”; 返回hello w 分析: 以hello结尾 空格w开始 的内容 String str = “ss shello world”; String reg=”hello\bw”; 失败 分析: hello后面跟着w 违反了\b前后不能同时为\w所以匹配不到 reg就写错了 String str = “ss shello,world”; String reg=”hello\b.w”; 返回 hello,w 虽然.是任何字符都行 但是还是要根据str来判断 如文中 ,不是\w所以可以匹配到 如果是String str = “ss shellosworld”; 则无法匹配到

优秀的个人博客,低调大师

ThinkPHP框架学习(含数据库及I函数)

参考文献:https://www.kancloud.cn/manual/thinkphphttps://www.jianshu.com/p/ef3ee8260b2d www WEB子目录 ├─index.php 入口文件 ├─README.md README文件仅用于说明,实际部署的时候可以删除。 ├─Application 应用目录,默认是空的,但是第一次访问入口文件会自动生成 ├─Public 资源文件目录 └─ThinkPHP 框架目录 │ ├─Common 核心公共函数目录 │ ├─Conf 核心配置目录 │ ├─Lang 核心语言包目录 │ ├─Library 框架类库目录 │ │ ├─Think 核心Think类库包目录 │ │ ├─Behavior 行为类库目录 │ │ ├─Org Org类库包目录 │ │ ├─Vendor 第三方类库目录 │ │ ├─ ... 更多类库目录 │ ├─Mode 框架应用模式目录 │ ├─Tpl 系统模板目录 │ ├─LICENSE.txt 框架授权协议文件 │ ├─logo.png 框架LOGO文件 │ ├─README.txt 框架README文件 │ └─ThinkPHP.php 框架入口文件 入口文件(index.php) //定义应用入口文件 define('APP_PATH','./Application/'); require './ThinkPHP/ThinkPHP.php'; APP_PATH的定义支持相对路径和绝对路径,但必须以“/”结束 自动创建目录(第一次访问index.php): Application ├─Common 应用公共模块(不能直接访问) │ ├─Common 应用公共函数目录 │ └─Conf 应用公共配置文件目录 ├─Home 默认生成的Home模块 │ ├─Conf 模块配置文件目录 │ ├─Common 模块公共函数目录 │ ├─Controller 模块控制器目录 │ ├─Model 模块模型目录 │ └─View 模块视图文件目录 ├─Runtime 运行时目录 │ ├─Cache 模版缓存目录 │ ├─Data 数据目录 │ ├─Logs 日志目录 │ └─Temp 缓存目录 在自动生成目录结构的同时,在各个目录下面还生成了index.html文件,这是ThinkPHP自动生成的目录安全文件。为了避免某些服务器开启了目录浏览权限后可以直接在浏览器输入URL地址查看目录,系统默认开启了目录安全文件机制,会在自动生成目录的时候生成空白的index.html文件 控制器 在自动生成的Application/Home/Controller目录下面有一个 IndexController.class.php 文件,这就是默认的Index控制器文件。控制器类的命名方式是: 控制器名(驼峰法,首字母大写)+Controller 控制器文件的命名方式是:类名+class.php(类文件后缀) 命名规范 类文件都是以.class.php为后缀(这里是指的ThinkPHP内部使用的类库文件,不代表外部加载的类库文件),使用驼峰法命名,并且首字母大写,例如 DbMysql.class.php; 类的命名空间地址和所在的路径地址一致,例如Home\Controller\UserController类所在的路径应该是 Application/Home/Controller/UserController.class.php; 确保文件的命名和调用大小写一致,是由于在类Unix系统上面,对大小写是敏感的(而ThinkPHP在调试模式下面,即使在Windows平台也会严格检查大小写); 类名和文件名一致(包括上面说的大小写一致),例如 UserController类的文件命名是UserController.class.php, InfoModel类的文件名是InfoModel.class.php, 并且不同的类库的类命名有一定的规范; 函数、配置文件等其他类库文件之外的一般是以.php为后缀(第三方引入的不做要求); 函数的命名使用小写字母和下划线的方式,例如 get_client_ip; 方法的命名使用驼峰法,并且首字母小写或者使用下划线“_”,例如 getUserName,_parseType,通常下划线开头的方法属于私有方法; 属性的命名使用驼峰法,并且首字母小写或者使用下划线“_”,例如 tableName、_instance,通常下划线开头的属性属于私有属性; 以双下划线__打头的函数或方法作为魔法方法,例如 __call 和 __autoload; 常量以大写字母和下划线命名,例如 HAS_ONE和 MANY_TO_MANY; 配置参数以大写字母和下划线命名,例如HTML_CACHE_ON; 语言变量以大写字母和下划线命名,例如MY_LANG,以下划线打头的语言变量通常用于系统语言变量,例如 CLASS_NOT_EXIST; 对变量的命名没有强制的规范,可以根据团队规范来进行; ThinkPHP的模板文件默认是以.html 为后缀(可以通过配置修改); 数据表和字段采用小写加下划线方式命名,并注意字段名不要以下划线开头,例如 think_user 表和 user_name字段是正确写法,类似 _username 这样的数据表字段可能会被过滤。 配置加载 惯例配置->应用配置->模式配置->调试配置->状态配置->模块配置->扩展配置->动态配置//配置的优先顺序从右到左 框架内置有一个惯例配置文件(ThinkPHP/Conf/convention.php) 应用配置文件也就是调用所有模块之前都会首先加载的公共配置文件(Application/Common/Conf/config.php) 如果使用了普通应用模式之外的应用模式的话,还可以为应用模式单独定义配置文件,文件命名规范是: Application/Common/Conf/config_应用模式名称.php(仅在运行该模式下面才会加载)(可选) 开启调试模式,会自动加载框架的调试配置文件(ThinkPHP/Conf/debug.php)和应用调试配置文件(Application/Common/Conf/debug.php)(可选) 在公司和家里分别设置不同的数据库测试环境。在公司,在入口文件中定义:define('APP_STATUS','office');就会自动加载该状态对应的配置文件(Application/Common/Conf/office.php)。回家后,修改为:define('APP_STATUS','home');就会自动加载该状态对应的配置文件(Application/Common/Conf/home.php)。(可选) 读取配置 配置文件,统一使用系统提供的C方法来读取已有的配置。(ThinkPHP/Common/functions.php) 用法:C('参数名称') $model = C('URL_MODEL');//读取当前的URL模式配置参数 C('my_config',null,'default_config');//如果my_config尚未设置的话,则返回default_config字符串 因为配置参数是全局有效的,因此C方法可以在任何地方读取任何配置,即使某个设置参数已经生效过期了。 加载扩展配置 ①在Application/Home/Conf目录下新建user.php,内容如下 <?php return array( 'USER_TYPE' => 2, //用户类型 'USER_AUTH_ID' => 10, //用户认证ID 'USER_AUTH_TYPE' => 2, //用户认证模式 );//可以不用加?> ②修改同级的config.php <?php return array( 'LOAD_EXT_CONFIG'=> array('USER'=>'user') ); ③修改Application/Home/Controller/IndexController.class.php <?php namespace Home\Controller; use Think\Controller; class IndexController extends Controller { public function index(){ print_r(C('USER')); } } 打开localhost,会出现Array ( [USER_TYPE] => 2 [USER_AUTH_ID] => 10 [USER_AUTH_TYPE] => 2 ) 数据库操作分析 ①系统公共函数库: \ThinkPHP\Common\functions.php(封装了TP开放给外部的函数) ②ThinkPHP Model模型类: ThinkPHP\Library\Think\Model.class.php (TP的数据库架构类,提供curd类库,是一个对外的接口 ) ③TP内部curd类: ThinkPHP\Library\Think\Db\Driver.class.php (这个类的函数都被Model类中的curd操作间接的调用) Tp在执行数据库操作之前 函数M使用了以后会自动创建new Model类并且会实例化为一个对象返回此资源。 接着这个对象调用了where方法并且格式化处理以后,会将这个值赋值给此对象的一个成员变量$options(注:如果说我们在此对象中有调用其他的方法赋值例如where,table,alias,data,field,order,limit,page,group,having,join,union,distinct,lock,cache,comment等等这种操作方法,那么都会先赋值给此对象,而不是在代码直接进行sql语句拼接,所以我们使用Tp的连贯操作的时候,就不需要像SQL语句拼接那样需要考虑到关键字的顺序问题 处理完了前面选项之后,接下来就会去调用我们的find()方法去调用底层的一个select方法(Driver.class.php这个类中的select方法)来获取数据。所谓的find()方法就是等同于先给此对象的一个成员变量$options赋值操作limit=1然后进行select操作来获取对应的数据。 最终的sql语句:SELECT * FROMblog_adminWHERE username='admin' limit 1 如果给赋值了一个操作:M('admin')->field('username,password')->where( array('username'=>$username) )->find(); 执行的语句为SELECTusername,password FROMblog_adminWHERE username='admin' limit 1 ①thinkphp\ThinkPHP\Library\Think\Model.class.php 里的重要成员变量: protected $pk = 'id';// 主键名称 protected $fields = array(); // 字段信息 protected $options = array(); // 数据信息 protected $methods = array('strict','order','alias','having','group','lock','distinct','auto','filter','validate','result','token','index','force');// 链操作方法列表 ②where()方法的执行过程: $parse = array_map(array($this->db,'escapeString'),$parse);//对传递到字符串类型的数据,调用mysql_escape_string函数来处理任何返回 可以通过官方文档查看TP如何防止SQL注入:(http://document.thinkphp.cn/manual_3_2.html#sql_injection)where() 方法如果 传递的是$Model->where("id=%d and username='%s' and xx='%f'",array($id,$username,$xx)) 这种格式的,就会进行 mysql 的mysql_escape_string函数进行处理(mysql_escape_string总是将“ ' ”转换成“ \ ”)。 处理完成以后就会将已处理完成的数组赋值到M对象的成员函数$this->options['where'](options为success,error,display.....) 随后返回,供我们进行下一步的处理。 ③find()方法的执行过程: $options = $this->_parseOptions($options);//分析表达式,获取执行的表的名称,获取模型的名称,最后对表的字段进行处理。 $resultSet = $this->db->select($options);//调用db对象里的select方法查询数据 此方法的功能就是 获取主键,完善model类的成员变量,options数组,然后实例化 db类,调用select 方法获取数据,然后处理数据完以后返回数据 Find方法使用的 $this->_parseOptions() 这个方法的主要就是 获取操作的表名,查看是否有取别名,获取操作的模型,比对当前表的数据库字段是否一致,若有不一致的字段 $this->options['strict'] 设置了的时侯,进行报错处理 否则进行删除多余字段的处理。 执行过滤的方法为_parseType ,功能是数据类型检测并且进行强制转换(强制转换的类型为int,float,bool 三种类型) Find方法使用的$this->db->select() 方法 $this->db 是在ThinkPHP\Library\Think\Db类中的方法 ①其中parseSql()这个函数的主要功能是拼接sql语句$this->parseWhere(!empty($options['where'])?$options['where']:''),parseWhere 方法比其他的要复杂的多,其他的都是拼接字符串,过滤,然后返回。) ②parseWhere()方法 这方法会去判断传进来的变量内容是否是字符串,如果是的话,就直接返回,如果不是字符串而是数组的话,那么就会挨个的解析,并且判断是否是特殊的条件表达式,如果是,调用parseThinkWhere()方法(主要是解析特殊的条件并且调用parseValue ()方法),已上条件都不匹配的情况下就认为是普通查询 普通查询都会调用parseWhereItem 方法 ③parseWhereItem()方法 protected function parseWhereItem($key,$val)//$val值是从where函数接收到的数据,然后经过这个方法会先去判断是否是数组,不是数组就会到下面的流程 ...... $exp = strtolower($val[0]);//$exp的值=执行表达式(可以操作sql拼接的流程)=$val[0] ······ elseif('bind' == $exp ){ // 使用表达式 $whereStr .= $key.' = :'.$val[1]; }elseif('exp' == $exp ){ // 使用表达式 $whereStr .= $key.' '.$val[1]; }elseif(preg_match('/^(notin|not in|in)$/',$exp)){ // IN 运算 if(isset($val[2]) && 'exp'==$val[2]) { $whereStr .= $key.' '.$this->exp[$exp].' '.$val[1];//这三句 $whereStr .没有使用TP的过滤。 进入此方法以后会发现这个方法会根据 $exp 变量的不同 拼接不同的sql语句 而在这个方法中看到最多的就是parseValue()方法了 ④parseValue ()方法 这个方法会去调用escapeString()方法 (将传进来的变量进行addslashes 然后返回) 嗯。。。。。。。整体流程看起来我们可以发现TP使用的过滤方法就是一个简单addslashes 来防止过滤 I 函数(官方http://document.thinkphp.cn/manual_3_2.html#input_filter) 路径:ThinkPHP\Common\functions.php 方法名:function I($name,$default='',$filter=null,$datas=null)主要功能 确定数据类型 对数据进行循环取值 调用think_filter 函数进行过滤 在think_filter中: if(preg_match('/^(EXP|NEQ|GT|EGT|LT|ELT|OR|XOR|LIKE|NOTLIKE|NOT BETWEEN|NOTBETWEEN|BETWEEN|NOTIN|NOT IN|IN)$/i',$value)){ $value .= ' ';//正则匹配,过滤查询特殊字符,然后在这些条件表达式后面添加一个空格 例如: 没有使用think_filter 函数时: goods_name[0]=in&goods_name[1]=(true) and (updatexml(1,concat(1,(select user())),1))--&goods_name[2]=exp 使用了think_filter函数时: goods_name[0]=in &goods_name[1]=(true) and (updatexml(1,concat(1,(select user())),1))--&goods_name[2]=exp 其中elseif(preg_match('/^(notin|not in|in)$/',$exp))中If( in(空格) == 'in')不匹配,也就防止了sql注入的产生 审计方式 ①进入目录直接搜索 $_POST $_GET 查看是否带入了 where 查询。 ② 全局搜索 where 查看是否有字符串拼接的痕迹 ③全局搜索 order,having,group,alias (例:当我们可以操控 order传进去的参数时,也是可以进行注入的,这是因为tp对order方法只是一个字符串拼接的操作 ) ④全局搜索 join ,field,执行的都是字符串拼接,只要可以外部操作就可以产生注入

优秀的个人博客,低调大师

【数据结构】ArrayList原理及实现学习总结

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_34173549/article/details/79936622 一、ArrayList介绍 ArrayList是一种线性数据结构,它的底层是用数组实现的,相当于动态数组。与Java中的数组相比,它的容量能动态增长。类似于C语言中的动态申请内存,动态增长内存。当创建一个数组的时候,就必须确定它的大小,系统会在内存中开辟一块连续的空间,用来保存数组,因此数组容量固定且无法动态改变。ArrayList在保留数组可以快速查找的优势的基础上,弥补了数组在创建后,要往数组添加元素的弊端。实现的基本方法如下:1. 快速查找:在物理内存上采用顺序存储结构,因此可根据索引快速的查找元素。2. 容量动态增长: 当数组容量不够用时(表1),创建一个比原数组容量大的新数组(表2),将数组中的元素“搬”到新数组(表3),再将新的元素也放入新数组(表4),最后将新数组赋给原数组即可。(从左到右依次为表1,表2、表3、表4) 二、ArrayList继承关系 ArrayList继承于AbstractList,实现了List, RandomAccess, Cloneable, java.io.Serializable这些接口。实现了所有List接口的操作,并ArrayList允许存储null值。除了没有进行同步,ArrayList基本等同于Vector。在Vector中几乎对所有的方法都进行了同步,但ArrayList仅对writeObject和readObject进行了同步,其它比如add(Object)、remove(int)等都没有同步。 public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { } 1 2 ArrayList与Collection关系如下图,实线代表继承,虚线代表实现接口: AbstractList提供了List接口的默认实现(个别方法为抽象方法)。 List接口定义了列表必须实现的方法。 实现了RandomAccess接口:提供了随机访问功能。RandmoAccess是java中用来被List实现,为List提供快速访问功能的。在ArrayList中,我们即可以通过元素的序号快速获取元素对象;这就是快速随机访问。 实现了Cloneable接口:可以调用Object.clone方法返回该对象的浅拷贝。 实现了 java.io.Serializable 接口:可以启用其序列化功能,能通过序列化去传输。未实现此接口的类将无法使其任何状态序列化或反序列化。序列化接口没有方法或字段,仅用于标识可序列化的语义。 三、ArrayList的实现 对于ArrayList而言,它实现List接口、底层使用数组保存所有元素。其操作基本上是对数组的操作。下面进行具体的介绍: 1. 私有属性 // 保存ArrayList中数据的数组 private transient Object[] elementData; // ArrayList中实际数据的数量 private int size; 1 2 3 4 很容易理解,elementData存储ArrayList内的元素,size表示它包含的元素的数量。有个关键字需要解释:transient。Java的serialization提供了一种持久化对象实例的机制。当持久化对象时,可能有一个特殊的对象数据成员,我们不想用serialization机制来保存它。为了在一个特定对象的一个域上关闭serialization,可以在这个域前加上关键字transient。 2.构造函数 ArrayList提供了三种方式的构造器,可以构造一个指定初始容量的空列表、构造一个默认初始容量为10的空列表以及构造一个包含指定collection的元素的列表,这些元素按照该collection的迭代器返回它们的顺序排列的。 // ArrayList带容量大小的构造函数。 public ArrayList(int initialCapacity) { super(); if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity); // 新建一个数组 this.elementData = new Object[initialCapacity]; } // ArrayList构造函数。默认容量是10。 public ArrayList() { this(10); } // 创建一个包含collection的ArrayList public ArrayList(Collection<? extends E> c) { elementData = c.toArray(); size = elementData.length; // c.toArray might (incorrectly) not return Object[] (see 6260652) if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 3.元素存储ArrayList是基于数组实现的,当添加元素的时候,如果数组大,则在将某个位置的值设置为指定元素即可,如果数组容量不够了,以add(E e)为例,可以看到add(E e)中先调用了ensureCapacity(size+1)方法,之后将元素的索引赋给elementData[size],而后size自增。例如初次添加时,size为0,add将elementData[0]赋值为e,然后size设置为1(类似执行以下两条语句elementData[0]=e;size=1)。将元素的索引赋给elementData[size]不是会出现数组越界的情况吗?这里关键就在ensureCapacity(size+1)中了。具体实现如下:(1)当调用下面这两个方法向数组中添加元素时,默认是添加到数组中最后一个元素的后面。内存结构变化如下: // 添加元素e public boolean add(E e) { // 确定ArrayList的容量大小 ensureCapacity(size + 1); // Increments modCount!! // 添加e到ArrayList中 elementData[size++] = e; return true; } // 将集合c追加到ArrayList中 public boolean addAll(Collection<? extends E> c) { Object[] a = c.toArray(); int numNew = a.length; ensureCapacity(size + numNew); // Increments modCount System.arraycopy(a, 0, elementData, size, numNew); size += numNew; return numNew != 0; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 (2)当调用下面这两个方法向数组中添加元素或集合时,会先查找索引位置,然后将元素添加到索引处,最后把添加前索引后面的元素追加到新元素的后面。 // 将e添加到ArrayList的指定位置 public void add(int index, E element) { if (index > size || index < 0) throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size); ensureCapacity(size + 1); // Increments modCount!! System.arraycopy(elementData, index, elementData, index + 1, size - index); elementData[index] = element; size++; } // 从index位置开始,将集合c添加到ArrayList public boolean addAll(int index, Collection<? extends E> c) { if (index > size || index < 0) throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size); Object[] a = c.toArray(); int numNew = a.length; ensureCapacity(size + numNew); // Increments modCount int numMoved = size - index; if (numMoved > 0) System.arraycopy(elementData, index, elementData, index + numNew, numMoved); System.arraycopy(a, 0, elementData, index, numNew); size += numNew; return numNew != 0; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 (3)调用该方法会将index位置的元素用新元素替代 // 设置index位置的值为element public E set(int index, E element) { RangeCheck(index); E oldValue = (E) elementData[index]; elementData[index] = element; return oldValue; } 1 2 3 4 5 6 7 4.元素读取 // 返回此列表中指定位置上的元素。 public E get(int index) { RangeCheck(index); return (E) elementData[index]; } 1 2 3 4 5 5.元素删除ArrayList提供了根据下标或者指定对象两种方式的删除功能。如下:romove(int index),首先是检查范围,修改modCount,保留将要被移除的元素,将移除位置之后的元素向前挪动一个位置,将list末尾元素置空(null),返回被移除的元素。 // 删除ArrayList指定位置的元素 public E remove(int index) { RangeCheck(index); modCount++; E oldValue = (E) elementData[index]; int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index + 1, elementData, index, numMoved); elementData[--size] = null; // Let gc do its work return oldValue; } 1 2 3 4 5 6 7 8 9 10 11 6. 调整数组容量ensureCapacity(1)从上面介绍的向ArrayList中存储元素的代码中,我们看到,每当向数组中添加元素时,都要去检查添加后元素的个数是否会超出当前数组的长度,如果超出,数组将会进行扩容,以满足添加数据的需求。数组扩容通过一个公开的方法ensureCapacity(int minCapacity)来实现。在实际添加大量元素前,我也可以使用ensureCapacity来手动增加ArrayList实例的容量,以减少递增式再分配的数量。 // 确定ArrarList的容量。 // 若ArrayList的容量不足以容纳当前的全部元素,设置 新的容量=“(原始容量x3)/2 + 1” public void ensureCapacity(int minCapacity) { // 将“修改统计数”+1 modCount++; int oldCapacity = elementData.length; // 若当前容量不足以容纳当前的元素个数,设置 新的容量=“(原始容量x3)/2 + 1” if (minCapacity > oldCapacity) { Object oldData[] = elementData; int newCapacity = (oldCapacity * 3) / 2 + 1; if (newCapacity < minCapacity) newCapacity = minCapacity; elementData = Arrays.copyOf(elementData, newCapacity); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 从上述代码中可以看出,数组进行扩容时,会将老数组中的元素重新拷贝一份到新的数组中,每次数组容量的增长大约是其原容量的1.5倍。这种操作的代价是很高的,因此在实际使用时,我们应该尽量避免数组容量的扩张。当我们可预知要保存的元素的多少时,要在构造ArrayList实例时,就指定其容量,以避免数组扩容的发生。或者根据实际需求,通过调用ensureCapacity方法来手动增加ArrayList实例的容量。(2)ArrayList还给我们提供了将底层数组的容量调整为当前列表保存的实际元素的大小的功能。它可以通过trimToSize方法来实现。代码如下: // 将当前容量值设为 =实际元素个数 public void trimToSize() { modCount++; int oldCapacity = elementData.length; if (size < oldCapacity) { elementData = Arrays.copyOf(elementData, size); } } 1 2 3 4 5 6 7 8 由于elementData的长度会被拓展,size标记的是其中包含的元素的个数。所以会出现size很小但elementData.length很大的情况,将出现空间的浪费。trimToSize将返回一个新的数组给elementData,元素内容保持不变,length和size相同,节省空间。7.转为静态数组toArray的两种方法(1)调用Arrays.copyOf将返回一个数组,数组内容是size个elementData的元素,即拷贝elementData从0至size-1位置的元素到新数组并返回。 // 返回ArrayList的Object数组 public Object[] toArray() { return Arrays.copyOf(elementData, size); } 1 2 3 4 (2)如果传入数组的长度小于size,返回一个新的数组,大小为size,类型与传入数组相同。所传入数组长度与size相等,则将elementData复制到传入数组中并返回传入的数组。若传入数组长度大于size,除了复制elementData外,还将把返回数组的第size个元素置为空。 // 返回ArrayList的模板数组。所谓模板数组,即可以将T设为任意的数据类型 public <T> T[] toArray(T[] a) { // 若数组a的大小 < ArrayList的元素个数; // 则新建一个T[]数组,数组大小是“ArrayList的元素个数”,并将“ArrayList”全部拷贝到新数组中 if (a.length < size) return (T[]) Arrays.copyOf(elementData, size, a.getClass()); // 若数组a的大小 >= ArrayList的元素个数; // 则将ArrayList的全部元素都拷贝到数组a中。 System.arraycopy(elementData, 0, a, 0, size); if (a.length > size) a[size] = null; return a; } 1 2 3 4 5 6 7 8 9 10 11 12 13 8.实现了Cloneable接口,进行数据浅拷贝 // 克隆函数 public Object clone() { try { ArrayList<E> v = (ArrayList<E>) super.clone(); // 将当前ArrayList的全部元素拷贝到v中 v.elementData = Arrays.copyOf(elementData, size); v.modCount = 0; return v; } catch (CloneNotSupportedException e) { // this shouldn't happen, since we are Cloneable throw new InternalError(); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 9.实现Serializable 接口,启用其序列化功能 // java.io.Serializable的写入函数 // 将ArrayList的“容量,所有的元素值”都写入到输出流中 private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { // Write out element count, and any hidden stuff int expectedModCount = modCount; s.defaultWriteObject(); // 写入“数组的容量” s.writeInt(elementData.length); // 写入“数组的每一个元素” for (int i = 0; i < size; i++) s.writeObject(elementData[i]); if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } } // java.io.Serializable的读取函数:根据写入方式读出 // 先将ArrayList的“容量”读出,然后将“所有的元素值”读出 private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { // Read in size, and any hidden stuff s.defaultReadObject(); // 从输入流中读取ArrayList的“容量” int arrayLength = s.readInt(); Object[] a = elementData = new Object[arrayLength]; // 从输入流中将“所有的元素值”读出 for (int i = 0; i < size; i++) a[i] = s.readObject(); }

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

用户登录
用户注册