Java反射实战

简介: 背景使用spring-ldap操作LDAP,完成LdapTemplate设置后,进行多条件查询,因为需求是能够多条件查询,所以在使用ldapTemplate.search系列方法时,着重考虑了下面的重载实现,第二个参数直接写过滤条件,只要按过滤语法书写即可,胜任各种复杂查询。

背景

使用spring-ldap操作LDAP,完成LdapTemplate设置后,进行多条件查询,因为需求是能够多条件查询,所以在使用ldapTemplate.search系列方法时,着重考虑了下面的重载实现,第二个参数直接写过滤条件,只要按过滤语法书写即可,胜任各种复杂查询。

List<LdapUser> users = ldapTemplate.search("ou=People", "(&(objectClass=person)(smart-type=E1))", new AttributesMapper<LdapUser>() {
    @Override
    public LdapUser mapFromAttributes(Attributes attributes) throws NamingException {
        
        return null;
    }
});

但是这个查询的,不能使用spring-ldap的ODM,不能把LDAP返回的查询结果,直接转成POJO;也就是需要自己把Attributes attributes转成我需要的LdapUser实体对象。

LdapUser.java如下
import lombok.Getter;
import lombok.Setter;
import org.springframework.ldap.odm.annotations.Attribute;
import org.springframework.ldap.odm.annotations.Entry;
import org.springframework.ldap.odm.annotations.Id;
import javax.naming.Name;

@Setter
@Getter
@Entry(objectClasses = { "inetOrgPerson" ,"top","person" },base="ou=People")   // ou=Internal,   (只指定People,可按uid查询单个人员)
public class LdapUser extends BaseDTO  {

    @Id
    protected Name dn;

    @Attribute(name = "uid")
    protected String uid;

    @Attribute(name = "smart-type")
    protected String userType;

    @Attribute(name = "mail")
    protected String mail;

    @Attribute(name = "mobile")
    protected String mobile;

    @Attribute(name = "departmentNumber")
    protected String deptId;

    @Attribute(name = "departmentName")
    protected String deptName;
    // ...
    }
}

这个POJO使用了spring-ldap 的ODM注解,类和属性分别使用了@Entry
和@Attribute(name = "uid"),其中@Attribute 中的name则标识LDAP查询返回的Attributes 中的一个属性的名称。

下面要做的反射任务

就是读取到POJO 这个LdapUser实体类中所有有带Attribute的属性,拿取到name后,从查询返回结果的Attributes 中遍历拿到对应name的 值,利用反射,给bean设置属性值。实现如下:

List<LdapUser> users = ldapTemplate.search("ou=People", "(&(objectClass=person)(smart-type=E1))", new AttributesMapper<LdapUser>() {
            @Override
            public LdapUser mapFromAttributes(Attributes attributes) throws NamingException {
                LdapUser bean = null;
                if (LdapUser.class.isAnnotationPresent(Entry.class)) {//是否加@Entry
                    try {
                        bean = LdapUser.class.newInstance();
                    } catch (Exception e) {
                        throw new RuntimeException("反射创建PIJO[<T extends BaseDTO>]实例对象失败",e);
                    }

                    Field[] fields = LdapUser.class.getDeclaredFields();//拿到bean对应的属性
                    for (Field field : fields) {//遍历POJO 所有属性
                        boolean fieldHasAnno = field.isAnnotationPresent(Attribute.class);//类属性上是否加@Attribute(name = "smart-type")
                        if (fieldHasAnno) {
                            Attribute fieldAnno = field.getAnnotation(Attribute.class);
                            String name = fieldAnno.name();//注解的name值,如smart-type
                            for (NamingEnumeration attrEnumeration = attributes.getAll(); attrEnumeration.hasMore(); ) {
                                javax.naming.directory.Attribute attr = (javax.naming.directory.Attribute) attrEnumeration.next();
                                String ldapAttr = attr.getID();
                                if (ldapAttr.equals(name)) {
                                    String ldapValue = attr.get().toString();
                                    setProperty(bean, field.getName(), ldapValue);//反射:给bean设置属性值
                                } else {
                                    continue;
                                }
                            }
                        }
                    }
                } else {
                    throw new RuntimeException("PIJO[<T extends BaseDTO>]需使用@Entry注解");
                }
                return bean;
            }
        });


    /**
     * 反射:给bean设置属性值
     *
     * @param bean
     * @param name
     * @param value
     */
    private void setProperty(Object bean, String name, Object value) {
        String setterName = "set" + StringUtils.capitalize(name);
        Method setter;
        try {
            setter = bean.getClass().getMethod(setterName, value.getClass());
            setter.invoke(bean, value);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

因为考虑到通用性,就把这一部分反射的工作抽象出来,用泛型替换具体类型,
实现了一个通用的转化ConvertAttributesMapper

import org.apache.commons.lang3.StringUtils;
import org.springframework.ldap.core.AttributesMapper;
import org.springframework.ldap.odm.annotations.Attribute;
import org.springframework.ldap.odm.annotations.Entry;

import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attributes;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * 实现Attributes 转成POJO
 */
public class ConvertAttributesMapper<T extends BaseDTO> implements AttributesMapper {

    Class<T> clazz;

    public ConvertAttributesMapper(Class<T> clazz) {
        this.clazz = clazz;
    }

    @Override
    public T mapFromAttributes(Attributes attributes) throws NamingException {
        T bean = null;
        if (clazz.isAnnotationPresent(Entry.class)) {//是否加@Entry
            try {
                bean = clazz.newInstance();
            } catch (Exception e) {
                throw new RuntimeException("反射创建PIJO[<T extends BaseDTO>]实例对象失败",e);
            }

            Field[] fields = clazz.getDeclaredFields();//拿到bean对应的属性
            for (Field field : fields) {//遍历POJO 所有属性
                boolean fieldHasAnno = field.isAnnotationPresent(Attribute.class);//类属性上是否加@Attribute(name = "smart-type")
                if (fieldHasAnno) {
                    Attribute fieldAnno = field.getAnnotation(Attribute.class);
                    String name = fieldAnno.name();//注解的name值,如smart-type
                    for (NamingEnumeration attrEnumeration = attributes.getAll(); attrEnumeration.hasMore(); ) {
                        javax.naming.directory.Attribute attr = (javax.naming.directory.Attribute) attrEnumeration.next();
                        String ldapAttr = attr.getID();
                        if (ldapAttr.equals(name)) {
                            String ldapValue = attr.get().toString();
                            setProperty(bean, field.getName(), ldapValue);//反射:给bean设置属性值
                        } else {
                            continue;
                        }
                    }
                }
            }
        } else {
            throw new RuntimeException("PIJO[<T extends BaseDTO>]需使用@Entry注解");
        }
        return bean;
    }

    /**
     * 反射:给bean设置属性值
     *
     * @param bean
     * @param name
     * @param value
     */
    private void setProperty(Object bean, String name, Object value) {
        String setterName = "set" + StringUtils.capitalize(name);
        Method setter;
        try {
            setter = bean.getClass().getMethod(setterName, value.getClass());
            setter.invoke(bean, value);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

//使用样例
AttributesMapper attributesMapper = new ConvertAttributesMapper<LdapUser>(LdapUser.class);
List<LdapUser> users = ldapTemplate.search("ou=People", "(&(objectClass=person)(smart-type=E1))", attributesMapper);

ps:BaseDTO是一个空的抽象类。

目录
相关文章
|
3月前
|
存储 Java 开发者
Java Map实战:用HashMap和TreeMap轻松解决复杂数据结构问题!
【10月更文挑战第17天】本文深入探讨了Java中HashMap和TreeMap两种Map类型的特性和应用场景。HashMap基于哈希表实现,支持高效的数据操作且允许键值为null;TreeMap基于红黑树实现,支持自然排序或自定义排序,确保元素有序。文章通过具体示例展示了两者的实战应用,帮助开发者根据实际需求选择合适的数据结构,提高开发效率。
88 2
|
11天前
|
Java 数据库连接 Spring
反射-----浅解析(Java)
在java中,我们可以通过反射机制,知道任何一个类的成员变量(成员属性)和成员方法,也可以堆任何一个对象,调用这个对象的任何属性和方法,更进一步我们还可以修改部分信息和。
|
17天前
|
Java
Java基础却常被忽略:全面讲解this的实战技巧!
本次分享来自于一道Java基础的面试试题,对this的各种妙用进行了深度讲解,并分析了一些关于this的常见面试陷阱,主要包括以下几方面内容: 1.什么是this 2.this的场景化使用案例 3.关于this的误区 4.总结与练习
|
1月前
|
Java 程序员
Java基础却常被忽略:全面讲解this的实战技巧!
小米,29岁程序员,分享Java中`this`关键字的用法。`this`代表当前对象引用,用于区分成员变量与局部变量、构造方法间调用、支持链式调用及作为参数传递。文章还探讨了`this`在静态方法和匿名内部类中的使用误区,并提供了练习题。
34 1
|
2月前
|
监控 Java
Java基础——反射
本文介绍了Java反射机制的基本概念和使用方法,包括`Class`类的使用、动态加载类、获取方法和成员变量信息、方法反射操作、以及通过反射了解集合泛型的本质。同时,文章还探讨了动态代理的概念及其应用,通过实例展示了如何利用动态代理实现面向切面编程(AOP),例如为方法执行添加性能监控。
|
2月前
|
安全 Java 开发者
Java 多线程并发控制:深入理解与实战应用
《Java多线程并发控制:深入理解与实战应用》一书详细解析了Java多线程编程的核心概念、并发控制技术及其实战技巧,适合Java开发者深入学习和实践参考。
70 6
|
2月前
|
存储 安全 Java
Java多线程编程中的并发容器:深入解析与实战应用####
在本文中,我们将探讨Java多线程编程中的一个核心话题——并发容器。不同于传统单一线程环境下的数据结构,并发容器专为多线程场景设计,确保数据访问的线程安全性和高效性。我们将从基础概念出发,逐步深入到`java.util.concurrent`包下的核心并发容器实现,如`ConcurrentHashMap`、`CopyOnWriteArrayList`以及`BlockingQueue`等,通过实例代码演示其使用方法,并分析它们背后的设计原理与适用场景。无论你是Java并发编程的初学者还是希望深化理解的开发者,本文都将为你提供有价值的见解与实践指导。 --- ####
|
3月前
|
存储 消息中间件 安全
JUC组件实战:实现RRPC(Java与硬件通过MQTT的同步通信)
【10月更文挑战第9天】本文介绍了如何利用JUC组件实现Java服务与硬件通过MQTT的同步通信(RRPC)。通过模拟MQTT通信流程,使用`LinkedBlockingQueue`作为消息队列,详细讲解了消息发送、接收及响应的同步处理机制,包括任务超时处理和内存泄漏的预防措施。文中还提供了具体的类设计和方法实现,帮助理解同步通信的内部工作原理。
JUC组件实战:实现RRPC(Java与硬件通过MQTT的同步通信)
|
2月前
|
Java
Java的反射
Java的反射。
39 2
|
3月前
|
存储 Java
[Java]反射
本文详细介绍了Java反射机制的基本概念、使用方法及其注意事项。首先解释了反射的定义和类加载过程,接着通过具体示例展示了如何使用反射获取和操作类的构造方法、方法和变量。文章还讨论了反射在类加载、内部类、父类成员访问等方面的特殊行为,并提供了通过反射跳过泛型检查的示例。最后,简要介绍了字面量和符号引用的概念。全文旨在帮助读者深入理解反射机制及其应用场景。
43 0
[Java]反射