这里是想介绍一下如何通过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 extends Annotation> 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 extends Annotation> 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