0%

安装Gradle

  1. java环境必须
  2. 下载Gradle,其中binary-only为单独的安装包,complete为文档加安装包,然后解压
  3. 在path中添加系统环境变量F:\xuan\gradle-4.7\bin
  4. 执行gradle -v检查是否安装成功

使用Gradle

  1. 进入一个Gradle项目,或者拉取一个 git clone git@github.com:gradle/gradle-build-scan-quickstart.git

Gradle build scans使用

Build Scans是用于开发和维护Gradle构建的重要工具。它为你提供了构建的详细信息,并为你识别构建环境、依赖或性能上存在的问题,同时可以帮你更全面地理解并提升构建过程,也便于与他人的合作。

大概理解就是该插件能把构建过程的数据分享出去,方便让别人查看分析构建信息。

  1. 进入项目跟目录执行构建扫描命令./gradlew build --scan,有可能会提示Do you accept these terms?是否接受许可协议,输入yes即可,成功后日志会输出一个链接类似https://scans.gradle.com/s/z5i6rmnpd4sbu,访问该链接就可以查看构建日志信息了(有的打开也许需要邮箱)。

  2. 上面虽然得到了链接,但是可以直接在build.gradle添加相关配置信息,构建扫描插件build-scan,为了将构建扫码发布到https://gradle.com/terms-of-service需要接受协议

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    plugins {
    id 'com.gradle.build-scan' version '1.13.2' //如果是低版本一定要放到其他插件前面
    }
    //配置扫描发送地址,以及同意协议
    buildScan {
    termsOfServiceUrl = 'https://gradle.com/terms-of-service'
    termsOfServiceAgree = 'yes' //同意协议
    tag 'xuan test' //打标签
    //项目的地址,这里需要是http地址,用git开头的地址编译报错
    link 'GitHub','https://github.com/gradle/gradle-build-scan-quickstart'
    }
  3. 执行gradle build -Dscan运行得到链接,然后访问即可

Creating New Gradle Builds

初始化gradle 项目
1
2
3
4
5
6
7
8
9
10
11
12
13
mkdir gradle-demo #创建项目目录
cd gradle-demo #进入项目目录
gradle init #初始化为gradle项目
#生成如下目录文件
.
├── build.gradle #项目配置脚本
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar #wrapper可执行jar
│ └── gradle-wrapper.properties #wrapper 配置文件
├── gradlew # unix systems(linux)系统下的脚本
├── gradlew.bat # window 下的脚本
└── settings.gradle #配置那些项目参与构建脚本
创建task任务名叫copy
1
2
3
4
task copy(type: Copy, group: "Custom", description: "复制src目录到dest目录") {
from "src" //前提需要有src目录
into "dest" //不需要创建,会自动创建
}

命令行执行./gradlew copy代表执行task里的copy任务

Gradle 应用插件

插件仓库

这里以base插件,base插件功能主要是打包为zip文件

1
2
3
4
5
6
7
8
9
10
plugins {   //这个代码快必须放顶部
id "base"
}
task copy(type: Copy, group: "Custom", description: "复制src目录到dest目录") {
from "src"
into "dest"
}
task zip(type: Zip, group: "Util", description: "压缩src目录文件") {
from "src"
}

执行./gradlew zip,然后在目录 .\build\distributions下就可以看到gradle-demo.zip文件了

常用命令
1
2
3
./gradlew tasks #查看可用的task任务
./gradlew zip --scan #结合scan执行zip,分析执行信息
./gradlew properties #查看可用的配置属性,类似环境变量(配置属性)的

build.gradle可以更改properties变量的值,但是不能新增,例如修改项目描述和版本号

1
2
3
//放在build.gradle文件中,但是不能放在plugins前面,因为plugins这个必须在顶部
description = "该项目为测试学习用"
version = "1.0"

手册

Gradle 命令手册

注意事项

不能在gradle项目的子目录执行gradle init

RecyclerView 基本使用

  1. 添加依赖v7包依赖,版本号和compileSdkVersion一致,不然报错

    1
    2
    implementation 'com.android.support:appcompat-v7:25.0.0'
    implementation 'com.android.support:recyclerview-v7:25.0.0'
  2. 在layout里添加RecyclerView布局的引用

    1
    2
    3
    4
    5
    <android.support.v7.widget.RecyclerView
    android:id="@+id/recyView1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    />
  3. 添加一个item的布局

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    <?xml version="1.0" encoding="utf-8"?>
    <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
    android:id="@+id/recyNumberPhone"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="TextView"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

    <TextView
    android:id="@+id/recyNumberName"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="TextView"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintTop_toTopOf="parent" />
    </android.support.constraint.ConstraintLayout>
  4. 创建适配器RecyNumberAdapter继承 RecyclerView.Adapter

    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
    public class RecyNumberAdapter extends RecyclerView.Adapter<RecyNumberAdapter.NumberHolder>{
    private Context context; //上下文
    private List<Map<String,String>> mDatas; //数据源

    public RecyNumberAdapter(Context context,List<Map<String,String>> mDatas){
    //构造方法传入数据
    this.context=context;
    this.mDatas=mDatas;
    }

    @Override
    public NumberHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    // 填充布局item
    View view = LayoutInflater.from(context).inflate(R.layout.recy_number, null);
    NumberHolder holder = new NumberHolder(view);
    return holder;
    }

    @Override
    public void onBindViewHolder(final NumberHolder holder, final int position) {
    //绑定view
    holder.recyNumberName.setText(String.valueOf(mDatas.get(position).get("name"))); //用户名
    holder.recyNumberPhone.setText(String.valueOf(mDatas.get(position).get("id"))); //电话号码
    }

    @Override
    public int getItemCount() {
    return mDatas.size(); //数据长度
    }
    //类部类
    class NumberHolder extends RecyclerView.ViewHolder{
    //获取item子布局的控件
    private TextView recyNumberPhone;
    private TextView recyNumberName;
    public NumberHolder(View view){
    super(view);
    recyNumberPhone=view.findViewById(R.id.recyNumberPhone);
    recyNumberName=view.findViewById(R.id.recyNumberName);
    }
    }
    }
  5. 调用测试

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    RecyclerView recyNumberView=findViewById(R.id.personalNumber);
    // 设置布局管理器有横向,表格等等
    GridLayoutManager gridLayoutManager=new GridLayoutManager(this,4);
    recyNumberView.setLayoutManager(gridLayoutManager);
    //测试数据
    List<Map<String,String>> datas=new ArrayList<>();
    Map<String,String> map=new HashMap<>();
    map.put("id","13432861290");
    map.put("name","张三");
    datas.add(map);
    //设置适配器
    recyNumberView.setAdapter(new RecyNumberAdapter(this,datas));
  6. 添加点击事件,在RecyNumberAdapter里添加

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    private OnItemClickListener mItemClickListener;
    //item的回调接口
    public interface OnItemClickListener {
    void onItemClick(View view, int Position);
    }
    //定义一个设置点击监听器的方法
    public void setOnItemClickListener(OnItemClickListener itemClickListener) {
    this.mItemClickListener = itemClickListener;
    }
    //在覆写的onBindViewHolder方法中添加
    //如果设置了回调,则设置点击事件
    if (mItemClickListener != null) {
    holder.itemView.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
    mItemClickListener.onItemClick(holder.itemView, position);
    }
    });
    }
  7. 调用的时候只需要recyNumberAdapter.setOnItemClickListener即可

dubbo官网/dubbo仓库地址

  1. 环境准备新建一个空的project,分别三个model

    model名称 model类型 说明
    common_interface java类型空项目 用于存放服务提供者和服务消费者的公共接口,避免写两次而已
    consumer springboot空项目 消费者服务依赖common_interface model
    provider springboot空项目 提供者服务依赖common_interface model
  2. 上面项目建立好,model依赖关系加好之后,下面开始引入dubbo框架

  3. 两个springboot项目都引入dubbo依赖这里用的gradle,仓库地址可以去dubbo仓库地址查看

    1
    compile group: 'com.alibaba', name: 'dubbo', version: '2.6.1'
  4. common_interface项目中添加一个接口类

    1
    2
    3
    4
    5
    package exxk.dubbo.commonimpl;

    public interface DemoService {
    String sayHello(String name);
    }
  5. provider项目中实现DemoService接口,在java目录下新建impl包,并添加一个DemoServiceImpl实现类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    package exxk.dubbo.provider.impl;

    import exxk.dubbo.commonimpl.DemoService;

    public class DemoServiceImpl implements DemoService{
    @Override
    public String sayHello(String name) {
    return "hello"+ name;
    }
    }
  6. provider项目中resource目录下添加一个dubbo配置文件dubbo-provider.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <?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:dubbo="http://code.alibabatech.com/schema/dubbo"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
    <!--该提供者服务名称-->
    <dubbo:application name="dubbo-provider"/>
    <!--组播模式的注册中心,推荐用zookeeper-->
    <dubbo:registry address="multicast://224.5.6.7:1234"/>
    <!--暴露的端口服务-->
    <dubbo:protocol name="dubbo" port="20880"/>
    <!--声明暴露服务公共接口类-->
    <dubbo:service interface="exxk.dubbo.commonimpl.DemoService" ref="demoService"/>
    <!--提供者实现类-->
    <bean id="demoService" class="exxk.dubbo.provider.impl.DemoServiceImpl"/>
    </beans>
  7. provider项目中java目录下添加一个dubbo 启动类Provider.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    package exxk.dubbo.provider;

    import org.springframework.context.support.ClassPathXmlApplicationContext;

    public class Provider {
    public static void main(String[] args) throws Exception {
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
    new String[]{"dubbo-provider.xml"}); //读取dubbo配置文件
    context.start();
    //按任何键推出
    System.in.read();
    }
    }
  8. 上面的服务提供者基本完成,然后启动服务提供者,直接运行Provider.java静态方法即可

  9. consumer项目中resource目录下添加一个dubbo配置文件dubbo-consumer.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <?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:dubbo="http://code.alibabatech.com/schema/dubbo"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
    <dubbo:application name="dubbo-consumer"/>
    <!--注册中心地址(多播)-->
    <dubbo:registry address="multicast://224.5.6.7:1234"/>
    <dubbo:reference id="demoService" interface="exxk.dubbo.commonimpl.DemoService"/>
    </beans>
  10. consumer项目中java目录下添加一个dubbo 启动类Consumer.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package exxk.dubbo.consumer;

import exxk.dubbo.commonimpl.DemoService;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Consumer {
public static void main(String[] args) throws Exception{
ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext(
new String[]{"dubbo-consumer.xml"});
context.start();
DemoService demoService= (DemoService) context.getBean("demoService");
String hello= demoService.sayHello("world");
System.out.print(hello);
}
}
  1. 启动consumer服务消费者项目,这里用debug模式运行Consumer.java在里面打断点,主要是日志不好找,因此debug

基于springboot优化启动类

在springboot启动类添加dubbo启动,去掉默认的dubbo启动

  1. 最直接的是把dubbo启动类的配置xml内容直接放到application启动类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    @SpringBootApplication
    public class ProviderApplication {
    public static void main(String[] args) {
    SpringApplication.run(ProviderApplication.class, args);
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
    new String[]{"dubbo-provider.xml"}); //读取dubbo配置文件
    context.start();
    //同步锁,保持该线程一直运行
    synchronized (Provider.class){
    while(true){
    try {
    Provider.class.wait();
    }catch (Exception e){
    System.out.print("synchronized===:"+e);
    }
    }
    }
    }
    }
  2. 这种当然不优雅,可以直接把xml配置文件配置到注解@ImportResource

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    @SpringBootApplication
    public class ProviderApplication {
    public static void main(String[] args) {
    SpringApplication.run(ProviderApplication.class, args);
    //同步锁,保持该线程一直运行
    synchronized (Provider.class){
    while(true){
    try {
    Provider.class.wait();
    }catch (Exception e){
    System.out.print("synchronized===:"+e);
    }
    }
    }
    }
    }
  3. 代码是少了,但是据说,dubbo包含web框架,会让springboot当成web程序运行,这里自定义SpringApplicationBuilder禁用web

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    @SpringBootApplication
    public class ProviderApplication {
    public static void main(String[] args) {
    ApplicationContext context= new SpringApplicationBuilder()
    .sources(ProviderApplication.class)
    .web(WebApplicationType.NONE) //禁用web服务
    .run(args);
    //同步锁,保持该线程一直运行
    synchronized (Provider.class){
    while(true){
    try {
    Provider.class.wait();
    }catch (Exception e){
    System.out.print("synchronized===:"+e);
    }
    }
    }
    }
    }
  4. 同步锁线程也换种方式实现,利用同步工具类CountDownLatch,该工具类的大概作用就是有等待指定线程(数)执行完了,再执行

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    @SpringBootApplication
    @ImportResource({"classpath:dubbo-provider.xml"})
    public class ProviderApplication {

    @Bean
    public CountDownLatch closeLatch() {
    return new CountDownLatch(1);
    }

    public static void main(String[] args) throws InterruptedException {
    //SpringApplication.run(ProviderApplication.class, args);
    ApplicationContext context= new SpringApplicationBuilder()
    .sources(ProviderApplication.class)
    .web(WebApplicationType.NONE) //禁用web服务
    .run(args);

    CountDownLatch closeLatch=context.getBean(CountDownLatch.class);
    closeLatch.await(); //等待所有子线程完成
    }
    }
  5. 最后删除旧的启动器,到此大功告成

Spring Boot干货系列总纲 阅读笔记

配置文件解析

默认src/main/resources目录下,两种格式application.propertiesapplication.yml

自定义属性,在配置文件application.properties定义diy.name="hello" ,在使用的地方加上注解

1
2
@Value("${diy.name}")
private String name;

自定义配置类,需要在springboot入口类添加@EnableConfigurationProperties({ConfigBean.class})

1
2
3
4
5
6
@ConfigurationProperties(prefix = "diy")
public class ConfigBean {
private String name;
private String want;
// 省略getter和setter
}

有bugbug。。。。。。。。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*
批量修改文件编码
*/
console.log("开始修改文件编码");
var fs= require("fs");
//---------同步----------------
var files =fs.readdirSync("./");
files.forEach(function (filename){
var stats=fs.statSync(filename);
console.log(filename+"文件状态",stats);
//if(stats.isDirectory()) filename +='/';
//process.
});
//----------异步------------------
fs.readdir("./",function(err,files){
var len=files.length;
var file=null;
for(var i=0;i<len;i++){
file=files[i];
console.log("读取文件",file);
var stats=fs.stat(file);
console.log("文件状态",stats);
}
})
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
#!/usr/bin/env node
/*
批量修改文件编码
*/
console.log("开始修改文件编码");
var fs = require("fs");
var jschardet = require("jschardet");
var path = "node_modules/jschardet";
readDir(path);
/*
修改文件编码
*/
function readDir(dirPath) {
console.log("目录:",dirPath);
var files = fs.readdirSync(dirPath);
files.forEach(function(file) {
var filepath = dirPath +"/"+file;
var stats = fs.statSync(filepath);
//console.log(filename+"文件状态",stats);
if (stats.isFile()) {
var buff = fs.readFileSync(file);
var info = jschardet.detect(buff);
console.log(filename + "文件编码", info);
} else if (stats.isDirectory()) {
console.log("目录" + filepath);
readDir(filepath);
}
});
}

安装运行

  1. tomcat 下载apache-tomcat-8.5.12-windows-x64然后解压安装

  2. 编辑tomcat/conf/server.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <!-- 9301为自定义端口号,默认为8080 -->  
    <Connector port="9301" protocol="HTTP/1.1"
    connectionTimeout="20000"
    redirectPort="8443" />
    <!--复制或者修改host-->
    <Host name="localhost" appBase="webapps"
    unpackWARs="true" autoDeploy="true">
    <!-- 新加context /app为url上下文,app为webapps下的app.war包-->
    <Context path="/app" docBase="app" reloadable="false"
    source="org.eclipse.jst.jee.server:tsj-spring"/>
    <Valve className="org.apache.catalina.valves.AccessLogValve"
    directory="logs" prefix="localhost_access_log" suffix=".txt"
    pattern="%h %l %u %t &quot;%r&quot; %s %b" />
    </Host>
  3. 运行tomcat,window下双击打开bin目录下的startup.bat启动app.war

  4. 访问为ip:9301/app/

tomcat远程自动部署

  1. 安装tomcat8.5.x

  2. 修改tomcat配置文件conf/server.xml

    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
    <?xml version="1.0" encoding="UTF-8"?>
    <Server port="8019" shutdown="SHUTDOWN">
    <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
    <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
    <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
    <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
    <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
    <GlobalNamingResources>
    <Resource name="UserDatabase" auth="Container"
    type="org.apache.catalina.UserDatabase"
    description="User database that can be updated and saved"
    factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
    pathname="conf/tomcat-users.xml" />
    </GlobalNamingResources>
    <Service name="Catalina">
    <Connector port="8005" protocol="HTTP/1.1"
    connectionTimeout="20010"
    redirectPort="8454" />
    <Connector port="8021" protocol="AJP/1.3" redirectPort="8454" />

    <Engine name="Catalina" defaultHost="H8005">

    <Realm className="org.apache.catalina.realm.LockOutRealm">
    <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
    resourceName="UserDatabase"/>
    </Realm>

    <Host name="H8005" appBase="webapps"
    unpackWARs="true" autoDeploy="true">

    <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
    prefix="localhost_access_log" suffix=".txt"
    pattern="%h %l %u %t &quot;%r&quot; %s %b" />

    </Host>
    </Engine>
    </Service>
    </Server>
  3. 修改conf/tomcat-users.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <tomcat-users xmlns="http://tomcat.apache.org/xml"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://tomcat.apache.org/xml tomcat-users.xsd"
    version="1.0">

    <role rolename="admin-gui"/>
    <role rolename="admin-script"/>
    <role rolename="manager-gui"/>
    <role rolename="manager-script"/>
    <role rolename="manager-jmx"/>
    <role rolename="manager-status"/>
    <user username="admin" password="ideaadmin" roles="manager-gui,manager-script,manager-jmx,manager-status,admin-script,admin-gui"/>

    </tomcat-users>
  4. 启动tomcat执行./bin/catalina.sh start | tail -f ./logs/catalina.out,如果要修改启动内存,启动前提前修改catalina.sh

  5. conf/Catalina/h8005添加manager.xml

    1
    2
    3
    4
    <Context privileged="true" antiResourceLocking="false"
    docBase="${catalina.home}/webapps/manager">
    <Valve className="org.apache.catalina.valves.RemoteAddrValve" allow="^.*$" />
    </Context>
  6. 然后打开http://ip:8005/manager,输入用户名`admin`密码`idaeadmin`,进入之后保留**manager**删除其他所有applications

  7. 修改maven的.m2/setting.xml文件

    1
    2
    3
    4
    5
    6
    7
    8
    <servers>
    <server>
    <id>innerCs</id>
    <username>admin</username>
    <password>ideaadmin</password>
    </server>
    ...
    </servers>
  8. 在项目里面的pom.xml添加

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    <build>
    <finalName>InnerCS</finalName>
    <plugins>
    ...
    <plugin>
    <groupId>org.apache.tomcat.maven</groupId>
    <artifactId>tomcat7-maven-plugin</artifactId>
    <version>2.2</version>
    <configuration>
    <url>http://ip:8005/manager/text</url>
    <path>/</path>
    <uriEncoding>UTF-8</uriEncoding>
    <server>innerCs</server>
    </configuration>
    </plugin>
    </plugins>
    </build>
  9. 编译Lifecyle->clean->install部署lifecyle->deploy发布到maven私库

  10. 远程第一次部署plugins->tomcat7->deploy,会上传ROOT.war/webapps/并解压运行生成ROOT因为我配置的path为/所以是ROOT

注意事项
  1. 第九步时,如果时maven多模块项目,需要在父项目,添加上传依赖jar的地址

    1
    2
    3
    4
    5
    6
    7
    8
    <!-- 配置maven地址 -->
    <distributionManagement>
    <snapshotRepository>
    <id>nexus-snapshots</id>
    <name>Nexus Snapshot Repository</name>
    <url>http://192.168.101.200:8081/repository/maven-snapshots/</url>
    </snapshotRepository>
    </distributionManagement>
  2. 还需要在maven的setting.xml配置maven的用户名,才有权限上传

    1
    2
    3
    4
    5
    <server>
    <id>nexus-snapshots</id>
    <username>admin</username>
    <password>admin123</password>
    </server>
  3. 多模块项目需要在父级里面进行clean-install-deploy,注意勾选idea maven右侧菜单里面的Profiles,不然父级不知道编译那个子项目

常见问题

  1. tomcat启动时出现java.lang.IllegalArgumentException: Illegal character(s) in message header field: Pragma:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    java.lang.IllegalArgumentException: Illegal character(s) in message header field: Pragma:
    2019-02-20 17:40:47 331 - at sun.net.www.protocol.http.HttpURLConnection.checkMessageHeader(HttpURLConnection.java:511)
    2019-02-20 17:40:47 331 - at sun.net.www.protocol.http.HttpURLConnection.isExternalMessageHeaderAllowed(HttpURLConnection.java:481)
    2019-02-20 17:40:47 331 - at sun.net.www.protocol.http.HttpURLConnection.setRequestProperty(HttpURLConnection.java:2895)
    2019-02-20 17:40:47 331 - at sun.net.www.protocol.https.HttpsURLConnectionImpl.setRequestProperty(HttpsURLConnectionImpl.java:325)
    2019-02-20 17:40:47 331 - at mmo.common.utils.HttpUtils.sendPost(HttpUtils.java:28)
    2019-02-20 17:40:47 331 - at com.surelive.app.server.service.QQGroupApiService$1.run(QQGroupApiService.java:169)
    2019-02-20 17:40:47 331 - at com.surelive.app.server.entities.ext.QueueThreadHandle.run(QueueThreadHandle.java:52)
    2019-02-20 17:40:47 331 - at com.surelive.app.server.service.QueueThreadPoolServer$1.run(QueueThreadPoolServer.java:26)
    2019-02-20 17:40:47 331 - at java.lang.Thread.run(Thread.java:748)

    解决:执行env检查环境变量中是否有JAVA_HOME,没有设置好这些环境变量

ZSH

zsh (Mac 系统自带,无需安装)。

安装Oh-My-Zsh管理zsh的配置工具 sh -c "$(curl -fsSL https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"

常用功能

配置文件cat ~/.zshrc

命令历史记录功能ctrl+r

历史记录存放在cat ~/.zsh_history

ctrl+r搜索命令历史记录,!!执行上一条命了

补全TAB

命令别名alias

~/.zshrc中添加alias name=command即可

查看所有命令别名alias

插件

git

iterm2 修改配色

Solarized Dark Higher Contrast

在这里找到自己https://github.com/mbadolato/iTerm2-Color-Schemes/tree/master/schemes喜欢的

然后保存文件,双击安装,然后color->color下来框选择自己安装的

item2 安装powerlevel9k主题

1
2
3
git clone https://github.com/bhilburn/powerlevel9k.git ~/.oh-my-zsh/custom/themes/powerlevel9k #下载主题
vim ~/.zshrc #编辑配置文件设置主题ZSH_THEME="powerlevel9k/powerlevel9k",去用户名添加 DEFAULT_USER="your user name"
git clone https://github.com/supermarin/powerline-fonts.git #下载字体

安装字体,双击/Monaco/Monaco for Powerline.otf文件安装字体

参考

https://gist.github.com/qhh0205/5570934d25a627dd9e9629a8ceeb415c

Compos file 版本3以上

Compos file v3官网

build

只支持单机运行

1
2
3
4
5
6
7
8
version: "3" #指明版本3,docker stack需要版本3以上
services:
webapp:
build: #集群部署会忽略构建镜像,stack只支持提前构建好镜像
context: ./dir #构建上下文路径
dockerfile: Dockerfile-alternate #构建文件
args: #构建参数
buildno: 1

configs (swarm模式)

只支持集群模式运行,只需要在单机上引用,会自动在其他节点创建配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
version: "3.3"
services:
redis:
image: redis:latest
deploy:
replicas: 1
configs:
- my_config
- my_other_config
configs:
my_config:
file: ./my_config.txt
my_other_config:
external: true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
version: '3'
services:
nginx:
image: harbor.iexxk.dev/library/web-gate-id-v3:S3.2.1
restart: unless-stopped
ports:
- "88:80"
# volumes:
# - ./config/default.conf:/etc/nginx/conf.d/default.conf 不支持集群使用configs代替
configs:
- source: nginx_config
target: /etc/nginx/conf.d/default.conf
env_file:
- ./config/nginx.env #env也支持集群
configs:
nginx_config:
file: ./config/default.conf #支持集群

command

会覆盖dockerfile里面的命令例如:

1
2
command: bundle exec thin -p 3000 #docker-compose.yml覆盖dockerfile的命令
command: ["bundle", "exec", "thin", "-p", "3000"] #dockerfile等效于上面的命令

deploy(swarm模式)

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
version: '3.2'
services:
redis:
image: redis:alpine
labels: #容器上的标签
com.example.description: " containers 上的标签"
deploy:
mode: global #可选global(全局模式,每台机一个)/replicated(多台)
replicas: 6 #部署的总数量
update_config:
parallelism: 2
delay: 10s
restart_policy:
condition: on-failure
endpoint_mode: vip #可选vip(默认)/dnsrr,主要是IP啥的
labels: #服务器上的标签
com.example.description: "This label will appear on the web service"
placement: #指定部署的信息
constraints:
- node.role == manager #只在管理节点运行
- engine.labels.operatingsystem == ubuntu 14.04
preferences:
- spread: node.labels.zone
resources: #资源限制
limits:
#cpu个数0.5指一个cpu的50%,2.00指2个cpu(猜测99%)其中docker stats命令可以查看cpu使用率和内存使用,其中cpu使用率是指单核的使用率,也就是如果cpus设置为2,使用率200%就代表2个核都使用达到了100%
cpus: '0.50'
memory: 50M
reservations:
cpus: '0.25'
memory: 20M
restart_policy: #重启策略
condition: on-failure
delay: 5s
max_attempts: 3
window: 120s
update_config: #更新配置策略
parallelism: 2
delay: 10s
order: stop-first
depends_on: #依赖的服务模块,在db启动才启动服务,但是不能保证db启动完,如果要设置启动顺序见
#https://docs.docker.com/compose/startup-order/
- db
- redis
healthcheck: #健康检查
test: ["CMD", "curl", "-f", "http://localhost"]
interval: 1m30s
timeout: 10s
retries: 3
start_period: 40s
logging: #日志服务
driver: syslog
options:
syslog-address: "tcp://192.168.0.42:123"
volumes: #自定义挂在卷
- type: volume
source: mydata
target: /data
volume:
nocopy: true
- type: bind
source: ./static
target: /opt/app/static
ports: #自定义端口
- target: 80
published: 8080
protocol: tcp
mode: host

问题

错误services.nginx.ports.0 must be a string or number是因为自定义端口,只支持3.2以上

1
2
3
4
5
6
version: '3.2' 
ports: #自定义端口
- target: 80
published: 8080
protocol: tcp
mode: host

将一个html H5网页打包成一个apk

  1. MainActivity初始化一个webview,启用JavaScript脚步,然后设置自定义Webviewclient,接着就是设置些缓存等,这些非必须,最后终哟啊的一个方法就是加载webview.loadUrl这里添加你的h5链接,如果是本地,直接是路径就可以了,最后设置setContentView(webview);,里面还加了webview返回是网页的返回,到返回完了,在返回就是退出应用提示dialog

    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
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    public class MainActivity extends AppCompatActivity {
    private WebView webview;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    //实例化WebView对象
    webview = new WebView(this);
    //设置WebView属性,能够执行Javascript脚本
    webview.getSettings().setJavaScriptEnabled(true);
    //重写WebViewClient
    webview.setWebViewClient(new WebViewClientDiy(this));
    //额外的设置,缓存等
    webview.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
    webview.getSettings().setJavaScriptEnabled(true);
    webview.getSettings().setDomStorageEnabled(true);
    webview.getSettings().setAppCacheMaxSize(1024 * 1024 * 8);
    String appCachePath = this.getApplicationContext().getCacheDir()
    .getAbsolutePath();
    webview.getSettings().setAppCachePath(appCachePath);
    webview.getSettings().setAllowFileAccess(true);
    webview.getSettings().setAppCacheEnabled(true);


    //加载需要显示的网页
    //webview.loadUrl("file:///android_asset/index.html");//显示本地网页
    webview.loadUrl("http://dev.clothes.yaokexing.com/mobile/welcome");//显示远程网页
    //设置Web视图
    setContentView(webview);
    }

    @Override//设置回退
    public boolean onKeyDown(int keyCode, KeyEvent event) {
    if ((keyCode == KeyEvent.KEYCODE_BACK) && webview.canGoBack()) {
    webview.goBack(); //goBack()表示返回WebView的上一页面
    return true;
    } else {
    AlertDialog exitDialog = new AlertDialog.Builder(this).create();
    exitDialog.setTitle("系统提示");
    exitDialog.setMessage("你确定要退出吗");
    exitDialog.setButton(DialogInterface.BUTTON_POSITIVE, "确定", new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which) {
    finish();
    }
    });
    exitDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "取消", new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which) {
    Toast.makeText(MainActivity.this, "欢迎回来", Toast.LENGTH_SHORT).show();
    }
    });
    //onKeyListener用于设置监听手机back键的操作
    exitDialog.setOnKeyListener(new DialogInterface.OnKeyListener() {
    @Override
    public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_BACK) {
    return true;// false时dialog会消失
    }
    return true;
    }
    });
    exitDialog.show();
    }
    return super.onKeyDown(keyCode, event); //退出程序
    }
  2. 自定义WebViewClient类,里面主要覆写shouldOverrideUrlLoading这个方法,因存在老版本和新版本,所以把判断是否用webview加载独立方法出来,一些第三方登录需要用系统的浏览器,所以这里就起到作用了

    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
    public class WebViewClientDiy extends WebViewClient {
    Context context;
    public WebViewClientDiy(Context context) {
    this.context=context;
    }

    @SuppressWarnings("deprecation")
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
    final Uri uri = Uri.parse(url); //老版本
    return handleUri(uri,view);
    }

    @TargetApi(Build.VERSION_CODES.N)
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
    final Uri uri = request.getUrl(); //新版本
    return handleUri(uri,view);
    }

    private boolean handleUri(final Uri uri,WebView view) {
    Log.i("测试", "Uri =" + uri);
    if (uri.toString().contains("weixin://wap/pay?")){
    Intent intent = new Intent();
    intent.setAction(Intent.ACTION_VIEW);
    intent.setData(Uri.parse(uri.toString()));
    context.startActivity(intent); //系统默认加载方法
    return true;
    }
    view.loadUrl(uri.toString()); //调用webview的加载方法
    return true;
    }
    }
  3. AndroidMainfest.xml添加网络权限<uses-permission android:name="android.permission.INTERNET"/>,到此基本就可以用了

  4. 现在做一些优化关闭标题栏,修改AndroidMainfest.xml的them为自定义样式android:theme="@style/AppTheme.NoActionBar">

    改样式需要在styles.xml添加如下内容

    1
    2
    3
    4
    5
    <style name="AppTheme.NoActionBar" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="windowActionBar">false</item>
    <item name="windowNoTitle">true</item>
    <item name="android:windowFullscreen">false</item>
    </style>

Cordova环境搭建

混合开发环境,Cordova提供了js和原生API的调用接口,通过插件,我们可以实现例如拍照,扫码等操作; 并且提供了静态文件转换成APP的功能。

步骤:

  1. 安装nodejs

  2. 安装cordova执行npm install -g cordova

  3. 在工作空间目录执行cordova create MyApp创建一个Cordova项目

  4. 切换进入刚刚创建的项目跟目录cd MyApp

  5. cordova platform查看该项目可用的平台和已安装的平台

    1
    2
    3
    4
    5
    6
    7
    8
    Installed platforms:
    android 7.0.0
    Available platforms:
    browser ~5.0.1
    ios ~4.5.4
    osx ~4.0.1
    windows ~5.0.0
    www ^3.12.0
  6. 如果没有安装,添加一个平台cordova platform add android

  7. 添加Android sdk系统环境变量三个

    1
    2
    3
    JAVA_HOME:C:\Program Files\Java\jdk1.8.0_131
    ANDROID_HOME:F:\xuan\sdk
    PATH:%ANDROID_HOME%\tools;%ANDROID_HOME%\platform-tools;
  8. 然后执行cordova requirements检查相关环境,如果失败,环境变量配置有问题,这里需要注意,环境变量配置了要重启终端,才会生效

    1
    2
    3
    4
    5
    6
    7
    Android Studio project detected

    Requirements check results for android:
    Java JDK: installed 1.8.0
    Android SDK: installed true
    Android target: installed android-27,android-26,android-25,android-24,android-23,android-22
    Gradle: installed C:\Program Files\Android\Android Studio\gradle\gradle-4.1\bin\gradle
  9. 然后执行cordova build android编译项目

  10. 然后执行cordova run android运行项目,需要连接Android设备,如果是模拟器执行cordova emulate android

  11. chrome浏览器调试chrome://inspect/#devices

Cordova 实例

调用相机demo

  1. 顺序执行下面的命令

    1
    2
    3
    4
    cordova create cameraDemo #创建cameraDemo相机demo
    cd cameraDemo #进入工程目录
    cordova platform add android #添加android
    cordova plugin add cordova-plugin-camera #添加相机组件
  2. 在上面步骤生成应用的目录,修改www目录下的index.html

    1
    2
    3
    4
      <script type="text/javascript" src="cordova.js"></script>
    <script type="text/javascript" src="js/index.js"></script>
    <button id = "camera">调用相机</button>
    <button id = "wxlogin">微信</button>
  3. 修改www\js目录下的index.js

    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
    var app = {
    initialize: function() {
    document.addEventListener('deviceready', this.onDeviceReady.bind(this), false);
    document.getElementById("camera").addEventListener("click", this.wxloginEvent);
    },
    wxloginEvent: function(){
    // alert('触发了');
    navigator.camera.getPicture(onSuccess,onFail,{
    quality: 50,
    destinationType: Camera.DestinationType.DATA_URL
    });

    function onSuccess(imageData) {
    var image = document.getElementById('myImage');
    image.src = "data:image/jpeg;base64," + imageData;
    }
    function onFail(message) {
    alert('Failed because: ' + message);
    }
    },
    onDeviceReady: function() {
    this.receivedEvent('deviceready');
    },
    receivedEvent: function(id) {
    var parentElement = document.getElementById(id);
    var listeningElement = parentElement.querySelector('.listening');
    var receivedElement = parentElement.querySelector('.received');

    listeningElement.setAttribute('style', 'display:none;');
    receivedElement.setAttribute('style', 'display:block;');
    console.log('Received Event: ' + id);
    }
    };
    window.onload=function(){ //注意添加此方法包裹,不然会还没加载就调用方法而报错
    app.initialize();
    }
  4. 执行cordova run android运行项目,需要连接Android设备

Cordova 加载远程HTML(修改了html,一定要清除app的缓存)

上面已经成功创建了一个相机demo,但是他是读取的本地网页,下面记录访问远程html

先分析下生成的目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|cordovaDemo  //跟目录
├─hooks
├─node_modules //模块
│ ├─cordova-android //Android设备
│ ├─cordova-plugin-barcodescanner
│ ├─cordova-plugin-compat
│ └─cordova-plugin-whitelist
├─platforms //设备目录
│ └─android //Android源码
│ ├─assets
│ │ └─www //html资源目录,要在远程运行需要把此目录放到远程目录,改相应的配置文件
│ ├─CordovaLib //Cordova 依赖
│ ├─res
│ ├─xml─config.xml //修改里面的<content src="url地址" /> 默认是index.html
│ └─src //Android java源文件
├─plugins //插件目录
│ ├─cordova-plugin-barcodescanner
│ ├─cordova-plugin-compat
│ └─cordova-plugin-whitelist
├─res
└─www //网页源文件,修改之后,build(run)会改变android-assets
  1. 在上面相机demo运行完项目后,用as打开生成的Android的项目(.\platforms\android)

  2. 复制.\platforms\android\assets目录下的源码到服务器上运行,注意idea复制包名有可能丢失

  3. 修改Android项目下的res\xml\config.xml配置文件

    1
    2
    <!--<content src="index.html" />-->
    <content src="http://112." /> //服务器地址
  4. 如果要制作个android外壳,还需要解决webview跳出到浏览器的问题,修改CordovaLib\java\...\engine\SystemWebViewClient下的

    1
    2
    3
    4
    5
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
    // return parentEngine.client.onNavigationAttempt(url); //注释这句
    return false; //添加这句,具体可以详查webview的使用
    }
  5. 直接在as里面运行Android项目,如果用run android运行会覆盖修改掉的东西

config.xml文件详解

在新建的cordova项目跟目录下的config.xml将被复制到各个平台配置文件下,不会被更改,Android的是app/platforms/android/res/xml/config.xml

下面个详细介绍下里面的配置

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
<?xml version='1.0' encoding='utf-8'?>
<widget id="通用包名" android-packageName="Android包名" version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
<!--插件-->
<feature name="Whitelist">
<param name="android-package" value="org.apache.cordova.whitelist.WhitelistPlugin" />
<param name="onload" value="true" />
</feature>
<!--应用名称-->
<name>e想行空</name>
<!--应用图标,更改后卸载重装生效-->
<icon src="res/icon/logo.png" />
<!--应用描述-->
<description>
e想天开,天马行空!
</description>
<!--作者email,网站-->
<author email="exxk.lx@gmail.com" href="http://www.blog.iexxk.com">
e想行空
</author>
<!--加载h5的资源,默认index.html是本地资源-->
<content src="http://www.blog.iexxk.com" />
<!--允许哪些域可以和组件通信-->
<access origin="http://www.blog.iexxk.com/*" />
<!--允许哪些域通过webview打开-->
<allow-navigation href="http://www.blog.iexxk.com/*" />
<!--允许哪些域可以被打开-->
<allow-intent href="http://*/*" />
<allow-intent href="https://*/*" />
<allow-intent href="tel:*" />
<allow-intent href="sms:*" />
<allow-intent href="mailto:*" />
<allow-intent href="geo:*" />
<allow-intent href="market:*" />
<preference name="loglevel" value="DEBUG" />
</widget>

app签名

  1. 在项目跟目录新建一个build.json

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    {
    "android": {
    "debug": {
    "keystore": "./android.keystore",
    "storePassword": "android",
    "alias": "mykey1",
    "password" : "password",
    "keystoreType": ""
    },
    "release": {
    "keystore": "./android.keystore",
    "storePassword": "",
    "alias": "mykey2",
    "password" : "password",
    "keystoreType": ""
    }
    }
    }
  2. 复制android.keystore到项目跟目录

  3. 执行cordova build --release android然后生成platforms\android\app\build\outputs\apk\release\app-release.apk

文档资源

官网cordova

cordova plugins

常见问题

  1. 浏览器打开提示www/cordova.js net::ERR_FILE_NOT_FOUND此错误,但是打包Android不会出现

  2. 安装插件:

    1
    2
    3
    Plugin doesn't support this project's cordova-android version. cordova-android: 7.0.0, failed version requirement:
    <6.3.0
    Skipping 'cordova-plugin-compat' for android

    解决执行cordova platform rm androidcordova platform remove android然后安装cordova platform add android@6.2.0