■
EclipseJdt
EclipseJdt { //定义一个clos的数组 def clos = [] //定义fp这个文件参数(.factorypath文件) def fp = file('.factorypath') //把outputs的file定义为fp所指的文件 //这样如果.factorypath文件存在的时候,EclipseJdt就不会被重复执行 outputs.file fp clos += { //clos这个闭包的参数为fp.withWriter fp.withWriter { //定义domaJar这个参数,寻找配置中以doma-2开头 def domaJar = configurations.compile.find { it.name.startsWith('doma-2') } //增加了一行EXTJAR new groovy.xml.MarkupBuilder(it).factorypath() { factorypathentry(kind:'EXTJAR', id:domaJar, enabled:true, runInBatchMode:false) } } } //定义一个prefs的方法 def prefs = { name, contents -> //定义了一个文件的参数f def f = file(".settings/$name") //把contents的内容多行分解成string,赋值给f.text clos += { f.text = contents.stripMargin() } //输出内容到f这个参数定义的文件中去 outputs.file f } //调用prefs这个方法,org.eclipse.jdt.apt.core.prefs这个文件里面,写下面四行内容。 //第一行的“\”是什么意思→表示这行没有换行 prefs 'org.eclipse.jdt.apt.core.prefs', """\ |eclipse.preferences.version=1 |org.eclipse.jdt.apt.aptEnabled=true |org.eclipse.jdt.apt.genSrcDir=${aptDir} |org.eclipse.jdt.apt.reconcileEnabled=true |""" doLast { clos*.run() } }
增量式构建
为一个Task定义输入(inputs)和输出(outputs),在执行该Task时,如果它的输入和输出与前一次执行时没有变化,
那么Gradle便会认为该Task是最新的(日志会输出“UP-TO-DATE“),因此不会重复执行
task combineFileContent { def sources = fileTree('sourceDir') def destination = file('destination.txt') inputs.dir sources // 将sources声明为该Task的inputs outputs.file destination // 将destination声明为outputs doLast { destination.withPrintWriter { writer -> sources.each {source -> writer.println source.text } } } }
当首次执行combineFileContent时,Gradle会完整地执行该Task,但是紧接着再执行一次,命令行显示:
如果修改inputs(上述即sourceDir文件夹)中的任何一个文件或删除destination.txt,再次调用“gradle combineFileContent”时,该Task又会重新执行
在这个例子中inputs是目录,outputs是文件。
当然也可以inputs是文件,outputs是目录。
理解doLast
doLast意思是定义一个行为(映射Gradle中的Action类),放在当前task的最后,类似的,还有doFirst, 表示将定义的行为放在当前task最前面,例如
task hello { doLast{ println "Hello world" } doFirst{ println "I am xxx" } }
执行gradle hello, 将输出 "I am xxx" "Hello world"
■
Gradle refresh时产生的classpath错误
现象
选中项目⇒Gradle⇒refresh之后, ディフォルト出力フォルダ从“APIBot/bin/main”变成了“APIBot/bin/default”
Git上有一些内容可以参考
1. #3825(Closed)
Gradle从4.4版本开始,为每一个源文件目录使用一个单独的出力目录,
缺省情况下,这些出力目录都在bin目录下。
例子:
eclipse {
classpath.defaultOutputDir = file('build-eclipse')
}
虽然通过上述命令指定了缺省目录,但生成的classpath如下,编译的目标目录还是在bin下面
<classpathentry path="build-eclipse" kind="output"/> <classpathentry output="bin/main" kind="src" path="src/main/java"> <attributes> <attribute name="gradle_scope" value="main"/> <attribute name="gradle_used_by_scope" value="main,test,integrationTest"/> </attributes> </classpathentry> <classpathentry output="bin/main" kind="src" path="src/main/resources"> <attributes> <attribute name="gradle_scope" value="main"/> <attribute name="gradle_used_by_scope" value="main,test,integrationTest"/> </attributes> </classpathentry> <classpathentry output="bin/test" kind="src" path="src/test/java"> <attributes> <attribute name="gradle_scope" value="test"/> <attribute name="gradle_used_by_scope" value="test"/> </attributes> </classpathentry> <classpathentry output="bin/test" kind="src" path="src/test/resources"> <attributes> <attribute name="gradle_scope" value="test"/> <attribute name="gradle_used_by_scope" value="test"/> </attributes> </classpathentry>
defaultOutputDir正如其字面意思一样,是为那些没有指定目标目录的源文件准备的。
Gradle目前给每个目标目录指定了目标目录,因此defaultOutputDir应该没有什么机会被用到
解决方案
classpath { defaultOutputDir = file('build/classes/java/main') file.whenMerged { entries.each { source -> // This seems kludgy. If the second test is omitted, it fails processing a 'Project Dependency' entry if (source.kind == 'src' && source.toString().contains('output')) { source.output = 'build/classes/java/main' } } } }
其实想给所有源文件指定目标目录,更像是应该用outputBaseDir
outputBaseDir这个功能在Gradle的#3829票里面准备提供。
2. #4563(Closed)
内容和3825一样。因此被关闭
3. #3839(Open)
oehme提供了一个临时解决方案
classpath { file.whenMerged { entries.each { entry -> if (entry instanceof org.gradle.plugins.ide.eclipse.model.Output) { entry.path = entry.path.replace('bin', 'build/eclipse') } } } }
今后可能会提供一个永久解决方案
classpath {
outputBaseDir = 'build/eclipse'
}
apply plugin: 'java' apply plugin: 'eclipse' eclipse { classpath { file { //if you want to mess with the resulting XML in whatever way you fancy withXml { def node = it.asNode() node.appendNode('xml', 'is what I love') } //closure executed after .classpath content is loaded from existing file //从既存文件中载入.classpath的内容之后,这个闭包内容会被执行 //but before gradle build information is merged //但是gradle build information被merge之前。 // beforeMerged { classpath -> //you can tinker with the Classpath here } //closure executed after .classpath content is loaded from existing file //从既存文件中载入.classpath的内容之后,这个闭包内容会被执行(跟beforeMerged相同) //and after gradle build information is merged //但是gradle build information被merge之后。 whenMerged { classpath -> //you can tinker with the Classpath here } } } }
关键还是gradle build information的merge时间点,以及什么information。
从跑出来的一些例子看,这个information应该是指所有依赖关系
Gradle官方文档中关于defaultOutputDir的描述
缺省就是bin/default
所以一定要指定。
Groovy 中关于 eclipse plugin的一些理解
EclipseJdt
对Eclipse plugin的详细内容进行优化调节
首先看官方文档中例子
apply plugin: 'java' //要用eclipse插件,当然需要申明引入java插件 apply plugin: 'eclipse' //申明引入eclipse插件 //groovy语言的特点,都是用{}括起来的closure,针对eclipse这个closure进行配置 //引入了eclipse插件,自然就有了eclipse这个closure eclipse { //eclipse里的jdt,下面有3个属性可以设置 //1.file;2.sourceCompatibility;3.targetCompatibility jdt { //if you want to alter the java versions (by default they are configured with gradle java plugin settings): sourceCompatibility = 1.6 targetCompatibility = 1.5 file { //whenMerged closure is the highest voodoo //and probably should be used only to solve tricky edge cases. //the type passed to the closure is Jdt //closure executed after jdt file content is loaded from existing file //and after gradle build information is merged whenMerged { jdt //you can tinker with the Jdt here } //withProperties allows addition of properties not currently //modeled by Gradle withProperties { properties -> //you can tinker with the Properties here } } } }
属性的详细
file
这是一个PropertiesFileContentMerger类型(只读)
具体参照,EclipseJdt这个closure的file的closure内容 什么是只读
sourceCompatibility
定义了java源文件的java版本,缺省是project.sourceCompatibility
targetCompatibility
定义了生成java字节码文件的jvm版本,缺省是project.targetCompatibility
Script blocks file:Enables advanced configuration like affecting the way existing jdt file content is merged with gradle build information 可以进一步配置
file{}
Enables advanced configuration like affecting the way existing jdt file content is merged with gradle build information
The object passed to whenMerged{} and beforeMerged{} closures is of type Jdt
The object passed to withProperties{} closures is of type Properties
For example see docs for EclipseJdt
Groovy的Class
EclipseJdt
PropertiesFileContentMerger
DOMA的例子
eclipse { //it是Groovy的关键字,闭包内的唯一参数的隐式参数 //为什么不直接用等号? jdt.file.withProperties { it['org.eclipse.jdt.core.compiler.processAnnotations'] = 'enabled' } //classpath定义了有什么用,跟上面sourceCompatibility有什么区别? classpath { containers = [ 'org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8' ] } }
这里只是将eclipse的注解编译器打开
eclipseJdt { def clos = [] def fp = file('.factorypath') outputs.file fp clos += { fp.withWriter { def domaJar = configurations.compile.find { it.name.startsWith('doma-2') } new groovy.xml.MarkupBuilder(it).factorypath() { factorypathentry(kind:'EXTJAR', id:domaJar, enabled:true, runInBatchMode:false) } } } //定义一个prefs的方法 def prefs = { name, contents -> //定义了一个文件的参数f def f = file(".settings/$name") //把contents的内容多行分解成string,赋值给f.text clos += { f.text = contents.stripMargin() } //输出内容到f这个参数定义的文件中去 outputs.file f } //调用prefs这个方法,org.eclipse.jdt.apt.core.prefs这个文件里面,写下面四行内容。 //第一行的“\”是什么意思→表示这行没有换行 prefs 'org.eclipse.jdt.apt.core.prefs', """\ |eclipse.preferences.version=1 |org.eclipse.jdt.apt.aptEnabled=true |org.eclipse.jdt.apt.genSrcDir=${aptDir} |org.eclipse.jdt.apt.reconcileEnabled=true |""" doLast { clos*.run() } }
Spring+DOMA开发过程中遇到的问题
DOMA使用过程中,无法扫描到类的原因
困扰了好几天的问题终于解决了。 在一个最简单的DOMA例子中,一直发生下列错误
*************************** APPLICATION FAILED TO START *************************** Description: Field customerRepository in com.apibot.service.CustomerService required a bean of type 'com.apibot.dao.CustomerRepository' that could not be found. Action: Consider defining a bean of type 'com.apibot.dao.CustomerRepository' in your configuration.
感觉是Repository类一直没有被扫描到的缘故,试过了在前面博客里面介绍过的方法。
【关于Autowire自动注入的类】
增加了
@SpringBootApplication(scanBasePackages = { "com.apibot" })
或是
@ComponentScan("com.apibot")
但是没有效果,最后还是在官网找到了答案。
原因一:没有设置factory path,因此注解没有被解析。
问题是解决了,但原因还不是很明白,factory 里面设置的注解解析包,为什么要这么设置的原因,
留到今后寻找。
原因二:项目的缺省编译路径没有正确设置
错误路径:APIBot/bin/default
正确路径:APIBot/bin/main
这里也存在一个疑问没有明白,java和resource的路径虽然都已经是设置了,
但是DOMA的SQL没有使用这个路径,而是用了下面的缺省路径。
■
Spring Framework总结点滴
关于Autowire自动注入的类
Spring虽然会对有下面这些注解的类进行自动扫描,
@Component,@Controller,@Repository,@Service
但如果不在SpringApplication类相同目录下,是扫描不到的,
因此需要指定扫描目录。
扫描几种方法
对Application类的@SpringBootApplication注解指定扫描目录,
这样在这个目录以及这个目录的子目录里面类都会被扫描
@SpringBootApplication(scanBasePackages = { "com.apibot" })
对Application类增加@ComponentScan注解指定扫描目录
Spring+Doma进行数据库连接
DataSource的使用
Spring项目的application.yml一般会有下面一段关于DB的定义
spring: datasource: url: jdbc:postgresql://localhost:5432/apibot driver-class-name: org.postgresql.Driver username: apibot password: apibot platform: postgresql
Spring框架的DataSourceProperties类里面有以下定义:
@ConfigurationProperties(prefix = "spring.datasource") public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {
Spring框架会自动扫描spring.datasource的定义项
具体有哪些定义项可以使用,可以根据DataSourceProperties类的成员变量推断出来。
// --------------------------- // Doma用の設定 // --------------------------- @Autowired DataSourceProperties dataSourceProperties; //←自动注入DatasourceProperties DataSource realDataSource() { SimpleDataSource dataSource = new SimpleDataSource(); dataSource.setUrl(dataSourceProperties.getUrl()); dataSource.setUser(dataSourceProperties.getUsername()); dataSource.setPassword(dataSourceProperties.getPassword()); return dataSource; }
通过Autowired自动注入了datasourceProperties
生成一个SimplieDataSource类实例,从DataSourceProperites中取出DB连接所需要的参数,赋值给DataSource
在这个上面的例子中给url,user,password
@Bean DataSource dataSource() { /* TransactionAwareDataSourceProxyでラップしないとDomaのコネクションが Springの管理外になって実行時例外発生時にRollbackされない */ return new TransactionAwareDataSourceProxy( new Log4jdbcProxyDataSource( realDataSource())); }
@Bean的用法可以参考下方连接:
Doma的Transcation管理
Doma建立的DB连接,没有被TransactionAwareDataSourceProxy管理。
因此需要通过Wrapper,交给TransactionAwareDataSourceProxy管理。
也就是说,connection的建立,不是通过DataSource建立getconnection。
而是通过Log4jdbcProxyDataSource里的成员方法建立连接。
关于doma的连接需要进行Transaction管理一节,可以参考以下连接。
Warning
这是在测试环境下的,正式的生产环境还是用其他的jdbc。
@Bean Dialect dialect() { return new PostgresDialect(); } @Bean Config config() { return new Config() { @Override public DataSource getDataSource() { return dataSource(); } @Override public Dialect getDialect() { return dialect(); } @Override public SqlFileRepository getSqlFileRepository() { return sqlFileRepository(); } }; }
Dialect是DB的方言消除Class,通过使用这个Class可以消除DB之间关于SQL文的语法差异。
SqlFileRepository是存放SQL的位置,可以用来指定SQL的存放位置
以上为标准写法。可以customize。
■
Gradle的一些理解
repositories可以使用在以下3个位置。
- buildscript:gradle脚本本身运行时需要的外部repository
- gradle.build文件本身:是指脚本运行时需要的外部repository
- allprojects:是指子工程需要的外部的外部repository
■apply plugin: 'java'
build.gradle文件里一般都会需要这个plugin
增加java plugin的目的是为了增加java相关的各种task
下面这些命令就可以gradle -build,gradle -compile,
■apply plugin: 'org.springframework.boot'
build.gradle文件里一般都会需要这个plugin
springframework最基本的插件,没有这个插件的话,会出现下面的错误
例:Could not resolve: org.springframework.boot:spring-boot-starter-web
■apply plugin: 'io.spring.dependency-management'
build.gradle文件里一般都会需要这个plugin
版本管理最基本的插件,没有这个插件的话,会出现下面的错误
例:Could not resolve: org.springframework.boot:spring-boot-starter-jetty
During the build, one or more dependencies that were declared without a version failed to resolve:
org.springframework.boot:spring-boot-starter-jetty:
Gradle的结构
- buildscript
- plugin 本体
- dependencies依赖
buildscript { //定义外部变量 ext { springBootVersion = '2.0.3.RELEASE' } //buildscript运行需要的repository repositories { mavenCentral() } //脚本执行时的依赖包 dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") } } //增加compile,build等task apply plugin: 'java' //把java的路径,写到eclipse的.project文件里 apply plugin: 'eclipse' //spring最基本的插件 apply plugin: 'org.springframework.boot' //版本管理的插件 apply plugin: 'io.spring.dependency-management' //编译的JRE版本 sourceCompatibility = 1.8 //jar包的名字 jar { baseName = 'apibot' version = '0.0.1-SNAPSHOT' } //编译,打包,junit运行时需要的repository repositories { mavenCentral() jcenter() } // Doma-Genで出力するJavaクラスとSQLファイルの出力先ディレクトリを同じにする processResources.destinationDir = compileJava.destinationDir processTestResources.destinationDir = compileTestJava.destinationDir // Doma-Genでコンパイルより前にSQLファイルを出力先ディレクトリにコピーする // ために依存関係を逆転する compileJava.dependsOn processResources compileTestJava.dependsOn processTestResources dependencies { compile('org.springframework.boot:spring-boot-starter') compile('org.springframework.boot:spring-boot-starter-web') compile('org.springframework.boot:spring-boot-starter-jersey') compile('org.springframework.boot:spring-boot-starter-thymeleaf') compile('org.springframework.boot:spring-boot-starter-security') testCompile('org.springframework.boot:spring-boot-starter-test') compile("org.springframework.boot:spring-boot-starter-jetty") compile("org.springframework.boot:spring-boot-starter-actuator") compile("org.projectlombok:lombok:1.16.16") compile("org.seasar.doma:doma:${domaVersion}") /* DBアクセス関連 */ // https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-jdbc compile("org.springframework.boot:spring-boot-starter-jdbc") // https://mvnrepository.com/artifact/org.seasar.doma.boot/doma-spring-boot-starter compile("org.seasar.doma.boot:doma-spring-boot-starter:${domaBootVersion}") compile("org.lazyluke:log4jdbc-remix:0.2.7") runtime(group: "org.postgresql", name: "postgresql") }
关于Sublime Text3 使用MarkDown的配置
Sublime Text3 使用技巧
(一)如何使用Markdown插件
1. 安装Package Control
按[CTRL+`],在跳出的Console窗口中输入以下命令
import urllib.request,os,hashlib; h = '6f4c264a24d933ce70df5dedcf1dcaee' + 'ebe013ee18cced0ef93d5f746d80ef60'; pf = 'Package Control.sublime-package'; ipp = sublime.installed_packages_path(); urllib.request.install_opener( urllib.request.build_opener( urllib.request.ProxyHandler()) ); by = urllib.request.urlopen( 'http://packagecontrol.io/' + pf.replace(' ', '%20')).read(); dh = hashlib.sha256(by).hexdigest(); print('Error validating download (got %s instead of %s), please try manual install' % (dh, h)) if dh != h else open(os.path.join( ipp, pf), 'wb' ).write(by)
可以参照https://packagecontrol.io/installation
2. Sublime中的Markdown插件
- OmniMarkupPreviewer – Markdownで書いた文章をブラウザでリアルタイムプレビュー
- Monokai Extended – Markdownをシンタックスハイライト
- Markdown Extended – Markdown内のソースコードをシンタックスハイライト
- Table Editor – Markdownでの表作成を簡単に
- TralingSpaces – 行末のスペースを表示
安装完上述插件,就可以使用Markdown语言来写展示文档。 例如以下java代码
public void static main( String args[]){ }
例如以下标题3: