我有一个带有ThreadLocal变量的上下文类,我想用它来存储数据。
LDAPAttributesContextHolder

public class LDAPAttributesContextHolder {

    private static final ThreadLocal<List<Attributes>> threadLocalScope = new ThreadLocal<>();

    private LDAPAttributesContextHolder() {
        throw new IllegalStateException("ThreadLocal context class");
    }

    public static final List<Attributes> getAttributes() {
        return threadLocalScope.get();
    }

    public static final void setAttributes(List<Attributes> attributes) {
        threadLocalScope.set(attributes);
    }

    public static final void destroy() {
        threadLocalScope.remove();
    }
}


我使用此类存储用户属性,并将其用于其他服务。
服务1

@Override
    public boolean searchInLDAP(String userName, String email) {
        LOG.debug("Current thread is {}", Thread.currentThread().getName());
        LOG.debug("Start search user with login {} and email {} in LDAP directory", userName, email);
        List<Attributes> attributeList = new ArrayList<>();
        if(isEmpty(LDAPAttributesContextHolder.getAttributes())) {
            attributeList = ldapTemplate.search(query().base("ou=people").where("uid").is(userName).and("mail").is(email),
                    (AttributesMapper<Attributes>) attributes -> {
                        if(attributes == null) {
                            return null;
                        }
                        return attributes;
                    });
            LDAPAttributesContextHolder.setAttributes(attributeList);
        }
        LOG.debug("Status of searching user with login {} and email {} in LDAP is {}", userName, email, (!isEmpty(attributeList)) ? "success" : "failed");
        if(nonNull(attributeList) && !isEmpty(attributeList)) {
            logAttributes(userName);
        }
        return nonNull(attributeList) && !isEmpty(attributeList);
    }


服务2

public List<String> getAllFacultyGroupNamesByFacultyName() {
        String studentFacultyName = "";
        LOG.debug("Current thread is {}", Thread.currentThread().getName());
        LOG.debug("LDAPContextHolder size {}", LDAPAttributesContextHolder.getAttributes().size());
        List<Attributes> attributeList = LDAPAttributesContextHolder.getAttributes();
        LOG.debug("In method {} ,Size of attributes is  {}", Thread.currentThread().getStackTrace()[0].getMethodName(), attributeList.size());
        for(Attributes attributes : attributeList) {
            try {
                if(attributes.get(FACULTY_ATTRIBUTE) != null &&
                        attributes.get(ROLE_ATTRIBUTE) != null &&
                        !attributes.get(ROLE_ATTRIBUTE).get().toString().equals(ORGANIZATIONAL_PERSON)
                ) {
                    studentFacultyName = attributes.get(FACULTY_ATTRIBUTE).get().toString();
                    studentFacultyName = studentFacultyName.contains(IT_FACULTY.toLowerCase()) ? IT_FACULTY : studentFacultyName;
                    LOG.debug("Student faculty is {}", studentFacultyName);
                }
            } catch(NamingException e) {
                LOG.error("Error while parsing LDAP attributes. {}", e);
            }
        }

        return ...;
    }


问题在于,在第一个方法线程中为120,但在第二个线程中由于某种原因,线程为115,当我尝试获取上下文时,它抛出NullPointer。
我错过了什么?

最佳答案

ThreadLocal保存一个引用per thread。因此,除非每个线程对其进行初始化,否则您将获得空指针异常。从Javadoc:


  这些变量与普通变量不同,因为每个访问一个线程(通过其getset方法)的线程都有其自己的,独立初始化的变量副本。


听起来像您想要单例模式。由于您使用的是Spring Boot,因此可以创建类型为LDAPAttributesContextHolder的bean,并将其自动连接到我们使用过的组件/服务中。

关于java - Tomcat的ThreadLocal,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/55401895/

10-13 03:42