Groovy_and_Grails_Recipes 之Groovy部分
尉迟子民
2023-12-01
感觉这本书说的东西也不少,可是都不是很深入,可以了解一下,以后找重点再看过,有点像CookBook
Map的类型中 可以直接使用Object.key来访问
string.eachMatch(pattern){match-> mathehed+=match[0]} ...方便的整合了正则
as 用于进行类型转化, in用于判断该对象是否属于一种类型
可以通过修改指定名称的方法,来实现运算符的重载
将Groovy与Java整合的方法
1:最简单的就是编译成.class文件后使用
2:Using GroovyShell
3:Using GroovyScriptEngine
4:Using GroovyClassLoader
5:Using JSR 223
Groovy中的数据类型可以分为 简单数据类型 和 集合数据类型 两种
Simple data types include strings, regular expressions (regexes), and numbers.
Collective data types include lists, maps, and ranges
?安全符不能直接和其他的运算符一起使用 如:];assert a?+3==null
Range一些需要注意的地方
[1..3] == [[1,2,3]]
可以使用*打开[],类似flatten方法
[*1..3]==[1,2,3]
不过在用于表示数组下标的时候,不需要进行拆开就可以直接使用
a[0..2] == [...]
Groovy可以在一个文件中定义多个类class,而且不需要类名与文件同名
而且也可以在class的同时,放入脚本
注意多个类在同一个文件的时候,要保证class名和文件不能同名,否则有可能报错,和编译重名有关
使用as 防止类名重复的情况出现
import java.sql.Date as SQLDate
这种特性也可以用于方便的继承类和重写方法,同时保持类名一样
可以使用闭包的方式去实现一个接口,方法有两种
1:使用Map的方式
x=[Method1:{...方法体}] as 接口类型; 然后就可以直接使用x.方法名进行方法调用
2:使用new的方式..比较不好理解,意思就是用一个{} as成接口的方法,然后进行直接的调用
其实都是通过:赋值给接口中的方法
方法参数的接受方式
1:使用默认值=
2:使用List
3:使用数组Objec[]
4:使用Map
多种通过构造函数创建一个对象的方法
1:普通的new Entity("x","y")
2:def entity=['x','y'] as Entity
3:Entity e=['x','y'] ...............这里注意def 和Entity定义的不同
PS: Groovy创建的类,默认支持所有的构造函数,也支持使用:来给属性赋值,不过还不知道能不能在java在通过不同
的构造函数进行创建
GroovyBean中直接用.设置属性的时候,调用的是set方法
如果想直接设置属性,可以使用.@方法
可以使用bean对象.Properties来访问bean中的属性,应该是是以map的形式保存
例:employee.properties.containsValue("name")
GPaths 用于解析对象的结构
"".class.methods.name.sort() 用于解析String拥有的所有方法
*.运算符用于调用集合中的每个元素,grep方法调用类似
可以直接在GroovyBean的实例上增加闭包 entity.ss={},其中闭包可以访问所需要属性,不过似乎需要声明成
Expando对象
MetaClass的基础
所有的Groovy对象都实现了Groovy.lang.GroovyObject 接口,如果要控制一个JavaBean拥有GroovyBean的能力
,那么就要使其实现这个接口,也可以选择去继承groovy.lang.GroovyObjectSupport类
例: 类名.metaClass.respondsTo(类名,"方法名")
类名.metaClass.hasProperty(类名,"属性名")
方法的拦截
在调用方法的时候,会自动调用对应的invokeMethod(x,y)方法
可以覆盖该方法,然后用于方法的拦截,和定义动态的方法
其中传入的两个参数,第一个为完整的方法名,第二个为传入的参数
另外一种用于拦截未定义的方法
在对象中重写 methodMissing(String name,args)
参数和上面一样,不过会拦截所有未定义的方法,这点是区别上面的例子
参数列表是一样
还有一种是使用ExpandoMetaClass来动态添加方法,具体后面描述,会作用到
同类型的所有方法
脚本中运行方法,需要注意声明的顺序
file的eachline方法将会自动的打开和关闭文件流
调用Closure的三种方法
def c={println it}
1:c("x")
2:c.call("x")
3:c.docall("x")
闭包可以访问定义他的类里面所有scope的方法
在Closure中,
this表示定义他们类的名称
owner 根据域的不同,输出的名称也不同,在一级的Closure中表示类名,在子Closure,即脚本中则输出该Closure
delegate 输出的都是Script的名称,有时和owner一样
如果Closure定义在一个方法中,那将不会被return,只相当于continue,跳过一次循环
Closure中的curry的特殊用法.. 用于设置参数的默认值,已经返回新类型的Closure
def original={x,y,z -> return x+y+z}
def addOne = original.curry(1) //将会返回代替掉第一个参数的Closure
其中curry也可以带多个参数
Closure可以通过两个方法获取参数的数量和类别 getXX......
其中的获取类别的方法,返回的是一个数组,然后判断每个数组元素[n]格式的 in对象
使用map的形式使用一个数组
key={value -> println value }
def m=[(key):value]
注意这里需要使用()号包围key,为了避免将key转换成字符串使用
访问的方式可以有m[key] , m.get(key) 不过如果使用m.key 将会返回null
同时也可以将Closure使用在value上,调用的时候m2.key.call()
Builders的定义,更像一种模板,生成指定结构的代码,可以用于创建DSLs
MarkupBulider ----主要用于生成带标签的文本
def bulider = new groovy.xml.MarkupBulider()
bulider.authors{
author(name:'Bas...'){
book(title:'xdsd...',edition:1)
}
}
生成的xml格式为
<authors>
<author name="Bas...">
<book title="xdsd..." edition='1'/>
</anthor>
</authors>
如果要生成标签之间的值<>value</> 那么直接在上面的()之间, Object.value就可以 区别于属于x:b
结合文件输出与MarkupBuilder
def writer = new FileWriter("c:\\temp\\td.html")
def html = new groovy.xml.MarkupBuilder(writer)
使用的时候
html.标签名{
标签名{
标签名 '值'
}
标签名{
标签名 '值'
标签名(标签属性){
}
}
}
值与属性的区别在于格式 值为不带key, 属性需要以map的格式
ObjectGraphBuilder 和NodeBulider都是用于使用对象生成一定的结构,方便进行访问,区别在于后者不需要Javabean的类存在, 这两个都是方便的去创建多个类的实例,已经属性的设置
另外的两个AntBulider和SwingBulider都是针对指定领域的模板,用于简化开发
可以通过继承groovy.util.BuilderSupport来自定义Bulider,需要实现六个方法
数据库操作
连接数据库的方式
import groovy.sql.Sql
connection= Sql.newInstance("jdbc:mysql://localhost:3306/company",root,"pwd","driver.class")
需要数据库URL地址,用户名,密码,驱动类名
其中驱动类名可以省略,因为Groovy可以通过连接的URL推算出需要的连接类名
也可以通过newInstance加载一个Properties资源文件来创建连接
使用数据库连接池 ,只适合做测试,不做详细描述
通过Sql的实例 可以通过execute去执行SQl的数据库操作,这里可以使用'''去操作多行命令
指定多行命令的时候需要注意数据库是否支持多个statement同时执行,比如mysql就需要在连接的url
加入指定的参数 ?allowMultiQueries=true
或者可以使用java.util.properties的配置
def props = new Properties()
props.setPropertiy("user","root")
props.setPropertiy("password","")
props.setPropertiy("allowMultQueries","true")
connection= Sql.newInstance("jdbc:mysql...",props,"driver.class")
执行数据库的CRUD
普通的SQL语句直接使用execute就可以执行,不过为了性能考虑,建议使用prepared statements,带参数绑定,介绍解析的次数
String stmt='INSERT INTO employees(firstName,lastName) VALUES (?,?);'
connection.execute(stmt,['arg1','arg2']);
带参数的指定语句,第二个参数为list
也可以使用二维的List 来实现多个语句的执行
def employees=[['bar','ab'],['xx','yy']]
然后使用同一条带??的语句的情况ia
使用employees.each{employee -> connection.execute stmt,employ}
使用数据库进行查询,可以使用三种方法 eachRow,query,rows
使用rows将返回一个包含查询结果的List,(其实更像是Map,适应it.列名进行访问)
connection.rows("select firstName,lastName from employees").each{println "${it.firstName} ${it.lastName}"}
使用query将返回一个ResultSet
connection.query('SELECT * from employees'){result ->
while(resultSet.next()){
println "${resultSet.getString('firstName')} ${resultSet.getString(lastName)}"
}}
eachRow的方法区别与上面两个的地方在于,上两个均是返回一个结果后,再进行操作,而eachRow是传入一个Closure
进行操作
connection.eachRow('select firstname,lastname from employees'){
employee -> ..employee.firstname...
}
获取表的元数据Metadata(表名.列类型),分为两个部分操作
connection.query('SELECT * from employees'){result ->
ResultSetMetaData metaData = result.metaData
println "Table name is "+metaData.getTableName(1)
for(i in 0..<metaData.columnCount)
{
metaData.getColumnLabel(i+1)
metaData.getColumnTypeName(i+1)
}}
DataSet是对于Sql实例的扩展
创建的两种方式
dataSet = new DataSet(connection,"employees") //其中后者为表面
dataset = connection.dataSet("employees")
插入数据: dataSet.add(firstName:'alean',lastName:'Mitchell') //key为列名
查询数据:dataSet.each{println "${it.firstName} ${it.lastName}"}
也可以直接使用dataSet.rows().size()返回列数
Groovy Test
内嵌了Junit3.X的支持, 可以通过extends GroovyTestCase来实现测试类,不过注意方法名需要以test开头
测试异常的方法
shouldFail(RuntimeException)
{
//可能抛出异常的方法
}
运行的时候执行按照Groovy类运行即可(不需要按照Junit执行)
也可以使用IDE创建专门的Junit Test Suite 用于管理测试用例
public static Test suite(){
TestSuite suite = new TestSuite();
suite.addTestSuite(BubbleSortTest.class)....
return suite
}
不过如果要测试Groovy的类,那么需要先编译成.class文件 可以使用GroovyTestSuite进行设置
public static Test suite(){
TestSuite suite = new TestSuite();
GroovyTestSuite gsuite=new GroovyTestSuite();
try{
suite.addTestSuite(gsuite.compile("""
src\\cp\\ap\\BubbleSortTest.groovy
"""))
}catch(Exception e){}
return suite
}
也可以通过使用IDE和groovy.util.AllTestSuite类来提供测试的遍历,需要在vm参数里面加上一些特别的配置
使用Map方便进行测试,代替JavaBean中的属性
其中涉及到了Map中的闭包使用
def creditHistory=[getCreaditScore:{ssn -> if(ssn==123) return 300;if(ssc==300)....}]
会根据credilt.getCreditScore(xx) 中xx的值不同,返回不同的值
这里使用key模拟方法名,而value是传入的参数,同时也是closure闭包中传入的参数
还有其他的就是使用def user1=[ssn:123] 来模拟属性 然后在方法内部使用user.ssn进行访问属性
使用Expando模拟对象
def creditHistory = new Expando()
creditHistory.getCreditScore = {ssn ->
if(ssn==123) return 400
if(ssn ==12) return 500
}
也是模拟成属性的调用
使用GroovyLogTestCase 可以用于测试的时候,同时记录log日志
void testEvenOddLog(){
def log = stringLog(level.FINER,'EvenOdd'){
numbers.each{evenOdd.isEven(it)}
}}
然后log就是日志的内容,可以直接输出控制台,或者保存到文本中
使用Cobertura进行代码覆盖率测试
使用TemplateEngine制作模板代码,类似JSP标签的方式生成key,也类似GString的方式生成指定的文本
如果使用方法 可以使用<%=new java.text.SimpleDateFormat("MM\\\\dd\\\\yyyy").format(new Date())%>
其他的均用$key格式
使用的时候
def bingding=["key":"vlaue"]
def engine = new SimpleTemplateEngine()
template = engine.createTempate(text).make(binging) //其中text为模板字符串
println template.toString()
也可以定义在一个单独的文件中regetemp.template,加载如下
def f = new File('/home/..包/文件名')
engine = new GStringTemplateEngine()
template = engine.createTemplate(f).make(binding) //binding定义见上个例子
println template.toString()
建立一个简单的GroovyServlet
建立一个文件夹,并放置在tomcat/webapps下
工程结构为 /WEB-INF/web.xml
<!....>
<web-app>
<servlet>
<servlet-name>Groovy</servlet-name>
<servlet-class>groovy.servlet.GroovyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Groovy</servlet-name>
<url-pattern>*.groovy</url-pattern>
</servlet-mapping>
</web-app>
这里的映射会将所有的*.Groovy的url请求传递到指定的类中
在这个Groovy类中,可以访问到类似Servlet中需要的值
以及可以使用html.html去使用MarkupBuilder去创建一个Html文本格式
GroovyServlet中的关键字(域)
request: response: applicationand context: session: out: headers: params: html:
解析xml的选择 JDK自动的 DOM和SAX 第三方的JDOM和dom4j Groovy自带的XmlParser和XmlSlurper
其中第二种均为流解析的方式(Dom4j不确定)
xmlSlurper 解析方法返回的对象为GPathResult 代替xmlParser的Node,以及所有的查询只有需要的时候才返回结果
使用流代替在内存中构建xml document..
def food = new XmlSlurper().parser(new File('/home.../xx.xml'))
访问的时候可以直接使用food.name...进行访问节点属性
Jdk5之上内嵌了XPath的使用,可以关注下
对于小规模的xml 使用XmlParser解析会更方便一些
def url="http:...." //rss地址
def channel = new XmlParser().parse(url).channel[0]
println channel.title.text() //使用text()方法访问<>..<>之间的内容
循环多个相同标签
def items = channel.item
for(item in item[0..2]){
println item.title.text() //...还可以访问到其他的属性
}
使用Groovy执行外部程序
浏览文件目录
Linux and Unix 版
def process = "ls -l".execute()
println "${process.text}"
window版
def process = "cmd.exe /c dir".execute()
println "${process.text}"
使用Groovy风格去下载一个文件
def download(address)
{
def file = new FileOutputStream(address.tokenize("/")[-1])
def out = new BufferedOutputStream(file)
out << new URL(address).openStream()
out.close()
}
download("htt...//..")
其实就是截取URL设置文件名, 建立文件建立缓冲 打开URL的输入流,并且输出到文件中 关闭输入流
获取文件的最后修改日期,指定文件夹下
new File('/home/old').eachFileRecurse{f -> println f.lastModified()}
查询文档中所有字符的数量
使用一个Map保存所需要文件的key 然后通过判断map.containsKey(word)是否存在,不存在新增,存在就+1