diff --git a/core/modules/views/src/Attribute/ViewsJoin.php b/core/modules/views/src/Attribute/ViewsJoin.php
new file mode 100644
index 0000000000000000000000000000000000000000..365b47d75da1e546d85ae20a316effe654ef13b6
--- /dev/null
+++ b/core/modules/views/src/Attribute/ViewsJoin.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace Drupal\views\Attribute;
+
+use Drupal\Component\Plugin\Attribute\PluginID;
+
+/**
+ * Defines a Plugin attribute object for views join plugins.
+ *
+ * @see \Drupal\views\Plugin\views\join\JoinPluginBase
+ *
+ * @ingroup views_join_handlers
+ */
+#[\Attribute(\Attribute::TARGET_CLASS)]
+class ViewsJoin extends PluginID {
+
+}
diff --git a/core/modules/views/src/Plugin/views/join/CastedIntFieldJoin.php b/core/modules/views/src/Plugin/views/join/CastedIntFieldJoin.php
index fe0fdbc570e8edaa81d67c6fd3d3d74af67a6eeb..c091222ae51545e230444addd2e9f8e6913d7980 100644
--- a/core/modules/views/src/Plugin/views/join/CastedIntFieldJoin.php
+++ b/core/modules/views/src/Plugin/views/join/CastedIntFieldJoin.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\views\Plugin\views\join;
 
+use Drupal\views\Attribute\ViewsJoin;
+
 /**
  * Implementation for the "casted_int_field_join" join.
  *
@@ -11,9 +13,8 @@
  * are strict when comparing numbers and strings.
  *
  * @ingroup views_join_handlers
- *
- * @ViewsJoin("casted_int_field_join")
  */
+#[ViewsJoin("casted_int_field_join")]
 class CastedIntFieldJoin extends JoinPluginBase {
 
   /**
diff --git a/core/modules/views/src/Plugin/views/join/FieldOrLanguageJoin.php b/core/modules/views/src/Plugin/views/join/FieldOrLanguageJoin.php
index 25dcc92bce3f0240b92b6b0b2a941d5a5755659a..77c880bdd7bf6129f303e210b95af94282ab270a 100644
--- a/core/modules/views/src/Plugin/views/join/FieldOrLanguageJoin.php
+++ b/core/modules/views/src/Plugin/views/join/FieldOrLanguageJoin.php
@@ -3,6 +3,7 @@
 namespace Drupal\views\Plugin\views\join;
 
 use Drupal\Core\Database\Query\SelectInterface;
+use Drupal\views\Attribute\ViewsJoin;
 
 /**
  * Implementation for the "field OR language" join.
@@ -58,9 +59,8 @@
  * @see views_field_default_views_data()
  *
  * @ingroup views_join_handlers
- *
- * @ViewsJoin("field_or_language_join")
  */
+#[ViewsJoin("field_or_language_join")]
 class FieldOrLanguageJoin extends JoinPluginBase {
 
   /**
diff --git a/core/modules/views/src/Plugin/views/join/JoinPluginBase.php b/core/modules/views/src/Plugin/views/join/JoinPluginBase.php
index 57c18eeee1432fe44be8ca06044d9d04ab3be45b..daadb546f6688b0139a67557777e04b50150c198 100644
--- a/core/modules/views/src/Plugin/views/join/JoinPluginBase.php
+++ b/core/modules/views/src/Plugin/views/join/JoinPluginBase.php
@@ -14,7 +14,7 @@
  * handle table joins.
  *
  * Views join handlers extend \Drupal\views\Plugin\views\join\JoinPluginBase.
- * They must be annotated with \Drupal\views\Annotation\ViewsJoin annotation,
+ * They must be attributed with \Drupal\views\Attribute\ViewsJoin attribute,
  * and they must be in namespace directory Plugin\views\join.
  *
  * Here are some examples of configuration for the join plugins.
diff --git a/core/modules/views/src/Plugin/views/join/Standard.php b/core/modules/views/src/Plugin/views/join/Standard.php
index c21026ef90b7923c166311081848dfe1bbc9f042..50e6d92117d89dd5bb38108887d235b5162a2f8a 100644
--- a/core/modules/views/src/Plugin/views/join/Standard.php
+++ b/core/modules/views/src/Plugin/views/join/Standard.php
@@ -2,13 +2,14 @@
 
 namespace Drupal\views\Plugin\views\join;
 
+use Drupal\views\Attribute\ViewsJoin;
+
 /**
  * Default implementation of the join plugin.
  *
  * @ingroup views_join_handlers
- *
- * @ViewsJoin("standard")
  */
+#[ViewsJoin("standard")]
 class Standard extends JoinPluginBase {
 
 }
diff --git a/core/modules/views/src/Plugin/views/join/Subquery.php b/core/modules/views/src/Plugin/views/join/Subquery.php
index ec9eefd9cf9c346ece68790f89fc4487e2d476ce..adc52e3950658cbe921bebf2e2771f454ed8ff1e 100644
--- a/core/modules/views/src/Plugin/views/join/Subquery.php
+++ b/core/modules/views/src/Plugin/views/join/Subquery.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\views\Plugin\views\join;
 
+use Drupal\views\Attribute\ViewsJoin;
+
 /**
  * Join handler for relationships that join with a subquery as the left field.
  *
@@ -15,8 +17,8 @@
  * - left_query: The subquery to use in the left side of the join clause.
  *
  * @ingroup views_join_handlers
- * @ViewsJoin("subquery")
  */
+#[ViewsJoin("subquery")]
 class Subquery extends JoinPluginBase {
 
   /**
diff --git a/core/modules/views/tests/modules/views_test_data/src/Plugin/views/join/JoinTest.php b/core/modules/views/tests/modules/views_test_data/src/Plugin/views/join/JoinTest.php
index 025a7748f35d0be6e313e484be04914edb35a635..83b07725510e92204d1c4d505068a7acc7d2fc39 100644
--- a/core/modules/views/tests/modules/views_test_data/src/Plugin/views/join/JoinTest.php
+++ b/core/modules/views/tests/modules/views_test_data/src/Plugin/views/join/JoinTest.php
@@ -2,13 +2,13 @@
 
 namespace Drupal\views_test_data\Plugin\views\join;
 
+use Drupal\views\Attribute\ViewsJoin;
 use Drupal\views\Plugin\views\join\JoinPluginBase;
 
 /**
  * Defines a join test plugin.
- *
- * @ViewsJoin("join_test")
  */
+#[ViewsJoin("join_test")]
 class JoinTest extends JoinPluginBase {
   /**
    * A value which is used to build an additional join condition.