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

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

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

需要使用到的技术:

1、使用@interface:实现对需要进行注入的对象使用注解进行标注

2、配合Java反射:实现对注解进行标注的,类、属性、方法,通过反射进行代理的操作权限处理

3、注解处理类:注解需要处理的业务操作,从而使注解在标注过程存在真实的意义

实现过程:

1、自定注解类:

package com.wangyong.learnproject.annotation;

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 的类型
     */
    String type() default "A";

    /*
     * sql-map-config文件的路径,用于加载iBatis的sqlMapClient对象
     */
    String sqlMap() default "";

    /*
     * 协议方式
     */
    DataSource.Protocol protocol() default DataSource.Protocol.DEFAULT;

    public static enum Protocol {
        DEFAULT,
        DUBBO,
        REST;

        private Protocol() {
        }
    }
}

2、自定义注解处理类的接口:(提供注解需要做的业务操作接口)

package com.wangyong.learnproject.annotation;

/*
 * 封装类业务处理说明:
 * 自定义注解需要进行注解业务处理的接口
 * 定义资源注入的接口IFieldWiring
 * 之所以这里要定义这个接口,是为了以后拓展用,我们很方便的定义更多的自定义注解
 */

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

public interface IFieldWiring {
    Class annotationClass();
    void wiring(Object object, Field field);
}

3、自定义注解处理类的业务处理服务类:(提供注解需要做的业务操作实际业务处理)

package com.wangyong.learnproject.annotation;

//import com.mchange.v2.reflect.ReflectUtils;

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

/*
 * 封装类业务处理说明:
 * 自定义注解进行业务处理的服务方法类
 * 通过实现自定义注解处理的接口的方法使其对外提供业务处理
 */

public class DataSourceWiring implements IFieldWiring {

    @Override
    public Class annotationClass() {
        return DataSource.class;
    }

    @Override
    public void wiring(Object object, Field field) {
        Object fileObj = ReflectUtils.getFieldValue(object,field.getName());
        if (fileObj != 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);
    }

}

4、Java反射操作工具:

package com.wangyong.learnproject.annotation;

import org.apache.commons.lang.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class ReflectUtils {

    /*
     * 取得字段值
     */
    public static Object getFieldValue(Object object, String fieldName){
        if (object == null || fieldName == null || "".equals(fieldName)){
            return null;
        }

        Class clazz = object.getClass();
        try{
            String methodname = "get"+ StringUtils.capitalize(fieldName);
            Method method = clazz.getDeclaredMethod(methodname);
            method.setAccessible(true);
            return method.invoke(object);
        }catch (Exception e){
            try {
                Field field = clazz.getDeclaredField(fieldName);
                field.setAccessible(true);
                return  field.get(object);
            }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();
            }
        }
    }
}

5、调用注解处理业务方法使注解生效

package com.wangyong.learnproject.annotation;

/*
 * 封装类业务处理说明:
 * 已经基本大功告成,只要将我们的DataSourceWiring.java类使用起来即可
 * MyAnnotationBeanProcessor.java,这个类主要用于为bean对象注入资源
 */

import java.lang.reflect.Field;
import java.util.List;

public class MyAnnotationBeanProcessor {

    /**
     * 函数业务处理含义
     * 注入资源
     *
     * @param serviceObject
     * @param fieldAutoWirings 所有实现IFieldWiring的接口的对象,我们可以在此扩展
     * @return
     */
    public  void wire(Object serviceObject, List 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;
                }
            }
        }
    }
}

6、需要使用注解进行注入(bean)的实体类

package com.wangyong.learnproject.annotation;

/*
 * 封装类业务处理说明:
 * 自定义sqlMapClient类,用来演示
 * 模拟数据操作的客户端bean
 * 需要注入的bean,通过自定义的注解进行注入
 */

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

import java.util.Map;

//@SupperssWarnings("unchecked")
public class SqlMapClient {
    private String type = null;
    private String sqlMap = null;

    public SqlMapClient(String type, String sqlMap) {
        this.type = type;
        this.sqlMap = sqlMap;
    }

    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();
    }
}

7、进行bean资源注入引用的服务类

package com.wangyong.learnproject.annotation;

/*
 * 封装类业务处理说明:
 * 自定义sqlMapClient类,用来演示
 * 模拟数据操作的客户端bean
 * 需要注入的bean,通过自定义的注解进行注入
 */

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

import java.util.Map;

//@SupperssWarnings("unchecked")
public class SqlMapClient {
    private String type = null;
    private String sqlMap = null;

    public SqlMapClient(String type, String sqlMap) {
        this.type = type;
        this.sqlMap = sqlMap;
    }

    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();
    }
}

8、启动测试类:

package com.wangyong.learnproject.annotation;

import java.util.ArrayList;
import java.util.List;

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

        MyService b = new MyService();
        List iFieldWiring = new ArrayList();
        iFieldWiring.add(new DataSourceWiring());
        processor.wire(b,iFieldWiring);

        System.out.println(b.selectForObjectFromA("",null));
        System.out.println(b.selectForObjectFromB("",null));
    }
}

运行结果:

SqlMapClient[sqlMap=A,type=com/annotation/sql-map-config-a.xml]
SqlMapClient[sqlMap=B,type=com/annotation/sql-map-config-B.xml]

Process finished with exit code 0

由执行结果可以说明DataSource资源已经被我们正确的注入了。
如果想扩展的话,只需要新建一个类实现IFieldWiring接口即可。假设叫InParamWiring.java,实现了接口定义的两个方法后,在使用的时候,只要用以下代码便可将资源注入了:

spring boot注解:SpringBoot之自定义注解(基于BeanPostProcessor接口实现)

原文引用:https://www.cnblogs.com/fallmoon/articles/2425690.html

你可能感兴趣的:(Java)