也许我误解了您的问题,但我怀疑需要让您的存储库接口了解异步实现是不必要的(并且可能是一个糟糕的设计选择)。
我建议使用Optional 作为接口方法的返回值,而不是返回User 并在未找到时抛出特殊异常。 Optional 类旨在包装可能合法为空的对象引用,而不是表示错误的空。在Generics syntax of Java 中,我们说Optional < User >,这意味着Optional 包装器要么专门携带User 对象,要么根本不携带任何东西。
所以让我们把界面改成这样。
package work.basil.demo.wait;
import java.util.Optional;
public interface UserRepository
{
Optional < User > fetchByEmail ( String email );
}
这是该接口的一个实现,它返回一个假对象。
我们使用到达的任何电子邮件作为参数创建一个User 对象。对于该电子邮件地址,我们附加了一个用户名值Fake。这些值放在我们的User 对象中。
我们在 fetchByEmail 方法中休眠片刻,以模拟需要一段时间的数据库获取。
在底部,我们有两个结局。一行返回一个假用户。另一个返回一个空的Optional 来模拟找不到匹配项。您可以注释/注释任一行以切换行为。
package work.basil.demo.wait;
import java.time.Duration;
import java.util.Objects;
import java.util.Optional;
public class FakeUserRepository implements UserRepository
{
@Override
public Optional < User > fetchByEmail ( String email )
{
// Validate inputs.
Objects.requireNonNull( email , "Email argument must be provided, cannot be null. Message # a6d23f55-8ac9-490b-9969-b444d3db0737." );
if ( email.isBlank() ) { throw new IllegalArgumentException( "Email argument cannot be blank. Message # 793f58f5-c7be-4ebb-9351-0319809a6971." ); }
// Sleep a moment to pretend that this method takes a while to hit the database before returning.
try { Thread.sleep( Duration.ofSeconds( 2 ).toMillis() ); }catch ( InterruptedException e ) { e.printStackTrace(); }
// Return whatever email is sent, inventing fake `User` object.
return Optional.of( new User( email , "Fake" ) );
// Return empty optional, to simulate the case where the desired email address is not found to belong to any existing users.
// return Optional.empty() ;
}
}
这是我们简单的User 类。为简洁起见,我们使用 Java 16 的新 records 功能。您也可以定义一个常规类。
public record User(String email , String name) {}
最后,我们的应用程序类使用main 方法来演示此操作。
首先我们建立我们的存储库对象。这可以在您的真实应用生命周期的早期完成。
我们建立一个执行器服务来处理我们的后台线程。我们向该服务提交我们的任务实例,调用存储库的fetchByEmail 方法。这里我们使用 lambda 语法,但您也可以使用常规语法。请注意,在submit 之后,我们在我们的服务上调用shutdown。这将停止任何更多的提交,并在已经提交的任务完成后要求服务关闭。此调用不会 阻塞。
与此同时,我们继续做其他工作,其他工作不需要等待User 对象。在这段代码中,我们通过休眠当前线程来模拟其他工作。
最终,我们完成了没有User 对象的所有工作。所以我们在Future 对象上调用get。 此调用阻塞。当前线程等待后台线程完成并返回我们需要的User 对象。如果后台线程已经完成,这个get方法会立即返回。
解开Optional 后,我们手头有一个User 对象。或者我们发现 Optional 为空,在这种情况下,我们知道没有找到与该电子邮件地址输入值匹配的内容(或者在查询过程中出现问题)。
package work.basil.demo.wait;
import java.time.Duration;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.*;
public class App
{
public static void main ( String[] args )
{
UserRepository userRepository = new FakeUserRepository();
// …
ExecutorService executorService = Executors.newSingleThreadExecutor();
Callable < Optional < User > > task = ( ) -> userRepository.fetchByEmail( "fake@example.com" );
Future < Optional < User > > future = executorService.submit( task );
executorService.shutdown(); // Shuts down the executor service after previously submitted tasks are done/cancelled/failed.
// Do some stuff.
// We sleep this thread to pretend that some other work is executing.
try { Thread.sleep( Duration.ofSeconds( 3 ).toMillis() ); } catch ( InterruptedException e ) { e.printStackTrace(); }
// Other work done. So now we need our `User` object from repository before we can proceed.
if ( future.isCancelled() )
{
// Deal with task being cancelled.
} else
{
Optional < User > optionalUser = null; // Blocks, waiting until done to return result. Or, if already done, immediately returns result.
try { optionalUser = future.get(); } catch ( InterruptedException e ) { e.printStackTrace(); }catch ( ExecutionException e ) { e.printStackTrace(); }
if ( optionalUser.isPresent() )
{
System.out.println( "user = " + optionalUser.get() + ". Message # 2bff0033-3000-4b77-b8e0-654cf51db88b." );
} else // Else no user found, the Optional is empty.
{
System.out.println( "No user found. The Optional is empty. Message # cd733ffe-0a38-4813-9c80-98f5d9c35755." );
}
}
if ( Objects.nonNull( executorService ) )
{
try { executorService.awaitTermination( 5 , TimeUnit.SECONDS ); } catch ( InterruptedException e ) { e.printStackTrace(); }
}
System.out.println( "Demo done. Message # e64cc194-2087-4aae-a142-3a27a4950483." );
}
}
运行时,我们得到这样的输出。
用户 = 用户 [email=fake@example.com, name=Fake]。消息 # 2bff0033-3000-4b77-b8e0-654cf51db88b。
演示完成。消息 #e64cc194-2087-4aae-a142-3a27a4950483。