From e163078a56648991789c264c43dcb89f798f46ba Mon Sep 17 00:00:00 2001 From: Spring Builds Date: Wed, 11 May 2022 07:31:57 +0000 Subject: [PATCH 01/44] Next development version (v5.2.23.BUILD-SNAPSHOT) --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index ed823eeb01..03a81596ff 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version=5.2.22.BUILD-SNAPSHOT +version=5.2.23.BUILD-SNAPSHOT org.gradle.jvmargs=-Xmx1536M org.gradle.caching=true org.gradle.parallel=true -- Gitee From 1488b67712fe936f3e050bd640b738404ada286e Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Sun, 19 Mar 2023 21:56:01 +0100 Subject: [PATCH 02/44] Use docker credentials for fetching CI images --- ci/pipeline.yml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/ci/pipeline.yml b/ci/pipeline.yml index 1f57b3d999..510a0f1ca9 100644 --- a/ci/pipeline.yml +++ b/ci/pipeline.yml @@ -26,7 +26,6 @@ anchors: docker-resource-source: &docker-resource-source username: ((docker-hub-username)) password: ((docker-hub-password)) - tag: ((milestone)) slack-fail-params: &slack-fail-params text: > :concourse-failed: @@ -44,24 +43,34 @@ anchors: GITHUB_TOKEN: ((github-ci-release-token)) resource_types: +- name: registry-image + type: registry-image + source: + <<: *docker-resource-source + repository: concourse/registry-image-resource + tag: 1.7.1 - name: artifactory-resource type: registry-image source: + <<: *docker-resource-source repository: springio/artifactory-resource tag: 0.0.17 - name: github-release type: registry-image source: + <<: *docker-resource-source repository: concourse/github-release-resource tag: 1.5.5 - name: github-status-resource type: registry-image source: + <<: *docker-resource-source repository: dpb587/github-status-resource tag: master - name: slack-notification type: registry-image source: + <<: *docker-resource-source repository: cfcommunity/slack-notification-resource tag: latest resources: @@ -90,12 +99,14 @@ resources: source: <<: *docker-resource-source repository: ((docker-hub-organization))/spring-framework-ci + tag: ((milestone)) - name: ci-image-jdk11 type: docker-image icon: docker source: <<: *docker-resource-source repository: ((docker-hub-organization))/spring-framework-ci-jdk11 + tag: ((milestone)) - name: artifactory-repo type: artifactory-resource icon: package-variant -- Gitee From 9c6cb744d4383f4a2c83c114db76d04d2c2cbb1a Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Thu, 16 Mar 2023 16:33:07 +0100 Subject: [PATCH 03/44] Polishing --- .../expression/spel/EvaluationTests.java | 32 ++++++++----------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java index 98b9704ea3..773baf0457 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2023 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. @@ -168,39 +168,35 @@ public class EvaluationTests extends AbstractExpressionTests { } @Test - public void testRelOperatorsMatches01() { - evaluate("'5.0067' matches '^-?\\d+(\\.\\d{2})?$'", "false", Boolean.class); + void matchesTrue() { + evaluate("'5.00' matches '^-?\\d+(\\.\\d{2})?$'", "true", Boolean.class); } @Test - public void testRelOperatorsMatches02() { - evaluate("'5.00' matches '^-?\\d+(\\.\\d{2})?$'", "true", Boolean.class); + void matchesFalse() { + evaluate("'5.0067' matches '^-?\\d+(\\.\\d{2})?$'", "false", Boolean.class); } @Test - public void testRelOperatorsMatches03() { - evaluateAndCheckError("null matches '^.*$'", SpelMessage.INVALID_FIRST_OPERAND_FOR_MATCHES_OPERATOR, 0, null); + void matchesWithInputConversion() { + evaluate("27 matches '^.*2.*$'", true, Boolean.class); // conversion int --> string } @Test - public void testRelOperatorsMatches04() { - evaluateAndCheckError("'abc' matches null", SpelMessage.INVALID_SECOND_OPERAND_FOR_MATCHES_OPERATOR, 14, null); + void matchesWithNullInput() { + evaluateAndCheckError("null matches '^.*$'", SpelMessage.INVALID_FIRST_OPERAND_FOR_MATCHES_OPERATOR, 0, null); } @Test - public void testRelOperatorsMatches05() { - evaluate("27 matches '^.*2.*$'", true, Boolean.class); // conversion int>string + void matchesWithNullPattern() { + evaluateAndCheckError("'abc' matches null", SpelMessage.INVALID_SECOND_OPERAND_FOR_MATCHES_OPERATOR, 14, null); } @Test // SPR-16731 - public void testMatchesWithPatternAccessThreshold() { + void matchesWithPatternAccessThreshold() { String pattern = "^(?=[a-z0-9-]{1,47})([a-z0-9]+[-]{0,1}){1,47}[a-z0-9]{1}$"; - String expression = "'abcde-fghijklmn-o42pasdfasdfasdf.qrstuvwxyz10x.xx.yyy.zasdfasfd' matches \'" + pattern + "\'"; - Expression expr = parser.parseExpression(expression); - assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy( - expr::getValue) - .withCauseInstanceOf(IllegalStateException.class) - .satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.FLAWED_PATTERN)); + String expression = "'abcde-fghijklmn-o42pasdfasdfasdf.qrstuvwxyz10x.xx.yyy.zasdfasfd' matches '" + pattern + "'"; + evaluateAndCheckError(expression, SpelMessage.FLAWED_PATTERN); } // mixing operators -- Gitee From 52c93b1c4b24d70de233a958e60e7c5822bd274f Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Fri, 17 Mar 2023 12:12:22 +0100 Subject: [PATCH 04/44] Increase scope of regex pattern cache for the SpEL `matches` operator Prior to this commit, the pattern cache for the SpEL `matches` operator only applied to expressions such as the following where the same `matches` operator is invoked multiple times with different input: "map.keySet().?[#this matches '.+xyz']" The pattern cache did not apply to expressions such as the following where the same pattern ('.+xyz') is used in multiple `matches` operations: "foo matches '.+xyz' AND bar matches '.+xyz'" This commit addresses this by moving the instance of the pattern cache map from OperatorMatches to InternalSpelExpressionParser so that the cache can be reused for all `matches` operations for the given parser. Closes gh-30148 --- .../expression/spel/ast/OperatorMatches.java | 24 +++++++++++++++---- .../InternalSpelExpressionParser.java | 10 ++++++-- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorMatches.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorMatches.java index 22795a9287..6b02dbc2db 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorMatches.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorMatches.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2023 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. @@ -36,19 +36,35 @@ import org.springframework.expression.spel.support.BooleanTypedValue; * * @author Andy Clement * @author Juergen Hoeller + * @author Sam Brannen * @since 3.0 */ public class OperatorMatches extends Operator { private static final int PATTERN_ACCESS_THRESHOLD = 1000000; - private final ConcurrentMap patternCache = new ConcurrentHashMap<>(); + private final ConcurrentMap patternCache; + /** + * Create a new {@link OperatorMatches} instance. + * @deprecated as of Spring Framework 5.2.23 in favor of invoking + * {@link #OperatorMatches(ConcurrentMap, int, int, SpelNodeImpl...)} + * with a shared pattern cache instead + */ + @Deprecated public OperatorMatches(int startPos, int endPos, SpelNodeImpl... operands) { - super("matches", startPos, endPos, operands); + this(new ConcurrentHashMap<>(), startPos, endPos, operands); } + /** + * Create a new {@link OperatorMatches} instance with a shared pattern cache. + * @since 5.2.23 + */ + public OperatorMatches(ConcurrentMap patternCache, int startPos, int endPos, SpelNodeImpl... operands) { + super("matches", startPos, endPos, operands); + this.patternCache = patternCache; + } /** * Check the first operand matches the regex specified as the second operand. @@ -63,7 +79,7 @@ public class OperatorMatches extends Operator { SpelNodeImpl leftOp = getLeftOperand(); SpelNodeImpl rightOp = getRightOperand(); String left = leftOp.getValue(state, String.class); - Object right = getRightOperand().getValue(state); + Object right = rightOp.getValue(state); if (left == null) { throw new SpelEvaluationException(leftOp.getStartPosition(), diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java b/spring-expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java index 5b3f69f781..6ca3781d0f 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2023 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. @@ -21,6 +21,8 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Deque; import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import java.util.regex.Pattern; import org.springframework.expression.ParseException; @@ -83,6 +85,7 @@ import org.springframework.util.StringUtils; * @author Andy Clement * @author Juergen Hoeller * @author Phillip Webb + * @author Sam Brannen * @since 3.0 */ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { @@ -95,6 +98,9 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { // For rules that build nodes, they are stacked here for return private final Deque constructedNodes = new ArrayDeque<>(); + // Shared cache for compiled regex patterns + private final ConcurrentMap patternCache = new ConcurrentHashMap<>(); + // The expression being parsed private String expressionString = ""; @@ -248,7 +254,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { } if (tk == TokenKind.MATCHES) { - return new OperatorMatches(t.startPos, t.endPos, expr, rhExpr); + return new OperatorMatches(this.patternCache, t.startPos, t.endPos, expr, rhExpr); } Assert.isTrue(tk == TokenKind.BETWEEN, "Between token expected"); -- Gitee From 4542b531035eaaf7765f4557ab433e6695bfa1a9 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Thu, 16 Mar 2023 17:28:37 +0100 Subject: [PATCH 05/44] Improve diagnostics in SpEL for repeated text Attempting to create repeated text in a SpEL expression using the repeat operator can result in errors that are not very helpful to the user. This commit improves the diagnostics in SpEL for the repeat operator by throwing a SpelEvaluationException with a meaningful error message in order to better assist the user. Closes gh-30149 --- .../expression/spel/SpelMessage.java | 8 +++-- .../expression/spel/ast/OpMultiply.java | 29 +++++++++++++++---- .../expression/spel/OperatorTests.java | 23 +++++++++++---- 3 files changed, 47 insertions(+), 13 deletions(-) diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java b/spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java index a3f588a1de..4fc4eae7be 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 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. @@ -260,7 +260,11 @@ public enum SpelMessage { /** @since 5.2.20 */ MAX_ARRAY_ELEMENTS_THRESHOLD_EXCEEDED(Kind.ERROR, 1075, - "Array declares too many elements, exceeding the threshold of ''{0}''"); + "Array declares too many elements, exceeding the threshold of ''{0}''"), + + /** @since 5.2.23 */ + MAX_REPEATED_TEXT_SIZE_EXCEEDED(Kind.ERROR, 1076, + "Repeated text results in too many characters, exceeding the threshold of ''{0}''"); private final Kind kind; diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpMultiply.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpMultiply.java index fbf28317b3..0cff5cb0c8 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpMultiply.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpMultiply.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2023 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. @@ -25,6 +25,8 @@ import org.springframework.expression.Operation; import org.springframework.expression.TypedValue; import org.springframework.expression.spel.CodeFlow; import org.springframework.expression.spel.ExpressionState; +import org.springframework.expression.spel.SpelEvaluationException; +import org.springframework.expression.spel.SpelMessage; import org.springframework.util.Assert; import org.springframework.util.NumberUtils; @@ -52,6 +54,13 @@ import org.springframework.util.NumberUtils; */ public class OpMultiply extends Operator { + /** + * Maximum number of characters permitted in repeated text. + * @since 5.2.23 + */ + private static final int MAX_REPEATED_TEXT_SIZE = 256; + + public OpMultiply(int startPos, int endPos, SpelNodeImpl... operands) { super("*", startPos, endPos, operands); } @@ -109,10 +118,13 @@ public class OpMultiply extends Operator { } if (leftOperand instanceof String && rightOperand instanceof Integer) { - int repeats = (Integer) rightOperand; - StringBuilder result = new StringBuilder(); - for (int i = 0; i < repeats; i++) { - result.append(leftOperand); + String text = (String) leftOperand; + int count = (Integer) rightOperand; + int requestedSize = text.length() * count; + checkRepeatedTextSize(requestedSize); + StringBuilder result = new StringBuilder(requestedSize); + for (int i = 0; i < count; i++) { + result.append(text); } return new TypedValue(result.toString()); } @@ -120,6 +132,13 @@ public class OpMultiply extends Operator { return state.operate(Operation.MULTIPLY, leftOperand, rightOperand); } + private void checkRepeatedTextSize(int requestedSize) { + if (requestedSize > MAX_REPEATED_TEXT_SIZE) { + throw new SpelEvaluationException(getStartPosition(), + SpelMessage.MAX_REPEATED_TEXT_SIZE_EXCEEDED, MAX_REPEATED_TEXT_SIZE); + } + } + @Override public boolean isCompilable() { if (!getLeftOperand().isCompilable()) { diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/OperatorTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/OperatorTests.java index 92fef3bec6..26a219ece3 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/OperatorTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/OperatorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2023 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. @@ -21,10 +21,12 @@ import java.math.BigInteger; import org.junit.jupiter.api.Test; +import org.springframework.expression.Expression; import org.springframework.expression.spel.ast.Operator; import org.springframework.expression.spel.standard.SpelExpression; import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.expression.spel.SpelMessage.MAX_REPEATED_TEXT_SIZE_EXCEEDED; /** * Tests the evaluation of expressions using relational operators. @@ -32,6 +34,7 @@ import static org.assertj.core.api.Assertions.assertThat; * @author Andy Clement * @author Juergen Hoeller * @author Giovanni Dall'Oglio Risso + * @author Sam Brannen */ public class OperatorTests extends AbstractExpressionTests { @@ -324,11 +327,6 @@ public class OperatorTests extends AbstractExpressionTests { evaluate("3.5", 3.5d, Double.class); } - @Test - public void testMultiplyStringInt() { - evaluate("'a' * 5", "aaaaa", String.class); - } - @Test public void testMultiplyDoubleDoubleGivesDouble() { evaluate("3.0d * 5.0d", 15.0d, Double.class); @@ -576,6 +574,19 @@ public class OperatorTests extends AbstractExpressionTests { evaluate("'abc' != 'def'", true, Boolean.class); } + @Test + void stringRepeat() { + evaluate("'abc' * 0", "", String.class); + evaluate("'abc' * 1", "abc", String.class); + evaluate("'abc' * 2", "abcabc", String.class); + + Expression expr = parser.parseExpression("'a' * 256"); + assertThat(expr.getValue(context, String.class)).hasSize(256); + + // 4 is the position of the '*' (repeat operator) + evaluateAndCheckError("'a' * 257", String.class, MAX_REPEATED_TEXT_SIZE_EXCEEDED, 4); + } + @Test public void testLongs() { evaluate("3L == 4L", false, Boolean.class); -- Gitee From b9b31afcc905cd9d5e63739ff5920d849f7f20f0 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Fri, 17 Mar 2023 12:56:53 +0100 Subject: [PATCH 06/44] Improve diagnostics in SpEL for `matches` operator Supplying a large regular expression to the `matches` operator in a SpEL expression can result in errors that are not very helpful to the user. This commit improves the diagnostics in SpEL for the `matches` operator by throwing a SpelEvaluationException with a meaningful error message to better assist the user. Closes gh-30150 --- .../expression/spel/SpelMessage.java | 6 +++- .../expression/spel/ast/OperatorMatches.java | 33 ++++++++++++++----- .../expression/spel/EvaluationTests.java | 14 ++++++++ 3 files changed, 43 insertions(+), 10 deletions(-) diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java b/spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java index 4fc4eae7be..f1fd0f67f7 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java @@ -264,7 +264,11 @@ public enum SpelMessage { /** @since 5.2.23 */ MAX_REPEATED_TEXT_SIZE_EXCEEDED(Kind.ERROR, 1076, - "Repeated text results in too many characters, exceeding the threshold of ''{0}''"); + "Repeated text results in too many characters, exceeding the threshold of ''{0}''"), + + /** @since 5.2.23 */ + MAX_REGEX_LENGTH_EXCEEDED(Kind.ERROR, 1077, + "Regular expression contains too many characters, exceeding the threshold of ''{0}''"); private final Kind kind; diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorMatches.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorMatches.java index 6b02dbc2db..0863716c18 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorMatches.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorMatches.java @@ -43,6 +43,12 @@ public class OperatorMatches extends Operator { private static final int PATTERN_ACCESS_THRESHOLD = 1000000; + /** + * Maximum number of characters permitted in a regular expression. + * @since 5.2.23 + */ + private static final int MAX_REGEX_LENGTH = 256; + private final ConcurrentMap patternCache; @@ -78,26 +84,28 @@ public class OperatorMatches extends Operator { public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException { SpelNodeImpl leftOp = getLeftOperand(); SpelNodeImpl rightOp = getRightOperand(); - String left = leftOp.getValue(state, String.class); - Object right = rightOp.getValue(state); - if (left == null) { + String input = leftOp.getValue(state, String.class); + if (input == null) { throw new SpelEvaluationException(leftOp.getStartPosition(), SpelMessage.INVALID_FIRST_OPERAND_FOR_MATCHES_OPERATOR, (Object) null); } + + Object right = rightOp.getValue(state); if (!(right instanceof String)) { throw new SpelEvaluationException(rightOp.getStartPosition(), SpelMessage.INVALID_SECOND_OPERAND_FOR_MATCHES_OPERATOR, right); } + String regex = (String) right; try { - String rightString = (String) right; - Pattern pattern = this.patternCache.get(rightString); + Pattern pattern = this.patternCache.get(regex); if (pattern == null) { - pattern = Pattern.compile(rightString); - this.patternCache.putIfAbsent(rightString, pattern); + checkRegexLength(regex); + pattern = Pattern.compile(regex); + this.patternCache.putIfAbsent(regex, pattern); } - Matcher matcher = pattern.matcher(new MatcherInput(left, new AccessCount())); + Matcher matcher = pattern.matcher(new MatcherInput(input, new AccessCount())); return BooleanTypedValue.forValue(matcher.matches()); } catch (PatternSyntaxException ex) { @@ -110,6 +118,13 @@ public class OperatorMatches extends Operator { } } + private void checkRegexLength(String regex) { + if (regex.length() > MAX_REGEX_LENGTH) { + throw new SpelEvaluationException(getStartPosition(), + SpelMessage.MAX_REGEX_LENGTH_EXCEEDED, MAX_REGEX_LENGTH); + } + } + private static class AccessCount { @@ -127,7 +142,7 @@ public class OperatorMatches extends Operator { private final CharSequence value; - private AccessCount access; + private final AccessCount access; public MatcherInput(CharSequence value, AccessCount access) { this.value = value; diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java index 773baf0457..6b5f02a05b 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java @@ -199,6 +199,20 @@ public class EvaluationTests extends AbstractExpressionTests { evaluateAndCheckError(expression, SpelMessage.FLAWED_PATTERN); } + @Test + void matchesWithPatternLengthThreshold() { + String pattern = "(0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" + + "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" + + "01234567890123456789012345678901234567890123456789|abc)"; + assertThat(pattern).hasSize(256); + Expression expr = parser.parseExpression("'abc' matches '" + pattern + "'"); + assertThat(expr.getValue(context, Boolean.class)).isTrue(); + + pattern += "?"; + assertThat(pattern).hasSize(257); + evaluateAndCheckError("'abc' matches '" + pattern + "'", Boolean.class, SpelMessage.MAX_REGEX_LENGTH_EXCEEDED); + } + // mixing operators @Test public void testMixingOperators01() { -- Gitee From ad191277a6c0c2b59ce96e7a04e4a3d5130a55f7 Mon Sep 17 00:00:00 2001 From: Spring Builds Date: Mon, 20 Mar 2023 14:22:41 +0000 Subject: [PATCH 07/44] Next development version (v5.2.24.BUILD-SNAPSHOT) --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 03a81596ff..a1db56f1a5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version=5.2.23.BUILD-SNAPSHOT +version=5.2.24.BUILD-SNAPSHOT org.gradle.jvmargs=-Xmx1536M org.gradle.caching=true org.gradle.parallel=true -- Gitee From 18403cddea3151475069c5db03b4edf9c760f544 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Tue, 11 Apr 2023 23:08:53 +0200 Subject: [PATCH 08/44] Change max regex length in SpEL expressions to 1000 This commit changes the max regex length in SpEL expressions from 1024 to 1000 in order to consistently use "round" numbers for recently introduced limits. See gh-30265 --- .../expression/spel/ast/OperatorMatches.java | 2 +- .../expression/spel/EvaluationTests.java | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorMatches.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorMatches.java index 0863716c18..de277c68ea 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorMatches.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorMatches.java @@ -47,7 +47,7 @@ public class OperatorMatches extends Operator { * Maximum number of characters permitted in a regular expression. * @since 5.2.23 */ - private static final int MAX_REGEX_LENGTH = 256; + private static final int MAX_REGEX_LENGTH = 1000; private final ConcurrentMap patternCache; diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java index 6b5f02a05b..75d2a25f55 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java @@ -201,18 +201,24 @@ public class EvaluationTests extends AbstractExpressionTests { @Test void matchesWithPatternLengthThreshold() { - String pattern = "(0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" + - "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" + - "01234567890123456789012345678901234567890123456789|abc)"; - assertThat(pattern).hasSize(256); - Expression expr = parser.parseExpression("'abc' matches '" + pattern + "'"); + String pattern = String.format("^(%s|X)", repeat("12345", 199)); + assertThat(pattern).hasSize(1000); + Expression expr = parser.parseExpression("'X' matches '" + pattern + "'"); assertThat(expr.getValue(context, Boolean.class)).isTrue(); pattern += "?"; - assertThat(pattern).hasSize(257); + assertThat(pattern).hasSize(1001); evaluateAndCheckError("'abc' matches '" + pattern + "'", Boolean.class, SpelMessage.MAX_REGEX_LENGTH_EXCEEDED); } + private String repeat(String str, int count) { + String result = ""; + for (int i = 0; i < count; i++) { + result += str; + } + return result; + } + // mixing operators @Test public void testMixingOperators01() { -- Gitee From cbbb2871b3a4092c623a4a87c4dd24f54aa24225 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Fri, 7 Apr 2023 17:02:09 +0200 Subject: [PATCH 09/44] Limit string concatenation in SpEL expressions This commit introduces support for limiting the maximum length of a string resulting from the concatenation operator (+) in SpEL expressions. Closes gh-30332 --- .../expression/spel/SpelMessage.java | 6 +- .../expression/spel/ast/OpPlus.java | 45 ++++++++++-- .../expression/spel/OperatorTests.java | 70 ++++++++++++++++--- 3 files changed, 104 insertions(+), 17 deletions(-) diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java b/spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java index f1fd0f67f7..3ee35997fd 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java @@ -268,7 +268,11 @@ public enum SpelMessage { /** @since 5.2.23 */ MAX_REGEX_LENGTH_EXCEEDED(Kind.ERROR, 1077, - "Regular expression contains too many characters, exceeding the threshold of ''{0}''"); + "Regular expression contains too many characters, exceeding the threshold of ''{0}''"), + + /** @since 5.2.24 */ + MAX_CONCATENATED_STRING_LENGTH_EXCEEDED(Kind.ERROR, 1078, + "Concatenated string is too long, exceeding the threshold of ''{0}'' characters"); private final Kind kind; diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpPlus.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpPlus.java index d128f56b2d..365aa8399b 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpPlus.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpPlus.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2023 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. @@ -27,6 +27,8 @@ import org.springframework.expression.TypeConverter; import org.springframework.expression.TypedValue; import org.springframework.expression.spel.CodeFlow; import org.springframework.expression.spel.ExpressionState; +import org.springframework.expression.spel.SpelEvaluationException; +import org.springframework.expression.spel.SpelMessage; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.NumberUtils; @@ -46,10 +48,18 @@ import org.springframework.util.NumberUtils; * @author Juergen Hoeller * @author Ivo Smid * @author Giovanni Dall'Oglio Risso + * @author Sam Brannen * @since 3.0 */ public class OpPlus extends Operator { + /** + * Maximum number of characters permitted in a concatenated string. + * @since 5.2.24 + */ + private static final int MAX_CONCATENATED_STRING_LENGTH = 100_000; + + public OpPlus(int startPos, int endPos, SpelNodeImpl... operands) { super("+", startPos, endPos, operands); Assert.notEmpty(operands, "Operands must not be empty"); @@ -123,22 +133,45 @@ public class OpPlus extends Operator { if (leftOperand instanceof String && rightOperand instanceof String) { this.exitTypeDescriptor = "Ljava/lang/String"; - return new TypedValue((String) leftOperand + rightOperand); + String leftString = (String) leftOperand; + String rightString = (String) rightOperand; + checkStringLength(leftString); + checkStringLength(rightString); + return concatenate(leftString, rightString); } if (leftOperand instanceof String) { - return new TypedValue( - leftOperand + (rightOperand == null ? "null" : convertTypedValueToString(operandTwoValue, state))); + String leftString = (String) leftOperand; + checkStringLength(leftString); + String rightString = (rightOperand == null ? "null" : convertTypedValueToString(operandTwoValue, state)); + checkStringLength(rightString); + return concatenate(leftString, rightString); } if (rightOperand instanceof String) { - return new TypedValue( - (leftOperand == null ? "null" : convertTypedValueToString(operandOneValue, state)) + rightOperand); + String rightString = (String) rightOperand; + checkStringLength(rightString); + String leftString = (leftOperand == null ? "null" : convertTypedValueToString(operandOneValue, state)); + checkStringLength(leftString); + return concatenate(leftString, rightString); } return state.operate(Operation.ADD, leftOperand, rightOperand); } + private void checkStringLength(String string) { + if (string.length() > MAX_CONCATENATED_STRING_LENGTH) { + throw new SpelEvaluationException(getStartPosition(), + SpelMessage.MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, MAX_CONCATENATED_STRING_LENGTH); + } + } + + private TypedValue concatenate(String leftString, String rightString) { + String result = leftString + rightString; + checkStringLength(result); + return new TypedValue(result); + } + @Override public String toStringAST() { if (this.children.length < 2) { // unary plus diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/OperatorTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/OperatorTests.java index 26a219ece3..83904eb1a6 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/OperatorTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/OperatorTests.java @@ -26,6 +26,7 @@ import org.springframework.expression.spel.ast.Operator; import org.springframework.expression.spel.standard.SpelExpression; import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.expression.spel.SpelMessage.MAX_CONCATENATED_STRING_LENGTH_EXCEEDED; import static org.springframework.expression.spel.SpelMessage.MAX_REPEATED_TEXT_SIZE_EXCEEDED; /** @@ -391,11 +392,7 @@ public class OperatorTests extends AbstractExpressionTests { evaluate("3.0f + 5.0f", 8.0f, Float.class); evaluate("3.0d + 5.0d", 8.0d, Double.class); evaluate("3 + new java.math.BigDecimal('5')", new BigDecimal("8"), BigDecimal.class); - - evaluate("'ab' + 2", "ab2", String.class); - evaluate("2 + 'a'", "2a", String.class); - evaluate("'ab' + null", "abnull", String.class); - evaluate("null + 'ab'", "nullab", String.class); + evaluate("5 + new Integer('37')", 42, Integer.class); // AST: SpelExpression expr = (SpelExpression)parser.parseExpression("+3"); @@ -404,11 +401,11 @@ public class OperatorTests extends AbstractExpressionTests { assertThat(expr.toStringAST()).isEqualTo("(2 + 3)"); // use as a unary operator - evaluate("+5d",5d,Double.class); - evaluate("+5L",5L,Long.class); - evaluate("+5",5,Integer.class); - evaluate("+new java.math.BigDecimal('5')", new BigDecimal("5"),BigDecimal.class); - evaluateAndCheckError("+'abc'",SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES); + evaluate("+5d", 5d, Double.class); + evaluate("+5L", 5L, Long.class); + evaluate("+5", 5, Integer.class); + evaluate("+new java.math.BigDecimal('5')", new BigDecimal("5"), BigDecimal.class); + evaluateAndCheckError("+'abc'", SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES); // string concatenation evaluate("'abc'+'def'","abcdef",String.class); @@ -587,6 +584,59 @@ public class OperatorTests extends AbstractExpressionTests { evaluateAndCheckError("'a' * 257", String.class, MAX_REPEATED_TEXT_SIZE_EXCEEDED, 4); } + @Test + void stringConcatenation() { + evaluate("'' + ''", "", String.class); + evaluate("'' + null", "null", String.class); + evaluate("null + ''", "null", String.class); + evaluate("'ab' + null", "abnull", String.class); + evaluate("null + 'ab'", "nullab", String.class); + evaluate("'ab' + 2", "ab2", String.class); + evaluate("2 + 'ab'", "2ab", String.class); + evaluate("'abc' + 'def'", "abcdef", String.class); + + // Text is big but not too big + final int maxSize = 100_000; + context.setVariable("text1", createString(maxSize)); + Expression expr = parser.parseExpression("#text1 + ''"); + assertThat(expr.getValue(context, String.class)).hasSize(maxSize); + + expr = parser.parseExpression("'' + #text1"); + assertThat(expr.getValue(context, String.class)).hasSize(maxSize); + + context.setVariable("text1", createString(maxSize / 2)); + expr = parser.parseExpression("#text1 + #text1"); + assertThat(expr.getValue(context, String.class)).hasSize(maxSize); + + // Text is too big + context.setVariable("text1", createString(maxSize + 1)); + evaluateAndCheckError("#text1 + ''", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 7); + evaluateAndCheckError("#text1 + true", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 7); + evaluateAndCheckError("'' + #text1", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 3); + evaluateAndCheckError("true + #text1", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 5); + + context.setVariable("text1", createString(maxSize / 2)); + context.setVariable("text2", createString((maxSize / 2) + 1)); + evaluateAndCheckError("#text1 + #text2", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 7); + evaluateAndCheckError("#text1 + #text2 + true", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 7); + evaluateAndCheckError("#text1 + true + #text2", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 14); + evaluateAndCheckError("true + #text1 + #text2", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 14); + + evaluateAndCheckError("#text2 + #text1", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 7); + evaluateAndCheckError("#text2 + #text1 + true", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 7); + evaluateAndCheckError("#text2 + true + #text1", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 14); + evaluateAndCheckError("true + #text2 + #text1", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 14); + + context.setVariable("text1", createString((maxSize / 3) + 1)); + evaluateAndCheckError("#text1 + #text1 + #text1", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 16); + evaluateAndCheckError("(#text1 + #text1) + #text1", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 18); + evaluateAndCheckError("#text1 + (#text1 + #text1)", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 7); + } + + private static String createString(int size) { + return new String(new char[size]); + } + @Test public void testLongs() { evaluate("3L == 4L", false, Boolean.class); -- Gitee From 965a6392757d20f9db19241126fcc719a51eac15 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Fri, 7 Apr 2023 18:13:02 +0200 Subject: [PATCH 10/44] Limit SpEL expression length This commit enforces a limit of the maximum size of a single SpEL expression. Closes gh-30330 --- .../expression/spel/SpelMessage.java | 10 ++++-- .../InternalSpelExpressionParser.java | 15 +++++++++ .../expression/spel/EvaluationTests.java | 31 ++++++++++++++----- 3 files changed, 45 insertions(+), 11 deletions(-) diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java b/spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java index 3ee35997fd..6bb84f86ec 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java @@ -264,15 +264,19 @@ public enum SpelMessage { /** @since 5.2.23 */ MAX_REPEATED_TEXT_SIZE_EXCEEDED(Kind.ERROR, 1076, - "Repeated text results in too many characters, exceeding the threshold of ''{0}''"), + "Repeated text is too long, exceeding the threshold of ''{0}'' characters"), /** @since 5.2.23 */ MAX_REGEX_LENGTH_EXCEEDED(Kind.ERROR, 1077, - "Regular expression contains too many characters, exceeding the threshold of ''{0}''"), + "Regular expression is too long, exceeding the threshold of ''{0}'' characters"), /** @since 5.2.24 */ MAX_CONCATENATED_STRING_LENGTH_EXCEEDED(Kind.ERROR, 1078, - "Concatenated string is too long, exceeding the threshold of ''{0}'' characters"); + "Concatenated string is too long, exceeding the threshold of ''{0}'' characters"), + + /** @since 5.2.24 */ + MAX_EXPRESSION_LENGTH_EXCEEDED(Kind.ERROR, 1079, + "SpEL expression is too long, exceeding the threshold of ''{0}'' characters"); private final Kind kind; diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java b/spring-expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java index 6ca3781d0f..23a09f7197 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java @@ -29,6 +29,7 @@ import org.springframework.expression.ParseException; import org.springframework.expression.ParserContext; import org.springframework.expression.common.TemplateAwareExpressionParser; import org.springframework.expression.spel.InternalParseException; +import org.springframework.expression.spel.SpelEvaluationException; import org.springframework.expression.spel.SpelMessage; import org.springframework.expression.spel.SpelParseException; import org.springframework.expression.spel.SpelParserConfiguration; @@ -92,6 +93,12 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { private static final Pattern VALID_QUALIFIED_ID_PATTERN = Pattern.compile("[\\p{L}\\p{N}_$]+"); + /** + * Maximum length permitted for a SpEL expression. + * @since 5.2.24 + */ + private static final int MAX_EXPRESSION_LENGTH = 10_000; + private final SpelParserConfiguration configuration; @@ -127,6 +134,8 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { protected SpelExpression doParseExpression(String expressionString, @Nullable ParserContext context) throws ParseException { + checkExpressionLength(expressionString); + try { this.expressionString = expressionString; Tokenizer tokenizer = new Tokenizer(expressionString); @@ -148,6 +157,12 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { } } + private void checkExpressionLength(String string) { + if (string.length() > MAX_EXPRESSION_LENGTH) { + throw new SpelEvaluationException(SpelMessage.MAX_EXPRESSION_LENGTH_EXCEEDED, MAX_EXPRESSION_LENGTH); + } + } + // expression // : logicalOrExpression // ( (ASSIGN^ logicalOrExpression) diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java index 75d2a25f55..4a2f596ee2 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java @@ -59,6 +59,20 @@ import static org.assertj.core.api.Assertions.within; */ public class EvaluationTests extends AbstractExpressionTests { + @Test + void expressionLength() { + String expression = String.format("'X' + '%s'", repeat(" ", 9_992)); + assertThat(expression).hasSize(10_000); + Expression expr = parser.parseExpression(expression); + String result = expr.getValue(context, String.class); + assertThat(result).hasSize(9_993); + assertThat(result.trim()).isEqualTo("X"); + + expression = String.format("'X' + '%s'", repeat(" ", 9_993)); + assertThat(expression).hasSize(10_001); + evaluateAndCheckError(expression, String.class, SpelMessage.MAX_EXPRESSION_LENGTH_EXCEEDED); + } + @Test public void testCreateListsOnAttemptToIndexNull01() throws EvaluationException, ParseException { ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); @@ -211,14 +225,6 @@ public class EvaluationTests extends AbstractExpressionTests { evaluateAndCheckError("'abc' matches '" + pattern + "'", Boolean.class, SpelMessage.MAX_REGEX_LENGTH_EXCEEDED); } - private String repeat(String str, int count) { - String result = ""; - for (int i = 0; i < count; i++) { - result += str; - } - return result; - } - // mixing operators @Test public void testMixingOperators01() { @@ -1373,6 +1379,15 @@ public class EvaluationTests extends AbstractExpressionTests { } + private static String repeat(String str, int count) { + String result = ""; + for (int i = 0; i < count; i++) { + result += str; + } + return result; + } + + @SuppressWarnings("rawtypes") static class TestClass { -- Gitee From e246b47f4d735962c3f793435670828cfd92259c Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Sun, 9 Apr 2023 18:19:33 +0200 Subject: [PATCH 11/44] Disable variable assignment in SimpleEvaluationContext This commit introduces infrastructure to differentiate between programmatic setting of a variable in an EvaluationContext versus the assignment of a variable within a SpEL expression using the assignment operator (=). In addition, this commit disables variable assignment within expressions when using the SimpleEvaluationContext. Closes gh-30328 --- .../expression/EvaluationContext.java | 45 ++++++++++++++++--- .../expression/spel/ExpressionState.java | 33 ++++++++++++-- .../expression/spel/SpelMessage.java | 6 ++- .../expression/spel/ast/Assign.java | 7 ++- .../spel/ast/CompoundExpression.java | 13 ++++-- .../expression/spel/ast/Indexer.java | 14 ++++-- .../spel/ast/PropertyOrFieldReference.java | 12 +++-- .../expression/spel/ast/SpelNodeImpl.java | 32 +++++++++++-- .../spel/ast/VariableReference.java | 21 +++++---- .../spel/support/SimpleEvaluationContext.java | 15 ++++++- .../expression/spel/EvaluationTests.java | 24 +++++++++- 11 files changed, 181 insertions(+), 41 deletions(-) diff --git a/spring-expression/src/main/java/org/springframework/expression/EvaluationContext.java b/spring-expression/src/main/java/org/springframework/expression/EvaluationContext.java index 0b4d2eab97..0c393a86db 100644 --- a/spring-expression/src/main/java/org/springframework/expression/EvaluationContext.java +++ b/spring-expression/src/main/java/org/springframework/expression/EvaluationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2023 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. @@ -17,6 +17,7 @@ package org.springframework.expression; import java.util.List; +import java.util.function.Supplier; import org.springframework.lang.Nullable; @@ -24,12 +25,21 @@ import org.springframework.lang.Nullable; * Expressions are executed in an evaluation context. It is in this context that * references are resolved when encountered during expression evaluation. * - *

There is a default implementation of this EvaluationContext interface: - * {@link org.springframework.expression.spel.support.StandardEvaluationContext} - * which can be extended, rather than having to implement everything manually. + *

There are two default implementations of this interface. + *

    + *
  • {@link org.springframework.expression.spel.support.SimpleEvaluationContext + * SimpleEvaluationContext}: a simpler builder-style {@code EvaluationContext} + * variant for data-binding purposes, which allows for opting into several SpEL + * features as needed.
  • + *
  • {@link org.springframework.expression.spel.support.StandardEvaluationContext + * StandardEvaluationContext}: a powerful and highly configurable {@code EvaluationContext} + * implementation, which can be extended, rather than having to implement everything + * manually.
  • + *
* * @author Andy Clement * @author Juergen Hoeller + * @author Sam Brannen * @since 3.0 */ public interface EvaluationContext { @@ -85,7 +95,30 @@ public interface EvaluationContext { OperatorOverloader getOperatorOverloader(); /** - * Set a named variable within this evaluation context to a specified value. + * Assign the value created by the specified {@link Supplier} to a named variable + * within this evaluation context. + *

In contrast to {@link #setVariable(String, Object)}, this method should only + * be invoked to support the assignment operator ({@code =}) within an expression. + *

By default, this method delegates to {@code setVariable(String, Object)}, + * providing the value created by the {@code valueSupplier}. Concrete implementations + * may override this default method to provide different semantics. + * @param name the name of the variable to assign + * @param valueSupplier the supplier of the value to be assigned to the variable + * @return a {@link TypedValue} wrapping the assigned value + * @since 5.2.24 + */ + default TypedValue assignVariable(String name, Supplier valueSupplier) { + TypedValue typedValue = valueSupplier.get(); + setVariable(name, typedValue.getValue()); + return typedValue; + } + + /** + * Set a named variable in this evaluation context to a specified value. + *

In contrast to {@link #assignVariable(String, Supplier)}, this method + * should only be invoked programmatically when interacting directly with the + * {@code EvaluationContext} — for example, to provide initial + * configuration for the context. * @param name the name of the variable to set * @param value the value to be placed in the variable */ @@ -93,7 +126,7 @@ public interface EvaluationContext { /** * Look up a named variable within this evaluation context. - * @param name variable to lookup + * @param name the name of the variable to look up * @return the value of the variable, or {@code null} if not found */ @Nullable diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ExpressionState.java b/spring-expression/src/main/java/org/springframework/expression/spel/ExpressionState.java index 252af447af..8dadae5967 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ExpressionState.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ExpressionState.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2023 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. @@ -23,6 +23,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; +import java.util.function.Supplier; import org.springframework.core.convert.TypeDescriptor; import org.springframework.expression.EvaluationContext; @@ -38,18 +39,19 @@ import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; /** - * An ExpressionState is for maintaining per-expression-evaluation state, any changes to - * it are not seen by other expressions but it gives a place to hold local variables and + * ExpressionState is for maintaining per-expression-evaluation state: any changes to + * it are not seen by other expressions, but it gives a place to hold local variables and * for component expressions in a compound expression to communicate state. This is in * contrast to the EvaluationContext, which is shared amongst expression evaluations, and * any changes to it will be seen by other expressions or any code that chooses to ask * questions of the context. * - *

It also acts as a place for to define common utility routines that the various AST + *

It also acts as a place to define common utility routines that the various AST * nodes might need. * * @author Andy Clement * @author Juergen Hoeller + * @author Sam Brannen * @since 3.0 */ public class ExpressionState { @@ -138,6 +140,29 @@ public class ExpressionState { return this.scopeRootObjects.element(); } + /** + * Assign the value created by the specified {@link Supplier} to a named variable + * within the evaluation context. + *

In contrast to {@link #setVariable(String, Object)}, this method should + * only be invoked to support assignment within an expression. + * @param name the name of the variable to assign + * @param valueSupplier the supplier of the value to be assigned to the variable + * @return a {@link TypedValue} wrapping the assigned value + * @since 5.2.24 + * @see EvaluationContext#assignVariable(String, Supplier) + */ + public TypedValue assignVariable(String name, Supplier valueSupplier) { + return this.relatedContext.assignVariable(name, valueSupplier); + } + + /** + * Set a named variable in the evaluation context to a specified value. + *

In contrast to {@link #assignVariable(String, Supplier)}, this method + * should only be invoked programmatically. + * @param name the name of the variable to set + * @param value the value to be placed in the variable + * @see EvaluationContext#setVariable(String, Object) + */ public void setVariable(String name, @Nullable Object value) { this.relatedContext.setVariable(name, value); } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java b/spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java index 6bb84f86ec..74246bc45c 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java @@ -276,7 +276,11 @@ public enum SpelMessage { /** @since 5.2.24 */ MAX_EXPRESSION_LENGTH_EXCEEDED(Kind.ERROR, 1079, - "SpEL expression is too long, exceeding the threshold of ''{0}'' characters"); + "SpEL expression is too long, exceeding the threshold of ''{0}'' characters"), + + /** @since 5.2.24 */ + VARIABLE_ASSIGNMENT_NOT_SUPPORTED(Kind.ERROR, 1080, + "Assignment to variable ''{0}'' is not supported"); private final Kind kind; diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Assign.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Assign.java index a009a07db5..55e5d2e4ff 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Assign.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Assign.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2023 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. @@ -27,6 +27,7 @@ import org.springframework.expression.spel.ExpressionState; *

Example: 'someNumberProperty=42' * * @author Andy Clement + * @author Sam Brannen * @since 3.0 */ public class Assign extends SpelNodeImpl { @@ -38,9 +39,7 @@ public class Assign extends SpelNodeImpl { @Override public TypedValue getValueInternal(ExpressionState state) throws EvaluationException { - TypedValue newValue = this.children[1].getValueInternal(state); - getChild(0).setValue(state, newValue.getValue()); - return newValue; + return this.children[0].setValueInternal(state, () -> this.children[1].getValueInternal(state)); } @Override diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/CompoundExpression.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/CompoundExpression.java index 0e47facfa7..616a503a4e 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/CompoundExpression.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/CompoundExpression.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2023 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. @@ -17,6 +17,7 @@ package org.springframework.expression.spel.ast; import java.util.StringJoiner; +import java.util.function.Supplier; import org.springframework.asm.MethodVisitor; import org.springframework.expression.EvaluationException; @@ -24,13 +25,13 @@ import org.springframework.expression.TypedValue; import org.springframework.expression.spel.CodeFlow; import org.springframework.expression.spel.ExpressionState; import org.springframework.expression.spel.SpelEvaluationException; -import org.springframework.lang.Nullable; /** * Represents a DOT separated expression sequence, such as * {@code 'property1.property2.methodOne()'}. * * @author Andy Clement + * @author Sam Brannen * @since 3.0 */ public class CompoundExpression extends SpelNodeImpl { @@ -95,8 +96,12 @@ public class CompoundExpression extends SpelNodeImpl { } @Override - public void setValue(ExpressionState state, @Nullable Object value) throws EvaluationException { - getValueRef(state).setValue(value); + public TypedValue setValueInternal(ExpressionState state, Supplier valueSupplier) + throws EvaluationException { + + TypedValue typedValue = valueSupplier.get(); + getValueRef(state).setValue(typedValue.getValue()); + return typedValue; } @Override diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java index 4ef4a0ed4e..5ae8f96213 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2023 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. @@ -25,6 +25,7 @@ import java.util.Collection; import java.util.List; import java.util.Map; import java.util.StringJoiner; +import java.util.function.Supplier; import org.springframework.asm.MethodVisitor; import org.springframework.core.convert.TypeDescriptor; @@ -45,11 +46,12 @@ import org.springframework.util.ReflectionUtils; /** * An Indexer can index into some proceeding structure to access a particular piece of it. - * Supported structures are: strings / collections (lists/sets) / arrays. + *

Supported structures are: strings / collections (lists/sets) / arrays. * * @author Andy Clement * @author Phillip Webb * @author Stephane Nicoll + * @author Sam Brannen * @since 3.0 */ // TODO support multidimensional arrays @@ -102,8 +104,12 @@ public class Indexer extends SpelNodeImpl { } @Override - public void setValue(ExpressionState state, @Nullable Object newValue) throws EvaluationException { - getValueRef(state).setValue(newValue); + public TypedValue setValueInternal(ExpressionState state, Supplier valueSupplier) + throws EvaluationException { + + TypedValue typedValue = valueSupplier.get(); + getValueRef(state).setValue(typedValue.getValue()); + return typedValue; } @Override diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/PropertyOrFieldReference.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/PropertyOrFieldReference.java index 476fb4c9b7..87feb58656 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/PropertyOrFieldReference.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/PropertyOrFieldReference.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2023 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. @@ -21,6 +21,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.function.Supplier; import org.springframework.asm.Label; import org.springframework.asm.MethodVisitor; @@ -46,6 +47,7 @@ import org.springframework.util.ReflectionUtils; * @author Andy Clement * @author Juergen Hoeller * @author Clark Duplichien + * @author Sam Brannen * @since 3.0 */ public class PropertyOrFieldReference extends SpelNodeImpl { @@ -147,8 +149,12 @@ public class PropertyOrFieldReference extends SpelNodeImpl { } @Override - public void setValue(ExpressionState state, @Nullable Object newValue) throws EvaluationException { - writeProperty(state.getActiveContextObject(), state.getEvaluationContext(), this.name, newValue); + public TypedValue setValueInternal(ExpressionState state, Supplier valueSupplier) + throws EvaluationException { + + TypedValue typedValue = valueSupplier.get(); + writeProperty(state.getActiveContextObject(), state.getEvaluationContext(), this.name, typedValue.getValue()); + return typedValue; } @Override diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/SpelNodeImpl.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/SpelNodeImpl.java index 3976fbf342..daff267578 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/SpelNodeImpl.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/SpelNodeImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2023 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. @@ -19,6 +19,7 @@ package org.springframework.expression.spel.ast; import java.lang.reflect.Constructor; import java.lang.reflect.Member; import java.lang.reflect.Method; +import java.util.function.Supplier; import org.springframework.asm.MethodVisitor; import org.springframework.asm.Opcodes; @@ -40,6 +41,7 @@ import org.springframework.util.ObjectUtils; * * @author Andy Clement * @author Juergen Hoeller + * @author Sam Brannen * @since 3.0 */ public abstract class SpelNodeImpl implements SpelNode, Opcodes { @@ -64,7 +66,7 @@ public abstract class SpelNodeImpl implements SpelNode, Opcodes { *

The descriptor is like the bytecode form but is slightly easier to work with. * It does not include the trailing semicolon (for non array reference types). * Some examples: Ljava/lang/String, I, [I - */ + */ @Nullable protected volatile String exitTypeDescriptor; @@ -83,8 +85,8 @@ public abstract class SpelNodeImpl implements SpelNode, Opcodes { /** - * Return {@code true} if the next child is one of the specified classes. - */ + * Return {@code true} if the next child is one of the specified classes. + */ protected boolean nextChildIs(Class... classes) { if (this.parent != null) { SpelNodeImpl[] peers = this.parent.children; @@ -125,6 +127,28 @@ public abstract class SpelNodeImpl implements SpelNode, Opcodes { @Override public void setValue(ExpressionState expressionState, @Nullable Object newValue) throws EvaluationException { + setValueInternal(expressionState, () -> new TypedValue(newValue)); + } + + /** + * Evaluate the expression to a node and then set the new value created by the + * specified {@link Supplier} on that node. + *

For example, if the expression evaluates to a property reference, then the + * property will be set to the new value. + *

Favor this method over {@link #setValue(ExpressionState, Object)} when + * the value should be lazily computed. + *

By default, this method throws a {@link SpelEvaluationException}, + * effectively disabling this feature. Subclasses may override this method to + * provide an actual implementation. + * @param expressionState the current expression state (includes the context) + * @param valueSupplier a supplier of the new value + * @throws EvaluationException if any problem occurs evaluating the expression or + * setting the new value + * @since 5.2.24 + */ + public TypedValue setValueInternal(ExpressionState expressionState, Supplier valueSupplier) + throws EvaluationException { + throw new SpelEvaluationException(getStartPosition(), SpelMessage.SETVALUE_NOT_SUPPORTED, getClass()); } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/VariableReference.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/VariableReference.java index 769e4efedb..97dae78e90 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/VariableReference.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/VariableReference.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2023 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. @@ -17,9 +17,11 @@ package org.springframework.expression.spel.ast; import java.lang.reflect.Modifier; +import java.util.function.Supplier; import org.springframework.asm.MethodVisitor; import org.springframework.expression.EvaluationContext; +import org.springframework.expression.EvaluationException; import org.springframework.expression.TypedValue; import org.springframework.expression.spel.CodeFlow; import org.springframework.expression.spel.ExpressionState; @@ -27,10 +29,11 @@ import org.springframework.expression.spel.SpelEvaluationException; import org.springframework.lang.Nullable; /** - * Represents a variable reference, eg. #someVar. Note this is different to a *local* - * variable like $someVar + * Represents a variable reference — for example, {@code #someVar}. Note + * that this is different than a local variable like {@code $someVar}. * * @author Andy Clement + * @author Sam Brannen * @since 3.0 */ public class VariableReference extends SpelNodeImpl { @@ -53,14 +56,14 @@ public class VariableReference extends SpelNodeImpl { @Override public ValueRef getValueRef(ExpressionState state) throws SpelEvaluationException { if (this.name.equals(THIS)) { - return new ValueRef.TypedValueHolderValueRef(state.getActiveContextObject(),this); + return new ValueRef.TypedValueHolderValueRef(state.getActiveContextObject(), this); } if (this.name.equals(ROOT)) { - return new ValueRef.TypedValueHolderValueRef(state.getRootContextObject(),this); + return new ValueRef.TypedValueHolderValueRef(state.getRootContextObject(), this); } TypedValue result = state.lookupVariable(this.name); // a null value will mean either the value was null or the variable was not found - return new VariableRef(this.name,result,state.getEvaluationContext()); + return new VariableRef(this.name, result, state.getEvaluationContext()); } @Override @@ -90,8 +93,10 @@ public class VariableReference extends SpelNodeImpl { } @Override - public void setValue(ExpressionState state, @Nullable Object value) throws SpelEvaluationException { - state.setVariable(this.name, value); + public TypedValue setValueInternal(ExpressionState state, Supplier valueSupplier) + throws EvaluationException { + + return state.assignVariable(this.name, valueSupplier); } @Override diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/support/SimpleEvaluationContext.java b/spring-expression/src/main/java/org/springframework/expression/spel/support/SimpleEvaluationContext.java index d8826e3447..1168c9c91a 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/support/SimpleEvaluationContext.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/support/SimpleEvaluationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2023 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. @@ -21,6 +21,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.function.Supplier; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.TypeDescriptor; @@ -78,6 +79,7 @@ import org.springframework.lang.Nullable; * * @author Rossen Stoyanchev * @author Juergen Hoeller + * @author Sam Brannen * @since 4.3.15 * @see #forPropertyAccessors * @see #forReadOnlyDataBinding() @@ -200,6 +202,17 @@ public final class SimpleEvaluationContext implements EvaluationContext { return this.operatorOverloader; } + /** + * {@code SimpleEvaluationContext} does not support variable assignment within + * expressions. + * @throws SpelEvaluationException with {@link SpelMessage#VARIABLE_ASSIGNMENT_NOT_SUPPORTED} + * @since 5.2.24 + */ + @Override + public TypedValue assignVariable(String name, Supplier valueSupplier) { + throw new SpelEvaluationException(SpelMessage.VARIABLE_ASSIGNMENT_NOT_SUPPORTED, "#" + name); + } + @Override public void setVariable(String name, @Nullable Object value) { this.variables.put(name, value); diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java index 4a2f596ee2..f84a439bad 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java @@ -24,6 +24,8 @@ import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import org.springframework.core.convert.TypeDescriptor; import org.springframework.expression.AccessException; @@ -38,6 +40,7 @@ import org.springframework.expression.MethodResolver; import org.springframework.expression.ParseException; import org.springframework.expression.spel.standard.SpelExpression; import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.expression.spel.support.SimpleEvaluationContext; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.expression.spel.support.StandardTypeLocator; import org.springframework.expression.spel.testresources.TestPerson; @@ -367,9 +370,26 @@ public class EvaluationTests extends AbstractExpressionTests { } // assignment + @Test - public void testAssignmentToVariables01() { - evaluate("#var1='value1'", "value1", String.class); + void assignmentToVariableWithStandardEvaluationContext() { + evaluate("#var1 = 'value1'", "value1", String.class); + } + + @ParameterizedTest + @CsvSource(delimiterString = "->", value = { + "'#var1 = \"value1\"' -> #var1", + "'true ? #myVar = 4 : 0' -> #myVar" + }) + void assignmentToVariableWithSimpleEvaluationContext(String expression, String varName) { + EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build(); + Expression expr = parser.parseExpression(expression); + assertThatExceptionOfType(SpelEvaluationException.class) + .isThrownBy(() -> expr.getValue(context)) + .satisfies(ex -> { + assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.VARIABLE_ASSIGNMENT_NOT_SUPPORTED); + assertThat(ex.getInserts()).as("inserts").containsExactly(varName); + }); } @Test -- Gitee From a8bc09925df4ff1f0cc914d6b8cc43eb3f74d3ef Mon Sep 17 00:00:00 2001 From: Spring Builds Date: Thu, 13 Apr 2023 09:30:19 +0000 Subject: [PATCH 12/44] Next development version (v5.2.25.BUILD-SNAPSHOT) --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index a1db56f1a5..b286ab4353 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version=5.2.24.BUILD-SNAPSHOT +version=5.2.25.BUILD-SNAPSHOT org.gradle.jvmargs=-Xmx1536M org.gradle.caching=true org.gradle.parallel=true -- Gitee From 89a3d64adafb4fc716473ad2d21f7b76ad63db2a Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Wed, 10 May 2023 15:47:49 +0200 Subject: [PATCH 13/44] Reintroduce support for null SpEL expressions Closes gh-30464 --- .../standard/InternalSpelExpressionParser.java | 2 +- .../expression/spel/standard/SpelParserTests.java | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java b/spring-expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java index 23a09f7197..0d1a50472c 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java @@ -158,7 +158,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { } private void checkExpressionLength(String string) { - if (string.length() > MAX_EXPRESSION_LENGTH) { + if (string != null && string.length() > MAX_EXPRESSION_LENGTH) { throw new SpelEvaluationException(SpelMessage.MAX_EXPRESSION_LENGTH_EXCEEDED, MAX_EXPRESSION_LENGTH); } } diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/standard/SpelParserTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/standard/SpelParserTests.java index f2a37e55ed..787489d476 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/standard/SpelParserTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/standard/SpelParserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2023 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. @@ -21,7 +21,9 @@ import java.util.function.Consumer; import org.junit.jupiter.api.Test; import org.springframework.expression.EvaluationContext; +import org.springframework.expression.Expression; import org.springframework.expression.ExpressionException; +import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.SpelMessage; import org.springframework.expression.spel.SpelNode; import org.springframework.expression.spel.SpelParseException; @@ -35,9 +37,19 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; /** * @author Andy Clement * @author Juergen Hoeller + * @author Sam Brannen */ public class SpelParserTests { + @Test // gh-30464 + public void nullExpression() { + ExpressionParser parser = new SpelExpressionParser(); + String expression = null; + Expression expr = parser.parseExpression(expression); + Object result = expr.getValue(); + assertThat(result).isNull(); + } + @Test public void theMostBasic() { SpelExpressionParser parser = new SpelExpressionParser(); -- Gitee From 7c8fed7f3b173fd3c485ebb10f1e903675b83c4f Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Wed, 10 May 2023 11:20:07 +0200 Subject: [PATCH 14/44] Make maximum SpEL expression length configurable Closes gh-30452 --- .../spel/SpelParserConfiguration.java | 39 ++++++++++++++++++- .../InternalSpelExpressionParser.java | 14 +++---- .../spel/AbstractExpressionTests.java | 20 +++++++++- .../expression/spel/EvaluationTests.java | 20 ++++++++++ 4 files changed, 82 insertions(+), 11 deletions(-) diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/SpelParserConfiguration.java b/spring-expression/src/main/java/org/springframework/expression/spel/SpelParserConfiguration.java index fb1260570d..9e4ccfd68c 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/SpelParserConfiguration.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/SpelParserConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2023 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. @@ -25,11 +25,19 @@ import org.springframework.lang.Nullable; * @author Juergen Hoeller * @author Phillip Webb * @author Andy Clement + * @author Sam Brannen * @since 3.0 * @see org.springframework.expression.spel.standard.SpelExpressionParser#SpelExpressionParser(SpelParserConfiguration) */ public class SpelParserConfiguration { + /** + * Default maximum length permitted for a SpEL expression. + * @since 5.2.24 + */ + private static final int DEFAULT_MAX_EXPRESSION_LENGTH = 10_000; + + private static final SpelCompilerMode defaultCompilerMode; static { @@ -50,6 +58,8 @@ public class SpelParserConfiguration { private final int maximumAutoGrowSize; + private final int maximumExpressionLength; + /** * Create a new {@code SpelParserConfiguration} instance with default settings. @@ -98,11 +108,30 @@ public class SpelParserConfiguration { public SpelParserConfiguration(@Nullable SpelCompilerMode compilerMode, @Nullable ClassLoader compilerClassLoader, boolean autoGrowNullReferences, boolean autoGrowCollections, int maximumAutoGrowSize) { + this(compilerMode, compilerClassLoader, autoGrowNullReferences, autoGrowCollections, + maximumAutoGrowSize, DEFAULT_MAX_EXPRESSION_LENGTH); + } + + /** + * Create a new {@code SpelParserConfiguration} instance. + * @param compilerMode the compiler mode that parsers using this configuration object should use + * @param compilerClassLoader the ClassLoader to use as the basis for expression compilation + * @param autoGrowNullReferences if null references should automatically grow + * @param autoGrowCollections if collections should automatically grow + * @param maximumAutoGrowSize the maximum size that a collection can auto grow + * @param maximumExpressionLength the maximum length of a SpEL expression; + * must be a positive number + * @since 5.2.25 + */ + public SpelParserConfiguration(@Nullable SpelCompilerMode compilerMode, @Nullable ClassLoader compilerClassLoader, + boolean autoGrowNullReferences, boolean autoGrowCollections, int maximumAutoGrowSize, int maximumExpressionLength) { + this.compilerMode = (compilerMode != null ? compilerMode : defaultCompilerMode); this.compilerClassLoader = compilerClassLoader; this.autoGrowNullReferences = autoGrowNullReferences; this.autoGrowCollections = autoGrowCollections; this.maximumAutoGrowSize = maximumAutoGrowSize; + this.maximumExpressionLength = maximumExpressionLength; } @@ -142,4 +171,12 @@ public class SpelParserConfiguration { return this.maximumAutoGrowSize; } + /** + * Return the maximum number of characters that a SpEL expression can contain. + * @since 5.2.25 + */ + public int getMaximumExpressionLength() { + return this.maximumExpressionLength; + } + } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java b/spring-expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java index 0d1a50472c..062a82f12b 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java @@ -93,13 +93,6 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { private static final Pattern VALID_QUALIFIED_ID_PATTERN = Pattern.compile("[\\p{L}\\p{N}_$]+"); - /** - * Maximum length permitted for a SpEL expression. - * @since 5.2.24 - */ - private static final int MAX_EXPRESSION_LENGTH = 10_000; - - private final SpelParserConfiguration configuration; // For rules that build nodes, they are stacked here for return @@ -158,8 +151,11 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { } private void checkExpressionLength(String string) { - if (string != null && string.length() > MAX_EXPRESSION_LENGTH) { - throw new SpelEvaluationException(SpelMessage.MAX_EXPRESSION_LENGTH_EXCEEDED, MAX_EXPRESSION_LENGTH); + if (string != null) { + int maxLength = this.configuration.getMaximumExpressionLength(); + if (string.length() > maxLength) { + throw new SpelEvaluationException(SpelMessage.MAX_EXPRESSION_LENGTH_EXCEEDED, maxLength); + } } } diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/AbstractExpressionTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/AbstractExpressionTests.java index 7a682dbd4e..eade073745 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/AbstractExpressionTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/AbstractExpressionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2023 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. @@ -164,6 +164,24 @@ public abstract class AbstractExpressionTests { */ protected void evaluateAndCheckError(String expression, Class expectedReturnType, SpelMessage expectedMessage, Object... otherProperties) { + + evaluateAndCheckError(this.parser, expression, expectedReturnType, expectedMessage, otherProperties); + } + + /** + * Evaluate the specified expression and ensure the expected message comes out. + * The message may have inserts and they will be checked if otherProperties is specified. + * The first entry in otherProperties should always be the position. + * @param parser the expression parser to use + * @param expression the expression to evaluate + * @param expectedReturnType ask the expression return value to be of this type if possible + * ({@code null} indicates don't ask for conversion) + * @param expectedMessage the expected message + * @param otherProperties the expected inserts within the message + */ + protected void evaluateAndCheckError(ExpressionParser parser, String expression, Class expectedReturnType, SpelMessage expectedMessage, + Object... otherProperties) { + assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> { Expression expr = parser.parseExpression(expression); assertThat(expr).as("expression").isNotNull(); diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java index f84a439bad..1b0bc6bf14 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java @@ -76,6 +76,26 @@ public class EvaluationTests extends AbstractExpressionTests { evaluateAndCheckError(expression, String.class, SpelMessage.MAX_EXPRESSION_LENGTH_EXCEEDED); } + @Test + void maxExpressionLengthIsConfigurable() { + int maximumExpressionLength = 20_000; + + String expression = String.format("'%s'", repeat("Y", 19_998)); + assertThat(expression).hasSize(maximumExpressionLength); + + SpelParserConfiguration configuration = + new SpelParserConfiguration(null, null, false, false, 0, maximumExpressionLength); + ExpressionParser parser = new SpelExpressionParser(configuration); + + Expression expr = parser.parseExpression(expression); + String result = expr.getValue(String.class); + assertThat(result).hasSize(19_998); + + expression = String.format("'%s'", repeat("Y", 25_000)); + assertThat(expression).hasSize(25_002); + evaluateAndCheckError(parser, expression, String.class, SpelMessage.MAX_EXPRESSION_LENGTH_EXCEEDED); + } + @Test public void testCreateListsOnAttemptToIndexNull01() throws EvaluationException, ParseException { ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); -- Gitee From 921635b3bce7971be11a60e5cf33ba57dc6585d6 Mon Sep 17 00:00:00 2001 From: rstoyanchev Date: Tue, 23 May 2023 15:15:42 +0100 Subject: [PATCH 15/44] Add ignore rule for cached-antora-playbook.yml In case of checking out the 5.3.x branch after 6.0.x or main --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 3f904904f7..ac42ede106 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,5 @@ out test-output atlassian-ide-plugin.xml .gradletasknamecache + +cached-antora-playbook.yml -- Gitee From ac82b5cd36ae067afec02c2707d912e87311356e Mon Sep 17 00:00:00 2001 From: rstoyanchev Date: Tue, 11 Jul 2023 12:17:47 +0100 Subject: [PATCH 16/44] Encapsulate full path initialization --- .../web/util/pattern/PathPatternParser.java | 14 +++++++++++++- .../function/server/RequestPredicates.java | 9 ++++----- .../handler/AbstractUrlHandlerMapping.java | 19 +++++-------------- .../resource/ResourceUrlProvider.java | 18 ++++-------------- .../result/method/RequestMappingInfo.java | 10 ++++------ .../servlet/function/RequestPredicates.java | 17 +++++++++-------- .../handler/HandlerMappingIntrospector.java | 2 +- .../condition/PatternsRequestCondition.java | 7 +++---- .../annotation/MvcUriComponentsBuilder.java | 7 +++---- 9 files changed, 46 insertions(+), 57 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/util/pattern/PathPatternParser.java b/spring-web/src/main/java/org/springframework/web/util/pattern/PathPatternParser.java index f7122a5318..c7be4075ac 100644 --- a/spring-web/src/main/java/org/springframework/web/util/pattern/PathPatternParser.java +++ b/spring-web/src/main/java/org/springframework/web/util/pattern/PathPatternParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2023 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. @@ -20,6 +20,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.http.server.PathContainer; +import org.springframework.util.StringUtils; /** * Parser for URI path patterns producing {@link PathPattern} instances that can @@ -103,6 +104,17 @@ public class PathPatternParser { } + /** + * Prepare the given pattern for use in matching to full URL paths. + *

By default, prepend a leading slash if needed for non-empty patterns. + * @param pattern the pattern to initialize + * @return the updated pattern + * @since 5.2.25 + */ + public String initFullPathPattern(String pattern) { + return (StringUtils.hasLength(pattern) && !pattern.startsWith("/") ? "/" + pattern : pattern); + } + /** * Process the path pattern content, a character at a time, breaking it into * path elements around separator boundaries and verifying the structure at each diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/RequestPredicates.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/RequestPredicates.java index 006c8fea46..b587686a67 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/RequestPredicates.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/RequestPredicates.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2023 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. @@ -109,10 +109,9 @@ public abstract class RequestPredicates { */ public static RequestPredicate path(String pattern) { Assert.notNull(pattern, "'pattern' must not be null"); - if (!pattern.isEmpty() && !pattern.startsWith("/")) { - pattern = "/" + pattern; - } - return pathPredicates(PathPatternParser.defaultInstance).apply(pattern); + PathPatternParser parser = PathPatternParser.defaultInstance; + pattern = parser.initFullPathPattern(pattern); + return pathPredicates(parser).apply(pattern); } /** diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/handler/AbstractUrlHandlerMapping.java b/spring-webflux/src/main/java/org/springframework/web/reactive/handler/AbstractUrlHandlerMapping.java index 10296b8d4b..9c7a6bf8cb 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/handler/AbstractUrlHandlerMapping.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/handler/AbstractUrlHandlerMapping.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2023 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. @@ -28,9 +28,9 @@ import org.springframework.beans.BeansException; import org.springframework.http.server.PathContainer; import org.springframework.lang.Nullable; import org.springframework.util.Assert; -import org.springframework.util.StringUtils; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.util.pattern.PathPattern; +import org.springframework.web.util.pattern.PathPatternParser; /** * Abstract base class for URL-mapped @@ -185,8 +185,9 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping { Object resolvedHandler = handler; // Parse path pattern - urlPath = prependLeadingSlash(urlPath); - PathPattern pattern = getPathPatternParser().parse(urlPath); + PathPatternParser parser = getPathPatternParser(); + urlPath = parser.initFullPathPattern(urlPath); + PathPattern pattern = parser.parse(urlPath); if (this.handlerMap.containsKey(pattern)) { Object existingHandler = this.handlerMap.get(pattern); if (existingHandler != null && existingHandler != resolvedHandler) { @@ -215,14 +216,4 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping { return (handler instanceof String ? "'" + handler + "'" : handler.toString()); } - - private static String prependLeadingSlash(String pattern) { - if (StringUtils.hasLength(pattern) && !pattern.startsWith("/")) { - return "/" + pattern; - } - else { - return pattern; - } - } - } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceUrlProvider.java b/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceUrlProvider.java index b78d853200..211b5da503 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceUrlProvider.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceUrlProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2023 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. @@ -35,7 +35,6 @@ import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.http.server.PathContainer; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.lang.Nullable; -import org.springframework.util.StringUtils; import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.util.pattern.PathPattern; @@ -86,8 +85,9 @@ public class ResourceUrlProvider implements ApplicationListener handlerMap) { this.handlerMap.clear(); handlerMap.forEach((rawPattern, resourceWebHandler) -> { - rawPattern = prependLeadingSlash(rawPattern); - PathPattern pattern = PathPatternParser.defaultInstance.parse(rawPattern); + PathPatternParser parser = PathPatternParser.defaultInstance; + rawPattern = parser.initFullPathPattern(rawPattern); + PathPattern pattern = parser.parse(rawPattern); this.handlerMap.put(pattern, resourceWebHandler); }); } @@ -173,14 +173,4 @@ public class ResourceUrlProvider implements ApplicationListener result = new ArrayList<>(patterns.length); - for (String path : patterns) { - if (StringUtils.hasText(path) && !path.startsWith("/")) { - path = "/" + path; - } - result.add(parser.parse(path)); + for (String pattern : patterns) { + pattern = parser.initFullPathPattern(pattern); + result.add(parser.parse(pattern)); } return result; } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/function/RequestPredicates.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/function/RequestPredicates.java index 342074a816..3f73e59bd0 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/function/RequestPredicates.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/function/RequestPredicates.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2023 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. @@ -107,10 +107,9 @@ public abstract class RequestPredicates { */ public static RequestPredicate path(String pattern) { Assert.notNull(pattern, "'pattern' must not be null"); - if (!pattern.isEmpty() && !pattern.startsWith("/")) { - pattern = "/" + pattern; - } - return pathPredicates(PathPatternParser.defaultInstance).apply(pattern); + PathPatternParser parser = PathPatternParser.defaultInstance; + pattern = parser.initFullPathPattern(pattern); + return pathPredicates(parser).apply(pattern); } /** @@ -333,14 +332,14 @@ public abstract class RequestPredicates { void method(Set methods); /** - * Receive notification of an path predicate. + * Receive notification of a path predicate. * @param pattern the path pattern that makes up the predicate * @see RequestPredicates#path(String) */ void path(String pattern); /** - * Receive notification of an path extension predicate. + * Receive notification of a path extension predicate. * @param extension the path extension that makes up the predicate * @see RequestPredicates#pathExtension(String) */ @@ -426,11 +425,11 @@ public abstract class RequestPredicates { void unknown(RequestPredicate predicate); } + private static class HttpMethodPredicate implements RequestPredicate { private final Set httpMethods; - public HttpMethodPredicate(HttpMethod httpMethod) { Assert.notNull(httpMethod, "HttpMethod must not be null"); this.httpMethods = EnumSet.of(httpMethod); @@ -641,12 +640,14 @@ public abstract class RequestPredicates { } } + private static class PathExtensionPredicate implements RequestPredicate { private final Predicate extensionPredicate; @Nullable private final String extension; + public PathExtensionPredicate(Predicate extensionPredicate) { Assert.notNull(extensionPredicate, "Predicate must not be null"); this.extensionPredicate = extensionPredicate; diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/HandlerMappingIntrospector.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/HandlerMappingIntrospector.java index 03ae39abdf..9421de0d80 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/HandlerMappingIntrospector.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/HandlerMappingIntrospector.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2023 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. diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/PatternsRequestCondition.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/PatternsRequestCondition.java index 4062a05340..8d23ab7b8d 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/PatternsRequestCondition.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/PatternsRequestCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2023 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. @@ -34,6 +34,7 @@ import org.springframework.util.PathMatcher; import org.springframework.util.StringUtils; import org.springframework.web.servlet.HandlerMapping; import org.springframework.web.util.UrlPathHelper; +import org.springframework.web.util.pattern.PathPatternParser; /** * A logical disjunction (' || ') request condition that matches a request @@ -142,9 +143,7 @@ public class PatternsRequestCondition extends AbstractRequestCondition result = new LinkedHashSet<>(patterns.length); for (String pattern : patterns) { - if (StringUtils.hasLength(pattern) && !pattern.startsWith("/")) { - pattern = "/" + pattern; - } + pattern = PathPatternParser.defaultInstance.initFullPathPattern(pattern); result.add(pattern); } return result; diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.java index a437295c92..efffafe865 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2023 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. @@ -65,6 +65,7 @@ import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import org.springframework.web.util.UriComponentsBuilder; +import org.springframework.web.util.pattern.PathPatternParser; /** * Creates instances of {@link org.springframework.web.util.UriComponentsBuilder} @@ -545,9 +546,7 @@ public class MvcUriComponentsBuilder { String typePath = getClassMapping(controllerType); String methodPath = getMethodMapping(method); String path = pathMatcher.combine(typePath, methodPath); - if (StringUtils.hasLength(path) && !path.startsWith("/")) { - path = "/" + path; - } + path = PathPatternParser.defaultInstance.initFullPathPattern(path); builder.path(path); return applyContributors(builder, method, args); -- Gitee From 2e72269eb0e7212fbbfcc7e16be990a2ee64f2c0 Mon Sep 17 00:00:00 2001 From: rstoyanchev Date: Tue, 11 Jul 2023 14:00:17 +0100 Subject: [PATCH 17/44] Update links to asciidoc resources Essentially a backport of 572bbe and 7dae3a from 5.3.x --- gradle/docs.gradle | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/gradle/docs.gradle b/gradle/docs.gradle index 2700582731..2086366517 100644 --- a/gradle/docs.gradle +++ b/gradle/docs.gradle @@ -3,7 +3,7 @@ configurations { } dependencies { - asciidoctorExt("io.spring.asciidoctor:spring-asciidoctor-extensions-block-switch:0.4.0.RELEASE") + asciidoctorExt("io.spring.asciidoctor:spring-asciidoctor-extensions-block-switch:0.6.1") } repositories { @@ -107,9 +107,8 @@ dokka { } task downloadResources(type: Download) { - def version = "0.2.1.RELEASE" - src "https://repo.spring.io/release/io/spring/docresources/" + - "spring-doc-resources/$version/spring-doc-resources-${version}.zip" + src "https://repo.spring.io/artifactory/snapshot/io/spring/docresources/" + + "spring-doc-resources/0.2.6-SNAPSHOT/spring-doc-resources-0.2.6-20210308.231804-2.zip" dest project.file("$buildDir/docs/spring-doc-resources.zip") onlyIfModified true useETag "all" -- Gitee From ca01ee05f7928258694a0dc490f83143d959cd31 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Wed, 12 Jul 2023 08:22:34 +0200 Subject: [PATCH 18/44] Update JDK and OS versions in CI image --- ci/images/ci-image/Dockerfile | 2 +- ci/images/get-jdk-url.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ci/images/ci-image/Dockerfile b/ci/images/ci-image/Dockerfile index 157d7f28b8..e208a98191 100644 --- a/ci/images/ci-image/Dockerfile +++ b/ci/images/ci-image/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:focal-20220302 +FROM ubuntu:jammy-20230624 ADD setup.sh /setup.sh ADD get-jdk-url.sh /get-jdk-url.sh diff --git a/ci/images/get-jdk-url.sh b/ci/images/get-jdk-url.sh index fca99d595e..aa55e129d5 100755 --- a/ci/images/get-jdk-url.sh +++ b/ci/images/get-jdk-url.sh @@ -3,10 +3,10 @@ set -e case "$1" in java8) - echo "https://github.com/adoptium/temurin8-binaries/releases/download/jdk8u322-b06/OpenJDK8U-jdk_x64_linux_hotspot_8u322b06.tar.gz" + echo "https://github.com/bell-sw/Liberica/releases/download/8u372+7/bellsoft-jdk8u372+7-linux-amd64.tar.gz" ;; java11) - echo "https://github.com/adoptium/temurin11-binaries/releases/download/jdk-11.0.14.1%2B1/OpenJDK11U-jdk_x64_linux_hotspot_11.0.14.1_1.tar.gz" + echo "https://github.com/bell-sw/Liberica/releases/download/11.0.19%2B7/bellsoft-jdk11.0.19+7-linux-amd64.tar.gz" ;; *) echo $"Unknown java version" -- Gitee From c206866fedb1e381102748a2ab74c555b803310b Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Wed, 12 Jul 2023 08:23:28 +0200 Subject: [PATCH 19/44] Use concourse-release-script docker image in CI --- ci/pipeline.yml | 3 --- ci/scripts/promote-version.sh | 4 ++-- ci/tasks/promote-version.yml | 7 +++++++ 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/ci/pipeline.yml b/ci/pipeline.yml index 510a0f1ca9..9211871608 100644 --- a/ci/pipeline.yml +++ b/ci/pipeline.yml @@ -277,7 +277,6 @@ jobs: download_artifacts: false save_build_info: true - task: promote - image: ci-image file: git-repo/ci/tasks/promote-version.yml params: RELEASE_TYPE: M @@ -322,7 +321,6 @@ jobs: download_artifacts: false save_build_info: true - task: promote - image: ci-image file: git-repo/ci/tasks/promote-version.yml params: RELEASE_TYPE: RC @@ -367,7 +365,6 @@ jobs: download_artifacts: true save_build_info: true - task: promote - image: ci-image file: git-repo/ci/tasks/promote-version.yml params: RELEASE_TYPE: RELEASE diff --git a/ci/scripts/promote-version.sh b/ci/scripts/promote-version.sh index 44c5ff626f..2b932f5f20 100755 --- a/ci/scripts/promote-version.sh +++ b/ci/scripts/promote-version.sh @@ -6,11 +6,11 @@ CONFIG_DIR=git-repo/ci/config version=$( cat artifactory-repo/build-info.json | jq -r '.buildInfo.modules[0].id' | sed 's/.*:.*:\(.*\)/\1/' ) export BUILD_INFO_LOCATION=$(pwd)/artifactory-repo/build-info.json -java -jar /opt/concourse-release-scripts.jar \ +java -jar /concourse-release-scripts.jar \ --spring.config.location=${CONFIG_DIR}/release-scripts.yml \ publishToCentral $RELEASE_TYPE $BUILD_INFO_LOCATION artifactory-repo || { exit 1; } -java -jar /opt/concourse-release-scripts.jar \ +java -jar /concourse-release-scripts.jar \ --spring.config.location=${CONFIG_DIR}/release-scripts.yml \ promote $RELEASE_TYPE $BUILD_INFO_LOCATION || { exit 1; } diff --git a/ci/tasks/promote-version.yml b/ci/tasks/promote-version.yml index abdd8fed5c..2a59aea38d 100644 --- a/ci/tasks/promote-version.yml +++ b/ci/tasks/promote-version.yml @@ -1,5 +1,12 @@ --- platform: linux +image_resource: + type: registry-image + source: + repository: springio/concourse-release-scripts + tag: '0.3.4' + username: ((docker-hub-username)) + password: ((docker-hub-password)) inputs: - name: git-repo - name: artifactory-repo -- Gitee From e6d5b385f6861352024919643a82c2932ce16ccb Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Wed, 12 Jul 2023 08:30:00 +0200 Subject: [PATCH 20/44] Use docker credentials for fetching changelog image This commit updates the CI pipeline to use the configured docker credentials for fetching the changelog-generator CI image for the relevant task. --- ci/tasks/generate-changelog.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ci/tasks/generate-changelog.yml b/ci/tasks/generate-changelog.yml index b3f40278c8..f4004743db 100755 --- a/ci/tasks/generate-changelog.yml +++ b/ci/tasks/generate-changelog.yml @@ -4,7 +4,9 @@ image_resource: type: registry-image source: repository: springio/github-changelog-generator - tag: '0.0.7' + tag: '0.0.8' + username: ((docker-hub-username)) + password: ((docker-hub-password)) inputs: - name: git-repo - name: artifactory-repo -- Gitee From a789e29a52d08bc998b02d2bfb589601ae24f99d Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Wed, 12 Jul 2023 11:57:12 +0200 Subject: [PATCH 21/44] Add "/framework-docs/build" to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index ac42ede106..08a19adb84 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,7 @@ buildSrc/build /spring-*/build /spring-core/kotlin-coroutines/build /framework-bom/build +/framework-docs/build /integration-tests/build /src/asciidoc/build target/ -- Gitee From b40e8e047c1c38d46dbc72110ea27cc29394fb2c Mon Sep 17 00:00:00 2001 From: Spring Builds Date: Thu, 13 Jul 2023 07:51:55 +0000 Subject: [PATCH 22/44] Next development version (v5.2.26.BUILD-SNAPSHOT) --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index b286ab4353..277573b546 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version=5.2.25.BUILD-SNAPSHOT +version=5.2.26.BUILD-SNAPSHOT org.gradle.jvmargs=-Xmx1536M org.gradle.caching=true org.gradle.parallel=true -- Gitee From a315a2cacd60c148458decf3b87ed492a5058d0a Mon Sep 17 00:00:00 2001 From: chensj Date: Mon, 4 Sep 2023 14:51:16 +0800 Subject: [PATCH 23/44] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=87=AA=E5=AE=9A?= =?UTF-8?q?=E4=B9=89=E4=BB=93=E5=BA=93=E5=9C=B0=E5=9D=80=E5=92=8C=E5=90=88?= =?UTF-8?q?=E5=B9=B6=E4=B9=8B=E5=89=8D=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 12 +++ .../support/DefaultListableBeanFactory.java | 87 ++++++------------- .../DefaultBeanDefinitionDocumentReader.java | 63 +++++++++++--- .../factory/xml/XmlBeanDefinitionReader.java | 42 +++++---- .../beans/factory/xml/XmlBeanFactory.java | 3 + 5 files changed, 114 insertions(+), 93 deletions(-) diff --git a/build.gradle b/build.gradle index 79d6cf76de..c46c4d2857 100644 --- a/build.gradle +++ b/build.gradle @@ -1,3 +1,15 @@ +buildscript { + repositories { + maven { url 'https://maven.aliyun.com/nexus/content/groups/public/' } + maven { url 'https://maven.aliyun.com/nexus/content/repositories/jcenter'} + maven { url 'https://maven.aliyun.com/repository/public'} + maven { url 'https://maven.aliyun.com/repository/central'} + maven { url "https://maven.aliyun.com/repository/spring-plugin" } + maven { url 'https://maven.aliyun.com/repository/gradle-plugin'} + gradlePluginPortal() + } +} + plugins { id 'io.spring.dependency-management' version '1.0.9.RELEASE' apply false id 'io.spring.ge.conventions' version '0.0.7' diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java index 52c5f67aef..25cdf0310a 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java @@ -16,75 +16,33 @@ package org.springframework.beans.factory.support; -import java.io.IOException; -import java.io.NotSerializableException; -import java.io.ObjectInputStream; -import java.io.ObjectStreamException; -import java.io.Serializable; +import org.springframework.beans.BeansException; +import org.springframework.beans.TypeConverter; +import org.springframework.beans.factory.*; +import org.springframework.beans.factory.config.*; +import org.springframework.core.OrderComparator; +import org.springframework.core.ResolvableType; +import org.springframework.core.annotation.MergedAnnotation; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; +import org.springframework.core.log.LogMessage; +import org.springframework.lang.Nullable; +import org.springframework.util.*; + +import javax.inject.Provider; +import java.io.*; import java.lang.annotation.Annotation; import java.lang.ref.Reference; import java.lang.ref.WeakReference; import java.lang.reflect.Method; import java.security.AccessController; import java.security.PrivilegedAction; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Comparator; -import java.util.IdentityHashMap; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; import java.util.function.Predicate; import java.util.stream.Stream; -import javax.inject.Provider; - -import org.springframework.beans.BeansException; -import org.springframework.beans.TypeConverter; -import org.springframework.beans.factory.BeanCreationException; -import org.springframework.beans.factory.BeanCurrentlyInCreationException; -import org.springframework.beans.factory.BeanDefinitionStoreException; -import org.springframework.beans.factory.BeanFactory; -import org.springframework.beans.factory.BeanFactoryAware; -import org.springframework.beans.factory.BeanFactoryUtils; -import org.springframework.beans.factory.BeanNotOfRequiredTypeException; -import org.springframework.beans.factory.CannotLoadBeanClassException; -import org.springframework.beans.factory.FactoryBean; -import org.springframework.beans.factory.InjectionPoint; -import org.springframework.beans.factory.NoSuchBeanDefinitionException; -import org.springframework.beans.factory.NoUniqueBeanDefinitionException; -import org.springframework.beans.factory.ObjectFactory; -import org.springframework.beans.factory.ObjectProvider; -import org.springframework.beans.factory.SmartFactoryBean; -import org.springframework.beans.factory.SmartInitializingSingleton; -import org.springframework.beans.factory.config.AutowireCapableBeanFactory; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.BeanDefinitionHolder; -import org.springframework.beans.factory.config.BeanPostProcessor; -import org.springframework.beans.factory.config.ConfigurableBeanFactory; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.config.DependencyDescriptor; -import org.springframework.beans.factory.config.NamedBeanHolder; -import org.springframework.core.OrderComparator; -import org.springframework.core.ResolvableType; -import org.springframework.core.annotation.MergedAnnotation; -import org.springframework.core.annotation.MergedAnnotations; -import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; -import org.springframework.core.log.LogMessage; -import org.springframework.lang.Nullable; -import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; -import org.springframework.util.CompositeIterator; -import org.springframework.util.ObjectUtils; -import org.springframework.util.StringUtils; - /** * Spring's default implementation of the {@link ConfigurableListableBeanFactory} * and {@link BeanDefinitionRegistry} interfaces: a full-fledged bean factory @@ -931,6 +889,11 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto if (beanDefinition instanceof AbstractBeanDefinition) { try { + /** + * bean注册前做最后一次校验,这个校验是在与之前加载的xml文件之间进行 + * 主要对于AbstractBeanDefinition属性中的methodOverrides进行校验 + * methodOverrides:是否与工厂方法并存或methodOverrides指定的方法不存在 + */ ((AbstractBeanDefinition) beanDefinition).validate(); } catch (BeanDefinitionValidationException ex) { @@ -938,12 +901,15 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto "Validation of bean definition failed", ex); } } - + // 从已定义bean中获取 BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName); + // 如果已经存在,则判断是否允许重写 if (existingDefinition != null) { + // 已经存在,并且不允许重写覆盖,则报错 if (!isAllowBeanDefinitionOverriding()) { throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition); } + // 判断bean定义的role else if (existingDefinition.getRole() < beanDefinition.getRole()) { // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE if (logger.isInfoEnabled()) { @@ -969,8 +935,10 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto this.beanDefinitionMap.put(beanName, beanDefinition); } else { + // 不存在beanName 先判断是否已经在创建阶段 if (hasBeanCreationStarted()) { // Cannot modify startup-time collection elements anymore (for stable iteration) + // beanDefinitionMap全局变量,存在并发情况 synchronized (this.beanDefinitionMap) { this.beanDefinitionMap.put(beanName, beanDefinition); List updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1); @@ -990,6 +958,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto } if (existingDefinition != null || containsSingleton(beanName)) { + // 重置所有beanName对应的缓存 resetBeanDefinition(beanName); } else if (isConfigurationFrozen()) { diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultBeanDefinitionDocumentReader.java b/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultBeanDefinitionDocumentReader.java index af5026dce3..ddd960e95c 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultBeanDefinitionDocumentReader.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultBeanDefinitionDocumentReader.java @@ -16,18 +16,8 @@ package org.springframework.beans.factory.xml; -import java.io.IOException; -import java.net.URISyntaxException; -import java.util.LinkedHashSet; -import java.util.Set; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.parsing.BeanComponentDefinition; @@ -38,8 +28,19 @@ import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ResourceUtils; import org.springframework.util.StringUtils; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.LinkedHashSet; +import java.util.Set; /** + * BeanDefinitionDocumentReader默认实现,可以读取符合spring-beans定义的bean信息 + * xml中的结构、元素和属性都是通过硬编码方式进行解析的 * Default implementation of the {@link BeanDefinitionDocumentReader} interface that * reads bean definitions according to the "spring-beans" DTD and XSD format * (Spring's default XML bean definition format). @@ -93,6 +94,7 @@ public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocume @Override public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; + // 注册bean doRegisterBeanDefinitions(doc.getDocumentElement()); } @@ -115,6 +117,7 @@ public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocume /** + * 开始准备解析 document中包含的bean * Register each bean definition within the given root {@code } element. */ @SuppressWarnings("deprecation") // for Environment.acceptsProfiles(String...) @@ -125,14 +128,19 @@ public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocume // the new (child) delegate with a reference to the parent for fallback purposes, // then ultimately reset this.delegate back to its original (parent) reference. // this behavior emulates a stack of delegates without actually necessitating one. + // 任何嵌套的都会导致该方法的递归使用。为了正确传播和保存 的default-*属性正确性 + // 请跟踪当前(父)委托,该委托可以为null + // 创建父类委托和子类 BeanDefinitionParserDelegate parent = this.delegate; this.delegate = createDelegate(getReaderContext(), root, parent); - + // 判断是否为默认命名空间 主要是否为了解析profile属性 if (this.delegate.isDefaultNamespace(root)) { + // 解析profile属性 String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); + // 判断是否接受profile // We cannot use Profiles.of(...) since profile expressions are not supported // in XML config. See SPR-12458 for details. if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { @@ -144,9 +152,11 @@ public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocume } } } - + // 解析前 preProcessXml(root); + // 解析 parseBeanDefinitions(root, this.delegate); + // 解析后 postProcessXml(root); this.delegate = parent; @@ -166,36 +176,49 @@ public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocume * @param root the DOM root element of the document */ protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { + // 判断是否为默认值命令空间 if (delegate.isDefaultNamespace(root)) { NodeList nl = root.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; + // 判断是否为默认值命令空间 if (delegate.isDefaultNamespace(ele)) { parseDefaultElement(ele, delegate); } else { + // 解析自定义元素 delegate.parseCustomElement(ele); } } } } else { + // 自定义明明空间 delegate.parseCustomElement(root); } } + /** + * 解析使用spring标签注册的bean元素 + * @param ele 元素 + * @param delegate 代理 + */ private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { + // 解析import if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { importBeanDefinitionResource(ele); } + // 解析alias else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { processAliasRegistration(ele); } + // 解析bean else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { processBeanDefinition(ele, delegate); } + // 解析 递归 else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // recurse doRegisterBeanDefinitions(ele); @@ -207,18 +230,22 @@ public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocume * from the given resource into the bean factory. */ protected void importBeanDefinitionResource(Element ele) { + // 解析resource属性 String location = ele.getAttribute(RESOURCE_ATTRIBUTE); + // 判断是否为空 if (!StringUtils.hasText(location)) { getReaderContext().error("Resource location must not be empty", ele); return; } // Resolve system properties: e.g. "${user.dir}" + // 将系统属性参数替换 location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location); Set actualResources = new LinkedHashSet<>(4); // Discover whether the location is an absolute or relative URI + // 判断location是否是绝对uri,还是相对的uri boolean absoluteLocation = false; try { absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute(); @@ -229,6 +256,7 @@ public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocume } // Absolute or relative? + // 如果是绝对的uri,则直接根据地址加载资源文件 if (absoluteLocation) { try { int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources); @@ -243,6 +271,7 @@ public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocume } else { // No URL -> considering resource location as relative to the current file. + // 如果是相对的地址,则需要根据相对地址计算出来绝对的地址,加载绝对地址下的资源文件 try { int importCount; Resource relativeResource = getReaderContext().getResource().createRelative(location); @@ -251,6 +280,7 @@ public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocume actualResources.add(relativeResource); } else { + // 如果解析不成功,则使用默认的ResourcePatternResolver解析 String baseLocation = getReaderContext().getResource().getURL().toString(); importCount = getReaderContext().getReader().loadBeanDefinitions( StringUtils.applyRelativePath(baseLocation, location), actualResources); @@ -268,6 +298,7 @@ public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocume } } Resource[] actResArray = actualResources.toArray(new Resource[0]); + // 解析完成后,触发监听器做后续的处理 getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele)); } @@ -278,36 +309,44 @@ public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocume String name = ele.getAttribute(NAME_ATTRIBUTE); String alias = ele.getAttribute(ALIAS_ATTRIBUTE); boolean valid = true; + // 判断别名的name属性 if (!StringUtils.hasText(name)) { getReaderContext().error("Name must not be empty", ele); valid = false; } + // 判断别名的alias属性 if (!StringUtils.hasText(alias)) { getReaderContext().error("Alias must not be empty", ele); valid = false; } if (valid) { try { + // 注册别名 getReaderContext().getRegistry().registerAlias(name, alias); } catch (Exception ex) { getReaderContext().error("Failed to register alias '" + alias + "' for bean with name '" + name + "'", ele, ex); } + // 别名注册后,通知响应的监听器做后续的处理 getReaderContext().fireAliasRegistered(name, alias, extractSource(ele)); } } /** + * 解析给定的bean元素,并进行注册 * Process the given bean element, parsing the bean definition * and registering it with the registry. */ protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { + // 解析bean定义到 BeanDefinitionHolder BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { + // 装饰bean 主要是用于自定义标签的解析中 bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // Register the final decorated instance. + // 注册bean BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/xml/XmlBeanDefinitionReader.java b/spring-beans/src/main/java/org/springframework/beans/factory/xml/XmlBeanDefinitionReader.java index 589208a4d3..1f3fe71e68 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/xml/XmlBeanDefinitionReader.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/xml/XmlBeanDefinitionReader.java @@ -16,28 +16,9 @@ package org.springframework.beans.factory.xml; -import java.io.IOException; -import java.io.InputStream; -import java.util.HashSet; -import java.util.Set; - -import javax.xml.parsers.ParserConfigurationException; - -import org.w3c.dom.Document; -import org.xml.sax.EntityResolver; -import org.xml.sax.ErrorHandler; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; -import org.xml.sax.SAXParseException; - import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.BeanDefinitionStoreException; -import org.springframework.beans.factory.parsing.EmptyReaderEventListener; -import org.springframework.beans.factory.parsing.FailFastProblemReporter; -import org.springframework.beans.factory.parsing.NullSourceExtractor; -import org.springframework.beans.factory.parsing.ProblemReporter; -import org.springframework.beans.factory.parsing.ReaderEventListener; -import org.springframework.beans.factory.parsing.SourceExtractor; +import org.springframework.beans.factory.parsing.*; import org.springframework.beans.factory.support.AbstractBeanDefinitionReader; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.core.Constants; @@ -50,6 +31,14 @@ import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.xml.SimpleSaxErrorHandler; import org.springframework.util.xml.XmlValidationModeDetector; +import org.w3c.dom.Document; +import org.xml.sax.*; + +import javax.xml.parsers.ParserConfigurationException; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashSet; +import java.util.Set; /** * Bean definition reader for XML bean definitions. @@ -195,6 +184,7 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { } /** + * 判断是否包含自定义命名空间 * Return whether or not the XML parser should be XML namespace aware. */ public boolean isNamespaceAware() { @@ -322,19 +312,21 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { if (logger.isTraceEnabled()) { logger.trace("Loading XML bean definitions from " + encodedResource); } - + // 获取当前已经资源文件 Set currentResources = this.resourcesCurrentlyBeingLoaded.get(); if (!currentResources.add(encodedResource)) { throw new BeanDefinitionStoreException( "Detected cyclic loading of " + encodedResource + " - check your import definitions!"); } - + // 判断是否已经加载过当前这个资源 try (InputStream inputStream = encodedResource.getResource().getInputStream()) { + // 转换输入流到org.xml.sax.InputSource InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } + // 加载、读取、注册bean return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } catch (IOException ex) { @@ -387,7 +379,9 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { throws BeanDefinitionStoreException { try { + // 获取资源的文档信息 Document doc = doLoadDocument(inputSource, resource); + // 解析和注册 int count = registerBeanDefinitions(doc, resource); if (logger.isDebugEnabled()) { logger.debug("Loaded " + count + " bean definitions from " + resource); @@ -434,6 +428,7 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { } /** + * 获取xml的校验类型 dtd xsd * Determine the validation mode for the specified {@link Resource}. * If no explicit validation mode has been configured, then the validation * mode gets {@link #detectValidationMode detected} from the given resource. @@ -506,8 +501,11 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { * @see BeanDefinitionDocumentReader#registerBeanDefinitions */ public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { + // 获取默认的读取器 BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); + // 获取已经读取bean总数 int countBefore = getRegistry().getBeanDefinitionCount(); + // 读取注册bean documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore; } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/xml/XmlBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/xml/XmlBeanFactory.java index b762a41810..13836b2ebb 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/xml/XmlBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/xml/XmlBeanFactory.java @@ -75,7 +75,10 @@ public class XmlBeanFactory extends DefaultListableBeanFactory { * @throws BeansException in case of loading or parsing errors */ public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException { + // 初始化父级BeanFactory + // 指定派出扫描类 在AbstractAutowireCapableBeanFactory中配置 super(parentBeanFactory); + // 读取、加载和注册bean信息 this.reader.loadBeanDefinitions(resource); } -- Gitee From 9222c66470afd2a0553d4aa8bd646043928005d7 Mon Sep 17 00:00:00 2001 From: chensj Date: Mon, 11 Sep 2023 09:29:57 +0800 Subject: [PATCH 24/44] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xml/BeanDefinitionParserDelegate.java | 108 ++++++++++-------- .../DefaultBeanDefinitionDocumentReader.java | 2 +- 2 files changed, 59 insertions(+), 51 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/xml/BeanDefinitionParserDelegate.java b/spring-beans/src/main/java/org/springframework/beans/factory/xml/BeanDefinitionParserDelegate.java index 6be311eb1e..dc604c8af4 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/xml/BeanDefinitionParserDelegate.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/xml/BeanDefinitionParserDelegate.java @@ -16,56 +16,23 @@ package org.springframework.beans.factory.xml; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.Set; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.w3c.dom.Element; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - import org.springframework.beans.BeanMetadataAttribute; import org.springframework.beans.BeanMetadataAttributeAccessor; import org.springframework.beans.PropertyValue; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.BeanDefinitionHolder; -import org.springframework.beans.factory.config.ConstructorArgumentValues; -import org.springframework.beans.factory.config.RuntimeBeanNameReference; -import org.springframework.beans.factory.config.RuntimeBeanReference; -import org.springframework.beans.factory.config.TypedStringValue; -import org.springframework.beans.factory.parsing.BeanEntry; -import org.springframework.beans.factory.parsing.ConstructorArgumentEntry; -import org.springframework.beans.factory.parsing.ParseState; -import org.springframework.beans.factory.parsing.PropertyEntry; -import org.springframework.beans.factory.parsing.QualifierEntry; -import org.springframework.beans.factory.support.AbstractBeanDefinition; -import org.springframework.beans.factory.support.AutowireCandidateQualifier; -import org.springframework.beans.factory.support.BeanDefinitionDefaults; -import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; -import org.springframework.beans.factory.support.LookupOverride; -import org.springframework.beans.factory.support.ManagedArray; -import org.springframework.beans.factory.support.ManagedList; -import org.springframework.beans.factory.support.ManagedMap; -import org.springframework.beans.factory.support.ManagedProperties; -import org.springframework.beans.factory.support.ManagedSet; -import org.springframework.beans.factory.support.MethodOverrides; -import org.springframework.beans.factory.support.ReplaceOverride; +import org.springframework.beans.factory.config.*; +import org.springframework.beans.factory.parsing.*; +import org.springframework.beans.factory.support.*; import org.springframework.lang.Nullable; -import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; -import org.springframework.util.CollectionUtils; -import org.springframework.util.ObjectUtils; -import org.springframework.util.PatternMatchUtils; -import org.springframework.util.StringUtils; +import org.springframework.util.*; import org.springframework.util.xml.DomUtils; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import java.util.*; /** * Stateful delegate class used to parse XML bean definitions. @@ -412,9 +379,12 @@ public class BeanDefinitionParserDelegate { */ @Nullable public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) { + // 解析id String id = ele.getAttribute(ID_ATTRIBUTE); + // 解析name String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); + // 解析别名 List aliases = new ArrayList<>(); if (StringUtils.hasLength(nameAttr)) { String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS); @@ -422,6 +392,7 @@ public class BeanDefinitionParserDelegate { } String beanName = id; + // 判断beanName 和别名 if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) { beanName = aliases.remove(0); if (logger.isTraceEnabled()) { @@ -431,13 +402,16 @@ public class BeanDefinitionParserDelegate { } if (containingBean == null) { + // 判断名称是否唯一 checkNameUniqueness(beanName, aliases, ele); } - + // 硬解析bean信息 AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); if (beanDefinition != null) { + // beanName是否为空 if (!StringUtils.hasText(beanName)) { try { + // 如果不存在beanName,那么根据spring中提供的命名规则为当前bean生成对应的beanName if (containingBean != null) { beanName = BeanDefinitionReaderUtils.generateBeanName( beanDefinition, this.readerContext.getRegistry(), true); @@ -502,32 +476,42 @@ public class BeanDefinitionParserDelegate { this.parseState.push(new BeanEntry(beanName)); + // 解析class属性 String className = null; if (ele.hasAttribute(CLASS_ATTRIBUTE)) { className = ele.getAttribute(CLASS_ATTRIBUTE).trim(); } + // 解析 parent属性 String parent = null; if (ele.hasAttribute(PARENT_ATTRIBUTE)) { parent = ele.getAttribute(PARENT_ATTRIBUTE); } try { + // 根据上面解析的信息创建抽象的beanDefinition + // 创建用于承载属性的AbstractBeanDefinition类型的GenericBeanDefinition AbstractBeanDefinition bd = createBeanDefinition(className, parent); - + // 解析bean标签属性,硬编码解析 parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); + // 解析描述信息 bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT)); - + // 解析meta子标签 parseMetaElements(ele, bd); + // 解析look-up子标签 parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); + // 解析replace-method方法 parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); - + // 解析构造器注入属性 parseConstructorArgElements(ele, bd); + // 解析property注入 parsePropertyElements(ele, bd); + // 解析qualifier注入 parseQualifierElements(ele, bd); - + // 指定资源文件 bd.setResource(this.readerContext.getResource()); + // 设置资源信息 用于依赖时候使用 bd.setSource(extractSource(ele)); - + // 解析完成 return bd; } catch (ClassNotFoundException ex) { @@ -547,6 +531,7 @@ public class BeanDefinitionParserDelegate { } /** + * 解析bean标签上面的属性信息 * Apply the attributes of the given bean element to the given bean * definition. * @param ele bean declaration element * @param beanName bean name @@ -556,6 +541,7 @@ public class BeanDefinitionParserDelegate { public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName, @Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) { + // 解析scope属性 if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) { error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele); } @@ -567,24 +553,29 @@ public class BeanDefinitionParserDelegate { bd.setScope(containingBean.getScope()); } + // 解析abstract属性 if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) { bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE))); } + // 解析lazy-init属性 String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE); if (isDefaultValue(lazyInit)) { lazyInit = this.defaults.getLazyInit(); } bd.setLazyInit(TRUE_VALUE.equals(lazyInit)); + // autowire属性 String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE); bd.setAutowireMode(getAutowireMode(autowire)); + // 解析depends-on属性 if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) { String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE); bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS)); } + // 解析autowire-candidate属性 String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE); if (isDefaultValue(autowireCandidate)) { String candidatePattern = this.defaults.getAutowireCandidates(); @@ -597,10 +588,12 @@ public class BeanDefinitionParserDelegate { bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate)); } + // 解析primary属性 if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) { bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE))); } + // 解析init-method方法 if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) { String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE); bd.setInitMethodName(initMethodName); @@ -610,6 +603,7 @@ public class BeanDefinitionParserDelegate { bd.setEnforceInitMethod(false); } + // 解析destory-method方法 if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) { String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE); bd.setDestroyMethodName(destroyMethodName); @@ -619,9 +613,11 @@ public class BeanDefinitionParserDelegate { bd.setEnforceDestroyMethod(false); } +// 解析工厂方法属性 if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) { bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE)); } +// 解析工厂bean属性 if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) { bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE)); } @@ -1372,6 +1368,7 @@ public class BeanDefinitionParserDelegate { } /** + * 自定义命名空间元素解析 * Parse a custom element (outside of the default namespace). * @param ele the element to parse * @param containingBd the containing bean definition (if any) @@ -1379,19 +1376,24 @@ public class BeanDefinitionParserDelegate { */ @Nullable public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) { + // 获取对应的命名空间 String namespaceUri = getNamespaceURI(ele); if (namespaceUri == null) { return null; } + // 获取命名空间对应的解析类 + // 这里的NamespaceHandlerResolver是DefaultNamespaceHandlerResolver NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); if (handler == null) { error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele); return null; } + // 使用指定的解析类去去做处理,比如之前写的那个Handler return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); } /** + * 在需要的情况下,通过给定命名空间处理器来给bean 描述进行装饰 * Decorate the given bean definition through a namespace handler, if applicable. * @param ele the current element * @param originalDef the current bean definition @@ -1414,6 +1416,7 @@ public class BeanDefinitionParserDelegate { BeanDefinitionHolder finalDefinition = originalDef; // Decorate based on custom attributes first. + // 遍历所有的属性,看看是否存在属于修饰的属性 NamedNodeMap attributes = ele.getAttributes(); for (int i = 0; i < attributes.getLength(); i++) { Node node = attributes.item(i); @@ -1421,6 +1424,7 @@ public class BeanDefinitionParserDelegate { } // Decorate based on custom nested elements. + // 遍历所有的子元素,看看是否存在属于修饰的子元素 NodeList children = ele.getChildNodes(); for (int i = 0; i < children.getLength(); i++) { Node node = children.item(i); @@ -1441,11 +1445,15 @@ public class BeanDefinitionParserDelegate { */ public BeanDefinitionHolder decorateIfRequired( Node node, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) { - + // 获取自定义命名空间的uri String namespaceUri = getNamespaceURI(node); + // 判断是否是默认是的命名空间 if (namespaceUri != null && !isDefaultNamespace(namespaceUri)) { + // 根据命令空间查找对应的解析器 + // 在META-INF/spring.handlers中配置的 NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); if (handler != null) { + // 使用获取到的解析器解析属性或者子元素 BeanDefinitionHolder decorated = handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd)); if (decorated != null) { diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultBeanDefinitionDocumentReader.java b/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultBeanDefinitionDocumentReader.java index ddd960e95c..01e92e2f1c 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultBeanDefinitionDocumentReader.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultBeanDefinitionDocumentReader.java @@ -195,7 +195,7 @@ public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocume } } else { - // 自定义明明空间 + // 自定义命名空间 delegate.parseCustomElement(root); } } -- Gitee From 9354edd1874824902fa90942c06a38579d92d439 Mon Sep 17 00:00:00 2001 From: chensj Date: Fri, 22 Sep 2023 09:16:54 +0800 Subject: [PATCH 25/44] =?UTF-8?q?=E5=A2=9E=E5=8A=A0xml=E6=A0=A1=E9=AA=8C?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F=E7=9A=84=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../beans/factory/xml/BeansDtdResolver.java | 13 +++++++------ .../factory/xml/DelegatingEntityResolver.java | 13 ++++++++----- .../factory/xml/PluggableSchemaResolver.java | 19 ++++++++++--------- .../factory/xml/XmlBeanDefinitionReader.java | 18 ++++++++++++++++++ .../util/xml/XmlValidationModeDetector.java | 13 +++++++------ 5 files changed, 50 insertions(+), 26 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/xml/BeansDtdResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/xml/BeansDtdResolver.java index 16496d31b9..c9b9c8e5d8 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/xml/BeansDtdResolver.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/xml/BeansDtdResolver.java @@ -16,19 +16,20 @@ package org.springframework.beans.factory.xml; -import java.io.FileNotFoundException; -import java.io.IOException; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.xml.sax.EntityResolver; -import org.xml.sax.InputSource; - import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.lang.Nullable; +import org.xml.sax.EntityResolver; +import org.xml.sax.InputSource; + +import java.io.FileNotFoundException; +import java.io.IOException; /** + * dtd 定义默认获取定义处理类,从classpath中获取spring-beans.dtd的定义信息 + * * {@link EntityResolver} implementation for the Spring beans DTD, * to load the DTD from the Spring class path (or JAR file). * diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/xml/DelegatingEntityResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/xml/DelegatingEntityResolver.java index 1335d04017..76d0c291b8 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/xml/DelegatingEntityResolver.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/xml/DelegatingEntityResolver.java @@ -16,16 +16,17 @@ package org.springframework.beans.factory.xml; -import java.io.IOException; - +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; import org.xml.sax.EntityResolver; import org.xml.sax.InputSource; import org.xml.sax.SAXException; -import org.springframework.lang.Nullable; -import org.springframework.util.Assert; +import java.io.IOException; /** + * {@link EntityResolver}代理类,内部包含xsd和dtd两种EntityResolver的解析 + * * {@link EntityResolver} implementation that delegates to a {@link BeansDtdResolver} * and a {@link PluggableSchemaResolver} for DTDs and XML schemas, respectively. * @@ -81,11 +82,13 @@ public class DelegatingEntityResolver implements EntityResolver { @Nullable public InputSource resolveEntity(@Nullable String publicId, @Nullable String systemId) throws SAXException, IOException { - + // 判断 systemId if (systemId != null) { + // 如果systemId 是dtd 结尾 if (systemId.endsWith(DTD_SUFFIX)) { return this.dtdResolver.resolveEntity(publicId, systemId); } + // 如果systemId 是xsd 结尾 else if (systemId.endsWith(XSD_SUFFIX)) { return this.schemaResolver.resolveEntity(publicId, systemId); } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/xml/PluggableSchemaResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/xml/PluggableSchemaResolver.java index 3b1bff1feb..7e0851d293 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/xml/PluggableSchemaResolver.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/xml/PluggableSchemaResolver.java @@ -16,25 +16,26 @@ package org.springframework.beans.factory.xml; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.Map; -import java.util.Properties; -import java.util.concurrent.ConcurrentHashMap; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.xml.sax.EntityResolver; -import org.xml.sax.InputSource; - import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PropertiesLoaderUtils; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; +import org.xml.sax.EntityResolver; +import org.xml.sax.InputSource; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.ConcurrentHashMap; /** + * xsd 定义默认获取定义处理类,从META-INF/spring.schemas中获取定义 + * * {@link EntityResolver} implementation that attempts to resolve schema URLs into * local {@link ClassPathResource classpath resources} using a set of mappings files. * diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/xml/XmlBeanDefinitionReader.java b/spring-beans/src/main/java/org/springframework/beans/factory/xml/XmlBeanDefinitionReader.java index 1f3fe71e68..24f65857cd 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/xml/XmlBeanDefinitionReader.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/xml/XmlBeanDefinitionReader.java @@ -249,6 +249,21 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { } /** + * EntityResolver,SAX解析时候需要DTD或XSD的定义,一般是从网络上面查找 + * 但是在网络不通的情况下,就需要程序提供一个查找DTD或XSD定义,就是程序来实现寻找DTD声明的过程 + * 比如 + * xsd PluggableSchemaResolver 获取程序中定义 + * xsi:schemaLocation="http://www.springframework.org/schema/beans + * https://www.springframework.org/schema/beans/spring-beans.xsd" + * 对应EntityResolver + * publicId: null + * systemId:https://www.springframework.org/schema/beans/spring-beans.xsd + * + * dtd BeansDtdResolver 获取程序中的定义 + * + * publicId:-//SPRING//DTD BEAN 2.0//EN + * systemId:https://www.springframework.org/dtd/spring-beans-2.0.dtd + * * Return the EntityResolver to use, building a default resolver * if none specified. */ @@ -448,10 +463,12 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { // Hmm, we didn't get a clear indication... Let's assume XSD, // since apparently no DTD declaration has been found up until // detection stopped (before finding the document's root tag). + // 默认采用xsd来做校验 return VALIDATION_XSD; } /** + * 定义xml使用那种模式的校验 * Detect which kind of validation to perform on the XML file identified * by the supplied {@link Resource}. If the file has a {@code DOCTYPE} * definition then DTD validation is used otherwise XSD validation is assumed. @@ -479,6 +496,7 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { } try { + // 模式校验 return this.validationModeDetector.detectValidationMode(inputStream); } catch (IOException ex) { diff --git a/spring-core/src/main/java/org/springframework/util/xml/XmlValidationModeDetector.java b/spring-core/src/main/java/org/springframework/util/xml/XmlValidationModeDetector.java index 305619e1fb..da42e06c96 100644 --- a/spring-core/src/main/java/org/springframework/util/xml/XmlValidationModeDetector.java +++ b/spring-core/src/main/java/org/springframework/util/xml/XmlValidationModeDetector.java @@ -16,15 +16,11 @@ package org.springframework.util.xml; -import java.io.BufferedReader; -import java.io.CharConversionException; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; - import org.springframework.lang.Nullable; import org.springframework.util.StringUtils; +import java.io.*; + /** * Detects whether an XML stream is using DTD- or XSD-based validation. * @@ -81,6 +77,7 @@ public class XmlValidationModeDetector { /** + * 从 {@link InputStream}读取XML 定义信息来判断xml使用的模式 * Detect the validation mode for the XML document in the supplied {@link InputStream}. * Note that the supplied {@link InputStream} is closed by this method before returning. * @param inputStream the InputStream to parse @@ -90,19 +87,23 @@ public class XmlValidationModeDetector { */ public int detectValidationMode(InputStream inputStream) throws IOException { // Peek into the file to look for DOCTYPE. + // 读取内容 BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); try { boolean isDtdValidated = false; String content; while ((content = reader.readLine()) != null) { content = consumeCommentTokens(content); + // 如果读取的行是空行或者注释则略过 if (this.inComment || !StringUtils.hasText(content)) { continue; } + // 判断是否存在 DOCTYPE,如果存在则是DTD if (hasDoctype(content)) { isDtdValidated = true; break; } + // 判断当前内容是否为<,就是标签开始的地方,验证模式一定在标签开始的地方定义 if (hasOpeningTag(content)) { // End of meaningful data... break; -- Gitee From a9ea3d65d2fe24dce01afc75edca6b98702e797a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?chensj=28=E9=99=88=E4=B8=96=E6=9D=B0=29?= Date: Sun, 24 Sep 2023 21:02:42 +0800 Subject: [PATCH 26/44] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xml/BeanDefinitionParserDelegate.java | 2 + .../DefaultBeanDefinitionDocumentReader.java | 1 + .../xml/DefaultNamespaceHandlerResolver.java | 12 +-- .../factory/xml/XmlBeanDefinitionReader.java | 89 ++++++++++--------- 4 files changed, 58 insertions(+), 46 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/xml/BeanDefinitionParserDelegate.java b/spring-beans/src/main/java/org/springframework/beans/factory/xml/BeanDefinitionParserDelegate.java index dc604c8af4..7c844dfd28 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/xml/BeanDefinitionParserDelegate.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/xml/BeanDefinitionParserDelegate.java @@ -1358,6 +1358,8 @@ public class BeanDefinitionParserDelegate { } /** + * 解析自定义namespace + * * Parse a custom element (outside of the default namespace). * @param ele the element to parse * @return the resulting bean definition diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultBeanDefinitionDocumentReader.java b/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultBeanDefinitionDocumentReader.java index 01e92e2f1c..96733d4c93 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultBeanDefinitionDocumentReader.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultBeanDefinitionDocumentReader.java @@ -132,6 +132,7 @@ public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocume // 请跟踪当前(父)委托,该委托可以为null // 创建父类委托和子类 BeanDefinitionParserDelegate parent = this.delegate; + // 创建代理类来做解析 this.delegate = createDelegate(getReaderContext(), root, parent); // 判断是否为默认命名空间 主要是否为了解析profile属性 if (this.delegate.isDefaultNamespace(root)) { diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultNamespaceHandlerResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultNamespaceHandlerResolver.java index 6dfda38e6b..701c4629ee 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultNamespaceHandlerResolver.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultNamespaceHandlerResolver.java @@ -16,14 +16,8 @@ package org.springframework.beans.factory.xml; -import java.io.IOException; -import java.util.Map; -import java.util.Properties; -import java.util.concurrent.ConcurrentHashMap; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - import org.springframework.beans.BeanUtils; import org.springframework.beans.FatalBeanException; import org.springframework.core.io.support.PropertiesLoaderUtils; @@ -32,6 +26,11 @@ import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.CollectionUtils; +import java.io.IOException; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.ConcurrentHashMap; + /** * Default implementation of the {@link NamespaceHandlerResolver} interface. * Resolves namespace URIs to implementation classes based on the mappings @@ -89,6 +88,7 @@ public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver * @see #DEFAULT_HANDLER_MAPPINGS_LOCATION */ public DefaultNamespaceHandlerResolver(@Nullable ClassLoader classLoader) { + // 这里指定从META-INF/spring.handlers中获取映射信息 this(classLoader, DEFAULT_HANDLER_MAPPINGS_LOCATION); } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/xml/XmlBeanDefinitionReader.java b/spring-beans/src/main/java/org/springframework/beans/factory/xml/XmlBeanDefinitionReader.java index 24f65857cd..57e99a7fb5 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/xml/XmlBeanDefinitionReader.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/xml/XmlBeanDefinitionReader.java @@ -57,13 +57,13 @@ import java.util.Set; * @author Juergen Hoeller * @author Rob Harrop * @author Chris Beams - * @since 26.11.2003 * @see #setDocumentReaderClass * @see BeanDefinitionDocumentReader * @see DefaultBeanDefinitionDocumentReader * @see BeanDefinitionRegistry * @see org.springframework.beans.factory.support.DefaultListableBeanFactory * @see org.springframework.context.support.GenericApplicationContext + * @since 26.11.2003 */ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { @@ -88,7 +88,9 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { public static final int VALIDATION_XSD = XmlValidationModeDetector.VALIDATION_XSD; - /** Constants instance for this class. */ + /** + * Constants instance for this class. + */ private static final Constants constants = new Constants(XmlBeanDefinitionReader.class); private int validationMode = VALIDATION_AUTO; @@ -117,7 +119,7 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { private final XmlValidationModeDetector validationModeDetector = new XmlValidationModeDetector(); private final ThreadLocal> resourcesCurrentlyBeingLoaded = - new NamedThreadLocal>("XML bean definition resources currently being loaded"){ + new NamedThreadLocal>("XML bean definition resources currently being loaded") { @Override protected Set initialValue() { return new HashSet<>(4); @@ -127,8 +129,9 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { /** * Create new XmlBeanDefinitionReader for the given bean factory. + * * @param registry the BeanFactory to load bean definitions into, - * in the form of a BeanDefinitionRegistry + * in the form of a BeanDefinitionRegistry */ public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) { super(registry); @@ -139,6 +142,7 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { * Set whether to use XML validation. Default is {@code true}. *

This method switches namespace awareness on if validation is turned off, * in order to still process schema namespaces properly in such a scenario. + * * @see #setValidationMode * @see #setNamespaceAware */ @@ -149,6 +153,7 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { /** * Set the validation mode to use by name. Defaults to {@link #VALIDATION_AUTO}. + * * @see #setValidationMode */ public void setValidationModeName(String validationModeName) { @@ -254,16 +259,16 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { * 比如 * xsd PluggableSchemaResolver 获取程序中定义 * xsi:schemaLocation="http://www.springframework.org/schema/beans - * https://www.springframework.org/schema/beans/spring-beans.xsd" + * https://www.springframework.org/schema/beans/spring-beans.xsd" * 对应EntityResolver * publicId: null * systemId:https://www.springframework.org/schema/beans/spring-beans.xsd - * + *

* dtd BeansDtdResolver 获取程序中的定义 * * publicId:-//SPRING//DTD BEAN 2.0//EN * systemId:https://www.springframework.org/dtd/spring-beans-2.0.dtd - * + *

* Return the EntityResolver to use, building a default resolver * if none specified. */ @@ -273,8 +278,7 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { ResourceLoader resourceLoader = getResourceLoader(); if (resourceLoader != null) { this.entityResolver = new ResourceEntityResolver(resourceLoader); - } - else { + } else { this.entityResolver = new DelegatingEntityResolver(getBeanClassLoader()); } } @@ -287,6 +291,7 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { *

If not set, a default SimpleSaxErrorHandler is used that simply * logs warnings using the logger instance of the view class, * and rethrows errors to discontinue the XML transformation. + * * @see SimpleSaxErrorHandler */ public void setErrorHandler(ErrorHandler errorHandler) { @@ -297,6 +302,7 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { * Specify the {@link BeanDefinitionDocumentReader} implementation to use, * responsible for the actual reading of the XML bean definition document. *

The default is {@link DefaultBeanDefinitionDocumentReader}. + * * @param documentReaderClass the desired BeanDefinitionDocumentReader implementation class */ public void setDocumentReaderClass(Class documentReaderClass) { @@ -306,6 +312,7 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { /** * Load bean definitions from the specified XML file. + * * @param resource the resource descriptor for the XML file * @return the number of bean definitions found * @throws BeanDefinitionStoreException in case of loading or parsing errors @@ -317,8 +324,9 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { /** * Load bean definitions from the specified XML file. + * * @param encodedResource the resource descriptor for the XML file, - * allowing to specify an encoding to use for parsing the file + * allowing to specify an encoding to use for parsing the file * @return the number of bean definitions found * @throws BeanDefinitionStoreException in case of loading or parsing errors */ @@ -343,12 +351,10 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { } // 加载、读取、注册bean return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); - } - catch (IOException ex) { + } catch (IOException ex) { throw new BeanDefinitionStoreException( "IOException parsing XML document from " + encodedResource.getResource(), ex); - } - finally { + } finally { currentResources.remove(encodedResource); if (currentResources.isEmpty()) { this.resourcesCurrentlyBeingLoaded.remove(); @@ -358,6 +364,7 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { /** * Load bean definitions from the specified XML file. + * * @param inputSource the SAX InputSource to read from * @return the number of bean definitions found * @throws BeanDefinitionStoreException in case of loading or parsing errors @@ -368,9 +375,10 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { /** * Load bean definitions from the specified XML file. - * @param inputSource the SAX InputSource to read from + * + * @param inputSource the SAX InputSource to read from * @param resourceDescription a description of the resource - * (can be {@code null} or empty) + * (can be {@code null} or empty) * @return the number of bean definitions found * @throws BeanDefinitionStoreException in case of loading or parsing errors */ @@ -383,8 +391,9 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { /** * Actually load bean definitions from the specified XML file. + * * @param inputSource the SAX InputSource to read from - * @param resource the resource descriptor for the XML file + * @param resource the resource descriptor for the XML file * @return the number of bean definitions found * @throws BeanDefinitionStoreException in case of loading or parsing errors * @see #doLoadDocument @@ -402,27 +411,21 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { logger.debug("Loaded " + count + " bean definitions from " + resource); } return count; - } - catch (BeanDefinitionStoreException ex) { + } catch (BeanDefinitionStoreException ex) { throw ex; - } - catch (SAXParseException ex) { + } catch (SAXParseException ex) { throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex); - } - catch (SAXException ex) { + } catch (SAXException ex) { throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", ex); - } - catch (ParserConfigurationException ex) { + } catch (ParserConfigurationException ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, ex); - } - catch (IOException ex) { + } catch (IOException ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, ex); - } - catch (Throwable ex) { + } catch (Throwable ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, ex); } @@ -430,8 +433,9 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { /** * Actually load the specified document using the configured DocumentLoader. + * * @param inputSource the SAX InputSource to read from - * @param resource the resource descriptor for the XML file + * @param resource the resource descriptor for the XML file * @return the DOM Document * @throws Exception when thrown from the DocumentLoader * @see #setDocumentLoader @@ -449,6 +453,7 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { * mode gets {@link #detectValidationMode detected} from the given resource. *

Override this method if you would like full control over the validation * mode, even when something other than {@link #VALIDATION_AUTO} was set. + * * @see #detectValidationMode */ protected int getValidationModeForResource(Resource resource) { @@ -479,27 +484,25 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { if (resource.isOpen()) { throw new BeanDefinitionStoreException( "Passed-in Resource [" + resource + "] contains an open stream: " + - "cannot determine validation mode automatically. Either pass in a Resource " + - "that is able to create fresh streams, or explicitly specify the validationMode " + - "on your XmlBeanDefinitionReader instance."); + "cannot determine validation mode automatically. Either pass in a Resource " + + "that is able to create fresh streams, or explicitly specify the validationMode " + + "on your XmlBeanDefinitionReader instance."); } InputStream inputStream; try { inputStream = resource.getInputStream(); - } - catch (IOException ex) { + } catch (IOException ex) { throw new BeanDefinitionStoreException( "Unable to determine validation mode for [" + resource + "]: cannot open InputStream. " + - "Did you attempt to load directly from a SAX InputSource without specifying the " + - "validationMode on your XmlBeanDefinitionReader instance?", ex); + "Did you attempt to load directly from a SAX InputSource without specifying the " + + "validationMode on your XmlBeanDefinitionReader instance?", ex); } try { // 模式校验 return this.validationModeDetector.detectValidationMode(inputStream); - } - catch (IOException ex) { + } catch (IOException ex) { throw new BeanDefinitionStoreException("Unable to determine validation mode for [" + resource + "]: an error occurred whilst reading from the InputStream.", ex); } @@ -510,7 +513,8 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { * Called by {@code loadBeanDefinitions}. *

Creates a new instance of the parser class and invokes * {@code registerBeanDefinitions} on it. - * @param doc the DOM document + * + * @param doc the DOM document * @param resource the resource descriptor (for context information) * @return the number of bean definitions found * @throws BeanDefinitionStoreException in case of parsing errors @@ -532,6 +536,7 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { * Create the {@link BeanDefinitionDocumentReader} to use for actually * reading bean definitions from an XML document. *

The default implementation instantiates the specified "documentReaderClass". + * * @see #setDocumentReaderClass */ protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() { @@ -547,7 +552,10 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { } /** + * 获取namespace对应的解析器 + *

* Lazily create a default NamespaceHandlerResolver, if not set before. + * * @see #createDefaultNamespaceHandlerResolver() */ public NamespaceHandlerResolver getNamespaceHandlerResolver() { @@ -560,6 +568,7 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { /** * Create the default implementation of {@link NamespaceHandlerResolver} used if none is specified. *

The default implementation returns an instance of {@link DefaultNamespaceHandlerResolver}. + * * @see DefaultNamespaceHandlerResolver#DefaultNamespaceHandlerResolver(ClassLoader) */ protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() { -- Gitee From 3110d7023fefc295b0b50867f6d1bd611ea2ea17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?chensj=28=E9=99=88=E4=B8=96=E6=9D=B0=29?= Date: Sun, 24 Sep 2023 21:07:06 +0800 Subject: [PATCH 27/44] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../beans/factory/xml/DefaultNamespaceHandlerResolver.java | 4 ++++ .../beans/factory/xml/XmlBeanDefinitionReader.java | 1 + 2 files changed, 5 insertions(+) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultNamespaceHandlerResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultNamespaceHandlerResolver.java index 701c4629ee..b34367482f 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultNamespaceHandlerResolver.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultNamespaceHandlerResolver.java @@ -107,6 +107,8 @@ public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver /** + * 根据传入的{@code namespaceUri获取配置的NamespaceHandler} + * 会在获取前加载一下classpath下面的配置META-INF/spring.handlers的配置 * Locate the {@link NamespaceHandler} for the supplied namespace URI * from the configured mappings. * @param namespaceUri the relevant namespace URI @@ -115,6 +117,7 @@ public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver @Override @Nullable public NamespaceHandler resolve(String namespaceUri) { + // 加载一下classpath下面的配置META-INF/spring.handlers Map handlerMappings = getHandlerMappings(); Object handlerOrClassName = handlerMappings.get(namespaceUri); if (handlerOrClassName == null) { @@ -160,6 +163,7 @@ public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver logger.trace("Loading NamespaceHandler mappings from [" + this.handlerMappingsLocation + "]"); } try { + // 从classpath下META-INF/spring.handlers中加载自定义配置信息 Properties mappings = PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader); if (logger.isTraceEnabled()) { diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/xml/XmlBeanDefinitionReader.java b/spring-beans/src/main/java/org/springframework/beans/factory/xml/XmlBeanDefinitionReader.java index 57e99a7fb5..eb1ec5293b 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/xml/XmlBeanDefinitionReader.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/xml/XmlBeanDefinitionReader.java @@ -566,6 +566,7 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { } /** + * 创建一个默认的namespaceHandler * Create the default implementation of {@link NamespaceHandlerResolver} used if none is specified. *

The default implementation returns an instance of {@link DefaultNamespaceHandlerResolver}. * -- Gitee From dc137d68420e9e8a2dc1a79c61502032e2b6acbf Mon Sep 17 00:00:00 2001 From: chensj Date: Thu, 28 Sep 2023 15:02:37 +0800 Subject: [PATCH 28/44] =?UTF-8?q?=E7=A0=94=E7=A9=B6EntityResolver?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../factory/xml/DefaultDocumentLoader.java | 13 +++++++------ .../factory/xml/DelegatingEntityResolver.java | 2 ++ .../factory/xml/ResourceEntityResolver.java | 19 +++++++++++-------- .../AnnotationConfigApplicationContext.java | 7 +++++-- 4 files changed, 25 insertions(+), 16 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultDocumentLoader.java b/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultDocumentLoader.java index a443e7b1d6..098f06ccbb 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultDocumentLoader.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultDocumentLoader.java @@ -16,19 +16,18 @@ package org.springframework.beans.factory.xml; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.lang.Nullable; +import org.springframework.util.xml.XmlValidationModeDetector; import org.w3c.dom.Document; import org.xml.sax.EntityResolver; import org.xml.sax.ErrorHandler; import org.xml.sax.InputSource; -import org.springframework.lang.Nullable; -import org.springframework.util.xml.XmlValidationModeDetector; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; /** * Spring's default {@link DocumentLoader} implementation. @@ -114,6 +113,8 @@ public class DefaultDocumentLoader implements DocumentLoader { } /** + * 创建一个jaxb的DocumentBuilder,用于解析xml中定义的bean信息 + * * Create a JAXP DocumentBuilder that this bean definition reader * will use for parsing XML documents. Can be overridden in subclasses, * adding further initialization of the builder. diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/xml/DelegatingEntityResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/xml/DelegatingEntityResolver.java index 76d0c291b8..ff4c4df3a6 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/xml/DelegatingEntityResolver.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/xml/DelegatingEntityResolver.java @@ -26,6 +26,8 @@ import java.io.IOException; /** * {@link EntityResolver}代理类,内部包含xsd和dtd两种EntityResolver的解析 + * dtd --> {@link BeansDtdResolver} + * zsd --> {@link PluggableSchemaResolver} * * {@link EntityResolver} implementation that delegates to a {@link BeansDtdResolver} * and a {@link PluggableSchemaResolver} for DTDs and XML schemas, respectively. diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/xml/ResourceEntityResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/xml/ResourceEntityResolver.java index b74e013326..46642dca18 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/xml/ResourceEntityResolver.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/xml/ResourceEntityResolver.java @@ -16,21 +16,24 @@ package org.springframework.beans.factory.xml; -import java.io.File; -import java.io.IOException; -import java.net.URL; -import java.net.URLDecoder; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; - import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.springframework.lang.Nullable; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.net.URLDecoder; /** + * {@code EntityResolver}的实现类,尝试通过使用{@link org.springframework.core.io.ResourceLoader} + * (通常使用{@code ApplicationContext}的resource)来解析dtd或xsd的定义,如果在适用的情况下 + * 会使用${@link DelegatingEntityResolver}来解析dtd或xsd的定义 + *

* {@code EntityResolver} implementation that tries to resolve entity references * through a {@link org.springframework.core.io.ResourceLoader} (usually, * relative to the resource base of an {@code ApplicationContext}), if applicable. diff --git a/spring-context/src/main/java/org/springframework/context/annotation/AnnotationConfigApplicationContext.java b/spring-context/src/main/java/org/springframework/context/annotation/AnnotationConfigApplicationContext.java index 696655ad0d..5c4943eba1 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/AnnotationConfigApplicationContext.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/AnnotationConfigApplicationContext.java @@ -16,8 +16,6 @@ package org.springframework.context.annotation; -import java.util.function.Supplier; - import org.springframework.beans.factory.config.BeanDefinitionCustomizer; import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.beans.factory.support.DefaultListableBeanFactory; @@ -26,7 +24,12 @@ import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.lang.Nullable; import org.springframework.util.Assert; +import java.util.function.Supplier; + /** + * 标准ApplicationContext的视线,接受使用{@link Configuration @Configuration}中定义的Component classes, + * 同样也可以支持@Component和@Resource注解的bean + * * Standalone application context, accepting component classes as input — * in particular {@link Configuration @Configuration}-annotated classes, but also plain * {@link org.springframework.stereotype.Component @Component} types and JSR-330 compliant -- Gitee From b0e2dc4ee9fa6e90b45bd0524abe77d9fbdebfa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?chensj=28=E9=99=88=E4=B8=96=E6=9D=B0=29?= Date: Thu, 28 Sep 2023 23:56:37 +0800 Subject: [PATCH 29/44] =?UTF-8?q?=E5=AD=90=E5=85=83=E7=B4=A0=E8=A7=A3?= =?UTF-8?q?=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../support/DefaultListableBeanFactory.java | 8 +++----- .../xml/BeanDefinitionParserDelegate.java | 18 ++++++++++++++++++ .../xml/DefaultNamespaceHandlerResolver.java | 3 +++ 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java index 25cdf0310a..a85446a2c7 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java @@ -889,11 +889,9 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto if (beanDefinition instanceof AbstractBeanDefinition) { try { - /** - * bean注册前做最后一次校验,这个校验是在与之前加载的xml文件之间进行 - * 主要对于AbstractBeanDefinition属性中的methodOverrides进行校验 - * methodOverrides:是否与工厂方法并存或methodOverrides指定的方法不存在 - */ + //bean注册前做最后一次校验,这个校验是在与之前加载的xml文件之间进行 + //主要对于AbstractBeanDefinition属性中的methodOverrides进行校验 + //methodOverrides:是否与工厂方法并存或methodOverrides指定的方法不存在 ((AbstractBeanDefinition) beanDefinition).validate(); } catch (BeanDefinitionValidationException ex) { diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/xml/BeanDefinitionParserDelegate.java b/spring-beans/src/main/java/org/springframework/beans/factory/xml/BeanDefinitionParserDelegate.java index 7c844dfd28..32137abfd1 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/xml/BeanDefinitionParserDelegate.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/xml/BeanDefinitionParserDelegate.java @@ -35,6 +35,8 @@ import org.w3c.dom.NodeList; import java.util.*; /** + * xml 解析代理类 + * * Stateful delegate class used to parse XML bean definitions. * Intended for use by both the main parser and any extension * {@link BeanDefinitionParser BeanDefinitionParsers} or @@ -968,6 +970,7 @@ public class BeanDefinitionParserDelegate { } /** + * 解析子元素 * Parse a value, ref or collection sub-element of a property or * constructor-arg element. * @param ele subelement of property element; we don't know which yet @@ -977,9 +980,11 @@ public class BeanDefinitionParserDelegate { */ @Nullable public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd, @Nullable String defaultValueType) { + // 判断是否是默认命名空间 if (!isDefaultNamespace(ele)) { return parseNestedCustomElement(ele, bd); } + // 判断解析子元素是否为bean else if (nodeNameEquals(ele, BEAN_ELEMENT)) { BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd); if (nestedBd != null) { @@ -987,6 +992,7 @@ public class BeanDefinitionParserDelegate { } return nestedBd; } + // 判断解析子元素是否为ref else if (nodeNameEquals(ele, REF_ELEMENT)) { // A generic reference to any name of any bean. String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE); @@ -1008,12 +1014,15 @@ public class BeanDefinitionParserDelegate { ref.setSource(extractSource(ele)); return ref; } + // 判断解析子元素是否为idref else if (nodeNameEquals(ele, IDREF_ELEMENT)) { return parseIdRefElement(ele); } + // 判断解析子元素是否为value else if (nodeNameEquals(ele, VALUE_ELEMENT)) { return parseValueElement(ele, defaultValueType); } + // 判断解析子元素是否为null else if (nodeNameEquals(ele, NULL_ELEMENT)) { // It's a distinguished null value. Let's wrap it in a TypedStringValue // object in order to preserve the source location. @@ -1021,18 +1030,23 @@ public class BeanDefinitionParserDelegate { nullHolder.setSource(extractSource(ele)); return nullHolder; } + // 判断解析子元素是否为array else if (nodeNameEquals(ele, ARRAY_ELEMENT)) { return parseArrayElement(ele, bd); } + // 判断解析子元素是否为list else if (nodeNameEquals(ele, LIST_ELEMENT)) { return parseListElement(ele, bd); } + // 判断解析子元素是否为set else if (nodeNameEquals(ele, SET_ELEMENT)) { return parseSetElement(ele, bd); } + // 判断解析子元素是否为map else if (nodeNameEquals(ele, MAP_ELEMENT)) { return parseMapElement(ele, bd); } + // 判断解析子元素是否为props else if (nodeNameEquals(ele, PROPS_ELEMENT)) { return parsePropsElement(ele); } @@ -1149,6 +1163,9 @@ public class BeanDefinitionParserDelegate { return target; } + /** + * 解析集合元素 + */ protected void parseCollectionElements( NodeList elementNodes, Collection target, @Nullable BeanDefinition bd, String defaultElementType) { @@ -1323,6 +1340,7 @@ public class BeanDefinitionParserDelegate { } /** + * 解析prop标签 * Parse a props element. */ public Properties parsePropsElement(Element propsEle) { diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultNamespaceHandlerResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultNamespaceHandlerResolver.java index b34367482f..09a8f87f98 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultNamespaceHandlerResolver.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultNamespaceHandlerResolver.java @@ -119,13 +119,16 @@ public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver public NamespaceHandler resolve(String namespaceUri) { // 加载一下classpath下面的配置META-INF/spring.handlers Map handlerMappings = getHandlerMappings(); + // 根据url 获取解析class Object handlerOrClassName = handlerMappings.get(namespaceUri); if (handlerOrClassName == null) { return null; } + // 判断是否已经初始化 else if (handlerOrClassName instanceof NamespaceHandler) { return (NamespaceHandler) handlerOrClassName; } + // 初始化 else { String className = (String) handlerOrClassName; try { -- Gitee From 61c540796c72defe44675aadcecfaf9fabdbbee6 Mon Sep 17 00:00:00 2001 From: chensj Date: Fri, 29 Sep 2023 21:22:15 +0800 Subject: [PATCH 30/44] =?UTF-8?q?xml=E8=A7=A3=E6=9E=90bean?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xml/BeanDefinitionParserDelegate.java | 56 ++++++++++++++++++- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/xml/BeanDefinitionParserDelegate.java b/spring-beans/src/main/java/org/springframework/beans/factory/xml/BeanDefinitionParserDelegate.java index 32137abfd1..ad506e43d6 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/xml/BeanDefinitionParserDelegate.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/xml/BeanDefinitionParserDelegate.java @@ -288,6 +288,7 @@ public class BeanDefinitionParserDelegate { * @param root the root element of the current bean definition document (or nested beans element) */ protected void populateDefaults(DocumentDefaultsDefinition defaults, @Nullable DocumentDefaultsDefinition parentDefaults, Element root) { + // 解析属性default-lazy-init String lazyInit = root.getAttribute(DEFAULT_LAZY_INIT_ATTRIBUTE); if (isDefaultValue(lazyInit)) { // Potentially inherited from outer sections, otherwise falling back to false. @@ -295,6 +296,7 @@ public class BeanDefinitionParserDelegate { } defaults.setLazyInit(lazyInit); + // 解析属性default-merge String merge = root.getAttribute(DEFAULT_MERGE_ATTRIBUTE); if (isDefaultValue(merge)) { // Potentially inherited from outer sections, otherwise falling back to false. @@ -302,6 +304,7 @@ public class BeanDefinitionParserDelegate { } defaults.setMerge(merge); + // 解析属性default-autowire String autowire = root.getAttribute(DEFAULT_AUTOWIRE_ATTRIBUTE); if (isDefaultValue(autowire)) { // Potentially inherited from outer sections, otherwise falling back to 'no'. @@ -309,6 +312,7 @@ public class BeanDefinitionParserDelegate { } defaults.setAutowire(autowire); + // 解析属性default-autowire-candidates if (root.hasAttribute(DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE)) { defaults.setAutowireCandidates(root.getAttribute(DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE)); } @@ -316,6 +320,7 @@ public class BeanDefinitionParserDelegate { defaults.setAutowireCandidates(parentDefaults.getAutowireCandidates()); } + // 解析属性default-init-method if (root.hasAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE)) { defaults.setInitMethod(root.getAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE)); } @@ -323,6 +328,7 @@ public class BeanDefinitionParserDelegate { defaults.setInitMethod(parentDefaults.getInitMethod()); } + // 解析属性default-destroy-method if (root.hasAttribute(DEFAULT_DESTROY_METHOD_ATTRIBUTE)) { defaults.setDestroyMethod(root.getAttribute(DEFAULT_DESTROY_METHOD_ATTRIBUTE)); } @@ -341,6 +347,7 @@ public class BeanDefinitionParserDelegate { } /** + * 获取默认bean定义,带有默认的bean属性 * Return the default settings for bean definitions as indicated within * the attributes of the top-level {@code } element. */ @@ -365,6 +372,7 @@ public class BeanDefinitionParserDelegate { /** + * 解析对应的bean * Parses the supplied {@code } element. May return {@code null} * if there were errors during parse. Errors are reported to the * {@link org.springframework.beans.factory.parsing.ProblemReporter}. @@ -448,6 +456,7 @@ public class BeanDefinitionParserDelegate { } /** + * 校验beanName是否唯一 * Validate that the specified bean name and aliases have not been used already * within the current level of beans element nesting. */ @@ -469,6 +478,7 @@ public class BeanDefinitionParserDelegate { } /** + * 硬编码解析bean标签定义的bean信息 * Parse the bean definition itself, without regard to name or aliases. May return * {@code null} if problems occurred during the parsing of the bean definition. */ @@ -615,11 +625,11 @@ public class BeanDefinitionParserDelegate { bd.setEnforceDestroyMethod(false); } -// 解析工厂方法属性 + // 解析工厂方法属性 if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) { bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE)); } -// 解析工厂bean属性 + // 解析工厂bean属性 if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) { bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE)); } @@ -628,6 +638,7 @@ public class BeanDefinitionParserDelegate { } /** + * 创建一个bean的定义,根据提供的{@code className}和{@code parentName} * Create a bean definition for the given class name and parent name. * @param className the name of the bean class * @param parentName the name of the bean's parent bean @@ -642,6 +653,7 @@ public class BeanDefinitionParserDelegate { } /** + * 解析 标签 * Parse the meta elements underneath the given element, if any. */ public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) { @@ -660,6 +672,7 @@ public class BeanDefinitionParserDelegate { } /** + * 解析获取Autowire的模式 * Parse the given autowire attribute value into * {@link AbstractBeanDefinition} autowire constants. */ @@ -687,6 +700,8 @@ public class BeanDefinitionParserDelegate { } /** + * 解析构造器注入参数 + * 对应的内容 * Parse constructor-arg sub-elements of the given bean element. */ public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) { @@ -700,6 +715,7 @@ public class BeanDefinitionParserDelegate { } /** + * 解析property属性元素 * Parse property sub-elements of the given bean element. */ public void parsePropertyElements(Element beanEle, BeanDefinition bd) { @@ -713,6 +729,7 @@ public class BeanDefinitionParserDelegate { } /** + * 解析元素 * Parse qualifier sub-elements of the given bean element. */ public void parseQualifierElements(Element beanEle, AbstractBeanDefinition bd) { @@ -726,6 +743,7 @@ public class BeanDefinitionParserDelegate { } /** + * 解析元素 * Parse lookup-override sub-elements of the given bean element. */ public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) { @@ -744,6 +762,7 @@ public class BeanDefinitionParserDelegate { } /** + * 解析元素 * Parse replaced-method sub-elements of the given bean element. */ public void parseReplacedMethodSubElements(Element beanEle, MethodOverrides overrides) { @@ -771,6 +790,8 @@ public class BeanDefinitionParserDelegate { } /** + * 构造器参数解析 + * 比如 参数索引,类型、名称 * Parse a constructor-arg element. */ public void parseConstructorArgElement(Element ele, BeanDefinition bd) { @@ -832,6 +853,7 @@ public class BeanDefinitionParserDelegate { } /** + * 解析元素property内容 * Parse a property element. */ public void parsePropertyElement(Element ele, BeanDefinition bd) { @@ -858,6 +880,7 @@ public class BeanDefinitionParserDelegate { } /** + * 解析元素property对应属性类型,值参数 * Parse a qualifier element. */ public void parseQualifierElement(Element ele, AbstractBeanDefinition bd) { @@ -900,6 +923,7 @@ public class BeanDefinitionParserDelegate { } /** + * 解析元素qualifier内容 * Get the value of a property element. May be a list etc. * Also used for constructor arguments, "propertyName" being null in this case. */ @@ -948,6 +972,7 @@ public class BeanDefinitionParserDelegate { valueHolder.setSource(extractSource(ele)); return valueHolder; } + // 解析子元素 else if (subElement != null) { return parsePropertySubElement(subElement, bd); } @@ -959,6 +984,7 @@ public class BeanDefinitionParserDelegate { } /** + * 解析子元素内容 * Parse a value, ref or collection sub-element of a property or * constructor-arg element. * @param ele subelement of property element; we don't know which yet @@ -1057,6 +1083,7 @@ public class BeanDefinitionParserDelegate { } /** + * 解析idref标签对应内容 * Return a typed String value Object for the given 'idref' element. */ @Nullable @@ -1077,6 +1104,7 @@ public class BeanDefinitionParserDelegate { } /** + * 解析value标签对应内容 * Return a typed String value Object for the given value element. */ public Object parseValueElement(Element ele, @Nullable String defaultTypeName) { @@ -1100,6 +1128,7 @@ public class BeanDefinitionParserDelegate { } /** + * 构建string类型参数 * Build a typed String value Object for the given raw value. * @see org.springframework.beans.factory.config.TypedStringValue */ @@ -1122,6 +1151,7 @@ public class BeanDefinitionParserDelegate { } /** + * 解析array标签对应内容 * Parse an array element. */ public Object parseArrayElement(Element arrayEle, @Nullable BeanDefinition bd) { @@ -1136,6 +1166,7 @@ public class BeanDefinitionParserDelegate { } /** + * 解析list标签对应内容 * Parse a list element. */ public List parseListElement(Element collectionEle, @Nullable BeanDefinition bd) { @@ -1150,6 +1181,7 @@ public class BeanDefinitionParserDelegate { } /** + * 解析set标签对应内容 * Parse a set element. */ public Set parseSetElement(Element collectionEle, @Nullable BeanDefinition bd) { @@ -1178,6 +1210,7 @@ public class BeanDefinitionParserDelegate { } /** + * 解析map标签对应内容 * Parse a map element. */ public Map parseMapElement(Element mapEle, @Nullable BeanDefinition bd) { @@ -1299,6 +1332,7 @@ public class BeanDefinitionParserDelegate { } /** + * 构建map类型的参数 * Build a typed String value Object for the given raw value. * @see org.springframework.beans.factory.config.TypedStringValue */ @@ -1315,6 +1349,7 @@ public class BeanDefinitionParserDelegate { } /** + * 构建map类型参数key * Parse a key sub-element of a map element. */ @Nullable @@ -1365,6 +1400,7 @@ public class BeanDefinitionParserDelegate { } /** + * 解析merge属性信息 * Parse the merge attribute of a collection element, if any. */ public boolean parseMergeAttribute(Element collectionElement) { @@ -1424,6 +1460,7 @@ public class BeanDefinitionParserDelegate { } /** + * 装饰bean,这里会通过namespace handler来进行装饰 * Decorate the given bean definition through a namespace handler, if applicable. * @param ele the current element * @param originalDef the current bean definition @@ -1456,6 +1493,7 @@ public class BeanDefinitionParserDelegate { } /** + * 装饰bean,这里会通过namespace handler来进行装饰 * Decorate the given bean definition through a namespace handler, * if applicable. * @param node the current child node @@ -1493,6 +1531,9 @@ public class BeanDefinitionParserDelegate { return originalDef; } + /** + * 解析内部自定义bean元素 + */ @Nullable private BeanDefinitionHolder parseNestedCustomElement(Element ele, @Nullable BeanDefinition containingBd) { BeanDefinition innerDefinition = parseCustomElement(ele, containingBd); @@ -1512,6 +1553,7 @@ public class BeanDefinitionParserDelegate { /** + * 获取namespace url * Get the namespace URI for the supplied node. *

The default implementation uses {@link Node#getNamespaceURI}. * Subclasses may override the default implementation to provide a @@ -1524,6 +1566,7 @@ public class BeanDefinitionParserDelegate { } /** + * 获取node的本地名称 * Get the local name for the supplied {@link Node}. *

The default implementation calls {@link Node#getLocalName}. * Subclasses may override the default implementation to provide a @@ -1535,6 +1578,7 @@ public class BeanDefinitionParserDelegate { } /** + * 判断node名称是否相等 * Determine whether the name of the supplied node is equal to the supplied name. *

The default implementation checks the supplied desired name against both * {@link Node#getNodeName()} and {@link Node#getLocalName()}. @@ -1548,6 +1592,7 @@ public class BeanDefinitionParserDelegate { } /** + * 判断是否是默认的命名空间 * Determine whether the given URI indicates the default namespace. */ public boolean isDefaultNamespace(@Nullable String namespaceUri) { @@ -1555,16 +1600,23 @@ public class BeanDefinitionParserDelegate { } /** + * 判断是否是默认的命名空间 * Determine whether the given node indicates the default namespace. */ public boolean isDefaultNamespace(Node node) { return isDefaultNamespace(getNamespaceURI(node)); } + /** + * 判断是否会是默认值 + */ private boolean isDefaultValue(String value) { return !StringUtils.hasLength(value) || DEFAULT_VALUE.equals(value); } + /** + * 判断是否会是备选元素 + */ private boolean isCandidateElement(Node node) { return (node instanceof Element && (isDefaultNamespace(node) || !isDefaultNamespace(node.getParentNode()))); } -- Gitee From e20d8becbd8e18945e48157414a283ecf0ed28b3 Mon Sep 17 00:00:00 2001 From: chensj Date: Sat, 30 Sep 2023 17:46:38 +0800 Subject: [PATCH 31/44] =?UTF-8?q?classpath=20=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../support/BeanDefinitionReaderUtils.java | 1 + .../ClassPathBeanDefinitionScanner.java | 14 ++++---- ...athScanningCandidateComponentProvider.java | 34 +++++++++++-------- 3 files changed, 26 insertions(+), 23 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionReaderUtils.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionReaderUtils.java index b02687792e..c0aef3e91a 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionReaderUtils.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionReaderUtils.java @@ -150,6 +150,7 @@ public abstract class BeanDefinitionReaderUtils { } /** + * 注册Bean定义信息,这里是通过Bean Factory提供的接口注册 * Register the given bean definition with the given bean factory. * @param definitionHolder the bean definition including name and aliases * @param registry the bean factory to register with diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java b/spring-context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java index 65cbb9bdb9..6888e57b6c 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java @@ -16,17 +16,10 @@ package org.springframework.context.annotation; -import java.util.LinkedHashSet; -import java.util.Set; - import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; -import org.springframework.beans.factory.support.AbstractBeanDefinition; -import org.springframework.beans.factory.support.BeanDefinitionDefaults; -import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.beans.factory.support.BeanNameGenerator; +import org.springframework.beans.factory.support.*; import org.springframework.core.env.Environment; import org.springframework.core.env.EnvironmentCapable; import org.springframework.core.env.StandardEnvironment; @@ -35,7 +28,12 @@ import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.PatternMatchUtils; +import java.util.LinkedHashSet; +import java.util.Set; + /** + * 从classpath获取bean定义信息,并且通过BeanFactory或者ApplicationContext提供的接口注册bean定义信息 + *

* A bean definition scanner that detects bean candidates on the classpath, * registering corresponding bean definitions with a given registry ({@code BeanFactory} * or {@code ApplicationContext}). diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java b/spring-context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java index 92d502a13f..3ddae4f736 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java @@ -16,18 +16,8 @@ package org.springframework.context.annotation; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.lang.annotation.Annotation; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; import org.springframework.beans.factory.annotation.Lookup; @@ -53,15 +43,18 @@ import org.springframework.core.type.filter.AnnotationTypeFilter; import org.springframework.core.type.filter.AssignableTypeFilter; import org.springframework.core.type.filter.TypeFilter; import org.springframework.lang.Nullable; -import org.springframework.stereotype.Component; -import org.springframework.stereotype.Controller; -import org.springframework.stereotype.Indexed; -import org.springframework.stereotype.Repository; -import org.springframework.stereotype.Service; +import org.springframework.stereotype.*; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.lang.annotation.Annotation; +import java.util.*; + /** + * 组件提供者,从一个basePackage中读取 + *

* A component provider that provides candidate components from a base package. Can * use {@link CandidateComponentsIndex the index} if it is available of scans the * classpath otherwise. Candidate components are identified by applying exclude and @@ -193,6 +186,8 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC } /** + * 注册默认过滤器,用于 {@link Component @Component} + *

* Register the default filter for {@link Component @Component}. *

This will implicitly register all annotations that have the * {@link Component @Component} meta-annotation including the @@ -304,6 +299,7 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC /** + * 扫描classpath下面的备选组件,其中basePackage 是扫描路径包名信息 * Scan the class path for candidate components. * @param basePackage the package to check for annotated classes * @return a corresponding Set of autodetected bean definitions @@ -413,6 +409,10 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC return candidates; } + /** + * 从${@code basePackage}中获取备选的组件信息 + * classpath*:basePackage\/**\/*.class + */ private Set scanCandidateComponents(String basePackage) { Set candidates = new LinkedHashSet<>(); try { @@ -426,9 +426,13 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC logger.trace("Scanning " + resource); } try { + // 解析获取的bean 信息 MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource); + // 判断是否备选的bean if (isCandidateComponent(metadataReader)) { + // 创建ScannedGenericBeanDefinition ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); + // 设置来源 sbd.setSource(resource); if (isCandidateComponent(sbd)) { if (debugEnabled) { -- Gitee From b6d5bec9a5e85e3baa9e0cec737e2aed4e14c3a2 Mon Sep 17 00:00:00 2001 From: chensj Date: Fri, 13 Oct 2023 15:57:53 +0800 Subject: [PATCH 32/44] =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/springframework/beans/factory/BeanFactory.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java index f31c29d851..fe062432e1 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java @@ -21,6 +21,8 @@ import org.springframework.core.ResolvableType; import org.springframework.lang.Nullable; /** + * 访问spring bean 容器的基础接口 + *

* The root interface for accessing a Spring bean container. * *

This is the basic client view of a bean container; -- Gitee From 98aee06888d11edb804b74522bbaccc8f62aa851 Mon Sep 17 00:00:00 2001 From: chensj Date: Fri, 13 Oct 2023 16:16:33 +0800 Subject: [PATCH 33/44] =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/org/springframework/core/AliasRegistry.java | 1 + 1 file changed, 1 insertion(+) diff --git a/spring-core/src/main/java/org/springframework/core/AliasRegistry.java b/spring-core/src/main/java/org/springframework/core/AliasRegistry.java index 689b3aeea9..28cfc8ed54 100644 --- a/spring-core/src/main/java/org/springframework/core/AliasRegistry.java +++ b/spring-core/src/main/java/org/springframework/core/AliasRegistry.java @@ -17,6 +17,7 @@ package org.springframework.core; /** + * 别名注册中心,也是别名管理接口 * Common interface for managing aliases. Serves as a super-interface for * {@link org.springframework.beans.factory.support.BeanDefinitionRegistry}. * -- Gitee From 526e7ef579df04653ddf2e2685a7a4a805af5c28 Mon Sep 17 00:00:00 2001 From: chensj Date: Thu, 2 Nov 2023 16:20:59 +0800 Subject: [PATCH 34/44] =?UTF-8?q?=E6=9E=84=E9=80=A0=E5=99=A8=E6=B3=A8?= =?UTF-8?q?=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AbstractAutowireCapableBeanFactory.java | 66 +++----------- .../factory/support/ConstructorResolver.java | 86 +++++++++++-------- 2 files changed, 60 insertions(+), 92 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java index d8447022b1..1b2e3a1bab 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java @@ -16,6 +16,15 @@ package org.springframework.beans.factory.support; +import org.apache.commons.logging.Log; +import org.springframework.beans.*; +import org.springframework.beans.factory.*; +import org.springframework.beans.factory.config.*; +import org.springframework.core.*; +import org.springframework.lang.Nullable; +import org.springframework.util.*; +import org.springframework.util.ReflectionUtils.MethodCallback; + import java.beans.PropertyDescriptor; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; @@ -25,65 +34,11 @@ import java.security.AccessController; import java.security.PrivilegedAction; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; -import java.util.TreeSet; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.function.Supplier; -import org.apache.commons.logging.Log; - -import org.springframework.beans.BeanUtils; -import org.springframework.beans.BeanWrapper; -import org.springframework.beans.BeanWrapperImpl; -import org.springframework.beans.BeansException; -import org.springframework.beans.MutablePropertyValues; -import org.springframework.beans.PropertyAccessorUtils; -import org.springframework.beans.PropertyValue; -import org.springframework.beans.PropertyValues; -import org.springframework.beans.TypeConverter; -import org.springframework.beans.factory.Aware; -import org.springframework.beans.factory.BeanClassLoaderAware; -import org.springframework.beans.factory.BeanCreationException; -import org.springframework.beans.factory.BeanCurrentlyInCreationException; -import org.springframework.beans.factory.BeanDefinitionStoreException; -import org.springframework.beans.factory.BeanFactory; -import org.springframework.beans.factory.BeanFactoryAware; -import org.springframework.beans.factory.BeanNameAware; -import org.springframework.beans.factory.FactoryBean; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.beans.factory.InjectionPoint; -import org.springframework.beans.factory.UnsatisfiedDependencyException; -import org.springframework.beans.factory.config.AutowireCapableBeanFactory; -import org.springframework.beans.factory.config.AutowiredPropertyMarker; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.BeanPostProcessor; -import org.springframework.beans.factory.config.ConfigurableBeanFactory; -import org.springframework.beans.factory.config.ConstructorArgumentValues; -import org.springframework.beans.factory.config.DependencyDescriptor; -import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor; -import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor; -import org.springframework.beans.factory.config.TypedStringValue; -import org.springframework.core.DefaultParameterNameDiscoverer; -import org.springframework.core.MethodParameter; -import org.springframework.core.NamedThreadLocal; -import org.springframework.core.ParameterNameDiscoverer; -import org.springframework.core.PriorityOrdered; -import org.springframework.core.ResolvableType; -import org.springframework.lang.Nullable; -import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; -import org.springframework.util.ObjectUtils; -import org.springframework.util.ReflectionUtils; -import org.springframework.util.ReflectionUtils.MethodCallback; -import org.springframework.util.StringUtils; - /** * Abstract bean factory superclass that implements default bean creation, * with the full capabilities specified by the {@link RootBeanDefinition} class. @@ -1184,6 +1139,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac // Shortcut when re-creating the same bean... boolean resolved = false; boolean autowireNecessary = false; + // 判断是否走构造器注入 if (args == null) { synchronized (mbd.constructorArgumentLock) { if (mbd.resolvedConstructorOrFactoryMethod != null) { diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java index db450ce008..9723d67dce 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java @@ -16,38 +16,9 @@ package org.springframework.beans.factory.support; -import java.beans.ConstructorProperties; -import java.lang.reflect.Array; -import java.lang.reflect.Constructor; -import java.lang.reflect.Executable; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; - import org.apache.commons.logging.Log; - -import org.springframework.beans.BeanMetadataElement; -import org.springframework.beans.BeanWrapper; -import org.springframework.beans.BeanWrapperImpl; -import org.springframework.beans.BeansException; -import org.springframework.beans.TypeConverter; -import org.springframework.beans.TypeMismatchException; -import org.springframework.beans.factory.BeanCreationException; -import org.springframework.beans.factory.BeanDefinitionStoreException; -import org.springframework.beans.factory.InjectionPoint; -import org.springframework.beans.factory.NoSuchBeanDefinitionException; -import org.springframework.beans.factory.NoUniqueBeanDefinitionException; -import org.springframework.beans.factory.UnsatisfiedDependencyException; +import org.springframework.beans.*; +import org.springframework.beans.factory.*; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.beans.factory.config.ConstructorArgumentValues; import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder; @@ -57,12 +28,13 @@ import org.springframework.core.MethodParameter; import org.springframework.core.NamedThreadLocal; import org.springframework.core.ParameterNameDiscoverer; import org.springframework.lang.Nullable; -import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; -import org.springframework.util.MethodInvoker; -import org.springframework.util.ObjectUtils; -import org.springframework.util.ReflectionUtils; -import org.springframework.util.StringUtils; +import org.springframework.util.*; + +import java.beans.ConstructorProperties; +import java.lang.reflect.*; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.*; /** * Delegate for resolving constructors and factory methods. @@ -133,10 +105,12 @@ class ConstructorResolver { ArgumentsHolder argsHolderToUse = null; Object[] argsToUse = null; + // 判断构造器注入参数 if (explicitArgs != null) { argsToUse = explicitArgs; } else { + // 判断是否解析了构造方法或者工厂方法 Object[] argsToResolve = null; synchronized (mbd.constructorArgumentLock) { constructorToUse = (Constructor) mbd.resolvedConstructorOrFactoryMethod; @@ -153,7 +127,9 @@ class ConstructorResolver { } } + // 如果没有获取需要的构造方法或者参数 if (constructorToUse == null || argsToUse == null) { + // 获取所有已知的的构造方法 // Take specified constructors, if any. Constructor[] candidates = chosenCtors; if (candidates == null) { @@ -169,6 +145,7 @@ class ConstructorResolver { } } + // 备选构造器唯一、显示参数不存在,并且没有设置构造参数,则直接实例化 if (candidates.length == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) { Constructor uniqueCandidate = candidates[0]; if (uniqueCandidate.getParameterCount() == 0) { @@ -182,49 +159,69 @@ class ConstructorResolver { } } + // 判断当前bean是否是基于构造器注入 // Need to resolve the constructor. boolean autowiring = (chosenCtors != null || mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR); ConstructorArgumentValues resolvedValues = null; + // 设置最小依赖数值 int minNrOfArgs; if (explicitArgs != null) { minNrOfArgs = explicitArgs.length; } else { + // 从bean定义中获取到设置的构造器参数 + // 通过设置的argValue解析,从bean定义中获取参数值 ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues(); resolvedValues = new ConstructorArgumentValues(); + // 获取构造器最小参数,同时解析生成构造器参数 minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues); } + // 开始解析构造器 AutowireUtils.sortConstructors(candidates); int minTypeDiffWeight = Integer.MAX_VALUE; Set> ambiguousConstructors = null; LinkedList causes = null; + // 循环构造器,获取需要的构造器 for (Constructor candidate : candidates) { + // 获取构造器需要参数大小 int parameterCount = candidate.getParameterCount(); + // 已经获取到需要使用的构造器和参数 就不在这里继续循环 if (constructorToUse != null && argsToUse != null && argsToUse.length > parameterCount) { // Already found greedy constructor that can be satisfied -> // do not look any further, there are only less greedy constructors left. break; } + + // 如果参数小于最小值 直接跳过 if (parameterCount < minNrOfArgs) { continue; } + // 获取构造参数列表参数 + // 如果参数大于最小值,开始创建args ArgumentsHolder argsHolder; Class[] paramTypes = candidate.getParameterTypes(); + // 如果最小值存在,则走这里 if (resolvedValues != null) { try { + // 获取构造器的参数名 + // 这里是通过注解@ConstructorProperties获取 String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, parameterCount); if (paramNames == null) { + // 通过beanFactory中设置的ParameterNameDiscoverer[DefaultParameterNameDiscoverer]获取 + // DefaultParameterNameDiscoverer默认集成 + // StandardReflectionParameterNameDiscoverer和LocalVariableTableParameterNameDiscoverer ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer(); if (pnd != null) { paramNames = pnd.getParameterNames(candidate); } } + // 创建参数数组 argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames, getUserDeclaredConstructor(candidate), autowiring, candidates.length == 1); } @@ -248,9 +245,11 @@ class ConstructorResolver { argsHolder = new ArgumentsHolder(explicitArgs); } + // 判断构造方法是宽松模式还是严格模式注入 int typeDiffWeight = (mbd.isLenientConstructorResolution() ? argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes)); // Choose this constructor if it represents the closest match. + // 选择适配的构造器 if (typeDiffWeight < minTypeDiffWeight) { constructorToUse = candidate; argsHolderToUse = argsHolder; @@ -292,6 +291,7 @@ class ConstructorResolver { } Assert.state(argsToUse != null, "Unresolved constructor arguments"); + // 实例化 bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse)); return bw; } @@ -660,6 +660,8 @@ class ConstructorResolver { } /** + * 解析构造器参数解析为resolvedValues对象,这个过程中可能会触发查找其他bean + *

* Resolve the constructor arguments for this bean into the resolvedValues object. * This may involve looking up other beans. *

This method is also used for handling invocations of static factory methods. @@ -674,6 +676,7 @@ class ConstructorResolver { int minNrOfArgs = cargs.getArgumentCount(); + // 循环已经设置的属性值 for (Map.Entry entry : cargs.getIndexedArgumentValues().entrySet()) { int index = entry.getKey(); if (index < 0) { @@ -697,6 +700,7 @@ class ConstructorResolver { } } + // 处理泛型参数 for (ConstructorArgumentValues.ValueHolder valueHolder : cargs.getGenericArgumentValues()) { if (valueHolder.isConverted()) { resolvedValues.addGenericArgumentValue(valueHolder); @@ -715,6 +719,7 @@ class ConstructorResolver { } /** + * 创建一个数组参数,用于调用构造或工厂方法时候提供参数 * Create an array of arguments to invoke a constructor or factory method, * given the resolved constructor argument values. */ @@ -726,7 +731,9 @@ class ConstructorResolver { TypeConverter customConverter = this.beanFactory.getCustomTypeConverter(); TypeConverter converter = (customConverter != null ? customConverter : bw); + // 封装一个参数放置器 ArgumentsHolder args = new ArgumentsHolder(paramTypes.length); + // 封装一个参数值放置器 Set usedValueHolders = new HashSet<>(paramTypes.length); Set autowiredBeanNames = new LinkedHashSet<>(4); @@ -734,12 +741,15 @@ class ConstructorResolver { Class paramType = paramTypes[paramIndex]; String paramName = (paramNames != null ? paramNames[paramIndex] : ""); // Try to find matching constructor argument value, either indexed or generic. + // 尝试去匹配 下标或者通用 ConstructorArgumentValues.ValueHolder valueHolder = null; if (resolvedValues != null) { + // 使用设置序号的参数去赋值 valueHolder = resolvedValues.getArgumentValue(paramIndex, paramType, paramName, usedValueHolders); // If we couldn't find a direct match and are not supposed to autowire, // let's try the next generic, untyped argument value as fallback: // it could match after type conversion (for example, String -> int). + // 如果不是则通过通用模式去匹配 if (valueHolder == null && (!autowiring || paramTypes.length == resolvedValues.getArgumentCount())) { valueHolder = resolvedValues.getGenericArgumentValue(null, null, usedValueHolders); } @@ -777,6 +787,7 @@ class ConstructorResolver { args.rawArguments[paramIndex] = originalValue; } else { + // 解析需要注入的值 MethodParameter methodParam = MethodParameter.forExecutable(executable, paramIndex); // No explicit match found: we're either supposed to autowire or // have to fail creating an argument array for the given constructor. @@ -801,6 +812,7 @@ class ConstructorResolver { } } + // 设置注入关系 for (String autowiredBeanName : autowiredBeanNames) { this.beanFactory.registerDependentBean(autowiredBeanName, beanName); if (logger.isDebugEnabled()) { -- Gitee From b3d98c3663ef431b60e1e738ef787d9d3e90e20b Mon Sep 17 00:00:00 2001 From: chensj Date: Mon, 29 Jan 2024 15:17:00 +0800 Subject: [PATCH 35/44] =?UTF-8?q?=E8=A7=A3=E6=9E=90=E6=B3=A8=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../support/AbstractAutowireCapableBeanFactory.java | 6 ++++++ .../beans/factory/support/BeanDefinitionReaderUtils.java | 4 ++++ .../beans/factory/xml/BeanDefinitionParserDelegate.java | 8 +++++++- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java index 1b2e3a1bab..b95c30c4af 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java @@ -1122,16 +1122,19 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac // Make sure bean class is actually resolved at this point. Class beanClass = resolveBeanClass(mbd, beanName); + // 不是公共方法或者不允许非公共访问 if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Bean class isn't public, and non-public access not allowed: " + beanClass.getName()); } + // 通过实例工厂方法创建 Supplier instanceSupplier = mbd.getInstanceSupplier(); if (instanceSupplier != null) { return obtainFromSupplier(instanceSupplier, beanName); } + // 通过工厂方法创建 if (mbd.getFactoryMethodName() != null) { return instantiateUsingFactoryMethod(beanName, mbd, args); } @@ -1157,6 +1160,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac } } + // 考虑使用备选构造器去注入 // Candidate constructors for autowiring? Constructor[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName); if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR || @@ -1164,12 +1168,14 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac return autowireConstructor(beanName, mbd, ctors, args); } + // 推荐的构造器去注入 // Preferred constructors for default construction? ctors = mbd.getPreferredConstructors(); if (ctors != null) { return autowireConstructor(beanName, mbd, ctors, null); } + // 没有特别的,那么就走无参的构造器 // No special handling: simply use no-arg constructor. return instantiateBean(beanName, mbd); } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionReaderUtils.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionReaderUtils.java index c0aef3e91a..53aeefa195 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionReaderUtils.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionReaderUtils.java @@ -45,6 +45,7 @@ public abstract class BeanDefinitionReaderUtils { /** + * 使用给定的{@code parentName}和{@code className}创建一个GenericBeanDefinition * Create a new GenericBeanDefinition for the given parent name and class name, * eagerly loading the bean class if a ClassLoader has been specified. * @param parentName the name of the parent bean, if any @@ -58,8 +59,11 @@ public abstract class BeanDefinitionReaderUtils { @Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException { GenericBeanDefinition bd = new GenericBeanDefinition(); + // 可能为空 bd.setParentName(parentName); if (className != null) { + // 如果ClassLoader不为空,则可以使用ClassLoader把className加载成Class + // 否则只记录名称 if (classLoader != null) { bd.setBeanClass(ClassUtils.forName(className, classLoader)); } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/xml/BeanDefinitionParserDelegate.java b/spring-beans/src/main/java/org/springframework/beans/factory/xml/BeanDefinitionParserDelegate.java index ad506e43d6..5e9547d0d0 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/xml/BeanDefinitionParserDelegate.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/xml/BeanDefinitionParserDelegate.java @@ -427,10 +427,12 @@ public class BeanDefinitionParserDelegate { beanDefinition, this.readerContext.getRegistry(), true); } else { + // 生成beanName beanName = this.readerContext.generateBeanName(beanDefinition); // Register an alias for the plain bean class name, if still possible, // if the generator returned the class name plus a suffix. // This is expected for Spring 1.2/2.0 backwards compatibility. + // 生成别名,如果beanName是类名,那么为beanName添加一个别名 String beanClassName = beanDefinition.getBeanClassName(); if (beanClassName != null && beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && @@ -638,7 +640,7 @@ public class BeanDefinitionParserDelegate { } /** - * 创建一个bean的定义,根据提供的{@code className}和{@code parentName} + * 根据提供的{@code className}和{@code parentName}创建一个bean的定义 * Create a bean definition for the given class name and parent name. * @param className the name of the bean class * @param parentName the name of the bean's parent bean @@ -795,8 +797,11 @@ public class BeanDefinitionParserDelegate { * Parse a constructor-arg element. */ public void parseConstructorArgElement(Element ele, BeanDefinition bd) { + // 获取索引 属性 String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE); + // 获取类型 属性 String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE); + // 获取名称 属性 String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); if (StringUtils.hasLength(indexAttr)) { try { @@ -807,6 +812,7 @@ public class BeanDefinitionParserDelegate { else { try { this.parseState.push(new ConstructorArgumentEntry(index)); + // 解析ele对应的属性值 Object value = parsePropertyValue(ele, bd, null); ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value); if (StringUtils.hasLength(typeAttr)) { -- Gitee From 3430725cc197f01023ecf3467227ad4e90490d5e Mon Sep 17 00:00:00 2001 From: chensj Date: Thu, 29 Feb 2024 20:43:09 +0800 Subject: [PATCH 36/44] =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89=E6=A0=87?= =?UTF-8?q?=E7=AD=BE=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../factory/xml/AbstractBeanDefinitionParser.java | 3 +++ .../factory/xml/DefaultNamespaceHandlerResolver.java | 7 +++++++ .../beans/factory/xml/NamespaceHandlerSupport.java | 12 ++++++------ 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/xml/AbstractBeanDefinitionParser.java b/spring-beans/src/main/java/org/springframework/beans/factory/xml/AbstractBeanDefinitionParser.java index 288ad403f4..7944b73d58 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/xml/AbstractBeanDefinitionParser.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/xml/AbstractBeanDefinitionParser.java @@ -60,6 +60,7 @@ public abstract class AbstractBeanDefinitionParser implements BeanDefinitionPars @Override @Nullable public final BeanDefinition parse(Element element, ParserContext parserContext) { + // 调用子类实现的解析方法parseInternal AbstractBeanDefinition definition = parseInternal(element, parserContext); if (definition != null && !parserContext.isNested()) { try { @@ -76,9 +77,11 @@ public abstract class AbstractBeanDefinitionParser implements BeanDefinitionPars aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name)); } } + // 将AbstractBeanDefinition转化为BeanDefinitionHolder BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases); registerBeanDefinition(holder, parserContext.getRegistry()); if (shouldFireEvents()) { + // 需要通知监听器侧进行处理 BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder); postProcessComponentDefinition(componentDefinition); parserContext.registerComponent(componentDefinition); diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultNamespaceHandlerResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultNamespaceHandlerResolver.java index 09a8f87f98..cba711d08d 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultNamespaceHandlerResolver.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultNamespaceHandlerResolver.java @@ -130,15 +130,20 @@ public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver } // 初始化 else { + //没做过解析,这里返回的是类路径 String className = (String) handlerOrClassName; try { + // 反射方法将类路径转化为类 Class handlerClass = ClassUtils.forName(className, this.classLoader); if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) { throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri + "] does not implement the [" + NamespaceHandler.class.getName() + "] interface"); } + // 初始化 NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass); + // 调用NamespaceHandler的初始化方法 namespaceHandler.init(); + // 存入缓存 handlerMappings.put(namespaceUri, namespaceHandler); return namespaceHandler; } @@ -158,6 +163,7 @@ public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver */ private Map getHandlerMappings() { Map handlerMappings = this.handlerMappings; + // 如果没有缓存,则开始加载 if (handlerMappings == null) { synchronized (this) { handlerMappings = this.handlerMappings; @@ -173,6 +179,7 @@ public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver logger.trace("Loaded NamespaceHandler mappings: " + mappings); } handlerMappings = new ConcurrentHashMap<>(mappings.size()); + // 将解析的Properties合并到handlerMappings CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings); this.handlerMappings = handlerMappings; } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/xml/NamespaceHandlerSupport.java b/spring-beans/src/main/java/org/springframework/beans/factory/xml/NamespaceHandlerSupport.java index b1eec9bbc9..f9340c2b4f 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/xml/NamespaceHandlerSupport.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/xml/NamespaceHandlerSupport.java @@ -16,16 +16,15 @@ package org.springframework.beans.factory.xml; -import java.util.HashMap; -import java.util.Map; - +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.BeanDefinitionHolder; +import org.springframework.lang.Nullable; import org.w3c.dom.Attr; import org.w3c.dom.Element; import org.w3c.dom.Node; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.BeanDefinitionHolder; -import org.springframework.lang.Nullable; +import java.util.HashMap; +import java.util.Map; /** * Support class for implementing custom {@link NamespaceHandler NamespaceHandlers}. @@ -135,6 +134,7 @@ public abstract class NamespaceHandlerSupport implements NamespaceHandler { * name. */ protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) { + // 实现类注册元素和解析器关系 this.parsers.put(elementName, parser); } -- Gitee From 43305627b32654c06faa895edc18e106b0685826 Mon Sep 17 00:00:00 2001 From: chensj Date: Thu, 29 Feb 2024 20:49:40 +0800 Subject: [PATCH 37/44] =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/springframework/beans/factory/xml/XmlBeanFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/xml/XmlBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/xml/XmlBeanFactory.java index 13836b2ebb..0a0f276959 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/xml/XmlBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/xml/XmlBeanFactory.java @@ -76,7 +76,7 @@ public class XmlBeanFactory extends DefaultListableBeanFactory { */ public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException { // 初始化父级BeanFactory - // 指定派出扫描类 在AbstractAutowireCapableBeanFactory中配置 + // 指定排除扫描类 在AbstractAutowireCapableBeanFactory中配置 super(parentBeanFactory); // 读取、加载和注册bean信息 this.reader.loadBeanDefinitions(resource); -- Gitee From 85b8426c83a7b1a32ffd125d0810a40ba6995939 Mon Sep 17 00:00:00 2001 From: chensj Date: Fri, 1 Mar 2024 09:34:45 +0800 Subject: [PATCH 38/44] =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89=E6=A0=87?= =?UTF-8?q?=E7=AD=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- settings.gradle | 1 + .../xml/AbstractBeanDefinitionParser.java | 4 ++ .../AbstractSingleBeanDefinitionParser.java | 4 ++ spring-demo/spring-demo.gradle | 36 +++++++++++++ .../handler/UserBeanNamespaceHandler.java | 19 +++++++ .../parser/UserBeanDefinitionParser.java | 47 +++++++++++++++++ .../java/org/chensj/test/custom/tag/User.java | 50 +++++++++++++++++++ .../main/resources/META-INF/spring.handlers | 1 + .../main/resources/META-INF/spring.schemas | 1 + .../src/main/resources/META-INF/tag-user.xsd | 13 +++++ spring-demo/src/test/java/UserTagTest.java | 22 ++++++++ spring-demo/src/test/resources/spring-tag.xml | 10 ++++ 12 files changed, 208 insertions(+) create mode 100644 spring-demo/spring-demo.gradle create mode 100644 spring-demo/src/main/java/org/chensj/test/custom/handler/UserBeanNamespaceHandler.java create mode 100644 spring-demo/src/main/java/org/chensj/test/custom/parser/UserBeanDefinitionParser.java create mode 100644 spring-demo/src/main/java/org/chensj/test/custom/tag/User.java create mode 100644 spring-demo/src/main/resources/META-INF/spring.handlers create mode 100644 spring-demo/src/main/resources/META-INF/spring.schemas create mode 100644 spring-demo/src/main/resources/META-INF/tag-user.xsd create mode 100644 spring-demo/src/test/java/UserTagTest.java create mode 100644 spring-demo/src/test/resources/spring-tag.xml diff --git a/settings.gradle b/settings.gradle index cf24142b94..be2addbdd1 100644 --- a/settings.gradle +++ b/settings.gradle @@ -13,6 +13,7 @@ include "spring-context" include "spring-context-indexer" include "spring-context-support" include "spring-core" +include "spring-demo" include "kotlin-coroutines" project(':kotlin-coroutines').projectDir = file('spring-core/kotlin-coroutines') include "spring-expression" diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/xml/AbstractBeanDefinitionParser.java b/spring-beans/src/main/java/org/springframework/beans/factory/xml/AbstractBeanDefinitionParser.java index 7944b73d58..a362f268bb 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/xml/AbstractBeanDefinitionParser.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/xml/AbstractBeanDefinitionParser.java @@ -29,6 +29,10 @@ import org.springframework.lang.Nullable; import org.springframework.util.StringUtils; /** + * {@link BeanDefinitionParser}实现的抽象类 + * 提供了许多便利的方法和模板方法{@link AbstractBeanDefinitionParser#parseInternal} + * 必须由子类覆盖以提供实际的解析逻辑。 + * * Abstract {@link BeanDefinitionParser} implementation providing * a number of convenience methods and a * {@link AbstractBeanDefinitionParser#parseInternal template method} diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/xml/AbstractSingleBeanDefinitionParser.java b/spring-beans/src/main/java/org/springframework/beans/factory/xml/AbstractSingleBeanDefinitionParser.java index 75b70796e1..171570e1d4 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/xml/AbstractSingleBeanDefinitionParser.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/xml/AbstractSingleBeanDefinitionParser.java @@ -61,10 +61,12 @@ public abstract class AbstractSingleBeanDefinitionParser extends AbstractBeanDef @Override protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(); + // 从实现类中获取解析父类的名称 String parentName = getParentName(element); if (parentName != null) { builder.getRawBeanDefinition().setParentName(parentName); } + // 从实现类中获取解析的bean的class Class beanClass = getBeanClass(element); if (beanClass != null) { builder.getRawBeanDefinition().setBeanClass(beanClass); @@ -75,6 +77,7 @@ public abstract class AbstractSingleBeanDefinitionParser extends AbstractBeanDef builder.getRawBeanDefinition().setBeanClassName(beanClassName); } } + // 设置bean的source builder.getRawBeanDefinition().setSource(parserContext.extractSource(element)); BeanDefinition containingBd = parserContext.getContainingBeanDefinition(); if (containingBd != null) { @@ -85,6 +88,7 @@ public abstract class AbstractSingleBeanDefinitionParser extends AbstractBeanDef // Default-lazy-init applies to custom bean definitions as well. builder.setLazyInit(true); } + // 调用子类实现的解析方法 doParse(element, parserContext, builder); return builder.getBeanDefinition(); } diff --git a/spring-demo/spring-demo.gradle b/spring-demo/spring-demo.gradle new file mode 100644 index 0000000000..dea91d9f8e --- /dev/null +++ b/spring-demo/spring-demo.gradle @@ -0,0 +1,36 @@ +description = "Spring Demo" + +apply plugin: "groovy" +apply plugin: "kotlin" + +dependencies { + compile(project(":spring-context")) + optional("javax.inject:javax.inject") + optional("org.yaml:snakeyaml") + optional("org.codehaus.groovy:groovy-xml") + optional("org.jetbrains.kotlin:kotlin-reflect") + optional("org.jetbrains.kotlin:kotlin-stdlib") + testCompile(testFixtures(project(":spring-core"))) + testCompile("javax.annotation:javax.annotation-api") + testFixturesApi("org.junit.jupiter:junit-jupiter-api") + testFixturesImplementation("org.assertj:assertj-core") +} + +// This module does joint compilation for Java and Groovy code with the compileGroovy task. +sourceSets { + main.groovy.srcDirs += "src/main/java" + main.java.srcDirs = [] +} + +compileGroovy { + sourceCompatibility = 1.8 + targetCompatibility = 1.8 + options.compilerArgs += "-Werror" +} + +// This module also builds Kotlin code and the compileKotlin task naturally depends on +// compileJava. We need to redefine dependencies to break task cycles. +def deps = compileGroovy.taskDependencies.immutableValues + compileGroovy.taskDependencies.mutableValues +compileGroovy.dependsOn = deps - "compileJava" +compileKotlin.dependsOn(compileGroovy) +compileKotlin.classpath += files(compileGroovy.destinationDir) diff --git a/spring-demo/src/main/java/org/chensj/test/custom/handler/UserBeanNamespaceHandler.java b/spring-demo/src/main/java/org/chensj/test/custom/handler/UserBeanNamespaceHandler.java new file mode 100644 index 0000000000..8424593101 --- /dev/null +++ b/spring-demo/src/main/java/org/chensj/test/custom/handler/UserBeanNamespaceHandler.java @@ -0,0 +1,19 @@ +package org.chensj.test.custom.handler; + +import org.chensj.test.custom.parser.UserBeanDefinitionParser; +import org.springframework.beans.factory.xml.NamespaceHandlerSupport; + +/** + * user-tag + * + * @author chensj + * @description user-tag + * @date 2024-02-29 21:12 + * @package org.chensj.test.custom.handler + */ +public class UserBeanNamespaceHandler extends NamespaceHandlerSupport { + @Override + public void init() { + registerBeanDefinitionParser("user", new UserBeanDefinitionParser()); + } +} diff --git a/spring-demo/src/main/java/org/chensj/test/custom/parser/UserBeanDefinitionParser.java b/spring-demo/src/main/java/org/chensj/test/custom/parser/UserBeanDefinitionParser.java new file mode 100644 index 0000000000..8654dfea12 --- /dev/null +++ b/spring-demo/src/main/java/org/chensj/test/custom/parser/UserBeanDefinitionParser.java @@ -0,0 +1,47 @@ +package org.chensj.test.custom.parser; + +import org.chensj.test.custom.tag.User; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser; +import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; +import org.springframework.util.StringUtils; +import org.w3c.dom.Element; + +/** + * user 解析 + * + * @author chensj + * @description user 解析 + * @date 2024-02-29 21:08 + * @package org.chensj.test.custom.parser + */ +public class UserBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { + + @Override + protected Class getBeanClass(Element element) { + return User.class; + } + + @Override + protected String getBeanClassName(Element element) { + return User.class.getName(); + } + + @Override + protected void doParse(Element element, BeanDefinitionBuilder builder) { + String id = element.getAttribute("id"); + String username = element.getAttribute("username"); + String password = element.getAttribute("password"); + String email = element.getAttribute("email"); + // 将取出的属性放置到BeanDefinitionBuilder中,待完成所有bean解析后统一注册到BeanFactory中 + if(StringUtils.hasText(username)){ + builder.addPropertyValue("username",username); + } + if(StringUtils.hasText(password)){ + builder.addPropertyValue("password",password); + } + if(StringUtils.hasText(email)){ + builder.addPropertyValue("email",email); + } + } +} diff --git a/spring-demo/src/main/java/org/chensj/test/custom/tag/User.java b/spring-demo/src/main/java/org/chensj/test/custom/tag/User.java new file mode 100644 index 0000000000..b481853930 --- /dev/null +++ b/spring-demo/src/main/java/org/chensj/test/custom/tag/User.java @@ -0,0 +1,50 @@ +package org.chensj.test.custom.tag; + +/** + * User + * + * @author chensj + * @description User + * @date 2024-02-29 21:01 + * @package org.chensj.test.custom.tag + */ +public class User { + + private String id; + private String username; + private String password; + + private String email; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } +} diff --git a/spring-demo/src/main/resources/META-INF/spring.handlers b/spring-demo/src/main/resources/META-INF/spring.handlers new file mode 100644 index 0000000000..dc4acf1772 --- /dev/null +++ b/spring-demo/src/main/resources/META-INF/spring.handlers @@ -0,0 +1 @@ +http\://www.chensj.org/schema/spring-user=org.chensj.test.custom.handler.UserBeanNamespaceHandler \ No newline at end of file diff --git a/spring-demo/src/main/resources/META-INF/spring.schemas b/spring-demo/src/main/resources/META-INF/spring.schemas new file mode 100644 index 0000000000..9bde9216cb --- /dev/null +++ b/spring-demo/src/main/resources/META-INF/spring.schemas @@ -0,0 +1 @@ +http\://www.chensj.org/schema/user/spring-user.xsd=META-INF/tag-user.xsd \ No newline at end of file diff --git a/spring-demo/src/main/resources/META-INF/tag-user.xsd b/spring-demo/src/main/resources/META-INF/tag-user.xsd new file mode 100644 index 0000000000..d5f1006c83 --- /dev/null +++ b/spring-demo/src/main/resources/META-INF/tag-user.xsd @@ -0,0 +1,13 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/spring-demo/src/test/java/UserTagTest.java b/spring-demo/src/test/java/UserTagTest.java new file mode 100644 index 0000000000..78287d0249 --- /dev/null +++ b/spring-demo/src/test/java/UserTagTest.java @@ -0,0 +1,22 @@ +import org.chensj.test.custom.tag.User; +import org.springframework.beans.factory.xml.XmlBeanFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +/** + * user + * + * @author chensj + * @description user + * @date 2024-02-29 21:25 + * @package PACKAGE_NAME + */ +public class UserTagTest { + + public static void main(String[] args) { + ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-tag.xml"); + + User user = (User) applicationContext.getBean("user"); + System.out.println(user.getEmail()); + } +} diff --git a/spring-demo/src/test/resources/spring-tag.xml b/spring-demo/src/test/resources/spring-tag.xml new file mode 100644 index 0000000000..f2c3a24b09 --- /dev/null +++ b/spring-demo/src/test/resources/spring-tag.xml @@ -0,0 +1,10 @@ + + + + + \ No newline at end of file -- Gitee From b48265d6fca2fa9dea8ed0aea173aba1ed7eb02d Mon Sep 17 00:00:00 2001 From: chensj Date: Fri, 1 Mar 2024 11:36:43 +0800 Subject: [PATCH 39/44] =?UTF-8?q?=E9=AA=8C=E8=AF=81=E8=87=AA=E5=AE=9A?= =?UTF-8?q?=E4=B9=89=E6=A0=87=E7=AD=BE=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spring-demo/src/test/resources/spring-tag.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-demo/src/test/resources/spring-tag.xml b/spring-demo/src/test/resources/spring-tag.xml index f2c3a24b09..48aa254abc 100644 --- a/spring-demo/src/test/resources/spring-tag.xml +++ b/spring-demo/src/test/resources/spring-tag.xml @@ -6,5 +6,5 @@ http://www.springframework.org/schema/beans/spring-beans.xsd http://www.chensj.org/schema/spring-user http://www.chensj.org/schema/user/spring-user.xsd"> - + \ No newline at end of file -- Gitee From e27b125ac5a06c0589abe1f2561ab2f8528598e5 Mon Sep 17 00:00:00 2001 From: chensj Date: Fri, 22 Mar 2024 09:49:51 +0800 Subject: [PATCH 40/44] =?UTF-8?q?bean=E5=88=9D=E5=A7=8B=E5=8C=96=E8=BF=87?= =?UTF-8?q?=E7=A8=8B-1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../support/AbstractApplicationContext.java | 67 +++++++++---------- .../FileSystemXmlApplicationContext.java | 12 +++- 2 files changed, 42 insertions(+), 37 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java b/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java index 83be8cdd2b..ef1d088993 100644 --- a/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java +++ b/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java @@ -16,21 +16,8 @@ package org.springframework.context.support; -import java.io.IOException; -import java.lang.annotation.Annotation; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Date; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.atomic.AtomicBoolean; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - import org.springframework.beans.BeansException; import org.springframework.beans.CachedIntrospectionResults; import org.springframework.beans.factory.BeanFactory; @@ -40,29 +27,8 @@ import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.support.ResourceEditorRegistrar; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.context.ApplicationEvent; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.context.ApplicationEventPublisherAware; -import org.springframework.context.ApplicationListener; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.EmbeddedValueResolverAware; -import org.springframework.context.EnvironmentAware; -import org.springframework.context.HierarchicalMessageSource; -import org.springframework.context.LifecycleProcessor; -import org.springframework.context.MessageSource; -import org.springframework.context.MessageSourceAware; -import org.springframework.context.MessageSourceResolvable; -import org.springframework.context.NoSuchMessageException; -import org.springframework.context.PayloadApplicationEvent; -import org.springframework.context.ResourceLoaderAware; -import org.springframework.context.event.ApplicationEventMulticaster; -import org.springframework.context.event.ContextClosedEvent; -import org.springframework.context.event.ContextRefreshedEvent; -import org.springframework.context.event.ContextStartedEvent; -import org.springframework.context.event.ContextStoppedEvent; -import org.springframework.context.event.SimpleApplicationEventMulticaster; +import org.springframework.context.*; +import org.springframework.context.event.*; import org.springframework.context.expression.StandardBeanExpressionResolver; import org.springframework.context.weaving.LoadTimeWeaverAware; import org.springframework.context.weaving.LoadTimeWeaverAwareProcessor; @@ -83,6 +49,11 @@ import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.ReflectionUtils; +import java.io.IOException; +import java.lang.annotation.Annotation; +import java.util.*; +import java.util.concurrent.atomic.AtomicBoolean; + /** * Abstract implementation of the {@link org.springframework.context.ApplicationContext} * interface. Doesn't mandate the type of storage used for configuration; simply @@ -513,38 +484,60 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader return this.applicationListeners; } + /** + * 容器初始化的过程:BeanDefinition 的 Resource 定位、BeanDefinition 的载入、BeanDefinition 的注册。 + * BeanDefinition 的载入和 bean 的依赖注入是两个独立的过程,依赖注入一般发生在 应用第一次通过 + * getBean() 方法从容器获取 bean 时。 + * + * 另外需要注意的是,IoC 容器有一个预实例化的配置(即,将 AbstractBeanDefinition 中的 lazyInit 属性 + * 设为 true),使用户可以对容器的初始化过程做一个微小的调控,lazyInit 设为 false 的 bean + * 将在容器初始化时进行依赖注入,而不会等到 getBean() 方法调用时才进行 + * + */ @Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. + // 调用容器准备刷新,获取容器的当前时间,同时给容器设置同步标识 prepareRefresh(); // Tell the subclass to refresh the internal bean factory. + // 告诉子类启动 refreshBeanFactory() 方法,BeanDefinition 资源文件的载入从子类的 refreshBeanFactory() 方法启动开始 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. + // 为 BeanFactory 配置容器特性,例如类加载器、事件处理器等 prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. + // 为容器的某些子类指定特殊的 BeanPost 事件处理器 postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. + // 调用所有注册的 BeanFactoryPostProcessor 的 Bean invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. + // 为 BeanFactory 注册 BeanPost 事件处理器. + // BeanPostProcessor 是 Bean 后置处理器,用于监听容器触发的事件 registerBeanPostProcessors(beanFactory); // Initialize message source for this context. + // 初始化信息源,和国际化相关. initMessageSource(); // Initialize event multicaster for this context. + // 初始化事件广播器,用于监听容器触发的事件 initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. + // 调用子类的某些特殊 Bean 初始化方法 + // 初始化其他特殊的 Bean,留给子类实现 onRefresh(); // Check for listener beans and register them. + // 为事件传播器注册事件监听器. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. @@ -689,6 +682,8 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader } /** + * 在标准初始化后修改应用程序上下文的内部 Bean 工厂。 + * 所有 Bean 定义都将被加载,但尚未实例化任何 Bean。 * Modify the application context's internal bean factory after its standard * initialization. All bean definitions will have been loaded, but no beans * will have been instantiated yet. This allows for registering special diff --git a/spring-context/src/main/java/org/springframework/context/support/FileSystemXmlApplicationContext.java b/spring-context/src/main/java/org/springframework/context/support/FileSystemXmlApplicationContext.java index e6abe2b0f1..1f521f33fa 100644 --- a/spring-context/src/main/java/org/springframework/context/support/FileSystemXmlApplicationContext.java +++ b/spring-context/src/main/java/org/springframework/context/support/FileSystemXmlApplicationContext.java @@ -76,6 +76,8 @@ public class FileSystemXmlApplicationContext extends AbstractXmlApplicationConte } /** + * 单个路径下面包含beanDefinition的xml文件 + * 下面四个方法都是走第五个方法进入处理 * Create a new FileSystemXmlApplicationContext, loading the definitions * from the given XML file and automatically refreshing the context. * @param configLocation file path @@ -86,6 +88,7 @@ public class FileSystemXmlApplicationContext extends AbstractXmlApplicationConte } /** + * 可以定义多个 BeanDefinition 所在的文件路径 * Create a new FileSystemXmlApplicationContext, loading the definitions * from the given XML files and automatically refreshing the context. * @param configLocations array of file paths @@ -96,6 +99,7 @@ public class FileSystemXmlApplicationContext extends AbstractXmlApplicationConte } /** + * 在定义多个 BeanDefinition 所在的文件路径 的同时,还能指定自己的双亲 IoC 容器 * Create a new FileSystemXmlApplicationContext with the given parent, * loading the definitions from the given XML files and automatically * refreshing the context. @@ -122,6 +126,7 @@ public class FileSystemXmlApplicationContext extends AbstractXmlApplicationConte } /** + * 如果应用直接使用 FileSystemXmlApplicationContext 进行实例化,则都会进到这个构造方法中来 * Create a new FileSystemXmlApplicationContext with the given parent, * loading the definitions from the given XML files. * @param configLocations array of file paths @@ -135,9 +140,11 @@ public class FileSystemXmlApplicationContext extends AbstractXmlApplicationConte public FileSystemXmlApplicationContext( String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException { - + // 动态地确定用哪个加载器去加载我们的配置文件 super(parent); + // 告诉读取器 配置文件放在哪里,该方法继承于爷类 AbstractRefreshableConfigApplicationContext setConfigLocations(configLocations); + // 容器初始化 if (refresh) { refresh(); } @@ -145,6 +152,9 @@ public class FileSystemXmlApplicationContext extends AbstractXmlApplicationConte /** + * + * 实例化一个 FileSystemResource 并返回,以便后续对资源的 IO 操作 + * 本方法是在其父类 DefaultResourceLoader 的 getResource 方法中被调用的, * Resolve resource paths as file system paths. *

Note: Even if a given path starts with a slash, it will get * interpreted as relative to the current VM working directory. -- Gitee From 685515d566782147eed173e394265cec79d8d5c0 Mon Sep 17 00:00:00 2001 From: chensj Date: Sat, 23 Mar 2024 20:03:18 +0800 Subject: [PATCH 41/44] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../support/AbstractBeanDefinitionReader.java | 23 +++++++++++++++---- .../support/AbstractApplicationContext.java | 10 ++++++++ ...AbstractRefreshableApplicationContext.java | 10 ++++++-- .../AbstractXmlApplicationContext.java | 23 +++++++++++++++++-- .../core/io/DefaultResourceLoader.java | 20 +++++++++++----- 5 files changed, 71 insertions(+), 15 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinitionReader.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinitionReader.java index a9a556caea..5159182a9d 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinitionReader.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinitionReader.java @@ -16,13 +16,8 @@ package org.springframework.beans.factory.support; -import java.io.IOException; -import java.util.Collections; -import java.util.Set; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.core.env.Environment; import org.springframework.core.env.EnvironmentCapable; @@ -34,6 +29,10 @@ import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.lang.Nullable; import org.springframework.util.Assert; +import java.io.IOException; +import java.util.Collections; +import java.util.Set; + /** * Abstract base class for bean definition readers which implement * the {@link BeanDefinitionReader} interface. @@ -180,9 +179,13 @@ public abstract class AbstractBeanDefinitionReader implements BeanDefinitionRead } + /** + * loadBeanDefinitions() 方法的重载方法之一,调用了另一个重载方法 loadBeanDefinitions(resource) + */ @Override public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException { Assert.notNull(resources, "Resource array must not be null"); + // 计数器,统计加载了多少个BeanDefinition int count = 0; for (Resource resource : resources) { count += loadBeanDefinitions(resource); @@ -211,6 +214,9 @@ public abstract class AbstractBeanDefinitionReader implements BeanDefinitionRead * @see #loadBeanDefinitions(org.springframework.core.io.Resource[]) */ public int loadBeanDefinitions(String location, @Nullable Set actualResources) throws BeanDefinitionStoreException { + // 获取 ResourceLoader,用于加载资源 + // 在ClasspathXmlApplicationContext中,父类AbstractXmlApplicationContext, + // 调用loadBeanDefinitions时候会初始化Reader,这个时候就会设置resourceLoader ResourceLoader resourceLoader = getResourceLoader(); if (resourceLoader == null) { throw new BeanDefinitionStoreException( @@ -220,7 +226,10 @@ public abstract class AbstractBeanDefinitionReader implements BeanDefinitionRead if (resourceLoader instanceof ResourcePatternResolver) { // Resource pattern matching available. try { + // 将指定位置的 BeanDefinition 资源文件解析为 IoC 容器封装的资源 + // 加载多个指定位置的 BeanDefinition 资源文件 Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); + // 委派调用其子类 XmlBeanDefinitionReader 的方法,实现加载功能 int count = loadBeanDefinitions(resources); if (actualResources != null) { Collections.addAll(actualResources, resources); @@ -249,10 +258,14 @@ public abstract class AbstractBeanDefinitionReader implements BeanDefinitionRead } } + /** + * loadBeanDefinitions() 方法的重载方法之一,调用了另一个重载方法 loadBeanDefinitions(String) + */ @Override public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException { Assert.notNull(locations, "Location array must not be null"); int count = 0; + // 计数器,统计加载了多少个BeanDefinition for (String location : locations) { count += loadBeanDefinitions(location); } diff --git a/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java b/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java index ef1d088993..6ee9e0d6a8 100644 --- a/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java +++ b/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java @@ -541,9 +541,11 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. + // 实例化所有剩余的(非懒加载)单例 Bean finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. + // 完成刷新过程,通知生命周期处理器 lifecycleProcessor 刷新过程 finishRefresh(); } @@ -554,9 +556,11 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader } // Destroy already created singletons to avoid dangling resources. + // 销毁已经创建的单例 Bean,以避免资源泄露 destroyBeans(); // Reset 'active' flag. + // 取消 refresh 操作,重置容器的同步标识 cancelRefresh(ex); // Propagate exception to caller. @@ -622,13 +626,18 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader } /** + * 告诉子类去刷新内部的 beanFactory * Tell the subclass to refresh the internal bean factory. * @return the fresh BeanFactory instance * @see #refreshBeanFactory() * @see #getBeanFactory() */ protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { + // 自己定义了抽象的 refreshBeanFactory() 方法,具体实现交给了自己的子类 refreshBeanFactory(); + // getBeanFactory() 也是一个抽象方法,交由子类实现 + // 看到这里是不是很容易想起 “模板方法模式”,父类在模板方法中定义好流程,定义好抽象方法 + // 具体实现交由子类完成 return getBeanFactory(); } @@ -699,6 +708,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader *

Must be called before singleton instantiation. */ protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) { + // 调用 BeanFactoryPostProcessor 的方法 PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors()); // Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime diff --git a/spring-context/src/main/java/org/springframework/context/support/AbstractRefreshableApplicationContext.java b/spring-context/src/main/java/org/springframework/context/support/AbstractRefreshableApplicationContext.java index 9c87844e9d..acb6fee93c 100644 --- a/spring-context/src/main/java/org/springframework/context/support/AbstractRefreshableApplicationContext.java +++ b/spring-context/src/main/java/org/springframework/context/support/AbstractRefreshableApplicationContext.java @@ -16,8 +16,6 @@ package org.springframework.context.support; -import java.io.IOException; - import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.DefaultListableBeanFactory; @@ -25,6 +23,8 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextException; import org.springframework.lang.Nullable; +import java.io.IOException; + /** * Base class for {@link org.springframework.context.ApplicationContext} * implementations which are supposed to support multiple calls to {@link #refresh()}, @@ -113,21 +113,27 @@ public abstract class AbstractRefreshableApplicationContext extends AbstractAppl /** + * 在这里完成了容器的初始化,并赋值给自己私有的 beanFactory 属性,为下一步调用做准备 * This implementation performs an actual refresh of this context's underlying * bean factory, shutting down the previous bean factory (if any) and * initializing a fresh bean factory for the next phase of the context's lifecycle. */ @Override protected final void refreshBeanFactory() throws BeansException { + // 如果已经存在 beanFactory,则销毁它 if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { + // 创建一个新的 beanFactory, DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); + // 定制化 beanFactory,如设置启动参数,开启注解的自动装配等 customizeBeanFactory(beanFactory); + // 加载 beanDefinitions, 在当前类中只定义了抽象的 loadBeanDefinitions() 方法,具体实现 调用子类容器 loadBeanDefinitions(beanFactory); + // 给自己的属性赋值 this.beanFactory = beanFactory; } catch (IOException ex) { diff --git a/spring-context/src/main/java/org/springframework/context/support/AbstractXmlApplicationContext.java b/spring-context/src/main/java/org/springframework/context/support/AbstractXmlApplicationContext.java index 85cb2250b5..68dd816c09 100644 --- a/spring-context/src/main/java/org/springframework/context/support/AbstractXmlApplicationContext.java +++ b/spring-context/src/main/java/org/springframework/context/support/AbstractXmlApplicationContext.java @@ -16,8 +16,6 @@ package org.springframework.context.support; -import java.io.IOException; - import org.springframework.beans.BeansException; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.xml.ResourceEntityResolver; @@ -26,6 +24,8 @@ import org.springframework.context.ApplicationContext; import org.springframework.core.io.Resource; import org.springframework.lang.Nullable; +import java.io.IOException; + /** * Convenient base class for {@link org.springframework.context.ApplicationContext} * implementations, drawing configuration from XML documents containing bean definitions @@ -72,6 +72,7 @@ public abstract class AbstractXmlApplicationContext extends AbstractRefreshableC /** + * 实现了基类 AbstractRefreshableApplicationContext 的抽象方法 * Loads the bean definitions via an XmlBeanDefinitionReader. * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader * @see #initBeanDefinitionReader @@ -80,17 +81,26 @@ public abstract class AbstractXmlApplicationContext extends AbstractRefreshableC @Override protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // Create a new XmlBeanDefinitionReader for the given BeanFactory. + // 创建一个Bean xml定义的读取器 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); + // 初始化 BeanDefinitionReader // Configure the bean definition reader with this context's // resource loading environment. beanDefinitionReader.setEnvironment(this.getEnvironment()); + // 设置资源加载器,因为基类实现了继承了DefaultResourceLoader beanDefinitionReader.setResourceLoader(this); + // 设置实体解析器 + // 设置 SAX 解析器,SAX(simple API for XML)是另一种 XML 解析方法。相比于 DOM,SAX 速度更快,占用内存更小。 + // 它逐行扫描文档,一边扫描一边解析。相比于先将整个 XML 文件扫描近内存,再进行解析的 DOM,SAX 可以在解析文档的 + // 任意时刻停止解析,但操作也比 DOM 复杂。 beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // Allow a subclass to provide custom initialization of the reader, // then proceed with actually loading the bean definitions. + // 初始化 BeanDefinitionReader, 该方法同时启用了 Xml 的校验机制 initBeanDefinitionReader(beanDefinitionReader); + // 加载Bean定义信息 loadBeanDefinitions(beanDefinitionReader); } @@ -107,6 +117,7 @@ public abstract class AbstractXmlApplicationContext extends AbstractRefreshableC } /** + * 用传进来的 XmlBeanDefinitionReader 读取器加载 Xml 文件中的 BeanDefinition * Load the bean definitions with the given XmlBeanDefinitionReader. *

The lifecycle of the bean factory is handled by the {@link #refreshBeanFactory} * method; hence this method is just supposed to load and/or register bean definitions. @@ -119,12 +130,20 @@ public abstract class AbstractXmlApplicationContext extends AbstractRefreshableC * @see #getResourcePatternResolver */ protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { + // 获取存放了 BeanDefinition 的所有 Resource,FileSystemXmlApplicationContext 类未对 + // getConfigResources() 进行重写,所以调用父类的,return null。 + // 而 ClassPathXmlApplicationContext 对该方法进行了重写,返回设置的值 Resource[] configResources = getConfigResources(); if (configResources != null) { + // XmlBeanDefinitionReader 调用其父类 AbstractBeanDefinitionReader 的方法加载 BeanDefinition reader.loadBeanDefinitions(configResources); } + // 获取配置文件路径 + // 调用父类 AbstractRefreshableConfigApplicationContext 的实现, + // 优先返回 FileSystemXmlApplicationContext 构造方法中调用 setConfigLocations() 方法设置的资源 String[] configLocations = getConfigLocations(); if (configLocations != null) { + // XmlBeanDefinitionReader 调用其父类 AbstractBeanDefinitionReader 的方法从配置位置加载 BeanDefinition reader.loadBeanDefinitions(configLocations); } } diff --git a/spring-core/src/main/java/org/springframework/core/io/DefaultResourceLoader.java b/spring-core/src/main/java/org/springframework/core/io/DefaultResourceLoader.java index 936c1d3e50..9e4bfecbf5 100644 --- a/spring-core/src/main/java/org/springframework/core/io/DefaultResourceLoader.java +++ b/spring-core/src/main/java/org/springframework/core/io/DefaultResourceLoader.java @@ -16,6 +16,12 @@ package org.springframework.core.io; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; +import org.springframework.util.ResourceUtils; +import org.springframework.util.StringUtils; + import java.net.MalformedURLException; import java.net.URL; import java.util.Collection; @@ -24,12 +30,6 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import org.springframework.lang.Nullable; -import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; -import org.springframework.util.ResourceUtils; -import org.springframework.util.StringUtils; - /** * Default implementation of the {@link ResourceLoader} interface. * Used by {@link ResourceEditor}, and serves as base class for @@ -151,19 +151,26 @@ public class DefaultResourceLoader implements ResourceLoader { } } + // 如果是文件路径,则走文件路径进行加载 if (location.startsWith("/")) { return getResourceByPath(location); } + // // 如果 location 是类路径的方式,返回 ClassPathResource 类型的文件资源对象 else if (location.startsWith(CLASSPATH_URL_PREFIX)) { return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader()); } else { try { // Try to parse the location as a URL... + // 如果是 URL 方式,返回 UrlResource 类型的文件资源对象, + // 否则将抛出的异常进入 catch 代码块,返回另一种资源对象 URL url = new URL(location); return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url)); } catch (MalformedURLException ex) { + // 如果既不是 classpath 标识,又不是 URL 标识的 Resource 定位,则调用容器本身的 + // getResourceByPath() 方法获取 Resource。根据实例化的子类对象,调用其子类对象中 + // 重写的此方法,如 FileSystemXmlApplicationContext 子类中对此方法的重写 // No URL -> resolve as resource path. return getResourceByPath(location); } @@ -171,6 +178,7 @@ public class DefaultResourceLoader implements ResourceLoader { } /** + * 返回一个ClassPathContextResource对象,该对象表示给定路径的资源。 * Return a Resource handle for the resource at the given path. *

The default implementation supports class path locations. This should * be appropriate for standalone implementations but can be overridden, -- Gitee From ea3edb195655cbc405de58a2d37fad90479f50a8 Mon Sep 17 00:00:00 2001 From: chensj Date: Wed, 27 Mar 2024 09:28:26 +0800 Subject: [PATCH 42/44] =?UTF-8?q?bean=E8=A7=A3=E6=9E=90=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../factory/support/BeanDefinitionReaderUtils.java | 3 +++ .../factory/support/DefaultListableBeanFactory.java | 11 +++++++++-- .../factory/xml/BeanDefinitionDocumentReader.java | 1 + .../xml/DefaultBeanDefinitionDocumentReader.java | 2 ++ .../beans/factory/xml/XmlBeanDefinitionReader.java | 10 ++++++++-- 5 files changed, 23 insertions(+), 4 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionReaderUtils.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionReaderUtils.java index 53aeefa195..502651b9ae 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionReaderUtils.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionReaderUtils.java @@ -165,10 +165,13 @@ public abstract class BeanDefinitionReaderUtils { throws BeanDefinitionStoreException { // Register bean definition under primary name. + // 获取Bean注册的名称 String beanName = definitionHolder.getBeanName(); + // 注册Bean registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // Register aliases for bean name, if any. + // 注册别名 String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String alias : aliases) { diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java index a85446a2c7..367f7b2c74 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java @@ -118,7 +118,9 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto /** Map from dependency type to corresponding autowired value. */ private final Map, Object> resolvableDependencies = new ConcurrentHashMap<>(16); - /** Map of bean definition objects, keyed by bean name. */ + /** Map of bean definition objects, keyed by bean name. + * IoC容器 的实际体现, key --> beanName,value --> BeanDefinition对象 + */ private final Map beanDefinitionMap = new ConcurrentHashMap<>(256); /** Map from bean name to merged BeanDefinitionHolder. */ @@ -130,7 +132,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto /** Map of singleton-only bean names, keyed by dependency type. */ private final Map, String[]> singletonBeanNamesByType = new ConcurrentHashMap<>(64); - /** List of bean definition names, in registration order. */ + /** List of bean definition names, in registration order.按注册顺序排列的 beanDefinition名称列表(即 beanName) */ private volatile List beanDefinitionNames = new ArrayList<>(256); /** List of names of manually registered singletons, in registration order. */ @@ -880,6 +882,9 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto // Implementation of BeanDefinitionRegistry interface //--------------------------------------------------------------------- + /** + * 向 IoC容器 注册解析的 beanName 和 BeanDefinition对象 + */ @Override public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { @@ -887,6 +892,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto Assert.hasText(beanName, "Bean name must not be empty"); Assert.notNull(beanDefinition, "BeanDefinition must not be null"); + // 校验解析的 BeanDefiniton对象 if (beanDefinition instanceof AbstractBeanDefinition) { try { //bean注册前做最后一次校验,这个校验是在与之前加载的xml文件之间进行 @@ -937,6 +943,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto if (hasBeanCreationStarted()) { // Cannot modify startup-time collection elements anymore (for stable iteration) // beanDefinitionMap全局变量,存在并发情况 + // 注册的过程中需要线程同步,以保证数据的一致性 synchronized (this.beanDefinitionMap) { this.beanDefinitionMap.put(beanName, beanDefinition); List updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1); diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/xml/BeanDefinitionDocumentReader.java b/spring-beans/src/main/java/org/springframework/beans/factory/xml/BeanDefinitionDocumentReader.java index 8bd14ff74a..ea852a1f2c 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/xml/BeanDefinitionDocumentReader.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/xml/BeanDefinitionDocumentReader.java @@ -21,6 +21,7 @@ import org.w3c.dom.Document; import org.springframework.beans.factory.BeanDefinitionStoreException; /** + * SPI 用于解析XML文档中包含Spring bean定义。 * SPI for parsing an XML document that contains Spring bean definitions. * Used by {@link XmlBeanDefinitionReader} for actually parsing a DOM document. * diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultBeanDefinitionDocumentReader.java b/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultBeanDefinitionDocumentReader.java index 96733d4c93..0997e1b2e8 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultBeanDefinitionDocumentReader.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultBeanDefinitionDocumentReader.java @@ -186,6 +186,7 @@ public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocume Element ele = (Element) node; // 判断是否为默认值命令空间 if (delegate.isDefaultNamespace(ele)) { + // 解析默认元素,基于spring 方式来解析bean parseDefaultElement(ele, delegate); } else { @@ -341,6 +342,7 @@ public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocume */ protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { // 解析bean定义到 BeanDefinitionHolder + // BeanDefinitionHolder是对BeanDefinition的进一步封装,持有一个BeanDefinition对象及其对应beanName、aliases别名 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { // 装饰bean 主要是用于自定义标签的解析中 diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/xml/XmlBeanDefinitionReader.java b/spring-beans/src/main/java/org/springframework/beans/factory/xml/XmlBeanDefinitionReader.java index eb1ec5293b..2355f4c1c2 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/xml/XmlBeanDefinitionReader.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/xml/XmlBeanDefinitionReader.java @@ -311,6 +311,7 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { /** + * XmlBeanDefinitionReader 加载资源的入口方法 * Load bean definitions from the specified XML file. * * @param resource the resource descriptor for the XML file @@ -319,10 +320,14 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { */ @Override public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { + // 调用本类的重载方法,通过 new EncodedResource(resource) 获得的 EncodedResource 对象 + // 能够将资源与读取资源所需的编码组合在一起 return loadBeanDefinitions(new EncodedResource(resource)); } /** + * + * 通过encodedResource进行资源解析,encodedResource对象持有resource对象和encoding编码格式 * Load bean definitions from the specified XML file. * * @param encodedResource the resource descriptor for the XML file, @@ -390,6 +395,7 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { /** + * 实际加载方法,从XML文件中解析bean,封装成BeanDefinition对象的具体实现 * Actually load bean definitions from the specified XML file. * * @param inputSource the SAX InputSource to read from @@ -403,7 +409,7 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { throws BeanDefinitionStoreException { try { - // 获取资源的文档信息 + // 获取资源的文档信息,通过documentLoader获取到document对象 Document doc = doLoadDocument(inputSource, resource); // 解析和注册 int count = registerBeanDefinitions(doc, resource); @@ -523,7 +529,7 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { * @see BeanDefinitionDocumentReader#registerBeanDefinitions */ public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { - // 获取默认的读取器 + // 获取默认的读取器,主要将xml文件中的bean定义转换成BeanDefinition对象 BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); // 获取已经读取bean总数 int countBefore = getRegistry().getBeanDefinitionCount(); -- Gitee From 6a6a7ae41777a077a93788f68722a069f1112953 Mon Sep 17 00:00:00 2001 From: chensj Date: Thu, 26 Sep 2024 17:57:08 +0800 Subject: [PATCH 43/44] https --- spring-demo/src/main/resources/META-INF/spring.handlers | 2 +- spring-demo/src/main/resources/META-INF/spring.schemas | 2 +- spring-demo/src/main/resources/META-INF/tag-user.xsd | 2 +- spring-demo/src/test/resources/spring-tag.xml | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/spring-demo/src/main/resources/META-INF/spring.handlers b/spring-demo/src/main/resources/META-INF/spring.handlers index dc4acf1772..0de71084b0 100644 --- a/spring-demo/src/main/resources/META-INF/spring.handlers +++ b/spring-demo/src/main/resources/META-INF/spring.handlers @@ -1 +1 @@ -http\://www.chensj.org/schema/spring-user=org.chensj.test.custom.handler.UserBeanNamespaceHandler \ No newline at end of file +https\://www.chensj.org/schema/spring-user=org.chensj.test.custom.handler.UserBeanNamespaceHandler \ No newline at end of file diff --git a/spring-demo/src/main/resources/META-INF/spring.schemas b/spring-demo/src/main/resources/META-INF/spring.schemas index 9bde9216cb..cb025ba17b 100644 --- a/spring-demo/src/main/resources/META-INF/spring.schemas +++ b/spring-demo/src/main/resources/META-INF/spring.schemas @@ -1 +1 @@ -http\://www.chensj.org/schema/user/spring-user.xsd=META-INF/tag-user.xsd \ No newline at end of file +https\://www.chensj.org/schema/user/spring-user.xsd=META-INF/tag-user.xsd \ No newline at end of file diff --git a/spring-demo/src/main/resources/META-INF/tag-user.xsd b/spring-demo/src/main/resources/META-INF/tag-user.xsd index d5f1006c83..20bf3c9c2b 100644 --- a/spring-demo/src/main/resources/META-INF/tag-user.xsd +++ b/spring-demo/src/main/resources/META-INF/tag-user.xsd @@ -1,6 +1,6 @@ diff --git a/spring-demo/src/test/resources/spring-tag.xml b/spring-demo/src/test/resources/spring-tag.xml index 48aa254abc..8830068eea 100644 --- a/spring-demo/src/test/resources/spring-tag.xml +++ b/spring-demo/src/test/resources/spring-tag.xml @@ -1,10 +1,10 @@ + https://www.springframework.org/schema/beans/spring-beans.xsd + https://www.chensj.org/schema/spring-user https://www.chensj.org/schema/user/spring-user.xsd"> \ No newline at end of file -- Gitee From 125a0d91552933aede903370e3222fae1f4c1339 Mon Sep 17 00:00:00 2001 From: chensj Date: Thu, 13 Mar 2025 23:23:58 +0800 Subject: [PATCH 44/44] =?UTF-8?q?=E5=AE=9E=E4=BE=8B=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AbstractAutowireCapableBeanFactory.java | 42 ++++++++++++++----- .../support/AbstractBeanDefinition.java | 2 + 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java index b95c30c4af..256a0d822b 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java @@ -16,15 +16,6 @@ package org.springframework.beans.factory.support; -import org.apache.commons.logging.Log; -import org.springframework.beans.*; -import org.springframework.beans.factory.*; -import org.springframework.beans.factory.config.*; -import org.springframework.core.*; -import org.springframework.lang.Nullable; -import org.springframework.util.*; -import org.springframework.util.ReflectionUtils.MethodCallback; - import java.beans.PropertyDescriptor; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; @@ -34,11 +25,27 @@ import java.security.AccessController; import java.security.PrivilegedAction; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.function.Supplier; +import org.apache.commons.logging.Log; +import org.springframework.beans.*; +import org.springframework.beans.factory.*; +import org.springframework.beans.factory.config.*; +import org.springframework.core.*; +import org.springframework.lang.Nullable; +import org.springframework.util.*; +import org.springframework.util.ReflectionUtils.MethodCallback; + /** * Abstract bean factory superclass that implements default bean creation, * with the full capabilities specified by the {@link RootBeanDefinition} class. @@ -440,7 +447,13 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac // Make sure bean class is actually resolved at this point, and // clone the bean definition in case of a dynamically resolved Class // which cannot be stored in the shared merged bean definition. + // 解析 Bean 的 Class 对象 Class resolvedClass = resolveBeanClass(mbd, beanName); + + // 如果满足以下三个条件,则需要创建一个新的 RootBeanDefinition: + // 1. resolvedClass 不为空(已成功解析到 Class) + // 2. !mbd.hasBeanClass()(原 BeanDefinition 中没有 Class 对象) + // 3. mbd.getBeanClassName() 不为空(有类名) if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) { mbdToUse = new RootBeanDefinition(mbd); mbdToUse.setBeanClass(resolvedClass); @@ -448,6 +461,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac // Prepare method overrides. try { + // lookup method overrides for this bean mbdToUse.prepareMethodOverrides(); } catch (BeanDefinitionValidationException ex) { @@ -1065,14 +1079,20 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac @Nullable protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) { Object bean = null; + // 如果 beforeInstantiationResolved 没有被设置为 false if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) { // Make sure bean class is actually resolved at this point. + // 如果不是合成的 Bean 且存在 InstantiationAwareBeanPostProcessor if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { + // 确定目标类型 Class targetType = determineTargetType(beanName, mbd); if (targetType != null) { + // InstantiationAwareBeanPostProcessor 的前置处理 bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName); + // 如果生成了代理对象,并且不为空,则进行后置处理 if (bean != null) { - bean = applyBeanPostProcessorsAfterInitialization(bean, beanName); + // 后置处理 + bean = applyBeanPostProcessorsAfterInitialization(bean, beanName); } } } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java index d4b0aef015..46a5215bf4 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java @@ -1130,6 +1130,7 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess */ public void prepareMethodOverrides() throws BeanDefinitionValidationException { // Check that lookup methods exist and determine their overloaded status. + // 如果没有方法重写,直接返回 if (hasMethodOverrides()) { getMethodOverrides().getOverrides().forEach(this::prepareMethodOverride); } @@ -1143,6 +1144,7 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess * @throws BeanDefinitionValidationException in case of validation failure */ protected void prepareMethodOverride(MethodOverride mo) throws BeanDefinitionValidationException { + // 获取对应方法的个数 int count = ClassUtils.getMethodCountForName(getBeanClass(), mo.getMethodName()); if (count == 0) { throw new BeanDefinitionValidationException( -- Gitee