【问题标题】:Wrap method implementations of Java interfaces包装Java接口的方法实现
【发布时间】:2018-12-13 22:03:39
【问题描述】:

我有多个接口,每个接口都定义了多个方法,如下所示:

public interface X {
    void methodX1;

    void methodX2(String s);
}

public interface Y {
    void methodY1;

    void methodY2(int i);

    void methodY3(SomeType s);
}

....

目前方法实现如下:

public class XImpl implements X {
        public void methodX1() {
            // method implementation
        }
    }

对于接口的每个实现,我需要用try-with-resource 块包装方法实现,如下所示:

public class XImpl implements X {
    public void methodX1() {
        try (SomeResource res = new SomeResource()) {
            // method implementation
        }
    }
}

由于我对 AOP 的有限概念,我相信我们可以在 JoinPoint 之前和之后做一些事情,在这种情况下是方法,但是我们如何像上面那样包装实现呢?我正在寻找是否可以使用注释或 lambda 来完成,即我不必单独更改每个方法。

任何关于如何做到这一点的想法将不胜感激。

【问题讨论】:

  • 为什么不使用 AOP 库,例如 AspectJ?
  • 如果您有许多实现 X 的具体类,您可以编写一个代理类来添加该方面——但这仍然需要接口上的每个方法的实现。您还可以使用 java.lang.reflect.Proxy 推出自己的 AOP,这不需要任何重复。或者正如@Bohemian 建议的那样,使用现有的 AOP 库。
  • 这将取决于所使用的体系结构:基于容器、编译器时解析/运行时解析...考虑一下,您不必更改每种方法,但您仍然会必须为每个方法添加注释

标签: java methods wrapper spring-aop


【解决方案1】:

由于我对 AOP 的有限概念,我相信我们可以在 JoinPoint 之前和之后做一些事情,在这种情况下,但是我们怎样才能像上面那样包装实现呢?

你读过很好的Spring AOP manual吗?您会注意到的第一件事是对建议类型的解释,并且不仅有之前和之后,还有围绕建议。这就是你想要使用的。

基本上是这样的:

Java 帮助类:

package de.scrum_master.app;

public class SomeType {}
package de.scrum_master.app;

public class SomeResource implements AutoCloseable {
  @Override public void close() throws Exception {}
}

接口:

package de.scrum_master.app;

public interface X {
  void methodX1();
  int methodX2(String s);
}
package de.scrum_master.app;

public interface Y {
  void methodY1();
  String methodY2(int i);
  void methodY3(SomeType s);
}

接口实现+驱动应用:

我在 AspectJ 中实现了这个示例,而不是在 Spring AOP 中。因此,您看不到应用程序上下文,但您知道该怎么做,不是吗?

package de.scrum_master.app;

import org.springframework.stereotype.Component;

@Component
public class MyImpl implements X, Y {
  @Override public void methodY1() { System.out.println("Y1"); methodX2("bar"); }
  @Override public String methodY2(int i) { System.out.println("Y2"); return "dummy"; }
  @Override public void methodY3(SomeType s) { System.out.println("Y3"); }
  @Override public void methodX1() { System.out.println("X1"); methodY1(); }
  @Override public int methodX2(String s) {  System.out.println("X2"); return 42; }

  public static void main(String[] args) {
    MyImpl myImpl = new MyImpl();
    myImpl.methodX1();
    myImpl.methodX2("foo");
    myImpl.methodY1();
    myImpl.methodY2(11);
    myImpl.methodY3(new SomeType());
  }
}

请注意methodX1()methodY1() 在内部调用方法。这对于后面 Spring AOP 和 AspectJ 之间的区别很重要。

方面:

在Spring AOP中你可以省略execution(* *(..)) &&部分,我这里只是为了避免call()等其他连接点被拦截而导致日志臃肿。由于 Spring AOP 除了execution() 之外知道的不多,所以那里没有必要。切入点的... || ... 块周围的括号也可以去掉。

package de.scrum_master.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

import de.scrum_master.app.SomeResource;

@Component
@Aspect
public class WrapMethodsAspect {
  @Around("execution(* *(..)) && (within(de.scrum_master.app.X+) || within(de.scrum_master.app.Y+))")
  public Object wrapperAdvice(ProceedingJoinPoint thisJoinPoint) throws Throwable {
    System.out.println("Wrapping " + thisJoinPoint);
    try (SomeResource res = new SomeResource()) {
      return thisJoinPoint.proceed();
    }
    finally {
      System.out.println("Unwrapping " + thisJoinPoint);
    }
  }
}

使用 AspectJ 进行控制台输出:

Wrapping execution(void de.scrum_master.app.MyImpl.main(String[]))
Wrapping execution(void de.scrum_master.app.MyImpl.methodX1())
X1
Wrapping execution(void de.scrum_master.app.MyImpl.methodY1())
Y1
Wrapping execution(int de.scrum_master.app.MyImpl.methodX2(String))
X2
Unwrapping execution(int de.scrum_master.app.MyImpl.methodX2(String))
Unwrapping execution(void de.scrum_master.app.MyImpl.methodY1())
Unwrapping execution(void de.scrum_master.app.MyImpl.methodX1())
Wrapping execution(int de.scrum_master.app.MyImpl.methodX2(String))
X2
Unwrapping execution(int de.scrum_master.app.MyImpl.methodX2(String))
Wrapping execution(void de.scrum_master.app.MyImpl.methodY1())
Y1
Wrapping execution(int de.scrum_master.app.MyImpl.methodX2(String))
X2
Unwrapping execution(int de.scrum_master.app.MyImpl.methodX2(String))
Unwrapping execution(void de.scrum_master.app.MyImpl.methodY1())
Wrapping execution(String de.scrum_master.app.MyImpl.methodY2(int))
Y2
Unwrapping execution(String de.scrum_master.app.MyImpl.methodY2(int))
Wrapping execution(void de.scrum_master.app.MyImpl.methodY3(SomeType))
Y3
Unwrapping execution(void de.scrum_master.app.MyImpl.methodY3(SomeType))
Unwrapping execution(void de.scrum_master.app.MyImpl.main(String[]))

您在这里注意到两件事:

  • 记录了静态main(..) 方法的执行。 Spring AOP 不会发生这种情况。
  • 记录内部方法调用。 Spring AOP 也不会发生这种情况。

使用 Spring AOP 的控制台输出:

Wrapping execution(void de.scrum_master.app.MyImpl.methodX1())
X1
Unwrapping execution(void de.scrum_master.app.MyImpl.methodX1())
Wrapping execution(int de.scrum_master.app.MyImpl.methodX2(String))
X2
Unwrapping execution(int de.scrum_master.app.MyImpl.methodX2(String))
Wrapping execution(void de.scrum_master.app.MyImpl.methodY1())
Y1
Unwrapping execution(void de.scrum_master.app.MyImpl.methodY1())
Wrapping execution(String de.scrum_master.app.MyImpl.methodY2(int))
Y2
Unwrapping execution(String de.scrum_master.app.MyImpl.methodY2(int))
Wrapping execution(void de.scrum_master.app.MyImpl.methodY3(SomeType))
Y3
Unwrapping execution(void de.scrum_master.app.MyImpl.methodY3(SomeType))

在大多数情况下,Spring AOP 对于 Spring 用户来说已经足够了。但是,如果您需要更强大的方法来捕获其他类型的切入点或例如内部的嵌套方法调用,您将使用AspectJ via load-time weaving (LTW)

【讨论】:

  • 谢谢@kriegaex。这很有帮助。
  • 是否可以在wrapperAdvice方法中添加额外的参数以供advice使用?
  • 比如?也许您想用示例代码和解释提出一个新的、完整的问题。
  • 没关系。可用方法JoinPoint.getArgs() 回答了我的问题。一切就绪。
【解决方案2】:

大概是这样的:

public abstract class ResourceProcessingService {
    protected <T> T processResource(Resource resource, Function<Reader, T> function) {
        try (InputStream fileInputStream = resource.getInputStream()) {
            Reader reader = new BufferedReader(new InputStreamReader(fileInputStream, StandardCharsets.UTF_8));
            return function.apply(reader);
        } catch (IOException e) {
            throw new YourRuntimeException("Could not process resource: " + resource.getFilename() + ", " + e.getMessage());
        }
    }
}

在您的具体实现中,您现在可以执行以下操作:

public class XImpl extends ResourceProcessingService implements X {

    public double performSomeResourceProcessing() {
        return processResource(yourResource, (reader) -> readTheResource(reader));
    }

    private double readTheResource(Reader reader) {
        // perform the resource reading
    }
}

【讨论】:

    猜你喜欢
    • 2010-10-05
    • 1970-01-01
    • 1970-01-01
    • 2016-02-19
    • 2021-01-11
    • 1970-01-01
    • 1970-01-01
    • 2011-11-20
    • 1970-01-01
    相关资源
    最近更新 更多