Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.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 这是注释处理的好模式吗?_Java_Design Patterns_Annotations - Fatal编程技术网

Java 这是注释处理的好模式吗?

Java 这是注释处理的好模式吗?,java,design-patterns,annotations,Java,Design Patterns,Annotations,我有一个相当标准的SpringWebApp,我有许多自定义注释,我想用它们来表示应用于给定web服务方法的需求和约束。例如,我可以对任何需要有效用户会话的方法应用@RequiresLogin注释,对需要设置“name”和“email”的方法应用@requireparameters(paramNames={“name”,“email”}),等等 为了支持这一点,我实现了一个临时实用程序,用于在运行时验证方法的注释约束,它基本上遵循以下模式: Map<Class<? extends An

我有一个相当标准的SpringWebApp,我有许多自定义注释,我想用它们来表示应用于给定web服务方法的需求和约束。例如,我可以对任何需要有效用户会话的方法应用
@RequiresLogin
注释,对需要设置“name”和“email”的方法应用
@requireparameters(paramNames={“name”,“email”})
,等等

为了支持这一点,我实现了一个临时实用程序,用于在运行时验证方法的注释约束,它基本上遵循以下模式:

Map<Class<? extends Annotation>, Annotation> annotations = mergeConstraintsFromClassAndMethod(serviceClass, serviceMethod);
if (annotations.containsKey(AnnotationType1.class)) {
    AnnotationType1 annotation = (AnnotationType1)annotations.get(AnnotationType1.class);
    //do validation appropriate to 'AnnotationType1'
}
if (annotations.containsKey(AnnotationType2.class)) {
    AnnotationType2 annotation = (AnnotationType2)annotations.get(AnnotationType2.class);
    //do validation appropriate to 'AnnotationType2'
}
//...
然后可以实现注释,如下所示:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface RequiresLogin {

    public static class Processor implements AnnotationProcessor {

        @Override
        public boolean processRequest(Annotation theAnnotation, HttpServletRequest request) {
            if (! (theAnnotation instanceof RequiresLogin)) {
                //someone made an invalid call, just return true
                return true;
            }
            return request.getSession().getAttribute(Constants.SESSION_USER_KEY) != null;
        }
    }
}
这使验证逻辑保持良好,并与正在验证的注释紧密耦合。然后,我的所有临时验证代码都可以替换为:

List<Annotation> annotations = mergeConstraintsFromClassAndMethod(serviceClass, serviceMethod);
for (Annotation annotation : annotations) {
    processAnnotation(annotation, request);
}


private static boolean processAnnotation(Annotation annotation, HttpServletRequest request) {
    AnnotationProcessor processor = null;
    for (Class<?> processorClass : annotation.annotationType().getDeclaredClasses()) {
        if (AnnotationProcessor.class.isAssignableFrom(processorClass)) {
            try {
                processor = (AnnotationProcessor)processorClass.newInstance();
                break;
            }
            catch (Exception ignored) {
                //couldn't create it, but maybe there is another inner 
                //class that also implements the required interface that 
                //we can construct, so keep going
            }
        }
    }
    if (processor != null) {
        return processor.processRequest(annotation, request);
    }

    //couldn't get a a processor and thus can't process the 
    //annotation, perhaps this annotation does not support
    //validation, return true
    return true;
}
List annotations=mergeConstraintsFromClassAndMethod(serviceClass,serviceMethod);
用于(注释:注释){
processAnnotation(注释、请求);
}
私有静态布尔processAnnotation(注释注释,HttpServletRequest){
AnnotationProcessor=null;
对于(类processorClass:annotation.annotationType().getDeclaredClasses()){
if(AnnotationProcessor.class.isAssignableFrom(processorClass)){
试一试{
processor=(AnnotationProcessor)processorClass.newInstance();
打破
}
捕获(忽略异常){
//无法创建它,但可能还有另一个内在的
//类,该类还实现了
//我们可以建造,所以继续前进
}
}
}
if(处理器!=null){
返回处理器.processRequest(注释,请求);
}
//无法获取处理器,因此无法处理
//注释,可能此注释不支持
//验证,返回true
返回true;
}
这样就不再需要在每次添加新注释类型时修改特别代码。我只是将验证器作为注释的一部分来实现,我就完成了


这似乎是一种合理的使用模式吗?如果没有,那么什么可能更好呢?

您可能需要调查AOP。您可以建议公开某些注释并相应地执行预/后处理的方法。

我只想补充一点,虽然AOP是一个很好的解决方案,但Spring框架已经通过@Secured注释提供了此功能

@Secured("ROLE_USER")
public void foo() {

}

Spring还支持带有@Valid注释的JSR-303验证。因此,至少在这些用例中,您似乎正在重新发明轮子。

我想,如何将访问者模式与工厂结合起来。工厂将返回一个包装器对象,该对象知道确切的注释类型,并且访问者将能够

class MyVisitor {
    public void visit(VisitableAnnotationType1 at) {
        //something AnnotationType1 specific
    }
    public void visit(VisitableAnnotationType2 at) {
        //something AnnotationType2 specific
    }
    ... // put methods for further annotation types here
}

class VisitableFactory {
    public abstract class VisitableAnnotation {
        public abstract void accept(MyVisitor visitor);
    }

    class VisitableAnnotationType1 implements VisitableAnnotation {
        public void accept(MyVisitor visitor) {
            visitor.visit(this);
        }
    }

    public static VisitableAnnotation getVisitable(Annotation a) {
        if(AnnotationType1.class.isAssignableFrom(a.getClass()) {
            //explicitely cast to the respective AnnotationType
            return new VisitableAnnotationType1((AnnotationType1)a);
        } else if (AnnotationType2.class.isAssignableFrom(a.getClass()) {
            //explicitely cast to the respective AnnotationType
            return new VisitableAnnotationType1((AnnotationType1)a);
        }
    }
}
由于无法扩展注释,我们需要在工厂中使用这些包装器类。您还可以传递原始注释,该注释随后包含在该包装器类中

您必须做的是:对于每个新的AnnotationType,向工厂添加一个新的“包装器”类,扩展工厂的

getVisitable()
方法,并根据访问者添加相应的方法:

public void doSomething(VisitableAnnotationTypeXYZ at) {
    //something AnnotationTypeXYZ specific
}
现在,通用验证(或其他)代码如下所示:

List<ValidatableAnnotation> annotations = mergeConstraintsFromClassAndMethod(serviceClass, serviceMethod);
MyVisitor visitor = new MyVisitor();
for (ValidatableAnnotation annotation : annotations) {
    VisitableFactory.getVisitable(annotation).accept(visitor);
}
List annotations=mergeConstraintsFromClassAndMethod(serviceClass,serviceMethod);
MyVisitor=新的MyVisitor();
用于(ValidatableAnnotation:annotations){
VisitableFactory.getVisitable(注释).accept(访问者);
}
访问通过间接方式工作,即被访问对象以自身为参数调用访问者,从而调用正确的访问方法。 希望有帮助;-)
代码没有经过测试,不过…

+1为了教我,你可以在注释中放一个内部类,这很有趣。我同意hoipolloi的观点,AOP是一个很好的解决方案,它还可以让您不必在每个带注释的方法的顶部调用验证方法。@OpenSauce-在每个带注释的方法的开头都没有调用验证方法。这样做有点违背了这样做的目的。验证作为顶级Spring控制器(
DispatcherServlet
)的一部分执行,该控制器充当带注释服务API的网关。其中一个调用是在允许请求进入服务API之前运行请求前验证,另一个调用是在呈现服务API返回的
ModelAndView
之前运行请求后验证,就是这样。AOP是一个选项,因为我构建的基本上是一个AOP框架。但第三方库能否正确处理注释的“继承”之类的事情?利用我现在所拥有的,我可以用类似于
@RequiresLogin
的东西来注释一个类,并且该类中的每个方法的行为都会像用
@RequiresLogin
注释一样,除非它们显式重写“继承的”注释。(很抱歉重复评论,我注意到了几个令人讨厌的打字错误,我觉得必须纠正一下)@aroth:总之,是的。可以为具有特定注释的类中的所有方法定义切入点。有关更多信息,请参见这个问题:对,但是如果我希望一个类中的所有方法都使用特定的注释进行注释,除了使用其他注释进行注释的方法之外,它会处理这个问题吗?不用我列举所有可能的排列?例如,我用
@RequiresLogin
注释
ClassA
,然后在
方法1
方法2
上我没有注释,在
方法3
上我有
@NoInheritance
,我想匹配
方法1
方法2
,但不匹配
方法3
。虽然看起来比我喜欢的要迟钝一点。@aroth:取决于您的AOP实现。作为
public void doSomething(VisitableAnnotationTypeXYZ at) {
    //something AnnotationTypeXYZ specific
}
List<ValidatableAnnotation> annotations = mergeConstraintsFromClassAndMethod(serviceClass, serviceMethod);
MyVisitor visitor = new MyVisitor();
for (ValidatableAnnotation annotation : annotations) {
    VisitableFactory.getVisitable(annotation).accept(visitor);
}