Spring @Configuration 注解

@Configuration 标注在类上,相当于把该类作为 spring 的 xml 配置文件中的 <beans>,作用为:配置 spring 容器(context)

package com.test.spring.support.configuration;

@Configuration
public class TestConfiguration {
    public TestConfiguration(){
        System.out.println("spring 容器启动初始化......");
    }
}

相当于

<?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:context="http://www.springframework.org/schema/context" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
    xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:util="http://www.springframework.org/schema/util" xmlns:task="http://www.springframework.org/schema/task" xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
        http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.0.xsd
        http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd
        http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.0.xsd" default-lazy-init="false">

</beans>

测试

package com.test.spring.support.configuration;

public class TestMain {
    public static void main(String[] args) {

        //@Configuration 注解的 spring 容器加载方式,用 AnnotationConfigApplicationContext 替换 ClassPathXmlApplicationContext
        ApplicationContext context = new AnnotationConfigApplicationContext(TestConfiguration.class);

        // 如果加载 spring-context.xml 文件:
        //ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
    }
}

2018-07-11 spring , annotation , notes , spring-boot

Java 查漏补缺之 Class.isAssignableFrom() 和 instanceof 区别

Class.isAssignableFrom() 是用来判断一个类 Class1 和另一个类 Class2 是否相同或是另一个类的超类 superclass 或接口 superinterface。

调用方式: X.class.isAssignableFrom(Y.class)

调用者和参数都是 java.lang.Class 类型。上面例子,如果返回为 true,则表示 X 是 Y 的超类或者接口,Y 可以是一个类也可以是一个接口。

instanceof 是用来判断一个对象实例是否是一个类或接口的或其子类子接口的实例。

调用方式:o instanceof TypeName

第一个参数是对象实例名,第二个参数是具体的类名或接口名

延伸

Class 中还有一个相关的方法 isInstance() ,这个方法用来检查实例

MyClass.class.isInstance(obj)

如果返回 true,则表示 obj 是类 MyClass 的实例或者子类实例。

public class NewMain
{
    public static void main(String[] args)
    {
        NewMain nm = new NewMain();
        nm.doit();
    }

    public void doit()
    {
        A myA = new A();
        B myB = new B();
        A[] aArr = new A[0];
        B[] bArr = new B[0];

        System.out.println("b instanceof a: " + (myB instanceof A));
        System.out.println("b isInstance a: " + A.class.isInstance(myB));
        System.out.println("a isInstance b: " + B.class.isInstance(myA));
        System.out.println("b isAssignableFrom a: " + A.class.isAssignableFrom(B.class));
        System.out.println("a isAssignableFrom b: " + B.class.isAssignableFrom(A.class));
        System.out.println("bArr isInstance A: " + A.class.isInstance(bArr));
        System.out.println("bArr isInstance aArr: " + aArr.getClass().isInstance(bArr));
        System.out.println("bArr isAssignableFrom aArr: " + aArr.getClass().isAssignableFrom(bArr.getClass()));
    }

    class A
    {
    }

    class B extends A
    {
    }
}

结果是:

b instanceof a: true
b isInstance a: true
a isInstance b: false
b isAssignableFrom a: true
a isAssignableFrom b: false
bArr isInstance A: false
bArr isInstance aArr: true
bArr isAssignableFrom aArr: true

reference


2018-07-10 java , linux , object , class

Maven 插件

Maven 本质上是一个插件框架,它的核心并不执行任何具体的构建任务,而是将所有任务都交给插件来完成,例如编译源代码是由 maven-compiler-plugin 完成的。进一步说,每个任务对应了一个插件目标(goal),每个插件会有一个或者多个目标,例如 maven-compiler-plugin 的 compile 目标用来编译位于 src/main/java/ 目录下的主源码,testCompile 目标用来编译位于 src/test/java/ 目录下的测试源码。

maven-source-plugin

[[maven-source-plugin]] 打包插件,会根据当前的源码文件创建 jar 包。默认情况下 jar 文件会在项目 target 目录下。

如果没有进行特殊配置,maven 会按照标准接口查找和处理各种类型文件。一个标准的 maven 项目

├── project.iml
├── pom.xml
├── src
│   ├── main
│   │   ├── java
│   │   └── resources
│   └── test
│       ├── java
│       └── resources
└── target
    ├── classes
    │   └── info
    ├── generated-sources
    │   └── annotations
    ├── generated-test-sources
    │   └── test-annotations
    └── test-classes

src/main/javasrc/test/java 中的所有 *.java 文件都会在 Maven 的 compile 和 test-compile 阶段被编译,结果会分别放到 target/classestarget/test-classes 目录中。

src/main/resourcessrc/test/resources 这两个目录的文件也会被复制到 target/classestarget/test-classes 目录中。打包插件默认会将 target/classes 中的所有内容打包到 jar 包或者 war 包中。

如果想要 deploy 阶段跳过 sources.jar ,可以在命令中使用:1

-Dmaven.source.skip

如果可以修改 POM,可以使用如下配置不生成 sources.jar 文件:

  <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-source-plugin</artifactId>
    <version>2.2.1</version>
    <configuration>
      <skipSource>true</skipSource>
    </configuration>
  </plugin>

maven-archetype-plugin

Archetype 插件允许用户从模板中创建 Maven 项目,该插件需要 Java 6 及以上版本。2

maven-compiler-plugin

compiler 插件是最常用到的一个,定义 build 的 Java 版本,编译 Java 代码。

pom 文件定义,默认的 source 和 target 都是 1.5。

<project>
  [...]
  <build>
    [...]
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.7.0</version>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
          <fork>true</fork>
          <verbose>true</verbose>
          <encoding>UTF-8</encoding>
        </configuration>
      </plugin>
    </plugins>
    [...]
  </build>
  [...]
</project>

maven-dependency-plugin

maven-dependency-plugin 是处理依赖相关的插件,该插件有很多 goal,常用的比如 mvn dependency:treemvn dependency:analyze 等等。该插件方便用来排查依赖相关的问题。

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-dependency-plugin</artifactId>
    <version>2.8</version>
</plugin>

surefire 是 maven 的单元测试的插件,默认情况下 surefire 会执行文件名以 Test 开头或者结尾的测试用例,或者以 TestCase 结尾的用例。

pom 文件中定义,需要 Maven 2.2.1 or 3.x, and JDK 1.6 or higher

<project>
  [...]
  <build>
    <pluginManagement>
      <plugins>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>2.22.0</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
  [...]
</project>

在 mvn 的生命周期中 ,mvn test 时会调用该插件。如果要在 build 时跳过 test 可以定义

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.22.0</version>
    <configuration>
        <skip>true</skip>
    </configuration>
</plugin>

排除某些类

<configuration>
  <skip>true</skip>
  <excludes>
    <exclude>**/T</exclude>
  </excludes>
</configuration>

另外如果要添加或者排除某些目录可以参考官网 的例子。

如果要在开发过程中执行 test,可以

mvn -Dtest=TestCircle test   # test 表示当前测试方法所在的测试类,不需要扩展名

maven-enforcer-plugin

在一个稍大一点的组织或团队中,你无法保证所有成员都熟悉 Maven,那他们做一些比较愚蠢的事情就会变得很正常,例如给项目引入了外部的 SNAPSHOT 依赖而导致构建不稳定,使用了一个与大家不一致的 Maven 版本而经常抱怨构建出现诡异问题。maven-enforcer-plugin 能够帮助你避免之类问题,它允许你创建一系列规则强制所有使用者遵守,包括设定 Java 版本、设定 Maven 版本、禁止某些有漏洞的依赖、禁止 SNAPSHOT 依赖等等。只需要在一个父 POM 配置规则,然后各项目继承,当规则遭到破坏的时候,Maven 就会无法编译并抛出相应的错误。除了标准的规则之外,你还可以扩展该插件,编写自己的规则。maven-enforcer-plugin 的 enforce 目标负责检查规则,它默认绑定到[[Maven 生命周期]]的 validate 阶段。

使用如下定义

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-enforcer-plugin</artifactId>
  <version>1.3.1</version>
</plugin>

enforcer 插件有两个 goal,第一个是 display-info, 使用 mvn enforcer:display-info 会显示当前环境信息

[INFO] Maven Version: 3.3.9
[INFO] JDK Version: 1.8.0_131 normalized as: 1.8.0-131
[INFO] OS Info: Arch: amd64 Family: unix Name: linux Version: 4.4.0-116-generic
[INFO]

第二个也是最主要的 goal,就是 mvn enforcer:enforce 用来检查依赖。enforce goal 和 maven 生命周期 validate 阶段绑定,也就意味着,如果要执行 enforcer:enforce 可以通过 mvn validate 来触发。

如果使用 Maven > 3.1.1enforcer 1.4.1 可以使用 @ 语法来执行 特定 id 的 execution。

mvn enforcer:enforce@no-duplicate-declared-dependencies

configuration vs executions

插件的配置中能直接看到插件的 configuration 配置和 executions 中的 configuration 配置,这两个 configuration 配置有什么区别呢?在 executions 中的配置和 maven 的生命周期绑定,而外层插件的 configuration 是全局的配置。

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-enforcer-plugin</artifactId>
    <version>1.0.1</version>
    <executions>
      <execution>
        <id>convergence</id>
        <goals>
          <goal>enforce</goal>
        </goals>
        <configuration>
          <rules>
            <dependencyConvergence/>
          </rules>
          <fail>false</fail>
        </configuration>
      </execution>
    </executions>
    <configuration>
        <rules>
            <AlwaysPass />
        </rules>
        <fail>true</fail>
    </configuration>
</plugin>

如果要执行 executions 中的规则,则需要运行 mvn validate 来触发,而在定义在外层 configuration 中的规则,通过 mvn enforcer:enforce 就能够执行。

内置的规则

enforcer 插件自带了很多规则 下面就列举比较常用的几个。

dependencyConvergence 规则 可以保证 maven 以来的包都使用同一个版本。

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-enforcer-plugin</artifactId>
  <version>3.0.0-M2</version>
  <executions>
    <execution>
      <id>convergence</id>
      <configuration>
        <rules>
          <dependencyConvergence/>
        </rules>
      </configuration>
      <goals>
        <goal>enforce</goal>
      </goals>
    </execution>
  </executions>
</plugin>

单独执行该 goal 可以使用 mvn enforcer:enforce@convergence

bannedDependencies 用来禁用某依赖

<execution>
  <!-- mvn enforcer:enforce@ban-dependencies -->
  <id>ban-dependencies</id>
  <goals>
    <goal>enforce</goal>
  </goals>
  <configuration>
    <rules>
      <bannedDependencies>
        <!-- 是否检查依赖传递 -->
        <searchTransitive>false</searchTransitive>
        <excludes>
          <exclude>org.apache.maven</exclude>
          <exclude>org.apache.maven:badArtifact</exclude>
          <exclude>*:badArtifact</exclude>
          <!--<exclude>com.google.code.gson</exclude>-->
        </excludes>
        <includes>
          <!--only 1.0 of badArtifact is allowed-->
          <include>org.apache.maven:badArtifact:1.0</include>
        </includes>
        <message>检查这些依赖</message>
      </bannedDependencies>
    </rules>
    <fail>true</fail>
  </configuration>
</execution>

excludes 中包含一些列禁止的 artifacts,格式是 groupId[:artifactId][:version][:type][:scope][:classifier] 除了 groupId 其他都是可选的,可以使用通配符

  • org.apache.maven
  • org.apache.maven:badArtifact
  • org.apache.maven:artifact:badVersion
  • org.apache.maven:*:1.2 (exclude version 1.2 and above, equivalent to [1.2,) )
  • org.apache.maven:*:[1.2] (explicit exclude of version 1.2)
  • org.apache.maven:::jar:test
  • ::*:jar:compile:tests
  • org.apache.:maven-:*

bannedPlugins 禁止使用某些插件。

更多的插件内置规则可以查看:http://maven.apache.org/enforcer/enforcer-rules/index.html

maven-release-plugin

可用于构建 release 版本项目,实现自动打 tag、递增版本号、分发 release 版本 jar 包至仓库。

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-release-plugin</artifactId>
    <version>2.5</version>
</plugin>

一般来说引入 maven release plugin 需要如下几步:

  • 在项目的 pom 文件中增加,无需添加 mvn-release-plugin 的依赖,因为它默认被包含于 maven 的 effective pom 中;
  • 检查自己的 maven 的 settings.xml 是否包含了私服的用户名密码;
  • 确保自己本地代码是在主分支,并且是最新的副本;
  • 执行 mvn release:prepare, 这时插件会扫描项目依赖查看是否有 SNAPSHOT, 是否存在未提交的文件,确定当前 release 的版本号和下一个迭代的版本号,插件会运行单元测试,并向 git 中提交两次 commit, 一次是 release 版本,一次是下一个迭代的版本。并将当前 release 版本打一个 tag 并提交到 git 上面去;
  • 执行 mvn release:perform, 插件会执行 mvn deploy 操作,并 clean 掉生成的缓存文件。

pom 设置:

<distributionManagement>
    <repository>
        <id>releases</id>
        <name>xxxx internal releases repository</name>
        <url>http://xxx.com/nexus/content/repositories/releases</url>
    </repository>
    <snapshotRepository>
        <id>snapshots</id>
        <name>xxxx internal snapshots repository</name>
        <url>http://xxx.com/nexus/content/repositories/snapshots</url>
    </snapshotRepository>
</distributionManagement>

<scm>
    <url>http://xxx.com/project</url>
    <connection>scm:git:git@xxx.com/project.git</connection>
    <developerConnection>scm:git:git@xxx.com/project.git</developerConnection>
    <tag>HEAD</tag>
</scm>

Apache Maven Checkstyle Plugin

[[maven-checkstyle-plugin]] 引入 pom

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-checkstyle-plugin</artifactId>
    <version>3.0.0</version>
    <executions>
        <execution>
            <id>validate</id>
            <phase>validate</phase>
            <configuration>
                <configLocation>checkStyle.xml</configLocation>
                <encoding>UTF-8</encoding>
                <consoleOutput>true</consoleOutput>
                <failsOnError>true</failsOnError>
                <includeTestSourceDirectory>true</includeTestSourceDirectory>
            </configuration>
            <goals>
                <goal>check</goal>
            </goals>
        </execution>
    </executions>
</plugin>

用来检查代码规范。

jetty-maven-plugin

http://wiki.eclipse.org/Jetty/Feature/Jetty_Maven_Plugin

在进行 Web 开发的时候,打开浏览器对应用进行手动的测试几乎是无法避免的,这种测试方法通常就是将项目打包成 war 文件,然后部署到 Web 容器中,再启动容器进行验证,这显然十分耗时。为了帮助开发者节省时间,jetty-maven-plugin 应运而生,它完全兼容 Maven 项目的目录结构,能够周期性地检查源文件,一旦发现变更后自动更新到内置的 Jetty Web 容器中。做一些基本配置后(例如 Web 应用的 contextPath 和自动扫描变更的时间间隔),你只要执行 mvn jetty:run ,然后在 IDE 中修改代码,代码经 IDE 自动编译后产生变更,再由 jetty-maven-plugin 侦测到后更新至 Jetty 容器,这时你就可以直接测试 Web 页面了。需要注意的是,jetty-maven-plugin 并不是宿主于 Apache 或 Codehaus 的官方插件,因此使用的时候需要额外的配置 settings.xml 的 pluginGroups 元素,将 org.mortbay.jetty 这个 pluginGroup 加入。

更多的插件可以到这里查到:http://maven.apache.org/plugins/index.html

maven-git-commit-id-plugin

这不是一个官方的插件,实现的功能是能够在打包的二进制文件中关联 git 代码的版本。

地址:

maven 项目构建项目,打包成 jar 时,默认情况是 名字加上版本号,通过这个插件可以在命名的时候再增加一个 git 版本号,比如

com-einverne-api-1.0.0-SNAPSHOT-b31229dd.jar


<build>
    <plugins>
        <plugin>
            <groupId>pl.project13.maven</groupId>
            <artifactId>git-commit-id-plugin</artifactId>
            <version>2.2.4</version>
            <executions>
                <execution>
                    <goals>
                        <goal>revision</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <verbose>true</verbose>
                <generateGitPropertiesFile>true</generateGitPropertiesFile>
                <injectAllReactorProjects>true</injectAllReactorProjects>
                <!-- git 描述, JGIT 提供 -->
                <gitDescribe>
                    <!-- 是否生成描述属性 -->
                    <skip>false</skip>
                    <!-- 提交操作未发现 tag 时,仅打印提交操作 ID,-->
                    <always>false</always>
                    <!-- 提交操作 ID 显式字符长度,最大值为:40; 默认值:7;
                        0 代表特殊意义;后面有解释;
                    -->
                    <abbrev>7</abbrev>
                    <!-- 构建触发时,代码有修改时(即"dirty state"), 添加指定后缀;默认值:"";-->
                    <dirty>-dirty</dirty>
                    <!--always print using the "tag-commits_from_tag-g_commit_id-maybe_dirty" format, even if "on" a tag.
                        The distance will always be 0 if you're "on" the tag.
                    -->
                    <forceLongFormat>false</forceLongFormat>
                </gitDescribe>
            </configuration>
        </plugin>
    </plugins>
</build>

说明:

  • dotGitDirectory 默认值为 ${project.basedir}/.git,表示 .git 文件夹路径,可以自定义 ${project.basedir}/../.git
  • failOnNoGitDirectory 默认值:true,.git 文件夹未找到时,构建是否失败;若设置 true, 则构建失败;若设置 false, 则跳过执行该目标

更多详细的设置可以参考这里

appassembler-maven-plugin

Mojo Appassembler

主要作用是将 Java 程序打包成单一可执行程序,以往编写单一的可执行的 Java 程序可能非常复杂。The Application Assembler Plugin 是一个用来生成直接启动 Java 程序脚本的 Maven 插件。所有的依赖和构建都会被放到一个定义好的 assemble 目录中,所有的依赖都会在脚本中添加到 classpath 中。

Goals

  • appassembler:assemble Assembles the artifacts and generates bin scripts for the configured applications.
  • appassembler:create-repository Creates an appassembler repository.
  • appassembler:generate-daemons Generates JSW based daemon wrappers.

Usages

如果直接使用 mvn 命令

mvn archetype:generate \
  -DarchetypeGroupId=org.apache.maven.archetypes \
  -DarchetypeArtifactId=maven-archetype-quickstart \
  -DgroupId=com.mycompany.app \
  -DartifactId=my-app \
  -Dversion=1.0-SNAPSHOT

或者定义到 pom 文件中

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>appassembler-maven-plugin</artifactId>
        <version>1.10</version>
        <configuration>
          <programs>
            <program>
              <mainClass>com.mycompany.app.App</mainClass>
              <id>app</id>
            </program>
          </programs>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

然后使用生成的脚本

$ mvn package appassembler:assemble
...
$ sh target/appassembler/bin/app
Hello World!

其他更多的控制选项可以参考这里

reference


2018-07-09 maven , java , build , management , build-tool

Spring boot 笔记

Spring aims to make developer challenges easy, like creating web applications, working with databases, securing applications, and microservices.

Spring Boot builds on top of Spring to make Spring even easier with simplified dependency management, automatic configuration, and runtime insights.

笔记

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;


@SpringBootApplication()        // 开启组件扫描和自动配置
public class Application {
    public static void main(String[] args) {
        try {
            SpringApplication.run(Application.class, args); // 负责启动引导程序
        } catch (Throwable ex) {
            ex.printStackTrace();
        }
    }
}

@SpringBootApplication 将三个有用的注解组合在了一起

  • Spring 的 @Configuration : 标明该类使用 Spring 基于 Java 的配置。
  • Spring 的 @ComponentScan : 启用组件扫描,这样你写的 Web 控制器类和其他组件才能被自动发现并注册为 Spring 应用程序上下文里的 Bean。
  • Spring Boot 的 @EnableAutoConfiguration 开启了 Spring Boot 自动配置

配置

application.properties

只要它存在就会被加载,Spring 和应用程序代码都能获取其中的属性

maven plugin

<plugin>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-maven-plugin</artifactId>
  <configuration>
    <mainClass>com.einverne.api.Application</mainClass>
  </configuration>
</plugin>

构建插件的主要功能是把项目打包成一个可执行的超级 JAR (uber-JAR), 包括把应用程序的所有依赖打入 JAR 文件内,并为 JAR 添加一个描述文件,其中的内容能让你用 java -jar target.jar 来运行应用程序。


2018-07-06 spring-boot , spring , java , web

phpMyAdmin 登录无反应问题记录

今天遇到一个神奇的错误,登录 phpMyAdmin 无论如何都登录不进去,开始还以为密码记错了,但是再输入错误密码时,会显示错误的提示

#1045 - Access denied for user 'root'@'localhost' (using password: YES)

而在在我确认密码没有问题时,phpMyAdmin 的表现就像是登录成功过了,但是却没有跳转。

然后为了解决这个问题,修改过 php.ini 中的 session.save_path ,修改了 session 文件的 权限 chmod 777 /path/to/session ,重启了 apache,尝试了所有的可能,直到看到一个解决方法是,因为浏览器的问题,换别的浏览器就能登录,我果断用 Vivaldi 试了一下,果然可以。

于是这个奇怪的问题就解决了,然后我清理了一下 Chrome 的 Cookie ,Session 然后就好了。至于是 phpMyAdmin 的问题还是 Chrome 的问题我也就无从得知了。奇怪了。


2018-07-03 php , phpmyadmin , mysql , root , password , browser

Java 查漏补缺:线程间通信

wait notify

方法 wait() 是让当前执行代码的线程等待,wait() 是 Object 类的方法。在调用 wait() 方法前,线程需要获得对象级别锁,只能在同步方法或者同步块中调用,执行 wait() 方法后,当前线程释放锁。如果调用时没有持有适当锁,会抛出 IllegalMonitorStateException 异常。

方法 notify() 也需要在同步方法或者同步块中调用,同样调用前也需要获取对象锁。如果调用没有持有适当的锁,也会抛出 IllegalMonitorStateException 异常。notify() 方法永安里通知可能处于等待该对象锁的其他线程,如果有多个线程等待,由线程规划器随机挑选一个呈现 wait 状态的线程,发出 notify 通知,并使它获得该对象的对象锁。notify() 方法执行后,当前线程不会马上释放该对象锁,wait 状态的线程也不能马上获取该对象锁,需要等执行 notify() 方法的线程将程序执行完,也就是退出 synchronized 代码块之后,当前线程才会被释放锁。

如果发出 notify() 操作时没有处于阻塞状态的线程,命令会被忽视。

wait() 方法使线程停止运行,而 notify() 使停止的线程继续运行。

join

如果主线程要等待子线程执行完成之后结束,可以使用 join() 方法。

join(long) 在内部使用 wait(long) 方法实现,所以会释放锁。而 Thread.sleep(long) 方法不会释放锁。

ThreadLocal

ThreadLocal 类主要解决的就是每个线程绑定自己的值,每个线程都有自己的私有数据。

公平锁 vs 非公平锁

公平锁线程在获取锁的顺序是按照线程加锁的顺序来分配的,即先来先得 FIFO ,而非公平锁就是获取锁的抢占机制,随机获的锁。

ReentrantReadWriteLock

类 [[ReentrantLock]] 是完全互斥排他,同一时间只有一个线程能够执行 ReentrantLock 后任务,这样的方式保证了实例变量的线程安全,但是效率低下。所以 JDK 中提供了另外一个读写锁 ReentrantReadWriteLock ,在某些不需要操作实例变量的方法中,可以使用读写锁 ReentrantReadWriteLock 来提升方法的效率。

读写锁表示有两个锁,一个读相关锁,也成为共享锁,另一个是写相关的锁,也叫排它锁。读锁之间不排斥,写锁与其他锁互相排斥。多个线程可以同时读操作,但是同一时刻只允许有一个线程写操作。


2018-07-02 java , linux , thread , synchronized

通过 WebDAV 连接 NextCloud

NextCloud 支持 WebDAV 协议,用户可以完全通过 WebDAV 来连接并同步文件。虽然官方还是推荐 使用客户端来同步文件,不过如果要临时访问 NextCloud 上的文件,使用 WebDAV 方式还是很便捷的。

假设 NextCloud 的地址是 example.com 这个,那么在 Linux 下,一般文件管理器中有连接到服务器的选项在其中,填入服务器 IP 地址,访问端口,然后访问地址填写 /remote.php/dav/files/[USERNAME] 输入该用户的用户名和密码即可访问该用户的所有文件列表。

如果要直接在文件管理器中访问,可以尝试使用如下的协议地址:

dav://[USERNAME]@[SERVER_IP]:[PORT]/remote.php/dav/files/[USERNAME]

在 Linux 下还能通过命令行访问,具体可参考下面的链接。

NextCloud 通过 webdav 同步 Joplin

同步地址,其中 ip, port, username,需要替换:

http://ip:port/remote.php/dav/files/username/JoplinSync

reference


2018-07-01 nextcloud , webdav , dav , sync , files , linux

每天学习一个命令:使用 jpegoptim 和 optipng 优化压缩图片

今天突然遇到一个问题,使用手机拍摄的照片 5+M 体积巨大,但是网易要求的图片大小需要小于 1M,所以就有了压缩图片的需求。记得很久以前使用过 TinyPNG 这个服务,但是唯一一点不好的就是无法脱离他在线的服务,需要把照片上传到他的网站上才能进行压缩。虽然有 tinypng-cli 这个命令行的工具,但其实也是依赖在线服务的。所以就找了一下,然后发现了 jpegoptimoptipng 这两个工具。

jpegoptim

安装

apt-get install jpegoptim

使用

jpegoptim file.jpg

然后使用 du -h file.jpg 查看文件大小。使用这种方式 jpegoptim 会尽量使用无损失的压缩方式,所以几乎看不出区别。而如果想要指定大小,比如想要压缩到 500k 大小,可以

jpegoptim --size=500k file.jpg

而如果要批量压缩,在目录中 jpegoptim *.jpg 即可。

如果要将 EXIF 信息移走可以添加参数

jpegoptim --strip-exif file.jpg

结合 find

find images_folder/ *.{jpeg,jpg} -exec jpegoptim {} \;

optipng

安装

apt-get install optipng

使用

optipng file.png

结合 find

find images_folder/ *.png -exec optipng {} \;

mozjpeg

优化 jpeg 压缩算法

命令行版本

安装方式:

npm install -g mozjpeg

mozjpeg --help

在 macOS 下优化压缩图片

macOS 下有一个不错的工具叫做 imageOptim

安装:

brew install imageoptim-cli

优化当前目录中的 PNG:

imageoptim --imagealpha '**/*.png'

优化 JPG:

imageoptim --jpegmini '**/*.jpg' '**/*.jpeg'

reference


2018-06-30 compress , optimize , linux , command , jpeg , png

Java 查漏补缺之位运算符

今天在看 Kafka 源码 LongSerializer 时看到将 Long 型转 byte 数组的实现,感觉需要补习一下位运算符 (bitwise operator),所以有了这篇。

Java 提供的位运算符有:

  • 左移 ( << )、右移 ( >> )
  • 无符号左移 ( <<<)、无符号右移 ( >>> )
  • 位与 ( & )
  • 位或 ( | )
  • 位非 ( ~ )
  • 位异或 ( ^ )

除了位非 ( ~ ) 是一元操作符外,其它的都是二元操作符。

与或非,亦或 就不说了。

左移

左移 1 位 (<<1) ,值乘 2,以后依次乘 2.

右移

右移 1 位 (>>1),值除以 2。

无符号右移

对于负数,二进制高位为 1,所以一个 int 类型的 -5 二进制表示是

1111 1111 1111 1111 1111 1111 1111 1011

对于负数,使用右移 >> 时,则高位补 1,而对于无符号右移,则高位补 0 。

延伸

由位运算操作符衍生而来的有:

  • &= 按位与赋值
  • |= 按位或赋值
  • ^= 按位非赋值
  • >>= 右移赋值 a »= 1 就是 a = a » 1
  • <<= 赋值左移
  • >>>= 无符号右移赋值

+= 一样,理解为 a += 1 也就是 a = a + 1。


2018-06-26 java , bit , operator , notes

Python Linux 系统管理与自动化运维读书笔记

小工具

HTTP Server

python -m SimpleHTTPServer

python -m http.server

JSON 格式化

echo '<json string>' | python -m json.tool

验证第三方库安装

python -c "import paramiko"

pip 用法

源码安装

python setup.py install

pip 子命令

子命令 说明
search 搜索 pip search flask
install 安装 pip install flask==0.8 , pip install -r requirements.txt
uninstall 卸载
show 查看包详情
check pip 9.0.1 之后提供,检查包是否完整
list 列出已安装
freeze 导出已安装包列表 pip freeze > requirements.txt
completion 生成命令补全配置 pip completion -z >> ~/.zshrc && source ~/.zshrc

加速 pip 安装

下载时指定

pip install -i https://pypi.douban.com/simple/ flask

或者创建 ~/.pip/pip.conf ,写入

[global]
index-url = https://pypi.douban.com/simple/

2018-06-24 python , linux , notes , deploy

电子书

本站提供服务

最近文章

  • Dinox 又一款 AI 语音实时转录工具 前两天介绍过 [[Voicenotes]],也是一款 AI 转录文字的笔记软件,之前在调查 Voicenotes 的时候就留意到了 Dinox,因为是在小红书留意到的,所以猜测应该是国内的某位独立开发者的作品,整个应用使用起来也比较舒服,但相较于 Voicenotes,Dinox 更偏向于一个手机端的笔记软件,因为他整体的设计中没有将语音作为首选,用户也可以添加文字的笔记,反而在 Voicenotes 中,语音作为了所有笔记的首选,当然 Voicenotes 也可以自己编辑笔记,但是语音是它的核心。
  • 音流:一款支持 Navidrom 兼容 Subsonic 的跨平台音乐播放器 之前一篇文章介绍了Navidrome,搭建了一个自己在线音乐流媒体库,把我本地通过 [[Syncthing]] 同步的 80 G 音乐导入了。自己也尝试了 Navidrome 官网列出的 Subsonic 兼容客户端 [[substreamer]],以及 macOS 上面的 [[Sonixd]],体验都还不错。但是在了解的过程中又发现了一款中文名叫做「音流」(英文 Stream Music)的应用,初步体验了一下感觉还不错,所以分享出来。
  • 泰国 DTV 数字游民签证 泰国一直是 [[Digital Nomad]] 数字游民青睐的选择地,尤其是清迈以其优美的自然环境、低廉的生活成本和友好的社区氛围而闻名。许多数字游民选择在泰国清迈定居,可以在清迈租用廉价的公寓或民宿,享受美食和文化,并与其他数字游民分享经验和资源。
  • VoceChat 一款可以自托管的在线聊天室 VoceChat 是一款使用 Rust(后端),React(前端),Flutter(移动端)开发的,开源,支持独立部署的在线聊天服务。VoceChat 非常轻量,后端服务只有 15MB 的大小,打包的 Docker 镜像文件也只有 61 MB,VoceChat 可部署在任何的服务器上。
  • 结合了 Google 和 AI 的对话搜索引擎:Perplexity AI 在日本,因为 SoftBank 和 Perplexity AI 开展了合作 ,所以最近大量的使用 Perplexity ,这一篇文章就总结一下 Perplexity 的优势和使用技巧。