首页
留言板
友链
关于
Search
1
Nginx搭建一个简易的图床
492 阅读
2
内测“合金弹头”嘿嘿
467 阅读
3
Kettle循环遍历结果集作为参数传入转换
373 阅读
4
Apache Dubbo初步认识
344 阅读
5
给站点添加邮件通知功能
306 阅读
知识库
好奇猫
日常说
笔记本
登录
/
注册
Search
标签搜索
maven
Spring
Java
Java代码
idea
帆软
vue
MySQL
git
前端
CSS
游戏
Bootstrap
生活
网上冲浪
邮件配置
说说
Nginx
Excel
数据库调优
龙流
累计撰写
58
篇文章
累计收到
12
条评论
首页
栏目
知识库
好奇猫
日常说
笔记本
页面
留言板
友链
关于
搜索到
37
篇与
笔记本
的结果
2021-12-14
Apache Dubbo初步认识
Dubbo简介 Apache Double是一款高性能的Java RPC框架。其前身是阿里巴巴公司开源的一个高性能、轻量级的开源Java RPC框架,可以和Spring框架无缝集成。 什么是RPC? RPC全称为remote procedure call,即**远程过程调用**。比如两台服务器A和B,A服务器上部署一个应用,B服务器上部署一个应用,A服务器上的应用 想调用B服务器上的应用提供的方法,由于两个应用不在一个内存空间,不能直接调用,所以需要通过网络来表达调用的语义和传达调用的数据。 需要注意的是RPC并不是一个具体的技术,而是指整个网络远程调用过程。 RPC是一个泛化的概念,严格来说一切远程过程调用手段都属于RPC范畴。各种开发语言都有自己的RPC框架。 Java中的RPC框架比较多,广泛使用的有RMI、Hessian、Dubbo等。 Dubbo官网地址:http://dubbo.apache.org Dubbo提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。Dubbo架构图节点角色说明:节点角色名称Provider暴露服务的服务提供方Consumer调用远程服务的服务消费方Registry服务注册与发现的注册中心Monitor统计服务的调用次数和调用时间的监控中心Container服务运行容器虚线都是异步访问,实线都是同步访问蓝色虚线:在启动时完成的功能红色虚线(实线)都是程序运行过程中执行的功能服务注册中心Zookeeper通过前面的Dubbo架构图可以看到,Registry(服务注册中心)在其中起着至关重要的作用。Dubbo官方推荐使用Zookeeper作为服务注册中心。Zookeeper介绍 Zookeeper 是 Apache Hadoop 的子项目,是一个树型的目录服务,支持变更推送,适合作为 Dubbo 服务的注册中心,工业强度较高,可用于生产环境,并推荐使用 。Zookeeper树型目录服务:流程说明:服务提供者(Provider)启动时: 向 /dubbo/com.foo.BarService/providers 目录下写入自己的 URL 地址服务消费者(Consumer)启动时: 订阅 /dubbo/com.foo.BarService/providers 目录下的提供者 URL 地址,并向 `/dubbo/com.foo.BarService/consumers` 目录下写入自己的 URL 地址监控中心(Monitor)启动时: 订阅 /dubbo/com.foo.BarService 目录下的所有提供者和消费者 URL 地址安装Zookeeper下载地址:http://archive.apache.org/dist/zookeeper/ 安装步骤: 第一步:安装 jdk(略) 第二步:把 zookeeper 的压缩包(zookeeper-3.4.6.tar.gz)上传到 linux 系统 第三步:解压缩压缩包 tar -zxvf zookeeper-3.4.6.tar.gz 第四步:进入zookeeper-3.4.6目录,创建data目录 mkdir data 第五步:进入conf目录 ,把zoo_sample.cfg 改名为zoo.cfg cd conf mv zoo_sample.cfg zoo.cfg 第六步:打开zoo.cfg文件, 修改data属性:dataDir=/root/zookeeper-3.4.6/data启动、停止Zookeeper进入Zookeeper的bin目录,启动服务命令 ./zkServer.sh start 停止服务命令 ./zkServer.sh stop 查看服务状态: ./zkServer.sh statusDubbo快速入门Dubbo作为一个RPC框架,其最核心的功能就是要实现跨网络的远程调用。实例创建两个应用,一个作为服务的提供方,一个作为服务的消费方。通过Dubbo来实现服务消费方远程调用服务提供方的方法。服务提供方开发{collapse}{collapse-item label="(1)创建maven工程(打包方式为war)dubbodemo_provider,在pom.xml文件中导入如下坐标"}<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <spring.version>5.0.5.RELEASE</spring.version> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jms</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${spring.version}</version> </dependency> <!-- dubbo相关 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <version>2.6.0</version> </dependency> <dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.4.7</version> </dependency> <dependency> <groupId>com.github.sgroschupf</groupId> <artifactId>zkclient</artifactId> <version>0.1</version> </dependency> <dependency> <groupId>javassist</groupId> <artifactId>javassist</artifactId> <version>3.12.1.GA</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.47</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.3.2</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <configuration> <!-- 指定端口 --> <port>8081</port> <!-- 请求路径 --> <path>/</path> </configuration> </plugin> </plugins> </build>{/collapse-item}{collapse-item label="(2)配置web.xml文件"}<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Archetype Created Web Application</display-name> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext*.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> </web-app>{/collapse-item}{collapse-item label="(3)创建服务接口"}package com.lwlong.service; public interface HelloService { public String sayHello(String name); }{/collapse-item}{collapse-item label="(4)创建服务实现类"}package com.lwlong.service.impl; import com.alibaba.dubbo.config.annotation.Service; import com.lwlong.service.HelloService; @Service public class HelloServiceImpl implements HelloService { public String sayHello(String name) { return "hello " + name; } } //注意:服务实现类上使用的Service注解是Dubbo提供的,用于对外发布服务{/collapse-item}{collapse-item label="(5)在src/main/resources下创建applicationContext-service.xml "}<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 当前应用名称,用于注册中心计算应用间依赖关系,注意:消费者和提供者应用名不要一样 --> <dubbo:application name="dubbodemo_provider" /> <!-- 连接服务注册中心zookeeper ip为zookeeper所在服务器的ip地址--> <dubbo:registry address="zookeeper://192.168.134.129:2181"/> <!-- 注册 协议和port 端口默认是20880 --> <dubbo:protocol name="dubbo" port="20881"></dubbo:protocol> <!-- 扫描指定包,加入@Service注解的类会被发布为服务 --> <dubbo:annotation package="com.lwlong.service.impl" /> </beans>{/collapse-item}{/collapse}(6)启动服务 tomcat7:run服务消费方开发 (1)创建maven工程(打包方式为war)dubbodemo_consumer,pom.xml配置和上面服务提供者相同,只需要将Tomcat插件的端口号改为8082即可{collapse}{collapse-item label="(2)配置web.xml文件"}<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Archetype Created Web Application</display-name> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 指定加载的配置文件 ,通过参数contextConfigLocation加载 --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext-web.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> </web-app>{/collapse-item}{/collapse} (3)将服务提供者工程中的HelloService接口复制到当前工程{collapse}{collapse-item label="(4)编写Controller"}package com.lwlong.controller; import com.alibaba.dubbo.config.annotation.Reference; import com.lwlong.service.HelloService; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller @RequestMapping("/demo") public class HelloController { @Reference private HelloService helloService; @RequestMapping("/hello") @ResponseBody public String getName(String name){ //远程调用 String result = helloService.sayHello(name); System.out.println(result); return result; } } //注意:Controller中注入HelloService使用的是Dubbo提供的@Reference注解{/collapse-item}{collapse-item label="(5)在src/main/resources下创建applicationContext-web.xml"}<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 当前应用名称,用于注册中心计算应用间依赖关系,注意:消费者和提供者应用名不要一样 --> <dubbo:application name="dubbodemo-consumer" /> <!-- 连接服务注册中心zookeeper ip为zookeeper所在服务器的ip地址--> <dubbo:registry address="zookeeper://192.168.134.129:2181"/> <!-- 扫描的方式暴露接口 --> <dubbo:annotation package="com.lwlong.controller" /> </beans>{/collapse-item}{/collapse}(6)运行测试 tomcat7:run启动 在浏览器输入http://localhost:8082/demo/hello.do?name=Jack,查看浏览器输出结果思考一:上面的Dubbo入门案例中我们是将HelloService接口从服务提供者工程(dubbodemo_provider)复制到服务消费者工程(dubbodemo_consumer)中,这种做法是否合适?还有没有更好的方式?答:这种做法显然是不好的,同一个接口被复制了两份,不利于后期维护。更好的方式是单独创建一个maven工程,将此接口创建在这个maven工程中。需要依赖此接口的工程只需要在自己工程的pom.xml文件中引入maven坐标即可。思考二:在服务消费者工程(dubbodemo_consumer)中只是引用了HelloService接口,并没有提供实现类,Dubbo是如何做到远程调用的?答:Dubbo底层是基于代理技术为HelloService接口创建代理对象,远程调用是通过此代理对象完成的。可以通过开发工具的debug功能查看此代理对象的内部结构。另外,Dubbo实现网络传输底层是基于Netty框架完成的。思考三:上面的Dubbo入门案例中我们使用Zookeeper作为服务注册中心,服务提供者需要将自己的服务信息注册到Zookeeper,服务消费者需要从Zookeeper订阅自己所需要的服务,此时Zookeeper服务就变得非常重要了,那如何防止Zookeeper单点故障呢?答:Zookeeper其实是支持集群模式的,可以配置Zookeeper集群来达到Zookeeper服务的高可用,防止出现单点故障。Dubbo相关配置说明 **包扫描** <dubbo:annotation package="com.lwlong.service" /> 服务提供者和服务消费者都需要配置,表示包扫描,作用是扫描指定包(包括子包)下的类。 如果不使用包扫描,也可以通过如下配置的方式来发布服务: <bean id="helloService" class="com.lwlong.service.impl.HelloServiceImpl" /> <dubbo:service interface="com.lwlong.api.HelloService" ref="helloService" /> 作为服务消费者,可以通过如下配置来引用服务: <!-- 生成远程服务代理,可以和本地bean一样使用helloService --> <dubbo:reference id="helloService" interface="com.lwlong.api.HelloService" /> 上面这种方式发布和引用服务,一个配置项(<dubbo:service>、<dubbo:reference>)只能发布或者引用一个服务, 如果有多个服务,这种方式就比较繁琐了。推荐使用包扫描方式。 **协议** <dubbo:protocol name="dubbo" port="20880"/> 一般在服务提供者一方配置,可以指定使用的协议名称和端口号。 其中Dubbo支持的协议有:dubbo、rmi、hessian、http、webservice、rest、redis等。 推荐使用的是dubbo协议。 dubbo 协议采用单一长连接和 NIO 异步通讯,适合于小数据量大并发的服务调用,以及服务消费者机器数远大于 服务提供者机器数的情况。不适合传送大数据量的服务,比如传文件,传视频等,除非请求量很低。 也可以在同一个工程中配置多个协议,不同服务可以使用不同的协议,例如: <!-- 多协议配置 --> <dubbo:protocol name="dubbo" port="20880" /> <dubbo:protocol name="rmi" port="1099" /> <!-- 使用dubbo协议暴露服务 --> <dubbo:service interface="com.lwlong.api.HelloService" ref="helloService" protocol="dubbo" /> <!-- 使用rmi协议暴露服务 --> <dubbo:service interface="com.lwlong.api.DemoService" ref="demoService" protocol="rmi" /> **启动时检查** <dubbo:consumer check="false"/> 上面这个配置需要配置在服务消费者一方,如果不配置默认check值为true。Dubbo 缺省会在启动时检查依赖的服务 是否可用,不可用时会抛出异常,阻止 Spring 初始化完成,以便上线时,能及早发现问题。 可以通过将check值改为false来关闭检查。建议在开发阶段将check值设置为false,在生产环境下改为true。 **负载均衡** 负载均衡(Load Balance):其实就是将请求分摊到多个操作单元上进行执行,从而共同完成工作任务。 在集群负载均衡时,Dubbo 提供了多种均衡策略(包括随机、轮询、最少活跃调用数、一致性Hash),缺省为random随机调用。 配置负载均衡策略,既可以在服务提供者一方配置,也可以在服务消费者一方配置,如下: @Controller @RequestMapping("/demo") public class HelloController { //在服务消费者一方配置负载均衡策略 @Reference(check = false,loadbalance = "random") private HelloService helloService; @RequestMapping("/hello") @ResponseBody public String getName(String name){ //远程调用 String result = helloService.sayHello(name); System.out.println(result); return result; } } //在服务提供者一方配置负载均衡 @Service(loadbalance = "random") public class HelloServiceImpl implements HelloService { public String sayHello(String name) { return "hello " + name; } } /** 可以通过启动多个服务提供者来观察Dubbo负载均衡效果。 注意:因为我们是在一台机器上启动多个服务提供者,所以需要修改tomcat的端口号和Dubbo服务的端口号来防止端口冲突。 在实际生产环境中,多个服务提供者是分别部署在不同的机器上,所以不存在端口冲突问题。 */
2021年12月14日
344 阅读
0 评论
0 点赞
2021-12-09
node.js安装 简介 使用
Node.js安装1、下载地址:https://nodejs.org/en/download/ 下载你系统对于的安装包(.msi文件可执行安装程序包) 2、以.msi安装程序包为例:下载完成后双击运行,一直下一步选择安装目录即可。 3、环境配置:这里的环境配置主要配置的是npm安装的全局模块所在的路径,也可以不配置即使用默认配置路径默认在C盘 我希望将全模块所在路径和缓存路径放在我node.js安装的文件夹中,则在我安装的文件夹【D:\Develop\nodejs】下创建两个文件夹【node_global】及【node_cache】 创建完两个空文件夹之后,打开cmd命令窗口,输入 npm config set prefix "D:\Develop\nodejs\node_global" npm config set cache "D:\Develop\nodejs\node_cache" 接下来设置环境变量,关闭cmd窗口,“我的电脑”-右键-“属性”-“高级系统设置”-“高级”-“环境变量” 进入环境变量对话框,在【系统变量】下新建【NODE_PATH】,输入【D:\Develop\nodejs\node_global\node_modules】,将【用户变量】下的【Path】修改为【D:\Develop\nodejs\node_global】 4、测试: node -v 查看node版本 npm -v 查看npm版本 安装个module测试下,我们就安装最常用的express模块 npm install express -g # -g是全局安装的意思Node.js简介Node.js 是一个开源和跨平台的 JavaScript 运行时环境。 它几乎是任何类型项目的流行工具!Node.js 在浏览器之外运行 V8 JavaScript 引擎(Google Chrome 的内核)。 这使得 Node.js 的性能非常好。npm 以其简单的结构帮助 Node.js 生态系统蓬勃发展,现在 npm 仓库托管了超过 1,000,000 个开源包,你可以自由使用。更多详细了解请移步文档说明Node.js使用运行 Node.js 程序的常规方法是,运行全局可用的 node 命令(已安装 Node.js)并传入要执行的文件的名称。 如何从 Node.js 程序退出:1、当在控制台中运行程序时,可以使用 ctrl C 将其关闭 2、使用process.exit() 进程会被立即强制终止 3、也可以设置process.exitCode = 1 当进程完成所有处理后,程序会正常地退出。
2021年12月09日
74 阅读
0 评论
0 点赞
2021-11-25
git安装详细介绍(不要再按有些教程说的只管点下一步,咱们要知其然并知其所以然)
git详细安装过程,操作解释git下载官网地址: https://git-scm.com/安装双击exe安装包1、软件说明2、设置安装路径3、设置一下插件配置4、自定义应用名称5、Git的默认编辑器,建议选默认的Vim编辑器,直接下一步6、这个界面是调整您的PATH环境。 第一种配置是“仅从Git Bash使用Git”。这是最安全的选择,因为您的PATH根本不会被修改。您只能使用 Git Bash 的 Git 命令行工具。但是这将不能通过第三方软件使用。 第二种配置是“从命令行以及第三方软件进行Git”。该选项被认为是安全的,因为它仅向PATH添加了一些最小的Git包装器,以避免使用可选的Unix工具造成环境混乱。您将能够从Git Bash,命令提示符和Windows PowerShell以及在PATH中寻找Git的任何第三方软件中使用Git。这也是推荐的选项。 第三种配置是“从命令提示符使用Git和可选的Unix工具”。警告:这将覆盖Windows工具,如 “ find 和 sort ”。只有在了解其含义后才使用此选项。 推荐的选项第二种配置,点击“Next”按钮继续到下图的界面:7、选择Https后端传输 第一个选项是“使用 OpenSSL 库”。服务器证书将使用ca-bundle.crt文件进行验证。这也是我们常用的选项。 第二个选项是“使用本地 Windows 安全通道库”。服务器证书将使用Windows证书存储验证。此选项还允许您使用公司的内部根CA证书,例如通过Active Directory Domain Services 。 我使用默认选项第一项,点击“Next”按钮继续到下图的界面:8、配置尾行符合转换这个界面是配置行尾符号转换。 第一个选项是“签出Windows风格,提交Unix风格的行尾”。签出文本文件时,Git会将LF转换为CRLF。提交文本文件时,CRLF将转换为LF。对于跨平台项目,这是Windows上的推荐设置(“ core.autocrlf”设置为“ true”) 第二个选项是“按原样签出,提交Unix样式的行尾”。签出文本文件时,Git不会执行任何转换。 提交文本文件时,CRLF将转换为LF。对于跨平台项目,这是Unix上的建议设置(“ core.autocrlf”设置为“ input”) 第三种选项是“按原样签出,按原样提交”。当签出或提交文本文件时,Git不会执行任何转换。不建议跨平台项目选择此选项(“ core.autocrlf”设置为“ false”) 我选择第一种选项,点击“Next”按钮继续到下图的界面:9、配置终端模拟器以与Git Bash一起使用这个界面是配置终端模拟器以与Git Bash一起使用。 第一个选项是“使用MinTTY(MSYS2的默认终端)”。Git Bash将使用MinTTY作为终端模拟器,该模拟器具有可调整大小的窗口,非矩形选择和Unicode字体。Windows控制台程序(例如交互式Python)必须通过“ winpty”启动才能在MinTTY中运行。 第二个选项是“使用Windows的默认控制台窗口”。Git将使用Windows的默认控制台窗口(“cmd.exe”),该窗口可以与Win32控制台程序(如交互式Python或node.js)一起使用,但默认的回滚非常有限,需要配置为使用unicode 字体以正确显示非ASCII字符,并且在Windows 10之前,其窗口不能自由调整大小,并且只允许矩形文本选择。 我选择默认的第一种选项,点击“Next”按钮继续到下图的界面:10、配置额外的选项这个界面是配置配置额外的选项。 第一个选项是“启用文件系统缓存”。文件系统数据将被批量读取并缓存在内存中用于某些操作(“core.fscache”设置为“true”)。 这提供了显著的性能提升。 第二个选项是“启用Git凭证管理器”。Windows的Git凭证管理器为Windows提供安全的Git凭证存储,最显着的是对Visual Studio Team Services和GitHub的多因素身份验证支持。 (需要.NET Framework v4.5.1或更高版本) 第三个选项是“启用符号链接”。启用符号链接(需要SeCreateSymbolicLink权限)。请注意,现有存储库不受此设置的影响。 我勾选默认的第一、第二选项,点击“Install”11、安装完成12、启动测试到此,Git的安装完成,可以在开始菜单中看到Git的三个启动图标(Git Bash、Git CMD(Deprecated)、Git GUI)。 Git Bash,是Git配套的一个控制台 Git CMD(Deprecated),是通过CMD使用Git(不推荐使用) Git GUI,是Git的可视化操作工具
2021年11月25日
163 阅读
0 评论
0 点赞
2021-11-11
Maven3.3.9的安装与配置
安装下载地址:http://archive.apache.org/dist/maven/maven-3/3.3.9/binaries/首先Maven安装简单三步:下载、解压、配置环境变量 解压到全英文路径下 解压后配置MAVEN_HOME环境变量 右键计算机–>属性–>高级系统设置–>环境变量 变量名填入:MAVEN_HOME 变量值填入:Maven解压的路径,我这里是D:\Program Files\apache-maven-3.3.9 点击确定 找到Path(这里不区分大小写),编辑它 点击新建–>填入%MAVEN_HOME%\bin -->确定 验证: WIN + R, 输入cmd 输入mvn -v 输出Apach Maven 3.3.9字样说明安装配置成功配置settings.xml配置settings文件,找到maven安装目录下的config,打开settings文件'找到<localRepository>标签,配置默认仓库指定目录' '<localRepository>D:/Program Files/DevApps/apache-maven-3.3.9/repository</localRepository>' 配置阿里云镜像,找到标签 <!--阿里源--> ~~ <mirror> <id>alimaven</id> <mirrorOf>central</mirrorOf> <name>aliyun maven</name> <url>http://maven.aliyun.com/nexus/content/repositories/central/</url> </mirror>~~ 已不能使用 <mirror> <id>mirrorId</id> <mirrorOf>repositoryId</mirrorOf> <name>Human Readable Name for this Mirror.</name> <url>http://my.repository.com/repo/path</url> </mirror>选配JDK版本,找到标签 <!--配置JDK--> <profile> <id>jdk-1.8</id> <activation> <activeByDefault>true</activeByDefault> <jdk>1.8</jdk> </activation> <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion> </properties> </profile>idea中使用Maven点击File -> Settings
2021年11月11日
176 阅读
0 评论
0 点赞
2021-11-10
Maven打包插件与idea
日常开发一个小项目或在企业中开发项目时,Maven管理项目是很好的一个选择; 项目所需的依赖包,项目的打包安装,都很方便也提高了开发人员操作效率。 然它也会带来使用上的成本,所以对于需要使用的功能进行一个记录,方便下次更方便的使用。Maven插件:打包第三方jar包 <!-- spring-boot-maven-plugin (提供了直接运行项目的插件:如果是通过parent方式继承spring-boot-starter-parent则不用此插件) --> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <executable>true</executable> <includeSystemScope>true</includeSystemScope><!--引入第三方jar包--> </configuration> <executions> <execution> <goals> <goal>repackage</goal><!--可以把依赖的包都打包到生成的Jar包中--> </goals> </execution> </executions> </plugin>Maven安装请参考
2021年11月10日
256 阅读
0 评论
0 点赞
2021-10-18
Java关于文件和目录的操作
{collapse}{collapse-item label="判断文件大小是否变化" open} 通过持续判断文件大大小有没有变化,在网络传输过程中,稳定的传输文件大小肯定会不断变大;当文件一段时间文件大小不变,不考虑其他情况(如断网),说明文件传输完成注意点:1、等待文件(非目录)读写完毕,费时的操作,不要放在主线程2、判断文件长度的方法不适用于粘贴复制操作的判断,粘贴复制一般file.length()时固定的不会有写入写出(test)public static boolean checkFileWritingOn(String fileName) throws Exception{ long oldLen = 0; long newLen = 0; File file = new File(fileName); while(true){ newLen = file.length(); if ((newLen - oldLen) > 0) { oldLen = newLen; System.out.println(file.length()); Thread.sleep(10000); } else { System.out.println("done"); return true; } } }{/collapse-item}{collapse-item label="获取文件创建时间、最后更新时间等" open} /** * 获取文件的创建时间 * @param file * @return */ public static Date getCreationTime(File file) throws IOException { if (file == null) { return null; }else { Path path = file.toPath(); BasicFileAttributes attr = Files.readAttributes(path, BasicFileAttributes.class); // 创建时间 Instant instant = attr.creationTime().toInstant(); Date fordate = Date.from(instant); return fordate; } // 更新时间 //Instant instant = attr.lastModifiedTime().toInstant(); // 上次访问时间 //Instant instant = attr.lastAccessTime().toInstant(); //String format = DateTimeFormatter.ofPattern("yyyyMMddHHmmss").withZone(ZoneId.systemDefault()).format(instant); //return format; }{/collapse-item}{collapse-item label="获取某路径下的所有文件/文件夹" open} /** * 获取路径下的所有文件/文件夹 * @param rootDir 需要遍历的文件夹路径 * @param isAddDirectory 是否将子文件夹的路径也添加到list集合中 * @return */ public static List<String> getAllFile(String rootDir, boolean isAddDirectory){ List<String> list = new ArrayList<String>(); File baseFile = new File(rootDir); if (baseFile.isFile() || !baseFile.exists()) { return list; } File[] files = baseFile.listFiles(); for (File file : files) { if (file.isDirectory()) { if(isAddDirectory){ list.add(file.getAbsolutePath()); } list.addAll(getAllFile(file.getAbsolutePath(),isAddDirectory)); } else { list.add(file.getAbsolutePath()); } } return list; }{/collapse-item}{collapse-item label="复制文件到指定目录" open} /** * @param filePath * @param targetPath * @throws IOException */ public static void fileCope(String filePath, String targetPath) throws IOException { //获得流 FileInputStream fileInputStream = new FileInputStream(filePath); File targetFile = new File(targetPath); //获取父目录 File parentFile = targetFile.getParentFile(); //判断是否存在 if (!parentFile.exists()) { // 创建父目录文件夹 parentFile.mkdirs(); } //判断文件是否存在 if (!targetFile.exists()) { //创建文件 targetFile.createNewFile(); } //新文件输出流 FileOutputStream fileOutputStream = new FileOutputStream (targetFile); byte[] buffer= new byte[1024]; int len; //将文件流信息读取文件缓存区,如果读取结果不为-1就代表文件没有读取完毕,反之已经读取完毕 while ((len=fileInputStream.read(buffer))!=-1) { fileOutputStream.write(buffer, 0, len); fileOutputStream.flush(); } fileInputStream.close(); fileOutputStream.close(); }{/collapse-item}{/collapse}
2021年10月18日
131 阅读
0 评论
0 点赞
2021-10-09
数据库连接池介绍
为什么使用连接池? 连接数据库使用的TCP协议,每次连接需要进行"三次握手"非常耗时,所以产生了连接池需求。程序一开始就创建一定数量的连接,放在一个容器中,这个容器称为连接池(相当于碗柜/容器)。使用的时候直接从连接池中取一个已经创建好的连接对象。关闭的时候不是真正关闭连接,而是将连接对象再次放回到连接池中。连接池使用: Java为数据库连接池提供了公共的接口:javax.sql.DataSource,各个厂商需要让自己的连接池实现这个接口。(eg:需遵循统一的规则,这样应用程序可以方便的切换不同厂商的连接池!)常见的第三方连接池:C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。C3P0是异步操作的,所以一些操作时间过长的JDBC通过其它的辅助线程完成。目前使用它的开源项目有Hibernate,Spring等。C3P0有自动回收空闲连接功能阿里巴巴-德鲁伊druid连接池:Druid是阿里巴巴开源平台上的一个项目,整个项目由数据库连接池、插件框架和SQL解析器组成。该项目主要是为了扩展JDBC的一些限制,可以让程序员实现一些特殊的需求。DBCP(DataBase Connection Pool)数据库连接池,是Apache上的一个Java连接池项目,也是Tomcat使用的连接池组件。dbcp没有自动回收空闲连接的功能。使用步骤:1. C3P0连接池使用导入jar包 (两个)c3p0-0.9.5.2.jar mchange-commons-java-0.2.12.jar拷贝配置文件到src目录创建连接池(配置文件自动读取的)编写配置文件 c3p0-config.xml最后数据库本身的驱动jar包不要忘记了,这里用的是mysql-connector-java-5.1.37-bin.jar<c3p0-config> <!-- 使用默认的配置读取连接池对象 --> <default-config> <!-- 连接参数 --> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="jdbcUrl">jdbc:mysql://localhost:3306/day19</property> <property name="user">root</property> <property name="password">root</property> <!-- 连接池参数 --> <property name="initialPoolSize">5</property> </default-config> </c3p0-config>c3p0连接池常用的配置参数:参数说明initialPoolSize初始连接数maxPoolSize最大连接数checkoutTimeout最大等待时间maxIdleTime最大空闲回收时间编写Java代码/** * C3P0连接池的工具类 * */ public class C3P0Utils { //1. 创建一个C3P0的连接池对象(会自动读取src目录下的c3p0-config.xml,所以不需要我们解析配置文件) public static DataSource ds = new ComboPooledDataSource(); //2. 提供 从连接池中 获取连接对象的方法 public static Connection getConnection() throws SQLException { Connection conn = ds.getConnection(); return conn; } //3. 提供 获得数据源(连接池对象)的方法 public static DataSource getDataSource(){ return ds; } }2. Druid连接池使用Druid的下载地址:https://github.com/alibaba/druidDRUID连接池使用的jar包:`druid-1.1.16.jar步骤:导入DRUID jar 包拷贝配置文件到src目录根据配置文件 创建连接池对象从连接池对象获得连接编写配置文件druid.properties, 放在src目录下driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/day19 username=root password=root编写Java代码/** * 阿里巴巴的连接池 Druid 工具类 */ public class DruidUtils { /* 1. 加载 druid.properties 配置文件 2. 创建 Druid 连接池对象 3. 提供 获得 连接池对象的方法 4. 提供 从连接池中 获取连接对象Connection的 方法 */ public static DataSource ds = null; static { try { //1. 加载 druid.properties 配置文件 InputStream is = DruidUtils.class.getClassLoader().getResourceAsStream("druid.properties"); Properties prop = new Properties(); prop.load(is); //2. 创建 Druid 连接池对象 ds = DruidDataSourceFactory.createDataSource(prop); } catch (Exception e) { e.printStackTrace(); } } /* 3. 提供 获得 连接池对象的方法 */ public static DataSource getDataSource(){ return ds; } /* 4. 提供 从连接池中 获取连接对象Connection的 方法 */ public static Connection getConnetion() throws SQLException { Connection conn = ds.getConnection(); return conn; } }
2021年10月09日
121 阅读
0 评论
0 点赞
2021-09-15
Java基础
Java基础包括但不限于{collapse}{collapse-item label="1、基本数据类型" open}学习java基本数据之前先清楚了解计算机存储单元与存储设备的最小信息单元的区别?在计算机中无论是硬盘还是内存,计算机存储设备的最小信息单元叫“位(bit)”,我们又称之为“比特位”,通常用小写字母'b'表示。存储单元:计算机中最基本的存储单元叫“字节(byte)”,通常用大写字母'B'表示,一个字节是由连续的8个位组成。 除字节外还有一些常见的存储单位,其换算单位如下: 1B(字节) = 8bit 1KB = 1024B 1MB = 1024KB 1GB = 1024MB 1TB = 1024GBjava中的基本数据类型数据类型关键字内存占用取值范围整数byte1-128~127整数short2-32768~32767整数int4-2的31次方到2的31次方-1整数long8-2的63次方到2的63次方-1浮点float4负数:-3.402823E+38到-1.401298E-45 正数: 1.401298E-45到 3.402823E+38浮点double8负数:-1.797693E+308到-4.9000000E-324 正数:4.9000000E-324 到 1.797693E+308字符char20-65535布尔boolean1true,false说明:e+38表示是乘以10的38次方,同样,e-45表示乘以10的负45次方。在java中整数默认是int类型,浮点数默认是double类型。基本数据类型转换 在Java中,一些数据类型之间是可以相互转换的。分为两种情况:自动类型转换和强制类型转换。自动类型转换:把一个表示数据范围小的数值或者变量赋值给另一个表示数据范围大的变量。这种转换方式是自动的,直接书写即可。例如:double num = 10; // 将int类型的10直接赋值给double类型 System.out.println(num); // 输出10.0强制类型转换:把一个表示数据范围大的数值或者变量赋值给另一个表示数据范围小的变量。强制类型转换格式:目标数据类型 变量名 = (目标数据类型)值或者变量;例如:double num1 = 5.5; int num2 = (int) num1; // 将double类型的num1强制转换为int类型 System.out.println(num2); // 输出5(小数位直接舍弃){/collapse-item}{collapse-item label="2、什么是常量/变量"} 变量的定义 变量:在程序运行过程中,其值可以发生改变的量。从本质上讲,变量是内存中的一小块区域,其值可以在一定范围内变化。 常量的定义 常量:在程序运行过程中,其值不可以发生改变的量。Java中的常量分类:字符串常量 用双引号括起来的多个字符(可以包含0个、一个或多个),例如"a"、"abc"、"中国"等整数常量 整数,例如:-10、0、88等小数常量 小数,例如:-5.5、1.0、88.88等字符常量 用单引号括起来的一个字符,例如:'a'、'5'、'B'、'中'等布尔常量 布尔值,表示真假,只有两个值true和false空常量 一个特殊的值,空值,值为null除空常量外,其他常量均可使用输出语句直接输出 使用变量时的注意事项在同一对花括号中,变量名不能重复。变量在使用之前,必须初始化(赋值)。定义long类型的变量时,需要在整数的后面加L(大小写均可,建议大写)。因为整数默认是int类型,整数太大可能超出int范围。定义float类型的变量时,需要在小数的后面加F(大小写均可,建议大写)。因为浮点数的默认类型是double, double的取值范围是大于float的,类型不兼容{/collapse-item}{/collapse}
2021年09月15日
43 阅读
0 评论
0 点赞
2021-09-14
FTP文件同步监控 CSV数据分析拆分多个
概述{dotted startColor="#ff6c6c" endColor="#1989fa"/}{x}通过properties配置两个FTP服务器,可以同步某个文件夹内容{x}实现FileAlterationListener监控文件夹文件新增/改变{x}通过后缀筛选文件类型{x}apache.commons包获取FTP连接客户端{x}net.sourceforge.javacsv包操作scv文件pom文件1、首先建议养成先配置日志的习惯 <!-- 日志工具包 --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.10.0</version> </dependency> <!--日志核心包--> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.10.0</version> </dependency> <!--slf4j的log4j实现类--> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j-impl</artifactId> <version>2.9.1</version> </dependency> <!--程序运行的时候检测用了哪种日志实现类--> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-jcl</artifactId> <version>2.9.1</version> </dependency> 2、使用到的工具包 <!--功能强大,操作文件,FTP连接--> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.3</version> </dependency> <!--快速读取和生成CSV文件--> <dependency> <groupId>net.sourceforge.javacsv</groupId> <artifactId>javacsv</artifactId> <version>2.0</version> </dependency>public class ITransferImpl implements ITransfer { @Override public void transferInner(String fromAccount, String outAccount, BigDecimal amount) { //使用java的JDBC //1、注册驱动 try { Class.forName("com.mysql.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace(); } Connection connection = null; //Statement statement = null; try { //2、获取连接 connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","root"); //3、执行SQL对象 // statement = connection.createStatement(); } catch (SQLException e) { e.printStackTrace(); } //4、查询转出账户余额是否大于转出金额 BigDecimal amount1 = null; String fromAccountSQL = "SELECT amount from ACCOUNT where accountid = ?"; try { PreparedStatement preparedStatement = connection.prepareStatement(fromAccountSQL); preparedStatement.setString(1,fromAccount); ResultSet resultSet = preparedStatement.executeQuery(); while (resultSet.next()){ amount1 = resultSet.getBigDecimal("amount"); if (amount.compareTo(amount1) == 1){ System.out.println("账户金额不足!"); return; } } //5、开启事务 connection.setAutoCommit(false); //6、更新转出账户余额 BigDecimal updateFromAccount = amount1.subtract(amount); String updateFromAccountSQL = "UPDATE ACCOUNT set amount = ? WHERE ACCOUNTID = ?"; PreparedStatement pps = connection.prepareStatement(updateFromAccountSQL); pps.setBigDecimal(1,updateFromAccount); pps.setString(2,fromAccount); pps.executeUpdate(); //7、更新转入账户余额 //7.1先查询转入账户余额 PreparedStatement ppsOut = connection.prepareStatement(fromAccountSQL); ppsOut.setString(1,outAccount); ResultSet resultSet1 = ppsOut.executeQuery(); BigDecimal amount2 = null; while (resultSet1.next()){ amount2 = resultSet1.getBigDecimal("amount"); } //7.2余额与转入额相加 BigDecimal updateOutAccount = amount2.add(amount); PreparedStatement ppsOutAccount = connection.prepareStatement(updateFromAccountSQL); //ppsOutAccount.setBigDecimal(1,updateOutAccount); ppsOutAccount.setString(2,outAccount); ppsOutAccount.executeUpdate(); //8、提交事务 connection.commit(); //9、关闭连接 connection.close(); } catch (SQLException e) { e.printStackTrace(); } } }
2021年09月14日
208 阅读
1 评论
0 点赞
2021-08-18
“葵花宝典”——持续更新
{collapse}{collapse-item label="两种动态代理的区别?" }回答要点:1、动态代理的作用2、动态代理的特点3、动态代理的分类4、两种动态代理的使用要求1、不改变原代码的情况下,增强方法2、动态代理的特点,字节码随用随创建,随用随加载3、4、基于接口的动态代理(JDK动态代理):由JDK官方提供要求:被代理类最少实现一个接口涉及类:Proxy创建代理对象的方法:newProxyInstance方法中的参数:ClassLoader:类加载器。 负责加载代理对象的字节码。 和被代理对象使用相同的类加载器。固定写法。 Class[]:字节码数组。 负责让生成的代理对象具有和被代理对象相同的方法。写什么要看被代理对象是一个接口还是一个实现类。 如果是一个接口: new Class[]{接口字节码} 如果是一个实现类:XXX.getClass().getInterfaces();固定写法 InvocationHandler:一个接口,需要我们提供该接口的实现。 作用是用于对方法增强。 增强的代码,谁用谁写。写的是一个接口的实现类。 通常是一个匿名内部类,但是不绝对。 基于子类的动态代理(CGlib动态代理):由cglib第三方提供要求:需要导入cglib的坐标。被代理类不能是最终类。(不能被final修饰)涉及类:Enhancer创建代理对象的方法:create方法中的参数:Class:字节码对象。 用于加载代理对象字节码的。 写的是被代理对象的字节码。是固定写法Callback:如何代理。提供增强代码的。它是个接口,需要自己写实现。 该接口没有方法,需要用它的子接口MethodInterceptor 应用场景1、基于aop思想的方法增强。2、自定义连接池中,实现Connection的close方法还回池中操作3、解决tomcat8.5以下全站中文乱码,get和post两种提交方式的。在get方式需要对以下三个方法增强,用于解决乱码: String value = getParameter(String name); String[] value = getParameterValues(String name); Map<String,String> map = getParameterMap();{/collapse-item}{collapse-item label="IOC和DI的关系" }1、技术介绍2、应用场景IOC(控制反转)全称为: Inverse of Control。从字面上理解就是控制反转了,将在自身对象中的一个内置对象的控制反转,反转后不再由自己本身的对象进行控制这个内置对象的创建,而是由第三方系统去控制这个内置对象的创建。简单来说就是把本来在类内部控制的对象,反转到类外部进行创建后注入,不再由类本身进行控制,这就是IOC的本质。DI(依赖注入)全称为Dependency Injection,意思是自身对象中的内置对象是通过注入的方式进行创建。IOC和DI的关系ioc就是容器,di就是注入这一行为,那么di 确实就是ioc的具体功能的实现。而ioc则是di 发挥的平台和空间。所以说,ioc和 di即是相辅相成的搭档。最重要的是,他们都是为了实现解耦而服务的。DI是如何实现的?依赖注入可以通过setter方法注入(设值注入)、构造器注入和接口注入三种方式来实现,Spring支持setter注入和构造器注入,通常使用构造器注入来注入必须的依赖关系,对于可选的依赖关系,则setter注入是更好的选择,setter注入需要类提供无参构造器或者无参的静态工厂方法来创建对象。应用场景例如:在生产实际中,一个类中会调用其他类的方法,一般情况(原始情况)下直接new创建类对象,但这种方法是需要开发人员自行维护二者的依赖关系,也就是说当依赖关系发生改变时需要修改代码并重新构建整个系统,这对公司业务而言是有损失的(重启需暂停服务,对用户而言难以接受)。这个时候就出现了IOC这一技术,如果通过一个容器来管理这些对象以及对象的依赖关系,则只需要在类A中定义好用于关联接口B的方法(接口B通过构造器或setter方法注入),将类A和接口B的实现类C放入容器中,通过对容器的配置来实现二者的关联。{/collapse-item}{collapse-item label="怎么对IOC容器的配置,管理IOC容器中的对象bean"}通过 Spring 提供的 IOC容器可以将对象间的依赖关系交由 Spring 进行控制,避免硬编码所造成的过度程序耦合。一是通过xml配置文件的方式配置bean(使用标签)二是通过注解的方式配置bean(常用注解@Controller、@Service、@Repository、@Component)@Autowired注解,可以实现自动装配@Autowired private BookService bookservice;//等同于bookservice = ioc.getbean(BookService.class)依赖注入原理: (1)先按类型找: 找到一个就赋值; 没找到就报错; (2)找到多个按照属性名作为id名继续匹配–>找到就装配,找不到还是报错Tips:1)加@Qualifier表示用指定的变量名作为id名去找组件而非属性名了;2)默认通过@Autowired注解实现自动装配是一定要找到的,所有情况下找不到就会报错;3)@Autowired注解的required属性指定某个属性允许找不到还不报错@Autowired(required = false) private BookService bookservice;4)如果对一个数组或集合类型标注自动装配,则自动匹配类型兼容的所有bean组件5)@autowired和@resource都能实现自动装配,区别:@autowired–只能在spring框架里用@resource–j2ee的标准,扩展性更强Spring之IOC容器创建bean对象的XML配置总结{/collapse-item}{collapse-item label="Git使用重点" }Git版本控制,版本管理git本地仓库 删除文件git checkout . #本地所有修改的。没有的提交的,都返回到原来的状态git stash #把所有没有提交的修改暂存到stash里面。可用git stash pop恢复。git reset --hard HASH #返回到某个节点,不保留修改。git reset --soft HASH #返回到某个节点。保留修改原文地址:http://www.pizida.com/git-rm.html{/collapse-item}{collapse-item label="EL与JSTL" }学习EL与JSTL不然首先要创建JSP,在JSP中:1、JSP指令 格式:<%@ 指令名称 属性名1=属性值1 属性名2=属性值2 ... %> - page:contentType -> 等同于response.setContentType() import -> 导包 errorPage -> 当前页面发生异常后跳转到指定页面 isErrorPage -> 标识当前页面是否是错误页面,默认为false。设置为true时,可以使用内置对象exception - include:设置包含某页面 - taglib:导入资源2、JSP内置对象 在JSP页面中不需要创建,直接使用的对象;一共有9个 - pageContext 当前页面共享数据,还可以获取其他八个内置对象 - request 一次请求访问的多个资源(转发) - session 一次会话的多个请求间 - application 所有用户间共享数据 - response 响应对象 - page 当前页面(Servlet)的对象 - out 输出对象,数据输出到页面上 - config Servlet的配置对象 - exception 异常对象3、EL - EL运算符:empty应用于判断字符串、集合、数组对象是否为null并且长度是否为0 ${empty list} - 获取值 **el表达式只能从域对象中获取值** 语法:${域名称.键名} 从指定域中获取指定键的值 - 对象属性的获取,其实将对象的getter和setter方法去掉前面的get和set, 然后大写字母变小写获得的值就是就可以用EL表达式${对象.属性}即可获得属性值 {gird column="2" gap="15"}{gird-item}{/gird-item}{gird-item}{/gird-item}{/gird}4、JSTL - JSP标准标签库; 用于简化和替换JSP页面上的java代码 - 首先需要导入jstl相关jar包 - 引入标签库:taglib指令 <%@ taglib uri="" %> - 常用JSTL标签 1、if :相当于java中的if 2、choose :相当于java中的witch 3、foreach :相当于java中的for循环{/collapse-item}{/collapse}
2021年08月18日
81 阅读
0 评论
0 点赞
1
2
3
4