ALL is Well!

敏捷是一条很长的路,摸索着前进着

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  30 随笔 :: 23 文章 :: 71 评论 :: 0 Trackbacks
这里是想介绍一下如何通过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
posted on 2010-10-04 10:19 李 明 阅读(5334) 评论(0)  编辑  收藏 所属分类: Java

只有注册用户登录后才能发表评论。


网站导航: