Java 8 为什么一次调用会执行两次可观察功能?

Java 8 为什么一次调用会执行两次可观察功能?,java-8,rx-java,Java 8,Rx Java,程序的完整结构 注释: @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface UserAnnotation { } 然后创建了一个拦截器: public class UserInterceptor implements MethodInterceptor { private static final Logger logger = LoggerFactory.getLogge

程序的完整结构

注释:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface UserAnnotation {
}
然后创建了一个拦截器:

public class UserInterceptor implements MethodInterceptor {

    private static final Logger logger = LoggerFactory.getLogger(UserInterceptor.class);

    @Inject
    UserService userService; // this is not working

    public Object invoke(MethodInvocation invocation) throws Throwable {
        logger.info("UserInterceptor : Interceptor Invoked");
        Object result = invocation.proceed();
        Observable<List<User>> observable = (Observable<List<Sample>>) result;
        observable.flatMap(Observable::from).subscribe(object -> {
            User user = (User)object
            SampleSender sender = new SampleSender();
            sender.setBoolean(user.isBoolean());
            logger.info("Pushing Data into Sender");
            userService.insert(String.join("_", "key", "value"), sender); 
        }
        return result;
    }
}
}

我在其中使用上述注释的类是

// This class also have so many method and this was already declared and using in another services, I created a sample class here
class UserClassForInterceptor {

      @Inject
      AnotherClass anotherClass;
      // this userMethod() is not a new method, its already created, 
      // now I am adding annotation to it, because after finishing this functionality, 
      // I want something should be done, so created annotation and added here
      @UserAnnotation
      public Observable<List<Sample>> userMethod() {
            logger.info("This is printing only once");
            return anotherClass.getUser().flatMap(user ->{
                logger.info("This is also printing twice");
                // this logger printed twise means, this code snippet is getting executed twise
            });
      }
}

public class AnotherClass{
          public Observable<User> getUser(){
           Observable<Sample> observableSample = methodReturnsObservableSample();
           logger.info("Getting this logger only once");
           return observableSample.map(response-> {
               logger.info("This logger is printing twice");
               //here have code to return observable of User
           });
      }
}
现在我有了一个bootsrap类,在其中我绑定了
RestModule

public class Bootstrap extends ServerBootstrap {
   binder.install(new RestModule());
}
用法:-

@Path("service/sample")
public class SampleRS {
    @Inject
    UserClassForInterceptor userClassForInterceptor;

    public void someMethod() {
        userClassForInterceptor.sampleMethod();
    }
}

您创建了一个注释,
@UserAnnotation
,以及一个与注释一起使用的拦截器类。将注释附加到方法,
userMethod()

拦截器例程要做的第一件事是调用
userMethod()
,以获取它返回的可观察对象,然后拦截器订阅返回的可观察对象,从而显示第一条日志消息。最终,拦截器将可观察的返回给原始调用者。当其他对象订阅返回的可观察对象时,观察者链第二次被激活,因此日志消息出现两次

RxJava有副作用

虽然RxJava是“函数式反应式编程”概念的实现,但您(以函数方式)构建的观察者链只有在订阅时才起作用,并且这些订阅具有副作用。测井输出是一个副作用,可能是最良性的;对具有副作用的变量或方法调用的更改具有更广泛的影响

当观察者链被构造(正确地)时,它充当潜在的计算,直到有订户为止。如果您需要有多个订阅服务器,就像您在问题域中可能需要的那样,那么您必须决定是否需要为每个订阅(正常情况下)激活观察者链,或者仅为所有重叠订阅激活一次

如果希望所有重叠订阅共享相同的可观察对象,则可以使用
share()
运算符。有许多相关的操作符会影响可观察对象和订阅的生存期。以下是概述:

面向方面编程:拦截器和GUI

您的代码正在使用Guice提供一种称为“面向方面编程”的功能。这允许您在程序中引入代码来解决交叉关注点,或者通过设置受控网关来增强其功能。使用Guice或类似的AOP方法需要规范

在您的例子中,您使用拦截过程通过订阅具有非平凡副作用的观察者链来产生无法解释的(直到现在)副作用。假设您截取的方法建立了一个一次性连接,并且您的拦截器在执行其工作时使用了该连接,使原始调用方无法使用该连接

您需要的规则是理解拦截器必须遵循的规则。想想“首先,不要伤害”之类的规则

以FRP方式做事

如果在处理用户信息时需要添加额外的步骤,则应在拦截器中构造一个新的可观察对象,但仅当原始调用者订阅该可观察对象时:

    Object result = invocation.proceed();
    Observable<List<User>> observable = (Observable<List<Sample>>) result;
    Observable<List<User>> newObservable = observable
      .doOnNext( sampleList ->
         Observable.fromIterable( sampleList )
           .subscribe(object -> {
             User user = (User)object
             SampleSender sender = new SampleSender();
             sender.setBoolean(user.isBoolean());
             logger.info("Pushing Data into Sender");
             userService.insert(String.join("_", "key", "value"), sender); 
           }));
    return newObservable;
Object result=invocation.continue();
可观察=(可观察)结果;
可观测的新可观测的=可观测的
.doOnNext(样本列表->
可观察的。来自可观察的(样本列表)
.订阅(对象->{
用户=(用户)对象
SampleSender sender=新的SampleSender();
sender.setBoolean(user.isBoolean());
logger.info(“将数据推送到发送方”);
insert(String.join(“_”,“key”,“value”),发送方);
}));
返回新的可观察的;

通过返回修改后的观察者链,您不会从原始观察者链引入副作用,并确保您在自己的代码中引入的副作用只会在订阅原始观察者链时触发。

此代码也帮助了我

public Object invoke(MethodInvocation invocation) throws Throwable {
    Object result = null;
    try{
        logger.debug("Interceptor Invoked");
        result = invocation.proceed();
        Observable<List<User>> observable = (Observable<List<User>>)result;

        return observable
                .doOnNext(this::updateUser);
    }
    catch(Exception ex){
        logger.error("Error: ",ex);
    }
    return result;
}

private void updateUser(List<User> users) {
    if(CollectionUtils.isNotEmpty(users)) {
        for(User user: users) {
            SampleSender sender = new SampleSender();
            sender.setBoolean(user.isBoolean());
            logger.info("Pushing Data into Sender");
            userService.insert(String.join("_", "key", "value"), sender); 
        }
    }
}
public对象调用(MethodInvocation调用)抛出可丢弃的{
对象结果=空;
试一试{
debug(“调用了拦截器”);
结果=调用。继续();
可观察=(可观察)结果;
可观测回波
.doOnNext(this::updateUser);
}
捕获(例外情况除外){
logger.error(“错误:”,ex);
}
返回结果;
}
私有void updateUser(列出用户){
if(CollectionUtils.isNotEmpty(用户)){
for(用户:用户){
SampleSender sender=新的SampleSender();
sender.setBoolean(user.isBoolean());
logger.info(“将数据推送到发送方”);
insert(String.join(“_”,“key”,“value”),发送方);
}
}
}

您的
UserAnnotation
是如何实现的?这将是第一个寻找双重调用的地方。另外,您在
getestobject()
中缺少
return
关键字。我已经更新了代码和
UserAnnocation
实现,其用法等与我在
getestobject()
链接中共享的
SampleAnnotation
相同,您可以创建一个从
getUser()开始的观察者链.flatMap…
。但是,由于没有订阅,它不会发生任何变化。您向我们展示的代码不会显示您描述的行为。使用我的示例应用程序结构更新了问题我再说一遍,在方法
getestobject()
中,语句
getUser().flatMapUser(user->{…})无效,因为没有订阅。无法执行该语句中的日志语句。这意味着您没有向我们展示展示您描述的行为的代码。
    Object result = invocation.proceed();
    Observable<List<User>> observable = (Observable<List<Sample>>) result;
    Observable<List<User>> newObservable = observable
      .doOnNext( sampleList ->
         Observable.fromIterable( sampleList )
           .subscribe(object -> {
             User user = (User)object
             SampleSender sender = new SampleSender();
             sender.setBoolean(user.isBoolean());
             logger.info("Pushing Data into Sender");
             userService.insert(String.join("_", "key", "value"), sender); 
           }));
    return newObservable;
public Object invoke(MethodInvocation invocation) throws Throwable {
    Object result = null;
    try{
        logger.debug("Interceptor Invoked");
        result = invocation.proceed();
        Observable<List<User>> observable = (Observable<List<User>>)result;

        return observable
                .doOnNext(this::updateUser);
    }
    catch(Exception ex){
        logger.error("Error: ",ex);
    }
    return result;
}

private void updateUser(List<User> users) {
    if(CollectionUtils.isNotEmpty(users)) {
        for(User user: users) {
            SampleSender sender = new SampleSender();
            sender.setBoolean(user.isBoolean());
            logger.info("Pushing Data into Sender");
            userService.insert(String.join("_", "key", "value"), sender); 
        }
    }
}