Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/jsp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java &引用;在类型“上找不到属性”;在JSP EL中使用接口默认方法时_Java_Jsp_Java 8_El_Default Method - Fatal编程技术网

Java &引用;在类型“上找不到属性”;在JSP EL中使用接口默认方法时

Java &引用;在类型“上找不到属性”;在JSP EL中使用接口默认方法时,java,jsp,java-8,el,default-method,Java,Jsp,Java 8,El,Default Method,考虑以下接口: public interface I { default String getProperty() { return "..."; } } 以及仅重新使用默认实现的实现类: public final class C implements I { // empty } 每当在JSP EL脚本上下文中使用C的实例时: <jsp:useBean id = "c" class = "com.example.C" scope = "requ

考虑以下接口:

public interface I {
    default String getProperty() {
        return "...";
    }
}
以及仅重新使用默认实现的实现类:

public final class C implements I {
    // empty
}
每当在JSP EL脚本上下文中使用
C
的实例时:

<jsp:useBean id = "c" class = "com.example.C" scope = "request"/>
${c.property}
我最初的想法Tomcat6.0对于Java1.8功能来说太旧了,但我惊讶地看到Tomcat8.0也受到了影响。当然,我可以通过显式调用默认实现来解决这个问题:

    @Override
    public String getProperty() {
        return I.super.getProperty();
    }
--但为什么默认方法对Tomcat来说是个问题呢

更新:进一步的测试显示无法找到默认属性,而默认方法可以,因此另一个解决方法(Tomcat 7+)是:


${c.getProperty()}

您可以通过创建处理默认方法的自定义
ELResolver
实现来解决此问题。我在这里所做的实现扩展了
SimpleSpringBeanELResolver
。这是ELResolver的Springs实现,但是没有Spring,同样的想法应该是一样的

此类查找在bean接口上定义的bean属性签名,并尝试使用它们。如果在接口上找不到bean prop签名,它将继续沿着默认行为链发送它

import org.apache.commons.beanutils.PropertyUtils;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.access.el.SimpleSpringBeanELResolver;

import javax.el.ELContext;
import javax.el.ELException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.util.Optional;
import java.util.stream.Stream;

/**
 * Resolves bean properties defined as default interface methods for the ELResolver.
 * Retains default SimpleSpringBeanELResolver for anything which isn't a default method.
 *
 * Created by nstuart on 12/2/2016.
 */
public class DefaultMethodELResolver extends SimpleSpringBeanELResolver {
    /**
     * @param beanFactory the Spring BeanFactory to delegate to
     */
    public DefaultMethodELResolver(BeanFactory beanFactory) {
        super(beanFactory);
    }

    @Override
    public Object getValue(ELContext elContext, Object base, Object property) throws ELException {

        if(base != null && property != null) {
            String propStr = property.toString();
            if(propStr != null) {
                Optional<Object> ret = attemptDefaultMethodInvoke(base, propStr);
                if (ret != null) {
                    // notify the ELContext that our prop was resolved and return it.
                    elContext.setPropertyResolved(true);
                    return ret.get();
                }
            }
        }

        // delegate to super
        return super.getValue(elContext, base, property);
    }

    /**
     * Attempts to find the given bean property on our base object which is defined as a default method on an interface.
     * @param base base object to look on
     * @param property property name to look for (bean name)
     * @return null if no property could be located, Optional of bean value if found.
     */
    private Optional<Object> attemptDefaultMethodInvoke(Object base, String property) {
        try {
            // look through interfaces and try to find the method
            for(Class<?> intf : base.getClass().getInterfaces()) {
                // find property descriptor for interface which matches our property
                Optional<PropertyDescriptor> desc = Stream.of(PropertyUtils.getPropertyDescriptors(intf))
                        .filter(d->d.getName().equals(property))
                        .findFirst();

                // ONLY handle default methods, if its not default we dont handle it
                if(desc.isPresent() && desc.get().getReadMethod() != null && desc.get().getReadMethod().isDefault()) {
                    // found read method, invoke it on our object.
                    return Optional.ofNullable(desc.get().getReadMethod().invoke(base));
                }
            }
        } catch (InvocationTargetException | IllegalAccessException e) {
            throw new RuntimeException("Unable to access default method using reflection", e);
        }

        // no value found, return null
        return null;
    }

}
我不是100%确定,如果这是适当的地方添加我们的解析器,但它确实工作得很好。您还可以在
javax.servlet.ServletContextListener.contextInitialized


以下是参考资料中的
ELResolver

我猜内省不适用于接口默认方法?我对答案很感兴趣:)你试过添加注释@FunctionInterface吗?@rickz:没有,原因有两个:1 IRL,我的接口有不止一个方法(因此没有资格被注释),2
@FunctionInterface
有不同的范围(几乎从未与
默认方法一起使用):通常没有默认实现,并且有很多匿名实现。我已经厌倦了IntelliJ IDEA礼貌地提醒我,每次我碰巧声明一个方法接口时,我都应该用
@functioninterface
为接口添加注释(参见相关内容):/我还没有尝试过这个方法,但是如果覆盖了默认方法,这个解析器会返回覆盖的值还是默认值?我希望它返回被覆盖的值。@battmanz我猜它应该返回被覆盖的值。然而,我没有一个快速的方法来测试这一点。如果没有,那么您可以随时更改属性解析器的行为。
<jsp:useBean id = "c" class = "com.example.C" scope = "request"/>
<%-- ${c.property} --%>
${c.getProperty()}
import org.apache.commons.beanutils.PropertyUtils;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.access.el.SimpleSpringBeanELResolver;

import javax.el.ELContext;
import javax.el.ELException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.util.Optional;
import java.util.stream.Stream;

/**
 * Resolves bean properties defined as default interface methods for the ELResolver.
 * Retains default SimpleSpringBeanELResolver for anything which isn't a default method.
 *
 * Created by nstuart on 12/2/2016.
 */
public class DefaultMethodELResolver extends SimpleSpringBeanELResolver {
    /**
     * @param beanFactory the Spring BeanFactory to delegate to
     */
    public DefaultMethodELResolver(BeanFactory beanFactory) {
        super(beanFactory);
    }

    @Override
    public Object getValue(ELContext elContext, Object base, Object property) throws ELException {

        if(base != null && property != null) {
            String propStr = property.toString();
            if(propStr != null) {
                Optional<Object> ret = attemptDefaultMethodInvoke(base, propStr);
                if (ret != null) {
                    // notify the ELContext that our prop was resolved and return it.
                    elContext.setPropertyResolved(true);
                    return ret.get();
                }
            }
        }

        // delegate to super
        return super.getValue(elContext, base, property);
    }

    /**
     * Attempts to find the given bean property on our base object which is defined as a default method on an interface.
     * @param base base object to look on
     * @param property property name to look for (bean name)
     * @return null if no property could be located, Optional of bean value if found.
     */
    private Optional<Object> attemptDefaultMethodInvoke(Object base, String property) {
        try {
            // look through interfaces and try to find the method
            for(Class<?> intf : base.getClass().getInterfaces()) {
                // find property descriptor for interface which matches our property
                Optional<PropertyDescriptor> desc = Stream.of(PropertyUtils.getPropertyDescriptors(intf))
                        .filter(d->d.getName().equals(property))
                        .findFirst();

                // ONLY handle default methods, if its not default we dont handle it
                if(desc.isPresent() && desc.get().getReadMethod() != null && desc.get().getReadMethod().isDefault()) {
                    // found read method, invoke it on our object.
                    return Optional.ofNullable(desc.get().getReadMethod().invoke(base));
                }
            }
        } catch (InvocationTargetException | IllegalAccessException e) {
            throw new RuntimeException("Unable to access default method using reflection", e);
        }

        // no value found, return null
        return null;
    }

}
@Configuration
...
public class SpringConfig extends WebMvcConfigurationSupport {
    ...
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        ...
        // add our default method resolver to our ELResolver list.
        JspApplicationContext jspContext = JspFactory.getDefaultFactory().getJspApplicationContext(getServletContext());
        jspContext.addELResolver(new DefaultMethodELResolver(getApplicationContext()));
    }
}