Spring 4 方法拦截器和使用自定义实现
Spring 4 method interceptor and use custom implementation
我有以下类和界面
interface abc {
public A do();
}
package x;
public Impl1 implements abc{
public A do(){
}
}
package y;
public Impl2 implements abc{
public A do(){
}
}
我没有 Impl1 或 Impl2 的源代码。但是想拦截对 do() 方法的任何调用并使用我自己的实现。也可能根据某些条件调用实际的do()实现,其他情况不会委托给原来的实现。
能否请您告诉我这是否可以实现。如果是,如何实现?
我正在使用 Spring 4 和 JDK 7。
您的请求可以使用 Spring AOP 完成,更具体地说,使用 @Around
建议。 @Around 建议,除其他事项外,允许您将调用直接传递给原始实现,或者使调用短路并调用您的实现。您需要提供选择其中之一的逻辑。
向@Around 方法传递了一个ProceedingJoinPoint。要调用原始实现,您可以使用“proceed
”方法。如果要短路,则不要调用 proceed;而是调用您自己的方法来创建 'A' 对象。
以下代码显示了一个基于 @Aspect 的 class,它演示了这两种技术。您必须注入您自己的实现,以便您可以根据需要创建自己的 A 对象。
您应该阅读一些关于 Spring 一般 AOP 的内容,更具体地说,关于切入点(需要拦截调用)和 @Around 建议。值得注意的是,您可以组合切入点并使用通配符,因此如果您使切入点足够通用以捕获所有实现的方法,则很可能可以在 @Aspect class 中使用有限数量的方法实现您想要的做方法。
示例代码显示了对原始实现的传递和调用您自己的短路。
@Aspect
@Component
public class DoAspects {
@Autowired
@Qualifier("YourQualifier")
private abc p3;
@Around("execution(* pkg1.Pkg1AImpl.do())")
public A p1(ProceedingJoinPoint jp) throws Throwable {
// Some logic determines to call the original implementation (i.e. proceed)
A a = (A)jp.proceed(); // Let the other implementation create A
return a;
}
@Around("execution(* pkg2.Pkg2AImpl.do())")
public A p2(ProceedingJoinPoint jp) {
// Some logic determines to short-circuit, and call own implementation
A a = p3.do(); // You create A
return a;
}
}
我将提供一个独立的 AspectJ 解决方案,但在 spring AOP 中它会以相同的方式,只有方面和你的目标 classes 需要是 Spring beans/components,所以不要忘记像 @Component
这样的注释,如 Ian Mc.
所示
助手class+接口+实现:
package de.scrum_master.app;
public class A {
private String name;
public A(String name) {
this.name = name;
}
@Override
public String toString() {
return "A [name=" + name + "]";
}
}
package de.scrum_master.app;
public interface MyInterface {
public A doSomething();
}
package de.scrum_master.app;
public class FirstImpl implements MyInterface {
@Override
public A doSomething() {
return new A("First");
}
}
package de.scrum_master.app;
public class SecondImpl implements MyInterface {
@Override
public A doSomething() {
return new A("Second");
}
}
驱动申请:
package de.scrum_master.app;
public class Application {
private static MyInterface myInterface;
public static void main(String[] args) {
myInterface = new FirstImpl();
for (int i = 0; i < 5; i++) {
System.out.println(myInterface.doSomething());
}
myInterface = new SecondImpl();
for (int i = 0; i < 5; i++) {
System.out.println(myInterface.doSomething());
}
}
}
没有方面的控制台日志:
A [name=First]
A [name=First]
A [name=First]
A [name=First]
A [name=First]
A [name=Second]
A [name=Second]
A [name=Second]
A [name=Second]
A [name=Second]
到目前为止,太无聊了。
看点:
现在让我们实现一个愚蠢的小方面,随机决定方法执行与跳过并提供另一个 return 值来代替(因为我不知道会导致您跳过方法执行的真实情况):
package de.scrum_master.aspect;
import java.util.Random;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import de.scrum_master.app.A;
@Aspect
public class MyAspect {
private static Random random = new Random();
@Around("execution(A de.scrum_master.app.MyInterface.*(..))")
public A interceptCalls(ProceedingJoinPoint thisJoinPoint) throws Throwable {
if (random.nextBoolean())
return (A) thisJoinPoint.proceed();
else
return new A("Aspect");
}
}
具有活动方面的控制台日志:
A [name=Aspect]
A [name=First]
A [name=Aspect]
A [name=Aspect]
A [name=First]
A [name=Aspect]
A [name=Second]
A [name=Second]
A [name=Aspect]
A [name=Second]
我有以下类和界面
interface abc {
public A do();
}
package x;
public Impl1 implements abc{
public A do(){
}
}
package y;
public Impl2 implements abc{
public A do(){
}
}
我没有 Impl1 或 Impl2 的源代码。但是想拦截对 do() 方法的任何调用并使用我自己的实现。也可能根据某些条件调用实际的do()实现,其他情况不会委托给原来的实现。
能否请您告诉我这是否可以实现。如果是,如何实现?
我正在使用 Spring 4 和 JDK 7。
您的请求可以使用 Spring AOP 完成,更具体地说,使用 @Around
建议。 @Around 建议,除其他事项外,允许您将调用直接传递给原始实现,或者使调用短路并调用您的实现。您需要提供选择其中之一的逻辑。
向@Around 方法传递了一个ProceedingJoinPoint。要调用原始实现,您可以使用“proceed
”方法。如果要短路,则不要调用 proceed;而是调用您自己的方法来创建 'A' 对象。
以下代码显示了一个基于 @Aspect 的 class,它演示了这两种技术。您必须注入您自己的实现,以便您可以根据需要创建自己的 A 对象。
您应该阅读一些关于 Spring 一般 AOP 的内容,更具体地说,关于切入点(需要拦截调用)和 @Around 建议。值得注意的是,您可以组合切入点并使用通配符,因此如果您使切入点足够通用以捕获所有实现的方法,则很可能可以在 @Aspect class 中使用有限数量的方法实现您想要的做方法。
示例代码显示了对原始实现的传递和调用您自己的短路。
@Aspect
@Component
public class DoAspects {
@Autowired
@Qualifier("YourQualifier")
private abc p3;
@Around("execution(* pkg1.Pkg1AImpl.do())")
public A p1(ProceedingJoinPoint jp) throws Throwable {
// Some logic determines to call the original implementation (i.e. proceed)
A a = (A)jp.proceed(); // Let the other implementation create A
return a;
}
@Around("execution(* pkg2.Pkg2AImpl.do())")
public A p2(ProceedingJoinPoint jp) {
// Some logic determines to short-circuit, and call own implementation
A a = p3.do(); // You create A
return a;
}
}
我将提供一个独立的 AspectJ 解决方案,但在 spring AOP 中它会以相同的方式,只有方面和你的目标 classes 需要是 Spring beans/components,所以不要忘记像 @Component
这样的注释,如 Ian Mc.
助手class+接口+实现:
package de.scrum_master.app;
public class A {
private String name;
public A(String name) {
this.name = name;
}
@Override
public String toString() {
return "A [name=" + name + "]";
}
}
package de.scrum_master.app;
public interface MyInterface {
public A doSomething();
}
package de.scrum_master.app;
public class FirstImpl implements MyInterface {
@Override
public A doSomething() {
return new A("First");
}
}
package de.scrum_master.app;
public class SecondImpl implements MyInterface {
@Override
public A doSomething() {
return new A("Second");
}
}
驱动申请:
package de.scrum_master.app;
public class Application {
private static MyInterface myInterface;
public static void main(String[] args) {
myInterface = new FirstImpl();
for (int i = 0; i < 5; i++) {
System.out.println(myInterface.doSomething());
}
myInterface = new SecondImpl();
for (int i = 0; i < 5; i++) {
System.out.println(myInterface.doSomething());
}
}
}
没有方面的控制台日志:
A [name=First]
A [name=First]
A [name=First]
A [name=First]
A [name=First]
A [name=Second]
A [name=Second]
A [name=Second]
A [name=Second]
A [name=Second]
到目前为止,太无聊了。
看点:
现在让我们实现一个愚蠢的小方面,随机决定方法执行与跳过并提供另一个 return 值来代替(因为我不知道会导致您跳过方法执行的真实情况):
package de.scrum_master.aspect;
import java.util.Random;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import de.scrum_master.app.A;
@Aspect
public class MyAspect {
private static Random random = new Random();
@Around("execution(A de.scrum_master.app.MyInterface.*(..))")
public A interceptCalls(ProceedingJoinPoint thisJoinPoint) throws Throwable {
if (random.nextBoolean())
return (A) thisJoinPoint.proceed();
else
return new A("Aspect");
}
}
具有活动方面的控制台日志:
A [name=Aspect]
A [name=First]
A [name=Aspect]
A [name=Aspect]
A [name=First]
A [name=Aspect]
A [name=Second]
A [name=Second]
A [name=Aspect]
A [name=Second]