这里是想介绍一下如何通过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));
- }
- }
执行结果:
由执行结果可以说明DataSource资源已经被我们正确的注入了。
如果想扩展的话,只需要新建一个类实现IFieldWiring接口即可。假设叫InParamWiring.java,实现了接口定义的两个方法后,在使用的时候,只要用以下代码便可将资源注入了:
注:以上代码重在演示,其实这个需求可以在Spring中管理两个不同的SqlMapClient对象,然后通过Spring的自动注入实现。
下一篇将介绍怎么通过Spring实现这样的自定义资源注入。
转载地址:http://www.fengfly.com/plus/view-191036-1.html