這里是想介紹一下如何通過Java的注解機制,實現對bean資源的注入。主要介紹實現的方法,至于例子的實用性不必討論。
需求:一個應用有兩個數據庫,分別為DB-A,DB-B。
假設持久層框架使用iBatis來實現,那么SqlMapClient對象在創建時,對于兩個不同的DB連接要有兩個不同的SqlMapClient對象,
假設我們有一個Service類為MyService.java,該類中有兩個SqlMapClient對象sqlMapA、sqlMapB分別對應著DB-A、DB-B。
先看看我們的SqlMapClient.java類:(自定義SqlMapClient類,用來演示。)
import java.util.Map;

import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;

@SuppressWarnings("unchecked")

public class SqlMapClient
{

public SqlMapClient(String s, String t)
{
sqlMap = s;
type = t;
}

public SqlMapClient()
{
}

private String type = null;

private String sqlMap = null;
// get、set方法 略

// 用于演示查詢后返回一個String的返回結果

public String selectForObject(String sql, Map in)
{
return this.toString();
}
@Override

public String toString()
{
return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).append("sqlMap", sqlMap)
.append("type", type).toString();
}
}
MyService.java類實現:
import java.util.Map;

@SuppressWarnings("unchecked")

public class MyService
{
@DataSource(type="B", sqlMap="com/annotation/sql-map-config-B.xml")
private SqlMapClient sqlMapB = null;
@DataSource(type="A", sqlMap="com/annotation/sql-map-config-A.xml")
private SqlMapClient sqlMapA = null;
// get、set方法 略

// 模擬在DB-B數據庫取得數據

public String selectForObjectFromB(String sql, Map in)
{
return sqlMapB.selectForObject("", null);
}
// 模擬在DB-A數據庫取得數據

public String selectForObjectFromA(String sql, Map in)
{
return sqlMapA.selectForObject("", null);
}
}
接下來就是我們的注解類:DataSource.java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)

public @interface DataSource
{

/** *//**
* Dao的類型
* @return
*/
String type() default "A"; // 連接的數據庫類型 A or B
String sqlMap() default ""; // Sql-Map-Config文件的路徑,用于加載iBatis的SqlMapClient對象
}
定義資源注入的接口 IFieldWiring.java。
之所以這里要定義這個接口,是為了以后擴展用,我們很方便的定義更多的自定義注解。
IFieldWiring.java

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;


public interface IFieldWiring
{
Class<? extends Annotation> annotationClass();
void wiring(Object object, Field field);
}
IFieldWiring.java的實現類----DataSourceWiring.java。(該類實現只為演示用,有很多地方是可以改進的)
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;


public class DataSourceWiring implements IFieldWiring
{

@Override

public void wiring(Object object, Field field)
{
Object fieldObj = ReflectUtils.getFieldValue(object, field.getName()); // 獲得field對應的對象

if (fieldObj != null)
{
return;
}
DataSource annotation = field.getAnnotation(DataSource.class);
String type = annotation.type();
String sqlMap = annotation.sqlMap();
// 這里可以用緩存來實現,不用每次都去創建新的SqlMapClient對象
SqlMapClient sqlMapImpl = new SqlMapClient(sqlMap, type);
// 將生成SqlMapClient注入到bean對象的字段上
ReflectUtils.setFieldValue(object, field.getName(), SqlMapClient.class, sqlMapImpl);
}

@Override

public Class<? extends Annotation> annotationClass()
{
return DataSource.class;
}
}
這里的ReflectUtils.java 也是我們自定義的,并非有Spring提供的:
import java.lang.reflect.Field;
import java.lang.reflect.Method;

import org.apache.commons.lang.StringUtils;


public class ReflectUtils
{


/** *//**
* 取得字段值
*
* @param obj
* @param fieldName
* @return
*/

public static Object getFieldValue(Object obj, String fieldName)
{

if (obj == null || fieldName == null || "".equals(fieldName))
{
return null;
}

Class<?> clazz = obj.getClass();

try
{
String methodname = "get" + StringUtils.capitalize(fieldName);
Method method = clazz.getDeclaredMethod(methodname);
method.setAccessible(true);
return method.invoke(obj);

} catch (Exception e)
{

try
{
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
return field.get(obj);

} catch (Exception e1)
{
e1.printStackTrace();
}
}
return null;
}

public static void setFieldValue(Object target, String fname, Class<?> fieldClass,

Object fieldObj)
{

if (!fieldClass.isAssignableFrom(fieldObj.getClass()))
{
return;
}
Class<?> clazz = target.getClass();

try
{
Method method = clazz.getDeclaredMethod("set" + Character.toUpperCase(fname.charAt(0))
+ fname.substring(1), fieldClass);
method.setAccessible(true);
method.invoke(target, fieldObj);

} catch (Exception e)
{

try
{
Field field = clazz.getDeclaredField(fname);
field.setAccessible(true);
field.set(target, fieldObj);

} catch (Exception e1)
{
e1.printStackTrace();
}
}
}
}
已經基本大功告成了,只要將我們的DataSourceWiring.java類使用起來即可。
MyAnnotationBeanProcessor.java,這個類主要用于為bean對象注入資源。
import java.lang.reflect.Field;


public class MyAnnotationBeanProcessor
{


/** *//**
* 注入資源
* @param serviceObject
* @param fieldAutoWirings // 所有實現IFieldWiring的接口的對象,我們可以在此擴展
* @throws Exception
*/
public void wire(Object serviceObject, IFieldWiring
fieldAutoWirings)

throws Exception
{
Class<?> cls = serviceObject.getClass();

for (Field field : cls.getDeclaredFields())
{

for (IFieldWiring fieldAutoWiring : fieldAutoWirings)
{

if (field.isAnnotationPresent(fieldAutoWiring.annotationClass()))
{
fieldAutoWiring.wiring(serviceObject, field);
break;
}
}
}
}
}
好了,開始我們的測試類:FieldWiringTest.java

public class FieldWiringTest
{

public static void main(String args[]) throws Exception
{
MyAnnotationBeanProcessor processor = new MyAnnotationBeanProcessor();

MyService b = new MyService();

processor.wire(b, new DataSourceWiring()); // 注入DataSource資源
System.out.println(b.selectForObjectFromB("", null));
System.out.println(b.selectForObjectFromA("", null));
}
}
執行結果:
SqlMapClient[sqlMap=com/annotation/sql-map-config-B.xml,type=B]
SqlMapClient[sqlMap=com/annotation/sql-map-config-A.xml,type=A]
由執行結果可以說明DataSource資源已經被我們正確的注入了。
如果想擴展的話,只需要新建一個類實現IFieldWiring接口即可。假設叫InParamWiring.java,實現了接口定義的兩個方法后,在使用的時候,只要用以下代碼便可將資源注入了:
MyAnnotationBeanProcessor processor = new MyAnnotationBeanProcessor();
MyService b = new MyService();
processor.wire(b, new DataSourceWiring(), new InParamWiring()); // 注入DataSource、InParam資源
注:以上代碼重在演示,其實這個需求可以在Spring中管理兩個不同的SqlMapClient對象,然后通過Spring的自動注入實現。
下一篇將介紹怎么通過Spring實現這樣的自定義資源注入。
本文為原創,歡迎轉載,轉載請注明出處
BlogJava。