◎筱米加步枪◎.Blog

Happy coding

Sping中自定义默认注解名生成器

在类中贴上@Repository等标签,相当于在Spring容器里定义了一个Bean类,这个Bean的名称如果不指定名称,将默认使用当前类名(不含包名)的首字母小写作为Bean的名字.

需求:

要确保这个Bean名称要唯一,显然这样定义的Bean名称太过简单,比较容易造成一样情况,如果能使用全类名作为Bean可防止这样的情况,因为不存在全类名一样的类.例如:org.kirinsoft.frame.AClass

技术实现:

指定定义一个Bean名字生成器,实现Spring提供的BeanNameGenerator,在generateBeanName方法中设计出自己需要的Bean名称.

另外在Spring配置文件中的自动注解配置要指定名字生成器类,如:

	<!-- 自动注解配置 -->
	<context:annotation-config />
	<context:component-scan base-package="com.kirinsoft.frame"
		name-generator="com.kirinsoft.frame.pub.UniqueBeanNameGenerator" />

具体实现方法:

/**
 * 唯一Bean名称生成器.以类全名作为Bean名称.
 */
public class UniqueBeanNameGenerator implements BeanNameGenerator{

	@Override
	public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
		return definition.getBeanClassName();
	}
}

 

Spring中事务的作用范围

下面一种情景:

在做项目中通常会进行分层架构.假设可分为Service、Biz、Dao3层.关系如下:

一个Service类调用多个Biz类,一个Biz调用多个Dao类完成业务逻辑.

基于这种情况,通常我们把事务控制到Service层.

很当然的引入Spring中配置:(其他配置略)

<property name="beanNames">
            <list>
                <value>*ServiceImpl</value>
            </list>
        </property>

 

那么,引入一个问题:如果同时对Service层和Biz层加入Spring的事务配置中,是哪一层起作用呢、?

换句话说,假设一个Service有两个Biz类,那么第一个Biz类操作成功,而第二个操作失败,此时第一个Biz是否有回滚呢。?

对应的Spring配置:

        <property name="beanNames">
            <list>
                <value>*ServiceImpl</value>
                <value>*BizImpl</value>
            </list>
        </property>

通过实验证明可得到结论:哪个包含的范围大,哪个事务起作用。

上面的假设,Service的事务起作用,第一个Biz会被回滚掉。

有关Hibernate中PO对象Blob字段的注解

PO对象中使用Hibernate注解方式进行映射.Blob字段描述如下:

1.首先PO对象中属性名可指定为byte数组(byte[])
   

private byte [] templateData;

2.在Setter方法中进行注解方式如下:

    @Lob
    @Basic(fetch = FetchType.LAZY)
    @Column(name = "TEMPLATE_DATA",columnDefinition="BLOB")
    public byte [] getTemplateData() {
        return this.templateData;
    }

 

Velocity基础笔记

今天初步学了一下Velocity,顺手写了一些简单例子,主要是测试VTL(Velocity描述语言)的语法.

先来看看如何使用Velocity的:

//需要依赖的包:common.collections.jar,common-lang.jar
public class VeloccityTest {
	
	public static void main(String[] args) {
		//创建引擎
		VelocityEngine velocityEngine = new VelocityEngine();
		
		//设定输入输出编码可支持中文
		Properties prop = new Properties();
		prop.setProperty(Velocity.ENCODING_DEFAULT, "utf-8"); 
		prop.setProperty(Velocity.INPUT_ENCODING, "utf-8"); 
		prop.setProperty(Velocity.OUTPUT_ENCODING, "utf-8"); 
		
		//可以根据属性对象或者属性文件进行定义,原理是一样的.
		velocityEngine.init(prop);
		
		//取得VTL定义的文件获取模板对象
		Template template = velocityEngine.getTemplate("/src/template.vm");
		
		//新创一个Velocity上下文
		VelocityContext velocityContext = new VelocityContext();
		
		//放入VTL中定义的变量值
		velocityContext.put("name", "ChenST");
		velocityContext.put("address", "淘宝商城");
		
		List<String> products = new ArrayList<String>();
		products.add("百事可乐");
		products.add("可口可乐");
		//可放入一个ArrayList集合代表数组
		velocityContext.put("products", products);
		
		Map<String, String> map = new HashMap<String, String>();
		map.put("key", "mapValue");
		//可放入一个Map结构的对象
		velocityContext.put("map", map);
		
		People people = new People();
		people.setName("张三");
		//可放入一个自定义对象
		velocityContext.put("people", people);
		
		StringWriter stringWriter = new StringWriter();
		//根据模板定义的内容进行合并,并将结果输出给流对象
		template.merge(velocityContext, stringWriter);
		
		System.out.println(stringWriter.toString());
	}
}

接下来再来看看具体的VTL是如何定义和描述的,(习惯将一些学习过程写在代码中...)

#####这个是注释#####

#####parse脚本元素包含另一个VTL的本地文件######

测试解析:#parse("/src/other.vm")

#####include包含另外一个文件,只是文本包含,不进行解析######
测试文本包含:#include("/src/other.vm");

#####set脚本元素定义变量#####
#set($defaultDept = "技术部")

#####引用变量值,可以是VTL中指定的,也可以是Java程序中注入上下文的值#####
#####可以$name也可以${name},后者比较规范#####

测试获取变量值:你好, $name,欢迎来到 $address,默认部门:$defaultDept

#####判定元素if-else-end#####
#set($i = 1)
#set($j = 2)
#if( $i == $j)
测试判定元素:i值和j值相等
#else
测试判定元素:i值和j值不相等
#end

#####VTL变量的算数运算######
#set($plus = ${i}+${j})
#set($minus = ${i}-${j})
#set($multi = ${i}*${j})
#set($divis = ${i}/${j})
测试运算:
加法运算:结果:${plus}
减法运算:结果:${minus}
乘法运算:结果:${multi}
除法运算:结果:${divis}

#####foreach-end脚本元素用于循环操作,products是一个ArrayList#####
#####velocityCount变量的名字是Velocity默认的名字,可以通过修改velocity.properties文件来改变它###
#####directive.foreach.counter.name = velocityCount #####
#####directive.foreach.counter.initial.value = 1 #####

测试循环元素:
#foreach($product in $products)
第$velocityCount条记录 - ${product}
#end

#####使用.有两种含义,一种可以是HashMap,HashTable这种结构,另外一种可以是对象的属性值或者方法#####
测试Map的值:${map.key}
测试对象的值:${people.name}
测试对象方法:${people.call()}

#####macro元素标签可用于定义一个可重用的模板,如reTemp为模块名,可跟任意个参数#####
#macro(reTemp $parm1 $parm2)
测试定义可重用模板,参数:${parm1},${parm2}
#end
#####使用定义的可重用的模块#####
#reTemp(${i} ${j})

#####可以使用中括号操作符表示一个范围,默认增量为1#####
测试范围元素:
#foreach ( $foo in [3..1])
$foo
#end

#####$test和$!的区别:前者找不到时原串输出,后缀如果找不到输出空串#####
找不到test变量值输出:$test
找不到test变量值输出:$!test

另外一个被引用的文件

测试引用其他文件:你好, $name,欢迎来到 $address

感觉Velocity还是挺好的..可以自己一定一些模板,比如java代码,XML代码之类的都可以.可以自己试着做一些小工具,生成自己开发模式的一些代码.还可以结合Eclipse写一些小插件之类的。

 

Ant文件间脚本的调用

每个项目都有一个ant打包部署脚本,一个一个ant文件执行麻烦,需要有一个ant脚本统一调到这些ant文件,把这些ant文件给串起来.

贴个Ant文件间的调用脚本:(部分Ant脚本省略..有代表性的贴出些..)

svn.java.dir=.
svn.eframe.dir=${svn.java.dir}/EFrame
svn.workflow.dir=${svn.java.dir}/workflow
### jar dir ###
eframework.dir = ${svn.eframe.dir}/eframework
casclient.dir=${svn.eframe.dir}/casclient
### war dir  ###
CAS.dir = ${svn.eframe.dir}/CAS
e_workflowService.dir = ${svn.workflow.dir}/e_workflowService
<project basedir="." default="packageAll" name="allJar">
	<property environment="env" />
	<property file="build-all.properties" />
	
	<!-- 重建目录 -->
	<target name="rebuild">
		<delete dir="${basedir}/dist/lib" />
		<delete dir="${basedir}/dist/deploy"/>
		<mkdir dir="${basedir}/dist/lib"/>
		<mkdir dir="${basedir}/dist/deploy"/>
	</target>
	
	<!-- 调用其他项目的ant脚本(打jar包)-->
	<target name="packageJar" depends="rebuild">
		<ant antfile="build.xml" dir="${eframework.dir}"/>
		<ant antfile="build.xml" dir="${casclient.dir}"/>
	</target>
	
	<!-- 调用其他项目的ant脚本(打war包)-->
	<target name="packageWar" depends="packageJar">
		<ant antfile="build.xml" dir="${CAS.dir}"/>
		<ant antfile="build.xml" dir="${e_workflowService.dir}"/>
	</target>
	
	<target name="packageAll" depends="packageWar"/>
</project>

完毕..

使用Ant生成JBoss上部署项目的脚本

用Ant脚本打包部署项目,生成Jboss上部署后的文件信息.(跟MyEclipse部署到Jboss的效果一样)

搞了一天多..Ant不是很熟..贴个备忘下..

svn.lib.path=../../lib
svn.dist.path=../../dist
svn.target.lib=${svn.dist.path}/lib
svn.target.deploy=${svn.dist.path}/deploy
target.war.file=${svn.target.deploy}/EFrameWeb.war
busi.jars=e_workflowclient.jar,jbpmengineclient.jar
<project basedir="." default="deploy" name="EFrameWeb">
	<property environment="env" />
	<!-- 引用属性文件的属性定义 -->
	<property file="build.properties" />

	<property name="debuglevel" value="source,lines,vars" />
	<property name="target" value="1.6" />
	<property name="source" value="1.6" />
	<!-- 定义WebRoot路径 -->
	<property name="WebRoot.dir" value="${basedir}/WebRoot"/>
	<!-- 定义编译路径 -->
	<property name="compile.dir" value="${WebRoot.dir}/WEB-INF/classes"/>
	<!-- 定义源代码路径 -->
	<property name="src.dir" value="${basedir}/src"/>
	<!-- 定义项目所用到的其他包 -->
	<property name="lib.dir" value="${WebRoot.dir}/WEB-INF/lib"/>
	
	<!-- 定义生成的war包的classes路径 -->
	<property name="target.war.file.classes" value="${target.war.file}/WEB-INF/classes"/>
	<!-- 定义生成的war包的lib路径-->
	<property name="target.war.file.lib" value="${target.war.file}/WEB-INF/lib"/>
	
	
	<path id="appAPI.classpath">
		<!-- 包含项目中涉及到的业务包和工具包 -->
		<fileset dir="${svn.target.lib}">
			<include name="**/*.jar"/>
		</fileset>
		
		<!-- 包含项目中涉及到第三方开源包 -->
		<fileset dir="${svn.lib.path}">
			<include name="**/*.jar" />
		</fileset>
		
		<!-- 包含存在项目自身lib目录下的包 -->
		<fileset dir="${lib.dir}">
			<include name="**/*.jar" />
		</fileset>
	</path>
	<target name="clean">
		<delete dir="${compile.dir}" />
		<delete dir="${target.war.file}/" />
	</target>
	<target name="init" depends="clean">
		<mkdir dir="${target.war.file}" /> 
		<mkdir dir="${compile.dir}" />
		
		<!-- 属性信息 -->
		<echo message="${ant.project.name}: ${ant.file}" />
		
		<!-- 编译程序 -->
		<javac debug="true" 
			   includeantruntime="false" 
			   debuglevel="${debuglevel}" 
			   encoding="GBK" 
			   destdir="${compile.dir}" 
			   source="${source}" 
			   target="${target}">
			<src path="${src.dir}" />
			<classpath refid="appAPI.classpath" />
		</javac>
		
		<!-- 拷贝WebRoot下的东西 -->
		<copy includeemptydirs="false" todir="${target.war.file}">
			<fileset dir="${WebRoot.dir}">
				<exclude name="**/.svn" />
				<exclude name="**/*.launch" />
			</fileset>

		</copy>
		
		<!-- 拷贝项目classses非编译文件 -->
		<copy includeemptydirs="false" todir="${target.war.file.classes}">
			<fileset dir="${src.dir}">
				<exclude name="**/*.launch" />
				<exclude name="**/*.java" />
				<exclude name="**/.svn" />
			</fileset>
		</copy>

		<!-- 拷贝项目所依赖的业务包 -->
		<copy includeemptydirs="false" todir="${target.war.file.lib}">
			<fileset dir="${svn.target.lib}" includes="${busi.jars}"/>
		</copy>
	</target>

	<target name="deploy" depends="init"></target>
</project>

 

使用Ant编译出现“找不到符号”错误原因与解决方法

这两天做写Ant脚本打包和部署项目.期间遇到一个问题,花了两三个小时解决.真郁闷..笔记下下.
问题描述如下:
使用Ant脚本编译打包程序时出错,出错信息:“找不到符号”,检查编译程序出来的代码无误,也确保需要的jar包已经引入。
后来偶然发现目录中存在一个早期版本打成的一个同名的jar包.而我写的ant脚本加载器又是优先加载旧版本的jar包.因此识别到的类没有新项目中所用的方法.自然就报"找不到符号"的错误了.
解决方法:
1.最根本的办法:就是去除旧版本的包,然后重新编译即可.
2.不除去旧版本包,可以优先加载新版本的jar程序.

EhCache基础知识学习笔记

项目需要,稍微过了一下有关EhCache缓存框架的知识.

简单介绍下EhCache:EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点.

一些不太经常变化的数据可以放入缓存,需要时候直接从缓存获取,可以提供系统的性能.

具体来看一下ehcache的知识吧.

ehcache需要一个xml配置文件:

内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
<!-- 缓存数据文件创建的地方,java.io.tmpdir是默认的临时文件路径 -->
<diskStore path="java.io.tmpdir"/>

	    <!-- Sets the path to the directory where cache .data files are created. 
	    	  设置为缓存的地方。数据文件创建的目录的路径

	         If the path is a Java System Property it is replaced by 
	         its value in the running VM.
	                        如果路径是Java系统属性中被替换其值在运行虚拟机。
	         
	         
	         The following properties are translated:下列属性换算
	         user.home - User's home directory           用户的主目录
	         user.dir - User's current working directory 用户的当前工作目录
	         java.io.tmpdir - Default temp file path     默认临时文件路径
	    <diskStore path="java.io.tmpdir"/> -->
	    
	    <!--Default Cache configuration. These will applied to caches programmatically created through
	        the CacheManager.
		默认的缓存配置。这将适用于通过编程方式创建缓存在CacheManager。
	
	        The following attributes are required: 下面的属性是必需的
	
	        maxElementsInMemory            - Sets the maximum number of objects that will be created in memory 
                                                 设置对象的最大数量,将在内存中创建
	        eternal                        - Sets whether elements are eternal. If eternal,  timeouts are ignored and the
	                                         element is never expired. 
	                                         设置元素是否是永恒的。如果永恒的,超时被忽略,并且元素是永远不会过期(缓存是否永远不销毁)
	        overflowToDisk                 - Sets whether elements can overflow to disk when the in-memory cache
	                                         has reached the maxInMemory limit.
	                                         设置当缓存中的数据达到最大值时,是否把缓存数据写入磁盘
	
	        The following attributes are optional: 以下属性是可选的
	        timeToIdleSeconds              - Sets the time to idle for an element before it expires.
	                                         i.e. The maximum amount of time between accesses before an element expires
	                                         Is only used if the element is not eternal.
	                                         Optional attribute. A value of 0 means that an Element can idle for infinity.
	                                         The default value is 0.
	                                         设置 当缓存闲置指定时间,当闲置时间到达指定时间时,缓存则自动销毁,可选的属性。
                                                 0值表示一个元素可以闲置无穷。默认值是0
	                                         
	        timeToLiveSeconds              - Sets the time to live for an element before it expires.
	                                         i.e. The maximum time between creation time and when an element expires.
	                                         Is only used if the element is not eternal.
	                                         Optional attribute. A value of 0 means that and Element can live for infinity.
	                                         The default value is 0.
                                                 设置当缓存创建之后到达的指定时间,当缓存生存超过指定的时间,缓存则自动销毁
                                                 可选的属性。0值表示和元素可以活无限。默认值是0
	        diskPersistent                 - Whether the disk store persists between restarts of the Virtual Machine.
	                                         The default value is false.
                                                 无论是磁盘存储之间仍然存在的虚拟机重新启动。默认值是false
	        diskExpiryThreadIntervalSeconds- The number of seconds between runs of the disk expiry thread. The default value
	                                         is 120 seconds.
                                                 磁盘之间的到期线程运行的秒数。默认值是120秒
	        -->
	        
    <defaultCache
        maxElementsInMemory="100000"
        eternal="false"
        overflowToDisk="true"
        timeToIdleSeconds="60" 
        timeToLiveSeconds="120"
        />

	<cache name="systemConfigCache"
       maxElementsInMemory="100000"
       eternal="false"
       overflowToDisk="true">
    </cache>

</ehcache>

以上的一些翻译是结合google翻译和一些自己的理解.ehcache涉及的配置元素和属性说明都在上头了.

以下是Java代码部分,描述了一些关于Ehcache的简单使用和一些API,具体其他的API就参考具体文档咯..

//必须导入commons-logging.jar
public class EhCache {
	
	//默认缓存配置路径
	public static String DEFAULT_CACHE_CONFIG_PATH = "conf/ehcache.xml";
	
	public static void main(String[] args) {
		//使用指定的配置文件路径创建缓存管理器
		CacheManager cacheManager = new CacheManager(DEFAULT_CACHE_CONFIG_PATH);
		//获取配置的缓存Cache名字
		String [] cacheNames = cacheManager.getCacheNames();
		
		//会在当前classpath中去寻找ehcache.xml配置文件
		CacheManager cacheManager1 = new CacheManager(); 
		//或者 CacheManager.getInstance(); 
		//或者CacheManager.create();

		//由缓存管理器中获得缓存,可以是配置在文件中的缓存也可以是代码中new出来的.
		Cache cache = cacheManager.getCache("systemConfigCache");
		
		//数据放入缓存
		cache.put(new Element("key", "value"));

		//更新缓存数据
		cache.put(new Element("key","value2"));
		
		//获取缓存的值(序列画值)
		Element element = cache.get("key");
		Serializable value = element.getValue();
		
		//获取缓存的值(对象值)
		Object object = element.getObjectValue();
		
		//移除缓存数据
		cache.remove("key");

		//取得缓存中的属性maxElementsInMemory,其他的值类似
		int maxelementsInMemory = cache.getMaxElementsInMemory();
		
		String name = "codeCreateCache";  //缓存名字
		int maxElementsInMemory = 1000;   //缓存可以存储的总记录量
		boolean overflowToDisk = false;   //当缓存中的数据达到最大值时,是否把缓存数据写入磁盘
		boolean eternal = true;           //缓存是否永远不销毁
		long timeToLiveSeconds = 60;      //当缓存闲置指定时间,则自动销毁
		long timeToIdleSeconds = 120;     //当缓存创建之后到达时间自动销毁
		
		//代码中创建缓存
		Cache cache2 = new Cache(name, maxElementsInMemory, overflowToDisk, eternal, timeToLiveSeconds,
				timeToIdleSeconds);
		
		//添加缓存
		cacheManager.addCache(cache2);
		
		//移除缓存
		cacheManager.removeCache("systemConfigCache");
		
		//卸载缓存管理器
		cacheManager.shutdown();
	}
}

使用起来还是挺方便的,如果有其他需要也可以基于上面封装满足自己的缓存框架.

CAS单点登录用户名中文乱码问题解决方案

做项目使用CAS做单点登录时发现用户名输入中文名,后台日志打出居然是个乱码,自然就匹配不到数据库中的数据了..Google了一下,在CAS项目的web.xml写一个编码过滤器,可以使用Spring提供的一个编码过滤器,配置如下:

	<filter>
		<filter-name>encodingFilter</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<init-param>
			<param-name>encoding</param-name>
			<param-value>gb2312</param-value>
		</init-param>
		<init-param>
			<param-name>forceEncoding</param-name>
			<param-value>true</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>encodingFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

注意:以上过滤器应配置在所有过滤器之前.

再测试,中文用户名测试通过.

json-lib中关于Date转换的问题

前两天,关于使用json-lib工具包来将一个java对象序列化成json串,结果总是报如下错误:

net.sf.json.JSONException: java.lang.reflect.InvocationTargetException

得原因是因为Java对象中存在Date类型的对象无法进行解析,但是我又必须使用Date类型,还好json-lib提供了可扩展的接口JsonValueProcessor接口来让我们自定义的处理Json返回值的接口,看具体的对日期返回值的操作扩展:(为缩小篇幅,注释没那么规范),另外需要依赖的第三方包有如下:

commons-beanutils.jar

commons-collections-3.1.jar

commons-lang-2.1.jar

commons-logging-1.1.1.jar

ezmorph-1.0.3.jar

json-lib-2.3-jdk15.jar

//Date日期类型的Json值处理器,可自定义一些类的处理方式,是json提供的一个扩展接口
public class DateJsonValueProcessor implements JsonValueProcessor {

	private String format = "yyyy-MM-dd'T'HH:mm:ss";   
    private SimpleDateFormat sdf = new SimpleDateFormat(format);   

    //处理数组的值
	@Override
	public Object processArrayValue(Object value, JsonConfig jsonConfig) {
		return this.process(value);
	}

	//处理对象的值
	@Override
	public Object processObjectValue(String key, Object value, JsonConfig jsonConfig) {
		return this.process(value);
	}
	
	//处理Date类型返回的Json数值
	private Object process(Object value) {
		if (value == null) {
			return "";
		} else if (value instanceof Date)
			return sdf.format((Date) value);
		else {
			return value.toString();
		}
	}
}
public class TestBean {
	
	private String name;
	
	private double price;
	
	//日期类型的时间
	private Date time;
        
        //....此处省略setter和getter
}
public class Test {

	//测试
	public static void main(String[] args) {
		//初始化信息
		TestBean testBean = new TestBean();
		testBean.setName("1234");
		testBean.setPrice(3.14145);
		testBean.setTime(new Date());
		
		JsonValueProcessor jsonProcessor = new DateJsonValueProcessor();
		JsonConfig jsonConfig = new JsonConfig();
		//注册值处理器
        jsonConfig.registerJsonValueProcessor(Date.class, jsonProcessor);
        String json = JSONSerializer.toJSON(testBean , jsonConfig).toString();
		System.out.println(json);
	}
}

结果符合我的要求如下:

{"name":"1234","price":3.14145,"time":"2010-11-09T19:46:29"}