It is a bit of an oversimplification but conceptually Reactor sources are either lazy or eager. More advanced ones, like an HTTP request, are expected to be lazily evaluated. On the other side the most simple ones like Mono.just or Flux.fromIterable are eager.

By that, I mean that calling Mono.just(System.currentTimeMillis()) will immediately invoke the currentTimeMillis() method and capture the result. Said result is only emitted by the Mono once it is subscribed to. Subscribing multiple times doesn't change the value either:

Mono<Long> clock = Mono.just(System.currentTimeMillis());
//time == t0

Thread.sleep(10_000);
//time == t10
clock.block(); //we use block for demonstration purposes, returns t0

Thread.sleep(7_000);
//time == t17
clock.block(); //we re-subscribe to clock, still returns t0

The defer operator is there to make this source lazy, re-evaluating the content of the lambda each time there is a new subscriber:

Mono<Long> clock = Mono.defer(() -> Mono.just(System.currentTimeMillis()));
//time == t0

Thread.sleep(10_000);
//time == t10
clock.block(); //invoked currentTimeMillis() here and returns t10

Thread.sleep(7_000);
//time == t17
clock.block(); //invoke currentTimeMillis() once again here and returns t17

with simple words if you see in the first view it is like Mono.just() but is not. when you run Mono.just() it creates immediately an Observable(Mono)and reuses it but when you use defer it doesn't create it immediately it creates a new Observable in every subscribe.

One use case to see the difference

    int a = 5;
@Override
public void run(String... args) throws Exception {

    Mono<Integer> monoJust = Mono.just(a);
    Mono<Integer> monoDefer = Mono.defer(() -> Mono.just(a));

    monoJust.subscribe(integer1 -> System.out.println(integer1));
    monoDefer.subscribe(integer1 -> System.out.println(integer1));

    a = 7;
    monoJust.subscribe(integer1 -> System.out.println(integer1));
    monoDefer.subscribe(integer1 -> System.out.println(integer1));
}

print:

5
5
5
7

if you see mono.just has created the observable immediately and it doesn't change even if the value has changed but the defer create the observable in subscribe so you will work with the current onSubscribe value