`

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

    博客分类:
  • Java
 
阅读更多

这里是想介绍一下如何通过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 {
    
    Classextends
 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 Classextends
 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的自动注入实现。

 
一个不错的注解自定义的实践,转自:http://www.blogjava.net/jnbzwm/archive/2010/10/04/333720.html
分享到:
评论

相关推荐

    java 元注解+拦截器实现自定义注解.rar

    java 元注解+拦截器实现自定义注解 @CmwAutoWired:自定义依赖注入 注意:注入的接口和实现类需要在同一包名下,注解的是类则无限制 @FieldAnnotation:自定义属性注解 @MethodAnnotation:自定义方法注解 @...

    自定义注解实现拦截sql.rar

    自定义注解实现拦截sql,并在sql中增加相应的条件 。 对应博客: https://blog.csdn.net/qq_37716298/article/details/120659139

    java SpringBoot自定义注解,及自定义解析器实现对象自动注入操作

    主要介绍了java SpringBoot自定义注解,及自定义解析器实现对象自动注入操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

    IoC容器的设计(利用反射、注解和工厂模式实现)

    实验要求 1. 利用注解、反射和工厂模式设计一个简单的IoC容器 2. 该IoC容器包含3个...然后我们需要自己实现一个IoC容器类,处理自定义的注解的基本逻辑; 接下来在test类中实例化IoC容器,并从中取得bean,调用其方法

    ViewInject:类似XUtils的View的注解模块,实现布局加载注入,view注入,view监听事件注入

    功能1和2通过反射获取到自定义的注解,解析注解,通过反射调用设置布局的setContentView和findViewById方法来实现; 功能3设置监听事件,除了需要使用反射获取信息外,还需要运用动态代理,来代理view对象,才能调用...

    Java资源包01

    WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...

    JAVA上百实例源码以及开源项目源代码

     Java 3DMenu 界面源码,有人说用到游戏中不错,其实平时我信编写Java应用程序时候也能用到吧,不一定非要局限于游戏吧,RES、SRC资源都有,都在压缩包内。 Java zip压缩包查看程序源码 1个目标文件 摘要:Java源码...

    JAVA上百实例源码以及开源项目

     Java 3DMenu 界面源码,有人说用到游戏中不错,其实平时我信编写Java应用程序时候也能用到吧,不一定非要局限于游戏吧,RES、SRC资源都有,都在压缩包内。 Java zip压缩包查看程序源码 1个目标文件 摘要:Java源码...

    java开源包4

    WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...

    java开源包3

    WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...

    java开源包101

    WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...

    java开源包11

    WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...

    java开源包6

    WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...

    java开源包9

    WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...

    java开源包5

    WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...

    java开源包8

    WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...

    java开源包10

    WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...

    SpringBoot发送邮件神器,只需简单配置即可,支持自定义模板.zip

    Spring框架利用容器管理对象的生命周期,容器可以通过扫描XML文件或类上特定Java注解来配置对象,开发者可以通过依赖查找或依赖注入来获得对象。Spring框架具有面向切面编程(AOP)框架,SpringAOP框架基于代理模式...

Global site tag (gtag.js) - Google Analytics