JUnit 4 extension model is a bit scary. You have @Rule, @ClassRule, and different Runner implementations. Very often you need to use all of them at once. On top of that you will probably end up with multiple rules that you will need to chain properly. Doable, but scary for beginners. Luckily, JUnit 5 offers a much better solution to that conundrum with a "single, coherent concept": the JUnit 5 Extension model. Its not perfect, but in my opinion it is a vast improvement.
I've added a very basic code (to GitHub) to demonstrate this. A few extensions and a couple of tests. The following code shows a basic extensions that implements all of the previously mentioned interfaces and simply prints out when each implemented method is called.
As you can see in each implemented method you have access to the ExtensionContext. This class encapsulates the context in which the current test or container is being executed. You can use it to access the test method, class, unique id etc. Unfortunately, here you don't get an access to the TestIdentifier object like you do in a TestExecutionLister implementation. I am not quite sure why JUnit 5 has it this way.
You can register a JUnit 5 extension declaratively via the @ExtendWith annotation.
After all the tests finish.
You can also register extensions programmatically, on field level, using the @RegisterExtension annotation. And, a third option is to use Java's ServiceLoader mechanism.
If you want, you can also register multiple extensions. You can use multiple @ExtendWith annotations.
Or, list more extensions in the same annotation.
The order in which you declare the annotation will be the order in which they will execute.
Not long ago I wrote about dynamic test in JUnit 5. The @BeforeEach and @AfterEach are all called once per @TestFactory. That means that the BeforeTestExecutionCallback and AfterTestExecutionCallback callbacks of an extension will be called once per @TestFactory. And that is maybe one of the main downsides of the dynamic tests.
The JUnit 5 extension model makes it much easier to extend the tests and the way they run. It is simple, easy to use, and straightforward. In my opinion, it is the single good enough reason to do the switch to JUnit 5. On the other hand, it is a huge breaking change. If your testing framework depends heavily on rules and runners, then you are into some remodeling. However, it is well worth the effort.