利用自定义Java注解实现资源注入

这里是想介绍一下如何通过Java的注解机制,实现对bean资源的注入。主要介绍实现的方法,至于例子的实用性不必讨论。
需求:一个应用有两个数据库,分别为DB-A,DB-B。
假设持久层框架使用iBatis来实现,那么SqlMapClient对象在创建时,对于两个不同的DB连接要有两个不同的SqlMapClient对象,
假设我们有一个Service类为MyService.java,该类中有两个SqlMapClient对象sqlMapA、sqlMapB分别对应着DB-A、DB-B。

先看看我们的SqlMapClient.java类:(自定义SqlMapClient类,用来演示。)
 

  
  
  
  
  1. import java.util.Map;  
  2.  
  3. import org.apache.commons.lang.builder.ToStringBuilder;  
  4. import org.apache.commons.lang.builder.ToStringStyle;  
  5.  
  6. @SuppressWarnings("unchecked")  
  7. public class SqlMapClient {  
  8.     public SqlMapClient(String s, String t) {  
  9.         sqlMap = s;  
  10.         type = t;  
  11.     }  
  12.       
  13.     public SqlMapClient() {  
  14.     }  
  15.  
  16.     private String type   = null;  
  17.  
  18.     private String sqlMap = null;  
  19.     // get、set方法 略  
  20.  
  21.     // 用于演示查询后返回一个String的返回结果  
  22.     public String selectForObject(String sql, Map in) {  
  23.         return this.toString();  
  24.     }  
  25.       
  26.     @Override 
  27.     public String toString() {  
  28.         return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).append("sqlMap", sqlMap)  
  29.         .append("type", type).toString();  
  30.     }  


MyService.java类实现:
 

  
  
  
  
  1. import java.util.Map;  
  2.  
  3. @SuppressWarnings("unchecked")  
  4. public class MyService {  
  5.     @DataSource(type="B", sqlMap="com/annotation/sql-map-config-B.xml")  
  6.     private SqlMapClient sqlMapB = null;  
  7.       
  8.     @DataSource(type="A", sqlMap="com/annotation/sql-map-config-A.xml")  
  9.     private SqlMapClient sqlMapA = null;  
  10.       
  11.     // get、set方法 略  
  12.  
  13.     // 模拟在DB-B数据库取得数据  
  14.     public String selectForObjectFromB(String sql, Map in) {  
  15.         return sqlMapB.selectForObject(""null);  
  16.     }  
  17.       
  18.     // 模拟在DB-A数据库取得数据  
  19.     public String selectForObjectFromA(String sql, Map in) {  
  20.         return sqlMapA.selectForObject(""null);  
  21.     }  


接下来就是我们的注解类:DataSource.java
 

  
  
  
  
  1. import java.lang.annotation.ElementType;  
  2. import java.lang.annotation.Retention;  
  3. import java.lang.annotation.RetentionPolicy;  
  4. import java.lang.annotation.Target;  
  5.  
  6. @Target(ElementType.FIELD)  
  7. @Retention(RetentionPolicy.RUNTIME)  
  8. public @interface DataSource {  
  9.     /** *//**  
  10.      * Dao的类型  
  11.      * @return  
  12.      */ 
  13.     String type() default "A"// 连接的数据库类型 A or B  
  14.       
  15.     String sqlMap() default ""// Sql-Map-Config文件的路径,用于加载iBatis的SqlMapClient对象  


定义资源注入的接口 IFieldWiring.java。
之所以这里要定义这个接口,是为了以后扩展用,我们很方便的定义更多的自定义注解。
 

  
  
  
  
  1. IFieldWiring.java  
  2.  
  3. import java.lang.annotation.Annotation;  
  4. import java.lang.reflect.Field;  
  5.  
  6. public interface IFieldWiring {  
  7.       
  8.     Class<? extends Annotation> annotationClass();  
  9.       
  10.     void wiring(Object object, Field field);  


IFieldWiring.java的实现类----DataSourceWiring.java。(该类实现只为演示用,有很多地方是可以改进的)
 

  
  
  
  
  1. import java.lang.annotation.Annotation;  
  2. import java.lang.reflect.Field;  
  3.  
  4. public class DataSourceWiring implements IFieldWiring{  
  5.  
  6.     @Override 
  7.     public void wiring(Object object, Field field) {  
  8.         Object fieldObj = ReflectUtils.getFieldValue(object, field.getName()); // 获得field对应的对象  
  9.         if (fieldObj != null) {  
  10.             return;  
  11.         }  
  12.         DataSource annotation = field.getAnnotation(DataSource.class);  
  13.         String type = annotation.type();  
  14.         String sqlMap = annotation.sqlMap();  
  15.         // 这里可以用缓存来实现,不用每次都去创建新的SqlMapClient对象  
  16.         SqlMapClient sqlMapImpl = new SqlMapClient(sqlMap, type);  
  17.         // 将生成SqlMapClient注入到bean对象的字段上  
  18.         ReflectUtils.setFieldValue(object, field.getName(), SqlMapClient.class, sqlMapImpl);  
  19.     }  
  20.  
  21.     @Override 
  22.     public Class<? extends Annotation> annotationClass() {  
  23.         return DataSource.class;  
  24.     }  


这里的ReflectUtils.java 也是我们自定义的,并非有Spring提供的:
 

  
  
  
  
  1. import java.lang.reflect.Field;  
  2. import java.lang.reflect.Method;  
  3.  
  4. import org.apache.commons.lang.StringUtils;  
  5.  
  6. public class ReflectUtils {  
  7.  
  8.     /** *//**  
  9.      * 取得字段值  
  10.      *   
  11.      * @param obj  
  12.      * @param fieldName  
  13.      * @return  
  14.      */ 
  15.     public static Object getFieldValue(Object obj, String fieldName) {  
  16.         if (obj == null || fieldName == null || "".equals(fieldName)) {  
  17.             return null;  
  18.         }  
  19.  
  20.         Class<?> clazz = obj.getClass();  
  21.         try {  
  22.             String methodname = "get" + StringUtils.capitalize(fieldName);  
  23.             Method method = clazz.getDeclaredMethod(methodname);  
  24.             method.setAccessible(true);  
  25.             return method.invoke(obj);  
  26.         } catch (Exception e) {  
  27.             try {  
  28.                 Field field = clazz.getDeclaredField(fieldName);  
  29.                 field.setAccessible(true);  
  30.                 return field.get(obj);  
  31.             } catch (Exception e1) {  
  32.                 e1.printStackTrace();  
  33.             }  
  34.         }  
  35.         return null;  
  36.     }  
  37.  
  38.     public static void setFieldValue(Object target, String fname, Class<?> fieldClass,  
  39.         Object fieldObj) {  
  40.         if (!fieldClass.isAssignableFrom(fieldObj.getClass())) {  
  41.             return;  
  42.         }  
  43.         Class<?> clazz = target.getClass();  
  44.         try {  
  45.             Method method = clazz.getDeclaredMethod("set" + Character.toUpperCase(fname.charAt(0))  
  46.                 + fname.substring(1), fieldClass);  
  47.             method.setAccessible(true);  
  48.             method.invoke(target, fieldObj);  
  49.         } catch (Exception e) {  
  50.             try {  
  51.                 Field field = clazz.getDeclaredField(fname);  
  52.                 field.setAccessible(true);  
  53.                 field.set(target, fieldObj);  
  54.             } catch (Exception e1) {  
  55.                 e1.printStackTrace();  
  56.             }  
  57.         }  
  58.     }  


已经基本大功告成了,只要将我们的DataSourceWiring.java类使用起来即可。
MyAnnotationBeanProcessor.java,这个类主要用于为bean对象注入资源。
 

  
  
  
  
  1. import java.lang.reflect.Field;  
  2.  
  3. public class MyAnnotationBeanProcessor {  
  4.  
  5.     /** *//**  
  6.      * 注入资源  
  7.      * @param serviceObject  
  8.      * @param fieldAutoWirings // 所有实现IFieldWiring的接口的对象,我们可以在此扩展  
  9.      * @throws Exception  
  10.      */ 
  11.     public void wire(Object serviceObject, IFieldWiring fieldAutoWirings)  
  12.             throws Exception {  
  13.         Class<?> cls = serviceObject.getClass();  
  14.         for (Field field : cls.getDeclaredFields()) {  
  15.             for (IFieldWiring fieldAutoWiring : fieldAutoWirings) {  
  16.                 if (field.isAnnotationPresent(fieldAutoWiring.annotationClass())) {  
  17.                     fieldAutoWiring.wiring(serviceObject, field);  
  18.                     break;  
  19.                 }  
  20.             }  
  21.         }  
  22.     }  


好了,开始我们的测试类:FieldWiringTest.java
 

  
  
  
  
  1. public class FieldWiringTest {  
  2.     public static void main(String args[]) throws Exception {  
  3.         MyAnnotationBeanProcessor processor = new MyAnnotationBeanProcessor();  
  4.  
  5.         MyService b = new MyService();  
  6.  
  7.         processor.wire(b, new DataSourceWiring()); // 注入DataSource资源  
  8.           
  9.         System.out.println(b.selectForObjectFromB(""null));  
  10.         System.out.println(b.selectForObjectFromA(""null));  
  11.     }  


执行结果:
 

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实现这样的自定义资源注入。

转载地址:http://www.fengfly.com/plus/view-191036-1.html

你可能感兴趣的:(java,注解,属性注入)