mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Compare commits
142 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 006c0bc418 | |||
| 4734837fe4 | |||
| 4a13655aa5 | |||
| 5ef9972528 | |||
| a9b1d6335e | |||
| 98d4046e4b | |||
| 282fee5c95 | |||
| 0d4737aeec | |||
| 1ef8734881 | |||
| 6dfa54b8ea | |||
| 2d3a2c5cbd | |||
| 62e590a437 | |||
| 7f29df4d79 | |||
| 8fb119df1e | |||
| 1bb0fdda6b | |||
| 6507367d10 | |||
| fee7e6b7e2 | |||
| 35c4973504 | |||
| 77b54c7409 | |||
| ff9ba15762 | |||
| c33b82f2a1 | |||
| ec459aefbf | |||
| b227701473 | |||
| 7a85ce959b | |||
| e93e55b456 | |||
| e22b1648bf | |||
| 78967d4bd6 | |||
| 1d2351c87b | |||
| 02dc4b6342 | |||
| 143828e27c | |||
| 7998bc9de2 | |||
| 8a1a9d2ab2 | |||
| 394a97d7e0 | |||
| 05ffe8249f | |||
| 20970a4a37 | |||
| cb4b87cce2 | |||
| 1d470fc375 | |||
| 7fc619acdc | |||
| e5f8c5b7ae | |||
| 3666ad9d7f | |||
| 652d509cfc | |||
| 153c378bb2 | |||
| cdbaa7f3a7 | |||
| d1b0bdb3d5 | |||
| 59a9d0fcb9 | |||
| db9045ae28 | |||
| 7c2159fbe9 | |||
| f2d3da3f32 | |||
| f1c86b1bbc | |||
| 7fde8a6fa3 | |||
| 033c309d39 | |||
| ac243fae65 | |||
| ad19c1ac82 | |||
| 29d125e659 | |||
| d31bd8139d | |||
| fb10455d3c | |||
| 40350653e1 | |||
| a08a4d6068 | |||
| 6fd84e4c2f | |||
| 516bf1c606 | |||
| 32b55e68fc | |||
| b1f427f137 | |||
| f52481493c | |||
| 149397ed10 | |||
| e1d3076570 | |||
| 16f4b23c32 | |||
| 54c3d01270 | |||
| 799a520c35 | |||
| bf5386712c | |||
| 5d33de943b | |||
| 7da65fe4fc | |||
| 0d612b3b1d | |||
| 4dacc64a30 | |||
| ba47dd0714 | |||
| 79386b0e83 | |||
| d1bfe03358 | |||
| 4bc9f68451 | |||
| ce0e37536b | |||
| b0ac8df13e | |||
| 80a57c2468 | |||
| 02e05b40b0 | |||
| c8c99d0464 | |||
| 867dd0e972 | |||
| 2c96f18439 | |||
| 96fb5d72eb | |||
| bc01eeb433 | |||
| 77c24b28b6 | |||
| e809395635 | |||
| 801035bea7 | |||
| 9e4a54bb99 | |||
| e56f7c1e53 | |||
| 65bdc78a1e | |||
| fd3bbb057e | |||
| c47da1247a | |||
| 22cf7958a5 | |||
| ab314aadd9 | |||
| 611811d8fc | |||
| 0c9f40b171 | |||
| 5b4eda3d20 | |||
| af8fd8f07d | |||
| 345fa51e77 | |||
| c05ea841bc | |||
| dab28b5dce | |||
| 1c4b20287f | |||
| 8fbd147f0b | |||
| 1c56ec3f7e | |||
| 65565de1a8 | |||
| b3c26255b1 | |||
| 66962875f9 | |||
| 5490ba94ae | |||
| 17e02804ba | |||
| 357205723f | |||
| c4c167c0e1 | |||
| 4119ecc25b | |||
| 9ebfdb8b6a | |||
| c4cb55fbde | |||
| 7917ae53e8 | |||
| 693ec8219f | |||
| 9b95482557 | |||
| a029dfc6a8 | |||
| a921c487b4 | |||
| 3f01830d11 | |||
| 64d95d7f3e | |||
| 5a4b1bd1d1 | |||
| a91402dcc6 | |||
| 0014e0b3c9 | |||
| 49da766551 | |||
| 50bbe6191a | |||
| a8c33c13e2 | |||
| b164db35c1 | |||
| 385e62dbf0 | |||
| 4d4adeb2c6 | |||
| 7aa862f5ec | |||
| 2d2809f45c | |||
| 3f390d55f0 | |||
| 2b96a61063 | |||
| c55351aa4a | |||
| 48bedd0af1 | |||
| c1b0e68d6c | |||
| fe2113f5a3 | |||
| 2ff2a89f22 | |||
| 14641c4299 |
@@ -1,6 +1,13 @@
|
||||
changelog:
|
||||
repository: spring-projects/spring-framework
|
||||
sections:
|
||||
- title: ":warning: Attention Required"
|
||||
labels:
|
||||
- "for: upgrade-attention"
|
||||
summary:
|
||||
mode: "member-comment"
|
||||
config:
|
||||
prefix: "Attention Required:"
|
||||
- title: ":star: New Features"
|
||||
labels:
|
||||
- "type: enhancement"
|
||||
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
uses: ./.github/actions/print-jvm-thread-dumps
|
||||
- name: Upload Build Reports
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: build-reports
|
||||
path: '**/build/reports/'
|
||||
|
||||
@@ -64,7 +64,7 @@ jobs:
|
||||
run: ./gradlew spring-framework-release-verification-tests:test
|
||||
- name: Upload Build Reports on Failure
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: build-reports
|
||||
path: '**/build/reports/'
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ plugins {
|
||||
id 'com.github.bjornvester.xjc' version '1.8.2' apply false
|
||||
id 'com.gradleup.shadow' version "9.2.2" apply false
|
||||
id 'me.champeau.jmh' version '0.7.2' apply false
|
||||
id 'io.spring.nullability' version '0.0.9' apply false
|
||||
id 'io.spring.nullability' version '0.0.11' apply false
|
||||
}
|
||||
|
||||
ext {
|
||||
|
||||
@@ -50,7 +50,7 @@ public class CheckstyleConventions {
|
||||
project.getPlugins().apply(CheckstylePlugin.class);
|
||||
project.getTasks().withType(Checkstyle.class).forEach(checkstyle -> checkstyle.getMaxHeapSize().set("1g"));
|
||||
CheckstyleExtension checkstyle = project.getExtensions().getByType(CheckstyleExtension.class);
|
||||
checkstyle.setToolVersion("13.0.0");
|
||||
checkstyle.setToolVersion("13.2.0");
|
||||
checkstyle.getConfigDirectory().set(project.getRootProject().file("src/checkstyle"));
|
||||
String version = SpringJavaFormatPlugin.class.getPackage().getImplementationVersion();
|
||||
DependencySet checkstyleDependencies = project.getConfigurations().getByName("checkstyle").getDependencies();
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 158 KiB |
@@ -17,6 +17,7 @@
|
||||
class="st5"
|
||||
id="svg5499"
|
||||
version="1.1"
|
||||
font-family="Helvetica, Arial, sans-serif"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="mvc-splitted-contexts.svg"
|
||||
style="font-size:12px;overflow:visible;color-interpolation-filters:sRGB;fill:none;fill-rule:evenodd;stroke-linecap:square;stroke-miterlimit:3"
|
||||
@@ -36,7 +37,7 @@
|
||||
inkscape:stockid="Arrow2Mend"><path
|
||||
transform="scale(0.6) rotate(180) translate(0,0)"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
id="path9164" /></marker><marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
@@ -47,7 +48,7 @@
|
||||
inkscape:stockid="Arrow2Mend"><path
|
||||
transform="scale(0.6) rotate(180) translate(0,0)"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
id="path8836" /></marker><marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
@@ -58,7 +59,7 @@
|
||||
inkscape:stockid="Arrow2Mend"><path
|
||||
transform="scale(0.6) rotate(180) translate(0,0)"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
id="path8520" /></marker><marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
@@ -69,7 +70,7 @@
|
||||
inkscape:stockid="Arrow2Mend"><path
|
||||
transform="scale(0.6) rotate(180) translate(0,0)"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
id="path8216" /></marker><marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
@@ -80,7 +81,7 @@
|
||||
inkscape:stockid="Arrow2Mend"><path
|
||||
transform="scale(0.6) rotate(180) translate(0,0)"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
id="path7924" /></marker><marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
@@ -91,7 +92,7 @@
|
||||
inkscape:stockid="Arrow2Mend"><path
|
||||
transform="scale(0.6) rotate(180) translate(0,0)"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
id="path7644" /></marker><marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
@@ -102,7 +103,7 @@
|
||||
inkscape:stockid="Arrow2Mend"><path
|
||||
transform="scale(0.6) rotate(180) translate(0,0)"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
id="path7375" /></marker><marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
@@ -113,7 +114,7 @@
|
||||
inkscape:stockid="Arrow2Mend"><path
|
||||
transform="scale(0.6) rotate(180) translate(0,0)"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
id="path7119" /></marker><marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
@@ -124,7 +125,7 @@
|
||||
inkscape:stockid="Arrow2Mend"><path
|
||||
transform="scale(0.6) rotate(180) translate(0,0)"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
id="path6875" /></marker><marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
@@ -135,7 +136,7 @@
|
||||
inkscape:stockid="Arrow2Mend"><path
|
||||
transform="scale(0.6) rotate(180) translate(0,0)"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
id="path6643" /></marker><marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
@@ -146,7 +147,7 @@
|
||||
inkscape:stockid="Arrow2Mend"><path
|
||||
transform="scale(0.6) rotate(180) translate(0,0)"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
id="path6423" /></marker><marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
@@ -157,7 +158,7 @@
|
||||
inkscape:stockid="Arrow2Mend"><path
|
||||
transform="scale(0.6) rotate(180) translate(0,0)"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
id="path6215" /></marker><marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
@@ -168,7 +169,7 @@
|
||||
inkscape:stockid="Arrow2Mend"><path
|
||||
transform="scale(0.6) rotate(180) translate(0,0)"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
id="path6019" /></marker><marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
@@ -179,7 +180,7 @@
|
||||
inkscape:stockid="Arrow2Mend"><path
|
||||
transform="scale(0.6) rotate(180) translate(0,0)"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
id="path5835" /></marker><marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
@@ -190,7 +191,7 @@
|
||||
inkscape:stockid="Arrow2Mend"><path
|
||||
transform="scale(0.6) rotate(180) translate(0,0)"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
id="path5663" /></marker><marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
@@ -201,7 +202,7 @@
|
||||
inkscape:stockid="Arrow2Mend"><path
|
||||
transform="scale(0.6) rotate(180) translate(0,0)"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
id="path5503" /></marker><marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
@@ -212,7 +213,7 @@
|
||||
inkscape:stockid="Arrow2Mend"><path
|
||||
transform="scale(0.6) rotate(180) translate(0,0)"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
id="path5355" /></marker><marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
@@ -223,7 +224,7 @@
|
||||
inkscape:stockid="Arrow2Mend"><path
|
||||
transform="scale(0.6) rotate(180) translate(0,0)"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
id="path5219" /></marker><marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
@@ -234,7 +235,7 @@
|
||||
inkscape:stockid="Arrow2Mend"><path
|
||||
transform="scale(0.6) rotate(180) translate(0,0)"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
id="path5095" /></marker><marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
@@ -245,7 +246,7 @@
|
||||
inkscape:stockid="Arrow2Mend"><path
|
||||
transform="scale(0.6) rotate(180) translate(0,0)"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
id="path4983" /></marker><marker
|
||||
inkscape:stockid="Arrow2Mend"
|
||||
orient="auto"
|
||||
@@ -256,7 +257,7 @@
|
||||
inkscape:isstock="true"
|
||||
inkscape:collect="always"><path
|
||||
id="path7394"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
transform="scale(0.6) rotate(180) translate(0,0)" /></marker><marker
|
||||
inkscape:stockid="Arrow2Lend"
|
||||
@@ -267,7 +268,7 @@
|
||||
style="overflow:visible;"
|
||||
inkscape:isstock="true"><path
|
||||
id="path8123"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
transform="scale(1.1) rotate(180) translate(1,0)" /></marker><marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
@@ -279,7 +280,7 @@
|
||||
inkscape:isstock="true"><path
|
||||
id="path8035"
|
||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke:#333333;stroke-width:1pt;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
transform="scale(0.4) rotate(180) translate(10,0)" /></marker><marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
@@ -290,7 +291,7 @@
|
||||
inkscape:isstock="true"><path
|
||||
id="path7959"
|
||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke:#333333;stroke-width:1pt;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
transform="scale(0.4) rotate(180) translate(10,0)" /></marker><marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
@@ -301,7 +302,7 @@
|
||||
inkscape:isstock="true"><path
|
||||
id="path7376"
|
||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke:#333333;stroke-width:1pt;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
transform="scale(0.4) rotate(180) translate(10,0)" /></marker><marker
|
||||
inkscape:stockid="Arrow2Lend"
|
||||
orient="auto"
|
||||
@@ -311,7 +312,7 @@
|
||||
style="overflow:visible;"
|
||||
inkscape:isstock="true"><path
|
||||
id="path7388"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
transform="scale(1.1) rotate(180) translate(1,0)" /></marker><marker
|
||||
inkscape:stockid="Arrow1Lend"
|
||||
@@ -323,7 +324,7 @@
|
||||
inkscape:isstock="true"><path
|
||||
id="path7370"
|
||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke:#333333;stroke-width:1pt;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
transform="scale(0.8) rotate(180) translate(12.5,0)" /></marker></defs><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
@@ -352,9 +353,9 @@
|
||||
v:viewMarkup="false" /><style
|
||||
type="text/css"
|
||||
id="style5501"><![CDATA[
|
||||
.st1 {fill:#969696;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.24}
|
||||
.st2 {fill:#dde2cd;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.24}
|
||||
.st3 {fill:#000000;font-family:Arial;font-size:2.50001em;font-weight:bold}
|
||||
.st1 {fill:#969696;stroke:#333333;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.24}
|
||||
.st2 {fill:#F8F9FA;stroke:#333333;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.24}
|
||||
.st3 {fill:#333333;font-family:Helvetica, Arial, sans-serif;font-size:2.50001em;font-weight:bold}
|
||||
.st4 {font-size:0.333333em;font-weight:normal}
|
||||
.st5 {fill:none;fill-rule:evenodd;font-size:12;overflow:visible;stroke-linecap:square;stroke-miterlimit:3}
|
||||
]]></style><g
|
||||
@@ -363,20 +364,22 @@
|
||||
v:groupContext="foregroundPage"
|
||||
id="g5503"
|
||||
transform="matrix(0.99998201,0,0,1.0824094,-40.812382,-98.908648)"><rect
|
||||
style="fill:#dde2cd;fill-opacity:1;stroke:#000000;stroke-width:1.53790233;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
|
||||
style="fill:#F8F9FA;fill-opacity:1;stroke:#6db33f;stroke-width:2;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect6599"
|
||||
width="382.68423"
|
||||
height="146.09897"
|
||||
x="87.884865"
|
||||
y="148.26482" /><v:userDefs><v:ud
|
||||
y="148.26482"
|
||||
rx="8" /><v:userDefs><v:ud
|
||||
v:nameU="SchemeName"
|
||||
v:val="VT4(Default)" /></v:userDefs><rect
|
||||
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:1.53790233;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
|
||||
style="fill:none;fill-opacity:1;stroke:#6db33f;stroke-width:2;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect5725"
|
||||
width="464.31128"
|
||||
height="374.11411"
|
||||
x="41.684383"
|
||||
y="112.3262" /><title
|
||||
y="112.3262"
|
||||
rx="8" /><title
|
||||
id="title5505">Page-1</title><v:pageProperties
|
||||
v:drawingScale="0.0393701"
|
||||
v:pageScale="0.0393701"
|
||||
@@ -385,15 +388,16 @@
|
||||
v:shadowOffsetY="-8.50394" /><v:layer
|
||||
v:name="Connector"
|
||||
v:index="0" /><rect
|
||||
style="fill:#dde2cd;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.53790233;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
|
||||
style="fill:#F8F9FA;fill-opacity:1;fill-rule:evenodd;stroke:#6db33f;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect5715"
|
||||
width="322.8194"
|
||||
height="43.63184"
|
||||
x="119.95335"
|
||||
y="-135.66222"
|
||||
transform="scale(1,-1)" /><text
|
||||
transform="scale(1,-1)"
|
||||
rx="8" /><text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:30.7580471px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:30.7580471px;line-height:125%;font-family:Helvetica, Arial, sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#333333;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="168.843"
|
||||
y="124.32391"
|
||||
id="text5717"
|
||||
@@ -401,12 +405,12 @@
|
||||
transform="scale(1.0403984,0.96117025)"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan5719"
|
||||
x="168.843"
|
||||
x="172.843"
|
||||
y="124.32391"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:23.06853676px;font-family:sans-serif;-inkscape-font-specification:sans-serif">DispatcherServlet</tspan></text>
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:23.06853676px;font-family:Helvetica, Arial, sans-serif;-inkscape-font-specification:sans-serif;fill:#333333">DispatcherServlet</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Helvetica, Arial, sans-serif;letter-spacing:0px;word-spacing:0px;fill:#333333;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="89.770851"
|
||||
y="181.20923"
|
||||
id="text6589"
|
||||
@@ -414,12 +418,12 @@
|
||||
transform="scale(1.0403984,0.96117025)"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan6591"
|
||||
x="89.770851"
|
||||
x="106.770851"
|
||||
y="181.20923"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:23.06853676px;font-family:sans-serif;-inkscape-font-specification:sans-serif">Servlet WebApplicationContext</tspan></text>
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:23.06853676px;font-family:Helvetica, Arial, sans-serif;-inkscape-font-specification:sans-serif;fill:#333333">Servlet WebApplicationContext</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Helvetica, Arial, sans-serif;letter-spacing:0px;word-spacing:0px;fill:#333333;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="260.00443"
|
||||
y="198.41273"
|
||||
id="text6593"
|
||||
@@ -429,36 +433,39 @@
|
||||
id="tspan6595"
|
||||
x="260.00443"
|
||||
y="198.41273"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.53426838px;font-family:sans-serif;-inkscape-font-specification:sans-serif;text-align:center;text-anchor:middle">(containing controllers, view resolvers,</tspan><tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.53426838px;font-family:Helvetica, Arial, sans-serif;-inkscape-font-specification:sans-serif;text-align:center;text-anchor:middle;fill:#555555">(containing controllers, view resolvers,</tspan><tspan
|
||||
sodipodi:role="line"
|
||||
x="260.00443"
|
||||
y="212.83057"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.53426838px;font-family:sans-serif;-inkscape-font-specification:sans-serif;text-align:center;text-anchor:middle"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.53426838px;font-family:Helvetica, Arial, sans-serif;-inkscape-font-specification:sans-serif;text-align:center;text-anchor:middle;fill:#555555"
|
||||
id="tspan6597">and other web-related beans)</tspan></text>
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.86203903;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#6db33f;stroke-width:1.5;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect6620"
|
||||
width="82.040657"
|
||||
height="36.72575"
|
||||
x="114.52653"
|
||||
y="-259.43161"
|
||||
transform="scale(1,-1)" /><rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.89166164;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
|
||||
transform="scale(1,-1)"
|
||||
rx="4" /><rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#6db33f;stroke-width:1.5;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect6622"
|
||||
width="87.843979"
|
||||
height="36.697304"
|
||||
x="223.39864"
|
||||
y="-287.19809"
|
||||
transform="scale(1,-1)" /><rect
|
||||
transform="scale(1,-1)"
|
||||
rx="4" /><rect
|
||||
transform="scale(1,-1)"
|
||||
y="-264.81918"
|
||||
x="117.92834"
|
||||
height="36.72575"
|
||||
width="82.040657"
|
||||
id="rect6614"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.86203903;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1" /><text
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#6db33f;stroke-width:1.5;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
|
||||
rx="4" /><text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Helvetica, Arial, sans-serif;letter-spacing:0px;word-spacing:0px;fill:#333333;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="121.24728"
|
||||
y="260.14957"
|
||||
id="text6616"
|
||||
@@ -466,20 +473,20 @@
|
||||
transform="scale(1.0403984,0.96117025)"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan6618"
|
||||
x="121.24728"
|
||||
x="124.24728"
|
||||
y="260.14957"
|
||||
style="font-size:11.53426838px">Controllers</tspan></text>
|
||||
style="font-size:11.53426838px;fill:#333333">Controllers</tspan></text>
|
||||
<text
|
||||
transform="scale(1.0403984,0.96117025)"
|
||||
sodipodi:linespacing="125%"
|
||||
id="text6624"
|
||||
y="282.70709"
|
||||
x="219.61203"
|
||||
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Helvetica, Arial, sans-serif;letter-spacing:0px;word-spacing:0px;fill:#333333;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
xml:space="preserve"><tspan
|
||||
style="font-size:11.53426838px"
|
||||
style="font-size:11.53426838px;fill:#333333"
|
||||
y="282.70709"
|
||||
x="219.61203"
|
||||
x="221.61203"
|
||||
id="tspan6626"
|
||||
sodipodi:role="line">ViewResolver</tspan></text>
|
||||
<rect
|
||||
@@ -489,9 +496,10 @@
|
||||
height="36.577778"
|
||||
width="114.4539"
|
||||
id="rect6628"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.0161339;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1" /><text
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#6db33f;stroke-width:1.5;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
|
||||
rx="4" /><text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Helvetica, Arial, sans-serif;letter-spacing:0px;word-spacing:0px;fill:#333333;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="327.51276"
|
||||
y="255.28464"
|
||||
id="text6630"
|
||||
@@ -499,26 +507,27 @@
|
||||
transform="scale(1.0403984,0.96117025)"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan6632"
|
||||
x="327.51276"
|
||||
x="332.51276"
|
||||
y="255.28464"
|
||||
style="font-size:11.53426838px">HandlerMapping</tspan></text>
|
||||
style="font-size:11.53426838px;fill:#333333">HandlerMapping</tspan></text>
|
||||
<rect
|
||||
y="338.69724"
|
||||
x="87.803261"
|
||||
height="121.5683"
|
||||
width="382.84744"
|
||||
id="rect6634"
|
||||
style="fill:#dde2cd;fill-opacity:1;stroke:#000000;stroke-width:1.53790233;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1" /><text
|
||||
style="fill:#F8F9FA;fill-opacity:1;stroke:#6db33f;stroke-width:2;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
|
||||
rx="8" /><text
|
||||
transform="scale(1.0403984,0.96117025)"
|
||||
sodipodi:linespacing="125%"
|
||||
id="text6636"
|
||||
y="376.61673"
|
||||
x="108.61351"
|
||||
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Helvetica, Arial, sans-serif;letter-spacing:0px;word-spacing:0px;fill:#333333;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
xml:space="preserve"><tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:23.06853676px;font-family:sans-serif;-inkscape-font-specification:sans-serif"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:23.06853676px;font-family:Helvetica, Arial, sans-serif;-inkscape-font-specification:sans-serif;fill:#333333"
|
||||
y="376.61673"
|
||||
x="108.61351"
|
||||
x="113.61351"
|
||||
id="tspan6638"
|
||||
sodipodi:role="line">Root WebApplicationContext</tspan></text>
|
||||
<text
|
||||
@@ -527,10 +536,10 @@
|
||||
id="text6640"
|
||||
y="395.35812"
|
||||
x="260.93863"
|
||||
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Helvetica, Arial, sans-serif;letter-spacing:0px;word-spacing:0px;fill:#333333;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
xml:space="preserve"><tspan
|
||||
id="tspan6644"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.53426838px;font-family:sans-serif;-inkscape-font-specification:sans-serif;text-align:center;text-anchor:middle"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.53426838px;font-family:Helvetica, Arial, sans-serif;-inkscape-font-specification:sans-serif;text-align:center;text-anchor:middle;fill:#555555"
|
||||
y="395.35812"
|
||||
x="260.93863"
|
||||
sodipodi:role="line">(containing middle-tier services, datasources, etc.)</tspan></text>
|
||||
@@ -541,43 +550,47 @@
|
||||
height="36.72575"
|
||||
width="82.040657"
|
||||
id="rect6648"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.86203903;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1" /><rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.86203903;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#6db33f;stroke-width:1.5;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
|
||||
rx="4" /><rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#6db33f;stroke-width:1.5;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect6650"
|
||||
width="82.040657"
|
||||
height="36.72575"
|
||||
x="167.73116"
|
||||
y="-445.45563"
|
||||
transform="scale(1,-1)" /><text
|
||||
transform="scale(1,-1)"
|
||||
rx="4" /><text
|
||||
transform="scale(1.0403984,0.96117025)"
|
||||
sodipodi:linespacing="125%"
|
||||
id="text6652"
|
||||
y="448.55054"
|
||||
x="175.87148"
|
||||
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Helvetica, Arial, sans-serif;letter-spacing:0px;word-spacing:0px;fill:#333333;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
xml:space="preserve"><tspan
|
||||
style="font-size:11.53426838px"
|
||||
style="font-size:11.53426838px;fill:#333333"
|
||||
y="448.55054"
|
||||
x="175.87148"
|
||||
x="178.87148"
|
||||
id="tspan6654"
|
||||
sodipodi:role="line">Services</tspan></text>
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.88435173;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#6db33f;stroke-width:1.5;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect6656"
|
||||
width="86.393044"
|
||||
height="36.704323"
|
||||
x="306.86328"
|
||||
y="-439.60837"
|
||||
transform="scale(1,-1)" /><rect
|
||||
transform="scale(1,-1)"
|
||||
rx="4" /><rect
|
||||
transform="scale(1,-1)"
|
||||
y="-444.99475"
|
||||
x="310.26624"
|
||||
height="36.701977"
|
||||
width="86.876686"
|
||||
id="rect6658"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.88679528;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1" /><text
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#6db33f;stroke-width:1.5;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
|
||||
rx="4" /><text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Helvetica, Arial, sans-serif;letter-spacing:0px;word-spacing:0px;fill:#333333;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="305.30771"
|
||||
y="448.55054"
|
||||
id="text6660"
|
||||
@@ -585,20 +598,20 @@
|
||||
transform="scale(1.0403984,0.96117025)"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan6662"
|
||||
x="305.30771"
|
||||
x="308.30771"
|
||||
y="448.55054"
|
||||
style="font-size:11.53426838px">Repositories</tspan></text>
|
||||
style="font-size:11.53426838px;fill:#333333">Repositories</tspan></text>
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.76895118px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
style="fill:none;fill-rule:evenodd;stroke:#333333;stroke-width:0.76895118px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 265.33234,295.60379 c 0,42.65169 0,42.65169 0,0 z"
|
||||
id="path7643"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.46028023;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow2Mend)"
|
||||
style="fill:#333333;fill-opacity:1;fill-rule:evenodd;stroke:#333333;stroke-width:3.46028023;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow2Mend)"
|
||||
d="m 270.43505,294.70585 c 0,39.4721 0,39.87903 0,39.87903"
|
||||
id="path7645"
|
||||
inkscape:connector-curvature="0" /><text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Helvetica, Arial, sans-serif;letter-spacing:0px;word-spacing:0px;fill:#333333;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="352.55331"
|
||||
y="333.03622"
|
||||
id="text4963"
|
||||
@@ -607,6 +620,6 @@
|
||||
sodipodi:role="line"
|
||||
x="352.55331"
|
||||
y="333.03622"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.53426838px;font-family:sans-serif;-inkscape-font-specification:sans-serif;text-align:center;text-anchor:middle"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.53426838px;font-family:Helvetica, Arial, sans-serif;-inkscape-font-specification:sans-serif;text-align:center;text-anchor:middle;fill:#555555"
|
||||
id="tspan4965">Delegates if no bean found</tspan></text>
|
||||
</g></svg>
|
||||
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 31 KiB |
@@ -19,10 +19,6 @@ its behavior changes.
|
||||
| Initialization of `SmartInitializingSingleton` beans.
|
||||
| `beanName` the name of the bean.
|
||||
|
||||
| `spring.context.annotated-bean-reader.create`
|
||||
| Creation of the `AnnotatedBeanDefinitionReader`.
|
||||
|
|
||||
|
||||
| `spring.context.base-packages.scan`
|
||||
| Scanning of base packages.
|
||||
| `packages` array of base packages for scanning.
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
[[beans-standard-annotations]]
|
||||
= Using JSR 330 Standard Annotations
|
||||
= Using JSR-330 Standard Annotations
|
||||
|
||||
Spring offers support for JSR-330 standard annotations (Dependency Injection). Those
|
||||
annotations are scanned in the same way as the Spring annotations. To use them, you need
|
||||
to have the relevant jars in your classpath.
|
||||
Spring offers support for JSR-330 standard _Dependency Injection_ annotations which are
|
||||
available in the `jakarta.inject` package. These annotations may optionally be used as
|
||||
alternatives to Spring annotations.
|
||||
|
||||
To use them, you need to have the relevant jar in your classpath. For example, the
|
||||
`jakarta.inject` artifact is available in the standard Maven repository
|
||||
(`https://repo.maven.apache.org/maven2/jakarta/inject/jakarta.inject-api/2.0.0/`),
|
||||
|
||||
[NOTE]
|
||||
=====
|
||||
If you use Maven, the `jakarta.inject` artifact is available in the standard Maven
|
||||
repository (
|
||||
https://repo.maven.apache.org/maven2/jakarta/inject/jakarta.inject-api/2.0.0/[https://repo.maven.apache.org/maven2/jakarta/inject/jakarta.inject-api/2.0.0/]).
|
||||
You can add the following dependency to your file pom.xml:
|
||||
If you use Maven, you can add the following dependency to your `pom.xml` file.
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@@ -26,13 +27,14 @@ You can add the following dependency to your file pom.xml:
|
||||
[[beans-inject-named]]
|
||||
== Dependency Injection with `@Inject` and `@Named`
|
||||
|
||||
Instead of `@Autowired`, you can use `@jakarta.inject.Inject` as follows:
|
||||
Instead of using `@Autowired` for dependency injection, you may optionally choose to use
|
||||
`@jakarta.inject.Inject` as follows.
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
[source,java,indent=0,subs="verbatim,quotes",fold="-imports"]
|
||||
----
|
||||
import jakarta.inject.Inject;
|
||||
|
||||
@@ -54,7 +56,7 @@ Java::
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",fold="-imports"]
|
||||
----
|
||||
import jakarta.inject.Inject
|
||||
|
||||
@@ -72,17 +74,19 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
As with `@Autowired`, you can use `@Inject` at the field level, method level
|
||||
and constructor-argument level. Furthermore, you may declare your injection point as a
|
||||
`Provider`, allowing for on-demand access to beans of shorter scopes or lazy access to
|
||||
other beans through a `Provider.get()` call. The following example offers a variant of the
|
||||
preceding example:
|
||||
As with `@Autowired`, you can use `@Inject` at the field level, method level, and
|
||||
constructor-argument level.
|
||||
|
||||
Furthermore, as an alternative to Spring's `ObjectProvider` mechanism, you may choose to
|
||||
declare your injection point as a `jakarta.inject.Provider`, allowing for on-demand
|
||||
access to beans of shorter scopes or lazy access to other beans through a
|
||||
`Provider.get()` call. The following example offers a variant of the preceding example.
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
[source,java,indent=0,subs="verbatim,quotes",fold="-imports"]
|
||||
----
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Provider;
|
||||
@@ -105,9 +109,10 @@ Java::
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",fold="-imports"]
|
||||
----
|
||||
import jakarta.inject.Inject
|
||||
import jakarta.inject.Provider
|
||||
|
||||
class SimpleMovieLister {
|
||||
|
||||
@@ -123,14 +128,15 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
If you would like to use a qualified name for the dependency that should be injected,
|
||||
you should use the `@Named` annotation, as the following example shows:
|
||||
If you would like to use a qualified name for the dependency that should be injected, you
|
||||
may choose to use the `@Named` annotation as an alternative to Spring's `@Qualifier`
|
||||
support, as the following example shows.
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
[source,java,indent=0,subs="verbatim,quotes",fold="-imports"]
|
||||
----
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Named;
|
||||
@@ -150,7 +156,7 @@ Java::
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",fold="-imports"]
|
||||
----
|
||||
import jakarta.inject.Inject
|
||||
import jakarta.inject.Named
|
||||
@@ -170,12 +176,15 @@ Kotlin::
|
||||
======
|
||||
|
||||
As with `@Autowired`, `@Inject` can also be used with `java.util.Optional` or
|
||||
`@Nullable`. This is even more applicable here, since `@Inject` does not have
|
||||
a `required` attribute. The following pair of examples show how to use `@Inject` and
|
||||
`@Nullable`:
|
||||
`@Nullable`. This is even more applicable here, since `@Inject` does not have a
|
||||
`required` attribute. The following examples show how to use `@Inject` with `Optional`,
|
||||
`@Nullable`, and Kotlin's built-in support for nullable types.
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
[source,java,indent=0,subs="verbatim,quotes",fold="-imports"]
|
||||
----
|
||||
import jakarta.inject.Inject;
|
||||
import java.util.Optional;
|
||||
|
||||
public class SimpleMovieLister {
|
||||
|
||||
@Inject
|
||||
@@ -189,8 +198,11 @@ a `required` attribute. The following pair of examples show how to use `@Inject`
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
[source,java,indent=0,subs="verbatim,quotes",fold="-imports"]
|
||||
----
|
||||
import jakarta.inject.Inject;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
public class SimpleMovieLister {
|
||||
|
||||
@Inject
|
||||
@@ -202,8 +214,10 @@ Java::
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",fold="-imports"]
|
||||
----
|
||||
import jakarta.inject.Inject
|
||||
|
||||
class SimpleMovieLister {
|
||||
|
||||
@Inject
|
||||
@@ -214,21 +228,21 @@ Kotlin::
|
||||
|
||||
|
||||
[[beans-named]]
|
||||
== `@Named` and `@ManagedBean`: Standard Equivalents to the `@Component` Annotation
|
||||
== `@Named`: Standard Equivalent to the `@Component` Annotation
|
||||
|
||||
Instead of `@Component`, you can use `@jakarta.inject.Named` or `jakarta.annotation.ManagedBean`,
|
||||
as the following example shows:
|
||||
Instead of `@Component` or other Spring stereotype annotations, you may optionally choose
|
||||
to use `@jakarta.inject.Named`, as the following example shows.
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
[source,java,indent=0,subs="verbatim,quotes",fold="-imports"]
|
||||
----
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Named;
|
||||
|
||||
@Named("movieListener") // @ManagedBean("movieListener") could be used as well
|
||||
@Named("movieListener")
|
||||
public class SimpleMovieLister {
|
||||
|
||||
private MovieFinder movieFinder;
|
||||
@@ -244,12 +258,12 @@ Java::
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",fold="-imports"]
|
||||
----
|
||||
import jakarta.inject.Inject
|
||||
import jakarta.inject.Named
|
||||
|
||||
@Named("movieListener") // @ManagedBean("movieListener") could be used as well
|
||||
@Named("movieListener")
|
||||
class SimpleMovieLister {
|
||||
|
||||
@Inject
|
||||
@@ -260,14 +274,15 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
It is very common to use `@Component` without specifying a name for the component.
|
||||
`@Named` can be used in a similar fashion, as the following example shows:
|
||||
It is very common to use `@Component` or other Spring stereotype annotations without
|
||||
specifying an explicit name for the component, and `@Named` can be used in a similar
|
||||
fashion, as the following example shows.
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
[source,java,indent=0,subs="verbatim,quotes",fold="-imports"]
|
||||
----
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Named;
|
||||
@@ -288,7 +303,7 @@ Java::
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",fold="-imports"]
|
||||
----
|
||||
import jakarta.inject.Inject
|
||||
import jakarta.inject.Named
|
||||
@@ -304,14 +319,14 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
When you use `@Named` or `@ManagedBean`, you can use component scanning in the
|
||||
exact same way as when you use Spring annotations, as the following example shows:
|
||||
When you use `@Named`, you can use component scanning in the exact same way as when you
|
||||
use Spring annotations, as the following example shows.
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
[source,java,indent=0,subs="verbatim,quotes",fold="-imports"]
|
||||
----
|
||||
@Configuration
|
||||
@ComponentScan(basePackages = "org.example")
|
||||
@@ -322,7 +337,7 @@ Java::
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",fold="-imports"]
|
||||
----
|
||||
@Configuration
|
||||
@ComponentScan(basePackages = ["org.example"])
|
||||
@@ -332,55 +347,106 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
NOTE: In contrast to `@Component`, the JSR-330 `@Named` and the JSR-250 `@ManagedBean`
|
||||
annotations are not composable. You should use Spring's stereotype model for building
|
||||
custom component annotations.
|
||||
NOTE: In contrast to `@Component`, the JSR-330 `@Named` annotation is not composable. You
|
||||
should use Spring's stereotype model for building custom component annotations.
|
||||
|
||||
[TIP]
|
||||
====
|
||||
If you work with legacy systems that still use `@javax.inject.Named` or
|
||||
`@javax.annotation.ManagedBean` for components (note the `javax` package namespace), you
|
||||
can explicitly configure component scanning to include those annotation types, as shown
|
||||
in the following example.
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",fold="-imports"]
|
||||
----
|
||||
@Configuration
|
||||
@ComponentScan(
|
||||
basePackages = "org.example",
|
||||
includeFilters = @Filter({
|
||||
javax.inject.Named.class,
|
||||
javax.annotation.ManagedBean.class
|
||||
})
|
||||
)
|
||||
public class AppConfig {
|
||||
// ...
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",fold="-imports"]
|
||||
----
|
||||
@Configuration
|
||||
@ComponentScan(
|
||||
basePackages = ["org.example"],
|
||||
includeFilters = [Filter([
|
||||
javax.inject.Named::class,
|
||||
javax.annotation.ManagedBean::class
|
||||
])]
|
||||
)
|
||||
class AppConfig {
|
||||
// ...
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
In addition, if you would like for the `value` attributes in `@javax.inject.Named` and
|
||||
`@javax.annotation.ManagedBean` to be used as component names, you need to override the
|
||||
`isStereotypeWithNameValue(...)` method in `AnnotationBeanNameGenerator` to add explicit
|
||||
support for `javax.annotation.ManagedBean` and `javax.inject.Named` and register your
|
||||
custom `AnnotationBeanNameGenerator` via the `nameGenerator` attribute in
|
||||
`@ComponentScan`.
|
||||
====
|
||||
|
||||
|
||||
[[beans-standard-annotations-limitations]]
|
||||
== Limitations of JSR-330 Standard Annotations
|
||||
|
||||
When you work with standard annotations, you should know that some significant
|
||||
features are not available, as the following table shows:
|
||||
When you work with JSR-330 standard annotations, you should know that some significant
|
||||
features are not available, as the following table shows.
|
||||
|
||||
[[annotations-comparison]]
|
||||
.Spring component model elements versus JSR-330 variants
|
||||
.Spring component model versus JSR-330 variants
|
||||
|===
|
||||
| Spring| jakarta.inject.*| jakarta.inject restrictions / comments
|
||||
| Spring | JSR-330 | JSR-330 restrictions / comments
|
||||
|
||||
| @Autowired
|
||||
| @Inject
|
||||
| `@Inject` has no 'required' attribute. Can be used with Java 8's `Optional` instead.
|
||||
| `@Autowired`
|
||||
| `@Inject`
|
||||
| `@Inject` has no `required` attribute. Can be used with Java's `Optional` instead.
|
||||
|
||||
| @Component
|
||||
| @Named / @ManagedBean
|
||||
| `@Component`
|
||||
| `@Named`
|
||||
| JSR-330 does not provide a composable model, only a way to identify named components.
|
||||
|
||||
| @Scope("singleton")
|
||||
| @Singleton
|
||||
| `@Scope("singleton")`
|
||||
| `@Singleton`
|
||||
| The JSR-330 default scope is like Spring's `prototype`. However, in order to keep it
|
||||
consistent with Spring's general defaults, a JSR-330 bean declared in the Spring
|
||||
container is a `singleton` by default. In order to use a scope other than `singleton`,
|
||||
you should use Spring's `@Scope` annotation. `jakarta.inject` also provides a
|
||||
`jakarta.inject.Scope` annotation: however, this one is only intended to be used
|
||||
`jakarta.inject.Scope` annotation; however, this one is only intended to be used
|
||||
for creating custom annotations.
|
||||
|
||||
| @Qualifier
|
||||
| @Qualifier / @Named
|
||||
| `@Qualifier`
|
||||
| `@Qualifier` / `@Named`
|
||||
| `jakarta.inject.Qualifier` is just a meta-annotation for building custom qualifiers.
|
||||
Concrete `String` qualifiers (like Spring's `@Qualifier` with a value) can be associated
|
||||
through `jakarta.inject.Named`.
|
||||
|
||||
| @Value
|
||||
| `@Value`
|
||||
| -
|
||||
| no equivalent
|
||||
|
||||
| @Lazy
|
||||
| `@Lazy`
|
||||
| -
|
||||
| no equivalent
|
||||
|
||||
| ObjectFactory
|
||||
| Provider
|
||||
| `ObjectFactory`
|
||||
| `Provider`
|
||||
| `jakarta.inject.Provider` is a direct alternative to Spring's `ObjectFactory`,
|
||||
only with a shorter `get()` method name. It can also be used in combination with
|
||||
Spring's `@Autowired` or with non-annotated constructors and setter methods.
|
||||
|
||||
@@ -14,7 +14,7 @@ As of Spring Framework 7.0, Spring requires Hibernate ORM 7.x for Spring's
|
||||
`HibernateJpaVendorAdapter`.
|
||||
|
||||
The `org.springframework.orm.jpa.hibernate` package supersedes the former `orm.hibernate5`:
|
||||
now for use with Hibernate ORM 7.1+, tightly integrated with `HibernateJpaVendorAdapter`
|
||||
now for use with Hibernate ORM 7.x, tightly integrated with `HibernateJpaVendorAdapter`
|
||||
as well as supporting Hibernate's native `SessionFactory.getCurrentSession()` style.
|
||||
====
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
[[coroutines]]
|
||||
= Coroutines
|
||||
|
||||
Kotlin {kotlin-docs}/coroutines-overview.html[Coroutines] are Kotlin
|
||||
lightweight threads allowing to write non-blocking code in an imperative way. On language side,
|
||||
Kotlin {kotlin-docs}/coroutines-overview.html[Coroutines] are instances of
|
||||
suspendable computations allowing to write non-blocking code in an imperative way. On language side,
|
||||
suspending functions provides an abstraction for asynchronous operations while on library side
|
||||
{kotlin-github-org}/kotlinx.coroutines[kotlinx.coroutines] provides functions like
|
||||
{kotlin-coroutines-api}/kotlinx-coroutines-core/kotlinx.coroutines/async.html[`async { }`]
|
||||
|
||||
+43
-1
@@ -5,6 +5,7 @@ The following annotations are supported when used in conjunction with the
|
||||
xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit-jupiter-extension[`SpringExtension`]
|
||||
and JUnit Jupiter (that is, the programming model in JUnit):
|
||||
|
||||
* xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-springextensionconfig[`@SpringExtensionConfig`]
|
||||
* xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-junit-jupiter-springjunitconfig[`@SpringJUnitConfig`]
|
||||
* xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-junit-jupiter-springjunitwebconfig[`@SpringJUnitWebConfig`]
|
||||
* xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-testconstructor[`@TestConstructor`]
|
||||
@@ -14,6 +15,47 @@ and JUnit Jupiter (that is, the programming model in JUnit):
|
||||
* xref:testing/annotations/integration-spring/annotation-disabledinaotmode.adoc[`@DisabledInAotMode`]
|
||||
|
||||
|
||||
[[integration-testing-annotations-springextensionconfig]]
|
||||
== `@SpringExtensionConfig`
|
||||
|
||||
`@SpringExtensionConfig` is a type-level annotation that can be used to configure the
|
||||
behavior of the `SpringExtension`.
|
||||
|
||||
As of Spring Framework 7.0, the `SpringExtension` is configured to use a test-method
|
||||
scoped `ExtensionContext`, which enables consistent dependency injection into fields and
|
||||
constructors from the `ApplicationContext` for the current test method in a `@Nested`
|
||||
test class hierarchy. However, if a third-party `TestExecutionListener` is not compatible
|
||||
with the semantics associated with a test-method scoped extension context — or if a
|
||||
developer wishes to switch to test-class scoped semantics — the `SpringExtension` can be
|
||||
configured to use a test-class scoped `ExtensionContext` by annotating a top-level test
|
||||
class with `@SpringExtensionConfig(useTestClassScopedExtensionContext = true)`.
|
||||
|
||||
[TIP]
|
||||
====
|
||||
If your top-level test class is configured to use JUnit Jupiter's
|
||||
`@TestInstance(Lifecycle.PER_CLASS)` semantics, the `SpringExtension` will always use a
|
||||
test-class scoped `ExtensionContext`, and there is no need to declare
|
||||
`@SpringExtensionConfig(useTestClassScopedExtensionContext = true)`.
|
||||
====
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
This annotation is currently only applicable to `@Nested` test class hierarchies and
|
||||
should be applied to the top-level enclosing class of a `@Nested` test class hierarchy.
|
||||
|
||||
Consequently, there is no need to declare this annotation on a test class that does not
|
||||
contain `@Nested` test classes.
|
||||
====
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-nestedtestconfiguration[`@NestedTestConfiguration`]
|
||||
does not apply to this annotation.
|
||||
|
||||
`@SpringExtensionConfig` will always be detected within a `@Nested` test class hierarchy,
|
||||
effectively disregarding any `@NestedTestConfiguration(OVERRIDE)` declarations.
|
||||
====
|
||||
|
||||
[[integration-testing-annotations-junit-jupiter-springjunitconfig]]
|
||||
== `@SpringJUnitConfig`
|
||||
|
||||
@@ -187,7 +229,7 @@ default mode may be set via the
|
||||
xref:appendix.adoc#appendix-spring-properties[`SpringProperties`] mechanism.
|
||||
|
||||
The default mode may also be configured as a
|
||||
https://docs.junit.org/current/user-guide/#running-tests-config-params[JUnit Platform configuration parameter].
|
||||
https://docs.junit.org/current/running-tests/configuration-parameters.html[JUnit Platform configuration parameter].
|
||||
|
||||
If the `spring.test.constructor.autowire.mode` property is not set, test class
|
||||
constructors will not be automatically autowired.
|
||||
|
||||
@@ -9,7 +9,6 @@ and can be used anywhere in the Spring Framework.
|
||||
* `@Qualifier`
|
||||
* `@Value`
|
||||
* `@Resource` (jakarta.annotation) if JSR-250 is present
|
||||
* `@ManagedBean` (jakarta.annotation) if JSR-250 is present
|
||||
* `@Inject` (jakarta.inject) if JSR-330 is present
|
||||
* `@Named` (jakarta.inject) if JSR-330 is present
|
||||
* `@PersistenceContext` (jakarta.persistence) if JPA is present
|
||||
|
||||
+13
-2
@@ -25,7 +25,7 @@ TestNG:
|
||||
* Dependency injection for test constructors, test methods, and test lifecycle callback
|
||||
methods. See xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit-jupiter-di[Dependency
|
||||
Injection with the `SpringExtension`] for further details.
|
||||
* Powerful support for link:https://docs.junit.org/current/user-guide/#extensions-conditions[conditional
|
||||
* Powerful support for link:https://docs.junit.org/current/extensions/conditional-test-execution.html[conditional
|
||||
test execution] based on SpEL expressions, environment variables, system properties,
|
||||
and so on. See the documentation for `@EnabledIf` and `@DisabledIf` in
|
||||
xref:testing/annotations/integration-junit-jupiter.adoc[Spring JUnit Jupiter Testing Annotations]
|
||||
@@ -163,7 +163,7 @@ for further details.
|
||||
=== Dependency Injection with the `SpringExtension`
|
||||
|
||||
The `SpringExtension` implements the
|
||||
link:https://docs.junit.org/current/user-guide/#extensions-parameter-resolution[`ParameterResolver`]
|
||||
link:https://docs.junit.org/current/extensions/parameter-resolution.html[`ParameterResolver`]
|
||||
extension API from JUnit Jupiter, which lets Spring provide dependency injection for test
|
||||
constructors, test methods, and test lifecycle callback methods.
|
||||
|
||||
@@ -392,6 +392,17 @@ any of its subclasses and nested classes. Thus, you may annotate a top-level tes
|
||||
with `@NestedTestConfiguration`, and that will apply to all of its nested test classes
|
||||
recursively.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
As of Spring Framework 7.0, the `SpringExtension` uses a test-method scoped
|
||||
`ExtensionContext` within `@Nested` test class hierarchies by default. However, the
|
||||
`SpringExtension` can be configured to use a test-class scoped `ExtensionContext`.
|
||||
|
||||
See the documentation for
|
||||
xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-springextensionconfig[`@SpringExtensionConfig`]
|
||||
for details.
|
||||
====
|
||||
|
||||
[TIP]
|
||||
====
|
||||
If you are developing a component that integrates with the Spring TestContext Framework
|
||||
|
||||
@@ -39,9 +39,9 @@ required CORS response headers set.
|
||||
|
||||
In order to enable cross-origin requests (that is, the `Origin` header is present and
|
||||
differs from the host of the request), you need to have some explicitly declared CORS
|
||||
configuration. If no matching CORS configuration is found, preflight requests are
|
||||
rejected. No CORS headers are added to the responses of simple and actual CORS requests
|
||||
and, consequently, browsers reject them.
|
||||
configuration. If no matching CORS configuration is found, no CORS headers are added to
|
||||
the responses to preflight, simple and actual CORS requests and, consequently, browsers
|
||||
reject them.
|
||||
|
||||
Each `HandlerMapping` can be
|
||||
{spring-framework-api}/web/reactive/handler/AbstractHandlerMapping.html#setCorsConfigurations(java.util.Map)[configured]
|
||||
|
||||
@@ -32,9 +32,9 @@ any `@RequestMapping` method to render an RFC 9457 response. This is processed a
|
||||
- The `status` property of `ProblemDetail` determines the HTTP status.
|
||||
- The `instance` property of `ProblemDetail` is set from the current URL path, if not
|
||||
already set.
|
||||
- For content negotiation, the Jackson `HttpMessageConverter` prefers
|
||||
"application/problem+json" over "application/json" when rendering a `ProblemDetail`,
|
||||
and also falls back on it if no compatible media type is found.
|
||||
- The Jackson JSON and XML message converters use "application/problem+json" or
|
||||
"application/problem+xml" respectively as the producible media types for `ProblemDetail`
|
||||
to ensure they are favored for content negotiation.
|
||||
|
||||
To enable RFC 9457 responses for Spring WebFlux exceptions and for any
|
||||
`ErrorResponseException`, extend `ResponseEntityExceptionHandler` and declare it as an
|
||||
|
||||
@@ -381,16 +381,23 @@ See the section on xref:web/webflux-cors.adoc[CORS] and the xref:web/webflux-cor
|
||||
You may want your controller endpoints to match routes with or without a trailing slash in the URL path.
|
||||
For example, both "GET /home" and "GET /home/" should be handled by a controller method annotated with `@GetMapping("/home")`.
|
||||
|
||||
Adding trailing slash variants to all mapping declarations is not the best way to handle this use case.
|
||||
The `UrlHandlerFilter` web filter has been designed for this purpose. It can be configured to:
|
||||
Spring provides `UrlHandlerFilter` that removes the trailing slash from URL paths to ensure a consistent view of paths with or without a trailing slash.
|
||||
This is important to avoid a mismatch between URL-based authorization decisions and web framework request mappings.
|
||||
The filter can remove the trailing slash in one of a couple of ways:
|
||||
|
||||
* respond with an HTTP redirect status when receiving URLs with trailing slashes, sending browsers to the non-trailing slash URL variant.
|
||||
* mutate the request to act as if the request was sent without a trailing slash and continue the processing of the request.
|
||||
* respond with an HTTP redirect status that sends clients to the same path without a trailing slash.
|
||||
* mutate the request to remove the trailing slash.
|
||||
|
||||
Here is how you can instantiate and configure a `UrlHandlerFilter` for a blog application:
|
||||
|
||||
include-code::./UrlHandlerFilterConfiguration[tag=config,indent=0]
|
||||
|
||||
Keep in mind the following:
|
||||
|
||||
- the root path `"/"` is excluded from trailing slash handling.
|
||||
- `@RequestMapping("/")` adds a trailing slash to a type-level mapping, and therefore will
|
||||
not map when trailing slash handling applies; use `@RequestMapping` (no path attribute) instead.
|
||||
|
||||
|
||||
[[webflux-exception-handler]]
|
||||
== Exceptions
|
||||
|
||||
@@ -66,9 +66,9 @@ required CORS response headers set.
|
||||
|
||||
In order to enable cross-origin requests (that is, the `Origin` header is present and
|
||||
differs from the host of the request), you need to have some explicitly declared CORS
|
||||
configuration. If no matching CORS configuration is found, preflight requests are
|
||||
rejected. No CORS headers are added to the responses of simple and actual CORS requests
|
||||
and, consequently, browsers reject them.
|
||||
configuration. If no matching CORS configuration is found, no CORS headers are added to
|
||||
the responses to preflight, simple and actual CORS requests and, consequently, browsers
|
||||
reject them.
|
||||
|
||||
Each `HandlerMapping` can be
|
||||
{spring-framework-api}/web/servlet/handler/AbstractHandlerMapping.html#setCorsConfigurations(java.util.Map)[configured]
|
||||
|
||||
@@ -836,7 +836,6 @@ Java::
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
ClassPathResource index = new ClassPathResource("static/index.html");
|
||||
List<String> extensions = List.of("js", "css", "ico", "png", "jpg", "gif");
|
||||
RequestPredicate spaPredicate = path("/api/**").or(path("/error")).negate();
|
||||
RouterFunction<ServerResponse> redirectToIndex = route()
|
||||
.resource(spaPredicate, index)
|
||||
|
||||
@@ -120,17 +120,26 @@ See the sections on xref:web/webmvc-cors.adoc[CORS] and the xref:web/webmvc-cors
|
||||
== URL Handler
|
||||
[.small]#xref:web/webflux/reactive-spring.adoc#filters.url-handler[See equivalent in the Reactive stack]#
|
||||
|
||||
In previous Spring Framework versions, Spring MVC could be configured to ignore trailing slashes in URL paths
|
||||
when mapping incoming requests on controller methods. This means that sending a "GET /home/" request would be
|
||||
handled by a controller method annotated with `@GetMapping("/home")`.
|
||||
You may want your controller endpoints to match routes with or without a trailing slash in the URL path.
|
||||
For example, both "GET /home" and "GET /home/" should be handled by a controller method annotated with `@GetMapping("/home")`.
|
||||
|
||||
This option was deprecated in 6.0 and removed in 7.0, but applications are still expected to handle such
|
||||
requests in a safe way. The `UrlHandlerFilter` Servlet filter has been designed for this purpose.
|
||||
It can be configured to:
|
||||
Spring provides `UrlHandlerFilter` that removes the trailing slash from URL paths to ensure a consistent view of paths with or without a trailing slash.
|
||||
This is important to avoid a mismatch between URL-based authorization decisions and web framework request mappings.
|
||||
The filter can remove the trailing slash in one of a couple of ways:
|
||||
|
||||
* respond with an HTTP redirect status when receiving URLs with trailing slashes, sending browsers to the non-trailing slash URL variant.
|
||||
* wrap the request to act as if the request was sent without a trailing slash and continue the processing of the request.
|
||||
* respond with an HTTP redirect status that sends clients to the same path without a trailing slash.
|
||||
* wrap the request to remove the trailing slash.
|
||||
|
||||
NOTE: Historically Spring MVC supported trailing slash matching of URL paths.
|
||||
This capability was deprecated in 6.0 for security reasons and removed in 7.0 with
|
||||
`UrlHandlerFilter` providing a safer alternative.
|
||||
|
||||
Here is how you can instantiate and configure a `UrlHandlerFilter` for a blog application:
|
||||
|
||||
include-code::./UrlHandlerFilterConfiguration[tag=config,indent=0]
|
||||
|
||||
Keep in mind the following:
|
||||
|
||||
- the root path `"/"` is excluded from trailing slash handling.
|
||||
- `@RequestMapping("/")` adds a trailing slash to a type-level mapping, and therefore will
|
||||
not map when trailing slash handling applies; use `@RequestMapping` (no path attribute) instead.
|
||||
|
||||
@@ -45,7 +45,7 @@ By default, this converter supports `text/xml` and `application/xml`.
|
||||
| `JacksonJsonHttpMessageConverter`
|
||||
| An `HttpMessageConverter` implementation that can read and write JSON by using Jackson's `JsonMapper`.
|
||||
You can customize JSON mapping as needed through the use of Jackson's provided annotations.
|
||||
When you need further control (for cases where custom JSON serializers/deserializers need to be provided for specific types), you can inject a custom `JsonMapper` through the `JsonMapper` or `JsonMapper.Builder ` constructor parameters.
|
||||
When you need further control (for cases where custom JSON serializers/deserializers need to be provided for specific types), you can inject a custom `JsonMapper` through the `JsonMapper` or `JsonMapper.Builder` constructor parameters.
|
||||
By default, this converter supports `application/json`. This requires the `tools.jackson.core:jackson-databind` dependency.
|
||||
|
||||
| `JacksonXmlHttpMessageConverter`
|
||||
|
||||
@@ -32,9 +32,9 @@ any `@RequestMapping` method to render an RFC 9457 response. This is processed a
|
||||
- The `status` property of `ProblemDetail` determines the HTTP status.
|
||||
- The `instance` property of `ProblemDetail` is set from the current URL path, if not
|
||||
already set.
|
||||
- For content negotiation, the Jackson `HttpMessageConverter` prefers
|
||||
"application/problem+json" over "application/json" when rendering a `ProblemDetail`,
|
||||
and also falls back on it if no compatible media type is found.
|
||||
- The Jackson JSON and XML codecs use "application/problem+json" or
|
||||
"application/problem+xml" respectively as the producible media types for `ProblemDetail`
|
||||
to ensure they are favored for content negotiation.
|
||||
|
||||
To enable RFC 9457 responses for Spring MVC exceptions and for any
|
||||
`ErrorResponseException`, extend `ResponseEntityExceptionHandler` and declare it as an
|
||||
|
||||
@@ -14,55 +14,12 @@ In turn, the `DispatcherServlet` uses Spring configuration to discover
|
||||
the delegate components it needs for request mapping, view resolution, exception
|
||||
handling, xref:web/webmvc/mvc-servlet/special-bean-types.adoc[and more].
|
||||
|
||||
The following example of the Java configuration registers and initializes
|
||||
The following example shows the programmatic registration and initialization of
|
||||
the `DispatcherServlet`, which is auto-detected by the Servlet container
|
||||
(see xref:web/webmvc/mvc-servlet/container-config.adoc[Servlet Config]):
|
||||
(see xref:web/webmvc/mvc-servlet/container-config.adoc[Servlet Config]), and the
|
||||
equivalent `web.xml`:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
public class MyWebApplicationInitializer implements WebApplicationInitializer {
|
||||
|
||||
@Override
|
||||
public void onStartup(ServletContext servletContext) {
|
||||
|
||||
// Load Spring web application configuration
|
||||
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
|
||||
context.register(AppConfig.class);
|
||||
|
||||
// Create and register the DispatcherServlet
|
||||
DispatcherServlet servlet = new DispatcherServlet(context);
|
||||
ServletRegistration.Dynamic registration = servletContext.addServlet("app", servlet);
|
||||
registration.setLoadOnStartup(1);
|
||||
registration.addMapping("/app/*");
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
class MyWebApplicationInitializer : WebApplicationInitializer {
|
||||
|
||||
override fun onStartup(servletContext: ServletContext) {
|
||||
|
||||
// Load Spring web application configuration
|
||||
val context = AnnotationConfigWebApplicationContext()
|
||||
context.register(AppConfig::class.java)
|
||||
|
||||
// Create and register the DispatcherServlet
|
||||
val servlet = DispatcherServlet(context)
|
||||
val registration = servletContext.addServlet("app", servlet)
|
||||
registration.setLoadOnStartup(1)
|
||||
registration.addMapping("/app/*")
|
||||
}
|
||||
}
|
||||
----
|
||||
======
|
||||
include-code::./MyWebApplicationInitializer[tag=snippet,indent=0]
|
||||
|
||||
NOTE: In addition to using the ServletContext API directly, you can also extend
|
||||
`AbstractAnnotationConfigDispatcherServletInitializer` and override specific methods
|
||||
@@ -73,39 +30,6 @@ alternative to `AnnotationConfigWebApplicationContext`. See the
|
||||
{spring-framework-api}/web/context/support/GenericWebApplicationContext.html[`GenericWebApplicationContext`]
|
||||
javadoc for details.
|
||||
|
||||
The following example of `web.xml` configuration registers and initializes the `DispatcherServlet`:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
<web-app>
|
||||
|
||||
<listener>
|
||||
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
|
||||
</listener>
|
||||
|
||||
<context-param>
|
||||
<param-name>contextConfigLocation</param-name>
|
||||
<param-value>/WEB-INF/app-context.xml</param-value>
|
||||
</context-param>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>app</servlet-name>
|
||||
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
|
||||
<init-param>
|
||||
<param-name>contextConfigLocation</param-name>
|
||||
<param-value></param-value>
|
||||
</init-param>
|
||||
<load-on-startup>1</load-on-startup>
|
||||
</servlet>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>app</servlet-name>
|
||||
<url-pattern>/app/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
</web-app>
|
||||
----
|
||||
|
||||
NOTE: Spring Boot follows a different initialization sequence. Rather than hooking into
|
||||
the lifecycle of the Servlet container, Spring Boot uses Spring configuration to
|
||||
bootstrap itself and the embedded Servlet container. `Filter` and `Servlet` declarations
|
||||
|
||||
@@ -5,48 +5,7 @@ In a Servlet environment, you have the option of configuring the Servlet contain
|
||||
programmatically as an alternative or in combination with a `web.xml` file.
|
||||
The following example registers a `DispatcherServlet`:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
import org.springframework.web.WebApplicationInitializer;
|
||||
|
||||
public class MyWebApplicationInitializer implements WebApplicationInitializer {
|
||||
|
||||
@Override
|
||||
public void onStartup(ServletContext container) {
|
||||
XmlWebApplicationContext appContext = new XmlWebApplicationContext();
|
||||
appContext.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml");
|
||||
|
||||
ServletRegistration.Dynamic registration = container.addServlet("dispatcher", new DispatcherServlet(appContext));
|
||||
registration.setLoadOnStartup(1);
|
||||
registration.addMapping("/");
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
import org.springframework.web.WebApplicationInitializer
|
||||
|
||||
class MyWebApplicationInitializer : WebApplicationInitializer {
|
||||
|
||||
override fun onStartup(container: ServletContext) {
|
||||
val appContext = XmlWebApplicationContext()
|
||||
appContext.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml")
|
||||
|
||||
val registration = container.addServlet("dispatcher", DispatcherServlet(appContext))
|
||||
registration.setLoadOnStartup(1)
|
||||
registration.addMapping("/")
|
||||
}
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
include-code::./MyWebApplicationInitializer[tag=snippet,indent=0]
|
||||
|
||||
`WebApplicationInitializer` is an interface provided by Spring MVC that ensures your
|
||||
implementation is detected and automatically used to initialize any Servlet 3 container.
|
||||
@@ -55,144 +14,21 @@ An abstract base class implementation of `WebApplicationInitializer` named
|
||||
`DispatcherServlet` by overriding methods to specify the servlet mapping and the
|
||||
location of the `DispatcherServlet` configuration.
|
||||
|
||||
This is recommended for applications that use Java-based Spring configuration, as the
|
||||
This is recommended for applications that use programmatic Spring configuration, as the
|
||||
following example shows:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getRootConfigClasses() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getServletConfigClasses() {
|
||||
return new Class<?>[] { MyWebConfig.class };
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] getServletMappings() {
|
||||
return new String[] { "/" };
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
class MyWebAppInitializer : AbstractAnnotationConfigDispatcherServletInitializer() {
|
||||
|
||||
override fun getRootConfigClasses(): Array<Class<*>>? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun getServletConfigClasses(): Array<Class<*>>? {
|
||||
return arrayOf(MyWebConfig::class.java)
|
||||
}
|
||||
|
||||
override fun getServletMappings(): Array<String> {
|
||||
return arrayOf("/")
|
||||
}
|
||||
}
|
||||
----
|
||||
======
|
||||
include-code::./MyWebAppInitializer[tag=snippet,indent=0]
|
||||
|
||||
If you use XML-based Spring configuration, you should extend directly from
|
||||
`AbstractDispatcherServletInitializer`, as the following example shows:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
public class MyWebAppInitializer extends AbstractDispatcherServletInitializer {
|
||||
|
||||
@Override
|
||||
protected WebApplicationContext createRootApplicationContext() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WebApplicationContext createServletApplicationContext() {
|
||||
XmlWebApplicationContext cxt = new XmlWebApplicationContext();
|
||||
cxt.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml");
|
||||
return cxt;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] getServletMappings() {
|
||||
return new String[] { "/" };
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
class MyWebAppInitializer : AbstractDispatcherServletInitializer() {
|
||||
|
||||
override fun createRootApplicationContext(): WebApplicationContext? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun createServletApplicationContext(): WebApplicationContext {
|
||||
return XmlWebApplicationContext().apply {
|
||||
setConfigLocation("/WEB-INF/spring/dispatcher-config.xml")
|
||||
}
|
||||
}
|
||||
|
||||
override fun getServletMappings(): Array<String> {
|
||||
return arrayOf("/")
|
||||
}
|
||||
}
|
||||
----
|
||||
======
|
||||
include-code::./MyXmlDispatcherServletInitializer[tag=snippet,indent=0]
|
||||
|
||||
`AbstractDispatcherServletInitializer` also provides a convenient way to add `Filter`
|
||||
instances and have them be automatically mapped to the `DispatcherServlet`, as the
|
||||
following example shows:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
public class MyWebAppInitializer extends AbstractDispatcherServletInitializer {
|
||||
|
||||
// ...
|
||||
|
||||
@Override
|
||||
protected Filter[] getServletFilters() {
|
||||
return new Filter[] {
|
||||
new HiddenHttpMethodFilter(), new CharacterEncodingFilter() };
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
class MyWebAppInitializer : AbstractDispatcherServletInitializer() {
|
||||
|
||||
// ...
|
||||
|
||||
override fun getServletFilters(): Array<Filter> {
|
||||
return arrayOf(HiddenHttpMethodFilter(), CharacterEncodingFilter())
|
||||
}
|
||||
}
|
||||
----
|
||||
======
|
||||
include-code::./MyFilterDispatcherServletInitializer[tag=snippet,indent=0]
|
||||
|
||||
Each filter is added with a default name based on its concrete type and automatically
|
||||
mapped to the `DispatcherServlet`.
|
||||
|
||||
@@ -20,91 +20,15 @@ are effectively inherited and can be overridden (that is, re-declared) in the Se
|
||||
child `WebApplicationContext`, which typically contains beans local to the given `Servlet`.
|
||||
The following image shows this relationship:
|
||||
|
||||
image::mvc-context-hierarchy.png[]
|
||||
image::mvc-context-hierarchy.png[width=60%,align="center"]
|
||||
|
||||
The following example configures a `WebApplicationContext` hierarchy:
|
||||
The following example configures a `WebApplicationContext` hierarchy, and the equivalent `web.xml`:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getRootConfigClasses() {
|
||||
return new Class<?>[] { RootConfig.class };
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getServletConfigClasses() {
|
||||
return new Class<?>[] { App1Config.class };
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] getServletMappings() {
|
||||
return new String[] { "/app1/*" };
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
class MyWebAppInitializer : AbstractAnnotationConfigDispatcherServletInitializer() {
|
||||
|
||||
override fun getRootConfigClasses(): Array<Class<*>> {
|
||||
return arrayOf(RootConfig::class.java)
|
||||
}
|
||||
|
||||
override fun getServletConfigClasses(): Array<Class<*>> {
|
||||
return arrayOf(App1Config::class.java)
|
||||
}
|
||||
|
||||
override fun getServletMappings(): Array<String> {
|
||||
return arrayOf("/app1/*")
|
||||
}
|
||||
}
|
||||
----
|
||||
======
|
||||
include-code::./MyWebAppInitializer[tag=snippet,indent=0]
|
||||
|
||||
TIP: If an application context hierarchy is not required, applications can return all
|
||||
configuration through `getRootConfigClasses()` and `null` from `getServletConfigClasses()`.
|
||||
|
||||
The following example shows the `web.xml` equivalent:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
<web-app>
|
||||
|
||||
<listener>
|
||||
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
|
||||
</listener>
|
||||
|
||||
<context-param>
|
||||
<param-name>contextConfigLocation</param-name>
|
||||
<param-value>/WEB-INF/root-context.xml</param-value>
|
||||
</context-param>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>app1</servlet-name>
|
||||
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
|
||||
<init-param>
|
||||
<param-name>contextConfigLocation</param-name>
|
||||
<param-value>/WEB-INF/app1-context.xml</param-value>
|
||||
</init-param>
|
||||
<load-on-startup>1</load-on-startup>
|
||||
</servlet>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>app1</servlet-name>
|
||||
<url-pattern>/app1/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
</web-app>
|
||||
----
|
||||
|
||||
TIP: If an application context hierarchy is not required, applications may configure a
|
||||
"`root`" context only and leave the `contextConfigLocation` Servlet parameter empty.
|
||||
|
||||
@@ -74,42 +74,7 @@ Servlet container makes an ERROR dispatch within the container to the configured
|
||||
to a `@Controller`, which could be implemented to return an error view name with a model
|
||||
or to render a JSON response, as the following example shows:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@RestController
|
||||
public class ErrorController {
|
||||
|
||||
@RequestMapping(path = "/error")
|
||||
public Map<String, Object> handle(HttpServletRequest request) {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("status", request.getAttribute("jakarta.servlet.error.status_code"));
|
||||
map.put("reason", request.getAttribute("jakarta.servlet.error.message"));
|
||||
return map;
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@RestController
|
||||
class ErrorController {
|
||||
|
||||
@RequestMapping(path = ["/error"])
|
||||
fun handle(request: HttpServletRequest): Map<String, Any> {
|
||||
val map = HashMap<String, Any>()
|
||||
map["status"] = request.getAttribute("jakarta.servlet.error.status_code")
|
||||
map["reason"] = request.getAttribute("jakarta.servlet.error.message")
|
||||
return map
|
||||
}
|
||||
}
|
||||
----
|
||||
======
|
||||
include-code::./ErrorController[tag=snippet,indent=0]
|
||||
|
||||
TIP: The Servlet API does not provide a way to create error page mappings in Java. You can,
|
||||
however, use both a `WebApplicationInitializer` and a minimal `web.xml`.
|
||||
|
||||
@@ -25,62 +25,7 @@ through the `enableLoggingRequestDetails` property on `DispatcherServlet`.
|
||||
|
||||
The following example shows how to do so by using Java configuration:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
public class MyInitializer
|
||||
extends AbstractAnnotationConfigDispatcherServletInitializer {
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getRootConfigClasses() {
|
||||
return ... ;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getServletConfigClasses() {
|
||||
return ... ;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] getServletMappings() {
|
||||
return ... ;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void customizeRegistration(ServletRegistration.Dynamic registration) {
|
||||
registration.setInitParameter("enableLoggingRequestDetails", "true");
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
class MyInitializer : AbstractAnnotationConfigDispatcherServletInitializer() {
|
||||
|
||||
override fun getRootConfigClasses(): Array<Class<*>>? {
|
||||
return ...
|
||||
}
|
||||
|
||||
override fun getServletConfigClasses(): Array<Class<*>>? {
|
||||
return ...
|
||||
}
|
||||
|
||||
override fun getServletMappings(): Array<String> {
|
||||
return ...
|
||||
}
|
||||
|
||||
override fun customizeRegistration(registration: ServletRegistration.Dynamic) {
|
||||
registration.setInitParameter("enableLoggingRequestDetails", "true")
|
||||
}
|
||||
}
|
||||
----
|
||||
======
|
||||
include-code::./MyInitializer[tag=snippet,indent=0]
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -26,43 +26,7 @@ To do so:
|
||||
|
||||
The following example shows how to set a `MultipartConfigElement` on the Servlet registration:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
|
||||
|
||||
// ...
|
||||
|
||||
@Override
|
||||
protected void customizeRegistration(ServletRegistration.Dynamic registration) {
|
||||
|
||||
// Optionally also set maxFileSize, maxRequestSize, fileSizeThreshold
|
||||
registration.setMultipartConfig(new MultipartConfigElement("/tmp"));
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
class AppInitializer : AbstractAnnotationConfigDispatcherServletInitializer() {
|
||||
|
||||
// ...
|
||||
|
||||
override fun customizeRegistration(registration: ServletRegistration.Dynamic) {
|
||||
|
||||
// Optionally also set maxFileSize, maxRequestSize, fileSizeThreshold
|
||||
registration.setMultipartConfig(MultipartConfigElement("/tmp"))
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
======
|
||||
include-code::./AppInitializer[tag=snippet,indent=0]
|
||||
|
||||
Once the Servlet multipart configuration is in place, you can add a bean of type
|
||||
`StandardServletMultipartResolver` with a name of `multipartResolver`.
|
||||
|
||||
@@ -59,7 +59,7 @@ destination:/queue/trade
|
||||
content-type:application/json
|
||||
content-length:44
|
||||
|
||||
{"action":"BUY","ticker":"MMM","shares",44}^@
|
||||
{"action":"BUY","ticker":"MMM","shares":44}^@
|
||||
----
|
||||
|
||||
After the execution, the server can
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"@antora/collector-extension": "1.0.2",
|
||||
"@asciidoctor/tabs": "1.0.0-beta.6",
|
||||
"@springio/antora-extensions": "1.14.7",
|
||||
"fast-xml-parser": "4.5.2",
|
||||
"fast-xml-parser": "5.3.4",
|
||||
"@springio/asciidoctor-extensions": "1.0.0-alpha.17"
|
||||
}
|
||||
}
|
||||
|
||||
+12
-15
@@ -200,6 +200,7 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
|
||||
=======================================================================
|
||||
|
||||
SPRING FRAMEWORK ${version} SUBCOMPONENTS:
|
||||
@@ -212,7 +213,7 @@ code for these subcomponents is subject to the terms and
|
||||
conditions of the following licenses.
|
||||
|
||||
|
||||
>>> ASM 9.1 (org.ow2.asm:asm:9.1, org.ow2.asm:asm-commons:9.1):
|
||||
>>> ASM 9.9.1 (org.ow2.asm:asm:9.9.1):
|
||||
|
||||
Copyright (c) 2000-2011 INRIA, France Telecom
|
||||
All rights reserved.
|
||||
@@ -249,32 +250,28 @@ Copyright (c) 1999-2009, OW2 Consortium <https://www.ow2.org/>
|
||||
|
||||
>>> CGLIB 3.3 (cglib:cglib:3.3):
|
||||
|
||||
Per the LICENSE file in the CGLIB JAR distribution downloaded from
|
||||
https://github.com/cglib/cglib/releases/download/RELEASE_3_3_0/cglib-3.3.0.jar,
|
||||
CGLIB 3.3 is licensed under the Apache License, version 2.0, the text of which
|
||||
is included above.
|
||||
Per the LICENSE file in the CGLIB distribution, CGLIB 3.3 is licensed
|
||||
under the Apache License, version 2.0, the text of which is included above.
|
||||
|
||||
|
||||
>>> JavaPoet 1.13.0 (com.squareup:javapoet:1.13.0):
|
||||
>>> JavaPoet 0.10.0 (com.palantir.javapoet:javapoet:0.10.0):
|
||||
|
||||
Per the LICENSE file in the JavaPoet JAR distribution downloaded from
|
||||
https://github.com/square/javapoet/archive/refs/tags/javapoet-1.13.0.zip,
|
||||
JavaPoet 1.13.0 is licensed under the Apache License, version 2.0, the text of
|
||||
which is included above.
|
||||
Per the LICENSE file in the JavaPoet distribution, JavaPoet 0.10.0 is licensed
|
||||
under the Apache License, version 2.0, the text of which is included above.
|
||||
|
||||
|
||||
>>> Objenesis 3.4 (org.objenesis:objenesis:3.4):
|
||||
>>> Objenesis 3.5 (org.objenesis:objenesis:3.5):
|
||||
|
||||
Per the LICENSE file in the Objenesis ZIP distribution downloaded from
|
||||
http://objenesis.org/download.html, Objenesis 3.4 is licensed under the
|
||||
Per the LICENSE file in the Objenesis distribution downloaded from
|
||||
http://objenesis.org/download.html, Objenesis 3.5 is licensed under the
|
||||
Apache License, version 2.0, the text of which is included above.
|
||||
|
||||
Per the NOTICE file in the Objenesis ZIP distribution downloaded from
|
||||
Per the NOTICE file in the Objenesis distribution downloaded from
|
||||
http://objenesis.org/download.html and corresponding to section 4d of the
|
||||
Apache License, Version 2.0, in this case for Objenesis:
|
||||
|
||||
Objenesis
|
||||
Copyright 2006-2019 Joe Walnes, Henri Tremblay, Leonardo Mesquita
|
||||
Copyright 2006-2026 Joe Walnes, Henri Tremblay, Leonardo Mesquita
|
||||
|
||||
|
||||
===============================================================================
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webmvc.mvcservlet;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class AppConfig {
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webmvc.mvcservlet;
|
||||
|
||||
import jakarta.servlet.ServletContext;
|
||||
import jakarta.servlet.ServletRegistration;
|
||||
|
||||
import org.springframework.web.WebApplicationInitializer;
|
||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
||||
import org.springframework.web.servlet.DispatcherServlet;
|
||||
|
||||
// tag::snippet[]
|
||||
public class MyWebApplicationInitializer implements WebApplicationInitializer {
|
||||
|
||||
@Override
|
||||
public void onStartup(ServletContext servletContext) {
|
||||
|
||||
// Load Spring web application configuration
|
||||
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
|
||||
context.register(AppConfig.class);
|
||||
|
||||
// Create and register the DispatcherServlet
|
||||
DispatcherServlet servlet = new DispatcherServlet(context);
|
||||
ServletRegistration.Dynamic registration = servletContext.addServlet("app", servlet);
|
||||
registration.setLoadOnStartup(1);
|
||||
registration.addMapping("/app/*");
|
||||
}
|
||||
}
|
||||
// end::snippet[]
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webmvc.mvcservlet.mvcanncustomerservletcontainererrorpage;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
// tag::snippet[]
|
||||
@RestController
|
||||
public class ErrorController {
|
||||
|
||||
@RequestMapping(path = "/error")
|
||||
public Map<String, Object> handle(HttpServletRequest request) {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("status", request.getAttribute("jakarta.servlet.error.status_code"));
|
||||
map.put("reason", request.getAttribute("jakarta.servlet.error.message"));
|
||||
return map;
|
||||
}
|
||||
}
|
||||
// end::snippet[]
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webmvc.mvcservlet.mvccontainerconfig;
|
||||
|
||||
import jakarta.servlet.Filter;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.filter.CharacterEncodingFilter;
|
||||
import org.springframework.web.filter.HiddenHttpMethodFilter;
|
||||
import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;
|
||||
|
||||
// tag::snippet[]
|
||||
public class MyFilterDispatcherServletInitializer extends AbstractDispatcherServletInitializer {
|
||||
|
||||
@Override
|
||||
protected Filter[] getServletFilters() {
|
||||
return new Filter[] {
|
||||
new HiddenHttpMethodFilter(), new CharacterEncodingFilter() };
|
||||
}
|
||||
|
||||
// @fold:on
|
||||
@Override
|
||||
protected WebApplicationContext createServletApplicationContext() {
|
||||
/**/return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] getServletMappings() {
|
||||
/**/return new String[] { "/" };
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WebApplicationContext createRootApplicationContext() {
|
||||
/**/return null;
|
||||
}
|
||||
// @fold:off
|
||||
}
|
||||
// end::snippet[]
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webmvc.mvcservlet.mvccontainerconfig;
|
||||
|
||||
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
|
||||
|
||||
// tag::snippet[]
|
||||
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getRootConfigClasses() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getServletConfigClasses() {
|
||||
return new Class<?>[] { MyWebConfig.class };
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] getServletMappings() {
|
||||
return new String[] { "/" };
|
||||
}
|
||||
}
|
||||
// end::snippet[]
|
||||
|
||||
class MyWebConfig {}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webmvc.mvcservlet.mvccontainerconfig;
|
||||
|
||||
import jakarta.servlet.ServletContext;
|
||||
import jakarta.servlet.ServletRegistration;
|
||||
import org.springframework.web.WebApplicationInitializer;
|
||||
import org.springframework.web.context.support.XmlWebApplicationContext;
|
||||
import org.springframework.web.servlet.DispatcherServlet;
|
||||
|
||||
// tag::snippet[]
|
||||
public class MyWebApplicationInitializer implements WebApplicationInitializer {
|
||||
|
||||
@Override
|
||||
public void onStartup(ServletContext container) {
|
||||
XmlWebApplicationContext appContext = new XmlWebApplicationContext();
|
||||
appContext.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml");
|
||||
|
||||
ServletRegistration.Dynamic registration = container.addServlet("dispatcher", new DispatcherServlet(appContext));
|
||||
registration.setLoadOnStartup(1);
|
||||
registration.addMapping("/");
|
||||
}
|
||||
}
|
||||
// end::snippet[]
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webmvc.mvcservlet.mvccontainerconfig;
|
||||
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.context.support.XmlWebApplicationContext;
|
||||
import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;
|
||||
|
||||
// tag::snippet[]
|
||||
public class MyXmlDispatcherServletInitializer extends AbstractDispatcherServletInitializer {
|
||||
|
||||
@Override
|
||||
protected WebApplicationContext createRootApplicationContext() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WebApplicationContext createServletApplicationContext() {
|
||||
XmlWebApplicationContext cxt = new XmlWebApplicationContext();
|
||||
cxt.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml");
|
||||
return cxt;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] getServletMappings() {
|
||||
return new String[] { "/" };
|
||||
}
|
||||
}
|
||||
// end::snippet[]
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webmvc.mvcservlet.mvcloggingsensitivedata;
|
||||
|
||||
import jakarta.servlet.ServletRegistration;
|
||||
|
||||
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
|
||||
|
||||
// tag::snippet[]
|
||||
public class MyInitializer
|
||||
extends AbstractAnnotationConfigDispatcherServletInitializer {
|
||||
|
||||
// @fold:on
|
||||
@Override
|
||||
protected Class<?>[] getRootConfigClasses() {
|
||||
/**/throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getServletConfigClasses() {
|
||||
/**/throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] getServletMappings() {
|
||||
/**/throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
// @fold:off
|
||||
@Override
|
||||
protected void customizeRegistration(ServletRegistration.Dynamic registration) {
|
||||
registration.setInitParameter("enableLoggingRequestDetails", "true");
|
||||
}
|
||||
|
||||
}
|
||||
// end::snippet[]
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webmvc.mvcservlet.mvcmultipartresolverstandard;
|
||||
|
||||
import jakarta.servlet.MultipartConfigElement;
|
||||
import jakarta.servlet.ServletRegistration;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
|
||||
|
||||
// tag::snippet[]
|
||||
public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
|
||||
|
||||
// @fold:on
|
||||
@Override
|
||||
protected String[] getServletMappings() {
|
||||
/**/throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> @Nullable [] getRootConfigClasses() {
|
||||
/**/throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> @Nullable [] getServletConfigClasses() {
|
||||
/**/throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
// @fold:off
|
||||
@Override
|
||||
protected void customizeRegistration(ServletRegistration.Dynamic registration) {
|
||||
|
||||
// Optionally also set maxFileSize, maxRequestSize, fileSizeThreshold
|
||||
registration.setMultipartConfig(new MultipartConfigElement("/tmp"));
|
||||
}
|
||||
}
|
||||
// end::snippet[]
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webmvc.mvcservlet.mvcservletcontexthierarchy;
|
||||
|
||||
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
|
||||
|
||||
// tag::snippet[]
|
||||
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getRootConfigClasses() {
|
||||
return new Class<?>[] { RootConfig.class };
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getServletConfigClasses() {
|
||||
return new Class<?>[] { App1Config.class };
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] getServletMappings() {
|
||||
return new String[] { "/app1/*" };
|
||||
}
|
||||
}
|
||||
// end::snippet[]
|
||||
|
||||
class RootConfig {}
|
||||
class App1Config {}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webmvc.mvcservlet
|
||||
|
||||
import org.springframework.context.annotation.Configuration
|
||||
|
||||
@Configuration
|
||||
class AppConfig
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webmvc.mvcservlet
|
||||
|
||||
import jakarta.servlet.ServletContext
|
||||
import org.springframework.web.WebApplicationInitializer
|
||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext
|
||||
import org.springframework.web.servlet.DispatcherServlet
|
||||
|
||||
// tag::snippet[]
|
||||
class MyWebApplicationInitializer : WebApplicationInitializer {
|
||||
|
||||
override fun onStartup(servletContext: ServletContext) {
|
||||
|
||||
// Load Spring web application configuration
|
||||
val context = AnnotationConfigWebApplicationContext()
|
||||
context.register(AppConfig::class.java)
|
||||
|
||||
// Create and register the DispatcherServlet
|
||||
val servlet = DispatcherServlet(context)
|
||||
val registration = servletContext.addServlet("app", servlet)
|
||||
registration.setLoadOnStartup(1)
|
||||
registration.addMapping("/app/*")
|
||||
}
|
||||
}
|
||||
// end::snippet[]
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webmvc.mvcservlet.mvcanncustomerservletcontainererrorpage
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest
|
||||
import org.springframework.web.bind.annotation.RequestMapping
|
||||
import org.springframework.web.bind.annotation.RestController
|
||||
|
||||
// tag::snippet[]
|
||||
@RestController
|
||||
class ErrorController {
|
||||
|
||||
@RequestMapping(path = ["/error"])
|
||||
fun handle(request: HttpServletRequest): Map<String, Any> {
|
||||
val map = HashMap<String, Any>()
|
||||
map["status"] = request.getAttribute("jakarta.servlet.error.status_code")!!
|
||||
map["reason"] = request.getAttribute("jakarta.servlet.error.message")!!
|
||||
return map
|
||||
}
|
||||
}
|
||||
// end::snippet[]
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webmvc.mvcservlet.mvccontainerconfig
|
||||
|
||||
import jakarta.servlet.Filter
|
||||
import org.springframework.web.context.WebApplicationContext
|
||||
import org.springframework.web.filter.CharacterEncodingFilter
|
||||
import org.springframework.web.filter.HiddenHttpMethodFilter
|
||||
import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer
|
||||
|
||||
// tag::snippet[]
|
||||
class MyFilterDispatcherServletInitializer : AbstractDispatcherServletInitializer() {
|
||||
|
||||
override fun getServletFilters(): Array<Filter> {
|
||||
return arrayOf(HiddenHttpMethodFilter(), CharacterEncodingFilter())
|
||||
}
|
||||
|
||||
// @fold:on
|
||||
override fun createServletApplicationContext(): WebApplicationContext {
|
||||
/**/TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun getServletMappings(): Array<String> {
|
||||
/**/TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun createRootApplicationContext(): WebApplicationContext? {
|
||||
/**/TODO("Not yet implemented")
|
||||
}
|
||||
// @fold:off
|
||||
}
|
||||
// end::snippet[]
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webmvc.mvcservlet.mvccontainerconfig
|
||||
|
||||
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer
|
||||
|
||||
// tag::snippet[]
|
||||
class MyWebAppInitializer : AbstractAnnotationConfigDispatcherServletInitializer() {
|
||||
|
||||
override fun getRootConfigClasses(): Array<Class<*>>? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun getServletConfigClasses(): Array<Class<*>>? {
|
||||
return arrayOf(MyWebConfig::class.java)
|
||||
}
|
||||
|
||||
override fun getServletMappings(): Array<String> {
|
||||
return arrayOf("/")
|
||||
}
|
||||
}
|
||||
// end::snippet[]
|
||||
|
||||
class MyWebConfig
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webmvc.mvcservlet.mvccontainerconfig
|
||||
|
||||
import jakarta.servlet.ServletContext
|
||||
import org.springframework.web.WebApplicationInitializer
|
||||
import org.springframework.web.context.support.XmlWebApplicationContext
|
||||
import org.springframework.web.servlet.DispatcherServlet
|
||||
|
||||
// tag::snippet[]
|
||||
class MyWebApplicationInitializer : WebApplicationInitializer {
|
||||
|
||||
override fun onStartup(container: ServletContext) {
|
||||
val appContext = XmlWebApplicationContext()
|
||||
appContext.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml")
|
||||
|
||||
val registration = container.addServlet("dispatcher", DispatcherServlet(appContext))
|
||||
registration.setLoadOnStartup(1)
|
||||
registration.addMapping("/")
|
||||
}
|
||||
}
|
||||
// end::snippet[]
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webmvc.mvcservlet.mvccontainerconfig
|
||||
|
||||
import org.springframework.web.context.WebApplicationContext
|
||||
import org.springframework.web.context.support.XmlWebApplicationContext
|
||||
import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer
|
||||
|
||||
// tag::snippet[]
|
||||
class MyXmlDispatcherServletInitializer : AbstractDispatcherServletInitializer() {
|
||||
|
||||
override fun createRootApplicationContext(): WebApplicationContext? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun createServletApplicationContext(): WebApplicationContext {
|
||||
return XmlWebApplicationContext().apply {
|
||||
setConfigLocation("/WEB-INF/spring/dispatcher-config.xml")
|
||||
}
|
||||
}
|
||||
|
||||
override fun getServletMappings(): Array<String> {
|
||||
return arrayOf("/")
|
||||
}
|
||||
}
|
||||
// end::snippet[]
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webmvc.mvcservlet.mvcloggingsensitivedata
|
||||
|
||||
import jakarta.servlet.ServletRegistration
|
||||
|
||||
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer
|
||||
|
||||
// tag::snippet[]
|
||||
class MyInitializer : AbstractAnnotationConfigDispatcherServletInitializer() {
|
||||
|
||||
// @fold:on
|
||||
override fun getRootConfigClasses(): Array<Class<*>>? {
|
||||
/**/TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun getServletConfigClasses(): Array<Class<*>>? {
|
||||
/**/TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun getServletMappings(): Array<String> {
|
||||
/**/TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
// @fold:off
|
||||
override fun customizeRegistration(registration: ServletRegistration.Dynamic) {
|
||||
registration.setInitParameter("enableLoggingRequestDetails", "true")
|
||||
}
|
||||
}
|
||||
// end::snippet[]
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webmvc.mvcservlet.mvcmultipartresolverstandard
|
||||
|
||||
import jakarta.servlet.MultipartConfigElement
|
||||
import jakarta.servlet.ServletRegistration
|
||||
|
||||
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer
|
||||
|
||||
// tag::snippet[]
|
||||
class AppInitializer : AbstractAnnotationConfigDispatcherServletInitializer() {
|
||||
|
||||
// @fold:on
|
||||
override fun getServletMappings(): Array<out String> {
|
||||
/**/TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun getRootConfigClasses(): Array<out Class<*>>? {
|
||||
/**/TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun getServletConfigClasses(): Array<out Class<*>>? {
|
||||
/**/TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
// @fold:off
|
||||
override fun customizeRegistration(registration: ServletRegistration.Dynamic) {
|
||||
|
||||
// Optionally also set maxFileSize, maxRequestSize, fileSizeThreshold
|
||||
registration.setMultipartConfig(MultipartConfigElement("/tmp"))
|
||||
}
|
||||
|
||||
}
|
||||
// end::snippet[]
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webmvc.mvcservlet.mvcservletcontexthierarchy
|
||||
|
||||
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer
|
||||
|
||||
// tag::snippet[]
|
||||
class MyWebAppInitializer : AbstractAnnotationConfigDispatcherServletInitializer() {
|
||||
|
||||
override fun getRootConfigClasses(): Array<Class<*>> {
|
||||
return arrayOf(RootConfig::class.java)
|
||||
}
|
||||
|
||||
override fun getServletConfigClasses(): Array<Class<*>> {
|
||||
return arrayOf(App1Config::class.java)
|
||||
}
|
||||
|
||||
override fun getServletMappings(): Array<String> {
|
||||
return arrayOf("/app1/*")
|
||||
}
|
||||
}
|
||||
// end::snippet[]
|
||||
|
||||
class RootConfig
|
||||
class App1Config
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
<!-- tag::snippet[] -->
|
||||
<web-app>
|
||||
|
||||
<listener>
|
||||
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
|
||||
</listener>
|
||||
|
||||
<context-param>
|
||||
<param-name>contextConfigLocation</param-name>
|
||||
<param-value>/WEB-INF/app-context.xml</param-value>
|
||||
</context-param>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>app</servlet-name>
|
||||
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
|
||||
<init-param>
|
||||
<param-name>contextConfigLocation</param-name>
|
||||
<param-value></param-value>
|
||||
</init-param>
|
||||
<load-on-startup>1</load-on-startup>
|
||||
</servlet>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>app</servlet-name>
|
||||
<url-pattern>/app/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
</web-app>
|
||||
<!-- end::snippet[] -->
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
<!-- tag::snippet[] -->
|
||||
<web-app>
|
||||
|
||||
<listener>
|
||||
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
|
||||
</listener>
|
||||
|
||||
<context-param>
|
||||
<param-name>contextConfigLocation</param-name>
|
||||
<param-value>/WEB-INF/root-context.xml</param-value>
|
||||
</context-param>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>app1</servlet-name>
|
||||
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
|
||||
<init-param>
|
||||
<param-name>contextConfigLocation</param-name>
|
||||
<param-value>/WEB-INF/app1-context.xml</param-value>
|
||||
</init-param>
|
||||
<load-on-startup>1</load-on-startup>
|
||||
</servlet>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>app1</servlet-name>
|
||||
<url-pattern>/app1/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
</web-app>
|
||||
<!-- end::snippet[] -->
|
||||
@@ -7,21 +7,21 @@ javaPlatform {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(platform("com.fasterxml.jackson:jackson-bom:2.20.1"))
|
||||
api(platform("io.micrometer:micrometer-bom:1.16.2"))
|
||||
api(platform("io.netty:netty-bom:4.2.9.Final"))
|
||||
api(platform("io.projectreactor:reactor-bom:2025.0.2"))
|
||||
api(platform("com.fasterxml.jackson:jackson-bom:2.20.2"))
|
||||
api(platform("io.micrometer:micrometer-bom:1.16.3"))
|
||||
api(platform("io.netty:netty-bom:4.2.10.Final"))
|
||||
api(platform("io.projectreactor:reactor-bom:2025.0.3"))
|
||||
api(platform("io.rsocket:rsocket-bom:1.1.5"))
|
||||
api(platform("org.apache.groovy:groovy-bom:5.0.3"))
|
||||
api(platform("org.apache.groovy:groovy-bom:5.0.4"))
|
||||
api(platform("org.apache.logging.log4j:log4j-bom:2.25.3"))
|
||||
api(platform("org.assertj:assertj-bom:3.27.6"))
|
||||
api(platform("org.eclipse.jetty:jetty-bom:12.1.5"))
|
||||
api(platform("org.eclipse.jetty.ee11:jetty-ee11-bom:12.1.5"))
|
||||
api(platform("org.assertj:assertj-bom:3.27.7"))
|
||||
api(platform("org.eclipse.jetty:jetty-bom:12.1.6"))
|
||||
api(platform("org.eclipse.jetty.ee11:jetty-ee11-bom:12.1.6"))
|
||||
api(platform("org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.10.2"))
|
||||
api(platform("org.jetbrains.kotlinx:kotlinx-serialization-bom:1.9.0"))
|
||||
api(platform("org.junit:junit-bom:6.0.2"))
|
||||
api(platform("org.mockito:mockito-bom:5.21.0"))
|
||||
api(platform("tools.jackson:jackson-bom:3.0.3"))
|
||||
api(platform("tools.jackson:jackson-bom:3.0.4"))
|
||||
|
||||
constraints {
|
||||
api("com.fasterxml:aalto-xml:1.3.4")
|
||||
@@ -31,7 +31,7 @@ dependencies {
|
||||
api("com.google.code.findbugs:findbugs:3.0.1")
|
||||
api("com.google.code.findbugs:jsr305:3.0.2")
|
||||
api("com.google.code.gson:gson:2.13.2")
|
||||
api("com.google.protobuf:protobuf-java-util:4.33.2")
|
||||
api("com.google.protobuf:protobuf-java-util:4.33.4")
|
||||
api("com.h2database:h2:2.3.232")
|
||||
api("com.jayway.jsonpath:json-path:2.10.0")
|
||||
api("com.networknt:json-schema-validator:1.5.3")
|
||||
@@ -44,7 +44,7 @@ dependencies {
|
||||
api("com.sun.xml.bind:jaxb-xjc:3.0.2")
|
||||
api("com.thoughtworks.qdox:qdox:2.2.0")
|
||||
api("com.thoughtworks.xstream:xstream:1.4.21")
|
||||
api("commons-io:commons-io:2.15.0")
|
||||
api("commons-io:commons-io:2.21.0")
|
||||
api("commons-logging:commons-logging:1.3.5")
|
||||
api("de.bechte.junit:junit-hierarchicalcontextrunner:4.12.2")
|
||||
api("io.mockk:mockk:1.14.5")
|
||||
@@ -57,7 +57,6 @@ dependencies {
|
||||
api("io.vavr:vavr:0.11.0")
|
||||
api("jakarta.activation:jakarta.activation-api:2.1.3")
|
||||
api("jakarta.annotation:jakarta.annotation-api:3.0.0")
|
||||
api("jakarta.validation:jakarta.validation-api:3.1.0")
|
||||
api("jakarta.ejb:jakarta.ejb-api:4.0.1")
|
||||
api("jakarta.el:jakarta.el-api:6.0.1")
|
||||
api("jakarta.enterprise.concurrent:jakarta.enterprise.concurrent-api:3.1.1")
|
||||
@@ -96,11 +95,11 @@ dependencies {
|
||||
api("org.apache.derby:derbytools:10.16.1.1")
|
||||
api("org.apache.httpcomponents.client5:httpclient5:5.6")
|
||||
api("org.apache.httpcomponents.core5:httpcore5-reactive:5.4")
|
||||
api("org.apache.poi:poi-ooxml:5.2.5")
|
||||
api("org.apache.tomcat.embed:tomcat-embed-core:11.0.15")
|
||||
api("org.apache.tomcat.embed:tomcat-embed-websocket:11.0.15")
|
||||
api("org.apache.tomcat:tomcat-util:11.0.15")
|
||||
api("org.apache.tomcat:tomcat-websocket:11.0.15")
|
||||
api("org.apache.poi:poi-ooxml:5.5.1")
|
||||
api("org.apache.tomcat.embed:tomcat-embed-core:11.0.18")
|
||||
api("org.apache.tomcat.embed:tomcat-embed-websocket:11.0.18")
|
||||
api("org.apache.tomcat:tomcat-util:11.0.18")
|
||||
api("org.apache.tomcat:tomcat-websocket:11.0.18")
|
||||
api("org.aspectj:aspectjrt:1.9.25")
|
||||
api("org.aspectj:aspectjtools:1.9.25")
|
||||
api("org.aspectj:aspectjweaver:1.9.25")
|
||||
@@ -121,7 +120,7 @@ dependencies {
|
||||
api("org.glassfish:jakarta.el:4.0.2")
|
||||
api("org.graalvm.sdk:graal-sdk:22.3.1")
|
||||
api("org.hamcrest:hamcrest:3.0")
|
||||
api("org.hibernate.orm:hibernate-core:7.2.0.Final")
|
||||
api("org.hibernate.orm:hibernate-core:7.2.4.Final")
|
||||
api("org.hibernate.validator:hibernate-validator:9.1.0.Final")
|
||||
api("org.hsqldb:hsqldb:2.7.4")
|
||||
api("org.htmlunit:htmlunit:4.21.0")
|
||||
@@ -135,10 +134,10 @@ dependencies {
|
||||
api("org.python:jython-standalone:2.7.4")
|
||||
api("org.quartz-scheduler:quartz:2.3.2")
|
||||
api("org.reactivestreams:reactive-streams:1.0.4")
|
||||
api("org.seleniumhq.selenium:htmlunit3-driver:4.39.0")
|
||||
api("org.seleniumhq.selenium:selenium-java:4.39.0")
|
||||
api("org.seleniumhq.selenium:htmlunit3-driver:4.40.0")
|
||||
api("org.seleniumhq.selenium:selenium-java:4.40.0")
|
||||
api("org.skyscreamer:jsonassert:1.5.3")
|
||||
api("org.testng:testng:7.11.0")
|
||||
api("org.testng:testng:7.12.0")
|
||||
api("org.webjars:underscorejs:1.8.3")
|
||||
api("org.webjars:webjars-locator-lite:1.1.0")
|
||||
api("org.xmlunit:xmlunit-assertj:2.10.4")
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
version=7.0.3-SNAPSHOT
|
||||
version=7.0.4
|
||||
|
||||
org.gradle.caching=true
|
||||
org.gradle.jvmargs=-Xmx2048m
|
||||
|
||||
Vendored
BIN
Binary file not shown.
+1
-1
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
+11
-18
@@ -17,7 +17,6 @@
|
||||
package org.springframework.aop.framework.autoproxy
|
||||
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.aopalliance.intercept.MethodInterceptor
|
||||
import org.aopalliance.intercept.MethodInvocation
|
||||
import org.aspectj.lang.ProceedingJoinPoint
|
||||
@@ -73,43 +72,37 @@ class AspectJAutoProxyInterceptorKotlinIntegrationTests(
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Multiple interceptors with suspending function`() {
|
||||
suspend fun `Multiple interceptors with suspending function`() {
|
||||
assertThat(firstAdvisor.interceptor.invocations).isEmpty()
|
||||
assertThat(secondAdvisor.interceptor.invocations).isEmpty()
|
||||
val value = "Hello!"
|
||||
runBlocking {
|
||||
assertThat(echo.suspendingEcho(value)).isEqualTo(value)
|
||||
}
|
||||
assertThat(echo.suspendingEcho(value)).isEqualTo(value)
|
||||
assertThat(firstAdvisor.interceptor.invocations).singleElement().matches { Mono::class.java.isAssignableFrom(it) }
|
||||
assertThat(secondAdvisor.interceptor.invocations).singleElement().matches { Mono::class.java.isAssignableFrom(it) }
|
||||
}
|
||||
|
||||
@Test // gh-33095
|
||||
fun `Aspect and reactive transactional with suspending function`() {
|
||||
suspend fun `Aspect and reactive transactional with suspending function`() {
|
||||
assertThat(countingAspect.counter).isZero()
|
||||
assertThat(reactiveTransactionManager.commits).isZero()
|
||||
val value = "Hello!"
|
||||
runBlocking {
|
||||
assertThat(echo.suspendingTransactionalEcho(value)).isEqualTo(value)
|
||||
}
|
||||
assertThat(echo.suspendingTransactionalEcho(value)).isEqualTo(value)
|
||||
assertThat(countingAspect.counter).`as`("aspect applied").isOne()
|
||||
assertThat(reactiveTransactionManager.begun).isOne()
|
||||
assertThat(reactiveTransactionManager.commits).`as`("transactional applied").isOne()
|
||||
}
|
||||
|
||||
@Test // gh-33210
|
||||
fun `Aspect and cacheable with suspending function`() {
|
||||
suspend fun `Aspect and cacheable with suspending function`() {
|
||||
assertThat(countingAspect.counter).isZero()
|
||||
val value = "Hello!"
|
||||
runBlocking {
|
||||
assertThat(echo.suspendingCacheableEcho(value)).isEqualTo("$value 0")
|
||||
assertThat(echo.suspendingCacheableEcho(value)).isEqualTo("$value 0")
|
||||
assertThat(echo.suspendingCacheableEcho(value)).isEqualTo("$value 0")
|
||||
assertThat(countingAspect.counter).`as`("aspect applied once").isOne()
|
||||
assertThat(echo.suspendingCacheableEcho(value)).isEqualTo("$value 0")
|
||||
assertThat(echo.suspendingCacheableEcho(value)).isEqualTo("$value 0")
|
||||
assertThat(echo.suspendingCacheableEcho(value)).isEqualTo("$value 0")
|
||||
assertThat(countingAspect.counter).`as`("aspect applied once").isOne()
|
||||
|
||||
assertThat(echo.suspendingCacheableEcho("$value bis")).isEqualTo("$value bis 1")
|
||||
assertThat(echo.suspendingCacheableEcho("$value bis")).isEqualTo("$value bis 1")
|
||||
}
|
||||
assertThat(echo.suspendingCacheableEcho("$value bis")).isEqualTo("$value bis 1")
|
||||
assertThat(echo.suspendingCacheableEcho("$value bis")).isEqualTo("$value bis 1")
|
||||
assertThat(countingAspect.counter).`as`("aspect applied once per key").isEqualTo(2)
|
||||
}
|
||||
|
||||
|
||||
+5
-14
@@ -19,7 +19,6 @@ package org.springframework.aop.framework
|
||||
import kotlinx.coroutines.CoroutineName
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.toList
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Test
|
||||
import reactor.core.publisher.Flux
|
||||
@@ -37,39 +36,31 @@ class CoroutinesUtilsTests {
|
||||
fun awaitSingleNonNullValue() {
|
||||
val value = "foo"
|
||||
val continuation = Continuation<Any>(CoroutineName("test")) { }
|
||||
runBlocking {
|
||||
assertThat(CoroutinesUtils.awaitSingleOrNull(value, continuation)).isEqualTo(value)
|
||||
}
|
||||
assertThat(CoroutinesUtils.awaitSingleOrNull(value, continuation)).isEqualTo(value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun awaitSingleNullValue() {
|
||||
val value = null
|
||||
val continuation = Continuation<Any>(CoroutineName("test")) { }
|
||||
runBlocking {
|
||||
assertThat(CoroutinesUtils.awaitSingleOrNull(value, continuation)).isNull()
|
||||
}
|
||||
assertThat(CoroutinesUtils.awaitSingleOrNull(value, continuation)).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun awaitSingleMonoValue() {
|
||||
val value = "foo"
|
||||
val continuation = Continuation<Any>(CoroutineName("test")) { }
|
||||
runBlocking {
|
||||
assertThat(CoroutinesUtils.awaitSingleOrNull(Mono.just(value), continuation)).isEqualTo(value)
|
||||
}
|
||||
assertThat(CoroutinesUtils.awaitSingleOrNull(Mono.just(value), continuation)).isEqualTo(value)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun flow() {
|
||||
suspend fun flow() {
|
||||
val value1 = "foo"
|
||||
val value2 = "bar"
|
||||
val values = Flux.just(value1, value2)
|
||||
val flow = CoroutinesUtils.asFlow(values) as Flow<String>
|
||||
runBlocking {
|
||||
assertThat(flow.toList()).containsExactly(value1, value2)
|
||||
}
|
||||
assertThat(flow.toList()).containsExactly(value1, value2)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ class AopUtilsKotlinTests {
|
||||
@Test
|
||||
fun `Invoking suspending function on bridged method should return Mono`() {
|
||||
val value = "foo"
|
||||
val bridgedMethod = ReflectionUtils.findMethod(WithInterface::class.java, "handle", Object::class.java, Continuation::class.java)!!
|
||||
val bridgedMethod = ReflectionUtils.findMethod(WithInterface::class.java, "handle", Any::class.java, Continuation::class.java)!!
|
||||
val continuation = Continuation<Any>(CoroutineName("test")) { }
|
||||
val result = AopUtils.invokeJoinpointUsingReflection(WithInterface(), bridgedMethod, arrayOf(value, continuation))
|
||||
assertThat(result).isInstanceOfSatisfying(Mono::class.java) {
|
||||
|
||||
@@ -207,6 +207,13 @@ public interface BeanRegistry {
|
||||
*/
|
||||
Spec<T> prototype();
|
||||
|
||||
/**
|
||||
* Configure this bean with a custom scope.
|
||||
* @since 7.0.4
|
||||
* @see BeanDefinition#setScope(String)
|
||||
*/
|
||||
Spec<T> scope(String scope);
|
||||
|
||||
/**
|
||||
* Set the supplier to construct a bean instance.
|
||||
* @see AbstractBeanDefinition#setInstanceSupplier(Supplier)
|
||||
|
||||
+10
-9
@@ -44,9 +44,10 @@ import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* {@link AutowireCandidateResolver} implementation that matches bean definition qualifiers
|
||||
* against {@link Qualifier qualifier annotations} on the field or parameter to be autowired.
|
||||
* Also supports suggested expression values through a {@link Value value} annotation.
|
||||
* {@link AutowireCandidateResolver} implementation that matches bean definition
|
||||
* qualifiers against {@link #addQualifierType(Class) qualifier annotations} on
|
||||
* the field or parameter to be autowired. Also supports suggested expression
|
||||
* values through a {@link #setValueAnnotationType(Class) value annotation}.
|
||||
*
|
||||
* <p>Also supports JSR-330's {@link jakarta.inject.Qualifier} annotation if available.
|
||||
*
|
||||
@@ -68,7 +69,7 @@ public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwa
|
||||
|
||||
/**
|
||||
* Create a new {@code QualifierAnnotationAutowireCandidateResolver} for Spring's
|
||||
* standard {@link Qualifier} annotation.
|
||||
* standard {@link Qualifier @Qualifier} annotation.
|
||||
* <p>Also supports JSR-330's {@link jakarta.inject.Qualifier} annotation if available.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -107,11 +108,11 @@ public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwa
|
||||
/**
|
||||
* Register the given type to be used as a qualifier when autowiring.
|
||||
* <p>This identifies qualifier annotations for direct use (on fields,
|
||||
* method parameters and constructor parameters) as well as meta
|
||||
* annotations that in turn identify actual qualifier annotations.
|
||||
* method parameters and constructor parameters) as well as
|
||||
* meta-annotations that in turn identify actual qualifier annotations.
|
||||
* <p>This implementation only supports annotations as qualifier types.
|
||||
* The default is Spring's {@link Qualifier} annotation which serves
|
||||
* as a qualifier for direct use and also as a meta annotation.
|
||||
* The default is Spring's {@link Qualifier @Qualifier} annotation which serves
|
||||
* as a qualifier for direct use and also as a meta-annotation.
|
||||
* @param qualifierType the annotation type to register
|
||||
*/
|
||||
public void addQualifierType(Class<? extends Annotation> qualifierType) {
|
||||
@@ -122,7 +123,7 @@ public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwa
|
||||
* Set the 'value' annotation type, to be used on fields, method parameters
|
||||
* and constructor parameters.
|
||||
* <p>The default value annotation type is the Spring-provided
|
||||
* {@link Value} annotation.
|
||||
* {@link Value @Value} annotation.
|
||||
* <p>This setter property exists so that developers can provide their own
|
||||
* (non-Spring-specific) annotation type to indicate a default value
|
||||
* expression for a specific argument.
|
||||
|
||||
-1
@@ -113,7 +113,6 @@ class BeanDefinitionPropertiesCodeGenerator {
|
||||
.createValueCodeGenerator(generatedMethods, customDelegates);
|
||||
}
|
||||
|
||||
@SuppressWarnings("NullAway") // https://github.com/uber/NullAway/issues/1128
|
||||
CodeBlock generateCode(RootBeanDefinition beanDefinition) {
|
||||
CodeBlock.Builder code = CodeBlock.builder();
|
||||
addStatementForValue(code, beanDefinition, BeanDefinition::getScope,
|
||||
|
||||
+29
-5
@@ -304,13 +304,37 @@ public abstract class YamlProcessor {
|
||||
* @since 4.1.3
|
||||
*/
|
||||
protected final Map<String, Object> getFlattenedMap(Map<String, Object> source) {
|
||||
return getFlattenedMap(source, false, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a flattened version of the given map, recursively following any nested Map
|
||||
* or Collection values. Entries from the resulting map retain the same order as the
|
||||
* source. When called with the Map from a {@link MatchCallback} the result will
|
||||
* contain the same values as the {@link MatchCallback} Properties.
|
||||
* @param source the source map
|
||||
* @param includeEmpty whether empty entries should be included in the result
|
||||
* @param emptyValue the value used to represent an empty entry — for
|
||||
* example, {@code null} or an empty {@code String}
|
||||
* @return a flattened map
|
||||
* @since 7.0.4
|
||||
*/
|
||||
protected final Map<String, Object> getFlattenedMap(Map<String, Object> source, boolean includeEmpty,
|
||||
@Nullable Object emptyValue) {
|
||||
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
buildFlattenedMap(result, source, null);
|
||||
buildFlattenedMap(result, source, null, includeEmpty, emptyValue);
|
||||
return result;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
private void buildFlattenedMap(Map<String, Object> result, Map<String, Object> source, @Nullable String path) {
|
||||
private void buildFlattenedMap(Map<String, Object> result, Map<String, Object> source, @Nullable String path,
|
||||
boolean includeEmpty, @Nullable Object emptyValue) {
|
||||
|
||||
if (includeEmpty && source.isEmpty()) {
|
||||
result.put(path, emptyValue);
|
||||
return;
|
||||
}
|
||||
source.forEach((key, value) -> {
|
||||
if (StringUtils.hasText(path)) {
|
||||
if (key.startsWith("[")) {
|
||||
@@ -325,7 +349,7 @@ public abstract class YamlProcessor {
|
||||
}
|
||||
else if (value instanceof Map map) {
|
||||
// Need a compound key
|
||||
buildFlattenedMap(result, map, key);
|
||||
buildFlattenedMap(result, map, key, includeEmpty, emptyValue);
|
||||
}
|
||||
else if (value instanceof Collection collection) {
|
||||
// Need a compound key
|
||||
@@ -336,12 +360,12 @@ public abstract class YamlProcessor {
|
||||
int count = 0;
|
||||
for (Object object : collection) {
|
||||
buildFlattenedMap(result, Collections.singletonMap(
|
||||
"[" + (count++) + "]", object), key);
|
||||
"[" + (count++) + "]", object), key, includeEmpty, emptyValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
result.put(key, (value != null ? value : ""));
|
||||
result.put(key, (value != null ? value : (includeEmpty ? emptyValue : "")));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+6
@@ -279,6 +279,12 @@ public class BeanRegistryAdapter implements BeanRegistry {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Spec<T> scope(String scope) {
|
||||
this.beanDefinition.setScope(scope);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Spec<T> supplier(Function<SupplierContext, T> supplier) {
|
||||
this.beanDefinition.setInstanceSupplier(() ->
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ import org.springframework.util.ClassUtils;
|
||||
*
|
||||
* <p>This is the base class for
|
||||
* {@link org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver},
|
||||
* providing an implementation all non-annotation-based resolution steps at this level.
|
||||
* providing an implementation for all non-annotation-based resolution steps at this level.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 4.0
|
||||
|
||||
+44
-2
@@ -44,8 +44,7 @@ import static org.assertj.core.api.InstanceOfAssertFactories.set;
|
||||
*/
|
||||
class YamlProcessorTests {
|
||||
|
||||
private final YamlProcessor processor = new YamlProcessor() {
|
||||
};
|
||||
private final TestYamlProcessor processor = new TestYamlProcessor();
|
||||
|
||||
|
||||
@Test
|
||||
@@ -182,8 +181,51 @@ class YamlProcessorTests {
|
||||
.withMessageContaining("Global tag is not allowed: tag:yaml.org,2002:java.net.URL");
|
||||
}
|
||||
|
||||
@Test
|
||||
void processAndFlattenWithoutIncludedNulls() {
|
||||
setYaml("avalue: value\nanull: null\natilde: ~\nparent:\n anempty: {}\n");
|
||||
Map<String, Object> flattened = this.processor.processAndFlatten(false, null);
|
||||
assertThat(flattened).containsOnly(
|
||||
entry("avalue", "value"),
|
||||
entry("anull", ""),
|
||||
entry("atilde", ""));
|
||||
}
|
||||
|
||||
@Test
|
||||
void processAndFlattenWithIncludedNulls() {
|
||||
setYaml("avalue: value\nanull: null\natilde: ~\nparent:\n anempty: {}\n");
|
||||
Map<String, Object> flattened = this.processor.processAndFlatten(true, null);
|
||||
assertThat(flattened).containsOnly(
|
||||
entry("avalue", "value"),
|
||||
entry("anull", null),
|
||||
entry("atilde", null),
|
||||
entry("parent.anempty", null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void processAndFlattenWithIncludedBlankString() {
|
||||
setYaml("avalue: value\nanull: null\natilde: ~\nparent:\n anempty: {}\n");
|
||||
Map<String, Object> flattened = this.processor.processAndFlatten(true, "");
|
||||
assertThat(flattened).containsOnly(
|
||||
entry("avalue", "value"),
|
||||
entry("anull", ""),
|
||||
entry("atilde", ""),
|
||||
entry("parent.anempty", ""));
|
||||
}
|
||||
|
||||
private void setYaml(String yaml) {
|
||||
this.processor.setResources(new ByteArrayResource(yaml.getBytes()));
|
||||
}
|
||||
|
||||
private static class TestYamlProcessor extends YamlProcessor {
|
||||
|
||||
Map<String, Object> processAndFlatten(boolean includeEmpty, Object emptyValue) {
|
||||
Map<String, Object> flattened = new LinkedHashMap<>();
|
||||
process((properties, map) -> flattened.putAll(getFlattenedMap(map, includeEmpty, emptyValue)));
|
||||
return flattened;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+16
@@ -186,6 +186,14 @@ public class BeanRegistryAdapterTests {
|
||||
assertThat(beanDefinition.getScope()).isEqualTo(AbstractBeanDefinition.SCOPE_PROTOTYPE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void customScope() {
|
||||
BeanRegistryAdapter adapter = new BeanRegistryAdapter(this.beanFactory, this.beanFactory, this.env, ScopeBeanRegistrar.class);
|
||||
new ScopeBeanRegistrar().register(adapter, env);
|
||||
BeanDefinition beanDefinition = this.beanFactory.getBeanDefinition("foo");
|
||||
assertThat(beanDefinition.getScope()).isEqualTo("custom");
|
||||
}
|
||||
|
||||
@Test
|
||||
void defaultSupplier() {
|
||||
BeanRegistryAdapter adapter = new BeanRegistryAdapter(this.beanFactory, this.beanFactory, this.env, DefaultBeanRegistrar.class);
|
||||
@@ -308,6 +316,14 @@ public class BeanRegistryAdapterTests {
|
||||
}
|
||||
}
|
||||
|
||||
private static class ScopeBeanRegistrar implements BeanRegistrar {
|
||||
|
||||
@Override
|
||||
public void register(BeanRegistry registry, Environment env) {
|
||||
registry.registerBean("foo", Foo.class, spec -> spec.scope("custom"));
|
||||
}
|
||||
}
|
||||
|
||||
private static class SupplierBeanRegistrar implements BeanRegistrar {
|
||||
|
||||
@Override
|
||||
|
||||
-1
@@ -46,7 +46,6 @@ public abstract class AbstractJCacheConfiguration extends AbstractCachingConfigu
|
||||
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("NullAway") // See https://github.com/uber/NullAway/issues/1290
|
||||
protected void useCachingConfigurer(CachingConfigurerSupplier cachingConfigurerSupplier) {
|
||||
super.useCachingConfigurer(cachingConfigurerSupplier);
|
||||
this.exceptionCacheResolver = cachingConfigurerSupplier.adapt(config -> {
|
||||
|
||||
+29
-18
@@ -20,8 +20,11 @@ import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.junit.jupiter.api.AutoClose;
|
||||
import org.junit.jupiter.api.Named;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedClass;
|
||||
import org.junit.jupiter.params.provider.FieldSource;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@@ -34,21 +37,38 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Named.named;
|
||||
|
||||
/**
|
||||
* Tests for annotation-based caching methods that use reactive operators.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @since 6.1
|
||||
*/
|
||||
@ParameterizedClass
|
||||
@FieldSource("configClasses")
|
||||
class CaffeineReactiveCachingTests {
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(classes = {AsyncCacheModeConfig.class, AsyncCacheModeConfig.class})
|
||||
void cacheHitDetermination(Class<?> configClass) {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(configClass, ReactiveCacheableService.class);
|
||||
ReactiveCacheableService service = ctx.getBean(ReactiveCacheableService.class);
|
||||
static List<Named<Class<?>>> configClasses = List.of(
|
||||
named(AsyncCacheModeConfig.class.getSimpleName(), AsyncCacheModeConfig.class),
|
||||
named(AsyncCacheModeWithoutNullValuesConfig.class.getSimpleName(), AsyncCacheModeWithoutNullValuesConfig.class));
|
||||
|
||||
|
||||
@AutoClose
|
||||
private final AnnotationConfigApplicationContext ctx;
|
||||
|
||||
private final ReactiveCacheableService service;
|
||||
|
||||
|
||||
CaffeineReactiveCachingTests(Class<?> configClass) {
|
||||
this.ctx = new AnnotationConfigApplicationContext(configClass, ReactiveCacheableService.class);
|
||||
this.service = ctx.getBean(ReactiveCacheableService.class);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void cacheHitDetermination() {
|
||||
Object key = new Object();
|
||||
|
||||
Long r1 = service.cacheFuture(key).join();
|
||||
@@ -102,17 +122,10 @@ class CaffeineReactiveCachingTests {
|
||||
|
||||
assertThat(r1).isNotNull();
|
||||
assertThat(r1).isSameAs(r2).isSameAs(r3);
|
||||
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(classes = {AsyncCacheModeConfig.class, AsyncCacheModeConfig.class})
|
||||
void fluxCacheDoesntDependOnFirstRequest(Class<?> configClass) {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(configClass, ReactiveCacheableService.class);
|
||||
ReactiveCacheableService service = ctx.getBean(ReactiveCacheableService.class);
|
||||
|
||||
@Test
|
||||
void fluxCacheDoesntDependOnFirstRequest() {
|
||||
Object key = new Object();
|
||||
|
||||
List<Long> l1 = service.cacheFlux(key).take(1L, true).collectList().block();
|
||||
@@ -124,8 +137,6 @@ class CaffeineReactiveCachingTests {
|
||||
assertThat(l1).as("l1").containsExactly(first);
|
||||
assertThat(l2).as("l2").containsExactly(first, 0L, -1L);
|
||||
assertThat(l3).as("l3").containsExactly(first, 0L, -1L, -2L, -3L);
|
||||
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
|
||||
|
||||
Vendored
-1
@@ -94,7 +94,6 @@ public abstract class AbstractCachingConfiguration implements ImportAware {
|
||||
/**
|
||||
* Extract the configuration from the nominated {@link CachingConfigurer}.
|
||||
*/
|
||||
@SuppressWarnings("NullAway") // https://github.com/uber/NullAway/issues/1128
|
||||
protected void useCachingConfigurer(CachingConfigurerSupplier cachingConfigurerSupplier) {
|
||||
this.cacheManager = cachingConfigurerSupplier.adapt(CachingConfigurer::cacheManager);
|
||||
this.cacheResolver = cachingConfigurerSupplier.adapt(CachingConfigurer::cacheResolver);
|
||||
|
||||
+1
@@ -999,6 +999,7 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
.addStatement("throw new $T(\"Failed to read metadata for '$L'\", ex)",
|
||||
IllegalStateException.class, beanRegistrarEntry.getKey())
|
||||
.endControlFlow();
|
||||
generationContext.getRuntimeHints().resources().registerType(TypeReference.of(beanRegistrarEntry.getKey()));
|
||||
}
|
||||
code.addStatement("$L.register(new $T(($T)$L, $L, $L, $L.getClass(), $L), $L)", beanRegistrarName,
|
||||
BeanRegistryAdapter.class, BeanDefinitionRegistry.class, BeanFactoryInitializationCode.BEAN_FACTORY_VARIABLE,
|
||||
|
||||
+8
-2
@@ -44,6 +44,7 @@ import org.springframework.core.annotation.AnnotationUtils;
|
||||
* driven by the {@link Lazy} annotation in the {@code context.annotation} package.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @since 4.0
|
||||
*/
|
||||
public class ContextAnnotationAutowireCandidateResolver extends QualifierAnnotationAutowireCandidateResolver {
|
||||
@@ -60,7 +61,12 @@ public class ContextAnnotationAutowireCandidateResolver extends QualifierAnnotat
|
||||
|
||||
protected boolean isLazy(DependencyDescriptor descriptor) {
|
||||
for (Annotation ann : descriptor.getAnnotations()) {
|
||||
Lazy lazy = AnnotationUtils.getAnnotation(ann, Lazy.class);
|
||||
// Directly present?
|
||||
if (ann instanceof Lazy lazy && lazy.value()) {
|
||||
return true;
|
||||
}
|
||||
// Meta-present?
|
||||
Lazy lazy = AnnotationUtils.findAnnotation(ann.annotationType(), Lazy.class);
|
||||
if (lazy != null && lazy.value()) {
|
||||
return true;
|
||||
}
|
||||
@@ -69,7 +75,7 @@ public class ContextAnnotationAutowireCandidateResolver extends QualifierAnnotat
|
||||
if (methodParam != null) {
|
||||
Method method = methodParam.getMethod();
|
||||
if (method == null || void.class == method.getReturnType()) {
|
||||
Lazy lazy = AnnotationUtils.getAnnotation(methodParam.getAnnotatedElement(), Lazy.class);
|
||||
Lazy lazy = AnnotationUtils.findAnnotation(methodParam.getAnnotatedElement(), Lazy.class);
|
||||
if (lazy != null && lazy.value()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -54,6 +54,9 @@ import java.lang.annotation.Target;
|
||||
* for optional dependencies. For a programmatic equivalent, allowing for lazy references
|
||||
* with more sophistication, consider {@link org.springframework.beans.factory.ObjectProvider}.
|
||||
*
|
||||
* <p>This annotation may be used as a <em>meta-annotation</em> to create custom
|
||||
* <em>composed annotations</em>.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
|
||||
+28
-7
@@ -27,6 +27,7 @@ import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
@@ -1070,11 +1071,9 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
this.shutdownHook = new Thread(SHUTDOWN_HOOK_THREAD_NAME) {
|
||||
@Override
|
||||
public void run() {
|
||||
if (isStartupShutdownThreadStuck()) {
|
||||
active.set(false);
|
||||
if (!tryLockForShutdown()) {
|
||||
return;
|
||||
}
|
||||
startupShutdownLock.lock();
|
||||
try {
|
||||
doClose();
|
||||
}
|
||||
@@ -1087,6 +1086,30 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to acquire the common startup/shutdown lock, backing out if
|
||||
* the main startup/shutdown thread is stuck or on interruption.
|
||||
* @see #isStartupShutdownThreadStuck()
|
||||
*/
|
||||
private boolean tryLockForShutdown() {
|
||||
try {
|
||||
while (!this.startupShutdownLock.tryLock(100, TimeUnit.MILLISECONDS)) {
|
||||
if (!this.active.get() || this.closed.get()) {
|
||||
return false;
|
||||
}
|
||||
if (isStartupShutdownThreadStuck()) {
|
||||
this.active.set(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether an active startup/shutdown thread is currently stuck,
|
||||
* for example, through a {@code System.exit} call in a user component.
|
||||
@@ -1098,7 +1121,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
activeThread.interrupt();
|
||||
try {
|
||||
// Leave just a little bit of time for the interruption to show effect
|
||||
Thread.sleep(1);
|
||||
Thread.sleep(10);
|
||||
}
|
||||
catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
@@ -1120,12 +1143,10 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
*/
|
||||
@Override
|
||||
public void close() {
|
||||
if (isStartupShutdownThreadStuck()) {
|
||||
this.active.set(false);
|
||||
if (!tryLockForShutdown()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.startupShutdownLock.lock();
|
||||
try {
|
||||
this.startupShutdownThread = Thread.currentThread();
|
||||
|
||||
|
||||
+21
-10
@@ -101,14 +101,14 @@ public class ConcurrencyLimitBeanPostProcessor extends AbstractBeanFactoryAwareA
|
||||
interceptor = holder.methodInterceptors.get(method);
|
||||
if (interceptor == null) {
|
||||
boolean perMethod = false;
|
||||
ConcurrencyLimit annotation = AnnotatedElementUtils.getMergedAnnotation(method, ConcurrencyLimit.class);
|
||||
ConcurrencyLimit annotation = AnnotatedElementUtils.findMergedAnnotation(method, ConcurrencyLimit.class);
|
||||
if (annotation != null) {
|
||||
perMethod = true;
|
||||
}
|
||||
else {
|
||||
interceptor = holder.classInterceptor;
|
||||
if (interceptor == null) {
|
||||
annotation = AnnotatedElementUtils.getMergedAnnotation(targetClass, ConcurrencyLimit.class);
|
||||
annotation = AnnotatedElementUtils.findMergedAnnotation(targetClass, ConcurrencyLimit.class);
|
||||
}
|
||||
}
|
||||
if (interceptor == null) {
|
||||
@@ -117,11 +117,10 @@ public class ConcurrencyLimitBeanPostProcessor extends AbstractBeanFactoryAwareA
|
||||
if (concurrencyLimit < -1) {
|
||||
throw new IllegalStateException(annotation + " must be configured with a valid limit");
|
||||
}
|
||||
String name = (perMethod ? ClassUtils.getQualifiedMethodName(method) : targetClass.getName());
|
||||
interceptor = (annotation.policy() == ConcurrencyLimit.ThrottlePolicy.REJECT ?
|
||||
new RejectingConcurrencyThrottleInterceptor(concurrencyLimit,
|
||||
(perMethod ? ClassUtils.getQualifiedMethodName(method) : targetClass.getName()),
|
||||
instance) :
|
||||
new ConcurrencyThrottleInterceptor(concurrencyLimit));
|
||||
new RejectingConcurrencyThrottleInterceptor(concurrencyLimit, name, instance) :
|
||||
new ResilienceConcurrencyThrottleInterceptor(concurrencyLimit, name, instance));
|
||||
if (!perMethod) {
|
||||
holder.classInterceptor = interceptor;
|
||||
}
|
||||
@@ -155,22 +154,34 @@ public class ConcurrencyLimitBeanPostProcessor extends AbstractBeanFactoryAwareA
|
||||
}
|
||||
|
||||
|
||||
private static class RejectingConcurrencyThrottleInterceptor extends ConcurrencyThrottleInterceptor {
|
||||
private static class ResilienceConcurrencyThrottleInterceptor extends ConcurrencyThrottleInterceptor {
|
||||
|
||||
private final String identifier;
|
||||
|
||||
private final Object target;
|
||||
|
||||
public RejectingConcurrencyThrottleInterceptor(int concurrencyLimit, String identifier, Object target) {
|
||||
public ResilienceConcurrencyThrottleInterceptor(int concurrencyLimit, String identifier, Object target) {
|
||||
super(concurrencyLimit);
|
||||
this.identifier = identifier;
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAccessRejected(String msg) {
|
||||
throw new InvocationRejectedException(msg + " " + this.identifier, this.target);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class RejectingConcurrencyThrottleInterceptor extends ResilienceConcurrencyThrottleInterceptor {
|
||||
|
||||
public RejectingConcurrencyThrottleInterceptor(int concurrencyLimit, String identifier, Object target) {
|
||||
super(concurrencyLimit, identifier, target);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLimitReached() {
|
||||
throw new InvocationRejectedException(
|
||||
"Concurrency limit reached for " + this.identifier + ": " + getConcurrencyLimit(), this.target);
|
||||
onAccessRejected("Concurrency limit reached: " + getConcurrencyLimit() + " - not allowed to enter");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -93,9 +93,9 @@ public class RetryAnnotationBeanPostProcessor extends AbstractBeanFactoryAwareAd
|
||||
return retrySpec;
|
||||
}
|
||||
|
||||
Retryable retryable = AnnotatedElementUtils.getMergedAnnotation(method, Retryable.class);
|
||||
Retryable retryable = AnnotatedElementUtils.findMergedAnnotation(method, Retryable.class);
|
||||
if (retryable == null) {
|
||||
retryable = AnnotatedElementUtils.getMergedAnnotation(targetClass, Retryable.class);
|
||||
retryable = AnnotatedElementUtils.findMergedAnnotation(targetClass, Retryable.class);
|
||||
if (retryable == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
+1
-1
@@ -67,7 +67,7 @@ public abstract class AbstractAsyncConfiguration implements ImportAware {
|
||||
* Collect any {@link AsyncConfigurer} beans through autowiring.
|
||||
*/
|
||||
@Autowired
|
||||
@SuppressWarnings("NullAway") // https://github.com/uber/NullAway/issues/1128
|
||||
@SuppressWarnings("NullAway") // https://github.com/uber/NullAway/issues/1290
|
||||
void setConfigurers(ObjectProvider<AsyncConfigurer> configurers) {
|
||||
SingletonSupplier<AsyncConfigurer> configurer = SingletonSupplier.ofNullable(() -> {
|
||||
List<AsyncConfigurer> candidates = configurers.stream().toList();
|
||||
|
||||
@@ -41,6 +41,9 @@ import java.lang.annotation.Target;
|
||||
* for a specific bean to begin with. Can also be used as a meta-annotation on a
|
||||
* custom stereotype annotation or a custom group-specific validated annotation.
|
||||
*
|
||||
* <p>This annotation may be used as a <em>meta-annotation</em> to create custom
|
||||
* <em>composed annotations</em>.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
* @see jakarta.validation.Validator#validate(Object, Class[])
|
||||
|
||||
+35
-3
@@ -17,14 +17,18 @@
|
||||
package org.springframework.validation.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.aop.framework.AopProxyUtils;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
|
||||
/**
|
||||
* Utility class for handling validation annotations.
|
||||
* Mainly for internal use within the framework.
|
||||
*
|
||||
* <p>Mainly for internal use within the framework.
|
||||
*
|
||||
* @author Christoph Dreis
|
||||
* @author Juergen Hoeller
|
||||
@@ -34,9 +38,11 @@ public abstract class ValidationAnnotationUtils {
|
||||
|
||||
private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
|
||||
|
||||
private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];
|
||||
|
||||
|
||||
/**
|
||||
* Determine any validation hints by the given annotation.
|
||||
* Determine any validation hints for the given annotation.
|
||||
* <p>This implementation checks for Spring's
|
||||
* {@link org.springframework.validation.annotation.Validated},
|
||||
* {@code @jakarta.validation.Valid}, and custom annotations whose
|
||||
@@ -57,7 +63,7 @@ public abstract class ValidationAnnotationUtils {
|
||||
return EMPTY_OBJECT_ARRAY;
|
||||
}
|
||||
// Meta presence of @Validated ?
|
||||
Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
|
||||
Validated validatedAnn = AnnotationUtils.findAnnotation(annotationType, Validated.class);
|
||||
if (validatedAnn != null) {
|
||||
return validatedAnn.value();
|
||||
}
|
||||
@@ -76,4 +82,30 @@ public abstract class ValidationAnnotationUtils {
|
||||
return (hints instanceof Object[] objectHints ? objectHints : new Object[] {hints});
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the applicable validation groups from an
|
||||
* {@link org.springframework.validation.annotation.Validated @Validated}
|
||||
* annotation either on the method, or on the containing target class of
|
||||
* the method, or for an AOP proxy without a target (with all behavior in
|
||||
* advisors), also check on proxied interfaces.
|
||||
* @since 7.0.4
|
||||
*/
|
||||
public static Class<?>[] determineValidationGroups(Object target, Method method) {
|
||||
Validated validatedAnn = AnnotationUtils.findAnnotation(method, Validated.class);
|
||||
if (validatedAnn == null) {
|
||||
if (AopUtils.isAopProxy(target)) {
|
||||
for (Class<?> type : AopProxyUtils.proxiedUserInterfaces(target)) {
|
||||
validatedAnn = AnnotationUtils.findAnnotation(type, Validated.class);
|
||||
if (validatedAnn != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
validatedAnn = AnnotationUtils.findAnnotation(target.getClass(), Validated.class);
|
||||
}
|
||||
}
|
||||
return (validatedAnn != null ? validatedAnn.value() : EMPTY_CLASS_ARRAY);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+15
-17
@@ -261,7 +261,7 @@ public class LocalValidatorFactoryBean extends SpringValidatorAdapter
|
||||
configuration = bootstrap.configure();
|
||||
}
|
||||
|
||||
// Try Hibernate Validator 5.2's externalClassLoader(ClassLoader) method
|
||||
// Try Hibernate Validator's externalClassLoader(ClassLoader) method
|
||||
if (this.applicationContext != null) {
|
||||
try {
|
||||
Method eclMethod = configuration.getClass().getMethod("externalClassLoader", ClassLoader.class);
|
||||
@@ -269,7 +269,7 @@ public class LocalValidatorFactoryBean extends SpringValidatorAdapter
|
||||
ReflectionUtils.invokeMethod(eclMethod, configuration, this.applicationContext.getClassLoader());
|
||||
}
|
||||
catch (NoSuchMethodException ignored) {
|
||||
// no Hibernate Validator 5.2+ or similar provider
|
||||
// no Hibernate Validator or similar provider
|
||||
}
|
||||
}
|
||||
|
||||
@@ -370,47 +370,45 @@ public class LocalValidatorFactoryBean extends SpringValidatorAdapter
|
||||
protected void postProcessConfiguration(Configuration<?> configuration) {
|
||||
}
|
||||
|
||||
private ValidatorFactory obtainValidatorFactory() {
|
||||
Assert.state(this.validatorFactory != null, "No target ValidatorFactory set");
|
||||
return this.validatorFactory;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Validator getValidator() {
|
||||
Assert.state(this.validatorFactory != null, "No target ValidatorFactory set");
|
||||
return this.validatorFactory.getValidator();
|
||||
return obtainValidatorFactory().getValidator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValidatorContext usingContext() {
|
||||
Assert.state(this.validatorFactory != null, "No target ValidatorFactory set");
|
||||
return this.validatorFactory.usingContext();
|
||||
return obtainValidatorFactory().usingContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageInterpolator getMessageInterpolator() {
|
||||
Assert.state(this.validatorFactory != null, "No target ValidatorFactory set");
|
||||
return this.validatorFactory.getMessageInterpolator();
|
||||
return obtainValidatorFactory().getMessageInterpolator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraversableResolver getTraversableResolver() {
|
||||
Assert.state(this.validatorFactory != null, "No target ValidatorFactory set");
|
||||
return this.validatorFactory.getTraversableResolver();
|
||||
return obtainValidatorFactory().getTraversableResolver();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConstraintValidatorFactory getConstraintValidatorFactory() {
|
||||
Assert.state(this.validatorFactory != null, "No target ValidatorFactory set");
|
||||
return this.validatorFactory.getConstraintValidatorFactory();
|
||||
return obtainValidatorFactory().getConstraintValidatorFactory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParameterNameProvider getParameterNameProvider() {
|
||||
Assert.state(this.validatorFactory != null, "No target ValidatorFactory set");
|
||||
return this.validatorFactory.getParameterNameProvider();
|
||||
return obtainValidatorFactory().getParameterNameProvider();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClockProvider getClockProvider() {
|
||||
Assert.state(this.validatorFactory != null, "No target ValidatorFactory set");
|
||||
return this.validatorFactory.getClockProvider();
|
||||
return obtainValidatorFactory().getClockProvider();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -421,7 +419,7 @@ public class LocalValidatorFactoryBean extends SpringValidatorAdapter
|
||||
return super.unwrap(type);
|
||||
}
|
||||
catch (ValidationException ignored) {
|
||||
// we'll try ValidatorFactory unwrapping next
|
||||
// Trying ValidatorFactory unwrapping next
|
||||
}
|
||||
}
|
||||
if (this.validatorFactory != null) {
|
||||
|
||||
+3
-1
@@ -26,7 +26,7 @@ import org.springframework.context.support.MessageSourceResourceBundle;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Implementation of Hibernate Validator 4.3/5.x's {@link ResourceBundleLocator} interface,
|
||||
* Implementation of Hibernate Validator's {@link ResourceBundleLocator} interface,
|
||||
* exposing a Spring {@link MessageSource} as localized {@link MessageSourceResourceBundle}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
@@ -39,6 +39,7 @@ public class MessageSourceResourceBundleLocator implements ResourceBundleLocator
|
||||
|
||||
private final MessageSource messageSource;
|
||||
|
||||
|
||||
/**
|
||||
* Build a MessageSourceResourceBundleLocator for the given MessageSource.
|
||||
* @param messageSource the Spring MessageSource to wrap
|
||||
@@ -48,6 +49,7 @@ public class MessageSourceResourceBundleLocator implements ResourceBundleLocator
|
||||
this.messageSource = messageSource;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ResourceBundle getResourceBundle(Locale locale) {
|
||||
return new MessageSourceResourceBundle(this.messageSource, locale);
|
||||
|
||||
+6
-18
@@ -40,8 +40,6 @@ import jakarta.validation.executable.ExecutableValidator;
|
||||
import jakarta.validation.metadata.ConstraintDescriptor;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.aop.framework.AopProxyUtils;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.context.MessageSourceResolvable;
|
||||
import org.springframework.context.support.DefaultMessageSourceResolvable;
|
||||
import org.springframework.core.BridgeMethodResolver;
|
||||
@@ -50,7 +48,6 @@ import org.springframework.core.DefaultParameterNameDiscoverer;
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.function.SingletonSupplier;
|
||||
import org.springframework.validation.BeanPropertyBindingResult;
|
||||
@@ -59,6 +56,7 @@ import org.springframework.validation.DefaultMessageCodesResolver;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.validation.MessageCodesResolver;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.validation.annotation.ValidationAnnotationUtils;
|
||||
import org.springframework.validation.method.MethodValidationResult;
|
||||
import org.springframework.validation.method.MethodValidator;
|
||||
import org.springframework.validation.method.ParameterErrors;
|
||||
@@ -213,24 +211,14 @@ public class MethodValidationAdapter implements MethodValidator {
|
||||
* annotation on the method, or on the containing target class of the method,
|
||||
* or for an AOP proxy without a target (with all behavior in advisors), also
|
||||
* check on proxied interfaces.
|
||||
* @deprecated in favor of
|
||||
* {@link org.springframework.validation.annotation.ValidationAnnotationUtils#determineValidationGroups(Object, Method)}
|
||||
*/
|
||||
@SuppressWarnings("removal")
|
||||
@Deprecated(since = "7.0.4", forRemoval = true)
|
||||
@Override
|
||||
public Class<?>[] determineValidationGroups(Object target, Method method) {
|
||||
Validated validatedAnn = AnnotationUtils.findAnnotation(method, Validated.class);
|
||||
if (validatedAnn == null) {
|
||||
if (AopUtils.isAopProxy(target)) {
|
||||
for (Class<?> type : AopProxyUtils.proxiedUserInterfaces(target)) {
|
||||
validatedAnn = AnnotationUtils.findAnnotation(type, Validated.class);
|
||||
if (validatedAnn != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
validatedAnn = AnnotationUtils.findAnnotation(target.getClass(), Validated.class);
|
||||
}
|
||||
}
|
||||
return (validatedAnn != null ? validatedAnn.value() : new Class<?>[0]);
|
||||
return ValidationAnnotationUtils.determineValidationGroups(target, method);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+2
-1
@@ -46,6 +46,7 @@ import org.springframework.util.ClassUtils;
|
||||
import org.springframework.validation.BeanPropertyBindingResult;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.validation.annotation.ValidationAnnotationUtils;
|
||||
import org.springframework.validation.method.MethodValidationException;
|
||||
import org.springframework.validation.method.MethodValidationResult;
|
||||
import org.springframework.validation.method.ParameterErrors;
|
||||
@@ -224,7 +225,7 @@ public class MethodValidationInterceptor implements MethodInterceptor {
|
||||
*/
|
||||
protected Class<?>[] determineValidationGroups(MethodInvocation invocation) {
|
||||
Object target = getTarget(invocation);
|
||||
return this.validationAdapter.determineValidationGroups(target, invocation.getMethod());
|
||||
return ValidationAnnotationUtils.determineValidationGroups(target, invocation.getMethod());
|
||||
}
|
||||
|
||||
|
||||
|
||||
+7
-2
@@ -39,7 +39,10 @@ public interface MethodValidator {
|
||||
* @param target the target Object
|
||||
* @param method the target method
|
||||
* @return the applicable validation groups as a {@code Class} array
|
||||
* @deprecated in favor of
|
||||
* {@link org.springframework.validation.annotation.ValidationAnnotationUtils#determineValidationGroups(Object, Method)}
|
||||
*/
|
||||
@Deprecated(since = "7.0.4", forRemoval = true)
|
||||
Class<?>[] determineValidationGroups(Object target, Method method);
|
||||
|
||||
/**
|
||||
@@ -48,7 +51,8 @@ public interface MethodValidator {
|
||||
* @param method the target method
|
||||
* @param parameters the parameters, if already created and available
|
||||
* @param arguments the candidate argument values to validate
|
||||
* @param groups validation groups from {@link #determineValidationGroups}
|
||||
* @param groups validation groups from
|
||||
* {@link org.springframework.validation.annotation.ValidationAnnotationUtils#determineValidationGroups(Object, Method)}
|
||||
* @return the result of validation
|
||||
*/
|
||||
MethodValidationResult validateArguments(
|
||||
@@ -78,7 +82,8 @@ public interface MethodValidator {
|
||||
* @param method the target method
|
||||
* @param returnType the return parameter, if already created and available
|
||||
* @param returnValue the return value to validate
|
||||
* @param groups validation groups from {@link #determineValidationGroups}
|
||||
* @param groups validation groups from
|
||||
* {@link org.springframework.validation.annotation.ValidationAnnotationUtils#determineValidationGroups(Object, Method)}
|
||||
* @return the result of validation
|
||||
*/
|
||||
MethodValidationResult validateReturnValue(
|
||||
|
||||
+18
@@ -176,6 +176,24 @@ public class ConfigurationClassPostProcessorAotContributionTests {
|
||||
));
|
||||
}
|
||||
|
||||
@Test
|
||||
void applyToWhenHasImportAwareBeanRegistrarRegistersHints() {
|
||||
BeanFactoryInitializationAotContribution contribution = getContribution(BeanRegistrarTests.ImportAwareConfiguration.class);
|
||||
contribution.applyTo(generationContext, beanFactoryInitializationCode);
|
||||
assertThat(generationContext.getRuntimeHints().resources().resourcePatternHints())
|
||||
.singleElement()
|
||||
.satisfies(resourceHint -> assertThat(resourceHint.getIncludes())
|
||||
.map(ResourcePatternHint::getPattern)
|
||||
.containsExactlyInAnyOrder(
|
||||
"/",
|
||||
"org",
|
||||
"org/springframework",
|
||||
"org/springframework/context",
|
||||
"org/springframework/context/annotation",
|
||||
"org/springframework/context/annotation/ConfigurationClassPostProcessorAotContributionTests$BeanRegistrarTests$ImportAwareConfiguration.class"
|
||||
));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void compile(BiConsumer<Consumer<DefaultListableBeanFactory>, Compiled> result) {
|
||||
MethodReference methodReference = beanFactoryInitializationCode.getInitializers().get(0);
|
||||
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.context.annotation;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.BeforeTestExecutionCallback;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
import org.springframework.beans.factory.config.DependencyDescriptor;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ContextAnnotationAutowireCandidateResolver}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 7.0.4
|
||||
*/
|
||||
class ContextAnnotationAutowireCandidateResolverTests {
|
||||
|
||||
final ContextAnnotationAutowireCandidateResolver resolver = new ContextAnnotationAutowireCandidateResolver();
|
||||
|
||||
Method testMethod;
|
||||
|
||||
@RegisterExtension
|
||||
BeforeTestExecutionCallback extension = context -> this.testMethod = context.getRequiredTestMethod();
|
||||
|
||||
|
||||
@Test
|
||||
void isNotLazy() {
|
||||
assertNotLazy();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isLazy() {
|
||||
assertLazy();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isMetaLazy() {
|
||||
assertLazy();
|
||||
}
|
||||
|
||||
@Test // gh-36306
|
||||
void isMetaMetaLazy() {
|
||||
assertLazy();
|
||||
}
|
||||
|
||||
private void assertLazy() {
|
||||
assertThat(this.resolver.isLazy(getMethodDescriptor()))
|
||||
.as("%sMethod() is @Lazy", this.testMethod.getName()).isTrue();
|
||||
assertThat(this.resolver.isLazy(getParameterDescriptor()))
|
||||
.as("parameter in %sParameter() is @Lazy", this.testMethod.getName()).isTrue();
|
||||
}
|
||||
|
||||
private void assertNotLazy() {
|
||||
assertThat(this.resolver.isLazy(getMethodDescriptor()))
|
||||
.as("%sMethod() is not @Lazy", this.testMethod.getName()).isFalse();
|
||||
assertThat(this.resolver.isLazy(getParameterDescriptor()))
|
||||
.as("parameter in %sParameter() is not @Lazy", this.testMethod.getName()).isFalse();
|
||||
}
|
||||
|
||||
private DependencyDescriptor getMethodDescriptor() {
|
||||
var method = ReflectionUtils.findMethod(getClass(), this.testMethod.getName() + "Method");
|
||||
var methodParameter = MethodParameter.forExecutable(method, -1);
|
||||
return new DependencyDescriptor(methodParameter, true);
|
||||
}
|
||||
|
||||
private DependencyDescriptor getParameterDescriptor() {
|
||||
var method = ReflectionUtils.findMethod(getClass(), this.testMethod.getName() + "Parameter", String.class);
|
||||
var methodParameter = MethodParameter.forExecutable(method, 0);
|
||||
return new DependencyDescriptor(methodParameter, true);
|
||||
}
|
||||
|
||||
|
||||
void isNotLazyMethod() {
|
||||
}
|
||||
|
||||
@Lazy
|
||||
void isLazyMethod() {
|
||||
}
|
||||
|
||||
@MetaLazy
|
||||
void isMetaLazyMethod() {
|
||||
}
|
||||
|
||||
@MetaMetaLazy
|
||||
void isMetaMetaLazyMethod() {
|
||||
}
|
||||
|
||||
void isNotLazyParameter(String enigma) {
|
||||
}
|
||||
|
||||
void isLazyParameter(@Lazy String enigma) {
|
||||
}
|
||||
|
||||
void isMetaLazyParameter(@MetaLazy String enigma) {
|
||||
}
|
||||
|
||||
void isMetaMetaLazyParameter(@MetaMetaLazy String enigma) {
|
||||
}
|
||||
|
||||
|
||||
@Lazy
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface MetaLazy {
|
||||
}
|
||||
|
||||
@MetaLazy
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface MetaMetaLazy {
|
||||
}
|
||||
|
||||
}
|
||||
+65
-1
@@ -23,9 +23,11 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aop.config.AopConfigUtils;
|
||||
import org.springframework.aop.framework.AopProxyUtils;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.aop.interceptor.ConcurrencyThrottleInterceptor;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
@@ -77,6 +79,32 @@ class ConcurrencyLimitTests {
|
||||
assertThat(target.current).hasValue(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void withPostProcessorForMethodWithInterface() {
|
||||
AnnotatedInterface proxy = createProxy(AnnotatedMethodBeanWithInterface.class, AnnotatedInterface.class, false);
|
||||
AnnotatedMethodBeanWithInterface target = (AnnotatedMethodBeanWithInterface) AopProxyUtils.getSingletonTarget(proxy);
|
||||
|
||||
List<CompletableFuture<?>> futures = new ArrayList<>(10);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
futures.add(CompletableFuture.runAsync(proxy::concurrentOperation));
|
||||
}
|
||||
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
|
||||
assertThat(target.current).hasValue(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void withPostProcessorForMethodWithInterfaceAndDefaultTargetClass() {
|
||||
AnnotatedInterface proxy = createProxy(AnnotatedMethodBeanWithInterface.class, AnnotatedInterface.class, true);
|
||||
AnnotatedMethodBeanWithInterface target = (AnnotatedMethodBeanWithInterface) AopProxyUtils.getSingletonTarget(proxy);
|
||||
|
||||
List<CompletableFuture<?>> futures = new ArrayList<>(10);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
futures.add(CompletableFuture.runAsync(proxy::concurrentOperation));
|
||||
}
|
||||
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
|
||||
assertThat(target.current).hasValue(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void withPostProcessorForMethodWithUnboundedConcurrency() {
|
||||
AnnotatedMethodBean proxy = createProxy(AnnotatedMethodBean.class);
|
||||
@@ -201,12 +229,21 @@ class ConcurrencyLimitTests {
|
||||
|
||||
|
||||
private static <T> T createProxy(Class<T> beanClass) {
|
||||
return createProxy(beanClass, beanClass, true);
|
||||
}
|
||||
|
||||
private static <T> T createProxy(Class<?> beanClass, Class<T> exposedClass, boolean proxyTargetClass) {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
if (exposedClass.isInterface() && proxyTargetClass) {
|
||||
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(bf);
|
||||
}
|
||||
bf.registerBeanDefinition("bean", new RootBeanDefinition(beanClass));
|
||||
ConcurrencyLimitBeanPostProcessor bpp = new ConcurrencyLimitBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
return bf.getBean(beanClass);
|
||||
T proxy = bf.getBean(exposedClass);
|
||||
assertThat(proxyTargetClass ? AopUtils.isCglibProxy(proxy) : AopUtils.isJdkDynamicProxy(proxy)).isTrue();
|
||||
return proxy;
|
||||
}
|
||||
|
||||
|
||||
@@ -274,6 +311,33 @@ class ConcurrencyLimitTests {
|
||||
}
|
||||
|
||||
|
||||
static class AnnotatedMethodBeanWithInterface implements AnnotatedInterface {
|
||||
|
||||
final AtomicInteger current = new AtomicInteger();
|
||||
|
||||
@Override
|
||||
public void concurrentOperation() {
|
||||
if (current.incrementAndGet() > 2) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
}
|
||||
catch (InterruptedException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
current.decrementAndGet();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
interface AnnotatedInterface {
|
||||
|
||||
@ConcurrencyLimit(2)
|
||||
void concurrentOperation();
|
||||
}
|
||||
|
||||
|
||||
@ConcurrencyLimit(2)
|
||||
static class AnnotatedClassBean {
|
||||
|
||||
|
||||
@@ -523,7 +523,6 @@ class RetryInterceptorTests {
|
||||
|
||||
int counter = 0;
|
||||
|
||||
@Retryable(maxRetries = 5, delay = 10)
|
||||
@Override
|
||||
public void retryOperation() throws IOException {
|
||||
counter++;
|
||||
|
||||
+3
-2
@@ -31,6 +31,7 @@ import org.springframework.scheduling.TriggerContext;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.junit.jupiter.api.Named.named;
|
||||
import static org.junit.jupiter.params.provider.Arguments.arguments;
|
||||
|
||||
/**
|
||||
@@ -47,8 +48,8 @@ import static org.junit.jupiter.params.provider.Arguments.arguments;
|
||||
class CronTriggerTests {
|
||||
|
||||
static List<Arguments> parameters = List.of(
|
||||
arguments(new Date(), TimeZone.getTimeZone("PST")),
|
||||
arguments(new Date(), TimeZone.getTimeZone("CET")));
|
||||
arguments(new Date(), named("PST", TimeZone.getTimeZone("PST"))),
|
||||
arguments(new Date(), named("CET", TimeZone.getTimeZone("CET"))));
|
||||
|
||||
|
||||
private final Calendar calendar = new GregorianCalendar();
|
||||
|
||||
+196
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.validation.annotation;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.groups.Default;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestInfo;
|
||||
import org.junit.jupiter.api.extension.BeforeTestExecutionCallback;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ValidationAnnotationUtils}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 7.0.4
|
||||
*/
|
||||
class ValidationAnnotationUtilsTests {
|
||||
|
||||
@Nested
|
||||
class DetermineValidationHintsTests {
|
||||
|
||||
Method method;
|
||||
|
||||
@RegisterExtension
|
||||
BeforeTestExecutionCallback extension = context -> this.method = context.getRequiredTestMethod();
|
||||
|
||||
@Test
|
||||
void nonValidatedMethod(TestInfo testInfo) {
|
||||
var annotation = this.method.getAnnotation(Test.class);
|
||||
var hints = ValidationAnnotationUtils.determineValidationHints(annotation);
|
||||
|
||||
assertThat(hints).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Validated({ GroupA.class, Default.class })
|
||||
void springValidated(TestInfo testInfo) {
|
||||
var annotation = this.method.getAnnotation(Validated.class);
|
||||
var hints = ValidationAnnotationUtils.determineValidationHints(annotation);
|
||||
|
||||
assertThat(hints).containsExactly(GroupA.class, Default.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@MetaValidated
|
||||
void springMetaValidated(TestInfo testInfo) {
|
||||
var annotation = this.method.getAnnotation(MetaValidated.class);
|
||||
var hints = ValidationAnnotationUtils.determineValidationHints(annotation);
|
||||
|
||||
assertThat(hints).containsExactly(GroupB.class, Default.class);
|
||||
}
|
||||
|
||||
@Test // gh-36274
|
||||
@MetaMetaValidated
|
||||
void springMetaMetaValidated(TestInfo testInfo) {
|
||||
var annotation = this.method.getAnnotation(MetaMetaValidated.class);
|
||||
var hints = ValidationAnnotationUtils.determineValidationHints(annotation);
|
||||
|
||||
assertThat(hints).containsExactly(GroupB.class, Default.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Valid
|
||||
void jakartaValid(TestInfo testInfo) {
|
||||
var annotation = this.method.getAnnotation(Valid.class);
|
||||
var hints = ValidationAnnotationUtils.determineValidationHints(annotation);
|
||||
|
||||
assertThat(hints).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@ValidPlain
|
||||
void plainCustomValidAnnotation(TestInfo testInfo) {
|
||||
var annotation = this.method.getAnnotation(ValidPlain.class);
|
||||
var hints = ValidationAnnotationUtils.determineValidationHints(annotation);
|
||||
|
||||
assertThat(hints).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@ValidParameterized
|
||||
void parameterizedCustomValidAnnotationWithEmptyGroups(TestInfo testInfo) {
|
||||
var annotation = this.method.getAnnotation(ValidParameterized.class);
|
||||
var hints = ValidationAnnotationUtils.determineValidationHints(annotation);
|
||||
|
||||
assertThat(hints).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@ValidParameterized({ GroupA.class, GroupB.class })
|
||||
void parameterizedCustomValidAnnotationWithNonEmptyGroups(TestInfo testInfo) {
|
||||
var annotation = this.method.getAnnotation(ValidParameterized.class);
|
||||
var hints = ValidationAnnotationUtils.determineValidationHints(annotation);
|
||||
|
||||
assertThat(hints).containsExactly(GroupA.class, GroupB.class);
|
||||
}
|
||||
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface ValidPlain {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface ValidParameterized {
|
||||
Class<?>[] value() default {};
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class DetermineValidationGroupsTests {
|
||||
|
||||
Method method;
|
||||
|
||||
@RegisterExtension
|
||||
BeforeTestExecutionCallback extension = context -> this.method = context.getRequiredTestMethod();
|
||||
|
||||
@Test
|
||||
void nonValidatedMethod(TestInfo testInfo) {
|
||||
var hints = ValidationAnnotationUtils.determineValidationGroups(this, this.method);
|
||||
|
||||
assertThat(hints).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Validated({ GroupA.class, Default.class })
|
||||
void springValidated(TestInfo testInfo) {
|
||||
var hints = ValidationAnnotationUtils.determineValidationGroups(this, this.method);
|
||||
|
||||
assertThat(hints).containsExactly(GroupA.class, Default.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@MetaValidated
|
||||
void springMetaValidated(TestInfo testInfo) {
|
||||
var hints = ValidationAnnotationUtils.determineValidationGroups(this, this.method);
|
||||
|
||||
assertThat(hints).containsExactly(GroupB.class, Default.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@MetaMetaValidated
|
||||
void springMetaMetaValidated(TestInfo testInfo) {
|
||||
var hints = ValidationAnnotationUtils.determineValidationGroups(this, this.method);
|
||||
|
||||
assertThat(hints).containsExactly(GroupB.class, Default.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Valid
|
||||
void jakartaValid(TestInfo testInfo) {
|
||||
var hints = ValidationAnnotationUtils.determineValidationGroups(this, this.method);
|
||||
|
||||
assertThat(hints).isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
interface GroupA {
|
||||
}
|
||||
|
||||
interface GroupB {
|
||||
}
|
||||
|
||||
@Validated({ GroupB.class, Default.class })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface MetaValidated {
|
||||
}
|
||||
|
||||
@MetaValidated
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface MetaMetaValidated {
|
||||
}
|
||||
|
||||
}
|
||||
+38
-43
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.springframework.cache
|
||||
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.springframework.beans.testfixture.beans.TestBean
|
||||
@@ -33,51 +32,47 @@ import org.springframework.context.annotation.Configuration
|
||||
class KotlinCacheReproTests {
|
||||
|
||||
@Test
|
||||
fun spr14235AdaptsToSuspendingFunction() {
|
||||
runBlocking {
|
||||
val context = AnnotationConfigApplicationContext(
|
||||
Spr14235Config::class.java,
|
||||
Spr14235SuspendingService::class.java
|
||||
)
|
||||
val bean = context.getBean(Spr14235SuspendingService::class.java)
|
||||
val cache = context.getBean(CacheManager::class.java).getCache("itemCache")!!
|
||||
val tb: TestBean = bean.findById("tb1")
|
||||
assertThat(bean.findById("tb1")).isSameAs(tb)
|
||||
assertThat(cache["tb1"]!!.get()).isSameAs(tb)
|
||||
bean.clear()
|
||||
val tb2: TestBean = bean.findById("tb1")
|
||||
assertThat(tb2).isNotSameAs(tb)
|
||||
assertThat(cache["tb1"]!!.get()).isSameAs(tb2)
|
||||
bean.clear()
|
||||
bean.insertItem(tb)
|
||||
assertThat(bean.findById("tb1")).isSameAs(tb)
|
||||
assertThat(cache["tb1"]!!.get()).isSameAs(tb)
|
||||
context.close()
|
||||
}
|
||||
suspend fun spr14235AdaptsToSuspendingFunction() {
|
||||
val context = AnnotationConfigApplicationContext(
|
||||
Spr14235Config::class.java,
|
||||
Spr14235SuspendingService::class.java
|
||||
)
|
||||
val bean = context.getBean(Spr14235SuspendingService::class.java)
|
||||
val cache = context.getBean(CacheManager::class.java).getCache("itemCache")!!
|
||||
val tb: TestBean = bean.findById("tb1")
|
||||
assertThat(bean.findById("tb1")).isSameAs(tb)
|
||||
assertThat(cache["tb1"]!!.get()).isSameAs(tb)
|
||||
bean.clear()
|
||||
val tb2: TestBean = bean.findById("tb1")
|
||||
assertThat(tb2).isNotSameAs(tb)
|
||||
assertThat(cache["tb1"]!!.get()).isSameAs(tb2)
|
||||
bean.clear()
|
||||
bean.insertItem(tb)
|
||||
assertThat(bean.findById("tb1")).isSameAs(tb)
|
||||
assertThat(cache["tb1"]!!.get()).isSameAs(tb)
|
||||
context.close()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun spr14235AdaptsToSuspendingFunctionWithSync() {
|
||||
runBlocking {
|
||||
val context = AnnotationConfigApplicationContext(
|
||||
Spr14235Config::class.java,
|
||||
Spr14235SuspendingServiceSync::class.java
|
||||
)
|
||||
val bean = context.getBean(Spr14235SuspendingServiceSync::class.java)
|
||||
val cache = context.getBean(CacheManager::class.java).getCache("itemCache")!!
|
||||
val tb = bean.findById("tb1")
|
||||
assertThat(bean.findById("tb1")).isSameAs(tb)
|
||||
assertThat(cache["tb1"]!!.get()).isSameAs(tb)
|
||||
cache.clear()
|
||||
val tb2 = bean.findById("tb1")
|
||||
assertThat(tb2).isNotSameAs(tb)
|
||||
assertThat(cache["tb1"]!!.get()).isSameAs(tb2)
|
||||
cache.clear()
|
||||
bean.insertItem(tb)
|
||||
assertThat(bean.findById("tb1")).isSameAs(tb)
|
||||
assertThat(cache["tb1"]!!.get()).isSameAs(tb)
|
||||
context.close()
|
||||
}
|
||||
suspend fun spr14235AdaptsToSuspendingFunctionWithSync() {
|
||||
val context = AnnotationConfigApplicationContext(
|
||||
Spr14235Config::class.java,
|
||||
Spr14235SuspendingServiceSync::class.java
|
||||
)
|
||||
val bean = context.getBean(Spr14235SuspendingServiceSync::class.java)
|
||||
val cache = context.getBean(CacheManager::class.java).getCache("itemCache")!!
|
||||
val tb = bean.findById("tb1")
|
||||
assertThat(bean.findById("tb1")).isSameAs(tb)
|
||||
assertThat(cache["tb1"]!!.get()).isSameAs(tb)
|
||||
cache.clear()
|
||||
val tb2 = bean.findById("tb1")
|
||||
assertThat(tb2).isNotSameAs(tb)
|
||||
assertThat(cache["tb1"]!!.get()).isSameAs(tb2)
|
||||
cache.clear()
|
||||
bean.insertItem(tb)
|
||||
assertThat(bean.findById("tb1")).isSameAs(tb)
|
||||
assertThat(cache["tb1"]!!.get()).isSameAs(tb)
|
||||
context.close()
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+1
-1
@@ -49,7 +49,7 @@ class MethodValidationKotlinTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun coroutinesParameterValidation() = runBlocking<Unit> {
|
||||
suspend fun coroutinesParameterValidation() {
|
||||
val bean = MyValidCoroutinesBean()
|
||||
val proxyFactory = ProxyFactory(bean)
|
||||
val validator = LocalValidatorFactoryBean()
|
||||
|
||||
+4
-1
@@ -44,6 +44,7 @@ class InvocationsRecorderClassTransformer implements ClassFileTransformer {
|
||||
|
||||
private final String[] ignoredPackages;
|
||||
|
||||
|
||||
public InvocationsRecorderClassTransformer(String[] instrumentedPackages, String[] ignoredPackages) {
|
||||
Assert.notNull(instrumentedPackages, "instrumentedPackages must not be null");
|
||||
Assert.notNull(ignoredPackages, "ignoredPackages must not be null");
|
||||
@@ -56,6 +57,7 @@ class InvocationsRecorderClassTransformer implements ClassFileTransformer {
|
||||
.toArray(String[]::new);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public byte[] transform(@Nullable ClassLoader classLoader, String className, Class<?> classBeingRedefined,
|
||||
ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
|
||||
@@ -102,7 +104,7 @@ class InvocationsRecorderClassTransformer implements ClassFileTransformer {
|
||||
fileReader.accept(classVisitor, 0);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
System.err.println("Failed to transform class: " + ex);
|
||||
return classfileBuffer;
|
||||
}
|
||||
if (classVisitor.isTransformed()) {
|
||||
@@ -110,4 +112,5 @@ class InvocationsRecorderClassTransformer implements ClassFileTransformer {
|
||||
}
|
||||
return classfileBuffer;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -49,7 +49,6 @@ public final class RuntimeHintsAgent {
|
||||
private static boolean loaded = false;
|
||||
|
||||
private RuntimeHintsAgent() {
|
||||
|
||||
}
|
||||
|
||||
public static void premain(@Nullable String agentArgs, Instrumentation inst) {
|
||||
@@ -68,6 +67,7 @@ public final class RuntimeHintsAgent {
|
||||
return loaded;
|
||||
}
|
||||
|
||||
|
||||
private static final class ParsedArguments {
|
||||
|
||||
List<String> instrumentedPackages;
|
||||
@@ -108,6 +108,6 @@ public final class RuntimeHintsAgent {
|
||||
}
|
||||
return new ParsedArguments(included, excluded);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@ multiRelease {
|
||||
releaseVersions 21, 24
|
||||
}
|
||||
|
||||
def javapoetVersion = "0.7.0"
|
||||
def objenesisVersion = "3.4"
|
||||
def javapoetVersion = "0.10.0"
|
||||
def objenesisVersion = "3.5"
|
||||
|
||||
configurations {
|
||||
java21Api.extendsFrom(api)
|
||||
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.aot.nativex.substitution;
|
||||
|
||||
import com.oracle.svm.core.annotate.Alias;
|
||||
import com.oracle.svm.core.annotate.TargetClass;
|
||||
|
||||
/**
|
||||
* Allow to reference {@code com.sun.beans.finder.ClassFinder} from
|
||||
* {@link Target_Introspector}.
|
||||
*
|
||||
* TODO Remove once Spring Framework requires GraalVM 23.0+, see <a href="https://github.com/oracle/graal/pull/5224">graal#5224</a>.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @since 6.0
|
||||
*/
|
||||
@TargetClass(className = "com.sun.beans.finder.ClassFinder")
|
||||
final class Target_ClassFinder {
|
||||
|
||||
@Alias
|
||||
public static Class<?> findClass(String name, ClassLoader loader) throws ClassNotFoundException {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
-56
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.aot.nativex.substitution;
|
||||
|
||||
import java.beans.Customizer;
|
||||
|
||||
import com.oracle.svm.core.annotate.Substitute;
|
||||
import com.oracle.svm.core.annotate.TargetClass;
|
||||
|
||||
/**
|
||||
* {@link java.beans.Introspector} substitution with a refined {@code findCustomizerClass} implementation
|
||||
* designed to avoid thousands of AWT classes to be included in the native image.
|
||||
*
|
||||
* TODO Remove once Spring Framework requires GraalVM 23.0+, see <a href="https://github.com/oracle/graal/pull/5224">graal#5224</a>.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @since 6.0
|
||||
*/
|
||||
@TargetClass(className = "java.beans.Introspector")
|
||||
final class Target_Introspector {
|
||||
|
||||
@Substitute
|
||||
private static Class<?> findCustomizerClass(Class<?> type) {
|
||||
String name = type.getName() + "Customizer";
|
||||
try {
|
||||
type = Target_ClassFinder.findClass(name, type.getClassLoader());
|
||||
if (Customizer.class.isAssignableFrom(type)) {
|
||||
Class<?> c = type;
|
||||
do {
|
||||
c = c.getSuperclass();
|
||||
if (c.getName().equals("java.awt.Component")) {
|
||||
return type;
|
||||
}
|
||||
} while (!c.getName().equals("java.lang.Object"));
|
||||
}
|
||||
}
|
||||
catch (Exception ignored) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
/**
|
||||
* GraalVM native image substitutions, not part of Spring Framework public API.
|
||||
*/
|
||||
@NullUnmarked
|
||||
package org.springframework.aot.nativex.substitution;
|
||||
|
||||
import org.jspecify.annotations.NullUnmarked;
|
||||
@@ -95,7 +95,7 @@ public class Attribute {
|
||||
* a Code attribute that contains labels.
|
||||
* @deprecated no longer used by ASM.
|
||||
*/
|
||||
@Deprecated
|
||||
@Deprecated(forRemoval = false)
|
||||
protected Label[] getLabels() {
|
||||
return new Label[0];
|
||||
}
|
||||
@@ -174,6 +174,7 @@ public class Attribute {
|
||||
* ClassReader overrides {@link ClassReader#readLabel}. Hence {@link #read(ClassReader, int, int,
|
||||
* char[], int, Label[])} must not manually create {@link Label} instances.
|
||||
*
|
||||
* @param classReader the class that contains the attribute to be read.
|
||||
* @param bytecodeOffset a bytecode offset in a method.
|
||||
* @param labels the already created labels, indexed by their offset. If a label already exists
|
||||
* for bytecodeOffset this method does not create a new one. Otherwise it stores the new label
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user