转自:http://www.blogjava.net/jnbzwm/archive/2010/10/04/333720.html
这里是想介绍一下如何通过Java的注解机制,实现对bean资源的注入。主要介绍实现的方法,至于例子的实用性不必讨论。
需求:一个应用有两个数据库,分别为DB-A,DB-B。
假设持久层框架使用iBatis来实现,那么SqlMapClient对象在创建时,对于两个不同的DB连接要有两个不同的SqlMapClient对象,
假设我们有一个Service类为MyService.java,该类中有两个SqlMapClient对象sqlMapA、sqlMapB分别对应着DB-A、DB-B。
先看看我们的SqlMapClient.java类:(自定义SqlMapClient类,用来演示。)
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类实现:
@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.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。
之所以这里要定义这个接口,是为了以后扩展用,我们很方便的定义更多的自定义注解。
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.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.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对象注入资源。
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 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-A.xml,type=A]
由执行结果可以说明DataSource资源已经被我们正确的注入了。
如果想扩展的话,只需要新建一个类实现IFieldWiring接口即可。假设叫InParamWiring.java,实现了接口定义的两个方法后,在使用的时候,只要用以下代码便可将资源注入了:
MyService b = new MyService();
processor.wire(b, new DataSourceWiring(), new InParamWiring()); // 注入DataSource、InParam资源
注:以上代码重在演示,其实这个需求可以在Spring中管理两个不同的SqlMapClient对象,然后通过Spring的自动注入实现。
下一篇将介绍怎么通过Spring实现这样的自定义资源注入。
相关推荐
以上就是利用自定义注解实现SQL拦截的基本原理和步骤,这个技术在处理复杂业务逻辑或者权限控制时非常有用,但同时也需要注意其潜在的风险和挑战。在实际项目中,应根据需求和项目规模合理使用。
java 元注解+拦截器实现自定义注解 @CmwAutoWired:自定义依赖注入 注意:注入的接口和实现类需要在同一包名下,注解的是类则无限制 @FieldAnnotation:自定义属性注解 @MethodAnnotation:自定义方法注解 @...
在Android开发中,自定义注解是一种非常有用的工具,它能帮助我们简化代码,提高代码的可读性和可...同时,这也将帮助你掌握如何编写和使用注解处理器,以及如何在Android项目中有效地利用自定义注解来优化代码结构。
要模拟实现这个功能,我们需要创建一个自定义注解,例如`@MyAutowired`,然后编写一个处理该注解的后处理器,使用Java的反射API来查找和注入依赖。 ```java @Retention(RetentionPolicy.RUNTIME) @Target(Element...
Java SpringBoot 自定义注解及自定义解析器实现对象自动注入操作 在 Java SpringBoot 框架中,自定义注解和自定义解析器是非常重要的概念,它们可以帮助开发者轻松地实现对象自动注入操作。下面,我们将详细介绍 ...
Java自定义注解和通过反射获取注解是Java编程中重要的高级特性,它们极大地增强了代码的可读性和可维护性。注解(Annotation)是一种元数据,提供了在编译时和运行时对代码进行标记的方法,而反射(Reflection)则是...
Spring框架充分利用了Java注解的功能,简化了配置过程,并提供了许多内置的注解来支持依赖注入、事务管理等功能。以下是一些常用的Spring注解: 1. **@Component** - 用于标记一个类作为Spring管理的Bean。 - ...
Java自定义注解是Java平台提供的一种元编程机制,它允许程序员在代码中插入特定的标记,这些标记可以在编译时或运行时被处理器解析,从而实现特定的功能。自定义注解可以用来增强代码的可读性,简化代码处理,以及...
为了解决这些问题,我们可以采用自定义注解实现Inversion of Control(IoC,控制反转)和Dependency Injection(DI,依赖注入)来简化代码,提高可读性和可测试性。本文将深入探讨如何通过自定义注解来实现这一目标...
// 运行时注解注入控件 initViews(); } private void initViews() { // 使用Java反射获取所有被@BindView注解的字段 Field[] fields = this.getClass().getDeclaredFields(); for (Field field : fields) { ...
### Java自定义注解Annotation的使用 #### 1. 前言 自从JDK 1.5引入了注解这一特性以来,它已经成为Java开发中的一个重要组成部分。注解最初是为了推动EJB 3.0的普及和发展而设计的,其目的是减少配置文件的使用,...
Java自定义注解和Spring的BeanPostProcessor是Java企业级开发中的两个重要概念,它们在构建灵活、可扩展的应用程序中发挥着关键作用。本文将深入探讨这两个话题,并结合源码分析,帮助开发者更好地理解和应用。 ...
本文将深入探讨如何在Spring环境中通过`component-scan`配置来处理自定义Java注解。 首先,让我们了解什么是自定义注解。在Java中,注解是一种元数据,可以提供有关代码的信息,但不直接影响代码的执行。自定义注解...
Java自定义注解是Java平台提供的一种元数据机制,它允许程序员在代码中添加额外的信息,这些信息可以被编译器、JVM或其他工具在编译时或运行时读取,用于实现各种功能,如代码生成、代码分析、依赖注入等。自定义...
自定义注解允许我们创建自己的标记,用于特定目的,比如代码分析、验证、依赖注入等。 要创建自定义注解,我们需要使用`@interface`关键字。例如,我们可以定义一个名为`MyComponent`的注解: ```java import java...
例如,在Spring框架中,可以使用自定义注解来实现组件扫描和依赖注入。 总的来说,Java自定义注解是一种强大的工具,能够帮助我们编写更清晰、更具可维护性的代码。通过定义、使用和解析自定义注解,我们可以为代码...
在本例中,我们定义了一个名为 InfoResolver 的注解处理类,该类使用自定义的注解 @InfoAnnotation 并将值注入参数。 ```java public class InfoResolver implements HandlerMethodArgumentResolver { @Override ...
- **框架集成**:例如Spring框架利用注解进行依赖注入。 - **编译时处理**:通过注解处理器自动生成代码。 - **运行时处理**:通过反射机制访问注解信息,在运行时动态改变程序行为。 #### 三、常见注解示例 - **`...
本教程以“superMarket”为例,深入探讨如何在Java中实现自定义注解。 首先,我们需要了解注解的基本结构。自定义注解以`@interface`关键字开始,然后是注解的名称,通常遵循驼峰命名规则。例如,我们可以创建一个...
综上所述,使用Java自定义注解来拼接SQL语句是一种高效且灵活的编程方式,它可以提高代码的可读性和可维护性,同时减少错误的可能性。然而,也需要权衡其带来的性能影响,并遵循最佳实践来确保代码的质量和安全。