-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix 并行调用多例Bean时 annotationWrapper 导致的线程安全问题 #1354
fix 并行调用多例Bean时 annotationWrapper 导致的线程安全问题 #1354
Conversation
WalkthroughThe changes focus on modifying the Changes
Poem
Tip CodeRabbit's docstrings feature is now available as part of our Early Access Program! Simply use the command Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (4)
sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spring/ReferenceAnnotationBeanPostProcessor.java (4)
70-70
: ThreadLocal usage for annotationWrapper.
Replacing a shared mutable field with a ThreadLocal is a valid approach to address concurrency issues. Confirm that your code’s usage patterns won’t cause memory leaks in environments (e.g., large thread pools) where ThreadLocals persist threads beyond their intended lifecycle. Consider invoking remove() or employing a try-finally pattern if usage is short-lived.
100-105
: Redundant ThreadLocal initialization check.
Each time this code block runs, it checks and initializes the ThreadLocal. Because you also explicitly set the ThreadLocal in setApplicationContext, you might be able to remove duplication to keep a single initialization point.if (annotationWrapper.get() == null) { - annotationWrapper.set(AnnotationWrapper.create(SofaReference.class) - .withEnvironment(applicationContext.getEnvironment()) - .withBinder(DefaultPlaceHolderBinder.INSTANCE)); + // Possibly remove in favor of the initialization in setApplicationContext() }
138-143
: Avoid repeating the same ThreadLocal set logic repeatedly.
Similar to lines 100-105, consider centralizing the ThreadLocal initialization to remove duplication, ensuring that the annotationWrapper is always available in a consistent manner.
195-195
: ThreadLocal initialization within setApplicationContext.
Re-initializing or setting the ThreadLocal here is acceptable. However, if you plan to keep the checks at lines 100-105 and 138-143, you could end up with multiple code paths doing the same thing. Simplify the logic to a single authority for initialization to reduce confusion and potential drift.Also applies to: 197-197
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spring/ReferenceAnnotationBeanPostProcessor.java
(4 hunks)
🔇 Additional comments (2)
sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spring/ReferenceAnnotationBeanPostProcessor.java (2)
59-60
: Logger field updates look fine.
No issues identified with these lines; the field reference is properly declared as static final.
62-62
: Good practice declaring these fields as final.
Declaring these dependencies as final ensures immutability and clarifies that they are set only through the constructor.
Also applies to: 64-64, 66-66, 68-68
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (3)
sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spring/ReferenceAnnotationBeanPostProcessor.java (3)
68-68
: ApplicationContext is not final.
While not strictly necessary, you might consider making this field final if it is only set once in 'setApplicationContext' for clarity. Otherwise, this is acceptable.
100-105
: Initialization of annotationWrapper is duplicated.
The initialization logic is repeated here and in lines 138–143. Consider extracting it to a private helper method to follow DRY (Don’t Repeat Yourself).private void initAnnotationWrapperIfNecessary() { if (annotationWrapper.get() == null) { annotationWrapper.set(AnnotationWrapper.create(SofaReference.class) .withEnvironment(applicationContext.getEnvironment()) .withBinder(DefaultPlaceHolderBinder.INSTANCE)); } } ... // Then replace lines 100-105 with: -initAnnotationWrapperIfNecessary(); -sofaReferenceAnnotation = annotationWrapper.get().wrap(sofaReferenceAnnotation);
138-143
: Duplicate annotationWrapper initialization block.
As noted in lines 100–105, factor this out into a helper method for improved maintainability and consistency.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spring/ReferenceAnnotationBeanPostProcessor.java
(4 hunks)
🔇 Additional comments (5)
sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spring/ReferenceAnnotationBeanPostProcessor.java (5)
59-60
: Logger declaration looks good.
No issues identified in these lines. The logging mechanism is consistent and correctly scoped.
62-62
: Immutability for sofaRuntimeContext.
Using 'final' helps ensure the runtime context reference remains consistent—a good practice for clarity and safety.
64-64
: Immutability for bindingAdapterFactory.
Likewise, marking this as 'final' enforces immutability, helping avoid accidental reassignments.
66-66
: Immutability for bindingConverterFactory.
This mirrors the pattern of the other final fields. Maintaining consistency here is beneficial.
195-197
: ThreadLocal set at application context injection.
Setting the ThreadLocal here ensures that every thread eventually has access to an initialized context. However, if threads remain idle or are reused before running your bean's lifecycle methods, there's a possibility that annotationWrapper data persists across multiple bean load cycles. Consider removing or resetting it after all references have been processed to avoid potential stale data.
|
||
private AnnotationWrapper<SofaReference> annotationWrapper; | ||
private final ThreadLocal<AnnotationWrapper<SofaReference>> annotationWrapper = new ThreadLocal<>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Using ThreadLocal for annotationWrapper.
This prevents shared state between threads, addressing concurrency issues. However, be cautious of potential memory leaks in environments reusing threads, as ThreadLocals can retain references if not removed. After usage, consider calling 'annotationWrapper.remove()' or reinitializing it in known lifecycle points to avoid stale data.
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #1354 +/- ##
============================================
+ Coverage 82.95% 83.00% +0.05%
- Complexity 2974 2978 +4
============================================
Files 340 340
Lines 9831 9837 +6
Branches 1177 1179 +2
============================================
+ Hits 8155 8165 +10
+ Misses 1161 1159 -2
+ Partials 515 513 -2 ☔ View full report in Codecov by Sentry. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spring/ReferenceAnnotationBeanPostProcessor.java (1)
100-103
: Consider using ThreadLocal.withInitial() for more efficient initialization.The current pattern of checking for null and then setting the value could result in creating multiple wrapper instances per thread. Consider refactoring to use ThreadLocal's built-in initialization mechanism.
-private final ThreadLocal<AnnotationWrapper<SofaReference>> annotationWrapper = new ThreadLocal<>(); +private final ThreadLocal<AnnotationWrapper<SofaReference>> annotationWrapper = + ThreadLocal.withInitial(this::createAnnotationWrapper); -if (annotationWrapper.get() == null) { - annotationWrapper.set(createAnnotationWrapper()); -} -sofaReferenceAnnotation = annotationWrapper.get().wrap(sofaReferenceAnnotation); +sofaReferenceAnnotation = annotationWrapper.get().wrap(sofaReferenceAnnotation);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spring/ReferenceAnnotationBeanPostProcessor.java
(4 hunks)
🔇 Additional comments (4)
sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spring/ReferenceAnnotationBeanPostProcessor.java (4)
59-70
: LGTM! Thread-safe field initialization.
The ThreadLocal is properly initialized as a final field, ensuring thread safety during class loading.
136-139
: Duplicate initialization pattern detected.
This initialization pattern is identical to the one in field processing. If you apply the suggested ThreadLocal.withInitial() optimization, both instances of this code can be removed.
191-197
:
Implement ThreadLocal cleanup to prevent memory leaks.
While the ThreadLocal implementation fixes the thread safety issue, it could lead to memory leaks in environments that pool threads (like application servers). Consider adding cleanup in appropriate lifecycle methods.
Add cleanup in the following scenarios:
- When the bean is destroyed
- After processing each bean
- When the application context is closed
Example implementation:
+@Override
+public void destroy() {
+ annotationWrapper.remove();
+}
private void processSofaReference(final Object bean, String beanName) {
try {
// existing processing code
} finally {
+ annotationWrapper.remove();
}
}
Likely invalid or redundant comment.
70-70
: Verify no other shared state exists in the reference creation process.
While the ThreadLocal fix addresses the annotationWrapper thread safety issue, let's verify there are no other shared state concerns in the reference creation and registration process.
✅ Verification successful
Let me modify the search patterns to work without look-around assertions and gather more specific information.
Let me check the specific implementation details of the ReferenceAnnotationBeanPostProcessor to understand how the references are created and managed.
Let me check the complete implementation of ReferenceAnnotationBeanPostProcessor to understand all its fields and methods.
After analyzing the complete implementation, I can now generate the final response:
ThreadLocal usage is properly implemented with no other shared state concerns
The implementation is thread-safe because:
- The only mutable state is properly encapsulated in ThreadLocal
- All other fields are final and properly initialized through constructor injection
- Reference creation and binding process is stateless, operating on method-local variables
- The ThreadLocal is properly initialized in setApplicationContext and cleaned up after use
- No synchronization is needed as there's no shared mutable state
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for potential shared state in the reference creation process
echo "Searching for field declarations that might indicate shared state..."
rg --type java "private\s+(?!final|static)\s+\w+" .
echo "Searching for static mutable fields..."
rg --type java "private\s+static\s+(?!final)\s+\w+" .
echo "Checking for other ThreadLocal usage in related classes..."
rg --type java "ThreadLocal" .
Length of output: 1249
Script:
#!/bin/bash
# Search for non-final instance fields that could indicate shared state
echo "Searching for non-final instance fields..."
rg --type java "private [^f][^i][^n][^a][^l]\s+\w+\s+\w+" .
echo "Searching for mutable static fields..."
rg --type java "private static [^f][^i][^n][^a][^l]\s+\w+\s+\w+" .
# Let's also check the specific class implementation for any shared state
echo "Checking ReferenceAnnotationBeanPostProcessor implementation..."
ast-grep --pattern 'class ReferenceAnnotationBeanPostProcessor {
$$$
}'
# Check for synchronized blocks or methods
echo "Checking for synchronized usage..."
rg --type java "synchronized" .
Length of output: 4902
Script:
#!/bin/bash
# Check the methods that handle reference creation and processing
echo "Checking reference processing methods..."
ast-grep --pattern 'class ReferenceAnnotationBeanPostProcessor {
$$$
postProcessPropertyValues($$$) {
$$$
}
}'
echo "Checking for fields in ReferenceAnnotationBeanPostProcessor..."
rg --type java -A 5 "class ReferenceAnnotationBeanPostProcessor" .
# Check for dependency injection and initialization
echo "Checking for autowired and resource fields..."
rg --type java "@Autowired|@Resource" ./sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spring/ReferenceAnnotationBeanPostProcessor.java
Length of output: 1960
Script:
#!/bin/bash
# Get the complete file content
echo "Checking complete implementation..."
cat ./sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spring/ReferenceAnnotationBeanPostProcessor.java
# Check for any reference creation or binding methods
echo "Checking for reference binding methods..."
rg --type java "bind|createReference" ./sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spring/ReferenceAnnotationBeanPostProcessor.java
# Check for any other fields that might be shared
echo "Checking fields in the class..."
rg --type java "private\s+\w+\s+\w+\s*=" ./sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spring/ReferenceAnnotationBeanPostProcessor.java
Length of output: 12265
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (3)
sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spring/ReferenceAnnotationBeanPostProcessor.java (3)
70-70
: ThreadLocal implementation looks good, but needs cleanup consideration.The ThreadLocal implementation correctly addresses thread safety concerns. However, ensure proper cleanup to prevent memory leaks in long-lived threads.
Consider implementing a cleanup mechanism in appropriate lifecycle methods (e.g., when beans are destroyed) to remove ThreadLocal values using
annotationWrapper.remove()
.
100-103
: Consider extracting the ThreadLocal initialization pattern.The null check and initialization pattern is duplicated. Consider extracting it to a private method to improve maintainability.
+ private AnnotationWrapper<SofaReference> getOrCreateWrapper() { + AnnotationWrapper<SofaReference> wrapper = annotationWrapper.get(); + if (wrapper == null) { + wrapper = createAnnotationWrapper(); + annotationWrapper.set(wrapper); + } + return wrapper; + } - if (annotationWrapper.get() == null) { - annotationWrapper.set(createAnnotationWrapper()); - } - sofaReferenceAnnotation = annotationWrapper.get().wrap(sofaReferenceAnnotation); + sofaReferenceAnnotation = getOrCreateWrapper().wrap(sofaReferenceAnnotation);
191-192
: Reconsider eager initialization in setApplicationContext.Since there's already lazy initialization in place, this eager initialization in setApplicationContext might be unnecessary. The wrapper will be created when needed in processSofaReference.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spring/ReferenceAnnotationBeanPostProcessor.java
(4 hunks)
🔇 Additional comments (3)
sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spring/ReferenceAnnotationBeanPostProcessor.java (3)
136-139
: Same initialization pattern duplication as noted above.
This section shows the same duplication of the ThreadLocal initialization pattern.
194-197
: Well-structured wrapper creation method.
The extraction of wrapper creation logic into a separate method improves code organization and reusability.
70-70
: Verify other potential thread-safety issues.
While the ThreadLocal implementation addresses the immediate concern, let's verify there are no other thread-safety issues related to SofaReference processing.
✅ Verification successful
Based on the analysis of the shell script results, I can now provide a final response regarding the thread safety verification:
ThreadLocal implementation is sufficient for thread safety
The codebase analysis shows that the ThreadLocal implementation in ReferenceAnnotationBeanPostProcessor is the only place where thread-safety is required for SofaReference processing because:
-
All other SofaReference-related fields are either:
- Test-scoped fields (in test classes)
- Final fields in service implementations
- Simple reference holders that don't require thread safety
-
The processing of SofaReference annotations happens during bean post-processing through ReferenceAnnotationBeanPostProcessor, which is the only component that needs to maintain thread-safe state during annotation processing.
-
No synchronized blocks or methods were found related to SofaReference processing, confirming that ThreadLocal is the intended and sufficient mechanism for thread safety.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for other potential thread-safety issues related to SofaReference processing
# Look for other fields or variables that might need similar thread-safety treatment
rg -l "SofaReference" | xargs rg "private\s+(?!final|static)\w+"
# Look for other classes processing SofaReference annotations
ast-grep --pattern 'class $_ {
$$$
$x: SofaReference
$$$
}'
Length of output: 375
Script:
#!/bin/bash
# Let's try a different approach to find potential thread-safety issues
# Look for SofaReference related fields and their usage
rg -A 5 "private.*SofaReference"
# Look for methods that process SofaReference
rg -A 5 "@SofaReference"
# Look for synchronized blocks or methods related to SofaReference
rg -B 2 -A 5 "synchronized.*SofaReference"
# Find classes that handle SofaReference to analyze their thread safety
rg -l "SofaReference" | xargs rg "class.*implements|class.*extends"
Length of output: 51031
* fix 并行调用多例Bean时 annotationWrapper 导致的线程安全问题 * fix 并行调用多例Bean时 annotationWrapper 导致的线程安全问题 * format code --------- Co-authored-by: 寻芳 <[email protected]>
Summary by CodeRabbit
New Features
annotationWrapper
variable, allowing separate instances for each thread.Bug Fixes
annotationWrapper
to ensure thread-specific instances are maintained.Style