Repository: xuchengsheng/spring-reading Branch: master Commit: 9911cbb4df10 Files: 774 Total size: 2.3 MB Directory structure: gitextract_1jka44ep/ ├── .gitignore ├── LICENSE ├── README.md ├── index.html ├── pom.xml ├── spring-annotation/ │ ├── pom.xml │ ├── spring-annotation-autowired/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── AutowiredApplication.java │ │ ├── config/ │ │ │ └── MyConfiguration.java │ │ ├── controller/ │ │ │ └── MyController.java │ │ └── service/ │ │ └── MyService.java │ ├── spring-annotation-bean/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── BeanApplication.java │ │ ├── bean/ │ │ │ └── MyBean.java │ │ └── config/ │ │ └── MyConfiguration.java │ ├── spring-annotation-componentScan/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── ComponentScanApplication.java │ │ ├── config/ │ │ │ └── MyConfiguration.java │ │ ├── repository/ │ │ │ └── UserRepository.java │ │ ├── service/ │ │ │ ├── AdminService.java │ │ │ └── UserService.java │ │ └── special/ │ │ └── SpecialComponent.java │ ├── spring-annotation-conditional/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── bean/ │ │ │ ├── ConditionBeanApplication.java │ │ │ ├── condition/ │ │ │ │ └── BeanPropertyCondition.java │ │ │ ├── config/ │ │ │ │ └── MyBeanConfiguration.java │ │ │ └── entity/ │ │ │ ├── User1.java │ │ │ └── User2.java │ │ ├── configuration/ │ │ │ ├── ConditionConfigurationApplication.java │ │ │ ├── condition/ │ │ │ │ └── ConfigPropertyCondition.java │ │ │ ├── config/ │ │ │ │ └── MyConfigConfiguration.java │ │ │ └── entity/ │ │ │ ├── User3.java │ │ │ └── User4.java │ │ └── custom/ │ │ ├── ConditionCustomApplication.java │ │ ├── annotation/ │ │ │ └── ConditionalOnCustomActive.java │ │ ├── condition/ │ │ │ └── CustomActiveCondition.java │ │ ├── config/ │ │ │ └── MyCustomConfiguration.java │ │ └── entity/ │ │ ├── User5.java │ │ └── User6.java │ ├── spring-annotation-configuration/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── ConfigurationApplication.java │ │ ├── bean/ │ │ │ └── MyBean.java │ │ └── config/ │ │ └── MyConfiguration.java │ ├── spring-annotation-dependsOn/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── DependsOnApplication.java │ │ ├── bean/ │ │ │ ├── BeanA.java │ │ │ ├── BeanB.java │ │ │ └── BeanC.java │ │ └── config/ │ │ └── MyConfiguration.java │ ├── spring-annotation-import/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── ImportApplication.java │ │ ├── bean/ │ │ │ ├── MyBean.java │ │ │ ├── MyBeanA.java │ │ │ ├── MyBeanB.java │ │ │ └── MyBeanC.java │ │ └── config/ │ │ ├── MyConfiguration.java │ │ ├── MyDeferredImportSelector.java │ │ ├── MyImportBeanDefinitionRegistrar.java │ │ └── MyImportSelector.java │ ├── spring-annotation-lazy/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── LazyApplication.java │ │ ├── bean/ │ │ │ └── MyBean.java │ │ ├── config/ │ │ │ └── MyConfiguration.java │ │ └── service/ │ │ └── MyService.java │ ├── spring-annotation-profile/ │ │ ├── README.md │ │ └── pom.xml │ ├── spring-annotation-propertySource/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── xcs/ │ │ │ └── spring/ │ │ │ ├── PropertySourceApplication.java │ │ │ └── config/ │ │ │ └── MyConfiguration.java │ │ └── resources/ │ │ └── my-application.yml │ └── spring-annotation-value/ │ ├── README.md │ ├── pom.xml │ └── src/ │ └── main/ │ ├── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── ValueApplication.java │ │ ├── config/ │ │ │ └── MyConfiguration.java │ │ └── service/ │ │ └── MyService.java │ └── resources/ │ └── application.properties ├── spring-aop/ │ ├── pom.xml │ ├── spring-aop-advice/ │ │ ├── README.md │ │ └── pom.xml │ ├── spring-aop-advice-afterReturningAdvice/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── AfterReturningAdviceDemo.java │ │ ├── MyAfterReturningAdvice.java │ │ └── MyService.java │ ├── spring-aop-advice-introductionInterceptor/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── IntroductionInterceptorDemo.java │ │ ├── MyMonitoringCapable.java │ │ ├── MyMonitoringIntroductionAdvice.java │ │ └── MyService.java │ ├── spring-aop-advice-methodBeforeAdvice/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── MethodBeforeAdviceDemo.java │ │ ├── MyMethodBeforeAdvice.java │ │ └── MyService.java │ ├── spring-aop-advice-methodInterceptor/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── MethodInterceptorDemo.java │ │ ├── MyMethodInterceptor.java │ │ └── MyService.java │ ├── spring-aop-advice-throwsAdvice/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── MyService.java │ │ ├── MyThrowsAdvice.java │ │ └── ThrowsAdviceDemo.java │ ├── spring-aop-advised/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── AdvisedDemo.java │ │ ├── MyService.java │ │ └── MyServiceImpl.java │ ├── spring-aop-advisor/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── AdvisorDemo.java │ │ ├── MyAdvice.java │ │ ├── MyCustomAdvisor.java │ │ ├── MyCustomAnnotation.java │ │ └── MyService.java │ ├── spring-aop-advisorAdapter/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── AdvisorAdapterDemo.java │ │ ├── MyNullReturningAdvice.java │ │ ├── MyService.java │ │ ├── NullReturningAdvice.java │ │ ├── NullReturningAdviceAdapter.java │ │ └── NullReturningAdviceInterceptor.java │ ├── spring-aop-advisorAdapterRegistry/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── AdvisorAdapterRegistryDemo.java │ │ └── MyMethodBeforeAdvice.java │ ├── spring-aop-advisorChainFactory/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── AdvisorChainFactoryDemo.java │ │ ├── MyAfterReturningAdvice.java │ │ ├── MyMethodBeforeAdvice.java │ │ └── MyService.java │ ├── spring-aop-annotationAwareAspectJAutoProxyCreator/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── AnnotationAwareAspectJAutoProxyCreatorDemo.java │ │ ├── AppConfig.java │ │ ├── MyAspect.java │ │ └── MyService.java │ ├── spring-aop-aopContext/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── AopContextDemo.java │ │ ├── MyAnnotation.java │ │ ├── MyMethodBeforeAdvice.java │ │ └── MyService.java │ ├── spring-aop-aopProxy/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── AopProxyDemo.java │ │ ├── MyMethodInterceptor.java │ │ ├── MyService.java │ │ └── MyServiceImpl.java │ ├── spring-aop-aopProxyFactory/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── AopProxyFactoryDemo.java │ │ ├── MyService.java │ │ └── MyServiceImpl.java │ ├── spring-aop-aspectInstanceFactory/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── AspectInstanceFactoryDemo.java │ │ └── MyAspect.java │ ├── spring-aop-aspectJAdvisorFactory/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── AspectJAdvisorFactoryDemo.java │ │ ├── MyAspect.java │ │ └── MyService.java │ ├── spring-aop-beanFactoryAdvisorRetrievalHelper/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── AppConfig.java │ │ ├── BeanFactoryAdvisorRetrievalHelperDemo.java │ │ ├── MyAdvice.java │ │ ├── MyAdvisor.java │ │ └── MyService.java │ ├── spring-aop-beanFactoryAspectJAdvisorsBuilder/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── AppConfig.java │ │ ├── BeanFactoryAspectJAdvisorsBuilderDemo.java │ │ ├── MyAspect.java │ │ └── MyService.java │ ├── spring-aop-cglibProxy/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── CglibProxyDemo.java │ │ ├── MyMethodInterceptor.java │ │ ├── MyService.java │ │ └── MyServiceImpl.java │ ├── spring-aop-classFilter/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── ClassFilterDemo.java │ │ ├── MyClassAnnotation.java │ │ ├── MyService.java │ │ └── MySubService.java │ ├── spring-aop-enableAspectJAutoProxy/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── AppConfig.java │ │ ├── EnableAspectJAutoProxyDemo.java │ │ ├── MyAspect.java │ │ └── MyService.java │ ├── spring-aop-enableLoadTimeWeaving/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── xcs/ │ │ │ └── spring/ │ │ │ ├── AppConfig.java │ │ │ ├── EnableLoadTimeWeavingDemo.java │ │ │ ├── MyLTWAspect.java │ │ │ └── MyService.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── aop.xml │ ├── spring-aop-exposeInvocationInterceptor/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── AppConfig.java │ │ ├── ExposeInvocationInterceptorDemo.java │ │ ├── LogUtil.java │ │ ├── MyMethodInterceptor.java │ │ └── MyService.java │ ├── spring-aop-jdkProxy/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── JdkProxyDemo.java │ │ ├── MyInvocationHandler.java │ │ ├── MyService.java │ │ └── MyServiceImpl.java │ ├── spring-aop-metadataAwareAspectInstanceFactory/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── MetadataAwareAspectInstanceFactoryDemo.java │ │ └── MyAspect.java │ ├── spring-aop-methodMatcher/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── MethodMatcherDemo.java │ │ ├── MyMethodAnnotation.java │ │ └── MyService.java │ ├── spring-aop-pointcut/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── MyClassAnnotation.java │ │ ├── MyCustomAdvice.java │ │ ├── MyCustomPointcut.java │ │ ├── MyMethodAnnotation.java │ │ ├── MyService.java │ │ └── PointcutDemo.java │ ├── spring-aop-proxyFactory/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── MyService.java │ │ └── ProxyFactoryDemo.java │ ├── spring-aop-proxyMethodInvocation/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── MyInvocationHandler.java │ │ ├── MyMethodInterceptor.java │ │ ├── MyReflectiveMethodInvocation.java │ │ ├── MyService.java │ │ ├── MyServiceImpl.java │ │ └── ProxyMethodInvocationDemo.java │ ├── spring-aop-targetSource/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── ConnectionPoolTargetSource.java │ │ ├── MyConnection.java │ │ └── TargetSourceDemo.java │ └── spring-aop-targetSourceCreator/ │ ├── README.md │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── com/ │ └── xcs/ │ └── spring/ │ ├── AppConfig.java │ ├── ConnectionPoolTargetSource.java │ ├── MyConnection.java │ ├── MyTargetSourceCreator.java │ ├── SetMyTargetSourceCreator.java │ └── TargetSourceCreatorDemo.java ├── spring-aware/ │ ├── pom.xml │ ├── spring-aware-applicationContextAware/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── ApplicationContextAwareApplication.java │ │ └── config/ │ │ ├── MyApplicationContextAware.java │ │ └── MyConfiguration.java │ ├── spring-aware-applicationEventPublisherAware/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── ApplicationEventPublisherAwareApplication.java │ │ ├── config/ │ │ │ ├── MyApplicationEventPublisherAware.java │ │ │ └── MyConfiguration.java │ │ └── event/ │ │ ├── MyEvent.java │ │ └── MyEventListener.java │ ├── spring-aware-applicationStartupAware/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── ApplicationStartupAwareApplication.java │ │ └── config/ │ │ ├── MyApplicationStartupAware.java │ │ └── MyConfiguration.java │ ├── spring-aware-beanClassLoaderAware/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── BeanClassLoaderAwareApplication.java │ │ ├── config/ │ │ │ ├── MyBeanClassLoaderAware.java │ │ │ └── MyConfiguration.java │ │ └── service/ │ │ ├── UserService.java │ │ └── UserServiceImpl.java │ ├── spring-aware-beanFactoryAware/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── BeanFactoryAwareApplication.java │ │ ├── config/ │ │ │ └── MyConfiguration.java │ │ ├── service/ │ │ │ └── UserService.java │ │ └── validate/ │ │ ├── ComplexUserValidator.java │ │ ├── SimpleUserValidator.java │ │ └── UserValidator.java │ ├── spring-aware-beanNameAware/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── BeanNameAwareApplication.java │ │ ├── config/ │ │ │ └── MyConfiguration.java │ │ └── service/ │ │ ├── MyAliPayService.java │ │ ├── MyBasePayService.java │ │ └── MyWeChatPayService.java │ ├── spring-aware-embeddedValueResolverAware/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── EmbeddedValueResolverAwareApplication.java │ │ └── config/ │ │ ├── MyConfiguration.java │ │ └── MyEmbeddedValueResolverAware.java │ ├── spring-aware-environmentAware/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── xcs/ │ │ │ └── spring/ │ │ │ ├── EnvironmentAwareApplication.java │ │ │ └── config/ │ │ │ ├── MyConfiguration.java │ │ │ └── MyEnvironmentAware.java │ │ └── resources/ │ │ └── application.properties │ ├── spring-aware-importAware/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── ImportAwareApplication.java │ │ ├── annotation/ │ │ │ └── EnableXcs.java │ │ └── config/ │ │ ├── MyConfiguration.java │ │ └── MyImportAware.java │ ├── spring-aware-messageSourceAware/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── xcs/ │ │ │ └── spring/ │ │ │ ├── MessageSourceAwareApplication.java │ │ │ └── config/ │ │ │ ├── MyConfiguration.java │ │ │ └── MyMessageSourceAware.java │ │ └── resources/ │ │ └── i18n/ │ │ ├── messages_en.properties │ │ └── messages_zh_CN.properties │ └── spring-aware-resourceLoaderAware/ │ ├── README.md │ ├── pom.xml │ └── src/ │ └── main/ │ ├── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── ResourceLoaderAwareApplication.java │ │ └── config/ │ │ ├── MyConfiguration.java │ │ └── MyResourceLoaderAware.java │ └── resources/ │ └── xcs.txt ├── spring-beans/ │ ├── pom.xml │ ├── spring-bean-annotatedBeanDefinitionReader/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── AnnotatedBeanDefinitionReaderDemo.java │ │ └── bean/ │ │ └── MyBean.java │ ├── spring-bean-beanDefinition/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── BeanDefinitionDemo.java │ │ └── bean/ │ │ └── MyBean.java │ ├── spring-bean-beanDefinitionHolder/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── BeanDefinitionHolderDemo.java │ │ └── bean/ │ │ └── MyBean.java │ ├── spring-bean-beanDefinitionRegistry/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── BeanDefinitionRegistryDemo.java │ │ └── bean/ │ │ └── MyBean.java │ ├── spring-bean-classPathBeanDefinitionScanner/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── ClassPathBeanDefinitionScannerDemo.java │ │ ├── controller/ │ │ │ └── MyController.java │ │ ├── repository/ │ │ │ └── MyRepository.java │ │ └── service/ │ │ └── MyService.java │ ├── spring-bean-groovyBeanDefinitionReader/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── xcs/ │ │ │ └── spring/ │ │ │ ├── GroovyBeanDefinitionReaderDemo.java │ │ │ └── service/ │ │ │ └── MyService.java │ │ └── resources/ │ │ └── my-beans.groovy │ ├── spring-bean-propertiesBeanDefinitionReader/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── xcs/ │ │ │ └── spring/ │ │ │ ├── PropertiesBeanDefinitionReaderDemo.java │ │ │ └── bean/ │ │ │ └── MyBean.java │ │ └── resources/ │ │ └── bean-definitions.properties │ └── spring-bean-xmlBeanDefinitionReader/ │ ├── README.md │ ├── pom.xml │ └── src/ │ └── main/ │ ├── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── XmlBeanDefinitionReaderDemo.java │ │ └── bean/ │ │ └── MyBean.java │ └── resources/ │ └── beans.xml ├── spring-context/ │ ├── pom.xml │ ├── spring-context-annotationConfigApplicationContext/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── AnnotationConfigApplicationContextDemo.java │ │ ├── bean/ │ │ │ └── MyBean.java │ │ ├── controller/ │ │ │ └── MyController.java │ │ ├── repository/ │ │ │ └── MyRepository.java │ │ └── service/ │ │ └── MyService.java │ ├── spring-context-classPathXmlApplicationContext/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── xcs/ │ │ │ └── spring/ │ │ │ ├── ClassPathXmlApplicationContextDemo.java │ │ │ └── bean/ │ │ │ └── MyBean.java │ │ └── resources/ │ │ └── beans.xml │ └── spring-context-genericApplicationContext/ │ └── pom.xml ├── spring-core/ │ ├── pom.xml │ ├── spring-core-destroyBean/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── DestroyBeanApplication.java │ │ └── bean/ │ │ └── MyBean.java │ ├── spring-core-getBean/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── GetBeanApplication.java │ │ ├── config/ │ │ │ └── MyConfiguration.java │ │ └── service/ │ │ ├── MyServiceA.java │ │ └── MyServiceB.java │ ├── spring-core-registerBeanDefinition/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── RegisterBeanDefinitionApplication.java │ │ ├── bean/ │ │ │ └── MyBean.java │ │ ├── controller/ │ │ │ └── MyController.java │ │ ├── repository/ │ │ │ └── MyRepository.java │ │ └── service/ │ │ └── MyService.java │ └── spring-core-resolveDependency/ │ ├── README.md │ ├── pom.xml │ └── src/ │ └── main/ │ ├── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── ResolveDependencyApplication.java │ │ ├── config/ │ │ │ └── MyConfiguration.java │ │ └── service/ │ │ ├── MyServiceA.java │ │ └── MyServiceB.java │ └── resources/ │ └── application.properties ├── spring-dataops/ │ ├── pom.xml │ ├── spring-dataops-conditionalConverter/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── ConditionalConverterDemo.java │ │ └── converter/ │ │ └── StringToIntegerConditionalConverter.java │ ├── spring-dataops-conversionService/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ └── ConversionServiceDemo.java │ ├── spring-dataops-converter/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── ConverterDemo.java │ │ └── converter/ │ │ ├── StringToBooleanConverter.java │ │ └── StringToLocalDateConverter.java │ ├── spring-dataops-converterFactory/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── CharacterToNumberFactoryDemo.java │ │ └── converter/ │ │ └── StringToNumberConverterFactory.java │ ├── spring-dataops-genericConverter/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── GenericConverterDemo.java │ │ ├── annotation/ │ │ │ └── DateFormat.java │ │ ├── bean/ │ │ │ └── MyBean.java │ │ └── convert/ │ │ └── AnnotatedStringToDateConverter.java │ ├── spring-dataops-parser/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── ParserDemo.java │ │ └── parser/ │ │ └── CurrencyParser.java │ ├── spring-dataops-printer/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── PrinterDemo.java │ │ └── printer/ │ │ └── CurrencyPrinter.java │ ├── spring-dataops-propertyEditor/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── MyCustomDateEditor.java │ │ ├── PropertyEditorDemo.java │ │ └── bean/ │ │ └── MyBean.java │ └── spring-dataops-validator/ │ ├── README.md │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── com/ │ └── xcs/ │ └── spring/ │ ├── Person.java │ ├── PersonValidator.java │ └── ValidatorDemo.java ├── spring-env/ │ ├── pom.xml │ ├── spring-env-configurableEnvironment/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ └── ConfigurableEnvironmentDemo.java │ ├── spring-env-configurablePropertyResolver/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ └── ConfigurablePropertyResolverDemo.java │ ├── spring-env-environment/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ └── EnvironmentDemo.java │ ├── spring-env-propertyResolver/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ └── SimplePropertyResolverDemo.java │ ├── spring-env-propertySource/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── xcs/ │ │ │ └── spring/ │ │ │ └── PropertySourceDemo.java │ │ └── resources/ │ │ └── application.properties │ └── spring-env-propertySources/ │ ├── README.md │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── com/ │ └── xcs/ │ └── spring/ │ └── PropertySourcesDemo.java ├── spring-factory/ │ ├── pom.xml │ ├── spring-factory-autowireCapableBeanFactory/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── AutowireCapableBeanFactoryDemo.java │ │ ├── config/ │ │ │ ├── MyBeanPostProcessor.java │ │ │ └── MyConfiguration.java │ │ ├── repository/ │ │ │ └── MyRepository.java │ │ └── service/ │ │ └── MyService.java │ ├── spring-factory-beanFactory/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── xcs/ │ │ │ └── spring/ │ │ │ ├── BeanFactoryDemo.java │ │ │ └── bean/ │ │ │ └── MyBean.java │ │ └── resources/ │ │ └── beans.xml │ ├── spring-factory-configurableBeanFactory/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── ConfigurableBeanFactoryDemo.java │ │ ├── config/ │ │ │ └── MyConfiguration.java │ │ └── service/ │ │ ├── MyService.java │ │ └── impl/ │ │ └── MyServiceImpl.java │ ├── spring-factory-configurableListableBeanFactory/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── ConfigurableListableBeanFactoryDemo.java │ │ ├── config/ │ │ │ └── MyConfiguration.java │ │ ├── controller/ │ │ │ └── MyController.java │ │ └── service/ │ │ ├── MyService.java │ │ └── impl/ │ │ └── MyServiceImpl.java │ ├── spring-factory-hierarchicalBeanFactory/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── HierarchicalBeanFactoryDemo.java │ │ └── bean/ │ │ └── MyBean.java │ └── spring-factory-listableBeanFactory/ │ ├── README.md │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── com/ │ └── xcs/ │ └── spring/ │ ├── ListableBeanFactoryDemo.java │ ├── config/ │ │ └── MyConfiguration.java │ └── service/ │ └── MyService.java ├── spring-interface/ │ ├── pom.xml │ ├── spring-interface-beanDefinitionRegistryPostProcessor/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── BeanDefinitionRegistryPostProcessorApplication.java │ │ └── config/ │ │ ├── MyBeanDefinitionRegistryPostProcessor.java │ │ ├── MyConfiguration.java │ │ └── MySimpleBean.java │ ├── spring-interface-beanFactoryPostProcessor/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── BeanFactoryPostProcessorApplication.java │ │ └── config/ │ │ ├── MyBeanFactoryPostProcessor.java │ │ ├── MyConfiguration.java │ │ └── MySimpleBean.java │ ├── spring-interface-beanPostProcessor/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── BeanPostProcessorApplication.java │ │ ├── config/ │ │ │ ├── MyBeanPostProcessor.java │ │ │ └── MyConfiguration.java │ │ └── service/ │ │ ├── MyService.java │ │ └── MyServiceImpl.java │ ├── spring-interface-destructionAwareBeanPostProcessor/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── DestructionAwareBeanPostProcessorApplication.java │ │ ├── config/ │ │ │ ├── MyConfiguration.java │ │ │ └── MyDestructionAwareBeanPostProcessor.java │ │ └── service/ │ │ ├── ConnectionService.java │ │ └── ConnectionServiceImpl.java │ ├── spring-interface-disposableBean/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── DisposableBeanApplication.java │ │ └── config/ │ │ ├── MyConfiguration.java │ │ └── MyDisposableBean.java │ ├── spring-interface-initializingBean/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── InitializingBeanApplication.java │ │ └── config/ │ │ ├── MyConfiguration.java │ │ └── MyInitializingBean.java │ ├── spring-interface-instantiationAwareBeanPostProcessor/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── InstantiationAwareBeanPostProcessorApplication.java │ │ ├── config/ │ │ │ ├── MyConfiguration.java │ │ │ └── MyInstantiationAwareBeanPostProcessor.java │ │ └── service/ │ │ ├── DataBase.java │ │ └── DataBaseImpl.java │ ├── spring-interface-mergedBeanDefinitionPostProcessor/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── MergedBeanDefinitionPostProcessorApplication.java │ │ ├── annotation/ │ │ │ └── MyValue.java │ │ ├── bean/ │ │ │ └── MyBean.java │ │ └── config/ │ │ ├── MyConfiguration.java │ │ └── MyMergedBeanDefinitionPostProcessor.java │ ├── spring-interface-smartInitializingSingleton/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── SmartInitializingSingletonApplication.java │ │ ├── config/ │ │ │ ├── MyConfiguration.java │ │ │ └── MySmartInitializingSingleton.java │ │ └── service/ │ │ └── MyService.java │ └── spring-interface-smartInstantiationAwareBeanPostProcessor/ │ ├── README.md │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── com/ │ └── xcs/ │ └── spring/ │ ├── SmartInstantiationAwareBeanPostProcessorApplication.java │ ├── annotation/ │ │ └── MyAutowired.java │ ├── config/ │ │ ├── MyConfiguration.java │ │ └── MySmartInstantiationAwareBeanPostProcessor.java │ └── service/ │ ├── MyService.java │ ├── MyServiceA.java │ └── MyServiceB.java ├── spring-jsr/ │ ├── pom.xml │ ├── spring-jsr250-postConstruct/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── PostConstructApplication.java │ │ ├── config/ │ │ │ └── MyConfiguration.java │ │ └── service/ │ │ └── MyService.java │ ├── spring-jsr250-preDestroy/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── PreDestroyApplication.java │ │ ├── config/ │ │ │ └── MyConfiguration.java │ │ └── service/ │ │ └── MyService.java │ ├── spring-jsr250-resource/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── ResourceApplication.java │ │ ├── config/ │ │ │ └── MyConfiguration.java │ │ ├── controller/ │ │ │ └── MyController.java │ │ └── service/ │ │ └── MyService.java │ ├── spring-jsr330-inject/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── InjectApplication.java │ │ ├── config/ │ │ │ └── MyConfiguration.java │ │ ├── controller/ │ │ │ └── MyController.java │ │ └── service/ │ │ └── MyService.java │ ├── spring-jsr330-named/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── NamedApplication.java │ │ ├── config/ │ │ │ └── MyConfiguration.java │ │ ├── controller/ │ │ │ └── MyController.java │ │ └── service/ │ │ ├── MyService.java │ │ ├── MyServiceA.java │ │ └── MyServiceB.java │ ├── spring-jsr330-provider/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── ProviderApplication.java │ │ ├── config/ │ │ │ └── MyConfiguration.java │ │ ├── controller/ │ │ │ └── MyController.java │ │ └── service/ │ │ └── MyService.java │ ├── spring-jsr330-qualifier/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── QualifierApplication.java │ │ ├── annotation/ │ │ │ ├── Email.java │ │ │ └── SMS.java │ │ ├── config/ │ │ │ └── MyConfiguration.java │ │ ├── controller/ │ │ │ └── MessageController.java │ │ └── service/ │ │ ├── MessageService.java │ │ └── impl/ │ │ ├── EmailServiceImpl.java │ │ └── SMSServiceImpl.java │ ├── spring-jsr330-scope/ │ │ ├── README.md │ │ └── pom.xml │ └── spring-jsr330-singleton/ │ ├── README.md │ └── pom.xml ├── spring-metadata/ │ ├── pom.xml │ ├── spring-metadata-annotationMetadata/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── AnnotationMetadataDemoByASM.java │ │ ├── AnnotationMetadataDemoByReflection.java │ │ └── bean/ │ │ └── MyBean.java │ ├── spring-metadata-condition/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── ConditionDemo.java │ │ ├── bean/ │ │ │ ├── MyBeanA.java │ │ │ └── MyBeanB.java │ │ └── condition/ │ │ └── MyOnClassCondition.java │ ├── spring-metadata-metadataReader/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── MetadataReaderDemo.java │ │ ├── annotation/ │ │ │ ├── MyAnnotation.java │ │ │ └── MyClassAnnotation.java │ │ └── bean/ │ │ ├── MyAbstract.java │ │ └── MyBean.java │ └── spring-metadata-typeFilter/ │ ├── README.md │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── com/ │ └── xcs/ │ └── spring/ │ ├── TypeFilterDemo.java │ ├── annotation/ │ │ └── MyAnnotation.java │ ├── component/ │ │ └── MyComponent.java │ ├── controller/ │ │ └── MyController.java │ ├── repository/ │ │ └── MyRepository.java │ └── service/ │ └── MyService.java ├── spring-mvc/ │ └── pom.xml ├── spring-resources/ │ ├── pom.xml │ ├── spring-resource/ │ │ ├── README.md │ │ ├── myfile.txt │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── xcs/ │ │ │ └── spring/ │ │ │ ├── ByteArrayResourceDemo.java │ │ │ ├── ClassPathResourceDemo.java │ │ │ ├── FileSystemResourceDemo.java │ │ │ ├── InputStreamResourceDemo.java │ │ │ └── UrlResourceDemo.java │ │ └── resources/ │ │ └── application.properties │ ├── spring-resource-documentLoader/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── xcs/ │ │ │ └── spring/ │ │ │ ├── DocumentLoaderDemo.java │ │ │ └── bean/ │ │ │ └── MyBean.java │ │ └── resources/ │ │ └── sample.xml │ ├── spring-resource-resourceLoader/ │ │ ├── README.md │ │ ├── myfile1.txt │ │ ├── myfile2.txt │ │ ├── myfile3.txt │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── xcs/ │ │ │ └── spring/ │ │ │ ├── DefaultResourceLoaderDemo.java │ │ │ └── PathMatchingResourcePatternResolverDemo.java │ │ └── resources/ │ │ ├── application.properties │ │ └── bootstrap.properties │ └── spring-resource-resourcePatternResolver/ │ ├── README.md │ ├── myfile1.txt │ ├── myfile2.txt │ ├── myfile3.txt │ ├── pom.xml │ └── src/ │ └── main/ │ ├── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ └── ResourcePatternResolverDemo.java │ └── resources/ │ ├── application.properties │ └── bootstrap.properties ├── spring-spel/ │ ├── pom.xml │ ├── spring-spel-beanResolver/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── BeanResolverDemo.java │ │ └── MyBean.java │ ├── spring-spel-constructorResolver/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── ConstructorResolverDemo.java │ │ └── MyBean.java │ ├── spring-spel-evaluationContext/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ └── EvaluationContextDemo.java │ ├── spring-spel-expression/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ └── ExpressionDemo.java │ ├── spring-spel-expressionParser/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ └── ExpressionParserDemo.java │ ├── spring-spel-methodResolver/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── MethodResolverDemo.java │ │ └── MyBean.java │ ├── spring-spel-operatorOverloader/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ ├── CustomOperatorOverloader.java │ │ ├── MyBean.java │ │ └── OperatorOverloaderDemo.java │ ├── spring-spel-propertyAccessor/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ ├── MyBean.java │ │ └── PropertyAccessorDemo.java │ ├── spring-spel-typeComparator/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ └── TypeComparatorDemo.java │ ├── spring-spel-typeConverter/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── xcs/ │ │ └── spring/ │ │ └── TypeConverterDemo.java │ └── spring-spel-typeLocator/ │ ├── README.md │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── com/ │ └── xcs/ │ └── spring/ │ └── TypeLocatorDemo.java └── spring-transaction/ ├── pom.xml ├── spring-transaction-connection/ │ ├── README.md │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── com/ │ └── xcs/ │ └── spring/ │ └── ConnectionDemo.java ├── spring-transaction-dataSource/ │ ├── README.md │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── com/ │ └── xcs/ │ └── spring/ │ └── DataSourceDemo.java ├── spring-transaction-driverManager/ │ ├── README.md │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── com/ │ └── xcs/ │ └── spring/ │ └── DriverManagerDemo.java ├── spring-transaction-enableTransactionManagement/ │ ├── README.md │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── com/ │ └── xcs/ │ └── spring/ │ ├── AppConfig.java │ ├── EnableTransactionManagementDemo.java │ ├── ScoresService.java │ └── ScoresServiceImpl.java ├── spring-transaction-jdbcTemplate/ │ ├── README.md │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── com/ │ └── xcs/ │ └── spring/ │ ├── JdbcTemplateDemo.java │ └── Scores.java ├── spring-transaction-platformTransactionManager/ │ ├── README.md │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── com/ │ └── xcs/ │ └── spring/ │ └── PlatformTransactionManagerDemo.java ├── spring-transaction-springTransactionAnnotationParser/ │ ├── README.md │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── com/ │ └── xcs/ │ └── spring/ │ ├── ScoresService.java │ ├── ScoresServiceImpl.java │ └── SpringTransactionAnnotationParserDemo.java ├── spring-transaction-transactionAttributeSource/ │ ├── README.md │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── com/ │ └── xcs/ │ └── spring/ │ ├── ScoresService.java │ ├── ScoresServiceImpl.java │ └── TransactionAttributeSourceDemo.java ├── spring-transaction-transactionDefinition/ │ ├── README.md │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── com/ │ └── xcs/ │ └── spring/ │ └── TransactionDefinitionDemo.java ├── spring-transaction-transactionInterceptor/ │ ├── README.md │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── com/ │ └── xcs/ │ └── spring/ │ ├── ScoresService.java │ ├── ScoresServiceImpl.java │ └── TransactionInterceptorDemo.java ├── spring-transaction-transactionTemplate/ │ ├── README.md │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── com/ │ └── xcs/ │ └── spring/ │ └── TransactionTemplateDemo.java └── sql/ └── test.sql ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ HELP.md target/ !.mvn/wrapper/maven-wrapper.jar !**/src/main/**/target/ !**/src/test/**/target/ ### STS ### .apt_generated .classpath .factorypath .project .settings .springBeans .sts4-cache ### IntelliJ IDEA ### .idea *.iws *.iml *.ipr ### NetBeans ### /nbproject/private/ /nbbuild/ /dist/ /nbdist/ /.nb-gradle/ build/ !**/src/main/**/build/ !**/src/test/**/build/ ### VS Code ### .vscode/ ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2024 xuchengsheng Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================
logo

深入Spring,从源码开始!

探索Java最受欢迎的框架,理解它的内部机制,带大家从入门到精通。

Stars Badge Follow Badge Fork Badge Watchers Badge

Visitor Badge Wechat Badge CSDN Badge Featured|HelloGitHub

技术 | 👋 简介 | 🍵 Why | 🙏 点个星 | 🌱 Spring源码 | 💬 联系我 | ⛵ 贡献 | 🔄 更新 | 💻 统计

--- ## ⚡技术
## 👋简介 大家好呀,我是Lex👨‍💻。我是一名拥有8年经验的Java 后端开发人员👨‍💼,也是一个对 Spring 框架充满热情❤️的程序员。为了帮助那些希望深入了解 Spring 框架的程序员们🧑‍💻,我创建了这个 “Spring 源码阅读系列”📖。通过这个系列,我希望能够与你们共同探索 Spring 的内部工作机制⚙️。如果您有同样的兴趣或问题🤔,请联系我📩! ## 🍵**为何做Spring源码分析** 在我作为框架研发的开发者👨‍🔬的工作中,我经常遇到需要深入理解和调整框架行为的情况🔧。这些工作不只是简单地使用框架的API,更多地是需要对框架的内部工作方式有详细的了解🔍。虽然Github上有关于Spring的简化版本📦,这些对于入门学习确实有很大的帮助✅,但当涉及到真实的项目应用时,与真正的Spring框架还是有很大的差异❌。因此,我开始深入研究Spring的源码,希望能够更透彻地理解其内部的工作机制,以便更好地应用到我的实际工作中🧰。分享我的源码分析📝,也是为了给那些希望真正理解Spring,而不仅仅是使用它的开发者提供一些参考和帮助🙌。 ## 🙏顺手点个星 亲爱的朋友们👥,我真的花了很多心思💭去研究和整理这个“Spring 源码阅读系列”📘。如果你觉得这东西还不错👍,或者给你带来了一点点帮助🤗,麻烦点一下星星吧🌟。这真的对我意义重大🎖,每一颗星✨都能让我觉得所有的努力都是值得的💪。我知道这是小事一桩,但你的那一下点击🖱,对我来说就是最好的鼓励🎉。无论如何,都要感谢你抽时间🕰阅读我的内容,真的很感激🙏! ## 🌱Spring 源码阅读系列 + Spring Core + 资源加载与访问 - [Resource](spring-resources/spring-resource/README.md):抽象接口,表示文件、类路径等,用于访问不同来源的资源。 - [ResourceLoader](spring-resources/spring-resource-resourceLoader/README.md):资源获取核心接口,实现统一加载不同位置资源的策略。 - [ResourcePatternResolver](spring-resources/spring-resource-resourcePatternResolver/README.md):资源模式解析接口,用于灵活加载应用中的多种资源。 - [DocumentLoader](spring-resources/spring-resource-documentLoader/README.md):XML文档加载解析核心接口,支持后台自动配置Spring应用。 + 元数据与过滤 - [MetadataReader](spring-metadata/spring-metadata-metadataReader/README.md):类元数据获取核心,支持组件扫描、条件化注解、AOP等高级功能。 - [AnnotationMetadata](spring-metadata/spring-metadata-annotationMetadata/README.md):动态获取和操作运行时类注解信息。 - [TypeFilter](spring-metadata/spring-metadata-typeFilter/README.md):组件扫描时自定义类筛选,支持复杂条件和精确过滤。 - [Condition](spring-metadata/spring-metadata-condition/README.md):条件判断,决定Bean创建和配置的灵活机制。 + 验证、数据绑定和类型转换 - [Validator](spring-dataops/spring-dataops-validator/README.md):提供自定义数据验证逻辑,确保模型对象满足业务规则。 - [PropertyEditor](spring-dataops/spring-dataops-propertyEditor/README.md):自定义JavaBean属性的转换逻辑,处理属性类型转换。 - [Converter](spring-dataops/spring-dataops-converter/README.md):用于不同类型间的转换,定义简单的源至目标类型转换规则。 - [ConverterFactory](spring-dataops/spring-dataops-converterFactory/README.md):创建针对特定源类型的转换器,用于类型转换。 - [GenericConverter](spring-dataops/spring-dataops-genericConverter/README.md):更复杂的转换器,支持多种源和目标类型转换。 - [ConditionalConverter](spring-dataops/spring-dataops-conditionalConverter/README.md):根据条件选择是否执行转换的转换器。 - [ConversionService](spring-dataops/spring-dataops-conversionService/README.md):提供统一的类型转换服务接口,管理转换器。 - [Printer](spring-dataops/spring-dataops-printer/README.md):用于将对象格式化为文本,专注于格式化输出。 - [Parser](spring-dataops/spring-dataops-parser/README.md):用于将文本解析为对象,专注于解析逻辑。 + Spring 表达式语言(SpEL) - [ExpressionParser](spring-spel/spring-spel-expressionParser/README.md): 解析字符串形式的 SpEL 表达式,创建并返回 Expression 实例。 - [Expression](spring-spel/spring-spel-expression/README.md): 对表达式字符串进行求值的功能,支持类型转换、获取原始字符串等操作。 - [EvaluationContext](spring-spel/spring-spel-evaluationContext/README.md): 管理SpEL表达式的上下文信息。 - [PropertyAccessor](spring-spel/spring-spel-propertyAccessor/README.md): 用于读取和写入对象的属性,可用于实现自定义的属性访问逻辑。 - [ConstructorResolver](spring-spel/spring-spel-constructorResolver/README.md): 解析构造函数确定bean的实例化方式。 - [MethodResolver](spring-spel/spring-spel-methodResolver/README.md): 解析类方法,确保正确调用,处理重载和参数匹配。 - [BeanResolver](spring-spel/spring-spel-beanResolver/README.md): 解析bean定义,包括依赖、属性设置,实例化并返回。 - [TypeLocator](spring-spel/spring-spel-typeLocator/README.md): 动态查找类,返回Class对象,在表达式解析、类型转换等。 - [TypeConverter](spring-spel/spring-spel-typeLocator/README.md): 类型转换功能,将表达式中的数据从一种类型转换为另一种类型。 - [TypeComparator](spring-spel/spring-spel-typeLocator/README.md): 类型比较功能,定义了比较两个对象是否相等的方法。 - [OperatorOverloader](spring-spel/spring-spel-typeLocator/README.md): 运算符重载功能,对表达式中的运算符进行自定义操作的方法。 + Bean定义与注册 - [BeanDefinition](spring-beans/spring-bean-beanDefinition/README.md):详细描述Bean,支持依赖注入、AOP、作用域控制等核心功能。 - [BeanDefinitionHolder](spring-beans/spring-bean-beanDefinitionHolder/README.md):管理和操作BeanDefinition的关键类。 - [BeanDefinitionRegistry](spring-beans/spring-bean-beanDefinitionRegistry/README.md):Bean定义注册管理关键接口,处理Bean元数据。 + Bean定义读取与扫描 - [XmlBeanDefinitionReader](spring-beans/spring-bean-xmlBeanDefinitionReader/README.md):加载解析XML配置,构建IOC容器,注册Bean定义。 - [PropertiesBeanDefinitionReader](spring-beans/spring-bean-propertiesBeanDefinitionReader/README.md):属性文件加载,解析为Bean定义。 - [GroovyBeanDefinitionReader](spring-beans/spring-bean-groovyBeanDefinitionReader/README.md):Groovy脚本解析为Bean定义。 - [AnnotatedBeanDefinitionReader](spring-beans/spring-bean-annotatedBeanDefinitionReader/README.md):注解配置,自动扫描注册Spring组件,简化Bean定义配置。 - [ClassPathBeanDefinitionScanner](spring-beans/spring-bean-classPathBeanDefinitionScanner/README.md):类路径扫描注册Spring Bean,支持自动装配。 + Bean工厂 - [BeanFactory](spring-factory/spring-factory-beanFactory/README.md):Spring的核心接口,提供对Bean的配置、创建、管理的基本功能。 - [ListableBeanFactory](spring-factory/spring-factory-listableBeanFactory/README.md):支持按类型获取Bean的集合。 - [HierarchicalBeanFactory](spring-factory/spring-factory-hierarchicalBeanFactory/README.md):支持父子容器关系,实现Bean定义的层次结构。 - [ConfigurableBeanFactory](spring-factory/spring-factory-configurableBeanFactory/README.md):提供对BeanFactory配置的扩展,如属性编辑器、作用域等。 - [AutowireCapableBeanFactory](spring-factory/spring-factory-autowireCapableBeanFactory/README.md):Bean创建、初始化、注入、销毁的核心功能接口。 - [ConfigurableListableBeanFactory](spring-factory/spring-factory-configurableListableBeanFactory/README.md):支持配置和列表操作的可配置Bean工厂接口。 + 容器上下文 - [ClassPathXmlApplicationContext](spring-context/spring-context-classPathXmlApplicationContext/README.md):类路径(classpath)加载 XML 配置文件的上下文。 - [AnnotationConfigApplicationContext](spring-context/spring-context-annotationConfigApplicationContext/README.md):注解配置类中加载配置信息的上下文。 - GenericApplicationContext:支持多种配置方式,XML、注解、手动注册的上下文。 + Bean定义导入与组合 - ImportBeanDefinitionRegistrar:运行时动态注册 Bean,实现灵活配置,扩展配置类功能。 - ImportSelector:运行时动态导入配置类,实现条件选择和灵活配置。 - DeferredImportSelector:运行时动态导入配置,支持条件选择和按组别延迟加载。 + Bean生命周期 - [Bean的定义注册过程](spring-core/spring-core-registerBeanDefinition):加载与解析配置文件,注册解析Bean定义,类名、作用域、属性等。 - [Bean的初始化过程](spring-core/spring-core-getBean/README.md):实例化、属性注入、Aware回调、后置处理器、初始化方法调用。 - [Bean的依赖解析过程](spring-core/spring-core-resolveDependency/README.md):声明依赖,查找依赖,注入依赖,处理循环依赖,延迟依赖解析。 - [Bean的销毁过程](spring-core/spring-core-destroyBean/README.md):销毁方法调用,接口回调,后处理清理,通知触发,GC回收资源。 + 属性解析和环境配置 - [PropertySource](spring-env/spring-env-propertySource/README.md):管理各种配置源的抽象类,支持灵活地加载和访问应用配置。 - [PropertySources](spring-env/spring-env-propertySources/README.md):用于统一管理和访问多个 PropertySource 实例,简化配置数据的处理。 - [PropertyResolver](spring-env/spring-env-propertyResolver/README.md):通用属性解析,获取配置值,处理属性缺失,简便灵活。 - [ConfigurablePropertyResolver](spring-env/spring-env-configurablePropertyResolver/README.md):属性解析配置,占位符设置,适应不同配置需求。 - [Environment](spring-env/spring-env-environment/README.md):应用环境表示,提供属性访问,支持配置文件,实现动态配置。 - [ConfigurableEnvironment](spring-env/spring-env-configurableEnvironment/README.md):动态配置应用环境,激活、默认配置,提升应用灵活性。 + Bean初始化与扩展点 - [InitializingBean](spring-interface/spring-interface-initializingBean/README.md):提供Bean初始化时执行自定义逻辑的接口。 - [DisposableBean](spring-interface/spring-interface-disposableBean/README.md):定义Bean销毁前执行清理操作的接口。 - [BeanDefinitionRegistryPostProcessor](spring-interface/spring-interface-beanDefinitionRegistryPostProcessor/README.md):在容器启动时,对BeanDefinition动态修改或添加。 - [BeanFactoryPostProcessor](spring-interface/spring-interface-beanFactoryPostProcessor/README.md):在Bean实例化前,对BeanFactory进行全局修改或配置。 - [BeanPostProcessor](spring-interface/spring-interface-beanPostProcessor/README.md):在Bean初始化前后,进行自定义处理,可影响所有Bean。 - [InstantiationAwareBeanPostProcessor](spring-interface/spring-interface-instantiationAwareBeanPostProcessor/README.md):提供更深层次的实例化和属性注入控制。 - [DestructionAwareBeanPostProcessor](spring-interface/spring-interface-destructionAwareBeanPostProcessor/README.md): 允许在Bean销毁前进行额外的清理操作。 - [MergedBeanDefinitionPostProcessor](spring-interface/spring-interface-mergedBeanDefinitionPostProcessor/README.md):在合并Bean定义时对BeanDefinition进行处理。 - [SmartInstantiationAwareBeanPostProcessor](spring-interface/spring-interface-smartInstantiationAwareBeanPostProcessor/README.md):提供更智能的实例化控制。 - [SmartInitializingSingleton](spring-interface/spring-interface-smartInitializingSingleton/README.md):在所有单例Bean初始化完成后,执行自定义逻辑。 + Aware接口系列 - [BeanNameAware](spring-aware/spring-aware-beanNameAware/README.md):让Bean获取自身在容器中的名字。 - [BeanClassLoaderAware](spring-aware/spring-aware-beanClassLoaderAware/README.md):允许Bean获取其类加载器。 - [BeanFactoryAware](spring-aware/spring-aware-beanFactoryAware/README.md):提供Bean获取所属的BeanFactory。 - [EnvironmentAware](spring-aware/spring-aware-environmentAware/README.md):允许Bean获取应用程序环境配置。 - [EmbeddedValueResolverAware](spring-aware/spring-aware-embeddedValueResolverAware/README.md):允许Bean解析嵌入式值占位符。 - [ResourceLoaderAware](spring-aware/spring-aware-beanClassLoaderAware/README.md):允许Bean获取资源加载器。 - [ApplicationEventPublisherAware](spring-aware/spring-aware-applicationEventPublisherAware/README.md):允许Bean发布应用程序事件。 - [MessageSourceAware](spring-aware/spring-aware-messageSourceAware/README.md):允许Bean获取消息源。 - [ApplicationContextAware](spring-aware/spring-aware-applicationContextAware/README.md):允许Bean获取应用程序上下文。 - [ImportAware](spring-aware/spring-aware-importAware/README.md):允许被导入的配置类获取导入它的类的信息。 + 核心注解 - [@Configuration](spring-annotation/spring-annotation-configuration/README.md):声明类为配置类,定义Bean和Bean之间的依赖关系。 - [@ComponentScan](spring-annotation/spring-annotation-componentScan/README.md):启用组件扫描,自动发现并注册标记为组件的类。 - [@Bean](spring-annotation/spring-annotation-bean/README.md):在配置类中声明方法,返回Bean实例。 - [@Import](spring-annotation/spring-annotation-import/README.md):引入其他配置类,将其Bean定义合并到当前容器。 - [@PropertySource](spring-annotation/spring-annotation-propertySource/README.md):指定属性文件,加载外部配置到环境中。 - [@DependsOn](spring-annotation/spring-annotation-dependsOn/README.md):指定Bean的依赖顺序,确保特定Bean在其他Bean之前初始化。 - [@Conditional](spring-annotation/spring-annotation-conditional/README.md):根据条件决定是否创建Bean。 - [@Lazy](spring-annotation/spring-annotation-lazy/README.md):指定Bean的延迟初始化,只有在首次使用时才创建。 - [@Value](spring-annotation/spring-annotation-value/README.md):注入简单值或表达式到Bean的字段或方法参数。 - [@Autowired](spring-annotation/spring-annotation-autowired/README.md):自动装配Bean依赖。 - @Primary:指定在多个候选Bean中优先选择的首选Bean。 - @Description:为Bean提供描述性信息。 - @Role:为Bean提供角色提示,用于区分相似类型的Bean。 - @Indexed: 标记Bean用于索引。 - @Order:指定Bean的加载顺序。 + JSR规范 - [@Inject](spring-jsr/spring-jsr330-inject/README.md):JSR-330标准的依赖注入注解。 - [@Named](spring-jsr/spring-jsr330-named/README.md):JSR-330标准的命名注解。 - [@Resource](spring-jsr/spring-jsr250-resource/README.md):Java EE标准的资源注入注解。 - [@Qualifier](spring-jsr/spring-jsr330-qualifier/README.md):用于限定注入的Bean。 - [@Scope](spring-jsr/spring-jsr330-scope/README.md):指定Bean的作用域。 - [@Singleton](spring-jsr/spring-jsr330-singleton/README.md):指定Bean为单例。 - [@PostConstruct](spring-jsr/spring-jsr250-postConstruct/README.md):指定初始化方法。 - [@PreDestroy](spring-jsr/spring-jsr250-preDestroy/README.md):指定销毁方法。 - [Provider](spring-jsr/spring-jsr330-provider/README.md):Java标准库提供的通用Bean工厂接口。 + Spring AOP - [JDK动态代理](spring-aop/spring-aop-jdkProxy/README.md):接口实现,动态生成代理类,处理方法调用,统一横切关注点。 - [Cglib动态代理](spring-aop/spring-aop-cglibProxy/README.md):基于字节码生成的库,无需接口,可拦截类方法并进行增强。 - [ClassFilter](spring-aop/spring-aop-classFilter/README.md):确定类是否匹配拦截条件。 - [MethodMatcher](spring-aop/spring-aop-methodMatcher/README.md):确定方法是否匹配拦截条件。 - [Pointcut](spring-aop/spring-aop-pointcut/README.md):定义切入点,匹配被拦截的方法。 - [Advice](spring-aop/spring-aop-advice/README.md):AOP中定义各种通知类型行为的核心接口。 - [MethodInterceptor](spring-aop/spring-aop-advice-methodInterceptor/README.md):拦截方法执行,允许在前后添加额外逻辑。 - [MethodBeforeAdvice](spring-aop/spring-aop-advice-methodBeforeAdvice/README.md):允许在方法调用之前插入自定义逻辑。 - [AfterReturningAdvice](spring-aop/spring-aop-advice-afterReturningAdvice/README.md):允许在方法调用之后插入自定义逻辑。< - [ThrowsAdvice](spring-aop/spring-aop-advice-throwsAdvice/README.md):异常通知,捕获方法抛出的异常,执行额外逻辑。 - [IntroductionInterceptor](spring-aop/spring-aop-advice-introductionInterceptor/README.md):动态地向目标对象引入新的功能或属性。 - [Advisor](spring-aop/spring-aop-advisor/README.md):用于将通知和切点结合,实现切面编程的横切关注点。 - [Advised](spring-aop/spring-aop-advised/README.md):配置AOP代理的通知、通知器、目标等。 - [ProxyFactory](spring-aop/spring-aop-proxyFactory/README.md):一种便捷的方式来创建代理对象。 - [AopProxyFactory](spring-aop/spring-aop-aopProxyFactory/README.md):创建AOP代理工厂,支持JDK和CGLIB。 - [AopProxy](spring-aop/spring-aop-aopProxy/README.md):创建和管理AOP代理对象。 - [AdvisorChainFactory](spring-aop/spring-aop-advisorChainFactory/README.md):创建Advisor链的工厂接口。 - [AdvisorAdapterRegistry](spring-aop/spring-aop-advisorAdapterRegistry/README.md):适配各种Advice到AOP拦截器,注册和管理Advisor适配器。 - [AdvisorAdapter](spring-aop/spring-aop-advisorAdapter/README.md):适配不同类型通知到拦截器链。 - [ProxyMethodInvocation](spring-aop/spring-aop-proxyMethodInvocation/README.md):AOP方法调用代理,处理拦截器链和方法调用。 - [@EnableAspectJAutoProxy](spring-aop/spring-aop-enableAspectJAutoProxy/README.md):启用AspectJ切面自动代理。 - [AnnotationAwareAspectJAutoProxyCreator](spring-aop/spring-aop-annotationAwareAspectJAutoProxyCreator/README.md):创建AOP代理以应用AspectJ风格的切面。 - [BeanFactoryAdvisorRetrievalHelper](spring-aop/spring-aop-beanFactoryAdvisorRetrievalHelper/README.md):帮助检索并管理Spring AOP 中的 Advisor Beans。 - [BeanFactoryAspectJAdvisorsBuilder](spring-aop/spring-aop-beanFactoryAspectJAdvisorsBuilder/README.md):构建@AspectJ注解切面,生成Spring AOP Advisors。 - [AspectInstanceFactory](spring-aop/spring-aop-aspectInstanceFactory/README.md):创建切面实例,支持多种实现方式。 - [MetadataAwareAspectInstanceFactory](spring-aop/spring-aop-metadataAwareAspectInstanceFactory/README.md):管理切面实例和元数据,支持多种实例化策略。 - [AspectJAdvisorFactory](spring-aop/spring-aop-aspectJAdvisorFactory/README.md):创建AspectJ通知器实例,管理切面通知的创建和配置。 - [TargetSource](spring-aop/spring-aop-targetSource/README.md):管理AOP代理对象的获取与释放。 - [TargetSourceCreator](spring-aop/spring-aop-targetSourceCreator/README.md):创建特殊的目标源,定制代理对象的创建和管理。 - [AopContext](spring-aop/spring-aop-aopContext/README.md):获取Spring AOP代理对象的工具。 - [ExposeInvocationInterceptor](spring-aop/spring-aop-exposeInvocationInterceptor/README.md):暴露Spring AOP方法调用上下文的拦截器。 - [@EnableLoadTimeWeaving](spring-aop/spring-aop-enableLoadTimeWeaving/README.md):启用Spring加载时编织。 + Spring 事务 + [Connection](spring-transaction/spring-transaction-connection/README.md):管理数据库连接,执行SQL,处理事务。 + [DataSource](spring-transaction/spring-transaction-dataSource/README.md):提供高效管理数据库连接的接口。 + [DriverManager](spring-transaction/spring-transaction-driverManager/README.md):管理和建立数据库连接的核心类。 + [JdbcTemplate](spring-transaction/spring-transaction-jdbcTemplate/README.md):简化了JDBC操作,提供了方便的数据库访问抽象。 + [TransactionDefinition](spring-transaction/spring-transaction-transactionDefinition/README.md):定义事务的传播行为和隔离级别。 + [TransactionAttributeSource](spring-transaction/spring-transaction-transactionAttributeSource/README.md):用于获取事务属性的策略接口。 + [PlatformTransactionManager](spring-transaction/spring-transaction-platformTransactionManager/README.md):用于管理和协调事务的生命周期和执行。 + [TransactionTemplate](spring-transaction/spring-transaction-transactionTemplate/README.md):简化事务管理,支持编程式事务控制与异常处理。 + [SpringTransactionAnnotationParser](spring-transaction/spring-transaction-springTransactionAnnotationParser/README.md):解析 `@Transactional`注解并转换为事务配置。 + [TransactionInterceptor](spring-transaction/spring-transaction-transactionInterceptor/README.md):事务拦截器,用于管理方法级别的事务处理。 + [EnableTransactionManagement](spring-transaction/spring-transaction-enableTransactionManagement/README.md):启用Spring的注解驱动事务管理。 + Spring MVC + Spring OpenFeign ## 💬与我联系 ✉️ [Email](xuchengshengsuper@163.com) | 💬 [Issue](https://github.com/xuchengsheng/spring-reading/issues) | 🌐 [CSDN](https://blog.csdn.net/duzhuang2399?type=blog) Me about everything! ## ⛵欢迎贡献! 如果你发现任何错误🔍或者有改进建议🛠️,欢迎提交 issue 或者 pull request。你的反馈📢对于我非常宝贵💎! ## 🔄持续更新中 为了给大家提供最新🌱、最有价值的内容💼,我会坚持每天更新这个仓库⏳。每一天,你都可以期待看到一些新的内容或者对已有内容的改进✨。如果你有任何建议或反馈📣,欢迎随时联系我📞。我非常珍视每一个反馈💌,因为这是我持续改进的动力🚀。 ## ✨Star History Star History Chart ## 🎉Stargazers [![Stargazers123 repo roster for @xuchengsheng/spring-reading](https://reporoster.com/stars/xuchengsheng/spring-reading)](https://github.com/xuchengsheng/spring-reading/stargazers) ## 🎉Forkers [![Forkers repo roster for @xuchengsheng/spring-reading](https://reporoster.com/forks/xuchengsheng/spring-reading)](https://github.com/xuchengsheng/spring-reading/network/members) ## 🍱请我吃盒饭? 作者晚上还要写博客✍️,平时还需要工作💼,如果帮到了你可以请作者吃个盒饭🥡
logo       logo
## 👥**关注公众号** 关注后,回复关键字 **“加群”**,即可加入我们的技术交流群,与更多开发者一起交流学习。
logo
================================================ FILE: index.html ================================================
标题 地址 难度级别 视频讲解
【资源加载与访问】
资源加载 Resource
资源加载器 ResourceLoader
XML资源加载器 DocumentLoader
【元数据与过滤】
类元数据读取 MetadataReader
注解元数据 AnnotationMetadata
类过滤器 TypeFilter
条件过滤器 Condition
【Bean定义与注册】
Bean定义 BeanDefinition
Bean定义持有者 BeanDefinitionHolder
Bean定义注册器 BeanDefinitionRegistry
【Bean定义读取与扫描】
XML Bean定义读取器 XmlBeanDefinitionReader
属性文件Bean定义读取器 PropertiesBeanDefinitionReader
Groovy脚本Bean定义读取器 GroovyBeanDefinitionReader
注解Bean定义读取器 AnnotatedBeanDefinitionReader
类路径Bean定义扫描器 ClassPathBeanDefinitionScanner
【Bean生命周期过程】
Bean的定义解析 Bean的定义解析
Bean的初始化过程 Bean的初始化过程
Bean的依赖解析过程 Bean的依赖解析过程
Bean的销毁过程 Bean的销毁过程
【后置处理器与初始化】
属性设置后的初始化操作 InitializingBean
资源清理与销毁 DisposableBean
动态修改Bean定义 BeanDefinitionRegistryPostProcessor
动态调整Bean配置 BeanFactoryPostProcessor
调整Bean属性 BeanPostProcessor
Bean实例拦截 InstantiationAwareBeanPostProcessor
Bean销毁生命周期 DestructionAwareBeanPostProcessor
Bean定义的动态处理 MergedBeanDefinitionPostProcessor
调整Bean实例化策略 SmartInstantiationAwareBeanPostProcessor
All Beans完全初始化后 SmartInitializingSingleton
【Aware接口】
获取Bean名称 BeanNameAware
获取类加载器 BeanClassLoaderAware
与Bean工厂互动 BeanFactoryAware
感知运行环境 EnvironmentAware
嵌入值解析 EmbeddedValueResolverAware
资源加载策略 ResourceLoaderAware
发布应用事件 ApplicationEventPublisherAware
访问消息源 MessageSourceAware
感知应用启动过程 ApplicationStartupAware
访问应用上下文 ApplicationContextAware
了解关联导入信息 ImportAware
【核心注解】
Java配置 @Configuration
组件扫描 @ComponentScan
Bean定义 @Bean
导入配置 @Import
属性绑定 @PropertySource
初始化顺序 @DependsOn
条件注册 @Conditional
延迟加载 @Lazy
属性注入 @Value
依赖注入 @Autowired
注入依赖 @Inject
具名组件 @Named
初始化后操作 @PostConstruct
销毁前操作 @PreDestroy
资源绑定 @Resource
提供者机制 Provider
限定符 @Qualifier
作用域定义 @Scope
单例模式 @Singleton
定义主要候选项 @Primary
添加描述信息 @Description
指定注解角色 @Role
标记为可索引 @Indexed
指定顺序 @Order
================================================ FILE: pom.xml ================================================ 4.0.0 pom com.xcs.spring spring-reading 0.0.1-SNAPSHOT spring-reading spring-reading 11 11 11 5.2.15.RELEASE 8.0.30 2.3.12.RELEASE spring-annotation spring-aware spring-interface spring-jsr spring-core spring-aop spring-mvc spring-resources spring-metadata spring-beans spring-context spring-factory spring-env spring-dataops spring-spel spring-transaction org.springframework.boot spring-boot-starter-web ${spring.boot.version} org.springframework.boot spring-boot-starter-aop ${spring.boot.version} org.springframework.boot spring-boot-starter-jdbc ${spring.boot.version} ================================================ FILE: spring-annotation/pom.xml ================================================ spring-reading com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-annotation pom spring-annotation-configuration spring-annotation-bean spring-annotation-import spring-annotation-propertySource spring-annotation-componentScan spring-annotation-dependsOn spring-annotation-lazy spring-annotation-conditional spring-annotation-value spring-annotation-autowired spring-annotation-profile ================================================ FILE: spring-annotation/spring-annotation-autowired/README.md ================================================ ## @Autowired - [@Autowired](#autowired) - [一、基本信息](#一基本信息) - [二、注解描述](#二注解描述) - [三、接口源码](#三接口源码) - [四、主要功能](#四主要功能) - [五、最佳实践](#五最佳实践) - [六、时序图](#六时序图) - [七、源码分析](#七源码分析) - [前置条件](#前置条件) - [收集阶段](#收集阶段) - [注入阶段](#注入阶段) - [八、注意事项](#八注意事项) - [九、总结](#九总结) - [最佳实践总结](#最佳实践总结) - [源码分析总结](#源码分析总结) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [我的CSDN](https://blog.csdn.net/duzhuang2399/article/details/133833938) 📚 **文章目录** - [所有文章](https://github.com/xuchengsheng/spring-reading) 🔗 **源码地址** - [@Autowired源码](https://github.com/xuchengsheng/spring-reading/tree/master/spring-annotation/spring-annotation-autowired) ### 二、注解描述 `@Autowired` 注解,用于实现依赖注入(Dependency Injection, DI)。当我们在 Spring 中定义了一个 Bean 并想要使用另一个 Bean 时,可以使用 `@Autowired` 注解来自动注入所需的 Bean,而我们无需手动查找和配置它。 ### 三、接口源码 `@Autowired`注解是 Spring 框架自 2.5 版本开始引入的一个核心注解,该注解用于告知 Spring 框架的依赖注入工具自动注入所需的依赖。 ```java /** * 通过Spring的依赖注入机制标记构造函数、字段、setter方法或配置方法。 * 这是JSR-330 Inject 注解的一种替代,增加了必需与可选的语义。 * * 自动注入的构造函数: * 任何给定的bean类只有一个构造函数可以声明此注解,且其required属性设置为 true, * 表示当作为Spring bean使用时要自动注入的构造函数。如果required属性设置为true, * 则只能有一个构造函数被标记为Autowired。如果多个非必需的构造函数声明了这个注解, * 它们都会被视为自动注入的候选者。Spring会选择可以满足最多依赖的构造函数进行注入。如果没有一个候选者满足条件, * 则会使用默认的构造函数。如果一个类声明了多个构造函数,但没有一个使用Autowired, * 则会使用默认的构造函数。如果一个类从一开始就只声明了一个构造函数,它总是会被使用,即使没有被标记。被标记的构造函数不需要是public的。 * * 自动注入的字段: * 字段会在bean构造完成后注入,任何配置方法调用之前。配置字段不需要是public的。 * * 自动注入的方法: * 配置方法可以有任意名称和任意数量的参数;其中每一个参数都会通过Spring容器中的一个匹配的bean进行自动注入。 * Bean属性的setter方法在实质上只是这种通用配置方法的一个特例。这些配置方法不需要是public的。 * * 自动注入的参数: * 尽管从Spring 5.0开始Autowired可以在方法或构造函数的单独参数上声明, * 但框架的大部分都会忽略这样的声明。唯一支持自动注入参数的Spring核心部分是 * spring-test模块中的JUnit Jupiter支持。 * * 多参数与'required'语义: * 对于多参数的构造函数或方法,required属性适用于所有参数。 * * 自动装配数组、集合和映射: * 对于数组、Collection或Map类型的依赖,容器会自动注入所有匹配声明的值类型的bean。 * 对于这种目的,map的键必须声明为类型String,它会解析为相应的bean名称。 * * 在BeanPostProcessor或BeanFactoryPostProcessor中不支持: * 请注意,实际的注入是通过BeanPostProcessor执行的,这意味着我们不能使用Autowired在 * BeanPostProcessor或BeanFactoryPostProcessor类型中进行注入。 * * @author Juergen Hoeller * @author Mark Fisher * @author Sam Brannen * @since 2.5 * @see AutowiredAnnotationBeanPostProcessor * @see Qualifier * @see Value */ @Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Autowired { /** * 声明注解的依赖是否是必需的。 * 默认值为true。 */ boolean required() default true; } ``` ### 四、主要功能 1. **自动注入依赖** + 不需要明确指定 bean 之间的关系,Spring 会自动找到并注入所需的依赖。 2. **字段注入** + 可以直接标记在类的字段上,使得该字段在 Bean 初始化时被自动注入。 3. **构造函数注入** + 当标记在构造函数上时,该构造函数会被用于创建 bean 实例并注入所需的依赖。 4. **方法注入** + 当标记在 setter 方法或其他方法上时,这些方法会在 Bean 初始化时被调用以注入依赖。 5. **指定必需性** + 通过 `required` 属性,可以指定某个依赖是否是必需的。如果标记为必需但没有找到相应的依赖,Spring 会抛出异常。 ### 五、最佳实践 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyController`类型的bean并调用了`showService`方法, ```java public class AutowiredApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MyController controller = context.getBean(MyController.class); controller.showService(); } } ``` 在`MyConfiguration`类中,使用了`@ComponentScan("com.xcs.spring")`注解告诉 Spring 在指定的包(在这里是 "`com.xcs.spring`")及其子包中搜索带有 `@Component`、`@Service`、`@Repository` 和 `@Controller` 等注解的类,并将它们自动注册为 beans。这样,spring就不必为每个组件明确写一个 bean 定义。Spring 会自动识别并注册它们。 ```java @Configuration @ComponentScan("com.xcs.spring") public class MyConfiguration { } ``` Spring 容器在初始化 `MyController` 时自动注入一个 `MyService` 类型的 bean 到 `myService` 字段。 ```java @Controller public class MyController { @Autowired private MyService myService; public void showService(){ System.out.println("myService = " + myService); } } ``` `MyService` 是一个简单的服务类,但我们没有定义任何方法或功能。 ```java @Service public class MyService { } ``` 运行结果发现,我们使用 `@Autowired` 注解的功能,在我们的 Spring 上下文中工作正常,并且它成功地自动注入了所需的依赖关系。 ```java myService = com.xcs.spring.service.MyService@4a883b15 ``` ### 六、时序图 ~~~mermaid sequenceDiagram Title: @Autowired注解时序图 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:applyMergedBeanDefinitionPostProcessors(mbd,beanType,beanName)
应用Bean定义的后置处理器 AbstractAutowireCapableBeanFactory->>AutowiredAnnotationBeanPostProcessor:postProcessMergedBeanDefinition(beanDefinition,beanType,beanName)
处理已合并的Bean定义 AutowiredAnnotationBeanPostProcessor->>AutowiredAnnotationBeanPostProcessor:findAutowiringMetadata(beanName,clazz,pvs)
查找自动注入的元数据 AutowiredAnnotationBeanPostProcessor->>AutowiredAnnotationBeanPostProcessor:buildAutowiringMetadata(clazz)
构建自动注入的元数据 AutowiredAnnotationBeanPostProcessor->>ReflectionUtils:doWithLocalFields(clazz,fc)
处理类的本地字段 ReflectionUtils->>AutowiredAnnotationBeanPostProcessor:解析有@Autowired注解的字段 AutowiredAnnotationBeanPostProcessor->>ReflectionUtils:doWithLocalMethods(clazz,fc)
处理类的本地方法 ReflectionUtils->>AutowiredAnnotationBeanPostProcessor:解析有@Autowired注解的方法 AutowiredAnnotationBeanPostProcessor->>AutowiredAnnotationBeanPostProcessor:injectionMetadataCache.put(cacheKey, metadata)
将元数据存入缓存 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:populateBean(beanName,mbd,bw)
填充Bean的属性值 AbstractAutowireCapableBeanFactory->>AutowiredAnnotationBeanPostProcessor:postProcessProperties(pvs,bean,beanName)
后处理Bean的属性 AutowiredAnnotationBeanPostProcessor->>AutowiredAnnotationBeanPostProcessor:findAutowiringMetadata(beanName,clazz,pvs)
再次查找自动注入的元数据 Note right of AutowiredAnnotationBeanPostProcessor:
从缓存中获取注入的元数据 AutowiredAnnotationBeanPostProcessor->>InjectionMetadata:inject(bean, beanName, pvs)
执行实际的属性注入 InjectionMetadata->>AutowiredFieldElement:inject(target, beanName, pvs)
注入特定的字段元素 AutowiredFieldElement->>AutowiredFieldElement:resolveFieldValue(field,bean,beanName)
解析字段的值 AutowiredFieldElement->>DefaultListableBeanFactory:resolveDependency(desc, beanName, autowiredBeanNames, typeConverter)
解析字段的依赖 DefaultListableBeanFactory->>DefaultListableBeanFactory:doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter)
解析指定的依赖关系 DefaultListableBeanFactory->>DefaultListableBeanFactory:findAutowireCandidates(beanName, type, descriptor)
查找符合自动装配条件的候选 Bean DefaultListableBeanFactory->>DefaultListableBeanFactory:addCandidateEntry(result, candidate, descriptor, requiredType)
向结果集中添加候选 Bean DefaultListableBeanFactory->>AbstractBeanFactory:getType(name)
获取指定 Bean 的类型 AbstractBeanFactory->>DefaultListableBeanFactory:返回被依赖Bean的类
返回依赖 Bean 的实际类 DefaultListableBeanFactory->>DependencyDescriptor:resolveCandidate(beanName, requiredType, beanFactory)
解析候选的依赖 Bean DependencyDescriptor->>AbstractBeanFactory:getBean(name)
获取指定的 Bean 实例 AbstractBeanFactory->>DependencyDescriptor:
返回具体的依赖 Bean 实例 DependencyDescriptor->>DefaultListableBeanFactory:
返回依赖的 Bean 实例给工厂 DefaultListableBeanFactory->>AutowiredFieldElement:
返回依赖的 Bean 给字段注入器 AutowiredFieldElement->>Field:field.set(bean, value)
实际设置 Bean 的字段值 ~~~ ### 七、源码分析 #### 前置条件 在Spring中,`AutowiredAnnotationBeanPostProcessor`是处理`@Autowired`等注解的关键类,它实现了下述两个接口。因此,为了深入理解`@Autowired`的工作方式,研究这个类是非常有用的。简而言之,为了完全理解`@Autowired`的工作机制,了解下述接口确实是必要的。这两个接口提供了对bean生命周期中关键阶段的干预,从而允许进行属性注入和其他相关的操作。 1. `MergedBeanDefinitionPostProcessor`接口 - 此接口提供的`postProcessMergedBeanDefinition`方法允许后处理器修改合并后的bean定义。合并后的bean定义是一个已经考虑了所有父bean定义属性的bean定义。对于`@Autowired`注解的处理,这一步通常涉及到收集需要被解析的`@Autowired`注解信息并准备对其进行后续处理。 - 🔗 [MergedBeanDefinitionPostProcessor接口传送门](https://github.com/xuchengsheng/spring-reading/tree/master/spring-interface/spring-interface-mergedBeanDefinitionPostProcessor) 2. `InstantiationAwareBeanPostProcessor`接口 - 此接口提供了几个回调方法,允许后处理器在bean实例化之前和实例化之后介入bean的创建过程。特别是,`postProcessProperties`方法允许后处理器对bean的属性进行操作。对于`@Autowired`注解,这通常需要在属性设置或依赖注入阶段对 bean 进行处理,并将解析得到的值注入到bean中。 - 🔗 [InstantiationAwareBeanPostProcessor接口传送门](https://github.com/xuchengsheng/spring-reading/tree/master/spring-interface/spring-interface-instantiationAwareBeanPostProcessor) #### 收集阶段 在`org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition`方法中,主要确保给定的bean定义与其预期的自动装配元数据一致。 ```java @Override public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class beanType, String beanName) { // 对于给定的bean名称和类型,它首先尝试查找相关的InjectionMetadata,这可能包含了该bean的字段和方法的注入信息 InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null); // 使用找到的InjectionMetadata来验证bean定义中的配置成员是否与预期的注入元数据匹配。 metadata.checkConfigMembers(beanDefinition); } ``` 在`org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#findAutowiringMetadata`方法中,确保了始终为给定的bean名称和类获取最新和相关的`InjectionMetadata`,并利用缓存机制优化性能。 ```java private InjectionMetadata findAutowiringMetadata(String beanName, Class clazz, @Nullable PropertyValues pvs) { // 如果beanName为空,则使用类名作为缓存键。 String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName()); // 首先尝试从并发缓存中获取InjectionMetadata。 InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey); // 检查获取到的元数据是否需要刷新。 if (InjectionMetadata.needsRefresh(metadata, clazz)) { // 使用双重检查锁定确保线程安全。 synchronized (this.injectionMetadataCache) { metadata = this.injectionMetadataCache.get(cacheKey); if (InjectionMetadata.needsRefresh(metadata, clazz)) { // 如果有旧的元数据,清除它。 if (metadata != null) { metadata.clear(pvs); } // 为给定的类构建新的InjectionMetadata。 metadata = buildAutowiringMetadata(clazz); // 将新构建的元数据更新到缓存中。 this.injectionMetadataCache.put(cacheKey, metadata); } } } // 返回找到的或新构建的元数据。 return metadata; } ``` 在`org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#buildAutowiringMetadata`方法中,查找类及其所有父类中的字段和方法,以找出所有带有自动装配注解的字段和方法,并为它们创建一个统一的`InjectionMetadata`对象。 ```java private InjectionMetadata buildAutowiringMetadata(final Class clazz) { // 检查类是否含有自动装配注解,若无则直接返回空的InjectionMetadata。 if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) { return InjectionMetadata.EMPTY; } // 初始化存放注入元素的列表。 List elements = new ArrayList<>(); Class targetClass = clazz; do { // 当前类中要注入的元素列表。 final List currElements = new ArrayList<>(); // 处理类中的所有字段。 ReflectionUtils.doWithLocalFields(targetClass, field -> { // 查找字段上的自动装配注解。 MergedAnnotation ann = findAutowiredAnnotation(field); if (ann != null) { // ... [代码部分省略以简化] boolean required = determineRequiredStatus(ann); // 创建一个新的AutowiredFieldElement并加入到列表。 currElements.add(new AutowiredFieldElement(field, required)); } }); // 处理类中的所有方法。 ReflectionUtils.doWithLocalMethods(targetClass, method -> { Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) { return; } // 查找方法上的自动装配注解。 MergedAnnotation ann = findAutowiredAnnotation(bridgedMethod); if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) { // ... [代码部分省略以简化] boolean required = determineRequiredStatus(ann); PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz); // 创建一个新的AutowiredMethodElement并加入到列表。 currElements.add(new AutowiredMethodElement(method, required, pd)); } }); // 将当前类的注入元素加入到总的注入元素列表的开头。 elements.addAll(0, currElements); // 处理父类。 targetClass = targetClass.getSuperclass(); } // 循环直至Object类。 while (targetClass != null && targetClass != Object.class); // 返回为元素列表创建的新的InjectionMetadata。 return InjectionMetadata.forElements(elements, clazz); } ``` 在`org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#autowiredAnnotationTypes`字段中,主要的用途是告诉`AutowiredAnnotationBeanPostProcessor`哪些注解它应该处理。当Spring容器解析bean定义并创建bean实例时,如果这个bean的字段、方法或构造函数上的注解被包含在这个`autowiredAnnotationTypes`集合中,那么`AutowiredAnnotationBeanPostProcessor`就会对它进行处理。 ```java public AutowiredAnnotationBeanPostProcessor() { this.autowiredAnnotationTypes.add(Autowired.class); // ... [代码部分省略以简化] } ``` #### 注入阶段 在`org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessProperties`方法中,用于处理bean属性的后处理,特别是通过`@Autowired`等注解进行的属性注入。 ```java @Override public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) { // 获取与bean名称和类相关的InjectionMetadata。 // 这包括该bean需要进行注入的所有字段和方法。 InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs); try { // 使用获取到的InjectionMetadata,实际进行属性的注入。 metadata.inject(bean, beanName, pvs); } // 如果在注入过程中出现BeanCreationException,直接抛出。 catch (BeanCreationException ex) { throw ex; } // 捕获其他异常,并以BeanCreationException的形式抛出,提供详细的错误信息。 catch (Throwable ex) { throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex); } // 返回原始的PropertyValues,因为这个方法主要关注依赖注入而不是修改属性。 return pvs; } ``` 在`org.springframework.beans.factory.annotation.InjectionMetadata#inject`方法中,主要目的是将所有需要注入的元素(例如带有`@Autowired`等注解的字段或方法)注入到目标bean中。 ```java public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable { // 获取已经检查的元素。通常,在初始化阶段,所有的元素都会被检查一次。 Collection checkedElements = this.checkedElements; // 如果已经有检查过的元素,则使用它们,否则使用所有注入的元素。 Collection elementsToIterate = (checkedElements != null ? checkedElements : this.injectedElements); // 如果有需要注入的元素... if (!elementsToIterate.isEmpty()) { // 遍历每个元素并注入到目标bean中。 for (InjectedElement element : elementsToIterate) { // 对每个元素(字段或方法)执行注入操作。 element.inject(target, beanName, pvs); } } } ``` 在`org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject`方法中,首先检查字段的值是否已经被缓存。如果已缓存,则从缓存中获取,否则重新解析。然后,它确保字段是可访问的(特别是对于私有字段),并将解析的值设置到目标bean的相应字段中。 ```java @Override protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable { // 步骤1. 获取代表带有@Autowired注解的字段的Field对象。 Field field = (Field) this.member; Object value; // 步骤2. 如果字段的值已经被缓存(即先前已解析过),则尝试从缓存中获取。 if (this.cached) { try { // 从缓存中获取已解析的字段值。 value = resolvedCachedArgument(beanName, this.cachedFieldValue); } catch (NoSuchBeanDefinitionException ex) { // 如果缓存中的bean已被意外删除 -> 重新解析。 value = resolveFieldValue(field, bean, beanName); } } else { // 步骤3. 如果字段值未被缓存,直接解析。 value = resolveFieldValue(field, bean, beanName); } // 步骤4. 如果解析到的值不为null... if (value != null) { // 步骤4.1. 使字段可访问,这是必要的,特别是当字段是private时。 ReflectionUtils.makeAccessible(field); // 步骤4.2. 实际将解析的值注入到目标bean的字段中。 field.set(bean, value); } } ``` 首先来到`org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject`方法中的步骤3。在`org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#resolveFieldValue`方法中,通过`beanFactory.resolveDependency`方法从Spring的bean工厂中解析字段的值。 ```java @Nullable private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) { // ... [代码部分省略以简化] Object value; try { // 通过`beanFactory.resolveDependency`方法从Spring的bean工厂中解析字段的值 value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter); } catch (BeansException ex) { throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex); } // ... [代码部分省略以简化] return value; } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency`方法中,首先尝试获取一个延迟解析代理。如果无法获得,它会进一步尝试解析依赖。`doResolveDependency` 是实际进行解析工作的方法。 ```java public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName, @Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException { // ... [代码部分省略以简化] Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary( descriptor, requestingBeanName); if (result == null) { result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter); } return result; } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency`方法中,尝试解析一个特定的依赖,首先查找所有可能的匹配的 bean,然后选择一个最佳匹配的 bean。如果存在多个匹配的 bean 或没有找到匹配的 bean,它会进行相应的处理。 ```java public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName, @Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException { // ... [代码部分省略以简化] try { // 如果存在快捷解决依赖的方法,使用它 Object shortcut = descriptor.resolveShortcut(this); if (shortcut != null) { return shortcut; } // 获取依赖的类型 Class type = descriptor.getDependencyType(); // ... [代码部分省略以简化] // 步骤1. 根据依赖描述符查找匹配的bean Map matchingBeans = findAutowireCandidates(beanName, type, descriptor); // 如果没有找到匹配的bean if (matchingBeans.isEmpty()) { if (isRequired(descriptor)) { // 如果依赖是必需的,抛出异常 raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor); } return null; } String autowiredBeanName; Object instanceCandidate; // 当找到多个匹配的bean if (matchingBeans.size() > 1) { // 确定最佳的自动装配候选者 autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor); if (autowiredBeanName == null) { if (isRequired(descriptor) || !indicatesMultipleBeans(type)) { // 如果不能确定唯一的bean,尝试解析不唯一的依赖 return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans); } else { return null; } } instanceCandidate = matchingBeans.get(autowiredBeanName); } else { // 只找到一个匹配的bean Map.Entry entry = matchingBeans.entrySet().iterator().next(); autowiredBeanName = entry.getKey(); instanceCandidate = entry.getValue(); } // 添加自动装配的bean名到集合 if (autowiredBeanNames != null) { autowiredBeanNames.add(autowiredBeanName); } // 步骤2. 如果候选者是一个类,实例化它 if (instanceCandidate instanceof Class) { instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this); } Object result = instanceCandidate; // ... [代码部分省略以简化] return result; } // ... [代码部分省略以简化] } ``` 我们来到在`org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency`方法中的步骤1。在`org.springframework.beans.factory.support.DefaultListableBeanFactory#findAutowireCandidates`方法中,首先基于给定的类型获取所有可能的bean名。接着,对于每一个可能的候选bean,它检查该bean是否是一个合适的自动注入候选,如果是,它将这个bean添加到结果集中。最后,方法返回找到的所有合适的候选bean。 ```java protected Map findAutowireCandidates( @Nullable String beanName, Class requiredType, DependencyDescriptor descriptor) { // 根据所需的类型,包括所有父工厂中的bean,获取所有可能的bean名 String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this, requiredType, true, descriptor.isEager()); // ... [代码部分省略以简化] // 遍历所有候选bean名 for (String candidate : candidateNames) { // 如果候选bean不是正在查找的bean本身并且它是一个合适的自动注入候选 if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) { // 添加这个候选bean到结果中 addCandidateEntry(result, candidate, descriptor, requiredType); } } // ... [代码部分省略以简化] // 返回找到的所有候选bean return result; } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#addCandidateEntry`方法中,主要获取候选bean的类型,并将其添加到候选bean的集合中。 ```java private void addCandidateEntry(Map candidates, String candidateName, DependencyDescriptor descriptor, Class requiredType) { // ... [代码部分省略以简化] candidates.put(candidateName, getType(candidateName)); } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#getType(name)`方法中,通过bean的名字来获取对应bean的类型。 ```java public Class getType(String name) throws NoSuchBeanDefinitionException { return getType(name, true); } ``` 我们来到在`org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency`方法中的步骤2。在`org.springframework.beans.factory.config.DependencyDescriptor#resolveCandidate`方法中,最后发现`@Autowired` 的整个流程最终还是从Spring容器中获取一个bean实例并注入到相应的字段或构造函数参数中。 ```java public Object resolveCandidate(String beanName, Class requiredType, BeanFactory beanFactory) throws BeansException { return beanFactory.getBean(beanName); } ``` 最后我们来到`org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject`方法中的步骤4.2。在 `AutowiredFieldElement#inject` 方法内部,通过`resolveFieldValue(field, bean, beanName)`方法,来确定了正确的bean值并满足某个字段的 `@Autowired` 注解,将使用反射来实际设置这个值。具体地说,它会使用 `Field` 类的 `set` 方法来为目标对象的这个字段设置相应的值。这就是 `@Autowired` 在字段上使用时如何使得Spring能够自动为这个字段注入值的背后原理。 ```java // 步骤4. 如果解析到的值不为null... if (value != null) { // 步骤4.1. 使字段可访问,这是必要的,特别是当字段是private时。 ReflectionUtils.makeAccessible(field); // 步骤4.2. 实际将解析的值注入到目标bean的字段中。 field.set(bean, value); } ``` ### 八、注意事项 1. **默认情况下,依赖是必需的** - 如果Spring找不到匹配的bean来注入,它会抛出一个异常。我们可以通过将 `required` 属性设置为 `false` 来使其变为非必需:`@Autowired(required=false)`。 2. **类型匹配** - 默认情况下,Spring使用类型匹配来解析依赖。如果有多个匹配的bean,它会抛出一个异常。 3. **使用 `@Qualifier`** - 如果有多个相同类型的bean,我们可以使用 `@Qualifier` 注解来指定bean的名称,以解决歧义。 4. **循环依赖** - `@Autowired` 可能导致循环依赖的问题。例如,A依赖于B,而B依赖于A。Spring有一定的机制来处理单例作用域的bean的循环依赖,但对于原型作用域的bean,循环依赖会导致异常。 5. **注入位置** - 我们可以在字段、构造函数、或setter方法上使用 `@Autowired`。但是,推荐的做法是在构造函数上使用它,这样可以确保所有的依赖在对象创建时都已经注入。 6. **影响范围** - 使用 `@Autowired` 时,请注意不要在大范围的bean(例如单例)中注入小范围的bean(例如原型),除非我们清楚地知道自己在做什么。 7. **考虑使用构造函数注入** - 使用构造函数注入可以确保bean在构造时已完全初始化,从而使bean处于不变的状态。这也有助于在单元测试中模拟依赖关系。 8. **私有字段注入** - 尽管可以将 `@Autowired` 应用于私有字段,但这意味着Spring通过反射绕过了正常的Java访问控制来注入字段,这可能不是最佳实践。 9. **不支持静态字段** + `@Autowired` 不能用于静态字段。这是因为静态字段属于类而不是实例,而Spring是通过实例进行依赖注入的。 10. **不支持静态方法** + `@Autowired` 也不能用于静态setter方法或其他静态方法 ### 九、总结 #### 最佳实践总结 1. **上下文初始化** - 当我们创建 `AnnotationConfigApplicationContext` 并提供 `MyConfiguration` 类作为参数时,Spring 开始初始化上下文。这意味着它会加载所有的bean定义并准备创建实例。 2. **组件扫描** - 在 `MyConfiguration` 类中,我们使用了 `@ComponentScan` 注解指定了扫描的包路径。这使得Spring扫描指定包和其子包中的所有类,并查找标记为 `@Component`、`@Service`、`@Repository` 和 `@Controller` 等注解的类。找到后,Spring 会自动将这些类注册为bean。 3. **依赖解析** - 在 `MyController` 类中,我们在 `myService` 字段上使用了 `@Autowired` 注解。这告诉Spring,当创建 `MyController` bean时,需要找到一个 `MyService` 类型的bean,并自动注入到该字段中。 4. **实例化并注入** - 当我们从上下文中请求 `MyController` 类型的bean时,Spring会先创建 `MyController` 的一个实例。但在此之前,它会查看所有带有 `@Autowired` 注解的字段,然后为这些字段找到匹配的bean并注入。 - 在我们的例子中,Spring找到了 `MyService` 类型的bean并将其注入到了 `myService` 字段中。 5. **执行业务逻辑** - 在 `showService` 方法被调用时,它简单地打印了 `myService` 字段。由于这个字段已经被成功地自动注入,所以我们看到了预期的输出,证明 `@Autowired` 功能正常。 6. **结果** - 最终输出显示了 `myService` 已经被成功地注入到 `MyController` 中,并显示了其实例的内存地址。 #### 源码分析总结 1. **核心后处理器** - `AutowiredAnnotationBeanPostProcessor`是处理`@Autowired`等注解的主要后处理器。它实现了两个关键的接口,`MergedBeanDefinitionPostProcessor`和`InstantiationAwareBeanPostProcessor`,这两个接口允许在bean的生命周期中的关键阶段进行干预,为属性注入提供了机制。 2. **收集阶段** + 检索Autowired的元数据 - Spring首先使用`postProcessMergedBeanDefinition`方法确保给定的bean定义与其预期的自动装配元数据一致。 - 在该方法中, Spring会尝试查找与给定bean名称和类型相关的`InjectionMetadata`。这可能包括了该bean的字段和方法的注入信息。 + 寻找匹配的Autowiring元数据 - 在`findAutowiringMetadata`中,Spring确保始终为给定的bean名称和类获取最新和相关的`InjectionMetadata`。Spring也利用了缓存机制,以提高性能。 + 构建Autowiring元数据 - 在`buildAutowiringMetadata`方法中,Spring会查找类及其所有父类中的字段和方法,以找出所有带有自动装配注解的字段和方法。 - 然后,为这些字段和方法创建一个统一的`InjectionMetadata`对象。 + 检查注解类型 - 在`AutowiredAnnotationBeanPostProcessor`的构造方法中,主要的目的是告诉这个后处理器它应该处理哪些注解。例如, `@Autowired`就是这些注解之一。 3. **注入阶段** + 处理bean属性的后处理 - 在`postProcessProperties`中,Spring用于处理bean属性的后处理,特别是通过`@Autowired`进行的属性注入。 - 这涉及到实际将解析得到的值注入到bean中。 + 注入元数据的实际注入操作 - 在`InjectionMetadata#inject`方法中,这里会对bean进行属性的实际注入。 - Spring会遍历每一个需要注入的元素,并执行实际的注入操作。 + 字段的实际注入 - 在`AutowiredFieldElement#inject`中,Spring首先会检查字段的值是否已经被缓存。如果已缓存,则从缓存中获取,否则重新解析。 - 然后,它确保字段是可访问的,并将解析的值设置到目标bean的相应字段中。 + 解析依赖 - 在`doResolveDependency`方法中,Spring开始尝试解析一个特定的依赖。 - 首先,基于给定的类型,Spring会查找所有匹配的bean。 - 如果找到多个匹配的bean,它会尝试确定哪一个是最佳的自动装配候选。 + 获取bean的类型 - 在`addCandidateEntry`方法中,Spring主要获取候选bean的类型,并将其添加到候选bean的集合中。 - 使用`getType`方法,Spring可以通过bean的名字来获取对应bean的类型。 + 从Spring容器中获取bean实例 - 在`resolveCandidate`中,即从Spring容器中获取一个bean实例并注入到相应的字段或构造函数参数中。 + 反射注入 + 通过`field.set(bean, value)`来完成实际字段注入的步骤,将解析出的bean实例(value)注入到目标bean的对应字段上。这是整个`@Autowired`流程的最终步骤 ================================================ FILE: spring-annotation/spring-annotation-autowired/pom.xml ================================================ spring-annotation com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-annotation-autowired 11 11 ================================================ FILE: spring-annotation/spring-annotation-autowired/src/main/java/com/xcs/spring/AutowiredApplication.java ================================================ package com.xcs.spring; import com.xcs.spring.config.MyConfiguration; import com.xcs.spring.controller.MyController; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年08月07日 16时21分 **/ public class AutowiredApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MyController controller = context.getBean(MyController.class); controller.showService(); } } ================================================ FILE: spring-annotation/spring-annotation-autowired/src/main/java/com/xcs/spring/config/MyConfiguration.java ================================================ package com.xcs.spring.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan("com.xcs.spring") public class MyConfiguration { } ================================================ FILE: spring-annotation/spring-annotation-autowired/src/main/java/com/xcs/spring/controller/MyController.java ================================================ package com.xcs.spring.controller; import com.xcs.spring.service.MyService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; @Controller public class MyController { @Autowired private MyService myService; public void showService(){ System.out.println("myService = " + myService); } } ================================================ FILE: spring-annotation/spring-annotation-autowired/src/main/java/com/xcs/spring/service/MyService.java ================================================ package com.xcs.spring.service; import org.springframework.stereotype.Service; @Service public class MyService { } ================================================ FILE: spring-annotation/spring-annotation-bean/README.md ================================================ ## @Bean - [@Bean](#bean) - [一、基本信息](#一基本信息) - [二、注解描述](#二注解描述) - [三、注解源码](#三注解源码) - [四、主要功能](#四主要功能) - [五、最佳实践](#五最佳实践) - [六、时序图](#六时序图) - [@Bean注册时序图](#bean注册时序图) - [@Bean初始化方法时序图](#bean初始化方法时序图) - [@Bean销毁方法时序图](#bean销毁方法时序图) - [七、源码分析](#七源码分析) - [@Bean注册源码分析](#bean注册源码分析) - [@Bean初始化源码分析](#bean初始化源码分析) - [@Bean销毁源码分析](#bean销毁源码分析) - [八、注意事项](#八注意事项) - [九、总结](#九总结) - [最佳实践总结](#最佳实践总结) - [源码分析总结](#源码分析总结) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [我的CSDN](https://blog.csdn.net/duzhuang2399/article/details/132498762) 📚 **文章目录** - [所有文章](https://github.com/xuchengsheng/spring-reading) 🔗 **源码地址** - [@Bean源码](https://github.com/xuchengsheng/spring-reading/tree/master/spring-annotation/spring-annotation-bean) ### 二、注解描述 `@Bean` 是 Spring 框架的核心注解,用于标记一个方法,表明这个方法返回值应被注册为 Spring 容器中的一个对象(Bean)。与传统的 XML 配置方式相比,它提供了一种更加简洁和直观的方式来定义 Bean。通常,`@Bean` 与 `@Configuration` 注解一起使用,后者标记一个类为 Spring 的配置类。方法名默认作为 Bean 的 ID,但也可以通过 `@Bean` 的 `name` 属性自定义。这种声明式的 Bean 定义方式在 Java 代码中提供了强大的灵活性,允许我们利用 Java 的完整特性来配置和初始化对象。此外,结合其他 Spring 特性,如 `@Autowired`,可以轻松实现依赖注入,进一步简化了应用的配置和组件管理。总之,通过 `@Bean` 注解,Spring 为现代化的应用开发提供了强大的支持,使得代码更为整洁和易于维护。 ### 三、注解源码 `@Bean`注解是 Spring 框架自 3.0 版本开始引入的一个核心注解,这个注解表明一个方法会返回一个对象,该对象应该被注册为 Spring 应用上下文中的一个 bean。 ```java /** * 指示一个方法会产生一个由Spring容器管理的Bean。 * @author Rod Johnson * @author Costin Leau * @author Chris Beams * @author Juergen Hoeller * @author Sam Brannen * @since 3.0 * @see Configuration * @see Scope * @see DependsOn * @see Lazy * @see Primary * @see org.springframework.stereotype.Component * @see org.springframework.beans.factory.annotation.Autowired * @see org.springframework.beans.factory.annotation.Value */ @Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Bean { /** * {@link #name}的别名。 * 当不需要其他属性时可以使用,例如:{@code @Bean("customBeanName")}。 * @see #name */ @AliasFor("name") String[] value() default {}; /** * 这个bean的名称,或者如果有多个名称,则为主要的bean名称加上别名。 * 如果未指定,bean的名称是注解方法的名称。 * @see #value */ @AliasFor("value") String[] name() default {}; /** * 是否通过基于习惯的按名称或类型进行自动装配? * 注意,这种自动装配模式是基于按照习惯通过bean属性的setter方法进行的外部驱动的自动装配。 * @deprecated ... */ @Deprecated Autowire autowire() default Autowire.NO; /** * 这个bean是否可以作为其他bean的自动注入候选者? */ boolean autowireCandidate() default true; /** * 在初始化时调用bean实例上的方法的可选名称。 * 通常,可以在带有@Bean注解的方法的主体中直接以编程方式调用该方法。 */ String initMethod() default ""; /** * 在关闭应用程序上下文时调用bean实例上的方法的可选名称。 * 例如,一个JDBC DataSource的close()方法。 */ String destroyMethod() default AbstractBeanDefinition.INFER_METHOD; } ``` ### 四、主要功能 1. **Bean 的创建与注册**: + `@Bean` 注解用于标记一个方法,该方法返回的对象会被 Spring 容器管理。这意味着当应用上下文启动时,该方法会被调用,并且它的返回值会被添加到容器中作为一个 bean。 2. **自定义 Bean 名称** + 虽然默认的 bean 名称是标注 `@Bean` 的方法的名称,但可以通过 `@Bean` 的 `name` 属性为 bean 指定一个或多个名称。 3. **生命周期管理** + 通过 `initMethod` 和 `destroyMethod` 属性,可以为 bean 指定初始化和销毁的回调方法。当 bean 被创建或销毁时,这些方法会被调用。 4. **替代 XML 配置** + 在早期的 Spring 版本中,bean 通常是在 XML 文件中定义的。使用 `@Bean` 注解可以完全用 Java 配置来替代 XML,从而使配置更为集中和类型安全。 5. **灵活的依赖注入** + 在 `@Bean` 方法内部,可以直接调用其他 `@Bean` 方法,实现依赖的注入。这种方式保证了类型安全,并使得代码与配置紧密结合。 6. **与其他注解结合** + `@Bean` 注解经常与其他 Spring 注解一起使用,如 `@Scope`(定义 bean 的范围,如单例或原型),`@Lazy`(延迟 bean 的初始化),`@Primary`(当存在多个相同类型的 bean 时,标记一个为首选)等,为 bean 提供更详细的配置。 7. **控制自动装配行为** + 通过 `autowireCandidate` 属性,可以控制该 bean 是否应被视为自动装配的候选对象,当其他 bean 需要进行类型匹配的自动装配时。 ### 五、最佳实践 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyBean`类型的bean,最后调用`context.close()`方法关闭容器。 ```java public class BeanApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); System.out.println(context.getBean(MyBean.class)); context.close(); } } ``` 在 `MyConfiguration` 配置类中,通过 `@Bean` 注解定义了一个名为 `myBean` 的 bean。这个 bean 是 `MyBean` 类的实例。当 Spring 容器初始化这个 bean 时,它会首先调用 `MyBean` 的 `init` 方法(由 `initMethod` 属性指定)。当这个 bean 在容器关闭时被销毁,它会调用 `MyBean` 的 `destroy` 方法(由 `destroyMethod` 属性指定)。 ```java @Configuration public class MyConfiguration { @Bean(initMethod = "init",destroyMethod = "destroy") public MyBean myBean(){ return new MyBean(); } } ``` 当 Spring 容器创建 `MyBean` 的实例时,`init` 方法会被自动调用,因此 "MyBean.init" 会被输出到控制台。同样地,当 Spring 容器准备销毁这个 bean 或关闭时,`destroy` 方法会被调用,于是 "MyBean.destroy" 会被输出到控制台。 ```java public class MyBean { public void init(){ System.out.println("MyBean.init"); } public void destroy(){ System.out.println("MyBean.destroy"); } } ``` 运行结果发现,证明了在创建 bean 时 `init` 方法的调用以及在销毁 bean 时 `destroy` 方法的调用,从而展示了通过 `@Bean` 注解指定的生命周期方法在实际应用中的执行顺序。 ```java MyBean.init com.xcs.spring.bean.MyBean@2fb3536e MyBean.destroy ``` ### 六、时序图 #### @Bean注册时序图 ~~~mermaid sequenceDiagram Title: @Bean注册时序图 BeanApplication->>AnnotationConfigApplicationContext: AnnotationConfigApplicationContext(componentClasses)
创建应用上下文 AnnotationConfigApplicationContext->>AbstractApplicationContext: refresh()
刷新应用上下文 AbstractApplicationContext->>AbstractApplicationContext: invokeBeanFactoryPostProcessors(beanFactory)
调用BeanFactory后置处理器 AbstractApplicationContext->>PostProcessorRegistrationDelegate: invokeBeanFactoryPostProcessors(beanFactory,beanFactoryPostProcessors)
执行BeanFactory的后置处理器 PostProcessorRegistrationDelegate->>PostProcessorRegistrationDelegate: invokeBeanDefinitionRegistryPostProcessors(postProcessors,registry,applicationStartup)
执行BeanDefinition的注册后处理器 PostProcessorRegistrationDelegate->>ConfigurationClassPostProcessor: postProcessBeanDefinitionRegistry(registry)
处理@Configuration注解的类的Bean定义 ConfigurationClassPostProcessor->>ConfigurationClassPostProcessor: processConfigBeanDefinitions(registry)
处理配置类的Bean定义 ConfigurationClassPostProcessor->>ConfigurationClassParser: ConfigurationClassParser(...)
创建配置类解析器 ConfigurationClassParser-->>ConfigurationClassPostProcessor: 返回parser解析器
ConfigurationClassPostProcessor->>ConfigurationClassParser: parser.parse(candidates)
解析候选的@Configuration类 ConfigurationClassParser->>ConfigurationClassParser: parse(metadata, String beanName)
解析具体的@Configuration类 ConfigurationClassParser->>ConfigurationClassParser: processConfigurationClass(configClass,filter)
处理@Configuration类 ConfigurationClassParser->>+ConfigurationClassParser: doProcessConfigurationClass(configClass,sourceClass,filter)
实际处理@Configuration类 ConfigurationClassParser->>+ConfigurationClassParser: retrieveBeanMethodMetadata(sourceClass)
获取@Bean方法的元数据 ConfigurationClassParser-->>-ConfigurationClassParser: 返回Set
ConfigurationClassParser-->>-ConfigurationClassParser: 返回SourceClass
ConfigurationClassPostProcessor->>ConfigurationClassBeanDefinitionReader: loadBeanDefinitions(configurationModel)
加载Bean定义 ConfigurationClassBeanDefinitionReader->>ConfigurationClassBeanDefinitionReader: loadBeanDefinitionsForConfigurationClass(configClass,trackedConditionEvaluator)
为@Configuration类加载Bean定义 ConfigurationClassBeanDefinitionReader->>ConfigurationClassBeanDefinitionReader: loadBeanDefinitionsForBeanMethod(beanMethod)
为@Bean方法加载Bean定义 ConfigurationClassBeanDefinitionReader->>DefaultListableBeanFactory: registerBeanDefinition(beanName, beanDefToRegister)
在BeanFactory中注册Bean定义 ~~~ #### @Bean初始化方法时序图 ~~~mermaid sequenceDiagram Title: @Bean初始化方法时序图 BeanApplication->>AnnotationConfigApplicationContext: AnnotationConfigApplicationContext(componentClasses)
创建应用上下文 AnnotationConfigApplicationContext->>AbstractApplicationContext: refresh()
刷新应用上下文 AbstractApplicationContext->>AbstractApplicationContext: finishBeanFactoryInitialization(beanFactory)
完成BeanFactory的初始化 AbstractApplicationContext->>DefaultListableBeanFactory: preInstantiateSingletons()
预先实例化单例Bean DefaultListableBeanFactory->>AbstractBeanFactory: getBean(name)
获取Bean AbstractBeanFactory->>AbstractBeanFactory: doGetBean(name,requiredType,args,typeCheckOnly)
执行真实的获取Bean的操作 AbstractBeanFactory->>DefaultSingletonBeanRegistry: getSingleton(beanName,singletonFactory)
从单例注册表中获取Bean DefaultSingletonBeanRegistry->>AbstractBeanFactory: singletonFactory.getObject()
从singleton工厂中获取Bean对象 AbstractBeanFactory->>AbstractAutowireCapableBeanFactory: createBean(beanName,mbd,args)
创建Bean AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory: doCreateBean(beanName,mbd,args)
执行Bean的创建过程 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory: initializeBean(beanName,bean,mbd)
初始化Bean AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory: invokeInitMethods(beanName,bean,mbd)
调用Bean的初始化方法 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory: invokeCustomInitMethod(beanName,bean,mbd)
执行自定义的初始化方法 AbstractAutowireCapableBeanFactory->>Method: invoke(obj,args)
反射调用Bean的方法 Method->>MyBean: init()
执行Bean的init方法 AbstractBeanFactory-->>DefaultListableBeanFactory: 返回最终创建的Bean ~~~ #### @Bean销毁方法时序图 ~~~mermaid sequenceDiagram Title: @Bean销毁方法时序图 BeanApplication->>AnnotationConfigApplicationContext: AnnotationConfigApplicationContext(componentClasses)
创建应用上下文 AnnotationConfigApplicationContext-->>BeanApplication: 应用上下文初始化完成并返回应用 BeanApplication->>AbstractApplicationContext: close()
请求关闭应用上下文 AbstractApplicationContext->>AbstractApplicationContext: doClose()
执行关闭操作 AbstractApplicationContext->>AbstractApplicationContext: destroyBeans()
销毁容器中的所有Beans AbstractApplicationContext->>DefaultListableBeanFactory: destroySingletons()
销毁所有单例Beans DefaultListableBeanFactory->>DefaultSingletonBeanRegistry: super.destroySingletons()
调用父类方法,销毁所有单例Beans DefaultSingletonBeanRegistry->>DefaultSingletonBeanRegistry: destroySingleton(beanName)
销毁指定名称的单例Bean DefaultSingletonBeanRegistry->>DefaultSingletonBeanRegistry: destroyBean(beanName,bean)
执行Bean的销毁逻辑 DefaultSingletonBeanRegistry->>DisposableBeanAdapter: destroy()
调用Bean的销毁方法 DisposableBeanAdapter->>DisposableBeanAdapter: invokeCustomDestroyMethod(destroyMethod)
调用自定义的销毁方法 DisposableBeanAdapter->>Method: invoke(this.bean, args)
反射调用Bean的销毁方法 Method->>MyBean: destroy()
执行Bean的destroy方法 ~~~ ### 七、源码分析 #### @Bean注册源码分析 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyBean`类型的bean,最后调用`context.close()`方法关闭容器。 ```java public class BeanApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); System.out.println(context.getBean(MyBean.class)); context.close(); } } ``` 在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法。 ```java public AnnotationConfigApplicationContext(Class... componentClasses) { this(); register(componentClasses); refresh(); } ``` 在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 ```java @Override public void refresh() throws BeansException, IllegalStateException { // ... [代码部分省略以简化] // 调用在上下文中注册为bean的工厂处理器 invokeBeanFactoryPostProcessors(beanFactory); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors`方法中,又委托了`PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()`进行调用。 ```java protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) { PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors()); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors`方法中,首先调用了 `BeanDefinitionRegistryPostProcessor`(这是 `BeanFactoryPostProcessor` 的子接口)。它专门用来在所有其他 bean 定义加载之前修改默认的 bean 定义。 ```java public static void invokeBeanFactoryPostProcessors( ConfigurableListableBeanFactory beanFactory, List beanFactoryPostProcessors) { // ... [代码部分省略以简化] invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup()); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanDefinitionRegistryPostProcessors`方法中,循环调用了实现`BeanDefinitionRegistryPostProcessor`接口中的`postProcessBeanDefinitionRegistry(registry)`方法 ```java private static void invokeBeanDefinitionRegistryPostProcessors( Collection postProcessors, BeanDefinitionRegistry registry, ApplicationStartup applicationStartup) { for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) { StartupStep postProcessBeanDefRegistry = applicationStartup.start("spring.context.beandef-registry.post-process") .tag("postProcessor", postProcessor::toString); postProcessor.postProcessBeanDefinitionRegistry(registry); postProcessBeanDefRegistry.end(); } } ``` 在`org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry`方法中,调用了`processConfigBeanDefinitions`方法,该方法的主要目的是处理和注册配置类中定义的beans。 ```java @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { // ... [代码部分省略以简化] processConfigBeanDefinitions(registry); } ``` 在`org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions`方法中,这个方法主要处理了配置类的解析和验证,并确保了所有在配置类中定义的beans都被正确地注册到Spring的bean定义注册表中。 ```java public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { // ... [代码部分省略以简化] // 步骤1:创建一个用于解析配置类的解析器 ConfigurationClassParser parser = new ConfigurationClassParser( this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry); // 步骤2:初始化候选配置类集合以及已解析配置类集合 Set candidates = new LinkedHashSet<>(configCandidates); Set alreadyParsed = new HashSet<>(configCandidates.size()); // 步骤3:循环处理所有候选配置类,直至没有候选类为止 do { // 步骤3.1 解析配置类 parser.parse(candidates); // 步骤3.2 验证配置类 parser.validate(); // 获取解析后的配置类,并从中移除已经处理过的 Set configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); configClasses.removeAll(alreadyParsed); // 步骤4:如果reader为空,则创建一个新的Bean定义读取器 if (this.reader == null) { this.reader = new ConfigurationClassBeanDefinitionReader( registry, this.sourceExtractor, this.resourceLoader, this.environment, this.importBeanNameGenerator, parser.getImportRegistry()); } // 步骤5:使用读取器为解析的配置类加载Bean定义 this.reader.loadBeanDefinitions(configClasses); // ... [代码部分省略以简化] } while (!candidates.isEmpty()); // ... [代码部分省略以简化] } ``` 我们来到`org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions`方法中的步骤3.1。在`org.springframework.context.annotation.ConfigurationClassParser#parse`方法中,主要是遍历所有的配置类候选者,并对每一个带有注解的Bean定义进行解析。这通常涉及到查找该配置类中的@Bean方法、组件扫描指令等,并将这些信息注册到Spring容器中。 ```java public void parse(Set configCandidates) { // ... [代码部分省略以简化] parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName()); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.annotation.ConfigurationClassParser#parse(metadata, beanName)`方法中,将注解元数据和Bean名称转化为一个配置类,然后对其进行处理。处理配置类是Spring配置驱动的核心,它涉及到许多关键操作,如解析@Bean方法、处理@ComponentScan注解、处理@Import注解等。 ```java protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException { processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER); } ``` 在`org.springframework.context.annotation.ConfigurationClassParser#processConfigurationClass`方法中,处理一个给定的配置类。它首先递归地处理配置类及其父类,以确保所有相关的配置都被正确地读取并解析。在递归处理完所有相关配置后,它将配置类添加到已解析的配置类的映射中。 ```java protected void processConfigurationClass(ConfigurationClass configClass, Predicate filter) throws IOException { // ... [代码部分省略以简化] // 步骤1:递归地处理配置类及其超类层次结构 SourceClass sourceClass = asSourceClass(configClass, filter); do { sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter); } while (sourceClass != null); // 步骤2:将处理后的配置类放入映射中 this.configurationClasses.put(configClass, configClass); } ``` 在`org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass`方法中,进一步处理配置类。特别是,它处理配置类中所有带有@Bean注解的方法。这些方法定义了如何在Spring应用程序上下文中初始化bean。这里简化的注释重点放在@Bean方法上,但原方法可能还包含其他重要的处理逻辑。 ```java @Nullable protected final SourceClass doProcessConfigurationClass( ConfigurationClass configClass, SourceClass sourceClass, Predicate filter) throws IOException { // ... [代码部分省略以简化] // 处理配置类中的每一个@Bean注解的方法 // 这里的目标是识别和注册配置类中定义的所有@Bean方法。这些方法定义了如何初始化应用程序中的bean。 Set beanMethods = retrieveBeanMethodMetadata(sourceClass); for (MethodMetadata methodMetadata : beanMethods) { configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); } // ... [代码部分省略以简化] return null; } ``` 在`org.springframework.context.annotation.ConfigurationClassParser#retrieveBeanMethodMetadata`方法中,终于看到对`@Bean`注解的解析过程了,首先是尝试使用ASM获取确定的方法顺序,这一步是因为Java反射API返回的方法顺序是不确定的,但在某些场景下,方法的声明顺序可能是重要的。为了获得一个确定的顺序,我们尝试使用ASM(一个Java字节码处理框架)来读取类文件。其次是比较反射和ASM的结果,如果通过ASM获取到的方法数大于或等于通过反射获取的方法数,那么我们会进一步比较这两个集合。如果这两个集合的方法名相匹配,我们会更倾向于使用ASM的结果,因为它提供了确定的方法顺序。最后,方法返回一个`Set`,表示找到的所有标注了@Bean的方法。 **为什么要关心方法的顺序?** 在某些情况下,我们可能期望@Bean方法按照它们在类中的声明顺序执行。虽然这种依赖通常应该避免,但Spring还是尽可能地根据我们声明的这种顺序,特别是在涉及生命周期回调和依赖注入时。 ```java private Set retrieveBeanMethodMetadata(SourceClass sourceClass) { // 从源类中获取其注解元数据 AnnotationMetadata original = sourceClass.getMetadata(); // 从注解元数据中获取所有带有@Bean注解的方法 Set beanMethods = original.getAnnotatedMethods(Bean.class.getName()); // 如果发现多个@Bean方法,并且注解元数据是StandardAnnotationMetadata的实例, // 则尝试使用ASM库读取类文件,以获取确定的方法声明顺序。 if (beanMethods.size() > 1 && original instanceof StandardAnnotationMetadata) { try { // 使用ASM读取类文件的元数据 AnnotationMetadata asm = this.metadataReaderFactory.getMetadataReader(original.getClassName()).getAnnotationMetadata(); Set asmMethods = asm.getAnnotatedMethods(Bean.class.getName()); // 检查ASM读取的方法集是否包含所有通过标准反射检测到的方法 if (asmMethods.size() >= beanMethods.size()) { Set selectedMethods = new LinkedHashSet<>(asmMethods.size()); for (MethodMetadata asmMethod : asmMethods) { for (MethodMetadata beanMethod : beanMethods) { if (beanMethod.getMethodName().equals(asmMethod.getMethodName())) { selectedMethods.add(beanMethod); break; } } } if (selectedMethods.size() == beanMethods.size()) { // 所有通过反射检测到的方法都在ASM方法集中,因此使用ASM方法集 beanMethods = selectedMethods; } } } catch (IOException ex) { // 如果使用ASM读取类文件时发生错误,记录错误信息,并继续使用反射元数据 logger.debug("Failed to read class file via ASM for determining @Bean method order", ex); } } // 返回检索到的@Bean方法集 return beanMethods; } ``` 上面是步骤3.1对`@Bean`方法进行扫描,并将扫描结果存储在`ConfigurationClass`类中的`beanMethods`字段中。 我们来到`org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions`方法中的步骤4。在`org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitions`方法中,首先设置了一个跟踪条件评估的工具,然后遍历每一个配置类,为每一个配置类加载bean定义。这是Spring Java配置方式的核心过程,确保`@Bean`方法被正确地解析并在容器中注册为bean。 ```java public void loadBeanDefinitions(Set configurationModel) { TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator(); for (ConfigurationClass configClass : configurationModel) { loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator); } } ``` 在`org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass`方法中,对于每一个`BeanMethod`,调用了`loadBeanDefinitionsForBeanMethod`方法。这个方法会针对单个@Bean方法进行解析,并将其转换为一个Spring IoC容器可以管理的Bean定义。 ```java private void loadBeanDefinitionsForConfigurationClass( ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) { // ... [代码部分省略以简化] for (BeanMethod beanMethod : configClass.getBeanMethods()) { loadBeanDefinitionsForBeanMethod(beanMethod); } // ... [代码部分省略以简化] } ``` 在`org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForBeanMethod`方法中,如果@Bean方法是静态的,那么它直接使用方法所在类作为bean的类。否则,它使用配置类的bean名称作为工厂bean名,并设置工厂方法名为当前@Bean方法的名字,然后尝试获取更详细的方法元数据,特别是如果元数据是`StandardMethodMetadata`类型的,它可以直接获取原始方法对象,然后还要处理`autowire`, `autowireCandidate`, `initMethod`, 和`destroyMethod`等属性,这些属性提供了有关如何处理bean生命周期和依赖注入的信息。然后如果@Bean方法有`@Scope`注解,它会处理这个注解的属性,如作用域名和代理模式,然后需要为这个bean创建一个代理,最后,使用`registerBeanDefinition`方法将Bean定义注册到IoC容器中。 ```java private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) { // ... [代码部分省略以简化] // 创建一个新的Bean定义基于@Bean方法的元数据 ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata, beanName); // 设置Bean定义的来源信息 beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource())); // 检查@Bean方法是否为静态方法 if (metadata.isStatic()) { // 处理静态@Bean方法 if (configClass.getMetadata() instanceof StandardAnnotationMetadata) { beanDef.setBeanClass(((StandardAnnotationMetadata) configClass.getMetadata()).getIntrospectedClass()); } else { beanDef.setBeanClassName(configClass.getMetadata().getClassName()); } beanDef.setUniqueFactoryMethodName(methodName); } else { // 处理实例@Bean方法 beanDef.setFactoryBeanName(configClass.getBeanName()); beanDef.setUniqueFactoryMethodName(methodName); } // 如果元数据是StandardMethodMetadata类型的,设置已解析的工厂方法 if (metadata instanceof StandardMethodMetadata) { beanDef.setResolvedFactoryMethod(((StandardMethodMetadata) metadata).getIntrospectedMethod()); } // 设置自动装配模式为构造器自动装配 beanDef.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR); beanDef.setAttribute(org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor. SKIP_REQUIRED_CHECK_ATTRIBUTE, Boolean.TRUE); // 处理Bean定义的通用注解属性 AnnotationConfigUtils.processCommonDefinitionAnnotations(beanDef, metadata); // 获取并设置autowire属性 Autowire autowire = bean.getEnum("autowire"); if (autowire.isAutowire()) { beanDef.setAutowireMode(autowire.value()); } // 获取并设置autowireCandidate属性 boolean autowireCandidate = bean.getBoolean("autowireCandidate"); if (!autowireCandidate) { beanDef.setAutowireCandidate(false); } // 获取并设置初始化方法名 String initMethodName = bean.getString("initMethod"); if (StringUtils.hasText(initMethodName)) { beanDef.setInitMethodName(initMethodName); } // 获取并设置销毁方法名 String destroyMethodName = bean.getString("destroyMethod"); beanDef.setDestroyMethodName(destroyMethodName); // 根据@Scope注解处理Bean的作用域及其代理模式 ScopedProxyMode proxyMode = ScopedProxyMode.NO; AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(metadata, Scope.class); if (attributes != null) { beanDef.setScope(attributes.getString("value")); proxyMode = attributes.getEnum("proxyMode"); if (proxyMode == ScopedProxyMode.DEFAULT) { proxyMode = ScopedProxyMode.NO; } } // 如果需要,创建一个代理的Bean定义 BeanDefinition beanDefToRegister = beanDef; if (proxyMode != ScopedProxyMode.NO) { BeanDefinitionHolder proxyDef = ScopedProxyCreator.createScopedProxy( new BeanDefinitionHolder(beanDef, beanName), this.registry, proxyMode == ScopedProxyMode.TARGET_CLASS); beanDefToRegister = new ConfigurationClassBeanDefinition( (RootBeanDefinition) proxyDef.getBeanDefinition(), configClass, metadata, beanName); } // 最终将@Bean方法对应的Bean定义注册到容器 this.registry.registerBeanDefinition(beanName, beanDefToRegister); } ``` #### @Bean初始化源码分析 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyBean`类型的bean,最后调用`context.close()`方法关闭容器。 ```java public class BeanApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); System.out.println(context.getBean(MyBean.class)); context.close(); } } ``` 在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法 ```java public AnnotationConfigApplicationContext(Class... componentClasses) { this(); register(componentClasses); refresh(); } ``` 在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 ```java @Override public void refresh() throws BeansException, IllegalStateException { // ... [代码部分省略以简化] // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization`方法中,会继续调用`DefaultListableBeanFactory`类中的`preInstantiateSingletons`方法来完成所有剩余非懒加载的单列Bean对象。 ```java /** * 完成此工厂的bean初始化,实例化所有剩余的非延迟初始化单例bean。 * * @param beanFactory 要初始化的bean工厂 */ protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { // ... [代码部分省略以简化] // 完成所有剩余非懒加载的单列Bean对象。 beanFactory.preInstantiateSingletons(); } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons`方法中,主要的核心目的是预先实例化所有非懒加载的单例bean。在Spring的上下文初始化完成后,该方法会被触发,以确保所有单例bean都被正确地创建并初始化。其中`getBean(beanName)`是此方法的核心操作。对于容器中定义的每一个单例bean,它都会调用`getBean`方法,这将触发bean的实例化、初始化及其依赖的注入。如果bean之前没有被创建过,那么这个调用会导致其被实例化和初始化。 ```java public void preInstantiateSingletons() throws BeansException { // ... [代码部分省略以简化] // 循环遍历所有bean的名称 for (String beanName : beanNames) { getBean(beanName); } // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#getBean()`方法中,又调用了`doGetBean`方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,`doGetBean`负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。 ```java @Override public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中,首先检查所请求的bean是否是一个单例并且已经创建。如果尚未创建,它将创建一个新的实例。在这个过程中,它处理可能的异常情况,如循环引用,并确保返回的bean是正确的类型。这是Spring容器bean生命周期管理的核心部分。 ```java protected T doGetBean( String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException { // ... [代码部分省略以简化] // 开始创建bean实例 if (mbd.isSingleton()) { // 如果bean是单例的,我们会尝试从单例缓存中获取它 // 如果不存在,则使用lambda创建一个新的实例 sharedInstance = getSingleton(beanName, () -> { try { // 尝试创建bean实例 return createBean(beanName, mbd, args); } catch (BeansException ex) { // ... [代码部分省略以简化] } }); // 对于某些bean(例如FactoryBeans),可能需要进一步处理以获取真正的bean实例 beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } // ... [代码部分省略以简化] // 确保返回的bean实例与请求的类型匹配 return adaptBeanInstance(name, beanInstance, requiredType); } ``` 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()`方法中,主要负责从单例缓存中获取一个已存在的bean实例,或者使用提供的`ObjectFactory`创建一个新的实例。这是确保bean在Spring容器中作为单例存在的关键部分。 ```java public Object getSingleton(String beanName, ObjectFactory singletonFactory) { // 断言bean名称不能为空 Assert.notNull(beanName, "Bean name must not be null"); // 同步访问单例对象缓存,确保线程安全 synchronized (this.singletonObjects) { // 从缓存中获取单例对象 Object singletonObject = this.singletonObjects.get(beanName); // 如果缓存中没有找到 if (singletonObject == null) { // ... [代码部分省略以简化] try { // 使用工厂创建新的单例实例 singletonObject = singletonFactory.getObject(); newSingleton = true; } catch (IllegalStateException ex) { // ... [代码部分省略以简化] } catch (BeanCreationException ex) { // ... [代码部分省略以简化] } finally { // ... [代码部分省略以简化] } // ... [代码部分省略以简化] } // 返回单例对象 return singletonObject; } } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()`方法中,主要的逻辑是调用 `doCreateBean`,这是真正进行 bean 实例化、属性填充和初始化的地方。这个方法会返回新创建的 bean 实例。 ```java @Override protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // ... [代码部分省略以简化] try { // 正常的bean实例化、属性注入和初始化。 // 这里是真正进行bean创建的部分。 Object beanInstance = doCreateBean(beanName, mbdToUse, args); // 记录bean成功创建的日志 if (logger.isTraceEnabled()) { logger.trace("Finished creating instance of bean '" + beanName + "'"); } return beanInstance; } catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { // ... [代码部分省略以简化] } catch (Throwable ex) { // ... [代码部分省略以简化] } } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中,`initializeBean`方法是bean初始化,确保bean是完全配置和准备好的。 ```java protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // 声明一个对象,后续可能用于存放初始化后的bean或它的代理对象 Object exposedObject = bean; // ... [代码部分省略以简化] try { // ... [代码部分省略以简化] // bean初始化 exposedObject = initializeBean(beanName, exposedObject, mbd); } catch (Throwable ex) { // ... [代码部分省略以简化] } // 返回创建和初始化后的bean return exposedObject; } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean`方法中,如果bean实现了`InitializingBean`接口,则它的`afterPropertiesSet`方法会在此处被调用。此外,如果bean配置中定义了自定义的初始化方法,spring会在这里被调用。 ```java protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { // ... [代码部分省略以简化] try { invokeInitMethods(beanName, wrappedBean, mbd); } catch (Throwable ex) { // ... [代码部分省略以简化] } // ... [代码部分省略以简化] return wrappedBean; } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeInitMethods`方法中,`afterPropertiesSet`是`InitializingBean`接口中定义的一个特殊的初始化方法。如果bean实现了这个接口,这个方法会在bean的属性被注入之后自动被调用。在这里,为了避免重复调用,Spring会检查bean是否是`InitializingBean`的实例,以及其初始化方法名是否为`afterPropertiesSet`。 ```java protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd) throws Throwable { // ... [代码部分省略以简化] // 检查Bean定义是否存在,且bean实例不是一个NullBean(Spring中用于标记占位bean的特殊类型) if (mbd != null && bean.getClass() != NullBean.class) { // 获取初始化方法名 String initMethodName = mbd.getInitMethodName(); // 检查是否有初始化方法需要被调用 if (StringUtils.hasLength(initMethodName) && // bean不是一个InitializingBean,或者初始化方法名不是"afterPropertiesSet" !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) && // 初始化方法没有被外部管理(例如,通过AspectJ的切面) !mbd.isExternallyManagedInitMethod(initMethodName)) { // 调用自定义的初始化方法 invokeCustomInitMethod(beanName, bean, mbd); } } } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeCustomInitMethod`方法中,使用Java反射API来动态地调用Bean的初始化方法。 ```java protected void invokeCustomInitMethod(String beanName, Object bean, RootBeanDefinition mbd) throws Throwable { // ... [代码部分省略以简化] // 使用反射来调用Bean的初始化方法 try { // 确保要调用的方法是可访问的 ReflectionUtils.makeAccessible(methodToInvoke); // 调用Bean的自定义初始化方法 methodToInvoke.invoke(bean); } catch (InvocationTargetException ex) { // 如果初始化方法抛出了异常,这里会捕获到,并继续抛出该异常的目标异常 throw ex.getTargetException(); } // ... [代码部分省略以简化] } ``` #### @Bean销毁源码分析 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyBean`类型的bean,最后调用`context.close()`方法关闭容器。 ```java public class BeanApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); System.out.println(context.getBean(MyBean.class)); context.close(); } } ``` 在`org.springframework.context.support.AbstractApplicationContext#close`方法中,首先是启动了一个同步块,它同步在 `startupShutdownMonitor` 对象上。这确保了在给定时刻只有一个线程可以执行这个块内的代码,防止多线程导致的资源竞争或数据不一致,然后是调用了 `doClose` 方法,最后是为 JVM 注册了一个关闭钩子。 ```java @Override public void close() { synchronized (this.startupShutdownMonitor) { doClose(); // If we registered a JVM shutdown hook, we don't need it anymore now: // We've already explicitly closed the context. if (this.shutdownHook != null) { try { Runtime.getRuntime().removeShutdownHook(this.shutdownHook); } catch (IllegalStateException ex) { // ignore - VM is already shutting down } } } } ``` 在`org.springframework.context.support.AbstractApplicationContext#doClose`方法中,又调用了 `destroyBeans` 方法。 ```java protected void doClose() { // ... [代码部分省略以简化] // Destroy all cached singletons in the context's BeanFactory. destroyBeans(); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.AbstractApplicationContext#destroyBeans`方法中,首先是调用了`getBeanFactory()`返回 Spring 的 `BeanFactory` ,然后在获得的 `BeanFactory` 上,调用了 `destroySingletons` 方法,这个方法的目的是销毁所有在 `BeanFactory` 中缓存的单例 beans。 ```java protected void destroyBeans() { getBeanFactory().destroySingletons(); } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#destroySingletons`方法中,首先是调用了父类的 `destroySingletons` 方法,为了确保继承自父类的销毁逻辑得到了执行。 ```java @Override public void destroySingletons() { super.destroySingletons(); updateManualSingletonNames(Set::clear, set -> !set.isEmpty()); clearByTypeCache(); } ``` 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#destroySingletons`方法中,首先是在`disposableBeans` 字段上,从其键集合中获取所有的 bean 名称,并将它们转换为一个字符串数组。`disposableBeans` 可能包含了实现了 `DisposableBean` 接口的 beans,这些 beans 需要在容器销毁时特殊处理,最后倒序循环,从最后一个开始,销毁所有在 `disposableBeans` 列表中的 beans。这样做是为了确保依赖关系正确地处理,beans先被创建的应该后被销毁。 ```java public void destroySingletons() { // ... [代码部分省略以简化] String[] disposableBeanNames; synchronized (this.disposableBeans) { disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet()); } for (int i = disposableBeanNames.length - 1; i >= 0; i--) { destroySingleton(disposableBeanNames[i]); } // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#destroySingleton`方法中,首先是调用了父类的 `destroySingleton` 方法,为了确保继承自父类的销毁逻辑得到了执行。 ```java @Override public void destroySingleton(String beanName) { super.destroySingleton(beanName); removeManualSingletonName(beanName); clearByTypeCache(); } ``` 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#destroySingleton`方法中,首先是使用 `synchronized` 关键字在 `disposableBeans` 对象上进行同步,以确保在多线程环境中安全地访问和修改它,从 `disposableBeans` 集合中移除指定名称的 bean,并将其转换为 `DisposableBean` 类型,最后调用`destroyBean`方法执行实际的销毁操作。 ```java public void destroySingleton(String beanName) { // Remove a registered singleton of the given name, if any. removeSingleton(beanName); // Destroy the corresponding DisposableBean instance. DisposableBean disposableBean; synchronized (this.disposableBeans) { disposableBean = (DisposableBean) this.disposableBeans.remove(beanName); } destroyBean(beanName, disposableBean); } ``` 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#destroyBean`方法中,直接调用 `bean` 的 `destroy` 方法。因为 `bean` 是一个 `DisposableBean` 类型的实例,所以它一定有一个 `destroy` 方法,该方法提供了 bean 的自定义销毁逻辑。 ```java protected void destroyBean(String beanName, @Nullable DisposableBean bean) { // ... [代码部分省略以简化] // Actually destroy the bean now... if (bean != null) { try { bean.destroy(); } catch (Throwable ex) { // ... [代码部分省略以简化] } } // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.support.DisposableBeanAdapter#destroy`方法中,此方法首先检查是否已经为bean指定了一个销毁方法(`destroyMethod`)。如果有,则直接调用`invokeCustomDestroyMethod`方法。如果没有提供具体的方法,但提供了方法的名称(`destroyMethodName`),那么它会尝试根据这个名称找到对应的方法。一旦找到了方法,就会调用`invokeCustomDestroyMethod`方法。 ```java @Override public void destroy() { // ... [代码部分省略以简化] // 如果提供了销毁方法(destroyMethod),则直接调用它 if (this.destroyMethod != null) { invokeCustomDestroyMethod(this.destroyMethod); } // 否则,如果提供了销毁方法的名称(destroyMethodName),则尝试找到对应的方法并调用它 else if (this.destroyMethodName != null) { // 根据方法名确定要调用的销毁方法 Method methodToInvoke = determineDestroyMethod(this.destroyMethodName); if (methodToInvoke != null) { // 如果找到了方法,则调用它,但确保使用的是方法的接口版本(如果可能的话) invokeCustomDestroyMethod(ClassUtils.getInterfaceMethodIfPossible(methodToInvoke)); } } } ``` 在`org.springframework.beans.factory.support.DisposableBeanAdapter#invokeCustomDestroyMethod`方法中,用于执行自定义的销毁方法。自定义销毁方法是由我们在Spring配置中为bean定义的,用于在bean的生命周期结束时释放资源、清理等。 ```java private void invokeCustomDestroyMethod(final Method destroyMethod) { // ... [代码部分省略以简化] try { if (System.getSecurityManager() != null) { // ... [代码部分省略以简化] } else { ReflectionUtils.makeAccessible(destroyMethod); destroyMethod.invoke(this.bean, args); } } catch (InvocationTargetException ex) { // ... [代码部分省略以简化] } catch (Throwable ex) { // ... [代码部分省略以简化] } } ``` ### 八、注意事项 1. **方法名称即Bean名称** + 默认情况下,使用`@Bean`注解的方法名称将用作Bean的名称。如果需要自定义bean的名称,可以在`@Bean`注解中指定名称,如`@Bean("customBeanName")`。 2. **返回类型** + 确保`@Bean`方法的返回类型与实际创建的bean对象匹配。 3. **调用其他`@Bean`方法** + 在一个配置类中,可以调用一个`@Bean`方法来注入另一个`@Bean`方法的结果。这是因为`@Bean`方法在调用时是通过Spring代理进行的,这确保了单例beans的单一实例性。 4. **作用域** + 使用`@Scope`注解与`@Bean`一起可以定义Bean的作用域,例如单例(默认)、原型、请求、会话等。 5. **生命周期回调** + `@Bean`注解允许指定`initMethod`和`destroyMethod`,这些方法分别在bean初始化和销毁时被调用。 6. **避免重复定义** + 确保不在XML和Java配置中同时定义同一个bean。如果必须这样做,确保bean的名称和定义相同,否则会出现不可预测的行为。 7. **慎用`@Lazy`** + `@Lazy`注解使得bean在首次请求时才被初始化。如果一个bean需要在应用启动时就初始化,那么不应该标记为延迟初始化。 8. **参数化`@Bean`方法** + `@Bean`方法可以接受参数,这些参数会从Spring容器中自动解析。这在创建一个bean依赖于另一个bean时特别有用。 9. **考虑线程安全性** + 特别是对于原型作用域的bean,确保bean是线程安全的,或者不会在多个线程之间共享。 10. **使用条件注解** + 配合`@Conditional`或其他相关注解,可以在满足某些条件时才创建bean。 11. **`@Bean`与`@Component`的区别** + 虽然两者都用于定义bean,但`@Bean`通常用于方法,特别是在Java配置类中,而`@Component`(及其特化:`@Service`、`@Repository`、`@Controller`等)用于类。 12. **注意潜在的循环依赖** + 如果两个`@Bean`方法相互调用,可能会出现循环依赖。Spring可以解决单例作用域的bean之间的循环依赖,但不推荐这样做。 13. **配置类应被Spring管理** + 确保包含`@Bean`方法的类由Spring管理,并标记为`@Configuration`。这确保`@Bean`方法由Spring的代理调用,从而支持上述提到的特性,如单例保证和循环引用。 ### 九、总结 #### 最佳实践总结 1. **应用启动** + 使用`AnnotationConfigApplicationContext`可以基于Java注解来启动和配置Spring的上下文。在上述示例中,我们传递了一个Java配置类 `MyConfiguration` 作为参数来初始化这个上下文。 2. **配置类的使用** + 使用 `@Configuration` 注解标记配置类,表明这个类包含Spring的bean定义。在配置类中,可以使用 `@Bean` 注解来定义bean。这个注解的方法的名称默认会作为bean的名称,返回的实例则为Spring容器管理的bean实例。 3. **Bean的生命周期** + 通过 `@Bean` 注解的 `initMethod` 和 `destroyMethod` 属性,可以为bean定义初始化和销毁时要调用的方法。这为bean提供了一种自定义的初始化和清理机制。当bean被Spring容器实例化时,指定的初始化方法会被调用;当bean被销毁或容器关闭时,指定的销毁方法会被调用。 #### 源码分析总结 1. **@Bean 注册源码分析总结** + **启动及配置加载** + 使用 `AnnotationConfigApplicationContext` 初始化Spring上下文,并传入 `MyConfiguration` 配置类。该上下文构造函数将执行 `register()` 和 `refresh()` 方法。 + **Bean定义的解析** + `refresh()` 方法触发上下文的刷新,其中涉及到对bean定义的处理。调用 `invokeBeanFactoryPostProcessors()` 方法来处理bean工厂后置处理器。 + **处理配置类** + `BeanDefinitionRegistryPostProcessor` 是特殊的 `BeanFactoryPostProcessor`,它会在所有其他bean定义被加载之前运行。这个流程主要通过 `ConfigurationClassPostProcessor` 实现,该类负责处理标有 `@Configuration` 的类。 + **解析 `@Bean` 方法** + 对每个 `@Configuration` 类执行解析,将其中的 `@Bean` 方法解析为bean定义。这涉及读取方法元数据,并将其转化为一个Spring可以理解和管理的 `BeanDefinition` 对象。 + **处理bean作用域与代理** + 如果在 `@Bean` 方法上有 `@Scope` 注解,会根据其属性处理bean的作用域。根据需要,可能会为bean创建一个代理,这是通过 `ScopedProxyCreator` 来完成的。 + **bean定义的注册** + 最终,解析出的bean定义会被注册到Spring的bean定义注册表中,这样在上下文启动后,这些定义的bean就可以被实例化和使用了。 2. **@Bean初始化源码分析总结** + **启动** + 当使用`AnnotationConfigApplicationContext`并提供`MyConfiguration`类作为参数时,Spring容器开始初始化。 + **刷新上下文** + 通过调用`refresh()`方法,Spring上下文被刷新。这是在上下文中创建和初始化所有bean的关键阶段。 + **初始化Bean工厂** + 方法`finishBeanFactoryInitialization`会确保所有非懒加载的单例bean都被初始化。它调用`DefaultListableBeanFactory`中的`preInstantiateSingletons`方法,该方法循环遍历容器中定义的每一个单例bean并使用`getBean`方法进行初始化。 + **获取Bean**: + 在获取bean时,首先会检查是否已存在此单例bean。如果不存在,则会创建它。核心方法是`doGetBean`,它管理整个bean的生命周期,从查找bean定义到初始化bean。 + **单例处理** + `getSingleton`方法管理单例bean的缓存。它首先尝试从缓存中获取bean,如果未找到,则使用提供的`ObjectFactory`创建一个新的实例。 + **创建Bean** + 如果需要,`createBean`方法将负责实例化bean。最终,真正的bean创建工作是在`doCreateBean`方法中完成的。 + **初始化Bean** + 一旦bean被实例化,就需要进行初始化。如果bean实现了`InitializingBean`接口,那么它的`afterPropertiesSet`方法会被调用。此外,如果bean配置中定义了自定义的初始化方法,该方法也会在此时被调用。 + **反射调用** + 最终,如果定义了自定义的初始化方法,Spring会使用Java的反射API来调用它。 3. **@Bean销毁源码分析总结** + **启动和关闭** + 通过`AnnotationConfigApplicationContext`,Spring容器初始化后,`context.close()`方法会被调用以关闭容器。 + **关闭上下文** + `close()`方法首先同步`startupShutdownMonitor`,确保在特定时刻只有一个线程能关闭上下文,接着调用`doClose`方法执行实际关闭操作。 + **销毁Beans** + 在`doClose`方法中,`destroyBeans`方法被调用以销毁所有缓存中的单例bean。 + **销毁单例Beans** + `destroyBeans`方法会调用BeanFactory的`destroySingletons`方法来销毁所有缓存的单例beans。 + **遍历并销毁** + 在`destroySingletons`中,所有缓存的单例bean都会被遍历。对于每一个bean,`destroySingleton`方法会被调用来执行销毁操作。 + **实际的销毁操作** + 在`destroySingleton`中,会从`disposableBeans`列表中移除对应的bean,并执行实际的销毁操作。 + **自定义销毁逻辑** + 如果bean实现了`DisposableBean`接口或者定义了自定义的销毁方法,Spring容器会确保在销毁bean时调用这些方法。销毁方法可以是定义在bean配置中的任何方法,不需要特定的方法签名。 + **反射调用销毁方法** + 最后,Spring使用Java反射API来动态地调用bean的自定义销毁方法。 ================================================ FILE: spring-annotation/spring-annotation-bean/pom.xml ================================================ spring-annotation com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-annotation-bean ================================================ FILE: spring-annotation/spring-annotation-bean/src/main/java/com/xcs/spring/BeanApplication.java ================================================ package com.xcs.spring; import com.xcs.spring.bean.MyBean; import com.xcs.spring.config.MyConfiguration; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年08月07日 16时21分 **/ public class BeanApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); System.out.println(context.getBean(MyBean.class)); context.close(); } } ================================================ FILE: spring-annotation/spring-annotation-bean/src/main/java/com/xcs/spring/bean/MyBean.java ================================================ package com.xcs.spring.bean; /** * @author xcs * @date 2023年08月25日 10时22分 **/ public class MyBean { public void init(){ System.out.println("MyBean.init"); } public void destroy(){ System.out.println("MyBean.destroy"); } } ================================================ FILE: spring-annotation/spring-annotation-bean/src/main/java/com/xcs/spring/config/MyConfiguration.java ================================================ package com.xcs.spring.config; import com.xcs.spring.bean.MyBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @author xcs * @date 2023年08月07日 16时25分 **/ @Configuration public class MyConfiguration { @Bean(initMethod = "init",destroyMethod = "destroy") public MyBean myBean(){ return new MyBean(); } } ================================================ FILE: spring-annotation/spring-annotation-componentScan/README.md ================================================ ## @ComponentScan - [@ComponentScan](#componentscan) - [一、基本信息](#一基本信息) - [二、注解描述](#二注解描述) - [三、注解源码](#三注解源码) - [四、主要功能](#四主要功能) - [五、最佳实践](#五最佳实践) - [六、时序图](#六时序图) - [七、源码分析](#七源码分析) - [八、注意事项](#八注意事项) - [九、总结](#九总结) - [最佳实践总结](#最佳实践总结) - [源码分析总结](#源码分析总结) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [我的CSDN](https://blog.csdn.net/duzhuang2399/article/details/132346179) 📚 **文章目录** - [所有文章](https://github.com/xuchengsheng/spring-reading) 🔗 **源码地址** - [@ComponentScan源码](https://github.com/xuchengsheng/spring-reading/blob/master/spring-annotation/spring-annotation-componentScan/README.md) ### 二、注解描述 `@ComponentScan` 注解,用于自动扫描特定包(和其子包)中的组件,并自动注册为 Spring 容器中的 bean。当我们使用 Spring Boot,它默认会扫描主应用程序所在的包以及子包。但是,如果我们需要更细粒度的控制,或者我们在使用传统的 Spring 而非 Spring Boot,那么我们可能会明确地使用 `@ComponentScan`。 ### 三、注解源码 `@ComponentScan`注解是 Spring 框架自 3.1 版本开始引入的一个核心注解,用于指导如何扫描组件。与 `@Configuration` 配合使用,其功能与 Spring XML 的 `` 类似。除了允许指定要扫描的包,它还提供了多种属性,如命名生成器、范围解析器、代理设置等,以精细地控制组件的扫描和注册过程。若不指定扫描包,它默认从注解声明的位置开始。与此同时,`@Filter` 注解定义了类型过滤器,特别用于 `@ComponentScan` 中的组件包含和排除设置。它允许基于特定类型、类或模式来筛选组件。 ```java /** * 配置 @Configuration 类使用的组件扫描指令。 * 提供与 Spring XML 的 元素相似的支持。 * * 可以指定 #basePackageClasses 或 #basePackages (或其别名 * #value }) 来定义要扫描的特定包。如果没有定义特定的包, * 则从声明此注解的类的包开始扫描。 * * 注意, 元素有一个 * annotation-config 属性; 但是,此注解没有。这是因为 * 在几乎所有使用 @ComponentScan 的情况下,默认的注解配置 * 处理(例如处理 @Autowired 及其朋友们)都是预期的。此外, * 使用 AnnotationConfigApplicationContext 时,总是会注册注解配置处理器, * 这意味着在 @ComponentScan 级别尝试禁用它们都会被忽略。 * * 有关使用示例,请参见 Configuration @Configuration 的 Javadoc。 * * @author Chris Beams * @author Juergen Hoeller * @author Sam Brannen * @since 3.1 * @see Configuration */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Repeatable(ComponentScans.class) public @interface ComponentScan { /** * #basePackages 的别名。 * 如果不需要其他属性,则允许更简洁的注解声明,例如,@ComponentScan("org.my.pkg") * 而不是 @ComponentScan(basePackages = "org.my.pkg")。 */ @AliasFor("basePackages") String[] value() default {}; /** * 扫描带注解的组件的基础包。 * #value 是此属性的别名(且与此属性互斥)。 * 使用 #basePackageClasses 作为基于类型安全的替代方法 * 来指定要扫描注解的组件的包。将扫描每个指定类的包。 */ @AliasFor("value") String[] basePackages() default {}; /** * 指定要扫描的包的类型安全替代方法。每个指定类的包都会被扫描。 * 考虑在每个包中创建一个特殊的无操作标记类或接口, * 除了被此属性引用之外,没有其他用途。 */ Class[] basePackageClasses() default {}; /** * 在Spring容器内为检测到的组件命名的 BeanNameGenerator 类。 * BeanNameGenerator 接口的默认值表明处理此 @ComponentScan 注解的扫描器 * 应使用它的继承的bean命名生成器,例如默认的 * AnnotationBeanNameGenerator 或在启动时提供给应用上下文的任何自定义实例。 */ Class nameGenerator() default BeanNameGenerator.class; /** * 用于解析检测到的组件范围的 ScopeMetadataResolver。 */ Class scopeResolver() default AnnotationScopeMetadataResolver.class; /** * 指示是否应为检测到的组件生成代理,这在以代理风格使用范围时可能是必要的。 * 默认值是延迟到执行实际扫描的组件扫描器的默认行为。 * 注意,设置此属性会覆盖为 #scopeResolver 设置的任何值。 */ ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT; /** * 控制适用于组件检测的类文件。 * 考虑使用 #includeFilters 和 #excludeFilters * 来采用更灵活的方法。 */ String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN; /** * 指示是否应启用使用 @Component @Repository, @Service, 或 @Controller 注解的类的自动检测。 */ boolean useDefaultFilters() default true; /** * 指定哪些类型有资格进行组件扫描。 * 进一步从 #basePackages 中的所有内容缩小到匹配给定过滤器或过滤器的基包中的所有内容。 * 注意,这些过滤器将附加到默认过滤器(如果指定)。即使它与默认过滤器不匹配(例如,没有使用 @Component 注解), * 任何匹配给定过滤器的基包下的类型都将被包括。 */ Filter[] includeFilters() default {}; /** * 指定哪些类型不适合进行组件扫描。 */ Filter[] excludeFilters() default {}; /** * 指定是否应注册扫描的beans以进行延迟初始化。 * 默认值是 false;如果需要,切换为 true。 */ boolean lazyInit() default false; /** * 声明用作 ComponentScan#includeFilters include filter 或 * ComponentScan#excludeFilters exclude filter 的类型过滤器。 */ @Retention(RetentionPolicy.RUNTIME) @Target({}) @interface Filter { /** * 要使用的过滤器类型。 * 默认为 FilterType#ANNOTATION。 * @see #classes * @see #pattern */ FilterType type() default FilterType.ANNOTATION; /** * #classes 的别名。 */ @AliasFor("classes") Class[] value() default {}; /** * 用作过滤器的类或类。 * 根据 #type 属性的配置值,以下表格解释了如何解释这些类 * ... * 这部分包含了一个表格和其它详细说明,由于格式限制,需要额外的处理来适应中文文档 * ... */ @AliasFor("value") Class[] classes() default {}; /** * 用作过滤器的模式(或模式),作为指定类 #value 的替代。 * 如果 #type 设置为 FilterType#ASPECTJ ASPECTJ,这是一个 AspectJ 类型模式表达式。 * 如果 #type 设置为 FilterType#REGEX REGEX,这是一个正则模式,用于匹配完全限定的类名。 */ String[] pattern() default {}; } } ``` `ScopedProxyMode` 是一个枚举,定义了不同的作用域代理选项,用于决定如何为特定的作用域 bean 创建代理。作用域代理是 Spring 中一个高级特性,允许在不同的上下文中共享 bean 实例,如请求或会话。此枚举的主要用途是为这些作用域 bean 提供不同的代理机制。 ```java /** * 枚举各种作用域代理选项。 * * 为了更完整地讨论什么是作用域代理,请查看 Spring 参考文档中标题为 '作为依赖的作用域 beans' 的部分。 * * @author Mark Fisher * @since 2.5 * @see ScopeMetadata */ public enum ScopedProxyMode { /** * 默认通常等于 #NO,除非在组件扫描指令级别配置了不同的默认值。 */ DEFAULT, /** * 不创建一个作用域代理。 *

当与非单例作用域实例一起使用时,这种代理模式通常不太有用,如果要作为依赖项使用, * 它应该优先使用 #INTERFACES 或 #TARGET_CLASS 代理模式。 */ NO, /** * 创建一个JDK动态代理,实现目标对象的类所暴露的所有接口。 */ INTERFACES, /** * 创建一个基于类的代理(使用CGLIB)。 */ TARGET_CLASS } ``` `FilterType` 是一个枚举,定义了与 `@ComponentScan` 注解结合使用时的不同类型过滤器选项。这些过滤器用于决定在组件扫描过程中哪些组件应被包括或排除。 ```java /** * 与 ComponentScan @ComponentScan 结合使用的类型过滤器的枚举。 * 该枚举定义了在组件扫描过程中可以用于过滤组件的不同类型。 * * @author Mark Fisher * @author Juergen Hoeller * @author Chris Beams * @since 2.5 * @see ComponentScan * @see ComponentScan#includeFilters() * @see ComponentScan#excludeFilters() * @see org.springframework.core.type.filter.TypeFilter */ public enum FilterType { /** * 过滤带有指定注解的候选项。 * @see org.springframework.core.type.filter.AnnotationTypeFilter */ ANNOTATION, /** * 过滤可以赋值给指定类型的候选项。 * @see org.springframework.core.type.filter.AssignableTypeFilter */ ASSIGNABLE_TYPE, /** * 过滤与指定的AspectJ类型模式表达式匹配的候选项。 * @see org.springframework.core.type.filter.AspectJTypeFilter */ ASPECTJ, /** * 过滤与指定的正则表达式模式匹配的候选项。 * @see org.springframework.core.type.filter.RegexPatternTypeFilter */ REGEX, /** * 使用给定的自定义 org.springframework.core.type.filter.TypeFilter 实现来过滤候选项。 */ CUSTOM } ``` ### 四、主要功能 1. **指定扫描的包** + 通过 `basePackages` 和 `basePackageClasses` 属性,用户可以明确告诉 Spring 在哪些包中查找带有 `@Component`、`@Service`、`@Repository` 和 `@Controller` 等注解的类。 2. **自动扫描** + 如果用户没有明确指定要扫描的包,则默认从声明 `@ComponentScan` 的类所在的包开始进行扫描。 3. **过滤扫描的组件** + 通过 `includeFilters` 和 `excludeFilters` 属性,用户可以更精细地控制哪些组件应被扫描或排除。 4. **其他配置** + 此注解还提供了其他属性,如 `nameGenerator`(为检测到的组件命名)、`scopeResolver`(解析组件的范围)、`scopedProxy`(是否为组件生成代理)等,以提供更高级的配置。 ### 五、最佳实践 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。在初始化上下文后,该程序会遍历并打印所有在 Spring 容器中定义的 beans 的名字。 ```java public class ComponentScanApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); for (String beanDefinitionName : context.getBeanDefinitionNames()) { System.out.println("beanName = " + beanDefinitionName); } } } ``` 在`MyConfiguration`类中,Spring 扫描 `com.xcs.spring` 包及其子包,包括所有 `SpecialComponent` 类型的组件,但排除所有 `AdminService` 类型的组件。 ```java @Configuration @ComponentScan( basePackages = "com.xcs.spring", includeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = SpecialComponent.class), excludeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = AdminService.class) ) public class MyConfiguration { } ``` `UserRepository` 的类,位于 `com.xcs.spring.repository` 包中,并用 `@Repository` 注解标记。 ```java package com.xcs.spring.repository; @Repository public class UserRepository { } ``` `AdminService` 和 `UserService`,它们都位于 `com.xcs.spring.service` 包中并分别用 `@Service` 注解标记。 ```java package com.xcs.spring.service; @Service public class AdminService { } @Service public class UserService { } ``` `SpecialComponent` 的类,它位于 `com.xcs.spring.special` 包中,没有使用spring中的任何注解标记。 ```java package com.xcs.spring.special; public class SpecialComponent { } ``` 运行结果发现,`UserRepository` 将被自动检测并注册为一个 Spring bean,因为它位于我们指定的 `com.xcs.spring` 包路径下。`UserService` 将被自动检测并注册为一个 Spring bean,因为它位于我们指定的 `com.xcs.spring` 包路径下。但是,由于 `@ComponentScan` 配置中使用了 `excludeFilters` 明确排除了 `AdminService`,所以即使 `AdminService` 位于 `com.xcs.spring` 包路径下,它也不会被注册为一个 Spring bean。虽然`SpecialComponent` 类是一个没有任何 Spring 注解的普通 Java 类。但通过使用 `@ComponentScan` 的 `includeFilters` 和 `FilterType.ASSIGNABLE_TYPE`,我们可以强制 Spring 上下文扫描并注册它为一个 bean,即使它没有标记为 `@Component` 或其他 Spring 注解。 ```java beanName = org.springframework.context.annotation.internalConfigurationAnnotationProcessor beanName = org.springframework.context.annotation.internalAutowiredAnnotationProcessor beanName = org.springframework.context.event.internalEventListenerProcessor beanName = org.springframework.context.event.internalEventListenerFactory beanName = myConfiguration beanName = userRepository beanName = userService beanName = specialComponent ``` ### 六、时序图 ~~~mermaid sequenceDiagram Title: @ComponentScan注解时序图 ComponentScanApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses) AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh() AbstractApplicationContext->>AbstractApplicationContext:invokeBeanFactoryPostProcessors(beanFactory) AbstractApplicationContext->>PostProcessorRegistrationDelegate:invokeBeanFactoryPostProcessors(beanFactory,beanFactoryPostProcessors) PostProcessorRegistrationDelegate->>PostProcessorRegistrationDelegate:invokeBeanDefinitionRegistryPostProcessors(postProcessors,registry,applicationStartup) PostProcessorRegistrationDelegate->>ConfigurationClassPostProcessor:postProcessBeanDefinitionRegistry(registry) ConfigurationClassPostProcessor->>ConfigurationClassPostProcessor:processConfigBeanDefinitions(registry) ConfigurationClassPostProcessor->>ConfigurationClassParser:ConfigurationClassParser(...) ConfigurationClassParser-->>ConfigurationClassPostProcessor:返回解析解析器 ConfigurationClassPostProcessor->>ConfigurationClassParser:parser.parse(candidates) ConfigurationClassParser->>ConfigurationClassParser:parse(metadata, String beanName) ConfigurationClassParser->>ConfigurationClassParser:processConfigurationClass(configClass,filter) ConfigurationClassParser->>ConfigurationClassParser:doProcessConfigurationClass(configClass,sourceClass,filter) ConfigurationClassParser->>ComponentScanAnnotationParser:parse(componentScan,declaringClass) ComponentScanAnnotationParser->>ClassPathBeanDefinitionScanner:ClassPathBeanDefinitionScanner(registry,useDefaultFilters,environment,resourceLoader) ClassPathBeanDefinitionScanner->>ClassPathBeanDefinitionScanner:registerDefaultFilters() ClassPathBeanDefinitionScanner-->>ComponentScanAnnotationParser:返回扫描器 ComponentScanAnnotationParser->>ClassPathBeanDefinitionScanner:doScan(basePackages) ClassPathBeanDefinitionScanner->>ClassPathScanningCandidateComponentProvider:findCandidateComponents(basePackage) ClassPathScanningCandidateComponentProvider->>ClassPathScanningCandidateComponentProvider:scanCandidateComponents(basePackage) ClassPathScanningCandidateComponentProvider-->>ClassPathBeanDefinitionScanner:返回BeanDefinition ClassPathBeanDefinitionScanner->>ClassPathBeanDefinitionScanner:registerBeanDefinition(definitionHolder,registry) ClassPathBeanDefinitionScanner->>BeanDefinitionReaderUtils:registerBeanDefinition(definitionHolder, registry) BeanDefinitionReaderUtils->>DefaultListableBeanFactory:registerBeanDefinition(beanName,beanDefinition) ClassPathBeanDefinitionScanner-->>ComponentScanAnnotationParser:返回BeanDefinition ~~~ ### 七、源码分析 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。在初始化上下文后,该程序会遍历并打印所有在 Spring 容器中定义的 beans 的名字。 ```java public class ComponentScanApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); for (String beanDefinitionName : context.getBeanDefinitionNames()) { System.out.println("beanName = " + beanDefinitionName); } } } ``` 在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法。 ```java public AnnotationConfigApplicationContext(Class... componentClasses) { this(); register(componentClasses); refresh(); } ``` 在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 ```java @Override public void refresh() throws BeansException, IllegalStateException { // ... [代码部分省略以简化] // 调用在上下文中注册为bean的工厂处理器 invokeBeanFactoryPostProcessors(beanFactory); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors`方法中,又委托了`PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()`进行调用。 ```java protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) { PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors()); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors`方法中,首先调用了 `BeanDefinitionRegistryPostProcessor`(这是 `BeanFactoryPostProcessor` 的子接口)。它专门用来在所有其他 bean 定义加载之前修改默认的 bean 定义。 ```java public static void invokeBeanFactoryPostProcessors( ConfigurableListableBeanFactory beanFactory, List beanFactoryPostProcessors) { // ... [代码部分省略以简化] invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup()); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanDefinitionRegistryPostProcessors`方法中,循环调用了实现`BeanDefinitionRegistryPostProcessor`接口中的`postProcessBeanDefinitionRegistry(registry)`方法 ```java private static void invokeBeanDefinitionRegistryPostProcessors( Collection postProcessors, BeanDefinitionRegistry registry, ApplicationStartup applicationStartup) { for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) { StartupStep postProcessBeanDefRegistry = applicationStartup.start("spring.context.beandef-registry.post-process") .tag("postProcessor", postProcessor::toString); postProcessor.postProcessBeanDefinitionRegistry(registry); postProcessBeanDefRegistry.end(); } } ``` 在`org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry`方法中,调用了`processConfigBeanDefinitions`方法,该方法的主要目的是处理和注册配置类中定义的beans。 ```java @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { // ... [代码部分省略以简化] processConfigBeanDefinitions(registry); } ``` 在`org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions`方法中,这个方法主要处理了配置类的解析和验证,并确保了所有在配置类中定义的beans都被正确地注册到Spring的bean定义注册表中。 ```java public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { // ... [代码部分省略以简化] // 步骤1:创建一个用于解析配置类的解析器 ConfigurationClassParser parser = new ConfigurationClassParser( this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry); // 步骤2:初始化候选配置类集合以及已解析配置类集合 Set candidates = new LinkedHashSet<>(configCandidates); Set alreadyParsed = new HashSet<>(configCandidates.size()); // 步骤3:循环处理所有候选配置类,直至没有候选类为止 do { // 步骤3.1 解析配置类 parser.parse(candidates); // 步骤3.2 验证配置类 parser.validate(); // 获取解析后的配置类,并从中移除已经处理过的 Set configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); configClasses.removeAll(alreadyParsed); // 步骤4:如果reader为空,则创建一个新的Bean定义读取器 if (this.reader == null) { this.reader = new ConfigurationClassBeanDefinitionReader( registry, this.sourceExtractor, this.resourceLoader, this.environment, this.importBeanNameGenerator, parser.getImportRegistry()); } // 步骤5:使用读取器为解析的配置类加载Bean定义 this.reader.loadBeanDefinitions(configClasses); // ... [代码部分省略以简化] } while (!candidates.isEmpty()); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.annotation.ConfigurationClassParser#parse`方法中,主要是遍历所有的配置类候选者,并对每一个带有注解的Bean定义进行解析。这通常涉及到查找该配置类中的@Bean方法、组件扫描指令等,并将这些信息注册到Spring容器中。 ```java public void parse(Set configCandidates) { // ... [代码部分省略以简化] parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName()); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.annotation.ConfigurationClassParser#parse(metadata, beanName)`方法中,将注解元数据和Bean名称转化为一个配置类,然后对其进行处理。处理配置类是Spring配置驱动的核心,它涉及到许多关键操作,如处理`@ComponentScan`注解等等。 ```java protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException { processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER); } ``` 在`org.springframework.context.annotation.ConfigurationClassParser#processConfigurationClass`方法中,处理一个给定的配置类。它首先递归地处理配置类及其父类,以确保所有相关的配置都被正确地读取并解析。在递归处理完所有相关配置后,它将配置类添加到已解析的配置类的映射中。 ```java protected void processConfigurationClass(ConfigurationClass configClass, Predicate filter) throws IOException { // ... [代码部分省略以简化] // 步骤1:递归地处理配置类及其超类层次结构 SourceClass sourceClass = asSourceClass(configClass, filter); do { sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter); } while (sourceClass != null); // 步骤2:将处理后的配置类放入映射中 this.configurationClasses.put(configClass, configClass); } ``` 在`org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass`方法中,这个方法的目标是处理和解析标有 `@Configuration` 的类,执行组件扫描,并确保所有相关的配置类都被递归地解析。 ```java @Nullable protected final SourceClass doProcessConfigurationClass( ConfigurationClass configClass, SourceClass sourceClass, Predicate filter) throws IOException { // ... [代码部分省略以简化] // 处理任何 @ComponentScan 注解 // 获取当前类(sourceClass)的所有 @ComponentScan 和 @ComponentScans 注解的属性 Set componentScans = AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); // 如果存在 @ComponentScan 或 @ComponentScans 注解,并且该类没有被条件评估排除 if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { // 遍历每一个 @ComponentScan 注解 for (AnnotationAttributes componentScan : componentScans) { // 对标有 @ComponentScan 的配置类进行立即扫描 Set scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); // 检查扫描到的定义中是否有任何进一步的配置类,如果需要,则递归解析 for (BeanDefinitionHolder holder : scannedBeanDefinitions) { BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition(); if (bdCand == null) { bdCand = holder.getBeanDefinition(); } // 检查 BeanDefinition 是否是一个配置类的候选者 if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) { // 如果是,递归解析它 parse(bdCand.getBeanClassName(), holder.getBeanName()); } } } } // ... [代码部分省略以简化] // 没有父类 -> 处理完成 return null; } ``` 在`org.springframework.context.annotation.ComponentScanAnnotationParser#parse`方法中,主要目的是为 `@ComponentScan` 配置的类提供了详细的处理,并指导了如何根据给定的属性配置和执行组件扫描。 ```java public Set parse(AnnotationAttributes componentScan, final String declaringClass) { // 步骤1. 创建一个新的扫描器 ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry, componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader); // 步骤2. 根据nameGenerator属性设置Bean名称生成器 Class generatorClass = componentScan.getClass("nameGenerator"); boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass); scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator : BeanUtils.instantiateClass(generatorClass)); // 步骤3. 设置作用域代理模式或者作用域元数据解析器 ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy"); if (scopedProxyMode != ScopedProxyMode.DEFAULT) { scanner.setScopedProxyMode(scopedProxyMode); } else { Class resolverClass = componentScan.getClass("scopeResolver"); scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass)); } // 步骤4. 设置资源模式 scanner.setResourcePattern(componentScan.getString("resourcePattern")); // 步骤5. 根据includeFilters和excludeFilters属性添加类型过滤器 for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) { for (TypeFilter typeFilter : typeFiltersFor(filter)) { scanner.addIncludeFilter(typeFilter); } } for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) { for (TypeFilter typeFilter : typeFiltersFor(filter)) { scanner.addExcludeFilter(typeFilter); } } // 步骤6. 设置bean是否为懒加载 boolean lazyInit = componentScan.getBoolean("lazyInit"); if (lazyInit) { scanner.getBeanDefinitionDefaults().setLazyInit(true); } // 步骤7. 确定扫描器的基础包 Set basePackages = new LinkedHashSet<>(); String[] basePackagesArray = componentScan.getStringArray("basePackages"); for (String pkg : basePackagesArray) { String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg), ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); Collections.addAll(basePackages, tokenized); } for (Class clazz : componentScan.getClassArray("basePackageClasses")) { basePackages.add(ClassUtils.getPackageName(clazz)); } if (basePackages.isEmpty()) { basePackages.add(ClassUtils.getPackageName(declaringClass)); } // 步骤8. 确保声明@ComponentScan的类本身不被注册为bean scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) { @Override protected boolean matchClassName(String className) { return declaringClass.equals(className); } }); // 步骤9. 使用配置好的扫描器执行实际的组件扫描 return scanner.doScan(StringUtils.toStringArray(basePackages)); } ``` 我们来到`org.springframework.context.annotation.ComponentScanAnnotationParser#parse`方法中的步骤1。在`org.springframework.context.annotation.ClassPathBeanDefinitionScanner#ClassPathBeanDefinitionScanner`方法中,首先在这个构造方法初始化了一个新的`ClassPathBeanDefinitionScanner`对象,根据传入的参数决定是否使用默认过滤器,并设置了其环境和资源加载器。 ```java public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, Environment environment, @Nullable ResourceLoader resourceLoader) { // 断言确保注册表不为空 Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); // 将传入的BeanDefinitionRegistry赋值给成员变量registry this.registry = registry; // 根据useDefaultFilters决定是否注册默认的过滤器 if (useDefaultFilters) { registerDefaultFilters(); } // 设置扫描器的环境 setEnvironment(environment); // 设置资源加载器 setResourceLoader(resourceLoader); } ``` 在`org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#registerDefaultFilters`方法中,此方法主要用于注册默认的类型过滤器。它首先注册了用于查找带有`@Component`注解的类的过滤器。然后,它尝试注册两个JSR标准的注解过滤器:JSR-250的`@ManagedBean`和JSR-330的`@Named`。如果相关的类不在类路径上,那么这两个过滤器将不会被注册。 ```java protected void registerDefaultFilters() { // 添加一个过滤器来包括带有@Component注解的类 this.includeFilters.add(new AnnotationTypeFilter(Component.class)); // 获取当前类的类加载器 ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader(); try { // 尝试添加一个过滤器来包括带有JSR-250 'javax.annotation.ManagedBean'注解的类 this.includeFilters.add(new AnnotationTypeFilter( ((Class) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false)); logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning"); } catch (ClassNotFoundException ex) { // 如果JSR-250 1.1 API(如Java EE 6中包含的)不可用,仅仅跳过 } try { // 尝试添加一个过滤器来包括带有JSR-330 'javax.inject.Named'注解的类 this.includeFilters.add(new AnnotationTypeFilter( ((Class) ClassUtils.forName("javax.inject.Named", cl)), false)); logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning"); } catch (ClassNotFoundException ex) { // 如果JSR-330 API不可用,仅仅跳过 } } ``` 我们来到`org.springframework.context.annotation.ComponentScanAnnotationParser#parse`方法中的步骤9。在`org.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScan`方法中,主要目标是找到指定`basePackages`中所有的组件,并为它们创建 `BeanDefinition`。这些 `BeanDefinition` 之后会被 Spring 容器用来创建实际的 bean 实例。 ```java protected Set doScan(String... basePackages) { // 断言确保至少有一个基础包被指定 Assert.notEmpty(basePackages, "At least one base package must be specified"); // 用于保存找到的bean定义的集合 Set beanDefinitions = new LinkedHashSet<>(); // 遍历每个基础包 for (String basePackage : basePackages) { // 步骤1. 在给定的基础包中找到所有候选的bean定义 Set candidates = findCandidateComponents(basePackage); // 遍历找到的bean定义 for (BeanDefinition candidate : candidates) { // 步骤2. 解析bean的作用域元数据 ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); // 设置bean的作用域 candidate.setScope(scopeMetadata.getScopeName()); // 步骤3. 生成bean的名字 String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); // 步骤4. 如果是AbstractBeanDefinition,进行后处理 if (candidate instanceof AbstractBeanDefinition) { postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); } // 步骤5. 如果是AnnotatedBeanDefinition,处理常见的注解定义 if (candidate instanceof AnnotatedBeanDefinition) { AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); } // 步骤6. 检查给定的bean名字是否已经存在,如果不存在,进行注册 if (checkCandidate(beanName, candidate)) { BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); // 步骤7. 应用作用域代理模式,如有必要为bean创建代理 definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); // 将bean定义加入集合中 beanDefinitions.add(definitionHolder); // 步骤8. 在bean注册表中注册bean定义 registerBeanDefinition(definitionHolder, this.registry); } } } // 返回所有注册的bean定义 return beanDefinitions; } ``` 我们来到`org.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScan`方法中的步骤1。在`org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#findCandidateComponents`方法中,主要提供了两种方式查找组件:通过预先生成的索引(如果可用且支持)或通过传统的扫描方式(我们重点关注传统的扫描方式)。 ```java public Set findCandidateComponents(String basePackage) { // 如果存在组件索引并且支持include过滤器 if (this.componentsIndex != null && indexSupportsIncludeFilters()) { // 从索引中添加候选组件 return addCandidateComponentsFromIndex(this.componentsIndex, basePackage); } else { // 扫描给定基础包中的候选组件 return scanCandidateComponents(basePackage); } } ``` 在`org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#scanCandidateComponents`方法中,首先是构建搜索路径,用于在类路径中搜索指定包,然后是扫描类路径,获取匹配的资源(通常是 `.class` 文件),再然后是对于每个资源,检查是否是候选组件,例如是否有 `@Component` 注解,最后对于是候选组件的类,创建一个 `BeanDefinition` 对象并添加到结果集中。 ```java private Set scanCandidateComponents(String basePackage) { // 用于保存候选的Bean定义 Set candidates = new LinkedHashSet<>(); try { // 构建包搜索路径,例如:"classpath*:com/example/*" String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + '/' + this.resourcePattern; // 使用模式解析器获取所有匹配的资源(即.class文件) Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath); // ... [代码部分省略以简化] for (Resource resource : resources) { // ... [代码部分省略以简化] // 检查资源是否可读 if (resource.isReadable()) { try { // 使用元数据读取器获取类的元数据 MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource); // 检查类是否是候选组件(例如,是否带有@Component注释) if (isCandidateComponent(metadataReader)) { ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); sbd.setSource(resource); // 再次检查Bean定义是否是候选组件 if (isCandidateComponent(sbd)) { // ... [代码部分省略以简化] candidates.add(sbd); } else { // ... [代码部分省略以简化] } } else { // ... [代码部分省略以简化] } } catch (Throwable ex) { // ... [代码部分省略以简化] } } else { // ... [代码部分省略以简化] } } } catch (IOException ex) { // ... [代码部分省略以简化] } return candidates; } ``` 在`org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#isCandidateComponent`方法中,首先确保类不在排除列表中,然后检查它是否在包含列表中,并确保它满足任何其他指定条件。 ```java protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException { // 遍历所有的排除过滤器 for (TypeFilter tf : this.excludeFilters) { // 如果当前类与任一排除过滤器匹配,则直接返回false,说明不是候选组件 if (tf.match(metadataReader, getMetadataReaderFactory())) { return false; } } // 遍历所有的包含过滤器 for (TypeFilter tf : this.includeFilters) { // 如果当前类与任一包含过滤器匹配 if (tf.match(metadataReader, getMetadataReaderFactory())) { // 判断该组件是否满足特定的条件 return isConditionMatch(metadataReader); } } // 默认返回false,说明不是候选组件 return false; } ``` 我们来到`org.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScan`方法中的步骤6。在org.springframework.context.annotation.ClassPathBeanDefinitionScanner#checkCandidate方法中,确保Spring容器中没有重名的、不兼容的bean定义。 ```java protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException { // 检查bean定义注册表中是否已包含给定名称的bean定义 if (!this.registry.containsBeanDefinition(beanName)) { return true; // 如果不存在相同名称的bean定义,则返回true } // 获取已存在的bean定义 BeanDefinition existingDef = this.registry.getBeanDefinition(beanName); // 获取原始的bean定义(如果有的话) BeanDefinition originatingDef = existingDef.getOriginatingBeanDefinition(); if (originatingDef != null) { existingDef = originatingDef; } // 检查给定的bean定义与已存在的bean定义是否兼容 if (isCompatible(beanDefinition, existingDef)) { return false; // 如果它们是兼容的,则返回false } // 如果给定的bean定义与已存在的bean定义不兼容,则抛出异常 throw new ConflictingBeanDefinitionException("Annotation-specified bean name '" + beanName + "' for bean class [" + beanDefinition.getBeanClassName() + "] conflicts with existing, " + "non-compatible bean definition of same name and class [" + existingDef.getBeanClassName() + "]"); } ``` 我们来到`org.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScan`方法中的步骤8。在`org.springframework.context.annotation.ClassPathBeanDefinitionScanner#registerBeanDefinition`方法中,主要调用 `BeanDefinitionReaderUtils` 类的 `registerBeanDefinition` 方法,用于实际的 `BeanDefinition` 注册过程。 ```java protected void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) { BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry); } ``` 在`org.springframework.beans.factory.support.BeanDefinitionReaderUtils#registerBeanDefinition`方法中,主要用于将提供的 `BeanDefinitionHolder` 中的 `BeanDefinition` 及其所有别名注册到 `BeanDefinitionRegistry` 中。对于`@ComponentScan`的扫描和注册阶段而言,当`registerBeanDefinition`方法被调用时,已经完成了。但对于整个Spring容器的生命周期来说,还有其他重要的步骤将在后续发生,如bean的生命周期回调、bean的实例化、bean的初始化等。 ```java public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // 获取 bean 的主名称,并在 registry 中注册它 String beanName = definitionHolder.getBeanName(); registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // 如果提供了 bean 的别名,则注册这些别名 String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String alias : aliases) { registry.registerAlias(beanName, alias); } } } ``` ### 八、注意事项 **默认扫描** + 如果未指定具体的包,`@ComponentScan` 默认会扫描声明此注解的类所在的包及其子包。 **性能考虑** + 避免扫描不必要的包,因为这可能导致性能问题。尤其是在大型项目中,指定扫描的精确路径可以加速启动时间。 **默认过滤器** + 默认情况下,`@ComponentScan` 使用的过滤器会搜索带有 `@Component`, `@Service`, `@Repository`, 和 `@Controller` 的类。可以通过 `includeFilters` 和 `excludeFilters` 属性进行定制。 **冲突的 Bean 名称** + 确保没有重复的 Bean 名称,否则可能会导致 `BeanDefinitionStoreException`。 **使用 `basePackages` 和 `basePackageClasses`** + `basePackages` 允许我们指定要扫描的包的名称,而 `basePackageClasses` 允许我们指定一个或多个类,Spring 将扫描这些类所在的包。 **避免使用多个配置** + 不建议在同一个配置类中使用多个 `@ComponentScan`。如果确实需要,考虑使用 `@ComponentScans`。 **代理模式** + 考虑如何使用 `scopedProxy` 属性,特别是当我们使用非单例作用域的 beans 时。 **注解属性的覆盖** + 当多个 `@ComponentScan` 在多个配置类中定义时,后面的定义将覆盖前面的定义。这里需要我们自己去确认。 **对于大型项目,考虑使用模块化** + 在大型项目中,为了更好的管理和维护,可以考虑将应用分成多个模块,每个模块有其自己的配置类和 `@ComponentScan`。 ### 九、总结 #### 最佳实践总结 1. **应用启动** + 在 `ComponentScanApplication` 的主方法中,使用 `AnnotationConfigApplicationContext` 初始化了 Spring 上下文,并将配置类 `MyConfiguration` 传递给它。这告诉 Spring 在 `MyConfiguration` 类中查找配置信息。 2. **配置类** + `MyConfiguration` 类被标记为 `@Configuration`,表明它是一个配置类。这个类进一步使用 `@ComponentScan` 注解指定了 Spring 应该在哪里寻找组件。具体来说,Spring 将扫描 `com.xcs.spring` 包及其所有子包。 3. **扫描规则** + 在 `@ComponentScan` 中,我们使用 `includeFilters` 明确指定 `SpecialComponent` 类被包含在 Spring 容器中,即使它没有使用任何 Spring 注解。同时,使用 `excludeFilters` 指定 `AdminService` 类不应该被 Spring 容器管理,即使它被标记为一个 `@Service`。 4. **组件类**: - `UserRepository` 类在 `com.xcs.spring.repository` 包中,并被标记为 `@Repository`,因此它自动被 Spring 容器管理。 - `UserService` 类在 `com.xcs.spring.service` 包中,并被标记为 `@Service`,因此它也自动被 Spring 容器管理。 - `AdminService` 虽然也被标记为 `@Service`,但由于 `@ComponentScan` 的 `excludeFilters` 配置,它没有被 Spring 容器管理。 - `SpecialComponent` 类没有使用任何 Spring 注解,但由于 `@ComponentScan` 的 `includeFilters` 配置,它被 Spring 容器管理。 5. **运行结果** + 当应用启动时,所有被 Spring 容器管理的 beans 的名字都被打印出来,这包括了 `UserRepository`, `UserService`, 和 `SpecialComponent`。不包括 `AdminService`,因为它被排除了。 #### 源码分析总结 1. **应用启动** + 通过 `AnnotationConfigApplicationContext` 的构造方法,传入配置类 `MyConfiguration`,来启动Spring应用。 2. **刷新上下文** + 在构造方法内部,调用了 `refresh()` 方法开始执行容器的刷新操作。 3. **执行BeanFactory的后处理器** + `invokeBeanFactoryPostProcessors(beanFactory)` 方法被调用,它主要执行 `BeanDefinitionRegistryPostProcessor` 和 `BeanFactoryPostProcessor`。其中, `BeanDefinitionRegistryPostProcessor` 是在所有其他bean定义加载之前,用来修改默认的bean定义。 4. **处理配置类** + `ConfigurationClassPostProcessor` 是一个核心的后处理器,它会解析配置类(如带有 `@Configuration` 的类),找到 `@ComponentScan` 注解并解析它的属性,然后进行组件扫描。 5. **执行组件扫描** + 通过 `ComponentScanAnnotationParser` 类进行详细的扫描操作。它创建一个 `ClassPathBeanDefinitionScanner` 对象,设置其属性(如是否使用默认过滤器、资源加载器、作用域解析器、资源模式、包含和排除的过滤器等),然后扫描指定的基础包。 6. **扫描候选组件** + 对于每个基础包,它会查找所有的组件,并为这些组件创建 `BeanDefinition` 对象。 7. **注册Bean定义** + 找到的组件都会被注册到Spring容器中。这是通过调用 `registerBeanDefinition` 方法来完成的。如果在容器中已存在同名的bean定义,会进行冲突检查。 8. **完成组件扫描** + 当所有的基础包都被扫描完成,`@ComponentScan` 的操作就执行结束了。 ================================================ FILE: spring-annotation/spring-annotation-componentScan/pom.xml ================================================ spring-annotation com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-annotation-componentScan ================================================ FILE: spring-annotation/spring-annotation-componentScan/src/main/java/com/xcs/spring/ComponentScanApplication.java ================================================ package com.xcs.spring; import com.xcs.spring.config.MyConfiguration; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年08月07日 16时21分 **/ public class ComponentScanApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); for (String beanDefinitionName : context.getBeanDefinitionNames()) { System.out.println("beanName = " + beanDefinitionName); } } } ================================================ FILE: spring-annotation/spring-annotation-componentScan/src/main/java/com/xcs/spring/config/MyConfiguration.java ================================================ package com.xcs.spring.config; import com.xcs.spring.service.AdminService; import com.xcs.spring.special.SpecialComponent; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; /** * @author xcs * @date 2023年08月07日 16时25分 **/ @Configuration @ComponentScan( basePackages = "com.xcs.spring", includeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = SpecialComponent.class), excludeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = AdminService.class) ) public class MyConfiguration { } ================================================ FILE: spring-annotation/spring-annotation-componentScan/src/main/java/com/xcs/spring/repository/UserRepository.java ================================================ package com.xcs.spring.repository; import org.springframework.stereotype.Repository; /** * @author xcs * @date 2023年10月07日 11时51分 **/ @Repository public class UserRepository { } ================================================ FILE: spring-annotation/spring-annotation-componentScan/src/main/java/com/xcs/spring/service/AdminService.java ================================================ package com.xcs.spring.service; import org.springframework.stereotype.Service; /** * @author xcs * @date 2023年10月07日 11时51分 **/ @Service public class AdminService { } ================================================ FILE: spring-annotation/spring-annotation-componentScan/src/main/java/com/xcs/spring/service/UserService.java ================================================ package com.xcs.spring.service; import org.springframework.stereotype.Service; /** * @author xcs * @date 2023年10月07日 11时50分 **/ @Service public class UserService { } ================================================ FILE: spring-annotation/spring-annotation-componentScan/src/main/java/com/xcs/spring/special/SpecialComponent.java ================================================ package com.xcs.spring.special; /** * @author xcs * @date 2023年10月07日 11时52分 **/ public class SpecialComponent { } ================================================ FILE: spring-annotation/spring-annotation-conditional/README.md ================================================ ## @Conditional - [@Conditional](#conditional) - [一、基本信息](#一基本信息) - [二、注解描述](#二注解描述) - [三、注解源码](#三注解源码) - [四、主要功能](#四主要功能) - [五、最佳实践](#五最佳实践) - [在@Bean上使用](#在bean上使用) - [在@Configuration上使用](#在configuration上使用) - [自定义组合注解](#自定义组合注解) - [六、时序图](#六时序图) - [七、源码分析](#七源码分析) - [八、注意事项](#八注意事项) - [九、总结](#九总结) - [最佳实践总结](#最佳实践总结) - [源码分析总结](#源码分析总结) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [我的CSDN](https://blog.csdn.net/duzhuang2399/article/details/133800722) 📚 **文章目录** - [所有文章](https://github.com/xuchengsheng/spring-reading) 🔗 **源码地址** - [@Conditional源码](https://github.com/xuchengsheng/spring-reading/tree/master/spring-annotation/spring-annotation-conditional) ### 二、注解描述 `@Conditional`注解,是用来基于满足某些特定条件来决定一个Bean是否应该被注册到Spring容器的。这提供了一种灵活的方式来根据环境、配置或其他因素来决定是否激活或者创建某个Bean。 ### 三、注解源码 `@Conditional`注解是 Spring 框架自 3.0 版本开始引入的一个核心注解,用于指示一个组件只在所有指定条件匹配时才能被注册。 ```java /** * 表明只有当所有指定的条件都满足时,组件才有资格被注册。 * * 条件是可以在bean定义被注册前以编程方式确定的任何状态(参考 Condition 获取详情)。 * * @Conditional 注解可以以下列方式使用: * * 作为直接或间接使用 @Component 注解的任何类的类型级别注解,包括 Configuration @Configuration 类 * 作为元注解,用于组合自定义的范型注解 * 作为任何 Bean @Bean 方法的方法级别注解 * * * 如果一个 @Configuration 类标记为 @Conditional,与该类关联的所有 @Bean 方法、Import @Import 注解, * 和 ComponentScan @ComponentScan 注解都将受到这些条件的限制。 * * 注意:不支持 @Conditional 注解的继承;任何从超类或从被覆盖的方法继承的条件都不会被考虑。 * 为了强制这些语义,@Conditional 本身未声明为 java.lang.annotation.Inherited @Inherited; * 此外,任何用 @Conditional 作为元注解的自定义组成注解也不应声明为 @Inherited。 * * @author Phillip Webb * @author Sam Brannen * @since 4.0 * @see Condition */ @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Conditional { /** * 所有必须满足的 {@link Condition} 类,以便组件可以被注册。 */ Class[] value(); } ``` ### 四、主要功能 1. **条件化 Bean 注册** + 可以根据特定的条件来决定是否创建并注册一个 Bean。这允许我们根据环境、配置或其他因素动态地选择哪些 Bean 需要被实例化。 2. **条件化配置类** + 不仅可以对单个 Bean 使用,还可以对整个配置类使用。如果配置类上的条件不满足,那么该配置类中定义的所有 Beans 都不会被注册。 3. **灵活性** + 与一个实现了 `Condition` 接口的类一起使用。这个接口允许我们定义自己的条件逻辑,使得其可以非常灵活地根据各种场景来决定是否注册 Bean。 4. **与其他注解组合** + 除了与 `@Bean` 和 `@Configuration` 注解一起使用外,`@Conditional` 还可以作为元注解,用于创建自定义的组合注解,这些组合注解内部使用 `@Conditional` 来应用条件逻辑。 5. **对整个配置类的影响** + 当 `@Conditional` 用于配置类时,不仅仅是该类,还有与该类关联的所有 `@Bean` 方法、`@Import` 注解和 `@ComponentScan` 注解都将受到条件的影响。 6. **不支持继承** + `@Conditional` 注解本身不是继承的,因此,从父类或接口继承的条件不会被子类考虑。 ### 五、最佳实践 #### 在@Bean上使用 首先来看看启动类入口,首先设置一个系统属性`enable.bean`为`true`,然后上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类,最后打印了Spring上下文中所有的bean定义名称。 ```java public class ConditionBeanApplication { public static void main(String[] args) { System.setProperty("enable.bean","true"); AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyBeanConfiguration.class); for (String beanDefinitionName : context.getBeanDefinitionNames()) { System.out.println("beanDefinitionName = " + beanDefinitionName); } } } ``` `MyBeanConfiguration`,其中定义了两个bean:`user1`和`user2`。`user1` bean的创建是基于条件的,具体取决于`BeanPropertyCondition`条件的结果。而`user2` bean则无条件创建。 ```java @Configuration public class MyBeanConfiguration { @Bean @Conditional(BeanPropertyCondition.class) public User1 user1() { return new User1(); } @Bean public User2 user2() { return new User2(); } } ``` `BeanPropertyCondition`。这个实现会根据`enable.bean`属性的值决定是否满足条件。具体来说:如果环境属性`enable.bean`的值是`true`,则`user1` bean会被创建并添加到Spring容器。如果`enable.bean`不是`true`(或者没有设置这个属性),`user1` bean不会被创建。 ```java public class BeanPropertyCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { return "true".equals(context.getEnvironment().getProperty("enable.bean")); } } ``` 定义两个简单的Java类:`User1`和`User2`。 ```java public class User1 { } public class User2 { } ``` 当`enable.bean`为`true`运行结果发现,根据`enable.bean`属性的值来注册`user1` bean,而`user2` bean则不受此属性的影响。 ```java beanDefinitionName = myBeanConfiguration beanDefinitionName = user1 beanDefinitionName = user2 ``` 当`enable.bean`为`false`运行结果发现,`enable.bean`值为`false`,所以条件不满足。因此`user1`bean不会被注册,`user2` bean不受任何条件的影响。 ```java beanDefinitionName = myBeanConfiguration beanDefinitionName = user2 ``` #### 在@Configuration上使用 首先来看看启动类入口,首先设置一个系统属性`enable.config`为`true`,然后上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类,最后打印了Spring上下文中所有的bean定义名称。 ```java public class ConditionConfigurationApplication { public static void main(String[] args) { System.setProperty("enable.config","true"); AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfigConfiguration.class); for (String beanDefinitionName : context.getBeanDefinitionNames()) { System.out.println("beanDefinitionName = " + beanDefinitionName); } } } ``` `MyConfigConfiguration`,其中定义了两个bean:`user3`和`user4`。当`ConfigPropertyCondition`条件不满足时`MyConfigConfiguration`配置类不被激活,该配置类中定义的`user3`和`user4`不会被注册。 ```java @Configuration @Conditional(ConfigPropertyCondition.class) public class MyConfigConfiguration { @Bean public User3 user3() { return new User3(); } @Bean public User4 user4() { return new User4(); } } ``` `ConfigPropertyCondition`。这个实现会根据`enable.config`属性的值决定是否满足条件。具体来说:当`enable.config`设置为`true`,`ConfigPropertyCondition`满足,`MyConfigConfiguration`配置类被激活,`user3`和`user4` beans都将被注册到Spring上下文。当`enable.config`设置为`false`或未设置,`ConfigPropertyCondition`不满足,`MyConfigConfiguration`配置类不被激活,`user3`和`user4` beans都不会被注册。 ```java public class ConfigPropertyCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { return "true".equals(System.getProperty("enable.config")); } } ``` 定义两个简单的Java类:`User3`和`User4`。 ```java public class User3 { } public class User4 { } ``` 当`enable.config`为`true`运行结果发现,`MyConfigConfiguration`中的`user3`和`user4`都被注册了。 ```java beanDefinitionName = myConfigConfiguration beanDefinitionName = user3 beanDefinitionName = user4 ``` 当`enable.config`为`false`运行结果发现,我们不会看到任何与 `MyConfigConfiguration` (包括它自己)相关的 beans 被注册了。 ```java 无任何bean ``` #### 自定义组合注解 首先来看看启动类入口,首先设置一个系统属性`enable.custom`为`true`,然后上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类,最后打印了Spring上下文中所有的bean定义名称。 ```java public class ConditionCustomApplication { public static void main(String[] args) { System.setProperty("enable.custom","true"); AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyCustomConfiguration.class); for (String beanDefinitionName : context.getBeanDefinitionNames()) { System.out.println("beanDefinitionName = " + beanDefinitionName); } } } ``` `MyCustomConfiguration`,其中定义了两个bean:`user5`和`user6`。如果`@ConditionalOnCustomActive`的条件满足,`MyCustomConfiguration`配置类将被激活,在此配置类中定义`user5`和`user6`将被注册到Spring容器中。如果`@ConditionalOnCustomActive`的条件不满足,`MyCustomConfiguration`配置类将不被激活。`user5`和`user6` beans都不会被注册。 ```java @Configuration @ConditionalOnCustomActive public class MyCustomConfiguration { @Bean public User5 user5() { return new User5(); } @Bean public User6 user6() { return new User6(); } } ``` `@ConditionalOnCustomActive`定义了一个组合注解,并通过`@Conditional`元注解将其关联到`CustomActiveCondition`。 ```java @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Conditional(CustomActiveCondition.class) public @interface ConditionalOnCustomActive { } ``` `CustomActiveCondition`。这个实现会根据`enable.custom`属性的值决定是否满足条件。具体来说:当`enable.custom`设置为`true`,`CustomActiveCondition`满足条件,因为`matches`方法会返回`true`,`MyCustomConfiguration`配置类由于带有`@ConditionalOnCustomActive`注解(该注解内部引用了`CustomActiveCondition`)将被激活,在该配置类中定义的`user5`和`user6`将被注册到Spring容器中。当`enable.custom`设置为`false`或未设置`CustomActiveCondition`不满足条件,因为`matches`方法会返回`false`,由于`MyCustomConfiguration`带有`@ConditionalOnCustomActive`注解,该配置类不被激活。`user5`和`user6` beans都不会被注册。 ```java public class CustomActiveCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { return "true".equals(System.getProperty("enable.custom")); } } ``` 定义两个简单的Java类:`User5`和`User6`。 ```java public class User5 { } public class User6 { } ``` 当`enable.custom`为`true`运行结果发现,我们的组合注解 `@ConditionalOnCustomActive` 和相应的条件 `CustomActiveCondition` 正常工作,正确地根据 `enable.custom` 系统属性的值来激活 `MyCustomConfiguration` 配置类。 ```java beanDefinitionName = myCustomConfiguration beanDefinitionName = user5 beanDefinitionName = user6 ``` 当`enable.custom`为`false`运行结果发现,与 `MyCustomConfiguration` 相关的任何 beans(包括它自己)都不会被注册到 Spring 容器中。 ```java 无任何bean ``` ### 六、时序图 ~~~mermaid sequenceDiagram ConditionCustomApplication->>AnnotationConfigApplicationContext: AnnotationConfigApplicationContext(componentClasses)
启动上下文 AnnotationConfigApplicationContext-->>ConfigurationApplication: 返回context
返回上下文实例 AnnotationConfigApplicationContext->>AnnotationConfigApplicationContext: register(componentClasses)
注册组件类 AnnotationConfigApplicationContext->>AnnotatedBeanDefinitionReader: register(componentClasses)
读取器注册类 AnnotatedBeanDefinitionReader-->>AnnotatedBeanDefinitionReader: registerBean(beanClass)
注册Bean类 AnnotatedBeanDefinitionReader-->>AnnotatedBeanDefinitionReader: doRegisterBean(beanClass,name,qualifiers, supplier,customizers)
执行Bean注册 AnnotatedBeanDefinitionReader->>ConditionEvaluator:shouldSkip(metadata) ConditionEvaluator->>ConditionEvaluator:shouldSkip(metadata,phase) ConditionEvaluator->>ConditionEvaluator:getConditionClasses(metadata) Note right of ConditionEvaluator: 返回 List ConditionEvaluator->>ConditionEvaluator:getCondition(conditionClassName,classloader) ConditionEvaluator->>AnnotationAwareOrderComparator:sort(conditions) ConditionEvaluator->>CustomActiveCondition:matches(context,metadata) CustomActiveCondition->>ConditionEvaluator:返回true or false ConditionEvaluator->>AnnotatedBeanDefinitionReader:返回true or false AnnotatedBeanDefinitionReader->>AnnotatedBeanDefinitionReader:如果shouldSkip返回是true,跳过Bean的注册 ~~~ ### 七、源码分析 首先来看看启动类入口,首先设置一个系统属性`enable.custom`为`true`,然后上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类,最后打印了Spring上下文中所有的bean定义名称。 ```java public class ConditionCustomApplication { public static void main(String[] args) { System.setProperty("enable.custom","true"); AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyCustomConfiguration.class); for (String beanDefinitionName : context.getBeanDefinitionNames()) { System.out.println("beanDefinitionName = " + beanDefinitionName); } } } ``` 在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法。 ```java public AnnotationConfigApplicationContext(Class... componentClasses) { this(); register(componentClasses); refresh(); } ``` 在`org.springframework.context.annotation.AnnotationConfigApplicationContext#register`方法中,主要是允许我们注册一个或多个组件类(例如,那些使用 `@Component`, `@Service`, `@Repository`, `@Controller`, `@Configuration` 等注解的类)到Spring容器。 ```java @Override public void register(Class... componentClasses) { Assert.notEmpty(componentClasses, "At least one component class must be specified"); StartupStep registerComponentClass = this.getApplicationStartup().start("spring.context.component-classes.register") .tag("classes", () -> Arrays.toString(componentClasses)); this.reader.register(componentClasses); registerComponentClass.end(); } ``` 在`org.springframework.context.annotation.AnnotatedBeanDefinitionReader#register`方法中,遍历每一个传入的组件类,并逐一调用另一个方法来完成实际的注册工作。 ```java public void register(Class... componentClasses) { for (Class componentClass : componentClasses) { registerBean(componentClass); } } ``` 在`org.springframework.context.annotation.AnnotatedBeanDefinitionReader#registerBean(beanClass)`方法中,主要目的是快速注册一个 bean 类型,而不需要指定其他详细的配置或参数。 ```java public void registerBean(Class beanClass) { doRegisterBean(beanClass, null, null, null, null); } ``` 在`org.springframework.context.annotation.AnnotatedBeanDefinitionReader#doRegisterBean`方法中,主要用于条件性注册 bean 的逻辑,只有当特定的条件满足时,bean 才会被注册。 ```java private void doRegisterBean(Class beanClass, @Nullable String name, @Nullable Class[] qualifiers, @Nullable Supplier supplier, @Nullable BeanDefinitionCustomizer[] customizers) { // 基于给定的 bean 类创建一个带注解的 bean 定义。 AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass); // 利用条件评估器检查是否应该跳过当前 bean 的注册。 // 如果 bean 不满足指定的条件,那么将直接返回,不继续执行后续的注册逻辑。 if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) { return; } // ... [代码部分省略以简化] } ``` 在`org.springframework.context.annotation.ConditionEvaluator#shouldSkip(metadata)`方法中,又委托给另一个版本的 `shouldSkip` 方法,并为第二个参数传入 `null`。 ```java public boolean shouldSkip(AnnotatedTypeMetadata metadata) { return shouldSkip(metadata, null); } ``` 在`org.springframework.context.annotation.ConditionEvaluator#shouldSkip(metadata,phase)`方法中,主要目的是决定是否应根据给定的条件(通常由 `@Conditional` 注解定义)跳过某个配置类或 bean 的注册。 ```java /** * 根据提供的元数据和配置阶段判断是否应跳过某个操作或逻辑。 * * @param metadata 元数据,与注解相关。 * @param phase 当前的配置阶段,可能为 null。 * @return 如果应跳过,则返回 true;否则返回 false。 */ public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) { // 如果元数据为空或未标注 @Conditional 注解,则不跳过。 if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) { return false; } // 如果没有指定配置阶段,确定正确的配置阶段。 if (phase == null) { // 如果元数据是注解元数据,并且是配置候选项,则选择 PARSE_CONFIGURATION 阶段。 if (metadata instanceof AnnotationMetadata && ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) { return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION); } // 否则选择 REGISTER_BEAN 阶段。 return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN); } // 获取所有的条件,并从相关的条件类实例化它们。 List conditions = new ArrayList<>(); for (String[] conditionClasses : getConditionClasses(metadata)) { for (String conditionClass : conditionClasses) { Condition condition = getCondition(conditionClass, this.context.getClassLoader()); conditions.add(condition); } } // 对条件进行排序。 AnnotationAwareOrderComparator.sort(conditions); // 遍历所有条件,检查它们是否与当前配置阶段匹配。 for (Condition condition : conditions) { ConfigurationPhase requiredPhase = null; if (condition instanceof ConfigurationCondition) { requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase(); } // 如果条件不匹配上下文和元数据,则跳过。 if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) { return true; } } return false; } ``` 在`org.springframework.context.annotation.ConditionEvaluator#getConditionClasses`方法中,从提供的注解元数据中获取与 `@Conditional` 注解关联的条件类的名称。 ```java /** * 从提供的注解元数据中获取与 @Conditional 注解关联的条件类的名称。 * * @param metadata 元数据,通常与某个 bean 或配置类的注解相关。 * @return 一个列表,其中包含与 @Conditional 注解关联的条件类的名称。如果没有相关的条件类,则返回一个空列表。 */ private List getConditionClasses(AnnotatedTypeMetadata metadata) { // 获取 @Conditional 注解的所有属性值。 MultiValueMap attributes = metadata.getAllAnnotationAttributes(Conditional.class.getName(), true); // 试图从属性值中获取 "value",它应该是一个指向条件类名称的引用。 Object values = (attributes != null ? attributes.get("value") : null); // 返回条件类名称的列表,或者如果没有条件类,则返回一个空列表。 return (List) (values != null ? values : Collections.emptyList()); } ``` 在`org.springframework.context.annotation.ConditionEvaluator#getCondition`方法中,根据给定的条件类名称和类加载器实例化一个 `Condition` 对象。 ```java /** * 根据提供的条件类名称和类加载器实例化一个 Condition 对象。 * * @param conditionClassName 条件类的完全限定名。 * @param classloader 用于加载条件类的类加载器,可以为 null。 * @return 实例化的 Condition 对象。 */ private Condition getCondition(String conditionClassName, @Nullable ClassLoader classloader) { // 使用类加载器解析并加载指定的条件类。 Class conditionClass = ClassUtils.resolveClassName(conditionClassName, classloader); // 实例化解析的条件类并返回。 return (Condition) BeanUtils.instantiateClass(conditionClass); } ``` ### 八、注意事项 1. **实现 `Condition` 接口** + 为了使用 `@Conditional`, 我们需要实现 `Condition` 接口。该接口只有一个方法,`matches(ConditionContext context, AnnotatedTypeMetadata metadata)`,我们需要在这里放置我们的条件逻辑。 2. **类级别和方法级别**: - `@Conditional` 可以应用于 `@Bean` 方法,用于控制特定 bean 的创建。 - 也可以应用于 `@Configuration` 类,控制整个配置类的加载。 3. **组合多个条件** + 可以使用 `@Conditional` 注解的数组形式来组合多个条件,所有条件都必须满足才能创建 bean。 4. **与其他注解的组合** + `@Conditional` 可以与其他注解一起使用,如 `@Profile`。但是注意他们之间的交互效果。例如,如果一个 bean 标记为特定的 `@Profile` 并且使用 `@Conditional`,那么两者都必须为 true 才能创建该 bean。 5. **避免复杂的逻辑** + 虽然 `Condition` 允许我们编写任意的条件逻辑,但最好避免过于复杂。简单明了的逻辑更易于理解和维护。 6. **性能** + `matches` 方法可能会在应用的生命周期中被多次调用,因此应确保其执行效率,避免在此方法中进行高开销的操作。 7. **配合 `ConditionContext`**: + `ConditionContext` 提供了关于当前应用上下文、环境属性、系统属性等的信息,可以使我们的条件判断更加具体和强大。 8. **自定义条件注解** + 为了重用或组合多个条件,我们可以创建自己的条件注解。例如,我们可以创建一个 `@ConditionalOnCustomActive` 注解,它封装了检查`enable.custom`的条件。 9. **注意与 `@Profile` 的区别** + 虽然 `@Conditional` 和 `@Profile` 在某些情况下可以达到相同的效果,但它们的目的不同。`@Profile` 基于环境,而 `@Conditional` 更加通用,允许我们基于任意条件创建 bean。 ### 九、总结 #### 最佳实践总结 1. **基于`@Bean`的条件配置** - **场景描述** - 在单个bean的创建上应用条件。 - **实现方法** - 通过在`@Bean`注解方法上直接使用`@Conditional`。 - **结果**: - 当条件满足(如`enable.bean`为`true`),特定的bean(如`user1`)会被注册。 - 当条件不满足,该bean不会被注册。 2. **基于`@Configuration`的条件配置** - **场景描述** - 控制整个配置类的激活状态,从而影响该配置中定义的所有beans。 - **实现方法** - 在`@Configuration`注解的类上直接使用`@Conditional`。 - **结果**: - 当条件满足(如`enable.config`为`true`),配置类被激活,其内部的所有beans(如`user3`和`user4`)都会被注册。 - 当条件不满足,配置类及其内部定义的所有beans都不会被注册。 3. **使用自定义组合注解** - **场景描述** - 创建自己的条件注解,以提供更清晰、更简洁的语法,或为特定的业务逻辑封装条件逻辑。 - **实现方法** - 定义一个新的注解(如`@ConditionalOnCustomActive`),并使用`@Conditional`元注解将其关联到特定的条件类。 - **结果**: - 当条件满足(如`enable.custom`为`true`),带有`@ConditionalOnCustomActive`注解的配置类或bean会被注册。 - 当条件不满足,它们不会被注册。 #### 源码分析总结 1. **初始化与启动** - 通过 `AnnotationConfigApplicationContext` 构造函数初始化 Spring 上下文,并通过 `register` 和 `refresh` 方法完成 bean 的注册和容器的刷新。 2. **注册组件类** + 使用 `AnnotatedBeanDefinitionReader` 来注册组件类。在 `register` 方法中,每一个组件类都会通过 `registerBean` 方法进行注册。 3. **条件检查** + 在 `doRegisterBean` 方法中,执行了核心的条件检查逻辑。使用 `ConditionEvaluator` 来评估与给定 bean 或配置类关联的条件是否满足。 4. **元数据检查** + `ConditionEvaluator` 会首先检查提供的元数据(通常与特定的 bean 或配置类的注解关联)是否包含 `@Conditional` 注解。如果没有,直接进行下一步。如果存在,继续检查条件是否满足。 5. **条件匹配** + `ConditionEvaluator` 获取与 `@Conditional` 注解关联的所有条件类,并为每一个条件类创建一个实例。随后,它会遍历所有的条件,并检查它们是否满足。这是通过调用每一个条件的 `matches` 方法来完成的。如果任何一个条件不满足,整个条件检查逻辑返回 `false`,表示不应注册与 `@Conditional` 注解关联的 bean 或配置类。 6. **条件实例化** + 通过 `getCondition` 方法,`ConditionEvaluator` 能够根据提供的条件类名称和类加载器实例化一个 `Condition` 对象。 ================================================ FILE: spring-annotation/spring-annotation-conditional/pom.xml ================================================ spring-annotation com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-annotation-conditional ================================================ FILE: spring-annotation/spring-annotation-conditional/src/main/java/com/xcs/spring/bean/ConditionBeanApplication.java ================================================ package com.xcs.spring.bean; import com.xcs.spring.bean.config.MyBeanConfiguration; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年08月07日 16时21分 **/ public class ConditionBeanApplication { public static void main(String[] args) { System.setProperty("enable.bean","false"); AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyBeanConfiguration.class); for (String beanDefinitionName : context.getBeanDefinitionNames()) { System.out.println("beanDefinitionName = " + beanDefinitionName); } } } ================================================ FILE: spring-annotation/spring-annotation-conditional/src/main/java/com/xcs/spring/bean/condition/BeanPropertyCondition.java ================================================ package com.xcs.spring.bean.condition; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata; public class BeanPropertyCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { return "true".equals(context.getEnvironment().getProperty("enable.bean")); } } ================================================ FILE: spring-annotation/spring-annotation-conditional/src/main/java/com/xcs/spring/bean/config/MyBeanConfiguration.java ================================================ package com.xcs.spring.bean.config; import com.xcs.spring.bean.condition.BeanPropertyCondition; import com.xcs.spring.bean.entity.User1; import com.xcs.spring.bean.entity.User2; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; @Configuration public class MyBeanConfiguration { @Bean @Conditional(BeanPropertyCondition.class) public User1 user1() { return new User1(); } @Bean public User2 user2() { return new User2(); } } ================================================ FILE: spring-annotation/spring-annotation-conditional/src/main/java/com/xcs/spring/bean/entity/User1.java ================================================ package com.xcs.spring.bean.entity; public class User1 { } ================================================ FILE: spring-annotation/spring-annotation-conditional/src/main/java/com/xcs/spring/bean/entity/User2.java ================================================ package com.xcs.spring.bean.entity; public class User2 { } ================================================ FILE: spring-annotation/spring-annotation-conditional/src/main/java/com/xcs/spring/configuration/ConditionConfigurationApplication.java ================================================ package com.xcs.spring.configuration; import com.xcs.spring.configuration.config.MyConfigConfiguration; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年08月07日 16时21分 **/ public class ConditionConfigurationApplication { public static void main(String[] args) { System.setProperty("enable.config","false"); AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfigConfiguration.class); for (String beanDefinitionName : context.getBeanDefinitionNames()) { System.out.println("beanDefinitionName = " + beanDefinitionName); } } } ================================================ FILE: spring-annotation/spring-annotation-conditional/src/main/java/com/xcs/spring/configuration/condition/ConfigPropertyCondition.java ================================================ package com.xcs.spring.configuration.condition; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata; public class ConfigPropertyCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { return "true".equals(System.getProperty("enable.config")); } } ================================================ FILE: spring-annotation/spring-annotation-conditional/src/main/java/com/xcs/spring/configuration/config/MyConfigConfiguration.java ================================================ package com.xcs.spring.configuration.config; import com.xcs.spring.configuration.condition.ConfigPropertyCondition; import com.xcs.spring.configuration.entity.User3; import com.xcs.spring.configuration.entity.User4; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; @Configuration @Conditional(ConfigPropertyCondition.class) public class MyConfigConfiguration { @Bean public User3 user3() { return new User3(); } @Bean public User4 user4() { return new User4(); } } ================================================ FILE: spring-annotation/spring-annotation-conditional/src/main/java/com/xcs/spring/configuration/entity/User3.java ================================================ package com.xcs.spring.configuration.entity; public class User3 { } ================================================ FILE: spring-annotation/spring-annotation-conditional/src/main/java/com/xcs/spring/configuration/entity/User4.java ================================================ package com.xcs.spring.configuration.entity; public class User4 { } ================================================ FILE: spring-annotation/spring-annotation-conditional/src/main/java/com/xcs/spring/custom/ConditionCustomApplication.java ================================================ package com.xcs.spring.custom; import com.xcs.spring.custom.config.MyCustomConfiguration; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年08月07日 16时21分 **/ public class ConditionCustomApplication { public static void main(String[] args) { System.setProperty("enable.custom","false"); AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyCustomConfiguration.class); for (String beanDefinitionName : context.getBeanDefinitionNames()) { System.out.println("beanDefinitionName = " + beanDefinitionName); } } } ================================================ FILE: spring-annotation/spring-annotation-conditional/src/main/java/com/xcs/spring/custom/annotation/ConditionalOnCustomActive.java ================================================ package com.xcs.spring.custom.annotation; import com.xcs.spring.custom.condition.CustomActiveCondition; import org.springframework.context.annotation.Conditional; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Conditional(CustomActiveCondition.class) public @interface ConditionalOnCustomActive { } ================================================ FILE: spring-annotation/spring-annotation-conditional/src/main/java/com/xcs/spring/custom/condition/CustomActiveCondition.java ================================================ package com.xcs.spring.custom.condition; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata; public class CustomActiveCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { return "true".equals(System.getProperty("enable.custom")); } } ================================================ FILE: spring-annotation/spring-annotation-conditional/src/main/java/com/xcs/spring/custom/config/MyCustomConfiguration.java ================================================ package com.xcs.spring.custom.config; import com.xcs.spring.custom.annotation.ConditionalOnCustomActive; import com.xcs.spring.custom.entity.User5; import com.xcs.spring.custom.entity.User6; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration @ConditionalOnCustomActive public class MyCustomConfiguration { @Bean public User5 user5() { return new User5(); } @Bean public User6 user6() { return new User6(); } } ================================================ FILE: spring-annotation/spring-annotation-conditional/src/main/java/com/xcs/spring/custom/entity/User5.java ================================================ package com.xcs.spring.custom.entity; public class User5 { } ================================================ FILE: spring-annotation/spring-annotation-conditional/src/main/java/com/xcs/spring/custom/entity/User6.java ================================================ package com.xcs.spring.custom.entity; public class User6 { } ================================================ FILE: spring-annotation/spring-annotation-configuration/README.md ================================================ ## @Configuration - [@Configuration](#configuration) - [一、基本信息](#一基本信息) - [二、注解描述](#二注解描述) - [三、注解源码](#三注解源码) - [四、主要功能](#四主要功能) - [五、最佳实践](#五最佳实践) - [proxyBeanMethods设置为true](#proxybeanmethods设置为true) - [proxyBeanMethods设置为false](#proxybeanmethods设置为false) - [六、时序图](#六时序图) - [初始化流程](#初始化流程) - [注册流程](#注册流程) - [增强流程](#增强流程) - [七、源码分析](#七源码分析) - [初始化流程](#初始化流程-1) - [注册流程](#注册流程-1) - [增强流程](#增强流程-1) - [八、注意事项](#八注意事项) - [九、总结](#九总结) - [最佳实践总结](#最佳实践总结) - [源码分析总结](#源码分析总结) - [十、常见问题](#十常见问题) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [我的CSDN](https://blog.csdn.net/duzhuang2399/article/details/132212963) 📚 **文章目录** - [所有文章](https://github.com/xuchengsheng/spring-reading) 🔗 **源码地址** - [@Configuration源码](https://github.com/xuchengsheng/spring-reading/blob/master/spring-annotation/spring-annotation-configuration/README.md) ### 二、注解描述 `@Configuration` 是 Spring 框架中提供的一个核心注解,它指示一个类声明了一个或多个 `@Bean` 定义方法,这些方法由 Spring 容器管理并执行,以便在运行时为 bean 实例化、配置和初始化对象。 ### 三、注解源码 `@Configuration`注解是 Spring 框架自 3.0 版本开始引入的一个核心注解,标记一个类为 Spring 的配置类,该类可能包含一个或多个 `@Bean` 方法来定义和配置 Spring beans,其中一个`value` 属性允许用户明确指定与 `@Configuration` 类关联的 Spring bean 定义的名称。如果未指定,名称会自动生成,另外一个`proxyBeanMethods` 属性决定是否应代理 `@Bean` 方法来强制实施 bean 生命周期行为,如确保返回的是单例 bean 实例。 ```java /** * @Configuration 是一个核心注解,用于指示该类是一个 Spring 配置类, * 可能包含一个或多个 @Bean 定义方法。此注解与 XML 配置是等效的,但以编程方式 * 提供了更多的类型安全和灵活性。它通常与 @Bean、@ComponentScan 和其他注解结合使用, * 为 Spring 应用程序上下文定义 beans 和配置。 * * 通过 @Component 注解,@Configuration 被视为一个组件, * 这意味着 Spring 的组件扫描可以自动检测和处理它。 * * 例如,当在应用程序上下文中注册 @Configuration 类时, * 该类中定义的 @Bean 方法将被解析,并在上下文中注册相应的 beans。 * * @author Rod Johnson * @author Chris Beams * @author Juergen Hoeller * @since 3.0 * @see Bean * @see Profile * @see Import * @see ImportResource * @see ComponentScan * @see Lazy * @see PropertySource * @see AnnotationConfigApplicationContext * @see ConfigurationClassPostProcessor * @see org.springframework.core.env.Environment * @see org.springframework.test.context.ContextConfiguration */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Configuration { /** * 明确指定与 @Configuration 类相关的 Spring bean 定义的名称。 * 如果未指定(这是常见情况),则会自动生成 bean 的名称。 * 这个自定义名称仅在 @Configuration 类通过组件扫描被检测到, * 或直接提供给 AnnotationConfigApplicationContext 时才有效。 * 如果 @Configuration 类以传统的 XML bean 定义方式注册, * 则 bean 元素的名称/ID 会优先。 */ @AliasFor(annotation = Component.class) String value() default ""; /** * 指定是否应代理 @Bean 方法,以强制执行 bean 生命周期行为。 * 例如,即使在用户代码中直接调用了 @Bean 方法,也要返回共享的单例 bean 实例。 * 这个特性需要方法拦截,通过在运行时生成的 CGLIB 子类来实现, * 这带有一些限制,如配置类和其方法不能声明为 final。 * * 默认为 true,这允许在配置类内部通过直接方法调用进行"bean之间的引用", * 以及从另一个配置类对此配置的 @Bean 方法的外部调用。 * 如果这不是必需的,因为此特定配置的每个 @Bean 方法都是独立的, * 并设计为容器使用的简单工厂方法,可以将此标志设置为 false 以避免 CGLIB 子类处理。 * * 关闭 bean 方法拦截实际上是独立处理 @Bean 方法, * 就像在非 @Configuration 类上声明的那样,即 "@Bean 简易模式"。 * 在行为上,它等同于删除 @Configuration 注解。 */ boolean proxyBeanMethods() default true; } ``` ### 四、主要功能 1. **Bean定义方法** + `@Configuration` 类中可以包含一个或多个使用 `@Bean` 注解的方法,这些方法用于创建和配置应用程序上下文中的 beans。 1. **代理支持** + 当 `@Configuration` 中的 `proxyBeanMethods` 属性设置为 `true`(这是默认值)时,`@Bean` 方法会被代理以确保正确的 bean 生命周期。这允许在一个配置类中,一个 `@Bean` 方法调用另一个 `@Bean` 方法并返回单例 bean 实例。 1. **组件扫描** + 由于 `@Configuration` 注解本身带有 `@Component` 注解,因此它可以被 Spring 的组件扫描机制自动检测。这意味着在启用组件扫描的应用程序上下文中,只需声明 `@Configuration` 类而无需明确注册。 1. **模块化和组合** + 通过使用 `@Import` 注解,我们可以将多个 `@Configuration` 类组合在一起,从而实现配置的模块化。此外,`@Profile` 注解可以与 `@Configuration` 一起使用,以提供基于环境或其他条件的配置。 1. **属性源和属性占位符** + `@Configuration` 类可以与 `@PropertySource` 注解结合使用,从而将属性文件的值导入 Spring 环境。这些值可以使用 `@Value` 注解或直接通过 `Environment` API 注入到 beans 中。 1. **与其他注解结合**: + `@Configuration` 类通常与其他 Spring 注解(如 `@ComponentScan`、`@PropertySource` 等)结合使用,以提供全面的配置机制。 ### 五、最佳实践 #### proxyBeanMethods设置为true 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyConfiguration`类型的bean,最后调用了 `myBean` 方法两次,并将其结果打印到控制台。 ```java public class ConfigurationApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MyConfiguration configuration = context.getBean(MyConfiguration.class); System.out.println(configuration.myBean()); System.out.println(configuration.myBean()); } } ``` `MyConfiguration` 类定义了一个名为 `myBean` 的 bean,这个 bean 的类型是 `MyBean`。每次从 Spring 容器请求这个 bean 时,都会得到同一个 `MyBean` 的实例。 ```java // proxyBeanMethods默认就是true,此处不设置 @Configuration public class MyConfiguration { @Bean public MyBean myBean(){ return new MyBean(); } } ``` `MyBean` 的简单类,它没有任何属性或方法。 ```java public class MyBean { } ``` 运行结果发现,两次对 `MyBean` 对象的引用,这两个引用具有相同的 hashcode (`@f736069`),表示它们引用的是相同的对象,因为在 `MyConfiguration` 类中,`myBean()` 方法被 `@Bean` 注解标记,它默认返回单例对象。当我们在 `ConfigurationApplication` 的 `main` 方法中两次调用 `myBean()` 方法时,Spring 容器都返回相同的 `MyBean` 实例。 ```java com.xcs.spring.bean.MyBean@f736069 com.xcs.spring.bean.MyBean@f736069 ``` #### proxyBeanMethods设置为false 将 `proxyBeanMethods` 设置为 `false` 时,此代理行为被禁用。这意味着,如果我们在配置类内部多次调用同一个 `@Bean` 方法,每次都会创建一个新的实例。 ```java @Configuration(proxyBeanMethods = false) public class MyConfiguration { @Bean public MyBean myBean(){ return new MyBean(); } } ``` 运行结果发现,两次对 `MyBean` 对象的引用,这两个引用具有不相同的 hashcode (`@3b69e7d1`,`@815b41f`),表示它们引用的是不相同的对象。因为我们在 `@Configuration` 注解设置了 `proxyBeanMethods = false`,并在 `ConfigurationApplication` 的 `main` 方法中两次调用 `myBean()` 方法,每次调用都会创建一个新的 `MyBean` 实例,这就是为什么我们看到两个不同的 hashcodes。 ``` com.xcs.spring.bean.MyBean@3b69e7d1 com.xcs.spring.bean.MyBean@815b41f ``` ### 六、时序图 时序图主要分为三个关键步骤 #### 初始化流程 - 当 `AnnotationConfigApplicationContext` 被实例化时,它开始初始化过程。 - 在这个过程中,`AnnotatedBeanDefinitionReader` 会被创建。这个读取器负责解析带注解的类,并将其转化为 Spring 可理解的 `BeanDefinition`。 - 然后,Spring 的核心工具类 `AnnotationConfigUtils` 会注册一些默认的处理器,特别是 `ConfigurationClassPostProcessor`,这是处理 `@Configuration` 注解的核心类。 ~~~mermaid sequenceDiagram ConfigurationApplication->>AnnotationConfigApplicationContext: AnnotationConfigApplicationContext(componentClasses)
启动上下文 AnnotationConfigApplicationContext-->>ConfigurationApplication: 返回context
返回实例 AnnotationConfigApplicationContext->>AnnotationConfigApplicationContext: AnnotationConfigApplicationContext()
构造函数 AnnotationConfigApplicationContext->>AnnotatedBeanDefinitionReader: AnnotatedBeanDefinitionReader(registry)
创建读取器 AnnotatedBeanDefinitionReader-->>AnnotationConfigApplicationContext: 返回reader AnnotatedBeanDefinitionReader-->>AnnotationConfigUtils: registerAnnotationConfigProcessors(registry)
注册处理器 AnnotationConfigUtils-->>AnnotationConfigUtils: registerAnnotationConfigProcessors(registry,source)
处理器注册 AnnotationConfigUtils-->>AnnotationConfigUtils: registerPostProcessor(registry,definition,beanName)
后置处理器 AnnotationConfigUtils-->>DefaultListableBeanFactory: registerBeanDefinition(beanName, beanDefinition)
注册Bean定义 ~~~ #### 注册流程 - 使用 `AnnotationConfigApplicationContext` 的 `register` 方法,配置类(带有 `@Bean` 方法的类)会被注册到Spring上下文中。 - `AnnotatedBeanDefinitionReader` 负责解析这些配置类,并创建相应的 `BeanDefinition`。 - 这些 `BeanDefinition` 最后会在 `DefaultListableBeanFactory` 中被注册,该工厂是 Spring IOC 容器的核心部分,它管理所有的 beans 和其定义。 ~~~mermaid sequenceDiagram ConfigurationApplication->>AnnotationConfigApplicationContext: AnnotationConfigApplicationContext(componentClasses)
启动上下文 AnnotationConfigApplicationContext-->>ConfigurationApplication: 返回context
返回上下文实例 AnnotationConfigApplicationContext->>AnnotationConfigApplicationContext: register(componentClasses)
注册组件类 AnnotationConfigApplicationContext->>AnnotatedBeanDefinitionReader: register(componentClasses)
读取器注册类 AnnotatedBeanDefinitionReader-->>AnnotatedBeanDefinitionReader: registerBean(beanClass)
注册Bean类 AnnotatedBeanDefinitionReader-->>AnnotatedBeanDefinitionReader: doRegisterBean(beanClass,name,qualifiers, supplier,customizers)
执行Bean注册 AnnotatedBeanDefinitionReader-->>BeanDefinitionReaderUtils: registerBeanDefinition(definitionHolder,registry)
注册Bean定义 BeanDefinitionReaderUtils-->>DefaultListableBeanFactory: registerBeanDefinition(beanName,beanDefinition)
工厂存Bean定义 ~~~ #### 增强流程 - 当容器开始刷新(通过 `refresh` 方法),它会启动 beans 的创建和初始化过程。 - 在这个过程中,所有的 `BeanFactoryPostProcessor` 会被触发,特别是 `ConfigurationClassPostProcessor`。 - `ConfigurationClassPostProcessor` 的主要职责是增强 `@Configuration` 类,确保 `@Bean` 方法的正确代理行为。它使用 `ConfigurationClassEnhancer` 来增强类,使其能够正确地管理 bean 的生命周期,并确保,例如,单例 beans 只被创建一次。 ~~~mermaid sequenceDiagram ConfigurationApplication->>AnnotationConfigApplicationContext: AnnotationConfigApplicationContext(componentClasses)
启动上下文 AnnotationConfigApplicationContext-->>ConfigurationApplication: 返回context
返回上下文实例 AnnotationConfigApplicationContext-->>AnnotationConfigApplicationContext: refresh
刷新容器 AnnotationConfigApplicationContext-->>AnnotationConfigApplicationContext: invokeBeanFactoryPostProcessors
触发后处理器 AnnotationConfigApplicationContext-->>PostProcessorRegistrationDelegate: invokeBeanFactoryPostProcessors(beanFactory,beanFactoryPostProcessors)
委派后处理 PostProcessorRegistrationDelegate-->>PostProcessorRegistrationDelegate: invokeBeanFactoryPostProcessors(postProcessors,beanFactory)
执行后处理 PostProcessorRegistrationDelegate-->>ConfigurationClassPostProcessor: postProcessBeanFactory(beanFactory)
处理配置类 ConfigurationClassPostProcessor-->>ConfigurationClassPostProcessor: enhanceConfigurationClasses(beanFactory)
增强配置类 ConfigurationClassPostProcessor-->>ConfigurationClassEnhancer: enhance(configClass,classLoader)
执行增强操作 ConfigurationClassEnhancer-->>ConfigurationClassEnhancer: createClass(enhancer)
创建增强类 ConfigurationClassEnhancer-->>ConfigurationClassPostProcessor: 增强后的Class类 ~~~ ### 七、源码分析 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyConfiguration`类型的bean,最后调用了 `myBean` 方法两次,并将其结果打印到控制台。 ```java public class ConfigurationApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MyConfiguration configuration = context.getBean(MyConfiguration.class); System.out.println(configuration.myBean()); System.out.println(configuration.myBean()); } } ``` 在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤。 ```java public AnnotationConfigApplicationContext(Class... componentClasses) { // 步骤1. 这个构造方法初始化了基本的配置读取器和类路径扫描器 this(); // 步骤2. 这个方法将这些类注册到 Spring 上下文中,这样它们可以被识别并进一步处理。 register(componentClasses); // 步骤3. 这个方法触发整个Spring容器的启动过程 refresh(); } ``` #### 初始化流程 我们首先来到`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中步骤1。在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`方法中,`AnnotationConfigApplicationContext` 的无参数构造函数中,初始化了 `AnnotatedBeanDefinitionReader` 和 `ClassPathBeanDefinitionScanner` 这两个核心组件。 ```java public AnnotationConfigApplicationContext() { StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create"); this.reader = new AnnotatedBeanDefinitionReader(this); createAnnotatedBeanDefReader.end(); this.scanner = new ClassPathBeanDefinitionScanner(this); } ``` 在`org.springframework.context.annotation.AnnotatedBeanDefinitionReader#AnnotatedBeanDefinitionReader`方法中,首先从注册表获取 `Environment`(配置环境的抽象)。如果没有获取到,它会创建一个新的环境,最后调用另一个构造函数来完成 `AnnotatedBeanDefinitionReader` 的实例化。 ```java public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) { this(registry, getOrCreateEnvironment(registry)); } ``` 在`org.springframework.context.annotation.AnnotatedBeanDefinitionReader#AnnotatedBeanDefinitionReader(registry,environment)`方法中,首先是设置了内部的条件评估器 (`conditionEvaluator`),条件评估器用于处理如 `@Conditional` 这样的注解。然后调用工具类 `AnnotationConfigUtils` 的 `registerAnnotationConfigProcessors` 方法来为注册表注册注解配置处理器,例如 `ConfigurationClassPostProcessor`,这是处理 `@Configuration` 注解的核心类。 ```java public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); Assert.notNull(environment, "Environment must not be null"); this.registry = registry; this.conditionEvaluator = new ConditionEvaluator(registry, environment, null); AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); } ``` 在`org.springframework.context.annotation.AnnotationConfigUtils#registerAnnotationConfigProcessors(registry)`方法中,该方法直接调用另一个重载版本的 `registerAnnotationConfigProcessors`,传入的 `registry` 和一个 `null` 值作为第二个参数。 ```java public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) { registerAnnotationConfigProcessors(registry, null); } ``` 在`org.springframework.context.annotation.AnnotationConfigUtils#registerAnnotationConfigProcessors(registry,source)`方法中,这个方法主要是确保了 `ConfigurationClassPostProcessor`被注册到指定的注册表中,从而保证了 `@Configuration` 注解及相关功能能够被正确处理。 ```java public static Set registerAnnotationConfigProcessors( BeanDefinitionRegistry registry, @Nullable Object source) { // ... [代码部分省略以简化] Set beanDefs = new LinkedHashSet<>(8); if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)); } // ... [代码部分省略以简化] return beanDefs; } ``` #### 注册流程 然后我们来到`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中步骤2。在org.springframework.context.annotation.AnnotationConfigApplicationContext#register方法中,主要是允许我们注册一个或多个组件类(例如,那些使用 `@Component`, `@Service`, `@Repository`, `@Controller`, `@Configuration` 等注解的类)到Spring容器。 ```java @Override public void register(Class... componentClasses) { Assert.notEmpty(componentClasses, "At least one component class must be specified"); StartupStep registerComponentClass = this.getApplicationStartup().start("spring.context.component-classes.register") .tag("classes", () -> Arrays.toString(componentClasses)); this.reader.register(componentClasses); registerComponentClass.end(); } ``` 在`org.springframework.context.annotation.AnnotatedBeanDefinitionReader#register`方法中,遍历每一个传入的组件类,并逐一调用另一个方法来完成实际的注册工作。 ```java public void register(Class... componentClasses) { for (Class componentClass : componentClasses) { registerBean(componentClass); } } ``` 在`org.springframework.context.annotation.AnnotatedBeanDefinitionReader#registerBean(beanClass)`方法中,主要目的是快速注册一个 bean 类型,而不需要指定其他详细的配置或参数。 ```java public void registerBean(Class beanClass) { doRegisterBean(beanClass, null, null, null, null); } ``` 在`org.springframework.context.annotation.AnnotatedBeanDefinitionReader#doRegisterBean`方法中,主要负责将给定的 bean 类型及其相关配置注册到Spring容器中。处理 bean 名称的生成、bean 定义的创建和注册,以及应用任何必要的代理模式。 ```java private void doRegisterBean(Class beanClass, @Nullable String name, @Nullable Class[] qualifiers, @Nullable Supplier supplier, @Nullable BeanDefinitionCustomizer[] customizers) { AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass); // ... [代码部分省略以简化] String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry)); // ... [代码部分省略以简化] BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName); definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry); } ``` 在`org.springframework.beans.factory.support.BeanDefinitionReaderUtils#registerBeanDefinition`方法中,主要负责bean定义及其所有相关别名都被注册到指定的 `BeanDefinitionRegistry`。这是 Spring 容器内部使用的一个实用方法,用于确保 bean 定义和其别名都正确存储,从而可以在后续的容器生命周期中被正确访问和使用。 ```java public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // Register bean definition under primary name. String beanName = definitionHolder.getBeanName(); registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // Register aliases for bean name, if any. String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String alias : aliases) { registry.registerAlias(beanName, alias); } } } ``` #### 增强流程 然后我们来到`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中步骤3。在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 ```java @Override public void refresh() throws BeansException, IllegalStateException { // ... [代码部分省略以简化] // 调用在上下文中注册为bean的工厂处理器 invokeBeanFactoryPostProcessors(beanFactory); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors`方法中,又委托了`PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()`进行调用。 ```java protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) { PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors()); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors`方法中,主要是处理Spring容器在启动时如何处理 `BeanFactoryPostProcessor` 的核心逻辑。 ```java public static void invokeBeanFactoryPostProcessors( ConfigurableListableBeanFactory beanFactory, List beanFactoryPostProcessors) { // ... [代码部分省略以简化] // Now, invoke the postProcessBeanFactory callback of all processors handled so far. invokeBeanFactoryPostProcessors(registryProcessors, beanFactory); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors`方法中,遍历提供的 `BeanFactoryPostProcessor` 集合,其中主要方法`postProcessBeanFactory` 是一个允许我们介入并修改 `BeanFactory` 的扩展点。在此实现中增强通过 `@Configuration` 注解定义的配置类。 ```java private static void invokeBeanFactoryPostProcessors( Collection postProcessors, ConfigurableListableBeanFactory beanFactory) { for (BeanFactoryPostProcessor postProcessor : postProcessors) { StartupStep postProcessBeanFactory = beanFactory.getApplicationStartup().start("spring.context.bean-factory.post-process") .tag("postProcessor", postProcessor::toString); postProcessor.postProcessBeanFactory(beanFactory); postProcessBeanFactory.end(); } } ``` 在`org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanFactory`方法中,主要目的是为 `@Configuration` 标注的类进行增强。 ```java @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { // ... [代码部分省略以简化] enhanceConfigurationClasses(beanFactory); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.annotation.ConfigurationClassPostProcessor#enhanceConfigurationClasses`方法中,主要是对标记为 `@Configuration` 的类进行增强,确保它们的 `@Bean` 方法在每次调用时都返回相同的实例(除非它们是原型作用域的)。这是通过使用 `ConfigurationClassEnhancer` 来创建代理类实现的。这种代理确保了 Spring IoC 容器的正确行为,尤其是对于配置类。 ```java public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) { // ... [代码部分省略以简化] ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer(); for (Map.Entry entry : configBeanDefs.entrySet()) { AbstractBeanDefinition beanDef = entry.getValue(); // If a @Configuration class gets proxied, always proxy the target class beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE); // Set enhanced subclass of the user-specified bean class Class configClass = beanDef.getBeanClass(); Class enhancedClass = enhancer.enhance(configClass, this.beanClassLoader); if (configClass != enhancedClass) { // ... [代码部分省略以简化] beanDef.setBeanClass(enhancedClass); } } // ... [代码部分省略以简化] } ``` 在`org.springframework.context.annotation.ConfigurationClassEnhancer#enhance`方法中,首先是查看该类还没有被增强,则创建一个新的增强器并生成一个代理类;如果它已经被增强,那么直接返回原始类。这种增强确保了 `@Configuration` 类中的 `@Bean` 方法在每次调用时都返回相同的实例。 ```java public Class enhance(Class configClass, @Nullable ClassLoader classLoader) { if (EnhancedConfiguration.class.isAssignableFrom(configClass)) { // ... [代码部分省略以简化] return configClass; } Class enhancedClass = createClass(newEnhancer(configClass, classLoader)); // ... [代码部分省略以简化] return enhancedClass; } ``` 在`org.springframework.context.annotation.ConfigurationClassEnhancer#newEnhancer`方法中,主要负责为给定的配置类创建一个用于生成代理类的 `Enhancer` 对象。 ```java private Enhancer newEnhancer(Class configSuperClass, @Nullable ClassLoader classLoader) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(configSuperClass); enhancer.setInterfaces(new Class[] {EnhancedConfiguration.class}); enhancer.setUseFactory(false); enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE); enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader)); enhancer.setCallbackFilter(CALLBACK_FILTER); enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes()); return enhancer; } ``` 在`org.springframework.context.annotation.ConfigurationClassEnhancer#createClass`方法中,使用提供的 `Enhancer` 对象来创建增强后的类,并为这个类注册静态回调。 ```java private Class createClass(Enhancer enhancer) { Class subclass = enhancer.createClass(); // Registering callbacks statically (as opposed to thread-local) // is critical for usage in an OSGi environment (SPR-5932)... Enhancer.registerStaticCallbacks(subclass, CALLBACKS); return subclass; } ``` 在`org.springframework.context.annotation.ConfigurationClassEnhancer#createClass`方法中,使用了一个名为 `CALLBACKS` 的静态常量数组,它包含了三个回调对象。这些回调对象在CGLIB库中用于拦截和处理增强(代理)类的方法调用。 ```java private static final Callback[] CALLBACKS = new Callback[] { new BeanMethodInterceptor(), new BeanFactoryAwareMethodInterceptor(), NoOp.INSTANCE }; ``` 在`org.springframework.context.annotation.ConfigurationClassEnhancer.BeanMethodInterceptor#intercept`方法中,主要是对 `@Bean` 方法的拦截逻辑,确保它们在被调用时总是返回正确的bean实例。这是通过结合检查当前方法、解析bean名称、处理特殊情况(如 `FactoryBeans` 或作用域代理)以及从bean工厂解析实际的bean引用来实现的。 ```java public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs, MethodProxy cglibMethodProxy) throws Throwable { // 获取关联的 BeanFactory 通过反射读取了代理类中的$$beanFactory字段 ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance); // 确定当前 @Bean 方法对应的 bean 名称 String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod); // 检查当前的 @Bean 方法是否定义了一个作用域代理 if (BeanAnnotationHelper.isScopedProxy(beanMethod)) { String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName); if (beanFactory.isCurrentlyInCreation(scopedBeanName)) { beanName = scopedBeanName; } } // FactoryBeans 在 Spring 中是特殊的 beans,它们不产生 bean 实例本身,而是产生其他 beans。 // 此代码块处理了当 FactoryBean 被请求时的情况, // 确保返回的是 FactoryBean 创建的实际 bean,而不是 FactoryBean 本身。 if (factoryContainsBean(beanFactory, BeanFactory.FACTORY_BEAN_PREFIX + beanName) && factoryContainsBean(beanFactory, beanName)) { // 此部分代码省略,但它处理 FactoryBean 创建的 bean 的返回和增强 } // 检查当前的方法是否是正在被工厂调用的工厂方法 if (isCurrentlyInvokedFactoryMethod(beanMethod)) { // 如果是,直接调用方法的原始实现 return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs); } // 尝试从 bean 工厂中解析并返回 bean 的引用 return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName); } ``` 在`org.springframework.context.annotation.ConfigurationClassEnhancer.BeanMethodInterceptor#resolveBeanReference`方法中,主要责任是确保能够从 `BeanFactory` 中安全、正确地获取到bean实例,并处理所有相关的边缘情况和潜在异常。 ```java private Object resolveBeanReference(Method beanMethod, Object[] beanMethodArgs, ConfigurableBeanFactory beanFactory, String beanName) { // 判断bean是否正在创建中 boolean alreadyInCreation = beanFactory.isCurrentlyInCreation(beanName); try { // 如果bean正在创建中,暂时将其设置为不在创建中,以避免异常 if (alreadyInCreation) { beanFactory.setCurrentlyInCreation(beanName, false); } boolean useArgs = !ObjectUtils.isEmpty(beanMethodArgs); // 对于单例的bean,如果bean方法的参数包含null,则可能预期它们会被自动装配 if (useArgs && beanFactory.isSingleton(beanName)) { for (Object arg : beanMethodArgs) { if (arg == null) { useArgs = false; break; } } } // 根据上面的判断,从BeanFactory中获取bean实例 Object beanInstance = (useArgs ? beanFactory.getBean(beanName, beanMethodArgs) : beanFactory.getBean(beanName)); // 检查获取的bean实例是否与@Bean方法的返回类型兼容 if (!ClassUtils.isAssignableValue(beanMethod.getReturnType(), beanInstance)) { if (beanInstance.equals(null)) { // 如果返回了特定的NullBean实例,进行相应的处理 // ... [日志输出代码] beanInstance = null; } else { // 抛出异常,说明有一个同名但类型不兼容的bean覆盖了当前的bean String msg = String.format("@Bean method %s.%s ...", // ... [代码省略以简化] ); throw new IllegalStateException(msg); } } // 如果当前正在调用另一个@Bean方法,处理其依赖关系 Method currentlyInvoked = SimpleInstantiationStrategy.getCurrentlyInvokedFactoryMethod(); if (currentlyInvoked != null) { String outerBeanName = BeanAnnotationHelper.determineBeanNameFor(currentlyInvoked); beanFactory.registerDependentBean(beanName, outerBeanName); } return beanInstance; } finally { // 清理阶段,恢复bean的创建状态 if (alreadyInCreation) { beanFactory.setCurrentlyInCreation(beanName, true); } } } ``` ### 八、注意事项 1. **单例保证**: - 在 `@Configuration` 类中,如果一个方法被标记为 `@Bean` 并被多次调用,它不会多次实例化一个 bean,而是返回同一个实例。这是因为 CGLIB 增强了 `@Configuration` 类,以确保 bean 的单例特性。 2. **proxyBeanMethods属性**: - `@Configuration(proxyBeanMethods = false)` 会关闭 CGLIB 代理的生成。这样做可以提高性能,但可能会导致单例 bean 的引用不一致,特别是在同一个配置类中直接调用其他 `@Bean` 方法时。 3. **防止循环引用**: - 在 `@Configuration` 类中,避免创建循环依赖。这可能会导致创建 bean 时出现问题。 4. **使用`@Profile`**: - 可以与 `@Configuration` 一起使用 `@Profile` 注解,以根据当前环境条件决定是否加载某个配置。 5. **避免使用 `final`**: - 由于 `@Configuration` 类是通过 CGLIB 增强的,因此它们不能是 `final` 类型,同样,它们的方法也不能声明为 `final`。 ### 九、总结 #### 最佳实践总结 1. **`proxyBeanMethods = true`(默认)**: - 当在 `@Configuration` 类中调用一个由 `@Bean` 注解的方法时,Spring 容器确保每次都返回同一个 bean 实例。这是通过 CGLIB 代理实现的,该代理拦截对该方法的所有调用并返回 bean 的单例实例。这就是为什么在 `ConfigurationApplication` 的 `main` 方法中,两次调用 `myBean()` 方法都返回具有相同 hashcode 的 `MyBean` 实例。 2. **`proxyBeanMethods = false`**: - 在这种配置下,`@Configuration` 类中的方法不再被代理。因此,如果在配置类内部多次调用同一个 `@Bean` 方法,每次调用都会创建一个新的 bean 实例。在 `ConfigurationApplication` 的 `main` 方法中,两次调用 `myBean()` 方法会返回具有不同 hashcode 的 `MyBean` 实例,这证明了两次调用返回了两个不同的对象。 #### 源码分析总结 1. **初始化流程**: - 当使用 `AnnotationConfigApplicationContext` 启动 Spring 应用时,会调用其构造函数,该函数执行三个主要步骤:初始化、注册和刷新。 - 初始化过程中,Spring 上下文创建 `AnnotatedBeanDefinitionReader` 和 `ClassPathBeanDefinitionScanner`。`AnnotatedBeanDefinitionReader` 负责注册通过注解定义的 beans。 - `AnnotationConfigUtils.registerAnnotationConfigProcessors` 方法确保必要的后处理器(如 `ConfigurationClassPostProcessor`)注册到 Spring 容器中,从而能够识别和处理 `@Configuration` 类。 2. **注册流程**: - 使用 `AnnotatedBeanDefinitionReader` 将配置类(如 `MyConfiguration`)注册到 Spring 容器中。 - 针对每个 `@Bean` 方法,`BeanDefinition`(bean 定义)被创建和注册到 `BeanDefinitionRegistry` 中。 3. **增强流程**: - 在容器的刷新过程中,`invokeBeanFactoryPostProcessors` 方法被调用,以执行所有的 `BeanFactoryPostProcessor` 实现。 - `ConfigurationClassPostProcessor` 是一个关键的后处理器,它识别配置类并进行增强。 - 对于每个标记为 `@Configuration` 的类,通过 `ConfigurationClassEnhancer` 创建一个 CGLIB 代理类。 - 这个代理确保对配置类中的 `@Bean` 方法的每次调用都返回同一个 bean 实例,除非它是原型作用域的。 - 当应用上下文启动完成后,对于任何请求的 bean,代理的 `@Bean` 方法会从 Spring 容器中返回已存在的 bean 实例,而不是重新创建一个新的实例。 ### 十、常见问题 1. **@Configuration中full模式与lite模式如何选择?** `@Configuration` 注解有两种模式:`full` 和 `lite`。它们在功能和性能上有所不同。了解它们的优缺点有助于为特定的场景做出合适的选择。 + Full 模式 - 启用方式:在 `@Configuration` 注解中不设置 `proxyBeanMethods` 或将其设置为 `true`。 - 功能:当在配置类中的 `@Bean` 方法内部调用另一个 `@Bean` 方法时,Spring 会确保返回的是容器中的单例bean,而不是一个新的实例。这是通过CGLIB代理实现的。 - 优势:保持单例语义,确保容器中的单例Bean在配置类中的调用中始终是单例的。 - 劣势:需要通过CGLIB创建配置类的子类,可能带来一些性能开销,增加了启动时间,可能与某些库不兼容,这些库期望操作实际类而不是其CGLIB代理。 + +Lite 模式 - 启用方式:在 `@Configuration` 注解中设置 `proxyBeanMethods` 为 `false`。 - 功能:禁用CGLIB代理。`@Bean` 方法之间的调用就像普通的Java方法调用,每次都会创建一个新的实例。 - 优势:更快的启动时间,因为不需要通过CGLIB增强配置类,对于简单的注入,这种模式可能更为简洁和直接。 - 劣势:不保持单例语义。如果在一个 `@Bean` 方法内部调用另一个 `@Bean` 方法,会创建一个新的bean实例。 + 如何选择 - 如果我们的配置中需要确保在配置类中调用的bean始终是Spring容器中的单例bean,选择full模式。 - 如果我们的配置类只是简单地定义beans并注入依赖,且不需要在配置类方法之间共享单例实例,选择lite模式。 - 如果我们关心应用的启动性能,特别是在云环境或微服务中,使用lite模式可能更合适,因为它避免了额外的CGLIB处理。 ​ 最终,根据项目的具体需求和场景选择合适的模式。如果没有特殊的单例需求,推荐使用lite模式,因为它更简单且启动性能更好。 ================================================ FILE: spring-annotation/spring-annotation-configuration/pom.xml ================================================ spring-annotation com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-annotation-configuration ================================================ FILE: spring-annotation/spring-annotation-configuration/src/main/java/com/xcs/spring/ConfigurationApplication.java ================================================ package com.xcs.spring; import com.xcs.spring.config.MyConfiguration; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年08月07日 16时21分 **/ public class ConfigurationApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MyConfiguration configuration = context.getBean(MyConfiguration.class); System.out.println(configuration.myBean()); System.out.println(configuration.myBean()); } } ================================================ FILE: spring-annotation/spring-annotation-configuration/src/main/java/com/xcs/spring/bean/MyBean.java ================================================ package com.xcs.spring.bean; /** * @author xcs * @date 2023年08月07日 16时26分 **/ public class MyBean { } ================================================ FILE: spring-annotation/spring-annotation-configuration/src/main/java/com/xcs/spring/config/MyConfiguration.java ================================================ package com.xcs.spring.config; import com.xcs.spring.bean.MyBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @author xcs * @date 2023年08月07日 16时25分 **/ @Configuration public class MyConfiguration { @Bean public MyBean myBean(){ return new MyBean(); } } ================================================ FILE: spring-annotation/spring-annotation-dependsOn/README.md ================================================ ## @DependsOn - [@DependsOn](#dependson) - [一、基本信息](#一基本信息) - [二、注解描述](#二注解描述) - [三、注解源码](#三注解源码) - [四、主要功能](#四主要功能) - [五、最佳实践](#五最佳实践) - [六、时序图](#六时序图) - [Bean注册时序图](#bean注册时序图) - [Bean创建时序图](#bean创建时序图) - [Bean销毁时序图](#bean销毁时序图) - [六、源码分析](#六源码分析) - [Bean注册源码分析](#bean注册源码分析) - [Bean创建源码分析](#bean创建源码分析) - [Bean销毁源码分析](#bean销毁源码分析) - [七、注意事项](#七注意事项) - [八、总结](#八总结) - [最佳实践总结](#最佳实践总结) - [源码分析总结](#源码分析总结) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [我的CSDN](https://blog.csdn.net/duzhuang2399/article/details/133800615) 📚 **文章目录** - [所有文章](https://github.com/xuchengsheng/spring-reading) 🔗 **源码地址** - [@DependsOn源码](https://github.com/xuchengsheng/spring-reading/tree/master/spring-annotation/spring-annotation-dependsOn) ### 二、注解描述 `@DependsOn`注解,用于定义 Bean 初始化顺序。有时,我们可能会碰到某些 Bean 需要在其他 Bean 之前被初始化的情况。在这种情况下,我们可以使用 `@DependsOn` 注解来明确指定 Bean 的初始化顺序。 ### 三、注解源码 `@DependsOn`注解是 Spring 框架自 3.0 版本开始引入的一个核心注解,其中`value`属性是 `@DependsOn` 注解的主要属性,它允许我们定义当前bean依赖的其他bean的名称。 ```java /** * 当前bean所依赖的其他bean。任何指定的bean都保证在这个bean之前被容器创建。 * 在少数情况下使用,当一个bean不通过属性或构造函数参数明确地依赖于另一个bean, * 而是依赖于另一个bean的初始化的副作用时。 * * depends-on 声明既可以指定初始化时的依赖,又可以在单例bean的情况下,指定对应的销毁时的依赖。 * 定义了 depends-on 关系的依赖bean会首先被销毁,然后再销毁给定的bean。 * 因此,depends-on 声明也可以控制关闭顺序。 * * 可以在直接或间接使用 org.springframework.stereotype.Component 注解的任何类上, * 或在使用 Bean 注解的方法上使用。 * * 在类级别使用 DependsOn 在未使用组件扫描的情况下不会产生任何效果。 * 如果通过XML声明了使用 DependsOn 注解的类,DependsOn 注解的元数据会被忽略, * 而 会被考虑。 * * @author Juergen Hoeller * @since 3.0 */ @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface DependsOn { // 定义当前bean所依赖的其他bean的名称。 String[] value() default {}; } ``` ### 四、主要功能 1. **初始化顺序** + 使用 `@DependsOn` 可以确保某个或某些 bean 在当前 bean 之前被初始化。这在某个 bean 的初始化逻辑依赖于另一个 bean 的副作用时特别有用。 2. **销毁顺序(仅限单例 bean)** + 除了影响初始化顺序,`@DependsOn` 也会影响单例 bean 的销毁顺序。依赖关系中的 bean 会在它们所依赖的 bean 之前被销毁。 3. **指定多个依赖** + `@DependsOn` 允许我们指定多个依赖,这意味着我们可以确保多个 bean 都在当前 bean 之前被初始化。 ### 五、最佳实践 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类,最后调用`context.close()`方法关闭容器。 ```java public class DependsOnApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); context.close(); } } ``` 这里使用`@Bean`注解,定义了三个Bean,是为了确保`BeanA`,`BeanB`,`BeanC`被 Spring 容器执行,其中`BeanA`依赖于 `BeanB`,`BeanB`依赖于 `BeanC`,`BeanC`没有明确的依赖关系。 ```java @Configuration public class MyConfiguration { @Bean @DependsOn("beanB") public BeanA beanA() { return new BeanA(); } @Bean @DependsOn("beanC") public BeanB beanB() { return new BeanB(); } @Bean public BeanC beanC() { return new BeanC(); } } ``` `BeanA`, `BeanB`, 和 `BeanC`,每一个都有各种的构造函数与实现 `DisposableBean` 接口, ```java public class BeanA implements DisposableBean { public BeanA() { System.out.println("BeanA Initialized"); } @Override public void destroy() throws Exception { System.out.println("BeanA Destroyed"); } } public class BeanB implements DisposableBean { public BeanB() { System.out.println("BeanB Initialized"); } @Override public void destroy() throws Exception { System.out.println("BeanB Destroyed"); } } public class BeanC implements DisposableBean { public BeanC() { System.out.println("BeanC Initialized"); } @Override public void destroy() throws Exception { System.out.println("BeanC Destroyed"); } } ``` 运行结果发现,通过 `@DependsOn` 注解和 `DisposableBean` 接口的 `destroy()` 方法,我们不仅可以控制 bean 的初始化顺序,还可以控制它们的销毁顺序。 ```assembly BeanC Initialized BeanB Initialized BeanA Initialized BeanA Destroyed BeanB Destroyed BeanC Destroyed PS 初始化的顺序为: 1. `BeanC` (`BeanC Initialized` 将会被打印) 2. `BeanB` (`BeanB Initialized` 将会被打印) 3. `BeanA` (`BeanA Initialized` 将会被打印) 当关闭 Spring 容器时,销毁的顺序是与初始化的顺序相反: 1. `BeanA` (`BeanA Destroyed` 将会被打印) 2. `BeanB` (`BeanB Destroyed` 将会被打印) 3. `BeanC` (`BeanC Destroyed` 将会被打印) ``` ### 六、时序图 #### Bean注册时序图 ~~~mermaid sequenceDiagram DependsOnApplication->>AnnotationConfigApplicationContext: AnnotationConfigApplicationContext(componentClasses)
启动上下文 AnnotationConfigApplicationContext-->>ConfigurationApplication: 返回context
返回上下文实例 AnnotationConfigApplicationContext->>AnnotationConfigApplicationContext: register(componentClasses)
注册组件类 AnnotationConfigApplicationContext->>AnnotatedBeanDefinitionReader: register(componentClasses)
读取器注册类 AnnotatedBeanDefinitionReader-->>AnnotatedBeanDefinitionReader: registerBean(beanClass)
注册Bean类 AnnotatedBeanDefinitionReader-->>AnnotatedBeanDefinitionReader: doRegisterBean(beanClass,name,qualifiers, supplier,customizers)
执行Bean注册 AnnotatedBeanDefinitionReader->>AnnotationConfigUtils:processCommonDefinitionAnnotations(abd)
处理通用定义注解 AnnotationConfigUtils-->>AnnotationConfigUtils:processCommonDefinitionAnnotations(abd,metadata)
解析DependsOn注解并存储在BeanDefinition中 AnnotatedBeanDefinitionReader->>BeanDefinitionReaderUtils: registerBeanDefinition(definitionHolder,registry)
注册Bean定义 BeanDefinitionReaderUtils->>DefaultListableBeanFactory: registerBeanDefinition(beanName,beanDefinition)
工厂存Bean定义 ~~~ #### Bean创建时序图 ~~~mermaid sequenceDiagram DependsOnApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)
创建应用上下文 AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()
刷新应用上下文 AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory)
完成bean工厂初始化 AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons()
预实例化单例beans DefaultListableBeanFactory->>+AbstractBeanFactory:getBean(name)
获取bean实例 AbstractBeanFactory->>AbstractBeanFactory:doGetBean(name,requiredType,args,typeCheckOnly)
具体获取bean逻辑 AbstractBeanFactory->>AbstractBeanDefinition:获取bean所依赖的bean名称 AbstractBeanDefinition->>AbstractBeanFactory:返回被依赖的bean名称 AbstractBeanFactory->>DefaultSingletonBeanRegistry:isDependent(beanName,dependentBeanName)
检查依赖关系 DefaultSingletonBeanRegistry->>DefaultSingletonBeanRegistry:isDependent(beanName,dependentBeanName,alreadySeen)
检查依赖关系 DefaultSingletonBeanRegistry->>AbstractBeanFactory:返回是否存在依赖 true or false AbstractBeanFactory->>-AbstractBeanFactory:getBean(name)
获取被依赖bean实例(递归) ~~~ #### Bean销毁时序图 ~~~mermaid sequenceDiagram DisposableBeanApplication->>AnnotationConfigApplicationContext: AnnotationConfigApplicationContext(componentClasses )
应用开始初始化上下文 AnnotationConfigApplicationContext-->>DisposableBeanApplication:初始化完成 DisposableBeanApplication->>AbstractApplicationContext:close()
请求关闭上下文 AbstractApplicationContext->>AbstractApplicationContext:doClose()
执行关闭逻辑 AbstractApplicationContext->>AbstractApplicationContext:destroyBeans()
开始销毁beans AbstractApplicationContext->>DefaultListableBeanFactory:destroySingletons()
销毁单例beans DefaultListableBeanFactory->>DefaultSingletonBeanRegistry:super.destroySingletons()
调父类销毁方法 DefaultSingletonBeanRegistry-->>DefaultListableBeanFactory:destroySingleton(beanName)
销毁单个bean DefaultListableBeanFactory->>DefaultSingletonBeanRegistry:super.destroySingleton(beanName)
调父类销毁bean方法 DefaultSingletonBeanRegistry->>DefaultSingletonBeanRegistry:destroyBean(beanName,bean)
执行销毁bean操作 DefaultSingletonBeanRegistry->>DefaultSingletonBeanRegistry:删除被依赖映射关系(dependentBeanMap) DefaultSingletonBeanRegistry->>DefaultSingletonBeanRegistry:删除依赖映射关系(dependenciesForBeanMap) ~~~ ### 六、源码分析 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类,最后调用`context.close()`方法关闭容器。 ```java public class DependsOnApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); context.close(); } } ``` 在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤。 ```java public AnnotationConfigApplicationContext(Class... componentClasses) { // 步骤1. 这个构造方法初始化了基本的配置读取器和类路径扫描器 this(); // 步骤2. 这个方法将这些类注册到 Spring 上下文中,这样它们可以被识别并进一步处理。 register(componentClasses); // 步骤3. 这个方法触发整个Spring容器的启动过程 refresh(); } ``` #### Bean注册源码分析 首先我们来到`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中步骤2。在`org.springframework.context.annotation.AnnotationConfigApplicationContext#register`方法中,主要是允许我们注册一个或多个组件类(例如,那些使用 `@Component`, `@Service`, `@Repository`, `@Controller`, `@Configuration` 等注解的类)到Spring容器。 ```java @Override public void register(Class... componentClasses) { Assert.notEmpty(componentClasses, "At least one component class must be specified"); StartupStep registerComponentClass = this.getApplicationStartup().start("spring.context.component-classes.register") .tag("classes", () -> Arrays.toString(componentClasses)); this.reader.register(componentClasses); registerComponentClass.end(); } ``` 在`org.springframework.context.annotation.AnnotatedBeanDefinitionReader#register`方法中,遍历每一个传入的组件类,并逐一调用另一个方法来完成实际的注册工作。 ```java public void register(Class... componentClasses) { for (Class componentClass : componentClasses) { registerBean(componentClass); } } ``` 在`org.springframework.context.annotation.AnnotatedBeanDefinitionReader#registerBean(beanClass)`方法中,主要目的是快速注册一个 bean 类型,而不需要指定其他详细的配置或参数。 ```java public void registerBean(Class beanClass) { doRegisterBean(beanClass, null, null, null, null); } ``` 在`org.springframework.context.annotation.AnnotatedBeanDefinitionReader#doRegisterBean`方法中,主要是处理 bean 定义上的`@DependsOn` 注解。 ```java private void doRegisterBean(Class beanClass, @Nullable String name, @Nullable Class[] qualifiers, @Nullable Supplier supplier, @Nullable BeanDefinitionCustomizer[] customizers) { // ... [代码部分省略以简化] AnnotationConfigUtils.processCommonDefinitionAnnotations(abd); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.annotation.AnnotationConfigUtils#processCommonDefinitionAnnotations`方法中,主要目的是将具体的注解处理逻辑委托给另一个方法,以此来进行真正的配置工作。 ```java public static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd) { processCommonDefinitionAnnotations(abd, abd.getMetadata()); } ``` 在`org.springframework.context.annotation.AnnotationConfigUtils#processCommonDefinitionAnnotations(abd,metadata)`方法中,使用 `attributesFor(metadata, DependsOn.class)` 方法从 `metadata` 中获取 `@DependsOn` 注解的属性。这可能会返回 `AnnotationAttributes` 对象,这个对象提供了方便的方法来访问注解的属性值。如果找到了 `@DependsOn` 注解(即 `dependsOn` 不为 `null`),则从该注解中获取 `value` 属性。这个属性是一个字符串数组,代表了其他 Bean 的名称,当前 Bean 依赖于这些名称。使用 `abd.setDependsOn()` 方法设置这个 Bean 依赖的其他 Bean 名称。 ```java static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) { // ... [代码部分省略以简化] AnnotationAttributes dependsOn = attributesFor(metadata, DependsOn.class); if (dependsOn != null) { abd.setDependsOn(dependsOn.getStringArray("value")); } // ... [代码部分省略以简化] } ``` #### Bean创建源码分析 然后我们来到`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中步骤3。在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中,我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 ```java @Override public void refresh() throws BeansException, IllegalStateException { // ... [代码部分省略以简化] // 实例化所有剩余非懒加载的单列Bean对象 finishBeanFactoryInitialization(beanFactory); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization`方法中,会继续调用`DefaultListableBeanFactory`类中的`preInstantiateSingletons`方法来完成所有剩余非懒加载的单列Bean对象。 ```java protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { // ... [代码部分省略以简化] // 完成所有剩余非懒加载的单列Bean对象。 beanFactory.preInstantiateSingletons(); } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons`方法中,主要的核心目的是预先实例化所有非懒加载的单例bean。在Spring的上下文初始化完成后,该方法会被触发,以确保所有单例bean都被正确地创建并初始化。其中`getBean(beanName)`是此方法的核心操作。对于容器中定义的每一个单例bean,它都会调用`getBean`方法,这将触发bean的实例化、初始化及其依赖的注入。如果bean之前没有被创建过,那么这个调用会导致其被实例化和初始化。 ```java public void preInstantiateSingletons() throws BeansException { // ... [代码部分省略以简化] // 循环遍历所有bean的名称 for (String beanName : beanNames) { getBean(beanName); } // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#getBean()`方法中,又调用了`doGetBean`方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,`doGetBean`负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。 ```java @Override public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中,从 `BeanDefinition` 中,首先提取由 `@DependsOn` 注解定义的依赖关系,并将这些依赖存储在 `dependsOn` 字符串数组中。接着,系统会遍历当前 bean 的每一个依赖。通过使用 `isDependent(beanName, dep)` 方法,Spring 检查是否存在循环依赖。如果发现当前 bean 同时也是 `dep` bean 的依赖,那么 Spring 将抛出 `BeanCreationException`,因为这明确地表示了一个循环依赖。接着,系统使用 `registerDependentBean(dep, beanName)` 方法来通知 Spring 容器,表示 `beanName` 依赖于其他的 bean。最后,通过 `getBean(dep)` 方法,系统会尝试初始化并获取该依赖 bean 的实例。 ```java protected T doGetBean( String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException { // ... [代码部分省略以简化] RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); checkMergedBeanDefinition(mbd, beanName, args); // Guarantee initialization of beans that the current bean depends on. String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) { for (String dep : dependsOn) { if (isDependent(beanName, dep)) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'"); } registerDependentBean(dep, beanName); try { getBean(dep); } catch (NoSuchBeanDefinitionException ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", ex); } } } // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#isDependent`方法中,用于检查一个 bean 是否直接或间接地依赖于另一个 bean。 ```java protected boolean isDependent(String beanName, String dependentBeanName) { synchronized (this.dependentBeanMap) { return isDependent(beanName, dependentBeanName, null); } } ``` 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#isDependent(beanName, dependentBeanName, alreadySeen)`方法中,用于递归地检查一个 bean 是否依赖于另一个 bean。它通过跟踪直接和间接的依赖关系来实现这一点。 ```java private boolean isDependent(String beanName, String dependentBeanName, @Nullable Set alreadySeen) { // 检查bean是否已在已处理的bean集合中,如果是,则返回false,以避免无限递归 if (alreadySeen != null && alreadySeen.contains(beanName)) { return false; } // 获取bean的规范名称(可能涉及转换或别名解析) String canonicalName = canonicalName(beanName); // 从依赖映射中获取bean的直接依赖 Set dependentBeans = this.dependentBeanMap.get(canonicalName); // 如果bean没有任何直接依赖,则返回false if (dependentBeans == null) { return false; } // 如果bean的直接依赖包含目标依赖bean,则返回true if (dependentBeans.contains(dependentBeanName)) { return true; } // 递归检查bean的每一个直接依赖,看它们是否间接依赖于目标依赖bean for (String transitiveDependency : dependentBeans) { // 如果还没有创建已处理的bean集合,那么创建它 if (alreadySeen == null) { alreadySeen = new HashSet<>(); } // 将当前bean添加到已处理的bean集合中 alreadySeen.add(beanName); // 递归检查 if (isDependent(transitiveDependency, dependentBeanName, alreadySeen)) { return true; } } // 如果上述所有检查都未确认存在依赖关系,则返回false return false; } ``` 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#registerDependentBean`方法中,记录了两个 beans 之间的依赖关系,并确保了这种关系是双向的,即 A 依赖于 B,同时 B 被 A 依赖。这在解析和管理 bean 之间的复杂依赖关系时非常有用。 ```java public void registerDependentBean(String beanName, String dependentBeanName) { // 获取bean的规范名称(可能涉及转换或别名解析) String canonicalName = canonicalName(beanName); // 同步代码块,确保线程安全地更新bean的依赖映射 synchronized (this.dependentBeanMap) { // 获取或创建bean的依赖集合 Set dependentBeans = this.dependentBeanMap.computeIfAbsent(canonicalName, k -> new LinkedHashSet<>(8)); // 如果依赖bean名尚未添加,则添加;否则直接返回 if (!dependentBeans.add(dependentBeanName)) { return; } } // 同步代码块,确保线程安全地更新bean的被依赖映射(反向依赖) synchronized (this.dependenciesForBeanMap) { // 获取或创建被依赖bean的反向依赖集合 Set dependenciesForBean = this.dependenciesForBeanMap.computeIfAbsent(dependentBeanName, k -> new LinkedHashSet<>(8)); // 添加反向依赖信息 dependenciesForBean.add(canonicalName); } } ``` #### Bean销毁源码分析 在`org.springframework.context.support.AbstractApplicationContext#close`方法中,首先是启动了一个同步块,它同步在 `startupShutdownMonitor` 对象上。这确保了在给定时刻只有一个线程可以执行这个块内的代码,防止多线程导致的资源竞争或数据不一致,然后是调用了 `doClose` 方法,最后是为 JVM 注册了一个关闭钩子。 ```java @Override public void close() { synchronized (this.startupShutdownMonitor) { doClose(); // ... [代码部分省略以简化] } } ``` 在`org.springframework.context.support.AbstractApplicationContext#doClose`方法中,又调用了 `destroyBeans` 方法。 ```java protected void doClose() { // ... [代码部分省略以简化] // Destroy all cached singletons in the context's BeanFactory. destroyBeans(); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.AbstractApplicationContext#destroyBeans`方法中,首先是调用了`getBeanFactory()`返回 Spring 的 `BeanFactory` ,然后在获得的 `BeanFactory` 上,调用了 `destroySingletons` 方法,这个方法的目的是销毁所有在 `BeanFactory` 中缓存的单例 beans。 ```java protected void destroyBeans() { getBeanFactory().destroySingletons(); } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#destroySingletons`方法中,首先是调用了父类的 `destroySingletons` 方法,为了确保继承自父类的销毁逻辑得到了执行。 ```java @Override public void destroySingletons() { super.destroySingletons(); // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#destroySingletons`方法中,首先是在`disposableBeans` 字段上,从其键集合中获取所有的 bean 名称,并将它们转换为一个字符串数组。`disposableBeans` 可能包含了实现了 `DisposableBean` 接口的 beans,这些 beans 需要在容器销毁时特殊处理,最后倒序循环,从最后一个开始,销毁所有在 `disposableBeans` 列表中的 beans。这样做是为了确保依赖关系正确地处理,beans先被创建的应该后被销毁。 ```java public void destroySingletons() { // ... [代码部分省略以简化] String[] disposableBeanNames; synchronized (this.disposableBeans) { disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet()); } for (int i = disposableBeanNames.length - 1; i >= 0; i--) { destroySingleton(disposableBeanNames[i]); } // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#destroySingleton`方法中,首先是调用了父类的 `destroySingleton` 方法,为了确保继承自父类的销毁逻辑得到了执行。 ```java @Override public void destroySingleton(String beanName) { super.destroySingleton(beanName); // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#destroySingleton`方法中,首先是使用 `synchronized` 关键字在 `disposableBeans` 对象上进行同步,以确保在多线程环境中安全地访问和修改它,从 `disposableBeans` 集合中移除指定名称的 bean,并将其转换为 `DisposableBean` 类型,最后调用`destroyBean`方法执行实际的销毁操作。 ```java public void destroySingleton(String beanName) { // ... [代码部分省略以简化] destroyBean(beanName, disposableBean); } ``` 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#destroyBean`方法中, bean 从所有依赖它的其他 beans 的依赖列表中被移除,并且这个 bean 的所有已准备的依赖信息也被删除。 ```java protected void destroyBean(String beanName, @Nullable DisposableBean bean) { // ... [代码部分省略以简化] // 从其他beans的依赖中移除已销毁的bean synchronized (this.dependentBeanMap) { // 遍历存储依赖关系的map for (Iterator>> it = this.dependentBeanMap.entrySet().iterator(); it.hasNext();) { Map.Entry> entry = it.next(); // 获取当前bean的依赖列表 Set dependenciesToClean = entry.getValue(); // 从依赖列表中移除已销毁的bean dependenciesToClean.remove(beanName); // 如果当前bean的依赖列表为空,则从map中移除该条目 if (dependenciesToClean.isEmpty()) { it.remove(); } } } // 移除已销毁bean的已准备的依赖信息 this.dependenciesForBeanMap.remove(beanName); } ``` ### 七、注意事项 1. **避免循环依赖** + 确保不创建一个循环依赖的场景,即 Bean A 依赖 Bean B,同时 Bean B 又依赖 Bean A。这会导致 Spring 容器无法成功初始化这两个 beans。 2. **不要过度使用** + 只在真正需要控制初始化顺序时使用这个注解。过度使用可能使代码更难理解和维护。 3. **与构造器/属性注入结合使用** + 即使我们使用了 `@DependsOn`,如果一个 bean 需要另一个 bean 作为构造函数参数或属性,我们还是应该使用 `@Autowired` 或 XML 配置进行注入。 4. **销毁顺序** + `@DependsOn` 也会影响 beans 的销毁顺序。如果 Bean A 依赖于 Bean B,那么在销毁时,Bean A 会在 Bean B 之后被销毁。 5. **不是为运行时依赖** + 请注意,`@DependsOn` 只确保初始化顺序。如果我们的 bean 在运行时需要另一个 bean,那么我们应该考虑其他方法,如注入。 6. **与 `@Lazy` 结合使用** + 如果我们的 bean 使用了 `@Lazy` 注解(表示它会延迟初始化),同时又用 `@DependsOn` 指定了依赖,那么这可能会导致意外的初始化顺序,因为延迟初始化的 bean 可能不会按预期的顺序被初始化。 7. **组件扫描与显式声明** + 如果我们使用组件扫描(通过 `@ComponentScan`)并且在类级别使用了 `@DependsOn`,那么这个注解会生效。但如果通过 XML 定义了该 bean,并且还在类上使用了 `@DependsOn`,那么注解会被忽略,我们应该使用 XML 的 `depends-on` 属性来声明依赖。 8. **不适用于 `@Bean` 方法的参数** + 如果我们在 Java 配置类中使用 `@Bean` 方法定义 beans,并尝试通过方法参数注入依赖,那么 `@DependsOn` 不会对这些依赖产生影响,因为方法参数自然地声明了初始化顺序。 ### 八、总结 #### 最佳实践总结 1. **启动类设置** + 我们使用 `AnnotationConfigApplicationContext` 来启动 Spring 容器,并指定了 `MyConfiguration` 作为配置类。当程序运行完毕,我们关闭了该容器。 2. **配置类与依赖声明** + 在 `MyConfiguration` 配置类中,我们使用 `@Bean` 注解定义了三个 bean:`BeanA`, `BeanB`, 和 `BeanC`。通过 `@DependsOn` 注解,我们明确地指定了它们之间的依赖关系,确保 `BeanA` 依赖于 `BeanB` 的初始化,而 `BeanB` 依赖于 `BeanC` 的初始化。 3. **Bean的声明与销毁逻辑** + 每个 bean 都实现了 `DisposableBean` 接口。在各自的构造函数中,它们打印一个消息表示它们已经被初始化,而在 `destroy` 方法中,它们打印一个消息表示它们已经被销毁。 4. **结果与结论** + 我们运行程序时,初始化的顺序遵循了我们通过 `@DependsOn` 注解定义的依赖关系。同样地,销毁的顺序与初始化顺序相反,这确保了所有的依赖都在被依赖的 bean 之前被销毁。 #### 源码分析总结 1. **启动和注册** + 使用 `AnnotationConfigApplicationContext` 启动 Spring 容器,并将配置类注册到 Spring 上下文中。 2. **Bean 注册分析** - 在 `AnnotationConfigApplicationContext` 构造函数中,执行了注册和启动容器的两个关键步骤。 - `register` 方法允许我们将组件类(如使用 `@Component` 或 `@Configuration` 注解的类)注册到 Spring 容器。 - `AnnotatedBeanDefinitionReader` 负责注册这些类,然后在 `doRegisterBean` 方法中为给定的 `beanClass` 创建一个 bean 定义并配置它。 3. **处理 @DependsOn 注解** - 在 bean 的定义过程中,Spring 将解析 `@DependsOn` 注解并存储其依赖关系。 - 这些关系将在后面的 bean 生命周期中使用,以确保按正确的顺序创建和销毁 beans。 4. **Bean 创建分析** - 在容器启动过程的 `refresh` 方法中,会实例化所有的单例 beans。 - `preInstantiateSingletons` 方法会触发所有非懒加载单例 beans 的创建过程。 - 如果一个 bean 通过 `@DependsOn` 指定了依赖,这些依赖会首先被初始化。 5. **Bean 销毁分析** - 在容器关闭时,会调用 `destroySingletons` 方法来销毁所有缓存的单例 beans。 - Beans 的销毁顺序与其创建顺序相反,以确保所有依赖项在销毁过程中得到正确的处理。 6. **处理循环依赖** - Spring 会检查 `@DependsOn` 指定的依赖是否导致了循环依赖,如果是这种情况,Spring 会抛出异常。 ================================================ FILE: spring-annotation/spring-annotation-dependsOn/pom.xml ================================================ spring-annotation com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-annotation-dependsOn ================================================ FILE: spring-annotation/spring-annotation-dependsOn/src/main/java/com/xcs/spring/DependsOnApplication.java ================================================ package com.xcs.spring; import com.xcs.spring.config.MyConfiguration; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年08月07日 16时21分 **/ public class DependsOnApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); context.close(); } } ================================================ FILE: spring-annotation/spring-annotation-dependsOn/src/main/java/com/xcs/spring/bean/BeanA.java ================================================ package com.xcs.spring.bean; import org.springframework.beans.factory.DisposableBean; /** * @author xcs * @date 2023年10月09日 16时45分 **/ public class BeanA implements DisposableBean { public BeanA() { System.out.println("BeanA Initialized"); } @Override public void destroy() throws Exception { System.out.println("BeanA Destroyed"); } } ================================================ FILE: spring-annotation/spring-annotation-dependsOn/src/main/java/com/xcs/spring/bean/BeanB.java ================================================ package com.xcs.spring.bean; import org.springframework.beans.factory.DisposableBean; /** * @author xcs * @date 2023年10月09日 16时46分 **/ public class BeanB implements DisposableBean { public BeanB() { System.out.println("BeanB Initialized"); } @Override public void destroy() throws Exception { System.out.println("BeanB Destroyed"); } } ================================================ FILE: spring-annotation/spring-annotation-dependsOn/src/main/java/com/xcs/spring/bean/BeanC.java ================================================ package com.xcs.spring.bean; import org.springframework.beans.factory.DisposableBean; /** * @author xcs * @date 2023年10月09日 16时46分 **/ public class BeanC implements DisposableBean { public BeanC() { System.out.println("BeanC Initialized"); } @Override public void destroy() throws Exception { System.out.println("BeanC Destroyed"); } } ================================================ FILE: spring-annotation/spring-annotation-dependsOn/src/main/java/com/xcs/spring/config/MyConfiguration.java ================================================ package com.xcs.spring.config; import com.xcs.spring.bean.BeanA; import com.xcs.spring.bean.BeanB; import com.xcs.spring.bean.BeanC; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.DependsOn; /** * @author xcs * @date 2023年08月07日 16时25分 **/ @Configuration public class MyConfiguration { @Bean @DependsOn("beanB") public BeanA beanA() { return new BeanA(); } @Bean @DependsOn("beanC") public BeanB beanB() { return new BeanB(); } @Bean public BeanC beanC() { return new BeanC(); } } ================================================ FILE: spring-annotation/spring-annotation-import/README.md ================================================ ## @Import - [@Import](#import) - [一、基本信息](#一基本信息) - [二、注解描述](#二注解描述) - [三、注解源码](#三注解源码) - [四、主要功能](#四主要功能) - [五、最佳实践](#五最佳实践) - [六、时序图](#六时序图) - [七、源码分析](#七源码分析) - [八、注意事项](#八注意事项) - [九、总结](#九总结) - [最佳实践总结](#最佳实践总结) - [源码分析总结](#源码分析总结) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [我的CSDN](https://blog.csdn.net/duzhuang2399/article/details/132806548) 📚 **文章目录** - [所有文章](https://github.com/xuchengsheng/spring-reading) 🔗 **源码地址** - [@Import源码](https://github.com/xuchengsheng/spring-reading/tree/master/spring-annotation/spring-annotation-import) ### 二、注解描述 `@Import` 是 Spring 框架的核心注解,用于导入配置类或组件到当前的 Spring 上下文中。它可以用于导入常规的 `@Configuration` 类、常规组件类,或实现了 `ImportSelector` 和 `ImportBeanDefinitionRegistrar` 接口的类。`ImportSelector` 允许根据条件动态地选择要导入的组件,而 `ImportBeanDefinitionRegistrar` 提供了一种以编程方式注册bean的方法。使用 `@Import` 注解,我们可以更灵活、模块化地组织 Spring 的配置,确保上下文中有所需的所有组件和配置。 ### 三、注解源码 `@Import` 是 Spring 框架自 3.0 版本开始引入的一个核心注解。允许我们导入一个或多个组件类,这些类通常是 `@Configuration` 类。它在功能上相当于 Spring XML 中的 `` 元素,导入类型`@Configuration`类、`ImportSelector`、`ImportBeanDefinitionRegistrar`的实现以及其他常规组件类,在导入的 `@Configuration` 类中声明的 bean 定义应使用 `@Autowired` 进行注入。 ```java /** * 表示要导入的一个或多个组件类 —— 通常是 * Configuration @Configuration 类。 * * 提供与Spring XML中的 元素相同的功能。 * 允许导入 @Configuration 类、ImportSelector 和 * ImportBeanDefinitionRegistrar 的实现,以及常规组件 * 类 (从 4.2 开始;与 AnnotationConfigApplicationContext#register 相似)。 * * 在导入的 @Configuration 类中声明的 @Bean 定义应通过 * org.springframework.beans.factory.annotation.Autowired @Autowired 注入。 * 可以自动注入bean本身,也可以自动注入声明bean的配置类实例。 * 后者允许在 @Configuration 类方法之间进行明确的、IDE友好的导航。 * * 可以在类级别声明或作为元注解。 * * 如果需要导入XML或其他非-@Configuration 的bean定义资源, * 请改用 ImportResource @ImportResource 注解。 * * @author Chris Beams * @author Juergen Hoeller * @since 3.0 * @see Configuration * @see ImportSelector * @see ImportBeanDefinitionRegistrar * @see ImportResource */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Import { /** * 要导入的 Configuration @Configuration、ImportSelector、 * ImportBeanDefinitionRegistrar 或常规组件类。 */ Class[] value(); } ``` ### 四、主要功能 1. **导入配置类** + 允许一个 `@Configuration` 类引入另一个 `@Configuration` 类。 2. **导入选择器** + 通过实现 `ImportSelector` 接口,可以动态地选择和导入配置类。 3. **手动注册Bean** + 通过实现 `ImportBeanDefinitionRegistrar` 接口,可以在运行时手动注册 bean。 4. **导入常规组件类** + 从 Spring 4.2 开始,还可以导入常规的组件类。 ### 五、最佳实践 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类,然后遍历并打印所有的bean定义名。 ```java public class ImportApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); for (String beanDefinitionName : context.getBeanDefinitionNames()) { System.out.println("beanName = " + beanDefinitionName); } } } ``` 使用`@Import` 注解允许导入其他组件或配置到当前的配置类。在 `MyConfiguration` 类中,它导入了四个不同的组件或选择器,第一个是`MyBean.class`一个常规Bean组件。第二个是`MyImportSelector.class`一个实现了 `ImportSelector` 的类,用于动态选择并导入配置。第三个是`MyDeferredImportSelector.class`一个实现了 `DeferredImportSelector` 的类,用于延迟地选择并导入配置。第四个是`MyImportBeanDefinitionRegistrar.class`一个实现了 `ImportBeanDefinitionRegistrar` 的类,用于手动注册bean。 ```java @Configuration @Import({MyBean.class, MyImportSelector.class, MyDeferredImportSelector.class, MyImportBeanDefinitionRegistrar.class}) public class MyConfiguration { } ``` `MyImportSelector` 类提供了一种动态导入 `MyBeanA` 组件的机制。确保 `MyBeanA` 被加入到Spring的上下文中。 ```java public class MyImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[]{MyBeanA.class.getName()}; } } ``` `MyDeferredImportSelector` 类提供了一种延迟导入 `MyBeanB` 组件的机制,确保 `MyBeanB` 被添加到Spring的上下文中。与普通的 `ImportSelector` 不同,`DeferredImportSelector` 允许在Spring处理完所有其他配置类之后再进行导入,从而确保某些特定的处理顺序。 ```java public class MyDeferredImportSelector implements DeferredImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[]{MyBeanB.class.getName()}; } } ``` `MyImportBeanDefinitionRegistrar` 类提供手动注册 `MyBeanC` 组件到Spring容器的方法,而不依赖于组件扫描或其他自动配置机制。确保 `MyBeanC` 被添加到Spring的上下文中。 ```java public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { RootBeanDefinition beanDefinition = new RootBeanDefinition(MyBeanC.class); registry.registerBeanDefinition(MyBeanC.class.getName(), beanDefinition); } } ``` 使用`@Import`注解和其相关的选择器或注册器来将这些bean类导入到Spring上下文中 ```java public class MyBean { } public class MyBeanA { } public class MyBeanB { } public class MyBeanC { } ``` ### 六、时序图 ~~~mermaid sequenceDiagram participant ImportApplication participant AnnotationConfigApplicationContext participant AbstractApplicationContext participant PostProcessorRegistrationDelegate participant ConfigurationClassPostProcessor participant ConfigurationClassParser participant DeferredImportSelectorHandler participant DeferredImportSelectorGroupingHandler participant DeferredImportSelectorGrouping participant DefaultDeferredImportSelectorGroup participant ConfigurationClassBeanDefinitionReader participant MyImportSelector participant MyDeferredImportSelector participant MyImportBeanDefinitionRegistrar participant DefaultListableBeanFactory ImportApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)
初始化上下文 AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()
刷新上下文 AbstractApplicationContext->>AbstractApplicationContext:invokeBeanFactoryPostProcessors(beanFactory)
调用BeanFactory的后处理器 AbstractApplicationContext->>PostProcessorRegistrationDelegate:invokeBeanFactoryPostProcessors(beanFactory,beanFactoryPostProcessors)
委托调用BeanFactory的后处理器 PostProcessorRegistrationDelegate->>PostProcessorRegistrationDelegate:invokeBeanDefinitionRegistryPostProcessors(postProcessors,registry,applicationStartup)
执行BeanDefinition的注册后处理器 PostProcessorRegistrationDelegate->>ConfigurationClassPostProcessor:postProcessBeanDefinitionRegistry(registry)
处理配置类 ConfigurationClassPostProcessor->>ConfigurationClassPostProcessor:processConfigBeanDefinitions(registry)
处理配置类bean的定义 ConfigurationClassPostProcessor->>ConfigurationClassParser:new ConfigurationClassParser()
创建配置类解析器 ConfigurationClassParser-->>ConfigurationClassPostProcessor:返回parser ConfigurationClassPostProcessor->>ConfigurationClassParser:parser.parse(candidates)
解析候选类 ConfigurationClassParser->>ConfigurationClassParser:parse(metadata,beanName)
进一步解析类元数据 ConfigurationClassParser->>ConfigurationClassParser:processConfigurationClass(configClass,filter)
处理@Configuration类 ConfigurationClassParser->>+ConfigurationClassParser:doProcessConfigurationClass(configClass, sourceClass, filter)
实际处理配置类 ConfigurationClassParser-->>-ConfigurationClassParser:返回SourceClass ConfigurationClassParser->>+ConfigurationClassParser:processImports(configClass, sourceClass, importCandidates, filter, true)
处理导入 ConfigurationClassParser->>MyImportSelector:selectImports(importingClassMetadata)
调用自定义的导入选择器 MyImportSelector-->>ConfigurationClassParser:返回Bean数组 ConfigurationClassParser->>DeferredImportSelectorHandler:process()
处理延迟导入选择器 DeferredImportSelectorHandler->>DeferredImportSelectorGroupingHandler:processGroupImports()
处理组导入 DeferredImportSelectorGroupingHandler->>DeferredImportSelectorGrouping:getImports()
获取导入 DeferredImportSelectorGrouping->>DefaultDeferredImportSelectorGroup:process(metadata,selector)
处理默认延迟导入选择器的组 DefaultDeferredImportSelectorGroup->>MyDeferredImportSelector:selectImports(importingClassMetadata)
调用自定义的导入选择器 MyDeferredImportSelector-->>DefaultDeferredImportSelectorGroup:返回Bean数组,存储在imports字段中 DeferredImportSelectorGrouping->>DefaultDeferredImportSelectorGroup:selectImports()
选择导入 DeferredImportSelectorGrouping-->>DeferredImportSelectorGroupingHandler:返回Iterable DeferredImportSelectorGroupingHandler->>ConfigurationClassParser:processImports(configClass, sourceClass, importCandidates, filter, true)
再次处理导入 ConfigurationClassParser-->>-ConfigurationClassParser:递归处理@Configuration ConfigurationClassPostProcessor->>ConfigurationClassBeanDefinitionReader:loadBeanDefinitions(configClasses)
加载bean定义 ConfigurationClassBeanDefinitionReader->>ConfigurationClassBeanDefinitionReader:loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator)
加载配置类的bean定义 ConfigurationClassBeanDefinitionReader->>ConfigurationClassBeanDefinitionReader:registerBeanDefinitionForImportedConfigurationClass(configClass)
注册导入的配置类的bean定义 ConfigurationClassBeanDefinitionReader->>DefaultListableBeanFactory:registerBeanDefinition(beanName,beanDefinition)
在bean工厂中注册bean定义 ConfigurationClassBeanDefinitionReader->>ConfigurationClassBeanDefinitionReader:loadBeanDefinitionsFromRegistrars(registrars)
从注册器中加载bean定义 ConfigurationClassBeanDefinitionReader->>MyImportBeanDefinitionRegistrar:registerBeanDefinitions(importingClassMetadata,registry)
调用自定义的bean定义注册器 MyImportBeanDefinitionRegistrar->>DefaultListableBeanFactory:registerBeanDefinition(beanName,beanDefinition)
在bean工厂中注册bean定义 ~~~ ### 七、源码分析 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类,然后遍历并打印所有的bean定义名。 ```java public class ImportApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); for (String beanDefinitionName : context.getBeanDefinitionNames()) { System.out.println("beanName = " + beanDefinitionName); } } } ``` 在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法。 ```java public AnnotationConfigApplicationContext(Class... componentClasses) { this(); register(componentClasses); refresh(); } ``` 在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 ```java @Override public void refresh() throws BeansException, IllegalStateException { // ... [代码部分省略以简化] // 调用在上下文中注册为bean的工厂处理器 invokeBeanFactoryPostProcessors(beanFactory); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors`方法中,又委托了`PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()`进行调用。 ```java protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) { PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors()); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors`方法中,首先调用了 `BeanDefinitionRegistryPostProcessor`(这是 `BeanFactoryPostProcessor` 的子接口)。它专门用来在所有其他 bean 定义加载之前修改默认的 bean 定义。 ```java public static void invokeBeanFactoryPostProcessors( ConfigurableListableBeanFactory beanFactory, List beanFactoryPostProcessors) { // ... [代码部分省略以简化] invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup()); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanDefinitionRegistryPostProcessors`方法中,循环调用了实现`BeanDefinitionRegistryPostProcessor`接口中的`postProcessBeanDefinitionRegistry(registry)`方法 ```java private static void invokeBeanDefinitionRegistryPostProcessors( Collection postProcessors, BeanDefinitionRegistry registry, ApplicationStartup applicationStartup) { for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) { StartupStep postProcessBeanDefRegistry = applicationStartup.start("spring.context.beandef-registry.post-process") .tag("postProcessor", postProcessor::toString); postProcessor.postProcessBeanDefinitionRegistry(registry); postProcessBeanDefRegistry.end(); } } ``` 在`org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry`方法中,调用了`processConfigBeanDefinitions`方法,该方法的主要目的是处理和注册配置类中定义的beans。 ```java @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { // ... [代码部分省略以简化] processConfigBeanDefinitions(registry); } ``` 在`org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions`方法中,这个方法主要处理了配置类的解析和验证,并确保了所有在配置类中定义的beans都被正确地注册到Spring的bean定义注册表中。 ```java public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { // ... [代码部分省略以简化] // 步骤1:创建一个用于解析配置类的解析器 ConfigurationClassParser parser = new ConfigurationClassParser( this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry); // 步骤2:初始化候选配置类集合以及已解析配置类集合 Set candidates = new LinkedHashSet<>(configCandidates); Set alreadyParsed = new HashSet<>(configCandidates.size()); // 步骤3:循环处理所有候选配置类,直至没有候选类为止 do { // 步骤3.1 解析配置类 parser.parse(candidates); // 步骤3.2 验证配置类 parser.validate(); // 获取解析后的配置类,并从中移除已经处理过的 Set configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); configClasses.removeAll(alreadyParsed); // 步骤4:如果reader为空,则创建一个新的Bean定义读取器 if (this.reader == null) { this.reader = new ConfigurationClassBeanDefinitionReader( registry, this.sourceExtractor, this.resourceLoader, this.environment, this.importBeanNameGenerator, parser.getImportRegistry()); } // 步骤5:使用读取器为解析的配置类加载Bean定义 this.reader.loadBeanDefinitions(configClasses); // ... [代码部分省略以简化] } while (!candidates.isEmpty()); // ... [代码部分省略以简化] } ``` 我们来到`org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions`方法中的步骤3.1。在`org.springframework.context.annotation.ConfigurationClassParser#parse`方法中,主要是遍历所有的配置类候选者,并对每一个带有注解的Bean定义进行解析。这通常涉及到查找该配置类中的@Bean方法、组件扫描指令等,并将这些信息注册到Spring容器中。 ```java public void parse(Set configCandidates) { // 遍历每个配置类的持有者 for (BeanDefinitionHolder holder : configCandidates) { BeanDefinition bd = holder.getBeanDefinition(); try { // 步骤1:如果Bean定义是AnnotatedBeanDefinition的实例,则获取其注解元数据并解析 if (bd instanceof AnnotatedBeanDefinition) { parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName()); } // 步骤2:如果Bean定义是AbstractBeanDefinition并且已经有关联的Bean类,则直接解析该Bean类 else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) { parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName()); } // 步骤3:如果上述情况都不符合,则直接使用Bean的类名进行解析 else { parse(bd.getBeanClassName(), holder.getBeanName()); } } // 如果在解析过程中捕获到BeanDefinitionStoreException,则直接抛出 catch (BeanDefinitionStoreException ex) { throw ex; } // 对于其他类型的异常,封装并抛出一个新的BeanDefinitionStoreException catch (Throwable ex) { // ... [代码部分省略以简化] } } // 步骤4:在所有配置类都被解析后,处理所有延迟的导入选择器 this.deferredImportSelectorHandler.process(); } ``` 我们来到`org.springframework.context.annotation.ConfigurationClassParser#parse`方法的第一步,在`org.springframework.context.annotation.ConfigurationClassParser#parse(metadata, beanName)`方法中,将注解元数据和Bean名称转化为一个配置类,然后对其进行处理。处理配置类是Spring配置驱动的核心,它涉及到许多关键操作,如解析`@Bean`方法、处理`@ComponentScan`注解、处理`@Import`注解等。 ```java protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException { processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER); } ``` 在`org.springframework.context.annotation.ConfigurationClassParser#processConfigurationClass`方法中,处理一个给定的配置类。它首先递归地处理配置类及其父类,以确保所有相关的配置都被正确地读取并解析。在递归处理完所有相关配置后,它将配置类添加到已解析的配置类的映射中。 ```java protected void processConfigurationClass(ConfigurationClass configClass, Predicate filter) throws IOException { // ... [代码部分省略以简化] // 步骤1:递归地处理配置类及其超类层次结构 SourceClass sourceClass = asSourceClass(configClass, filter); do { sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter); } while (sourceClass != null); // 步骤2:将处理后的配置类放入映射中 this.configurationClasses.put(configClass, configClass); } ``` 在`org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass`方法中,其中主要目的是处理给定`ConfigurationClass`中的`@Import`注解。在处理完所有的导入之后,它完成了任务并返回null。 ```java @Nullable protected final SourceClass doProcessConfigurationClass( ConfigurationClass configClass, SourceClass sourceClass, Predicate filter) throws IOException { // ... [代码部分省略以简化] // 处理 sourceClass 上的所有 @Import 注解 processImports(configClass, sourceClass, getImports(sourceClass), filter, true); // ... [代码部分省略以简化] // 当前实现中,直接返回null,意味着没有超类需要进一步处理 return null; } ``` 在`org.springframework.context.annotation.ConfigurationClassParser#processImports`方法中,主要分为三个步骤。第一步是对于`ImportSelector`,`processImports`方法主要完成了动态选择和导入类的功能,使得Spring配置更加灵活和模块化。第二步是`ImportBeanDefinitionRegistrar`提供了一个在bean定义注册过程中的插入点,允许动态、条件性配置。`processImports`方法确保这些逻辑在处理`@Import`时正确执行。第三步是当遇到一个不是`ImportSelector`或`ImportBeanDefinitionRegistrar`的类时,直接视其为一个普通的Spring组件或配置类,并按照常规的处理流程进行。这确保了所有通过`@Import`导入的类,不论它们的类型如何,都会被正确地注册和处理。 ```java private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass, Collection importCandidates, Predicate exclusionFilter, boolean checkForCircularImports) { // 如果没有要导入的候选类,直接返回 if (importCandidates.isEmpty()) { return; } // 如果设置了检查循环导入,并且当前的配置类在导入堆栈中形成了一个链,报告循环导入问题 if (checkForCircularImports && isChainedImportOnStack(configClass)) { this.problemReporter.error(new CircularImportProblem(configClass, this.importStack)); } else { // 将当前的配置类推入导入堆栈中 this.importStack.push(configClass); try { for (SourceClass candidate : importCandidates) { // 步骤1:判断候选类是否实现了 ImportSelector 接口 if (candidate.isAssignable(ImportSelector.class)) { // 加载当前的候选类 Class candidateClass = candidate.loadClass(); // 通过工具方法实例化 ImportSelector 接口的实现类 ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class, this.environment, this.resourceLoader, this.registry); // 获取从 ImportSelector 指定的排除过滤器 Predicate selectorFilter = selector.getExclusionFilter(); // 如果选择器提供了额外的排除过滤器,则合并当前的排除过滤器 if (selectorFilter != null) { exclusionFilter = exclusionFilter.or(selectorFilter); } // 步骤1.1:检查该选择器是否是 DeferredImportSelector 的实例 if (selector instanceof DeferredImportSelector) { // 如果是 DeferredImportSelector,使用特定的处理器来处理它 this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector); } else { // 步骤1.2: 如果不是 DeferredImportSelector,则使用选择器来选择要导入的类 String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); // 将这些类名转化为 SourceClass 集合 Collection importSourceClasses = asSourceClasses(importClassNames, exclusionFilter); // 递归地处理这些要导入的类 processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false); } } // 步骤2:检查当前候选类是否实现了 ImportBeanDefinitionRegistrar 接口 else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) { // 加载当前的候选类 Class candidateClass = candidate.loadClass(); // 通过工具方法实例化 ImportBeanDefinitionRegistrar 接口的实现类 ImportBeanDefinitionRegistrar registrar = ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class, this.environment, this.resourceLoader, this.registry); // 将实例化的 registrar 添加到当前的配置类中 configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata()); } // 步骤3:如果候选类既不是 ImportSelector 也不是 ImportBeanDefinitionRegistrar else { // 在导入堆栈中为当前的源类注册导入的类 this.importStack.registerImport( currentSourceClass.getMetadata(), candidate.getMetadata().getClassName()); // 将候选类处理为一个配置类,并递归地处理它 processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter); } } } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Throwable ex) { // ... [代码部分省略以简化] } finally { // 在处理完成后,从导入堆栈中弹出当前配置类 this.importStack.pop(); } } } ``` 在`org.springframework.context.annotation.ConfigurationClassParser#processImports`方法中的步骤1.2中,最后调用到我们自定义的实现逻辑中来,`selectImports`方法始终返回`MyBeanA`类的完全限定名,表示当这个`ImportSelector`被处理时,`MyBeanA`类将被导入到Spring应用上下文中。 ```java public class MyImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[]{MyBeanA.class.getName()}; } } ``` 我们回到`org.springframework.context.annotation.ConfigurationClassParser#parse`方法的第四步,在`org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorHandler#process`方法中,首先获取当前待处理的延迟导入选择器,然后使用`DeferredImportSelectorGroupingHandler`来按组处理它们,最后确保再次初始化延迟导入选择器列表,为下一次处理做准备。 ```java /** * 处理所有注册的延迟导入选择器。 */ public void process() { // 获取当前的延迟导入选择器列表 List deferredImports = this.deferredImportSelectors; // 将当前的延迟导入选择器列表设为null,表示它们即将被处理 this.deferredImportSelectors = null; try { // 如果存在待处理的延迟导入选择器 if (deferredImports != null) { // 创建一个新的分组处理器,用于处理延迟导入选择器的分组逻辑 DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler(); // 对延迟导入选择器进行排序,确保按预期的顺序进行处理 deferredImports.sort(DEFERRED_IMPORT_COMPARATOR); // 遍历每个延迟导入选择器,使用分组处理器进行注册 deferredImports.forEach(handler::register); // 使用分组处理器处理分组的导入 handler.processGroupImports(); } } // 确保在方法结束时,无论是否发生异常,都将延迟导入选择器列表重新初始化为一个新的空列表 finally { this.deferredImportSelectors = new ArrayList<>(); } } ``` 在`org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorGroupingHandler#processGroupImports`方法中,遍历每个已注册的延迟导入选择器分组,并对每个分组中的每个导入条目进行处理。处理的内容包括递归地处理所有相关的`@Import`和`@Bean`定义。 ```java /** * 处理每个分组中的延迟导入选择器。 */ public void processGroupImports() { // 遍历每个已注册的延迟导入选择器分组 for (DeferredImportSelectorGrouping grouping : this.groupings.values()) { // 获取分组的候选类排除过滤器 Predicate exclusionFilter = grouping.getCandidateFilter(); // 遍历分组中的每个导入选择器 grouping.getImports().forEach(entry -> { // 获取与当前导入条目关联的配置类 ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata()); try { // 对每个导入条目进行处理,包括递归地处理所有相关的@Import和@Bean定义 processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter), Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)), exclusionFilter, false); } // 如果在处理过程中捕获到BeanDefinitionStoreException,则直接抛出 catch (BeanDefinitionStoreException ex) { throw ex; } // 对于其他类型的异常,处理相关逻辑(此处简化,不展开详细的异常处理逻辑) catch (Throwable ex) { // ... [代码部分省略以简化] } }); } } ``` 在`org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorGrouping#getImports`方法中,遍历每个已注册的延迟导入选择器,将它们的相关信息传递给分组进行处理,然后最终使用分组实例来选择并返回要导入的类。 ```java public Iterable getImports() { // 遍历每个已注册的延迟导入选择器 for (DeferredImportSelectorHolder deferredImport : this.deferredImports) { // 对于每个延迟导入选择器,将其关联的配置类的元数据和其选择器传递给分组进行处理 this.group.process(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getImportSelector()); } // 使用分组实例来选择并返回要导入的类 return this.group.selectImports(); } ``` 在`org.springframework.context.annotation.ConfigurationClassParser.DefaultDeferredImportSelectorGroup#process`方法中,主要给传入的`DeferredImportSelector`,找出它选择导入的所有类,并将这些类与提供的注解元数据一起存储在一个列表中,以供以后使用。 ```java @Override public void process(AnnotationMetadata metadata, DeferredImportSelector selector) { // 使用选择器和提供的元数据选择要导入的类 for (String importClassName : selector.selectImports(metadata)) { // 对于每个选定的导入类,创建一个新的Entry对象并将其添加到当前分组的导入列表中 this.imports.add(new Entry(metadata, importClassName)); } } ``` 最后调用到我们自定义的实现逻辑中来,`selectImports`方法始终返回`MyBeanB`类的完全限定名,表示当这个`DeferredImportSelector`被处理时,`MyBeanB`类将被导入到Spring应用上下文中。 ```java public class MyDeferredImportSelector implements DeferredImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[]{MyBeanB.class.getName()}; } } ``` 我们来到`org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions`方法中的步骤5。在`org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitions`方法中,遍历给定的一组`ConfigurationClass`对象(这些对象代表的是`@Configuration`注解的类),并为每个类加载其相关的Bean定义。 ```java public void loadBeanDefinitions(Set configurationModel) { TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator(); for (ConfigurationClass configClass : configurationModel) { loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator); } } ``` 在`org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass`方法中,如果配置类是通过`@Import`注解或其他机制导入的,则该方法会为其注册一个Bean定义,另外该方法会处理与`ConfigurationClass`关联的所有`ImportBeanDefinitionRegistrar`。这些注册器允许在解析配置类时以编程方式动态地添加额外的Bean定义。 ```java private void loadBeanDefinitionsForConfigurationClass( ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) { // ... [可能的初始化或其他预处理] // 步骤1:如果这个配置类是由于@Import或其他机制而被导入的,那么为它注册一个Bean定义。 // 这确保了导入的@Configuration类也被视为一个Bean,并可以在ApplicationContext中被检索。 if (configClass.isImported()) { registerBeanDefinitionForImportedConfigurationClass(configClass); } // ... [可能的其他处理或Bean定义加载] // 步骤2:从当前配置类关联的ImportBeanDefinitionRegistrars加载Bean定义。 // ImportBeanDefinitionRegistrar允许我们在解析@Configuration类时进行编程式地注册额外的Bean定义。 loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars()); } ``` 我们来到`org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass`方法中步骤1,在`org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#registerBeanDefinitionForImportedConfigurationClass`方法中,首先,它根据配置类的注解元数据创建一个Bean定义。然后,它确定并设置Bean的作用域,例如单例或原型。为Bean定义生成一个唯一的名称后,该方法处理了其上的常见注解,例如`@Lazy`和`@Primary`。如果Bean的作用域如请求或会话需要代理,会应用相应的代理。最后,它将Bean定义注册到Spring容器,并将生成的名称与原始配置类关联。这确保了导入的配置类在Spring中也可以作为一个常规Bean进行访问或注入。 ```java /** * 为导入的ConfigurationClass注册Bean定义。 * * @param configClass 要处理的ConfigurationClass */ private void registerBeanDefinitionForImportedConfigurationClass(ConfigurationClass configClass) { // 获取configClass的元数据 AnnotationMetadata metadata = configClass.getMetadata(); // 创建基于元数据的Bean定义 AnnotatedGenericBeanDefinition configBeanDef = new AnnotatedGenericBeanDefinition(metadata); // 解析Bean的作用域(例如:singleton, prototype) ScopeMetadata scopeMetadata = scopeMetadataResolver.resolveScopeMetadata(configBeanDef); configBeanDef.setScope(scopeMetadata.getScopeName()); // 为configBeanDef生成Bean名称 String configBeanName = this.importBeanNameGenerator.generateBeanName(configBeanDef, this.registry); // 处理常见的注解定义(例如:@Lazy, @Primary) AnnotationConfigUtils.processCommonDefinitionAnnotations(configBeanDef, metadata); // 将Bean定义封装在BeanDefinitionHolder中,以携带其他配置元数据 BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(configBeanDef, configBeanName); // 根据需要应用作用域代理模式(例如,对于"request"和"session"作用域的Beans) definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); // 在Bean定义注册表中注册Bean定义 this.registry.registerBeanDefinition(definitionHolder.getBeanName(), definitionHolder.getBeanDefinition()); // 将生成的Bean名称设置到configClass中 configClass.setBeanName(configBeanName); // 如果日志级别为TRACE,记录一条关于注册的消息 if (logger.isTraceEnabled()) { logger.trace("Registered bean definition for imported class '" + configBeanName + "'"); } } ``` 我们来到`org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass`方法中步骤2,每个`ImportBeanDefinitionRegistrar`都有机会基于其自定义逻辑在Spring容器中注册额外的Bean定义。 ```java private void loadBeanDefinitionsFromRegistrars(Map registrars) { registrars.forEach((registrar, metadata) -> registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator)); } ``` 最后调用到我们自定义的实现逻辑中来,`MyImportBeanDefinitionRegistrar` 类提供手动注册 `MyBeanC` 组件到Spring容器的方法,而不依赖于组件扫描或其他自动配置机制。确保 `MyBeanC` 被添加到Spring的上下文中。 ```java public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { RootBeanDefinition beanDefinition = new RootBeanDefinition(MyBeanC.class); registry.registerBeanDefinition(MyBeanC.class.getName(), beanDefinition); } } ``` ### 八、注意事项 1. **避免循环引用** + 确保我们没有创建循环引用,即一个配置类导入另一个配置类,而后者又反过来导入前者。 2. **与`@ComponentScan`的关系** + `@Import`和`@ComponentScan`都可以用于注册bean,但是它们的用途稍有不同。`@ComponentScan`用于自动扫描和注册bean,而`@Import`用于明确地导入其他配置类。 3. **属性覆盖** + 如果从多个配置类中导入相同的bean定义,并设置了不同的属性或值,那么后导入的bean定义将覆盖先导入的bean定义。 4. **与`@Profile`的关系** + 可以结合使用`@Import`和`@Profile`,这样只有在特定的激活配置文件中才会导入某个配置类。 ### 九、总结 #### 最佳实践总结 1. **启动类** + 通过`ImportApplication`类,我们初始化了Spring的上下文环境,选择了`AnnotationConfigApplicationContext`,这是Spring提供的使用Java注解配置方式。在该上下文中,我们指定了`MyConfiguration`作为主要的配置类,并遍历了上下文中所有已注册的bean名称。 2. **主配置类** + `MyConfiguration`类通过使用`@Import`注解导入了四种不同类型的组件或选择器: - `MyBean`:一个普通的Java类。 - `MyImportSelector`:一个实现了`ImportSelector`接口的类。 - `MyDeferredImportSelector`:一个实现了`DeferredImportSelector`接口的类。 - `MyImportBeanDefinitionRegistrar`:一个实现了`ImportBeanDefinitionRegistrar`接口的类。 3. **选择器与注册器**: - `MyImportSelector`通过实现`ImportSelector`接口,动态地选择并导入了`MyBeanA`类。 - `MyDeferredImportSelector`作为`DeferredImportSelector`的实现,延迟地选择并导入了`MyBeanB`类。与`ImportSelector`不同,它允许在所有其他配置类被处理后再进行导入。 - `MyImportBeanDefinitionRegistrar`提供了手动注册bean的功能。在这个示例中,它将`MyBeanC`手动注册到了Spring上下文中。 4. **组件定义** + 在我们最佳实践中包含四个Java类,分别为`MyBean`、`MyBeanA`、`MyBeanB`和`MyBeanC`,它们都被上述的选择器或注册器选择并导入到Spring上下文中。 #### 源码分析总结 1. **启动阶段** + 在Spring启动时,会使用`AnnotationConfigApplicationContext`来加载和解析配置类,这个配置类可能包含有`@Import`注解。 2. **解析配置类** + 在`AnnotationConfigApplicationContext`的构造函数中,通过`refresh()`方法开始刷新容器并解析Bean定义。进一步,在`AbstractApplicationContext#refresh`方法中,会调用`invokeBeanFactoryPostProcessors`方法来处理在上下文中注册为bean的工厂处理器。 3. **处理`@Import`注解** + 处理过程中会特别关注`BeanDefinitionRegistryPostProcessor`,这是`BeanFactoryPostProcessor`的子接口,专门用于在所有其他bean定义加载之前修改默认的bean定义。然后,系统解析配置类并验证其内容,这主要涉及查找该类中的@Bean方法、组件扫描指令等,并将这些信息注册到Spring容器中。 4. **处理`ImportSelector`和`DeferredImportSelector`** + 在上述解析过程中,如果配置类包含一个实现了`ImportSelector`或`DeferredImportSelector`接口的类,那么它们将被用来选择并导入其他的类。在示例中,`MyImportSelector`和`MyDeferredImportSelector`是这样的选择器,分别导入`MyBeanA`和`MyBeanB`。 5. **处理`ImportBeanDefinitionRegistrar`** + 在加载Bean定义的过程中,也会处理与`ConfigurationClass`关联的所有`ImportBeanDefinitionRegistrar`。这些注册器允许在解析时动态地、以编程方式地添加额外的Bean定义,如`MyImportBeanDefinitionRegistrar`示例中为`MyBeanC`进行的手动注册。 6. **注册导入的配置类** + 如果一个`@Configuration`类是由于`@Import`或其他机制导入的,Spring不仅会处理它的`@Bean`方法和其他注解,还会为这个类本身注册一个Bean定义,以便它也可以被注入到其他Bean或由应用程序检索。 7. **额外Bean的注册** + 通过`ImportBeanDefinitionRegistrar`和其自定义逻辑,能够根据需要将任意数量的额外Bean定义注册到容器中。 ================================================ FILE: spring-annotation/spring-annotation-import/pom.xml ================================================ spring-annotation com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-annotation-import ================================================ FILE: spring-annotation/spring-annotation-import/src/main/java/com/xcs/spring/ImportApplication.java ================================================ package com.xcs.spring; import com.xcs.spring.config.MyConfiguration; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年08月07日 16时21分 **/ public class ImportApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); for (String beanDefinitionName : context.getBeanDefinitionNames()) { System.out.println("beanName = " + beanDefinitionName); } } } ================================================ FILE: spring-annotation/spring-annotation-import/src/main/java/com/xcs/spring/bean/MyBean.java ================================================ package com.xcs.spring.bean; /** * @author xcs * @date 2023年09月11日 11时13分 **/ public class MyBean { } ================================================ FILE: spring-annotation/spring-annotation-import/src/main/java/com/xcs/spring/bean/MyBeanA.java ================================================ package com.xcs.spring.bean; /** * @author xcs * @date 2023年08月28日 11时13分 **/ public class MyBeanA { } ================================================ FILE: spring-annotation/spring-annotation-import/src/main/java/com/xcs/spring/bean/MyBeanB.java ================================================ package com.xcs.spring.bean; /** * @author xcs * @date 2023年08月28日 11时13分 **/ public class MyBeanB { } ================================================ FILE: spring-annotation/spring-annotation-import/src/main/java/com/xcs/spring/bean/MyBeanC.java ================================================ package com.xcs.spring.bean; /** * @author xcs * @date 2023年08月28日 11时13分 **/ public class MyBeanC { } ================================================ FILE: spring-annotation/spring-annotation-import/src/main/java/com/xcs/spring/config/MyConfiguration.java ================================================ package com.xcs.spring.config; import com.xcs.spring.bean.MyBean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; /** * @author xcs * @date 2023年08月07日 16时25分 **/ @Configuration @Import({MyBean.class, MyImportSelector.class, MyDeferredImportSelector.class, MyImportBeanDefinitionRegistrar.class}) public class MyConfiguration { } ================================================ FILE: spring-annotation/spring-annotation-import/src/main/java/com/xcs/spring/config/MyDeferredImportSelector.java ================================================ package com.xcs.spring.config; import com.xcs.spring.bean.MyBeanB; import org.springframework.context.annotation.DeferredImportSelector; import org.springframework.core.type.AnnotationMetadata; /** * @author xcs * @date 2023年08月29日 11时08分 **/ public class MyDeferredImportSelector implements DeferredImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[]{MyBeanB.class.getName()}; } } ================================================ FILE: spring-annotation/spring-annotation-import/src/main/java/com/xcs/spring/config/MyImportBeanDefinitionRegistrar.java ================================================ package com.xcs.spring.config; import com.xcs.spring.bean.MyBeanC; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.type.AnnotationMetadata; /** * @author xcs * @date 2023年08月28日 11时17分 **/ public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { RootBeanDefinition beanDefinition = new RootBeanDefinition(MyBeanC.class); registry.registerBeanDefinition(MyBeanC.class.getName(), beanDefinition); } } ================================================ FILE: spring-annotation/spring-annotation-import/src/main/java/com/xcs/spring/config/MyImportSelector.java ================================================ package com.xcs.spring.config; import com.xcs.spring.bean.MyBeanA; import org.springframework.context.annotation.ImportSelector; import org.springframework.core.type.AnnotationMetadata; /** * @author xcs * @date 2023年08月28日 11时12分 **/ public class MyImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[]{MyBeanA.class.getName()}; } } ================================================ FILE: spring-annotation/spring-annotation-lazy/README.md ================================================ ## @Lazy - [@Lazy](#lazy) - [一、基本信息](#一基本信息) - [二、注解描述](#二注解描述) - [三、注解源码](#三注解源码) - [四、主要功能](#四主要功能) - [五、最佳实践](#五最佳实践) - [六、时序图](#六时序图) - [Bean注册时序图](#bean注册时序图) - [Bean延迟创建时序图](#bean延迟创建时序图) - [Bean延迟注入时序图](#bean延迟注入时序图) - [七、源码分析](#七源码分析) - [延迟初始化](#延迟初始化) - [延迟注入](#延迟注入) - [八、注意事项](#八注意事项) - [九、总结](#九总结) - [最佳实践总结](#最佳实践总结) - [源码分析总结](#源码分析总结) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [我的CSDN](https://blog.csdn.net/duzhuang2399/article/details/133800805) 📚 **文章目录** - [所有文章](https://github.com/xuchengsheng/spring-reading) 🔗 **源码地址** - [@Lazy源码](https://github.com/xuchengsheng/spring-reading/tree/master/spring-annotation/spring-annotation-lazy) ### 二、注解描述 `@Lazy`注解,它的主要用途是延迟依赖注入的初始化。通常情况下,当 ApplicationContext 被启动和刷新时,所有的单例 bean 会被立即初始化。但有时,可能希望某些 bean 在首次使用时才被初始化。 ### 三、注解源码 `@Lazy`注解是 Spring 框架自 3.0 版本开始引入的一个核心注解,用于控制 bean 的懒加载行为。默认情况下,当 `@Lazy` 被应用,bean 不会在 Spring 容器启动时立即初始化,而是在首次被引用或请求时。这适用于通过 `@Component` 或 `@Bean` 定义的 bean。此外,`@Lazy` 还可以用于注入点,如 `@Autowired`,创建一个懒解析代理,从而实现延迟注入。当用在 `@Configuration` 类上时,它影响该配置中所有的 `@Bean` 定义。 ```java /** * 指示一个bean是否应懒加载初始化。 * * 可直接或间接地用于带 org.springframework.stereotype.Component @Component 注解的类, * 或者用于带有 Bean @Bean 注解的方法。 * * 如果此注解不在 @Component 或 @Bean 定义上,将会立即初始化。 * 如果存在并且设置为 true,除非被另一个bean引用或从包围的 org.springframework.beans.factory.BeanFactory BeanFactory 中显式检索, * 否则 @Bean 或 @Component 不会初始化。如果存在并设置为 false,那么执行积极初始化单例的bean工厂将在启动时实例化bean。 * * 如果Lazy存在于 Configuration @Configuration 类上,表示该 @Configuration 中的所有 @Bean 方法都应懒加载。 * 如果在一个带有 @Lazy 注解的 @Configuration 类的 @Bean 方法上 @Lazy 设置为false,则表示覆盖了“默认懒惰”行为,该bean应立即初始化。 * * 除了其在组件初始化中的作用外,此注解也可用于带有 org.springframework.beans.factory.annotation.Autowired 或 javax.inject.Inject 的注入点: * 在这种情况下,它会导致为所有受影响的依赖关系创建一个懒解析代理,作为使用 org.springframework.beans.factory.ObjectFactory 或 javax.inject.Provider 的替代方法。 * * @author Chris Beams * @author Juergen Hoeller * @since 3.0 * @see Primary * @see Bean * @see Configuration * @see org.springframework.stereotype.Component */ @Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Lazy { /** * 是否应进行懒加载初始化。 */ boolean value() default true; } ``` ### 四、主要功能 1. **延迟初始化** + 当 `@Lazy` 注解应用于一个 `@Bean` 或 `@Component` 上时,该 bean 不会在 Spring 容器启动时立即初始化。而是直到首次被引用或请求时才进行初始化。 2. **延迟注入** + 当 `@Lazy` 注解应用于 `@Autowired` 或其他注入点上时,它导致为所注入的依赖关系创建一个懒解析代理,实现首次访问时的延迟注入。 ### 五、最佳实践 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类,然后从中获取一个 `MyService` bean 并调用其 `show` 方法。 ```java public class LazyApplication { public static void main(String[] args) { System.out.println("启动 Spring ApplicationContext..."); AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); System.out.println("启动完成 Spring ApplicationContext..."); System.out.println("获取MyService..."); MyService myService = context.getBean(MyService.class); System.out.println("调用show方法..."); myService.show(); } } ``` 这里使用`@Bean`注解,`MyBean` 被标注了 `@Lazy`,这意味着它只会在首次被请求时才会初始化。`MyService` 没有 `@Lazy` 注解,所以它会被立即初始化。 ```java @Configuration public class MyConfiguration { @Bean @Lazy public MyBean myBean(){ System.out.println("MyBean 初始化了!"); return new MyBean(); } @Bean public MyService myService(){ return new MyService(); } } ``` `MyBean` 类定义很简单。它有一个构造函数,在构造函数中打印了 "MyBean 的构造函数被调用了!",并有一个 `show` 方法,该方法打印 "hello world!"。 ```java public class MyBean { public MyBean() { System.out.println("MyBean 的构造函数被调用了!"); } public void show() { System.out.println("hello world!"); } } ``` `MyService` 类有一个对 `MyBean` 的引用,而该引用通过 `@Autowired` 进行依赖注入。由于我们还在这个注入点上添加了 `@Lazy` 注解,这意味着 `myBean` 的实际注入将被延迟,直到我们首次尝试访问它时。 ```java public class MyService { @Autowired @Lazy private MyBean myBean; public void show() { System.out.println("MyBean Class = " + myBean.getClass()); myBean.show(); } } ``` 运行结果发现 1. **延迟初始化**: - 尽管 `MyService` 在应用上下文启动后可用,但由于 `MyBean` 上的 `@Lazy` 注解,`MyBean` 在启动时并未被初始化。 - 只有在首次访问或使用 `MyBean` 时,它才真正被初始化。这在 "MyBean 初始化了!" 和 "MyBean 的构造函数被调用了!" 的输出中得到了体现。 2. **延迟注入**: - 在 `MyService` 的 `show` 方法首次被调用时,由于 `@Autowired` 和 `@Lazy` 注解的组合,Spring 不是直接注入原始的 `MyBean` 实例,而是注入一个代理对象。 - 这个代理对象的类名为 `com.xcs.spring.bean.MyBean$$EnhancerBySpringCGLIB$$2a517c55`,表明它是由 Spring 的 CGLIB 动态生成的。 - 当尝试访问该代理对象上的方法(如 `show` 方法)时,代理会确保真正的 `MyBean` 被初始化并代理该方法调用。 ```java 启动 Spring ApplicationContext... 启动完成 Spring ApplicationContext... 获取MyService... 调用show方法... MyBean Class = class com.xcs.spring.bean.MyBean$$EnhancerBySpringCGLIB$$2a517c55 MyBean 初始化了! MyBean 的构造函数被调用了! hello world! ``` ### 六、时序图 #### Bean注册时序图 ~~~mermaid sequenceDiagram LazyApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)
创建应用上下文 AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()
刷新应用上下文 AbstractApplicationContext->>AbstractApplicationContext:invokeBeanFactoryPostProcessors(beanFactory)
调用BFPP方法 AbstractApplicationContext->>PostProcessorRegistrationDelegate:invokeBeanFactoryPostProcessors(beanFactory,beanFactoryPostProcessors)
委托BFPP处理 PostProcessorRegistrationDelegate->>PostProcessorRegistrationDelegate:invokeBeanDefinitionRegistryPostProcessors(postProcessors,registry,applicationStartup)
调用BDRPP方法 PostProcessorRegistrationDelegate->>ConfigurationClassPostProcessor:postProcessBeanDefinitionRegistry(registry)
处理Bean定义 ConfigurationClassPostProcessor->>ConfigurationClassPostProcessor:processConfigBeanDefinitions(registry)
处理配置类Bean ConfigurationClassPostProcessor->>ConfigurationClassBeanDefinitionReader:loadBeanDefinitions(configurationModel)
加载Bean定义 ConfigurationClassBeanDefinitionReader->>ConfigurationClassBeanDefinitionReader:loadBeanDefinitionsForConfigurationClass(configClass,trackedConditionEvaluator)
为配置类加载 ConfigurationClassBeanDefinitionReader->>ConfigurationClassBeanDefinitionReader:loadBeanDefinitionsForBeanMethod(beanMethod)
为@Bean方法加载 ConfigurationClassBeanDefinitionReader->>AnnotationConfigUtils:processCommonDefinitionAnnotations(abd,metadata)
处理注解定义 AnnotationConfigUtils->>AnnotationConfigUtils:attributesFor(metadata, Lazy.class)
获取注解属性 AnnotationConfigUtils->>AbstractBeanDefinition:setLazyInit(lazyInit)
设置懒加载属性 ~~~ #### Bean延迟创建时序图 ~~~mermaid sequenceDiagram DependsOnApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)
创建应用上下文 AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()
刷新应用上下文 AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory)
完成bean工厂初始化 AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons()
预实例化单例beans DefaultListableBeanFactory->>AbstractBeanDefinition:isLazyInit() AbstractBeanDefinition->>DefaultListableBeanFactory:返回Bean是否懒加载 DefaultListableBeanFactory->>AbstractBeanFactory:getBean(beanName) alt Bean是懒加载 AbstractBeanFactory->>DefaultListableBeanFactory: 执行初始化Bean对象 else Bean不是懒加载 AbstractBeanFactory->>DefaultListableBeanFactory: 跳过Bean初始化,等待真正使用时在初始化 end ~~~ #### Bean延迟注入时序图 ~~~mermaid sequenceDiagram Title: InstantiationAwareBeanPostProcessor时序图 InstantiationAwareBeanPostProcessorApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext
开始初始化 AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh
刷新上下文 AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization
实例化非懒加载的单列Bean AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons
预实例化Singleton DefaultListableBeanFactory->>AbstractBeanFactory:getBean
根据beanName获取对象 AbstractBeanFactory->>AbstractBeanFactory:doGetBean
返回指定bean的实例 AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton
获取单例对象 DefaultSingletonBeanRegistry->>AbstractBeanFactory:getObject
ObjectFactory接口的工厂方法 AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean
创建一个bean实例,填充bean实例,应用后处理器 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:doCreateBean(beanName,mbd,args) AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:populateBean(beanName,mbd,bw) AbstractAutowireCapableBeanFactory->>AutowiredAnnotationBeanPostProcessor:postProcessProperties(pvs,bean,beanName) AutowiredAnnotationBeanPostProcessor->>AutowiredAnnotationBeanPostProcessor:findAutowiringMetadata(beanName,clazz,pvs) AutowiredAnnotationBeanPostProcessor->>InjectionMetadata:inject(target,beanName,pvs) InjectionMetadata->>AutowiredFieldElement:inject(bean,beanName,pvs) AutowiredFieldElement->>AutowiredFieldElement:resolveFieldValue(field,bean,beanName) AutowiredFieldElement->>DefaultListableBeanFactory:resolveDependency(descriptor,requestingBeanName,autowiredBeanNames,typeConverter) DefaultListableBeanFactory->>DefaultListableBeanFactory:getAutowireCandidateResolver() DefaultListableBeanFactory->>ContextAnnotationAutowireCandidateResolver:getLazyResolutionProxyIfNecessary(descriptor,beanName) ContextAnnotationAutowireCandidateResolver->>ContextAnnotationAutowireCandidateResolver:isLazy(descriptor) ContextAnnotationAutowireCandidateResolver->>ContextAnnotationAutowireCandidateResolver:buildLazyResolutionProxy(descriptor, beanName) ContextAnnotationAutowireCandidateResolver->>ProxyFactory:getProxy(classLoader) ProxyFactory->>ContextAnnotationAutowireCandidateResolver:返回被代理的对象 ContextAnnotationAutowireCandidateResolver->>DefaultListableBeanFactory:返回被代理的对象 DefaultListableBeanFactory->>AutowiredFieldElement:返回被代理的对象 AutowiredFieldElement->>Field:field.set(bean, value)注入被代理的对象 ~~~ ### 七、源码分析 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类,然后从中获取一个 `MyService` bean 并调用其 `show` 方法。 ```java public class LazyApplication { public static void main(String[] args) { System.out.println("启动 Spring ApplicationContext..."); AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); System.out.println("启动完成 Spring ApplicationContext..."); System.out.println("获取MyService..."); MyService myService = context.getBean(MyService.class); System.out.println("调用show方法..."); myService.show(); } } ``` 在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤。 ```java public AnnotationConfigApplicationContext(Class... componentClasses) { this(); register(componentClasses); refresh(); } ``` 在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中,我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 ```java @Override public void refresh() throws BeansException, IllegalStateException { // ... [代码部分省略以简化] // 实例化所有剩余非懒加载的单列Bean对象 finishBeanFactoryInitialization(beanFactory); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization`方法中,会继续调用`DefaultListableBeanFactory`类中的`preInstantiateSingletons`方法来完成所有剩余非懒加载的单列Bean对象。 ```java protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { // ... [代码部分省略以简化] // 完成所有剩余非懒加载的单列Bean对象。 beanFactory.preInstantiateSingletons(); } ``` #### 延迟初始化 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons`方法中,确保所有非懒加载的单例 beans 在容器启动时都被初始化,除非它们显式地标记为懒加载。这也是为什么 `@Lazy` 注解对于那些想要推迟 bean 初始化的场景非常有用。 ```java public void preInstantiateSingletons() throws BeansException { // ... [代码部分省略以简化] // 复制Bean名称列表 List beanNames = new ArrayList<>(this.beanDefinitionNames); // 初始化非懒加载单例 for (String beanName : beanNames) { RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); // Bean属性检查 if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { // 是否为工厂Bean if (isFactoryBean(beanName)) { Object bean = getBean(FACTORY_BEAN_PREFIX + beanName); // 是否为FactoryBean实例 if (bean instanceof FactoryBean) { FactoryBean factory = (FactoryBean) bean; boolean isEagerInit; // 安全权限检查 if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) { isEagerInit = AccessController.doPrivileged( (PrivilegedAction) ((SmartFactoryBean) factory)::isEagerInit, getAccessControlContext()); } else { // 是否立即初始化 isEagerInit = (factory instanceof SmartFactoryBean && ((SmartFactoryBean) factory).isEagerInit()); } // 立即初始化Bean if (isEagerInit) { getBean(beanName); } } } else { // 获取/创建Bean getBean(beanName); } } } // ... [代码部分省略以简化] } ``` #### 延迟注入 在`org.springframework.beans.factory.support.AbstractBeanFactory#getBean()`方法中,又调用了`doGetBean`方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,`doGetBean`负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。 ```java @Override public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中,首先检查所请求的bean是否是一个单例并且已经创建。如果尚未创建,它将创建一个新的实例。在这个过程中,它处理可能的异常情况,如循环引用,并确保返回的bean是正确的类型。这是Spring容器bean生命周期管理的核心部分。 ```java protected T doGetBean( String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException { // ... [代码部分省略以简化] // 开始创建bean实例 if (mbd.isSingleton()) { // 如果bean是单例的,我们会尝试从单例缓存中获取它 // 如果不存在,则使用lambda创建一个新的实例 sharedInstance = getSingleton(beanName, () -> { try { // 尝试创建bean实例 return createBean(beanName, mbd, args); } catch (BeansException ex) { // ... [代码部分省略以简化] } }); // 对于某些bean(例如FactoryBeans),可能需要进一步处理以获取真正的bean实例 beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } // ... [代码部分省略以简化] // 确保返回的bean实例与请求的类型匹配 return adaptBeanInstance(name, beanInstance, requiredType); } ``` 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()`方法中,主要负责从单例缓存中获取一个已存在的bean实例,或者使用提供的`ObjectFactory`创建一个新的实例。这是确保bean在Spring容器中作为单例存在的关键部分。 ```java public Object getSingleton(String beanName, ObjectFactory singletonFactory) { // ... [代码部分省略以简化] // 同步访问单例对象缓存,确保线程安全 synchronized (this.singletonObjects) { // 从缓存中获取单例对象 Object singletonObject = this.singletonObjects.get(beanName); // 如果缓存中没有找到 if (singletonObject == null) { // ... [代码部分省略以简化] try { // 使用工厂创建新的单例实例 singletonObject = singletonFactory.getObject(); newSingleton = true; } catch (IllegalStateException ex) { // ... [代码部分省略以简化] } catch (BeanCreationException ex) { // ... [代码部分省略以简化] } finally { // ... [代码部分省略以简化] } // ... [代码部分省略以简化] } // 返回单例对象 return singletonObject; } } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()`方法中,主要的逻辑是调用 `doCreateBean`,这是真正进行 bean 实例化、属性填充和初始化的地方。这个方法会返回新创建的 bean 实例。 ```java @Override protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // ... [代码部分省略以简化] try { // 正常的bean实例化、属性注入和初始化。 // 这里是真正进行bean创建的部分。 Object beanInstance = doCreateBean(beanName, mbdToUse, args); // 记录bean成功创建的日志 if (logger.isTraceEnabled()) { logger.trace("Finished creating instance of bean '" + beanName + "'"); } return beanInstance; } catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { // ... [代码部分省略以简化] } catch (Throwable ex) { // ... [代码部分省略以简化] } } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中,对 bean 的属性进行注入。 ```java protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // ... [代码部分省略以简化] try { // 属性注入:这一步将配置中的属性值注入到bean实例中。例如,XML中定义的属性或使用@Autowired和@Value注解的属性都会在这里被注入 populateBean(beanName, mbd, instanceWrapper); // ... [代码部分省略以简化] } catch (Throwable ex) { // ... [代码部分省略以简化] } // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean`方法中,如果一个属性被标记为 `@Autowired`,并且与 `@Lazy` 结合使用,那么实际的懒加载逻辑会在这个步骤中的其他部分被处理(特别是通过 `AutowiredAnnotationBeanPostProcessor`)。 ```java protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) { // ... [代码部分省略以简化] // 对每一个InstantiationAwareBeanPostProcessor进行处理,这些处理器可能会修改Bean的属性值。 for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) { // 尝试使用新版本的方法 postProcessProperties PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName); // ... [代码部分省略以简化] } // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessProperties`方法中,用于处理 `@Autowired` 注解的依赖注入。 ```java @Override public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) { // 根据bean的名称和类查找@Autowired注解元数据 InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs); try { // 执行实际的依赖注入 metadata.inject(bean, beanName, pvs); } catch (BeanCreationException ex) { // 抛出bean创建异常 throw ex; } catch (Throwable ex) { // 处理其他类型的异常,转换为Bean创建异常 throw new BeanCreationException(beanName, "依赖自动注入失败", ex); } // 返回属性值 return pvs; } ``` 在`org.springframework.beans.factory.annotation.InjectionMetadata#inject`方法中,遍历所有这些元素并调用其 `inject` 方法,这样实现了对带有注解的字段或方法的实际注入。 ```java public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable { // 获取已校验的注入元素 Collection checkedElements = this.checkedElements; // 如果没有已校验的元素,则使用所有注入元素 Collection elementsToIterate = (checkedElements != null ? checkedElements : this.injectedElements); if (!elementsToIterate.isEmpty()) { // 遍历所有待注入的元素(字段或方法) for (InjectedElement element : elementsToIterate) { // 执行实际的注入操作 element.inject(target, beanName, pvs); } } } ``` 在`org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject`方法中,这个 `inject` 方法的核心逻辑是解析 `@Autowired` 字段的值(即找到匹配的 bean 依赖)并注入到目标 bean 中。在这个过程中,使用缓存以提高性能。 ```java @Override protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable { // 获取带有@Autowired注解的字段 Field field = (Field) this.member; Object value; // 如果字段的值已经被缓存,则直接从缓存中获取 if (this.cached) { try { value = resolvedCachedArgument(beanName, this.cachedFieldValue); } catch (NoSuchBeanDefinitionException ex) { // 如果缓存中的bean意外被移除 -> 重新解析 value = resolveFieldValue(field, bean, beanName); } } else { // 解析字段的值(即找到要注入的bean) value = resolveFieldValue(field, bean, beanName); } if (value != null) { // 使字段可访问(例如,如果它是私有的) ReflectionUtils.makeAccessible(field); // 将解析出的值(bean)注入到目标bean的字段中 field.set(bean, value); } } ``` 在`org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#resolveFieldValue`方法中,主要用于解析 `@Autowired` 注解的字段值。它确定了应该为目标字段注入哪个 bean。 ```java @Nullable private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) { // ... [代码部分省略以简化] Object value; try { // 解析依赖:这里的核心逻辑是使用bean工厂去解析字段的依赖。它会查找合适的bean来注入到此字段。 value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter); } catch (BeansException ex) { // 当无法解析依赖时,抛出异常 throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex); } // ... [代码部分省略以简化] // 返回解析到的值(bean) return value; } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency`方法中,这里的关键逻辑是 `getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary`,该方法尝试为描述的依赖关系获取一个懒加载代理。如果该依赖关系标记为懒加载 (`@Lazy`),并且被 `@Autowired` 引用,那么它将返回一个懒加载代理,而不是实际的 bean。这样,只有当应用程序真正访问该依赖关系时,实际的 bean 才会被初始化。 ```java @Override @Nullable public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName, @Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException { // ... [代码部分省略以简化] // 尝试为描述的依赖关系获取一个懒加载代理。如果依赖是懒加载的,这将返回一个代理对象。 Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary( descriptor, requestingBeanName); // 如果没有为懒加载的依赖关系获取到代理,则尝试直接解析依赖关系 if (result == null) { result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter); } return result; // 返回解析得到的bean或者懒加载代理 } ``` 在`org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver#getLazyResolutionProxyIfNecessary`方法中,如果是懒加载,会调用 `buildLazyResolutionProxy` 来创建一个代理,当首次访问该代理时,代理会触发实际的 bean 初始化。 ```java @Override @Nullable public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName) { // 判断依赖描述是否被标记为懒加载 // 如果是懒加载,为其构建一个懒加载代理 // 如果不是懒加载,则返回null return (isLazy(descriptor) ? buildLazyResolutionProxy(descriptor, beanName) : null); } ``` 在`org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver#buildLazyResolutionProxy`方法中,核心部分是 `TargetSource` 和 `ProxyFactory`。当我们访问这个懒加载代理时,`TargetSource` 的 `getTarget` 方法会被调用,它会解析和返回真正的 bean(或其他依赖项)。使用 `ProxyFactory`,可以为给定的 `TargetSource` 创建一个新的代理。这是 `@Lazy` 注解在字段注入时的实际实现,确保在首次访问字段时才触发依赖的解析和bean的初始化。 ```java protected Object buildLazyResolutionProxy(final DependencyDescriptor descriptor, final @Nullable String beanName) { // 获取当前的BeanFactory BeanFactory beanFactory = getBeanFactory(); // 确认BeanFactory是DefaultListableBeanFactory的实例 Assert.state(beanFactory instanceof DefaultListableBeanFactory, "BeanFactory needs to be a DefaultListableBeanFactory"); final DefaultListableBeanFactory dlbf = (DefaultListableBeanFactory) beanFactory; // 创建一个目标源(TargetSource)用于懒加载代理 TargetSource ts = new TargetSource() { // 获取依赖的类型 @Override public Class getTargetClass() { return descriptor.getDependencyType(); } @Override public boolean isStatic() { return false; } // 当访问代理时,该方法被调用来解析实际的依赖关系 @Override public Object getTarget() { // ... [代码部分省略以简化] return target; // 返回解析得到的bean } @Override public void releaseTarget(Object target) { } }; // 使用Spring的ProxyFactory创建一个新的代理 ProxyFactory pf = new ProxyFactory(); pf.setTargetSource(ts); Class dependencyType = descriptor.getDependencyType(); if (dependencyType.isInterface()) { pf.addInterface(dependencyType); } // 返回懒加载代理 return pf.getProxy(dlbf.getBeanClassLoader()); } ``` ### 八、注意事项 1. **默认行为** + 如果没有使用 `@Lazy` 注解,Spring 容器会在启动时立即实例化单例 bean。通过使用 `@Lazy`,我们可以改变这个行为,使得 bean 只在首次请求时被初始化。 2. **构造函数注入** + 如果一个懒加载的 bean 依赖于另一个非懒加载的 bean,那么该懒加载的 bean 会在容器启动时被初始化,因为它的依赖需要它。这种情况常常在构造函数注入时出现。 3. **上下文的生命周期** + `@Lazy` 对于应用上下文的启动速度可能有益,因为少了一些即时初始化的工作。但请注意,延迟初始化可能会导致我们在首次访问 bean 时遇到延迟。 4. **与 `@Autowired` 结合使用** + 如果我们在一个字段或 setter 方法上同时使用 `@Autowired` 和 `@Lazy`,Spring 会注入一个代理对象。这个代理对象会在我们首次访问它时触发真正的 bean 初始化。 5. **懒加载的代理** + 当与 `@Autowired` 结合使用时,要确保 bean 的类型与代理类型兼容。如果注入的 bean 类型是一个接口,Spring 会创建一个基于接口的代理。如果是一个类,Spring 会创建一个基于类的代理。 6. **错误处理** + 延迟初始化意味着某些错误可能在应用启动时不会被立即发现,例如 bean 配置错误。这样的错误只有在实际尝试初始化 bean 时才会被抛出。 7. **在组件类上使用** + 对于直接或间接使用 `@Component`、`@Service`、`@Repository` 或 `@Controller` 注解的类,可以使用 `@Lazy` 注解来使这些组件在首次被注入或查找时才被初始化。 ### 九、总结 #### 最佳实践总结 1. **上下文初始化**: - 使用 `AnnotationConfigApplicationContext` 初始化应用上下文是针对基于Java的配置的推荐做法。 - 提供一个配置类(如 `MyConfiguration`)作为参数可以帮助上下文知道如何加载和配置beans。 2. **懒加载配置**: - 通过在配置类的 `@Bean` 方法上添加 `@Lazy` 注解,可以确保特定的bean只在首次请求时被初始化,而不是在应用上下文启动时。 - 这可以提高应用启动速度,特别是对于资源密集型的beans或需要长时间初始化的beans。 3. **依赖注入**: - 使用 `@Autowired` 注解是Spring的核心特性,它可以自动注入bean的依赖关系。 - 当与 `@Lazy` 注解组合使用时,Spring会注入一个代理对象而不是实际的bean实例。这个代理对象在首次访问时会触发真正的bean初始化。 4. **理解代理**: - 由于延迟注入,通过 `@Autowired` 和 `@Lazy` 注解注入的对象是一个由CGLIB生成的代理对象。 - 这个代理对象负责在首次访问时初始化真正的bean。 5. **输出与验证**: - 通过在bean的初始化和方法调用中添加日志或打印语句,可以验证和观察懒加载和代理的行为。 - 这对于确保应用的预期行为和性能调优非常有用。 #### 源码分析总结 1. **启动及初始化**: - 使用`AnnotationConfigApplicationContext`初始化应用上下文。 - 在`AnnotationConfigApplicationContext`的构造函数中,执行了注册(`register`)和刷新(`refresh`)方法。 2. **Bean的实例化**: - 在上下文刷新过程中,`finishBeanFactoryInitialization(beanFactory)`方法确保所有非懒加载的单例Beans被实例化。 - `DefaultListableBeanFactory#preInstantiateSingletons`方法确保所有非懒加载的单例Beans在容器启动时被初始化。 3. **延迟初始化**: - 如果Bean被标记为`@Lazy`,它将不会在容器启动时被初始化,但只在首次请求时。 - `DefaultListableBeanFactory#preInstantiateSingletons`方法中,对于`isLazyInit`返回`true`的Beans,不会进行预初始化。 4. **Bean的获取与依赖注入**: - 使用`AbstractBeanFactory#getBean`方法获取Bean实例。 - 如果Bean尚未创建,`doGetBean`方法将执行Bean的实际创建,包括解析依赖关系、处理循环引用等。 - 对于单例Beans,它们将被缓存,确保每次都返回相同的实例。 - 通过`AbstractAutowireCapableBeanFactory#createBean`来进行实际的Bean创建,并且将其属性通过`populateBean`方法注入。 5. **延迟注入**: - 如果一个字段或属性被`@Autowired`注解,并与`@Lazy`结合使用,实际的懒加载逻辑会在属性填充阶段被处理。 - 使用`AutowiredAnnotationBeanPostProcessor`来处理带有`@Autowired`注解的属性的注入。 - 在`AutowiredFieldElement#inject`方法中,如果字段被标记为`@Lazy`,Spring不会直接注入真实的Bean,而是注入一个懒加载代理。 - 这个懒加载代理的实际行为是在首次访问时触发真正的Bean初始化。 6. **懒加载代理的创建**: - 使用`ContextAnnotationAutowireCandidateResolver`来检查依赖关系是否需要懒加载。 - 如果需要懒加载,它将使用`buildLazyResolutionProxy`方法来为该依赖关系创建一个代理。 - 这个代理的行为是:在首次访问时,它会解析和返回真正的Bean或其他依赖项。 - 使用Spring的`ProxyFactory`来为给定的目标源创建新的代理。 ================================================ FILE: spring-annotation/spring-annotation-lazy/pom.xml ================================================ spring-annotation com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-annotation-lazy ================================================ FILE: spring-annotation/spring-annotation-lazy/src/main/java/com/xcs/spring/LazyApplication.java ================================================ package com.xcs.spring; import com.xcs.spring.config.MyConfiguration; import com.xcs.spring.service.MyService; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年08月07日 16时21分 **/ public class LazyApplication { public static void main(String[] args) { System.out.println("启动 Spring ApplicationContext..."); AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); System.out.println("启动完成 Spring ApplicationContext..."); System.out.println("获取MyService..."); MyService myService = context.getBean(MyService.class); System.out.println("调用show方法..."); myService.show(); } } ================================================ FILE: spring-annotation/spring-annotation-lazy/src/main/java/com/xcs/spring/bean/MyBean.java ================================================ package com.xcs.spring.bean; public class MyBean { public MyBean() { System.out.println("MyBean 的构造函数被调用了!"); } public void show() { System.out.println("hello world!"); } } ================================================ FILE: spring-annotation/spring-annotation-lazy/src/main/java/com/xcs/spring/config/MyConfiguration.java ================================================ package com.xcs.spring.config; import com.xcs.spring.bean.MyBean; import com.xcs.spring.service.MyService; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Lazy; @Configuration public class MyConfiguration { @Bean @Lazy public MyBean myBean(){ System.out.println("MyBean 初始化了!"); return new MyBean(); } @Bean public MyService myService(){ return new MyService(); } } ================================================ FILE: spring-annotation/spring-annotation-lazy/src/main/java/com/xcs/spring/service/MyService.java ================================================ package com.xcs.spring.service; import com.xcs.spring.bean.MyBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; public class MyService { @Autowired @Lazy private MyBean myBean; public void show() { System.out.println("MyBean Class = " + myBean.getClass()); myBean.show(); } } ================================================ FILE: spring-annotation/spring-annotation-profile/README.md ================================================ ## TODO ================================================ FILE: spring-annotation/spring-annotation-profile/pom.xml ================================================ spring-annotation com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-annotation-profile 11 11 ================================================ FILE: spring-annotation/spring-annotation-propertySource/README.md ================================================ ## @PropertySource - [@PropertySource](#propertysource) - [一、基本信息](#一基本信息) - [二、注解描述](#二注解描述) - [三、注解源码](#三注解源码) - [四、主要功能](#四主要功能) - [五、最佳实践](#五最佳实践) - [六、时序图](#六时序图) - [七、源码分析](#七源码分析) - [八、注意事项](#八注意事项) - [九、总结](#九总结) - [最佳实践总结](#最佳实践总结) - [源码分析总结](#源码分析总结) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [我的CSDN](https://blog.csdn.net/duzhuang2399/article/details/133800438) 📚 **文章目录** - [所有文章](https://github.com/xuchengsheng/spring-reading) 🔗 **源码地址** - [@PropertySource源码](https://github.com/xuchengsheng/spring-reading/tree/master/spring-annotation/spring-annotation-propertySource) ### 二、注解描述 `@PropertySource` 注解,用于指定外部的属性文件,从而将该文件中的键值对加载到 Spring 的 `Environment` 中。这样,我们就可以在应用程序中轻松地访问和使用这些属性值。 ### 三、注解源码 `@Configuration`注解是 Spring 框架自 3.1 版本开始引入的一个核心注解,`@PropertySource` 是用于在 Spring 框架中声明属性源的注解。它允许我们指定外部属性文件(例如 `.properties` 或 `.xml` 文件),并将这些文件中的键值对加载到 Spring 的 `Environment` 中,从而使得应用程序可以轻松访问和使用这些属性值。 ```java /** * @PropertySource 是一个用于在 Spring 框架中声明属性源的注解。 * 使用此注解,我们可以指定外部的属性文件(如 .properties 或 .xml 文件), * 从而将该文件中的键值对加载到 Spring 的 Environment 中。 * 这样,应用程序就可以轻松地访问和使用这些属性值。 * * 通常,此注解与 @Configuration 注解类一起使用,为整个 Spring 环境提供配置属性。 * * 示例: * Configuration * PropertySource("classpath:/com/myco/app.properties") * public class AppConfig { * // ... * } * * 通过上面的配置,应用程序可以确保 `app.properties` 文件中的属性都可以在 Spring Environment 中使用。 * * @author Chris Beams * @author Juergen Hoeller * @author Phillip Webb * @author Sam Brannen * @since 3.1 * @see PropertySources * @see Configuration * @see org.springframework.core.env.PropertySource * @see org.springframework.core.env.ConfigurableEnvironment#getPropertySources() * @see org.springframework.core.env.MutablePropertySources */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Repeatable(PropertySources.class) public @interface PropertySource { /** * 指定此属性源的名称。如果省略,工厂将根据底层资源生成一个名称。 */ String name() default ""; /** * 指定要加载的属性文件的资源位置。 * 支持传统的属性文件和XML格式,例如:"classpath:/com/myco/app.properties" 或 "file:/path/to/file.xml"。 * 不支持资源位置的通配符。 * ${...} 占位符将根据已在 Environment 中注册的所有属性源进行解析。 * 每个位置将按声明的顺序添加到封闭的 Environment 作为它自己的属性源。 */ String[] value(); /** * 指示是否应忽略找不到的属性资源。 * 如果属性文件是完全可选的,则 true 是合适的。 * 默认值为 false。 */ boolean ignoreResourceNotFound() default false; /** * 为给定的资源指定一个特定的字符编码,例如 "UTF-8"。 */ String encoding() default ""; /** * 指定一个自定义的 PropertySourceFactory。 * 默认情况下,将使用标准资源文件的默认工厂。 */ Class factory() default PropertySourceFactory.class; } ``` ### 四、主要功能 1. **加载属性文件** + 使我们能够指定一个或多个外部属性文件(如 `.properties` 或 `.xml` 文件),并将其加载到 Spring 的上下文中。 2. **声明资源位置** + `@PropertySource` 提供一个 `value` 属性,用于指定外部属性文件的位置,如 `classpath:/com/myco/app.properties` 或 `file:/path/to/file.xml`。 3. **属性覆盖** + 当使用多个 `@PropertySource` 注解或 `PropertySources` 注解(该注解允许我们指定多个 `@PropertySource`)时,后声明的属性源会覆盖先声明的属性源中的同名属性。 4. **处理资源找不到的情况** + 通过 `ignoreResourceNotFound` 属性,我们可以指定当资源找不到时是否抛出异常。这在某些属性文件是可选的情况下特别有用。 5. **指定文件编码** + 通过 `encoding` 属性,我们可以为资源文件指定特定的字符编码,如 "UTF-8"。 6. **自定义属性源工厂** + 使用 `factory` 属性,我们可以为属性源指定一个自定义的 `PropertySourceFactory`,用于解析和加载属性。 7. **支持占位符解析** + 在 `@PropertySource` 指定的文件中的属性值可以使用 `${...}` 占位符,这些占位符将被解析为在 `Environment` 中已经加载的其他属性的值。 8. **与 `Environment` 交互** + 一旦属性文件被加载到 `Environment` 中,可以通过 `@Autowired` 注入 `Environment` 来查询和使用这些属性。 ### 五、最佳实践 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从中获取两个属性:`apiVersion` 和 `kind`。 ```java public class PropertySourceApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); System.out.println("apiVersion = " + context.getEnvironment().getProperty("apiVersion")); System.out.println("kind = " + context.getEnvironment().getProperty("kind")); } } ``` `MyConfiguration` 类标注了 `@Configuration`,这意味着它是一个基于Java的Spring配置类。同时,它还使用 `@PropertySource` 注解来加载名为 `my-application.yml` 的外部属性文件。 ```java @Configuration @PropertySource("classpath:my-application.yml") public class MyConfiguration { } ``` `my-application.yml` 文件包含以下内容 ```yaml apiVersion: v1 kind: ConfigMap ``` 运行结果发现,我们从Spring应用成功地从一个YAML文件中加载了属性,并能够在应用中使用这些属性值。 ```java apiVersion = v1 kind = ConfigMap ``` ### 六、时序图 ~~~mermaid sequenceDiagram Title: @PropertySource注解时序图 ComponentScanApplication->>AnnotationConfigApplicationContext: AnnotationConfigApplicationContext(componentClasses)
创建上下文 AnnotationConfigApplicationContext->>AbstractApplicationContext: refresh()
刷新上下文 AbstractApplicationContext->>AbstractApplicationContext: invokeBeanFactoryPostProcessors(beanFactory)
调用Bean工厂后置处理器 AbstractApplicationContext->>PostProcessorRegistrationDelegate: invokeBeanFactoryPostProcessors(beanFactory,beanFactoryPostProcessors)
委托处理 PostProcessorRegistrationDelegate->>PostProcessorRegistrationDelegate: invokeBeanDefinitionRegistryPostProcessors(postProcessors,registry,applicationStartup)
注册后置处理器 PostProcessorRegistrationDelegate->>ConfigurationClassPostProcessor: postProcessBeanDefinitionRegistry(registry)
配置类后处理 ConfigurationClassPostProcessor->>ConfigurationClassPostProcessor: processConfigBeanDefinitions(registry)
处理Bean定义 ConfigurationClassPostProcessor->>ConfigurationClassParser: ConfigurationClassParser(...)
创建解析器 ConfigurationClassParser-->>ConfigurationClassPostProcessor: 返回解析器
ConfigurationClassPostProcessor->>ConfigurationClassParser: parser.parse(candidates)
解析候选项 ConfigurationClassParser->>ConfigurationClassParser: parse(metadata, String beanName)
解析元数据 ConfigurationClassParser->>ConfigurationClassParser: processConfigurationClass(configClass,filter)
处理配置类 ConfigurationClassParser->>ConfigurationClassParser: doProcessConfigurationClass(configClass,sourceClass,filter)
执行处理操作 ConfigurationClassParser->>ConfigurationClassParser: processPropertySource(propertySource)
处理属性源 ConfigurationClassParser->>DefaultPropertySourceFactory:createPropertySource(name,resource)
创建属性源 DefaultPropertySourceFactory->>ConfigurationClassParser:返回PropertySource ConfigurationClassParser->>StandardEnvironment:getPropertySources()
获取属性源 StandardEnvironment->>ConfigurationClassParser:返回MutablePropertySources ConfigurationClassParser->>ConfigurationClassParser: addPropertySource(propertySource)
添加属性源 ConfigurationClassParser->>MutablePropertySources:addLast(propertySource)
添加最低优先级的属性源 ~~~ ### 七、源码分析 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从中获取两个属性:`apiVersion` 和 `kind`。 ```java public class PropertySourceApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); System.out.println("apiVersion = " + context.getEnvironment().getProperty("apiVersion")); System.out.println("kind = " + context.getEnvironment().getProperty("kind")); } } ``` 在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法。 ```java public AnnotationConfigApplicationContext(Class... componentClasses) { this(); register(componentClasses); refresh(); } ``` 在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 ```java @Override public void refresh() throws BeansException, IllegalStateException { // ... [代码部分省略以简化] // 调用在上下文中注册为bean的工厂处理器 invokeBeanFactoryPostProcessors(beanFactory); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors`方法中,又委托了`PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()`进行调用。 ```java protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) { PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors()); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors`方法中,首先调用了 `BeanDefinitionRegistryPostProcessor`(这是 `BeanFactoryPostProcessor` 的子接口)。它专门用来在所有其他 bean 定义加载之前修改默认的 bean 定义。 ```java public static void invokeBeanFactoryPostProcessors( ConfigurableListableBeanFactory beanFactory, List beanFactoryPostProcessors) { // ... [代码部分省略以简化] invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup()); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanDefinitionRegistryPostProcessors`方法中,循环调用了实现`BeanDefinitionRegistryPostProcessor`接口中的`postProcessBeanDefinitionRegistry(registry)`方法 ```java private static void invokeBeanDefinitionRegistryPostProcessors( Collection postProcessors, BeanDefinitionRegistry registry, ApplicationStartup applicationStartup) { for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) { StartupStep postProcessBeanDefRegistry = applicationStartup.start("spring.context.beandef-registry.post-process") .tag("postProcessor", postProcessor::toString); postProcessor.postProcessBeanDefinitionRegistry(registry); postProcessBeanDefRegistry.end(); } } ``` 在`org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry`方法中,调用了`processConfigBeanDefinitions`方法,该方法的主要目的是处理和注册配置类中定义的beans。 ```java @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { // ... [代码部分省略以简化] processConfigBeanDefinitions(registry); } ``` 在`org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions`方法中,这个方法主要处理了配置类的解析和验证,并确保了所有在配置类中定义的beans都被正确地注册到Spring的bean定义注册表中。 ```java public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { // ... [代码部分省略以简化] // 步骤1:创建一个用于解析配置类的解析器 ConfigurationClassParser parser = new ConfigurationClassParser( this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry); // 步骤2:初始化候选配置类集合以及已解析配置类集合 Set candidates = new LinkedHashSet<>(configCandidates); Set alreadyParsed = new HashSet<>(configCandidates.size()); // 步骤3:循环处理所有候选配置类,直至没有候选类为止 do { // 步骤3.1 解析配置类 parser.parse(candidates); // 步骤3.2 验证配置类 parser.validate(); // ... [代码部分省略以简化] } while (!candidates.isEmpty()); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.annotation.ConfigurationClassParser#parse`方法中,主要是遍历所有的配置类候选者,并对每一个带有注解的Bean定义进行解析。这通常涉及到查找该配置类中的@Bean方法、组件扫描指令等,并将这些信息注册到Spring容器中。 ```java public void parse(Set configCandidates) { // ... [代码部分省略以简化] parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName()); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.annotation.ConfigurationClassParser#parse(metadata, beanName)`方法中,将注解元数据和Bean名称转化为一个配置类,然后对其进行处理。处理配置类是Spring配置驱动的核心,它涉及到许多关键操作,如处理`@ComponentScan`注解等等。 ```java protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException { processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER); } ``` 在`org.springframework.context.annotation.ConfigurationClassParser#processConfigurationClass`方法中,处理一个给定的配置类。它首先递归地处理配置类及其父类,以确保所有相关的配置都被正确地读取并解析。在递归处理完所有相关配置后,它将配置类添加到已解析的配置类的映射中。 ```java protected void processConfigurationClass(ConfigurationClass configClass, Predicate filter) throws IOException { // ... [代码部分省略以简化] // 步骤1:递归地处理配置类及其超类层次结构 SourceClass sourceClass = asSourceClass(configClass, filter); do { sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter); } while (sourceClass != null); // 步骤2:将处理后的配置类放入映射中 this.configurationClasses.put(configClass, configClass); } ``` 在`org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass`方法中,主要任务是处理 `ConfigurationClass` 中定义的 `@PropertySource` 注解,并确保它们被正确地添加到 `ConfigurableEnvironment`。如果环境不可配置(即不是 `ConfigurableEnvironment` 的实例),则会忽略该注解并记录相关信息。 ```java protected final SourceClass doProcessConfigurationClass( ConfigurationClass configClass, SourceClass sourceClass, Predicate filter) throws IOException { // 处理任何 @PropertySource 注解 for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), PropertySources.class, org.springframework.context.annotation.PropertySource.class)) { // 检查当前环境是否为ConfigurableEnvironment的实例 if (this.environment instanceof ConfigurableEnvironment) { // 如果是,则处理该属性源 processPropertySource(propertySource); } else { // 否则,记录一条信息,说明正在忽略@PropertySource注解 logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() + "]. Reason: Environment must implement ConfigurableEnvironment"); } } // 没有超类 -> 处理完成 return null; } ``` 在`org.springframework.context.annotation.ConfigurationClassParser#processPropertySource`方法中,首先解析 `@PropertySource` 注解的各个属性,如 `name`、`encoding`、`value` 和 `factory`。然后,它尝试加载每个指定的属性文件,并根据文件的内容和其他相关属性创建一个属性源,最后将这个属性源添加到Spring环境中。 ```java /** * 处理给定的@PropertySource注解属性。 * * @param propertySource @PropertySource注解的属性 * @throws IOException 如果处理时发生IO异常 */ private void processPropertySource(AnnotationAttributes propertySource) throws IOException { // 获取"name"属性,如果没有指定,设置为null String name = propertySource.getString("name"); if (!StringUtils.hasLength(name)) { name = null; } // 获取"encoding"属性,如果没有指定,设置为null String encoding = propertySource.getString("encoding"); if (!StringUtils.hasLength(encoding)) { encoding = null; } // 获取"value"属性,它表示属性文件的位置 String[] locations = propertySource.getStringArray("value"); // 确保至少指定了一个位置 Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required"); // 获取"ignoreResourceNotFound"属性,表示如果资源找不到是否应该忽略 boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound"); // 获取并实例化PropertySourceFactory,用于创建属性源 Class factoryClass = propertySource.getClass("factory"); PropertySourceFactory factory = (factoryClass == PropertySourceFactory.class ? DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiateClass(factoryClass)); // 对于每一个指定的位置: for (String location : locations) { try { // 解析位置中的占位符 String resolvedLocation = this.environment.resolveRequiredPlaceholders(location); // 获取资源 Resource resource = this.resourceLoader.getResource(resolvedLocation); // 添加属性源到环境中 addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding))); } catch (IllegalArgumentException | FileNotFoundException | UnknownHostException | SocketException ex) { // 当尝试打开资源时,占位符不可解析或资源找不到 if (ignoreResourceNotFound) { // 如果忽略资源找不到,则记录一条信息 if (logger.isInfoEnabled()) { logger.info("Properties location [" + location + "] not resolvable: " + ex.getMessage()); } } else { // 否则,抛出异常 throw ex; } } } } ``` 在`org.springframework.core.io.support.DefaultPropertySourceFactory#createPropertySource`方法中,如果提供了名称,它将为该属性源使用该名称。如果没有提供名称,它将只基于资源创建一个属性源,属性源的名称可能会基于资源自己的描述或其他默认方式来确定。 ```java @Override public PropertySource createPropertySource(@Nullable String name, EncodedResource resource) throws IOException { return (name != null ? new ResourcePropertySource(name, resource) : new ResourcePropertySource(resource)); } ``` 在`org.springframework.context.annotation.ConfigurationClassParser#addPropertySource`方法中,首先检查是否已经添加了具有相同名称的属性源。如果是,则它会将新的属性源与现有的属性源合并。如果没有,它将按照适当的顺序添加新的属性源。 ```java private void addPropertySource(PropertySource propertySource) { // 获取属性源的名称 String name = propertySource.getName(); // 获取当前环境的所有属性源 MutablePropertySources propertySources = ((ConfigurableEnvironment) this.environment).getPropertySources(); // 如果此名称的属性源已经被添加过 if (this.propertySourceNames.contains(name)) { // 我们已经添加了一个版本,需要扩展它 PropertySource existing = propertySources.get(name); if (existing != null) { PropertySource newSource = (propertySource instanceof ResourcePropertySource ? ((ResourcePropertySource) propertySource).withResourceName() : propertySource); // 如果现有的属性源是CompositePropertySource if (existing instanceof CompositePropertySource) { ((CompositePropertySource) existing).addFirstPropertySource(newSource); } else { // 创建一个新的CompositePropertySource并将新的和现有的属性源添加进去 if (existing instanceof ResourcePropertySource) { existing = ((ResourcePropertySource) existing).withResourceName(); } CompositePropertySource composite = new CompositePropertySource(name); composite.addPropertySource(newSource); composite.addPropertySource(existing); propertySources.replace(name, composite); } return; } } // 如果还没有添加任何属性源 if (this.propertySourceNames.isEmpty()) { propertySources.addLast(propertySource); } else { // 添加属性源到上次处理的属性源之前 String firstProcessed = this.propertySourceNames.get(this.propertySourceNames.size() - 1); propertySources.addBefore(firstProcessed, propertySource); } // 将属性源名称添加到跟踪的名称列表中 this.propertySourceNames.add(name); } ``` ### 八、注意事项 1. **文件位置** + 确保你提供的文件路径是正确的。例如,`classpath:` 前缀表示文件应该在类路径中,而 `file:` 前缀则表示文件应该在文件系统的特定位置。 2. **占位符** + 在 `@PropertySource` 的 `value` 属性中,你可以使用 `${...}` 占位符,它们将会被已注册的任何属性源解析。 3. **处理重复的属性源名称** + 如果你有多个 `@PropertySource` 注解(或使用 `@PropertySources` 注解)且它们具有相同的名称,那么它们会合并。后声明的 `@PropertySource` 将覆盖先前声明的同名 `@PropertySource`。 4. **属性源的顺序** + 属性源的顺序很重要,因为在多个属性源中定义的同名属性将使用先找到的值。你可以使用 `PropertySource` 的 `name` 属性来明确指定属性源的名称,以控制其在环境中的顺序。 5. **忽略找不到的资源** + 你可以使用 `ignoreResourceNotFound` 属性来指定当属性文件找不到时是否应该抛出异常。默认情况下,这是 `false`,意味着如果属性文件找不到,会抛出异常。设置为 `true` 可以让Spring在找不到文件时安静地继续运行。 6. **字符编码** + 从Spring 4.3开始,`@PropertySource` 注解有一个 `encoding` 属性,允许你为给定的资源指定特定的字符编码。 7. **自定义属性源工厂** + 如果你需要特殊的逻辑来创建属性源,可以使用 `factory` 属性来指定一个自定义的 `PropertySourceFactory`。 8. **激活属性占位符解析** + 仅仅使用 `@PropertySource` 并不会激活属性占位符解析。为了替换你的bean定义中的 `${...}` 占位符,你还需要添加 `@Bean` 定义为 `PropertySourcesPlaceholderConfigurer`。 9. **与Profiles结合** + 你可以与Spring的Profile功能结合使用 `@PropertySource`,以根据不同的环境加载不同的属性文件。 ### 九、总结 #### 最佳实践总结 1. **初始化上下文** + 使用 `AnnotationConfigApplicationContext` 作为Spring容器的初始化方式,它允许从Java注解中配置Spring容器。 2. **定义配置类** + 创建一个名为 `MyConfiguration` 的类,并使用 `@Configuration` 注解来标记它,表示这是一个基于Java的Spring配置类。 3. **指定属性源** + 在 `MyConfiguration` 类中,使用 `@PropertySource` 注解指定了一个外部属性文件的位置,文件名为 `my-application.yml`。 4. **定义属性内容** + 在 `my-application.yml` 文件中定义了两个属性:`apiVersion` 和 `kind`。 5. **加载并访问属性** + 运行应用程序后,使用Spring的 `Environment` API来访问这些属性,并将其输出到控制台。 6. **运行结果** + 从YAML文件成功加载的属性在控制台上显示为 `apiVersion = v1` 和 `kind = ConfigMap`。 #### 源码分析总结 1. **上下文初始化** + 通过使用 `AnnotationConfigApplicationContext`, Spring 上下文被初始化。该类允许Spring容器从Java注解中进行配置。传递的配置类是 `MyConfiguration`,该类定义了要从中加载的属性源。 2. **处理@PropertySource注解** + 在配置类处理期间,Spring 检查每个配置类以查找 `@PropertySource` 注解。如果找到,则属性源的相关数据(例如其位置和其他属性)被提取出来。 3. **资源位置解析** + 对于 `@PropertySource` 注解中定义的每个属性文件位置,Spring 尝试加载和解析该文件。它首先解析任何占位符,然后尝试加载资源。 4. **创建属性源** + 使用 `DefaultPropertySourceFactory`, Spring 创建一个属性源。这个工厂可以从给定的资源(例如 .properties 或 .yml 文件)创建一个属性源。 5. **向环境中添加属性源** + 一旦属性源被创建,它就被添加到Spring的运行时环境中。如果已经存在具有相同名称的属性源,新的属性源可能会与旧的合并,或者会以适当的方式进行处理。 6. **处理完成** + 在完成所有配置类和属性源的处理之后,Spring上下文继续其正常的启动过程,最终在应用程序运行时提供这些属性。 ================================================ FILE: spring-annotation/spring-annotation-propertySource/pom.xml ================================================ spring-annotation com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-annotation-propertySource ================================================ FILE: spring-annotation/spring-annotation-propertySource/src/main/java/com/xcs/spring/PropertySourceApplication.java ================================================ package com.xcs.spring; import com.xcs.spring.config.MyConfiguration; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年08月07日 16时21分 **/ public class PropertySourceApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); System.out.println("apiVersion = " + context.getEnvironment().getProperty("apiVersion")); System.out.println("kind = " + context.getEnvironment().getProperty("kind")); } } ================================================ FILE: spring-annotation/spring-annotation-propertySource/src/main/java/com/xcs/spring/config/MyConfiguration.java ================================================ package com.xcs.spring.config; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; /** * @author xcs * @date 2023年08月07日 16时25分 **/ @Configuration @PropertySource("classpath:my-application.yml") public class MyConfiguration { } ================================================ FILE: spring-annotation/spring-annotation-propertySource/src/main/resources/my-application.yml ================================================ apiVersion: v1 kind: ConfigMap ================================================ FILE: spring-annotation/spring-annotation-value/README.md ================================================ ## @Value - [@Value](#value) - [一、基本信息](#一基本信息) - [二、注解描述](#二注解描述) - [三、注解源码](#三注解源码) - [四、主要功能](#四主要功能) - [五、最佳实践](#五最佳实践) - [六、时序图](#六时序图) - [七、源码分析](#七源码分析) - [前置条件](#前置条件) - [收集阶段](#收集阶段) - [注入阶段](#注入阶段) - [八、注意事项](#八注意事项) - [九、总结](#九总结) - [最佳实践总结](#最佳实践总结) - [源码分析总结](#源码分析总结) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [我的CSDN](https://blog.csdn.net/duzhuang2399/article/details/133815435) 📚 **文章目录** - [所有文章](https://github.com/xuchengsheng/spring-reading) 🔗 **源码地址** - [@Value源码](https://github.com/xuchengsheng/spring-reading/tree/master/spring-annotation/spring-annotation-value) ### 二、注解描述 `@Value` 注解,是一个非常有用的功能,它允许我们从配置文件中注入属性值到Java类的字段或方法参数中。这样,我们可以将配置和代码分离,使应用更容易配置和维护。 ### 三、注解源码 `@Value`注解是 Spring 框架自 3.1 版本开始引入的一个核心注解,主要目的是允许我们在Spring管理的bean中直接注入来自各种源(如属性文件、系统属性等)的值,而不需要显式地编写代码来解析这些值。 ```java /** * 用于字段或方法/构造函数参数级别的注解, * 表示被注解元素的默认值表达式。 * * 通常用于基于表达式或属性的依赖注入。 * 也支持动态解析处理器方法参数,例如在Spring MVC中。 * * 常见的使用场景是使用 `#{systemProperties.myProp}` 这样的SpEL(Spring表达式语言)表达式来注入值。 * 或者,使用 `${my.app.myProp}` 这样的属性占位符来注入值。 * * 注意,@Value 注解的实际处理是由 BeanPostProcessor 执行的, * 这意味着我们**不能**在 BeanPostProcessor 或 BeanFactoryPostProcessor 类型中使用 @Value。 * 请查阅 AutowiredAnnotationBeanPostProcessor 类的javadoc(默认检查此注解的存在)。 * * @author Juergen Hoeller * @since 3.0 * @see AutowiredAnnotationBeanPostProcessor * @see Autowired * @see org.springframework.beans.factory.config.BeanExpressionResolver * @see org.springframework.beans.factory.support.AutowireCandidateResolver#getSuggestedValue */ @Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Value { /** * 实际的值表达式,如 `#{systemProperties.myProp}`, * 或属性占位符,如 `${my.app.myProp}`。 */ String value(); } ``` ### 四、主要功能 1. **提供属性注入** + 允许从不同的配置源(如属性文件、系统属性等)直接向 Spring 管理的 beans 中注入值。 2. **支持表达式**: - SpEL (Spring Expression Language) 表达式:例如,`#{systemProperties.myProp}` 可以从系统属性中获取名为 `myProp` 的值。 - 属性占位符:例如,`${my.app.myProp}` 可以从预定义的配置源,如 `application.properties` 或 `application.yml` 文件,获取名为 `my.app.myProp` 的属性值。 3. **动态值解析** + 与只能在启动时设置静态值相比,`@Value` 注解可以解析动态表达式,从而为字段或构造函数参数提供动态值。 4. **用于字段、方法参数、构造函数参数和注解** + 它可以被应用到这些元素上,以提供必要的值。 5. **与其他注解协同工作** + 尽管 `@Value` 本身是用于注入值的,但它经常与其他如 `@Component`、`@Service` 和 `@Controller` 之类的 Spring 注解一起使用,以创建完全由 Spring 管理和配置的 beans。 6. **与属性解析器配合** + 为了正确解析 `@Value` 中的表达式,Spring 应用上下文中需要有一个属性解析器,例如 `PropertySourcesPlaceholderConfigurer`。在 Spring Boot 项目中,这已经默认配置好了。 ### 五、最佳实践 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。 ```java public class ValueApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); } } ``` 这里使用`@Bean`注解,定义了一个Bean,是为了确保 `MyService` 被 Spring 容器执行,另外使用`@PropertySource`注解从类路径下的`application.properties`文件中加载属性。这意味着我们可以在这个文件中定义属性,然后在应用中使用`Environment`对象来访问它们。 ```java @Configuration @PropertySource("classpath:application.properties") public class MyConfiguration { @Bean public MyService myService(){ return new MyService(); } } ``` `application.properties`文件在`src/main/resources`目录中,并添加以下内容。 ```properties app.name=My Spring Application app.servers=server1,server2,server3 app.val1=10 app.val2=20 ``` `MyService`类,展示了如何使用`@Value`注解的五种不同方式进行属性注入。 ```java public class MyService implements InitializingBean { /** * 直接注入值 */ @Value("Some String Value") private String someString; /** * 从属性文件中注入值方式 */ @Value("${app.name}") private String appName; /** * 使用默认值方式 */ @Value("${app.description:我是默认值}") private String appDescription; /** * 注入列表和属性 */ @Value("#{'${app.servers}'.split(',')}") private List servers; /** * 使用Spring的SpEL */ @Value("#{${app.val1} + ${app.val2}}") private int sumOfValues; @Override public void afterPropertiesSet() throws Exception { System.out.println("直接注入值: " + someString); System.out.println("从属性文件中注入值: " + appName); System.out.println("使用默认值: " + appDescription); System.out.println("注入列表和属性: " + servers); System.out.println("使用Spring的SpEL: " + sumOfValues); } } ``` 运行结果发现, `@Value` 注解都被正确地解析并注入了预期的值。 ```java 直接注入值: Some String Value 从属性文件中注入值: My Spring Application 使用默认值: 我是默认值 注入列表和属性: [server1, server2, server3] 使用Spring的SpEL: 30 ``` ### 六、时序图 ~~~mermaid sequenceDiagram Title: @Value注解时序图 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:applyMergedBeanDefinitionPostProcessors(mbd,beanType,beanName)
应用Bean定义的后置处理器 AbstractAutowireCapableBeanFactory->>AutowiredAnnotationBeanPostProcessor:postProcessMergedBeanDefinition(beanDefinition,beanType,beanName)
处理已合并的Bean定义 AutowiredAnnotationBeanPostProcessor->>AutowiredAnnotationBeanPostProcessor:findAutowiringMetadata(beanName,clazz,pvs)
查找自动注入的元数据 AutowiredAnnotationBeanPostProcessor->>AutowiredAnnotationBeanPostProcessor:buildAutowiringMetadata(clazz)
构建自动注入的元数据 AutowiredAnnotationBeanPostProcessor->>ReflectionUtils:doWithLocalFields(clazz,fc)
处理类的本地字段 ReflectionUtils->>AutowiredAnnotationBeanPostProcessor:解析有@Value注解的字段 AutowiredAnnotationBeanPostProcessor->>ReflectionUtils:doWithLocalMethods(clazz,fc)
处理类的本地方法 ReflectionUtils->>AutowiredAnnotationBeanPostProcessor:解析有@Value注解的方法 AutowiredAnnotationBeanPostProcessor->>AutowiredAnnotationBeanPostProcessor:injectionMetadataCache.put(cacheKey, metadata)
将元数据存入缓存 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:populateBean(beanName,mbd,bw)
填充Bean的属性值 AbstractAutowireCapableBeanFactory->>AutowiredAnnotationBeanPostProcessor:postProcessProperties(pvs,bean,beanName)
后处理Bean的属性 AutowiredAnnotationBeanPostProcessor->>AutowiredAnnotationBeanPostProcessor:findAutowiringMetadata(beanName,clazz,pvs)
再次查找自动注入的元数据 Note right of AutowiredAnnotationBeanPostProcessor:
从缓存中获取注入的元数据 AutowiredAnnotationBeanPostProcessor->>InjectionMetadata:inject(bean, beanName, pvs)
执行实际的属性注入 InjectionMetadata->>AutowiredFieldElement:inject(target, beanName, pvs)
注入特定的字段元素 AutowiredFieldElement->>AutowiredFieldElement:resolveFieldValue(field,bean,beanName)
解析字段的值 AutowiredFieldElement->>DefaultListableBeanFactory:resolveDependency(desc, beanName, autowiredBeanNames, typeConverter)
解析字段的依赖 DefaultListableBeanFactory->>AutowiredFieldElement:返回解析后的value值
返回解析后的属性值 AutowiredFieldElement->>Field:field.set(bean, value)
设置Bean字段的值 ~~~ ### 七、源码分析 #### 前置条件 在Spring中,`AutowiredAnnotationBeanPostProcessor`是处理`@Value`等注解的关键类,它实现了下述两个接口。因此,为了深入理解`@Value`的工作方式,研究这个类是非常有用的。简而言之,为了完全理解`@Value`的工作机制,了解下述接口确实是必要的。这两个接口提供了对bean生命周期中关键阶段的干预,从而允许进行属性注入和其他相关的操作。 1. **`MergedBeanDefinitionPostProcessor`接口** + 此接口提供的`postProcessMergedBeanDefinition`方法允许后处理器修改合并后的bean定义。合并后的bean定义是一个已经考虑了所有父bean定义属性的bean定义。对于`@Value`注解的处理,这一步通常涉及到收集需要被解析的`@Value`注解信息并准备对其进行后续处理。 + 🔗 [MergedBeanDefinitionPostProcessor接口传送门](https://github.com/xuchengsheng/spring-reading/tree/master/spring-interface/spring-interface-mergedBeanDefinitionPostProcessor) 2. **`InstantiationAwareBeanPostProcessor`接口** + 此接口提供了几个回调方法,允许后处理器在bean实例化之前和实例化之后介入bean的创建过程。特别是,`postProcessProperties`方法允许后处理器对bean的属性进行操作。对于`@Value`注解,这通常涉及到实际地解析注解中的表达式或属性占位符,并将解析得到的值注入到bean中。 + 🔗 [InstantiationAwareBeanPostProcessor接口传送门](https://github.com/xuchengsheng/spring-reading/tree/master/spring-interface/spring-interface-instantiationAwareBeanPostProcessor) #### 收集阶段 在`org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition`方法中,主要确保给定的bean定义与其预期的自动装配元数据一致。 ```java @Override public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class beanType, String beanName) { // 对于给定的bean名称和类型,它首先尝试查找相关的InjectionMetadata,这可能包含了该bean的字段和方法的注入信息 InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null); // 使用找到的InjectionMetadata来验证bean定义中的配置成员是否与预期的注入元数据匹配。 metadata.checkConfigMembers(beanDefinition); } ``` 在`org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#findAutowiringMetadata`方法中,确保了始终为给定的bean名称和类获取最新和相关的`InjectionMetadata`,并利用缓存机制优化性能。 ```java private InjectionMetadata findAutowiringMetadata(String beanName, Class clazz, @Nullable PropertyValues pvs) { // 如果beanName为空,则使用类名作为缓存键。 String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName()); // 首先尝试从并发缓存中获取InjectionMetadata。 InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey); // 检查获取到的元数据是否需要刷新。 if (InjectionMetadata.needsRefresh(metadata, clazz)) { // 使用双重检查锁定确保线程安全。 synchronized (this.injectionMetadataCache) { metadata = this.injectionMetadataCache.get(cacheKey); if (InjectionMetadata.needsRefresh(metadata, clazz)) { // 如果有旧的元数据,清除它。 if (metadata != null) { metadata.clear(pvs); } // 为给定的类构建新的InjectionMetadata。 metadata = buildAutowiringMetadata(clazz); // 将新构建的元数据更新到缓存中。 this.injectionMetadataCache.put(cacheKey, metadata); } } } // 返回找到的或新构建的元数据。 return metadata; } ``` 在`org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#buildAutowiringMetadata`方法中,查找类及其所有父类中的字段和方法,以找出所有带有自动装配注解的字段和方法,并为它们创建一个统一的`InjectionMetadata`对象。 ```java private InjectionMetadata buildAutowiringMetadata(final Class clazz) { // 检查类是否含有自动装配注解,若无则直接返回空的InjectionMetadata。 if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) { return InjectionMetadata.EMPTY; } // 初始化存放注入元素的列表。 List elements = new ArrayList<>(); Class targetClass = clazz; do { // 当前类中要注入的元素列表。 final List currElements = new ArrayList<>(); // 处理类中的所有字段。 ReflectionUtils.doWithLocalFields(targetClass, field -> { // 查找字段上的自动装配注解。 MergedAnnotation ann = findAutowiredAnnotation(field); if (ann != null) { // 忽略静态字段。 if (Modifier.isStatic(field.getModifiers())) { if (logger.isInfoEnabled()) { logger.info("Autowired annotation is not supported on static fields: " + field); } return; } boolean required = determineRequiredStatus(ann); // 创建一个新的AutowiredFieldElement并加入到列表。 currElements.add(new AutowiredFieldElement(field, required)); } }); // 处理类中的所有方法。 ReflectionUtils.doWithLocalMethods(targetClass, method -> { Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) { return; } // 查找方法上的自动装配注解。 MergedAnnotation ann = findAutowiredAnnotation(bridgedMethod); if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) { // 忽略静态方法。 if (Modifier.isStatic(method.getModifiers())) { if (logger.isInfoEnabled()) { logger.info("Autowired annotation is not supported on static methods: " + method); } return; } // 只处理带参数的方法。 if (method.getParameterCount() == 0) { if (logger.isInfoEnabled()) { logger.info("Autowired annotation should only be used on methods with parameters: " + method); } } boolean required = determineRequiredStatus(ann); PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz); // 创建一个新的AutowiredMethodElement并加入到列表。 currElements.add(new AutowiredMethodElement(method, required, pd)); } }); // 将当前类的注入元素加入到总的注入元素列表的开头。 elements.addAll(0, currElements); // 处理父类。 targetClass = targetClass.getSuperclass(); } // 循环直至Object类。 while (targetClass != null && targetClass != Object.class); // 返回为元素列表创建的新的InjectionMetadata。 return InjectionMetadata.forElements(elements, clazz); } ``` 在`org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#autowiredAnnotationTypes`字段中,主要的用途是告诉`AutowiredAnnotationBeanPostProcessor`哪些注解它应该处理。当Spring容器解析bean定义并创建bean实例时,如果这个bean的字段、方法或构造函数上的注解被包含在这个`autowiredAnnotationTypes`集合中,那么`AutowiredAnnotationBeanPostProcessor`就会对它进行处理。 ```java public AutowiredAnnotationBeanPostProcessor() { this.autowiredAnnotationTypes.add(Value.class); // ... [代码部分省略以简化] } ``` #### 注入阶段 在`org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessProperties`方法中,用于处理bean属性的后处理,特别是通过`@Value`等注解进行的属性注入。 ```java @Override public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) { // 获取与bean名称和类相关的InjectionMetadata。 // 这包括该bean需要进行注入的所有字段和方法。 InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs); try { // 使用获取到的InjectionMetadata,实际进行属性的注入。 metadata.inject(bean, beanName, pvs); } // 如果在注入过程中出现BeanCreationException,直接抛出。 catch (BeanCreationException ex) { throw ex; } // 捕获其他异常,并以BeanCreationException的形式抛出,提供详细的错误信息。 catch (Throwable ex) { throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex); } // 返回原始的PropertyValues,因为这个方法主要关注依赖注入而不是修改属性。 return pvs; } ``` 在`org.springframework.beans.factory.annotation.InjectionMetadata#inject`方法中,主要目的是将所有需要注入的元素(例如带有`@Value`等注解的字段或方法)注入到目标bean中。 ```java public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable { // 获取已经检查的元素。通常,在初始化阶段,所有的元素都会被检查一次。 Collection checkedElements = this.checkedElements; // 如果已经有检查过的元素,则使用它们,否则使用所有注入的元素。 Collection elementsToIterate = (checkedElements != null ? checkedElements : this.injectedElements); // 如果有需要注入的元素... if (!elementsToIterate.isEmpty()) { // 遍历每个元素并注入到目标bean中。 for (InjectedElement element : elementsToIterate) { // 对每个元素(字段或方法)执行注入操作。 element.inject(target, beanName, pvs); } } } ``` 在`org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject`方法中,首先检查字段的值是否已经被缓存。如果已缓存,则从缓存中获取,否则重新解析。然后,它确保字段是可访问的(特别是对于私有字段),并将解析的值设置到目标bean的相应字段中。 ```java @Override protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable { // 步骤1. 获取代表带有@Autowired注解的字段的Field对象。 Field field = (Field) this.member; Object value; // 步骤2. 如果字段的值已经被缓存(即先前已解析过),则尝试从缓存中获取。 if (this.cached) { try { // 从缓存中获取已解析的字段值。 value = resolvedCachedArgument(beanName, this.cachedFieldValue); } catch (NoSuchBeanDefinitionException ex) { // 如果缓存中的bean已被意外删除 -> 重新解析。 value = resolveFieldValue(field, bean, beanName); } } else { // 步骤3. 如果字段值未被缓存,直接解析。 value = resolveFieldValue(field, bean, beanName); } // 步骤4. 如果解析到的值不为null... if (value != null) { // 步骤4.1. 使字段可访问,这是必要的,特别是当字段是private时。 ReflectionUtils.makeAccessible(field); // 步骤4.2. 实际将解析的值注入到目标bean的字段中。 field.set(bean, value); } } ``` 首先来到`org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject`方法中的步骤3。在`org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#resolveFieldValue`方法中,通过`beanFactory.resolveDependency`方法从Spring的bean工厂中解析字段的值。 ```java @Nullable private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) { // ... [代码部分省略以简化] Object value; try { // 通过`beanFactory.resolveDependency`方法从Spring的bean工厂中解析字段的值 value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter); } catch (BeansException ex) { throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex); } // ... [代码部分省略以简化] return value; } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency`方法中,首先尝试获取一个延迟解析代理。如果无法获得,它会进一步尝试解析依赖。`doResolveDependency` 是实际进行解析工作的方法。 ```java public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName, @Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException { // ... [代码部分省略以简化] Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary( descriptor, requestingBeanName); if (result == null) { result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter); } return result; } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency`方法中,首先从`DependencyDescriptor`获取注解值,然后处理其中的字符串属性占位符和SpEL表达式。最后,确保值根据目标字段或参数类型进行正确的类型转换,并将其注入相应的位置。 ```java @Nullable public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName, @Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException { // ... [其他代码部分省略以简化] try { // 尝试快速解析依赖 Object shortcut = descriptor.resolveShortcut(this); if (shortcut != null) { return shortcut; } // 获取依赖的类型 Class type = descriptor.getDependencyType(); // 步骤1. 获取依赖的建议值,例如@Value注解的值 Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor); // 如果建议的值是字符串类型 if (value instanceof String) { // 步骤2. 解析嵌入的值,如处理属性占位符 String strVal = resolveEmbeddedValue((String) value); // 获取与bean名称相关的BeanDefinition BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null); // 步骤3. 对Bean定义字符串进行评估,如处理SpEL表达式 value = evaluateBeanDefinitionString(strVal, bd); } TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter()); try { // 步骤4. 获取类型转换器并进行必要的类型转换 return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor()); } catch (UnsupportedOperationException ex) { // ... [其他代码部分省略以简化] } // ... [其他代码部分省略以简化] } // ... [其他代码部分省略以简化] } ``` 首先来到在`org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency`方法中的步骤1。在`org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver#getSuggestedValue`方法中,主要是用于解析与`DependencyDescriptor`相关的注解值,特别是`@Value`注解。如果字段或方法参数上有`@Value`注解,它会从注解中提取相应的值或表达式。 ```java @Override @Nullable public Object getSuggestedValue(DependencyDescriptor descriptor) { // 从描述符的注解中查找@Value注解提供的值 Object value = findValue(descriptor.getAnnotations()); // 如果在描述符的注解中没有找到,检查是否存在与此描述符关联的方法参数 if (value == null) { MethodParameter methodParam = descriptor.getMethodParameter(); if (methodParam != null) { // 如果存在方法参数,再从方法参数的注解中查找@Value提供的值 value = findValue(methodParam.getMethodAnnotations()); } } // 返回找到的值,如果没有找到则返回null return value; } ``` 在`org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver#findValue`方法中,目标是在提供的注解集合中找到并返回`@Value`注解的值。如果没有找到,它会返回null。 ```java protected Object findValue(Annotation[] annotationsToSearch) { if (annotationsToSearch.length > 0) { // qualifier annotations have to be local AnnotationAttributes attr = AnnotatedElementUtils.getMergedAnnotationAttributes( AnnotatedElementUtils.forAnnotations(annotationsToSearch), this.valueAnnotationType); if (attr != null) { return extractValue(attr); } } return null; } ``` 在`org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver#extractValue`方法中,目的是从`AnnotationAttributes`对象中直接提取`@Value`注解的值。如果没有提供值,它会抛出异常。 ```java protected Object extractValue(AnnotationAttributes attr) { Object value = attr.get(AnnotationUtils.VALUE); if (value == null) { throw new IllegalStateException("Value annotation must have a value attribute"); } return value; } ``` 当我们使用 `@Value("${app.description:我是默认值}")` 在你的字段上时,Spring 会在运行时尝试解析这个属性占位符。当 Spring 容器处理这个字段的注入时,它会使用 `QualifierAnnotationAutowireCandidateResolver`(或其他相关的后处理器)来获取并解析这个属性值。在我们的最佳实践下,`extractValue` 方法就是从注解属性中提取该属性占位符的逻辑,即返回值为 `"${app.description:我是默认值}"`。这个值随后会被 Spring 的属性解析器进一步处理,解析真实的值或使用默认值,并最终注入到 `appDescription` 字段中。 ```java ${app.description:我是默认值} ``` 然后我们来到在`org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency`方法中的步骤2。在`org.springframework.beans.factory.support.AbstractBeanFactory#resolveEmbeddedValue`方法中,用于解析给定字符串中的内嵌值。它遍历所有注册的`StringValueResolver`解析器,对给定的字符串值进行连续解析,以处理可能存在的多重内嵌值或引用。例如,如果字符串中有一个`${property}`形式的属性,它可以通过注册的解析器进行处理和解析为实际的属性值。 ```java @Override @Nullable public String resolveEmbeddedValue(@Nullable String value) { // 初始检查:如果提供的值为null,则直接返回null if (value == null) { return null; } String result = value; // 遍历所有的内嵌值解析器 for (StringValueResolver resolver : this.embeddedValueResolvers) { // 使用当前解析器解析result中的值 result = resolver.resolveStringValue(result); // 如果解析后的值为null,则直接返回null if (result == null) { return null; } } // 返回所有解析器处理后的最终值 return result; } ``` 然后我们来到在`org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency`方法中的步骤3。在`org.springframework.beans.factory.support.AbstractBeanFactory#evaluateBeanDefinitionString`方法中,用于评估给定的字符串值,特别是处理可能包含Spring表达式语言 (SpEL) 表达式的字符串。首先,它检查是否有一个`beanExpressionResolver`可用来解析SpEL。如果有,它可能会获取bean定义的作用域(如果提供了bean定义),然后使用`beanExpressionResolver`对字符串值进行评估,并考虑到相关的作用域上下文。 ```java @Nullable protected Object evaluateBeanDefinitionString(@Nullable String value, @Nullable BeanDefinition beanDefinition) { // 如果没有设置bean表达式解析器,直接返回原始值 if (this.beanExpressionResolver == null) { return value; } Scope scope = null; // 如果提供了bean定义 if (beanDefinition != null) { // 获取bean的作用域 String scopeName = beanDefinition.getScope(); // 如果作用域名称不为空,则尝试从已注册的作用域中获取对应的作用域 if (scopeName != null) { scope = getRegisteredScope(scopeName); } } // 使用bean表达式解析器解析提供的值,并返回结果 // 这可以处理例如使用Spring EL的情况 return this.beanExpressionResolver.evaluate(value, new BeanExpressionContext(this, scope)); } ``` 在`org.springframework.context.expression.StandardBeanExpressionResolver#evaluate`方法中,主要目的是解析并评估给定的值(可能是一个Spring EL表达式)。为了提高性能,它使用缓存来存储先前解析的表达式和评估上下文。此方法首先从缓存中检索或解析表达式,然后准备一个评估上下文,并使用它评估表达式。这个评估上下文被配置为能够访问与Spring容器相关的各种内容,如beans、环境属性等。 ```java @Override @Nullable public Object evaluate(@Nullable String value, BeanExpressionContext evalContext) throws BeansException { // 如果提供的值为空或没有内容,直接返回该值 if (!StringUtils.hasLength(value)) { return value; } try { // 从缓存中尝试获取表达式 Expression expr = this.expressionCache.get(value); // 如果缓存中没有表达式,则使用表达式解析器解析该值,并将其放入缓存中 if (expr == null) { expr = this.expressionParser.parseExpression(value, this.beanExpressionParserContext); this.expressionCache.put(value, expr); } // 尝试从缓存中获取评估上下文 StandardEvaluationContext sec = this.evaluationCache.get(evalContext); // 如果缓存中没有评估上下文,则创建一个新的,并进行一些初始化配置 if (sec == null) { sec = new StandardEvaluationContext(evalContext); // 添加各种属性访问器以支持对特定类型的属性的访问 sec.addPropertyAccessor(new BeanExpressionContextAccessor()); sec.addPropertyAccessor(new BeanFactoryAccessor()); sec.addPropertyAccessor(new MapAccessor()); sec.addPropertyAccessor(new EnvironmentAccessor()); // 设置bean解析器和类型定位器 sec.setBeanResolver(new BeanFactoryResolver(evalContext.getBeanFactory())); sec.setTypeLocator(new StandardTypeLocator(evalContext.getBeanFactory().getBeanClassLoader())); // 如果有可用的转换服务,则设置类型转换器 ConversionService conversionService = evalContext.getBeanFactory().getConversionService(); if (conversionService != null) { sec.setTypeConverter(new StandardTypeConverter(conversionService)); } // 自定义评估上下文,允许子类提供额外的配置 customizeEvaluationContext(sec); // 将创建的评估上下文放入缓存 this.evaluationCache.put(evalContext, sec); } // 使用已准备好的评估上下文评估表达式并返回结果 return expr.getValue(sec); } catch (Throwable ex) { // 如果在解析或评估过程中出现任何异常,抛出BeanExpressionException throw new BeanExpressionException("Expression parsing failed", ex); } } ``` 然后我们来到在`org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency`方法中的步骤4。在`org.springframework.beans.TypeConverterSupport#convertIfNecessary(value,requiredType,typeDescriptor)`方法中,又重新委托给`typeConverterDelegate`进行实际的转换工作 ```java @Nullable @Override public T convertIfNecessary(@Nullable Object value, @Nullable Class requiredType, @Nullable TypeDescriptor typeDescriptor) throws TypeMismatchException { Assert.state(this.typeConverterDelegate != null, "No TypeConverterDelegate"); try { // 委托给typeConverterDelegate进行实际的转换工作 return this.typeConverterDelegate.convertIfNecessary(null, null, value, requiredType, typeDescriptor); } catch (ConverterNotFoundException | IllegalStateException ex) { throw new ConversionNotSupportedException(value, requiredType, ex); } catch (ConversionException | IllegalArgumentException ex) { throw new TypeMismatchException(value, requiredType, ex); } } ``` 在`org.springframework.beans.TypeConverterDelegate#convertIfNecessary(propertyName,oldValue,newValue,requiredType,typeDescriptor)`方法中,负责将一个值转换为必需的类型。首先,它会尝试查找对应的自定义编辑器。如果没有找到编辑器但设置了自定义的转换服务,它会尝试使用此服务进行转换。如果上述两步都失败,该方法还会尝试执行一些标准的转换规则,例如从字符串到枚举或从数字到其他数字类型的转换。如果所有尝试都失败,该方法会抛出相应的异常,指出不能执行所需的转换。 ```java public T convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue, @Nullable Class requiredType, @Nullable TypeDescriptor typeDescriptor) throws IllegalArgumentException { // 查找此类型的自定义编辑器 PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName); // 用于捕获ConversionService尝试失败的异常 ConversionFailedException conversionAttemptEx = null; // 没有自定义编辑器,但是否指定了自定义ConversionService? ConversionService conversionService = this.propertyEditorRegistry.getConversionService(); if (editor == null && conversionService != null && newValue != null && typeDescriptor != null) { TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue); if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) { try { return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor); } catch (ConversionFailedException ex) { conversionAttemptEx = ex; // 记录转换尝试失败 } } } Object convertedValue = newValue; // 如果值不是所需类型,进行转换 if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) { if (typeDescriptor != null && requiredType != null && Collection.class.isAssignableFrom(requiredType) && convertedValue instanceof String) { TypeDescriptor elementTypeDesc = typeDescriptor.getElementTypeDescriptor(); if (elementTypeDesc != null) { Class elementType = elementTypeDesc.getType(); if (Class.class == elementType || Enum.class.isAssignableFrom(elementType)) { convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue); } } } if (editor == null) { editor = findDefaultEditor(requiredType); // 如果没有自定义编辑器,找默认的 } convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor); // 进行转换 } // 对于特定情况尝试标准类型转换,如字符串到枚举、数字转换等 boolean standardConversion = false; if (requiredType != null) { if (convertedValue != null) { // 若目标类型为Object,则直接返回转换值 if (Object.class == requiredType) { return (T) convertedValue; } // 若目标类型为数组,进行数组转换 else if (requiredType.isArray()) { if (convertedValue instanceof String && Enum.class.isAssignableFrom(requiredType.getComponentType())) { convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue); } return (T) convertToTypedArray(convertedValue, propertyName, requiredType.getComponentType()); } // 如果是Collection或Map,则尝试转换集合或映射的内容 else if (convertedValue instanceof Collection) { convertedValue = convertToTypedCollection((Collection) convertedValue, propertyName, requiredType, typeDescriptor); standardConversion = true; } else if (convertedValue instanceof Map) { convertedValue = convertToTypedMap((Map) convertedValue, propertyName, requiredType, typeDescriptor); standardConversion = true; } if (convertedValue.getClass().isArray() && Array.getLength(convertedValue) == 1) { convertedValue = Array.get(convertedValue, 0); standardConversion = true; } if (String.class == requiredType && ClassUtils.isPrimitiveOrWrapper(convertedValue.getClass())) { return (T) convertedValue.toString(); } else if (convertedValue instanceof String && !requiredType.isInstance(convertedValue)) { String trimmedValue = ((String) convertedValue).trim(); if (requiredType.isEnum() && trimmedValue.isEmpty()) { return null; } convertedValue = attemptToConvertStringToEnum(requiredType, trimmedValue, convertedValue); standardConversion = true; } else if (convertedValue instanceof Number && Number.class.isAssignableFrom(requiredType)) { convertedValue = NumberUtils.convertNumberToTargetClass((Number) convertedValue, (Class) requiredType); standardConversion = true; } } else if (requiredType == Optional.class) { convertedValue = Optional.empty(); } // 如果经过上述所有转换后,值仍不匹配目标类型,则抛出异常 if (!ClassUtils.isAssignableValue(requiredType, convertedValue)) { if (conversionAttemptEx != null) { throw conversionAttemptEx; } else if (conversionService != null && typeDescriptor != null) { TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue); if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) { return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor); } } throw new IllegalStateException("转换失败"); // 实际异常消息会更详细,这里简化了 } } if (conversionAttemptEx != null) { throw conversionAttemptEx; } return (T) convertedValue; } ``` ### 八、注意事项 1. **SpEL表达式** + `@Value`可以用来解析Spring Expression Language (SpEL) 表达式。确保我们的表达式是正确的,以防止运行时错误。 2. **默认值** + 我们可以为`@Value`注解提供默认值,以防止某个属性在属性文件中未被定义。例如:`@Value("${some.property:default}")`。 3. **类型转换** + 确保`@Value`提供的值可以被转换为字段或方法参数的类型。Spring会尝试自动进行这种转换,但不一定总是成功。 4. **不适用于复杂类型** + 尽管`@Value`可以用于简单的类型(如字符串、整数、枚举等),但不应用于复杂的bean注入,这时应该使用`@Autowired`或`@Inject`。 5. **不可用于`BeanPostProcessor`或`BeanFactoryPostProcessor`** + `@Value`注解在`BeanPostProcessor`或`BeanFactoryPostProcessor`实现中是不起作用的,因为它们在Spring容器生命周期中的处理时机早于`@Value`的处理。 6. **占位符解析器的配置** + 要使用属性占位符(如`${property.name}`),需要确保已配置了`PropertySourcesPlaceholderConfigurer`或`PropertyPlaceholderConfigurer`。 7. **环境变量与系统属性** + 我们可以使用`@Value`来引用环境变量或系统属性,例如:`@Value("${JAVA_HOME}")`。 8. **防止注入敏感信息** + 不要使用`@Value`来注入敏感信息,如密码,除非它们是适当加密的。考虑使用专门的解决方案,如Spring Cloud Config的Vault集成。 9. **循环依赖** + 尽管与`@Autowired`不同,但需要注意的是,使用`@Value`可能间接导致循环依赖,尤其是当注入的值是其他bean的属性时。 10. **性能考虑** + 大量使用SpEL表达式可能对性能产生轻微的影响,因为这些表达式需要在运行时进行解析。 ### 九、总结 #### 最佳实践总结 1. **启动类入口**: - 使用`AnnotationConfigApplicationContext`来启动Spring上下文,该上下文支持基于Java注解的配置。 - 在创建上下文时,为其提供了`MyConfiguration`作为配置类。 2. **配置类**: - `MyConfiguration`类标记为`@Configuration`,表示它提供了bean定义的配置信息。 - 使用`@PropertySource`指定一个属性文件`application.properties`来为上下文加载属性。 - 定义了一个bean:`MyService`,确保其在Spring容器中被创建和初始化。 3. **属性文件**: - 在`application.properties`文件中定义了几个属性,这些属性可以在应用程序中使用。 4. **属性注入**: - 在`MyService`类中,展示了如何使用`@Value`注解进行五种不同方式的属性注入,从直接注入字符串值到使用SpEL表达式。 5. **注入结果的验证**: - 实现`InitializingBean`接口并重写`afterPropertiesSet`方法来验证注入的属性值。 - 运行应用后,该方法会打印出所有注入属性的值,从而验证`@Value`注解正确地解析并注入了预期的值。 #### 源码分析总结 1. **核心后处理器**: + `AutowiredAnnotationBeanPostProcessor`是处理`@Value`等注解的主要后处理器。它实现了两个关键的接口,`MergedBeanDefinitionPostProcessor`和`InstantiationAwareBeanPostProcessor`,这两个接口允许在bean的生命周期中的关键阶段进行干预,为属性注入提供了机制。 2. **收集阶段**: - 在`postProcessMergedBeanDefinition`方法中,`AutowiredAnnotationBeanPostProcessor`确保bean的定义与预期的自动装配元数据匹配。 - `findAutowiringMetadata`方法确保为给定的bean名称和类获取相关的`InjectionMetadata`,并利用缓存机制优化性能。 - `buildAutowiringMetadata`方法检查类及其所有父类,确定带有`@Autowired`、`@Value`等注解的字段和方法,并为这些元素创建一个统一的`InjectionMetadata`对象。 3. **注入阶段**: + `postProcessProperties`方法用于处理bean的属性的后处理,特别是注入由`@Value`等注解标记的属性。 + `InjectionMetadata#inject`方法用于将所有需要注入的元素(例如带有`@Value`的字段)注入到目标bean中。 + `AutowiredFieldElement#inject`方法处理具体的字段注入,包括解析`@Value`注解中的值。 + `DefaultListableBeanFactory#resolveDependency`方法从Spring的bean工厂中解析字段的值。 + `DefaultListableBeanFactory#doResolveDependency`方法是实际解析工作的主要场所,涉及到处理`@Value`注解中的字符串属性占位符和SpEL表达式,并确保值经过正确的类型转换。 ================================================ FILE: spring-annotation/spring-annotation-value/pom.xml ================================================ spring-annotation com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-annotation-value 11 11 ================================================ FILE: spring-annotation/spring-annotation-value/src/main/java/com/xcs/spring/ValueApplication.java ================================================ package com.xcs.spring; import com.xcs.spring.config.MyConfiguration; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年08月07日 16时21分 **/ public class ValueApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); } } ================================================ FILE: spring-annotation/spring-annotation-value/src/main/java/com/xcs/spring/config/MyConfiguration.java ================================================ package com.xcs.spring.config; import com.xcs.spring.service.MyService; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; /** * @author xcs * @date 2023年10月13日 14时54分 **/ @Configuration @PropertySource("classpath:application.properties") public class MyConfiguration { @Bean public MyService myService(){ return new MyService(); } } ================================================ FILE: spring-annotation/spring-annotation-value/src/main/java/com/xcs/spring/service/MyService.java ================================================ package com.xcs.spring.service; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Value; import java.util.List; /** * @author xcs * @date 2023年10月13日 14时55分 **/ public class MyService implements InitializingBean { /** * 直接注入值 */ @Value("Some String Value") private String someString; /** * 从属性文件中注入值方式 */ @Value("${app.name}") private String appName; /** * 使用默认值方式 */ @Value("${app.description:我是默认值}") private String appDescription; /** * 注入列表和属性 */ @Value("#{'${app.servers}'.split(',')}") private List servers; /** * 使用Spring的SpEL */ @Value("#{${app.val1} + ${app.val2}}") private int sumOfValues; @Value("${myapp.names[0]}") private String firstName; @Override public void afterPropertiesSet() throws Exception { System.out.println("直接注入值: " + someString); System.out.println("从属性文件中注入值: " + appName); System.out.println("使用默认值: " + appDescription); System.out.println("注入列表和属性: " + servers); System.out.println("使用Spring的SpEL: " + sumOfValues); System.out.println("firstName: " + firstName); } } ================================================ FILE: spring-annotation/spring-annotation-value/src/main/resources/application.properties ================================================ app.name=My Spring Application app.servers=server1,server2,server3 app.val1=10 app.val2=20 myapp.names[0]=Alice myapp.names[1]=Bob myapp.names[2]=Charlie ================================================ FILE: spring-aop/pom.xml ================================================ spring-reading com.xcs.spring 0.0.1-SNAPSHOT pom spring-aop-advisor spring-aop-pointcut spring-aop-advisorAdapter spring-aop-targetSource spring-aop-aopProxy spring-aop-classFilter spring-aop-methodMatcher spring-aop-jdkProxy spring-aop-cglibProxy spring-aop-enableAspectJAutoProxy spring-aop-enableLoadTimeWeaving spring-aop-advice-methodInterceptor spring-aop-advice-methodBeforeAdvice spring-aop-advice-afterReturningAdvice spring-aop-advice-throwsAdvice spring-aop-advice-introductionInterceptor spring-aop-aopProxyFactory spring-aop-annotationAwareAspectJAutoProxyCreator spring-aop-aspectInstanceFactory spring-aop-metadataAwareAspectInstanceFactory spring-aop-aspectJAdvisorFactory spring-aop-beanFactoryAspectJAdvisorsBuilder spring-aop-beanFactoryAdvisorRetrievalHelper spring-aop-proxyFactory spring-aop-advisorChainFactory spring-aop-advisorAdapterRegistry spring-aop-advised spring-aop-aopContext spring-aop-targetSourceCreator spring-aop-exposeInvocationInterceptor spring-aop-advice spring-aop-proxyMethodInvocation 4.0.0 spring-aop ================================================ FILE: spring-aop/spring-aop-advice/README.md ================================================ ## Advice - [Advice](#advice) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、接口源码](#四接口源码) - [五、子接口](#五子接口) - [六、类关系图](#六类关系图) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 `Advice`接口是Spring AOP中的核心接口之一,用于定义在切面逻辑中要执行的操作。它允许我们在目标方法执行前、执行后、抛出异常时等不同的连接点上添加自定义的行为。`Advice`接口的实现类可以通过方法拦截器(MethodInterceptor)、前置通知(BeforeAdvice)、后置通知(AfterReturningAdvice)、异常通知(ThrowsAdvice)等方式来实现不同类型的通知逻辑。 ### 三、主要功能 1. **定义通知逻辑** + 允许我们定义在目标方法执行前、执行后、抛出异常时等不同连接点上执行的操作。 2. **支持不同类型的通知** + `Advice`接口的实现类可以实现不同类型的通知逻辑,如前置通知、后置通知、环绕通知、异常通知等。 3. **与切点结合** + `Advice`通常与切点(Pointcut)结合使用,以确定通知应该在哪些连接点上执行。 4. **应用于Advisor** + `Advice`通常作为`Advisor`的一部分,与切点结合,以实现切面的逻辑。 ### 四、接口源码 `Advice`接口是Spring AOP中的一个标签接口,用于定义各种类型的通知,例如拦截器。通过实现该接口,我们可以定义在方法执行前、执行后、抛出异常时等不同连接点上执行的操作,从而实现对应用程序行为的干预和控制。 ```java /** * Advice的标签接口。实现可以是任何类型的通知,例如拦截器。 * * 该接口用于定义通知。通知可以是在方法执行前、执行后、抛出异常时等不同连接点上执行的操作。 * 实现该接口的类可以是拦截器(Interceptors)等任何类型的通知。 * * @author Rod Johnson * @version $Id: Advice.java,v 1.1 2004/03/19 17:02:16 johnsonr Exp $ */ public interface Advice { } ``` ### 五、子接口 1. **AfterAdvice(后置通知)** + 是一个标记接口,用于表示后置通知的类型。 2. **AfterReturningAdvice(返回后通知)** + 用于在目标方法成功执行并返回结果后执行自定义逻辑。 3. **BeforeAdvice(前置通知)** + 用于在目标方法执行前执行自定义逻辑。 4. **ConstructorInterceptor(构造器拦截器)** + 实现该接口的类可以在目标对象的构造器被调用时执行自定义逻辑。 5. **Interceptor(拦截器)** + 是一个标记接口,表示通用的拦截器类型,通常用于包装方法调用。 6. **IntroductionInterceptor(引介拦截器)** + 实现该接口的类可以在目标对象上添加新的方法和属性,用于实现AOP引介功能。 7. **MethodBeforeAdvice(方法前置通知)** + 用于在目标方法执行前执行自定义逻辑。 8. **MethodInterceptor(方法拦截器)** + 实现该接口的类可以在目标方法执行前、执行后以及抛出异常时进行拦截,并执行自定义的逻辑。 9. **ThrowsAdvice(异常通知)** + 用于在目标方法抛出异常时执行自定义逻辑。 ### 六、类关系图 ~~~mermaid classDiagram direction BT class Advice { <> } class AfterAdvice { <> } class AfterReturningAdvice { <> } class BeforeAdvice { <> } class ConstructorInterceptor { <> } class Interceptor { <> } class IntroductionInterceptor { <> } class MethodBeforeAdvice { <> } class MethodInterceptor { <> } class ThrowsAdvice { <> } AfterAdvice --> Advice AfterReturningAdvice --> AfterAdvice BeforeAdvice --> Advice ConstructorInterceptor --> Interceptor Interceptor --> Advice IntroductionInterceptor --> Advice IntroductionInterceptor --> MethodInterceptor MethodBeforeAdvice --> BeforeAdvice MethodInterceptor --> Interceptor ThrowsAdvice --> AfterAdvice ~~~ ================================================ FILE: spring-aop/spring-aop-advice/pom.xml ================================================ com.xcs.spring spring-aop 0.0.1-SNAPSHOT 4.0.0 spring-aop-advice ================================================ FILE: spring-aop/spring-aop-advice-afterReturningAdvice/README.md ================================================ ## AfterReturningAdvice - [AfterReturningAdvice](#afterreturningadvice) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、接口源码](#四接口源码) - [五、主要实现](#五主要实现) - [六、类关系图](#六类关系图) - [七、最佳实践](#七最佳实践) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 `AfterReturningAdvice`接口是Spring AOP框架中的一个核心接口,用于在目标方法执行后拦截并执行自定义逻辑。通过实现该接口的`afterReturning`方法,可以在目标方法成功返回结果后进行一些操作。 ### 三、主要功能 1. **日志记录** + 记录目标方法的执行情况,如方法名称、参数值、返回结果等,以便跟踪应用程序的运行状态。 2. **性能监控** + 统计目标方法的执行时间,分析应用程序的性能瓶颈,优化程序性能。 3. **缓存处理** + 在方法返回结果后,将结果缓存起来,以提高后续相同请求的响应速度。 4. **统一处理** + 执行一些与业务逻辑无关的统一处理,如资源释放、清理等。 ### 四、接口源码 `AfterReturningAdvice`接口主要用于在方法正常返回时执行后置通知。后置通知可以查看方法的返回值,但不能修改它,并且仅在方法正常返回时被调用,不会在抛出异常时被调用。该接口提供了一个`afterReturning`方法,在方法成功返回后进行回调,其中包含了返回值、被调用的方法、方法的参数以及方法调用的目标对象等信息。 ```java /** * 后置返回通知仅在方法正常返回时被调用,如果抛出异常则不会被调用。这样的通知可以查看方法的返回值,但不能修改它。 * * 作者:Rod Johnson * @see MethodBeforeAdvice * @see ThrowsAdvice */ public interface AfterReturningAdvice extends AfterAdvice { /** * 在给定方法成功返回后的回调。 * @param returnValue 方法返回的值,如果有的话 * @param method 被调用的方法 * @param args 方法的参数 * @param target 方法调用的目标对象。可能为{@code null}。 * @throws Throwable 如果此对象希望中止调用。如果方法签名允许,将返回任何抛出的异常给调用者。否则,异常将被包装为运行时异常。 */ void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable; } ``` ### 五、主要实现 1. **AspectJAfterReturningAdvice** + 实现了返回后通知,使用 AspectJ 风格定义的通知,用于在目标方法成功执行并返回结果后执行额外的逻辑。 ### 六、类关系图 ~~~mermaid classDiagram direction BT class Advice { <> } class AfterAdvice { <> } class AfterReturningAdvice { <> } class AspectJAfterReturningAdvice AfterAdvice --> Advice AfterReturningAdvice --> AfterAdvice AspectJAfterReturningAdvice ..> Advice AspectJAfterReturningAdvice ..> AfterAdvice AspectJAfterReturningAdvice ..> AfterReturningAdvice ~~~ ### 七、最佳实践 使用Spring AOP中的后置返回通知(AfterReturningAdvice)。首先,创建了一个代理工厂(ProxyFactory)并指定目标对象(MyService)。然后,创建了一个后置返回通知(MyAfterReturningAdvice)并添加到代理工厂中。接着,通过代理工厂获取代理对象,并调用代理对象的方法。 ```java public class AfterReturningAdviceDemo { public static void main(String[] args) { // 创建代理工厂&创建目标对象 ProxyFactory proxyFactory = new ProxyFactory(new MyService()); // 创建通知 proxyFactory.addAdvice(new MyAfterReturningAdvice()); // 获取代理对象 MyService proxy = (MyService) proxyFactory.getProxy(); // 调用代理对象的方法 proxy.foo(); } } ``` `MyAfterReturningAdvice`的类,它实现了Spring AOP框架中的`AfterReturningAdvice`接口。在`afterReturning`方法中,当目标方法成功返回结果时,它将打印出目标方法的名称以及返回的值。 ```java public class MyAfterReturningAdvice implements AfterReturningAdvice { @Override public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println("After Method " + method.getName()); } } ``` `MyService` 类是一个简单的服务类,其中包含了一个名为 `foo()` 的方法。在上下文中,`MyService` 类被用作目标对象,即需要被拦截和增强的对象。 ```java public class MyService { public void foo() { System.out.println("foo..."); } } ``` 运行结果,成功地执行了代理对象的`foo()`方法,并在方法执行完成后,后置返回通知`MyAfterReturningAdvice`被触发。 ```java foo... After Method foo ``` ================================================ FILE: spring-aop/spring-aop-advice-afterReturningAdvice/pom.xml ================================================ com.xcs.spring spring-aop 0.0.1-SNAPSHOT 4.0.0 spring-aop-advice-afterReturningAdvice ================================================ FILE: spring-aop/spring-aop-advice-afterReturningAdvice/src/main/java/com/xcs/spring/AfterReturningAdviceDemo.java ================================================ package com.xcs.spring; import org.springframework.aop.framework.ProxyFactory; public class AfterReturningAdviceDemo { public static void main(String[] args) { // 创建代理工厂&创建目标对象 ProxyFactory proxyFactory = new ProxyFactory(new MyService()); // 创建通知 proxyFactory.addAdvice(new MyAfterReturningAdvice()); // 获取代理对象 MyService proxy = (MyService) proxyFactory.getProxy(); // 调用代理对象的方法 proxy.foo(); } } ================================================ FILE: spring-aop/spring-aop-advice-afterReturningAdvice/src/main/java/com/xcs/spring/MyAfterReturningAdvice.java ================================================ package com.xcs.spring; import org.springframework.aop.AfterReturningAdvice; import java.lang.reflect.Method; public class MyAfterReturningAdvice implements AfterReturningAdvice { @Override public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println("After Method " + method.getName()); } } ================================================ FILE: spring-aop/spring-aop-advice-afterReturningAdvice/src/main/java/com/xcs/spring/MyService.java ================================================ package com.xcs.spring; public class MyService { public void foo() { System.out.println("foo..."); } } ================================================ FILE: spring-aop/spring-aop-advice-introductionInterceptor/README.md ================================================ ## IntroductionInterceptor - [IntroductionInterceptor](#introductioninterceptor) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、接口源码](#四接口源码) - [五、主要实现](#五主要实现) - [六、类关系图](#六类关系图) - [七、最佳实践](#七最佳实践) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 `IntroductionInterceptor` 接口是 Spring AOP 中的一个关键接口,用于实现引介(Introduction)功能,允许向目标对象动态地添加新的方法或属性,而无需修改目标类的代码,从而实现横切关注点的功能,如日志记录、事务管理等。 ### 三、主要功能 1. **引介新的接口或类** + 通过实现 `implementsInterface()` 方法,在目标对象的方法调用之前,向目标对象引介新的接口或类,从而使目标对象具有额外的功能或属性。 ### 四、接口源码 `IntroductionInterceptor` 接口,它是 AOP 联盟 `MethodInterceptor` 的子接口,允许拦截器实现额外的接口,并通过使用该拦截器的代理对象来使用这些接口。`IntroductionInterceptor` 接口体现了 AOP 中的引介(Introduction)概念,通过引介,可以将额外的功能添加到目标对象中,类似于混合(mixins)的概念,使得可以构建复合对象,实现类似于 Java 中多继承的目标。 ```java /** * AOP联盟 MethodInterceptor 的子接口,允许拦截器实现额外的接口,并通过使用该拦截器的代理对象来使用这些接口。这是一个基本的AOP概念,称为引介。 * *

引介通常是混合,允许构建能够实现多继承目标的复合对象。 * * @author Rod Johnson * @see DynamicIntroductionAdvice */ public interface IntroductionInterceptor extends MethodInterceptor, DynamicIntroductionAdvice { } ``` ### 五、主要实现 1. **DelegatingIntroductionInterceptor** + `DelegatingIntroductionInterceptor` 是 Spring AOP 提供的通用引介拦截器,允许我们定义自定义的引介逻辑,并在需要时将其应用于目标对象。通过配置,可以动态地向目标对象添加新的方法或属性,而不必修改目标类的代码,提高了代码的可维护性和灵活性。 2. **DelegatePerTargetObjectIntroductionInterceptor** + `DelegatePerTargetObjectIntroductionInterceptor` 是 `DelegatingIntroductionInterceptor` 的子类,为每个目标对象创建一个独立的引介代理对象。这意味着每个目标对象都可以拥有自己独立的引介逻辑,而不会受到其他目标对象的影响。这种灵活性特别适用于需要为不同的目标对象动态添加不同功能或属性的场景,提供了更高级的定制能力。 ### 六、类关系图 ~~~mermaid classDiagram direction BT class Advice { <> } class DelegatePerTargetObjectIntroductionInterceptor class DelegatingIntroductionInterceptor class DynamicIntroductionAdvice { <> } class Interceptor { <> } class IntroductionInterceptor { <> } class MethodInterceptor { <> } DelegatePerTargetObjectIntroductionInterceptor ..> IntroductionInterceptor DelegatingIntroductionInterceptor ..> IntroductionInterceptor DynamicIntroductionAdvice --> Advice Interceptor --> Advice IntroductionInterceptor --> DynamicIntroductionAdvice IntroductionInterceptor --> MethodInterceptor MethodInterceptor --> Interceptor ~~~ ### 七、最佳实践 使用 Spring AOP 中的引介功能。它创建了一个代理工厂,并通过设置强制使用 CGLIB 代理来创建代理对象。然后,它添加了一个通知器,将自定义的引介通知(`MyMonitoringIntroductionAdvice`)应用于目标对象(`MyService` 类),使得目标对象实现了 `MyMonitoringCapable` 接口。最后,它调用了代理对象的方法,并在必要时启用了监控功能,展示了如何在运行时动态地向目标对象引入新的功能。 ```java public class IntroductionInterceptorDemo { public static void main(String[] args) { // 创建代理工厂&创建目标对象 ProxyFactory proxyFactory = new ProxyFactory(new MyService()); // 强制私用CGLIB proxyFactory.setProxyTargetClass(true); // 创建通知 proxyFactory.addAdvisor(new DefaultIntroductionAdvisor(new MyMonitoringIntroductionAdvice(), MyMonitoringCapable.class)); // 创建代理对象 MyService proxy = (MyService) proxyFactory.getProxy(); // 调用代理对象的方法 proxy.foo(); // 开始监控 ((MyMonitoringCapable) proxy).toggleMonitoring(); // 再次调用代理对象的方法 proxy.foo(); } } ``` `MyMonitoringIntroductionAdvice` 类是一个实现了 `DelegatingIntroductionInterceptor` 接口和 `MyMonitoringCapable` 接口的自定义引介通知类。它具有一个 `active` 属性来表示监控是否处于激活状态,并提供了一个方法 `toggleMonitoring()` 来启用监控功能。在被监控的方法被调用时,如果监控处于激活状态,该类会输出相应的日志信息,包括方法执行时间等。通过继承 `doProceed()` 方法,它能够在方法执行前后添加自定义逻辑,实现了监控功能的动态引入。 ```java public class MyMonitoringIntroductionAdvice extends DelegatingIntroductionInterceptor implements MyMonitoringCapable { private boolean active = false; public void setActive(boolean active) { this.active = active; } @Override public void toggleMonitoring() { setActive(true); } // 当被监控的方法被调用时,如果监控处于激活状态,则输出日志 @Override protected Object doProceed(MethodInvocation mi) throws Throwable { if (this.active) { System.out.println("[开启监控" + mi.getMethod().getName() + "]"); long startTime = System.currentTimeMillis(); Object result = super.doProceed(mi); long endTime = System.currentTimeMillis(); System.out.println("[结束监控" + mi.getMethod().getName() + "] 耗费时间:" + (endTime - startTime) + " 毫秒"); return result; } return super.doProceed(mi); } } ``` `MyMonitoringCapable` 接口定义了一个 `toggleMonitoring()` 方法,用于启用或禁用监控功能。 ```java public interface MyMonitoringCapable { void toggleMonitoring(); } ``` `MyService` 类是一个简单的服务类,其中包含了一个名为 `foo()` 的方法。在上下文中,`MyService` 类被用作目标对象,即需要被拦截和增强的对象。 ```java public class MyService { public void foo() { System.out.println("foo..."); try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } } } ``` 运行结果,这个运行结果说明了引介通知成功地增强了目标方法,实现了在目标方法执行前后动态地添加额外的逻辑。 ```java foo... [开启监控foo] foo... [结束监控foo] 耗费时间:1008 毫秒 ``` ================================================ FILE: spring-aop/spring-aop-advice-introductionInterceptor/pom.xml ================================================ com.xcs.spring spring-aop 0.0.1-SNAPSHOT 4.0.0 spring-aop-advice-introductionInterceptor ================================================ FILE: spring-aop/spring-aop-advice-introductionInterceptor/src/main/java/com/xcs/spring/IntroductionInterceptorDemo.java ================================================ package com.xcs.spring; import org.springframework.aop.framework.ProxyFactory; import org.springframework.aop.support.DefaultIntroductionAdvisor; public class IntroductionInterceptorDemo { public static void main(String[] args) { // 创建代理工厂&创建目标对象 ProxyFactory proxyFactory = new ProxyFactory(new MyService()); // 强制私用CGLIB proxyFactory.setProxyTargetClass(true); // 创建通知 proxyFactory.addAdvisor(new DefaultIntroductionAdvisor(new MyMonitoringIntroductionAdvice(), MyMonitoringCapable.class)); // 创建代理对象 MyService proxy = (MyService) proxyFactory.getProxy(); // 调用代理对象的方法 proxy.foo(); // 开始监控 ((MyMonitoringCapable) proxy).toggleMonitoring(); // 再次调用代理对象的方法 proxy.foo(); } } ================================================ FILE: spring-aop/spring-aop-advice-introductionInterceptor/src/main/java/com/xcs/spring/MyMonitoringCapable.java ================================================ package com.xcs.spring; public interface MyMonitoringCapable { void toggleMonitoring(); } ================================================ FILE: spring-aop/spring-aop-advice-introductionInterceptor/src/main/java/com/xcs/spring/MyMonitoringIntroductionAdvice.java ================================================ package com.xcs.spring; import org.aopalliance.intercept.MethodInvocation; import org.springframework.aop.support.DelegatingIntroductionInterceptor; public class MyMonitoringIntroductionAdvice extends DelegatingIntroductionInterceptor implements MyMonitoringCapable { private boolean active = false; public void setActive(boolean active) { this.active = active; } @Override public void toggleMonitoring() { setActive(true); } // 当被监控的方法被调用时,如果监控处于激活状态,则输出日志 @Override protected Object doProceed(MethodInvocation mi) throws Throwable { if (this.active) { System.out.println("[开启监控" + mi.getMethod().getName() + "]"); long startTime = System.currentTimeMillis(); Object result = super.doProceed(mi); long endTime = System.currentTimeMillis(); System.out.println("[结束监控" + mi.getMethod().getName() + "] 耗费时间:" + (endTime - startTime) + " 毫秒"); return result; } return super.doProceed(mi); } } ================================================ FILE: spring-aop/spring-aop-advice-introductionInterceptor/src/main/java/com/xcs/spring/MyService.java ================================================ package com.xcs.spring; public class MyService { public void foo() { System.out.println("foo..."); try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } } } ================================================ FILE: spring-aop/spring-aop-advice-methodBeforeAdvice/README.md ================================================ ## MethodBeforeAdvice - [MethodBeforeAdvice](#methodbeforeadvice) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、接口源码](#四接口源码) - [五、主要实现](#五主要实现) - [六、类关系图](#六类关系图) - [七、最佳实践](#七最佳实践) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 `MethodBeforeAdvice`接口是Spring AOP中的一个核心接口,允许我们在目标方法执行之前插入自定义的逻辑,例如参数验证、日志记录等,从而实现面向切面编程的前置通知功能。 ### 三、主要功能 1. **前置通知** + 允许我们在目标方法执行之前执行额外的逻辑操作。 2. **横切关注点的分离** + 将与业务逻辑无关的横切关注点(如日志记录、性能监控、安全验证等)与核心业务逻辑分离开来,提高代码的模块化和可维护性。 3. **参数验证** + 在目标方法执行前对方法参数进行验证,确保参数的合法性。 4. **权限控制** + 在方法执行前进行权限检查,确保只有具有足够权限的用户能够执行该方法。 ### 四、接口源码 `MethodBeforeAdvice`接口,用于在方法调用之前执行通知。通知方法`before`接收被调用的方法、方法参数以及方法调用的目标对象作为参数,并可以抛出Throwable以中止方法调用。这样的通知无法阻止方法调用的继续进行,除非它们抛出了Throwable。 ```java /** * 在方法被调用之前调用的通知。这样的通知不能阻止方法调用的继续进行,除非它们抛出了一个Throwable。 * * @author Rod Johnson * @see AfterReturningAdvice * @see ThrowsAdvice */ public interface MethodBeforeAdvice extends BeforeAdvice { /** * 在给定方法被调用之前的回调。 * @param method 被调用的方法 * @param args 方法的参数 * @param target 方法调用的目标对象。可能为 {@code null}。 * @throws Throwable 如果此对象希望中止调用。任何抛出的异常如果方法签名允许,将返回给调用者。否则异常将作为运行时异常进行包装。 */ void before(Method method, Object[] args, @Nullable Object target) throws Throwable; } ``` ### 五、主要实现 1. **AspectJMethodBeforeAdvice** - 实现了前置通知,使用 AspectJ 风格定义的通知,用于在目标方法执行前执行额外的逻辑。 ### 六、类关系图 ~~~mermaid classDiagram direction BT class Advice { <> } class AspectJMethodBeforeAdvice class BeforeAdvice { <> } class MethodBeforeAdvice { <> } AspectJMethodBeforeAdvice ..> Advice AspectJMethodBeforeAdvice ..> MethodBeforeAdvice BeforeAdvice --> Advice MethodBeforeAdvice --> BeforeAdvice ~~~ ### 七、最佳实践 使用`MethodBeforeAdvice`接口。首先,通过创建代理工厂和目标对象,然后创建自定义的前置通知`MyMethodBeforeAdvice`,将其添加到代理工厂中。接着,通过代理工厂获取代理对象,并调用代理对象的方法。在方法调用之前,前置通知会被触发执行,执行自定义的逻辑。 ```java public class MethodBeforeAdviceDemo { public static void main(String[] args) { // 创建代理工厂&创建目标对象 ProxyFactory proxyFactory = new ProxyFactory(new MyService()); // 创建通知 proxyFactory.addAdvice(new MyMethodBeforeAdvice()); // 获取代理对象 MyService proxy = (MyService) proxyFactory.getProxy(); // 调用代理对象的方法 proxy.doSomething(); } } ``` `MyMethodBeforeAdvice`类实现了`MethodBeforeAdvice`接口,在其`before`方法中,打印出目标方法被调用之前的信息,包括方法名。这个类可以用作Spring AOP中的前置通知,在目标方法执行之前执行一些额外的逻辑。 ```java public class MyMethodBeforeAdvice implements MethodBeforeAdvice { @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("Before Method " + method.getName()); } } ``` `MyService` 类是一个简单的服务类,其中包含了一个名为 `doSomething()` 的方法。在上下文中,`MyService` 类被用作目标对象,即需要被拦截和增强的对象。 ```java public class MyService { public void foo() { System.out.println("foo..."); } } ``` 运行结果,调用目标方法`foo`之前,`MyMethodBeforeAdvice`中的前置通知被成功触发,并打印了相应的信息。 ```java Before Method foo foo... ``` ================================================ FILE: spring-aop/spring-aop-advice-methodBeforeAdvice/pom.xml ================================================ com.xcs.spring spring-aop 0.0.1-SNAPSHOT 4.0.0 spring-aop-advice-methodBeforeAdvice ================================================ FILE: spring-aop/spring-aop-advice-methodBeforeAdvice/src/main/java/com/xcs/spring/MethodBeforeAdviceDemo.java ================================================ package com.xcs.spring; import org.springframework.aop.framework.ProxyFactory; public class MethodBeforeAdviceDemo { public static void main(String[] args) { // 创建代理工厂&创建目标对象 ProxyFactory proxyFactory = new ProxyFactory(new MyService()); // 创建通知 proxyFactory.addAdvice(new MyMethodBeforeAdvice()); // 获取代理对象 MyService proxy = (MyService) proxyFactory.getProxy(); // 调用代理对象的方法 proxy.foo(); } } ================================================ FILE: spring-aop/spring-aop-advice-methodBeforeAdvice/src/main/java/com/xcs/spring/MyMethodBeforeAdvice.java ================================================ package com.xcs.spring; import org.springframework.aop.MethodBeforeAdvice; import java.lang.reflect.Method; public class MyMethodBeforeAdvice implements MethodBeforeAdvice { @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("Before Method " + method.getName()); } } ================================================ FILE: spring-aop/spring-aop-advice-methodBeforeAdvice/src/main/java/com/xcs/spring/MyService.java ================================================ package com.xcs.spring; public class MyService { public void foo() { System.out.println("foo..."); } } ================================================ FILE: spring-aop/spring-aop-advice-methodInterceptor/README.md ================================================ ## MethodInterceptor - [MethodInterceptor](#methodinterceptor) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、接口源码](#四接口源码) - [五、主要实现](#五主要实现) - [六、类关系图](#六类关系图) - [七、最佳实践](#七最佳实践) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 `MethodInterceptor`接口是Spring框架中的一个核心接口,用于实现面向切面编程(AOP)。通过实现该接口,可以在目标方法执行前后、异常抛出时等关键点对方法进行拦截和增强,从而实现横切关注点的集中管理,提高代码的可维护性和灵活性。 ### 三、主要功能 1. **方法拦截和增强** + 可以在目标方法执行前后、异常抛出时等关键点对方法进行拦截和增强,从而实现横切关注点的代码集中管理。 ### 四、接口源码 `MethodInterceptor`接口是用于拦截接口方法调用并在目标方法之前和之后执行额外处理的核心接口。我们需要实现其中的`invoke`方法来定义拦截器的具体行为,例如,可以实现一个跟踪拦截器来追踪被拦截方法的调用情况。在`invoke`方法中,通常会调用`proceed()`方法来继续执行目标方法,并在必要时对返回值或异常进行处理。 ```java /** * 拦截器,用于拦截接口方法调用并在目标方法之前和之后执行额外处理。 * 这些拦截器被嵌套在目标方法之上。 * *

用户应该实现 {@link #invoke(MethodInvocation)} 方法来修改原始行为。例如,以下类实现了一个跟踪拦截器(跟踪所有被拦截方法的调用) * * @author Rod Johnson */ @FunctionalInterface public interface MethodInterceptor extends Interceptor { /** * 实现此方法以在调用之前和之后执行额外处理。礼貌的实现应该肯定地调用 {@link Joinpoint#proceed()}。 * @param invocation 方法调用连接点 * @return 调用 {@link Joinpoint#proceed()} 的结果;可能会被拦截器拦截 * @throws Throwable 如果拦截器或目标对象抛出异常 */ @Nullable Object invoke(@Nonnull MethodInvocation invocation) throws Throwable; } ``` ### 五、主要实现 1. **MethodBeforeAdviceInterceptor** + 实现了前置通知的拦截器。前置通知在目标方法执行之前执行,允许我们在方法执行前插入额外的逻辑。 2. **AfterReturningAdviceInterceptor** + 实现了返回后通知的拦截器。返回后通知在目标方法成功执行并返回结果后执行,允许我们在方法返回后插入额外的逻辑。 3. **ThrowsAdviceInterceptor** + 实现了异常抛出后通知的拦截器。异常抛出后通知在目标方法抛出异常后执行,允许我们在方法抛出异常后插入额外的逻辑。 4. **AspectJAfterAdvice** + 实现了后置通知(After Advice),在目标方法执行后执行额外逻辑,不影响目标方法的执行结果。 5. **AspectJAfterThrowingAdvice** + 实现了异常抛出后通知(After Throwing Advice),在目标方法抛出异常后执行额外逻辑,允许处理异常或执行一些清理操作。 6. **AspectJAroundAdvice** + 实现了环绕通知(Around Advice),是最强大的一种通知类型,允许在目标方法执行前后添加额外逻辑,并完全控制目标方法的执行过程,包括是否执行目标方法和如何处理返回值。 ### 六、类关系图 ~~~mermaid classDiagram direction BT class AbstractAspectJAdvice class Advice { <> } class AspectJAfterAdvice class AspectJAfterThrowingAdvice class AspectJAroundAdvice class Interceptor { <> } class MethodBeforeAdviceInterceptor class AfterReturningAdviceInterceptor class MethodInterceptor { <> } class ThrowsAdviceInterceptor AbstractAspectJAdvice ..> Advice AfterReturningAdviceInterceptor ..> Advice AfterReturningAdviceInterceptor ..> MethodInterceptor AspectJAfterAdvice --> AbstractAspectJAdvice AspectJAfterAdvice ..> Advice AspectJAfterAdvice ..> MethodInterceptor AspectJAfterThrowingAdvice --> AbstractAspectJAdvice AspectJAfterThrowingAdvice ..> Advice AspectJAfterThrowingAdvice ..> MethodInterceptor AspectJAroundAdvice --> AbstractAspectJAdvice AspectJAroundAdvice ..> MethodInterceptor Interceptor --> Advice MethodBeforeAdviceInterceptor ..> Advice MethodBeforeAdviceInterceptor ..> MethodInterceptor MethodInterceptor --> Interceptor ThrowsAdviceInterceptor ..> Advice ThrowsAdviceInterceptor ..> MethodInterceptor ~~~ ### 七、最佳实践 创建了一个代理工厂 `ProxyFactory`,并传入了目标对象 `MyService`。然后通过 `proxyFactory.addAdvice()` 方法添加了一个自定义的方法拦截器 `MyMethodInterceptor` 作为通知。接着,通过 `proxyFactory.getProxy()` 方法获取代理对象 `MyService` 的实例。最后,调用代理对象的方法 `doSomething()`。 ```java public class MethodInterceptorDemo { public static void main(String[] args) { // 创建代理工厂&创建目标对象 ProxyFactory proxyFactory = new ProxyFactory(new MyService()); // 创建通知 proxyFactory.addAdvice(new MyMethodInterceptor()); // 获取代理对象 MyService proxy = (MyService) proxyFactory.getProxy(); // 调用代理对象的方法 proxy.foo(); } } ``` `MyMethodInterceptor` 类用于实现方法拦截和增强的功能。在 `invoke()` 方法中,首先通过 `MethodInvocation` 对象获取被调用方法的信息,例如方法名等,并在方法调用之前输出方法被调用的信息。然后调用 `invocation.proceed()` 方法来执行原始方法,获取方法执行结果。最后并将其返回。 ```java public class MyMethodInterceptor implements MethodInterceptor { @Override public Object invoke(MethodInvocation invocation) throws Throwable { // 在方法调用之前执行的逻辑 System.out.println("Before Method " + invocation.getMethod().getName()); // 调用原始方法 Object result = invocation.proceed(); // 在方法调用之后执行的逻辑 System.out.println("After Method " + invocation.getMethod().getName()); return result; } } ``` `MyService` 类是一个简单的服务类,其中包含了一个名为 `foo()` 的方法。在上下文中,`MyService` 类被用作目标对象,即需要被拦截和增强的对象。 ```java public class MyService { public void foo() { System.out.println("foo..."); } } ``` 运行结果,在调用 `MyService` 实例的 `foo()` 方法时,`MyMethodInterceptor` 拦截器成功地拦截了方法的执行,并在方法执行前后添加了额外的逻辑处理。 ```java Before Method foo foo... After Method foo ``` ================================================ FILE: spring-aop/spring-aop-advice-methodInterceptor/pom.xml ================================================ com.xcs.spring spring-aop 0.0.1-SNAPSHOT 4.0.0 spring-aop-advice-methodInterceptor ================================================ FILE: spring-aop/spring-aop-advice-methodInterceptor/src/main/java/com/xcs/spring/MethodInterceptorDemo.java ================================================ package com.xcs.spring; import org.springframework.aop.framework.ProxyFactory; public class MethodInterceptorDemo { public static void main(String[] args) { // 创建代理工厂&创建目标对象 ProxyFactory proxyFactory = new ProxyFactory(new MyService()); // 创建通知 proxyFactory.addAdvice(new MyMethodInterceptor()); // 获取代理对象 MyService proxy = (MyService) proxyFactory.getProxy(); // 调用代理对象的方法 proxy.foo(); } } ================================================ FILE: spring-aop/spring-aop-advice-methodInterceptor/src/main/java/com/xcs/spring/MyMethodInterceptor.java ================================================ package com.xcs.spring; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class MyMethodInterceptor implements MethodInterceptor { @Override public Object invoke(MethodInvocation invocation) throws Throwable { // 在方法调用之前执行的逻辑 System.out.println("Before Method " + invocation.getMethod().getName()); // 调用原始方法 Object result = invocation.proceed(); // 在方法调用之后执行的逻辑 System.out.println("After Method " + invocation.getMethod().getName()); return result; } } ================================================ FILE: spring-aop/spring-aop-advice-methodInterceptor/src/main/java/com/xcs/spring/MyService.java ================================================ package com.xcs.spring; public class MyService { public void foo() { System.out.println("foo..."); } } ================================================ FILE: spring-aop/spring-aop-advice-throwsAdvice/README.md ================================================ ## ThrowsAdvice - [ThrowsAdvice](#throwsadvice) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、接口源码](#四接口源码) - [五、主要实现](#五主要实现) - [六、类关系图](#六类关系图) - [七、最佳实践](#七最佳实践) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 `ThrowsAdvice`接口是Spring AOP中的一种通知类型,用于在方法抛出异常时执行额外的逻辑。实现该接口的类可以捕获方法抛出的异常并执行自定义的异常处理逻辑。 ### 三、主要功能 1. **捕获异常** + 允许在目标方法抛出异常时捕获这些异常。 2. **执行额外逻辑** + 提供了`afterThrowing()`方法,允许实现类在方法抛出异常时执行额外的逻辑,比如记录日志、发送通知等。 3. **参数传递** + `afterThrowing()`方法提供了抛出异常的方法对象、参数数组、目标对象和异常对象作为参数,方便实现类在处理异常时获取相关信息。 4. **定制化处理** + 可以根据业务需求定制化异常处理逻辑,使应用程序更加灵活和健壮。 ### 四、接口源码 `ThrowsAdvice`接口,用作异常通知的标签接口。它没有任何方法,方法是通过反射调用的。实现类必须实现`afterThrowing()`方法,以处理方法抛出的异常。该方法的参数形式为`void afterThrowing([Method, args, target], ThrowableSubclass)`,前三个参数可选,用于提供关于连接点的更多信息, ```java /** * 用于异常通知的标签接口。 * *

该接口没有任何方法,因为方法是通过反射调用的。实现类必须实现以下形式的方法: * *

void afterThrowing([Method, args, target], ThrowableSubclass);
* *

以下是一些有效的方法示例: * *

public void afterThrowing(Exception ex)
*
public void afterThrowing(RemoteException)
*
public void afterThrowing(Method method, Object[] args, Object target, Exception ex)
*
public void afterThrowing(Method method, Object[] args, Object target, ServletException ex)
* * 前三个参数是可选的,只有在我们需要有关连接点更多信息时才有用,如AspectJ中的after-throwing通知。 * *

注意: 如果throws-advice方法本身抛出异常,它将覆盖原始异常(即将异常更改为用户)。 * 覆盖的异常通常是RuntimeException; 这与任何方法签名兼容。但是,如果throws-advice方法抛出一个已检查的异常, * 它将必须匹配目标方法的声明异常,并且在某种程度上与特定目标方法签名耦合。 * 不要抛出与目标方法签名不兼容的未声明的检查异常! * * @author Rod Johnson * @author Juergen Hoeller * @see AfterReturningAdvice * @see MethodBeforeAdvice */ public interface ThrowsAdvice extends AfterAdvice { } ``` ### 五、主要实现 1. **ThrowsAdviceInterceptor** + 用于拦截方法抛出的异常,并触发相应的异常通知(`ThrowsAdvice`)。它负责捕获方法执行过程中抛出的异常,并调用相关的异常通知来处理异常情况。 ### 六、类关系图 ~~~mermaid classDiagram direction BT class Advice { <> } class AfterAdvice { <> } class ThrowsAdvice { <> } AfterAdvice --> Advice ThrowsAdvice --> AfterAdvice ~~~ ### 七、最佳实践 使用`ThrowsAdvice`接口来处理方法抛出的异常。它创建了一个代理工厂,并将目标对象(`MyService`)和异常通知(`MyThrowsAdvice`)传递给代理工厂。然后,它通过代理工厂获取代理对象,并调用代理对象的方法`foo()`。 ```java public class ThrowsAdviceDemo { public static void main(String[] args) { // 创建代理工厂&创建目标对象 ProxyFactory proxyFactory = new ProxyFactory(new MyService()); // 创建通知 proxyFactory.addAdvice(new MyThrowsAdvice()); // 获取代理对象 MyService proxy = (MyService) proxyFactory.getProxy(); // 调用代理对象的方法 proxy.foo(); } } ``` `MyThrowsAdvice`类实现了`ThrowsAdvice`接口,并定义了`afterThrowing()`方法,用于处理方法抛出的异常。当目标方法抛出异常时,该方法将被调用,并打印出异常信息。 ```java public class MyThrowsAdvice implements ThrowsAdvice { public void afterThrowing(Exception ex) throws Throwable { System.out.println("Exception thrown: " + ex.getMessage()); } } ``` `MyService`类包含了一个名为`foo()`的方法,该方法执行某些操作,并故意引发了一个异常(通过除以零)。 ```java public class MyService { public void foo() { System.out.println("foo..."); int i = 1 / 0; } } ``` 运行结果,当调用了`MyService`类的`foo()`方法,但在该方法中发生了除以零的错误,导致了`java.lang.ArithmeticException: / by zero`异常的抛出。 ```java Doing something exception... Exception thrown: / by zero Exception in thread "main" java.lang.ArithmeticException: / by zero at com.xcs.spring.MyService.doSomethingException(MyService.java:7) at com.xcs.spring.MyService$$FastClassBySpringCGLIB$$c768e93b.invoke() at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:779) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750) at org.springframework.aop.framework.adapter.ThrowsAdviceInterceptor.invoke(ThrowsAdviceInterceptor.java:113) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750) at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:692) at com.xcs.spring.MyService$$EnhancerBySpringCGLIB$$abe9fbc2.doSomethingException() at com.xcs.spring.ThrowsAdviceDemo.main(ThrowsAdviceDemo.java:15) ``` ================================================ FILE: spring-aop/spring-aop-advice-throwsAdvice/pom.xml ================================================ com.xcs.spring spring-aop 0.0.1-SNAPSHOT 4.0.0 spring-aop-advice-throwsAdvice ================================================ FILE: spring-aop/spring-aop-advice-throwsAdvice/src/main/java/com/xcs/spring/MyService.java ================================================ package com.xcs.spring; public class MyService { public void foo() { System.out.println("foo..."); int i = 1 / 0; } } ================================================ FILE: spring-aop/spring-aop-advice-throwsAdvice/src/main/java/com/xcs/spring/MyThrowsAdvice.java ================================================ package com.xcs.spring; import org.springframework.aop.ThrowsAdvice; public class MyThrowsAdvice implements ThrowsAdvice { public void afterThrowing(Exception ex) throws Throwable { System.out.println("Exception thrown: " + ex.getMessage()); } } ================================================ FILE: spring-aop/spring-aop-advice-throwsAdvice/src/main/java/com/xcs/spring/ThrowsAdviceDemo.java ================================================ package com.xcs.spring; import org.springframework.aop.framework.ProxyFactory; public class ThrowsAdviceDemo { public static void main(String[] args) { // 创建代理工厂&创建目标对象 ProxyFactory proxyFactory = new ProxyFactory(new MyService()); // 创建通知 proxyFactory.addAdvice(new MyThrowsAdvice()); // 获取代理对象 MyService proxy = (MyService) proxyFactory.getProxy(); // 调用代理对象的方法 proxy.foo(); } } ================================================ FILE: spring-aop/spring-aop-advised/README.md ================================================ ## Advised - [Advised](#advised) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、接口源码](#四接口源码) - [五、主要实现](#五主要实现) - [六、类关系图](#六类关系图) - [七、最佳实践](#七最佳实践) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 `Advised` 接口是 Spring AOP 中的核心接口之一,代表了被 Spring AOP 支持的被通知对象,它提供了对被通知对象的通用管理方法,包括检查是否被冻结、是否代理目标类、获取被代理接口、添加、移除和获取通知等,通过这些方法可以实现对被通知对象的动态通知管理。 ### 三、主要功能 1. **冻结状态检查(isFrozen)** + 可以检查被通知对象是否处于冻结状态,即是否可以修改其 AOP 配置。 2. **代理类型检查(isProxyTargetClass)** + 可以检查是否代理了目标类,而不是目标接口。 3. **获取被代理接口(getProxiedInterfaces)** + 可以获取被代理对象所实现的接口数组。 4. **接口代理检查(isInterfaceProxied)** + 可以检查给定的接口是否被代理。 5. **添加通知(addAdvice)** + 可以向被通知对象添加通知,实现对目标方法的增强。 6. **指定位置添加通知(addAdvice)** + 可以在指定位置添加通知,控制通知的执行顺序。 7. **获取所有通知(getAdvices)** + 可以获取所有添加到被通知对象的通知。 8. **移除通知(removeAdvice)** + 可以移除指定的通知,动态调整通知的配置。 ### 四、接口源码 `Advised`接口 ,规定了代理工厂配置的结构和行为。这个接口包含了许多方法,用于管理AOP代理的配置,包括拦截器、通知器、被代理的接口等。通过这个接口,可以获取和操作AOP代理的配置信息,如是否冻结、是否代理目标类、获取被代理的接口、添加和移除通知器等。 ```java /** * 用于实现持有 AOP 代理工厂配置的类的接口。该配置包括拦截器和其他通知器、通知器以及被代理的接口。 * * 从 Spring 获取的任何 AOP 代理都可以转换为此接口,以允许对其 AOP 通知进行操作。 * * @author Rod Johnson * @author Juergen Hoeller * @since 13.03.2003 * @see org.springframework.aop.framework.AdvisedSupport */ public interface Advised extends TargetClassAware { /** * 返回 Advised 配置是否已冻结,如果已冻结,则无法进行通知更改。 */ boolean isFrozen(); /** * 是否代理了完整的目标类,而不是指定的接口? */ boolean isProxyTargetClass(); /** * 返回 AOP 代理所代理的接口。 * 不会包括目标类,目标类可能也会被代理。 */ Class[] getProxiedInterfaces(); /** * 确定给定的接口是否被代理。 * @param intf 要检查的接口 */ boolean isInterfaceProxied(Class intf); /** * 更改此 Advised 对象使用的 TargetSource。 * 仅在配置未被冻结时有效。 * @param targetSource 要使用的新 TargetSource */ void setTargetSource(TargetSource targetSource); /** * 返回此 Advised 对象使用的 TargetSource。 */ TargetSource getTargetSource(); /** * 设置代理是否应该被 AOP 框架公开为 {@link ThreadLocal},以便通过 {@link AopContext} 类进行检索。 * 如果需要在通知对象上调用自身的方法并应用通知,则可能需要公开代理。否则,如果通知对象调用 {@code this} 的方法,将不会应用任何通知。 * 默认为 {@code false},以获得最佳性能。 */ void setExposeProxy(boolean exposeProxy); /** * 返回工厂是否应将代理公开为 {@link ThreadLocal}。 * 如果需要在通知对象上调用自身的方法并应用通知,则可能需要公开代理。否则,如果通知对象调用 {@code this} 的方法,将不会应用任何通知。 * 获取代理类似于 EJB 调用 {@code getEJBObject()}。 * @see AopContext */ boolean isExposeProxy(); /** * 设置此代理配置是否经过预过滤,以便仅包含适用的通知器(与此代理的目标类匹配)。 * 默认为 "false"。如果通知器已经被预过滤,即可以跳过构建实际的代理调用链时的 ClassFilter 检查,则将其设置为 "true"。 * @see org.springframework.aop.ClassFilter */ void setPreFiltered(boolean preFiltered); /** * 返回此代理配置是否经过预过滤,以便仅包含适用的通知器(与此代理的目标类匹配)。 */ boolean isPreFiltered(); /** * 返回应用于此代理的通知器。 * @return 应用于此代理的通知器列表(永远不会为 {@code null}) */ Advisor[] getAdvisors(); /** * 返回应用于此代理的通知器数量。 * 默认实现委托给 {@code getAdvisors().length}。 * @since 5.3.1 */ default int getAdvisorCount() { return getAdvisors().length; } /** * 在通知器链的末尾添加一个通知器。 * 通知器可以是 {@link org.springframework.aop.IntroductionAdvisor}, * 在从相关工厂下次获取代理时将提供新的接口。 * @param advisor 要添加到链的末尾的通知器 * @throws AopConfigException 如果通知器无效 */ void addAdvisor(Advisor advisor) throws AopConfigException; /** * 在链中的指定位置添加一个通知器。 * @param advisor 要在链中指定位置添加的通知器 * @param pos 链中的位置(0 是头)。必须有效。 * @throws AopConfigException 如果通知器无效 */ void addAdvisor(int pos, Advisor advisor) throws AopConfigException; /** * 删除给定的通知器。 * @param advisor 要移除的通知器 * @return 如果已移除通知器,则返回 {@code true};如果未找到该通知器,因此无法移除,则返回 {@code false} */ boolean removeAdvisor(Advisor advisor); /** * 移除给定索引处的通知器。 * @param index 要移除的通知器的索引 * @throws AopConfigException 如果索引无效 */ void removeAdvisor(int index) throws AopConfigException; /** * 返回给定通知器的索引(从 0 开始), * 如果没有这样的通知器适用于此代理,则返回 -1。 * 此方法的返回值可用于索引到通知器数组中。 * @param advisor 要搜索的通知器 * @return 此通知器的从 0 开始的索引,如果没有这样的通知器,则返回 -1 */ int indexOf(Advisor advisor); /** * 替换给定的通知器。 * 注意如果通知器是 {@link org.springframework.aop.IntroductionAdvisor}, * 并且替换项不是或实现了不同的接口,则需要重新获取代理,否则旧接口将不被支持,新接口也将不被实现。 * @param a 要替换的通知器 * @param b 要替换的新通知器 * @return 是否已替换。如果通知器未在通知器列表中找到,则此方法返回 {@code false},并且不执行任何操作。 * @throws AopConfigException 如果通知器无效 */ boolean replaceAdvisor(Advisor a, Advisor b) throws AopConfigException; /** * 将给定的 AOP Alliance 通知添加到通知(拦截器)链的末尾。 * 这将被包装在一个始终适用的 DefaultPointcutAdvisor 中,并从 {@code getAdvisors()} 方法以此包装形式返回。 * 请注意,给定的通知将应用于代理的所有调用,甚至是 {@code toString()} 方法!使用适当的通知实现或指定适当的切入点以适用于更窄范围的方法。 * @param advice 要添加到链末尾的通知 * @throws AopConfigException 如果通知无效 * @see #addAdvice(int, Advice) * @see org.springframework.aop.support.DefaultPointcutAdvisor */ void addAdvice(Advice advice) throws AopConfigException; /** * 将给定的 AOP Alliance 通知添加到通知链的指定位置。 * 这将被包装在一个 {@link org.springframework.aop.support.DefaultPointcutAdvisor} 中, * 并以此包装形式从 {@link #getAdvisors()} 方法返回。 * 注意给定的通知将应用于代理的所有调用,甚至是 {@code toString()} 方法!使用适当的通知实现或指定适当的切入点以适用于更窄范围的方法。 * @param pos 从 0 开始的索引(头部) * @param advice 要在通知链的指定位置添加的通知 * @throws AopConfigException 如果通知无效 */ void addAdvice(int pos, Advice advice) throws AopConfigException; /** * 移除包含给定通知的通知器。 * @param advice 要移除的通知 * @return 如果找到并移除了通知,则返回 {@code true};如果没有找到该通知,则返回 {@code false} */ boolean removeAdvice(Advice advice); /** * 返回给定 AOP Alliance 通知的索引(从 0 开始), * 如果没有这样的通知是此代理的通知,则返回 -1。 * 此方法的返回值可用于索引到通知器数组中。 * @param advice 要搜索的 AOP Alliance 通知 * @return 此通知的从 0 开始的索引,如果没有这样的通知,则返回 -1 */ int indexOf(Advice advice); /** * 由于通常将 {@code toString()} 委托给目标,因此此方法返回 AOP 代理的等效描述。 * @return 代理配置的字符串描述 */ String toProxyConfigString(); } ``` ### 五、主要实现 + **AdvisedSupport** + 负责管理 AOP 代理配置信息的核心类,它包含了通知器、目标对象、目标源等关键属性,能够灵活地配置和管理 AOP 代理的创建过程,并提供了各种方法来处理代理配置的冻结状态、代理暴露等功能。 ### 六、类关系图 ~~~mermaid classDiagram direction BT class Advised { <> } class AdvisedSupport class TargetClassAware { <> } Advised --> TargetClassAware AdvisedSupport ..> Advised ~~~ ### 七、最佳实践 使用 `AdvisedSupport` 类来配置代理对象的相关属性,包括设置目标对象、接口、通知、通知器、是否暴露代理对象、是否使用 CGLIB 代理以及冻结对象,并通过 `toProxyConfigString()` 方法打印代理配置信息。 ```java public class AdvisedDemo { public static void main(String[] args) { // 创建 AdvisedSupport 对象 AdvisedSupport advisedSupport = new AdvisedSupport(); // 设置目标对象 advisedSupport.setTarget(new MyServiceImpl()); // 设置目标对象实现的接口 advisedSupport.setInterfaces(MyService.class); // 添加通知 advisedSupport.addAdvice(new Advice() {}); // 添加通知器 advisedSupport.addAdvisor(new DefaultPointcutAdvisor()); // 暴露代理对象 advisedSupport.setExposeProxy(true); // 设置CGLIB 代理 advisedSupport.setProxyTargetClass(true); // 冻结对象 advisedSupport.setFrozen(true); // 打印 System.out.println("AdvisedSupport = " + advisedSupport.toProxyConfigString()); } } ``` 定义了一个 `MyService` 接口 ```java public interface MyService { void foo(); } ``` 实现了 `MyService` 接口的 `MyServiceImpl` 类。 ```java public class MyServiceImpl implements MyService { @Override public void foo() { System.out.println("foo..."); } } ``` 运行结果,显示了 `AdvisedSupport` 对象的配置信息,包括代理的接口、通知器、目标对象信息、代理类型以及其他相关设置。 ```java AdvisedSupport = org.springframework.aop.framework.AdvisedSupport: 1 interfaces [com.xcs.spring.MyService]; 2 advisors [org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [Pointcut.TRUE]; advice [com.xcs.spring.AdvisedDemo$1@32d992b2], org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [Pointcut.TRUE]; advice [org.springframework.aop.Advisor$1@215be6bb]]; targetSource [SingletonTargetSource for target object [com.xcs.spring.MyServiceImpl@5d5eef3d]]; proxyTargetClass=true; optimize=false; opaque=false; exposeProxy=true; frozen=true ``` ================================================ FILE: spring-aop/spring-aop-advised/pom.xml ================================================ com.xcs.spring spring-aop 0.0.1-SNAPSHOT 4.0.0 spring-aop-advised ================================================ FILE: spring-aop/spring-aop-advised/src/main/java/com/xcs/spring/AdvisedDemo.java ================================================ package com.xcs.spring; import org.aopalliance.aop.Advice; import org.springframework.aop.framework.AdvisedSupport; import org.springframework.aop.support.DefaultPointcutAdvisor; public class AdvisedDemo { public static void main(String[] args) { // 创建 AdvisedSupport 对象 AdvisedSupport advisedSupport = new AdvisedSupport(); // 设置目标对象 advisedSupport.setTarget(new MyServiceImpl()); // 设置目标对象实现的接口 advisedSupport.setInterfaces(MyService.class); // 添加通知 advisedSupport.addAdvice(new Advice() {}); // 添加通知器 advisedSupport.addAdvisor(new DefaultPointcutAdvisor()); // 暴露代理对象 advisedSupport.setExposeProxy(true); // 设置CGLIB 代理 advisedSupport.setProxyTargetClass(true); // 冻结对象 advisedSupport.setFrozen(true); // 打印 System.out.println("AdvisedSupport = " + advisedSupport.toProxyConfigString()); } } ================================================ FILE: spring-aop/spring-aop-advised/src/main/java/com/xcs/spring/MyService.java ================================================ package com.xcs.spring; public interface MyService { void foo(); } ================================================ FILE: spring-aop/spring-aop-advised/src/main/java/com/xcs/spring/MyServiceImpl.java ================================================ package com.xcs.spring; public class MyServiceImpl implements MyService { @Override public void foo() { System.out.println("foo..."); } } ================================================ FILE: spring-aop/spring-aop-advisor/README.md ================================================ ## Advisor - [Advisor](#advisor) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、接口源码](#四接口源码) - [五、主要实现](#五主要实现) - [六、类关系图](#六类关系图) - [七、最佳实践](#七最佳实践) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 `Advisor`接口是Spring框架中的一个关键接口,用于将切点(Pointcut)和通知(Advice)组合起来,以便在AOP(面向切面编程)中定义何时、何地以及如何应用横切关注点。 ### 三、主要功能 1. **组合切点和通知** + Advisor接口允许将切点(Pointcut)和通知(Advice)组合在一起。切点确定何时应该应用通知,而通知定义了在连接点处执行的代码。 ### 四、接口源码 `Advisor`接口是Spring框架中的一个基础接口,用于持有AOP通知(在连接点执行的操作)和确定通知适用性的过滤器(例如切点)。该接口定义了获取通知部分的方法`getAdvice()`,以及确定通知是否与特定实例相关联的方法`isPerInstance()`。同时,该接口还提供了一个常量`EMPTY_ADVICE`,用作当未配置适当通知时的占位符。在Spring AOP中,Advisor接口允许支持不同类型的通知,例如拦截器、前置通知、异常通知等,并且并非所有通知都需要使用拦截来实现。 ```java /** * 基础接口,持有AOP 通知(在连接点执行的操作)和确定通知适用性的过滤器(例如切点)。 * 此接口不供Spring用户使用,而是为了在支持不同类型的通知时提供共性。 * *

Spring AOP基于通过方法拦截(interception)提供的环绕通知,符合AOP Alliance拦截API。 * Advisor接口允许支持不同类型的通知,例如前置后置通知,它们不一定要使用拦截来实现。 * * @author Rod Johnson * @author Juergen Hoeller */ public interface Advisor { /** * 如果尚未配置适当的通知,则从{@link #getAdvice()}返回一个空的{@code Advice}的常用占位符。 * @since 5.0 */ Advice EMPTY_ADVICE = new Advice() {}; /** * 返回此方面的通知部分。通知可以是拦截器、前置通知、异常通知等。 * @return 如果切点匹配,则应应用的通知 * @see org.aopalliance.intercept.MethodInterceptor * @see BeforeAdvice * @see ThrowsAdvice * @see AfterReturningAdvice */ Advice getAdvice(); /** * 返回此通知是否与特定实例相关联(例如,创建混入),或与从同一Spring bean工厂获取的被通知类的所有实例共享。 *

请注意,框架当前不使用此方法。 * 典型的Advisor实现总是返回{@code true}。 * 使用singleton/prototype bean定义或适当的编程代理创建来确保Advisor具有正确的生命周期模型。 * @return 此通知是否与特定目标实例关联 */ boolean isPerInstance(); } ``` `PointcutAdvisor`接口是所有由切点驱动的Advisor的超级接口。它覆盖了几乎所有的Advisor,但不包括引介Advisor,因为引介Advisor不适用于方法级别的匹配。该接口表示由切点驱动的Advisor,通过`getPointcut()`方法获取驱动该Advisor的切点。 PointcutAdvisor通常用于基于切点的切面,通过指定切点来确定通知逻辑应该应用于哪些连接点。 ```java /** * 所有由切点驱动的Advisor的超级接口。 * 这几乎涵盖了所有的Advisor,除了引介Advisor, * 因为方法级别的匹配不适用于引介Advisor。 * * 该接口是Advisor的子接口,用于表示由切点驱动的Advisor。 * 切点驱动的Advisor通常用于基于切点的切面,通过指定切点来确定通知逻辑应该应用于哪些连接点。 * * 作者:Rod Johnson */ public interface PointcutAdvisor extends Advisor { /** * 获取驱动该Advisor的切点。 */ Pointcut getPointcut(); } ``` ### 五、主要实现 1. **RegexpMethodPointcutAdvisor** - 基于正则表达式来匹配方法名的切点。通过使用正则表达式,可以根据方法名模式匹配连接点,并将通知应用于匹配的连接点,从而实现基于方法名模式的切面逻辑。 2. **AspectJExpressionPointcutAdvisor** - 基于AspectJ表达式来定义切点。通过使用AspectJ的语法,可以更灵活地定义切面,从而匹配连接点,并将通知应用于匹配的连接点,实现更复杂的切面逻辑。 3. **NameMatchMethodPointcutAdvisor** - 基于方法名模式匹配来定义切点。通过使用方法名模式,可以轻松地匹配连接点,并将通知应用于匹配的连接点,从而实现基于方法名模式的切面逻辑。 4. **DefaultPointcutAdvisor** - 一个通用的切点Advisor,用于将切点和通知组合在一起。它允许将任何类型的通知与任何类型的切点结合使用,并将通知应用于匹配的连接点,从而实现横切关注点的管理。 ### 六、类关系图 ~~~mermaid classDiagram direction BT class Advisor { <> } class AspectJPointcutAdvisor class DefaultPointcutAdvisor class NameMatchMethodPointcutAdvisor class PointcutAdvisor { <> } class RegexpMethodPointcutAdvisor AspectJPointcutAdvisor ..> PointcutAdvisor DefaultPointcutAdvisor ..> PointcutAdvisor NameMatchMethodPointcutAdvisor ..> PointcutAdvisor PointcutAdvisor --> Advisor RegexpMethodPointcutAdvisor ..> PointcutAdvisor ~~~ ### 七、最佳实践 使用Advisor来创建代理对象并应用切面逻辑。首先,通过创建代理工厂`ProxyFactory`,并将目标对象`MyService`传递给它。然后,通过`proxyFactory.addAdvisor(new MyCustomAdvisor())`添加了一个自定义的Advisor,其中包含了切点和通知的定义。接着,通过`proxyFactory.getProxy()`获取了代理对象`MyService`。最后,调用了代理对象的`foo()`方法,该方法触发了切面逻辑的执行。 ```java public class AdvisorDemo { public static void main(String[] args) { // 创建代理工厂 ProxyFactory proxyFactory = new ProxyFactory(new MyService()); // 添加Advisor proxyFactory.addAdvisor(new MyCustomAdvisor()); // 获取代理对象 MyService proxy = (MyService) proxyFactory.getProxy(); // 调用方法 proxy.foo(); } } ``` `MyCustomAdvisor`类是一个自定义的Advisor,它实现了`PointcutAdvisor`接口,并用于将通知应用于带有特定注解的方法。在该类中,我们定义了一个通知对象`advice`和一个切点对象`pointcut`,切点用于匹配带有自定义注解`MyCustomAnnotation`的方法。通过实现`getPointcut()`方法和`getAdvice()`方法,我们指定了切点和通知的逻辑。 ```java /** * 自定义Advisor,用于将通知应用于带有特定注解的方法。 */ public class MyCustomAdvisor implements PointcutAdvisor { /** * 通知对象 */ private final Advice advice = new MyAdvice(); /** * 切点对象,用于匹配带有自定义注解的方法 */ private final Pointcut pointcut = new AnnotationMatchingPointcut(null, MyCustomAnnotation.class); @Override public Pointcut getPointcut() { return pointcut; } @Override public Advice getAdvice() { return advice; } @Override public boolean isPerInstance() { return true; } } ``` `MyAdvice`类是一个通知类,它实现了`MethodInterceptor`接口,用于在方法执行前后添加额外的逻辑。在`invoke`方法中,我们首先输出了正在调用的方法名,然后调用了`invocation.proceed()`方法来执行原始方法,并获取了方法执行的结果。最后,我们在方法执行之后再次输出了方法名。 ```java public class MyAdvice implements MethodInterceptor { @Override public Object invoke(MethodInvocation invocation) throws Throwable { // 在方法调用之前执行的逻辑 System.out.println("Before Method " + invocation.getMethod().getName()); // 调用原始方法 Object result = invocation.proceed(); // 在方法调用之后执行的逻辑 System.out.println("After Method " + invocation.getMethod().getName()); return result; } } ``` `MyCustomAnnotation`是一个自定义的注解,通常用于标记需要特殊处理的方法。 ```java @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface MyCustomAnnotation { } ``` `MyCustomAnnotation`类其中包含一个名为`foo`的方法。该方法被`@MyCustomAnnotation`注解标记,表明需要特殊处理。 ```java public class MyService { @MyCustomAnnotation public void foo() { System.out.println("foo..."); } } ``` 运行结果,切面逻辑成功应用于带有特定注解的方法。 ```java Before Method foo foo... After Method foo ``` ================================================ FILE: spring-aop/spring-aop-advisor/pom.xml ================================================ com.xcs.spring spring-aop 0.0.1-SNAPSHOT 4.0.0 spring-aop-advisor ================================================ FILE: spring-aop/spring-aop-advisor/src/main/java/com/xcs/spring/AdvisorDemo.java ================================================ package com.xcs.spring; import org.springframework.aop.framework.ProxyFactory; public class AdvisorDemo { public static void main(String[] args) { // 创建代理工厂 ProxyFactory proxyFactory = new ProxyFactory(new MyService()); // 添加Advisor proxyFactory.addAdvisor(new MyCustomAdvisor()); // 获取代理对象 MyService proxy = (MyService) proxyFactory.getProxy(); // 调用方法 proxy.foo(); } } ================================================ FILE: spring-aop/spring-aop-advisor/src/main/java/com/xcs/spring/MyAdvice.java ================================================ package com.xcs.spring; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class MyAdvice implements MethodInterceptor { @Override public Object invoke(MethodInvocation invocation) throws Throwable { // 在方法调用之前执行的逻辑 System.out.println("Before Method " + invocation.getMethod().getName()); // 调用原始方法 Object result = invocation.proceed(); // 在方法调用之后执行的逻辑 System.out.println("After Method " + invocation.getMethod().getName()); return result; } } ================================================ FILE: spring-aop/spring-aop-advisor/src/main/java/com/xcs/spring/MyCustomAdvisor.java ================================================ package com.xcs.spring; import org.aopalliance.aop.Advice; import org.springframework.aop.Pointcut; import org.springframework.aop.PointcutAdvisor; import org.springframework.aop.support.annotation.AnnotationMatchingPointcut; /** * 自定义Advisor,用于将通知应用于带有特定注解的方法。 */ public class MyCustomAdvisor implements PointcutAdvisor { /** * 通知对象 */ private final Advice advice = new MyAdvice(); /** * 切点对象,用于匹配带有自定义注解的方法 */ private final Pointcut pointcut = new AnnotationMatchingPointcut(null, MyCustomAnnotation.class); @Override public Pointcut getPointcut() { return pointcut; } @Override public Advice getAdvice() { return advice; } @Override public boolean isPerInstance() { return true; } } ================================================ FILE: spring-aop/spring-aop-advisor/src/main/java/com/xcs/spring/MyCustomAnnotation.java ================================================ package com.xcs.spring; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 自定义注解 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface MyCustomAnnotation { } ================================================ FILE: spring-aop/spring-aop-advisor/src/main/java/com/xcs/spring/MyService.java ================================================ package com.xcs.spring; public class MyService { @MyCustomAnnotation public void foo() { System.out.println("foo..."); } } ================================================ FILE: spring-aop/spring-aop-advisorAdapter/README.md ================================================ ## AdvisorAdapter - [AdvisorAdapter](#advisoradapter) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、接口源码](#四接口源码) - [五、主要实现](#五主要实现) - [六、类关系图](#六类关系图) - [七、最佳实践](#七最佳实践) - [八、源码分析](#八源码分析) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 `AdvisorAdapter` 接口是 Spring AOP 中的一个重要接口,用于将不同类型的通知(Advice)适配到拦截器链中,以便将其应用于目标方法的执行。它允许我们自定义适配器来将自定义的通知与 Spring AOP 框架结合,从而实现对目标方法的前置、后置、环绕等类型的增强操作,为 AOP 的灵活性和可扩展性提供了支持。 ### 三、主要功能 1. **通知适配** + 将不同类型的通知(Advice)适配到 Spring AOP 拦截器链中,以便将其应用于目标方法的执行。 2. **支持不同通知类型** + 支持适配各种类型的通知,包括前置通知(MethodBeforeAdvice)、后置通知(AfterReturningAdvice)、环绕通知(MethodInterceptor)、抛出异常通知(ThrowsAdvice)等。 3. **适配器注册和管理** + 允许我们注册和管理不同类型通知的适配器,以便在应用中使用不同类型的通知。 ### 四、接口源码 这个接口定义了一种机制,允许向 Spring AOP 框架中引入新的 Advisor 和 Advice 类型。实现该接口的对象可以将自定义的 Advice 类型转换为 AOP Alliance 拦截器,使得这些自定义的 Advice 类型能够在 Spring AOP 框架中被使用。通常情况下,大多数 Spring 用户不需要直接实现这个接口;只有在需要引入新的 Advisor 或 Advice 类型时才需要这样做。 ```java /** * 允许扩展 Spring AOP 框架的接口,以处理新的 Advisor 和 Advice 类型。 * *

实现该接口的对象可以从自定义的 Advice 类型创建 AOP Alliance 拦截器, * 从而使得这些 Advice 类型可以在 Spring AOP 框架中使用,该框架在底层使用拦截。 * *

大多数 Spring 用户无需实现此接口;只有在需要向 Spring 引入更多的 Advisor 或 Advice 类型时才需要这样做。 * * @author Rod Johnson */ public interface AdvisorAdapter { /** * 此适配器是否了解该通知对象?是否可以使用 Advisor 包含此通知作为参数调用 getInterceptors 方法? * @param advice 一个 Advice,如 BeforeAdvice * @return 此适配器是否了解给定的 Advice 对象 * @see #getInterceptor(org.springframework.aop.Advisor) * @see org.springframework.aop.BeforeAdvice */ boolean supportsAdvice(Advice advice); /** * 返回一个 AOP Alliance MethodInterceptor,将给定 Advice 的行为暴露给基于拦截的 AOP 框架。 *

不必担心 Advisor 中包含的 Pointcut;AOP 框架将负责检查切点。 * @param advisor Advisor。supportsAdvice() 方法必须在此对象上返回 true * @return 此 Advisor 的 AOP Alliance 拦截器。无需为效率缓存实例,因为 AOP 框架会缓存 Advice 链。 */ MethodInterceptor getInterceptor(Advisor advisor); } ``` ### 五、主要实现 1. **MethodBeforeAdviceAdapter** + 用于将 `MethodBeforeAdvice` 类型的通知适配到 Spring AOP 拦截器链中。`MethodBeforeAdvice` 是一个在目标方法执行前执行的通知接口。 2. **ThrowsAdviceAdapter** + 用于将 `ThrowsAdvice` 类型的通知适配到 Spring AOP 拦截器链中。`ThrowsAdvice` 通知用于捕获目标方法抛出的异常。 3. **AfterReturningAdviceAdapter** + 用于将 `AfterReturningAdvice` 类型的通知适配到 Spring AOP 拦截器链中。`AfterReturningAdvice` 通知在目标方法正常返回后执行。 ### 六、类关系图 ~~~mermaid classDiagram direction BT class AdvisorAdapter { <> } class AfterReturningAdviceAdapter class MethodBeforeAdviceAdapter class ThrowsAdviceAdapter AfterReturningAdviceAdapter ..> AdvisorAdapter MethodBeforeAdviceAdapter ..> AdvisorAdapter ThrowsAdviceAdapter ..> AdvisorAdapter ~~~ ### 七、最佳实践 用自定义的 AdvisorAdapter 和 Advice 来实现对目标方法的增强。在示例中,首先注册了一个自定义的 AdvisorAdapter(NullReturningAdviceAdapter),然后创建了一个代理工厂(ProxyFactory)并向其添加了一个自定义的通知(MyNullReturningAdvice)。最后,通过代理工厂获取了代理对象,并调用了两个方法,其中一个方法会触发通知,另一个方法不会触发通知。 ```java public class AdvisorAdapterDemo { public static void main(String[] args) { // 注册自定义适配器 GlobalAdvisorAdapterRegistry.getInstance().registerAdvisorAdapter(new NullReturningAdviceAdapter()); // 创建代理工厂 ProxyFactory proxyFactory = new ProxyFactory(new MyService()); // 添加Advisor proxyFactory.addAdvice(new MyNullReturningAdvice()); // 获取代理对象 MyService proxy = (MyService) proxyFactory.getProxy(); // 不会触发通知 System.out.println("foo return value : " + proxy.foo()); // 换行 System.out.println("=================================="); // 会触发通知 System.out.println("bar return value : " + proxy.bar()); } } ``` 一个空返回通知的适配器,用于将空返回通知(NullReturningAdvice)适配到拦截器链中。它实现了 AdvisorAdapter 接口,包含了支持给定通知和获取方法拦截器的功能,以便将特定类型的通知行为暴露给基于拦截的 AOP 框架。 ```java /** * 空返回通知适配器,用于将空返回通知(NullReturningAdvice)适配到拦截器链中。 */ public class NullReturningAdviceAdapter implements AdvisorAdapter { /** * 判断该适配器是否支持给定的通知。 * @param advice 一个通知,如空返回通知(NullReturningAdvice) * @return 如果该适配器支持给定的通知,则返回 true;否则返回 false */ @Override public boolean supportsAdvice(Advice advice) { return (advice instanceof NullReturningAdvice); } /** * 获取一个方法拦截器,将给定的通知行为暴露给基于拦截的 AOP 框架。 * @param advisor Advisor。supportsAdvice() 方法必须在此对象上返回 true * @return 给定 Advisor 的方法拦截器 */ @Override public MethodInterceptor getInterceptor(Advisor advisor) { NullReturningAdvice advice = (NullReturningAdvice) advisor.getAdvice(); return new NullReturningAdviceInterceptor(advice); } } ``` 一个空返回通知拦截器,用于在方法执行后检查返回值是否为空,并根据情况执行空返回通知的逻辑。它实现了 MethodInterceptor 和 AfterAdvice 接口,通过拦截方法调用后的返回值来判断是否需要执行空返回通知,并在必要时调用空返回通知的逻辑。 ```java /** * 空返回通知拦截器,用于在方法执行后检查返回值是否为空,并根据情况执行空返回通知的逻辑。 */ public class NullReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice { /** 空返回通知 */ private final NullReturningAdvice advice; /** * 构造一个空返回通知拦截器。 * @param advice 空返回通知 */ public NullReturningAdviceInterceptor(NullReturningAdvice advice) { Assert.notNull(advice, "Advice must not be null"); this.advice = advice; } /** * 在方法执行后拦截,检查返回值是否为空,并根据情况执行空返回通知的逻辑。 * @param mi 方法调用信息 * @return 方法执行结果,如果返回值为空,则根据空返回通知执行后的返回值 * @throws Throwable 如果方法调用过程中发生异常,则抛出异常 */ @Override public Object invoke(MethodInvocation mi) throws Throwable { // 执行方法调用,获取返回值 Object retVal = mi.proceed(); // 如果返回值为空,则根据空返回通知执行后的返回值 if (retVal == null) { retVal = this.advice.nullReturning(mi.getMethod(), mi.getArguments(), mi.getThis()); } return retVal; } } ``` 一个空返回通知的定义,继承了 AfterAdvice 接口。它包含了一个方法 nullReturning,用于在目标方法返回值为空时执行相应的逻辑,并返回一个新的返回值。 ```java /** * 空返回通知接口,继承自 AfterAdvice。 */ public interface NullReturningAdvice extends AfterAdvice { /** * 当目标方法返回值为空时调用的方法。 * @param method 目标方法 * @param args 方法参数 * @param target 目标对象 * @return 空返回通知执行后的返回值 * @throws Throwable 如果在执行空返回通知的过程中发生异常,则抛出异常 */ Object nullReturning(Method method, Object[] args, @Nullable Object target) throws Throwable; } ``` 实现了`NullReturningAdvice`空返回通知接口,用于在目标方法返回值为空时执行特定逻辑。在 nullReturning 方法中返回一个默认的字符串值。 ```java public class MyNullReturningAdvice implements NullReturningAdvice { @Override public Object nullReturning(Method method, Object[] args, Object target) throws Throwable { return "this is a defaultValue"; } } ``` 简单的服务类,包含了两个方法 foo 和 bar。foo 方法执行后返回字符串 "this is a foo",而 bar 方法执行后返回 null。 ```java public class MyService { public String foo() { System.out.println("foo..."); return "this is a foo"; } public String bar() { System.out.println("bar..."); return null; } } ``` 运行结果,调用了 foo 方法,它返回 "this is a foo";然后调用了 bar 方法,由于其返回值为 null,因此触发了空返回通知,打印了相应的消息,并返回了默认值 "this is a defaultValue"。 ```java foo... foo return value : this is a foo ================================== bar... bar return value : this is a defaultValue ``` ### 八、源码分析 **注册适配器** 在`org.springframework.aop.framework.adapter.DefaultAdvisorAdapterRegistry#registerAdvisorAdapter`方法中,向适配器列表中注册一个新的 AdvisorAdapter 实例。 ```java /** * 注册一个Advisor适配器。 * @param adapter 要注册的Advisor适配器 */ @Override public void registerAdvisorAdapter(AdvisorAdapter adapter) { this.adapters.add(adapter); } ``` 在`org.springframework.aop.framework.adapter.DefaultAdvisorAdapterRegistry#adapters`字段中,用于存储 AdvisorAdapter 实例 ```java private final List adapters = new ArrayList<>(3); ``` **适配器转换拦截器** 在`org.springframework.aop.framework.JdkDynamicAopProxy#invoke`方法中,JDK动态代理入口中,获取指定方法的拦截链。 ```java @Override @Nullable public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // ... [代码部分省略以简化] try { // ... [代码部分省略以简化] // Get the interception chain for this method. List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); // ... [代码部分省略以简化] } finally { // ... [代码部分省略以简化] } } ``` 在`org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept`方法中,CGLIB动态代理入口中,获取指定方法的拦截链。 ```java @Override @Nullable public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { // ... [代码部分省略以简化] try { // ... [代码部分省略以简化] List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); // ... [代码部分省略以简化] } finally { // ... [代码部分省略以简化] } } ``` 在`org.springframework.aop.framework.AdvisedSupport#getInterceptorsAndDynamicInterceptionAdvice`方法中,配置确定给定方法的拦截器链,首先尝试从缓存中获取,如果缓存中不存在,则通过 AdvisorChainFactory 获取,并将结果存入缓存后返回。 ```java /** * 根据配置确定给定方法的拦截器链。 * @param method 被代理的方法 * @param targetClass 目标类 * @return 方法拦截器列表(可能还包括 InterceptorAndDynamicMethodMatchers) */ public List getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class targetClass) { // 创建方法缓存键 MethodCacheKey cacheKey = new MethodCacheKey(method); // 从缓存中获取拦截器链 List cached = this.methodCache.get(cacheKey); // 如果缓存为空 if (cached == null) { // 通过AdvisorChainFactory获取拦截器链 cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice( this, method, targetClass); // 将拦截器链放入缓存中 this.methodCache.put(cacheKey, cached); } // 返回拦截器链 return cached; } ``` 在`org.springframework.aop.framework.DefaultAdvisorChainFactory#getInterceptorsAndDynamicInterceptionAdvice`方法中,根据给定的AOP配置和方法,从Advisor列表中获取适用于该方法的拦截器链和动态拦截通知。然后根据配置和目标类的匹配情况选择性地添加适当的拦截器到列表中,并返回该列表。 ```java /** * 根据给定的AOP配置和方法,获取拦截器链和动态拦截通知。 * @param config AOP配置对象 * @param method 被代理的方法 * @param targetClass 目标类 * @return 返回一个拦截器链和动态拦截通知的列表 */ @Override public List getInterceptorsAndDynamicInterceptionAdvice( Advised config, Method method, @Nullable Class targetClass) { // 获取全局的AdvisorAdapterRegistry实例 AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance(); // 获取AOP配置中的所有Advisor数组 Advisor[] advisors = config.getAdvisors(); // 创建一个拦截器列表,初始化大小为advisors数组的长度 List interceptorList = new ArrayList<>(advisors.length); // ... [代码部分省略以简化] // 遍历所有的Advisor for (Advisor advisor : advisors) { // ... [代码部分省略以简化] // 获取Advisor对应的拦截器数组 Interceptor[] interceptors = registry.getInterceptors(advisor); // 将拦截器数组添加到拦截器列表中 interceptorList.addAll(Arrays.asList(interceptors)); // ... [代码部分省略以简化] } // 返回拦截器列表 return interceptorList; } ``` 在`org.springframework.aop.framework.adapter.DefaultAdvisorAdapterRegistry#getInterceptors`方法中,根据给定的Advisor对象,获取其对应的拦截器数组。它首先检查Advisor中的Advice类型,如果是MethodInterceptor类型,则直接添加到拦截器列表中。然后遍历注册的AdvisorAdapter,查找适配器支持的Advice类型,并将适配器返回的拦截器添加到列表中。最后将拦截器列表转换为数组并返回,如果未找到适配的拦截器则抛出UnknownAdviceTypeException异常。 ```java /** * 根据Advisor获取拦截器数组。 * @param advisor Advisor对象 * @return 返回拦截器数组 * @throws UnknownAdviceTypeException 如果Advisor中的Advice类型无法识别 */ @Override public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException { // 创建一个拦截器列表 List interceptors = new ArrayList<>(3); // 获取Advisor中的Advice对象 Advice advice = advisor.getAdvice(); // 如果Advice对象是MethodInterceptor类型 if (advice instanceof MethodInterceptor) { // 将MethodInterceptor添加到拦截器列表中 interceptors.add((MethodInterceptor) advice); } // 遍历所有的适配器 for (AdvisorAdapter adapter : this.adapters) { // 如果适配器支持Advice对象 if (adapter.supportsAdvice(advice)) { // 获取适配器的拦截器并添加到拦截器列表中 interceptors.add(adapter.getInterceptor(advisor)); } } // 如果拦截器列表为空 if (interceptors.isEmpty()) { // 抛出未知通知类型异常 throw new UnknownAdviceTypeException(advisor.getAdvice()); } // 将拦截器列表转换为数组并返回 return interceptors.toArray(new MethodInterceptor[0]); } ``` 在`com.xcs.spring.NullReturningAdviceAdapter#supportsAdvice`方法中,检查该适配器是否支持给定的Advice类型。 ```java /** * 检查该适配器是否支持给定的Advice类型。 * @param advice Advice对象 * @return 如果适配器支持给定的Advice类型,则返回true;否则返回false */ @Override public boolean supportsAdvice(Advice advice) { // 检查Advice对象是否是NullReturningAdvice类型 return (advice instanceof NullReturningAdvice); } ``` 在`com.xcs.spring.NullReturningAdviceAdapter#getInterceptor`方法中,首先从Advisor中获取Advice对象,并将其强制转换为NullReturningAdvice类型。然后,使用该Advice对象创建一个NullReturningAdviceInterceptor拦截器,并返回。 ```java /** * 根据Advisor获取拦截器。 * @param advisor Advisor对象 * @return 返回一个拦截器 */ @Override public MethodInterceptor getInterceptor(Advisor advisor) { // 强制转换Advisor中的Advice对象为NullReturningAdvice类型 NullReturningAdvice advice = (NullReturningAdvice) advisor.getAdvice(); // 创建一个NullReturningAdviceInterceptor拦截器,将Advisor中的Advice作为参数传入 return new NullReturningAdviceInterceptor(advice); } ``` ================================================ FILE: spring-aop/spring-aop-advisorAdapter/pom.xml ================================================ com.xcs.spring spring-aop 0.0.1-SNAPSHOT 4.0.0 spring-aop-advisorAdapter ================================================ FILE: spring-aop/spring-aop-advisorAdapter/src/main/java/com/xcs/spring/AdvisorAdapterDemo.java ================================================ package com.xcs.spring; import org.springframework.aop.framework.ProxyFactory; import org.springframework.aop.framework.adapter.GlobalAdvisorAdapterRegistry; public class AdvisorAdapterDemo { public static void main(String[] args) { // 注册自定义适配器 GlobalAdvisorAdapterRegistry.getInstance().registerAdvisorAdapter(new NullReturningAdviceAdapter()); // 创建代理工厂 ProxyFactory proxyFactory = new ProxyFactory(new MyService()); // 添加Advisor proxyFactory.addAdvice(new MyNullReturningAdvice()); // 获取代理对象 MyService proxy = (MyService) proxyFactory.getProxy(); // 不会触发通知 System.out.println("foo return value : " + proxy.foo()); // 换行 System.out.println("=================================="); // 会触发通知 System.out.println("bar return value : " + proxy.bar()); } } ================================================ FILE: spring-aop/spring-aop-advisorAdapter/src/main/java/com/xcs/spring/MyNullReturningAdvice.java ================================================ package com.xcs.spring; import java.lang.reflect.Method; public class MyNullReturningAdvice implements NullReturningAdvice { @Override public Object nullReturning(Method method, Object[] args, Object target) throws Throwable { return "this is a defaultValue"; } } ================================================ FILE: spring-aop/spring-aop-advisorAdapter/src/main/java/com/xcs/spring/MyService.java ================================================ package com.xcs.spring; public class MyService { public String foo() { System.out.println("foo..."); return "this is a foo"; } public String bar() { System.out.println("bar..."); return null; } } ================================================ FILE: spring-aop/spring-aop-advisorAdapter/src/main/java/com/xcs/spring/NullReturningAdvice.java ================================================ package com.xcs.spring; import org.springframework.aop.AfterAdvice; import org.springframework.lang.Nullable; import java.lang.reflect.Method; /** * 空返回通知接口,继承自 AfterAdvice。 */ public interface NullReturningAdvice extends AfterAdvice { /** * 当目标方法返回值为空时调用的方法。 * @param method 目标方法 * @param args 方法参数 * @param target 目标对象 * @return 空返回通知执行后的返回值 * @throws Throwable 如果在执行空返回通知的过程中发生异常,则抛出异常 */ Object nullReturning(Method method, Object[] args, @Nullable Object target) throws Throwable; } ================================================ FILE: spring-aop/spring-aop-advisorAdapter/src/main/java/com/xcs/spring/NullReturningAdviceAdapter.java ================================================ package com.xcs.spring; import org.aopalliance.aop.Advice; import org.aopalliance.intercept.MethodInterceptor; import org.springframework.aop.Advisor; import org.springframework.aop.framework.adapter.AdvisorAdapter; /** * 空返回通知适配器,用于将空返回通知(NullReturningAdvice)适配到拦截器链中。 */ public class NullReturningAdviceAdapter implements AdvisorAdapter { /** * 判断该适配器是否支持给定的通知。 * @param advice 一个通知,如空返回通知(NullReturningAdvice) * @return 如果该适配器支持给定的通知,则返回 true;否则返回 false */ @Override public boolean supportsAdvice(Advice advice) { return (advice instanceof NullReturningAdvice); } /** * 获取一个方法拦截器,将给定的通知行为暴露给基于拦截的 AOP 框架。 * @param advisor Advisor。supportsAdvice() 方法必须在此对象上返回 true * @return 给定 Advisor 的方法拦截器 */ @Override public MethodInterceptor getInterceptor(Advisor advisor) { NullReturningAdvice advice = (NullReturningAdvice) advisor.getAdvice(); return new NullReturningAdviceInterceptor(advice); } } ================================================ FILE: spring-aop/spring-aop-advisorAdapter/src/main/java/com/xcs/spring/NullReturningAdviceInterceptor.java ================================================ package com.xcs.spring; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import org.springframework.aop.AfterAdvice; import org.springframework.util.Assert; /** * 空返回通知拦截器,用于在方法执行后检查返回值是否为空,并根据情况执行空返回通知的逻辑。 */ public class NullReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice { /** 空返回通知 */ private final NullReturningAdvice advice; /** * 构造一个空返回通知拦截器。 * @param advice 空返回通知 */ public NullReturningAdviceInterceptor(NullReturningAdvice advice) { Assert.notNull(advice, "Advice must not be null"); this.advice = advice; } /** * 在方法执行后拦截,检查返回值是否为空,并根据情况执行空返回通知的逻辑。 * @param mi 方法调用信息 * @return 方法执行结果,如果返回值为空,则根据空返回通知执行后的返回值 * @throws Throwable 如果方法调用过程中发生异常,则抛出异常 */ @Override public Object invoke(MethodInvocation mi) throws Throwable { // 执行方法调用,获取返回值 Object retVal = mi.proceed(); // 如果返回值为空,则根据空返回通知执行后的返回值 if (retVal == null) { retVal = this.advice.nullReturning(mi.getMethod(), mi.getArguments(), mi.getThis()); } return retVal; } } ================================================ FILE: spring-aop/spring-aop-advisorAdapterRegistry/README.md ================================================ ## AdvisorAdapterRegistry - [AdvisorAdapterRegistry](#advisoradapterregistry) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、接口源码](#四接口源码) - [五、主要实现](#五主要实现) - [六、类关系图](#六类关系图) - [七、最佳实践](#七最佳实践) - [八、源码分析](#八源码分析) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 `AdvisorAdapterRegistry`接口是Spring AOP中的关键接口之一,用于注册和管理AdvisorAdapters,它负责将Advisor与AOP框架所支持的特定拦截器关联起来,实现对目标对象方法的拦截和增强,从而实现面向切面编程的功能。 ### 三、主要功能 1. **注册AdvisorAdapters** + 允许我们注册自定义的AdvisorAdapters,以适配新的拦截器类型或扩展现有的拦截器逻辑。 3. **支持内置拦截器** + 默认实现预先注册了一些标准的AdvisorAdapters,用于支持Spring AOP框架内置的拦截器类型(如BeforeAdvice、AfterReturningAdvice等)。 ### 四、接口源码 作为Advisor适配器的注册表。它提供了方法来包装给定的advice为Advisor,并获取Advisor中的拦截器数组。通过注册AdvisorAdapter,实现了Advisor与AOP框架所支持的不同拦截器类型之间的适配。 ```java /** * Advisor适配器注册表的接口。 * *

这是一个SPI接口,不应该由任何Spring用户实现。 * * @author Rod Johnson * @author Rob Harrop */ public interface AdvisorAdapterRegistry { /** * 返回一个包装了给定advice的{@link Advisor}。 *

默认情况下应该至少支持 * {@link org.aopalliance.intercept.MethodInterceptor}, * {@link org.springframework.aop.MethodBeforeAdvice}, * {@link org.springframework.aop.AfterReturningAdvice}, * {@link org.springframework.aop.ThrowsAdvice}。 * @param advice 应该是一个advice的对象 * @return 包装了给定advice的Advisor(永远不会为{@code null}; * 如果advice参数本身就是一个Advisor,则直接返回) * @throws UnknownAdviceTypeException 如果没有注册的advisor adapter * 能够包装给定的advice */ Advisor wrap(Object advice) throws UnknownAdviceTypeException; /** * 返回一组AOP Alliance MethodInterceptors,以允许在基于拦截的框架中使用给定的Advisor。 *

如果Advisor是一个{@link org.springframework.aop.PointcutAdvisor}, * 则不必担心与其关联的切入点表达式只需返回一个拦截器。 * @param advisor 要查找拦截器的Advisor * @return 一组MethodInterceptor,用于暴露此Advisor的行为 * @throws UnknownAdviceTypeException 如果Advisor类型 * 不被任何注册的AdvisorAdapter理解 */ MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException; /** * 注册给定的{@link AdvisorAdapter}。 * 注意,不需要为AOP Alliance Interceptors或Spring Advices注册适配器 * 这些必须由{@code AdvisorAdapterRegistry}的实现自动识别。 * @param adapter 理解特定Advisor或Advice类型的AdvisorAdapter */ void registerAdvisorAdapter(AdvisorAdapter adapter); } ``` ### 五、主要实现 1. **DefaultAdvisorAdapterRegistry** + 默认Advisor适配器注册表实现,预先注册了标准的Advisor适配器,支持将各种类型的Advice适配到AOP Alliance MethodInterceptor,并允许我们注册自定义的Advisor适配器,从而实现了Advisor与拦截器之间的灵活适配和管理。 ### 六、类关系图 ~~~mermaid classDiagram direction BT class AdvisorAdapterRegistry { <> } class DefaultAdvisorAdapterRegistry DefaultAdvisorAdapterRegistry ..> AdvisorAdapterRegistry ~~~ ### 七、最佳实践 使用`DefaultAdvisorAdapterRegistry`来包装自定义的`MyMethodBeforeAdvice`,并获取其对应的拦截器数组。通过`wrap()`方法将`MyMethodBeforeAdvice`转换为`Advisor`,然后使用`getInterceptors()`方法获取该`Advisor`中的拦截器数组,最后输出拦截器的信息。 ```java public class AdvisorAdapterRegistryDemo { public static void main(String[] args) { // 创建默认的Advisor适配器注册表实例 DefaultAdvisorAdapterRegistry registry = new DefaultAdvisorAdapterRegistry(); // 包装给定的MyMethodBeforeAdvice为Advisor Advisor advisor = registry.wrap(new MyMethodBeforeAdvice()); // 获取Advisor中的拦截器数组 MethodInterceptor[] interceptors = registry.getInterceptors(advisor); // 输出拦截器信息 for (MethodInterceptor interceptor : interceptors) { System.out.println("interceptor = " + interceptor); } } } ``` ### 八、源码分析 实现了`AdvisorAdapterRegistry`接口的默认实现`DefaultAdvisorAdapterRegistry`,支持将不同类型的Advice对象适配为Advisor,并提供获取Advisor中拦截器数组的功能。它预先注册了一些常见的Advisor适配器,并允许用户注册自定义的适配器。其核心逻辑包括将Advice对象包装为Advisor、根据Advisor获取拦截器数组以及注册Advisor适配器。 [AdvisorAdapter源码分析](../spring-aop-advisorAdapter/README.md) ```java /** * AdvisorAdapterRegistry接口的默认实现。 * 支持{@link org.aopalliance.intercept.MethodInterceptor}、 * {@link org.springframework.aop.MethodBeforeAdvice}、 * {@link org.springframework.aop.AfterReturningAdvice}、 * {@link org.springframework.aop.ThrowsAdvice}。 * * @author Rod Johnson * @author Rob Harrop * @author Juergen Hoeller */ @SuppressWarnings("serial") public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable { // 用于存储注册的AdvisorAdapter的列表 private final List adapters = new ArrayList<>(3); /** * 创建一个新的DefaultAdvisorAdapterRegistry实例,并注册已知的适配器。 * 这里的“已知的适配器”包括MethodBeforeAdviceAdapter、AfterReturningAdviceAdapter、ThrowsAdviceAdapter。 */ public DefaultAdvisorAdapterRegistry() { // 注册MethodBeforeAdviceAdapter适配器 registerAdvisorAdapter(new MethodBeforeAdviceAdapter()); // 注册AfterReturningAdviceAdapter适配器 registerAdvisorAdapter(new AfterReturningAdviceAdapter()); // 注册ThrowsAdviceAdapter适配器 registerAdvisorAdapter(new ThrowsAdviceAdapter()); } /** * 将给定的adviceObject包装为Advisor。 * 如果adviceObject已经是Advisor,则直接返回; * 如果不是Advice类型,则抛出UnknownAdviceTypeException; * 如果advice是MethodInterceptor类型,则创建一个DefaultPointcutAdvisor并返回; * 否则,遍历已注册的AdvisorAdapter,找到支持advice的适配器,创建一个DefaultPointcutAdvisor并返回。 * * @param adviceObject 要包装为Advisor的Advice对象 * @return 包装后的Advisor对象 * @throws UnknownAdviceTypeException 如果adviceObject无法被识别为Advisor或Advice */ @Override public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException { if (adviceObject instanceof Advisor) { return (Advisor) adviceObject; } if (!(adviceObject instanceof Advice)) { throw new UnknownAdviceTypeException(adviceObject); } Advice advice = (Advice) adviceObject; if (advice instanceof MethodInterceptor) { // 对于MethodInterceptor类型的Advice,不需要适配器,直接创建Advisor并返回 return new DefaultPointcutAdvisor(advice); } // 遍历已注册的AdvisorAdapter,查找支持当前Advice的适配器 for (AdvisorAdapter adapter : this.adapters) { // 检查是否支持当前Advice if (adapter.supportsAdvice(advice)) { // 创建Advisor并返回 return new DefaultPointcutAdvisor(advice); } } // 如果无法找到合适的适配器,抛出异常 throw new UnknownAdviceTypeException(advice); } /** * 获取Advisor中的拦截器数组。 * 如果Advisor中的Advice是MethodInterceptor类型,则直接返回; * 否则,遍历已注册的AdvisorAdapter,找到支持Advisor中的Advice的适配器,并获取对应的拦截器,返回拦截器数组。 * * @param advisor 要获取拦截器数组的Advisor对象 * @return 包含Advisor中拦截器的数组 * @throws UnknownAdviceTypeException 如果Advisor中的Advice无法被识别 */ @Override public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException { List interceptors = new ArrayList<>(3); Advice advice = advisor.getAdvice(); // 如果Advisor中的Advice是MethodInterceptor类型,直接将其添加到拦截器数组中 if (advice instanceof MethodInterceptor) { interceptors.add((MethodInterceptor) advice); } // 遍历已注册的AdvisorAdapter,查找支持Advisor中的Advice的适配器 for (AdvisorAdapter adapter : this.adapters) { // 如果适配器支持当前Advice,获取其拦截器并添加到数组中 if (adapter.supportsAdvice(advice)) { interceptors.add(adapter.getInterceptor(advisor)); } } // 如果拦截器数组为空,表示未找到适配器,抛出异常 if (interceptors.isEmpty()) { throw new UnknownAdviceTypeException(advice); } // 将拦截器数组转换为数组并返回 return interceptors.toArray(new MethodInterceptor[0]); } /** * 注册给定的AdvisorAdapter。 * * @param adapter 要注册的AdvisorAdapter对象 */ @Override public void registerAdvisorAdapter(AdvisorAdapter adapter) { this.adapters.add(adapter); } } ``` ================================================ FILE: spring-aop/spring-aop-advisorAdapterRegistry/pom.xml ================================================ com.xcs.spring spring-aop 0.0.1-SNAPSHOT 4.0.0 spring-aop-advisorAdapterRegistry ================================================ FILE: spring-aop/spring-aop-advisorAdapterRegistry/src/main/java/com/xcs/spring/AdvisorAdapterRegistryDemo.java ================================================ package com.xcs.spring; import org.aopalliance.intercept.MethodInterceptor; import org.springframework.aop.Advisor; import org.springframework.aop.framework.adapter.DefaultAdvisorAdapterRegistry; public class AdvisorAdapterRegistryDemo { public static void main(String[] args) { // 创建默认的Advisor适配器注册表实例 DefaultAdvisorAdapterRegistry registry = new DefaultAdvisorAdapterRegistry(); // 包装给定的MyMethodBeforeAdvice为Advisor Advisor advisor = registry.wrap(new MyMethodBeforeAdvice()); // 获取Advisor中的拦截器数组 MethodInterceptor[] interceptors = registry.getInterceptors(advisor); // 输出拦截器信息 for (MethodInterceptor interceptor : interceptors) { System.out.println("interceptor = " + interceptor); } } } ================================================ FILE: spring-aop/spring-aop-advisorAdapterRegistry/src/main/java/com/xcs/spring/MyMethodBeforeAdvice.java ================================================ package com.xcs.spring; import org.springframework.aop.MethodBeforeAdvice; import java.lang.reflect.Method; public class MyMethodBeforeAdvice implements MethodBeforeAdvice { @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("Before method: " + method.getName()); } } ================================================ FILE: spring-aop/spring-aop-advisorChainFactory/README.md ================================================ ## AdvisorChainFactory - [AdvisorChainFactory](#advisorchainfactory) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、接口源码](#四接口源码) - [五、主要实现](#五主要实现) - [六、类关系图](#六类关系图) - [七、最佳实践](#七最佳实践) - [八、源码分析](#八源码分析) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 `AdvisorChainFactory`接口是Spring AOP中负责创建顾问链的工厂接口,通过`getInterceptorsAndDynamicInterceptionAdvice()`方法,它能够将一组顾问对象转换为拦截器数组,用于管理和执行切面逻辑,提供了灵活性和可扩展性来定制切面的执行方式。 ### 三、主要功能 1. **创建顾问链(Advisor Chain)** + 通过`getInterceptorsAndDynamicInterceptionAdvice()`方法,将一组顾问对象转换为拦截器数组,形成顾问链,用于在目标方法执行前后执行特定的操作。 2. **动态顾问链的创建** + 可以根据运行时的情况动态地创建顾问链,例如根据目标对象的类型或方法签名动态地决定哪些通知要被执行。 ### 四、接口源码 `AdvisorChainFactory`接口 ,用于创建Advisor链的工厂接口。其中的方法 `getInterceptorsAndDynamicInterceptionAdvice()` 接受AOP配置(`Advised`对象)、被代理的方法以及目标类,并返回一个包含MethodInterceptors的列表,用于配置Advisor链。这个接口的目的是根据给定的配置,确定在代理方法执行时应该应用哪些拦截器,以及是否需要动态匹配方法。 ```java /** * Advisor链工厂的工厂接口。 * Factory interface for advisor chains. * * @author Rod Johnson * @author Juergen Hoeller */ public interface AdvisorChainFactory { /** * 根据给定的Advisor链配置,确定一组MethodInterceptor对象。 * Determine a list of {@link org.aopalliance.intercept.MethodInterceptor} objects * for the given advisor chain configuration. * @param config 表示AOP配置的Advised对象 * @param method 被代理的方法 * @param targetClass 目标类(可能为null,表示没有目标对象的代理,在这种情况下,方法的声明类是下一个最佳选择) * @return 一个MethodInterceptors的列表(也可能包括InterceptorAndDynamicMethodMatchers) */ List getInterceptorsAndDynamicInterceptionAdvice(Advised config, Method method, @Nullable Class targetClass); } ``` ### 五、主要实现 1. **DefaultAdvisorChainFactory** + 负责根据给定的AOP配置、被代理的方法和目标类,确定应该应用哪些拦截器,并支持动态方法匹配和缓存机制,以提供高效的顾问链创建功能 ### 六、类关系图 ~~~mermaid classDiagram direction BT class AdvisorChainFactory { <> } class DefaultAdvisorChainFactory DefaultAdvisorChainFactory ..> AdvisorChainFactory ~~~ ### 七、最佳实践 使用`DefaultAdvisorChainFactory`类来创建Advisor链。首先,创建了一个`AdvisedSupport`对象,配置了前置通知和后置返回通知。然后,指定了目标类和目标方法。接着,实例化了`DefaultAdvisorChainFactory`类,并调用其`getInterceptorsAndDynamicInterceptionAdvice()`方法获取Advisor链。最后,打印了Advisor链中的拦截器。 ```java public class AdvisorChainFactoryDemo { public static void main(String[] args) throws NoSuchMethodException { // 创建AOP配置对象 AdvisedSupport config = new AdvisedSupport(); // 添加前置通知 config.addAdvice(new MyMethodBeforeAdvice()); // 添加后置返回通知 config.addAdvice(new MyAfterReturningAdvice()); // 设置目标类 Class targetClass = MyService.class; // 获取目标方法 Method method = targetClass.getDeclaredMethod("foo"); // 创建默认的Advisor链工厂实例 DefaultAdvisorChainFactory chainFactory = new DefaultAdvisorChainFactory(); // 获取Advisor链 List chain = chainFactory.getInterceptorsAndDynamicInterceptionAdvice(config, method, targetClass); // 打印Advisor链中的拦截器 chain.forEach(System.out::println); } } ``` 运行结果,显示了Advisor链中的两个拦截器,分别是`MethodBeforeAdviceInterceptor`和`AfterReturningAdviceInterceptor`。这些拦截器是根据配置的前置通知和后置返回通知生成的,用于在目标方法执行前后进行相应的操作。 ```java org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor@215be6bb org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor@4439f31e ``` ### 八、源码分析 `DefaultAdvisorChainFactory`类。它提供了一种简单但确定的方法,根据给定的`Advised`对象,在方法级别确定通知链的构建顺序。通过遍历配置的Advisor数组,并根据Advisor的类型和Pointcut来确定应该应用哪些拦截器,最终返回一个拦截器列表。在此过程中,它支持动态方法匹配和引入拦截器的处理,并提供了一个缓存机制来提高性能。 [AdvisorAdapterRegistry源码分析](../spring-aop-advisorAdapterRegistry/README.md) ```java /** * 给定一个 {@link Advised} 对象,为一个方法确定一个通知链的简单但确定的方法。总是重新构建每个通知链; * 子类可以提供缓存功能。 * * @author Juergen Hoeller * @author Rod Johnson * @author Adrian Colyer * @since 2.0.3 */ @SuppressWarnings("serial") public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializable { @Override public List getInterceptorsAndDynamicInterceptionAdvice( Advised config, Method method, @Nullable Class targetClass) { // 获取Advisor适配器注册表 AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance(); // 获取AOP配置中的所有Advisor Advisor[] advisors = config.getAdvisors(); // 创建一个拦截器列表 List interceptorList = new ArrayList<>(advisors.length); // 获取实际类 Class actualClass = (targetClass != null ? targetClass : method.getDeclaringClass()); Boolean hasIntroductions = null; // 遍历所有Advisor for (Advisor advisor : advisors) { if (advisor instanceof PointcutAdvisor) { // 添加条件性地。 PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor; if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) { // 获取Advisor的Pointcut和MethodMatcher MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher(); boolean match; if (mm instanceof IntroductionAwareMethodMatcher) { if (hasIntroductions == null) { // 检查是否存在匹配的IntroductionAdvisor hasIntroductions = hasMatchingIntroductions(advisors, actualClass); } match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions); } else { match = mm.matches(method, actualClass); } // 如果匹配,则将Interceptor添加到拦截器列表中 if (match) { MethodInterceptor[] interceptors = registry.getInterceptors(advisor); if (mm.isRuntime()) { // 如果是动态匹配,则创建一个新的InterceptorAndDynamicMethodMatcher对象 for (MethodInterceptor interceptor : interceptors) { interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm)); } } else { // 否则直接添加Interceptor interceptorList.addAll(Arrays.asList(interceptors)); } } } } else if (advisor instanceof IntroductionAdvisor) { IntroductionAdvisor ia = (IntroductionAdvisor) advisor; if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) { // 如果是IntroductionAdvisor,则直接获取Interceptor并添加到拦截器列表中 Interceptor[] interceptors = registry.getInterceptors(advisor); interceptorList.addAll(Arrays.asList(interceptors)); } } else { // 对于其他类型的Advisor,直接获取Interceptor并添加到拦截器列表中 Interceptor[] interceptors = registry.getInterceptors(advisor); interceptorList.addAll(Arrays.asList(interceptors)); } } // 返回拦截器列表 return interceptorList; } /** * 判断Advisor中是否存在匹配的引入拦截器。 */ private static boolean hasMatchingIntroductions(Advisor[] advisors, Class actualClass) { for (Advisor advisor : advisors) { if (advisor instanceof IntroductionAdvisor) { IntroductionAdvisor ia = (IntroductionAdvisor) advisor; if (ia.getClassFilter().matches(actualClass)) { return true; } } } return false; } } ``` ================================================ FILE: spring-aop/spring-aop-advisorChainFactory/pom.xml ================================================ com.xcs.spring spring-aop 0.0.1-SNAPSHOT 4.0.0 spring-aop-advisorChainFactory ================================================ FILE: spring-aop/spring-aop-advisorChainFactory/src/main/java/com/xcs/spring/AdvisorChainFactoryDemo.java ================================================ package com.xcs.spring; import org.springframework.aop.framework.AdvisedSupport; import org.springframework.aop.framework.DefaultAdvisorChainFactory; import java.lang.reflect.Method; import java.util.List; public class AdvisorChainFactoryDemo { public static void main(String[] args) throws NoSuchMethodException { // 创建AOP配置对象 AdvisedSupport config = new AdvisedSupport(); // 添加前置通知 config.addAdvice(new MyMethodBeforeAdvice()); // 添加后置返回通知 config.addAdvice(new MyAfterReturningAdvice()); // 设置目标类 Class targetClass = MyService.class; // 获取目标方法 Method method = targetClass.getDeclaredMethod("foo"); // 创建默认的Advisor链工厂实例 DefaultAdvisorChainFactory chainFactory = new DefaultAdvisorChainFactory(); // 获取Advisor链 List chain = chainFactory.getInterceptorsAndDynamicInterceptionAdvice(config, method, targetClass); // 打印Advisor链中的拦截器 chain.forEach(System.out::println); } } ================================================ FILE: spring-aop/spring-aop-advisorChainFactory/src/main/java/com/xcs/spring/MyAfterReturningAdvice.java ================================================ package com.xcs.spring; import org.springframework.aop.AfterReturningAdvice; import java.lang.reflect.Method; public class MyAfterReturningAdvice implements AfterReturningAdvice { @Override public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println("After method: " + method.getName()); } } ================================================ FILE: spring-aop/spring-aop-advisorChainFactory/src/main/java/com/xcs/spring/MyMethodBeforeAdvice.java ================================================ package com.xcs.spring; import org.springframework.aop.MethodBeforeAdvice; import java.lang.reflect.Method; public class MyMethodBeforeAdvice implements MethodBeforeAdvice { @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("Before method: " + method.getName()); } } ================================================ FILE: spring-aop/spring-aop-advisorChainFactory/src/main/java/com/xcs/spring/MyService.java ================================================ package com.xcs.spring; public class MyService { public void foo() { System.out.println("foo..."); } } ================================================ FILE: spring-aop/spring-aop-annotationAwareAspectJAutoProxyCreator/README.md ================================================ ## AnnotationAwareAspectJAutoProxyCreator - [AnnotationAwareAspectJAutoProxyCreator](#annotationawareaspectjautoproxycreator) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、类关系图](#四类关系图) - [五、最佳实践](#五最佳实践) - [六、时序图](#六时序图) - [七、源码分析](#七源码分析) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 `AnnotationAwareAspectJAutoProxyCreator`是Spring AOP中的关键类,负责自动检测标记有`@Aspect`注解的切面类,并将其代理到Spring应用程序的bean中,实现切面逻辑的自动织入,从而支持注解驱动的面向切面编程。 ### 三、主要功能 1. **自动代理创建** + 检测应用程序上下文中被`@Aspect`注解标记的切面类,并自动创建代理对象,使切面逻辑能够在目标bean的方法调用中被织入。 2. **切面逻辑织入** + 将切面逻辑织入到目标bean的方法调用中,实现例如在方法执行前后、异常抛出时等的切面操作,以实现各种横切关注点的功能。 3. **支持注解驱动的切面编程** + 提供了基于注解的切面编程的支持,通过`@Aspect`等注解,开发者能够更便捷地定义切面类和切面逻辑。 4. **灵活的切面配置** + 允许我们通过注解方式配置切面,而不需要在XML配置文件中显式声明,从而使切面的配置更加灵活和便捷。 5. **AOP功能整合** + 将AspectJ切面功能整合到Spring框架中,使得我们能够在Spring应用程序中使用AspectJ风格的切面编程,从而更好地实现横切关注点的模块化和重用。 ### 四、类关系图 ~~~mermaid classDiagram direction BT class AbstractAdvisorAutoProxyCreator class AbstractAutoProxyCreator class AnnotationAwareAspectJAutoProxyCreator class AopInfrastructureBean { <> } class AspectJAwareAdvisorAutoProxyCreator class Aware { <> } class BeanClassLoaderAware { <> } class BeanFactoryAware { <> } class BeanPostProcessor { <> } class InstantiationAwareBeanPostProcessor { <> } class ProxyConfig class ProxyProcessorSupport class SmartInstantiationAwareBeanPostProcessor { <> } AbstractAdvisorAutoProxyCreator --> AbstractAutoProxyCreator AbstractAutoProxyCreator ..> BeanFactoryAware AbstractAutoProxyCreator --> ProxyProcessorSupport AbstractAutoProxyCreator ..> SmartInstantiationAwareBeanPostProcessor AnnotationAwareAspectJAutoProxyCreator --> AspectJAwareAdvisorAutoProxyCreator AspectJAwareAdvisorAutoProxyCreator --> AbstractAdvisorAutoProxyCreator BeanClassLoaderAware --> Aware BeanFactoryAware --> Aware InstantiationAwareBeanPostProcessor --> BeanPostProcessor ProxyProcessorSupport ..> AopInfrastructureBean ProxyProcessorSupport ..> BeanClassLoaderAware ProxyProcessorSupport --> ProxyConfig SmartInstantiationAwareBeanPostProcessor --> InstantiationAwareBeanPostProcessor ~~~ ### 五、最佳实践 使用`EnableAspectJAutoProxy` 注解和Spring的基于注解的应用上下文来启用AspectJ自动代理功能。在程序中,首先创建了一个基于注解的应用上下文,然后通过该上下文获取了`MyService` bean,并调用了其方法。 ```java public class EnableAspectJAutoProxyDemo { public static void main(String[] args) { // 创建基于注解的应用上下文 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); // 从应用上下文中获取MyService bean MyService myService = context.getBean(MyService.class); // 调用MyService的方法 myService.foo(); } } ``` `AppConfig` 类是一个使用 `@Configuration` 注解标记的配置类,通过 `@EnableAspectJAutoProxy` 开启了 AspectJ 自动代理功能,并通过 `@ComponentScan` 启用了组件扫描,用于自动发现和注册 Spring 组件。 ```java @Configuration @EnableAspectJAutoProxy @ComponentScan public class AppConfig { } ``` `MyService` 类是一个使用 `@Service` 注解标记的服务类,提供了一个名为 `foo()` 的方法,该方法在调用时会打印消息 "foo..."。 ```java @Service public class MyService { public void foo() { System.out.println("foo..."); } } ``` `MyAspect`是一个使用了`@Aspect`注解的Java类,表示它是一个切面。在这个类中,定义了一个名为`advice`的方法,并使用了`@Before` 注解来指定在目标方法执行之前执行的通知。 ```java @Aspect @Component public class MyAspect { @Before("execution(* com.xcs.spring.MyService+.*(..))") public void before() { System.out.println("Before method execution"); } } ``` 运行结果,调用 `MyService` 类的 `foo()` 方法之前,成功地执行了一个切面通知,输出了 "Before method execution" 的消息,然后执行了 `foo()` 方法,输出了 "foo..." 的消息。 ```java Before method execution foo... ``` ### 六、时序图 ~~~mermaid sequenceDiagram autonumber BeanFactory->>AbstractAutoProxyCreator:postProcessAfterInitialization() Note over BeanFactory,AbstractAutoProxyCreator: BeanFactory调用初始化后处理方法 AbstractAutoProxyCreator->>AbstractAutoProxyCreator:wrapIfNecessary() Note over AbstractAutoProxyCreator,AbstractAutoProxyCreator: 条件判断与代理创建 AbstractAutoProxyCreator->>AbstractAdvisorAutoProxyCreator:getAdvicesAndAdvisorsForBean() Note over AbstractAutoProxyCreator,AbstractAdvisorAutoProxyCreator: 获取并返回适用的Advisors数组 AbstractAdvisorAutoProxyCreator->>AbstractAdvisorAutoProxyCreator:findEligibleAdvisors() Note over AbstractAdvisorAutoProxyCreator,AbstractAdvisorAutoProxyCreator: 查找并扩展可应用的Advisors列表 AbstractAdvisorAutoProxyCreator->>AnnotationAwareAspectJAutoProxyCreator:findCandidateAdvisors() Note over AbstractAdvisorAutoProxyCreator,AnnotationAwareAspectJAutoProxyCreator: 添加Spring和AspectJ,合并为候选Advisors列表 AnnotationAwareAspectJAutoProxyCreator->>AbstractAdvisorAutoProxyCreator:super.findCandidateAdvisors() Note over AnnotationAwareAspectJAutoProxyCreator,AbstractAdvisorAutoProxyCreator: 获取自动代理的候选Advisors列表 AbstractAdvisorAutoProxyCreator->>BeanFactoryAdvisorRetrievalHelper:findAdvisorBeans() Note over AbstractAdvisorAutoProxyCreator,BeanFactoryAdvisorRetrievalHelper: 获取当前bean工厂中所有合格的Advisor beans AnnotationAwareAspectJAutoProxyCreator->>BeanFactoryAspectJAdvisorsBuilder:buildAspectJAdvisors() Note over AnnotationAwareAspectJAutoProxyCreator,BeanFactoryAspectJAdvisorsBuilder: 获取AspectJ注解的切面并创建Advisor AnnotationAwareAspectJAutoProxyCreator->>AbstractAdvisorAutoProxyCreator:返回Advisors AbstractAdvisorAutoProxyCreator->>AbstractAdvisorAutoProxyCreator:findAdvisorsThatCanApply() Note over AbstractAdvisorAutoProxyCreator,AbstractAdvisorAutoProxyCreator: 查找适用的候选顾问并设置代理 AbstractAdvisorAutoProxyCreator->>AopUtils:findAdvisorsThatCanApply() Note over AbstractAdvisorAutoProxyCreator,AopUtils: 筛选适用的顾问并添加到列表。 AopUtils->>AopUtils:canApply(advisor,targetClass,hasIntroductions) Note over AopUtils,AopUtils: 判断顾问是否适用于目标类。 AopUtils->>AopUtils:canApply(pc,targetClass,hasIntroductions) Note over AopUtils,AopUtils: 判断切点是否适用于目标类。 AbstractAdvisorAutoProxyCreator->>AspectJAwareAdvisorAutoProxyCreator:extendAdvisors() Note over AbstractAdvisorAutoProxyCreator,AspectJAwareAdvisorAutoProxyCreator: 添加AspectJ支持到Advisor链。 AspectJAwareAdvisorAutoProxyCreator->>AspectJProxyUtils:makeAdvisorChainAspectJCapableIfNecessary() Note over AspectJAwareAdvisorAutoProxyCreator,AspectJProxyUtils: 在Advisor链中添加AspectJ支持。 AbstractAdvisorAutoProxyCreator->>AbstractAutoProxyCreator:返回拦截器 AbstractAutoProxyCreator->>AbstractAutoProxyCreator:createProxy() Note over AbstractAutoProxyCreator,AbstractAutoProxyCreator: 创建 AOP 代理对象的过程。 AbstractAutoProxyCreator->>ProxyFactory:new ProxyFactory() ProxyFactory->>AbstractAutoProxyCreator:返回proxyFactory AbstractAutoProxyCreator->>ProxyFactory:getProxy() ProxyFactory->>AbstractAutoProxyCreator:返回代理对象 AbstractAutoProxyCreator->>BeanFactory:返回代理对象 ~~~ ### 七、源码分析 在`org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization`方法中,用于在初始化后对bean进行后置处理。它的作用是检查是否需要对bean创建代理,并在需要时使用配置的拦截器创建代理对象,以实现AOP功能。 ```java /** * 如果子类确定要将该bean标识为需要代理的bean,则使用配置的拦截器创建代理。 * @see #getAdvicesAndAdvisorsForBean */ @Override public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) { if (bean != null) { // 获取缓存键 Object cacheKey = getCacheKey(bean.getClass(), beanName); // 如果早期代理引用集合中存在该bean,并且不是同一引用,则进行包装 if (this.earlyProxyReferences.remove(cacheKey) != bean) { return wrapIfNecessary(bean, beanName, cacheKey); } } // 返回bean return bean; } ``` 在`org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNecessary`方法中,首先检查bean的名称是否存在且是否属于目标源bean,如果是,则直接返回原始bean实例;然后检查缓存中是否存在已标记为不需要代理的bean,如果是,则同样直接返回原始bean实例;接着检查bean的类是否为基础结构类或者是否应该跳过该bean,如果是,则将其标记为不需要代理并返回原始bean实例;最后,如果存在通知(拦截器),则创建代理对象并返回,否则同样将其标记为不需要代理并返回原始bean实例。 ```java /** * 如果需要,对给定的bean进行包装,即如果它符合被代理的条件。 * @param bean 原始的bean实例 * @param beanName bean的名称 * @param cacheKey 元数据访问的缓存键 * @return 包装了bean的代理,或者原始的bean实例 */ protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { // 如果beanName非空,并且该bean已经被目标源bean包含,则直接返回原始的bean实例 if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) { return bean; } // 如果缓存中已经存在该bean的标记为不需要代理,则直接返回原始的bean实例 if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) { return bean; } // 如果bean的类为基础结构类,或者应该跳过该bean的类,则直接返回原始的bean实例 if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; } // 如果存在通知,则创建代理 Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); if (specificInterceptors != DO_NOT_PROXY) { this.advisedBeans.put(cacheKey, Boolean.TRUE); Object proxy = createProxy( bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; } // 没有通知,将bean标记为不需要代理 this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; } ``` 在`org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean`方法中,首先调用`findEligibleAdvisors`方法查找适用于该bean的Advisors,然后将其转换为数组并返回。如果没有找到适用的Advisors,则返回一个特定的标记值`DO_NOT_PROXY`。 ```java /** * 获取适用于给定bean的Advisors。 * @param beanClass bean的类 * @param beanName bean的名称 * @param targetSource 目标源 * @return 包含Advisors的数组,如果没有找到适用的Advisors,则返回DO_NOT_PROXY */ @Override @Nullable protected Object[] getAdvicesAndAdvisorsForBean( Class beanClass, String beanName, @Nullable TargetSource targetSource) { // 查找适用于bean的Advisors List advisors = findEligibleAdvisors(beanClass, beanName); // 如果没有找到适用的Advisors,则返回DO_NOT_PROXY if (advisors.isEmpty()) { return DO_NOT_PROXY; } // 返回Advisors的数组 return advisors.toArray(); } ``` 在`org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#findEligibleAdvisors`方法中,主要用于查找适用于自动代理给定类的所有合适的 Advisors。它首先调用 findCandidateAdvisors 方法来查找候选的 Advisors,然后通过 findAdvisorsThatCanApply 方法筛选出可以应用于当前类的 Advisors。接着,它调用 extendAdvisors 方法来扩展 Advisors 列表,以确保适当的拦截器和通知已被应用。最后,如果有适用的 Advisors,则对 Advisors 列表进行排序并返回;如果没有适用的 Advisors,则返回一个空列表。 ```java /** * 查找适用于自动代理该类的所有合格Advisors。 * @param beanClass 要查找Advisors的类 * @param beanName 当前代理的bean的名称 * @return 空列表,非null,如果没有切点或拦截器 * @see #findCandidateAdvisors * @see #sortAdvisors * @see #extendAdvisors */ protected List findEligibleAdvisors(Class beanClass, String beanName) { // 查找候选Advisors List candidateAdvisors = findCandidateAdvisors(); // 查找可应用的Advisors List eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName); // 扩展Advisors extendAdvisors(eligibleAdvisors); // 如果有可应用的Advisors,则对Advisors进行排序 if (!eligibleAdvisors.isEmpty()) { eligibleAdvisors = sortAdvisors(eligibleAdvisors); } return eligibleAdvisors; } ``` 在`org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors`方法中,重写了父类方法 `findCandidateAdvisors()`,首先调用了父类方法以获取所有Spring Advisors,然后利用 `aspectJAdvisorsBuilder` 构建了所有AspectJ切面对应的Advisors,并将其添加到`advisors`列表中返回。 [BeanFactoryAspectJAdvisorsBuilder源码分析](../spring-aop-beanFactoryAspectJAdvisorsBuilder/README.md) ```java @Override protected List findCandidateAdvisors() { // 添加根据超类规则找到的所有Spring顾问。 List advisors = super.findCandidateAdvisors(); // 为bean工厂中的所有AspectJ切面构建顾问。 if (this.aspectJAdvisorsBuilder != null) { advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors()); } return advisors; } ``` 在`org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#findCandidateAdvisors`方法中,调用`BeanFactoryAdvisorRetrievalHelper.findAdvisorBeans()` 方法来获取候选的Advisors列表。 [BeanFactoryAdvisorRetrievalHelper源码分析](../spring-aop-beanFactoryAdvisorRetrievalHelper/README.md) ```java /** * 查找用于自动代理的所有候选Advisors。 * @return 候选Advisors的列表 */ protected List findCandidateAdvisors() { Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available"); return this.advisorRetrievalHelper.findAdvisorBeans(); } ``` 在`org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#findAdvisorsThatCanApply`方法中,搜索给定的候选 Advisors,以找到所有能够应用于指定 bean 的 Advisors。它设置当前被代理的 bean 名称,并尝试查找所有适用于指定 bean 的 Advisors。在查找完成后,它会清除当前被代理的 bean 名称。 ```java /** * 搜索给定的候选顾问,找到所有适用于指定bean的顾问。 * @param candidateAdvisors 候选顾问列表 * @param beanClass 目标bean的类 * @param beanName 目标bean的名称 * @return 适用的顾问列表 * @see ProxyCreationContext#getCurrentProxiedBeanName() */ protected List findAdvisorsThatCanApply( List candidateAdvisors, Class beanClass, String beanName) { // 设置当前代理的bean名称 ProxyCreationContext.setCurrentProxiedBeanName(beanName); try { // 查找适用于指定bean的顾问 return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass); } finally { // 清除当前代理的bean名称 ProxyCreationContext.setCurrentProxiedBeanName(null); } } ``` 在`org.springframework.aop.support.AopUtils#findAdvisorsThatCanApply`方法中,用于确定适用于给定类的候选Advisors子列表。它遍历给定的Advisors列表,首先将能够应用于指定类的引介Advisors添加到结果列表中。然后,它检查是否已经存在引介Advisors,如果存在,则跳过;如果不存在,则继续遍历Advisors列表,并将能够应用于指定类的其他Advisors添加到结果列表中。最终返回这个子列表,其中包含可以应用于给定类的所有Advisors。 ```java /** * 确定 {@code candidateAdvisors} 列表中适用于给定类的子列表。 * @param candidateAdvisors 要评估的顾问列表 * @param clazz 目标类 * @return 可应用于给定类的顾问子列表 * (可能是原始列表) */ public static List findAdvisorsThatCanApply(List candidateAdvisors, Class clazz) { // 如果候选顾问列表为空,则直接返回空列表 if (candidateAdvisors.isEmpty()) { return candidateAdvisors; } // 创建一个用于存储适用于给定类的顾问的列表 List eligibleAdvisors = new ArrayList<>(); // 遍历候选顾问列表 for (Advisor candidate : candidateAdvisors) { // 如果候选顾问是引介顾问,并且可以应用于给定类,则将其添加到结果列表中 if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) { eligibleAdvisors.add(candidate); } } // 检查是否存在引介顾问 boolean hasIntroductions = !eligibleAdvisors.isEmpty(); // 继续遍历候选顾问列表 for (Advisor candidate : candidateAdvisors) { // 如果候选顾问是引介顾问,则跳过 if (candidate instanceof IntroductionAdvisor) { // 已经处理过 continue; } // 如果候选顾问可以应用于给定类,则将其添加到结果列表中 if (canApply(candidate, clazz, hasIntroductions)) { eligibleAdvisors.add(candidate); } } return eligibleAdvisors; } ``` 在`org.springframework.aop.support.AopUtils#canApply(org.springframework.aop.Advisor, java.lang.Class, boolean)`方法中,判断给定的顾问是否能够在指定的类上应用。它首先检查顾问是否是引介顾问,如果是,则通过类过滤器来判断是否可以应用于目标类。如果顾问不是引介顾问,而是切点顾问,则通过切点来判断是否可以应用于目标类。如果顾问既不是引介顾问也不是切点顾问,则假设它适用于目标类。 ```java /** * 判断给定的顾问是否能够在指定的类上应用。 *

这是一个重要的测试,因为它可以用于优化掉一个类的顾问。 * 这个版本还考虑了引介(对于IntroductionAwareMethodMatchers)。 * @param advisor 要检查的顾问 * @param targetClass 我们正在测试的类 * @param hasIntroductions 顾问链中是否包含任何引介 * @return 切点是否能够应用于任何方法 */ public static boolean canApply(Advisor advisor, Class targetClass, boolean hasIntroductions) { // 如果顾问是引介顾问,则通过类过滤器来判断是否可以应用于目标类 if (advisor instanceof IntroductionAdvisor) { return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass); } // 如果顾问是切点顾问,则通过切点来判断是否可以应用于目标类 else if (advisor instanceof PointcutAdvisor) { PointcutAdvisor pca = (PointcutAdvisor) advisor; return canApply(pca.getPointcut(), targetClass, hasIntroductions); } // 否则,假设它适用于目标类 else { // 它没有切点,因此我们假设它适用。 return true; } } ``` 在`org.springframework.aop.support.AopUtils#canApply(org.springframework.aop.Pointcut, java.lang.Class, boolean)`方法中,确定给定的切点是否能够在指定的类上应用。首先,它检查切点的类过滤器是否与目标类匹配。如果不匹配,则返回 false。如果类过滤器匹配目标类,它会检查方法匹配器是否为 MethodMatcher.TRUE,如果是,则表示切点适用于目标类的任何方法,直接返回 true。如果方法匹配器不是 MethodMatcher.TRUE,则遍历目标类及其所有接口,并检查每个类中的方法是否与切点匹配。如果找到匹配的方法,则返回 true;如果没有找到匹配的方法,则返回 false。 ```java /** * 判断给定的切点是否能够在指定的类上应用。 *

这是一个重要的测试,因为它可以用于优化掉一个类的切点。 * @param pc 要检查的静态或动态切点 * @param targetClass 要测试的类 * @param hasIntroductions 顾问链中是否包含任何引介 * @return 切点是否能够应用于任何方法 */ public static boolean canApply(Pointcut pc, Class targetClass, boolean hasIntroductions) { Assert.notNull(pc, "Pointcut must not be null"); // 首先检查类过滤器是否匹配目标类 if (!pc.getClassFilter().matches(targetClass)) { return false; } MethodMatcher methodMatcher = pc.getMethodMatcher(); // 如果方法匹配器是 MethodMatcher.TRUE,则不需要遍历方法,直接返回true if (methodMatcher == MethodMatcher.TRUE) { // 如果我们匹配任何方法,则不需要遍历方法... return true; } IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null; if (methodMatcher instanceof IntroductionAwareMethodMatcher) { introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher; } // 获取目标类及其所有接口的集合 Set> classes = new LinkedHashSet<>(); // 如果目标类不是代理类,则将其添加到类集合中 if (!Proxy.isProxyClass(targetClass)) { classes.add(ClassUtils.getUserClass(targetClass)); } // 将目标类的所有接口添加到类集合中 classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass)); // 遍历类集合 for (Class clazz : classes) { // 获取类中声明的所有方法 Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz); // 遍历方法 for (Method method : methods) { // 如果存在引介感知的方法匹配器,并且方法匹配,则返回true; // 否则,如果方法匹配器匹配方法,则返回true if (introductionAwareMethodMatcher != null ? introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) : methodMatcher.matches(method, targetClass)) { return true; } } } return false; } ``` 在`org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator#extendAdvisors`方法中,将 `ExposeInvocationInterceptor` 添加到通知链的开头。这是必要的额外处理,特别是在使用 AspectJ 切点表达式和 AspectJ 风格的建议时。 ```java /** * 将 {@link ExposeInvocationInterceptor} 添加到通知链的开头。 *

在使用AspectJ切点表达式和AspectJ风格的建议时,需要添加此额外的建议。 * @Override * @param candidateAdvisors 候选的Advisors列表 */ @Override protected void extendAdvisors(List candidateAdvisors) { // 如果需要,使Advisor链支持AspectJ AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(candidateAdvisors); } ``` 在`org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#createProxy`方法中,首先检查`BeanFactory`是否是`ConfigurableListableBeanFactory`类型的,如果是,则调用`AutoProxyUtils.exposeTargetClass`方法来暴露目标类。然后创建一个`ProxyFactory`实例,并根据当前的代理配置进行设置。根据是否启用了代理目标类的标志,决定是否将代理目标类设置为true或false。接着构建适用于该bean的所有顾问,将它们添加到`ProxyFactory`中,并设置目标源为预先配置好的`TargetSource`。最后,根据当前的代理配置和代理类加载器,使用`ProxyFactory`获取代理实例并返回。 ```java /** * 为给定的 bean 创建一个 AOP 代理。 * @param beanClass bean 的类 * @param beanName bean 的名称 * @param specificInterceptors 适用于此 bean 的拦截器集合(可能为空,但不为 null) * @param targetSource 代理的 TargetSource,已预先配置以访问该 bean * @return bean 的 AOP 代理 * @see #buildAdvisors */ protected Object createProxy(Class beanClass, @Nullable String beanName, @Nullable Object[] specificInterceptors, TargetSource targetSource) { // 如果 beanFactory 是 ConfigurableListableBeanFactory 类型的,则暴露目标类 if (this.beanFactory instanceof ConfigurableListableBeanFactory) { AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass); } // 创建 ProxyFactory 实例 ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.copyFrom(this); // 如果需要使用代理目标类,则设置为 true if (proxyFactory.isProxyTargetClass()) { // 对 JDK 代理目标进行显式处理(用于介绍建议场景) if (Proxy.isProxyClass(beanClass)) { // 必须允许引入;不能只设置接口为代理的接口。 for (Class ifc : beanClass.getInterfaces()) { proxyFactory.addInterface(ifc); } } } else { // 未强制代理目标类标志,让我们应用默认检查... if (shouldProxyTargetClass(beanClass, beanName)) { proxyFactory.setProxyTargetClass(true); } else { evaluateProxyInterfaces(beanClass, proxyFactory); } } // 构建顾问数组 Advisor[] advisors = buildAdvisors(beanName, specificInterceptors); // 将顾问添加到 ProxyFactory proxyFactory.addAdvisors(advisors); proxyFactory.setTargetSource(targetSource); // 自定义 ProxyFactory customizeProxyFactory(proxyFactory); proxyFactory.setFrozen(this.freezeProxy); if (advisorsPreFiltered()) { proxyFactory.setPreFiltered(true); } // 如果 bean 类没有在重写类加载器中本地加载,则使用原始 ClassLoader ClassLoader classLoader = getProxyClassLoader(); if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader()) { classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader(); } // 获取代理实例 return proxyFactory.getProxy(classLoader); } ``` ================================================ FILE: spring-aop/spring-aop-annotationAwareAspectJAutoProxyCreator/pom.xml ================================================ com.xcs.spring spring-aop 0.0.1-SNAPSHOT 4.0.0 spring-aop-annotationAwareAspectJAutoProxyCreator ================================================ FILE: spring-aop/spring-aop-annotationAwareAspectJAutoProxyCreator/src/main/java/com/xcs/spring/AnnotationAwareAspectJAutoProxyCreatorDemo.java ================================================ package com.xcs.spring; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class AnnotationAwareAspectJAutoProxyCreatorDemo { public static void main(String[] args) { // 创建基于注解的应用上下文 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); // 从应用上下文中获取MyService bean MyService myService = context.getBean(MyService.class); // 调用MyService的方法 myService.foo(); } } ================================================ FILE: spring-aop/spring-aop-annotationAwareAspectJAutoProxyCreator/src/main/java/com/xcs/spring/AppConfig.java ================================================ package com.xcs.spring; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; @Configuration @EnableAspectJAutoProxy @ComponentScan public class AppConfig { } ================================================ FILE: spring-aop/spring-aop-annotationAwareAspectJAutoProxyCreator/src/main/java/com/xcs/spring/MyAspect.java ================================================ package com.xcs.spring; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Aspect @Component public class MyAspect { @Before("execution(* com.xcs.spring.MyService+.*(..))") public void before() { System.out.println("Before method execution"); } } ================================================ FILE: spring-aop/spring-aop-annotationAwareAspectJAutoProxyCreator/src/main/java/com/xcs/spring/MyService.java ================================================ package com.xcs.spring; import org.springframework.stereotype.Service; @Service public class MyService { public void foo() { System.out.println("foo..."); } } ================================================ FILE: spring-aop/spring-aop-aopContext/README.md ================================================ ## AopContext - [AopContext](#aopcontext) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、类源码](#四类源码) - [五、最佳实践](#五最佳实践) - [六、源码分析](#六源码分析) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 `AopContext`类是Spring AOP框架提供的一个工具类,用于在方法内部访问当前AOP代理对象。通过`currentProxy()`方法,可以获取当前方法被AOP代理后的代理对象,从而在方法内部执行代理对象的其他方法或获取相关信息。 ### 三、主要功能 1. **获取当前AOP代理对象** + 通过调用`currentProxy()`方法,可以在方法内部获取到当前的AOP代理对象,即被AOP增强后的对象。 2. **允许在方法内部调用代理对象的方法** + 获得代理对象后,可以在方法内部直接调用代理对象的其他方法,包括被增强的切面方法或目标对象的方法。 3. **解决代理对象的传递问题** + 在一些特定场景下,可能需要在不同的方法间传递AOP代理对象,而不是直接调用`this`。`AopContext`类提供了一种解决方案,可以在方法调用间传递AOP代理对象。 ### 四、类源码 `AopContext`类提供了用于获取当前AOP调用信息的静态方法集合。通过`currentProxy()`方法可以获取当前AOP代理对象,前提是AOP框架已配置为暴露代理对象。这对目标对象或通知进行增强调用,以及查找通知配置非常有用。然而,由于性能成本较高,Spring的AOP框架默认不会暴露代理对象。 ```java /** * 用于获取当前AOP调用信息的静态方法集合。 * *

如果AOP框架配置为暴露当前代理对象(非默认情况),则可使用 {@code currentProxy()} 方法获取正在使用的AOP代理对象。 * 目标对象或通知可以使用此方法进行增强调用,类似于EJB中的 {@code getEJBObject()}。也可用于查找通知配置。 * *

Spring的AOP框架默认不暴露代理对象,因为这样做会带来性能开销。 * *

此类中的功能可被目标对象使用,以获取调用中的资源。然而,当存在合理替代方案时,不应使用此方法,因为这会使应用程序代码依赖于AOP下的使用和Spring AOP框架。 * * @author Rod Johnson * @author Juergen Hoeller * @since 13.03.2003 */ public final class AopContext { /** * 线程本地变量,用于保存与该线程关联的AOP代理对象。 * 除非控制代理配置的“exposeProxy”属性被设置为“true”,否则将包含{@code null}。 * @see ProxyConfig#setExposeProxy */ private static final ThreadLocal currentProxy = new NamedThreadLocal<>("Current AOP proxy"); private AopContext() { } /** * 尝试返回当前AOP代理对象。此方法仅在调用方法通过AOP调用,并且AOP框架已设置为暴露代理对象时可用。 * 否则,此方法将抛出IllegalStateException异常。 * @return 当前AOP代理对象(永远不会返回{@code null}) * @throws IllegalStateException 如果无法找到代理对象,因为该方法是在AOP调用上下文之外调用的,或者因为AOP框架尚未配置为暴露代理对象 */ public static Object currentProxy() throws IllegalStateException { Object proxy = currentProxy.get(); if (proxy == null) { throw new IllegalStateException( "Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available, and " + "ensure that AopContext.currentProxy() is invoked in the same thread as the AOP invocation context."); } return proxy; } /** * 使给定的代理对象可通过{@code currentProxy()}方法访问。 *

注意,调用者应谨慎地保留旧值。 * @param proxy 要暴露的代理对象(或{@code null}以重置) * @return 旧的代理对象,如果没有绑定,则可能为{@code null} * @see #currentProxy() */ @Nullable static Object setCurrentProxy(@Nullable Object proxy) { Object old = currentProxy.get(); if (proxy != null) { currentProxy.set(proxy); } else { currentProxy.remove(); } return old; } } ``` ### 五、最佳实践 使用Spring AOP创建一个代理对象,并在代理对象的方法调用前应用自定义的前置通知。首先,通过`ProxyFactory`创建了一个代理工厂,并设置了要被代理的目标对象`MyService`。然后通过`proxyFactory.setExposeProxy(true)`来暴露代理对象,以便在方法内部可以使用`AopContext`类访问到代理对象。接着,使用`proxyFactory.addAdvisor()`方法添加了一个切面通知器,将自定义的前置通知`MyMethodBeforeAdvice`应用到被`MyAnnotation`注解标记的方法上。最后,通过`proxyFactory.getProxy()`获取代理对象,并调用其方法`foo()`。 ```java public class AopContextDemo { public static void main(String[] args) { // 创建代理工厂&创建目标对象 ProxyFactory proxyFactory = new ProxyFactory(new MyService()); // 暴露代理对象 proxyFactory.setExposeProxy(true); // 创建通知器 proxyFactory.addAdvisor(new DefaultPointcutAdvisor(new AnnotationMatchingPointcut(MyAnnotation.class), new MyMethodBeforeAdvice())); // 获取代理对象 MyService proxy = (MyService) proxyFactory.getProxy(); // 调用代理对象的方法 proxy.foo(); } } ``` `MyMethodBeforeAdvice`自定义前置通知类``。 ```java public class MyMethodBeforeAdvice implements MethodBeforeAdvice { @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("Before method " + method.getName() + " is called."); } } ``` 自定义注解`MyAnnotation`。 ```java @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface MyAnnotation { } ``` 定义了一个名为`MyService`的Java类,它被`@MyAnnotation`注解标记。该类包含两个方法,`foo()`和`bar()`。在`foo()`方法中,通过调用`AopContext.currentProxy()`来获取当前的AOP代理对象,并使用该代理对象来调用`bar()`方法,以确保AOP切面可以正确地被应用。这种方式避免了直接调用`bar()`方法而导致AOP切面失效的问题。 ```java @MyAnnotation public class MyService { public void foo() { System.out.println("foo..."); // 直接调用bar会导致切入无效 // this.bar(); // 获取代理对象并调用bar ((MyService) AopContext.currentProxy()).bar(); } public void bar() { System.out.println("bar..."); } } ``` 运行结果1,`foo()`方法被调用时,会执行前置通知打印日志,然后调用`this.bar()`方法,由于直接调用`this.bar()`方法绕过了AOP代理对象,因此不会触发AOP切面的逻辑。 ```java Before method foo is called. foo... bar... ``` 运行结果2,`foo()`方法被调用时,会执行前置通知打印日志,然后调用`((MyService) AopContext.currentProxy()).bar();`方法。由于使用了`AopContext.currentProxy()`获取了当前的AOP代理对象,并调用了该代理对象的`bar()`方法,因此会触发AOP切面的逻辑。 ```java Before method foo is called. foo... Before method bar is called. bar... ``` ### 六、源码分析 在Spring AOP框架中,无论是在JDK动态代理还是CGLIB动态代理的拦截器中,都对`AopContext.setCurrentProxy(proxy)`进行了赋值操作。这个赋值操作的目的是将当前AOP代理对象设置为当前线程的上下文中,以便在方法内部可以通过`AopContext.currentProxy()`获取代理对象。 **JDK动态代理拦截器** 在`org.springframework.aop.framework.JdkDynamicAopProxy#invoke`方法中,是JDK动态代理拦截器的一部分。主要处理了AOP代理的上下文。具体来说,在方法执行前,如果AOP代理对象已经暴露了(即`this.advised.exposeProxy`为`true`),则通过`AopContext.setCurrentProxy(proxy)`方法将当前的AOP代理对象设置为当前线程的上下文中,以便在方法内部可以通过`AopContext.currentProxy()`来获取代理对象。在方法执行完成后,将之前设置的代理对象恢复,以保证AOP代理对象的上下文不会影响其他线程。 ```java @Override @Nullable public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object oldProxy = null; boolean setProxyContext = false; // ... [代码部分省略以简化] try { // ... [代码部分省略以简化] if (this.advised.exposeProxy) { // Make invocation available if necessary. oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; } // ... [代码部分省略以简化] } finally { // ... [代码部分省略以简化] if (setProxyContext) { // Restore old proxy. AopContext.setCurrentProxy(oldProxy); } } } ``` **CGLIB动态代理拦截器** 在`org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept`方法中,是CGLIB动态代理拦截器的一部分。在方法拦截过程中,它主要处理了AOP代理的上下文。具体来说,如果AOP代理对象已经暴露了(即`this.advised.exposeProxy`为`true`),则通过`AopContext.setCurrentProxy(proxy)`方法将当前的AOP代理对象设置为当前线程的上下文中,以便在方法内部可以通过`AopContext.currentProxy()`来获取代理对象。在方法执行完成后,将之前设置的代理对象恢复,以保证AOP代理对象的上下文不会影响其他线程。 ```java @Override @Nullable public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { Object oldProxy = null; boolean setProxyContext = false; // ... [代码部分省略以简化] try { if (this.advised.exposeProxy) { // Make invocation available if necessary. oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; } // ... [代码部分省略以简化] } finally { // ... [代码部分省略以简化] if (setProxyContext) { // Restore old proxy. AopContext.setCurrentProxy(oldProxy); } } } ``` ================================================ FILE: spring-aop/spring-aop-aopContext/pom.xml ================================================ com.xcs.spring spring-aop 0.0.1-SNAPSHOT 4.0.0 spring-aop-aopContext ================================================ FILE: spring-aop/spring-aop-aopContext/src/main/java/com/xcs/spring/AopContextDemo.java ================================================ package com.xcs.spring; import org.springframework.aop.framework.ProxyFactory; import org.springframework.aop.support.DefaultPointcutAdvisor; import org.springframework.aop.support.annotation.AnnotationMatchingPointcut; public class AopContextDemo { public static void main(String[] args) { // 创建代理工厂&创建目标对象 ProxyFactory proxyFactory = new ProxyFactory(new MyService()); // 暴露代理对象 proxyFactory.setExposeProxy(true); // 创建通知器 proxyFactory.addAdvisor(new DefaultPointcutAdvisor(new AnnotationMatchingPointcut(MyAnnotation.class), new MyMethodBeforeAdvice())); // 获取代理对象 MyService proxy = (MyService) proxyFactory.getProxy(); // 调用代理对象的方法 proxy.foo(); } } ================================================ FILE: spring-aop/spring-aop-aopContext/src/main/java/com/xcs/spring/MyAnnotation.java ================================================ package com.xcs.spring; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 自定义注解 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface MyAnnotation { } ================================================ FILE: spring-aop/spring-aop-aopContext/src/main/java/com/xcs/spring/MyMethodBeforeAdvice.java ================================================ package com.xcs.spring; import org.springframework.aop.MethodBeforeAdvice; import java.lang.reflect.Method; public class MyMethodBeforeAdvice implements MethodBeforeAdvice { @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("Before method " + method.getName() + " is called."); } } ================================================ FILE: spring-aop/spring-aop-aopContext/src/main/java/com/xcs/spring/MyService.java ================================================ package com.xcs.spring; import org.springframework.aop.framework.AopContext; @MyAnnotation public class MyService { public void foo() { System.out.println("foo..."); // 直接调用bar会导致切入无效 // this.bar(); // 获取代理对象并调用bar ((MyService) AopContext.currentProxy()).bar(); } public void bar() { System.out.println("bar..."); } } ================================================ FILE: spring-aop/spring-aop-aopProxy/README.md ================================================ ## AopProxy - [AopProxy](#aopproxy) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、接口源码](#四接口源码) - [五、主要实现](#五主要实现) - [六、类关系图](#六类关系图) - [七、最佳实践](#七最佳实践) - [八、时序图](#八时序图) - [九、源码分析](#九源码分析) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 `AopProxy` 接口是Spring框架中用于支持面向切面编程(AOP)的关键组件之一,它定义了生成代理对象的标准接口,允许在运行时动态地创建代理对象,以实现对目标对象的方法调用进行拦截和增强。 ### 三、主要功能 1. **代理对象的创建与管理** + `AopProxy` 接口定义了创建和管理代理对象的标准方法,可以通过这些方法在运行时动态地生成代理对象。 3. **支持不同的代理方式** + `AopProxy` 接口支持多种代理方式,包括JDK动态代理和CGLIB代理。这样可以根据目标对象是否实现接口来选择合适的代理方式。 ### 四、接口源码 `AopProxy` 接口是一个委托接口,用于配置AOP代理,并允许创建实际的代理对象。它提供了两个方法用于创建代理对象,第一个方法使用默认的类加载器创建代理对象,通常是线程上下文类加载器;第二个方法允许指定类加载器创建代理对象。可以使用JDK动态代理或者CGLIB代理技术来生成代理对象。 ```java /** * 配置AOP代理的委托接口,允许创建实际的代理对象。 * *

默认情况下,可用于创建代理对象的实现包括JDK动态代理和CGLIB代理, * 这些代理实现由 {@link DefaultAopProxyFactory} 应用。 * * @author Rod Johnson * @author Juergen Hoeller * @see DefaultAopProxyFactory */ public interface AopProxy { /** * 创建一个新的代理对象。 *

使用AopProxy的默认类加载器(必要时用于代理创建): * 通常为线程上下文类加载器。 * @return 新的代理对象(永远不会是 {@code null}) * @see Thread#getContextClassLoader() */ Object getProxy(); /** * 创建一个新的代理对象。 *

使用给定的类加载器(必要时用于代理创建)。 * 如果给定的类加载器为 {@code null},则简单地传递并因此导致低级代理工具的默认值, * 这通常不同于AopProxy实现的 {@link #getProxy()} 方法选择的默认值。 * @param classLoader 用于创建代理的类加载器 * (或 {@code null} 表示使用低级代理工具的默认值) * @return 新的代理对象(永远不会是 {@code null}) */ Object getProxy(@Nullable ClassLoader classLoader); } ``` ### 五、主要实现 1. **JdkDynamicAopProxy** + 使用 JDK 动态代理实现的 `AopProxy` 实现类。当目标对象实现了至少一个接口时,Spring 将使用该类创建代理对象。该类通过 Java 标准库中的 `java.lang.reflect.Proxy` 类来创建代理对象。 2. **CglibAopProxy** + 使用 CGLIB(Code Generation Library)动态代理实现的 `AopProxy` 实现类。当目标对象没有实现任何接口时,Spring 将使用该类创建代理对象。该类通过生成目标类的子类来创建代理对象,实现了对目标对象方法的拦截和增强。 ### 六、类关系图 ~~~mermaid classDiagram direction BT class AopProxy { <> } class CglibAopProxy class JdkDynamicAopProxy class ObjenesisCglibAopProxy CglibAopProxy ..> AopProxy JdkDynamicAopProxy ..> AopProxy ObjenesisCglibAopProxy --> CglibAopProxy ~~~ ### 七、最佳实践 **JDK动态代理** 使用 JDK 动态代理来创建 AOP 代理对象。在 `jdkProxy` 方法中,通过配置 `AdvisedSupport` 对象,设置目标对象和接口,然后利用反射创建 `JdkDynamicAopProxy` 实例,并调用 `AopProxy` 接口的 `getProxy` 方法生成代理对象。最后,输出代理对象的信息和调用代理对象方法的结果。 ```java public class AopProxyDemo { public static void main(String[] args) throws Exception { jdkProxy(); } /** * Jdk代理 * * @throws Exception */ private static void jdkProxy() throws Exception { // 创建AdvisedSupport对象,用于配置AOP代理 AdvisedSupport advisedSupport = new AdvisedSupport(); // 设置目标对象 advisedSupport.setTarget(new MyServiceImpl()); // 设置目标对象实现的接口 advisedSupport.setInterfaces(MyService.class); // 获取JdkDynamicAopProxy的Class对象 Class jdkClass = Class.forName("org.springframework.aop.framework.JdkDynamicAopProxy"); // 获取JdkDynamicAopProxy的构造方法 Constructor constructor = jdkClass.getConstructor(AdvisedSupport.class); constructor.setAccessible(true); // 使用构造方法创建JdkDynamicAopProxy实例 AopProxy aopProxy = (AopProxy) constructor.newInstance(advisedSupport); // 调用getProxy方法创建代理对象 MyService myService = (MyService) aopProxy.getProxy(); // 输出代理对象的信息 System.out.println("JDK Class = " + myService.getClass()); // 调用代理对象的方法 myService.foo(); } } ``` 运行结果,代理对象的类为 `com.sun.proxy.$Proxy0`。 ```java JDK Class = class com.sun.proxy.$Proxy0 Before foo foo... After foo ``` **CGLIB代理** 使用 CGLIB 动态代理来创建 AOP 代理对象。在 `cglibProxy` 方法中,通过配置 `AdvisedSupport` 对象,设置目标对象,然后利用反射创建 `CglibAopProxy` 实例,并调用 `AopProxy` 接口的 `getProxy` 方法生成代理对象。最后,输出代理对象的信息和调用代理对象方法的结果。 ```java public class AopProxyDemo { public static void main(String[] args) throws Exception { cglibProxy(); } /** * cglib代理 * * @throws Exception */ private static void cglibProxy() throws Exception { // 创建AdvisedSupport对象,用于配置AOP代理 AdvisedSupport advisedSupport = new AdvisedSupport(); // 设置目标对象 advisedSupport.setTarget(new MyServiceImpl()); // 获取CglibAopProxy的Class对象 Class cglibClass = Class.forName("org.springframework.aop.framework.CglibAopProxy"); // 获取CglibAopProxy的构造方法 Constructor constructor = cglibClass.getConstructor(AdvisedSupport.class); constructor.setAccessible(true); // 使用构造方法创建CglibAopProxy实例 AopProxy aopProxy = (AopProxy) constructor.newInstance(advisedSupport); // 调用getProxy方法创建代理对象 MyService myService = (MyService) aopProxy.getProxy(); // 输出代理对象的信息 System.out.println("Cglib Class = " + myService.getClass()); // 调用代理对象的方法 myService.foo(); } } ``` 运行结果,代理对象的类为 `com.xcs.spring.MyServiceImpl$$EnhancerBySpringCGLIB$$db84547f`。 ```java Cglib Class = class com.xcs.spring.MyServiceImpl$$EnhancerBySpringCGLIB$$db84547f Before foo foo... After foo ``` ### 八、时序图 **JdkDynamicAopProxy** ~~~mermaid sequenceDiagram autonumber AopProxyDemo->>JdkDynamicAopProxy:new JdkDynamicAopProxy() JdkDynamicAopProxy->>JdkDynamicAopProxy:this.advised JdkDynamicAopProxy->>JdkDynamicAopProxy:this.proxiedInterfaces JdkDynamicAopProxy->>AopProxyDemo:返回aopProxy AopProxyDemo->>JdkDynamicAopProxy:aopProxy.getProxy() JdkDynamicAopProxy->>JdkDynamicAopProxy:getProxy(classLoader) JdkDynamicAopProxy->>Proxy:Proxy.newProxyInstance() JdkDynamicAopProxy->>AopProxyDemo:返回代理对象 AopProxyDemo->>$Proxy0:aopProxy.foo() $Proxy0->>JdkDynamicAopProxy:invoke() JdkDynamicAopProxy->>ReflectiveMethodInvocation:new ReflectiveMethodInvocation() ReflectiveMethodInvocation->>JdkDynamicAopProxy:返回invocation JdkDynamicAopProxy->>ReflectiveMethodInvocation:invocation.proceed() ~~~ **CglibAopProxy** ~~~mermaid sequenceDiagram autonumber AopProxyDemo->>CglibAopProxy:new CglibAopProxy() CglibAopProxy->>CglibAopProxy:this.advised CglibAopProxy->>CglibAopProxy:this.advisedDispatcher CglibAopProxy->>AopProxyDemo:返回aopProxy AopProxyDemo->>CglibAopProxy:aopProxy.getProxy() CglibAopProxy->>CglibAopProxy:getProxy(classLoader) CglibAopProxy->>Enhancer:new Enhancer() Enhancer->>CglibAopProxy:返回enhancer CglibAopProxy->>CglibAopProxy:getCallbacks() CglibAopProxy->>CglibAopProxy:createProxyClassAndInstance() CglibAopProxy->>Enhancer:enhancer.create() CglibAopProxy->>AopProxyDemo:返回代理对象 AopProxyDemo->>MyServiceImpl$$EnhancerBySpringCGLIB$$:aopProxy.foo() MyServiceImpl$$EnhancerBySpringCGLIB$$->>DynamicAdvisedInterceptor:intercept() DynamicAdvisedInterceptor->>CglibMethodInvocation:new CglibMethodInvocation() CglibMethodInvocation->>DynamicAdvisedInterceptor:返回invocation DynamicAdvisedInterceptor->>CglibMethodInvocation:invocation.proceed() ~~~ ### 九、源码分析 **JdkDynamicAopProxy** 在`org.springframework.aop.framework.JdkDynamicAopProxy#getProxy()`方法中,主要作用是返回一个代理对象,使用默认的类加载器来生成代理。 ```java @Override public Object getProxy() { return getProxy(ClassUtils.getDefaultClassLoader()); } ``` 在`org.springframework.aop.framework.JdkDynamicAopProxy#getProxy(java.lang.ClassLoader)`方法中,接收一个类加载器作为参数,并根据传入的类加载器和被代理的接口数组来创建一个 JDK 动态代理对象。 ```java @Override public Object getProxy(@Nullable ClassLoader classLoader) { if (logger.isTraceEnabled()) { logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource()); } return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this); } ``` 在`org.springframework.aop.framework.JdkDynamicAopProxy#invoke`方法中,`JdkDynamicAopProxy`实现了`InvocationHandler`接口,因此可以执行`invoke`方法。在方法中,首先根据方法是否为`equals`或`hashCode`方法进行特殊处理,然后获取目标对象并获取拦截器链。接着,根据拦截器链是否为空,选择直接调用目标对象方法或者通过方法拦截器链依次执行。最后,根据方法的返回值类型进行处理,如果返回值为目标对象并且返回类型与代理类型相同,则将返回值修改为代理对象。在方法执行完毕后,确保释放目标对象并恢复旧的代理对象。 [AdvisorChainFactory源码分析](../spring-aop-advisorChainFactory/README.md) [ProxyMethodInvocation源码分析](../spring-aop-proxyMethodInvocation/README.md) ```java /** * 实现了 {@code InvocationHandler.invoke} 方法。 *

调用者将看到目标对象抛出的异常,除非一个钩子方法抛出异常。 */ @Override @Nullable public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 旧的代理对象 Object oldProxy = null; // 是否设置了代理上下文标志 boolean setProxyContext = false; // 目标源 TargetSource targetSource = this.advised.targetSource; // 目标对象 Object target = null; try { if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) { // 目标对象未实现 equals(Object) 方法 return equals(args[0]); } else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) { // 目标对象未实现 hashCode() 方法 return hashCode(); } else if (method.getDeclaringClass() == DecoratingProxy.class) { // 只有 getDecoratedClass() 声明 -> 转发到代理配置 return AopProxyUtils.ultimateTargetClass(this.advised); } else if (!this.advised.opaque && method.getDeclaringClass().isInterface() && method.getDeclaringClass().isAssignableFrom(Advised.class)) { // 在代理配置上执行服务调用... return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args); } Object retVal; if (this.advised.exposeProxy) { // 必要时使调用可用 oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; } // 尽可能晚地获取目标对象,以最小化我们“拥有”目标对象的时间,以防它来自池。 target = targetSource.getTarget(); Class targetClass = (target != null ? target.getClass() : null); // 获取此方法的拦截器链。 List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); // 检查是否有任何通知。如果没有,则可以回退到直接反射调用目标,避免创建 MethodInvocation。 if (chain.isEmpty()) { // 我们可以跳过创建一个 MethodInvocation:直接调用目标 // 注意,最终的调用者必须是一个 InvokerInterceptor,这样我们就知道它只是在目标上执行反射操作,而没有热交换或花哨的代理。 Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse); } else { // 我们需要创建一个方法调用... MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); // 通过拦截器链继续进行连接点。 retVal = invocation.proceed(); } // 如果需要,修改返回值。 Class returnType = method.getReturnType(); if (retVal != null && retVal == target && returnType != Object.class && returnType.isInstance(proxy) && !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) { // 特殊情况:它返回了“this”,并且方法的返回类型与之相容。 // 请注意,如果目标在另一个返回对象中设置了对自身的引用,我们无法帮助。 retVal = proxy; } else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) { throw new AopInvocationException( "Null return value from advice does not match primitive return type for: " + method); } return retVal; } finally { if (target != null && !targetSource.isStatic()) { // 必须来自 TargetSource。 targetSource.releaseTarget(target); } if (setProxyContext) { // 恢复旧代理。 AopContext.setCurrentProxy(oldProxy); } } } ``` **CglibAopProxy** 在`org.springframework.aop.framework.CglibAopProxy#getProxy()`方法中,它返回代理对象。在没有指定目标类加载器的情况下,它调用了另一个重载方法 `getProxy(null)` 来生成代理对象并返回。 ```java @Override public Object getProxy() { return getProxy(null); } ``` 在`org.springframework.aop.framework.CglibAopProxy#getProxy(java.lang.ClassLoader)`方法中,使用 CGLIB 动态生成代理类,并创建代理对象。首先,它检查是否启用了跟踪日志,然后获取目标类的根类,并确保目标类可用于创建代理。接着,它设置代理类的父类为目标类的根类,并根据需要添加额外的接口。在配置 CGLIB Enhancer 之后,它为代理类设置回调函数,并最终生成代理类并创建代理实例。 ```java @Override public Object getProxy(@Nullable ClassLoader classLoader) { // 如果启用了跟踪日志,则记录正在创建 CGLIB 代理的信息 if (logger.isTraceEnabled()) { logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource()); } try { // 获取目标类的根类 Class rootClass = this.advised.getTargetClass(); // 断言目标类必须可用于创建 CGLIB 代理 Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy"); // 设置代理类的父类为目标类的根类 Class proxySuperClass = rootClass; // 如果目标类的名称包含了 CGLIB 分隔符,则将父类修改为目标类的父类,并将额外的接口添加到代理类中 if (rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) { proxySuperClass = rootClass.getSuperclass(); // 获取额外的接口并添加到代理类中 Class[] additionalInterfaces = rootClass.getInterfaces(); for (Class additionalInterface : additionalInterfaces) { this.advised.addInterface(additionalInterface); } } // 在需要时验证类,并写入日志消息 validateClassIfNecessary(proxySuperClass, classLoader); // 配置 CGLIB Enhancer... Enhancer enhancer = createEnhancer(); // 如果指定了类加载器,则设置 Enhancer 的类加载器,并在类加载器为可重新加载时禁用缓存 if (classLoader != null) { enhancer.setClassLoader(classLoader); if (classLoader instanceof SmartClassLoader && ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) { enhancer.setUseCache(false); } } // 设置代理类的父类 enhancer.setSuperclass(proxySuperClass); // 设置代理类实现的接口 enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised)); // 设置命名策略 enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE); // 设置策略以考虑类加载器 enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader)); // 获取回调对象和对应的类型 Callback[] callbacks = getCallbacks(rootClass); Class[] types = new Class[callbacks.length]; for (int x = 0; x < types.length; x++) { types[x] = callbacks[x].getClass(); } // fixedInterceptorMap 只在上面的 getCallbacks 调用后才填充 // 设置回调过滤器,用于过滤固定拦截器 enhancer.setCallbackFilter(new ProxyCallbackFilter( this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset)); // 设置回调类型 enhancer.setCallbackTypes(types); // 生成代理类并创建代理实例 return createProxyClassAndInstance(enhancer, callbacks); } catch (CodeGenerationException | IllegalArgumentException ex) { // 如果生成代理类出现异常,则抛出 AopConfigException throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() + ": Common causes of this problem include using a final class or a non-visible class", ex); } catch (Throwable ex) { // 如果获取目标类实例失败,则抛出 AopConfigException throw new AopConfigException("Unexpected AOP exception", ex); } } ``` 在`org.springframework.aop.framework.CglibAopProxy#getCallbacks`方法中,根据代理配置的不同情况,选择不同的拦截器和分发器,并根据目标类的静态性和建议链的冻结状态进行优化选择。如果目标是静态的并且建议链是冻结的,它会通过使用固定链将AOP调用直接发送到目标来进行一些优化。最终返回一个包含所有选定回调的数组。 ```java private Callback[] getCallbacks(Class rootClass) throws Exception { // 用于优化选择的参数... boolean exposeProxy = this.advised.isExposeProxy(); boolean isFrozen = this.advised.isFrozen(); boolean isStatic = this.advised.getTargetSource().isStatic(); // 选择一个“AOP”拦截器(用于AOP调用)。 Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised); // 选择一个“直接到目标”的拦截器(用于无通知的调用,但可以返回this)。 Callback targetInterceptor; if (exposeProxy) { targetInterceptor = (isStatic ? new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()) : new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource())); } else { targetInterceptor = (isStatic ? new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) : new DynamicUnadvisedInterceptor(this.advised.getTargetSource())); } // 选择一个“直接到目标”的分发器(用于对静态目标的未通知调用,无法返回this)。 Callback targetDispatcher = (isStatic ? new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp()); Callback[] mainCallbacks = new Callback[] { aopInterceptor, // 用于普通建议 targetInterceptor, // 在优化的情况下调用目标,不考虑建议 new SerializableNoOp(), // 对于映射到此的方法,没有覆盖 targetDispatcher, this.advisedDispatcher, new EqualsInterceptor(this.advised), new HashCodeInterceptor(this.advised) }; Callback[] callbacks; // 如果目标是静态的并且建议链被冻结, // 则我们可以通过使用固定链将AOP调用直接发送到目标来进行一些优化。 if (isStatic && isFrozen) { Method[] methods = rootClass.getMethods(); Callback[] fixedCallbacks = new Callback[methods.length]; this.fixedInterceptorMap = CollectionUtils.newHashMap(methods.length); // TODO: 这里进行了一些内存优化(可以跳过没有建议的方法的创建) for (int x = 0; x < methods.length; x++) { Method method = methods[x]; List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, rootClass); fixedCallbacks[x] = new FixedChainStaticTargetInterceptor( chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass()); this.fixedInterceptorMap.put(method, x); } // 现在将mainCallbacks和fixedCallbacks中的回调复制到callbacks数组中。 callbacks = new Callback[mainCallbacks.length + fixedCallbacks.length]; System.arraycopy(mainCallbacks, 0, callbacks, 0, mainCallbacks.length); System.arraycopy(fixedCallbacks, 0, callbacks, mainCallbacks.length, fixedCallbacks.length); this.fixedInterceptorOffset = mainCallbacks.length; } else { callbacks = mainCallbacks; } return callbacks; } ``` 在`org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept`方法中,首先,它获取目标对象和目标类,并获取与指定方法相关的拦截器链。然后,根据拦截器链和方法的特性进行适当的处理。如果拦截器链为空且方法是公共的,则直接调用目标方法,否则创建一个方法调用。最后,处理方法调用的返回值并返回结果。在方法执行过程中,还会根据配置决定是否暴露代理对象,并在必要时设置AOP上下文。最后,在finally块中释放目标对象,并在必要时恢复旧的代理对象。 [AdvisorChainFactory源码分析](../spring-aop-advisorChainFactory/README.md) [ProxyMethodInvocation源码分析](../spring-aop-proxyMethodInvocation/README.md) ```java @Override @Nullable public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { // 保存旧代理对象 Object oldProxy = null; // 是否设置了代理上下文 boolean setProxyContext = false; // 目标对象 Object target = null; // 获取目标源 TargetSource targetSource = this.advised.getTargetSource(); try { if (this.advised.exposeProxy) { // 如果配置中允许暴露代理对象,则将当前代理对象设置为Aop上下文的当前代理对象 oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; } // 获取目标对象,尽可能晚地获取以最小化拥有目标的时间,以防它来自池... target = targetSource.getTarget(); // 目标对象的类 Class targetClass = (target != null ? target.getClass() : null); // 获取拦截器链 List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); // 方法调用返回值 Object retVal; // 检查是否只有一个 InvokerInterceptor:即,没有真正的建议,而只是目标的反射调用。 if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) { // 我们可以跳过创建一个 MethodInvocation:直接调用目标。 // 请注意,最终调用者必须是一个 InvokerInterceptor,因此我们知道它只是对目标进行了反射操作,并且没有热交换或花哨的代理。 Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); // 直接调用目标方法 retVal = methodProxy.invoke(target, argsToUse); } else { // 我们需要创建一个方法调用... // 创建方法调用并执行 retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed(); } // 处理返回类型 retVal = processReturnType(proxy, target, method, retVal); // 返回方法调用结果 return retVal; } finally { if (target != null && !targetSource.isStatic()) { // 如果目标对象不是静态的,则释放目标对象 targetSource.releaseTarget(target); } if (setProxyContext) { // 恢复旧代理对象。 AopContext.setCurrentProxy(oldProxy); // 恢复Aop上下文的当前代理对象为旧代理对象 } } } ``` ================================================ FILE: spring-aop/spring-aop-aopProxy/pom.xml ================================================ com.xcs.spring spring-aop 0.0.1-SNAPSHOT 4.0.0 spring-aop-aopProxy ================================================ FILE: spring-aop/spring-aop-aopProxy/src/main/java/com/xcs/spring/AopProxyDemo.java ================================================ package com.xcs.spring; import org.springframework.aop.framework.AdvisedSupport; import org.springframework.aop.framework.AopProxy; import java.lang.reflect.Constructor; public class AopProxyDemo { public static void main(String[] args) throws Exception { // cglibProxy(); jdkProxy(); } /** * cglib代理 * * @throws Exception */ private static void cglibProxy() throws Exception { // 创建AdvisedSupport对象,用于配置AOP代理 AdvisedSupport advisedSupport = new AdvisedSupport(); // 设置目标对象 advisedSupport.setTarget(new MyServiceImpl()); // 添加拦截器 advisedSupport.addAdvice(new MyMethodInterceptor()); // 获取CglibAopProxy的Class对象 Class cglibClass = Class.forName("org.springframework.aop.framework.CglibAopProxy"); // 获取CglibAopProxy的构造方法 Constructor constructor = cglibClass.getConstructor(AdvisedSupport.class); constructor.setAccessible(true); // 使用构造方法创建CglibAopProxy实例 AopProxy aopProxy = (AopProxy) constructor.newInstance(advisedSupport); // 调用getProxy方法创建代理对象 MyService myService = (MyService) aopProxy.getProxy(); // 输出代理对象的信息 System.out.println("Cglib Class = " + myService.getClass()); // 调用代理对象的方法 myService.foo(); } /** * Jdk代理 * * @throws Exception */ private static void jdkProxy() throws Exception { // 创建AdvisedSupport对象,用于配置AOP代理 AdvisedSupport advisedSupport = new AdvisedSupport(); // 设置目标对象 advisedSupport.setTarget(new MyServiceImpl()); // 设置目标对象实现的接口 advisedSupport.setInterfaces(MyService.class); // 添加拦截器 advisedSupport.addAdvice(new MyMethodInterceptor()); // 获取JdkDynamicAopProxy的Class对象 Class jdkClass = Class.forName("org.springframework.aop.framework.JdkDynamicAopProxy"); // 获取JdkDynamicAopProxy的构造方法 Constructor constructor = jdkClass.getConstructor(AdvisedSupport.class); constructor.setAccessible(true); // 使用构造方法创建JdkDynamicAopProxy实例 AopProxy aopProxy = (AopProxy) constructor.newInstance(advisedSupport); // 调用getProxy方法创建代理对象 MyService myService = (MyService) aopProxy.getProxy(); // 输出代理对象的信息 System.out.println("JDK Class = " + myService.getClass()); // 调用代理对象的方法 myService.foo(); } } ================================================ FILE: spring-aop/spring-aop-aopProxy/src/main/java/com/xcs/spring/MyMethodInterceptor.java ================================================ package com.xcs.spring; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class MyMethodInterceptor implements MethodInterceptor { @Override public Object invoke(MethodInvocation invocation) throws Throwable { // 在方法调用之前执行的逻辑 System.out.println("Before " + invocation.getMethod().getName()); // 调用原始方法 Object result = invocation.proceed(); // 在方法调用之后执行的逻辑 System.out.println("After " + invocation.getMethod().getName()); return result; } } ================================================ FILE: spring-aop/spring-aop-aopProxy/src/main/java/com/xcs/spring/MyService.java ================================================ package com.xcs.spring; public interface MyService { void foo(); } ================================================ FILE: spring-aop/spring-aop-aopProxy/src/main/java/com/xcs/spring/MyServiceImpl.java ================================================ package com.xcs.spring; public class MyServiceImpl implements MyService { @Override public void foo() { System.out.println("foo..."); } } ================================================ FILE: spring-aop/spring-aop-aopProxyFactory/README.md ================================================ ## AopProxyFactory - [AopProxyFactory](#aopproxyfactory) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、接口源码](#四接口源码) - [五、主要实现](#五主要实现) - [六、类关系图](#六类关系图) - [七、最佳实践](#七最佳实践) - [八、源码分析](#八源码分析) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 `AopProxyFactory` 接口是 Spring AOP 框架的关键组件之一,负责根据配置信息创建 AOP 代理对象,支持根据目标对象的类型和配置选择合适的代理方式(JDK 动态代理或 CGLIB 代理)。 ### 三、主要功能 1. **创建 AOP 代理对象** + 根据传入的 `AdvisedSupport` 对象,利用指定的代理方式(JDK 动态代理或 CGLIB 代理)生成 AOP 代理对象。 2. **决定代理方式** + 根据目标类的类型和配置信息,确定是否使用 JDK 动态代理或 CGLIB 代理。这个决定通常是基于配置中的一些条件,例如是否需要代理接口或者是否允许使用 CGLIB 代理。 3. **支持灵活配置** + 通过实现该接口,可以灵活地定制 AOP 代理的生成方式,以满足不同场景下的需求。 ### 四、接口源码 `AopProxyFactory`接口 ,其功能是创建基于 `AdvisedSupport` 配置对象的 AOP 代理。代理对象需要满足一系列约定,包括实现被配置指示的所有接口、实现 `Advised` 接口、实现 `equals` 方法用于比较被代理的接口、通知和目标、并且在通知者和目标都是可序列化的情况下应该是可序列化的,以及在通知者和目标都是线程安全的情况下应该是线程安全的。该接口的实现应该能够根据给定的 AOP 配置创建相应的 AOP 代理对象,并在配置无效时抛出 `AopConfigException` 异常。 ```java /** * 接口,由能够基于 {@link AdvisedSupport} 配置对象创建 AOP 代理的工厂实现。 * *

代理对象应遵守以下约定: *

    *
  • 它们应该实现配置中指示应该被代理的所有接口。 *
  • 它们应该实现 {@link Advised} 接口。 *
  • 它们应该实现 equals 方法以比较被代理的接口、通知和目标。 *
  • 如果所有通知者和目标都是可序列化的,它们应该是可序列化的。 *
  • 如果通知者和目标都是线程安全的,它们应该是线程安全的。 *
* *

代理可能允许或不允许更改通知。如果它们不允许更改通知(例如,因为配置已被冻结),则代理应在尝试更改通知时抛出 {@link AopConfigException}。 * * @author Rod Johnson * @author Juergen Hoeller */ public interface AopProxyFactory { /** * 根据给定的 AOP 配置创建一个 {@link AopProxy}。 * @param config 以 AdvisedSupport 对象形式表示的 AOP 配置 * @return 相应的 AOP 代理 * @throws AopConfigException 如果配置无效 */ AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException; } ``` ### 五、主要实现 1. **DefaultAopProxyFactory** + `DefaultAopProxyFactory` 是 `AopProxyFactory` 接口的默认实现类,它负责根据给定的 `AdvisedSupport` 配置对象创建 AOP 代理,根据配置信息选择合适的代理方式(JDK 动态代理或 CGLIB 代理),并处理可能出现的异常情况,使得 Spring AOP 能够灵活地生成并使用 AOP 代理对象,实现切面编程的功能。 ### 六、类关系图 ~~~mermaid classDiagram direction BT class AopProxyFactory { <> } class DefaultAopProxyFactory DefaultAopProxyFactory ..> AopProxyFactory ~~~ ### 七、最佳实践 何使用 Spring AOP 创建 JDK 动态代理和 CGLIB 代理。在 `jdkProxy()` 方法中,首先创建了一个 `AdvisedSupport` 对象用于配置 AOP 代理,然后通过 `DefaultAopProxyFactory` 实例创建 JDK 动态代理对象,并打印生成的代理类。而在 `cglibProxy()` 方法中,同样创建了一个 `AdvisedSupport` 对象用于配置 AOP 代理,然后通过 `DefaultAopProxyFactory` 实例创建 CGLIB 代理对象,并打印生成的代理类。 ```java public class AopProxyFactoryDemo { public static void main(String[] args) { // 分别演示 JDK 动态代理和 CGLIB 代理 jdkProxy(); cglibProxy(); } /** * JDK 动态代理示例 */ private static void jdkProxy() { // 创建 AdvisedSupport 对象,用于配置 AOP 代理 AdvisedSupport advisedSupport = new AdvisedSupport(); // 设置目标对象 advisedSupport.setTarget(new MyServiceImpl()); // 设置目标对象的类 advisedSupport.setTargetClass(MyService.class); // 创建 DefaultAopProxyFactory 实例 AopProxyFactory aopProxyFactory = new DefaultAopProxyFactory(); // 创建 JDK 动态代理对象 MyService myService = (MyService) aopProxyFactory.createAopProxy(advisedSupport).getProxy(); // 打印生成的代理类 System.out.println("jdkProxy = " + myService.getClass()); } /** * CGLIB 代理示例 */ private static void cglibProxy() { // 创建 AdvisedSupport 对象,用于配置 AOP 代理 AdvisedSupport advisedSupport = new AdvisedSupport(); // 设置目标对象 advisedSupport.setTarget(new MyServiceImpl()); // 创建 DefaultAopProxyFactory 实例 AopProxyFactory aopProxyFactory = new DefaultAopProxyFactory(); // 创建 CGLIB 代理对象 MyService myService = (MyService) aopProxyFactory.createAopProxy(advisedSupport).getProxy(); // 打印生成的代理类 System.out.println("cglibProxy = " + myService.getClass()); } } ``` 运行结果,显示了两种不同的代理类。`jdkProxy` 是使用 JDK 动态代理生成的代理类,它由 `com.sun.proxy.$Proxy0` 表示。而 `cglibProxy` 是使用 CGLIB 生成的代理类,它由 `com.xcs.spring.MyServiceImpl$$EnhancerBySpringCGLIB$$3c109cf5` 表示。这两种代理类分别对应了不同的代理方式,分别由 Spring AOP 根据配置信息生成。 ```java jdkProxy = class com.sun.proxy.$Proxy0 cglibProxy = class com.xcs.spring.MyServiceImpl$$EnhancerBySpringCGLIB$$3c109cf5 ``` ### 八、源码分析 `DefaultAopProxyFactory` 是 `AopProxyFactory` 接口的默认实现,根据给定的 `AdvisedSupport` 配置对象,可以创建 CGLIB 代理或 JDK 动态代理。如果对于给定的 `AdvisedSupport` 实例满足以下条件之一,则会创建 CGLIB 代理:优化标志被设置、代理目标类标志被设置,或者未指定代理接口。通常情况下,可以通过指定 `proxyTargetClass` 来强制使用 CGLIB 代理,或者通过指定一个或多个接口来使用 JDK 动态代理。 [AopProxy源码分析](../spring-aop-aopProxy/README.md) ```java /** * 默认的 {@link AopProxyFactory} 实现,根据条件创建 CGLIB 代理或 JDK 动态代理。 * *

如果对于给定的 {@link AdvisedSupport} 实例满足以下条件之一,则创建 CGLIB 代理: *

    *
  • 设置了 {@code optimize} 标志 *
  • 设置了 {@code proxyTargetClass} 标志 *
  • 未指定代理接口 *
* *

通常情况下,可以通过指定 {@code proxyTargetClass} 来强制使用 CGLIB 代理,或者通过指定一个或多个接口来使用 JDK 动态代理。 * * @author Rod Johnson * @author Juergen Hoeller * @author Sebastien Deleuze * @since 2004-03-12 * @see AdvisedSupport#setOptimize * @see AdvisedSupport#setProxyTargetClass * @see AdvisedSupport#setInterfaces */ @SuppressWarnings("serial") public class DefaultAopProxyFactory implements AopProxyFactory, Serializable { @Override public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { // 检查是否支持CGLIB代理,如果是,则创建CGLIB代理 if (!NativeDetector.inNativeImage() && (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) { // 获取目标类 Class targetClass = config.getTargetClass(); if (targetClass == null) { // 如果目标类为空,则抛出AopConfigException异常 throw new AopConfigException("TargetSource cannot determine target class: " + "Either an interface or a target is required for proxy creation."); } // 如果目标类是接口或者是代理类,则创建JDK动态代理 if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) { return new JdkDynamicAopProxy(config); } // 否则,创建CGLIB代理 return new ObjenesisCglibAopProxy(config); } else { // 否则,创建JDK动态代理 return new JdkDynamicAopProxy(config); } } /** * 确定提供的 {@link AdvisedSupport} 是否仅指定了 {@link org.springframework.aop.SpringProxy} 接口 * (或者根本没有指定代理接口)。 */ private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) { Class[] ifcs = config.getProxiedInterfaces(); return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0]))); } } ``` ================================================ FILE: spring-aop/spring-aop-aopProxyFactory/pom.xml ================================================ com.xcs.spring spring-aop 0.0.1-SNAPSHOT 4.0.0 spring-aop-aopProxyFactory ================================================ FILE: spring-aop/spring-aop-aopProxyFactory/src/main/java/com/xcs/spring/AopProxyFactoryDemo.java ================================================ package com.xcs.spring; import org.springframework.aop.framework.AdvisedSupport; import org.springframework.aop.framework.AopProxyFactory; import org.springframework.aop.framework.DefaultAopProxyFactory; public class AopProxyFactoryDemo { public static void main(String[] args) { // 分别演示 JDK 动态代理和 CGLIB 代理 jdkProxy(); cglibProxy(); } /** * JDK 动态代理示例 */ private static void jdkProxy() { // 创建 AdvisedSupport 对象,用于配置 AOP 代理 AdvisedSupport advisedSupport = new AdvisedSupport(); // 设置目标对象 advisedSupport.setTarget(new MyServiceImpl()); // 设置目标对象的类 advisedSupport.setTargetClass(MyService.class); // 创建 DefaultAopProxyFactory 实例 AopProxyFactory aopProxyFactory = new DefaultAopProxyFactory(); // 创建 JDK 动态代理对象 MyService myService = (MyService) aopProxyFactory.createAopProxy(advisedSupport).getProxy(); // 打印生成的代理类 System.out.println("jdkProxy = " + myService.getClass()); } /** * CGLIB 代理示例 */ private static void cglibProxy() { // 创建 AdvisedSupport 对象,用于配置 AOP 代理 AdvisedSupport advisedSupport = new AdvisedSupport(); // 设置目标对象 advisedSupport.setTarget(new MyServiceImpl()); // 创建 DefaultAopProxyFactory 实例 AopProxyFactory aopProxyFactory = new DefaultAopProxyFactory(); // 创建 CGLIB 代理对象 MyService myService = (MyService) aopProxyFactory.createAopProxy(advisedSupport).getProxy(); // 打印生成的代理类 System.out.println("cglibProxy = " + myService.getClass()); } } ================================================ FILE: spring-aop/spring-aop-aopProxyFactory/src/main/java/com/xcs/spring/MyService.java ================================================ package com.xcs.spring; public interface MyService { String doSomething(); } ================================================ FILE: spring-aop/spring-aop-aopProxyFactory/src/main/java/com/xcs/spring/MyServiceImpl.java ================================================ package com.xcs.spring; public class MyServiceImpl implements MyService { @Override public String doSomething() { return "hello world"; } } ================================================ FILE: spring-aop/spring-aop-aspectInstanceFactory/README.md ================================================ ## AspectInstanceFactory - [AspectInstanceFactory](#aspectinstancefactory) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、接口源码](#四接口源码) - [五、主要实现](#五主要实现) - [六、类关系图](#六类关系图) - [七、最佳实践](#七最佳实践) - [八、源码分析](#八源码分析) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 `AspectInstanceFactory` 接口是 Spring AOP 中的关键接口,负责在运行时动态创建切面实例,以适应不同的场景和需求,其实现类通过 `getAspectInstance()` 方法提供切面实例,并可指定切面的创建模式。 ### 三、主要功能 1. **动态创建切面实例** + 允许在运行时动态地创建切面实例,以便在应用程序中应用切面功能。 2. **提供切面实例** + 通过 `getAspectInstance()` 方法获取切面实例,以供 Spring AOP 使用。 3. **管理切面生命周期** + 可控制切面实例的生命周期,例如可以指定为单例模式(Singleton)或原型模式(Prototype)。 4. **灵活适应不同需求** + 允许根据应用程序的需求定制切面实例的创建和管理方式,提供了灵活性和可扩展性。 ### 四、接口源码 `AspectInstanceFactory`接口,用于提供 AspectJ 切面的实例。它与 Spring 的 bean 工厂解耦,通过 `getAspectInstance()` 方法创建切面实例,并通过 `getAspectClassLoader()` 方法公开切面类加载器。此接口还继承了 `Ordered` 接口,以表达切面在链中的顺序值。 ```java /** * 用于提供一个 AspectJ 切面实例的接口,与 Spring 的 bean 工厂解耦。 * *

扩展了 {@link org.springframework.core.Ordered} 接口,用于表达链中底层切面的顺序值。 * * @author Rod Johnson * @author Juergen Hoeller * @since 2.0 * @see org.springframework.beans.factory.BeanFactory#getBean */ public interface AspectInstanceFactory extends Ordered { /** * 创建此工厂的切面实例。 * @return 切面实例(永远不会为 {@code null}) */ Object getAspectInstance(); /** * 公开此工厂使用的切面类加载器。 * @return 切面类加载器(对于引导加载器,为 {@code null}) * @see org.springframework.util.ClassUtils#getDefaultClassLoader() */ @Nullable ClassLoader getAspectClassLoader(); } ``` ### 五、主要实现 1. **SimpleAspectInstanceFactory** + 一个简单的切面实例工厂,用于创建基于注解的切面实例。 2. **SingletonAspectInstanceFactory** + 一个单例的切面实例工厂,用于创建单例的切面实例。 3. **SimpleBeanFactoryAwareAspectInstanceFactory** + 一个简单的 Bean 工厂感知切面实例工厂,用于在创建切面实例时考虑 Bean 工厂的上下文信息。 ### 六、类关系图 ~~~mermaid classDiagram direction BT class AspectInstanceFactory { <> } class SimpleAspectInstanceFactory class SimpleBeanFactoryAwareAspectInstanceFactory class SingletonAspectInstanceFactory SimpleAspectInstanceFactory ..> AspectInstanceFactory SimpleBeanFactoryAwareAspectInstanceFactory ..> AspectInstanceFactory SingletonAspectInstanceFactory ..> AspectInstanceFactory ~~~ ### 七、最佳实践 使用不同类型的 `AspectInstanceFactory` 实现类来创建和管理切面实例。首先,通过 `SimpleAspectInstanceFactory` 和 `SingletonAspectInstanceFactory` 分别创建简单实例和单例实例的切面。然后,通过注册一个名为 "myAspect" 的单例 bean,并将其用于配置 `SimpleBeanFactoryAwareAspectInstanceFactory`,从而创建一个依赖于 Bean 工厂的切面实例。最后,展示了获取 `SimpleBeanFactoryAwareAspectInstanceFactory` 实例的切面对象,并输出其结果。 ```java public class AspectInstanceFactoryDemo { public static void main(String[] args) { // 使用 SimpleAspectInstanceFactory 创建切面实例 SimpleAspectInstanceFactory sAif = new SimpleAspectInstanceFactory(MyAspect.class); System.out.println("SimpleAspectInstanceFactory (1): " + sAif.getAspectInstance()); System.out.println("SimpleAspectInstanceFactory (2): " + sAif.getAspectInstance()); // 使用 SingletonAspectInstanceFactory 创建单例切面实例 SingletonAspectInstanceFactory singletonAif = new SingletonAspectInstanceFactory(new MyAspect()); System.out.println("SingletonAspectInstanceFactory (1): " + singletonAif.getAspectInstance()); System.out.println("SingletonAspectInstanceFactory (2): " + singletonAif.getAspectInstance()); // 创建一个 DefaultListableBeanFactory 实例,用于注册和管理 bean DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); // 注册一个名为 "myAspect" 的单例 bean,类型为 MyAspect beanFactory.registerSingleton("myAspect", new MyAspect()); // 创建一个切面工厂的 BeanDefinition RootBeanDefinition aspectFactoryDef = new RootBeanDefinition(SimpleBeanFactoryAwareAspectInstanceFactory.class); // 设置切面工厂的属性 aspectBeanName 为 "myAspect" aspectFactoryDef.getPropertyValues().add("aspectBeanName", "myAspect"); // 设置切面工厂为合成的,即不对外暴露 aspectFactoryDef.setSynthetic(true); // 注册名为 "simpleBeanFactoryAwareAspectInstanceFactory" 的 bean,并使用切面工厂的 BeanDefinition beanFactory.registerBeanDefinition("simpleBeanFactoryAwareAspectInstanceFactory", aspectFactoryDef); // 从 BeanFactory 中获取 SimpleBeanFactoryAwareAspectInstanceFactory 的实例 SimpleBeanFactoryAwareAspectInstanceFactory simpleBeanFactoryAwareAif = beanFactory.getBean(SimpleBeanFactoryAwareAspectInstanceFactory.class); System.out.println("SimpleBeanFactoryAwareAspectInstanceFactory (1): " + simpleBeanFactoryAwareAif.getAspectInstance()); System.out.println("SimpleBeanFactoryAwareAspectInstanceFactory (2): " + simpleBeanFactoryAwareAif.getAspectInstance()); } } ``` 运行结果,通过不同的切面实例工厂创建切面对象的情况:`SimpleAspectInstanceFactory` 每次调用 `getAspectInstance()` 都会创建一个新的切面对象,因此得到的实例不同;而 `SingletonAspectInstanceFactory` 返回的是单例对象,所以多次调用 `getAspectInstance()` 得到的是同一个实例;`SimpleBeanFactoryAwareAspectInstanceFactory` 从 `BeanFactory` 中获取指定名称的 bean,该 bean 默认是单例的,因此也得到相同的实例。 ```java SimpleAspectInstanceFactory (1): com.xcs.spring.MyAspect@6d8a00e3 SimpleAspectInstanceFactory (2): com.xcs.spring.MyAspect@548b7f67 SingletonAspectInstanceFactory (1): com.xcs.spring.MyAspect@5f375618 SingletonAspectInstanceFactory (2): com.xcs.spring.MyAspect@5f375618 SimpleBeanFactoryAwareAspectInstanceFactory (1): com.xcs.spring.MyAspect@41ee392b SimpleBeanFactoryAwareAspectInstanceFactory (2): com.xcs.spring.MyAspect@41ee392b ``` ### 八、源码分析 **SimpleAspectInstanceFactory** `SimpleAspectInstanceFactory`类是 `AspectInstanceFactory` 接口的实现。每次调用 `getAspectInstance()` 方法创建指定切面类的新实例。它通过反射机制在运行时实例化切面类,并提供了方法来获取切面类、获取切面类的类加载器以及确定切面实例的顺序。 ```java /** * {@link AspectInstanceFactory} 接口的实现类,用于在每次调用 {@link #getAspectInstance()} 方法时为指定的切面类创建一个新实例。 * 创建新实例的切面工厂。 * * @author Juergen Hoeller * @since 2.0.4 */ public class SimpleAspectInstanceFactory implements AspectInstanceFactory { // 切面类 private final Class aspectClass; /** * 为给定的切面类创建一个新的 SimpleAspectInstanceFactory。 * @param aspectClass 切面类 */ public SimpleAspectInstanceFactory(Class aspectClass) { Assert.notNull(aspectClass, "Aspect class must not be null"); this.aspectClass = aspectClass; } /** * 返回指定的切面类(永远不为 {@code null})。 */ public final Class getAspectClass() { return this.aspectClass; } @Override public final Object getAspectInstance() { try { // 使用反射获取切面类的可访问构造函数,并创建新实例 return ReflectionUtils.accessibleConstructor(this.aspectClass).newInstance(); } catch (NoSuchMethodException ex) { throw new AopConfigException("No default constructor on aspect class: " + this.aspectClass.getName(), ex); } catch (InstantiationException ex) { throw new AopConfigException("Unable to instantiate aspect class: " + this.aspectClass.getName(), ex); } catch (IllegalAccessException ex) { throw new AopConfigException("Could not access aspect constructor: " + this.aspectClass.getName(), ex); } catch (InvocationTargetException ex) { throw new AopConfigException("Failed to invoke aspect constructor: " + this.aspectClass.getName(), ex.getTargetException()); } } @Override @Nullable public ClassLoader getAspectClassLoader() { // 返回切面类的类加载器 return this.aspectClass.getClassLoader(); } /** * 确定此工厂的切面实例的顺序, * 可通过实现 {@link org.springframework.core.Ordered} 接口表达实例特定的顺序, * 或者使用一个默认顺序。 * @see org.springframework.core.Ordered * @see #getOrderForAspectClass */ @Override public int getOrder() { return getOrderForAspectClass(this.aspectClass); } /** * 确定在切面实例没有通过实现 {@link org.springframework.core.Ordered} 接口表达实例特定顺序时的后备顺序。 *

默认实现简单地返回 {@code Ordered.LOWEST_PRECEDENCE}。 * @param aspectClass 切面类 */ protected int getOrderForAspectClass(Class aspectClass) { return Ordered.LOWEST_PRECEDENCE; } } ``` **SingletonAspectInstanceFactory** `SingletonAspectInstanceFactory` 类是 `AspectInstanceFactory` 接口的实现。该类通过指定的单例对象作为后端支持,每次调用 `getAspectInstance()` 方法时都返回相同的实例。此外,它还提供了方法来获取切面实例的类加载器以及确定切面实例的顺序,支持实现了 `Ordered` 接口的切面实例。 ```java /** * {@link AspectInstanceFactory} 接口的实现类,由指定的单例对象支持, * 每次调用 {@link #getAspectInstance()} 方法时返回相同的实例。 * 单例切面实例工厂。 * * 由指定的单例对象支持,每次调用 getAspectInstance() 方法时返回相同的实例。 * * @author Rod Johnson * @author Juergen Hoeller * @since 2.0 * @see SimpleAspectInstanceFactory */ @SuppressWarnings("serial") public class SingletonAspectInstanceFactory implements AspectInstanceFactory, Serializable { // 单例切面实例 private final Object aspectInstance; /** * 为给定的切面实例创建一个新的 SingletonAspectInstanceFactory。 * @param aspectInstance 单例切面实例 */ public SingletonAspectInstanceFactory(Object aspectInstance) { Assert.notNull(aspectInstance, "Aspect instance must not be null"); this.aspectInstance = aspectInstance; } @Override public final Object getAspectInstance() { // 返回单例切面实例 return this.aspectInstance; } @Override @Nullable public ClassLoader getAspectClassLoader() { // 返回切面实例的类加载器 return this.aspectInstance.getClass().getClassLoader(); } /** * 确定此工厂的切面实例的顺序, * 可通过实现 {@link org.springframework.core.Ordered} 接口表达实例特定的顺序, * 或者使用一个默认顺序。 * @see org.springframework.core.Ordered * @see #getOrderForAspectClass */ @Override public int getOrder() { if (this.aspectInstance instanceof Ordered) { // 如果切面实例实现了 Ordered 接口,则返回其顺序 return ((Ordered) this.aspectInstance).getOrder(); } // 否则返回切面实例类的默认顺序 return getOrderForAspectClass(this.aspectInstance.getClass()); } /** * 确定在切面实例没有通过实现 {@link org.springframework.core.Ordered} 接口表达实例特定顺序时的后备顺序。 *

默认实现简单地返回 {@code Ordered.LOWEST_PRECEDENCE}。 * @param aspectClass 切面类 */ protected int getOrderForAspectClass(Class aspectClass) { return Ordered.LOWEST_PRECEDENCE; } } ``` **SimpleBeanFactoryAwareAspectInstanceFactory** `SimpleBeanFactoryAwareAspectInstanceFactory` 类是 `AspectInstanceFactory` 接口的实现。该类通过配置的 bean 名称从 `BeanFactory` 中定位切面实例。每次调用 `getAspectInstance()` 方法时,都会查找并返回指定名称的 bean。此外,它还提供了方法来获取切面实例的类加载器以及确定切面实例的顺序,支持实现了 `Ordered` 接口的切面实例。 ```java /** * {@link AspectInstanceFactory} 接口的实现类,通过配置的 bean 名称从 {@link org.springframework.beans.factory.BeanFactory} 中定位切面。 * SimpleBeanFactoryAwareAspectInstanceFactory 类。 * * 通过配置的 bean 名称从 BeanFactory 中定位切面。 * * @author Rob Harrop * @author Juergen Hoeller * @since 2.0 */ public class SimpleBeanFactoryAwareAspectInstanceFactory implements AspectInstanceFactory, BeanFactoryAware { // 切面 bean 名称 @Nullable private String aspectBeanName; // BeanFactory @Nullable private BeanFactory beanFactory; /** * 设置切面 bean 的名称。调用 {@link #getAspectInstance()} 时返回该 bean。 * @param aspectBeanName 切面 bean 名称 */ public void setAspectBeanName(String aspectBeanName) { this.aspectBeanName = aspectBeanName; } @Override public void setBeanFactory(BeanFactory beanFactory) { this.beanFactory = beanFactory; Assert.notNull(this.aspectBeanName, "'aspectBeanName' is required"); } /** * 从 BeanFactory 中查找切面 bean 并返回。 * @see #setAspectBeanName */ @Override public Object getAspectInstance() { Assert.state(this.beanFactory != null, "No BeanFactory set"); Assert.state(this.aspectBeanName != null, "No 'aspectBeanName' set"); return this.beanFactory.getBean(this.aspectBeanName); } @Override @Nullable public ClassLoader getAspectClassLoader() { if (this.beanFactory instanceof ConfigurableBeanFactory) { return ((ConfigurableBeanFactory) this.beanFactory).getBeanClassLoader(); } else { return ClassUtils.getDefaultClassLoader(); } } @Override public int getOrder() { if (this.beanFactory != null && this.aspectBeanName != null && this.beanFactory.isSingleton(this.aspectBeanName) && this.beanFactory.isTypeMatch(this.aspectBeanName, Ordered.class)) { return ((Ordered) this.beanFactory.getBean(this.aspectBeanName)).getOrder(); } return Ordered.LOWEST_PRECEDENCE; } } ``` ================================================ FILE: spring-aop/spring-aop-aspectInstanceFactory/pom.xml ================================================ com.xcs.spring spring-aop 0.0.1-SNAPSHOT 4.0.0 spring-aop-aspectInstanceFactory ================================================ FILE: spring-aop/spring-aop-aspectInstanceFactory/src/main/java/com/xcs/spring/AspectInstanceFactoryDemo.java ================================================ package com.xcs.spring; import org.springframework.aop.aspectj.SimpleAspectInstanceFactory; import org.springframework.aop.aspectj.SingletonAspectInstanceFactory; import org.springframework.aop.config.SimpleBeanFactoryAwareAspectInstanceFactory; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.RootBeanDefinition; public class AspectInstanceFactoryDemo { public static void main(String[] args) { // 使用 SimpleAspectInstanceFactory 创建切面实例 SimpleAspectInstanceFactory sAif = new SimpleAspectInstanceFactory(MyAspect.class); System.out.println("SimpleAspectInstanceFactory (1): " + sAif.getAspectInstance()); System.out.println("SimpleAspectInstanceFactory (2): " + sAif.getAspectInstance()); // 使用 SingletonAspectInstanceFactory 创建单例切面实例 SingletonAspectInstanceFactory singletonAif = new SingletonAspectInstanceFactory(new MyAspect()); System.out.println("SingletonAspectInstanceFactory (1): " + singletonAif.getAspectInstance()); System.out.println("SingletonAspectInstanceFactory (2): " + singletonAif.getAspectInstance()); // 创建一个 DefaultListableBeanFactory 实例,用于注册和管理 bean DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); // 注册一个名为 "myAspect" 的单例 bean,类型为 MyAspect beanFactory.registerSingleton("myAspect", new MyAspect()); // 创建一个切面工厂的 BeanDefinition RootBeanDefinition aspectFactoryDef = new RootBeanDefinition(SimpleBeanFactoryAwareAspectInstanceFactory.class); // 设置切面工厂的属性 aspectBeanName 为 "myAspect" aspectFactoryDef.getPropertyValues().add("aspectBeanName", "myAspect"); // 设置切面工厂为合成的,即不对外暴露 aspectFactoryDef.setSynthetic(true); // 注册名为 "simpleBeanFactoryAwareAspectInstanceFactory" 的 bean,并使用切面工厂的 BeanDefinition beanFactory.registerBeanDefinition("simpleBeanFactoryAwareAspectInstanceFactory", aspectFactoryDef); // 从 BeanFactory 中获取 SimpleBeanFactoryAwareAspectInstanceFactory 的实例 SimpleBeanFactoryAwareAspectInstanceFactory simpleBeanFactoryAwareAif = beanFactory.getBean(SimpleBeanFactoryAwareAspectInstanceFactory.class); System.out.println("SimpleBeanFactoryAwareAspectInstanceFactory (1): " + simpleBeanFactoryAwareAif.getAspectInstance()); System.out.println("SimpleBeanFactoryAwareAspectInstanceFactory (2): " + simpleBeanFactoryAwareAif.getAspectInstance()); } } ================================================ FILE: spring-aop/spring-aop-aspectInstanceFactory/src/main/java/com/xcs/spring/MyAspect.java ================================================ package com.xcs.spring; import org.aspectj.lang.annotation.Aspect; @Aspect class MyAspect { } ================================================ FILE: spring-aop/spring-aop-aspectJAdvisorFactory/README.md ================================================ ## AspectJAdvisorFactory - [AspectJAdvisorFactory](#aspectjadvisorfactory) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、接口源码](#四接口源码) - [五、主要实现](#五主要实现) - [六、类关系图](#六类关系图) - [七、最佳实践](#七最佳实践) - [八、源码分析](#八源码分析) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 `AspectJAdvisorFactory` 接口是 Spring AOP 中负责将 AspectJ 注解标记的切面类转换为 Advisor 对象的关键接口,实现类解析注解并生成 Advisor,使得 Spring AOP 能够与 AspectJ 注解风格结合,提供灵活的面向切面编程能力。 ### 三、主要功能 1. **解析AspectJ注解** + AspectJAdvisorFactory 实现类负责解析 AspectJ 注解,如 @Aspect、@Before、@After 等,以及切点表达式等相关内容。 2. **创建Advisor对象** + 根据解析得到的 AspectJ 注解信息,AspectJAdvisorFactory 实现类生成对应的 Advisor 对象,其中包含切面的通知(Advice)和切入点(Pointcut)。 3. **注册Advisor对象** + 生成的 Advisor 对象可以被注册到 Spring AOP 框架中,以便在运行时实现面向切面编程的功能。 4. **支持与AspectJ注解风格的结合** + 通过 AspectJAdvisorFactory,Spring AOP 可以与 AspectJ 注解风格结合使用,为开发者提供了更为灵活和方便的 AOP 编程方式。 ### 四、接口源码 `AspectJAdvisorFactory`接口,用于创建 Spring AOP Advisors,其中 Advisors 是根据 AspectJ 注解语法标记的类来生成的。该接口包含了判断类是否为切面、验证切面类的有效性、构建切面实例的 Advisors 以及为给定的 AspectJ advice 方法构建 Spring AOP Advisor 和 Advice 的方法。 ```java /** * 用于从用 AspectJ 注解语法注释的类中创建 Spring AOP Advisor 的工厂接口。 * * @author Rod Johnson * @author Juergen Hoeller * @since 2.0 * @see AspectMetadata * @see org.aspectj.lang.reflect.AjTypeSystem */ public interface AspectJAdvisorFactory { /** * 确定给定的类是否是一个切面,由 AspectJ 的 {@link org.aspectj.lang.reflect.AjTypeSystem} 报告。 *

如果所谓的切面无效(例如扩展了具体切面类),则简单地返回 {@code false}。 * 对于一些 Spring AOP 无法处理的切面,例如具有不受支持的实例化模型,将返回 true。 * 如果需要处理这些情况,请使用 {@link #validate} 方法。 * @param clazz 所谓的注解式 AspectJ 类 * @return 此类是否被 AspectJ 识别为切面类 */ boolean isAspect(Class clazz); /** * 给定的类是否是有效的 AspectJ 切面类? * @param aspectClass 要验证的所谓的 AspectJ 注解式类 * @throws AopConfigException 如果类是无效的切面(永远不合法) * @throws NotAnAtAspectException 如果类根本不是一个切面(根据上下文的不同可能合法也可能不合法) */ void validate(Class aspectClass) throws AopConfigException; /** * 为指定的切面实例上的所有带有注解的 At-AspectJ 方法构建 Spring AOP Advisors。 * @param aspectInstanceFactory 切面实例工厂 * (而不是切面实例本身,以避免过早实例化) * @return 此类的一组 advisors */ List getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory); /** * 为给定的 AspectJ advice 方法构建 Spring AOP Advisor。 * @param candidateAdviceMethod 候选的 advice 方法 * @param aspectInstanceFactory 切面实例工厂 * @param declarationOrder 在切面内的声明顺序 * @param aspectName 切面的名称 * @return 如果方法不是 AspectJ advice 方法,或者是将被其他 advice 使用但不会单独创建 Spring advice 的切入点,则返回 {@code null} */ @Nullable Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName); /** * 为给定的 AspectJ advice 方法构建 Spring AOP Advice。 * @param candidateAdviceMethod 候选的 advice 方法 * @param expressionPointcut AspectJ 表达式切入点 * @param aspectInstanceFactory 切面实例工厂 * @param declarationOrder 在切面内的声明顺序 * @param aspectName 切面的名称 * @return 如果方法不是 AspectJ advice 方法,或者是将被其他 advice 使用但不会单独创建 Spring advice 的切入点,则返回 {@code null} * @see org.springframework.aop.aspectj.AspectJAroundAdvice * @see org.springframework.aop.aspectj.AspectJMethodBeforeAdvice * @see org.springframework.aop.aspectj.AspectJAfterAdvice * @see org.springframework.aop.aspectj.AspectJAfterReturningAdvice * @see org.springframework.aop.aspectj.AspectJAfterThrowingAdvice */ @Nullable Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName); } ``` ### 五、主要实现 1. **ReflectiveAspectJAdvisorFactory** + `ReflectiveAspectJAdvisorFactory` 实现类是利用反射机制解析 AspectJ 注解,并创建相应的 Advisor 对象,支持注解风格的 AspectJ 切面,为我们提供了灵活而强大的面向切面编程能力。 ### 六、类关系图 ~~~mermaid classDiagram direction BT class AbstractAspectJAdvisorFactory class AspectJAdvisorFactory { <> } class ReflectiveAspectJAdvisorFactory AbstractAspectJAdvisorFactory ..> AspectJAdvisorFactory ReflectiveAspectJAdvisorFactory --> AbstractAspectJAdvisorFactory ~~~ ### 七、最佳实践 使用 `AspectJAdvisorFactory` 实现类 `ReflectiveAspectJAdvisorFactory`,以创建 Advisors 并打印它们。首先,通过 `DefaultListableBeanFactory` 创建了一个默认的 Bean 工厂,并在其中注册了一个名为 "myAspect" 的单例 Bean,类型为 `MyAspect`。然后,创建了一个 `MetadataAwareAspectInstanceFactory` 实例 `factory`,用于实例化切面。接着,创建了 `ReflectiveAspectJAdvisorFactory` 实例 `aspectJAdvisorFactory`,并使用它获取所有注解式 AspectJ 方法的 Advisors。最后,通过遍历 Advisors 并打印的方式展示了这些 Advisors。 ```java public class AspectJAdvisorFactoryDemo { public static void main(String[] args) { // 创建一个默认的 Bean 工厂 DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); // 在 Bean 工厂中注册一个名为 "myAspect" 的单例 Bean,类型为 MyAspect beanFactory.registerSingleton("myAspect", new MyAspect()); // 创建一个 Aspect 实例工厂,用于实例化切面 MetadataAwareAspectInstanceFactory factory = new BeanFactoryAspectInstanceFactory(beanFactory, "myAspect"); // 创建 ReflectiveAspectJAdvisorFactory 实例,用于创建 Advisor ReflectiveAspectJAdvisorFactory aspectJAdvisorFactory = new ReflectiveAspectJAdvisorFactory(beanFactory); // 获取所有注解式 AspectJ 方法的 Advisors List advisors = aspectJAdvisorFactory.getAdvisors(factory); // 打印 Advisors advisors.forEach(System.out::println); } } ``` 使用了 AspectJ 的注解 `@Aspect` 进行标记。在该切面类中,包含了两个通知方法`before()` 和 `after()`,分别使用 `@Before` 和 `@After` 注解标记。这两个通知方法分别在目标方法 `com.xcs.spring.MyService.doSomething()` 执行之前和之后执行,并输出相应的日志信息。 ```java @Aspect class MyAspect { @Before("execution(* com.xcs.spring.MyService.doSomething(..))") public void before() { System.out.println("Before executing the method..." ); } @After("execution(* com.xcs.spring.MyService.doSomething(..))") public void after() { System.out.println("After executing the method..." ); } } ``` 定义了一个名为 `MyService` 的简单 Java 类,其中包含一个名为 `doSomething()` 的方法。该方法简单地打印一条日志信息 "Doing something..."。这个类作为示例类使用,用来演示在 AOP 中如何应用切面逻辑。 ```java public class MyService { public void doSomething() { System.out.println("Doing something..."); } } ``` 运行结果,显示了两个 Advisor 对象的信息,它们分别对应着切面类 `MyAspect` 中的 `before()` 和 `after()` 方法,并针对相同的切点表达式 `execution(* com.xcs.spring.MyService.doSomething(..))`。 ```java InstantiationModelAwarePointcutAdvisor: expression [execution(* com.xcs.spring.MyService.doSomething(..))]; advice method [public void com.xcs.spring.MyAspect.before()]; perClauseKind=SINGLETON InstantiationModelAwarePointcutAdvisor: expression [execution(* com.xcs.spring.MyService.doSomething(..))]; advice method [public void com.xcs.spring.MyAspect.after()]; perClauseKind=SINGLETON ``` ### 八、源码分析 在`org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory#getAdvisors`方法中,根据给定的切面实例工厂,获取切面类中的通知器列表。首先,验证切面类的有效性,然后使用元数据判断是否需要延迟实例化切面实例工厂。接着,遍历切面类中的方法,获取通知器,并将其添加到通知器列表中。如果切面是针对目标的并且是延迟实例化的,则添加一个虚拟实例化通知器。最后,查找切面类中的引入字段,获取相应的通知器,并将其添加到通知器列表中,最终返回该列表。 ```java /** * 获取通知器列表。 * @param aspectInstanceFactory 切面实例工厂 * @return 通知器列表 */ @Override public List getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) { // 获取切面类和切面名称 Class aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass(); String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName(); // 验证切面类的有效性 validate(aspectClass); // 将MetadataAwareAspectInstanceFactory包装成装饰器,以保证只实例化一次 MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory = new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory); // 创建通知器列表 List advisors = new ArrayList<>(); // 遍历切面类中的方法,获取通知器 for (Method method : getAdvisorMethods(aspectClass)) { // 由于JDK不再以源代码中的声明顺序返回方法,因此固定declarationOrderInAspect为0以支持跨JVM启动的可靠通知顺序 Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName); if (advisor != null) { advisors.add(advisor); } } // 如果是针对目标的切面,则添加一个虚拟实例化通知器 if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) { Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory); advisors.add(0, instantiationAdvisor); } // 查找切面类中的引入字段 for (Field field : aspectClass.getDeclaredFields()) { Advisor advisor = getDeclareParentsAdvisor(field); if (advisor != null) { advisors.add(advisor); } } return advisors; } ``` 在`org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory#getAdvisorMethods`方法中,获取切面类中作为通知的方法列表。它通过反射遍历切面类的方法,并使用 adviceMethodFilter 过滤出通知方法,最后根据方法数量进行排序后返回。 ```java /** * 获取切面类中用作通知的方法列表。 * * @param aspectClass 切面类 * @return 切面类中的通知方法列表 */ private List getAdvisorMethods(Class aspectClass) { // 创建一个空的方法列表 List methods = new ArrayList<>(); // 使用 ReflectionUtils 遍历切面类中的方法,将符合条件的方法添加到方法列表中 ReflectionUtils.doWithMethods(aspectClass, methods::add, adviceMethodFilter); // 如果方法数量大于1,即存在多个通知方法,则按照指定的比较器对方法列表进行排序 if (methods.size() > 1) { methods.sort(adviceMethodComparator); } // 返回方法列表 return methods; } ``` 在`org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory#adviceMethodFilter`字段中,用于筛选切面类中的通知方法。它通过 ReflectionUtils.USER_DECLARED_METHODS 筛选出用户自定义的方法,并排除带有 @Pointcut 注解的方法。 ```java private static final MethodFilter adviceMethodFilter = ReflectionUtils.USER_DECLARED_METHODS .and(method -> (AnnotationUtils.getAnnotation(method, Pointcut.class) == null)); ``` 在 `org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory#adviceMethodComparator` 字段中,按照通知的类型依次排列:`Around`、`Before`、`After`、`AfterReturning`、`AfterThrowing`。同时考虑了其中的特殊情况,即使 @After 方法在排序上位于 `@AfterReturning` 和 `@AfterThrowing` 之前,但实际上 @After 方法会在 `@AfterReturning` 和 `@AfterThrowing` 方法之后被调用。这是因为 `AspectJAfterAdvice.invoke(MethodInvocation)` 方法在 `try` 块中调用了 `proceed()` 方法,并且只有在相应的 `finally` 块中才调用了 `@After` 方法。 ```java private static final Comparator adviceMethodComparator; static { // 注意:尽管 @After 排在 @AfterReturning 和 @AfterThrowing 之前, // 但实际上 @After 通知方法会在 @AfterReturning 和 @AfterThrowing 方法之后被调用, // 这是因为 AspectJAfterAdvice.invoke(MethodInvocation) 方法在 `try` 块中调用了 proceed() 方法, // 并且只有在相应的 `finally` 块中才调用了 @After 方法。 // 定义一个方法比较器,按照通知的类型依次排列:Around、Before、After、AfterReturning、AfterThrowing。 // 同时考虑了其中的特殊情况,即使 @After 方法在排序上位于 @AfterReturning 和 @AfterThrowing 之前, // 但实际上 @After 方法会在 @AfterReturning 和 @AfterThrowing 方法之后被调用。 Comparator adviceKindComparator = new ConvertingComparator<>( new InstanceComparator<>( Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class), (Converter) method -> { // 使用 AspectJAnnotation 查找方法上的 AspectJ 注解 AspectJAnnotation ann = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(method); return (ann != null ? ann.getAnnotation() : null); }); // 定义一个方法名比较器 Comparator methodNameComparator = new ConvertingComparator<>(Method::getName); // 将两个比较器按顺序组合,首先按照通知类型排序,然后按照方法名称排序 adviceMethodComparator = adviceKindComparator.thenComparing(methodNameComparator); } ``` 在`org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory#getAdvisor`方法中,根据候选的通知方法获取 Advisor 对象的功能,首先验证了 Aspect 类的有效性,然后获取切点表达式,如果切点表达式为空,则返回 null,否则创建一个 InstantiationModelAwarePointcutAdvisorImpl 对象,并使用该对象包装切点表达式、通知方法等信息。 ```java /** * 根据候选的通知方法获取 Advisor 对象。 * * @param candidateAdviceMethod 候选的通知方法 * @param aspectInstanceFactory Aspect 实例工厂,用于创建 Aspect 实例 * @param declarationOrderInAspect 在 Aspect 中的声明顺序 * @param aspectName Aspect 的名称 * @return Advisor 对象,如果候选方法不是有效的切点,则返回 null */ @Override @Nullable public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrderInAspect, String aspectName) { // 验证 Aspect 类的有效性 validate(aspectInstanceFactory.getAspectMetadata().getAspectClass()); // 获取切点表达式 AspectJExpressionPointcut expressionPointcut = getPointcut( candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass()); // 如果切点表达式为空,则返回 null if (expressionPointcut == null) { return null; } // 创建 InstantiationModelAwarePointcutAdvisorImpl 对象,用于管理切点表达式、通知方法等信息 return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod, this, aspectInstanceFactory, declarationOrderInAspect, aspectName); } ``` 在`org.springframework.aop.aspectj.annotation.InstantiationModelAwarePointcutAdvisorImpl#InstantiationModelAwarePointcutAdvisorImpl`方法中,初始化了切面相关的属性,包括声明的切点、切面方法的声明类、方法名称、参数类型、切面工厂、切面实例工厂、声明顺序和切面名称。如果切面是延迟实例化的,它会创建一个动态切点,并将静态切点与初始切点联合起来,以实现从预实例化到后实例化状态的动态变化。如果切面不是延迟实例化的,则使用初始切点,同时实例化通知方法。 ```java public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut, Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) { // 声明切点 this.declaredPointcut = declaredPointcut; // 获取通知方法所在的类 this.declaringClass = aspectJAdviceMethod.getDeclaringClass(); // 获取通知方法的名称 this.methodName = aspectJAdviceMethod.getName(); // 获取通知方法的参数类型 this.parameterTypes = aspectJAdviceMethod.getParameterTypes(); // 设置 AspectJ 的通知方法 this.aspectJAdviceMethod = aspectJAdviceMethod; // 设置 AspectJ 的 AdvisorFactory this.aspectJAdvisorFactory = aspectJAdvisorFactory; // 设置 Aspect 实例工厂 this.aspectInstanceFactory = aspectInstanceFactory; // 设置声明顺序 this.declarationOrder = declarationOrder; // 设置切面名称 this.aspectName = aspectName; // 如果切面是延迟实例化的 if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) { // 切点的静态部分是一个延迟类型 Pointcut preInstantiationPointcut = Pointcuts.union( aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut); // 使之动态:必须从预实例化状态变为后实例化状态 // 如果它不是动态切点,则可能会在第一次评估后被 Spring AOP 基础设施优化掉 this.pointcut = new PerTargetInstantiationModelPointcut( this.declaredPointcut, preInstantiationPointcut, aspectInstanceFactory); // 设置为延迟 this.lazy = true; } else { // 单例切面 this.pointcut = this.declaredPointcut; this.lazy = false; // 实例化通知 this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut); } } ``` 在`org.springframework.aop.aspectj.annotation.InstantiationModelAwarePointcutAdvisorImpl#instantiateAdvice`方法中,使用 AspectJAdvisorFactory 获取通知实例,然后检查是否为空,如果为空则返回一个空的通知。 ```java /** * 根据给定的切点实例化通知。 * * @param pointcut 切点表达式 * @return 实例化的通知 */ private Advice instantiateAdvice(AspectJExpressionPointcut pointcut) { // 使用AspectJAdvisorFactory获取通知实例 Advice advice = this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pointcut, this.aspectInstanceFactory, this.declarationOrder, this.aspectName); // 如果获取的通知实例为空,则返回空通知 return (advice != null ? advice : EMPTY_ADVICE); } ``` 在`org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory#getAdvice`方法中,根据给定的候选通知方法、切点表达式、切面实例工厂等信息,获取对应的 Spring AOP 通知实例。它首先验证切面类的有效性,然后根据候选通知方法的 AspectJ 注解类型,实例化相应的 Spring AOP 通知,如环绕通知、前置通知、后置通知等,并配置相关的通知属性,最后返回所生成的 Spring AOP 通知实例。 ```java @Override @Nullable public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) { // 获取候选通知方法所在的切面类 Class candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass(); // 验证切面类的有效性 validate(candidateAspectClass); // 获取候选通知方法的 AspectJ 注解 AspectJAnnotation aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod); // 如果候选通知方法没有 AspectJ 注解,则返回 null if (aspectJAnnotation == null) { return null; } // 判断切面类是否为 AspectJ 注解的类 if (!isAspect(candidateAspectClass)) { // 如果不是,则抛出异常 throw new AopConfigException("Advice must be declared inside an aspect type: " + "Offending method '" + candidateAdviceMethod + "' in class [" + candidateAspectClass.getName() + "]"); } // 如果日志级别为 DEBUG,则打印找到的 AspectJ 方法信息 if (logger.isDebugEnabled()) { logger.debug("Found AspectJ method: " + candidateAdviceMethod); } // 声明 Spring AOP 通知实例 AbstractAspectJAdvice springAdvice; // 根据注解类型实例化相应的 Spring AOP 通知 switch (aspectJAnnotation.getAnnotationType()) { // 处理切点注解 case AtPointcut: if (logger.isDebugEnabled()) { logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'"); } return null; // 处理环绕通知 case AtAround: springAdvice = new AspectJAroundAdvice( candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); break; // 处理前置通知 case AtBefore: springAdvice = new AspectJMethodBeforeAdvice( candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); break; // 处理后置通知 case AtAfter: springAdvice = new AspectJAfterAdvice( candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); break; // 处理返回后通知 case AtAfterReturning: springAdvice = new AspectJAfterReturningAdvice( candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation(); if (StringUtils.hasText(afterReturningAnnotation.returning())) { springAdvice.setReturningName(afterReturningAnnotation.returning()); } break; // 处理抛出异常后通知 case AtAfterThrowing: springAdvice = new AspectJAfterThrowingAdvice( candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation(); if (StringUtils.hasText(afterThrowingAnnotation.throwing())) { springAdvice.setThrowingName(afterThrowingAnnotation.throwing()); } break; // 处理其他类型的通知,抛出不支持的操作异常 default: throw new UnsupportedOperationException( "Unsupported advice type on method: " + candidateAdviceMethod); } // 配置通知的相关属性 springAdvice.setAspectName(aspectName); springAdvice.setDeclarationOrder(declarationOrder); // 获取通知方法的参数名数组 String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod); // 如果参数名数组不为空,则设置到通知中 if (argNames != null) { springAdvice.setArgumentNamesFromStringArray(argNames); } // 计算参数绑定 springAdvice.calculateArgumentBindings(); return springAdvice; } ``` ================================================ FILE: spring-aop/spring-aop-aspectJAdvisorFactory/pom.xml ================================================ com.xcs.spring spring-aop 0.0.1-SNAPSHOT 4.0.0 spring-aop-aspectJAdvisorFactory ================================================ FILE: spring-aop/spring-aop-aspectJAdvisorFactory/src/main/java/com/xcs/spring/AspectJAdvisorFactoryDemo.java ================================================ package com.xcs.spring; import org.springframework.aop.Advisor; import org.springframework.aop.aspectj.annotation.BeanFactoryAspectInstanceFactory; import org.springframework.aop.aspectj.annotation.MetadataAwareAspectInstanceFactory; import org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import java.util.List; public class AspectJAdvisorFactoryDemo { public static void main(String[] args) { // 创建一个默认的 Bean 工厂 DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); // 在 Bean 工厂中注册一个名为 "myAspect" 的单例 Bean,类型为 MyAspect beanFactory.registerSingleton("myAspect", new MyAspect()); // 创建一个 Aspect 实例工厂,用于实例化切面 MetadataAwareAspectInstanceFactory factory = new BeanFactoryAspectInstanceFactory(beanFactory, "myAspect"); // 创建 ReflectiveAspectJAdvisorFactory 实例,用于创建 Advisor ReflectiveAspectJAdvisorFactory aspectJAdvisorFactory = new ReflectiveAspectJAdvisorFactory(beanFactory); // 获取所有注解式 AspectJ 方法的 Advisors List advisors = aspectJAdvisorFactory.getAdvisors(factory); // 打印 Advisors advisors.forEach(System.out::println); } } ================================================ FILE: spring-aop/spring-aop-aspectJAdvisorFactory/src/main/java/com/xcs/spring/MyAspect.java ================================================ package com.xcs.spring; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; @Aspect class MyAspect { @Before("execution(* com.xcs.spring.MyService.foo(..))") public void before() { System.out.println("Before executing the method..." ); } @After("execution(* com.xcs.spring.MyService.foo(..))") public void after() { System.out.println("After executing the method..." ); } } ================================================ FILE: spring-aop/spring-aop-aspectJAdvisorFactory/src/main/java/com/xcs/spring/MyService.java ================================================ package com.xcs.spring; public class MyService { public void foo() { System.out.println("foo..."); } } ================================================ FILE: spring-aop/spring-aop-beanFactoryAdvisorRetrievalHelper/README.md ================================================ ## BeanFactoryAdvisorRetrievalHelper - [BeanFactoryAdvisorRetrievalHelper](#beanfactoryadvisorretrievalhelper) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、最佳实践](#四最佳实践) - [五、时序图](#五时序图) - [六、源码分析](#六源码分析) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 `BeanFactoryAdvisorRetrievalHelper` 类是 Spring AOP 框架中的辅助工具,用于在 Bean 工厂中检索 Advisor,这些 Advisor 定义了切面逻辑,可以在目标 Bean 的方法调用中织入相应的通知。 ### 三、主要功能 1. **协助Advisor的检索** + 帮助 Spring AOP 框架在应用程序的 Bean 工厂中查找与目标 Bean 相关的 Advisor。 2. **解析Advisor的Bean名称** + 解析 Advisor 在 Spring 容器中的 Bean 名称,并根据名称从 Bean 工厂中获取相应的 Advisor 实例。 3. **适配不同类型的Advisor** + 支持不同类型的 Advisor,包括前置通知(BeforeAdvice)、后置通知(AfterAdvice)、环绕通知(AroundAdvice)等,能够正确地应用到目标 Bean 上。 4. **辅助创建代理** + 辅助 Spring 容器创建代理对象,并将 Advisor 中定义的通知逻辑织入到目标 Bean 的方法调用中。 ### 四、最佳实践 使用基于注解的应用上下文来获取并调用 `MyService` Bean 的 `foo()` 方法。首先,创建了一个 `AnnotationConfigApplicationContext` 实例,通过传入 `AppConfig.class` 来初始化基于注解的应用上下文。然后,通过 `context.getBean(MyService.class)` 获取了 `MyService` Bean 的实例,并调用了其 `foo()` 方法。 ```java public class BeanFactoryAdvisorRetrievalHelperDemo { public static void main(String[] args) { // 创建基于注解的应用上下文 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); // 从应用上下文中获取MyService bean MyService myService = context.getBean(MyService.class); // 调用MyService的方法 myService.foo(); } } ``` `AppConfig` 类是一个使用 `@Configuration` 注解标记的配置类,通过 `@EnableAspectJAutoProxy` 开启了 AspectJ 自动代理功能,并通过 `@ComponentScan` 启用了组件扫描,用于自动发现和注册 Spring 组件。 ```java @Configuration @EnableAspectJAutoProxy @ComponentScan public class AppConfig { } ``` 使用 `@Component` 注解标记的自定义 Advisor,继承自 `AbstractPointcutAdvisor`。它定义了一个总是返回真值的 Pointcut,并将一个自定义的 Advice `MyAdvice` 应用于目标方法上。 ```java @Component public class MyAdvisor extends AbstractPointcutAdvisor { @Override public Pointcut getPointcut() { return Pointcut.TRUE; } @Override public Advice getAdvice() { return new MyAdvice(); } } ``` `MyAdvice` 类是一个实现了 `MethodBeforeAdvice` 接口的自定义通知类,用于在目标方法执行前执行特定逻辑。在 `before()` 方法中,它打印了一条消息:"Before method execution"。 ```java public class MyAdvice implements MethodBeforeAdvice { @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("Before method execution"); } } ``` `MyService` 类是一个使用 `@Service` 注解标记的服务类,提供了一个名为 `foo()` 的方法,该方法在调用时会打印消息 "foo..."。 ```java @Service public class MyService { public void foo() { System.out.println("foo..."); } } ``` 运行结果,调用 `MyService` 类的 `foo()` 方法之前,成功地执行了一个切面通知,输出了 "Before method execution" 的消息,然后执行了 `foo()` 方法,输出了 "foo..." 的消息。 ```java Before method execution foo... ``` ### 五、时序图 ~~~mermaid sequenceDiagram AbstractAutowireCapableBeanFactory->>AbstractAutoProxyCreator: postProcessAfterInitialization() Note over AbstractAutowireCapableBeanFactory,AbstractAutoProxyCreator: 调用后处理方法 AbstractAutoProxyCreator->>AbstractAutoProxyCreator: wrapIfNecessary() Note over AbstractAutoProxyCreator: 调用包装方法 AbstractAutoProxyCreator->>AbstractAdvisorAutoProxyCreator: getAdvicesAndAdvisorsForBean() Note over AbstractAutoProxyCreator,AbstractAdvisorAutoProxyCreator: 获取通知和 Advisors AbstractAdvisorAutoProxyCreator->>AbstractAdvisorAutoProxyCreator: findEligibleAdvisors() Note over AbstractAdvisorAutoProxyCreator: 查找合适的 Advisors AbstractAdvisorAutoProxyCreator->>AnnotationAwareAspectJAutoProxyCreator: findCandidateAdvisors() Note over AbstractAdvisorAutoProxyCreator,AnnotationAwareAspectJAutoProxyCreator: 查找候选的 Advisors AnnotationAwareAspectJAutoProxyCreator->>AbstractAdvisorAutoProxyCreator: super.findCandidateAdvisors() Note over AnnotationAwareAspectJAutoProxyCreator,AbstractAdvisorAutoProxyCreator: 调用父类的查找候选的 Advisors AbstractAdvisorAutoProxyCreator->>BeanFactoryAdvisorRetrievalHelper: findAdvisorBeans() Note over AbstractAdvisorAutoProxyCreator,BeanFactoryAdvisorRetrievalHelper: 查找当前Bean工厂中所有符合条件的Advisor BeanFactoryAdvisorRetrievalHelper->>AbstractAutoProxyCreator: 返回 advisors ~~~ ### 六、源码分析 在`org.springframework.aop.framework.autoproxy.BeanFactoryAdvisorRetrievalHelper#findAdvisorBeans`方法中,主要功能是在当前的 Bean 工厂中查找所有符合条件的 Advisor Beans。它忽略了 FactoryBeans,并排除了当前正在创建中的 Beans。该方法首先确定 Advisor Bean 的名称列表,如果尚未缓存,则通过 `BeanFactoryUtils.beanNamesForTypeIncludingAncestors()` 方法获取。然后,它遍历这些 Advisor Bean 的名称,检查它们是否符合条件,并将符合条件的 Advisor Bean 添加到结果列表中。在添加之前,它会检查该 Bean 是否当前正在创建中,如果是,则跳过。最后,返回包含所有符合条件的 Advisor Beans 的列表。 ```java /** * 在当前 Bean 工厂中查找所有符合条件的 Advisor Bean, * 忽略 FactoryBeans,并排除当前正在创建的 Bean。 * @return {@link org.springframework.aop.Advisor} Bean 的列表 * @see #isEligibleBean */ public List findAdvisorBeans() { // 如果未缓存 Advisor Bean 的名称列表,则确定该列表。 String[] advisorNames = this.cachedAdvisorBeanNames; if (advisorNames == null) { // 不要在这里初始化 FactoryBeans我们需要保持所有常规 Bean 未初始化,以便自动代理创建器应用到它们上! advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this.beanFactory, Advisor.class, true, false); this.cachedAdvisorBeanNames = advisorNames; } if (advisorNames.length == 0) { return new ArrayList<>(); } List advisors = new ArrayList<>(); // 遍历 Advisor Bean 名称列表 for (String name : advisorNames) { // 检查 Bean 是否符合条件 if (isEligibleBean(name)) { // 如果 Bean 当前正在创建中,则跳过 if (this.beanFactory.isCurrentlyInCreation(name)) { if (logger.isTraceEnabled()) { logger.trace("Skipping currently created advisor '" + name + "'"); } } else { try { // 尝试获取 Advisor Bean,并添加到列表中 advisors.add(this.beanFactory.getBean(name, Advisor.class)); } catch (BeanCreationException ex) { Throwable rootCause = ex.getMostSpecificCause(); if (rootCause instanceof BeanCurrentlyInCreationException) { BeanCreationException bce = (BeanCreationException) rootCause; String bceBeanName = bce.getBeanName(); // 如果当前 Bean 依赖于正在创建的 Bean,则跳过 if (bceBeanName != null && this.beanFactory.isCurrentlyInCreation(bceBeanName)) { if (logger.isTraceEnabled()) { logger.trace("Skipping advisor '" + name + "' with dependency on currently created bean: " + ex.getMessage()); } // 忽略表示对当前正在尝试进行通知的 Bean 的引用。 // 我们希望找到除当前正在创建的 Bean 本身之外的其他 Advisor。 continue; } } // 如果获取 Advisor Bean 失败,则抛出异常 throw ex; } } } } return advisors; } ``` ================================================ FILE: spring-aop/spring-aop-beanFactoryAdvisorRetrievalHelper/pom.xml ================================================ com.xcs.spring spring-aop 0.0.1-SNAPSHOT 4.0.0 spring-aop-beanFactoryAdvisorRetrievalHelper ================================================ FILE: spring-aop/spring-aop-beanFactoryAdvisorRetrievalHelper/src/main/java/com/xcs/spring/AppConfig.java ================================================ package com.xcs.spring; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; @Configuration @EnableAspectJAutoProxy @ComponentScan public class AppConfig { } ================================================ FILE: spring-aop/spring-aop-beanFactoryAdvisorRetrievalHelper/src/main/java/com/xcs/spring/BeanFactoryAdvisorRetrievalHelperDemo.java ================================================ package com.xcs.spring; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class BeanFactoryAdvisorRetrievalHelperDemo { public static void main(String[] args) { // 创建基于注解的应用上下文 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); // 从应用上下文中获取MyService bean MyService myService = context.getBean(MyService.class); // 调用MyService的方法 myService.foo(); } } ================================================ FILE: spring-aop/spring-aop-beanFactoryAdvisorRetrievalHelper/src/main/java/com/xcs/spring/MyAdvice.java ================================================ package com.xcs.spring; import org.springframework.aop.MethodBeforeAdvice; import java.lang.reflect.Method; public class MyAdvice implements MethodBeforeAdvice { @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("Before method execution"); } } ================================================ FILE: spring-aop/spring-aop-beanFactoryAdvisorRetrievalHelper/src/main/java/com/xcs/spring/MyAdvisor.java ================================================ package com.xcs.spring; import org.aopalliance.aop.Advice; import org.springframework.aop.Pointcut; import org.springframework.aop.support.AbstractPointcutAdvisor; import org.springframework.stereotype.Component; @Component public class MyAdvisor extends AbstractPointcutAdvisor { @Override public Pointcut getPointcut() { return Pointcut.TRUE; } @Override public Advice getAdvice() { return new MyAdvice(); } } ================================================ FILE: spring-aop/spring-aop-beanFactoryAdvisorRetrievalHelper/src/main/java/com/xcs/spring/MyService.java ================================================ package com.xcs.spring; import org.springframework.stereotype.Service; @Service public class MyService { public void foo() { System.out.println("foo..."); } } ================================================ FILE: spring-aop/spring-aop-beanFactoryAspectJAdvisorsBuilder/README.md ================================================ ## BeanFactoryAspectJAdvisorsBuilder - [BeanFactoryAspectJAdvisorsBuilder](#beanfactoryaspectjadvisorsbuilder) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、最佳实践](#四最佳实践) - [五、时序图](#五时序图) - [六、源码分析](#六源码分析) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 `BeanFactoryAspectJAdvisorsBuilder` 是 Spring AOP 中的一个重要类,负责将应用中使用 AspectJ 注解标记的切面解析并转换为 Spring AOP 中的通知器,从而实现基于注解的切面编程。 ### 三、主要功能 1. **扫描 AspectJ 注解** + 这个类会扫描应用中的类,查找带有 AspectJ 注解的类,比如 `@Aspect`。它会识别这些类,并将它们转换成 Spring AOP 中的通知器。 2. **解析切面和通知** + 一旦发现带有 AspectJ 注解的类,`BeanFactoryAspectJAdvisorsBuilder` 将解析这些类,找到其中定义的切面以及切面中的通知。 3. **创建通知器(advisors)** + 基于解析得到的切面和通知信息,这个类会创建对应的通知器。通知器包含了切面逻辑以及连接点(切入点)信息,它们将被应用到目标对象的方法调用中。 4. **注册通知器** + 最后,`BeanFactoryAspectJAdvisorsBuilder` 将创建的通知器注册到 Spring 的 AOP 框架中,以便在应用程序运行时生效。 ### 四、最佳实践 使用基于注解的应用上下文来获取并调用 `MyService` Bean 的 `foo()` 方法。首先,创建了一个 `AnnotationConfigApplicationContext` 实例,通过传入 `AppConfig.class` 来初始化基于注解的应用上下文。然后,通过 `context.getBean(MyService.class)` 获取了 `MyService` Bean 的实例,并调用了其 `foo()` 方法。 ```java public class BeanFactoryAspectJAdvisorsBuilderDemo { public static void main(String[] args) { // 创建基于注解的应用上下文 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); // 从应用上下文中获取MyService bean MyService myService = context.getBean(MyService.class); // 调用MyService的方法 myService.foo(); } } ``` `AppConfig` 类是一个使用 `@Configuration` 注解标记的配置类,通过 `@EnableAspectJAutoProxy` 开启了 AspectJ 自动代理功能,并通过 `@ComponentScan` 启用了组件扫描,用于自动发现和注册 Spring 组件。 ```java @Configuration @EnableAspectJAutoProxy @ComponentScan public class AppConfig { } ``` 通过 @Aspect 和 @Component 注解将其标记为 Spring 组件,并定义了一个在 com.xcs.spring.MyService 类的 foo 方法执行前执行的前置通知(Before advice)。 ```java @Aspect @Component class MyAspect { @Before("execution(* com.xcs.spring.MyService.foo(..))") public void before() { System.out.println("Before method execution"); } } ``` `MyService` 类是一个使用 `@Service` 注解标记的服务类,提供了一个名为 `foo()` 的方法,该方法在调用时会打印消息 "foo..."。 ```java @Service public class MyService { public void foo() { System.out.println("foo..."); } } ``` 运行结果,调用 `MyService` 类的 `foo()` 方法之前,成功地执行了一个切面通知,输出了 "Before method execution" 的消息,然后执行了 `foo()` 方法,输出了 "foo..." 的消息。 ```java Before method execution foo... ``` ### 五、时序图 ~~~mermaid sequenceDiagram AbstractAutowireCapableBeanFactory->>AbstractAutoProxyCreator: postProcessAfterInitialization() Note over AbstractAutowireCapableBeanFactory,AbstractAutoProxyCreator: 调用后处理方法 AbstractAutoProxyCreator->>AbstractAutoProxyCreator: wrapIfNecessary() Note over AbstractAutoProxyCreator: 调用包装方法 AbstractAutoProxyCreator->>AbstractAdvisorAutoProxyCreator: getAdvicesAndAdvisorsForBean() Note over AbstractAutoProxyCreator,AbstractAdvisorAutoProxyCreator: 获取通知和 Advisors AbstractAdvisorAutoProxyCreator->>AbstractAdvisorAutoProxyCreator: findEligibleAdvisors() Note over AbstractAdvisorAutoProxyCreator: 查找合适的 Advisors AbstractAdvisorAutoProxyCreator->>AnnotationAwareAspectJAutoProxyCreator: findCandidateAdvisors() Note over AbstractAdvisorAutoProxyCreator,AnnotationAwareAspectJAutoProxyCreator: 查找候选的 Advisors AnnotationAwareAspectJAutoProxyCreator->>BeanFactoryAspectJAdvisorsBuilder: buildAspectJAdvisors() Note over AnnotationAwareAspectJAutoProxyCreator,BeanFactoryAspectJAdvisorsBuilder: 构建 AspectJ Advisors BeanFactoryAspectJAdvisorsBuilder->>AbstractAutoProxyCreator: 返回 advisors ~~~ ### 六、源码分析 在`org.springframework.aop.aspectj.annotation.BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors`方法中,主要负责在当前的 Bean 工厂中查找使用 AspectJ 注解标记的切面 Bean,并将其转换为 Spring AOP Advisors 的列表。它遍历所有的 Bean 名称,识别切面 Bean,并根据其实例化模型(单例或多例)创建对应的 AspectJ Advisors。在处理过程中,还会缓存单例切面的 Advisors,以提高性能。 ```java /** * 在当前 Bean 工厂中查找使用 AspectJ 注解标记的切面 Bean,并返回表示它们的 Spring AOP Advisors 列表。 *

为每个 AspectJ 的通知方法创建一个 Spring Advisor。 * @return 包含 {@link org.springframework.aop.Advisor} beans 的列表 * @see #isEligibleBean */ public List buildAspectJAdvisors() { // 如果切面 Bean 名称列表为空,则进行查找 List aspectNames = this.aspectBeanNames; if (aspectNames == null) { synchronized (this) { aspectNames = this.aspectBeanNames; if (aspectNames == null) { // 初始化切面 Advisors 列表和切面 Bean 名称列表 List advisors = new ArrayList<>(); aspectNames = new ArrayList<>(); // 获取当前 Bean 工厂中的所有 Bean 名称 String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this.beanFactory, Object.class, true, false); // 遍历所有 Bean 名称 for (String beanName : beanNames) { // 检查 Bean 是否符合条件 if (!isEligibleBean(beanName)) { continue; } // 获取 Bean 的类型 Class beanType = this.beanFactory.getType(beanName, false); // 如果无法获取类型,则跳过 if (beanType == null) { continue; } // 判断 Bean 是否是切面 if (this.advisorFactory.isAspect(beanType)) { // 将切面 Bean 名称加入列表 aspectNames.add(beanName); // 获取切面元数据 AspectMetadata amd = new AspectMetadata(beanType, beanName); // 判断切面的实例化模型 if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) { // 单例模式 // 创建单例模式的切面实例工厂 MetadataAwareAspectInstanceFactory factory = new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName); // 获取切面的 Advisors List classAdvisors = this.advisorFactory.getAdvisors(factory); // 缓存单例切面的 Advisors if (this.beanFactory.isSingleton(beanName)) { this.advisorsCache.put(beanName, classAdvisors); } else { this.aspectFactoryCache.put(beanName, factory); } advisors.addAll(classAdvisors); } else { // 多例模式 if (this.beanFactory.isSingleton(beanName)) { // 如果切面实例化模型为多例,但 Bean 是单例,则抛出异常 throw new IllegalArgumentException("Bean with name '" + beanName + "' is a singleton, but aspect instantiation model is not singleton"); } // 创建多例模式的切面实例工厂 MetadataAwareAspectInstanceFactory factory = new PrototypeAspectInstanceFactory(this.beanFactory, beanName); // 缓存切面实例工厂 this.aspectFactoryCache.put(beanName, factory); // 获取切面的 Advisors advisors.addAll(this.advisorFactory.getAdvisors(factory)); } } } // 将切面 Bean 名称列表缓存起来 this.aspectBeanNames = aspectNames; return advisors; } } } // 如果切面 Bean 名称列表为空,则返回空列表 if (aspectNames.isEmpty()) { return Collections.emptyList(); } // 创建用于存储所有 Advisors 的列表 List advisors = new ArrayList<>(); // 遍历切面 Bean 名称列表 for (String aspectName : aspectNames) { // 从缓存中获取 Advisors List cachedAdvisors = this.advisorsCache.get(aspectName); if (cachedAdvisors != null) { // 如果缓存中有 Advisors,则加入到结果列表中 advisors.addAll(cachedAdvisors); } else { // 如果缓存中没有 Advisors,则从切面实例工厂中获取 Advisors MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName); advisors.addAll(this.advisorFactory.getAdvisors(factory)); } } return advisors; } ``` 在org.springframework.aop.aspectj.annotation.AbstractAspectJAdvisorFactory#isAspect方法中,判断给定的类是否是一个切面。它首先检查类是否带有 AspectJ 注解,然后再确认该类不是由 AspectJ 编译器编译的。如果符合这两个条件,则返回 true,表示该类是一个切面;否则返回 false。 ```java /** * 判断给定的类是否是切面。 * @param clazz 要检查的类 * @return 如果类是切面,则返回 true;否则返回 false */ @Override public boolean isAspect(Class clazz) { // 判断类是否带有 AspectJ 注解,并且不是由 AspectJ 编译器编译的 return (hasAspectAnnotation(clazz) && !compiledByAjc(clazz)); } ``` 在`org.springframework.aop.aspectj.annotation.AbstractAspectJAdvisorFactory#hasAspectAnnotation`方法中,检查给定的类是否带有 AspectJ 注解。 ```java /** * 判断给定的类是否带有 AspectJ 注解。 * @param clazz 要检查的类 * @return 如果类带有 AspectJ 注解,则返回 true;否则返回 false */ private boolean hasAspectAnnotation(Class clazz) { // 使用 AnnotationUtils.findAnnotation 方法查找类上的 Aspect 注解 return (AnnotationUtils.findAnnotation(clazz, Aspect.class) != null); } ``` ================================================ FILE: spring-aop/spring-aop-beanFactoryAspectJAdvisorsBuilder/pom.xml ================================================ 4.0.0 com.xcs.spring spring-aop 0.0.1-SNAPSHOT spring-aop-beanFactoryAspectJAdvisorsBuilder 11 11 UTF-8 ================================================ FILE: spring-aop/spring-aop-beanFactoryAspectJAdvisorsBuilder/src/main/java/com/xcs/spring/AppConfig.java ================================================ package com.xcs.spring; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; @Configuration @EnableAspectJAutoProxy @ComponentScan public class AppConfig { } ================================================ FILE: spring-aop/spring-aop-beanFactoryAspectJAdvisorsBuilder/src/main/java/com/xcs/spring/BeanFactoryAspectJAdvisorsBuilderDemo.java ================================================ package com.xcs.spring; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class BeanFactoryAspectJAdvisorsBuilderDemo { public static void main(String[] args) { // 创建基于注解的应用上下文 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); // 从应用上下文中获取MyService bean MyService myService = context.getBean(MyService.class); // 调用MyService的方法 myService.foo(); } } ================================================ FILE: spring-aop/spring-aop-beanFactoryAspectJAdvisorsBuilder/src/main/java/com/xcs/spring/MyAspect.java ================================================ package com.xcs.spring; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Aspect @Component class MyAspect { @Before("execution(* com.xcs.spring.MyService.foo(..))") public void before() { System.out.println("Before method execution"); } } ================================================ FILE: spring-aop/spring-aop-beanFactoryAspectJAdvisorsBuilder/src/main/java/com/xcs/spring/MyService.java ================================================ package com.xcs.spring; import org.springframework.stereotype.Service; @Service public class MyService { public void foo() { System.out.println("foo..."); } } ================================================ FILE: spring-aop/spring-aop-cglibProxy/README.md ================================================ ## Cglib动态代理 - [Cglib动态代理](#cglib动态代理) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、最佳实践](#四最佳实践) - [五、源码分析](#五源码分析) - [六、常见问题](#六常见问题) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 Cglib 是一个基于 Java 的开源代码生成库,它通过动态生成字节码的方式实现了对类的动态代理,无需目标类实现接口即可进行代理,常用于 AOP 编程、方法拦截与增强等场景,提供了灵活而高效的代理解决方案。 ### 三、主要功能 1. **动态代理生成** + Cglib 能够在运行时动态生成类的子类,从而实现对目标类的动态代理,无需目标类实现接口。 2. **AOP 支持** + Cglib 是 AOP 编程中常用的工具之一,它可以通过代理技术实现方法拦截和增强,方便实现横切关注点的功能。 3. **字节码操作** + Cglib 允许我们直接操作字节码,从而能够在运行时修改类的结构和行为。 4. **高性能** + 相对于 JDK 标准库中的动态代理,Cglib 生成的代理类性能更高,因为它直接操作字节码,而不是通过反射调用。 5. **无接口代理** + 与 JDK 动态代理不同,Cglib 可以代理那些没有实现接口的类,提供了更广泛的应用场景。 ### 四、最佳实践 使用 Cglib 实现动态代理。它首先创建了一个 Enhancer 对象,然后设置了目标对象的父类和回调拦截器,最后通过 Enhancer 创建了代理对象。这个代理对象可以调用目标对象的方法,并且在方法执行前后执行拦截器中定义的逻辑。 ```java public class CglibProxyDemo { public static void main(String[] args) { // 创建 Enhancer 对象,用于生成代理类 Enhancer enhancer = new Enhancer(); // 设置目标对象的父类 enhancer.setSuperclass(MyServiceImpl.class); // 设置回调拦截器 enhancer.setCallback(new MyMethodInterceptor()); // 创建代理对象 MyService proxyObject = (MyService) enhancer.create(); // 输出代理对象的类名 System.out.println("ProxyObject = " + proxyObject.getClass()); // 调用代理对象的方法 proxyObject.doSomething(); } } ``` 实现了 `MethodInterceptor` 接口的类,用于定义拦截器的行为。在 `intercept` 方法中,它接收被代理对象、目标方法、方法参数以及方法代理对象作为参数,并在目标方法执行前后执行一些逻辑。 ```java public class MyMethodInterceptor implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("Before invoking method: " + method.getName()); Object result = methodProxy.invokeSuper(obj, args); System.out.println("After invoking method: " + method.getName()); return result; } } ``` 定义了一个接口 `MyService`,其中包含一个抽象方法 `doSomething()`。然后,定义了一个实现了 `MyService` 接口的类 `MyServiceImpl`,并实现了 `doSomething()` 方法。 ```java public interface MyService { void doSomething(); } public class MyServiceImpl implements MyService { @Override public void doSomething() { System.out.println("hello world"); } } ``` 运行结果,成功创建了代理对象,并且在调用 `doSomething()` 方法前后执行了拦截器中定义的逻辑。 ```java ProxyObject = class com.xcs.spring.MyServiceImpl$$EnhancerByCGLIB$$bff4cd04 Before invoking method: doSomething hello world After invoking method: doSomething ``` ### 五、源码分析 这段代码是通过反编译工具(arthas)得到的 Cglib 生成的代理类的源代码。这个代理类继承了目标类 `MyServiceImpl`,并实现了 `Factory` 接口。它重写了目标类的方法,并添加了拦截器逻辑。在每个方法的实现中,先尝试获取拦截器对象,然后通过拦截器的 `intercept` 方法执行拦截逻辑,最终调用目标方法。除此之外,它还包含了一些静态方法和静态字段,用于初始化和支持代理类的其他操作。 ```java package com.xcs.spring; import com.xcs.spring.MyServiceImpl; import java.lang.reflect.Method; import org.springframework.cglib.core.ReflectUtils; import org.springframework.cglib.core.Signature; import org.springframework.cglib.proxy.Callback; import org.springframework.cglib.proxy.Factory; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; public class MyServiceImpl$$EnhancerByCGLIB$$bff4cd04 extends MyServiceImpl implements Factory { private boolean CGLIB$BOUND; public static Object CGLIB$FACTORY_DATA; private static final ThreadLocal CGLIB$THREAD_CALLBACKS; private static final Callback[] CGLIB$STATIC_CALLBACKS; private MethodInterceptor CGLIB$CALLBACK_0; private static Object CGLIB$CALLBACK_FILTER; private static final Method CGLIB$doSomething$0$Method; private static final MethodProxy CGLIB$doSomething$0$Proxy; private static final Object[] CGLIB$emptyArgs; private static final Method CGLIB$equals$1$Method; private static final MethodProxy CGLIB$equals$1$Proxy; private static final Method CGLIB$toString$2$Method; private static final MethodProxy CGLIB$toString$2$Proxy; private static final Method CGLIB$hashCode$3$Method; private static final MethodProxy CGLIB$hashCode$3$Proxy; private static final Method CGLIB$clone$4$Method; private static final MethodProxy CGLIB$clone$4$Proxy; static void CGLIB$STATICHOOK1() { CGLIB$THREAD_CALLBACKS = new ThreadLocal(); CGLIB$emptyArgs = new Object[0]; Class clazz = Class.forName("com.xcs.spring.MyServiceImpl$$EnhancerByCGLIB$$bff4cd04"); Class clazz2 = Class.forName("java.lang.Object"); Method[] methodArray = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, clazz2.getDeclaredMethods()); CGLIB$equals$1$Method = methodArray[0]; CGLIB$equals$1$Proxy = MethodProxy.create(clazz2, clazz, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1"); CGLIB$toString$2$Method = methodArray[1]; CGLIB$toString$2$Proxy = MethodProxy.create(clazz2, clazz, "()Ljava/lang/String;", "toString", "CGLIB$toString$2"); CGLIB$hashCode$3$Method = methodArray[2]; CGLIB$hashCode$3$Proxy = MethodProxy.create(clazz2, clazz, "()I", "hashCode", "CGLIB$hashCode$3"); CGLIB$clone$4$Method = methodArray[3]; CGLIB$clone$4$Proxy = MethodProxy.create(clazz2, clazz, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4"); clazz2 = Class.forName("com.xcs.spring.MyServiceImpl"); CGLIB$doSomething$0$Method = ReflectUtils.findMethods(new String[]{"doSomething", "()V"}, clazz2.getDeclaredMethods())[0]; CGLIB$doSomething$0$Proxy = MethodProxy.create(clazz2, clazz, "()V", "doSomething", "CGLIB$doSomething$0"); } final void CGLIB$doSomething$0() { super.doSomething(); } public final void doSomething() { MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0; if (methodInterceptor == null) { MyServiceImpl$$EnhancerByCGLIB$$bff4cd04.CGLIB$BIND_CALLBACKS(this); methodInterceptor = this.CGLIB$CALLBACK_0; } if (methodInterceptor != null) { Object object = methodInterceptor.intercept(this, CGLIB$doSomething$0$Method, CGLIB$emptyArgs, CGLIB$doSomething$0$Proxy); return; } super.doSomething(); } // ... [代码部分省略以简化] // ... [toString代码部分省略以简化] // ... [hashCode代码部分省略以简化] // ... [equals代码部分省略以简化] // ... [clone代码部分省略以简化] // ... [代码部分省略以简化] static { MyServiceImpl$$EnhancerByCGLIB$$bff4cd04.CGLIB$STATICHOOK1(); } } ``` ### 六、常见问题 1. **Final 类和方法无法代理** + 由于 Cglib 是通过生成目标类的子类来实现代理,所以无法代理被 final 修饰的类和方法。如果目标类或方法被标记为 final,则无法使用 Cglib 进行动态代理。 2. **构造函数无法被代理** + Cglib 无法代理目标类的构造函数。因为构造函数的调用是在对象创建阶段完成的,而代理对象在目标对象创建后才生成,因此无法代理构造函数。 3. **内部类无法被代理** + Cglib 无法代理目标类中的内部类。这是因为 Cglib 是通过生成目标类的子类来实现代理,而内部类无法被继承。 ================================================ FILE: spring-aop/spring-aop-cglibProxy/pom.xml ================================================ com.xcs.spring spring-aop 0.0.1-SNAPSHOT 4.0.0 spring-aop-cglibProxy ================================================ FILE: spring-aop/spring-aop-cglibProxy/src/main/java/com/xcs/spring/CglibProxyDemo.java ================================================ package com.xcs.spring; import org.springframework.cglib.proxy.Enhancer; public class CglibProxyDemo { public static void main(String[] args) { // 创建 Enhancer 对象,用于生成代理类 Enhancer enhancer = new Enhancer(); // 设置目标对象的父类 enhancer.setSuperclass(MyServiceImpl.class); // 设置回调拦截器 enhancer.setCallback(new MyMethodInterceptor()); // 创建代理对象 MyService proxyObject = (MyService) enhancer.create(); // 输出代理对象的类名 System.out.println("ProxyObject = " + proxyObject.getClass()); // 调用代理对象的方法 proxyObject.doSomething(); } } ================================================ FILE: spring-aop/spring-aop-cglibProxy/src/main/java/com/xcs/spring/MyMethodInterceptor.java ================================================ package com.xcs.spring; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class MyMethodInterceptor implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("Before invoking method: " + method.getName()); Object result = methodProxy.invokeSuper(obj, args); System.out.println("After invoking method: " + method.getName()); return result; } } ================================================ FILE: spring-aop/spring-aop-cglibProxy/src/main/java/com/xcs/spring/MyService.java ================================================ package com.xcs.spring; public interface MyService { void doSomething(); } ================================================ FILE: spring-aop/spring-aop-cglibProxy/src/main/java/com/xcs/spring/MyServiceImpl.java ================================================ package com.xcs.spring; public class MyServiceImpl implements MyService { @Override public void doSomething() { System.out.println("hello world"); } } ================================================ FILE: spring-aop/spring-aop-classFilter/README.md ================================================ ## ClassFilter - [ClassFilter](#classfilter) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、接口源码](#四接口源码) - [五、主要实现](#五主要实现) - [六、类关系图](#六类关系图) - [七、最佳实践](#七最佳实践) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 `ClassFilter` 接口是 Spring AOP 框架中的一个关键组件,用于定义切面(Aspect)应该拦截哪些类的规则。允许我们根据具体的条件来判断传入的类是否应该被拦截。通过实现该接口,可以灵活地定义过滤器,以匹配特定的类或者类的集合,从而精确地控制切面的作用范围。 ### 三、主要功能 1. **指定切面拦截的类** + 允许我们定义规则,确定哪些类应该被应用切面。通过实现 `matches(Class clazz)` 方法,可以根据特定的条件来判断传入的类是否应该被拦截。 2. **过滤器功能** + 作为过滤器模式的一种应用,`ClassFilter` 接口允许我们定义过滤器,以匹配特定的类或者类的集合。这样可以灵活地控制切面的作用范围,只针对符合条件的类应用切面逻辑。 3. **精确定义切面作用范围** + 通过 `ClassFilter` 接口,可以实现非常灵活的切面选择逻辑,例如只拦截某个特定包下的类、只拦截实现了某个接口的类等,从而精确地定义切面的作用范围。 ### 四、接口源码 `ClassFilter` 接口是一个过滤器,用于限制某个切点或引入的匹配范围到一组指定的目标类。通过实现 `matches(Class clazz)` 方法,可以确定切面是否应该应用到给定的目标类上。 ```java /** * 过滤器,用于限制一个切点或引入的匹配到一组给定的目标类。 * *

可以作为 {@link Pointcut} 的一部分或者用于整个 {@link IntroductionAdvisor} 的定位。 * *

这个接口的具体实现通常应该提供 {@link Object#equals(Object)} 和 {@link Object#hashCode()} 的适当实现, * 以便允许在缓存场景中使用过滤器,例如,在 CGLIB 生成的代理中。 * * @author Rod Johnson * @see Pointcut * @see MethodMatcher */ @FunctionalInterface public interface ClassFilter { /** * 是否应该应用到给定的接口或目标类? * @param clazz 候选目标类 * @return 是否应该将通知应用到给定的目标类 */ boolean matches(Class clazz); /** * 匹配所有类的 ClassFilter 的规范实例。 */ ClassFilter TRUE = TrueClassFilter.INSTANCE; } ``` ### 五、主要实现 1. **AnnotationClassFilter** - 根据注解匹配类的过滤器,用于选取带有指定注解的类。 2. **TypePatternClassFilter** + 根据类型模式匹配类的过滤器,用于匹配满足指定类型模式的类。 3. **RootClassFilter** + 匹配指定类的根类的过滤器。 4. **AspectJExpressionPointcut** + 主要用于基于 AspectJ 表达式匹配目标类。 ### 六、类关系图 ~~~mermaid classDiagram direction BT class AnnotationClassFilter class AspectJExpressionPointcut class ClassFilter { <> } class RootClassFilter class TypePatternClassFilter AnnotationClassFilter ..> ClassFilter AspectJExpressionPointcut ..> ClassFilter RootClassFilter ..> ClassFilter TypePatternClassFilter ..> ClassFilter ~~~ ### 七、最佳实践 使用不同类型的类过滤器(AnnotationClassFilter、TypePatternClassFilter、RootClassFilter)以及基于 AspectJ 表达式的切点(AspectJExpressionPointcut)来匹配目标类,并输出匹配结果。 ```java public class ClassFilterDemo { public static void main(String[] args) { // 创建 AnnotationClassFilter 实例,匹配带有 MyAnnotation 注解的类 ClassFilter annotationClassFilter = new AnnotationClassFilter(MyClassAnnotation.class); System.out.println("annotationClassFilter matches =" + annotationClassFilter.matches(MyService.class)); // 创建 TypePatternClassFilter 实例,匹配指定类名的类 ClassFilter typePatternClassFilter = new TypePatternClassFilter("com.xcs.spring.MyService"); System.out.println("typePatternClassFilter matches =" + typePatternClassFilter.matches(MyService.class)); // 创建 RootClassFilter 实例,匹配指定类的根类 ClassFilter rootClassFilter = new RootClassFilter(MyService.class); System.out.println("rootClassFilter matches = " + rootClassFilter.matches(MySubService.class)); // 创建 AspectJExpressionPointcut 实例,根据 AspectJ 表达式匹配类和方法 AspectJExpressionPointcut aspectJExpressionPointcut = new AspectJExpressionPointcut(); aspectJExpressionPointcut.setExpression("execution(* com.xcs.spring.MyService.*(..))"); System.out.println("aspectJExpressionPointcut matches = " + aspectJExpressionPointcut.matches(MyService.class)); } } ``` `MyService` 类被 `@MyClassAnnotation` 注解修饰。 ```java @MyClassAnnotation public class MyService { } ``` `MyClassAnnotation` 注解,应用于类级别的元素。 ```java @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface MyClassAnnotation { } ``` 运行结果,四种不同类型的类过滤器都成功地匹配了相应的目标类。 ```java annotationClassFilter matches =true typePatternClassFilter matches =true rootClassFilter matches = true aspectJExpressionPointcut matches = true ``` ================================================ FILE: spring-aop/spring-aop-classFilter/pom.xml ================================================ com.xcs.spring spring-aop 0.0.1-SNAPSHOT 4.0.0 spring-aop-classFilter ================================================ FILE: spring-aop/spring-aop-classFilter/src/main/java/com/xcs/spring/ClassFilterDemo.java ================================================ package com.xcs.spring; import org.springframework.aop.ClassFilter; import org.springframework.aop.aspectj.AspectJExpressionPointcut; import org.springframework.aop.aspectj.TypePatternClassFilter; import org.springframework.aop.support.RootClassFilter; import org.springframework.aop.support.annotation.AnnotationClassFilter; public class ClassFilterDemo { public static void main(String[] args) { // 创建 AnnotationClassFilter 实例,匹配带有 MyAnnotation 注解的类 ClassFilter annotationClassFilter = new AnnotationClassFilter(MyClassAnnotation.class); System.out.println("annotationClassFilter matches =" + annotationClassFilter.matches(MyService.class)); // 创建 TypePatternClassFilter 实例,匹配指定类名的类 ClassFilter typePatternClassFilter = new TypePatternClassFilter("com.xcs.spring.MyService"); System.out.println("typePatternClassFilter matches =" + typePatternClassFilter.matches(MyService.class)); // 创建 RootClassFilter 实例,匹配指定类的根类 ClassFilter rootClassFilter = new RootClassFilter(MyService.class); System.out.println("rootClassFilter matches = " + rootClassFilter.matches(MySubService.class)); // 创建 AspectJExpressionPointcut 实例,根据 AspectJ 表达式匹配类和方法 AspectJExpressionPointcut aspectJExpressionPointcut = new AspectJExpressionPointcut(); aspectJExpressionPointcut.setExpression("execution(* com.xcs.spring.MyService.*(..))"); System.out.println("aspectJExpressionPointcut matches = " + aspectJExpressionPointcut.matches(MyService.class)); } } ================================================ FILE: spring-aop/spring-aop-classFilter/src/main/java/com/xcs/spring/MyClassAnnotation.java ================================================ package com.xcs.spring; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface MyClassAnnotation { } ================================================ FILE: spring-aop/spring-aop-classFilter/src/main/java/com/xcs/spring/MyService.java ================================================ package com.xcs.spring; @MyClassAnnotation public class MyService { } ================================================ FILE: spring-aop/spring-aop-classFilter/src/main/java/com/xcs/spring/MySubService.java ================================================ package com.xcs.spring; public class MySubService extends MyService{ } ================================================ FILE: spring-aop/spring-aop-enableAspectJAutoProxy/README.md ================================================ ## @EnableAspectJAutoProxy - [@EnableAspectJAutoProxy](#enableaspectjautoproxy) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、注解源码](#四注解源码) - [五、最佳实践](#五最佳实践) - [六、源码分析](#六源码分析) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 `EnableAspectJAutoProxy`注解是Spring框架中的一个注解,用于启用AspectJ自动代理功能,它能够自动将AspectJ切面与Spring的IoC容器集成,无需显式配置大量AOP相关的内容,从而简化AOP的使用和配置。 ### 三、主要功能 1. **启用AspectJ自动代理功能** + 通过在配置类上添加该注解,Spring会自动启用AspectJ自动代理功能,无需显式配置大量AOP相关内容。 2. **自动创建代理对象** + Spring会自动创建代理对象来应用切面,将切面逻辑与目标对象进行结合。 3. **集成AspectJ切面与Spring容器** + 能够方便地将AspectJ切面与Spring的IoC容器集成,实现横切关注点的模块化管理。 4. **简化AOP配置** + 减少了手动配置AOP所需的繁琐步骤,提高了开发效率。 5. **自动扫描切面** + Spring会自动扫描应用中的AspectJ切面,并将其应用到相应的目标对象中,无需手动配置切面。 ### 四、注解源码 `EnableAspectJAutoProxy`注解,用于启用支持处理使用AspectJ的`@Aspect`注解标记的组件,在Spring中类似于XML配置中的``功能。它允许通过配置类轻松集成AspectJ切面,并控制代理类型和代理的可见性。 ```java /** * 启用支持处理使用AspectJ的{@code @Aspect}注解标记的组件, * 类似于Spring的{@code } XML元素中的功能。 * 应用在如下的@{@link Configuration}类上: * *

 * @Configuration
 * @EnableAspectJAutoProxy
 * public class AppConfig {
 *
 *     @Bean
 *     public FooService fooService() {
 *         return new FooService();
 *     }
 *
 *     @Bean
 *     public MyAspect myAspect() {
 *         return new MyAspect();
 *     }
 * }
* * 这里的{@code FooService}是一个典型的POJO组件,{@code MyAspect}是一个 * {@code @Aspect}-风格的切面: * *
 * public class FooService {
 *
 *     // 各种方法
 * }
* *
 * @Aspect
 * public class MyAspect {
 *
 *     @Before("execution(* FooService+.*(..))")
 *     public void advice() {
 *         // 适当地提供FooService方法的建议
 *     }
 * }
* * 在上述场景中,{@code @EnableAspectJAutoProxy}确保{@code MyAspect} * 将被正确处理,并且{@code FooService}将被代理,混合其中的建议。 * *

用户可以通过{@link #proxyTargetClass()}属性控制为{@code FooService}创建的代理类型。 * 以下示例启用了CGLIB风格的“子类”代理,而不是默认的基于接口的JDK代理方式。 * *

 * @Configuration
 * @EnableAspectJAutoProxy(proxyTargetClass=true)
 * public class AppConfig {
 *     // ...
 * }
* *

注意,{@code @Aspect} bean可以像任何其他组件一样进行组件扫描。 * 只需将切面标记为{@code @Aspect}和{@code @Component}: * *

 * package com.foo;
 *
 * @Component
 * public class FooService { ... }
 *
 * @Aspect
 * @Component
 * public class MyAspect { ... }
* * 然后使用@{@link ComponentScan}注解来同时选择它们: * *
 * @Configuration
 * @ComponentScan("com.foo")
 * @EnableAspectJAutoProxy
 * public class AppConfig {
 *
 *     // 不需要显式的{@code @Bean}定义
 * }
* * 注意:{@code @EnableAspectJAutoProxy}仅适用于其本地应用上下文, * 允许在不同级别选择性地对bean进行代理。 * 如果需要在多个级别应用其行为,例如常见的根Web应用程序上下文和任何单独的{@code DispatcherServlet}应用程序上下文中, * 请在每个单独的上下文中重新声明{@code @EnableAspectJAutoProxy}。 * *

该功能要求类路径上存在{@code aspectjweaver}。 * 虽然{@code spring-aop}一般情况下对该依赖是可选的,但是对于{@code @EnableAspectJAutoProxy}及其基础设施,它是必需的。 * * @author Chris Beams * @author Juergen Hoeller * @since 3.1 * @see org.aspectj.lang.annotation.Aspect */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(AspectJAutoProxyRegistrar.class) public @interface EnableAspectJAutoProxy { /** * 指示是否创建基于子类(CGLIB)的代理,而不是标准的基于Java接口的代理。默认值为{@code false}。 */ boolean proxyTargetClass() default false; /** * 指示代理是否应该被AOP框架公开为一个{@code ThreadLocal},以便通过{@link org.springframework.aop.framework.AopContext}类进行检索。 * 默认情况下关闭,即不保证{@code AopContext}访问将起作用。 * @since 4.3.1 */ boolean exposeProxy() default false; } ``` ### 五、最佳实践 使用`EnableAspectJAutoProxy` 注解和Spring的基于注解的应用上下文来启用AspectJ自动代理功能。在程序中,首先创建了一个基于注解的应用上下文,然后通过该上下文获取了`MyService` bean,并调用了其方法。 ```java public class EnableAspectJAutoProxyDemo { public static void main(String[] args) { // 创建基于注解的应用上下文 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); // 从应用上下文中获取MyService bean MyService myService = context.getBean(MyService.class); // 调用MyService的方法 myService.foo(); } } ``` `AppConfig` 类是一个使用 `@Configuration` 注解标记的配置类,通过 `@EnableAspectJAutoProxy` 开启了 AspectJ 自动代理功能,并通过 `@ComponentScan` 启用了组件扫描,用于自动发现和注册 Spring 组件。 ```java @Configuration @EnableAspectJAutoProxy @ComponentScan public class AppConfig { } ``` `MyService` 类是一个使用 `@Service` 注解标记的服务类,提供了一个名为 `foo()` 的方法,该方法在调用时会打印消息 "foo..."。 ```java @Service public class MyService { public void foo() { System.out.println("foo..."); } } ``` `MyAspect`是一个使用了`@Aspect`注解的Java类,表示它是一个切面。在这个类中,定义了一个名为`advice`的方法,并使用了`@Before`注解来指定在目标方法执行之前执行的通知。 ```java @Aspect @Component public class MyAspect { @Before("execution(* com.xcs.spring.MyService+.*(..))") public void before() { System.out.println("Before method execution"); } } ``` 运行结果,调用 `MyService` 类的 `foo()` 方法之前,成功地执行了一个切面通知,输出了 "Before method execution" 的消息,然后执行了 `foo()` 方法,输出了 "foo..." 的消息。 ```java Before method execution foo... ``` ### 六、源码分析 在`org.springframework.context.annotation.AspectJAutoProxyRegistrar#registerBeanDefinitions`方法中,首先注册了AspectJ注解自动代理创建器,然后获取了`@EnableAspectJAutoProxy`注解的属性。如果`@EnableAspectJAutoProxy`注解中指定了`proxyTargetClass`属性为true,则强制使用CGLIB代理;如果指定了`exposeProxy`属性为true,则强制代理对象暴露为ThreadLocal。 ```java @Override public void registerBeanDefinitions( AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { // 1、注册AspectJ注解自动代理创建器 AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry); // 获取@EnableAspectJAutoProxy注解的属性 AnnotationAttributes enableAspectJAutoProxy = AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class); if (enableAspectJAutoProxy != null) { ///2.如果@EnableAspectJAutoProxy注解指定了proxyTargetClass属性为true,则强制使用CGLIB代理 if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) { AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); } // 3.如果@EnableAspectJAutoProxy注解指定了exposeProxy属性为true,则强制代理对象暴露为ThreadLocal if (enableAspectJAutoProxy.getBoolean("exposeProxy")) { AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry); } } } ``` 在`org.springframework.aop.config.AopConfigUtils#registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry)`方法中,注册AspectJ注解自动代理创建器。它提供了一个重载方法,允许传入一个额外的参数,但这里调用的是没有额外参数的版本。在方法中,它会根据给定的`BeanDefinitionRegistry`对象来注册AspectJ注解自动代理创建器,并返回相应的BeanDefinition。 ```java @Nullable public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) { return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, null); } ``` 在`org.springframework.aop.config.AopConfigUtils#registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry,source)`方法中,注册AspectJ注解自动代理创建器,并且可以指定源对象。在方法中,它调用了一个辅助方法`registerOrEscalateApcAsRequired`,该方法会根据需要注册或升级AspectJ注解自动代理创建器,并返回相应的BeanDefinition。 [AnnotationAwareAspectJAutoProxyCreator源码分析](../spring-aop-annotationAwareAspectJAutoProxyCreator/README.md) ```java @Nullable public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary( BeanDefinitionRegistry registry, @Nullable Object source) { return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source); } ``` 在`org.springframework.aop.config.AopConfigUtils#registerOrEscalateApcAsRequired`方法中,首先检查给定的`BeanDefinitionRegistry`中是否已经存在了自动代理创建器的定义。如果存在,则比较现有自动代理创建器与指定类的优先级,如果指定类的优先级更高,则进行升级操作;如果不存在,则创建一个新的自动代理创建器的定义,并将其注册到给定的`BeanDefinitionRegistry`中。 ```java @Nullable private static BeanDefinition registerOrEscalateApcAsRequired( Class cls, BeanDefinitionRegistry registry, @Nullable Object source) { // 检查BeanDefinitionRegistry对象是否为null Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); // 如果已经存在相同名称的自动代理创建器,则进行升级操作 if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) { // 获取已存在的自动代理创建器的BeanDefinition BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME); // 比较已存在的自动代理创建器与指定类的优先级 if (!cls.getName().equals(apcDefinition.getBeanClassName())) { int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName()); int requiredPriority = findPriorityForClass(cls); // 如果指定类的优先级高于已存在的自动代理创建器,则进行升级 if (currentPriority < requiredPriority) { apcDefinition.setBeanClassName(cls.getName()); } } return null; } // 如果不存在相同名称的自动代理创建器,则进行注册操作 // 创建新的自动代理创建器的BeanDefinition RootBeanDefinition beanDefinition = new RootBeanDefinition(cls); beanDefinition.setSource(source); beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE); beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); // 注册新的自动代理创建器的BeanDefinition registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition); return beanDefinition; } ``` 在`org.springframework.aop.config.AopConfigUtils#forceAutoProxyCreatorToUseClassProxying`方法中,强制自动代理创建器使用基于类的代理。它首先检查给定的`BeanDefinitionRegistry`中是否已经存在了自动代理创建器的定义。如果存在,则获取该定义并设置其`proxyTargetClass`属性为`true`,表示使用基于类的代理。 ```java public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) { // 检查是否存在自动代理创建器的BeanDefinition if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) { // 获取自动代理创建器的BeanDefinition BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME); // 设置proxyTargetClass属性为true,表示使用基于类的代理 definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE); } } ``` 在`org.springframework.aop.config.AopConfigUtils#forceAutoProxyCreatorToExposeProxy`方法中,强制自动代理创建器暴露代理对象。它首先检查给定的`BeanDefinitionRegistry`中是否已经存在了自动代理创建器的定义。如果存在,则获取该定义并设置其`exposeProxy`属性为`true`,表示暴露代理对象。 ```java public static void forceAutoProxyCreatorToExposeProxy(BeanDefinitionRegistry registry) { // 检查是否存在自动代理创建器的BeanDefinition if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) { // 获取自动代理创建器的BeanDefinition BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME); // 设置exposeProxy属性为true,表示暴露代理对象 definition.getPropertyValues().add("exposeProxy", Boolean.TRUE); } } ``` ================================================ FILE: spring-aop/spring-aop-enableAspectJAutoProxy/pom.xml ================================================ com.xcs.spring spring-aop 0.0.1-SNAPSHOT 4.0.0 spring-aop-enableAspectJAutoProxy ================================================ FILE: spring-aop/spring-aop-enableAspectJAutoProxy/src/main/java/com/xcs/spring/AppConfig.java ================================================ package com.xcs.spring; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; @Configuration @EnableAspectJAutoProxy @ComponentScan public class AppConfig { } ================================================ FILE: spring-aop/spring-aop-enableAspectJAutoProxy/src/main/java/com/xcs/spring/EnableAspectJAutoProxyDemo.java ================================================ package com.xcs.spring; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class EnableAspectJAutoProxyDemo { public static void main(String[] args) { // 创建基于注解的应用上下文 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); // 从应用上下文中获取MyService bean MyService myService = context.getBean(MyService.class); // 调用MyService的方法 myService.foo(); } } ================================================ FILE: spring-aop/spring-aop-enableAspectJAutoProxy/src/main/java/com/xcs/spring/MyAspect.java ================================================ package com.xcs.spring; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Aspect @Component public class MyAspect { @Before("execution(* com.xcs.spring.MyService+.*(..))") public void before() { System.out.println("Before method execution"); } } ================================================ FILE: spring-aop/spring-aop-enableAspectJAutoProxy/src/main/java/com/xcs/spring/MyService.java ================================================ package com.xcs.spring; import org.springframework.stereotype.Service; @Service public class MyService { public void foo() { System.out.println("foo..."); } } ================================================ FILE: spring-aop/spring-aop-enableLoadTimeWeaving/README.md ================================================ ## @EnableLoadTimeWeaving - [@EnableLoadTimeWeaving](#enableloadtimeweaving) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、注解源码](#四注解源码) - [五、最佳实践](#五最佳实践) - [六、源码分析](#六源码分析) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 `@EnableLoadTimeWeaving` 是 Spring 框架提供的注解,用于启用加载时编织(Load Time Weaving,LTW),允许在类加载过程中动态织入切面逻辑,以实现诸如日志记录、性能监控等横切关注点的功能。 ### 三、主要功能 1. **启用加载时编织(LTW)** + 允许在类加载的过程中动态地织入切面逻辑,而无需修改源代码或者使用特定的编译器。 2. **支持横切关注点的实现** + 通过加载时编织,可以将横切关注点与应用的核心业务逻辑分离,提高代码的模块化和可维护性。 3. **灵活性和可配置性** + 可以通过 AspectJ 提供的丰富语法和功能,灵活地定义切点和切面逻辑,以满足各种复杂的应用场景。 4. **不依赖源代码** + 加载时编织不依赖于源代码的修改或特殊的编译器,因此可以在已有的应用中轻松地引入切面逻辑,而无需对现有代码进行重构。 ### 四、注解源码 注解 `@EnableLoadTimeWeaving`,用于激活 Spring 应用上下文中的加载时编织(Load Time Weaving)。通过该注解,可以方便地配置加载时编织,类似于 Spring XML 配置中的 `` 元素。同时,还可以通过 `aspectjWeaving()` 属性控制是否启用基于 AspectJ 的编织,提供了灵活的配置选项。 ```java /** * 激活一个 Spring {@link LoadTimeWeaver} 用于该应用程序上下文,可作为一个名为 "loadTimeWeaver" 的 bean 使用, * 类似于 Spring XML 中的 {@code } 元素。 * *

要在 @{@link org.springframework.context.annotation.Configuration Configuration} 类上使用; * 最简单的示例如下 * *

 * @Configuration
 * @EnableLoadTimeWeaving
 * public class AppConfig {
 *
 *     // 应用特定的 @Bean 定义...
 * }
* * 上面的示例等价于以下的 Spring XML 配置 * *
 * <beans>
 *
 *     <context:load-time-weaver/>
 *
 *     <!-- 应用特定的 <bean> 定义 -->
 *
 * </beans>
 * 
* *

{@code LoadTimeWeaverAware} 接口

* 任何实现 {@link org.springframework.context.weaving.LoadTimeWeaverAware LoadTimeWeaverAware} 接口的 bean * 都将自动接收到 {@code LoadTimeWeaver} 引用;例如,Spring 的 JPA 启动支持。 * *

定制 {@code LoadTimeWeaver}

* 默认的 weaver 将自动确定参见 {@link DefaultContextLoadTimeWeaver}。 * *

要定制使用的 weaver,{@code @Configuration} 类可以实现 {@link LoadTimeWeavingConfigurer} 接口,并通过 * {@code #getLoadTimeWeaver} 方法返回一个自定义的 {@code LoadTimeWeaver} 实例 * *

 * @Configuration
 * @EnableLoadTimeWeaving
 * public class AppConfig implements LoadTimeWeavingConfigurer {
 *
 *     @Override
 *     public LoadTimeWeaver getLoadTimeWeaver() {
 *         MyLoadTimeWeaver ltw = new MyLoadTimeWeaver();
 *         ltw.addClassTransformer(myClassFileTransformer);
 *         // ...
 *         return ltw;
 *     }
 * }
* * 上面的示例可与以下 Spring XML 配置进行比较 * *
 * <beans>
 *
 *     <context:load-time-weaver weaverClass="com.acme.MyLoadTimeWeaver"/>
 *
 * </beans>
 * 
* * 代码示例与 XML 示例的区别在于它实际上实例化了 {@code MyLoadTimeWeaver} 类型,这意味着它还可以配置实例, * 例如调用 {@code #addClassTransformer} 方法。这展示了基于代码的配置方法通过直接编程访问更加灵活。 * *

启用基于 AspectJ 的编织

* 可通过 {@link #aspectjWeaving()} 属性启用 AspectJ 加载时编织,这将导致通过 {@link LoadTimeWeaver#addTransformer} * 注册 {@linkplain org.aspectj.weaver.loadtime.ClassPreProcessorAgentAdapter AspectJ 类转换器}。如果类路径中存在 * "META-INF/aop.xml" 资源,则默认情况下将激活 AspectJ 编织。示例 * *
 * @Configuration
 * @EnableLoadTimeWeaving(aspectjWeaving=ENABLED)
 * public class AppConfig {
 * }
* * 上面的示例可与以下 Spring XML 配置进行比较 * *
 * <beans>
 *
 *     <context:load-time-weaver aspectj-weaving="on"/>
 *
 * </beans>
 * 
* * 这两个示例是等价的,但有一个重要的例外在 XML 的情况下,当 {@code aspectj-weaving} 是 "on" 时, * {@code } 的功能将自动启用。在使用 {@code @EnableLoadTimeWeaving(aspectjWeaving=ENABLED)} * 时,这种情况不会发生。相反,您必须显式添加 {@code @EnableSpringConfigured}(包含在 {@code spring-aspects} 模块中)。 * * @author Chris Beams * @since 3.1 * @see LoadTimeWeaver * @see DefaultContextLoadTimeWeaver * @see org.aspectj.weaver.loadtime.ClassPreProcessorAgentAdapter */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(LoadTimeWeavingConfiguration.class) public @interface EnableLoadTimeWeaving { /** * 是否启用 AspectJ 编织。 */ AspectJWeaving aspectjWeaving() default AspectJWeaving.AUTODETECT; /** * AspectJ 编织启用选项。 */ enum AspectJWeaving { /** * 启用基于 Spring 的 AspectJ 加载时编织。 */ ENABLED, /** * 关闭基于 Spring 的 AspectJ 加载时编织(即使类路径上存在 "META-INF/aop.xml" 资源)。 */ DISABLED, /** * 如果类路径上存在 "META-INF/aop.xml" 资源,则启用 AspectJ 加载时编织。 * 如果没有此类资源,则关闭 AspectJ 加载时编织。 */ AUTODETECT; } } ``` ### 五、最佳实践 使用加载时编织(Load Time Weaving)功能。首先,它创建了一个基于注解的 Spring 应用程序上下文,并通过 `AppConfig` 类配置了应用程序的相关组件。创建了一个 `MyService` 的普通实例,并调用了其 `foo` 方法。 ```java public class EnableLoadTimeWeavingDemo { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); MyService myService = new MyService(); myService.foo(); context.close(); } } ``` 通过 `@Configuration` 注解表明这是一个配置类,而 `@EnableLoadTimeWeaving` 注解则启用了加载时编织功能。 ```java @Configuration @EnableLoadTimeWeaving public class AppConfig { } ``` 定义了一个切面类 `MyLTWAspect`,用于实现加载时编织(Load Time Weaving)功能。通过 `@Aspect` 注解标记该类为一个切面,并在其中定义了一个环绕通知方法 `around` ,用于在目标方法调用前后执行特定逻辑。在该方法中,首先输出了目标方法的名称,然后调用了原始方法,并输出了方法返回值。同时,通过 `@Pointcut` 注解定义了一个切点 `ltwPointcut()`,指定了需要被切入的目标方法,这里是 `com.xcs.spring.MyService` 类中的所有公共方法。 ```java @Aspect public class MyLTWAspect { @Around("ltwPointcut()") public Object around(ProceedingJoinPoint pjp) throws Throwable { // 在方法调用之前执行的逻辑 System.out.println("Before Method " + pjp.getSignature().getName()); // 调用原始方法 Object result = pjp.proceed(); // 在方法调用之后执行的逻辑 System.out.println("After Method " + pjp.getSignature().getName()); return result; } @Pointcut("execution(public * com.xcs.spring.MyService.*(..))") public void ltwPointcut() { } } ``` 定义了加载时编织(Load Time Weaving)的规则和切面配置。在 `` 元素中指定了仅对应用程序特定包中的类进行编织,这里是 `com.xcs.spring` 包及其子包下的所有类。然后,在 `` 元素中指定了要编织的切面,即 `com.xcs.spring.MyLTWAspect`。 ```java ``` `MyService` 类定义了一个简单的方法 `foo()`。 ```java public class MyService { public void foo() { System.out.println("foo..."); } } ``` 这是启动参数,使用AspectJ Weaver和Spring Instrumentation实现加载时编织。它确保了在应用程序启动时启用了加载时编织,使AspectJ切面能够拦截和处理方法调用。 > 使用自定义的jar包存放位置(如aspectjweaver-1.9.7.jar,spring-instrument-5.3.10.jar)时,注意确保在引用这些jar包时路径替换的正确性。在启动参数或配置文件中指定的路径应该与实际jar包存放位置一致,以避免加载时编织或其他功能无法正常工作。 ```shell java -javaagent:D:\tools\repository\org\aspectj\aspectjweaver\1.9.7\aspectjweaver-1.9.7.jar -javaagent:D:\tools\repository\org\springframework\spring-instrument\5.3.10\spring-instrument-5.3.10.jar -Dfile.encoding=UTF-8 com.xcs.spring.EnableLoadTimeWeavingDemo ``` 运行结果,直接使用 `new` 操作符创建的 `MyService` 对象。在调用 `foo()` 方法时,都会先打印方法被调用的消息,然后执行原始的方法逻辑(打印 "foo..."),这证明切面 `MyLTWAspect` 中定义的逻辑在目标方法调用前后得到了执行,不论是从 Spring 容器中获取的 bean 还是直接创建的对象,都受到了拦截。 ```java Before Method foo foo... After Method foo ``` ### 六、源码分析 `LoadTimeWeavingConfiguration` 类,负责注册一个 `LoadTimeWeaver` bean,用于启用加载时编织(Load-Time Weaving)功能。在应用中使用 `@EnableLoadTimeWeaving` 注解时,这个配置类会被自动导入。它通过检查 `EnableLoadTimeWeaving` 注解的属性来决定是否启用 AspectJ 编织功能,并根据配置创建相应的 `LoadTimeWeaver` 实例。如果用户提供了自定义的 `LoadTimeWeavingConfigurer` 实例,则会使用用户提供的实例;否则,会创建一个默认的 `DefaultContextLoadTimeWeaver` 实例作为 `LoadTimeWeaver`。根据 `EnableLoadTimeWeaving` 注解中的配置,决定是否启用 AspectJ 编织功能,并根据情况调用 `AspectJWeavingEnabler` 中的方法来实现编织。 ```java /** * {@code @Configuration} 类,注册一个 {@link LoadTimeWeaver} bean。 * *

当使用 {@link EnableLoadTimeWeaving} 注解时,这个配置类会自动导入。 * 完整的使用详情请参阅 {@code @EnableLoadTimeWeaving} 的 javadoc。 * *

作者Chris Beams * * @since 3.1 * @see LoadTimeWeavingConfigurer * @see ConfigurableApplicationContext#LOAD_TIME_WEAVER_BEAN_NAME */ @Configuration(proxyBeanMethods = false) @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public class LoadTimeWeavingConfiguration implements ImportAware, BeanClassLoaderAware { @Nullable private AnnotationAttributes enableLTW; @Nullable private LoadTimeWeavingConfigurer ltwConfigurer; @Nullable private ClassLoader beanClassLoader; @Override public void setImportMetadata(AnnotationMetadata importMetadata) { // 获取 @EnableLoadTimeWeaving 注解的属性 this.enableLTW = AnnotationConfigUtils.attributesFor(importMetadata, EnableLoadTimeWeaving.class); if (this.enableLTW == null) { throw new IllegalArgumentException( "@EnableLoadTimeWeaving is not present on importing class " + importMetadata.getClassName()); } } @Autowired(required = false) public void setLoadTimeWeavingConfigurer(LoadTimeWeavingConfigurer ltwConfigurer) { // 设置用户自定义的 LoadTimeWeavingConfigurer 实例 this.ltwConfigurer = ltwConfigurer; } @Override public void setBeanClassLoader(ClassLoader beanClassLoader) { // 设置类加载器 this.beanClassLoader = beanClassLoader; } /** * 注册 LoadTimeWeaver bean。 */ @Bean(name = ConfigurableApplicationContext.LOAD_TIME_WEAVER_BEAN_NAME) @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public LoadTimeWeaver loadTimeWeaver() { Assert.state(this.beanClassLoader != null, "No ClassLoader set"); LoadTimeWeaver loadTimeWeaver = null; if (this.ltwConfigurer != null) { // 用户提供了自定义的 LoadTimeWeaver 实例 loadTimeWeaver = this.ltwConfigurer.getLoadTimeWeaver(); } if (loadTimeWeaver == null) { // 没有提供自定义的 LoadTimeWeaver -> 使用默认的 loadTimeWeaver = new DefaultContextLoadTimeWeaver(this.beanClassLoader); } if (this.enableLTW != null) { // 获取启用 AspectJ 编织的配置 AspectJWeaving aspectJWeaving = this.enableLTW.getEnum("aspectjWeaving"); switch (aspectJWeaving) { case DISABLED: // AspectJ 编织被禁用 -> 什么也不做 break; case AUTODETECT: if (this.beanClassLoader.getResource(AspectJWeavingEnabler.ASPECTJ_AOP_XML_RESOURCE) == null) { // 类路径上没有 aop.xml -> 视为 'disabled' break; } // 类路径上有 aop.xml -> 启用编织 AspectJWeavingEnabler.enableAspectJWeaving(loadTimeWeaver, this.beanClassLoader); break; case ENABLED: // 启用 AspectJ 编织 AspectJWeavingEnabler.enableAspectJWeaving(loadTimeWeaver, this.beanClassLoader); break; } } return loadTimeWeaver; } } ``` `AspectJWeavingEnabler` 的后处理器,它实现了多个接口,包括 `BeanFactoryPostProcessor`、`BeanClassLoaderAware`、`LoadTimeWeaverAware` 和 `Ordered`。它的主要作用是在 Spring 应用程序上下文中注册 AspectJ 的 `ClassPreProcessorAgentAdapter`,并与默认的 `LoadTimeWeaver` 进行关联。其中,`enableAspectJWeaving` 方法用于启用 AspectJ 编织功能,而 `AspectJClassBypassingClassFileTransformer` 类则实现了一个用于绕过 AspectJ 类处理的装饰器,以避免潜在的链接错误。 ```java /** * 后处理器,将 AspectJ 的 {@link org.aspectj.weaver.loadtime.ClassPreProcessorAgentAdapter} * 注册到 Spring 应用程序上下文的默认 {@link org.springframework.instrument.classloading.LoadTimeWeaver} 中。 * 用于启用 AspectJ 的编织功能。 * *

作者Juergen Hoeller,Ramnivas Laddad * * @since 2.5 */ public class AspectJWeavingEnabler implements BeanFactoryPostProcessor, BeanClassLoaderAware, LoadTimeWeaverAware, Ordered { /** * {@code aop.xml} 资源位置。 */ public static final String ASPECTJ_AOP_XML_RESOURCE = "META-INF/aop.xml"; @Nullable private ClassLoader beanClassLoader; @Nullable private LoadTimeWeaver loadTimeWeaver; @Override public void setBeanClassLoader(ClassLoader classLoader) { this.beanClassLoader = classLoader; } @Override public void setLoadTimeWeaver(LoadTimeWeaver loadTimeWeaver) { this.loadTimeWeaver = loadTimeWeaver; } @Override public int getOrder() { return HIGHEST_PRECEDENCE; } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { // 启用 AspectJ 编织 enableAspectJWeaving(this.loadTimeWeaver, this.beanClassLoader); } /** * 使用给定的 {@link LoadTimeWeaver} 启用 AspectJ 编织。 * * @param weaverToUse 要应用的 LoadTimeWeaver(或 {@code null} 表示使用默认的 weaver) * @param beanClassLoader 如果需要,为其创建默认 weaver 的类加载器 */ public static void enableAspectJWeaving( @Nullable LoadTimeWeaver weaverToUse, @Nullable ClassLoader beanClassLoader) { if (weaverToUse == null) { if (InstrumentationLoadTimeWeaver.isInstrumentationAvailable()) { // 如果可以使用 Instrumentation,创建 InstrumentationLoadTimeWeaver weaverToUse = new InstrumentationLoadTimeWeaver(beanClassLoader); } else { throw new IllegalStateException("No LoadTimeWeaver available"); } } // 添加一个 ClassFileTransformer,用于绕过 AspectJ 类的处理,以避免潜在的 LinkageErrors weaverToUse.addTransformer( new AspectJClassBypassingClassFileTransformer(new ClassPreProcessorAgentAdapter())); } /** * ClassFileTransformer 的装饰器,用于禁止处理 AspectJ 类,以避免潜在的 LinkageErrors。 * * @see org.springframework.context.annotation.LoadTimeWeavingConfiguration */ private static class AspectJClassBypassingClassFileTransformer implements ClassFileTransformer { private final ClassFileTransformer delegate; public AspectJClassBypassingClassFileTransformer(ClassFileTransformer delegate) { this.delegate = delegate; } @Override public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { if (className.startsWith("org.aspectj") || className.startsWith("org/aspectj")) { // 如果是 AspectJ 类,则直接返回原始字节码 return classfileBuffer; } // 否则,调用委托的 ClassFileTransformer 处理字节码 return this.delegate.transform(loader, className, classBeingRedefined, protectionDomain, classfileBuffer); } } } ``` ================================================ FILE: spring-aop/spring-aop-enableLoadTimeWeaving/pom.xml ================================================ com.xcs.spring spring-aop 0.0.1-SNAPSHOT jar 4.0.0 spring-aop-enableLoadTimeWeaving 1.9.7 2.3.5.RELEASE org.springframework spring-instrument ${spring.version} ================================================ FILE: spring-aop/spring-aop-enableLoadTimeWeaving/src/main/java/com/xcs/spring/AppConfig.java ================================================ package com.xcs.spring; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableLoadTimeWeaving; @Configuration @EnableLoadTimeWeaving public class AppConfig { } ================================================ FILE: spring-aop/spring-aop-enableLoadTimeWeaving/src/main/java/com/xcs/spring/EnableLoadTimeWeavingDemo.java ================================================ package com.xcs.spring; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class EnableLoadTimeWeavingDemo { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); MyService myService = new MyService(); myService.foo(); context.close(); } } ================================================ FILE: spring-aop/spring-aop-enableLoadTimeWeaving/src/main/java/com/xcs/spring/MyLTWAspect.java ================================================ package com.xcs.spring; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; @Aspect public class MyLTWAspect { @Around("ltwPointcut()") public Object around(ProceedingJoinPoint pjp) throws Throwable { // 在方法调用之前执行的逻辑 System.out.println("Before Method " + pjp.getSignature().getName()); // 调用原始方法 Object result = pjp.proceed(); // 在方法调用之后执行的逻辑 System.out.println("After Method " + pjp.getSignature().getName()); return result; } @Pointcut("execution(public * com.xcs.spring.MyService.*(..))") public void ltwPointcut() { } } ================================================ FILE: spring-aop/spring-aop-enableLoadTimeWeaving/src/main/java/com/xcs/spring/MyService.java ================================================ package com.xcs.spring; public class MyService { public void foo() { System.out.println("foo..."); } } ================================================ FILE: spring-aop/spring-aop-enableLoadTimeWeaving/src/main/resources/META-INF/aop.xml ================================================ ================================================ FILE: spring-aop/spring-aop-exposeInvocationInterceptor/README.md ================================================ ## ExposeInvocationInterceptor - [ExposeInvocationInterceptor](#exposeinvocationinterceptor) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、类源码](#四类源码) - [五、最佳实践](#五最佳实践) - [六、时序图](#六时序图) - [七、源码分析](#七源码分析) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 `ExposeInvocationInterceptor`是Spring AOP中的一个拦截器类,主要功能是在AOP调用链中暴露当前方法调用的上下文信息,通过暴露`MethodInvocation`对象,使其他拦截器或切面能够访问并处理方法调用的相关信息。 ### 三、主要功能 1. **暴露当前方法调用的上下文信息** + 通过暴露`MethodInvocation`对象,允许其他拦截器或切面访问当前方法调用的相关信息,如目标对象、方法、参数等。 2. **提供`currentInvocation()`方法** + 允许在拦截器或切面中调用`currentInvocation()`方法来获取当前方法调用的`MethodInvocation`对象,从而获取方法调用的上下文信息。 3. **支持AOP调用链的处理** + 作为Spring AOP的一个拦截器,`ExposeInvocationInterceptor`能够被添加到AOP代理链中,确保在调用链的初始阶段就将`MethodInvocation`对象暴露出来,以便后续的拦截器或切面可以使用。 ### 四、类源码 `ExposeInvocationInterceptor`拦截器,其主要目的是将当前的方法调用上下文暴露为线程本地对象。它允许在Spring AOP中获取方法调用的详细信息,例如目标对象、方法、参数等。这个拦截器在AOP链中通常是第一个,用于确保其他拦截器或切面能够访问方法调用的完整上下文。 ```java /** * 拦截器,将当前{@link org.aopalliance.intercept.MethodInvocation}暴露为线程本地对象。 * 仅在必要时使用此拦截器;例如,当切点(例如,AspectJ表达式切点)需要知道完整的调用上下文时。 * *

除非绝对必要,否则不要使用此拦截器。目标对象通常不应知道Spring AOP, * 因为这会创建对Spring API的依赖。目标对象应尽可能是纯POJO。 * *

如果使用,此拦截器通常将是拦截器链中的第一个。 * * @author Rod Johnson * @author Juergen Hoeller */ @SuppressWarnings("serial") public final class ExposeInvocationInterceptor implements MethodInterceptor, PriorityOrdered, Serializable { /** 此类的单例实例。 */ public static final ExposeInvocationInterceptor INSTANCE = new ExposeInvocationInterceptor(); /** * 此类的单例顾问。在使用Spring AOP时,请使用它,因为它可以避免创建新的Advisor来包装该实例。 */ public static final Advisor ADVISOR = new DefaultPointcutAdvisor(INSTANCE) { @Override public String toString() { return ExposeInvocationInterceptor.class.getName() +".ADVISOR"; } }; private static final ThreadLocal invocation = new NamedThreadLocal<>("Current AOP method invocation"); /** * 返回与当前调用关联的AOP Alliance MethodInvocation对象。 * @return 与当前调用关联的调用对象 * @throws IllegalStateException 如果当前没有AOP调用, * 或者ExposeInvocationInterceptor未添加到此拦截器链中 */ public static MethodInvocation currentInvocation() throws IllegalStateException { MethodInvocation mi = invocation.get(); if (mi == null) { throw new IllegalStateException( "No MethodInvocation found: Check that an AOP invocation is in progress and that the " + "ExposeInvocationInterceptor is upfront in the interceptor chain. Specifically, note that " + "advices with order HIGHEST_PRECEDENCE will execute before ExposeInvocationInterceptor! " + "In addition, ExposeInvocationInterceptor and ExposeInvocationInterceptor.currentInvocation() " + "must be invoked from the same thread."); } return mi; } /** * 确保只能创建规范实例。 */ private ExposeInvocationInterceptor() { } @Override @Nullable public Object invoke(MethodInvocation mi) throws Throwable { MethodInvocation oldInvocation = invocation.get(); invocation.set(mi); try { return mi.proceed(); } finally { invocation.set(oldInvocation); } } @Override public int getOrder() { return PriorityOrdered.HIGHEST_PRECEDENCE + 1; } /** * Required to support serialization. Replaces with canonical instance * on deserialization, protecting Singleton pattern. *

Alternative to overriding the {@code equals} method. */ private Object readResolve() { return INSTANCE; } } ``` ### 五、最佳实践 创建了一个基于注解的应用程序上下文,从中获取了一个名为 `MyService` 的 bean,并调用了其 `foo()` 方法。 ```java public class ExposeInvocationInterceptorDemo { public static void main(String[] args) { // 创建一个基于注解的应用程序上下文 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); // 从上下文中获取 MyService MyService myService = context.getBean(MyService.class); // 调用方法 myService.foo(); } } ``` 使用了 `@EnableAspectJAutoProxy` 注解启用了 AspectJ 自动代理功能,并且通过 `@ComponentScan` 注解扫描了包 `com.xcs.spring` 下的组件。 ```java @EnableAspectJAutoProxy @Configuration @ComponentScan("com.xcs.spring") public class AppConfig { } ``` 一个服务类,用于业务逻辑的实现。 ```java @Service public class MyService { public void foo() { System.out.println("foo..."); } } ``` `MyMethodInterceptor`标记为 `@Aspect` 和 `@Component`,表明它是一个切面,并且由 Spring 容器进行管理。其中包含一个名为 `before()` 的方法,使用 `@Before` 注解标记,表示在目标方法执行之前执行。方法内部调用了 `LogUtil.print()` 方法,用于记录日志或执行其他操作。这个切面主要是针对 `com.xcs.spring.MyService` 类中所有公共方法的执行,在方法执行之前添加了特定的逻辑。 ```java @Aspect @Component public class MyMethodInterceptor { @Before("execution(public * com.xcs.spring.MyService.*(..))") public void before() { LogUtil.print(); } } ``` 通过 `ExposeInvocationInterceptor.currentInvocation()` 获取当前方法调用的 `ProxyMethodInvocation` 对象,然后打印了方法名称、参数长度、目标对象以及代理对象的类名。 ```java public class LogUtil { public static void print() { ProxyMethodInvocation methodInvocation = (ProxyMethodInvocation) ExposeInvocationInterceptor.currentInvocation(); System.out.println("Method = " + methodInvocation.getMethod()); System.out.println("Arguments Length = " + methodInvocation.getArguments().length); System.out.println("Target = " + methodInvocation.getThis()); System.out.println("Proxy Class = " + methodInvocation.getProxy().getClass()); } } ``` 运行结果,通过`ExposeInvocationInterceptor.currentInvocation()`获取方法调用上下文实现日志打印。 ```java Method = public void com.xcs.spring.MyService.doSomething() Arguments Length = 0 Target = com.xcs.spring.MyService@49964d75 Proxy Class = class com.xcs.spring.MyService$$EnhancerBySpringCGLIB$$f30643a6 Doing something... ``` ### 六、时序图 ~~~mermaid sequenceDiagram AbstractAutowireCapableBeanFactory->>AbstractAutoProxyCreator: postProcessAfterInitialization() Note over AbstractAutowireCapableBeanFactory,AbstractAutoProxyCreator: 调用后处理方法 AbstractAutoProxyCreator->>AbstractAutoProxyCreator: wrapIfNecessary() Note over AbstractAutoProxyCreator: 调用包装方法 AbstractAutoProxyCreator->>AbstractAdvisorAutoProxyCreator: getAdvicesAndAdvisorsForBean() Note over AbstractAutoProxyCreator,AbstractAdvisorAutoProxyCreator: 获取通知和 Advisors AbstractAdvisorAutoProxyCreator->>AbstractAdvisorAutoProxyCreator: findEligibleAdvisors() Note over AbstractAdvisorAutoProxyCreator: 查找合适的 Advisors AbstractAdvisorAutoProxyCreator->>AspectJAwareAdvisorAutoProxyCreator: extendAdvisors() Note over AbstractAdvisorAutoProxyCreator,AspectJAwareAdvisorAutoProxyCreator: Advisor 的扩展钩子 AspectJAwareAdvisorAutoProxyCreator->>AspectJProxyUtils:makeAdvisorChainAspectJCapableIfNecessary() Note over AspectJAwareAdvisorAutoProxyCreator,AspectJProxyUtils: 添加特殊的拦截器 ~~~ ### 七、源码分析 在`org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator#extendAdvisors`方法中,在开头添加了一个 `ExposeInvocationInterceptor`。 ```java /** * 将{@link ExposeInvocationInterceptor}添加到advice链的开头。 *

在使用AspectJ切点表达式和AspectJ风格的advice时,需要此额外的Advisors。 */ @Override protected void extendAdvisors(List candidateAdvisors) { AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(candidateAdvisors); } ``` 在`org.springframework.aop.aspectj.AspectJProxyUtils#makeAdvisorChainAspectJCapableIfNecessary` 方法中,用于在代理链中添加特殊的拦截器,以确保与包含AspectJ建议的代理链一起正常工作。具体来说,它将 `ExposeInvocationInterceptor` 添加到advisors列表的开头。这样做的目的是为了暴露当前Spring AOP调用(对于某些AspectJ切点匹配是必要的),并使当前AspectJ JoinPoint可用。如果advisors链中不存在AspectJ advisor,则此调用不会产生任何效果。方法返回 `true` 表示成功向建议列表中添加了 `ExposeInvocationInterceptor`,否则返回 `false`。 ```java /** * 如果需要,向包含AspectJ建议的代理链中添加特殊的建议: * 具体来说,在列表的开头添加{@link ExposeInvocationInterceptor}。 *

这将暴露当前Spring AOP调用(对于某些AspectJ切点匹配是必要的), * 并使当前AspectJ JoinPoint可用。如果建议链中没有AspectJ建议,则调用不会产生任何效果。 * @param advisors 可用的建议列表 * @return 如果向列表中添加了{@link ExposeInvocationInterceptor},则返回{@code true},否则返回{@code false} */ public static boolean makeAdvisorChainAspectJCapableIfNecessary(List advisors) { // 不要向空列表添加建议;这可能表示不需要代理 if (!advisors.isEmpty()) { boolean foundAspectJAdvice = false; for (Advisor advisor : advisors) { // 谨慎使用不带保护的Advice,因为这可能会急切地实例化非单例的AspectJ切面... if (isAspectJAdvice(advisor)) { foundAspectJAdvice = true; break; } } // 如果在建议链中找到AspectJ建议,并且没有ExposeInvocationInterceptor.ADVISOR,则添加 if (foundAspectJAdvice && !advisors.contains(ExposeInvocationInterceptor.ADVISOR)) { advisors.add(0, ExposeInvocationInterceptor.ADVISOR); return true; } } return false; } ``` 在`org.springframework.aop.aspectj.AspectJProxyUtils#isAspectJAdvice`方法中,判断给定的 Advisor 是否包含 AspectJ Advice。它检查 Advisor 实例是否属于特定类型或者其 Advice 是否是 AbstractAspectJAdvice 的子类,或者其 Pointcut 是否是 AspectJExpressionPointcut 的实例。 ```java /** * 判断给定的 Advisor 是否包含 AspectJ Advice。 * @param advisor 要检查的 Advisor */ private static boolean isAspectJAdvice(Advisor advisor) { return (advisor instanceof InstantiationModelAwarePointcutAdvisor || advisor.getAdvice() instanceof AbstractAspectJAdvice || (advisor instanceof PointcutAdvisor && ((PointcutAdvisor) advisor).getPointcut() instanceof AspectJExpressionPointcut)); } ``` ================================================ FILE: spring-aop/spring-aop-exposeInvocationInterceptor/pom.xml ================================================ com.xcs.spring spring-aop 0.0.1-SNAPSHOT 4.0.0 spring-aop-exposeInvocationInterceptor ================================================ FILE: spring-aop/spring-aop-exposeInvocationInterceptor/src/main/java/com/xcs/spring/AppConfig.java ================================================ package com.xcs.spring; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; @EnableAspectJAutoProxy @Configuration @ComponentScan("com.xcs.spring") public class AppConfig { } ================================================ FILE: spring-aop/spring-aop-exposeInvocationInterceptor/src/main/java/com/xcs/spring/ExposeInvocationInterceptorDemo.java ================================================ package com.xcs.spring; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class ExposeInvocationInterceptorDemo { public static void main(String[] args) { // 创建一个基于注解的应用程序上下文 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); // 从上下文中获取 MyService MyService myService = context.getBean(MyService.class); // 调用方法 myService.foo(); } } ================================================ FILE: spring-aop/spring-aop-exposeInvocationInterceptor/src/main/java/com/xcs/spring/LogUtil.java ================================================ package com.xcs.spring; import org.springframework.aop.ProxyMethodInvocation; import org.springframework.aop.interceptor.ExposeInvocationInterceptor; public class LogUtil { public static void print() { ProxyMethodInvocation methodInvocation = (ProxyMethodInvocation) ExposeInvocationInterceptor.currentInvocation(); System.out.println("Method = " + methodInvocation.getMethod()); System.out.println("Arguments Length = " + methodInvocation.getArguments().length); System.out.println("Target = " + methodInvocation.getThis()); System.out.println("Proxy Class = " + methodInvocation.getProxy().getClass()); } } ================================================ FILE: spring-aop/spring-aop-exposeInvocationInterceptor/src/main/java/com/xcs/spring/MyMethodInterceptor.java ================================================ package com.xcs.spring; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Aspect @Component public class MyMethodInterceptor { @Before("execution(public * com.xcs.spring.MyService.*(..))") public void before() { LogUtil.print(); } } ================================================ FILE: spring-aop/spring-aop-exposeInvocationInterceptor/src/main/java/com/xcs/spring/MyService.java ================================================ package com.xcs.spring; import org.springframework.stereotype.Service; @Service public class MyService { public void foo() { System.out.println("foo..."); } } ================================================ FILE: spring-aop/spring-aop-jdkProxy/README.md ================================================ ## JDK动态代理 - [JDK动态代理](#jdk动态代理) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、最佳实践](#四最佳实践) - [五、源码分析](#五源码分析) - [六、常见问题](#六常见问题) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 JDK动态代理是一种在运行时生成代理类的机制,它基于接口实现,通过在代理类中重定向方法调用到实际对象,并提供了InvocationHandler接口来实现代理对象方法调用的处理逻辑,适用于AOP、远程方法调用等场景,能够在不修改原始类的情况下实现横切关注点的统一管理,提供更灵活和可维护的代码结构。 ### 三、主要功能 1. **代理对象生成** + 在运行时生成代理对象,无需提前编写代理类的代码。 2. **接口实现** + 动态代理是基于接口的,可以为接口生成代理对象,而不是具体的类。 3. **方法重定向** + 代理对象可以重定向方法调用到实际对象,允许在方法调用前后执行一些额外逻辑。 4. **横切关注点的统一管理** + 通过代理对象,在方法调用前后执行统一的逻辑,如日志记录、权限验证、事务管理等,实现了横切关注点的统一管理。 5. **AOP的实现** + 动态代理是AOP(面向切面编程)的基础之一,可以通过动态代理实现切面的横切关注点,将应用程序核心业务逻辑与横切关注点分离开来,提高了代码的可维护性和灵活性。 ### 四、最佳实践 使用 JDK 动态代理的基本流程。首先,创建目标对象 `MyService` 的实例,然后获取目标对象的类信息。接着,通过调用 `Proxy.newProxyInstance` 方法创建代理对象,传入目标对象的类加载器、实现的接口以及调用处理器。最后,通过代理对象调用方法,实际上会调用 `MyInvocationHandler` 中的 `invoke` 方法来处理方法调用,并在方法执行前后添加额外的逻辑。 ```java public class JdkProxyDemo { public static void main(String[] args) { // 创建目标对象 MyService target = new MyServiceImpl(); // 获取目标对象的类对象 Class clz = target.getClass(); // 创建代理对象,并指定目标对象的类加载器、实现的接口以及调用处理器 MyService proxyObject = (MyService) Proxy.newProxyInstance(clz.getClassLoader(), clz.getInterfaces(), new MyInvocationHandler(target)); // 打印代理对象的类信息 System.out.println("ProxyObject = " + proxyObject.getClass()); // 通过代理对象调用方法,实际上会调用 MyInvocationHandler 中的 invoke 方法 proxyObject.doSomething(); } } ``` 实现了 `InvocationHandler` 接口,定义了一个名为 `MyInvocationHandler` 的类。该类包含一个私有属性 `target`,用于存储目标对象。构造函数接收一个目标对象作为参数,并将其存储在 `target` 属性中。在 `invoke` 方法中,会在目标方法执行前打印 "Before method execution",然后通过反射调用目标对象的方法,并获取方法的返回结果。最后,在目标方法执行后打印 "After method execution",并返回方法的结果。 ```java class MyInvocationHandler implements InvocationHandler { private Object target; public MyInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Before method execution"); Object result = method.invoke(target, args); System.out.println("After method execution"); return result; } } ``` 定义了一个接口 `MyService`,其中包含一个抽象方法 `doSomething()`。然后,定义了一个实现了 `MyService` 接口的类 `MyServiceImpl`,并实现了 `doSomething()` 方法。 ```java public interface MyService { void doSomething(); } public class MyServiceImpl implements MyService { @Override public void doSomething() { System.out.println("hello world"); } } ``` 运行结果,成功使用了 JDK 动态代理。首先打印了代理对象的类信息,确认了代理对象确实是由 `$Proxy0` 类生成的。接着,打印了 "Before method execution",表示方法执行前的逻辑已经被执行。然后调用了目标对象的 `doSomething()` 方法,输出了 "hello world"。最后打印了 "After method execution",表示方法执行后的逻辑也被执行了。这表明 JDK 动态代理成功地代理了目标对象的方法,并在方法执行前后执行了额外的逻辑。 ```java ProxyObject = class com.sun.proxy.$Proxy0 Before method execution hello world After method execution ``` ### 五、源码分析 这段代码是通过 Arthas 工具反编译得到的结果。它是一个代理类,位于 `com.sun.proxy` 包下,命名为 `$Proxy0`。该类继承自 `Proxy` 类,并实现了 `MyService` 接口。在 `doSomething` 方法中,通过 `InvocationHandler` 对象的 `invoke` 方法调用了目标对象的 `doSomething` 方法。在静态代码块中,获取了 `java.lang.Object` 类中的 `equals`、`hashCode` 和 `toString` 方法,以及 `MyService` 接口中的 `doSomething` 方法的 `Method` 对象。 ```java package com.sun.proxy; import com.xcs.spring.MyService; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; public final class $Proxy0 extends Proxy implements MyService { private static Method m1; private static Method m3; private static Method m2; private static Method m0; public $Proxy0(InvocationHandler invocationHandler) { super(invocationHandler); } public final void doSomething() { try { this.h.invoke(this, m3, null); return; } catch (Error | RuntimeException throwable) { throw throwable; } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } // ... [toString代码部分省略以简化] // ... [hashCode代码部分省略以简化] // ... [equals代码部分省略以简化] static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m3 = Class.forName("com.xcs.spring.MyService").getMethod("doSomething", new Class[0]); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); return; } catch (NoSuchMethodException noSuchMethodException) { throw new NoSuchMethodError(noSuchMethodException.getMessage()); } catch (ClassNotFoundException classNotFoundException) { throw new NoClassDefFoundError(classNotFoundException.getMessage()); } } } ``` ### 六、常见问题 1. **代理对象的类型限制** + 由于 JDK 动态代理是基于接口实现的,因此只能代理实现了接口的类。如果目标对象没有实现接口,就无法使用 JDK 动态代理。 2. **无法代理 final 类和方法** + JDK 动态代理无法代理 final 类和 final 方法。因为 final 类无法被继承,final 方法无法被重写,而动态代理需要生成代理类并继承目标类或实现目标接口。 3. **无法代理静态方法** + JDK 动态代理无法代理静态方法,因为动态代理是基于实例的,而静态方法是属于类的。 4. **性能开销** + 与静态代理相比,JDK 动态代理的性能开销较大。动态代理在运行时生成代理类的字节码,并通过反射来调用方法,相比静态代理的直接方法调用,会增加额外的开销。 5. **泛型的处理** + 如果目标方法涉及泛型参数,代理对象可能无法正确处理。因为在泛型擦除后,代理对象无法获取到准确的泛型信息。 6. **调用堆栈的可读性** + 由于动态代理的调用会经过 `InvocationHandler`,可能会增加调用堆栈的深度,降低代码的可读性和调试的便利性。 ================================================ FILE: spring-aop/spring-aop-jdkProxy/pom.xml ================================================ com.xcs.spring spring-aop 0.0.1-SNAPSHOT 4.0.0 spring-aop-jdkProxy ================================================ FILE: spring-aop/spring-aop-jdkProxy/src/main/java/com/xcs/spring/JdkProxyDemo.java ================================================ package com.xcs.spring; import java.lang.reflect.Proxy; public class JdkProxyDemo { public static void main(String[] args) { // 创建目标对象 MyService target = new MyServiceImpl(); // 获取目标对象的类对象 Class clz = target.getClass(); // 创建代理对象,并指定目标对象的类加载器、实现的接口以及调用处理器 MyService proxyObject = (MyService) Proxy.newProxyInstance(clz.getClassLoader(), clz.getInterfaces(), new MyInvocationHandler(target)); // 打印代理对象的类信息 System.out.println("ProxyObject = " + proxyObject.getClass()); // 通过代理对象调用方法,实际上会调用 MyInvocationHandler 中的 invoke 方法 proxyObject.doSomething(); } } ================================================ FILE: spring-aop/spring-aop-jdkProxy/src/main/java/com/xcs/spring/MyInvocationHandler.java ================================================ package com.xcs.spring; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; class MyInvocationHandler implements InvocationHandler { private Object target; public MyInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Before method execution"); Object result = method.invoke(target, args); System.out.println("After method execution"); return result; } } ================================================ FILE: spring-aop/spring-aop-jdkProxy/src/main/java/com/xcs/spring/MyService.java ================================================ package com.xcs.spring; public interface MyService { void doSomething(); } ================================================ FILE: spring-aop/spring-aop-jdkProxy/src/main/java/com/xcs/spring/MyServiceImpl.java ================================================ package com.xcs.spring; public class MyServiceImpl implements MyService { @Override public void doSomething() { System.out.println("hello world"); } } ================================================ FILE: spring-aop/spring-aop-metadataAwareAspectInstanceFactory/README.md ================================================ ## MetadataAwareAspectInstanceFactory - [MetadataAwareAspectInstanceFactory](#metadataawareaspectinstancefactory) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、接口源码](#四接口源码) - [五、主要实现](#五主要实现) - [六、类关系图](#六类关系图) - [七、最佳实践](#七最佳实践) - [八、源码分析](#八源码分析) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https//juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https//github.com/xuchengsheng/spring-reading) ### 二、基本描述 `MetadataAwareAspectInstanceFactory` 接口是 Spring AOP 中的关键接口,用于实例化切面并处理其元数据信息,为 Spring 框架提供了对 AspectJ 注解风格的 AOP 切面的支持。 ### 三、主要功能 1. **实例化切面** + 通过 `getAspectInstance()` 方法,提供切面实例,以便在运行时应用切面的通知。 2. **处理元数据信息** + 通过 `getAspectMetadata()` 方法,获取切面类的元数据信息,如类名、所属的类、切点表达式等,以便在运行时能够正确地应用切面。 3. **支持 AspectJ 注解风格的 AOP** + 通过这个接口,Spring AOP 能够实现对 AspectJ 注解风格的 AOP 切面的实例化和元数据处理,从而支持在 Spring 应用中使用 AspectJ 注解定义切面。 ### 四、接口源码 `MetadataAwareAspectInstanceFactory` 接口是 `AspectInstanceFactory` 的子接口,用于返回与 AspectJ 注解类关联的 `AspectMetadata`。`AspectMetadata` 包含与切面相关的元数据信息。此接口还定义了一个方法 `getAspectCreationMutex()`,用于返回此工厂的最佳创建互斥锁。由于 `AspectMetadata` 使用了 Java 5 专用的 `org.aspectj.lang.reflect.AjType`,因此需要将此方法拆分到这个子接口中。 ```java /** * {@link org.springframework.aop.aspectj.AspectInstanceFactory} 的子接口,用于返回与 AspectJ 注解类关联的 {@link AspectMetadata}。 * *

理想情况下,AspectInstanceFactory 本身应该包括此方法,但由于 AspectMetadata 使用了 Java 5 专用的 {@link org.aspectj.lang.reflect.AjType}, * 我们需要拆分出这个子接口。 * * @author Rod Johnson * @since 2.0 * @see AspectMetadata * @see org.aspectj.lang.reflect.AjType */ public interface MetadataAwareAspectInstanceFactory extends AspectInstanceFactory { /** * 返回此工厂的切面的 AspectJ AspectMetadata。 * @return 切面元数据 */ AspectMetadata getAspectMetadata(); /** * 返回此工厂的最佳创建互斥锁。 * @return 互斥锁对象(如果不需要使用锁,则可能为 {@code null}) * @since 4.3 */ @Nullable Object getAspectCreationMutex(); } ``` ### 五、主要实现 1. **SimpleMetadataAwareAspectInstanceFactory** - 这个实现类是最简单的一种,它用于创建单例的切面实例。它简单地实例化切面类,并提供其实例作为切面的实例。 2. **SingletonMetadataAwareAspectInstanceFactory** - 与 `SimpleMetadataAwareAspectInstanceFactory` 类似,这个实现类也用于创建单例的切面实例,但是它可以与 Spring 的容器集成,以便将切面实例作为容器中的单例 bean 进行管理。 3. **BeanFactoryAspectInstanceFactory** - 这个实现类与 Spring 的 BeanFactory 集成,它用于创建切面实例,并且能够处理切面类的依赖注入。它可以在切面类中注入其他 Spring 管理的 bean,实现更复杂的业务逻辑。 4. **PrototypeAspectInstanceFactory** - 这个实现类用于创建原型(prototype)的切面实例。与单例不同,原型实例每次请求时都会创建一个新的实例,适用于需要在每次使用时都重新创建实例的场景。 5. **LazySingletonAspectInstanceFactoryDecorator** - 这个实现类是一个装饰器,用于延迟初始化单例的切面实例。它在首次请求切面实例时才进行实例化,以提高性能并延迟资源消耗。 ### 六、类关系图 ~~~mermaid classDiagram direction BT class AspectInstanceFactory { <> } class BeanFactoryAspectInstanceFactory class LazySingletonAspectInstanceFactoryDecorator class MetadataAwareAspectInstanceFactory { <> } class PrototypeAspectInstanceFactory class SimpleAspectInstanceFactory class SimpleMetadataAwareAspectInstanceFactory class SingletonAspectInstanceFactory class SingletonMetadataAwareAspectInstanceFactory BeanFactoryAspectInstanceFactory ..> MetadataAwareAspectInstanceFactory LazySingletonAspectInstanceFactoryDecorator ..> MetadataAwareAspectInstanceFactory MetadataAwareAspectInstanceFactory --> AspectInstanceFactory PrototypeAspectInstanceFactory --> BeanFactoryAspectInstanceFactory SimpleAspectInstanceFactory ..> AspectInstanceFactory SimpleMetadataAwareAspectInstanceFactory ..> MetadataAwareAspectInstanceFactory SimpleMetadataAwareAspectInstanceFactory --> SimpleAspectInstanceFactory SingletonAspectInstanceFactory ..> AspectInstanceFactory SingletonMetadataAwareAspectInstanceFactory ..> MetadataAwareAspectInstanceFactory SingletonMetadataAwareAspectInstanceFactory --> SingletonAspectInstanceFactory ~~~ ### 七、最佳实践 使用不同的 `MetadataAwareAspectInstanceFactory` 实现类来实例化切面,并展示了它们的不同行为。首先,使用 `SimpleMetadataAwareAspectInstanceFactory` 和 `SingletonMetadataAwareAspectInstanceFactory` 分别创建单例的切面实例,然后使用 `BeanFactoryAspectInstanceFactory` 在 Spring Bean 工厂中注册并实例化切面,最后使用 `LazySingletonAspectInstanceFactoryDecorator` 延迟初始化单例切面实例。在每个步骤中,都输出了切面实例及其元数据信息。 ```java public class MetadataAwareAspectInstanceFactoryDemo { public static void main(String[] args) { // 使用 SimpleMetadataAwareAspectInstanceFactory 实例化切面 SimpleMetadataAwareAspectInstanceFactory simpleMetadataAwareAif = new SimpleMetadataAwareAspectInstanceFactory(MyAspect.class, "myAspect"); System.out.println("SimpleMetadataAwareAspectInstanceFactory (1) = " + simpleMetadataAwareAif.getAspectInstance()); System.out.println("SimpleMetadataAwareAspectInstanceFactory (2) = " + simpleMetadataAwareAif.getAspectInstance()); System.out.println("SimpleMetadataAwareAspectInstanceFactory AspectMetadata = " + JSONUtil.toJsonStr(simpleMetadataAwareAif.getAspectMetadata())); System.out.println(); // 使用 SingletonMetadataAwareAspectInstanceFactory 实例化切面 SingletonMetadataAwareAspectInstanceFactory singletonMetadataAwareAif = new SingletonMetadataAwareAspectInstanceFactory(new MyAspect(), "myAspect"); System.out.println("SingletonMetadataAwareAspectInstanceFactory (1) = " + singletonMetadataAwareAif.getAspectInstance()); System.out.println("SingletonMetadataAwareAspectInstanceFactory (2) = " + singletonMetadataAwareAif.getAspectInstance()); System.out.println("SimpleMetadataAwareAspectInstanceFactory AspectMetadata = " + JSONUtil.toJsonStr(singletonMetadataAwareAif.getAspectMetadata())); System.out.println(); // 使用 BeanFactoryAspectInstanceFactory 实例化切面 DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); beanFactory.registerSingleton("myAspect", new MyAspect()); BeanFactoryAspectInstanceFactory banFactoryAif = new BeanFactoryAspectInstanceFactory(beanFactory, "myAspect"); System.out.println("BeanFactoryAspectInstanceFactory (1) = " + banFactoryAif.getAspectInstance()); System.out.println("BeanFactoryAspectInstanceFactory (2) = " + banFactoryAif.getAspectInstance()); System.out.println("SimpleMetadataAwareAspectInstanceFactory AspectMetadata = " + JSONUtil.toJsonStr(banFactoryAif.getAspectMetadata())); System.out.println(); // 使用 LazySingletonAspectInstanceFactoryDecorator 实例化切面 LazySingletonAspectInstanceFactoryDecorator lazySingletonAifD = new LazySingletonAspectInstanceFactoryDecorator(banFactoryAif); System.out.println("LazySingletonAspectInstanceFactoryDecorator (1) = " + lazySingletonAifD.getAspectInstance()); System.out.println("LazySingletonAspectInstanceFactoryDecorator (2) = " + lazySingletonAifD.getAspectInstance()); System.out.println("LazySingletonAspectInstanceFactoryDecorator AspectCreationMutex = " + lazySingletonAifD.getAspectCreationMutex()); System.out.println("LazySingletonAspectInstanceFactoryDecorator AspectMetadata = " + JSONUtil.toJsonStr(lazySingletonAifD.getAspectMetadata())); System.out.println(); } } ``` 运行结果,展示了不同类型的 `MetadataAwareAspectInstanceFactory` 实现类的行为。`SimpleMetadataAwareAspectInstanceFactory` 每次返回不同的切面实例,而 `SingletonMetadataAwareAspectInstanceFactory` 每次返回相同的实例,说明了它们的单例和非单例的行为。`BeanFactoryAspectInstanceFactory` 在 Spring Bean 工厂中注册并实例化切面,表现出与前两者类似的行为。`LazySingletonAspectInstanceFactoryDecorator` 是对 `BeanFactoryAspectInstanceFactory` 的装饰器,延迟初始化单例切面实例,但最终结果与 `BeanFactoryAspectInstanceFactory` 相同。 ```java SimpleMetadataAwareAspectInstanceFactory (1) = com.xcs.spring.MyAspect@5f341870 SimpleMetadataAwareAspectInstanceFactory (2) = com.xcs.spring.MyAspect@553f17c SimpleMetadataAwareAspectInstanceFactory AspectMetadata = {"aspectName":"myAspect","aspectClass":"com.xcs.spring.MyAspect","perClausePointcut":{}} SingletonMetadataAwareAspectInstanceFactory (1) = com.xcs.spring.MyAspect@1da51a35 SingletonMetadataAwareAspectInstanceFactory (2) = com.xcs.spring.MyAspect@1da51a35 SimpleMetadataAwareAspectInstanceFactory AspectMetadata = {"aspectName":"myAspect","aspectClass":"com.xcs.spring.MyAspect","perClausePointcut":{}} BeanFactoryAspectInstanceFactory (1) = com.xcs.spring.MyAspect@6646153 BeanFactoryAspectInstanceFactory (2) = com.xcs.spring.MyAspect@6646153 SimpleMetadataAwareAspectInstanceFactory AspectMetadata = {"aspectName":"myAspect","aspectClass":"com.xcs.spring.MyAspect","perClausePointcut":{}} LazySingletonAspectInstanceFactoryDecorator (1) = com.xcs.spring.MyAspect@6646153 LazySingletonAspectInstanceFactoryDecorator (2) = com.xcs.spring.MyAspect@6646153 LazySingletonAspectInstanceFactoryDecorator AspectCreationMutex = null LazySingletonAspectInstanceFactoryDecorator AspectMetadata = {"aspectName":"myAspect","aspectClass":"com.xcs.spring.MyAspect","perClausePointcut":{}} ``` ### 八、源码分析 **SimpleMetadataAwareAspectInstanceFactory** `SimpleMetadataAwareAspectInstanceFactory` 是一个实现了 `MetadataAwareAspectInstanceFactory` 接口的类,它在每次调用 `getAspectInstance()` 方法时都会为指定的切面类创建一个新的实例。这个类通过 `AspectMetadata` 对象来管理切面的元数据信息,并且实现了 `getAspectMetadata()` 方法来提供这些元数据。它还实现了 `getAspectCreationMutex()` 方法来返回切面实例的创建锁,以及 `getOrderForAspectClass()` 方法来确定切面类的顺序。 ```java /** * 实现了 {@link MetadataAwareAspectInstanceFactory} 接口的类,每次调用 {@link #getAspectInstance()} 方法都会为指定的切面类创建一个新的实例。 * * @author Juergen Hoeller * @since 2.0.4 */ public class SimpleMetadataAwareAspectInstanceFactory extends SimpleAspectInstanceFactory implements MetadataAwareAspectInstanceFactory { private final AspectMetadata metadata; // 切面的元数据信息 /** * 创建一个新的 SimpleMetadataAwareAspectInstanceFactory 实例,用于给定的切面类。 * * @param aspectClass 切面类 * @param aspectName 切面名称 */ public SimpleMetadataAwareAspectInstanceFactory(Class aspectClass, String aspectName) { super(aspectClass); this.metadata = new AspectMetadata(aspectClass, aspectName); // 创建切面的元数据信息 } /** * 获取切面的元数据信息。 * * @return 切面的元数据信息 */ @Override public final AspectMetadata getAspectMetadata() { return this.metadata; } /** * 获取切面实例的创建锁。 * * @return 切面实例的创建锁 */ @Override public Object getAspectCreationMutex() { return this; } /** * 获取切面类的顺序。 * * @param aspectClass 切面类 * @return 切面类的顺序 */ @Override protected int getOrderForAspectClass(Class aspectClass) { return OrderUtils.getOrder(aspectClass, Ordered.LOWEST_PRECEDENCE); // 获取切面类的顺序 } } ``` **SingletonMetadataAwareAspectInstanceFactory** `SingletonMetadataAwareAspectInstanceFactory` 是一个实现了 `MetadataAwareAspectInstanceFactory` 接口的类,它通过指定的单例对象支持切面实例的创建,每次调用 `getAspectInstance()` 方法都返回相同的实例。该类使用一个单例的切面实例,并通过 `AspectMetadata` 对象管理切面的元数据信息。它也实现了 `Serializable` 接口以支持序列化,并且继承自 `SingletonAspectInstanceFactory`,提供了获取切面实例的相关方法和逻辑。 ```java /** * 实现了 {@link MetadataAwareAspectInstanceFactory} 接口的类,通过指定的单例对象支持切面实例的创建,每次调用 {@link #getAspectInstance()} 方法都返回同一个实例。 * * 该类通过 {@link AspectMetadata} 对象管理切面的元数据信息,并且实现了 {@link Serializable} 接口以支持序列化。 * * 作者:Rod Johnson, Juergen Hoeller * @since 2.0 * @see SimpleMetadataAwareAspectInstanceFactory */ @SuppressWarnings("serial") public class SingletonMetadataAwareAspectInstanceFactory extends SingletonAspectInstanceFactory implements MetadataAwareAspectInstanceFactory, Serializable { private final AspectMetadata metadata; // 切面的元数据信息 /** * 为给定的切面创建一个新的 SingletonMetadataAwareAspectInstanceFactory。 * * @param aspectInstance 切面的单例实例 * @param aspectName 切面的名称 */ public SingletonMetadataAwareAspectInstanceFactory(Object aspectInstance, String aspectName) { super(aspectInstance); // 调用父类的构造方法,传入切面的单例实例 this.metadata = new AspectMetadata(aspectInstance.getClass(), aspectName); // 创建切面的元数据信息 } /** * 获取切面的元数据信息。 * * @return 切面的元数据信息 */ @Override public final AspectMetadata getAspectMetadata() { return this.metadata; } /** * 获取切面实例的创建锁。 * * @return 切面实例的创建锁 */ @Override public Object getAspectCreationMutex() { return this; } /** * 获取切面类的顺序。 * * @param aspectClass 切面类 * @return 切面类的顺序 */ @Override protected int getOrderForAspectClass(Class aspectClass) { return OrderUtils.getOrder(aspectClass, Ordered.LOWEST_PRECEDENCE); // 获取切面类的顺序 } } ``` **BeanFactoryAspectInstanceFactory** `BeanFactoryAspectInstanceFactory` 是一个实现了 `MetadataAwareAspectInstanceFactory` 接口的类,它通过 Spring 的 `BeanFactory` 支持切面实例的创建。这个工厂可以通过指定的 bean 名称从 `BeanFactory` 中获取切面实例,并且可以通过提供的类型来自省以创建 AspectJ 的元数据信息。它可以处理单例和非单例的情况,并且能够确定切面的顺序,支持使用 `Ordered` 接口或 `@Order` 注解来定义顺序。 ```java /** * {@link org.springframework.aop.aspectj.AspectInstanceFactory} 接口的实现, * 由 Spring {@link org.springframework.beans.factory.BeanFactory} 支持。 * *

注意,如果使用原型模式可能会多次实例化,这可能不会得到您期望的语义。 * 使用 {@link LazySingletonAspectInstanceFactoryDecorator} 来包装这个工厂, * 以确保只返回一个新的切面。 * * 作者:Rod Johnson, Juergen Hoeller * @since 2.0 * @see org.springframework.beans.factory.BeanFactory * @see LazySingletonAspectInstanceFactoryDecorator */ @SuppressWarnings("serial") public class BeanFactoryAspectInstanceFactory implements MetadataAwareAspectInstanceFactory, Serializable { private final BeanFactory beanFactory; // Bean 工厂 private final String name; // Bean 名称 private final AspectMetadata aspectMetadata; // 切面的元数据信息 /** * 创建一个 BeanFactoryAspectInstanceFactory。AspectJ 将被调用来自省, * 使用从 BeanFactory 中为给定的 bean 名称返回的类型创建 AJType 元数据。 * * @param beanFactory BeanFactory,用于获取实例 * @param name bean 的名称 */ public BeanFactoryAspectInstanceFactory(BeanFactory beanFactory, String name) { this(beanFactory, name, null); } /** * 创建一个 BeanFactoryAspectInstanceFactory,提供一个类型,AspectJ 应该自省以创建 AJType 元数据。 * 如果 BeanFactory 可能将类型视为子类(例如使用 CGLIB),并且信息应该与超类相关,则使用此选项。 * * @param beanFactory BeanFactory,用于获取实例 * @param name bean 的名称 * @param type AspectJ 应该自省的类型({@code null} 表示通过 bean 名称解析通过 {@link BeanFactory#getType} 的类型) */ public BeanFactoryAspectInstanceFactory(BeanFactory beanFactory, String name, @Nullable Class type) { Assert.notNull(beanFactory, "BeanFactory must not be null"); Assert.notNull(name, "Bean name must not be null"); this.beanFactory = beanFactory; this.name = name; Class resolvedType = type; if (type == null) { resolvedType = beanFactory.getType(name); Assert.notNull(resolvedType, "Unresolvable bean type - explicitly specify the aspect class"); } this.aspectMetadata = new AspectMetadata(resolvedType, name); // 创建切面的元数据信息 } /** * 获取切面实例。 * * @return 切面实例 */ @Override public Object getAspectInstance() { return this.beanFactory.getBean(this.name); } /** * 获取切面的类加载器。 * * @return 切面的类加载器 */ @Override @Nullable public ClassLoader getAspectClassLoader() { return (this.beanFactory instanceof ConfigurableBeanFactory ? ((ConfigurableBeanFactory) this.beanFactory).getBeanClassLoader() : ClassUtils.getDefaultClassLoader()); } /** * 获取切面的元数据信息。 * * @return 切面的元数据信息 */ @Override public AspectMetadata getAspectMetadata() { return this.aspectMetadata; } /** * 获取切面实例的创建锁。 * * @return 切面实例的创建锁 */ @Override @Nullable public Object getAspectCreationMutex() { if (this.beanFactory.isSingleton(this.name)) { // 依赖于工厂提供的单例语义 -> 没有本地锁。 return null; } else if (this.beanFactory instanceof ConfigurableBeanFactory) { // 从工厂中没有单例保证 -> 让我们本地锁定,但重用工厂的单例锁,以防万一我们的通知 bean 的惰性依赖项 // 不小心触发了单例锁隐式... return ((ConfigurableBeanFactory) this.beanFactory).getSingletonMutex(); } else { return this; } } /** * 确定此工厂目标切面的顺序,可以通过实现 {@link org.springframework.core.Ordered} 接口来表达实例特定的顺序 * (仅对单例 bean 进行检查),也可以通过 {@link org.springframework.core.annotation.Order} 注解在类级别表达顺序。 * * @see org.springframework.core.Ordered * @see org.springframework.core.annotation.Order */ @Override public int getOrder() { Class type = this.beanFactory.getType(this.name); if (type != null) { if (Ordered.class.isAssignableFrom(type) && this.beanFactory.isSingleton(this.name)) { return ((Ordered) this.beanFactory.getBean(this.name)).getOrder(); } return OrderUtils.getOrder(type, Ordered.LOWEST_PRECEDENCE); } return Ordered.LOWEST_PRECEDENCE; } @Override public String toString() { return getClass().getSimpleName() + ": bean name '" + this.name + "'"; } } ``` **LazySingletonAspectInstanceFactoryDecorator** `LazySingletonAspectInstanceFactoryDecorator`类是一个装饰器,用于确保一个 `MetadataAwareAspectInstanceFactory` 只实例化一次。它包装了另一个 `MetadataAwareAspectInstanceFactory` 实例,并在首次调用 `getAspectInstance()` 方法时进行实例化。在后续的调用中,它将返回已经实例化的对象,而不会再次实例化。 ```java /** * 修饰器,使 {@link MetadataAwareAspectInstanceFactory} 仅实例化一次。 * * 作者:Rod Johnson, Juergen Hoeller * @since 2.0 */ @SuppressWarnings("serial") public class LazySingletonAspectInstanceFactoryDecorator implements MetadataAwareAspectInstanceFactory, Serializable { private final MetadataAwareAspectInstanceFactory maaif; // 要装饰的 MetadataAwareAspectInstanceFactory 实例 @Nullable private volatile Object materialized; // 实例化的对象 /** * 创建一个对给定 AspectInstanceFactory 进行懒初始化的修饰器。 * @param maaif 要装饰的 MetadataAwareAspectInstanceFactory */ public LazySingletonAspectInstanceFactoryDecorator(MetadataAwareAspectInstanceFactory maaif) { Assert.notNull(maaif, "AspectInstanceFactory must not be null"); this.maaif = maaif; } /** * 获取切面实例。 * 如果实例化过程中已经存在一个实例,则直接返回该实例; * 否则,根据实例化互斥锁(如果存在)保证多线程环境下只实例化一次。 * 如果没有互斥锁,则直接实例化切面对象并将其赋值给 materialized 变量,然后返回该实例。 * 如果存在互斥锁,则使用该锁来保护实例化过程,确保在多线程环境下只有一个线程可以执行实例化操作。 * * @return 切面实例 */ @Override public Object getAspectInstance() { // 尝试获取已实例化的对象 Object aspectInstance = this.materialized; // 如果不存在已实例化的对象 if (aspectInstance == null) { // 获取实例化互斥锁 Object mutex = this.maaif.getAspectCreationMutex(); // 如果不存在互斥锁 if (mutex == null) { // 直接实例化切面对象 aspectInstance = this.maaif.getAspectInstance(); // 将实例化后的对象赋值给 materialized 变量 this.materialized = aspectInstance; } else { // 使用互斥锁保护实例化过程 synchronized (mutex) { // 再次尝试获取已实例化的对象 aspectInstance = this.materialized; // 双重检查,确保在锁内部只实例化一次 if (aspectInstance == null) { // 实例化切面对象 aspectInstance = this.maaif.getAspectInstance(); // 将实例化后的对象赋值给 materialized 变量 this.materialized = aspectInstance; } } } } return aspectInstance; // 返回切面实例 } /** * 返回是否已经实例化。 */ public boolean isMaterialized() { return (this.materialized != null); } @Override @Nullable public ClassLoader getAspectClassLoader() { return this.maaif.getAspectClassLoader(); } @Override public AspectMetadata getAspectMetadata() { return this.maaif.getAspectMetadata(); } @Override @Nullable public Object getAspectCreationMutex() { return this.maaif.getAspectCreationMutex(); } @Override public int getOrder() { return this.maaif.getOrder(); } @Override public String toString() { return "LazySingletonAspectInstanceFactoryDecorator: decorating " + this.maaif; } } ``` ================================================ FILE: spring-aop/spring-aop-metadataAwareAspectInstanceFactory/pom.xml ================================================ com.xcs.spring spring-aop 0.0.1-SNAPSHOT 4.0.0 spring-aop-metadataAwareAspectInstanceFactory cn.hutool hutool-json 5.8.27 ================================================ FILE: spring-aop/spring-aop-metadataAwareAspectInstanceFactory/src/main/java/com/xcs/spring/MetadataAwareAspectInstanceFactoryDemo.java ================================================ package com.xcs.spring; import cn.hutool.json.JSONUtil; import org.springframework.aop.aspectj.annotation.*; import org.springframework.beans.factory.support.DefaultListableBeanFactory; public class MetadataAwareAspectInstanceFactoryDemo { public static void main(String[] args) { // 使用 SimpleMetadataAwareAspectInstanceFactory 实例化切面 SimpleMetadataAwareAspectInstanceFactory simpleMetadataAwareAif = new SimpleMetadataAwareAspectInstanceFactory(MyAspect.class, "myAspect"); System.out.println("SimpleMetadataAwareAspectInstanceFactory (1) = " + simpleMetadataAwareAif.getAspectInstance()); System.out.println("SimpleMetadataAwareAspectInstanceFactory (2) = " + simpleMetadataAwareAif.getAspectInstance()); System.out.println("SimpleMetadataAwareAspectInstanceFactory AspectMetadata = " + JSONUtil.toJsonStr(simpleMetadataAwareAif.getAspectMetadata())); System.out.println(); // 使用 SingletonMetadataAwareAspectInstanceFactory 实例化切面 SingletonMetadataAwareAspectInstanceFactory singletonMetadataAwareAif = new SingletonMetadataAwareAspectInstanceFactory(new MyAspect(), "myAspect"); System.out.println("SingletonMetadataAwareAspectInstanceFactory (1) = " + singletonMetadataAwareAif.getAspectInstance()); System.out.println("SingletonMetadataAwareAspectInstanceFactory (2) = " + singletonMetadataAwareAif.getAspectInstance()); System.out.println("SimpleMetadataAwareAspectInstanceFactory AspectMetadata = " + JSONUtil.toJsonStr(singletonMetadataAwareAif.getAspectMetadata())); System.out.println(); // 使用 BeanFactoryAspectInstanceFactory 实例化切面 DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); beanFactory.registerSingleton("myAspect", new MyAspect()); BeanFactoryAspectInstanceFactory banFactoryAif = new BeanFactoryAspectInstanceFactory(beanFactory, "myAspect"); System.out.println("BeanFactoryAspectInstanceFactory (1) = " + banFactoryAif.getAspectInstance()); System.out.println("BeanFactoryAspectInstanceFactory (2) = " + banFactoryAif.getAspectInstance()); System.out.println("SimpleMetadataAwareAspectInstanceFactory AspectMetadata = " + JSONUtil.toJsonStr(banFactoryAif.getAspectMetadata())); System.out.println(); // 使用 LazySingletonAspectInstanceFactoryDecorator 实例化切面 LazySingletonAspectInstanceFactoryDecorator lazySingletonAifD = new LazySingletonAspectInstanceFactoryDecorator(banFactoryAif); System.out.println("LazySingletonAspectInstanceFactoryDecorator (1) = " + lazySingletonAifD.getAspectInstance()); System.out.println("LazySingletonAspectInstanceFactoryDecorator (2) = " + lazySingletonAifD.getAspectInstance()); System.out.println("LazySingletonAspectInstanceFactoryDecorator AspectCreationMutex = " + lazySingletonAifD.getAspectCreationMutex()); System.out.println("LazySingletonAspectInstanceFactoryDecorator AspectMetadata = " + JSONUtil.toJsonStr(lazySingletonAifD.getAspectMetadata())); System.out.println(); } } ================================================ FILE: spring-aop/spring-aop-metadataAwareAspectInstanceFactory/src/main/java/com/xcs/spring/MyAspect.java ================================================ package com.xcs.spring; import org.aspectj.lang.annotation.Aspect; @Aspect class MyAspect { } ================================================ FILE: spring-aop/spring-aop-methodMatcher/README.md ================================================ ## MethodMatcher - [MethodMatcher](#methodmatcher) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、接口源码](#四接口源码) - [五、主要实现](#五主要实现) - [六、类关系图](#六类关系图) - [七、最佳实践](#七最佳实践) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 `MethodMatcher` 接口是Spring AOP中的一个关键接口,用于判断一个给定的方法是否匹配指定的切点表达式。它定义了方法匹配的规则和逻辑,我们可以通过实现这个接口来自定义方法匹配的行为,从而实现针对特定方法的切面逻辑的拦截和执行。 ### 三、主要功能 1. **方法匹配** + 判断一个给定的方法是否符合指定的切点表达式,即确定是否应该对该方法进行拦截和应用额外的逻辑。 2. **静态匹配** + 可以静态地匹配方法,这意味着方法的匹配逻辑可以在编译时确定,并且在整个应用程序的生命周期内保持不变。 3. **动态匹配** + 有些切点需要在运行时根据方法的参数或其他条件来动态确定匹配与否,`MethodMatcher` 接口也支持这种动态匹配的能力。 4. **运行时效率** + `MethodMatcher` 的实现应该具有高效率,尤其是在动态匹配的情况下,以避免对应用程序性能造成过大的负担。 5. **可扩展性** + `MethodMatcher` 接口的设计应该具有良好的扩展性,我们可以根据实际需求自定义方法匹配的规则和逻辑,以满足不同的业务场景和需求。 ### 四、接口源码 `MethodMatcher` 接口用于检查目标方法是否符合通知的条件。它支持静态匹配和动态匹配两种方式,静态匹配在编译时确定,而动态匹配在运行时根据方法参数和先前通知的执行情况进行判断。 ```java /** * {@link Pointcut}的一部分:检查目标方法是否符合通知的条件。 * *

MethodMatcher 可以静态动态地评估。 * 静态匹配涉及方法和(可能的)方法属性。 * 动态匹配还可以使调用的参数可用,并且可以考虑到之前应用于连接点的先前通知的任何效果。 * *

如果实现从其{@link #isRuntime()}方法返回{@code false},则可以静态地执行评估, * 并且对于此方法的所有调用,无论其参数如何,结果都将相同。 * 这意味着如果{@link #isRuntime()}方法返回{@code false},则永远不会调用 3-arg * {@link #matches(java.lang.reflect.Method, Class, Object[])} 方法。 * *

如果实现从其 2-arg {@link #matches(java.lang.reflect.Method, Class)} 方法返回{@code true}, * 并且其{@link #isRuntime()}方法返回{@code true},则将在每次相关通知的潜在执行之前 * 调用 3-arg {@link #matches(java.lang.reflect.Method, Class, Object[])} 方法, * 以决定是否应该运行通知。 * 所有先前的通知,例如拦截器链中的较早拦截器,都将已运行,因此在评估时将可用参数或ThreadLocal状态的任何状态更改。 * *

此接口的具体实现通常应提供{@link Object#equals(Object)}和{@link Object#hashCode()}的正确实现, * 以便允许在缓存方案中使用匹配器 - 例如,由CGLIB生成的代理。 * * @author Rod Johnson * @since 11.11.2003 * @see Pointcut * @see ClassFilter */ public interface MethodMatcher { /** * 执行静态检查,确定给定的方法是否匹配。 *

如果此方法返回{@code false},或者{@link #isRuntime()}方法返回{@code false}, * 则不会进行运行时检查(即不会调用 {@link #matches(java.lang.reflect.Method, Class, Object[])} 方法)。 * @param method 候选方法 * @param targetClass 目标类 * @return 此方法是否静态匹配 */ boolean matches(Method method, Class targetClass); /** * 此 MethodMatcher 是否是动态的,也就是说,即使 2-arg matches 方法返回 {@code true}, * 在运行时是否必须对 {@link #matches(java.lang.reflect.Method, Class, Object[])} 方法进行最终调用? *

可以在创建AOP代理时调用,不需要在每次方法调用之前再次调用。 * @return 是否需要运行时匹配 */ boolean isRuntime(); /** * 检查此方法是否存在运行时(动态)匹配,此匹配必须已经通过静态匹配。 *

仅在给定方法和目标类的 2-arg matches 方法返回{@code true}, * 并且 {@link #isRuntime()} 方法返回{@code true} 时才会调用此方法。 * 在潜在运行通知之前立即调用,之前的通知链中的所有通知已运行。 * @param method 候选方法 * @param targetClass 目标类 * @param args 方法的参数 * @return 是否存在运行时匹配 * @see MethodMatcher#matches(Method, Class) */ boolean matches(Method method, Class targetClass, Object... args); /** * 匹配所有方法的规范实例。 */ MethodMatcher TRUE = TrueMethodMatcher.INSTANCE; } ``` ### 五、主要实现 1. **AnnotationMethodMatcher** + 这个类是用于匹配带有特定注解的方法的方法匹配器。它可以用来创建切点,以便对带有特定注解的方法进行拦截和增强。 2. **ControlFlowPointcut** + 控制流切点用于定义在特定的方法调用链中触发通知的位置。它允许我们指定只有在控制流程满足某些条件时才触发通知。 3. **JdkRegexpMethodPointcut** + 这个类使用基于正则表达式的方法匹配来创建切点。它允许我们根据方法的名称来定义匹配规则,从而决定哪些方法应该被拦截。 4. **NameMatchMethodPointcut** + 这个类是基于方法名称的匹配器,它允许我们根据方法的名称模式来定义切点。只要方法名称匹配指定的模式,就可以触发通知。 5. **AspectJExpressionPointcut** + 这个类使用 AspectJ 表达式语言来创建切点,它允许我们使用更加灵活和强大的语法来定义切点。AspectJ 表达式支持更多的特性,包括访问方法参数、异常类型等。 ### 六、类关系图 ~~~mermaid classDiagram direction BT class AbstractRegexpMethodPointcut class AnnotationMethodMatcher class AspectJExpressionPointcut class ControlFlowPointcut class IntroductionAwareMethodMatcher { <> } class JdkRegexpMethodPointcut class MethodMatcher { <> } class NameMatchMethodPointcut class StaticMethodMatcher class StaticMethodMatcherPointcut AbstractRegexpMethodPointcut --> StaticMethodMatcherPointcut AnnotationMethodMatcher --> StaticMethodMatcher AspectJExpressionPointcut ..> IntroductionAwareMethodMatcher ControlFlowPointcut ..> MethodMatcher IntroductionAwareMethodMatcher --> MethodMatcher JdkRegexpMethodPointcut --> AbstractRegexpMethodPointcut NameMatchMethodPointcut --> StaticMethodMatcherPointcut StaticMethodMatcher ..> MethodMatcher StaticMethodMatcherPointcut --> StaticMethodMatcher ~~~ ### 七、最佳实践 获取了名为 "setName" 的方法,并使用四种不同类型的方法匹配器对其进行匹配检查。其中,AnnotationMethodMatcher 检查该方法是否具有特定注解,AspectJExpressionPointcut 基于 AspectJ 表达式匹配方法,NameMatchMethodPointcut 基于方法名称匹配方法,JdkRegexpMethodPointcut 基于正则表达式匹配方法。最后,程序输出了每种匹配器的匹配结果。 ```java public class MethodMatcherDemo { public static void main(String[] args) throws Exception { Class target = MyService.class; Method setNameMethod = target.getDeclaredMethod("setName"); // 使用 AnnotationMethodMatcher 检查是否具有特定注解 AnnotationMethodMatcher annotationMethodMatcher = new AnnotationMethodMatcher(MyMethodAnnotation.class); System.out.println("annotationMethodMatcher matches = " + annotationMethodMatcher.matches(setNameMethod, target)); // 使用 AspectJExpressionPointcut 基于 AspectJ 表达式匹配方法 AspectJExpressionPointcut aspectJExpressionPointcut = new AspectJExpressionPointcut(); aspectJExpressionPointcut.setExpression("execution(* com.xcs.spring.MyService.*(..))"); System.out.println("aspectJExpressionPointcut matches = " + aspectJExpressionPointcut.matches(setNameMethod, target)); // 使用 NameMatchMethodPointcut 基于方法名称匹配方法 NameMatchMethodPointcut nameMatchMethodPointcut = new NameMatchMethodPointcut(); nameMatchMethodPointcut.setMappedName("setName"); System.out.println("nameMatchMethodPointcut matches = " + nameMatchMethodPointcut.matches(setNameMethod, target)); // 使用 JdkRegexpMethodPointcut 基于正则表达式匹配方法 JdkRegexpMethodPointcut jdkRegexpMethodPointcut = new JdkRegexpMethodPointcut(); jdkRegexpMethodPointcut.setPattern(".*set.*"); System.out.println("jdkRegexpMethodPointcut matches = " + jdkRegexpMethodPointcut.matches(setNameMethod, target)); } } ``` `MyService` 类中的 `setName` 方法被 `@MyMethodAnnotation` 注解修饰,表示该方法具有特定的自定义注解。 ```java public class MyService { @MyMethodAnnotation public void setName() { System.out.println("setName..."); } } ``` `MyMethodAnnotation` 是一个自定义注解,该注解可以应用于方法上。 ```java @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface MyMethodAnnotation { } ``` 运行结果,对于目标类中的 "setName" 方法,无论是基于注解、AspectJ 表达式、方法名称还是正则表达式的匹配器,都返回了 true,即这些匹配器都成功匹配了该方法。 ```java annotationMethodMatcher matches = true aspectJExpressionPointcut matches = true nameMatchMethodPointcut matches = true jdkRegexpMethodPointcut matches = true ``` ================================================ FILE: spring-aop/spring-aop-methodMatcher/pom.xml ================================================ com.xcs.spring spring-aop 0.0.1-SNAPSHOT 4.0.0 spring-aop-methodMatcher ================================================ FILE: spring-aop/spring-aop-methodMatcher/src/main/java/com/xcs/spring/MethodMatcherDemo.java ================================================ package com.xcs.spring; import org.springframework.aop.aspectj.AspectJExpressionPointcut; import org.springframework.aop.support.JdkRegexpMethodPointcut; import org.springframework.aop.support.NameMatchMethodPointcut; import org.springframework.aop.support.annotation.AnnotationMethodMatcher; import java.lang.reflect.Method; public class MethodMatcherDemo { public static void main(String[] args) throws Exception { Class target = MyService.class; Method setNameMethod = target.getDeclaredMethod("setName"); // 使用 AnnotationMethodMatcher 检查是否具有特定注解 AnnotationMethodMatcher annotationMethodMatcher = new AnnotationMethodMatcher(MyMethodAnnotation.class); System.out.println("annotationMethodMatcher matches = " + annotationMethodMatcher.matches(setNameMethod, target)); // 使用 AspectJExpressionPointcut 基于 AspectJ 表达式匹配方法 AspectJExpressionPointcut aspectJExpressionPointcut = new AspectJExpressionPointcut(); aspectJExpressionPointcut.setExpression("execution(* com.xcs.spring.MyService.*(..))"); System.out.println("aspectJExpressionPointcut matches = " + aspectJExpressionPointcut.matches(setNameMethod, target)); // 使用 NameMatchMethodPointcut 基于方法名称匹配方法 NameMatchMethodPointcut nameMatchMethodPointcut = new NameMatchMethodPointcut(); nameMatchMethodPointcut.setMappedName("setName"); System.out.println("nameMatchMethodPointcut matches = " + nameMatchMethodPointcut.matches(setNameMethod, target)); // 使用 JdkRegexpMethodPointcut 基于正则表达式匹配方法 JdkRegexpMethodPointcut jdkRegexpMethodPointcut = new JdkRegexpMethodPointcut(); jdkRegexpMethodPointcut.setPattern(".*set.*"); System.out.println("jdkRegexpMethodPointcut matches = " + jdkRegexpMethodPointcut.matches(setNameMethod, target)); } } ================================================ FILE: spring-aop/spring-aop-methodMatcher/src/main/java/com/xcs/spring/MyMethodAnnotation.java ================================================ package com.xcs.spring; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface MyMethodAnnotation { } ================================================ FILE: spring-aop/spring-aop-methodMatcher/src/main/java/com/xcs/spring/MyService.java ================================================ package com.xcs.spring; public class MyService { @MyMethodAnnotation public void setName() { System.out.println("setName..."); } } ================================================ FILE: spring-aop/spring-aop-pointcut/README.md ================================================ ## Pointcut - [Pointcut](#pointcut) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、接口源码](#四接口源码) - [五、主要实现](#五主要实现) - [六、类关系图](#六类关系图) - [七、最佳实践](#七最佳实践) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 `Pointcut` 接口主要用于定义切入点,即确定哪些方法应该被切面所影响。Pointcut 接口提供了匹配规则,以确定在哪些类的哪些方法上应用切面,以及在何种情况下应该应用切面。 ### 三、主要功能 1. **定义切入点** + Pointcut 接口用于定义切入点,即确定哪些方法应该被切面所影响。它允许我们指定在哪些类的哪些方法上应用切面。 2. **匹配规则** + 提供了匹配规则,以确定在哪些方法上应用切面。这些规则可以基于方法的名称、参数、返回类型、类名称等多种条件来定义,从而实现对切入点的精确定位。 3. **获取类过滤器** + `getClassFilter()` 方法用于获取一个 `ClassFilter` 对象,该对象用于确定哪些类应该被匹配。我们可以根据自己的需求自定义类过滤逻辑。 4. **获取方法匹配器** + `getMethodMatcher()` 方法用于获取一个 `MethodMatcher` 对象,该对象用于确定哪些方法应该被匹配。我们可以根据自己的需求自定义方法匹配逻辑。 ### 四、接口源码 `Pointcut`接口定义了 Spring AOP 中的切入点的核心抽象,由 `ClassFilter` 和 `MethodMatcher` 组成,分别用于确定哪些类和方法应该被匹配。通过这个接口,可以创建不同的切入点,并灵活地组合它们来定义复杂的切面。接口中还定义了一个常量 `TRUE`,代表始终匹配的切入点。 ```java /** * 核心的 Spring 切入点抽象。 * *

一个切入点由一个 {@link ClassFilter} 和一个 {@link MethodMatcher} 组成。 * 这两个基本术语以及一个切入点本身可以组合起来构建组合(例如通过 {@link org.springframework.aop.support.ComposablePointcut})。 * * @author Rod Johnson * @see ClassFilter * @see MethodMatcher * @see org.springframework.aop.support.Pointcuts * @see org.springframework.aop.support.ClassFilters * @see org.springframework.aop.support.MethodMatchers */ public interface Pointcut { /** * 返回此切入点的 ClassFilter。 * @return ClassFilter(永不为 {@code null}) */ ClassFilter getClassFilter(); /** * 返回此切入点的 MethodMatcher。 * @return MethodMatcher(永不为 {@code null}) */ MethodMatcher getMethodMatcher(); /** * 始终匹配的规范切入点实例。 */ Pointcut TRUE = TruePointcut.INSTANCE; } ``` ### 五、主要实现 1. **NameMatchMethodPointcut** + 根据方法名称匹配的切入点。可以配置指定的方法名称或通配符,以匹配目标类中的方法。 2. **JdkRegexpMethodPointcut** + 使用正则表达式匹配方法的切入点。可以使用正则表达式指定方法的匹配规则。 3. **AspectJExpressionPointcut** + 使用 AspectJ 切入点表达式匹配方法的切入点。可以使用 AspectJ 的语法来定义更灵活的切入点匹配规则。 4. **ComposablePointcut** + 可组合的切入点,允许将多个切入点组合起来使用,支持与、或、非等逻辑操作。 5. **StaticMethodMatcherPointcut** + 静态方法匹配器切入点,用于直接指定方法匹配规则,不支持动态匹配。 6. **TruePointcut** + 始终匹配的切入点,代表不进行任何匹配,即匹配所有的类和方法。 7. **AnnotationMatchingPointcut** + 用于基于注解匹配的切入点定义。它可以根据指定的注解类型匹配类或方法,并用于将通知应用于带有特定注解的目标对象的方法。 ### 六、类关系图 ~~~mermaid classDiagram direction BT class AbstractExpressionPointcut class AbstractRegexpMethodPointcut class AnnotationMatchingPointcut class AspectJExpressionPointcut class DynamicMethodMatcherPointcut class ExpressionPointcut { <> } class JdkRegexpMethodPointcut class NameMatchMethodPointcut class Pointcut { <> } class StaticMethodMatcherPointcut class TruePointcut AbstractExpressionPointcut ..> ExpressionPointcut AbstractRegexpMethodPointcut --> StaticMethodMatcherPointcut AnnotationMatchingPointcut ..> Pointcut AspectJExpressionPointcut --> AbstractExpressionPointcut DynamicMethodMatcherPointcut ..> Pointcut ExpressionPointcut --> Pointcut JdkRegexpMethodPointcut --> AbstractRegexpMethodPointcut NameMatchMethodPointcut --> StaticMethodMatcherPointcut StaticMethodMatcherPointcut ..> Pointcut TruePointcut ..> Pointcut ~~~ ### 七、最佳实践 **MyCustomPointcut** 使用自定义的 `Pointcut` 对象 `MyCustomPointcut`。在 `customPointcut` 方法中,我们创建了 `MyCustomPointcut` 的实例,并通过 `showMatchesLog` 方法展示了其对类和方法的匹配情况。最后,我们通过调用 `showMatchesLog` 方法来检查 `MyCustomPointcut` 对象对目标类 `MyService` 中的方法的匹配情况,并输出匹配结果。 ```java public class PointcutDemo { public static void main(String[] args) { customPointcut(); } /** * 自定义 Pointcut */ private static void customPointcut() { MyCustomPointcut pointcut = new MyCustomPointcut(); showMatchesLog(pointcut); } public static void showMatchesLog(Pointcut pointcut) { try { Class target = MyService.class; Method getNameMethod = target.getDeclaredMethod("getName"); Method getAgeMethod = target.getDeclaredMethod("getAge"); Method setNameMethod = target.getDeclaredMethod("setName"); ClassFilter classFilter = pointcut.getClassFilter(); MethodMatcher methodMatcher = pointcut.getMethodMatcher(); System.out.println("ClassFilter MyService = " + classFilter.matches(target)); System.out.println("MethodMatcher MyService getName = " + methodMatcher.matches(getNameMethod, target)); System.out.println("MethodMatcher MyService getAge = " + methodMatcher.matches(getAgeMethod, target)); System.out.println("MethodMatcher MyService setName = " + methodMatcher.matches(setNameMethod, target)); } catch (Exception e) { e.printStackTrace(); } } } ``` 自定义了一个自定义的切入点 `MyCustomPointcut`,该切入点匹配所有类,并且匹配所有方法名以 "get" 开头的方法。这意味着通过该切入点定义的切面将会拦截所有类的所有以 "get" 开头的方法调用。 ```java class MyCustomPointcut implements Pointcut { @Override public ClassFilter getClassFilter() { // 匹配所有类 return clazz -> true; } @Override public MethodMatcher getMethodMatcher() { return new MethodMatcher() { @Override public boolean matches(Method method, Class targetClass) { // 匹配所有以 "get" 开头的方法 return method.getName().startsWith("get"); } @Override public boolean isRuntime() { // 是否需要在运行时动态匹配 return false; } @Override public boolean matches(Method method, Class targetClass, Object... args) { // 运行时匹配,这里不需要,所以简单返回 false return false; } }; } } ``` `MyService` 类是一个示例服务类,标注了类级别的 `@MyClassAnnotation` 注解,其中包含了三个方法:`getName()`、`setName()` 和 `getAge()`。其中,`setName()` 方法标注了方法级别的 `@MyMethodAnnotation` 注解。 ```java @MyClassAnnotation public class MyService { public void getName() { System.out.println("getName..."); } @MyMethodAnnotation public void setName() { System.out.println("setName..."); } public void getAge() { System.out.println("getAge..."); } } ``` 运行结果,`MyService` 类级别的过滤器匹配成功,而在方法级别,`getName` 和 `getAge` 方法成功匹配,但 `setName` 方法未匹配成功。 ```java ClassFilter MyService = true MethodMatcher MyService getName = true MethodMatcher MyService getAge = true MethodMatcher MyService setName = false ``` **AspectJExpressionPointcut** 使用 `AspectJExpressionPointcut` 创建一个基于 AspectJ 表达式的切入点。在 `aspectJExpressionPointcut` 方法中,我们创建了 `AspectJExpressionPointcut` 的实例,并设置了 AspectJ 表达式 `"execution(* com.xcs.spring.MyService.get*())"`,该表达式匹配了 `com.xcs.spring.MyService` 类中以 `get` 开头的所有方法。最后,我们通过调用 `showMatchesLog` 方法来检查 `AspectJExpressionPointcut` 对象对指定类中的方法的匹配情况,并输出匹配结果。 ```java public class PointcutDemo { public static void main(String[] args) { aspectJExpressionPointcut(); } /** * AspectJExpressionPointcut */ private static void aspectJExpressionPointcut() { // 创建 AspectJ 表达式切入点 AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut(); pointcut.setExpression("execution(* com.xcs.spring.MyService.get*())"); showMatchesLog(pointcut); } } ``` 运行结果,`MyService` 类级别的过滤器匹配成功,而在方法级别,`getName` 和 `getAge` 方法成功匹配,但 `setName` 方法未匹配成功。 ```java ClassFilter MyService = true MethodMatcher MyService getName = true MethodMatcher MyService getAge = true MethodMatcher MyService setName = false ``` **AnnotationMatchingPointcut** 使用 `AnnotationMatchingPointcut` 创建一个基于注解匹配的切入点。在 `annotationMatchingPointcut` 方法中,我们创建了 `AnnotationMatchingPointcut` 的实例,并指定了类级别注解 `MyClassAnnotation` 和方法级别注解 `MyMethodAnnotation`,同时设置了不检查继承的方法。最后,我们通过调用 `showMatchesLog` 方法来检查 `AnnotationMatchingPointcut` 对象对指定类中的方法的匹配情况,并输出匹配结果。 ```java public class PointcutDemo { public static void main(String[] args) { annotationMatchingPointcut(); } /** * AnnotationMatchingPointcut */ private static void annotationMatchingPointcut() { // 使用AnnotationMatchingPointcut切入点 AnnotationMatchingPointcut pointcut = new AnnotationMatchingPointcut(MyClassAnnotation.class, MyMethodAnnotation.class, false); showMatchesLog(pointcut); } } ``` 运行结果,`MyService` 类级别的过滤器匹配成功,而方法级别的匹配器成功匹配了 `setName` 方法,但未匹配 `getName` 和 `getAge` 方法。 ```java ClassFilter MyService = true MethodMatcher MyService getName = false MethodMatcher MyService getAge = false MethodMatcher MyService setName = true ``` **NameMatchMethodPointcut** 使用 `NameMatchMethodPointcut` 创建一个基于方法名匹配的切入点。在 `nameMatchMethodPointcut` 方法中,我们创建了 `NameMatchMethodPointcut` 的实例,并添加了要匹配的方法名 `getAge`。然后,我们通过调用 `showMatchesLog` 方法来检查 `NameMatchMethodPointcut` 对象对指定类中的方法的匹配情况,并输出匹配结果。 ```java public class PointcutDemo { public static void main(String[] args) { nameMatchMethodPointcut(); } /** * AspectJExpressionPointcut */ private static void nameMatchMethodPointcut() { // 使用AnnotationMatchingPointcut切入点 NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut(); pointcut.addMethodName("getAge"); showMatchesLog(pointcut); } } ``` 运行结果, `MyService` 类级别的过滤器匹配成功,而方法级别的匹配器成功匹配了 `getAge` 方法,但未匹配 `getName` 和 `setName` 方法。 ```java ClassFilter MyService = true MethodMatcher MyService getName = false MethodMatcher MyService getAge = true MethodMatcher MyService setName = false ``` **JdkRegexpMethodPointcut** 使用 `JdkRegexpMethodPointcut` 创建一个基于 JDK 正则表达式匹配的切入点。在 `jdkRegexpMethodPointcut` 方法中,我们创建了 `JdkRegexpMethodPointcut` 的实例,并设置了正则表达式模式 `".*set.*"`,该模式匹配了所有包含 "set" 字符串的方法名。然后,我们通过调用 `showMatchesLog` 方法来检查 `JdkRegexpMethodPointcut` 对象对指定类中的方法的匹配情况,并输出匹配结果。 ```java public class PointcutDemo { public static void main(String[] args) { jdkRegexpMethodPointcut(); } /** * JdkRegexpMethodPointcut */ private static void jdkRegexpMethodPointcut() { JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut(); pointcut.setPattern(".*set.*"); showMatchesLog(pointcut); } } ``` 运行结果,`MyService` 类级别的过滤器匹配成功,而方法级别的匹配器成功匹配了 `setName` 方法,但未匹配 `getName` 和 `getAge` 方法。 ```java ClassFilter MyService = true MethodMatcher MyService getName = false MethodMatcher MyService getAge = false MethodMatcher MyService setName = true ``` ================================================ FILE: spring-aop/spring-aop-pointcut/pom.xml ================================================ com.xcs.spring spring-aop 0.0.1-SNAPSHOT 4.0.0 spring-aop-pointcut ================================================ FILE: spring-aop/spring-aop-pointcut/src/main/java/com/xcs/spring/MyClassAnnotation.java ================================================ package com.xcs.spring; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 定义自定义注解 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface MyClassAnnotation { } ================================================ FILE: spring-aop/spring-aop-pointcut/src/main/java/com/xcs/spring/MyCustomAdvice.java ================================================ package com.xcs.spring; import org.springframework.aop.MethodBeforeAdvice; import java.lang.reflect.Method; class MyCustomAdvice implements MethodBeforeAdvice { @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("Before advice is executed"); } } ================================================ FILE: spring-aop/spring-aop-pointcut/src/main/java/com/xcs/spring/MyCustomPointcut.java ================================================ package com.xcs.spring; import org.springframework.aop.ClassFilter; import org.springframework.aop.MethodMatcher; import org.springframework.aop.Pointcut; import java.lang.reflect.Method; class MyCustomPointcut implements Pointcut { @Override public ClassFilter getClassFilter() { // 匹配所有类 return clazz -> true; } @Override public MethodMatcher getMethodMatcher() { return new MethodMatcher() { @Override public boolean matches(Method method, Class targetClass) { // 匹配所有以 "get" 开头的方法 return method.getName().startsWith("get"); } @Override public boolean isRuntime() { // 是否需要在运行时动态匹配 return false; } @Override public boolean matches(Method method, Class targetClass, Object... args) { // 运行时匹配,这里不需要,所以简单返回 false return false; } }; } } ================================================ FILE: spring-aop/spring-aop-pointcut/src/main/java/com/xcs/spring/MyMethodAnnotation.java ================================================ package com.xcs.spring; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 定义自定义注解 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface MyMethodAnnotation { } ================================================ FILE: spring-aop/spring-aop-pointcut/src/main/java/com/xcs/spring/MyService.java ================================================ package com.xcs.spring; @MyClassAnnotation public class MyService { public void getName() { System.out.println("getName..."); } @MyMethodAnnotation public void setName() { System.out.println("setName..."); } public void getAge() { System.out.println("getAge..."); } } ================================================ FILE: spring-aop/spring-aop-pointcut/src/main/java/com/xcs/spring/PointcutDemo.java ================================================ package com.xcs.spring; import org.springframework.aop.ClassFilter; import org.springframework.aop.MethodMatcher; import org.springframework.aop.Pointcut; import org.springframework.aop.aspectj.AspectJExpressionPointcut; import org.springframework.aop.support.JdkRegexpMethodPointcut; import org.springframework.aop.support.NameMatchMethodPointcut; import org.springframework.aop.support.annotation.AnnotationMatchingPointcut; import java.lang.reflect.Method; /** * @author xcs * @date 2024年4月7日15:42:49 */ public class PointcutDemo { public static void main(String[] args) { customPointcut(); // aspectJExpressionPointcut(); // annotationMatchingPointcut(); // nameMatchMethodPointcut(); // jdkRegexpMethodPointcut(); } /** * 自定义 Pointcut */ private static void customPointcut() { MyCustomPointcut pointcut = new MyCustomPointcut(); showMatchesLog(pointcut); } /** * AspectJExpressionPointcut */ private static void aspectJExpressionPointcut() { // 创建 AspectJ 表达式切入点 AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut(); pointcut.setExpression("execution(* com.xcs.spring.MyService.get*())"); showMatchesLog(pointcut); } /** * AnnotationMatchingPointcut */ private static void annotationMatchingPointcut() { // 使用AnnotationMatchingPointcut切入点 AnnotationMatchingPointcut pointcut = new AnnotationMatchingPointcut(MyClassAnnotation.class, MyMethodAnnotation.class, false); showMatchesLog(pointcut); } /** * AspectJExpressionPointcut */ private static void nameMatchMethodPointcut() { // 使用AnnotationMatchingPointcut切入点 NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut(); pointcut.addMethodName("getAge"); showMatchesLog(pointcut); } /** * JdkRegexpMethodPointcut */ private static void jdkRegexpMethodPointcut() { JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut(); pointcut.setPattern(".*set.*"); showMatchesLog(pointcut); } public static void showMatchesLog(Pointcut pointcut) { try { Class target = MyService.class; Method getNameMethod = target.getDeclaredMethod("getName"); Method getAgeMethod = target.getDeclaredMethod("getAge"); Method setNameMethod = target.getDeclaredMethod("setName"); ClassFilter classFilter = pointcut.getClassFilter(); MethodMatcher methodMatcher = pointcut.getMethodMatcher(); System.out.println("ClassFilter MyService = " + classFilter.matches(target)); System.out.println("MethodMatcher MyService getName = " + methodMatcher.matches(getNameMethod, target)); System.out.println("MethodMatcher MyService getAge = " + methodMatcher.matches(getAgeMethod, target)); System.out.println("MethodMatcher MyService setName = " + methodMatcher.matches(setNameMethod, target)); } catch (Exception e) { e.printStackTrace(); } } } ================================================ FILE: spring-aop/spring-aop-proxyFactory/README.md ================================================ ## ProxyFactory - [ProxyFactory](#proxyfactory) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、类关系图](#四类关系图) - [五、最佳实践](#五最佳实践) - [六、源码分析](#六源码分析) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 `ProxyFactory`类是Spring AOP中的关键组件之一,用于动态创建代理对象并将切面逻辑织入到目标对象的方法调用中。通过设置目标对象、接口、通知等属性,`ProxyFactory`提供了一种便捷的方式来创建代理对象,实现面向切面编程的功能。 ### 三、主要功能 1. **创建代理对象** + 允许动态地创建代理对象,这些代理对象可以代表目标对象,并在方法调用前后执行额外的逻辑。 2. **切面逻辑织入** + 通过添加通知(Advice),`ProxyFactory`可以将切面逻辑织入到代理对象的方法调用中,实现横切关注点的处理。 3. **支持接口代理和类代理** + 支持基于接口的JDK动态代理和基于类的CGLIB代理,可以根据需要选择合适的代理方式。 4. **灵活的配置选项** + 提供了丰富的配置选项,如设置目标对象、接口、通知、代理方式等,可以根据具体需求进行灵活配置。 5. **简化AOP配置** + 简化了AOP配置的过程,通过简单的API调用,即可实现代理对象的创建和切面逻辑的织入,降低了AOP配置的复杂度。 ### 四、类关系图 ~~~mermaid classDiagram direction BT class Advised { <> } class AdvisedSupport class ProxyConfig class ProxyCreatorSupport class ProxyFactory class TargetClassAware { <> } Advised --> TargetClassAware AdvisedSupport ..> Advised AdvisedSupport --> ProxyConfig ProxyCreatorSupport --> AdvisedSupport ProxyFactory --> ProxyCreatorSupport ~~~ ### 五、最佳实践 使用`ProxyFactory`类来创建代理对象,并将前置通知(MethodBeforeAdvice)应用于目标对象的方法调用中。首先,通过`ProxyFactory`创建代理工厂,并指定目标对象为`MyService`类的实例。然后,添加前置通知`MyMethodBeforeAdvice`到代理工厂中。接着,调用`getProxy()`方法获取代理对象,并将其存储在`Object`类型的变量中。最后,打印出代理对象的类名。 ```java public class ProxyFactoryDemo { public static void main(String[] args) { // 创建代理工厂&创建目标对象 ProxyFactory proxyFactory = new ProxyFactory(new MyService()); // 创建通知 proxyFactory.addAdvice(new MyMethodBeforeAdvice()); // 获取代理对象 Object proxy = proxyFactory.getProxy(); // 调用代理对象的方法 System.out.println("proxy = " + proxy.getClass()); } } ``` 这个类可以作为一个目标对象,通过代理工厂创建代理对象,并在其方法调用前后应用额外的逻辑,实现面向切面编程的功能。 ```java public class MyService { } ``` 运行结果,表明代理对象是通过Spring的CGLIB动态代理生成的,它是`MyService`类的一个增强版本。 ```java proxy = class com.xcs.spring.MyService$$EnhancerBySpringCGLIB$$d9bdf44b ``` ### 六、源码分析 **初始化阶段** 在`org.springframework.aop.framework.ProxyCreatorSupport#ProxyCreatorSupport()`方法中,`ProxyCreatorSupport`类是`ProxyFactory`类的父类,因此当初始化`ProxyFactory`时,`ProxyCreatorSupport`也会跟着初始化,确保在创建代理对象时能够利用`ProxyCreatorSupport`的功能。在构造函数中,它初始化了`aopProxyFactory`成员变量,将其设置为一个`DefaultAopProxyFactory`对象,用于后续创建AOP代理对象。 ```java /** * 创建一个新的 ProxyCreatorSupport 实例。 */ public ProxyCreatorSupport() { this.aopProxyFactory = new DefaultAopProxyFactory(); } ``` 在`org.springframework.aop.framework.ProxyFactory#ProxyFactory(java.lang.Object)`方法中,构造函数接受一个目标对象作为参数,并将其设置为要被代理的目标对象。然后,通过调用`ClassUtils.getAllInterfaces(target)`方法获取目标对象实现的所有接口,并将它们设置为代理工厂要代理的接口。 ```java /** * 创建一个新的ProxyFactory。 *

将代理目标对象实现的所有接口。 * @param target 要被代理的目标对象 */ public ProxyFactory(Object target) { setTarget(target); setInterfaces(ClassUtils.getAllInterfaces(target)); } ``` 在`org.springframework.aop.framework.AdvisedSupport#setTarget`方法中,它接受一个对象作为参数,并将该对象设置为代理工厂的目标对象。在内部,它会使用`SingletonTargetSource`来包装目标对象,以确保每次获取代理时都返回相同的目标对象实例。 ```java /** * 将给定的对象设置为目标对象。 * 将为该对象创建一个 SingletonTargetSource。 * @see #setTargetSource * @see org.springframework.aop.target.SingletonTargetSource */ public void setTarget(Object target) { setTargetSource(new SingletonTargetSource(target)); } ``` 在`org.springframework.aop.framework.AdvisedSupport#setInterfaces`方法中,用于设置要被代理的接口。它接受一个可变参数`interfaces`,表示需要被代理的接口列表。在方法内部,首先通过`Assert.notNull(interfaces, "Interfaces must not be null")`断言确保传入的接口数组不为空。然后,清空当前`ProxyFactory`对象中已经设置的接口列表,并遍历传入的接口数组,逐个调用`addInterface(ifc)`方法将接口添加到接口列表中。这样做是为了确保在创建代理对象时,只代理指定的接口。 ```java /** * 设置要被代理的接口。 */ public void setInterfaces(Class... interfaces) { Assert.notNull(interfaces, "Interfaces must not be null"); this.interfaces.clear(); for (Class ifc : interfaces) { addInterface(ifc); } } ``` **创建代理阶段** 在`org.springframework.aop.framework.ProxyFactory#getProxy()`方法中,根据工厂中的配置创建一个新的代理对象。可以重复调用此方法,根据已添加或删除的接口以及添加或移除的拦截器的不同,其效果会有所变化。该方法会使用默认的类加载器,通常是线程上下文类加载器(如果需要代理创建时)。最终返回创建的代理对象。 [AopProxy源码分析](../spring-aop-aopProxy/README.md) ```java /** * 根据该工厂中的设置创建一个新的代理对象。 *

可以重复调用。如果已添加或删除接口,则效果会有所不同。可以添加和删除拦截器。 *

使用默认的类加载器:通常是线程上下文类加载器(如果需要创建代理)。 * @return 代理对象 */ public Object getProxy() { return createAopProxy().getProxy(); } ``` 在`org.springframework.aop.framework.ProxyCreatorSupport#createAopProxy`方法中,首先检查`active`标志,如果当前`ProxyFactory`对象未激活,则调用`activate()`方法进行激活。然后,通过调用`getAopProxyFactory().createAopProxy(this)`来获取AOP代理对象的工厂,并使用当前`ProxyFactory`对象作为参数来创建AOP代理。最终返回创建的AOP代理对象。 [AopProxyFactory源码分析](../spring-aop-aopProxyFactory/README.md) ```java /** * 子类应调用此方法以获取一个新的AOP代理。它们不应该使用 {@code this} 作为参数创建AOP代理。 */ protected final synchronized AopProxy createAopProxy() { if (!this.active) { activate(); } return getAopProxyFactory().createAopProxy(this); } ``` 在`org.springframework.aop.framework.ProxyCreatorSupport#getAopProxyFactory` 方法中,用于返回该代理配置所使用的AOP代理工厂(AopProxyFactory)。在`ProxyCreatorSupport()`构造方法中,`aopProxyFactory` 对象被初始化为`DefaultAopProxyFactory`的实例。因此,当调用`getAopProxyFactory()`方法时,将返回一个`DefaultAopProxyFactory` 对象,该对象用于创建AOP代理对象。 ```java /** * 返回该代理配置使用的AopProxyFactory。 */ public AopProxyFactory getAopProxyFactory() { return this.aopProxyFactory; } ``` ================================================ FILE: spring-aop/spring-aop-proxyFactory/pom.xml ================================================ com.xcs.spring spring-aop 0.0.1-SNAPSHOT 4.0.0 spring-aop-proxyFactory ================================================ FILE: spring-aop/spring-aop-proxyFactory/src/main/java/com/xcs/spring/MyService.java ================================================ package com.xcs.spring; public class MyService { } ================================================ FILE: spring-aop/spring-aop-proxyFactory/src/main/java/com/xcs/spring/ProxyFactoryDemo.java ================================================ package com.xcs.spring; import org.springframework.aop.framework.ProxyFactory; public class ProxyFactoryDemo { public static void main(String[] args) { // 创建代理工厂&创建目标对象 ProxyFactory proxyFactory = new ProxyFactory(new MyService()); // 获取代理对象 Object proxy = proxyFactory.getProxy(); // 调用代理对象的方法 System.out.println("proxy = " + proxy.getClass()); } } ================================================ FILE: spring-aop/spring-aop-proxyMethodInvocation/README.md ================================================ ## ProxyMethodInvocation - [ProxyMethodInvocation](#proxymethodinvocation) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、接口源码](#四接口源码) - [五、主要实现](#五主要实现) - [六、类关系图](#六类关系图) - [七、最佳实践](#七最佳实践) - [八、源码分析](#八源码分析) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 `ProxyMethodInvocation`是Spring AOP中的核心接口之一,用于表示代理方法调用,提供了获取方法、参数、目标对象等信息的方法,并允许拦截器链中的拦截器对方法调用进行处理,是实现方法拦截和增强逻辑的关键组件。 ### 三、主要功能 1. **获取方法信息** + 通过`getMethod()`方法可以获取当前正在调用的方法对象,包括方法名、参数类型等信息。 2. **获取方法参数** + 使用`getArguments()`方法可以获取方法调用时传递的参数数组,允许拦截器对参数进行处理或检查。 3. **执行下一个拦截器或目标方法** + 通过`proceed()`方法可以继续执行拦截器链中的下一个拦截器或者调用目标方法。如果`proceed()`方法不被调用,拦截器链可能会被终止,不会执行目标方法。 4. **获取目标对象** + `getThis()`方法允许获取被代理的目标对象,即被拦截的对象实例。 5. **获取代理对象** + 通过`getProxy()`方法获取执行当前方法调用的代理对象,这对于需要在拦截器中替换返回值为代理对象的情况非常有用。 ### 四、接口源码 `ProxyMethodInvocation`接口,它是AOP联盟 `MethodInvocation` 接口的扩展,允许访问通过方法调用所使用的代理对象。该接口提供了获取代理对象、创建方法调用的克隆、设置方法调用的参数、添加和获取用户属性等功能,用于在AOP环境中处理方法调用并进行增强。 ```java /** * 扩展了AOP联盟 {@link org.aopalliance.intercept.MethodInvocation} 接口, * 允许访问通过方法调用所使用的代理对象。 * *

如果需要的话,通过此接口可以方便地使用代理对象替换返回值, * 例如如果调用目标返回了自身对象。 * * @author Juergen Hoeller * @author Adrian Colyer * @since 1.1.3 * @see org.springframework.aop.framework.ReflectiveMethodInvocation * @see org.springframework.aop.support.DelegatingIntroductionInterceptor */ public interface ProxyMethodInvocation extends MethodInvocation { /** * 返回执行此方法调用的代理对象。 * @return 原始代理对象 */ Object getProxy(); /** * 创建此对象的克隆。如果在此对象上调用 {@code proceed()} 之前进行克隆, * 则每个克隆可以调用 {@code proceed()} 一次,以多次调用连接点(以及其余的通知链)。 * @return 此调用的可调用克隆。 * {@code proceed()} 可以每个克隆调用一次。 */ MethodInvocation invocableClone(); /** * 创建此对象的克隆,并指定克隆对象所使用的参数。如果在此对象上调用 {@code proceed()} 之前进行克隆, * 则每个克隆可以调用 {@code proceed()} 一次,以多次调用连接点(以及其余的通知链)。 * @param arguments 克隆调用所使用的参数,覆盖原始参数 * @return 此调用的可调用克隆。 * {@code proceed()} 可以每个克隆调用一次。 */ MethodInvocation invocableClone(Object... arguments); /** * 设置将在此链中的后续调用中使用的参数。 * @param arguments 参数数组 */ void setArguments(Object... arguments); /** * 向此方法调用添加指定的用户属性和给定的值。 *

这些属性在AOP框架内部不使用。它们只是作为调用对象的一部分保留, * 供特殊拦截器使用。 * @param key 属性的名称 * @param value 属性的值,如果要重置则传入 {@code null} */ void setUserAttribute(String key, @Nullable Object value); /** * 返回指定用户属性的值。 * @param key 属性的名称 * @return 属性的值,如果未设置则返回 {@code null} * @see #setUserAttribute */ @Nullable Object getUserAttribute(String key); } ``` ### 五、主要实现 1. **ReflectiveMethodInvocation** + 通过Java反射机制执行方法调用。当目标对象是基于接口的JDK动态代理时,Spring会使用`ReflectiveMethodInvocation`来处理方法调用。它具有通用性但性能较低,适用于代理接口类型的目标对象。 2. **CglibMethodInvocation** + 基于CGLIB动态代理生成子类来执行方法调用。当目标对象是基于类的CGLIB代理时,Spring会使用`CglibMethodInvocation`来处理方法调用。它通常比`ReflectiveMethodInvocation`性能更高,主要用于代理非接口类型的目标对象。 ### 六、类关系图 ~~~mermaid classDiagram direction BT class CglibMethodInvocation class Invocation { <> } class Joinpoint { <> } class MethodInvocation { <> } class ProxyMethodInvocation { <> } class ReflectiveMethodInvocation CglibMethodInvocation --> ReflectiveMethodInvocation Invocation --> Joinpoint MethodInvocation --> Invocation ProxyMethodInvocation --> MethodInvocation ReflectiveMethodInvocation ..> ProxyMethodInvocation ~~~ ### 七、最佳实践 使用Java动态代理创建代理对象并调用方法。首先,创建了一个目标对象 `MyService target = new MyServiceImpl()`,然后通过 `Proxy.newProxyInstance()` 方法创建了代理对象,指定了目标对象的类加载器、实现的接口以及调用处理器。最后,通过代理对象调用方法,实际上会触发调用处理器中的 `invoke()` 方法来执行额外的逻辑。 ```java public class ProxyMethodInvocationDemo { public static void main(String[] args) { // 创建目标对象 MyService target = new MyServiceImpl(); // 获取目标对象的类对象 Class clz = target.getClass(); // 创建代理对象,并指定目标对象的类加载器、实现的接口以及调用处理器 MyService proxyObject = (MyService) Proxy.newProxyInstance(clz.getClassLoader(), clz.getInterfaces(), new MyInvocationHandler(target)); // 通过代理对象调用方法,实际上会调用 MyInvocationHandler 中的 invoke 方法 proxyObject.foo(); } } ``` `MyInvocationHandler` 类实现了 `InvocationHandler` 接口,作为 Java 动态代理的调用处理器。在 `invoke()` 方法中,它接收代理对象、方法对象和方法参数,并使用这些信息创建一个 `MyReflectiveMethodInvocation` 对象,然后调用 `proceed()` 方法来执行方法调用链。这个类的目的是将方法调用转发给拦截器链处理,以实现额外的逻辑或增强功能。 ```java /** * 自定义的 InvocationHandler 实现类,用于处理 Java 动态代理的方法调用。 */ class MyInvocationHandler implements InvocationHandler { // 目标对象 private final Object target; /** * 构造方法,初始化目标对象。 * @param target 目标对象 */ public MyInvocationHandler(Object target) { this.target = target; } /** * 处理方法调用的核心方法。 * @param proxy 代理对象 * @param method 被调用的方法对象 * @param args 方法参数 * @return 方法调用结果 * @throws Throwable 可能抛出的异常 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 创建 MyReflectiveMethodInvocation 对象,用于执行方法调用链 MyReflectiveMethodInvocation invocation = new MyReflectiveMethodInvocation(proxy, target, method, args, target.getClass(), List.of(new MyMethodInterceptor())); // 执行方法调用链,并返回结果 return invocation.proceed(); } } ``` 我们自定义 `MyReflectiveMethodInvocation` 类是为了继承 Spring AOP 中的 `ReflectiveMethodInvocation` 并提供一个公开的构造方法。这样做允许你在自定义的方法调用对象中添加额外的逻辑或功能,并且可以在其它地方使用这个自定义的方法调用对象。 ```java /** * 自定义的方法调用对象,继承自 Spring AOP 的 ReflectiveMethodInvocation 类。 * 用于在方法调用中加入自定义逻辑或增强功能。 */ public class MyReflectiveMethodInvocation extends ReflectiveMethodInvocation { /** * 构造方法,初始化方法调用对象。 * @param proxy 代理对象 * @param target 目标对象 * @param method 被调用的方法对象 * @param arguments 方法参数 * @param targetClass 目标对象的类 * @param interceptorsAndDynamicMethodMatchers 拦截器链和动态方法匹配器列表 */ public MyReflectiveMethodInvocation(Object proxy, Object target, Method method, Object[] arguments, Class targetClass, List interceptorsAndDynamicMethodMatchers) { super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers); } } ``` `MyMethodInterceptor` 类用于实现方法拦截和增强的功能。在 `invoke()` 方法中,首先通过 `MethodInvocation` 对象获取被调用方法的信息,例如方法名等,并在方法调用之前输出方法被调用的信息。然后调用 `invocation.proceed()` 方法来执行原始方法,获取方法执行结果。最后并将其返回。 ```java public class MyMethodInterceptor implements MethodInterceptor { @Override public Object invoke(MethodInvocation invocation) throws Throwable { // 在方法调用之前执行的逻辑 System.out.println("Before Method " + invocation.getMethod().getName()); // 调用原始方法 Object result = invocation.proceed(); // 在方法调用之后执行的逻辑 System.out.println("After Method " + invocation.getMethod().getName()); return result; } } ``` 运行结果,在调用 `MyService` 实例的 `foo()` 方法时,`MyMethodInterceptor` 拦截器成功地拦截了方法的执行,并在方法执行前后添加了额外的逻辑处理。 ```java Before Method foo foo... After Method foo ``` ### 八、源码分析 在`org.springframework.aop.framework.ReflectiveMethodInvocation#ReflectiveMethodInvocation`方法中,`ReflectiveMethodInvocation`类的构造函数,用于创建一个反射方法调用对象。它接收代理对象、目标对象、要调用的方法、方法参数、目标类以及拦截器和动态方法匹配器列表作为参数,并在构造过程中对这些参数进行初始化。 ```java /** * 使用给定参数构造一个新的 ReflectiveMethodInvocation。 * @param proxy 调用所在的代理对象 * @param target 要调用的目标对象 * @param method 要调用的方法 * @param arguments 调用方法时传入的参数 * @param targetClass 目标类,用于方法匹配器的调用 * @param interceptorsAndDynamicMethodMatchers 应该应用的拦截器,以及需要在运行时进行评估的任何 InterceptorAndDynamicMethodMatchers。 * 此结构中包含的 MethodMatchers 必须已经被找到并匹配,尽可能地是静态的。传递一个数组可能会快约10%,但会使代码复杂化。并且它只能用于静态切入点。 */ protected ReflectiveMethodInvocation( Object proxy, @Nullable Object target, Method method, @Nullable Object[] arguments, @Nullable Class targetClass, List interceptorsAndDynamicMethodMatchers) { // 代理对象 this.proxy = proxy; // 目标对象 this.target = target; // 目标类 this.targetClass = targetClass; // 找到桥接方法 this.method = BridgeMethodResolver.findBridgedMethod(method); // 调整参数 this.arguments = AopProxyUtils.adaptArgumentsIfNecessary(method, arguments); // 拦截器和动态方法匹配器列表 this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers; } ``` 在`org.springframework.aop.framework.ReflectiveMethodInvocation#proceed`方法中,首先判断当前拦截器索引是否到达了拦截器链的末尾,如果是,则调用连接点方法;否则,获取下一个拦截器或拦截器与动态方法匹配器对象,并进行动态方法匹配。如果方法匹配成功,则调用拦截器的 `invoke()` 方法;如果方法匹配失败,则跳过当前拦截器并调用链中的下一个拦截器。如果获取的是拦截器对象,则直接调用拦截器的 `invoke()` 方法。这个方法负责在方法调用链中依次执行拦截器或目标方法,实现了方法调用链的顺序执行。 ```java /** * 执行拦截器链中的下一个拦截器或目标方法。 * @return 方法调用结果 * @throws Throwable 可能抛出的异常 */ @Override @Nullable public Object proceed() throws Throwable { // 我们从索引 -1 开始并提前递增。 if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { // 如果当前拦截器索引达到了拦截器链的末尾,则调用连接点方法。 return invokeJoinpoint(); } // 获取下一个拦截器或拦截器与动态方法匹配器对象 Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { // 如果是拦截器与动态方法匹配器,则进行动态方法匹配 InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; // 获取目标类对象,如果目标类对象不为空则使用目标类对象,否则使用方法的声明类对象 Class targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass()); if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) { // 如果方法匹配成功,则调用拦截器的invoke方法 return dm.interceptor.invoke(this); } else { // 动态匹配失败,跳过当前拦截器并调用链中的下一个拦截器 return proceed(); } } else { // 如果是拦截器,则直接调用拦截器的invoke方法 return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); } } ``` 在`org.springframework.aop.framework.ReflectiveMethodInvocation#invokeJoinpoint`方法中,使用反射调用连接点。 ```java /** * 使用反射调用连接点。 * 子类可以重写此方法以使用自定义调用。 * @return 连接点的返回值 * @throws Throwable 如果调用连接点导致异常 */ @Nullable protected Object invokeJoinpoint() throws Throwable { // 使用反射调用连接点 return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments); } ``` 在`org.springframework.aop.support.AopUtils#invokeJoinpointUsingReflection`方法中,通过反射调用目标方法,作为AOP方法调用的一部分。它接收目标对象、要调用的方法以及方法的参数作为输入,并尝试使用反射机制来调用方法。 ```java /** * 使用反射调用给定的目标方法,作为AOP方法调用的一部分。 * @param target 目标对象 * @param method 要调用的方法 * @param args 方法的参数 * @return 调用结果,如果有的话 * @throws Throwable 如果目标方法抛出异常 * @throws org.springframework.aop.AopInvocationException 如果发生反射错误 */ @Nullable public static Object invokeJoinpointUsingReflection(@Nullable Object target, Method method, Object[] args) throws Throwable { // 使用反射调用方法。 try { // 设置方法可访问性 ReflectionUtils.makeAccessible(method); // 调用方法 return method.invoke(target, args); } catch (InvocationTargetException ex) { // 调用的方法抛出了已检查的异常。 // 我们必须重新抛出它。客户端不会看到拦截器。 throw ex.getTargetException(); } catch (IllegalArgumentException ex) { // 如果发生参数错误,则抛出AOP调用异常 throw new AopInvocationException("AOP configuration seems to be invalid: tried calling method [" + method + "] on target [" + target + "]", ex); } catch (IllegalAccessException ex) { // 如果无法访问方法,则抛出AOP调用异常 throw new AopInvocationException("Could not access method [" + method + "]", ex); } } ``` ================================================ FILE: spring-aop/spring-aop-proxyMethodInvocation/pom.xml ================================================ com.xcs.spring spring-aop 0.0.1-SNAPSHOT 4.0.0 spring-aop-proxyMethodInvocation ================================================ FILE: spring-aop/spring-aop-proxyMethodInvocation/src/main/java/com/xcs/spring/MyInvocationHandler.java ================================================ package com.xcs.spring; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.util.List; /** * 自定义的 InvocationHandler 实现类,用于处理 Java 动态代理的方法调用。 */ class MyInvocationHandler implements InvocationHandler { // 目标对象 private final Object target; /** * 构造方法,初始化目标对象。 * @param target 目标对象 */ public MyInvocationHandler(Object target) { this.target = target; } /** * 处理方法调用的核心方法。 * @param proxy 代理对象 * @param method 被调用的方法对象 * @param args 方法参数 * @return 方法调用结果 * @throws Throwable 可能抛出的异常 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 创建 MyReflectiveMethodInvocation 对象,用于执行方法调用链 MyReflectiveMethodInvocation invocation = new MyReflectiveMethodInvocation(proxy, target, method, args, target.getClass(), List.of(new MyMethodInterceptor())); // 执行方法调用链,并返回结果 return invocation.proceed(); } } ================================================ FILE: spring-aop/spring-aop-proxyMethodInvocation/src/main/java/com/xcs/spring/MyMethodInterceptor.java ================================================ package com.xcs.spring; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class MyMethodInterceptor implements MethodInterceptor { @Override public Object invoke(MethodInvocation invocation) throws Throwable { // 在方法调用之前执行的逻辑 System.out.println("Before Method " + invocation.getMethod().getName()); // 调用原始方法 Object result = invocation.proceed(); // 在方法调用之后执行的逻辑 System.out.println("After Method " + invocation.getMethod().getName()); return result; } } ================================================ FILE: spring-aop/spring-aop-proxyMethodInvocation/src/main/java/com/xcs/spring/MyReflectiveMethodInvocation.java ================================================ package com.xcs.spring; import org.springframework.aop.framework.ReflectiveMethodInvocation; import java.lang.reflect.Method; import java.util.List; /** * 自定义的方法调用对象,继承自 Spring AOP 的 ReflectiveMethodInvocation 类。 * 用于在方法调用中加入自定义逻辑或增强功能。 */ public class MyReflectiveMethodInvocation extends ReflectiveMethodInvocation { /** * 构造方法,初始化方法调用对象。 * @param proxy 代理对象 * @param target 目标对象 * @param method 被调用的方法对象 * @param arguments 方法参数 * @param targetClass 目标对象的类 * @param interceptorsAndDynamicMethodMatchers 拦截器链和动态方法匹配器列表 */ public MyReflectiveMethodInvocation(Object proxy, Object target, Method method, Object[] arguments, Class targetClass, List interceptorsAndDynamicMethodMatchers) { super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers); } } ================================================ FILE: spring-aop/spring-aop-proxyMethodInvocation/src/main/java/com/xcs/spring/MyService.java ================================================ package com.xcs.spring; public interface MyService { void foo(); } ================================================ FILE: spring-aop/spring-aop-proxyMethodInvocation/src/main/java/com/xcs/spring/MyServiceImpl.java ================================================ package com.xcs.spring; public class MyServiceImpl implements MyService { @Override public void foo() { System.out.println("foo..."); } } ================================================ FILE: spring-aop/spring-aop-proxyMethodInvocation/src/main/java/com/xcs/spring/ProxyMethodInvocationDemo.java ================================================ package com.xcs.spring; import java.lang.reflect.Proxy; public class ProxyMethodInvocationDemo { public static void main(String[] args) { // 创建目标对象 MyService target = new MyServiceImpl(); // 获取目标对象的类对象 Class clz = target.getClass(); // 创建代理对象,并指定目标对象的类加载器、实现的接口以及调用处理器 MyService proxyObject = (MyService) Proxy.newProxyInstance(clz.getClassLoader(), clz.getInterfaces(), new MyInvocationHandler(target)); // 通过代理对象调用方法,实际上会调用 MyInvocationHandler 中的 invoke 方法 proxyObject.foo(); } } ================================================ FILE: spring-aop/spring-aop-targetSource/README.md ================================================ ## TargetSource - [TargetSource](#targetsource) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、接口源码](#四接口源码) - [五、主要实现](#五主要实现) - [六、类关系图](#六类关系图) - [七、最佳实践](#七最佳实践) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 `TargetSource` 接口是 Spring AOP 框架中的一个关键组件,用于定义获取目标对象的策略,允许我们灵活地管理目标对象的创建和管理。通过实现该接口,可以实现各种目标对象的获取方式,如单例、原型、池化等,从而为 AOP 切面提供了更高度的可定制性和灵活性。 ### 三、主要功能 1. **目标对象获取方法** + `getTarget()`,该方法根据具体的策略获取目标对象,如单例、原型、池化等。 2. **目标对象释放方法** + `releaseTarget(Object target)`,一些实现可能需要释放目标对象,如将对象返回到对象池中。 3. **灵活的目标对象管理** + 可以根据应用程序的需求实现自定义的目标对象获取策略,从而实现对目标对象的灵活管理。 4. **内置的目标源实现** + Spring AOP 提供了几种内置的 `TargetSource` 实现,如 `SingletonTargetSource`、`PrototypeTargetSource`、`ThreadLocalTargetSource` 等,可以根据具体情况选择合适的目标源实现。 5. **扩展性和定制性** + 我们可以通过实现 `TargetSource` 接口来实现自定义的目标源,从而满足特定场景下的需求,如基于线程的对象管理、对象池管理等。 ### 四、接口源码 `TargetSource` 接口用于获取当前 AOP 调用的目标对象,通过 `getTarget()` 方法获取目标对象,并通过 `releaseTarget(Object target)` 方法释放目标对象。接口定义了 `getTargetClass()` 方法用于返回目标对象的类型,`isStatic()` 方法用于检查是否所有调用 `getTarget()` 方法的返回值都是相同的对象。此接口支持静态和动态目标源,静态目标源始终返回相同的目标对象,而动态目标源支持池化、热交换等功能。 ```java /** * TargetSource 接口用于获取 AOP 调用的当前目标对象,如果没有环绕通知选择结束拦截器链,则将通过反射调用目标对象。 * *

如果 TargetSource 是 "static",它将始终返回相同的目标对象,从而允许 AOP 框架进行优化。动态目标源可以支持池化、热交换等。 * *

应用程序开发人员通常不需要直接使用 TargetSources:这是一个 AOP 框架接口。 * * @author Rod Johnson * @author Juergen Hoeller */ public interface TargetSource extends TargetClassAware { /** * 返回此 TargetSource 返回的目标类型。 *

可以返回 {@code null},尽管某些 TargetSource 的用法可能只使用预定的目标类。 * * @return 此 TargetSource 返回的目标类型 */ @Override @Nullable Class getTargetClass(); /** * 所有对 getTarget() 的调用是否将返回相同的对象? *

在这种情况下,不需要调用 releaseTarget(Object),并且 AOP 框架可以缓存 getTarget() 的返回值。 * * @return 如果目标是不可变的,则为 true * @see #getTarget */ boolean isStatic(); /** * 返回一个目标实例。在 AOP 框架调用 AOP 方法调用的目标之前立即调用此方法。 * * @return 包含连接点的目标对象,如果没有实际目标实例,则为 {@code null} * @throws Exception 如果无法解析目标对象 */ @Nullable Object getTarget() throws Exception; /** * 释放从 getTarget() 方法获取的给定目标对象(如果有)。 * * @param target 从调用 getTarget() 获取的对象 * @throws Exception 如果无法释放对象 */ void releaseTarget(Object target) throws Exception; } ``` ### 五、主要实现 1. **SingletonTargetSource** + 用于管理单例对象的目标源。该实现每次调用 `getTarget()` 方法都返回同一个单例对象,适用于目标对象是单例的情况。 2. **PrototypeTargetSource** + 用于每次调用时创建新对象的目标源。该实现每次调用 `getTarget()` 方法都返回一个新的目标对象实例,适用于目标对象需要频繁更新或重置的情况。 3. **ThreadLocalTargetSource** + 用于在每个线程中保持一个目标对象的引用。该实现在每个线程中都维护一个目标对象的副本,适用于需要在多线程环境中使用不同的目标对象实例的情况。 4. **CommonsPool2TargetSource** + 用于使用 Apache Commons Pool 来管理目标对象的池化目标源。该实现通过对象池管理目标对象的创建和销毁,以提高对象的重用性和性能。 ### 六、类关系图 ~~~mermaid classDiagram direction BT class CommonsPool2TargetSource class PrototypeTargetSource class SingletonTargetSource class TargetClassAware { <> } class TargetSource { <> } class ThreadLocalTargetSource CommonsPool2TargetSource ..> TargetSource PrototypeTargetSource ..> TargetSource SingletonTargetSource ..> TargetSource TargetSource --> TargetClassAware ThreadLocalTargetSource ..> TargetSource ~~~ ### 七、最佳实践 使用 Spring 的代理工厂(`ProxyFactory`)和目标源(`TargetSource`)来创建代理对象。在这个示例中,我们创建了一个连接池目标源(`ConnectionPoolTargetSource`),设置连接池的大小为 3。然后,我们将这个连接池目标源设置为代理工厂的目标源,并通过代理工厂获取代理对象。最后,我们通过代理对象调用了10次方法。 ```java public class TargetSourceDemo { public static void main(String[] args) { // 创建代理工厂 ProxyFactory proxyFactory = new ProxyFactory(); // 设置目标源为连接池目标源,连接池大小为3 proxyFactory.setTargetSource(new ConnectionPoolTargetSource(3)); // 获取代理对象 MyConnection proxy = (MyConnection) proxyFactory.getProxy(); // 调用10次方法 for (int i = 0; i < 10; i++) { System.out.println("MyConnection Name = " + proxy.getName()); } } } ``` `ConnectionPoolTargetSource` 类实现了 Spring 的 `TargetSource` 接口,用于管理自定义连接对象的连接池。在构造函数中,它会初始化一个固定大小的阻塞队列作为连接池,并填充连接对象。通过实现 `getTarget()` 方法,它能够从连接池中获取连接对象,并在 `releaseTarget()` 方法中释放连接对象。 ```java /** * 连接池目标源,用于管理自定义连接对象的连接池。 * * @author xcs * @date 2024年4月9日15:26:28 */ public class ConnectionPoolTargetSource implements TargetSource { /** * 连接池 */ private final BlockingQueue connectionPool; /** * 构造函数,初始化连接池。 * * @param poolSize 连接池大小 */ public ConnectionPoolTargetSource(int poolSize) { this.connectionPool = new ArrayBlockingQueue<>(poolSize); initializeConnectionPool(poolSize); } /** * 初始化连接池,填充连接对象。 * * @param poolSize 连接池大小 */ private void initializeConnectionPool(int poolSize) { for (int i = 0; i < poolSize; i++) { MyConnection connection = new MyConnection("Connection" + i); connectionPool.offer(connection); } } /** * 获取目标类的类型。 * * @return 目标类的类型 */ @Override public Class getTargetClass() { return MyConnection.class; } /** * 判断目标对象是否是静态的。 * * @return 如果目标对象是静态的,则返回true,否则返回false */ @Override public boolean isStatic() { return false; } /** * 获取连接对象。 * * @return 连接对象 * @throws Exception 如果获取连接对象时发生异常 */ @Override public Object getTarget() throws Exception { return connectionPool.take(); } /** * 释放连接对象。 * * @param target 待释放的连接对象 * @throws Exception 如果释放连接对象时发生异常 */ @Override public void releaseTarget(Object target) throws Exception { if (target instanceof MyConnection) { connectionPool.offer((MyConnection) target); } } } ``` `MyConnection` 类代表了一个自定义的连接对象。 ```java public class MyConnection { private String name; public MyConnection(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "MyConnection{" + "name='" + name + '\'' + '}'; } } ``` 运行结果,连接池会循环地提供连接对象,直到连接池中的所有连接对象都被使用过一次后,再重新开始循环。这与预期的连接池行为一致,确保了连接对象的复用和管理。 ```java MyConnection Name = Connection0 MyConnection Name = Connection1 MyConnection Name = Connection2 MyConnection Name = Connection0 MyConnection Name = Connection1 MyConnection Name = Connection2 MyConnection Name = Connection0 MyConnection Name = Connection1 MyConnection Name = Connection2 MyConnection Name = Connection0 ``` ================================================ FILE: spring-aop/spring-aop-targetSource/pom.xml ================================================ com.xcs.spring spring-aop 0.0.1-SNAPSHOT 4.0.0 spring-aop-targetSource ================================================ FILE: spring-aop/spring-aop-targetSource/src/main/java/com/xcs/spring/ConnectionPoolTargetSource.java ================================================ package com.xcs.spring; import org.springframework.aop.TargetSource; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; /** * 连接池目标源,用于管理自定义连接对象的连接池。 * * @author xcs * @date 2024年4月9日15:26:28 */ public class ConnectionPoolTargetSource implements TargetSource { /** * 连接池 */ private final BlockingQueue connectionPool; /** * 构造函数,初始化连接池。 * * @param poolSize 连接池大小 */ public ConnectionPoolTargetSource(int poolSize) { this.connectionPool = new ArrayBlockingQueue<>(poolSize); initializeConnectionPool(poolSize); } /** * 初始化连接池,填充连接对象。 * * @param poolSize 连接池大小 */ private void initializeConnectionPool(int poolSize) { for (int i = 0; i < poolSize; i++) { MyConnection connection = new MyConnection("Connection" + i); connectionPool.offer(connection); } } /** * 获取目标类的类型。 * * @return 目标类的类型 */ @Override public Class getTargetClass() { return MyConnection.class; } /** * 判断目标对象是否是静态的。 * * @return 如果目标对象是静态的,则返回true,否则返回false */ @Override public boolean isStatic() { return false; } /** * 获取连接对象。 * * @return 连接对象 * @throws Exception 如果获取连接对象时发生异常 */ @Override public Object getTarget() throws Exception { return connectionPool.take(); } /** * 释放连接对象。 * * @param target 待释放的连接对象 * @throws Exception 如果释放连接对象时发生异常 */ @Override public void releaseTarget(Object target) throws Exception { if (target instanceof MyConnection) { connectionPool.offer((MyConnection) target); } } } ================================================ FILE: spring-aop/spring-aop-targetSource/src/main/java/com/xcs/spring/MyConnection.java ================================================ package com.xcs.spring; public class MyConnection { private String name; public MyConnection(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "MyConnection{" + "name='" + name + '\'' + '}'; } } ================================================ FILE: spring-aop/spring-aop-targetSource/src/main/java/com/xcs/spring/TargetSourceDemo.java ================================================ package com.xcs.spring; import org.springframework.aop.framework.ProxyFactory; public class TargetSourceDemo { public static void main(String[] args) { // 创建代理工厂 ProxyFactory proxyFactory = new ProxyFactory(); // 设置目标源为连接池目标源,连接池大小为3 proxyFactory.setTargetSource(new ConnectionPoolTargetSource(3)); // 获取代理对象 MyConnection proxy = (MyConnection) proxyFactory.getProxy(); // 调用10次方法 for (int i = 0; i < 10; i++) { System.out.println("MyConnection Name = " + proxy.getName()); } } } ================================================ FILE: spring-aop/spring-aop-targetSourceCreator/README.md ================================================ ## TargetSourceCreator - [TargetSourceCreator](#targetsourcecreator) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、接口源码](#四接口源码) - [五、主要实现](#五主要实现) - [六、类关系图](#六类关系图) - [七、最佳实践](#七最佳实践) - [八、源码分析](#八源码分析) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 `TargetSourceCreator`接口,主要用于创建目标对象的代理。通过实现该接口,你可以自定义代理对象的创建逻辑,例如根据不同的条件返回不同的代理对象。这在AOP(面向切面编程)和代理模式中非常有用,可以灵活地控制代理对象的生成过程。 ### 三、主要功能 1. **创建代理对象的目标源(TargetSource)** + 该接口允许我们定义创建代理对象的逻辑,包括决定何时创建代理对象以及如何创建代理对象的目标源。 2. **定制代理对象的创建过程** + 通过实现该接口,你可以根据需要定制代理对象的创建过程。这包括根据不同的条件返回不同的目标源,或者在创建代理对象之前或之后执行特定的逻辑。 3. **支持AOP的灵活配置** + 在Spring框架中,AOP(面向切面编程)经常使用代理对象来实现横切关注点。`TargetSourceCreator`接口允许我们灵活地控制代理对象的生成过程,从而为AOP提供了更高的定制性和灵活性。 ### 四、接口源码 `TargetSourceCreator`接口,它允许实现类创建特殊的目标源(TargetSource),例如池化目标源,用于特定的bean。实现类可以基于目标类的属性,如池化属性,来决定选择哪种类型的目标源。`AbstractAutoProxyCreator`可以支持多个`TargetSourceCreators`,它们将按顺序应用,为Spring框架中的代理对象创建提供了灵活性和定制性。 ```java /** * 实现类可以为特定的bean创建特殊的目标源,例如池化目标源。例如,它们可以根据目标类上的属性(例如池化属性)来选择目标源。 * *

AbstractAutoProxyCreator 可以支持多个 TargetSourceCreators,它们将按顺序应用。 * * @author Rod Johnson * @author Juergen Hoeller */ @FunctionalInterface public interface TargetSourceCreator { /** * 为给定的bean创建一个特殊的目标源(如果有的话)。 * @param beanClass bean的类 * @param beanName bean的名称 * @return 特殊的目标源,如果此 TargetSourceCreator 不感兴趣于特定的bean,则返回 {@code null} */ @Nullable TargetSource getTargetSource(Class beanClass, String beanName); } ``` ### 五、主要实现 1. **QuickTargetSourceCreator** + 用于快速创建目标源。它适用于那些不需要延迟加载的情况,通过特定的条件或策略,可以快速地生成目标源,以提高性能或满足其他需求。 2. **LazyInitTargetSourceCreator** + 用于延迟创建目标源。它适用于需要延迟加载的场景,以减少启动时间或资源占用。根据特定的条件或策略,它会延迟地创建目标源,直到被请求时才进行加载。 ### 六、类关系图 ~~~mermaid classDiagram direction BT class AbstractBeanFactoryBasedTargetSourceCreator class LazyInitTargetSourceCreator class QuickTargetSourceCreator class TargetSourceCreator { <> } AbstractBeanFactoryBasedTargetSourceCreator ..> TargetSourceCreator LazyInitTargetSourceCreator --> AbstractBeanFactoryBasedTargetSourceCreator QuickTargetSourceCreator --> AbstractBeanFactoryBasedTargetSourceCreator ~~~ ### 七、最佳实践 使用Spring框架中的注解配置来创建应用程序上下文,并从上下文中获取`MyConnection` bean。然后,它打印了`MyConnection`实例的类名,并循环调用了`MyConnection`实例的`getName()`方法来获取实例的名称并打印输出。 ```java public class TargetSourceCreatorDemo { public static void main(String[] args) { // 创建一个基于注解的应用程序上下文 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); // 从上下文中获取 MyConnection bean MyConnection myConnection = context.getBean(MyConnection.class); // 打印 MyConnection 实例的类名 System.out.println("MyConnection Class = " + myConnection.getClass()); // 循环调用 MyConnection 实例的 getName() 方法 for (int i = 0; i < 10; i++) { // 打印 MyConnection 实例的名称 System.out.println("MyConnection Name = " + myConnection.getName()); } } } ``` 通过`@EnableAspectJAutoProxy`注解启用了AspectJ自动代理功能,允许Spring框架创建和管理切面(Aspects)。同时,通过`@Configuration`注解标识这是一个配置类,并使用`@ComponentScan("com.xcs.spring")`注解来指定需要扫描的包,以便Spring能够自动装配Bean和发现组件。 ```java @EnableAspectJAutoProxy @Configuration @ComponentScan("com.xcs.spring") public class AppConfig { } ``` `SetMyTargetSourceCreator`类实现了Spring框架的`BeanPostProcessor`接口和`PriorityOrdered`接口。在`postProcessAfterInitialization`方法中,通过判断bean是否为`AbstractAutoProxyCreator`的实例,然后为其设置了自定义的目标源创建器`MyTargetSourceCreator`。通过实现`PriorityOrdered`接口并重写`getOrder`方法,确保了该后置处理器具有最高的优先级,以确保在其他后置处理器之前执行。 ```java @Component public class SetMyTargetSourceCreator implements BeanPostProcessor , PriorityOrdered { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof AbstractAutoProxyCreator) { ((AbstractAutoProxyCreator) bean).setCustomTargetSourceCreators(new MyTargetSourceCreator()); } return bean; } @Override public int getOrder() { return Ordered.HIGHEST_PRECEDENCE; } } ``` `MyTargetSourceCreator`类实现了`TargetSourceCreator`接口。在`getTargetSource`方法中,它根据传入的bean类和bean名称来判断是否需要为特定的bean创建目标源。如果传入的bean类是`MyConnection`类或其子类,它将返回一个具有连接池功能的目标源`ConnectionPoolTargetSource`,并设置连接池的大小为3。 ```java public class MyTargetSourceCreator implements TargetSourceCreator { @Override public TargetSource getTargetSource(Class beanClass, String beanName) { if (MyConnection.class.isAssignableFrom(beanClass)) { return new ConnectionPoolTargetSource(3); } return null; } } ``` `ConnectionPoolTargetSource` 类实现了 Spring 的 `TargetSource` 接口,用于管理自定义连接对象的连接池。在构造函数中,它会初始化一个固定大小的阻塞队列作为连接池,并填充连接对象。通过实现 `getTarget()` 方法,它能够从连接池中获取连接对象,并在 `releaseTarget()` 方法中释放连接对象。 ```java /** * 连接池目标源,用于管理自定义连接对象的连接池。 * * @author xcs * @date 2024年4月9日15:26:28 */ public class ConnectionPoolTargetSource implements TargetSource { /** * 连接池 */ private final BlockingQueue connectionPool; /** * 构造函数,初始化连接池。 * * @param poolSize 连接池大小 */ public ConnectionPoolTargetSource(int poolSize) { this.connectionPool = new ArrayBlockingQueue<>(poolSize); initializeConnectionPool(poolSize); } /** * 初始化连接池,填充连接对象。 * * @param poolSize 连接池大小 */ private void initializeConnectionPool(int poolSize) { for (int i = 0; i < poolSize; i++) { MyConnection connection = new MyConnection("Connection" + i); connectionPool.offer(connection); } } /** * 获取目标类的类型。 * * @return 目标类的类型 */ @Override public Class getTargetClass() { return MyConnection.class; } /** * 判断目标对象是否是静态的。 * * @return 如果目标对象是静态的,则返回true,否则返回false */ @Override public boolean isStatic() { return false; } /** * 获取连接对象。 * * @return 连接对象 * @throws Exception 如果获取连接对象时发生异常 */ @Override public Object getTarget() throws Exception { return connectionPool.take(); } /** * 释放连接对象。 * * @param target 待释放的连接对象 * @throws Exception 如果释放连接对象时发生异常 */ @Override public void releaseTarget(Object target) throws Exception { if (target instanceof MyConnection) { connectionPool.offer((MyConnection) target); } } } ``` `MyConnection` 类代表了一个自定义的连接对象。 ```java public class MyConnection { private String name; public MyConnection(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "MyConnection{" + "name='" + name + '\'' + '}'; } } ``` 运行结果,`MyTargetSourceCreator`类成功地为`MyConnection`类创建了一个连接池目标源。Spring框架通过CGLIB动态代理增强了`MyConnection`类,使其能够使用连接池功能。每次调用`getName()`方法时,都从连接池中获取连接,并返回连续的"Connection0"、"Connection1"和"Connection2"字符串,表明连接池的大小为3且连接名称在连接池中循环使用。 ```java MyConnection Class = class com.xcs.spring.MyConnection$$EnhancerBySpringCGLIB$$fb2fa879 MyConnection Name = Connection0 MyConnection Name = Connection1 MyConnection Name = Connection2 MyConnection Name = Connection0 MyConnection Name = Connection1 MyConnection Name = Connection2 MyConnection Name = Connection0 MyConnection Name = Connection1 MyConnection Name = Connection2 MyConnection Name = Connection0 ``` ### 八、源码分析 在`org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessBeforeInstantiation`方法中,在Bean实例化之前进行处理。首先,它检查缓存中是否存在目标Bean的信息,如果存在则直接返回null,否则继续执行。然后,它检查Bean是否是基础设施类或是否应该被跳过,如果是,则将其标记为不需要增强,并返回null。最后,如果存在自定义的目标源(TargetSource),则创建代理对象,并使用自定义的目标源处理目标实例,从而避免不必要的默认实例化过程。 ```java @Override public Object postProcessBeforeInstantiation(Class beanClass, String beanName) { // 获取缓存键 Object cacheKey = getCacheKey(beanClass, beanName); // 如果bean名称为空或不在目标源bean列表中,且缓存中存在该键,则返回null if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) { if (this.advisedBeans.containsKey(cacheKey)) { return null; } // 如果bean类是基础设施类或应跳过,则将其标记为不需要增强,并返回null if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); return null; } } // 如果存在自定义的目标源,则在此处创建代理: // 避免不必要的目标bean默认实例化: // 目标源将以自定义方式处理目标实例。 TargetSource targetSource = getCustomTargetSource(beanClass, beanName); if (targetSource != null) { // 如果bean名称不为空,则将其添加到目标源bean列表中 if (StringUtils.hasLength(beanName)) { this.targetSourcedBeans.add(beanName); } // 获取适用于bean的特定拦截器和增强器 Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource); // 创建代理对象 Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource); // 将代理对象的类与缓存键关联 this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; } return null; } ``` 在`org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#getCustomTargetSource`方法中,根据bean的类和名称创建目标源(TargetSource)。如果设置了自定义的TargetSourceCreators,并且bean工厂中包含了指定名称的bean,则会尝试使用这些TargetSourceCreators来创建目标源。方法会遍历所有的TargetSourceCreators,调用它们的getTargetSource方法来获取目标源。如果找到了匹配的目标源,则返回该目标源;否则返回null。 ```java /** * 为bean实例创建目标源。如果设置了任何TargetSourceCreators,则使用它们。 * 如果不应使用自定义的TargetSource,则返回{@code null}。 *

此实现使用"customTargetSourceCreators"属性。 * 子类可以重写此方法以使用不同的机制。 * @param beanClass bean的类 * @param beanName bean的名称 * @return 用于此bean的目标源 * @see #setCustomTargetSourceCreators */ @Nullable protected TargetSource getCustomTargetSource(Class beanClass, String beanName) { // 对于直接注册的单例bean,我们无法创建复杂的目标源。 if (this.customTargetSourceCreators != null && this.beanFactory != null && this.beanFactory.containsBean(beanName)) { // 遍历所有的TargetSourceCreators for (TargetSourceCreator tsc : this.customTargetSourceCreators) { // 通过TargetSourceCreator获取目标源 TargetSource ts = tsc.getTargetSource(beanClass, beanName); // 如果找到匹配的目标源,则返回 if (ts != null) { // 找到了匹配的目标源。 if (logger.isTraceEnabled()) { logger.trace("TargetSourceCreator [" + tsc + "] found custom TargetSource for bean with name '" + beanName + "'"); } return ts; } } } // 没有找到自定义的目标源。 return null; } ``` ================================================ FILE: spring-aop/spring-aop-targetSourceCreator/pom.xml ================================================ com.xcs.spring spring-aop 0.0.1-SNAPSHOT 4.0.0 spring-aop-targetSourceCreator ================================================ FILE: spring-aop/spring-aop-targetSourceCreator/src/main/java/com/xcs/spring/AppConfig.java ================================================ package com.xcs.spring; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; @EnableAspectJAutoProxy @Configuration @ComponentScan("com.xcs.spring") public class AppConfig { } ================================================ FILE: spring-aop/spring-aop-targetSourceCreator/src/main/java/com/xcs/spring/ConnectionPoolTargetSource.java ================================================ package com.xcs.spring; import org.springframework.aop.TargetSource; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; /** * 连接池目标源,用于管理自定义连接对象的连接池。 * * @author xcs * @date 2024年4月9日15:26:28 */ public class ConnectionPoolTargetSource implements TargetSource { /** * 连接池 */ private final BlockingQueue connectionPool; /** * 构造函数,初始化连接池。 * * @param poolSize 连接池大小 */ public ConnectionPoolTargetSource(int poolSize) { this.connectionPool = new ArrayBlockingQueue<>(poolSize); initializeConnectionPool(poolSize); } /** * 初始化连接池,填充连接对象。 * * @param poolSize 连接池大小 */ private void initializeConnectionPool(int poolSize) { for (int i = 0; i < poolSize; i++) { MyConnection connection = new MyConnection("Connection" + i); connectionPool.offer(connection); } } /** * 获取目标类的类型。 * * @return 目标类的类型 */ @Override public Class getTargetClass() { return MyConnection.class; } /** * 判断目标对象是否是静态的。 * * @return 如果目标对象是静态的,则返回true,否则返回false */ @Override public boolean isStatic() { return false; } /** * 获取连接对象。 * * @return 连接对象 * @throws Exception 如果获取连接对象时发生异常 */ @Override public Object getTarget() throws Exception { return connectionPool.take(); } /** * 释放连接对象。 * * @param target 待释放的连接对象 * @throws Exception 如果释放连接对象时发生异常 */ @Override public void releaseTarget(Object target) throws Exception { if (target instanceof MyConnection) { connectionPool.offer((MyConnection) target); } } } ================================================ FILE: spring-aop/spring-aop-targetSourceCreator/src/main/java/com/xcs/spring/MyConnection.java ================================================ package com.xcs.spring; import org.springframework.stereotype.Service; @Service public class MyConnection { private String name; public MyConnection(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "MyConnection{" + "name='" + name + '\'' + '}'; } } ================================================ FILE: spring-aop/spring-aop-targetSourceCreator/src/main/java/com/xcs/spring/MyTargetSourceCreator.java ================================================ package com.xcs.spring; import org.springframework.aop.TargetSource; import org.springframework.aop.framework.autoproxy.TargetSourceCreator; public class MyTargetSourceCreator implements TargetSourceCreator { @Override public TargetSource getTargetSource(Class beanClass, String beanName) { if (MyConnection.class.isAssignableFrom(beanClass)) { return new ConnectionPoolTargetSource(3); } return null; } } ================================================ FILE: spring-aop/spring-aop-targetSourceCreator/src/main/java/com/xcs/spring/SetMyTargetSourceCreator.java ================================================ package com.xcs.spring; import org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.core.Ordered; import org.springframework.core.PriorityOrdered; import org.springframework.stereotype.Component; @Component public class SetMyTargetSourceCreator implements BeanPostProcessor , PriorityOrdered { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof AbstractAutoProxyCreator) { ((AbstractAutoProxyCreator) bean).setCustomTargetSourceCreators(new MyTargetSourceCreator()); } return bean; } @Override public int getOrder() { return Ordered.HIGHEST_PRECEDENCE; } } ================================================ FILE: spring-aop/spring-aop-targetSourceCreator/src/main/java/com/xcs/spring/TargetSourceCreatorDemo.java ================================================ package com.xcs.spring; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class TargetSourceCreatorDemo { public static void main(String[] args) { // 创建一个基于注解的应用程序上下文 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); // 从上下文中获取 MyConnection bean MyConnection myConnection = context.getBean(MyConnection.class); // 打印 MyConnection 实例的类名 System.out.println("MyConnection Class = " + myConnection.getClass()); // 循环调用 MyConnection 实例的 getName() 方法 for (int i = 0; i < 10; i++) { // 打印 MyConnection 实例的名称 System.out.println("MyConnection Name = " + myConnection.getName()); } } } ================================================ FILE: spring-aware/pom.xml ================================================ spring-reading com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-aware pom spring-aware-beanNameAware spring-aware-beanFactoryAware spring-aware-beanClassLoaderAware spring-aware-applicationContextAware spring-aware-applicationEventPublisherAware spring-aware-environmentAware spring-aware-resourceLoaderAware spring-aware-embeddedValueResolverAware spring-aware-messageSourceAware spring-aware-applicationStartupAware spring-aware-importAware ================================================ FILE: spring-aware/spring-aware-applicationContextAware/README.md ================================================ ## ApplicationContextAware - [ApplicationContextAware](#applicationcontextaware) - [一、基本信息](#一基本信息) - [二、接口描述](#二接口描述) - [三、接口源码](#三接口源码) - [四、主要功能](#四主要功能) - [五、最佳实践](#五最佳实践) - [六、时序图](#六时序图) - [七、源码分析](#七源码分析) - [八、注意事项](#八注意事项) - [九、总结](#九总结) - [最佳实践总结](#最佳实践总结) - [源码分析总结](#源码分析总结) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [我的CSDN](https://blog.csdn.net/duzhuang2399/article/details/133914136) 📚 **文章目录** - [所有文章](https://github.com/xuchengsheng/spring-reading) 🔗 **源码地址** - [ApplicationContextAware源码](https://github.com/xuchengsheng/spring-reading/tree/master/spring-aware/spring-aware-applicationContextAware) ### 二、接口描述 `ApplicationContextAware` 接口,允许我们访问当前的应用上下文 (`ApplicationContext`)。这通常在某些Spring bean需要访问应用上下文本身或其内部其他bean时会有用。 ### 三、接口源码 实现`ApplicationContextAware`接口的对象会在Spring容器中被自动注入一个`ApplicationContext`实例。 ```java /** * 该接口应由希望接收其运行的 ApplicationContext 通知的任何对象实现。 * * 例如,当对象需要访问一组合作的bean时,实现此接口是有意义的。 * 注意,通过bean引用进行配置优于仅为查找bean的目的而实现此接口。 * * 如果对象需要访问文件资源(即希望调用getResource), * 想要发布一个应用事件,或需要访问MessageSource,也可以实现此接口。 * 但在这种特定情况下,最好实现更为特定的 ResourceLoaderAware, * ApplicationEventPublisherAware 或 MessageSourceAware 接口。 * * 注意,文件资源依赖也可以作为类型为 org.springframework.core.io.Resource 的bean属性暴露, * 通过字符串填充,由bean工厂自动进行类型转换。这样就不需要为访问特定文件资源而实现任何回调接口了。 * * org.springframework.context.support.ApplicationObjectSupport 是应用对象的便利基类, * 它实现了此接口。 * * 关于所有bean生命周期方法的列表,请参阅 * org.springframework.beans.factory.BeanFactory BeanFactory的javadocs。 * * @author Rod Johnson * @author Juergen Hoeller * @author Chris Beams * @see ResourceLoaderAware * @see ApplicationEventPublisherAware * @see MessageSourceAware * @see org.springframework.context.support.ApplicationObjectSupport * @see org.springframework.beans.factory.BeanFactoryAware */ public interface ApplicationContextAware extends Aware { /** * 设置此对象运行的 ApplicationContext。 * 此调用通常用于初始化对象。 * 此方法在普通bean属性被填充之后调用,但在诸如 org.springframework.beans.factory.InitializingBean#afterPropertiesSet() * 这样的初始化回调或自定义初始化方法之前调用。 * 在 ResourceLoaderAware#setResourceLoader, * ApplicationEventPublisherAware#setApplicationEventPublisher 和 * MessageSourceAware 之后调用(如果适用)。 * * @param applicationContext 要由此对象使用的 ApplicationContext 对象 * @throws ApplicationContextException 如果上下文初始化出错 * @throws BeansException 如果应用上下文方法抛出异常 * @see org.springframework.beans.factory.BeanInitializationException */ void setApplicationContext(ApplicationContext applicationContext) throws BeansException; } ``` ### 四、主要功能 1. **动态查找其他Beans** + 尽管我们通常使用依赖注入来获取其他beans的引用,但在某些动态或复杂情况下,bean可能需要在运行时查找其他beans。 2. **发布事件** + 通过 `ApplicationContext`,bean可以发布应用级事件,这些事件可以被其他beans捕获和处理,这是实现松耦合交互的一种方法。 3. **资源加载** + `ApplicationContext` 扩展了 `ResourceLoader`,因此bean可以使用它来加载外部资源,如文件或URL。 4. **访问消息源** + 对于支持国际化的应用程序,bean可以通过 `ApplicationContext` 访问消息源,从而解析特定的消息。 5. **访问其他应用上下文服务** + 除了上述功能,`ApplicationContext` 还提供了其他一些服务,例如与JNDI交互、访问应用的环境属性等。 ### 五、最佳实践 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。 ```java public class ApplicationContextAwareApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); } } ``` 这里使用`@Bean`注解,定义了一个Bean,是为了确保 `MyApplicationContextAware` 被 Spring 容器执行 ```java @Configuration public class MyConfiguration { @Bean public MyApplicationContextAware myApplicationContextAware(){ return new MyApplicationContextAware(); } } ``` `MyApplicationContextAware` 的实现,它实现了 `ApplicationContextAware` 接口。 ```java public class MyApplicationContextAware implements ApplicationContextAware { @Override public void setApplicationContext(ApplicationContext context) throws BeansException { System.out.println("实现ApplicationContextAware接口,自动调用setApplicationContext方法"); System.out.println("ApplicationContext = " + context); } } ``` 运行结果发现,Spring 容器确实自动调用了 `setApplicationContext` 方法并传递了 `ApplicationContext` 对象。 ```java 实现ApplicationContextAware接口,自动调用setApplicationContext方法 ApplicationContext = org.springframework.context.annotation.AnnotationConfigApplicationContext@64bf3bbf ``` ### 六、时序图 ~~~mermaid sequenceDiagram Title: BeanClassLoaderAware时序图 participant ApplicationContextAwareApplication participant AnnotationConfigApplicationContext participant AbstractApplicationContext participant DefaultListableBeanFactory participant AbstractBeanFactory participant DefaultSingletonBeanRegistry participant AbstractAutowireCapableBeanFactory participant ApplicationContextAwareProcessor participant MyApplicationContextAware ApplicationContextAwareApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)
创建上下文 AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()
刷新上下文 AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory)
初始化Bean工厂 AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons()
实例化单例 DefaultListableBeanFactory->>AbstractBeanFactory:getBean(name)
获取Bean AbstractBeanFactory->>AbstractBeanFactory:doGetBean(name,requiredType,args,typeCheckOnly)
执行获取Bean AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton(beanName,singletonFactory)
获取单例Bean DefaultSingletonBeanRegistry-->>AbstractBeanFactory:getObject()
获取Bean实例 AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean(beanName,mbd,args)
创建Bean AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:doCreateBean(beanName,mbd,args)
执行Bean创建 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:initializeBean(beanName,bean,mbd)
负责bean的初始化 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:applyBeanPostProcessorsBeforeInitialization(existingBean, beanName)
调用前置处理器 AbstractAutowireCapableBeanFactory->>ApplicationContextAwareProcessor:postProcessBeforeInitialization(bean,beanName)
触发Aware处理 ApplicationContextAwareProcessor->>ApplicationContextAwareProcessor:invokeAwareInterfaces(bean)
执行Aware回调 ApplicationContextAwareProcessor->>MyApplicationContextAware:setApplicationContext(context)
设置应用上下文 AbstractAutowireCapableBeanFactory-->>AbstractBeanFactory:返回Bean对象 AbstractBeanFactory-->>DefaultListableBeanFactory:返回Bean对象 AnnotationConfigApplicationContext-->>ApplicationContextAwareApplication:初始化完成 ~~~ ### 七、源码分析 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyApplicationContextAware`类型的bean,最后调用`publish`方法用于发布一个事件。 ```java public class ApplicationContextAwareApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MyApplicationContextAware contextAware = context.getBean(MyApplicationContextAware.class); contextAware.publish("hello world"); } } ``` 在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法 ```java public AnnotationConfigApplicationContext(Class... componentClasses) { this(); register(componentClasses); refresh(); ``` 在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 ```java @Override public void refresh() throws BeansException, IllegalStateException { // ... [代码部分省略以简化] // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization`方法中,会继续调用`DefaultListableBeanFactory`类中的`preInstantiateSingletons`方法来完成所有剩余非懒加载的单列Bean对象。 ```java /** * 完成此工厂的bean初始化,实例化所有剩余的非延迟初始化单例bean。 * * @param beanFactory 要初始化的bean工厂 */ protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { // ... [代码部分省略以简化] // 完成所有剩余非懒加载的单列Bean对象。 beanFactory.preInstantiateSingletons(); } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons`方法中,主要的核心目的是预先实例化所有非懒加载的单例bean。在Spring的上下文初始化完成后,该方法会被触发,以确保所有单例bean都被正确地创建并初始化。其中`getBean(beanName)`是此方法的核心操作。对于容器中定义的每一个单例bean,它都会调用`getBean`方法,这将触发bean的实例化、初始化及其依赖的注入。如果bean之前没有被创建过,那么这个调用会导致其被实例化和初始化。 ```java public void preInstantiateSingletons() throws BeansException { // ... [代码部分省略以简化] // 循环遍历所有bean的名称 for (String beanName : beanNames) { getBean(beanName); } // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#getBean()`方法中,又调用了`doGetBean`方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,`doGetBean`负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。 ```java @Override public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中,首先检查所请求的bean是否是一个单例并且已经创建。如果尚未创建,它将创建一个新的实例。在这个过程中,它处理可能的异常情况,如循环引用,并确保返回的bean是正确的类型。这是Spring容器bean生命周期管理的核心部分。 ```java protected T doGetBean( String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException { // ... [代码部分省略以简化] // 开始创建bean实例 if (mbd.isSingleton()) { // 如果bean是单例的,我们会尝试从单例缓存中获取它 // 如果不存在,则使用lambda创建一个新的实例 sharedInstance = getSingleton(beanName, () -> { try { // 尝试创建bean实例 return createBean(beanName, mbd, args); } catch (BeansException ex) { // ... [代码部分省略以简化] } }); // 对于某些bean(例如FactoryBeans),可能需要进一步处理以获取真正的bean实例 beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } // ... [代码部分省略以简化] // 确保返回的bean实例与请求的类型匹配 return adaptBeanInstance(name, beanInstance, requiredType); } ``` 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()`方法中,主要负责从单例缓存中获取一个已存在的bean实例,或者使用提供的`ObjectFactory`创建一个新的实例。这是确保bean在Spring容器中作为单例存在的关键部分。 ```java public Object getSingleton(String beanName, ObjectFactory singletonFactory) { // 断言bean名称不能为空 Assert.notNull(beanName, "Bean name must not be null"); // 同步访问单例对象缓存,确保线程安全 synchronized (this.singletonObjects) { // 从缓存中获取单例对象 Object singletonObject = this.singletonObjects.get(beanName); // 如果缓存中没有找到 if (singletonObject == null) { // ... [代码部分省略以简化] try { // 使用工厂创建新的单例实例 singletonObject = singletonFactory.getObject(); newSingleton = true; } catch (IllegalStateException ex) { // ... [代码部分省略以简化] } catch (BeanCreationException ex) { // ... [代码部分省略以简化] } finally { // ... [代码部分省略以简化] } // ... [代码部分省略以简化] } // 返回单例对象 return singletonObject; } } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()`方法中,主要的逻辑是调用 `doCreateBean`,这是真正进行 bean 实例化、属性填充和初始化的地方。这个方法会返回新创建的 bean 实例。 ```java @Override protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // ... [代码部分省略以简化] try { // 正常的bean实例化、属性注入和初始化。 // 这里是真正进行bean创建的部分。 Object beanInstance = doCreateBean(beanName, mbdToUse, args); // 记录bean成功创建的日志 if (logger.isTraceEnabled()) { logger.trace("Finished creating instance of bean '" + beanName + "'"); } return beanInstance; } catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { // ... [代码部分省略以简化] } catch (Throwable ex) { // ... [代码部分省略以简化] } } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中,`initializeBean`方法是bean初始化,确保bean是完全配置和准备好的。 ```java protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // 声明一个对象,后续可能用于存放初始化后的bean或它的代理对象 Object exposedObject = bean; // ... [代码部分省略以简化] try { // ... [代码部分省略以简化] // bean初始化 exposedObject = initializeBean(beanName, exposedObject, mbd); } catch (Throwable ex) { // ... [代码部分省略以简化] } // 返回创建和初始化后的bean return exposedObject; } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean`方法中,如果条件满足(即 bean 不是合成的),那么它会调用 `applyBeanPostProcessorsBeforeInitialization` 方法。这个方法是 Spring 生命周期中的一个关键点,它会遍历所有已注册的 `BeanPostProcessor` 实现,并调用它们的 `postProcessBeforeInitialization` 方法。这允许我们和内部处理器在 bean 初始化之前对其进行修改或执行其他操作。 ```java protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { // ... [代码部分省略以简化] Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } // ... [代码部分省略以简化] return wrappedBean; } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization`方法中,遍历每一个 `BeanPostProcessor` 的 `postProcessBeforeInitialization` 方法都有机会对bean进行修改或增强 ```java @Override public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; for (BeanPostProcessor processor : getBeanPostProcessors()) { Object current = processor.postProcessBeforeInitialization(result, beanName); if (current == null) { return result; } result = current; } return result; } ``` 在`org.springframework.context.support.ApplicationContextAwareProcessor#postProcessBeforeInitialization`方法中,在这个方法的实现特别关注那些实现了 "aware" 接口的 beans,并为它们提供所需的运行环境信息。 ```java @Override @Nullable public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware || bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware || bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware || bean instanceof ApplicationStartupAware)) { return bean; } // ... [代码部分省略以简化] invokeAwareInterfaces(bean); return bean; } ``` 在`org.springframework.context.support.ApplicationContextAwareProcessor#invokeAwareInterfaces`方法中,用于处理实现了"Aware"接口的beans。这些接口使得beans能够被自动"感知"并获得对其运行环境或特定依赖的引用,而不需要显式地进行查找或注入。 ```java private void invokeAwareInterfaces(Object bean) { // ... [代码部分省略以简化] // 对ApplicationContextAware接口进行回调 if (bean instanceof ApplicationContextAware) { ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext); } } ``` 最后执行到我们自定义的逻辑中,使用 `ApplicationContextAware` 来获取 `ApplicationContext` 的引用。 ```java public class MyApplicationContextAware implements ApplicationContextAware { @Override public void setApplicationContext(ApplicationContext context) throws BeansException { System.out.println("实现ApplicationContextAware接口,自动调用setApplicationContext方法"); System.out.println("ApplicationContext = " + context); } } ``` ### 八、注意事项 1. **记住生命周期** + 当我们实现 `ApplicationContextAware` 时,记住上下文是在 bean 的生命周期的一个特定点被注入的。这通常是在属性注入后、初始化方法前。 2. **记住上下文层次结构** + 在更复杂的应用中,可能会有多个 `ApplicationContext` 层次结构(例如,一个根上下文和一个或多个子上下文)。确保我们了解从哪个上下文检索 beans,以及这些 beans 的生命周期和可见性。 3. **小心与懒加载 beans 的交互** + 如果我们使用 `ApplicationContextAware` 来动态检索一个定义为懒加载的 bean,那么这将导致该 bean 被立即初始化。 4. **避免创建循环依赖** + 如果使用 `ApplicationContext` 来动态查找 beans,要确保不会创建意外的循环依赖。 5. **避免在构造函数中使用 ApplicationContext** + 当 bean 实现 `ApplicationContextAware` 时,`setApplicationContext` 方法是在 bean 的属性注入之后、初始化方法(如 `afterPropertiesSet` 或自定义的 init 方法)之前调用的。因此,不应该试图在构造函数中访问 `ApplicationContext`,因为它在那时可能还没有被设置。 ### 九、总结 #### 最佳实践总结 1. **启动与配置**: - 在 `ApplicationContextAwareApplication` 的 `main` 方法中,我们使用 `AnnotationConfigApplicationContext` 来启动 Spring 容器,这是一个基于 Java 注解的配置方式。 - `MyConfiguration` 类被指定为配置类,这意味着 Spring 将会查找这个类中定义的 beans 和配置。 2. **Bean 定义**: - 在 `MyConfiguration` 配置类中,我们使用 `@Bean` 注解定义了一个 `MyApplicationContextAware` 类型的 bean。这确保 `MyApplicationContextAware` 将被 Spring 容器管理。 3. **实现 Aware 接口**: - `MyApplicationContextAware` 类实现了 `ApplicationContextAware` 接口。这是一个特殊的接口,当一个 bean 实现它,Spring 容器会在 bean 初始化时自动调用 `setApplicationContext` 方法并传入当前的 `ApplicationContext` 对象。 4. **运行结果**: - 当应用启动时,Spring 容器确实检测到 `MyApplicationContextAware` 实现了 `ApplicationContextAware` 接口,并自动调用了 `setApplicationContext` 方法。 - 控制台上的输出明确显示了这个过程,并显示了传递给该方法的 `ApplicationContext` 实例。 5. **结论**: - 通过 `ApplicationContextAware` 接口,我们可以轻松地在 Spring 容器中管理的 bean 中获取 `ApplicationContext`。这为我们提供了一个强大的机制,使得 bean 可以感知其所在的环境,并据此执行相应的操作。 #### 源码分析总结 1. **启动与上下文初始化** + 使用 `AnnotationConfigApplicationContext` 启动 Spring 应用,并传递 `MyConfiguration` 作为参数进行上下文初始化。 2. **Spring 上下文刷新** + 在 `refresh()` 方法中,主要关注点是调用 `finishBeanFactoryInitialization(beanFactory)`,负责实例化所有非延迟加载的单例 bean。 3. **Bean 实例化** + `preInstantiateSingletons()` 方法在 Spring 上下文初始化完成后被调用,确保所有非延迟加载的单例 beans 都被实例化,对于每个 bean,都会调用 `getBean(beanName)`,这会触发 bean 的实例化、初始化以及依赖注入。 4. **Bean 创建与初始化** + 在 `doCreateBean` 方法中,核心操作是调用 `initializeBean` 进行 bean 初始化,确保 bean 完全配置并准备好,`initializeBean` 中会调用 `applyBeanPostProcessorsBeforeInitialization`,在 bean 初始化之前遍历所有已注册的 `BeanPostProcessor`。 5. **处理 Aware 接口** + `ApplicationContextAwareProcessor` 的作用是对实现了 "Aware" 接口的 beans 进行特殊处理。在 `invokeAwareInterfaces` 方法中,针对不同的 "Aware" 接口进行了处理,使得 beans 可以自动感知其运行环境或特定依赖。 ================================================ FILE: spring-aware/spring-aware-applicationContextAware/pom.xml ================================================ spring-aware com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-aware-applicationContextAware ================================================ FILE: spring-aware/spring-aware-applicationContextAware/src/main/java/com/xcs/spring/ApplicationContextAwareApplication.java ================================================ package com.xcs.spring; import com.xcs.spring.config.MyApplicationContextAware; import com.xcs.spring.config.MyConfiguration; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年09月16日 16时09分 **/ public class ApplicationContextAwareApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); } } ================================================ FILE: spring-aware/spring-aware-applicationContextAware/src/main/java/com/xcs/spring/config/MyApplicationContextAware.java ================================================ package com.xcs.spring.config; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; public class MyApplicationContextAware implements ApplicationContextAware { @Override public void setApplicationContext(ApplicationContext context) throws BeansException { System.out.println("实现ApplicationContextAware接口,自动调用setApplicationContext方法"); System.out.println("ApplicationContext = " + context); } } ================================================ FILE: spring-aware/spring-aware-applicationContextAware/src/main/java/com/xcs/spring/config/MyConfiguration.java ================================================ package com.xcs.spring.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @author xcs * @date 2023年09月19日 16时35分 **/ @Configuration public class MyConfiguration { @Bean public MyApplicationContextAware myApplicationContextAware(){ return new MyApplicationContextAware(); } } ================================================ FILE: spring-aware/spring-aware-applicationEventPublisherAware/README.md ================================================ ## ApplicationEventPublisherAware - [ApplicationEventPublisherAware](#applicationeventpublisheraware) - [一、基本信息](#一基本信息) - [二、接口描述](#二接口描述) - [三、接口源码](#三接口源码) - [四、主要功能](#四主要功能) - [五、最佳实践](#五最佳实践) - [六、时序图](#六时序图) - [七、源码分析](#七源码分析) - [八、注意事项](#八注意事项) - [九、总结](#九总结) - [最佳实践总结](#最佳实践总结) - [源码分析总结](#源码分析总结) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [我的CSDN](https://blog.csdn.net/duzhuang2399/article/details/133914254) 📚 **文章目录** - [所有文章](https://github.com/xuchengsheng/spring-reading) 🔗 **源码地址** - [ApplicationEventPublisherAware源码](https://github.com/xuchengsheng/spring-reading/tree/master/spring-aware/spring-aware-applicationEventPublisherAware) ### 二、接口描述 `ApplicationEventPublisherAware` 接口,用于给需要发布应用事件的bean提供一个便捷的方式。实现此接口的bean可以接收到一个 `ApplicationEventPublisher` 的引用,这样它们就可以发布事件到 Spring 应用上下文中。 ### 三、接口源码 `ApplicationEventPublisherAware` 是 Spring 框架自 1.1.1 开始引入的一个核心接口。实现`ApplicationEventPublisherAware`接口的对象会在Spring容器中被自动注入一个`ApplicationEventPublisher`(通常是 `ApplicationContext`)实例。 ```java /** * 任何希望被通知 ApplicationEventPublisher(通常是 ApplicationContext)的对象都应该实现的接口。 * 当对象运行在某个 ApplicationEventPublisher 中时,它将被通知。 * * @author Juergen Hoeller * @author Chris Beams * @since 1.1.1 * @see ApplicationContextAware */ public interface ApplicationEventPublisherAware extends Aware { /** * 设置此对象运行的 ApplicationEventPublisher。 * 此方法在正常bean属性填充之后被调用,但在init回调(如 InitializingBean 的 afterPropertiesSet 或自定义的 init-method)之前被调用。 * 并且在 ApplicationContextAware 的 setApplicationContext 之前被调用。 * * @param applicationEventPublisher 由这个对象使用的事件发布器 */ void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher); } ``` ### 四、主要功能 1. **事件发布能力** + 它允许 Spring beans 获得事件发布的能力,使它们能够发布事件到 Spring 应用上下文中。 2. **回调机制** + 当一个 bean 实现了 `ApplicationEventPublisherAware` 接口时,Spring 容器会自动注入 `ApplicationEventPublisher` 实例到该 bean 中。 3. **与 ApplicationContext 的关联** + 通常,所注入的 `ApplicationEventPublisher` 实例实际上就是 `ApplicationContext` 本身,这意味着 beans 可以使用它来发布事件。 ### 五、最佳实践 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyApplicationEventPublisherAware`类型的bean,最后调用`publish`方法用于发布一个事件。 ```java public class ApplicationEventPublisherAwareApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MyApplicationEventPublisherAware publisherAware = context.getBean(MyApplicationEventPublisherAware.class); publisherAware.publish("hello world"); } } ``` 这里使用`@Bean`注解,定义了两个Bean,是为了确保`MyEventListener`, `MyApplicationEventPublisherAware` 被 Spring 容器执行 ```java @Configuration public class MyConfiguration { @Bean public MyApplicationEventPublisherAware myApplicationEventPublisherAware(){ return new MyApplicationEventPublisherAware(); } @Bean public MyEventListener MyEventListener(){ return new MyEventListener(); } } ``` `MyApplicationContextAware` 类使用 `ApplicationEventPublisherAware` 来获取 `ApplicationEventPublisher` 的引用,并使用这个引用来发布自定义事件。 ```java public class MyApplicationEventPublisherAware implements ApplicationEventPublisherAware { private ApplicationEventPublisher publisher; @Override public void setApplicationEventPublisher(ApplicationEventPublisher publisher) { this.publisher = publisher; } public void publish(String message) { publisher.publishEvent(new MyEvent(this, message)); } } ``` `MyEvent` 是我们自定义的 Spring 应用事件,用于传递一个字符串消息。 ```java public class MyEvent extends ApplicationEvent { private final String message; public MyEvent(Object source, String message) { super(source); this.message = message; } public String getMessage() { return message; } } ``` `MyEventListener` 是一个监听器。当 `MyEvent` 事件被发布时,此监听器会自动被触发,执行 `onApplicationEvent` 方法的逻辑。 ```java public class MyEventListener implements ApplicationListener { @Override public void onApplicationEvent(MyEvent event) { System.out.println("Received my event - " + event.getMessage()); } } ``` 运行结果发现,这表示监听器成功地捕获了该事件并处理了它。 ```java Received my event - hello world ``` ### 六、时序图 ~~~mermaid sequenceDiagram Title: ApplicationEventPublisherAware时序图 participant ApplicationEventPublisherAwareApplication participant AnnotationConfigApplicationContext participant AbstractApplicationContext participant DefaultListableBeanFactory participant AbstractBeanFactory participant DefaultSingletonBeanRegistry participant AbstractAutowireCapableBeanFactory participant ApplicationContextAwareProcessor participant MyApplicationEventPublisherAware ApplicationEventPublisherAwareApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)
创建上下文 AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()
刷新上下文 AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory)
初始化Bean工厂 AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons()
实例化单例 DefaultListableBeanFactory->>AbstractBeanFactory:getBean(name)
获取Bean AbstractBeanFactory->>AbstractBeanFactory:doGetBean(name,requiredType,args,typeCheckOnly)
执行获取Bean AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton(beanName,singletonFactory)
获取单例Bean DefaultSingletonBeanRegistry-->>AbstractBeanFactory:getObject()
获取Bean实例 AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean(beanName,mbd,args)
创建Bean AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:doCreateBean(beanName,mbd,args)
执行Bean创建 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:initializeBean(beanName,bean,mbd)
负责bean的初始化 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:applyBeanPostProcessorsBeforeInitialization(existingBean, beanName)
调用前置处理 AbstractAutowireCapableBeanFactory->>ApplicationContextAwareProcessor:postProcessBeforeInitialization(bean,beanName)
前置处理器方法 ApplicationContextAwareProcessor->>ApplicationContextAwareProcessor:invokeAwareInterfaces(bean)
调用Aware接口 ApplicationContextAwareProcessor->>MyApplicationEventPublisherAware:setApplicationEventPublisher(publisher)
注入事件发布器 AbstractAutowireCapableBeanFactory-->>AbstractBeanFactory:返回Bean对象 AbstractBeanFactory-->>DefaultListableBeanFactory:返回Bean对象 AnnotationConfigApplicationContext-->>ApplicationEventPublisherAwareApplication:初始化完成 ~~~ ### 七、源码分析 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyApplicationEventPublisherAware`类型的bean,最后调用`publish`方法用于发布一个事件。 ```java public class ApplicationEventPublisherAwareApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MyApplicationEventPublisherAware publisherAware = context.getBean(MyApplicationEventPublisherAware.class); publisherAware.publish("hello world"); } } ``` 在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法 ```java public AnnotationConfigApplicationContext(Class... componentClasses) { this(); register(componentClasses); refresh(); ``` 在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 ```java @Override public void refresh() throws BeansException, IllegalStateException { // ... [代码部分省略以简化] // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization`方法中,会继续调用`DefaultListableBeanFactory`类中的`preInstantiateSingletons`方法来完成所有剩余非懒加载的单列Bean对象。 ```java /** * 完成此工厂的bean初始化,实例化所有剩余的非延迟初始化单例bean。 * * @param beanFactory 要初始化的bean工厂 */ protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { // ... [代码部分省略以简化] // 完成所有剩余非懒加载的单列Bean对象。 beanFactory.preInstantiateSingletons(); } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons`方法中,主要的核心目的是预先实例化所有非懒加载的单例bean。在Spring的上下文初始化完成后,该方法会被触发,以确保所有单例bean都被正确地创建并初始化。其中`getBean(beanName)`是此方法的核心操作。对于容器中定义的每一个单例bean,它都会调用`getBean`方法,这将触发bean的实例化、初始化及其依赖的注入。如果bean之前没有被创建过,那么这个调用会导致其被实例化和初始化。 ```java public void preInstantiateSingletons() throws BeansException { // ... [代码部分省略以简化] // 循环遍历所有bean的名称 for (String beanName : beanNames) { getBean(beanName); } // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#getBean()`方法中,又调用了`doGetBean`方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,`doGetBean`负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。 ```java @Override public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中,首先检查所请求的bean是否是一个单例并且已经创建。如果尚未创建,它将创建一个新的实例。在这个过程中,它处理可能的异常情况,如循环引用,并确保返回的bean是正确的类型。这是Spring容器bean生命周期管理的核心部分。 ```java protected T doGetBean( String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException { // ... [代码部分省略以简化] // 开始创建bean实例 if (mbd.isSingleton()) { // 如果bean是单例的,我们会尝试从单例缓存中获取它 // 如果不存在,则使用lambda创建一个新的实例 sharedInstance = getSingleton(beanName, () -> { try { // 尝试创建bean实例 return createBean(beanName, mbd, args); } catch (BeansException ex) { // ... [代码部分省略以简化] } }); // 对于某些bean(例如FactoryBeans),可能需要进一步处理以获取真正的bean实例 beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } // ... [代码部分省略以简化] // 确保返回的bean实例与请求的类型匹配 return adaptBeanInstance(name, beanInstance, requiredType); } ``` 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()`方法中,主要负责从单例缓存中获取一个已存在的bean实例,或者使用提供的`ObjectFactory`创建一个新的实例。这是确保bean在Spring容器中作为单例存在的关键部分。 ```java public Object getSingleton(String beanName, ObjectFactory singletonFactory) { // 断言bean名称不能为空 Assert.notNull(beanName, "Bean name must not be null"); // 同步访问单例对象缓存,确保线程安全 synchronized (this.singletonObjects) { // 从缓存中获取单例对象 Object singletonObject = this.singletonObjects.get(beanName); // 如果缓存中没有找到 if (singletonObject == null) { // ... [代码部分省略以简化] try { // 使用工厂创建新的单例实例 singletonObject = singletonFactory.getObject(); newSingleton = true; } catch (IllegalStateException ex) { // ... [代码部分省略以简化] } catch (BeanCreationException ex) { // ... [代码部分省略以简化] } finally { // ... [代码部分省略以简化] } // ... [代码部分省略以简化] } // 返回单例对象 return singletonObject; } } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()`方法中,主要的逻辑是调用 `doCreateBean`,这是真正进行 bean 实例化、属性填充和初始化的地方。这个方法会返回新创建的 bean 实例。 ```java @Override protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // ... [代码部分省略以简化] try { // 正常的bean实例化、属性注入和初始化。 // 这里是真正进行bean创建的部分。 Object beanInstance = doCreateBean(beanName, mbdToUse, args); // 记录bean成功创建的日志 if (logger.isTraceEnabled()) { logger.trace("Finished creating instance of bean '" + beanName + "'"); } return beanInstance; } catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { // ... [代码部分省略以简化] } catch (Throwable ex) { // ... [代码部分省略以简化] } } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中,`initializeBean`方法是bean初始化,确保bean是完全配置和准备好的。 ```java protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // 声明一个对象,后续可能用于存放初始化后的bean或它的代理对象 Object exposedObject = bean; // ... [代码部分省略以简化] try { // ... [代码部分省略以简化] // bean初始化 exposedObject = initializeBean(beanName, exposedObject, mbd); } catch (Throwable ex) { // ... [代码部分省略以简化] } // 返回创建和初始化后的bean return exposedObject; } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean`方法中,如果条件满足(即 bean 不是合成的),那么它会调用 `applyBeanPostProcessorsBeforeInitialization` 方法。这个方法是 Spring 生命周期中的一个关键点,它会遍历所有已注册的 `BeanPostProcessor` 实现,并调用它们的 `postProcessBeforeInitialization` 方法。这允许我们和内部处理器在 bean 初始化之前对其进行修改或执行其他操作。 ```java protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { // ... [代码部分省略以简化] Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } // ... [代码部分省略以简化] return wrappedBean; } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization`方法中,遍历每一个 `BeanPostProcessor` 的 `postProcessBeforeInitialization` 方法都有机会对bean进行修改或增强 ```java @Override public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; for (BeanPostProcessor processor : getBeanPostProcessors()) { Object current = processor.postProcessBeforeInitialization(result, beanName); if (current == null) { return result; } result = current; } return result; } ``` 在`org.springframework.context.support.ApplicationContextAwareProcessor#postProcessBeforeInitialization`方法中,在这个方法的实现特别关注那些实现了 "aware" 接口的 beans,并为它们提供所需的运行环境信息。 ```java @Override @Nullable public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware || bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware || bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware || bean instanceof ApplicationStartupAware)) { return bean; } // ... [代码部分省略以简化] invokeAwareInterfaces(bean); return bean; } ``` 在`org.springframework.context.support.ApplicationContextAwareProcessor#invokeAwareInterfaces`方法中,用于处理实现了"Aware"接口的beans。这些接口使得beans能够被自动"感知"并获得对其运行环境或特定依赖的引用,而不需要显式地进行查找或注入。 ```java private void invokeAwareInterfaces(Object bean) { // ... [代码部分省略以简化] // 对ApplicationEventPublisherAware接口进行回调 if (bean instanceof ApplicationEventPublisherAware) { ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext); } // ... [代码部分省略以简化] } ``` 最后执行到我们自定义的逻辑中,使用 `ApplicationEventPublisherAware` 来获取 `ApplicationEventPublisher` 的引用,并使用这个引用来发布自定义事件。 ```java public class MyApplicationEventPublisherAware implements ApplicationEventPublisherAware { private ApplicationEventPublisher publisher; @Override public void setApplicationEventPublisher(ApplicationEventPublisher publisher) { this.publisher = publisher; } public void publish(String message) { publisher.publishEvent(new MyEvent(this, message)); } } ``` ### 八、注意事项 1. **确保容器支持** + 不是所有的Spring容器都支持 `Aware` 接口。例如,基本的 `BeanFactory` 不支持,而 `ApplicationContext` 支持。确保我们的bean是由支持 `ApplicationEventPublisherAware` 的容器管理的。 2. **避免复杂的业务逻辑** + 在实现的 `setApplicationEventPublisher` 方法中,尽量避免放入复杂的业务逻辑,该方法主要是用于注入 `ApplicationEventPublisher` 的。 3. **注意事件的目标** + 当使用 `ApplicationEventPublisher` 发布事件时,这些事件会被所有相应的监听器捕获。确保我们了解这些监听器的存在和它们的行为,以避免出现一些奇奇怪怪的问题。 4. **不要手动调用** + `setApplicationEventPublisher` 方法是为了由Spring容器调用的,而不是为了应用程序代码调用的。我们不应该在业务逻辑中手动调用这个方法。 ### 九、总结 #### 最佳实践总结 1. **启动应用** + 在 `ApplicationEventPublisherAwareApplication` 的主方法中,使用 `AnnotationConfigApplicationContext` 初始化了 Spring 上下文,并指定了配置类 `MyConfiguration`。 2. **配置类** + `MyConfiguration` 使用了 `@Configuration` 注解,表示它是一个 Spring 配置类。此类中使用 `@Bean` 注解定义了两个 Bean:`MyApplicationEventPublisherAware` 和 `MyEventListener`,确保它们被 Spring 容器管理。 3. **事件发布者** + `MyApplicationEventPublisherAware` 类实现了 `ApplicationEventPublisherAware` 接口,从而可以获取 `ApplicationEventPublisher` 的引用。它还定义了一个 `publish` 方法,用于发布自定义的 `MyEvent` 事件。 4. **自定义事件** + `MyEvent` 是一个自定义事件类,继承自 `ApplicationEvent`。它携带一个字符串消息。 5. **事件监听器** + `MyEventListener` 是一个事件监听器,它实现了 `ApplicationListener` 并专门用于监听 `MyEvent` 事件。当相应事件被发布时,它的 `onApplicationEvent` 方法会被自动触发。 6. **执行结果** + 当运行 `ApplicationEventPublisherAwareApplication` 主方法时,应用发布了一个 `MyEvent` 事件,携带了 "hello world" 消息。`MyEventListener` 成功捕获此事件,并输出了相应的消息。 #### 源码分析总结 1. **启动应用** + 通过 `ApplicationEventPublisherAwareApplication` 的主方法,使用 `AnnotationConfigApplicationContext` 初始化了 Spring 上下文,并指定了配置类 `MyConfiguration`。 2. **注册和刷新** + 在 `AnnotationConfigApplicationContext` 构造函数中,先注册组件类,然后调用 `refresh()` 来启动Spring容器的初始化过程。 3. **初始化Bean工厂** + 在 `AbstractApplicationContext#refresh` 方法中,调用 `finishBeanFactoryInitialization` 以实例化所有非懒加载的单例Bean。 4. **预实例化单例** + 在 `DefaultListableBeanFactory` 中,通过 `preInstantiateSingletons` 方法预先实例化所有非懒加载的单例Bean。 5. **Bean创建** + 在 `AbstractBeanFactory#getBean` 中,调用 `doGetBean` 来真正执行Bean的创建过程。此方法中涉及到真正的Bean实例化、属性注入和初始化。 6. **初始化Bean** + 在 `AbstractAutowireCapableBeanFactory` 类中,`initializeBean` 方法用于确保bean完全配置并准备就绪。这个过程中会应用所有的 `BeanPostProcessor`,它们能在bean初始化前后做额外的处理。 7. **处理Aware接口** + 在 `ApplicationContextAwareProcessor` 中,`invokeAwareInterfaces` 方法负责处理实现了 `Aware` 接口的beans,为它们自动注入对应的依赖或运行环境信息。 8. **发布事件** + 在我们的自定义逻辑中,使用 `ApplicationEventPublisherAware` 接口来获取Spring的事件发布器。然后,使用这个事件发布器,我们可以发布自定义事件。 ================================================ FILE: spring-aware/spring-aware-applicationEventPublisherAware/pom.xml ================================================ spring-aware com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-aware-applicationEventPublisherAware ================================================ FILE: spring-aware/spring-aware-applicationEventPublisherAware/src/main/java/com/xcs/spring/ApplicationEventPublisherAwareApplication.java ================================================ package com.xcs.spring; import com.xcs.spring.config.MyApplicationEventPublisherAware; import com.xcs.spring.config.MyConfiguration; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年09月16日 16时09分 **/ public class ApplicationEventPublisherAwareApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MyApplicationEventPublisherAware publisherAware = context.getBean(MyApplicationEventPublisherAware.class); publisherAware.publish("hello world"); } } ================================================ FILE: spring-aware/spring-aware-applicationEventPublisherAware/src/main/java/com/xcs/spring/config/MyApplicationEventPublisherAware.java ================================================ package com.xcs.spring.config; import com.xcs.spring.event.MyEvent; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; public class MyApplicationEventPublisherAware implements ApplicationEventPublisherAware { private ApplicationEventPublisher publisher; @Override public void setApplicationEventPublisher(ApplicationEventPublisher publisher) { this.publisher = publisher; } public void publish(String message) { publisher.publishEvent(new MyEvent(this, message)); } } ================================================ FILE: spring-aware/spring-aware-applicationEventPublisherAware/src/main/java/com/xcs/spring/config/MyConfiguration.java ================================================ package com.xcs.spring.config; import com.xcs.spring.event.MyEventListener; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @author xcs * @date 2023年09月19日 16时35分 **/ @Configuration public class MyConfiguration { @Bean public MyApplicationEventPublisherAware myApplicationEventPublisherAware(){ return new MyApplicationEventPublisherAware(); } @Bean public MyEventListener MyEventListener(){ return new MyEventListener(); } } ================================================ FILE: spring-aware/spring-aware-applicationEventPublisherAware/src/main/java/com/xcs/spring/event/MyEvent.java ================================================ package com.xcs.spring.event; import org.springframework.context.ApplicationEvent; public class MyEvent extends ApplicationEvent { private final String message; public MyEvent(Object source, String message) { super(source); this.message = message; } public String getMessage() { return message; } } ================================================ FILE: spring-aware/spring-aware-applicationEventPublisherAware/src/main/java/com/xcs/spring/event/MyEventListener.java ================================================ package com.xcs.spring.event; import org.springframework.context.ApplicationListener; public class MyEventListener implements ApplicationListener { @Override public void onApplicationEvent(MyEvent event) { System.out.println("Received my event - " + event.getMessage()); } } ================================================ FILE: spring-aware/spring-aware-applicationStartupAware/README.md ================================================ ## ApplicationStartupAware - [ApplicationStartupAware](#applicationstartupaware) - [一、基本信息](#一基本信息) - [二、接口描述](#二接口描述) - [三、接口源码](#三接口源码) - [四、主要功能](#四主要功能) - [五、最佳实践](#五最佳实践) - [六、时序图](#六时序图) - [七、源码分析](#七源码分析) - [八、注意事项](#八注意事项) - [九、总结](#九总结) - [最佳实践总结](#最佳实践总结) - [源码分析总结](#源码分析总结) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [我的CSDN](https://blog.csdn.net/duzhuang2399/article/details/133914474) 📚 **文章目录** - [所有文章](https://github.com/xuchengsheng/spring-reading) 🔗 **源码地址** - [ApplicationStartupAware源码]() ### 二、接口描述 `ApplicationStartupAware`接口,是为了提供对这一过程的细粒度跟踪。通过`StartupStep`,我们可以定义应用启动过程中的各个步骤,并收集关于它们的性能和上下文信息。 ### 三、接口源码 `ApplicationStartupAware` 是 Spring 框架自 5.3 开始引入的一个核心接口。实现`ApplicationStartupAware`接口的对象会在Spring容器中被自动注入一个`ApplicationStartup`实例。 ```java /** * 任何希望在运行时被通知其关联的ApplicationStartup实例的对象都应实现此接口。 * 在更简单的术语中,这是一个使bean意识到应用启动跟踪机制的接口。 * * @author Brian Clozel * @since 5.3 * @see ApplicationContextAware */ public interface ApplicationStartupAware extends Aware { /** * 设置此对象运行时的ApplicationStartup。 * 此方法的调用时机为正常bean属性填充之后,但在任何初始化回调(例如InitializingBean的afterPropertiesSet或自定义的初始化方法)之前。 * 并且在ApplicationContextAware的setApplicationContext之前调用。 * * @param applicationStartup 由此对象使用的application startup实例 */ void setApplicationStartup(ApplicationStartup applicationStartup); } ``` ### 四、主要功能 1. **启动性能跟踪** + 通过提供对`ApplicationStartup`的访问,实现此接口的beans可以使用`StartupStep`API来跟踪它们在启动过程中的各个步骤。这对于检测和优化启动性能非常有用。 2. **为beans提供跟踪能力** + 而不仅仅是Spring框架内部使用。这意味着我们可以为他们的自定义beans或组件提供与Spring框架同样的启动跟踪能力。 3. **细粒度控制** + 与`StartupStep`一起使用,`ApplicationStartupAware`允许beans对其启动过程中的特定部分进行跟踪,例如数据库初始化、外部服务连接或任何其他可能需要时间的操作。 ### 五、最佳实践 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),首先设置了`BufferingApplicationStartup`,这是Spring Boot提供的一个`ApplicationStartup`实现,缓存了最后的100个启动步骤。这使得我们可以在应用启动后查看并分析这些步骤,以便了解哪些操作可能会影响启动性能,然后使用`register`方法,我们告诉Spring上下文加载`MyConfiguration`类,最后调用`refresh`方法会触发应用上下文的初始化,包括bean的创建和依赖注入。 ```java public class ApplicationStartupAwareApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.setApplicationStartup(new BufferingApplicationStartup(100)); context.register(MyConfiguration.class); context.refresh(); context.close(); } } ``` 这里使用`@Bean`注解,定义了一个Bean,是为了确保 `MyApplicationStartupAware` 被 Spring 容器执行。 ```java @Configuration public class MyConfiguration { @Bean public MyApplicationStartupAware myApplicationStartupAware(){ return new MyApplicationStartupAware(); } } ``` `MyApplicationStartupAware`类的主要目的是展示如何使用`ApplicationStartup`来跟踪Spring应用程序启动过程中的特定逻辑。这对于我们程序来说是有用的,因为我们可以看到哪些启动步骤是最消耗时间的,然后据此进行优化。在这个具体的实现中,仅仅模拟了两个步骤,但在实际应用中,可以跟踪任意数量和类型的步骤。 ```java public class MyApplicationStartupAware implements ApplicationStartupAware, InitializingBean { private ApplicationStartup applicationStartup; @Override public void setApplicationStartup(ApplicationStartup applicationStartup) { this.applicationStartup = applicationStartup; } @Override public void afterPropertiesSet() throws Exception { StartupStep step1 = applicationStartup.start("MyApplicationStartupAware Logic Step 1"); // 自定义逻辑 Thread.sleep(200); step1.tag("status", "done").end(); StartupStep step2 = applicationStartup.start("MyApplicationStartupAware Logic Step 2"); // 自定义逻辑 Thread.sleep(300); step2.tag("status", "done").end(); } } ``` ### 六、时序图 ~~~mermaid sequenceDiagram Title: ApplicationStartupAware时序图 participant ApplicationStartupAwareApplication participant AnnotationConfigApplicationContext participant AbstractApplicationContext participant DefaultListableBeanFactory participant AbstractBeanFactory participant DefaultSingletonBeanRegistry participant AbstractAutowireCapableBeanFactory participant ApplicationContextAwareProcessor participant MyApplicationStartupAware ApplicationStartupAwareApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)
创建上下文 AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()
刷新上下文 AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory)
初始化Bean工厂 AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons()
实例化单例 DefaultListableBeanFactory->>AbstractBeanFactory:getBean(name)
获取Bean AbstractBeanFactory->>AbstractBeanFactory:doGetBean(name,requiredType,args,typeCheckOnly)
执行获取Bean AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton(beanName,singletonFactory)
获取单例Bean DefaultSingletonBeanRegistry-->>AbstractBeanFactory:getObject()
获取Bean实例 AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean(beanName,mbd,args)
创建Bean AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:doCreateBean(beanName,mbd,args)
执行Bean创建 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:initializeBean(beanName,bean,mbd)
负责bean的初始化 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:applyBeanPostProcessorsBeforeInitialization(existingBean, beanName)
调用前置处理器 AbstractAutowireCapableBeanFactory->>ApplicationContextAwareProcessor:postProcessBeforeInitialization(bean,beanName)
触发Aware处理 ApplicationContextAwareProcessor->>ApplicationContextAwareProcessor:invokeAwareInterfaces(bean)
执行Aware回调 ApplicationContextAwareProcessor->>MyApplicationStartupAware:setApplicationStartup(applicationStartup)
设置运行环境 AbstractAutowireCapableBeanFactory-->>AbstractBeanFactory:返回Bean对象 AbstractBeanFactory-->>DefaultListableBeanFactory:返回Bean对象 AnnotationConfigApplicationContext-->>ApplicationStartupAwareApplication:初始化完成 ~~~ ### 七、源码分析 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),首先设置了`BufferingApplicationStartup`,这是Spring Boot提供的一个`ApplicationStartup`实现,缓存了最后的100个启动步骤。这使得我们可以在应用启动后查看并分析这些步骤,以便了解哪些操作可能会影响启动性能,然后使用`register`方法,我们告诉Spring上下文加载`MyConfiguration`类,最后调用`refresh`方法会触发应用上下文的初始化,包括bean的创建和依赖注入。 ```java public class ApplicationStartupAwareApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.setApplicationStartup(new BufferingApplicationStartup(100)); context.register(MyConfiguration.class); context.refresh(); context.close(); } } ``` 在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法 ```java public AnnotationConfigApplicationContext(Class... componentClasses) { this(); register(componentClasses); refresh(); ``` 在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 ```java @Override public void refresh() throws BeansException, IllegalStateException { // ... [代码部分省略以简化] // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization`方法中,会继续调用`DefaultListableBeanFactory`类中的`preInstantiateSingletons`方法来完成所有剩余非懒加载的单列Bean对象。 ```java /** * 完成此工厂的bean初始化,实例化所有剩余的非延迟初始化单例bean。 * * @param beanFactory 要初始化的bean工厂 */ protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { // ... [代码部分省略以简化] // 完成所有剩余非懒加载的单列Bean对象。 beanFactory.preInstantiateSingletons(); } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons`方法中,主要的核心目的是预先实例化所有非懒加载的单例bean。在Spring的上下文初始化完成后,该方法会被触发,以确保所有单例bean都被正确地创建并初始化。其中`getBean(beanName)`是此方法的核心操作。对于容器中定义的每一个单例bean,它都会调用`getBean`方法,这将触发bean的实例化、初始化及其依赖的注入。如果bean之前没有被创建过,那么这个调用会导致其被实例化和初始化。 ```java public void preInstantiateSingletons() throws BeansException { // ... [代码部分省略以简化] // 循环遍历所有bean的名称 for (String beanName : beanNames) { getBean(beanName); } // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#getBean()`方法中,又调用了`doGetBean`方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,`doGetBean`负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。 ```java @Override public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中,首先检查所请求的bean是否是一个单例并且已经创建。如果尚未创建,它将创建一个新的实例。在这个过程中,它处理可能的异常情况,如循环引用,并确保返回的bean是正确的类型。这是Spring容器bean生命周期管理的核心部分。 ```java protected T doGetBean( String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException { // ... [代码部分省略以简化] // 开始创建bean实例 if (mbd.isSingleton()) { // 如果bean是单例的,我们会尝试从单例缓存中获取它 // 如果不存在,则使用lambda创建一个新的实例 sharedInstance = getSingleton(beanName, () -> { try { // 尝试创建bean实例 return createBean(beanName, mbd, args); } catch (BeansException ex) { // ... [代码部分省略以简化] } }); // 对于某些bean(例如FactoryBeans),可能需要进一步处理以获取真正的bean实例 beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } // ... [代码部分省略以简化] // 确保返回的bean实例与请求的类型匹配 return adaptBeanInstance(name, beanInstance, requiredType); } ``` 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()`方法中,主要负责从单例缓存中获取一个已存在的bean实例,或者使用提供的`ObjectFactory`创建一个新的实例。这是确保bean在Spring容器中作为单例存在的关键部分。 ```java public Object getSingleton(String beanName, ObjectFactory singletonFactory) { // 断言bean名称不能为空 Assert.notNull(beanName, "Bean name must not be null"); // 同步访问单例对象缓存,确保线程安全 synchronized (this.singletonObjects) { // 从缓存中获取单例对象 Object singletonObject = this.singletonObjects.get(beanName); // 如果缓存中没有找到 if (singletonObject == null) { // ... [代码部分省略以简化] try { // 使用工厂创建新的单例实例 singletonObject = singletonFactory.getObject(); newSingleton = true; } catch (IllegalStateException ex) { // ... [代码部分省略以简化] } catch (BeanCreationException ex) { // ... [代码部分省略以简化] } finally { // ... [代码部分省略以简化] } // ... [代码部分省略以简化] } // 返回单例对象 return singletonObject; } } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()`方法中,主要的逻辑是调用 `doCreateBean`,这是真正进行 bean 实例化、属性填充和初始化的地方。这个方法会返回新创建的 bean 实例。 ```java @Override protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // ... [代码部分省略以简化] try { // 正常的bean实例化、属性注入和初始化。 // 这里是真正进行bean创建的部分。 Object beanInstance = doCreateBean(beanName, mbdToUse, args); // 记录bean成功创建的日志 if (logger.isTraceEnabled()) { logger.trace("Finished creating instance of bean '" + beanName + "'"); } return beanInstance; } catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { // ... [代码部分省略以简化] } catch (Throwable ex) { // ... [代码部分省略以简化] } } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中,`initializeBean`方法是bean初始化,确保bean是完全配置和准备好的。 ```java protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // 声明一个对象,后续可能用于存放初始化后的bean或它的代理对象 Object exposedObject = bean; // ... [代码部分省略以简化] try { // ... [代码部分省略以简化] // bean初始化 exposedObject = initializeBean(beanName, exposedObject, mbd); } catch (Throwable ex) { // ... [代码部分省略以简化] } // 返回创建和初始化后的bean return exposedObject; } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean`方法中,如果条件满足(即 bean 不是合成的),那么它会调用 `applyBeanPostProcessorsBeforeInitialization` 方法。这个方法是 Spring 生命周期中的一个关键点,它会遍历所有已注册的 `BeanPostProcessor` 实现,并调用它们的 `postProcessBeforeInitialization` 方法。这允许我们和内部处理器在 bean 初始化之前对其进行修改或执行其他操作。 ```java protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { // ... [代码部分省略以简化] Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } // ... [代码部分省略以简化] return wrappedBean; } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization`方法中,遍历每一个 `BeanPostProcessor` 的 `postProcessBeforeInitialization` 方法都有机会对bean进行修改或增强 ```java @Override public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; for (BeanPostProcessor processor : getBeanPostProcessors()) { Object current = processor.postProcessBeforeInitialization(result, beanName); if (current == null) { return result; } result = current; } return result; } ``` 在`org.springframework.context.support.ApplicationContextAwareProcessor#postProcessBeforeInitialization`方法中,在这个方法的实现特别关注那些实现了 "aware" 接口的 beans,并为它们提供所需的运行环境信息。 ```java @Override @Nullable public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware || bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware || bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware || bean instanceof ApplicationStartupAware)) { return bean; } // ... [代码部分省略以简化] invokeAwareInterfaces(bean); return bean; } ``` 在`org.springframework.context.support.ApplicationContextAwareProcessor#invokeAwareInterfaces`方法中,用于处理实现了"Aware"接口的beans。这些接口使得beans能够被自动"感知"并获得对其运行环境或特定依赖的引用,而不需要显式地进行查找或注入。 ```java private void invokeAwareInterfaces(Object bean) { // ... [代码部分省略以简化] if (bean instanceof ApplicationStartupAware) { ((ApplicationStartupAware) bean).setApplicationStartup(this.applicationContext.getApplicationStartup()); } // ... [代码部分省略以简化] } ``` 最后执行到我们自定义的逻辑中,`MyApplicationStartupAware`类的主要目的是展示如何使用`ApplicationStartup`来跟踪Spring应用程序启动过程中的特定逻辑。这对于我们程序来说是有用的,因为我们可以看到哪些启动步骤是最消耗时间的,然后据此进行优化。在这个具体的实现中,仅仅模拟了两个步骤,但在实际应用中,可以跟踪任意数量和类型的步骤。 ```java public class MyApplicationStartupAware implements ApplicationStartupAware, InitializingBean { private ApplicationStartup applicationStartup; @Override public void setApplicationStartup(ApplicationStartup applicationStartup) { this.applicationStartup = applicationStartup; } @Override public void afterPropertiesSet() throws Exception { StartupStep step1 = applicationStartup.start("MyApplicationStartupAware Logic Step 1"); // 自定义逻辑 Thread.sleep(200); step1.tag("status", "done").end(); StartupStep step2 = applicationStartup.start("MyApplicationStartupAware Logic Step 2"); // 自定义逻辑 Thread.sleep(300); step2.tag("status", "done").end(); } } ``` ### 八、注意事项 1. **生命周期时机** + `setApplicationStartup`方法在其他bean属性设置之后、`InitializingBean`的`afterPropertiesSet`方法之前调用。确保我们的bean在这一阶段不依赖于其他尚未初始化或注入的属性。 2. **性能考虑** + 虽然启动跟踪对于分析应用程序启动时间很有用,但添加太多的启动步骤跟踪可能会对性能产生微小的影响。在生产环境中,我们可能需要权衡跟踪的详细程度和性能的关系。 3. **清晰的步骤名称** + 当定义`StartupStep`时,为其提供清晰、描述性的名称,这样其他我们可以更容易地理解它代表的步骤。 4. **不要滥用** + 尽量只为那些真正重要和可能影响启动性能的步骤使用启动跟踪。不需要为每个小操作都添加跟踪。 5. **不要忘记结束步骤** + 每当我们开始一个`StartupStep`,记得在适当的时机调用`end`方法结束它。否则,该步骤可能会在报告中显示为仍在运行,这可能会导致混淆。 ### 九、总结 #### 最佳实践总结 1. **启动类概述** + 使用`AnnotationConfigApplicationContext`,一个基于Java注解的Spring上下文初始化方法。设置`BufferingApplicationStartup`来缓存应用启动过程的最后100个步骤。这样可以分析哪些步骤可能影响启动性能。注册`MyConfiguration`类以加载相应的配置。刷新并初始化应用上下文,从而触发bean的创建和依赖注入。关闭上下文。 2. **配置类概述** + 使用`@Configuration`注解标记,告诉Spring这是一个配置类。通过`@Bean`注解定义了`MyApplicationStartupAware` bean。这样可以确保它被Spring容器处理,并在容器启动时执行其生命周期方法。 3. **`MyApplicationStartupAware`类概述** + 实现了`ApplicationStartupAware`接口,允许它在启动时获知`ApplicationStartup`实例。定义了两个启动步骤来模拟潜在的长时间运行任务,并使用`StartupStep`进行跟踪。在每个步骤的末尾,都有一个标记状态为"done",然后结束该步骤。 #### 源码分析总结 1. **实例化Beans** + 在`AbstractApplicationContext`的`refresh()`方法中,`finishBeanFactoryInitialization`方法被调用,确保所有单例Bean被预先实例化。 2. **Bean预实例化** + `DefaultListableBeanFactory`的`preInstantiateSingletons`方法确保所有非懒加载的单例Beans被实例化。核心操作是调用`getBean(beanName)`。 3. **获取Bean实例** + `AbstractBeanFactory`的`getBean`方法进一步调用`doGetBean`来真正实例化Bean,处理异常和依赖,并返回Bean实例。 4. **Bean单例获取** + `DefaultSingletonBeanRegistry`的`getSingleton`方法确保Bean以单例形式存在,从缓存获取或使用提供的`ObjectFactory`创建新实例。 5. **创建Bean实例** + `AbstractAutowireCapableBeanFactory`的`createBean`方法调用`doCreateBean`进行Bean的实际实例化,并进行初始化,确保Bean完全配置并准备就绪。 6. **Bean初始化** + `AbstractAutowireCapableBeanFactory`的`initializeBean`方法确保Bean被正确初始化,其中调用`applyBeanPostProcessorsBeforeInitialization`方法是Spring生命周期中的关键点,允许BeanPostProcessors在Bean初始化之前进行操作。 7. **处理Aware接口** + 在Bean初始化过程中,`ApplicationContextAwareProcessor`确保实现了`Aware`接口的Beans被正确处理,这些Beans会自动"感知"并获得其运行环境或特定依赖的引用。 8. **自定义逻辑执行** + `MyApplicationStartupAware`类实现了`ApplicationStartupAware`接口,它将接收一个`ApplicationStartup`实例。 ================================================ FILE: spring-aware/spring-aware-applicationStartupAware/pom.xml ================================================ spring-aware com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-aware-applicationStartupAware ================================================ FILE: spring-aware/spring-aware-applicationStartupAware/src/main/java/com/xcs/spring/ApplicationStartupAwareApplication.java ================================================ package com.xcs.spring; import com.xcs.spring.config.MyConfiguration; import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年09月16日 16时09分 **/ public class ApplicationStartupAwareApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.setApplicationStartup(new BufferingApplicationStartup(100)); context.register(MyConfiguration.class); context.refresh(); context.close(); } } ================================================ FILE: spring-aware/spring-aware-applicationStartupAware/src/main/java/com/xcs/spring/config/MyApplicationStartupAware.java ================================================ package com.xcs.spring.config; import org.springframework.beans.factory.InitializingBean; import org.springframework.context.ApplicationStartupAware; import org.springframework.context.EnvironmentAware; import org.springframework.core.env.Environment; import org.springframework.core.metrics.ApplicationStartup; import org.springframework.core.metrics.StartupStep; public class MyApplicationStartupAware implements ApplicationStartupAware, InitializingBean { private ApplicationStartup applicationStartup; @Override public void setApplicationStartup(ApplicationStartup applicationStartup) { this.applicationStartup = applicationStartup; } @Override public void afterPropertiesSet() throws Exception { StartupStep step1 = applicationStartup.start("MyApplicationStartupAware Logic Step 1"); // 自定义逻辑 Thread.sleep(200); step1.tag("status", "done").end(); StartupStep step2 = applicationStartup.start("MyApplicationStartupAware Logic Step 2"); // 自定义逻辑 Thread.sleep(300); step2.tag("status", "done").end(); } } ================================================ FILE: spring-aware/spring-aware-applicationStartupAware/src/main/java/com/xcs/spring/config/MyConfiguration.java ================================================ package com.xcs.spring.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @author xcs * @date 2023年09月19日 16时35分 **/ @Configuration public class MyConfiguration { @Bean public MyApplicationStartupAware myApplicationStartupAware(){ return new MyApplicationStartupAware(); } } ================================================ FILE: spring-aware/spring-aware-beanClassLoaderAware/README.md ================================================ ## BeanClassLoaderAware - [BeanClassLoaderAware](#beanclassloaderaware) - [一、基本信息](#一基本信息) - [二、接口描述](#二接口描述) - [三、接口源码](#三接口源码) - [四、主要功能](#四主要功能) - [五、最佳实践](#五最佳实践) - [六、时序图](#六时序图) - [七、源码分析](#七源码分析) - [八、注意事项](#八注意事项) - [九、总结](#九总结) - [最佳实践总结](#最佳实践总结) - [源码分析总结](#源码分析总结) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [我的CSDN](https://blog.csdn.net/duzhuang2399/article/details/133916700) 📚 **文章目录** - [所有文章](https://github.com/xuchengsheng/spring-reading) 🔗 **源码地址** - [BeanClassLoaderAware源码](https://github.com/xuchengsheng/spring-reading/tree/master/spring-aware/spring-aware-beanClassLoaderAware) ### 二、接口描述 `BeanClassLoaderAware` 接口,允许 bean 得知其加载的类加载器。当一个 bean 实现了这个接口时,Spring 容器在 bean 初始化的时候会设置它的类加载器。 ### 三、接口源码 `BeanClassLoaderAware` 是 Spring 框架自 2.0 版本开始引入的一个核心接口。当一个 bean 实现了这个接口,并在 Spring 容器中被初始化时,Spring 容器会自动调用 `setClassLoader` 方法,并将加载该 bean 的类加载器传入。 ```java /** * 回调接口,允许 bean 了解其所使用的 ClassLoader 类加载器; * 也就是当前 bean 工厂用于加载 bean 类的类加载器。 * * 主要目的是供那些需要按名称选择应用类的框架类实现,尽管这些框架类可能是由共享的类加载器加载的。 * * 对于所有 bean 生命周期方法的列表,请参阅 BeanFactory BeanFactory 的 javadocs。 * * @author Juergen Hoeller * @author Chris Beams * @since 2.0 * @see BeanNameAware * @see BeanFactoryAware * @see InitializingBean */ public interface BeanClassLoaderAware extends Aware { /** * 提供 bean 的 ClassLoader 类加载器 的回调方法。 * 在填充普通的 bean 属性之后但在初始化回调(如 InitializingBean InitializingBean 的 * InitializingBean#afterPropertiesSet() 方法或自定义初始化方法)之前调用此方法。 * * @param classLoader 拥有的类加载器 */ void setBeanClassLoader(ClassLoader classLoader); } ``` ### 四、主要功能 1. **提供类加载器信息** + Bean 可以得知其加载的类加载器,从而可以利用该类加载器进行动态的类加载或资源查找。 2. **框架与应用类加载器隔离** + 在某些复杂的环境中,框架类和应用程序类可能是由不同的类加载器加载的。例如,在某些应用服务器环境中,框架可能是由共享的类加载器加载的,而应用程序类是由专门的类加载器加载的。通过 `BeanClassLoaderAware`,框架类可以确保使用正确的类加载器来加载或访问应用程序类。 3. **生命周期管理** + `setBeanClassLoader` 方法会在填充 bean 的普通属性之后但在调用任何初始化回调(如 `InitializingBean#afterPropertiesSet()`)之前被调用。这确保了在 bean 的生命周期的合适阶段提供类加载器信息。 ### 五、最佳实践 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyBeanClassLoaderAware`类型的bean,最后调用`loadAndExecute`方法。 ```java public class BeanClassLoaderAwareApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MyBeanClassLoaderAware myBeanClassLoaderAware = context.getBean(MyBeanClassLoaderAware.class); myBeanClassLoaderAware.loadAndExecute(); } } ``` 这里使用`@Bean`注解,定义了一个Bean,是为了确保`MyBeanClassLoaderAware` 被 Spring 容器执行 ```java @Configuration public class MyConfiguration { @Bean public MyBeanClassLoaderAware myBeanClassLoaderAware(){ return new MyBeanClassLoaderAware(); } } ``` 在`MyBeanClassLoaderAware` 类中,我们实现了 `BeanClassLoaderAware` 接口,允许这个 bean 在初始化时获取其 `ClassLoader`。接着,在 `loadAndExecute` 方法中,我们使用这个 `ClassLoader` 来动态加载一个名为 `com.xcs.spring.service.UserServiceImpl` 的类并执行它的 `getUserInfo` 方法。 ```java public class MyBeanClassLoaderAware implements BeanClassLoaderAware { private ClassLoader classLoader; @Override public void setBeanClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; } public void loadAndExecute() { try { Class clazz = classLoader.loadClass("com.xcs.spring.service.UserServiceImpl"); UserServiceImpl instance = (UserServiceImpl) clazz.getDeclaredConstructor().newInstance(); System.out.println("UserInfo = " + instance.getUserInfo()); } catch (Exception e) { e.printStackTrace(); } } } ``` 定义一个接口与此接口的实现类。 ```java package com.xcs.spring.service; public interface UserService { String getUserInfo(); } public class UserServiceImpl implements UserService { @Override public String getUserInfo() { return "this is user info"; } } ``` 运行结果发现,通过这种方式,我们保证了`MyBeanClassLoaderAware`的代码与`UserServiceImpl`的具体实现解耦。这意味着,如果`UserServiceImpl`的具体实现发生了变化,或者有了新的实现,只要我们遵循`UserService`接口,我们的`MyBeanClassLoaderAware`代码就不需要任何更改。 ```java UserInfo = this is user info ``` ### 六、时序图 ~~~mermaid sequenceDiagram Title: BeanClassLoaderAware时序图 participant BeanFactoryAwareApplication participant AnnotationConfigApplicationContext participant AbstractApplicationContext participant DefaultListableBeanFactory participant AbstractBeanFactory participant DefaultSingletonBeanRegistry participant AbstractAutowireCapableBeanFactory participant BeanClassLoaderAware BeanFactoryAwareApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)
创建上下文 AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()
刷新上下文 AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory)
初始化Bean工厂 AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons()
实例化单例 DefaultListableBeanFactory->>AbstractBeanFactory:getBean(name)
获取Bean AbstractBeanFactory->>AbstractBeanFactory:doGetBean(name,requiredType,args,typeCheckOnly)
执行获取Bean AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton(beanName,singletonFactory)
获取单例Bean DefaultSingletonBeanRegistry-->>AbstractBeanFactory:getObject()
获取Bean实例 AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean(beanName,mbd,args)
创建Bean AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:doCreateBean(beanName,mbd,args)
执行Bean创建 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:initializeBean(beanName,bean,mbd)
负责bean的初始化 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:invokeAwareMethods(beanName, bean)
调用Aware方法 AbstractAutowireCapableBeanFactory->>BeanClassLoaderAware:setBeanClassLoader(classLoader)
设置classLoader AbstractAutowireCapableBeanFactory-->>AbstractBeanFactory:返回Bean对象 AbstractBeanFactory-->>DefaultListableBeanFactory:返回Bean对象 AnnotationConfigApplicationContext-->>BeanFactoryAwareApplication:初始化完成 ~~~ ### 七、源码分析 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyBeanClassLoaderAware`类型的bean,最后调用`loadAndExecute`方法。 ```java public class BeanClassLoaderAwareApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MyBeanClassLoaderAware myBeanClassLoaderAware = context.getBean(MyBeanClassLoaderAware.class); myBeanClassLoaderAware.loadAndExecute(); } } ``` 在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法 ```java public AnnotationConfigApplicationContext(Class... componentClasses) { this(); register(componentClasses); refresh(); ``` 在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 ```java @Override public void refresh() throws BeansException, IllegalStateException { // ... [代码部分省略以简化] // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization`方法中,会继续调用`DefaultListableBeanFactory`类中的`preInstantiateSingletons`方法来完成所有剩余非懒加载的单列Bean对象。 ```java /** * 完成此工厂的bean初始化,实例化所有剩余的非延迟初始化单例bean。 * * @param beanFactory 要初始化的bean工厂 */ protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { // ... [代码部分省略以简化] // 完成所有剩余非懒加载的单列Bean对象。 beanFactory.preInstantiateSingletons(); } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons`方法中,主要的核心目的是预先实例化所有非懒加载的单例bean。在Spring的上下文初始化完成后,该方法会被触发,以确保所有单例bean都被正确地创建并初始化。其中`getBean(beanName)`是此方法的核心操作。对于容器中定义的每一个单例bean,它都会调用`getBean`方法,这将触发bean的实例化、初始化及其依赖的注入。如果bean之前没有被创建过,那么这个调用会导致其被实例化和初始化。 ```java public void preInstantiateSingletons() throws BeansException { // ... [代码部分省略以简化] // 循环遍历所有bean的名称 for (String beanName : beanNames) { getBean(beanName); } // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#getBean()`方法中,又调用了`doGetBean`方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,`doGetBean`负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。 ```java @Override public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中,首先检查所请求的bean是否是一个单例并且已经创建。如果尚未创建,它将创建一个新的实例。在这个过程中,它处理可能的异常情况,如循环引用,并确保返回的bean是正确的类型。这是Spring容器bean生命周期管理的核心部分。 ```java protected T doGetBean( String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException { // ... [代码部分省略以简化] // 开始创建bean实例 if (mbd.isSingleton()) { // 如果bean是单例的,我们会尝试从单例缓存中获取它 // 如果不存在,则使用lambda创建一个新的实例 sharedInstance = getSingleton(beanName, () -> { try { // 尝试创建bean实例 return createBean(beanName, mbd, args); } catch (BeansException ex) { // ... [代码部分省略以简化] } }); // 对于某些bean(例如FactoryBeans),可能需要进一步处理以获取真正的bean实例 beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } // ... [代码部分省略以简化] // 确保返回的bean实例与请求的类型匹配 return adaptBeanInstance(name, beanInstance, requiredType); } ``` 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()`方法中,主要负责从单例缓存中获取一个已存在的bean实例,或者使用提供的`ObjectFactory`创建一个新的实例。这是确保bean在Spring容器中作为单例存在的关键部分。 ```java public Object getSingleton(String beanName, ObjectFactory singletonFactory) { // 断言bean名称不能为空 Assert.notNull(beanName, "Bean name must not be null"); // 同步访问单例对象缓存,确保线程安全 synchronized (this.singletonObjects) { // 从缓存中获取单例对象 Object singletonObject = this.singletonObjects.get(beanName); // 如果缓存中没有找到 if (singletonObject == null) { // ... [代码部分省略以简化] try { // 使用工厂创建新的单例实例 singletonObject = singletonFactory.getObject(); newSingleton = true; } catch (IllegalStateException ex) { // ... [代码部分省略以简化] } catch (BeanCreationException ex) { // ... [代码部分省略以简化] } finally { // ... [代码部分省略以简化] } // ... [代码部分省略以简化] } // 返回单例对象 return singletonObject; } } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()`方法中,主要的逻辑是调用 `doCreateBean`,这是真正进行 bean 实例化、属性填充和初始化的地方。这个方法会返回新创建的 bean 实例。 ```java @Override protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // ... [代码部分省略以简化] try { // 正常的bean实例化、属性注入和初始化。 // 这里是真正进行bean创建的部分。 Object beanInstance = doCreateBean(beanName, mbdToUse, args); // 记录bean成功创建的日志 if (logger.isTraceEnabled()) { logger.trace("Finished creating instance of bean '" + beanName + "'"); } return beanInstance; } catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { // ... [代码部分省略以简化] } catch (Throwable ex) { // ... [代码部分省略以简化] } } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中,`initializeBean`方法是bean初始化,确保bean是完全配置和准备好的。 ```java protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // 声明一个对象,后续可能用于存放初始化后的bean或它的代理对象 Object exposedObject = bean; // ... [代码部分省略以简化] try { // ... [代码部分省略以简化] // bean初始化 exposedObject = initializeBean(beanName, exposedObject, mbd); } catch (Throwable ex) { // ... [代码部分省略以简化] } // 返回创建和初始化后的bean return exposedObject; } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean`方法中,`invokeAwareMethods(beanName, bean)`是一个非常重要的步骤。这个方法是为了处理实现了Spring的`Aware`接口族的Beans(例如`BeanNameAware`, `BeanFactoryAware`等)。如果提供的bean实现了任何这些接口,该方法会回调相应的`Aware`方法。 ```java protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { // ... [代码部分省略以简化] invokeAwareMethods(beanName, bean); // ... [代码部分省略以简化] return wrappedBean; } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeAwareMethods`方法中,用于处理实现了 Spring `Aware` 接口族的 beans。当一个 bean 实现了如 `BeanNameAware`、`BeanClassLoaderAware` 或 `BeanFactoryAware` 等接口时,此方法确保正确的回调方法被调用,从而为 bean 提供关于其运行环境或其他相关信息。 ```java private void invokeAwareMethods(String beanName, Object bean) { if (bean instanceof Aware) { if (bean instanceof BeanNameAware) { // ... [代码部分省略以简化] } if (bean instanceof BeanClassLoaderAware) { ClassLoader bcl = getBeanClassLoader(); if (bcl != null) { ((BeanClassLoaderAware) bean).setBeanClassLoader(bcl); } } if (bean instanceof BeanFactoryAware) { // ... [代码部分省略以简化] } } } ``` 最后执行到我们自定义的逻辑中,我们实现了 `BeanClassLoaderAware` 接口,允许这个 bean 在初始化时获取其 `ClassLoader`。 ```java public class MyBeanClassLoaderAware implements BeanClassLoaderAware { private ClassLoader classLoader; @Override public void setBeanClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; } public void loadAndExecute() { try { Class clazz = classLoader.loadClass("com.xcs.spring.service.UserServiceImpl"); UserServiceImpl instance = (UserServiceImpl) clazz.getDeclaredConstructor().newInstance(); System.out.println("UserInfo = " + instance.getUserInfo()); } catch (Exception e) { e.printStackTrace(); } } } ``` ### 八、注意事项 1. **避免过度使用** + 只有当我们真的需要访问类加载器时才使用 `BeanClassLoaderAware`。不要滥用它,因为这可能会导致代码与Spring框架过度耦合。 2. **考虑类加载器层次结构** + 在Java中,类加载器通常有一个父子关系。如果我们不能使用当前的类加载器找到一个类,可能需要检查其父类加载器。 3. **不要在setter中执行复杂的逻辑** + `setBeanClassLoader` 是一个setter方法,应该避免在其中执行复杂的逻辑。它应该只用于设置类加载器。 4. **避免存储状态** + 尽量不要在实现了`BeanClassLoaderAware`的bean中存储状态或依赖于其他bean的状态。这会使bean的生命周期和初始化更加复杂。 5. **考虑使用其他技术** + 在某些情况下,可能有其他技术或方法可以达到同样的目的,而无需使用 `BeanClassLoaderAware`。例如,使用Spring的`@Value`注解或`ResourceLoader`来加载资源,而不是直接使用类加载器。 6. **考虑类加载器层次结构** + 在Java中,类加载器通常有一个父子关系。如果我们不能使用当前的类加载器找到一个类,可能需要检查其父类加载器。 7. **资源管理** + 类加载器不仅可以加载类,还可以用来加载其他资源(例如,属性文件)。但是,要小心确保资源路径正确,并记住类加载器的搜索行为可能与文件系统或其他加载机制不同。 ### 九、总结 #### 最佳实践总结 1. **启动及上下文配置** + 利用 `AnnotationConfigApplicationContext` 初始化Spring容器,使用 `MyConfiguration` 作为配置类来为Spring上下文提供设置。 2. **配置类定义** + 标记配置类为 `@Configuration`,使用 `@Bean` 注解来确保 `MyBeanClassLoaderAware` 被Spring容器管理。 3. **实现 `BeanClassLoaderAware`** + 通过实现 `BeanClassLoaderAware` 接口,bean 可以在初始化时获得其加载的 `ClassLoader`,在 `loadAndExecute` 方法中,动态加载并执行特定的服务方法。 4. **接口与实现** + 定义清晰的 `UserService` 接口和相应的 `UserServiceImpl` 实现。 5. **结果及结论** + 运行程序后,我们能够看到预期输出,这表明成功地将 `MyBeanClassLoaderAware` 与特定实现解耦。 #### 源码分析总结 1. **应用启动入口** + 通过`AnnotationConfigApplicationContext`,以`MyConfiguration`为配置类,初始化Spring容器。随后获取`MyBeanClassLoaderAware` bean并调用其`loadAndExecute`方法。 2. **初始化Spring上下文** + 在`AnnotationConfigApplicationContext`构造函数中,`refresh()`方法被调用来刷新或初始化Spring容器。 3. **Bean的预实例化** + 在Spring的上下文初始化的`refresh()`方法中,`finishBeanFactoryInitialization(beanFactory)`方法确保所有非延迟加载的单例bean被实例化。 4. **单例Bean的创建** + `DefaultListableBeanFactory`中的`preInstantiateSingletons`方法负责预先实例化所有非懒加载的单例bean。它会对容器中的每个单例bean调用`getBean`方法。 5. **Bean的实例化及初始化** + 在获取bean的过程中,如果bean还未创建,`doCreateBean`方法会被调用,完成bean的实例化、属性填充和初始化。 6. **处理Aware接口族** + 在bean的初始化阶段,`invokeAwareMethods`方法确保任何实现了`Aware`接口族(如`BeanNameAware`、`BeanClassLoaderAware`等)的bean都会得到适当的回调。 7. **BeanClassLoaderAware的实现** + 对于实现了`BeanClassLoaderAware`接口的bean,Spring容器在初始化阶段会通过`setBeanClassLoader`方法设置bean的`ClassLoader`。 8. **自定义逻辑的执行** + 在`MyBeanClassLoaderAware`中,已经保存了bean的类加载器,然后在`loadAndExecute`方法中,利用这个类加载器动态加载并执行特定的类和方法。 ================================================ FILE: spring-aware/spring-aware-beanClassLoaderAware/pom.xml ================================================ spring-aware com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-aware-beanClassLoaderAware ================================================ FILE: spring-aware/spring-aware-beanClassLoaderAware/src/main/java/com/xcs/spring/BeanClassLoaderAwareApplication.java ================================================ package com.xcs.spring; import com.xcs.spring.config.MyBeanClassLoaderAware; import com.xcs.spring.config.MyConfiguration; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年09月16日 16时09分 **/ public class BeanClassLoaderAwareApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MyBeanClassLoaderAware myBeanClassLoaderAware = context.getBean(MyBeanClassLoaderAware.class); myBeanClassLoaderAware.loadAndExecute(); } } ================================================ FILE: spring-aware/spring-aware-beanClassLoaderAware/src/main/java/com/xcs/spring/config/MyBeanClassLoaderAware.java ================================================ package com.xcs.spring.config; import com.xcs.spring.service.UserServiceImpl; import org.springframework.beans.factory.BeanClassLoaderAware; public class MyBeanClassLoaderAware implements BeanClassLoaderAware { private ClassLoader classLoader; @Override public void setBeanClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; } public void loadAndExecute() { try { Class clazz = classLoader.loadClass("com.xcs.spring.service.UserServiceImpl"); UserServiceImpl instance = (UserServiceImpl) clazz.getDeclaredConstructor().newInstance(); System.out.println("UserInfo = " + instance.getUserInfo()); } catch (Exception e) { e.printStackTrace(); } } } ================================================ FILE: spring-aware/spring-aware-beanClassLoaderAware/src/main/java/com/xcs/spring/config/MyConfiguration.java ================================================ package com.xcs.spring.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @author xcs * @date 2023年09月19日 16时35分 **/ @Configuration public class MyConfiguration { @Bean public MyBeanClassLoaderAware myBeanClassLoaderAware(){ return new MyBeanClassLoaderAware(); } } ================================================ FILE: spring-aware/spring-aware-beanClassLoaderAware/src/main/java/com/xcs/spring/service/UserService.java ================================================ package com.xcs.spring.service; public interface UserService { String getUserInfo(); } ================================================ FILE: spring-aware/spring-aware-beanClassLoaderAware/src/main/java/com/xcs/spring/service/UserServiceImpl.java ================================================ package com.xcs.spring.service; public class UserServiceImpl implements UserService { @Override public String getUserInfo() { return "this is user info"; } } ================================================ FILE: spring-aware/spring-aware-beanFactoryAware/README.md ================================================ ## BeanFactoryAware - [BeanFactoryAware](#beanfactoryaware) - [一、基本信息](#一基本信息) - [二、接口描述](#二接口描述) - [三、接口源码](#三接口源码) - [四、主要功能](#四主要功能) - [五、最佳实践](#五最佳实践) - [六、时序图](#六时序图) - [七、源码分析](#七源码分析) - [八、注意事项](#八注意事项) - [九、总结](#九总结) - [最佳实践总结](#最佳实践总结) - [源码分析总结](#源码分析总结) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [我的CSDN](https://blog.csdn.net/duzhuang2399/article/details/133914782) 📚 **文章目录** - [所有文章](https://github.com/xuchengsheng/spring-reading) 🔗 **源码地址** - [BeanFactoryAware源码](https://github.com/xuchengsheng/spring-reading/tree/master/spring-aware/spring-aware-beanFactoryAware) ### 二、接口描述 `BeanFactoryAware` 接口,允许 Spring bean 获得其所在的 `BeanFactory` 的引用。当一个 bean 实现了这个接口,Spring 容器在初始化该 bean 时,会自动调用 `setBeanFactory()` 方法,并传递一个 `BeanFactory` 实例。 ### 三、接口源码 `BeanFactoryAware` 是 Spring 框架自 11.03.2003 开始引入的一个核心接口。允许 Spring beans 获知并与其所在的 `BeanFactory` 进行交互。这为 beans 提供了直接访问 `BeanFactory` 的能力,进而可以查询和交互其他的 beans。 ```java /** * 由希望知道其所属的 BeanFactory 的 beans 实现的接口。 * * 例如,beans 可以通过工厂查找合作的 beans(依赖查找)。 * 请注意,大多数 beans 会选择通过相应的 bean 属性或构造函数参数 * 接收对合作 beans 的引用(依赖注入)。 * * 有关所有 bean 生命周期方法的列表,请参阅 * BeanFactory BeanFactory javadocs。 * * @author Rod Johnson * @author Chris Beams * @since 11.03.2003 * @see BeanNameAware * @see BeanClassLoaderAware * @see InitializingBean * @see org.springframework.context.ApplicationContextAware */ public interface BeanFactoryAware extends Aware { /** * 向 bean 实例提供其拥有的工厂的回调。 * 在正常 bean 属性填充之后但在初始化回调之前(如 * InitializingBean#afterPropertiesSet() 或自定义的初始化方法)调用。 * @param beanFactory 拥有的 BeanFactory(永远不会是 null)。 * bean 可以立即调用工厂上的方法。 * @throws BeansException 初始化错误的情况下 * @see BeanInitializationException */ void setBeanFactory(BeanFactory beanFactory) throws BeansException; } ``` ### 四、主要功能 1. **获取 `BeanFactory` 引用** + 通过实现 `BeanFactoryAware` 接口并重写 `setBeanFactory` 方法,bean 在初始化过程中会收到其所属的 `BeanFactory` 的引用。Spring 容器会自动为实现了该接口的 bean 调用 `setBeanFactory` 方法。 2. **依赖查找** + 一旦 bean 有了 `BeanFactory` 的引用,它就可以使用这个工厂来动态地查找其他 beans。这种方式被称为“依赖查找”(Dependency Lookup),与常见的“依赖注入”(Dependency Injection)方式相对。 3. **与 `BeanFactory` 进行交互** + 获取 `BeanFactory` 的引用不仅仅是为了查找其他 beans,bean 还可以与其所在的 `BeanFactory` 进行更广泛的互动,例如检查 bean 的作用域、检查 bean 是否为单例、或获取 bean 的别名等。 ### 五、最佳实践 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`UserService`类型的bean,最后调用`validateUser`方法。 ```java public class BeanNameAwareApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); UserService userService = context.getBean(UserService.class); userService.validateUser("root", "123456"); } } ``` 使用`@ComponentScan`注解,告诉 Spring 容器去 "`com.xcs.spring.validate`" "`com.xcs.spring.service`"扫描包及其子包 ```java @Configuration @ComponentScan({"com.xcs.spring.validate", "com.xcs.spring.service"}) public class MyConfiguration { } ``` **UserValidator**是一个简单的验证器接口,具有一个方法 `validate`,用于验证用户名和密码是否有效。SimpleUserValidator是一个实现。它进行简单的验证,仅检查用户名和密码是否为非空。`ComplexUserValidator` 是 `UserValidator` 接口的另一个实现。这个验证器有点复杂,除了检查用户名和密码是否为空外,还检查用户名的长度是否大于 5 以及密码的长度是否大于 8。 ```java public interface UserValidator { boolean validate(String username, String password); } @Component("simpleUserValidator") public class SimpleUserValidator implements UserValidator { @Override public boolean validate(String username, String password) { System.out.println("使用SimpleUserValidator"); return username != null && password != null; } } @Component("complexUserValidator") public class ComplexUserValidator implements UserValidator { @Override public boolean validate(String username, String password) { System.out.println("使用ComplexUserValidator"); return username != null && username.length() > 5 && password != null && password.length() > 8; } } ``` `UserService` 类利用了 Spring 的 `BeanFactoryAware` 和 `InitializingBean` 接口,动态地选择了一个验证器。这种设计提供了极大的灵活性,允许 `UserService` 根据不同的配置或条件使用不同的验证方法。这也意味着在未来,如果需要添加更多的验证方法,只需简单地添加新的验证器实现,然后在 `someConfigurationMethod()` 中进行相应的调整。 ```java @Service public class UserService implements BeanFactoryAware, InitializingBean { private BeanFactory beanFactory; private UserValidator userValidator; @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = beanFactory; } @Override public void afterPropertiesSet() throws Exception { if (someConfigurationMethod()) { userValidator = beanFactory.getBean("simpleUserValidator", UserValidator.class); } else { userValidator = beanFactory.getBean("complexUserValidator", UserValidator.class); } } public void validateUser(String username, String password) { boolean success = userValidator.validate(username, password); if (success){ System.out.println("验证账号密码成功"); }else{ System.out.println("验证账号密码失败"); } } private boolean someConfigurationMethod() { return true; } } ``` 运行结果发现,使用了`SimpleUserValidator`来验证账号密码,并且验证成功。 ```java 使用SimpleUserValidator 验证账号密码成功 ``` ### 六、时序图 ~~~mermaid sequenceDiagram Title: BeanFactoryAware时序图 participant BeanFactoryAwareApplication participant AnnotationConfigApplicationContext participant AbstractApplicationContext participant DefaultListableBeanFactory participant AbstractBeanFactory participant DefaultSingletonBeanRegistry participant AbstractAutowireCapableBeanFactory participant UserService BeanFactoryAwareApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)
创建上下文 AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()
刷新上下文 AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory)
初始化Bean工厂 AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons()
实例化单例 DefaultListableBeanFactory->>AbstractBeanFactory:getBean(name)
获取Bean AbstractBeanFactory->>AbstractBeanFactory:doGetBean(name,requiredType,args,typeCheckOnly)
执行获取Bean AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton(beanName,singletonFactory)
获取单例Bean DefaultSingletonBeanRegistry-->>AbstractBeanFactory:getObject()
获取Bean实例 AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean(beanName,mbd,args)
创建Bean AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:doCreateBean(beanName,mbd,args)
执行Bean创建 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:initializeBean(beanName,bean,mbd)
负责bean的初始化 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:invokeAwareMethods(beanName, bean)
调用Aware方法 AbstractAutowireCapableBeanFactory->>UserService:setBeanFactory(beanFactory)
设置beanFactory AbstractAutowireCapableBeanFactory-->>AbstractBeanFactory:返回Bean对象 AbstractBeanFactory-->>DefaultListableBeanFactory:返回Bean对象 AnnotationConfigApplicationContext-->>BeanFactoryAwareApplication:初始化完成 ~~~ ### 七、源码分析 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`UserService`类型的bean,最后调用`validateUser`方法。 ```java public class BeanNameAwareApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); UserService userService = context.getBean(UserService.class); userService.validateUser("root", "123456"); } } ``` 在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法 ```java public AnnotationConfigApplicationContext(Class... componentClasses) { this(); register(componentClasses); refresh(); } ``` 在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 ```java @Override public void refresh() throws BeansException, IllegalStateException { // ... [代码部分省略以简化] // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization`方法中,会继续调用`DefaultListableBeanFactory`类中的`preInstantiateSingletons`方法来完成所有剩余非懒加载的单列Bean对象。 ```java /** * 完成此工厂的bean初始化,实例化所有剩余的非延迟初始化单例bean。 * * @param beanFactory 要初始化的bean工厂 */ protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { // ... [代码部分省略以简化] // 完成所有剩余非懒加载的单列Bean对象。 beanFactory.preInstantiateSingletons(); } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons`方法中,主要的核心目的是预先实例化所有非懒加载的单例bean。在Spring的上下文初始化完成后,该方法会被触发,以确保所有单例bean都被正确地创建并初始化。其中`getBean(beanName)`是此方法的核心操作。对于容器中定义的每一个单例bean,它都会调用`getBean`方法,这将触发bean的实例化、初始化及其依赖的注入。如果bean之前没有被创建过,那么这个调用会导致其被实例化和初始化。 ```java public void preInstantiateSingletons() throws BeansException { // ... [代码部分省略以简化] // 循环遍历所有bean的名称 for (String beanName : beanNames) { getBean(beanName); } // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#getBean()`方法中,又调用了`doGetBean`方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,`doGetBean`负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。 ```java @Override public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中,首先检查所请求的bean是否是一个单例并且已经创建。如果尚未创建,它将创建一个新的实例。在这个过程中,它处理可能的异常情况,如循环引用,并确保返回的bean是正确的类型。这是Spring容器bean生命周期管理的核心部分。 ```java protected T doGetBean( String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException { // ... [代码部分省略以简化] // 开始创建bean实例 if (mbd.isSingleton()) { // 如果bean是单例的,我们会尝试从单例缓存中获取它 // 如果不存在,则使用lambda创建一个新的实例 sharedInstance = getSingleton(beanName, () -> { try { // 尝试创建bean实例 return createBean(beanName, mbd, args); } catch (BeansException ex) { // ... [代码部分省略以简化] } }); // 对于某些bean(例如FactoryBeans),可能需要进一步处理以获取真正的bean实例 beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } // ... [代码部分省略以简化] // 确保返回的bean实例与请求的类型匹配 return adaptBeanInstance(name, beanInstance, requiredType); } ``` 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()`方法中,主要负责从单例缓存中获取一个已存在的bean实例,或者使用提供的`ObjectFactory`创建一个新的实例。这是确保bean在Spring容器中作为单例存在的关键部分。 ```java public Object getSingleton(String beanName, ObjectFactory singletonFactory) { // 断言bean名称不能为空 Assert.notNull(beanName, "Bean name must not be null"); // 同步访问单例对象缓存,确保线程安全 synchronized (this.singletonObjects) { // 从缓存中获取单例对象 Object singletonObject = this.singletonObjects.get(beanName); // 如果缓存中没有找到 if (singletonObject == null) { // ... [代码部分省略以简化] try { // 使用工厂创建新的单例实例 singletonObject = singletonFactory.getObject(); newSingleton = true; } catch (IllegalStateException ex) { // ... [代码部分省略以简化] } catch (BeanCreationException ex) { // ... [代码部分省略以简化] } finally { // ... [代码部分省略以简化] } // ... [代码部分省略以简化] } // 返回单例对象 return singletonObject; } } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()`方法中,主要的逻辑是调用 `doCreateBean`,这是真正进行 bean 实例化、属性填充和初始化的地方。这个方法会返回新创建的 bean 实例。 ```java @Override protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // ... [代码部分省略以简化] try { // 正常的bean实例化、属性注入和初始化。 // 这里是真正进行bean创建的部分。 Object beanInstance = doCreateBean(beanName, mbdToUse, args); // 记录bean成功创建的日志 if (logger.isTraceEnabled()) { logger.trace("Finished creating instance of bean '" + beanName + "'"); } return beanInstance; } catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { // ... [代码部分省略以简化] } catch (Throwable ex) { // ... [代码部分省略以简化] } } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中,`initializeBean`方法是bean初始化,确保bean是完全配置和准备好的。 ```java protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // 声明一个对象,后续可能用于存放初始化后的bean或它的代理对象 Object exposedObject = bean; // ... [代码部分省略以简化] try { // ... [代码部分省略以简化] // bean初始化 exposedObject = initializeBean(beanName, exposedObject, mbd); } catch (Throwable ex) { // ... [代码部分省略以简化] } // 返回创建和初始化后的bean return exposedObject; } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean`方法中,`invokeAwareMethods(beanName, bean)`是一个非常重要的步骤。这个方法是为了处理实现了Spring的`Aware`接口族的Beans(例如`BeanNameAware`, `BeanFactoryAware`等)。如果提供的bean实现了任何这些接口,该方法会回调相应的`Aware`方法。 ```java protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { // ... [代码部分省略以简化] invokeAwareMethods(beanName, bean); // ... [代码部分省略以简化] return wrappedBean; } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeAwareMethods`方法中,用于处理实现了 Spring `Aware` 接口族的 beans。当一个 bean 实现了如 `BeanNameAware`、`BeanClassLoaderAware` 或 `BeanFactoryAware` 等接口时,此方法确保正确的回调方法被调用,从而为 bean 提供关于其运行环境或其他相关信息。 ```java private void invokeAwareMethods(String beanName, Object bean) { if (bean instanceof Aware) { if (bean instanceof BeanNameAware) { // ... [代码部分省略以简化] } if (bean instanceof BeanClassLoaderAware) { // ... [代码部分省略以简化] } if (bean instanceof BeanFactoryAware) { ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this); } } } ``` 最后执行到我们自定义的逻辑中,容器将调用 `setBeanFactory()` 方法,并将当前的 bean factory 实例作为参数传递。 ```java @Service public class UserService implements BeanFactoryAware, InitializingBean { private BeanFactory beanFactory; private UserValidator userValidator; @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = beanFactory; } @Override public void afterPropertiesSet() throws Exception { // ... [代码部分省略以简化] } public void validateUser(String username, String password) { // ... [代码部分省略以简化] } private boolean someConfigurationMethod() { return true; } } ``` ### 八、注意事项 1. **生命周期时机** + `setBeanFactory` 方法是在 bean 属性设置之后但在其他初始化方法(如 `@PostConstruct`、`InitializingBean#afterPropertiesSet` 或指定的初始化方法)之前调用的。 2. **避免循环依赖** + 当 beans 通过 `BeanFactory` 查找其他 beans 时,可能会出现循环依赖的情况。例如,bean A 在其 `setBeanFactory` 方法中查找 bean B,而 bean B 在其 `setBeanFactory` 方法中查找 bean A。这种情况会导致容器初始化失败。 3. **知道 bean 的作用域** + 当从 `BeanFactory` 获取 beans 时,请记住 bean 的作用域。如果 bean 是原型作用域的,每次 `getBean` 调用都会返回一个新的实例。 4. **不要过度自定义** + 除非有很好的理由,否则应避免在 `setBeanFactory` 方法中执行大量的自定义逻辑。这会使 bean 的初始化过程变得复杂,并可能导致不可预见的副作用。 ### 九、总结 #### 最佳实践总结 1. **构建与配置** + 在 `BeanNameAwareApplication` 启动类中,使用了 `AnnotationConfigApplicationContext` 来基于 Java 配置类 (`MyConfiguration`) 初始化 Spring 上下文。这是一个 Java-based 的配置方法,与传统的 XML-based 配置相比,更加直观和灵活。 2. **组件扫描** + `MyConfiguration` 配置类使用 `@ComponentScan` 注解指定了需要被扫描的包路径。Spring 容器会自动扫描这些包以及其子包下的组件,并将它们注册为 Spring beans。 3. **验证器设计** + 我们设计了一个 `UserValidator` 接口,以及两个实现该接口的类:`SimpleUserValidator` 和 `ComplexUserValidator`。这两个验证器具有不同的验证逻辑,以满足不同的验证需求。 4. **动态选择验证器** + `UserService` 类是此应用的核心,它根据某些配置动态地从 `BeanFactory` 中选择一个验证器。这是通过实现 `BeanFactoryAware` 和 `InitializingBean` 接口来完成的:`BeanFactoryAware` 允许 `UserService` 访问 Spring 容器的 `BeanFactory`。InitializingBean` 确保在所有属性(例如依赖注入)设置完毕后,选择合适的验证器。 5. **运行与输出** + 当调用 `validateUser` 方法验证用户名和密码时,根据所选择的验证器(在此示例中是 `SimpleUserValidator`),将输出相应的验证信息。此外,验证器本身也输出了它正在使用的验证方法。 #### 源码分析总结 1. **应用启动与上下文初始化** + 当启动类 `BeanNameAwareApplication` 被执行,一个新的 `AnnotationConfigApplicationContext` 被创建并初始化,其中传入了配置类 `MyConfiguration`。 2. **配置类与组件扫描** + `MyConfiguration` 是一个 Java 配置类,它告诉 Spring 容器去扫描特定的包以查找组件。 3. **单例bean的预实例化** + 在上下文的 `refresh()` 方法中,Spring 会预先实例化所有非懒加载的单例bean。这意味着在容器启动时,这些bean会被初始化。 4. **Bean的实例化和初始化** + 在上下文刷新的过程中,Spring 容器会逐个创建并初始化所有的单例bean。`doCreateBean` 方法负责实例化bean、注入依赖、并调用任何初始化方法。 5. **处理 Aware 接口** + 对于实现了 `Aware` 接口的bean,如 `BeanFactoryAware`,在初始化过程中,Spring 容器会调用相应的 `Aware` 方法(例如,`setBeanFactory`)。这使得bean可以获得关于其运行环境的信息或其他 Spring 功能。 6. **自定义逻辑执行** + 一旦bean被初始化,并且所有的 `Aware` 方法都被调用,就可以执行自定义逻辑。在这个例子中,这是通过 `UserService` 的 `validateUser` 方法来完成的。 7. **BeanFactoryAware 的特性** + 通过实现 `BeanFactoryAware`,`UserService` 能够获得对 `BeanFactory` 的访问权限。这使得它可以在运行时动态地从 `BeanFactory` 中获取bean,如在示例中的 `UserValidator`。 ================================================ FILE: spring-aware/spring-aware-beanFactoryAware/pom.xml ================================================ spring-aware com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-aware-beanFactoryAware ================================================ FILE: spring-aware/spring-aware-beanFactoryAware/src/main/java/com/xcs/spring/BeanFactoryAwareApplication.java ================================================ package com.xcs.spring; import com.xcs.spring.config.MyConfiguration; import com.xcs.spring.service.UserService; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年09月16日 16时09分 **/ public class BeanFactoryAwareApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); UserService userService = context.getBean(UserService.class); userService.validateUser("root", "123456"); } } ================================================ FILE: spring-aware/spring-aware-beanFactoryAware/src/main/java/com/xcs/spring/config/MyConfiguration.java ================================================ package com.xcs.spring.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; /** * @author xcs * @date 2023年09月19日 16时35分 **/ @Configuration @ComponentScan({"com.xcs.spring.validate", "com.xcs.spring.service"}) public class MyConfiguration { } ================================================ FILE: spring-aware/spring-aware-beanFactoryAware/src/main/java/com/xcs/spring/service/UserService.java ================================================ package com.xcs.spring.service; import com.xcs.spring.validate.UserValidator; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.InitializingBean; import org.springframework.stereotype.Service; @Service public class UserService implements BeanFactoryAware, InitializingBean { private BeanFactory beanFactory; private UserValidator userValidator; @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = beanFactory; } @Override public void afterPropertiesSet() throws Exception { if (someConfigurationMethod()) { userValidator = beanFactory.getBean("simpleUserValidator", UserValidator.class); } else { userValidator = beanFactory.getBean("complexUserValidator", UserValidator.class); } } public void validateUser(String username, String password) { boolean success = userValidator.validate(username, password); if (success){ System.out.println("验证账号密码成功"); }else{ System.out.println("验证账号密码失败"); } } private boolean someConfigurationMethod() { return true; } } ================================================ FILE: spring-aware/spring-aware-beanFactoryAware/src/main/java/com/xcs/spring/validate/ComplexUserValidator.java ================================================ package com.xcs.spring.validate; import org.springframework.stereotype.Component; @Component("complexUserValidator") public class ComplexUserValidator implements UserValidator { @Override public boolean validate(String username, String password) { System.out.println("使用ComplexUserValidator"); return username != null && username.length() > 5 && password != null && password.length() > 8; } } ================================================ FILE: spring-aware/spring-aware-beanFactoryAware/src/main/java/com/xcs/spring/validate/SimpleUserValidator.java ================================================ package com.xcs.spring.validate; import org.springframework.stereotype.Component; @Component("simpleUserValidator") public class SimpleUserValidator implements UserValidator { @Override public boolean validate(String username, String password) { System.out.println("使用SimpleUserValidator"); return username != null && password != null; } } ================================================ FILE: spring-aware/spring-aware-beanFactoryAware/src/main/java/com/xcs/spring/validate/UserValidator.java ================================================ package com.xcs.spring.validate; public interface UserValidator { boolean validate(String username, String password); } ================================================ FILE: spring-aware/spring-aware-beanNameAware/README.md ================================================ ## BeanNameAware - [BeanNameAware](#beannameaware) - [一、基本信息](#一基本信息) - [二、接口描述](#二接口描述) - [三、接口源码](#三接口源码) - [四、主要功能](#四主要功能) - [五、最佳实践](#五最佳实践) - [六、时序图](#六时序图) - [七、源码分析](#七源码分析) - [八、注意事项](#八注意事项) - [九、总结](#九总结) - [最佳实践总结](#最佳实践总结) - [源码分析总结](#源码分析总结) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [我的CSDN]() 📚 **文章目录** - [所有文章](https://github.com/xuchengsheng/spring-reading) 🔗 **源码地址** - [BeanNameAware源码](https://github.com/xuchengsheng/spring-reading/tree/master/spring-aware/spring-aware-beanNameAware) ### 二、接口描述 `BeanNameAware` 接口。当一个 Bean 实现了此接口,可以感知其在 Spring 容器中的名称。 ### 三、接口源码 `BeanNameAware` 是 Spring 框架自 01.11.2003 开始引入的一个核心接口。实现`BeanNameAware`接口的对象会在Spring容器中被自动注入Bean的名称。 ```java /** * 由希望知道其在 bean 工厂中名称的 beans 实现的接口。 * 注意通常不推荐一个对象依赖于其 bean 名称,因为这可能导致对外部配置的脆弱依赖, * 以及可能的不必要的对 Spring API 的依赖。 * * 有关所有 bean 生命周期方法的列表,请参见 * BeanFactory BeanFactory javadocs。 * * @author Juergen Hoeller * @author Chris Beams * @since 01.11.2003 * @see BeanClassLoaderAware * @see BeanFactoryAware * @see InitializingBean */ public interface BeanNameAware extends Aware { /** * 设置在创建此 bean 的 bean 工厂中的 bean 的名称。 * 此方法在填充常规 bean 属性之后被调用,但在如 InitializingBean#afterPropertiesSet() 这样的 * 初始化回调或自定义初始化方法之前被调用。 * @param name 工厂中的 bean 的名称。注意,这个名称是工厂中使用的实际 bean 名称, * 这可能与最初指定的名称不同:尤其对于内部 bean 名称,实际的 bean 名称可能已通过添加 "#..." 后缀变得唯一。 * 如果需要,可以使用 BeanFactoryUtils#originalBeanName(String) 方法来提取没有后缀的原始 bean 名称。 */ void setBeanName(String name); } ``` ### 四、主要功能 1. **提供 `setBeanName` 方法** + 当一个 Bean 实现了 `BeanNameAware` 接口,它需要提供 `setBeanName` 方法的实现。这个方法有一个参数,即该 Bean 在 Spring 容器中的名称。 2. **自动回调** + 当 Spring 容器创建并配置一个实现了 `BeanNameAware` 接口的 Bean 时,容器会自动回调 `setBeanName` 方法,并传入该 Bean 在容器中的名称。这意味着我们不需要显式地调用这个方法;Spring 容器会自动处理。 3. **获取 Bean 的名称** + 有时,Bean 可能需要知道其在容器中的名称以执行特定的逻辑或功能,或者为了日志记录或其他目的。通过实现 `BeanNameAware`,Bean 可以轻松获得此信息。 ### 五、最佳实践 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。 ```java public class BeanNameAwareApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); } } ``` 使用`@ComponentScan`注解,告诉 Spring 容器去 "`com.xcs.spring.service`" 扫描包及其子包 ```java @Configuration @ComponentScan("com.xcs.spring.service") public class MyConfiguration { } ``` `MyBasePayService` 是一个抽象类,结合了 Spring 的三个特殊接口:`BeanNameAware`(让 Bean 知道其名字)、`InitializingBean`(Bean 属性设置后的初始化操作)和 `DisposableBean`(Bean 销毁前的操作)。 ```java public abstract class MyBasePayService implements BeanNameAware, InitializingBean, DisposableBean { private String beanName; @Override public void setBeanName(String beanName) { this.beanName = beanName; } @Override public void afterPropertiesSet() throws Exception { System.out.println("Module " + beanName + " has been registered."); } @Override public void destroy() throws Exception { System.out.println("Module " + beanName + " has been unregistered."); } } ``` `MyAliPayService` 和 `MyWeChatPayService` 是两个支付服务类,都继承自 `MyBasePayService`,因此它们会自动获得与 Spring 容器生命周期相关的基本功能。这种设计方式为多个支付服务提供了一个共同的生命周期管理模式,同时允许每个服务添加其特定的支付逻辑。 ```java @Service public class MyAliPayService extends MyBasePayService{ } @Service public class MyWeChatPayService extends MyBasePayService{ } ``` 运行结果发现,当 Spring 容器启动并初始化 Beans 时,它正确地识别并实例化了 `MyAliPayService` 和 `MyWeChatPayService` 这两个服务。由于这两个服务都继承自 `MyBasePayService`,在 Bean 的属性被设置之后(即在 `afterPropertiesSet()` 方法中),它们分别打印出了 "Module myAliPayService has been registered." 和 "Module myWeChatPayService has been registered." 这两条信息,提供了一个共同的生命周期管理模式。 ```java Module myAliPayService has been registered. Module myWeChatPayService has been registered. ``` ### 六、时序图 ~~~mermaid sequenceDiagram Title: BeanNameAware时序图 participant BeanNameAwareApplication participant AnnotationConfigApplicationContext participant AbstractApplicationContext participant DefaultListableBeanFactory participant AbstractBeanFactory participant DefaultSingletonBeanRegistry participant AbstractAutowireCapableBeanFactory participant MyBasePayService BeanNameAwareApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)
创建上下文 AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()
刷新上下文 AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory)
初始化Bean工厂 AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons()
实例化单例 DefaultListableBeanFactory->>AbstractBeanFactory:getBean(name)
获取Bean AbstractBeanFactory->>AbstractBeanFactory:doGetBean(name,requiredType,args,typeCheckOnly)
执行获取Bean AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton(beanName,singletonFactory)
获取单例Bean DefaultSingletonBeanRegistry-->>AbstractBeanFactory:getObject()
获取Bean实例 AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean(beanName,mbd,args)
创建Bean AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:doCreateBean(beanName,mbd,args)
执行Bean创建 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:initializeBean(beanName,bean,mbd)
负责bean的初始化 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:invokeAwareMethods(beanName, bean)
调用Aware方法 AbstractAutowireCapableBeanFactory->>MyBasePayService:setBeanName(beanName)
设置Bean名称 AbstractAutowireCapableBeanFactory-->>AbstractBeanFactory:返回Bean对象 AbstractBeanFactory-->>DefaultListableBeanFactory:返回Bean对象 AnnotationConfigApplicationContext-->>BeanNameAwareApplication:初始化完成 ~~~ ### 七、源码分析 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。 ```java public class BeanNameAwareApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); } } ``` 在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法 ```java public AnnotationConfigApplicationContext(Class... componentClasses) { this(); register(componentClasses); refresh(); } ``` 在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 ```java @Override public void refresh() throws BeansException, IllegalStateException { // ... [代码部分省略以简化] // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization`方法中,会继续调用`DefaultListableBeanFactory`类中的`preInstantiateSingletons`方法来完成所有剩余非懒加载的单列Bean对象。 ```java /** * 完成此工厂的bean初始化,实例化所有剩余的非延迟初始化单例bean。 * * @param beanFactory 要初始化的bean工厂 */ protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { // ... [代码部分省略以简化] // 完成所有剩余非懒加载的单列Bean对象。 beanFactory.preInstantiateSingletons(); } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons`方法中,主要的核心目的是预先实例化所有非懒加载的单例bean。在Spring的上下文初始化完成后,该方法会被触发,以确保所有单例bean都被正确地创建并初始化。其中`getBean(beanName)`是此方法的核心操作。对于容器中定义的每一个单例bean,它都会调用`getBean`方法,这将触发bean的实例化、初始化及其依赖的注入。如果bean之前没有被创建过,那么这个调用会导致其被实例化和初始化。 ```java public void preInstantiateSingletons() throws BeansException { // ... [代码部分省略以简化] // 循环遍历所有bean的名称 for (String beanName : beanNames) { getBean(beanName); } // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#getBean()`方法中,又调用了`doGetBean`方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,`doGetBean`负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。 ```java @Override public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中,首先检查所请求的bean是否是一个单例并且已经创建。如果尚未创建,它将创建一个新的实例。在这个过程中,它处理可能的异常情况,如循环引用,并确保返回的bean是正确的类型。这是Spring容器bean生命周期管理的核心部分。 ```java protected T doGetBean( String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException { // ... [代码部分省略以简化] // 开始创建bean实例 if (mbd.isSingleton()) { // 如果bean是单例的,我们会尝试从单例缓存中获取它 // 如果不存在,则使用lambda创建一个新的实例 sharedInstance = getSingleton(beanName, () -> { try { // 尝试创建bean实例 return createBean(beanName, mbd, args); } catch (BeansException ex) { // ... [代码部分省略以简化] } }); // 对于某些bean(例如FactoryBeans),可能需要进一步处理以获取真正的bean实例 beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } // ... [代码部分省略以简化] // 确保返回的bean实例与请求的类型匹配 return adaptBeanInstance(name, beanInstance, requiredType); } ``` 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()`方法中,主要负责从单例缓存中获取一个已存在的bean实例,或者使用提供的`ObjectFactory`创建一个新的实例。这是确保bean在Spring容器中作为单例存在的关键部分。 ```java public Object getSingleton(String beanName, ObjectFactory singletonFactory) { // 断言bean名称不能为空 Assert.notNull(beanName, "Bean name must not be null"); // 同步访问单例对象缓存,确保线程安全 synchronized (this.singletonObjects) { // 从缓存中获取单例对象 Object singletonObject = this.singletonObjects.get(beanName); // 如果缓存中没有找到 if (singletonObject == null) { // ... [代码部分省略以简化] try { // 使用工厂创建新的单例实例 singletonObject = singletonFactory.getObject(); newSingleton = true; } catch (IllegalStateException ex) { // ... [代码部分省略以简化] } catch (BeanCreationException ex) { // ... [代码部分省略以简化] } finally { // ... [代码部分省略以简化] } // ... [代码部分省略以简化] } // 返回单例对象 return singletonObject; } } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()`方法中,主要的逻辑是调用 `doCreateBean`,这是真正进行 bean 实例化、属性填充和初始化的地方。这个方法会返回新创建的 bean 实例。 ```java @Override protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // ... [代码部分省略以简化] try { // 正常的bean实例化、属性注入和初始化。 // 这里是真正进行bean创建的部分。 Object beanInstance = doCreateBean(beanName, mbdToUse, args); // 记录bean成功创建的日志 if (logger.isTraceEnabled()) { logger.trace("Finished creating instance of bean '" + beanName + "'"); } return beanInstance; } catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { // ... [代码部分省略以简化] } catch (Throwable ex) { // ... [代码部分省略以简化] } } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中,`initializeBean`方法是bean初始化,确保bean是完全配置和准备好的。 ```java protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // 声明一个对象,后续可能用于存放初始化后的bean或它的代理对象 Object exposedObject = bean; // ... [代码部分省略以简化] try { // ... [代码部分省略以简化] // bean初始化 exposedObject = initializeBean(beanName, exposedObject, mbd); } catch (Throwable ex) { // ... [代码部分省略以简化] } // 返回创建和初始化后的bean return exposedObject; } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean`方法中,`invokeAwareMethods(beanName, bean)`是一个非常重要的步骤。这个方法是为了处理实现了Spring的`Aware`接口族的Beans(例如`BeanNameAware`, `BeanFactoryAware`等)。如果提供的bean实现了任何这些接口,该方法会回调相应的`Aware`方法。 ```java protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { // ... [代码部分省略以简化] invokeAwareMethods(beanName, bean); // ... [代码部分省略以简化] return wrappedBean; } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeAwareMethods`方法中,用于处理实现了 Spring `Aware` 接口族的 beans。当一个 bean 实现了如 `BeanNameAware`、`BeanClassLoaderAware` 或 `BeanFactoryAware` 等接口时,此方法确保正确的回调方法被调用,从而为 bean 提供关于其运行环境或其他相关信息。 ```java private void invokeAwareMethods(String beanName, Object bean) { if (bean instanceof Aware) { if (bean instanceof BeanNameAware) { ((BeanNameAware) bean).setBeanName(beanName); } if (bean instanceof BeanClassLoaderAware) { // ... [代码部分省略以简化] } if (bean instanceof BeanFactoryAware) { // ... [代码部分省略以简化] } } } ``` 最后执行到我们自定义的逻辑中,我们将这个名称存储在 `beanName` 变量中,以便后续使用。 ```java public abstract class MyBasePayService implements BeanNameAware, InitializingBean, DisposableBean { private String beanName; @Override public void setBeanName(String beanName) { this.beanName = beanName; } @Override public void afterPropertiesSet() throws Exception { System.out.println("Module " + beanName + " has been registered."); } @Override public void destroy() throws Exception { System.out.println("Module " + beanName + " has been unregistered."); } } ``` ### 八、注意事项 1. **与其他生命周期方法的顺序** + `setBeanName` 方法的调用是在其他许多生命周期方法之前的,例如 `InitializingBean#afterPropertiesSet` 和任何定义的初始化方法。因此,我们不应该在 `setBeanName` 方法内部预期其他配置或初始化逻辑已经完成。 2. **仅在容器管理的 Beans 中有效** + 只有当 bean 是由 Spring 容器管理时,`BeanNameAware` 才会生效。简单地创建一个类的实例(例如通过 `new` 关键字)并不会触发 `BeanNameAware` 功能。 3. **与其他 Aware 接口的组合使用** + 当一个 bean 同时实现多个 `Aware` 接口时,需要注意它们的调用顺序。例如,`BeanNameAware`、`BeanFactoryAware` 和 `ApplicationContextAware` 的回调方法调用顺序是固定的。 4. **Bean 名称的唯一性** + Spring 容器内的 bean 名称是唯一的,但如果使用别名,同一个 bean 可能会有多个名称。当实现 `BeanNameAware` 时,我们获得的是 bean 的主要名称。 ### 九、总结 #### 最佳实践总结 1. **启动及配置** + 我们使用了 `AnnotationConfigApplicationContext` 作为 Spring 容器的入口,专门为基于 Java 的配置设计。该容器被初始化并加载了 `MyConfiguration` 类,它定义了应用的主要配置。 2. **组件扫描** + 通过在 `MyConfiguration` 类中使用 `@ComponentScan` 注解,我们告诉 Spring 容器去扫描 "`com.xcs.spring.service`" 包及其子包,以找到和管理 Beans。 3. **生命周期管理** + `MyBasePayService`类展示了如何利用 Spring 的特殊接口,例如 `BeanNameAware`、`InitializingBean` 和 `DisposableBean`,来插入到 Bean 的生命周期的特定阶段。当一个 Bean 实例被创建并管理 by Spring, 它会被赋予一个名称(通过 `BeanNameAware`)、在所有属性设置后初始化(通过 `InitializingBean`)以及在应用结束或 Bean 被销毁时执行特定操作(通过 `DisposableBean`)。 4. **具体的服务实现** + 有两个具体的支付服务,`MyAliPayService` 和 `MyWeChatPayService`,它们都继承了 `MyBasePayService`。这意味着它们都自动继承了上述的生命周期管理功能。当 Spring 容器启动时,这两个服务的相关生命周期方法会被调用,如我们从打印的消息中所看到的。 5. **实际效果** + 当应用运行时,每个服务类都会打印出其已经被注册和注销的消息,这是由于它们都继承了 `MyBasePayService` 中定义的生命周期方法。 #### 源码分析总结 1. **启动和上下文初始化** + 使用`AnnotationConfigApplicationContext`初始化Spring容器,其中传递了配置类`MyConfiguration`。 2. **注册和刷新上下文** + 在`AnnotationConfigApplicationContext`构造函数中,`register()`方法注册配置类,而`refresh()`方法开始加载和初始化beans。 3. **开始bean的实例化** + `refresh()`方法进一步调用了`finishBeanFactoryInitialization(beanFactory)`,该方法负责预先实例化所有非懒加载的单例bean。 4. **实例化单例bean** + `preInstantiateSingletons()`方法遍历所有bean名称,并通过调用`getBean(beanName)`来实例化和初始化bean。 5. **创建bean实例** + `doGetBean()`是实际进行bean创建的核心方法,它处理了bean的实例化、依赖注入和初始化等逻辑。 6. **处理Aware接口族** + 在bean的初始化过程中,`invokeAwareMethods(beanName, bean)`被调用,负责处理实现了`Aware`接口族的beans。这是我们的`BeanNameAware`接口发挥作用的地方,当bean实现此接口时,其`setBeanName`方法会被调用。 7. **用户定义的逻辑** + 在`MyBasePayService`类中,我们实现了`BeanNameAware`接口,并重写了`setBeanName`方法来保存bean的名称。此外,还使用了`InitializingBean`和`DisposableBean`接口来在bean的生命周期的特定时刻执行自定义的逻辑。 ================================================ FILE: spring-aware/spring-aware-beanNameAware/pom.xml ================================================ spring-aware com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-aware-beanNameAware ================================================ FILE: spring-aware/spring-aware-beanNameAware/src/main/java/com/xcs/spring/BeanNameAwareApplication.java ================================================ package com.xcs.spring; import com.xcs.spring.config.MyConfiguration; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年09月16日 16时09分 **/ public class BeanNameAwareApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); } } ================================================ FILE: spring-aware/spring-aware-beanNameAware/src/main/java/com/xcs/spring/config/MyConfiguration.java ================================================ package com.xcs.spring.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; /** * @author xcs * @date 2023年09月19日 16时35分 **/ @Configuration @ComponentScan("com.xcs.spring.service") public class MyConfiguration { } ================================================ FILE: spring-aware/spring-aware-beanNameAware/src/main/java/com/xcs/spring/service/MyAliPayService.java ================================================ package com.xcs.spring.service; import org.springframework.stereotype.Service; @Service public class MyAliPayService extends MyBasePayService{ } ================================================ FILE: spring-aware/spring-aware-beanNameAware/src/main/java/com/xcs/spring/service/MyBasePayService.java ================================================ package com.xcs.spring.service; import org.springframework.beans.factory.BeanNameAware; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; public abstract class MyBasePayService implements BeanNameAware, InitializingBean, DisposableBean { private String beanName; @Override public void setBeanName(String beanName) { this.beanName = beanName; } @Override public void afterPropertiesSet() throws Exception { System.out.println("Module " + beanName + " has been registered."); } @Override public void destroy() throws Exception { System.out.println("Module " + beanName + " has been unregistered."); } } ================================================ FILE: spring-aware/spring-aware-beanNameAware/src/main/java/com/xcs/spring/service/MyWeChatPayService.java ================================================ package com.xcs.spring.service; import org.springframework.stereotype.Service; @Service public class MyWeChatPayService extends MyBasePayService{ } ================================================ FILE: spring-aware/spring-aware-embeddedValueResolverAware/README.md ================================================ ## EmbeddedValueResolverAware - [EmbeddedValueResolverAware](#embeddedvalueresolveraware) - [一、基本信息](#一基本信息) - [二、接口描述](#二接口描述) - [三、接口源码](#三接口源码) - [四、主要功能](#四主要功能) - [五、最佳实践](#五最佳实践) - [六、时序图](#六时序图) - [七、源码分析](#七源码分析) - [八、注意事项](#八注意事项) - [九、总结](#九总结) - [最佳实践总结](#最佳实践总结) - [源码分析总结](#源码分析总结) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [我的CSDN](https://blog.csdn.net/duzhuang2399/article/details/133914999) 📚 **文章目录** - [所有文章](https://github.com/xuchengsheng/spring-reading) 🔗 **源码地址** - [EmbeddedValueResolverAware源码](https://github.com/xuchengsheng/spring-reading/tree/master/spring-aware/spring-aware-embeddedValueResolverAware) ### 二、接口描述 `EmbeddedValueResolverAware` 接口,主要用于提供一个字符串值解析器,这可以在 Bean 属性中解析占位符和表达式。如果我们熟悉 Spring 的 `${...}` 占位符和 `#{...}` 表达式,那么这个接口将帮助我们在自定义组件中解析这些值。 ### 三、接口源码 `EmbeddedValueResolverAware` 是 Spring 框架自 3.0.3 开始引入的一个核心接口。实现`EmbeddedValueResolverAware`接口的对象会在Spring容器中被自动注入一个`StringValueResolver`实例。 ```java /** * 如果对象希望被通知一个 StringValueResolver,以解析嵌入的定义值,那么它应实现此接口。 * * 这提供了一个通过 ApplicationContextAware/BeanFactoryAware 接口 * 依赖于完整的 ConfigurableBeanFactory 的替代方法。 * * @author Juergen Hoeller * @author Chris Beams * @since 3.0.3 * @see org.springframework.beans.factory.config.ConfigurableBeanFactory#resolveEmbeddedValue(String) * @see org.springframework.beans.factory.config.ConfigurableBeanFactory#getBeanExpressionResolver() * @see org.springframework.beans.factory.config.EmbeddedValueResolver */ public interface EmbeddedValueResolverAware extends Aware { /** * 设置用于解析嵌入定义值的 StringValueResolver。 */ void setEmbeddedValueResolver(StringValueResolver resolver); } ``` ### 四、主要功能 1. **解析嵌入的字符串值** + 当我们在 Bean 的属性或构造函数参数中有一个值,如 `${property.name}` 或 `#{some.expression}`,这需要被解析成实际的值时,`StringValueResolver` 可以帮助做这件事。 2. **避免对 `ConfigurableBeanFactory` 的直接依赖** + 通过使用 `EmbeddedValueResolverAware`,我们可以间接地得到这种解析功能,而不必直接依赖于整个 `ConfigurableBeanFactory`。这提供了一种更轻量级、更关注特定功能的方法来解析嵌入的值。 3. **自动注入 `StringValueResolver`** + 当我们的 Bean 实现了 `EmbeddedValueResolverAware` 接口,Spring 容器会在 Bean 初始化时自动调用 `setEmbeddedValueResolver` 方法,为其注入一个 `StringValueResolver` 实例。这样,Bean 可以在其生命周期中任何时候使用它来解析字符串值。 ### 五、最佳实践 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyEmbeddedValueResolverAware`类型的bean,最后调用`resolve`方法。 ```java public class EmbeddedValueResolverAwareApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MyEmbeddedValueResolverAware resolverAware = context.getBean(MyEmbeddedValueResolverAware.class); resolverAware.resolve(); } } ``` 这里使用`@Bean`注解,定义了一个Bean,是为了确保 `MyEmbeddedValueResolverAware` 被 Spring 容器执行。 ```java @Configuration public class MyConfiguration { @Bean public MyEmbeddedValueResolverAware myEmbeddedValueResolverAware(){ return new MyEmbeddedValueResolverAware(); } } ``` `MyEmbeddedValueResolverAware`类实现了`EmbeddedValueResolverAware`接口,当Spring容器初始化此类的Bean时,此方法将被调用,容器将传入一个`StringValueResolver`实例,然后通过`resolve()`方法,使用注入的`stringValueResolver`来解析包含占位符和SpEL表达式的字符串,并将解析后的字符串打印到控制台。 ```java public class MyEmbeddedValueResolverAware implements EmbeddedValueResolverAware { private StringValueResolver stringValueResolver; @Override public void setEmbeddedValueResolver(StringValueResolver resolver) { this.stringValueResolver = resolver; } public void resolve() { String resolvedValue = stringValueResolver.resolveStringValue("Hello, ${user.name:xcs}! Today is #{T(java.time.LocalDate).now().toString()}"); System.out.println(resolvedValue); } } ``` 运行结果发现,结合 Spring 的 `Environment` 和 SpEL 功能来解析嵌入的字符串值,并得到了预期的运行结果。 ```java Hello, Lex! Today is 2023-10-03 ``` ### 六、时序图 ~~~mermaid sequenceDiagram Title: EnvironmentAware时序图 participant EmbeddedValueResolverAwareApplication participant AnnotationConfigApplicationContext participant AbstractApplicationContext participant DefaultListableBeanFactory participant AbstractBeanFactory participant DefaultSingletonBeanRegistry participant AbstractAutowireCapableBeanFactory participant ApplicationContextAwareProcessor participant MyEmbeddedValueResolverAware EmbeddedValueResolverAwareApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)
创建上下文 AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()
刷新上下文 AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory)
初始化Bean工厂 AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons()
实例化单例 DefaultListableBeanFactory->>AbstractBeanFactory:getBean(name)
获取Bean AbstractBeanFactory->>AbstractBeanFactory:doGetBean(name,requiredType,args,typeCheckOnly)
执行获取Bean AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton(beanName,singletonFactory)
获取单例Bean DefaultSingletonBeanRegistry-->>AbstractBeanFactory:getObject()
获取Bean实例 AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean(beanName,mbd,args)
创建Bean AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:doCreateBean(beanName,mbd,args)
执行Bean创建 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:initializeBean(beanName,bean,mbd)
负责bean的初始化 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:applyBeanPostProcessorsBeforeInitialization(existingBean, beanName)
调用前置处理器 AbstractAutowireCapableBeanFactory->>ApplicationContextAwareProcessor:postProcessBeforeInitialization(bean,beanName)
触发Aware处理 ApplicationContextAwareProcessor->>ApplicationContextAwareProcessor:invokeAwareInterfaces(bean)
执行Aware回调 ApplicationContextAwareProcessor->>MyEmbeddedValueResolverAware:setEmbeddedValueResolver(resolver)
设置StringValueResolver AbstractAutowireCapableBeanFactory-->>AbstractBeanFactory:返回Bean对象 AbstractBeanFactory-->>DefaultListableBeanFactory:返回Bean对象 AnnotationConfigApplicationContext-->>EmbeddedValueResolverAwareApplication:初始化完成 ~~~ ### 七、源码分析 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyEmbeddedValueResolverAware`类型的bean,最后调用`resolve`方法。 ```java public class EmbeddedValueResolverAwareApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MyEmbeddedValueResolverAware resolverAware = context.getBean(MyEmbeddedValueResolverAware.class); resolverAware.resolve(); } } ``` 在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法 ```java public AnnotationConfigApplicationContext(Class... componentClasses) { this(); register(componentClasses); refresh(); ``` 在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 ```java @Override public void refresh() throws BeansException, IllegalStateException { // ... [代码部分省略以简化] // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization`方法中,会继续调用`DefaultListableBeanFactory`类中的`preInstantiateSingletons`方法来完成所有剩余非懒加载的单列Bean对象。 ```java /** * 完成此工厂的bean初始化,实例化所有剩余的非延迟初始化单例bean。 * * @param beanFactory 要初始化的bean工厂 */ protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { // ... [代码部分省略以简化] // 完成所有剩余非懒加载的单列Bean对象。 beanFactory.preInstantiateSingletons(); } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons`方法中,主要的核心目的是预先实例化所有非懒加载的单例bean。在Spring的上下文初始化完成后,该方法会被触发,以确保所有单例bean都被正确地创建并初始化。其中`getBean(beanName)`是此方法的核心操作。对于容器中定义的每一个单例bean,它都会调用`getBean`方法,这将触发bean的实例化、初始化及其依赖的注入。如果bean之前没有被创建过,那么这个调用会导致其被实例化和初始化。 ```java public void preInstantiateSingletons() throws BeansException { // ... [代码部分省略以简化] // 循环遍历所有bean的名称 for (String beanName : beanNames) { getBean(beanName); } // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#getBean()`方法中,又调用了`doGetBean`方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,`doGetBean`负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。 ```java @Override public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中,首先检查所请求的bean是否是一个单例并且已经创建。如果尚未创建,它将创建一个新的实例。在这个过程中,它处理可能的异常情况,如循环引用,并确保返回的bean是正确的类型。这是Spring容器bean生命周期管理的核心部分。 ```java protected T doGetBean( String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException { // ... [代码部分省略以简化] // 开始创建bean实例 if (mbd.isSingleton()) { // 如果bean是单例的,我们会尝试从单例缓存中获取它 // 如果不存在,则使用lambda创建一个新的实例 sharedInstance = getSingleton(beanName, () -> { try { // 尝试创建bean实例 return createBean(beanName, mbd, args); } catch (BeansException ex) { // ... [代码部分省略以简化] } }); // 对于某些bean(例如FactoryBeans),可能需要进一步处理以获取真正的bean实例 beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } // ... [代码部分省略以简化] // 确保返回的bean实例与请求的类型匹配 return adaptBeanInstance(name, beanInstance, requiredType); } ``` 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()`方法中,主要负责从单例缓存中获取一个已存在的bean实例,或者使用提供的`ObjectFactory`创建一个新的实例。这是确保bean在Spring容器中作为单例存在的关键部分。 ```java public Object getSingleton(String beanName, ObjectFactory singletonFactory) { // 断言bean名称不能为空 Assert.notNull(beanName, "Bean name must not be null"); // 同步访问单例对象缓存,确保线程安全 synchronized (this.singletonObjects) { // 从缓存中获取单例对象 Object singletonObject = this.singletonObjects.get(beanName); // 如果缓存中没有找到 if (singletonObject == null) { // ... [代码部分省略以简化] try { // 使用工厂创建新的单例实例 singletonObject = singletonFactory.getObject(); newSingleton = true; } catch (IllegalStateException ex) { // ... [代码部分省略以简化] } catch (BeanCreationException ex) { // ... [代码部分省略以简化] } finally { // ... [代码部分省略以简化] } // ... [代码部分省略以简化] } // 返回单例对象 return singletonObject; } } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()`方法中,主要的逻辑是调用 `doCreateBean`,这是真正进行 bean 实例化、属性填充和初始化的地方。这个方法会返回新创建的 bean 实例。 ```java @Override protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // ... [代码部分省略以简化] try { // 正常的bean实例化、属性注入和初始化。 // 这里是真正进行bean创建的部分。 Object beanInstance = doCreateBean(beanName, mbdToUse, args); // 记录bean成功创建的日志 if (logger.isTraceEnabled()) { logger.trace("Finished creating instance of bean '" + beanName + "'"); } return beanInstance; } catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { // ... [代码部分省略以简化] } catch (Throwable ex) { // ... [代码部分省略以简化] } } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中,`initializeBean`方法是bean初始化,确保bean是完全配置和准备好的。 ```java protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // 声明一个对象,后续可能用于存放初始化后的bean或它的代理对象 Object exposedObject = bean; // ... [代码部分省略以简化] try { // ... [代码部分省略以简化] // bean初始化 exposedObject = initializeBean(beanName, exposedObject, mbd); } catch (Throwable ex) { // ... [代码部分省略以简化] } // 返回创建和初始化后的bean return exposedObject; } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean`方法中,如果条件满足(即 bean 不是合成的),那么它会调用 `applyBeanPostProcessorsBeforeInitialization` 方法。这个方法是 Spring 生命周期中的一个关键点,它会遍历所有已注册的 `BeanPostProcessor` 实现,并调用它们的 `postProcessBeforeInitialization` 方法。这允许我们和内部处理器在 bean 初始化之前对其进行修改或执行其他操作。 ```java protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { // ... [代码部分省略以简化] Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } // ... [代码部分省略以简化] return wrappedBean; } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization`方法中,遍历每一个 `BeanPostProcessor` 的 `postProcessBeforeInitialization` 方法都有机会对bean进行修改或增强 ```java @Override public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; for (BeanPostProcessor processor : getBeanPostProcessors()) { Object current = processor.postProcessBeforeInitialization(result, beanName); if (current == null) { return result; } result = current; } return result; } ``` 在`org.springframework.context.support.ApplicationContextAwareProcessor#postProcessBeforeInitialization`方法中,在这个方法的实现特别关注那些实现了 "aware" 接口的 beans,并为它们提供所需的运行环境信息。 ```java @Override @Nullable public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware || bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware || bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware || bean instanceof ApplicationStartupAware)) { return bean; } // ... [代码部分省略以简化] invokeAwareInterfaces(bean); return bean; } ``` 在`org.springframework.context.support.ApplicationContextAwareProcessor#invokeAwareInterfaces`方法中,用于处理实现了"Aware"接口的beans。这些接口使得beans能够被自动"感知"并获得对其运行环境或特定依赖的引用,而不需要显式地进行查找或注入。 ```java private void invokeAwareInterfaces(Object bean) { // ... [代码部分省略以简化] if (bean instanceof EmbeddedValueResolverAware) { ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver); } // ... [代码部分省略以简化] } ``` 最后执行到我们自定义的逻辑中,`MyEmbeddedValueResolverAware`类实现了`EmbeddedValueResolverAware`接口,当Spring容器初始化此类的Bean时,此方法将被调用,容器将传入一个`StringValueResolver`实例,然后通过`resolve()`方法,使用注入的`stringValueResolver`来解析包含占位符和SpEL表达式的字符串,并将解析后的字符串打印到控制台。 ```java public class MyEmbeddedValueResolverAware implements EmbeddedValueResolverAware { private StringValueResolver stringValueResolver; @Override public void setEmbeddedValueResolver(StringValueResolver resolver) { this.stringValueResolver = resolver; } public void resolve() { String resolvedValue = stringValueResolver.resolveStringValue("Hello, ${user.name:xcs}! Today is #{T(java.time.LocalDate).now().toString()}"); System.out.println(resolvedValue); } } ``` ### 八、注意事项 1. **正确的环境** + 确保我们在 Spring 的环境中使用它,因为 `StringValueResolver` 需要 Spring 上下文来正确解析嵌入的值。 2. **非延迟依赖注入** + `setEmbeddedValueResolver` 方法在 Bean 初始化时调用。如果我们太早地尝试使用 `StringValueResolver`(例如,在构造函数中),它可能还没有被注入。 3. **默认值** + 当使用 `${user.name:xcs}` 语法时,如果 `user.name` 没有在环境中定义,它将使用 `xcs`。这可以避免因缺少配置而导致的错误。 4. **明确解析的范围** + `EmbeddedValueResolverAware` 通常用于解析占位符和 SpEL 表达式。确保不将它与更复杂的 Bean 解析逻辑混淆。 5. **错误处理** + 当解析一个字符串值失败时,Spring 通常会抛出一个异常。确保在代码中适当地处理这些异常。 6. **与其他 Aware 接口的交互** + 如果我们的 Bean 实现了多个 `Aware` 接口,需要确保我们理解了每个接口的初始化时机和顺序,以及如何与其他 Aware 方法(如 `setBeanFactory` 或 `setApplicationContext`)交互。 ### 九、总结 #### 最佳实践总结 1. **启动类** + 在 `EmbeddedValueResolverAwareApplication` 中,我们初始化了 Spring 的 `AnnotationConfigApplicationContext` 并加载了 `MyConfiguration` 作为配置类。接着,我们从上下文中取得 `MyEmbeddedValueResolverAware` 的 Bean,并调用了其 `resolve` 方法。 2. **配置与Bean声明** + 在 `MyConfiguration` 配置类中,我们声明了 `MyEmbeddedValueResolverAware` 为一个 Bean,这确保了它会被 Spring 容器管理,并且会接收到 `StringValueResolver` 的实例注入。 3. **嵌入值解析** + `MyEmbeddedValueResolverAware` 类实现了 `EmbeddedValueResolverAware` 接口,这意味着在该 Bean 被初始化时,Spring 会自动提供一个 `StringValueResolver` 实例。这个解析器之后被用于解析字符串 "Hello, ${user.name:xcs}! Today is #{T(java.time.LocalDate).now().toString()}"。 #### 源码分析总结 1. **应用启动** + 在`EmbeddedValueResolverAwareApplication`类中,使用`AnnotationConfigApplicationContext`来启动Spring应用并加载`MyConfiguration`配置类。 2. **容器初始化** + 在构造函数`AnnotationConfigApplicationContext`中,`refresh()`方法被调用来初始化Spring容器。 3. **实例化Beans** + 在`AbstractApplicationContext`的`refresh()`方法中,`finishBeanFactoryInitialization`方法被调用,确保所有单例Bean被预先实例化。 4. **Bean预实例化** + `DefaultListableBeanFactory`的`preInstantiateSingletons`方法确保所有非懒加载的单例Beans被实例化。核心操作是调用`getBean(beanName)`。 5. **获取Bean实例** + `AbstractBeanFactory`的`getBean`方法进一步调用`doGetBean`来真正实例化Bean,处理异常和依赖,并返回Bean实例。 6. **Bean单例获取** + `DefaultSingletonBeanRegistry`的`getSingleton`方法确保Bean以单例形式存在,从缓存获取或使用提供的`ObjectFactory`创建新实例。 7. **创建Bean实例** + `AbstractAutowireCapableBeanFactory`的`createBean`方法调用`doCreateBean`进行Bean的实际实例化,并进行初始化,确保Bean完全配置并准备就绪。 8. **Bean初始化** + `AbstractAutowireCapableBeanFactory`的`initializeBean`方法确保Bean被正确初始化,其中调用`applyBeanPostProcessorsBeforeInitialization`方法是Spring生命周期中的关键点,允许BeanPostProcessors在Bean初始化之前进行操作。 9. **处理Aware接口** + 在Bean初始化过程中,`ApplicationContextAwareProcessor`确保实现了`Aware`接口的Beans被正确处理,这些Beans会自动"感知"并获得其运行环境或特定依赖的引用。 10. **值解析** + 最后,我们的`MyEmbeddedValueResolverAware` Bean接收到了一个`StringValueResolver`实例。此时,当`resolve`方法被调用,它会使用这个解析器来解析嵌入的字符串值,并打印到控制台。 ================================================ FILE: spring-aware/spring-aware-embeddedValueResolverAware/pom.xml ================================================ spring-aware com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-aware-embeddedValueResolverAware ================================================ FILE: spring-aware/spring-aware-embeddedValueResolverAware/src/main/java/com/xcs/spring/EmbeddedValueResolverAwareApplication.java ================================================ package com.xcs.spring; import com.xcs.spring.config.MyConfiguration; import com.xcs.spring.config.MyEmbeddedValueResolverAware; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年09月16日 16时09分 **/ public class EmbeddedValueResolverAwareApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MyEmbeddedValueResolverAware resolverAware = context.getBean(MyEmbeddedValueResolverAware.class); resolverAware.resolve(); } } ================================================ FILE: spring-aware/spring-aware-embeddedValueResolverAware/src/main/java/com/xcs/spring/config/MyConfiguration.java ================================================ package com.xcs.spring.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @author xcs * @date 2023年09月19日 16时35分 **/ @Configuration public class MyConfiguration { @Bean public MyEmbeddedValueResolverAware myEmbeddedValueResolverAware(){ return new MyEmbeddedValueResolverAware(); } } ================================================ FILE: spring-aware/spring-aware-embeddedValueResolverAware/src/main/java/com/xcs/spring/config/MyEmbeddedValueResolverAware.java ================================================ package com.xcs.spring.config; import org.springframework.context.EmbeddedValueResolverAware; import org.springframework.util.StringValueResolver; public class MyEmbeddedValueResolverAware implements EmbeddedValueResolverAware { private StringValueResolver stringValueResolver; @Override public void setEmbeddedValueResolver(StringValueResolver resolver) { this.stringValueResolver = resolver; } public void resolve() { String resolvedValue = stringValueResolver.resolveStringValue("Hello, ${user.name:xcs}! Today is #{T(java.time.LocalDate).now().toString()}"); System.out.println(resolvedValue); } } ================================================ FILE: spring-aware/spring-aware-environmentAware/README.md ================================================ ## EnvironmentAware - [EnvironmentAware](#environmentaware) - [一、基本信息](#一基本信息) - [二、接口描述](#二接口描述) - [三、接口源码](#三接口源码) - [四、主要功能](#四主要功能) - [五、最佳实践](#五最佳实践) - [六、时序图](#六时序图) - [七、源码分析](#七源码分析) - [八、注意事项](#八注意事项) - [九、总结](#九总结) - [最佳实践总结](#最佳实践总结) - [源码分析总结](#源码分析总结) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [我的CSDN](https://blog.csdn.net/duzhuang2399/article/details/133915522) 📚 **文章目录** - [所有文章](https://github.com/xuchengsheng/spring-reading) 🔗 **源码地址** - [EnvironmentAware源码](https://github.com/xuchengsheng/spring-reading/tree/master/spring-aware/spring-aware-environmentAware) ### 二、接口描述 `EnvironmentAware` 接口,允许Beans访问`Environment`对象。这是一个回调接口,当实现该接口的Bean被Spring容器管理时,Spring容器会为该Bean设置`Environment`对象。 ### 三、接口源码 `EnvironmentAware` 是 Spring 框架自 3.1 开始引入的一个核心接口。实现`EnvironmentAware`接口的对象会在Spring容器中被自动注入一个`Environment`实例。 ```java /** * 任何希望被通知其运行的Environment的bean应该实现的接口。 * * @author Chris Beams * @since 3.1 * @see org.springframework.core.env.EnvironmentCapable */ public interface EnvironmentAware extends Aware { /** * 设置此组件运行所在的Environment。 */ void setEnvironment(Environment environment); } ``` ### 四、主要功能 1. **访问环境属性** + 通过实现 `EnvironmentAware`,beans 可以直接访问应用上下文的`Environment`对象。这意味着它们可以读取环境属性,这些属性可能来自多个来源,例如系统属性、JVM参数、操作系统环境变量、属性文件等。 2. **识别运行时环境** + beans可以通过`Environment`对象来检查和确定当前激活的Spring profiles。这使得bean可以根据不同的运行环境(例如开发、测试、生产等)进行特定的操作或配置。 3. **自动回调** + 当Spring容器识别到一个bean实现了`EnvironmentAware`接口时,容器会自动调用 `setEnvironment` 方法并传递当前的 `Environment` 对象。这意味着我们不需要特意去手动设置或获取它。 4. **框架级别的集成** + 此接口提供了一个标准机制,允许框架级别的代码(如其他Spring组件和第三方库)访问和集成`Environment`对象,而不必依赖特定的注入策略或其他机制。 ### 五、最佳实践 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyEnvironmentAware`类型的bean,最后调用`getAppProperty`方法并打印。 ```java public class EnvironmentAwareApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MyEnvironmentAware environmentAware = context.getBean(MyEnvironmentAware.class); System.out.println("AppProperty = " + environmentAware.getAppProperty()); } } ``` 这里使用`@Bean`注解,定义了以个Bean,是为了确保 `MyEnvironmentAware` 被 Spring 容器执行,另外使用`@PropertySource`注解从类路径下的`application.properties`文件中加载属性。这意味着我们可以在这个文件中定义属性,然后在应用中使用`Environment`对象来访问它们。 ```java @Configuration @PropertySource("classpath:application.properties") public class MyConfiguration { @Bean public MyEnvironmentAware myEnvironmentAware(){ return new MyEnvironmentAware(); } } ``` `MyEnvironmentAware`类实现了`EnvironmentAware`接口,并重写了`setEnvironment`方法,以便在Spring容器初始化它时获取`Environment`对象。之后,我们可以使用`getPropertyValue`方法来查询`application.properties`中的任何属性。 ```java public class MyEnvironmentAware implements EnvironmentAware { private String appProperty; @Override public void setEnvironment(Environment environment) { this.appProperty = environment.getProperty("app.xcs.property"); } public String getAppProperty() { return appProperty; } } ``` 运行结果发现,这个输出证明了`EnvironmentAware`接口及其与`application.properties`文件的整合成功工作,我们已经成功地使用Spring环境获取了配置属性。 ```java AppProperty = Hello from EnvironmentAware! ``` ### 六、时序图 ~~~mermaid sequenceDiagram Title: EnvironmentAware时序图 participant EnvironmentAwareApplication participant AnnotationConfigApplicationContext participant AbstractApplicationContext participant DefaultListableBeanFactory participant AbstractBeanFactory participant DefaultSingletonBeanRegistry participant AbstractAutowireCapableBeanFactory participant ApplicationContextAwareProcessor participant MyEnvironmentAware EnvironmentAwareApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)
创建上下文 AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()
刷新上下文 AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory)
初始化Bean工厂 AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons()
实例化单例 DefaultListableBeanFactory->>AbstractBeanFactory:getBean(name)
获取Bean AbstractBeanFactory->>AbstractBeanFactory:doGetBean(name,requiredType,args,typeCheckOnly)
执行获取Bean AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton(beanName,singletonFactory)
获取单例Bean DefaultSingletonBeanRegistry-->>AbstractBeanFactory:getObject()
获取Bean实例 AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean(beanName,mbd,args)
创建Bean AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:doCreateBean(beanName,mbd,args)
执行Bean创建 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:initializeBean(beanName,bean,mbd)
负责bean的初始化 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:applyBeanPostProcessorsBeforeInitialization(existingBean, beanName)
调用前置处理器 AbstractAutowireCapableBeanFactory->>ApplicationContextAwareProcessor:postProcessBeforeInitialization(bean,beanName)
触发Aware处理 ApplicationContextAwareProcessor->>ApplicationContextAwareProcessor:invokeAwareInterfaces(bean)
执行Aware回调 ApplicationContextAwareProcessor->>MyEnvironmentAware:setEnvironment(environment)
设置运行环境 AbstractAutowireCapableBeanFactory-->>AbstractBeanFactory:返回Bean对象 AbstractBeanFactory-->>DefaultListableBeanFactory:返回Bean对象 AnnotationConfigApplicationContext-->>EnvironmentAwareApplication:初始化完成 ~~~ ### 七、源码分析 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyEnvironmentAware`类型的bean,最后调用`getAppProperty`方法并打印。 ```java public class EnvironmentAwareApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MyEnvironmentAware environmentAware = context.getBean(MyEnvironmentAware.class); System.out.println("AppProperty = " + environmentAware.getAppProperty()); } } ``` 在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法 ```java public AnnotationConfigApplicationContext(Class... componentClasses) { this(); register(componentClasses); refresh(); ``` 在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 ```java @Override public void refresh() throws BeansException, IllegalStateException { // ... [代码部分省略以简化] // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization`方法中,会继续调用`DefaultListableBeanFactory`类中的`preInstantiateSingletons`方法来完成所有剩余非懒加载的单列Bean对象。 ```java /** * 完成此工厂的bean初始化,实例化所有剩余的非延迟初始化单例bean。 * * @param beanFactory 要初始化的bean工厂 */ protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { // ... [代码部分省略以简化] // 完成所有剩余非懒加载的单列Bean对象。 beanFactory.preInstantiateSingletons(); } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons`方法中,主要的核心目的是预先实例化所有非懒加载的单例bean。在Spring的上下文初始化完成后,该方法会被触发,以确保所有单例bean都被正确地创建并初始化。其中`getBean(beanName)`是此方法的核心操作。对于容器中定义的每一个单例bean,它都会调用`getBean`方法,这将触发bean的实例化、初始化及其依赖的注入。如果bean之前没有被创建过,那么这个调用会导致其被实例化和初始化。 ```java public void preInstantiateSingletons() throws BeansException { // ... [代码部分省略以简化] // 循环遍历所有bean的名称 for (String beanName : beanNames) { getBean(beanName); } // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#getBean()`方法中,又调用了`doGetBean`方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,`doGetBean`负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。 ```java @Override public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中,首先检查所请求的bean是否是一个单例并且已经创建。如果尚未创建,它将创建一个新的实例。在这个过程中,它处理可能的异常情况,如循环引用,并确保返回的bean是正确的类型。这是Spring容器bean生命周期管理的核心部分。 ```java protected T doGetBean( String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException { // ... [代码部分省略以简化] // 开始创建bean实例 if (mbd.isSingleton()) { // 如果bean是单例的,我们会尝试从单例缓存中获取它 // 如果不存在,则使用lambda创建一个新的实例 sharedInstance = getSingleton(beanName, () -> { try { // 尝试创建bean实例 return createBean(beanName, mbd, args); } catch (BeansException ex) { // ... [代码部分省略以简化] } }); // 对于某些bean(例如FactoryBeans),可能需要进一步处理以获取真正的bean实例 beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } // ... [代码部分省略以简化] // 确保返回的bean实例与请求的类型匹配 return adaptBeanInstance(name, beanInstance, requiredType); } ``` 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()`方法中,主要负责从单例缓存中获取一个已存在的bean实例,或者使用提供的`ObjectFactory`创建一个新的实例。这是确保bean在Spring容器中作为单例存在的关键部分。 ```java public Object getSingleton(String beanName, ObjectFactory singletonFactory) { // 断言bean名称不能为空 Assert.notNull(beanName, "Bean name must not be null"); // 同步访问单例对象缓存,确保线程安全 synchronized (this.singletonObjects) { // 从缓存中获取单例对象 Object singletonObject = this.singletonObjects.get(beanName); // 如果缓存中没有找到 if (singletonObject == null) { // ... [代码部分省略以简化] try { // 使用工厂创建新的单例实例 singletonObject = singletonFactory.getObject(); newSingleton = true; } catch (IllegalStateException ex) { // ... [代码部分省略以简化] } catch (BeanCreationException ex) { // ... [代码部分省略以简化] } finally { // ... [代码部分省略以简化] } // ... [代码部分省略以简化] } // 返回单例对象 return singletonObject; } } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()`方法中,主要的逻辑是调用 `doCreateBean`,这是真正进行 bean 实例化、属性填充和初始化的地方。这个方法会返回新创建的 bean 实例。 ```java @Override protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // ... [代码部分省略以简化] try { // 正常的bean实例化、属性注入和初始化。 // 这里是真正进行bean创建的部分。 Object beanInstance = doCreateBean(beanName, mbdToUse, args); // 记录bean成功创建的日志 if (logger.isTraceEnabled()) { logger.trace("Finished creating instance of bean '" + beanName + "'"); } return beanInstance; } catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { // ... [代码部分省略以简化] } catch (Throwable ex) { // ... [代码部分省略以简化] } } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中,`initializeBean`方法是bean初始化,确保bean是完全配置和准备好的。 ```java protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // 声明一个对象,后续可能用于存放初始化后的bean或它的代理对象 Object exposedObject = bean; // ... [代码部分省略以简化] try { // ... [代码部分省略以简化] // bean初始化 exposedObject = initializeBean(beanName, exposedObject, mbd); } catch (Throwable ex) { // ... [代码部分省略以简化] } // 返回创建和初始化后的bean return exposedObject; } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean`方法中,如果条件满足(即 bean 不是合成的),那么它会调用 `applyBeanPostProcessorsBeforeInitialization` 方法。这个方法是 Spring 生命周期中的一个关键点,它会遍历所有已注册的 `BeanPostProcessor` 实现,并调用它们的 `postProcessBeforeInitialization` 方法。这允许我们和内部处理器在 bean 初始化之前对其进行修改或执行其他操作。 ```java protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { // ... [代码部分省略以简化] Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } // ... [代码部分省略以简化] return wrappedBean; } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization`方法中,遍历每一个 `BeanPostProcessor` 的 `postProcessBeforeInitialization` 方法都有机会对bean进行修改或增强 ```java @Override public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; for (BeanPostProcessor processor : getBeanPostProcessors()) { Object current = processor.postProcessBeforeInitialization(result, beanName); if (current == null) { return result; } result = current; } return result; } ``` 在`org.springframework.context.support.ApplicationContextAwareProcessor#postProcessBeforeInitialization`方法中,在这个方法的实现特别关注那些实现了 "aware" 接口的 beans,并为它们提供所需的运行环境信息。 ```java @Override @Nullable public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware || bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware || bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware || bean instanceof ApplicationStartupAware)) { return bean; } // ... [代码部分省略以简化] invokeAwareInterfaces(bean); return bean; } ``` 在`org.springframework.context.support.ApplicationContextAwareProcessor#invokeAwareInterfaces`方法中,用于处理实现了"Aware"接口的beans。这些接口使得beans能够被自动"感知"并获得对其运行环境或特定依赖的引用,而不需要显式地进行查找或注入。 ```java private void invokeAwareInterfaces(Object bean) { if (bean instanceof EnvironmentAware) { ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment()); } // ... [代码部分省略以简化] } ``` 最后执行到我们自定义的逻辑中,`MyEnvironmentAware`类实现了`EnvironmentAware`接口,并重写了`setEnvironment`方法,以便在Spring容器初始化它时获取`Environment`对象。之后,我们可以使用`getPropertyValue`方法来查询`application.properties`中的任何属性。 ```java public class MyEnvironmentAware implements EnvironmentAware { private String appProperty; @Override public void setEnvironment(Environment environment) { this.appProperty = environment.getProperty("app.xcs.property"); } public String getAppProperty() { return appProperty; } } ``` ### 八、注意事项 1. **不要过度使用** + 虽然`EnvironmentAware`为Bean提供了一个直接访问`Environment`的方法,但这并不意味着所有的Bean都应该使用它。在可能的情况下,首先考虑使用Spring的属性注入功能,例如`@Value`。 2. **避免使用硬编码的属性键** + 当从`Environment`对象中获取属性时,尽量避免在代码中硬编码属性键。最好是将这些键作为常量或在外部配置中定义。 3. **处理不存在的属性** + 当使用`Environment`获取属性时,如果该属性不存在,`Environment`可能会返回`null`。确保在代码中正确处理这种情况,或使用`Environment`提供的默认值方法。 4. **记住激活的配置文件** + `Environment`允许我们查询当前激活的配置文件(profiles)。确保我们知道哪些profiles是激活的,尤其是在使用特定于profile的属性时。 5. **了解Environment的层次结构** + `Environment`对象可能会从多个来源获取属性(例如系统属性、环境变量、配置文件等)。了解这些来源的优先级和加载顺序,以便正确地理解在存在冲突时哪个属性值会被使用。 ### 九、总结 #### 最佳实践总结 1. **启动过程** + 通过`EnvironmentAwareApplication`作为主入口,我们使用了`AnnotationConfigApplicationContext`来启动Spring上下文,并加载了`MyConfiguration`作为配置类。 2. **加载属性** + 在`MyConfiguration`类中,我们使用了`@PropertySource`注解指定了从类路径下的`application.properties`文件加载属性到Spring的环境中。 3. **注册Bean** + 在配置类`MyConfiguration`中,我们定义了一个bean `MyEnvironmentAware`。这保证了当Spring容器启动时,`MyEnvironmentAware`对象会被创建并由Spring管理。 4. **访问环境属性** + `MyEnvironmentAware`类实现了`EnvironmentAware`接口,这使得当Spring容器初始化该bean时,它会自动调用`setEnvironment`方法,注入当前的`Environment`对象。我们使用这个方法来读取`app.xcs.property`属性,并将其值存储在`appProperty`私有变量中。 5. **显示属性** + 最后,在`EnvironmentAwareApplication`主程序中,我们从Spring上下文中获取了`MyEnvironmentAware` bean,并调用了`getAppProperty`方法来获取属性值,然后将其打印到控制台。 6. **输出** + 结果显示为“AppProperty = Hello from EnvironmentAware!”,这证明了`EnvironmentAware`接口和`application.properties`文件成功地结合起来,并且我们已经成功地使用Spring环境获取了配置属性。 #### 源码分析总结 1. **应用启动** + 通过`EnvironmentAwareApplication`作为入口,使用`AnnotationConfigApplicationContext`来初始化Spring的上下文,并加载`MyConfiguration`作为配置类。 2. **属性加载** + 在`MyConfiguration`类中,利用`@PropertySource`注解,指定从`application.properties`文件加载属性到Spring环境中。 3. **Bean注册与初始化** + 在上下文的`refresh()`方法中,调用`finishBeanFactoryInitialization()`确保所有非懒加载的单例bean都被实例化。这个过程在`preInstantiateSingletons()`中通过循环调用`getBean()`完成,该方法将触发bean的创建、初始化及其依赖的注入。 4. **Bean后处理与"感知"** + 在bean的初始化过程中,`ApplicationContextAwareProcessor`负责检查并调用那些实现了Aware接口的bean的特定方法。对于实现了`EnvironmentAware`接口的beans,它会调用`setEnvironment()`方法并传入当前的`Environment`对象。 5. **自定义Bean的处理** + `MyEnvironmentAware`在其`setEnvironment()`方法中,从传入的`Environment`对象中获取了`app.xcs.property`属性,并存储到了它的私有变量`appProperty`中。 6. **应用结果输出** + 在`EnvironmentAwareApplication`的主方法中,从Spring上下文获取了`MyEnvironmentAware` bean并调用其`getAppProperty()`方法,然后将获得的属性值输出到控制台。 ================================================ FILE: spring-aware/spring-aware-environmentAware/pom.xml ================================================ spring-aware com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-aware-environmentAware ================================================ FILE: spring-aware/spring-aware-environmentAware/src/main/java/com/xcs/spring/EnvironmentAwareApplication.java ================================================ package com.xcs.spring; import com.xcs.spring.config.MyEnvironmentAware; import com.xcs.spring.config.MyConfiguration; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年09月16日 16时09分 **/ public class EnvironmentAwareApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MyEnvironmentAware environmentAware = context.getBean(MyEnvironmentAware.class); System.out.println("AppProperty = " + environmentAware.getAppProperty()); } } ================================================ FILE: spring-aware/spring-aware-environmentAware/src/main/java/com/xcs/spring/config/MyConfiguration.java ================================================ package com.xcs.spring.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; /** * @author xcs * @date 2023年09月19日 16时35分 **/ @Configuration @PropertySource("classpath:application.properties") public class MyConfiguration { @Bean public MyEnvironmentAware myEnvironmentAware(){ return new MyEnvironmentAware(); } } ================================================ FILE: spring-aware/spring-aware-environmentAware/src/main/java/com/xcs/spring/config/MyEnvironmentAware.java ================================================ package com.xcs.spring.config; import org.springframework.context.EnvironmentAware; import org.springframework.core.env.Environment; public class MyEnvironmentAware implements EnvironmentAware { private String appProperty; @Override public void setEnvironment(Environment environment) { this.appProperty = environment.getProperty("app.xcs.property"); } public String getAppProperty() { return appProperty; } } ================================================ FILE: spring-aware/spring-aware-environmentAware/src/main/resources/application.properties ================================================ app.xcs.property = Hello from EnvironmentAware! ================================================ FILE: spring-aware/spring-aware-importAware/README.md ================================================ ## ImportAware - [ImportAware](#importaware) - [一、基本信息](#一基本信息) - [二、接口描述](#二接口描述) - [三、接口源码](#三接口源码) - [四、主要功能](#四主要功能) - [五、最佳实践](#五最佳实践) - [六、时序图](#六时序图) - [七、源码分析](#七源码分析) - [八、注意事项](#八注意事项) - [九、总结](#九总结) - [最佳实践总结](#最佳实践总结) - [源码分析总结](#源码分析总结) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [我的CSDN](https://blog.csdn.net/duzhuang2399/article/details/133915616) 📚 **文章目录** - [所有文章](https://github.com/xuchengsheng/spring-reading) 🔗 **源码地址** - [ImportAware源码]() ### 二、接口描述 `ImportAware` 接口,提供被导入类的访问功能。当一个类实现了 `ImportAware` 接口,并且被通过 @Import 注解导入到其他配置类中,该类可以获得对导入它的 `AnnotationMetadata` 的访问权。 ### 三、接口源码 `ApplicationStartupAware` 是 Spring 框架自 3.1 开始引入的一个核心接口。实现`ImportAware`接口的对象会在Spring容器中被自动注入一个`AnnotationMetadata`实例。 ```java /** * 任何希望被注入其导入它的Configuration类的AnnotationMetadata的Configuration类都应实现此接口。 * 与使用Import作为元注解的注解结合使用时特别有用。 * * @author Chris Beams * @since 3.1 */ public interface ImportAware extends Aware { /** * 设置导入的@Configuration类的注解元数据。 */ void setImportMetadata(AnnotationMetadata importMetadata); } ``` ### 四、主要功能 1. **访问导入类的注解元数据** + 当一个类实现了 `ImportAware` 接口,并且它是通过 `@Import` 或其他特定方式被导入的,Spring 容器会自动调用它的 `setImportMetadata` 方法,并传入与导入该类的注解相关的 `AnnotationMetadata`。 2. **条件性的行为** + 通过访问导入类的注解元数据,可以实现基于特定条件的行为。例如,根据导入类上的注解属性,决定是否注册某个 bean,或者为 bean 设置特定的属性值。 3. **框架和库的开发** + `ImportAware` 在 Spring 框架内部和某些第三方库中被用于执行特定的初始化和配置任务。例如,某些特性的自动配置可能会根据导入它们的配置类上的注解属性进行调整。 4. **增强诊断和调试信息** + 可以基于导入类的元数据为我们提供更多的上下文信息,这在诊断复杂的配置问题时可能会很有用。 ### 五、最佳实践 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`String`类型的bean并打印。 ```java public class ImportAwareApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); String customBean = context.getBean(String.class); System.out.println(customBean); } } ``` `MyConfiguration` 是一个 Spring 配置类,然后通过这个类启用了通过 `@EnableXcs` 注解提供`ImportAware`功能。 ```java @Configuration @EnableXcs public class MyConfiguration { } ``` `EnableXcs`是一个注解类,`@Import(MyImportAware.class)`会对 Spring 上下文进行某种配置或修改 ```java @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import(MyImportAware.class) public @interface EnableXcs { } ``` 由于 `MyImportAware` 实现了 `ImportAware`,它会检查导入它的配置类上是否存在 `@EnableXcs` 注解。如果存在,则继续处理并注册 `customBean` 这个 String 类型的 bean 到 Spring 上下文中。如果不存在 `@EnableXcs` 注解,则抛出异常。 ```java public class MyImportAware implements ImportAware { private AnnotationAttributes enableXcs; @Override public void setImportMetadata(AnnotationMetadata importMetadata) { this.enableXcs = AnnotationAttributes.fromMap( importMetadata.getAnnotationAttributes(EnableXcs.class.getName(), false)); if (this.enableXcs == null) { throw new IllegalArgumentException( "@EnableXcs is not present on importing class " + importMetadata.getClassName()); } } @Bean public String customBean() { return "This is a custom bean!"; } } ``` 运行结果发现,当我们在 Spring 上下文中使用 `@EnableXcs` 注解并运行程序时,`MyImportAware` 类会被导入并处理,最后在 Spring 容器中注册一个 String 类型的 bean,其值为 "This is a custom bean!"。 ```java This is a custom bean! ``` 当我们不通过`@EnableXcs` 注解方式去导入`MyImportAware`类,而是直接在MyConfiguration类中导入`@Import(MyImportAware.class)`来看看另外一种情况。 ```java @Configuration @Import(MyImportAware.class) public class MyConfiguration { } ``` 运行结果发现,当我们直接使用`@Import(MyImportAware.class)`导入`MyImportAware`类而不使用`@EnableXcs`注解时,由于`MyConfiguration`上没有`@EnableXcs`注解,所以`enableXcs`的值为null,由于此时`enableXcs`是null,`MyImportAware`抛出了一个`IllegalArgumentException`异常。 ```java Caused by: java.lang.IllegalArgumentException: @EnableXcs is not present on importing class com.xcs.spring.config.MyConfiguration at com.xcs.spring.config.MyImportAware.setImportMetadata(MyImportAware.java:19) at org.springframework.context.annotation.ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor.postProcessBeforeInitialization(ConfigurationClassPostProcessor.java:484) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:440) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1796) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:620) ... 10 more ``` ### 六、时序图 ~~~mermaid sequenceDiagram Title: ImportAware时序图 participant ImportAwareApplication participant AnnotationConfigApplicationContext participant AbstractApplicationContext participant DefaultListableBeanFactory participant AbstractBeanFactory participant DefaultSingletonBeanRegistry participant AbstractAutowireCapableBeanFactory participant ImportAwareBeanPostProcessor participant MyImportAware ImportAwareApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses) AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh() AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory) AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons() DefaultListableBeanFactory->>AbstractBeanFactory:getBean(name) AbstractBeanFactory->>AbstractBeanFactory:doGetBean(name,requiredType,args,typeCheckOnly) AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton(beanName,singletonFactory) DefaultSingletonBeanRegistry-->>AbstractBeanFactory:getObject() AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean(beanName,mbd,args) AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:doCreateBean(beanName,mbd,args) AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:initializeBean(beanName,bean,mbd) AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:applyBeanPostProcessorsBeforeInitialization(existingBean,beanName) AbstractAutowireCapableBeanFactory->>ImportAwareBeanPostProcessor: postProcessBeforeInitialization(bean,beanName) ImportAwareBeanPostProcessor->>MyImportAware:setImportMetadata(importMetadata)设置importMetadata ImportAwareBeanPostProcessor-->>AbstractAutowireCapableBeanFactory: 返回Bean对象 AbstractAutowireCapableBeanFactory-->>AbstractBeanFactory:返回Bean对象 AbstractBeanFactory-->>DefaultListableBeanFactory:返回Bean对象 AnnotationConfigApplicationContext->>ImportAwareApplication: 初始化完成 ~~~ ### 七、源码分析 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`String`类型的bean并打印。 ```java public class ImportAwareApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); String customBean = context.getBean(String.class); System.out.println(customBean); } } ``` 在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法。 ```java public AnnotationConfigApplicationContext(Class... componentClasses) { this(); register(componentClasses); refresh(); } ``` 在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中,我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 ```java @Override public void refresh() throws BeansException, IllegalStateException { // ... [代码部分省略以简化] // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization`方法中,会继续调用`DefaultListableBeanFactory`类中的`preInstantiateSingletons`方法来完成所有剩余非懒加载的单列Bean对象。 ```java /** * 完成此工厂的bean初始化,实例化所有剩余的非延迟初始化单例bean。 * * @param beanFactory 要初始化的bean工厂 */ protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { // ... [代码部分省略以简化] // 完成所有剩余非懒加载的单列Bean对象。 beanFactory.preInstantiateSingletons(); } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons`方法中,主要的核心目的是预先实例化所有非懒加载的单例bean。在Spring的上下文初始化完成后,该方法会被触发,以确保所有单例bean都被正确地创建并初始化。其中`getBean(beanName)`是此方法的核心操作。对于容器中定义的每一个单例bean,它都会调用`getBean`方法,这将触发bean的实例化、初始化及其依赖的注入。如果bean之前没有被创建过,那么这个调用会导致其被实例化和初始化。 ```java public void preInstantiateSingletons() throws BeansException { // ... [代码部分省略以简化] // 循环遍历所有bean的名称 for (String beanName : beanNames) { getBean(beanName); } // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#getBean()`方法中,又调用了`doGetBean`方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,`doGetBean`负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。 ```java @Override public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中,首先检查所请求的bean是否是一个单例并且已经创建。如果尚未创建,它将创建一个新的实例。在这个过程中,它处理可能的异常情况,如循环引用,并确保返回的bean是正确的类型。这是Spring容器bean生命周期管理的核心部分。 ```java protected T doGetBean( String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException { // ... [代码部分省略以简化] // 开始创建bean实例 if (mbd.isSingleton()) { // 如果bean是单例的,我们会尝试从单例缓存中获取它 // 如果不存在,则使用lambda创建一个新的实例 sharedInstance = getSingleton(beanName, () -> { try { // 尝试创建bean实例 return createBean(beanName, mbd, args); } catch (BeansException ex) { // ... [代码部分省略以简化] } }); // 对于某些bean(例如FactoryBeans),可能需要进一步处理以获取真正的bean实例 beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } // ... [代码部分省略以简化] // 确保返回的bean实例与请求的类型匹配 return adaptBeanInstance(name, beanInstance, requiredType); } ``` 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()`方法中,主要负责从单例缓存中获取一个已存在的bean实例,或者使用提供的`ObjectFactory`创建一个新的实例。这是确保bean在Spring容器中作为单例存在的关键部分。 ```java public Object getSingleton(String beanName, ObjectFactory singletonFactory) { // 断言bean名称不能为空 Assert.notNull(beanName, "Bean name must not be null"); // 同步访问单例对象缓存,确保线程安全 synchronized (this.singletonObjects) { // 从缓存中获取单例对象 Object singletonObject = this.singletonObjects.get(beanName); // 如果缓存中没有找到 if (singletonObject == null) { // ... [代码部分省略以简化] try { // 使用工厂创建新的单例实例 singletonObject = singletonFactory.getObject(); newSingleton = true; } catch (IllegalStateException ex) { // ... [代码部分省略以简化] } catch (BeanCreationException ex) { // ... [代码部分省略以简化] } finally { // ... [代码部分省略以简化] } // ... [代码部分省略以简化] } // 返回单例对象 return singletonObject; } } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()`方法中,主要的逻辑是调用 `doCreateBean`,这是真正进行 bean 实例化、属性填充和初始化的地方。这个方法会返回新创建的 bean 实例。 ```java @Override protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // ... [代码部分省略以简化] try { // 正常的bean实例化、属性注入和初始化。 // 这里是真正进行bean创建的部分。 Object beanInstance = doCreateBean(beanName, mbdToUse, args); // 记录bean成功创建的日志 if (logger.isTraceEnabled()) { logger.trace("Finished creating instance of bean '" + beanName + "'"); } return beanInstance; } catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { // ... [代码部分省略以简化] } catch (Throwable ex) { // ... [代码部分省略以简化] } } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中,主要负责bean初始化。 ```java protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // 声明一个对象,后续可能用于存放初始化后的bean或它的代理对象 Object exposedObject = bean; // ... [代码部分省略以简化] try { // ... [代码部分省略以简化] // bean初始化 exposedObject = initializeBean(beanName, exposedObject, mbd); } catch (Throwable ex) { // ... [代码部分省略以简化] } // 返回创建和初始化后的bean return exposedObject; } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean()`方法中,核心逻辑是对`BeanPostProcessors`接口中的`postProcessBeforeInitialization`进行回调。 ```java protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { // ... [代码部分省略以简化] Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } // ... [代码部分省略以简化] return wrappedBean; } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization`方法中,遍历每一个 `BeanPostProcessor` 的 `postProcessBeforeInitialization` 方法都有机会对bean进行修改或增强。 ```java @Override public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; for (BeanPostProcessor processor : getBeanPostProcessors()) { Object current = processor.postProcessBeforeInitialization(result, beanName); if (current == null) { return result; } result = current; } return result; } ``` 在`org.springframework.context.annotation.ConfigurationClassPostProcessor.ImportAwareBeanPostProcessor#postProcessBeforeInitialization`方法中,主要作用是为实现了 `ImportAware` 接口的 beans 设置导入它们的类的 `AnnotationMetadata`。这样,任何实现了 `ImportAware` 接口的 bean 都可以知道它是由哪个类导入的,以及这个导入类上的所有注解信息。 ```java @Override public Object postProcessBeforeInitialization(Object bean, String beanName) { if (bean instanceof ImportAware) { ImportRegistry ir = this.beanFactory.getBean(IMPORT_REGISTRY_BEAN_NAME, ImportRegistry.class); AnnotationMetadata importingClass = ir.getImportingClassFor(ClassUtils.getUserClass(bean).getName()); if (importingClass != null) { ((ImportAware) bean).setImportMetadata(importingClass); } } return bean; } ``` 最后执行到我们自定义的逻辑中,由于 `MyImportAware` 实现了 `ImportAware`,它会检查导入它的配置类上是否存在 `@EnableXcs` 注解。如果存在,则继续处理并注册 `customBean` 这个 String 类型的 bean 到 Spring 上下文中。如果不存在 `@EnableXcs` 注解,则抛出异常。 ```java public class MyImportAware implements ImportAware { private AnnotationAttributes enableXcs; @Override public void setImportMetadata(AnnotationMetadata importMetadata) { this.enableXcs = AnnotationAttributes.fromMap( importMetadata.getAnnotationAttributes(EnableXcs.class.getName(), false)); if (this.enableXcs == null) { throw new IllegalArgumentException( "@EnableXcs is not present on importing class " + importMetadata.getClassName()); } } @Bean public String customBean() { return "This is a custom bean!"; } } ``` ### 八、注意事项 1. **明确需求** + 在决定实现 `ImportAware` 之前,请确保我们确实需要知道是哪个类导入了我们的组件,并且需要访问其注解元数据。避免不必要的复杂性。 2. **正确的上下文** + `ImportAware` 只对通过 `@Import` 导入的类有意义。对于其他方式注册的 beans(例如,通过 component scanning 或 XML 配置),`setImportMetadata` 方法可能不会被调用。 3. **小心处理元数据** + 当访问 `AnnotationMetadata` 时,确保处理不存在的注解或属性的情况,以避免空指针异常。 4. **注意与其他 `BeanPostProcessor` 的交互** + `ImportAware` 的功能部分是通过 `BeanPostProcessor` 机制实现的。如果我们在应用中使用其他 `BeanPostProcessor`,请确保我们了解它们之间的交互和执行顺序。 5. **不要过度使用** + 虽然 `ImportAware` 可以带来一些灵活性,但不应在不需要的地方使用它。过度使用可能会导致配置变得复杂且难以追踪。 ### 九、总结 #### 最佳实践总结 1. **初始化与运行** + 使用 `AnnotationConfigApplicationContext` 初始化了一个 Spring 上下文,加载了 `MyConfiguration` 配置类,并从上下文中获取了一个类型为 `String` 的 bean。 2. **`@EnableXcs` 注解的作用** + `@EnableXcs` 是一个自定义注解,其主要作用是通过 `@Import` 注解导入 `MyImportAware` 类,从而启动 `ImportAware` 功能。 3. **`MyImportAware` 类与 `ImportAware`** + `MyImportAware` 实现了 `ImportAware` 接口,允许它获取关于导入它的类的注解信息。在 `setImportMetadata` 方法中,`MyImportAware` 会检查导入它的类是否有 `@EnableXcs` 注解。如果存在 `@EnableXcs` 注解,它会继续并注册一个 `String` 类型的 bean,值为 "This is a custom bean!"。如果不存在,它会抛出异常,提示 `@EnableXcs` 注解不存在于导入它的类上。 4. **正常使用** + 当 `MyConfiguration` 使用 `@EnableXcs` 注解时,程序可以正常运行,从上下文中获取到的 String 类型的 bean 值为 "This is a custom bean!"。 5. **异常情况** + 但如果 `MyConfiguration` 直接使用 `@Import(MyImportAware.class)` 导入 `MyImportAware` 类,而不使用 `@EnableXcs` 注解,会导致 `MyImportAware` 在查找 `@EnableXcs` 注解时发现它不存在,从而抛出异常。 #### 源码分析总结 1. **应用程序启动** + 使用 `AnnotationConfigApplicationContext` 初始化 Spring 上下文,加载 `MyConfiguration` 配置类。程序试图从 Spring 上下文中获取一个类型为 `String` 的 bean。 2. **上下文刷新** + 在构造 `AnnotationConfigApplicationContext` 时,会调用 `refresh()` 方法,这是 Spring 上下文的初始化和刷新过程的入口点。 3. **实例化Beans** + 执行 `finishBeanFactoryInitialization`,该方法负责预实例化上下文中的所有非懒加载单例bean。对于每个bean,它都会调用 `getBean` 方法。 4. **处理 `ImportAware` Beans** + 如果bean实现了 `ImportAware` 接口,`postProcessBeforeInitialization` 方法会为该 bean 设置导入它的类的注解元数据。在我们的例子中,`MyImportAware` 就是这样一个bean。 5. **检查 `@EnableXcs`** + 在 `MyImportAware` 的 `setImportMetadata` 方法中,它会检查导入它的类是否有 `@EnableXcs` 注解。如果存在该注解,则继续处理;如果不存在,则抛出异常。 6. **Bean创建** + 如果导入类上存在 `@EnableXcs` 注解,`MyImportAware` 继续并定义了一个 `String` 类型的 bean。这就是我们从上下文中检索并在控制台上打印的bean。 7. **异常处理** + 如果直接使用 `@Import` 导入 `MyImportAware` 而不使用 `@EnableXcs` 注解,会发生异常,因为 `MyImportAware` 期望导入它的类上有 `@EnableXcs` 注解。 ================================================ FILE: spring-aware/spring-aware-importAware/pom.xml ================================================ spring-aware com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-aware-importAware ================================================ FILE: spring-aware/spring-aware-importAware/src/main/java/com/xcs/spring/ImportAwareApplication.java ================================================ package com.xcs.spring; import com.xcs.spring.config.MyConfiguration; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class ImportAwareApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); String customBean = context.getBean(String.class); System.out.println(customBean); } } ================================================ FILE: spring-aware/spring-aware-importAware/src/main/java/com/xcs/spring/annotation/EnableXcs.java ================================================ package com.xcs.spring.annotation; import com.xcs.spring.config.MyImportAware; import org.springframework.context.annotation.Import; import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import(MyImportAware.class) public @interface EnableXcs { } ================================================ FILE: spring-aware/spring-aware-importAware/src/main/java/com/xcs/spring/config/MyConfiguration.java ================================================ package com.xcs.spring.config; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @Configuration @Import(MyImportAware.class) public class MyConfiguration { } ================================================ FILE: spring-aware/spring-aware-importAware/src/main/java/com/xcs/spring/config/MyImportAware.java ================================================ package com.xcs.spring.config; import com.xcs.spring.annotation.EnableXcs; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ImportAware; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.type.AnnotationMetadata; public class MyImportAware implements ImportAware { private AnnotationAttributes enableXcs; @Override public void setImportMetadata(AnnotationMetadata importMetadata) { this.enableXcs = AnnotationAttributes.fromMap( importMetadata.getAnnotationAttributes(EnableXcs.class.getName(), false)); if (this.enableXcs == null) { throw new IllegalArgumentException( "@EnableXcs is not present on importing class " + importMetadata.getClassName()); } } @Bean public String customBean() { return "This is a custom bean!"; } } ================================================ FILE: spring-aware/spring-aware-messageSourceAware/README.md ================================================ ## MessageSourceAware - [MessageSourceAware](#messagesourceaware) - [一、基本信息](#一基本信息) - [二、接口描述](#二接口描述) - [三、接口源码](#三接口源码) - [四、主要功能](#四主要功能) - [五、最佳实践](#五最佳实践) - [六、时序图](#六时序图) - [七、源码分析](#七源码分析) - [八、注意事项](#八注意事项) - [八、总结](#八总结) - [最佳实践总结](#最佳实践总结) - [源码分析总结](#源码分析总结) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [我的CSDN](https://blog.csdn.net/duzhuang2399/article/details/133916775) 📚 **文章目录** - [所有文章](https://github.com/xuchengsheng/spring-reading) 🔗 **源码地址** - [MessageSourceAware源码](https://github.com/xuchengsheng/spring-reading/blob/master/spring-aware/spring-aware-messageSourceAware) ### 二、接口描述 `MessageSourceAware` 接口,主要用于对象希望被注入`MessageSource`。`MessageSource`是Spring中用于国际化(i18n)的接口,它提供了从不同的消息资源(例如:属性文件)获取消息的方法。使用`MessageSource`,我们可以为应用程序提供国际化的消息支持。 ### 三、接口源码 `MessageSourceAware` 是 Spring 框架自 1.1.1 开始引入的一个核心接口。实现`MessageSourceAware`接口的对象会在Spring容器中被自动注入一个`MessageSource`实例。 ```java /** * 任何希望被通知运行其中的MessageSource(通常是ApplicationContext)的对象需要实现的接口。 * * 注意,MessageSource通常也可以作为bean引用传递 * (到任意的bean属性或构造函数参数),因为它在应用上下文中通常是以"name"为messageSource的bean定义的。 * * 作者: Juergen Hoeller, Chris Beams * 版本: 1.1.1 * 参见: ApplicationContextAware */ public interface MessageSourceAware extends Aware { /** * 设置此对象运行的MessageSource。 * 此方法在常规bean属性被填充之后调用,但在初始化回调(如InitializingBean的afterPropertiesSet或自定义的init-method)之前调用。 * 此方法在ApplicationContextAware的setApplicationContext方法之前被调用。 * * @param messageSource 此对象要使用的消息源 */ void setMessageSource(MessageSource messageSource); } ``` ### 四、主要功能 1. **自动注入** + 当一个bean实现了`MessageSourceAware`接口,并且被Spring容器管理时,Spring将会自动调用该bean的`setMessageSource`方法,传入当前应用上下文的`MessageSource`实例。 2. **国际化支持** + 通过`MessageSourceAware`,beans可以获得对`MessageSource`的访问权,从而可以根据不同的地区和语言获取相应的消息。这对于需要显示不同语言的错误消息、UI标签或其他用户面向的文本的beans特别有用。 3. **简化配置** + 虽然我们可以通过常规的依赖注入方法将`MessageSource`注入到beans中,但`MessageSourceAware`提供了一种更加自动化和明确的方法,特别是当我们的bean需要在初始化过程的特定阶段获得`MessageSource`时。 ### 五、最佳实践 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyMessageSourceAware`类型的bean,最后调用`getMessage`方法。 ```java public class MessageSourceAwareApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MyMessageSourceAware messageSourceAware = context.getBean(MyMessageSourceAware.class); messageSourceAware.getMessage(); } } ``` 这里使用`@Bean`注解,定义了两个Bean,是为了确保 `MyMessageSourceAware` ,`MessageSource`被 Spring 容器执行。其中`ResourceBundleMessageSource` 是 Spring 框架中用于国际化(i18n)的一个具体实现。它为应用程序提供了从属性文件中读取国际化消息的能力。 ```java @Configuration public class MyConfiguration { @Bean public MyMessageSourceAware myMessageSourceAware(){ return new MyMessageSourceAware(); } @Bean public MessageSource messageSource() { ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); messageSource.setBasename("i18n/messages"); return messageSource; } } ``` `MyMessageSourceAware`类使用`MessageSourceAware`接口来自动获得对`MessageSource`的引用。这个引用可以用来根据不同的语言或地区检索国际化的消息。然后利用注入的`MessageSource`,从属性文件中检索并打印两个国际化的消息,一个是英文的,另一个是简体中文的。 ```java public class MyMessageSourceAware implements MessageSourceAware { private MessageSource messageSource; @Override public void setMessageSource(MessageSource messageSource) { this.messageSource = messageSource; } public void getMessage() { System.out.println("English:"+messageSource.getMessage("greeting", null, Locale.ENGLISH)); System.out.println("中文:"+messageSource.getMessage("greeting", null, Locale.SIMPLIFIED_CHINESE)); } } ``` 运行结果发现,`MyMessageSourceAware`类已成功从属性文件中获取了国际化消息。 ```java English:Hello! 中文:我们好 ``` ### 六、时序图 ~~~mermaid sequenceDiagram Title: EnvironmentAware时序图 participant MessageSourceAwareApplication participant AnnotationConfigApplicationContext participant AbstractApplicationContext participant DefaultListableBeanFactory participant AbstractBeanFactory participant DefaultSingletonBeanRegistry participant AbstractAutowireCapableBeanFactory participant ApplicationContextAwareProcessor participant MyMessageSourceAware MessageSourceAwareApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)
创建上下文 AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()
刷新上下文 AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory)
初始化Bean工厂 AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons()
实例化单例 DefaultListableBeanFactory->>AbstractBeanFactory:getBean(name)
获取Bean AbstractBeanFactory->>AbstractBeanFactory:doGetBean(name,requiredType,args,typeCheckOnly)
执行获取Bean AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton(beanName,singletonFactory)
获取单例Bean DefaultSingletonBeanRegistry-->>AbstractBeanFactory:getObject()
获取Bean实例 AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean(beanName,mbd,args)
创建Bean AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:doCreateBean(beanName,mbd,args)
执行Bean创建 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:initializeBean(beanName,bean,mbd)
负责bean的初始化 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:applyBeanPostProcessorsBeforeInitialization(existingBean, beanName)
调用前置处理器 AbstractAutowireCapableBeanFactory->>ApplicationContextAwareProcessor:postProcessBeforeInitialization(bean,beanName)
触发Aware处理 ApplicationContextAwareProcessor->>ApplicationContextAwareProcessor:invokeAwareInterfaces(bean)
执行Aware回调 ApplicationContextAwareProcessor->>MyMessageSourceAware:setMessageSource(messageSource)
设置messageSource AbstractAutowireCapableBeanFactory-->>AbstractBeanFactory:返回Bean对象 AbstractBeanFactory-->>DefaultListableBeanFactory:返回Bean对象 AnnotationConfigApplicationContext-->>MessageSourceAwareApplication:初始化完成 ~~~ ### 七、源码分析 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyMessageSourceAware`类型的bean,最后调用`getMessage`方法。 ```java public class MessageSourceAwareApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MyMessageSourceAware messageSourceAware = context.getBean(MyMessageSourceAware.class); messageSourceAware.getMessage(); } } ``` 在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法 ```java public AnnotationConfigApplicationContext(Class... componentClasses) { this(); register(componentClasses); refresh(); ``` 在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中,我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 ```java @Override public void refresh() throws BeansException, IllegalStateException { // ... [代码部分省略以简化] // 步骤1. Initialize message source for this context. initMessageSource(); // 步骤2. Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // ... [代码部分省略以简化] } ``` 我们来到`org.springframework.context.support.AbstractApplicationContext#refresh`方法中的步骤1,在`org.springframework.context.support.AbstractApplicationContext#initMessageSource`方法中,这个方法确保Spring应用上下文总是有一个`MessageSource` bean可用,无论是否明确定义了它。如果用户没有定义,它会提供一个默认实现。这意味着在Spring上下文中,我们总是可以安全地调用`getMessage()`,因为总会有一个`MessageSource`可用。 ```java protected void initMessageSource() { // 获取Bean工厂 ConfigurableListableBeanFactory beanFactory = getBeanFactory(); // 检查是否已经存在名为MESSAGE_SOURCE_BEAN_NAME的bean if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) { // 如果存在,则获取该bean this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class); // 如果当前MessageSource具有层次结构并且没有设置父MessageSource if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) { HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource; if (hms.getParentMessageSource() == null) { // 设置父上下文作为父MessageSource(如果之前没有注册过父MessageSource) hms.setParentMessageSource(getInternalParentMessageSource()); } } // ... [代码部分省略以简化] } else { // 如果不存在MESSAGE_SOURCE_BEAN_NAME的bean,则创建一个DelegatingMessageSource并注册到上下文 // 使用DelegatingMessageSource以便能够处理getMessage调用 DelegatingMessageSource dms = new DelegatingMessageSource(); dms.setParentMessageSource(getInternalParentMessageSource()); this.messageSource = dms; beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource); // ... [代码部分省略以简化] } } ``` 我们来到`org.springframework.context.support.AbstractApplicationContext#refresh`方法中的步骤2,在`org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization`方法中,会继续调用`DefaultListableBeanFactory`类中的`preInstantiateSingletons`方法来完成所有剩余非懒加载的单列Bean对象。 ```java /** * 完成此工厂的bean初始化,实例化所有剩余的非延迟初始化单例bean。 * * @param beanFactory 要初始化的bean工厂 */ protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { // ... [代码部分省略以简化] // 完成所有剩余非懒加载的单列Bean对象。 beanFactory.preInstantiateSingletons(); } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons`方法中,主要的核心目的是预先实例化所有非懒加载的单例bean。在Spring的上下文初始化完成后,该方法会被触发,以确保所有单例bean都被正确地创建并初始化。其中`getBean(beanName)`是此方法的核心操作。对于容器中定义的每一个单例bean,它都会调用`getBean`方法,这将触发bean的实例化、初始化及其依赖的注入。如果bean之前没有被创建过,那么这个调用会导致其被实例化和初始化。 ```java public void preInstantiateSingletons() throws BeansException { // ... [代码部分省略以简化] // 循环遍历所有bean的名称 for (String beanName : beanNames) { getBean(beanName); } // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#getBean()`方法中,又调用了`doGetBean`方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,`doGetBean`负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。 ```java @Override public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中,首先检查所请求的bean是否是一个单例并且已经创建。如果尚未创建,它将创建一个新的实例。在这个过程中,它处理可能的异常情况,如循环引用,并确保返回的bean是正确的类型。这是Spring容器bean生命周期管理的核心部分。 ```java protected T doGetBean( String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException { // ... [代码部分省略以简化] // 开始创建bean实例 if (mbd.isSingleton()) { // 如果bean是单例的,我们会尝试从单例缓存中获取它 // 如果不存在,则使用lambda创建一个新的实例 sharedInstance = getSingleton(beanName, () -> { try { // 尝试创建bean实例 return createBean(beanName, mbd, args); } catch (BeansException ex) { // ... [代码部分省略以简化] } }); // 对于某些bean(例如FactoryBeans),可能需要进一步处理以获取真正的bean实例 beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } // ... [代码部分省略以简化] // 确保返回的bean实例与请求的类型匹配 return adaptBeanInstance(name, beanInstance, requiredType); } ``` 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()`方法中,主要负责从单例缓存中获取一个已存在的bean实例,或者使用提供的`ObjectFactory`创建一个新的实例。这是确保bean在Spring容器中作为单例存在的关键部分。 ```java public Object getSingleton(String beanName, ObjectFactory singletonFactory) { // 断言bean名称不能为空 Assert.notNull(beanName, "Bean name must not be null"); // 同步访问单例对象缓存,确保线程安全 synchronized (this.singletonObjects) { // 从缓存中获取单例对象 Object singletonObject = this.singletonObjects.get(beanName); // 如果缓存中没有找到 if (singletonObject == null) { // ... [代码部分省略以简化] try { // 使用工厂创建新的单例实例 singletonObject = singletonFactory.getObject(); newSingleton = true; } catch (IllegalStateException ex) { // ... [代码部分省略以简化] } catch (BeanCreationException ex) { // ... [代码部分省略以简化] } finally { // ... [代码部分省略以简化] } // ... [代码部分省略以简化] } // 返回单例对象 return singletonObject; } } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()`方法中,主要的逻辑是调用 `doCreateBean`,这是真正进行 bean 实例化、属性填充和初始化的地方。这个方法会返回新创建的 bean 实例。 ```java @Override protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // ... [代码部分省略以简化] try { // 正常的bean实例化、属性注入和初始化。 // 这里是真正进行bean创建的部分。 Object beanInstance = doCreateBean(beanName, mbdToUse, args); // 记录bean成功创建的日志 if (logger.isTraceEnabled()) { logger.trace("Finished creating instance of bean '" + beanName + "'"); } return beanInstance; } catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { // ... [代码部分省略以简化] } catch (Throwable ex) { // ... [代码部分省略以简化] } } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中,`initializeBean`方法是bean初始化,确保bean是完全配置和准备好的。 ```java protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // 声明一个对象,后续可能用于存放初始化后的bean或它的代理对象 Object exposedObject = bean; // ... [代码部分省略以简化] try { // ... [代码部分省略以简化] // bean初始化 exposedObject = initializeBean(beanName, exposedObject, mbd); } catch (Throwable ex) { // ... [代码部分省略以简化] } // 返回创建和初始化后的bean return exposedObject; } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean`方法中,如果条件满足(即 bean 不是合成的),那么它会调用 `applyBeanPostProcessorsBeforeInitialization` 方法。这个方法是 Spring 生命周期中的一个关键点,它会遍历所有已注册的 `BeanPostProcessor` 实现,并调用它们的 `postProcessBeforeInitialization` 方法。这允许我们和内部处理器在 bean 初始化之前对其进行修改或执行其他操作。 ```java protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { // ... [代码部分省略以简化] Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } // ... [代码部分省略以简化] return wrappedBean; } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization`方法中,遍历每一个 `BeanPostProcessor` 的 `postProcessBeforeInitialization` 方法都有机会对bean进行修改或增强 ```java @Override public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; for (BeanPostProcessor processor : getBeanPostProcessors()) { Object current = processor.postProcessBeforeInitialization(result, beanName); if (current == null) { return result; } result = current; } return result; } ``` 在`org.springframework.context.support.ApplicationContextAwareProcessor#postProcessBeforeInitialization`方法中,在这个方法的实现特别关注那些实现了 "aware" 接口的 beans,并为它们提供所需的运行环境信息。 ```java @Override @Nullable public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware || bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware || bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware || bean instanceof ApplicationStartupAware)) { return bean; } // ... [代码部分省略以简化] invokeAwareInterfaces(bean); return bean; } ``` 在`org.springframework.context.support.ApplicationContextAwareProcessor#invokeAwareInterfaces`方法中,用于处理实现了"Aware"接口的beans。这些接口使得beans能够被自动"感知"并获得对其运行环境或特定依赖的引用,而不需要显式地进行查找或注入。 ```java private void invokeAwareInterfaces(Object bean) { // ... [代码部分省略以简化] if (bean instanceof MessageSourceAware) { ((MessageSourceAware) bean).setMessageSource(this.applicationContext); } // ... [代码部分省略以简化] } ``` 最后执行到我们自定义的逻辑中,`MyMessageSourceAware`类使用`MessageSourceAware`接口来自动获得对`MessageSource`的引用。这个引用可以用来根据不同的语言或地区检索国际化的消息。然后利用注入的`MessageSource`,从属性文件中检索并打印两个国际化的消息,一个是英文的,另一个是简体中文的。 ```java public class MyMessageSourceAware implements MessageSourceAware { private MessageSource messageSource; @Override public void setMessageSource(MessageSource messageSource) { this.messageSource = messageSource; } public void getMessage() { System.out.println("English:"+messageSource.getMessage("greeting", null, Locale.ENGLISH)); System.out.println("中文:"+messageSource.getMessage("greeting", null, Locale.SIMPLIFIED_CHINESE)); } } ``` ### 八、注意事项 1. **明确的配置** + 确保我们的Spring上下文中有一个`MessageSource` bean,通常命名为“messageSource”。虽然Spring提供了一个默认的,但为了满足自定义需求,我们可能需要明确地配置它。 2. **生命周期时机** + `MessageSourceAware`的`setMessageSource`方法在常规属性设置之后和初始化方法(如`InitializingBean`的`afterPropertiesSet`或任何自定义的init方法)之前被调用。确保我们的bean不在其生命周期的早期阶段(例如,在构造函数中)期望使用`MessageSource`。 3. **文件位置和命名** + 如果我们使用`ResourceBundleMessageSource`或类似的机制,确保我们的属性文件位于类路径上,并且与我们在`MessageSource`配置中指定的basename匹配。 4. **编码问题** + 属性文件默认使用ISO-8859-1编码。如果我们的消息包含非此编码的字符(例如中文、俄文等),确保使用Unicode转义或正确设置文件的编码。 5. **父子上下文** + 在使用Spring的父子上下文(例如,在Web应用中)时,子上下文可以访问父上下文中的`MessageSource`,但反之则不行。确保我们在正确的上下文中配置了`MessageSource`。 6. **避免硬编码** + 尽量不要在代码中硬编码消息键或默认消息。最好在属性文件中管理它们,这样在未来需要更改或添加新的语言支持时,我们不需要修改代码。 7. **默认消息** + 当使用`MessageSource`检索消息时,考虑提供一个默认消息。这可以在未找到特定消息时提供一个后备,避免抛出异常。 ### 八、总结 #### 最佳实践总结 1. **启动类** + 在`MessageSourceAwareApplication`类中,使用了`AnnotationConfigApplicationContext`来启动Spring应用。这个上下文是专为基于Java注解的配置而设计的。启动时,它加载了`MyConfiguration`配置类,并从上下文中获取了`MyMessageSourceAware`bean,随后调用了`getMessage`方法显示消息。 2. **配置类** + `MyConfiguration`是一个基于Java的Spring配置类,其中定义了两个bean:`MyMessageSourceAware`和`messageSource`。`messageSource` bean是一个`ResourceBundleMessageSource`实例,用于从`i18n/messages`基本名称的属性文件中读取国际化消息。 3. **实现MessageSourceAware接口** + `MyMessageSourceAware`类实现了`MessageSourceAware`接口,这意味着Spring容器会自动注入一个`MessageSource`实例到这个bean中。这是通过`setMessageSource`方法完成的。 4. **消息检索** + 在`MyMessageSourceAware`的`getMessage`方法中,使用了注入的`MessageSource`来检索和打印两种语言的国际化消息:英文和简体中文。 5. **运行结果** + 当应用程序执行时,它成功地从对应的属性文件中获取并显示了英文和简体中文的国际化消息。 #### 源码分析总结 1. **应用启动** + 我们从`MessageSourceAwareApplication`启动应用,使用`AnnotationConfigApplicationContext`初始化Spring容器,并加载`MyConfiguration`配置。 2. **容器初始化** + 在`AnnotationConfigApplicationContext`的构造函数中,执行了`register`和`refresh`方法,其中`refresh`是最重要的,它触发了容器的初始化和bean的创建过程。 3. **消息源初始化** + 在容器刷新的`refresh`方法中,首先确保了一个`MessageSource` bean存在,这是通过`initMessageSource`方法完成的。如果没有明确定义`MessageSource` bean,Spring会提供一个默认实现,确保应用上下文总是有一个可用。 4. **bean实例化** + 随后,在`refresh`方法中,通过调用`finishBeanFactoryInitialization`方法,容器开始实例化所有非延迟加载的单例bean。 5. **Bean的生命周期** + 在bean的创建过程中,Spring容器会确保所有的生命周期回调都被正确地执行,其中最重要的是`BeanPostProcessors`。这些处理器提供了一个插件机制,允许我们在bean的初始化前后执行自定义的逻辑。 6. **处理Aware接口** + `ApplicationContextAwareProcessor`是一个特殊的`BeanPostProcessor`,它关心那些实现了"Aware"接口的beans。对于实现了`MessageSourceAware`的beans,该处理器会自动注入应用上下文的`MessageSource`。 7. **消息检索** + 在我们的`MyMessageSourceAware`类中,已经成功地获取了`MessageSource`的引用。然后,我们调用其`getMessage`方法,从属性文件中检索并打印两个国际化的消息。 ================================================ FILE: spring-aware/spring-aware-messageSourceAware/pom.xml ================================================ spring-aware com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-aware-messageSourceAware ================================================ FILE: spring-aware/spring-aware-messageSourceAware/src/main/java/com/xcs/spring/MessageSourceAwareApplication.java ================================================ package com.xcs.spring; import com.xcs.spring.config.MyConfiguration; import com.xcs.spring.config.MyMessageSourceAware; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年09月16日 16时09分 **/ public class MessageSourceAwareApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MyMessageSourceAware messageSourceAware = context.getBean(MyMessageSourceAware.class); messageSourceAware.getMessage(); } } ================================================ FILE: spring-aware/spring-aware-messageSourceAware/src/main/java/com/xcs/spring/config/MyConfiguration.java ================================================ package com.xcs.spring.config; import org.springframework.context.MessageSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.support.ResourceBundleMessageSource; /** * @author xcs * @date 2023年09月19日 16时35分 **/ @Configuration public class MyConfiguration { @Bean public MyMessageSourceAware myMessageSourceAware(){ return new MyMessageSourceAware(); } @Bean public MessageSource messageSource() { ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); messageSource.setBasename("i18n/messages"); return messageSource; } } ================================================ FILE: spring-aware/spring-aware-messageSourceAware/src/main/java/com/xcs/spring/config/MyMessageSourceAware.java ================================================ package com.xcs.spring.config; import org.springframework.context.MessageSource; import org.springframework.context.MessageSourceAware; import java.util.Locale; public class MyMessageSourceAware implements MessageSourceAware { private MessageSource messageSource; @Override public void setMessageSource(MessageSource messageSource) { this.messageSource = messageSource; } public void getMessage() { System.out.println("English:"+messageSource.getMessage("greeting", null, Locale.ENGLISH)); System.out.println("中文:"+messageSource.getMessage("greeting", null, Locale.SIMPLIFIED_CHINESE)); } } ================================================ FILE: spring-aware/spring-aware-messageSourceAware/src/main/resources/i18n/messages_en.properties ================================================ greeting=Hello! ================================================ FILE: spring-aware/spring-aware-messageSourceAware/src/main/resources/i18n/messages_zh_CN.properties ================================================ greeting=\u4f60\u597d ================================================ FILE: spring-aware/spring-aware-resourceLoaderAware/README.md ================================================ ## ResourceLoaderAware - [ResourceLoaderAware](#resourceloaderaware) - [一、基本信息](#一基本信息) - [二、接口描述](#二接口描述) - [三、接口源码](#三接口源码) - [四、主要功能](#四主要功能) - [五、最佳实践](#五最佳实践) - [六、时序图](#六时序图) - [七、源码分析](#七源码分析) - [八、注意事项](#八注意事项) - [九、总结](#九总结) - [最佳实践总结](#最佳实践总结) - [源码分析总结](#源码分析总结) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [我的CSDN](https://blog.csdn.net/duzhuang2399/article/details/133915709) 📚 **文章目录** - [所有文章](https://github.com/xuchengsheng/spring-reading) 🔗 **源码地址** - [ResourceLoaderAware源码](https://github.com/xuchengsheng/spring-reading/tree/master/spring-aware/spring-aware-resourceLoaderAware) ### 二、接口描述 `ResourceLoaderAware` 接口,它用于为需要访问 `ResourceLoader` 的 bean 提供一个回调。`ResourceLoader` 是一个简单的策略接口,定义了如何加载底层资源(如类路径或文件系统资源)的方法。 ### 三、接口源码 `ResourceLoaderAware` 是 Spring 框架自 10.03.2004 开始引入的一个核心接口。实现`ResourceLoaderAware`接口的对象会在Spring容器中被自动注入一个`ResourceLoader`实例。 ```java /** * 任何希望被通知 ResourceLoader(通常是 ApplicationContext)的对象都应实现此接口。 * 这是通过 org.springframework.context.ApplicationContextAware 接口完全依赖 ApplicationContext 的另一种方式。 * * 请注意,org.springframework.core.io.Resource 依赖也可以暴露为类型为 Resource 或 Resource[] 的bean属性, * 通过字符串在bean工厂中进行自动类型转换进行填充。这样就消除了为了访问特定文件资源而实现任何回调接口的需求。 * * 当我们的应用对象需要访问其名称经过计算的各种文件资源时,通常需要一个 ResourceLoader。 * 一个好策略是使对象使用 org.springframework.core.io.DefaultResourceLoader,但仍然实现 ResourceLoaderAware, * 以允许在 ApplicationContext 中运行时覆盖。参考 org.springframework.context.support.ReloadableResourceBundleMessageSource 为例。 * * 传入的 ResourceLoader 也可以检查是否为 org.springframework.core.io.support.ResourcePatternResolver 接口 * 并相应地进行类型转换,以将资源模式解析为 Resource 对象的数组。在 ApplicationContext 中运行时,这总是可行的 * (因为 context 接口扩展了 ResourcePatternResolver 接口)。默认情况下,使用 org.springframework.core.io.support.PathMatchingResourcePatternResolver; * 也可以查看 ResourcePatternUtils.getResourcePatternResolver 方法。 * * 作为 ResourcePatternResolver 依赖的替代,考虑暴露类型为 Resource[] 的 bean 属性,通过模式字符串 * 在bean工厂的绑定时间进行自动类型转换进行填充。 * * @author Juergen Hoeller * @author Chris Beams * @since 10.03.2004 * @see ApplicationContextAware * @see org.springframework.core.io.Resource * @see org.springframework.core.io.ResourceLoader * @see org.springframework.core.io.support.ResourcePatternResolver */ public interface ResourceLoaderAware extends Aware { /** * 设置此对象运行的 ResourceLoader。 * 可能是一个 ResourcePatternResolver,可以通过 instanceof ResourcePatternResolver 检查。 * 也可以查看 ResourcePatternUtils.getResourcePatternResolver 方法。 * 在填充正常的bean属性之后但在像 InitializingBean 的 afterPropertiesSet 这样的初始化回调或自定义初始化方法之前被调用。 * 在 ApplicationContextAware 的 setApplicationContext 之前调用。 * @param resourceLoader 此对象要使用的 ResourceLoader * @see org.springframework.core.io.support.ResourcePatternResolver * @see org.springframework.core.io.support.ResourcePatternUtils#getResourcePatternResolver */ void setResourceLoader(ResourceLoader resourceLoader); } ``` ### 四、主要功能 1. **资源加载回调** + 当 bean 实现了 `ResourceLoaderAware` 接口,Spring 容器会在该 bean 初始化时,自动将一个 `ResourceLoader` 注入到该 bean 中,从而使得 bean 可以加载资源。 2. **提供资源加载策略** + 通过 `ResourceLoader`, bean 可以加载各种类型的资源,如类路径资源、文件系统资源、URL 资源等。它为资源访问提供了一个统一的策略。 3. **减少对 ApplicationContext 的直接依赖** + 虽然 `ApplicationContext` 也扩展了 `ResourceLoader` 的功能,但有时候 bean 只需要资源加载功能,而不需要其他的 ApplicationContext 功能。通过实现 `ResourceLoaderAware`,bean 可以只获得资源加载功能,从而降低与完整的 `ApplicationContext` 的耦合。 ### 五、最佳实践 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyResourceLoaderAware`类型的bean,最后调用`getResource`方法并传递了一个路径。 ```java public class ResourceLoaderAwareApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MyResourceLoaderAware resourceLoaderAware = context.getBean(MyResourceLoaderAware.class); resourceLoaderAware.getResource("classpath:xcs.txt"); } } ``` 这里使用`@Bean`注解,定义了以个Bean,是为了确保 `MyResourceLoaderAware` 被 Spring 容器执行。 ```java @Configuration public class MyConfiguration { @Bean public MyResourceLoaderAware myResourceLoaderAware(){ return new MyResourceLoaderAware(); } } ``` `MyResourceLoaderAware` 类是一个简单的实用工具,我们利用 Spring 的 `ResourceLoader` 机制,可以用于加载和打印资源内容。 ```java public class MyResourceLoaderAware implements ResourceLoaderAware { private ResourceLoader resourceLoader; @Override public void setResourceLoader(ResourceLoader resourceLoader) { this.resourceLoader = resourceLoader; } public void getResource(String location){ try { Resource resource = resourceLoader.getResource(location); System.out.println("Resource content: " + new String(FileCopyUtils.copyToByteArray(resource.getInputStream()))); } catch (IOException e) { e.printStackTrace(); } } } ``` 运行结果发现, `MyResourceLoaderAware` 类成功地读取了资源文件的内容并将其打印到了控制台。 ```java Resource content: hello world ``` ### 六、时序图 ~~~mermaid sequenceDiagram Title: EnvironmentAware时序图 participant ResourceLoaderAwareApplication participant AnnotationConfigApplicationContext participant AbstractApplicationContext participant DefaultListableBeanFactory participant AbstractBeanFactory participant DefaultSingletonBeanRegistry participant AbstractAutowireCapableBeanFactory participant ApplicationContextAwareProcessor participant MyResourceLoaderAware ResourceLoaderAwareApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)
创建上下文 AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()
刷新上下文 AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory)
初始化Bean工厂 AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons()
实例化单例 DefaultListableBeanFactory->>AbstractBeanFactory:getBean(name)
获取Bean AbstractBeanFactory->>AbstractBeanFactory:doGetBean(name,requiredType,args,typeCheckOnly)
执行获取Bean AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton(beanName,singletonFactory)
获取单例Bean DefaultSingletonBeanRegistry-->>AbstractBeanFactory:getObject()
获取Bean实例 AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean(beanName,mbd,args)
创建Bean AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:doCreateBean(beanName,mbd,args)
执行Bean创建 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:initializeBean(beanName,bean,mbd)
负责bean的初始化 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:applyBeanPostProcessorsBeforeInitialization(existingBean, beanName)
调用前置处理器 AbstractAutowireCapableBeanFactory->>ApplicationContextAwareProcessor:postProcessBeforeInitialization(bean,beanName)
触发Aware处理 ApplicationContextAwareProcessor->>ApplicationContextAwareProcessor:invokeAwareInterfaces(bean)
执行Aware回调 ApplicationContextAwareProcessor->>MyResourceLoaderAware:setResourceLoader(resourceLoader)
设置resourceLoader AbstractAutowireCapableBeanFactory-->>AbstractBeanFactory:返回Bean对象 AbstractBeanFactory-->>DefaultListableBeanFactory:返回Bean对象 AnnotationConfigApplicationContext-->>ResourceLoaderAwareApplication:初始化完成 ~~~ ### 七、源码分析 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyResourceLoaderAware`类型的bean,最后调用`getResource`方法并传递了一个路径。 ```java public class ResourceLoaderAwareApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MyResourceLoaderAware resourceLoaderAware = context.getBean(MyResourceLoaderAware.class); resourceLoaderAware.getResource("classpath:xcs.txt"); } } ``` 在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法 ```java public AnnotationConfigApplicationContext(Class... componentClasses) { this(); register(componentClasses); refresh(); ``` 在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 ```java @Override public void refresh() throws BeansException, IllegalStateException { // ... [代码部分省略以简化] // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization`方法中,会继续调用`DefaultListableBeanFactory`类中的`preInstantiateSingletons`方法来完成所有剩余非懒加载的单列Bean对象。 ```java /** * 完成此工厂的bean初始化,实例化所有剩余的非延迟初始化单例bean。 * * @param beanFactory 要初始化的bean工厂 */ protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { // ... [代码部分省略以简化] // 完成所有剩余非懒加载的单列Bean对象。 beanFactory.preInstantiateSingletons(); } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons`方法中,主要的核心目的是预先实例化所有非懒加载的单例bean。在Spring的上下文初始化完成后,该方法会被触发,以确保所有单例bean都被正确地创建并初始化。其中`getBean(beanName)`是此方法的核心操作。对于容器中定义的每一个单例bean,它都会调用`getBean`方法,这将触发bean的实例化、初始化及其依赖的注入。如果bean之前没有被创建过,那么这个调用会导致其被实例化和初始化。 ```java public void preInstantiateSingletons() throws BeansException { // ... [代码部分省略以简化] // 循环遍历所有bean的名称 for (String beanName : beanNames) { getBean(beanName); } // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#getBean()`方法中,又调用了`doGetBean`方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,`doGetBean`负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。 ```java @Override public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中,首先检查所请求的bean是否是一个单例并且已经创建。如果尚未创建,它将创建一个新的实例。在这个过程中,它处理可能的异常情况,如循环引用,并确保返回的bean是正确的类型。这是Spring容器bean生命周期管理的核心部分。 ```java protected T doGetBean( String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException { // ... [代码部分省略以简化] // 开始创建bean实例 if (mbd.isSingleton()) { // 如果bean是单例的,我们会尝试从单例缓存中获取它 // 如果不存在,则使用lambda创建一个新的实例 sharedInstance = getSingleton(beanName, () -> { try { // 尝试创建bean实例 return createBean(beanName, mbd, args); } catch (BeansException ex) { // ... [代码部分省略以简化] } }); // 对于某些bean(例如FactoryBeans),可能需要进一步处理以获取真正的bean实例 beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } // ... [代码部分省略以简化] // 确保返回的bean实例与请求的类型匹配 return adaptBeanInstance(name, beanInstance, requiredType); } ``` 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()`方法中,主要负责从单例缓存中获取一个已存在的bean实例,或者使用提供的`ObjectFactory`创建一个新的实例。这是确保bean在Spring容器中作为单例存在的关键部分。 ```java public Object getSingleton(String beanName, ObjectFactory singletonFactory) { // 断言bean名称不能为空 Assert.notNull(beanName, "Bean name must not be null"); // 同步访问单例对象缓存,确保线程安全 synchronized (this.singletonObjects) { // 从缓存中获取单例对象 Object singletonObject = this.singletonObjects.get(beanName); // 如果缓存中没有找到 if (singletonObject == null) { // ... [代码部分省略以简化] try { // 使用工厂创建新的单例实例 singletonObject = singletonFactory.getObject(); newSingleton = true; } catch (IllegalStateException ex) { // ... [代码部分省略以简化] } catch (BeanCreationException ex) { // ... [代码部分省略以简化] } finally { // ... [代码部分省略以简化] } // ... [代码部分省略以简化] } // 返回单例对象 return singletonObject; } } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()`方法中,主要的逻辑是调用 `doCreateBean`,这是真正进行 bean 实例化、属性填充和初始化的地方。这个方法会返回新创建的 bean 实例。 ```java @Override protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // ... [代码部分省略以简化] try { // 正常的bean实例化、属性注入和初始化。 // 这里是真正进行bean创建的部分。 Object beanInstance = doCreateBean(beanName, mbdToUse, args); // 记录bean成功创建的日志 if (logger.isTraceEnabled()) { logger.trace("Finished creating instance of bean '" + beanName + "'"); } return beanInstance; } catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { // ... [代码部分省略以简化] } catch (Throwable ex) { // ... [代码部分省略以简化] } } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中,`initializeBean`方法是bean初始化,确保bean是完全配置和准备好的。 ```java protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // 声明一个对象,后续可能用于存放初始化后的bean或它的代理对象 Object exposedObject = bean; // ... [代码部分省略以简化] try { // ... [代码部分省略以简化] // bean初始化 exposedObject = initializeBean(beanName, exposedObject, mbd); } catch (Throwable ex) { // ... [代码部分省略以简化] } // 返回创建和初始化后的bean return exposedObject; } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean`方法中,如果条件满足(即 bean 不是合成的),那么它会调用 `applyBeanPostProcessorsBeforeInitialization` 方法。这个方法是 Spring 生命周期中的一个关键点,它会遍历所有已注册的 `BeanPostProcessor` 实现,并调用它们的 `postProcessBeforeInitialization` 方法。这允许我们和内部处理器在 bean 初始化之前对其进行修改或执行其他操作。 ```java protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { // ... [代码部分省略以简化] Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } // ... [代码部分省略以简化] return wrappedBean; } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization`方法中,遍历每一个 `BeanPostProcessor` 的 `postProcessBeforeInitialization` 方法都有机会对bean进行修改或增强 ```java @Override public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; for (BeanPostProcessor processor : getBeanPostProcessors()) { Object current = processor.postProcessBeforeInitialization(result, beanName); if (current == null) { return result; } result = current; } return result; } ``` 在`org.springframework.context.support.ApplicationContextAwareProcessor#postProcessBeforeInitialization`方法中,在这个方法的实现特别关注那些实现了 "aware" 接口的 beans,并为它们提供所需的运行环境信息。 ```java @Override @Nullable public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware || bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware || bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware || bean instanceof ApplicationStartupAware)) { return bean; } // ... [代码部分省略以简化] invokeAwareInterfaces(bean); return bean; } ``` 在`org.springframework.context.support.ApplicationContextAwareProcessor#invokeAwareInterfaces`方法中,用于处理实现了"Aware"接口的beans。这些接口使得beans能够被自动"感知"并获得对其运行环境或特定依赖的引用,而不需要显式地进行查找或注入。 ```java private void invokeAwareInterfaces(Object bean) { // ... [代码部分省略以简化] if (bean instanceof ResourceLoaderAware) { ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext); } // ... [代码部分省略以简化] } ``` 最后执行到我们自定义的逻辑中,`MyResourceLoaderAware` 类是一个简单的实用工具,我们利用 Spring 的 `ResourceLoader` 机制,可以用于加载和打印资源内容。 ```java public class MyResourceLoaderAware implements ResourceLoaderAware { private ResourceLoader resourceLoader; @Override public void setResourceLoader(ResourceLoader resourceLoader) { this.resourceLoader = resourceLoader; } public void getResource(String location){ try { Resource resource = resourceLoader.getResource(location); System.out.println("Resource content: " + new String(FileCopyUtils.copyToByteArray(resource.getInputStream()))); } catch (IOException e) { e.printStackTrace(); } } } ``` ### 八、注意事项 1. **资源路径** + 当使用 `ResourceLoader` 获取资源时,需要提供完整的路径。例如,使用 "classpath:" 前缀来加载类路径上的资源。我们应确保路径是正确的,否则 `ResourceLoader` 可能找不到资源。 2. **资源缓存** + `ResourceLoader` 本身不提供资源内容的缓存功能。每次调用 `getResource` 方法都可能返回一个新的 `Resource` 实例。如果需要缓存资源内容,我们应该自己实现。 3. **资源存在性检查** + 使用 `ResourceLoader` 获取的 `Resource` 不保证资源确实存在。在尝试访问资源内容之前,我们应使用 `Resource.exists()` 方法检查资源是否存在。 4. **资源类型的多样性** + 根据运行环境和 `ResourceLoader` 的具体实现,它可以加载多种类型的资源,如类路径资源、文件系统资源、URL资源等。我们应当了解当前环境支持的资源类型,并正确使用。 5. **避免过度使用** + 虽然 `ResourceLoaderAware` 提供了一种方便的方式来访问资源,但不是所有的 beans 都需要它。只有当 bean 真正需要动态地加载资源时,才应实现这个接口。否则,更简洁的方式是直接注入 `Resource` 类型的属性。 6. **生命周期时机** + 当一个 bean 实现了 `ResourceLoaderAware` 接口,`setResourceLoader` 方法会在 bean 初始化的早期被调用,这确保了后续的 bean 初始化和业务逻辑可以使用到 `ResourceLoader`。但我们应确保不在构造函数中访问 `ResourceLoader`,因为它此时尚未被设置。 7. **避免过度使用** + 虽然 `ResourceLoaderAware` 提供了一种方便的方式来访问资源,但不是所有的 beans 都需要它。只有当 bean 真正需要动态地加载资源时,才应实现这个接口。否则,更简洁的方式是直接注入 `Resource` 类型的属性。 8. **与 `ApplicationContextAware` 的区别** + `ApplicationContext` 本身也是一个 `ResourceLoader`,因此实现 `ApplicationContextAware` 也可以获得类似的资源加载功能。但如果我们的 bean 只需要资源加载功能,而不需要其他的 `ApplicationContext` 功能,那么最好只实现 `ResourceLoaderAware` 以减少耦合。 ### 九、总结 #### 最佳实践总结 1. **启动类入口** + 使用了 `AnnotationConfigApplicationContext` 类来启动Spring应用。这是一个使用基于Java的注解来配置Spring容器的方式。上下文初始化时使用了 `MyConfiguration` 类作为配置类。接着,从Spring上下文中获取了一个 `MyResourceLoaderAware` 类型的bean。最后,调用了 `getResource` 方法并传入了一个指定的路径。 2. **配置类** + `MyConfiguration` 是一个标注有 `@Configuration` 的配置类,表示它是一个Spring配置类。在这个配置类中,通过 `@Bean` 注解定义了一个 `MyResourceLoaderAware` 类型的bean。这确保 `MyResourceLoaderAware` 被Spring容器管理,并且 `ResourceLoader` 被正确注入。 3. **资源加载实现** + `MyResourceLoaderAware` 类实现了 `ResourceLoaderAware` 接口,从而允许Spring容器在bean初始化时自动注入 `ResourceLoader`。`getResource` 方法使用注入的 `ResourceLoader` 来加载给定路径的资源,然后读取并打印资源的内容。 4. **运行结果** + 当运行应用程序时,`MyResourceLoaderAware` 成功地从指定的资源路径加载内容,并将 "hello world" 打印到控制台。 #### 源码分析总结 1. **启动与上下文初始化** + 使用 `AnnotationConfigApplicationContext` 创建了一个基于Java注解的Spring容器,传入了 `MyConfiguration` 作为配置。从上下文中获取 `MyResourceLoaderAware` 类型的bean,并调用了其 `getResource` 方法。 2. **配置类与Bean注册** + 在 `MyConfiguration` 配置类中,通过 `@Bean` 注解注册了 `MyResourceLoaderAware` 类型的bean。 3. **上下文刷新与Bean实例化** + 在上下文的 `refresh` 方法中,调用了 `finishBeanFactoryInitialization` 方法以实例化所有剩余的非懒加载单例Bean。在此方法中,调用了 `preInstantiateSingletons` 方法预先实例化所有非懒加载的单例bean。 4. **Bean获取与创建流程** + 使用 `getBean` 方法来实际获取Bean,这可能会触发Bean的创建。在 `doGetBean` 方法中,如果bean还未创建,会尝试创建新实例,处理依赖关系,并返回正确的bean实例。 5. **单例Bean的创建与缓存** + 在 `getSingleton` 方法中,首先尝试从单例缓存中获取bean实例。如果尚未创建,则使用提供的 `ObjectFactory` 创建新实例,并存入缓存。 6. **Bean初始化** + 在Bean创建完成后,进行初始化。在 `initializeBean` 方法中,会对特定的bean应用 `BeanPostProcessor` 逻辑。 7. **Aware接口的处理** + 使用 `ApplicationContextAwareProcessor` 处理实现了 `Aware` 接口的beans。对于实现了 `ResourceLoaderAware` 的beans,会注入一个 `ResourceLoader` 实例。 8. **自定义逻辑** + 在 `MyResourceLoaderAware` 类中,利用注入的 `ResourceLoader`,加载并打印资源内容。 ================================================ FILE: spring-aware/spring-aware-resourceLoaderAware/pom.xml ================================================ spring-aware com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-aware-resourceLoaderAware ================================================ FILE: spring-aware/spring-aware-resourceLoaderAware/src/main/java/com/xcs/spring/ResourceLoaderAwareApplication.java ================================================ package com.xcs.spring; import com.xcs.spring.config.MyConfiguration; import com.xcs.spring.config.MyResourceLoaderAware; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年09月16日 16时09分 **/ public class ResourceLoaderAwareApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MyResourceLoaderAware resourceLoaderAware = context.getBean(MyResourceLoaderAware.class); resourceLoaderAware.getResource("classpath:xcs.txt"); } } ================================================ FILE: spring-aware/spring-aware-resourceLoaderAware/src/main/java/com/xcs/spring/config/MyConfiguration.java ================================================ package com.xcs.spring.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @author xcs * @date 2023年09月19日 16时35分 **/ @Configuration public class MyConfiguration { @Bean public MyResourceLoaderAware myResourceLoaderAware(){ return new MyResourceLoaderAware(); } } ================================================ FILE: spring-aware/spring-aware-resourceLoaderAware/src/main/java/com/xcs/spring/config/MyResourceLoaderAware.java ================================================ package com.xcs.spring.config; import org.springframework.context.ResourceLoaderAware; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.springframework.util.FileCopyUtils; import java.io.IOException; public class MyResourceLoaderAware implements ResourceLoaderAware { private ResourceLoader resourceLoader; @Override public void setResourceLoader(ResourceLoader resourceLoader) { this.resourceLoader = resourceLoader; } public void getResource(String location){ try { Resource resource = resourceLoader.getResource(location); System.out.println("Resource content: " + new String(FileCopyUtils.copyToByteArray(resource.getInputStream()))); } catch (IOException e) { e.printStackTrace(); } } } ================================================ FILE: spring-aware/spring-aware-resourceLoaderAware/src/main/resources/xcs.txt ================================================ hello world ================================================ FILE: spring-beans/pom.xml ================================================ spring-reading com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-beans pom spring-bean-beanDefinition spring-bean-beanDefinitionHolder spring-bean-beanDefinitionRegistry spring-bean-xmlBeanDefinitionReader spring-bean-propertiesBeanDefinitionReader spring-bean-groovyBeanDefinitionReader spring-bean-annotatedBeanDefinitionReader spring-bean-classPathBeanDefinitionScanner ================================================ FILE: spring-beans/spring-bean-annotatedBeanDefinitionReader/README.md ================================================ ## AnnotatedBeanDefinitionReader - [AnnotatedBeanDefinitionReader](#annotatedbeandefinitionreader) - [一、知识储备](#一知识储备) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、最佳实践](#四最佳实践) - [五、时序图](#五时序图) - [六、源码分析](#六源码分析) - [注册注解后置处理器阶段](#注册注解后置处理器阶段) - [注册Bean定义阶段](#注册bean定义阶段) - [七、与其他组件的关系](#七与其他组件的关系) - [八、常见问题](#八常见问题) ### 一、知识储备 1. **`AnnotationMetadata`** + `AnnotationMetadata` 是 Spring 框架中用于处理类上的注解信息的接口,它提供了对类上注解信息的访问和操作方法。 `AnnotatedBeanDefinitionReader` 利用 `AnnotationMetadata` 解析类上的注解信息,并将其转化为 Spring 的 BeanDefinition。 + [点击查看AnnotationMetadata接口](https://github.com/xuchengsheng/spring-reading/tree/master/spring-metadata/spring-metadata-annotationMetadata) 2. **`BeanDefinition`** + `BeanDefinition` 是 Spring 中描述和管理 Bean 配置的核心概念,它包括了有关 Bean 的信息,如类名、作用域、依赖关系、初始化方法等,而 `AnnotatedBeanDefinitionReader` 的主要任务之一是将使用注解配置的类转化为 `BeanDefinition` 并注册到 Spring 容器中。 + [点击查看BeanDefinition接口](https://github.com/xuchengsheng/spring-reading/tree/master/spring-beans/spring-bean-beanDefinition) ### 二、基本描述 `AnnotatedBeanDefinitionReader`是一个用于读取和解析带有注解的Bean定义的类,它主要用于基于注解的配置方式,允许开发者将Java类标记为Spring组件,从而让Spring容器自动扫描和注册这些组件,而不需要显式配置这些组件的Bean定义。 ### 三、主要功能 1. **注册Bean定义** + 适用于那些没有使用特定注解的类,但需要交给 Spring 容器管理的情况。 2. **集成 Spring 容器** + `AnnotatedBeanDefinitionReader` 通常与 Spring 容器的注册机(如 `GenericApplicationContext`)一起使用,从而将解析的 Bean 定义注册到容器。 ### 四、最佳实践 创建一个 `DefaultListableBeanFactory` 容器和关联的 `AnnotatedBeanDefinitionReader`,手动注册一个 `MyBean` 类为 Spring Bean,然后获取和打印该 Bean 的实例。 ```java public class AnnotatedBeanDefinitionReaderDemo { public static void main(String[] args) { // 创建一个 AnnotationConfigApplicationContext DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); // 创建 AnnotatedBeanDefinitionReader 并将其关联到容器 AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(factory); // 使用 AnnotatedBeanDefinitionReader 注册Bean对象 reader.registerBean(MyBean.class); // 获取并打印 MyBean System.out.println("MyBean = " + factory.getBean(MyBean.class)); } } ``` `MyBean` 的Java类,代表了一个简单的Java Bean。 ```java public class MyBean { } ``` 运行结果发现,手动注册和获取 Bean 的过程成功,并且 `MyBean` 现在已经被 Spring 容器管理。 ```java MyBean = com.xcs.spring.bean.MyBean@6166e06f ``` ### 五、时序图 ~~~mermaid sequenceDiagram Title: AnnotatedBeanDefinitionReader时序图 par 注册注解后置处理器阶段 AnnotatedBeanDefinitionReaderDemo->>AnnotatedBeanDefinitionReader:new AnnotatedBeanDefinitionReader(registry) Note over AnnotatedBeanDefinitionReaderDemo, AnnotatedBeanDefinitionReader: 创建 AnnotatedBeanDefinitionReader 对象 AnnotatedBeanDefinitionReader->>AnnotatedBeanDefinitionReader:AnnotatedBeanDefinitionReader(registry, environment) Note over AnnotatedBeanDefinitionReader: 使用容器注册和环境信息 AnnotatedBeanDefinitionReader->>AnnotationConfigUtils:registerAnnotationConfigProcessors(registry) Note over AnnotatedBeanDefinitionReader, AnnotationConfigUtils: 调用工具类方法注册注解后置处理器 AnnotationConfigUtils->>AnnotationConfigUtils:registerAnnotationConfigProcessors(registry, source) Note over AnnotationConfigUtils: 注册注解后置处理器到容器 AnnotatedBeanDefinitionReader->>AnnotatedBeanDefinitionReaderDemo:返回reader Note over AnnotatedBeanDefinitionReaderDemo,AnnotatedBeanDefinitionReader: 返回 AnnotatedBeanDefinitionReader 实例 end par 注册Bean定义阶段 AnnotatedBeanDefinitionReaderDemo->>AnnotatedBeanDefinitionReader:registerBean(beanClass) Note over AnnotatedBeanDefinitionReaderDemo, AnnotatedBeanDefinitionReader: 注册 BeanClass AnnotatedBeanDefinitionReader->>AnnotatedBeanDefinitionReader:doRegisterBean(beanClass, name, qualifiers, supplier, customizers) Note over AnnotatedBeanDefinitionReader: 执行 Bean 注册 AnnotatedBeanDefinitionReader->>AnnotatedBeanDefinitionReader:new AnnotatedGenericBeanDefinition(beanClass) Note over AnnotatedBeanDefinitionReader: 创建 AnnotatedGenericBeanDefinition 对象 AnnotatedBeanDefinitionReader->>AnnotationConfigUtils:processCommonDefinitionAnnotations(abd) Note over AnnotatedBeanDefinitionReader, AnnotationConfigUtils: 处理常见的Bean定义注解 AnnotationConfigUtils->>AnnotationConfigUtils:processCommonDefinitionAnnotations(abd,metadata) Note over AnnotationConfigUtils: 处理 Bean 的元数据信息 AnnotatedBeanDefinitionReader->>AnnotatedBeanDefinitionReader:new BeanDefinitionHolder(abd, beanName) Note over AnnotatedBeanDefinitionReader: 创建 BeanDefinitionHolder AnnotatedBeanDefinitionReader->>BeanDefinitionReaderUtils:registerBeanDefinition(definitionHolder, registry) Note over AnnotatedBeanDefinitionReader, BeanDefinitionReaderUtils: 注册 Bean 定义 end ~~~ ### 六、源码分析 #### 注册注解后置处理器阶段 在`org.springframework.context.annotation.AnnotatedBeanDefinitionReader#AnnotatedBeanDefinitionReader(registry)`方法中,实际上是通过委派给另一个构造函数来创建,并且获取了上下文的环境变量进行传递。 ```java public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) { this(registry, getOrCreateEnvironment(registry)); } ``` 在`org.springframework.context.annotation.AnnotatedBeanDefinitionReader#AnnotatedBeanDefinitionReader(registry,environment)`方法中,主要目的是为了初始化 `AnnotatedBeanDefinitionReader`,并确保相关的条件评估器和注解处理器已经注册到 Spring 容器中,以便进行基于注解的组件扫描和 Bean 注册。 ```java public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); Assert.notNull(environment, "Environment must not be null"); // 初始化 BeanDefinitionRegistry 和 Environment this.registry = registry; this.conditionEvaluator = new ConditionEvaluator(registry, environment, null); // 注册注解相关的后置处理器,以支持基于注解的组件扫描和 Bean 注册 AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); } ``` 在`org.springframework.context.annotation.AnnotationConfigUtils#registerAnnotationConfigProcessors(registry)`方法中,注解配置处理器注册到 Spring 容器中,从而启用注解驱动的配置。 ```java public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) { registerAnnotationConfigProcessors(registry, null); } ``` 在`org.springframework.context.annotation.AnnotationConfigUtils#registerAnnotationConfigProcessors(registry, source)`方法中,主要目的是注册一些关键的注解配置处理器,以便支持注解驱动的配置和处理不同类型的注解。 ```java public static Set registerAnnotationConfigProcessors( BeanDefinitionRegistry registry, @Nullable Object source) { // 1. 如果 BeanDefinitionRegistry 是 DefaultListableBeanFactory 的实例,执行以下操作 DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry); if (beanFactory != null) { // 1.1 检查当前的依赖比较器是否是 AnnotationAwareOrderComparator 的实例,如果不是,设置依赖比较器为 AnnotationAwareOrderComparator.INSTANCE,用于处理注解驱动排序。 if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) { beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE); } // 1.2 检查当前的自动装配候选解析器是否是 ContextAnnotationAutowireCandidateResolver 的实例,如果不是,设置自动装配候选解析器为 ContextAnnotationAutowireCandidateResolver,用于处理注解驱动的自动装配。 if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) { beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); } } // 2. 创建一个空的 LinkedHashSet 用于存储将要注册的 Bean 定义。 Set beanDefs = new LinkedHashSet<>(8); // 3. 检查是否已经注册了名为 CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME 的 Bean 定义。如果没有,创建一个 ConfigurationClassPostProcessor 类型的 Bean 定义,并将其添加到 BeanDefinitionRegistry 中。 if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)); } // 4. 检查是否已经注册了名为 AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME 的 Bean 定义。如果没有,创建一个 AutowiredAnnotationBeanPostProcessor 类型的 Bean 定义,并将其添加到 BeanDefinitionRegistry 中。 if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)); } // 5. 检查是否已经注册了名为 COMMON_ANNOTATION_PROCESSOR_BEAN_NAME 的 Bean 定义,这是用于支持 JSR-250 注解的处理器。如果没有,并且检测到 JSR-250 的支持,创建一个 CommonAnnotationBeanPostProcessor 类型的 Bean 定义,并将其添加到 BeanDefinitionRegistry 中。 if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)); } // 6. 检查是否已经注册了名为 PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME 的 Bean 定义,这是用于支持 JPA 注解的处理器。如果没有,并且检测到 JPA 的支持,创建一个 PersistenceAnnotationBeanPostProcessor 类型的 Bean 定义,并将其添加到 BeanDefinitionRegistry 中。 if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(); try { def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, AnnotationConfigUtils.class.getClassLoader())); } catch (ClassNotFoundException ex) { throw new IllegalStateException( "Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex); } def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)); } // 7. 检查是否已经注册了名为 EVENT_LISTENER_PROCESSOR_BEAN_NAME 的 Bean 定义。如果没有,创建一个 EventListenerMethodProcessor 类型的 Bean 定义,并将其添加到 BeanDefinitionRegistry 中。 if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME)); } // 8. 检查是否已经注册了名为 EVENT_LISTENER_FACTORY_BEAN_NAME 的 Bean 定义。如果没有,创建一个 DefaultEventListenerFactory 类型的 Bean 定义,并将其添加到 BeanDefinitionRegistry 中。 if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME)); } // 9. 返回包含注册的 Bean 定义的 LinkedHashSet。 return beanDefs; } ``` #### 注册Bean定义阶段 在`org.springframework.context.annotation.AnnotatedBeanDefinitionReader#registerBean(beanClass)`方法中,实际上又调用了`doRegisterBean`方法。 ```java public void registerBean(Class beanClass) { doRegisterBean(beanClass, null, null, null, null); } ``` 在`org.springframework.context.annotation.AnnotatedBeanDefinitionReader#doRegisterBean`方法中,创建一个 Bean 的定义,设置其属性(如作用域、名称、限定符、自定义配置等),最后将这个定义注册到 Spring 容器中,使其成为容器管理的 Bean。 ```java private void doRegisterBean(Class beanClass, @Nullable String name, @Nullable Class[] qualifiers, @Nullable Supplier supplier, @Nullable BeanDefinitionCustomizer[] customizers) { // 步骤1: 创建一个 AnnotatedGenericBeanDefinition,用于表示要注册的 Bean 的定义 AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass); // 如果存在条件判断,根据条件结果决定是否注册 Bean if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) { return; } // 设置 Bean 的实例提供者(supplier),用于创建 Bean 实例 abd.setInstanceSupplier(supplier); // 解析 Bean 的作用域并设置,确定 Bean 的范围(Scope) ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd); abd.setScope(scopeMetadata.getScopeName()); // 生成或使用指定的 Bean 名称,如果没有指定名称,生成默认的 Bean 名称 String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry)); // 步骤2: 处理常见的 Bean 定义注解,如 @Lazy、@Primary 等 AnnotationConfigUtils.processCommonDefinitionAnnotations(abd); // 处理 Bean 的限定符(qualifiers),如 @Qualifier 注解 if (qualifiers != null) { for (Class qualifier : qualifiers) { if (Primary.class == qualifier) { abd.setPrimary(true); } else if (Lazy.class == qualifier) { abd.setLazyInit(true); } else { abd.addQualifier(new AutowireCandidateQualifier(qualifier)); } } } // 自定义 Bean 定义,如果有自定义操作的话 if (customizers != null) { for (BeanDefinitionCustomizer customizer : customizers) { customizer.customize(abd); } } // 步骤3: 将 Bean 注册到 Spring 容器中,使其成为容器管理的 Bean BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName); definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry); } ``` > [`org.springframework.context.annotation.AnnotatedBeanDefinitionReader#doRegisterBean`步骤2] 在`org.springframework.context.annotation.AnnotationConfigUtils#processCommonDefinitionAnnotations(abd)`方法中,处理 Bean 定义上的常见注解,以确保 Bean 在容器中的行为和属性符合这些注解的规定。 ```java public static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd) { processCommonDefinitionAnnotations(abd, abd.getMetadata()); } ``` 在`org.springframework.context.annotation.AnnotationConfigUtils#processCommonDefinitionAnnotations(abd,metadata)`方法中,处理常见的 Bean 定义注解,如 `@Lazy`、`@Primary`、`@DependsOn`、`@Role` 和 `@Description`。 ```java static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) { // 处理 @Lazy 注解 AnnotationAttributes lazy = attributesFor(metadata, Lazy.class); if (lazy != null) { abd.setLazyInit(lazy.getBoolean("value")); } else if (abd.getMetadata() != metadata) { lazy = attributesFor(abd.getMetadata(), Lazy.class); if (lazy != null) { abd.setLazyInit(lazy.getBoolean("value")); } } // 处理 @Primary 注解 if (metadata.isAnnotated(Primary.class.getName())) { abd.setPrimary(true); } // 处理 @DependsOn 注解 AnnotationAttributes dependsOn = attributesFor(metadata, DependsOn.class); if (dependsOn != null) { abd.setDependsOn(dependsOn.getStringArray("value")); } // 处理 @Role 注解 AnnotationAttributes role = attributesFor(metadata, Role.class); if (role != null) { abd.setRole(role.getNumber("value").intValue()); } // 处理 @Description 注解 AnnotationAttributes description = attributesFor(metadata, Description.class); if (description != null) { abd.setDescription(description.getString("value")); } } ``` > [`org.springframework.context.annotation.AnnotatedBeanDefinitionReader#doRegisterBean`步骤3] 在`org.springframework.beans.factory.support.BeanDefinitionReaderUtils#registerBeanDefinition`方法中,将Bean定义注册到Spring容器的Bean定义注册表中,并处理别名的注册。 ```java public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // Register bean definition under primary name. String beanName = definitionHolder.getBeanName(); registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // Register aliases for bean name, if any. String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String alias : aliases) { registry.registerAlias(beanName, alias); } } } ``` ### 七、与其他组件的关系 1. **`BeanDefinitionLoader`** + 通常与 Spring Boot 应用程序的加载和初始化过程相关。在 Spring Boot 中,`BeanDefinitionLoader` 用于加载应用程序的配置,并可能涉及到 `AnnotationConfigApplicationContext` 或 `AnnotationConfigWebApplicationContext`,这些上下文类会使用 `AnnotatedBeanDefinitionReader` 来注册基于注解的 Bean。 2. **`AnnotationConfigServletWebApplicationContext`** + 这是 Spring Boot Web 应用程序的上下文类,用于基于注解的配置。它内部使用 `AnnotatedBeanDefinitionReader` 来处理组件扫描和 Bean 的注册,以支持 Spring Boot Web 应用程序的初始化。 3. **`AnnotationConfigApplicationContext`** + 这是 Spring 的标准应用程序上下文,用于基于注解的配置。它内部使用 `AnnotatedBeanDefinitionReader` 来注册基于注解的 Bean 定义,并支持组件扫描以及自动配置。 4. **`AnnotationConfigWebApplicationContext`** + 这是 Spring Web 应用程序的上下文类,用于基于注解的配置。它类似于 `AnnotationConfigApplicationContext`,但专门用于 Web 应用程序。它也使用 `AnnotatedBeanDefinitionReader` 来处理组件扫描和 Bean 的注册,以支持 Spring Web 应用程序的初始化。 ### 八、常见问题 1. **基于注解的 Spring 配置** + `AnnotatedBeanDefinitionReader` 可以用于创建基于注解的 Spring 配置,从而避免使用传统的 XML 配置。这在现代的 Spring 应用程序开发中非常常见,可以提高配置的可读性和维护性。 2. **Spring Boot 自动配置** + 在 Spring Boot 中,`AnnotatedBeanDefinitionReader` 用于自动扫描并注册 Spring Boot 自动配置类。这使得 Spring Boot 应用程序可以自动配置各种功能,从而降低了我们的工作负担。 3. **自定义组件扫描** + 我们可以自定义组件扫描器,使用 `AnnotatedBeanDefinitionReader` 的功能来实现特定的组件注册逻辑。这在某些复杂场景下非常有用,例如需要根据特定条件动态注册组件。 ================================================ FILE: spring-beans/spring-bean-annotatedBeanDefinitionReader/pom.xml ================================================ spring-beans com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-bean-annotatedBeanDefinitionReader ================================================ FILE: spring-beans/spring-bean-annotatedBeanDefinitionReader/src/main/java/com/xcs/spring/AnnotatedBeanDefinitionReaderDemo.java ================================================ package com.xcs.spring; import com.xcs.spring.bean.MyBean; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.context.annotation.AnnotatedBeanDefinitionReader; /** * @author xcs * @date 2023年11月07日 16时13分 **/ public class AnnotatedBeanDefinitionReaderDemo { public static void main(String[] args) { // 创建一个 AnnotationConfigApplicationContext DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); // 创建 AnnotatedBeanDefinitionReader 并将其关联到容器 AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(factory); // 使用 AnnotatedBeanDefinitionReader 注册Bean对象 reader.registerBean(MyBean.class); // 获取并打印 MyBean System.out.println("MyBean = " + factory.getBean(MyBean.class)); } } ================================================ FILE: spring-beans/spring-bean-annotatedBeanDefinitionReader/src/main/java/com/xcs/spring/bean/MyBean.java ================================================ package com.xcs.spring.bean; /** * @author xcs * @date 2023年11月07日 16时15分 **/ public class MyBean { } ================================================ FILE: spring-beans/spring-bean-beanDefinition/README.md ================================================ ## BeanDefinition - [BeanDefinition](#beandefinition) - [一、知识储备](#一知识储备) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、接口源码](#四接口源码) - [五、主要实现](#五主要实现) - [六、最佳实践](#六最佳实践) - [七、与其他组件的关系](#七与其他组件的关系) - [八、常见问题](#八常见问题) ### 一、知识储备 1. **`MetadataReader`** + `MetadataReader` 是 Spring 提供的一个接口,用于读取类的元数据信息。它可以用于扫描类文件,获取类的基本信息,如类名、类的注解等。在注解驱动的开发中,`MetadataReader` 通常用于扫描包中的类,并从这些类中提取注解信息,以便配置 Spring Bean。[点击查看](https://github.com/xuchengsheng/spring-reading/tree/master/spring-metadata/spring-metadata-metadataReader) 2. **`AnnotationMetadata.introspect`** + `AnnotationMetadata` 接口中的 `introspect` 方法用于深入分析类的注解信息。它可以帮助我们获取类上的注解、类的方法上的注解、类的字段上的注解等。在 Spring 中,`introspect` 方法通常用于解析被 `@Component`, `@Configuration` 和其他注解标记的类,以确定它们如何被实例化并配置为 Spring Bean。[点击查看](https://github.com/xuchengsheng/spring-reading/tree/master/spring-metadata/spring-metadata-annotationMetadata) ### 二、基本描述 `BeanDefinition` 是 Spring 框架中的关键构建块,它是一种配置元数据,用于详细描述和定义应用程序中的 Bean 对象,包括 Bean 的类名、作用域、依赖关系、构造函数参数、属性值、初始化方法、销毁方法等信息,从而允许 Spring 容器准确地实例化、配置和管理这些 Bean。通过`BeanDefinition`,我们可以灵活地配置应用程序中的组件,使其能够实现依赖注入、AOP 切面、作用域控制等核心功能,促进松耦合、可维护和可扩展的应用程序开发。 ### 三、主要功能 1. **定义 Bean 的类** + 用于指定要实例化的 Bean 的类名。它告诉 Spring 容器要创建哪个 Java 类的对象。 2. **定义 Bean 的作用域** + 允许我们指定 Bean 的作用域,例如 singleton(单例)或 prototype(多例)。这影响了 Bean 在容器中的生命周期。 3. **构造函数参数和属性值** + 允许我们指定 Bean 的构造函数参数和属性值,以便在实例化 Bean 时传递参数或设置属性。 4. **定义初始化和销毁方法** + 定义 Bean 的初始化方法和销毁方法,以确保在 Bean 创建和销毁时执行特定的逻辑。 5. **Bean 的延迟初始化** + 允许我们设置 Bean 是否延迟初始化,即在第一次请求时创建 Bean 实例。 6. **依赖关系** + 允许我们指定 Bean 之间的依赖关系,以确保在创建 Bean 时正确注入依赖的其他 Bean。 7. **描述 Bean 的角色** + 允许我们为 Bean 指定一个角色(role),通常包括应用程序 Bean、基础设施 Bean、测试 Bean 等。 8. **Bean 的属性覆盖** + 允许我们使用属性覆盖机制,通过不同的配置源(如属性文件或环境变量)覆盖已定义的属性值。 9. **Bean 的注解和元数据** + 可以包含关于 Bean 的注解信息和元数据,这对于处理注解驱动的开发非常有用。 10. **动态创建和注册 Bean** + 允许我们在运行时动态创建和注册 Bean,而不仅仅是静态配置。 ### 四、接口源码 从`BeanDefinition` 接口源码来看,它描述和配置 Spring Bean 的各个方面。它包括了配置 Bean 的类名、作用域、初始化和销毁方法、构造函数参数、属性值等。 ```java /** * BeanDefinition 描述一个 Bean 实例,包括属性值、构造函数参数值以及具体实现提供的更多信息。 * * 这只是一个最小的接口:主要意图是允许 BeanFactoryPostProcessor 检查和修改属性值以及其他 Bean 元数据。 * * @author Juergen Hoeller * @author Rob Harrop * @since 2004-03-19 * @see ConfigurableListableBeanFactory#getBeanDefinition * @see org.springframework.beans.factory.support.RootBeanDefinition * @see org.springframework.beans.factory.support.ChildBeanDefinition */ public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement { /** * 标准单例范围的范围标识符:value。 * 请注意,扩展的 Bean 工厂可能支持更多范围。 * @see #setScope * @see ConfigurableBeanFactory#SCOPE_SINGLETON */ String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON; /** * 标准原型范围的范围标识符:value。 * 请注意,扩展的 Bean 工厂可能支持更多范围。 * @see #setScope * @see ConfigurableBeanFactory#SCOPE_PROTOTYPE */ String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE; /** * 表示 BeanDefinition 是应用程序的主要部分的角色提示。 * 通常对应于用户定义的 Bean。 */ int ROLE_APPLICATION = 0; /** * 表示 BeanDefinition 是某个更大配置的支持部分的角色提示,通常是外部 * org.springframework.beans.factory.parsing.ComponentDefinition。 * 当在特定的 org.springframework.beans.factory.parsing.ComponentDefinition 上查看时,这些"SUPPORT" Bean 被认为足够重要, * 但在查看应用程序的整体配置时,它们无关紧要。 */ int ROLE_SUPPORT = 1; /** * 表示 BeanDefinition 提供了完全背景角色,与最终用户无关。 * 当注册完全属于 org.springframework.beans.factory.parsing.ComponentDefinition 内部工作的 Bean 时使用此提示。 */ int ROLE_INFRASTRUCTURE = 2; /** * 设置此 Bean 定义的父定义的名称,如果有的话。 */ void setParentName(@Nullable String parentName); /** * 返回此 Bean 定义的父定义的名称,如果有的话。 */ @Nullable String getParentName(); /** * 指定此 Bean 定义的 Bean 类名称。 * 在 Bean 工厂后处理期间,可以修改类名,通常用解析后的类名替换原始类名。 */ void setBeanClassName(@Nullable String beanClassName); /** * 返回此 Bean 定义的当前 Bean 类名。 * 请注意,这不一定是运行时实际使用的类名,在子定义从父定义继承类名的情况下。 * 此外,在工厂方法上调用的类可能为空。 * 因此,不要将其视为运行时的确定 Bean 类型,而只在个别 Bean 定义级别用于解析目的。 */ @Nullable String getBeanClassName(); /** * 覆盖此 Bean 的目标范围,指定新的范围名称。 * @see #SCOPE_SINGLETON * @see #SCOPE_PROTOTYPE */ void setScope(@Nullable String scope); /** * 返回此 Bean 的当前目标范围名称,如果尚未知,则返回 null。 */ @Nullable String getScope(); /** * 设置此 Bean 是否应懒初始化。 * 如果为 false,则 Bean 将由执行单例的 Bean 工厂在启动时实例化。 */ void setLazyInit(boolean lazyInit); /** * 返回此 Bean 是否应懒初始化,即不在启动时急切实例化。仅适用于单例 Bean。 */ boolean isLazyInit(); /** * 设置此 Bean 依赖于初始化的 Bean 的名称。 * Bean 工厂将保证这些 Bean 首先得到初始化。 */ void setDependsOn(@Nullable String... dependsOn); /** * 返回此 Bean 依赖的 Bean 名称。 */ @Nullable String[] getDependsOn(); /** * 设置此 Bean 是否是自动装配候选 Bean。 * 请注意,此标志仅用于影响基于类型的自动装配。 * 它不影响名称上的显式引用,如果指定的 Bean 未标记为自动装配候选 Bean,则名称匹配仍然会注入 Bean。 */ void setAutowireCandidate(boolean autowireCandidate); /** * 返回此 Bean 是否是自动装配候选 Bean。 */ boolean isAutowireCandidate(); /** * 设置此 Bean 是否是主要自动装配候选 Bean。 * 如果多个匹配的候选 Bean 中有一个 Bean 的此值为 true,则它将作为补充选项。 */ void setPrimary(boolean primary); /** * 返回此 Bean 是否是主要自动装配候选 Bean。 */ boolean isPrimary(); /** * 指定要使用的工厂 Bean,如果有的话。 * 这是要调用指定工厂方法的 Bean 的名称。 * @see #setFactoryMethodName */ void setFactoryBeanName(@Nullable String factoryBeanName); /** * 返回工厂 Bean 名称,如果有的话。 */ @Nullable String getFactoryBeanName(); /** * 指定工厂方法,如果有的话。此方法将使用构造函数参数调用, * 或者如果未指定参数,则使用没有参数调用。 * 该方法将在指定工厂 Bean 上调用,如果没有指定工厂 Bean,则在本地 Bean 类上调用。 * @see #setFactoryBeanName * @see #setBeanClassName */ void setFactoryMethodName(@Nullable String factoryMethodName); /** * 返回工厂方法,如果有的话。 */ @Nullable String getFactoryMethodName(); /** * 返回此 Bean 的构造函数参数值。 * 返回的实例可以在 Bean 工厂后处理期间修改。 * @return 构造函数参数值对象(永不为 null) */ ConstructorArgumentValues getConstructorArgumentValues(); /** * 返回是否为此 Bean 定义定义了构造函数参数值。 * @since 5.0.2 */ default boolean hasConstructorArgumentValues() { return !getConstructorArgumentValues().isEmpty(); } /** * 返回要应用于 Bean 新实例的属性值。 * 返回的实例可以在 Bean 工厂后处理期间修改。 * @return 可变属性值对象(永不为 null) */ MutablePropertyValues getPropertyValues(); /** * 返回是否为此 Bean 定义定义了属性值。 * @since 5.0.2 */ default boolean hasPropertyValues() { return !getPropertyValues().isEmpty(); } /** * 设置初始化方法的名称。 * @since 5.1 */ void setInitMethodName(@Nullable String initMethodName); /** * 返回初始化方法的名称。 * @since 5.1 */ @Nullable String getInitMethodName(); /** * 设置销毁方法的名称。 * @since 5.1 */ void setDestroyMethodName(@Nullable String destroyMethodName); /** * 返回销毁方法的名称。 * @since 5.1 */ @Nullable String getDestroyMethodName(); /** * 设置 BeanDefinition 的角色提示。角色提示提供框架和工具一个有关特定 BeanDefinition 的角色和重要性的指示。 * @since 5.1 * @see #ROLE_APPLICATION * @see #ROLE_SUPPORT * @see #ROLE_INFRASTRUCTURE */ void setRole(int role); /** * 获取 BeanDefinition 的角色提示。角色提示提供框架和工具一个有关特定 BeanDefinition 的角色和重要性的指示。 * @see #ROLE_APPLICATION * @see #ROLE_SUPPORT * @see #ROLE_INFRASTRUCTURE */ int getRole(); /** * 设置 BeanDefinition 的人类可读描述。 * @since 5.1 */ void setDescription(@Nullable String description); /** * 返回 BeanDefinition 的人类可读描述。 */ @Nullable String getDescription(); /** * 基于 Bean 类或其他特定元数据返回可解析的类型的类型。 * 这通常在运行时合并的 Bean 定义上完全解析,但不一定在配置时定义实例上解析。 * @return 可解析类型(可能为 ResolvableType#NONE) * @since 5.2 * @see ConfigurableBeanFactory#getMergedBeanDefinition */ ResolvableType getResolvableType(); /** * 返回是否为Singleton,在所有调用上返回单个共享实例。 * @see #SCOPE_SINGLETON */ boolean isSingleton(); /** * 返回是否为Prototype,每次调用都返回独立的实例。 * @since 3.0 * @see #SCOPE_PROTOTYPE */ boolean isPrototype(); /** * 返回此 Bean 是否是"抽象"的,即不应该被实例化。 */ boolean isAbstract(); /** * 返回此 Bean 定义所来自的资源的描述(以便在出现错误时显示上下文)。 */ @Nullable String getResourceDescription(); /** * 返回原始的 Bean 定义,如果没有则返回 {@code null}。 * 允许检索已装饰的 Bean 定义(如果有)。 * 请注意,此方法返回最直接的起源。遍历起源链以找到用户定义的原始 Bean 定义。 */ @Nullable BeanDefinition getOriginatingBeanDefinition(); } ``` ### 五、主要实现 1. **`GenericBeanDefinition`** - 描述通用的 Bean 定义,可以用于大多数类型的 Bean。 - 具有灵活的属性配置,可以设置类名、作用域、初始化和销毁方法、构造函数参数、属性值等。 - 通常用于手动配置 Bean 或需要自定义 Bean 定义的情况。 2. **`RootBeanDefinition`** - 用于表示独立的根级 Bean 定义,通常直接定义 Bean。 - 可以继承 `GenericBeanDefinition`,并支持配置 Bean 类的其他信息。 - 通常用于定义应用程序中的独立 Bean。 3. **`ChildBeanDefinition`** - 用于表示派生的子级 Bean 定义,继承父级 Bean 定义的配置。 - 通常用于创建一个 Bean 的变体,继承父 Bean 的配置并进行部分覆盖或修改。 - 具有指向父 Bean 的引用。 4. **`AnnotatedGenericBeanDefinition`** - 用于基于注解的 Bean 定义,通常用于扫描组件和配置类。 - 可以表示使用注解定义的 Bean,支持类级别的注解配置。 - 通常用于自动扫描和注册组件。 5. **`ConfigurationClassBeanDefinition`** - 用于表示配置类(`@Configuration` 注解)的 Bean 定义,通常用于 Spring 配置。 - 表示配置类作为 Bean 定义,支持包含其他 Bean 定义的配置类。 - 通常由 Spring 容器自动创建,以支持 `@Configuration` 注解的配置。 6. **`ScannedGenericBeanDefinition`** - 用于表示扫描到的 Bean 的 Bean 定义,通常用于自动扫描组件。 - 通常在组件扫描过程中创建,表示被发现的组件类。 - 通常用于自动注册组件,如 `@Component` 注解的类。 ~~~mermaid classDiagram direction BT class BeanDefinition { <> } class AnnotatedBeanDefinition { <> } class AbstractBeanDefinition { <> } class GenericBeanDefinition { } class RootBeanDefinition { } class ChildBeanDefinition { } class AnnotatedGenericBeanDefinition { } class ScannedGenericBeanDefinition { } class ConfigurationClassBeanDefinition { } AnnotatedBeanDefinition ..|> BeanDefinition AbstractBeanDefinition --|> BeanDefinition GenericBeanDefinition ..|> AbstractBeanDefinition RootBeanDefinition ..|> AbstractBeanDefinition ChildBeanDefinition ..|> AbstractBeanDefinition AnnotatedGenericBeanDefinition ..|> GenericBeanDefinition ScannedGenericBeanDefinition ..|> GenericBeanDefinition ConfigurationClassBeanDefinition ..|> RootBeanDefinition ~~~ ### 六、最佳实践 创建了一个Spring容器(`DefaultListableBeanFactory`),然后通过`createBeanDefinition()`方法创建并配置了一个自定义的Bean定义(`ScannedGenericBeanDefinition`),包括作用域、初始化方法、销毁方法、属性值等。接着,它注册这个Bean定义到容器中,并使用容器获取Bean实例,打印出Bean的内容。最后,它通过容器销毁这个Bean,调用其销毁方法。 ```java public class BeanDefinitionDemo { public static void main(String[] args) throws Exception { DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); beanFactory.registerBeanDefinition("myBean", createBeanDefinition()); // 获取MyBean MyBean myChildBean = beanFactory.getBean("myBean", MyBean.class); // 打印Bean对象 System.out.println("MyBean = " + myChildBean); // 销毁myBean beanFactory.destroySingleton("myBean"); } private static BeanDefinition createBeanDefinition() throws IOException { SimpleMetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory(); MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(MyBean.class.getName()); ScannedGenericBeanDefinition beanDefinition = new ScannedGenericBeanDefinition(metadataReader); beanDefinition.setScope("singleton"); beanDefinition.setLazyInit(true); beanDefinition.setPrimary(true); beanDefinition.setAbstract(false); beanDefinition.setInitMethodName("init"); beanDefinition.setDestroyMethodName("destroy"); beanDefinition.setAutowireCandidate(true); beanDefinition.setRole(BeanDefinition.ROLE_APPLICATION); beanDefinition.setDescription("This is a custom bean definition"); beanDefinition.setResourceDescription("com.xcs.spring.BeanDefinitionDemo"); beanDefinition.getPropertyValues().add("name", "lex"); beanDefinition.getPropertyValues().add("age", "18"); return beanDefinition; } } ``` `MyBean` 的Java类,代表了一个简单的Java Bean。 ```java public class MyBean { private String name; private String age; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAge() { return age; } public void setAge(String age) { this.age = age; } public void init(){ System.out.println("execute com.xcs.spring.bean.MyBean.init"); } public void destroy(){ System.out.println("execute com.xcs.spring.bean.MyBean.destroy"); } @Override public String toString() { return "MyBean{" + "name='" + name + '\'' + ", age='" + age + '\'' + '}'; } } ``` 运行结果发现,与`BeanDefinition`配置相关,初始化方法和销毁方法的调用以及属性值的设置受`BeanDefinition`中相应的配置项影响,`BeanDefinition`用于定义和配置Bean的元信息,使Spring容器可以正确管理Bean的生命周期和属性。 ```java execute com.xcs.spring.bean.MyBean.init MyBean = MyBean{name='lex', age='18'} execute com.xcs.spring.bean.MyBean.destroy ``` ### 七、与其他组件的关系 1. **`DefaultListableBeanFactory`** + 负责管理Bean的创建、初始化和销毁,而`BeanDefinition`提供了描述Bean的元信息的方式,`DefaultListableBeanFactory`使用`BeanDefinition`来创建和管理Bean实例。 2. **`BeanPostProcessor`** + 拦截Bean初始化过程的接口,它可以在Bean创建后、初始化前后对Bean进行处理。`BeanDefinition`的信息可以在`BeanPostProcessor`中使用,例如在初始化前修改Bean的属性值。 3. **`BeanDefinitionRegistry`** + 注册和管理`BeanDefinition`的接口,定义了`BeanDefinition`的注册和访问方法。`BeanFactory`和`ApplicationContext`实现了`BeanDefinitionRegistry`接口,通过它们可以注册和获取`BeanDefinition`。 4. **`BeanDefinitionReader`** + 从外部配置文件(如`XML、YAML、Properties`文件)中读取`BeanDefinition`的工具。它将外部配置信息解析成`BeanDefinition`并注册到`BeanFactory`中。 ### 八、常见问题 1. **`BeanDefinition`的作用是什么?** + 主要作用是定义和配置Bean的属性和行为,以便Spring容器可以根据这些信息动态地创建、初始化和管理Bean实例。 2. **`BeanDefinition`的生命周期是怎样的?** + 生命周期与Spring容器相同,它在容器启动时进行注册和解析,然后被用于创建和管理Bean实例的生命周期。 3. **`BeanDefinition`如何注册?** + 通过`BeanDefinitionRegistry`接口的实现类(如`DefaultListableBeanFactory`)进行注册,或者通过`BeanDefinitionReader`从外部配置文件中加载并注册。 4. **`BeanDefinition`的属性有哪些?** + 属性包括Bean的类名、作用域、初始化方法、销毁方法、属性值等。具体属性取决于Bean的配置需求。 5. **`BeanDefinition`如何修改?** + 可以在注册后通过编程方式进行修改,例如更改属性值、作用域、初始化方法、销毁方法等。 6. **`BeanDefinition`的作用域有哪些?** + 包括Singleton(单例)、Prototype(原型)、Request(请求)、Session(会话)、等等,可以根据需求选择合适的作用域。 7. **`BeanDefinition`的注册和加载有什么区别?** + 注册是将已创建的`BeanDefinition`添加到容器中,而加载是从外部配置文件中读取Bean的元信息并注册到容器中。 8. **如何使用`BeanDefinition`来实现依赖注入?** + 通过设置`BeanDefinition`中的属性,如构造函数参数、属性值、引用其他Bean的方式,可以实现依赖注入。 9. **`BeanDefinition`是否可以动态生成?** + 可以在运行时动态生成并注册到容器中,这在某些复杂的情况下非常有用,例如基于条件的Bean注册。 ================================================ FILE: spring-beans/spring-bean-beanDefinition/pom.xml ================================================ spring-beans com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-bean-beanDefinition ================================================ FILE: spring-beans/spring-bean-beanDefinition/src/main/java/com/xcs/spring/BeanDefinitionDemo.java ================================================ package com.xcs.spring; import com.xcs.spring.bean.MyBean; import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.context.annotation.ScannedGenericBeanDefinition; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.SimpleMetadataReaderFactory; import java.io.IOException; /** * @author xcs * @date 2023年11月01日 11时21分 **/ public class BeanDefinitionDemo { public static void main(String[] args) throws Exception { DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); beanFactory.registerBeanDefinition("myBean", createBeanDefinition()); // 获取MyBean MyBean myChildBean = beanFactory.getBean("myBean", MyBean.class); // 打印Bean对象 System.out.println("MyBean = " + myChildBean); // 销毁myBean beanFactory.destroySingleton("myBean"); } private static BeanDefinition createBeanDefinition() throws IOException { SimpleMetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory(); MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(MyBean.class.getName()); ScannedGenericBeanDefinition beanDefinition = new ScannedGenericBeanDefinition(metadataReader); beanDefinition.setScope("singleton"); beanDefinition.setLazyInit(true); beanDefinition.setPrimary(true); beanDefinition.setAbstract(false); beanDefinition.setInitMethodName("init"); beanDefinition.setDestroyMethodName("destroy"); beanDefinition.setAutowireCandidate(true); beanDefinition.setRole(BeanDefinition.ROLE_APPLICATION); beanDefinition.setDescription("This is a custom bean definition"); beanDefinition.setResourceDescription("com.xcs.spring.BeanDefinitionDemo"); beanDefinition.getPropertyValues().add("name", "lex"); beanDefinition.getPropertyValues().add("age", "18"); return beanDefinition; } } ================================================ FILE: spring-beans/spring-bean-beanDefinition/src/main/java/com/xcs/spring/bean/MyBean.java ================================================ package com.xcs.spring.bean; /** * @author xcs * @date 2023年11月01日 15时41分 **/ public class MyBean { private String name; private String age; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAge() { return age; } public void setAge(String age) { this.age = age; } public void init(){ System.out.println("execute com.xcs.spring.bean.MyBean.init"); } public void destroy(){ System.out.println("execute com.xcs.spring.bean.MyBean.destroy"); } @Override public String toString() { return "MyBean{" + "name='" + name + '\'' + ", age='" + age + '\'' + '}'; } } ================================================ FILE: spring-beans/spring-bean-beanDefinitionHolder/README.md ================================================ ## BeanDefinitionHolder - [BeanDefinitionHolder](#beandefinitionholder) - [一、知识储备](#一知识储备) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、接口源码](#四接口源码) - [五、主要实现](#五主要实现) - [六、最佳实践](#六最佳实践) - [七、与其他组件的关系](#七与其他组件的关系) - [八、常见问题](#八常见问题) ### 一、知识储备 1. **Spring Framework 基础知识** + 我们需要熟悉 Spring Framework 的基本概念,包括什么是 Bean,什么是 `BeanDefinition`,以及 Spring 容器是如何管理和实例化 Bean 的。 2. **`BeanDefinition`的理解** + 我们需要了解什么是 `BeanDefinition`,它包含了哪些信息,例如 Bean 的类名、作用域、属性、初始化方法、销毁方法等。这是理解 `BeanDefinitionHolder` 的基础。 3. **Spring IOC 容器** + 我们需要了解 Spring 的 IOC(Inversion of Control)容器,包括如何配置容器、如何加载 `BeanDefinition`,以及如何从容器中获取 Bean 实例。 ### 二、基本描述 `BeanDefinitionHolder` 是 Spring Framework 中的一个类,用于持有一个 `BeanDefinition` 对象以及与之相关联的名称。它通常用于内部 Spring 操作,用于管理和操作 `BeanDefinition`。 ### 三、主要功能 1. **持有 `BeanDefinition` 对象** + `BeanDefinitionHolder` 主要用于持有一个 `BeanDefinition` 对象,它包含有关 Bean 的元数据信息,如类名、作用域、属性、初始化方法等。 2. **与名称相关联** + `BeanDefinitionHolder` 与一个 Bean 的名称相关联,以便可以轻松地标识和操作特定的 `BeanDefinition`。 3. **包装`BeanDefinition`** + 它包装了 `BeanDefinition` 以及与之关联的名称,将它们组合在一起,形成一个单元,有助于更好地管理和处理 `BeanDefinition`。 4. **提供额外的元数据** + 除了 `BeanDefinition` 和名称,`BeanDefinitionHolder` 还可以包含其他元数据,如 `BeanDefinition` 的原始来源(通常是一个 Resource 对象)以及 `BeanDefinition` 的描述信息。 5. **用于注册和管理 `BeanDefinition`** + `BeanDefinitionHolder` 可以与 `BeanDefinitionRegistry` 接口一起使用,用于在 Spring 容器中注册、修改和管理 `BeanDefinition`。 ### 四、接口源码 `BeanDefinitionHolder` 通常在 Spring 内部用于处理 `BeanDefinition` 注册和操作,提供了访问 `BeanDefinition`、Bean 名称和别名的方法。 ```java public class BeanDefinitionHolder implements BeanMetadataElement { // 封装的 BeanDefinition 对象 private final BeanDefinition beanDefinition; // Bean 的名称 private final String beanName; // 别名数组 @Nullable private final String[] aliases; /** * 创建一个新的 BeanDefinitionHolder。 * @param beanDefinition 要包装的 BeanDefinition * @param beanName Bean 的名称,如在 Bean 定义中指定 */ public BeanDefinitionHolder(BeanDefinition beanDefinition, String beanName) { this(beanDefinition, beanName, null); } /** * 创建一个新的 BeanDefinitionHolder。 * @param beanDefinition 要包装的 BeanDefinition * @param beanName Bean 的名称,如在 Bean 定义中指定 * @param aliases Bean 的别名,如果没有别名则为 null */ public BeanDefinitionHolder(BeanDefinition beanDefinition, String beanName, @Nullable String[] aliases) { Assert.notNull(beanDefinition, "BeanDefinition 必须不为 null"); Assert.notNull(beanName, "Bean 名称必须不为 null"); this.beanDefinition = beanDefinition; this.beanName = beanName; this.aliases = aliases; } /** * 复制构造函数:创建一个新的 BeanDefinitionHolder,其内容与给定的 BeanDefinitionHolder 实例相同。 * 注意:封装的 BeanDefinition 引用保持原样;不会进行深层复制。 * @param beanDefinitionHolder 要复制的 BeanDefinitionHolder 实例 */ public BeanDefinitionHolder(BeanDefinitionHolder beanDefinitionHolder) { Assert.notNull(beanDefinitionHolder, "BeanDefinitionHolder 必须不为 null"); this.beanDefinition = beanDefinitionHolder.getBeanDefinition(); this.beanName = beanDefinitionHolder.getBeanName(); this.aliases = beanDefinitionHolder.getAliases(); } /** * 返回封装的 BeanDefinition。 */ public BeanDefinition getBeanDefinition() { return this.beanDefinition; } /** * 返回 Bean 的主要名称,如在 Bean 定义中指定。 */ public String getBeanName() { return this.beanName; } /** * 返回 Bean 的别名数组,如在 Bean 定义中直接指定。 * @return 别名数组,如果没有别名则为 null */ @Nullable public String[] getAliases() { return this.aliases; } /** * 公开 Bean 定义的源对象。 * @see BeanDefinition#getSource() */ @Override @Nullable public Object getSource() { return this.beanDefinition.getSource(); } /** * 确定给定的候选名称是否与存储在该 Bean 定义中的 Bean 名称或别名匹配。 */ public boolean matchesName(@Nullable String candidateName) { return (candidateName != null && (candidateName.equals(this.beanName) || candidateName.equals(BeanFactoryUtils.transformedBeanName(this.beanName)) || ObjectUtils.containsElement(this.aliases, candidateName))); } // ... [部分代码省略以简化] } ``` ### 五、主要实现 `BeanDefinitionHolder` 不是一个接口,也不需要其他实现类。它是一个具体的类,用于包装 `BeanDefinition` 对象、Bean 名称和别名,以便更好地管理和操作 `BeanDefinition`。 ### 六、最佳实践 使用 `BeanDefinitionHolder` 和 Spring IOC 容器,通过创建一个 `BeanDefinition` 对象定义了名为 "`myBean`" 的 Bean,然后给它指定了别名 "`myBeanX`" 和 "`myBeanY`",并最终将这个 `BeanDefinition` 注册到容器中。随后,使用容器的 `getBean` 方法来检索这些 Bean,验证它们的正确注册和别名的设置。 ```java public class BeanDefinitionHolderDemo { public static void main(String[] args) { // 创建一个 DefaultListableBeanFactory,它是 BeanDefinitionRegistry 的一个实现 DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); // 创建一个新的 BeanDefinition 对象 GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); beanDefinition.setBeanClass(MyBean.class); // Bean名称 String beanName = "myBean"; // 设置别名(aliases) String[] aliases = {"myBeanX", "myBeanY"}; // 创建一个 BeanDefinitionHolder,将 BeanDefinition 与名称关联起来 BeanDefinitionHolder beanDefinitionHolder = new BeanDefinitionHolder(beanDefinition, beanName, aliases); // 使用 BeanDefinitionReaderUtils 注册 BeanDefinitionHolder BeanDefinitionReaderUtils.registerBeanDefinition(beanDefinitionHolder, beanFactory); System.out.println("myBean = " + beanFactory.getBean("myBean")); System.out.println("myBeanX = " + beanFactory.getBean("myBeanX")); System.out.println("myBeanY = " + beanFactory.getBean("myBeanY")); } } ``` `MyBean` 的Java类,代表了一个简单的Java Bean。 ```java public class MyBean { } ``` 运行结果发现,我们可以使多个名称都引用同一个Bean定义,最终获取相同的Bean实例。 ```java myBean = com.xcs.spring.bean.MyBean@5bcea91b myBeanX = com.xcs.spring.bean.MyBean@5bcea91b myBeanY = com.xcs.spring.bean.MyBean@5bcea91b ``` ### 七、与其他组件的关系 1. **`BeanDefinitionReaderUtils`** + Spring 提供了一个实用工具类 `BeanDefinitionReaderUtils`,用于帮助注册 `BeanDefinition`,它通常与 `BeanDefinitionHolder` 一起使用。`BeanDefinitionReaderUtils.registerBeanDefinition` 方法可用于将 `BeanDefinitionHolder` 注册到 `BeanDefinitionRegistry` 中。 2. **`BeanDefinitionParser` 和自定义解析器** + 在 Spring XML 配置文件中,`BeanDefinition` 可以通过自定义的 `BeanDefinitionParser` 或 `NamespaceHandler` 解析器来解析并注册到容器中。这些解析器通常使用 `BeanDefinitionHolder` 来包装 `BeanDefinition`,并通过解析器注册到容器中。 3. **组件扫描器** + Spring 提供了不同的组件扫描器,如 `ClassPathBeanDefinitionScanner` 和 `AnnotatedBeanDefinitionReader`,用于扫描类路径或带有注解的类,并将它们注册为 `BeanDefinition`。这些扫描器通常使用 `BeanDefinitionHolder` 来包装扫描到的 `BeanDefinition`,并注册到容器中。 4. **@Import 和相关机制** + 在 Spring 中,`@Import` 注解以及相关的 `ImportBeanDefinitionRegistrar` 和 `ImportSelector` 接口用于导入其他配置类的 `BeanDefinition`。在这个过程中,`BeanDefinitionHolder` 可能用于包装被导入的 `BeanDefinition`,并将其注册到容器中。 ### 八、常见问题 1. **如何使用`BeanDefinitionHolder`?** - `BeanDefinitionHolder` 通常在Spring内部用于处理`BeanDefinition`的注册和操作,但应用开发人员通常不需要直接与它交互(除非你是一个框架开发人员)。它可以在自定义`BeanDefinition`解析器、组件扫描、@Import导入等Spring配置的过程中被自动使用。 2. **`BeanDefinitionHolder`和`BeanDefinition`之间的关系是什么?** - `BeanDefinitionHolder` 是一个包装器,它包含一个`BeanDefinition`对象以及相关的名称和别名。它用于将`BeanDefinition`与名称关联起来,以便更好地管理和操作`BeanDefinition`。 3. **如何为`BeanDefinition`设置别名?** - 别名(aliases)是通过在`BeanDefinitionHolder`构造函数中传递字符串数组来设置的。在注册`BeanDefinitionHolder`时,别名将与`BeanDefinition`一起注册,从而使不同的名称都引用同一个`BeanDefinition`。 4. **是否可以在运行时更改Bean的别名?** - 通常情况下,一旦`BeanDefinition`注册到容器中,别名是固定的,不能在运行时更改。如果需要更改别名,通常需要重新注册`BeanDefinition`,使用不同的别名。 5. **如何判断两个Bean是否有相同的别名?** - 可以使用`BeanDefinitionHolder`的`matchesName`方法来判断给定的候选名称是否与Bean的名称或别名匹配。这个方法会检查给定的名称是否与Bean名称或别名中的任何一个匹配。 6. **别名的作用是什么?** - 别名提供了更多的灵活性,使您可以使用不同的名称来引用同一个Bean。这对于允许不同的组件、模块或配置使用不同的名称来访问Bean定义非常有用。 7. **如何处理具有相同名称的多个`BeanDefinition`?** - 如果多个`BeanDefinition`具有相同的名称,通常情况下,只有最后一个注册的`BeanDefinition`会生效。这可能会导致Bean覆盖的问题,因此在配置时要注意Bean名称的唯一性。 ================================================ FILE: spring-beans/spring-bean-beanDefinitionHolder/pom.xml ================================================ spring-beans com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-bean-beanDefinitionHolder ================================================ FILE: spring-beans/spring-bean-beanDefinitionHolder/src/main/java/com/xcs/spring/BeanDefinitionHolderDemo.java ================================================ package com.xcs.spring; import com.xcs.spring.bean.MyBean; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.GenericBeanDefinition; import java.util.Arrays; /** * @author xcs * @date 2023年11月03日 17时20分 **/ public class BeanDefinitionHolderDemo { public static void main(String[] args) { // 创建一个 DefaultListableBeanFactory,它是 BeanDefinitionRegistry 的一个实现 DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); // 创建一个新的 BeanDefinition 对象 GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); beanDefinition.setBeanClass(MyBean.class); // Bean名称 String beanName = "myBean"; // 设置别名(aliases) String[] aliases = {"myBeanX", "myBeanY"}; // 创建一个 BeanDefinitionHolder,将 BeanDefinition 与名称关联起来 BeanDefinitionHolder beanDefinitionHolder = new BeanDefinitionHolder(beanDefinition, beanName, aliases); // 使用 BeanDefinitionReaderUtils 注册 BeanDefinitionHolder BeanDefinitionReaderUtils.registerBeanDefinition(beanDefinitionHolder, beanFactory); System.out.println("myBean = " + beanFactory.getBean("myBean")); System.out.println("myBeanX = " + beanFactory.getBean("myBeanX")); System.out.println("myBeanY = " + beanFactory.getBean("myBeanY")); } } ================================================ FILE: spring-beans/spring-bean-beanDefinitionHolder/src/main/java/com/xcs/spring/bean/MyBean.java ================================================ package com.xcs.spring.bean; /** * @author xcs * @date 2023年11月01日 15时41分 **/ public class MyBean { } ================================================ FILE: spring-beans/spring-bean-beanDefinitionRegistry/README.md ================================================ ## BeanDefinitionRegistry - [BeanDefinitionRegistry](#beandefinitionregistry) - [一、知识储备](#一知识储备) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、接口源码](#四接口源码) - [五、主要实现](#五主要实现) - [六、最佳实践](#六最佳实践) - [七、与其他组件的关系](#七与其他组件的关系) - [八、常见问题](#八常见问题) ### 一、知识储备 1. **Bean定义:** - 理解什么是Bean定义,包括Bean的名称、类名、作用域、依赖关系等。 - 了解如何使用XML配置、注解或Java配置来定义Bean。 - 掌握Bean定义中的属性设置、构造函数注入、属性注入等相关概念。 ### 二、基本描述 `BeanDefinitionRegistry`接口是Spring框架中的一个关键接口之一,用于管理和注册Bean定义(Bean definitions)。Bean定义是描述Spring容器中的对象(Bean)的元数据,包括Bean的类名、依赖关系、作用域等信息。 ### 三、主要功能 1. **注册Bean定义** + 通过 `BeanDefinitionRegistry` 接口,我们可以向Spring容器注册Bean定义。这意味着我们可以将Bean的元数据信息(如类名、作用域、属性等)添加到容器中,以便容器能够根据这些定义实例化和管理Bean对象。 2. **移除Bean定义** + 我们可以使用接口提供的方法来移除不再需要的Bean定义。这可以有助于释放资源和减轻容器的负担。通常,这是在运行时不再需要某些Bean定义时使用的功能。 3. **检查Bean定义是否存在** + 我们可以使用接口提供的方法来检查容器中是否存在特定名称的Bean定义。这可以帮助我们避免重复注册相同名称的Bean。 4. **获取Bean定义信息** + 通过接口,我们可以获取已注册的Bean定义的详细信息,包括Bean的类名、作用域、属性值等。这对于在运行时查看或修改Bean定义很有用。 5. **批量操作** + 除了单个Bean定义的操作,`BeanDefinitionRegistry` 还支持批量注册和移除操作。这对于在一次操作中注册多个Bean定义非常有用,以减少重复的操作。 6. **层次性注册** + Spring容器可以是层次性的,其中可以包含多个容器层次结构。`BeanDefinitionRegistry` 接口支持在这些容器层次结构中注册Bean定义。这对于分模块的应用程序非常有用。 7. **自定义扩展** + Spring框架允许通过自定义实现 `BeanDefinitionRegistry` 接口来创建自定义的Bean定义注册器,以满足特定的需求。这使得Spring框架具有高度的可扩展性。 ### 四、接口源码 `BeanDefinitionRegistry` 接口,用于管理和注册Bean定义。它允许我们注册、移除、检索Bean定义以及执行其他与Bean定义相关的操作。这个接口用于实现Spring的IOC容器,允许我们在应用程序中注册并管理Bean定义,使其能够进行依赖注入。 ```java /** * 用于持有Bean定义(例如RootBeanDefinition和ChildBeanDefinition实例)的注册表的接口。 * 通常由与AbstractBeanDefinition层次结构内部一起工作的Bean工厂实现。 * * 这是Spring的bean工厂包中唯一封装注册bean定义的接口。 * 标准的BeanFactory接口只覆盖对完全配置的工厂实例的访问。 * * Spring的Bean定义读取器希望在这个接口的实现上工作。 * Spring核心中已知的实现者包括DefaultListableBeanFactory和GenericApplicationContext。 * * @author Juergen Hoeller * @since 26.11.2003 * @see org.springframework.beans.factory.config.BeanDefinition * @see AbstractBeanDefinition * @see RootBeanDefinition * @see ChildBeanDefinition * @see DefaultListableBeanFactory * @see org.springframework.context.support.GenericApplicationContext * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader * @see PropertiesBeanDefinitionReader */ public interface BeanDefinitionRegistry extends AliasRegistry { /** * 向此注册表注册一个新的Bean定义。 * 必须支持RootBeanDefinition和ChildBeanDefinition。 * * @param beanName 要注册的Bean实例的名称 * @param beanDefinition 要注册的Bean实例的定义 * @throws BeanDefinitionStoreException 如果BeanDefinition无效 * @throws BeanDefinitionOverrideException 如果已经存在指定名称的BeanDefinition并且不允许覆盖 * @see GenericBeanDefinition * @see RootBeanDefinition * @see ChildBeanDefinition */ void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException; /** * 移除给定名称的BeanDefinition。 * * @param beanName 要注册的Bean实例的名称 * @throws NoSuchBeanDefinitionException 如果没有这样的Bean定义 */ void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException; /** * 返回给定Bean名称的BeanDefinition。 * * @param beanName 要查找定义的Bean名称 * @return 给定名称的BeanDefinition(永远不为{@code null}) * @throws NoSuchBeanDefinitionException 如果没有这样的Bean定义 */ BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException; /** * 检查此注册表是否包含给定名称的Bean定义。 * * @param beanName 要查找的Bean的名称 * @return 如果此注册表包含给定名称的Bean定义,则返回true */ boolean containsBeanDefinition(String beanName); /** * 返回此注册表中定义的所有Bean的名称。 * * @return 此注册表中定义的所有Bean的名称数组,如果没有定义则返回空数组 */ String[] getBeanDefinitionNames(); /** * 返回注册表中定义的Bean数量。 * * @return 注册表中定义的Bean数量 */ int getBeanDefinitionCount(); /** * 确定给定的Bean名称是否已在此注册表中使用,即是否已在此名称下注册了本地Bean或别名。 * * @param beanName 要检查的名称 * @return 给定的Bean名称是否已经在使用 */ boolean isBeanNameInUse(String beanName); } ``` ### 五、主要实现 1. **`DefaultListableBeanFactory`** + 这是Spring框架中最常用的`BeanDefinitionRegistry` 接口的实现之一。它是Spring的核心容器,负责管理Bean定义,实例化Bean对象以及提供依赖注入功能。通常,我们可以在XML配置文件中配置`DefaultListableBeanFactory`来定义和注册Bean。 2. **`GenericApplicationContext`** + `GenericApplicationContext` 是Spring的应用上下文的实现,它实现了 `BeanDefinitionRegistry` 接口。这个上下文是一个通用的应用程序上下文,可以用于在程序中注册和管理Bean定义。 3. **`SimpleBeanDefinitionRegistry`** + `SimpleBeanDefinitionRegistry`提供了基本的`BeanDefinition`注册和管理功能,但它相对较简单,不支持高级特性,如XML配置解析等。它通常用于快速原型开发、单元测试和一些较为简单的应用程序中,以减少配置和复杂性。 ~~~mermaid classDiagram direction BT class AliasRegistry { <> } class BeanDefinitionRegistry { <> } class DefaultListableBeanFactory { } class GenericApplicationContext { } class SimpleBeanDefinitionRegistry { } BeanDefinitionRegistry ..|> AliasRegistry DefaultListableBeanFactory --|> BeanDefinitionRegistry GenericApplicationContext --|> BeanDefinitionRegistry SimpleBeanDefinitionRegistry --|> BeanDefinitionRegistry ~~~ ### 六、最佳实践 我们使用Spring的`DefaultListableBeanFactory`和`BeanDefinitionRegistry`接口来注册、管理和操作Bean定义的过程,包括创建Bean定义、注册到容器、获取、检查是否存在、获取所有定义的名称和数量、检查名称是否已使用,以及移除Bean定义。这展示了Spring的IOC容器的核心功能,允许应用程序在运行时动态配置和管理Bean。 ```java public class BeanDefinitionRegistryDemo { public static void main(String[] args) { // 创建一个BeanFactory,它是BeanDefinitionRegistry DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); // 创建一个Bean定义 GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); beanDefinition.setBeanClass(MyBean.class); // 注册Bean定义到BeanFactory beanFactory.registerBeanDefinition("myBean", beanDefinition); // 获取BeanDefinition BeanDefinition retrievedBeanDefinition = beanFactory.getBeanDefinition("myBean"); System.out.println("Bean定义的类名 = " + retrievedBeanDefinition.getBeanClassName()); // 检查Bean定义是否存在 boolean containsBeanDefinition = beanFactory.containsBeanDefinition("myBean"); System.out.println("Bean定义是否包含(myBean) = " + containsBeanDefinition); // 获取所有Bean定义的名称 String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames(); System.out.println("Bean定义的名称 = " + String.join(", ", beanDefinitionNames)); // 获取Bean定义的数量 int beanDefinitionCount = beanFactory.getBeanDefinitionCount(); System.out.println("Bean定义的数量 = " + beanDefinitionCount); // 检查Bean名称是否已被使用 boolean isBeanNameInUse = beanFactory.isBeanNameInUse("myBean"); System.out.println("Bean名称(myBean)是否被使用 = " + isBeanNameInUse); // 移除Bean定义 beanFactory.removeBeanDefinition("myBean"); System.out.println("Bean定义已被移除(myBean)"); } } ``` `MyBean` 的Java类,代表了一个简单的Java Bean。 ```java public class MyBean { } ``` 运行结果发现,我们的各种操作已成功执行,包括注册、查询、操作和移除Bean定义。这展示了Spring的IoC容器的核心功能,使我们可以在运行时动态地管理Bean定义。 ```java Bean定义的类名 = com.xcs.spring.bean.MyBean Bean定义是否包含(myBean) = true Bean定义的名称 = myBean Bean定义的数量 = 1 Bean名称(myBean)是否被使用 = true Bean定义已被移除(myBean) ``` ### 七、与其他组件的关系 1. **`DefaultListableBeanFactory`** + `BeanDefinitionRegistry` 是`DefaultListableBeanFactory`的一个主要实现,它负责注册和管理Bean定义(`BeanDefinition`)。 `BeanFactory`是Spring的IOC容器,用于实例化和管理Bean对象。 `DefaultListableBeanFactory`可以通过 `BeanDefinitionRegistry` 来注册和获取Bean定义,然后使用这些定义来创建Bean实例。 2. **`GenericApplicationContext`** + `GenericApplicationContext` 是`DefaultListableBeanFactory`的更高级别扩展,它不仅实现了 `DefaultListableBeanFactory` 的功能,还提供了应用程序级别的服务,如国际化、事件发布、资源加载等。 `DefaultListableBeanFactory` 通常使用 `BeanDefinitionRegistry` 来注册和管理Bean定义,以便在应用程序上下文中配置和管理Bean。 3. **`BeanDefinition`** + `BeanDefinitionRegistry` 主要与Bean定义(`BeanDefinition`)相关联。它用于注册、查询和管理Bean定义,包括Bean的类名、作用域、依赖关系等元数据信息。 `BeanFactory` 和 `ApplicationContext` 使用这些Bean定义来创建和管理Bean实例。 4. **`BeanPostProcessor`** + `BeanPostProcessor` 是一个拦截Bean实例化的接口,`DefaultListableBeanFactory`和 `GenericApplicationContext` 中都可以使用。 `BeanDefinitionRegistry` 可以与 `BeanPostProcessor` 结合使用,以在Bean的实例化前后执行定制的处理逻辑。这允许我们在Bean创建过程中干预并定制Bean的初始化和销毁过程。 ### 八、常见问题 1. **什么是 `BeanDefinitionRegistry`?** - `BeanDefinitionRegistry` 是Spring框架中的一个接口,它用于注册和管理Bean定义。它是IOC容器的核心组成部分,用于将应用程序中的Bean定义注册到容器中,以便后续实例化和管理。 2. **`BeanDefinitionRegistry` 与 `DefaultListableBeanFactory` 之间的关系是什么?** - `DefaultListableBeanFactory` 是Spring的IOC容器,而 `BeanDefinitionRegistry` 是 `DefaultListableBeanFactory` 的一个具体实现。它允许 `DefaultListableBeanFactory` 在内部管理和访问Bean定义。 `DefaultListableBeanFactory` 实际上是一个包含了 `BeanDefinitionRegistry` 功能的IOC容器。 3. **`BeanDefinition` 和 `BeanDefinitionRegistry` 之间的关系是什么?** - `BeanDefinition` 是Bean定义的元数据,它描述了如何创建和配置Bean。 `BeanDefinitionRegistry` 用于注册和管理这些 `BeanDefinition`。 `BeanDefinitionRegistry` 允许我们注册Bean定义并在容器中实例化这些Bean。 4. **如何使用 `BeanDefinitionRegistry` 注册Bean定义?** - 我们可以创建一个实现了 `BeanDefinitionRegistry` 接口的容器(如 `DefaultListableBeanFactory` 或 `GenericApplicationContext`),然后使用其 `registerBeanDefinition` 方法来注册Bean定义。通常,我们需要提供Bean的名称和相应的 `BeanDefinition`。 5. **可以注册多个相同名称的Bean吗?** - 默认情况下,我们不能注册多个相同名称的Bean。如果尝试注册相同名称的Bean,通常会抛出 `BeanDefinitionOverrideException` 异常。但有些情况下,我们可以允许覆盖现有的Bean定义。 6. **如何检查是否存在特定名称的Bean定义?** - 我们可以使用 `containsBeanDefinition` 方法来检查是否存在特定名称的Bean定义。这个方法将返回一个布尔值,指示是否存在该Bean定义。 7. **如何获取已注册的Bean定义的名称和数量?** - 使用 `getBeanDefinitionNames` 方法可以获取所有已注册的Bean定义的名称数组,而使用 `getBeanDefinitionCount` 方法可以获取已注册的Bean定义的数量。 8. **如何移除已注册的Bean定义?** - 我们可以使用 `removeBeanDefinition` 方法来移除已注册的Bean定义。这将从容器中删除该Bean定义,使其不再可用。 9. **`BeanDefinitionRegistry`与`BeanPostProcessor` 之间有什么关系?** - `BeanPostProcessor` 是一个接口,用于在Bean实例化和初始化过程中进行处理。 `BeanDefinitionRegistry` 可以与 `BeanPostProcessor` 结合使用,以在Bean定义的创建和初始化过程中执行自定义逻辑。这允许我们在Bean创建时进行定制操作。 ================================================ FILE: spring-beans/spring-bean-beanDefinitionRegistry/pom.xml ================================================ spring-beans com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-bean-beanDefinitionRegistry ================================================ FILE: spring-beans/spring-bean-beanDefinitionRegistry/src/main/java/com/xcs/spring/BeanDefinitionRegistryDemo.java ================================================ package com.xcs.spring; import com.xcs.spring.bean.MyBean; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.GenericBeanDefinition; /** * @author xcs * @date 2023年11月04日 11时28分 **/ public class BeanDefinitionRegistryDemo { public static void main(String[] args) { // 创建一个BeanFactory,它是BeanDefinitionRegistry DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); // 创建一个Bean定义 GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); beanDefinition.setBeanClass(MyBean.class); // 注册Bean定义到BeanFactory beanFactory.registerBeanDefinition("myBean", beanDefinition); // 获取BeanDefinition BeanDefinition retrievedBeanDefinition = beanFactory.getBeanDefinition("myBean"); System.out.println("Bean定义的类名 = " + retrievedBeanDefinition.getBeanClassName()); // 检查Bean定义是否存在 boolean containsBeanDefinition = beanFactory.containsBeanDefinition("myBean"); System.out.println("Bean定义是否包含(myBean) = " + containsBeanDefinition); // 获取所有Bean定义的名称 String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames(); System.out.println("Bean定义的名称 = " + String.join(", ", beanDefinitionNames)); // 获取Bean定义的数量 int beanDefinitionCount = beanFactory.getBeanDefinitionCount(); System.out.println("Bean定义的数量 = " + beanDefinitionCount); // 检查Bean名称是否已被使用 boolean isBeanNameInUse = beanFactory.isBeanNameInUse("myBean"); System.out.println("Bean名称(myBean)是否被使用 = " + isBeanNameInUse); // 移除Bean定义 beanFactory.removeBeanDefinition("myBean"); System.out.println("Bean定义已被移除(myBean)"); } } ================================================ FILE: spring-beans/spring-bean-beanDefinitionRegistry/src/main/java/com/xcs/spring/bean/MyBean.java ================================================ package com.xcs.spring.bean; /** * @author xcs * @date 2023年11月01日 15时41分 **/ public class MyBean { } ================================================ FILE: spring-beans/spring-bean-classPathBeanDefinitionScanner/README.md ================================================ ## ClassPathBeanDefinitionScanner - [ClassPathBeanDefinitionScanner](#classpathbeandefinitionscanner) - [一、知识储备](#一知识储备) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、最佳实践](#四最佳实践) - [五、时序图](#五时序图) - [六、源码分析](#六源码分析) - [注册默认过滤器阶段](#注册默认过滤器阶段) - [扫描Bean定义阶段](#扫描bean定义阶段) - [七、与其他组件的关系](#七与其他组件的关系) - [八、常见问题](#八常见问题) ### 一、知识储备 5. **`Resource`** - `Resource` 代表一个资源,可以是文件、类路径上的文件、URL 等。它提供了对资源的抽象和访问方法。 - [点击查看Resource接口](https://github.com/xuchengsheng/spring-reading/tree/master/spring-resources/spring-resource) 2. **`ResourceLoader`** - `ResourceLoader` 可以用于获取资源,这些资源可以是文件、类路径上的资源、URL、甚至是远程资源。它提供了一种统一的方式来加载资源,无论这些资源位于何处。 - [点击查看ResourceLoader接口](https://github.com/xuchengsheng/spring-reading/tree/master/spring-resources/spring-resource-resourceLoader) 3. **`MetadataReader`** + `MetadataReader` 是 Spring 框架中用于读取类文件和类元数据的接口,它提供了有关类的信息,包括类名、注解、父类信息等。在 `ClassPathBeanDefinitionScanner` 中,`MetadataReader` 被用于扫描类路径,识别候选的 Bean 类,并读取类的元数据以创建相应的 `BeanDefinition`。 + [点击查看MetadataReader接口](https://github.com/xuchengsheng/spring-reading/tree/master/spring-metadata/spring-metadata-metadataReader) 4. **`AnnotationMetadata`** - `AnnotationMetadata` 是 Spring 框架中用于处理类上的注解信息的接口,它提供了对类上注解信息的访问和操作方法。 `AnnotatedBeanDefinitionReader` 利用 `AnnotationMetadata` 解析类上的注解信息,并将其转化为 Spring 的 BeanDefinition。 - [点击查看AnnotationMetadata接口](https://github.com/xuchengsheng/spring-reading/tree/master/spring-metadata/spring-metadata-annotationMetadata) 5. **`BeanDefinition`** - `BeanDefinition` 是 Spring 中描述和管理 Bean 配置的核心概念,它包括了有关 Bean 的信息,如类名、作用域、依赖关系、初始化方法等,而 `AnnotatedBeanDefinitionReader` 的主要任务之一是将使用注解配置的类转化为 `BeanDefinition` 并注册到 Spring 容器中。 - [点击查看BeanDefinition接口](https://github.com/xuchengsheng/spring-reading/tree/master/spring-beans/spring-bean-beanDefinition) ### 二、基本描述 `ClassPathBeanDefinitionScanner` 类,用于在类路径上扫描指定包及其子包中的类,识别符合条件的类,并将其注册为 Spring Bean 的定义,从而实现组件扫描和自动装配,使我们能够方便地管理和配置应用程序中的 Bean。它允许我们定义过滤条件,以确定哪些类应被注册为 Bean,以及配合自动装配实现依赖注入,提高了应用程序的可维护性和扩展性。 ### 三、主要功能 1. **包扫描** + 扫描类路径上的指定包及其子包中的类文件,用于寻找潜在的 Spring Bean 候选类。 2. **`BeanDefinition`注册** + 将找到的符合条件的类注册为 Spring Bean 的定义(`BeanDefinition`)。这使得这些类的实例可以由 Spring 容器进行管理。 3. **自动装配** + 自动装配机制结合使用,以自动识别并注入依赖的 Bean。这降低了手动配置 Bean 依赖的需求,提高了应用程序的可维护性。 4. **注解识别** + 扫描器能够识别带有特定注解的类,例如 `@Component`、`@Service`、`@Repository` 等,然后将这些类注册为 Spring Bean。 5. **过滤条件** + 配置过滤条件,以决定哪些类应该被注册为 Bean。过滤条件可以是注解类型、类名规则、类实现的接口等,从而筛选出符合条件的类。 6. **Bean 名称生成** + 自动生成 Bean 的名称,通常是类名的首字母小写形式,但也可以通过配置进行自定义。 7. **自定义配置** + 通过配置 `ClassPathBeanDefinitionScanner` 的属性来自定义其行为,例如指定要扫描的包、设置过滤条件、配置 Bean 名称生成规则等。 ### 四、最佳实践 创建 `DefaultListableBeanFactory` 和关联的 `ClassPathBeanDefinitionScanner` 对象,然后使用扫描器的 `scan` 方法扫描指定包中的类,将这些类注册为 Spring Bean,最终能够通过工厂获取并使用这些 Bean。 ```java public class ClassPathBeanDefinitionScannerDemo { public static void main(String[] args) { // 创建一个 AnnotationConfigApplicationContext DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); // 创建 ClassPathBeanDefinitionScanner 并将其关联到容器 ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(factory); // 使用 ClassPathBeanDefinitionScanner的scan方法扫描Bean对象 scanner.scan("com.xcs.spring"); System.out.println("MyController = " + factory.getBean(MyController.class)); System.out.println("MyService = " + factory.getBean(MyService.class)); System.out.println("MyRepository = " + factory.getBean(MyRepository.class)); } } ``` `@Controller`、`@Service` 和 `@Repository`。这些注解通常用于标记不同层次的组件,以便 Spring 容器可以自动识别和注册它们为 Spring Bean。 ```java @Controller public class MyController { } @Service public class MyService { } @Repository public class MyRepository { } ``` 运行结果发现, `ClassPathBeanDefinitionScanner` 成功扫描了指定包中的类,并将这些类注册为 Spring Bean。 ```java MyController = com.xcs.spring.controller.MyController@1e802ef9 MyService = com.xcs.spring.service.MyService@2b6faea6 MyRepository = com.xcs.spring.repository.MyRepository@778d1062 ``` ### 五、时序图 ~~~mermaid sequenceDiagram Title: ClassPathBeanDefinitionScanner时序图 par 注册默认过滤器阶段 ClassPathBeanDefinitionScannerDemo->>ClassPathBeanDefinitionScanner:new ClassPathBeanDefinitionScanner(registry) note right of ClassPathBeanDefinitionScannerDemo: 创建 ClassPathBeanDefinitionScanner 实例,并关联到注册表 ClassPathBeanDefinitionScanner->>ClassPathBeanDefinitionScanner:ClassPathBeanDefinitionScanner(registry,useDefaultFilters) note over ClassPathBeanDefinitionScanner: 创建 ClassPathBeanDefinitionScanner 实例,启用默认过滤器 ClassPathBeanDefinitionScanner->>ClassPathBeanDefinitionScanner:ClassPathBeanDefinitionScanner(registry,useDefaultFilters,environment) note over ClassPathBeanDefinitionScanner: 创建 ClassPathBeanDefinitionScanner 实例,设置环境信息 ClassPathBeanDefinitionScanner->>ClassPathBeanDefinitionScanner:ClassPathBeanDefinitionScanner(registry,useDefaultFilters,environment,resourceLoader) note over ClassPathBeanDefinitionScanner: 创建 ClassPathBeanDefinitionScanner 实例,设置资源加载器 ClassPathBeanDefinitionScanner->>ClassPathBeanDefinitionScanner:registerDefaultFilters() note over ClassPathBeanDefinitionScanner: 注册默认的组件过滤器 ClassPathBeanDefinitionScanner->>ClassPathBeanDefinitionScannerDemo:返回scanner note over ClassPathBeanDefinitionScanner,ClassPathBeanDefinitionScannerDemo: 返回 ClassPathBeanDefinitionScanner 实例 end par 扫描Bean定义阶段 ClassPathBeanDefinitionScannerDemo->>ClassPathBeanDefinitionScanner:scan(basePackages) note over ClassPathBeanDefinitionScannerDemo,ClassPathBeanDefinitionScanner: 执行组件扫描操作 ClassPathBeanDefinitionScanner->>ClassPathBeanDefinitionScanner:doScan(basePackages) note over ClassPathBeanDefinitionScanner: 执行组件扫描操作 ClassPathBeanDefinitionScanner->>ClassPathScanningCandidateComponentProvider:findCandidateComponents(basePackage) note over ClassPathScanningCandidateComponentProvider,ClassPathBeanDefinitionScanner: 查找符合条件的候选组件 ClassPathScanningCandidateComponentProvider->>ClassPathScanningCandidateComponentProvider:scanCandidateComponents(basePackage) note over ClassPathScanningCandidateComponentProvider: 扫描候选组件 ClassPathScanningCandidateComponentProvider->>ClassPathScanningCandidateComponentProvider:isCandidateComponent(metadataReader) note over ClassPathScanningCandidateComponentProvider: 判断是否是候选组件 ClassPathScanningCandidateComponentProvider->>AbstractTypeHierarchyTraversingFilter:match(metadataReader,metadataReaderFactory) note over AbstractTypeHierarchyTraversingFilter,ClassPathScanningCandidateComponentProvider: 使用过滤器匹配 AbstractTypeHierarchyTraversingFilter->>AnnotationTypeFilter:matchSelf(metadataReader) note left of AnnotationTypeFilter: 调用 matchSelf(metadataReader) AnnotationTypeFilter->>AbstractTypeHierarchyTraversingFilter:返回是否匹配 AbstractTypeHierarchyTraversingFilter->>ClassPathScanningCandidateComponentProvider:返回是否匹配 ClassPathScanningCandidateComponentProvider->>ClassPathScanningCandidateComponentProvider:candidates.add(sbd) note over ClassPathScanningCandidateComponentProvider: 如果匹配,则将候选组件加入列表 ClassPathScanningCandidateComponentProvider->>ClassPathBeanDefinitionScanner:返回扫描到的Bean定义 ClassPathBeanDefinitionScanner->>AnnotationConfigUtils:processCommonDefinitionAnnotations(abd) Note over ClassPathBeanDefinitionScanner, AnnotationConfigUtils: 处理常见的Bean定义注解 AnnotationConfigUtils->>AnnotationConfigUtils:processCommonDefinitionAnnotations(abd,metadata) Note over AnnotationConfigUtils: 处理 Bean 的元数据信息 ClassPathBeanDefinitionScanner->>ClassPathBeanDefinitionScanner:checkCandidate(beanName, candidate) note over ClassPathBeanDefinitionScanner: 检查候选组件 ClassPathBeanDefinitionScanner->>ClassPathBeanDefinitionScanner:registerBeanDefinition(definitionHolder,registry) note over ClassPathBeanDefinitionScanner: 注册 Bean 定义 ClassPathBeanDefinitionScanner->>AnnotationConfigUtils:registerBeanDefinition(definitionHolder,registry) note over ClassPathBeanDefinitionScanner,AnnotationConfigUtils: 注册 Bean 定义 ClassPathBeanDefinitionScanner->>AnnotationConfigUtils:registerAnnotationConfigProcessors(registry) note over ClassPathBeanDefinitionScanner,AnnotationConfigUtils: 启用注解驱动的配置 AnnotationConfigUtils->>AnnotationConfigUtils:registerAnnotationConfigProcessors(registry,source) note over AnnotationConfigUtils: 启用注解驱动的配置 end ~~~ ### 六、源码分析 #### 注册默认过滤器阶段 在`org.springframework.context.annotation.ClassPathBeanDefinitionScanner#ClassPathBeanDefinitionScanner(registry)`方法中,实际上是通过委派给另一个构造函数来创建,并且在第二个参数设置为 `true` ,表示会包括注册注解配置处理器(`Annotation Config Processors`),这通常用于支持注解配置,包括 `@Component`、`@Service`、`@Repository` 等注解。 ```java public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) { this(registry, true); } ``` 在`org.springframework.context.annotation.ClassPathBeanDefinitionScanner#ClassPathBeanDefinitionScanner(registry,useDefaultFilters)`方法中,实际上是通过委派给另一个构造函数来创建,并且获取了上下文的环境变量进行传递。 ```java public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) { this(registry, useDefaultFilters, getOrCreateEnvironment(registry)); } ``` 在`org.springframework.context.annotation.ClassPathBeanDefinitionScanner#ClassPathBeanDefinitionScanner(registry,useDefaultFilters,environment)`方法中,实际上是通过委派给另一个构造函数来创建,最后一个参数是 `ResourceLoader`,如果 `BeanDefinitionRegistry` 也是 `ResourceLoader` 的实例,那么它会将 `ResourceLoader` 传递给后面的构造函数。 ```java public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, Environment environment) { this(registry, useDefaultFilters, environment, (registry instanceof ResourceLoader ? (ResourceLoader) registry : null)); } ``` 在`org.springframework.context.annotation.ClassPathBeanDefinitionScanner#ClassPathBeanDefinitionScanner(registry,useDefaultFilters,environment,resourceLoader)`方法中,我们重点关注`registerDefaultFilters()`方法,此方法主要是注册默认的过滤器,这些过滤器用于扫描默认的注解类,如 `@Component`、`@Service`、`@Repository` 等。 ```java public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, Environment environment, @Nullable ResourceLoader resourceLoader) { // 确保 BeanDefinitionRegistry 不为空 Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); this.registry = registry; // 如果需要使用默认过滤器,注册默认过滤器 if (useDefaultFilters) { registerDefaultFilters(); } // 设置环境信息 setEnvironment(environment); // 设置资源加载器(ResourceLoader) setResourceLoader(resourceLoader); } ``` 在`org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#registerDefaultFilters`方法中, 主要目的就是注册`@Component` 注解,用于扫描和识别标记为组件的类,同时还提供了对一些 Java EE 规范中的注解的支持。 ```java protected void registerDefaultFilters() { // 向 includeFilters 集合中添加默认的过滤器 this.includeFilters.add(new AnnotationTypeFilter(Component.class)); ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader(); // 尝试注册 JSR-250 'javax.annotation.ManagedBean' 过滤器(如果可用) try { this.includeFilters.add(new AnnotationTypeFilter( ((Class) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false)); logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning"); } catch (ClassNotFoundException ex) { // 如果 JSR-250 1.1 API(包括在 Java EE 6 中)不可用,简单地跳过。 } // 尝试注册 JSR-330 'javax.inject.Named' 过滤器(如果可用) try { this.includeFilters.add(new AnnotationTypeFilter( ((Class) ClassUtils.forName("javax.inject.Named", cl)), false)); logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning"); } catch (ClassNotFoundException ex) { // 如果 JSR-330 API 不可用,简单地跳过。 } } ``` #### 扫描Bean定义阶段 在`org.springframework.context.annotation.ClassPathBeanDefinitionScanner#scan`方法中,实际是调用 `doScan` 方法来扫描指定的包,查找符合条件的类,并将它们注册为 `BeanDefinition`,另外还计算新的 `BeanDefinition` 计数与扫描前的计数之差。 ```java public int scan(String... basePackages) { // 记录扫描开始时的 BeanDefinition 数量 int beanCountAtScanStart = this.registry.getBeanDefinitionCount(); // 步骤1: 执行扫描操作 doScan(basePackages); // 步骤2: 注册注解配置处理器(如果需要的话) if (this.includeAnnotationConfig) { AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); } // 返回扫描后的 BeanDefinition 数量与扫描开始前的数量的差,表示扫描期间注册的新 BeanDefinition 数量 return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart); } ``` > [`org.springframework.context.annotation.ClassPathBeanDefinitionScanner#scan`步骤1] 在`org.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScan`方法中,主要负责找到符合条件的类,处理它们,并注册为 Spring Bean。 ```java protected Set doScan(String... basePackages) { Assert.notEmpty(basePackages, "At least one base package must be specified"); Set beanDefinitions = new LinkedHashSet<>(); for (String basePackage : basePackages) { // 步骤1: 查找符合条件的 BeanDefinition 候选类 Set candidates = findCandidateComponents(basePackage); for (BeanDefinition candidate : candidates) { // 步骤2: 解析 Bean 的作用域信息 ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); candidate.setScope(scopeMetadata.getScopeName()); // 步骤3: 生成 Bean 的名称 String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); // 步骤4: 对 BeanDefinition 进行进一步处理 if (candidate instanceof AbstractBeanDefinition) { postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); } // 步骤5: 处理通用的注解 if (candidate instanceof AnnotatedBeanDefinition) { AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); } // 步骤6: 注册BeanDefinition if (checkCandidate(beanName, candidate)) { // 创建 BeanDefinitionHolder 并注册为 BeanDefinition BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); // 处理 Bean 的作用域代理模式 definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); beanDefinitions.add(definitionHolder); // 步骤6.1 将 BeanDefinition 注册到 BeanDefinitionRegistry 中 registerBeanDefinition(definitionHolder, this.registry); } } } return beanDefinitions; } ``` > [`org.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScan`步骤1] 在`org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#findCandidateComponents`方法中,先是走组件索引中获取候选 `BeanDefinition` ,还是执行标准的包扫描操作。 ```java public Set findCandidateComponents(String basePackage) { if (this.componentsIndex != null && indexSupportsIncludeFilters()) { // 如果存在组件索引并且索引支持包含过滤器 return addCandidateComponentsFromIndex(this.componentsIndex, basePackage); } else { // 否则,执行标准的扫描操作 return scanCandidateComponents(basePackage); } } ``` 在`org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#scanCandidateComponents`方法中,扫描指定包路径下的类,并找到候选的 `BeanDefinition`。它包含了文件扫描、元数据读取和过滤候选类的逻辑。 ```java private Set scanCandidateComponents(String basePackage) { // 创建一个用于存储候选 BeanDefinition 的集合 Set candidates = new LinkedHashSet<>(); try { // 构建包搜索路径,使用类路径前缀和解析的基本包路径 String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + '/' + this.resourcePattern; // 获取资源模式解析器的资源数组 Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath); // 检查日志级别是否开启跟踪和调试 boolean traceEnabled = logger.isTraceEnabled(); boolean debugEnabled = logger.isDebugEnabled(); // 遍历资源数组,查找候选 BeanDefinition for (Resource resource : resources) { // ... [代码部分省略以简化] if (resource.isReadable()) { try { // 获取资源的元数据读取器 MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource); if (isCandidateComponent(metadataReader)) { // 如果是候选的组件类 ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); sbd.setSource(resource); // 如果是候选的组件类,添加到候选 BeanDefinition 集合 if (isCandidateComponent(sbd)) { // ... [代码部分省略以简化] candidates.add(sbd); } else { // ... [代码部分省略以简化] } } else { // ... [代码部分省略以简化] } } catch (Throwable ex) { // ... [代码部分省略以简化] } } else { // ... [代码部分省略以简化] } } } catch (IOException ex) { // ... [代码部分省略以简化] } // 返回找到的候选 BeanDefinition 集合 return candidates; } ``` 在`org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#isCandidateComponent(metadataReader)`方法中,主要用于在组件扫描过程中,根据过滤器的规则和条件检查,判断是否给定的类是一个候选的组件类,用于决定是否将其注册为 `Spring` `BeanDefinition`。 ```java protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException { // 遍历排除过滤器列表 for (TypeFilter tf : this.excludeFilters) { // 如果匹配了任何一个排除过滤器,将其标记为不是候选组件 if (tf.match(metadataReader, getMetadataReaderFactory())) { return false; } } // 遍历包含过滤器列表 for (TypeFilter tf : this.includeFilters) { // 如果匹配了任何一个包含过滤器 if (tf.match(metadataReader, getMetadataReaderFactory())) { // 检查是否符合条件(通常用于进一步条件检查) return isConditionMatch(metadataReader); } } // 默认情况下,不是候选组件 return false; } ``` 在`org.springframework.core.type.filter.AbstractTypeHierarchyTraversingFilter#match(metadataReader, metadataReaderFactory)`方法中, 主要用于在过滤过程中检查是否应该匹配给定的 `MetadataReader`。 ```java @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { // 优化:首先检查是否可以直接匹配当前的 metadataReader if (matchSelf(metadataReader)) { return true; } // 获取类元数据信息 ClassMetadata metadata = metadataReader.getClassMetadata(); // 检查是否匹配类的名称 if (matchClassName(metadata.getClassName())) { return true; } // ... [代码部分省略以简化] // 如果没有任何条件匹配成功,返回 false 表示不匹配 return false; } ``` 在`org.springframework.core.type.filter.AnnotationTypeFilter#matchSelf`方法中,主要用于检查给定的 `metadataReader` 是否满足特定的匹配条件。如果 `metadataReader` 是一个类,该类被 `@Component` 注解修饰,那么 `matchSelf` 方法将被用于检查是否匹配 `@Component` 注解或其元注解。 ```java @Override protected boolean matchSelf(MetadataReader metadataReader) { // 获取注解元数据信息 AnnotationMetadata metadata = metadataReader.getAnnotationMetadata(); // 检查是否包含指定的注解或其元注解 return metadata.hasAnnotation(this.annotationType.getName()) || (this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName())); } ``` > [`org.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScan`步骤2] 在`org.springframework.context.annotation.AnnotationConfigUtils#processCommonDefinitionAnnotations(abd)`方法中,处理 Bean 定义上的常见注解,以确保 Bean 在容器中的行为和属性符合这些注解的规定。 ```java public static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd) { processCommonDefinitionAnnotations(abd, abd.getMetadata()); } ``` 在`org.springframework.context.annotation.AnnotationConfigUtils#processCommonDefinitionAnnotations(abd,metadata)`方法中,处理常见的 Bean 定义注解,如 `@Lazy`、`@Primary`、`@DependsOn`、`@Role` 和 `@Description`。 ```java static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) { // 处理 @Lazy 注解 AnnotationAttributes lazy = attributesFor(metadata, Lazy.class); if (lazy != null) { abd.setLazyInit(lazy.getBoolean("value")); } else if (abd.getMetadata() != metadata) { lazy = attributesFor(abd.getMetadata(), Lazy.class); if (lazy != null) { abd.setLazyInit(lazy.getBoolean("value")); } } // 处理 @Primary 注解 if (metadata.isAnnotated(Primary.class.getName())) { abd.setPrimary(true); } // 处理 @DependsOn 注解 AnnotationAttributes dependsOn = attributesFor(metadata, DependsOn.class); if (dependsOn != null) { abd.setDependsOn(dependsOn.getStringArray("value")); } // 处理 @Role 注解 AnnotationAttributes role = attributesFor(metadata, Role.class); if (role != null) { abd.setRole(role.getNumber("value").intValue()); } // 处理 @Description 注解 AnnotationAttributes description = attributesFor(metadata, Description.class); if (description != null) { abd.setDescription(description.getString("value")); } } ``` > [`org.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScan`步骤6] 在`org.springframework.context.annotation.ClassPathBeanDefinitionScanner#checkCandidate`方法中,确保在注册 Bean 定义时,不会出现相同名称的 Bean 定义,或者如果名称相同,那么它们必须是兼容的。如果出现不兼容的情况,将抛出异常以防止冲突的 Bean 定义被注册。 ```java protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException { if (!this.registry.containsBeanDefinition(beanName)) { // 如果注册表中不包含具有相同名称的Bean定义,返回true,表示候选的Bean可以被注册。 return true; } BeanDefinition existingDef = this.registry.getBeanDefinition(beanName); BeanDefinition originatingDef = existingDef.getOriginatingBeanDefinition(); if (originatingDef != null) { existingDef = originatingDef; } if (isCompatible(beanDefinition, existingDef)) { // 如果候选的Bean定义与已存在的Bean定义兼容,返回false,表示不需要覆盖已存在的Bean定义。 return false; } throw new ConflictingBeanDefinitionException("Annotation-specified bean name '" + beanName + "' for bean class [" + beanDefinition.getBeanClassName() + "] conflicts with existing, " + "non-compatible bean definition of same name and class [" + existingDef.getBeanClassName() + "]"); // 如果候选的Bean定义与已存在的Bean定义不兼容,抛出异常以防止冲突的Bean定义被注册。 } ``` > [`org.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScan`步骤6.1] 在`org.springframework.context.annotation.ClassPathBeanDefinitionScanner#registerBeanDefinition`方法中,又调用了另外一个方法。 ```java protected void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) { BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry); } ``` 在`org.springframework.beans.factory.support.BeanDefinitionReaderUtils#registerBeanDefinition`方法中,将Bean定义注册到Spring容器的Bean定义注册表中,并处理别名的注册。 ```java public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // Register bean definition under primary name. String beanName = definitionHolder.getBeanName(); registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // Register aliases for bean name, if any. String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String alias : aliases) { registry.registerAlias(beanName, alias); } } } ``` > [`org.springframework.context.annotation.ClassPathBeanDefinitionScanner#scan`步骤2] 在`org.springframework.context.annotation.AnnotationConfigUtils#registerAnnotationConfigProcessors(registry)`方法中,注解配置处理器注册到 Spring 容器中,从而启用注解驱动的配置。 ```java public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) { registerAnnotationConfigProcessors(registry, null); } ``` 在`org.springframework.context.annotation.AnnotationConfigUtils#registerAnnotationConfigProcessors(registry, source)`方法中,主要目的是注册一些关键的注解配置处理器,以便支持注解驱动的配置和处理不同类型的注解。 ```java public static Set registerAnnotationConfigProcessors( BeanDefinitionRegistry registry, @Nullable Object source) { // 1. 如果 BeanDefinitionRegistry 是 DefaultListableBeanFactory 的实例,执行以下操作 DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry); if (beanFactory != null) { // 1.1 检查当前的依赖比较器是否是 AnnotationAwareOrderComparator 的实例,如果不是,设置依赖比较器为 AnnotationAwareOrderComparator.INSTANCE,用于处理注解驱动排序。 if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) { beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE); } // 1.2 检查当前的自动装配候选解析器是否是 ContextAnnotationAutowireCandidateResolver 的实例,如果不是,设置自动装配候选解析器为 ContextAnnotationAutowireCandidateResolver,用于处理注解驱动的自动装配。 if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) { beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); } } // 2. 创建一个空的 LinkedHashSet 用于存储将要注册的 Bean 定义。 Set beanDefs = new LinkedHashSet<>(8); // 3. 检查是否已经注册了名为 CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME 的 Bean 定义。如果没有,创建一个 ConfigurationClassPostProcessor 类型的 Bean 定义,并将其添加到 BeanDefinitionRegistry 中。 if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)); } // 4. 检查是否已经注册了名为 AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME 的 Bean 定义。如果没有,创建一个 AutowiredAnnotationBeanPostProcessor 类型的 Bean 定义,并将其添加到 BeanDefinitionRegistry 中。 if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)); } // 5. 检查是否已经注册了名为 COMMON_ANNOTATION_PROCESSOR_BEAN_NAME 的 Bean 定义,这是用于支持 JSR-250 注解的处理器。如果没有,并且检测到 JSR-250 的支持,创建一个 CommonAnnotationBeanPostProcessor 类型的 Bean 定义,并将其添加到 BeanDefinitionRegistry 中。 if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)); } // 6. 检查是否已经注册了名为 PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME 的 Bean 定义,这是用于支持 JPA 注解的处理器。如果没有,并且检测到 JPA 的支持,创建一个 PersistenceAnnotationBeanPostProcessor 类型的 Bean 定义,并将其添加到 BeanDefinitionRegistry 中。 if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(); try { def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, AnnotationConfigUtils.class.getClassLoader())); } catch (ClassNotFoundException ex) { throw new IllegalStateException( "Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex); } def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)); } // 7. 检查是否已经注册了名为 EVENT_LISTENER_PROCESSOR_BEAN_NAME 的 Bean 定义。如果没有,创建一个 EventListenerMethodProcessor 类型的 Bean 定义,并将其添加到 BeanDefinitionRegistry 中。 if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME)); } // 8. 检查是否已经注册了名为 EVENT_LISTENER_FACTORY_BEAN_NAME 的 Bean 定义。如果没有,创建一个 DefaultEventListenerFactory 类型的 Bean 定义,并将其添加到 BeanDefinitionRegistry 中。 if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME)); } // 9. 返回包含注册的 Bean 定义的 LinkedHashSet。 return beanDefs; } ``` ### 七、与其他组件的关系 1. **`BeanDefinitionLoader`** + Spring Boot 中的 `BeanDefinitionLoader` 负责加载 Bean 定义,通常与 `ClassPathBeanDefinitionScanner` 一起使用,以支持自动配置。 2. **`AnnotationConfigApplicationContext`** + 这是 Spring Framework 中的 `AnnotationConfigApplicationContext`,它用于配置 Spring 应用程序上下文并支持注解驱动的配置。在内部,它可能使用 `ClassPathBeanDefinitionScanner` 来扫描和注册组件。 3. **`ComponentScanAnnotationParser` &`ComponentScanBeanDefinitionParser`** + 这些类通常用于解析 `@ComponentScan` 注解,以指定需要扫描的包和组件。它们可能使用 `ClassPathBeanDefinitionScanner` 来执行扫描操作。 4. **`AnnotationConfigWebApplicationContext`** + 这是 Spring Web 应用程序中的 `AnnotationConfigWebApplicationContext`,通常用于配置 Web 应用程序的 Spring 上下文。它可以使用 `ClassPathBeanDefinitionScanner` 来进行组件扫描。 ### 八、常见问题 1. **`ClassPathBeanDefinitionScanner` 是什么?** - 用于执行组件扫描和注册。它用于查找应用程序类路径上的标记为组件的类(如标有 `@Component`、`@Service`、`@Repository` 注解的类),并将它们注册为 Spring Bean。 2. **`ClassPathBeanDefinitionScanner` 的主要功能是什么?** - 主要功能包括扫描指定包中的类,识别带有特定注解的类,将它们注册为 Spring Bean,并支持自动装配。它还支持自定义的过滤器,以便根据应用程序需求筛选要扫描和注册的类。 3. **如何在 Spring 配置中使用 `ClassPathBeanDefinitionScanner`?** - 通常,`ClassPathBeanDefinitionScanner` 不会直接在配置文件中使用。它通常由 Spring 的注解驱动配置或 Spring Boot 的自动配置机制隐式使用。配置类中使用 `@ComponentScan` 注解来启用组件扫描,`ClassPathBeanDefinitionScanner` 将在内部使用。 4. **如何自定义 `ClassPathBeanDefinitionScanner` 的行为?** - 可以通过创建自定义组件扫描器并扩展 `ClassPathBeanDefinitionScanner`,然后使用自定义扫描器来覆盖默认的组件扫描行为。这允许我们自定义扫描过滤器、扫描路径等。 5. **`ClassPathBeanDefinitionScanner` 是否支持自定义注解?** - 可以配置 `ClassPathBeanDefinitionScanner` 来扫描具有自定义注解的类。通过设置包含和排除过滤器,我们可以指定要扫描的自定义注解类型。 6. **`ClassPathBeanDefinitionScanner` 是否支持排除特定类?** - 可以使用 `ClassPathBeanDefinitionScanner` 的过滤器来排除特定类,以防止它们被扫描和注册为 Spring Bean。 7. **`ClassPathBeanDefinitionScanner` 是否支持排除特定包?** - 可以使用 `ClassPathBeanDefinitionScanner` 的过滤器来排除特定包中的类,以防止它们被扫描和注册为 Spring Bean。 8. **如何在 Spring Boot 中使用 `ClassPathBeanDefinitionScanner`?** - 在 Spring Boot 应用程序中,通常不需要显式使用 `ClassPathBeanDefinitionScanner`。Spring Boot 使用自动配置机制来扫描和注册组件。如果需要自定义组件扫描,可以在配置类中使用 `@ComponentScan` 注解来指定要扫描的包。 9. **`ClassPathBeanDefinitionScanner` 是否支持排除特定注解?** - 可以使用 `ClassPathBeanDefinitionScanner` 的过滤器来排除具有特定注解的类,以防止它们被扫描和注册为 Spring Bean。 ================================================ FILE: spring-beans/spring-bean-classPathBeanDefinitionScanner/pom.xml ================================================ spring-beans com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-bean-classPathBeanDefinitionScanner ================================================ FILE: spring-beans/spring-bean-classPathBeanDefinitionScanner/src/main/java/com/xcs/spring/ClassPathBeanDefinitionScannerDemo.java ================================================ package com.xcs.spring; import com.xcs.spring.controller.MyController; import com.xcs.spring.repository.MyRepository; import com.xcs.spring.service.MyService; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; /** * @author xcs * @date 2023年11月07日 17时45分 **/ public class ClassPathBeanDefinitionScannerDemo { public static void main(String[] args) { // 创建一个 AnnotationConfigApplicationContext DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); // 创建 ClassPathBeanDefinitionScanner 并将其关联到容器 ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(factory); // 使用 ClassPathBeanDefinitionScanner的scan方法扫描Bean对象 scanner.scan("com.xcs.spring"); System.out.println("MyController = " + factory.getBean(MyController.class)); System.out.println("MyService = " + factory.getBean(MyService.class)); System.out.println("MyRepository = " + factory.getBean(MyRepository.class)); } } ================================================ FILE: spring-beans/spring-bean-classPathBeanDefinitionScanner/src/main/java/com/xcs/spring/controller/MyController.java ================================================ package com.xcs.spring.controller; import org.springframework.stereotype.Controller; /** * @author xcs * @date 2023年11月07日 17时47分 **/ @Controller public class MyController { } ================================================ FILE: spring-beans/spring-bean-classPathBeanDefinitionScanner/src/main/java/com/xcs/spring/repository/MyRepository.java ================================================ package com.xcs.spring.repository; import org.springframework.stereotype.Repository; /** * @author xcs * @date 2023年11月07日 17时46分 **/ @Repository public class MyRepository { } ================================================ FILE: spring-beans/spring-bean-classPathBeanDefinitionScanner/src/main/java/com/xcs/spring/service/MyService.java ================================================ package com.xcs.spring.service; import org.springframework.stereotype.Service; /** * @author xcs * @date 2023年11月07日 17时46分 **/ @Service public class MyService { } ================================================ FILE: spring-beans/spring-bean-groovyBeanDefinitionReader/README.md ================================================ ## GroovyBeanDefinitionReader - [GroovyBeanDefinitionReader](#groovybeandefinitionreader) - [一、知识储备](#一知识储备) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、最佳实践](#四最佳实践) - [五、源码分析](#五源码分析) - [六、与其他组件的关系](#六与其他组件的关系) - [七、常见问题](#七常见问题) ### 一、知识储备 1. **Groovy 语言** + 了解 Groovy 语言的基础语法和特性非常重要,因为 `GroovyBeanDefinitionReader` 用于解析 Groovy 脚本文件。我们应该理解 Groovy 的闭包、动态类型、元编程等概念。 2. **Spring 配置** + 了解如何配置 Spring 应用程序上下文,包括 XML 配置、注解配置以及使用 Groovy 脚本文件配置的方式。我们应该知道如何定义和配置 Spring Bean,并了解 Bean 的作用域、生命周期等。 3. **Groovy 脚本编写** + 学习如何编写 Groovy 脚本以定义和配置 Spring Bean。这可能包括创建 Bean 定义、引入其他类和资源、执行自定义逻辑等。 4. **Spring 脚本支持** + 了解 Spring 如何支持不同类型的配置方式,包括 XML、注解和 Groovy 脚本,以及它们之间的差异和优缺点。 ### 二、基本描述 `GroovyBeanDefinitionReader` 是一个用于读取 Groovy 脚本文件并将其解析为 Spring 的 Bean 定义的组件。它是 Spring Framework 的一部分,主要用于支持将 Groovy 脚本用于配置应用程序上下文中的 Bean。 ### 三、主要功能 1. **加载 Groovy 脚本文件** + `GroovyBeanDefinitionReader` 允许我们加载 Groovy 脚本文件,这些脚本文件可以包含 Spring Bean 的定义以及其他配置信息。 2. **解析 Bean 定义** + 它解析 Groovy 脚本中定义的 Bean,包括 Bean 的类型、属性、依赖关系等信息,并将其转化为 Spring Bean 定义。 3. **注册 Bean 定义** + 解析后的 Bean 定义将被注册到 Spring 容器中,使这些 Bean 可以在应用程序中被管理和使用。 4. **支持依赖注入** + `GroovyBeanDefinitionReader` 支持在 Groovy 脚本中使用依赖注入,允许我们引用其他 Bean 或资源,并将它们注入到正在创建的 Bean 中。 5. **支持自定义逻辑** + 我们可以在 Groovy 脚本中编写自定义逻辑来动态计算 Bean 的属性值,从而实现更复杂的配置。 6. **支持Bean的作用域和生命周期** + 我们可以在 Groovy 脚本中指定 Bean 的作用域(例如单例或原型)以及 Bean 的生命周期回调方法(例如初始化方法和销毁方法)。 ### 四、最佳实践 首先通过`GroovyBeanDefinitionReader`加载和注册了Spring Bean定义,从`my-beans.groovy` Groovy文件中创建了`MyService` Bean实例,并通过该Bean打印一条消息,。 ```java public class GroovyBeanDefinitionReaderDemo { public static void main(String[] args) { // 创建一个 Spring IOC 容器 DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); // 创建一个 GroovyBeanDefinitionReader GroovyBeanDefinitionReader reader = new GroovyBeanDefinitionReader(factory); // 加载 Groovy 文件并注册 Bean 定义 reader.loadBeanDefinitions(new ClassPathResource("my-beans.groovy")); // 获取MyService MyService myService = factory.getBean(MyService.class); // 打印消息 myService.showMessage(); } } ``` 定义一个`MyService`接口 ```java public interface MyService { void showMessage(); } ``` 我们在`classpath:my-beans.groovy`文件中,定义了一个名为`MyServiceImpl`的Groovy类,实现了`MyService`接口和`InitializingBean`接口,其中包含了一个`message`字段和对应的setter和getter方法,以及在初始化时会调用的`afterPropertiesSet`方法。通过`Groovy DSL`的`beans`闭包,我们定义了一个名为`myServiceImpl`的Spring Bean,指定其类型为`MyServiceImpl`,并设置了`message`属性为"`hello world`"。 ```groovy import com.xcs.spring.service.MyService import org.springframework.beans.factory.InitializingBean class MyServiceImpl implements MyService, InitializingBean { private String message void setMessage(String message) { this.message = message } String getMessage() { return message } @Override void afterPropertiesSet() throws Exception { System.out.println("MyServiceImpl.afterPropertiesSet") } @Override void showMessage() { System.out.println(message) } } beans { myServiceImpl(MyServiceImpl) { message = "hello world" } } ``` 运行结果发现,我们成功创建了`myServiceImpl` Bean,并在初始化时调用了`afterPropertiesSet`方法。最后,`myServiceImpl` Bean 打印了"`hello world`"消息,这是我们在配置文件中设置的消息内容。这表明我们的`Groovy DSL`配置和Spring容器设置正常工作。 ```java MyServiceImpl.afterPropertiesSet hello world ``` ### 五、源码分析 在`org.springframework.beans.factory.support.AbstractBeanDefinitionReader#loadBeanDefinitions(location)`方法中,又调用了 `loadBeanDefinitions(encodedResource)` 方法,同时将 `resource` 包装成一个 `EncodedResource` 对象。 ```java @Override public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { return loadBeanDefinitions(new EncodedResource(resource)); } ``` 在`org.springframework.beans.factory.support.AbstractBeanDefinitionReader#loadBeanDefinitions(location,actualResources)`方法中,主要用于加载 Groovy 配置文件中的 Bean 定义。如果文件扩展名是 "`.xml`",则会委托给标准的 `XmlBeanDefinitionReader` 进行加载。否则,它使用 Groovy 脚本加载 Bean 定义,创建 `GroovyShell` 和 `Binding` 对象,通过 Groovy 脚本执行加载操作。 ```java public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { // 检查是否为 XML 文件,如果是,将其重定向到 "standard" XmlBeanDefinitionReader 加载。 String filename = encodedResource.getResource().getFilename(); if (StringUtils.endsWithIgnoreCase(filename, ".xml")) { return this.standardXmlBeanDefinitionReader.loadBeanDefinitions(encodedResource); } // 如果不是 XML 文件,执行 Groovy Bean 定义加载。 if (logger.isTraceEnabled()) { logger.trace("Loading Groovy bean definitions from " + encodedResource); } // 创建 Closure 对象 "beans" 用于处理 Bean 定义。 @SuppressWarnings("serial") Closure beans = new Closure(this) { @Override public Object call(Object... args) { // 调用 invokeBeanDefiningClosure 方法处理 Bean 定义。 invokeBeanDefiningClosure((Closure) args[0]); return null; } }; // 创建 Binding 对象,用于将变量绑定到 Groovy 脚本。 Binding binding = new Binding() { @Override public void setVariable(String name, Object value) { if (currentBeanDefinition != null) { // 如果存在当前 Bean 定义,将属性应用到 Bean 定义中。 applyPropertyToBeanDefinition(name, value); } else { super.setVariable(name, value); } } }; binding.setVariable("beans", beans); // 记录加载 Bean 定义之前的数量。 int countBefore = getRegistry().getBeanDefinitionCount(); try { // 创建 GroovyShell,并使用 Binding 绑定变量。 GroovyShell shell = new GroovyShell(getBeanClassLoader(), binding); // 评估 Groovy 脚本以加载 Bean 定义。 shell.evaluate(encodedResource.getReader(), "beans"); } catch (Throwable ex) { throw new BeanDefinitionParsingException(new Problem("Error evaluating Groovy script: " + ex.getMessage(), new Location(encodedResource.getResource()), null, ex)); } // 计算加载后的 Bean 定义数量。 int count = getRegistry().getBeanDefinitionCount() - countBefore; if (logger.isDebugEnabled()) { logger.debug("Loaded " + count + " bean definitions from " + encodedResource); } return count; } ``` 在`org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader#invokeMethod`方法中,方法用于处理 `Groovy DSL` 中的方法调用,根据方法名和参数执行不同的操作,包括创建 Bean 定义、处理引用和其他操作。 ```java @Override public Object invokeMethod(String name, Object arg) { // 将参数转换为 Object 数组。 Object[] args = (Object[]) arg; if ("beans".equals(name) && args.length == 1 && args[0] instanceof Closure) { // 如果方法名是 "beans",并且只有一个参数是闭包,调用 beans 方法。 return beans((Closure) args[0]); } else if ("ref".equals(name)) { // 如果方法名是 "ref",处理 Bean 引用。 String refName; if (args[0] == null) { throw new IllegalArgumentException("Argument to ref() is not a valid bean or was not found"); } if (args[0] instanceof RuntimeBeanReference) { refName = ((RuntimeBeanReference) args[0]).getBeanName(); } else { refName = args[0].toString(); } boolean parentRef = false; if (args.length > 1 && args[1] instanceof Boolean) { parentRef = (Boolean) args[1]; } return new RuntimeBeanReference(refName, parentRef); } else if (this.namespaces.containsKey(name) && args.length > 0 && args[0] instanceof Closure) { // 如果方法名匹配已知的命名空间,且参数包含闭包,处理动态元素。 GroovyDynamicElementReader reader = createDynamicElementReader(name); reader.invokeMethod("doCall", args); } else if (args.length > 0 && args[0] instanceof Closure) { // 如果参数包含闭包,处理抽象 Bean 定义。 return invokeBeanDefiningMethod(name, args); } else if (args.length > 0 && (args[0] instanceof Class || args[0] instanceof RuntimeBeanReference || args[0] instanceof Map)) { // 如果参数包含类、RuntimeBeanReference 对象或映射,处理 Bean 定义。 return invokeBeanDefiningMethod(name, args); } else if (args.length > 1 && args[args.length - 1] instanceof Closure) { // 如果参数包含闭包且是最后一个参数,处理 Bean 定义。 return invokeBeanDefiningMethod(name, args); } MetaClass mc = DefaultGroovyMethods.getMetaClass(getRegistry()); if (!mc.respondsTo(getRegistry(), name, args).isEmpty()) { // 如果以上条件都不匹配,尝试调用 Groovy MetaClass 中的相应方法。 return mc.invokeMethod(getRegistry(), name, args); } return this; } ``` 在`org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader#invokeBeanDefiningMethod`方法中,根据传入的参数创建或更新 Bean 定义,并将其注册到 `BeanFactory` 中。 ```java private GroovyBeanDefinitionWrapper invokeBeanDefiningMethod(String beanName, Object[] args) { boolean hasClosureArgument = (args[args.length - 1] instanceof Closure); if (args[0] instanceof Class) { Class beanClass = (Class) args[0]; if (hasClosureArgument) { // 如果参数包含闭包,创建 GroovyBeanDefinitionWrapper,解析构造参数。 if (args.length - 1 != 1) { this.currentBeanDefinition = new GroovyBeanDefinitionWrapper( beanName, beanClass, resolveConstructorArguments(args, 1, args.length - 1)); } else { this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName, beanClass); } } else { // 如果没有闭包参数,创建 GroovyBeanDefinitionWrapper,解析构造参数。 this.currentBeanDefinition = new GroovyBeanDefinitionWrapper( beanName, beanClass, resolveConstructorArguments(args, 1, args.length)); } } else if (args[0] instanceof RuntimeBeanReference) { // 如果参数是 RuntimeBeanReference,创建 GroovyBeanDefinitionWrapper 表示引用其他 Bean。 this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName); this.currentBeanDefinition.getBeanDefinition().setFactoryBeanName(((RuntimeBeanReference) args[0]).getBeanName()); } else if (args[0] instanceof Map) { // 如果参数是映射,可能表示具名构造参数或工厂方法。 if (args.length > 1 && args[1] instanceof Class) { // 具名构造参数情况,解析构造参数并设置属性。 List constructorArgs = resolveConstructorArguments(args, 2, hasClosureArgument ? args.length - 1 : args.length); this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName, (Class) args[1], constructorArgs); Map namedArgs = (Map) args[0]; for (Map.Entry entity : namedArgs.entrySet()) { String propName = (String) entity.getKey(); setProperty(propName, entity.getValue()); } } else { // 工厂方法情况,解析参数并设置工厂相关属性。 this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName); Map.Entry factoryBeanEntry = ((Map) args[0]).entrySet().iterator().next(); int constructorArgsTest = (hasClosureArgument ? 2 : 1); if (args.length > constructorArgsTest){ // 存在构造参数,解析构造参数。 int endOfConstructArgs = (hasClosureArgument ? args.length - 1 : args.length); this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName, null, resolveConstructorArguments(args, 1, endOfConstructArgs)); } else { this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName); } this.currentBeanDefinition.getBeanDefinition().setFactoryBeanName(factoryBeanEntry.getKey().toString()); this.currentBeanDefinition.getBeanDefinition().setFactoryMethodName(factoryBeanEntry.getValue().toString()); } } else if (args[0] instanceof Closure) { // 如果参数是闭包,创建 GroovyBeanDefinitionWrapper 表示抽象 Bean 定义。 this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName); this.currentBeanDefinition.getBeanDefinition().setAbstract(true); } else { // 其他情况,解析构造参数。 List constructorArgs = resolveConstructorArguments(args, 0, hasClosureArgument ? args.length - 1 : args.length); this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName, null, constructorArgs); } if (hasClosureArgument) { // 如果存在闭包参数,设置闭包的代理和解析策略,并调用闭包处理 Bean 定义。 Closure callable = (Closure) args[args.length - 1]; callable.setDelegate(this); callable.setResolveStrategy(Closure.DELEGATE_FIRST); callable.call(this.currentBeanDefinition); } // 获取 Bean 定义并将其注册到 BeanFactory。 GroovyBeanDefinitionWrapper beanDefinition = this.currentBeanDefinition; this.currentBeanDefinition = null; beanDefinition.getBeanDefinition().setAttribute(GroovyBeanDefinitionWrapper.class.getName(), beanDefinition); getRegistry().registerBeanDefinition(beanName, beanDefinition.getBeanDefinition()); return beanDefinition; } ``` ### 六、与其他组件的关系 1. **`GenericGroovyApplicationContext`** + 这是一个实现了 `org.springframework.context.ApplicationContext` 接口的 Spring 应用上下文类。它允许你加载 Groovy 配置文件,并使用 `GroovyBeanDefinitionReader` 来解析 `Groovy DSL`。 2. **`GroovyScriptFactory`** + 这个类允许你将 Groovy 脚本作为 Spring Bean 定义加载,使用 `GroovyBeanDefinitionReader` 解析 Groovy 脚本。 ### 七、常见问题 1. **找不到类或 Bean 定义错误** - 在 `Groovy DSL` 文件中引用了不存在的类或 Bean 定义,导致找不到类的错误。我们需要确保类和 Bean 定义的名称和路径是正确的,确保 Groovy 文件中的引用与实际类名一致。 2. **Groovy 语法错误** - `Groovy DSL` 文件中存在语法错误,导致加载失败。我们需要仔细检查 `Groovy DSL` 文件中的语法,确保没有语法错误。可以使用 `Groovy IDE` 或编辑器来辅助检查语法。 3. **类路径问题** - `Groovy DSL` 文件中引用的类不在类路径上,导致加载失败。我们需要确保引用的类位于类路径上,可以通过修改类路径或将类文件放在正确的位置来解决。 4. **Groovy 版本兼容性** - 使用的 Groovy 版本不兼容 Spring 版本,导致加载失败。我们需要确保 Groovy 版本与所使用的 Spring 版本兼容。查看 Spring 文档以获取支持的 Groovy 版本信息。 ================================================ FILE: spring-beans/spring-bean-groovyBeanDefinitionReader/pom.xml ================================================ spring-beans com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-bean-groovyBeanDefinitionReader org.codehaus.groovy groovy 3.0.19 org.codehaus.groovy groovy-ant 3.0.19 org.codehaus.groovy groovy-astbuilder 3.0.19 org.codehaus.groovy groovy-cli-picocli 3.0.19 org.codehaus.groovy groovy-console 3.0.19 org.codehaus.groovy groovy-datetime 3.0.19 org.codehaus.groovy groovy-docgenerator 3.0.19 org.codehaus.groovy groovy-groovydoc 3.0.19 org.codehaus.groovy groovy-groovysh 3.0.19 org.codehaus.groovy groovy-jmx 3.0.19 org.codehaus.groovy groovy-json 3.0.19 org.codehaus.groovy groovy-jsr223 3.0.19 org.codehaus.groovy groovy-macro 3.0.19 org.codehaus.groovy groovy-nio 3.0.19 org.codehaus.groovy groovy-servlet 3.0.19 org.codehaus.groovy groovy-sql 3.0.19 org.codehaus.groovy groovy-swing 3.0.19 org.codehaus.groovy groovy-templates 3.0.19 org.codehaus.groovy groovy-test 3.0.19 org.codehaus.groovy groovy-test-junit5 3.0.19 org.codehaus.groovy groovy-testng 3.0.19 org.codehaus.groovy groovy-xml 3.0.19 ================================================ FILE: spring-beans/spring-bean-groovyBeanDefinitionReader/src/main/java/com/xcs/spring/GroovyBeanDefinitionReaderDemo.java ================================================ package com.xcs.spring; import com.xcs.spring.service.MyService; import org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; /** * @author xcs * @date 2023年11月07日 10时01分 **/ public class GroovyBeanDefinitionReaderDemo { public static void main(String[] args) { // 创建一个 Spring IOC 容器 DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); // 创建一个 GroovyBeanDefinitionReader GroovyBeanDefinitionReader reader = new GroovyBeanDefinitionReader(factory); // 加载 Groovy 文件并注册 Bean 定义 reader.loadBeanDefinitions(new ClassPathResource("my-beans.groovy")); // 获取MyService MyService myService = factory.getBean(MyService.class); // 打印消息 myService.showMessage(); } } ================================================ FILE: spring-beans/spring-bean-groovyBeanDefinitionReader/src/main/java/com/xcs/spring/service/MyService.java ================================================ package com.xcs.spring.service; public interface MyService { void showMessage(); } ================================================ FILE: spring-beans/spring-bean-groovyBeanDefinitionReader/src/main/resources/my-beans.groovy ================================================ import com.xcs.spring.service.MyService import org.springframework.beans.factory.InitializingBean class MyServiceImpl implements MyService, InitializingBean { private String message void setMessage(String message) { this.message = message } String getMessage() { return message } @Override void afterPropertiesSet() throws Exception { System.out.println("MyServiceImpl.afterPropertiesSet") } @Override void showMessage() { System.out.println(message) } } beans { myServiceImpl(MyServiceImpl) { message = "hello world123456" } } ================================================ FILE: spring-beans/spring-bean-propertiesBeanDefinitionReader/README.md ================================================ ## PropertiesBeanDefinitionReader - [PropertiesBeanDefinitionReader](#propertiesbeandefinitionreader) - [一、知识储备](#一知识储备) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、最佳实践](#四最佳实践) - [五、时序图](#五时序图) - [六、源码分析](#六源码分析) - [七、与其他组件的关系](#七与其他组件的关系) - [八、常见问题](#八常见问题) ### 一、知识储备 1. `Resource` - `Resource` 代表一个资源,可以是文件、类路径上的文件、URL 等。它提供了对资源的抽象和访问方法。 - [点击查看Resource接口](https://github.com/xuchengsheng/spring-reading/tree/master/spring-resources/spring-resource) 2. `BeanDefinition` - `BeanDefinition` 是 Spring 中描述和管理 Bean 配置的核心概念,它包括了有关 Bean 的信息,如类名、作用域、依赖关系、初始化方法等,而 `AnnotatedBeanDefinitionReader` 的主要任务之一是将使用注解配置的类转化为 `BeanDefinition` 并注册到 Spring 容器中。 - [点击查看BeanDefinition接口](https://github.com/xuchengsheng/spring-reading/tree/master/spring-beans/spring-bean-beanDefinition) ### 二、基本描述 `PropertiesBeanDefinitionReader` 是 Spring 框架的一个组件,它的主要功能是从属性文件中加载 Bean 的配置信息,然后将这些配置信息解析并转化为 Spring 容器中的 Bean 定义。属性文件的格式通常是键值对,键表示 Bean 的名称,值表示 Bean 的类名或其他配置属性。这个过程允许我们将 Bean 配置与应用程序代码和 XML 配置文件分离,以实现动态加载和管理 Bean 定义,增强了应用程序的可扩展性和配置的灵活性。 ### 三、主要功能 1. **加载属性文件** + `PropertiesBeanDefinitionReader` 通过 `loadBeanDefinitions(Resource resource)` 方法加载属性文件,其中的 `Resource` 可以是文件路径、类路径或其他资源标识符,指定了包含 Bean 定义信息的属性文件的位置。 2. **解析属性文件** + 一旦属性文件被加载,`PropertiesBeanDefinitionReader` 会解析文件中的内容,识别 Bean 的名称、类名以及其他配置属性。 3. **创建 Bean 定义** + 根据属性文件中的配置信息,`PropertiesBeanDefinitionReader` 创建相应的 `BeanDefinition` 对象,其中包含了 Bean 的定义信息,包括名称、类、属性、依赖关系等。 4. **注册 Bean 定义** + `PropertiesBeanDefinitionReader` 将解析得到的 Bean 定义注册到 Spring IOC 容器中,使这些 Bean 可以在应用程序中被实例化和使用。 ### 四、最佳实践 我们创建了一个 Spring Bean 工厂(`DefaultListableBeanFactory`)和一个属性文件读取器(`PropertiesBeanDefinitionReader`),然后从属性文件中加载 Bean 的定义,然后通过 Bean 工厂获取相应的 Bean 对象,以实现动态加载和管理 Bean 配置 ```java public class PropertiesBeanDefinitionReaderDemo { public static void main(String[] args) { DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); PropertiesBeanDefinitionReader reader = new PropertiesBeanDefinitionReader(beanFactory); // 从properties文件加载bean定义 reader.loadBeanDefinitions(new ClassPathResource("bean-definitions.properties")); // 获取bean System.out.println("myBean = " + beanFactory.getBean("myBean")); System.out.println("myBean = " + beanFactory.getBean("myBean")); } } ``` 属性文件 "`bean-definitions.properties`" 中的内容如下: ```properties myBean.(class)=com.xcs.spring.bean.MyBean myBean.message=hello world myBean.(lazy-init)=true myBean.(scope)=prototype ``` `MyBean` 的Java类,代表了一个简单的Java Bean。 ```java public class MyBean { private String message; public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } @Override public String toString() { return "MyBean{" + "message='" + message + '\'' + ", hashCode='0x" + Integer.toHexString(System.identityHashCode(this)).toUpperCase() + '\'' + '}'; } } ``` 运行结果发现,我们从属性文件中动态加载 Bean 配置,同时控制懒加载和作用域,以实现更灵活的 Bean 管理。在这个场景中,每次请求 "`myBeanA`" 都会得到一个新的实例,这是因为作用域设置为 "`prototype`"。 ```java myBean = MyBean{message='hello world', hashCode='0x6646153'} myBean = MyBean{message='hello world', hashCode='0x45DD4EDA'} ``` ### 五、时序图 ~~~mermaid sequenceDiagram autonumber Title: PropertiesBeanDefinitionReader时序图 PropertiesBeanDefinitionReaderDemo->>PropertiesBeanDefinitionReader:loadBeanDefinitions(resource) Note right of PropertiesBeanDefinitionReaderDemo: 从指定的属性文件加载bean定义 PropertiesBeanDefinitionReader->>PropertiesBeanDefinitionReader:loadBeanDefinitions(encodedResource,prefix) Note over PropertiesBeanDefinitionReader: 从指定的属性文件加载bean定义 PropertiesBeanDefinitionReader->>PropertiesBeanDefinitionReader:registerBeanDefinitions(map,prefix,resourceDescription) Note over PropertiesBeanDefinitionReader: 注册Map中包含的bean定义 PropertiesBeanDefinitionReader->>DefaultListableBeanFactory:registerBeanDefinition(beanName,beanDefinition) Note over PropertiesBeanDefinitionReader,DefaultListableBeanFactory: 注册Bean定义到容器 ~~~ ### 六、源码分析 在`org.springframework.beans.factory.support.PropertiesBeanDefinitionReader#loadBeanDefinitions(resource)`方法中,又调用 `loadBeanDefinitions(encodedResource,prefix)` 方法来实际加载 Bean 定义。 ```java @Override public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { return loadBeanDefinitions(new EncodedResource(resource), null); } ``` 在`org.springframework.beans.factory.support.PropertiesBeanDefinitionReader#loadBeanDefinitions(encodedResource,prefix)`方法中,主要是从属性文件中加载和管理 Bean 配置,实现了 Spring 中属性文件的解析和注册。 ```java public int loadBeanDefinitions(EncodedResource encodedResource, @Nullable String prefix) throws BeanDefinitionStoreException { // 如果启用了跟踪级别的日志,则输出加载属性文件的日志信息 if (logger.isTraceEnabled()) { logger.trace("Loading properties bean definitions from " + encodedResource); } // 创建 Properties 对象,用于存储属性文件中的键值对配置 Properties props = new Properties(); try { try (InputStream is = encodedResource.getResource().getInputStream()) { // 如果指定了编码,使用指定编码读取属性文件 if (encodedResource.getEncoding() != null) { getPropertiesPersister().load(props, new InputStreamReader(is, encodedResource.getEncoding())); } // 否则,使用默认编码读取属性文件 else { getPropertiesPersister().load(props, is); } } // 注册 Bean 定义,将属性文件中的配置转化为 Bean 定义并注册到容器中 int count = registerBeanDefinitions(props, prefix, encodedResource.getResource().getDescription()); // 如果启用了调试级别的日志,则输出已加载的 Bean 定义数量的日志信息 if (logger.isDebugEnabled()) { logger.debug("Loaded " + count + " bean definitions from " + encodedResource); } return count; } catch (IOException ex) { // 处理异常,如果在读取或注册过程中发生异常,抛出 BeanDefinitionStoreException 异常 throw new BeanDefinitionStoreException("Could not parse properties from " + encodedResource.getResource(), ex); } } ``` 在`org.springframework.beans.factory.support.PropertiesBeanDefinitionReader#registerBeanDefinitions(map, prefix,resourceDescription)`方法中,首先遍历属性文件中的键值对配置,根据配置的前缀以及键的格式,将合法的 Bean 定义注册到容器中。如果键不符合要求或已经注册,则会忽略。最终,该方法返回已注册的 Bean 定义数量,用于跟踪注册的 Bean 数量。 ```java public int registerBeanDefinitions(Map map, @Nullable String prefix, String resourceDescription) throws BeansException { if (prefix == null) { prefix = ""; } int beanCount = 0; // 遍历属性文件中的键值对配置 for (Object key : map.keySet()) { if (!(key instanceof String)) { throw new IllegalArgumentException("Illegal key [" + key + "]: only Strings allowed"); } String keyString = (String) key; if (keyString.startsWith(prefix)) { // 键的格式为:prefix<名称>.属性 String nameAndProperty = keyString.substring(prefix.length()); // 查找属性名之前的点号,忽略属性键中的点号。 int sepIdx ; int propKeyIdx = nameAndProperty.indexOf(PropertyAccessor.PROPERTY_KEY_PREFIX); if (propKeyIdx != -1) { sepIdx = nameAndProperty.lastIndexOf(SEPARATOR, propKeyIdx); } else { sepIdx = nameAndProperty.lastIndexOf(SEPARATOR); } if (sepIdx != -1) { String beanName = nameAndProperty.substring(0, sepIdx); if (logger.isTraceEnabled()) { logger.trace("Found bean name '" + beanName + "'"); } if (!getRegistry().containsBeanDefinition(beanName)) { // 如果还未注册该 Bean... // 注册 Bean 定义 registerBeanDefinition(beanName, map, prefix + beanName, resourceDescription); ++beanCount; } } else { // 忽略该键:它不是有效的 Bean 名称和属性, // 尽管它以所需的前缀开始。 if (logger.isDebugEnabled()) { logger.debug("Invalid bean name and property [" + nameAndProperty + "]"); } } } } // 返回已注册的 Bean 定义数量 return beanCount; } ``` 在`org.springframework.beans.factory.support.PropertiesBeanDefinitionReader#registerBeanDefinition(beanName,map,prefix, resourceDescription)`方法中,主要是处理属性文件中的配置并将其转化为 Spring Bean 定义,然后注册到容器中。 ```java protected void registerBeanDefinition(String beanName, Map map, String prefix, String resourceDescription) throws BeansException { // ... [代码部分省略以简化] try { AbstractBeanDefinition bd = BeanDefinitionReaderUtils.createBeanDefinition( parent, className, getBeanClassLoader()); bd.setScope(scope); bd.setAbstract(isAbstract); bd.setLazyInit(lazyInit); bd.setConstructorArgumentValues(cas); bd.setPropertyValues(pvs); getRegistry().registerBeanDefinition(beanName, bd); } catch (ClassNotFoundException ex) { throw new CannotLoadBeanClassException(resourceDescription, beanName, className, ex); } catch (LinkageError err) { throw new CannotLoadBeanClassException(resourceDescription, beanName, className, err); } } ``` ### 七、与其他组件的关系 1. **独立应用程序** + 在一些独立的 Spring 应用程序中,我们可以使用 `PropertiesBeanDefinitionReader` 来动态加载 Bean 定义,从而使应用程序的配置更加灵活。这样,应用程序可以根据需要动态配置和管理 Bean。 2. **基于属性文件的配置** + 一些应用程序采用基于属性文件的配置方式,将各种 Bean 的配置信息存储在属性文件中。`PropertiesBeanDefinitionReader` 可以用来加载这些配置文件,并将配置信息转化为 Spring 的 Bean 定义。 ### 八、常见问题 1. **如何使用 `PropertiesBeanDefinitionReader`?** + 使用 `PropertiesBeanDefinitionReader` 需要创建一个实例并与 Spring 的 Bean 工厂(如 `DefaultListableBeanFactory`)结合使用。然后,使用 `loadBeanDefinitions` 方法加载属性文件中的配置,并将 Bean 定义注册到容器中。 2. **属性文件的格式是什么?** + 属性文件的格式通常采用键值对(key=value)的形式,其中键表示 Bean 的名称,而值表示 Bean 的配置信息。属性文件中可以包含 Bean 的类、作用域、属性值等信息。 3. **如何配置懒加载和作用域?** + 在属性文件中,我们可以使用 `lazy-init` 和 `scope` 来配置懒加载和作用域。例如,`myBean.(lazy-init)=true` 可以配置一个 Bean 为懒加载,而 `myBean.(scope)=prototype` 可以配置 Bean 的作用域为原型。 4. **什么是属性文件中的 Bean 名称?** + 属性文件中的 Bean 名称通常与配置的键(key)一致。例如,`myBeanA=(class)=com.example.MyBean` 中的 Bean 名称是 "myBeanA"。 5. **为什么使用 `PropertiesBeanDefinitionReader`?** + `PropertiesBeanDefinitionReader` 可以使应用程序更加灵活,允许我们将配置信息从代码中分离出来,从而实现应用程序的可配置性。它还支持动态加载和管理 Bean,适用于需要动态配置的场景。 ================================================ FILE: spring-beans/spring-bean-propertiesBeanDefinitionReader/pom.xml ================================================ spring-beans com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-bean-propertiesBeanDefinitionReader ================================================ FILE: spring-beans/spring-bean-propertiesBeanDefinitionReader/src/main/java/com/xcs/spring/PropertiesBeanDefinitionReaderDemo.java ================================================ package com.xcs.spring; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.PropertiesBeanDefinitionReader; import org.springframework.core.io.ClassPathResource; /** * @author xcs * @date 2023年11月06日 10时09分 **/ public class PropertiesBeanDefinitionReaderDemo { public static void main(String[] args) { DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); PropertiesBeanDefinitionReader reader = new PropertiesBeanDefinitionReader(beanFactory); // 从properties文件加载bean定义 reader.loadBeanDefinitions(new ClassPathResource("bean-definitions.properties")); // 获取bean System.out.println("myBean = " + beanFactory.getBean("myBean")); System.out.println("myBean = " + beanFactory.getBean("myBean")); } } ================================================ FILE: spring-beans/spring-bean-propertiesBeanDefinitionReader/src/main/java/com/xcs/spring/bean/MyBean.java ================================================ package com.xcs.spring.bean; /** * @author xcs * @date 2023年11月06日 10时11分 **/ public class MyBean { private String message; public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } @Override public String toString() { return "MyBean{" + "message='" + message + '\'' + ", hashCode='0x" + Integer.toHexString(System.identityHashCode(this)).toUpperCase() + '\'' + '}'; } } ================================================ FILE: spring-beans/spring-bean-propertiesBeanDefinitionReader/src/main/resources/bean-definitions.properties ================================================ myBean.(class)=com.xcs.spring.bean.MyBean myBean.message=hello world myBean.(lazy-init)=true myBean.(scope)=prototype ================================================ FILE: spring-beans/spring-bean-xmlBeanDefinitionReader/README.md ================================================ ## XmlBeanDefinitionReader - [XmlBeanDefinitionReader](#xmlbeandefinitionreader) - [一、知识储备](#一知识储备) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、最佳实践](#四最佳实践) - [五、时序图](#五时序图) - [六、源码分析](#六源码分析) - [七、与其他组件的关系](#七与其他组件的关系) - [八、常见问题](#八常见问题) ### 一、知识储备 1. **`Resource`** - `Resource` 代表一个资源,可以是文件、类路径上的文件、URL 等。它提供了对资源的抽象和访问方法。 - [点击查看Resource接口](https://github.com/xuchengsheng/spring-reading/tree/master/spring-resources/spring-resource) 2. **`ResourceLoader`** - `ResourceLoader` 可以用于获取资源,这些资源可以是文件、类路径上的资源、URL、甚至是远程资源。它提供了一种统一的方式来加载资源,无论这些资源位于何处。 - [点击查看ResourceLoader接口](https://github.com/xuchengsheng/spring-reading/tree/master/spring-resources/spring-resource-resourceLoader) 3. **`DocumentLoader`** + `XmlBeanDefinitionReader`依赖于`DocumentLoader`来加载和解析XML配置文件,以便可以将XML文件中的Bean定义信息转化为Spring容器内部的Bean定义对象。 + [点击查看DocumentLoader接口](https://github.com/xuchengsheng/spring-reading/tree/master/spring-resources/spring-resource-documentLoader) ### 二、基本描述 `XmlBeanDefinitionReader`是Spring Framework中的一个类,用于加载和解析XML格式的Bean定义配置文件,将配置文件中定义的Bean元数据信息提取为Spring容器内部的Bean定义对象,进而实现IOC容器的构建和管理。这类负责读取XML配置文件,解析Bean的定义信息(包括ID、类名、属性、依赖等),并将这些定义注册到Spring应用程序上下文,使我们能够方便地配置和管理应用程序中的各种Bean组件。 ### 三、主要功能 1. **加载XML配置文件** + `XmlBeanDefinitionReader`能够加载XML格式的Spring配置文件,通常使用Resource对象来指定配置文件的位置,如`ClassPathResource`、`FileSystemResource`等。 2. **解析XML文件** + 它将XML配置文件解析为Spring容器内部数据结构,使用`DocumentLoader`来实现。这包括将XML元素和属性转化为Spring的Bean定义信息。 3. **注册Bean定义** + 一旦`XmlBeanDefinitionReader`成功解析XML文件中的Bean定义信息,它会将这些信息注册到Spring容器的Bean工厂,以便容器能够创建和管理这些Bean实例。 ### 四、最佳实践 首先创建了一个Spring容器(`DefaultListableBeanFactory`),然后使用`XmlBeanDefinitionReader`来加载和解析名为"`beans.xml`"的XML配置文件,将其中定义的Bean元数据信息注册到容器中。随后,通过容器获取名为"`myBean`"的Bean实例,最后将该Bean实例打印出来。这样的操作实现了Spring容器的初始化、XML配置文件的解析,以及Bean的获取和使用。 ```java public class XmlBeanDefinitionReaderDemo { public static void main(String[] args) { // 创建一个DefaultListableBeanFactory作为Spring容器 DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); // 创建XmlBeanDefinitionReader实例用于解析XML配置 XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); // 加载XML配置文件 reader.loadBeanDefinitions(new ClassPathResource("beans.xml")); // 获取并使用Bean MyBean myBean = factory.getBean("myBean", MyBean.class); System.out.println("myBean = " + myBean); } } ``` `ClassPathResource("sample.xml")` 将加载类路径下名为 "`sample.xml`" 的资源文件的内容。在我们的示例配置文件中,这个资源文件定义了一个名为 "`myBean`" 的 Spring Bean,该 Bean 具有一个属性 "message",其值设置为 "Hello World"。 ```xml ``` `MyBean` 的Java类,代表了一个简单的Java Bean。 ```java public class MyBean { private String message; public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } @Override public String toString() { return "MyBean{" + "message='" + message + '\'' + '}'; } } ``` ### 五、时序图 ~~~mermaid sequenceDiagram autonumber Title: XmlBeanDefinitionReader时序图 XmlBeanDefinitionReaderDemo->>XmlBeanDefinitionReader:loadBeanDefinitions(resource) XmlBeanDefinitionReader->>XmlBeanDefinitionReader:loadBeanDefinitions(encodedResource) note over XmlBeanDefinitionReader: 解码资源并加载Bean定义 XmlBeanDefinitionReader->>XmlBeanDefinitionReader:doLoadBeanDefinitions(inputSource,resource) note over XmlBeanDefinitionReader: 使用InputSource加载Bean定义 XmlBeanDefinitionReader->>XmlBeanDefinitionReader:doLoadDocument(inputSource,resource) note over XmlBeanDefinitionReader: 调用doLoadDocument方法解析XML XmlBeanDefinitionReader->>DefaultDocumentLoader:loadDocument(...) note over XmlBeanDefinitionReader,DefaultDocumentLoader: 加载XML DefaultDocumentLoader->>XmlBeanDefinitionReader:返回Document XmlBeanDefinitionReader->>XmlBeanDefinitionReader:registerBeanDefinitions(doc,resource) note over XmlBeanDefinitionReader: 调用registerBeanDefinitions方法注册Bean定义 XmlBeanDefinitionReader->>DefaultBeanDefinitionDocumentReader:createBeanDefinitionDocumentReader() note over XmlBeanDefinitionReader,DefaultBeanDefinitionDocumentReader: 创建BeanDefinitionDocumentReader DefaultBeanDefinitionDocumentReader->>XmlBeanDefinitionReader:返回documentReader XmlBeanDefinitionReader->>XmlReaderContext:new XmlReaderContext() note over XmlBeanDefinitionReader,XmlReaderContext: 创建XmlReaderContext XmlReaderContext->>XmlBeanDefinitionReader:返回readerContext XmlBeanDefinitionReader->>DefaultBeanDefinitionDocumentReader:registerBeanDefinitions(doc,readerContext) note over XmlBeanDefinitionReader,DefaultBeanDefinitionDocumentReader: 调用registerBeanDefinitions注册Bean定义 loop Every minute DefaultBeanDefinitionDocumentReader->>DefaultBeanDefinitionDocumentReader:doRegisterBeanDefinitions(root) note over DefaultBeanDefinitionDocumentReader,DefaultBeanDefinitionDocumentReader: 处理beans标签 DefaultBeanDefinitionDocumentReader->>DefaultBeanDefinitionDocumentReader:parseBeanDefinitions(root,delegate) note over DefaultBeanDefinitionDocumentReader,DefaultBeanDefinitionDocumentReader: 处理“import”、“alias”、“bean”标签 DefaultBeanDefinitionDocumentReader->>DefaultBeanDefinitionDocumentReader:parseDefaultElement(ele,delegate) note over DefaultBeanDefinitionDocumentReader,DefaultBeanDefinitionDocumentReader: 解析默认元素 alt 如果是import标签 DefaultBeanDefinitionDocumentReader->>DefaultBeanDefinitionDocumentReader:importBeanDefinitionResource(ele) note over DefaultBeanDefinitionDocumentReader,DefaultBeanDefinitionDocumentReader: 处理import标签 else 如果是alias标签 DefaultBeanDefinitionDocumentReader->>DefaultBeanDefinitionDocumentReader:processAliasRegistration(ele) note over DefaultBeanDefinitionDocumentReader,DefaultBeanDefinitionDocumentReader: 处理alias标签 else 如果是bean标签 rect rgb(207,207,207) DefaultBeanDefinitionDocumentReader->>DefaultBeanDefinitionDocumentReader:processBeanDefinition(ele, delegate) note over DefaultBeanDefinitionDocumentReader,DefaultBeanDefinitionDocumentReader: 处理bean标签 DefaultBeanDefinitionDocumentReader->>XmlBeanDefinitionReader:getRegistry() note over DefaultBeanDefinitionDocumentReader,XmlBeanDefinitionReader: 获取BeanDefinitionRegistry XmlBeanDefinitionReader->>DefaultBeanDefinitionDocumentReader:返回BeanDefinitionRegistry DefaultBeanDefinitionDocumentReader->>BeanDefinitionReaderUtils:registerBeanDefinition(definitionHolder,registry) note over DefaultBeanDefinitionDocumentReader,BeanDefinitionReaderUtils: 注册Bean定义 BeanDefinitionReaderUtils->>BeanDefinitionReaderUtils:registerBeanDefinition(beanName,beanDefinition) note over BeanDefinitionReaderUtils: 注册Bean定义到容器 end else 如果是beans标签 DefaultBeanDefinitionDocumentReader->>DefaultBeanDefinitionDocumentReader:doRegisterBeanDefinitions(ele) note over DefaultBeanDefinitionDocumentReader,DefaultBeanDefinitionDocumentReader: 处理beans标签(重新递归) end end ~~~ ### 六、源码分析 在`org.springframework.beans.factory.xml.XmlBeanDefinitionReader#loadBeanDefinitions(resource)`方法中,又调用了 `loadBeanDefinitions(encodedResource)` 方法,同时将 `resource` 包装成一个 `EncodedResource` 对象。 ```java @Override public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { return loadBeanDefinitions(new EncodedResource(resource)); } ``` 在`org.springframework.beans.factory.xml.XmlBeanDefinitionReader#loadBeanDefinitions(encodedResource)`方法中,首先是创建一个 `InputSource` 对象,用于解析XML。最后调用 `doLoadBeanDefinitions` 方法,实际的XML解析和Bean定义加载。 ```java public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { // ... [代码部分省略以简化] try (InputStream inputStream = encodedResource.getResource().getInputStream()) { // 创建一个 InputSource 对象,用于解析XML InputSource inputSource = new InputSource(inputStream); // 如果 encodedResource 包含字符编码信息,设置 InputSource 的编码 if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } // 调用 doLoadBeanDefinitions 方法,实际的XML解析和Bean定义加载 return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } catch (IOException ex) { // ... [代码部分省略以简化] } finally { // ... [代码部分省略以简化] } } ``` 在`org.springframework.beans.factory.xml.XmlBeanDefinitionReader#doLoadBeanDefinitions`方法中,主要用于加载XML配置文件,解析其中的Bean定义,并注册这些Bean定义到Spring容器中 ```java protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { // 解析XML文档 Document doc = doLoadDocument(inputSource, resource); // 注册Bean定义并获取已加载的Bean数量 int count = registerBeanDefinitions(doc, resource); if (logger.isDebugEnabled()) { logger.debug("Loaded " + count + " bean definitions from " + resource); } return count; } // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.xml.XmlBeanDefinitionReader#doLoadDocument`方法中,主要由`DocumentLoader`执行实际的XML加载和解析操作,并将解析后的`Document`对象返回。 + [点击查看DocumentLoader接口](https://github.com/xuchengsheng/spring-reading/tree/master/spring-resources/spring-resource-documentLoader) ```java protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception { return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler, getValidationModeForResource(resource), isNamespaceAware()); } ``` 在`org.springframework.beans.factory.xml.XmlBeanDefinitionReader#registerBeanDefinitions`方法中,首先创建一个 `BeanDefinitionDocumentReader` 实例,然后调用它的 `registerBeanDefinitions` 方法,将XML文档中的Bean定义注册到Spring容器中,最后返回成功注册的Bean数量。 ```java public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); int countBefore = getRegistry().getBeanDefinitionCount(); documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore; } ``` 在`org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#registerBeanDefinitions`方法中,又调用 `doRegisterBeanDefinitions` 方法,开始实际的Bean定义注册过程。 ```java @Override public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; doRegisterBeanDefinitions(doc.getDocumentElement()); } ``` 在`org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions`方法中,核心内容是,XML的预处理、Bean定义的解析和XML的后处理。 ```java protected void doRegisterBeanDefinitions(Element root) { // ... [代码部分省略以简化] // 调用 preProcessXml 方法,执行XML预处理操作 preProcessXml(root); // 调用 parseBeanDefinitions 方法,解析Bean定义 parseBeanDefinitions(root, this.delegate); // 调用 postProcessXml 方法,执行XML后处理操作 postProcessXml(root); // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseBeanDefinitions`方法中,主要解析XML文档中的Bean定义元素,根据元素所属的命名空间(默认命名空间或自定义命名空间)分别调用合适的方法进行解析。 ```java 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); } } ``` 在`org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseDefaultElement`方法中,主要负责根据元素的类型选择合适的方法进行处理,从而实现了XML配置文件中不同元素的解析和Bean定义的注册。 ```java private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { // 根据元素的名称进行不同的处理 if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { // 步骤1: 如果是 元素,导入其他Bean定义资源 importBeanDefinitionResource(ele); } else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { // 步骤2: 如果是 元素,处理Bean的别名注册 processAliasRegistration(ele); } else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { // 步骤3: 如果是 元素,处理普通Bean定义 processBeanDefinition(ele, delegate); } else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // 步骤4: 如果是 元素,递归注册内部Bean定义 doRegisterBeanDefinitions(ele); } } ``` > `org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseDefaultElement`步骤1] 在`org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#importBeanDefinitionResource`方法中,主要是对XML文档中import元素指定的其他资源进行解析和加载,从而实现多个XML配置文件的合并与包含功能。 ```java protected void importBeanDefinitionResource(Element ele) { // 获取import元素的resource属性值 String location = ele.getAttribute(RESOURCE_ATTRIBUTE); // 检查resource属性值是否为空字符串 if (!StringUtils.hasText(location)) { // 抛出错误信息 getReaderContext().error("Resource location must not be empty", ele); // 返回空结果 return; } // 解析resource属性值中的占位符变量 location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location); // 创建一个新的LinkedHashSet,用于存储所有解析后的实际资源 Set actualResources = new LinkedHashSet<>(4); // 判断resource属性值是否为绝对路径 boolean absoluteLocation = false; try { // 如果resource属性值符合URL格式,或者可以被转换为一个绝对URI,那么认为它是一个绝对路径 absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute(); } catch (URISyntaxException ex) { // ... [代码部分省略以简化] } // 如果resource属性值是一个绝对路径 if (absoluteLocation) { try { // 使用父类的loadBeanDefinitions方法加载资源,并将结果添加到actualResources集合中 int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources); // ... [代码部分省略以简化] } catch (BeanDefinitionStoreException ex) { // ... [代码部分省略以简化] } } else { // 如果resource属性值不是一个绝对路径 try { int importCount; // 尝试在当前文件所在的目录下找到resource属性值所指向的资源 Resource relativeResource = getReaderContext().getResource().createRelative(location); if (relativeResource.exists()) { // 加载找到的资源,并将结果添加到actualResources集合中 importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource); actualResources.add(relativeResource); } else { // 如果在当前文件所在的目录下找不到resource属性值所指向的资源,那么认为resource属性值是一个相对于当前文件所在位置的相对路径 String baseLocation = getReaderContext().getResource().getURL().toString(); // 调整resource属性值,使其成为一个绝对路径 String adjustedLocation = StringUtils.applyRelativePath(baseLocation, location); // 加载调整后的资源,并将结果添加到actualResources集合中 importCount = getReaderContext().getReader().loadBeanDefinitions(adjustedLocation, actualResources); } // ... [代码部分省略以简化] } catch (IOException | BeanDefinitionStoreException ex) { // ... [代码部分省略以简化] } } // 将actualResources集合中的所有资源转换为数组,并调用fireImportProcessed方法通知监听器已经完成了一次import操作 Resource[] actResArray = actualResources.toArray(new Resource[0]); getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele)); } ``` > `org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseDefaultElement`步骤2] 在`org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#processAliasRegistration`方法中,主要处理XML配置文件中的alias元素的方法。alias元素用于创建别名,即为已有的Bean ID分配一个新名称。这样,我们就可以在Bean引用时使用新的别名而不是原始的Bean ID。 ```java protected void processAliasRegistration(Element ele) { // 获取alias元素的name属性值和alias属性值 String name = ele.getAttribute(NAME_ATTRIBUTE); String alias = ele.getAttribute(ALIAS_ATTRIBUTE); // 判断两个属性值是否都有效 boolean valid = true; if (!StringUtils.hasText(name)) { // 抛出错误信息 getReaderContext().error("Name must not be empty", ele); // 设置标志位,表明该别名注册无效 valid = false; } if (!StringUtils.hasText(alias)) { // 抛出错误信息 getReaderContext().error("Alias must not be empty", ele); // 设置标志位,表明该别名注册无效 valid = false; } // 如果两个属性值都有效 if (valid) { try { // 调用Registry接口的registerAlias方法为原始Bean ID注册一个别名 getReaderContext().getRegistry().registerAlias(name, alias); } catch (Exception ex) { // ... [代码部分省略以简化] } // 调用fireAliasRegistered方法通知监听器已经完成了一次别名注册操作 getReaderContext().fireAliasRegistered(name, alias, extractSource(ele)); } } ``` > [`org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseDefaultElement`步骤3] 在`org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#processBeanDefinition`方法中,主要是解析 `` 元素,注册Bean定义到Spring容器。 ```java protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { // 调用 BeanDefinitionParserDelegate 的 parseBeanDefinitionElement 方法,解析 元素 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { // 如果解析成功,调用 delegate.decorateBeanDefinitionIfRequired 方法,如果需要的话装饰 Bean 定义 bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // 注册最终装饰后的 Bean 定义 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { // 处理注册时的异常,如果注册失败,抛出 BeanDefinitionStoreException 异常 getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex); } // 发送 Bean 注册事件 getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); } } ``` 在`org.springframework.beans.factory.support.BeanDefinitionReaderUtils#registerBeanDefinition`方法中,将Bean定义注册到Spring容器的Bean定义注册表中,并处理别名的注册。 ```java public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // Register bean definition under primary name. String beanName = definitionHolder.getBeanName(); registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // Register aliases for bean name, if any. String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String alias : aliases) { registry.registerAlias(beanName, alias); } } } ``` ### 七、与其他组件的关系 1. **`ApplicationContext`的实现类** + Spring的`ApplicationContext`接口有多个实现,其中一些实现类在其内部使用`XmlBeanDefinitionReader`来处理XML配置文件。例如,`ClassPathXmlApplicationContext`、`FileSystemXmlApplicationContext`和`XmlWebApplicationContext`都使用`XmlBeanDefinitionReader`。 2. **Custom XML配置加载** + 如果我们编写自己的Spring应用程序或自定义的Spring容器,我们可以使用`XmlBeanDefinitionReader`来实现自定义的XML配置加载逻辑。 3. **单独的`XmlBeanDefinitionReader`使用** + 有时,我们可能需要在应用程序中手动创建`XmlBeanDefinitionReader`实例,然后使用它来加载和注册Bean定义。 ### 八、常见问题 1. **XML文件路径正确性** + 确保XML配置文件的路径和名称正确,相对路径或绝对路径都要检查,否则将导致文件无法找到或读取。 2. **XML文件格式和编码** + XML文件必须符合XML语法规则,包括标签嵌套、属性格式和XML声明。同时,确保文件使用正确的字符编码,与指定的编码一致,否则可能导致解析错误或乱码。 3. **Bean定义的正确性** + XML文件中的Bean定义必须正确,包括Bean的属性、名称、类路径等。不正确的Bean定义可能导致Spring容器初始化失败。 4. **版本兼容性** + 确保所使用的Spring框架版本与XML配置文件中的XML schema(XSD)版本兼容。不同版本可能需要不同的XSD版本。 5. **错误处理和日志记录** + 在出现问题时,查看Spring框架的错误日志和异常堆栈信息,以便更好地识别和解决配置问题。及时的错误处理和日志记录有助于定位问题并加快排查过程。 ================================================ FILE: spring-beans/spring-bean-xmlBeanDefinitionReader/pom.xml ================================================ spring-beans com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-bean-xmlBeanDefinitionReader ================================================ FILE: spring-beans/spring-bean-xmlBeanDefinitionReader/src/main/java/com/xcs/spring/XmlBeanDefinitionReaderDemo.java ================================================ package com.xcs.spring; import com.xcs.spring.bean.MyBean; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; import org.springframework.core.io.ClassPathResource; /** * @author xcs * @date 2023年11月04日 15时51分 **/ public class XmlBeanDefinitionReaderDemo { public static void main(String[] args) { // 创建一个DefaultListableBeanFactory作为Spring容器 DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); // 创建XmlBeanDefinitionReader实例用于解析XML配置 XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); // 加载XML配置文件 reader.loadBeanDefinitions(new ClassPathResource("beans.xml")); // 获取并使用Bean MyBean myBean = factory.getBean("myBean", MyBean.class); System.out.println("myBean = " + myBean); } } ================================================ FILE: spring-beans/spring-bean-xmlBeanDefinitionReader/src/main/java/com/xcs/spring/bean/MyBean.java ================================================ package com.xcs.spring.bean; /** * @author xcs * @date 2023年11月04日 15时51分 **/ public class MyBean { private String message; public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } @Override public String toString() { return "MyBean{" + "message='" + message + '\'' + '}'; } } ================================================ FILE: spring-beans/spring-bean-xmlBeanDefinitionReader/src/main/resources/beans.xml ================================================ ================================================ FILE: spring-context/pom.xml ================================================ spring-reading com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-context pom spring-context-classPathXmlApplicationContext spring-context-annotationConfigApplicationContext spring-context-genericApplicationContext ================================================ FILE: spring-context/spring-context-annotationConfigApplicationContext/README.md ================================================ ## AnnotationConfigApplicationContext - [AnnotationConfigApplicationContext](#annotationconfigapplicationcontext) - [一、基本信息](#一基本信息) - [二、知识储备](#二知识储备) - [三、基本描述](#三基本描述) - [四、最佳实践](#四最佳实践) - [五、时序图](#五时序图) - [六、源码分析](#六源码分析) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、知识储备 + **ClassPathBeanDefinitionScanner** + [ClassPathBeanDefinitionScanner](https://github.com/xuchengsheng/spring-reading/blob/master/spring-beans/spring-bean-classPathBeanDefinitionScanner/README.md) 类,用于在类路径上扫描指定包及其子包中的类,识别符合条件的类,并将其注册为 Spring Bean 的定义,从而实现组件扫描和自动装配,使我们能够方便地管理和配置应用程序中的 Bean。它允许我们定义过滤条件,以确定哪些类应被注册为 Bean,以及配合自动装配实现依赖注入,提高了应用程序的可维护性和扩展性。 + **AnnotatedBeanDefinitionReader** + [AnnotatedBeanDefinitionReader](https://github.com/xuchengsheng/spring-reading/blob/master/spring-beans/spring-bean-annotatedBeanDefinitionReader/README.md)是一个用于读取和解析带有注解的Bean定义的类,它主要用于基于注解的配置方式,允许开发者将Java类标记为Spring组件,从而让Spring容器自动扫描和注册这些组件,而不需要显式配置这些组件的Bean定义。 ### 三、基本描述 `AnnotationConfigApplicationContext` 提供了一种基于 Java 注解的轻量级、类型安全的方式来配置和管理 Spring 容器中的 bean。这种方式相对于传统的 XML 配置方式更加灵活,并且有助于减少配置文件的数量和复杂性。 ### 四、最佳实践 通过`AnnotationConfigApplicationContext`容器,以手动注册和包扫描两种方式注册Bean定义,其中手动注册了单个Bean(`MyBean`类),并通过包扫描注册了指定包路径下的所有标有`@Component`及其派生注解的类。最后,通过打印输出了所有已注册的Bean定义的名称。 ```java public class RegisterBeanDefinitionApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); // 注册Bean context.register(MyBean.class); // 扫描包 context.scan("com.xcs.spring"); // 打印Bean定义 for (String beanDefinitionName : context.getBeanDefinitionNames()) { System.out.println("beanDefinitionName = " + beanDefinitionName); } } } ``` 一个简单的Java类 `MyBean`,这个类没有任何注解或其他特殊标记。 ```java public class MyBean { } ``` 分别使用`@Controller`、`@Service`和`@Repository`。这些注解是Spring框架中用于标识不同层次组件的特殊注解。 ```java @Controller public class MyController { } @Service public class MyService { } @Repository public class MyRepository { } ``` 运行结果发现,Spring应用上下文中成功加载了配置,并注册了各种Bean定义,包括手动注册和通过注解自动注册的。 PS:前面4个是Spring容器内部的注册的Bean定义。 ```java beanDefinitionName = org.springframework.context.annotation.internalConfigurationAnnotationProcessor beanDefinitionName = org.springframework.context.annotation.internalAutowiredAnnotationProcessor beanDefinitionName = org.springframework.context.event.internalEventListenerProcessor beanDefinitionName = org.springframework.context.event.internalEventListenerFactory beanDefinitionName = myBean beanDefinitionName = myController beanDefinitionName = myRepository beanDefinitionName = myService ``` ### 五、时序图 ~~~mermaid sequenceDiagram Title: AnnotationConfigApplicationContext时序图 AnnotationConfigApplicationContextDemo->>AnnotationConfigApplicationContext: AnnotationConfigApplicationContext() Note over AnnotationConfigApplicationContextDemo,AnnotationConfigApplicationContext: 创建 AnnotationConfigApplicationContext 实例 AnnotationConfigApplicationContext->>AnnotatedBeanDefinitionReader: new AnnotatedBeanDefinitionReader(this) Note over AnnotationConfigApplicationContext,AnnotatedBeanDefinitionReader: 创建 AnnotatedBeanDefinitionReader 实例 AnnotatedBeanDefinitionReader->>AnnotationConfigApplicationContext: 返回 AnnotatedBeanDefinitionReader Note over AnnotatedBeanDefinitionReader,AnnotationConfigApplicationContext: 获取 AnnotatedBeanDefinitionReader 实例 AnnotationConfigApplicationContext->>ClassPathBeanDefinitionScanner: new ClassPathBeanDefinitionScanner(this) Note over AnnotationConfigApplicationContext,ClassPathBeanDefinitionScanner: 创建 ClassPathBeanDefinitionScanner 实例 ClassPathBeanDefinitionScanner->>AnnotationConfigApplicationContext: 返回 ClassPathBeanDefinitionScanner Note over ClassPathBeanDefinitionScanner,AnnotationConfigApplicationContext: 获取 ClassPathBeanDefinitionScanner 实例 AnnotationConfigApplicationContextDemo->>AnnotationConfigApplicationContext: register(componentClasses) Note over AnnotationConfigApplicationContextDemo,AnnotationConfigApplicationContext: 注册组件类 AnnotationConfigApplicationContext->>AnnotatedBeanDefinitionReader: this.reader.register(componentClasses) Note over AnnotationConfigApplicationContext,AnnotatedBeanDefinitionReader: 使用 AnnotatedBeanDefinitionReader 注册组件类 AnnotationConfigApplicationContextDemo->>AnnotationConfigApplicationContext: scan(basePackages) Note over AnnotationConfigApplicationContextDemo,AnnotationConfigApplicationContext: 扫描指定包 AnnotationConfigApplicationContext->>ClassPathBeanDefinitionScanner: this.scanner.scan(basePackages) Note over AnnotationConfigApplicationContext,ClassPathBeanDefinitionScanner: 使用 ClassPathBeanDefinitionScanner 扫描包 ~~~ ### 六、源码分析 在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext()`方法中,初始化`AnnotationConfigApplicationContext`实例中的两个关键组件:`AnnotatedBeanDefinitionReader`和`ClassPathBeanDefinitionScanner`,这两者分别用于处理注解式Bean定义和类路径扫描注册Bean定义。 ```java public AnnotationConfigApplicationContext() { StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create"); this.reader = new AnnotatedBeanDefinitionReader(this); createAnnotatedBeanDefReader.end(); this.scanner = new ClassPathBeanDefinitionScanner(this); } ``` 在`org.springframework.context.annotation.AnnotationConfigApplicationContext#register`方法中,主要作用是通过`AnnotatedBeanDefinitionReader`注册一组组件类,将它们解析为Spring容器中的Bean定义。 > **`this.reader.register(componentClasses)`方法的源码分析已经在另外一篇关于[AnnotatedBeanDefinitionReader](https://github.com/xuchengsheng/spring-reading/blob/master/spring-beans/spring-bean-annotatedBeanDefinitionReader/README.md)类的博客中详细分析了。** ```java @Override public void register(Class... componentClasses) { Assert.notEmpty(componentClasses, "At least one component class must be specified"); StartupStep registerComponentClass = this.getApplicationStartup().start("spring.context.component-classes.register") .tag("classes", () -> Arrays.toString(componentClasses)); this.reader.register(componentClasses); registerComponentClass.end(); } ``` 在`org.springframework.context.annotation.AnnotationConfigApplicationContext#scan`方法中,主要作用是通过`ClassPathBeanDefinitionScanner`扫描指定的基础包路径下的类,将它们解析为Spring容器中的Bean定义。 > **`this.scanner.scan(basePackages)`方法的源码分析已经在另外一篇关于[ClassPathBeanDefinitionScanner](https://github.com/xuchengsheng/spring-reading/blob/master/spring-beans/spring-bean-classPathBeanDefinitionScanner/README.md)类的博客中详细分析了。** ```java @Override public void scan(String... basePackages) { Assert.notEmpty(basePackages, "At least one base package must be specified"); StartupStep scanPackages = this.getApplicationStartup().start("spring.context.base-packages.scan") .tag("packages", () -> Arrays.toString(basePackages)); this.scanner.scan(basePackages); scanPackages.end(); } ``` ================================================ FILE: spring-context/spring-context-annotationConfigApplicationContext/pom.xml ================================================ spring-context com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-context-annotationConfigApplicationContext ================================================ FILE: spring-context/spring-context-annotationConfigApplicationContext/src/main/java/com/xcs/spring/AnnotationConfigApplicationContextDemo.java ================================================ package com.xcs.spring; import com.xcs.spring.bean.MyBean; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年11月23日 18时06分 **/ public class AnnotationConfigApplicationContextDemo { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); // 注册Bean context.register(MyBean.class); // 扫描包 context.scan("com.xcs.spring"); // 打印Bean定义 for (String beanDefinitionName : context.getBeanDefinitionNames()) { System.out.println("beanDefinitionName = " + beanDefinitionName); } } } ================================================ FILE: spring-context/spring-context-annotationConfigApplicationContext/src/main/java/com/xcs/spring/bean/MyBean.java ================================================ package com.xcs.spring.bean; /** * @author xcs * @date 2023年11月22日 11时23分 **/ public class MyBean { } ================================================ FILE: spring-context/spring-context-annotationConfigApplicationContext/src/main/java/com/xcs/spring/controller/MyController.java ================================================ package com.xcs.spring.controller; import org.springframework.stereotype.Controller; /** * @author xcs * @date 2023年11月07日 17时47分 **/ @Controller public class MyController { } ================================================ FILE: spring-context/spring-context-annotationConfigApplicationContext/src/main/java/com/xcs/spring/repository/MyRepository.java ================================================ package com.xcs.spring.repository; import org.springframework.stereotype.Repository; /** * @author xcs * @date 2023年11月07日 17时46分 **/ @Repository public class MyRepository { } ================================================ FILE: spring-context/spring-context-annotationConfigApplicationContext/src/main/java/com/xcs/spring/service/MyService.java ================================================ package com.xcs.spring.service; import org.springframework.stereotype.Service; /** * @author xcs * @date 2023年11月07日 17时46分 **/ @Service public class MyService { } ================================================ FILE: spring-context/spring-context-classPathXmlApplicationContext/README.md ================================================ ## ClassPathXmlApplicationContext - [ClassPathXmlApplicationContext](#classpathxmlapplicationcontext) - [一、基本信息](#一基本信息) - [二、知识储备](#二知识储备) - [三、基本描述](#三基本描述) - [四、主要功能](#四主要功能) - [五、最佳实践](#五最佳实践) - [六、时序图](#六时序图) - [七、源码分析](#七源码分析) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、知识储备 1. **XmlBeanDefinitionReader** + [XmlBeanDefinitionReader](https://github.com/xuchengsheng/spring-reading/blob/master/spring-beans/spring-bean-xmlBeanDefinitionReader/README.md)是Spring Framework中的一个类,用于加载和解析XML格式的Bean定义配置文件,将配置文件中定义的Bean元数据信息提取为Spring容器内部的Bean定义对象,进而实现IOC容器的构建和管理。这类负责读取XML配置文件,解析Bean的定义信息(包括ID、类名、属性、依赖等),并将这些定义注册到Spring应用程序上下文,使我们能够方便地配置和管理应用程序中的各种Bean组件。 ### 三、基本描述 `ClassPathXmlApplicationContext` 是 Spring 框架中用于从类路径(classpath)加载 XML 配置文件并初始化 Spring 容器的一种方式。它是 `ApplicationContext` 接口的实现类之一,负责读取配置文件,解析配置信息,然后创建和管理 Spring 容器中的 bean 实例。 ### 四、主要功能 1. **加载配置文件** + 主要功能是加载指定的 XML 配置文件,该配置文件包含了应用程序中各个组件(bean)的定义、依赖关系、配置信息等。 2. **容器初始化** + `ClassPathXmlApplicationContext` 在被实例化时,会读取并解析配置文件,然后初始化 Spring 容器。这个过程包括创建和管理 bean 实例、解决 bean 之间的依赖关系等。 3. **获取 bean 实例** + 通过容器的 `getBean` 方法,可以从容器中获取在配置文件中定义的 bean 实例。 4. **IoC(控制反转)** + `ClassPathXmlApplicationContext` 是 IoC 容器的一种实现,它负责管理和控制组件的生命周期。在容器初始化时,会根据配置文件中的信息实例化和装配 bean,而不是由应用程序代码直接创建对象。 5. **依赖注入** + 容器通过读取配置文件中的信息,自动解决 bean 之间的依赖关系。这意味着在配置文件中声明的 bean 可以通过属性注入或构造函数注入的方式获取其依赖的其他 bean。 6. **AOP(面向切面编程)** + `ClassPathXmlApplicationContext` 支持通过配置文件定义切面和通知,实现横切关注点的分离,使得应用程序的关注点更加清晰和模块化。 7. **事件传播** + Spring 容器支持事件机制,`ClassPathXmlApplicationContext` 可以发布应用程序中发生的事件,以便其他组件能够监听并作出相应的响应。 ### 五、最佳实践 通过 `ClassPathXmlApplicationContext` 构造方法创建 Spring 容器的实例,加载类路径下的 "beans.xml" 配置文件。使用容器的 `getBean` 方法,通过指定 bean 的类型(`MyBean.class`)获取在配置文件中定义的 bean 实例,并将其打印出来。 ```java public class ClassPathXmlApplicationContextDemo { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:beans.xml"); System.out.println("MyBean = " + context.getBean(MyBean.class)); } } ``` 加载类路径下名为 "`classpath:beans.xml`" 的资源文件的内容。在我们的示例配置文件中,这个资源文件定义了一个名为 "`myBean`" 的 Spring Bean,该 Bean 具有一个属性 "message",其值设置为 "Hello World"。 ```xml ``` `MyBean` 的Java类,代表了一个简单的Java Bean。 ```java public class MyBean { private String message; public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } } ``` ### 六、时序图 ~~~mermaid sequenceDiagram Title: ClassPathXmlApplicationContext时序图 ClassPathXmlApplicationContextDemo->>ClassPathXmlApplicationContext:ClassPathXmlApplicationContext(configLocation) Note over ClassPathXmlApplicationContextDemo,ClassPathXmlApplicationContext: 创建ClassPathXmlApplicationContext实例 ClassPathXmlApplicationContext->>ClassPathXmlApplicationContext:ClassPathXmlApplicationContext(configLocations,refresh,parent) Note over ClassPathXmlApplicationContext: 通过多个配置文件路径、刷新标志和父上下文构造实例 ClassPathXmlApplicationContext->>AbstractApplicationContext:refresh() Note over ClassPathXmlApplicationContext,AbstractApplicationContext: 刷新应用程序上下文 AbstractApplicationContext->>AbstractApplicationContext:obtainFreshBeanFactory() Note over AbstractApplicationContext: 获取刷新过的bean工厂 AbstractApplicationContext->>AbstractRefreshableApplicationContext:refreshBeanFactory() Note over AbstractApplicationContext,AbstractRefreshableApplicationContext: 调用子类的refreshBeanFactory() AbstractRefreshableApplicationContext->>AbstractXmlApplicationContext:loadBeanDefinitions(beanFactory) Note over AbstractRefreshableApplicationContext,AbstractXmlApplicationContext: 调用子类的loadBeanDefinitions方法 AbstractXmlApplicationContext->>XmlBeanDefinitionReader:new XmlBeanDefinitionReader(beanFactory) Note over AbstractXmlApplicationContext,XmlBeanDefinitionReader: 创建XmlBeanDefinitionReader实例 XmlBeanDefinitionReader->>AbstractXmlApplicationContext:返回Bean定义读取器 Note over XmlBeanDefinitionReader,AbstractXmlApplicationContext: 返回Bean定义读取器 AbstractXmlApplicationContext->>AbstractXmlApplicationContext:loadBeanDefinitions(reader) Note over AbstractXmlApplicationContext: 调用XmlBeanDefinitionReader的loadBeanDefinitions方法 AbstractXmlApplicationContext->>XmlBeanDefinitionReader:reader.loadBeanDefinitions(configLocations) Note over AbstractXmlApplicationContext,XmlBeanDefinitionReader: 加载配置文件中的Bean定义 AbstractApplicationContext->>AbstractRefreshableApplicationContext:getBeanFactory() Note over AbstractApplicationContext,AbstractRefreshableApplicationContext: 获取Bean工厂 AbstractRefreshableApplicationContext->>AbstractApplicationContext:返回Bean工厂 Note over AbstractApplicationContext: 返回Bean工厂 ~~~ ### 七、源码分析 在`org.springframework.context.support.ClassPathXmlApplicationContext#ClassPathXmlApplicationContext(configLocation)`方法中,又调用了另外一个构造方法。 ```java public ClassPathXmlApplicationContext(String configLocation) throws BeansException { this(new String[] {configLocation}, true, null); } ``` 在`org.springframework.context.support.ClassPathXmlApplicationContext#ClassPathXmlApplicationContext(configLocations, refresh,parent)`方法中,首先设置了父应用程序上下文和配置文件位置,然后根据是否需要刷新,决定是否立即执行应用程序上下文的刷新操作。 ```java public ClassPathXmlApplicationContext( String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException { super(parent); setConfigLocations(configLocations); if (refresh) { refresh(); } } ``` 在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中,调用 `obtainFreshBeanFactory` 方法来刷新内部的 bean 工厂,从而触发容器的初始化过程。 ```java @Override public void refresh() throws BeansException, IllegalStateException { // ... [代码部分省略以简化] // 该方法由子类实现,用于刷新内部的 bean 工厂。 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.AbstractApplicationContext#obtainFreshBeanFactory`方法中,首先调用 `refreshBeanFactory` 方法来刷新内部的 bean 工厂,并最终返回这个刷新过的 bean 工厂。 ```java protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { refreshBeanFactory(); return getBeanFactory(); } ``` 在`org.springframework.context.support.AbstractRefreshableApplicationContext#refreshBeanFactory`方法中,首先检查是否已存在 bean 工厂,如果存在则先销毁已有的 bean 并关闭 bean 工厂。接着,创建一个新的 `DefaultListableBeanFactory` 实例,设置其序列化 ID 为容器的 ID,然后调用 `customizeBeanFactory` 定制 bean 工厂。随后,通过 `loadBeanDefinitions` 加载 bean 的定义,包括读取配置文件、解析 bean 的定义等。 ```java @Override protected final void refreshBeanFactory() throws BeansException { // 如果已经存在 bean 工厂,则销毁已有的 bean 并关闭 bean 工厂 if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { // 创建一个新的 DefaultListableBeanFactory 实例 DefaultListableBeanFactory beanFactory = createBeanFactory(); // 设置 bean 工厂的序列化 ID 为容器的 ID beanFactory.setSerializationId(getId()); // 定制 bean 工厂,由子类实现 customizeBeanFactory(beanFactory); // 加载 bean 的定义,包括读取配置文件、解析 bean 的定义等 loadBeanDefinitions(beanFactory); // 将创建好的 bean 工厂赋值给容器 this.beanFactory = beanFactory; } catch (IOException ex) { // 如果在解析 bean 定义时发生 I/O 错误,则抛出 ApplicationContextException 异常 throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } } ``` 在`org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(beanFactory)`方法中,首先创建了一个 `XmlBeanDefinitionReader` 对象,用于读取 XML 格式的 bean 定义。然后,配置了 bean 定义阅读器的环境、资源加载器和实体解析器等属性。接着,允许子类通过 `initBeanDefinitionReader` 提供自定义的初始化操作。最后,调用阅读器的加载方法,实际上读取配置文件,解析 bean 定义,将它们注册到 bean 工厂中。 ```java protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // 为给定的 BeanFactory 创建一个新的 XmlBeanDefinitionReader XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // 使用该上下文的资源加载环境配置 bean 定义阅读器 beanDefinitionReader.setEnvironment(this.getEnvironment()); // 将资源加载器设置为当前应用程序上下文 beanDefinitionReader.setResourceLoader(this); // 设置实体解析器为 ResourceEntityResolver,用于解析 bean 定义中的实体引用 beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // 允许子类提供对阅读器的自定义初始化,然后继续实际加载 bean 定义 initBeanDefinitionReader(beanDefinitionReader); // 调用阅读器的加载方法,实际上会读取配置文件,解析 bean 定义,将它们注册到 bean 工厂中 loadBeanDefinitions(beanDefinitionReader); } ``` 在`org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(reader)`方法中,首先获取配置资源(`Resource`)数组,这些资源通过类路径方式加载的配置文件。如果配置资源不为空,则通过 `XmlBeanDefinitionReader` 对象加载这些资源中的 bean 定义。接着,获取配置文件路径数组,这些路径通过字符串形式指定的配置文件路径。如果配置文件路径不为空,则同样通过`XmlBeanDefinitionReader`来加载这些路径中的 bean 定义。 > **关于`reader.loadBeanDefinitions`方法的源码分析已经在另外一篇关于[XmlBeanDefinitionReader](https://github.com/xuchengsheng/spring-reading/blob/master/spring-beans/spring-bean-xmlBeanDefinitionReader/README.md)类的博客中详细分析了。** ```java protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { // 获取配置资源(Resource)数组,通常表示通过类路径或其他方式加载的配置文件 Resource[] configResources = getConfigResources(); // 如果配置资源不为空,则通过阅读器加载这些资源中的 bean 定义 if (configResources != null) { reader.loadBeanDefinitions(configResources); } // 获取配置文件路径数组,通常表示通过字符串形式指定的配置文件路径 String[] configLocations = getConfigLocations(); // 如果配置文件路径不为空,则通过阅读器加载这些路径中的 bean 定义 if (configLocations != null) { reader.loadBeanDefinitions(configLocations); } } ``` ================================================ FILE: spring-context/spring-context-classPathXmlApplicationContext/pom.xml ================================================ spring-context com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-context-classPathXmlApplicationContext ================================================ FILE: spring-context/spring-context-classPathXmlApplicationContext/src/main/java/com/xcs/spring/ClassPathXmlApplicationContextDemo.java ================================================ package com.xcs.spring; import com.xcs.spring.bean.MyBean; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * @author xcs * @date 2023年11月23日 16时27分 **/ public class ClassPathXmlApplicationContextDemo { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:beans.xml"); System.out.println("MyBean = " + context.getBean(MyBean.class)); } } ================================================ FILE: spring-context/spring-context-classPathXmlApplicationContext/src/main/java/com/xcs/spring/bean/MyBean.java ================================================ package com.xcs.spring.bean; /** * @author xcs * @date 2023年11月23日 16时28分 **/ public class MyBean { private String message; public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } } ================================================ FILE: spring-context/spring-context-classPathXmlApplicationContext/src/main/resources/beans.xml ================================================ ================================================ FILE: spring-context/spring-context-genericApplicationContext/pom.xml ================================================ com.xcs.spring spring-context 0.0.1-SNAPSHOT 4.0.0 spring-context-genericApplicationContext ================================================ FILE: spring-core/pom.xml ================================================ spring-reading com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-core pom spring-core-getBean spring-core-resolveDependency spring-core-registerBeanDefinition spring-core-destroyBean ================================================ FILE: spring-core/spring-core-destroyBean/README.md ================================================ ## Bean的销毁过程 - [Bean的销毁过程](#bean的销毁过程) - [一、基本信息](#一基本信息) - [二、知识储备](#二知识储备) - [三、基本描述](#三基本描述) - [四、主要功能](#四主要功能) - [五、最佳实践](#五最佳实践) - [六、时序图](#六时序图) - [七、源码分析](#七源码分析) - [注册适配器](#注册适配器) - [Bean销毁](#bean销毁) - [八、注意事项](#八注意事项) - [九、总结](#九总结) - [最佳实践总结](#最佳实践总结) - [源码分析总结](#源码分析总结) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、知识储备 1. [**Bean的定义注册过程**](https://github.com/xuchengsheng/spring-reading/tree/master/spring-core/spring-core-registerBeanDefinition) + Bean的注册包括加载和解析配置文件,从中提取Bean定义。解析后的Bean信息,如类名、作用域、属性等,被注册到Spring容器。通过解析配置文件,容器获得Bean的元数据,进而创建Bean定义,包括类名、作用域(如singleton或prototype)、属性值等。这些定义通过唯一标识符与容器关联,以便后续通过ApplicationContext获取和管理。 2. [**Bean的初始化过程**](https://github.com/xuchengsheng/spring-reading/blob/master/spring-core/spring-core-getBean/README.md) + 通过构造函数实例化Bean,随后进行属性注入满足依赖关系。若Bean实现了Aware接口,容器通过相应回调方法注入上下文信息。注册的后置处理器在初始化前后对Bean进行额外处理。Bean实现InitializingBean接口时,容器调用afterPropertiesSet执行初始化逻辑。通过配置中的init-method属性也可指定自定义初始化方法。最终,Bean标记为已初始化状态,可被应用程序使用。 3. [**Bean的依赖解析过程**](https://github.com/xuchengsheng/spring-reading/blob/master/spring-core/spring-core-resolveDependency/README.md) + 通过声明依赖,Bean表达对其他Bean的需求。容器通过查找依赖,即在ApplicationContext中寻找所需的Bean定义。然后,容器进行依赖注入,将找到的Bean实例注入到相应的属性或构造函数参数中。在处理循环依赖时,Spring通过使用提前暴露的代理对象来解决循环引用问题。最后,容器支持延迟依赖解析,即在需要使用Bean时再进行实际的依赖解析和注入,以提高性能和减少启动时间。 ### 三、基本描述 容器调用实现了`DisposableBean`接口的Bean的`destroy`方法执行自定义销毁逻辑。其次,如果Bean配置中通过`destroy-method`属性指定了自定义的销毁方法,容器也会调用。接着,对于实现了`DestructionAwareBeanPostProcessor`接口的后置处理器,容器分别调用其`postProcessBeforeDestruction`和`postProcessAfterDestruction`方法,允许进行额外的清理工作。通过`@PreDestroy`注解,我们可以在Bean方法上标记销毁前的清理操作。 ### 四、主要功能 1. **执行自定义销毁逻辑** - 调用实现了`DisposableBean`接口的Bean的`destroy`方法,执行我们定义的自定义销毁逻辑,用于释放资源或执行必要的清理工作。 2. **调用自定义销毁方法** - 如果Bean配置中通过`destroy-method`属性指定了自定义的销毁方法,容器会调用这个方法,允许我们在销毁时执行特定的清理操作。 3. **后置处理器清理工作** - 对于实现了`DestructionAwareBeanPostProcessor`接口的后置处理器,容器在销毁前后分别调用其`postProcessBeforeDestruction`和`postProcessAfterDestruction`方法,允许进行额外的清理工作。 4. **执行`@PreDestroy`注解方法** - 通过`@PreDestroy`注解,我们可以在Bean的方法上标记销毁前的清理操作,确保在销毁时执行特定的业务逻辑。 5. **触发销毁通知** - Spring容器会触发销毁通知,通知相关的监听器或观察者,允许应用程序在Bean销毁时执行特定的处理。 ### 五、最佳实践 使用 Spring 的基于注解的配置方式,创建一个应用程序上下文,注册 bean,然后关闭应用程序上下文。 ```java public class DestroyBeanApplication { public static void main(String[] args) { // 创建一个基于注解的应用程序上下文对象 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); // 注册配置类 MyBean,告诉 Spring 在容器中管理这个配置类所定义的 bean context.register(MyBean.class); // 刷新应用程序上下文,初始化并启动 Spring 容器 context.refresh(); // 关闭应用程序上下文,销毁 Spring 容器并释放资源 context.close(); } } ``` 实现了 Spring 框架中 `DisposableBean` 接口的类,通过实现这个接口,我们可以在 bean 销毁时执行一些自定义的清理逻辑。 ```java public class MyBean implements DisposableBean { @Override public void destroy() throws Exception { System.out.println("MyBean被销毁了"); } } ``` 运行结果发现,当 Spring 容器关闭时,会触发 bean 的销毁阶段,而实现了 `DisposableBean` 接口的 bean 就会调用其 `destroy` 方法。在我们的 `MyBean` 类中,`destroy` 方法中只有一行代码,即打印一条消息,因此我们在运行时看到的输出就是 `"MyBean被销毁了"`。 ```java MyBean被销毁了 ``` ### 六、时序图 ~~~mermaid sequenceDiagram Title: Bean的销毁过程时序图 par 注册适配器阶段 DestroyBeanApplication->>AbstractApplicationContext: refresh() Note over AbstractApplicationContext: 初始化应用程序上下文 AbstractApplicationContext->>AbstractApplicationContext: finishBeanFactoryInitialization(beanFactory) Note over AbstractApplicationContext: 完成bean工厂的初始化 AbstractApplicationContext->>DefaultListableBeanFactory: preInstantiateSingletons() Note over DefaultListableBeanFactory: 预实例化所有单例bean DefaultListableBeanFactory->>AbstractBeanFactory: getBean(name) Note over AbstractBeanFactory: 从bean工厂获取bean实例 AbstractBeanFactory->>AbstractBeanFactory: doGetBean(name, requiredType, args, typeCheckOnly) Note over AbstractBeanFactory: 实际获取bean的方法 AbstractBeanFactory->>DefaultSingletonBeanRegistry: getSingleton(beanName, singletonFactory) Note over DefaultSingletonBeanRegistry: 从单例注册表获取单例bean DefaultSingletonBeanRegistry->>AbstractBeanFactory: getObject() Note over AbstractBeanFactory: 获取bean的实例对象 AbstractBeanFactory->>AbstractAutowireCapableBeanFactory: createBean(beanName, mbd, args) Note over AbstractAutowireCapableBeanFactory: 创建bean实例 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory: doCreateBean(beanName, mbdToUse, args) Note over AbstractAutowireCapableBeanFactory: 实际创建bean实例的方法 AbstractAutowireCapableBeanFactory->>AbstractBeanFactory: registerDisposableBeanIfNecessary(beanName, bean, mbd) Note over AbstractBeanFactory: 注册bean的销毁适配器 AbstractBeanFactory->>DisposableBeanAdapter: new DisposableBeanAdapter(bean, beanName, beanDefinition, postProcessors, acc) Note over DisposableBeanAdapter: 创建bean的销毁适配器 DisposableBeanAdapter->>AbstractBeanFactory: 返回销毁Bean适配器 Note over AbstractBeanFactory: 返回销毁Bean适配器 AbstractBeanFactory->>DefaultSingletonBeanRegistry: registerDisposableBean(beanName, bean) Note over DefaultSingletonBeanRegistry: 注册单例bean的销毁适配器 end par Bean销毁阶段 DestroyBeanApplication->>AbstractApplicationContext: close() Note over AbstractApplicationContext: 关闭应用程序上下文 AbstractApplicationContext->>AbstractApplicationContext: doClose() Note over AbstractApplicationContext: 执行关闭操作 AbstractApplicationContext->>AbstractApplicationContext: destroyBeans() Note over AbstractApplicationContext: 销毁所有bean AbstractApplicationContext->>DefaultListableBeanFactory: destroySingletons() Note over DefaultListableBeanFactory: 销毁所有单例bean DefaultListableBeanFactory->>DefaultSingletonBeanRegistry: destroySingletons() Note over DefaultSingletonBeanRegistry: 销毁所有单例bean DefaultSingletonBeanRegistry->>DefaultSingletonBeanRegistry: destroySingleton(beanName) Note over DefaultSingletonBeanRegistry: 销毁单例bean DefaultSingletonBeanRegistry->>DefaultSingletonBeanRegistry: destroyBean(beanName, disposableBean) Note over DefaultSingletonBeanRegistry: 销毁bean的销毁适配器 DefaultSingletonBeanRegistry->>DisposableBeanAdapter: destroy() Note over DisposableBeanAdapter: 调用bean的destroy方法 DisposableBeanAdapter->>MyBean: destroy() Note over MyBean: MyBean的销毁逻辑执行 end ~~~ ### 七、源码分析 #### 注册适配器 在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 ```java @Override public void refresh() throws BeansException, IllegalStateException { // ... [代码部分省略以简化] // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization`方法中,会继续调用`DefaultListableBeanFactory`类中的`preInstantiateSingletons`方法来完成所有剩余非懒加载的单列Bean对象。 ```java protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { // ... [代码部分省略以简化] // 完成所有剩余非懒加载的单列Bean对象。 beanFactory.preInstantiateSingletons(); } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons`方法中,主要的核心目的是预先实例化所有非懒加载的单例bean。在Spring的上下文初始化完成后,该方法会被触发,以确保所有单例bean都被正确地创建并初始化。其中`getBean(beanName)`是此方法的核心操作。对于容器中定义的每一个单例bean,都会调用`getBean`方法,这将触发bean的实例化、初始化及其依赖的注入。如果bean之前没有被创建过,那么这个调用会导致其被实例化和初始化。 ```java public void preInstantiateSingletons() throws BeansException { // ... [代码部分省略以简化] // 循环遍历所有bean的名称 for (String beanName : beanNames) { getBean(beanName); } // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#getBean()`方法中,又调用了`doGetBean`方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,`doGetBean`负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。 ```java @Override public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中,首先检查所请求的bean是否是一个单例并且已经创建。如果尚未创建,将创建一个新的实例。在这个过程中,处理可能的异常情况,如循环引用,并确保返回的bean是正确的类型。这是Spring容器bean生命周期管理的核心部分。 ```java protected T doGetBean( String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException { // ... [代码部分省略以简化] // 开始创建bean实例 if (mbd.isSingleton()) { // 如果bean是单例的,我们会尝试从单例缓存中获取 // 如果不存在,则使用lambda创建一个新的实例 sharedInstance = getSingleton(beanName, () -> { try { // 尝试创建bean实例 return createBean(beanName, mbd, args); } catch (BeansException ex) { // ... [代码部分省略以简化] } }); // 对于某些bean(例如FactoryBeans),可能需要进一步处理以获取真正的bean实例 beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } // ... [代码部分省略以简化] // 确保返回的bean实例与请求的类型匹配 return adaptBeanInstance(name, beanInstance, requiredType); } ``` 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()`方法中,主要负责从单例缓存中获取一个已存在的bean实例,或者使用提供的`ObjectFactory`创建一个新的实例。这是确保bean在Spring容器中作为单例存在的关键部分。 ```java public Object getSingleton(String beanName, ObjectFactory singletonFactory) { // 断言bean名称不能为空 Assert.notNull(beanName, "Bean name must not be null"); // 同步访问单例对象缓存,确保线程安全 synchronized (this.singletonObjects) { // 从缓存中获取单例对象 Object singletonObject = this.singletonObjects.get(beanName); // 如果缓存中没有找到 if (singletonObject == null) { // ... [代码部分省略以简化] try { // 使用工厂创建新的单例实例 singletonObject = singletonFactory.getObject(); newSingleton = true; } catch (IllegalStateException ex) { // ... [代码部分省略以简化] } catch (BeanCreationException ex) { // ... [代码部分省略以简化] } finally { // ... [代码部分省略以简化] } // ... [代码部分省略以简化] } // 返回单例对象 return singletonObject; } } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()`方法中,主要的逻辑是调用 `doCreateBean`,这是真正进行 bean 实例化、属性填充和初始化的地方。这个方法会返回新创建的 bean 实例。 ```java @Override protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // ... [代码部分省略以简化] try { // 正常的bean实例化、属性注入和初始化。 // 这里是真正进行bean创建的部分。 Object beanInstance = doCreateBean(beanName, mbdToUse, args); // 记录bean成功创建的日志 if (logger.isTraceEnabled()) { logger.trace("Finished creating instance of bean '" + beanName + "'"); } return beanInstance; } catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { // ... [代码部分省略以简化] } catch (Throwable ex) { // ... [代码部分省略以简化] } } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中,主要是在创建 bean 实例的过程中注册了销毁适配器,以便在容器关闭时能够执行相应的销毁逻辑。 ```java protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // 在此注册bean以便在销毁时进行处理。 try { registerDisposableBeanIfNecessary(beanName, bean, mbd); } catch (BeanDefinitionValidationException ex) { // ... [代码部分省略以简化] } } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#registerDisposableBeanIfNecessary`方法中,首先检查Bean的作用域是否为单例且是否需要执行销毁逻辑。如果是单例作用域,注册一个`DisposableBean`适配器,负责执行各种销毁工作,包括`DestructionAwareBeanPostProcessors`、`DisposableBean`接口以及自定义的销毁方法。对于自定义作用域,查找相应的Scope并注册销毁回调。 ```java protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) { // ... [代码部分省略以简化] // 检查是否为原型作用域(prototype),以及是否需要销毁 if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) { if (mbd.isSingleton()) { // 如果是单例作用域,注册一个 DisposableBean 适配器,该适配器执行所有销毁工作 // 包括 DestructionAwareBeanPostProcessors、DisposableBean 接口、自定义销毁方法等 registerDisposableBean(beanName, new DisposableBeanAdapter( bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, acc)); } else { // 如果是自定义作用域,找到相应的 Scope 并注册销毁回调 Scope scope = this.scopes.get(mbd.getScope()); if (scope == null) { throw new IllegalStateException("No Scope registered for scope name '" + mbd.getScope() + "'"); } // 注册一个 DisposableBean 适配器,该适配器执行所有销毁工作 scope.registerDestructionCallback(beanName, new DisposableBeanAdapter( bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, acc)); } } } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#requiresDestruction`方法中,如果 bean 类型不是 `NullBean` 且存在销毁方法或与销毁相关的 `DestructionAwareBeanPostProcessor`,则返回 `true`,表示该 bean 需要执行销毁逻辑。否则,返回 `false`,表示不需要执行销毁逻辑。 ```java protected boolean requiresDestruction(Object bean, RootBeanDefinition mbd) { return (bean.getClass() != NullBean.class && (DisposableBeanAdapter.hasDestroyMethod(bean, mbd) || (hasDestructionAwareBeanPostProcessors() && DisposableBeanAdapter.hasApplicableProcessors( bean, getBeanPostProcessorCache().destructionAware)))); } ``` 在`org.springframework.beans.factory.support.DisposableBeanAdapter#DisposableBeanAdapter`方法中,主要用于处理 Bean 销毁逻辑的适配器。根据 Bean 的定义和类型信息,确定是否存在可调用的销毁方法,以及如何执行销毁逻辑。在初始化过程中,检查是否实现了 `DisposableBean` 接口、存在自定义的销毁方法,以及相关的 BeanPostProcessor。如果存在可调用的销毁方法,将进行相关的配置,以便在容器关闭时执行销毁逻辑。 ```java public DisposableBeanAdapter(Object bean, String beanName, RootBeanDefinition beanDefinition, List postProcessors, @Nullable AccessControlContext acc) { // 确保待销毁的 Bean 实例不为 null Assert.notNull(bean, "Disposable bean must not be null"); // 初始化实例变量 this.bean = bean; this.beanName = beanName; // 确定是否需要调用 DisposableBean 接口的 destroy 方法 this.invokeDisposableBean = (this.bean instanceof DisposableBean && !beanDefinition.isExternallyManagedDestroyMethod("destroy")); // 是否允许访问非公共方法 this.nonPublicAccessAllowed = beanDefinition.isNonPublicAccessAllowed(); this.acc = acc; // 推断销毁方法的名称 String destroyMethodName = inferDestroyMethodIfNecessary(bean, beanDefinition); // 如果存在销毁方法,并且不是外部管理的 destroy 方法,则进行相关处理 if (destroyMethodName != null && !(this.invokeDisposableBean && "destroy".equals(destroyMethodName)) && !beanDefinition.isExternallyManagedDestroyMethod(destroyMethodName)) { // 记录销毁方法的名称 this.destroyMethodName = destroyMethodName; // 确定销毁方法 Method destroyMethod = determineDestroyMethod(destroyMethodName); // 如果找不到销毁方法,并且 Bean 定义要求强制存在,则抛出异常 if (destroyMethod == null) { // ... [代码部分省略以简化] } // 否则,检查销毁方法的合法性 else { if (destroyMethod.getParameterCount() > 0) { Class[] paramTypes = destroyMethod.getParameterTypes(); // ... [代码部分省略以简化] } // 获取销毁方法的接口方法(如果存在) destroyMethod = ClassUtils.getInterfaceMethodIfPossible(destroyMethod); } // 记录销毁方法 this.destroyMethod = destroyMethod; } // 过滤与销毁相关的 BeanPostProcessor this.beanPostProcessors = filterPostProcessors(postProcessors, bean); } ``` 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#registerDisposableBean`方法中, ```java public void registerDisposableBean(String beanName, DisposableBean bean) { // 使用同步块确保线程安全 synchronized (this.disposableBeans) { // 将待销毁的 Bean 放入 disposableBeans 集合中 this.disposableBeans.put(beanName, bean); } } ``` #### Bean销毁 在`org.springframework.context.support.AbstractApplicationContext#close`方法中,首先是启动了一个同步块,它同步在 `startupShutdownMonitor` 对象上。这确保了在给定时刻只有一个线程可以执行这个块内的代码,防止多线程导致的资源竞争或数据不一致,然后是调用了 `doClose` 方法。 ```java @Override public void close() { synchronized (this.startupShutdownMonitor) { doClose(); // ... [代码部分省略以简化] } } ``` 在`org.springframework.context.support.AbstractApplicationContext#doClose`方法中,又调用了 `destroyBeans` 方法。 ```java protected void doClose() { // ... [代码部分省略以简化] // Destroy all cached singletons in the context's BeanFactory. destroyBeans(); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.AbstractApplicationContext#destroyBeans`方法中,首先是调用了`getBeanFactory()`返回 Spring 的 `BeanFactory` ,然后在获得的 `BeanFactory` 上,调用了 `destroySingletons` 方法,这个方法的目的是销毁所有在 `BeanFactory` 中缓存的单例 beans。 ```java protected void destroyBeans() { getBeanFactory().destroySingletons(); } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#destroySingletons`方法中,首先是调用了父类的 `destroySingletons` 方法,为了确保继承自父类的销毁逻辑得到了执行。 ```java @Override public void destroySingletons() { super.destroySingletons(); // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#destroySingletons`方法中,首先是在`disposableBeans` 字段上,从其键集合中获取所有的 bean 名称,并将它们转换为一个字符串数组。`disposableBeans` 可能包含了实现了 `DisposableBean` 接口的 beans,这些 beans 需要在容器销毁时特殊处理,最后倒序循环,从最后一个开始,销毁所有在 `disposableBeans` 列表中的 beans。这样做是为了确保依赖关系正确地处理,beans先被创建的应该后被销毁。 ```java public void destroySingletons() { // ... [代码部分省略以简化] String[] disposableBeanNames; synchronized (this.disposableBeans) { disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet()); } for (int i = disposableBeanNames.length - 1; i >= 0; i--) { destroySingleton(disposableBeanNames[i]); } // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#destroySingleton`方法中,首先是调用了父类的 `destroySingleton` 方法,为了确保继承自父类的销毁逻辑得到了执行。 ```java @Override public void destroySingleton(String beanName) { super.destroySingleton(beanName); // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#destroySingleton`方法中,首先是使用 `synchronized` 关键字在 `disposableBeans` 对象上进行同步,以确保在多线程环境中安全地访问和修改它,从 `disposableBeans` 集合中移除指定名称的 bean,并将其转换为 `DisposableBean` 类型,最后调用`destroyBean`方法执行实际的销毁操作。 ```java public void destroySingleton(String beanName) { // 从已注册的单例中移除指定名称的单例 Bean removeSingleton(beanName); // 获取对应的 DisposableBean 实例 DisposableBean disposableBean; synchronized (this.disposableBeans) { disposableBean = (DisposableBean) this.disposableBeans.remove(beanName); } // 执行销毁逻辑 destroyBean(beanName, disposableBean); } ``` 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#destroyBean`方法中,主要用于销毁给定名称的 Bean,包括触发其依赖关系的销毁、执行 Bean 的 `destroy` 方法以及销毁该 Bean 包含的其他 Bean,最后,清理已销毁 Bean 的依赖关系和准备好的依赖信息。 ```java protected void destroyBean(String beanName, @Nullable DisposableBean bean) { // 触发销毁依赖于该 Bean 的其他 Bean... Set dependencies; // 在完全同步的情况下,以确保获取的 Set 是线程安全的 synchronized (this.dependentBeanMap) { dependencies = this.dependentBeanMap.remove(beanName); } if (dependencies != null) { // ... [代码部分省略以简化] for (String dependentBeanName : dependencies) { destroySingleton(dependentBeanName); } } // 实际执行 Bean 的销毁逻辑... if (bean != null) { try { bean.destroy(); } catch (Throwable ex) { // ... [代码部分省略以简化] } } // 触发销毁该 Bean 包含的其他 Bean... Set containedBeans; synchronized (this.containedBeanMap) { // 在完全同步的情况下,以确保获取的 Set 是断开连接的 containedBeans = this.containedBeanMap.remove(beanName); } if (containedBeans != null) { for (String containedBeanName : containedBeans) { destroySingleton(containedBeanName); } } // 从其他 Bean 的依赖关系中移除已销毁的 Bean... synchronized (this.dependentBeanMap) { for (Iterator>> it = this.dependentBeanMap.entrySet().iterator(); it.hasNext();) { Map.Entry> entry = it.next(); Set dependenciesToClean = entry.getValue(); dependenciesToClean.remove(beanName); if (dependenciesToClean.isEmpty()) { it.remove(); } } } // 移除已销毁 Bean 的准备好的依赖信息 this.dependenciesForBeanMap.remove(beanName); } ``` 在`org.springframework.beans.factory.support.DisposableBeanAdapter#destroy`方法中,实现了 `DisposableBean` 接口的销毁方法,负责在 Bean 销毁阶段执行各个销毁相关的操作。这包括前置销毁处理、调用 `DisposableBean` 接口的 `destroy` 方法、执行自定义的销毁方法。 ```java @Override public void destroy() { // 执行 DestructionAwareBeanPostProcessor 的前置销毁处理 if (!CollectionUtils.isEmpty(this.beanPostProcessors)) { for (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) { processor.postProcessBeforeDestruction(this.bean, this.beanName); } } // 如果实现了 DisposableBean 接口,则调用其 destroy 方法 if (this.invokeDisposableBean) { // ... [代码部分省略以简化] try { // ... [代码部分省略以简化] ((DisposableBean) this.bean).destroy(); } catch (Throwable ex) { // ... [代码部分省略以简化] } } // 如果存在自定义的销毁方法,则执行 if (this.destroyMethod != null) { invokeCustomDestroyMethod(this.destroyMethod); } // 如果存在指定名称的自定义销毁方法,则查找并执行 else if (this.destroyMethodName != null) { Method methodToInvoke = determineDestroyMethod(this.destroyMethodName); if (methodToInvoke != null) { // 获取接口方法(如果存在)并执行自定义销毁方法 invokeCustomDestroyMethod(ClassUtils.getInterfaceMethodIfPossible(methodToInvoke)); } } } ``` ### 八、注意事项 1. **销毁方法避免抛出异常** + 在销毁方法中应尽量避免抛出异常,因为抛出的异常可能会影响到其他Bean的销毁过程。如果有异常,最好进行适当的记录。 2. **注意Bean的依赖关系** + 确保销毁Bean的顺序符合依赖关系,首先销毁依赖关系较少的Bean,然后再销毁依赖关系较多的Bean。Spring容器会尽量按照依赖关系的顺序销毁Bean。 3. **注意Bean的生命周期和作用域** + 对于单例(Singleton)Bean,其销毁过程会在容器关闭时触发。对于原型(Prototype)Bean,Spring容器不会负责销毁,需要手动管理。 ### 九、总结 #### 最佳实践总结 1. **创建应用程序上下文** + 使用`AnnotationConfigApplicationContext`创建一个基于注解的应用程序上下文对象。 2. **注册Bean** + 使用`context.register(MyBean.class)`注册了`MyBean`类,告诉Spring容器要管理这个配置类所定义的bean。 3. **刷新应用程序上下文** + 使用`context.refresh()`刷新应用程序上下文,初始化并启动Spring容器。 4. **容器关闭** + 使用`context.close()`关闭应用程序上下文。在关闭过程中,Spring容器会触发bean的销毁阶段。 5. **Bean销毁** + 由于`MyBean`实现了`DisposableBean`接口,因此在容器关闭时,会调用`MyBean`的`destroy`方法。在`destroy`方法中,我们简单地打印一条消息,表示`MyBean`被销毁了。 + 最后,控制台输出了`MyBean被销毁了`的消息,证明了bean的销毁过程已经执行。 #### 源码分析总结 1. **注册Bean销毁适配器** + 在`AbstractApplicationContext`的`finishBeanFactoryInitialization`方法中,调用了`DefaultListableBeanFactory`的`preInstantiateSingletons`方法,该方法会预先实例化所有非懒加载的单例bean。在实例化的过程中,对于每个bean,会调用`registerDisposableBeanIfNecessary`方法注册销毁适配器,以确保在容器关闭时能够执行相应的销毁逻辑。 2. **Bean销毁** + 在容器关闭时,通过`AbstractApplicationContext`的`close`方法触发销毁过程。在`destroyBeans`方法中,调用了`DefaultListableBeanFactory`的`destroySingletons`方法,该方法销毁所有在`BeanFactory`中缓存的单例beans。 + 在`destroySingletons`方法中,遍历`disposableBeans`集合,获取所有待销毁的bean名称,并倒序循环销毁这些bean。在销毁过程中,首先调用`destroyBean`方法执行实际的销毁操作。 + 在`destroyBean`方法中,触发销毁依赖于该bean的其他bean,执行bean的`destroy`方法,销毁bean包含的其他bean,从其他bean的依赖关系中移除已销毁的bean,清理已销毁bean的准备好的依赖信息。 + 最后通过`DisposableBeanAdapter`类处理Bean的销毁逻辑。该类实现了`DisposableBean`接口的`destroy`方法,执行前置销毁处理、调用`DisposableBean`接口的`destroy`方法、执行自定义的销毁方法等操作。 ================================================ FILE: spring-core/spring-core-destroyBean/pom.xml ================================================ spring-core com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-core-destroyBean ================================================ FILE: spring-core/spring-core-destroyBean/src/main/java/com/xcs/spring/DestroyBeanApplication.java ================================================ package com.xcs.spring; import com.xcs.spring.bean.MyBean; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年11月22日 14时27分 **/ public class DestroyBeanApplication { public static void main(String[] args) { // 创建一个基于注解的应用程序上下文对象 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); // 注册配置类 MyBean,告诉 Spring 在容器中管理这个配置类所定义的 bean context.register(MyBean.class); // 刷新应用程序上下文,初始化并启动 Spring 容器 context.refresh(); // 关闭应用程序上下文,销毁 Spring 容器并释放资源 context.close(); } } ================================================ FILE: spring-core/spring-core-destroyBean/src/main/java/com/xcs/spring/bean/MyBean.java ================================================ package com.xcs.spring.bean; import org.springframework.beans.factory.DisposableBean; /** * @author xcs * @date 2023年11月22日 14时28分 **/ public class MyBean implements DisposableBean { @Override public void destroy() throws Exception { System.out.println("MyBean被销毁了"); } } ================================================ FILE: spring-core/spring-core-getBean/README.md ================================================ ## getBean - [getBean](#getbean) - [一、基本信息](#一基本信息) - [二、方法源码](#二方法源码) - [三、主要功能](#三主要功能) - [四、最佳实践](#四最佳实践) - [五、时序图](#五时序图) - [六、源码分析](#六源码分析) - [七、注意事项](#七注意事项) - [八、总结](#八总结) - [最佳实践总结](#最佳实践总结) - [源码分析总结](#源码分析总结) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [我的CSDN]() 📚 **文章目录** - [源码地址](https://github.com/xuchengsheng/spring-reading) 在 Spring 框架中,`getBean` 方法是 `ApplicationContext` 接口中的一个核心方法,用于从 Spring 容器中检索 bean。Spring 的核心是控制反转(Inversion of Control, IoC)和依赖注入(Dependency Injection, DI),`getBean` 方法正是实现这两个核心概念的重要方法。 ### 二、方法源码 这个方法的定义和说明表明了 Spring IoC 容器的一些核心概念和工作机制。当我们请求一个 bean 时,Spring 会查找该 bean、处理任何别名、检查其作用域(例如,单例或原型),并最终返回适当的 bean 实例给调用者。 ```java /** * 返回指定bean的实例,该实例可能是共享的或独立的。 * 此方法使Spring BeanFactory可以替代单例或原型设计模式。在单例bean的情况下,调用者可以保留返回对象的引用。 * 将别名转换回相应的规范bean名称。 * 如果在这个工厂实例中找不到bean,将询问父工厂。 * * @param name 要检索的bean的名称 * @return bean的实例 * @throws NoSuchBeanDefinitionException 如果没有指定名称的bean * @throws BeansException 如果无法获取bean */ Object getBean(String name) throws BeansException; ``` ### 三、主要功能 1. **检索 Bean** + 从 Spring 容器中检索并返回指定名称或类型的 bean 的实例。 2. **作用域处理** + 根据 bean 的配置和作用域(例如 "singleton" 或 "prototype"),`getBean` 可以返回单例的 bean 实例或每次都创建一个新的实例。 3. **别名处理** + 如果 bean 有别名,`getBean` 可以根据这些别名解析并返回相应的 bean 实例。 4. **考虑父容器** + 如果在当前容器中找不到 bean,但容器有父容器,则 `getBean` 会在父容器中查找该 bean。 5. **类型转换** + `getBean` 还有一个重载版本,允许用户指定返回 bean 的类型,这样可以避免在后续使用中进行显式的类型转换。 6. **异常处理** + 如果容器中不存在指定的 bean,它会抛出 `NoSuchBeanDefinitionException`。如果在尝试创建或检索 bean 时出现其他问题,它会抛出 `BeansException`。 7. **支持依赖查找** + 尽管 Spring 的主要目标是通过依赖注入提供依赖关系,但 `getBean` 方法提供了一种手动查找依赖的方式。 8. **初始化 Bean** + 如果 bean 尚未初始化(例如,对于单例 bean 在首次请求时),`getBean` 方法会触发其初始化。 ### 四、最佳实践 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类,然后从Spring上下文中获取两个Bean对象`myServiceA`,`myServiceB`类型的bean。 ```java public class GetBeanApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); System.out.println("myServiceA = " + context.getBean("myServiceA")); System.out.println("myServiceB = " + context.getBean("myServiceB")); } } ``` 在配置类中,使用`@ComponentScan`注解让Spring扫描`com.xcs.spring.service`包以及其子包,由此扫描到的任何类,如果它们上面有特定的注解(如`@Component`, `@Service`, `@Repository`, `@Controller`等),都会被Spring自动识别并添加到容器中,成为容器管理的bean。 ```java @Configuration @ComponentScan("com.xcs.spring.service") public class MyConfiguration { } ``` 由于我们配置中启用了`@ComponentScan`(如在`MyConfiguration`类中)并指定了正确的包路径,那么这两个类将被自动识别并注册到Spring容器中。 ```java package com.xcs.spring.service; @Component public class MyServiceA { } @Component public class MyServiceB { } ``` 运行结果发现,这是我们自己定义的两个服务类。它们都被标记为`@Component`,因此Spring容器会为每个类创建一个bean实例。 ```java myServiceA = com.xcs.spring.service.MyServiceA@23c30a20 myServiceB = com.xcs.spring.service.MyServiceB@1e1a0406 ``` ### 五、时序图 ~~~mermaid sequenceDiagram DefaultListableBeanFactory->>AbstractBeanFactory:getBean(name) note over AbstractBeanFactory: 请求一个Bean AbstractBeanFactory->>AbstractBeanFactory:doGetBean(name,requiredType,args,typeCheckOnly) note over AbstractBeanFactory: 执行实际的获取Bean逻辑 AbstractBeanFactory->>AbstractBeanFactory:transformedBeanName(name) note over AbstractBeanFactory: 获取真正的bean名称 AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton(beanName) note over DefaultSingletonBeanRegistry: 检查Bean是否为单例 DefaultSingletonBeanRegistry->>DefaultSingletonBeanRegistry:getSingleton(beanName,allowEarlyReference) note over DefaultSingletonBeanRegistry: 如果允许,检查早期引用的单例Bean DefaultSingletonBeanRegistry->>AbstractBeanFactory:返回已注册的singleton对象 note over AbstractBeanFactory: 如果已注册,则返回这个单例Bean AbstractBeanFactory->>DefaultListableBeanFactory:返回Bean对象 note over DefaultListableBeanFactory: 返回到原始的请求源 note over AbstractBeanFactory: Bean不在缓存中,需要创建 AbstractBeanFactory->>AbstractBeanFactory:getParentBeanFactory() note over AbstractBeanFactory: 检查是否有父Bean工厂 AbstractBeanFactory->>DefaultListableBeanFactory:parentBeanFactory.getBean(name) note over DefaultListableBeanFactory: 在父工厂中请求Bean AbstractBeanFactory->>AbstractBeanFactory:markBeanAsCreated(beanName) note over AbstractBeanFactory: 标记该Bean为已创建 AbstractBeanFactory->>AbstractBeanFactory:getMergedLocalBeanDefinition(beanName) note over AbstractBeanFactory: 获取合并后的Bean定义 AbstractBeanFactory->>AbstractBeanFactory:checkMergedBeanDefinition(mbd, beanName, args) note over AbstractBeanFactory: 检查合并后的Bean定义是否有效 AbstractBeanFactory->>AbstractBeanDefinition:getDependsOn() note over AbstractBeanFactory: 获取该Bean的依赖 AbstractBeanFactory->>DefaultSingletonBeanRegistry:isDependent(beanName, dep) note over DefaultSingletonBeanRegistry: 检查是否存在依赖 DefaultSingletonBeanRegistry->>DefaultSingletonBeanRegistry:isDependent(beanName, dependentBeanName, null) note over DefaultSingletonBeanRegistry: 检查依赖 DefaultSingletonBeanRegistry->>DefaultSingletonBeanRegistry:canonicalName(beanName) note over DefaultSingletonBeanRegistry: 获取Bean的规范名称 DefaultSingletonBeanRegistry->>AbstractBeanFactory:返回是否存在循环依赖的情况 note over AbstractBeanFactory: 返回循环依赖的检查结果 note over AbstractBeanFactory: 如果存在循环依赖,则抛出异常 throw new BeanCreationException(""Circular depends-on relationship between") AbstractBeanFactory->>DefaultSingletonBeanRegistry:registerDependentBean(dep, beanName) note over DefaultSingletonBeanRegistry: 注册依赖关系 AbstractBeanFactory->>DefaultListableBeanFactory:getBean(name) note over DefaultListableBeanFactory: 获取被依赖的bean对象 AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton(beanName,singletonFactory) note over DefaultSingletonBeanRegistry: 获取或创建单例Bean DefaultSingletonBeanRegistry->>DefaultSingletonBeanRegistry:beforeSingletonCreation(beanName) note over DefaultSingletonBeanRegistry: 在创建单例之前的准备工作 DefaultSingletonBeanRegistry->>AbstractBeanFactory:singletonFactory.getObject() note over AbstractBeanFactory: 使用单例工厂创建Bean AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean(beanName, mbd, args) note over AbstractAutowireCapableBeanFactory: 创建新的Bean实例 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:resolveBeanClass(mbd, beanName) note over AbstractAutowireCapableBeanFactory: 解析Bean的类 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:resolveBeforeInstantiation(beanName, mbdToUse) note over AbstractAutowireCapableBeanFactory: 在实例化前尝试解析Bean AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:doCreateBean(beanName,mbd,args) note over AbstractAutowireCapableBeanFactory: 执行实际的Bean创建 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:createBeanInstance(beanName, mbd, args) note over AbstractAutowireCapableBeanFactory: 创建Bean实例 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:determineConstructorsFromBeanPostProcessors(beanClass, beanName) note over AbstractAutowireCapableBeanFactory: 从SmartInstantiationAwareBeanPostProcessor确定构造器 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:instantiateBean(beanName, mbd) note over AbstractAutowireCapableBeanFactory: 实例化Bean AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName) note over AbstractAutowireCapableBeanFactory: 应用合并后的Bean定义后处理器 AbstractAutowireCapableBeanFactory->>DefaultSingletonBeanRegistry:addSingletonFactory(beanName,singletonFactory) note over DefaultSingletonBeanRegistry: 添加单例工厂 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:populateBean(beanName, mbd, instanceWrapper) note over AbstractAutowireCapableBeanFactory: 填充Bean的属性 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:postProcessAfterInstantiation(bean,beanName) note over AbstractAutowireCapableBeanFactory: 实例化后的后处理 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:postProcessProperties(pvs,bean,beanName) note over AbstractAutowireCapableBeanFactory: 属性后处理 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:postProcessPropertyValues(pvs,pds,bean,beanName) note over AbstractAutowireCapableBeanFactory: 属性后处理 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:applyPropertyValues(beanName,mbd,bw,pvs) note over AbstractAutowireCapableBeanFactory: 应用属性值 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:initializeBean(beanName, exposedObject, mbd) note over AbstractAutowireCapableBeanFactory: 初始化Bean AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:invokeAwareMethods(beanName, bean) note over AbstractAutowireCapableBeanFactory: 调用Aware接口方法 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName) note over AbstractAutowireCapableBeanFactory: 在初始化前应用BeanPostProcessors AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:invokeInitMethods(beanName, wrappedBean, mbd) note over AbstractAutowireCapableBeanFactory: 调用初始化方法 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName) note over AbstractAutowireCapableBeanFactory: 在初始化后应用BeanPostProcessors AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:registerDisposableBeanIfNecessary(beanName, bean, mbd) note over AbstractAutowireCapableBeanFactory: 如果需要,注册可销毁的Bean AbstractAutowireCapableBeanFactory->>AbstractBeanFactory:返回创建的单例Bean AbstractBeanFactory->>DefaultSingletonBeanRegistry:返回创建的单例Bean DefaultSingletonBeanRegistry->>DefaultSingletonBeanRegistry:afterSingletonCreation(beanName) note over DefaultSingletonBeanRegistry: 单例创建后的后续处理 DefaultSingletonBeanRegistry->>DefaultSingletonBeanRegistry:addSingleton(beanName, singletonObject) note over DefaultSingletonBeanRegistry: 向注册表中添加新的单例Bean DefaultSingletonBeanRegistry->>AbstractBeanFactory:返回创建的单例Bean AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:getObjectForBeanInstance(sharedInstance, name, beanName, mbd) note over AbstractAutowireCapableBeanFactory: 获取Bean实例的对象 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:处理FactoryBean note over AbstractAutowireCapableBeanFactory: 如果是FactoryBean,则处理 AbstractAutowireCapableBeanFactory->>AbstractBeanFactory:返回真正的Bean对象 note over AbstractBeanFactory: 返回真实的Bean对象,而不是FactoryBean AbstractBeanFactory->>AbstractBeanFactory:adaptBeanInstance(name, beanInstance, requiredType) note over AbstractBeanFactory: 适配Bean实例的类型 AbstractBeanFactory->>DefaultListableBeanFactory:返回真正的Bean对象 note over DefaultListableBeanFactory: 返回到原始的请求源 ~~~ ### 六、源码分析 在`org.springframework.beans.factory.support.AbstractBeanFactory#getBean(name)`方法中,提供了一个简单的方式,让调用者能够基于bean的名称从Spring IoC容器中检索bean,而不需要提供任何其他的上下文信息或参数。 ```java @Override public Object getBean(String name) throws BeansException { // 调用doGetBean方法来真正的获取bean。 // 参数说明: // 1. name: 要获取的bean的名称。 // 2. null: bean的所需类型,这里为null表示没有指定具体类型。 // 3. null: 构造函数或工厂方法的参数,这里为null表示默认构造方法或工厂方法。 // 4. false: 指定是否仅进行类型检查,false表示需要实例化bean。 return doGetBean(name, null, null, false); } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中,是Spring框架中`AbstractBeanFactory`类的核心方法,用于获取bean实例。它考虑了单例、原型、特定作用域bean的创建,还处理了bean定义、循环引用、依赖等各种情况。 ```java protected T doGetBean( String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException { // 步骤1: 转换bean名称 String beanName = transformedBeanName(name); // 步骤2: 尝试从缓存中检索单例bean Object sharedInstance = getSingleton(beanName); Object beanInstance; if (sharedInstance != null && args == null) { // 获取bean实例本身 beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null); } else { // 步骤3: 处理原型作用域的bean,并检查是否已在创建中 if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } // 步骤4: 尝试在父Bean工厂中检索bean定义 BeanFactory parentBeanFactory = getParentBeanFactory(); if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { String nameToLookup = originalBeanName(name); // ... [代码部分省略以简化] return (T) parentBeanFactory.getBean(nameToLookup); } // 步骤5: 标记bean为已创建状态 if (!typeCheckOnly) { markBeanAsCreated(beanName); } try { // 步骤6: 获取合并后的bean定义 RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); checkMergedBeanDefinition(mbd, beanName, args); // 步骤7: 确保bean的依赖已经初始化 String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) { for (String dep : dependsOn) { // 步骤7.1: 是否存在循环依赖 if (isDependent(beanName, dep)) { // ... [代码部分省略以简化] } // 步骤7.2: 注册Bean与Bean之间的依赖关系 registerDependentBean(dep, beanName); // 步骤7.3: 获取被依赖的Bean对象 getBean(dep); } } // 步骤8: 根据bean的作用域,创建或检索bean实例 if (mbd.isSingleton()) { // 步骤8.1: 处理单例作用域 sharedInstance = getSingleton(beanName, () -> { try { // 步骤8.2: 创建Bean return createBean(beanName, mbd, args); } catch (BeansException ex) { // ... [代码部分省略以简化] } }); // 步骤8.3: 获取bean实例本身 beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } else if (mbd.isPrototype()) { // 处理原型作用域 // ... [代码部分省略以简化] } else { // 处理其他作用域 // ... [代码部分省略以简化] } } catch (BeansException ex) { // 处理bean创建失败的情况 // ... [代码部分省略以简化] } finally { // ... [代码部分省略以简化] } } // 步骤9: 适配bean实例 return adaptBeanInstance(name, beanInstance, requiredType); } ``` > `org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中的步骤1。 在`org.springframework.beans.factory.support.AbstractBeanFactory#transformedBeanName`方法中,主要作用是对给定的bean名称进行转换,确保返回的名称是规范的、没有任何前缀,并处理可能的别名。 ```java protected String transformedBeanName(String name) { // 首先,使用BeanFactoryUtils的transformedBeanName方法来处理传入的name。 // 这通常用于移除bean名称前缀,例如"&",这在工厂beans中使用。 // 使用canonicalName方法来获取别名映射后的真实bean名称。 return canonicalName(BeanFactoryUtils.transformedBeanName(name)); } ``` 在`org.springframework.core.SimpleAliasRegistry#canonicalName`方法中,持续地从别名映射中查找真实的bean名称,直到找不到更多的别名为止,从而确保返回的是真实的bean名称,而不是任何别名。 ```java public String canonicalName(String name) { // 初始化canonicalName为传入的name String canonicalName = name; // 循环处理别名映射 String resolvedName; do { // 从别名映射中获取真实的bean名称 resolvedName = this.aliasMap.get(canonicalName); // 如果找到了一个真实的bean名称(即resolvedName不为null),则更新canonicalName为这个新找到的名称 if (resolvedName != null) { canonicalName = resolvedName; } } // 如果还可以在aliasMap中找到resolvedName的别名,继续循环 while (resolvedName != null); // 返回最终确定的bean名称 return canonicalName; } ``` > `org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中的步骤2。 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(beanName)`方法中,目的是简化单例bean的获取过程。它默认允许在bean正在创建过程中返回早期的bean引用,这在解决循环依赖的场景中是有用的。 ```java @Override @Nullable public Object getSingleton(String beanName) { // 调用重载的getSingleton方法来获取单例bean。 // 参数说明: // 1. beanName: 要获取的单例bean的名称。 // 2. true: 表示如果当前bean正在创建中(例如处理循环引用的情况),则允许返回早期的单例bean引用。 return getSingleton(beanName, true); } ``` 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(beanName,allowEarlyReference)`方法中,主要目的是检索beanName指定的单例对象,考虑了多种可能的缓存位置,包括完全初始化的缓存、早期的单例对象缓存和单例工厂缓存。如果bean目前正在创建中(这可能是由于循环引用),该方法还会处理这种情况。 ```java @Nullable protected Object getSingleton(String beanName, boolean allowEarlyReference) { // 尝试从缓存中快速检索已存在的bean实例,避免完全锁定单例 Object singletonObject = this.singletonObjects.get(beanName); // 如果找不到实例,并且该bean当前正在创建中(例如,处理循环引用) if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { singletonObject = this.earlySingletonObjects.get(beanName); // 如果允许提前引用并且在早期单例对象中仍未找到 if (singletonObject == null && allowEarlyReference) { synchronized (this.singletonObjects) { // 在完整的单例锁定范围内,确保早期引用的一致性创建 singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { singletonObject = this.earlySingletonObjects.get(beanName); // 如果在早期的单例对象中仍然找不到,并且存在一个单例工厂来创建这个bean if (singletonObject == null) { ObjectFactory singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { // 使用单例工厂创建bean singletonObject = singletonFactory.getObject(); // 将新创建的bean存放到早期单例对象缓存中 this.earlySingletonObjects.put(beanName, singletonObject); // 从单例工厂缓存中删除对应的工厂 this.singletonFactories.remove(beanName); } } } } } } // 返回找到的单例bean实例,如果没有找到则返回null return singletonObject; } ``` > `org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中的步骤3。 在`org.springframework.beans.factory.support.AbstractBeanFactory#isPrototypeCurrentlyInCreation`方法中,检查一个特定的bean名称是否正在创建中的原型beans列表中。这是为了处理可能出现的原型bean的循环引用。 ```java protected boolean isPrototypeCurrentlyInCreation(String beanName) { // 获取当前正在创建的原型bean的值 Object curVal = this.prototypesCurrentlyInCreation.get(); // 检查当前值是否不为空,并且 // 1) 当前值是否等于给定的bean名称,或者 // 2) 当前值是否是一个Set并且该Set包含给定的bean名称 return (curVal != null && (curVal.equals(beanName) || (curVal instanceof Set && ((Set) curVal).contains(beanName)))); } ``` > `org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中的步骤5。 在`org.springframework.beans.factory.support.AbstractBeanFactory#markBeanAsCreated`方法中,主要目的是标记指定的bean已经被创建或正在被创建。它在Spring的bean生命周期中起到关键作用,特别是当需要确保bean只被创建一次或者对其进行某些状态检查时。 ```java protected void markBeanAsCreated(String beanName) { // 1. 初步检查bean是否已被标记为已创建 if (!this.alreadyCreated.contains(beanName)) { // 2. 为了确保在多线程环境下的线程安全,进行同步操作 synchronized (this.mergedBeanDefinitions) { // 3. 双重检查锁定模式:再次确认bean是否已被标记为已创建 if (!this.alreadyCreated.contains(beanName)) { // 4. 清除bean的合并定义,以便在后续访问时重新合并 clearMergedBeanDefinition(beanName); // 5. 在集合中标记bean已被创建 this.alreadyCreated.add(beanName); } } } } ``` > `org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中的步骤6。 在`org.springframework.beans.factory.support.AbstractBeanFactory#getMergedLocalBeanDefinition`方法中,主要是用于获取给定bean名称的合并bean定义。合并的bean定义是从父bean和子bean(如果有的话)定义中合并的结果。 ```java protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException { // 1. 快速从并发映射中检查bean定义,这样做可以最小化锁定。 RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName); // 2. 如果合并的bean定义存在并且没有过期,直接返回它。 if (mbd != null && !mbd.stale) { return mbd; } // 3. 如果上述检查失败,进一步获取并返回合并的bean定义。 return getMergedBeanDefinition(beanName, getBeanDefinition(beanName)); } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#getBeanDefinition`方法中,主要用于从当前Bean工厂的bean定义映射中检索指定名称的bean定义。如果没有找到指定的bean定义,它会抛出一个`NoSuchBeanDefinitionException`异常。 ```java @Override public BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException { // 1. 从beanDefinitionMap中获取bean的定义 BeanDefinition bd = this.beanDefinitionMap.get(beanName); // 2. 如果没有找到BeanDefinition,进行日志跟踪并抛出异常 if (bd == null) { // 如果启用了trace级别的日志,记录一条日志 if (logger.isTraceEnabled()) { logger.trace("No bean named '" + beanName + "' found in " + this); } // 抛出没有找到BeanDefinition的异常 throw new NoSuchBeanDefinitionException(beanName); } // 3. 返回找到的BeanDefinition return bd; } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#getMergedBeanDefinition(beanName, bd)`方法中,又调用了另一个`getMergedBeanDefinition`方法版本,为给定的Bean名称和Bean定义获取一个合并的`RootBeanDefinition`。 ```java protected RootBeanDefinition getMergedBeanDefinition(String beanName, BeanDefinition bd) throws BeanDefinitionStoreException { // 为给定的Bean名称和Bean定义获取一个合并的RootBeanDefinition, // 由于这个版本的方法没有提供一个父Bean定义,所以我们传递null作为第三个参数。 return getMergedBeanDefinition(beanName, bd, null); } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#getMergedBeanDefinition(beanName,bd,containingBd)`方法中,主要目的是获取指定bean名称的合并bean定义。它的主要工作是处理bean定义的父子关系和其他相关设置,然后返回一个合并后的bean定义。 ```java protected RootBeanDefinition getMergedBeanDefinition( String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd) throws BeanDefinitionStoreException { // 1. 对mergedBeanDefinitions进行同步以确保线程安全。 synchronized (this.mergedBeanDefinitions) { RootBeanDefinition mbd = null; RootBeanDefinition previous = null; // 2. 在完整的锁定中检查,以确保使用相同的合并实例。 if (containingBd == null) { mbd = this.mergedBeanDefinitions.get(beanName); } // 3. 如果bean定义未被合并或已过期,进行合并操作。 if (mbd == null || mbd.stale) { previous = mbd; // 4. 处理没有父定义的情况。 if (bd.getParentName() == null) { if (bd instanceof RootBeanDefinition) { mbd = ((RootBeanDefinition) bd).cloneBeanDefinition(); } else { mbd = new RootBeanDefinition(bd); } } // 5. 处理有父定义的情况:需要与父定义合并。 else { BeanDefinition pbd; try { String parentBeanName = transformedBeanName(bd.getParentName()); if (!beanName.equals(parentBeanName)) { pbd = getMergedBeanDefinition(parentBeanName); } else { BeanFactory parent = getParentBeanFactory(); if (parent instanceof ConfigurableBeanFactory) { pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName); } else { // ... [代码部分省略以简化] } } } catch (NoSuchBeanDefinitionException ex) { // ... [代码部分省略以简化] } mbd = new RootBeanDefinition(pbd); mbd.overrideFrom(bd); } // 6. 如果bean定义的范围没有明确设置,将其默认为单例。 if (!StringUtils.hasLength(mbd.getScope())) { mbd.setScope(SCOPE_SINGLETON); } // 7. 非单例bean中的bean不能是单例。在这里修复这种情况。 if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) { mbd.setScope(containingBd.getScope()); } // 8. 如果需要,缓存合并后的bean定义。 if (containingBd == null && isCacheBeanMetadata()) { this.mergedBeanDefinitions.put(beanName, mbd); } } // 9. 如果之前存在一个bean定义,复制相关的缓存。 if (previous != null) { copyRelevantMergedBeanDefinitionCaches(previous, mbd); } // 10. 返回合并后的bean定义。 return mbd; } } ``` > `org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中的步骤7.1。 在Spring的bean初始化过程中,`@DependsOn`注解扮演了一个关键的角色,用于确保某个bean在其他指定的beans之前初始化。下面的代码片段详细展示了如何处理这个注解。为了深入了解这些细节,特别是`@DependsOn`注解背后的工作原理,我建议参考这篇文章: [**初始化顺序@DependsOn**](https://github.com/xuchengsheng/spring-reading/blob/master/spring-annotation/spring-annotation-dependsOn) - 精确控制 Spring Beans 的加载顺序。这篇文章详细解析了注解的源码,并深入探讨了其在Spring框架中的作用。 ```java String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) { for (String dep : dependsOn) { // 步骤7.1: 是否存在循环依赖 // 它首先检查是否存在循环依赖,这意味着Bean A依赖Bean B,而Bean B又依赖Bean A。 // 如果存在这样的情况它会抛出一个BeanCreationException异常。 if (isDependent(beanName, dep)) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'"); } // 步骤7.2: 注册Bean与Bean之间的依赖关系 // 当前的Bean工厂中注册bean之间的依赖关系。这样,当获取或销毁bean时,Spring可以保持正确的顺序。 registerDependentBean(dep, beanName); // 步骤7.3: 获取被依赖的Bean对象 // 确保每个被依赖的bean都已经被创建。这是通过直接调用getBean方法完成的,该方法负责初始化并返回指定的bean。 getBean(dep); } } ``` > `org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中的步骤8.1。 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(beanName,singletonFactory)`方法中,首先尝试从缓存中检索该bean。如果没有找到,它会使用提供的`singletonFactory`来创建这个bean,并在创建过程中进行前置和后置处理,以确保处理诸如循环引用等问题。创建的bean会被添加到缓存中。此外,该方法还处理了在创建过程中可能出现的各种异常,并确保在多线程环境中的线程安全。最后,返回所需的单例bean。 ```java public Object getSingleton(String beanName, ObjectFactory singletonFactory) { synchronized (this.singletonObjects) { // 首先尝试从缓存中获取单例 Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { // ... [代码部分省略以简化] // 步骤1: 前置处理,例如标记这个bean正在创建,以处理循环引用等问题。 beforeSingletonCreation(beanName); // ... [代码部分省略以简化] try { // 步骤2: 使用singletonFactory创建单例对象 singletonObject = singletonFactory.getObject(); newSingleton = true; } catch (IllegalStateException ex) { // ... [代码部分省略以简化] } catch (BeanCreationException ex) { // ... [代码部分省略以简化] } finally { // ... [代码部分省略以简化] // 步骤3: 创建单例之后的回调 afterSingletonCreation(beanName); } // 步骤4: 如果成功创建了新的单例bean,将其添加到缓存中 if (newSingleton) { addSingleton(beanName, singletonObject); } } // 返回现有或新创建的单例bean return singletonObject; } } ``` > `org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(beanName,singletonFactory)`方法中的步骤1。 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#beforeSingletonCreation`方法中,Spring框架尝试创建单例bean之前调用的,用于确保当前bean没有同时被多次创建,这样可以避免因循环引用导致的问题。如果bean已经在创建过程中,此方法会抛出一个异常。 ```java protected void beforeSingletonCreation(String beanName) { // 检查beanName是否在排除列表中或已经在创建中的集合中。 // 如果bean不在排除列表中并且也不能添加到创建中的集合中,意味着bean已经在创建中。 if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } } ``` > `org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中的步骤8.2。 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(beanName, mbd, args)`方法中,主要责根据指定的bean定义创建bean实例。此方法考虑了各种细节,例如是否有工厂方法、构造函数注入等,以及如何处理前置和后置处理器。 ```java @Override protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // 对mbd进行一些预处理,这可能包括克隆bean定义,如果mbd是非共享的原型。 RootBeanDefinition mbdToUse = mbd; // 步骤1: 尝试使用InstantiationAwareBeanPostProcessors来实例化bean。 // 如果后处理器产生bean实例(例如通过AOP代理),则直接返回该实例。 try { Object bean = resolveBeforeInstantiation(beanName, mbdToUse); if (bean != null) { return bean; } } catch (Throwable ex) { // 如果解析失败,记录异常并继续常规的bean创建。 // ... [代码部分省略以简化] } // 如果前置处理没有返回bean实例,进入常规的bean创建过程。 try { // 步骤2: 创建bean实例。这可能是通过工厂方法、构造函数注入等完成的。 Object beanInstance = doCreateBean(beanName, mbdToUse, args); return beanInstance; } // 捕获创建过程中可能出现的异常,并处理它们。 catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { // ... [代码部分省略以简化] } catch (Throwable ex) { // ... [代码部分省略以简化] } } ``` > `org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(beanName, mbd, args)`方法中的步骤1。 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation`方法中,在Spring框架中,`InstantiationAwareBeanPostProcessor`允许在标准实例化前拦截bean的创建。这一功能主要通过`resolveBeforeInstantiation`方法体现。为深入理解其工作机制,推荐我们阅读:[**Bean实例拦截InstantiationAwareBeanPostProcessor**](https://github.com/xuchengsheng/spring-reading/blob/master/spring-interface/spring-interface-instantiationAwareBeanPostProcessor)。这篇文章详细探讨了该接口在Spring中的核心作用。 ```java @Nullable protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) { // 初始化一个bean变量,它可能会被后续的处理过程赋值 Object bean = null; // 检查'beforeInstantiationResolved'属性是否为FALSE。如果是FALSE,则跳过后续的处理 if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) { // 首先,确保此时的bean类已经被解析。 // 然后,对于非合成的bean,并且如果有任何InstantiationAwareBeanPostProcessors,尝试进行前置处理。 if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { // 确定目标类型。这可能涉及到类的解析和其他初始化操作。 Class targetType = determineTargetType(beanName, mbd); if (targetType != null) { // 如果确定了目标类型,首先应用BeanPostProcessors的前置处理。这可能会返回一个bean实例, // 这样我们就可以避免标准的实例化过程。 bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName); // 如果bean实例在上述步骤中被创建,则还需要进行初始化后的BeanPostProcessors处理。 if (bean != null) { bean = applyBeanPostProcessorsAfterInitialization(bean, beanName); } } } // 更新'mbd.beforeInstantiationResolved'的值,如果bean在上述步骤中被创建,则为true,否则为false。 mbd.beforeInstantiationResolved = (bean != null); } // 返回可能已经在上述过程中创建的bean实例,或者如果没有创建bean,则返回null。 return bean; } ``` > `org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(beanName, mbd, args)`方法中的步骤2。 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中,主要处理了bean生命周期中的多个关键阶段,从bean的实例化、属性注入、初始化,到bean的清理注册。 ```java protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { BeanWrapper instanceWrapper = null; // ... [代码部分省略以简化] // 步骤1: 尝试实例化bean if (instanceWrapper == null) { instanceWrapper = createBeanInstance(beanName, mbd, args); } // ... [代码部分省略以简化] // 步骤2: 合并bean定义的后置处理 synchronized (mbd.postProcessingLock) { if (!mbd.postProcessed) { try { applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); } catch (Throwable ex) { // ... [代码部分省略以简化] } mbd.postProcessed = true; } } // 步骤3: 处理可能的循环引用,通过提前暴露bean的引用 boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { // ... [代码部分省略以简化] // 步骤3.1: 注册一个`ObjectFactory` addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } // 步骤4: 初始化bean实例,填充bean属性并应用后处理器 Object exposedObject = bean; try { // 步骤4.1: 属性填充 populateBean(beanName, mbd, instanceWrapper); // 步骤4.2: 初始化bean exposedObject = initializeBean(beanName, exposedObject, mbd); } catch (Throwable ex) { // ... [代码部分省略以简化] } // Step 5: 如果需要,注册bean以便在容器关闭时进行清理 try { registerDisposableBeanIfNecessary(beanName, bean, mbd); } catch (BeanDefinitionValidationException ex) { // ... [代码部分省略以简化] } return exposedObject; } ``` > `org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中的步骤1。 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance`方法中,首先尝试从后处理器获取构造函数,然后检查是否有首选构造函数,最后如果没有其他选项,它会使用无参数构造函数。 ```java protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) { // ... [代码部分省略以简化] // 步骤1: 首先尝试从BeanPostProcessors确定构造函数,这主要是为了处理例如@Autowired注解的情况 Constructor[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName); // 如果确定了构造函数或者bean定义中有相关的自动装配模式和构造函数参数,则使用自动装配构造函数创建bean实例 if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR || mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) { return autowireConstructor(beanName, mbd, ctors, args); } // 步骤2: 如果BeanDefinition中存在首选构造函数,使用这些构造函数 ctors = mbd.getPreferredConstructors(); if (ctors != null) { return autowireConstructor(beanName, mbd, ctors, null); } // 步骤3: 如果前面的步骤都没有返回bean实例,那么使用无参数构造函数实例化bean return instantiateBean(beanName, mbd); } ``` > `org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance`方法中的步骤1。 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#determineConstructorsFromBeanPostProcessors`方法中,`SmartInstantiationAwareBeanPostProcessor`提供了智能的bean实例化策略,尤其是通过`determineConstructorsFromBeanPostProcessors`方法调整构造函数选择。为了深入理解其作用,建议阅读:[**调整Bean实例化策略SmartInstantiationAwareBeanPostProcessor**](https://github.com/xuchengsheng/spring-reading/blob/master/spring-interface/spring-interface-smartInstantiationAwareBeanPostProcessor)。这篇文章深入分析了其在Spring的核心作用。 ```java @Nullable protected Constructor[] determineConstructorsFromBeanPostProcessors(@Nullable Class beanClass, String beanName) throws BeansException { // 检查提供的beanClass是否不为null,以及是否存在任何InstantiationAwareBeanPostProcessor if (beanClass != null && hasInstantiationAwareBeanPostProcessors()) { // 遍历所有的SmartInstantiationAwareBeanPostProcessor for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) { // 使用当前的BeanPostProcessor获取候选的构造函数 Constructor[] ctors = bp.determineCandidateConstructors(beanClass, beanName); // 如果找到了合适的构造函数,直接返回它们 if (ctors != null) { return ctors; } } } // 如果没有找到合适的构造函数,或beanClass为null,或没有相应的BeanPostProcessor,返回null return null; } ``` > `org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance`方法中的步骤3。 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#instantiateBean`方法中,主要用于根据提供的bean定义来实例化一个新的bean,并返回一个包装了该bean实例的`BeanWrapper`。这允许对bean实例进行进一步的操作,例如属性注入。 ```java protected BeanWrapper instantiateBean(String beanName, RootBeanDefinition mbd) { try { Object beanInstance; if (System.getSecurityManager() != null) { // ... [代码部分省略以简化] } else { // 如果不存在,使用实例化策略来创建bean实例 beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, this); } // 使用创建的bean实例初始化BeanWrapper BeanWrapper bw = new BeanWrapperImpl(beanInstance); // 初始化BeanWrapper,可以设置一些自定义的属性编辑器等 initBeanWrapper(bw); // 返回包装了bean实例的BeanWrapper return bw; } catch (Throwable ex) { // 处理创建bean实例过程中可能发生的异常 // ... [代码部分省略以简化] } } ``` > `org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中的步骤2。 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyMergedBeanDefinitionPostProcessors`方法中,在Spring框架中,`MergedBeanDefinitionPostProcessor`是一个关键接口,负责在bean实例化前对其定义进行调整和合并。为了深入了解这一机制和其在Spring中的重要性,建议查看:[**Bean定义的动态处理MergedBeanDefinitionPostProcessor**](https://github.com/xuchengsheng/spring-reading/blob/master/spring-interface/spring-interface-mergedBeanDefinitionPostProcessor)。这篇文章详细地探讨了该接口的源码和核心功能。 ```java protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class beanType, String beanName) { // 遍历已缓存的所有MergedBeanDefinitionPostProcessor类型的后处理器 for (MergedBeanDefinitionPostProcessor processor : getBeanPostProcessorCache().mergedDefinition) { // 调用每个后处理器的postProcessMergedBeanDefinition方法,对合并后的bean定义进行处理 processor.postProcessMergedBeanDefinition(mbd, beanType, beanName); } } ``` > `org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中的步骤3.1。 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingletonFactory`方法中,主要目的是为一个bean名称注册一个`ObjectFactory`,这可以用于在bean真正被创建之前解决循环引用问题。当其他bean尝试早期引用这个bean时,它可以使用这个`ObjectFactory`来获取一个bean的早期引用。 ```java protected void addSingletonFactory(String beanName, ObjectFactory singletonFactory) { // 确保传入的singletonFactory不为null Assert.notNull(singletonFactory, "Singleton factory must not be null"); synchronized (this.singletonObjects) { // 如果指定名称的bean尚未在singletonObjects缓存中 if (!this.singletonObjects.containsKey(beanName)) { // 将传入的singletonFactory添加到singletonFactories缓存中 this.singletonFactories.put(beanName, singletonFactory); // 从earlySingletonObjects缓存中移除指定bean名称,因为它现在已有一个完整的ObjectFactory this.earlySingletonObjects.remove(beanName); // 将bean名称添加到registeredSingletons集合中,标记它已被注册 this.registeredSingletons.add(beanName); } } } ``` > `org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中的步骤4.1。 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean`方法中,主要用于填充bean的属性。它会遍历所有的`InstantiationAwareBeanPostProcessors`,并调用它们的`postProcessAfterInstantiation`和`postProcessProperties`方法来后处理bean的属性。如果`InstantiationAwareBeanPostProcessor`返回`false`或`null`属性值,则提前结束bean属性的设置。 ```java protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) { // ... [代码部分省略以简化] // 如果当前的bean不是合成的,并且存在InstantiationAwareBeanPostProcessors if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { // 遍历所有的InstantiationAwareBeanPostProcessors for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) { // 调用postProcessAfterInstantiation方法 if (!bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) { // 如果返回false,则提前结束bean属性的设置 return; } } } // 获取bean定义中的属性值 PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null); // ... [代码部分省略以简化] // 检查是否有InstantiationAwareBeanPostProcessors boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors(); // 确定是否需要进行依赖性检查 boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE); PropertyDescriptor[] filteredPds = null; if (hasInstAwareBpps) { // 如果没有属性值,则从bean定义中获取 if (pvs == null) { pvs = mbd.getPropertyValues(); } // 遍历所有的InstantiationAwareBeanPostProcessors for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) { // 调用postProcessProperties方法处理属性值 PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName); if (pvsToUse == null) { if (filteredPds == null) { filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); } pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName); if (pvsToUse == null) { return; } } pvs = pvsToUse; } } // 如果需要进行依赖性检查 if (needsDepCheck) { if (filteredPds == null) { filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); } checkDependencies(beanName, mbd, filteredPds, pvs); } // 最后,将处理后的属性值应用到bean实例上 if (pvs != null) { applyPropertyValues(beanName, mbd, bw, pvs); } } ``` > `org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中的步骤4.2。 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(beanName, bean,mbd)`方法中,主要负责bean的初始化过程,包括调用Aware接口方法、执行`BeanPostProcessors`的初始化前后方法以及bean的自定义初始化方法。 ```java protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { // 如果存在SecurityManager,执行相应的安全代码(省略部分) if (System.getSecurityManager() != null) { // ... [代码部分省略以简化] } else { // 如果bean实现了特定的Aware接口(如BeanNameAware, BeanFactoryAware等),则调用相应的方法 invokeAwareMethods(beanName, bean); } // 初始化前的预处理 // 如果bean不是合成的,调用所有BeanPostProcessors的postProcessBeforeInitialization方法 Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } try { // 调用bean的初始化方法,例如afterPropertiesSet和custom init-method invokeInitMethods(beanName, wrappedBean, mbd); } catch (Throwable ex) { // ... [代码部分省略以简化] } // 初始化后的后处理 // 如果bean不是合成的,调用所有BeanPostProcessors的postProcessAfterInitialization方法 if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } // 返回最终的bean实例,可能被AOP代理等包装 return wrappedBean; } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeAwareMethods`方法中,在Spring框架中,`Aware`接口赋予beans与容器交互的能力,如获取其名字、类加载器或与bean工厂的交互。为更深入地探究这些接口,我推荐我们查看以下文章,它们详细分析了这些`Aware`接口在Spring中的实现: - [**获取Bean名称BeanNameAware**](https://github.com/xuchengsheng/spring-reading/blob/master/spring-aware/spring-aware-beanNameAware) - 这个接口使bean能够获取其在Spring容器中的名字。 - [**获取类加载器BeanClassLoaderAware**](https://github.com/xuchengsheng/spring-reading/blob/master/spring-aware/spring-aware-beanClassLoaderAware) - 通过这个接口,bean可以获得与其相关的类加载器的引用。 - [**与Bean工厂互动BeanFactoryAware**](https://github.com/xuchengsheng/spring-reading/blob/master/spring-aware/spring-aware-beanFactoryAware) - 这个接口让bean可以与其所在的bean工厂或应用上下文互动。 ```java private void invokeAwareMethods(String beanName, Object bean) { // 检查bean是否实现了Aware接口 if (bean instanceof Aware) { // 如果bean实现了BeanNameAware接口,设置bean的名字 if (bean instanceof BeanNameAware) { ((BeanNameAware) bean).setBeanName(beanName); } // 如果bean实现了BeanClassLoaderAware接口,设置bean的类加载器 if (bean instanceof BeanClassLoaderAware) { ClassLoader bcl = getBeanClassLoader(); if (bcl != null) { ((BeanClassLoaderAware) bean).setBeanClassLoader(bcl); } } // 如果bean实现了BeanFactoryAware接口,设置bean的工厂 if (bean instanceof BeanFactoryAware) { ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this); } } } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization`方法中,在Spring中,`BeanPostProcessor`接口提供了在bean初始化过程中进行拦截的能力。要深入了解其工作原理,建议阅读:[**调整Bean属性BeanPostProcessor**](https://github.com/xuchengsheng/spring-reading/blob/master/spring-interface/spring-interface-beanPostProcessor)。这篇文章详细解析了其在Spring中的关键作用。 ```java @Override public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException { // 设置当前bean为传入的bean Object result = existingBean; // 遍历容器中所有的BeanPostProcessors for (BeanPostProcessor processor : getBeanPostProcessors()) { // 调用每个BeanPostProcessor的postProcessBeforeInitialization方法 Object current = processor.postProcessBeforeInitialization(result, beanName); // 如果postProcessBeforeInitialization返回null,则直接返回原bean if (current == null) { return result; } result = current; // 更新result为postProcessBeforeInitialization处理后的bean } // 返回所有BeanPostProcessors处理后的bean return result; } ``` > `org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中的步骤5。 在`org.springframework.beans.factory.support.AbstractBeanFactory#registerDisposableBeanIfNecessary`方法中,主要目的是为在Spring容器中管理的bean注册一个销毁回调。当容器关闭并且bean需要清理资源或执行其他销毁逻辑时,这个销毁回调会被调用。 ```java protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) { // ... [代码部分省略以简化] registerDisposableBean(beanName, new DisposableBeanAdapter( bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, acc)); // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#registerDisposableBean`方法中,将给定的bean名字和对应的`DisposableBean`实例放入`disposableBeans`映射中。这个映射会在容器关闭时被遍历,所有的`DisposableBean`实例的`destroy`方法会被调用,以确保资源得到适当的释放和bean得到适当的销毁。 ```java public void registerDisposableBean(String beanName, DisposableBean bean) { synchronized (this.disposableBeans) { this.disposableBeans.put(beanName, bean); } } ``` > `org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(beanName,singletonFactory)`方法中的步骤3。 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#afterSingletonCreation`方法中,此方法确保bean的创建过程是线程安全的,并保护系统免受不正确的并发访问,特别是当多个线程试图同时访问或修改同一个bean的状态时。 ```java protected void afterSingletonCreation(String beanName) { // 检查给定的bean名称是否在排除列表中,如果不是,继续检查该bean是否正在创建 if (!this.inCreationCheckExclusions.contains(beanName) // 尝试从表示“当前正在创建的单例bean”集合中移除给定的bean名称 && !this.singletonsCurrentlyInCreation.remove(beanName)) { // 如果给定的bean名称无法从集合中移除,说明在此时该bean不应该在创建中。 // 这可能表示bean的创建有问题或被错误地标记为“当前正在创建”,因此抛出异常。 throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation"); } } ``` > `org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(beanName,singletonFactory)`方法中的步骤4。 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingleton`方法中,处理了与单例bean生命周期相关的各种缓存和集合。 ```java protected void addSingleton(String beanName, Object singletonObject) { // 使用`synchronized`块确保多线程环境中对单例对象的线程安全操作 synchronized (this.singletonObjects) { // 将新创建的单例对象添加到`singletonObjects`缓存中 this.singletonObjects.put(beanName, singletonObject); // 从`singletonFactories`中移除bean名称,因为现在我们已经完成了该bean的完整实例化 this.singletonFactories.remove(beanName); // 从`earlySingletonObjects`中移除bean名称,因为该bean现在已经完全初始化并存储在`singletonObjects`中 this.earlySingletonObjects.remove(beanName); // 将bean名称添加到`registeredSingletons`集合中,以表示该bean已经被注册为一个单例 this.registeredSingletons.add(beanName); } } ``` > `org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中的步骤8.3。 在`org.springframework.beans.factory.support.AbstractBeanFactory#getObjectForBeanInstance`方法中,根据提供的bean实例和名称,要么返回bean实例本身,要么从`FactoryBean`中获取对象。同时,它还处理了与工厂bean缓存相关的各种细节。 ```java protected Object getObjectForBeanInstance( Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) { // 检查名称是否有工厂的解引用前缀(例如'&')并且bean实例不是工厂 if (BeanFactoryUtils.isFactoryDereference(name)) { // 当bean实例是NullBean时,直接返回bean实例 if (beanInstance instanceof NullBean) { return beanInstance; } // 如果bean实例不是一个FactoryBean,抛出异常 if (!(beanInstance instanceof FactoryBean)) { throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass()); } // 如果bean定义不为null,标记它为一个FactoryBean if (mbd != null) { mbd.isFactoryBean = true; } return beanInstance; } // 如果bean实例不是一个FactoryBean,则直接返回bean实例 if (!(beanInstance instanceof FactoryBean)) { return beanInstance; } Object object = null; // 如果bean定义不为null,标记它为一个FactoryBean if (mbd != null) { mbd.isFactoryBean = true; } // 如果没有提供bean定义,则尝试从缓存中获取工厂bean生成的对象 else { object = getCachedObjectForFactoryBean(beanName); } // 如果缓存中没有对象,则需要从FactoryBean中获取 if (object == null) { FactoryBean factory = (FactoryBean) beanInstance; // 如果存在bean定义并且没有为给定的beanName缓存对象,则获取合并的bean定义 if (mbd == null && containsBeanDefinition(beanName)) { mbd = getMergedLocalBeanDefinition(beanName); } // 检查bean定义是否为合成的(例如,由基础设施代码创建的) boolean synthetic = (mbd != null && mbd.isSynthetic()); // 从FactoryBean获取对象 object = getObjectFromFactoryBean(factory, beanName, !synthetic); } return object; } ``` > `org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中的步骤9。 在`org.springframework.beans.factory.support.AbstractBeanFactory#adaptBeanInstance`方法中,目的是确保给定的bean实例与指定的目标类型匹配。如果它们不匹配,此方法将尝试使用类型转换器将bean实例转换为所需的类型。如果转换失败,它将抛出一个异常。 ```java T adaptBeanInstance(String name, Object bean, @Nullable Class requiredType) { // 检查所需的类型是否与实际bean实例的类型匹配 if (requiredType != null && !requiredType.isInstance(bean)) { try { // 如果不匹配,尝试转换bean实例为所需的类型 Object convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType); // 如果转换后的bean为null,抛出异常 if (convertedBean == null) { throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); } return (T) convertedBean; } catch (TypeMismatchException ex) { // 如果类型转换失败,记录trace日志并抛出异常 if (logger.isTraceEnabled()) { logger.trace("Failed to convert bean '" + name + "' to required type '" + ClassUtils.getQualifiedName(requiredType) + "'", ex); } throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); } } // 如果bean实例的类型与所需的类型匹配,直接返回bean实例 return (T) bean; } ``` ### 七、注意事项 1. **Bean的存在性** + 确保bean确实已经在Spring上下文中定义了。如果没有,`NoSuchBeanDefinitionException`将被抛出。 2. **正确的Bean名称** + 确保我们使用的名称是bean的正确ID或名称。Spring的bean名称默认是非限定类名的首字母小写,但如果在bean定义中指定了不同的名称,我们应该使用那个。 3. **Bean的生命周期** + `getBean()`方法每次都可能返回不同的实例或相同的实例,具体取决于bean的范围(singleton、prototype等)。 4. **类型安全** + 使用`getBean(name, class)`可以确保返回的bean是期望的类型,从而避免在运行时出现类转换异常。 5. **循环依赖** + 如果在bean的依赖关系中存在循环依赖,`getBean`可能会失败,并抛出`BeanCurrentlyInCreationException`。 6. **Lazy初始化** + 对于懒惰初始化的bean,第一次调用`getBean()`会触发bean的创建和初始化。 7. **可能的副作用** + 因为`getBean()`可以触发bean的创建和初始化,所以可能会有副作用,例如数据库连接、文件IO或其他资源的初始化。 8. **不要过度使用** + 在一个Spring管理的bean中频繁调用`getBean()`并不是一个好的实践。这违背了控制反转的原则,可能导致代码难以测试和维护。我们应该尽可能地依赖注入,而不是显式地从容器中获取bean。 9. **线程安全性** + 虽然`getBean()`方法是线程安全的,但返回的bean可能不是,除非我们确保它是线程安全的。 10. **生命周期回调** + 请记住,当我们通过`getBean`方法创建一个新的bean实例时(例如,范围为prototype的bean),Spring将不会管理该bean的完整生命周期。特别是,Spring不会调用prototype bean的销毁方法。 ### 八、总结 #### 最佳实践总结 1. **使用正确的上下文环境** + 选择`AnnotationConfigApplicationContext`作为Spring上下文环境,这是专为Java注解配置的Spring容器。 2. **定义配置类** + 建立一个配置类如`MyConfiguration`,使用`@Configuration`注解标记它。这将告诉Spring,该类包含bean的配置信息。 3. **启用组件扫描** + 在配置类上使用`@ComponentScan`,并为其提供需要扫描的包名。这允许Spring自动检测带有特定注解的类,并将其注册为bean。 4. **定义Bean** + 在目标类(如服务类)上使用`@Component`或其他相关注解(如`@Service`, `@Repository`, `@Controller`等)。这确保Spring可以识别它们并自动将它们添加到容器中。 5. **获取和使用Bean** + 在应用程序入口中,初始化上下文并使用`context.getBean()`方法从Spring容器中获取bean。 6. **查看结果** + 运行应用程序并确认输出,确保Spring正确地识别并初始化了期望的bean。 #### 源码分析总结 1. **获取Bean定义** + 通过`getBean`方法,Spring提供了一个方式让调用者基于bean的名称从Spring IoC容器中检索bean。 2. **获取Bean名称** + `transformedBeanName`方法处理了bean名称的转换,确保返回的名称是规范的并处理了可能的别名。别名处理由`canonicalName`方法完成,这个方法不断地从别名映射中查找真实的bean名称。 3. **检索单例Bean** + `getSingleton`方法尝试从缓存中检索单例bean。它会考虑完全初始化的bean、早期引用以及单例工厂缓存的bean。此方法在解决循环依赖问题时特别有用。 4. **处理原型Bean** + `isPrototypeCurrentlyInCreation`方法检查特定的bean是否正在创建中的原型beans列表中,以处理原型bean的循环引用。 5. **标记Bean已创建** + `markBeanAsCreated`方法标记了指定的bean已经被创建或正在创建,这在Spring的bean生命周期中起到了关键作用。 6. **获取合并的Bean定义** + `getMergedLocalBeanDefinition`方法负责获取给定bean名称的合并bean定义。合并的bean定义是从父bean和子bean定义中合并的结果。而`getMergedBeanDefinition`进一步处理了bean定义的父子关系并返回了合并后的bean定义。 7. **处理@DependsOn注解** + 如果bean定义中指定了@DependsOn注解,Spring会确保在当前bean之前创建它所依赖的其他beans。该处理包括:检查是否存在循环依赖,在Bean工厂中注册bean之间的依赖关系,确保每个被依赖的bean都已经被创建。 8. **Singleton Bean的缓存获取** - 在`DefaultSingletonBeanRegistry#getSingleton(beanName,singletonFactory)`方法中,Spring首先尝试从缓存中检索该bean。若无法在缓存中找到,它会使用提供的`singletonFactory`来创建bean。创建的bean会被加入到缓存中,这保证了其单例性。 9. **处理循环引用** - 在`beforeSingletonCreation`方法中,Spring确保当前bean不会被多次创建,这样可以避免因循环引用导致的问题。 10. **创建Bean实例** - `AbstractAutowireCapableBeanFactory#createBean(beanName, mbd, args)`是bean创建的核心方法。在这里,Spring会考虑工厂方法、构造函数注入等多种方式来实例化bean。同时,此处还会处理前置和后置处理器。 11. **处理Bean实例化之前的逻辑** - 在`resolveBeforeInstantiation`方法中,`InstantiationAwareBeanPostProcessor`后处理器可能会拦截bean的标准实例化流程。这主要用于如AOP的场景。 12. **Bean的实例化、属性注入、初始化** - `doCreateBean`方法处理了bean生命周期中的多个关键阶段,从bean的实例化、属性注入、初始化,到bean的清理注册。 13. **选择构造函数并实例化bean** - 在`createBeanInstance`方法中,Spring首先尝试从后处理器获取构造函数。如果没有找到合适的构造函数,它可能会使用无参数构造函数,或者考虑其他逻辑,如首选构造函数。 14. **智能实例化策略** - `determineConstructorsFromBeanPostProcessors`方法中,通过`SmartInstantiationAwareBeanPostProcessor`,Spring可以调整构造函数选择,提供更加智能的bean实例化策略。 15. **直接实例化bean** - `instantiateBean`方法是一个简单的bean实例化过程,通常用于没有特定构造函数或工厂方法的bean。 16. **`MergedBeanDefinitionPostProcessor`处理**: - 在`applyMergedBeanDefinitionPostProcessors`方法中,`MergedBeanDefinitionPostProcessor`接口用于在bean实例化前对其定义进行处理和调整。 17. **处理循环引用**: - `addSingletonFactory`方法注册一个`ObjectFactory`,旨在解决bean创建前的循环引用问题。 18. **填充bean属性**: - `populateBean`方法负责填充bean的属性。它遍历所有的`InstantiationAwareBeanPostProcessors`,调用它们的方法进行bean属性的后处理。 19. **bean初始化**: - `initializeBean`方法处理bean的初始化,包括调用Aware接口方法、执行`BeanPostProcessors`的初始化前后方法,以及bean的自定义初始化方法。 - `invokeAwareMethods`方法处理bean的Aware接口调用,让bean可以获得Spring容器提供的一些能力。 20. **注册bean的销毁方法**: - `registerDisposableBeanIfNecessary`方法负责为bean注册一个销毁回调。当容器关闭并需要清理资源或执行其他销毁逻辑时,这个回调会被触发。 21. **保护并发bean创建**: - `afterSingletonCreation`方法确保bean创建过程是线程安全的,并保护系统免受不正确的并发访问。 22. **处理单例bean的生命周期**: - `addSingleton`方法处理与单例bean生命周期相关的各种缓存和集合。 23. **获取或转换bean实例**: - `getObjectForBeanInstance`方法根据提供的bean实例和名称,要么返回bean实例本身,要么从`FactoryBean`中获取对象。 - `adaptBeanInstance`方法确保bean实例与指定的目标类型匹配,如果不匹配,它将尝试转换bean实例。 ================================================ FILE: spring-core/spring-core-getBean/pom.xml ================================================ spring-core com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-core-getBean ================================================ FILE: spring-core/spring-core-getBean/src/main/java/com/xcs/spring/GetBeanApplication.java ================================================ package com.xcs.spring; import com.xcs.spring.config.MyConfiguration; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年09月16日 16时09分 **/ public class GetBeanApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); System.out.println("myServiceA = " + context.getBean("myServiceA")); System.out.println("myServiceB = " + context.getBean("myServiceB")); } } ================================================ FILE: spring-core/spring-core-getBean/src/main/java/com/xcs/spring/config/MyConfiguration.java ================================================ package com.xcs.spring.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; /** * @author xcs * @date 2023年09月19日 16时35分 **/ @Configuration @ComponentScan("com.xcs.spring.service") public class MyConfiguration { } ================================================ FILE: spring-core/spring-core-getBean/src/main/java/com/xcs/spring/service/MyServiceA.java ================================================ package com.xcs.spring.service; import org.springframework.stereotype.Component; /** * @author xcs * @date 2023年09月21日 10时30分 **/ @Component public class MyServiceA { public void destroy(){ System.out.println("MyServiceA.destroy"); } } ================================================ FILE: spring-core/spring-core-getBean/src/main/java/com/xcs/spring/service/MyServiceB.java ================================================ package com.xcs.spring.service; import org.springframework.stereotype.Component; /** * @author xcs * @date 2023年09月21日 10时30分 **/ @Component public class MyServiceB { } ================================================ FILE: spring-core/spring-core-registerBeanDefinition/README.md ================================================ ## Bean的定义注册 - [Bean的定义注册](#bean的定义注册) - [一、基本信息](#一基本信息) - [二、知识储备](#二知识储备) - [三、最佳实践](#三最佳实践) - [四、源码分析](#四源码分析) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、知识储备 + **ClassPathBeanDefinitionScanner** + [ClassPathBeanDefinitionScanner](https://github.com/xuchengsheng/spring-reading/blob/master/spring-beans/spring-bean-classPathBeanDefinitionScanner/README.md) 类,用于在类路径上扫描指定包及其子包中的类,识别符合条件的类,并将其注册为 Spring Bean 的定义,从而实现组件扫描和自动装配,使我们能够方便地管理和配置应用程序中的 Bean。它允许我们定义过滤条件,以确定哪些类应被注册为 Bean,以及配合自动装配实现依赖注入,提高了应用程序的可维护性和扩展性。 + **AnnotatedBeanDefinitionReader** + [AnnotatedBeanDefinitionReader](https://github.com/xuchengsheng/spring-reading/blob/master/spring-beans/spring-bean-annotatedBeanDefinitionReader/README.md)是一个用于读取和解析带有注解的Bean定义的类,它主要用于基于注解的配置方式,允许开发者将Java类标记为Spring组件,从而让Spring容器自动扫描和注册这些组件,而不需要显式配置这些组件的Bean定义。 ### 三、最佳实践 通过`AnnotationConfigApplicationContext`容器,以手动注册和包扫描两种方式注册Bean定义,其中手动注册了单个Bean(`MyBean`类),并通过包扫描注册了指定包路径下的所有标有`@Component`及其派生注解的类。最后,通过打印输出了所有已注册的Bean定义的名称。 ```java public class RegisterBeanDefinitionApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); // 注册Bean context.register(MyBean.class); // 扫描包 context.scan("com.xcs.spring"); // 打印Bean定义 for (String beanDefinitionName : context.getBeanDefinitionNames()) { System.out.println("beanDefinitionName = " + beanDefinitionName); } } } ``` 一个简单的Java类 `MyBean`,这个类没有任何注解或其他特殊标记。 ```java public class MyBean { } ``` 分别使用`@Controller`、`@Service`和`@Repository`。这些注解是Spring框架中用于标识不同层次组件的特殊注解。 ```java @Controller public class MyController { } @Service public class MyService { } @Repository public class MyRepository { } ``` 运行结果发现,Spring应用上下文中成功加载了配置,并注册了各种Bean定义,包括手动注册和通过注解自动注册的。 PS:前面4个是Spring容器内部的注册的Bean定义。 ```java beanDefinitionName = org.springframework.context.annotation.internalConfigurationAnnotationProcessor beanDefinitionName = org.springframework.context.annotation.internalAutowiredAnnotationProcessor beanDefinitionName = org.springframework.context.event.internalEventListenerProcessor beanDefinitionName = org.springframework.context.event.internalEventListenerFactory beanDefinitionName = myBean beanDefinitionName = myController beanDefinitionName = myRepository beanDefinitionName = myService ``` ### 四、源码分析 在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext()`方法中,初始化`AnnotationConfigApplicationContext`实例中的两个关键组件:`AnnotatedBeanDefinitionReader`和`ClassPathBeanDefinitionScanner`,这两者分别用于处理注解式Bean定义和类路径扫描注册Bean定义。 ```java public AnnotationConfigApplicationContext() { StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create"); this.reader = new AnnotatedBeanDefinitionReader(this); createAnnotatedBeanDefReader.end(); this.scanner = new ClassPathBeanDefinitionScanner(this); } ``` 在`org.springframework.context.annotation.AnnotationConfigApplicationContext#register`方法中,主要作用是通过`AnnotatedBeanDefinitionReader`注册一组组件类,将它们解析为Spring容器中的Bean定义。 > **具体源码分析已经在另外一篇关于[AnnotatedBeanDefinitionReader](https://github.com/xuchengsheng/spring-reading/blob/master/spring-beans/spring-bean-annotatedBeanDefinitionReader/README.md)类的博客中详细分析了。** ```java @Override public void register(Class... componentClasses) { Assert.notEmpty(componentClasses, "At least one component class must be specified"); StartupStep registerComponentClass = this.getApplicationStartup().start("spring.context.component-classes.register") .tag("classes", () -> Arrays.toString(componentClasses)); this.reader.register(componentClasses); registerComponentClass.end(); } ``` 在`org.springframework.context.annotation.AnnotationConfigApplicationContext#scan`方法中,主要作用是通过`ClassPathBeanDefinitionScanner`扫描指定的基础包路径下的类,将它们解析为Spring容器中的Bean定义。 > **具体源码分析已经在另外一篇关于[ClassPathBeanDefinitionScanner](https://github.com/xuchengsheng/spring-reading/blob/master/spring-beans/spring-bean-classPathBeanDefinitionScanner/README.md)类的博客中详细分析了。** ```java @Override public void scan(String... basePackages) { Assert.notEmpty(basePackages, "At least one base package must be specified"); StartupStep scanPackages = this.getApplicationStartup().start("spring.context.base-packages.scan") .tag("packages", () -> Arrays.toString(basePackages)); this.scanner.scan(basePackages); scanPackages.end(); } ``` ================================================ FILE: spring-core/spring-core-registerBeanDefinition/pom.xml ================================================ spring-core com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-core-registerBeanDefinition ================================================ FILE: spring-core/spring-core-registerBeanDefinition/src/main/java/com/xcs/spring/RegisterBeanDefinitionApplication.java ================================================ package com.xcs.spring; import com.xcs.spring.bean.MyBean; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年11月22日 09时58分 **/ public class RegisterBeanDefinitionApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); // 注册Bean context.register(MyBean.class); // 扫描包 context.scan("com.xcs.spring"); // 打印Bean定义 for (String beanDefinitionName : context.getBeanDefinitionNames()) { System.out.println("beanDefinitionName = " + beanDefinitionName); } } } ================================================ FILE: spring-core/spring-core-registerBeanDefinition/src/main/java/com/xcs/spring/bean/MyBean.java ================================================ package com.xcs.spring.bean; /** * @author xcs * @date 2023年11月22日 11时23分 **/ public class MyBean { } ================================================ FILE: spring-core/spring-core-registerBeanDefinition/src/main/java/com/xcs/spring/controller/MyController.java ================================================ package com.xcs.spring.controller; import org.springframework.stereotype.Controller; /** * @author xcs * @date 2023年11月07日 17时47分 **/ @Controller public class MyController { } ================================================ FILE: spring-core/spring-core-registerBeanDefinition/src/main/java/com/xcs/spring/repository/MyRepository.java ================================================ package com.xcs.spring.repository; import org.springframework.stereotype.Repository; /** * @author xcs * @date 2023年11月07日 17时46分 **/ @Repository public class MyRepository { } ================================================ FILE: spring-core/spring-core-registerBeanDefinition/src/main/java/com/xcs/spring/service/MyService.java ================================================ package com.xcs.spring.service; import org.springframework.stereotype.Service; /** * @author xcs * @date 2023年11月07日 17时46分 **/ @Service public class MyService { } ================================================ FILE: spring-core/spring-core-resolveDependency/README.md ================================================ ## resolveDependency - [resolveDependency](#resolvedependency) - [一、基本信息](#一基本信息) - [二、方法描述](#二方法描述) - [三、方法源码](#三方法源码) - [四、主要功能](#四主要功能) - [五、最佳实践](#五最佳实践) - [六、时序图](#六时序图) - [解析环境变量](#解析环境变量) - [解析Bean依赖](#解析bean依赖) - [七、源码分析](#七源码分析) - [解析环境变量](#解析环境变量-1) - [解析Bean依赖](#解析bean依赖-1) - [八、注意事项](#八注意事项) - [九、总结](#九总结) - [最佳实践总结](#最佳实践总结) - [源码分析总结](#源码分析总结) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [CSDN](https://blog.csdn.net/duzhuang2399) | [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [GitHub](https://github.com/xuchengsheng/spring-reading) ### 二、方法描述 `resolveDependency` 是 Spring 框架中 `ConfigurableListableBeanFactory` 接口中定义的一个方法。这个方法的核心功能是解析并提供 bean 的依赖项。它主要在处理 `@Autowired` , `@Inject` ,`@Value`这样的自动装配场景时起作用。 ### 三、方法源码 Spring IOC 容器中的一个核心方法,用于解析 bean 之间的依赖关系。当容器需要知道应该为特定的 bean、字段、构造函数或方法注入哪个其他 bean 时,就会调用这个方法。 ```java /** * 在此工厂中定义的 beans 之间,解析指定的依赖项。 * * 该方法尝试匹配和返回一个适当的 bean 实例来满足给定的依赖描述符。 * 依赖描述符可以描述字段、方法或构造函数中的依赖。 * * @param descriptor 描述依赖的对象,提供有关依赖类型、限定符等的详细信息。 * @param requestingBeanName 声明或请求依赖的 bean 的名称。这通常用于解决 bean 之间的循环依赖。 * @return 对应的 bean 实例以满足该依赖,如果没有合适的匹配,则返回 null。 * * @throws NoSuchBeanDefinitionException 当没有找到匹配的 bean 时抛出。 * @throws NoUniqueBeanDefinitionException 当存在多个可能的匹配并且没有明确的选择时抛出。 * @throws BeansException 如果由于其他原因解析失败则抛出。 * * @since 2.5 * @see #resolveDependency(DependencyDescriptor, String, Set, TypeConverter) 用于更复杂的依赖解析场景,允许传递排除的 beans 和类型转换器。 */ @Nullable Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName) throws BeansException; ``` ### 四、主要功能 1. **依赖描述符** + 该方法接收一个 `DependencyDescriptor` 参数,它描述了所需的依赖关系。这个描述符可以表示一个字段、方法参数或构造函数参数的依赖。 2. **自动装配限定符** + 如果存在多个匹配的 bean,并且所需的依赖有 `@Autowired` 和 `@Qualifier` 注解,那么 `resolveDependency` 会考虑这些注解来确定正确的 bean。 3. **解决循环依赖** + 该方法也会考虑循环依赖的问题。例如,如果 Bean A 依赖于 Bean B,而 Bean B 又依赖于 Bean A,那么这种情况会被处理。 4. **返回匹配的 bean** + 此方法尝试返回一个与描述符匹配的 bean 实例。如果找不到合适的 bean,它可能返回 null 或抛出一个异常,具体取决于描述符的设置。 5. **异常处理** + 如果没有找到合适的 bean 或找到了多个合适的 bean 且没有明确的选择,该方法会抛出相应的异常。 6. **其他 Considerations**: - 可以考虑其他因素,如 `@Primary` 注解。 - 对于基本类型或字符串类型的属性,可以解析 `@Value` 注解,从属性文件或环境变量中获取值。 ### 五、最佳实践 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。代码调用了两个方法(`methodResolveDependency` 和 `fieldResolveDependency`)来分别解析方法和字段的依赖。具体来说,它们会使用反射来获取目标方法或字段。创建一个描述这个方法或字段的 `DependencyDescriptor`。使用 `resolveDependency` 方法来从 Spring 容器中解析真正的依赖。使用反射来将解析得到的依赖注入到目标对象中。在解析方法和字段的依赖之后,代码通过格式化的字符串打印了相关信息。例如,它显示了方法或字段的完全限定名称和解析得到的依赖对象的值。 ```java public class ResolveDependencyApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); // 获得Bean工厂 ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); // 被注入对象 MyServiceB injectTarget = new MyServiceB(); System.out.println("Before MyServiceB = " + injectTarget + "\n"); methodResolveDependency(beanFactory, injectTarget, "setMethodMyServiceA"); fieldResolveDependency(beanFactory, injectTarget, "fieldMyServiceA"); fieldResolveDependency(beanFactory, injectTarget, "myPropertyValue"); System.out.println("After MyServiceB = " + injectTarget + "\n"); } /** * 解析方法依赖 * * @param beanFactory * @param injectTarget */ public static void methodResolveDependency(ConfigurableListableBeanFactory beanFactory, Object injectTarget, String name) { try { // 1. 获取MyServiceB类中名为setMethodMyServiceA的方法的引用 Method method = injectTarget.getClass().getMethod(name, MyServiceA.class); // 2. 创建一个描述此方法参数的DependencyDescriptor DependencyDescriptor desc = new DependencyDescriptor(new MethodParameter(method, 0), true); // 3. 使用BeanFactory来解析这个方法参数的依赖 Object value = beanFactory.resolveDependency(desc, null); System.out.println("解析方法依赖结果:"); System.out.println("---------------------------------------------"); System.out.println(String.format("Name: %s.%s",method.getDeclaringClass().getName(),method.getName())); System.out.println(String.format("Value: %s", value)); System.out.println("---------------------------------------------\n"); // 4. 使方法可访问(特别是如果它是private的) ReflectionUtils.makeAccessible(method); // 5. 使用反射调用这个方法,将解析得到的依赖注入到目标对象中 method.invoke(injectTarget, value); } catch (Exception e) { e.printStackTrace(); } } /** * 解析字段依赖 * * @param beanFactory * @param injectTarget */ public static void fieldResolveDependency(ConfigurableListableBeanFactory beanFactory, Object injectTarget, String name) { try { // 1. 获取MyServiceB类中名为fieldMyServiceA的字段的引用 Field field = injectTarget.getClass().getDeclaredField(name); // 2. 创建一个描述此字段的DependencyDescriptor DependencyDescriptor desc = new DependencyDescriptor(field, true); // 3. 使用BeanFactory来解析这个字段的依赖 Object value = beanFactory.resolveDependency(desc, null); System.out.println("解析字段依赖结果:"); System.out.println("---------------------------------------------"); System.out.println(String.format("Name: %s.%s", field.getDeclaringClass().getName(), field.getName())); System.out.println(String.format("Value: %s", value)); System.out.println("---------------------------------------------\n"); // 4. 使字段可访问(特别是如果它是private的) ReflectionUtils.makeAccessible(field); // 5. 使用反射设置这个字段的值,将解析得到的依赖注入到目标对象中 field.set(injectTarget, value); } catch (Exception e) { e.printStackTrace(); } } } ``` 在`MyConfiguration`类中,使用了`@ComponentScan("com.xcs.spring")`注解告诉 Spring 在指定的包(在这里是 "`com.xcs.spring`")及其子包中搜索带有 `@Component`、`@Service`、`@Repository` 和 `@Controller` 等注解的类,并将它们自动注册为 beans。这样,spring就不必为每个组件明确写一个 bean 定义。Spring 会自动识别并注册它们。另外使用`@PropertySource`注解从类路径下的`application.properties`文件中加载属性。 ```java @Configuration @ComponentScan("com.xcs.spring") @PropertySource("classpath:application.properties") public class MyConfiguration { } ``` `application.properties`文件在`src/main/resources`目录中,并添加以下内容。 ```properties my.property.value = Hello from Environment! ``` `MyService` 是一个简单的服务类,但我们没有定义任何方法或功能。 ```java @Service public class MyServiceA { } ``` 首先 `MyServiceB` 没有被 Spring 托管,那么它在代码中的表现就与一个普通的 Java 类没有什么不同。虽然 `MyServiceB` 本身不是 Spring 托管的,但 `ResolveDependencyApplication` 类中,我给大家展示了如何使用 Spring 的底层 API 手动解析和注入依赖。 - **字段 `myPropertyValue`** + 虽然它有一个 `@Value` 注解,但由于 `MyServiceB` 不是一个 Spring 管理的 bean,所以这个注解不会自动被解析。 - **字段 `myServiceA` 和 `fieldMyServiceA`** + 由于没有自动的 Spring 依赖注入,这两个字段默认为 `null`。 - **方法 `setMyServiceA`** + 这是一个普通的 setter 方法。但在 `MyServiceB` 不被 Spring 托管的情况下,它只是一个普通的 setter。 - **方法 `toString`** + 该方法为该类提供了一个自定义的字符串表示。这与 `MyServiceB` 是否被 Spring 托管无关。 ```java public class MyServiceB { /** * 方法注入 */ private MyServiceA methodMyServiceA; /** * 字段注入 */ private MyServiceA fieldMyServiceA; /** * 字段注入 (环境变量) */ @Value("${my.property.value}") private String myPropertyValue; public void setMethodMyServiceA(MyServiceA methodMyServiceA){ this.methodMyServiceA = methodMyServiceA; } @Override public String toString() { return "MyServiceB{" + "myPropertyValue='" + myPropertyValue + '\'' + ", methodMyServiceA=" + methodMyServiceA + ", fieldMyServiceA=" + fieldMyServiceA + '}'; } } ``` 运行结果发现,使用了 Spring 的底层 `resolveDependency` 方法来为 `MyServiceB` 类的字段和方法手动注入依赖。虽然在常规的 Spring 开发中我们通常不这样做(因为 Spring 提供了更高级的自动化工具进行依赖注入),但这主要目的是给大家展示 Spring 如何在底层工作原理,并提供了一种手动控制依赖注入的方法。 ```java Before MyServiceB = MyServiceB{myPropertyValue='null', myServiceA=null, fieldMyServiceA=null} 解析方法依赖结果: --------------------------------------------- Name: com.xcs.spring.service.MyServiceB.setMyServiceA Value: com.xcs.spring.service.MyServiceA@202b0582 --------------------------------------------- 解析字段依赖结果: --------------------------------------------- Name: com.xcs.spring.service.MyServiceB.fieldMyServiceA Value: com.xcs.spring.service.MyServiceA@202b0582 --------------------------------------------- 解析字段依赖结果: --------------------------------------------- Name: com.xcs.spring.service.MyServiceB.myPropertyValue Value: Hello from Environment! --------------------------------------------- After MyServiceB = MyServiceB{myPropertyValue='Hello from Environment!', myServiceA=com.xcs.spring.service.MyServiceA@202b0582, fieldMyServiceA=com.xcs.spring.service.MyServiceA@202b0582} ``` ### 六、时序图 #### 解析环境变量 通过使用`@Value`注解,我们可以请求一个特定的环境属性或变量。例如,我们可能会这样请求一个名为`my.property.value`的属性。 ~~~mermaid sequenceDiagram Title: resolveDependency方法解析环境属性和变量时序图 ResolveDependencyApplication-->>AbstractAutowireCapableBeanFactory:resolveDependency(descriptor,requestingBeanName) note over AbstractAutowireCapableBeanFactory: 请求解析依赖 AbstractAutowireCapableBeanFactory->>DefaultListableBeanFactory:resolveDependency(descriptor, requestingBeanName, null, null) note over DefaultListableBeanFactory: 转到具体的Bean工厂进行解析 DefaultListableBeanFactory->>DefaultListableBeanFactory:doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter) note over DefaultListableBeanFactory: 进行实际的依赖解析 DefaultListableBeanFactory->>DefaultListableBeanFactory:getAutowireCandidateResolver() note over DefaultListableBeanFactory: 获取当前的依赖解析策略 DefaultListableBeanFactory->>QualifierAnnotationAutowireCandidateResolver:getSuggestedValue(descriptor) note over QualifierAnnotationAutowireCandidateResolver: 基于给定的描述符查找建议的值 QualifierAnnotationAutowireCandidateResolver->>QualifierAnnotationAutowireCandidateResolver:findValue(annotationsToSearch) note over QualifierAnnotationAutowireCandidateResolver: 在指定的注解中寻找值 QualifierAnnotationAutowireCandidateResolver->>QualifierAnnotationAutowireCandidateResolver:extractValue(attr) note over QualifierAnnotationAutowireCandidateResolver: 从注解属性中提取值 QualifierAnnotationAutowireCandidateResolver->>DefaultListableBeanFactory:返回@Value注解的表达式 note over DefaultListableBeanFactory: 解析注解中的表达式或值 DefaultListableBeanFactory->>AbstractBeanFactory:resolveEmbeddedValue(value) note over AbstractBeanFactory: 解析嵌入的值(如占位符或SpEL表达式) DefaultListableBeanFactory->>AbstractBeanFactory:evaluateBeanDefinitionString(strVal, bd) note over AbstractBeanFactory: 对bean定义字符串进行评估(可能是一个表达式) DefaultListableBeanFactory->>AbstractBeanFactory:getTypeConverter() note over AbstractBeanFactory: 获取用于类型转换的转换器 DefaultListableBeanFactory->>TypeConverterSupport:convertIfNecessary(value, type, typeDescriptor()) note over TypeConverterSupport: 根据需要将值转换为指定的类型 TypeConverterSupport->>DefaultListableBeanFactory:返回类型转换的结果 note over DefaultListableBeanFactory: 用转换后的值继续解析流程 DefaultListableBeanFactory->>AbstractAutowireCapableBeanFactory:返回环境属性 AbstractAutowireCapableBeanFactory->>ResolveDependencyApplication:返回环境属性 ~~~ #### 解析Bean依赖 这是其主要的功能。当我们有一个bean,它需要另一个bean的实例时,`resolveDependency` 会为我们找到并提供所需的bean。例如,如果一个`MyServiceB`类需要一个`MyServiceA`类的实例,那么`resolveDependency` 可以为`MyServiceB`类提供一个`MyServiceA`的实例。 ~~~mermaid sequenceDiagram Title: resolveDependency方法解析Bean依赖时序图 ResolveDependencyApplication-->>AbstractAutowireCapableBeanFactory:resolveDependency(descriptor,requestingBeanName) note right of AbstractAutowireCapableBeanFactory: 开始解析依赖请求 AbstractAutowireCapableBeanFactory->>DefaultListableBeanFactory:resolveDependency(descriptor, requestingBeanName, null, null) note right of DefaultListableBeanFactory: 委托给具体的Bean工厂进行依赖解析 DefaultListableBeanFactory->>DefaultListableBeanFactory:doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter) note right of DefaultListableBeanFactory: 执行具体的依赖解析逻辑 DefaultListableBeanFactory->>DefaultListableBeanFactory:resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter) note right of DefaultListableBeanFactory: 解析满足条件的多个Beans DefaultListableBeanFactory->>DefaultListableBeanFactory:findAutowireCandidates(beanName, type, descriptor) note right of DefaultListableBeanFactory: 查找适合自动装配的候选Beans DefaultListableBeanFactory->>DefaultListableBeanFactory:isSelfReference(beanName, candidate) note right of DefaultListableBeanFactory: 检查bean是否引用了自身 DefaultListableBeanFactory->>DefaultListableBeanFactory:isAutowireCandidate(candidate, descriptor) note right of DefaultListableBeanFactory: 检查bean是否是一个合适的自动装配候选 DefaultListableBeanFactory->>DefaultListableBeanFactory:addCandidateEntry(result, candidate, descriptor, requiredType) note right of DefaultListableBeanFactory: 将找到的候选添加到结果集中 DefaultListableBeanFactory->>DependencyDescriptor:resolveCandidate(candidateName, requiredType, beanFactory) note right of DependencyDescriptor: 尝试解析并返回候选Bean DependencyDescriptor->>AbstractBeanFactory:getBean(String name) note right of AbstractBeanFactory: 请求获取指定名称的bean AbstractBeanFactory->>DependencyDescriptor:返回候选的Bean对象 note right of DependencyDescriptor: 返回找到的Bean实例 DependencyDescriptor->>DefaultListableBeanFactory:返回候选的Bean对象 note right of DefaultListableBeanFactory: 将Bean实例返回给Bean工厂 DefaultListableBeanFactory->>DefaultListableBeanFactory:determineAutowireCandidate(matchingBeans, descriptor) note right of DefaultListableBeanFactory: 从匹配的Beans中确定自动装配的候选 DefaultListableBeanFactory->>AbstractAutowireCapableBeanFactory:返回Bean note right of AbstractAutowireCapableBeanFactory: 将确定的Bean返回给上一级的Bean工厂 AbstractAutowireCapableBeanFactory->>ResolveDependencyApplication:返回bean note right of ResolveDependencyApplication: 返回bean给依赖解析的请求者 ~~~ ### 七、源码分析 #### 解析环境变量 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency`方法中,此方法将尝试使用懒加载代理或直接解析依赖项,具体使用哪种方式取决于上下文及其他配置(本次研究环境属性)。 ```java @Override @Nullable public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName, @Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException { // ... [代码部分省略以简化] // 尝试为依赖获取懒加载代理 Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary( descriptor, requestingBeanName); // 如果懒加载代理不可用,则执行实际的依赖解析 if (result == null) { result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter); } return result; } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency`方法中,此代码段尝试解析一个依赖,先查找建议的值(`@Value`注解),然后进行必要的类型转换。 ```java @Nullable public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName, @Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException { try { // ... [代码部分省略以简化] // 获取依赖描述符中定义的依赖类型 Class type = descriptor.getDependencyType(); // 步骤1: 从自动装配候选解析器中获取建议的依赖值 Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor); if (value != null) { // 如果建议的值是一个字符串 if (value instanceof String) { // 步骤2: 解析嵌入在字符串中的值(例如,从环境变量中查找) String strVal = resolveEmbeddedValue((String) value); // 获取bean定义,如果beanName存在且已经在容器中,则获取与beanName相关的bean定义 BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null); // 步骤3: 对字符串值进行求值,可能会处理表达式之类的内容 value = evaluateBeanDefinitionString(strVal, bd); } // 获取类型转换器,如果传入了外部转换器则使用它,否则使用默认的转换器 TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter()); try { // 步骤4: 尝试进行必要的类型转换 return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor()); } catch (UnsupportedOperationException ex) { // 处理不支持的操作异常(具体实现已省略) } } // ... [代码部分省略以简化] } finally { // ... [代码部分省略以简化] } } ``` > `org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency`方法步骤1。 在`org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver#getSuggestedValue`方法中,主要目的是从一个`DependencyDescriptor`中提取建议的值。`DependencyDescriptor`可以描述一个bean的属性(可能是一个字段或者方法参数)。 ```java @Override @Nullable public Object getSuggestedValue(DependencyDescriptor descriptor) { // 从字段的注解中尝试找到建议的值 Object value = findValue(descriptor.getAnnotations()); // 如果在字段上没有找到,则从方法的注解中尝试找到 if (value == null) { MethodParameter methodParam = descriptor.getMethodParameter(); if (methodParam != null) { value = findValue(methodParam.getMethodAnnotations()); } } // 返回建议的值,如果没有找到则返回null return value; } ``` 在`org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver#findValue`方法中,主要目的是从提供的注解数组中寻找一个特定的注解(`@Value`)并提取其值。 ```java @Nullable protected Object findValue(Annotation[] annotationsToSearch) { // 如果注解数组非空,则进入检查逻辑 if (annotationsToSearch.length > 0) { // 从注解数组中获取合并后的特定注解属性(这里的特定注解可能是@Value) AnnotationAttributes attr = AnnotatedElementUtils.getMergedAnnotationAttributes( AnnotatedElementUtils.forAnnotations(annotationsToSearch), this.valueAnnotationType); // 如果找到了相应的注解属性,则提取它的值 if (attr != null) { return extractValue(attr); } } // 没有找到相应的注解属性或值,返回null return null; } ``` 在`org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver#extractValue`方法中,从`@Value`注解属性中,提取一个`value`属性值。 ```java protected Object extractValue(AnnotationAttributes attr) { // 从注解属性中尝试获取'VALUE'(一般为"value")的属性值 Object value = attr.get(AnnotationUtils.VALUE); // 如果该属性值为空,那么抛出一个异常 if (value == null) { throw new IllegalStateException("Value annotation must have a value attribute"); } // 返回提取的值 return value; } ``` > `org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency`方法步骤2。 在`org.springframework.beans.factory.support.AbstractBeanFactory#resolveEmbeddedValue`方法中,主要接受一个字符串值,并利用`StringValueResolver`逐一对其进行解析`StringValueResolver`逐一对其进行解析,然后返回最终解析后的字符串值。 ```java @Override @Nullable public String resolveEmbeddedValue(@Nullable String value) { // 如果传入的值为空,直接返回null if (value == null) { return null; } // 初始化结果为传入的值 String result = value; // 遍历当前对象中所有的字符串值解析器 for (StringValueResolver resolver : this.embeddedValueResolvers) { // 使用解析器解析当前的结果值 result = resolver.resolveStringValue(result); // 如果解析后的值为空,直接返回null if (result == null) { return null; } } // 返回最终解析后的字符串值 return result; } ``` > `org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency`方法步骤3。 在`org.springframework.beans.factory.support.AbstractBeanFactory#evaluateBeanDefinitionString`方法中,主要负责对一个Bean定义中的字符串值(可能是一个表达式`${my.property.value}`)进行求值。如果配置了`beanExpressionResolver`,则会使用它进行求值;如果没有,则直接返回原始值。 ```java @Nullable protected Object evaluateBeanDefinitionString(@Nullable String value, @Nullable BeanDefinition beanDefinition) { // 如果没有设置beanExpressionResolver(即解析表达式的解析器),直接返回原始值 if (this.beanExpressionResolver == null) { return value; } Scope scope = null; // 定义作用域变量 if (beanDefinition != null) { // 从Bean定义中获取作用域名称 String scopeName = beanDefinition.getScope(); if (scopeName != null) { // 获取该作用域的具体实例 scope = getRegisteredScope(scopeName); } } // 使用beanExpressionResolver对字符串值进行求值,并返回结果 return this.beanExpressionResolver.evaluate(value, new BeanExpressionContext(this, scope)); } ``` > `org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency`方法步骤4。 在`org.springframework.beans.TypeConverterSupport#convertIfNecessary(value,requiredType,typeDescriptor)`方法中,首先确保有一个可用的`typeConverterDelegate`来进行实际的转换工作,然后尝试将给定值转换为指定的类型。 ```java @Nullable @Override public T convertIfNecessary(@Nullable Object value, @Nullable Class requiredType, @Nullable TypeDescriptor typeDescriptor) throws TypeMismatchException { // 断言,确保typeConverterDelegate(类型转换代理)不为空 Assert.state(this.typeConverterDelegate != null, "No TypeConverterDelegate"); try { // 通过typeConverterDelegate进行类型转换 return this.typeConverterDelegate.convertIfNecessary(null, null, value, requiredType, typeDescriptor); } // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.TypeConverterDelegate#convertIfNecessary(propertyName, oldValue, newValue,requiredType, typeDescriptor)`方法中,主要考虑了许多转换场景,使其能够处理各种类型和结构的数据。 ```java public T convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue, @Nullable Class requiredType, @Nullable TypeDescriptor typeDescriptor) throws IllegalArgumentException { // 查找对于此类型的自定义编辑器 PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName); ConversionFailedException conversionAttemptEx = null; // 如果没有自定义编辑器但指定了自定义ConversionService,则尝试转换 ConversionService conversionService = this.propertyEditorRegistry.getConversionService(); if (editor == null && conversionService != null && newValue != null && typeDescriptor != null) { // ... [代码部分省略以简化] } Object convertedValue = newValue; // 如果值不是所需的类型,进行转换 if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) { // ... [代码部分省略以简化] } boolean standardConversion = false; // 尝试应用标准类型转换规则(如果适用) if (requiredType != null) { // 对不同类型的专门转换逻辑 if (convertedValue != null) { if (Object.class == requiredType) { return (T) convertedValue; } // 这里根据不同的requiredType类型进行了不同的转换处理,例如处理数组、集合、映射、枚举等不同类型的转换。 else if (requiredType.isArray()) { // ... [代码部分省略以简化] } else if (convertedValue instanceof Collection) { // ... [代码部分省略以简化] } else if (convertedValue instanceof Map) { // ... [代码部分省略以简化] } if (convertedValue.getClass().isArray() && Array.getLength(convertedValue) == 1) { // ... [代码部分省略以简化] } if (String.class == requiredType && ClassUtils.isPrimitiveOrWrapper(convertedValue.getClass())) { // ... [代码部分省略以简化] } else if (convertedValue instanceof String && !requiredType.isInstance(convertedValue)) { // ... [代码部分省略以简化] } else if (convertedValue instanceof Number && Number.class.isAssignableFrom(requiredType)) { // ... [代码部分省略以简化] } } else { // convertedValue == null if (requiredType == Optional.class) { convertedValue = Optional.empty(); } } if (!ClassUtils.isAssignableValue(requiredType, convertedValue)) { // ... [代码部分省略以简化] } } if (conversionAttemptEx != null) { // ... [代码部分省略以简化] } return (T) convertedValue; } ``` #### 解析Bean依赖 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency`方法中,此方法将尝试使用懒加载代理或直接解析依赖项,具体使用哪种方式取决于上下文及其他配置(本次研究Bean依赖)。 ```java @Override @Nullable public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName, @Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException { // ... [代码部分省略以简化] // 尝试为依赖获取懒加载代理 Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary( descriptor, requestingBeanName); // 如果懒加载代理不可用,则执行实际的依赖解析 if (result == null) { result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter); } return result; } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency`方法中,主要是解析并注入依赖项。首先,它尝试解析多个Bean候选项(例如,当需要注入的是List或Map类型)。如果没有找到匹配的Bean,它可能会抛出异常。如果有多个匹配的Bean,它会尝试确定最合适的一个进行注入。如果只有一个匹配的Bean,则直接进行注入。 ```java @Nullable public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName, @Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException { // ... [代码部分省略以简化] try { // ... [代码部分省略以简化] // 步骤1: 尝试解析多个Bean候选项。例如,当需要注入List或Map类型的时候 Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter); if (multipleBeans != null) { return multipleBeans; } // 步骤2: 寻找符合类型匹配的Bean候选项 Map matchingBeans = findAutowireCandidates(beanName, type, descriptor); if (matchingBeans.isEmpty()) { if (isRequired(descriptor)) { raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor); } return null; } String autowiredBeanName; Object instanceCandidate; // 步骤3: 如果有多个匹配的Bean,则尝试确定哪一个是最合适的 if (matchingBeans.size() > 1) { autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor); if (autowiredBeanName == null) { if (isRequired(descriptor) || !indicatesMultipleBeans(type)) { return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans); } else { // 对于非必需的Collection/Map,忽略非唯一的情况 return null; } } instanceCandidate = matchingBeans.get(autowiredBeanName); } else { // 只有一个匹配的Bean Map.Entry entry = matchingBeans.entrySet().iterator().next(); autowiredBeanName = entry.getKey(); instanceCandidate = entry.getValue(); } // 记录已经自动装配的Bean名称 if (autowiredBeanNames != null) { autowiredBeanNames.add(autowiredBeanName); } // 如果候选对象是一个类,则尝试解析为实际的Bean实例 if (instanceCandidate instanceof Class) { instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this); } Object result = instanceCandidate; // ... [代码部分省略以简化] return result; } finally { // ... [代码部分省略以简化] } } ``` > `org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency`方法步骤1。 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveMultipleBeans`方法中,解决了当依赖是多值类型(如`Stream`、数组、`Collection`、`Map`)时如何自动注入对应的Bean。该方法首先判断依赖的类型,然后针对不同类型进行解析。对于每种类型,它都会查找所有匹配的Bean候选项,并按需要转换和排序它们。 ```java @Nullable private Object resolveMultipleBeans(DependencyDescriptor descriptor, @Nullable String beanName, @Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) { Class type = descriptor.getDependencyType(); // 判断依赖是否为Java 8 Stream类型 if (descriptor instanceof StreamDependencyDescriptor) { // ... [代码部分省略以简化] } // 判断依赖是否为数组类型 else if (type.isArray()) { // ... [代码部分省略以简化] return result; } // 判断依赖是否为集合接口类型 else if (Collection.class.isAssignableFrom(type) && type.isInterface()) { // ... [代码部分省略以简化] return result; } // 判断依赖是否为Map类型 else if (Map.class == type) { // ... [代码部分省略以简化] return matchingBeans; } else { return null; } } ``` > `org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency`方法步骤2。 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#findAutowireCandidates`方法中,主要目的是确定哪些Bean适合自动注入给定的依赖。它首先从当前的`BeanFactory`及其祖先中获取所有可能的候选Bean,然后根据各种条件(如类型匹配、qualifiers等)过滤这些候选项。如果在首次查找中没有找到任何匹配的bean,它还会尝试回退匹配来找到适当的bean。 ```java protected Map findAutowireCandidates( @Nullable String beanName, Class requiredType, DependencyDescriptor descriptor) { // 1. 从BeanFactory及其祖先中获取所有可能的候选bean名称,这些bean的类型与所需的类型匹配。 String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this, requiredType, true, descriptor.isEager()); // 2. 初始化一个空的结果映射,用于保存候选bean。 Map result = CollectionUtils.newLinkedHashMap(candidateNames.length); // 3. 遍历预定义的可解析的依赖项。 for (Map.Entry, Object> classObjectEntry : this.resolvableDependencies.entrySet()) { Class autowiringType = classObjectEntry.getKey(); // 4. 检查当前的依赖项是否与所需的类型兼容。 if (autowiringType.isAssignableFrom(requiredType)) { Object autowiringValue = classObjectEntry.getValue(); // 5. 对当前的值进行自动装配的解析。 autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType); // 6. 如果解析后的值与所需的类型相匹配,将其添加到结果映射中。 if (requiredType.isInstance(autowiringValue)) { result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue); break; } } } // 7. 遍历所有可能的候选bean名称。 for (String candidate : candidateNames) { // 8. 如果当前bean名称不是对自己的引用,并且它是一个有效的自动装配候选项,则将其添加到结果映射中。 if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) { addCandidateEntry(result, candidate, descriptor, requiredType); } } // 9. 如果没有找到任何匹配的候选bean。 if (result.isEmpty()) { boolean multiple = indicatesMultipleBeans(requiredType); // 10. 为回退匹配创建一个新的描述符。 DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch(); // 11. 重新遍历所有候选bean名称。 for (String candidate : candidateNames) { // 12. 如果当前bean名称不是对自己的引用,并且它是一个有效的回退匹配的候选项,则将其添加到结果映射中。 if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, fallbackDescriptor) && (!multiple || getAutowireCandidateResolver().hasQualifier(descriptor))) { addCandidateEntry(result, candidate, descriptor, requiredType); } } // 13. 如果还没有找到任何匹配的候选bean,考虑自引用的bean作为候选bean。 if (result.isEmpty() && !multiple) { for (String candidate : candidateNames) { if (isSelfReference(beanName, candidate) && (!(descriptor instanceof MultiElementDescriptor) || !beanName.equals(candidate)) && isAutowireCandidate(candidate, fallbackDescriptor)) { addCandidateEntry(result, candidate, descriptor, requiredType); } } } } // 14. 返回找到的候选bean的映射。 return result; } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#addCandidateEntry`方法中,根据不同的`DependencyDescriptor`类型和其他条件决定如何将候选Bean及其实例或类型添加到提供的映射中。对于需要多个元素的依赖项(如集合或数组),它会添加所有的Bean实例;对于单例或需要排序的流描述符,它会添加Bean实例;而对于其他情况,只会添加Bean的类型。 ```java private void addCandidateEntry(Map candidates, String candidateName, DependencyDescriptor descriptor, Class requiredType) { // 如果DependencyDescriptor是一个MultiElementDescriptor(意味着描述的依赖可能有多个元素,例如集合或数组) if (descriptor instanceof MultiElementDescriptor) { // 解析候选Bean的实例 Object beanInstance = descriptor.resolveCandidate(candidateName, requiredType, this); // 如果解析的实例不是NullBean(即一个占位符对象,代表null值),则将其添加到候选列表中 if (!(beanInstance instanceof NullBean)) { candidates.put(candidateName, beanInstance); } } // 如果当前Bean是单例,或者DependencyDescriptor是一个StreamDependencyDescriptor并且有序 else if (containsSingleton(candidateName) || (descriptor instanceof StreamDependencyDescriptor && ((StreamDependencyDescriptor) descriptor).isOrdered())) { // 解析候选Bean的实例 Object beanInstance = descriptor.resolveCandidate(candidateName, requiredType, this); // 如果解析的实例是NullBean,则将null添加到映射中,否则添加实际的Bean实例 candidates.put(candidateName, (beanInstance instanceof NullBean ? null : beanInstance)); } // 其他情况下,只将Bean的类型而不是实例添加到映射中 else { candidates.put(candidateName, getType(candidateName)); } } ``` 在`org.springframework.beans.factory.config.DependencyDescriptor#resolveCandidate`方法中,最后发现,在Spring的依赖注入过程中,当一个bean依赖于另一个bean时,底层的实现最终会通过`getBean`方法从容器中获取所依赖的bean实例。 ```java public Object resolveCandidate(String beanName, Class requiredType, BeanFactory beanFactory) throws BeansException { // 根据提供的bean名称从beanFactory中获取并返回bean的实例 return beanFactory.getBean(beanName); } ``` > `org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency`方法步骤3。 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#determineAutowireCandidate`方法中,主要是存在多个候选bean时确定应该自动注入哪一个。它首先检查是否有被标记为"primary"的bean,然后检查是否有优先级最高的bean。如果这两种方法都无法确定,它会回退到查找与依赖描述符匹配的bean名字或是否该bean实例存在于可解析的依赖中。如果仍然没有找到匹配的bean,它会返回null。 ```java @Nullable protected String determineAutowireCandidate(Map candidates, DependencyDescriptor descriptor) { // 获取依赖的类型 Class requiredType = descriptor.getDependencyType(); // 确定是否有被标记为"primary"的候选bean String primaryCandidate = determinePrimaryCandidate(candidates, requiredType); if (primaryCandidate != null) { // 如果有主要的候选bean,则返回这个bean的名字 return primaryCandidate; } // 检查是否有最高优先级的候选bean String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType); if (priorityCandidate != null) { // 如果有优先级最高的候选bean,则返回这个bean的名字 return priorityCandidate; } // 如果上述两种方法都无法确定一个bean,那么进行回退处理 for (Map.Entry entry : candidates.entrySet()) { String candidateName = entry.getKey(); Object beanInstance = entry.getValue(); // 如果候选bean实例存在于可解析的依赖中,或者候选bean的名字与描述符中的依赖名字匹配,则选择该候选bean if ((beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) || matchesBeanName(candidateName, descriptor.getDependencyName())) { return candidateName; } } // 如果没有匹配的bean,返回null return null; } ``` ### 八、注意事项 1. **循环依赖** + 确保不引入循环依赖,否则会导致 Bean 创建失败。Spring 有一些内置的检测机制,但在自定义逻辑时仍需格外注意。 2. **候选 Bean 的选择** + 在有多个候选 Bean 可用于注入时,该如何选择是一个重要问题。Spring 提供了 `@Primary`, `@Priority` 等注解来辅助决策,但你可能需要考虑其他因素。 3. **自定义类型转换** + 如果需要,应确保提供适当的类型转换逻辑,以将 Bean 转换为所需的类型。 4. **性能考虑** + 避免不必要的重复查找或计算。在可能的情况下,考虑缓存结果,特别是对于高频率的依赖查找。 5. **生命周期和作用域** + 确保考虑到目标 Bean 的生命周期和作用域。例如,一个 prototype 作用域的 Bean 不应该被注入到一个 singleton 作用域的 Bean 中,除非你明确知道这样做的后果。 6. **考虑非常规的依赖源** + `resolveDependency` 可能需要考虑来自非常规来源的依赖,如 `FactoryBeans`、`BeanFactory` 或其他特殊的依赖提供者。 ### 九、总结 #### 最佳实践总结 1. **明确环境和配置** + 在示例中,使用了 `AnnotationConfigApplicationContext` 作为 Spring 的应用上下文,它基于 Java 注解来配置和初始化 Spring 容器。指定了 `MyConfiguration` 类作为配置源,它用于引导整个应用程序的上下文环境。 2. **简化依赖解析** + 通过将依赖解析过程封装到具体的方法中 (`methodResolveDependency` 和 `fieldResolveDependency`),代码的逻辑变得清晰且可维护。 3. **利用Spring的底层API** + 虽然大部分时间我们依赖于 Spring 的自动化特性进行依赖注入,但在这个实践中,我们深入地使用了 Spring 的底层 `resolveDependency` 方法来手动解析和注入依赖。这不仅展示了 Spring 的内部工作原理,还提供了当自动注入不适用时的替代解决方案。 4. **手动依赖注入的用例** + 即使 `MyServiceB` 不是由 Spring 托管的 Bean,我们仍然能够使用 Spring 的机制为它解析和注入所需的依赖。这种能力在某些特定场景下可能非常有用,例如当我们需要将 Spring 与非 Spring 管理的代码集成时。 5. **使用反射增强灵活性** + 通过反射API,我们能够动态地访问和操作目标对象的方法和字段,无论它们是否是公开的。这为我们提供了极大的灵活性,特别是当我们需要操作第三方库中的类时。 6. **确保配置的完整性** + 确保所需的所有配置资源,如 `application.properties` 文件,都在正确的位置并且被正确加载。在这个示例中,它被用来为 `MyServiceB` 提供一个属性值。 7. **验证和测试** + 最后,通过打印方法,确保了所有依赖都已正确解析和注入。在实际应用中,我们应该有相应的单元测试来验证这些行为。 #### 源码分析总结 **解析环境变量** 1. **解析 `@Value` 注解** + `doResolveDependency` 方法首先检查是否存在一个建议的值,通常来自 `@Value` 注解。如果找到这样的值,它会首先进行字符串替换(例如替换属性占位符),然后可能对该值进行求值(如果它是一个表达式),最后尝试将该值转换为目标类型。 2. **类型转换** + 如果找到了一个建议的值,或者已经解析了一个依赖项但其类型与所需的类型不匹配,系统会尝试进行类型转换。Spring有一个强大的类型转换系统,能够处理各种各样的转换场景。 3. **深入的类型转换** + 如果需要进行更复杂的类型转换(例如从集合到数组、从字符串到数字等),Spring提供了许多内置的转换规则来处理这些场景。 **解析Bean依赖** 1. **依赖解析的起点** + `resolveDependency`是Spring的主要方法,用于解析bean的依赖关系。 2. **懒加载代理判断** + 首先,尝试使用懒加载代理满足依赖关系。如果不能使用懒加载代理,它会进一步尝试解析依赖。 3. **解析具体的依赖** + 在`doResolveDependency`中,根据依赖的类型,Spring可能尝试解析多个Bean候选项,如List或Map。它会搜索匹配的Bean,并根据条件(如类型、qualifiers等)选择合适的Bean进行注入。 4. **多值依赖的处理** + 如果依赖是多值类型(如集合或映射),Spring将在`resolveMultipleBeans`中进行处理。 5. **确定注入的Bean** + 如果有多个可能的Bean候选项,`determineAutowireCandidate`将帮助确定哪一个Bean是最佳的注入选择,这可能基于“primary”标记或Bean的优先级。 6. **获取实际的Bean实例** + 在所有这些决策之后,Spring最终会通过`getBean`方法获取并返回所依赖的Bean实例。 ================================================ FILE: spring-core/spring-core-resolveDependency/pom.xml ================================================ spring-core com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-core-resolveDependency ================================================ FILE: spring-core/spring-core-resolveDependency/src/main/java/com/xcs/spring/ResolveDependencyApplication.java ================================================ package com.xcs.spring; import com.xcs.spring.config.MyConfiguration; import com.xcs.spring.service.MyServiceA; import com.xcs.spring.service.MyServiceB; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.DependencyDescriptor; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.core.MethodParameter; import org.springframework.util.ReflectionUtils; import java.lang.reflect.Field; import java.lang.reflect.Method; /** * @author xcs * @date 2023年10月25日 10时13分 **/ public class ResolveDependencyApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); // 获得Bean工厂 ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); // 被注入对象 MyServiceB injectTarget = new MyServiceB(); System.out.println("Before MyServiceB = " + injectTarget + "\n"); methodResolveDependency(beanFactory, injectTarget, "setMethodMyServiceA"); fieldResolveDependency(beanFactory, injectTarget, "fieldMyServiceA"); fieldResolveDependency(beanFactory, injectTarget, "myPropertyValue"); System.out.println("After MyServiceB = " + injectTarget + "\n"); } /** * 解析方法依赖 * * @param beanFactory * @param injectTarget */ public static void methodResolveDependency(ConfigurableListableBeanFactory beanFactory, Object injectTarget, String name) { try { // 1. 获取MyServiceB类中名为setMyServiceA的方法的引用 Method method = injectTarget.getClass().getMethod(name, MyServiceA.class); // 2. 创建一个描述此方法参数的DependencyDescriptor DependencyDescriptor desc = new DependencyDescriptor(new MethodParameter(method, 0), true); // 3. 使用BeanFactory来解析这个方法参数的依赖 Object value = beanFactory.resolveDependency(desc, null); System.out.println("解析方法依赖结果:"); System.out.println("---------------------------------------------"); System.out.println(String.format("Name: %s.%s",method.getDeclaringClass().getName(),method.getName())); System.out.println(String.format("Value: %s", value)); System.out.println("---------------------------------------------\n"); // 4. 使方法可访问(特别是如果它是private的) ReflectionUtils.makeAccessible(method); // 5. 使用反射调用这个方法,将解析得到的依赖注入到目标对象中 method.invoke(injectTarget, value); } catch (Exception e) { e.printStackTrace(); } } /** * 解析字段依赖 * * @param beanFactory * @param injectTarget */ public static void fieldResolveDependency(ConfigurableListableBeanFactory beanFactory, Object injectTarget, String name) { try { // 1. 获取MyServiceB类中名为fieldMyServiceA的字段的引用 Field field = injectTarget.getClass().getDeclaredField(name); // 2. 创建一个描述此字段的DependencyDescriptor DependencyDescriptor desc = new DependencyDescriptor(field, true); // 3. 使用BeanFactory来解析这个字段的依赖 Object value = beanFactory.resolveDependency(desc, null); System.out.println("解析字段依赖结果:"); System.out.println("---------------------------------------------"); System.out.println(String.format("Name: %s.%s", field.getDeclaringClass().getName(), field.getName())); System.out.println(String.format("Value: %s", value)); System.out.println("---------------------------------------------\n"); // 4. 使字段可访问(特别是如果它是private的) ReflectionUtils.makeAccessible(field); // 5. 使用反射设置这个字段的值,将解析得到的依赖注入到目标对象中 field.set(injectTarget, value); } catch (Exception e) { e.printStackTrace(); } } } ================================================ FILE: spring-core/spring-core-resolveDependency/src/main/java/com/xcs/spring/config/MyConfiguration.java ================================================ package com.xcs.spring.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; /** * @author xcs * @date 2023年09月19日 16时35分 **/ @Configuration @ComponentScan("com.xcs.spring") @PropertySource("classpath:application.properties") public class MyConfiguration { } ================================================ FILE: spring-core/spring-core-resolveDependency/src/main/java/com/xcs/spring/service/MyServiceA.java ================================================ package com.xcs.spring.service; import org.springframework.stereotype.Service; /** * @author xcs * @date 2023年10月25日 10时36分 **/ @Service public class MyServiceA { } ================================================ FILE: spring-core/spring-core-resolveDependency/src/main/java/com/xcs/spring/service/MyServiceB.java ================================================ package com.xcs.spring.service; import org.springframework.beans.factory.annotation.Value; /** * @author xcs * @date 2023年10月25日 10时37分 **/ public class MyServiceB { /** * 方法注入 */ private MyServiceA methodMyServiceA; /** * 字段注入 */ private MyServiceA fieldMyServiceA; /** * 字段注入 (环境变量) */ @Value("${my.property.value}") private String myPropertyValue; public void setMethodMyServiceA(MyServiceA methodMyServiceA){ this.methodMyServiceA = methodMyServiceA; } @Override public String toString() { return "MyServiceB{" + "myPropertyValue='" + myPropertyValue + '\'' + ", methodMyServiceA=" + methodMyServiceA + ", fieldMyServiceA=" + fieldMyServiceA + '}'; } } ================================================ FILE: spring-core/spring-core-resolveDependency/src/main/resources/application.properties ================================================ my.property.value = Hello from Environment! ================================================ FILE: spring-dataops/pom.xml ================================================ spring-reading com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-dataops pom spring-dataops-validator spring-dataops-propertyEditor spring-dataops-converter spring-dataops-genericConverter spring-dataops-converterFactory spring-dataops-conditionalConverter spring-dataops-conversionService spring-dataops-printer spring-dataops-parser ================================================ FILE: spring-dataops/spring-dataops-conditionalConverter/README.md ================================================ ## ConditionalConverter - [ConditionalConverter](#conditionalconverter) - [一、基本信息](#一基本信息) - [二、知识储备](#二知识储备) - [三、基本描述](#三基本描述) - [四、主要功能](#四主要功能) - [五、接口源码](#五接口源码) - [六、主要实现](#六主要实现) - [七、最佳实践](#七最佳实践) - [八、与其他组件的关系](#八与其他组件的关系) - [九、常见问题](#九常见问题) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、知识储备 1. **Converter** + [Converter](/spring-dataops/spring-dataops-converter/README.md) 接口是 Spring 框架中用于实现类型转换的一个关键组件。定义了一个简单的方法,用于将一种类型(源类型 S)转换为另一种类型(目标类型 T)。通过实现这个接口,我们可以创建自定义的转换逻辑,以便在 Spring 应用程序中无缝地进行复杂的数据转换。 2. **ConverterFactory** + [ConverterFactory](/spring-dataops/spring-dataops-converterFactory/README.md) 是 Spring 框架中的一个接口,用于实现类型转换的工厂。在 Spring 的类型转换体系中,`ConverterFactory` 接口扮演着创建特定类型转换器(`Converter`)的角色。这个接口主要用于那些有共同特性的一组类型转换场景。 3. **GenericConverter** + [GenericConverter](/spring-dataops/spring-dataops-genericConverter/README.md) 是 Spring 框架中的一个关键接口,专门用于类型转换。这个接口与 Spring 的类型转换系统紧密相关,其主要功能是将一个类型的对象转换为另一个类型。与 `Converter` 接口相比,`GenericConverter` 提供了更灵活的转换机制,允许转换操作在多个源目标类型和目标类型之间进行。 ### 三、基本描述 `ConditionalConverter` 是 Spring 框架中用于类型转换的一个接口,它是 Spring 类型转换系统的一部分。这个接口的主要目的是为转换器(Converter)提供一个条件检查的功能,使得在执行实际的类型转换之前,转换器能够根据特定的条件判断是否应该进行转换。 ### 四、主要功能 1. **条件性检查** - `ConditionalConverter` 提供了一个 `matches(TypeDescriptor sourceType, TypeDescriptor targetType)` 方法,这是它的主要功能。这个方法允许转换器在执行实际的转换逻辑之前,根据源类型和目标类型判断是否应该进行转换。 2. **灵活性** - 通过实现这个接口,我们可以编写更灵活的转换逻辑。转换器可以在运行时根据当前的上下文或特定的条件决定是否执行转换。这种方法特别适用于那些转换逻辑依赖于动态条件或环境变量的情况。 3. **与其他转换器接口的协作** - 虽然 `ConditionalConverter` 可以单独实现,但通常它会与其他类型转换器接口(如 `Converter` 或 `GenericConverter`)结合使用。在这种情况下,`ConditionalConverter` 为转换提供了一个额外的决策层,允许转换器根据条件进行智能选择。 4. **减少资源消耗** - 对于资源密集型的转换操作,使用 `ConditionalConverter` 可以减少不必要的资源消耗,因为它允许转换器在确定转换是必要的之前,先进行条件判断。 ### 五、接口源码 `ConditionalConverter` 接口在 Spring 框架中用于有条件的类型转换。它允许转换器(Converter)、泛型转换器(GenericConverter)或转换器工厂(ConverterFactory)基于源对象和目标对象的类型特征(如注解或特定方法的存在)来决定是否执行特定的转换操作。 ```java /** * 允许一个 {@link Converter}、{@link GenericConverter} 或 {@link ConverterFactory} * 根据 {@code source} 源类型和 {@code target} 目标类型的 {@link TypeDescriptor} 属性来有条件地执行转换。 * *

这通常用于根据字段或类级特征(如注解或方法)的存在,有选择性地匹配自定义转换逻辑。 * 例如,在将字符串字段转换为日期字段时,如果目标字段也用 {@code @DateTimeFormat} 注解标记, * 实现可能会返回 {@code true}。 * *

另一个例子是,在将字符串字段转换为 {@code Account} 字段时, * 如果目标 Account 类定义了一个 {@code public static findAccount(String)} 方法, * 实现可能会返回 {@code true}。 * * @author Phillip Webb * @author Keith Donald * @since 3.2 * @see Converter * @see GenericConverter * @see ConverterFactory * @see ConditionalGenericConverter */ public interface ConditionalConverter { /** * 是否应该选择当前考虑的从 {@code sourceType} 源类型到 {@code targetType} 目标类型的转换? * @param sourceType 正在转换的字段的类型描述符 * @param targetType 正在转换到的字段的类型描述符 * @return 如果应该执行转换则为 true,否则为 false */ boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType); } ``` ### 六、主要实现 1. **AnnotationParserConverter** - 解析注解并转换为相应的对象。 2. **AnnotationPrinterConverter** - 将对象转换为表示其注解的字符串形式。 3. **ArrayToArrayConverter** - 将一个数组转换为另一种类型的数组。 4. **ArrayToCollectionConverter** - 将数组转换为集合。 5. **ArrayToDelimitedStringConverter** - 将数组转换为由特定分隔符分隔的字符串。 6. **ArrayToObjectConverter** - 将数组转换为单个对象。 7. **ArrayToStringConverter** - 将数组转换为字符串表示形式。 8. **ByteBufferConverter** - 转换字节缓冲区为其他类型。 9. **CharSequenceToObjectConverter** - 将字符序列转换为对象。 10. **CollectionToArrayConverter** - 将集合转换为数组。 11. **CollectionToCollectionConverter** - 将一种类型的集合转换为另一种类型的集合。 12. **CollectionToDelimitedStringConverter** - 将集合转换为由特定分隔符分隔的字符串。 13. **CollectionToObjectConverter** - 将集合转换为单个对象。 14. **CollectionToStringConverter** - 将集合转换为字符串表示形式。 15. **DelimitedStringToArrayConverter** - 将由特定分隔符分隔的字符串转换为数组。 16. **DelimitedStringToCollectionConverter** - 将由特定分隔符分隔的字符串转换为集合。 17. **FallbackObjectToStringConverter** - 作为后备方案,将对象转换为字符串。 18. **IdToEntityConverter** - 将标识符转换为实体对象。 19. **MapToMapConverter** - 将一种类型的映射(Map)转换为另一种类型的映射。 20. **ObjectToArrayConverter** - 将对象转换为数组。 21. **ObjectToCollectionConverter** - 将对象转换为集合。 22. **ObjectToObjectConverter** - 将一种类型的对象转换为另一种类型的对象。 23. **ObjectToOptionalConverter** - 将对象转换为 `Optional` 类型。 24. **StreamConverter** - 处理流(Stream)相关的转换。 25. **StringToArrayConverter** - 将字符串转换为数组。 26. **StringToCollectionConverter** - 将字符串转换为集合。 27. **TypeConverterConverter** - 处理类型转换器之间的转换。 ### 七、最佳实践 使用 `StringToIntegerConditionalConverter` 转换器。首先,它创建了转换器的实例,并为源类型(`String`)和目标类型(`Integer`)定义了类型描述符。然后,它使用 `matches` 方法检查是否满足从 `String` 到 `Integer` 的转换条件。如果条件匹配,则使用 `convert` 方法执行转换并打印结果;如果条件不匹配,则输出一条消息表明转换不适用。 ```java public class ConditionalConverterDemo { public static void main(String[] args) { // 创建自定义的转换器实例 StringToIntegerConditionalConverter converter = new StringToIntegerConditionalConverter(); // 定义源类型(String)和目标类型(Integer)的描述符 TypeDescriptor sourceType = TypeDescriptor.valueOf(String.class); TypeDescriptor targetType = TypeDescriptor.valueOf(Integer.class); // 测试转换器是否适用于从 String 到 Integer 的转换 if (converter.matches(sourceType, targetType)) { // 如果转换条件匹配,则执行转换 Integer result = converter.convert("8"); System.out.println("Converted result: " + result); } else { // 如果条件不匹配,打印不适用的消息 System.out.println("Conversion not applicable."); } } } ``` `StringToIntegerConditionalConverter` 是一个根据源类型和目标类型的类型信息来决定是否执行转换的条件转换器。它只在源类型为 `String` 且目标类型为 `Integer` 时执行转换操作,并且提供了字符串到整数的具体转换逻辑。 ```java public class StringToIntegerConditionalConverter implements Converter, ConditionalConverter { @Override public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) { // 判断条件:当源类型是 String 且目标类型是 Integer 时返回 true return String.class.equals(sourceType.getType()) && Integer.class.equals(targetType.getType()); } @Override public Integer convert(String source) { if (source == null || source.isEmpty()) { return null; } try { return Integer.parseInt(source); } catch (NumberFormatException e) { throw new IllegalArgumentException("Unable to convert String to Integer: " + source, e); } } } ``` 运行结果发现,字符串 `"8"` 被正确转换为了整数 `8`。这个输出证明了您的自定义转换器按预期工作,并能够在满足特定条件时将字符串转换为整数。 ```java Converted result: 8 ``` ### 八、与其他组件的关系 1. **Converter** - `Converter` 是一个简单的接口,用于将一种类型 `S` 转换为另一种类型 `T`。`ConditionalConverter` 可以与 `Converter` 接口结合使用,允许基于特定条件执行转换。 2. **GenericConverter** - `GenericConverter` 是一个更复杂的接口,允许更灵活的类型转换,包括集合和泛型类型。`ConditionalConverter` 可以与 `GenericConverter` 结合使用,为复杂的转换提供条件控制。 3. **ConverterFactory** - `ConverterFactory` 用于创建特定类型之间的转换器。当与 `ConditionalConverter` 结合时,可以在创建转换器时加入条件判断的逻辑。 4. **ConditionalGenericConverter** - `ConditionalGenericConverter` 是 `GenericConverter` 和 `ConditionalConverter` 的结合体,用于复杂的转换场景,同时需要基于条件判断是否执行转换。 5. **ConversionService** - `ConversionService` 是 Spring 提供的一个中心接口,用于在整个框架中执行类型转换操作。实现 `ConditionalConverter` 的转换器可以被注册到 `ConversionService` 中,使得转换操作可以在满足特定条件时自动触发。 ### 九、常见问题 1. **判断条件不清晰或过于复杂** - 在实现 `matches` 方法时,需要明确和简洁地定义转换应用的条件。如果条件过于复杂,可能会导致理解困难和维护问题。确保条件逻辑既清晰又准确。 2. **性能问题** - 条件判断逻辑如果不够高效,可能会对应用程序的性能产生负面影响。特别是在大量数据需要转换时,应该优化条件判断逻辑,避免不必要的复杂计算。 3. **不正确的类型匹配** - 确保 `matches` 方法中的类型匹配逻辑正确无误。错误的类型匹配可能会导致转换器在不适当的情况下被调用,进而引发数据错误或异常。 ================================================ FILE: spring-dataops/spring-dataops-conditionalConverter/pom.xml ================================================ spring-dataops com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-dataops-conditionalConverter ================================================ FILE: spring-dataops/spring-dataops-conditionalConverter/src/main/java/com/xcs/spring/ConditionalConverterDemo.java ================================================ package com.xcs.spring; import com.xcs.spring.converter.StringToIntegerConditionalConverter; import org.springframework.core.convert.TypeDescriptor; /** * @author xcs * @date 2023年12月08日 11时02分 **/ public class ConditionalConverterDemo { public static void main(String[] args) { // 创建自定义的转换器实例 StringToIntegerConditionalConverter converter = new StringToIntegerConditionalConverter(); // 定义源类型(String)和目标类型(Integer)的描述符 TypeDescriptor sourceType = TypeDescriptor.valueOf(String.class); TypeDescriptor targetType = TypeDescriptor.valueOf(Integer.class); // 测试转换器是否适用于从 String 到 Integer 的转换 if (converter.matches(sourceType, targetType)) { // 如果转换条件匹配,则执行转换 Integer result = converter.convert("8"); System.out.println("Converted result: " + result); } else { // 如果条件不匹配,打印不适用的消息 System.out.println("Conversion not applicable."); } } } ================================================ FILE: spring-dataops/spring-dataops-conditionalConverter/src/main/java/com/xcs/spring/converter/StringToIntegerConditionalConverter.java ================================================ package com.xcs.spring.converter; import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.converter.ConditionalConverter; import org.springframework.core.convert.converter.Converter; /** * @author xcs * @date 2023年12月08日 11时05分 **/ public class StringToIntegerConditionalConverter implements Converter, ConditionalConverter { @Override public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) { // 判断条件:当源类型是 String 且目标类型是 Integer 时返回 true return String.class.equals(sourceType.getType()) && Integer.class.equals(targetType.getType()); } @Override public Integer convert(String source) { if (source == null || source.isEmpty()) { return null; } try { return Integer.parseInt(source); } catch (NumberFormatException e) { throw new IllegalArgumentException("Unable to convert String to Integer: " + source, e); } } } ================================================ FILE: spring-dataops/spring-dataops-conversionService/README.md ================================================ ## ConversionService - [ConversionService](#conversionservice) - [一、基本信息](#一基本信息) - [二、知识储备](#二知识储备) - [三、基本描述](#三基本描述) - [四、主要功能](#四主要功能) - [五、接口源码](#五接口源码) - [六、主要实现](#六主要实现) - [七、最佳实践](#七最佳实践) - [八、与其他组件的关系](#八与其他组件的关系) - [九、常见问题](#九常见问题) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、知识储备 1. **Converter** + [Converter](/spring-dataops/spring-dataops-converter/README.md) 接口是 Spring 框架中用于实现类型转换的一个关键组件。定义了一个简单的方法,用于将一种类型(源类型 S)转换为另一种类型(目标类型 T)。通过实现这个接口,我们可以创建自定义的转换逻辑,以便在 Spring 应用程序中无缝地进行复杂的数据转换。 2. **ConverterFactory** + [ConverterFactory](/spring-dataops/spring-dataops-converterFactory/README.md) 是 Spring 框架中的一个接口,用于实现类型转换的工厂。在 Spring 的类型转换体系中,`ConverterFactory` 接口扮演着创建特定类型转换器(`Converter`)的角色。这个接口主要用于那些有共同特性的一组类型转换场景。 3. **GenericConverter** + [GenericConverter](/spring-dataops/spring-dataops-genericConverter/README.md) 是 Spring 框架中的一个关键接口,专门用于类型转换。这个接口与 Spring 的类型转换系统紧密相关,其主要功能是将一个类型的对象转换为另一个类型。与 `Converter` 接口相比,`GenericConverter` 提供了更灵活的转换机制,允许转换操作在多个源目标类型和目标类型之间进行。 4. **ConditionalConverter** + [ConditionalConverter](/spring-dataops/spring-dataops-conditionalConverter/README.md) 是 Spring 框架中用于类型转换的一个接口,它是 Spring 类型转换系统的一部分。这个接口的主要目的是为转换器(Converter)提供一个条件检查的功能,使得在执行实际的类型转换之前,转换器能够根据特定的条件判断是否应该进行转换。 ### 三、基本描述 `ConversionService` 接口是 Spring 框架中的一个关键部分,专门用于在不同数据类型之间进行转换。这个接口提供了一个中心化的方式来将一个类型的对象转换为另一个类型,极大地简化了数据处理和操作的复杂性。它也支持自定义转换规则,并与 Spring 的数据绑定和验证机制紧密集成,特别适用于处理 Web 请求参数、配置数据以及数据库记录。 ### 四、主要功能 1. **类型转换** + 最基本的功能是将一种数据类型转换为另一种。这涵盖了从简单类型(如字符串到整数)到更复杂对象的转换。 2. **支持自定义转换器** + 允许我们注册自定义的转换器( `Converter`, `ConverterFactory`, 或 `GenericConverter`),以处理特定类型的转换逻辑。 3. **通用性** + 这个接口不局限于任何特定的数据类型,因此可以广泛应用于不同的场景,如用户界面数据绑定、数据持久化等。 4. **集成Spring MVC** + 在 Spring MVC中,`ConversionService` 被用来处理数据绑定和类型转换,特别是在处理表单提交或路径参数时。 6. **查询转换能力** + 可以查询 `ConversionService` 以确定是否可以在两种特定类型之间进行转换。 ### 五、接口源码 `ConversionService` 接口定义了一套灵活的方法来支持在不同类型之间的转换。它提供了两种核心功能:一是检查是否可以在特定的源类型和目标类型之间进行转换;二是执行实际的转换操作。这个接口特别强调了对于集合、数组和映射类型转换的特殊处理,并明确指出在这些情况下可能会遇到的异常。 ```java /** * 用于类型转换的服务接口。这是转换系统的入口点。 * 调用 {@link #convert(Object, Class)} 使用这个系统执行线程安全的类型转换。 * * @author Keith Donald * @author Phillip Webb * @since 3.0 */ public interface ConversionService { /** * 如果 {@code sourceType} 类型的对象可以转换为 {@code targetType},则返回 {@code true}。 *

如果此方法返回 {@code true},意味着 {@link #convert(Object, Class)} 能够将 {@code sourceType} 的实例转换为 {@code targetType}。 *

关于集合、数组和映射类型的特别说明: * 对于集合、数组和映射类型之间的转换,即使转换调用可能仍会产生 {@link ConversionException}(如果底层元素不可转换),此方法也会返回 {@code true}。 * 调用者在处理集合和映射时,应该处理这种异常情况。 * @param sourceType 要从中转换的源类型(如果源是 {@code null} 则可能为 {@code null}) * @param targetType 要转换到的目标类型(必需) * @return 如果可以执行转换则返回 {@code true},否则返回 {@code false} * @throws IllegalArgumentException 如果 {@code targetType} 为 {@code null} */ boolean canConvert(@Nullable Class sourceType, Class targetType); /** * 如果 {@code sourceType} 类型的对象可以转换为 {@code targetType},则返回 {@code true}。 * TypeDescriptors 提供有关转换将发生的源和目标位置的附加上下文,通常是对象字段或属性位置。 *

如果此方法返回 {@code true},意味着 {@link #convert(Object, TypeDescriptor, TypeDescriptor)} * 能够将 {@code sourceType} 的实例转换为 {@code targetType}。 *

关于集合、数组和映射类型的特别说明:同上。 * @param sourceType 关于要从中转换的源类型的上下文(如果源是 {@code null} 则可能为 {@code null}) * @param targetType 关于要转换到的目标类型的上下文(必需) * @return 如果可以在源和目标类型之间执行转换,则返回 {@code true},否则返回 {@code false} * @throws IllegalArgumentException 如果 {@code targetType} 为 {@code null} */ boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType); /** * 将给定的 {@code source} 转换为指定的 {@code targetType}。 * @param source 要转换的源对象(可能为 {@code null}) * @param targetType 要转换到的目标类型(必需) * @return 转换后的对象,是 targetType 的实例 * @throws ConversionException 如果转换过程中发生异常 * @throws IllegalArgumentException 如果 targetType 为 {@code null} */ @Nullable T convert(@Nullable Object source, Class targetType); /** * 将给定的 {@code source} 转换为指定的 {@code targetType}。 * TypeDescriptors 提供有关转换将发生的源和目标位置的附加上下文。 * @param source 要转换的源对象(可能为 {@code null}) * @param sourceType 关于要从中转换的源类型的上下文(如果源是 {@code null} 则可能为 {@code null}) * @param targetType 关于要转换到的目标类型的上下文(必需) * @return 转换后的对象,是 {@link TypeDescriptor#getObjectType() targetType} 的实例 * @throws ConversionException 如果转换过程中发生异常 * @throws IllegalArgumentException 如果 targetType 为 {@code null}, * 或者 {@code sourceType} 为 {@code null} 但源不是 {@code null} */ @Nullable Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType); } ``` ### 六、主要实现 1. **GenericConversionService** + 作为 `ConversionService` 的基本实现,`GenericConversionService` 提供了一个通用的转换框架,支持包括 `Converter`、`ConverterFactory` 和 `GenericConverter` 在内的各种转换器类型。它是构建更复杂转换服务的基础,因其通用性,适用于多种转换场景。 2. **DefaultConversionService** + 扩展自 `GenericConversionService`,`DefaultConversionService` 增加了许多常见数据类型的默认转换器,为大多数标准应用程序提供了即插即用的转换功能。这使它成为处理常规数据类型转换的理想选择。 3. **FormattingConversionService** + `FormattingConversionService` 不仅支持类型转换,还提供了对象到字符串的格式化以及从字符串到对象的解析功能,特别适合于处理用户界面相关的数据转换。它常用于 Web 应用程序,尤其在 Spring MVC 中处理表单数据和视图层数据展示。 4. **DefaultFormattingConversionService** + 这个服务是 `FormattingConversionService` 的具体实现,它集成了 `DefaultConversionService` 的所有功能,并添加了常用的格式化器和解析器。它提供了一套完整的解决方案,用于 Web 应用程序中的数据格式化和解析。 5. **ApplicationConversionService** + 为 Spring 应用程序设计的全面 `ConversionService` 实现,`ApplicationConversionService` 继承自 `DefaultFormattingConversionService`,加入了额外的 Spring 特定转换器。它是 Spring Boot 自动配置中的默认选择,适用于大多数 Spring 应用程序。 6. **TypeConverterConversionService** + 这是一个适配器类,将 `TypeConverter` 接口和 `ConversionService` 接口桥接在一起。它允许旧的 `PropertyEditor` 类型转换器在新的 `ConversionService` 结构中使用,适用于将旧代码迁移到使用新的转换服务框架的场景。 7. **WebConversionService** + 专门为 Web 环境设计的 `ConversionService` 实现,`WebConversionService` 扩展了 `FormattingConversionService`,提供了特定于 Web 的转换器和格式化器。这个服务特别适用于处理 HTTP 请求和响应中的数据转换,是 Web 应用程序的理想选择。 ### 七、最佳实践 使用 `DefaultConversionService` 来执行类型转换。首先,它通过 `canConvert` 方法检查是否可以从 `Integer` 类型转换为 `String` 类型。如果可以,它会执行转换并输出结果。接着,示例通过使用 `TypeDescriptor` 进行更细粒度的转换能力检查,这是一种更复杂的转换检查方式。使用 `TypeDescriptor` 可以处理更复杂的转换场景,例如,当源和目标类型需要更具体的上下文信息时。 ```java public class ConversionServiceDemo { public static void main(String[] args) { // 创建 DefaultConversionService 实例 ConversionService conversionService = new DefaultConversionService(); // 使用 canConvert 检查转换是否可能 if (conversionService.canConvert(Integer.class, String.class)) { System.out.println("Can convert from Integer to String"); // 执行转换操作,将 Integer 转换为 String String converted = conversionService.convert(666, String.class); System.out.println("Converted: " + converted); } // 使用 TypeDescriptor 检查转换是否可能 if (conversionService.canConvert( TypeDescriptor.valueOf(Integer.class), TypeDescriptor.valueOf(String.class))) { System.out.println("Can convert from Integer to String using TypeDescriptors"); // 使用 TypeDescriptor 执行转换 Object convertedWithTypeDescriptor = conversionService.convert( 888, TypeDescriptor.valueOf(Integer.class), TypeDescriptor.valueOf(String.class)); System.out.println("Converted with TypeDescriptors: " + convertedWithTypeDescriptor); } } } ``` 运行结果发现,`ConversionService` 的高效性和实用性:它不仅能够确认是否可以执行特定的类型转换(如从 `Integer` 到 `String`),而且能够实际执行这些转换,将整数值成功转换为相应的字符串表示。 ```java Can convert from Integer to String Converted: 666 Can convert from Integer to String using TypeDescriptors Converted with TypeDescriptors: 888 ``` ### 八、与其他组件的关系 1. **Converter&ConverterFactory** + `ConversionService` 依赖于 `Converter` 和 `ConverterFactory` 接口的实现来执行具体的类型转换,允许我们通过实现这些接口并将自定义转换器注册到 `ConversionService` 中,来提供特定的转换逻辑,从而增强了类型转换的灵活性和扩展性。 2. **GenericConverter** + `GenericConverter` 强化了 `ConversionService` 在 Spring 中的转换功能,提供灵活的多类型转换能力。通过将 `GenericConverter` 实现注册到 `ConversionService`,它使 `ConversionService` 能够处理更广泛和复杂的转换场景,包括那些需要额外上下文的转换任务。 3. **PropertyEditor** + `PropertyEditor` 是早期的类型转换工具,主要用于简单的属性编辑场景,但由于其非线程安全性和有限的灵活性,被 `ConversionService` 所取代。`ConversionService` 提供了一个现代、线程安全且灵活的类型转换机制,是现代 Spring 应用中进行类型转换的首选方法。虽然 `PropertyEditor` 仍得到支持,但在复杂和并发的应用环境中,`ConversionService` 更为合适。 4. **DataBinder** + 在数据绑定过程中,尤其是将表单数据绑定到对象或处理 MVC 控制器方法参数时,`DataBinder` 依赖 `ConversionService` 来转换属性值,这使得从一种类型到另一种类型的数据转换变得简洁高效。 5. **配置和属性管理** + `ConversionService` 在处理 Spring 配置属性时发挥重要作用,特别是在处理使用 `@Value` 注解注入的属性时,它能够将属性文件中的字符串值转换成更复杂的对象类型,简化了配置管理流程。 6. **BeanFactory&ApplicationContext** + 作为 Spring 容器中的一个 bean,`ConversionService` 可以在 `BeanFactory` 或 `ApplicationContext` 中注册,一旦注册,它便可被容器中的其他组件用于执行各种类型的转换操作,增强了整个 Spring 应用程序的类型转换能力和灵活性。 ### 九、常见问题 1. **自定义转换器注册** + 在使用 `ConversionService` 时,正确注册自定义转换器是一个常见挑战。我们需要确保他们的 `Converter`、`ConverterFactory` 或 `GenericConverter` 实现被适当地添加到 `ConversionService` 中,否则预期的转换可能不会发生,这可能是由于配置错误或遗漏。 2. **转换器冲突** + 当存在多个适用于同一转换任务的转换器时,可能会引发冲突,我们需要理解和管理这些转换器的优先级,以确保正确的转换器被应用于特定情况。 3. **复杂类型转换** + 在处理复杂或泛型类型的转换时,我们需要实现能够处理这些复杂情况的逻辑,尤其是当转换逻辑需要额外的上下文信息时,例如在集合类型之间进行转换并同时处理元素类型的转换。 4. **与其他 Spring 组件的集成** + `ConversionService` 与 Spring MVC、数据绑定和验证等组件的正确集成可能在复杂场景中变得复杂,需要深入理解 Spring 配置和组件交互。 5. **转换精度和数据丢失** + 在进行数值类型转换等操作时,应注意转换精度和潜在的数据丢失问题,确保数据的准确性和完整性。 ================================================ FILE: spring-dataops/spring-dataops-conversionService/pom.xml ================================================ spring-dataops com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-dataops-conversionService ================================================ FILE: spring-dataops/spring-dataops-conversionService/src/main/java/com/xcs/spring/ConversionServiceDemo.java ================================================ package com.xcs.spring; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.core.convert.support.GenericConversionService; /** * @author xcs * @date 2023年12月08日 16时30分 **/ public class ConversionServiceDemo { public static void main(String[] args) { // 创建 DefaultConversionService 实例 ConversionService conversionService = new DefaultConversionService(); // 使用 canConvert 检查转换是否可能 if (conversionService.canConvert(Integer.class, String.class)) { System.out.println("Can convert from Integer to String"); // 执行转换操作,将 Integer 转换为 String String converted = conversionService.convert(666, String.class); System.out.println("Converted: " + converted); } // 使用 TypeDescriptor 检查转换是否可能 if (conversionService.canConvert( TypeDescriptor.valueOf(Integer.class), TypeDescriptor.valueOf(String.class))) { System.out.println("Can convert from Integer to String using TypeDescriptors"); // 使用 TypeDescriptor 执行转换 Object convertedWithTypeDescriptor = conversionService.convert( 888, TypeDescriptor.valueOf(Integer.class), TypeDescriptor.valueOf(String.class)); System.out.println("Converted with TypeDescriptors: " + convertedWithTypeDescriptor); } } } ================================================ FILE: spring-dataops/spring-dataops-converter/README.md ================================================ ## Converter - [Converter](#converter) - [一、基本信息](#一基本信息) - [二、知识储备](#二知识储备) - [三、基本描述](#三基本描述) - [四、主要功能](#四主要功能) - [五、接口源码](#五接口源码) - [六、主要实现](#六主要实现) - [七、最佳实践](#七最佳实践) - [八、与其他组件的关系](#八与其他组件的关系) - [九、常见问题](#九常见问题) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、知识储备 1. **泛型(Generics)** + 由于 `Converter` 接口使用了 Java 泛型,因此对 Java 泛型有一定了解是必要的。理解如何使用泛型来创建类型安全的代码对实现自定义转换器非常重要。 2. **Spring MVC** + 如果你打算在 Web 应用中使用 `Converter` 接口,了解 Spring MVC 的工作原理是有帮助的。这包括理解控制器(Controllers)、请求映射(Request Mappings)、视图解析(View Resolvers)等。 3. **类型转换与格式化** + 理解 Java 中的类型转换基础,以及如何格式化数据。这对编写有效的转换器逻辑至关重要。 ### 三、基本描述 `Converter` 接口是 Spring 框架中用于实现类型转换的一个关键组件。定义了一个简单的方法,用于将一种类型(源类型 S)转换为另一种类型(目标类型 T)。通过实现这个接口,我们可以创建自定义的转换逻辑,以便在 Spring 应用程序中无缝地进行复杂的数据转换。 ### 四、主要功能 1. **类型转换** + 主要功能是提供一种机制来将一个对象从一种类型转换为另一种类型。这对于处理不兼容的数据类型非常有用,特别是在需要将外部系统的数据格式转换为内部使用的格式时。 2. **数据格式化** + 在 Web 应用程序中,`Converter` 可用于将来自用户界面的数据(通常是字符串格式)转换为后端系统可识别的格式,或反之。这对于日期、时间、数字等复杂数据类型尤其重要。 3. **数据绑定** + 在 Spring MVC 中,`Converter` 接口用于将请求参数(例如,来自 HTTP 请求的字符串)转换为控制器方法参数的适当类型。 4. **提升代码可维护性和重用性** + 通过定义清晰的转换规则,`Converter` 实现促进了代码的模块化,使得相同的转换逻辑可以在应用程序的不同部分重用。 5. **自定义转换逻辑** + 与内置的转换器相比,自定义 `Converter` 实现允许我们定义特定于应用程序的转换逻辑,从而处理更复杂或特定的转换需求。 6. **集成与扩展** + `Converter` 接口可以轻松集成到 Spring 框架的其他部分,如数据访问层、Spring Boot 自动配置等,同时也可以被扩展以支持更复杂的转换需求。 ### 五、接口源码 `Converter` 接口是 Spring 框架中用于实现从一种类型到另一种类型转换的标准机制。定义了一个单一的 `convert` 方法,用于执行实际的转换逻辑。这个接口是线程安全的,可在应用中共享和重复使用。除了基本的转换功能,该接口还提供了 `andThen` 方法,允许将多个转换器组合在一起,以实现更复杂的转换逻辑。 ```java /** * 一个转换器,负责将类型为 {@code S} 的源对象转换为类型为 {@code T} 的目标对象。 * *

该接口的实现应是线程安全的,并且可以共享。 * *

实现还可以选择实现 {@link ConditionalConverter}。 * * @param 源类型 * @param 目标类型 */ @FunctionalInterface public interface Converter { /** * 将类型为 {@code S} 的源对象转换为类型为 {@code T} 的目标对象。 * @param source 要转换的源对象,必须是类型 {@code S} 的实例(不能是 {@code null}) * @return 转换后的对象,必须是类型 {@code T} 的实例(可能是 {@code null}) * @throws IllegalArgumentException 如果源对象无法转换为所需的目标类型 */ @Nullable T convert(S source); /** * 构建一个组合的 {@link Converter},该转换器首先对其输入应用此 {@link Converter}, * 然后将 {@code after} {@link Converter} 应用于结果。 * @param after 在此 {@link Converter} 应用后要应用的 {@link Converter} * @param both the {@code after} {@link Converter} 和组合的 {@link Converter} 的输出类型 * @return 一个组合的 {@link Converter},首先应用此 {@link Converter},然后应用 {@code after} {@link Converter} * @since 5.3 */ default Converter andThen(Converter after) { Assert.notNull(after, "After Converter 不能为空"); return (S s) -> { T initialResult = convert(s); return (initialResult != null ? after.convert(initialResult) : null); }; } } ``` ### 六、主要实现 1. **StringToArrayConverter** - 将字符串转换为数组,例如将逗号分隔的值转换为字符串数组。 2. **StringToBooleanConverter** - 将字符串转换为布尔值。 3. **StringToCharacterConverter** - 将字符串转换为字符。 4. **StringToCharsetConverter** - 将字符串转换为 `Charset` 对象。 5. **StringToCollectionConverter** - 将字符串转换为集合,例如将逗号分隔的字符串转换为列表。 6. **StringToCurrencyConverter** - 将字符串转换为 `Currency` 对象。 7. **StringToLocaleConverter** - 将字符串转换为 `Locale` 对象。 ### 七、最佳实践 使用 `DefaultConversionService` 来管理和执行类型转换。首先,我们创建了一个转换服务的实例,并向其中注册了自定义的 `StringToLocalDateConverter` 和 `StringToBooleanConverter` 转换器。接着,代码通过 `canConvert` 方法检查是否可以执行特定的转换,并使用 `convert` 方法进行实际的转换。 ```java public class ConverterDemo { public static void main(String[] args) { // 创建一个默认的转换服务 DefaultConversionService service = new DefaultConversionService(); // 向服务中添加自定义的转换器 service.addConverter(new StringToLocalDateConverter()); service.addConverter(new StringToBooleanConverter()); // 检查是否可以将字符串转换为 LocalDate if (service.canConvert(String.class, LocalDate.class)) { LocalDate localDate = service.convert("2023-12-07", LocalDate.class); System.out.println("LocalDate = " + localDate); } // 检查是否可以将字符串 "yes" 转换为 Boolean if (service.canConvert(String.class, Boolean.class)) { Boolean boolValue = service.convert("yes", Boolean.class); System.out.println("yes = " + boolValue); } // 检查是否可以将字符串 "no" 转换为 Boolean if (service.canConvert(String.class, Boolean.class)) { Boolean boolValue = service.convert("no", Boolean.class); System.out.println("no = " + boolValue); } } } ``` 定义了一个 `StringToLocalDateConverter` 类,用于实现将格式为 "`yyyy-MM-dd`" 的字符串转换为 `LocalDate` 对象。我们通过实现 Spring 的 `Converter` 接口,使用 `DateTimeFormatter` 对字符串进行解析,从而实现类型重用的日期转换功能。 ```java public class StringToLocalDateConverter implements Converter { @Override public LocalDate convert(String source) { DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd"); return LocalDate.parse(source, df); } } ``` 定义了 `StringToBooleanConverter` 类,实现了将字符串转换为布尔值的功能。我们通过预定义的字符串集合来识别表示真值(如 "true", "yes", "1")和假值(如 "false", "no", "0")的字符串,并相应地返回 `Boolean.TRUE` 或 `Boolean.FALSE`。 ```java public class StringToBooleanConverter implements Converter { private static final Set trueValues = new HashSet<>(8); private static final Set falseValues = new HashSet<>(8); static { trueValues.add("true"); trueValues.add("on"); trueValues.add("yes"); trueValues.add("1"); falseValues.add("false"); falseValues.add("off"); falseValues.add("no"); falseValues.add("0"); } @Override @Nullable public Boolean convert(String source) { if (trueValues.contains(source)) { return Boolean.TRUE; } if (falseValues.contains(source)) { return Boolean.FALSE; } throw new IllegalArgumentException("Invalid boolean value '" + source + "'"); } } ``` 运行结果发现,字符串 "2023-12-07" 被转换为了对应的 `LocalDate` 对象,而字符串 "yes" 和 "no" 分别被准确地转换为了布尔值 `true` 和 `false`。这证明了自定义转换器在处理日期和布尔值转换时的有效性。 ```java LocalDate = 2023-12-07 yes = true no = false ``` ### 八、与其他组件的关系 1. **ConversionService** - `ConversionService` 是一个中心接口,用于执行类型转换。`Converter` 接口的实现通常被注册到 `ConversionService` 中,以便在整个应用程序中使用。 2. **Data Binding** - 在 Spring MVC 中,数据绑定过程利用 `Converter` 接口将 HTTP 请求参数转换为控制器方法参数的适当类型。这对于表单提交和查询参数处理尤其重要。 3. **Spring Boot and Spring Configuration** - 在 Spring Boot 和 Spring 应用程序配置中,`Converter` 接口用于将配置属性(如属性文件中的值)转换为更复杂的对象或自定义类型。 4. **Spring Data** - 在 Spring Data 的上下文中,`Converter` 接口可以用于在数据库实体和应用程序中使用的领域模型之间进行转换。 5. **Field Formatting** - `Converter` 通常与 `Formatter` 接口一起使用,后者专注于字段的格式化和解析,特别是在 Web 环境中。`Formatter` 可以视为一种特殊类型的 `Converter`。 6. **Bean Validation** - 在处理 Bean 验证时,`Converter` 接口可以用于在验证逻辑之前或之后转换数据。 7. **RestTemplate and WebClient** - 在使用 Spring 的 RestTemplate 或 WebClient 时,`Converter` 接口可以用于序列化和反序列化 HTTP 请求和响应体。 ### 九、常见问题 1. **转换逻辑错误** - 错误地实现转换逻辑可能导致数据转换不正确或转换过程中发生异常。重要的是要确保转换逻辑正确,并且能够处理各种输入情况。 2. **注册转换器失败** - 有时我们可能忘记将自定义转换器注册到 Spring 的 `ConversionService` 中,导致预期的转换未能发生。 3. **处理空值** - 我们需要决定如何处理 `null` 输入。某些情况下可能需要将 `null` 转换为默认值,而在其他情况下则应保持为 `null` 或抛出异常。 4. **类型检查和错误处理** - 正确处理输入类型和转换过程中可能出现的错误是至关重要的。例如,转换方法应检查输入是否为预期类型,并在不合适的情况下抛出适当的异常。 5. **与其他转换器的冲突** - 在大型项目中,可能会有多个转换器能够处理相同的源和目标类型。这可能导致不明确的转换逻辑,需要小心管理以避免冲突。 ================================================ FILE: spring-dataops/spring-dataops-converter/pom.xml ================================================ spring-dataops com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-dataops-converter 11 11 ================================================ FILE: spring-dataops/spring-dataops-converter/src/main/java/com/xcs/spring/ConverterDemo.java ================================================ package com.xcs.spring; import com.xcs.spring.converter.StringToBooleanConverter; import com.xcs.spring.converter.StringToLocalDateConverter; import org.springframework.core.convert.support.DefaultConversionService; import java.time.LocalDate; /** * @author xcs * @date 2023年12月07日 16时53分 **/ public class ConverterDemo { public static void main(String[] args) { // 创建一个默认的转换服务 DefaultConversionService service = new DefaultConversionService(); // 向服务中添加自定义的转换器 service.addConverter(new StringToLocalDateConverter()); service.addConverter(new StringToBooleanConverter()); // 检查是否可以将字符串转换为 LocalDate if (service.canConvert(String.class, LocalDate.class)) { LocalDate localDate = service.convert("2023-12-07", LocalDate.class); System.out.println("LocalDate = " + localDate); } // 检查是否可以将字符串 "yes" 转换为 Boolean if (service.canConvert(String.class, Boolean.class)) { Boolean boolValue = service.convert("yes", Boolean.class); System.out.println("yes = " + boolValue); } // 检查是否可以将字符串 "no" 转换为 Boolean if (service.canConvert(String.class, Boolean.class)) { Boolean boolValue = service.convert("no", Boolean.class); System.out.println("no = " + boolValue); } } } ================================================ FILE: spring-dataops/spring-dataops-converter/src/main/java/com/xcs/spring/converter/StringToBooleanConverter.java ================================================ package com.xcs.spring.converter; import org.springframework.core.convert.converter.Converter; import org.springframework.lang.Nullable; import java.util.HashSet; import java.util.Set; /** * @author xcs * @date 2023年12月07日 17时09分 **/ public class StringToBooleanConverter implements Converter { private static final Set trueValues = new HashSet<>(8); private static final Set falseValues = new HashSet<>(8); static { trueValues.add("true"); trueValues.add("on"); trueValues.add("yes"); trueValues.add("1"); falseValues.add("false"); falseValues.add("off"); falseValues.add("no"); falseValues.add("0"); } @Override @Nullable public Boolean convert(String source) { if (trueValues.contains(source)) { return Boolean.TRUE; } if (falseValues.contains(source)) { return Boolean.FALSE; } throw new IllegalArgumentException("Invalid boolean value '" + source + "'"); } } ================================================ FILE: spring-dataops/spring-dataops-converter/src/main/java/com/xcs/spring/converter/StringToLocalDateConverter.java ================================================ package com.xcs.spring.converter; import org.springframework.core.convert.converter.Converter; import java.time.LocalDate; import java.time.format.DateTimeFormatter; /** * @author xcs * @date 2023年12月07日 17时00分 **/ public class StringToLocalDateConverter implements Converter { @Override public LocalDate convert(String source) { DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd"); return LocalDate.parse(source, df); } } ================================================ FILE: spring-dataops/spring-dataops-converterFactory/README.md ================================================ ## ConverterFactory - [ConverterFactory](#converterfactory) - [一、基本信息](#一基本信息) - [二、知识储备](#二知识储备) - [三、基本描述](#三基本描述) - [四、主要功能](#四主要功能) - [五、接口源码](#五接口源码) - [六、主要实现](#六主要实现) - [七、最佳实践](#七最佳实践) - [八、与其他组件的关系](#八与其他组件的关系) - [九、常见问题](#九常见问题) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、知识储备 1. **Converter** + [Converter](/spring-dataops/spring-dataops-converter/README.md) 接口是 Spring 框架中用于实现类型转换的一个关键组件。定义了一个简单的方法,用于将一种类型(源类型 S)转换为另一种类型(目标类型 T)。通过实现这个接口,我们可以创建自定义的转换逻辑,以便在 Spring 应用程序中无缝地进行复杂的数据转换。 ### 三、基本描述 `ConverterFactory` 是 Spring 框架中的一个接口,用于实现类型转换的工厂。在 Spring 的类型转换体系中,`ConverterFactory` 接口扮演着创建特定类型转换器(`Converter`)的角色。这个接口主要用于那些有共同特性的一组类型转换场景。 ### 四、主要功能 1. **动态创建转换器** + `ConverterFactory` 能够根据不同的目标类型动态地创建相应的 `Converter` 实例。这意味着我们可以为一系列相关的转换任务只定义一个 `ConverterFactory`,而不是为每种转换任务单独定义一个 `Converter`。 2. **支持广泛的类型转换** + 可以用于处理各种复杂的类型转换需求,特别是当源类型和目标类型之间的转换规则类似,但目标类型有多种可能时。例如,将字符串转换为不同类型的枚举值或数字类型。 3. **提高代码的重用性和维护性** + 通过集中处理一组相似的转换逻辑,`ConverterFactory` 帮助减少代码的重复,并使类型转换的维护更加方便。 4. **灵活性和扩展性** + 提供了一种灵活的方式来扩展 Spring 的类型转换功能。开发者可以根据实际需求自定义 `ConverterFactory`,以适应特定应用程序的类型转换需求。 5. **与 Spring ConversionService 的集成** + 在 Spring 的类型转换体系中,`ConverterFactory` 可以轻松地与 `ConversionService` 集成,从而在整个应用程序中提供一致的类型转换服务。 6. **简化配置** + 在使用 Spring 配置时,`ConverterFactory` 使得针对一组相关类型的转换配置更加简洁,因为我们只需要注册一个工厂,而不是注册多个独立的转换器。 ### 五、接口源码 `ConverterFactory` 是 Spring 框架中的一个接口,用于创建能够将对象从一种源类型(S)转换到其目标类型(R)的子类型的转换器。这个接口主要适用于需要处理多种相关但具体不同的目标子类型的转换场景,提供了一种灵活和高效的方式来生成和管理这些类型转换器。通过其 `getConverter` 方法,可以根据特定的目标类型动态获取相应的转换器,从而简化了类型转换的实现和配置过程。 ```java /** * 用于创建能够将对象从 S 类型转换为 R 的子类型的“范围”转换器的工厂。 * *

实现类可以额外实现 {@link ConditionalConverter}。 * * @author Keith Donald * @since 3.0 * @param 此工厂创建的转换器可以从中转换的源类型 * @param 此工厂创建的转换器可以转换到的目标范围(或基础)类型; * 例如对于一组数字子类型来说就是 {@link Number}。 * @see ConditionalConverter */ public interface ConverterFactory { /** * 获取转换器以从 S 转换到目标类型 T,其中 T 也是 R 的实例。 * @param 目标类型 * @param targetType 要转换到的目标类型 * @return 从 S 到 T 的转换器 */ Converter getConverter(Class targetType); } ``` ### 六、主要实现 1. **CharacterToNumberFactory** + 用于创建将字符(`Character`)转换为数字(`Number`)类型的转换器。它可以用于场景,比如将字符 '1' 转换为数字 1。 2. **IntegerToEnumConverterFactory** + 用于创建将整数(`Integer`)转换为枚举(`Enum`)值的转换器。这在处理基于整数索引的枚举类型时非常有用,例如,将整数 0、1、2 分别转换为枚举中的第一个、第二个、第三个值。 3. **LenientObjectToEnumConverterFactory** + 用于提供一种从任意对象到枚举类型的宽松转换。它不仅处理直接的类型映射,而且通常包含更复杂的逻辑,以允许从各种不同的源类型安全地转换为枚举值。 4. **NumberToNumberConverterFactory** + 用于创建从一种数字类型转换为另一种数字类型的转换器。例如,它可以创建一个转换器,将 `Double` 转换为 `Integer`,或者将 `Long` 转换为 `Float` 等。 5. **StringToEnumConverterFactory** + 用于创建从字符串(`String`)转换到枚举(`Enum`)类型的转换器。它在处理来自文本源(如配置文件或用户输入)的枚举值时非常实用,能够将字符串映射到相应的枚举常量。 6. **StringToNumberConverterFactory** + 用于创建将字符串转换为数字的转换器。这在很多情况下都是必需的,比如在解析数字类型的配置参数或者用户输入时。它可以处理从字符串到各种数字类型(如 `Integer`、`Double`、`BigDecimal` 等)的转换。 ### 七、最佳实践 我们使用 `DefaultConversionService` 实例,这是 Spring 框架提供的一个用于默认类型转换的服务。接着,我们向这个服务中添加了一个我们自定义的转换工厂类`StringToNumberConverterFactory`,用于创建从字符串到数字的转换器的工厂类,最后我们通过调用 `conversionService.convert("8", Integer.class)`方法,将字符串 "8" 转换为 `Integer` 类型的数字。 ```java public class CharacterToNumberFactoryDemo { public static void main(String[] args) { // 创建一个默认的转换服务 // 这里使用 GenericConversionService,它是一个通用的类型转换服务 DefaultConversionService conversionService = new DefaultConversionService(); // 向转换服务中添加一个字符串到数字的转换器工厂 // StringToNumberConverterFactory 是一个工厂类,用于创建字符串到数字的转换器 conversionService.addConverterFactory(new StringToNumberConverterFactory()); // 使用转换服务将字符串 "8" 转换为 Integer 类型 // 这里演示了如何将字符串转换为对应的整数 Integer num = conversionService.convert("8", Integer.class); // 输出转换结果 System.out.println("String to Integer: " + num); } } ``` `StringToNumberConverterFactory` 类是一个实现了 `ConverterFactory` 接口的具体类,专门用于将字符串转换为数字类型。这个类内部定义了一个静态内部类 `StringToNumber`,它实现了 `Converter` 接口,用于执行实际的转换逻辑。 ```java public class StringToNumberConverterFactory implements ConverterFactory { @Override public Converter getConverter(Class targetType) { return new StringToNumberConverterFactory.StringToNumber<>(targetType); } private static final class StringToNumber implements Converter { private final Class targetType; public StringToNumber(Class targetType) { this.targetType = targetType; } @Override @Nullable public T convert(String source) { return NumberUtils.parseNumber(source, this.targetType); } } } ``` 运行结果发现,`StringToNumberConverterFactory` 类的实现成功地将字符串 "8" 转换成了 `Integer` 类型的数字 8。 ```java String to Integer: 8 ``` ### 八、与其他组件的关系 1. **Converter 接口** - `ConverterFactory` 用于创建 `Converter` 实例。`Converter` 接口是一个更简单的类型转换接口,它将一个类型 `S` 直接转换为另一个类型 `T`。`ConverterFactory` 用于更复杂的场景,其中需要基于目标类型动态创建转换器。 2. **ConditionalConverter** - `ConverterFactory` 实现可以选择性地实现 `ConditionalConverter` 接口。这允许转换器在执行转换前进行一些条件判断,比如检查源类型和目标类型是否满足特定条件。 3. **ConversionService** - `ConversionService` 是 Spring 的类型转换服务的核心接口。它定义了一个用于类型转换的统一服务接口,可以注册 `Converter` 和 `ConverterFactory` 实现。`ConversionService` 在进行类型转换时会查询注册的转换器和转换器工厂以执行转换任务。 4. **GenericConversionService** - `GenericConversionService` 是 `ConversionService` 接口的一个具体实现,提供了类型转换的通用实现。它管理着一组 `Converter` 和 `ConverterFactory` 实例,并根据需要进行类型转换。 5. **DefaultConversionService** - `DefaultConversionService` 是 `GenericConversionService` 的扩展,预先注册了一系列常用的 `Converter` 和 `ConverterFactory` 实例。它为常见的转换场景提供了即用型的支持。 ### 九、常见问题 1. **转换器未被正确注册** + 确保在 Spring 配置中正确注册了 `ConverterFactory`,并且 `ConversionService` 在应用程序上下文中可用。 2. **转换逻辑错误或不足** + 对 `Converter` 实现进行彻底测试,确保它能够正确处理各种输入情况,包括异常值和边界情况。 3. **类型转换异常** + 增加适当的错误处理和类型检查逻辑,确保在执行转换之前源类型和目标类型是兼容的。 4. **转换性能问题** + 优化转换器的实现,避免不必要的计算和资源消耗,可以考虑缓存某些重复的转换结果。 5. **泛型类型擦除问题** + 使用具体的目标类型而非泛型类型来注册和查找转换器。 6. **自定义转换规则的维护和更新** + 保持转换器逻辑的清晰和模块化,使其易于维护和扩展。 ================================================ FILE: spring-dataops/spring-dataops-converterFactory/pom.xml ================================================ spring-dataops com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-dataops-converterFactory ================================================ FILE: spring-dataops/spring-dataops-converterFactory/src/main/java/com/xcs/spring/CharacterToNumberFactoryDemo.java ================================================ package com.xcs.spring; import com.xcs.spring.converter.StringToNumberConverterFactory; import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.core.convert.support.GenericConversionService; public class CharacterToNumberFactoryDemo { public static void main(String[] args) { // 创建一个默认的转换服务 // 这里使用 GenericConversionService,它是一个通用的类型转换服务 GenericConversionService conversionService = new DefaultConversionService(); // 向转换服务中添加一个字符串到数字的转换器工厂 // StringToNumberConverterFactory 是一个工厂类,用于创建字符串到数字的转换器 conversionService.addConverterFactory(new StringToNumberConverterFactory()); // 使用转换服务将字符串 "8" 转换为 Integer 类型 // 这里演示了如何将字符串转换为对应的整数 Integer num = conversionService.convert("8", Integer.class); // 输出转换结果 System.out.println("String to Integer: " + num); } } ================================================ FILE: spring-dataops/spring-dataops-converterFactory/src/main/java/com/xcs/spring/converter/StringToNumberConverterFactory.java ================================================ package com.xcs.spring.converter; import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.ConverterFactory; import org.springframework.lang.Nullable; import org.springframework.util.NumberUtils; public class StringToNumberConverterFactory implements ConverterFactory { @Override public Converter getConverter(Class targetType) { return new StringToNumberConverterFactory.StringToNumber<>(targetType); } private static final class StringToNumber implements Converter { private final Class targetType; public StringToNumber(Class targetType) { this.targetType = targetType; } @Override @Nullable public T convert(String source) { return NumberUtils.parseNumber(source, this.targetType); } } } ================================================ FILE: spring-dataops/spring-dataops-genericConverter/README.md ================================================ ## GenericConverter - [GenericConverter](#genericconverter) - [一、基本信息](#一基本信息) - [二、知识储备](#二知识储备) - [三、基本描述](#三基本描述) - [四、主要功能](#四主要功能) - [五、接口源码](#五接口源码) - [六、主要实现](#六主要实现) - [七、最佳实践](#七最佳实践) - [八、与其他组件的关系](#八与其他组件的关系) - [九、常见问题](#九常见问题) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、知识储备 1. **Converter** + [Converter](/spring-dataops/spring-dataops-converter/README.md) 接口是 Spring 框架中用于实现类型转换的一个关键组件。定义了一个简单的方法,用于将一种类型(源类型 S)转换为另一种类型(目标类型 T)。通过实现这个接口,我们可以创建自定义的转换逻辑,以便在 Spring 应用程序中无缝地进行复杂的数据转换。 ### 三、基本描述 `GenericConverter` 是 Spring 框架中的一个关键接口,专门用于类型转换。这个接口与 Spring 的类型转换系统紧密相关,其主要功能是将一个类型的对象转换为另一个类型。与 `Converter` 接口相比,`GenericConverter` 提供了更灵活的转换机制,允许转换操作在多个源目标类型和目标类型之间进行。 ### 四、主要功能 1. **多元类型转换** + `GenericConverter` 能够处理更复杂的转换场景,支持从一个类型到多个目标类型的转换,也能从多个源类型转换到一个目标类型。这比简单的一对一转换(如 `Converter` 接口所提供的)更为复杂和灵活。 2. **支持泛型和复杂类型转换** + 使用 `TypeDescriptor`类,`GenericConverter` 能够处理包括泛型在内的更复杂的类型转换。这对于集合类型和泛型类的转换尤为重要。 3. **自定义转换逻辑** + 我们可以通过实现 `GenericConverter` 接口来提供自定义的转换逻辑,以满足特定的业务需求。 4. **与Spring类型转换体系的集成** + `GenericConverter` 可以无缝集成到 Spring 的类型转换体系中,与 Spring 的其他组件(如数据绑定)协同工作。 5. **灵活的转换类型声明** + 通过 `getConvertibleTypes()` 方法声明它可以处理的源类型和目标类型对,这使得 Spring 能夠更灵活地选择合适的转换器。 ### 五、接口源码 `GenericConverter`接口允许在多个源和目标类型对之间进行转换,并在转换过程中利用 `TypeDescriptor` 来访问和处理字段的上下文信息,如注解和泛型。这种能力使得 `GenericConverter` 能够实现更为复杂和特定的转换逻辑。此外,该接口还包含一个内部类 `ConvertiblePair`,用于表示可转换的源到目标类型对。 ```java /** * 用于在两种或多种类型之间进行转换的通用转换器接口。 * *

这是 Converter SPI 接口中最灵活但也最复杂的一个。它之所以灵活,是因为 GenericConverter 可以支持在多个源/目标 * 类型对之间进行转换(参见 {@link #getConvertibleTypes()})。此外,GenericConverter 实现在类型转换过程中可以访问源/目标 * {@link TypeDescriptor 字段上下文}。这允许解析源和目标字段的元数据,如注解和泛型信息,这些信息可以用来影响转换逻辑。 * *

当简单的 {@link Converter} 或 {@link ConverterFactory} 接口足够时,通常不应使用此接口。 * *

实现还可以选择实现 {@link ConditionalConverter}。 * * @author Keith Donald * @author Juergen Hoeller * @since 3.0 * @see TypeDescriptor * @see Converter * @see ConverterFactory * @see ConditionalConverter */ public interface GenericConverter { /** * 返回此转换器可以在其间转换的源和目标类型。 *

每个条目都是一个可转换的源到目标类型对。 *

对于{@link ConditionalConverter 条件转换器},此方法可能返回{@code null},表示应该考虑所有源到目标的对。 */ @Nullable Set getConvertibleTypes(); /** * 将源对象转换为 {@code TypeDescriptor} 描述的目标类型。 * @param source 要转换的源对象(可能是 {@code null}) * @param sourceType 我们要从中转换的字段的类型描述符 * @param targetType 我们要转换到的字段的类型描述符 * @return 转换后的对象 */ @Nullable Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType); /** * 用于源到目标类对的持有者。 */ final class ConvertiblePair { private final Class sourceType; private final Class targetType; /** * 创建一个新的源到目标对。 * @param sourceType 源类型 * @param targetType 目标类型 */ public ConvertiblePair(Class sourceType, Class targetType) { Assert.notNull(sourceType, "源类型不得为空"); Assert.notNull(targetType, "目标类型不得为空"); this.sourceType = sourceType; this.targetType = targetType; } public Class getSourceType() { return this.sourceType; } public Class getTargetType() { return this.targetType; } // 省略了 equals, hashCode 和 toString 方法的中文注释 } } ``` ### 六、主要实现 1. **ArrayToArrayConverter** - 用于将一个数组转换为另一种类型的数组。例如,从 `Integer[]` 转换为 `String[]`。 2. **ArrayToCollectionConverter** - 将数组转换为集合。例如,将 `String[]` 转换为 `List`。 3. **ArrayToObjectConverter** - 将数组转换为单个对象。通常用于从单元素数组中提取元素。 4. **ArrayToStringConverter** - 将数组转换为其字符串表示形式,通常用于打印或日志记录。 5. **ByteBufferConverter** - 用于将 `ByteBuffer` 转换为其他类型,如字符串或二进制数组。 6. **CollectionToArrayConverter** - 将集合转换为数组。例如,将 `List` 转换为 `Integer[]`。 7. **CollectionToCollectionConverter** - 将一种集合转换为另一种类型的集合。例如,从 `List` 转换为 `Set`。 8. **CollectionToObjectConverter** - 将集合转换为单个对象,通常用于从单元素集合中提取元素。 9. **CollectionToStringConverter** - 将集合转换为其字符串表示,通常用于打印或日志记录。 10. **EnumToIntegerConverter** - 将枚举值转换为整数,通常是枚举的顺序值。 11. **EnumToStringConverter** - 将枚举值转换为字符串。 12. **FallbackObjectToStringConverter** - 当没有其他更具体的转换器可用时,将对象转换为字符串的后备转换器。 13. **IdToEntityConverter** - 将ID转换为实体对象,通常用于数据库实体的转换。 14. **MapToMapConverter** - 将一种类型的映射转换为另一种类型的映射。 15. **NumberToCharacterConverter** - 将数字转换为字符。 16. **ObjectToArrayConverter** - 将对象转换为数组,例如将单个对象包装成单元素数组。 17. **ObjectToCollectionConverter** - 将对象转换为集合,例如将单个对象包装成单元素集合。 18. **ObjectToObjectConverter** - 将一个对象转换为另一个类型的对象,通常用于复杂对象之间的转换。 19. **ObjectToOptionalConverter** - 将对象转换为 `Optional` 类型。 20. **ObjectToStringConverter** - 将对象转换为字符串。 21. **PropertiesToStringConverter** - 将 `Properties` 对象转换为字符串。 22. **StreamConverter** - 用于处理 Java 8 流(Stream)类型的转换。 ### 七、最佳实践 使用 Spring 的 `DefaultConversionService` 来进行自定义类型转换。我们自定义的 `AnnotatedStringToDateConverter` 类然后添加到转换服务中,用于将字符串转换为 `Date` 对象。在我们的示例中,我给大家演示了两种转换:将普通日期字符串和包含时间的日期字符串分别转换为 `Date` 对象。转换后的日期被设置到 `MyBean` 实例的属性中,然后打印出这个实例以展示转换结果。 ```java public class GenericConverterDemo { public static void main(String[] args) { // 创建一个默认的转换服务 DefaultConversionService service = new DefaultConversionService(); // 向转换服务中添加自定义的转换器 service.addConverter(new AnnotatedStringToDateConverter()); // 定义源类型和目标类型,准备将 String 转换为 Date TypeDescriptor sourceType1 = TypeDescriptor.valueOf(String.class); TypeDescriptor targetType1 = new TypeDescriptor(ReflectionUtils.findField(MyBean.class, "date")); // 执行转换操作 Date date = (Date) service.convert("2023-01-01", sourceType1, targetType1); // 定义另一组源类型和目标类型,准备将另一个 String 格式转换为 Date TypeDescriptor sourceType2 = TypeDescriptor.valueOf(String.class); TypeDescriptor targetType2 = new TypeDescriptor(ReflectionUtils.findField(MyBean.class, "dateTime")); // 执行转换操作 Date dateTime = (Date) service.convert("2023-01-01 23:59:59", sourceType2, targetType2); // 使用转换得到的日期对象设置 MyBean 实例的属性 MyBean myBean = new MyBean(); myBean.setDate(date); myBean.setDateTime(dateTime); // 输出转换结果 System.out.println("myBean = " + myBean); } } ``` `AnnotatedStringToDateConverter` 类是一个自定义的类型转换器,用于将字符串转换为 `Date` 对象。转换逻辑不是固定的,而是基于目标 `Date` 类型字段上的 `DateFormat` 注解动态确定。 ```java public class AnnotatedStringToDateConverter implements GenericConverter { @Override public Set getConvertibleTypes() { // 定义可转换的类型对:从 String 到 Date return Collections.singleton(new ConvertiblePair(String.class, Date.class)); } @Override public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { // 如果源对象为空,直接返回 null if (source == null) { return null; } // 将源对象转换为字符串 String dateString = (String) source; // 获取目标类型(Date类型字段)上的 DateFormat 注解 DateFormat dateFormatAnnotation = targetType.getAnnotation(DateFormat.class); // 如果目标字段上没有 DateFormat 注解,则抛出异常 if (dateFormatAnnotation == null) { throw new IllegalArgumentException("目标字段上缺少DateFormat注解"); } try { // 根据注解中提供的日期格式创建 SimpleDateFormat SimpleDateFormat dateFormat = new SimpleDateFormat(dateFormatAnnotation.value()); // 使用 SimpleDateFormat 将字符串解析为日期对象 return dateFormat.parse(dateString); } catch (Exception e) { // 如果解析失败,抛出异常 throw new IllegalArgumentException("无法解析日期", e); } } } ``` 定义了一个 Java 注解 `DateFormat`,用于指定日期格式。 ```java @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface DateFormat { String value(); } ``` `MyBean`类中的 `date` 字段被注解为 `"yyyy-MM-dd"` 格式,而 `dateTime` 字段则被注解为 `"yyyy-MM-dd hh:mm:ss"` 格式。这些注解可以被用于自定义转换逻辑中,例如在使用之前定义的 `AnnotatedStringToDateConverter` 转换器时,这些注解将指导转换器如何将字符串转换为 `Date` 对象。 ```java public class MyBean { @DateFormat("yyyy-MM-dd") private Date date; @DateFormat("yyyy-MM-dd hh:mm:ss") private Date dateTime; public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } public Date getDateTime() { return dateTime; } public void setDateTime(Date dateTime) { this.dateTime = dateTime; } @Override public String toString() { return "MyBean{" + "date=" + date + ", dateTime=" + dateTime + '}'; } } ``` 运行结果发现,`MyBean` 类的实例成功地将字符串转换为了相应的 `Date` 对象,并且这些对象的值反映了源字符串中的日期和时间信息。这个结果是自定义类型转换在实际应用中的一个典型例子,展示了通过注解和转换器相结合来实现灵活的类型转换逻辑。 ```java myBean = MyBean{date=Sun Jan 01 00:00:00 CST 2023, dateTime=Sun Jan 01 23:59:59 CST 2023} ``` ### 八、与其他组件的关系 1. **Converter** + 用于简单的一对一类型转换。`Converter` 接口定义了一个方法,将一种类型 `S` 转换为另一种类型 `T`,适用于直接的类型转换场景。 2. **ConverterFactory** + 为一个源类型 `S` 到其子类型 `R` 的多个目标类型提供转换器。`ConverterFactory` 可以创建特定于目标类型的 `Converter` 实例,适合需要统一源类型到多个相关目标类型转换的场景。 3. **ConversionService** + 定义了一个类型转换服务,它在运行时提供类型转换功能。`ConversionService` 可以注册和管理多个 `Converter` 和 `GenericConverter` 实例,为应用程序提供集中的类型转换机制。 4. **ConditionalConverter** + 是一种特殊的转换器,它的执行依赖于特定条件。这种接口允许转换逻辑根据特定情况(如特定注解或字段属性)来决定是否执行。 ### 九、常见问题 1. **类型匹配问题** + 在转换过程中可能会出现源类型或目标类型不匹配的问题。要解决这个问题,需要确保 `getConvertibleTypes()` 方法正确返回所有支持的源和目标类型对。 2. **转换逻辑错误** + 错误的转换逻辑可能导致转换失败或产生不正确的结果。为了避免这种情况,应仔细检查 `convert` 方法的实现,并确保转换逻辑正确处理了所有边界情况和异常情况。 3. **性能问题** + 复杂的转换逻辑可能影响应用程序的性能。为了优化性能,应避免在 `convert` 方法中进行昂贵的操作,并考虑对常用的转换结果进行缓存。 4. **上下文信息不足** + 有时转换过程需要更多的上下文信息,而 `TypeDescriptor` 提供的信息可能不足。解决这个问题可以考虑使用 `TypeDescriptor` 的高级功能,如访问字段的注解或泛型信息,或者改用更适合的转换器接口。 5. **异常处理不当** + 在转换过程中可能遇到各种异常,如果没有正确处理这些异常,可能会导致程序崩溃或不稳定。为了解决这个问题,在 `convert` 方法中应妥善处理所有潜在的异常,并在必要时抛出适当的自定义异常。 ================================================ FILE: spring-dataops/spring-dataops-genericConverter/pom.xml ================================================ spring-dataops com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-dataops-genericConverter ================================================ FILE: spring-dataops/spring-dataops-genericConverter/src/main/java/com/xcs/spring/GenericConverterDemo.java ================================================ package com.xcs.spring; import com.xcs.spring.bean.MyBean; import com.xcs.spring.convert.AnnotatedStringToDateConverter; import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.util.ReflectionUtils; import java.util.Date; public class GenericConverterDemo { public static void main(String[] args) { // 创建一个默认的转换服务 DefaultConversionService service = new DefaultConversionService(); // 向转换服务中添加自定义的转换器 service.addConverter(new AnnotatedStringToDateConverter()); // 定义源类型和目标类型,准备将 String 转换为 Date TypeDescriptor sourceType1 = TypeDescriptor.valueOf(String.class); TypeDescriptor targetType1 = new TypeDescriptor(ReflectionUtils.findField(MyBean.class, "date")); // 执行转换操作 Date date = (Date) service.convert("2023-01-01", sourceType1, targetType1); // 定义另一组源类型和目标类型,准备将另一个 String 格式转换为 Date TypeDescriptor sourceType2 = TypeDescriptor.valueOf(String.class); TypeDescriptor targetType2 = new TypeDescriptor(ReflectionUtils.findField(MyBean.class, "dateTime")); // 执行转换操作 Date dateTime = (Date) service.convert("2023-01-01 23:59:59", sourceType2, targetType2); // 使用转换得到的日期对象设置 MyBean 实例的属性 MyBean myBean = new MyBean(); myBean.setDate(date); myBean.setDateTime(dateTime); // 输出转换结果 System.out.println("myBean = " + myBean); } } ================================================ FILE: spring-dataops/spring-dataops-genericConverter/src/main/java/com/xcs/spring/annotation/DateFormat.java ================================================ package com.xcs.spring.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface DateFormat { String value(); } ================================================ FILE: spring-dataops/spring-dataops-genericConverter/src/main/java/com/xcs/spring/bean/MyBean.java ================================================ package com.xcs.spring.bean; import com.xcs.spring.annotation.DateFormat; import java.util.Date; public class MyBean { @DateFormat("yyyy-MM-dd") private Date date; @DateFormat("yyyy-MM-dd hh:mm:ss") private Date dateTime; public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } public Date getDateTime() { return dateTime; } public void setDateTime(Date dateTime) { this.dateTime = dateTime; } @Override public String toString() { return "MyBean{" + "date=" + date + ", dateTime=" + dateTime + '}'; } } ================================================ FILE: spring-dataops/spring-dataops-genericConverter/src/main/java/com/xcs/spring/convert/AnnotatedStringToDateConverter.java ================================================ package com.xcs.spring.convert; import com.xcs.spring.annotation.DateFormat; import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.converter.GenericConverter; import java.text.SimpleDateFormat; import java.util.Collections; import java.util.Date; import java.util.Set; public class AnnotatedStringToDateConverter implements GenericConverter { @Override public Set getConvertibleTypes() { // 定义可转换的类型对:从 String 到 Date return Collections.singleton(new ConvertiblePair(String.class, Date.class)); } @Override public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { // 如果源对象为空,直接返回 null if (source == null) { return null; } // 将源对象转换为字符串 String dateString = (String) source; // 获取目标类型(Date类型字段)上的 DateFormat 注解 DateFormat dateFormatAnnotation = targetType.getAnnotation(DateFormat.class); // 如果目标字段上没有 DateFormat 注解,则抛出异常 if (dateFormatAnnotation == null) { throw new IllegalArgumentException("目标字段上缺少DateFormat注解"); } try { // 根据注解中提供的日期格式创建 SimpleDateFormat SimpleDateFormat dateFormat = new SimpleDateFormat(dateFormatAnnotation.value()); // 使用 SimpleDateFormat 将字符串解析为日期对象 return dateFormat.parse(dateString); } catch (Exception e) { // 如果解析失败,抛出异常 throw new IllegalArgumentException("无法解析日期", e); } } } ================================================ FILE: spring-dataops/spring-dataops-parser/README.md ================================================ ## Parser - [Parser](#parser) - [一、基本信息](#一基本信息) - [二、知识储备](#二知识储备) - [三、基本描述](#三基本描述) - [四、主要功能](#四主要功能) - [五、接口源码](#五接口源码) - [六、主要实现](#六主要实现) - [七、最佳实践](#七最佳实践) - [八、与其他组件的关系](#八与其他组件的关系) - [九、常见问题](#九常见问题) ### 一、基本信息 ✒ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、知识储备 1. **本地化和国际化(i18n)** + 了解如何根据不同的地区设置(Locale)进行数据格式化,这在全球化的应用开发中非常重要。 2. **Java API for Formatting** + 熟悉 Java 的格式化相关 API,例如 `java.text.DateFormat`、`java.text.SimpleDateFormat` 和 `java.time.format.DateTimeFormatter`,因为这些通常在实现 `Printer` 接口时使用。 3. **ConversionService** + `ConversionService` 是 Spring 中用于管理转换器(`Converters`)和格式化器(`Formatters`,包括 `Printer` 和 `Parser`)的服务。理解 `ConversionService` 如何工作,可以帮助您更好地集成和使用 `Printer` 接口。 ### 三、基本描述 `Parser` 接口在Spring框架中是一个关键的数据转换工具,专门用于将字符串格式的数据转换为更复杂的对象类型。这个接口提供了一个简洁的方法,允许我们自定义如何将文本输入转换成所需的数据类型,如从或者字符串到 `Number`。此接口的灵活性在于支持国际化,因此可以根据不同地区的标准来解析数据。 ### 四、主要功能 1. **字符串到对象的转换** + 将字符串数据转换成更复杂的对象类型。这对于处理来自用户输入或外部数据源的文本数据尤其重要。 2. **支持自定义类型转换** + 我们可以创建自定义的解析器来处理特定类型的数据转换,例如日期时间、货币或任何自定义的数据类型。 3. **国际化支持** + `Parser` 接口的方法通常包括一个 `Locale` 参数,这使得解析器可以根据不同的地区设置来正确解析字符串,支持多语言和区域的格式差异。 ### 五、接口源码 `Parser` 接口是Spring框架中用于从字符串到特定对象类型转换的关键接口。它定义了一个单一的方法 `parse`,该方法接受一个字符串和区域设置(Locale),返回一个指定类型(T)的实例。 ```java /** * 用于将文本字符串解析为T类型实例的接口。 * * @author Keith Donald * @since 3.0 * @param 这个解析器生成的对象类型 */ @FunctionalInterface public interface Parser { /** * 解析文本字符串以生成T类型的实例。 * @param text 要解析的文本字符串 * @param locale 当前用户的区域设置 * @return T类型的实例 * @throws ParseException 当java.text解析库发生解析异常时抛出 * @throws IllegalArgumentException 当发生解析异常时抛出 */ T parse(String text, Locale locale) throws ParseException; } ``` ### 六、主要实现 1. **`CharArrayFormatter`** + 用于将字符串解析为字符数组。适用于需要将文本数据转换为字符数组形式的场景。 2. **`CurrencyParser`** + 专门用于解析货币值的字符串,例如将 "$1,234.56" 解析成相应的数值表示。 3. **`CurrencyStyleFormatter`** + 用于格式化和解析货币值,不仅支持解析还支持将数值格式化为货币字符串。 4. **`CurrencyUnitFormatter`** + 用于格式化和解析货币单位,例如将 "USD" 或 "EUR" 等货币代码转换成相应的货币单位对象。 5. **`DateFormatter`** + 专门用于解析日期字符串,如将 "2023-12-31" 转换成 `java.util.Date` 对象。 6. **`DateTimeParser`** + 用于解析日期和时间的字符串,转换为 `java.time.LocalDateTime` 或类似的日期时间对象。 7. **`DurationFormatter`** + 用于解析表示时间长度的字符串,如 "PT10M"(表示10分钟),转换为 `java.time.Duration` 对象。 8. **`InetAddressFormatter`** + 用于解析IP地址的字符串,将其转换为 `java.net.InetAddress` 对象。 9. **`InstantFormatter`** + 用于解析表示特定时刻的字符串,如 "2020-01-01T00:00:00Z",转换为 `java.time.Instant` 对象。 10. **`IsoOffsetFormatter`** + 用于解析带时区偏移的日期时间字符串,转换为 `java.time.OffsetDateTime` 等类型。 11. **`LocalDateParser`** + 专门用于解析仅包含日期部分的字符串,如 "2020-01-01",转换为 `java.time.LocalDate` 对象。 12. **`LocalDateTimeParser`** + 用于解析包含日期和时间的字符串,转换为 `java.time.LocalDateTime` 对象。 13. **`LocalTimeParser`** + 专门用于解析时间字符串,如 "23:59",转换为 `java.time.LocalTime` 对象。 14. **`MonetaryAmountFormatter`** + 用于格式化和解析表示货币金额的字符串。 15. **`MonthDayFormatter`** + 用于解析表示月和日的字符串,如 "12-31",转换为 `java.time.MonthDay` 对象。 16. **`MonthFormatter`** + 用于解析表示月份的字符串,如 "January" 或 "01",转换为 `java.time.Month` 枚举。 17. **`NumberDecoratingFormatter`** + 一个装饰器,用于增强对数字的格式化和解析功能。 18. **`PatternDecoratingFormatter`** + 用于在解析和格式化时应用特定的模式或模板。 19. **`PeriodFormatter`** + 用于解析表示一段时间长度的字符串,如 "P1Y2M"(1年2个月),转换为 `java.time.Period` 对象。 20. **`TemporalAccessorParser`** + 用于解析 `java.time.temporal.TemporalAccessor` 的实现类,如 `LocalDate`、`LocalDateTime` 等。 21. **`YearFormatter`** + 用于解析年份字符串,如 "2020",转换为 `java.time.Year` 对象。 22. **`YearMonthFormatter`** + 用于解析表示年和月的字符串,如 "2020-01",转换为 `java.time.YearMonth` 对象。 ### 七、最佳实践 使用Spring框架的 `FormattingConversionService` 来进行货币字符串的解析。它首先创建了一个 `FormattingConversionService` 实例,并向其添加了一个 `CurrencyParser` 实现。通过设置 `LocaleContextHolder` 的区域设置,该代码能够根据不同的地区(美国和中国)解析相应格式的货币字符串(美元和人民币)。 ```java public class ParserDemo { public static void main(String[] args) { // 创建一个格式化转换服务 FormattingConversionService conversionService = new FormattingConversionService(); // 向转换服务中添加一个货币解析器 conversionService.addParser(new CurrencyParser()); // 设置当前线程的区域设置为美国 LocaleContextHolder.setLocale(Locale.US); // 将美元格式的字符串转换为数值类型 Number formattedAmountForUS = conversionService.convert("$1,234.56", Number.class); System.out.println("Parsed Currency (US): " + formattedAmountForUS); // 改变区域设置为中国 LocaleContextHolder.setLocale(Locale.CHINA); // 将人民币格式的字符串转换为数值类型 Number formattedAmountForCHINA = conversionService.convert("¥1,234.56", Number.class); System.out.println("Parsed Currency (CHINA): " + formattedAmountForCHINA); } } ``` `CurrencyParser` 类,它实现了 Spring 框架中的 `Parser` 接口,用于将货币格式的字符串解析为数值类型。这个类的 `parse` 方法接收一个货币格式的字符串和一个 `Locale` 对象作为参数。使用 `Locale` 参数,它创建了一个 `NumberFormat` 实例,这个实例专门用于解析特定区域(如美国、中国等)的货币格式。 ```java public class CurrencyParser implements Parser { @Override public Number parse(String text, Locale locale) throws ParseException { if (text == null) { throw new ParseException("Null string cannot be parsed to number", 0); } // 创建适用于特定区域的货币格式化器 NumberFormat format = NumberFormat.getCurrencyInstance(locale); // 解析字符串为数字 return format.parse(text); } } ``` 运行结果发现,`CurrencyParser` 类如何成功地将不同地区格式的货币字符串解析为数值。无论是美国的 "$1,234.56" 还是中国的 "¥1,234.56",都被正确地解析为相同的数值 `1234.56`。 ```java Parsed Currency (US): 1234.56 Parsed Currency (CHINA): 1234.56 ``` ### 八、与其他组件的关系 1. **Printer** + `Printer` 接口的职责是将对象格式化为字符串。这在显示数据或将对象序列化为字符串形式以便于存储和传输时非常有用。 2. **Formatter** - `Formatter` 接口是 `Parser` 接口的补充,不仅提供了从字符串到对象的解析功能(与 `Parser` 相同),还提供了将对象格式化为字符串的功能。在实际应用中,很多时候 `Formatter` 同时实现了解析和格式化两种功能。 3. **FormattingConversionService** - `FormattingConversionService` 是Spring的一个核心类,用于注册和使用格式化器和解析器。它可以注册实现了 `Parser` 接口的类,从而提供字符串到对象的类型转换服务。 4. **DataBinder&WebDataBinder** - 这些类用于数据绑定和类型转换。在处理表单提交或其他用户输入时,`DataBinder` 和 `WebDataBinder` 可以利用注册到 `FormattingConversionService` 的 `Parser` 实现,将字符串转换为相应的对象类型。 5. **LocaleContextHolder** - `LocaleContextHolder` 提供了一种访问当前线程特定区域设置(Locale)的方式。由于 `Parser` 接口的 `parse` 方法通常需要一个 `Locale` 参数,`LocaleContextHolder` 可以在解析过程中提供必要的区域信息,使解析能够适应不同的地区特定格式。 6. **Locale** - `Parser` 接口的 `parse` 方法接受一个 `Locale` 参数,这允许实现类根据不同的地区设置来格式化对象,支持国际化和本地化需求。 ### 九、常见问题 1. **复杂的解析逻辑** - 面对复杂的数据类型或需支持多种地区设置的复杂解析逻辑时,建议利用现有的 Java API(如 `DateFormat`, `NumberFormat` 等)进行处理。同时,可以结合Spring的国际化支持(如 `MessageSource`)来简化实现。 2. **处理不同的地区设置(Locale)** - 在国际化应用程序中,正确处理不同地区设置是一个挑战。确保 `parse` 方法适当地使用传入的 `Locale` 参数,并在可能的情况下利用Spring的本地化和国际化支持,以确保数据正确地解析。 3. **与Spring MVC的集成** - 在Spring MVC应用程序中,正确注册和使用自定义的 `Parser` 实现需要确保它在 `FormattingConversionService` 中被正确注册。此外,应检查控制器方法是否正确使用了数据绑定和解析逻辑。 4. **保证解析和格式化的一致性** - 当同时使用 `Parser` 和对应的 `Printer` 时,非常重要的一点是保证它们对于相同类型的数据有着一致的解析和格式化规则。这意味着相同类型的数据应该能够在解析和格式化之间无缝转换,而不会引入任何不一致性。 ================================================ FILE: spring-dataops/spring-dataops-parser/pom.xml ================================================ spring-dataops com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-dataops-parser ================================================ FILE: spring-dataops/spring-dataops-parser/src/main/java/com/xcs/spring/ParserDemo.java ================================================ package com.xcs.spring; import com.xcs.spring.parser.CurrencyParser; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.format.support.FormattingConversionService; import java.util.Locale; public class ParserDemo { public static void main(String[] args) { // 创建一个格式化转换服务 FormattingConversionService conversionService = new FormattingConversionService(); // 向转换服务中添加一个货币解析器 conversionService.addParser(new CurrencyParser()); // 设置当前线程的区域设置为美国 LocaleContextHolder.setLocale(Locale.US); // 将美元格式的字符串转换为数值类型 Number formattedAmountForUS = conversionService.convert("$1,234.56", Number.class); System.out.println("Parsed Currency (US): " + formattedAmountForUS); // 改变区域设置为中国 LocaleContextHolder.setLocale(Locale.CHINA); // 将人民币格式的字符串转换为数值类型 Number formattedAmountForCHINA = conversionService.convert("¥1,234.56", Number.class); System.out.println("Parsed Currency (CHINA): " + formattedAmountForCHINA); } } ================================================ FILE: spring-dataops/spring-dataops-parser/src/main/java/com/xcs/spring/parser/CurrencyParser.java ================================================ package com.xcs.spring.parser; import org.springframework.format.Parser; import java.text.NumberFormat; import java.text.ParseException; import java.util.Locale; public class CurrencyParser implements Parser { @Override public Number parse(String text, Locale locale) throws ParseException { // 创建适用于特定区域的货币格式化器 NumberFormat format = NumberFormat.getCurrencyInstance(locale); // 解析字符串为数字 return format.parse(text); } } ================================================ FILE: spring-dataops/spring-dataops-printer/README.md ================================================ ## Printer - [Printer](#printer) - [一、基本信息](#一基本信息) - [二、知识储备](#二知识储备) - [三、基本描述](#三基本描述) - [四、主要功能](#四主要功能) - [五、接口源码](#五接口源码) - [六、主要实现](#六主要实现) - [七、最佳实践](#七最佳实践) - [八、与其他组件的关系](#八与其他组件的关系) - [九、常见问题](#九常见问题) ### 一、基本信息 ✒ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、知识储备 1. **本地化和国际化(i18n)** + 了解如何根据不同的地区设置(Locale)进行数据格式化,这在全球化的应用开发中非常重要。 2. **Java API for Formatting** + 熟悉 Java 的格式化相关 API,例如 `java.text.DateFormat`、`java.text.SimpleDateFormat` 和 `java.time.format.DateTimeFormatter`,因为这些通常在实现 `Printer` 接口时使用。 3. **ConversionService** + `ConversionService` 是 Spring 中用于管理转换器(`Converters`)和格式化器(`Formatters`,包括 `Printer` 和 `Parser`)的服务。理解 `ConversionService` 如何工作,可以帮助您更好地集成和使用 `Printer` 接口。 ### 三、基本描述 `Printer` 接口是 Spring 框架中的一个关键部分,主要用于格式化对象到人类可读的字符串表示。这个接口定义了一个单一的方法 `String print(T object, Locale locale)`,其中 `T` 是要格式化的对象类型,`Locale` 是用于格式化操作的地区设置,允许根据不同的文化和地区标准来定制格式化输出。 ### 四、主要功能 1. **格式化对象为字符串** + 将各种类型的对象(如日期、时间、数字等)转换成人类可读的字符串形式。 2. **支持本地化** + 通过接受 `Locale` 参数,`Printer` 接口允许根据不同的地区设置来进行格式化,支持国际化和本地化需求。 3. **用户界面友好** + 使数据以一种对用户友好的方式展示,增强了数据在用户界面上的可读性和可理解性。 ### 五、接口源码 这个 `Printer` 接口定义了一个专门用于显示目的的打印方法。它是一个功能性接口,其主要作用是将类型为 T 的对象转换为适合展示的字符串形式。该接口接受两个参数:一个是要打印的对象实例 `object`,另一个是表示当前用户地区设置的 `locale`。这样设计的目的是为了支持根据不同地区的标准来格式化对象,从而适应国际化和本地化的需求。 ```java /** * 用于显示目的打印类型为 T 的对象。 * * @author Keith Donald * @since 3.0 * @param 这个 Printer 打印的对象类型 */ @FunctionalInterface public interface Printer { /** * 打印类型为 T 的对象以供显示。 * @param object 要打印的实例 * @param locale 当前用户的地区设置 * @return 打印出的文本字符串 */ String print(T object, Locale locale); } ``` ### 六、主要实现 1. **CharArrayFormatter** + 将字符数组格式化为字符串。这通常涉及将字符数组直接转换为相应的字符串表示。 2. **CurrencyUnitFormatter** + 用于格式化货币单位。它可能将像“USD”或“EUR”这样的货币代码转换为更完整或本地化的货币名称表示。 3. **DateFormatter** + 用于将 `java.util.Date` 对象格式化为易读的日期字符串。它可以支持多种日期格式,并可能根据不同的地区设置进行调整。 4. **DurationFormatter** + 将 `java.time.Duration` 实例(表示时间间隔)格式化为易于阅读的字符串,例如,将时长转换为“2小时15分钟”等形式。 5. **InetAddressFormatter** + 专门用于将 `java.net.InetAddress` 对象(例如,IP 地址)格式化为标准的字符串表示,比如将IP地址从二进制形式转换为点分十进制格式。 6. **InstantFormatter** + 用于将 `java.time.Instant`(表示特定时间点的对象)格式化为日期和时间的字符串表示,通常包括日期和时间。 7. **IsoOffsetFormatter** + 专注于格式化带有时区偏移信息的日期时间对象,按照 ISO 8601 标准(例如 "2023-03-28T15:20:45+02:00")。 8. **MillisecondInstantPrinter** + 将以毫秒为单位的时间戳转换为标准的日期和时间格式,通常用于将系统时间戳转换为人类可读的日期时间表示。 9. **MonetaryAmountFormatter** + 用于格式化表示货币金额的对象,可能包括数值与货币单位的结合,以及考虑货币格式的本地化。 10. **MonthDayFormatter** + 格式化 `java.time.MonthDay` 对象,这种格式化通常涉及将月份和日子转换为特定的字符串格式,如“3月28日”。 11. **MonthFormatter** + 专门用于格式化月份,将数字月份(如 1-12)转换为文本表示(如 "January", "February" 等)。 12. **NumberDecoratingFormatter** + 为数字提供装饰性的格式化,可能包括添加前缀、后缀或将数字格式化为特定的显示格式。 13. **PatternDecoratingFormatter** + 使用指定的模式来格式化和装饰字符串,这可能涉及到复杂的字符串处理和模式匹配。 14. **PeriodFormatter** + 用于将 `java.time.Period` 对象(表示日期间隔)格式化为易读的字符串,比如“2年6个月”。 15. **ReadableInstantPrinter** + 用于 Joda-Time 库中的 `ReadableInstant` 对象,将它们转换为日期和时间的字符串表示。 16. **ReadablePartialPrinter** + 用于 Joda-Time 库中的 `ReadablePartial` 对象,这些对象通常表示部分日期时间信息,如仅月日。 17. **TemporalAccessorPrinter** + 用于格式化任何实现了 `java.time.temporal.TemporalAccessor` 接口的日期时间对象,这包括大多数 Java 8 日期时间类型。 18. **YearFormatter** + 专门用于格式化年份,可能涉及将数字年份转换为特定的字符串表示。 19. **YearMonthFormatter** + 用于格式化 `java.time.YearMonth` 对象,通常将年和月结合为易读的格式,如“2023年3月”。 ### 七、最佳实践 使用 Spring 的 `FormattingConversionService` 结合自定义的 `CurrencyPrinter` 来格式化货币。在这个示例中,首先创建了一个 `FormattingConversionService` 实例并注册了 `CurrencyPrinter`。接着,通过设置 `LocaleContextHolder` 的 `Locale`,代码分别以美国和中国的格式来显示同一金额的字符串表示。 ```java public class PrinterDemo { public static void main(String[] args) { // 创建一个 FormattingConversionService 实例 FormattingConversionService conversionService = new FormattingConversionService(); // 将自定义的 CurrencyPrinter 注册到 conversionService conversionService.addPrinter(new CurrencyPrinter()); // 设置货币金额 double amount = 1234.56; // 设置当前线程的 Locale 为美国 LocaleContextHolder.setLocale(Locale.US); // 使用 conversionService 将金额转换为字符串,并应用美国的货币格式 String formattedAmountForUS = conversionService.convert(amount, String.class); System.out.println("Formatted Currency (US): " + formattedAmountForUS); // 设置当前线程的 Locale 为中国 LocaleContextHolder.setLocale(Locale.CHINA); // 使用 conversionService 将金额转换为字符串,并应用中国的货币格式 String formattedAmountForCHINA = conversionService.convert(amount, String.class); System.out.println("Formatted Currency (CHINA): " + formattedAmountForCHINA); } } ``` `CurrencyPrinter` 类实现了 Spring 的 `Printer` 接口。它的主要功能是将数字(`Number` 类型)格式化为货币字符串。在 `print` 方法中,使用了 Java 的 `NumberFormat.getCurrencyInstance` 方法来获取相应地区的货币格式化器,并用它将数字转换为该地区特定的货币字符串格式。 ```java public class CurrencyPrinter implements Printer { @Override public String print(Number number, Locale locale) { NumberFormat currencyFormat = NumberFormat.getCurrencyInstance(locale); return currencyFormat.format(number); } } ``` 运行结果发现, `CurrencyPrinter` 类和 `Locale` 设置来处理货币的国际化表示,确保金额在不同地区都能以用户熟悉和易于理解的方式呈现。 ```java Formatted Currency (US): $1,234.56 Formatted Currency (CHINA): ¥1,234.56 ``` ### 八、与其他组件的关系 1. **Parser** - `Printer` 和 `Parser` 接口通常一起使用。`Printer` 负责将对象格式化为字符串,而 `Parser` 负责执行相反的操作,即将字符串解析回原始对象。这种组合在处理表单数据、展示和用户输入时非常有用。 2. **Formatter** - `Formatter` 接口结合了 `Printer` 和 `Parser` 的功能,提供了一个统一的接口用于格式化和解析对象。任何实现了 `Formatter` 接口的类同时实现了 `print` 和 `parse` 方法,分别对应于 `Printer` 和 `Parser` 的功能。 3. **FormattingConversionService** - `FormattingConversionService` 是 Spring 的一个核心组件,用于注册和管理格式化器(Formatters)和转换器(Converters)。`Printer` 接口的实现可以注册到 `FormattingConversionService` 中,这样,Spring 就可以在需要时自动使用这些实现来格式化数据。 4. **DataBinder&WebDataBinder** - 在数据绑定和表单处理中,`Printer` 接口的实现用于将对象属性转换为适合于显示的字符串。例如,在 Spring MVC 中,`WebDataBinder` 可以使用注册的 `Printer` 实现来格式化模型属性以便在视图中显示。 5. **Locale** - `Printer` 接口的 `print` 方法接受一个 `Locale` 参数,这允许实现类根据不同的地区设置来格式化对象,支持国际化和本地化需求。 ### 九、常见问题 1. **复杂的格式化逻辑** + 对于处理复杂数据类型或支持多种地区设置的复杂格式化逻辑,建议使用现有的 Java API(如 `DateFormat`、`NumberFormat` 等),并结合 Spring 的国际化支持(如 `MessageSource`)来简化实现。 2. **处理不同的地区设置(`Locale`)** + 在国际化应用程序中正确处理不同的地区设置可能是一个挑战。确保 `print` 方法适当地使用传入的 `Locale` 参数,并在可能的情况下利用 Spring 的本地化和国际化支持。 3. **与 Spring MVC 的集成** + 在 Spring MVC 应用程序中正确注册和使用自定义的 `Printer` 实现,需要确保在 `FormattingConversionService` 中正确注册了 `Printer` 实现,并检查控制器方法是否正确使用数据绑定和格式化。 4. **保证格式化和解析的一致性** + 当同时使用 `Printer` 和对应的 `Parser` 时,重要的是保证它们对于相同类型的数据有着一致的格式化和解析规则。 ================================================ FILE: spring-dataops/spring-dataops-printer/pom.xml ================================================ spring-dataops com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-dataops-printer ================================================ FILE: spring-dataops/spring-dataops-printer/src/main/java/com/xcs/spring/PrinterDemo.java ================================================ package com.xcs.spring; import com.xcs.spring.printer.CurrencyPrinter; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.format.support.FormattingConversionService; import java.util.Locale; public class PrinterDemo { public static void main(String[] args) { // 创建一个 FormattingConversionService 实例 FormattingConversionService conversionService = new FormattingConversionService(); // 将自定义的 CurrencyPrinter 注册到 conversionService conversionService.addPrinter(new CurrencyPrinter()); // 设置货币金额 double amount = 1234.56; // 设置当前线程的 Locale 为美国 LocaleContextHolder.setLocale(Locale.US); // 使用 conversionService 将金额转换为字符串,并应用美国的货币格式 String formattedAmountForUS = conversionService.convert(amount, String.class); System.out.println("Formatted Currency (US): " + formattedAmountForUS); // 设置当前线程的 Locale 为中国 LocaleContextHolder.setLocale(Locale.CHINA); // 使用 conversionService 将金额转换为字符串,并应用中国的货币格式 String formattedAmountForCHINA = conversionService.convert(amount, String.class); System.out.println("Formatted Currency (CHINA): " + formattedAmountForCHINA); } } ================================================ FILE: spring-dataops/spring-dataops-printer/src/main/java/com/xcs/spring/printer/CurrencyPrinter.java ================================================ package com.xcs.spring.printer; import org.springframework.format.Printer; import java.text.NumberFormat; import java.util.Locale; public class CurrencyPrinter implements Printer { @Override public String print(Number number, Locale locale) { NumberFormat currencyFormat = NumberFormat.getCurrencyInstance(locale); return currencyFormat.format(number); } } ================================================ FILE: spring-dataops/spring-dataops-propertyEditor/README.md ================================================ ## PropertyEditor - [PropertyEditor](#propertyeditor) - [一、基本信息](#一基本信息) - [二、知识储备](#二知识储备) - [三、基本描述](#三基本描述) - [四、主要功能](#四主要功能) - [五、接口源码](#五接口源码) - [六、主要实现](#六主要实现) - [七、最佳实践](#七最佳实践) - [八、与其他组件的关系](#八与其他组件的关系) - [九、常见问题](#九常见问题) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、知识储备 1. **JavaBeans 规范** + 理解 JavaBeans 规范是关键,因为 `PropertyEditor` 是 JavaBeans 规范的一部分。了解 JavaBeans 的组件模型、属性、事件、方法等概念有助于更好地理解 `PropertyEditor` 的作用和应用场景。 2. **反射 API** + 熟悉 Java 反射 API 会很有帮助,因为 `PropertyEditor` 经常与反射一起使用,特别是在动态地处理属性和调用 getter/setter 方法时。 3. **类型转换和数据绑定** + 对类型转换和数据绑定的基本理解也是必要的,因为 `PropertyEditor` 常用于这些领域,尤其是在将字符串形式的配置值转换为复杂对象类型时。 ### 三、基本描述 `PropertyEditor` 接口是 Java Beans 规范的一部分,主要用于处理 Java Bean 属性的转换和编辑。这个接口允许将属性值从一种格式转换为另一种(如从字符串到对象),并提供自定义编辑功能。 ### 四、主要功能 1. **值转换** + 允许将属性值从一种格式转换为另一种格式。最常见的是将字符串转换为相应的对象类型,例如,将日期的字符串表示转换为 `Date` 对象。 2. **文本编辑** + 提供 `setAsText(String text)` 方法来接收一个字符串并将其转换为属性的类型。同样,`getAsText()` 方法用于将属性值转换为其字符串表示形式。 3. **状态管理** + 通过 `setValue(Object value)` 和 `getValue()` 方法来设置和获取属性的当前值。 4. **自定义编辑器支持** + 如果需要,可以提供一个自定义编辑器界面。`supportsCustomEditor()` 方法用于判断是否支持自定义编辑器,`getCustomEditor()` 方法用于获取自定义编辑器的实例。 5. **属性变更通知** + 支持属性变更监听,允许通过 `addPropertyChangeListener(PropertyChangeListener listener)` 和 `removePropertyChangeListener(PropertyChangeListener listener)` 方法添加和移除属性变更监听器。 6. **Java 初始化字符串** + `getJavaInitializationString()` 方法返回一个表示如何以代码形式初始化该属性值的字符串。 ### 五、接口源码 `PropertyEditor` 接口在 Java Beans 规范中用于管理和编辑属性。包括方法来转换属性值(如 `setAsText` 和 `getAsText`)、获取和设置属性(`setValue` 和 `getValue`)、支持属性的图形展示(`isPaintable` 和 `paintValue`)以及生成属性的初始化代码(`getJavaInitializationString`)。此外,接口支持自定义编辑器的创建和使用,以及属性更改的监听机制。 ```java public interface PropertyEditor { /** * 设置要编辑的对象。基本类型如 "int" 必须包装为相应的对象类型,如 "java.lang.Integer"。 * @param value 要编辑的新目标对象。注意,这个对象不应由 PropertyEditor 修改,而应创建一个新对象来保存任何修改后的值。 */ void setValue(Object value); /** * 获取属性值。 * @return 属性的值。基本类型如 "int" 将被包装为相应的对象类型,如 "java.lang.Integer"。 */ Object getValue(); /** * 判断此属性编辑器是否可进行绘制。 * @return 如果类支持 paintValue 方法,则返回 true。 */ boolean isPaintable(); /** * 在屏幕的给定区域内绘制值的表示。注意,PropertyEditor 负责做自己的剪切以适应给定的矩形。 * 如果 PropertyEditor 不支持绘制(见 isPaintable),则此方法应是一个静默的无操作。 * @param gfx 用于绘制的 Graphics 对象。 * @param box Graphics 对象中我们应该绘制的矩形区域。 */ void paintValue(java.awt.Graphics gfx, java.awt.Rectangle box); /** * 返回一段 Java 代码片段,可用于设置属性以匹配编辑器当前状态。此方法用于在通过属性编辑器进行更改时生成 Java 代码。 * @return 代表当前值的 Java 代码的片段。它不应包含用于结束表达式的分号 (';')。 */ String getJavaInitializationString(); /** * 以文本形式获取属性值。 * @return 属性值作为可编辑的字符串。如果值无法表示为可编辑的字符串,则返回 null。 * 如果返回非空值,则 PropertyEditor 应准备好将该字符串解析回 setAsText 中。 */ String getAsText(); /** * 通过解析给定的字符串来设置属性值。如果字符串格式不正确或这种类型的属性不能表示为文本,则可能引发 IllegalArgumentException。 * @param text 要解析的字符串。 */ void setAsText(String text) throws java.lang.IllegalArgumentException; /** * 如果属性值必须是一组已知的标记值之一,则此方法应返回这些标记的数组。这可以用来表示(例如)枚举值。 * 如果 PropertyEditor 支持标记,则应支持使用 setAsText 中的标记值作为设置值的方式。 * @return 此属性的标记值。如果此属性不能表示为标记值,则可能为 null。 */ String[] getTags(); /** * PropertyEditor 可以选择提供一个完整的自定义组件,用于编辑其属性值。PropertyEditor 负责将自己连接到其编辑器组件,并通过触发 PropertyChange 事件来报告属性值更改。 * @return 一个 java.awt.Component,允许用户直接编辑当前属性值。如果不支持此功能,则可能为 null。 */ java.awt.Component getCustomEditor(); /** * 判断此属性编辑器是否支持自定义编辑器。 * @return 如果 propertyEditor 可以提供自定义编辑器,则返回 true。 */ boolean supportsCustomEditor(); /** * 添加一个值更改的监听器。当属性编辑器更改其值时,应在所有注册的 PropertyChangeListener 上触发 PropertyChangeEvent,指定属性名称为 null 并将自己作为源。 * @param listener 要添加的 PropertyChangeListener。 */ void addPropertyChangeListener(PropertyChangeListener listener); /** * 删除值更改的监听器。 * @param listener 要移除的 PropertyChangeListener。 */ void removePropertyChangeListener(PropertyChangeListener listener); } ``` ### 六、主要实现 1. **`ByteArrayPropertyEditor`** - 将字符串转换为字节数组,通常用于处理编码的数据如 BASE64。 2. **`CharacterEditor`** - 将字符串转换为单个字符。 3. **`CharArrayPropertyEditor`** - 将字符串转换为字符数组。 4. **`CharsetEditor`** - 用于将字符串(代表字符集名称)转换为 `java.nio.charset.Charset` 对象。 5. **`ClassArrayEditor`** - 将字符串数组转换为 `Class` 对象数组,每个字符串代表一个类名。 6. **`ClassEditor`** - 将单个字符串(类名)转换为 `Class` 类型的对象。 7. **`CurrencyEditor`** - 用于将字符串转换为 `java.util.Currency` 实例。 8. **`CustomBooleanEditor`** - 将字符串转换为布尔值,可以处理更多的布尔表示,如 "on"/"off"。 9. **`CustomCollectionEditor`** - 用于将字符串转换为特定类型的集合,如 `List` 或 `Set`。 10. **`CustomDateEditor`** - 将字符串转换为 `java.util.Date` 对象,通常需要一个日期格式。 11. **`CustomMapEditor`** - 将字符串转换为 `Map` 类型的对象,用于处理键值对格式的数据。 12. **`CustomNumberEditor`** - 将字符串转换为数字,包括各种数值类型如 `Integer`, `Double`, `BigDecimal` 等。 13. **`FileEditor`** - 将文件路径字符串转换为 `java.io.File` 对象。 14. **`InputSourceEditor`** - 用于将字符串转换为 `org.xml.sax.InputSource` 对象,通常用于处理 XML 数据源。 15. **`InputStreamEditor`** - 将资源位置字符串转换为 `java.io.InputStream`。 16. **`LocaleEditor`** - 将字符串转换为 `java.util.Locale` 对象,例如 "en_US"。 17. **`PathEditor`** - 类似于 `FileEditor`,但用于转换为 `java.nio.file.Path` 对象。 18. **`PatternEditor`** - 将字符串转换为 `java.util.regex.Pattern` 对象,用于正则表达式。 19. **`PropertiesEditor`** - 将字符串转换为 `java.util.Properties` 对象,用于处理属性文件格式的字符串。 20. **`ReaderEditor`** - 将资源位置字符串转换为 `java.io.Reader`。 21. **`ResourceBundleEditor`** - 将字符串转换为 `java.util.ResourceBundle`,用于国际化资源。 22. **`StringArrayPropertyEditor`** - 将逗号分隔的字符串转换为字符串数组。 23. **`StringTrimmerEditor`** - 去除字符串前后的空白,并可选地将空字符串转换为 `null`。 24. **`TimeZoneEditor`** - 将字符串转换为 `java.util.TimeZone` 对象。 25. **`URIEditor`** - 将字符串转换为 `java.net.URI` 对象。 26. **`URLEditor`** - 将字符串转换为 `java.net.URL` 对象。 27. **`UUIDEditor`** - 将字符串转换为 `java.util.UUID` 对象。 28. **`ZoneIdEditor`** - 将字符串转换为 `java.time.ZoneId` 对象,用于处理时区信息。 ### 七、最佳实践 使用 `BeanWrapperImpl` 包装了 `MyBean` 类,并为 `Date` 类型属性注册了自定义编辑器 `MyCustomDateEditor` 来转换日期字符串。然后,为 `MyBean` 的 "date" 和 "path" 属性设置了值,并输出了经过处理的 `MyBean` 实例,展示了 Spring 中属性编辑器的应用和属性值的动态转换。 ```java public class PropertyEditorDemo { public static void main(String[] args) { // 创建一个 BeanWrapperImpl 实例,用于操作 MyBean 类。 BeanWrapperImpl beanWrapper = new BeanWrapperImpl(MyBean.class); // 为 Date 类型的属性注册自定义的属性编辑器 MyCustomDateEditor。 beanWrapper.overrideDefaultEditor(Date.class, new MyCustomDateEditor()); // 设置 MyBean 类中名为 "date" 的属性值,使用字符串 "2023-12-5"。 // 这里会使用注册的 MyCustomDateEditor 来将字符串转换为 Date 对象。 beanWrapper.setPropertyValue("date", "2023-12-5"); // 设置 MyBean 类中名为 "path" 的属性值,使用字符串 "/opt/spring-reading"。 // 因为没有为 Path 类型注册特定的编辑器,所以使用默认转换逻辑。 beanWrapper.setPropertyValue("path", "/opt/spring-reading"); // 输出最终包装的 MyBean 实例。 System.out.println("MyBean = " + beanWrapper.getWrappedInstance()); } } ``` `MyCustomDateEditor` 类是一个继承自 `PropertyEditorSupport` 的自定义属性编辑器,专门用于将符合 "`yyyy-MM-DD`" 格式的字符串转换为 `Date` 对象,以及将 `Date` 对象格式化为该字符串格式。这个类主要用于处理字符串与日期类型之间的相互转换,适用于 Spring 框架中的数据绑定和类型转换场景。 ```java public class MyCustomDateEditor extends PropertyEditorSupport { private final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-DD"); @Override public void setAsText(@Nullable String text) throws IllegalArgumentException { try { setValue(this.dateFormat.parse(text)); } catch (ParseException ex) { throw new IllegalArgumentException("Could not parse date: " + ex.getMessage(), ex); } } @Override public String getAsText() { Date value = (Date) getValue(); return (value != null ? this.dateFormat.format(value) : ""); } } ``` `MyBean`是一个 Java Bean,提供了基本的属性封装和访问方法。通过 getter 和 setter 方法,可以方便地访问和修改这两个属性的值。此外, `toString` 方法提供了一种方便的方式来查看 `MyBean` 实例的当前状态。 ```java public class MyBean { private Path path; private Date date; public Path getPath() { return path; } public void setPath(Path path) { this.path = path; } public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } @Override public String toString() { return "MyBean{" + "path=" + path + ", date=" + date + '}'; } } ``` 运行结果发现,`PropertyEditor` 接口的实现是类型转换过程的核心,使得可以在设置 Java Bean 属性时,自动将字符串转换为需要的复杂类型,如日期类型。这种转换是在 Spring 的上下文中处理配置和数据绑定时非常常见的场景。 ```java MyBean = MyBean{path=\opt\spring-reading, date=Thu Jan 05 00:00:00 CST 2023} ``` ### 八、与其他组件的关系 1. **Java Beans** - `PropertyEditor` 是 Java Beans 规范的一部分,用于管理 Java Bean 属性的编辑和转换。允许 Bean 属性从一种类型转换为另一种类型,特别是从字符串到复杂类型的转换。 2. **Spring 框架** - 在 Spring 框架中,`PropertyEditor` 用于在运行时处理属性文件或注解中的数据转换。使得可以将配置文件(如 XML)中的字符串转换为 Java 对象的属性值。`PropertyEditor` 与 Spring 的 `BeanWrapper` 和 `DataBinder` 组件紧密相关,这些组件用于绑定和验证 Bean 属性。 3. **数据绑定** - `PropertyEditor` 在数据绑定过程中起着关键作用,尤其是在 Web 应用程序中。在将请求参数映射到对象属性时,用于将字符串参数转换为相应的属性类型。 4. **类型转换** - 尽管 Spring 3.0 之后推荐使用 `ConversionService` 作为主要的类型转换机制,`PropertyEditor` 仍然在旧代码和一些特定情况下被使用。`ConversionService` 提供了一种更一致和通用的类型转换方法,但 `PropertyEditor` 仍然在处理某些类型的属性编辑和转换方面发挥作用。 ### 九、常见问题 1. **线程安全问题** - `PropertyEditor` 实例通常不是线程安全的。在多线程环境中共享 `PropertyEditor` 实例可能会导致数据不一致或竞争条件。 2. **状态管理** - `PropertyEditor` 保存状态信息,这意味着同一个实例不能同时用于多个属性或多次操作。每次使用时都需要创建新的实例或重置状态,这可能导致效率问题。 3. **复杂类型转换限制** - 对于一些复杂的数据类型,使用 `PropertyEditor` 进行转换可能会比较困难,特别是当类型转换涉及复杂的逻辑或多步骤处理时。 4. **自定义编辑器的开发和维护** - 我们自定义的 `PropertyEditor` 需要深入理解目标类型的内部结构和行为,这可能增加开发和维护的复杂性。 5. **与 Spring 的 `ConversionService` 的集成** - 自 Spring 3.0 引入 `ConversionService` 后,需要在 `PropertyEditor` 和 `ConversionService` 之间做出选择,或者在有些情况下需要它们共存,这可能导致配置和使用上的混淆。 ================================================ FILE: spring-dataops/spring-dataops-propertyEditor/pom.xml ================================================ spring-dataops com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-dataops-propertyEditor ================================================ FILE: spring-dataops/spring-dataops-propertyEditor/src/main/java/com/xcs/spring/MyCustomDateEditor.java ================================================ package com.xcs.spring; import org.springframework.lang.Nullable; import java.beans.PropertyEditorSupport; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class MyCustomDateEditor extends PropertyEditorSupport { private final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-DD"); @Override public void setAsText(@Nullable String text) throws IllegalArgumentException { try { setValue(this.dateFormat.parse(text)); } catch (ParseException ex) { throw new IllegalArgumentException("Could not parse date: " + ex.getMessage(), ex); } } @Override public String getAsText() { Date value = (Date) getValue(); return (value != null ? this.dateFormat.format(value) : ""); } } ================================================ FILE: spring-dataops/spring-dataops-propertyEditor/src/main/java/com/xcs/spring/PropertyEditorDemo.java ================================================ package com.xcs.spring; import com.xcs.spring.bean.MyBean; import org.springframework.beans.BeanWrapperImpl; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.support.ResourceEditorRegistrar; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import java.util.Date; public class PropertyEditorDemo { public static void main(String[] args) { // 创建一个 BeanWrapperImpl 实例,用于操作 MyBean 类。 BeanWrapperImpl beanWrapper = new BeanWrapperImpl(MyBean.class); // 为 Date 类型的属性注册自定义的属性编辑器 MyCustomDateEditor。 beanWrapper.overrideDefaultEditor(Date.class, new MyCustomDateEditor()); // 设置 MyBean 类中名为 "date" 的属性值,使用字符串 "2023-12-5"。 // 这里会使用注册的 MyCustomDateEditor 来将字符串转换为 Date 对象。 beanWrapper.setPropertyValue("date", "2023-12-5"); // 设置 MyBean 类中名为 "path" 的属性值,使用字符串 "/opt/spring-reading"。 // 因为没有为 Path 类型注册特定的编辑器,所以使用默认转换逻辑。 beanWrapper.setPropertyValue("path", "/opt/spring-reading"); // 输出最终包装的 MyBean 实例。 System.out.println("MyBean = " + beanWrapper.getWrappedInstance()); } } ================================================ FILE: spring-dataops/spring-dataops-propertyEditor/src/main/java/com/xcs/spring/bean/MyBean.java ================================================ package com.xcs.spring.bean; import java.nio.file.Path; import java.util.Date; public class MyBean { private Path path; private Date date; public Path getPath() { return path; } public void setPath(Path path) { this.path = path; } public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } @Override public String toString() { return "MyBean{" + "path=" + path + ", date=" + date + '}'; } } ================================================ FILE: spring-dataops/spring-dataops-validator/README.md ================================================ ## Validator - [Validator](#validator) - [一、基本信息](#一基本信息) - [二、知识储备](#二知识储备) - [三、基本描述](#三基本描述) - [四、主要功能](#四主要功能) - [五、接口源码](#五接口源码) - [六、主要实现](#六主要实现) - [七、最佳实践](#七最佳实践) - [八、与其他组件的关系](#八与其他组件的关系) - [九、常见问题](#九常见问题) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、知识储备 1. **Spring MVC** + 了解 Spring MVC 是很重要的。这包括控制器(Controllers)、请求映射(RequestMapping)、视图解析(View Resolution)等。理解如何处理 HTTP 请求和响应,以及 Spring MVC 中的数据绑定和表单处理。 2. **Bean Validation(JSR 303/JSR 380)** + 虽然 `Validator` 接口是 Spring 的一部分,了解 JSR 303/JSR 380(Bean Validation API)也是有益的,因为 Spring 支持这些标准。了解常用的 Bean Validation 注解,如 `@NotNull`, `@Size`, `@Min`, `@Max` 等。 3. **错误处理** + 理解 Spring 中的错误处理机制,特别是与数据验证相关的,如 `Errors` 和 `BindingResult` 接口。 ### 三、基本描述 `Validator` 接口是 Spring 框架中用于实现对象数据验证的关键部分,它提供了一种标准化的方法来验证 Java 对象,特别是在处理用户输入和表单数据时。 ### 四、主要功能 1. **自定义数据验证** + 允许我们根据业务需求实现自定义的验证逻辑,用于检查对象属性是否符合特定的约束和条件。 2. **支持多种对象类型** + 通过 `supports(Class clazz)` 方法,验证器可以指定它能够验证哪些类型的对象,使得同一个验证器可以用于不同类型的数据模型。 3. **错误记录** + 在 `validate(Object target, Errors errors)` 方法中,验证器检查目标对象的属性,如果发现不符合要求的情况,可以将错误信息添加到 `Errors` 对象中,这些信息可用于错误展示和后续处理。 4. **与 Spring MVC 集成** + 在 Spring MVC 中,`Validator` 可以集成到控制器流程中,自动验证模型对象,常与 `@Valid` 注解一同使用,用于处理表单提交和请求参数的验证。 ### 五、接口源码 `Validator` 接口,是一个用于自定义对象验证的关键组件。这个接口包含两个主要方法:`supports(Class clazz)` 用于判断验证器是否支持特定类型的对象,通常通过检查对象是否为特定类或其子类来实现;`validate(Object target, Errors errors)` 用于执行实际的验证逻辑,其中 `target` 是待验证的对象,而 `errors` 用于记录验证中发现的错误。 ```java /** * 用于应用程序特定对象的验证器。 * *

此接口与任何基础设施或上下文完全脱离关系;也就是说,它不仅限于验证Web层、数据访问层或任何其他层的对象。因此,它适用于应用程序的任何层次,并支持将验证逻辑作为其本身的一等公民进行封装。 * *

下面是一个简单但完整的 {@code Validator} 实现示例,它验证了 {@code UserLogin} 实例的各种 {@link String} 属性不为空(即它们不是 {@code null} 并且不完全由空白字符组成),以及任何存在的密码至少是 {@code 'MINIMUM_PASSWORD_LENGTH'} 个字符长。 * *

 public class UserLoginValidator implements Validator {
 *
 *    private static final int MINIMUM_PASSWORD_LENGTH = 6;
 *
 *    public boolean supports(Class clazz) {
 *       return UserLogin.class.isAssignableFrom(clazz);
 *    }
 *
 *    public void validate(Object target, Errors errors) {
 *       ValidationUtils.rejectIfEmptyOrWhitespace(errors, "userName", "field.required");
 *       ValidationUtils.rejectIfEmptyOrWhitespace(errors, "password", "field.required");
 *       UserLogin login = (UserLogin) target;
 *       if (login.getPassword() != null
 *             && login.getPassword().trim().length() < MINIMUM_PASSWORD_LENGTH) {
 *          errors.rejectValue("password", "field.min.length",
 *                new Object[]{Integer.valueOf(MINIMUM_PASSWORD_LENGTH)},
 *                "The password must be at least [" + MINIMUM_PASSWORD_LENGTH + "] characters in length.");
 *       }
 *    }
 * }
* *

有关 {@code Validator} 接口及其在企业应用中的角色的更全面讨论,请参阅 Spring 参考手册。 * * @author Rod Johnson * @see SmartValidator * @see Errors * @see ValidationUtils */ public interface Validator { /** * 判断这个 Validator 是否能够验证提供的 clazz 类型的实例。 *

这个方法通常这样实现: *

return Foo.class.isAssignableFrom(clazz);
* (其中 Foo 是实际要被验证的对象实例的类或超类。) * @param clazz 这个 Validator 被询问是否能验证的 Class 类型 * @return 如果这个 Validator 能验证提供的 clazz 类型的实例,返回 {@code true} */ boolean supports(Class clazz); /** * 对提供的目标对象进行验证,该目标对象必须是 supports(Class) 方法通常会(或可能会)返回 true 的 Class 类型。 *

提供的 Errors 实例可以用来报告任何验证错误。 * @param target 需要被验证的对象 * @param errors 验证过程中的上下文状态 * @see ValidationUtils */ void validate(Object target, Errors errors); } ``` ### 六、主要实现 1. **LocalValidatorFactoryBean** - 这是一个桥接类,用于在 Spring 应用中集成 JSR-303/JSR-380 Bean Validation API。它实现了 `Validator` 接口,允许使用标准的 Bean Validation 注解对对象进行验证。 2. **SpringValidatorAdapter** - 这个类适配 JSR-303/JSR-380 标准验证器到 Spring 的 `Validator` 接口。它使得可以在 Spring 中使用标准的 Bean Validation API。 ### 七、最佳实践 使用 Spring 的 `Validator` 接口来验证 `Person` 对象的属性。程序创建了一个 `Person` 实例并设置了潜在的无效值,接着实例化了一个自定义的 `PersonValidator` 来进行验证。通过 `BeanPropertyBindingResult` 对象来存储和追踪验证过程中的错误,最后检查并打印出所有验证错误。 ```java public class ValidatorDemo { public static void main(String[] args) { // 创建一个 Person 对象实例 Person person = new Person(); person.setName(null); person.setAge(130); // 创建一个 BeanPropertyBindingResult 对象,用于存储验证过程中的错误 BeanPropertyBindingResult result = new BeanPropertyBindingResult(person, "person"); // 创建一个 PersonValidator 实例,这是自定义的验证器 PersonValidator validator = new PersonValidator(); // 检查 PersonValidator 是否支持 Person 类的验证 if (validator.supports(person.getClass())) { // 执行验证逻辑 validator.validate(person, result); } // 检查是否存在验证错误,并打印出所有的字段错误 if (result.hasErrors()) { for (FieldError error : result.getFieldErrors()) { System.out.println(error.getField() + ":" + error.getDefaultMessage()); } } } } ``` `PersonValidator` 类用于检查 `Person` 对象的 `name` 是否为空以及 `age` 是否在有效范围内。如果发现不符合规则的字段,将相应的错误信息记录到 `Errors` 对象中。 ```java public class PersonValidator implements Validator { @Override public boolean supports(Class clazz) { return Person.class.equals(clazz); } @Override public void validate(Object obj, Errors e) { // 检查名称是否为空 ValidationUtils.rejectIfEmpty(e, "name", "name.empty", "姓名不能为空"); // 将对象转换为 Person 类型 Person p = (Person) obj; // 检查年龄是否为负数 if (p.getAge() < 0) { e.rejectValue("age", "negative.value", "年龄不能为负数"); } // 检查年龄是否超过 120 岁 else if (p.getAge() > 120) { e.rejectValue("age", "too.darn.old", "目前年龄最大的是120岁"); } } } ``` 定义了一个简单的 Java 类 `Person`。 ```java public class Person { private String name; private int age; // get and set } ``` 运行结果发现, `PersonValidator` 正确地根据定义的验证规则识别出了 `Person` 对象的不合规数据,并通过 `Errors` 对象生成了相应的错误消息。 ``` name:姓名不能为空 age:目前年龄最大的是120岁 ``` ### 八、与其他组件的关系 + **Spring MVC框架** + `Validator` 接口的功能主要集中在处理和验证Web层的用户输入和模型数据。它与控制器紧密结合,用于在处理客户端请求(如表单提交)前验证模型数据。在数据绑定过程中,`Validator` 可用于检查绑定到模型对象的请求参数是否满足特定的验证规则。此外,Spring MVC 通过 `LocalValidatorFactoryBean` 支持与 JSR 303/JSR 380 Bean Validation 的集成,这允许我们在保持现有代码不变的情况下利用标准的 Bean Validation 注解。 ### 九、常见问题 1. **正确实现 `supports` 方法** - 确保 `supports(Class clazz)` 方法正确实现是常见的问题之一。这个方法需要正确判断验证器是否支持特定类型的对象,否则可能导致验证逻辑不被执行。 2. **逻辑的分离与国际化** - 我们在保持验证逻辑与业务逻辑分离的同时,需要确保验证逻辑完整且与业务规则相符。另外管理和显示验证错误信息,特别是在需要支持多语言环境下进行错误信息国际化的情况。 3. **集成与 JSR 303/JSR 380 Bean Validation** + 在使用 `Validator` 与 JSR 303/JSR 380 Bean Validation 标准共同工作时,了解二者之间的区别和如何有效集成是一个常见问题。 5. **嵌套属性和复杂对象的验证** + 当验证对象具有嵌套属性或结构复杂时,实现有效且高效的验证逻辑可能会更加困难。 ================================================ FILE: spring-dataops/spring-dataops-validator/pom.xml ================================================ spring-dataops com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-dataops-validator ================================================ FILE: spring-dataops/spring-dataops-validator/src/main/java/com/xcs/spring/Person.java ================================================ package com.xcs.spring; /** * @author xcs * @date 2023年12月04日 14时13分 **/ public class Person { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } ================================================ FILE: spring-dataops/spring-dataops-validator/src/main/java/com/xcs/spring/PersonValidator.java ================================================ package com.xcs.spring; import org.springframework.validation.Errors; import org.springframework.validation.ValidationUtils; import org.springframework.validation.Validator; /** * @author xcs * @date 2023年12月04日 14时13分 **/ public class PersonValidator implements Validator { @Override public boolean supports(Class clazz) { return Person.class.equals(clazz); } @Override public void validate(Object obj, Errors e) { // 检查名称是否为空 ValidationUtils.rejectIfEmpty(e, "name", "name.empty", "姓名不能为空"); // 将对象转换为 Person 类型 Person p = (Person) obj; // 检查年龄是否为负数 if (p.getAge() < 0) { e.rejectValue("age", "negative.value", "年龄不能为负数"); } // 检查年龄是否超过 120 岁 else if (p.getAge() > 120) { e.rejectValue("age", "too.darn.old", "目前年龄最大的是120岁"); } } } ================================================ FILE: spring-dataops/spring-dataops-validator/src/main/java/com/xcs/spring/ValidatorDemo.java ================================================ package com.xcs.spring; import org.springframework.validation.BeanPropertyBindingResult; import org.springframework.validation.FieldError; /** * @author xcs * @date 2023年12月04日 14时06分 **/ public class ValidatorDemo { public static void main(String[] args) { // 创建一个 Person 对象实例 Person person = new Person(); person.setName(null); person.setAge(130); // 创建一个 BeanPropertyBindingResult 对象,用于存储验证过程中的错误 BeanPropertyBindingResult result = new BeanPropertyBindingResult(person, "person"); // 创建一个 PersonValidator 实例,这是自定义的验证器 PersonValidator validator = new PersonValidator(); // 检查 PersonValidator 是否支持 Person 类的验证 if (validator.supports(person.getClass())) { // 执行验证逻辑 validator.validate(person, result); } // 检查是否存在验证错误,并打印出所有的字段错误 if (result.hasErrors()) { for (FieldError error : result.getFieldErrors()) { System.out.println(error.getField() + ":" + error.getDefaultMessage()); } } } } ================================================ FILE: spring-env/pom.xml ================================================ spring-reading com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-env pom spring-env-propertyResolver spring-env-environment spring-env-configurableEnvironment spring-env-configurablePropertyResolver spring-env-propertySource spring-env-propertySources ================================================ FILE: spring-env/spring-env-configurableEnvironment/README.md ================================================ ## ConfigurableEnvironment - [ConfigurableEnvironment](#configurableenvironment) - [一、基本信息](#一基本信息) - [二、知识储备](#二知识储备) - [三、基本描述](#三基本描述) - [四、主要功能](#四主要功能) - [五、接口源码](#五接口源码) - [六、主要实现](#六主要实现) - [七、最佳实践](#七最佳实践) - [八、与其他组件的关系](#八与其他组件的关系) - [九、常见问题](#九常见问题) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、知识储备 1. **PropertyResolver** + [PropertyResolver](/spring-env/spring-env-propertyResolver/README.md) 接口是 Spring 框架的一个核心组件,专注于提供一套灵活且强大的机制来处理应用程序配置属性。它定义了一系列方法,用于访问和操纵来自各种源(例如属性文件、环境变量、JVM 参数)的属性值。 2. **ConfigurablePropertyResolver** + [ConfigurablePropertyResolver](/spring-env/spring-env-configurablePropertyResolver/README.md) 接口在Spring中关键作用是提供灵活的配置属性解析。它能从多种源读取并转换属性值,支持占位符解析以增强配置的动态性。接口提供类型转换,确保属性值符合期望格式。它还允许检查属性存在性,并处理默认值,增加健壮性。 3. **Environment** + [Environment](/spring-env/spring-env-environment/README.md) 接口是 Spring 框架中的一个核心部分,它提供了一个统一的方式来访问各种外部化的配置数据,例如环境变量、JVM 系统属性、命令行参数、以及应用程序配置文件(如 properties 或 YAML 文件)。 ### 三、基本描述 `ConfigurableEnvironment` 是 Spring 框架中的一个核心接口,用于灵活地管理和访问应用程序的配置环境。它提供了统一的接口来处理来自不同来源(如属性文件、环境变量、命令行参数)的配置数据,并允许在运行时动态地添加、移除或修改这些属性源。 ### 四、主要功能 1. **属性源管理** + 管理和操作属性源(PropertySources),如添加、移除和重新排序属性源。这允许从不同来源(如属性文件、环境变量、命令行参数)灵活地配置和访问属性。 2. **配置文件激活** + 支持基于激活的配置文件。例如,可以根据不同的环境(开发、测试、生产)激活不同的配置文件,通过 `@Profile` 注解或设置 `spring.profiles.active` 来实现。 3. **属性解析** + 提供了解析属性值的功能,支持将配置值转换为各种数据类型(如字符串、数字、布尔值)。 4. **环境抽象** + 作为 Spring 环境的一部分,它提供了一个抽象层,用于统一处理不同来源的配置数据。 5. **属性覆盖和优先级** + 支持属性覆盖机制,允许某些配置(如环境变量)优先于其他配置(如应用属性文件)。 6. **动态属性修改** + 允许在应用运行时动态添加、移除或修改属性源,为应用提供了更大的灵活性和动态配置能力。 ### 五、接口源码 `ConfigurableEnvironment` 是 Spring 框架中用于环境配置的核心接口。它不仅提供了设置活动和默认配置文件的能力,还允许操作底层属性源,使得配置更灵活、更符合不同环境的需求。该接口支持添加、移除、重新排序或替换属性源,以及添加新的属性源。 ```java /** * 需要由大多数(如果不是所有){@link Environment} 类型实现的配置接口。 * 提供了设置活动和默认配置文件的设施,并操纵底层属性源。 * 通过 {@link ConfigurablePropertyResolver} 超接口,允许客户端设置和验证所需的属性,自定义转换服务等。 * *

操作属性源

*

可以移除、重新排序或替换属性源;并且可以使用从 {@link #getPropertySources()} 返回的 {@link MutablePropertySources} 实例添加额外的属性源。 * 以下示例针对 {@link StandardEnvironment} 的实现,但通常适用于任何实现,尽管特定的默认属性源可能有所不同。 * *

示例:添加具有最高搜索优先级的新属性源

*
 * ConfigurableEnvironment environment = new StandardEnvironment();
 * MutablePropertySources propertySources = environment.getPropertySources();
 * Map<String, String> myMap = new HashMap<>();
 * myMap.put("xyz", "myValue");
 * propertySources.addFirst(new MapPropertySource("MY_MAP", myMap));
 * 
* *

示例:移除默认的系统属性属性源

*
 * MutablePropertySources propertySources = environment.getPropertySources();
 * propertySources.remove(StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME)
 * 
* *

示例:出于测试目的模拟系统环境

*
 * MutablePropertySources propertySources = environment.getPropertySources();
 * MockPropertySource mockEnvVars = new MockPropertySource().withProperty("xyz", "myValue");
 * propertySources.replace(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, mockEnvVars);
 * 
* * 当 {@link Environment} 被 {@code ApplicationContext} 使用时,重要的是在上下文的 {@link * org.springframework.context.support.AbstractApplicationContext#refresh() refresh()} 方法调用之前执行任何此类 {@code PropertySource} 操作。 * 这确保了所有属性源在容器引导过程中都可用,包括由 {@linkplain * org.springframework.context.support.PropertySourcesPlaceholderConfigurer property * placeholder configurers} 使用。 * * @author Chris Beams * @since 3.1 * @see StandardEnvironment * @see org.springframework.context.ConfigurableApplicationContext#getEnvironment */ public interface ConfigurableEnvironment extends Environment, ConfigurablePropertyResolver { /** * 设置此 {@code Environment} 的活动配置文件集。配置文件在容器引导期间评估,以确定是否应向容器注册 bean 定义。 * 使用给定参数替换任何现有的活动配置文件;调用时不带参数以清除当前的活动配置文件集。 * 使用 {@link #addActiveProfile} 添加配置文件,同时保留现有集合。 * @throws IllegalArgumentException 如果任何配置文件为 null、为空或仅为空格 */ void setActiveProfiles(String... profiles); /** * 向当前活动配置文件集添加一个配置文件。 * @throws IllegalArgumentException 如果配置文件为 null、为空或仅为空格 */ void addActiveProfile(String profile); /** * 指定默认情况下激活的配置文件集,如果没有通过 {@link #setActiveProfiles} 明确激活其他配置文件。 * @throws IllegalArgumentException 如果任何配置文件为 null、为空或仅为空格 */ void setDefaultProfiles(String... profiles); /** * 返回此 {@code Environment} 的 {@link PropertySources},以可变形式,允许操纵应在解析此 {@code Environment} 对象时搜索的 {@link PropertySource} 对象集。 * {@link MutablePropertySources} 的各种方法(如 {@link MutablePropertySources#addFirst addFirst}、 * {@link MutablePropertySources#addLast addLast}、{@link MutablePropertySources#addBefore addBefore} 和 * {@link MutablePropertySources#addAfter addAfter})允许对属性源排序进行精细控制。 */ MutablePropertySources getPropertySources(); /** * 返回当前 {@link SecurityManager} 允许的情况下 {@link System#getProperties()} 的值,否则返回一个尝试使用 {@link System#getProperty(String)} 调用访问各个键的映射实现。 */ Map getSystemProperties(); /** * 返回当前 {@link SecurityManager} 允许的情况下 {@link System#getenv()} 的值,否则返回一个尝试使用 {@link System#getenv(String)} 调用访问各个键的映射实现。 */ Map getSystemEnvironment(); /** * 将给定父环境的活动配置文件、默认配置文件和属性源追加到此(子)环境的各自集合中。 * 对于父子中均存在的同名 {@code PropertySource} 实例,保留子实例并丢弃父实例。 * 这有助于允许子环境重写属性源,同时避免通过公共属性源类型进行冗余搜索,例如系统环境和系统属性。 * 活动和默认配置文件名也进行了过滤,以避免混淆和冗余存储。 * 父环境在任何情况下均保持不变。请注意,调用 {@code merge} 后对父环境进行的任何更改将不会反映在子环境中。 * 因此,在调用 {@code merge} 之前应注意配置父属性源和配置文件信息。 */ void merge(ConfigurableEnvironment parent); } ``` ### 六、主要实现 1. **AbstractEnvironment** - `Environment` 接口的抽象基类,提供了共通的实现机制,供其他具体实现类继承。 2. **StandardEnvironment** - 通用实现,处理系统属性和环境变量,适用于大多数标准应用程序。 3. **StandardServletEnvironment** - 针对 Servlet-based Web 应用程序,增加对 Servlet 上下文和配置参数的支持。 ### 七、最佳实践 使用 `ConfigurableEnvironment` 来管理和操作应用程序的配置环境,包括激活特定的配置文件,获取和合并系统属性和环境变量,以及获取和操作属性源。 ```java public class ConfigurableEnvironmentDemo { public static void main(String[] args) { // 创建 StandardEnvironment 实例,用于访问属性和配置文件信息 ConfigurableEnvironment environment = new StandardEnvironment(); // 设置配置文件 environment.setActiveProfiles("dev"); System.out.println("Active Profiles: " + String.join(", ", environment.getActiveProfiles())); // 添加配置文件 environment.addActiveProfile("test"); System.out.println("Updated Active Profiles: " + String.join(", ", environment.getActiveProfiles())); // 设置默认配置文件 environment.setDefaultProfiles("default"); System.out.println("Default Profiles: " + String.join(", ", environment.getDefaultProfiles())); // 获取系统属性 Map systemProperties = environment.getSystemProperties(); System.out.println("System Properties: " + systemProperties); // 获取系统环境变量 Map systemEnvironment = environment.getSystemEnvironment(); System.out.println("System Environment: " + systemEnvironment); // 合并环境变量 Map properties = new HashMap<>(); properties.put("app.name", "Spring-Reading"); properties.put("app.version", "1.0.0"); StandardEnvironment standardEnvironment = new StandardEnvironment(); standardEnvironment.getPropertySources().addFirst(new MapPropertySource("myEnvironment", properties)); environment.merge(standardEnvironment); // 获取可变属性源 MutablePropertySources propertySources = environment.getPropertySources(); System.out.println("MutablePropertySources: " + propertySources); } } ``` 运行结果发现,`ConfigurableEnvironment` 提供了强大的功能来管理和访问应用程序的配置数据。通过设置活动和默认配置文件,获取系统属性和环境变量,以及操作可变属性源,我们可以根据不同的需求和环境灵活配置应用程序。 ```java Active Profiles: dev Updated Active Profiles: dev, test Default Profiles: default System Properties: {sun.desktop=windows, awt.toolkit=sun.awt.windows.WToolkit, java.specification.version=11, sun.cpu.isalist=amd64, sun.jnu.encoding=GBK, java.class.path=D:\idea-work-space-xcs\spring-reading\spring-env\spring-env-configurableEnvironment\target\classes;D:\tools\repository\org\springframework\spring-context\5.3.10\spring-context-5.3.10.jar;D:\tools\repository\org\springframework\spring-beans\5.3.10\spring-beans-5.3.10.jar;D:\tools\repository\org\springframework\spring-core\5.3.10\spring-core-5.3.10.jar;D:\tools\repository\org\springframework\spring-jcl\5.3.10\spring-jcl-5.3.10.jar;D:\tools\repository\org\springframework\spring-expression\5.3.10\spring-expression-5.3.10.jar;D:\tools\repository\org\springframework\spring-aspects\5.3.10\spring-aspects-5.3.10.jar;D:\tools\repository\org\aspectj\aspectjweaver\1.9.7\aspectjweaver-1.9.7.jar;D:\tools\repository\org\springframework\spring-aop\5.3.10\spring-aop-5.3.10.jar;D:\tools\repository\org\springframework\spring-tx\5.3.10\spring-tx-5.3.10.jar;D:\tools\repository\org\springframework\spring-webmvc\5.3.10\spring-webmvc-5.3.10.jar;D:\tools\repository\org\springframework\spring-web\5.3.10\spring-web-5.3.10.jar, java.vm.vendor=Oracle Corporation, sun.arch.data.model=64, user.variant=, java.vendor.url=http://java.oracle.com/, user.timezone=, os.name=Windows 10, java.vm.specification.version=11, sun.java.launcher=SUN_STANDARD, user.country=CN, sun.boot.library.path=D:\install\jdk-11\bin, sun.java.command=com.xcs.spring.ConfigurableEnvironmentDemo, jdk.debug=release, sun.cpu.endian=little, user.home=C:\Users\Lenovo, user.language=zh, java.specification.vendor=Oracle Corporation, java.version.date=2018-09-25, java.home=D:\install\jdk-11, file.separator=\, java.vm.compressedOopsMode=Zero based, line.separator= , java.specification.name=Java Platform API Specification, java.vm.specification.vendor=Oracle Corporation, java.awt.graphicsenv=sun.awt.Win32GraphicsEnvironment, user.script=, sun.management.compiler=HotSpot 64-Bit Tiered Compilers, java.runtime.version=11+28, user.name=Lenovo, path.separator=;, os.version=10.0, java.runtime.name=Java(TM) SE Runtime Environment, file.encoding=UTF-8, java.vm.name=Java HotSpot(TM) 64-Bit Server VM, java.vendor.version=18.9, java.vendor.url.bug=http://bugreport.java.com/bugreport/, java.io.tmpdir=C:\Users\Lenovo\AppData\Local\Temp\, java.version=11, user.dir=D:\idea-work-space-xcs\spring-reading, os.arch=amd64, java.vm.specification.name=Java Virtual Machine Specification, java.awt.printerjob=sun.awt.windows.WPrinterJob, sun.os.patch.level=, java.library.path=D:\install\jdk-11\bin;C:\WINDOWS\Sun\Java\bin;C:\WINDOWS\system32;C:\WINDOWS;D:\app\Lenovo\product\11.2.0\dbhome_1\bin;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\WINDOWS\System32\OpenSSH\;D:\install\Git\cmd;D:\install\jdk\bin;D:\install\jdk\jre\bin;C:\ProgramData\Microsoft\Windows\Start Menu\Programs\MySQL\MySQL Server 8.0\bin;D:\tools\apache-maven-3.8.4-bin\apache-maven-3.8.4\bin;D:\tools\apache-maven-3.8.4-bin\apache-maven-3.8.4\bin;D:\install\go\bin;D:\install\Xshell\;D:\install\Xftp\;C:\Program Files\dotnet\;C:\Program Files\Microsoft SQL Server\Client SDK\ODBC\170\Tools\Binn\;C:\Program Files\Microsoft SQL Server\150\Tools\Binn\;D:\tools\x86_64-8.1.0-release-win32-seh-rt_v6-rev0\mingw64\bin;;C:\Program Files\Docker\Docker\resources\bin;C:\Program Files\nodejs\;C:\Users\Lenovo\AppData\Local\Microsoft\WindowsApps;;C:\Users\Lenovo\AppData\Local\Programs\Fiddler;C:\WINDOWS\system32\config\systemprofile\go\bin;C:\WINDOWS\system32\config\systemprofile\AppData\Local\Microsoft\WindowsApps;D:\install\Microsoft VS Code\bin;C:\WINDOWS\system32\config\systemprofile\.dotnet\tools;D:\install\lens\resources\cli\bin;C:\Users\Lenovo\AppData\Roaming\npm;., java.vendor=Oracle Corporation, java.vm.info=mixed mode, java.vm.version=11+28, sun.io.unicode.encoding=UnicodeLittle, java.class.version=55.0} System Environment: {USERDOMAIN_ROAMINGPROFILE=DESKTOP-HRS3987, PROCESSOR_LEVEL=6, SESSIONNAME=Console, ALLUSERSPROFILE=C:\ProgramData, PROCESSOR_ARCHITECTURE=AMD64, PSModulePath=C:\Program Files\WindowsPowerShell\Modules;C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules, SystemDrive=C:, MAVEN_HOME=D:\tools\apache-maven-3.8.4-bin\apache-maven-3.8.4, USERNAME=Lenovo, ProgramFiles(x86)=C:\Program Files (x86), FPS_BROWSER_USER_PROFILE_STRING=Default, PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC, DriverData=C:\Windows\System32\Drivers\DriverData, OneDriveConsumer=C:\Users\Lenovo\OneDrive, GOPATH=C:\Users\Lenovo\go, ProgramData=C:\ProgramData, ProgramW6432=C:\Program Files, HOMEPATH=\Users\Lenovo, MYSQL_HOME=C:\ProgramData\Microsoft\Windows\Start Menu\Programs\MySQL\MySQL Server 8.0, PROCESSOR_IDENTIFIER=Intel64 Family 6 Model 165 Stepping 3, GenuineIntel, M2_HOME=D:\tools\apache-maven-3.8.4-bin\apache-maven-3.8.4, ProgramFiles=C:\Program Files, PUBLIC=C:\Users\Public, windir=C:\WINDOWS, =::=::\, ZES_ENABLE_SYSMAN=1, LOCALAPPDATA=C:\Users\Lenovo\AppData\Local, ChocolateyLastPathUpdate=133456990830913519, USERDOMAIN=DESKTOP-HRS3987, FPS_BROWSER_APP_PROFILE_STRING=Internet Explorer, LOGONSERVER=\\DESKTOP-HRS3987, JAVA_HOME=D:\install\jdk, EFC_9756=1, OneDrive=C:\Users\Lenovo\OneDrive, APPDATA=C:\Users\Lenovo\AppData\Roaming, ChocolateyInstall=C:\ProgramData\chocolatey, CommonProgramFiles=C:\Program Files\Common Files, Path=D:\app\Lenovo\product\11.2.0\dbhome_1\bin;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\WINDOWS\System32\OpenSSH\;D:\install\Git\cmd;D:\install\jdk\bin;D:\install\jdk\jre\bin;C:\ProgramData\Microsoft\Windows\Start Menu\Programs\MySQL\MySQL Server 8.0\bin;D:\tools\apache-maven-3.8.4-bin\apache-maven-3.8.4\bin;D:\tools\apache-maven-3.8.4-bin\apache-maven-3.8.4\bin;D:\install\go\bin;D:\install\Xshell\;D:\install\Xftp\;C:\Program Files\dotnet\;C:\Program Files\Microsoft SQL Server\Client SDK\ODBC\170\Tools\Binn\;C:\Program Files\Microsoft SQL Server\150\Tools\Binn\;D:\tools\x86_64-8.1.0-release-win32-seh-rt_v6-rev0\mingw64\bin;;C:\Program Files\Docker\Docker\resources\bin;C:\Program Files\nodejs\;C:\Users\Lenovo\AppData\Local\Microsoft\WindowsApps;;C:\Users\Lenovo\AppData\Local\Programs\Fiddler;C:\WINDOWS\system32\config\systemprofile\go\bin;C:\WINDOWS\system32\config\systemprofile\AppData\Local\Microsoft\WindowsApps;D:\install\Microsoft VS Code\bin;C:\WINDOWS\system32\config\systemprofile\.dotnet\tools;D:\install\lens\resources\cli\bin;C:\Users\Lenovo\AppData\Roaming\npm, OS=Windows_NT, COMPUTERNAME=DESKTOP-HRS3987, PROCESSOR_REVISION=a503, CLASSPATH=.;D:\install\jdk\lib\dt.jar;D:\install\jdk\lib\tools.jar;, CommonProgramW6432=C:\Program Files\Common Files, ComSpec=C:\WINDOWS\system32\cmd.exe, SystemRoot=C:\WINDOWS, TEMP=C:\Users\Lenovo\AppData\Local\Temp, HOMEDRIVE=C:, USERPROFILE=C:\Users\Lenovo, TMP=C:\Users\Lenovo\AppData\Local\Temp, CommonProgramFiles(x86)=C:\Program Files (x86)\Common Files, NUMBER_OF_PROCESSORS=12, IDEA_INITIAL_DIRECTORY=D:\tools\ideaIU-2021.2.2.win\bin} MutablePropertySources: [PropertiesPropertySource {name='systemProperties'}, SystemEnvironmentPropertySource {name='systemEnvironment'}, MapPropertySource {name='myEnvironment'}] ``` ### 八、与其他组件的关系 1. **ApplicationContext** + 在 Spring 的 `ApplicationContext` (应用上下文)中,`ConfigurableEnvironment` 被用来加载和管理应用程序的配置数据。`ApplicationContext` 使用 `Environment` 来解析配置文件和属性,支持基于不同环境(开发、测试、生产)的配置。 2. **PropertySource** + `ConfigurableEnvironment` 与 `PropertySource` 对象紧密相关。`PropertySources` 是属性源的集合,它们定义了配置数据的来源,如系统环境变量、JVM 属性、配置文件等。`ConfigurableEnvironment` 允许开发者添加、移除或自定义这些属性源。 3. **Profile** + `ConfigurableEnvironment` 管理 Spring 配置文件(Profiles),使得可以根据当前环境(例如开发、测试、生产)激活或停用特定的 bean 定义。这在构建具有不同配置需求的多环境应用时非常有用。 4. **PropertyResolver** + `ConfigurableEnvironment` 继承自 `ConfigurablePropertyResolver`,这使它能够解析属性值,包括从属性源中查找属性和将属性值转换为各种数据类型。 5. **BeanFactory** + 在 Spring 的 Bean 生命周期中,`Environment` 用于影响 bean 的创建和配置。例如,通过 `@Value` 注解可以将环境属性注入到 bean 中,或者通过 `@Conditional` 注解根据环境条件来决定是否创建某个 bean。 6. **PropertySourcesPlaceholderConfigurer** + 这是一个特殊的 bean,用于解析配置文件中的占位符。它与 `Environment` 接口协同工作,允许在定义 bean 时使用环境属性。 7. **Spring Boot** + 在 Spring Boot 应用中,`ConfigurableEnvironment` 的作用更为突出,因为它支持各种外部化配置和复杂的配置顺序,包括对配置文件的灵活处理和条件化配置。 ### 九、常见问题 1. **属性值不被正确读取或解析** + 如果在使用 `environment.getProperty()` 获取属性值时返回 `null` 或不正确的值,首先确保属性名称正确无误,并检查属性源是否已被正确添加到 `ConfigurableEnvironment` 中。此外,检查属性源的添加顺序,因为这可能导致预期外的属性覆盖。 2. **激活的配置文件不生效** + 如果设置了活动配置文件(Profiles)但应用没有使用这些配置,应该确保在 Spring 容器完全启动之前设置活动配置文件。在某些情况下,可能需要在应用启动参数中显式指定活动的配置文件。 3. **环境合并时出现问题** + 当使用 `environment.merge()` 方法合并两个环境时,如果出现属性值不一致或丢失,仔细检查合并逻辑,并理解父子环境中相同属性源的覆盖规则,以确保合并后的环境符合预期。 4. **配置文件(Profiles)之间的冲突** + 当多个配置文件被激活时,可能因为不同配置文件中定义了相同的 bean 而出现一些意料之外的行为。解决这个问题的方法是,明确配置文件间的优先级和作用域,尽量减少不同配置文件间的冲突,并合理组织配置文件以避免重复定义。 5. **属性源顺序导致的问题** + 属性源的添加顺序可能导致期望的属性值被其他源中的同名属性覆盖。解决这个问题的方法是调整属性源的添加顺序,确保高优先级的属性源被优先考虑。 ================================================ FILE: spring-env/spring-env-configurableEnvironment/pom.xml ================================================ spring-env com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-env-configurableEnvironment ================================================ FILE: spring-env/spring-env-configurableEnvironment/src/main/java/com/xcs/spring/ConfigurableEnvironmentDemo.java ================================================ package com.xcs.spring; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.MutablePropertySources; import org.springframework.core.env.StandardEnvironment; import java.util.HashMap; import java.util.Map; /** * @author xcs * @date 2023年12月01日 14时05分 **/ public class ConfigurableEnvironmentDemo { public static void main(String[] args) { // 创建 StandardEnvironment 实例,用于访问属性和配置文件信息 ConfigurableEnvironment environment = new StandardEnvironment(); // 设置配置文件 environment.setActiveProfiles("dev"); System.out.println("Active Profiles: " + String.join(", ", environment.getActiveProfiles())); // 添加配置文件 environment.addActiveProfile("test"); System.out.println("Updated Active Profiles: " + String.join(", ", environment.getActiveProfiles())); // 设置默认配置文件 environment.setDefaultProfiles("default"); System.out.println("Default Profiles: " + String.join(", ", environment.getDefaultProfiles())); // 获取系统属性 Map systemProperties = environment.getSystemProperties(); System.out.println("System Properties: " + systemProperties); // 获取系统环境变量 Map systemEnvironment = environment.getSystemEnvironment(); System.out.println("System Environment: " + systemEnvironment); // 合并环境变量 Map properties = new HashMap<>(); properties.put("app.name", "Spring-Reading"); properties.put("app.version", "1.0.0"); StandardEnvironment standardEnvironment = new StandardEnvironment(); standardEnvironment.getPropertySources().addFirst(new MapPropertySource("myEnvironment", properties)); environment.merge(standardEnvironment); // 获取可变属性源 MutablePropertySources propertySources = environment.getPropertySources(); System.out.println("MutablePropertySources: " + propertySources); } } ================================================ FILE: spring-env/spring-env-configurablePropertyResolver/README.md ================================================ ## ConfigurablePropertyResolver - [ConfigurablePropertyResolver](#configurablepropertyresolver) - [一、基本信息](#一基本信息) - [二、知识储备](#二知识储备) - [三、基本描述](#三基本描述) - [四、主要功能](#四主要功能) - [五、接口源码](#五接口源码) - [六、主要实现](#六主要实现) - [七、最佳实践](#七最佳实践) - [八、与其他组件的关系](#八与其他组件的关系) - [九、常见问题](#九常见问题) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、知识储备 1. **PropertyResolver** + [PropertyResolver](/spring-env/spring-env-propertyResolver/README.md) 接口是 Spring 框架的一个核心组件,专注于提供一套灵活且强大的机制来处理应用程序配置属性。它定义了一系列方法,用于访问和操纵来自各种源(例如属性文件、环境变量、JVM 参数)的属性值。 ### 三、基本描述 `ConfigurablePropertyResolver` 接口在Spring中关键作用是提供灵活的配置属性解析。它能从多种源读取并转换属性值,支持占位符解析以增强配置的动态性。接口提供类型转换,确保属性值符合期望格式。它还允许检查属性存在性,并处理默认值,增加健壮性。 ### 四、主要功能 1. **属性值解析** + 从各种属性源(如配置文件、环境变量等)中读取并解析属性值。 2. **占位符解析** + 支持在属性值中使用占位符(如`${property.name}`),并解析这些占位符为实际的配置值,提升配置的动态性和灵活性。 3. **类型转换** + 提供了将属性值从一种类型转换为另一种类型的功能,例如,从字符串转换为整数、布尔值等。 4. **默认值处理** + 允许为属性指定默认值,这在配置项可能缺失的情况下特别有用。 5. **属性存在性检查** + 提供方法来检查特定属性是否存在,这有助于在配置项可能缺失时采取相应的措施。 6. **属性源管理** + 管理不同的属性源,使得可以根据当前应用程序的运行环境灵活调整配置。 ### 五、接口源码 `ConfigurablePropertyResolver` 接口提供了一系列用于管理和处理配置属性的方法。允许自定义属性值的类型转换服务,管理占位符的前缀和后缀,处理默认值分隔符,以及设置是否忽略无法解析的嵌套占位符。 ```java /** * 配置接口,大多数 {@link PropertyResolver} 类型都应实现此接口。 * 提供访问和自定义在属性值类型转换时使用的 {@link org.springframework.core.convert.ConversionService ConversionService} 的功能。 * * @author Chris Beams * @since 3.1 */ public interface ConfigurablePropertyResolver extends PropertyResolver { /** * 返回用于执行属性类型转换时的 {@link ConfigurableConversionService}。 * 可配置的转换服务允许方便地添加和移除单个 {@code Converter} 实例。 * * @return 当前使用的转换服务 */ ConfigurableConversionService getConversionService(); /** * 设置在执行属性类型转换时使用的 {@link ConfigurableConversionService}。 * 注意:作为完全替换 ConversionService 的替代方案,考虑通过 getConversionService() 添加或移除单个 {@code Converter} 实例。 */ void setConversionService(ConfigurableConversionService conversionService); /** * 设置该解析器替换的占位符的前缀。 */ void setPlaceholderPrefix(String placeholderPrefix); /** * 设置该解析器替换的占位符的后缀。 */ void setPlaceholderSuffix(String placeholderSuffix); /** * 指定分隔该解析器替换的占位符及其关联默认值的字符。 * 如果不处理特殊字符作为值分隔符,则为 {@code null}。 */ void setValueSeparator(@Nullable String valueSeparator); /** * 设置当遇到无法解析的嵌套占位符时是否抛出异常。 * {@code false} 表示严格解析,即会抛出异常。 * {@code true} 表示未解析的嵌套占位符应以其未解析的 ${...} 形式通过。 */ void setIgnoreUnresolvableNestedPlaceholders(boolean ignoreUnresolvableNestedPlaceholders); /** * 指定必须存在的属性,通过 {@link #validateRequiredProperties()} 方法进行验证。 */ void setRequiredProperties(String... requiredProperties); /** * 验证通过 {@link #setRequiredProperties} 设置的每个属性是否存在并解析为非 {@code null} 值。 * 如果任何必需的属性无法解析,则抛出 MissingRequiredPropertiesException 异常。 */ void validateRequiredProperties() throws MissingRequiredPropertiesException; } ``` ### 六、主要实现 1. **`AbstractPropertyResolver`** + 这是一个抽象基类,为 `ConfigurablePropertyResolver` 接口的大部分通用实现提供了基础。它实现了大多数方法,但作为一个抽象类,不能直接实例化。 2. **`PropertySourcesPropertyResolver`** + 这个类是 `ConfigurablePropertyResolver` 的一个具体实现,它主要用于处理基于 `PropertySources` 的属性解析。`PropertySources` 是 Spring 环境抽象中的一部分,用于表示配置数据的源,如环境变量、JVM属性、配置文件等。 ### 七、最佳实践 使用 Spring 的 `ConfigurablePropertyResolver` 接口来管理和解析配置属性。我们首先创建并配置了属性源,接着实例化了 `PropertySourcesPropertyResolver` 作为属性解析器。在此基础上,代码设置了属性值的转换服务、定义了占位符的前后缀、配置了默认值分隔符,并处理了未解析占位符的情况。此外,还指定并验证了必需的属性,最后读取并输出了配置属性值。 ```java public class ConfigurablePropertyResolverDemo { public static void main(String[] args) { // 创建属性源 Map properties = new HashMap<>(); properties.put("app.name", "Spring-Reading"); properties.put("app.version", "1.0.0"); MapPropertySource propertySource = new MapPropertySource("myPropertySource", properties); MutablePropertySources propertySources = new MutablePropertySources(); propertySources.addLast(propertySource); // 创建 ConfigurablePropertyResolver PropertySourcesPropertyResolver propertyResolver = new PropertySourcesPropertyResolver(propertySources); // 设置和获取转换服务 ConfigurableConversionService conversionService = new DefaultConversionService(); propertyResolver.setConversionService(conversionService); // 设置占位符前后缀 propertyResolver.setPlaceholderPrefix("${"); propertyResolver.setPlaceholderSuffix("}"); // 设置默认值分隔符 propertyResolver.setValueSeparator(":"); // 设置未解析占位符的处理方式 propertyResolver.setIgnoreUnresolvableNestedPlaceholders(true); // 设置并验证必需的属性 propertyResolver.setRequiredProperties("app.name", "app.version"); propertyResolver.validateRequiredProperties(); // 读取属性 String appName = propertyResolver.getProperty("app.name"); String appVersion = propertyResolver.getProperty("app.version", String.class, "Unknown Version"); System.out.println("获取属性 app.name: " + appName); System.out.println("获取属性 app.version: " + appVersion); } } ``` 运行结果发现,`PropertySourcesPropertyResolver` 能够正确地从给定的属性源中解析出属性值,并且代码中的属性源配置和属性解析器的使用是正确的。 ```java 获取属性 app.name: Spring-Reading 获取属性 app.version: 1.0.0 ``` ### 八、与其他组件的关系 1. **`Environment` ** - `Environment` 接口继承自 `PropertyResolver`,而 `ConfigurableEnvironment` 是 `Environment` 的子接口,提供了对属性解析器的配置能力。在 Spring 中,`Environment` 用于封装所有的配置属性,包括系统属性、环境变量和应用配置文件。 - `ConfigurablePropertyResolver` 提供了更灵活的属性解析和转换功能,它允许自定义属性解析的行为,如设置占位符的前后缀、类型转换等。 2. **`PropertySources` ** - `PropertySources` 是一个包含多个 `PropertySource` 对象的容器,代表不同的属性源,如配置文件、环境变量、命令行参数等。 - `ConfigurablePropertyResolver` 通常与 `PropertySources` 一起使用,用于从这些属性源中解析属性值。 3. **`PropertySource` ** - `PropertySource` 是单个属性源的抽象,如一个配置文件或环境变量集。 - `ConfigurablePropertyResolver` 使用 `PropertySource`(通过 `PropertySources`)来获取具体的属性值。 4. **`ConversionService` ** - `ConversionService` 在 Spring 中用于类型转换,将一种类型的值转换为另一种类型。 - `ConfigurablePropertyResolver` 可以配置自定义的 `ConversionService`,以控制属性值的转换方式,这在处理非字符串类型的配置属性时特别有用。 5. **`Profile` 和 `@Conditional` 注解** - Spring 中的配置文件和组件可以根据不同的环境(开发、测试、生产等)激活不同的配置。 - `ConfigurablePropertyResolver` 可以用来检查哪些配置文件或 `Profile` 被激活,以及确定哪些条件注解(如 `@Conditional`)应该满足。 ### 九、常见问题 1. **占位符无法解析** + 如果在属性值中使用的占位符(如 `${property.name}`)无法正确解析,应确保占位符的前缀和后缀设置正确,并且相关属性已在属性源中定义。 2. **类型转换错误** + 当遇到类型转换异常时,检查是否为 `ConfigurablePropertyResolver` 设置了正确的 `ConversionService` 并注册了必要的类型转换器。 3. **属性值覆盖问题** + 如果多个属性源包含相同名称的属性可能导致属性值覆盖,需要了解并管理属性源的优先级,确保以正确的顺序将它们添加到环境中。 4. **处理默认值时的混淆** + 使用带有默认值的方法(如 `getProperty(String, Class, T defaultValue)`)时,要清楚地区分属性源中的空值和未定义的属性,避免混淆。 5. **必需属性缺失** + 使用 `validateRequiredProperties` 方法时,如果因为缺少必需属性而抛出异常,应在应用启动或配置阶段检查并确保所有必需的属性都已在属性源中定义。 6. **环境和配置不一致** + 在不同环境(开发、测试、生产)下遇到配置不一致的问题,可以通过使用 Spring Profiles 来管理不同环境的配置,并确保每个环境都有适当的配置文件和属性设置。 7. **并发问题** + 在多线程环境下使用 `ConfigurablePropertyResolver` 时,为了避免线程安全问题,应保证对属性源的访问是线程安全的,或在应用启动时预先解析所有必需的属性。 ================================================ FILE: spring-env/spring-env-configurablePropertyResolver/pom.xml ================================================ spring-env com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-env-configurablePropertyResolver ================================================ FILE: spring-env/spring-env-configurablePropertyResolver/src/main/java/com/xcs/spring/ConfigurablePropertyResolverDemo.java ================================================ package com.xcs.spring; import org.springframework.core.convert.support.ConfigurableConversionService; import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.MutablePropertySources; import org.springframework.core.env.PropertySourcesPropertyResolver; import java.util.HashMap; import java.util.Map; /** * @author xcs * @date 2023年11月30日 16时40分 **/ public class ConfigurablePropertyResolverDemo { public static void main(String[] args) { // 创建属性源 Map properties = new HashMap<>(); properties.put("app.name", "Spring-Reading"); properties.put("app.version", "1.0.0"); MapPropertySource propertySource = new MapPropertySource("myPropertySource", properties); MutablePropertySources propertySources = new MutablePropertySources(); propertySources.addLast(propertySource); // 创建 ConfigurablePropertyResolver PropertySourcesPropertyResolver propertyResolver = new PropertySourcesPropertyResolver(propertySources); // 设置和获取转换服务 ConfigurableConversionService conversionService = new DefaultConversionService(); propertyResolver.setConversionService(conversionService); // 设置占位符前后缀 propertyResolver.setPlaceholderPrefix("${"); propertyResolver.setPlaceholderSuffix("}"); // 设置默认值分隔符 propertyResolver.setValueSeparator(":"); // 设置未解析占位符的处理方式 propertyResolver.setIgnoreUnresolvableNestedPlaceholders(true); // 设置并验证必需的属性 propertyResolver.setRequiredProperties("app.name", "app.version"); propertyResolver.validateRequiredProperties(); // 读取属性 String appName = propertyResolver.getProperty("app.name"); String appVersion = propertyResolver.getProperty("app.version", String.class, "Unknown Version"); System.out.println("获取属性 app.name: " + appName); System.out.println("获取属性 app.version: " + appVersion); } } ================================================ FILE: spring-env/spring-env-environment/README.md ================================================ ## Environment - [Environment](#environment) - [一、基本信息](#一基本信息) - [二、知识储备](#二知识储备) - [三、基本描述](#三基本描述) - [四、主要功能](#四主要功能) - [五、接口源码](#五接口源码) - [六、主要实现](#六主要实现) - [七、最佳实践](#七最佳实践) - [八、与其他组件的关系](#八与其他组件的关系) - [九、常见问题](#九常见问题) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、知识储备 1. **PropertyResolver** + [PropertyResolver](/spring-env/spring-env-propertyResolver/README.md) 接口是 Spring 框架的一个核心组件,专注于提供一套灵活且强大的机制来处理应用程序配置属性。它定义了一系列方法,用于访问和操纵来自各种源(例如属性文件、环境变量、JVM 参数)的属性值。 2. **ConfigurablePropertyResolver** + [ConfigurablePropertyResolver](/spring-env/spring-env-configurablePropertyResolver/README.md) 接口在Spring中关键作用是提供灵活的配置属性解析。它能从多种源读取并转换属性值,支持占位符解析以增强配置的动态性。接口提供类型转换,确保属性值符合期望格式。它还允许检查属性存在性,并处理默认值,增加健壮性。 ### 三、基本描述 `Environment` 接口是 Spring 框架中的一个核心部分,它提供了一个统一的方式来访问各种外部化的配置数据,例如环境变量、JVM 系统属性、命令行参数、以及应用程序配置文件(如 properties 或 YAML 文件)。这个接口允许我们在运行时方便地获取和操作这些配置数据,同时也支持配置文件(Profiles)的概念,这使得在不同环境(如开发、测试、生产)下进行条件性的配置变得简单。通过 `Environment`,我们可以查询和操作属性值,检查属性的存在,以及处理多个属性源。 ### 四、主要功能 1. **属性访问** + 它允许从不同的属性源(如环境变量、系统属性、配置文件等)访问属性值。提供了方法来检索字符串类型或类型安全的属性值。 2. **属性源管理** + `Environment` 抽象支持多个属性源,并按优先级顺序检索属性。这意味着可以从多个地方(如不同的配置文件和环境变量)加载属性,并根据需要覆盖它们。 3. **配置文件(Profiles)** + 支持配置文件,这是一种用于根据不同环境(如开发、测试、生产)来分隔配置的机制。它允许应用程序根据当前激活的配置文件来调整其行为。 4. **属性存在性检查** + 提供了方法来检查是否存在特定的属性,这有助于在属性可能不存在的情况下编写更健壮的代码。 5. **类型安全的属性访问** + 除了普通的属性访问方法外,`Environment` 还提供了类型安全的方法,允许直接将属性值转换为期望的类型。 6. **必需属性的访问** + 可以获取标记为必需的属性,如果这些属性不存在,将抛出异常,这有助于在启动时捕捉配置错误。 ### 五、接口源码 `Environment` 接口是 Spring 应用程序环境的主要表现形式,重点关注两个方面:配置文件(profiles)和属性(properties)。配置文件是对一组逻辑分组的 Bean 定义的命名,只有在相应的配置文件被激活时才会注册这些 Bean。属性则是来自多种来源的配置数据,如文件、环境变量等。`Environment` 提供了一系列方法来激活和管理这些配置文件,以及方便地访问和解析这些属性。 ```java /** * 代表当前应用程序运行环境的接口。 * 模型化应用环境的两个关键方面:配置文件(profiles)属性(properties)。 * 与属性访问相关的方法通过 {@link PropertyResolver} 超接口暴露。 * *

一个 配置文件 是一组命名的、逻辑分组的 bean 定义,只有在给定的配置文件处于 活跃 状态时才会被容器注册。 * 无论是在 XML 中定义还是通过注解定义,Beans 都可以被分配到一个配置文件中; * 有关语法细节,请参阅 spring-beans 3.1 schema 或 {@link org.springframework.context.annotation.Profile @Profile} 注解。 * {@code Environment} 对象与配置文件相关的角色在于确定哪些配置文件(如果有的话)当前是 {@linkplain #getActiveProfiles 活跃的}, * 以及哪些配置文件(如果有的话)应该默认是活跃的。 * *

属性 在几乎所有应用程序中都扮演重要角色,可能来自多种来源:属性文件、JVM 系统属性、系统环境变量、JNDI、 * servlet 上下文参数、临时 Properties 对象、Maps 等。环境对象与属性相关的角色在于为用户提供一个方便的服务接口, * 用于配置属性源并从中解析属性。 * *

在 {@code ApplicationContext} 中管理的 Beans 可以注册为 {@link org.springframework.context.EnvironmentAware EnvironmentAware} * 或 {@code @Inject} {@code Environment},以查询配置文件状态或直接解析属性。 * *

然而,在大多数情况下,应用程序级别的 Beans 不应该直接与 {@code Environment} 交互, * 而应该通过像 {@link org.springframework.context.support.PropertySourcesPlaceholderConfigurer * PropertySourcesPlaceholderConfigurer} 这样的属性占位符配置器来替换 {@code ${...}} 属性值, * 这个配置器本身是 {@code EnvironmentAware},并且从 Spring 3.1 开始,使用 {@code } 时默认会注册。 * *

必须通过 {@code ConfigurableEnvironment} 接口对环境对象进行配置,所有 * {@code AbstractApplicationContext} 子类的 {@code getEnvironment()} 方法都返回此接口。 * 请参阅 {@link ConfigurableEnvironment} Javadoc,了解在应用程序上下文 {@code refresh()} 之前操作属性源的示例。 * * @author Chris Beams * @since 3.1 * @see PropertyResolver * @see EnvironmentCapable * @see ConfigurableEnvironment * @see AbstractEnvironment * @see StandardEnvironment * @see org.springframework.context.EnvironmentAware * @see org.springframework.context.ConfigurableApplicationContext#getEnvironment * @see org.springframework.context.ConfigurableApplicationContext#setEnvironment * @see org.springframework.context.support.AbstractApplicationContext#createEnvironment */ public interface Environment extends PropertyResolver { /** * 返回为此环境显式激活的配置文件集。配置文件用于有条件地注册 bean 定义,例如基于部署环境创建逻辑分组。 * 通过设置系统属性 {@linkplain AbstractEnvironment#ACTIVE_PROFILES_PROPERTY_NAME * "spring.profiles.active"} 或调用 {@link ConfigurableEnvironment#setActiveProfiles(String...)} 来激活配置文件。 *

如果没有明确指定活跃的配置文件,则任何 {@linkplain #getDefaultProfiles() 默认配置文件} 将自动激活。 * @see #getDefaultProfiles * @see ConfigurableEnvironment#setActiveProfiles * @see AbstractEnvironment#ACTIVE_PROFILES_PROPERTY_NAME */ String[] getActiveProfiles(); /** * 当没有明确设置活跃配置文件时,默认激活的配置文件集。 * @see #getActiveProfiles * @see ConfigurableEnvironment#setDefaultProfiles * @see AbstractEnvironment#DEFAULT_PROFILES_PROPERTY_NAME */ String[] getDefaultProfiles(); /** * 返回一个或多个给定配置文件是否活跃,或者在没有明确活跃配置文件的情况下,给定配置文件是否包含在默认配置文件集中。 * 如果配置文件以 '!' 开头,则逻辑被反转,即如果给定的配置文件 活跃,则方法将返回 {@code true}。 * 例如,{@code env.acceptsProfiles("p1", "!p2")} 将在 'p1' 配置文件活跃或 'p2' 配置文件不活跃时返回 {@code true}。 * @throws IllegalArgumentException 如果调用时没有参数,或者任何配置文件是 {@code null}、空的或只有空格 * @see #getActiveProfiles * @see #getDefaultProfiles * @see #acceptsProfiles(Profiles) * @deprecated 从 5.1 版本开始,建议使用 {@link #acceptsProfiles(Profiles)} */ @Deprecated boolean acceptsProfiles(String... profiles); /** * 返回 {@linkplain #getActiveProfiles() 活跃配置文件} 是否匹配给定的 {@link Profiles} 谓词。 */ boolean acceptsProfiles(Profiles profiles); } ``` ### 六、主要实现 1. **AbstractEnvironment** + `Environment` 接口的抽象基类,提供了共通的实现机制,供其他具体实现类继承。 2. **StandardEnvironment** + 通用实现,处理系统属性和环境变量,适用于大多数标准应用程序。 3. **StandardServletEnvironment** + 针对 Servlet-based Web 应用程序,增加对 Servlet 上下文和配置参数的支持。 ### 七、最佳实践 使用 Spring 的 `StandardEnvironment` 在 Java 程序中模拟配置文件的激活和属性访问。它设置并展示了激活的配置文件("test"),默认配置文件("dev"),并检查了特定配置文件("test")的激活状态,以及获取并打印了系统的 Java 版本。 ```java public class EnvironmentDemo { public static void main(String[] args) { // 设置系统属性以模拟 Spring 的配置文件功能 System.setProperty("spring.profiles.default", "dev"); System.setProperty("spring.profiles.active", "test"); // 创建 StandardEnvironment 实例,用于访问属性和配置文件信息 Environment environment = new StandardEnvironment(); // 使用 getProperty 方法获取系统属性。这里获取了 Java 版本 String javaVersion = environment.getProperty("java.version"); System.out.println("java.version: " + javaVersion); // 获取当前激活的配置文件(profiles) String[] activeProfiles = environment.getActiveProfiles(); System.out.println("activeProfiles = " + String.join(",", activeProfiles)); // 获取默认配置文件(当没有激活的配置文件时使用) String[] defaultProfiles = environment.getDefaultProfiles(); System.out.println("defaultProfiles = " + String.join(",", defaultProfiles)); // 检查是否激活了指定的配置文件。这里检查的是 'test' 配置文件 boolean isDevProfileActive = environment.acceptsProfiles("test"); System.out.println("acceptsProfiles('test'): " + isDevProfileActive); } } ``` 运行结果发现, `StandardEnvironment` 在模拟和管理 Spring 配置文件以及访问系统属性方面的有效性,特别是在不依赖于完整 Spring 应用程序上下文的场景中。 ```java java.version: 11 activeProfiles = test defaultProfiles = dev acceptsProfiles('test'): true ``` ### 八、与其他组件的关系 1. **ApplicationContext** - `ApplicationContext`(Spring 容器)在启动时会创建和使用 `Environment` 实例来加载和处理配置文件(profiles)和属性(properties)。它提供了对 `Environment` 的访问和定制的接口,例如,可以通过 `ApplicationContext` 获取或替换默认的 `Environment` 实例。 2. **PropertySourcesPlaceholderConfigurer** - 这是一个 `BeanFactoryPostProcessor`,用于解析属性占位符(例如 `${property.name}`)。它与 `Environment` 一起工作,从 `Environment` 管理的属性源中解析属性值。 3. **@PropertySource** 和 **PropertySources** - `@PropertySource` 注解和 `PropertySources` 类用于声明属性源(如 .properties 或 .yml 文件)。这些属性源被添加到 `Environment` 中,以便从中读取配置属性。 4. **@ConfigurationProperties** - 这个注解通常用于将配置属性绑定到具有字段的类。`@ConfigurationProperties` 类可以利用 `Environment` 来获取所需的属性值,并将它们映射到类的字段上。 ### 九、常见问题 1. **属性值未找到** + 如果尝试访问一个不存在的属性值,可能得到 `null` 或抛出异常。为解决这个问题,确保属性名称正确,并检查该属性是否已在正确的属性源中定义。 2. **配置文件未正确激活** + 当应用程序没有按预期激活特定的配置文件时,检查配置文件的激活方式(例如通过环境变量、JVM 参数等)是否正确,以及 `@Profile` 注解是否正确应用于相应的 Beans。 3. **属性类型转换错误** + 在将属性值转换为目标类型时出错时,确保属性值可以被正确地转换为所需的类型,并考虑定义自定义转换器。 4. **占位符未解析** + 当属性占位符(如 `${property.name}`)在应用程序中未被替换为实际的属性值时,检查 `PropertySourcesPlaceholderConfigurer` 或类似机制是否已正确配置在 Spring 上下文中。 5. **属性源优先级问题** + 遇到多个属性源中存在同名属性,导致获取到的值不是预期的那个问题时,了解和管理不同属性源的优先级,确保正确的属性源被优先读取。 ================================================ FILE: spring-env/spring-env-environment/pom.xml ================================================ spring-env com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-env-environment ================================================ FILE: spring-env/spring-env-environment/src/main/java/com/xcs/spring/EnvironmentDemo.java ================================================ package com.xcs.spring; import org.springframework.core.env.Environment; import org.springframework.core.env.StandardEnvironment; /** * @author xcs * @date 2023年11月30日 18时15分 **/ public class EnvironmentDemo { public static void main(String[] args) { // 设置系统属性以模拟 Spring 的配置文件功能 System.setProperty("spring.profiles.default", "dev"); System.setProperty("spring.profiles.active", "test"); // 创建 StandardEnvironment 实例,用于访问属性和配置文件信息 Environment environment = new StandardEnvironment(); // 使用 getProperty 方法获取系统属性。这里获取了 Java 版本 String javaVersion = environment.getProperty("java.version"); System.out.println("java.version: " + javaVersion); // 获取当前激活的配置文件(profiles) String[] activeProfiles = environment.getActiveProfiles(); System.out.println("activeProfiles = " + String.join(",", activeProfiles)); // 获取默认配置文件(当没有激活的配置文件时使用) String[] defaultProfiles = environment.getDefaultProfiles(); System.out.println("defaultProfiles = " + String.join(",", defaultProfiles)); // 检查是否激活了指定的配置文件。这里检查的是 'test' 配置文件 boolean isDevProfileActive = environment.acceptsProfiles("test"); System.out.println("acceptsProfiles('test'): " + isDevProfileActive); } } ================================================ FILE: spring-env/spring-env-propertyResolver/README.md ================================================ ## PropertyResolver - [PropertyResolver](#propertyresolver) - [一、基本信息](#一基本信息) - [二、知识储备](#二知识储备) - [三、基本描述](#三基本描述) - [四、主要功能](#四主要功能) - [五、接口源码](#五接口源码) - [六、主要实现](#六主要实现) - [七、最佳实践](#七最佳实践) - [八、与其他组件的关系](#八与其他组件的关系) - [九、常见问题](#九常见问题) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、知识储备 1. **PropertySource** - [PropertySource](https://github.com/xuchengsheng/spring-reading/blob/master/spring-env/spring-env-propertySources/spring-env/spring-env-propertySource/README.md) 类是 Spring 框架中的一个关键抽象类,专门用于封装不同来源的配置数据,如文件、环境变量、系统属性等。它为这些配置源提供了一个统一的接口,使得可以以一致的方式访问各种不同类型的配置数据。这个类的核心是其 `getProperty(String name)` 方法,它根据提供的属性名来检索属性值。在 Spring 的环境抽象中,`PropertySource` 的实例可以被添加到 `Environment` 对象中,从而允许我们在应用程序中方便地访问和管理这些属性。 ### 三、基本描述 `PropertyResolver` 接口是 Spring 框架的一个核心组件,专注于提供一套灵活且强大的机制来处理应用程序配置属性。它定义了一系列方法,用于访问和操纵来自各种源(例如属性文件、环境变量、JVM 参数)的属性值。 ### 四、主要功能 1. **获取属性值** + 通过 `getProperty(key)` 方法可以获取给定键名的属性值。这是处理配置数据时最常用的功能。 2. **带默认值的属性获取** + 如果指定的属性键不存在,`getProperty(key, defaultValue)` 方法允许返回一个默认值。 3. **属性值类型转换** + `getProperty(key, targetType)` 和 `getProperty(key,targetType,defaultValue)` 方法使得可以将属性值转换成指定的数据类型,如从字符串转换为整数或布尔值。 4. **检查属性存在性** + `containsProperty(key)` 方法用于判断是否存在特定的属性键。 5. **获取必需属性** + `getRequiredProperty(key)` 和 `getRequiredProperty(key, targetType)` 方法用于获取必须存在的属性值。如果属性不存在,这些方法会抛出异常。 6. **解析占位符** + `resolvePlaceholders(text)` 方法支持解析字符串中的占位符,并用相应的属性值替换它们。这对于处理包含动态内容的配置文件非常有用。 ### 五、接口源码 `PropertyResolver` 接口提供了一系列方法来处理属性值的解析,包括检查属性是否存在,获取属性值,支持默认值,以及类型转换,另外它还包含用于解析字符串中的占位符的方法,允许动态替换配置值。 ```java /** * 用于针对任何底层源解析属性的接口。 * * @author Chris Beams * @author Juergen Hoeller * @since 3.1 * @see Environment * @see PropertySourcesPropertyResolver */ public interface PropertyResolver { /** * 返回给定属性键是否可用于解析,即给定键的值是否不为 {@code null}。 */ boolean containsProperty(String key); /** * 返回与给定键关联的属性值,或者如果无法解析该键,则返回 {@code null}。 * @param key 要解析的属性名称 */ @Nullable String getProperty(String key); /** * 返回与给定键关联的属性值,或者如果无法解析该键,则返回 {@code defaultValue}。 * @param key 要解析的属性名称 * @param defaultValue 如果找不到值,则返回的默认值 */ String getProperty(String key, String defaultValue); /** * 返回与给定键关联的属性值,或者如果无法解析该键,则返回 {@code null}。 * @param key 要解析的属性名称 * @param targetType 期望的属性值类型 */ @Nullable T getProperty(String key, Class targetType); /** * 返回与给定键关联的属性值,或者如果无法解析该键,则返回 {@code defaultValue}。 * @param key 要解析的属性名称 * @param targetType 期望的属性值类型 * @param defaultValue 如果找不到值,则返回的默认值 */ T getProperty(String key, Class targetType, T defaultValue); /** * 返回与给定键关联的属性值(永远不会是 {@code null})。 * @throws IllegalStateException 如果无法解析该键 */ String getRequiredProperty(String key) throws IllegalStateException; /** * 返回与给定键关联的属性值,转换为给定的 targetType(永远不会是 {@code null})。 * @throws IllegalStateException 如果无法解析给定键 */ T getRequiredProperty(String key, Class targetType) throws IllegalStateException; /** * 解析给定文本中的 ${...} 占位符,将它们替换为 {@link #getProperty} 解析的相应属性值。 * 无法解析且没有默认值的占位符将被忽略并保持不变。 * @param text 要解析的字符串 * @return 解析后的字符串(永远不会是 {@code null}) * @throws IllegalArgumentException 如果给定的文本是 {@code null} */ String resolvePlaceholders(String text); /** * 解析给定文本中的 ${...} 占位符,将它们替换为 {@link #getProperty} 解析的相应属性值。 * 无法解析且没有默认值的占位符将导致抛出 IllegalArgumentException。 * @return 解析后的字符串(永远不会是 {@code null}) * @throws IllegalArgumentException 如果给定文本是 {@code null} 或任何占位符无法解析 */ String resolveRequiredPlaceholders(String text) throws IllegalArgumentException; } ``` ### 六、主要实现 1. **`PropertySourcesPropertyResolver`** + 这是最常用的 `PropertyResolver` 实现之一。它使用 `PropertySource` 对象(可能包含多个,如环境变量、JVM 属性、配置文件等)来解析属性。 2. **`AbstractEnvironment`** + 虽然 `AbstractEnvironment` 本身不直接实现 `PropertyResolver` 接口,但它提供了访问 `PropertyResolver` 功能的接口。`AbstractEnvironment` 通过内部持有的 `PropertySourcesPropertyResolver` 实例来提供属性解析服务。 3. **`StandardEnvironment` 和 `ConfigurableEnvironment`** + 这些类扩展了 `AbstractEnvironment`,提供了更具体的环境配置。它们通过继承 `AbstractEnvironment` 间接提供 `PropertyResolver` 的功能。 ### 七、最佳实践 使用 `PropertyResolver` 来演示配置属性的管理和解析。我们创建了一个包含应用信息的属性源,通过 `PropertySourcesPropertyResolver` 获取和检查属性,处理默认值,以及解析带有占位符的字符串。 ```java public class SimplePropertyResolverDemo { public static void main(String[] args) { // 创建属性源 Map properties = new HashMap<>(); properties.put("app.name", "Spring-Reading"); properties.put("app.version", "1.0.0"); properties.put("app.description", "This is a ${app.name} with version ${app.version}"); MapPropertySource propertySource = new MapPropertySource("myPropertySource", properties); MutablePropertySources propertySources = new MutablePropertySources(); propertySources.addLast(propertySource); // 使用 PropertySourcesPropertyResolver PropertyResolver propertyResolver = new PropertySourcesPropertyResolver(propertySources); // 获取属性 String appName = propertyResolver.getProperty("app.name"); String appVersion = propertyResolver.getProperty("app.version"); System.out.println("获取属性 app.name: " + appName); System.out.println("获取属性 app.version: " + appVersion); // 检查属性是否存在 boolean containsDescription = propertyResolver.containsProperty("app.description"); boolean containsReleaseDate = propertyResolver.containsProperty("app.releaseDate"); System.out.println("是否包含 'app.description' 属性: " + containsDescription); System.out.println("是否包含 'app.releaseDate' 属性: " + containsReleaseDate); // 带默认值的属性获取 String appReleaseDate = propertyResolver.getProperty("app.releaseDate", "2023-11-30"); System.out.println("带默认值的属性获取 app.releaseDate : " + appReleaseDate); // 获取必需属性 String requiredAppName = propertyResolver.getRequiredProperty("app.name"); System.out.println("获取必需属性 app.name: " + requiredAppName); // 解析占位符 String appDescription = propertyResolver.resolvePlaceholders(properties.get("app.description").toString()); System.out.println("解析占位符 app.description: " + appDescription); // 解析必需的占位符 String requiredAppDescription = propertyResolver.resolveRequiredPlaceholders("App Description: ${app.description}"); System.out.println("解析必需的占位符 : " + requiredAppDescription); } } ``` 运行结果发现,`PropertyResolver` 成功地从配置中获取了属性值、验证了属性的存在性、提供了默认值、解析了包含占位符的字符串, ```java 获取属性 app.name: Spring-Reading 获取属性 app.version: 1.0.0 是否包含 'app.description' 属性: true 是否包含 'app.releaseDate' 属性: false 带默认值的属性获取 app.releaseDate : 2023-11-30 获取必需属性 app.name: Spring-Reading 解析占位符 app.description: This is a Spring-Reading with version 1.0.0 解析必需的占位符 : App Description: This is a Spring-Reading with version 1.0.0 ``` ### 八、与其他组件的关系 1. **`Environment` 抽象** - `PropertyResolver` 是 `Environment` 接口的一部分,而 `Environment` 提供了一个更为全面的配置和属性管理接口。`Environment` 抽象了属性源(Property Sources),如系统环境变量、JVM 属性、配置文件等。 - 在实际使用中,当我们操作 `Environment` 对象时,我们也在使用 `PropertyResolver` 的功能,因为 `Environment` 继承了 `PropertyResolver` 接口。 2. **`PropertySource(s)` 和 `PropertySourcesPropertyResolver`** - `PropertySource` 代表了一个属性的源头,比如一个 `.properties` 文件或者环境变量。`MutablePropertySources` 是一个包含多个 `PropertySource` 的容器。 - `PropertySourcesPropertyResolver` 是 `PropertyResolver` 的具体实现,它可以解析由一个或多个 `PropertySource` 提供的属性。 3. **占位符解析** - `PropertyResolver` 提供了解析占位符(如 `${property.name}`)的能力,这在处理配置文件时特别有用。 - 它与 Spring 的 `PropertyPlaceholderConfigurer` 或者 `@Value` 注解协同工作,用于将配置文件中的属性值注入到 Spring 管理的 bean 中。 4. **配置类和注解** - 在使用基于注解的配置类(如使用 `@Configuration` 注解的类)时,`PropertyResolver` 可以用来动态解析和注入属性值,特别是结合 `@PropertySource` 注解使用时。 - 例如,可以使用 `@Value("${property.name}")` 注解来将属性值注入到配置类的字段或方法中。 5. **Profile 管理** - `PropertyResolver` 与 Spring Profiles 的概念紧密相连。通过 `PropertyResolver`,可以方便地访问和检查当前激活的 Profiles,这对于根据不同环境(开发、测试、生产等)来加载特定的配置非常有用。 ### 九、常见问题 1. **属性值未找到** + 如果尝试解析不存在的属性,可以通过提前使用 `containsProperty` 方法来检查属性是否存在,以避免问题。同时,确认属性名是否正确,并确保属性源已经包含了对应的属性。 2. **属性类型转换错误** + 在将属性值转换为不同的类型时,确保属性值的格式与目标类型兼容。如果格式不匹配,可以使用类型转换服务(如 Spring 的 `ConversionService`)进行显式转换。 3. **占位符未解析** + 如果属性值中的 `${...}` 占位符没有被正确解析,确保使用了合适的 `PropertyResolver` 实现(如 `PropertySourcesPropertyResolver`),并且相关的属性源已经包含了占位符引用的属性。 4. **环境相关属性处理** + 在处理不同环境(开发、测试、生产)下的属性时,可以使用 Spring Profiles 来定义环境特定的属性。启动应用时,确保激活了正确的 Profile。 5. **配置文件加载问题** + 如果属性文件没有被正确加载,检查属性文件的路径和格式是否正确,并确保在 Spring 配置类中使用了 `@PropertySource` 注解来指定属性文件。 6. **使用 `@Value` 注解注入属性时出错** + 在使用 `@Value` 注解注入属性值时,确保 `PropertyPlaceholderConfigurer` 或 `PropertySourcesPlaceholderConfigurer` 被正确配置并加载到 Spring 容器中。 ================================================ FILE: spring-env/spring-env-propertyResolver/pom.xml ================================================ spring-env com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-env-propertyResolver ================================================ FILE: spring-env/spring-env-propertyResolver/src/main/java/com/xcs/spring/SimplePropertyResolverDemo.java ================================================ package com.xcs.spring; import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.MutablePropertySources; import org.springframework.core.env.PropertyResolver; import org.springframework.core.env.PropertySourcesPropertyResolver; import java.util.HashMap; import java.util.Map; /** * @author xcs * @date 2023年11月30日 15时27分 **/ public class SimplePropertyResolverDemo { public static void main(String[] args) { // 创建属性源 Map properties = new HashMap<>(); properties.put("app.name", "Spring-Reading"); properties.put("app.version", "1.0.0"); properties.put("app.description", "This is a ${app.name} with version ${app.version}"); MapPropertySource propertySource = new MapPropertySource("myPropertySource", properties); MutablePropertySources propertySources = new MutablePropertySources(); propertySources.addLast(propertySource); // 使用 PropertySourcesPropertyResolver PropertyResolver propertyResolver = new PropertySourcesPropertyResolver(propertySources); // 获取属性 String appName = propertyResolver.getProperty("app.name"); String appVersion = propertyResolver.getProperty("app.version"); System.out.println("获取属性 app.name: " + appName); System.out.println("获取属性 app.version: " + appVersion); // 检查属性是否存在 boolean containsDescription = propertyResolver.containsProperty("app.description"); boolean containsReleaseDate = propertyResolver.containsProperty("app.releaseDate"); System.out.println("是否包含 'app.description' 属性: " + containsDescription); System.out.println("是否包含 'app.releaseDate' 属性: " + containsReleaseDate); // 带默认值的属性获取 String appReleaseDate = propertyResolver.getProperty("app.releaseDate", "2023-11-30"); System.out.println("带默认值的属性获取 app.releaseDate : " + appReleaseDate); // 获取必需属性 String requiredAppName = propertyResolver.getRequiredProperty("app.name"); System.out.println("获取必需属性 app.name: " + requiredAppName); // 解析占位符 String appDescription = propertyResolver.resolvePlaceholders(properties.get("app.description").toString()); System.out.println("解析占位符 app.description: " + appDescription); // 解析必需的占位符 String requiredAppDescription = propertyResolver.resolveRequiredPlaceholders("App Description: ${app.description}"); System.out.println("解析必需的占位符 : " + requiredAppDescription); } } ================================================ FILE: spring-env/spring-env-propertySource/README.md ================================================ ## PropertySource - [PropertySource](#propertysource) - [一、基本信息](#一基本信息) - [二、知识储备](#二知识储备) - [三、基本描述](#三基本描述) - [四、主要功能](#四主要功能) - [五、接口源码](#五接口源码) - [六、主要实现](#六主要实现) - [七、最佳实践](#七最佳实践) - [八、与其他组件的关系](#八与其他组件的关系) - [九、常见问题](#九常见问题) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、知识储备 1. **Java I/O 和文件处理** + 需要熟悉 Java 文件 I/O 操作,特别是如何读取和写入文件,理解处理 `.properties` 文件的 Java 类如 `Properties`,以及如何利用类路径和文件系统路径来定位和加载资源文件,这些知识对于从文件系统或类路径加载属性文件至关重要。 2. **Spring 资源抽象** + 需要了解 Spring 的 [Resource](/spring-resources/spring-resource/README.md) 接口及其实现,比如 `ClassPathResource`、`FileSystemResource` 和 `UrlResource`,这包括理解如何利用 Spring 的强大资源加载机制来读取外部配置文件,这对于从各种位置加载配置文件非常有用。 3. **Java 集合框架** + 需要了解 Java 集合框架有深入了解,特别是 `Map` 接口及其实现如 `HashMap` 和 `LinkedHashMap`,这包括知道如何在 Map 中存储、检索和更新键值对,这对于从键值对集合中加载属性至关重要。 4. **系统环境和 Java 系统属性** + 需要了解 Java 中如何访问和操作系统环境变量和系统属性,包括使用 `System.getenv()` 和 `System.getProperties()`,并理解这些变量在不同操作系统中如何设置和修改,这对于从操作系统环境中加载配置非常重要。 5. **命令行参数解析** + 需要了解如何在 Java 程序中解析命令行参数,包括不同的参数格式(如标志、键值对),以及如何使用第三方库简化命令行参数的解析,这对于从命令行参数中加载配置非常关键。 ### 三、基本描述 `PropertySource` 类是 Spring 框架中的一个关键抽象类,专门用于封装不同来源的配置数据,如文件、环境变量、系统属性等。它为这些配置源提供了一个统一的接口,使得可以以一致的方式访问各种不同类型的配置数据。这个类的核心是其 `getProperty(String name)` 方法,它根据提供的属性名来检索属性值。在 Spring 的环境抽象中,`PropertySource` 的实例可以被添加到 `Environment` 对象中,从而允许我们在应用程序中方便地访问和管理这些属性。 ### 四、主要功能 1. **统一属性源接口** + `PropertySource` 提供了一个统一的接口来访问不同来源的属性,如文件、环境变量、系统属性等。这种统一性使得在不同环境下处理配置数据变得更加简单。 2. **属性查找和访问** + 它允许通过键(属性名)来检索属性值。这是 `PropertySource` 最基本和直接的功能,支持对各种配置数据的读取。 3. **属性源优先级和覆盖** + 在 Spring 的环境中可以注册多个 `PropertySource` 实例,它们按照一定的优先级顺序搜索。这意味着可以通过调整不同 `PropertySource` 的优先级来控制哪些属性将覆盖其他的属性。 4. **环境集成** + `PropertySource` 实例通常被整合到 Spring 的 `Environment` 抽象中,这使得在整个应用程序中访问属性变得更加方便。 5. **可扩展性** + 由于 `PropertySource` 是抽象的,开发者可以创建自定义 `PropertySource` 实现,从而支持从几乎任何数据源加载属性,例如数据库、远程服务、非标准格式的文件等。 6. **支持多种数据格式** + Spring 提供了多种 `PropertySource` 实现,支持从标凑属性文件、JSON 文件、YAML 文件等多种格式中加载属性。 7. **动态属性更新** + 某些 `PropertySource` 实现支持动态更新属性,这对于需要在运行时改变配置的应用程序特别有用。 8. **配置隔离和模块化** + `PropertySource` 有助于将配置隔离到不同的模块中,使得配置管理更加模块化和清晰。 ### 五、接口源码 `PropertySource` 类是 Spring 框架中用于封装属性源的抽象基类。它允许从多种来源(如属性文件、环境变量等)以统一的方式访问配置数据。类中定义了一些核心方法,如获取属性值的 `getProperty` 方法、检查属性是否存在的 `containsProperty` 方法,以及获取属性源名称和源对象的方法。 ```java /** * 抽象基类,表示属性源。 * 属性源是键值对的集合,它们可以从各种源(如属性文件、环境变量等)加载。 * * @param 属性源的类型 */ public abstract class PropertySource { protected final Log logger = LogFactory.getLog(getClass()); // 属性源的名称 protected final String name; // 属性源对象 protected final T source; /** * 构造一个新的 PropertySource,带有给定的名称和源对象。 * * @param name 属性源的名称 * @param source 属性源对象 */ public PropertySource(String name, T source) { Assert.hasText(name, "Property source name must contain at least one character"); Assert.notNull(source, "Property source must not be null"); this.name = name; this.source = source; } /** * 构造函数重载,创建一个具有指定名称的 PropertySource,源对象为新的 Object 实例。 * 通常用于测试场景,创建匿名实现时不需要查询实际源。 * * @param name 属性源的名称 */ @SuppressWarnings("unchecked") public PropertySource(String name) { this(name, (T) new Object()); } /** * 获取此 PropertySource 的名称。 * * @return 属性源的名称 */ public String getName() { return this.name; } /** * 返回此 PropertySource 的底层源对象。 * * @return 属性源对象 */ public T getSource() { return this.source; } /** * 检查此 PropertySource 是否包含给定名称的属性。 * 默认实现仅检查 getProperty(String) 返回的值是否为 null。 * 子类可以实现更高效的算法。 * * @param name 要查找的属性名称 * @return 如果包含该属性,则返回 true */ public boolean containsProperty(String name) { return (getProperty(name) != null); } /** * 返回与给定名称关联的值,如果未找到,则返回 null。 * * @param name 要查找的属性名称 * @return 属性值或 null */ @Nullable public abstract Object getProperty(String name); // 其他方法(equals、hashCode、toString)省略 } ``` ### 六、主要实现 1. **`RopertiesPropertySource`** - 用于加载 `.properties` 文件中的属性。通常通过 `Properties` 对象来构造。 2. **`ResourcePropertySource`** - 扩展自 `PropertiesPropertySource`。可以直接从 `Resource` 对象(如文件系统资源、类路径资源等)中加载属性。 3. **`MapPropertySource`** - 从 `Map` 中加载属性。非常灵活,可以用于从任何键值对映射中提取属性。 4. **`SystemEnvironmentPropertySource`** - 用于访问操作系统环境变量。这个实现类特别处理了环境变量的命名约定(例如,将所有字符转换为小写,将非字母数字字符替换为下划线)。 5. **`CommandLinePropertySource`** - 用于处理命令行参数。支持简单的命名参数以及更复杂的键值对参数。 6. **`CompositePropertySource`** - 一个组合类,可以包含多个 `PropertySource` 实例。用于将多个 `PropertySource` 对象合并为一个逻辑单元,以便统一处理。 ### 七、最佳实践 演示了如何在 Java 应用中使用 Spring Framework 的 `PropertySource` 类来从不同来源(如属性文件、资源文件、Map 对象、系统环境变量和命令行参数)加载配置数据。最后,它使用 `CompositePropertySource` 将所有这些不同的属性源组合在一起,以便能够统一访问和管理这些来自不同来源的配置信息。 ```java public class PropertySourceDemo { public static void main(String[] args) throws Exception { // 从 .properties 文件加载属性 Properties source = PropertiesLoaderUtils.loadProperties(new ClassPathResource("application.properties")); PropertySource propertySource = new PropertiesPropertySource("properties", source); // 直接从Resource加载属性 ClassPathResource classPathResource = new ClassPathResource("application.properties"); PropertySource resourcePropertySource = new ResourcePropertySource("resource", classPathResource); // 从Map加载属性 Map map = new HashMap<>(); map.put("app.name", "Spring-Reading"); map.put("app.version", "1.0.0"); PropertySource mapPropertySource = new MapPropertySource("mapSource", map); // 访问系统环境变量 Map mapEnv = System.getenv(); PropertySource envPropertySource = new SystemEnvironmentPropertySource("systemEnv", mapEnv); // 解析命令行参数 String[] myArgs = {"--appName=MyApplication", "--port=8080"}; PropertySource commandLinePropertySource = new SimpleCommandLinePropertySource(myArgs); // 组合多个 PropertySource 实例 CompositePropertySource composite = new CompositePropertySource("composite"); composite.addPropertySource(propertySource); composite.addPropertySource(resourcePropertySource); composite.addPropertySource(mapPropertySource); composite.addPropertySource(envPropertySource); composite.addPropertySource(commandLinePropertySource); // 打印结果 for (PropertySource ps : composite.getPropertySources()) { System.out.printf("Name: %-15s || Source: %s%n", ps.getName(), ps.getSource()); } } } ``` 运行结果发现,每个 `PropertySource` 实例显示了其特定的数据源和内容,如从文件中读取的配置项、环境变量的详细列表,以及命令行参数的封装。 ```java Name: properties || Source: {app.name=Spring-Reading} Name: resource || Source: {app.name=Spring-Reading} Name: mapSource || Source: {app.name=Spring-Reading, app.version=1.0.0} Name: systemEnv || Source: {USERDOMAIN_ROAMINGPROFILE=DESKTOP-HRS3987, PROCESSOR_LEVEL=6, SESSIONNAME=Console, ALLUSERSPROFILE=C:\ProgramData, PROCESSOR_ARCHITECTURE=AMD64, PSModulePath=C:\Program Files\WindowsPowerShell\Modules;C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules, SystemDrive=C:, MAVEN_HOME=D:\tools\apache-maven-3.8.4-bin\apache-maven-3.8.4, USERNAME=Lenovo, ProgramFiles(x86)=C:\Program Files (x86), FPS_BROWSER_USER_PROFILE_STRING=Default, PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC, DriverData=C:\Windows\System32\Drivers\DriverData, OneDriveConsumer=C:\Users\Lenovo\OneDrive, GOPATH=C:\Users\Lenovo\go, ProgramData=C:\ProgramData, ProgramW6432=C:\Program Files, HOMEPATH=\Users\Lenovo, MYSQL_HOME=C:\ProgramData\Microsoft\Windows\Start Menu\Programs\MySQL\MySQL Server 8.0, PROCESSOR_IDENTIFIER=Intel64 Family 6 Model 165 Stepping 3, GenuineIntel, M2_HOME=D:\tools\apache-maven-3.8.4-bin\apache-maven-3.8.4, ProgramFiles=C:\Program Files, PUBLIC=C:\Users\Public, windir=C:\WINDOWS, =::=::\, ZES_ENABLE_SYSMAN=1, LOCALAPPDATA=C:\Users\Lenovo\AppData\Local, ChocolateyLastPathUpdate=133456990830913519, USERDOMAIN=DESKTOP-HRS3987, FPS_BROWSER_APP_PROFILE_STRING=Internet Explorer, LOGONSERVER=\\DESKTOP-HRS3987, JAVA_HOME=D:\install\jdk, EFC_9756=1, OneDrive=C:\Users\Lenovo\OneDrive, APPDATA=C:\Users\Lenovo\AppData\Roaming, ChocolateyInstall=C:\ProgramData\chocolatey, CommonProgramFiles=C:\Program Files\Common Files, Path=D:\app\Lenovo\product\11.2.0\dbhome_1\bin;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\WINDOWS\System32\OpenSSH\;D:\install\Git\cmd;D:\install\jdk\bin;D:\install\jdk\jre\bin;C:\ProgramData\Microsoft\Windows\Start Menu\Programs\MySQL\MySQL Server 8.0\bin;D:\tools\apache-maven-3.8.4-bin\apache-maven-3.8.4\bin;D:\tools\apache-maven-3.8.4-bin\apache-maven-3.8.4\bin;D:\install\go\bin;D:\install\Xshell\;D:\install\Xftp\;C:\Program Files\dotnet\;C:\Program Files\Microsoft SQL Server\Client SDK\ODBC\170\Tools\Binn\;C:\Program Files\Microsoft SQL Server\150\Tools\Binn\;D:\tools\x86_64-8.1.0-release-win32-seh-rt_v6-rev0\mingw64\bin;;C:\Program Files\Docker\Docker\resources\bin;C:\Program Files\nodejs\;C:\Users\Lenovo\AppData\Local\Microsoft\WindowsApps;;C:\Users\Lenovo\AppData\Local\Programs\Fiddler;C:\WINDOWS\system32\config\systemprofile\go\bin;C:\WINDOWS\system32\config\systemprofile\AppData\Local\Microsoft\WindowsApps;D:\install\Microsoft VS Code\bin;C:\WINDOWS\system32\config\systemprofile\.dotnet\tools;D:\install\lens\resources\cli\bin;C:\Users\Lenovo\AppData\Roaming\npm, OS=Windows_NT, COMPUTERNAME=DESKTOP-HRS3987, PROCESSOR_REVISION=a503, CLASSPATH=.;D:\install\jdk\lib\dt.jar;D:\install\jdk\lib\tools.jar;, CommonProgramW6432=C:\Program Files\Common Files, ComSpec=C:\WINDOWS\system32\cmd.exe, SystemRoot=C:\WINDOWS, TEMP=C:\Users\Lenovo\AppData\Local\Temp, HOMEDRIVE=C:, USERPROFILE=C:\Users\Lenovo, TMP=C:\Users\Lenovo\AppData\Local\Temp, CommonProgramFiles(x86)=C:\Program Files (x86)\Common Files, NUMBER_OF_PROCESSORS=12, IDEA_INITIAL_DIRECTORY=D:\tools\ideaIU-2021.2.2.win\bin} Name: commandLineArgs || Source: org.springframework.core.env.CommandLineArgs@6321e813 ``` ### 八、与其他组件的关系 1. **Environment** + `PropertySource` 提供配置数据给 `Environment`,后者是一个表示应用运行环境的接口,用于整合不同的属性源。 2. **ApplicationContext** + `PropertySource` 中的属性被 `ApplicationContext` 加载,用于影响和配置应用上下文中的 bean 和其他配置。 3. **@PropertySource 注解** + 通过 `@PropertySource` 注解可以将外部配置文件声明为 `PropertySource`,进而集成到 Spring 的 `Environment` 中。 4. **PropertyResolver** + `PropertyResolver` 接口提供从 `PropertySource` 解析属性的功能,是连接属性源和其查询机制的桥梁。 5. **@Value 注解** + `@Value` 注解允许直接从 `PropertySource` 注入属性值到 Spring 管理的 bean 字段中。 6. **Profile** + `PropertySource` 支持基于 Spring Profiles 的不同配置,使得可以根据应用运行的环境加载不同的属性集。 7. **PlaceholderConfigurer类** + 类似 `PropertySourcesPlaceholderConfigurer` 的配置器用于解析 `PropertySource` 中的属性占位符,并应用于 Spring bean 配置。 ### 九、常见问题 1. **属性值未找到** + 如果尝试访问不存在的属性值时返回 `null` 或抛出异常,确保属性文件或其他属性源已正确加载,并检查属性名是否拼写正确。 2. **属性覆盖问题** + 当多个 `PropertySource` 包含相同名称的属性时,为了避免不清楚哪个值会被使用的情况,需要明确 `PropertySource` 的优先级顺序,并了解 `Environment` 是如何处理多个属性源中相同属性的。 3. **属性类型转换问题** + 从 `PropertySource` 获取的属性值默认为字符串类型,可能需要转换为其他类型。使用合适的类型转换逻辑(如 `ConversionService`)或手动转换属性值。 4. **动态属性更新问题** + 在应用运行时动态更新 `PropertySource` 中的属性,并反映这些更改时,可以采用支持动态更新的 `PropertySource` 实现,如使用 Spring Cloud Config。 5. **环境变量和系统属性冲突** + 系统环境变量和 Java 系统属性可能具有相同的键,导致预期之外的覆盖。理解并明确 `SystemEnvironmentPropertySource` 和 `SystemPropertiesPropertySource` 的优先级,适当调整以避免冲突。 6. **`@Value` 注解不解析** + 使用 `@Value` 注解注入属性时,如果无法正确解析属性值,确保有一个活跃的 `PropertySourcesPlaceholderConfigurer` 在上下文中,用于解析占位符。 7. **配置文件路径问题** + 加载外部配置文件时,如果由于路径问题导致文件未被正确加载,检查文件路径是否正确,确保配置文件位于可访问的位置。 ================================================ FILE: spring-env/spring-env-propertySource/pom.xml ================================================ spring-env com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-env-propertySource ================================================ FILE: spring-env/spring-env-propertySource/src/main/java/com/xcs/spring/PropertySourceDemo.java ================================================ package com.xcs.spring; import org.springframework.core.env.*; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.support.PropertiesLoaderUtils; import org.springframework.core.io.support.ResourcePropertySource; import java.util.HashMap; import java.util.Map; import java.util.Properties; /** * @author xcs * @date 2023年12月01日 16时12分 **/ public class PropertySourceDemo { public static void main(String[] args) throws Exception { // 从 .properties 文件加载属性 Properties source = PropertiesLoaderUtils.loadProperties(new ClassPathResource("application.properties")); PropertySource propertySource = new PropertiesPropertySource("properties", source); // 直接从Resource加载属性 ClassPathResource classPathResource = new ClassPathResource("application.properties"); PropertySource resourcePropertySource = new ResourcePropertySource("resource", classPathResource); // 从Map加载属性 Map map = new HashMap<>(); map.put("app.name", "Spring-Reading"); map.put("app.version", "1.0.0"); PropertySource mapPropertySource = new MapPropertySource("mapSource", map); // 访问系统环境变量 Map mapEnv = System.getenv(); PropertySource envPropertySource = new SystemEnvironmentPropertySource("systemEnv", mapEnv); // 解析命令行参数 String[] myArgs = {"--appName=MyApplication", "--port=8080"}; PropertySource commandLinePropertySource = new SimpleCommandLinePropertySource(myArgs); // 组合多个 PropertySource 实例 CompositePropertySource composite = new CompositePropertySource("composite"); composite.addPropertySource(propertySource); composite.addPropertySource(resourcePropertySource); composite.addPropertySource(mapPropertySource); composite.addPropertySource(envPropertySource); composite.addPropertySource(commandLinePropertySource); // 打印结果 for (PropertySource ps : composite.getPropertySources()) { System.out.printf("Name: %-15s || Source: %s%n", ps.getName(), ps.getSource()); } } } ================================================ FILE: spring-env/spring-env-propertySource/src/main/resources/application.properties ================================================ app.name=Spring-Reading ================================================ FILE: spring-env/spring-env-propertySources/README.md ================================================ ## PropertySources - [PropertySources](#propertysources) - [一、基本信息](#一基本信息) - [二、知识储备](#二知识储备) - [三、基本描述](#三基本描述) - [四、主要功能](#四主要功能) - [五、接口源码](#五接口源码) - [六、主要实现](#六主要实现) - [七、最佳实践](#七最佳实践) - [八、与其他组件的关系](#八与其他组件的关系) - [九、常见问题](#九常见问题) ### 一、基本信息 ✒️**作者**- Lex 📝**博客**- [掘金](https://juejin.cn/user/4251135018533068/posts) 📚**源码地址**- [github](https://github.com/xuchengsheng/spring-reading) ### 二、知识储备 1. **PropertySource** + [PropertySource](spring-env/spring-env-propertySource/README.md) 类是 Spring 框架中的一个关键抽象类,专门用于封装不同来源的配置数据,如文件、环境变量、系统属性等。它为这些配置源提供了一个统一的接口,使得可以以一致的方式访问各种不同类型的配置数据。这个类的核心是其 `getProperty(String name)` 方法,它根据提供的属性名来检索属性值。在 Spring 的环境抽象中,`PropertySource` 的实例可以被添加到 `Environment` 对象中,从而允许我们在应用程序中方便地访问和管理这些属性。 ### 三、基本描述 `PropertySources` 是一个Spring框架中的接口,用于表示和管理一组属性源(PropertySource对象),这些属性源包含了应用程序环境中的配置数据。该接口提供了一系列方法来检索、添加、替换和删除这些属性源,允许开发者以统一的方式访问不同来源的配置信息,如环境变量、系统属性、配置文件等。 ### 四、主要功能 1. **属性源管理** + 接口允许添加、移除和替换属性源。这使得可以在运行时动态地调整应用程序的配置。 2. **属性检索** + 提供了方法来检查特定的属性是否存在于任何已注册的属性源中。 3. **属性访问** + 允许从一系列的属性源中检索属性值。由于属性源是有序的,这种检索会按照特定的顺序进行,通常是按照属性源添加的顺序。 4. **属性源枚举** + 可以枚举所有注册的属性源,这对于调试和分析配置环境非常有用。 5. **层次化属性源** + 支持属性源的层次化,使得可以有优先级和覆盖机制,例如,一个环境变量可以覆盖同名的系统属性。 6. **属性源自定义** + 由于它是一个接口,用户可以实现自定义的 `PropertySources`,提供更特定的属性源管理策略。 ### 五、接口源码 `PropertySources` 是一个在Spring框架中定义的接口,旨在作为一个容器,管理和封装一组 `PropertySource` 对象。这些对象代表了应用程序中的各种属性源,如环境变量、系统属性或配置文件。接口提供了流式访问这些属性源的功能,允许开发者以顺序流的形式对属性源进行操作。它还包括用于检查特定属性源是否存在的方法,以及根据名称检索属性源的能力 ```java /** * 包含一个或多个 {@link PropertySource} 对象的容器。 * * @author Chris Beams * @author Juergen Hoeller * @since 3.1 * @see PropertySource */ public interface PropertySources extends Iterable> { /** * 返回一个包含属性源的顺序 {@link Stream}。 * @since 5.1 */ default Stream> stream() { return StreamSupport.stream(spliterator(), false); } /** * 返回是否包含给定名称的属性源。 * @param name 要找的属性源的名称 */ boolean contains(String name); /** * 返回给定名称的属性源,如果未找到则返回 null。 * @param name 要找的属性源的名称 */ @Nullable PropertySource get(String name); } ``` ### 六、主要实现 1. **MutablePropertySources** + 这是最常用的实现,提供了一个可修改的属性源集合。它允许添加、替换和移除属性源,是处理动态环境属性时的首选。 ### 七、最佳实践 使用 Spring 的 `MutablePropertySources` 来管理应用配置属性的过程。它创建了一个属性源集合,向其中添加、替换和移除了不同的属性源,并展示了如何检查属性源的存在性。 ```java public class PropertySourcesDemo { public static void main(String[] args) { // 创建 MutablePropertySources 对象 MutablePropertySources propertySources = new MutablePropertySources(); // 创建两个 MapPropertySource 对象 Map config1 = new HashMap<>(); config1.put("key1", "value1"); PropertySource mapPropertySource1 = new MapPropertySource("config1", config1); Map config2 = new HashMap<>(); config2.put("key2", "value2"); PropertySource mapPropertySource2 = new MapPropertySource("config2", config2); // 添加属性源到开头 propertySources.addFirst(mapPropertySource1); // 添加属性源到末尾 propertySources.addLast(mapPropertySource2); // 打印 System.out.println("打印属性源"); for (PropertySource ps : propertySources) { System.out.printf("Name: %-10s || Source: %s%n", ps.getName(), ps.getSource()); } System.out.println(); // 替换属性源 Map newConfig = new HashMap<>(); newConfig.put("app.name", "Spring-Reading"); newConfig.put("app.version", "1.0.0"); PropertySource newMapPropertySource = new MapPropertySource("config1", newConfig); propertySources.replace("config1", newMapPropertySource); // 打印替换后 System.out.println("打印替换后的属性源"); for (PropertySource ps : propertySources) { System.out.printf("Name: %-10s || Source: %s%n", ps.getName(), ps.getSource()); } System.out.println(); // 检查是否包含属性源 System.out.println("检查是否包含属性源 config2: " + propertySources.contains("config2")); // 移除属性源 System.out.println("移除属性源 config2: " + propertySources.remove("config2")); // 再次检查是否包含属性源 System.out.println("删除后是否包含属性源 config2: " + propertySources.contains("config2")); } } ``` 运行结果发现,`MutablePropertySources` 如何灵活地管理属性源,包括添加、替换和移除操作,以及如何查询属性源的存在性。 ```java 打印属性源 Name: config1 || Source: {key1=value1} Name: config2 || Source: {key2=value2} 打印替换后的属性源 Name: config1 || Source: {app.name=Spring-Reading, app.version=1.0.0} Name: config2 || Source: {key2=value2} 检查是否包含属性源 config2: true 移除属性源 config2: MapPropertySource {name='config2'} 删除后是否包含属性源 config2: false ``` ### 八、与其他组件的关系 1. **`Environment`** + `PropertySources` 是 `Environment` 接口的核心组成部分,负责提供应用程序的配置数据。在 `Environment` 中,`PropertySources` 被用来搜索和访问不同来源的属性值,如系统属性、环境变量和配置文件,从而实现统一的配置属性访问机制。 2. **`PropertySource`** + `PropertySources` 管理一组 `PropertySource` 对象,其中每个 `PropertySource` 代表了一个单独的属性源,如配置文件或环境变量集。`PropertySources` 为这些不同的属性源提供集中式的访问和管理能力。 3. **`PropertyResolver`** + `PropertySources` 与 `PropertyResolver` 接口紧密相关,后者提供属性值的解析功能,例如处理占位符。在处理和转换属性值时,`PropertySources` 通常与 `PropertyResolver` 结合使用,以提高属性管理的灵活性和效率。 4. **`ApplicationContext`** + 在 Spring 的 `ApplicationContext` 中,`PropertySources` 被用于配置和管理应用程序的环境属性。它与应用上下文交互,允许灵活定义和访问应用程序的配置设置,从而成为 Spring 应用配置的重要组成部分。 5. **`Profile`** + `PropertySources` 与 `Profile` 相关联,后者用于定义特定环境的配置。通过 `PropertySources`,可以存储和访问与特定 `Profile` 相关的属性,同时在应用启动时激活或禁用特定的 `Profile`,控制配置的加载。 ### 九、常见问题 1. **属性值重复或覆盖** + 当多个属性源中定义了相同的属性时,可能会发生意外的覆盖。解决这个问题需要检查 `PropertySources` 中各个属性源的优先级和顺序,确保期望的属性源具有正确的优先级。 2. **属性值未找到** + 尝试获取不存在的属性值时可能会引发异常。解决这一问题的方法是确保所需属性已在相应属性源中定义,或使用默认值和条件检查来处理可能缺失的属性。 3. **环境依赖性问题** + 不同环境(如开发、测试、生产)可能需要不同的属性设置,如果混淆这些设置,可能会导致配置错误。使用 Spring Profiles 或条件注解区分不同环境的配置可以有效解决这一问题。 4. **属性类型不匹配** + 尝试将属性值转换为不匹配的数据类型时可能会发生类型转换异常。确保属性值与目标类型兼容,或使用自定义类型转换器,可以避免这种问题。 5. **属性源更新问题** + 在运行时动态更新属性值可能导致同步问题或更新不生效。使用 `MutablePropertySources` 并确保适当的同步和配置刷新,或考虑使用外部配置管理系统,可以解决这一问题。 6. **属性解析问题** + 处理占位符或表达式时可能出现解析错误。检查属性占位符的语法和解析性,确保所有相关的属性源已被正确加载和配置,可以帮助避免这类问题。 ================================================ FILE: spring-env/spring-env-propertySources/pom.xml ================================================ spring-env com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-env-propertySources ================================================ FILE: spring-env/spring-env-propertySources/src/main/java/com/xcs/spring/PropertySourcesDemo.java ================================================ package com.xcs.spring; import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.MutablePropertySources; import org.springframework.core.env.PropertySource; import java.util.HashMap; import java.util.Map; /** * @author xcs * @date 2023年12月02日 10时29分 **/ public class PropertySourcesDemo { public static void main(String[] args) { // 创建 MutablePropertySources 对象 MutablePropertySources propertySources = new MutablePropertySources(); // 创建两个 MapPropertySource 对象 Map config1 = new HashMap<>(); config1.put("key1", "value1"); PropertySource mapPropertySource1 = new MapPropertySource("config1", config1); Map config2 = new HashMap<>(); config2.put("key2", "value2"); PropertySource mapPropertySource2 = new MapPropertySource("config2", config2); // 添加属性源到开头 propertySources.addFirst(mapPropertySource1); // 添加属性源到末尾 propertySources.addLast(mapPropertySource2); // 打印 System.out.println("打印属性源"); for (PropertySource ps : propertySources) { System.out.printf("Name: %-10s || Source: %s%n", ps.getName(), ps.getSource()); } System.out.println(); // 替换属性源 Map newConfig = new HashMap<>(); newConfig.put("app.name", "Spring-Reading"); newConfig.put("app.version", "1.0.0"); PropertySource newMapPropertySource = new MapPropertySource("config1", newConfig); propertySources.replace("config1", newMapPropertySource); // 打印替换后 System.out.println("打印替换后的属性源"); for (PropertySource ps : propertySources) { System.out.printf("Name: %-10s || Source: %s%n", ps.getName(), ps.getSource()); } System.out.println(); // 检查是否包含属性源 System.out.println("检查是否包含属性源 config2: " + propertySources.contains("config2")); // 移除属性源 System.out.println("移除属性源 config2: " + propertySources.remove("config2")); // 再次检查是否包含属性源 System.out.println("删除后是否包含属性源 config2: " + propertySources.contains("config2")); } } ================================================ FILE: spring-factory/pom.xml ================================================ spring-reading com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-factory pom spring-factory-beanFactory spring-factory-listableBeanFactory spring-factory-hierarchicalBeanFactory spring-factory-configurableBeanFactory spring-factory-configurableListableBeanFactory spring-factory-autowireCapableBeanFactory ================================================ FILE: spring-factory/spring-factory-autowireCapableBeanFactory/README.md ================================================ ## AutowireCapableBeanFactory - [AutowireCapableBeanFactory](#autowirecapablebeanfactory) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、接口源码](#四接口源码) - [五、主要实现](#五主要实现) - [五、最佳实践](#五最佳实践) - [createBean](#createbean) - [configureBean](#configurebean) - [autowireBean](#autowirebean) - [autowire](#autowire) - [autowireBeanProperties](#autowirebeanproperties) - [applyBeanPropertyValues](#applybeanpropertyvalues) - [initializeBean](#initializebean) - [destroyBean](#destroybean) - [resolveDependency](#resolvedependency) - [常见问题](#常见问题) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 `AutowireCapableBeanFactory`接口是Spring框架中位于`org.springframework.beans.factory.config`包下的关键接口,扩展自`BeanFactory`,主要提供了在运行时进行Bean自动装配和创建的高级功能。其核心方法`createBean`允许动态创建Bean实例,并进行自动装配,解决了Bean之间的依赖关系,而其他方法如`autowireBean`和`applyBeanPostProcessorsBeforeInitialization`则提供了更细粒度的控制和定制点,使我们能够在Bean生命周期的不同阶段进行干预,实现更灵活的Bean管理和配置。这一接口的存在增强了Spring IoC容器的功能,使其能够更好地适应复杂系统的需求。 ### 三、主要功能 1. **Bean的创建和初始化** + 通过`createBean`方法,可以创建一个新的Bean实例,并在创建过程中执行完整的初始化,包括所有适用的`BeanPostProcessor`的回调。 2. **自动装配** + 提供了不同的自动装配模式,包括按名称、按类型、按构造函数等,通过`autowire`和`autowireBeanProperties`方法实现对Bean属性的自动注入。 3. **Bean配置和后处理器应用** + 通过`configureBean`方法,可以配置已存在的Bean实例,应用属性值、工厂回调等,同时执行所有`BeanPostProcessor`的回调。 4. **定制化初始化和销毁过程** + 通过`initializeBean`方法,可以在Bean初始化过程中应用定制化的操作,例如执行初始化回调、应用后处理器等。还提供了`destroyBean`方法用于销毁Bean实例。 5. **解析依赖** + 通过`resolveDependency`方法,可以解析指定的依赖关系,支持字段、方法、构造函数等各种依赖注入方式。 ### 四、接口源码 从`AutowireCapableBeanFactory`接口源码中看出,它承担了创建、配置和生命周期管理Bean实例的任务。通过定义常量和方法,它提供了细粒度的控制,包括特定的自动装配策略、初始化过程、属性注入、后处理器应用以及销毁阶段。 ```java /** * org.springframework.beans.factory.BeanFactory的扩展接口,由能够进行自动装配的Bean工厂实现, * 前提是它们希望为现有的Bean实例暴露此功能。 * * 此子接口不应在正常应用代码中使用:请使用org.springframework.beans.factory.BeanFactory * 或org.springframework.beans.factory.ListableBeanFactory以供典型用例使用。 * * 其他框架的集成代码可以利用此接口来连接和填充Spring不控制生命周期的现有Bean实例。 * 这对于WebWork Actions和Tapestry Page对象等情况特别有用。 * * 请注意,此接口不由org.springframework.context.ApplicationContext门面实现, * 因为它在应用代码中几乎不被使用。尽管如此,它仍可从应用程序上下文中访问,通过ApplicationContext的 * org.springframework.context.ApplicationContext#getAutowireCapableBeanFactory()方法获得。 * * 您还可以实现org.springframework.beans.factory.BeanFactoryAware接口, * 它在ApplicationContext中运行时公开内部BeanFactory,以便访问AutowireCapableBeanFactory: * 只需将传入的BeanFactory强制转换为AutowireCapableBeanFactory。 * * @author Juergen Hoeller * @since 04.12.2003 * @see org.springframework.beans.factory.BeanFactoryAware * @see org.springframework.beans.factory.config.ConfigurableListableBeanFactory * @see org.springframework.context.ApplicationContext#getAutowireCapableBeanFactory() */ public interface AutowireCapableBeanFactory extends BeanFactory { /** * 常量,表示没有外部定义的自动装配。请注意,仍将应用BeanFactoryAware等,并且将应用基于注释的注入。 * @see #createBean * @see #autowire * @see #autowireBeanProperties */ int AUTOWIRE_NO = 0; /** * 常量,表示按名称自动装配Bean属性(适用于所有Bean属性设置器)。 * @see #createBean * @see #autowire * @see #autowireBeanProperties */ int AUTOWIRE_BY_NAME = 1; /** * 常量,表示按类型自动装配Bean属性(适用于所有Bean属性设置器)。 * @see #createBean * @see #autowire * @see #autowireBeanProperties */ int AUTOWIRE_BY_TYPE = 2; /** * 常量,表示自动装配可以满足的最贪婪的构造函数(涉及解析适当的构造函数)。 * @see #createBean * @see #autowire */ int AUTOWIRE_CONSTRUCTOR = 3; /** * 常量,表示通过对Bean类进行内省来确定适当的自动装配策略。 * @see #createBean * @see #autowire * @deprecated 自Spring 3.0起:如果使用了混合自动装配策略,请优先使用基于注释的自动装配,以清晰标记自动装配需求。 */ @Deprecated int AUTOWIRE_AUTODETECT = 4; /** * 初始化现有Bean实例时使用的后缀,以实现“原始实例”约定:附加到完全限定的Bean类名称, * 例如“com.mypackage.MyClass.ORIGINAL”,以强制返回给定实例,即没有代理等。 * @since 5.1 * @see #initializeBean(Object, String) * @see #applyBeanPostProcessorsBeforeInitialization(Object, String) * @see #applyBeanPostProcessorsAfterInitialization(Object, String) */ String ORIGINAL_INSTANCE_SUFFIX = ".ORIGINAL"; /** * 完全创建给定类的新Bean实例。 * 执行Bean的完全初始化,包括所有适用的BeanPostProcessor BeanPostProcessors。 * 注意:这用于创建一个新实例,填充带注释的字段和方法以及应用所有标准的Bean初始化回调。 * 它不意味着传统的按名称或按类型自动装配属性;对于这些目的,请使用#createBean(Class, int, boolean)。 * @param beanClass 要创建的Bean的类 * @return 新的Bean实例 * @throws BeansException 如果实例化或装配失败 */ T createBean(Class beanClass) throws BeansException; /** * 通过应用实例化后回调和Bean属性后处理(例如用于注释驱动的注入)来填充给定的Bean实例。 * 注意:这主要用于(重新)填充带注释的字段和方法,无论是对于新实例还是对于反序列化的实例。 * 它不意味着传统的按名称或按类型自动装配属性;对于这些目的,请使用#autowireBeanProperties。 * @param existingBean 现有的Bean实例 * @throws BeansException 如果装配失败 */ void autowireBean(Object existingBean) throws BeansException; /** * 配置给定的原始Bean:自动装配Bean属性,应用Bean属性值, * 应用工厂回调,如{@code setBeanName和{@code setBeanFactory, * 以及应用所有Bean后处理器(包括可能包装给定原始Bean的后处理器)。 * 这实际上是#initializeBean提供的超集,完全应用相应Bean定义指定的配置。 * 注意:此方法需要给定名称的Bean定义! * @param existingBean 现有的Bean实例 * @param beanName Bean的名称,如果需要,将传递给它 * (必须存在该名称的Bean定义) * @return 用于使用的Bean实例,原始或包装的其中之一 * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException * 如果没有给定名称的Bean定义 * @throws BeansException 如果初始化失败 * @see #initializeBean */ Object configureBean(Object existingBean, String beanName) throws BeansException; /** * 以指定的自动装配策略完全创建给定类的新Bean实例。 * 此接口支持此处定义的所有常量。 * 执行Bean的完全初始化,包括所有适用的BeanPostProcessor BeanPostProcessors。 * 这实际上是#autowire提供的超集,添加了#initializeBean的行为。 * @param beanClass 要创建的Bean的类 * @param autowireMode 按名称或类型,使用此接口中的常量 * @param dependencyCheck 是否对对象执行依赖关系检查 * (不适用于构造函数的自动装配,因此在这里被忽略) * @return 新的Bean实例 * @throws BeansException 如果实例化或装配失败 * @see #AUTOWIRE_NO * @see #AUTOWIRE_BY_NAME * @see #AUTOWIRE_BY_TYPE * @see #AUTOWIRE_CONSTRUCTOR */ Object createBean(Class beanClass, int autowireMode, boolean dependencyCheck) throws BeansException; /** * 使用指定的自动装配策略实例化给定类的新Bean实例。 * 此处支持此接口中定义的所有常量。 * 也可以使用{@code AUTOWIRE_NO调用,以便仅应用实例化前回调(例如用于注释驱动的注入)。 * 不会应用标准的BeanPostProcessor BeanPostProcessors回调或对Bean的进一步初始化。 * 此接口为这些目的提供了不同的、细粒度的操作,例如#initializeBean。 * 然而,如果适用于实例的构建,将应用InstantiationAwareBeanPostProcessor回调。 * @param beanClass 要实例化的Bean的类 * @param autowireMode 按名称或类型,使用此接口中的常量 * @param dependencyCheck 是否对Bean实例中的对象引用执行依赖关系检查 * (不适用于构造函数的自动装配,因此在这里被忽略) * @return 新的Bean实例 * @throws BeansException 如果实例化或装配失败 * @see #AUTOWIRE_NO * @see #AUTOWIRE_BY_NAME * @see #AUTOWIRE_BY_TYPE * @see #AUTOWIRE_CONSTRUCTOR * @see #AUTOWIRE_AUTODETECT * @see #initializeBean * @see #applyBeanPostProcessorsBeforeInitialization * @see #applyBeanPostProcessorsAfterInitialization */ Object autowire(Class beanClass, int autowireMode, boolean dependencyCheck) throws BeansException; /** * 按名称或类型自动装配给定Bean实例的Bean属性。 * 也可以使用{@code AUTOWIRE_NO调用,以便仅应用实例化后回调(例如用于注释驱动的注入)。 * 不会应用标准的BeanPostProcessor BeanPostProcessors回调或对Bean的进一步初始化。 * 此接口为这些目的提供了不同的、细粒度的操作,例如#initializeBean。 * 然而,如果适用于实例的配置,将应用InstantiationAwareBeanPostProcessor回调。 * @param existingBean 现有的Bean实例 * @param autowireMode 按名称或类型,使用此接口中的常量 * @param dependencyCheck 是否对Bean实例中的对象引用执行依赖关系检查 * @throws BeansException 如果装配失败 * @see #AUTOWIRE_BY_NAME * @see #AUTOWIRE_BY_TYPE * @see #AUTOWIRE_NO */ void autowireBeanProperties(Object existingBean, int autowireMode, boolean dependencyCheck) throws BeansException; /** * 将给定bean定义名称的bean定义的属性值应用于给定的bean实例。 * bean定义可以定义一个完全独立的bean,重用其属性值,或仅用于现有bean实例的属性值。 * 此方法不会自动装配bean属性;它只应用显式定义的属性值。 * 使用#autowireBeanProperties方法来自动装配现有的bean实例。 * 注意:此方法需要给定名称的bean定义! * 不会应用标准的BeanPostProcessor BeanPostProcessors回调或对bean的进一步初始化。 * 此接口为这些目的提供了不同的、细粒度的操作,例如#initializeBean。 * 但是,如果适用于实例的配置,将应用InstantiationAwareBeanPostProcessor回调。 * @param existingBean 现有的bean实例 * @param beanName bean工厂中bean定义的名称 * (必须存在该名称的bean定义) * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException * 如果没有给定名称的bean定义 * @throws BeansException 如果应用属性值失败 * @see #autowireBeanProperties */ void applyBeanPropertyValues(Object existingBean, String beanName) throws BeansException; /** * 初始化给定的原始bean,应用工厂回调,例如{@code setBeanName和{@code setBeanFactory, * 也应用所有bean后处理器(包括可能包装给定原始bean的后处理器)。 * 请注意,给定名称的bean工厂不必存在bean定义。 * 传入的bean名称将仅用于回调,但不会与已注册的bean定义进行检查。 * @param existingBean 现有的bean实例 * @param beanName bean的名称,如果需要,将传递给它 * (仅传递给BeanPostProcessor BeanPostProcessors; * 可以遵循#ORIGINAL_INSTANCE_SUFFIX约定,以强制返回给定的实例, * 即没有代理等) * @return 要使用的bean实例,原始的或包装的其中之一 * @throws BeansException 如果初始化失败 * @see #ORIGINAL_INSTANCE_SUFFIX */ Object initializeBean(Object existingBean, String beanName) throws BeansException; /** * 将BeanPostProcessor BeanPostProcessors应用于给定的现有bean实例, * 调用其{@code postProcessBeforeInitialization方法。返回的bean实例可能是原始bean的包装。 * @param existingBean 现有的bean实例 * @param beanName bean的名称,如果需要,将传递给它 * (仅传递给BeanPostProcessor BeanPostProcessors; * 可以遵循#ORIGINAL_INSTANCE_SUFFIX约定,以强制返回给定的实例, * 即没有代理等) * @return 要使用的bean实例,原始的或包装的其中之一 * @throws BeansException 如果任何后处理失败 * @see BeanPostProcessor#postProcessBeforeInitialization * @see #ORIGINAL_INSTANCE_SUFFIX */ Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException; /** * 将BeanPostProcessor BeanPostProcessors应用于给定的现有bean实例, * 调用其{@code postProcessAfterInitialization方法。返回的bean实例可能是原始bean的包装。 * @param existingBean 现有的bean实例 * @param beanName bean的名称,如果需要,将传递给它 * (仅传递给BeanPostProcessor BeanPostProcessors; * 可以遵循#ORIGINAL_INSTANCE_SUFFIX约定,以强制返回给定的实例, * 即没有代理等) * @return 要使用的bean实例,原始的或包装的其中之一 * @throws BeansException 如果任何后处理失败 * @see BeanPostProcessor#postProcessAfterInitialization * @see #ORIGINAL_INSTANCE_SUFFIX */ Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException; /** * 销毁给定的bean实例(通常来自#createBean),应用 * org.springframework.beans.factory.DisposableBean合同以及注册的 * DestructionAwareBeanPostProcessor DestructionAwareBeanPostProcessors。 * 在销毁过程中出现的任何异常都应该被捕获并记录,而不是传播到此方法的调用方。 * @param existingBean 要销毁的bean实例 */ void destroyBean(Object existingBean); /** * 解析唯一匹配给定对象类型的bean实例,如果有的话,包括其bean名称。 * 这实际上是#getBean(Class)的一个变体,它保留匹配实例的bean名称。 * @param requiredType bean必须匹配的类型;可以是接口或超类 * @return bean名称加上bean实例 * @throws NoSuchBeanDefinitionException 如果没有找到匹配的bean * @throws NoUniqueBeanDefinitionException 如果找到多个匹配的bean * @throws BeansException 如果无法创建bean * @since 4.3.3 * @see #getBean(Class) */ NamedBeanHolder resolveNamedBean(Class requiredType) throws BeansException; /** * 为给定的bean名称解析bean实例,提供一个依赖项描述符,以供目标工厂方法使用。 * 这实际上是#getBean(String, Class)的一个变体,支持具有 * org.springframework.beans.factory.InjectionPoint参数的工厂方法。 * @param name 要查找的bean的名称 * @param descriptor 用于请求注入点的依赖项描述符 * @return 相应的bean实例 * @throws NoSuchBeanDefinitionException 如果没有指定名称的bean * @throws BeansException 如果无法创建bean * @since 5.1.5 * @see #getBean(String, Class) */ Object resolveBeanByName(String name, DependencyDescriptor descriptor) throws BeansException; /** * 解析针对此工厂中定义的bean的指定依赖项。 * @param descriptor 依赖项的描述符(字段/方法/构造函数) * @param requestingBeanName 声明给定依赖项的bean的名称 * @return 已解析的对象,如果找不到则返回{@code null * @throws NoSuchBeanDefinitionException 如果未找到匹配的bean * @throws NoUniqueBeanDefinitionException 如果找到多个匹配的bean * @throws BeansException 如果由于其他原因导致依赖项解析失败 * @since 2.5 * @see #resolveDependency(DependencyDescriptor, String, Set, TypeConverter) */ @Nullable Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName) throws BeansException; /** * 解析针对此工厂中定义的bean的指定依赖项。 * @param descriptor 依赖项的描述符(字段/方法/构造函数) * @param requestingBeanName 声明给定依赖项的bean的名称 * @param autowiredBeanNames 应将所有自动装配的bean的名称(用于解析给定依赖项)添加到的Set * @param typeConverter 用于填充数组和集合的TypeConverter * @return 已解析的对象,如果找不到则返回{@code null * @throws NoSuchBeanDefinitionException 如果未找到匹配的bean * @throws NoUniqueBeanDefinitionException 如果找到多个匹配的bean * @throws BeansException 如果由于其他原因导致依赖项解析失败 * @since 2.5 * @see DependencyDescriptor */ @Nullable Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName, @Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException; } ``` ### 五、主要实现 + `AbstractAutowireCapableBeanFactory` + `AbstractAutowireCapableBeanFactory`是`AutowireCapableBeanFactory`接口的抽象实现,为Spring框架提供了核心的Bean创建、初始化和销毁功能。它实现了`createBean`方法,支持对Bean的依赖注入、属性值应用、后置处理器的应用,以及初始化和销毁阶段的生命周期管理。 ~~~mermaid classDiagram direction BT class BeanFactory { <> } class AutowireCapableBeanFactory { <> } class AbstractAutowireCapableBeanFactory { } AutowireCapableBeanFactory --|> BeanFactory AbstractAutowireCapableBeanFactory --|> AutowireCapableBeanFactory ~~~ ### 五、最佳实践 使用`AnnotationConfigApplicationContext`创建了Spring应用程序上下文,手动注册了一个后置处理器(`MyBeanPostProcessor`)与一个单例Bean(`MyRepository`),最后获取了`AutowireCapableBeanFactory`。 ```java public static void main(String[] args) { // 创建 ApplicationContext AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfiguration.class); // 配置一个后置处理器,用于验证Bean的初始化前后拦截信息打印 applicationContext.getBeanFactory().addBeanPostProcessor(new MyBeanPostProcessor()); // 注册一个MyRepository的Bean对象 applicationContext.getBeanFactory().registerSingleton("myRepository", new MyRepository()); // 获取 AutowireCapableBeanFactory AutowireCapableBeanFactory beanFactory = applicationContext.getAutowireCapableBeanFactory(); } ``` `MyService`是一个经典的Spring Bean类,通过`@Autowired`和`@Value`实现了对其他Bean和配置属性的注入。它实现了`BeanNameAware`、`InitializingBean`和`DisposableBean`接口,分别在Bean分配名称、属性设置完成后和Bean销毁时执行相应的生命周期方法,最后通过调用`toString()`方法提供了方便的信息展示。 ```java public class MyService implements BeanNameAware, InitializingBean, DisposableBean { @Autowired private MyRepository myRepository; @Value("${java.home}") private String javaHome; @Override public void setBeanName(String name) { System.out.println("MyService.setBeanName方法被调用了"); } @Override public void afterPropertiesSet() throws Exception { System.out.println("MyService.afterPropertiesSet方法被调用了"); } @Override public void destroy() throws Exception { System.out.println("MyService.destroy方法被调用了"); } @Override public String toString() { return "MyService{" + "myRepository=" + myRepository + ", javaHome='" + javaHome + '\'' + '}'; } } ``` `MyBeanPostProcessor`是一个自定义的Bean后置处理器,实现了Spring的`BeanPostProcessor`接口。在Bean的初始化前后,它分别调用`postProcessBeforeInitialization`和`postProcessAfterInitialization`方法,在这个具体的实现中,我们简单地输出了一条日志,显示了被处理的Bean的名称。 ```java public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("MyBeanPostProcessor#postProcessBeforeInitialization方法被调用了,Bean名称 = " + beanName); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("MyBeanPostProcessor#postProcessBeforeInitialization方法被调用了,Bean名称 = " + beanName); return bean; } } ``` #### createBean 通过`AutowireCapableBeanFactory`的`createBean`方法,手动创建了一个`MyService`类型的Bean实例。 ```java private static void createBean(AutowireCapableBeanFactory beanFactory) { MyService myService = beanFactory.createBean(MyService.class); System.out.println("调用createBean方法,创建Bean对象 = " + myService); } ``` 运行结果发现,在`MyService`的生命周期中,`setBeanName`、`afterPropertiesSet`等回调方法都被成功触发,说明Bean的初始化过程正常执行。同时,`MyBeanPostProcessor`的后置处理器也在初始化前后成功拦截,并输出了Bean的名称。最重要的是,`MyService`中的`myRepository`和通过`@Value`注解注入的`javaHome`属性都成功被注入,表明依赖注入的过程也正常进行。 ```java MyService.setBeanName方法被调用了 MyBeanPostProcessor#postProcessBeforeInitialization方法被调用了,Bean名称 = com.xcs.spring.service.MyService MyService.afterPropertiesSet方法被调用了 MyBeanPostProcessor#postProcessBeforeInitialization方法被调用了,Bean名称 = com.xcs.spring.service.MyService 调用createBean方法,创建Bean对象 = MyService{myRepository=com.xcs.spring.repository.MyRepository@5b03b9fe, javaHome='D:\install\jdk-11'} ``` #### configureBean 通过`AutowireCapableBeanFactory`的`configureBean`方法手动配置Bean。首先,通过`registerBeanDefinition`方法注册了一个名为 "myService" 的`RootBeanDefinition`,表示要配置的Bean的定义。接着,创建了一个新的`MyService`实例,并通过`configureBean`方法对该实例进行配置,指定了使用之前注册的 "myService" Bean 定义。在方法执行前后,分别输出了`MyService`的实例信息,观察是否成功进行了配置。 ```java private static void configureBean(AutowireCapableBeanFactory beanFactory) { // 配置一个RootBeanDefinition ((DefaultListableBeanFactory) beanFactory).registerBeanDefinition("myService", new RootBeanDefinition(MyService.class)); MyService myService = new MyService(); System.out.println("调用configureBean前,MyService = " + myService); beanFactory.configureBean(myService, "myService"); System.out.println("调用configureBean后,MyService = " + myService); } ``` 运行结果发现,通过`configureBean`方法配置`MyService`实例的过程与使用`createBean`方法创建实例的结果相似。在调用`configureBean`之前,`MyService`的实例信息显示属性都为null。然后,`setBeanName`、`postProcessBeforeInitialization`、`afterPropertiesSet`等回调方法依次被触发,表明Bean的初始化过程正常执行。最终,调用`configureBean`之后,`MyService`的实例信息显示成功注入了`myRepository`和通过`@Value`注解注入的`javaHome`属性。 ```java 调用configureBean前,MyService = MyService{myRepository=null, javaHome='null'} MyService.setBeanName方法被调用了 MyBeanPostProcessor#postProcessBeforeInitialization方法被调用了,Bean名称 = myService MyService.afterPropertiesSet方法被调用了 MyBeanPostProcessor#postProcessBeforeInitialization方法被调用了,Bean名称 = myService 调用configureBean后,MyService = MyService{myRepository=com.xcs.spring.repository.MyRepository@5b03b9fe, javaHome='D:\install\jdk-11'} ``` #### autowireBean 通过`AutowireCapableBeanFactory`的`autowireBean`方法手动进行Bean的自动装配。首先,创建了一个`MyService`实例,然后通过`autowireBean`方法对该实例进行自动装配。在方法执行前后,分别输出了`MyService`的实例信息,观察是否成功进行了自动装配。 ```java private static void autowireBean(AutowireCapableBeanFactory beanFactory) { MyService myService = new MyService(); System.out.println("调用autowireBean前,MyService = " + myService); beanFactory.autowireBean(myService); System.out.println("调用autowireBean后,MyService = " + myService); } ``` 运行结果发现,使用`AutowireCapableBeanFactory`的`autowireBean`方法对一个新创建的`MyService`实例进行手动的自动装配。在调用`autowireBean`之前,`MyService`的实例信息显示属性都为null。然后,调用`autowireBean`方法后,`MyService`的实例信息显示成功注入了`myRepository`属性,该属性引用了`com.xcs.spring.repository.MyRepository`的实例,以及通过`@Value`注解注入的`javaHome`属性,该属性的值为'D:\install\jdk-11'。 然而,需要注意的是,使用`autowireBean`方法并没有触发`BeanNameAware`接口中的`setBeanName`方法、`InitializingBean`接口中的`afterPropertiesSet`方法,以及自定义的`MyBeanPostProcessor`后置处理器的相应回调方法。这是因为`autowireBean`方法主要关注依赖注入,而不涉及到完整的Bean生命周期管理。 ```java 调用autowireBean前,MyService = MyService{myRepository=null, javaHome='null'} 调用autowireBean后,MyService = MyService{myRepository=com.xcs.spring.repository.MyRepository@5b03b9fe, javaHome='D:\install\jdk-11'} ``` #### autowire 使用`AutowireCapableBeanFactory`的`autowire`方法来创建并自动装配一个`MyService`类型的Bean。通过指定`AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE`参数,表示使用类型自动装配。在方法执行后,输出了通过`autowire`方法创建的`MyService`实例的信息。 ```java private static void autowire(AutowireCapableBeanFactory beanFactory) { Object myService = beanFactory.autowire(MyService.class, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, false); System.out.println("调用autowire方法,创建Bean对象 =" + myService); } ``` 运行结果发现,通过`autowire`方法和`autowireBean`方法获得了相似的结果。 不过,需要注意的是,虽然结果相似,但是这两个方法的使用场景略有不同。`autowireBean`是直接对一个已有实例进行自动装配,而`autowire`方法则是根据指定的类型动态创建并自动装配一个Bean。因此,具体使用哪一种方法取决于实际的需求和场景。 ```java 调用autowire方法,创建Bean对象 =MyService{myRepository=com.xcs.spring.repository.MyRepository@4145bad8, javaHome='D:\install\jdk-11'} ``` #### autowireBeanProperties 使用`AutowireCapableBeanFactory`的`autowireBeanProperties`方法,对一个新创建的`MyService`实例进行自动属性装配。首先,创建了一个`MyService`实例,并输出了其初始状态。然后,通过`autowireBeanProperties`方法对该实例进行自动属性装配,使用的是`AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE`规则。最后,输出了`autowireBeanProperties`后的`MyService`实例信息,观察是否成功进行了自动属性装配。 ```java private static void autowireBeanProperties(AutowireCapableBeanFactory beanFactory) { MyService myService = new MyService(); System.out.println("调用autowireBeanProperties前,MyService = " + myService); beanFactory.autowireBeanProperties(myService, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, false); System.out.println("调用autowireBeanProperties后,MyService = " + myService); } ``` 运行结果发现,通过`autowireBeanProperties`方法与之前的`autowireBean`和`autowire`方法相比,得到了相似的结果。在调用`autowireBeanProperties`方法之前,`MyService`的实例信息显示属性都为null。然后,调用`autowireBeanProperties`方法后,`MyService`的实例信息显示成功注入了`myRepository`属性,该属性引用了`com.xcs.spring.repository.MyRepository`的实例,以及通过`@Value`注解注入的`javaHome`属性,该属性的值为'D:\install\jdk-11'。 ``` 调用autowireBeanProperties前,MyService = MyService{myRepository=null, javaHome='null'} 调用autowireBeanProperties后,MyService = MyService{myRepository=com.xcs.spring.repository.MyRepository@4145bad8, javaHome='D:\install\jdk-11'} ``` #### applyBeanPropertyValues 使用`AutowireCapableBeanFactory`的`applyBeanPropertyValues`方法,手动为`MyService`类型的Bean配置自定义属性值。首先,创建了一个`PropertyValue`实例,表示要设置的属性名为"javaHome",属性值为"这里是我自定义的javaHome路径配置"。接着,通过`MutablePropertyValues`构建了属性值的集合,并将之前创建的`PropertyValue`添加到集合中。然后,创建了一个`RootBeanDefinition`,并将属性值集合设置到该Bean定义中。最后,通过`registerBeanDefinition`方法注册了一个名为 "myService" 的Bean定义。在调用`applyBeanPropertyValues`方法之前,创建了一个新的`MyService`实例,并输出了其初始状态。然后,调用`applyBeanPropertyValues`方法后,输出了`applyBeanPropertyValues`后的`MyService`实例信息,观察是否成功应用了自定义的属性值。 ```java private static void applyBeanPropertyValues(AutowireCapableBeanFactory beanFactory) { PropertyValue propertyValue = new PropertyValue("javaHome", "这里是我自定义的javaHome路径配置"); MutablePropertyValues propertyValues = new MutablePropertyValues(); propertyValues.addPropertyValue(propertyValue); RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(MyService.class); rootBeanDefinition.setPropertyValues(propertyValues); // 配置一个RootBeanDefinition ((DefaultListableBeanFactory) beanFactory).registerBeanDefinition("myService", rootBeanDefinition); MyService myService = new MyService(); System.out.println("调用applyBeanPropertyValues前,MyService = " + myService); beanFactory.applyBeanPropertyValues(myService, "myService"); System.out.println("调用applyBeanPropertyValues后,MyService = " + myService); } ``` 运行结果发现,调用`applyBeanPropertyValues`方法后,并没有触发`BeanNameAware`接口中的`setBeanName`方法、`InitializingBean`接口中的`afterPropertiesSet`方法,以及自定义的`MyBeanPostProcessor`后置处理器的相应回调方法。这是因为`applyBeanPropertyValues`方法主要专注于属性值的应用,而不涉及完整的Bean初始化和生命周期管理。最终的运行结果显示`myRepository`属性为null,表明`applyBeanPropertyValues`方法并没有进行依赖注入。 ```java 调用applyBeanPropertyValues前,MyService = MyService{myRepository=null, javaHome='null'} 调用applyBeanPropertyValues后,MyService = MyService{myRepository=null, javaHome='这里是我自定义的javaHome路径配置'} ``` #### initializeBean 使用`AutowireCapableBeanFactory`的`initializeBean`方法,手动初始化`MyService`类型的Bean。首先,创建了一个新的`MyService`实例,并输出了其初始状态。然后,通过`initializeBean`方法对该实例进行初始化,指定了Bean的名称为 "myService"。在调用方法之后,输出了`initializeBean`后的`MyService`实例信息,观察是否成功进行了初始化。 ```java private static void initializeBean(AutowireCapableBeanFactory beanFactory) { MyService myService = new MyService(); System.out.println("调用initializeBean前,MyService = " + myService); beanFactory.initializeBean(myService, "myService"); System.out.println("调用initializeBean前,MyService = " + myService); } ``` 运行结果发现,`myRepository`和`javaHome`的值都显示为`null`,这是因为在调用`initializeBean`方法时,并没有提供属性值的注入。`initializeBean`方法主要用于手动触发Bean的初始化阶段,包括调用`afterPropertiesSet`方法和应用Bean后置处理器,但它并不负责属性的注入。 ```java 调用initializeBean前,MyService = MyService{myRepository=null, javaHome='null'} MyService.setBeanName方法被调用了 MyBeanPostProcessor#postProcessBeforeInitialization方法被调用了,Bean名称 = myService MyService.afterPropertiesSet方法被调用了 MyBeanPostProcessor#postProcessBeforeInitialization方法被调用了,Bean名称 = myService 调用initializeBean前,MyService = MyService{myRepository=null, javaHome='null'} ``` #### destroyBean 使用`AutowireCapableBeanFactory`的`destroyBean`方法,手动销毁(destroy)一个`MyService`类型的Bean实例。通过传递新创建的`MyService`实例作为参数,调用了`destroyBean`方法。 ```java private static void destroyBean(AutowireCapableBeanFactory beanFactory) { beanFactory.destroyBean(new MyService()); } ``` 运行结果发现,在调用`destroyBean`方法后,`MyService`实例的销毁方法 `destroy` 被成功调用。这表明`destroyBean`方法有效地触发了Bean的销毁阶段,执行了实现了`DisposableBean`接口的`destroy`方法。 ```java MyService.destroy方法被调用了 ``` #### resolveDependency 使用`AutowireCapableBeanFactory`的`resolveDependency`方法,手动解析一个依赖关系。通过创建一个`DependencyDescriptor`对象,表示`MyService`类中的`myRepository`属性,然后调用`resolveDependency`方法,尝试解析这个依赖关系。最后,输出解析得到的依赖对象。 ```java private static void resolveDependency(AutowireCapableBeanFactory beanFactory) { try { DependencyDescriptor dependencyDescriptor = new DependencyDescriptor(MyService.class.getDeclaredField("myRepository"), false); Object resolveDependency = beanFactory.resolveDependency(dependencyDescriptor, "myRepository"); System.out.println("resolveDependency = " + resolveDependency); } catch (NoSuchFieldException e) { e.printStackTrace(); } } ``` 运行结果发现,通过调用`resolveDependency`方法成功解析了依赖关系,将`myRepository`属性的依赖解析为`MyRepository`的实例。 ```java resolveDependency = com.xcs.spring.repository.MyRepository@37654521 ``` ### 常见问题 1. **createBean() 和 configureBean()** - `createBean()` 用于创建Bean的实例,即进行Bean的实例化。它是Bean创建过程中的第一步。 - `configureBean()` 则是在Bean实例创建之后,进行进一步的配置,如应用BeanPostProcessors等。这是在Bean实例化后、初始化之前的阶段。 2. **autowireBean() 和 autowire()** - `autowireBean()` 用于对现有的Bean实例进行自动装配,将依赖注入到Bean中。 - `autowire()` 是在创建Bean实例时使用指定的自动装配模式,用于生成新的Bean实例。 3. **autowireBeanProperties() 和 applyBeanPropertyValues()** - `autowireBeanProperties()` 主要用于对Bean实例的属性进行自动装配。 - `applyBeanPropertyValues()` 则是将属性值应用到Bean实例,包括在XML或注解中配置的属性值。 4. **initializeBean()、applyBeanPostProcessorsBeforeInitialization() 和 applyBeanPostProcessorsAfterInitialization()** - `initializeBean()` 是Bean生命周期中的最后一步,包括初始化和应用BeanPostProcessors等。 - `applyBeanPostProcessorsBeforeInitialization()` 用于在初始化之前应用BeanPostProcessors。 - `applyBeanPostProcessorsAfterInitialization()` 用于在初始化之后应用BeanPostProcessors。 5. **destroyBean()** - `destroyBean()` 用于销毁给定的Bean实例,释放资源等。通常在容器关闭时调用。 6. **resolveNamedBean() 和 resolveBeanByName()** - `resolveNamedBean()` 主要用于解析指定名称的Bean并返回Bean实例。 - `resolveBeanByName()` 则是解析指定名称的Bean定义,而不是直接返回Bean实例。 7. **resolveDependency()** - `resolveDependency()` 主要用于解析Bean之间的依赖关系,特别是在自动装配时。在`AbstractAutowireCapableBeanFactory`的`doResolveDependency()`方法中调用。 ================================================ FILE: spring-factory/spring-factory-autowireCapableBeanFactory/pom.xml ================================================ spring-factory com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-factory-autowireCapableBeanFactory ================================================ FILE: spring-factory/spring-factory-autowireCapableBeanFactory/src/main/java/com/xcs/spring/AutowireCapableBeanFactoryDemo.java ================================================ package com.xcs.spring; import com.xcs.spring.config.MyBeanPostProcessor; import com.xcs.spring.config.MyConfiguration; import com.xcs.spring.repository.MyRepository; import com.xcs.spring.service.MyService; import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.PropertyValue; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.beans.factory.config.DependencyDescriptor; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年11月24日 15时02分 **/ public class AutowireCapableBeanFactoryDemo { public static void main(String[] args) { // 创建 ApplicationContext AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfiguration.class); // 配置一个后置处理器,用于验证Bean的初始化前后拦截信息打印 applicationContext.getBeanFactory().addBeanPostProcessor(new MyBeanPostProcessor()); // 注册一个MyRepository的Bean对象 applicationContext.getBeanFactory().registerSingleton("myRepository", new MyRepository()); // 获取 AutowireCapableBeanFactory AutowireCapableBeanFactory beanFactory = applicationContext.getAutowireCapableBeanFactory(); // 创建指定Bean名称的实例 // createBean(beanFactory); // 对给定的Bean实例进行进一步的配置 // configureBean(beanFactory); // 对给定的Bean实例进行自动装配 // autowireBean(beanFactory); // 使用指定的自动装配模式创建Bean实例 // autowire(beanFactory); // 对给定的Bean实例的属性进行自动装配 // autowireBeanProperties(beanFactory); // 将属性值应用到给定的Bean实例 // applyBeanPropertyValues(beanFactory); // 初始化给定的Bean实例 // initializeBean(beanFactory); // 销毁给定的Bean实例 // destroyBean(beanFactory); // 解析Bean之间的依赖关系 // resolveDependency(beanFactory); } private static void createBean(AutowireCapableBeanFactory beanFactory) { MyService myService = beanFactory.createBean(MyService.class); System.out.println("调用createBean方法,创建Bean对象 = " + myService); } private static void configureBean(AutowireCapableBeanFactory beanFactory) { // 配置一个RootBeanDefinition ((DefaultListableBeanFactory) beanFactory).registerBeanDefinition("myService", new RootBeanDefinition(MyService.class)); MyService myService = new MyService(); System.out.println("调用configureBean前,MyService = " + myService); beanFactory.configureBean(myService, "myService"); System.out.println("调用configureBean后,MyService = " + myService); } private static void autowireBean(AutowireCapableBeanFactory beanFactory) { MyService myService = new MyService(); System.out.println("调用autowireBean前,MyService = " + myService); beanFactory.autowireBean(myService); System.out.println("调用autowireBean后,MyService = " + myService); } private static void autowire(AutowireCapableBeanFactory beanFactory) { Object myService = beanFactory.autowire(MyService.class, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, false); System.out.println("调用autowire方法,创建Bean对象 =" + myService); } private static void autowireBeanProperties(AutowireCapableBeanFactory beanFactory) { MyService myService = new MyService(); System.out.println("调用autowireBeanProperties前,MyService = " + myService); beanFactory.autowireBeanProperties(myService, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, false); System.out.println("调用autowireBeanProperties后,MyService = " + myService); } private static void applyBeanPropertyValues(AutowireCapableBeanFactory beanFactory) { PropertyValue propertyValue = new PropertyValue("javaHome", "这里是我自定义的javaHome路径配置"); MutablePropertyValues propertyValues = new MutablePropertyValues(); propertyValues.addPropertyValue(propertyValue); RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(MyService.class); rootBeanDefinition.setPropertyValues(propertyValues); // 配置一个RootBeanDefinition ((DefaultListableBeanFactory) beanFactory).registerBeanDefinition("myService", rootBeanDefinition); MyService myService = new MyService(); System.out.println("调用applyBeanPropertyValues前,MyService = " + myService); beanFactory.applyBeanPropertyValues(myService, "myService"); System.out.println("调用applyBeanPropertyValues后,MyService = " + myService); } private static void initializeBean(AutowireCapableBeanFactory beanFactory) { MyService myService = new MyService(); System.out.println("调用initializeBean前,MyService = " + myService); beanFactory.initializeBean(myService, "myService"); System.out.println("调用initializeBean前,MyService = " + myService); } private static void destroyBean(AutowireCapableBeanFactory beanFactory) { beanFactory.destroyBean(new MyService()); } private static void resolveDependency(AutowireCapableBeanFactory beanFactory) { try { DependencyDescriptor dependencyDescriptor = new DependencyDescriptor(MyService.class.getDeclaredField("myRepository"), false); Object resolveDependency = beanFactory.resolveDependency(dependencyDescriptor, "myRepository"); System.out.println("resolveDependency = " + resolveDependency); } catch (NoSuchFieldException e) { e.printStackTrace(); } } } ================================================ FILE: spring-factory/spring-factory-autowireCapableBeanFactory/src/main/java/com/xcs/spring/config/MyBeanPostProcessor.java ================================================ package com.xcs.spring.config; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; /** * @author xcs * @date 2023年11月27日 11时31分 **/ public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("MyBeanPostProcessor#postProcessBeforeInitialization方法被调用了,Bean名称 = " + beanName); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("MyBeanPostProcessor#postProcessBeforeInitialization方法被调用了,Bean名称 = " + beanName); return bean; } } ================================================ FILE: spring-factory/spring-factory-autowireCapableBeanFactory/src/main/java/com/xcs/spring/config/MyConfiguration.java ================================================ package com.xcs.spring.config; import org.springframework.context.annotation.Configuration; /** * @author xcs * @date 2023年11月24日 14时17分 **/ @Configuration public class MyConfiguration { } ================================================ FILE: spring-factory/spring-factory-autowireCapableBeanFactory/src/main/java/com/xcs/spring/repository/MyRepository.java ================================================ package com.xcs.spring.repository; /** * @author xcs * @date 2023年11月27日 11时36分 **/ public class MyRepository { } ================================================ FILE: spring-factory/spring-factory-autowireCapableBeanFactory/src/main/java/com/xcs/spring/service/MyService.java ================================================ package com.xcs.spring.service; import com.xcs.spring.repository.MyRepository; import org.springframework.beans.factory.BeanNameAware; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; /** * @author xcs * @date 2023年11月24日 14时17分 **/ public class MyService implements BeanNameAware, InitializingBean, DisposableBean { @Autowired private MyRepository myRepository; @Value("${java.home}") private String javaHome; @Override public void setBeanName(String name) { System.out.println("MyService.setBeanName方法被调用了"); } @Override public void afterPropertiesSet() throws Exception { System.out.println("MyService.afterPropertiesSet方法被调用了"); } @Override public void destroy() throws Exception { System.out.println("MyService.destroy方法被调用了"); } public void setJavaHome(String javaHome) { this.javaHome = javaHome; } @Override public String toString() { return "MyService{" + "myRepository=" + myRepository + ", javaHome='" + javaHome + '\'' + '}'; } } ================================================ FILE: spring-factory/spring-factory-beanFactory/README.md ================================================ ## BeanFactory - [BeanFactory](#beanfactory) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、接口源码](#四接口源码) - [五、主要实现](#五主要实现) - [六、最佳实践](#六最佳实践) - [七、与其他组件的关系](#七与其他组件的关系) - [八、常见问题](#八常见问题) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 `BeanFactory`接口是Spring框架中IoC容器的核心接口,定义了一套用于管理和获取Java对象实例的标准机制。通过`getBean`方法,可以按名称从容器中检索Bean实例,而`containsBean`方法用于检查容器中是否存在指定名称的Bean。提供的`getType`方法允许获取指定Bean名称的类型信息,而`isSingleton`方法则用于判断指定Bean是否为单例。该接口支持延迟加载,有助于提高性能。虽然`BeanFactory`是IoC容器的基础,但在实际应用中,通常使用`ApplicationContext`接口,它继承自`BeanFactory`并提供了更多高级特性,包括事件发布、AOP、国际化等,使得开发者更容易构建灵活且功能强大的应用。 ### 三、主要功能 1. **获取Bean** + `BeanFactory` 提供了 `getBean(String name)` 方法,用于从容器中获取指定名称的Bean实例。这使得我们可以通过配置文件或注解将Bean定义在容器中,并在需要时获取它们。 2. **检查是否包含Bean** + 通过 `containsBean(String name)` 方法,我们可以检查容器是否包含指定名称的Bean。这对于避免重复定义相同名称的Bean以及检查Bean是否已被注册很有用。 3. **获取Bean类型** + 使用 `getType(String name)` 方法,我们可以获取指定名称的Bean的类型信息。这允许在运行时动态地了解Bean的类型,从而采取相应的处理措施。 4. **判断Bean是否为单例** + 通过 `isSingleton(String name)` 方法,我们可以判断指定名称的Bean是否为单例。这对于了解Bean的作用域,以及是否在容器中共享同一个实例,具有重要意义。 ### 四、接口源码 `BeanFactory`接口是Spring框架中负责管理和获取Java对象(即Bean)的根接口,作为Bean容器的基本客户端视图。它集中了应用程序组件的配置,支持不同类型的Bean实例,提供丰富的Bean生命周期管理和依赖注入功能,同时支持工厂之间的层次结构。通过这一接口,Spring实现了控制反转(IoC)和依赖注入,为构建灵活、可维护的应用程序提供了基础。 ```java /** * Spring bean 容器访问的根接口。 * *

这是 bean 容器的基本客户端视图; * 针对特定目的,还提供了其他接口,如 {@link ListableBeanFactory} 和 * {@link org.springframework.beans.factory.config.ConfigurableBeanFactory}。 * *

此接口由包含多个 bean 定义的对象实现,每个 bean 定义都由字符串名称唯一标识。根据 bean 定义, * 工厂将返回包含对象的独立实例(原型设计模式)或单个共享实例 * (Singleton 设计模式的一种更好的替代,其中实例是工厂范围内的单例)。将返回哪种类型的实例取决于 * bean 工厂的配置:API 是相同的。从 Spring 2.0 开始,根据具体的应用上下文,还可以使用进一步的范围 * (例如 Web 环境中的 "request" 和 "session" 范围)。 * *

这种方法的要点是,BeanFactory 是应用程序组件的中央注册表,集中配置应用程序组件 * (例如,不再需要单独的对象读取属性文件)。有关此方法优点的讨论,请参阅《Expert One-on-One * J2EE Design and Development》的第 4 章和第 11 章。 * *

请注意,通常最好依赖于依赖注入("推"配置)通过 setter 或构造函数配置应用程序对象, * 而不是使用任何形式的 "拉" 配置,如 BeanFactory 查找。Spring 的依赖注入功能使用此 BeanFactory * 接口及其子接口实现。通常,BeanFactory 将加载存储在配置源中的 bean 定义(例如 XML 文档), * 并使用 {@code org.springframework.beans} 包来配置这些 bean。但是,实现可以简单地直接在 * Java 代码中根据需要返回它创建的 Java 对象。关于定义可能存储在何处的约束没有限制:LDAP、 * RDBMS、XML、属性文件等。鼓励实现支持 bean 之间的引用(依赖注入)。 * *

与 {@link ListableBeanFactory} 中的方法不同,此接口中的所有操作还将检查 * 父工厂,如果这是 {@link HierarchicalBeanFactory}。如果在此工厂实例中找不到 bean, * 将询问直接父工厂。在此工厂实例中的 bean 应该覆盖任何父工厂中具有相同名称的 bean。 * *

Bean 工厂实现应尽可能支持标准的 bean 生命周期接口。初始化方法及其标准顺序的完整集合是: *

    *
  1. BeanNameAware 的 {@code setBeanName} *
  2. BeanClassLoaderAware 的 {@code setBeanClassLoader} *
  3. BeanFactoryAware 的 {@code setBeanFactory} *
  4. EnvironmentAware 的 {@code setEnvironment} *
  5. EmbeddedValueResolverAware 的 {@code setEmbeddedValueResolver} *
  6. ResourceLoaderAware 的 {@code setResourceLoader} * (仅在运行在应用程序上下文中时适用) *
  7. ApplicationEventPublisherAware 的 {@code setApplicationEventPublisher} * (仅在运行在应用程序上下文中时适用) *
  8. MessageSourceAware 的 {@code setMessageSource} * (仅在运行在应用程序上下文中时适用) *
  9. ApplicationContextAware 的 {@code setApplicationContext} * (仅在运行在应用程序上下文中时适用) *
  10. ServletContextAware 的 {@code setServletContext} * (仅在运行在 Web 应用程序上下文中时适用) *
  11. BeanPostProcessors 的 {@code postProcessBeforeInitialization} 方法 *
  12. InitializingBean 的 {@code afterPropertiesSet} *
  13. 自定义 {@code init-method} 定义 *
  14. BeanPostProcessors 的 {@code postProcessAfterInitialization} 方法 *
* *

在关闭 bean 工厂时,以下生命周期方法适用: *

    *
  1. DestructionAwareBeanPostProcessors 的 {@code postProcessBeforeDestruction} 方法 *
  2. DisposableBean 的 {@code destroy} *
  3. 自定义 {@code destroy-method} 定义 *
* * @author Rod Johnson * @author Juergen Hoeller * @author Chris Beams * @since 13 April 2001 * @see BeanNameAware#setBeanName * @see BeanClassLoaderAware#setBeanClassLoader * @see BeanFactoryAware#setBeanFactory * @see org.springframework.context.EnvironmentAware#setEnvironment * @see org.springframework.context.EmbeddedValueResolverAware#setEmbeddedValueResolver * @see org.springframework.context.ResourceLoaderAware#setResourceLoader * @see org.springframework.context.ApplicationEventPublisherAware#setApplicationEventPublisher * @see org.springframework.context.MessageSourceAware#setMessageSource * @see org.springframework.context.ApplicationContextAware#setApplicationContext * @see org.springframework.web.context.ServletContextAware#setServletContext * @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization * @see InitializingBean#afterPropertiesSet * @see org.springframework.beans.factory.support.RootBeanDefinition#getInitMethodName * @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization * @see org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor#postProcessBeforeDestruction * @see DisposableBean#destroy * @see org.springframework.beans.factory.support.RootBeanDefinition#getDestroyMethodName */ public interface BeanFactory { /** * 用于取消引用 {@link FactoryBean} 实例并将其与由 FactoryBean 创建的 bean 区分开。 * 例如,如果名为 {@code myJndiObject} 的 bean 是 FactoryBean,则获取 {@code &myJndiObject} * 将返回工厂,而不是工厂返回的实例。 */ String FACTORY_BEAN_PREFIX = "&"; /** * 返回指定 bean 的实例,该实例可以是共享的或独立的。 *

此方法允许将 Spring BeanFactory 用作替代 Singleton 或 Prototype 设计模式。 * 在单例 bean 的情况下,调用者可以保留对返回对象的引用。 *

将别名转换回相应的规范 bean 名称。 *

如果在此工厂实例中找不到 bean,则将询问父工厂。 * @param name 要检索的 bean 的名称 * @return bean 的实例 * @throws NoSuchBeanDefinitionException 如果没有具有指定名称的 bean * @throws BeansException 如果无法获取 bean */ Object getBean(String name) throws BeansException; /** * 返回指定 bean 的实例,该实例可以是共享的或独立的。 *

与 {@link #getBean(String)} 行为相同,但通过抛出 BeanNotOfRequiredTypeException * 来提供类型安全性。这意味着与 {@link #getBean(String)} 可能发生的正确类型转换上不会抛出 ClassCastException。 *

将别名转换回相应的规范 bean 名称。 *

如果在此工厂实例中找不到 bean,则将询问父工厂。 * @param name 要检索的 bean 的名称 * @param requiredType bean 必须匹配的类型;可以是接口或超类 * @return bean 的实例 * @throws NoSuchBeanDefinitionException 如果没有这样的 bean 定义 * @throws BeanNotOfRequiredTypeException 如果 bean 不是所需类型 * @throws BeansException 如果无法创建 bean */ T getBean(String name, Class requiredType) throws BeansException; /** * 返回指定 bean 的实例,该实例可以是共享的或独立的。 *

允许指定显式构造函数参数/工厂方法参数,覆盖 bean 定义中指定的默认参数(如果有)。 * @param name 要检索的 bean 的名称 * @param args 在使用显式参数创建 bean 实例时使用的参数 * (仅在创建新实例而不是检索现有实例时应用) * @return bean 的实例 * @throws NoSuchBeanDefinitionException 如果没有这样的 bean 定义 * @throws BeanDefinitionStoreException 如果给定参数但受影响的 bean 不是原型 * @throws BeansException 如果无法创建 bean * @since 2.5 */ Object getBean(String name, Object... args) throws BeansException; /** * 返回与给定对象类型唯一匹配的 bean 实例(如果存在)。 *

此方法进入 {@link ListableBeanFactory} 按类型查找领域, * 但也可以根据给定类型的名称将其转换为传统的按名称查找。 * 要在一组 bean 中执行更广泛的检索操作,请使用 {@link ListableBeanFactory} 和/或 {@link BeanFactoryUtils}。 * @param requiredType bean 必须匹配的类型;可以是接口或超类 * @return 匹配所需类型的单个 bean 的实例 * @throws NoSuchBeanDefinitionException 如果没有找到给定类型的 bean * @throws NoUniqueBeanDefinitionException 如果找到给定类型的多个 bean * @throws BeansException 如果无法创建 bean * @since 3.0 * @see ListableBeanFactory */ T getBean(Class requiredType) throws BeansException; /** * 返回指定 bean 的实例,该实例可以是共享的或独立的。 *

允许指定显式构造函数参数/工厂方法参数,覆盖 bean 定义中指定的默认参数(如果有)。 *

此方法进入 {@link ListableBeanFactory} 按类型查找领域, * 但也可以根据给定类型的名称将其转换为传统的按名称查找。 * 要在一组 bean 中执行更广泛的检索操作,请使用 {@link ListableBeanFactory} 和/或 {@link BeanFactoryUtils}。 * @param requiredType bean 必须匹配的类型;可以是接口或超类 * @param args 在使用显式参数创建 bean 实例时使用的参数 * (仅在创建新实例而不是检索现有实例时应用) * @return bean 的实例 * @throws NoSuchBeanDefinitionException 如果没有这样的 bean 定义 * @throws BeanDefinitionStoreException 如果给定参数但受影响的 bean 不是原型 * @throws BeansException 如果无法创建 bean * @since 4.1 */ T getBean(Class requiredType, Object... args) throws BeansException; /** * 返回指定 bean 的提供程序,允许进行延迟的按需检索实例,包括可用性和唯一性选项。 * @param requiredType bean 必须匹配的类型;可以是接口或超类 * @return 相应的提供程序句柄 * @since 5.1 * @see #getBeanProvider(ResolvableType) */ ObjectProvider getBeanProvider(Class requiredType); /** * 返回指定 bean 的提供程序,允许进行延迟的按需检索实例,包括可用性和唯一性选项。 * @param requiredType bean 必须匹配的类型;可以是泛型类型声明。 * 请注意,此处不支持集合类型,与反射注入点形成对比。要以编程方式检索与特定类型匹配的 bean 列表,请在此处指定实际 bean 类型, * 然后随后使用 {@link ObjectProvider#orderedStream()} 或其惰性流/迭代选项。 * @return 相应的提供程序句柄 * @since 5.1 * @see ObjectProvider#iterator() * @see ObjectProvider#stream() * @see ObjectProvider#orderedStream() */ ObjectProvider getBeanProvider(ResolvableType requiredType); /** * 此 bean 工厂是否包含具有给定名称的 bean 定义或外部注册的单例实例? *

如果给定的名称是一个别名,它将被转换回相应的规范 bean 名称。 *

如果此工厂是分层的,如果在此工厂实例中找不到 bean,则将询问任何父工厂。 *

如果找到与给定名称匹配的 bean 定义或单例实例, * 无论指定的 bean 定义是具体的还是抽象的,是懒加载的还是急加载的,是否在范围内,此方法都将返回 {@code true}。 * 因此,请注意,此方法的 {@code true} 返回值不一定表示 {@link #getBean} * 将能够为相同名称获取实例。 * @param name 要查询的 bean 的名称 * @return 是否存在具有给定名称的 bean */ boolean containsBean(String name); /** * 此 bean 是否为共享单例?也就是说,{@link #getBean} 是否始终返回相同的实例? *

注意:此方法返回 {@code false} 不清楚地指示独立的实例。 * 它表示非单例实例,这可能对应于作用域 bean。使用 {@link #isPrototype} 操作明确检查独立实例。 *

将别名转换回相应的规范 bean 名称。 *

如果在此工厂实例中找不到 bean,则将询问父工厂。 * @param name 要查询的 bean 的名称 * @return 此 bean 是否对应于单例实例 * @throws NoSuchBeanDefinitionException 如果没有具有给定名称的 bean * @see #getBean * @see #isPrototype */ boolean isSingleton(String name) throws NoSuchBeanDefinitionException; /** * 此 bean 是否为原型?也就是说,{@link #getBean} 是否始终返回独立的实例? *

注意:此方法返回 {@code false} 不清楚地指示单例对象。 * 它表示非独立实例,这可能对应于作用域 bean。使用 {@link #isSingleton} 操作明确检查共享单例实例。 *

将别名转换回相应的规范 bean 名称。 *

如果在此工厂实例中找不到 bean,则将询问父工厂。 * @param name 要查询的 bean 的名称 * @return 此 bean 是否始终生成独立实例 * @throws NoSuchBeanDefinitionException 如果没有具有给定名称的 bean * @since 2.0.3 * @see #getBean * @see #isSingleton */ boolean isPrototype(String name) throws NoSuchBeanDefinitionException; /** * 检查具有给定名称的 bean 是否与指定类型匹配。 * 更具体地说,检查对给定名称的 {@link #getBean} 调用是否会返回可分配给指定目标类型的对象。 *

将别名转换回相应的规范 bean 名称。 *

如果在此工厂实例中找不到 bean,则将询问父工厂。 * @param name 要查询的 bean 的名称 * @param typeToMatch 要匹配的类型(作为 {@code ResolvableType}) * @return 如果 bean 类型匹配,则为 {@code true}, * 如果不匹配或尚不能确定,则为 {@code false} * @throws NoSuchBeanDefinitionException 如果没有具有给定名称的 bean * @since 4.2 * @see #getBean * @see #getType */ boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException; /** * 检查具有给定名称的 bean 是否与指定类型匹配。 * 更具体地说,检查对给定名称的 {@link #getBean} 调用是否会返回可分配给指定目标类型的对象。 *

将别名转换回相应的规范 bean 名称。 *

如果在此工厂实例中找不到 bean,则将询问父工厂。 * @param name 要查询的 bean 的名称 * @param typeToMatch 要匹配的类型(作为 {@code Class}) * @return 如果 bean 类型匹配,则为 {@code true}, * 如果不匹配或尚不能确定,则为 {@code false} * @throws NoSuchBeanDefinitionException 如果没有具有给定名称的 bean * @since 2.0.1 * @see #getBean * @see #getType */ boolean isTypeMatch(String name, Class typeToMatch) throws NoSuchBeanDefinitionException; /** * 确定具有给定名称的 bean 的类型。 * 更具体地说,确定 {@link #getBean} 对于给定名称将返回的对象的类型。 *

对于 {@link FactoryBean},返回 FactoryBean 创建的对象的类型,由 {@link FactoryBean#getObjectType()} 公开。 * 这可能导致以前未初始化的 {@code FactoryBean} 的初始化(请参阅 {@link #getType(String, boolean)})。 *

将别名转换回相应的规范 bean 名称。 *

如果在此工厂实例中找不到 bean,则将询问父工厂。 * @param name 要查询的 bean 的名称 * @return bean 的类型,如果无法确定则为 {@code null} * @throws NoSuchBeanDefinitionException 如果没有具有给定名称的 bean * @since 1.1.2 * @see #getBean * @see #isTypeMatch */ @Nullable Class getType(String name) throws NoSuchBeanDefinitionException; /** * 确定具有给定名称的 bean 的类型。 * 更具体地说,确定 {@link #getBean} 对于给定名称将返回的对象的类型。 *

对于 {@link FactoryBean},返回 FactoryBean 创建的对象的类型,由 {@link FactoryBean#getObjectType()} 公开。 * 根据 {@code allowFactoryBeanInit} 标志的不同,如果没有提供早期类型信息,则可能导致以前未初始化的 {@code FactoryBean} 的初始化。 *

将别名转换回相应的规范 bean 名称。 *

如果在此工厂实例中找不到 bean,则将询问父工厂。 * @param name 要查询的 bean 的名称 * @param allowFactoryBeanInit 是否可能为了确定其对象类型而初始化 {@code FactoryBean} * @return bean 的类型,如果无法确定则为 {@code null} * @throws NoSuchBeanDefinitionException 如果没有具有给定名称的 bean * @since 5.2 * @see #getBean * @see #isTypeMatch */ @Nullable Class getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException; /** * 返回给定 bean 名称的别名,如果有的话。 *

在 {@link #getBean} 调用中,所有这些别名都指向相同的 bean。 *

如果给定的名称是别名,则将返回相应的原始 bean 名称和其他别名(如果有的话), * 原始 bean 名称将是数组中的第一个元素。 *

如果在此工厂实例中找不到 bean,则将询问父工厂。 * @param name 要检查别名的 bean 名称 * @return 别名,如果没有则为空数组 * @see #getBean */ String[] getAliases(String name); } ``` ### 五、主要实现 + **DefaultListableBeanFactory** + `DefaultListableBeanFactory`是Spring框架中实现`BeanFactory`接口的关键类之一,负责注册、管理和初始化应用程序中的所有Bean定义。它支持依赖注入、不同作用域的Bean管理、处理`FactoryBean`、层次性容器、以及各种生命周期回调等功能,是Spring IoC容器的核心实现,提供了灵活而强大的Bean管理和配置机制。 ### 六、最佳实践 使用`BeanFactory`接口的不同方法来操作和查询Spring容器中的Bean,涵盖了获取Bean、类型判断、别名查询等功能。 ```java public class BeanFactoryDemo { public static void main(String[] args) { // 创建 BeanFactory BeanFactory beanFactory = new AnnotationConfigApplicationContext(MyBean.class).getBeanFactory(); // 根据名称获取 bean Object bean = beanFactory.getBean("myBean"); System.out.println("通过名称获取Bean: " + bean); // 获取 bean 的 ObjectProvider ObjectProvider objectProvider = beanFactory.getBeanProvider(MyBean.class); System.out.println("获取Bean的ObjectProvider: " + objectProvider); // 获取 bean 的类型 Class beanType = beanFactory.getType("myBean"); System.out.println("获取Bean的类型: " + beanType); // 判断是否包含某个 bean boolean containsBean = beanFactory.containsBean("myBean"); System.out.println("判断是否包含Bean: " + containsBean); // 判断 bean 是否为单例 boolean isSingleton = beanFactory.isSingleton("myBean"); System.out.println("判断是否为单例: " + isSingleton); // 判断 bean 是否为原型 boolean isPrototype = beanFactory.isPrototype("myBean"); System.out.println("判断是否为原型: " + isPrototype); // 判断 bean 是否匹配指定类型 boolean isTypeMatch = beanFactory.isTypeMatch("myBean", ResolvableType.forClass(MyBean.class)); System.out.println("判断是否匹配指定类型: " + isTypeMatch); // 获取 bean 的所有别名 String[] aliases = beanFactory.getAliases("myBean"); System.out.println("获取Bean的所有别名: " + String.join(", ", aliases)); } } ``` 运行结果发现,容器中成功创建并管理了名为`myBean`的`MyBean`实例,并提供了相应的类型信息、别名等。 ```java 通过名称获取Bean: com.xcs.spring.bean.MyBean@7b9a4292 获取Bean的ObjectProvider: org.springframework.beans.factory.support.DefaultListableBeanFactory$1@1aa7ecca 获取Bean的类型: class com.xcs.spring.bean.MyBean 判断是否包含Bean: true 判断是否为单例: true 判断是否为原型: false 判断是否匹配指定类型: true 获取Bean的所有别名: ``` ### 七、与其他组件的关系 1. **ApplicationContext** - `ApplicationContext` 是 `BeanFactory` 的子接口之一,提供了更多的企业级功能,是 Spring 应用程序的上下文容器。它扩展了 `BeanFactory`,并在其基础上添加了事件发布、国际化支持、AOP 功能等。 2. **ListableBeanFactory** + `ListableBeanFactory` 是继承自 `BeanFactory` 接口的子接口,它扩展了 `BeanFactory`,提供了更多的方法用于列举 Bean。可以列举所有的 Bean,包括按类型查找、按名称查找、获取所有 Bean 的名称等。 3. **ConfigurableBeanFactory** - `ConfigurableBeanFactory` 是继承自 `HierarchicalBeanFactory` 接口的子接口,它扩展了 `BeanFactory`,提供了更多的配置方法。允许配置属性编辑器、作用域、类加载器等。 4. **AutowireCapableBeanFactory** - `AutowireCapableBeanFactory` 是继承自 `BeanFactory` 接口的子接口,它扩展了 `BeanFactory`,提供了更多的自动装配方法。允许通过构造函数注入、属性注入等方式进行自动装配。 5. **SingletonBeanRegistry** + `SingletonBeanRegistry` 是定义了对单例 Bean 的注册和获取的接口。定义了注册和获取单例 Bean 的方法,允许在容器中注册和获取单例 Bean。 6. **BeanDefinitionRegistry** + `BeanDefinitionRegistry` 定义了对 Bean 定义的注册和获取的接口。允许在容器中注册和获取 Bean 定义,包括根据名称和类型注册 Bean。 7. **BeanPostProcessor** + `BeanPostProcessor` 是一个接口,允许在 Bean 的初始化前后执行自定义的逻辑。`BeanFactory` 通过注册 `BeanPostProcessor` 实现了对 Bean 生命周期的定制。 8. **BeanFactoryPostProcessor** - `BeanFactoryPostProcessor` 是一个接口,用于在容器实例化任何 Bean 之前修改容器的配置。允许在容器启动时对 `BeanFactory` 进行修改,例如修改属性值、注册新的 Bean 等。 ### 八、常见问题 1. **NoSuchBeanDefinitionException** - 在尝试通过 `getBean` 方法获取 Bean 时,抛出 `NoSuchBeanDefinitionException` 异常,表示找不到指定名称的 Bean。可能原因Bean 的名称拼写错误、Bean 没有被正确注册、或者 Bean 的作用域不符合预期。需要检查 Bean 的定义、名称是否正确,并确保 Bean 在容器中被正确注册。 2. **BeanCreationException** - 在容器初始化或获取 Bean 时,抛出 `BeanCreationException` 异常,表示无法创建指定的 Bean。可能原因Bean 的依赖项无法满足、Bean 构造函数抛出异常、或者其他初始化问题。需要检查 Bean 的依赖关系,确保构造函数和初始化方法不会抛出异常。 3. **CircularDependencyException** - Spring 容器检测到循环依赖,抛出 `CircularDependencyException` 异常。可能原因类之间存在循环依赖,A 类依赖 B 类,同时 B 类也依赖 A 类。需要重新设计类之间的依赖关系,使用构造函数注入或者 `@Autowired` 注解避免循环依赖。 4. **BeanNotOfRequiredTypeException** - 在获取 Bean 时,抛出 `BeanNotOfRequiredTypeException` 异常,表示获取到的 Bean 类型与期望的类型不匹配。可能原因获取 Bean 时指定的类型与实际类型不一致。需要检查获取 Bean 的代码,确保指定的类型正确。 5. **BeanInitializationException** - 在 Bean 初始化过程中,抛出 `BeanInitializationException` 异常,表示初始化时发生了异常。可能原因在初始化方法中发生了异常,可能是依赖项不满足或其他原因。需要检查初始化方法,确保不会抛出异常,或者处理异常情况。 6. **BeanCurrentlyInCreationException** - 在尝试获取正在创建中的 Bean 时,抛出 `BeanCurrentlyInCreationException` 异常。可能原因存在循环依赖,导致正在创建中的 Bean 尚未完成创建。需要检查类之间的依赖关系,避免循环依赖。 7. **NoUniqueBeanDefinitionException** - 在按类型获取 Bean 时,存在多个符合条件的 Bean,抛出 `NoUniqueBeanDefinitionException` 异常。可能原因存在多个同类型的 Bean,并且未指定具体的 Bean 名称。需要指定具体的 Bean 名称,或者使用 `@Qualifier` 注解消除歧义。 8. **BeanDefinitionStoreException** - 在加载 Bean 定义时,抛出 `BeanDefinitionStoreException` 异常,表示无法正确加载 Bean 的定义。可能原因Bean 的定义文件格式错误、路径问题等。需要检查 Bean 的定义文件,确保格式正确,路径正确。 ================================================ FILE: spring-factory/spring-factory-beanFactory/pom.xml ================================================ spring-factory com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-factory-beanFactory ================================================ FILE: spring-factory/spring-factory-beanFactory/src/main/java/com/xcs/spring/BeanFactoryDemo.java ================================================ package com.xcs.spring; import com.xcs.spring.bean.MyBean; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.ObjectProvider; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.core.ResolvableType; /** * @author xcs * @date 2023年11月23日 19时17分 **/ public class BeanFactoryDemo { public static void main(String[] args) { // 创建 BeanFactory BeanFactory beanFactory = new AnnotationConfigApplicationContext(MyBean.class).getBeanFactory(); // 根据名称获取 bean Object bean = beanFactory.getBean("myBean"); System.out.println("通过名称获取Bean: " + bean); // 获取 bean 的 ObjectProvider ObjectProvider objectProvider = beanFactory.getBeanProvider(MyBean.class); System.out.println("获取Bean的ObjectProvider: " + objectProvider); // 获取 bean 的类型 Class beanType = beanFactory.getType("myBean"); System.out.println("获取Bean的类型: " + beanType); // 判断是否包含某个 bean boolean containsBean = beanFactory.containsBean("myBean"); System.out.println("判断是否包含Bean: " + containsBean); // 判断 bean 是否为单例 boolean isSingleton = beanFactory.isSingleton("myBean"); System.out.println("判断是否为单例: " + isSingleton); // 判断 bean 是否为原型 boolean isPrototype = beanFactory.isPrototype("myBean"); System.out.println("判断是否为原型: " + isPrototype); // 判断 bean 是否匹配指定类型 boolean isTypeMatch = beanFactory.isTypeMatch("myBean", ResolvableType.forClass(MyBean.class)); System.out.println("判断是否匹配指定类型: " + isTypeMatch); // 获取 bean 的所有别名 String[] aliases = beanFactory.getAliases("myBean"); System.out.println("获取Bean的所有别名: " + String.join(", ", aliases)); } } ================================================ FILE: spring-factory/spring-factory-beanFactory/src/main/java/com/xcs/spring/bean/MyBean.java ================================================ package com.xcs.spring.bean; /** * @author xcs * @date 2023年11月23日 19时18分 **/ public class MyBean { private String message; public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } } ================================================ FILE: spring-factory/spring-factory-beanFactory/src/main/resources/beans.xml ================================================ ================================================ FILE: spring-factory/spring-factory-configurableBeanFactory/README.md ================================================ ## ConfigurableBeanFactory - [ConfigurableBeanFactory](#configurablebeanfactory) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、接口源码](#四接口源码) - [五、最佳实践](#五最佳实践) - [六、与其他组件的关系](#六与其他组件的关系) - [七、常见问题](#七常见问题) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 `ConfigurableBeanFactory`接口是Spring框架中的一个子接口,提供了一组方法用于在运行时配置和定制`BeanFactory`。通过这些方法,可以设置父级BeanFactory、类加载器、表达式解析器、类型转换服务等,以满足特定应用程序的需求。 ### 三、主要功能 1. **设置父级BeanFactory (`setParentBeanFactory`)** + 允许将当前的`BeanFactory`与一个父级`BeanFactory`相关联,以实现Bean的继承和层次结构。 2. **设置Bean类加载器 (`setBeanClassLoader`)** + 允许指定用于加载Bean类的类加载器,使得可以在运行时动态加载Bean的类。 3. **设置临时的类加载器 (`setTempClassLoader`)** + 允许配置一个临时的类加载器,该加载器在需要时用于解析类,提供更灵活的类加载机制。 4. **配置是否缓存Bean元数据 (`setCacheBeanMetadata`)** + 允许我们配置是否缓存Bean的元数据,以提高性能。 5. **设置Bean表达式解析器 (`setBeanExpressionResolver`)** + 允许配置用于解析SpEL表达式的解析器,支持在Bean定义中使用Spring表达式语言。 6. **设置类型转换服务 (`setConversionService`)** + 允许配置用于处理属性类型转换的ConversionService,影响属性注入时的类型转换。 7. **注册PropertyEditorRegistrar (`addPropertyEditorRegistrar`)** + 允许注册自定义的PropertyEditorRegistrar,以便注册自定义的PropertyEditors,用于处理属性值的类型转换。 8. **设置自动装配候选Bean的解析器 (`setAutowireCandidateResolver`)** + 允许配置用于确定自动装配候选Bean的解析器,支持自定义自动装配策略。 9. **设置作用域别名 (`setScopeAlias`)** + 允许为指定的作用域设置别名,提供更灵活的作用域配置。 10. **注册Bean别名 (`registerAlias`)** + 允许注册给定Bean名称的别名,支持通过不同的名称引用相同的Bean。 11. **设置Bean后处理器 (`addBeanPostProcessor`)** + 允许注册自定义的Bean后处理器,用于在Bean初始化前后执行一些自定义逻辑。 12. **设置属性编辑器 (`registerCustomEditor`)** + 允许注册自定义的属性编辑器,用于将字符串值转换为特定属性类型。 13. **设置Bean名称生成器 (`setBeanNameGenerator`)** + 允许配置用于生成默认Bean名称的Bean名称生成器。 14. **设置范围解析器 (`setScopeResolver`)** + 允许配置用于解析范围的解析器,支持自定义作用域的处理逻辑。 15. **设置忽略依赖接口 (`ignoreDependencyInterface`)** + 允许配置需要被忽略的依赖接口,以避免它们被自动装配。 ### 四、接口源码 `ConfigurableBeanFactory`接口是Spring框架中用于配置和定制Bean工厂的关键接口。它提供了设置父级Bean工厂、配置类加载器、设置Bean表达式解析器、注册自定义属性编辑器和后处理器、配置作用域等功能。 ```java /** * Configuration接口由大多数Bean工厂实现,提供配置Bean工厂的功能,除了{@link org.springframework.beans.factory.BeanFactory}接口中的客户端方法。 * *

这个Bean工厂接口不是用于正常应用程序代码的:通常使用{@link org.springframework.beans.factory.BeanFactory}或{@link org.springframework.beans.factory.ListableBeanFactory}来满足典型需求。 * 这个扩展接口只是为了允许内部框架的插拔和对Bean工厂配置方法的特殊访问。 * * @author Juergen Hoeller * @since 03.11.2003 * @see org.springframework.beans.factory.BeanFactory * @see org.springframework.beans.factory.ListableBeanFactory * @see ConfigurableListableBeanFactory */ public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, SingletonBeanRegistry { /** * 标准单例范围的范围标识符:{@value}。 *

可以通过{@code registerScope}添加自定义范围。 * @see #registerScope */ String SCOPE_SINGLETON = "singleton"; /** * 标准原型范围的范围标识符:{@value}。 *

可以通过{@code registerScope}添加自定义范围。 * @see #registerScope */ String SCOPE_PROTOTYPE = "prototype"; /** * 设置这个Bean工厂的父级。 *

请注意,父级不能更改:仅在构造函数之外设置,如果在工厂实例化时不可用。 * @param parentBeanFactory 父Bean工厂 * @throws IllegalStateException 如果此工厂已经与父Bean工厂关联 * @see #getParentBeanFactory() */ void setParentBeanFactory(BeanFactory parentBeanFactory) throws IllegalStateException; /** * 设置用于加载Bean类的类加载器。 * 默认为线程上下文类加载器。 * 注意,此类加载器仅适用于尚未解析Bean类的Bean定义。 * 这在Spring 2.0默认情况下是这样的:Bean定义只携带Bean类名称,一旦工厂处理Bean定义,就会解析Bean类。 * @param beanClassLoader 要使用的类加载器,如果为{@code null},则使用默认类加载器 */ void setBeanClassLoader(@Nullable ClassLoader beanClassLoader); /** * 获取用于加载Bean类的类加载器。 * (如果系统类加载器不可访问,甚至为{@code null})。 * @see org.springframework.util.ClassUtils#forName(String, ClassLoader) */ @Nullable ClassLoader getBeanClassLoader(); /** * 指定用于类型匹配目的的临时ClassLoader。 * 默认为none,简单地使用标准Bean类ClassLoader。 * 通常,仅在涉及时才指定临时ClassLoader, * 以确保实际的Bean类尽可能地懒加载。启动后,临时加载程序将被删除。 * @since 2.5 */ void setTempClassLoader(@Nullable ClassLoader tempClassLoader); /** * 获取用于类型匹配目的的临时ClassLoader,如果有的话。 * @since 2.5 */ @Nullable ClassLoader getTempClassLoader(); /** * 设置是否缓存诸如给定Bean定义(以合并方式)和已解析Bean类等Bean元数据。 * 默认为开。 * 关闭此标志以启用Bean定义对象和特别是Bean类的热刷新。如果关闭此标志,任何创建Bean实例都将重新查询Bean类加载程序以获取新解析的类。 */ void setCacheBeanMetadata(boolean cacheBeanMetadata); /** * 返回是否缓存诸如给定Bean定义(以合并方式)和已解析Bean类等Bean元数据。 */ boolean isCacheBeanMetadata(); /** * 指定Bean定义值中表达式的解析策略。 *

默认情况下,Bean工厂中没有激活表达式支持。 * ApplicationContext通常在这里设置标准的表达式策略,支持统一EL兼容样式的“#{...}”表达式。 * @since 3.0 */ void setBeanExpressionResolver(@Nullable BeanExpressionResolver resolver); /** * 返回Bean定义值中表达式的解析策略。 * @since 3.0 */ @Nullable BeanExpressionResolver getBeanExpressionResolver(); /** * 指定用于转换属性值的Spring 3.0 ConversionService,作为JavaBeans PropertyEditors的替代方法。 * @since 3.0 */ void setConversionService(@Nullable ConversionService conversionService); /** * 返回关联的ConversionService,如果有的话。 * @since 3.0 */ @Nullable ConversionService getConversionService(); /** * 添加一个PropertyEditorRegistrar,应用于所有Bean创建过程。 *

此类注册器创建新的PropertyEditor实例,并为每个Bean创建尝试将其注册在给定的注册表上。这避免了对自定义编辑器的同步的需求; * 因此,通常最好使用此方法,而不是{@link #registerCustomEditor}。 * @param registrar 要注册的PropertyEditorRegistrar */ void addPropertyEditorRegistrar(PropertyEditorRegistrar registrar); /** * 注册给定类型的所有属性的自定义属性编辑器。在工厂配置期间调用。 *

请注意,此方法将注册共享的自定义编辑器实例;对该实例的访问将同步以确保线程安全。通常最好使用{@link #addPropertyEditorRegistrar}, * 而不是此方法,以避免在自定义编辑器上进行同步的需求。 * @param requiredType 属性的类型 * @param propertyEditorClass 要注册的{@link PropertyEditor}类 */ void registerCustomEditor(Class requiredType, Class propertyEditorClass); /** * 使用在此BeanFactory中注册的自定义编辑器初始化给定的PropertyEditorRegistry。 * @param registry 要初始化的PropertyEditorRegistry */ void copyRegisteredEditorsTo(PropertyEditorRegistry registry); /** * 设置此BeanFactory应该用于转换Bean属性值、构造函数参数值等的自定义类型转换器。 *

这将覆盖默认的PropertyEditor机制,因此任何自定义编辑器或自定义编辑器注册器都将变得无关紧要。 * @since 2.5 * @see #addPropertyEditorRegistrar * @see #registerCustomEditor */ void setTypeConverter(TypeConverter typeConverter); /** * 获取此BeanFactory使用的类型转换器。每次调用可能都是新实例,因为TypeConverter通常不是线程安全的。 *

如果默认的PropertyEditor机制处于活动状态,返回的TypeConverter将意识到已注册的所有自定义编辑器。 * @since 2.5 */ TypeConverter getTypeConverter(); /** * 添加用于嵌入值(如注释属性)的String解析器。 * @param valueResolver 要应用于嵌入值的String解析器 * @since 3.0 */ void addEmbeddedValueResolver(StringValueResolver valueResolver); /** * 确定是否已向此工厂注册了嵌入值解析器,以通过{@link #resolveEmbeddedValue(String)}应用它们。 * @since 4.3 */ boolean hasEmbeddedValueResolver(); /** * 解析给定的嵌入值,例如注解属性。 * @param value 要解析的值 * @return 已解析的值(可能是原始值) * @since 3.0 */ @Nullable String resolveEmbeddedValue(String value); /** * 添加将应用于此工厂创建的所有Bean的新BeanPostProcessor。 * 在工厂配置期间调用。 *

注意:此处提交的后处理器将按照注册的顺序应用;通过实现{@link org.springframework.core.Ordered}接口表达的任何排序语义将被忽略。 * 请注意,在自动检测到的后处理器(例如,作为应用程序上下文中的bean)之后,总是会应用程序地注册的后处理器。 * @param beanPostProcessor 要注册的后处理器 */ void addBeanPostProcessor(BeanPostProcessor beanPostProcessor); /** * 返回当前已注册的BeanPostProcessor数量,如果有的话。 */ int getBeanPostProcessorCount(); /** * 注册给定的范围,由给定的Scope实现支持。 * @param scopeName 范围标识符 * @param scope 支持的Scope实现 */ void registerScope(String scopeName, Scope scope); /** * 返回当前已注册的所有Scope的名称。 *

这将仅返回明确注册的范围名称。内置范围,如“singleton”和“prototype”将不会被公开。 * @return 范围名称的数组,如果没有则为空数组 * @see #registerScope */ String[] getRegisteredScopeNames(); /** * 返回给定范围名称的Scope实现,如果有的话。 *

这将仅返回明确注册的范围。 * 内置范围,如“singleton”和“prototype”将不会被公开。 * @param scopeName 范围的名称 * @return 注册的Scope实现,如果没有则为{@code null} * @see #registerScope */ @Nullable Scope getRegisteredScope(String scopeName); /** * 为此Bean工厂设置{@code ApplicationStartup}。 *

这允许应用程序上下文在应用程序启动期间记录度量。 * @param applicationStartup 新的应用程序启动 * @since 5.3 */ void setApplicationStartup(ApplicationStartup applicationStartup); /** * 返回此Bean工厂的{@code ApplicationStartup}。 * @since 5.3 */ ApplicationStartup getApplicationStartup(); /** * 提供与此工厂相关的安全访问控制上下文。 * @return 适用的AccessControlContext(永远不为{@code null}) * @since 3.0 */ AccessControlContext getAccessControlContext(); /** * 从给定的其他工厂复制所有相关配置。 *

应包括所有标准配置设置,以及BeanPostProcessors、Scopes和工厂特定的内部设置。 * 不应包括任何实际Bean定义的元数据,如BeanDefinition对象和Bean名称别名。 * @param otherFactory 要从中复制的其他Bean工厂 */ void copyConfigurationFrom(ConfigurableBeanFactory otherFactory); /** * 给定一个bean名称,创建一个别名。通常在工厂配置期间调用此方法,但也可以用于别名的运行时注册。因此,工厂实现应该同步别名访问。 * * @param beanName 目标bean的规范名称 * @param alias 要为bean注册的别名 * @throws BeanDefinitionStoreException 如果别名已经被使用 */ void registerAlias(String beanName, String alias) throws BeanDefinitionStoreException; /** * 解析在此工厂中注册的所有别名目标名称和别名,将给定的StringValueResolver应用于它们。 * 值解析器可以解析目标bean名称中的占位符,甚至是别名中的占位符。 * * @param valueResolver 要应用的StringValueResolver * @since 2.5 */ void resolveAliases(StringValueResolver valueResolver); /** * 返回给定bean名称的合并BeanDefinition,如果需要,将子bean定义与其父定义合并。考虑在祖先工厂中的bean定义。 * * @param beanName 要检索合并定义的bean的名称 * @return 给定bean的(可能合并的)BeanDefinition * @throws NoSuchBeanDefinitionException 如果没有给定名称的bean定义 * @since 2.5 */ BeanDefinition getMergedBeanDefinition(String beanName) throws NoSuchBeanDefinitionException; /** * 确定具有给定名称的bean是否是FactoryBean。 * * @param name 要检查的bean的名称 * @return bean是否是FactoryBean({@code false}表示bean存在但不是FactoryBean) * @throws NoSuchBeanDefinitionException 如果没有给定名称的bean * @since 2.5 */ boolean isFactoryBean(String name) throws NoSuchBeanDefinitionException; /** * 显式控制指定bean的当前创建状态。仅供容器内部使用。 * * @param beanName bean的名称 * @param inCreation bean当前是否正在创建 * @since 3.1 */ void setCurrentlyInCreation(String beanName, boolean inCreation); /** * 确定指定的bean当前是否正在创建。 * * @param beanName bean的名称 * @return bean当前是否正在创建 * @since 2.5 */ boolean isCurrentlyInCreation(String beanName); /** * 为给定的bean注册一个依赖bean,该依赖bean在给定bean被销毁之前将被销毁。 * * @param beanName bean的名称 * @param dependentBeanName 依赖bean的名称 * @since 2.5 */ void registerDependentBean(String beanName, String dependentBeanName); /** * 返回所有依赖于指定bean的bean的名称(如果有的话)。 * * @param beanName bean的名称 * @return 依赖bean名称的数组,如果没有则为空数组 * @since 2.5 */ String[] getDependentBeans(String beanName); /** * 返回指定bean依赖的所有bean的名称(如果有的话)。 * * @param beanName bean的名称 * @return bean依赖的bean名称数组,如果没有则为空数组 * @since 2.5 */ String[] getDependenciesForBean(String beanName); /** * 根据其bean定义销毁给定的bean实例(通常是从此工厂获得的原型实例)。 *

销毁期间出现的任何异常都应捕获并记录,而不是传播到此方法的调用者。 * * @param beanName bean定义的名称 * @param beanInstance 要销毁的bean实例 */ void destroyBean(String beanName, Object beanInstance); /** * 在当前目标作用域中销毁指定的作用域bean,如果有的话。 *

销毁期间出现的任何异常都应捕获并记录,而不是传播到此方法的调用者。 * * @param beanName 作用域bean的名称 */ void destroyScopedBean(String beanName); /** * 销毁此工厂中的所有单例bean,包括已注册为可销毁的内部bean。在工厂关闭时调用。 *

销毁期间出现的任何异常都应捕获并记录,而不是传播到此方法的调用者。 */ void destroySingletons(); } ``` ### 五、最佳实践 演示了`ConfigurableBeanFactory`接口的一些常用方法。包括设置父级BeanFactory、获取BeanPostProcessor数量、注册别名、处理依赖关系等。 ```java public class ConfigurableBeanFactoryDemo { public static void main(String[] args) { // 创建 ApplicationContext ConfigurableBeanFactory configurableBeanFactory = new AnnotationConfigApplicationContext(MyConfiguration.class).getBeanFactory(); // 设置父级 BeanFactory configurableBeanFactory.setParentBeanFactory(new DefaultListableBeanFactory()); // 获取BeanPostProcessor数量 int beanPostProcessorCount = configurableBeanFactory.getBeanPostProcessorCount(); System.out.println("获取BeanPostProcessor数量: " + beanPostProcessorCount); // 获取所有已注册的 Scope 名称 String[] scopeNames = configurableBeanFactory.getRegisteredScopeNames(); System.out.println("获取所有已注册的Scope名称: " + String.join(", ", scopeNames)); // 获取注册的 Scope Scope customScope = configurableBeanFactory.getRegisteredScope("customScope"); System.out.println("获取注册的Scope :" + customScope); // 获取ApplicationStartup ApplicationStartup applicationStartup = configurableBeanFactory.getApplicationStartup(); System.out.println("获取ApplicationStartup: " + applicationStartup); // 获取AccessControlContext AccessControlContext accessControlContext = configurableBeanFactory.getAccessControlContext(); System.out.println("获取AccessControlContext: " + accessControlContext); // 拷贝配置 ConfigurableListableBeanFactory otherFactory = new DefaultListableBeanFactory(); configurableBeanFactory.copyConfigurationFrom(otherFactory); System.out.println("拷贝配置copyConfigurationFrom: " + otherFactory); // 注册别名 String beanName = "myService"; String alias = "helloService"; configurableBeanFactory.registerAlias(beanName, alias); System.out.println("注册别名registerAlias, BeanName: " + beanName + "alias: " + alias); // 获取合并后的 BeanDefinition BeanDefinition mergedBeanDefinition = configurableBeanFactory.getMergedBeanDefinition("myService"); System.out.println("获取合并后的 BeanDefinition: " + mergedBeanDefinition); // 判断是否为 FactoryBean String factoryBeanName = "myService"; boolean isFactoryBean = configurableBeanFactory.isFactoryBean(factoryBeanName); System.out.println("判断是否为FactoryBean" + isFactoryBean); // 设置当前 Bean 是否正在创建 String currentBeanName = "myService"; boolean inCreation = true; configurableBeanFactory.setCurrentlyInCreation(currentBeanName, inCreation); System.out.println("设置当前Bean是否正在创建: " + currentBeanName); // 判断指定的 Bean 是否正在创建 boolean isCurrentlyInCreation = configurableBeanFactory.isCurrentlyInCreation(currentBeanName); System.out.println("判断指定的Bean是否正在创建" + isCurrentlyInCreation); // 注册依赖关系 String dependentBeanName = "dependentBean"; configurableBeanFactory.registerDependentBean(beanName, dependentBeanName); System.out.println("注册依赖关系" + "beanName: " + beanName + "dependentBeanName: " + dependentBeanName); // 获取所有依赖于指定 Bean 的 Bean 名称 String[] dependentBeans = configurableBeanFactory.getDependentBeans(beanName); System.out.println("获取所有依赖于指定Bean的Bean名称: " + String.join(", ", dependentBeans)); // 获取指定 Bean 依赖的所有 Bean 名称 String[] dependencies = configurableBeanFactory.getDependenciesForBean(beanName); System.out.println("获取指定Bean依赖的所有Bean名称: " + String.join(", ", dependencies)); // 销毁指定 Bean 实例 Object beanInstance = configurableBeanFactory.getBean(beanName); configurableBeanFactory.destroyBean(beanName, beanInstance); System.out.println("销毁指定 Bean 实例: " + beanName); // 销毁所有单例 Bean configurableBeanFactory.destroySingletons(); System.out.println("销毁所有单例Bean destroySingletons" ); } } ``` ### 六、与其他组件的关系 1. **BeanFactory** - `ConfigurableBeanFactory` 继承自 `BeanFactory` 接口,因此它包含了 `BeanFactory` 的基本功能。它扩展了 `BeanFactory` 接口,提供了更多的配置和管理功能。 2. **HierarchicalBeanFactory** - `ConfigurableBeanFactory` 扩展了 `HierarchicalBeanFactory` 接口,因此它具有层次结构的特性。这允许配置一个父级 `BeanFactory`,从而实现层次化的容器结构。 3. **SingletonBeanRegistry** - `ConfigurableBeanFactory` 扩展了 `SingletonBeanRegistry` 接口,使其能够注册和管理单例对象。这包括对单例对象的创建、获取和销毁等操作。 4. **BeanPostProcessor** - `ConfigurableBeanFactory` 允许注册 `BeanPostProcessor` 实现,这些实现可以在容器中的 bean 实例创建和初始化阶段进行干预。通过 `addBeanPostProcessor` 方法,可以向容器注册自定义的 `BeanPostProcessor` 实现。 5. **Scope** - `ConfigurableBeanFactory` 定义了注册和获取作用域的方法。可以通过 `registerScope` 注册自定义作用域,并通过 `getRegisteredScope` 获取已注册的作用域。 6. **ConversionService** - `ConfigurableBeanFactory` 允许设置和获取 `ConversionService`,用于执行属性值的类型转换。通过 `setConversionService` 和 `getConversionService` 方法,可以配置和检索自定义的类型转换服务。 7. **BeanExpressionResolver** - `ConfigurableBeanFactory` 允许设置和获取 `BeanExpressionResolver`,用于解析 bean 定义中的表达式。通过 `setBeanExpressionResolver` 和 `getBeanExpressionResolver` 方法,可以配置和获取表达式解析器。 8. **TypeConverter** - `ConfigurableBeanFactory` 允许设置和获取 `TypeConverter`,用于执行 bean 属性值的类型转换。通过 `setTypeConverter` 和 `getTypeConverter` 方法,可以配置和获取类型转换器。 9. **StringValueResolver** - `ConfigurableBeanFactory` 允许添加和检查字符串值解析器,用于解析 bean 定义中的嵌入式值。通过 `addEmbeddedValueResolver` 和 `hasEmbeddedValueResolver` 方法,可以进行相应的操作。 10. **PropertyEditorRegistrar** - `ConfigurableBeanFactory` 允许添加 `PropertyEditorRegistrar`,用于注册自定义的 `PropertyEditor`。通过 `addPropertyEditorRegistrar` 方法,可以注册属性编辑器注册器。 11. **ApplicationStartup** - `ConfigurableBeanFactory` 允许设置和获取 `ApplicationStartup`,用于记录应用程序启动期间的度量。通过 `setApplicationStartup` 和 `getApplicationStartup` 方法,可以配置和获取启动度量实例。 12. **AccessControlContext** - `ConfigurableBeanFactory` 允许获取与该工厂相关的安全访问控制上下文。通过 `getAccessControlContext` 方法,可以获取安全访问控制上下文。 13. **AliasRegistry** - `ConfigurableBeanFactory` 继承了 `AliasRegistry` 接口,因此它也提供了对别名的注册和解析的功能。可以通过 `registerAlias` 和 `resolveAliases` 方法进行别名的注册和解析。 14. **ConfigurableListableBeanFactory** - `ConfigurableBeanFactory` 是 `ConfigurableListableBeanFactory` 的父接口。`ConfigurableListableBeanFactory` 除了提供配置和管理功能外,还允许对 bean 定义进行查询,包括合并的 bean 定义等。 ### 七、常见问题 1. **什么是 ConfigurableBeanFactory 接口的作用?** - `ConfigurableBeanFactory` 接口是 Spring 框架中 IoC 容器的配置接口之一。它扩展了 `BeanFactory` 接口,提供了一系列用于配置和管理 IoC 容器的方法,如设置父容器、注册作用域、添加 BeanPostProcessor 等。 2. **ConfigurableBeanFactory 与 BeanFactory 有什么区别?** - `ConfigurableBeanFactory` 继承自 `BeanFactory` 接口,相比于 `BeanFactory`,它提供了更多的配置和管理方法。通过 `ConfigurableBeanFactory`,可以设置父容器、注册作用域、添加 BeanPostProcessor 等,而 `BeanFactory` 只提供了基本的 bean 获取和类型检查的功能。 3. **如何设置 ConfigurableBeanFactory 的父容器?** - 可以使用 `setParentBeanFactory` 方法来设置 `ConfigurableBeanFactory` 的父容器。通过这个方法,可以建立容器的层次结构,实现父子容器之间的资源共享和依赖管理。 4. **ConfigurableBeanFactory 中的 Scope 是什么作用?** - `ConfigurableBeanFactory` 中的 Scope 用于定义和管理 bean 的作用域。作用域决定了 bean 的生命周期和可见范围,例如 singleton 作用域表示一个 bean 在容器中是单例的,而 prototype 作用域表示每次获取 bean 都会创建一个新实例。 5. **如何注册自定义的 Scope?** - 可以使用 `registerScope` 方法来注册自定义的作用域。通过该方法,可以添加自定义作用域的实现,并在容器中使用该作用域。 6. **ConfigurableBeanFactory 中的 BeanPostProcessor 有什么作用?** - `BeanPostProcessor` 在 bean 的创建和初始化阶段起作用,允许我们对 bean 进行定制和干预。通过 `addBeanPostProcessor` 方法,可以注册自定义的 `BeanPostProcessor` 实现。 7. **如何设置 ConfigurableBeanFactory 的 ClassLoader?** - 可以使用 `setBeanClassLoader` 方法来设置 `ConfigurableBeanFactory` 的 ClassLoader。这个 ClassLoader 用于加载 bean 的类,可以指定一个特定的 ClassLoader,也可以使用默认的线程上下文 ClassLoader。 8. **ConfigurableBeanFactory 中的 ConversionService 有什么作用?** - `ConversionService` 用于执行 bean 属性值的类型转换。通过 `setConversionService` 方法,可以设置自定义的类型转换服务,用于处理 bean 属性值的类型转换。 9. **如何配置 ConfigurableBeanFactory 中的 BeanExpressionResolver?** - 可以使用 `setBeanExpressionResolver` 方法来配置 `ConfigurableBeanFactory` 中的 `BeanExpressionResolver`。这个解析器用于解析 bean 定义中的表达式,例如 Spring EL 表达式。 10. **ConfigurableBeanFactory 中的 TypeConverter 有什么作用?** - `TypeConverter` 用于执行 bean 属性值的类型转换。通过 `setTypeConverter` 方法,可以设置自定义的类型转换器,用于处理 bean 属性值的类型转换。 11. **如何注册 ConfigurableBeanFactory 的 PropertyEditorRegistrar?** - 可以使用 `addPropertyEditorRegistrar` 方法来注册 `ConfigurableBeanFactory` 的 `PropertyEditorRegistrar`。这个注册器用于注册自定义的 `PropertyEditor`,处理属性编辑器的创建和注册。 12. **ConfigurableBeanFactory 中的 ApplicationStartup 有什么作用?** - `ApplicationStartup` 用于记录应用程序启动期间的度量。通过 `setApplicationStartup` 方法,可以设置自定义的 `ApplicationStartup` 实例,用于记录启动度量。 13. **ConfigurableBeanFactory 中的 destroySingletons 方法有什么作用?** - `destroySingletons` 方法用于销毁所有单例 bean,包括内部注册为可销毁的 bean。这个方法通常在容器关闭时调用,用于释放资源和执行清理操作。 ================================================ FILE: spring-factory/spring-factory-configurableBeanFactory/pom.xml ================================================ spring-factory com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-factory-configurableBeanFactory ================================================ FILE: spring-factory/spring-factory-configurableBeanFactory/src/main/java/com/xcs/spring/ConfigurableBeanFactoryDemo.java ================================================ package com.xcs.spring; import com.xcs.spring.config.MyConfiguration; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.Scope; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.core.metrics.ApplicationStartup; import java.security.AccessControlContext; /** * @author xcs * @date 2023年11月24日 13时56分 **/ public class ConfigurableBeanFactoryDemo { public static void main(String[] args) { // 创建 ApplicationContext ConfigurableBeanFactory configurableBeanFactory = new AnnotationConfigApplicationContext(MyConfiguration.class).getBeanFactory(); // 设置父级 BeanFactory configurableBeanFactory.setParentBeanFactory(new DefaultListableBeanFactory()); // 获取BeanPostProcessor数量 int beanPostProcessorCount = configurableBeanFactory.getBeanPostProcessorCount(); System.out.println("获取BeanPostProcessor数量: " + beanPostProcessorCount); // 获取所有已注册的 Scope 名称 String[] scopeNames = configurableBeanFactory.getRegisteredScopeNames(); System.out.println("获取所有已注册的Scope名称: " + String.join(", ", scopeNames)); // 获取注册的 Scope Scope customScope = configurableBeanFactory.getRegisteredScope("customScope"); System.out.println("获取注册的Scope :" + customScope); // 获取ApplicationStartup ApplicationStartup applicationStartup = configurableBeanFactory.getApplicationStartup(); System.out.println("获取ApplicationStartup: " + applicationStartup); // 获取AccessControlContext AccessControlContext accessControlContext = configurableBeanFactory.getAccessControlContext(); System.out.println("获取AccessControlContext: " + accessControlContext); // 拷贝配置 ConfigurableListableBeanFactory otherFactory = new DefaultListableBeanFactory(); configurableBeanFactory.copyConfigurationFrom(otherFactory); System.out.println("拷贝配置copyConfigurationFrom: " + otherFactory); // 注册别名 String beanName = "myService"; String alias = "helloService"; configurableBeanFactory.registerAlias(beanName, alias); System.out.println("注册别名registerAlias, BeanName: " + beanName + "alias: " + alias); // 获取合并后的 BeanDefinition BeanDefinition mergedBeanDefinition = configurableBeanFactory.getMergedBeanDefinition("myService"); System.out.println("获取合并后的 BeanDefinition: " + mergedBeanDefinition); // 判断是否为 FactoryBean String factoryBeanName = "myService"; boolean isFactoryBean = configurableBeanFactory.isFactoryBean(factoryBeanName); System.out.println("判断是否为FactoryBean" + isFactoryBean); // 设置当前 Bean 是否正在创建 String currentBeanName = "myService"; boolean inCreation = true; configurableBeanFactory.setCurrentlyInCreation(currentBeanName, inCreation); System.out.println("设置当前Bean是否正在创建: " + currentBeanName); // 判断指定的 Bean 是否正在创建 boolean isCurrentlyInCreation = configurableBeanFactory.isCurrentlyInCreation(currentBeanName); System.out.println("判断指定的Bean是否正在创建" + isCurrentlyInCreation); // 注册依赖关系 String dependentBeanName = "dependentBean"; configurableBeanFactory.registerDependentBean(beanName, dependentBeanName); System.out.println("注册依赖关系" + "beanName: " + beanName + "dependentBeanName: " + dependentBeanName); // 获取所有依赖于指定 Bean 的 Bean 名称 String[] dependentBeans = configurableBeanFactory.getDependentBeans(beanName); System.out.println("获取所有依赖于指定Bean的Bean名称: " + String.join(", ", dependentBeans)); // 获取指定 Bean 依赖的所有 Bean 名称 String[] dependencies = configurableBeanFactory.getDependenciesForBean(beanName); System.out.println("获取指定Bean依赖的所有Bean名称: " + String.join(", ", dependencies)); // 销毁指定 Bean 实例 Object beanInstance = configurableBeanFactory.getBean(beanName); configurableBeanFactory.destroyBean(beanName, beanInstance); System.out.println("销毁指定 Bean 实例: " + beanName); // 销毁所有单例 Bean configurableBeanFactory.destroySingletons(); System.out.println("销毁所有单例Bean destroySingletons" ); } } ================================================ FILE: spring-factory/spring-factory-configurableBeanFactory/src/main/java/com/xcs/spring/config/MyConfiguration.java ================================================ package com.xcs.spring.config; import com.xcs.spring.service.MyService; import com.xcs.spring.service.impl.MyServiceImpl; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @author xcs * @date 2023年11月24日 14时17分 **/ @Configuration public class MyConfiguration { @Bean public MyService myService() { return new MyServiceImpl(); } } ================================================ FILE: spring-factory/spring-factory-configurableBeanFactory/src/main/java/com/xcs/spring/service/MyService.java ================================================ package com.xcs.spring.service; /** * @author xcs * @date 2023年11月24日 14时17分 **/ public interface MyService { void greet(); } ================================================ FILE: spring-factory/spring-factory-configurableBeanFactory/src/main/java/com/xcs/spring/service/impl/MyServiceImpl.java ================================================ package com.xcs.spring.service.impl; import com.xcs.spring.service.MyService; /** * @author xcs * @date 2023年11月24日 14时17分 **/ public class MyServiceImpl implements MyService { @Override public void greet() { System.out.println("Hello from MyService!"); } } ================================================ FILE: spring-factory/spring-factory-configurableListableBeanFactory/README.md ================================================ ## ConfigurableListableBeanFactory - [ConfigurableListableBeanFactory](#configurablelistablebeanfactory) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、接口源码](#四接口源码) - [五、最佳实践](#五最佳实践) - [六、与其他组件的关系](#六与其他组件的关系) - [七、常见问题](#七常见问题) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 `ConfigurableListableBeanFactory`接口是Spring框架中的一个关键接口,扩展了`ListableBeanFactory`和`ConfigurableBeanFactory`,提供了更多用于配置和管理bean的方法。通过该接口,可以注册单例对象、自定义作用域、冻结配置、获取BeanDefinition信息、设置父BeanFactory等。 ### 三、主要功能 1. **注册单例对象** + 允许通过指定的bean名称注册一个单例对象,使用`registerSingleton(String beanName, Object singletonObject)`方法。 2. **注册作用域** + 提供方法`registerScope(String scopeName, Scope scope)`,用于注册自定义的作用域,扩展了Spring默认的单例、原型等作用域。 3. **冻结配置** + 通过`freezeConfiguration()`方法,可以冻结bean工厂的配置,防止在后续阶段对其进行修改。这在某些场景下可以增强应用程序的安全性。 4. **提供已注册的BeanDefinition** + 通过`getBeanDefinition(String beanName)`方法,可以获取指定bean名称的`BeanDefinition`对象,包含有关bean的配置信息,例如作用域、依赖等。 5. **提供已注册的所有BeanDefinition名称** + 使用`getBeanDefinitionNames()`方法,可以获取所有已注册bean定义的名称数组,方便进行遍历和查看。 6. **提供已注册的所有单例bean名称** + 通过`getSingletonNames()`方法,可以获取当前已注册的所有单例bean的名称数组,方便查看和管理已实例化的bean。 7. **设置父BeanFactory** + 允许通过`setParentBeanFactory(BeanFactory parentBeanFactory)`方法设置一个父BeanFactory,使得在父工厂中查找bean定义。 8. **获取类加载器** + 通过`getBeanClassLoader()`方法获取用于加载bean类的类加载器,这在动态加载类的场景中很有用。 ### 四、接口源码 ```java /** * ConfigurableListableBeanFactory接口是大多数可列举的bean工厂应该实现的配置接口。 * 除了继承自{@link ConfigurableBeanFactory}的方法外,它提供了分析和修改bean定义以及预实例化单例bean的功能。 * *

{@link org.springframework.beans.factory.BeanFactory}的这个子接口通常不应该在正常应用程序代码中使用: * 对于典型的用例,请使用{@link org.springframework.beans.factory.BeanFactory}或 * {@link org.springframework.beans.factory.ListableBeanFactory}。 * 此接口只是为了允许在需要访问bean工厂配置方法时进行框架内部的插拔。 * * @author Juergen Hoeller * @since 03.11.2003 * @see org.springframework.context.support.AbstractApplicationContext#getBeanFactory() */ public interface ConfigurableListableBeanFactory extends ListableBeanFactory, AutowireCapableBeanFactory, ConfigurableBeanFactory { /** * 忽略指定的自动装配依赖类型,例如String。默认为空。 * @param type 要忽略的依赖类型 */ void ignoreDependencyType(Class type); /** * 忽略指定的自动装配依赖接口。 *

通常由应用程序上下文使用,以注册通过其他方式解析的依赖关系, * 例如通过BeanFactoryAware或ApplicationContextAware。 *

默认情况下,仅忽略BeanFactoryAware接口。 * 若要忽略更多类型,请为每个类型调用此方法。 * @param ifc 要忽略的依赖接口 * @see org.springframework.beans.factory.BeanFactoryAware * @see org.springframework.context.ApplicationContextAware */ void ignoreDependencyInterface(Class ifc); /** * 注册特殊的依赖类型及其对应的自动装配值。 *

这用于应该是可自动装配但在工厂中未定义为bean的工厂/上下文引用, * 例如类型为ApplicationContext的依赖,解析为bean所在的ApplicationContext实例。 *

注意:在纯BeanFactory中没有默认类型注册,甚至没有为BeanFactory接口本身注册。 * @param dependencyType 要注册的依赖类型。这通常是一个基本接口,如BeanFactory, * 如果声明为自动装配依赖(例如,ListableBeanFactory),则扩展的接口也会被解析, * 只要给定的值实际上实现了扩展接口。 * @param autowiredValue 对应的自动装配值。这也可以是org.springframework.beans.factory.ObjectFactory * 接口的实现,允许延迟解析实际目标值。 */ void registerResolvableDependency(Class dependencyType, @Nullable Object autowiredValue); /** * 确定指定的bean是否符合自动装配的条件, * 即是否可注入到声明具有匹配类型的依赖关系的其他bean中。 *

此方法还检查祖先工厂。 * @param beanName 要检查的bean的名称 * @param descriptor 要解析的依赖项的描述符 * @return bean是否应被视为自动装配候选 * @throws NoSuchBeanDefinitionException 如果没有给定名称的bean */ boolean isAutowireCandidate(String beanName, DependencyDescriptor descriptor) throws NoSuchBeanDefinitionException; /** * 返回指定bean的注册BeanDefinition,允许访问其属性值和构造函数参数值 * (可以在bean工厂后处理期间进行修改)。 *

返回的BeanDefinition对象不应该是副本,而应该是在工厂中注册的原始定义对象。 * 这意味着,如果有必要,它应该可以转换为更具体的实现类型。 *

注意:此方法不考虑祖先工厂。它仅用于访问此工厂的本地bean定义。 * @param beanName 要获取的bean的名称 * @return 注册的BeanDefinition * @throws NoSuchBeanDefinitionException 如果在此工厂中没有给定名称的bean定义 */ BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException; /** * 返回此工厂管理的所有bean名称的统一视图。 *

包括bean定义名称以及手动注册的单例实例的名称, * bean定义名称始终首先出现,类似于通过类型/注解特定检索bean名称的方式。 * @return 用于bean名称视图的复合迭代器 * @since 4.1.2 * @see #containsBeanDefinition * @see #registerSingleton * @see #getBeanNamesForType * @see #getBeanNamesForAnnotation */ Iterator getBeanNamesIterator(); /** * 清除合并的bean定义缓存,删除不符合完全元数据缓存资格的bean的条目。 *

通常在对原始bean定义进行更改后触发,例如在应用BeanFactoryPostProcessor之后。 * 请注意,此时已经创建的bean的元数据将被保留。 * @since 4.2 * @see #getBeanDefinition * @see #getMergedBeanDefinition */ void clearMetadataCache(); /** * 冻结所有bean定义,表示注册的bean定义将不再被修改或进一步后处理。 *

这允许工厂积极地缓存bean定义元数据。 */ void freezeConfiguration(); /** * 返回此工厂的bean定义是否被冻结, * 即不应再被修改或进一步后处理。 * @return 如果工厂的配置被认为已冻结,则为{@code true} */ boolean isConfigurationFrozen(); /** * 确保实例化所有非懒加载单例,还考虑到org.springframework.beans.factory.FactoryBean。 * 通常在工厂设置结束时调用,如果需要的话。 * @throws BeansException 如果无法创建其中一个单例bean。 * 注意:这可能已经使工厂具有一些已初始化的bean!在这种情况下,调用destroySingletons()进行完全清理。 * @see #destroySingletons() */ void preInstantiateSingletons() throws BeansException; } ``` ### 五、最佳实践 演示了`ConfigurableListableBeanFactory`接口的一些常用方法。包括依赖类型的忽略、注册可解析的依赖、判断是否为自动注入的候选者、获取BeanDefinition等功能的使用。 ```java public class ConfigurableListableBeanFactoryDemo { public static void main(String[] args) throws NoSuchFieldException { // 创建 ApplicationContext AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfiguration.class); // 获取 ConfigurableListableBeanFactory ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory(); // 忽略指定类型的依赖 beanFactory.ignoreDependencyType(String.class); // 忽略指定接口的依赖 beanFactory.ignoreDependencyInterface(BeanFactory.class); // 注册可解析的依赖 beanFactory.registerResolvableDependency(ApplicationContext.class, applicationContext); // 判断指定的 Bean 是否可以作为自动注入的候选者 String beanName = "myService"; DependencyDescriptor dependencyDescriptor = new DependencyDescriptor(MyController.class.getDeclaredField("myService"), false); boolean isAutowireCandidate = beanFactory.isAutowireCandidate(beanName, dependencyDescriptor); System.out.println(beanName + " 是否为自动注入的候选者: " + isAutowireCandidate); // 获取指定 Bean 的 BeanDefinition BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName); System.out.println(beanName + " 的 BeanDefinition: " + beanDefinition); // 获取所有 Bean 的名称的迭代器 Iterator beanNamesIterator = beanFactory.getBeanNamesIterator(); System.out.print("所有 Bean 的名称: "); beanNamesIterator.forEachRemaining(System.out::print); // 清除元数据缓存 beanFactory.clearMetadataCache(); // 冻结配置 beanFactory.freezeConfiguration(); // 判断配置是否已冻结 boolean isConfigurationFrozen = beanFactory.isConfigurationFrozen(); System.out.println("配置是否已冻结: " + isConfigurationFrozen); // 预实例化所有非懒加载的单例 Bean beanFactory.preInstantiateSingletons(); } } ``` ### 六、与其他组件的关系 1. **BeanFactory** - `ConfigurableListableBeanFactory` 继承自 `BeanFactory` 接口,因此它继承了 `BeanFactory` 的基本功能,如 `getBean` 等方法。 - `ConfigurableListableBeanFactory` 是 `ListableBeanFactory` 和 `ConfigurableBeanFactory` 的组合,提供了对bean列表和配置的管理能力。 2. **ListableBeanFactory** - `ConfigurableListableBeanFactory` 扩展了 `ListableBeanFactory` 接口,使得它除了具备 `ListableBeanFactory` 的能力外,还能够进行更灵活的配置和管理。 3. **AutowireCapableBeanFactory** - `ConfigurableListableBeanFactory` 扩展了 `AutowireCapableBeanFactory` 接口,使得它能够进行更高级的自动装配,包括忽略依赖类型、判断是否为自动注入的候选者等。 4. **ConfigurableBeanFactory** - `ConfigurableListableBeanFactory` 实现了 `ConfigurableBeanFactory` 接口,提供了一系列配置 bean 工厂的方法,如设置 BeanClassLoader、注册单例、设置作用域等。 5. **ApplicationContext** - `ConfigurableListableBeanFactory` 是 Spring 应用上下文(`ApplicationContext`)的一部分。通过 `ApplicationContext` 实例,可以获取到 `ConfigurableListableBeanFactory` 的引用,进而对 bean 进行配置和管理。 - 具体实现类,例如 `GenericApplicationContext` 或 `AnnotationConfigApplicationContext`,内部持有 `ConfigurableListableBeanFactory` 实例,通过该实例管理 bean 定义和实例。 6. **BeanPostProcessor** - `ConfigurableListableBeanFactory` 与 `BeanPostProcessor` 接口协同工作。`BeanPostProcessor` 允许在 bean 实例化和初始化的过程中进行自定义的处理。`ConfigurableListableBeanFactory` 通过添加 `BeanPostProcessor` 来实现对 bean 的定制化处理。 7. **BeanDefinition** - `ConfigurableListableBeanFactory` 提供了获取 `BeanDefinition` 的方法,通过 `getBeanDefinition(String beanName)` 可以获取指定 bean 的定义信息。这对于查看和修改 bean 的配置信息非常有用。 ### 七、常见问题 1. **Bean的循环依赖问题** - 当两个或多个 bean 彼此依赖形成循环依赖时,可能导致应用程序启动失败或不稳定。我们应该尽量设计避免循环依赖,考虑通过构造函数注入、setter 方法注入、`@Lazy` 注解等方式来解决。 2. **Bean的配置错误** - 配置文件中的 bean 定义存在错误,导致无法正确创建 bean。 我们应该仔细检查配置文件,确保 XML 或 Java 配置正确无误,属性值和引用关系正确。 3. **依赖注入失败** - 依赖注入失败,bean 的某些属性为 null。我们应该检查 bean 的依赖关系,确保属性注入的名称和类型正确,使用 `@Autowired` 或者 `@Resource` 注解时检查自动装配是否生效。 4. **Bean的生命周期问题** - 想要在 bean 的初始化或销毁阶段执行一些特定操作,但操作未生效。我们应该使用 `InitializingBean` 和 `DisposableBean` 接口,或者通过 `@PostConstruct` 和 `@PreDestroy` 注解在方法上执行相应的初始化和销毁逻辑。 5. **Bean的作用域问题** - 需要使用不同的作用域(例如单例、原型等),但实际应用中未生效。我们应该在配置文件或 Java 配置中明确指定 bean 的作用域,确保作用域的使用符合预期。 6. **Bean的懒加载问题** - 想要将某个 bean 设置为懒加载,但该设置未生效。我们应该使用 `@Lazy` 注解或者在 XML 配置中设置 `lazy-init="true"`,确保懒加载的配置正确。 7. **Bean的条件化注册问题** - 想要根据条件动态注册某个 bean,但条件未生效。我们应该使用条件化注解(例如 `@Conditional`)或者通过编程方式在 `ConfigurableListableBeanFactory` 中注册 bean 前进行条件判断。 8. **Bean的后置处理器问题** - 想要对所有 bean 进行额外的处理,但自定义的 `BeanPostProcessor` 未生效。我们应该确保自定义的 `BeanPostProcessor` 被正确注册,可以通过 `ConfigurableListableBeanFactory.addBeanPostProcessor` 方法添加。 ================================================ FILE: spring-factory/spring-factory-configurableListableBeanFactory/pom.xml ================================================ spring-factory com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-factory-configurableListableBeanFactory ================================================ FILE: spring-factory/spring-factory-configurableListableBeanFactory/src/main/java/com/xcs/spring/ConfigurableListableBeanFactoryDemo.java ================================================ package com.xcs.spring; import com.xcs.spring.config.MyConfiguration; import com.xcs.spring.controller.MyController; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.DependencyDescriptor; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import java.util.Iterator; /** * @author xcs * @date 2023年11月24日 14时44分 **/ public class ConfigurableListableBeanFactoryDemo { public static void main(String[] args) throws NoSuchFieldException { // 创建 ApplicationContext AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfiguration.class); // 获取 ConfigurableListableBeanFactory ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory(); // 忽略指定类型的依赖 beanFactory.ignoreDependencyType(String.class); // 忽略指定接口的依赖 beanFactory.ignoreDependencyInterface(BeanFactory.class); // 注册可解析的依赖 beanFactory.registerResolvableDependency(ApplicationContext.class, applicationContext); // 判断指定的 Bean 是否可以作为自动注入的候选者 String beanName = "myService"; DependencyDescriptor dependencyDescriptor = new DependencyDescriptor(MyController.class.getDeclaredField("myService"), false); boolean isAutowireCandidate = beanFactory.isAutowireCandidate(beanName, dependencyDescriptor); System.out.println(beanName + " 是否为自动注入的候选者: " + isAutowireCandidate); // 获取指定 Bean 的 BeanDefinition BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName); System.out.println(beanName + " 的 BeanDefinition: " + beanDefinition); // 获取所有 Bean 的名称的迭代器 Iterator beanNamesIterator = beanFactory.getBeanNamesIterator(); System.out.print("所有 Bean 的名称: "); beanNamesIterator.forEachRemaining(System.out::print); // 清除元数据缓存 beanFactory.clearMetadataCache(); // 冻结配置 beanFactory.freezeConfiguration(); // 判断配置是否已冻结 boolean isConfigurationFrozen = beanFactory.isConfigurationFrozen(); System.out.println("配置是否已冻结: " + isConfigurationFrozen); // 预实例化所有非懒加载的单例 Bean beanFactory.preInstantiateSingletons(); } } ================================================ FILE: spring-factory/spring-factory-configurableListableBeanFactory/src/main/java/com/xcs/spring/config/MyConfiguration.java ================================================ package com.xcs.spring.config; import com.xcs.spring.service.MyService; import com.xcs.spring.service.impl.MyServiceImpl; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @author xcs * @date 2023年11月24日 14时17分 **/ @Configuration public class MyConfiguration { @Bean public MyService myService() { return new MyServiceImpl(); } } ================================================ FILE: spring-factory/spring-factory-configurableListableBeanFactory/src/main/java/com/xcs/spring/controller/MyController.java ================================================ package com.xcs.spring.controller; import com.xcs.spring.service.MyService; import org.springframework.beans.factory.annotation.Autowired; /** * @author xcs * @date 2023年11月24日 14时50分 **/ public class MyController { @Autowired private MyService myService; } ================================================ FILE: spring-factory/spring-factory-configurableListableBeanFactory/src/main/java/com/xcs/spring/service/MyService.java ================================================ package com.xcs.spring.service; /** * @author xcs * @date 2023年11月24日 14时17分 **/ public interface MyService { void greet(); } ================================================ FILE: spring-factory/spring-factory-configurableListableBeanFactory/src/main/java/com/xcs/spring/service/impl/MyServiceImpl.java ================================================ package com.xcs.spring.service.impl; import com.xcs.spring.service.MyService; /** * @author xcs * @date 2023年11月24日 14时17分 **/ public class MyServiceImpl implements MyService { @Override public void greet() { System.out.println("Hello from MyService!"); } } ================================================ FILE: spring-factory/spring-factory-hierarchicalBeanFactory/README.md ================================================ ## HierarchicalBeanFactory ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 `HierarchicalBeanFactory` 接口是Spring框架中的一个接口,用于表示具有层次结构的`BeanFactory`,即支持父子容器的概念。通过继承 `BeanFactory` 接口,它定义了一些方法,使得容器能够组织成为层次结构,其中子容器可以访问父容器的 Bean 定义。 ### 三、主要功能 1. **获取父容器** - 通过 `getParentBeanFactory()` 方法,可以获取当前 `BeanFactory` 的父级 `BeanFactory`,允许在子容器中访问和使用父容器中定义的 Bean。 2. **本地 Bean 的检查** - 通过 `containsLocalBean(String name)` 方法,可以检查当前 `BeanFactory` 是否包含具有给定名称的本地 Bean。本地 Bean 是指在当前容器中定义的 Bean,而不是从父容器继承的。 ### 四、接口源码 `HierarchicalBeanFactory` 接口是由能够成为Spring容器层次结构一部分的Bean工厂实现的子接口。它定义了获取父级Bean工厂和检查本地Bean工厂是否包含指定名称的Bean的方法 ```java /** * 由能够成为层次结构一部分的Bean工厂实现的子接口。 * *

对于那些允许以可配置的方式设置父级的Bean工厂, * 相应的 {@code setParentBeanFactory} 方法可以在 ConfigurableBeanFactory 接口中找到。 * * @author Rod Johnson * @author Juergen Hoeller * @since 07.07.2003 * @see org.springframework.beans.factory.config.ConfigurableBeanFactory#setParentBeanFactory */ public interface HierarchicalBeanFactory extends BeanFactory { /** * 返回父级Bean工厂,如果没有则返回 {@code null}。 */ @Nullable BeanFactory getParentBeanFactory(); /** * 返回本地Bean工厂是否包含给定名称的Bean, * 忽略在祖先上下文中定义的Bean。 *

这是 {@code containsBean} 的替代方法,忽略来自祖先Bean工厂的给定名称的Bean。 * @param name 要查询的Bean的名称 * @return 本地工厂中是否定义了具有给定名称的Bean * @see BeanFactory#containsBean */ boolean containsLocalBean(String name); } ``` ### 五、主要实现 - `DefaultListableBeanFactory` - `DefaultListableBeanFactory`是Spring框架中实现`BeanFactory`接口的关键类之一,负责注册、管理和初始化应用程序中的所有Bean定义。它支持依赖注入、不同作用域的Bean管理、处理`FactoryBean`、层次性容器、以及各种生命周期回调等功能,是Spring IoC容器的核心实现,提供了灵活而强大的Bean管理和配置机制。 ### 六、最佳实践 创建了一个包含父子层次结构的Spring容器,其中父容器包含一个名为`MyBean`的Bean,子容器继承了父容器并尝试获取这个Bean。代码通过`HierarchicalBeanFactory`接口的方法展示了如何在子容器中访问父容器的Bean,判断本地和整个BeanFactory中是否包含特定名称的Bean,并获取父级BeanFactory。 ```java public class HierarchicalBeanFactoryDemo { public static void main(String[] args) { // 创建父级容器 AnnotationConfigApplicationContext parentContext = new AnnotationConfigApplicationContext(MyBean.class); // 创建子级容器 AnnotationConfigApplicationContext childContext = new AnnotationConfigApplicationContext(); childContext.setParent(parentContext); // 在子级 BeanFactory 中获取 bean HierarchicalBeanFactory childHierarchicalBeanFactory = childContext.getBeanFactory(); System.out.println("在子级BeanFactory中获取Bean: " + childHierarchicalBeanFactory.getBean(MyBean.class)); // 在父级 BeanFactory 中获取 bean HierarchicalBeanFactory parentHierarchicalBeanFactory = parentContext.getBeanFactory(); System.out.println("在父级BeanFactory中获取Bean: " + parentHierarchicalBeanFactory.getBean(MyBean.class)); // 获取父级 BeanFactory BeanFactory parentBeanFactory = childHierarchicalBeanFactory.getParentBeanFactory(); System.out.println("获取父级BeanFactory: " + parentBeanFactory); // 判断本地 BeanFactory 是否包含指定名称的 bean boolean containsLocalBean = childHierarchicalBeanFactory.containsLocalBean("myBean"); System.out.println("判断本地BeanFactory是否包含指定名称的Bean: " + containsLocalBean); // 判断整个 BeanFactory 是否包含指定名称的 bean boolean containsBean = childHierarchicalBeanFactory.containsBean("myBean"); System.out.println("判断整个BeanFactory是否包含指定名称的Bean: " + containsBean); } } ``` 运行结果发现,容器层次结构的特性,包括父子容器之间的Bean继承和在不同层次的容器中判断Bean的存在。 ```java 在子级BeanFactory中获取Bean: com.xcs.spring.bean.MyBean@6379eb 在父级BeanFactory中获取Bean: com.xcs.spring.bean.MyBean@6379eb 获取父级BeanFactory: org.springframework.beans.factory.support.DefaultListableBeanFactory@2f112965: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.event.internalEventListenerProcessor,org.springframework.context.event.internalEventListenerFactory,myBean]; root of factory hierarchy 判断本地BeanFactory是否包含指定名称的Bean: false 判断整个BeanFactory是否包含指定名称的Bean: true ``` ### 七、与其他组件的关系 1. **ApplicationContext接口** - `HierarchicalBeanFactory` 接口是 `ApplicationContext` 接口的子接口。因此,任何实现了 `HierarchicalBeanFactory` 的类也是 `ApplicationContext` 的子类。`ApplicationContext` 是 Spring 中应用程序上下文的核心接口,提供了更多的功能,包括事件发布、国际化支持等。 2. **ConfigurableApplicationContext接口** - `ConfigurableApplicationContext` 是 `ApplicationContext` 的子接口,同时也扩展了 `HierarchicalBeanFactory`。它定义了一些用于配置应用程序上下文的额外方法,例如设置父级上下文、激活和取消激活配置文件等。 3. **BeanFactory接口** - `HierarchicalBeanFactory` 扩展了 `BeanFactory` 接口,因此它继承了 `BeanFactory` 中定义的许多方法,用于获取和管理 Bean 实例。 4. **DefaultListableBeanFactory类** - `DefaultListableBeanFactory` 是 Spring 框架中 `HierarchicalBeanFactory` 接口的默认实现类。它实现了 `HierarchicalBeanFactory` 接口,并提供了标准的 `BeanFactory` 功能。`DefaultListableBeanFactory` 也是 `ConfigurableListableBeanFactory` 接口的实现类,进一步增强了配置的能力。 5. **BeanDefinition接口** - `HierarchicalBeanFactory` 与 `BeanDefinition` 接口密切相关。`BeanDefinition` 定义了 Bean 的元数据,包括类名、属性值、构造函数参数等。在容器层次结构中,`HierarchicalBeanFactory` 负责管理和维护这些 `BeanDefinition`。 6. **ApplicationContext层次结构** - `HierarchicalBeanFactory` 接口支持 Spring 容器的层次结构。通过父子关系,容器可以继承和覆盖 Bean 定义。这有助于实现模块化和组织化的应用程序架构,不同层次的容器之间可以共享或隔离 Bean。 ### 八、常见问题 1. **Bean名称冲突** - 当子容器和父容器中都定义了相同名称的Bean时,可能会导致Bean名称冲突。在设计时,避免在父子容器中定义相同名称的Bean。如果冲突是不可避免的,可以通过在子容器中重新定义Bean来覆盖父容器中的定义。 2. **父子容器的初始化顺序** - 容器的初始化顺序可能会导致在子容器初始化之前尝试访问父容器中的Bean。我们需要确保在访问子容器中的Bean之前,子容器已经成功初始化。可以使用事件监听器等机制来确保容器的正确初始化顺序。 3. **父容器中Bean的更新** - 子容器在初始化时会获取父容器中的Bean定义,并且在后续运行时不会再次更新。如果需要动态更新父容器中的Bean定义,可以考虑在特定的时机重新刷新子容器。 4. **对父容器中Bean的引用** - 子容器中的Bean可以直接引用父容器中的Bean。Spring会自动处理这些引用,确保在子容器中正确注入父容器中的Bean。 5. **层次结构的深度** - 尽管Spring支持容器层次结构,但在设计时要考虑层次结构的深度。深层次结构可能会导致容器初始化和访问的复杂性,可能影响性能。 6. **配置错误** - 定期审查配置,确保正确设置了父子容器关系。使用合适的配置和注解来避免潜在的错误。 ================================================ FILE: spring-factory/spring-factory-hierarchicalBeanFactory/pom.xml ================================================ spring-factory com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-factory-hierarchicalBeanFactory ================================================ FILE: spring-factory/spring-factory-hierarchicalBeanFactory/src/main/java/com/xcs/spring/HierarchicalBeanFactoryDemo.java ================================================ package com.xcs.spring; import com.xcs.spring.bean.MyBean; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.HierarchicalBeanFactory; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年11月23日 20时22分 **/ public class HierarchicalBeanFactoryDemo { public static void main(String[] args) { // 创建父级容器 AnnotationConfigApplicationContext parentContext = new AnnotationConfigApplicationContext(MyBean.class); // 创建子级容器 AnnotationConfigApplicationContext childContext = new AnnotationConfigApplicationContext(); childContext.setParent(parentContext); // 在子级 BeanFactory 中获取 bean HierarchicalBeanFactory childHierarchicalBeanFactory = childContext.getBeanFactory(); System.out.println("在子级BeanFactory中获取Bean: " + childHierarchicalBeanFactory.getBean(MyBean.class)); // 在父级 BeanFactory 中获取 bean HierarchicalBeanFactory parentHierarchicalBeanFactory = parentContext.getBeanFactory(); System.out.println("在父级BeanFactory中获取Bean: " + parentHierarchicalBeanFactory.getBean(MyBean.class)); // 获取父级 BeanFactory BeanFactory parentBeanFactory = childHierarchicalBeanFactory.getParentBeanFactory(); System.out.println("获取父级BeanFactory: " + parentBeanFactory); // 判断本地 BeanFactory 是否包含指定名称的 bean boolean containsLocalBean = childHierarchicalBeanFactory.containsLocalBean("myBean"); System.out.println("判断本地BeanFactory是否包含指定名称的Bean: " + containsLocalBean); // 判断整个 BeanFactory 是否包含指定名称的 bean boolean containsBean = childHierarchicalBeanFactory.containsBean("myBean"); System.out.println("判断整个BeanFactory是否包含指定名称的Bean: " + containsBean); } } ================================================ FILE: spring-factory/spring-factory-hierarchicalBeanFactory/src/main/java/com/xcs/spring/bean/MyBean.java ================================================ package com.xcs.spring.bean; /** * @author xcs * @date 2023年11月23日 20时40分 **/ public class MyBean { } ================================================ FILE: spring-factory/spring-factory-listableBeanFactory/README.md ================================================ ## ListableBeanFactory ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 `ListableBeanFactory` 接口是 Spring 框架中的一个子接口,扩展了 `BeanFactory`,用于表示能够以列表形式获取 bean 定义的容器。它提供了方法来检索容器中所有 bean 的数量、名称,以及按类型或注解过滤的 bean 实例。通过这个接口,我们可以方便地获取有关容器中 bean 的信息,如动态查找 bean 名称、按类型检索 bean 实例等,为运行时动态管理和处理 bean 提供了灵活性和便利性。 ### 三、主要功能 1. **获取 Bean 定义数量** - 该方法返回容器中定义的 bean 的数量,允许我们了解容器中注册的所有 bean 的总数。 2. **获取所有 Bean 的名称** - 通过此方法,可以获取容器中所有 bean 的名称,提供了一个 bean 名称的列表。这对于遍历和检查容器中的所有 bean 是很有用的。 3. **按类型获取 Bean 的名称** - 通过指定类型,可以获取容器中所有与该类型兼容的 bean 的名称。这对于按照特定类型查找和处理 bean 是非常方便的。 4. **按类型获取 Bean 实例** - 该方法返回指定类型的所有 bean 实例,以 bean 的名称和实例的映射形式。这是一个强大的功能,特别是在需要按类型进行动态处理的情况下。 5. **按注解获取 Bean 实例** - 允许我们根据指定的注解获取所有带有该注解的 bean 实例,以 bean 的名称和实例的映射形式返回。这在基于注解的配置和处理中非常有用。 ### 四、接口源码 ```java /** * {@link BeanFactory}接口的扩展,由可以枚举其所有bean实例而不是按名称一个个查找bean的工厂实现。 * 预加载所有bean定义(例如基于XML的工厂)的BeanFactory可以实现此接口。 * *

如果这是一个{@link HierarchicalBeanFactory},返回值将不会考虑任何BeanFactory层次结构, * 而只会与当前工厂中定义的bean相关联。使用{@link BeanFactoryUtils}助手类来考虑祖先工厂中的bean。 * *

此接口中的方法只会尊重此工厂的bean定义。它们将忽略通过其他手段注册的任何单例bean, * 例如{@link org.springframework.beans.factory.config.ConfigurableBeanFactory}的 * {@code registerSingleton}方法,但 {@code getBeanNamesForType} 和 {@code getBeansOfType} * 将检查这样手动注册的单例。当然,BeanFactory的 {@code getBean} 也允许透明访问这样的特殊bean。 * 但是,在典型情况下,所有bean都将由外部bean定义来定义,因此大多数应用程序不需要担心这种区分。 * *

注意:除了 {@code getBeanDefinitionCount} 和 {@code containsBeanDefinition}, * 此接口中的方法并非设计用于频繁调用。实现可能会很慢。 * * @author Rod Johnson * @author Juergen Hoeller * @since 2001年4月16日 * @see HierarchicalBeanFactory * @see BeanFactoryUtils */ public interface ListableBeanFactory extends BeanFactory { /** * 检查此bean工厂是否包含给定名称的bean定义。 *

不考虑此工厂可能参与的任何层次结构,并忽略通过bean定义之外的其他手段注册的任何单例bean。 * @param beanName 要查找的bean的名称 * @return 如果此bean工厂包含给定名称的bean定义,则为true * @see #containsBean */ boolean containsBeanDefinition(String beanName); /** * 返回工厂中定义的bean的数量。 *

不考虑此工厂可能参与的任何层次结构,并忽略通过bean定义之外的其他手段注册的任何单例bean。 * @return 工厂中定义的bean的数量 */ int getBeanDefinitionCount(); /** * 返回此工厂中定义的所有bean的名称。 *

不考虑此工厂可能参与的任何层次结构,并忽略通过bean定义之外的其他手段注册的任何单例bean。 * @return 此工厂中定义的所有bean的名称,如果没有定义,则为空数组 */ String[] getBeanDefinitionNames(); /** * 返回指定bean的提供程序,允许按需延迟检索实例,包括可用性和唯一性选项。 * @param requiredType bean必须匹配的类型;可以是接口或超类 * @param allowEagerInit 是否允许基于流的访问初始化懒加载单例由FactoryBeans创建的对象 * (或通过带有 "factory-bean" 引用的工厂方法创建的对象)进行类型检查 * @return 相应的提供程序处理 * @since 5.3 * @see #getBeanProvider(ResolvableType, boolean) * @see #getBeanProvider(Class) * @see #getBeansOfType(Class, boolean, boolean) * @see #getBeanNamesForType(Class, boolean, boolean) */ ObjectProvider getBeanProvider(Class requiredType, boolean allowEagerInit); /** * 返回指定bean的提供程序,允许按需延迟检索实例,包括可用性和唯一性选项。 * @param requiredType bean必须匹配的类型;可以是泛型类型声明。 * 请注意,此处不支持集合类型,与反射注入点形成对比。要以编程方式检索匹配特定类型的bean列表,请在此处指定实际的bean类型, * 然后随后使用{@link ObjectProvider#orderedStream()}或其延迟流/迭代选项。 * @param allowEagerInit 是否允许基于流的访问初始化懒加载单例由FactoryBeans创建的对象 * (或通过带有 "factory-bean" 引用的工厂方法创建的对象)进行类型检查 * @return 相应的提供程序处理 * @since 5.3 * @see #getBeanProvider(ResolvableType) * @see ObjectProvider#iterator() * @see ObjectProvider#stream() * @see ObjectProvider#orderedStream() * @see #getBeanNamesForType(ResolvableType, boolean, boolean) */ ObjectProvider getBeanProvider(ResolvableType requiredType, boolean allowEagerInit); /** * 返回使用提供的{@link Annotation}类型进行注释的所有bean的名称,而不会实际创建相应的bean实例。 *

请注意,此方法会考虑由FactoryBeans创建的对象,这意味着将初始化FactoryBeans以确定其对象类型。 * @param annotationType 要查找的注释类型 * (在指定bean的类、接口或工厂方法级别) * @return 所有匹配的bean的名称 * @since 4.0 * @see #findAnnotationOnBean */ String[] getBeanNamesForAnnotation(Class annotationType); /** * 查找使用提供的{@link Annotation}类型进行注释的所有bean,返回具有相应的bean名称和相应bean实例的Map。 *

请注意,此方法会考虑由FactoryBeans创建的对象,这意味着将初始化FactoryBeans以确定其对象类型。 * @param annotationType 要查找的注释类型 * (在指定bean的类、接口或工厂方法级别) * @return 包含匹配bean的Map,其中包含bean名称作为键和相应的bean实例作为值 * @throws BeansException 如果无法创建bean * @since 3.0 * @see #findAnnotationOnBean */ Map getBeansWithAnnotation(Class annotationType) throws BeansException; /** * 在指定的bean上查找{@code annotationType}的{@link Annotation}, * 遍历其接口和超类如果在给定类本身上找不到注释,则检查bean的工厂方法(如果有的话)。 * @param beanName 要查找注释的bean的名称 * @param annotationType 要查找的注释类型 * (在指定bean的类、接口或工厂方法级别) * @return 如果找到给定类型的注释,则为该注释;否则为{@code null} * @throws NoSuchBeanDefinitionException 如果没有给定名称的bean * @since 3.0 * @see #getBeanNamesForAnnotation * @see #getBeansWithAnnotation */ @Nullable A findAnnotationOnBean(String beanName, Class annotationType) throws NoSuchBeanDefinitionException; /** * 返回使用提供的{@link Annotation}类型进行注释的所有bean的名称, * 返回具有相应的bean名称和相应bean实例的Map。 *

请注意,此方法会考虑由FactoryBeans创建的对象,这意味着将初始化FactoryBeans以确定其对象类型。 * @param annotationType 要查找的注释类型 * (在指定bean的类、接口或工厂方法级别) * @return 包含匹配bean的Map,其中包含bean名称作为键和相应的bean实例作为值 * @throws BeansException 如果无法创建bean * @since 3.0 * @see #findAnnotationOnBean */ Map getBeansWithAnnotation(Class annotationType) throws BeansException; /** * 在指定的bean上查找{@code annotationType}的{@link Annotation}, * 遍历其接口和超类如果在给定类本身上找不到注释,则检查bean的工厂方法(如果有的话)。 * @param beanName 要查找注释的bean的名称 * @param annotationType 要查找的注释类型 * (在指定bean的类、接口或工厂方法级别) * @return 如果找到给定类型的注释,则为该注释;否则为{@code null} * @throws NoSuchBeanDefinitionException 如果没有给定名称的bean * @since 3.0 * @see #getBeanNamesForAnnotation * @see #getBeansWithAnnotation */ @Nullable A findAnnotationOnBean(String beanName, Class annotationType) throws NoSuchBeanDefinitionException; /** * 返回与给定对象类型(包括子类)匹配的所有bean的名称,从bean定义或FactoryBeans的{@code getObjectType}的值判断。 *

注意:此方法仅内省顶级bean。它不会检查可能也匹配指定类型的嵌套bean。 *

如果FactoryBeans创建的对象符合条件,它将得到初始化。如果FactoryBean创建的对象不匹配, * 则原始FactoryBean本身将与类型匹配。 *

不考虑此工厂可能参与的任何层次结构。使用BeanFactoryUtils的{@code beanNamesForTypeIncludingAncestors} * 将祖先工厂中的bean包括在内。 *

注意:不会忽略通过bean定义之外的其他手段注册的任何单例bean。 *

此版本的{@code getBeanNamesForType}匹配所有类型的bean,无论是单例、原型还是FactoryBeans。 * 在大多数实现中,结果与{@code getBeanNamesForType(type, true, true)}相同。 *

此方法返回的bean名称应始终尽可能以后端配置中的定义顺序返回bean名称。 * @param type 要匹配的泛型类型或接口 * @return 匹配的对象类型(包括子类)的所有bean的名称,如果没有则为空数组 * @since 4.2 * @see #isTypeMatch(String, ResolvableType) * @see FactoryBean#getObjectType * @see BeanFactoryUtils#beanNamesForTypeIncludingAncestors(ListableBeanFactory, ResolvableType) */ String[] getBeanNamesForType(ResolvableType type); /** * 根据给定的类型(包括子类),从bean定义或FactoryBeans的{@code getObjectType}的值判断,返回匹配的bean的名称数组。 *

注意:此方法仅内省顶级bean。它不检查可能与指定类型匹配的嵌套bean。 *

如果设置了“allowEagerInit”标志,会考虑由FactoryBeans创建的对象,这意味着FactoryBeans将被初始化。 * 如果由FactoryBean创建的对象不匹配,则原始FactoryBean本身将与类型匹配。 * 如果未设置“allowEagerInit”,则仅检查原始FactoryBeans(这不需要初始化每个FactoryBean)。 *

不考虑此工厂可能参与的任何层次结构。 * 使用BeanFactoryUtils的{@code beanNamesForTypeIncludingAncestors}以包括祖先工厂中的bean。 *

注意:不会忽略以非bean定义的方式注册的单例bean。 *

此方法返回的bean名称数组应尽可能以后端配置中定义的顺序返回bean名称。 * * @param type 要匹配的泛型类型或接口 * @param includeNonSingletons 是否包括原型或作用域bean,或仅包括单例(也适用于FactoryBeans) * @param allowEagerInit 是否初始化延迟初始化的单例和 * 由FactoryBeans创建的对象(或带有 * "factory-bean"引用的工厂方法)以进行类型检查。 * 请注意,需要急切地初始化FactoryBeans和"factory-bean"引用,因此 * 请注意,为此标志传递“true”将初始化FactoryBeans和“factory-bean”引用。 * @return 匹配给定对象类型(包括子类)的bean的名称数组,如果没有则为空数组 * @since 5.2 * @see FactoryBean#getObjectType * @see BeanFactoryUtils#beanNamesForTypeIncludingAncestors(ListableBeanFactory, ResolvableType, boolean, boolean) */ String[] getBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit); /** * 根据给定类型(包括子类)返回匹配的bean的名称数组,根据bean定义或FactoryBeans的{@code getObjectType}的值判断。 *

注意:此方法仅内省顶级bean。它不检查可能与指定类型匹配的嵌套bean。 *

会考虑由FactoryBeans创建的对象,这意味着FactoryBeans将被初始化。 * 如果由FactoryBean创建的对象不匹配,则原始FactoryBean本身将与类型匹配。 *

不考虑此工厂可能参与的任何层次结构。 * 使用BeanFactoryUtils的{@code beanNamesForTypeIncludingAncestors}以包括祖先工厂中的bean。 *

注意:不会忽略以非bean定义的方式注册的单例bean。 *

此版本的{@code getBeanNamesForType}匹配所有类型的bean,无论是单例,原型还是FactoryBeans。 * 在大多数实现中,结果将与{@code getBeanNamesForType(type, true, true)}的结果相同。 *

此方法返回的bean名称数组应尽可能以后端配置中定义的顺序返回bean名称。 * * @param type 要匹配的类或接口,或{@code null}表示所有bean名称 * @return 匹配给定对象类型(包括子类)的bean的名称数组,如果没有则为空数组 * @see FactoryBean#getObjectType * @see BeanFactoryUtils#beanNamesForTypeIncludingAncestors(ListableBeanFactory, Class) */ String[] getBeanNamesForType(@Nullable Class type); /** * 根据给定类型(包括子类)返回匹配的bean的名称数组,根据bean定义或FactoryBeans的{@code getObjectType}的值判断。 *

注意:此方法仅内省顶级bean。它不检查可能与指定类型匹配的嵌套bean。 *

如果设置了“allowEagerInit”标志,会考虑由FactoryBeans创建的对象,这意味着FactoryBeans将被初始化。 * 如果由FactoryBean创建的对象不匹配,则原始FactoryBean本身将与类型匹配。 * 如果未设置“allowEagerInit”,则仅检查原始FactoryBeans(这不需要初始化每个FactoryBean)。 *

不考虑此工厂可能参与的任何层次结构。 * 使用BeanFactoryUtils的{@code beanNamesForTypeIncludingAncestors}以包括祖先工厂中的bean。 *

注意:不会忽略以非bean定义的方式注册的单例bean。 *

此方法返回的bean名称数组应尽可能以后端配置中定义的顺序返回bean名称。 * * @param type 要匹配的类或接口,或{@code null}表示所有bean名称 * @param includeNonSingletons 是否包括原型或作用域bean,或仅包括单例(也适用于FactoryBeans) * @param allowEagerInit 是否初始化延迟初始化的单例和 * 由FactoryBeans创建的对象(或带有 * "factory-bean"引用的工厂方法)以进行类型检查。 * 请注意,需要急切地初始化FactoryBeans和"factory-bean"引用,因此 * 请注意,为此标志传递“true”将初始化FactoryBeans和“factory-bean”引用。 * @return 匹配给定对象类型(包括子类)的bean的名称数组,如果没有则为空数组 * @see FactoryBean#getObjectType * @see BeanFactoryUtils#beanNamesForTypeIncludingAncestors(ListableBeanFactory, Class, boolean, boolean) */ String[] getBeanNamesForType(@Nullable Class type, boolean includeNonSingletons, boolean allowEagerInit); /** * 根据给定的对象类型(包括子类),从bean定义或FactoryBeans的{@code getObjectType}的值判断, * 返回匹配的bean实例Map。 *

注意:此方法仅内省顶级bean。它不检查可能与指定类型匹配的嵌套bean。 *

会考虑由FactoryBeans创建的对象,这意味着FactoryBeans将被初始化。 * 如果由FactoryBean创建的对象不匹配,则原始FactoryBean本身将与类型匹配。 *

不考虑此工厂可能参与的任何层次结构。 * 使用BeanFactoryUtils的{@code beansOfTypeIncludingAncestors}以包括祖先工厂中的bean。 *

注意:不会忽略以非bean定义的方式注册的单例bean。 *

此版本的getBeansOfType匹配所有类型的bean,无论是单例,原型还是FactoryBeans。 * 在大多数实现中,结果将与{@code getBeansOfType(type, true, true)}的结果相同。 *

此方法返回的Map应尽可能以后端配置中定义的顺序返回bean名称和相应的bean实例。 * * @param type 要匹配的类或接口,或{@code null}表示所有具体bean * @return 匹配的bean的Map,其中包含bean名称作为键,相应的bean实例作为值 * @throws BeansException 如果无法创建bean * @since 1.1.2 * @see FactoryBean#getObjectType * @see BeanFactoryUtils#beansOfTypeIncludingAncestors(ListableBeanFactory, Class) */ Map getBeansOfType(@Nullable Class type) throws BeansException; /** * 根据给定对象类型(包括子类),从bean定义或FactoryBeans的{@code getObjectType}的值判断, * 返回匹配的bean实例Map。 *

注意:此方法仅内省顶级bean。它不检查可能与指定类型匹配的嵌套bean。 *

如果设置了“allowEagerInit”标志,会考虑由FactoryBeans创建的对象, * 这意味着FactoryBeans将被初始化。如果由FactoryBean创建的对象不匹配, * 则原始FactoryBean本身将与类型匹配。 *

不考虑此工厂可能参与的任何层次结构。 * 使用BeanFactoryUtils的{@code beansOfTypeIncludingAncestors}以包括祖先工厂中的bean。 *

注意:不会忽略以非bean定义的方式注册的单例bean。 *

此方法返回的Map应尽可能以后端配置中定义的顺序返回bean名称和相应的bean实例。 * * @param type 要匹配的类或接口,或{@code null}表示所有具体bean * @param includeNonSingletons 是否包括原型或作用域bean,或仅包括单例(也适用于FactoryBeans) * @param allowEagerInit 是否初始化延迟初始化的单例和 * 由FactoryBeans创建的对象(或带有 * "factory-bean"引用的工厂方法)以进行类型检查。 * 请注意,需要急切地初始化FactoryBeans和"factory-bean"引用,因此 * 请注意,为此标志传递“true”将初始化FactoryBeans和“factory-bean”引用。 * @return 匹配的bean的Map,其中包含bean名称作为键,相应的bean实例作为值 * @throws BeansException 如果无法创建bean * @see FactoryBean#getObjectType * @see BeanFactoryUtils#beansOfTypeIncludingAncestors(ListableBeanFactory, Class, boolean, boolean) */ Map getBeansOfType(@Nullable Class type, boolean includeNonSingletons, boolean allowEagerInit) throws BeansException; /** * 查找所有具有提供的{@link Annotation}类型注释的bean的名称,尚未创建相应的bean实例。 *

请注意,此方法考虑由FactoryBeans创建的对象,这意味着FactoryBeans将初始化以确定其对象类型。 * * @param annotationType 要查找的注解类型 * (在指定bean的类、接口或工厂方法级别) * @return 所有匹配bean的名称 * @since 4.0 * @see #findAnnotationOnBean */ String[] getBeanNamesForAnnotation(Class annotationType); /** * 查找所有具有提供的{@link Annotation}类型注释的bean,返回包含bean名称作为键和相应bean实例作为值的Map。 *

请注意,此方法考虑由FactoryBeans创建的对象,这意味着FactoryBeans将初始化以确定其对象类型。 * * @param annotationType 要查找的注解类型 * (在指定bean的类、接口或工厂方法级别) * @return 包含匹配的bean的Map,其中包含bean名称作为键,相应的bean实例作为值 * @throws BeansException 如果无法创建bean * @since 3.0 * @see #findAnnotationOnBean */ Map getBeansWithAnnotation(Class annotationType) throws BeansException; /** * 在指定的bean上查找{@code annotationType}类型的{@link Annotation}, * 如果在给定类本身找不到注解,还会遍历其接口和超类,以及检查bean的工厂方法(如果有)。 * * @param beanName 要查找注解的bean的名称 * @param annotationType 要查找的注解类型 * (在指定bean的类、接口或工厂方法级别) * @return 如果找到,返回给定类型的注解,否则返回{@code null} * @throws NoSuchBeanDefinitionException 如果没有给定名称的bean * @since 3.0 * @see #getBeanNamesForAnnotation * @see #getBeansWithAnnotation */ @Nullable A findAnnotationOnBean(String beanName, Class annotationType) throws NoSuchBeanDefinitionException; } ``` ### 五、主要实现 - `DefaultListableBeanFactory` - `DefaultListableBeanFactory`是Spring框架中实现`BeanFactory`接口的关键类之一,负责注册、管理和初始化应用程序中的所有Bean定义。它支持依赖注入、不同作用域的Bean管理、处理`FactoryBean`、层次性容器、以及各种生命周期回调等功能,是Spring IoC容器的核心实现,提供了灵活而强大的Bean管理和配置机制。 ### 六、最佳实践 使用`ListableBeanFactory`接口的方法,通过Spring容器获取和检查bean定义和实例。给大家展示了如何判断是否包含特定名称的bean定义,获取所有bean定义的数量和名称,懒加载获取bean实例,根据类型和注解获取bean名称和实例,以及在指定bean上查找指定类型的注解。 ```java public class ListableBeanFactoryDemo { public static void main(String[] args) { // 创建 ListableBeanFactory ListableBeanFactory beanFactory = new AnnotationConfigApplicationContext(MyConfiguration.class).getBeanFactory(); // 判断是否包含指定名称的 bean 定义 boolean containsBeanDefinition = beanFactory.containsBeanDefinition("myService"); System.out.println("判断是否包含指定名称的Bean定义: " + containsBeanDefinition); // 获取工厂中所有 bean 定义的数量 int beanDefinitionCount = beanFactory.getBeanDefinitionCount(); System.out.println("获取工厂中所有Bean定义数量: " + beanDefinitionCount); // 获取工厂中所有 bean 定义的名称数组 String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames(); System.out.println("获取工厂中所有Bean定义名称: " + String.join(", ", beanDefinitionNames)); // 获取 ObjectProvider,并懒加载获取 bean 实例 ObjectProvider objectProvider = beanFactory.getBeanProvider(MyService.class, true); System.out.println("获取Bean的ObjectProvider: " + objectProvider.getObject()); // 根据类型获取所有 bean 的名称 String[] beanNamesForType = beanFactory.getBeanNamesForType(ResolvableType.forClass(MyService.class)); System.out.println("根据类型获取Bean名称: " + String.join(", ", beanNamesForType)); // 根据注解类型获取所有 bean 的名称 String[] beanNamesForAnnotation = beanFactory.getBeanNamesForAnnotation(Service.class); System.out.println("根据注解获取Bean名称: " + String.join(", ", beanNamesForAnnotation)); // 根据注解类型获取所有 bean 实例 Map beansWithAnnotation = beanFactory.getBeansWithAnnotation(Service.class); System.out.println("根据注解类型获取所有Bean实例: " + beansWithAnnotation); // 在指定 bean 上查找指定类型的注解 Service annotation = beanFactory.findAnnotationOnBean("myService", Service.class); System.out.println("指定Bean上查找指定类型的注解: " + annotation); } } ``` `MyConfiguration` 类扫描包 "`com.xcs.spring.service`" 以查找Spring组件,如 `@Component`、`@Service`、`@Repository` 和 `@Controller`。Spring将识别带有这些注解的类,并在应用程序上下文中将它们注册为Bean。 ```java @Configuration @ComponentScan("com.xcs.spring.service") public class MyConfiguration { } ``` 通过 `@Service` 注解告诉Spring框架它是一个被管理的组件。 ```java @Service public class MyService { } ``` 运行结果发现,Spring容器中Bean的定义、数量、名称,以及通过不同方式获取Bean的相关信息。 ```java 判断是否包含指定名称的Bean定义: true 获取工厂中所有Bean定义数量: 6 获取工厂中所有Bean定义名称: org.springframework.context.annotation.internalConfigurationAnnotationProcessor, org.springframework.context.annotation.internalAutowiredAnnotationProcessor, org.springframework.context.event.internalEventListenerProcessor, org.springframework.context.event.internalEventListenerFactory, myConfiguration, myService 获取Bean的ObjectProvider: com.xcs.spring.service.MyService@4b0d79fc 根据类型获取Bean名称: myService 根据注解获取Bean名称: myService 根据注解类型获取所有Bean实例: {myService=com.xcs.spring.service.MyService@4b0d79fc} 指定Bean上查找指定类型的注解: @org.springframework.stereotype.Service(value=) ``` ### 七、与其他组件的关系 1. **`BeanFactory` 接口** - `ListableBeanFactory` 继承自 `BeanFactory` 接口,因此拥有 `BeanFactory` 的基本功能,包括获取 Bean、判断 Bean 是否存在等。 2. **`ApplicationContext` 接口** - `ApplicationContext` 是 `BeanFactory` 的子接口,它继承了 `ListableBeanFactory`,因此拥有了 `ListableBeanFactory` 的所有功能。`ApplicationContext` 还提供了更多的应用级别的功能,例如国际化、事件发布等。 3. **`ConfigurableListableBeanFactory` 接口** - `ConfigurableListableBeanFactory` 是 `ListableBeanFactory` 的子接口,继承了 `ListableBeanFactory` 的功能,并在此基础上提供了配置修改的方法,例如注册 Bean 定义、销毁 Bean 等。 4. **`AbstractApplicationContext` 抽象类** - `AbstractApplicationContext` 是 `ApplicationContext` 接口的一个抽象实现,它实现了 `ConfigurableListableBeanFactory` 接口,包含了 `ListableBeanFactory` 和 `ConfigurableListableBeanFactory` 的功能。 5. **`GenericApplicationContext` 类** - `GenericApplicationContext` 是 `AbstractApplicationContext` 的具体实现,它可以通过配置文件、注解等方式进行配置,同时提供了 `ListableBeanFactory` 和 `ConfigurableListableBeanFactory` 的功能。 6. **`AnnotationConfigApplicationContext` 类:** - `AnnotationConfigApplicationContext` 是 `GenericApplicationContext` 的一个具体实现,它通过基于注解的配置类来初始化 Spring 容器,同时支持 `ListableBeanFactory` 和 `ConfigurableListableBeanFactory` 接口。 ### 八、常见问题 1. **无法获取到指定类型的Bean** - Bean 的定义未被扫描或注册到 Spring 容器中。确保相关包或类被包含在组件扫描的范围内,或手动配置相关的 Bean 定义。 2. **Bean数量不符合预期** - 组件扫描范围、条件过滤不准确,或者配置类中存在错误。仔细检查组件扫描的配置、条件过滤条件,确保配置类正确无误。 3. **`ObjectProvider` 无法获取到实例** - 目标类型的 Bean 不存在,或者存在多个符合条件的 Bean。确保目标类型的 Bean 在容器中存在且符合条件,或者使用 `ObjectProvider` 的其他方法进行更灵活的操作。 4. **`getBeansOfType` 返回的结果为空** - 目标类型的 Bean 在容器中不存在。确保目标类型的 Bean 在容器中存在,并检查是否符合条件。 5. **Bean名称顺序不符合期望** - Spring 容器中的 Bean 注册顺序和期望的不一致。对于有顺序要求的情况,可以考虑使用 `@Order` 注解或其他方式指定 Bean 注册顺序。 ================================================ FILE: spring-factory/spring-factory-listableBeanFactory/pom.xml ================================================ spring-factory com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-factory-listableBeanFactory ================================================ FILE: spring-factory/spring-factory-listableBeanFactory/src/main/java/com/xcs/spring/ListableBeanFactoryDemo.java ================================================ package com.xcs.spring; import com.xcs.spring.config.MyConfiguration; import com.xcs.spring.service.MyService; import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.ObjectProvider; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.core.ResolvableType; import org.springframework.stereotype.Service; import java.util.Map; /** * @author xcs * @date 2023年11月23日 20时01分 **/ public class ListableBeanFactoryDemo { public static void main(String[] args) { // 创建 ListableBeanFactory ListableBeanFactory beanFactory = new AnnotationConfigApplicationContext(MyConfiguration.class).getBeanFactory(); // 判断是否包含指定名称的 bean 定义 boolean containsBeanDefinition = beanFactory.containsBeanDefinition("myService"); System.out.println("判断是否包含指定名称的Bean定义: " + containsBeanDefinition); // 获取工厂中所有 bean 定义的数量 int beanDefinitionCount = beanFactory.getBeanDefinitionCount(); System.out.println("获取工厂中所有Bean定义数量: " + beanDefinitionCount); // 获取工厂中所有 bean 定义的名称数组 String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames(); System.out.println("获取工厂中所有Bean定义名称: " + String.join(", ", beanDefinitionNames)); // 获取 ObjectProvider,并懒加载获取 bean 实例 ObjectProvider objectProvider = beanFactory.getBeanProvider(MyService.class, true); System.out.println("获取Bean的ObjectProvider: " + objectProvider.getObject()); // 根据类型获取所有 bean 的名称 String[] beanNamesForType = beanFactory.getBeanNamesForType(ResolvableType.forClass(MyService.class)); System.out.println("根据类型获取Bean名称: " + String.join(", ", beanNamesForType)); // 根据注解类型获取所有 bean 的名称 String[] beanNamesForAnnotation = beanFactory.getBeanNamesForAnnotation(Service.class); System.out.println("根据注解获取Bean名称: " + String.join(", ", beanNamesForAnnotation)); // 根据注解类型获取所有 bean 实例 Map beansWithAnnotation = beanFactory.getBeansWithAnnotation(Service.class); System.out.println("根据注解类型获取所有Bean实例: " + beansWithAnnotation); // 在指定 bean 上查找指定类型的注解 Service annotation = beanFactory.findAnnotationOnBean("myService", Service.class); System.out.println("指定Bean上查找指定类型的注解: " + annotation); } } ================================================ FILE: spring-factory/spring-factory-listableBeanFactory/src/main/java/com/xcs/spring/config/MyConfiguration.java ================================================ package com.xcs.spring.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan("com.xcs.spring.service") public class MyConfiguration { } ================================================ FILE: spring-factory/spring-factory-listableBeanFactory/src/main/java/com/xcs/spring/service/MyService.java ================================================ package com.xcs.spring.service; import org.springframework.stereotype.Service; /** * @author xcs * @date 2023年11月23日 20时04分 **/ @Service public class MyService { } ================================================ FILE: spring-interface/pom.xml ================================================ spring-reading com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-interface pom spring-interface-beanFactoryPostProcessor spring-interface-beanPostProcessor spring-interface-beanDefinitionRegistryPostProcessor spring-interface-instantiationAwareBeanPostProcessor spring-interface-destructionAwareBeanPostProcessor spring-interface-mergedBeanDefinitionPostProcessor spring-interface-smartInstantiationAwareBeanPostProcessor spring-interface-initializingBean spring-interface-disposableBean spring-interface-smartInitializingSingleton ================================================ FILE: spring-interface/spring-interface-beanDefinitionRegistryPostProcessor/README.md ================================================ ## BeanDefinitionRegistryPostProcessor - [BeanDefinitionRegistryPostProcessor](#beandefinitionregistrypostprocessor) - [一、基本信息](#一基本信息) - [二、接口描述](#二接口描述) - [三、接口源码](#三接口源码) - [四、主要功能](#四主要功能) - [五、最佳实践](#五最佳实践) - [六、时序图](#六时序图) - [七、源码分析](#七源码分析) - [八、注意事项](#八注意事项) - [九、总结](#九总结) - [最佳实践总结](#最佳实践总结) - [源码分析总结](#源码分析总结) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [我的CSDN](https://blog.csdn.net/duzhuang2399/article/details/133844850) 📚 **文章目录** - [所有文章](https://github.com/xuchengsheng/spring-reading) 🔗 **源码地址** - [BeanDefinitionRegistryPostProcessor源码](https://github.com/xuchengsheng/spring-reading/tree/master/spring-interface/spring-interface-beanDefinitionRegistryPostProcessor) ### 二、接口描述 `BeanDefinitionRegistryPostProcessor` 是 Spring 框架中的一个接口,它用于在 Spring 容器的标准初始化过程中允许我们修改应用程序上下文的内部 bean 定义。它继承自 `BeanFactoryPostProcessor` 接口。与 `BeanFactoryPostProcessor` 不同的是,它还提供了对 `BeanDefinitionRegistry` 的访问,这使得我们能够在运行时注册新的 beans 或修改现有的 bean 定义。 ### 三、接口源码 `InstantiationAwareBeanPostProcessor` 是 Spring 框架自 3.0.1 版本开始引入的一个核心接口。最核心的方法是 `postProcessBeanDefinitionRegistry`,它允许我们在运行时注册新的 beans 或修改现有的 bean 定义。 ```java /** * 相对于标准的 {@link BeanFactoryPostProcessor} SPI 的扩展, * 允许在常规 BeanFactoryPostProcessor 检测启动之前 注册更多的 bean 定义。 * 特别地,BeanDefinitionRegistryPostProcessor 可以注册进一步的 bean 定义, * 这些定义可能会进一步定义 BeanFactoryPostProcessor 实例。 * * 作者:Juergen Hoeller * 自版本:3.0.1 起 * 参见:org.springframework.context.annotation.ConfigurationClassPostProcessor */ public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor { /** * 在其标准初始化之后,修改应用上下文的内部 bean 定义注册表。 * 此时,所有常规的 bean 定义都已经被加载,但还没有 bean 被实例化。 * 这允许在下一后处理阶段开始之前,添加更多的 bean 定义。 * * @param registry 应用上下文使用的 bean 定义注册表 * @throws org.springframework.beans.BeansException 如果发生错误 */ void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException; } ``` ### 四、主要功能 1. **注册新的 Bean 定义** + 该接口提供了一个机制,允许在 Spring 容器完成其标准初始化(即加载所有 bean 定义)之后,但在任何 bean 实例化之前,动态注册新的 bean 定义。 2. **修改现有的 Bean 定义** + 除了能够添加新的 bean 定义,`BeanDefinitionRegistryPostProcessor` 还可以修改已经注册的 bean 定义。例如,它可以修改 bean 的属性值、构造函数参数或其它设置。 3. **控制 `BeanFactoryPostProcessor` 的执行顺序** + 因为 `BeanDefinitionRegistryPostProcessor` 是 `BeanFactoryPostProcessor` 的子接口,它的实现还可以控制 `BeanFactoryPostProcessor` 的执行顺序。这是因为在 Spring 容器启动时,所有的 `BeanDefinitionRegistryPostProcessor` beans 首先会被实例化和调用,然后才是其他的 `BeanFactoryPostProcessor` beans。 4. **基于条件的 Bean 注册** + 可以利用 `BeanDefinitionRegistryPostProcessor` 来基于特定的运行时条件(例如类路径上是否存在某个特定的类)来决定是否注册某个 bean。 5. **扩展点以实现高级配置** + 对于复杂的应用或框架,这个接口提供了一个扩展点,可以在初始化过程中进行更高级的配置,如加载外部的配置或执行特殊的验证逻辑。 ### 五、最佳实践 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后我们从`AnnotationConfigApplicationContext`中获取`MySimpleBean`并调用`show`方法。 ```java public class BeanDefinitionRegistryPostProcessorApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MySimpleBean mySimpleBean1 = context.getBean(MySimpleBean.class); mySimpleBean1.show(); } } ``` 这里使用`@Bean`注解,为了确保 `MyBeanDefinitionRegistryPostProcessor` 被 Spring 容器执行,我们需要将它注册为一个 bean,该后处理器可以新增一个`BeanDefinition`。 ```java @Configuration public class MyConfiguration { @Bean public static MyBeanDefinitionRegistryPostProcessor myBeanDefinitionRegistryPostProcessor(){ return new MyBeanDefinitionRegistryPostProcessor(); } } ``` 在我自定义的`postProcessBeanDefinitionRegistry` 方法中,创建了一个新的 `RootBeanDefinition` 对象,该对象代表 `MySimpleBean` 类。然后,使用了 `registry` 的 `registerBeanDefinition` 方法来注册这个新的 bean 定义,并为它指定了名称 `"mySimpleBean"`。 ```java public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { } @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { System.out.println("开始新增Bean定义"); // 创建一个新的 BeanDefinition 对象 BeanDefinition beanDefinition = new RootBeanDefinition(MySimpleBean.class); // 使用 registry 来注册这个新的 bean 定义 registry.registerBeanDefinition("mySimpleBean", beanDefinition); System.out.println("完成新增Bean定义"); } } ``` 要被动态注册的 Bean ```java public class MySimpleBean { public void show() { System.out.println("MySimpleBean instance: " + this); } } ``` 运行结果发现,`MySimpleBean` 实例已成功创建,并打印了它的实例信息,这证明了 `BeanDefinitionRegistryPostProcessor` 成功地在运行时动态注册了这个 bean ```java 开始新增Bean定义 完成新增Bean定义 MySimpleBean instance: com.xcs.spring.config.MySimpleBean@7e5afaa6 ``` ### 六、时序图 ~~~mermaid sequenceDiagram Title: BeanFactoryPostProcessor时序图 participant BeanDefinitionRegistryPostProcessorApplication participant AnnotationConfigApplicationContext participant AbstractApplicationContext participant PostProcessorRegistrationDelegate participant MyBeanDefinitionRegistryPostProcessor participant BeanDefinitionRegistry BeanDefinitionRegistryPostProcessorApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(...)
启动上下文 AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh() AbstractApplicationContext->>AbstractApplicationContext:invokeBeanFactoryPostProcessors(...)
触发整个BeanFactoryPostProcessor调用的流程 AbstractApplicationContext->>PostProcessorRegistrationDelegate:invokeBeanFactoryPostProcessors(...)
确保正确的顺序触发BeanDefinitionRegistryPostProcessor调用的流程 PostProcessorRegistrationDelegate->>PostProcessorRegistrationDelegate:invokeBeanDefinitionRegistryPostProcessors(...)
最终对BeanDefinitionRegistryPostProcessor接口回调 PostProcessorRegistrationDelegate->>MyBeanDefinitionRegistryPostProcessor:postProcessBeanDefinitionRegistry(...)
执行自定义的逻辑 MyBeanDefinitionRegistryPostProcessor-->>BeanDefinitionRegistry:通过新增bean定义 BeanDefinitionRegistry-->>MyBeanDefinitionRegistryPostProcessor:新增已完成 PostProcessorRegistrationDelegate-->>AbstractApplicationContext: 调用Bean工厂后置处理器完成 AnnotationConfigApplicationContext->>BeanDefinitionRegistryPostProcessorApplication:初始化完成 ~~~ ### 七、源码分析 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后我们从`AnnotationConfigApplicationContext`中获取`MySimpleBean`并调用`show`方法。 ```java public class BeanDefinitionRegistryPostProcessorApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MySimpleBean mySimpleBean1 = context.getBean(MySimpleBean.class); mySimpleBean1.show(); } } ``` 在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法。 ```java public AnnotationConfigApplicationContext(Class... componentClasses) { this(); register(componentClasses); refresh(); } ``` 在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中,我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 ```java @Override public void refresh() throws BeansException, IllegalStateException { // ... [代码部分省略以简化] // 调用在上下文中注册为bean的工厂处理器 invokeBeanFactoryPostProcessors(beanFactory); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors`方法中,又委托了`PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()`进行调用。 ```java protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) { PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors()); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors`方法中,主要是对`BeanDefinitionRegistryPostProcessor`,`BeanFactoryPostProcessor`这两个接口的实现类进行回调,至于为什么这个方法里面代码很长呢?其实这个方法就做了一个事就是对处理器的执行顺序在做处理。比如说要先对实现了`PriorityOrdered.class`类回调,在对实现了`Ordered.class`类回调,最后才是对没有实现任何优先级的处理器进行回调。 ```java public static void invokeBeanFactoryPostProcessors( ConfigurableListableBeanFactory beanFactory, List beanFactoryPostProcessors) { // 先调用 BeanDefinitionRegistryPostProcessors (如果有的话) Set processedBeans = new HashSet<>(); // 判断 beanFactory 是否为 BeanDefinitionRegistry 的实例 if (beanFactory instanceof BeanDefinitionRegistry) { BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory; List regularPostProcessors = new ArrayList<>(); List registryProcessors = new ArrayList<>(); // 遍历所有的后处理器,按类型分类 for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) { if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) { BeanDefinitionRegistryPostProcessor registryProcessor = (BeanDefinitionRegistryPostProcessor) postProcessor; registryProcessor.postProcessBeanDefinitionRegistry(registry); registryProcessors.add(registryProcessor); } else { regularPostProcessors.add(postProcessor); } } // 这里不初始化 FactoryBeans,为了让 bean 工厂的后处理器可以应用到它们 List currentRegistryProcessors = new ArrayList<>(); // 首先,调用实现了 PriorityOrdered 的 BeanDefinitionRegistryPostProcessors String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); for (String ppName : postProcessorNames) { if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); processedBeans.add(ppName); } } sortPostProcessors(currentRegistryProcessors, beanFactory); registryProcessors.addAll(currentRegistryProcessors); invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup()); currentRegistryProcessors.clear(); // 接下来,调用实现了 Ordered 的 BeanDefinitionRegistryPostProcessors postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); for (String ppName : postProcessorNames) { if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) { currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); processedBeans.add(ppName); } } sortPostProcessors(currentRegistryProcessors, beanFactory); registryProcessors.addAll(currentRegistryProcessors); invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup()); currentRegistryProcessors.clear(); // 最后,调用所有其他的 BeanDefinitionRegistryPostProcessors,直到没有更多的后处理器出现 boolean reiterate = true; while (reiterate) { reiterate = false; postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); for (String ppName : postProcessorNames) { if (!processedBeans.contains(ppName)) { currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); processedBeans.add(ppName); reiterate = true; } } sortPostProcessors(currentRegistryProcessors, beanFactory); registryProcessors.addAll(currentRegistryProcessors); invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup()); currentRegistryProcessors.clear(); } // 现在,调用到目前为止处理过的所有处理器的 postProcessBeanFactory 回调 invokeBeanFactoryPostProcessors(registryProcessors, beanFactory); invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory); } else { // 调用在上下文实例中注册的工厂处理器 invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory); } // ... [代码部分省略以简化] } ``` 下面是我画的一个关于`BeanDefinitionRegistryPostProcessor`排序回调过程时序图大家可以参考一下。 ~~~mermaid sequenceDiagram Title: BeanDefinitionRegistryPostProcessor回调排序时序图 participant Init as invokeBeanFactoryPostProcessors participant BDRPP_PO as BDRPP(PriorityOrdered) participant BDRPP_O as BDRPP(Ordered) participant BDRPP as 其余的BDRPP Init->>BDRPP_PO: 回调 BDRPP_PO-->>Init: 完成 Init->>BDRPP_O: 回调 BDRPP_O-->>Init: 完成 Init->>BDRPP: 回调 BDRPP-->>Init: 完成 Note right of BDRPP: 提示: Note right of BDRPP: BDRPP = BeanFactoryPostProcessor ~~~ 在`org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanDefinitionRegistryPostProcessors`方法中,循环调用了实现`BeanDefinitionRegistryPostProcessor`接口中的`postProcessBeanDefinitionRegistry(registry)`方法 ```java private static void invokeBeanDefinitionRegistryPostProcessors( Collection postProcessors, BeanDefinitionRegistry registry, ApplicationStartup applicationStartup) { for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) { StartupStep postProcessBeanDefRegistry = applicationStartup.start("spring.context.beandef-registry.post-process") .tag("postProcessor", postProcessor::toString); postProcessor.postProcessBeanDefinitionRegistry(registry); postProcessBeanDefRegistry.end(); } } ``` 最后执行到我们自定义的逻辑中,我们在`postProcessBeanDefinitionRegistry`方法中注册了一个新的 bean 定义,在实际应用中,我们可以在 `postProcessBeanDefinitionRegistry` 方法内部执行更复杂的操作,例如修改 bean 的属性、对Bean对象进行代理做功能增强处理、更改它们的作用域或添加新的 bean 定义等。 ```java public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { } @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { System.out.println("开始新增Bean定义"); // 创建一个新的 BeanDefinition 对象 BeanDefinition beanDefinition = new RootBeanDefinition(MySimpleBean.class); // 使用 registry 来注册这个新的 bean 定义 registry.registerBeanDefinition("mySimpleBean", beanDefinition); System.out.println("完成新增Bean定义"); } } ``` ### 八、注意事项 1. **调用顺序** + `BeanDefinitionRegistryPostProcessor` 比 `BeanFactoryPostProcessor` 有更高的优先级。这意味着 `BeanDefinitionRegistryPostProcessor` 的 `postProcessBeanDefinitionRegistry` 方法会在任何 `BeanFactoryPostProcessor` 的 `postProcessBeanFactory` 方法之前被调用。如果我们在Spring中有多个 `BeanDefinitionRegistryPostProcessor`,它们之间的执行顺序可能会受到 `Ordered` 或 `PriorityOrdered` 接口的影响,所以要确保正确地实现这些接口以确保预期的执行顺序。 2. **过早实例化** + 一个常见的陷阱是,定义在 `@Configuration` 类中返回 `BeanDefinitionRegistryPostProcessor` 的非静态 `@Bean` 方法可能导致配置类过早实例化。为避免这一问题,这种方法应该被声明为 `static`。在本次最近实践中我们也是用到了static来修饰我们的`MyBeanDefinitionRegistryPostProcessor` 3. **不要过度使用** + 虽然 `BeanDefinitionRegistryPostProcessor` 提供了强大的功能,但不应该在不需要修改或动态添加 Bean 定义的情况下滥用它,除非我们是框架我们。在我们绝大部分业务系统中,我觉得我们其实只要使用`@Component`、`@Service`、`@Repository`、`@Controller` 和 `@Configuration` 注解应该足够使用。 ### 九、总结 #### 最佳实践总结 1. **应用启动** + 启动类 `BeanDefinitionRegistryPostProcessorApplication` 通过 `AnnotationConfigApplicationContext` 初始化 Spring 容器,并加载了 `MyConfiguration` 类作为配置。 2. **配置类定义** + 在 `MyConfiguration` 类中,我们通过 `@Bean` 注解定义了一个静态方法,该方法返回一个 `MyBeanDefinitionRegistryPostProcessor` 对象,确保它在 Spring 容器初始化时被执行。 3. **动态注册** + 在我们自定义的 `MyBeanDefinitionRegistryPostProcessor` 实现中,我们重写了 `postProcessBeanDefinitionRegistry` 方法。在这个方法里,我们创建了一个新的 `RootBeanDefinition` 对象来代表 `MySimpleBean` 类,并通过 `registry` 的 `registerBeanDefinition` 方法注册了这个新的 bean 定义,为它指定了名为 `"mySimpleBean"` 的名称。 4. **目标 Bean** + `MySimpleBean` 是一个简单的 bean 类,它的 `show` 方法用于输出它自身的实例信息。 5. **验证动态注册** + 当运行 `BeanDefinitionRegistryPostProcessorApplication` 时,可以观察到控制台首先打印了 "开始新增Bean定义" 和 "完成新增Bean定义",说明 `postProcessBeanDefinitionRegistry` 方法被正确执行。紧接着,`MySimpleBean` 的实例被创建并打印了它的实例信息,证明 `BeanDefinitionRegistryPostProcessor` 成功地在运行时动态注册了这个 bean。 #### 源码分析总结 1. **应用启动** + 通过 `AnnotationConfigApplicationContext` 来初始化 Spring 容器并加载 `MyConfiguration` 配置类。随后从该上下文中获取 `MySimpleBean` 并调用其 `show` 方法。 2. **Spring容器的刷新** + `AnnotationConfigApplicationContext` 的构造函数调用了 `refresh()` 方法,负责启动 Spring 容器的初始化流程。 3. **执行Bean工厂的后处理器** + 在 `refresh()` 方法中,调用了 `invokeBeanFactoryPostProcessors(beanFactory)` 方法,负责执行所有注册的 `BeanFactoryPostProcessor` 和 `BeanDefinitionRegistryPostProcessor` 接口实现。 4. **按优先级执行回调** + Spring 按照不同的优先级(如 `PriorityOrdered` 和 `Ordered`)对 `BeanDefinitionRegistryPostProcessor` 进行排序并回调。这确保了回调的执行顺序。 5. **动态注册Bean定义**: + 在我们自定义的 `MyBeanDefinitionRegistryPostProcessor` 中,我们重写了 `postProcessBeanDefinitionRegistry` 方法。在该方法内部,我们动态地创建了 `MySimpleBean` 的定义,并将其注册到了容器中。 6. **执行结果** + `MySimpleBean` 成功被动态注册到 Spring 容器中,从而能够在应用启动时被检索并使用。 ================================================ FILE: spring-interface/spring-interface-beanDefinitionRegistryPostProcessor/pom.xml ================================================ spring-interface com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-interface-beanDefinitionRegistryPostProcessor ================================================ FILE: spring-interface/spring-interface-beanDefinitionRegistryPostProcessor/src/main/java/com/xcs/spring/BeanDefinitionRegistryPostProcessorApplication.java ================================================ package com.xcs.spring; import com.xcs.spring.config.MyConfiguration; import com.xcs.spring.config.MySimpleBean; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年08月07日 16时21分 **/ public class BeanDefinitionRegistryPostProcessorApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MySimpleBean mySimpleBean1 = context.getBean(MySimpleBean.class); mySimpleBean1.show(); } } ================================================ FILE: spring-interface/spring-interface-beanDefinitionRegistryPostProcessor/src/main/java/com/xcs/spring/config/MyBeanDefinitionRegistryPostProcessor.java ================================================ package com.xcs.spring.config; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.beans.factory.support.RootBeanDefinition; /** * @author xcs * @date 2023年09月14日 14时17分 **/ public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { } @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { System.out.println("开始新增Bean定义"); // 创建一个新的 BeanDefinition 对象 BeanDefinition beanDefinition = new RootBeanDefinition(MySimpleBean.class); // 使用 registry 来注册这个新的 bean 定义 registry.registerBeanDefinition("mySimpleBean", beanDefinition); System.out.println("完成新增Bean定义"); } } ================================================ FILE: spring-interface/spring-interface-beanDefinitionRegistryPostProcessor/src/main/java/com/xcs/spring/config/MyConfiguration.java ================================================ package com.xcs.spring.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @author xcs * @date 2023年08月07日 16时25分 **/ @Configuration public class MyConfiguration { @Bean public static MyBeanDefinitionRegistryPostProcessor myBeanDefinitionRegistryPostProcessor(){ return new MyBeanDefinitionRegistryPostProcessor(); } } ================================================ FILE: spring-interface/spring-interface-beanDefinitionRegistryPostProcessor/src/main/java/com/xcs/spring/config/MySimpleBean.java ================================================ package com.xcs.spring.config; /** * @author xcs * @date 2023年09月14日 16时05分 **/ public class MySimpleBean { public void show() { System.out.println("MySimpleBean instance: " + this); } } ================================================ FILE: spring-interface/spring-interface-beanFactoryPostProcessor/README.md ================================================ ## BeanFactoryPostProcessor - [BeanFactoryPostProcessor](#beanfactorypostprocessor) - [一、基本信息](#一基本信息) - [二、接口描述](#二接口描述) - [三、接口源码](#三接口源码) - [四、主要功能](#四主要功能) - [五、最佳实践](#五最佳实践) - [六、时序图](#六时序图) - [七、源码分析](#七源码分析) - [八、注意事项](#八注意事项) - [九、总结](#九总结) - [最佳实践总结](#最佳实践总结) - [源码分析总结](#源码分析总结) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [我的CSDN](https://blog.csdn.net/duzhuang2399/article/details/133844965) 📚 **文章目录** - [所有文章](https://github.com/xuchengsheng/spring-reading) 🔗 **源码地址** - [BeanFactoryPostProcessor源码](https://github.com/xuchengsheng/spring-reading/tree/master/spring-interface/spring-interface-beanFactoryPostProcessor) ### 二、接口描述 `BeanFactoryPostProcessor` 是一个接口,任何实现此接口的类都必须提供`postProcessBeanFactory`方法的实现。此方法提供了一个机会,在bean实例化之前修改bean的定义。 ### 三、接口源码 `BeanFactoryPostProcessor` 是 Spring 框架自 06.07.2003 开始引入的一个核心接口。此接口提供了一个强大的方式来修改bean的属性和依赖关系,使得我们可以根据特定的配置或环境条件动态地调整这些值。 ```java /** * BeanFactoryPostProcessor 是一个核心接口,允许用户在Spring容器初始化bean之前修改bean定义。 * 它提供了一个强大的方式来修改bean的属性和依赖关系,使得我们可以根据特定的配置或环境条件动态地调整这些值。 * * @author Juergen Hoeller * @author Sam Brannen * @since 06.07.2003 * @see BeanPostProcessor * @see PropertyResourceConfigurer */ @FunctionalInterface public interface BeanFactoryPostProcessor { /** * 在应用上下文的内部bean工厂进行其标准初始化后修改它。 * 此时,所有bean定义都已加载,但尚未实例化任何bean。 * 这允许用户即使对于急切初始化的beans也可以覆盖或添加属性。 * * @param beanFactory 应用上下文使用的bean工厂 * @throws org.springframework.beans.BeansException 如果发生错误 */ void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException; } ``` ### 四、主要功能 1. **修改Bean定义** + 在Spring加载所有bean定义后,但在它开始实例化这些bean之前,`BeanFactoryPostProcessor`会被调用。这意味着我们可以使用它来修改这些bean定义。 2. **更改属性值** + 我们可以更改bean的属性或依赖,这在某些场景下,如需要根据环境或其他外部因素动态地配置bean时,会非常有用。 3. **添加或删除Bean定义** + 不仅可以修改现有的bean定义,还可以添加新的bean定义或删除现有的bean定义。 4. **应用多个BeanFactoryPostProcessors** + 如果有多个`BeanFactoryPostProcessor`,我们可以通过实现`Ordered`接口来控制它们的执行顺序。 ### 五、最佳实践 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后我们会调用`mySimpleBean1`和`mySimpleBean2`中的`show()`方法,我们可以判断`MySimpleBean`的作用域是单例还是原型。如果它们指向同一个实例,那么它是单例的;否则,它是原型的。 ```java public class BeanFactoryPostProcessorApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MySimpleBean mySimpleBean1 = context.getBean(MySimpleBean.class); MySimpleBean mySimpleBean2 = context.getBean(MySimpleBean.class); mySimpleBean1.show(); mySimpleBean2.show(); } } ``` 这里使用`@Bean`注解,定义了两个Bean,是为了确保`MySimpleBean`, `MyBeanFactoryPostProcessor` 被 Spring 容器执行 ```java @Configuration public class MyConfiguration { @Bean public MySimpleBean mySimpleBean(){ return new MySimpleBean(); } @Bean public static MyBeanFactoryPostProcessor myBeanFactoryPostProcessor(){ return new MyBeanFactoryPostProcessor(); } } ``` `MySimpleBean`类中的`show`方法在被调用时,会在控制台输出“MySimpleBean instance”和当前对象的实例地址(通过`this`关键字)。这有助于我们了解每次获取bean时是否返回相同的实例(单例)还是新的实例(原型)。 ```java public class MySimpleBean { public void show() { System.out.println("MySimpleBean instance: " + this); } } ``` 这个 `MyBeanFactoryPostProcessor` 类是一个简单的 `BeanFactoryPostProcessor` 的实现,它在被调用时,会从`beanFactory`工厂中获取名为`mySimpleBean`的bean定义,默认情况下,所有的bean都是单例的,然后将`mySimpleBean`的作用域从单例改为原型。在实际应用中,我们可能会在 `postProcessBeanFactory` 方法内部执行更复杂的操作,例如修改 bean 的属性、对Bean对象进行代理做功能增强处理、更改它们的作用域或添加新的 bean 定义等。 ```java public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { System.out.println("修改bean的定义"); BeanDefinition beanDefinition = beanFactory.getBeanDefinition("mySimpleBean"); beanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE); System.out.println("将mySimpleBean从默认的单例修改成多例"); System.out.println("修改bean的定义已完成"); } } ``` 运行结果发现,由于`mySimpleBean`现在是原型作用域,`[com.xcs.spring.config.MySimpleBean@11392934]`和`[com.xcs.spring.config.MySimpleBean@6892b3b6]`将是两个不同的地址,说明`MySimpleBean`的两个实例是不同的。 ```java 修改bean的定义 将mySimpleBean从默认的单例修改成多例 修改bean的定义已完成 MySimpleBean instance: com.xcs.spring.config.MySimpleBean@11392934 MySimpleBean instance: com.xcs.spring.config.MySimpleBean@6892b3b6 ``` ### 六、时序图 ```mermaid sequenceDiagram Title: BeanFactoryPostProcessor时序图 participant BeanFactoryPostProcessorApplication participant AnnotationConfigApplicationContext participant AbstractApplicationContext participant PostProcessorRegistrationDelegate participant MyBeanFactoryPostProcessor participant ConfigurableListableBeanFactory BeanFactoryPostProcessorApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)
启动上下文 AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh() AbstractApplicationContext->>AbstractApplicationContext:invokeBeanFactoryPostProcessors(beanFactory)
触发整个BeanFactoryPostProcessor调用的流程 AbstractApplicationContext->>PostProcessorRegistrationDelegate:invokeBeanFactoryPostProcessors(...)
确保正确的顺序触发BeanFactoryPostProcessor调用的流程 PostProcessorRegistrationDelegate->>PostProcessorRegistrationDelegate:invokeBeanFactoryPostProcessors(postProcessors,beanFactory)
最终对BeanFactoryPostProcessor接口回调 PostProcessorRegistrationDelegate->>MyBeanFactoryPostProcessor:postProcessBeanFactory(beanFactory)
执行自定义的逻辑 MyBeanFactoryPostProcessor-->>ConfigurableListableBeanFactory:访问和修改bean定义 ConfigurableListableBeanFactory-->>MyBeanFactoryPostProcessor:修改已完成 PostProcessorRegistrationDelegate-->>AbstractApplicationContext: 调用Bean工厂后置处理器完成 AnnotationConfigApplicationContext->>BeanFactoryPostProcessorApplication:初始化完成 ``` ### 七、源码分析 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。从Spring上下文中获取MySimpleBean的两个实例,调用这两个实例的show方法,如果MySimpleBean是单例的,那么这两个实例应该是同一个对象,反之则不是。 ```java public class BeanFactoryPostProcessorApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MySimpleBean mySimpleBean1 = context.getBean(MySimpleBean.class); MySimpleBean mySimpleBean2 = context.getBean(MySimpleBean.class); mySimpleBean1.show(); mySimpleBean2.show(); } } ``` 在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法。 ```java public AnnotationConfigApplicationContext(Class... componentClasses) { this(); register(componentClasses); refresh(); } ``` 在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 ```java @Override public void refresh() throws BeansException, IllegalStateException { // ... [代码部分省略以简化] // 调用在上下文中注册为bean的工厂处理器 invokeBeanFactoryPostProcessors(beanFactory); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors`方法中,又委托了`PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()`进行调用。 ```java protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) { PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors()); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors`方法中,主要是对`BeanDefinitionRegistryPostProcessor`,`BeanFactoryPostProcessor`这两个接口的实现类进行回调,至于为什么这个方法里面代码很长呢?其实这个方法就做了一个事就是对处理器的执行顺序在做处理。比如说要先对实现了`PriorityOrdered.class`类回调,在对实现了`Ordered.class`类回调,最后才是对没有实现任何优先级的处理器进行回调。 ```java public static void invokeBeanFactoryPostProcessors( ConfigurableListableBeanFactory beanFactory, List beanFactoryPostProcessors) { // ... [代码部分省略以简化] // 获取所有实现了BeanFactoryPostProcessor接口的bean的名称。 String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false); // 创建集合以区分不同类型的BeanFactoryPostProcessors List priorityOrderedPostProcessors = new ArrayList<>(); List orderedPostProcessorNames = new ArrayList<>(); List nonOrderedPostProcessorNames = new ArrayList<>(); // 对BeanFactoryPostProcessors进行分类 for (String ppName : postProcessorNames) { if (processedBeans.contains(ppName)) { // 如果这个bean已经被处理,直接跳过 } else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { // 优先排序的BeanFactoryPostProcessors priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class)); } else if (beanFactory.isTypeMatch(ppName, Ordered.class)) { // 有排序的BeanFactoryPostProcessors orderedPostProcessorNames.add(ppName); } else { // 没有排序的BeanFactoryPostProcessors nonOrderedPostProcessorNames.add(ppName); } } // 调用实现了PriorityOrdered接口的BeanFactoryPostProcessors sortPostProcessors(priorityOrderedPostProcessors, beanFactory); invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory); // 调用实现了Ordered接口的BeanFactoryPostProcessors List orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size()); for (String postProcessorName : orderedPostProcessorNames) { orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class)); } sortPostProcessors(orderedPostProcessors, beanFactory); invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory); // 调用其他所有的BeanFactoryPostProcessors List nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size()); for (String postProcessorName : nonOrderedPostProcessorNames) { nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class)); } invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory); // 清除元数据缓存,因为BeanFactoryPostProcessors可能已修改bean定义 beanFactory.clearMetadataCache(); } ``` 下面是我画的一个关于`BeanFactoryPostProcessor`排序回调过程时序图大家可以参考一下。 ~~~mermaid sequenceDiagram Title: BeanFactoryPostProcessor回调排序时序图 participant Init as invokeBeanFactoryPostProcessors participant BFPP_PO as BFPP(PriorityOrdered) participant BFPP_O as BFPP(Ordered) participant BFPP as 其余的BFPP Init->>BFPP_PO: 回调 BFPP_PO-->>Init: 完成 Init->>BFPP_O: 回调 BFPP_O-->>Init: 完成 Init->>BFPP: 回调 BFPP-->>Init: 完成 Note right of BFPP: 提示: Note right of BFPP: BFPP = BeanFactoryPostProcessor ~~~ 在`org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors`方法中,循环调用了实现`BeanFactoryPostProcessor`接口中的`postProcessBeanFactory(registry)`方法 ```java private static void invokeBeanFactoryPostProcessors( Collection postProcessors, ConfigurableListableBeanFactory beanFactory) { for (BeanFactoryPostProcessor postProcessor : postProcessors) { StartupStep postProcessBeanFactory = beanFactory.getApplicationStartup().start("spring.context.bean-factory.post-process") .tag("postProcessor", postProcessor::toString); postProcessor.postProcessBeanFactory(beanFactory); postProcessBeanFactory.end(); } } ``` 最后执行到我们自定义的逻辑中,在实际应用中,我们可以在 `postProcessBeanFactory` 方法内部执行更复杂的操作,例如修改 bean 的属性、对Bean对象进行代理做功能增强处理、更改它们的作用域或添加新的 bean 定义等。 ```java public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { System.out.println("修改bean的定义"); BeanDefinition beanDefinition = beanFactory.getBeanDefinition("mySimpleBean"); beanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE); System.out.println("将mySimpleBean从默认的单例修改成多例"); System.out.println("修改bean的定义已完成"); } } ``` ### 八、注意事项 1. **考虑其他的`BeanFactoryPostProcessor`** + 在大型应用程序中,可能存在多个`BeanFactoryPostProcessor`。我们需要确保它们不会互相冲突或导致不一致的bean定义。 2. **注意执行顺序** + 如果有多个`BeanFactoryPostProcessor`,它们的执行顺序可能会影响结果。使用`Ordered`接口或`PriorityOrdered`接口来明确设置执行顺序。 3. **避免循环依赖** + 修改bean定义可能会引入循环依赖。确保我们充分理解bean之间的依赖关系,并尝试避免修改这些关系。比如当我们修改了`BeanDefinition`构造函数,等等情况都有可能引入循环依赖。 4. **不要过度使用** + 虽然`BeanFactoryPostProcessor`是一个非常强大接口,但这并不意味着我们就应该频繁使用它。只在真正需要的时候使用它,并考虑是否有其他更简单、更直观的方法可以达到同样的目的。 5. **谨慎使用** + 虽然`BeanFactoryPostProcessor`是一个非常强大接口,允许我们修改bean的定义。这意味着我们可以更改bean的类、作用域、属性等。我们要在做这些更改时要非常小心,想想为什么要修改?影响的范围有多少?,以免引入不一致或不可预测的行为。 ### 九、总结 #### 最佳实践总结 1. **初始化与配置** + 使用`AnnotationConfigApplicationContext`, 我们成功地启动了Spring容器并加载了`MyConfiguration`配置。在`MyConfiguration`中, 我们定义了两个核心bean:`MySimpleBean`和`MyBeanFactoryPostProcessor`. 2. **修改Bean的作用域** + 虽然`MySimpleBean`默认是单例,但通过`MyBeanFactoryPostProcessor`,我们改变了这一默认行为,将其转变为原型作用域。这种转变是通过覆盖`postProcessBeanFactory`方法并更改`mySimpleBean`的bean定义来完成的。 3. **验证修改** + 当我们从主应用程序获取`MySimpleBean`的两个实例并调用它们的`show`方法时,输出的实例地址明确地告诉我们这两个bean是不同的实例。 #### 源码分析总结 1. **启动与上下文** + 利用 `AnnotationConfigApplicationContext`,我们初始化了Spring容器,并加载了`MyConfiguration`作为主要配置。 2. **核心调用** + Spring容器的`refresh`方法中,`invokeBeanFactoryPostProcessors(beanFactory)`确保所有的`BeanFactoryPostProcessor`得到适当的调用。 3. **回调顺序** + 在Spring中,实现了`BeanFactoryPostProcessor`接口的bean不是随机调用的。Spring确保它们按照`PriorityOrdered`、`Ordered`和无顺序的层次结构进行分类和调用。 4. **自定义逻辑** + 当我们实现`BeanFactoryPostProcessor`接口并提供自定义逻辑时(例如更改Bean的作用域),该逻辑将在上述过程的适当阶段被调用。 5. **具体实践** + 通过具体示例,我们查看了如何利用`BeanFactoryPostProcessor`在bean实例化前更改bean定义。在我们的例子中,`MySimpleBean`的作用域从单例被更改为原型。 ================================================ FILE: spring-interface/spring-interface-beanFactoryPostProcessor/pom.xml ================================================ spring-interface com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-interface-beanFactoryPostProcessor ================================================ FILE: spring-interface/spring-interface-beanFactoryPostProcessor/src/main/java/com/xcs/spring/BeanFactoryPostProcessorApplication.java ================================================ package com.xcs.spring; import com.xcs.spring.config.MyConfiguration; import com.xcs.spring.config.MySimpleBean; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年08月07日 16时21分 **/ public class BeanFactoryPostProcessorApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MySimpleBean mySimpleBean1 = context.getBean(MySimpleBean.class); MySimpleBean mySimpleBean2 = context.getBean(MySimpleBean.class); mySimpleBean1.show(); mySimpleBean2.show(); } } ================================================ FILE: spring-interface/spring-interface-beanFactoryPostProcessor/src/main/java/com/xcs/spring/config/MyBeanFactoryPostProcessor.java ================================================ package com.xcs.spring.config; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; /** * @author xcs * @date 2023年09月14日 14时17分 **/ public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { System.out.println("修改bean的定义"); BeanDefinition beanDefinition = beanFactory.getBeanDefinition("mySimpleBean"); beanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE); System.out.println("将mySimpleBean从默认的单例修改成多例"); System.out.println("修改bean的定义已完成"); } } ================================================ FILE: spring-interface/spring-interface-beanFactoryPostProcessor/src/main/java/com/xcs/spring/config/MyConfiguration.java ================================================ package com.xcs.spring.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @author xcs * @date 2023年08月07日 16时25分 **/ @Configuration public class MyConfiguration { @Bean public MySimpleBean mySimpleBean(){ return new MySimpleBean(); } @Bean public static MyBeanFactoryPostProcessor myBeanFactoryPostProcessor(){ return new MyBeanFactoryPostProcessor(); } } ================================================ FILE: spring-interface/spring-interface-beanFactoryPostProcessor/src/main/java/com/xcs/spring/config/MySimpleBean.java ================================================ package com.xcs.spring.config; /** * @author xcs * @date 2023年09月14日 16时05分 **/ public class MySimpleBean { public void show() { System.out.println("MySimpleBean instance: " + this); } } ================================================ FILE: spring-interface/spring-interface-beanPostProcessor/README.md ================================================ ## BeanPostProcessor - [BeanPostProcessor](#beanpostprocessor) - [一、接口描述](#一接口描述) - [二、接口源码](#二接口源码) - [三、主要功能](#三主要功能) - [四、最佳实践](#四最佳实践) - [五、时序图](#五时序图) - [六、源码分析](#六源码分析) - [七、注意事项](#七注意事项) - [八、总结](#八总结) - [8.1、最佳实践总结](#81最佳实践总结) - [8.2、源码分析总结](#82源码分析总结) ### 一、接口描述 `BeanPostProcessor`接口,允许对新创建的 Bean 进行操作和修改。 ### 二、接口源码 `BeanPostProcessor` 是 Spring 框架自 10.10.2003 开始引入的一个核心接口。该接口定义了两个主要的方法:`postProcessBeforeInitialization` 和 `postProcessAfterInitialization`,分别在 Bean 的初始化前后调用。从源码中可以发现`BeanPostProcessor`接口具备注册与排序功能,那什么是注册呢?首先是自动注册,当启动一个`AnnotationConfigApplicationContext`时,Spring 会自动扫描定义的所有 bean。如果某个 bean 实现了 `BeanPostProcessor` 接口,Spring 将自动识别并注册它。另外一个是手动注册通过调用 `ConfigurableBeanFactory` 的 `addBeanPostProcessor` 方法来完成。那为什么是排序呢?如果 `BeanPostProcessor` 实现了 `org.springframework.core.PriorityOrdered` 或 `org.springframework.core.Ordered` 接口,那么 Spring 将会根据它们的优先级或顺序进行排序。在 `AnnotationConfigApplicationContext` 中,Spring 会考虑上述两个接口的语义来确定 `BeanPostProcessor` 的执行顺序。但是需要注意的是,如果我们手动在 `ConfigurableBeanFactory` 中注册了 `BeanPostProcessor`,那么 Spring 会按照我们注册它们的顺序来应用这些后处理器,在这种情况下,即使 `BeanPostProcessor` 实现了 `PriorityOrdered` 或 `Ordered` 接口,这些排序语义也会被忽略。 ```java /** * 工厂钩子,允许对新创建的bean实例进行自定义修改。 * 例如,检查标记接口或使用代理包装beans。 * * 通常,通过标记接口等填充beans的后处理器会实现 #postProcessBeforeInitialization * 而使用代理包装beans的后处理器通常会实现 #postProcessAfterInitialization * * 注册 * ApplicationContext 可以在其bean定义中自动检测到 BeanPostProcessor beans, * 并将这些后处理器应用于随后创建的任何beans。一个普通的 BeanFactory 允许以编程方式注册 * 后处理器,将它们应用于通过bean工厂创建的所有beans。 * * 排序 * 在 ApplicationContext 中自动检测到的 BeanPostProcessor beans 将根据 * org.springframework.core.PriorityOrdered 和 * org.springframework.core.Ordered 语义进行排序。相反,以编程方式在 * BeanFactory 中注册的 BeanPostProcessor beans 将按注册顺序应用; * 通过实现 PriorityOrdered 或 Ordered 接口表达的任何排序语义 * 对于编程注册的后处理器都将被忽略。此外,org.springframework.core.annotation.Order @Order * 注释不适用于 BeanPostProcessor beans。 * * @author Juergen Hoeller * @author Sam Brannen * @since 10.10.2003 * @see InstantiationAwareBeanPostProcessor * @see DestructionAwareBeanPostProcessor * @see ConfigurableBeanFactory#addBeanPostProcessor * @see BeanFactoryPostProcessor */ public interface BeanPostProcessor { /** * 在任何bean初始化回调(如InitializingBean的 afterPropertiesSet * 或自定义初始化方法)之前,将此 BeanPostProcessor 应用于给定的新bean实例。 * 该bean已使用属性值填充。返回的bean实例可能是原始实例的包装。 * 默认实现返回给定的 bean。 * @param bean 新的bean实例 * @param beanName bean的名称 * @return 要使用的bean实例,可以是原始实例或其包装;如果为 null,则不会调用后续的BeanPostProcessors * @throws org.springframework.beans.BeansException 出错时 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet */ @Nullable default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } /** * 在任何bean初始化回调(如InitializingBean的 afterPropertiesSet * 或自定义初始化方法)之后,将此 BeanPostProcessor 应用于给定的新bean实例。 * 该bean已使用属性值填充。返回的bean实例可能是原始实例的包装。 * 对于FactoryBean,此回调将被调用,既适用于FactoryBean实例,也适用于FactoryBean创建的对象(自Spring 2.0起)。 * 后处理器可以决定通过相应的 bean instanceof FactoryBean 检查,是否应用于FactoryBean、创建的对象或两者。 * 此回调还将在由 InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation * 方法触发的短路之后调用,与所有其他 BeanPostProcessor 回调相反。 * 默认实现返回给定的 bean。 * @param bean 新的bean实例 * @param beanName bean的名称 * @return 要使用的bean实例,可以是原始实例或其包装;如果为 null,则不会调用后续的BeanPostProcessors * @throws org.springframework.beans.BeansException 出错时 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet * @see org.springframework.beans.factory.FactoryBean */ @Nullable default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } } ``` ### 三、主要功能 **修改 Bean 属性**:在 Bean 初始化之前或之后,可以修改 Bean 的属性。例如,可以根据某些条件为 Bean 的某些属性设置默认值。 **验证 Bean 的状态**:在 Bean 初始化完成后,可以检查 Bean 的状态,确保它满足某些条件或约束。 **返回代理 Bean**:最常见的用例是返回一个代理(proxy)来包装原始的 Bean。例如,Spring AOP 使用它来为目标 Bean 创建代理,从而实现切面的功能。 **改变返回的 Bean 类型**:可以完全替换一个 Bean 的实例,返回一个不同类型的对象。这是一个高级用例,但在某些场景中可能是必要的 ### 四、最佳实践 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyService`类型的bean,最后打印了该`show`方法返回的值。 ```java public class BeanPostProcessorApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MyService myService = context.getBean(MyService.class); System.out.println(myService.show()); context.close(); } } ``` 这里使用`@Bean`注解,定义了两个Bean,是为了确保`MyService`, `MyBeanPostProcessor` 被 Spring 容器执行 ```java @Configuration public class MyConfiguration { @Bean public MyService myService() { return new MyServiceImpl(); } @Bean public BeanPostProcessor myBeanPostProcessor() { return new MyBeanPostProcessor(); } } ``` 在`postProcessBeforeInitialization`方法中,将 `MyServiceImpl` 中的 `message` 属性,为其添加 `Prefix:` 前缀。在`postProcessAfterInitialization`方法中,将 `MyServiceImpl` 中的 `message` 属性,为其添加 `:Suffix` 后缀。 ```java public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if(bean instanceof MyServiceImpl) { MyServiceImpl myService = (MyServiceImpl) bean; myService.setMessage("Prefix: " + myService.getMessage()); } return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if(bean instanceof MyServiceImpl) { MyServiceImpl myService = (MyServiceImpl) bean; myService.setMessage(myService.getMessage() + " :Suffix"); } return bean; } } ``` 一个简单的接口 ```java public interface MyService { String show(); } ``` 一个简单的实现类 ```java public class MyServiceImpl implements MyService{ private String message = "Hello from MyService"; @Override public String show() { return message; } public void setMessage(String message) { this.message = message; } public String getMessage() { return message; } } ``` 运行结果发现, `MyBeanPostProcessor` 实现修改了 `MyServiceImpl` bean的`message`属性,首先在初始化前加了前缀,然后在初始化后加了后缀。 ```java Prefix: Hello from MyService :Suffix ``` ### 五、时序图 ~~~mermaid sequenceDiagram Title: BeanPostProcessor时序图 participant BeanPostProcessorApplication participant AnnotationConfigApplicationContext participant AbstractApplicationContext participant DefaultListableBeanFactory participant AbstractBeanFactory participant DefaultSingletonBeanRegistry participant AbstractAutowireCapableBeanFactory participant MyBeanPostProcessor BeanPostProcessorApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses) AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh() AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory) AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons() DefaultListableBeanFactory->>AbstractBeanFactory:getBean(name) AbstractBeanFactory->>AbstractBeanFactory:doGetBean(name,requiredType,args,typeCheckOnly) AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton(beanName,singletonFactory) DefaultSingletonBeanRegistry-->>AbstractBeanFactory:getObject() AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean(beanName,mbd,args) AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:doCreateBean(beanName,mbd,args) AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:initializeBean(beanName,bean,mbd) AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:applyBeanPostProcessorsBeforeInitialization(existingBean,beanName) AbstractAutowireCapableBeanFactory->>MyBeanPostProcessor: postProcessBeforeInitialization(bean,beanName) MyBeanPostProcessor-->>AbstractAutowireCapableBeanFactory: 返回Bean对象 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:applyBeanPostProcessorsAfterInitialization(existingBean,beanName) AbstractAutowireCapableBeanFactory->>MyBeanPostProcessor: postProcessAfterInitialization(bean,beanName) MyBeanPostProcessor-->>AbstractAutowireCapableBeanFactory: 返回Bean对象 AbstractAutowireCapableBeanFactory-->>AbstractBeanFactory:返回Bean对象 AbstractBeanFactory-->>DefaultListableBeanFactory:返回Bean对象 AnnotationConfigApplicationContext->>BeanPostProcessorApplication: 初始化完成 ~~~ ### 六、源码分析 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyService`类型的bean,最后调用了`MyService`对象的`show`方法,并将其返回值打印到控制台。 ```java public class BeanPostProcessorApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MyService myService = context.getBean(MyService.class); System.out.println(myService.show()); context.close(); } } ``` 在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法。 ```java public AnnotationConfigApplicationContext(Class... componentClasses) { this(); register(componentClasses); refresh(); } ``` 在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 ```java @Override public void refresh() throws BeansException, IllegalStateException { // ... [代码部分省略以简化] // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization`方法中,会继续调用`DefaultListableBeanFactory`类中的`preInstantiateSingletons`方法来完成所有剩余非懒加载的单列Bean对象。 ```java /** * 完成此工厂的bean初始化,实例化所有剩余的非延迟初始化单例bean。 * * @param beanFactory 要初始化的bean工厂 */ protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { // ... [代码部分省略以简化] // 完成所有剩余非懒加载的单列Bean对象。 beanFactory.preInstantiateSingletons(); } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons`方法中,主要的核心目的是预先实例化所有非懒加载的单例bean。在Spring的上下文初始化完成后,该方法会被触发,以确保所有单例bean都被正确地创建并初始化。其中`getBean(beanName)`是此方法的核心操作。对于容器中定义的每一个单例bean,它都会调用`getBean`方法,这将触发bean的实例化、初始化及其依赖的注入。如果bean之前没有被创建过,那么这个调用会导致其被实例化和初始化。 ```java public void preInstantiateSingletons() throws BeansException { // ... [代码部分省略以简化] // 循环遍历所有bean的名称 for (String beanName : beanNames) { getBean(beanName); } // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#getBean()`方法中,又调用了`doGetBean`方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,`doGetBean`负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。 ```java @Override public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中,首先检查所请求的bean是否是一个单例并且已经创建。如果尚未创建,它将创建一个新的实例。在这个过程中,它处理可能的异常情况,如循环引用,并确保返回的bean是正确的类型。这是Spring容器bean生命周期管理的核心部分。 ```java protected T doGetBean( String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException { // ... [代码部分省略以简化] // 开始创建bean实例 if (mbd.isSingleton()) { // 如果bean是单例的,我们会尝试从单例缓存中获取它 // 如果不存在,则使用lambda创建一个新的实例 sharedInstance = getSingleton(beanName, () -> { try { // 尝试创建bean实例 return createBean(beanName, mbd, args); } catch (BeansException ex) { // ... [代码部分省略以简化] } }); // 对于某些bean(例如FactoryBeans),可能需要进一步处理以获取真正的bean实例 beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } // ... [代码部分省略以简化] // 确保返回的bean实例与请求的类型匹配 return adaptBeanInstance(name, beanInstance, requiredType); } ``` 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()`方法中,主要负责从单例缓存中获取一个已存在的bean实例,或者使用提供的`ObjectFactory`创建一个新的实例。这是确保bean在Spring容器中作为单例存在的关键部分。 ```java public Object getSingleton(String beanName, ObjectFactory singletonFactory) { // 断言bean名称不能为空 Assert.notNull(beanName, "Bean name must not be null"); // 同步访问单例对象缓存,确保线程安全 synchronized (this.singletonObjects) { // 从缓存中获取单例对象 Object singletonObject = this.singletonObjects.get(beanName); // 如果缓存中没有找到 if (singletonObject == null) { // ... [代码部分省略以简化] try { // 使用工厂创建新的单例实例 singletonObject = singletonFactory.getObject(); newSingleton = true; } catch (IllegalStateException ex) { // ... [代码部分省略以简化] } catch (BeanCreationException ex) { // ... [代码部分省略以简化] } finally { // ... [代码部分省略以简化] } // ... [代码部分省略以简化] } // 返回单例对象 return singletonObject; } } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()`方法中,主要的逻辑是调用 `doCreateBean`,这是真正进行 bean 实例化、属性填充和初始化的地方。这个方法会返回新创建的 bean 实例。 ```java @Override protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // ... [代码部分省略以简化] try { // 正常的bean实例化、属性注入和初始化。 // 这里是真正进行bean创建的部分。 Object beanInstance = doCreateBean(beanName, mbdToUse, args); // 记录bean成功创建的日志 if (logger.isTraceEnabled()) { logger.trace("Finished creating instance of bean '" + beanName + "'"); } return beanInstance; } catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { // ... [代码部分省略以简化] } catch (Throwable ex) { // ... [代码部分省略以简化] } } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中,主要负责两大步骤,第一步是属性注入,第二步是bean初始化,确保bean是完全配置和准备好的。 ```java protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // 声明一个对象,后续可能用于存放初始化后的bean或它的代理对象 Object exposedObject = bean; // ... [代码部分省略以简化] try { // 属性注入:这一步将配置中的属性值注入到bean实例中。例如,XML中定义的属性或使用@Autowired和@Value注解的属性都会在这里被注入 populateBean(beanName, mbd, instanceWrapper); // bean初始化:这一步会执行bean的初始化方法,同时也会调用BeanPostProcessor的postProcessBeforeInitialization和postProcessAfterInitialization方法,它们可以对bean进行进一步的处理 exposedObject = initializeBean(beanName, exposedObject, mbd); } catch (Throwable ex) { // ... [代码部分省略以简化] } // 返回创建和初始化后的bean return exposedObject; } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean()`方法中,核心逻辑是对`BeanPostProcessors`接口中的`postProcessBeforeInitialization`,`postProcessAfterInitialization`进行回调。 ```java protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { // ... [代码部分省略以简化] Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } // ... [代码部分省略以简化] if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wrappedBean; } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization`方法中,遍历每一个 `BeanPostProcessor` 的 `postProcessBeforeInitialization` 方法都有机会对bean进行修改或增强 ```java @Override public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; for (BeanPostProcessor processor : getBeanPostProcessors()) { Object current = processor.postProcessBeforeInitialization(result, beanName); if (current == null) { return result; } result = current; } return result; } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization`方法中,遍历每一个 `BeanPostProcessor` 的 `postProcessAfterInitialization` 方法都有机会对bean进行修改或增强 ```java @Override public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; for (BeanPostProcessor processor : getBeanPostProcessors()) { Object current = processor.postProcessAfterInitialization(result, beanName); if (current == null) { return result; } result = current; } return result; } ``` 最后执行到我们自定义的逻辑中,将 `MyServiceImpl` 中的 `message` 属性,为其添加 `Prefix:` 前缀。在`postProcessAfterInitialization`方法中,将 `MyServiceImpl` 中的 `message` 属性,为其添加 `:Suffix` 后缀。 ```java public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if(bean instanceof MyServiceImpl) { MyServiceImpl myService = (MyServiceImpl) bean; myService.setMessage("Prefix: " + myService.getMessage()); } return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if(bean instanceof MyServiceImpl) { MyServiceImpl myService = (MyServiceImpl) bean; myService.setMessage(myService.getMessage() + " :Suffix"); } return bean; } } ``` ### 七、注意事项 **执行顺序**:Spring容器中可能注册了多个 `BeanPostProcessor`。它们的执行顺序是由它们的`Ordered`值决定的(如果实现了`Ordered`接口)。如果有多个`BeanPostProcessor`对同一个bean进行处理,它们的处理顺序可能会对结果产生影响。 **返回值**:`postProcessBeforeInitialization` 和 `postProcessAfterInitialization` 方法都可以返回一个新的bean来替代原始bean。如果我们返回`null`,那么后续的`BeanPostProcessor`不会被执行。 **注意性能**:由于`BeanPostProcessor`方法对容器中的所有bean都会被调用,所以它们可能对启动时间和性能产生影响。我们应该确保这些方法的实现尽可能高效。 **不要修改无状态bean**:无状态的bean(如工具类或帮助类)应该避免在 `BeanPostProcessor` 中被修改,因为这通常没有意义,并可能导致不必要的性能开销。 **避免在`BeanPostProcessor`中抛出异常**:应该避免在`BeanPostProcessor`方法中抛出异常。这可能会导致Spring容器启动失败或其他bean初始化失败。 ### 八、总结 #### 8.1、最佳实践总结 **启动类 `BeanPostProcessorApplication`:** 此类首先初始化了一个基于Java配置的Spring上下文。在初始化完成后,从上下文中获取了一个`MyService`类型的bean,并调用了它的`show`方法,打印了返回的值。 **配置类 `MyConfiguration`:** 这是一个基于Java的配置类,其中定义了两个beans:`MyService`和`MyBeanPostProcessor`。这确保了当Spring容器启动时,这两个beans都会被创建和管理。 **实现类 `MyBeanPostProcessor`:** 该类实现了`BeanPostProcessor`接口,并覆盖了`postProcessBeforeInitialization`和`postProcessAfterInitialization`方法。在这两个方法中,它检查当前正在处理的bean是否是`MyServiceImpl`的实例。如果是,它会修改该bean的`message`属性: - 在`postProcessBeforeInitialization`中,给`message`添加了`Prefix:`前缀。 - 在`postProcessAfterInitialization`中,给`message`添加了`:Suffix`后缀。 **接口与实现:** `MyService`接口定义了一个`show`方法,而`MyServiceImpl`是它的一个简单实现,其中包含一个`message`属性。 **运行结果:** 当我们运行`BeanPostProcessorApplication`主程序,输出结果显示`MyServiceImpl` bean的`message`属性被成功地修改了。这是通过`MyBeanPostProcessor`实现的,该实现首先在bean初始化前加了前缀,然后在初始化后加了后缀。 #### 8.2、源码分析总结 **Spring上下文初始化**: 通过`AnnotationConfigApplicationContext`的构造函数,Spring上下文会被初始化。这个过程中,`refresh()`方法是核心。 **实例化单例Beans**: 在`refresh()`方法中,`finishBeanFactoryInitialization(beanFactory)`方法负责实例化所有剩余的非懒加载单例beans。 **预实例化**: `preInstantiateSingletons()`方法负责预实例化所有的非懒加载的单例beans。对于每一个bean, 它都会调用`getBean`方法。 **获取Bean**: `getBean()`方法负责获取bean实例,其核心是`doGetBean`方法,它会处理bean的实例化、初始化以及依赖注入。 **单例保证**: 在获取单例bean时, 如果bean已存在于单例缓存中,直接返回,否则会创建一个新的实例。 **Bean创建**: `createBean`方法调用`doCreateBean`来真正进行bean的实例化、属性填充和初始化。 **属性填充与初始化**: `doCreateBean`方法有两个主要步骤: - `populateBean`: 属性注入 - `initializeBean`: 执行bean的初始化方法,同时调用`BeanPostProcessor`的方法。 **BeanPostProcessors回调**: - `initializeBean`会分别调用`applyBeanPostProcessorsBeforeInitialization`和`applyBeanPostProcessorsAfterInitialization`方法,分别在bean初始化前后进行处理。 - 每个`BeanPostProcessor`都会参与上述的回调,允许对bean进行修改或增强。 **自定义处理**: - 最后,我们的自定义`BeanPostProcessor` (`MyBeanPostProcessor`) 进行了特定的处理。在bean初始化前,为`message`添加了`Prefix:`,在bean初始化后,为`message`添加了`:Suffix`。 ================================================ FILE: spring-interface/spring-interface-beanPostProcessor/pom.xml ================================================ spring-interface com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-interface-beanPostProcessor ================================================ FILE: spring-interface/spring-interface-beanPostProcessor/src/main/java/com/xcs/spring/BeanPostProcessorApplication.java ================================================ package com.xcs.spring; import com.xcs.spring.config.MyConfiguration; import com.xcs.spring.service.MyService; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年09月16日 16时09分 **/ public class BeanPostProcessorApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MyService myService = context.getBean(MyService.class); System.out.println(myService.show()); context.close(); } } ================================================ FILE: spring-interface/spring-interface-beanPostProcessor/src/main/java/com/xcs/spring/config/MyBeanPostProcessor.java ================================================ package com.xcs.spring.config; import com.xcs.spring.service.MyServiceImpl; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; /** * @author xcs * @date 2023年09月19日 16时42分 **/ public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if(bean instanceof MyServiceImpl) { MyServiceImpl myService = (MyServiceImpl) bean; myService.setMessage("Prefix: " + myService.getMessage()); } return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if(bean instanceof MyServiceImpl) { MyServiceImpl myService = (MyServiceImpl) bean; myService.setMessage(myService.getMessage() + " :Suffix"); } return bean; } } ================================================ FILE: spring-interface/spring-interface-beanPostProcessor/src/main/java/com/xcs/spring/config/MyConfiguration.java ================================================ package com.xcs.spring.config; import com.xcs.spring.service.MyService; import com.xcs.spring.service.MyServiceImpl; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @author xcs * @date 2023年09月19日 16时35分 **/ @Configuration public class MyConfiguration { @Bean public MyService myService() { return new MyServiceImpl(); } @Bean public BeanPostProcessor myBeanPostProcessor() { return new MyBeanPostProcessor(); } } ================================================ FILE: spring-interface/spring-interface-beanPostProcessor/src/main/java/com/xcs/spring/service/MyService.java ================================================ package com.xcs.spring.service; /** * @author xcs * @date 2023年09月19日 16时43分 **/ public interface MyService { String show(); } ================================================ FILE: spring-interface/spring-interface-beanPostProcessor/src/main/java/com/xcs/spring/service/MyServiceImpl.java ================================================ package com.xcs.spring.service; /** * @author xcs * @date 2023年09月19日 16时43分 **/ public class MyServiceImpl implements MyService{ private String message = "Hello from MyService"; @Override public String show() { return message; } public void setMessage(String message) { this.message = message; } public String getMessage() { return message; } } ================================================ FILE: spring-interface/spring-interface-destructionAwareBeanPostProcessor/README.md ================================================ ## DestructionAwareBeanPostProcessor - [DestructionAwareBeanPostProcessor](#destructionawarebeanpostprocessor) - [一、基本信息](#一基本信息) - [二、接口描述](#二接口描述) - [三、接口源码](#三接口源码) - [四、主要功能](#四主要功能) - [五、最佳实践](#五最佳实践) - [六、时序图](#六时序图) - [七、源码分析](#七源码分析) - [八、注意事项](#八注意事项) - [八、总结](#八总结) - [最佳实践总结](#最佳实践总结) - [源码分析总结](#源码分析总结) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [我的CSDN](https://blog.csdn.net/duzhuang2399/article/details/133845486) 📚 **文章目录** - [所有文章](https://github.com/xuchengsheng/spring-reading) 🔗 **源码地址** - [DestructionAwareBeanPostProcessor源码](https://github.com/xuchengsheng/spring-reading/tree/master/spring-interface/spring-interface-destructionAwareBeanPostProcessor) ### 二、接口描述 `DestructionAwareBeanPostProcessor` 接口,用于提供在 bean 销毁之前进行额外处理或操作的机会。其主要职责是在 bean 即将被销毁时允许执行自定义的逻辑。 ### 三、接口源码 `DestructionAwareBeanPostProcessor` 是 Spring 框架自 1.0.1 版本开始引入的一个核心接口。为特定的 bean 类型调用自定义的初始化和销毁回调。这提供了一个机制,允许我们介入 Spring bean 的生命周期,在销毁阶段执行特定逻辑。 ```java /** * BeanPostProcessor的子接口,增加了销毁前的回调。 * * 典型的用途是在特定的bean类型上调用自定义的销毁回调, * 与相应的初始化回调相匹配。 * * @author Juergen Hoeller * @since 1.0.1 */ public interface DestructionAwareBeanPostProcessor extends BeanPostProcessor { /** * 在给定的 bean 实例销毁之前应用此 BeanPostProcessor, * 例如,调用自定义的销毁回调。 * 与 DisposableBean 的 {@code destroy} 方法和一个自定义的销毁方法一样,此回调 * 仅适用于容器完全管理其生命周期的 beans。这通常适用于单例和有作用域的 beans。 * @param bean 要被销毁的 bean 实例 * @param beanName bean 的名称 * @throws org.springframework.beans.BeansException 如果发生错误 * @see org.springframework.beans.factory.DisposableBean#destroy() * @see org.springframework.beans.factory.support.AbstractBeanDefinition#setDestroyMethodName(String) */ void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException; /** * 确定给定的 bean 实例是否需要由此后处理器销毁。 * 默认实现返回true。如果一个基于 pre-5 的 DestructionAwareBeanPostProcessor * 实现没有为此方法提供具体实现,Spring 也会默默地假设返回值为 true。 * @param bean 要检查的 bean 实例 * @return 如果需要为此 bean 实例最终调用 postProcessBeforeDestruction,返回 true,否则返回 false * @since 4.3 */ default boolean requiresDestruction(Object bean) { return true; } } ``` ### 四、主要功能 1. **销毁前逻辑** + 使用 `postProcessBeforeDestruction(Object bean, String beanName)` 方法,我们可以为 bean 执行自定义的销毁逻辑。当一个 bean 被容器标记为销毁时,此方法将被调用。(例如,容器关闭时进行资源释放,状态记录,依赖清理) ### 五、最佳实践 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类,然后从Spring上下文中获取一个`ConnectionService`类型的bean,并打印`isConnected`的状态。 ```java public class DestructionAwareBeanPostProcessorApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); ConnectionService connection = context.getBean("connectionService", ConnectionService.class); System.out.println("Is connected: " + connection.isConnected()); context.close(); } } ``` 这里使用`@Bean`注解,定义了两个Bean,是为了确保`ConnectionService`, `MyDestructionAwareBeanPostProcessor` 被 Spring 容器执行 ```java @Configuration public class MyConfiguration { @Bean public static MyDestructionAwareBeanPostProcessor myDestructionAwareBeanPostProcessor() { return new MyDestructionAwareBeanPostProcessor(); } @Bean public ConnectionService connectionService() { return new ConnectionServiceImpl(); } } ``` `MyDestructionAwareBeanPostProcessor` 类的目的是管理 `ConnectionServiceImpl` bean 的生命周期。当这个 bean 初始化完成后,它的连接被打开;当这个 bean 准备被销毁时,它的连接被关闭。这确保了资源在不再需要时被适当地释放。 ```java public class MyDestructionAwareBeanPostProcessor implements DestructionAwareBeanPostProcessor { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof ConnectionServiceImpl) { ((ConnectionServiceImpl) bean).openConnection(); } return bean; } @Override public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException { if (bean instanceof ConnectionServiceImpl) { ((ConnectionServiceImpl) bean).closeConnection(); } } @Override public boolean requiresDestruction(Object bean) { return (bean instanceof ConnectionServiceImpl); } } ``` 定义一个连接的服务接口 ```java public interface ConnectionService { void openConnection(); void closeConnection(); boolean isConnected(); } ``` `ConnectionServiceImpl` 类提供了一个模拟的连接服务。它可以跟踪其连接状态,并允许调用者打开和关闭连接,以及查询连接的状态。 ```java public class ConnectionServiceImpl implements ConnectionService { private boolean isConnected = false; @Override public void openConnection() { isConnected = true; System.out.println("connection opened."); } @Override public void closeConnection() { if (isConnected) { isConnected = false; System.out.println("connection closed."); } } @Override public boolean isConnected() { return isConnected; } } ``` 运行结果发现,由于在 `MyDestructionAwareBeanPostProcessor` 的 `postProcessAfterInitialization` 方法中,我们检测到 bean 是 `ConnectionServiceImpl` 的实例并调用了其 `openConnection` 方法,因此该连接被打开。然后我们在`DestructionAwareBeanPostProcessorApplication`类的main方法中调用 `isConnected` 方法并打印结果的直接效果。这证明了在应用上下文启动和运行期间,连接确实是打开的。由于在 `MyDestructionAwareBeanPostProcessor` 的 `postProcessBeforeDestruction` 方法中,我们检测到 bean 是 `ConnectionServiceImpl` 的实例并调用了其 `closeConnection` 方法,因此该连接被关闭。 ```java connection opened. Is connected: true connection closed. ``` ### 六、时序图 ~~~mermaid sequenceDiagram Title: DestructionAwareBeanPostProcessor时序图 participant DestructionAwareBeanPostProcessorApplication participant AnnotationConfigApplicationContext participant AbstractApplicationContext participant DefaultListableBeanFactory participant DefaultSingletonBeanRegistry participant DisposableBeanAdapter participant MyDestructionAwareBeanPostProcessor DestructionAwareBeanPostProcessorApplication->>AnnotationConfigApplicationContext: AnnotationConfigApplicationContext(componentClasses )
应用开始初始化上下文 AnnotationConfigApplicationContext-->>DestructionAwareBeanPostProcessorApplication:初始化完成 DestructionAwareBeanPostProcessorApplication->>AbstractApplicationContext:close()
请求关闭上下文 AbstractApplicationContext->>AbstractApplicationContext:doClose()
执行关闭逻辑 AbstractApplicationContext->>AbstractApplicationContext:destroyBeans()
开始销毁beans AbstractApplicationContext->>DefaultListableBeanFactory:destroySingletons()
销毁单例beans DefaultListableBeanFactory->>DefaultSingletonBeanRegistry:super.destroySingletons()
调父类销毁方法 DefaultSingletonBeanRegistry-->>DefaultListableBeanFactory:destroySingleton(beanName)
销毁单个bean DefaultListableBeanFactory->>DefaultSingletonBeanRegistry:super.destroySingleton(beanName)
调父类销毁bean方法 DefaultSingletonBeanRegistry->>DefaultSingletonBeanRegistry:destroyBean(beanName,bean)
执行销毁bean操作 DefaultSingletonBeanRegistry->>DisposableBeanAdapter:destroy()
适配器执行销毁 DisposableBeanAdapter->>MyDestructionAwareBeanPostProcessor:postProcessBeforeDestruction(bean,beanName)
执行自定义销毁逻辑 AbstractApplicationContext-->>DestructionAwareBeanPostProcessorApplication:请求关闭上下文结束 ~~~ ### 七、源码分析 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`ConnectionService`类型的bean,并打印`isConnected`的状态。 ```java public class DestructionAwareBeanPostProcessorApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); ConnectionService connection = context.getBean("connectionService", ConnectionService.class); System.out.println("Is connected: " + connection.isConnected()); context.close(); } } ``` 在`org.springframework.context.support.AbstractApplicationContext#close`方法中,首先是启动了一个同步块,它同步在 `startupShutdownMonitor` 对象上。这确保了在给定时刻只有一个线程可以执行这个块内的代码,防止多线程导致的资源竞争或数据不一致,然后是调用了 `doClose` 方法,最后是为 JVM 注册了一个关闭钩子。 ```java @Override public void close() { synchronized (this.startupShutdownMonitor) { doClose(); // If we registered a JVM shutdown hook, we don't need it anymore now: // We've already explicitly closed the context. if (this.shutdownHook != null) { try { Runtime.getRuntime().removeShutdownHook(this.shutdownHook); } catch (IllegalStateException ex) { // ignore - VM is already shutting down } } } } ``` 在`org.springframework.context.support.AbstractApplicationContext#doClose`方法中,又调用了 `destroyBeans` 方法。 ```java protected void doClose() { // ... [代码部分省略以简化] // Destroy all cached singletons in the context's BeanFactory. destroyBeans(); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.AbstractApplicationContext#destroyBeans`方法中,首先是调用了`getBeanFactory()`返回 Spring 的 `BeanFactory` ,然后在获得的 `BeanFactory` 上,调用了 `destroySingletons` 方法,这个方法的目的是销毁所有在 `BeanFactory` 中缓存的单例 beans。 ```java protected void destroyBeans() { getBeanFactory().destroySingletons(); } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#destroySingletons`方法中,首先是调用了父类的 `destroySingletons` 方法,为了确保继承自父类的销毁逻辑得到了执行。 ```java @Override public void destroySingletons() { super.destroySingletons(); updateManualSingletonNames(Set::clear, set -> !set.isEmpty()); clearByTypeCache(); } ``` 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#destroySingletons`方法中,首先是在`disposableBeans` 字段上,从其键集合中获取所有的 bean 名称,并将它们转换为一个字符串数组。`disposableBeans` 可能包含了实现了 `DisposableBean` 接口的 beans,这些 beans 需要在容器销毁时特殊处理,最后倒序循环,从最后一个开始,销毁所有在 `disposableBeans` 列表中的 beans。这样做是为了确保依赖关系正确地处理,beans先被创建的应该后被销毁。 ```java public void destroySingletons() { // ... [代码部分省略以简化] String[] disposableBeanNames; synchronized (this.disposableBeans) { disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet()); } for (int i = disposableBeanNames.length - 1; i >= 0; i--) { destroySingleton(disposableBeanNames[i]); } // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#destroySingleton`方法中,首先是调用了父类的 `destroySingleton` 方法,为了确保继承自父类的销毁逻辑得到了执行。 ```java @Override public void destroySingleton(String beanName) { super.destroySingleton(beanName); removeManualSingletonName(beanName); clearByTypeCache(); } ``` 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#destroySingleton`方法中,首先是使用 `synchronized` 关键字在 `disposableBeans` 对象上进行同步,以确保在多线程环境中安全地访问和修改它,从 `disposableBeans` 集合中移除指定名称的 bean,并将其转换为 `DisposableBean` 类型,最后调用`destroyBean`方法执行实际的销毁操作。 ```java public void destroySingleton(String beanName) { // Remove a registered singleton of the given name, if any. removeSingleton(beanName); // Destroy the corresponding DisposableBean instance. DisposableBean disposableBean; synchronized (this.disposableBeans) { disposableBean = (DisposableBean) this.disposableBeans.remove(beanName); } destroyBean(beanName, disposableBean); } ``` 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#destroyBean`方法中,直接调用 `bean` 的 `destroy` 方法。因为 `bean` 是一个 `DisposableBean` 类型的实例,所以它一定有一个 `destroy` 方法,该方法提供了 bean 的自定义销毁逻辑。 ```java protected void destroyBean(String beanName, @Nullable DisposableBean bean) { // ... [代码部分省略以简化] // Actually destroy the bean now... if (bean != null) { try { bean.destroy(); } catch (Throwable ex) { // ... [代码部分省略以简化] } } // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.support.DisposableBeanAdapter#destroy`方法中,遍历 `beanPostProcessors` 集合的循环。每一个元素都是 `DestructionAwareBeanPostProcessor` 类型,它是 `BeanPostProcessor` 的一个子接口,专门为销毁阶段提供了一个回调。 ```java @Override public void destroy() { if (!CollectionUtils.isEmpty(this.beanPostProcessors)) { for (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) { processor.postProcessBeforeDestruction(this.bean, this.beanName); } } // ... [代码部分省略以简化] } ``` 最后来到我们自定义的销毁`com.xcs.spring.config.MyDestructionAwareBeanPostProcessor#postProcessBeforeDestruction`方法中,在这个方法中,如果 bean 是 `ConnectionServiceImpl` 的一个实例,则该 bean 的 `closeConnection` 方法将被调用。这确保了每当 `ConnectionServiceImpl` 类型的 bean 被销毁之前,它的连接都会被关闭。 ```java public class MyDestructionAwareBeanPostProcessor implements DestructionAwareBeanPostProcessor { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof ConnectionServiceImpl) { ((ConnectionServiceImpl) bean).openConnection(); } return bean; } @Override public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException { if (bean instanceof ConnectionServiceImpl) { ((ConnectionServiceImpl) bean).closeConnection(); } } @Override public boolean requiresDestruction(Object bean) { return (bean instanceof ConnectionServiceImpl); } } ``` ### 八、注意事项 1. **性能影响** + 每一个 `DestructionAwareBeanPostProcessor` 在 bean 的生命周期结束时都会被调用,所以应该确保其中的代码高效执行,避免产生不必要的性能瓶颈。 2. **检查 `requiresDestruction`** + 实现 `requiresDestruction` 方法来指定哪些 beans 需要在销毁时进行处理,可以避免不必要的 `postProcessBeforeDestruction` 调用,从而提高性能。 3. **异常处理** + 在 `postProcessBeforeDestruction` 方法中可能会遇到任何类型的异常,应确保适当地处理这些异常,以避免影响其他 beans 的销毁。 4. **确保与其他 `BeanPostProcessors` 协调** + 如果我们的应用程序中还有其他 `BeanPostProcessors`,确保它们之间的相互作用不会导致问题。 5. **与 `@PreDestroy` 注解协同工作** + 如果 bean 已经使用了 `@PreDestroy` 注解来定义自己的销毁方法,这些方法会在 `postProcessBeforeDestruction` 被调用之前执行。确保这两者的逻辑不会互相干扰。 ### 八、总结 #### 最佳实践总结 1. **应用启动**: - 在 `DestructionAwareBeanPostProcessorApplication` 的 `main` 方法中,首先创建了一个 `AnnotationConfigApplicationContext` 上下文,并通过 `MyConfiguration` 类进行配置。 - 通过该上下文获取了一个名为 `connectionService` 的 `ConnectionService` 类型的 bean。 2. **Spring容器的初始化**: - 在初始化过程中,Spring容器将根据 `MyConfiguration` 类创建两个 beans: `MyDestructionAwareBeanPostProcessor` 和 `ConnectionServiceImpl`。 - 当 `ConnectionServiceImpl` bean 初始化完成后,`MyDestructionAwareBeanPostProcessor` 的 `postProcessAfterInitialization` 方法被调用,此时连接被打开。 - `MyDestructionAwareBeanPostProcessor` 的作用确保了 `ConnectionServiceImpl` bean 初始化完成后会立即打开连接。 4. **应用运行期**: - 在 `main` 方法中,应用查询了 `ConnectionService` bean 的连接状态,并打印出结果,显示连接为 "打开" 状态。 - 随后,上下文被关闭,意味着所有的单例 bean 将被销毁。 5. **销毁阶段**: - 在上下文关闭时,`MyDestructionAwareBeanPostProcessor` 的 `postProcessBeforeDestruction` 方法会被调用。 - 由于我们在这个方法中检查了 bean 是否是 `ConnectionServiceImpl` 的实例,所以 `closeConnection` 方法被调用,从而关闭连接。 - 这确保了在 bean 的生命周期结束时,资源被适当地释放。 6. **运行结果**: - 最终,控制台上的输出证明了在 bean 的生命周期开始时资源被打开,在生命周期结束时资源被关闭。 #### 源码分析总结 1. **应用启动**: - 应用通过 `AnnotationConfigApplicationContext` 启动,并用 `MyConfiguration` 类进行配置。 - 然后从Spring容器中获取了一个类型为 `ConnectionService` 的 bean,并检查其连接状态。 2. **Spring容器销毁**: - 通过调用 `context.close()`,应用启动了Spring容器的关闭流程。 - 关闭流程首先通过同步机制确保在任何时刻都只有一个线程能够执行关闭操作。 - 内部调用 `doClose` 方法来执行实际的关闭逻辑。 - JVM 的关闭钩子被移除,表示上下文已经明确关闭。 3. **销毁Beans**: - 在 `doClose` 方法中,`destroyBeans` 方法被调用,它的主要作用是销毁所有的单例 beans。 - `destroyBeans` 方法进而调用 `BeanFactory` 的 `destroySingletons` 方法。 4. **销毁单例Beans**: - `destroySingletons` 方法销毁所有在 `BeanFactory` 中缓存的单例 beans。 - 它首先获取所有需要被销毁的 beans 的名称,然后反向遍历这些 beans,确保依赖的 beans 先被销毁。 - 对每一个需要被销毁的 bean,`destroySingleton` 方法被调用。 5. **执行销毁逻辑**: - 在 `destroySingleton` 方法中,从 `disposableBeans` 列表中获取到实现了 `DisposableBean` 接口的 bean,然后调用它的 `destroy` 方法。 - 在 `DisposableBeanAdapter` 的 `destroy` 方法中,所有注册的 `DestructionAwareBeanPostProcessor` 将被遍历,对每一个处理器,都会调用其 `postProcessBeforeDestruction` 方法。 6. **自定义销毁逻辑**: - 最终,我们的自定义 `DestructionAwareBeanPostProcessor` 的 `postProcessBeforeDestruction` 方法被调用。 - 在这个方法中,检查 bean 是否是 `ConnectionServiceImpl` 的实例。如果是,关闭它的连接。 ================================================ FILE: spring-interface/spring-interface-destructionAwareBeanPostProcessor/pom.xml ================================================ spring-interface com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-interface-destructionAwareBeanPostProcessor ================================================ FILE: spring-interface/spring-interface-destructionAwareBeanPostProcessor/src/main/java/com/xcs/spring/DestructionAwareBeanPostProcessorApplication.java ================================================ package com.xcs.spring; import com.xcs.spring.config.MyConfiguration; import com.xcs.spring.service.ConnectionService; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年09月16日 16时09分 **/ public class DestructionAwareBeanPostProcessorApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); ConnectionService connection = context.getBean("connectionService", ConnectionService.class); System.out.println("Is connected: " + connection.isConnected()); context.close(); } } ================================================ FILE: spring-interface/spring-interface-destructionAwareBeanPostProcessor/src/main/java/com/xcs/spring/config/MyConfiguration.java ================================================ package com.xcs.spring.config; import com.xcs.spring.service.ConnectionService; import com.xcs.spring.service.ConnectionServiceImpl; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @author xcs * @date 2023年08月07日 16时25分 **/ @Configuration public class MyConfiguration { @Bean public static MyDestructionAwareBeanPostProcessor myDestructionAwareBeanPostProcessor() { return new MyDestructionAwareBeanPostProcessor(); } @Bean public ConnectionService connectionService() { return new ConnectionServiceImpl(); } } ================================================ FILE: spring-interface/spring-interface-destructionAwareBeanPostProcessor/src/main/java/com/xcs/spring/config/MyDestructionAwareBeanPostProcessor.java ================================================ package com.xcs.spring.config; import com.xcs.spring.service.ConnectionServiceImpl; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor; /** * @author xcs * @date 2023年09月18日 16时13分 **/ public class MyDestructionAwareBeanPostProcessor implements DestructionAwareBeanPostProcessor { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof ConnectionServiceImpl) { ((ConnectionServiceImpl) bean).openConnection(); } return bean; } @Override public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException { if (bean instanceof ConnectionServiceImpl) { ((ConnectionServiceImpl) bean).closeConnection(); } } @Override public boolean requiresDestruction(Object bean) { return (bean instanceof ConnectionServiceImpl); } } ================================================ FILE: spring-interface/spring-interface-destructionAwareBeanPostProcessor/src/main/java/com/xcs/spring/service/ConnectionService.java ================================================ package com.xcs.spring.service; /** * @author xcs * @date 2023年09月18日 17时29分 **/ public interface ConnectionService { void openConnection(); void closeConnection(); boolean isConnected(); } ================================================ FILE: spring-interface/spring-interface-destructionAwareBeanPostProcessor/src/main/java/com/xcs/spring/service/ConnectionServiceImpl.java ================================================ package com.xcs.spring.service; /** * @author xcs * @date 2023年09月18日 17时30分 **/ public class ConnectionServiceImpl implements ConnectionService { private boolean isConnected = false; @Override public void openConnection() { isConnected = true; System.out.println("connection opened."); } @Override public void closeConnection() { if (isConnected) { isConnected = false; System.out.println("connection closed."); } } @Override public boolean isConnected() { return isConnected; } } ================================================ FILE: spring-interface/spring-interface-disposableBean/README.md ================================================ ## DisposableBean - [DisposableBean](#disposablebean) - [一、基本信息](#一基本信息) - [二、接口描述](#二接口描述) - [三、接口源码](#三接口源码) - [四、主要功能](#四主要功能) - [五、最佳实践](#五最佳实践) - [六、时序图](#六时序图) - [七、源码分析](#七源码分析) - [八、注意事项](#八注意事项) - [九、总结](#九总结) - [最佳实践总结](#最佳实践总结) - [源码分析总结](#源码分析总结) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [我的CSDN](https://blog.csdn.net/duzhuang2399/article/details/133845687) 📚 **文章目录** - [所有文章](https://github.com/xuchengsheng/spring-reading) 🔗 **源码地址** - [DisposableBean源码](https://github.com/xuchengsheng/spring-reading/tree/master/spring-interface/spring-interface-disposableBean) ### 二、接口描述 `DisposableBean` 接口允许执行某些资源清理操作,比如我们可以使用这个接口来确保某些资源,比如文件句柄、网络连接、数据库连接等,被正确地释放或清理。 ### 三、接口源码 `DisposableBean` 是 Spring 框架自 12.08.2003 开始引入的一个核心接口。如果bean在销毁时希望释放资源,那么可以实现此接口,另外实现 Java 的 `AutoCloseable` 接口以达到同样的目的 ```java /** * 要实现此接口的 bean 在销毁时希望释放资源。 * 当单独销毁作用域内的 bean 时,BeanFactory 会调用 destroy 方法。 * 一个 org.springframework.context.ApplicationContext 应当在关闭时, * 根据应用的生命周期来销毁其所有的单例对象。 * * Spring 管理的 bean 也可以实现 Java 的 AutoCloseable 接口以达到同样的目的。 * 实现接口的另一种选择是指定一个自定义的销毁方法,例如在 XML bean 定义中。 * 关于所有 bean 生命周期方法的列表,参见 BeanFactory BeanFactory。 * * @author Juergen Hoeller * @since 12.08.2003 * @see InitializingBean * @see org.springframework.beans.factory.support.RootBeanDefinition#getDestroyMethodName() * @see org.springframework.beans.factory.config.ConfigurableBeanFactory#destroySingletons() * @see org.springframework.context.ConfigurableApplicationContext#close() */ public interface DisposableBean { /** * 当 bean 被其包含的 BeanFactory 销毁时调用。 * @throws Exception 如果在关闭时出现错误。错误会被记录, * 但不会被重新抛出,以允许其他 beans 正确释放他们的资源。 */ void destroy() throws Exception; } ``` ### 四、主要功能 1. **销毁回调** + 当 bean 被 Spring 容器销毁时,如果它实现了 `DisposableBean` 接口,容器会自动调用其 `destroy()` 方法。这为 beans 提供了一个机会在销毁之前执行任何必要的清理操作。 ### 五、最佳实践 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类,最后调用`context.close()`方法关闭容器。 ```java public class DisposableBeanApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); context.close(); } } ``` 这里使用`@Bean`注解,定义了一个Bean,是为了确保`MyDisposableBean`被 Spring 容器执行。 ```java @Configuration public class MyConfiguration { @Bean public static MyDisposableBean myDisposableBean(){ return new MyDisposableBean(); } } ``` `MyDisposableBean`类在实例化时会建立一个模拟的数据库连接,并且在销毁时会关闭这个连接。 ```java public class MyDisposableBean implements DisposableBean { // 模拟的数据库连接对象 private String databaseConnection; public MyDisposableBean() { // 在构造函数中模拟建立数据库连接 this.databaseConnection = "Database connection established"; System.out.println(databaseConnection); } @Override public void destroy() throws Exception { // 在 destroy 方法中模拟关闭数据库连接 databaseConnection = null; System.out.println("Database connection closed"); } } ``` 运行结果发现,当 `MyDisposableBean` bean 被销毁时,`destroy()` 方法确实被调用了,并模拟关闭了数据库连接。 ```java Database connection established Database connection closed ``` ### 六、时序图 ~~~mermaid sequenceDiagram Title: DisposableBean时序图 participant DisposableBeanApplication participant AnnotationConfigApplicationContext participant AbstractApplicationContext participant DefaultListableBeanFactory participant DefaultSingletonBeanRegistry participant DisposableBeanAdapter participant MyDisposableBean DisposableBeanApplication->>AnnotationConfigApplicationContext: AnnotationConfigApplicationContext(componentClasses )
应用开始初始化上下文 AnnotationConfigApplicationContext-->>DisposableBeanApplication:初始化完成 DisposableBeanApplication->>AbstractApplicationContext:close()
请求关闭上下文 AbstractApplicationContext->>AbstractApplicationContext:doClose()
执行关闭逻辑 AbstractApplicationContext->>AbstractApplicationContext:destroyBeans()
开始销毁beans AbstractApplicationContext->>DefaultListableBeanFactory:destroySingletons()
销毁单例beans DefaultListableBeanFactory->>DefaultSingletonBeanRegistry:super.destroySingletons()
调父类销毁方法 DefaultSingletonBeanRegistry-->>DefaultListableBeanFactory:destroySingleton(beanName)
销毁单个bean DefaultListableBeanFactory->>DefaultSingletonBeanRegistry:super.destroySingleton(beanName)
调父类销毁bean方法 DefaultSingletonBeanRegistry->>DefaultSingletonBeanRegistry:destroyBean(beanName,bean)
执行销毁bean操作 DefaultSingletonBeanRegistry->>DisposableBeanAdapter:destroy()
适配器执行销毁 DisposableBeanAdapter->>MyDisposableBean:destroy()
执行自定义销毁逻辑 AbstractApplicationContext-->>DisposableBeanApplication:请求关闭上下文结束 ~~~ ### 七、源码分析 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类,然后通过调用 `context.close()` 方法来关闭应用上下文。 ```java public class DisposableBeanApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); context.close(); } } ``` 在`org.springframework.context.support.AbstractApplicationContext#close`方法中,首先是启动了一个同步块,它同步在 `startupShutdownMonitor` 对象上。这确保了在给定时刻只有一个线程可以执行这个块内的代码,防止多线程导致的资源竞争或数据不一致,然后是调用了 `doClose` 方法,最后是为 JVM 注册了一个关闭钩子。 ```java @Override public void close() { synchronized (this.startupShutdownMonitor) { doClose(); // If we registered a JVM shutdown hook, we don't need it anymore now: // We've already explicitly closed the context. if (this.shutdownHook != null) { try { Runtime.getRuntime().removeShutdownHook(this.shutdownHook); } catch (IllegalStateException ex) { // ignore - VM is already shutting down } } } } ``` 在`org.springframework.context.support.AbstractApplicationContext#doClose`方法中,又调用了 `destroyBeans` 方法。 ```java protected void doClose() { // ... [代码部分省略以简化] // Destroy all cached singletons in the context's BeanFactory. destroyBeans(); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.AbstractApplicationContext#destroyBeans`方法中,首先是调用了`getBeanFactory()`返回 Spring 的 `BeanFactory` ,然后在获得的 `BeanFactory` 上,调用了 `destroySingletons` 方法,这个方法的目的是销毁所有在 `BeanFactory` 中缓存的单例 beans。 ```java protected void destroyBeans() { getBeanFactory().destroySingletons(); } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#destroySingletons`方法中,首先是调用了父类的 `destroySingletons` 方法,为了确保继承自父类的销毁逻辑得到了执行。 ```java @Override public void destroySingletons() { super.destroySingletons(); updateManualSingletonNames(Set::clear, set -> !set.isEmpty()); clearByTypeCache(); } ``` 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#destroySingletons`方法中,首先是在`disposableBeans` 字段上,从其键集合中获取所有的 bean 名称,并将它们转换为一个字符串数组。`disposableBeans` 可能包含了实现了 `DisposableBean` 接口的 beans,这些 beans 需要在容器销毁时特殊处理,最后倒序循环,从最后一个开始,销毁所有在 `disposableBeans` 列表中的 beans。这样做是为了确保依赖关系正确地处理,beans先被创建的应该后被销毁。 ```java public void destroySingletons() { // ... [代码部分省略以简化] String[] disposableBeanNames; synchronized (this.disposableBeans) { disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet()); } for (int i = disposableBeanNames.length - 1; i >= 0; i--) { destroySingleton(disposableBeanNames[i]); } // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#destroySingleton`方法中,首先是调用了父类的 `destroySingleton` 方法,为了确保继承自父类的销毁逻辑得到了执行。 ```java @Override public void destroySingleton(String beanName) { super.destroySingleton(beanName); removeManualSingletonName(beanName); clearByTypeCache(); } ``` 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#destroySingleton`方法中,首先是使用 `synchronized` 关键字在 `disposableBeans` 对象上进行同步,以确保在多线程环境中安全地访问和修改它,从 `disposableBeans` 集合中移除指定名称的 bean,并将其转换为 `DisposableBean` 类型,最后调用`destroyBean`方法执行实际的销毁操作。 ```java public void destroySingleton(String beanName) { // Remove a registered singleton of the given name, if any. removeSingleton(beanName); // Destroy the corresponding DisposableBean instance. DisposableBean disposableBean; synchronized (this.disposableBeans) { disposableBean = (DisposableBean) this.disposableBeans.remove(beanName); } destroyBean(beanName, disposableBean); } ``` 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#destroyBean`方法中,直接调用 `bean` 的 `destroy` 方法。因为 `bean` 是一个 `DisposableBean` 类型的实例,所以它一定有一个 `destroy` 方法,该方法提供了 bean 的自定义销毁逻辑。 ```java protected void destroyBean(String beanName, @Nullable DisposableBean bean) { // ... [代码部分省略以简化] // Actually destroy the bean now... if (bean != null) { try { bean.destroy(); } catch (Throwable ex) { // ... [代码部分省略以简化] } } // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.support.DisposableBeanAdapter#destroy`方法中,首先检查 `invokeDisposableBean` 变量,如果为true,就直接调用实现 `DisposableBean` 接口的 bean 的 `destroy` 方法。 ```java @Override public void destroy() { // ... [代码部分省略以简化] if (this.invokeDisposableBean) { if (logger.isTraceEnabled()) { logger.trace("Invoking destroy() on bean with name '" + this.beanName + "'"); } try { if (System.getSecurityManager() != null) { // ... [代码部分省略以简化] } else { ((DisposableBean) this.bean).destroy(); } } catch (Throwable ex) { // ... [代码部分省略以简化] } } } ``` 最后执行到我们自定义的逻辑中, `MyDisposableBean`类在实例化时会建立一个模拟的数据库连接,并且在销毁时会关闭这个连接。 ```java public class MyDisposableBean implements DisposableBean { // 模拟的数据库连接对象 private String databaseConnection; public MyDisposableBean() { // 在构造函数中模拟建立数据库连接 this.databaseConnection = "Database connection established"; System.out.println(databaseConnection); } @Override public void destroy() throws Exception { // 在 destroy 方法中模拟关闭数据库连接 databaseConnection = null; System.out.println("Database connection closed"); } } ``` ### 八、注意事项 1. **不要过度依赖** + 尽管 `DisposableBean` 提供了一种定义销毁逻辑的标准方法,但更推荐使用 `@PreDestroy` 注解或在 bean 定义中指定 `destroy-method` 属性。这些方法通常更简单,更具有声明性,并且避免了不必要的代码耦合。 2. **原型 bean** + 对于原型作用域的 beans,Spring 不会管理它们的完整生命周期。这意味着对于原型 beans,`DisposableBean` 的 `destroy()` 方法不会被自动调用。应确保通过其他方式处理这些 bean 的资源释放。 3. **与初始化方法配合** + 如果我们正在使用 `DisposableBean` 来处理销毁逻辑,可能也会考虑使用 `InitializingBean` 来处理初始化逻辑。但同样,推荐使用 `@PostConstruct` 注解或 `init-method` 属性来定义初始化方法。 4. **避免重复代码** + 如果多个 beans 具有相似的销毁逻辑,考虑将这些逻辑提取到一个共享方法或帮助类中,以减少重复代码和增强可维护性。 5. **避免长时间运行的操作** + `destroy()` 方法应该快速执行并释放资源。避免在其中执行可能耗时的操作,因为这可能会延迟应用的关闭过程。 ### 九、总结 #### 最佳实践总结 1. **应用启动** + 我们创建了一个启动类 `DisposableBeanApplication`,其中初始化了一个基于注解的应用上下文 `AnnotationConfigApplicationContext`。这个上下文根据 `MyConfiguration` 类配置了 Spring 容器。 2. **Java配置** + 在 `MyConfiguration` 类中,我们使用 `@Bean` 注解定义了一个名为 `myDisposableBean` 的 bean。这确保了 `MyDisposableBean` 类的实例被 Spring 容器管理。 3. **资源管理** + `MyDisposableBean` 类模拟了数据库的连接和断开过程。当这个类被实例化时,它模拟建立了一个数据库连接。然后,当这个 bean 被销毁时(由于上下文的关闭),它的 `destroy()` 方法被调用,模拟了数据库连接的关闭。 4. **应用关闭** + 在启动类的 `main` 方法的最后,通过调用 `context.close()` 方法来关闭应用上下文。这导致容器销毁所有单例 beans。在这个过程中,`MyDisposableBean` bean 的 `destroy()` 方法被调用,从而模拟关闭了数据库连接。 5. **输出结果** + 运行程序时,我们首先看到 `"Database connection established"`,这是 `MyDisposableBean` 构造函数中的输出,表示模拟的数据库连接已经建立。随后,当应用上下文关闭时,我们看到 `"Database connection closed"`,这是 `MyDisposableBean` 的 `destroy()` 方法中的输出,表示模拟的数据库连接已经关闭。 #### 源码分析总结 1. **启动和关闭**: - 我们创建了 `DisposableBeanApplication` 启动类,其中初始化了一个基于注解的应用上下文 `AnnotationConfigApplicationContext`。 - 通过传递 `MyConfiguration` 类作为构造参数来配置 Spring 容器。 - 使用 `context.close()` 方法来关闭应用上下文,触发资源的清理和释放。 2. **关闭的同步操作**: - 在 `AbstractApplicationContext#close` 方法中,启动了一个同步块,确保在给定时刻只有一个线程可以关闭应用上下文,防止资源竞争或数据不一致。 - 关闭上下文后,任何先前注册的 JVM 关闭钩子都会被移除,因为上下文已经明确地被关闭了。 3. **实际关闭操作**: - 在 `AbstractApplicationContext#doClose` 方法中,调用 `destroyBeans` 方法来销毁容器中的 beans。 4. **销毁beans**: - 通过 `AbstractApplicationContext#destroyBeans` 方法,`BeanFactory` 调用其 `destroySingletons` 方法来销毁所有缓存的单例 beans。 - 在 `DefaultListableBeanFactory` 中,首先确保调用了其父类的销毁逻辑。 - `DefaultSingletonBeanRegistry#destroySingletons` 会获取所有需要销毁的 beans 的名称,并以创建的相反顺序进行销毁,以正确处理依赖关系。 5. **具体的bean销毁**: - 对于每个要销毁的 bean,都会调用 `DefaultSingletonBeanRegistry#destroySingleton` 方法。 - 如果 bean 实现了 `DisposableBean` 接口,它的 `destroy` 方法会被调用。 - 为了确保线程安全,许多操作都在同步块中执行,特别是当操作 `disposableBeans` 时。 6. **自定义销毁逻辑**: - 最终,到达我们定义的 `MyDisposableBean` 类。当这个类的实例被销毁时,它会模拟关闭一个数据库连接。 ================================================ FILE: spring-interface/spring-interface-disposableBean/pom.xml ================================================ spring-interface com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-interface-disposableBean ================================================ FILE: spring-interface/spring-interface-disposableBean/src/main/java/com/xcs/spring/DisposableBeanApplication.java ================================================ package com.xcs.spring; import com.xcs.spring.config.MyConfiguration; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年09月16日 16时09分 **/ public class DisposableBeanApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); context.close(); } } ================================================ FILE: spring-interface/spring-interface-disposableBean/src/main/java/com/xcs/spring/config/MyConfiguration.java ================================================ package com.xcs.spring.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @author xcs * @date 2023年09月19日 16时35分 **/ @Configuration public class MyConfiguration { @Bean public static MyDisposableBean myDisposableBean(){ return new MyDisposableBean(); } } ================================================ FILE: spring-interface/spring-interface-disposableBean/src/main/java/com/xcs/spring/config/MyDisposableBean.java ================================================ package com.xcs.spring.config; import org.springframework.beans.factory.DisposableBean; /** * @author xcs * @date 2023年09月19日 16时42分 **/ public class MyDisposableBean implements DisposableBean { // 模拟的数据库连接对象 private String databaseConnection; public MyDisposableBean() { // 在构造函数中模拟建立数据库连接 this.databaseConnection = "Database connection established"; System.out.println(databaseConnection); } @Override public void destroy() throws Exception { // 在 destroy 方法中模拟关闭数据库连接 databaseConnection = null; System.out.println("Database connection closed"); } } ================================================ FILE: spring-interface/spring-interface-initializingBean/README.md ================================================ ## InitializingBean - [InitializingBean](#initializingbean) - [一、基本信息](#一基本信息) - [二、接口描述](#二接口描述) - [三、接口源码](#三接口源码) - [四、主要功能](#四主要功能) - [五、最佳实践](#五最佳实践) - [六、时序图](#六时序图) - [七、源码分析](#七源码分析) - [八、注意事项](#八注意事项) - [九、总结](#九总结) - [最佳实践总结](#最佳实践总结) - [源码分析总结](#源码分析总结) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [我的CSDN](https://blog.csdn.net/duzhuang2399/article/details/133845609) 📚 **文章目录** - [所有文章](https://github.com/xuchengsheng/spring-reading) 🔗 **源码地址** - [InitializingBean源码](https://github.com/xuchengsheng/spring-reading/tree/master/spring-interface/spring-interface-initializingBean) ### 二、接口描述 `InitializingBean` 接口,主要用于在 bean 的所有属性被初始化后,但在 bean 被实际使用之前,执行某些初始化逻辑或设置。 ### 三、接口源码 `InitializingBean` 接口,实现此接口的 beans 会在所有属性都设置完毕后,由 `BeanFactory` 调用其 `afterPropertiesSet()` 方法。 ```java /** * 接口定义,用于需要在其所有属性被 BeanFactory 设置后执行操作的 beans。 * 例如,可以执行自定义初始化或检查所有必需属性是否已设置。 * * 实现此接口的 beans 会在所有属性都设置完毕后,由 BeanFactory 调用其 `afterPropertiesSet()` 方法。 * * @author Rod Johnson * @author Juergen Hoeller * @see DisposableBean // 当 bean 不再需要时,用于回调的接口 * @see org.springframework.beans.factory.config.BeanDefinition#getPropertyValues() * @see org.springframework.beans.factory.support.AbstractBeanDefinition#getInitMethodName() */ public interface InitializingBean { /** * 当 BeanFactory 设置了 bean 的所有属性后调用此方法。 * 也即满足了 BeanFactoryAware, ApplicationContextAware 等条件后。 * * 此方法让 bean 实例可以在所有属性都设置后进行最终的配置验证和初始化。 * 如果出现配置错误(如未设置必需的属性)或因其他原因初始化失败,此方法可能会抛出异常。 * * @throws Exception 配置错误或其他任何初始化失败原因导致的异常 */ void afterPropertiesSet() throws Exception; } ``` ### 四、主要功能 1. **初始化回调** + `InitializingBean` 接口为 Spring 容器提供了一个机制,以确保在 bean 的所有属性都被设置后,但在 bean 被其他组件使用之前,可以执行某些初始化逻辑或操作。 2. **属性验证** + 在 `afterPropertiesSet` 方法中,我们可以验证 bean 的属性是否都已正确设置,特别是一些必要的属性。 3. **自定义初始化逻辑** + 如果 bean 需要进行特定的初始化操作,如开启资源、连接数据库、启动某些线程或其他任何初始化活动,那么这些操作可以在 `afterPropertiesSet` 方法中进行。 4. **生命周期管理** + `InitializingBean` 是 Spring 生命周期中的一个关键点,它在属性注入 (`Property Injection`) 之后和使用 bean 之前被调用。这提供了一个干净的生命周期钩子,可以用来确保 bean 在被使用之前是完全准备好的。 ### 五、最佳实践 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。 ```java public class InitializingBeanApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); } } ``` 这里使用`@Bean`注解,定义了一个Bean,是为了确保 `MyInitializingBean` 被 Spring 容器执行 ```java @Configuration public class MyConfiguration { @Bean public static MyInitializingBean myInitializingBean(){ return new MyInitializingBean(); } } ``` 在 `afterPropertiesSet()` 方法中,模拟了数据加载的过程。 ```java public class MyInitializingBean implements InitializingBean { private List data; public List getData() { return data; } @Override public void afterPropertiesSet() { // 在此方法中,我们模拟数据加载 data = new ArrayList<>(); data.add("数据1"); data.add("数据2"); data.add("数据3"); System.out.println("MyInitializingBean 初始化完毕,数据已加载!"); } } ``` 运行结果发现,我们会在控制台上看到 "`MyInitializingBean 初始化完毕,数据已加载!`" 这样的输出,表示数据已经被加载到 `data` 列表中。 ```java MyInitializingBean 初始化完毕,数据已加载! ``` ### 六、时序图 ~~~mermaid sequenceDiagram Title: InitializingBean时序图 participant InitializingBeanApplication participant AnnotationConfigApplicationContext participant AbstractApplicationContext participant DefaultListableBeanFactory participant AbstractBeanFactory participant DefaultSingletonBeanRegistry participant AbstractAutowireCapableBeanFactory participant MyInitializingBean InitializingBeanApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)
创建上下文 AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()
刷新上下文 AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory)
初始化Bean工厂 AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons()
实例化单例 DefaultListableBeanFactory->>AbstractBeanFactory:getBean(name)
获取Bean AbstractBeanFactory->>AbstractBeanFactory:doGetBean(name,requiredType,args,typeCheckOnly)
执行获取Bean AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton(beanName,singletonFactory)
获取单例Bean DefaultSingletonBeanRegistry-->>AbstractBeanFactory:getObject()
获取Bean实例 AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean(beanName,mbd,args)
创建Bean AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:doCreateBean(beanName,mbd,args)
执行Bean创建 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:initializeBean(beanName,bean,mbd)
负责bean的初始化 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:invokeInitMethods(beanName,bean,mbd)
调用bean的初始化方法 AbstractAutowireCapableBeanFactory->>MyInitializingBean:afterPropertiesSet()
执行InitializingBean接口的方法 AbstractAutowireCapableBeanFactory-->>AbstractBeanFactory:返回Bean对象 AbstractBeanFactory-->>DefaultListableBeanFactory:返回Bean对象 AnnotationConfigApplicationContext-->>InitializingBeanApplication:初始化完成 ~~~ ### 七、源码分析 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。 ```java public class InitializingBeanApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); } } ``` 在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法 ```java public AnnotationConfigApplicationContext(Class... componentClasses) { this(); register(componentClasses); refresh(); } ``` 在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 ```java @Override public void refresh() throws BeansException, IllegalStateException { // ... [代码部分省略以简化] // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization`方法中,会继续调用`DefaultListableBeanFactory`类中的`preInstantiateSingletons`方法来完成所有剩余非懒加载的单列Bean对象。 ```java /** * 完成此工厂的bean初始化,实例化所有剩余的非延迟初始化单例bean。 * * @param beanFactory 要初始化的bean工厂 */ protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { // ... [代码部分省略以简化] // 完成所有剩余非懒加载的单列Bean对象。 beanFactory.preInstantiateSingletons(); } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons`方法中,主要的核心目的是预先实例化所有非懒加载的单例bean。在Spring的上下文初始化完成后,该方法会被触发,以确保所有单例bean都被正确地创建并初始化。其中`getBean(beanName)`是此方法的核心操作。对于容器中定义的每一个单例bean,它都会调用`getBean`方法,这将触发bean的实例化、初始化及其依赖的注入。如果bean之前没有被创建过,那么这个调用会导致其被实例化和初始化。 ```java public void preInstantiateSingletons() throws BeansException { // ... [代码部分省略以简化] // 循环遍历所有bean的名称 for (String beanName : beanNames) { getBean(beanName); } // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#getBean()`方法中,又调用了`doGetBean`方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,`doGetBean`负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。 ```java @Override public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中,首先检查所请求的bean是否是一个单例并且已经创建。如果尚未创建,它将创建一个新的实例。在这个过程中,它处理可能的异常情况,如循环引用,并确保返回的bean是正确的类型。这是Spring容器bean生命周期管理的核心部分。 ```java protected T doGetBean( String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException { // ... [代码部分省略以简化] // 开始创建bean实例 if (mbd.isSingleton()) { // 如果bean是单例的,我们会尝试从单例缓存中获取它 // 如果不存在,则使用lambda创建一个新的实例 sharedInstance = getSingleton(beanName, () -> { try { // 尝试创建bean实例 return createBean(beanName, mbd, args); } catch (BeansException ex) { // ... [代码部分省略以简化] } }); // 对于某些bean(例如FactoryBeans),可能需要进一步处理以获取真正的bean实例 beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } // ... [代码部分省略以简化] // 确保返回的bean实例与请求的类型匹配 return adaptBeanInstance(name, beanInstance, requiredType); } ``` 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()`方法中,主要负责从单例缓存中获取一个已存在的bean实例,或者使用提供的`ObjectFactory`创建一个新的实例。这是确保bean在Spring容器中作为单例存在的关键部分。 ```java public Object getSingleton(String beanName, ObjectFactory singletonFactory) { // 断言bean名称不能为空 Assert.notNull(beanName, "Bean name must not be null"); // 同步访问单例对象缓存,确保线程安全 synchronized (this.singletonObjects) { // 从缓存中获取单例对象 Object singletonObject = this.singletonObjects.get(beanName); // 如果缓存中没有找到 if (singletonObject == null) { // ... [代码部分省略以简化] try { // 使用工厂创建新的单例实例 singletonObject = singletonFactory.getObject(); newSingleton = true; } catch (IllegalStateException ex) { // ... [代码部分省略以简化] } catch (BeanCreationException ex) { // ... [代码部分省略以简化] } finally { // ... [代码部分省略以简化] } // ... [代码部分省略以简化] } // 返回单例对象 return singletonObject; } } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()`方法中,主要的逻辑是调用 `doCreateBean`,这是真正进行 bean 实例化、属性填充和初始化的地方。这个方法会返回新创建的 bean 实例。 ```java @Override protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // ... [代码部分省略以简化] try { // 正常的bean实例化、属性注入和初始化。 // 这里是真正进行bean创建的部分。 Object beanInstance = doCreateBean(beanName, mbdToUse, args); // 记录bean成功创建的日志 if (logger.isTraceEnabled()) { logger.trace("Finished creating instance of bean '" + beanName + "'"); } return beanInstance; } catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { // ... [代码部分省略以简化] } catch (Throwable ex) { // ... [代码部分省略以简化] } } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中,`initializeBean`方法是bean初始化,确保bean是完全配置和准备好的。 ```java protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // 声明一个对象,后续可能用于存放初始化后的bean或它的代理对象 Object exposedObject = bean; // ... [代码部分省略以简化] try { // ... [代码部分省略以简化] // bean初始化 exposedObject = initializeBean(beanName, exposedObject, mbd); } catch (Throwable ex) { // ... [代码部分省略以简化] } // 返回创建和初始化后的bean return exposedObject; } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean`方法中,如果bean实现了`InitializingBean`接口,则它的`afterPropertiesSet`方法会在此处被调用。此外,如果bean配置中定义了自定义的初始化方法,spring会在这里被调用。 ```java protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { // ... [代码部分省略以简化] try { invokeInitMethods(beanName, wrappedBean, mbd); } catch (Throwable ex) { // ... [代码部分省略以简化] } // ... [代码部分省略以简化] return wrappedBean; } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeInitMethods`方法中,首先检查 bean 是否实现了 `InitializingBean` 接口。如果是,则进一步检查 `afterPropertiesSet` 方法是否被外部管理。如果不是,则调用该方法。这是 Spring bean 生命周期中的一个关键步骤,确保在 bean 被应用程序其他部分使用之前,它已经正确初始化。 ```java protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd) throws Throwable { boolean isInitializingBean = (bean instanceof InitializingBean); if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) { if (logger.isTraceEnabled()) { logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'"); } if (System.getSecurityManager() != null) { // ... [代码部分省略以简化] } else { ((InitializingBean) bean).afterPropertiesSet(); } } // ... [代码部分省略以简化] } ``` 最后执行到我们自定义的逻辑中,模拟了数据加载的过程。 ```java public class MyInitializingBean implements InitializingBean { private List data; public List getData() { return data; } @Override public void afterPropertiesSet() { // 在此方法中,我们模拟数据加载 data = new ArrayList<>(); data.add("数据1"); data.add("数据2"); data.add("数据3"); System.out.println("MyInitializingBean 初始化完毕,数据已加载!"); } } ``` ### 八、注意事项 1. **使用 @PostConstruct** + 尽管 `InitializingBean` 提供了一个初始化 bean 的方式,但现代的 Spring 我们更倾向于使用 `@PostConstruct` 注解,因为它是 JSR-250 的一部分,不依赖于 Spring 特定的接口。 2. **避免业务逻辑** + 在 `afterPropertiesSet` 方法中,应该只包含与初始化相关的逻辑。避免将核心的业务逻辑放在这里。 3. **处理异常** + `afterPropertiesSet` 方法允许抛出异常。确保我们处理了可能出现的所有异常,特别是可能阻止 bean 正确初始化的那些。 4. **明确的初始化顺序**: + 请记住,`afterPropertiesSet` 是在所有属性都设置之后调用的,但在任何自定义的 init 方法和 `@PostConstruct` 方法之前。 5. **不要过于依赖** + 尽量避免让太多的 beans 实现 `InitializingBean`,因为这可能会使代码难以阅读和管理。如果可能,考虑使用其他的初始化方法。如 `@PostConstruct` 注解。 ### 九、总结 #### 最佳实践总结 1. **启动类** + 在 `InitializingBeanApplication` 类中,我们使用 `AnnotationConfigApplicationContext` 为上下文环境。这种上下文环境使用 Java 注解来配置 Spring 容器,而不是传统的 XML。通过传递 `MyConfiguration` 类作为构造参数,我们告诉 Spring 在哪里找到 bean 的定义。 2. **配置类** + `MyConfiguration` 类使用 `@Configuration` 注解,标识它为一个 Spring 配置类。在该类中,我们定义了一个名为 `myInitializingBean` 的 bean,它返回一个新的 `MyInitializingBean` 实例。这样,我们确保 `MyInitializingBean` 类将由 Spring 容器管理,并且其生命周期方法(如 `afterPropertiesSet()`)会被调用。 3. **初始化逻辑** + `MyInitializingBean` 类实现了 `InitializingBean` 接口,并重写了其 `afterPropertiesSet()` 方法。在这个方法中,我们模拟了数据加载的过程,简单地向 `data` 列表中添加了三条字符串数据。当 Spring 容器初始化这个 bean 时,它会自动调用 `afterPropertiesSet()` 方法,从而执行这个初始化逻辑。 4. **运行结果** + 当我们运行应用程序时,由于 `MyInitializingBean` 已经被 Spring 容器管理并初始化,`afterPropertiesSet()` 方法被调用,因此我们会在控制台上看到 "`MyInitializingBean 初始化完毕,数据已加载!`" 的输出。 #### 源码分析总结 1. **启动上下文** + 使用 `AnnotationConfigApplicationContext` 以 Java 注解方式启动 Spring 上下文,传入 `MyConfiguration` 配置类为参数,此时 Spring 容器启动并初始化。 2. **构造函数中的重点** + `AnnotationConfigApplicationContext` 的构造函数执行了 `register` 和 `refresh` 方法,其中 `refresh` 是我们关注的核心。 3. **刷新上下文** + 在 `refresh` 方法中,Spring 上下文开始其核心的刷新过程,重点是 `finishBeanFactoryInitialization`,它确保实例化所有剩余的非懒加载的单例 Bean。 4. **预实例化单例 Beans** + 方法 `preInstantiateSingletons` 负责预先实例化所有非懒加载的单例 bean。这意味着在 Spring 上下文初始化完成后,所有的单例 beans 都会被实例化,初始化,并注入所需的依赖。 5. **获取 Bean** + 核心方法 `getBean` 和 `doGetBean` 负责从容器中检索 bean。如果 bean 尚未创建,这些方法还会负责 bean 的创建、属性注入和初始化。 6. **单例注册** + `getSingleton` 方法在 `DefaultSingletonBeanRegistry` 中确保 bean 作为单例存在。如果 bean 未在缓存中找到,它会使用提供的 `ObjectFactory` 创建一个新的实例。 7. **创建 Bean** + `createBean` 和 `doCreateBean` 方法负责实际的 bean 创建过程,其中包括实例化、属性注入和初始化。 8. **初始化 Bean** + 方法 `initializeBean` 负责 bean 的初始化,调用其初始化方法。这包括 `InitializingBean` 接口的 `afterPropertiesSet` 方法。 9. **初始化方法调用** + `invokeInitMethods` 方法会检查 bean 是否实现了 `InitializingBean` 接口。如果实现了,并且 `afterPropertiesSet` 方法不是外部管理的,那么它会被调用。 10. **自定义初始化逻辑** + 我们自定义的 `MyInitializingBean` 类实现了 `InitializingBean` 接口,并重写了 `afterPropertiesSet` 方法来模拟数据加载的过程。 ================================================ FILE: spring-interface/spring-interface-initializingBean/pom.xml ================================================ spring-interface com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-interface-initializingBean ================================================ FILE: spring-interface/spring-interface-initializingBean/src/main/java/com/xcs/spring/InitializingBeanApplication.java ================================================ package com.xcs.spring; import com.xcs.spring.config.MyConfiguration; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年09月16日 16时09分 **/ public class InitializingBeanApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); } } ================================================ FILE: spring-interface/spring-interface-initializingBean/src/main/java/com/xcs/spring/config/MyConfiguration.java ================================================ package com.xcs.spring.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @author xcs * @date 2023年09月19日 16时35分 **/ @Configuration public class MyConfiguration { @Bean public static MyInitializingBean myInitializingBean(){ return new MyInitializingBean(); } } ================================================ FILE: spring-interface/spring-interface-initializingBean/src/main/java/com/xcs/spring/config/MyInitializingBean.java ================================================ package com.xcs.spring.config; import org.springframework.beans.factory.InitializingBean; import java.util.ArrayList; import java.util.List; /** * @author xcs * @date 2023年09月19日 16时42分 **/ public class MyInitializingBean implements InitializingBean { private List data; public List getData() { return data; } @Override public void afterPropertiesSet() { // 在此方法中,我们模拟数据加载 data = new ArrayList<>(); data.add("数据1"); data.add("数据2"); data.add("数据3"); System.out.println("MyInitializingBean 初始化完毕,数据已加载!"); } } ================================================ FILE: spring-interface/spring-interface-instantiationAwareBeanPostProcessor/README.md ================================================ ## InstantiationAwareBeanPostProcessor - [InstantiationAwareBeanPostProcessor](#instantiationawarebeanpostprocessor) - [一、基本信息](#一基本信息) - [二、接口描述](#二接口描述) - [三、接口源码](#三接口源码) - [四、主要功能](#四主要功能) - [五、最佳实践](#五最佳实践) - [六、时序图](#六时序图) - [七、源码分析](#七源码分析) - [八、注意事项](#八注意事项) - [九、总结](#九总结) - [最佳实践总结](#最佳实践总结) - [源码分析总结](#源码分析总结) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [我的CSDN](https://blog.csdn.net/duzhuang2399/article/details/133845204) 📚 **文章目录** - [所有文章](https://github.com/xuchengsheng/spring-reading) 🔗 **源码地址** - [InstantiationAwareBeanPostProcessor源码](https://github.com/xuchengsheng/spring-reading/tree/master/spring-interface/spring-interface-instantiationAwareBeanPostProcessor) ### 二、接口描述 `InstantiationAwareBeanPostProcessor` 提供了在 bean 实例化之前和之后的回调。这意味着我们有机会在实际的目标 bean 实例之前返回一个代理,或者影响 bean 的构造。 ### 三、接口源码 `InstantiationAwareBeanPostProcessor` 是 Spring 框架自 1.2 版本开始引入的一个核心接口。**`postProcessBeforeInstantiation`**:在 bean 实例化之前调用。它允许我们返回 bean 的另一个实例,例如一个代理,这将阻止 Spring 实例化目标 bean。**`postProcessAfterInstantiation`**:在 bean 实例化之后但在设置任何属性之前调用。这可用于基于字段的依赖注入或其他自定义初始化任务。**`postProcessPropertyValues`**:在 bean 上设置属性值之前调用此方法。它允许我们修改属性,添加新属性,或返回一个完全不同的属性集。 ```java /** * 这是 BeanPostProcessor 的子接口,它为 bean 的实例化添加了新的回调方法。 * 主要是在 bean 实例化之前和之后,但在明确地设置属性或进行自动装配之前。 * * 通常,这个接口用于为特定的目标 beans 抑制默认的实例化。 * 例如,为了创建带有特殊 `TargetSources` 的代理(如池化的目标、延迟初始化的目标等), * 或为了实施其他的注入策略,例如字段注入。 * * 注意:这是一个特殊目的的接口,主要供框架内部使用。 * 建议尽量实现简单的 BeanPostProcessor 接口, * 或从 InstantiationAwareBeanPostProcessorAdapter 继承, * 以避免受到这个接口的扩展的影响。 * * @author Juergen Hoeller * @author Rod Johnson * @since 1.2 * @see org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#setCustomTargetSourceCreators * @see org.springframework.aop.framework.autoproxy.target.LazyInitTargetSourceCreator */ public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor { /** * 在目标 bean 被实例化之前应用此 BeanPostProcessor。返回的 bean 对象可能是一个代理, * 可用来代替目标 bean,有效地抑制了目标 bean 的默认实例化。 * 如果此方法返回一个非空对象,bean 的创建过程将被短路。 * * @param beanClass 要实例化的 bean 的类 * @param beanName bean 的名称 * @return 要替代目标 bean 的默认实例公开的 bean 对象,或 {@code null} 继续默认实例化 * @throws org.springframework.beans.BeansException 如果发生错误 */ @Nullable default Object postProcessBeforeInstantiation(Class beanClass, String beanName) throws BeansException { return null; } /** * 在 bean 通过构造函数或工厂方法被实例化后执行操作,但在 Spring 的属性设置(通过明确的属性或自动装配)发生之前。 * 这是在 Spring 的自动装配开始之前,对给定的 bean 实例执行自定义字段注入的理想回调。 * * @param bean 已创建的 bean 实例,其属性尚未设置 * @param beanName bean 的名称 * @return 如果应该在 bean 上设置属性,则为 {@code true};如果应跳过属性填充,则为 {@code false}。 * @throws org.springframework.beans.BeansException 如果发生错误 */ default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException { return true; } /** * 在工厂将它们应用于给定的 bean 之前,对给定的属性值进行后处理,而不需要属性描述符。 * * @param pvs 工厂即将应用的属性值(永远不为 {@code null}) * @param bean 已创建但其属性尚未设置的 bean 实例 * @param beanName bean 的名称 * @return 要应用于给定 bean 的实际属性值(可以是传入的 PropertyValues 实例),或 {@code null} * @throws org.springframework.beans.BeansException 如果发生错误 */ @Nullable default PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException { return null; } /** * 在工厂将它们应用于给定的 bean 之前,对给定的属性值进行后处理。允许检查所有的依赖关系是否都已满足, * 例如基于 bean 属性 setter 上的 "Required" 注解。 * * @param pvs 工厂即将应用的属性值(永远不为 {@code null}) * @param pds 目标 bean 的相关属性描述符 * @param bean 已创建但其属性尚未设置的 bean 实例 * @param beanName bean 的名称 * @return 要应用于给定 bean 的实际属性值(可以是传入的 PropertyValues 实例),或 {@code null} * @throws org.springframework.beans.BeansException 如果发生错误 */ @Deprecated @Nullable default PropertyValues postProcessPropertyValues( PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException { return pvs; } } ``` ### 四、主要功能 1. **实例化前的处理** + 在 bean 实际被实例化之前调用,允许你返回一个代理对象来替代真正的目标 bean,这样你可以避免 bean 的默认实例化过程,这是 AOP 和代理创建中非常有用的一个步骤。 2. **实例化后的处理** + 在 bean 实例化后但在属性注入之前调用,这个回调为你提供了在 Spring 自动装配或属性设置之前对 bean 进行自定义处理的机会。 3. **属性处理** + 允许你在 Spring 进行属性注入之前对 bean 的属性值进行处理或替换,这是在进行自定义属性注入或验证 bean 属性的理想之处。 ### 五、最佳实践 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`DataBase`类型的bean,最后打印了该bean的几个属性。这样我们就可以确认bean的状态啦。 ```java public class InstantiationAwareBeanPostProcessorApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); DataBase userService = context.getBean(DataBase.class); System.out.println("username = " + userService.getUsername()); System.out.println("password = " + userService.getPassword()); System.out.println("postInstantiationFlag = " + userService.isPostInstantiationFlag()); } } ``` 这里使用`@Bean`注解,定义了两个Bean,是为了确保`DataBase`, `MyInstantiationAwareBeanPostProcessor` 被 Spring 容器执行 ```java @Configuration public class MyConfiguration { @Bean public static MyInstantiationAwareBeanPostProcessor myInstantiationAwareBeanPostProcessor() { return new MyInstantiationAwareBeanPostProcessor(); } @Bean public DataBase dataBase() { return new DataBaseImpl(); } } ``` `MyInstantiationAwareBeanPostProcessor` 的类,该类实现了 Spring 的 `InstantiationAwareBeanPostProcessor` 接口,在`postProcessBeforeInstantiation`方法中在`DataBase`类型的bean开始实例化前,打印一条通知消息,表明我们正在准备实例化该 bean。在`postProcessAfterInstantiation`方法中`DataBase` bean实例化后,设置标记属性并通知bean已经实例化。在`postProcessProperties`:方法中给`DataBase` bean注入属性前,将密码屏蔽并打印一条消息说明密码已被屏蔽。 ```java public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor { @Override public Object postProcessBeforeInstantiation(Class beanClass, String beanName) throws BeansException { if (beanClass == DataBase.class) { System.out.println("正在准备实例化: " + beanName); } return null; } @Override public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException { if (bean instanceof DataBase) { ((DataBase) bean).setPostInstantiationFlag(true); System.out.println("Bean " + beanName + " 已实例化!"); return true; } return true; } @Override public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException { if (bean instanceof DataBase) { MutablePropertyValues mpvs = (MutablePropertyValues) pvs; mpvs.addPropertyValue("password", "******"); System.out.println(beanName + "的密码已被屏蔽:"); } return pvs; } } ``` 一个简单的接口定义 ```java public interface DataBase { String getUsername(); void setUsername(String username); String getPassword(); void setPassword(String password); boolean isPostInstantiationFlag(); void setPostInstantiationFlag(boolean flag); } ``` 使用 `@Value` 注解在 Spring 的上下文中为相应的属性提供了默认值。 ```java public class DataBaseImpl implements DataBase { @Value("root") private String username; @Value("123456") private String password; private boolean postInstantiationFlag; // get and set } ``` 运行结果发现,`dataBase`的准备实例化,到已实例化的过程日志打印了,另外最重要的是我们也吧`password`字段已经被屏蔽了。 ```java 正在准备实例化: dataBase Bean dataBase 已实例化! dataBase的密码已被屏蔽: username = root password = ****** postInstantiationFlag = true ``` ### 六、时序图 ~~~mermaid sequenceDiagram Title: InstantiationAwareBeanPostProcessor时序图 participant InstantiationAwareBeanPostProcessorApplication participant AnnotationConfigApplicationContext participant AbstractApplicationContext participant DefaultListableBeanFactory participant AbstractBeanFactory participant DefaultSingletonBeanRegistry participant AbstractAutowireCapableBeanFactory participant MyInstantiationAwareBeanPostProcessor InstantiationAwareBeanPostProcessorApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext
开始初始化 AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh
刷新上下文 AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization
实例化非懒加载的单列Bean AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons
预实例化Singleton DefaultListableBeanFactory->>AbstractBeanFactory:getBean
根据beanName获取对象 AbstractBeanFactory->>AbstractBeanFactory:doGetBean
返回指定bean的实例 AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton
获取单例对象 DefaultSingletonBeanRegistry->>AbstractBeanFactory:getObject
ObjectFactory接口的工厂方法 AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean
创建一个bean实例,填充bean实例,应用后处理器 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:resolveBeforeInstantiation AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:applyBeanPostProcessorsBeforeInstantiation AbstractAutowireCapableBeanFactory->>MyInstantiationAwareBeanPostProcessor:postProcessBeforeInstantiation
实例化前处理 MyInstantiationAwareBeanPostProcessor->>AbstractAutowireCapableBeanFactory:返回Bean对象 AbstractAutowireCapableBeanFactory-->>AbstractBeanFactory: 如果bean对象不为空直接返回,后续操作跳过 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:doCreateBean AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:populateBean AbstractAutowireCapableBeanFactory->>MyInstantiationAwareBeanPostProcessor:postProcessAfterInstantiation
实例化后处理 MyInstantiationAwareBeanPostProcessor-->>AbstractAutowireCapableBeanFactory:返回true or false
表示属性注入和后续的生命周期处理 AbstractAutowireCapableBeanFactory->>MyInstantiationAwareBeanPostProcessor:postProcessProperties
处理属性 AbstractAutowireCapableBeanFactory-->>AbstractBeanFactory: 返回Bean对象 AbstractBeanFactory-->>DefaultListableBeanFactory:返回Bean对象 AnnotationConfigApplicationContext->>InstantiationAwareBeanPostProcessorApplication:初始化完成 ~~~ ### 七、源码分析 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后通过调用`context.getBean(DataBase.class)`,应用程序从Spring容器中获取了一个名为`DataBase`的bean实例,并打印了用户名,密码,标志位。 ```java public class InstantiationAwareBeanPostProcessorApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); DataBase userService = context.getBean(DataBase.class); System.out.println("username = " + userService.getUsername()); System.out.println("password = " + userService.getPassword()); System.out.println("postInstantiationFlag = " + userService.isPostInstantiationFlag()); } } ``` 在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法 ```java public AnnotationConfigApplicationContext(Class... componentClasses) { this(); register(componentClasses); refresh(); } ``` 在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 ```java @Override public void refresh() throws BeansException, IllegalStateException { // ... [代码部分省略以简化] // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization`方法中,会继续调用`DefaultListableBeanFactory`类中的`preInstantiateSingletons`方法来完成所有剩余非懒加载的单列Bean对象。 ```java protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { // ... [代码部分省略以简化] // Instantiate all remaining (non-lazy-init) singletons. beanFactory.preInstantiateSingletons(); } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons`方法中,主要的核心目的是预先实例化所有非懒加载的单例bean。在Spring的上下文初始化完成后,该方法会被触发,以确保所有单例bean都被正确地创建并初始化。其中`getBean(beanName)`是此方法的核心操作。对于容器中定义的每一个单例bean,它都会调用`getBean`方法,这将触发bean的实例化、初始化及其依赖的注入。如果bean之前没有被创建过,那么这个调用会导致其被实例化和初始化。 ```java public void preInstantiateSingletons() throws BeansException { // ... [代码部分省略以简化] // 循环遍历所有bean的名称 for (String beanName : beanNames) { getBean(beanName); } // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#getBean()`方法中,又调用了`doGetBean`方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,`doGetBean`负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。 ```java @Override public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中,首先检查所请求的bean是否是一个单例并且已经创建。如果尚未创建,它将创建一个新的实例。在这个过程中,它处理可能的异常情况,如循环引用,并确保返回的bean是正确的类型。这是Spring容器bean生命周期管理的核心部分。 ```java protected T doGetBean( String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException { // ... [代码部分省略以简化] // 开始创建bean实例 if (mbd.isSingleton()) { // 如果bean是单例的,我们会尝试从单例缓存中获取它 // 如果不存在,则使用lambda创建一个新的实例 sharedInstance = getSingleton(beanName, () -> { try { // 尝试创建bean实例 return createBean(beanName, mbd, args); } catch (BeansException ex) { // ... [代码部分省略以简化] } }); // 对于某些bean(例如FactoryBeans),可能需要进一步处理以获取真正的bean实例 beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } // ... [代码部分省略以简化] // 确保返回的bean实例与请求的类型匹配 return adaptBeanInstance(name, beanInstance, requiredType); } ``` 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()`方法中,主要负责从单例缓存中获取一个已存在的bean实例,或者使用提供的`ObjectFactory`创建一个新的实例。这是确保bean在Spring容器中作为单例存在的关键部分。 ```java public Object getSingleton(String beanName, ObjectFactory singletonFactory) { // ... [代码部分省略以简化] // 同步访问单例对象缓存,确保线程安全 synchronized (this.singletonObjects) { // 从缓存中获取单例对象 Object singletonObject = this.singletonObjects.get(beanName); // 如果缓存中没有找到 if (singletonObject == null) { // ... [代码部分省略以简化] try { // 使用工厂创建新的单例实例 singletonObject = singletonFactory.getObject(); newSingleton = true; } catch (IllegalStateException ex) { // ... [代码部分省略以简化] } catch (BeanCreationException ex) { // ... [代码部分省略以简化] } finally { // ... [代码部分省略以简化] } // ... [代码部分省略以简化] } // 返回单例对象 return singletonObject; } } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()`方法中,首先尝试调用`resolveBeforeInstantiation`,这个方法给`InstantiationAwareBeanPostProcessor`一个机会,允许它们返回一个代理对象,而不是目标bean的实例。如果这一步返回了一个非空的对象(也就是说,一个`InstantiationAwareBeanPostProcessor`创建了一个代理对象),那么这个代理对象将作为该bean的实例返回,跳过正常的bean创建过程。如果上面的步骤没有返回任何对象,那么代码将执行`doCreateBean`方法,这个方法负责实际的bean实例化、属性注入和初始化。 ```java @Override protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // ... [代码部分省略以简化] try { // 1. 给BeanPostProcessors一个机会返回一个代理对象,而不是目标bean实例。 // 如果这步返回了一个非null的bean,那么这个bean将被返回,跳过正常的bean实例化过程。 Object bean = resolveBeforeInstantiation(beanName, mbdToUse); if (bean != null) { return bean; } } catch (Throwable ex) { // ... [代码部分省略以简化] } try { // 正常的bean实例化、属性注入和初始化。 // 2. 这里是真正进行bean创建的部分。 Object beanInstance = doCreateBean(beanName, mbdToUse, args); // 记录bean成功创建的日志 if (logger.isTraceEnabled()) { logger.trace("Finished creating instance of bean '" + beanName + "'"); } return beanInstance; } catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { // ... [代码部分省略以简化] } catch (Throwable ex) { // ... [代码部分省略以简化] } } ``` 我们来到`createBean(beanName,mbd,args)`方法中的第一步,在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation` 方法中,首先尝试在bean实际实例化之前提前完成bean的实例化。这通常是为了返回一个代理对象。`applyBeanPostProcessorsBeforeInstantiation` 方法,尝试使用 `InstantiationAwareBeanPostProcessor` 的 `postProcessBeforeInstantiation` 方法来预先实例化bean。如果上一步成功创建了bean(例如,返回了一个代理对象),那么这个bean还会经过所有注册的 `BeanPostProcessor` 的 `postProcessAfterInitialization` 方法,这是对bean进行初始化后的最后处理。 ```java protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) { Object bean = null; // 检查是否已尝试在实例化之前解析此bean if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) { // 如果bean不是合成的,并且存在InstantiationAwareBeanPostProcessors if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { // 确定目标bean的类型 Class targetType = determineTargetType(beanName, mbd); // 如果目标类型不为空 if (targetType != null) { // 尝试在实际实例化之前,通过BeanPostProcessors提前创建bean实例 bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName); // 如果bean实例创建成功 if (bean != null) { // 对bean实例应用postProcessAfterInitialization方法 bean = applyBeanPostProcessorsAfterInitialization(bean, beanName); } } } // 标记bean在实例化之前是否已被解析 mbd.beforeInstantiationResolved = (bean != null); } // 返回创建的bean实例或null return bean; } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInstantiation` 方法中,回调每一个`InstantiationAwareBeanPostProcessor`接口的`postProcessBeforeInstantiation`方法。 ```java protected Object applyBeanPostProcessorsBeforeInstantiation(Class beanClass, String beanName) { for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) { Object result = bp.postProcessBeforeInstantiation(beanClass, beanName); if (result != null) { return result; } } return null; } ``` 简单的实现`InstantiationAwareBeanPostProcessor`接口的类`MyInstantiationAwareBeanPostProcessor`,然后重写了`postProcessBeforeInstantiation`方法,此方法在bean实例化之前被调用。在bean实例化之前,我们可以选择返回一个不同的bean实例来替换原来要实例化的bean。如果我们从此方法返回非null的对象,Spring将使用我们返回的这个对象作为bean,并不会进入标准的实例化过程。如果返回null,则Spring将继续其正常的bean实例化过程,在`postProcessBeforeInstantiation`中我们仅是打印一个消息表示正在准备实例化该bean,并返回null。返回null意味着不中断正常的实例化过程。以上就是关于`InstantiationAwareBeanPostProcessor`类中的`postProcessBeforeInstantiation`方法的源码分析全过程,剩下两个方法请看后续分析。 ```java public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor { @Override public Object postProcessBeforeInstantiation(Class beanClass, String beanName) throws BeansException { if (beanClass == DataBase.class) { System.out.println("正在准备实例化: " + beanName); } return null; } } ``` 我们来到`createBean(beanName,mbd,args)`方法中的第二步,在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中,主要负责两大步骤,第一步是属性注入,第二步是bean初始化,确保bean是完全配置和准备好的。 ```java protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // 声明一个对象,后续可能用于存放初始化后的bean或它的代理对象 Object exposedObject = bean; try { // 属性注入:这一步将配置中的属性值注入到bean实例中。例如,XML中定义的属性或使用@Autowired和@Value注解的属性都会在这里被注入 populateBean(beanName, mbd, instanceWrapper); // bean初始化:这一步会执行bean的初始化方法,同时也会调用BeanPostProcessor的postProcessBeforeInitialization和postProcessAfterInitialization方法,它们可以对bean进行进一步的处理 exposedObject = initializeBean(beanName, exposedObject, mbd); } catch (Throwable ex) { // ... [代码部分省略以简化] } // 返回创建和初始化后的bean return exposedObject; } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean`方法中,首先会调用所有的`InstantiationAwareBeanPostProcessors`的`postProcessAfterInstantiation`方法,以给它们一个机会在属性设置之前修改bean的状态。如果`postProcessAfterInstantiation`方法返回的是true,它首先会尝试使用`postProcessProperties`方法来处理属性值。如果这个方法返回`null`,则会继续使用老版本的`postProcessPropertyValues`方法。 ```java protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) { // ... [代码部分省略以简化] // 如果当前的Bean不是合成的,并且存在InstantiationAwareBeanPostProcessors,那么给这些后处理器一个机会 // 在Spring填充属性之前进行处理,例如支持不同风格的字段注入。 if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) { // 如果返回false,代表此bean的属性不应继续被填充。 if (!bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) { return; } } } // ... [代码部分省略以简化] // 对每一个InstantiationAwareBeanPostProcessor进行处理,这些处理器可能会修改Bean的属性值。 for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) { // 尝试使用新版本的方法 postProcessProperties PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName); if (pvsToUse == null) { // 如果postProcessProperties返回null,尝试使用旧版本的方法 postProcessPropertyValues if (filteredPds == null) { filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); } pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName); // 如果旧版本的方法也返回null,那么此bean的属性不应继续被填充。 if (pvsToUse == null) { return; } } pvs = pvsToUse; } // ... [代码部分省略以简化] } ``` 最后调用到我们自定义的实现逻辑中来,在`postProcessAfterInstantiation`方法中设置了bean的`postInstantiationFlag`属性为`true`。这可以视为我们留下的标记,说明该bean已经被实例化了,然后返回`true`,表示我们允许Spring继续bean的初始化。然后再`postProcessProperties`方法中,修改bean的属性值将`password`属性的值修改为`"******"` ```java public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor { @Override public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException { if (bean instanceof DataBase) { ((DataBase) bean).setPostInstantiationFlag(true); System.out.println("Bean " + beanName + " 已实例化!"); return true; } return true; } @Override public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException { if (bean instanceof DataBase) { MutablePropertyValues mpvs = (MutablePropertyValues) pvs; mpvs.addPropertyValue("password", "******"); System.out.println(beanName + "的密码已被屏蔽:"); } return pvs; } } ``` ### 八、注意事项 1. **影响性能** + 这种后置处理器会在每个bean的创建过程中调用多次。尽量确保后处理器的逻辑简单且执行速度快,以减少对应用性能的影响。如果你在这三个方法`postProcessBeforeInstantiation`,`postProcessAfterInstantiation`,`postProcessProperties`中执行了复杂的逻辑,如数据库查询、远程调用或其他IO操作,由于每个bean的创建都会触发后处理器,这意味着上述方法将被频繁调用,这会严重影响应用启动时间和bean的创建性能。 2. **返回非空的bean** + 如果在`postProcessBeforeInstantiation`方法中返回了非空的bean,那么正常的bean实例化和属性设置流程将被短路。这意味着`postProcessAfterInstantiation`和`postProcessProperties`等方法将不会被调用。另外也会导致`BeanPostProcessor`类中的`postProcessBeforeInitialization`也不会被调用 3. **避免修改非目标bean** + 如果我们的`InstantiationAwareBeanPostProcessor`只对特定类型或名称的bean进行操作,确保在执行任何操作之前进行适当的检查。 ### 九、总结 #### 最佳实践总结 1. **启动与上下文初始化**: - 使用`AnnotationConfigApplicationContext`来启动应用,并注册了配置类`MyConfiguration`。 - 从Spring上下文中获取了一个`DataBase`类型的bean并打印了它的属性,这是为了验证bean状态的更改是否成功。 2. **配置类与Bean定义**: - 通过`MyConfiguration`配置类,两个Bean(`DataBase`和`MyInstantiationAwareBeanPostProcessor`)被定义。其中`MyInstantiationAwareBeanPostProcessor`是一个后处理器,它会在Spring容器中的其他Bean实例化时触发。 3. **拦截实例化过程**: - `MyInstantiationAwareBeanPostProcessor`类实现了Spring的`InstantiationAwareBeanPostProcessor`接口,这允许它介入bean的实例化、初始化和属性设置过程。 - 在`postProcessBeforeInstantiation`方法中,当`DataBase` bean开始实例化之前,一个通知消息被打印。 - 在`postProcessAfterInstantiation`方法中,bean已经实例化,此时会设置一个标记属性并打印一条通知消息。 - 在`postProcessProperties`方法中,修改了`DataBase` bean的密码属性,并打印了通知消息。 4. **DataBase接口与实现**: - 定义了一个`DataBase`接口,该接口定义了数据库连接的基本属性及其getters和setters。 - 在`DataBaseImpl`类中,实现了这个接口,并使用`@Value`注解为属性设置了默认值。 5. **运行结果**: - 从输出中可以看到,`dataBase` bean从准备实例化到实例化的过程都被成功拦截,并且密码已经被屏蔽。 #### 源码分析总结 1. **启动及Bean获取** - 应用程序启动时,`AnnotationConfigApplicationContext`类被用于初始化Spring上下文,并注册了配置类`MyConfiguration`。 - 然后,应用程序从Spring上下文中获取名为`DataBase`的bean实例并打印它的一些属性。 2. **注册Bean及后处理器** - 通过`MyConfiguration`配置类,注册了两个Bean,其中一个是`MyInstantiationAwareBeanPostProcessor`,这个后处理器用于在Bean实例化过程中介入。 3. **实例化前的拦截** - 在Bean实例化之前,Spring首先调用`postProcessBeforeInstantiation`方法。这里,我们只是简单地打印了一条消息并返回了null,表示让Spring继续执行标准的Bean实例化。 4. **Bean属性注入** - 在Bean实例化之后但属性注入之前,Spring调用`postProcessProperties`方法。 - 在这个示例中,我们修改了`password`属性的值为`"******"`并打印了一条消息。 5. **Bean实例化后的处理** - 紧接着,`postProcessAfterInstantiation`方法被调用。这里,我们简单地设置了`postInstantiationFlag`属性并打印了一条消息。 6. **Bean的完成** - 在所有这些拦截器运行后,Spring会继续进行属性注入、Bean初始化等后续工作。 - 之后,Bean将完全初始化并准备好供应用程序使用。 ================================================ FILE: spring-interface/spring-interface-instantiationAwareBeanPostProcessor/pom.xml ================================================ spring-interface com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-interface-instantiationAwareBeanPostProcessor ================================================ FILE: spring-interface/spring-interface-instantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/InstantiationAwareBeanPostProcessorApplication.java ================================================ package com.xcs.spring; import com.xcs.spring.config.MyConfiguration; import com.xcs.spring.service.DataBase; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年09月16日 16时09分 **/ public class InstantiationAwareBeanPostProcessorApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); DataBase userService = context.getBean(DataBase.class); System.out.println("username = " + userService.getUsername()); System.out.println("password = " + userService.getPassword()); System.out.println("postInstantiationFlag = " + userService.isPostInstantiationFlag()); } } ================================================ FILE: spring-interface/spring-interface-instantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/config/MyConfiguration.java ================================================ package com.xcs.spring.config; import com.xcs.spring.service.DataBaseImpl; import com.xcs.spring.service.DataBase; import org.springframework.beans.factory.InitializingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @author xcs * @date 2023年08月07日 16时25分 **/ @Configuration public class MyConfiguration implements InitializingBean { @Bean public static MyInstantiationAwareBeanPostProcessor myInstantiationAwareBeanPostProcessor() { return new MyInstantiationAwareBeanPostProcessor(); } @Bean public DataBase dataBase() { return new DataBaseImpl(); } @Override public void afterPropertiesSet() throws Exception { System.out.println("MyConfiguration.afterPropertiesSet"); } } ================================================ FILE: spring-interface/spring-interface-instantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/config/MyInstantiationAwareBeanPostProcessor.java ================================================ package com.xcs.spring.config; import com.xcs.spring.service.DataBase; import org.springframework.beans.BeansException; import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.PropertyValues; import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor; /** * @author xcs * @date 2023年09月16日 16时12分 **/ public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor { @Override public Object postProcessBeforeInstantiation(Class beanClass, String beanName) throws BeansException { if (beanClass == DataBase.class) { System.out.println("正在准备实例化: " + beanName); } return null; } @Override public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException { if (bean instanceof DataBase) { ((DataBase) bean).setPostInstantiationFlag(true); System.out.println("Bean " + beanName + " 已实例化!"); return true; } return true; } @Override public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException { if (bean instanceof DataBase) { MutablePropertyValues mpvs = (MutablePropertyValues) pvs; mpvs.addPropertyValue("password", "******"); System.out.println(beanName + "的密码已被屏蔽:"); } return pvs; } } ================================================ FILE: spring-interface/spring-interface-instantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/service/DataBase.java ================================================ package com.xcs.spring.service; /** * @author xcs * @date 2023年09月16日 16时10分 **/ public interface DataBase { String getUsername(); void setUsername(String username); String getPassword(); void setPassword(String password); boolean isPostInstantiationFlag(); void setPostInstantiationFlag(boolean flag); } ================================================ FILE: spring-interface/spring-interface-instantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/service/DataBaseImpl.java ================================================ package com.xcs.spring.service; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Value; /** * @author xcs * @date 2023年09月16日 16时10分 **/ public class DataBaseImpl implements DataBase , InitializingBean { @Value("root") private String username; @Value("123456") private String password; private boolean postInstantiationFlag; @Override public String getUsername() { return this.username; } @Override public void setUsername(String username) { this.username = username; } @Override public String getPassword() { return this.password; } @Override public void setPassword(String password) { this.password = password; } @Override public boolean isPostInstantiationFlag() { return this.postInstantiationFlag; } @Override public void setPostInstantiationFlag(boolean flag) { this.postInstantiationFlag = flag; } @Override public void afterPropertiesSet() throws Exception { System.out.println("DataBaseImpl.afterPropertiesSet"); } } ================================================ FILE: spring-interface/spring-interface-mergedBeanDefinitionPostProcessor/README.md ================================================ ## MergedBeanDefinitionPostProcessor - [MergedBeanDefinitionPostProcessor](#mergedbeandefinitionpostprocessor) - [一、基本信息](#一基本信息) - [二、接口描述](#二接口描述) - [三、接口源码](#三接口源码) - [四、主要功能](#四主要功能) - [五、最佳实践](#五最佳实践) - [六、时序图](#六时序图) - [七、源码分析](#七源码分析) - [八、注意事项](#八注意事项) - [九、总结](#九总结) - [最佳实践总结](#最佳实践总结) - [源码分析总结](#源码分析总结) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [我的CSDN](https://blog.csdn.net/duzhuang2399/article/details/133845274) 📚 **文章目录** - [所有文章](https://github.com/xuchengsheng/spring-reading) 🔗 **源码地址** - [MergedBeanDefinitionPostProcessor源码](https://github.com/xuchengsheng/spring-reading/blob/master/spring-interface/spring-interface-mergedBeanDefinitionPostProcessor) ### 二、接口描述 `MergedBeanDefinitionPostProcessor` 是 Spring 框架中的一个接口,主要用于在 bean 定义被合并后(但在 bean 实例化之前)进行后处理。它扩展了 `BeanPostProcessor`,增加了处理合并 bean 定义的能力。 ### 三、接口源码 `MergedBeanDefinitionPostProcessor` 是 Spring 框架自 2.5 版本开始引入的一个核心接口。其中的核心方法是`postProcessMergedBeanDefinition` 主要用途为提供了一个自定义或查询合并的 bean 定义的机会,例如应用自定义注释、修改 bean 元数据或基于合并的 bean 定义实现自定义行为。 ```java /** * 用于在运行时后处理合并的 bean 定义的回调接口。 * BeanPostProcessor 实现可以实现此子接口,以便在 Spring 的 BeanFactory * 用于创建 bean 实例的时候对已合并的 bean 定义(原始 bean 定义的处理副本)进行后处理。 * * #postProcessMergedBeanDefinition 方法可以用于内省 bean 定义, * 例如在后缀处理器 bean 的实例之前准备一些缓存的元数据。它也允许修改 bean 定义, * 但仅限于那些实际上用于并发修改的定义属性。本质上,这只应用于 RootBeanDefinition * 本身上定义的操作,但不包括其基类的属性。 * * @author Juergen Hoeller * @since 2.5 * @see org.springframework.beans.factory.config.ConfigurableBeanFactory#getMergedBeanDefinition */ public interface MergedBeanDefinitionPostProcessor extends BeanPostProcessor { /** * 后处理指定 bean 的给定合并 bean 定义。 * @param beanDefinition bean 的合并定义 * @param beanType 管理的 bean 实例的实际类型 * @param beanName bean 的名称 * @see AbstractAutowireCapableBeanFactory#applyMergedBeanDefinitionPostProcessors */ void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class beanType, String beanName); /** * 通知指定名称的 bean 定义已被重置, * 这个 post-processor 应该清除受影响的 bean 的任何元数据。 * 默认实现是空的。 * @param beanName bean 的名称 * @since 5.1 * @see DefaultListableBeanFactory#resetBeanDefinition */ default void resetBeanDefinition(String beanName) { } } ``` ### 四、主要功能 1. **处理合并后的 Bean 定义** + 在 Spring 中,一个 bean 可以继承另一个 bean 的配置,产生所谓的 "合并后的" bean 定义。这个合并的定义包括原始 bean 定义和任何父 bean 定义中的属性。`MergedBeanDefinitionPostProcessor` 允许我们在 bean 的实例化和初始化之前,基于这个合并的定义执行定制逻辑。 2. **缓存元数据** + 这个接口常常被用于检查 bean 定义并缓存相关的元数据,从而加速后续的 bean 实例化和初始化。例如,Spring 的 `AutowiredAnnotationBeanPostProcessor` 使用它来缓存 `@Autowired` 和 `@Value` 注解的信息。 3. **修改合并后的 Bean 定义** + 虽然不是主要的使用场景,但 `MergedBeanDefinitionPostProcessor` 也允许修改合并后的 bean 定义。但这种修改应该小心进行,并且通常只限于那些真正用于并发修改的定义属性。 ### 五、最佳实践 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyBean`类型的bean,最后打印了该`getMessage`方法返回的值。 ```java public class MergedBeanDefinitionPostProcessorApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MyBean bean = context.getBean(MyBean.class); System.out.println("message = " + bean.getMessage()); } } ``` 这里使用`@Bean`注解,定义了两个Bean,是为了确保`MyBean`, `MergedBeanDefinitionPostProcessor` 被 Spring 容器执行 ```java @Configuration public class MyConfiguration { @Bean public static MergedBeanDefinitionPostProcessor myBeanPostProcessor() { return new MyMergedBeanDefinitionPostProcessor(); } @Bean public MyBean myBean() { return new MyBean(); } } ``` 实现了 `MergedBeanDefinitionPostProcessor` 的类 `MyMergedBeanDefinitionPostProcessor`。这个类针对带有自定义注解 `MyValue` 的属性进行处理。在`postProcessMergedBeanDefinition`方法中,对于每个字段,检查是否有 `MyValue` 注解。如果有,则获取注解的值,并将字段和该值存储在 `defaultValues` 映射中。在`postProcessAfterInitialization`方法中,检查 `metadata` 是否包含这个 bean 的名称。如果包含,表示这个 bean 有需要处理的字段,还需检查该字段的当前值。如果字段的值为 null,则使用注解提供的值来设置该字段的值。 ```java public class MyMergedBeanDefinitionPostProcessor implements MergedBeanDefinitionPostProcessor { /** * 记录元数据 */ private Map> metadata = new HashMap<>(); @Override public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class beanType, String beanName) { Field[] fields = beanType.getDeclaredFields(); Map defaultValues = new HashMap<>(); for (Field field : fields) { if (field.isAnnotationPresent(MyValue.class)) { MyValue annotation = field.getAnnotation(MyValue.class); defaultValues.put(field, annotation.value()); } } if (!defaultValues.isEmpty()) { metadata.put(beanName, defaultValues); } } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (metadata.containsKey(beanName)) { Map defaultValues = metadata.get(beanName); for (Map.Entry entry : defaultValues.entrySet()) { Field field = entry.getKey(); String value = entry.getValue(); try { field.setAccessible(true); if (field.get(bean) == null) { field.set(bean, value); } } catch (IllegalAccessException e) { e.printStackTrace(); } } } return bean; } } ``` 一个简单的注解类 ```java @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface MyValue { String value(); } ``` 一个简单的Bean类 ```java public class MyBean { @MyValue("hello world") private String message; public String getMessage() { return message; } } ``` 运行结果发现,当 `MyBean` 实例化并初始化时,由于 `message` 字段的值未被明确设置,`MyMergedBeanDefinitionPostProcessor` 会使用 `MyValue` 注解中的默认值,即 "hello world",来为其赋值。 ``` message = hello world ``` ### 六、时序图 ~~~mermaid sequenceDiagram Title: MergedBeanDefinitionPostProcessor时序图 participant MergedBeanDefinitionPostProcessorApplication participant AnnotationConfigApplicationContext participant AbstractApplicationContext participant DefaultListableBeanFactory participant AbstractBeanFactory participant DefaultSingletonBeanRegistry participant AbstractAutowireCapableBeanFactory participant MyMergedBeanDefinitionPostProcessor MergedBeanDefinitionPostProcessorApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)
创建上下文 AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()
刷新上下文 AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory)
初始化Bean工厂 AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons()
实例化单例 DefaultListableBeanFactory->>AbstractBeanFactory:getBean(name)
获取Bean AbstractBeanFactory->>AbstractBeanFactory:doGetBean(name,requiredType,args,typeCheckOnly)
执行获取Bean AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton(beanName,singletonFactory)
获取单例Bean DefaultSingletonBeanRegistry-->>AbstractBeanFactory:getObject()
获取Bean实例 AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean(beanName,mbd,args)
创建Bean AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:doCreateBean(beanName,mbd,args)
执行Bean创建 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:applyMergedBeanDefinitionPostProcessors(mbd,beanType,beanName)
应用Post处理器 AbstractAutowireCapableBeanFactory->>MyMergedBeanDefinitionPostProcessor:postProcessMergedBeanDefinition(beanDefinition,beanType,beanName)
处理合并定义 AbstractAutowireCapableBeanFactory-->>AbstractBeanFactory:返回Bean对象 AbstractBeanFactory-->>DefaultListableBeanFactory:返回Bean对象 AnnotationConfigApplicationContext-->>MergedBeanDefinitionPostProcessorApplication:初始化完成 ~~~ ### 七、源码分析 ```java public class MergedBeanDefinitionPostProcessorApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MyBean bean = context.getBean(MyBean.class); System.out.println("message = " + bean.getMessage()); } } ``` 在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法 ```java public AnnotationConfigApplicationContext(Class... componentClasses) { this(); register(componentClasses); refresh(); } ``` `org.springframework.context.support.AbstractApplicationContext#refresh`方法中,我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 ```java @Override public void refresh() throws BeansException, IllegalStateException { // ... [代码部分省略以简化] // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization`方法中,会继续调用`DefaultListableBeanFactory`类中的`preInstantiateSingletons`方法来完成所有剩余非懒加载的单列Bean对象。 ```java /** * 完成此工厂的bean初始化,实例化所有剩余的非延迟初始化单例bean。 * * @param beanFactory 要初始化的bean工厂 */ protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { // ... [代码部分省略以简化] // 完成所有剩余非懒加载的单列Bean对象。 beanFactory.preInstantiateSingletons(); } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons`方法中,主要的核心目的是预先实例化所有非懒加载的单例bean。在Spring的上下文初始化完成后,该方法会被触发,以确保所有单例bean都被正确地创建并初始化。其中`getBean(beanName)`是此方法的核心操作。对于容器中定义的每一个单例bean,它都会调用`getBean`方法,这将触发bean的实例化、初始化及其依赖的注入。如果bean之前没有被创建过,那么这个调用会导致其被实例化和初始化。 ```java public void preInstantiateSingletons() throws BeansException { // ... [代码部分省略以简化] // 循环遍历所有bean的名称 for (String beanName : beanNames) { getBean(beanName); } // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#getBean()`方法中,又调用了`doGetBean`方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,`doGetBean`负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。 ```java @Override public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中,首先检查所请求的bean是否是一个单例并且已经创建。如果尚未创建,它将创建一个新的实例。在这个过程中,它处理可能的异常情况,如循环引用,并确保返回的bean是正确的类型。这是Spring容器bean生命周期管理的核心部分。 ```java protected T doGetBean( String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException { // ... [代码部分省略以简化] // 开始创建bean实例 if (mbd.isSingleton()) { // 如果bean是单例的,我们会尝试从单例缓存中获取它 // 如果不存在,则使用lambda创建一个新的实例 sharedInstance = getSingleton(beanName, () -> { try { // 尝试创建bean实例 return createBean(beanName, mbd, args); } catch (BeansException ex) { // ... [代码部分省略以简化] } }); // 对于某些bean(例如FactoryBeans),可能需要进一步处理以获取真正的bean实例 beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } // ... [代码部分省略以简化] // 确保返回的bean实例与请求的类型匹配 return adaptBeanInstance(name, beanInstance, requiredType); } ``` 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()`方法中,主要负责从单例缓存中获取一个已存在的bean实例,或者使用提供的`ObjectFactory`创建一个新的实例。这是确保bean在Spring容器中作为单例存在的关键部分。 ```java public Object getSingleton(String beanName, ObjectFactory singletonFactory) { // 断言bean名称不能为空 Assert.notNull(beanName, "Bean name must not be null"); // 同步访问单例对象缓存,确保线程安全 synchronized (this.singletonObjects) { // 从缓存中获取单例对象 Object singletonObject = this.singletonObjects.get(beanName); // 如果缓存中没有找到 if (singletonObject == null) { // ... [代码部分省略以简化] try { // 使用工厂创建新的单例实例 singletonObject = singletonFactory.getObject(); newSingleton = true; } catch (IllegalStateException ex) { // ... [代码部分省略以简化] } catch (BeanCreationException ex) { // ... [代码部分省略以简化] } finally { // ... [代码部分省略以简化] } // ... [代码部分省略以简化] } // 返回单例对象 return singletonObject; } } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()`方法中,主要的逻辑是调用 `doCreateBean`,这是真正进行 bean 实例化、属性填充和初始化的地方。这个方法会返回新创建的 bean 实例。 ```java @Override protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // ... [代码部分省略以简化] try { // 正常的bean实例化、属性注入和初始化。 // 这里是真正进行bean创建的部分。 Object beanInstance = doCreateBean(beanName, mbdToUse, args); // 记录bean成功创建的日志 if (logger.isTraceEnabled()) { logger.trace("Finished creating instance of bean '" + beanName + "'"); } return beanInstance; } catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { // ... [代码部分省略以简化] } catch (Throwable ex) { // ... [代码部分省略以简化] } } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中,在 bean 的实例化前,会对合并的 bean 定义进行处理。这是 `MergedBeanDefinitionPostProcessors` 可以介入和修改合并后的 bean 定义的地方,为了确保对每个 bean 定义只执行一次后处理,有一个 `postProcessed` 标志,如果此标志为 `false`,则会调用 `applyMergedBeanDefinitionPostProcessors` 方法应用所有的 `MergedBeanDefinitionPostProcessors`,为了线程安全,此操作在 `mbd.postProcessingLock` 的同步块中执行。这确保了并发的 bean 创建请求不会导致对同一 bean 定义的重复后处理。 ```java protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // ... [代码部分省略以简化] // 允许MergedBeanDefinitionPostProcessor修改合并的bean定义 synchronized (mbd.postProcessingLock) { if (!mbd.postProcessed) { try { applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); } catch (Throwable ex) { // ... [代码部分省略以简化] } mbd.postProcessed = true; } } // ... [代码部分省略以简化] // 返回创建和初始化后的bean return exposedObject; } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyMergedBeanDefinitionPostProcessors`方法中,遍历每一个 `MergedBeanDefinitionPostProcessor` 的 `postProcessMergedBeanDefinition` 方法,提供了一个自定义或查询合并的 bean 定义的机会。 ```java protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class beanType, String beanName) { for (MergedBeanDefinitionPostProcessor processor : getBeanPostProcessorCache().mergedDefinition) { processor.postProcessMergedBeanDefinition(mbd, beanType, beanName); } } ``` 最后执行到我们自定义的逻辑中,对于每个字段,检查是否带有 `MyValue` 注解。如果带有,从该字段获取 `MyValue` 注解,并将字段与注解的值存储在 `defaultValues` `Map` 中,如果 `defaultValues` 不为空(即存在至少一个带有 `MyValue` 注解的字段),则将该 `Map` 存储在 `metadata` 中,键为 bean 的名称。 ```java public class MyMergedBeanDefinitionPostProcessor implements MergedBeanDefinitionPostProcessor { /** * 记录元数据 */ private Map> metadata = new HashMap<>(); @Override public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class beanType, String beanName) { Field[] fields = beanType.getDeclaredFields(); Map defaultValues = new HashMap<>(); for (Field field : fields) { if (field.isAnnotationPresent(MyValue.class)) { MyValue annotation = field.getAnnotation(MyValue.class); defaultValues.put(field, annotation.value()); } } if (!defaultValues.isEmpty()) { metadata.put(beanName, defaultValues); } } } ``` ### 八、注意事项 1. **调用时机** + `postProcessMergedBeanDefinition` 是在 bean 处于一个 "半实例化" 的状态。更确切地说,在此时,bean 的实例已经被创建,但属性注入、初始化方法等还没有执行,这意味着我们不应该在此方法中尝试访问 bean 实例。 2. **避免修改不可变属性** + 虽然我们可以在 `postProcessMergedBeanDefinition` 方法中修改 `RootBeanDefinition`,但应该小心只修改那些预期为可变的属性。例如(Bean的类名,构造函数参数值,懒加载标记,依赖信息,作用域,等等) 3. **影响性能** + 如果 `postProcessMergedBeanDefinition` 执行的操作很重,这可能会影响应用的启动性能,因为它会被每个 bean 的创建过程调用。 4. **防止无限递归** + 如果我们在 `postProcessMergedBeanDefinition` 方法中尝试获取其他 beans,这可能会触发那些 beans 的创建,从而再次调用 `postProcessMergedBeanDefinition`。我们应该注意避免这种无限递归的情况。 ### 九、总结 #### 最佳实践总结 1. **启动类入口** + `MergedBeanDefinitionPostProcessorApplication` 是应用的主入口。在这里,我们使用了 `AnnotationConfigApplicationContext` 来初始化和配置 Spring 容器,并为其提供了一个配置类 `MyConfiguration`。 2. **配置类** + 在 `MyConfiguration` 中,我们定义了两个bean:一个是自定义的 `MyMergedBeanDefinitionPostProcessor`,另一个是一个简单的 `MyBean` 类实例。 3. **自定义后处理器** + `MyMergedBeanDefinitionPostProcessor` 实现了 `MergedBeanDefinitionPostProcessor` 接口,允许我们在bean的实例化之前处理和修改其定义。在这个示例中,我们检查bean的字段是否有 `MyValue` 注解。如果有,我们将字段的名称和注解的值存储在一个映射中。然后,在bean的实例化和初始化后,我们检查是否需要为字段设置值。如果字段的当前值是 `null`,我们使用 `MyValue` 注解提供的值来设置它。 4. **自定义注解** + `MyValue` 是一个简单的自定义注解,用于指定一个字段的默认值。 5. **目标Bean** + `MyBean` 是一个简单的Java类,其中一个字段 `message` 被标记为 `MyValue("hello world")`。这意味着,如果在Spring容器初始化此bean时,`message` 字段没有被明确设置一个值,那么它将使用 `MyValue` 注解中的默认值 "hello world"。 6. **执行结果** + 当应用程序运行时,Spring容器会实例化并初始化 `MyBean`。由于 `message` 字段的值未被明确设置,因此 `MyMergedBeanDefinitionPostProcessor` 将使用 `MyValue` 注解中的默认值 "hello world" 为其赋值。最后,应用程序输出 "message = hello world"。 #### 源码分析总结 1. **启动类** + 应用的主入口是`MergedBeanDefinitionPostProcessorApplication`。它使用`AnnotationConfigApplicationContext`来初始化Spring容器,并传入配置类`MyConfiguration`。 2. **初始化Spring容器** + 在`AnnotationConfigApplicationContext`的构造函数中,除了一些基本的配置外,它主要调用了`refresh()`方法来完成容器的初始化。 3. **容器刷新** + `refresh()`方法是在`AbstractApplicationContext`中定义的,用于完成容器的初始化。其中,`finishBeanFactoryInitialization(beanFactory)`方法被用来实例化所有非懒加载的单例Bean对象。 4. **实例化单例Beans** + `preInstantiateSingletons()`方法在`DefaultListableBeanFactory`中被调用,用于预先实例化所有非懒加载的单例bean。该方法通过调用`getBean(beanName)`来实例化和初始化bean。 5. **Bean获取** + `getBean()`方法在`AbstractBeanFactory`中定义,它最终会调用`doGetBean()`方法来完成实际的Bean创建工作。 6. **Bean的创建** + `doGetBean()`方法处理bean的查找、创建和依赖处理。如果请求的bean是一个单例并且尚未创建,则它将使用`getSingleton()`方法从单例缓存中获取或创建新的实例。 7. **处理单例Beans** + 在`DefaultSingletonBeanRegistry`中,`getSingleton()`方法用于从单例缓存中获取已存在的bean或使用`ObjectFactory`创建新的实例。 8. **实际Bean的创建** + 在`AbstractAutowireCapableBeanFactory`中,`createBean()`方法是Bean创建的入口,它主要调用`doCreateBean()`方法。在`doCreateBean()`中,`applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName)`方法允许`MergedBeanDefinitionPostProcessors`修改合并的bean定义。 9. **应用合并的Bean定义后处理器** + `applyMergedBeanDefinitionPostProcessors()`方法遍历并调用每个`MergedBeanDefinitionPostProcessor`的`postProcessMergedBeanDefinition()`方法。这为每个合并的Bean定义提供了自定义或查询的机会。 10. **自定义后处理器逻辑** + 在我们的例子中,`MyMergedBeanDefinitionPostProcessor`对带有`MyValue`注解的属性进行了处理。它在`postProcessMergedBeanDefinition()`中检查每个字段是否有`MyValue`注解,并为这些字段收集默认值。 ================================================ FILE: spring-interface/spring-interface-mergedBeanDefinitionPostProcessor/pom.xml ================================================ spring-interface com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-interface-mergedBeanDefinitionPostProcessor ================================================ FILE: spring-interface/spring-interface-mergedBeanDefinitionPostProcessor/src/main/java/com/xcs/spring/MergedBeanDefinitionPostProcessorApplication.java ================================================ package com.xcs.spring; import com.xcs.spring.bean.MyBean; import com.xcs.spring.config.MyConfiguration; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年09月16日 16时09分 **/ public class MergedBeanDefinitionPostProcessorApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MyBean bean = context.getBean(MyBean.class); System.out.println("message = " + bean.getMessage()); } } ================================================ FILE: spring-interface/spring-interface-mergedBeanDefinitionPostProcessor/src/main/java/com/xcs/spring/annotation/MyValue.java ================================================ package com.xcs.spring.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @author xcs * @date 2023年09月20日 10时35分 **/ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface MyValue { String value(); } ================================================ FILE: spring-interface/spring-interface-mergedBeanDefinitionPostProcessor/src/main/java/com/xcs/spring/bean/MyBean.java ================================================ package com.xcs.spring.bean; import com.xcs.spring.annotation.MyValue; /** * @author xcs * @date 2023年09月20日 10时52分 **/ public class MyBean { @MyValue("hello world") private String message; public String getMessage() { return message; } } ================================================ FILE: spring-interface/spring-interface-mergedBeanDefinitionPostProcessor/src/main/java/com/xcs/spring/config/MyConfiguration.java ================================================ package com.xcs.spring.config; import com.xcs.spring.bean.MyBean; import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @author xcs * @date 2023年09月19日 16时35分 **/ @Configuration public class MyConfiguration { @Bean public static MergedBeanDefinitionPostProcessor myBeanPostProcessor() { return new MyMergedBeanDefinitionPostProcessor(); } @Bean public MyBean myBean() { return new MyBean(); } } ================================================ FILE: spring-interface/spring-interface-mergedBeanDefinitionPostProcessor/src/main/java/com/xcs/spring/config/MyMergedBeanDefinitionPostProcessor.java ================================================ package com.xcs.spring.config; import com.xcs.spring.annotation.MyValue; import org.springframework.beans.BeansException; import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor; import org.springframework.beans.factory.support.RootBeanDefinition; import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map; /** * @author xcs * @date 2023年09月19日 16时42分 **/ public class MyMergedBeanDefinitionPostProcessor implements MergedBeanDefinitionPostProcessor { /** * 记录元数据 */ private Map> metadata = new HashMap<>(); @Override public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class beanType, String beanName) { Field[] fields = beanType.getDeclaredFields(); Map defaultValues = new HashMap<>(); for (Field field : fields) { if (field.isAnnotationPresent(MyValue.class)) { MyValue annotation = field.getAnnotation(MyValue.class); defaultValues.put(field, annotation.value()); } } if (!defaultValues.isEmpty()) { metadata.put(beanName, defaultValues); } } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (metadata.containsKey(beanName)) { Map defaultValues = metadata.get(beanName); for (Map.Entry entry : defaultValues.entrySet()) { Field field = entry.getKey(); String value = entry.getValue(); try { field.setAccessible(true); if (field.get(bean) == null) { field.set(bean, value); } } catch (IllegalAccessException e) { e.printStackTrace(); } } } return bean; } } ================================================ FILE: spring-interface/spring-interface-smartInitializingSingleton/README.md ================================================ ## SmartInitializingSingleton - [SmartInitializingSingleton](#smartinitializingsingleton) - [一、基本信息](#一基本信息) - [二、接口描述](#二接口描述) - [三、接口源码](#三接口源码) - [四、主要功能](#四主要功能) - [五、最佳实践](#五最佳实践) - [六、时序图](#六时序图) - [七、源码分析](#七源码分析) - [八、注意事项](#八注意事项) - [八、总结](#八总结) - [最佳实践总结](#最佳实践总结) - [源码分析总结](#源码分析总结) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [我的CSDN](https://blog.csdn.net/duzhuang2399/article/details/133845553) 📚 **文章目录** - [所有文章](https://github.com/xuchengsheng/spring-reading) 🔗 **源码地址** - [SmartInitializingSingleton源码](https://github.com/xuchengsheng/spring-reading/blob/master/spring-interface/spring-interface-smartInitializingSingleton) ### 二、接口描述 `SmartInitializingSingleton`接口,用于bean初始化,当所有单例bean都已完全初始化后,用此接口进行回调。 ### 三、接口源码 `SmartInitializingSingleton` 是 Spring 框架自 4.1 版本开始引入的一个核心接口。其中`afterSingletonsInstantiated()`方法会在单例预实例化阶段结束时被调用。它保证所有常规的单例beans在此时已经被创建和初始化。 ```java /** * 在BeanFactory启动时,单例预实例化阶段结束后触发的回调接口。 * 单例beans可以实现此接口,以在常规单例实例化算法后执行某些初始化, * 避免因意外的早期初始化(例如,从ListableBeanFactory#getBeansOfType调用)引起的副作用。 * 在这方面,它是InitializingBean的替代品,后者在bean的本地构建阶段结束时被触发。 * * 这个回调变种与org.springframework.context.event.ContextRefreshedEvent有些类似, * 但不需要实现org.springframework.context.ApplicationListener, * 也不需要在上下文层次结构中过滤上下文引用等。它还意味着仅依赖于beans包, * 并由单独的ListableBeanFactory实现尊重,不仅仅在org.springframework.context.ApplicationContext环境中。 * * 注意: 如果我们打算开始/管理异步任务,最好实现org.springframework.context.Lifecycle, * 它提供了一个更丰富的运行时管理模型,并允许分阶段启动/关闭。 * * @author Juergen Hoeller * @since 4.1 * @see org.springframework.beans.factory.config.ConfigurableListableBeanFactory#preInstantiateSingletons() */ public interface SmartInitializingSingleton { /** * 在单例预实例化阶段的末尾调用, * 保证所有常规单例beans已经创建。在此方法中的 * ListableBeanFactory#getBeansOfType调用不会在引导期间引起意外的副作用。 * 注意: 此回调不会为在BeanFactory启动后按需延迟初始化的单例beans触发, * 也不会触发任何其他bean范围。仅为具有预期引导语义的beans小心使用它。 */ void afterSingletonsInstantiated(); } ``` ### 四、主要功能 1. **bean已完全初始化后回调** + 提供了一个回调机制,允许单例bean在Spring容器中所有其他常规单例bean都已完全初始化之后,执行某些特定的初始化操作。具体来说,当所有的单例bean都被实例化和初始化后,`SmartInitializingSingleton`接口中的`afterSingletonsInstantiated()`方法会被调用。 ### 五、最佳实践 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。 ```java public class SmartInitializingSingletonApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); } } ``` 这里使用`@Bean`注解,定义了一个Bean,是为了确保 `MySmartInitializingSingleton` 被 Spring 容器执行 ```java @Configuration @ComponentScan("com.xcs.spring.service") public class MyConfiguration { @Bean public static MySmartInitializingSingleton mySmartInitializingSingleton(){ return new MySmartInitializingSingleton(); } } ``` `MySmartInitializingSingleton`类中,在所有其他的单例bean被完全初始化后,然后在`afterSingletonsInstantiated()`方法会启动`MyService`类中定义的定时任务。 ```java public class MySmartInitializingSingleton implements SmartInitializingSingleton { @Autowired private MyService myService; @Override public void afterSingletonsInstantiated() { myService.startScheduledTask(); } } ``` `MyService`定义了一个定时任务,该任务会每隔2秒打印出当前的日期时间和"hello world"消息。其中`MySmartInitializingSingleton`会在所有的单例bean完全初始化后,调用`startScheduledTask()`方法,从而启动定时任务。 ```java @Service public class MyService { /** * 这里使用了Java的Timer来模拟定时任务。在实际应用中,可能会使用更复杂的调度机制。 */ public void startScheduledTask() { new java.util.Timer().schedule( new java.util.TimerTask() { @Override public void run() { System.out.println(getDate() + " hello world "); } }, 0, 2000 ); } /** * 获取当前时间 * * @return */ public String getDate() { LocalDateTime now = LocalDateTime.now(); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); return now.format(formatter); } } ``` 运行结果发现,`MySmartInitializingSingleton`成功地在所有其他的单例bean初始化后启动了`MyService`中的定时任务。我们的实现是正确的,每隔2秒都会产生下述输出。 ```java 2023-09-27 10:41:36 hello world 2023-09-27 10:41:38 hello world 2023-09-27 10:41:40 hello world 2023-09-27 10:41:42 hello world 2023-09-27 10:41:44 hello world ``` ### 六、时序图 ~~~mermaid sequenceDiagram Title: SmartInitializingSingleton时序图 participant SmartInitializingSingletonApplication participant AnnotationConfigApplicationContext participant AbstractApplicationContext participant DefaultListableBeanFactory participant MySmartInitializingSingleton SmartInitializingSingletonApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)
创建上下文 AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()
刷新上下文 AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory)
初始化Bean工厂 AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons()
实例化单例 DefaultListableBeanFactory->>MySmartInitializingSingleton:afterSingletonsInstantiated()
所有单例初始化 ~~~ ### 七、源码分析 ```java public class SmartInitializingSingletonApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); } } ``` 在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法 ```java public AnnotationConfigApplicationContext(Class... componentClasses) { this(); register(componentClasses); refresh(); } ``` 在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 ```java @Override public void refresh() throws BeansException, IllegalStateException { // ... [代码部分省略以简化] // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization`方法中,会继续调用`DefaultListableBeanFactory`类中的`preInstantiateSingletons`方法来完成所有剩余非懒加载的单列Bean对象。 ```java protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { // ... [代码部分省略以简化] // Instantiate all remaining (non-lazy-init) singletons. beanFactory.preInstantiateSingletons(); } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons`方法中,实现`SmartInitializingSingleton`接口的beans在所有其他的单例beans完全实例化后才会触发其`afterSingletonsInstantiated`方法,从而确保了初始化的正确时序。 ```java @Override public void preInstantiateSingletons() throws BeansException { // ... [代码部分省略以简化] // 触发所有SmartInitializingSingleton bean的初始化后回调。。。 for (String beanName : beanNames) { Object singletonInstance = getSingleton(beanName); if (singletonInstance instanceof SmartInitializingSingleton) { // ... [代码部分省略以简化] smartSingleton.afterSingletonsInstantiated(); // ... [代码部分省略以简化] } } } ``` ### 八、注意事项 1. **避免复杂逻辑**: + `SmartInitializingSingleton`的设计是为了执行初始化后的逻辑。避免在`afterSingletonsInstantiated()`方法中加入过于复杂的逻辑。 2. **注意依赖关系** + 当`afterSingletonsInstantiated()`被调用时,所有的常规单例bean都已经被初始化。但请确保在这个方法中调用的任何bean已经完全初始化并且所有的依赖都被满足。 3. **避免早期初始化** + 请确保不会意外地触发其他bean的早期初始化,尤其是在`afterSingletonsInstantiated()`方法中。早期初始化可能会导致不可预见的副作用。 4. **限制范围** + `SmartInitializingSingleton`仅对常规单例bean起作用。对于在`BeanFactory`启动后按需延迟初始化或其他作用域的beans(如原型作用域),此回调不会被触发。 5. **异步任务** + 如果我们的目的是启动或管理异步任务,最好使用`Lifecycle`接口或考虑其他Spring的启动监听器,如`ApplicationListener`。`Lifecycle`为运行时管理提供了一个更完善的模型。 6. **确保幂等性** + 如果有可能多次刷新应用程序上下文(虽然在我看来这种情况基本上很少),请确保`afterSingletonsInstantiated()`方法的实现是幂等的,即多次执行与一次执行产生的效果相同。 7. **与`InitializingBean`和`@PostConstruct`的区别** + `SmartInitializingSingleton`和`InitializingBean`或`@PostConstruct`注解有区别。后两者是bean级别的初始化回调,而`SmartInitializingSingleton`是容器级别的,确保在所有bean初始化之后才执行。 8. **不要滥用** + 只有在确实需要确保所有其他bean都初始化后才执行某些操作时,才应使用`SmartInitializingSingleton`。如果不需要这种保证,考虑使用更标准的初始化回调。 ### 八、总结 #### 最佳实践总结 1. **启动入口** + 在示例的启动类`SmartInitializingSingletonApplication`中,我们使用了`AnnotationConfigApplicationContext`来加载和初始化Spring容器。我们为上下文提供了一个Java配置类`MyConfiguration`,该类定义了应该由Spring扫描和管理的bean。 2. **配置** + 在`MyConfiguration`类中,我们使用`@Bean`注解显式地定义了`MySmartInitializingSingleton`这个bean。这确保了`MySmartInitializingSingleton`被Spring容器管理并在适当的时机执行。 3. **实现SmartInitializingSingleton接口** + `MySmartInitializingSingleton`实现了`SmartInitializingSingleton`接口。当所有其他的单例bean都被完全初始化后,`afterSingletonsInstantiated()`方法被调用。在这个方法中,我们启动了`MyService`类中定义的定时任务。 4. **定时任务** + `MyService`中定义了一个使用Java的`Timer`模拟的定时任务。这个任务会每隔2秒打印当前时间和"hello world"这个消息。在实际应用中,可能会使用更复杂的调度机制,如Spring的`TaskScheduler`或Quartz等。 5. **结果** + 启动示例应用后,可以观察到每隔2秒在控制台上都会输出格式化的当前时间后跟着"hello world"这样的消息,证明定时任务已经成功启动并在运行。 #### 源码分析总结 1. **应用启动** + 一切从`SmartInitializingSingletonApplication`的主函数开始,其中初始化了`AnnotationConfigApplicationContext`,这是Spring用于Java注解配置的上下文。 2. **AnnotationConfigApplicationContext构造函数** + 在`AnnotationConfigApplicationContext`的构造函数中,执行了三个主要步骤,其中最关键的是`refresh()`方法。 3. **执行refresh方法** + `refresh()`方法是Spring上下文刷新的核心。在这里,重点是`finishBeanFactoryInitialization(beanFactory)`,它负责实例化所有剩余的非懒加载单例Bean。 4. **完成BeanFactory初始化** + 在`finishBeanFactoryInitialization`方法中,为了完成上述任务,它进一步调用了`beanFactory.preInstantiateSingletons()`。 5. **预实例化单例** + 这步是最关键的。在`DefaultListableBeanFactory`的`preInstantiateSingletons`方法中,所有单例beans都被实例化。紧接着,为那些实现了`SmartInitializingSingleton`接口的beans触发了`afterSingletonsInstantiated`回调,确保这些回调在所有其他单例beans完全实例化后才被执行。 ================================================ FILE: spring-interface/spring-interface-smartInitializingSingleton/pom.xml ================================================ spring-interface com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-interface-smartInitializingSingleton ================================================ FILE: spring-interface/spring-interface-smartInitializingSingleton/src/main/java/com/xcs/spring/SmartInitializingSingletonApplication.java ================================================ package com.xcs.spring; import com.xcs.spring.config.MyConfiguration; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年09月16日 16时09分 **/ public class SmartInitializingSingletonApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); } } ================================================ FILE: spring-interface/spring-interface-smartInitializingSingleton/src/main/java/com/xcs/spring/config/MyConfiguration.java ================================================ package com.xcs.spring.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; /** * @author xcs * @date 2023年09月19日 16时35分 **/ @Configuration @ComponentScan("com.xcs.spring.service") public class MyConfiguration { @Bean public static MySmartInitializingSingleton mySmartInitializingSingleton(){ return new MySmartInitializingSingleton(); } } ================================================ FILE: spring-interface/spring-interface-smartInitializingSingleton/src/main/java/com/xcs/spring/config/MySmartInitializingSingleton.java ================================================ package com.xcs.spring.config; import com.xcs.spring.service.MyService; import org.springframework.beans.factory.SmartInitializingSingleton; import org.springframework.beans.factory.annotation.Autowired; /** * @author xcs * @date 2023年09月19日 16时42分 **/ public class MySmartInitializingSingleton implements SmartInitializingSingleton { @Autowired private MyService myService; @Override public void afterSingletonsInstantiated() { myService.startScheduledTask(); } } ================================================ FILE: spring-interface/spring-interface-smartInitializingSingleton/src/main/java/com/xcs/spring/service/MyService.java ================================================ package com.xcs.spring.service; import org.springframework.stereotype.Service; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; /** * @author xcs * @date 2023年09月27日 10时36分 **/ @Service public class MyService { /** * 这里使用了Java的Timer来模拟定时任务。在实际应用中,可能会使用更复杂的调度机制。 */ public void startScheduledTask() { new java.util.Timer().schedule( new java.util.TimerTask() { @Override public void run() { System.out.println(getDate() + " hello world "); } }, 0, 2000 ); } /** * 获取当前时间 * * @return */ public String getDate() { LocalDateTime now = LocalDateTime.now(); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); return now.format(formatter); } } ================================================ FILE: spring-interface/spring-interface-smartInstantiationAwareBeanPostProcessor/README.md ================================================ ## SmartInstantiationAwareBeanPostProcessor - [SmartInstantiationAwareBeanPostProcessor](#smartinstantiationawarebeanpostprocessor) - [一、基本信息](#一基本信息) - [二、接口描述](#二接口描述) - [三、接口源码](#三接口源码) - [四、主要功能](#四主要功能) - [五、最佳实践](#五最佳实践) - [六、时序图](#六时序图) - [七、源码分析](#七源码分析) - [八、注意事项](#八注意事项) - [九、总结](#九总结) - [最佳实践总结](#最佳实践总结) - [源码分析总结](#源码分析总结) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [我的CSDN](https://blog.csdn.net/duzhuang2399/article/details/133845401) 📚 **文章目录** - [所有文章](https://github.com/xuchengsheng/spring-reading) 🔗 **源码地址** - [SmartInstantiationAwareBeanPostProcessor源码](https://github.com/xuchengsheng/spring-reading/blob/master/spring-interface/spring-interface-smartInstantiationAwareBeanPostProcessor) ### 二、接口描述 `InstantiationAwareBeanPostProcessor`。接口,能够对 Spring 容器创建的 beans 进行更精细的控制和更多的干预,尤其是在涉及代理和其他高级场景时。 ### 三、接口源码 `SmartInstantiationAwareBeanPostProcessor` 是 Spring 框架自 2.0.3 版本开始引入的一个核心接口,主要用于框架内部。正常情况下我们实现`BeanPostProcessor`接口或者`InstantiationAwareBeanPostProcessorAdapter`接口就能满足自定义需求。 ```java /** * InstantiationAwareBeanPostProcessor 接口的扩展, * 增加了预测处理的bean的最终类型的回调方法。 * * 注意: 这是一个特定目的的接口,主要用于 * 框架内部。一般来说,应用程序提供的后处理器应该 * 直接实现简单的 BeanPostProcessor * 接口或继承 InstantiationAwareBeanPostProcessorAdapter 类。 * 即使在点版本中,也可能向此接口添加新方法。 * * @author Juergen Hoeller * @since 2.0.3 * @see InstantiationAwareBeanPostProcessorAdapter */ public interface SmartInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessor { /** * 预测从此处理器的 #postProcessBeforeInstantiation 回调返回的bean的类型。 * 默认实现返回 null。 * @param beanClass bean的原始类 * @param beanName bean的名称 * @return bean的类型,如果不可预测则为 null * @throws org.springframework.beans.BeansException 出错时抛出 */ @Nullable default Class predictBeanType(Class beanClass, String beanName) throws BeansException { return null; } /** * 确定给定bean的候选构造函数。 * 默认实现返回 null。 * @param beanClass bean的原始类(永远不是 null) * @param beanName bean的名称 * @return 候选构造函数,如果没有指定则为 null * @throws org.springframework.beans.BeansException 出错时抛出 */ @Nullable default Constructor[] determineCandidateConstructors(Class beanClass, String beanName) throws BeansException { return null; } /** * 为了解决循环引用,提前获取指定bean的引用。 * 此回调为后处理器提供了一个机会,可以在目标bean实例完全初始化之前暴露一个包装器。 * 暴露的对象应当等同于 #postProcessBeforeInitialization / * #postProcessAfterInitialization 否则会暴露。需要注意的是, * 由此方法返回的对象将被用作bean引用,除非后处理器从上述后处理回调中返回一个不同的包装器。 * 默认实现返回给定的 bean 原样。 * @param bean 原始bean实例 * @param beanName bean的名称 * @return 作为bean引用暴露的对象(通常使用传入的bean实例作为默认值) * @throws org.springframework.beans.BeansException 出错时抛出 */ default Object getEarlyBeanReference(Object bean, String beanName) throws BeansException { return bean; } } ``` ### 四、主要功能 1. **预测 Bean 类型 (predictBeanType)** + 这个方法允许在实例化 bean 之前预测 bean 的最终类型。这在涉及代理或其他类型转换的场景中特别有用,例如,一个 bean 可能会被一个 AOP 代理包裹,此方法可以返回预期的代理类型而不是实际的目标类型,这有助于 Spring 在创建和连接 bean 时做出更加明智的决策,特别是在涉及类型匹配(如自动装配)时。 2. **确定候选构造函数 (determineCandidateConstructors)** + 在 bean 实例化之前,这个方法允许确定用于给定 bean 的构造函数,这为我们提供了一种方式来定制或干预 Spring 默认的构造函数选择逻辑,例如,当存在多个构造函数并且我们想基于特定逻辑选择其中一个时。 3. **获取早期 Bean 引用 (getEarlyBeanReference)** + 这个方法提供了一个机会,允许在 bean 完全初始化之前暴露一个包装器或代理,它在处理循环依赖时特别有用,当一个 bean 还未完全初始化但另一个 bean 需要引用它时,这个方法就会被调用,这样,我们可以暴露一个早期的 bean 引用,可能是一个代理,这个代理在完成所有初始化步骤后仍然有效。 ### 五、最佳实践 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。 ```java public class SmartInstantiationAwareBeanPostProcessorApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); } } ``` 这里使用`@Bean`注解,定义了一个Bean,是为了确保 `MySmartInstantiationAwareBeanPostProcessor` 被 Spring 容器执行 ```java @Configuration @ComponentScan("com.xcs.spring") public class MyConfiguration { @Bean public static MySmartInstantiationAwareBeanPostProcessor mySmartInstantiationAwareBeanPostProcessor(){ return new MySmartInstantiationAwareBeanPostProcessor(); } } ``` 自定义的 `SmartInstantiationAwareBeanPostProcessor` 实现,然后我们重写了 `determineCandidateConstructors` 方法。如果类中有一个或多个带有 `@MyAutowired` 注解的构造函数,这些构造函数将被作为候选返回,如果没有找到任何带有 `@MyAutowired` 注解的构造函数,那么后处理器会尝试查找默认(无参数)的构造函数,如果没有找到带有 `@MyAutowired` 注解的构造函数,并且没有默认构造函数,那么所有可用的构造函数将被作为候选返回,从而使 Spring 能够选择最具体的构造函数。 ```java public class MySmartInstantiationAwareBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor { @Override public Constructor[] determineCandidateConstructors(Class beanClass, String beanName) throws BeansException { // 首先,查找@MyAutowired带注释的构造函数 List> myAutowiredConstructors = Arrays.stream(beanClass.getConstructors()) .filter(constructor -> constructor.isAnnotationPresent(MyAutowired.class)) .collect(Collectors.toList()); if (!myAutowiredConstructors.isEmpty()) { return myAutowiredConstructors.toArray(new Constructor[0]); } // 其次,检查默认构造函数 try { Constructor defaultConstructor = beanClass.getDeclaredConstructor(); return new Constructor[]{defaultConstructor}; } catch (NoSuchMethodException e) { // 找不到默认构造函数,请继续选择合适的构造函数 } // 返回所有构造函数,让Spring将选择最具体的构造函数 return beanClass.getConstructors(); } } ``` 自定义注解 ```java @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.CONSTRUCTOR) public @interface MyAutowired { } ``` 两个普通Bean对象 ```java @Component public class MyServiceA { public void execute() { System.out.println("MyServiceA executed"); } } @Component public class MyServiceB { public void execute() { System.out.println("MyServiceB executed"); } } ``` 我们定义了四个构造函数(默认构造函数,只接受 `MyServiceA` 的构造函数,只接受 `MyServiceB` 的构造函数,同时接受 `MyServiceA` 和 `MyServiceB` 的构造函数)。根据`MySmartInstantiationAwareBeanPostProcessor`定义的分析下面选择构造函数过程,当 Spring 容器尝试实例化 `MyService` 类的一个实例时,由于存在一个被 `@MyAutowired` 标记的构造函数,所以它将首先尝试使用它。这意味着如果在 Spring 容器中已经有了 `MyServiceA` 和 `MyServiceB` 的 bean,那么这两者都会被注入,并且会输出 `"Constructor with ServiceA and ServiceB used"`。如果没有提供 `@MyAutowired` 或者没有适当的 bean 来满足带有 `@MyAutowired` 注解的构造函数的依赖关系,则会尝试使用默认构造函数。如果没有默认构造函数,Spring 将尝试其他构造函数并查找可以匹配的 bean。在这种情况下,如果 `MyServiceA` 和 `MyServiceB` 都可用,Spring 将选择接受最多参数的构造函数,因为这被视为最具体的构造函数。 ```java @Component public class MyService { private final MyServiceA myServiceA; private final MyServiceB myServiceB; public MyService() { System.out.println("Default constructor used"); this.myServiceA = null; this.myServiceB = null; } public MyService(MyServiceA myServiceA) { System.out.println("Constructor with ServiceA used"); this.myServiceA = myServiceA; this.myServiceB = null; } public MyService(MyServiceB serviceB) { System.out.println("Constructor with ServiceB used"); this.myServiceA = null; this.myServiceB = serviceB; } @MyAutowired public MyService(MyServiceA serviceA, MyServiceB serviceB) { System.out.println("Constructor with ServiceA and ServiceB used"); this.myServiceA = serviceA; this.myServiceB = serviceB; } } ``` 运行结果发现,Spring 容器成功地使用带有 `@MyAutowired` 注解的构造函数实例化了 `MyService` 类,并正确地注入了它的两个依赖:`MyServiceA` 和 `MyServiceB`。 ```java Constructor with ServiceA and ServiceB used ``` 注意:由于predictBeanType,getEarlyBeanReference方法是Spring框架内部使用无法演示出效果,因此不演示这两个方法。 ### 六、时序图 ~~~mermaid sequenceDiagram Title: SmartInstantiationAwareBeanPostProcessor时序图 participant SmartInstantiationAwareBeanPostProcessorApplication participant AnnotationConfigApplicationContext participant AbstractApplicationContext participant DefaultListableBeanFactory participant AbstractBeanFactory participant DefaultSingletonBeanRegistry participant AbstractAutowireCapableBeanFactory participant MySmartInstantiationAwareBeanPostProcessor SmartInstantiationAwareBeanPostProcessorApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)
创建上下文 AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()
刷新上下文 AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory)
初始化Bean工厂 AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons()
实例化单例 DefaultListableBeanFactory->>AbstractBeanFactory:getBean(name)
获取Bean AbstractBeanFactory->>AbstractBeanFactory:doGetBean(name,requiredType,args,typeCheckOnly)
执行获取Bean AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton(beanName,singletonFactory)
获取单例Bean DefaultSingletonBeanRegistry-->>AbstractBeanFactory:getObject()
获取Bean实例 AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean(beanName,mbd,args)
创建Bean AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:doCreateBean(beanName,mbd,args)
执行Bean创建 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:createBeanInstance(beanName,mbd,args)
创建bean实例 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:determineConstructorsFromBeanPostProcessors(beanClass, beanName)
确定构造方法 AbstractAutowireCapableBeanFactory->>MySmartInstantiationAwareBeanPostProcessor:determineCandidateConstructors(beanClass, beanName)
回调候选构造方法 MySmartInstantiationAwareBeanPostProcessor-->>AbstractAutowireCapableBeanFactory:返回构造方法 AbstractAutowireCapableBeanFactory-->>AbstractBeanFactory:返回Bean对象 AbstractBeanFactory-->>DefaultListableBeanFactory:返回Bean对象 AnnotationConfigApplicationContext-->>SmartInstantiationAwareBeanPostProcessorApplication:初始化完成 ~~~ ### 七、源码分析 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。 PS:由于`predictBeanType`, `determineCandidateConstructors`, 和 `getEarlyBeanReference` 这三个方法虽然都属于 `SmartInstantiationAwareBeanPostProcessor` 接口,但它们处理不同的关注点,具有不同的目的。在进行源码分析时,此处只演示determineCandidateConstructors方法。 ```java public class SmartInstantiationAwareBeanPostProcessorApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); } } ``` 在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法 ```java public AnnotationConfigApplicationContext(Class... componentClasses) { this(); register(componentClasses); refresh(); } ``` 在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中,我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 ```java @Override public void refresh() throws BeansException, IllegalStateException { // ... [代码部分省略以简化] // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization`方法中,会继续调用`DefaultListableBeanFactory`类中的`preInstantiateSingletons`方法来完成所有剩余非懒加载的单列Bean对象。 ```java /** * 完成此工厂的bean初始化,实例化所有剩余的非延迟初始化单例bean。 * * @param beanFactory 要初始化的bean工厂 */ protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { // ... [代码部分省略以简化] // 完成所有剩余非懒加载的单列Bean对象。 beanFactory.preInstantiateSingletons(); } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons`方法中,主要的核心目的是预先实例化所有非懒加载的单例bean。在Spring的上下文初始化完成后,该方法会被触发,以确保所有单例bean都被正确地创建并初始化。其中`getBean(beanName)`是此方法的核心操作。对于容器中定义的每一个单例bean,它都会调用`getBean`方法,这将触发bean的实例化、初始化及其依赖的注入。如果bean之前没有被创建过,那么这个调用会导致其被实例化和初始化。 ```java public void preInstantiateSingletons() throws BeansException { // ... [代码部分省略以简化] // 循环遍历所有bean的名称 for (String beanName : beanNames) { getBean(beanName); } // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#getBean()`方法中,又调用了`doGetBean`方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,`doGetBean`负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。 ```java @Override public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中,首先检查所请求的bean是否是一个单例并且已经创建。如果尚未创建,它将创建一个新的实例。在这个过程中,它处理可能的异常情况,如循环引用,并确保返回的bean是正确的类型。这是Spring容器bean生命周期管理的核心部分。 ```java protected T doGetBean( String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException { // ... [代码部分省略以简化] // 开始创建bean实例 if (mbd.isSingleton()) { // 如果bean是单例的,我们会尝试从单例缓存中获取它 // 如果不存在,则使用lambda创建一个新的实例 sharedInstance = getSingleton(beanName, () -> { try { // 尝试创建bean实例 return createBean(beanName, mbd, args); } catch (BeansException ex) { // ... [代码部分省略以简化] } }); // 对于某些bean(例如FactoryBeans),可能需要进一步处理以获取真正的bean实例 beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } // ... [代码部分省略以简化] // 确保返回的bean实例与请求的类型匹配 return adaptBeanInstance(name, beanInstance, requiredType); } ``` 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()`方法中,主要负责从单例缓存中获取一个已存在的bean实例,或者使用提供的`ObjectFactory`创建一个新的实例。这是确保bean在Spring容器中作为单例存在的关键部分。 ```java public Object getSingleton(String beanName, ObjectFactory singletonFactory) { // 断言bean名称不能为空 Assert.notNull(beanName, "Bean name must not be null"); // 同步访问单例对象缓存,确保线程安全 synchronized (this.singletonObjects) { // 从缓存中获取单例对象 Object singletonObject = this.singletonObjects.get(beanName); // 如果缓存中没有找到 if (singletonObject == null) { // ... [代码部分省略以简化] try { // 使用工厂创建新的单例实例 singletonObject = singletonFactory.getObject(); newSingleton = true; } catch (IllegalStateException ex) { // ... [代码部分省略以简化] } catch (BeanCreationException ex) { // ... [代码部分省略以简化] } finally { // ... [代码部分省略以简化] } // ... [代码部分省略以简化] } // 返回单例对象 return singletonObject; } } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()`方法中,主要的逻辑是调用 `doCreateBean`,这是真正进行 bean 实例化、属性填充和初始化的地方。这个方法会返回新创建的 bean 实例。 ```java @Override protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // ... [代码部分省略以简化] try { // 正常的bean实例化、属性注入和初始化。 // 这里是真正进行bean创建的部分。 Object beanInstance = doCreateBean(beanName, mbdToUse, args); // 记录bean成功创建的日志 if (logger.isTraceEnabled()) { logger.trace("Finished creating instance of bean '" + beanName + "'"); } return beanInstance; } catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { // ... [代码部分省略以简化] } catch (Throwable ex) { // ... [代码部分省略以简化] } } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中,如果是单例,尝试从工厂实例缓存中获取。如果缓存中没有实例,创建一个新的实例。 ```java protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // 实例化 bean。 BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { // 如果是单例,尝试从工厂实例缓存中获取。 instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { // 如果缓存中没有实例,创建一个新的实例。 instanceWrapper = createBeanInstance(beanName, mbd, args); } // ... [省略部分代码以简化] // 返回创建和初始化后的 bean。 return exposedObject; } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance`方法中,首先尝试从 `SmartInstantiationAwareBeanPostProcessor` 中确定用于 bean 的构造函数,接下来,它检查是否已经确定了某些构造函数、是否 bean 定义指明了使用构造函数自动装配、是否为 bean 提供了构造函数参数值或是否有明确的构造函数参数。如果满足这些条件之一,它将调用 `autowireConstructor` 方法,这个方法会使用确定的构造函数(或者选择一个)来实例化 bean。接着,如果之前没有选择构造函数,它会检查是否存在首选的默认构造函数。这些构造函数可以是由用户明确指定的或是由其他部分的框架预先确定的。如果有这样的构造函数,框架又会尝试使用 `autowireConstructor` 方法,最后,如果所有先前的步骤都没有返回构造函数,spring会默认为 bean 使用无参数构造函数。 ```java protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) { // ... [省略部分代码以简化] // 是否有用于自动装配的候选构造函数? Constructor[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName); // 如果从后处理器中确定了构造函数、或者 bean 定义信息指明了使用构造函数自动装配、或者存在构造函数参数值、或者提供了特定的构造函数参数 if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR || mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) { // 使用确定的构造函数进行自动装配 return autowireConstructor(beanName, mbd, ctors, args); } // 是否有优先使用的默认构造函数? ctors = mbd.getPreferredConstructors(); if (ctors != null) { // 使用优选的构造函数进行自动装配 return autowireConstructor(beanName, mbd, ctors, null); } // 没有特殊处理:直接使用无参数构造函数进行实例化 return instantiateBean(beanName, mbd); } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#determineConstructorsFromBeanPostProcessors`方法中,回调每一个 `SmartInstantiationAwareBeanPostProcessor`,调用它的 `determineCandidateConstructors` 方法以确定 bean 的构造函数。如果找到了候选的构造函数,就返回这些构造函数。如果多个 `SmartInstantiationAwareBeanPostProcessor` 都返回了构造函数,则只会使用第一个返回的构造函数。 ```java protected Constructor[] determineConstructorsFromBeanPostProcessors(@Nullable Class beanClass, String beanName) throws BeansException { if (beanClass != null && hasInstantiationAwareBeanPostProcessors()) { for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) { Constructor[] ctors = bp.determineCandidateConstructors(beanClass, beanName); if (ctors != null) { return ctors; } } } return null; } ``` 最后执行到我们自定义的逻辑中,在我们自定义的逻辑中,如果类中有一个或多个带有 `@MyAutowired` 注解的构造函数,这些构造函数将被作为候选返回,如果没有找到任何带有 `@MyAutowired` 注解的构造函数,那么后处理器会尝试查找默认(无参数)的构造函数,如果没有找到带有 `@MyAutowired` 注解的构造函数,并且没有默认构造函数,那么所有可用的构造函数将被作为候选返回,从而使 Spring 能够选择最具体的构造函数。 ```java public class MySmartInstantiationAwareBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor { @Override public Constructor[] determineCandidateConstructors(Class beanClass, String beanName) throws BeansException { // 首先,查找@MyAutowired带注释的构造函数 List> myAutowiredConstructors = Arrays.stream(beanClass.getConstructors()) .filter(constructor -> constructor.isAnnotationPresent(MyAutowired.class)) .collect(Collectors.toList()); if (!myAutowiredConstructors.isEmpty()) { return myAutowiredConstructors.toArray(new Constructor[0]); } // 其次,检查默认构造函数 try { Constructor defaultConstructor = beanClass.getDeclaredConstructor(); return new Constructor[]{defaultConstructor}; } catch (NoSuchMethodException e) { // 找不到默认构造函数,请继续选择合适的构造函数 } // 返回所有构造函数,让Spring将选择最具体的构造函数 return beanClass.getConstructors(); } } ``` ### 八、注意事项 1. **性能影响** + 每个 `SmartInstantiationAwareBeanPostProcessor`,都会对每个 bean 的创建过程产生额外的开销。因此,应避免创建不必要的 `SmartInstantiationAwareBeanPostProcessor`,并确保其实现尽可能高效。 2. **与其他后处理器的交互** + 如果有多个 `SmartInstantiationAwareBeanPostProcessor` ,它们会按照注册的顺序被调用。应确保这些后处理器的执行顺序是正确的,避免意外的覆盖或冲突,因为只会使用第一个返回的构造函数。 3. **返回非空值的考虑** + `determineCandidateConstructors`:当这个方法返回非空值时,Spring 容器将不会再尝试使用其他方式自动选择构造函数,`predictBeanType`:返回的类型应该尽可能准确地反映后处理器预期的最终 bean 类型,以确保类型匹配和自动装配的正确性。 4. **与 `InstantiationAwareBeanPostProcessor` 的区别** + 虽然 `SmartInstantiationAwareBeanPostProcessor` 扩展了 `InstantiationAwareBeanPostProcessor`,但它添加了更多的回调和复杂性。除非我们确实需要这些额外的功能,否则最好仅使用 `InstantiationAwareBeanPostProcessor`。 ### 九、总结 #### 最佳实践总结 1. **初始化 Spring 容器** + 通过 `AnnotationConfigApplicationContext` 初始化 Spring 容器,并使用 `MyConfiguration` 作为配置类。 2. **注册后处理器** + 在 `MyConfiguration` 中,我们注册了自定义的 `SmartInstantiationAwareBeanPostProcessor` 实现 `MySmartInstantiationAwareBeanPostProcessor`。这确保了它会在 Spring 容器中被考虑并执行。 3. **自定义后处理器的工作** + 我们的 `MySmartInstantiationAwareBeanPostProcessor` 重写了 `determineCandidateConstructors` 方法,该方法的目标是返回一组构造函数,供 Spring 选择用于 bean 的实例化。 4. **查找 `@MyAutowired`** + 首先,后处理器会查找带有 `@MyAutowired` 注解的构造函数。 5. **使用默认构造函数** + 如果没有带有 `@MyAutowired` 的构造函数,后处理器会查找默认构造函数。 6. **返回所有构造函数** + 如果没有找到上述两种情况的构造函数,所有的构造函数将被作为候选返回。 7. **Bean 的实例化** + 当 Spring 尝试实例化 `MyService` bean 时,它会使用 `MySmartInstantiationAwareBeanPostProcessor` 中指定的构造函数。在这个示例中,由于我们有一个带有 `@MyAutowired` 注解的构造函数,且两个依赖 `MyServiceA` 和 `MyServiceB` 都可用,这个构造函数被选择并使用,从而输出了 `"Constructor with ServiceA and ServiceB used"`。 8. **关于其他方法** + 虽然 `SmartInstantiationAwareBeanPostProcessor` 提供了其他方法,如 `predictBeanType` 和 `getEarlyBeanReference`,但这些主要是为 Spring 内部使用。在大多数常规用例中,我们可能不需要重写或使用它们。 #### 源码分析总结 1. **启动和初始化:** - 使用`AnnotationConfigApplicationContext`初始化Spring上下文。 - 构造参数中给定一个配置类,该配置类中定义了自定义的`SmartInstantiationAwareBeanPostProcessor`。 2. **Bean预实例化过程:** - 在上下文刷新过程中,`finishBeanFactoryInitialization`方法会预实例化所有非懒加载的单例Bean。 - `preInstantiateSingletons`方法循环遍历所有bean的名称并通过`getBean`方法实例化bean。 - 如果Bean已经存在并且是单例,则会从单例缓存中返回。否则,会创建一个新的bean实例。 3. **Bean创建过程:** - 创建Bean的核心逻辑在`doCreateBean`方法中。如果bean是单例并且在缓存中不存在,则会创建一个新的bean实例。 - 在创建bean实例时,首先从`SmartInstantiationAwareBeanPostProcessor`中确定用于bean的构造函数。 - 这个过程首先尝试使用带有特定注解(如我们的示例中的`@MyAutowired`)的构造函数。 - 如果没有这样的构造函数,则会选择默认构造函数。 - 如果没有带有注解的构造函数且没有默认构造函数,则会返回所有可用的构造函数,从而使Spring选择最具体的构造函数。 4. **自定义的逻辑:** - 自定义的`SmartInstantiationAwareBeanPostProcessor`实现首先检查是否有带有`@MyAutowired`注解的构造函数。 - 如果有,则这些构造函数会作为候选返回。 - 如果没有,则后处理器会检查是否存在默认的无参数构造函数。 - 如果既没有带有`@MyAutowired`注解的构造函数,也没有默认构造函数,则所有构造函数将被作为候选返回。 ================================================ FILE: spring-interface/spring-interface-smartInstantiationAwareBeanPostProcessor/pom.xml ================================================ spring-interface com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-interface-smartInstantiationAwareBeanPostProcessor ================================================ FILE: spring-interface/spring-interface-smartInstantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/SmartInstantiationAwareBeanPostProcessorApplication.java ================================================ package com.xcs.spring; import com.xcs.spring.config.MyConfiguration; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年09月16日 16时09分 **/ public class SmartInstantiationAwareBeanPostProcessorApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); } } ================================================ FILE: spring-interface/spring-interface-smartInstantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/annotation/MyAutowired.java ================================================ package com.xcs.spring.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @author xcs * @date 2023年09月21日 10时32分 **/ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.CONSTRUCTOR) public @interface MyAutowired { } ================================================ FILE: spring-interface/spring-interface-smartInstantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/config/MyConfiguration.java ================================================ package com.xcs.spring.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; /** * @author xcs * @date 2023年09月19日 16时35分 **/ @Configuration @ComponentScan("com.xcs.spring") public class MyConfiguration { @Bean public static MySmartInstantiationAwareBeanPostProcessor mySmartInstantiationAwareBeanPostProcessor(){ return new MySmartInstantiationAwareBeanPostProcessor(); } } ================================================ FILE: spring-interface/spring-interface-smartInstantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/config/MySmartInstantiationAwareBeanPostProcessor.java ================================================ package com.xcs.spring.config; import com.xcs.spring.annotation.MyAutowired; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor; import java.lang.reflect.Constructor; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; /** * @author xcs * @date 2023年09月19日 16时42分 **/ public class MySmartInstantiationAwareBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor { @Override public Constructor[] determineCandidateConstructors(Class beanClass, String beanName) throws BeansException { // 首先,查找@MyAutowired带注释的构造函数 List> myAutowiredConstructors = Arrays.stream(beanClass.getConstructors()) .filter(constructor -> constructor.isAnnotationPresent(MyAutowired.class)) .collect(Collectors.toList()); if (!myAutowiredConstructors.isEmpty()) { return myAutowiredConstructors.toArray(new Constructor[0]); } // 其次,检查默认构造函数 try { Constructor defaultConstructor = beanClass.getDeclaredConstructor(); return new Constructor[]{defaultConstructor}; } catch (NoSuchMethodException e) { // 找不到默认构造函数,请继续选择合适的构造函数 } // 返回所有构造函数,让Spring将选择最具体的构造函数 return beanClass.getConstructors(); } } ================================================ FILE: spring-interface/spring-interface-smartInstantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/service/MyService.java ================================================ package com.xcs.spring.service; import com.xcs.spring.annotation.MyAutowired; import org.springframework.stereotype.Component; /** * @author xcs * @date 2023年09月21日 10时31分 **/ @Component public class MyService { private final MyServiceA myServiceA; private final MyServiceB myServiceB; public MyService() { System.out.println("Default constructor used"); this.myServiceA = null; this.myServiceB = null; } public MyService(MyServiceA myServiceA) { System.out.println("Constructor with ServiceA used"); this.myServiceA = myServiceA; this.myServiceB = null; } public MyService(MyServiceB serviceB) { System.out.println("Constructor with ServiceB used"); this.myServiceA = null; this.myServiceB = serviceB; } @MyAutowired public MyService(MyServiceA serviceA, MyServiceB serviceB) { System.out.println("Constructor with ServiceA and ServiceB used"); this.myServiceA = serviceA; this.myServiceB = serviceB; } } ================================================ FILE: spring-interface/spring-interface-smartInstantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/service/MyServiceA.java ================================================ package com.xcs.spring.service; import org.springframework.stereotype.Component; /** * @author xcs * @date 2023年09月21日 10时30分 **/ @Component public class MyServiceA { public void execute() { System.out.println("MyServiceA executed"); } } ================================================ FILE: spring-interface/spring-interface-smartInstantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/service/MyServiceB.java ================================================ package com.xcs.spring.service; import org.springframework.stereotype.Component; /** * @author xcs * @date 2023年09月21日 10时30分 **/ @Component public class MyServiceB { public void execute() { System.out.println("MyServiceB executed"); } } ================================================ FILE: spring-jsr/pom.xml ================================================ spring-reading com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-jsr pom spring-jsr330-inject spring-jsr330-qualifier spring-jsr330-named spring-jsr330-singleton spring-jsr330-scope spring-jsr330-provider spring-jsr250-resource spring-jsr250-postConstruct spring-jsr250-preDestroy javax.inject javax.inject 1 jakarta.annotation jakarta.annotation-api 1.3.5 ================================================ FILE: spring-jsr/spring-jsr250-postConstruct/README.md ================================================ ## @PostConstruct - [@PostConstruct](#postconstruct) - [一、基本信息](#一基本信息) - [二、接口描述](#二接口描述) - [三、接口源码](#三接口源码) - [四、主要功能](#四主要功能) - [五、最佳实践](#五最佳实践) - [六、时序图](#六时序图) - [七、源码分析](#七源码分析) - [前置条件](#前置条件) - [收集阶段](#收集阶段) - [执行阶段](#执行阶段) - [八、注意事项](#八注意事项) - [九、总结](#九总结) - [最佳实践总结](#最佳实践总结) - [源码分析总结](#源码分析总结) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [我的CSDN](https://blog.csdn.net/duzhuang2399/article/details/133904802) 📚 **文章目录** - [所有文章](https://github.com/xuchengsheng/spring-reading) 🔗 **源码地址** - [@PostConstruct源码](https://github.com/xuchengsheng/spring-reading/blob/master/spring-jsr/spring-jsr250-postConstruct) ### 二、接口描述 `@PostConstruct` 注解来源于 JSR-250(Java Specification Request 250),这是一个定义了一些常用的Java EE注解的规范。这些注解的目的是提供一个与平台无关的方式,允许我们定义一些生命周期事件,比如在bean初始化之后要执行的方法。 ### 三、接口源码 `@PostConstruct` 是 Java EE 提供的一个标准注解,表示被标记的方法应该在对象实例化后立即执行。Spring 容器支持此注解,它确保在构造函数完成初始化工作之后、所有属性被设置之后、并且初始化回调(如 `InitializingBean.afterPropertiesSet()` 或自定义的 `init` 方法)被触发之前,这个特定的方法被调用。 ```java @Documented @Retention (RUNTIME) @Target(METHOD) public @interface PostConstruct { } ``` ### 四、主要功能 1. **初始化逻辑** + 允许在对象创建并完成依赖注入后执行特定的初始化逻辑。 2. **资源配置** + 对于需要访问数据库、文件或其他外部资源的组件,可以使用 `@PostConstruct` 来确保在使用资源之前它们已经正确配置。 3. **数据预加载** + 可以在应用启动时加载一些必要的数据或缓存。 4. **验证** + 确保组件的某些属性或配置在对象使用之前具有有效的状态或值。 5. **与平台无关** + `@PostConstruct` 是一个标准的 Java EE 注解,这意味着它在不同的容器和框架中都有一致的行为。 6. **执行顺序** + 在 Spring 中,`@PostConstruct` 被调用的时间是在构造函数之后、所有属性设置之后,并在任何初始化回调(如 `InitializingBean.afterPropertiesSet()` 或指定的 init 方法)之前。 ### 五、最佳实践 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。 ```java public class PostConstructApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); } } ``` 在`MyConfiguration`类中,使用了`@ComponentScan("com.xcs.spring")`注解告诉 Spring 在指定的包(在这里是 "`com.xcs.spring`")及其子包中搜索带有 `@Component`、`@Service`、`@Repository` 和 `@Controller` 等注解的类,并将它们自动注册为 beans。这样,spring就不必为每个组件明确写一个 bean 定义。Spring 会自动识别并注册它们。 ```java @Configuration @ComponentScan("com.xcs.spring") public class MyConfiguration { } ``` `MyService` 的 Spring Service 类。这个类有一个无参构造函数和一个使用 `@PostConstruct` 注解的方法。 ```java @Service public class MyService { public MyService(){ System.out.println("执行MyService构造函数"); } @PostConstruct public void postConstruct(){ System.out.println("执行@PostConstruct方法"); } } ``` 运行结果发现,当 Spring 容器初始化 `MyService` Bean 时,我们会首先看到构造函数的输出,紧接着看到 `@PostConstruct` 方法的输出。 ``` 执行MyService构造函数 执行@PostConstruct方法 ``` ### 六、时序图 ~~~mermaid sequenceDiagram Title: @PostConstruct注解时序图 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:applyMergedBeanDefinitionPostProcessors(mbd,beanType,beanName)
开始应用 BeanDefinition 的后置处理器。 AbstractAutowireCapableBeanFactory->>CommonAnnotationBeanPostProcessor:postProcessMergedBeanDefinition(beanDefinition,beanType,beanName)
处理 Bean 的通用注解。 CommonAnnotationBeanPostProcessor->>InitDestroyAnnotationBeanPostProcessor:super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName)
为生命周期注解(如 @PostConstruct)处理已合并的 Bean 定义。 InitDestroyAnnotationBeanPostProcessor->>InitDestroyAnnotationBeanPostProcessor:findLifecycleMetadata(clazz)
查找类的生命周期元数据。 InitDestroyAnnotationBeanPostProcessor->>InitDestroyAnnotationBeanPostProcessor:buildLifecycleMetadata(clazz)
构建类的生命周期元数据。 InitDestroyAnnotationBeanPostProcessor->>ReflectionUtils:doWithLocalMethods(clazz,fc)
处理类的所有本地方法。 ReflectionUtils->>InitDestroyAnnotationBeanPostProcessor:解析有@PostConstruct注解的方法
解析那些有 @PostConstruct 注解的方法。 InitDestroyAnnotationBeanPostProcessor->>LifecycleElement:new LifecycleElement(member,ae,pd)
创建新的生命周期元素,代表 @PostConstruct 方法。 InitDestroyAnnotationBeanPostProcessor->>LifecycleMetadata:new LifecycleMetadata(clazz, initMethods, destroyMethods)
创建存储生命周期方法(初始化和销毁)的元数据。 InitDestroyAnnotationBeanPostProcessor->>InitDestroyAnnotationBeanPostProcessor:this.lifecycleMetadataCache.put(clazz, metadata)
将构建的生命周期元数据缓存起来,方便后续访问。 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:initializeBean(beanName, exposedObject, mbd)
开始初始化 Bean。 AbstractAutowireCapableBeanFactory->>InitDestroyAnnotationBeanPostProcessor:postProcessBeforeInitialization(result, beanName)
在 Bean 初始化前执行后置处理。 InitDestroyAnnotationBeanPostProcessor->>InitDestroyAnnotationBeanPostProcessor:findResourceMetadata(beanName,clazz,pvs)
查找需要注入的资源的元数据。 Note right of InitDestroyAnnotationBeanPostProcessor:
从缓存中快速获取先前解析的生命周期元数据。 InitDestroyAnnotationBeanPostProcessor->>LifecycleMetadata:invokeInitMethods(bean, beanName)
调用所有标记为 @PostConstruct 的初始化方法。 LifecycleMetadata->>LifecycleElement:invoke(target)
执行具体的 @PostConstruct 方法。 LifecycleElement->>Method:this.method.invoke(target, (Object[]) null)
使用反射调用目标 Bean 的 @PostConstruct 方法。 ~~~ ### 七、源码分析 #### 前置条件 在Spring中,`InitDestroyAnnotationBeanPostProcessor`是处理`@PostConstruct`等注解的关键类,它实现了下述两个接口。因此,为了深入理解`@PostConstruct`的工作方式,研究这个类是非常有用的。简而言之,为了完全理解`@PostConstruct`的工作机制,了解下述接口确实是必要的。这两个接口提供了对bean生命周期中关键阶段的干预,从而允许进行方法执行和其他相关的操作。 1. `MergedBeanDefinitionPostProcessor`接口 - 此接口提供的`postProcessMergedBeanDefinition`方法允许后处理器修改合并后的bean定义。合并后的bean定义是一个已经考虑了所有父bean定义属性的bean定义。对于`@PostConstruct`注解的处理,这一步通常涉及到收集需要被解析的`@PostConstruct`注解信息并准备对其进行后续处理。 - 🔗 [MergedBeanDefinitionPostProcessor接口传送门](https://github.com/xuchengsheng/spring-reading/tree/master/spring-interface/spring-interface-mergedBeanDefinitionPostProcessor) 2. `BeanPostProcessor`接口 - 此接口提供了修改新实例化的 Bean 的机会,它允许在 Spring 容器初始化 Bean 的任何属性之前和之后执行自定义的修改。 - 对于 `@PostConstruct`,当容器调用 `postProcessBeforeInitialization` 方法时,`CommonAnnotationBeanPostProcessor`会检查 Bean 是否有标注 `@PostConstruct` 的方法,如果有,这些方法会在这个阶段被调用。 - 🔗 [BeanPostProcessor接口传送门](https://github.com/xuchengsheng/spring-reading/tree/master/spring-interface/spring-interface-beanPostProcessor) #### 收集阶段 在`org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#postProcessMergedBeanDefinition`方法中,首先调用了 `super.postProcessMergedBeanDefinition`,即调用了父类或接口默认的实现。 ```java @Override public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class beanType, String beanName) { super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName); // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor#postProcessMergedBeanDefinition`方法中,主要是处理与 `@PostConstruct` 注解相关的资源注入元数据,并在bean定义合并后对这些元数据进行进一步的处理或验证。这是Spring在处理JSR-250 `@PostConstruct` 注解时的处理入口。 ```java @Override public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class beanType, String beanName) { // 根据给定的bean类型查找与其相关的生命周期元数据。 LifecycleMetadata metadata = findLifecycleMetadata(beanType); // 使用找到的生命周期元数据来检查并可能修改给定的bean定义。 metadata.checkConfigMembers(beanDefinition); } ``` 在`org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor#findLifecycleMetadata`方法中,首先尝试从缓存中获取 `LifecycleMetadata`,如果它不存在或需要刷新,则会创建新的 `LifecycleMetadata` 并将其存入缓存。这种缓存策略可以提高效率,避免对同一类型的类反复构建注入元数据。 ```java private LifecycleMetadata findLifecycleMetadata(Class clazz) { // 检查生命周期元数据缓存是否为空,这可能发生在反序列化或销毁阶段。 if (this.lifecycleMetadataCache == null) { return buildLifecycleMetadata(clazz); } // 首先,在并发映射中进行快速检查,以最小的锁定。 LifecycleMetadata metadata = this.lifecycleMetadataCache.get(clazz); // 如果缓存中没有找到元数据,则构建元数据并放入缓存。 if (metadata == null) { synchronized (this.lifecycleMetadataCache) { metadata = this.lifecycleMetadataCache.get(clazz); // 双重检查锁定模式,确保只有一个线程构建和缓存元数据。 if (metadata == null) { // 根据给定的类构建生命周期元数据。 metadata = buildLifecycleMetadata(clazz); // 将新构建的元数据缓存,以便后续请求可以快速从缓存中检索。 this.lifecycleMetadataCache.put(clazz, metadata); } return metadata; } } return metadata; } ``` 在`org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor#buildLifecycleMetadata`方法中,这个方法首先查看类是否有`@PostConstruct`注解,然后遍历类的方法以找到标记为生命周期方法的那些带有 `@PostConstruct` 注解的方法。找到的方法被存储在列表中,并在结束时根据这些方法构建一个 `LifecycleMetadata` 对象。 ```java private LifecycleMetadata buildLifecycleMetadata(final Class clazz) { // 判断给定的类是否有@PostConstruct`注解。 if (!AnnotationUtils.isCandidateClass(clazz, Arrays.asList(this.initAnnotationType, this.destroyAnnotationType))) { return this.emptyLifecycleMetadata; } // 存储识别出的初始化和销毁方法的列表。 List initMethods = new ArrayList<>(); // ... [代码部分省略以简化] Class targetClass = clazz; // 遍历目标类及其所有父类,直到达到Object类。 do { final List currInitMethods = new ArrayList<>(); final List currDestroyMethods = new ArrayList<>(); // 使用反射处理类的所有本地方法。 ReflectionUtils.doWithLocalMethods(targetClass, method -> { // 查找标有初始化注解的方法。 if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) { LifecycleElement element = new LifecycleElement(method); currInitMethods.add(element); if (logger.isTraceEnabled()) { logger.trace("Found init method on class [" + clazz.getName() + "]: " + method); } } // ... [代码部分省略以简化] }); // 将当前类的生命周期方法添加到总列表中。 initMethods.addAll(0, currInitMethods); // ... [代码部分省略以简化] targetClass = targetClass.getSuperclass(); } while (targetClass != null && targetClass != Object.class); // 如果没有找到任何生命周期方法,则返回一个空的生命周期元数据对象;否则,返回新构建的元数据对象。 return (initMethods.isEmpty() && destroyMethods.isEmpty() ? this.emptyLifecycleMetadata : new LifecycleMetadata(clazz, initMethods, destroyMethods)); } ``` 在`org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.LifecycleElement#LifecycleElement`方法中,它用于封装与生命周期相关的方法(如 `@PostConstruct` 注解的方法)。 ```java public LifecycleElement(Method method) { // 检查提供的方法是否是无参数的。生命周期方法(如@PostConstruct)需要是无参数方法。 if (method.getParameterCount() != 0) { throw new IllegalStateException("Lifecycle method annotation requires a no-arg method: " + method); } // 存储提供的方法。 this.method = method; // 根据方法的修饰符(如private)确定唯一标识符。如果方法是私有的,我们使用完全限定名,否则只使用方法名。 this.identifier = (Modifier.isPrivate(method.getModifiers()) ? ClassUtils.getQualifiedMethodName(method) : method.getName()); } ``` #### 执行阶段 在`org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor#postProcessBeforeInitialization`方法中,实现了 `postProcessBeforeInitialization` 方法,它是 Spring 的 `BeanPostProcessor` 接口的一部分,用于初始化 Bean 的任何属性之前和之后执行自定义的修改。这个特定的实现与处理 `@PostConstruct`注解相关。 ```java @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { // 查找bean类的生命周期元数据。 LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass()); try { // 调用bean的初始化方法(如@PostConstruct注解的方法)。 metadata.invokeInitMethods(bean, beanName); } catch (InvocationTargetException ex) { // 如果初始化方法调用失败(如因为抛出的异常),则抛出Bean创建异常。 throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException()); } catch (Throwable ex) { // 对于其他错误,也抛出Bean创建异常。 throw new BeanCreationException(beanName, "Failed to invoke init method", ex); } // 返回原始bean实例。 return bean; } ``` 在`org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor#findLifecycleMetadata`方法中,首先`InitDestroyAnnotationBeanPostProcessor#postProcessMergedBeanDefinition` 元数据收集阶段,`findLifecycleMetadata` 被调用以处理和缓存与 `@PostConstruct`和其他相关注解的 `LifecycleMetadata`。这意味着,在`postProcessBeforeInitialization`阶段之后的其他生命周期方法中,当再次调用 `findLifecycleMetadata` 时,会直接从缓存中获取已处理的 `LifecycleMetadata`,而不需要重新构建它。 ```java private LifecycleMetadata findLifecycleMetadata(Class clazz) { // 检查生命周期元数据缓存是否为空,这可能发生在反序列化或销毁阶段。 if (this.lifecycleMetadataCache == null) { return buildLifecycleMetadata(clazz); } // 首先,在并发映射中进行快速检查,以最小的锁定。 LifecycleMetadata metadata = this.lifecycleMetadataCache.get(clazz); // 如果缓存中没有找到元数据,则构建元数据并放入缓存。 if (metadata == null) { synchronized (this.lifecycleMetadataCache) { metadata = this.lifecycleMetadataCache.get(clazz); // 双重检查锁定模式,确保只有一个线程构建和缓存元数据。 if (metadata == null) { // 根据给定的类构建生命周期元数据。 metadata = buildLifecycleMetadata(clazz); // 将新构建的元数据缓存,以便后续请求可以快速从缓存中检索。 this.lifecycleMetadataCache.put(clazz, metadata); } return metadata; } } return metadata; } ``` 在`org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.LifecycleMetadata#invokeInitMethods`方法中,主要是调用Spring Bean的初始化方法。初始化方法是由 `@PostConstruct` 注解标记的。 ```java public void invokeInitMethods(Object target, String beanName) throws Throwable { // 获取已检查的初始化方法集合。 Collection checkedInitMethods = this.checkedInitMethods; // 如果已有检查的初始化方法,则使用它们;否则,使用所有初始化方法。 Collection initMethodsToIterate = (checkedInitMethods != null ? checkedInitMethods : this.initMethods); // 如果存在初始化方法,则进行迭代调用。 if (!initMethodsToIterate.isEmpty()) { for (LifecycleElement element : initMethodsToIterate) { // 如果启用了跟踪日志,则记录每个初始化方法的调用信息。 if (logger.isTraceEnabled()) { logger.trace("Invoking init method on bean '" + beanName + "': " + element.getMethod()); } // 实际调用目标对象上的初始化方法。 element.invoke(target); } } } ``` 在`org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.LifecycleElement#invoke`方法中,使用反射调用目标对象上的特定方法。 ```java public void invoke(Object target) throws Throwable { // 确保封装的方法是可访问的,特别是如果它是私有的。 ReflectionUtils.makeAccessible(this.method); // 使用反射实际调用方法。由于该方法没有参数,所以传递一个null作为参数列表。 this.method.invoke(target, (Object[]) null); } ``` ### 八、注意事项 1. **无参数方法** + 使用 `@PostConstruct` 注解的方法必须不带任何参数。 2. **返回类型** + 这些方法应该没有返回值(即 `void`)。因为其他返回类型会被忽略。 3. **访问修饰符** + 虽然通常建议使用 `public` 或 `protected` 修饰符,但这并不是强制的。私有方法也可以使用此注解,Spring 会确保这些方法仍然被调用。 4. **异常处理** + 如果 `@PostConstruct` 注解的方法抛出任何未检查的异常,则组件的初始化将失败,Spring 容器可能拒绝加载该组件。 5. **多个 @PostConstruct 方法** + 虽然可能有多个方法都有 `@PostConstruct` 注解,但这并不是一个好的实践。正确的做法是只在一个方法上使用此注解,以避免混淆和不确定的初始化顺序。 6. **与其他生命周期方法的关系** + 如果我们同时使用 `@PostConstruct` 和 `InitializingBean` 接口(其有 `afterPropertiesSet` 方法),那么 `@PostConstruct` 注解的方法会在 `afterPropertiesSet` 之后执行。 7. **避免多次 `@PostConstruct`** + 避免在同一个bean中使用多次 `@PostConstruct`。如果确实有多个方法需要在bean初始化时执行,考虑将它们放在一个单独的 `@PostConstruct` 方法中,并按期望的顺序调用它们。 8. **跨框架支持** + 虽然 `@PostConstruct` 有其起源于 Java EE,但它在多个Java框架中都得到了支持,包括 Spring。然而,在不同的运行环境中,始终确保我们的运行时环境确实支持此注解。 9. **避免长时间运行的操作** + `@PostConstruct` 方法应该避免执行耗时很长的操作,因为它会阻塞bean的初始化过程。如果确实需要进行长时间运行的初始化,考虑使用其他机制,例如异步执行。 ### 九、总结 #### 最佳实践总结 1. **启动类入口** + `PostConstructApplication` 类的 `main` 方法中,使用 `AnnotationConfigApplicationContext` 初始化Spring容器,该方法使用Java注解来配置Spring。我们将 `MyConfiguration` 作为参数传递,意味着我们希望从这个类开始加载Spring的配置。 2. **配置类** + `MyConfiguration` 被标记为一个配置类(通过 `@Configuration` 注解)。其中的 `@ComponentScan` 注解告诉Spring应该在哪些包里搜索组件。在这个例子中,Spring将会扫描 "`com.xcs.spring`" 包以及其子包,寻找例如 `@Component`、`@Service`、`@Repository` 和 `@Controller` 的注解,以此自动地注册bean。 3. **服务类** + 当Spring扫描 "`com.xcs.spring`" 包时,它找到了 `MyService` 类,这个类被标记为一个Service(通过 `@Service` 注解)。因此,Spring会为这个类创建一个bean实例。 4. **生命周期**: - 当Spring创建 `MyService` 的实例时,它首先调用类的构造函数。这就是为什么我们首先看到 "执行MyService构造函数" 的输出。 - 在Bean的所有属性都已经被设置后,并且所有的Bean初始化回调(例如 `BeanPostProcessor` 的 `postProcessBeforeInitialization` 方法)都已经被调用后,`@PostConstruct` 注解的方法会被执行。在这个例子中,这个方法是 `postConstruct`。因此,接下来我们看到了 "执行@PostConstruct方法" 的输出。 #### 源码分析总结 1. **前置条件** - `@PostConstruct`注解的执行依赖于两个核心接口:`MergedBeanDefinitionPostProcessor`和`BeanPostProcessor`。 - 这两个接口允许Spring在bean生命周期的关键阶段进行干预,如属性注入后、初始化方法前、初始化方法后等。 2. **收集阶段** - 当Spring处理一个Bean的定义并且这个Bean可能有`@PostConstruct`注解时,`InitDestroyAnnotationBeanPostProcessor`的`postProcessMergedBeanDefinition`方法会被调用。 - 在此方法中,与bean相关的`LifecycleMetadata`(包括`@PostConstruct`方法信息)被收集并缓存起来,以便后续使用。 3. **执行阶段** - 在Spring bean的生命周期中,初始化之前的一个关键点是`postProcessBeforeInitialization`方法的执行。在这个阶段,如果Bean有一个或多个`@PostConstruct`注解的方法,那么这些方法将被执行。 - 执行是通过查找bean的`LifecycleMetadata`(在之前的收集阶段中已经构建),然后迭代这些元数据中的方法,并使用反射来调用它们。 4. **实际方法调用** - 当需要调用具体的`@PostConstruct`方法时,会使用`LifecycleElement`类的`invoke`方法,该方法再次使用反射来确保方法是可访问的,并实际调用它。 ================================================ FILE: spring-jsr/spring-jsr250-postConstruct/pom.xml ================================================ spring-jsr com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-jsr250-postConstruct 11 11 ================================================ FILE: spring-jsr/spring-jsr250-postConstruct/src/main/java/com/xcs/spring/PostConstructApplication.java ================================================ package com.xcs.spring; import com.xcs.spring.config.MyConfiguration; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年10月18日 14时17分 **/ public class PostConstructApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); } } ================================================ FILE: spring-jsr/spring-jsr250-postConstruct/src/main/java/com/xcs/spring/config/MyConfiguration.java ================================================ package com.xcs.spring.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; /** * @author xcs * @date 2023年09月19日 16时35分 **/ @Configuration @ComponentScan("com.xcs.spring") public class MyConfiguration { } ================================================ FILE: spring-jsr/spring-jsr250-postConstruct/src/main/java/com/xcs/spring/service/MyService.java ================================================ package com.xcs.spring.service; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; @Service public class MyService { public MyService(){ System.out.println("执行MyService构造函数"); } @PostConstruct public void postConstruct(){ System.out.println("执行@PostConstruct方法"); } } ================================================ FILE: spring-jsr/spring-jsr250-preDestroy/README.md ================================================ ## @PreDestroy - [@PreDestroy](#predestroy) - [一、基本信息](#一基本信息) - [二、接口描述](#二接口描述) - [三、接口源码](#三接口源码) - [四、主要功能](#四主要功能) - [五、最佳实践](#五最佳实践) - [六、时序图](#六时序图) - [七、源码分析](#七源码分析) - [前置条件](#前置条件) - [收集阶段](#收集阶段) - [销毁阶段](#销毁阶段) - [八、注意事项](#八注意事项) - [九、总结](#九总结) - [最佳实践总结](#最佳实践总结) - [源码分析总结](#源码分析总结) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [我的CSDN](https://blog.csdn.net/duzhuang2399/article/details/133911656) 📚 **文章目录** - [所有文章](https://github.com/xuchengsheng/spring-reading) 🔗 **源码地址** - [@PreDestroy源码](https://github.com/xuchengsheng/spring-reading/tree/master/spring-jsr/spring-jsr250-preDestroy) ### 二、接口描述 `@PreDestroy` 注解来源于 JSR-250(Java Specification Request 250),这是一个定义了一些常用的Java EE注解的规范。这些注解的目的是提供一个与平台无关的方式,允许我们定义一些生命周期事件,比如在bean销毁之后要执行的方法。 ### 三、接口源码 `@PreDestroy` 是 Java EE 提供的一个标准注解,表示被标记的方法应该在对象销毁后立即执行。 ```java @Documented @Retention (RUNTIME) @Target(METHOD) public @interface PreDestroy { } ``` ### 四、主要功能 1. **资源释放** + 例如,如果我们的 bean 打开了文件、数据库连接或网络连接,可以在 `@PreDestroy` 方法中关闭这些连接,确保资源被正确释放。 2. **清理工作** + 如果 bean 在其生命周期中创建了临时文件或临时数据结构,并且在 bean 销毁前需要删除或清除,可以在 `@PreDestroy` 方法中执行这些清理操作。 3. **日志和通知** + 在某些应用中,我们可能希望在 bean 的生命周期结束时记录日志或发送通知。可以使用 `@PreDestroy` 方法来实现这一点。 4. **状态存储** + 如果 bean 有状态,并且我们希望在其生命周期结束时保存这个状态,可以在 `@PreDestroy` 方法中做这个工作。 5. **与其他组件断开连接** + 如果 bean 在其生命周期中注册到了其他组件或服务,并且需要在销毁前从这些组件或服务中注销,可以在 `@PreDestroy` 方法中执行此操作。 6. **无需 XML 配置** + 与传统的 `destroy-method` XML 属性相比,使用 `@PreDestroy` 注解使代码更清晰,因为清理逻辑和 bean 代码位于同一位置,而无需查看 XML 配置文件。 ### 五、最佳实践 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类,最后调用`context.close()`方法关闭容器。 ```java public class PreDestroyApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); context.close(); } } ``` 在`MyConfiguration`类中,使用了`@ComponentScan("com.xcs.spring")`注解告诉 Spring 在指定的包(在这里是 "`com.xcs.spring`")及其子包中搜索带有 `@Component`、`@Service`、`@Repository` 和 `@Controller` 等注解的类,并将它们自动注册为 beans。这样,spring就不必为每个组件明确写一个 bean 定义。Spring 会自动识别并注册它们。 ```java @Configuration @ComponentScan("com.xcs.spring") public class MyConfiguration { } ``` `MyService` 的 Spring Service 类。这个类有一个无参构造函数和一个使用 `@PreDestroy` 注解的方法。 ```java @Service public class MyService { public MyService(){ System.out.println("执行MyService构造函数"); } @PreDestroy public void preDestroy(){ System.out.println("执行@PreDestroy方法"); } } ``` 运行结果发现,关闭上下文时 `@PreDestroy` 方法会被调用。 ```java 执行MyService构造函数 执行@PreDestroy方法 ``` ### 六、时序图 ~~~mermaid sequenceDiagram Title: @PreDestroy注解时序图 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:applyMergedBeanDefinitionPostProcessors(mbd,beanType,beanName)
开始应用 BeanDefinition 的后置处理器。 AbstractAutowireCapableBeanFactory->>CommonAnnotationBeanPostProcessor:postProcessMergedBeanDefinition(beanDefinition,beanType,beanName)
处理 Bean 的通用注解。 CommonAnnotationBeanPostProcessor->>InitDestroyAnnotationBeanPostProcessor:super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName)
为生命周期注解(如 @PreDestroy)处理已合并的 Bean 定义。 InitDestroyAnnotationBeanPostProcessor->>InitDestroyAnnotationBeanPostProcessor:findLifecycleMetadata(clazz)
查找类的生命周期元数据。 InitDestroyAnnotationBeanPostProcessor->>InitDestroyAnnotationBeanPostProcessor:buildLifecycleMetadata(clazz)
构建类的生命周期元数据。 InitDestroyAnnotationBeanPostProcessor->>ReflectionUtils:doWithLocalMethods(clazz,fc)
处理类的所有本地方法。 ReflectionUtils->>InitDestroyAnnotationBeanPostProcessor:
解析那些有 @PreDestroy 注解的方法。 InitDestroyAnnotationBeanPostProcessor->>LifecycleElement:new LifecycleElement(member,ae,pd)
创建新的生命周期元素,代表 @PreDestroy 方法。 InitDestroyAnnotationBeanPostProcessor->>LifecycleMetadata:new LifecycleMetadata(clazz, initMethods, destroyMethods)
创建存储生命周期方法(初始化和销毁)的元数据。 InitDestroyAnnotationBeanPostProcessor->>InitDestroyAnnotationBeanPostProcessor:this.lifecycleMetadataCache.put(clazz, metadata)
将构建的生命周期元数据缓存起来,方便后续访问。 AbstractAutowireCapableBeanFactory->>InitDestroyAnnotationBeanPostProcessor:doClose()
此处省略上下文关闭的步骤 InitDestroyAnnotationBeanPostProcessor->>InitDestroyAnnotationBeanPostProcessor:postProcessBeforeDestruction(bean,beanName) InitDestroyAnnotationBeanPostProcessor->>LifecycleMetadata:invokeDestroyMethods(target,beanName) LifecycleMetadata->>LifecycleElement:invoke(target) LifecycleElement->>Method:this.method.invoke(target, (Object[]) null) ~~~ ### 七、源码分析 #### 前置条件 在Spring中,`InitDestroyAnnotationBeanPostProcessor`是处理`@PreDestroy`等注解的关键类,它实现了下述两个接口。因此,为了深入理解`@PreDestroy`的工作方式,研究这个类是非常有用的。简而言之,为了完全理解`@PreDestroy`的工作机制,了解下述接口确实是必要的。这两个接口提供了对bean生命周期中关键阶段的干预,从而允许进行方法执行和其他相关的操作。 1. `MergedBeanDefinitionPostProcessor`接口 - 此接口提供的`postProcessMergedBeanDefinition`方法允许后处理器修改合并后的bean定义。合并后的bean定义是一个已经考虑了所有父bean定义属性的bean定义。对于`@PreDestroy`注解的处理,这一步通常涉及到收集需要被解析的`@PreDestroy`注解信息并准备对其进行后续处理。 - 🔗 [MergedBeanDefinitionPostProcessor接口传送门](https://github.com/xuchengsheng/spring-reading/tree/master/spring-interface/spring-interface-mergedBeanDefinitionPostProcessor) 2. `DestructionAwareBeanPostProcessor`接口 - 此接口提供了专门处理 bean 的销毁阶段。 - 对于 `@PreDestroy` 注解,`InitDestroyAnnotationBeanPostProcessor` 在这个方法中确保标注了 `@PreDestroy` 的方法在 bean 被销毁之前被执行。 - 🔗 [DestructionAwareBeanPostProcessor接口传送门](https://github.com/xuchengsheng/spring-reading/tree/master/spring-interface/spring-interface-destructionAwareBeanPostProcessor) #### 收集阶段 在`org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#postProcessMergedBeanDefinition`方法中,首先调用了 `super.postProcessMergedBeanDefinition`,即调用了父类或接口默认的实现。 ```java @Override public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class beanType, String beanName) { super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName); // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor#postProcessMergedBeanDefinition`方法中,主要是处理与 `@PreDestroy` 注解相关的资源注入元数据,并在bean定义合并后对这些元数据进行进一步的处理或验证。这是Spring在处理JSR-250 `@PreDestroy` 注解时的处理入口。 ```java @Override public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class beanType, String beanName) { // 根据给定的bean类型查找与其相关的生命周期元数据。 LifecycleMetadata metadata = findLifecycleMetadata(beanType); // 使用找到的生命周期元数据来检查并可能修改给定的bean定义。 metadata.checkConfigMembers(beanDefinition); } ``` 在`org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor#findLifecycleMetadata`方法中,首先尝试从缓存中获取 `LifecycleMetadata`,如果它不存在或需要刷新,则会创建新的 `LifecycleMetadata` 并将其存入缓存。这种缓存策略可以提高效率,避免对同一类型的类反复构建注入元数据。 ```java private LifecycleMetadata findLifecycleMetadata(Class clazz) { // 检查生命周期元数据缓存是否为空,这可能发生在反序列化或销毁阶段。 if (this.lifecycleMetadataCache == null) { return buildLifecycleMetadata(clazz); } // 首先,在并发映射中进行快速检查,以最小的锁定。 LifecycleMetadata metadata = this.lifecycleMetadataCache.get(clazz); // 如果缓存中没有找到元数据,则构建元数据并放入缓存。 if (metadata == null) { synchronized (this.lifecycleMetadataCache) { metadata = this.lifecycleMetadataCache.get(clazz); // 双重检查锁定模式,确保只有一个线程构建和缓存元数据。 if (metadata == null) { // 根据给定的类构建生命周期元数据。 metadata = buildLifecycleMetadata(clazz); // 将新构建的元数据缓存,以便后续请求可以快速从缓存中检索。 this.lifecycleMetadataCache.put(clazz, metadata); } return metadata; } } return metadata; } ``` 在`org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor#buildLifecycleMetadata`方法中,这个方法首先查看类是否有`@PreDestroy`注解,然后遍历类的方法以找到标记为生命周期方法的那些带有 `@PreDestroy` 注解的方法。找到的方法被存储在列表中,并在结束时根据这些方法构建一个 `LifecycleMetadata` 对象。 ```java private LifecycleMetadata buildLifecycleMetadata(final Class clazz) { // 判断给定的类是否有@PreDestroy注解。 if (!AnnotationUtils.isCandidateClass(clazz, Arrays.asList(this.initAnnotationType, this.destroyAnnotationType))) { return this.emptyLifecycleMetadata; } // 存储识别出的初始化和销毁方法的列表。 List destroyMethods = new ArrayList<>(); // ... [代码部分省略以简化] Class targetClass = clazz; // 遍历目标类及其所有父类,直到达到Object类。 do { final List currInitMethods = new ArrayList<>(); final List currDestroyMethods = new ArrayList<>(); // 使用反射处理类的所有本地方法。 ReflectionUtils.doWithLocalMethods(targetClass, method -> { // ... [代码部分省略以简化] // 查找标有@PreDestroy注解的方法。 if (this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) { currDestroyMethods.add(new LifecycleElement(method)); if (logger.isTraceEnabled()) { logger.trace("Found destroy method on class [" + clazz.getName() + "]: " + method); } } }); // 将当前类的生命周期方法添加到总列表中。 destroyMethods.addAll(currDestroyMethods); // ... [代码部分省略以简化] targetClass = targetClass.getSuperclass(); } while (targetClass != null && targetClass != Object.class); // 如果没有找到任何生命周期方法,则返回一个空的生命周期元数据对象;否则,返回新构建的元数据对象。 return (initMethods.isEmpty() && destroyMethods.isEmpty() ? this.emptyLifecycleMetadata : new LifecycleMetadata(clazz, initMethods, destroyMethods)); } ``` 在`org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.LifecycleElement#LifecycleElement`方法中,它用于封装与生命周期相关的方法(如 `@PreDestroy` 注解的方法)。 ```java public LifecycleElement(Method method) { // 检查提供的方法是否是无参数的。生命周期方法(如@PreDestroy)需要是无参数方法。 if (method.getParameterCount() != 0) { throw new IllegalStateException("Lifecycle method annotation requires a no-arg method: " + method); } // 存储提供的方法。 this.method = method; // 根据方法的修饰符(如private)确定唯一标识符。如果方法是私有的,我们使用完全限定名,否则只使用方法名。 this.identifier = (Modifier.isPrivate(method.getModifiers()) ? ClassUtils.getQualifiedMethodName(method) : method.getName()); } ``` #### 销毁阶段 在`org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor#postProcessBeforeDestruction`方法中,实现了 `postProcessBeforeDestruction` 方法,它是 Spring 的 `DestructionAwareBeanPostProcessor` 接口的一部分,用于bean销毁之后要执行的方法。这个特定的实现与处理 `@PreDestroy`注解相关。 ```java @Override public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException { LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass()); try { metadata.invokeDestroyMethods(bean, beanName); } catch (InvocationTargetException ex) { // ... [代码部分省略以简化] } catch (Throwable ex) { // ... [代码部分省略以简化] } } ``` 在`org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor#findLifecycleMetadata`方法中,首先`InitDestroyAnnotationBeanPostProcessor#postProcessMergedBeanDefinition` 元数据收集阶段,`findLifecycleMetadata` 被调用以处理和缓存与 `@PreDestroy`和其他相关注解的 `LifecycleMetadata`。这意味着,在`postProcessBeforeDestruction`阶段再次调用 `findLifecycleMetadata` 时,会直接从缓存中获取已处理的 `LifecycleMetadata`,而不需要重新构建它。 ```java private LifecycleMetadata findLifecycleMetadata(Class clazz) { // 检查生命周期元数据缓存是否为空,这可能发生在反序列化或销毁阶段。 if (this.lifecycleMetadataCache == null) { return buildLifecycleMetadata(clazz); } // 首先,在并发映射中进行快速检查,以最小的锁定。 LifecycleMetadata metadata = this.lifecycleMetadataCache.get(clazz); // 如果缓存中没有找到元数据,则构建元数据并放入缓存。 if (metadata == null) { synchronized (this.lifecycleMetadataCache) { metadata = this.lifecycleMetadataCache.get(clazz); // 双重检查锁定模式,确保只有一个线程构建和缓存元数据。 if (metadata == null) { // 根据给定的类构建生命周期元数据。 metadata = buildLifecycleMetadata(clazz); // 将新构建的元数据缓存,以便后续请求可以快速从缓存中检索。 this.lifecycleMetadataCache.put(clazz, metadata); } return metadata; } } return metadata; } ``` 在`org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.LifecycleMetadata#invokeDestroyMethods`方法中,主要负责在一个 bean 实例上调用销毁方法的地方。它基于预先确定的生命周期元素集合(这里是销毁方法集合)进行迭代,并对每个元素(方法)进行调用。 ```java public void invokeDestroyMethods(Object target, String beanName) throws Throwable { // 获取已经检查过的销毁方法集合 Collection checkedDestroyMethods = this.checkedDestroyMethods; // 根据是否存在已检查的销毁方法,选择要使用的销毁方法集合 Collection destroyMethodsToUse = (checkedDestroyMethods != null ? checkedDestroyMethods : this.destroyMethods); // 判断是否有销毁方法需要执行 if (!destroyMethodsToUse.isEmpty()) { // 遍历销毁方法集合 for (LifecycleElement element : destroyMethodsToUse) { // 如果日志级别为 TRACE,记录正在调用的销毁方法信息 if (logger.isTraceEnabled()) { logger.trace("Invoking destroy method on bean '" + beanName + "': " + element.getMethod()); } // 在目标 bean 上调用当前的销毁方法 element.invoke(target); } } } ``` 在`org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.LifecycleElement#invoke`方法中,使用反射调用目标对象上的特定方法。 ```java public void invoke(Object target) throws Throwable { // 确保封装的方法是可访问的,特别是如果它是私有的。 ReflectionUtils.makeAccessible(this.method); // 使用反射实际调用方法。由于该方法没有参数,所以传递一个null作为参数列表。 this.method.invoke(target, (Object[]) null); } ``` ### 八、注意事项 1. **无参数方法** + 标记为 `@PreDestroy` 的方法不应该有参数,并且应该是无返回值的(即返回 `void`)。 2. **异常处理** + 标记为 `@PreDestroy` 的方法不应该抛出受检异常。如果在执行方法时出现任何错误或异常,建议在方法内部处理,以避免可能的未预期的行为。 3. **访问修饰符** + 虽然可以在 private、protected 或 public 方法上使用 `@PreDestroy`,但最好确保方法是可访问的,特别是在子类或不同的包中。 4. **原型 Bean 的限制** + 对于原型作用域的 bean,`@PreDestroy` 方法不会被 Spring 容器调用,因为 Spring 不管理原型 bean 的完整生命周期。因此,不要依赖 `@PreDestroy` 来清理原型 bean 的资源。 5. **多个销毁方法** + 如果一个 bean 既有 `@PreDestroy` 注解的方法又有通过 XML `destroy-method` 属性指定的方法,那么 `@PreDestroy` 注解的方法将首先被调用,然后是 `destroy-method` 指定的方法。 6. **依赖关系** + 如果我们的 bean 依赖于其他 bean,并且这些依赖关系在销毁过程中仍然重要,那么我们需要确保这些依赖关系在 `@PreDestroy` 方法执行时仍然满足。 7. **执行顺序** + Spring 不保证 `@PreDestroy` 方法的执行顺序,尤其是跨多个 bean 的情况。如果销毁方法之间的执行顺序很重要,我们可能需要考虑其他方法来协调这些销毁动作。 8. **JSR-250 依赖** + `@PreDestroy` 是 JSR-250 规范的一部分。要使用它,确保有适当的库依赖(尽管大多数现代 Spring 项目都会有)。 9. **与 Bean 的生命周期配合** + 只有当 bean 真正被 Spring 容器管理其生命周期时,`@PreDestroy` 才会被调用。这意味着,例如,如果我们手动创建一个 bean 的实例(而不是从 Spring 容器中获取),`@PreDestroy` 方法不会被自动调用。 ### 九、总结 #### 最佳实践总结 1. **启动类入口** - `PreDestroyApplication` 类的 `main` 方法中,使用 `AnnotationConfigApplicationContext` 初始化Spring容器,该方法使用Java注解来配置Spring。我们将 `MyConfiguration` 作为参数传递,意味着我们希望从这个类开始加载Spring的配置。 2. **配置类** - `MyConfiguration` 被标记为一个配置类(通过 `@Configuration` 注解)。其中的 `@ComponentScan` 注解告诉Spring应该在哪些包里搜索组件。在这个例子中,Spring将会扫描 "`com.xcs.spring`" 包以及其子包,寻找例如 `@Component`、`@Service`、`@Repository` 和 `@Controller` 的注解,以此自动地注册bean。 3. **服务类** - 当Spring扫描 "`com.xcs.spring`" 包时,它找到了 `MyService` 类,这个类被标记为一个Service(通过 `@Service` 注解)。因此,Spring会为这个类创建一个bean实例。 4. **关闭上下文** - 当应用上下文被关闭(`context.close()`),所有的单例 beans 将被销毁。在这个过程中,任何带有 `@PreDestroy` 注解的方法都将被调用。所以,`MyService` 类中的 `preDestroy` 方法被执行,打印 "执行@PreDestroy方法"。 #### 源码分析总结 1. **前置条件** - `@PreDestroy`注解的执行依赖于两个核心接口:`MergedBeanDefinitionPostProcessor`和`DestructionAwareBeanPostProcessor`。 - 这两个接口允许Spring在bean生命周期的关键阶段进行干预,如属性注入后,销毁前等。 2. **收集阶段** - 当Spring处理一个Bean的定义并且这个Bean可能有`@PreDestroy`注解时,`InitDestroyAnnotationBeanPostProcessor`的`postProcessMergedBeanDefinition`方法会被调用。 - 在此方法中,与bean相关的`LifecycleMetadata`(包括`@PreDestroy`方法信息)被收集并缓存起来,以便后续使用。 3. **销毁阶段** - 当 Spring 上下文关闭时(如调用 `context.close()`),所有的单例 beans 将被销毁。 - `postProcessBeforeDestruction` 方法是 `DestructionAwareBeanPostProcessor` 接口的一部分,它会被调用来处理每一个需要销毁的 bean。 - 在这个方法中,再次调用 `findLifecycleMetadata` 方法从缓存中获取 `LifecycleMetadata`。 - 之后,`invokeDestroyMethods` 方法会遍历所有销毁方法并在目标 bean 上调用它们。 - 执行是通过查找bean的`LifecycleMetadata`(在之前的收集阶段中已经构建),然后迭代这些元数据中的方法,并使用反射来调用它们。 4. **实际方法调用** - 当需要调用具体的`@PreDestroy`方法时,会使用`LifecycleElement`类的`invoke`方法,该方法再次使用反射来确保方法是可访问的,并实际调用它。 ================================================ FILE: spring-jsr/spring-jsr250-preDestroy/pom.xml ================================================ spring-jsr com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-jsr250-preDestroy 11 11 ================================================ FILE: spring-jsr/spring-jsr250-preDestroy/src/main/java/com/xcs/spring/PreDestroyApplication.java ================================================ package com.xcs.spring; import com.xcs.spring.config.MyConfiguration; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年10月18日 14时17分 **/ public class PreDestroyApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); context.close(); } } ================================================ FILE: spring-jsr/spring-jsr250-preDestroy/src/main/java/com/xcs/spring/config/MyConfiguration.java ================================================ package com.xcs.spring.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; /** * @author xcs * @date 2023年09月19日 16时35分 **/ @Configuration @ComponentScan("com.xcs.spring") public class MyConfiguration { } ================================================ FILE: spring-jsr/spring-jsr250-preDestroy/src/main/java/com/xcs/spring/service/MyService.java ================================================ package com.xcs.spring.service; import org.springframework.stereotype.Service; import javax.annotation.PreDestroy; @Service public class MyService { public MyService(){ System.out.println("执行MyService构造函数"); } @PreDestroy public void preDestroy(){ System.out.println("执行@PreDestroy方法"); } } ================================================ FILE: spring-jsr/spring-jsr250-resource/README.md ================================================ ## @Resource - [@Resource](#resource) - [一、基本信息](#一基本信息) - [二、接口描述](#二接口描述) - [三、接口源码](#三接口源码) - [四、主要功能](#四主要功能) - [五、最佳实践](#五最佳实践) - [六、时序图](#六时序图) - [七、源码分析](#七源码分析) - [前置条件](#前置条件) - [收集阶段](#收集阶段) - [注入阶段](#注入阶段) - [八、注意事项](#八注意事项) - [九、总结](#九总结) - [最佳实践总结](#最佳实践总结) - [源码分析总结](#源码分析总结) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [我的CSDN](https://blog.csdn.net/duzhuang2399/article/details/133887864) 📚 **文章目录** - [所有文章](https://github.com/xuchengsheng/spring-reading) 🔗 **源码地址** - [@Resource源码](https://github.com/xuchengsheng/spring-reading/blob/master/spring-jsr/spring-jsr250-resource) ### 二、接口描述 `@Resource` 注解是由 JSR-250: Common Annotations for the Java Platform 规范定义的。这个规范定义了一组跨多个 Java 技术(如 Java EE 和 Java SE)的公共注解。 ### 三、接口源码 `@Resource` 注解的目的是为了声明和注入应用程序所需的外部资源,从而允许容器在运行时为应用程序组件提供这些资源。 ```java /** * 标注需要注入的资源的注解。 * 这个注解可以用于类、字段和方法上,指示容器为其注入资源。 */ @Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Repeatable(Resources.class) public @interface Resource { /** * 资源的 JNDI 名称。 * * @return 资源名称。 */ String name() default ""; /** * JNDI 查询名称,用于运行时查找资源。 * * @return JNDI 查询名称。 */ String lookup() default ""; /** * 资源的期望类型。默认为 Object,意味着类型不特定。 * * @return 资源类型。 */ Class type() default Object.class; /** * 资源的身份验证类型。 * * @return 身份验证类型。 */ Resource.AuthenticationType authenticationType() default Resource.AuthenticationType.CONTAINER; /** * 标示资源是否可以被多个客户端共享。 * * @return 如果资源可以被共享则返回 true,否则返回 false。 */ boolean shareable() default true; /** * 与资源环境引用关联的产品特定的名称。 * * @return 映射名称。 */ String mappedName() default ""; /** * 对资源的简要描述。 * * @return 资源描述。 */ String description() default ""; /** * 身份验证类型的枚举。 * CONTAINER: 容器管理身份验证。 * APPLICATION: 应用程序管理身份验证。 */ public static enum AuthenticationType { CONTAINER, APPLICATION; } } ``` ### 四、主要功能 1. **资源定位** + 通过 `name` 和 `lookup` 属性,`@Resource` 可以定位到特定的资源,如 JNDI 中的一个数据库连接。 2. **类型指定** + 通过 `type` 属性,它允许指定所需资源的具体Java类型,确保注入的资源与预期类型匹配,从而提供类型安全。 3. **身份验证策略** + `authenticationType` 属性允许我们选择资源的身份验证方式,决定是由容器还是应用程序来进行身份验证。 4. **共享策略** + 通过 `shareable` 属性,它指定资源是否可以在多个客户端或组件之间共享。 5. **供应商特定名称** + `mappedName` 属性可以提供与资源关联的供应商或平台特定的名称,增加部署的灵活性。 6. **描述信息** + 通过 `description` 属性,为资源提供了简要描述,有助于我们和系统管理员理解其用途。 ### 五、最佳实践 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyController`类型的bean并调用了`showService`方法, ```java public class ResourceApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MyController controller = context.getBean(MyController.class); controller.showService(); } } ``` 在`MyConfiguration`类中,使用了`@ComponentScan("com.xcs.spring")`注解告诉 Spring 在指定的包(在这里是 "`com.xcs.spring`")及其子包中搜索带有 `@Component`、`@Service`、`@Repository` 和 `@Controller` 等注解的类,并将它们自动注册为 beans。这样,spring就不必为每个组件明确写一个 bean 定义。Spring 会自动识别并注册它们。 ```java @Configuration @ComponentScan("com.xcs.spring") public class MyConfiguration { } ``` Spring 容器在初始化 `MyController` 时,我们使用了`@Resource`注解,会自动注入一个 `MyService` 类型的 bean 到 `myService` 字段。 ```java @Controller public class MyController { @Resource(name="myService") private MyService myService; public void showService(){ System.out.println("myService = " + myService); } } ``` `MyService` 是一个简单的服务类,但我们没有定义任何方法或功能。 ```java @Service public class MyService { } ``` 运行结果发现,我们使用 `@Resource` 注解的功能,在我们的 Spring 上下文中工作正常,并且它成功地自动注入了所需的依赖关系。 ```java myService = com.xcs.spring.service.MyService@f0c8a99 ``` ### 六、时序图 ~~~mermaid sequenceDiagram Title: @Resource注解时序图 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:applyMergedBeanDefinitionPostProcessors(mbd,beanType,beanName)
应用Bean定义的后置处理器 AbstractAutowireCapableBeanFactory->>CommonAnnotationBeanPostProcessor:postProcessMergedBeanDefinition(beanDefinition,beanType,beanName)
处理已合并的Bean定义 CommonAnnotationBeanPostProcessor->>CommonAnnotationBeanPostProcessor:findResourceMetadata(beanName,clazz,pvs) CommonAnnotationBeanPostProcessor->>CommonAnnotationBeanPostProcessor:buildResourceMetadata(clazz) CommonAnnotationBeanPostProcessor->>ReflectionUtils:doWithLocalFields(clazz,fc)
处理类的本地字段 ReflectionUtils->>CommonAnnotationBeanPostProcessor:解析有@Resource注解的字段 CommonAnnotationBeanPostProcessor->>ResourceElement:ResourceElement(member,ae,pd) CommonAnnotationBeanPostProcessor->>ReflectionUtils:doWithLocalMethods(clazz,fc)
处理类的本地方法 ReflectionUtils->>CommonAnnotationBeanPostProcessor:解析有@Resource注解的方法 CommonAnnotationBeanPostProcessor->>ResourceElement:ResourceElement(member,ae,pd) CommonAnnotationBeanPostProcessor->>CommonAnnotationBeanPostProcessor:injectionMetadataCache.put(cacheKey, metadata)
将元数据存入缓存 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:populateBean(beanName,mbd,bw)
填充Bean的属性值 AbstractAutowireCapableBeanFactory->>CommonAnnotationBeanPostProcessor:postProcessProperties(pvs,bean,beanName)
后处理Bean的属性 CommonAnnotationBeanPostProcessor->>CommonAnnotationBeanPostProcessor:findResourceMetadata(beanName,clazz,pvs) Note right of CommonAnnotationBeanPostProcessor:
从缓存中获取注入的元数据 CommonAnnotationBeanPostProcessor->>InjectionMetadata:inject(target,beanName,pvs) InjectionMetadata->>InjectedElement:inject(target,requestingBeanName,pvs) InjectedElement->>ResourceElement:getResourceToInject(target,requestingBeanName) ResourceElement->>CommonAnnotationBeanPostProcessor:getResource(element,requestingBeanName) CommonAnnotationBeanPostProcessor->>CommonAnnotationBeanPostProcessor:autowireResource(factory,element,requestingBeanName) CommonAnnotationBeanPostProcessor->>AbstractAutowireCapableBeanFactory:resolveBeanByName(name,descriptor) AbstractAutowireCapableBeanFactory->>CommonAnnotationBeanPostProcessor:返回被依赖的Bean CommonAnnotationBeanPostProcessor->>ResourceElement:返回被依赖的Bean ResourceElement->>InjectedElement:返回被依赖的Bean InjectedElement->>Field:field.set(target, 返回被依赖的Bean) ~~~ ### 七、源码分析 #### 前置条件 在Spring中,`CommonAnnotationBeanPostProcessor`是处理`@Inject`等注解的关键类,它实现了下述两个接口。因此,为了深入理解`@Inject`的工作方式,研究这个类是非常有用的。简而言之,为了完全理解`@Inject`的工作机制,了解下述接口确实是必要的。这两个接口提供了对bean生命周期中关键阶段的干预,从而允许进行属性注入和其他相关的操作。 1. `MergedBeanDefinitionPostProcessor`接口 - 此接口提供的`postProcessMergedBeanDefinition`方法允许后处理器修改合并后的bean定义。合并后的bean定义是一个已经考虑了所有父bean定义属性的bean定义。对于`@Inject`注解的处理,这一步通常涉及到收集需要被解析的`@Inject`注解信息并准备对其进行后续处理。 - 🔗 [MergedBeanDefinitionPostProcessor接口传送门](https://github.com/xuchengsheng/spring-reading/tree/master/spring-interface/spring-interface-mergedBeanDefinitionPostProcessor) 2. `InstantiationAwareBeanPostProcessor`接口 - 此接口提供了几个回调方法,允许后处理器在bean实例化之前和实例化之后介入bean的创建过程。特别是,`postProcessProperties`方法允许后处理器对bean的属性进行操作。对于`@Inject`注解,这通常需要在属性设置或依赖注入阶段对 bean 进行处理,并将解析得到的值注入到bean中。 - 🔗 [InstantiationAwareBeanPostProcessor接口传送门](https://github.com/xuchengsheng/spring-reading/tree/master/spring-interface/spring-interface-instantiationAwareBeanPostProcessor) #### 收集阶段 在`org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#postProcessMergedBeanDefinition`方法中,主要是处理与 `@Resource` 注解相关的资源注入元数据,并在bean定义合并后对这些元数据进行进一步的处理或验证。这是Spring在处理JSR-250 `@Resource` 注解时的处理入口。 ```java @Override public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class beanType, String beanName) { // 调用父类的 postProcessMergedBeanDefinition 方法,确保继承的处理逻辑得到执行 super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName); // 根据给定的 bean 名称和类型查找相关的资源注入元数据 InjectionMetadata metadata = findResourceMetadata(beanName, beanType, null); // 使用找到的资源注入元数据对bean定义进行进一步的处理或验证 metadata.checkConfigMembers(beanDefinition); } ``` 在`org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#findResourceMetadata`方法中,首先尝试从缓存中获取 `InjectionMetadata`,如果它不存在或需要刷新,则会创建新的 `InjectionMetadata` 并将其存入缓存。这种缓存策略可以提高效率,避免对同一类型的类反复构建注入元数据。 ```java private InjectionMetadata findResourceMetadata(String beanName, final Class clazz, @Nullable PropertyValues pvs) { // 若 beanName 有值,则使用 beanName 作为缓存键;否则,使用类名作为缓存键。 // 这也为那些自定义调用提供了向后兼容性。 String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName()); // 首先进行一个快速检查在并发Map中的缓存,以最小化锁定。 InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey); // 检查当前的 metadata 是否需要刷新,例如它可能是过时的或不再适用。 if (InjectionMetadata.needsRefresh(metadata, clazz)) { // 对缓存进行同步处理以确保线程安全 synchronized (this.injectionMetadataCache) { metadata = this.injectionMetadataCache.get(cacheKey); if (InjectionMetadata.needsRefresh(metadata, clazz)) { if (metadata != null) { // 清除旧的 metadata metadata.clear(pvs); } // 构建新的资源注入元数据 metadata = buildResourceMetadata(clazz); // 将新的 metadata 放入缓存 this.injectionMetadataCache.put(cacheKey, metadata); } } } // 返回找到或创建的资源注入元数据 return metadata; } ``` 在`org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#buildResourceMetadata`方法中,目的是检查给定类及其所有超类中的所有字段和方法,查找标有 `@Resource` 和其他相关注解的元素,并据此创建一个 `InjectionMetadata` 对象。这个对象会包含了`ResourceElement`类,此类会处理如何注入这些资源的所有必要信息。 ```java private InjectionMetadata buildResourceMetadata(final Class clazz) { // 判断给定的类是否可能包含任何资源注解。 if (!AnnotationUtils.isCandidateClass(clazz, resourceAnnotationTypes)) { return InjectionMetadata.EMPTY; // 如果不是,返回一个空的元数据。 } List elements = new ArrayList<>(); Class targetClass = clazz; // 开始遍历给定类及其所有超类 do { final List currElements = new ArrayList<>(); // 检查类的所有局部字段上的注解 ReflectionUtils.doWithLocalFields(targetClass, field -> { // ... (此处的代码检查各种注解,例如@WebServiceRef, @EJB 和 @Resource,并据此创建对应的元素) // ... [代码部分省略以简化] if (field.isAnnotationPresent(Resource.class)) { if (Modifier.isStatic(field.getModifiers())) { throw new IllegalStateException("@Resource annotation is not supported on static fields"); } if (!this.ignoredResourceTypes.contains(field.getType().getName())) { currElements.add(new ResourceElement(field, field, null)); } } }); // 检查类的所有局部方法上的注解 ReflectionUtils.doWithLocalMethods(targetClass, method -> { // ... (与字段相似,此处的代码检查各种注解,并据此创建对应的元素) // ... [代码部分省略以简化] if (bridgedMethod.isAnnotationPresent(Resource.class)) { if (Modifier.isStatic(method.getModifiers())) { throw new IllegalStateException("@Resource annotation is not supported on static methods"); } Class[] paramTypes = method.getParameterTypes(); if (paramTypes.length != 1) { throw new IllegalStateException("@Resource annotation requires a single-arg method: " + method); } if (!this.ignoredResourceTypes.contains(paramTypes[0].getName())) { PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz); currElements.add(new ResourceElement(method, bridgedMethod, pd)); } } }); // 将当前类找到的元素添加到总列表的开头 elements.addAll(0, currElements); // 移动到下一个超类进行处理 targetClass = targetClass.getSuperclass(); } // 持续处理,直到没有超类或达到 Object 类为止 while (targetClass != null && targetClass != Object.class); // 根据收集到的注入元素创建并返回一个 InjectionMetadata 实例 return InjectionMetadata.forElements(elements, clazz); } ``` 在`org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.ResourceElement#ResourceElement`方法中,主要基于 `@Resource` 注解和其他相关信息(如字段或方法名、`@Lazy` 注解等)初始化了一个 `ResourceElement` 实例,该实例将包含有关如何查找和注入特定资源的所有必要信息。 PS:在`ResourceElement`实现中,Spring只关心了`@Resource` 注解的 `name`, `type`, `lookup` 和 `mappedName` 这四个属性,这是因为这些属性与Spring的DI(依赖注入)机制最直接相关。其他的属性,如 `authenticationType`, `shareable`, 和 `description`,在Spring的上下文中并没有实际的用途或者没有被实现。 ```java public ResourceElement(Member member, AnnotatedElement ae, @Nullable PropertyDescriptor pd) { // 调用父类的构造函数,传入成员和属性描述符 super(member, pd); // 从给定的注解元素(字段或方法)获取 @Resource 注解 Resource resource = ae.getAnnotation(Resource.class); // 获取资源的名称和类型 String resourceName = resource.name(); Class resourceType = resource.type(); // 判断资源名称是否为默认名称(即没有明确指定) this.isDefaultName = !StringUtils.hasLength(resourceName); if (this.isDefaultName) { // 如果资源名称是默认的,使用成员的名称作为资源名称 resourceName = this.member.getName(); // 如果这是一个setter方法,可能会提取属性名称作为资源名称 if (this.member instanceof Method && resourceName.startsWith("set") && resourceName.length() > 3) { resourceName = Introspector.decapitalize(resourceName.substring(3)); } } // 解析可能的占位符或表达式 else if (embeddedValueResolver != null) { resourceName = embeddedValueResolver.resolveStringValue(resourceName); } // 如果资源类型明确指定,则验证该类型 if (Object.class != resourceType) { checkResourceType(resourceType); } else { // 如果没有明确指定资源类型,根据成员类型推断资源类型 resourceType = getResourceType(); } this.name = (resourceName != null ? resourceName : ""); this.lookupType = resourceType; // 获取查找值或映射名称 String lookupValue = resource.lookup(); this.mappedName = (StringUtils.hasLength(lookupValue) ? lookupValue : resource.mappedName()); // 检查是否存在 @Lazy 注解,并据此设置lazyLookup属性 Lazy lazy = ae.getAnnotation(Lazy.class); this.lazyLookup = (lazy != null && lazy.value()); } ``` #### 注入阶段 在`org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#postProcessProperties`方法中,实现了 `postProcessProperties` 方法,它是 Spring 的 `InstantiationAwareBeanPostProcessor` 接口的一部分,用于在实例化 bean 之后但在属性注入之前进行操作。这个特定的实现与处理 `@Resource` 注解相关。 ```java @Override public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) { // 根据bean的名称和类找到相应的资源注入元数据 InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs); try { // 尝试使用找到的元数据对给定的bean进行注入 metadata.inject(bean, beanName, pvs); } // 如果在注入过程中出现任何问题,抛出一个Bean创建异常 catch (Throwable ex) { throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex); } // 返回处理后的属性值 return pvs; } ``` 在`org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#findResourceMetadata`方法中,首先`CommonAnnotationBeanPostProcessor#postProcessMergedBeanDefinition` 元数据收集阶段,`findResourceMetadata` 被调用以处理和缓存与 `@Resource` 和其他相关注解相关的 `InjectionMetadata`。这意味着,在`postProcessProperties`阶段之后的其他生命周期方法中,当再次调用 `findResourceMetadata` 时,会直接从缓存中获取已处理的 `InjectionMetadata`,而不需要重新构建它。 ```java private InjectionMetadata findResourceMetadata(String beanName, final Class clazz, @Nullable PropertyValues pvs) { // 如果 beanName 存在则使用它作为缓存键,否则使用类名。这也确保了与自定义调用者的向后兼容性。 String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName()); // 首先在并发 Map 中进行快速检查,尽量减少锁的使用。 InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey); // 检查当前的 metadata 是否需要刷新。例如,它可能是过时的或不再适用。 if (InjectionMetadata.needsRefresh(metadata, clazz)) { synchronized (this.injectionMetadataCache) { // 对缓存进行同步处理以确保线程安全 metadata = this.injectionMetadataCache.get(cacheKey); if (InjectionMetadata.needsRefresh(metadata, clazz)) { if (metadata != null) { metadata.clear(pvs); // 清除旧的 metadata } // 构建新的资源注入元数据 metadata = buildResourceMetadata(clazz); // 将新的 metadata 放入缓存 this.injectionMetadataCache.put(cacheKey, metadata); } } } return metadata; // 返回找到或创建的资源注入元数据 } ``` 在`org.springframework.beans.factory.annotation.InjectionMetadata#inject`方法中,首先是遍历 `InjectedElement` 集合,并为给定的目标对象执行实际的注入操作。 ```java public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable { Collection checkedElements = this.checkedElements; Collection elementsToIterate = (checkedElements != null ? checkedElements : this.injectedElements); if (!elementsToIterate.isEmpty()) { for (InjectedElement element : elementsToIterate) { element.inject(target, beanName, pvs); } } } ``` 在`org.springframework.beans.factory.annotation.InjectionMetadata.InjectedElement#inject`方法中,主要根据注入点的类型(字段或方法)执行实际的资源注入操作。对于字段,它直接设置字段的值。对于方法,它调用该方法并将资源作为参数传递,从而实现注入。 ```java protected void inject(Object target, @Nullable String requestingBeanName, @Nullable PropertyValues pvs) throws Throwable { // 如果注入点是一个字段 if (this.isField) { Field field = (Field) this.member; // 获取字段信息 ReflectionUtils.makeAccessible(field); // 确保字段是可访问的,即使它是私有的 // 实际将资源设置/注入到目标对象的字段中 field.set(target, getResourceToInject(target, requestingBeanName)); } else { // 如果注入点是一个方法(例如setter方法) // 检查是否应跳过属性注入,可能基于提供的属性值(pvs) if (checkPropertySkipping(pvs)) { return; } try { Method method = (Method) this.member; // 获取方法信息 ReflectionUtils.makeAccessible(method); // 确保方法是可访问的,即使它是私有的 // 通过方法调用实际将资源注入到目标对象中 method.invoke(target, getResourceToInject(target, requestingBeanName)); } catch (InvocationTargetException ex) { // 如果调用方法时发生异常,抛出实际的目标异常 throw ex.getTargetException(); } } } ``` 在`org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.ResourceElement#getResourceToInject`方法中,首先根据 `lazyLookup` 属性来决定是否为资源构建一个懒加载代理。如果 `lazyLookup` 为 `true`,则返回一个代表懒加载资源的代理对象;否则,它直接返回资源实例。 ```java @Override protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) { // 检查资源是否应该懒加载 return (this.lazyLookup ? // 如果是懒加载,则构建一个资源的懒加载代理 buildLazyResourceProxy(this, requestingBeanName) : // 如果不是懒加载,则直接获取资源 getResource(this, requestingBeanName)); } ``` 在`org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#getResource`方法中,首先尝试从 JNDI 中获取资源,如果 JNDI 不可用或不适用,它会尝试从 Spring 上下文中自动装配资源。如果两者都不可用,它会抛出异常。 ```java protected Object getResource(LookupElement element, @Nullable String requestingBeanName) throws NoSuchBeanDefinitionException { // 如果 LookupElement 的 'mappedName' 属性有值 if (StringUtils.hasLength(element.mappedName)) { // 从 JNDI 中获取与 'mappedName' 匹配的资源 return this.jndiFactory.getBean(element.mappedName, element.lookupType); } // 如果配置为总是使用 JNDI 查找 if (this.alwaysUseJndiLookup) { // 使用 LookupElement 的 'name' 属性从 JNDI 中获取资源 return this.jndiFactory.getBean(element.name, element.lookupType); } // 如果没有配置 resourceFactory(例如,一个 Spring ApplicationContext) if (this.resourceFactory == null) { // 抛出异常,因为无法从 Spring 上下文中获取资源 throw new NoSuchBeanDefinitionException(element.lookupType, "No resource factory configured - specify the 'resourceFactory' property"); } // 从 Spring ApplicationContext 中自动装配资源 return autowireResource(this.resourceFactory, element, requestingBeanName); } ``` 在`org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#autowireResource`方法中,首先根据 `LookupElement` 提供的描述自动装配资源。它可以根据类型匹配来解析依赖,或者直接根据名称来解析。对于被自动装配的资源,如果`BeanFactory`是`ConfigurableBeanFactory`,会为每个自动装配的bean名称注册依赖关系。 ```java protected Object autowireResource(BeanFactory factory, LookupElement element, @Nullable String requestingBeanName) throws NoSuchBeanDefinitionException { Object resource; Set autowiredBeanNames; String name = element.name; // 如果工厂支持自动装配能力 if (factory instanceof AutowireCapableBeanFactory) { AutowireCapableBeanFactory beanFactory = (AutowireCapableBeanFactory) factory; DependencyDescriptor descriptor = element.getDependencyDescriptor(); // 当资源的名称为默认值,且在BeanFactory中没有与这个名字匹配的bean时 if (this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)) { autowiredBeanNames = new LinkedHashSet<>(); resource = beanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null); if (resource == null) { throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object"); } } // 否则,通过名称解析资源 else { resource = beanFactory.resolveBeanByName(name, descriptor); autowiredBeanNames = Collections.singleton(name); } } // 如果不支持自动装配,则直接使用名称和类型从BeanFactory获取资源 else { resource = factory.getBean(name, element.lookupType); autowiredBeanNames = Collections.singleton(name); } // 如果BeanFactory是可配置的,为每一个自动装配的bean名注册一个依赖bean if (factory instanceof ConfigurableBeanFactory) { ConfigurableBeanFactory beanFactory = (ConfigurableBeanFactory) factory; for (String autowiredBeanName : autowiredBeanNames) { if (requestingBeanName != null && beanFactory.containsBean(autowiredBeanName)) { beanFactory.registerDependentBean(autowiredBeanName, requestingBeanName); } } } return resource; } ``` ### 八、注意事项 1. **来源** + `@Resource` 注解来自 Java 的 JSR-250 规范,而不是 Spring 核心。尽管如此,Spring 提供了对此注解的全面支持。 2. **默认名称** + 如果不指定名称,`@Resource` 默认会按照属性名或方法名来寻找匹配的 bean。比如,`@Resource private MyService myService;` 会查找名为 "myService" 的 bean。 3. **类型 vs 名称** + `@Resource` 默认是基于名称进行自动装配的。如果没有找到名称匹配的bean,它会回退到类型匹配。这与 `@Autowired` 不同,后者默认基于类型进行自动装配。 4. **指定名称** + 我们可以通过 `name` 属性明确指定要注入的 bean 的名称:`@Resource(name = "myService")`。 5. **处理冲突** + 在一个上下文中,如果有多个相同类型的 bean,为避免冲突,最好使用 `name` 属性明确指定想要注入的 bean。 6. **与其他注解的结合** + 不建议在同一个字段或setter上同时使用 `@Resource` 和 `@Autowired` 或 `@Inject`。 7. **静态字段** + `@Resource` 不支持静态字段。静态字段不属于任何实例,因此无法注入依赖关系。 8. **必需性** + 默认情况下,`@Resource` 注解的依赖是必需的,即如果没有找到相应的 bean,会抛出异常。如果某些情况下允许依赖项为 null 或不存在,必须结合其他配置来实现,例如使用 `@Autowired(required = false)`。 9. **懒加载** + 在Spring中,如果我们希望延迟资源的初始化并在首次请求时加载它,可以结合 `@Lazy` 注解使用。 ### 九、总结 #### 最佳实践总结 1. **启动类入口** + `ResourceApplication` 类作为应用程序的入口。它创建了一个基于注解的 Spring 应用上下文,`AnnotationConfigApplicationContext`,并为其提供了一个配置类 `MyConfiguration`。 2. **上下文初始化** + 通过 `AnnotationConfigApplicationContext`,Spring 上下文初始化并加载配置类,同时扫描指定的包及其子包中的组件。 3. **组件扫描** + `MyConfiguration` 类使用 `@ComponentScan` 注解指定 Spring 搜索 "`com.xcs.spring`" 包及其子包。Spring 在这些包中搜索带有 `@Component`、`@Service`、`@Repository` 和 `@Controller` 等注解的类,并自动注册它们作为 beans。 4. **依赖注入** + 在 `MyController` 类中,一个名为 `myService` 的字段使用 `@Resource` 注解。这告诉 Spring,当创建 `MyController` 的实例时,它需要为 `myService` 字段注入一个类型为 `MyService` 的 bean。并且,由于指定了 `name="myService"`,Spring 将按名称而不是类型进行注入。 5. **服务创建** + `MyService` 类被标注为 `@Service`,这意味着 Spring 会自动创建其实例并将其注册到上下文中。在后续的依赖注入过程中,它被注入到了 `MyController` 的 `myService` 字段中。 6. **方法调用** + 在 `ResourceApplication` 的 `main` 方法中,从上下文中获取了 `MyController` 的 bean 并调用了其 `showService` 方法,从而输出了 `myService` 的实例信息,证明了注入过程是成功的。 7. **输出结果** + 应用程序输出了 `myService` 实例的信息,证明了 `@Resource` 注解成功地完成了依赖注入,并且整个过程工作得很好。 #### 源码分析总结 1. **前置条件** + `CommonAnnotationBeanPostProcessor`是处理`@Resource`和其他相关注解的核心类。为了完全理解`@Resource`的工作机制,我们关注了`MergedBeanDefinitionPostProcessor`和`InstantiationAwareBeanPostProcessor`两个接口。这两个接口提供了对bean生命周期中的关键阶段的干预,从而允许进行属性注入和其他相关操作。 2. **收集阶段** - `postProcessMergedBeanDefinition`方法:在bean定义合并后,对`@Resource`相关的资源注入元数据进行进一步处理。 - `findResourceMetadata`方法:尝试从缓存中获取与`@Resource`相关的`InjectionMetadata`。如果它不存在或需要刷新,创建一个新的`InjectionMetadata`并将其加入缓存。 - `buildResourceMetadata`方法:检查类及其所有超类,查找带有`@Resource`等相关注解的字段和方法,为这些元素创建一个新的`InjectionMetadata`对象。 3. **注入阶段** - `postProcessProperties`方法:在实例化bean之后但在属性注入之前,用于处理与`@Resource`注解相关的注入。 - `inject`方法:遍历`InjectedElement`集合,并为给定的目标对象执行实际的注入操作。 - `getResourceToInject`方法:根据`lazyLookup`属性决定是为资源构建一个懒加载代理还是直接返回资源实例。 - `getResource`方法:首先尝试从JNDI中获取资源。如果JNDI不可用或不适用,尝试从Spring上下文中自动装配资源。 - `autowireResource`方法:根据`LookupElement`描述自动装配资源。它可以通过类型匹配来解析依赖,或者直接通过名称来解析。 ================================================ FILE: spring-jsr/spring-jsr250-resource/pom.xml ================================================ spring-jsr com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-jsr250-resource ================================================ FILE: spring-jsr/spring-jsr250-resource/src/main/java/com/xcs/spring/ResourceApplication.java ================================================ package com.xcs.spring; import com.xcs.spring.config.MyConfiguration; import com.xcs.spring.controller.MyController; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年08月07日 16时21分 **/ public class ResourceApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MyController controller = context.getBean(MyController.class); controller.showService(); } } ================================================ FILE: spring-jsr/spring-jsr250-resource/src/main/java/com/xcs/spring/config/MyConfiguration.java ================================================ package com.xcs.spring.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan("com.xcs.spring") public class MyConfiguration { } ================================================ FILE: spring-jsr/spring-jsr250-resource/src/main/java/com/xcs/spring/controller/MyController.java ================================================ package com.xcs.spring.controller; import com.xcs.spring.service.MyService; import org.springframework.stereotype.Controller; import javax.annotation.Resource; import javax.inject.Inject; @Controller public class MyController { @Resource(name="myService") private MyService myService; public void showService(){ System.out.println("myService = " + myService); } } ================================================ FILE: spring-jsr/spring-jsr250-resource/src/main/java/com/xcs/spring/service/MyService.java ================================================ package com.xcs.spring.service; import org.springframework.stereotype.Service; @Service public class MyService { } ================================================ FILE: spring-jsr/spring-jsr330-inject/README.md ================================================ ## @Inject - [@Inject](#inject) - [一、基本信息](#一基本信息) - [二、注解描述](#二注解描述) - [三、接口源码](#三接口源码) - [四、主要功能](#四主要功能) - [五、最佳实践](#五最佳实践) - [六、时序图](#六时序图) - [七、源码分析](#七源码分析) - [前置条件](#前置条件) - [收集阶段](#收集阶段) - [注入阶段](#注入阶段) - [八、注意事项](#八注意事项) - [九、总结](#九总结) - [最佳实践总结](#最佳实践总结) - [源码分析总结](#源码分析总结) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [我的CSDN](https://blog.csdn.net/duzhuang2399/article/details/133880436) 📚 **文章目录** - [所有文章](https://github.com/xuchengsheng/spring-reading) 🔗 **源码地址** - [@Inject源码](https://github.com/xuchengsheng/spring-reading/blob/master/spring-jsr/spring-jsr330-inject) ### 二、注解描述 `@Inject`注解起源于JSR-330,也称为`javax.inject.Inject`。这是Java依赖注入的一个标准化规范。Spring支持这个注解,意味着我们可以在Spring应用中使用`@Inject`来执行依赖注入,与使用Spring原生的`@Autowired`注解类似。与`@Autowired`不同的是`@Inject`没有一个内置的“`required`”属性。这意味着,如果我们想要一个可选的依赖注入。但是,我们可以使用 Java 8 的 `java.util.Optional` 类型来达到类似的效果。 ### 三、接口源码 从源码上可以看到`@Inject`是为多种注入方式比如:字段注入、setter方法注入和构造函数注入。 ```java @Target({ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Inject { } ``` ### 四、主要功能 1. **自动注入依赖** - 使用 `@Inject`,我们可以请求框架自动为某个字段、构造函数或方法注入一个依赖。这意味着我们不需要手动创建和管理对象的实例。 2. **多种注入点** - **字段注入** - 可以直接在类的字段上使用 `@Inject`,从而请求框架为该字段提供相应的bean。 - **构造函数注入** - 将 `@Inject` 放在类的构造函数上,表示我们想通过该构造函数注入依赖。 - **方法注入** - 可以在setter方法或任何其他方法上使用 `@Inject`,表示我们希望框架通过调用该方法来注入依赖。 3. **与其他注解协同工作** - 特别是与 `@Named` 注解结合,用于消除依赖的歧义。例如,如果我们有多个实现同一接口的bean,我们可以使用 `@Named` 指定我们想要注入哪一个bean。 4. **跨框架兼容性** - 由于 `@Inject` 是 JSR-330 标准的一部分,使用它可以增加代码的可移植性。这意味着,理论上,使用 `@Inject` 注解的代码应该能在任何支持 JSR-330 的框架(如 Spring、Java EE、Google Guice 等)中运行。 ### 五、最佳实践 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyController`类型的bean并调用了`showService`方法, ```java public class InjectApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MyController controller = context.getBean(MyController.class); controller.showService(); } } ``` 在`MyConfiguration`类中,使用了`@ComponentScan("com.xcs.spring")`注解告诉 Spring 在指定的包(在这里是 "`com.xcs.spring`")及其子包中搜索带有 `@Component`、`@Service`、`@Repository` 和 `@Controller` 等注解的类,并将它们自动注册为 beans。这样,spring就不必为每个组件明确写一个 bean 定义。Spring 会自动识别并注册它们。 ```java @Configuration @ComponentScan("com.xcs.spring") public class MyConfiguration { } ``` Spring 容器在初始化 `MyController` 时,我们使用了`@Inject`注解,会自动注入一个 `MyService` 类型的 bean 到 `myService` 字段。 ```java @Controller public class MyController { @Inject private MyService myService; public void showService(){ System.out.println("myService = " + myService); } } ``` `MyService` 是一个简单的服务类,但我们没有定义任何方法或功能。 ```java @Service public class MyService { } ``` 运行结果发现,我们使用 `@Inject` 注解的功能,在我们的 Spring 上下文中工作正常,并且它成功地自动注入了所需的依赖关系。 ```java myService = com.xcs.spring.service.MyService@6e535154 ``` ### 六、时序图 ~~~mermaid sequenceDiagram Title: @Inject注解时序图 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:applyMergedBeanDefinitionPostProcessors(mbd,beanType,beanName)
应用Bean定义的后置处理器 AbstractAutowireCapableBeanFactory->>AutowiredAnnotationBeanPostProcessor:postProcessMergedBeanDefinition(beanDefinition,beanType,beanName)
处理已合并的Bean定义 AutowiredAnnotationBeanPostProcessor->>AutowiredAnnotationBeanPostProcessor:findAutowiringMetadata(beanName,clazz,pvs)
查找自动注入的元数据 AutowiredAnnotationBeanPostProcessor->>AutowiredAnnotationBeanPostProcessor:buildAutowiringMetadata(clazz)
构建自动注入的元数据 AutowiredAnnotationBeanPostProcessor->>ReflectionUtils:doWithLocalFields(clazz,fc)
处理类的本地字段 ReflectionUtils->>AutowiredAnnotationBeanPostProcessor:解析有@Inject注解的字段 AutowiredAnnotationBeanPostProcessor->>ReflectionUtils:doWithLocalMethods(clazz,fc)
处理类的本地方法 ReflectionUtils->>AutowiredAnnotationBeanPostProcessor:解析有@Inject注解的方法 AutowiredAnnotationBeanPostProcessor->>AutowiredAnnotationBeanPostProcessor:injectionMetadataCache.put(cacheKey, metadata)
将元数据存入缓存 AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:populateBean(beanName,mbd,bw)
填充Bean的属性值 AbstractAutowireCapableBeanFactory->>AutowiredAnnotationBeanPostProcessor:postProcessProperties(pvs,bean,beanName)
后处理Bean的属性 AutowiredAnnotationBeanPostProcessor->>AutowiredAnnotationBeanPostProcessor:findAutowiringMetadata(beanName,clazz,pvs)
再次查找自动注入的元数据 Note right of AutowiredAnnotationBeanPostProcessor:
从缓存中获取注入的元数据 AutowiredAnnotationBeanPostProcessor->>InjectionMetadata:inject(bean, beanName, pvs)
执行实际的属性注入 InjectionMetadata->>AutowiredFieldElement:inject(target, beanName, pvs)
注入特定的字段元素 AutowiredFieldElement->>AutowiredFieldElement:resolveFieldValue(field,bean,beanName)
解析字段的值 AutowiredFieldElement->>DefaultListableBeanFactory:resolveDependency(desc, beanName, autowiredBeanNames, typeConverter)
解析字段的依赖 DefaultListableBeanFactory->>DefaultListableBeanFactory:doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter)
解析指定的依赖关系 DefaultListableBeanFactory->>DefaultListableBeanFactory:findAutowireCandidates(beanName, type, descriptor)
查找符合自动装配条件的候选 Bean DefaultListableBeanFactory->>DefaultListableBeanFactory:addCandidateEntry(result, candidate, descriptor, requiredType)
向结果集中添加候选 Bean DefaultListableBeanFactory->>AbstractBeanFactory:getType(name)
获取指定 Bean 的类型 AbstractBeanFactory->>DefaultListableBeanFactory:返回被依赖Bean的类
返回依赖 Bean 的实际类 DefaultListableBeanFactory->>DependencyDescriptor:resolveCandidate(beanName, requiredType, beanFactory)
解析候选的依赖 Bean DependencyDescriptor->>AbstractBeanFactory:getBean(name)
获取指定的 Bean 实例 AbstractBeanFactory->>DependencyDescriptor:
返回具体的依赖 Bean 实例 DependencyDescriptor->>DefaultListableBeanFactory:
返回依赖的 Bean 实例给工厂 DefaultListableBeanFactory->>AutowiredFieldElement:
返回依赖的 Bean 给字段注入器 AutowiredFieldElement->>Field:field.set(bean, value)
实际设置 Bean 的字段值 ~~~ ### 七、源码分析 #### 前置条件 在Spring中,`AutowiredAnnotationBeanPostProcessor`是处理`@Inject`等注解的关键类,它实现了下述两个接口。因此,为了深入理解`@Inject`的工作方式,研究这个类是非常有用的。简而言之,为了完全理解`@Inject`的工作机制,了解下述接口确实是必要的。这两个接口提供了对bean生命周期中关键阶段的干预,从而允许进行属性注入和其他相关的操作。 1. `MergedBeanDefinitionPostProcessor`接口 - 此接口提供的`postProcessMergedBeanDefinition`方法允许后处理器修改合并后的bean定义。合并后的bean定义是一个已经考虑了所有父bean定义属性的bean定义。对于`@Inject`注解的处理,这一步通常涉及到收集需要被解析的`@Inject`注解信息并准备对其进行后续处理。 - 🔗 [MergedBeanDefinitionPostProcessor接口传送门](https://github.com/xuchengsheng/spring-reading/tree/master/spring-interface/spring-interface-mergedBeanDefinitionPostProcessor) 2. `InstantiationAwareBeanPostProcessor`接口 - 此接口提供了几个回调方法,允许后处理器在bean实例化之前和实例化之后介入bean的创建过程。特别是,`postProcessProperties`方法允许后处理器对bean的属性进行操作。对于`@Inject`注解,这通常需要在属性设置或依赖注入阶段对 bean 进行处理,并将解析得到的值注入到bean中。 - 🔗 [InstantiationAwareBeanPostProcessor接口传送门](https://github.com/xuchengsheng/spring-reading/tree/master/spring-interface/spring-interface-instantiationAwareBeanPostProcessor) #### 收集阶段 在`org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition`方法中,主要确保给定的bean定义与其预期的自动装配元数据一致。 ```java @Override public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class beanType, String beanName) { // 对于给定的bean名称和类型,它首先尝试查找相关的InjectionMetadata,这可能包含了该bean的字段和方法的注入信息 InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null); // 使用找到的InjectionMetadata来验证bean定义中的配置成员是否与预期的注入元数据匹配。 metadata.checkConfigMembers(beanDefinition); } ``` 在`org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#findAutowiringMetadata`方法中,确保了始终为给定的bean名称和类获取最新和相关的`InjectionMetadata`,并利用缓存机制优化性能。 ```java private InjectionMetadata findAutowiringMetadata(String beanName, Class clazz, @Nullable PropertyValues pvs) { // 如果beanName为空,则使用类名作为缓存键。 String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName()); // 首先尝试从并发缓存中获取InjectionMetadata。 InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey); // 检查获取到的元数据是否需要刷新。 if (InjectionMetadata.needsRefresh(metadata, clazz)) { // 使用双重检查锁定确保线程安全。 synchronized (this.injectionMetadataCache) { metadata = this.injectionMetadataCache.get(cacheKey); if (InjectionMetadata.needsRefresh(metadata, clazz)) { // 如果有旧的元数据,清除它。 if (metadata != null) { metadata.clear(pvs); } // 为给定的类构建新的InjectionMetadata。 metadata = buildAutowiringMetadata(clazz); // 将新构建的元数据更新到缓存中。 this.injectionMetadataCache.put(cacheKey, metadata); } } } // 返回找到的或新构建的元数据。 return metadata; } ``` 在`org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#buildAutowiringMetadata`方法中,查找类及其所有父类中的字段和方法,以找出所有带有自动装配注解的字段和方法,并为它们创建一个统一的`InjectionMetadata`对象。 ```java private InjectionMetadata buildAutowiringMetadata(final Class clazz) { // 检查类是否含有自动装配注解,若无则直接返回空的InjectionMetadata。 if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) { return InjectionMetadata.EMPTY; } // 初始化存放注入元素的列表。 List elements = new ArrayList<>(); Class targetClass = clazz; do { // 当前类中要注入的元素列表。 final List currElements = new ArrayList<>(); // 处理类中的所有字段。 ReflectionUtils.doWithLocalFields(targetClass, field -> { // 查找字段上的自动装配注解。 MergedAnnotation ann = findAutowiredAnnotation(field); if (ann != null) { // ... [代码部分省略以简化] boolean required = determineRequiredStatus(ann); // 创建一个新的AutowiredFieldElement并加入到列表。 currElements.add(new AutowiredFieldElement(field, required)); } }); // 处理类中的所有方法。 ReflectionUtils.doWithLocalMethods(targetClass, method -> { Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) { return; } // 查找方法上的自动装配注解。 MergedAnnotation ann = findAutowiredAnnotation(bridgedMethod); if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) { // ... [代码部分省略以简化] boolean required = determineRequiredStatus(ann); PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz); // 创建一个新的AutowiredMethodElement并加入到列表。 currElements.add(new AutowiredMethodElement(method, required, pd)); } }); // 将当前类的注入元素加入到总的注入元素列表的开头。 elements.addAll(0, currElements); // 处理父类。 targetClass = targetClass.getSuperclass(); } // 循环直至Object类。 while (targetClass != null && targetClass != Object.class); // 返回为元素列表创建的新的InjectionMetadata。 return InjectionMetadata.forElements(elements, clazz); } ``` 在`org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#autowiredAnnotationTypes`字段中,主要的用途是告诉`AutowiredAnnotationBeanPostProcessor`哪些注解它应该处理。当Spring容器解析bean定义并创建bean实例时,如果这个bean的字段、方法或构造函数上的注解被包含在这个`autowiredAnnotationTypes`集合中,那么`AutowiredAnnotationBeanPostProcessor`就会对它进行处理。 ```java public AutowiredAnnotationBeanPostProcessor() { // ... [代码部分省略以简化] try { this.autowiredAnnotationTypes.add((Class) ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader())); logger.trace("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring"); } catch (ClassNotFoundException ex) { // JSR-330 API not available - simply skip. } } ``` #### 注入阶段 在`org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessProperties`方法中,用于处理bean属性的后处理,特别是通过`@Inject`等注解进行的属性注入。 ```java @Override public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) { // 获取与bean名称和类相关的InjectionMetadata。 // 这包括该bean需要进行注入的所有字段和方法。 InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs); try { // 使用获取到的InjectionMetadata,实际进行属性的注入。 metadata.inject(bean, beanName, pvs); } // 如果在注入过程中出现BeanCreationException,直接抛出。 catch (BeanCreationException ex) { throw ex; } // 捕获其他异常,并以BeanCreationException的形式抛出,提供详细的错误信息。 catch (Throwable ex) { throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex); } // 返回原始的PropertyValues,因为这个方法主要关注依赖注入而不是修改属性。 return pvs; } ``` 在`org.springframework.beans.factory.annotation.InjectionMetadata#inject`方法中,主要目的是将所有需要注入的元素(例如带有`@Inject`等注解的字段或方法)注入到目标bean中。 ```java public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable { // 获取已经检查的元素。通常,在初始化阶段,所有的元素都会被检查一次。 Collection checkedElements = this.checkedElements; // 如果已经有检查过的元素,则使用它们,否则使用所有注入的元素。 Collection elementsToIterate = (checkedElements != null ? checkedElements : this.injectedElements); // 如果有需要注入的元素... if (!elementsToIterate.isEmpty()) { // 遍历每个元素并注入到目标bean中。 for (InjectedElement element : elementsToIterate) { // 对每个元素(字段或方法)执行注入操作。 element.inject(target, beanName, pvs); } } } ``` 在`org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject`方法中,首先检查字段的值是否已经被缓存。如果已缓存,则从缓存中获取,否则重新解析。然后,它确保字段是可访问的(特别是对于私有字段),并将解析的值设置到目标bean的相应字段中。 ```java @Override protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable { // 步骤1. 获取代表带有@Inject注解的字段的Field对象。 Field field = (Field) this.member; Object value; // 步骤2. 如果字段的值已经被缓存(即先前已解析过),则尝试从缓存中获取。 if (this.cached) { try { // 从缓存中获取已解析的字段值。 value = resolvedCachedArgument(beanName, this.cachedFieldValue); } catch (NoSuchBeanDefinitionException ex) { // 如果缓存中的bean已被意外删除 -> 重新解析。 value = resolveFieldValue(field, bean, beanName); } } else { // 步骤3. 如果字段值未被缓存,直接解析。 value = resolveFieldValue(field, bean, beanName); } // 步骤4. 如果解析到的值不为null... if (value != null) { // 步骤4.1. 使字段可访问,这是必要的,特别是当字段是private时。 ReflectionUtils.makeAccessible(field); // 步骤4.2. 实际将解析的值注入到目标bean的字段中。 field.set(bean, value); } } ``` 首先来到`org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject`方法中的步骤3。在`org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#resolveFieldValue`方法中,通过`beanFactory.resolveDependency`方法从Spring的bean工厂中解析字段的值。 ```java @Nullable private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) { // ... [代码部分省略以简化] Object value; try { // 通过`beanFactory.resolveDependency`方法从Spring的bean工厂中解析字段的值 value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter); } catch (BeansException ex) { throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex); } // ... [代码部分省略以简化] return value; } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency`方法中,首先尝试获取一个延迟解析代理。如果无法获得,它会进一步尝试解析依赖。`doResolveDependency` 是实际进行解析工作的方法。 ```java public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName, @Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException { // ... [代码部分省略以简化] Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary( descriptor, requestingBeanName); if (result == null) { result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter); } return result; } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency`方法中,尝试解析一个特定的依赖,首先查找所有可能的匹配的 bean,然后选择一个最佳匹配的 bean。如果存在多个匹配的 bean 或没有找到匹配的 bean,它会进行相应的处理。 ```java public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName, @Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException { // ... [代码部分省略以简化] try { // 如果存在快捷解决依赖的方法,使用它 Object shortcut = descriptor.resolveShortcut(this); if (shortcut != null) { return shortcut; } // 获取依赖的类型 Class type = descriptor.getDependencyType(); // ... [代码部分省略以简化] // 步骤1. 根据依赖描述符查找匹配的bean Map matchingBeans = findAutowireCandidates(beanName, type, descriptor); // 如果没有找到匹配的bean if (matchingBeans.isEmpty()) { if (isRequired(descriptor)) { // 如果依赖是必需的,抛出异常 raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor); } return null; } String autowiredBeanName; Object instanceCandidate; // 当找到多个匹配的bean if (matchingBeans.size() > 1) { // 确定最佳的自动装配候选者 autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor); if (autowiredBeanName == null) { if (isRequired(descriptor) || !indicatesMultipleBeans(type)) { // 如果不能确定唯一的bean,尝试解析不唯一的依赖 return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans); } else { return null; } } instanceCandidate = matchingBeans.get(autowiredBeanName); } else { // 只找到一个匹配的bean Map.Entry entry = matchingBeans.entrySet().iterator().next(); autowiredBeanName = entry.getKey(); instanceCandidate = entry.getValue(); } // 添加自动装配的bean名到集合 if (autowiredBeanNames != null) { autowiredBeanNames.add(autowiredBeanName); } // 步骤2. 如果候选者是一个类,实例化它 if (instanceCandidate instanceof Class) { instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this); } Object result = instanceCandidate; // ... [代码部分省略以简化] return result; } // ... [代码部分省略以简化] } ``` 我们来到在`org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency`方法中的步骤1。在`org.springframework.beans.factory.support.DefaultListableBeanFactory#findAutowireCandidates`方法中,首先基于给定的类型获取所有可能的bean名。接着,对于每一个可能的候选bean,它检查该bean是否是一个合适的自动注入候选,如果是,它将这个bean添加到结果集中。最后,方法返回找到的所有合适的候选bean。 ```java protected Map findAutowireCandidates( @Nullable String beanName, Class requiredType, DependencyDescriptor descriptor) { // 根据所需的类型,包括所有父工厂中的bean,获取所有可能的bean名 String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this, requiredType, true, descriptor.isEager()); // ... [代码部分省略以简化] // 遍历所有候选bean名 for (String candidate : candidateNames) { // 如果候选bean不是正在查找的bean本身并且它是一个合适的自动注入候选 if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) { // 添加这个候选bean到结果中 addCandidateEntry(result, candidate, descriptor, requiredType); } } // ... [代码部分省略以简化] // 返回找到的所有候选bean return result; } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#addCandidateEntry`方法中,主要获取候选bean的类型,并将其添加到候选bean的集合中。 ```java private void addCandidateEntry(Map candidates, String candidateName, DependencyDescriptor descriptor, Class requiredType) { // ... [代码部分省略以简化] candidates.put(candidateName, getType(candidateName)); } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#getType(name)`方法中,通过bean的名字来获取对应bean的类型。 ```java public Class getType(String name) throws NoSuchBeanDefinitionException { return getType(name, true); } ``` 我们来到在`org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency`方法中的步骤2。在`org.springframework.beans.factory.config.DependencyDescriptor#resolveCandidate`方法中,最后发现`@Inject` 的整个流程最终还是从Spring容器中获取一个bean实例并注入到相应的字段或构造函数参数中。 ```java public Object resolveCandidate(String beanName, Class requiredType, BeanFactory beanFactory) throws BeansException { return beanFactory.getBean(beanName); } ``` 最后我们来到`org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject`方法中的步骤4.2。在 `AutowiredFieldElement#inject` 方法内部,通过`resolveFieldValue(field, bean, beanName)`方法,来确定了正确的bean值并满足某个字段的 `@Inject` 注解,将使用反射来实际设置这个值。具体地说,它会使用 `Field` 类的 `set` 方法来为目标对象的这个字段设置相应的值。这就是 `@Inject` 在字段上使用时如何使得Spring能够自动为这个字段注入值的背后原理。 ```java // 步骤4. 如果解析到的值不为null... if (value != null) { // 步骤4.1. 使字段可访问,这是必要的,特别是当字段是private时。 ReflectionUtils.makeAccessible(field); // 步骤4.2. 实际将解析的值注入到目标bean的字段中。 field.set(bean, value); } ``` ### 八、注意事项 1. **需要依赖**: - 由于 `@Inject` 是 JSR-330 规范的一部分,我们需要在项目中添加 `javax.inject` 依赖。如果不这样做,我们的代码将无法编译。 2. **无 `required` 属性**: - 与 Spring 的 `@Autowired` 不同,`@Inject` 没有 `required` 属性。这意味着如果没有找到匹配的bean,它会默认抛出异常。 3. **与其他注解的组合**: - 为了指定具体的bean或解决多个可选bean之间的歧义,我们可以与 `@Named` 注解结合使用。例如:`@Inject @Named("specificBeanName")`。 4. **不仅限于Spring**: - 尽管 `@Inject` 在 Spring 中得到了很好的支持,但它并不是 Spring 特有的。其他实现了 JSR-330 规范的框架(如 Google Guice)也支持 `@Inject`。 5. **推荐使用构造器注入**: - 尽管我们可以在字段、方法和构造器上使用 `@Inject`,但现代的最佳实践建议使用构造器注入。这确保了bean的不变性和更好的测试性。 6. **循环依赖问题**: - 如果我们在使用字段或方法注入时不小心引入了循环依赖,Spring容器可能会抛出异常。使用构造器注入时,循环依赖会更明显地暴露出来。 7. **不要混合使用**: - 在一个项目中,尽量不要同时使用 `@Inject` 和 `@Autowired`,以保持一致性。选择其中之一并坚持使用。 8. **避免过度使用**: - 依赖注入是一个强大的特性,但也应该谨慎使用。过度使用自动注入,特别是在大型项目中,可能会使代码难以跟踪和维护。 9. **单一职责原则**: - 如果我们发现一个类需要太多的依赖,这可能是违反了单一职责原则的信号。考虑对类进行重构或分解。 10. **与Java EE的兼容性**: - 如果我们的应用程序在 Java EE 容器中运行,那么容器可能已经有了对 `@Inject` 的原生支持,而无需 Spring。 ### 九、总结 #### 最佳实践总结 1. **上下文初始化** - 当我们创建 `AnnotationConfigApplicationContext` 并提供 `MyConfiguration` 类作为参数时,Spring 开始初始化上下文。这意味着它会加载所有的bean定义并准备创建实例。 2. **组件扫描** - 在 `MyConfiguration` 类中,我们使用了 `@ComponentScan` 注解指定了扫描的包路径。这使得Spring扫描指定包和其子包中的所有类,并查找标记为 `@Component`、`@Service`、`@Repository` 和 `@Controller` 等注解的类。找到后,Spring 会自动将这些类注册为bean。 3. **依赖解析** - 在 `MyController` 类中,我们在 `myService` 字段上使用了 `Inject` 注解。这告诉Spring,当创建 `MyController` bean时,需要找到一个 `MyService` 类型的bean,并自动注入到该字段中。 4. **实例化并注入** - 当我们从上下文中请求 `MyController` 类型的bean时,Spring会先创建 `MyController` 的一个实例。但在此之前,它会查看所有带有 `@Inject` 注解的字段,然后为这些字段找到匹配的bean并注入。 - 在我们的例子中,Spring找到了 `MyService` 类型的bean并将其注入到了 `myService` 字段中。 5. **执行业务逻辑** - 在 `showService` 方法被调用时,它简单地打印了 `myService` 字段。由于这个字段已经被成功地自动注入,所以我们看到了预期的输出,证明 `@Inject` 功能正常。 6. **结果** - 最终输出显示了 `myService` 已经被成功地注入到 `MyController` 中,并显示了其实例的内存地址。 #### 源码分析总结 1. **核心后处理器** - `AutowiredAnnotationBeanPostProcessor`是处理`@Inject`等注解的主要后处理器。它实现了两个关键的接口,`MergedBeanDefinitionPostProcessor`和`InstantiationAwareBeanPostProcessor`,这两个接口允许在bean的生命周期中的关键阶段进行干预,为属性注入提供了机制。 2. **收集阶段** + 检索Inject的元数据 - Spring首先使用`postProcessMergedBeanDefinition`方法确保给定的bean定义与其预期的自动装配元数据一致。 - 在该方法中, Spring会尝试查找与给定bean名称和类型相关的`InjectionMetadata`。这可能包括了该bean的字段和方法的注入信息。 + 寻找匹配的Autowiring元数据 - 在`findAutowiringMetadata`中,Spring确保始终为给定的bean名称和类获取最新和相关的`InjectionMetadata`。Spring也利用了缓存机制,以提高性能。 + 构建Autowiring元数据 - 在`buildAutowiringMetadata`方法中,Spring会查找类及其所有父类中的字段和方法,以找出所有带有自动装配注解的字段和方法。 - 然后,为这些字段和方法创建一个统一的`InjectionMetadata`对象。 + 检查注解类型 - 在`AutowiredAnnotationBeanPostProcessor`的构造方法中,主要的目的是告诉这个后处理器它应该处理哪些注解。例如, `@Inject`就是这些注解之一。 3. **注入阶段** + 处理bean属性的后处理 - 在`postProcessProperties`中,Spring用于处理bean属性的后处理,特别是通过`@Inject`进行的属性注入。 - 这涉及到实际将解析得到的值注入到bean中。 + 注入元数据的实际注入操作 - 在`InjectionMetadata#inject`方法中,这里会对bean进行属性的实际注入。 - Spring会遍历每一个需要注入的元素,并执行实际的注入操作。 + 字段的实际注入 - 在`AutowiredFieldElement#inject`中,Spring首先会检查字段的值是否已经被缓存。如果已缓存,则从缓存中获取,否则重新解析。 - 然后,它确保字段是可访问的,并将解析的值设置到目标bean的相应字段中。 + 解析依赖 - 在`doResolveDependency`方法中,Spring开始尝试解析一个特定的依赖。 - 首先,基于给定的类型,Spring会查找所有匹配的bean。 - 如果找到多个匹配的bean,它会尝试确定哪一个是最佳的自动装配候选。 + 获取bean的类型 - 在`addCandidateEntry`方法中,Spring主要获取候选bean的类型,并将其添加到候选bean的集合中。 - 使用`getType`方法,Spring可以通过bean的名字来获取对应bean的类型。 + 从Spring容器中获取bean实例 - 在`resolveCandidate`中,即从Spring容器中获取一个bean实例并注入到相应的字段或构造函数参数中。 + 反射注入 + 通过`field.set(bean, value)`来完成实际字段注入的步骤,将解析出的bean实例(value)注入到目标bean的对应字段上。这是整个`@Inject`流程的最终步骤 ================================================ FILE: spring-jsr/spring-jsr330-inject/pom.xml ================================================ spring-jsr com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-jsr330-inject ================================================ FILE: spring-jsr/spring-jsr330-inject/src/main/java/com/xcs/spring/InjectApplication.java ================================================ package com.xcs.spring; import com.xcs.spring.config.MyConfiguration; import com.xcs.spring.controller.MyController; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年08月07日 16时21分 **/ public class InjectApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MyController controller = context.getBean(MyController.class); controller.showService(); } } ================================================ FILE: spring-jsr/spring-jsr330-inject/src/main/java/com/xcs/spring/config/MyConfiguration.java ================================================ package com.xcs.spring.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan("com.xcs.spring") public class MyConfiguration { } ================================================ FILE: spring-jsr/spring-jsr330-inject/src/main/java/com/xcs/spring/controller/MyController.java ================================================ package com.xcs.spring.controller; import com.xcs.spring.service.MyService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import javax.inject.Inject; @Controller public class MyController { @Inject private MyService myService; public void showService(){ System.out.println("myService = " + myService); } } ================================================ FILE: spring-jsr/spring-jsr330-inject/src/main/java/com/xcs/spring/service/MyService.java ================================================ package com.xcs.spring.service; import org.springframework.stereotype.Service; @Service public class MyService { } ================================================ FILE: spring-jsr/spring-jsr330-named/README.md ================================================ ## @Named - [@Named](#named) - [一、基本信息](#一基本信息) - [二、注解描述](#二注解描述) - [三、注解源码](#三注解源码) - [四、主要功能](#四主要功能) - [五、最佳实践](#五最佳实践) - [六、时序图](#六时序图) - [@Named注册默认过滤器](#named注册默认过滤器) - [@Named扫描组件](#named扫描组件) - [七、源码分析](#七源码分析) - [@Named注册默认过滤器](#named注册默认过滤器-1) - [@Named扫描组件](#named扫描组件-1) - [前置条件](#前置条件) - [扫描入口](#扫描入口) - [@Named依赖注入](#named依赖注入) - [八、注意事项](#八注意事项) - [九、总结](#九总结) - [最佳实践总结](#最佳实践总结) - [源码分析总结](#源码分析总结) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [我的CSDN](https://blog.csdn.net/duzhuang2399/article/details/133945784) 📚 **文章目录** - [所有文章](https://github.com/xuchengsheng/spring-reading) 🔗 **源码地址** - [@Named源码](https://github.com/xuchengsheng/spring-reading/blob/master/spring-jsr/spring-jsr330-named) ### 二、注解描述 `@Named` 注解是 Java 的标准注解,来源于 JSR 330: Dependency Injection for Java。它的主要目的是提供一种标准化的方式在 Java 平台上实现依赖注入。 ### 三、注解源码 `@Named` 是 JSR-330 中定义的一个注解,用于指定依赖注入的资格标识。其主要用途是在存在多个同类型的 bean 实例时,提供一个明确的名称或标识,以消除歧义。该注解具有一个 `value` 属性,允许用户指定 bean 的名称,默认值为空字符串。`@Named` 携带了一个重要的元注解:`@Qualifier` 表明其可以作为资格提供者此注解在 Java 的依赖注入场景中,尤其是解决注入歧义性时,起到了关键作用。 ```java @Qualifier @Documented @Retention(RetentionPolicy.RUNTIME) public @interface Named { String value() default ""; } ``` ### 四、主要功能 1. **Bean标识** + `@Named` 注解可以为一个类提供一个名称,使得这个类可以被识别并管理为一个bean。这样,这个bean就可以在其他地方通过这个名称被引用和注入。 2. **替代默认命名** + 默认情况下,没有具体指定名称的bean会使用其类名的首字母小写形式作为其名称。使用 `@Named`,我们可以`value`属性来覆盖这个默认名称。 3. **解决歧义性** + 在依赖注入中,有时候可能有多个bean都符合某个注入点的要求。在这种情况下,`@Named` 可以与 `@Inject` 注解结合使用,明确指定哪个bean应该被注入。 ### 五、最佳实践 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyController`类型的bean并调用了`showService`方法。 ```java public class NamedApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MyController controller = context.getBean(MyController.class); controller.showService(); } } ``` 在`MyConfiguration`类中,使用了`@ComponentScan("com.xcs.spring")`注解告诉 Spring 在指定的包(在这里是 "`com.xcs.spring`")及其子包中搜索带有 `@Component`、`@Service`、`@Repository` 和 `@Controller` 等注解的类,并将它们自动注册为 beans。这样,spring就不必为每个组件明确写一个 bean 定义。Spring 会自动识别并注册它们。 ```java @Configuration @ComponentScan("com.xcs.spring") public class MyConfiguration { } ``` `MyController` 由 `@Controller` 注解标记。该类依赖于一个类型为 `MyService` 的服务。通过使用 `@Inject` 注解,我们表示希望这个服务被注入,而 `@Named("myServiceB")` 注解则指定了具体要注入的服务实例的名称,即 "`myServiceB`"。`showService()` 方法简单地打印出当前注入的服务实例。 ```java @Controller public class MyController { @Inject @Named("myServiceB") private MyService myService; public void showService(){ System.out.println("myService = " + myService); } } ``` `MyService` 是一个基础类。`MyServiceA` 是 `MyService` 的一个子类,它被 `@Named("myServiceA")` 注解标记,意味着在依赖注入容器中,这个类的实例将被识别为 "myServiceA"。类似地,`MyServiceB` 也是 `MyService` 的一个子类,并被 `@Named("myServiceB")` 注解标记,在依赖注入容器中,这个类的实例将被识别为 "myServiceB"。 ```java public class MyService { } @Named("myServiceA") public class MyServiceA extends MyService{ } @Named("myServiceB") public class MyServiceB extends MyService{ } ``` 运行结果发现,通过`@Named("myServiceB")`注解,成功地注入了一个 `MyServiceB` 类型的实例。 ``` myService = com.xcs.spring.service.MyServiceB@2e8c1c9b ``` ### 六、时序图 #### @Named注册默认过滤器 ~~~mermaid sequenceDiagram Title: @Named注解注册默认过滤器时序图 Note right of NamedApplication: 初始化应用 NamedApplication->>AnnotationConfigApplicationContext: AnnotationConfigApplicationContext(componentClasses) Note right of AnnotationConfigApplicationContext: 初始化Spring上下文 AnnotationConfigApplicationContext->>AnnotationConfigApplicationContext: AnnotationConfigApplicationContext() Note right of AnnotationConfigApplicationContext: 创建Bean定义扫描器 AnnotationConfigApplicationContext->>ClassPathBeanDefinitionScanner: ClassPathBeanDefinitionScanner(registry) Note right of ClassPathBeanDefinitionScanner: 设置是否使用默认过滤器 ClassPathBeanDefinitionScanner->>ClassPathBeanDefinitionScanner: ClassPathBeanDefinitionScanner(registry,useDefaultFilters) Note right of ClassPathBeanDefinitionScanner: 获取或创建环境配置 ClassPathBeanDefinitionScanner->>ClassPathBeanDefinitionScanner: getOrCreateEnvironment(registry) Note right of ClassPathBeanDefinitionScanner: 使用指定环境初始化扫描器 ClassPathBeanDefinitionScanner->>ClassPathBeanDefinitionScanner: ClassPathBeanDefinitionScanner(registry,useDefaultFilters,environment) Note right of ClassPathBeanDefinitionScanner: 最终初始化扫描器 ClassPathBeanDefinitionScanner->>ClassPathBeanDefinitionScanner: ClassPathBeanDefinitionScanner(registry,useDefaultFilters,environment,resourceLoader) Note right of ClassPathBeanDefinitionScanner: 注册默认的注解过滤器 ClassPathBeanDefinitionScanner->>ClassPathScanningCandidateComponentProvider: registerDefaultFilters() Note right of ClassPathScanningCandidateComponentProvider: 加入 @Named 过滤器 ClassPathScanningCandidateComponentProvider->>ClassPathScanningCandidateComponentProvider: this.includeFilters.add("javax.inject.Named"); ~~~ #### @Named扫描组件 ~~~mermaid sequenceDiagram Title: @Named注解扫描组件时序图 ComponentScanAnnotationParser->>ClassPathBeanDefinitionScanner: doScan(basePackages) Note right of ClassPathBeanDefinitionScanner: 查找候选组件 ClassPathBeanDefinitionScanner->>ClassPathScanningCandidateComponentProvider: findCandidateComponents(basePackage) Note right of ClassPathScanningCandidateComponentProvider: 扫描候选组件 ClassPathScanningCandidateComponentProvider->>ClassPathScanningCandidateComponentProvider: scanCandidateComponents(basePackage) Note right of ClassPathScanningCandidateComponentProvider: 判断是否是候选组件 ClassPathScanningCandidateComponentProvider->>ClassPathScanningCandidateComponentProvider: isCandidateComponent(metadataReader) Note right of ClassPathScanningCandidateComponentProvider: 过滤器匹配检查 ClassPathScanningCandidateComponentProvider->>AbstractTypeHierarchyTraversingFilter: match(metadataReader,metadataReaderFactory) Note right of AbstractTypeHierarchyTraversingFilter: 检查注解匹配 AbstractTypeHierarchyTraversingFilter->>AnnotationTypeFilter: matchSelf(metadataReader) Note right of ClassPathScanningCandidateComponentProvider: 返回Bean定义结果 ClassPathScanningCandidateComponentProvider-->>ClassPathBeanDefinitionScanner: 返回BeanDefinition Note right of ClassPathBeanDefinitionScanner: 注册Bean定义 ClassPathBeanDefinitionScanner->>ClassPathBeanDefinitionScanner: registerBeanDefinition(definitionHolder,registry) Note right of ClassPathBeanDefinitionScanner: 辅助工具注册Bean ClassPathBeanDefinitionScanner->>BeanDefinitionReaderUtils: registerBeanDefinition(definitionHolder, registry) Note right of BeanDefinitionReaderUtils: 在Bean工厂中注册Bean BeanDefinitionReaderUtils->>DefaultListableBeanFactory: registerBeanDefinition(beanName,beanDefinition) ~~~ ### 七、源码分析 #### @Named注册默认过滤器 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyController`类型的bean并调用了`showService`方法。 ```java public class NamedApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MyController controller = context.getBean(MyController.class); controller.showService(); } } ``` 在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们本次重点关注`this()`。 ```java public AnnotationConfigApplicationContext(Class... componentClasses) { this(); register(componentClasses); refresh(); ``` 在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext()`方法中,初始化了的两个核心组件,一个用于读取注解定义的bean (`AnnotatedBeanDefinitionReader`),另一个用于扫描类路径并自动检测bean组件 (`ClassPathBeanDefinitionScanner`),也是本次重点分析的内容。 ```java public AnnotationConfigApplicationContext() { StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create"); this.reader = new AnnotatedBeanDefinitionReader(this); createAnnotatedBeanDefReader.end(); this.scanner = new ClassPathBeanDefinitionScanner(this); } ``` 在`org.springframework.context.annotation.ClassPathBeanDefinitionScanner#ClassPathBeanDefinitionScanner(registry)`方法中,又调用了另一个构造函数。 ```java public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) { this(registry, true); } ``` 在`org.springframework.context.annotation.ClassPathBeanDefinitionScanner#ClassPathBeanDefinitionScanner(registry, useDefaultFilters)`方法中,又调用了另一个构造函数。 ```java public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) { this(registry, useDefaultFilters, getOrCreateEnvironment(registry)); } ``` 在`org.springframework.context.annotation.ClassPathBeanDefinitionScanner#ClassPathBeanDefinitionScanner(registry, useDefaultFilters,environment)`方法中,最后,又调用另一个构造函数,使用所有这些参数进行实际的初始化工作。 ```java public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, Environment environment) { this(registry, useDefaultFilters, environment, (registry instanceof ResourceLoader ? (ResourceLoader) registry : null)); } ``` 在`org.springframework.context.annotation.ClassPathBeanDefinitionScanner#ClassPathBeanDefinitionScanner(registry, useDefaultFilters,environment,resourceLoader)`方法中, `useDefaultFilters` 为 `true` 时,除了常见的如 `@Component`, `@Service`, `@Repository` 等注解外,Spring也会自动注册JSR-330规范中的 `@Named` 注解作为一个默认的过滤器。这是Spring为了支持JSR-330注入规范而做的集成。 ```java public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, Environment environment, @Nullable ResourceLoader resourceLoader) { // ... [代码部分省略以简化] if (useDefaultFilters) { registerDefaultFilters(); } // ... [代码部分省略以简化] } ``` 在`org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#registerDefaultFilters`方法中,识别和处理带有 `@Named` 注解的组件,前提是该注解存在于类路径上。如果JSR-330库不在类路径上,Spring会优雅地跳过这一步,而不是导致失败。到此`@Named`注解的注册默认过滤器已经完成,待后续扫描组件过程中,会使用到此过滤器。 ```java protected void registerDefaultFilters() { // ... [代码部分省略以简化] try { this.includeFilters.add(new AnnotationTypeFilter( ((Class) ClassUtils.forName("javax.inject.Named", cl)), false)); logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning"); } catch (ClassNotFoundException ex) { // JSR-330 API not available - simply skip. } } ``` #### @Named扫描组件 ##### 前置条件 在Spring中,为了充分理解`@Named`注解的工作机制,需要深入研究组件扫描和bean定义注册过程。特别地,下述注解和接口为`@Named`的识别、解析和注册提供了基础支持。简而言之,为了完全理解`@Named`如何在Spring中工作,深入了解以下组件是关键的。 1. **`@ComponentScan` 注解** - `@ComponentScan` 注解用于配置类,指定Spring框架在哪些包中查找带有`@Component`, `@Service`, `@Repository`, `@Controller`和`@Named`等注解的类。这些被发现的组件将被自动注册为Spring应用上下文中的beans。特别地,`@Named`注解的组件会因此被扫描并注册。 - 🔗 [@ComponentScan注解传送门](https://github.com/xuchengsheng/spring-reading/blob/master/spring-annotation/spring-annotation-componentScan) 2. **`BeanDefinitionRegistryPostProcessor` 接口** - 其中的`postProcessBeanDefinitionRegistry`方法允许在所有其他bean定义被加载之后、但在任何bean实例化之前,修改或添加bean定义。特定于组件扫描,当解析到`@ComponentScan`注解时,相应的`BeanDefinitionRegistryPostProcessor`实现(如`ConfigurationClassPostProcessor`)会被触发,负责处理组件扫描并注册相应的bean定义。 - 🔗 [BeanDefinitionRegistryPostProcessor接口传送门](https://github.com/xuchengsheng/spring-reading/blob/master/spring-interface/spring-interface-beanDefinitionRegistryPostProcessor) ##### 扫描入口 在`org.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScan`方法中,主要目标是找到指定`basePackages`中所有的组件,并为它们创建 `BeanDefinition`。这些 `BeanDefinition` 之后会被 Spring 容器用来创建实际的 bean 实例。 ```java protected Set doScan(String... basePackages) { // 断言确保至少有一个基础包被指定 Assert.notEmpty(basePackages, "At least one base package must be specified"); // 用于保存找到的bean定义的集合 Set beanDefinitions = new LinkedHashSet<>(); // 遍历每个基础包 for (String basePackage : basePackages) { // 在给定的基础包中找到所有候选的bean定义 Set candidates = findCandidateComponents(basePackage); // 遍历找到的bean定义 for (BeanDefinition candidate : candidates) { // ... [代码部分省略以简化] // 检查给定的bean名字是否已经存在,如果不存在,进行注册 if (checkCandidate(beanName, candidate)) { // ... [代码部分省略以简化] // 在bean注册表中注册bean定义 registerBeanDefinition(definitionHolder, this.registry); } } } // 返回所有注册的bean定义 return beanDefinitions; } ``` 在`org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#findCandidateComponents`方法中,主要提供了两种方式查找组件:通过预先生成的索引(如果可用且支持)或通过传统的扫描方式(我们重点关注传统的扫描方式)。 ```java public Set findCandidateComponents(String basePackage) { // 如果存在组件索引并且支持include过滤器 if (this.componentsIndex != null && indexSupportsIncludeFilters()) { // 从索引中添加候选组件 return addCandidateComponentsFromIndex(this.componentsIndex, basePackage); } else { // 扫描给定基础包中的候选组件 return scanCandidateComponents(basePackage); } } ``` 在`org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#scanCandidateComponents`方法中,首先是构建搜索路径,用于在类路径中搜索指定包,然后是扫描类路径,获取匹配的资源(通常是 `.class` 文件),再然后是对于每个资源,检查是否是候选组件,例如是否有 `@Named` 注解,最后对于是候选组件的类,创建一个 `BeanDefinition` 对象并添加到结果集中。 ```java private Set scanCandidateComponents(String basePackage) { // 用于保存候选的Bean定义 Set candidates = new LinkedHashSet<>(); try { // 构建包搜索路径,例如:"classpath*:com/example/*" String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + '/' + this.resourcePattern; // 使用模式解析器获取所有匹配的资源(即.class文件) Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath); // ... [代码部分省略以简化] for (Resource resource : resources) { // ... [代码部分省略以简化] // 检查资源是否可读 if (resource.isReadable()) { try { // 使用元数据读取器获取类的元数据 MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource); // 检查类是否是候选组件(例如,是否带有@Named注释) if (isCandidateComponent(metadataReader)) { ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); sbd.setSource(resource); // 再次检查Bean定义是否是候选组件 if (isCandidateComponent(sbd)) { // ... [代码部分省略以简化] candidates.add(sbd); } else { // ... [代码部分省略以简化] } } else { // ... [代码部分省略以简化] } } catch (Throwable ex) { // ... [代码部分省略以简化] } } else { // ... [代码部分省略以简化] } } } catch (IOException ex) { // ... [代码部分省略以简化] } return candidates; } ``` 在`org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#isCandidateComponent`方法中,是扫描过程中的一个关键步骤,用于确定一个类是否应该被视为一个Spring组件的候选者。如果一个类带有 `@Named` 注解,那么对应的 `AnnotationTypeFilter` 将会识别它,并导致 `isCandidateComponent` 返回 `true`,表示这个类是一个合格的组件候选者。 ```java protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException { // ... [代码部分省略以简化] // 遍历所有的包含过滤器 for (TypeFilter tf : this.includeFilters) { // 如果当前类与任一包含过滤器匹配 if (tf.match(metadataReader, getMetadataReaderFactory())) { // 判断该组件是否满足特定的条件 return isConditionMatch(metadataReader); } } // 默认返回false,说明不是候选组件 return false; } ``` 在`org.springframework.core.type.filter.AbstractTypeHierarchyTraversingFilter#match(metadataReader, metadataReaderFactory)`方法中,作用是确定给定的`metadataReader`是否与某些特定条件匹配。在上下文中,这个方法通常用于检查类或其元数据是否符合某些特定的条件,例如检查一个类是否有`@Named`注解。 ```java @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { // This method optimizes avoiding unnecessary creation of ClassReaders // as well as visiting over those readers. if (matchSelf(metadataReader)) { return true; } // ... [代码部分省略以简化] return false; } ``` 在`org.springframework.core.type.filter.AnnotationTypeFilter#matchSelf`方法中,当我们在Spring框架中使用组件扫描时,我们的目标之一是自动发现并注册带有特定注解的类。其中,`@Named`注解是我们关心的一个,因为它标记了一个类,指示Spring应将其视为一个bean,并将其添加到Spring的应用上下文中。 **场景带入:** + 首先从`metadataReader`获取类的注解元数据。 + 接着,它检查`MyServiceA`类是否直接带有`@Named`注解。这是通过`metadata.hasAnnotation(this.annotationType.getName())`完成的,其中`this.annotationType.getName()`会返回`javax.inject.Named`(代表`@Named`的完全限定类名)。 + 如果`considerMetaAnnotations`是`true`,它还会检查`MyServiceA`类是否带有任何元注解,这些元注解自己可能被`@Named`标记。 + 在这种情况下,因为`MyServiceA`直接带有`@Named`注解,所以`matchSelf`会返回`true`。这意味着`MyServiceA`类满足过滤条件,并且应该被注册为Spring的一个bean。 ```java @Override protected boolean matchSelf(MetadataReader metadataReader) { AnnotationMetadata metadata = metadataReader.getAnnotationMetadata(); return metadata.hasAnnotation(this.annotationType.getName()) || (this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName())); } ``` #### @Named依赖注入 在Spring中,`@Named`注解既是用于标记一个类作为Spring的bean,同时也在依赖注入中作为一个限定符来解决歧义性。这里,`@Named`的功能与`@Qualifier`非常相似,实际上,`@Named`注解在Spring中也是作为一个特殊类型的`@Qualifier`来实现的。 1. **`@Qualifier`注解**: - 在Spring中,`@Qualifier`注解用于指定在多个相同类型的bean之间进行选择时所需的bean的名称。当与`@Named`或`@Inject`结合使用时,它帮助Spring容器解析正确的依赖关系。 - 🔗 [Qualifier注解传送门](https://github.com/xuchengsheng/spring-reading/tree/master/spring-annotation/spring-annotation-qualifier) ### 八、注意事项 1. **与@Component的区别** + 尽管`@Named`和`@Component`在功能上相似(都可以用于标识和注册beans),但它们来自不同的规范。`@Component`是Spring特有的,而`@Named`来自JSR-330。 2. **与@Qualifier的结合** + `@Named`可以与`@Inject`注解一起使用,作为`@Qualifier`的替代,以指定应该注入哪个具体的bean。 3. **不要混淆注解来源** + 由于Spring同时支持其自己的依赖注入注解和JSR-330,可能会在同一个项目中混合使用`@Autowired`和`@Inject`,这可能导致混淆。为了代码的一致性,最好在一个项目中坚持使用其中一种。 4. **类路径依赖** + 要使用`@Named`和其他JSR-330注解,需要确保`javax.inject`库在类路径上。如果没有添加此依赖,使用这些注解会导致类找不到错误。 5. **与JSR-330的其他注解的集成** + 当使用`@Named`时,我们可能还希望考虑使用JSR-330的其他注解,如`@Inject`,以确保一致性。 6. **避免名称冲突** + 当使用`@Named`为bean指定一个名称时,应确保在Spring上下文中没有其他bean使用相同的名称。否则,可能会出现不可预测的行为或错误。 7. **作用域** + `@Named`默认的作用域是singleton。但如果我们需要不同的作用域,例如prototype,我们需要结合使用`@Scope`注解。 ### 九、总结 #### 最佳实践总结 1. **启动程序** + 在 `NamedApplication` 中,我们初始化了 `AnnotationConfigApplicationContext` 上下文,指定使用 `MyConfiguration` 作为配置类。然后,从该上下文中获取 `MyController` 的 bean 实例,并调用其 `showService` 方法。 2. **配置类** + `MyConfiguration` 类使用 `@ComponentScan` 注解,指示Spring在 "`com.xcs.spring`" 包及其子包中搜索被Stereotype注解标记的组件。因此,所有带有 `@Component`、`@Service`、`@Repository` 和 `@Controller` 等注解的类都会被自动识别并注册为beans。 3. **控制器** + `MyController` 是一个通过 `@Controller` 注解标记的控制器类,它有一个 `MyService` 类型的依赖。我们使用 `@Inject` 注解要求这个依赖被注入,并通过 `@Named("myServiceB")` 注解明确指定我们想要注入哪个具体的bean实例。 4. **服务类** + 有三个与服务相关的类:基类 `MyService` 以及其两个子类 `MyServiceA` 和 `MyServiceB`。这两个子类都通过 `@Named` 注解被标记,这为它们在依赖注入容器中提供了明确的标识名称。 5. **运行结果** + 当程序执行时,通过 `@Named("myServiceB")` 的指示,成功地注入了一个 `MyServiceB` 类型的实例到 `MyController`。因此,当调用 `showService` 方法时,输出证明了 `MyServiceB` 的实例已经被正确注入。 #### 源码分析总结 1. **初始化上下文** + 启动类 `NamedApplication` 利用 `AnnotationConfigApplicationContext` 为上下文环境,注册了配置类 `MyConfiguration`,并从该上下文获取 `MyController` 类型的bean。 2. **注解配置类扫描** + Spring的 `AnnotationConfigApplicationContext` 在构造时初始化了两个关键组件:一个用于读取注解定义的bean (`AnnotatedBeanDefinitionReader`) 和另一个用于扫描类路径并自动发现bean组件 (`ClassPathBeanDefinitionScanner`)。 3. **`@Named` 注解的注册** + 在构造 `ClassPathBeanDefinitionScanner` 时,如果 `useDefaultFilters` 为 `true`,Spring会为 `@Named` 注解添加默认过滤器,从而使其在类路径扫描时被识别。这是Spring对JSR-330规范的集成,使得我们可以使用 `@Named` 作为组件标识。 4. **`@Named`扫描组件** + 当运行应用时,Spring会根据 `@ComponentScan` 的指示扫描指定包及其子包中的组件。在这个扫描过程中,它会检查每个类,看它们是否带有特定的注解,如 `@Named`。 + 在组件扫描过程中,`isCandidateComponent` 方法用于检查一个类是否应被视为一个Spring组件的候选者。它会通过应用已注册的过滤器(其中之一就是对 `@Named` 注解的过滤器)来决定。 5. **`@Named`依赖注入** + 在Spring中,`@Named` 注解的另一个关键用途是作为限定符用于依赖注入。当存在多个同类型的bean,而我们需要指定注入哪一个时,`@Named` 可以帮助解决歧义。在这方面,它的功能与 `@Qualifier` 注解类似。 ================================================ FILE: spring-jsr/spring-jsr330-named/pom.xml ================================================ spring-jsr com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-jsr330-named ================================================ FILE: spring-jsr/spring-jsr330-named/src/main/java/com/xcs/spring/NamedApplication.java ================================================ package com.xcs.spring; import com.xcs.spring.config.MyConfiguration; import com.xcs.spring.controller.MyController; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年08月07日 16时21分 **/ public class NamedApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MyController controller = context.getBean(MyController.class); controller.showService(); } } ================================================ FILE: spring-jsr/spring-jsr330-named/src/main/java/com/xcs/spring/config/MyConfiguration.java ================================================ package com.xcs.spring.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan("com.xcs.spring") public class MyConfiguration { } ================================================ FILE: spring-jsr/spring-jsr330-named/src/main/java/com/xcs/spring/controller/MyController.java ================================================ package com.xcs.spring.controller; import com.xcs.spring.service.MyService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import javax.inject.Inject; import javax.inject.Named; @Controller public class MyController { @Inject @Named("myServiceB") private MyService myService; public void showService(){ System.out.println("myService = " + myService); } } ================================================ FILE: spring-jsr/spring-jsr330-named/src/main/java/com/xcs/spring/service/MyService.java ================================================ package com.xcs.spring.service; /** * @author xcs * @date 2023年10月19日 16时44分 **/ public class MyService { } ================================================ FILE: spring-jsr/spring-jsr330-named/src/main/java/com/xcs/spring/service/MyServiceA.java ================================================ package com.xcs.spring.service; import javax.inject.Named; /** * @author xcs * @date 2023年10月19日 16时43分 **/ @Named("myServiceA") public class MyServiceA extends MyService{ } ================================================ FILE: spring-jsr/spring-jsr330-named/src/main/java/com/xcs/spring/service/MyServiceB.java ================================================ package com.xcs.spring.service; import javax.inject.Named; /** * @author xcs * @date 2023年10月19日 16时43分 **/ @Named("myServiceB") public class MyServiceB extends MyService{ } ================================================ FILE: spring-jsr/spring-jsr330-provider/README.md ================================================ ## javax.inject.Provider - [javax.inject.Provider](#javaxinjectprovider) - [一、基本信息](#一基本信息) - [二、接口描述](#二接口描述) - [三、接口源码](#三接口源码) - [四、主要功能](#四主要功能) - [五、最佳实践](#五最佳实践) - [六、时序图](#六时序图) - [Provider注册](#provider注册) - [Provider实例化](#provider实例化) - [Provider泛型提取](#provider泛型提取) - [七、源码分析](#七源码分析) - [Provider注册](#provider注册-1) - [Provider实例化](#provider实例化-1) - [Provider泛型提取](#provider泛型提取-1) - [八、注意事项](#八注意事项) - [九、总结](#九总结) - [最佳实践总结](#最佳实践总结) - [源码分析总结](#源码分析总结) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [我的CSDN]() 📚 **文章目录** - [所有文章](https://github.com/xuchengsheng/spring-reading) 🔗 **源码地址** - [Provider源码](https://github.com/xuchengsheng/spring-reading/tree/master/spring-jsr/spring-jsr330-provider) ### 二、接口描述 `javax.inject.Provider` 是一个在 JSR-330 "Dependency Injection for Java" 规范中定义的接口,它用于延迟地获取注入的依赖项。Spring 框架支持 JSR-330,并因此也支持 `javax.inject.Provider` 接口。 ### 三、接口源码 `Provider` 接口的目的主要是为了允许实时、按需的创建和提供对象。 ```java /** * 代表一个特定类型T的实例提供者。 * 它是 JSR-330 "Java的依赖注入" 规范的一部分。 * 该接口允许实现每次调用 get 方法时产生新的实例(类似于工厂方法), * 或者为多次调用返回相同的实例。 * * 它在以下情况尤为有用: * - 懒加载或按需创建对象。 * - 检索多个原型作用域 bean 的实例。 * - 在某些情况下解决循环依赖。 */ public interface Provider { /** * 提供 T 的一个实例。根据实现情况, * 每次调用此方法时都可能创建一个新的实例, * 或者为多次调用返回相同的实例。 * * @return T 的一个实例 * @throws RuntimeException 如果在提供实例时出现错误。 * 例如,在 T 的注入或构造过程中出现错误。 * 建议调用者不要捕获这些异常,因为行为可能因不同的注入器实现或配置而异。 */ T get(); } ``` ### 四、主要功能 1. **按需创建和提供实例** + 当我们请求一个对象的实例时,`Provider` 可以为我们提供一个实例,而不需要直接实例化或查找对象。这为按需创建对象提供了一种机制。 2. **处理原型作用域的 beans** + 在Spring中,如果我们有一个原型作用域的bean,使用 `Provider` 可以确保每次调用 `get()` 方法时都获得一个新的bean实例。 3. **解决循环依赖** + 在某些情况下,特别是当涉及到原型作用域的bean时,使用 `Provider` 可以帮助解决因直接注入而产生的循环依赖问题。 4. **提供更多的灵活性** + 与直接注入bean相比,使用 `Provider` 可以让我们决定何时以及如何获取bean实例。这可以为那些需要在运行时基于特定条件决定是否创建或获取bean的应用程序提供更大的灵活性。 ### 五、最佳实践 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyController`类型的bean并调用了`showService`方法, ```java public class ProviderApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MyController controller = context.getBean(MyController.class); controller.showService(); } } ``` 在`MyConfiguration`类中,使用了`@ComponentScan("com.xcs.spring")`注解告诉 Spring 在指定的包(在这里是 "`com.xcs.spring`")及其子包中搜索带有 `@Component`、`@Service`、`@Repository` 和 `@Controller` 等注解的类,并将它们自动注册为 beans。这样,spring就不必为每个组件明确写一个 bean 定义。Spring 会自动识别并注册它们。 ```java @Configuration @ComponentScan("com.xcs.spring") public class MyConfiguration { } ``` `MyController`,它使用`@Autowired`注入了一个`Provider`。然后,`MyController`类的`showService`方法中使用`myServiceProvider`来获取`MyService`的两个实例。 ```java @Controller public class MyController { @Autowired private Provider myServiceProvider; public void showService(){ System.out.println("myServiceProvider1 = " + myServiceProvider.get()); System.out.println("myServiceProvider2 = " + myServiceProvider.get()); } } ``` `MyService` 是一个简单的服务类,但我们没有定义任何方法或功能。 ```java @Service public class MyService { } ``` 运行结果发现,`myServiceProvider1` 和 `myServiceProvider2` 两次获取到的 `MyService` 实例具有相同的对象引用(`@235ecd9f`)。这说明 `MyService` bean 是单例作用域的。 | :warning: 注意! | | :----------------------------------------------------------- | | 在 Spring 中,默认的作用域是单例(singleton),这意味着在整个 Spring 容器中,一个特定的 bean 定义只有一个实例。因此,无论我们调用多少次 `myServiceProvider.get()`,它都会返回相同的 `MyService` 实例。如果我们想每次都获得一个新的 `MyService` 实例,我们需要将 `MyService` 定义为原型作用域(prototype)。 | ```java myServiceProvider1 = com.xcs.spring.service.MyService@235ecd9f myServiceProvider2 = com.xcs.spring.service.MyService@235ecd9f ``` ### 六、时序图 #### Provider注册 ~~~mermaid sequenceDiagram Title: Provider注册时序图 NamedApplication->>AnnotationConfigApplicationContext: AnnotationConfigApplicationContext(componentClasses) Note right of AnnotationConfigApplicationContext: 创建一个基于注解的应用上下文 AnnotationConfigApplicationContext->>AnnotationConfigApplicationContext: AnnotationConfigApplicationContext() Note right of AnnotationConfigApplicationContext: AnnotationConfigApplicationContext 构造器 AnnotationConfigApplicationContext->>GenericApplicationContext:GenericApplicationContext() Note right of GenericApplicationContext: 基于GenericApplicationContext的构造 GenericApplicationContext->>DefaultListableBeanFactory: new DefaultListableBeanFactory() Note right of DefaultListableBeanFactory: 创建一个默认的可列出的bean工厂 DefaultListableBeanFactory->>DefaultListableBeanFactory: ClassUtils.forName("javax.inject.Provider",classLoader) Note right of DefaultListableBeanFactory: 执行DefaultListableBeanFactory内的静态代码块 ~~~ #### Provider实例化 ~~~mermaid sequenceDiagram Title: Provider实例化时序图 AbstractAutowireCapableBeanFactory->>AutowiredAnnotationBeanPostProcessor: postProcessProperties(pvs,bean,beanName) Note right of AutowiredAnnotationBeanPostProcessor: 处理bean属性的注入 AutowiredAnnotationBeanPostProcessor->>InjectionMetadata: inject(target,beanName,pvs) Note right of InjectionMetadata: 对目标bean执行依赖注入 InjectionMetadata->>AutowiredFieldElement: inject(bean,beanName,pvs) Note right of AutowiredFieldElement: 对具体的字段执行依赖注入 AutowiredFieldElement->>AutowiredFieldElement: resolveFieldValue(field,bean,beanName) Note right of AutowiredFieldElement: 解析字段值以进行注入 AutowiredFieldElement->>DefaultListableBeanFactory: resolveDependency(desc, beanName, autowiredBeanNames, typeConverter) Note right of DefaultListableBeanFactory: 解析指定的依赖关系 DefaultListableBeanFactory->>Jsr330Factory: new Jsr330Factory() Note right of Jsr330Factory: 创建一个新的JSR-330工厂 Jsr330Factory->>DefaultListableBeanFactory: 返回Factory Note right of DefaultListableBeanFactory: 获取JSR-330工厂实例 DefaultListableBeanFactory->>Jsr330Factory: createDependencyProvider(descriptor,beanName) Note right of Jsr330Factory: 创建依赖提供者 Jsr330Factory->>Jsr330Provider: new Jsr330Provider(descriptor, beanName) Note right of Jsr330Provider: 创建一个新的JSR-330提供者 Jsr330Provider->>Jsr330Factory: 返回Provider Note right of Jsr330Factory: 获取JSR-330提供者实例 Jsr330Factory->>DefaultListableBeanFactory: 返回Provider Note right of DefaultListableBeanFactory: 获取JSR-330提供者 DefaultListableBeanFactory->>AutowiredFieldElement: 返回Provider Note right of AutowiredFieldElement: AutowiredFieldElement->>Field: field.set(bean, Provider) Note right of Field: 使用Provider设置字段值 ~~~ #### Provider泛型提取 ~~~mermaid sequenceDiagram Title: Provider泛型提取时序图 MyController->>Jsr330Provider:get() Note over Jsr330Provider: MyController尝试获取Bean。 Jsr330Provider->>DependencyObjectProvider:getValue() Note over DependencyObjectProvider: Jsr330Provider委托给DependencyObjectProvider来实际获取值。 DependencyObjectProvider->>DefaultListableBeanFactory:doResolveDependency(descriptor,beanName,autowiredBeanNames,typeConverter) Note over DefaultListableBeanFactory: DependencyObjectProvider将实际的依赖解析任务交给DefaultListableBeanFactory。 DefaultListableBeanFactory->>DependencyObjectProvider:返回Bean Note over DependencyObjectProvider: DefaultListableBeanFactory完成解析并返回相关Bean。 DependencyObjectProvider->>Jsr330Provider:返回Bean Note over Jsr330Provider: DependencyObjectProvider将Bean返回给Jsr330Provider。 Jsr330Provider->>MyController:返回Bean Note over MyController: Jsr330Provider将Bean返回给MyController。 ~~~ ### 七、源码分析 #### Provider注册 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyController`类型的bean并调用了`showService`方法, ```java public class ProviderApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MyController controller = context.getBean(MyController.class); controller.showService(); } } ``` 在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们本次重点关注`this()`。 ```java public AnnotationConfigApplicationContext(Class... componentClasses) { this(); register(componentClasses); refresh(); } ``` 在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext()`方法中,初始化了的两个核心组件,一个用于读取注解定义的bean (`AnnotatedBeanDefinitionReader`),另一个用于扫描类路径并自动检测bean组件 (`ClassPathBeanDefinitionScanner`),也是本次重点分析的内容。 ```java public AnnotationConfigApplicationContext() { StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create"); this.reader = new AnnotatedBeanDefinitionReader(this); createAnnotatedBeanDefReader.end(); this.scanner = new ClassPathBeanDefinitionScanner(this); } ``` 在`org.springframework.context.support.GenericApplicationContext#GenericApplicationContext()`方法中,创建了一个新的`DefaultListableBeanFactory`实例并赋值给`beanFactory`属性 ```java public GenericApplicationContext() { this.beanFactory = new DefaultListableBeanFactory(); } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory`静态代码块中,尝试加载JSR-330(即`javax.inject` API)中定义的`Provider`接口。这个接口是Java的依赖注入规范中的一部分。如果该接口可用,它将被赋给`javaxInjectProviderClass`变量;否则,如果接口不可用(例如,运行环境中没有提供相关的库),`javaxInjectProviderClass`将被设置为`null`。 ```java static { try { javaxInjectProviderClass = ClassUtils.forName("javax.inject.Provider", DefaultListableBeanFactory.class.getClassLoader()); } catch (ClassNotFoundException ex) { // JSR-330 API not available - Provider interface simply not supported then. javaxInjectProviderClass = null; } } ``` #### Provider实例化 在`org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessProperties`方法中,用于处理bean属性的后处理,特别是通过`@Autowired`等注解进行的属性注入。 ```java @Override public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) { // 获取与bean名称和类相关的InjectionMetadata。 // 这包括该bean需要进行注入的所有字段和方法。 InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs); try { // 使用获取到的InjectionMetadata,实际进行属性的注入。 metadata.inject(bean, beanName, pvs); } // 如果在注入过程中出现BeanCreationException,直接抛出。 catch (BeanCreationException ex) { throw ex; } // 捕获其他异常,并以BeanCreationException的形式抛出,提供详细的错误信息。 catch (Throwable ex) { throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex); } // 返回原始的PropertyValues,因为这个方法主要关注依赖注入而不是修改属性。 return pvs; } ``` 在`org.springframework.beans.factory.annotation.InjectionMetadata#inject`方法中,主要目的是将所有需要注入的元素(例如带有`@Autowired`等注解的字段或方法)注入到目标bean中。 ```java public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable { // 获取已经检查的元素。通常,在初始化阶段,所有的元素都会被检查一次。 Collection checkedElements = this.checkedElements; // 如果已经有检查过的元素,则使用它们,否则使用所有注入的元素。 Collection elementsToIterate = (checkedElements != null ? checkedElements : this.injectedElements); // 如果有需要注入的元素... if (!elementsToIterate.isEmpty()) { // 遍历每个元素并注入到目标bean中。 for (InjectedElement element : elementsToIterate) { // 对每个元素(字段或方法)执行注入操作。 element.inject(target, beanName, pvs); } } } ``` 在`org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject`方法中,首先检查字段的值是否已经被缓存。如果已缓存,则从缓存中获取,否则重新解析。然后,它确保字段是可访问的(特别是对于私有字段),并将解析的值设置到目标bean的相应字段中。 ```java @Override protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable { // 获取代表带有@Autowired注解的字段的Field对象。 Field field = (Field) this.member; Object value; // 如果字段的值已经被缓存(即先前已解析过),则尝试从缓存中获取。 if (this.cached) { try { // 从缓存中获取已解析的字段值。 value = resolvedCachedArgument(beanName, this.cachedFieldValue); } catch (NoSuchBeanDefinitionException ex) { // 如果缓存中的bean已被意外删除 -> 重新解析。 value = resolveFieldValue(field, bean, beanName); } } else { // 如果字段值未被缓存,直接解析。 value = resolveFieldValue(field, bean, beanName); } // 如果解析到的值不为null... if (value != null) { // 使字段可访问,这是必要的,特别是当字段是private时。 ReflectionUtils.makeAccessible(field); // 实际将解析的值注入到目标bean的字段中。 field.set(bean, value); } } ``` 在`org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#resolveFieldValue`方法中,通过`beanFactory.resolveDependency`方法从Spring的bean工厂中解析字段的值。 ```java @Nullable private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) { // ... [代码部分省略以简化] Object value; try { // 通过`beanFactory.resolveDependency`方法从Spring的bean工厂中解析字段的值 value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter); } catch (BeansException ex) { throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex); } // ... [代码部分省略以简化] return value; } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency`方法中,如果依赖类型确实是`javax.inject.Provider`,则创建一个`Jsr330Factory`实例并调用其`createDependencyProvider`方法。这将创建并返回一个满足JSR-330规范的`Provider`实例。 ```java @Override @Nullable public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName, @Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException { // ... [代码部分省略以简化] if (javaxInjectProviderClass == descriptor.getDependencyType()) { return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName); } // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory.Jsr330Factory#createDependencyProvider`方法中,创建并返回了一个 `Jsr330Provider` 的新实例。这个 `Jsr330Provider` 类是一个适配器或代理,用于实现 JSR-330 的 `Provider` 接口,从而允许 Spring 框架与其他遵循 JSR-330 规范的依赖注入框架互操作。 ```java public Object createDependencyProvider(DependencyDescriptor descriptor, @Nullable String beanName) { return new Jsr330Provider(descriptor, beanName); } ``` #### Provider泛型提取 `MyController`,它使用`@Autowired`注入了一个`Provider`。然后,`MyController`类的`showService`方法中使用`myServiceProvider`来获取`MyService`的两个实例。 ```java @Controller public class MyController { @Autowired private Provider myServiceProvider; public void showService(){ System.out.println("myServiceProvider1 = " + myServiceProvider.get()); System.out.println("myServiceProvider2 = " + myServiceProvider.get()); } } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory.Jsr330Factory.Jsr330Provider#get`方法中,简单地调用了 `getValue()` 方法来获取并返回一个对象。 ```java @Override @Nullable public Object get() throws BeansException { return getValue(); } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory.DependencyObjectProvider#getValue`方法中,直接解析和返回该依赖。 ```java @Nullable protected Object getValue() throws BeansException { if (this.optional) { return createOptionalDependency(this.descriptor, this.beanName); } else { return doResolveDependency(this.descriptor, this.beanName, null, null); } } ``` ### 八、注意事项 1. **依赖检查** + 与直接注入Bean不同,使用 `Provider` 注入的Bean在启动时不会进行立即检查。如果我们希望容器启动时进行依赖检查,我们应该避免使用 `Provider`。 2. **性能** + 每次调用 `Provider.get()` 时,都可能会触发一个新的Bean的创建(如果该Bean的scope是prototype的话)。所以,频繁地调用 `Provider.get()` 可能会有性能问题。 3. **原型作用域** + 如果我们使用 `Provider` 来注入原型作用域的Bean,那么每次调用 `Provider.get()` 都会返回一个新的实例。需要确保这是我们所期望的行为。 4. **泛型类型** + 当使用 `Provider` 时,必须为其提供泛型类型以指示所期望注入的Bean的类型。 5. **与其他注解的组合** + 虽然 `Provider` 可以与其他Spring注解(如 `@Qualifier`)组合使用,但必须确保这些组合在语义上是有意义的。 6. **错误处理** + `Provider.get()` 方法可能会抛出 `BeansException`。在使用它时,我们应该准备处理这些异常。 7. **与JSR-330的兼容性** + 尽管 Spring 提供了对 `Provider` 的支持,但如果我们正在使用其他支持JSR-330的容器,确保 `Provider` 的行为在这些容器中是一致的。 8. **循环依赖** + 与直接Bean注入相比,使用 `Provider` 可能会更容易解决某些循环依赖的问题,但仍然要注意避免引入不必要的复杂性。 ### 九、总结 #### 最佳实践总结 1. **启动类与上下文配置** + 利用 `AnnotationConfigApplicationContext` 为 Spring 提供了基于 Java 注解的配置方式。在这里,我们使用 `MyConfiguration` 类作为配置类来初始化 Spring 上下文。 2. **组件扫描** + 通过在 `MyConfiguration` 配置类上使用 `@ComponentScan` 注解,我们指示 Spring 自动扫描指定包及其子包中的组件(如 `@Component`、`@Service`、`@Repository` 和 `@Controller` 注解的类),并将它们注册为 beans,从而减少了明确为每个组件定义 bean 的需要。 3. **服务注入** + 在 `MyController` 类中,我们使用 `@Autowired` 注解来自动注入 `Provider` 类型的 bean。通过这种方式,我们可以轻松地从 Provider 中获取 `MyService` 实例。 4. **服务使用** + 在 `MyController` 的 `showService` 方法中,我们两次调用 `myServiceProvider.get()` 以演示如何从 Provider 获取 `MyService` 的实例。 5. **单例作用域** + 通过运行结果,我们可以观察到两次获取的 `MyService` 实例是相同的,这说明 `MyService` bean 是单例作用域的。在 Spring 中,默认的 bean 作用域是单例,这意味着对于一个特定的 bean 定义,在整个 Spring 容器中只有一个实例。 6. **注意点** + 尽管 Spring 的默认作用域是单例,但在某些场景下,我们可能希望每次请求都返回一个新的 bean 实例。在这种情况下,我们可以考虑将 bean 定义为原型作用域。 #### 源码分析总结 1. **初始化与上下文配置** - 启动时,我们使用`AnnotationConfigApplicationContext`为Spring创建了基于Java注解的配置环境,并使用`MyConfiguration`组件类作为构造参数。 - 在其构造函数中,通过`this()`初始化了两个核心组件:一个用于读取注解定义的bean (`AnnotatedBeanDefinitionReader`) 和一个用于扫描类路径并自动检测bean组件 (`ClassPathBeanDefinitionScanner`)。 2. **JSR-330 `Provider`的加载** - 在`DefaultListableBeanFactory`的静态代码块中,尝试加载了JSR-330规范的`Provider`接口。 - 如果`Provider`接口可用,它就会被赋给一个特定的类变量。 3. **处理`@Autowired`注解** - 通过`AutowiredAnnotationBeanPostProcessor`处理bean属性,尤其是通过`@Autowired`进行的属性注入。 - 在处理过程中,会解析字段的值并进行注入,其中涉及到`Provider`的使用和处理。 4. **解析`Provider`的依赖** - 当遇到一个字段或属性的类型为`Provider`时,Spring会在`DefaultListableBeanFactory`中特殊处理它。 - 对于JSR-330的`Provider`类型的依赖,Spring会创建一个特定的`Provider`实例来满足这个依赖,这是通过`Jsr330Factory`和其内部类`Jsr330Provider`实现的。 5. **获取`Provider`的值** - 在`Jsr330Provider`中的`get`方法被调用时,会进一步调用`getValue`方法。 - `getValue`方法负责实际的依赖解析,返回所需的bean实例。 ================================================ FILE: spring-jsr/spring-jsr330-provider/pom.xml ================================================ spring-jsr com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-jsr330-provider ================================================ FILE: spring-jsr/spring-jsr330-provider/src/main/java/com/xcs/spring/ProviderApplication.java ================================================ package com.xcs.spring; import com.xcs.spring.config.MyConfiguration; import com.xcs.spring.controller.MyController; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年08月07日 16时21分 **/ public class ProviderApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MyController controller = context.getBean(MyController.class); controller.showService(); } } ================================================ FILE: spring-jsr/spring-jsr330-provider/src/main/java/com/xcs/spring/config/MyConfiguration.java ================================================ package com.xcs.spring.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan("com.xcs.spring") public class MyConfiguration { } ================================================ FILE: spring-jsr/spring-jsr330-provider/src/main/java/com/xcs/spring/controller/MyController.java ================================================ package com.xcs.spring.controller; import com.xcs.spring.service.MyService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import javax.inject.Inject; import javax.inject.Named; import javax.inject.Provider; @Controller public class MyController { @Autowired private Provider myServiceProvider; public void showService(){ System.out.println("myServiceProvider1 = " + myServiceProvider.get()); System.out.println("myServiceProvider2 = " + myServiceProvider.get()); } } ================================================ FILE: spring-jsr/spring-jsr330-provider/src/main/java/com/xcs/spring/service/MyService.java ================================================ package com.xcs.spring.service; import org.springframework.stereotype.Service; /** * @author xcs * @date 2023年10月19日 16时44分 **/ @Service public class MyService { } ================================================ FILE: spring-jsr/spring-jsr330-qualifier/README.md ================================================ ## @Qualifier - [@Qualifier](#qualifier) - [一、基本信息](#一基本信息) - [二、注解描述](#二注解描述) - [三、注解源码](#三注解源码) - [四、主要功能](#四主要功能) - [五、最佳实践](#五最佳实践) - [六、时序图](#六时序图) - [@Qualifier注册](#qualifier注册) - [@Qualifier解析](#qualifier解析) - [七、源码分析](#七源码分析) - [@Qualifier注册](#qualifier注册-1) - [@Qualifier解析](#qualifier解析-1) - [前置条件](#前置条件) - [解析入口](#解析入口) - [八、注意事项](#八注意事项) - [九、总结](#九总结) - [最佳实践总结](#最佳实践总结) - [源码分析总结](#源码分析总结) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [我的CSDN]() 📚 **文章目录** - [所有文章](https://github.com/xuchengsheng/spring-reading) 🔗 **源码地址** - [@Qualifier源码](https://github.com/xuchengsheng/spring-reading/tree/master/spring-jsr/spring-jsr330-qualifier) ### 二、注解描述 `@Qualifier` 注解是 Java 的标准注解,来源于 JSR 330: Dependency Injection for Java。主要用于解决依赖注入中的歧义性,当有多个同类型的 bean 或实例可供选择时,指导容器明确选择哪一个进行注入。 ### 三、注解源码 `@Qualifier` 表示它是一个基础注解,主要用于创建其他的自定义注解,这些新的注解通常用于限定和解决依赖注入时的歧义性。这与 Spring 和 JSR 330 中 `@Qualifier` 的定义和目的是一致的。 ```java @Target({ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Qualifier { } ``` ### 四、主要功能 1. **解决歧义性** + 当容器中存在多个同一类型的 bean 时,`@Qualifier` 用于指定哪一个具体的 bean 应该被注入。 2. **与 `@Autowired` 和 `@Inject` 配合使用** - 在 Spring 中,`org.springframework.beans.factory.annotation.Qualifier` 通常与 `org.springframework.beans.factory.annotation.Autowired` 配合使用。这是为了从Spring容器中解析bean时解决歧义。 - 而在 JSR 330 规范(例如在 CDI 中)中,`javax.inject.Qualifier` 是一个元注解,用于定义新的注解作为限定符。这些自定义的限定符注解然后可以与 `javax.inject.Inject` 配合使用,以解决歧义。 3. **自定义限定符注解** + 尤其在 JSR 330 规范中,`@Qualifier` 用于创建其他自定义注解,这些新定义的注解随后可以用作限定符。 4. **提高代码的语义清晰度** + 通过使用 `@Qualifier` 或基于它的自定义注解,代码更具有描述性,更容易理解应注入哪个特定的 bean。 ### 五、最佳实践 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyController`类型的bean并调用了`showMessage`方法。 ```java public class QualifierApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MessageController messageController = context.getBean(MessageController.class); messageController.showMessage(); } } ``` 在`MyConfiguration`类中,使用了`@ComponentScan("com.xcs.spring")`注解告诉 Spring 在指定的包(在这里是 "`com.xcs.spring`")及其子包中搜索带有 `@Component`、`@Service`、`@Repository` 和 `@Controller` 等注解的类,并将它们自动注册为 beans。这样,spring就不必为每个组件明确写一个 bean 定义。Spring 会自动识别并注册它们。 ```java @Configuration @ComponentScan("com.xcs.spring") public class MyConfiguration { } ``` 我们定义了两个自定义注解,`@Email` 和 `@SMS`,它们都被标记为 `@Qualifier`。这意味着它们都可以被用作限定符注解。 ```java @Qualifier @Retention(RetentionPolicy.RUNTIME) public @interface Email { } @Qualifier @Retention(RetentionPolicy.RUNTIME) public @interface SMS { } ``` `EmailServiceImpl` & `SMSServiceImpl`这两个类都实现了 `MessageService` 接口,但分别带有 `@Email` 和 `@SMS` 限定符注解,意味着它们在被注入时可以被明确区分。 ```java public interface MessageService { String getMessage(); } @Email @Named public class EmailServiceImpl implements MessageService { @Override public String getMessage() { return "Email message"; } } @SMS @Named public class SMSServiceImpl implements MessageService { @Override public String getMessage() { return "SMS message"; } } ``` `MessageController` 注入了两种 `MessageService` 的实现:一种用于Email,另一种用于SMS。通过使用自定义的限定符注解 `@Email` 和 `@SMS`,确保了正确的服务实现被注入到对应的字段中。`showMessage` 方法用于显示这两个服务实现返回的消息。 ```java @Controller public class MessageController { @Inject @Email private MessageService emailService; @Inject @SMS private MessageService smsService; public void showMessage() { System.out.println("EmailService: " + emailService.getMessage()); System.out.println("SMSService: " + smsService.getMessage()); } } ``` 运行结果发现,`@Email` 限定符确保了 `EmailServiceImpl` 被注入到 `emailService` 字段。`@SMS` 限定符确保了 `SMSServiceImpl` 被注入到 `smsService` 字段。`@Qualifier` 注解在这里起到了关键的作用,确保了正确的服务实现被注入,从而使得运行结果符合预期。 ```java EmailService: Email message SMSService: SMS message ``` ### 六、时序图 #### @Qualifier注册 ~~~mermaid sequenceDiagram Title: @Qualifier注册时序图 QualifierApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses) Note over AnnotationConfigApplicationContext: 上下文初始化开始,传入了一些组件类 AnnotationConfigApplicationContext->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext() Note over AnnotationConfigApplicationContext: 上下文构造器调用 AnnotationConfigApplicationContext->>AnnotatedBeanDefinitionReader:AnnotatedBeanDefinitionReader(registry) Note over AnnotatedBeanDefinitionReader: 为给定的注册中心创建一个Bean定义读取器 AnnotatedBeanDefinitionReader->>AnnotatedBeanDefinitionReader:AnnotatedBeanDefinitionReader(registry,environment) Note over AnnotatedBeanDefinitionReader: 读取器构造器使用特定的环境设置 AnnotatedBeanDefinitionReader->>AnnotationConfigUtils:registerAnnotationConfigProcessors(registry) Note over AnnotationConfigUtils: 注册注解配置处理器 AnnotationConfigUtils->>AnnotationConfigUtils:registerAnnotationConfigProcessors(registry,source) Note right of AnnotationConfigUtils: 注册注解配置处理器 AnnotationConfigUtils->>QualifierAnnotationAutowireCandidateResolver:QualifierAnnotationAutowireCandidateResolver() Note over QualifierAnnotationAutowireCandidateResolver: 创建一个新的限定符自动装配候选解析器 QualifierAnnotationAutowireCandidateResolver->>QualifierAnnotationAutowireCandidateResolver:this.qualifierTypes.add("javax.inject.Qualifier"); Note right of QualifierAnnotationAutowireCandidateResolver: 添加javax.inject.Qualifier到限定符类型列表 QualifierAnnotationAutowireCandidateResolver->>AnnotationConfigUtils:返回Resolver Note right of AnnotationConfigUtils: 解析器创建完毕,现在返回给调用者 AnnotationConfigUtils->>DefaultListableBeanFactory:setAutowireCandidateResolver(autowireCandidateResolver) Note over DefaultListableBeanFactory: 在Bean工厂中设置自动装配候选解析器 ~~~ #### @Qualifier解析 ~~~mermaid sequenceDiagram Title: @Qualifier解析时序图 InjectionMetadata->>AutowiredFieldElement:inject(bean,beanName,pvs) Note over AutowiredFieldElement: 开始注入字段 AutowiredFieldElement->>AutowiredFieldElement:resolveFieldValue(field, bean, beanName) Note over AutowiredFieldElement: 尝试解析字段的值 AutowiredFieldElement->>DefaultListableBeanFactory:resolveDependency(desc,beanName,autowiredBeanNames,typeConverter) Note over DefaultListableBeanFactory: 解决字段的依赖关系 DefaultListableBeanFactory->>DefaultListableBeanFactory:doResolveDependency(descriptor,requestingBeanName,autowiredBeanNames,typeConverter) Note over DefaultListableBeanFactory: 执行依赖解析 DefaultListableBeanFactory->>DefaultListableBeanFactory:findAutowireCandidates(beanName,type,descriptor) Note over DefaultListableBeanFactory: 查找可能的自动装配候选者 DefaultListableBeanFactory->>DefaultListableBeanFactory:isAutowireCandidate(candidate,descriptor) Note over DefaultListableBeanFactory: 检查候选bean是否是合适的自动装配候选者 DefaultListableBeanFactory->>DefaultListableBeanFactory:isAutowireCandidate(beanName,descriptor,resolver) Note right of DefaultListableBeanFactory: 进一步检查,使用解析器 DefaultListableBeanFactory->>DefaultListableBeanFactory:isAutowireCandidate(beanName,mbd,descriptor,resolver) Note right of DefaultListableBeanFactory: 使用给定的bean定义进一步检查 DefaultListableBeanFactory->>QualifierAnnotationAutowireCandidateResolver:isAutowireCandidate(holder, descriptor) Note over QualifierAnnotationAutowireCandidateResolver: 判断是否根据@Qualifier注解是自动装配的候选者 QualifierAnnotationAutowireCandidateResolver->>QualifierAnnotationAutowireCandidateResolver:checkQualifiers(bdHolder,annotationsToSearch) Note over QualifierAnnotationAutowireCandidateResolver: 检查所有相关的限定符注解 QualifierAnnotationAutowireCandidateResolver->>QualifierAnnotationAutowireCandidateResolver:isQualifier(annotationType) Note over QualifierAnnotationAutowireCandidateResolver: 判断给定的注解类型是否是一个有效的限定符 ~~~ ### 七、源码分析 #### @Qualifier注册 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyController`类型的bean并调用了`showMessage`方法。 ```java public class QualifierApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MessageController messageController = context.getBean(MessageController.class); messageController.showMessage(); } } ``` 在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们本次重点关注`this()`。 ```java public AnnotationConfigApplicationContext(Class... componentClasses) { this(); register(componentClasses); refresh(); ``` 在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext()`方法中,初始化了的两个核心组件,一个用于读取注解定义的bean (`AnnotatedBeanDefinitionReader`)也是本次重点分析的内容,另一个用于扫描类路径并自动检测bean组件 (`ClassPathBeanDefinitionScanner`)。 ```java public AnnotationConfigApplicationContext() { StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create"); this.reader = new AnnotatedBeanDefinitionReader(this); createAnnotatedBeanDefReader.end(); this.scanner = new ClassPathBeanDefinitionScanner(this); } ``` 在`org.springframework.context.annotation.AnnotatedBeanDefinitionReader#AnnotatedBeanDefinitionReader(registry)`方法中,又调用了另一个构造函数。 ```java public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) { this(registry, getOrCreateEnvironment(registry)); } ``` 在`org.springframework.context.annotation.AnnotatedBeanDefinitionReader#AnnotatedBeanDefinitionReader(registry,environment)`方法中,这是一个重要的调用`registerAnnotationConfigProcessors`,会向容器注册一系列的后置处理器,这些后置处理器对于处理各种注解(如 `@Inject`, `@Qualifier`等)至关重要。 ```java public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); Assert.notNull(environment, "Environment must not be null"); this.registry = registry; this.conditionEvaluator = new ConditionEvaluator(registry, environment, null); AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); } ``` 在`org.springframework.context.annotation.AnnotationConfigUtils#registerAnnotationConfigProcessors(registry)`方法中,又调用了另外一个重载的方法。 ```java public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) { registerAnnotationConfigProcessors(registry, null); } ``` 在`org.springframework.context.annotation.AnnotationConfigUtils#registerAnnotationConfigProcessors(registry,source)`方法中,在 Spring 的自动装配机制中,当有多个候选bean可以被注入到某个位置时,需要有一个方式来解决哪个bean是最佳的候选者。`AutowireCandidateResolver` 就是用来做这个决策的。 ```java public static Set registerAnnotationConfigProcessors( BeanDefinitionRegistry registry, @Nullable Object source) { DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry); if (beanFactory != null) { // ... [代码部分省略以简化] if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) { beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); } } // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver#QualifierAnnotationAutowireCandidateResolver()`方法中,目的是为解析器设置一些基本配置。其中最重要的部分是尝试加载 JSR-330 的 `@Qualifier` 注解,并将其添加到 `qualifierTypes` 集合中,以便在后续的依赖注入过程中能够正确处理这个注解。如果 JSR-330 不可用,解析器会简单地跳过这个步骤。 ```java public QualifierAnnotationAutowireCandidateResolver() { // ... [代码部分省略以简化] try { this.qualifierTypes.add((Class) ClassUtils.forName("javax.inject.Qualifier", QualifierAnnotationAutowireCandidateResolver.class.getClassLoader())); } catch (ClassNotFoundException ex) { // JSR-330 API not available - simply skip. } } ``` #### @Qualifier解析 ##### 前置条件 在Spring中,`AutowiredAnnotationBeanPostProcessor` 负责处理多种依赖注入注解,包括 JSR-330 的 `@Inject` 注解。为了深入了解 `@Inject` 注解及其与 `@Qualifier` 注解的结合方式,研究这个后处理器是至关重要的。简而言之,为了完全掌握 `@Inject` 和 `@Qualifier` 的协同工作原理,深入阅读 `@Inject` 博客是非常必要的。这些博客为我们提供了对如何在 bean 生命周期中的关键阶段处理这些注解的深入理解。 1. **@Inject注解源码分析**: - 在这篇博客中,我们会深入了解 `@Inject` 注解的背后原理。从 JSR-330 规范的引入,到如何在 Spring 中正确使用,以及与其他注解如 `@Autowired` 的差异,这篇博客为我们提供了全面的视角。 - 🔗 [@Inject注解传送门](https://github.com/xuchengsheng/spring-reading/blob/master/spring-jsr/spring-jsr330-inject) ##### 解析入口 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency`方法中,首先尝试获取一个延迟解析代理。如果无法获得,它会进一步尝试解析依赖。`doResolveDependency` 是实际进行解析工作的方法。 ```java public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName, @Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException { // ... [代码部分省略以简化] Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary( descriptor, requestingBeanName); if (result == null) { result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter); } return result; } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency`方法中,主要目的是解析给定的依赖描述符 (`DependencyDescriptor`),并尝试找到一个合适的 bean 来满足这个依赖。 ```java public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName, @Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException { // ... [代码部分省略以简化] try { // ... [代码部分省略以简化] // 步骤1. 根据依赖描述符查找匹配的bean Map matchingBeans = findAutowireCandidates(beanName, type, descriptor); // ... [代码部分省略以简化] } // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#findAutowireCandidates`方法中,首先基于给定的类型获取所有可能的bean名。接着,对于每一个可能的候选bean,它检查该bean是否是一个合适的自动注入候选,如果是,它将这个bean添加到结果集中。最后,方法返回找到的所有合适的候选bean。 ```java protected Map findAutowireCandidates( @Nullable String beanName, Class requiredType, DependencyDescriptor descriptor) { // 根据所需的类型,包括所有父工厂中的bean,获取所有可能的bean名 String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this, requiredType, true, descriptor.isEager()); // ... [代码部分省略以简化] // 遍历所有候选bean名 for (String candidate : candidateNames) { // 如果候选bean不是正在查找的bean本身并且它是一个合适的自动注入候选 if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) { // 添加这个候选bean到结果中 addCandidateEntry(result, candidate, descriptor, requiredType); } } // ... [代码部分省略以简化] // 返回找到的所有候选bean return result; } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#isAutowireCandidate(beanName, descriptor)`方法中,首先是调用 `getAutowireCandidateResolver()` 方法时,我们会得到这里设置的那个解析器,会根据其实现来决定哪个 bean 是自动装配的候选者,尤其当存在多个可能的候选者时,会考虑到 `@Qualifier` 和其他相关的注解,然后会进一步调用 `isAutowireCandidate` 的另一个重载版本,并且会传入解析器。 ```java @Override public boolean isAutowireCandidate(String beanName, DependencyDescriptor descriptor) throws NoSuchBeanDefinitionException { return isAutowireCandidate(beanName, descriptor, getAutowireCandidateResolver()); } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#isAutowireCandidate(beanName,descriptor,resolver)`方法中,首先是调用`getMergedLocalBeanDefinition(bdName)`来获取被依赖Bean的定义, 然后会进一步调用 `isAutowireCandidate` 的另一个重载版本,并且会传入合并的 bean 定义。 ```java protected boolean isAutowireCandidate( String beanName, DependencyDescriptor descriptor, AutowireCandidateResolver resolver) throws NoSuchBeanDefinitionException { String bdName = BeanFactoryUtils.transformedBeanName(beanName); if (containsBeanDefinition(bdName)) { return isAutowireCandidate(beanName, getMergedLocalBeanDefinition(bdName), descriptor, resolver); } // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#isAutowireCandidate(beanName, mbd,descriptor, resolver)`方法中,首先是初始化了一个`BeanDefinitionHolder`,这是一个方便的数据结构,用于同时持有 `BeanDefinition`, bean 的名称和别名。这对于解析自动装配候选项很有用,因为有时我们需要知道 bean 的名称和别名,然后会进一步调用解析器的 `isAutowireCandidate` 方法,并且会传入`BeanDefinitionHolder`与依赖描述符 。 ```java protected boolean isAutowireCandidate(String beanName, RootBeanDefinition mbd, DependencyDescriptor descriptor, AutowireCandidateResolver resolver) { // ... [代码部分省略以简化] BeanDefinitionHolder holder = (beanName.equals(bdName) ? this.mergedBeanDefinitionHolders.computeIfAbsent(beanName, key -> new BeanDefinitionHolder(mbd, beanName, getAliases(bdName))) : new BeanDefinitionHolder(mbd, beanName, getAliases(bdName))); return resolver.isAutowireCandidate(holder, descriptor); } ``` 在`org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver#isAutowireCandidate`方法中,主要目的是增强了对 `@Qualifier` 注解的支持。如果一个bean或依赖被 `@Qualifier` 注解,并指定了一个bean的名称,那么只有名称匹配的bean会被认为是自动装配的候选者。如果没有指定名称,但指定了其他属性或元注解,那么只有匹配这些属性或元注解的bean会被认为是自动装配的候选者。 ```java @Override public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) { // 首先通过父类的方法进行基本的匹配检查。 boolean match = super.isAutowireCandidate(bdHolder, descriptor); // 如果基本检查通过,则进一步检查 bdHolder 和 descriptor 中的注解是否匹配。 if (match) { // 检查候选bean的资格符与依赖描述符的注解是否匹配。 match = checkQualifiers(bdHolder, descriptor.getAnnotations()); if (match) { // 获取与依赖相关的方法参数(如果有的话,例如当依赖是一个setter方法的参数时)。 MethodParameter methodParam = descriptor.getMethodParameter(); // 如果有关联的方法参数,则进行进一步的检查。 if (methodParam != null) { Method method = methodParam.getMethod(); // 如果方法是void返回类型(如setter方法),则检查它的注解与候选bean的资格符是否匹配。 if (method == null || void.class == method.getReturnType()) { match = checkQualifiers(bdHolder, methodParam.getMethodAnnotations()); } } } } // 返回最终的匹配结果。 return match; } ``` 在`org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver#checkQualifiers`方法中,主要目的是确保待注入的bean与所有相关的 `@Qualifier` 注解和元注解匹配。如果其中一个注解或元注解与bean定义不匹配,那么这个bean就不是当前注入点的合适候选。 ```java protected boolean checkQualifiers(BeanDefinitionHolder bdHolder, Annotation[] annotationsToSearch) { // 如果注解数组为空,直接返回 true if (ObjectUtils.isEmpty(annotationsToSearch)) { return true; } SimpleTypeConverter typeConverter = new SimpleTypeConverter(); for (Annotation annotation : annotationsToSearch) { Class type = annotation.annotationType(); boolean checkMeta = true; boolean fallbackToMeta = false; // 检查当前注解是否是 @Qualifier 或自定义的资格符注解 if (isQualifier(type)) { // 如果是,并且与bean定义匹配 if (!checkQualifier(bdHolder, annotation, typeConverter)) { // 不匹配则可能需要检查元注解 fallbackToMeta = true; } else { // 匹配则不需要进一步检查元注解 checkMeta = false; } } // 如果不是资格符注解或与bean定义不匹配,但有元注解是资格符注解 if (checkMeta) { boolean foundMeta = false; for (Annotation metaAnn : type.getAnnotations()) { Class metaType = metaAnn.annotationType(); // 检查元注解是否是资格符注解 if (isQualifier(metaType)) { foundMeta = true; // 只有当 @Qualifier 注解有值或其他条件满足时,才会接受元注解的匹配 if ((fallbackToMeta && ObjectUtils.isEmpty(AnnotationUtils.getValue(metaAnn))) || !checkQualifier(bdHolder, metaAnn, typeConverter)) { // 元注解不匹配 return false; } } } if (fallbackToMeta && !foundMeta) { return false; // 需要元注解,但没有找到 } } } // 所有注解都匹配 return true; } ``` 在`org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver#isQualifier`方法中,主要目的是确定给定的注解是否为一个"资格符"注解,也就是我们通常所说的限定符注解,如`@Qualifier`。这是通过检查注解是否存在于已知的`qualifierTypes`集合中,或者它是否带有这些资格符注解来完成的。 ```java protected boolean isQualifier(Class annotationType) { // 遍历已知的资格符注解类型 for (Class qualifierType : this.qualifierTypes) { // 如果给定的注解是已知的资格符注解,或者它带有这样的元注解 if (annotationType.equals(qualifierType) || annotationType.isAnnotationPresent(qualifierType)) { return true; // 是资格符注解 } } return false; // 不是资格符注解 } ``` ### 八、注意事项 1. **确保正确的依赖注入** + 使用 `@Qualifier` 主要是为了解决Spring容器中有多个同类型Bean的问题。没有 `@Qualifier`,Spring将无法决定注入哪一个Bean。但使用 `@Qualifier` 时,我们需要确保给定的资格符名称确实存在,否则Spring会抛出异常。 2. **与其他注解组合** + `@Qualifier` 通常与 `@Autowired` 或 `@Inject` 一起使用。确保在正确的地方使用它(字段、setter方法或构造函数)。 3. **自定义资格符注解** + 我们可以创建自己的注解,并使用 `@Qualifier` 作为元注解。这样,我们可以创建具有特定命名或其他语义的资格符注解,使代码更具可读性。 4. **使用字符串名称** + `@Qualifier` 默认使用字符串值来指定Bean的名称。这意味着,如果我们重命名了Bean或更改了其名称,我们也需要更改所有使用这个Bean名称的 `@Qualifier` 注解。 5. **与Java配置一起使用** + 当使用Java配置创建Beans时,可以使用 `@Bean` 的方法名称作为资格符名称。这使得使用Java配置和 `@Qualifier` 更为一致。 6. **与 `@Primary` 的关系** + 如果我们同时使用了 `@Primary` 和 `@Qualifier`,则 `@Qualifier` 的优先级更高。也就是说,如果一个Bean被标记为 `@Primary`,但在注入点使用了 `@Qualifier`,则会使用 `@Qualifier` 指定的Bean。 7. **名称和类型的匹配** + 尽管 `@Qualifier` 主要用于通过名称进行匹配,但Spring仍然会验证匹配的Bean类型。所以,如果Bean名称匹配但类型不匹配,仍然会出现异常。 8. **与JSR-330的兼容性** + Spring的 `@Qualifier` 注解与JSR-330(Java的依赖注入规范)中的 `javax.inject.Qualifier` 注解兼容。但是,当使用JSR-330标准时,确保依赖注入提供程序(如Spring)正确支持它。 ### 九、总结 #### 最佳实践总结 1. **明确的配置** + 使用 `AnnotationConfigApplicationContext` 作为Spring容器,并通过构造参数指定配置类,使得Spring容器初始化过程清晰明了。 2. **利用组件扫描** + 通过 `@ComponentScan` 注解,自动扫描指定包及其子包中的组件,减少了手动注册bean的工作,使代码更简洁、高效。 3. **自定义资格符注解** + 为了解决多个同类型Bean的问题,定义了 `@Email` 和 `@SMS` 两个自定义限定符注解。这使得代码更有可读性,并提供了更明确的注入意图。 4. **准确的服务注入** + 在 `MessageController` 中,使用了自定义的 `@Email` 和 `@SMS` 注解与 `@Inject` 组合,确保了正确的 `MessageService` 实现被注入到相应的字段。 5. **清晰的输出** + 通过 `showMessage` 方法的输出,我们可以清晰地看到 `@Qualifier` 的作用,确保了不同的服务实现被正确注入。 6. **充分利用Java配置** + 通过 `@Configuration` 和 `@ComponentScan`,结合Java配置,Spring容器的初始化和bean的注册过程都变得更加直观和简洁。 7. **注解的扩展性** + 通过使用 `@Qualifier` 作为元注解,自定义注解的方式体现了Spring的灵活性和注解的扩展性。 8. **保持代码整洁和模块化** + 每个类和接口都有明确的职责,并且代码被组织得清晰、模块化,这不仅使代码更易于维护,还提高了代码的可读性和可复用性。 #### 源码分析总结 1. **应用启动及上下文初始化** + 当创建`AnnotationConfigApplicationContext`时,它接收一个配置类(如`MyConfiguration`)作为参数,用于初始化Spring上下文。启动时,会从Spring上下文中获取并使用相应的Bean。 2. **AnnotationConfigApplicationContext的构造过程** + 在其构造函数中,`AnnotationConfigApplicationContext`执行了几个关键步骤,其中最核心的是`this()`,该方法初始化了两个组件:`AnnotatedBeanDefinitionReader`(负责读取注解定义的bean)和`ClassPathBeanDefinitionScanner`(负责扫描类路径并自动检测bean组件)。 3. **AnnotatedBeanDefinitionReader的初始化** + `AnnotatedBeanDefinitionReader`在其构造函数中调用`registerAnnotationConfigProcessors`,此方法向容器注册了一系列的后置处理器,这些后置处理器对于处理各种注解(如`@Inject`, `@Qualifier`等)是关键。 4. **Qualifier注解的注册** + 当创建`QualifierAnnotationAutowireCandidateResolver`实例时,它会尝试加载JSR-330的`@Qualifier`注解,并将其添加到`qualifierTypes`集合中。这使得在后续的依赖注入过程中,Spring可以正确处理这个注解。 5. **依赖解析过程** + 当Spring尝试解析某个依赖时,它会进入`DefaultListableBeanFactory#resolveDependency`。此方法首先尝试获取一个延迟解析代理,如果不能获得,则会尝试解析依赖,此过程涉及到`doResolveDependency`方法。 6. **自动装配候选检查** + 在`findAutowireCandidates`中,Spring首先找出所有可能的bean名称,然后检查每一个可能的候选bean,看看它是否是一个合适的自动装配候选。此检查涉及到`isAutowireCandidate`方法。 7. **利用AutowireCandidateResolver判断是否为自动装配候选者** + `isAutowireCandidate`方法的工作是判断某个bean是否是自动装配的候选者。在有多个可能的候选bean时,`AutowireCandidateResolver`(这里的实例是`QualifierAnnotationAutowireCandidateResolver`)会被用来做决策,考虑到`@Qualifier`和其他相关的注解。 8. **处理@Qualifier注解** + `QualifierAnnotationAutowireCandidateResolver`的主要任务是增强对`@Qualifier`注解的支持。它确保待注入的bean与所有相关的`@Qualifier`注解和元注解匹配。如果其中一个注解或元注解与bean定义不匹配,那么这个bean就不是当前注入点的合适候选。 9. **资格符检查** + `isQualifier`方法则用来确定给定的注解是否为一个资格符注解(限定符注解),例如`@Qualifier`。这是通过检查注解是否存在于已知的`qualifierTypes`集合中或它是否带有这些资格符注解来完成的。 ================================================ FILE: spring-jsr/spring-jsr330-qualifier/pom.xml ================================================ spring-jsr com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-jsr330-qualifier ================================================ FILE: spring-jsr/spring-jsr330-qualifier/src/main/java/com/xcs/spring/QualifierApplication.java ================================================ package com.xcs.spring; import com.xcs.spring.config.MyConfiguration; import com.xcs.spring.controller.MessageController; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author xcs * @date 2023年10月20日 14时56分 **/ public class QualifierApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MessageController messageController = context.getBean(MessageController.class); messageController.showMessage(); } } ================================================ FILE: spring-jsr/spring-jsr330-qualifier/src/main/java/com/xcs/spring/annotation/Email.java ================================================ package com.xcs.spring.annotation; import javax.inject.Qualifier; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Qualifier @Retention(RetentionPolicy.RUNTIME) public @interface Email { } ================================================ FILE: spring-jsr/spring-jsr330-qualifier/src/main/java/com/xcs/spring/annotation/SMS.java ================================================ package com.xcs.spring.annotation; import javax.inject.Qualifier; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Qualifier @Retention(RetentionPolicy.RUNTIME) public @interface SMS { } ================================================ FILE: spring-jsr/spring-jsr330-qualifier/src/main/java/com/xcs/spring/config/MyConfiguration.java ================================================ package com.xcs.spring.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; /** * @author xcs * @date 2023年10月20日 14时56分 **/ @Configuration @ComponentScan("com.xcs.spring") public class MyConfiguration { } ================================================ FILE: spring-jsr/spring-jsr330-qualifier/src/main/java/com/xcs/spring/controller/MessageController.java ================================================ package com.xcs.spring.controller; import com.xcs.spring.annotation.Email; import com.xcs.spring.annotation.SMS; import com.xcs.spring.service.MessageService; import org.springframework.stereotype.Controller; import javax.inject.Inject; /** * @author xcs * @date 2023年10月20日 14时59分 **/ @Controller public class MessageController { @Inject @Email private MessageService emailService; @Inject @SMS private MessageService smsService; public void showMessage() { System.out.println("EmailService: " + emailService.getMessage()); System.out.println("SMSService: " + smsService.getMessage()); } } ================================================ FILE: spring-jsr/spring-jsr330-qualifier/src/main/java/com/xcs/spring/service/MessageService.java ================================================ package com.xcs.spring.service; /** * @author xcs * @date 2023年10月20日 14时56分 **/ public interface MessageService { String getMessage(); } ================================================ FILE: spring-jsr/spring-jsr330-qualifier/src/main/java/com/xcs/spring/service/impl/EmailServiceImpl.java ================================================ package com.xcs.spring.service.impl; import com.xcs.spring.annotation.Email; import com.xcs.spring.service.MessageService; import javax.inject.Named; /** * @author xcs * @date 2023年10月20日 14时57分 **/ @Email @Named public class EmailServiceImpl implements MessageService { @Override public String getMessage() { return "Email message"; } } ================================================ FILE: spring-jsr/spring-jsr330-qualifier/src/main/java/com/xcs/spring/service/impl/SMSServiceImpl.java ================================================ package com.xcs.spring.service.impl; import com.xcs.spring.annotation.SMS; import com.xcs.spring.service.MessageService; import javax.inject.Named; /** * @author xcs * @date 2023年10月20日 14时57分 **/ @SMS @Named public class SMSServiceImpl implements MessageService { @Override public String getMessage() { return "SMS message"; } } ================================================ FILE: spring-jsr/spring-jsr330-scope/README.md ================================================ ## TODO ================================================ FILE: spring-jsr/spring-jsr330-scope/pom.xml ================================================ spring-jsr com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-jsr330-scope ================================================ FILE: spring-jsr/spring-jsr330-singleton/README.md ================================================ ## TODO ================================================ FILE: spring-jsr/spring-jsr330-singleton/pom.xml ================================================ spring-jsr com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-jsr330-singleton ================================================ FILE: spring-metadata/pom.xml ================================================ spring-reading com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-metadata pom spring-metadata-metadataReader spring-metadata-annotationMetadata spring-metadata-typeFilter spring-metadata-condition ================================================ FILE: spring-metadata/spring-metadata-annotationMetadata/README.md ================================================ ## AnnotationMetadata - [AnnotationMetadata](#annotationmetadata) - [一、知识储备](#一知识储备) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、接口源码](#四接口源码) - [五、主要实现](#五主要实现) - [六、最佳实践](#六最佳实践) - [ASM字节码技术](#asm字节码技术) - [JAVA反射技术](#java反射技术) - [七、与其他组件的关系](#七与其他组件的关系) - [八、常见问题](#八常见问题) ### 一、知识储备 1. **注解** + 了解什么是注解以及如何定义和使用它们是很重要的。`AnnotationMetadata` 用于处理类上的注解信息,因此我们需要了解如何编写自定义注解以及如何在类、方法或字段上应用标准和自定义注解。 2. **反射** + `AnnotationMetadata` 使用反射机制来分析类的注解信息,所以我们需要了解 Java 反射的基本原理,包括 `Class` 对象、`Method`、`Field`、`Annotation` 类等。 3. **类路径扫描** + 如果我们通过 `metadataReader.getAnnotationMetadata()` 方法来读取类的注解信息,我们需要了解如何进行类路径扫描和资源加载,以便正确定位和读取类的字节码。 ### 二、基本描述 `AnnotationMetadata` 是 Spring Framework 中的一个接口,用于访问和分析与类、方法、字段等元素相关的注解信息。它提供了一种方便的方式,让我们可以在运行时动态地获取和操作类上的注解信息。 ### 三、主要功能 1. **检查类上的注解**: - 我们可以使用 `hasAnnotation(String annotationName)` 方法来检查是否存在指定名称的注解在类上。这对于条件化的配置非常有用,可以根据类上的注解来控制不同的行为。 2. **获取注解属性值**: - 使用 `getAnnotationAttributes(String annotationName)` 方法,我们可以获取指定注解的属性值。这允许我们动态配置类的行为,根据注解属性的值来决定不同的处理逻辑。 3. **获取类上的所有注解**: - `getAnnotations()` 方法返回类上所有的注解,以 `AnnotationAttributes` 对象的列表形式。这使我们能够全面了解类的注解情况。 4. **获取元注解信息**: - `getMetaAnnotationTypes(String annotationName)` 方法用于获取指定注解上的元注解的类型,这对于分析注解层次结构非常有帮助。 5. **扫描带有指定注解的类**: - `getAnnotatedMethods(String annotationName)` 方法允许我们扫描指定包下所有带有指定注解的类,获取它们的元数据信息。这可用于实现自定义的组件扫描或注解处理逻辑。 6. **获取类的接口和超类信息**: - 我们可以使用 `getInterfaceNames()` 方法获取类实现的接口的全限定类名,以及使用 `getSuperClassName()` 方法获取类的超类的全限定类名。 7. **获取类的成员信息**: - 通过 `getMethodMetadata()`、`getFieldMetadata()` 和 `getClassMetadata()` 方法,我们可以获取类中方法、字段和类本身的元数据信息,以便分析这些元素上的注解。 ### 四、接口源码 `AnnotationMetadata` 接口提供了一组方法,用于在运行时分析和操作类的注解信息。它允许我们获取类上的注解、元注解、接口信息、超类信息以及方法上的注解信息,以便实现各种功能,如条件化配置、组件扫描和自定义注解处理。接口中的 `introspect` 方法用于创建 `AnnotationMetadata` 实例,以便在运行时分析类的注解信息。 ```java /** * 该接口定义了对特定类的注解的抽象访问,以一种无需加载该类的形式。 * * @author Juergen Hoeller * @author Mark Fisher * @author Phillip Webb * @author Sam Brannen * @since 2.5 * @see StandardAnnotationMetadata * @see org.springframework.core.type.classreading.MetadataReader#getAnnotationMetadata() * @see AnnotatedTypeMetadata */ public interface AnnotationMetadata extends ClassMetadata, AnnotatedTypeMetadata { /** * 获取底层类上的所有 存在 的注解类型的完全限定类名。 * @return 注解类型的名称 */ default Set getAnnotationTypes() { return getAnnotations().stream() .filter(MergedAnnotation::isDirectlyPresent) .map(annotation -> annotation.getType().getName()) .collect(Collectors.toCollection(LinkedHashSet::new)); } /** * 获取给定注解类型上底层类上 存在 的所有元注解类型的完全限定类名。 * @param annotationName 要查找的元注解类型的完全限定类名 * @return 元注解类型的名称集合,如果没有找到则返回空集合 */ default Set getMetaAnnotationTypes(String annotationName) { MergedAnnotation annotation = getAnnotations().get(annotationName, MergedAnnotation::isDirectlyPresent); if (!annotation.isPresent()) { return Collections.emptySet(); } return MergedAnnotations.from(annotation.getType(), SearchStrategy.INHERITED_ANNOTATIONS).stream() .map(mergedAnnotation -> mergedAnnotation.getType().getName()) .collect(Collectors.toCollection(LinkedHashSet::new)); } /** * 确定给定类型的注解是否 存在 在底层类上。 * @param annotationName 要查找的注解类型的完全限定类名 * @return 如果存在匹配的注解则返回 {@code true} */ default boolean hasAnnotation(String annotationName) { return getAnnotations().isDirectlyPresent(annotationName); } /** * 确定底层类是否具有其本身带有给定类型的元注解的注解。 * @param metaAnnotationName 要查找的元注解类型的完全限定类名 * @return 如果存在匹配的元注解则返回 {@code true} */ default boolean hasMetaAnnotation(String metaAnnotationName) { return getAnnotations().get(metaAnnotationName, MergedAnnotation::isMetaPresent).isPresent(); } /** * 确定底层类是否具有任何方法带有给定注解类型(或带有元注解的)。 * @param annotationName 要查找的注解类型的完全限定类名 */ default boolean hasAnnotatedMethods(String annotationName) { return !getAnnotatedMethods(annotationName).isEmpty(); } /** * 检索带有给定注解类型(或带有元注解的)的所有方法的方法元数据。 *

对于任何返回的方法,{@link MethodMetadata#isAnnotated} 将对给定的注解类型返回 {@code true}。 * @param annotationName 要查找的注解类型的完全限定类名 * @return 具有匹配注解的方法的{@link MethodMetadata}集合。如果没有方法匹配注解类型,则返回空集合。 */ Set getAnnotatedMethods(String annotationName); /** * 使用标准反射创建一个新的 {@link AnnotationMetadata} 实例的工厂方法,用于给定的类。 * @param type 要分析的类 * @return 新的 {@link AnnotationMetadata} 实例 * @since 5.2 */ static AnnotationMetadata introspect(Class type) { return StandardAnnotationMetadata.from(type); } } ``` ### 五、主要实现 1. **StandardAnnotationMetadata**: - 这个实现依赖于标准的 Java 反射机制,它使用 Java 的 `java.lang.Class` 对象来分析和访问类的注解信息。 - 通过这个实现,我们可以轻松地检查类上的注解、获取注解属性值以及执行其他与注解相关的操作。 2. **SimpleAnnotationMetadata**: - 是一个基于 ASM(字节码操作库)的实现,用于分析和访问类的注解信息。相比于 `StandardAnnotationMetadata`,它通常更轻量,不依赖于标准 Java 反射机制,而是直接解析类的字节码。 ~~~mermaid classDiagram direction BT class ClassMetadata { <> } class AnnotatedTypeMetadata { <> } class AnnotationMetadata { <> } class StandardClassMetadata { } class SimpleAnnotationMetadata { } class StandardAnnotationMetadata { } AnnotationMetadata ..|> ClassMetadata AnnotationMetadata ..|> AnnotatedTypeMetadata SimpleAnnotationMetadata --|> AnnotationMetadata StandardClassMetadata --|> ClassMetadata StandardAnnotationMetadata ..|> StandardClassMetadata StandardAnnotationMetadata --|> AnnotationMetadata ~~~ ### 六、最佳实践 #### ASM字节码技术 我们使用 ASM 字节码技术创建了一个 `AnnotationMetadata` 对象,通过创建 `MetadataReaderFactory` 和 `MetadataReader` 对象,加载了名为 "`com.xcs.spring.bean.MyBean`" 的类的元数据,然后通过 `AnnotationMetadata` 检查是否被 `@Component` 注解标记,并获取注解属性值,最后输出相应的结果。这个过程允许在不实际加载类的情况下,动态地分析和操作类的注解信息。 ```java public class AnnotationMetadataDemoByASM { public static void main(String[] args) throws Exception { // 创建 MetadataReaderFactory SimpleMetadataReaderFactory readerFactory = new SimpleMetadataReaderFactory(); // 获取 MetadataReader MetadataReader metadataReader = readerFactory.getMetadataReader("com.xcs.spring.bean.MyBean"); // 获取 AnnotationMetadata AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata(); System.out.println("AnnotationMetadata impl class is " + annotationMetadata.getClass()); // 检查 MyBean 类是否被 @Component 注解标记 boolean isComponent = annotationMetadata.hasAnnotation(Component.class.getName()); System.out.println("MyBean is a @Component: " + isComponent); // 获取 MyBean 类上的注解属性 if (isComponent) { Map annotationAttributes = annotationMetadata.getAnnotationAttributes(Component.class.getName()); System.out.println("@Component value is " + annotationAttributes.get("value")); } } } ``` 运行结果发现,`AnnotationMetadata` 的实现类是 `SimpleAnnotationMetadata`,这是一个基于 ASM 的实现,不依赖标准的 Java 反射机制。 ```java AnnotationMetadata impl class is class org.springframework.core.type.classreading.SimpleAnnotationMetadata MyBean is a @Component: true @Component value is myBean ``` #### JAVA反射技术 使用 Java 反射技术创建 `AnnotationMetadata` 对象,通过调用 `AnnotationMetadata.introspect(MyBean.class)` 方法,加载了名为 `MyBean` 的类的元数据。然后通过 `AnnotationMetadata` 检查是否被 `@Component` 注解标记,并获取注解属性值,最后输出相应的结果。使用标准 Java 反射机制来实现这些功能,这是一种通用的方式,适用于大多数 Spring 应用程序。 ```java public class AnnotationMetadataDemoByReflection { public static void main(String[] args) throws Exception { // 获取 AnnotationMetadata AnnotationMetadata annotationMetadata = AnnotationMetadata.introspect(MyBean.class); System.out.println("AnnotationMetadata impl class is " + annotationMetadata.getClass()); // 检查 MyBean 类是否被 @Component 注解标记 boolean isComponent = annotationMetadata.hasAnnotation(Component.class.getName()); System.out.println("MyBean is a @Component: " + isComponent); // 获取 MyBean 类上的注解属性 if (isComponent) { Map annotationAttributes = annotationMetadata.getAnnotationAttributes(Component.class.getName()); System.out.println("@Component value is " + annotationAttributes.get("value")); } } } ``` 运行结果发现,`AnnotationMetadata` 的实现类是 `StandardAnnotationMetadata`,这是一个基于 JAVA反射机制的实现的。 ```java AnnotationMetadata impl class is class org.springframework.core.type.StandardAnnotationMetadata MyBean is a @Component: true @Component value is myBean ``` ### 七、与其他组件的关系 1. **组件扫描** - `AnnotationMetadata` 与 `ClassPathBeanDefinitionScanner` 类相关。在组件扫描过程中,`ClassPathBeanDefinitionScanner` 使用 `AnnotationMetadata` 来检查类上的特定注解(如 `@Component`、`@Service`、`@Controller` 等),并将这些类注册为 Spring Beans。 2. **Bean 定义** - `AnnotationMetadata` 与 `AnnotatedGenericBeanDefinition`,`ScannedGenericBeanDefinition` 类相关。当 Spring 扫描到带有注解的类时,会使用 `AnnotationMetadata` 来构建 Bean 定义。它提取类上的注解信息,包括 Bean 名称、作用域、依赖关系等,然后将这些信息用于创建 Bean 定义。 3. **条件化的 Bean 注册** - `AnnotationMetadata` 与条件化 Bean 注册相关的类有 `Conditional` 注解和 `Condition` 接口。在条件化注册中,`AnnotationMetadata` 用于评估 `@Conditional` 注解,根据条件的计算结果来决定是否注册特定的 Bean。 ### 八、常见问题 1. **如何获取类上的特定注解信息?** - 使用 `AnnotationMetadata` 的 `getAnnotations()` 方法,然后可以过滤和检查每个注解以获取特定注解的信息。例如,使用 `isDirectlyPresent()` 来检查注解是否直接存在于类上,然后使用 `getAnnotationAttributes()` 获取注解的属性值。 2. **如何判断类是否被特定注解标记?** - 使用 `AnnotationMetadata` 的 `hasAnnotation()` 方法,提供注解的完全限定名,可以检查类是否被特定注解标记。 3. **如何处理条件化注册的问题?** - 当使用条件化注解(如 `@Conditional`)时,需要使用 `AnnotationMetadata` 评估条件,并根据条件的结果来决定是否注册 Bean。通常,这需要自定义条件类和实现 `Condition` 接口来处理条件逻辑。 4. **如何扫描和分析多个类?** - 可以使用 Spring 的组件扫描功能,通过配置 `@ComponentScan` 注解或 XML 配置文件来扫描多个类。然后,`AnnotationMetadata` 可以用于分析扫描到的每个类。 5. **如何自定义注解处理器?** - 如果需要自定义处理特定注解的逻辑,可以编写自定义注解处理器,并使用 `AnnotationMetadata` 来分析和处理注解信息。这通常涉及实现自定义逻辑,例如动态创建 Bean 定义或执行其他操作。 6. **如何选择使用 ASM 或标准 Java 反射?** - 当选择使用 `AnnotationMetadata` 时,需要根据具体需求和性能考虑选择使用 ASM 或标准 Java 反射的实现。ASM 更适合需要高性能的场景,而标准 Java 反射通常更易于使用和维护。选择取决于项目的要求和性能需求。 ================================================ FILE: spring-metadata/spring-metadata-annotationMetadata/pom.xml ================================================ spring-metadata com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-metadata-annotationMetadata ================================================ FILE: spring-metadata/spring-metadata-annotationMetadata/src/main/java/com/xcs/spring/AnnotationMetadataDemoByASM.java ================================================ package com.xcs.spring; import com.xcs.spring.bean.MyBean; import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.SimpleMetadataReaderFactory; import org.springframework.stereotype.Component; import java.io.IOException; import java.util.Map; /** * @author xcs * @date 2023年10月31日 16时17分 **/ public class AnnotationMetadataDemoByASM { public static void main(String[] args) throws Exception { // 创建 MetadataReaderFactory SimpleMetadataReaderFactory readerFactory = new SimpleMetadataReaderFactory(); // 获取 MetadataReader MetadataReader metadataReader = readerFactory.getMetadataReader("com.xcs.spring.bean.MyBean"); // 获取 AnnotationMetadata AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata(); System.out.println("AnnotationMetadata impl class is " + annotationMetadata.getClass()); // 检查 MyBean 类是否被 @Component 注解标记 boolean isComponent = annotationMetadata.hasAnnotation(Component.class.getName()); System.out.println("MyBean is a @Component: " + isComponent); // 获取 MyBean 类上的注解属性 if (isComponent) { Map annotationAttributes = annotationMetadata.getAnnotationAttributes(Component.class.getName()); System.out.println("@Component value is " + annotationAttributes.get("value")); } } } ================================================ FILE: spring-metadata/spring-metadata-annotationMetadata/src/main/java/com/xcs/spring/AnnotationMetadataDemoByReflection.java ================================================ package com.xcs.spring; import com.xcs.spring.bean.MyBean; import org.springframework.core.type.AnnotationMetadata; import org.springframework.stereotype.Component; import java.util.Map; /** * @author xcs * @date 2023年10月31日 16时17分 **/ public class AnnotationMetadataDemoByReflection { public static void main(String[] args) throws Exception { // 获取 AnnotationMetadata AnnotationMetadata annotationMetadata = AnnotationMetadata.introspect(MyBean.class); System.out.println("AnnotationMetadata impl class is " + annotationMetadata.getClass()); // 检查 MyBean 类是否被 @Component 注解标记 boolean isComponent = annotationMetadata.hasAnnotation(Component.class.getName()); System.out.println("MyBean is a @Component: " + isComponent); // 获取 MyBean 类上的注解属性 if (isComponent) { Map annotationAttributes = annotationMetadata.getAnnotationAttributes(Component.class.getName()); System.out.println("@Component value is " + annotationAttributes.get("value")); } } } ================================================ FILE: spring-metadata/spring-metadata-annotationMetadata/src/main/java/com/xcs/spring/bean/MyBean.java ================================================ package com.xcs.spring.bean; import org.springframework.stereotype.Component; /** * @author xcs * @date 2023年10月31日 16时21分 **/ @Component("myBean") public class MyBean { } ================================================ FILE: spring-metadata/spring-metadata-condition/README.md ================================================ ## Condition - [Condition](#condition) - [一、基本信息](#一基本信息) - [二、知识储备](#二知识储备) - [三、基本描述](#三基本描述) - [四、主要功能](#四主要功能) - [五、接口源码](#五接口源码) - [六、主要实现](#六主要实现) - [七、最佳实践](#七最佳实践) - [八、与其他组件的关系](#八与其他组件的关系) - [九、常见问题](#九常见问题) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、知识储备 1. **Resource接口** - [Resource](https://github.com/xuchengsheng/spring-reading/blob/master/spring-resources/spring-resource) 是用于访问资源的抽象接口。资源可以是文件、类路径中的文件、URL 等等。我们需要了解如何使用 `Resource` 接口来获取资源的输入流、文件路径等信息。 2. **AnnotationMetadata接口** - [AnnotationMetadata](https://github.com/xuchengsheng/spring-reading/tree/master/spring-metadata/spring-metadata-annotationMetadata) 是Spring 框架中用于处理类上的注解信息的接口,它提供了对类上注解信息的访问和操作方法。 3. **MetadataReader接口** - [MetadataReader](https://github.com/xuchengsheng/spring-reading/tree/master/spring-metadata/spring-metadata-metadataReader)是Spring 提供的一个接口,用于读取类的元数据信息。它可以用于扫描类文件,获取类的基本信息,如类名、类的注解等。在注解驱动的开发中,`MetadataReader` 通常用于扫描包中的类,并从这些类中提取注解信息,以便配置 Spring Bean。 4. **路径和模式解析** - Spring 中的路径解析,特别是使用 ant 风格的路径模式,例如 `classpath*:com/xcs/spring/bean/**/*.xml`。 ### 三、基本描述 `Condition` 接口的实现类通常用于`@Conditional`注解,该注解可以用在类级别或方法级别,通过判断条件是否满足,来确定是否应该创建Bean或应用特定的配置。这样的机制使得Spring应用更加灵活,能够根据不同的条件选择性地进行配置。 ### 四、主要功能 1. **动态决定Bean的创建** + `Condition` 接口的实现类通过实现 `matches` 方法,可以根据运行时的条件判断逻辑,决定是否满足条件,从而决定是否创建特定的Bean。 2. **条件化配置类的应用** + 通过 `@Conditional` 注解,`Condition` 接口的实现类可以用于配置类或者配置类中的方法上,从而条件化地应用或排除某个配置。 3. **与环境变量和系统属性结合使用** + 可以结合 `ConditionContext` 中的环境变量和系统属性,编写更加复杂的条件判断逻辑,使得Bean的创建或配置的应用更加灵活。 4. **支持自定义条件逻辑** + 可以根据应用的具体需求自定义实现 `Condition` 接口,实现灵活的条件逻辑,例如基于操作系统、特定的类是否存在于类路径等条件。 5. **提高应用的灵活性** + 通过条件化机制,可以在不同的环境中配置不同的Bean或应用不同的配置,从而提高应用的灵活性和可配置性。 ### 五、接口源码 `Condition`接口要求实现类通过`matches`方法在组件注册前进行条件匹配,可根据运行时条件动态决定是否注册。该接口的实现类可用于`@Conditional`注解,用于灵活控制Bean的注册,遵循与`BeanFactoryPostProcessor`相同的限制。特别强调条件不应与Bean实例进行交互。为更精细地控制条件与`@Configuration` bean的交互,建议考虑实现`ConfigurationCondition`接口。 ```java /** * 一个用于组件注册的单一 condition,必须在组件注册之前 #matches 匹配。 * * 条件将在 bean 定义即将注册之前立即检查,并可以根据在此时能够确定的任何条件否决注册。 * * 条件必须遵循与 BeanFactoryPostProcessor 相同的限制,并注意永远不要与 bean 实例进行交互。 * 要更精细地控制与 @Configuration bean 交互的条件,请考虑实现 ConfigurationCondition 接口。 * * @author Phillip Webb * @since 4.0 * @see ConfigurationCondition * @see Conditional * @see ConditionContext */ @FunctionalInterface public interface Condition { /** * 确定条件是否匹配。 * @param context 条件上下文 * @param metadata 被检查的 org.springframework.core.type.AnnotationMetadata类或org.springframework.core.type.MethodMetadata方法的元数据 * @return true 如果条件匹配且组件可以注册,或 false 以否决带有注解组件的注册 */ boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata); } ``` ### 六、主要实现 1. **ProfileCondition** + 通过判断运行时的profile信息,决定是否注册组件。例如,可以根据激活的profile选择性地注册特定的Bean。 2. **OnBeanCondition** + 根据容器中是否存在某个特定类型的Bean来进行条件判断,决定是否注册组件。这个条件允许根据容器中的Bean的存在与否来决定是否创建某个Bean。 3. **OnClassCondition** + 判断类路径中是否存在某个特定的类,决定是否注册组件。通过检查类路径,可以根据类的存在与否来动态控制组件的注册。 4. **OnPropertyCondition** + 根据配置文件中的属性值来进行条件判断,决定是否注册组件。可以根据配置文件中的属性来灵活地配置组件的注册。 5. **ResourceCondition** + 判断类路径中是否存在指定资源文件,决定是否注册组件。类似于`OnClassCondition`,但是可以判断任意资源文件的存在与否。 ~~~mermaid classDiagram direction BT class Condition{ <> } class ConfigurationCondition{ <> } class SpringBootCondition { <> } class FilteringSpringBootCondition { <> } class OnBeanCondition { } class OnClassCondition { } class OnPropertyCondition { } class OnResourceCondition { } class ProfileCondition { } ConfigurationCondition ..|> Condition SpringBootCondition --|> Condition FilteringSpringBootCondition ..|> SpringBootCondition OnBeanCondition ..|> FilteringSpringBootCondition OnBeanCondition --|> ConfigurationCondition OnClassCondition ..|> FilteringSpringBootCondition OnPropertyCondition ..|> SpringBootCondition OnResourceCondition ..|> SpringBootCondition ProfileCondition --|> Condition ~~~ ### 七、最佳实践 结合自定义的条件类`MyOnClassCondition`,来判断类路径下的资源是否满足指定的条件。通过创建资源解析器和元数据读取器工厂,然后从`classpath*:com/xcs/spring/**/*.class`路径下获取所有类文件,最后使用自定义的条件类判断每个资源是否满足条件。根据条件的结果,输出相应的信息。 ```java public class ConditionDemo { public static void main(String[] args) throws IOException { // 创建资源解析器,用于获取匹配指定模式的资源 PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); // 创建MetadataReader工厂,用于读取类的元数据信息 SimpleMetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory(); // 获取指定模式下的所有资源 Resource[] resources = resolver.getResources("classpath*:com/xcs/spring/bean/**/*.class"); // 创建自定义条件类的实例,用于条件匹配 Condition condition = new MyOnClassCondition("com.xcs.spring.ConditionDemo"); // 遍历每个资源,判断是否满足自定义条件 for (Resource resource : resources) { // 获取资源对应的元数据读取器 MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource); // 判断资源是否满足自定义条件 if (condition.matches(null, metadataReader.getAnnotationMetadata())) { System.out.println(resource.getFile().getName() + "满足条件"); } else { System.out.println(resource.getFile().getName() + "不满足条件"); } } } } ``` 定义了一个 `MyOnClassCondition` 的自定义条件类,实现了 Spring 框架的 `Condition` 接口。该条件类的主要功能是根据指定的类名,在运行时动态地尝试加载该类,若加载成功则条件匹配,返回 `true`,否则捕获 `ClassNotFoundException` 表示类不存在,条件不匹配,返回 `false`。 ```java public class MyOnClassCondition implements Condition { private final String className; public MyOnClassCondition(String className) { this.className = className; } @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { try { // 尝试加载类 getClass().getClassLoader().loadClass(className); // 类存在,条件匹配 return true; } catch (ClassNotFoundException e) { // 类不存在,条件不匹配 return false; } } } ``` `MyBeanA`和`MyBeanB`为两个简单的Java类。 ```java package com.xcs.spring.bean; public class MyBeanA { } public class MyBeanB { } ``` 运行结果发现,当`com.xcs.spring.ConditionDemo`存在的情况下,`MyOnClassCondition` 类中的 `matches` 方法尝试加载 `com.xcs.spring.ConditionDemo` 类,若加载成功则条件匹配,返回 `true`。因此,`MyBeanA.class` 和 `MyBeanB.class` 均满足条件,成功注册。 ```java MyBeanA.class满足条件 MyBeanB.class满足条件 ``` 当我们修改为 `new MyOnClassCondition("com.xcs.spring.ConditionDemo1")` 后,即尝试加载 `com.xcs.spring.ConditionDemo1` 类。结果中的 "不满足条件" 表明 `MyOnClassCondition` 的 `matches` 方法返回了 `false`,即未成功加载相应的类。因此,`MyBeanA.class` 和 `MyBeanB.class` 均不满足条件。 ```java MyBeanA.class不满足条件 MyBeanB.class不满足条件 ``` ### 八、与其他组件的关系 1. **ConditionEvaluator** + `ConditionEvaluator` 提供了 `shouldSkip` 方法,该方法接受一个 `Condition` 对象和一个 `ConditionContext` 对象,用于判断是否应该跳过(即不注册)特定的组件,例如 Bean。主要在处理 `@Conditional` 注解时被使用,当 `@Conditional` 注解标注在类或方法上时,Spring 在进行组件注册时会通过 `ConditionEvaluator` 来判断是否满足条件,从而决定是否注册相应的组件。这一机制使得在应用上下文初始化之前能够动态地根据条件来决定是否注册特定的组件,为 Spring 应用提供了更灵活的组件注册策略。 ### 九、常见问题 1. **条件的匹配时机** + 我们可能会想知道条件的匹配是在什么时候发生的。条件是在 bean 的定义注册之前立即进行检查的,因此可以在 Spring 应用上下文初始化之前进行条件匹配。 2. **多个条件的组合** + 当需要组合多个条件时,我们可能会疑惑如何更灵活地应用多个条件。Spring 提供了一些组合条件的方式,例如 `@ConditionalOnProperty` 和 `@ConditionalOnExpression`。 3. **条件与配置的关系** + 我们可能会困惑条件和配置的关系。条件是用于决定是否注册组件的机制,而配置则是用于配置组件的属性和行为。 4. **自定义条件的实现** + 我们可能想要自定义条件以满足特定的业务需求。这需要实现 `Condition` 接口,并确保在 `matches` 方法中提供适当的条件逻辑。 5. **条件的错误处理** + 当条件判断出现错误时,例如 `matches` 方法中抛出异常,可能会导致注册组件的失败。我们需要注意条件逻辑中的异常处理,以确保不会影响到整个应用的启动。 6. **条件的性能考虑** + 如果应用中有大量的条件,可能会影响启动性能。我们需要谨慎设计条件逻辑,以确保在条件数量较多的情况下,应用的启动性能仍然可接受。 7. **条件的动态性** + 我们可能关心条件是否支持动态变化。条件是在应用启动时进行一次性的检查,通常不会在运行时动态变化。如果需要更动态的条件判断,可以考虑使用其他机制,如 `Environment` 中的属性。 ================================================ FILE: spring-metadata/spring-metadata-condition/pom.xml ================================================ spring-metadata com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-metadata-condition ================================================ FILE: spring-metadata/spring-metadata-condition/src/main/java/com/xcs/spring/ConditionDemo.java ================================================ package com.xcs.spring; import com.xcs.spring.condition.MyOnClassCondition; import org.springframework.context.annotation.Condition; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.SimpleMetadataReaderFactory; import java.io.IOException; public class ConditionDemo { public static void main(String[] args) throws IOException { // 创建资源解析器,用于获取匹配指定模式的资源 PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); // 创建MetadataReader工厂,用于读取类的元数据信息 SimpleMetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory(); // 获取指定模式下的所有资源 Resource[] resources = resolver.getResources("classpath*:com/xcs/spring/bean/**/*.class"); // 创建自定义条件类的实例,用于条件匹配 Condition condition = new MyOnClassCondition("com.xcs.spring.ConditionDemo1"); // 遍历每个资源,判断是否满足自定义条件 for (Resource resource : resources) { // 获取资源对应的元数据读取器 MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource); // 判断资源是否满足自定义条件 if (condition.matches(null, metadataReader.getAnnotationMetadata())) { System.out.println(resource.getFile().getName() + "满足条件"); } else { System.out.println(resource.getFile().getName() + "不满足条件"); } } } } ================================================ FILE: spring-metadata/spring-metadata-condition/src/main/java/com/xcs/spring/bean/MyBeanA.java ================================================ package com.xcs.spring.bean; /** * @author xcs * @date 2023年11月20日 16时01分 **/ public class MyBeanA { } ================================================ FILE: spring-metadata/spring-metadata-condition/src/main/java/com/xcs/spring/bean/MyBeanB.java ================================================ package com.xcs.spring.bean; /** * @author xcs * @date 2023年11月20日 16时01分 **/ public class MyBeanB { } ================================================ FILE: spring-metadata/spring-metadata-condition/src/main/java/com/xcs/spring/condition/MyOnClassCondition.java ================================================ package com.xcs.spring.condition; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata; /** * @author xcs * @date 2023年11月20日 15时24分 **/ public class MyOnClassCondition implements Condition { private final String className; public MyOnClassCondition(String className) { this.className = className; } @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { try { // 尝试加载类 getClass().getClassLoader().loadClass(className); // 类存在,条件匹配 return true; } catch (ClassNotFoundException e) { // 类不存在,条件不匹配 return false; } } } ================================================ FILE: spring-metadata/spring-metadata-metadataReader/README.md ================================================ ## MetadataReader - [MetadataReader](#metadatareader) - [一、知识储备](#一知识储备) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、接口源码](#四接口源码) - [五、主要实现](#五主要实现) - [六、最佳实践](#六最佳实践) - [七、与其他组件的关系](#七与其他组件的关系) - [八、常见问题](#八常见问题) ### 一、知识储备 1. **Resource接口** + 了解 Spring 的 `Resource` 接口,它是用于访问资源的抽象接口。资源可以是文件、类路径中的文件、URL 等等。我们需要了解如何使用 `Resource` 接口来获取资源的输入流、文件路径等信息。[点击查看Resource接口](https://github.com/xuchengsheng/spring-reading/blob/master/spring-resources/spring-resource) 2. **ResourceLoader接口** + 了解 Spring 的 `ResourceLoader` 接口,它是用于获取 `Resource` 对象的工厂。我们需要了解如何使用 `ResourceLoader` 接口来获取 `Resource` 对象,以便加载资源。[点击查看ResourceLoader接口](https://github.com/xuchengsheng/spring-reading/blob/master/spring-resources/spring-resource-resourceLoader) 3. **类路径扫描** + 了解如何使用 `Resource` 接口和 `ResourceLoader` 接口来扫描类路径上的资源,以获取类的元数据信息。 4. **Spring配置** + 熟悉如何配置 Spring 应用程序上下文,以便能够使用 `Resource` 和 `ResourceLoader`。这可能涉及到配置文件(如 XML 或 Java 配置类)的编写和加载。 5. **ASM(可选)** + 虽然不是必需的,但了解基本的ASM(字节码操作框架)知识可以帮助我们更好地理解 `MetadataReader` 的内部工作原理。Spring的`SimpleMetadataReader` 使用ASM来解析类文件。 ### 二、基本描述 `org.springframework.core.type.classreading.MetadataReader` 接口是 Spring 框架中用于读取和解析类文件元数据的核心接口之一。它的主要作用是允许应用程序获取有关类的元数据信息,包括类的名称、访问修饰符、接口、超类、注解等等。这些元数据信息可以在运行时用于实现各种高级功能,例如组件扫描、条件化注解处理、AOP(面向切面编程)等。 ### 三、主要功能 1. **获取类的基本信息** + `MetadataReader` 允许我们获取关于类的基本信息,包括类名、是否为接口、是否为抽象类、是否为注解类等。 2. **获取类上的注解信息** + 我们可以使用 `MetadataReader` 获取类上的注解信息,包括注解的类型、注解的属性值等。 3. **获取方法上的注解信息** + 通过 `MetadataReader`,我们可以获取类中方法的元数据信息,包括方法的名称、返回类型、是否为抽象方法,以及方法上的注解信息。 4. **获取类的成员类信息** + `MetadataReader` 提供了方法来获取类中声明的成员类(嵌套类或内部类)的名称。 5. **获取类的资源信息** + 我们可以使用 `MetadataReader` 获取与类关联的资源信息,例如类文件的路径。 6. **获取类的超类信息** + `MetadataReader` 允许我们获取类的超类信息,包括超类的名称和是否有超类。 ### 四、接口源码 `MetadataReader` 接口是 Spring 框架中的一个简单接口,用于访问类元数据。它提供了获取类文件资源、类的基本元数据和类上注解元数据的方法。这些元数据是由 ASM 中的 ClassReader 读取的。这个接口的主要用途是在 Spring 框架中处理类级别的元数据和注解,使我们能够访问和操作类的信息和注解信息。 ```java /** * MetadataReader 接口: * 提供了一种访问类元数据的简单接口,以便读取类信息,包括类的基本信息和类上的注解信息。 * 这些信息是由 ASM 中的 ClassReader 读取的。 * * @author Juergen Hoeller * @since 2.5 */ public interface MetadataReader { /** * 返回类文件的资源引用。 */ Resource getResource(); /** * 读取底层类的基本元数据。 */ ClassMetadata getClassMetadata(); /** * 读取底层类的完整注解元数据,包括类上的注解信息以及带注解的方法的元数据。 */ AnnotationMetadata getAnnotationMetadata(); } ``` ### 五、主要实现 1. `SimpleMetadataReader` + 用于从类文件中读取元数据。它使用 ASM 库的 `ClassReader` 来解析类文件并提取元数据。 ~~~mermaid classDiagram direction BT class MetadataReader { <> + getResource() : Resource + getClassMetadata() : ClassMetadata + getAnnotationMetadata() : AnnotationMetadata } class SimpleMetadataReader { } SimpleMetadataReader ..|> MetadataReader ~~~ ### 六、最佳实践 首先创建了一个`SimpleMetadataReaderFactory`的实例,它是用于创建`MetadataReader`对象的工厂。然后,通过`SimpleMetadataReaderFactory`创建了一个`MetadataReader`。接着,使用`MetadataReader`获取了目标类的基本信息,包括类名、类的类型(接口、注解、抽象类、普通类等)以及其他相关属性,以便深入了解类的结构。接下来,获取了类上的注解信息,包括注解的类型,以帮助了解类是否使用了特定的注解。最后,遍历类中带有指定注解的方法,获取方法的详细信息,例如方法名、声明方法的类名、返回类型等,以便执行自定义注解处理或特定方法的逻辑。 ```java public class MetadataReaderDemo { public static void main(String[] args) throws IOException { // 创建 MetadataReaderFactory SimpleMetadataReaderFactory readerFactory = new SimpleMetadataReaderFactory(); // 获取 MetadataReader MetadataReader metadataReader = readerFactory.getMetadataReader("com.xcs.spring.bean.MyBean"); // 获取类的基本信息 ClassMetadata classMetadata = metadataReader.getClassMetadata(); System.out.println("Class Name = " + classMetadata.getClassName()); System.out.println("Class IsInterface = " + classMetadata.isInterface()); System.out.println("Class IsAnnotation = " + classMetadata.isAnnotation()); System.out.println("Class IsAbstract = " + classMetadata.isAbstract()); System.out.println("Class IsConcrete = " + classMetadata.isConcrete()); System.out.println("Class IsFinal = " + classMetadata.isFinal()); System.out.println("Class IsIndependent = " + classMetadata.isIndependent()); System.out.println("Class HasEnclosingClass = " + classMetadata.hasEnclosingClass()); System.out.println("Class EnclosingClassName = " + classMetadata.getEnclosingClassName()); System.out.println("Class HasSuperClass = " + classMetadata.hasSuperClass()); System.out.println("Class SuperClassName = " + classMetadata.getSuperClassName()); System.out.println("Class InterfaceNames = " + Arrays.toString(classMetadata.getInterfaceNames())); System.out.println("Class MemberClassNames = " + Arrays.toString(classMetadata.getMemberClassNames())); System.out.println("Class Annotations: " + metadataReader.getAnnotationMetadata().getAnnotationTypes()); System.out.println(); // 获取方法上的注解信息 for (MethodMetadata methodMetadata : metadataReader.getAnnotationMetadata().getAnnotatedMethods("com.xcs.spring.annotation.MyAnnotation")) { System.out.println("Method Name: " + methodMetadata.getMethodName()); System.out.println("Method DeclaringClassName: " + methodMetadata.getDeclaringClassName()); System.out.println("Method ReturnTypeName: " + methodMetadata.getReturnTypeName()); System.out.println("Method IsAbstract: " + methodMetadata.isAbstract()); System.out.println("Method IsStatic: " + methodMetadata.isStatic()); System.out.println("Method IsFinal: " + methodMetadata.isFinal()); System.out.println("Method IsOverridable: " + methodMetadata.isOverridable()); System.out.println(); } } } ``` 定义了一个Java类`MyBean`,它继承了一个抽象类`MyAbstract`,并被标记为`@MyClassAnnotation`的类级别注解。`MyBean`类包含字段`key`和`value`,以及三个方法:`myMethod1`(静态方法,标记有`@MyAnnotation`注解)、`myMethod2`(实例方法,返回字符串,同样标记有`@MyAnnotation`注解)和`myMethod3`(普通实例方法)。此外,`MyBean`类定义了一个静态内部类`MyInnerClass`。这个代码我给大家展示了Java类的不同方面,包括继承、实现接口、字段、方法、注解以及内部类的使用。 ```java public abstract class MyAbstract { } @MyClassAnnotation public final class MyBean extends MyAbstract implements Serializable { public String key; public String value; @MyAnnotation public static void myMethod1() { } @MyAnnotation public String myMethod2() { return "hello world"; } public void myMethod3() { } public static class MyInnerClass { // 内部类的定义 } } ``` 定义了两个自定义注解:`MyAnnotation` 和 `MyClassAnnotation`。 ```java @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { String value() default ""; } @Retention(RetentionPolicy.RUNTIME) public @interface MyClassAnnotation { String value() default ""; } ``` 运行结果发现,使用 `MetadataReader` 可以获取类和方法的基本信息、注解元数据,实现自定义注解处理逻辑,识别不带特定注解的方法,深入了解类的结构,以及在运行时动态检查类的特性,为开发自定义框架、组件扫描和反射操作提供了强大的工具。但是,`MetadataReader`不会收集没有特定注解的方法(比如:`myMethod3`)。这意味着未被注解标记的方法将不会包含在元数据中。 ```java Class Name = com.xcs.spring.bean.MyBean Class IsInterface = false Class IsAnnotation = false Class IsAbstract = false Class IsConcrete = true Class IsFinal = true Class IsIndependent = true Class HasEnclosingClass = false Class EnclosingClassName = null Class HasSuperClass = true Class SuperClassName = com.xcs.spring.bean.MyAbstract Class InterfaceNames = [java.io.Serializable] Class MemberClassNames = [com.xcs.spring.bean.MyBean$MyInnerClass] Class Annotations: [com.xcs.spring.annotation.MyClassAnnotation] Method Name: myMethod1 Method DeclaringClassName: com.xcs.spring.bean.MyBean Method ReturnTypeName: void Method IsAbstract: false Method IsStatic: true Method IsFinal: false Method IsOverridable: false Method Name: myMethod2 Method DeclaringClassName: com.xcs.spring.bean.MyBean Method ReturnTypeName: java.lang.String Method IsAbstract: false Method IsStatic: false Method IsFinal: false Method IsOverridable: true ``` ### 七、与其他组件的关系 1. **`ClassPathBeanDefinitionScanner`** + `ClassPathBeanDefinitionScanner`使用 `MetadataReader` 类来实现组件扫描。组件扫描是一种机制,它自动发现并注册应用程序中的类,以便它们可以被Spring容器管理。`MetadataReader` 用于扫描类的元数据,以确定哪些类应该被注册为Spring组件,例如标记为 `@Component`、`@Service`、`@Repository` 等注解的类。 2. **`MetadataReaderFactory`** + `MetadataReaderFactory`是一个工厂类,用于创建 `MetadataReader` 实例。而`MetadataReader` 是一个接口,用于读取和解析类的元数据信息,包括类级别和方法级别的注解信息。`MetadataReaderFactory`负责处理类资源(如类文件或字节码),并将其包装成 `MetadataReader` 对象,以便进一步处理类的元数据。 ### 八、常见问题 1. **`MetadataReader` 与 `ClassPathBeanDefinitionScanner` 之间的关系是什么?** - `ClassPathBeanDefinitionScanner` 是 Spring 框架用于执行组件扫描的类,它使用 `MetadataReader` 来扫描指定包中的类,识别哪些类应该被注册为 Spring Bean。 2. **在 Spring 中的哪些场景中使用 `MetadataReader`?** - `MetadataReader` 在 Spring 中主要用于组件扫描、AOP、注解处理、Bean后处理等场景中,以读取和处理类的元数据信息。 3. **为什么使用 `MetadataReader` 而不是反射?** - 使用 `MetadataReader` 通常比反射更高效,因为它直接读取字节码,而不需要加载整个类。此外,它允许进行更高级的元数据分析和自定义操作。 4. **有哪些常见的 `MetadataReader` 的实现类?** - Spring 框架提供了多个 `MetadataReader` 的实现类,最常见的是 `SimpleMetadataReader`。它们用于从类文件和注解信息中读取元数据。 5. **如何自定义 `MetadataReader` 的使用?** - 我们可以编写自定义的元数据读取器,实现 `MetadataReader` 接口,以满足特定需求,例如自定义注解处理、特殊的类加载逻辑等。 ================================================ FILE: spring-metadata/spring-metadata-metadataReader/pom.xml ================================================ spring-metadata com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-metadata-metadataReader ================================================ FILE: spring-metadata/spring-metadata-metadataReader/src/main/java/com/xcs/spring/MetadataReaderDemo.java ================================================ package com.xcs.spring; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.core.annotation.MergedAnnotation; import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.ClassMetadata; import org.springframework.core.type.MethodMetadata; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.SimpleMetadataReaderFactory; import java.io.IOException; import java.lang.annotation.Annotation; import java.util.Arrays; /** * @author xcs * @date 2023年10月31日 10时52分 **/ public class MetadataReaderDemo { public static void main(String[] args) throws IOException { // 创建 MetadataReaderFactory SimpleMetadataReaderFactory readerFactory = new SimpleMetadataReaderFactory(); // 获取 MetadataReader,通常由 Spring 容器自动创建 MetadataReader metadataReader = readerFactory.getMetadataReader("com.xcs.spring.bean.MyBean"); // 获取类的基本信息 ClassMetadata classMetadata = metadataReader.getClassMetadata(); System.out.println("Class Name = " + classMetadata.getClassName()); System.out.println("Class IsInterface = " + classMetadata.isInterface()); System.out.println("Class IsAnnotation = " + classMetadata.isAnnotation()); System.out.println("Class IsAbstract = " + classMetadata.isAbstract()); System.out.println("Class IsConcrete = " + classMetadata.isConcrete()); System.out.println("Class IsFinal = " + classMetadata.isFinal()); System.out.println("Class IsIndependent = " + classMetadata.isIndependent()); System.out.println("Class HasEnclosingClass = " + classMetadata.hasEnclosingClass()); System.out.println("Class EnclosingClassName = " + classMetadata.getEnclosingClassName()); System.out.println("Class HasSuperClass = " + classMetadata.hasSuperClass()); System.out.println("Class SuperClassName = " + classMetadata.getSuperClassName()); System.out.println("Class InterfaceNames = " + Arrays.toString(classMetadata.getInterfaceNames())); System.out.println("Class MemberClassNames = " + Arrays.toString(classMetadata.getMemberClassNames())); System.out.println("Class Annotations: " + metadataReader.getAnnotationMetadata().getAnnotationTypes()); System.out.println(); // 获取方法上的注解信息 for (MethodMetadata methodMetadata : metadataReader.getAnnotationMetadata().getAnnotatedMethods("com.xcs.spring.annotation.MyAnnotation")) { System.out.println("Method Name: " + methodMetadata.getMethodName()); System.out.println("Method DeclaringClassName: " + methodMetadata.getDeclaringClassName()); System.out.println("Method ReturnTypeName: " + methodMetadata.getReturnTypeName()); System.out.println("Method IsAbstract: " + methodMetadata.isAbstract()); System.out.println("Method IsStatic: " + methodMetadata.isStatic()); System.out.println("Method IsFinal: " + methodMetadata.isFinal()); System.out.println("Method IsOverridable: " + methodMetadata.isOverridable()); System.out.println(); } } } ================================================ FILE: spring-metadata/spring-metadata-metadataReader/src/main/java/com/xcs/spring/annotation/MyAnnotation.java ================================================ package com.xcs.spring.annotation; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { String value() default ""; } ================================================ FILE: spring-metadata/spring-metadata-metadataReader/src/main/java/com/xcs/spring/annotation/MyClassAnnotation.java ================================================ package com.xcs.spring.annotation; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) public @interface MyClassAnnotation { String value() default ""; } ================================================ FILE: spring-metadata/spring-metadata-metadataReader/src/main/java/com/xcs/spring/bean/MyAbstract.java ================================================ package com.xcs.spring.bean; /** * @author xcs * @date 2023年10月31日 11时29分 **/ public abstract class MyAbstract { } ================================================ FILE: spring-metadata/spring-metadata-metadataReader/src/main/java/com/xcs/spring/bean/MyBean.java ================================================ package com.xcs.spring.bean; import com.xcs.spring.annotation.MyAnnotation; import com.xcs.spring.annotation.MyClassAnnotation; import java.io.Serializable; /** * @author xcs * @date 2023年10月31日 10时53分 **/ @MyClassAnnotation public final class MyBean extends MyAbstract implements Serializable { public String key; public String value; @MyAnnotation public static void myMethod1() { // 方法1的实现 } @MyAnnotation public String myMethod2() { return "hello world"; } public void myMethod3() { // 方法3的实现 } public static class MyInnerClass { // 内部类的定义 } } ================================================ FILE: spring-metadata/spring-metadata-typeFilter/README.md ================================================ ## TypeFilter - [TypeFilter](#typefilter) - [一、基本信息](#一基本信息) - [二、知识储备](#二知识储备) - [三、基本描述](#三基本描述) - [四、主要功能](#四主要功能) - [五、接口源码](#五接口源码) - [六、主要实现](#六主要实现) - [七、最佳实践](#七最佳实践) - [八、与其他组件的关系](#八与其他组件的关系) - [九、常见问题](#九常见问题) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、知识储备 1. **Resource接口** + [Resource](https://github.com/xuchengsheng/spring-reading/blob/master/spring-resources/spring-resource) 是用于访问资源的抽象接口。资源可以是文件、类路径中的文件、URL 等等。我们需要了解如何使用 `Resource` 接口来获取资源的输入流、文件路径等信息。 2. **AnnotationMetadata接口** + [AnnotationMetadata](https://github.com/xuchengsheng/spring-reading/tree/master/spring-metadata/spring-metadata-annotationMetadata) 是Spring 框架中用于处理类上的注解信息的接口,它提供了对类上注解信息的访问和操作方法。 3. **MetadataReader接口** + [MetadataReader](https://github.com/xuchengsheng/spring-reading/tree/master/spring-metadata/spring-metadata-metadataReader)是Spring 提供的一个接口,用于读取类的元数据信息。它可以用于扫描类文件,获取类的基本信息,如类名、类的注解等。在注解驱动的开发中,`MetadataReader` 通常用于扫描包中的类,并从这些类中提取注解信息,以便配置 Spring Bean。 4. **路径和模式解析** + Spring 中的路径解析,特别是使用 ant 风格的路径模式,例如 `classpath*:com/xcs/spring/**/*.xml`。 ### 三、基本描述 `TypeFilter` 是 Spring 框架中的一个接口,用于在组件扫描期间对类进行筛选。通过实现 `match` 方法,我们可以定义自己的逻辑,决定哪些类应该被包含在组件扫描的结果中,而哪些类应该被排除。这一灵活的机制在spring中的 `@ComponentScan` 注解时被使用,可以通过自定义的 `TypeFilter` 对类进行精确的过滤,满足复杂应用程序结构或特定条件下的组件管理需求。Spring 提供了多个内置的 `TypeFilter` 实现,如 `AnnotationTypeFilter` 和 `AssignableTypeFilter`,用于基于注解或类型进行筛选。 ### 四、主要功能 1. **自定义过滤逻辑** + 我们可以实现 `TypeFilter` 接口,通过覆盖 `match` 方法定义自己的过滤逻辑。这使得可以根据特定的条件,如类的注解、实现的接口或继承关系等,来决定类是否应该被包含在组件扫描的结果中。 2. **组件扫描定制** + 在使用 `@ComponentScan` 注解配置类时,可以通过设置 `includeFilters` 和 `excludeFilters` 属性,传入自定义的 `TypeFilter` 实例,从而定制组件扫描的规则。这样可以更精确地控制哪些类应该被纳入 Spring 容器的管理,哪些类应该被排除。 3. **适应复杂应用结构** + 对于复杂的应用结构,可能存在不同模块或层次的类,而我们可能只想要将特定模块或层次的类纳入 Spring 容器。通过自定义 `TypeFilter`,可以根据项目的实际结构,有选择地将类包含或排除。 4. **灵活性和可扩展性** + `TypeFilter` 提供了一种灵活的机制,使得我们可以根据特定需求扩展和定制组件扫描的行为。这种灵活性对于需要动态适应不同场景的应用程序是非常有用的。 ### 五、接口源码 `TypeFilter`接口,作为组件扫描过程中自定义类过滤器的基础。该接口包含一个`match`方法,通过传入`MetadataReader`和`MetadataReaderFactory`,我们可以实现自定义的过滤逻辑,决定哪些类应该被包含在组件扫描结果中,从而增强了Spring框架在应用初始化时对类的筛选和管理的灵活性。 ```java /** * 使用 org.springframework.core.type.classreading.MetadataReader 的类型过滤器的基础接口。 * * @author Costin Leau * @author Juergen Hoeller * @author Mark Fisher * @since 2.5 */ @FunctionalInterface public interface TypeFilter { /** * 确定该过滤器是否与给定元数据描述的类匹配。 * * @param metadataReader 目标类的元数据读取器 * @param metadataReaderFactory 用于获取其他类的元数据读取器的工厂(例如超类和接口) * @return 是否匹配该过滤器 * @throws IOException 在读取元数据时发生 I/O 失败时抛出异常 */ boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException; } ``` ### 六、主要实现 1. **AnnotationTypeFilter(基于注解的过滤器)** + 匹配带有指定注解的类。在组件扫描期间,通过比对类的注解信息,确定是否将该类包含在扫描结果中。 2. **AssignableTypeFilter(基于类型的过滤器):** + 匹配指定类型的子类或实现类。通过与目标类的继承关系比对,确定是否将该类纳入组件扫描的结果中。 3. **AspectJTypeFilter(基于AspectJ表达式的过滤器):** + 使用AspectJ表达式进行匹配。它允许通过编写AspectJ风格的表达式,灵活地选择需要被扫描的类。 4. **RegexPatternTypeFilter(基于正则表达式的过滤器):** + 使用正则表达式来匹配类的名称。通过提供一个正则表达式,决定是否将符合条件的类包含在组件扫描的结果中。 ~~~mermaid classDiagram direction BT class TypeFilter{ <> } class AbstractTypeHierarchyTraversingFilter { <> } class AbstractClassTestingTypeFilter { <> } class AnnotationTypeFilter { } class AssignableTypeFilter { } class AspectJTypeFilter { } class RegexPatternTypeFilter { } AbstractTypeHierarchyTraversingFilter ..|> TypeFilter AnnotationTypeFilter --|> AbstractTypeHierarchyTraversingFilter AssignableTypeFilter --|> AbstractTypeHierarchyTraversingFilter AspectJTypeFilter ..|> TypeFilter AbstractClassTestingTypeFilter ..|> TypeFilter RegexPatternTypeFilter --|> AbstractClassTestingTypeFilter ~~~ ### 七、最佳实践 使用 Spring 框架的 `TypeFilter` 进行类的筛选过程。通过创建路径匹配的资源模式解析器和元数据读取器工厂,以及定义一个注解类型过滤器(我们最佳实践中为 `MyAnnotation`),然后从`classpath*:com/xcs/spring/**/*.class`路径下获取所有类文件,并筛选出带有指定注解的类。遍历扫描到的类文件,输出文件名以及注解类型过滤的结果。 ```java public class TypeFilterDemo { public static void main(String[] args) throws IOException { // 创建路径匹配的资源模式解析器 PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); // 创建一个简单的元数据读取器工厂 SimpleMetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory(); // 创建一个注解类型过滤器,用于匹配带有 MyAnnotation 注解的类 TypeFilter annotationTypeFilter = new AnnotationTypeFilter(MyAnnotation.class); // 使用资源模式解析器获取所有匹配指定路径的类文件 Resource[] resources = resolver.getResources("classpath*:com/xcs/spring/**/*.class"); // 遍历扫描到的类文件 for (Resource resource : resources) { // 获取元数据读取器 MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource); // 使用注解类型过滤器匹配当前类 boolean match = annotationTypeFilter.match(metadataReader, metadataReaderFactory); // 输出扫描到的文件名和匹配结果 System.out.printf("扫描到的文件: %-20s || 筛选器是否匹配: %s%n", resource.getFile().getName(), match); } } } ``` `MyAnnotation`注解被用作 `TypeFilter` 中的匹配条件,用于过滤带有此注解的类。 ```java @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface MyAnnotation { } ``` 定义了一个名为 `MyComponent` 的类,并使用了 `MyAnnotation` 注解。 ```java package com.xcs.spring.component; @MyAnnotation public class MyComponent { } ``` 定义了一个名为 `MyController` 的类,但是该类没有使用任何自定义注解。 ```java package com.xcs.spring.controller; public class MyController { } ``` 定义了一个名为 `MyRepository` 的类,但是该类没有使用任何自定义注解。 ```java package com.xcs.spring.repository; public class MyRepository { } ``` 定义了一个名为 `MyService` 的类,但是该类没有使用任何自定义注解。 ```java package com.xcs.spring.service; public class MyService { } ``` 运行结果发现,可以看出 `MyController`、`MyRepository`、`MyService`并没有匹配到注解类型过滤器,而带有 `MyAnnotation` 注解的类 `MyComponent` 成功匹配。这符合预期,证明了注解类型过滤器在筛选类时的有效性。 ```java 扫描到的文件: TypeFilterDemo.class || 筛选器是否匹配: false 扫描到的文件: MyAnnotation.class || 筛选器是否匹配: false 扫描到的文件: MyComponent.class || 筛选器是否匹配: true 扫描到的文件: MyController.class || 筛选器是否匹配: false 扫描到的文件: MyRepository.class || 筛选器是否匹配: false 扫描到的文件: MyService.class || 筛选器是否匹配: false ``` ### 八、与其他组件的关系 1. **ClassPathScanningCandidateComponentProvider** + `registerDefaultFilters` 方法是 `ClassPathScanningCandidateComponentProvider` 类的一个方法,用于注册默认的类型过滤器。这个方法在组件扫描时被调用,它会添加一些默认的过滤器到 `includeFilters` 集合中,这些过滤器包括,用于扫描带有 `@Component` 注解的类,以及JSR 规范中的一些注解类型过滤器,如 `ManagedBean` 和 `Named` 注解。 2. **ComponentScanAnnotationParser** + `typeFiltersFor`方法是 `ComponentScanAnnotationParser` 类的一个私有方法,用于根据 `@ComponentScan` 注解中的属性信息创建类型过滤器列表。根据不同的过滤器类型(如注解类型、可分配类型、自定义类型等),它会创建对应的 `TypeFilter` 实例并添加到列表中。 3. **ComponentScanBeanDefinitionParser** + `createTypeFilter` 方法属于 `ComponentScanBeanDefinitionParser` 类,用于在解析 `` 元素时根据 XML 配置信息创建类型过滤器。它根据配置中的 `filter-type` 属性和 `filter-expression` 属性,动态选择创建相应的类型过滤器。支持的过滤器类型包括注解类型、可分配类型、AspectJ 表达式、正则表达式等。 ### 九、常见问题 1. **无法正确匹配类** + 需要确保过滤器的匹配条件(如注解、类型、正则表达式等)与目标类的实际情况一致。检查过滤器的实例化和使用是否正确。 2. **自定义的 TypeFilter 不生效** + 确保自定义的 `TypeFilter` 实现正确并且被正确地配置。检查实现中的匹配逻辑是否符合预期。 3. **包扫描结果为空** + 检查包路径是否正确,确保过滤器条件与目标类匹配。也可以检查类加载器是否正确,以确保可以加载目标类。 4. **多个 TypeFilter 失效** + 确保多个 `TypeFilter` 的使用场景和条件不重叠,否则可能会出现只有一个过滤器生效的情况。 5. **AspectJ 表达式匹配失败:** + 确保 AspectJ 表达式正确,并且类加载器可访问相关的类。并检查 `AspectJTypeFilter` 的构造函数中的类加载器是否正确。 6. **性能问题:** + 在大型项目中,使用 `TypeFilter` 导致性能问题。考虑优化过滤器的实现,或者在适当的情况下缓存扫描结果。可以使用缓存或其他优化技术来减轻性能问题。 ================================================ FILE: spring-metadata/spring-metadata-typeFilter/pom.xml ================================================ spring-metadata com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-metadata-typeFilter ================================================ FILE: spring-metadata/spring-metadata-typeFilter/src/main/java/com/xcs/spring/TypeFilterDemo.java ================================================ package com.xcs.spring; import com.xcs.spring.annotation.MyAnnotation; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.type.classreading.CachingMetadataReaderFactory; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.SimpleMetadataReaderFactory; import org.springframework.core.type.filter.AnnotationTypeFilter; import org.springframework.core.type.filter.TypeFilter; import java.io.IOException; /** * @author xcs * @date 2023年11月20日 10时34分 **/ public class TypeFilterDemo { public static void main(String[] args) throws IOException { // 创建路径匹配的资源模式解析器 PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); // 创建一个简单的元数据读取器工厂 SimpleMetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory(); // 创建一个注解类型过滤器,用于匹配带有 MyAnnotation 注解的类 TypeFilter annotationTypeFilter = new AnnotationTypeFilter(MyAnnotation.class); // 使用资源模式解析器获取所有匹配指定路径的类文件 Resource[] resources = resolver.getResources("classpath*:com/xcs/spring/**/*.class"); // 遍历扫描到的类文件 for (Resource resource : resources) { // 获取元数据读取器 MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource); // 使用注解类型过滤器匹配当前类 boolean match = annotationTypeFilter.match(metadataReader, metadataReaderFactory); // 输出扫描到的文件名和匹配结果 System.out.printf("扫描到的文件: %-20s || 筛选器是否匹配: %s%n", resource.getFile().getName(), match); } } } ================================================ FILE: spring-metadata/spring-metadata-typeFilter/src/main/java/com/xcs/spring/annotation/MyAnnotation.java ================================================ package com.xcs.spring.annotation; import java.lang.annotation.*; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface MyAnnotation { } ================================================ FILE: spring-metadata/spring-metadata-typeFilter/src/main/java/com/xcs/spring/component/MyComponent.java ================================================ package com.xcs.spring.component; import com.xcs.spring.annotation.MyAnnotation; /** * @author xcs * @date 2023年11月20日 10时51分 **/ @MyAnnotation public class MyComponent { } ================================================ FILE: spring-metadata/spring-metadata-typeFilter/src/main/java/com/xcs/spring/controller/MyController.java ================================================ package com.xcs.spring.controller; /** * @author xcs * @date 2023年11月20日 10时41分 **/ public class MyController { } ================================================ FILE: spring-metadata/spring-metadata-typeFilter/src/main/java/com/xcs/spring/repository/MyRepository.java ================================================ package com.xcs.spring.repository; /** * @author xcs * @date 2023年10月07日 11时51分 **/ public class MyRepository { } ================================================ FILE: spring-metadata/spring-metadata-typeFilter/src/main/java/com/xcs/spring/service/MyService.java ================================================ package com.xcs.spring.service; /** * @author xcs * @date 2023年11月20日 10时40分 **/ public class MyService { } ================================================ FILE: spring-mvc/pom.xml ================================================ spring-reading com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-mvc ================================================ FILE: spring-resources/pom.xml ================================================ spring-reading com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-resources pom spring-resource spring-resource-resourceLoader spring-resource-documentLoader spring-resource-resourcePatternResolver ================================================ FILE: spring-resources/spring-resource/README.md ================================================ ## Resource - [Resource](#resource) - [一、基本信息](#一基本信息) - [二、知识储备](#二知识储备) - [三、基本描述](#三基本描述) - [四、主要功能](#四主要功能) - [五、接口源码](#五接口源码) - [六、主要实现](#六主要实现) - [七、最佳实践](#七最佳实践) - [八、与其他组件的关系](#八与其他组件的关系) - [九、常见问题](#九常见问题) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、知识储备 1. **I/O知识** + 了解文件、路径、输入/输出流等基础概念。 2. **类路径(Classpath)** + 了解什么是类路径,以及如何从类路径中加载资源。 3. **URL和URI概念** + 这对于理解如何从网络或其他协议中加载资源是必要的。 ### 三、基本描述 `Resource` 是 Spring 框架中用于简化和统一对底层资源(如文件、classpath 资源、URL 等)的访问的一个核心接口。它为不同来源的资源提供了一个共同的抽象,并隐藏了具体资源访问的细节。在 Java 开发中,资源的访问是常见的需求,如读取配置文件、图片、音频等。但 Java 的标准库为不同类型的资源提供了不同的访问机制:例如,对于文件系统中的资源,我们可能使用 `java.io.File`;对于 classpath 中的资源,我们可能使用 `ClassLoader` 的 `getResource` 或 `getResourceAsStream` 方法;对于网络资源,我们可能使用 `java.net.URL`。这些不同的机制意味着我们需要了解和使用多种方式来访问资源,这导致的问题是代码复杂性增加、重复代码以及可能的错误。为了提供一个统一、简化和更高级的资源访问机制,Spring 框架引入了 `Resource` 接口,这个接口为所有的资源提供了一个统一的抽象。 ### 四、主要功能 1. **统一的资源抽象** + 无论资源来自于文件系统、classpath、URL 还是其他来源,`Resource` 接口都为其提供了一个统一的抽象。 2. **资源描述** + 通过 `getDescription()` 方法,每个 `Resource` 实现都可以为其所代表的底层资源提供描述性信息,这对于错误处理和日志记录特别有用。 3. **读取能力** + `Resource` 提供了 `getInputStream()` 方法,允许直接读取资源内容,而无需关心资源的实际来源。 4. **存在性与可读性** + `Resource` 提供了 `exists()` 和 `isReadable()` 方法来确定资源是否存在及其是否可读。 5. **开放性检查** + `isOpen()` 方法用于检查资源是否表示一个已经打开的流,这有助于避免重复读取流资源。 6. **URI 和 URL 访问** + `Resource` 允许通过 `getURI()` 和 `getURL()` 方法获取其底层资源的 URI 和 URL,这为进一步的资源处理提供了可能。 7. **文件访问** + 当资源代表一个文件系统中的文件时,可以通过 `getFile()` 直接访问该文件。 8. **多种实现** + Spring 提供了多种 `Resource` 的实现,以支持不同来源的资源,如 `ClassPathResource`、`FileSystemResource` 和 `UrlResource` 等。 ### 五、接口源码 `InputStreamSource` 是一个简单的接口,用于提供一个输入流。它被设计为可以多次返回一个新的、未读取的输入流,这对于那些需要多次读取输入流的API。 ```java /** * 表示可以提供输入流的资源或对象的接口。 */ public interface InputStreamSource { /** * 返回基础资源内容的 InputStream。 * 期望每次调用都会创建一个新的流。 * 当我们考虑到像 JavaMail 这样的API时,这个要求尤为重要,因为在创建邮件附件时,JavaMail需要能够多次读取流。对于这样的用例,要求每个 getInputStream() 调用都返回一个新的流。 * @return 基础资源的输入流(不能为 null) * @throws java.io.FileNotFoundException 如果基础资源不存在 * @throws IOException 如果无法打开内容流 */ InputStream getInputStream() throws IOException; } ``` `Resource` 是Spring框架中的核心接口,代表了外部或内部的资源,如文件、类路径资源、URL资源等。它为访问底层资源提供了一个统一的抽象,从而使得代码可以独立于实际资源的类型。 ```java /** * 用于描述资源的接口,该接口抽象了底层资源的实际类型,如文件或类路径资源。 * *

对于每个资源,如果它在物理形式上存在,都可以打开一个输入流,但只有某些资源才能返回 URL 或文件句柄。具体行为取决于其实现。 */ public interface Resource extends InputStreamSource { /** * 判断此资源是否在物理形式上真正存在。 */ boolean exists(); /** * 指示是否可以通过 {@link #getInputStream()} 读取此资源的非空内容。 * 实际的内容读取可能仍然失败。 */ default boolean isReadable() { return exists(); } /** * 指示此资源是否代表一个打开的流的句柄。 * 如果为 true,则输入流不能被多次读取,并且在读取后必须被关闭,以避免资源泄露。 */ default boolean isOpen() { return false; } /** * 判断此资源是否代表文件系统中的文件。 */ default boolean isFile() { return false; } /** * 返回此资源的 URL 句柄。 */ URL getURL() throws IOException; /** * 返回此资源的 URI 句柄。 */ URI getURI() throws IOException; /** * 返回此资源的文件句柄。 */ File getFile() throws IOException; /** * 返回一个 {@link ReadableByteChannel}。 */ default ReadableByteChannel readableChannel() throws IOException { return Channels.newChannel(getInputStream()); } /** * 确定此资源的内容长度。 */ long contentLength() throws IOException; /** * 确定此资源的最后修改时间戳。 */ long lastModified() throws IOException; /** * 创建相对于此资源的资源。 */ Resource createRelative(String relativePath) throws IOException; /** * 返回此资源的文件名。 */ @Nullable String getFilename(); /** * 返回此资源的描述,用于在处理资源时的错误输出。 */ String getDescription(); } ``` ### 六、主要实现 1. **`ClassPathResource`** + 用于加载 classpath 下的资源。 2. **`FileSystemResource`** + 用于访问文件系统中的资源。 3. **`UrlResource`** + 用于基于 URL 的资源。 4. **`ServletContextResource`** + 用于 Web 应用中的资源。 5. **`ByteArrayResource`** & **`InputStreamResource`** + 基于内存和流的资源表示。 ~~~mermaid classDiagram direction BT class InputStreamSource{ <> + getInputStream(): InputStream } class Resource { <> + exists(): boolean + getDescription(): String + 其他方法() } class AbstractResource { <> } class ByteArrayResource { } class InputStreamResource { } class ClassPathResource { } class UrlResource { } class ServletContextResource { } class AbstractFileResolvingResource { <> } class FileSystemResource { } Resource ..|> InputStreamSource AbstractResource --|> Resource ByteArrayResource ..|> AbstractResource InputStreamResource ..|> AbstractResource AbstractFileResolvingResource ..|> AbstractResource FileSystemResource ..|> AbstractResource UrlResource ..|> AbstractFileResolvingResource ClassPathResource ..|> AbstractFileResolvingResource ServletContextResource ..|> AbstractFileResolvingResource ~~~ ### 七、最佳实践 **`ClassPathResource`** 使用`ClassPathResource` 是 Spring 框架中的一个组件,用于访问类路径上的资源。在此示例中,`path` 变量存储了类路径资源的路径。这里,文件位于 `src/main/resources/application.properties` 路径目录。 ```java public class ClassPathResourceDemo { public static void main(String[] args) throws Exception { String path = "application.properties"; Resource resource = new ClassPathResource(path); try (InputStream is = resource.getInputStream()) { // 读取和处理资源内容 System.out.println(new String(is.readAllBytes())); } } } ``` **`FileSystemResource`** 使用 `FileSystemResource` 是 Spring 框架中的一个组件,用于访问文件系统上的文件。在此示例中,`path` 变量存储了文件的完整路径。这个路径需要被替换为我们自己的有效文件路径。 ```java public class FileSystemResourceDemo { public static void main(String[] args) throws Exception { // 请替换我们自己的目录 String path = "D:\\idea-work-space-xcs\\spring-reading\\spring-resources\\spring-resource\\myfile.txt"; Resource resource = new FileSystemResource(path); try (InputStream is = resource.getInputStream()) { // 读取和处理资源内容 System.out.println(new String(is.readAllBytes())); } } } ``` **`UrlResource`** 使用`UrlResource` 类是 Spring 框架中的一个组件,用于访问网络上的资源。在这个示例中,它被用来读取一个在线的文本文件。 ```java public class UrlResourceDemo { public static void main(String[] args) throws Exception { Resource resource = new UrlResource("https://dist.apache.org/repos/dist/test/test.txt"); try (InputStream is = resource.getInputStream()) { // 读取和处理资源内容 System.out.println(new String(is.readAllBytes())); } } } ``` **`ByteArrayResource`** 使用`ByteArrayResource` 是 Spring 框架中的一个组件,用于访问字节数组作为资源。在这个示例中,它被用来读取一个简单的字符串 `"hello world"`。 ```java public class ByteArrayResourceDemo { public static void main(String[] args) throws Exception { byte[] data = "hello world".getBytes(); Resource resource = new ByteArrayResource(data); try (InputStream is = resource.getInputStream()) { // 读取和处理资源内容 System.out.println(new String(is.readAllBytes())); } } } ``` **`InputStreamResource`** 使用`InputStreamResource` 是 Spring 框架中的一个组件,用于将任何 `InputStream` 包装为 Spring 的 `Resource`。在这个示例中,使用字符串 "hello world" 的字节来创建一个 `ByteArrayInputStream`。这样我们就有了一个可以读取 "hello world" 的输入流。 ```java public class InputStreamResourceDemo { public static void main(String[] args) throws Exception { InputStream isSource = new ByteArrayInputStream("hello world".getBytes()); Resource resource = new InputStreamResource(isSource); try (InputStream is = resource.getInputStream()) { // 读取和处理资源内容 System.out.println(new String(is.readAllBytes())); } } } ``` ### 八、与其他组件的关系 1. **`BeanFactory&ApplicationContext`** + `BeanFactory`和应用上下文`ApplicationContext`在实例化和配置Bean时通常需要访问资源。`Resource` 接口提供了一种统一的方式来加载资源文件,这对于配置和初始化Bean非常有用。Bean定义中可以包含资源引用,使Bean能够使用这些资源。 2. **`ResourceLoader`** + Spring提供了`ResourceLoader`接口,该接口在应用上下文中广泛使用,以便加载资源。`ResourceLoader`的默认实现是`DefaultResourceLoader`,它基于`Resource`接口实现了资源加载功能。通过`ResourceLoader`,应用可以轻松获取和管理资源,无论资源是来自文件系统、类路径、URL还是其他来源。 3. **`PropertyPlaceholderConfigurer`** + `PropertyPlaceholderConfigurer`是Spring框架中用于替换属性占位符的类。它可以将属性值从资源文件中读取,然后替换配置文件中的占位符。这是通过 `locations` 属性指定的资源文件实现的。 4. **MVC框架** + Spring的MVC框架(如Spring MVC)通常需要处理文件上传和静态资源。`Resource` 接口及其实现可以用于管理和提供这些资源。`Resource`接口与`ResourceLoader`一起被用于加载静态资源,例如图像、样式表和JavaScript文件。 5. **自定义资源加载和处理** + 我们自己也可以使用 `Resource` 接口自定义资源加载和处理逻辑。例如,我们可以创建一个自定义的 `Resource` 实现,用于加载资源文件,执行特定的处理逻辑,然后将处理后的资源提供给应用程序。 ### 九、常见问题 1. **如何选择合适的 `Resource` 实现?** - `ClassPathResource`: 用于访问类路径下的资源。 - `FileSystemResource`: 用于访问文件系统中的资源。 - `UrlResource`: 用于基于URL的资源,如HTTP或FTP资源。 - `ServletContextResource`: 专为 Web 应用程序设计,用于访问`ServletContext`中的资源。 2. **资源未找到** - 如果尝试使用一个不存在的路径或URL创建资源,可能会得到一个 `FileNotFoundException`。 - 确保提供正确的路径,并检查资源是否真的存在。 3. **如何处理编码或字符集问题?** - 当从资源中读取文本内容时,可能需要处理编码问题。 - 使用 `Reader` 和适当的字符集,或使用 Spring 的 `EncodedResource` 类。 4. **相对路径的使用** - 当使用 `FileSystemResource` 时,相对路径可能会导致混淆。确保我们了解相对路径的基准。 5. **资源的实际URL或文件路径是什么?** - 虽然 `Resource` 接口为各种资源类型提供了一个统一的抽象,但有时可能需要知道资源的真实类型或位置。 - 使用 `resource.getURL()` 或 `resource.getFile()` 可以尝试获取资源的真实URL或文件。 6. **如何在非Web应用程序中使用 `ServletContextResource`?** - 这是不可能的,因为 `ServletContextResource` 是为Web应用程序设计的。如果尝试在非Web应用程序中使用它,将会得到错误。 7. **如何从Jar文件或War文件中读取资源?** - 使用 `ClassPathResource` 或 `UrlResource` 可以轻松地从Jar或War文件中读取资源。 - 但是,对于嵌套的Jar文件(例如,当使用Spring Boot可执行Jar时),需要特殊的处理,通常通过 `org.springframework.boot.loader.jar.JarFile` 类。 8. **如何刷新或重新加载已更改的资源?** - 默认情况下,`Resource` 实例不提供刷新或重新加载机制。但对于某些资源类型,如 `UrlResource`,每次调用 `getInputStream()` 都会重新读取内容。 - 对于需要刷新的资源,考虑使用缓存机制或其他方法来处理。 9. **资源加载的性能问题** - 大量频繁地加载资源可能会导致性能问题。 - 考虑缓存资源内容或使用更高效的资源加载策略。 ================================================ FILE: spring-resources/spring-resource/myfile.txt ================================================ hello world ================================================ FILE: spring-resources/spring-resource/pom.xml ================================================ spring-resources com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-resource org.mockito mockito-core 4.5.1 test org.mockito mockito-core 4.5.1 compile ================================================ FILE: spring-resources/spring-resource/src/main/java/com/xcs/spring/ByteArrayResourceDemo.java ================================================ package com.xcs.spring; import org.springframework.core.io.ByteArrayResource; import org.springframework.core.io.Resource; import java.io.InputStream; /** * @author xcs * @date 2023年10月30日 11时29分 **/ public class ByteArrayResourceDemo { public static void main(String[] args) throws Exception { byte[] data = "hello world".getBytes(); Resource resource = new ByteArrayResource(data); try (InputStream is = resource.getInputStream()) { // 读取和处理资源内容 System.out.println(new String(is.readAllBytes())); } } } ================================================ FILE: spring-resources/spring-resource/src/main/java/com/xcs/spring/ClassPathResourceDemo.java ================================================ package com.xcs.spring; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import java.io.InputStream; /** * @author xcs * @date 2023年10月30日 11时04分 **/ public class ClassPathResourceDemo { public static void main(String[] args) throws Exception { String path = "application.properties"; Resource resource = new ClassPathResource(path); try (InputStream is = resource.getInputStream()) { // 读取和处理资源内容 System.out.println(new String(is.readAllBytes())); } } } ================================================ FILE: spring-resources/spring-resource/src/main/java/com/xcs/spring/FileSystemResourceDemo.java ================================================ package com.xcs.spring; import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.Resource; import java.io.InputStream; /** * @author xcs * @date 2023年10月30日 11时11分 **/ public class FileSystemResourceDemo { public static void main(String[] args) throws Exception { // 请替换你自己的目录 String path = "D:\\idea-work-space-xcs\\spring-reading\\spring-resources\\spring-resource\\myfile.txt"; Resource resource = new FileSystemResource(path); try (InputStream is = resource.getInputStream()) { // 读取和处理资源内容 System.out.println(new String(is.readAllBytes())); } } } ================================================ FILE: spring-resources/spring-resource/src/main/java/com/xcs/spring/InputStreamResourceDemo.java ================================================ package com.xcs.spring; import org.springframework.core.io.InputStreamResource; import org.springframework.core.io.Resource; import java.io.ByteArrayInputStream; import java.io.InputStream; /** * @author xcs * @date 2023年10月30日 11时30分 **/ public class InputStreamResourceDemo { public static void main(String[] args) throws Exception { InputStream isSource = new ByteArrayInputStream("hello world".getBytes()); Resource resource = new InputStreamResource(isSource); try (InputStream is = resource.getInputStream()) { // 读取和处理资源内容 System.out.println(new String(is.readAllBytes())); } } } ================================================ FILE: spring-resources/spring-resource/src/main/java/com/xcs/spring/UrlResourceDemo.java ================================================ package com.xcs.spring; import org.springframework.core.io.Resource; import org.springframework.core.io.UrlResource; import java.io.InputStream; /** * @author xcs * @date 2023年10月30日 11时17分 **/ public class UrlResourceDemo { public static void main(String[] args) throws Exception { Resource resource = new UrlResource("https://dist.apache.org/repos/dist/test/test.txt"); try (InputStream is = resource.getInputStream()) { // 读取和处理资源内容 System.out.println(new String(is.readAllBytes())); } } } ================================================ FILE: spring-resources/spring-resource/src/main/resources/application.properties ================================================ welcome = hello world ================================================ FILE: spring-resources/spring-resource-documentLoader/README.md ================================================ ## DocumentLoader - [DocumentLoader](#documentloader) - [一、基本信息](#一基本信息) - [二、知识储备](#二知识储备) - [三、基本描述](#三基本描述) - [四、主要功能](#四主要功能) - [五、接口源码](#五接口源码) - [六、主要实现](#六主要实现) - [七、最佳实践](#七最佳实践) - [八、与其他组件的关系](#八与其他组件的关系) - [九、常见问题](#九常见问题) ### 一、基本信息 ✒️ **作者** - Lex 📝 - **博客** [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、知识储备 1. **XML 解析技术** + 了解 XML 解析技术,如 DOM(文档对象模型)和 SAX(简单 API for XML)。`DocumentLoader` 通常使用 DOM 来加载和解析 XML 文档,因此理解 DOM 操作是重要的。 2. **资源加载** + 了解如何使用 Java 资源加载机制,例如 [ResourceLoader](https://github.com/xuchengsheng/spring-reading/tree/master/spring-resources/spring-resource-resourceLoader) 和 [Resource](https://github.com/xuchengsheng/spring-reading/tree/master/spring-resources/spring-resource),来获取 XML 配置文件。Spring 使用这些机制来加载配置文件。 ### 三、基本描述 `DocumentLoader` 接口是 Spring 框架中的一个核心接口,用于加载和解析 XML 文档,通常用于解析 Spring 配置文件。它定义了一种方法来加载 XML 文档并将其解析为一个 `org.w3c.dom.Document` 对象,以便在 Spring 应用程序中使用,允许开发人员获取和操作 XML 配置文件的内容。这个接口允许配置文件的加载和解析过程在后台自动进行,以支持 Spring 应用的初始化和配置。 ### 四、主要功能 1. **加载 XML 文档**: + `DocumentLoader` 接口定义了一个方法,用于加载 XML 文档,可以从不同来源(例如文件、资源、URL 等)获取 XML 内容。 2. **解析 XML 文档** + 一旦加载了 XML 文档,`DocumentLoader` 将把它解析为一个 `org.w3c.dom.Document` 对象,该对象表示整个 XML 文档的结构,包括元素、属性、文本等。 3. **支持验证** + `DocumentLoader` 接口通常可以支持验证,通过指定验证模式(如 DTD 或 XML Schema 验证),可以确保文档的结构和内容符合规定的标准。 4. **处理实体引用** + 它还可以处理 XML 文档中的外部实体引用,通过提供 `EntityResolver` 接口来解决外部实体的引用,以确保加载和解析的过程顺利进行。 5. **错误处理** + `DocumentLoader` 接口还提供了一个 `ErrorHandler` 接口,用于处理 XML 解析过程中的错误信息,以便及时捕获和处理问题。 ### 五、接口源码 `DocumentLoader` 接口用于加载和解析XML文档的策略,它接受一个`InputSource`,一个实体解析器`EntityResolver`,一个错误处理器`ErrorHandler`,验证模式`validationMode`(可以是DTD或XSD验证),以及一个布尔值`namespaceAware`,表示是否启用XML命名空间支持。方法返回一个加载后的`Document`对象。 ```java /** * 用于加载 XML Document 的策略接口。 * * @author Rob Harrop * @since 2.0 * @see DefaultDocumentLoader */ public interface DocumentLoader { /** * 从提供的 InputSource source 加载一个 Document document。 * @param inputSource 要加载的文档的来源 * @param entityResolver 用于解析任何实体的解析器 * @param errorHandler 用于在加载文档过程中报告任何错误 * @param validationMode 验证的类型 * (org.springframework.util.xml.XmlValidationModeDetector#VALIDATION_DTD DTD * 或 org.springframework.util.xml.XmlValidationModeDetector#VALIDATION_XSD XSD) * @param namespaceAware 如果需要提供对XML命名空间的支持,则为 true * @return 加载的 Document document * @throws Exception 如果发生错误 */ Document loadDocument( InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception; } ``` ### 六、主要实现 1. **`DefaultDocumentLoader`** + `DefaultDocumentLoader` 是 Spring 框架的默认实现,它负责加载和解析XML配置文件,以支持Spring应用程序的初始化和配置。这个实现提供了灵活性,可以根据需要自定义配置文件的加载和解析行为。如果需要使用不同的加载策略或验证模式,可以通过配置来指定不同的`DocumentLoader`实现。 ~~~mermaid classDiagram direction BT class DocumentLoader { <> } class DefaultDocumentLoader { } DefaultDocumentLoader --|> DocumentLoader ~~~ ### 七、最佳实践 使用 Spring 框架的默认文档加载器(`DefaultDocumentLoader`)加载和解析XML配置文件,并以递归方式打印XML文档的详细信息,包括元素名称、属性和文本内容。通过创建一个 `Resource` 对象表示要加载的XML文件,然后使用 `DefaultDocumentLoader` 进行加载和解析,并最后递归打印文档内容。 ```java public class DocumentLoaderDemo { public static void main(String[] args) { try { // 创建要加载的资源对象 Resource resource = new ClassPathResource("sample.xml"); // 创建 DocumentLoader 实例 DefaultDocumentLoader documentLoader = new DefaultDocumentLoader(); // 加载和解析 XML 文档 Document document = documentLoader.loadDocument(new InputSource(resource.getInputStream()), null, null, 0, true); // 打印 XML 文档的内容 printDetailedDocumentInfo(document); } catch (Exception e) { e.printStackTrace(); } } /** * 打印详细的XML文档信息,包括元素、属性和文本内容。 * * @param document 要打印的XML文档对象 */ private static void printDetailedDocumentInfo(Document document) { Element rootElement = document.getDocumentElement(); printElementInfo(rootElement, ""); } /** * 递归打印XML元素的详细信息,包括元素名称、属性和子节点。 * * @param element 要打印的XML元素 * @param indent 当前打印的缩进 */ private static void printElementInfo(Element element, String indent) { // 打印元素名称 System.out.println(indent + "Element: " + element.getNodeName()); // 打印元素的属性 NamedNodeMap attributes = element.getAttributes(); for (int i = 0; i < attributes.getLength(); i++) { Node attribute = attributes.item(i); System.out.println(indent + " Attribute: " + attribute.getNodeName() + " = " + attribute.getNodeValue()); } // 打印元素的子节点(可能是元素或文本) NodeList childNodes = element.getChildNodes(); for (int i = 0; i < childNodes.getLength(); i++) { Node childNode = childNodes.item(i); if (childNode.getNodeType() == Node.ELEMENT_NODE) { // 如果子节点是元素,递归打印它 printElementInfo((Element) childNode, indent + " "); } else if (childNode.getNodeType() == Node.TEXT_NODE) { // 如果子节点是文本,打印文本内容 System.out.println(indent + " Text: " + childNode.getNodeValue().trim()); } } } } ``` `MyBean` 的Java类,代表了一个简单的Java Bean。 ```java public class MyBean { private String message; public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } } ``` `ClassPathResource("sample.xml")` 将加载类路径下名为 "`sample.xml`" 的资源文件的内容。在你的示例配置文件中,这个资源文件定义了一个名为 "`myBean`" 的 Spring Bean,该 Bean 具有一个属性 "message",其值设置为 "Hello World"。 ```xml ``` 运行结果发现,成功的解析出了XML配置文件的结构和内容,包括命名空间、元素、属性和文本内容,以及它们之间的层次关系。 ```yaml Element: beans Attribute: xmlns = http://www.springframework.org/schema/beans Attribute: xmlns:xsi = http://www.w3.org/2001/XMLSchema-instance Attribute: xsi:schemaLocation = http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd Text: Element: bean Attribute: class = com.xcs.spring.bean.MyBean Attribute: id = myBean Text: Element: property Attribute: name = message Attribute: value = Hello World Text: Text: ``` ### 八、与其他组件的关系 1. **`XmlBeanDefinitionReader`** + `XmlBeanDefinitionReader` 是用于读取和解析 XML 配置文件的类,它在 Spring 应用程序上下文的初始化过程中使用 `DefaultDocumentLoader` 来加载和解析配置文件。这是 Spring IOC 容器的关键组件之一,用于注册和管理 Bean 定义。 2. **`ApplicationContext`** + 实现Spring 的不同类型的应用程序上下文实现,如 `ClassPathXmlApplicationContext`、`FileSystemXmlApplicationContext`、`XmlWebApplicationContext` 等,都使用 `DefaultDocumentLoader` 或类似的加载器来加载配置文件并创建应用程序上下文。 3. **Spring Web MVC 配置** + 在 Spring Web MVC 应用程序中,通常会使用 `DispatcherServlet` 配置中的 `` 元素来指定 Spring 配置文件,这个配置文件将由 `DefaultDocumentLoader` 解析以配置 Web 应用程序上下文。 ### 九、常见问题 1. **XML 文档路径和资源定位问题** + 确保能够正确找到并加载XML文档,包括检查路径、文件是否存在以及资源定位的正确性。 2. **XML 验证和格式问题** + 确保XML文档遵循规范的DTD或XSD,以避免验证失败或格式错误。 3. **Entity解析问题** + 提供有效的`EntityResolver`以解析外部实体,避免解析问题。 4. **错误处理问题** + 使用`ErrorHandler`来处理解析期间的错误,以便更好地调试和处理问题。 ================================================ FILE: spring-resources/spring-resource-documentLoader/pom.xml ================================================ spring-resources com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-resource-documentLoader ================================================ FILE: spring-resources/spring-resource-documentLoader/src/main/java/com/xcs/spring/DocumentLoaderDemo.java ================================================ package com.xcs.spring; import org.springframework.beans.factory.xml.DefaultDocumentLoader; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.w3c.dom.*; import org.xml.sax.InputSource; /** * @author xcs * @date 2023年11月04日 14时27分 **/ public class DocumentLoaderDemo { public static void main(String[] args) { try { // 创建要加载的资源对象 Resource resource = new ClassPathResource("sample.xml"); // 创建 DocumentLoader 实例 DefaultDocumentLoader documentLoader = new DefaultDocumentLoader(); // 加载和解析 XML 文档 Document document = documentLoader.loadDocument(new InputSource(resource.getInputStream()), null, null, 0, true); // 打印 XML 文档的内容 printDetailedDocumentInfo(document); } catch (Exception e) { e.printStackTrace(); } } /** * 打印详细的XML文档信息,包括元素、属性和文本内容。 * * @param document 要打印的XML文档对象 */ private static void printDetailedDocumentInfo(Document document) { Element rootElement = document.getDocumentElement(); printElementInfo(rootElement, ""); } /** * 递归打印XML元素的详细信息,包括元素名称、属性和子节点。 * * @param element 要打印的XML元素 * @param indent 当前打印的缩进 */ private static void printElementInfo(Element element, String indent) { // 打印元素名称 System.out.println(indent + "Element: " + element.getNodeName()); // 打印元素的属性 NamedNodeMap attributes = element.getAttributes(); for (int i = 0; i < attributes.getLength(); i++) { Node attribute = attributes.item(i); System.out.println(indent + " Attribute: " + attribute.getNodeName() + " = " + attribute.getNodeValue()); } // 打印元素的子节点(可能是元素或文本) NodeList childNodes = element.getChildNodes(); for (int i = 0; i < childNodes.getLength(); i++) { Node childNode = childNodes.item(i); if (childNode.getNodeType() == Node.ELEMENT_NODE) { // 如果子节点是元素,递归打印它 printElementInfo((Element) childNode, indent + " "); } else if (childNode.getNodeType() == Node.TEXT_NODE) { // 如果子节点是文本,打印文本内容 System.out.println(indent + " Text: " + childNode.getNodeValue().trim()); } } } } ================================================ FILE: spring-resources/spring-resource-documentLoader/src/main/java/com/xcs/spring/bean/MyBean.java ================================================ package com.xcs.spring.bean; /** * @author xcs * @date 2023年11月04日 14时30分 **/ public class MyBean { private String message; public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } } ================================================ FILE: spring-resources/spring-resource-documentLoader/src/main/resources/sample.xml ================================================ ================================================ FILE: spring-resources/spring-resource-resourceLoader/README.md ================================================ ## ResourceLoader - [ResourceLoader](#resourceloader) - [一、基本信息](#一基本信息) - [二、知识储备](#二知识储备) - [三、基本描述](#三基本描述) - [四、主要功能](#四主要功能) - [五、接口源码](#五接口源码) - [六、主要实现](#六主要实现) - [七、最佳实践](#七最佳实践) - [八、与其他组件的关系](#八与其他组件的关系) - [九、常见问题](#九常见问题) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、知识储备 1. **Spring 资源抽象** - [Resource](https://github.com/xuchengsheng/spring-reading/blob/master/spring-resources/spring-resource) 接口及其各种实现。 2. **路径和模式解析** - Spring 中的路径解析,特别是使用 ant 风格的路径模式,例如 `classpath*:com/example/**/*.xml`。 3. **理解不同的资源类型** - 文件资源、类路径资源、URL 资源、JAR 中的资源等。 ### 三、基本描述 `org.springframework.core.io.ResourceLoader` 是 Spring 框架中的一个关键接口,它定义了如何获取资源(例如类路径资源、文件系统资源或网页资源)的策略。这个接口是 Spring 资源加载抽象的核心,使得应用程序可以从不同的资源位置以统一的方式加载资源。 ### 四、主要功能 1. **统一资源加载** - `ResourceLoader` 提供了一个标准化的方法来加载资源,不论资源是存放在类路径、文件系统、网络URL还是其他位置。 2. **资源位置解析** - 根据提供的资源字符串位置(如 "classpath:", "file:", "http:"),`ResourceLoader` 可以确定资源的类型,并为其创建相应的 `Resource` 实例。 3. **返回 `Resource` 实例** - 通过 `Resource getResource(String location)` 方法,`ResourceLoader` 返回一个 `Resource` 对象,代表了指定位置的资源。这使得读取和操作资源变得简单且统一。 4. **与 `ClassLoader` 的交互** - `ResourceLoader` 通过 `ClassLoader getClassLoader()` 方法返回与其关联的 `ClassLoader`。这使得资源加载策略可以与特定的类加载器关联。 5. **扩展性** - `ResourceLoader` 是一个接口,这意味着我们可以实现自己的资源加载策略,或者扩展默认的策略以满足特定需求。 6. **内置实现与整合** - Spring 提供了默认的 `ResourceLoader` 实现,如 `DefaultResourceLoader`。但更重要的是,`org.springframework.context.ApplicationContext` 也实现了 `ResourceLoader`,这意味着 Spring 上下文本身就是一个资源加载器。 ### 五、接口源码 `ResourceLoader` 接口为 Spring 框架定义了资源加载策略。它提供了获取资源的方法,并公开了其使用的 `ClassLoader`。通过这种策略,资源可以从各种来源(如类路径、文件系统等)以统一方式加载。这提供了资源加载的灵活性和一致性,并支持各种资源描述符,如 URL、类路径等。此外,它还允许对资源句柄进行多次重新使用和读取。 ```java /** * 用于加载资源(例如类路径或文件系统资源)的策略接口。 * 一个 ApplicationContext 需要提供此功能以及扩展的 ResourcePatternResolver 支持。 * * DefaultResourceLoader 是一个独立的实现,可在 ApplicationContext 外部使用,并被 ResourceEditor 使用。 * * 当在 ApplicationContext 中运行时,类型为 Resource 和 Resource[] 的 Bean 属性可以从字符串中填充,使用特定上下文的资源加载策略。 * * @author Juergen Hoeller * @since 10.03.2004 */ public interface ResourceLoader { /** 用于从类路径加载的伪 URL 前缀:"classpath:"。 */ String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX; /** * 返回指定资源位置的 Resource 句柄。 * 该句柄应始终是一个可重用的资源描述符,允许进行多次 Resource#getInputStream() 调用。 * 必须支持完全限定的 URLs,例如 "file:C:/test.dat"。 * 必须支持类路径伪-URLs,例如 "classpath:test.dat"。 * 应支持相对文件路径,例如 "WEB-INF/test.dat"。 * (这将是实现特定的,通常由 ApplicationContext 实现提供。) * 请注意,Resource 句柄并不意味着资源存在;我们需要调用 Resource#exists 来检查其存在性。 * * @param location 资源位置 */ Resource getResource(String location); /** * 公开此 ResourceLoader 使用的 ClassLoader。 * 需要直接访问 ClassLoader 的客户端可以与 ResourceLoader 以统一的方式这样做,而不是依赖线程上下文 ClassLoader。 * * @return ClassLoader(仅当连系统 ClassLoader 都不可访问时为 null) */ @Nullable ClassLoader getClassLoader(); } ``` ### 六、主要实现 1. **`DefaultResourceLoader`** + 这是基本的资源加载器实现。它可以处理 "classpath:" 前缀的资源,如果没有提供这样的前缀,它会尝试使用类加载器或文件系统来加载资源。 ~~~mermaid classDiagram direction BT class ResourceLoader { <> + getResource(location) : Resource + getClassLoader() : ClassLoader } class DefaultResourceLoader { -ClassLoader classLoader } DefaultResourceLoader ..|> ResourceLoader ~~~ ### 七、最佳实践 **`DefaultResourceLoader`** 使用 `DefaultResourceLoader` 从不同的资源(类路径和文件系统)加载内容。 ```java public class DefaultResourceLoaderDemo { public static void main(String[] args) { DefaultResourceLoader loader = new DefaultResourceLoader(); // 从类路径加载资源 Resource classpathResource = loader.getResource("classpath:application.properties"); try (InputStream is = classpathResource.getInputStream()) { // 读取和处理资源内容 System.out.println("Classpath = "+ new String(is.readAllBytes())); } catch (IOException e) { e.printStackTrace(); } // 加载文件系统中的资源 Resource fileResource = loader.getResource("file:/idea-work-space-xcs/spring-reading/spring-resources/spring-resource-resourceLoader/myfile1.txt"); try (InputStream is = fileResource.getInputStream()) { // 读取和处理资源内容 System.out.println("File = "+ new String(is.readAllBytes())); } catch (IOException e) { e.printStackTrace(); } } } ``` 运行结果发现 - 从类路径上,我们加载了一个文件:`application.properties` 。这意味着在我们的项目的类路径中,有这个文件。 - 从文件系统上,我们加载了一个文件:`myfile1.txt`。这些文件位于我们之前在代码中硬编码的文件路径 `/idea-work-space-xcs/spring-reading/spring-resources/spring-resource-resourceLoader/` 下。 ```java Classpath Exists= true File Exists = true ``` ### 八、与其他组件的关系 1. **`ApplicationContext`** + 所有的 Spring `ApplicationContext` 都实现了 `ResourceLoader`。这意味着我们可以使用 Spring 上下文本身来加载资源。 2. **`Resource`** + `ResourceLoader` 返回 `Resource` 对象,它代表实际的资源,可以是文件系统中的文件、类路径资源、URLs 等。`Resource` 提供了访问和读取资源内容的方法。 3. **`ResourcePatternResolver`** + 这是 `ResourceLoader` 的扩展,可以解析给定的位置模式以加载多个资源。`PathMatchingResourcePatternResolver` 是它的主要实现。 4. **`ResourceEditor`** + 这是一个属性编辑器,用于将字符串转换为 `Resource` 对象。它内部使用 `ResourceLoader` 来执行转换。 5. **`ResourceLoaderAware`** + 这是一个特殊的接口,任何 bean 如果实现了它,那么它就可以在被创建时获得对 `ResourceLoader` 的引用,这样它就可以自己加载资源。 6. **`EmbeddedValueResolverAware`** + 一些组件,如属性占位符处理器,可能需要解析值中的动态部分。它们可以使用 `ResourceLoader` 作为解析这些值的一部分,特别是当值代表资源位置时。 7. **`PathMatchingResourcePatternResolver`** + 它是 `ResourcePatternResolver` 的一个实现,它扩展了 `ResourceLoader` 来处理以 "classpath*:" 开头的资源模式,这允许加载所有匹配的资源,而不仅仅是第一个找到的资源。 ### 九、常见问题 1. **加载类路径资源** + 使用前缀 "classpath:",例如:`loader.getResource("classpath:myconfig.xml")`。 2. **加载文件系统资源** + 使用前缀 "file:",例如:`loader.getResource("file:/path/to/myconfig.xml")`。 3. **加载URL资源** + 直接使用 URL,例如:`loader.getResource("http://www.example.com/config.xml")`。 4. **资源不存在** + 使用 `Resource.exists()` 方法检查资源是否存在。确保路径或位置正确,并且资源真的存在于预期的位置。 5. **如何读取资源内容** + 从 `Resource` 对象中获取 `InputStream`,例如:`resource.getInputStream()`。 6. **从 `Resource` 获取到文件路径** + 使用 `Resource.getFile()`。但请注意,这并不总是有效的,例如当资源实际上是一个类路径资源或URL资源时。 7. **加载匹配特定模式的多个资源** + 使用 `ResourcePatternResolver` 或其实现 `PathMatchingResourcePatternResolver`。 8. **自动注入 `ResourceLoader`** + 实现 `ResourceLoaderAware` 接口,Spring 将自动为我们的 bean 提供 `ResourceLoader` 的引用。 9. **扩展或自定义资源加载机制** + 我们可以实现自己的 `ResourceLoader` 或继承现有的实现,如 `DefaultResourceLoader`。 11. **加载资源时考虑环境或属性占位符** + 使用 `PropertyPlaceholderConfigurer` 或 `PropertySourcesPlaceholderConfigurer` 与 `@Value` 注解可以解析属性值中的资源路径。 ================================================ FILE: spring-resources/spring-resource-resourceLoader/myfile1.txt ================================================ hello world1 ================================================ FILE: spring-resources/spring-resource-resourceLoader/myfile2.txt ================================================ hello world2 ================================================ FILE: spring-resources/spring-resource-resourceLoader/myfile3.txt ================================================ hello world3 ================================================ FILE: spring-resources/spring-resource-resourceLoader/pom.xml ================================================ spring-resources com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-resource-resourceLoader ================================================ FILE: spring-resources/spring-resource-resourceLoader/src/main/java/com/xcs/spring/DefaultResourceLoaderDemo.java ================================================ package com.xcs.spring; import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.Resource; /** * @author xcs * @date 2023年10月30日 17时22分 **/ public class DefaultResourceLoaderDemo { public static void main(String[] args) { DefaultResourceLoader loader = new DefaultResourceLoader(); // 从类路径加载资源 Resource classpathResource = loader.getResource("classpath:application.properties"); System.out.println("Classpath Exists= " + classpathResource.exists()); // 加载文件系统中的资源 Resource fileResource = loader.getResource("file:/idea-work-space-xcs/spring-reading/spring-resources/spring-resource-resourceLoader/myfile1.txt"); System.out.println("File Exists = " + fileResource.exists()); } } ================================================ FILE: spring-resources/spring-resource-resourceLoader/src/main/java/com/xcs/spring/PathMatchingResourcePatternResolverDemo.java ================================================ package com.xcs.spring; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; /** * @author xcs * @date 2023年10月30日 17时30分 **/ public class PathMatchingResourcePatternResolverDemo { public static void main(String[] args) throws Exception { PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); // 加载所有匹配的类路径资源 Resource[] resources = resolver.getResources("classpath*:*.properties"); for (Resource resource : resources) { System.out.println("Classpath = " + resource.getFilename()); } // 加载文件系统中的所有匹配资源 Resource[] fileResources = resolver.getResources("file:/idea-work-space-xcs/spring-reading/spring-resources/spring-resource-resourceLoader/*.txt"); for (Resource resource : fileResources) { System.out.println("File = " + resource.getFilename()); } } } ================================================ FILE: spring-resources/spring-resource-resourceLoader/src/main/resources/application.properties ================================================ welcome = hello world ================================================ FILE: spring-resources/spring-resource-resourceLoader/src/main/resources/bootstrap.properties ================================================ welcome = hello world ================================================ FILE: spring-resources/spring-resource-resourcePatternResolver/README.md ================================================ ### ResourcePatternResolver - [ResourcePatternResolver](#resourcepatternresolver) - [一、基本信息](#一基本信息) - [二、知识储备](#二知识储备) - [三、基本描述](#三基本描述) - [四、主要功能](#四主要功能) - [五、接口源码](#五接口源码) - [六、主要实现](#六主要实现) - [七、最佳实践](#七最佳实践) - [八、与其他组件的关系](#八与其他组件的关系) - [九、常见问题](#九常见问题) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、知识储备 + **`ResourceLoader`** + [ResourceLoader](https://github.com/xuchengsheng/spring-reading/blob/master/spring-resources/spring-resource-resourceLoader/README.md) 是 Spring 框架中的一个关键接口,它定义了如何获取资源(例如类路径资源、文件系统资源或网页资源)的策略。这个接口是 Spring 资源加载抽象的核心,使得应用程序可以从不同的资源位置以统一的方式加载资源。 ### 三、基本描述 `ResourcePatternResolver`是Spring框架中的一个接口,扩展自`ResourceLoader`,用于解析资源模式,支持通过模式匹配检索多个资源。其中,常见的实现类是`PathMatchingResourcePatternResolver`,它通过类路径、文件系统或URL等多种资源位置,能够根据给定的资源模式获取匹配的资源。通过调用`getResources(String locationPattern)`方法,您可以使用包含通配符的资源模式,例如`classpath*:com/example/**/*.xml`,来获取满足条件的资源数组。这提供了一种灵活的机制,使得在应用程序中能够方便地加载和处理符合特定模式的资源文件,如配置文件、模板文件等。 ### 四、主要功能 1. **资源模式解析** + 通过`getResources(String locationPattern)`方法,支持使用通配符的资源模式,如`classpath*:com/example/**/*.xml`,用于检索匹配特定模式的多个资源。 2. **资源获取** + 通过`getResources(Resource location)`方法,根据给定的资源对象,返回匹配的资源数组。这使得可以获取与特定资源相关联的其他资源,例如获取与给定类路径下的一个文件相关的所有资源。 3. **多种资源位置支持** + 可以处理不同的资源位置,包括类路径(classpath)、文件系统、URL等。这使得应用程序能够以不同的方式组织和存储资源,而不影响资源的检索和加载。 4. **灵活的资源加载** + 结合`ResourceLoader`的能力,`ResourcePatternResolver`允许在应用程序中以统一的方式加载各种资源,而无需关心底层资源的存储位置或形式。 5. **通用资源操作** + 通过`Resource`接口,提供了对资源的通用操作,例如获取资源的URL、输入流、文件句柄等。 ### 五、接口源码 `ResourcePatternResolver`是Spring框架中用于解析位置模式为资源对象的策略接口。它扩展了`ResourceLoader`接口,允许通过类路径、文件系统、URL等多种方式获取匹配指定模式的资源。 ```java /** * 策略接口,用于将位置模式(例如Ant风格的路径模式)解析为 Resource 对象。 * * 这是对 org.springframework.core.io.ResourceLoader 接口的扩展。传入的 * ResourceLoader(例如,在上下文中通过 org.springframework.context.ResourceLoaderAware * 传递的 org.springframework.context.ApplicationContext)可以检查是否也实现了这个扩展接口。 * * PathMatchingResourcePatternResolver 是一个独立的实现,可在不依赖 * ApplicationContext 的情况下使用,也被 ResourceArrayPropertyEditor 用于 * 填充 Resource 数组的 bean 属性。 * * 可用于任何类型的位置模式(例如 "/WEB-INF/*-context.xml"):输入模式必须与策略实现匹配。 * 这个接口只指定了转换方法,而不是具体的模式格式。 * * 该接口还引入了一个新的资源前缀 "classpath*:",用于匹配类路径下的所有资源。注意,在这种情况下, * 资源位置预期是没有占位符的路径(例如 "/beans.xml");类路径中的 JAR 文件或不同目录可以包含相同名称的多个文件。 * * @author Juergen Hoeller * @since 1.0.2 * @see org.springframework.core.io.Resource * @see org.springframework.core.io.ResourceLoader * @see org.springframework.context.ApplicationContext * @see org.springframework.context.ResourceLoaderAware */ public interface ResourcePatternResolver extends ResourceLoader { /** * 类路径匹配所有资源的伪 URL 前缀:"classpath*:" * 这与 ResourceLoader 的类路径 URL 前缀不同,它检索给定名称(例如 "/beans.xml")的 * 所有匹配资源,例如在所有部署的 JAR 文件的根目录中。 * 详见 org.springframework.core.io.ResourceLoader#CLASSPATH_URL_PREFIX */ String CLASSPATH_ALL_URL_PREFIX = "classpath*:"; /** * 将给定的位置模式解析为 Resource 对象。 * 应尽可能避免指向相同物理资源的重叠资源条目。结果应具有集合语义。 * @param locationPattern 要解析的位置模式 * @return 相应的 Resource 对象数组 * @throws IOException 如果发生 I/O 错误 */ Resource[] getResources(String locationPattern) throws IOException; } ``` ### 六、主要实现 + PathMatchingResourcePatternResolver + 支持使用Ant风格的路径模式,例如`classpath*:com/example/**/*.xml`,这允许在类路径中检索符合指定模式的资源。 ~~~mermaid classDiagram direction BT class ResourceLoader { <> + getResource(location) : Resource + getClassLoader() : ClassLoader } class ResourcePatternResolver { <> +getResources(locationPattern) : Resource[] } class PathMatchingResourcePatternResolver { -ResourceLoader resourceLoader } ResourcePatternResolver --|> ResourceLoader PathMatchingResourcePatternResolver ..|> ResourcePatternResolver ~~~ ### 七、最佳实践 使用 `PathMatchingResourcePatternResolver` 来加载匹配指定模式的资源。 ```java public class PathMatchingResourcePatternResolverDemo { public static void main(String[] args) throws Exception { PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); // 加载所有匹配的类路径资源 Resource[] resources = resolver.getResources("classpath*:*.properties"); for (Resource resource : resources) { System.out.println("Classpath = " + resource.getFilename()); } // 加载文件系统中的所有匹配资源 Resource[] fileResources = resolver.getResources("file:/idea-work-space-xcs/spring-reading/spring-resources/spring-resource-resourceLoader/*.txt"); for (Resource resource : fileResources) { System.out.println("File = " + resource.getFilename()); } } } ``` 运行结果发现 - 从类路径上,我们加载了两个文件:`application.properties` 和 `bootstrap.properties`。这意味着在我们的项目的类路径中,有这两个文件。 - 从文件系统上,我们加载了三个文件:`myfile1.txt`, `myfile2.txt`, 和 `myfile3.txt`。这些文件位于我们之前在代码中硬编码的文件路径 `/idea-work-space-xcs/spring-reading/spring-resources/spring-resource-resourceLoader/` 下。 ```java Classpath = application.properties Classpath = bootstrap.properties File = myfile1.txt File = myfile2.txt File = myfile3.txt ``` ### 八、与其他组件的关系 1. **BeanDefinitionLoader:** - `BeanDefinitionLoader` 是 Spring 框架中用于加载 `BeanDefinition` 的接口,提供了对加载过程的抽象。在加载过程中,需要使用 `PathMatchingResourcePatternResolver` 来解析资源模式。 2. **MessageSourceAutoConfiguration:** - `MessageSourceAutoConfiguration` 是 Spring Boot 中的一个自动配置类,用于配置消息源(MessageSource)。在配置中可能涉及到加载国际化资源文件,而 `PathMatchingResourcePatternResolver` 用于解析这些资源文件的路径。 3. **AbstractBeanDefinitionReader:** - `AbstractBeanDefinitionReader` 是 Spring 框架中用于读取 `BeanDefinition` 的抽象类,具体的实现类(如 `XmlBeanDefinitionReader`)在加载 XML 文件时可能使用 `PathMatchingResourcePatternResolver`。 4. **ClassPathScanningCandidateComponentProvider:** - `ClassPathScanningCandidateComponentProvider` 是 Spring 框架中用于基于类路径进行组件扫描的类,其中使用了 `PathMatchingResourcePatternResolver` 来解析类路径中的资源。 5. **AbstractApplicationContext:** - `AbstractApplicationContext` 是 Spring 框架中 `ApplicationContext` 接口的抽象实现,其中在资源加载阶段使用了 `PathMatchingResourcePatternResolver`。 6. **ResourcePatternUtils:** - `ResourcePatternUtils` 提供了一些工具方法,用于处理资源模式。在这个类中,涉及到使用 `PathMatchingResourcePatternResolver` 来解析资源模式。 7. **ResourceArrayPropertyEditor:** - `ResourceArrayPropertyEditor` 是 Spring 框架中用于处理 `Resource` 数组类型属性的属性编辑器。在处理这些属性时,需要解析资源模式,而 `PathMatchingResourcePatternResolver` 提供了相应的功能。 ### 九、常见问题 1. **资源模式不匹配** + 如果使用的资源模式没有正确匹配到预期的资源,可能是模式本身不正确或者资源位置设置有误。确保使用的资源模式与实际的资源路径匹配,并检查是否有拼写错误。 2. **类路径下资源无法加载** + 如果使用的是类路径前缀(例如 "classpath:" 或 "classpath*:"),但是资源未被正确加载,可能是因为资源位置设置有误。检查资源路径是否正确,尤其是相对于类路径的路径。 3. **文件系统资源加载问题** + 当使用文件系统路径时,确保路径的正确性,并确保应用程序有足够的权限来读取这些文件。同时,注意跨平台路径分隔符的问题,尽量使用`File.separator`来确保跨平台兼容性。 4. **资源不存在或为空** + 如果 `getResources` 方法返回的数组为空,可能是因为资源位置不存在,或者匹配的资源为空。确保资源位置正确,并验证是否存在符合条件的资源。 5. **资源路径中包含特殊字符** + 如果资源路径中包含特殊字符,可能会导致解析问题。考虑在使用路径时进行适当的转义或编码。 6. **性能问题** + 在处理大量资源或者模式时,可能会遇到性能问题。考虑对资源模式进行优化,避免过度使用通配符和递归,以提高性能。 ================================================ FILE: spring-resources/spring-resource-resourcePatternResolver/myfile1.txt ================================================ hello world1 ================================================ FILE: spring-resources/spring-resource-resourcePatternResolver/myfile2.txt ================================================ hello world2 ================================================ FILE: spring-resources/spring-resource-resourcePatternResolver/myfile3.txt ================================================ hello world3 ================================================ FILE: spring-resources/spring-resource-resourcePatternResolver/pom.xml ================================================ spring-resources com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-resource-resourcePatternResolver ================================================ FILE: spring-resources/spring-resource-resourcePatternResolver/src/main/java/com/xcs/spring/ResourcePatternResolverDemo.java ================================================ package com.xcs.spring; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; /** * @author xcs * @date 2023年10月30日 17时30分 **/ public class ResourcePatternResolverDemo { public static void main(String[] args) throws Exception { ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); // 加载所有匹配的类路径资源 Resource[] resources = resolver.getResources("classpath*:*.properties"); for (Resource resource : resources) { System.out.println("Classpath = " + resource.getFilename()); } // 加载文件系统中的所有匹配资源 Resource[] fileResources = resolver.getResources("file:/idea-work-space-xcs/spring-reading/spring-resources/spring-resource-resourceLoader/*.txt"); for (Resource resource : fileResources) { System.out.println("File = " + resource.getFilename()); } } } ================================================ FILE: spring-resources/spring-resource-resourcePatternResolver/src/main/resources/application.properties ================================================ welcome = hello world ================================================ FILE: spring-resources/spring-resource-resourcePatternResolver/src/main/resources/bootstrap.properties ================================================ welcome = hello world ================================================ FILE: spring-spel/pom.xml ================================================ spring-reading com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-spel pom spring-spel-expressionParser spring-spel-evaluationContext spring-spel-propertyAccessor spring-spel-constructorResolver spring-spel-methodResolver spring-spel-beanResolver spring-spel-typeLocator spring-spel-typeConverter spring-spel-typeComparator spring-spel-operatorOverloader spring-spel-expression ================================================ FILE: spring-spel/spring-spel-beanResolver/README.md ================================================ ## BeanResolver - [BeanResolver](#BeanResolver) - [一、基本信息](#一基本信息) - [二、知识储备](#二知识储备) - [三、基本描述](#三基本描述) - [四、主要功能](#四主要功能) - [五、接口源码](#五接口源码) - [六、主要实现](#六主要实现) - [七、最佳实践](#七最佳实践) - [八、与其他组件的关系](#八与其他组件的关系) - [九、常见问题](#九常见问题) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、知识储备 1. **Spring表达式语言(SpEL)** + 了解SpEL的语法和用法,包括如何使用表达式来访问和操作对象的属性、调用方法等。因为`BeanResolver`通常用于在SpEL中解析Bean,所以对SpEL的理解至关重要。 2. **Spring容器** + 理解Spring容器的概念和工作原理,包括BeanFactory和ApplicationContext之间的区别、Bean的生命周期、Bean的作用域等。因为`BeanResolver`通常用于从Spring容器中解析Bean,所以对Spring容器的了解是必要的。 3. **反射(Reflection)** + 理解Java反射的基本原理和用法,包括如何在运行时获取和操作类的信息。`BeanResolver`通常需要使用反射来动态地创建和操作对象,所以对反射有一定的了解是很有帮助的。 4. **设计模式** + 了解设计模式的基本原理和常见的设计模式,如工厂模式、策略模式等。因为`BeanResolver`接口通常用于实现依赖注入等功能,对设计模式的了解有助于编写更灵活、可扩展的解决方案。 ### 三、基本描述 `BeanResolver`接口是Spring框架中的一个关键接口,用于在Spring表达式语言(SpEL)中解析Bean。它定义了一个`resolve`方法,接收一个`EvaluationContext`对象和一个Bean的名称作为参数,然后返回相应的Bean实例。通过实现`BeanResolver`接口,可以在SpEL表达式中轻松地引用和操作Spring容器中的Bean,使得表达式更加灵活和强大。 ### 四、主要功能 1. **解析Bean** + 提供了解析Bean的方法,可以根据给定的Bean名称从Spring容器中获取相应的Bean实例。这样可以在运行时动态地获取和操作Spring容器中的Bean。 2. **支持SpEL** + 作为Spring表达式语言(SpEL)的一部分,`BeanResolver`允许在SpEL表达式中引用和操作Spring容器中的Bean。通过`BeanResolver`,可以在表达式中使用特殊的语法来引用Bean,并进行各种操作,如访问属性、调用方法等。 3. **提供上下文支持** + `BeanResolver`接口通常与`EvaluationContext`对象一起使用,它提供了表达式所需的上下文信息,包括变量、函数等。这样可以确保在解析Bean时具有必要的上下文信息。 4. **定制解析逻辑** + 尽管默认情况下`BeanResolver`的实现由Spring容器提供,但你可以根据需要自定义`BeanResolver`的实现。这样可以灵活地定制Bean的解析逻辑,以满足特定的业务需求。例如,你可以实现一个特殊的`BeanResolver`,用于按照特定的规则解析Bean,或者从非标准的地方获取Bean实例。 5. **解耦业务逻辑** + 通过使用`BeanResolver`,可以将业务逻辑与具体的Bean获取方式解耦。这样,在不同的环境或场景下,可以轻松地切换和替换`BeanResolver`的实现,而不影响业务逻辑的其他部分。 ### 五、接口源码 `BeanResolver` 接口允许在Spring的表达式语言(SpEL)中解析Bean引用。通过注册到评估上下文中,它可以根据给定的Bean名称查找相应的实例,支持使用 `@myBeanName` 和 `&myBeanName` 表达式来引用Bean,其中 `&` 前缀允许访问工厂Bean。 ```java /** * BeanResolver接口可以注册到评估上下文中,并且会为Bean引用 {@code @myBeanName} 和 {@code &myBeanName} 表达式提供解析支持。 * 当需要访问工厂Bean时,{@code &} 变体语法允许访问工厂Bean。 * * @author Andy Clement * @since 3.0.3 */ public interface BeanResolver { /** * 根据给定的名称查找Bean,并返回相应的实例。 * 对于尝试访问工厂Bean,名称需要以 & 前缀。 * @param context 当前的评估上下文 * @param beanName 要查找的Bean的名称 * @return 表示Bean的对象 * @throws AccessException 如果解析Bean时出现意外问题 */ Object resolve(EvaluationContext context, String beanName) throws AccessException; } ``` `BeanFactoryResolver` 实现了 `BeanResolver` 接口,用于在Spring的Bean工厂中解析Bean。它包含一个构造函数用于初始化,并实现了 `resolve` 方法来根据给定的Bean名称从Bean工厂中获取相应的Bean实例。 ```java /** * 用于与Spring的Bean工厂交互的EL bean解析器。 * * @author Juergen Hoeller * @since 3.0.4 */ public class BeanFactoryResolver implements BeanResolver { private final BeanFactory beanFactory; /** * 为给定的工厂创建一个新的BeanFactoryResolver。 * * @param beanFactory 要解析Bean名称的Bean工厂 */ public BeanFactoryResolver(BeanFactory beanFactory) { Assert.notNull(beanFactory, "BeanFactory must not be null"); this.beanFactory = beanFactory; } @Override public Object resolve(EvaluationContext context, String beanName) throws AccessException { try { return this.beanFactory.getBean(beanName); } catch (BeansException ex) { throw new AccessException("无法根据Bean工厂解析Bean引用", ex); } } } ``` ### 六、主要实现 1. **BeanFactoryResolver** + `BeanFactoryResolver` 是一个实现了 `BeanResolver` 接口的类,在 Spring 框架中用于从 Bean 工厂中解析 Bean。通过构造函数接收一个 BeanFactory 实例,在调用 resolve 方法时,根据给定的 Bean 名称从 BeanFactory 中获取相应的 Bean 实例。 ### 七、最佳实践 使用 `BeanResolver` 接口和 `BeanFactoryResolver` 实现类来解析 Spring 容器中的 Bean。首先,通过注解配置方式创建了一个 BeanFactory,然后使用 SpEL 表达式解析器解析表达式 `@myBean`,并将 `BeanFactoryResolver` 设置为评估上下文的 BeanResolver。最后,通过解析 SpEL 表达式获取到相应的 Bean 实例,并进行打印输出。 ```java public class BeanResolverDemo { public static void main(String[] args) { // 创建 BeanFactory // 这里使用注解配置的方式创建 BeanFactory BeanFactory beanFactory = new AnnotationConfigApplicationContext(MyBean.class).getBeanFactory(); // 创建一个SpEL表达式解析器 ExpressionParser parser = new SpelExpressionParser(); // 创建一个标准的评估上下文 StandardEvaluationContext context = new StandardEvaluationContext(); // 将 BeanFactoryResolver 设置为上下文的 BeanResolver context.setBeanResolver(new BeanFactoryResolver(beanFactory)); // 解析 SpEL 表达式,获取 Bean 实例 Object myBean = parser.parseExpression("@myBean").getValue(context); // 打印 Bean 实例 System.out.println("myBean = " + myBean); } } ``` 运行结果,表达式 `@myBean` 成功解析,并获取到了名为 `myBean` 的 Bean 实例。 ```properties myBean = com.xcs.spring.MyBean@34123d65 ``` ### 八、与其他组件的关系 1. **ExpressionParser** + `ExpressionParser` 是 Spring 框架中用于解析表达式的接口,它通常与 `BeanResolver` 接口一起使用。`ExpressionParser` 负责解析 SpEL 表达式,而 `BeanResolver` 则负责解析表达式中的 Bean 引用。 2. **EvaluationContext** + `EvaluationContext` 是 Spring 表达式解析过程中的上下文对象,用于提供表达式所需的变量、函数等信息。`BeanResolver` 接口通常作为 `EvaluationContext` 的一部分,用于解析表达式中的 Bean 引用。 3. **BeanFactory**: + `BeanFactory` 是 Spring 框架中的核心接口之一,用于管理和获取 Bean 实例。`BeanFactoryResolver` 类实现了 `BeanResolver` 接口,用于在 BeanFactory 中解析 Bean 引用。 ### 九、常见问题 1. **如何自定义 BeanResolver 的实现?** - 我们可能想要根据特定需求自定义 `BeanResolver` 的实现,例如从不同的数据源中获取 Bean 实例。解决方法通常包括创建一个新的类实现 `BeanResolver` 接口,并根据需要覆盖 `resolve` 方法。 2. **如何处理 Bean 解析失败的情况?** - 当 `BeanResolver` 无法解析请求的 Bean 时,可能会抛出异常。开发人员需要考虑如何处理这种异常情况,例如记录日志、返回默认值或者向用户提供友好的错误消息。 3. **如何在 SpEL 表达式中引用 Bean?** - 我们可能需要在 SpEL 表达式中引用 Spring 容器中的 Bean,以便执行特定的逻辑。通常情况下,可以使用 `@beanName` 或 `&beanName` 表达式来引用 Bean,其中 `@` 表示获取 Bean 实例,`&` 表示获取 Bean 的工厂实例。 4. **如何解决循环依赖问题?** - 当存在循环依赖的 Bean 时,可能会导致 `BeanResolver` 无法正常解析 Bean。我们需要谨慎设计 Bean 之间的依赖关系,或者使用延迟初始化等技术来解决循环依赖问题。 ================================================ FILE: spring-spel/spring-spel-beanResolver/pom.xml ================================================ com.xcs.spring spring-spel 0.0.1-SNAPSHOT 4.0.0 spring-spel-beanResolver ================================================ FILE: spring-spel/spring-spel-beanResolver/src/main/java/com/xcs/spring/BeanResolverDemo.java ================================================ package com.xcs.spring; import org.springframework.beans.factory.BeanFactory; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.expression.BeanFactoryResolver; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; public class BeanResolverDemo { public static void main(String[] args) { // 创建 BeanFactory // 这里使用注解配置的方式创建 BeanFactory BeanFactory beanFactory = new AnnotationConfigApplicationContext(MyBean.class).getBeanFactory(); // 创建一个SpEL表达式解析器 ExpressionParser parser = new SpelExpressionParser(); // 创建一个标准的评估上下文 StandardEvaluationContext context = new StandardEvaluationContext(); // 将 BeanFactoryResolver 设置为上下文的 BeanResolver context.setBeanResolver(new BeanFactoryResolver(beanFactory)); // 解析 SpEL 表达式,获取 Bean 实例 Object myBean = parser.parseExpression("@myBean").getValue(context); // 打印 Bean 实例 System.out.println("myBean = " + myBean); } } ================================================ FILE: spring-spel/spring-spel-beanResolver/src/main/java/com/xcs/spring/MyBean.java ================================================ package com.xcs.spring; public class MyBean { } ================================================ FILE: spring-spel/spring-spel-constructorResolver/README.md ================================================ ## ConstructorResolver - [ConstructorResolver](#ConstructorResolver) - [一、基本信息](#一基本信息) - [二、知识储备](#二知识储备) - [三、基本描述](#三基本描述) - [四、主要功能](#四主要功能) - [五、接口源码](#五接口源码) - [六、主要实现](#六主要实现) - [七、最佳实践](#七最佳实践) - [八、与其他组件的关系](#八与其他组件的关系) - [九、常见问题](#九常见问题) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、知识储备 1. **理解构造函数(Constructor)** + 构造函数是用于创建类的实例的特殊方法。了解构造函数的概念、使用方式以及与普通方法的区别是很重要的。 2. **反射(Reflection)** + 反射是Java语言的一个特性,允许程序在运行时检查和操作类、对象、方法和属性。学习如何使用`java.lang.reflect`包中的类来获取和操作构造函数是必要的。 3. **Spring表达式语言(SpEL)** + `ConstructorResolver`通常与Spring表达式语言(SpEL)一起使用。因此,了解SpEL的基本语法和用法是很有帮助的。 4. **设计模式** + 了解一些设计模式,特别是创建型模式(如工厂模式、建造者模式等),可以帮助理解对象创建的不同方法和策略。 ### 三、基本描述 `ConstructorResolver`接口是Spring框架中用于解析和执行构造函数的核心接口之一,它允许在运行时动态地确定和调用对象的构造函数。该接口定义了一个`resolve(ConstructorExecutor, TypedValue[])`方法,该方法用于根据给定的构造函数执行器和参数来解析构造函数。通过实现`ConstructorResolver`接口,Spring能够以灵活和动态的方式实现对象的实例化,从而使得依赖注入和对象创建更加灵活和可定制化。在Spring框架中的实现类中,通常会利用反射机制来动态地实例化对象,并根据对象的类型和参数列表来选择合适的构造函数进行调用。 ### 四、主要功能 1. **解析构造函数** + 接口定义了一个方法`resolve(ConstructorExecutor, TypedValue[])`,用于解析给定的构造函数。这意味着可以根据传入的参数和构造函数的签名来确定要调用的构造函数。 2. **执行构造函数** + 一旦构造函数被解析,接口负责调用相应的构造函数以创建对象实例。这涉及到创建实例、传递参数以及处理构造函数的执行。 3. **支持动态对象实例化** + `ConstructorResolver`接口使得在运行时能够动态地选择合适的构造函数实例化对象,从而提供了灵活性和可定制性。 ### 五、接口源码 `ConstructorResolver`接口定义了一个方法`resolve`,用于在给定的上下文中确定特定类型的合适构造函数,该构造函数能够处理指定的参数类型列表。接口返回一个`ConstructorExecutor`,可以用于调用找到的构造函数。 ```java /** * 构造函数解析器尝试定位一个构造函数,并返回一个ConstructorExecutor, * 该Executor可以用于调用该构造函数。ConstructorExecutor将被缓存, * 但如果它“过时”,则会重新调用解析器。 * * @author Andy Clement * @since 3.0 */ @FunctionalInterface public interface ConstructorResolver { /** * 在提供的上下文中确定指定类型上可以处理指定参数的合适构造函数。 * 返回一个ConstructorExecutor,可以用于调用该构造函数(如果找不到构造函数,则返回null)。 * * @param context 当前的评估上下文 * @param typeName 要查找构造函数的类型 * @param argumentTypes 构造函数必须能够处理的参数 * @return 一个ConstructorExecutor,可以调用该构造函数,如果找不到则返回null * @throws AccessException 访问异常 */ @Nullable ConstructorExecutor resolve(EvaluationContext context, String typeName, List argumentTypes) throws AccessException; } ``` `ReflectiveConstructorResolver`是Spring框架中的一个实现类,用于通过反射机制来定位应该被调用的构造函数。它能够根据提供的参数类型列表,对目标类的构造函数进行匹配,并选择合适的构造函数进行实例化。在匹配过程中,可能会发生三种类型的匹配:完全匹配、不完全匹配和需要类型转换的匹配。根据匹配结果,`ReflectiveConstructorResolver`将返回一个`ConstructorExecutor`对象,用于执行选定的构造函数。 ```java /** * 使用反射来定位应该被调用的构造函数的构造函数解析器。 * @author Andy Clement * @author Juergen Hoeller * @since 3.0 */ public class ReflectiveConstructorResolver implements ConstructorResolver { /** * 在类型上定位一个构造函数。可能会出现三种类型的匹配: *

    *
  1. 完全匹配,其中参数的类型与构造函数的类型相匹配 *
  2. 不完全匹配,其中我们要查找的类型是构造函数上定义的类型的子类型 *
  3. 匹配,其中我们能够将参数转换成构造函数预期的类型,根据已注册的类型转换器。 *
*/ @Override @Nullable public ConstructorExecutor resolve(EvaluationContext context, String typeName, List argumentTypes) throws AccessException { try { // 获取类型转换器和类类型 TypeConverter typeConverter = context.getTypeConverter(); Class type = context.getTypeLocator().findType(typeName); Constructor[] ctors = type.getConstructors(); // 按参数数量排序构造函数 Arrays.sort(ctors, Comparator.comparingInt(Constructor::getParameterCount)); // 初始化匹配变量 Constructor closeMatch = null; Constructor matchRequiringConversion = null; // 遍历构造函数 for (Constructor ctor : ctors) { int paramCount = ctor.getParameterCount(); List paramDescriptors = new ArrayList<>(paramCount); for (int i = 0; i < paramCount; i++) { paramDescriptors.add(new TypeDescriptor(new MethodParameter(ctor, i))); } ReflectionHelper.ArgumentsMatchInfo matchInfo = null; if (ctor.isVarArgs() && argumentTypes.size() >= paramCount - 1) { // 处理可变参数的匹配 matchInfo = ReflectionHelper.compareArgumentsVarargs(paramDescriptors, argumentTypes, typeConverter); } else if (paramCount == argumentTypes.size()) { // 处理普通参数的匹配 matchInfo = ReflectionHelper.compareArguments(paramDescriptors, argumentTypes, typeConverter); } if (matchInfo != null) { if (matchInfo.isExactMatch()) { return new ReflectiveConstructorExecutor(ctor); } else if (matchInfo.isCloseMatch()) { closeMatch = ctor; } else if (matchInfo.isMatchRequiringConversion()) { matchRequiringConversion = ctor; } } } // 返回匹配的构造函数执行器 if (closeMatch != null) { return new ReflectiveConstructorExecutor(closeMatch); } else if (matchRequiringConversion != null) { return new ReflectiveConstructorExecutor(matchRequiringConversion); } else { return null; } } catch (EvaluationException ex) { throw new AccessException("Failed to resolve constructor", ex); } } } ``` ### 六、主要实现 + **ReflectiveConstructorResolver** + 用于解析和执行构造函数的主要实现类。通过利用Java的反射机制,它能够动态地确定并调用类的构造函数,从而实现对象的实例化。 ### 七、最佳实践 创建了一个`SpelExpressionParser`对象,它是Spring表达式语言的解析器。然后,我们使用SpEL表达式`"new com.xcs.spring.MyBean('spring-reading')"`来创建一个`MyBean`对象,参数为`"spring-reading"`。接着,我们通过调用`getValue(MyBean.class)`方法来获取实例化后的`MyBean`对象。最后,我们打印输出了这个实例化的`MyBean`对象。 ```java public class ConstructorResolverDemo { public static void main(String[] args) { ExpressionParser parser = new SpelExpressionParser(); MyBean myBean = parser.parseExpression("new com.xcs.spring.MyBean('spring-reading')").getValue(MyBean.class); System.out.println(myBean); } } ``` 定义了一个简单的Java Bean,名为`MyBean`,具有一个私有属性`name`和一个带参构造函数、以及相应的getter和setter方法。此外,还重写了`toString()`方法,以便在打印对象时输出其属性值。 ```java public class MyBean { private String name; public MyBean(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "MyBean{" + "name='" + name + '\'' + '}'; } } ``` 运行结果,成功地创建了一个`MyBean`对象,并将其属性`name`设置为`"spring-reading"`。 ```properties MyBean{name='spring-reading'} ``` ### 八、与其他组件的关系 1. **EvaluationContext** + `ConstructorResolver`接口通常用于解析构造函数的过程中需要一个评估上下文,用于提供解析过程中所需的信息和环境,例如变量、函数、类型等。在Spring框架中,通常使用`EvaluationContext`接口或其实现类来表示评估上下文。 2. **TypedValue** + `ConstructorResolver`接口中的方法可能需要用到`TypedValue`对象,该对象用于表示表达式中的值,并提供了对值的类型信息的访问。在SpEL中,`TypedValue`通常用于表示表达式中的字面值、变量值等。 3. **ConstructorExecutor** + `ConstructorResolver`接口的实现类通常会返回一个`ConstructorExecutor`对象,该对象用于执行已解析的构造函数。`ConstructorExecutor`接口定义了一个`execute`方法,用于执行构造函数并返回结果对象。 4. **SpelExpressionParser** + `SpelExpressionParser`是Spring框架中用于解析SpEL表达式的核心类之一。在上下文中使用`SpelExpressionParser`来解析表达式时,可能会涉及到`ConstructorResolver`接口的实现类,用于处理表达式中的构造函数引用。 5. **ReflectiveConstructorResolver** + `ReflectiveConstructorResolver`是Spring框架中`ConstructorResolver`接口的主要实现类之一。它使用Java的反射机制来动态地解析和执行构造函数。 ### 九、常见问题 1. **如何处理构造函数参数的类型匹配问题?** - `ConstructorResolver`接口的实现类通常会根据提供的参数类型列表来选择合适的构造函数。如果参数类型与构造函数参数类型不匹配,可能会导致解析失败或选择错误的构造函数。因此,需要确保传递正确类型的参数。 2. **如何处理构造函数重载的情况?** - 当类中存在多个构造函数时,`ConstructorResolver`接口的实现类可能需要选择最适合的构造函数。通常情况下,会根据提供的参数类型列表和构造函数参数列表的匹配程度来进行选择。如果存在多个匹配的构造函数,可能会导致解析失败或选择不确定的构造函数。 3. **ConstructorResolver接口与Java反射机制的关系是什么?** - `ConstructorResolver`接口的实现类通常会利用Java的反射机制来动态地解析和执行构造函数。通过反射,可以在运行时检查类的结构,并调用特定的构造函数来实例化对象。因此,`ConstructorResolver`接口与Java反射机制密切相关。 4. **如何处理构造函数中可能抛出的异常?** - 在调用构造函数时,可能会出现各种异常,如参数类型不匹配、访问权限问题等。`ConstructorResolver`接口的实现类可能会处理这些异常,具体取决于实现。通常情况下,需要在调用构造函数之前进行适当的异常处理。 ================================================ FILE: spring-spel/spring-spel-constructorResolver/pom.xml ================================================ com.xcs.spring spring-spel 0.0.1-SNAPSHOT 4.0.0 spring-spel-constructorResolver ================================================ FILE: spring-spel/spring-spel-constructorResolver/src/main/java/com/xcs/spring/ConstructorResolverDemo.java ================================================ package com.xcs.spring; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; /** * @author xcs * @date 2024年3月12日11:28:47 **/ public class ConstructorResolverDemo { public static void main(String[] args) { // 创建一个SpEL表达式解析器 ExpressionParser parser = new SpelExpressionParser(); // 解析SpEL表达式,并使用构造函数实例化对象 // 这里的SpEL表达式是一个构造函数调用,创建了一个MyBean对象,参数为'spring-reading' MyBean myBean = parser.parseExpression("new com.xcs.spring.MyBean('spring-reading')").getValue(MyBean.class); // 打印输出实例化的MyBean对象 System.out.println(myBean); } } ================================================ FILE: spring-spel/spring-spel-constructorResolver/src/main/java/com/xcs/spring/MyBean.java ================================================ package com.xcs.spring; public class MyBean { private String name; public MyBean(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "MyBean{" + "name='" + name + '\'' + '}'; } } ================================================ FILE: spring-spel/spring-spel-evaluationContext/README.md ================================================ ## EvaluationContext - [EvaluationContext](#evaluationcontext) - [一、基本信息](#一基本信息) - [二、知识储备](#二知识储备) - [三、基本描述](#三基本描述) - [四、主要功能](#四主要功能) - [五、接口源码](#五接口源码) - [六、主要实现](#六主要实现) - [七、最佳实践](#七最佳实践) - [八、与其他组件的关系](#八与其他组件的关系) - [九、常见问题](#九常见问题) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、知识储备 1. **Spring Framework 基础** + 理解 Spring Framework 的核心概念和基本用法对于理解 `SpEL` 是至关重要的。这包括理解依赖注入、控制反转、面向切面编程等概念。 2. **Spring Expression Language(`SpEL`)** + 熟悉 `SpEL` 的语法、功能和用法是必要的。这包括了解如何在 Spring 应用程序中使用 `SpEL` 表达式,如何在 XML 配置、注解、Spring Data Query 方法等各种场景下使用 `SpEL`。 3. **Spring 表达式解析器** + 了解 Spring 如何解析和执行 `SpEL` 表达式是至关重要的。这包括熟悉 `SpEL` 的解析器、编译器、评估上下文等组件的工作原理和用法。 ### 三、基本描述 `EvaluationContext` 接口是 Spring Framework 中的一个关键接口,用于在运行时对 Spring 表达式语言(`SpEL`)表达式进行评估和处理。它提供了一个环境,用于管理 `SpEL` 表达式中的变量、函数、类型信息和其他上下文,以便在表达式求值时可以访问和操作这些信息。通过 `EvaluationContext`,我们可以定义和控制 `SpEL` 表达式的求值上下文,从而实现对应用程序对象图的动态访问和操作,以及实现诸如动态过滤、排序、计算等功能。 ### 四、主要功能 1. **管理变量** + `EvaluationContext` 允许在 `SpEL` 表达式中定义和使用变量。它提供了方法来设置和获取变量的值,使得在表达式求值时可以访问这些变量。 2. **管理函数** + 除了支持基本的运算符和操作之外,`SpEL` 还可以调用自定义函数。`EvaluationContext` 提供了注册和管理函数的方法,以便在表达式中调用这些函数。 3. **类型转换和类型检查** + 在 `SpEL` 表达式求值过程中,可能涉及到不同类型的数据操作,如类型转换和类型检查。`EvaluationContext` 提供了方法来执行这些操作,确保表达式中的操作可以正确执行。 4. **对象导航和方法调用** + `EvaluationContext` 提供了方法来执行对象导航和方法调用。这使得在 `SpEL` 表达式中可以直接访问对象的属性和方法,从而实现对应用程序对象图的动态访问和操作。 5. **安全性设置** + `EvaluationContext` 允许设置安全性相关的属性,如是否允许访问私有字段和方法、是否启用安全访问等,以确保表达式的安全性。 ### 五、接口源码 `EvaluationContext` 接口提供了 `SpEL` 表达式的评估上下文,用于支持在运行时对 `SpEL` 表达式进行求值和处理。其中,`EvaluationContext` 接口包括获取和设置根对象、获取属性访问器、构造函数解析器、方法解析器、bean 解析器等组件的方法,这些组件在表达式求值过程中起着重要的作用。此外,接口还提供了类型定位器、类型转换器、类型比较器和运算符重载器等功能,用于处理表达式中的类型转换、类型比较和运算符重载。最后,接口允许设置和查找命名变量,使得在表达式求值过程中可以动态地引用和修改变量值。综合来看,`EvaluationContext` 接口为 `SpEL` 表达式提供了必要的运行时环境和支持,使得我们能够灵活地处理和求值表达式。 ```java /** * 表达式在评估上下文中执行。在表达式评估过程中遇到引用时,将在此上下文中解析这些引用。 * *

有一个此 EvaluationContext 接口的默认实现: * {@link org.springframework.expression.spel.support.StandardEvaluationContext},可以进行扩展,而不必手动实现所有内容。 * * @author Andy Clement * @author Juergen Hoeller * @since 3.0 */ public interface EvaluationContext { /** * 返回默认的根上下文对象,对未限定的属性/方法等应进行解析的对象。 * 在评估表达式时可以覆盖此对象。 */ TypedValue getRootObject(); /** * 返回一个访问器列表,依次询问以读取/写入属性。 */ List getPropertyAccessors(); /** * 返回一个解析器列表,依次询问以查找构造函数。 */ List getConstructorResolvers(); /** * 返回一个解析器列表,依次询问以查找方法。 */ List getMethodResolvers(); /** * 返回一个 bean 解析器,可以按名称查找 bean。 */ @Nullable BeanResolver getBeanResolver(); /** * 返回一个类型定位器,可用于按短名称或完全限定名称查找类型。 */ TypeLocator getTypeLocator(); /** * 返回一个类型转换器,可以将一个值从一种类型转换(或强制转换)为另一种类型。 */ TypeConverter getTypeConverter(); /** * 返回一个类型比较器,用于比较对象对是否相等。 */ TypeComparator getTypeComparator(); /** * 返回一个运算符重载器,可能支持超出标准类型集的数学运算。 */ OperatorOverloader getOperatorOverloader(); /** * 将此评估上下文中的命名变量设置为指定的值。 * @param name 要设置的变量名称 * @param value 要放置在变量中的值 */ void setVariable(String name, @Nullable Object value); /** * 在此评估上下文中查找命名变量。 * @param name 要查找的变量名称 * @return 变量的值,如果未找到则返回 {@code null} */ @Nullable Object lookupVariable(String name); } ``` ### 六、主要实现 1. **StandardEvaluationContext** + `StandardEvaluationContext` 是 SpEL 默认的评估上下文实现,提供了对变量、函数、类型转换、类型检查等基本功能的支持。它是最常用的评估上下文实现,适用于大多数的 SpEL 使用场景。`StandardEvaluationContext` 也是其他上下文实现的基础。 2. **SimpleEvaluationContext** + `SimpleEvaluationContext` 是 `StandardEvaluationContext` 的另一个简化版本,它减少了一些不太常用的功能,从而提供了更轻量级的评估上下文实现。虽然功能相对较少,但是对于某些简单的应用场景或者对性能要求较高的场景,使用 `SimpleEvaluationContext` 可能是更合适的选择。 3. **CacheEvaluationContext** + `CacheEvaluationContext` 是 `StandardEvaluationContext` 的子类,它在标准的评估上下文的基础上增加了对表达式求值结果的缓存支持。这意味着对于相同的表达式,如果输入不变,将不会重复计算结果,而是直接返回缓存的值,从而提高了性能。这对于那些计算开销较大的表达式可以提供显著的性能提升。 4. **MethodBasedEvaluationContext** + `MethodBasedEvaluationContext` 是 `StandardEvaluationContext` 的另一个子类,它专门用于在表达式中调用方法。与标准评估上下文不同的是,`MethodBasedEvaluationContext` 允许将目标对象的方法暴露给 `SpEL` 表达式,并在表达式中进行调用。这使得在 `SpEL` 表达式中可以直接访问对象的方法,从而更加灵活地处理数据。 ### 七、最佳实践 使用 Spring Expression Language(`SpEL`)的 `EvaluationContext` 接口及其实现类 `StandardEvaluationContext` 来设置根对象、设置变量以及获取属性访问器、构造函数解析器、方法解析器、bean 解析器等上下文相关的信息。其中,通过创建一个自定义的根对象类 `MyRootObject` 并将其作为参数传递给 `StandardEvaluationContext` 构造函数来设置根对象,然后演示了如何设置变量、获取属性访问器、构造函数解析器等。 ```java public class EvaluationContextDemo { public static void main(String[] args) { // 创建评估上下文 EvaluationContext context = new StandardEvaluationContext(new MyRootObject("Root Data")); // 获取根对象 TypedValue root = context.getRootObject(); System.out.println("根对象: " + root.getValue()); // 设置变量 context.setVariable("name", "spring-reading"); System.out.println("变量'name'的值: " + context.lookupVariable("name")); // 获取属性访问器 List propertyAccessors = context.getPropertyAccessors(); System.out.println("属性访问器: " + propertyAccessors); // 获取构造函数解析器 List constructorResolvers = context.getConstructorResolvers(); System.out.println("构造函数解析器: " + constructorResolvers); // 获取方法解析器 List methodResolvers = context.getMethodResolvers(); System.out.println("方法解析器: " + methodResolvers); // 获取 bean 解析器 BeanResolver beanResolver = context.getBeanResolver(); System.out.println("bean 解析器: " + beanResolver); // 获取类型定位器 TypeLocator typeLocator = context.getTypeLocator(); System.out.println("类型定位器: " + typeLocator); // 获取类型转换器 TypeConverter typeConverter = context.getTypeConverter(); System.out.println("类型转换器: " + typeConverter); // 获取类型比较器 TypeComparator typeComparator = context.getTypeComparator(); System.out.println("类型比较器: " + typeComparator); // 获取运算符重载器 OperatorOverloader operatorOverloader = context.getOperatorOverloader(); System.out.println("运算符重载器: " + operatorOverloader); } // 定义根对象的类 static class MyRootObject { private String data; public MyRootObject(String data) { this.data = data; } public String getData() { return data; } public void setData(String data) { this.data = data; } } } ``` 运行结果发现,通过 `StandardEvaluationContext` 设置的评估上下文中的各种信息:根对象是 `EvaluationContextDemo` 类中定义的 `MyRootObject` 类的实例,变量 `'name'` 的值为 `'spring-reading'`,同时显示了使用的属性访问器、构造函数解析器、方法解析器、类型定位器、类型转换器、类型比较器以及运算符重载器等组件的具体实现类。 ```properties 根对象: com.xcs.spring.EvaluationContextDemo$MyRootObject@735b478 变量'name'的值: spring-reading 属性访问器: [org.springframework.expression.spel.support.ReflectivePropertyAccessor@13c78c0b] 构造函数解析器: [org.springframework.expression.spel.support.ReflectiveConstructorResolver@7ca48474] 方法解析器: [org.springframework.expression.spel.support.ReflectiveMetho dResolver@b97c004] bean 解析器: null 类型定位器: org.springframework.expression.spel.support.StandardTypeLocator@51565ec2 类型转换器: org.springframework.expression.spel.support.StandardTypeConverter@1c3a4799 类型比较器: org.springframework.expression.spel.support.StandardTypeComparator@131276c2 运算符重载器: org.springframework.expression.spel.support.StandardOperatorOverloader@26aa12dd ``` ### 八、与其他组件的关系 1. **StandardEvaluationContext** + `StandardEvaluationContext` 是 `EvaluationContext` 接口的一个实现类,它提供了一个标准的评估上下文实现,用于在 SpEL 表达式求值过程中管理上下文信息。 2. **TypedValue** + `TypedValue` 类用于包装表达式求值的结果,它与 `EvaluationContext` 接口一起工作,用于在表达式求值过程中传递和处理值。 3. **PropertyAccessor**、**ConstructorResolver**、**MethodResolver**、**BeanResolver** + 这些接口和类都与 `EvaluationContext` 接口相关联,用于支持 `SpEL` 表达式中属性、构造函数、方法和 bean 的解析和访问。 4. **TypeLocator**、**TypeConverter**、**TypeComparator**、**OperatorOverloader** + 这些接口和类也与 `EvaluationContext` 接口相关联,用于支持 `SpEL` 表达式中类型相关的操作和比较。 5. **ExpressionParser**、**Expression** + `ExpressionParser` 接口用于解析表达式字符串并返回 `Expression` 对象,而 `Expression` 接口用于表示已解析的表达式,它们与 `EvaluationContext` 接口一起工作,用于实际的表达式求值过程。 ### 九、常见问题 1. **如何创建和使用 `EvaluationContext` 实例?** - 通过 `StandardEvaluationContext` 或其子类来创建 `EvaluationContext` 实例,并使用其方法设置根对象、变量等上下文信息,然后将其传递给表达式求值器进行表达式求值。 2. **如何设置根对象和变量?** - 使用 `setRootObject` 方法设置根对象,使用 `setVariable` 方法设置变量。根对象是表达式求值的起点,而变量则提供了在表达式求值过程中使用的上下文信息。 3. **`EvaluationContext` 接口的实现类有哪些?它们有何不同?** - `EvaluationContext` 实现类包括 `StandardEvaluationContext`、`SimpleEvaluationContext` 等。它们在功能复杂度、性能等方面可能有所不同,我们可以根据具体需求选择合适的实现类。 4. **`EvaluationContext` 与 `ExpressionParser`、`Expression` 之间的关系是什么?** - `ExpressionParser` 用于解析表达式字符串并返回 `Expression` 对象,而 `EvaluationContext` 则用于在表达式求值过程中提供上下文信息。`Expression` 对象与 `EvaluationContext` 实例一起用于实际的表达式求值过程。 5. **`EvaluationContext` 如何与 SpEL 中的属性、方法、构造函数解析器等组件协作?** - `EvaluationContext` 可以通过其方法获取属性访问器、方法解析器、构造函数解析器等组件,并在表达式求值过程中使用它们来解析和访问属性、方法、构造函数等。 6. **如何处理表达式求值过程中可能出现的异常?** - 在表达式求值过程中可能出现各种异常,如语法错误、类型转换错误等。可以通过捕获异常并进行适当的处理来处理这些异常,以确保程序的稳定性和健壮性。 ================================================ FILE: spring-spel/spring-spel-evaluationContext/pom.xml ================================================ com.xcs.spring spring-spel 0.0.1-SNAPSHOT 4.0.0 spring-spel-evaluationContext ================================================ FILE: spring-spel/spring-spel-evaluationContext/src/main/java/com/xcs/spring/EvaluationContextDemo.java ================================================ package com.xcs.spring; import org.springframework.expression.*; import org.springframework.expression.spel.support.StandardEvaluationContext; import java.util.List; /** * @author xcs * @date 2024年3月12日11:28:47 **/ public class EvaluationContextDemo { public static void main(String[] args) { // 创建评估上下文 EvaluationContext context = new StandardEvaluationContext(new MyRootObject("Root Data")); // 获取根对象 TypedValue root = context.getRootObject(); System.out.println("根对象: " + root.getValue()); // 设置变量 context.setVariable("name", "John"); System.out.println("变量'name'的值: " + context.lookupVariable("name")); // 获取属性访问器 List propertyAccessors = context.getPropertyAccessors(); System.out.println("属性访问器: " + propertyAccessors); // 获取构造函数解析器 List constructorResolvers = context.getConstructorResolvers(); System.out.println("构造函数解析器: " + constructorResolvers); // 获取方法解析器 List methodResolvers = context.getMethodResolvers(); System.out.println("方法解析器: " + methodResolvers); // 获取 bean 解析器 BeanResolver beanResolver = context.getBeanResolver(); System.out.println("bean 解析器: " + beanResolver); // 获取类型定位器 TypeLocator typeLocator = context.getTypeLocator(); System.out.println("类型定位器: " + typeLocator); // 获取类型转换器 TypeConverter typeConverter = context.getTypeConverter(); System.out.println("类型转换器: " + typeConverter); // 获取类型比较器 TypeComparator typeComparator = context.getTypeComparator(); System.out.println("类型比较器: " + typeComparator); // 获取运算符重载器 OperatorOverloader operatorOverloader = context.getOperatorOverloader(); System.out.println("运算符重载器: " + operatorOverloader); } // 定义根对象的类 static class MyRootObject { private String data; public MyRootObject(String data) { this.data = data; } public String getData() { return data; } public void setData(String data) { this.data = data; } } } ================================================ FILE: spring-spel/spring-spel-expression/README.md ================================================ ## Expression - [Expression](#expression) - [一、基本信息](#一基本信息) - [二、知识储备](#二知识储备) - [三、基本描述](#三基本描述) - [四、主要功能](#四主要功能) - [五、接口源码](#五接口源码) - [六、主要实现](#六主要实现) - [七、最佳实践](#七最佳实践) - [八、时序图](#八时序图) - [九、源码分析](#九源码分析) - [十、与其他组件的关系](#十与其他组件的关系) - [十一、常见问题](#十一常见问题) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、知识储备 1. **表达式语言(SpEL)** + 理解表达式语言的基本语法和功能,包括表达式的结构、操作符、函数、变量引用等。熟悉 SpEL 的使用场景和用法,以及在 Spring 中如何应用 SpEL。 2. **评估上下文(Evaluation Context)** + 了解评估表达式时的上下文环境,包括根对象、变量、函数注册等。理解如何在评估表达式时提供自定义的上下文。 3. **类型转换和类型推断** + 理解表达式的类型转换机制,以及如何进行类型推断和类型安全的表达式评估。 4. **使用 SpEL 解析器** + 熟悉如何使用 `SpelExpressionParser` 类来解析表达式字符串并创建 `Expression` 对象,以及如何对表达式进行评估。 ### 三、基本描述 `Expression` 接口是 Spring Framework 中表达式语言(SpEL)模块的关键组件之一,它定义了评估表达式和获取表达式字符串表示形式的方法。通过该接口,我们可以使用 SpEL 强大的语法对对象进行动态查询和操作,实现了在 Spring 应用程序中灵活地处理配置、条件和数据转换等需求。该接口的实现类通常由 `SpelExpressionParser` 解析表达式字符串而创建,为 Spring 框架提供了一种便捷的方式来实现动态性和可扩展性。 ### 四、主要功能 1. **表达式求值(Evaluation)** + 该接口允许我们对表达式进行求值,从而获取表达式的结果。通过调用 `getValue()` 方法,可以在指定的评估上下文(Evaluation Context)中对表达式进行求值,并返回结果。这使得我们可以动态地在运行时获取表达式的结果,从而实现各种灵活的逻辑和业务需求。 2. **类型转换(Type Conversion)** + `Expression` 接口允许我们指定期望的结果类型,并在求值过程中进行类型转换。通过调用带有 `expectedType` 参数的 `getValue()` 方法,可以将表达式的结果转换为指定的目标类型。这对于确保表达式求值结果的类型安全性非常重要,并且能够避免类型转换错误。 3. **获取表达式字符串(Get Expression String)** + `Expression` 接口提供了 `getExpressionString()` 方法,用于获取表达式的字符串表示形式。这对于调试和日志记录非常有用,可以方便地查看表达式的实际内容,以便进行问题排查和分析。 ### 五、接口源码 `Expression` 接口是 Spring Framework 中表达式语言(SpEL)模块的核心组件之一,它封装了先前解析的表达式字符串,并提供了获取原始表达式字符串和在默认标准上下文中对表达式进行评估的方法。 ```java /** * 一个能够针对上下文对象自我评估的表达式。 * 封装了先前解析的表达式字符串的细节。 * 提供了对表达式评估的通用抽象。 * * @author Keith Donald * @author Andy Clement * @author Juergen Hoeller * @since 3.0 */ public interface Expression { /** * 返回用于创建此表达式的原始字符串(未修改)。 * @return 原始表达式字符串 */ String getExpressionString(); /** * 在默认标准上下文中评估此表达式。 * @return 评估结果 * @throws EvaluationException 如果在评估过程中出现问题 */ @Nullable Object getValue() throws EvaluationException; // ... [代码部分省略以简化] } ``` ### 六、主要实现 1. **CompositeStringExpression** + 用于表示复合字符串表达式,它由多个子表达式组成,每个子表达式都是一个字符串。 2. **LiteralExpression** + 用于表示字面值表达式,即直接返回给定的字面值,不进行任何计算或解析。 3. **SpelExpression** + SpEL 的默认实现之一,用于对 SpEL 表达式进行求值。 ### 七、最佳实践 使用 Spring Expression Language(SpEL)解析器来解析并求值一个基本的数学表达式。首先,创建了一个 SpEL 解析器实例,然后使用该解析器将字符串表达式 `"100 + 10"` 解析成一个 `Expression` 对象。接着,通过调用 `getValue(Integer.class)` 方法计算表达式的值,并将结果打印输出。 ```java public class ExpressionDemo { public static void main(String[] args) { // 创建解析器实例 ExpressionParser parser = new SpelExpressionParser(); // 解析基本表达式 Expression expression = parser.parseExpression("100 + 10"); // 为表达式计算结果 Integer result = expression.getValue(Integer.class); System.out.println("表达式 '100 + 10' 的结果为: " + result); } } ``` 运行结果,`Expression` 如何能够处理包括数学运算和变量替换在内的复杂表达式,并准确地计算出结果。 ```properties 表达式 '100 + 10' 的结果为: 110 ``` ### 八、时序图 ~~~mermaid sequenceDiagram autonumber title: Expression时序图 ExpressionDemo->>SpelExpression: getValue(expectedResultType) Note over ExpressionDemo,SpelExpression: 发起表达式求值请求 SpelExpression->>SpelNodeImpl: getTypedValue(expressionState) Note over SpelExpression,SpelNodeImpl: 调用内部节点获取值 SpelNodeImpl->>OpPlus: getValueInternal(state) Note over SpelNodeImpl,OpPlus: 处理加法操作 OpPlus->>SpelNodeImpl: 返回 TypedValue Note over OpPlus,SpelNodeImpl: 返回加法结果 SpelNodeImpl->>SpelExpression: 返回 TypedValue Note over SpelNodeImpl,SpelExpression: 将结果返回给 SpEL 表达式 SpelExpression->>ExpressionUtils: convertTypedValue(context,typedValue,targetType) Note over SpelExpression,ExpressionUtils: 转换结果类型 ExpressionUtils->>SpelExpression: 返回 SpEL 表达式结果 Note over ExpressionUtils,SpelExpression: 返回转换后的结果 SpelExpression->>ExpressionDemo: 返回 SpEL 表达式结果 Note over SpelExpression,ExpressionDemo: 返回表达式求值结果 ~~~ ### 九、源码分析 在`org.springframework.expression.spel.standard.SpelExpression#getValue(expectedResultType)`方法中,首先检查是否已经编译了表达式(通过检查 `compiledAst` 是否为 null),如果已编译,则尝试在编译后的抽象语法树上进行表达式求值。如果编译后的求值过程中出现异常,并且编译模式为 `SpelCompilerMode.MIXED`,则会将表达式恢复为解释模式。如果编译模式为 `SpelCompilerMode.IMMEDIATE`,则将异常抛出给调用者。如果尚未编译表达式,则在解释模式下对表达式进行求值。 ```java @Override @Nullable public T getValue(@Nullable Class expectedResultType) throws EvaluationException { CompiledExpression compiledAst = this.compiledAst; if (compiledAst != null) { try { // 获取评估上下文 EvaluationContext context = getEvaluationContext(); // 在编译后的抽象语法树上评估表达式 Object result = compiledAst.getValue(context.getRootObject().getValue(), context); // 如果期望的结果类型为 null,则直接返回结果 if (expectedResultType == null) { return (T) result; } else { // 否则将结果转换为期望的结果类型 return ExpressionUtils.convertTypedValue(getEvaluationContext(), new TypedValue(result), expectedResultType); } } catch (Throwable ex) { // 如果运行在混合模式下,则回退到解释模式 if (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) { // 恢复为解释模式 this.compiledAst = null; // 重置解释计数器 this.interpretedCount.set(0); } else { // 如果运行在即时编译模式下,将异常传播给调用者 throw new SpelEvaluationException(ex, SpelMessage.EXCEPTION_RUNNING_COMPILED_EXPRESSION); } } } // 如果尚未编译表达式,则在解释模式下评估表达式 ExpressionState expressionState = new ExpressionState(getEvaluationContext(), this.configuration); TypedValue typedResultValue = this.ast.getTypedValue(expressionState); // 检查编译状态 checkCompile(expressionState); // 将结果转换为期望的结果类型并返回 return ExpressionUtils.convertTypedValue(expressionState.getEvaluationContext(), typedResultValue, expectedResultType); } ``` 在`org.springframework.expression.spel.ast.SpelNodeImpl#getTypedValue`方法中,调用了 `getValueInternal()` 方法来获取表达式的值,并将其封装为 `TypedValue` 对象返回。这个方法用于在给定的表达式状态下获取表达式的值。 ```java @Override public final TypedValue getTypedValue(ExpressionState expressionState) throws EvaluationException { return getValueInternal(expressionState); } ``` 在`org.springframework.expression.spel.ast.OpPlus#getValueInternal`方法中,首先,它检查表达式的左操作数,如果只有一个操作数,则执行一元加法操作;如果有两个操作数,则执行加法操作。在执行加法操作时,它会根据操作数的类型进行相应的类型转换和计算,并返回计算结果。 ```java /** * 执行表达式的内部求值逻辑,主要用于处理加法操作。 * * @param state 表达式的状态 * @return 表达式的求值结果 * @throws EvaluationException 如果在求值过程中出现问题 */ @Override public TypedValue getValueInternal(ExpressionState state) throws EvaluationException { // 获取左操作数 SpelNodeImpl leftOp = getLeftOperand(); // 如果只有一个操作数,则执行一元加法操作 if (this.children.length < 2) { // 获取左操作数的值 Object operandOne = leftOp.getValueInternal(state).getValue(); // 如果左操作数是数字类型,则直接返回该数字 if (operandOne instanceof Number) { // 根据数字类型设置返回值的类型描述符 if (operandOne instanceof Double) { this.exitTypeDescriptor = "D"; } else if (operandOne instanceof Float) { this.exitTypeDescriptor = "F"; } else if (operandOne instanceof Long) { this.exitTypeDescriptor = "J"; } else if (operandOne instanceof Integer) { this.exitTypeDescriptor = "I"; } return new TypedValue(operandOne); } // 如果左操作数不是数字类型,则进行加法操作 return state.operate(Operation.ADD, operandOne, null); } // 获取左操作数的值 TypedValue operandOneValue = leftOp.getValueInternal(state); Object leftOperand = operandOneValue.getValue(); // 获取右操作数的值 TypedValue operandTwoValue = getRightOperand().getValueInternal(state); Object rightOperand = operandTwoValue.getValue(); // 如果左右操作数都是数字类型,则执行加法操作 if (leftOperand instanceof Number && rightOperand instanceof Number) { Number leftNumber = (Number) leftOperand; Number rightNumber = (Number) rightOperand; // 处理不同类型的数字相加的情况 if (...) { // 处理 BigDecimal 相加 } else if (...) { // 处理 Double 相加 } else if (...) { // 处理 Float 相加 } else if (...) { // 处理 BigInteger 相加 } else if (...) { // 处理 Long 相加 } else if (...) { // 处理 Integer 相加 } else { // 处理其他未知类型的相加,最佳猜测为 double 相加 } } // 如果左右操作数都是字符串类型,则执行字符串连接操作 if (leftOperand instanceof String && rightOperand instanceof String) { this.exitTypeDescriptor = "Ljava/lang/String"; return new TypedValue((String) leftOperand + rightOperand); } // 如果左操作数是字符串,则将右操作数转换为字符串并执行字符串连接操作 if (leftOperand instanceof String) { return new TypedValue( leftOperand + (rightOperand == null ? "null" : convertTypedValueToString(operandTwoValue, state))); } // 如果右操作数是字符串,则将左操作数转换为字符串并执行字符串连接操作 if (rightOperand instanceof String) { return new TypedValue( (leftOperand == null ? "null" : convertTypedValueToString(operandOneValue, state)) + rightOperand); } // 如果左右操作数不是数字类型或字符串类型,则执行加法操作 return state.operate(Operation.ADD, leftOperand, rightOperand); } ``` 在`org.springframework.expression.common.ExpressionUtils#convertTypedValue`方法中,首先,它获取 `TypedValue` 中的值,并检查目标类型是否为 null,如果是则直接返回值。然后,如果给定了评估上下文 `context`,则使用上下文的类型转换器将值转换为目标类型。如果没有给定上下文,则尝试使用 `ClassUtils.isAssignableValue()` 方法检查值是否可以直接赋值给目标类型。 ```java @Nullable public static T convertTypedValue( @Nullable EvaluationContext context, TypedValue typedValue, @Nullable Class targetType) { // 获取 TypedValue 中的值 Object value = typedValue.getValue(); // 如果目标类型为 null,则直接返回值 if (targetType == null) { return (T) value; } // 如果给定了评估上下文,则使用上下文的类型转换器进行转换 if (context != null) { return (T) context.getTypeConverter().convertValue( value, typedValue.getTypeDescriptor(), TypeDescriptor.valueOf(targetType)); } // 如果没有给定评估上下文,则尝试直接将值转换为目标类型 if (ClassUtils.isAssignableValue(targetType, value)) { return (T) value; } // 如果无法进行转换,则抛出异常 throw new EvaluationException("Cannot convert value '" + value + "' to type '" + targetType.getName() + "'"); } ``` ### 十、与其他组件的关系 1. **SpelExpressionParser** + `SpelExpressionParser` 类是用于解析表达式字符串并创建 `Expression` 对象的工厂类。它与 `Expression` 接口的关系是,通过调用 `parseExpression()` 方法创建 `Expression` 实例,从而实现对表达式的解析和处理。 2. **EvaluationContext** + `EvaluationContext` 接口定义了表达式的评估上下文,包括根对象、变量和函数注册等。`Expression` 接口通常需要一个 `EvaluationContext` 对象来执行表达式的求值操作。 3. **TypedValue** + `TypedValue` 类用于表示表达式的求值结果,包括值本身和值的类型描述符。在 `Expression` 接口的实现中,常常需要创建 `TypedValue` 对象来表示表达式的结果。 4. **ExpressionParser** + `ExpressionParser` 接口是用于解析表达式字符串并创建 `Expression` 对象的顶级接口,它定义了解析器的基本功能。`SpelExpressionParser` 类实现了 `ExpressionParser` 接口,因此与 `Expression` 接口的关系是,它是用于创建 `Expression` 实例的具体实现之一。 5. **EvaluationException** + `EvaluationException` 类是用于表示表达式求值过程中的异常情况,如语法错误、类型转换错误等。`Expression` 接口的实现通常会抛出 `EvaluationException` 异常来指示求值过程中出现的问题。 ### 十一、常见问题 1. **表达式语法错误** + 在编写表达式时可能会出现语法错误,例如括号未匹配、操作符使用错误等。 2. **类型转换错误** + 当表达式中的操作数类型与预期的结果类型不匹配时,可能会导致类型转换错误,例如将字符串转换为数字或将对象转换为不兼容的类型。 3. **空指针异常** + 如果在评估表达式时上下文对象为 null,或者表达式中涉及到的对象属性为 null,可能会导致空指针异常。 4. **表达式求值效率低下** + 某些复杂的表达式可能会导致求值效率较低,特别是在大数据量或高并发情况下。 5. **错误的结果** + 由于表达式求值逻辑错误或表达式解析错误,可能会导致错误的结果返回。 6. **不支持的操作** + 某些表达式可能包含不受支持的操作或函数调用,这可能会导致求值过程中抛出异常。 ================================================ FILE: spring-spel/spring-spel-expression/pom.xml ================================================ com.xcs.spring spring-spel 0.0.1-SNAPSHOT 4.0.0 spring-spel-expression ================================================ FILE: spring-spel/spring-spel-expression/src/main/java/com/xcs/spring/ExpressionDemo.java ================================================ package com.xcs.spring; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; public class ExpressionDemo { public static void main(String[] args) { // 创建解析器实例 ExpressionParser parser = new SpelExpressionParser(); // 解析基本表达式 Expression expression = parser.parseExpression("100 + 10"); // 为表达式计算结果 Integer result = expression.getValue(Integer.class); System.out.println("表达式 '100 + 10' 的结果为: " + result); } } ================================================ FILE: spring-spel/spring-spel-expressionParser/README.md ================================================ ## ExpressionParser - [ExpressionParser](#expressionparser) - [一、基本信息](#一基本信息) - [二、知识储备](#二知识储备) - [三、基本描述](#三基本描述) - [四、主要功能](#四主要功能) - [五、接口源码](#五接口源码) - [六、主要实现](#六主要实现) - [七、最佳实践](#七最佳实践) - [八、时序图](#八时序图) - [九、源码分析](#九源码分析) - [十、与其他组件的关系](#十与其他组件的关系) - [十一、常见问题](#十一常见问题) ### 一、基本信息 ✒ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、知识储备 1. **XML 和/或注解配置** + 熟悉使用 XML 或注解来配置 Spring 应用程序,因为 SpEL 常用于这些配置文件中。 2. **理解 SpEL 语法** + SpEL 有自己的语法规则。了解这些规则对于编写有效的 SpEL 表达式至关重要。 3. **了解 AOP 和事件处理** + 虽然不是必需的,但了解 Spring 中的 AOP 和事件处理机制对于理解 SpEL 在复杂场景下的应用是有益的。 ### 三、基本描述 `ExpressionParser` 接口是 Spring 框架中的一个关键组件,它专门用于解析和执行 Spring 表达式语言(SpEL)的表达式。SpEL 是一种功能丰富的表达式语言,允许在运行时动态地操作和查询对象图。通过 `ExpressionParser` 接口,我们可以将字符串形式的表达式转换为 `Expression` 对象,并对这些对象执行各种操作,例如获取值、设置值或执行复杂的表达式。 ### 四、主要功能 1. **解析表达式** + 它可以解析基于文本的表达式字符串,将其转换为 `Expression` 对象。这是它的核心功能,允许将动态表达式嵌入到应用程序中。 2. **表达式求值** + 通过解析得到的 `Expression` 对象,可以对表达式进行求值,以获取其运行时值。这可能涉及访问对象属性、调用方法、使用逻辑运算符等。 3. **设置表达式值** + 除了获取表达式的值,`ExpressionParser` 还可以用来修改目标对象的属性。通过表达式,可以直接设置对象的某个属性值。 4. **类型转换** + 它还支持类型转换功能,能夠在表达式求值过程中自动将值从一种类型转换为另一种类型。 5. **函数和运算符处理** + `ExpressionParser` 支持使用各种函数和运算符,例如数学运算、字符串操作、逻辑运算等。 6. **集合操作** + 支持对集合和数组的操作,包括选择(selection)、投影(projection)和聚合(aggregation)等。 7. **条件表达式** + 能够处理条件表达式,例如 if-then-else 结构,提供了增强的决策能力。 8. **模板解析** + 可以处理带有模板文本的表达式,例如拼接字符串与动态表达式的结合。 ### 五、接口源码 `ExpressionParser` 接口主要负责将表达式字符串转换为可评估的 `Expression` 对象。它支持解析标准表达式和模板,提供了两种方法来解析表达式:一种是基本解析,另一种允许通过上下文进行更灵活的解析。 ```java /** * 解析表达式字符串,编译成可评估的表达式。 * 支持解析标准表达式字符串以及模板。 * * @author Keith Donald * @author Andy Clement * @since 3.0 */ public interface ExpressionParser { /** * 解析表达式字符串并返回一个可用于重复评估的 Expression 对象。 *

一些例子: *

	 *     3 + 4
	 *     name.firstName
	 * 
* @param expressionString 需要解析的原始表达式字符串 * @return 已解析表达式的评估器 * @throws ParseException 解析过程中发生的异常 */ Expression parseExpression(String expressionString) throws ParseException; /** * 解析表达式字符串并返回一个可用于重复评估的 Expression 对象。 *

一些例子: *

	 *     3 + 4
	 *     name.firstName
	 * 
* @param expressionString 需要解析的原始表达式字符串 * @param context 影响此表达式解析过程的上下文(可选) * @return 已解析表达式的评估器 * @throws ParseException 解析过程中发生的异常 */ Expression parseExpression(String expressionString, ParserContext context) throws ParseException; } ``` ### 六、主要实现 1. **SpelExpressionParser** + 这是最常用的实现,提供了对 Spring 表达式语言(SpEL)的完整支持。它能够处理各种复杂的表达式,如方法调用、属性访问、关系运算符等。 ### 七、最佳实践 首先创建了一个 SpEL 表达式解析器实例 `SpelExpressionParser`,然后使用该解析器解析了一个简单的数学表达式 `"100 * 2 + 10"`,最后将`Expression` 打印输出。 ```java public class ExpressionParserDemo { public static void main(String[] args) { // 创建解析器实例 ExpressionParser parser = new SpelExpressionParser(); // 解析基本表达式 Expression expression = parser.parseExpression("100 * 2 + 10"); System.out.println("expression = " + expression); } } ``` 运行结果,`SpelExpressionParser` 解析给定表达式后返回的 `SpelExpression` 对象。这个对象包含了对表达式的内部表示,可以用于后续的表达式计算。 ```java expression = org.springframework.expression.spel.standard.SpelExpression@754ba872 ``` ### 八、时序图 ~~~mermaid sequenceDiagram autonumber title: ExpressionParser时序图 ExpressionParserDemo->>TemplateAwareExpressionParser: parseExpression(expressionString) TemplateAwareExpressionParser->>TemplateAwareExpressionParser: parseExpression(expressionString, context) TemplateAwareExpressionParser->>SpelExpressionParser: doParseExpression(expressionString, context) SpelExpressionParser->>InternalSpelExpressionParser: new InternalSpelExpressionParser(this.configuration) InternalSpelExpressionParser->>SpelExpressionParser: 返回parser SpelExpressionParser->>InternalSpelExpressionParser: doParseExpression(expressionString, context) InternalSpelExpressionParser->>Tokenizer: new Tokenizer(expressionString) Note over InternalSpelExpressionParser,Tokenizer: 字符串流分析为记号流 Tokenizer->>InternalSpelExpressionParser: 返回tokenizer InternalSpelExpressionParser->>Tokenizer: process() Note over InternalSpelExpressionParser,Tokenizer: 词法分析 InternalSpelExpressionParser->>InternalSpelExpressionParser: eatExpression() Note over InternalSpelExpressionParser: 生成抽象语法树 InternalSpelExpressionParser->>InternalSpelExpressionParser: eatLogicalOrExpression Note over InternalSpelExpressionParser: 解析逻辑或表达式 InternalSpelExpressionParser->>InternalSpelExpressionParser: eatLogicalAndExpression Note over InternalSpelExpressionParser: 解析逻辑与表达式 InternalSpelExpressionParser->>InternalSpelExpressionParser: eatRelationalExpression Note over InternalSpelExpressionParser: 解析关系表达式 InternalSpelExpressionParser->>InternalSpelExpressionParser: eatSumExpression Note over InternalSpelExpressionParser: 解析加减表达式 InternalSpelExpressionParser->>InternalSpelExpressionParser: eatProductExpression Note over InternalSpelExpressionParser: 解析乘除表达式 InternalSpelExpressionParser->>InternalSpelExpressionParser: eatPowerIncDecExpression Note over InternalSpelExpressionParser: 解析幂运算和自增自减表达式 InternalSpelExpressionParser->>InternalSpelExpressionParser: eatUnaryExpression Note over InternalSpelExpressionParser: 解析一元表达式 InternalSpelExpressionParser->>InternalSpelExpressionParser: eatPrimaryExpression Note over InternalSpelExpressionParser: 解析主表达式 InternalSpelExpressionParser->>InternalSpelExpressionParser: eatStartNode Note over InternalSpelExpressionParser: 解析起始节点 InternalSpelExpressionParser->>InternalSpelExpressionParser: maybeEatLiteral Note over InternalSpelExpressionParser: 尝试解析字面量 InternalSpelExpressionParser->>InternalSpelExpressionParser: maybeEatParenExpression Note over InternalSpelExpressionParser: 尝试解析括号表达式 InternalSpelExpressionParser->>InternalSpelExpressionParser: maybeEatTypeReference Note over InternalSpelExpressionParser: 尝试解析类型引用 InternalSpelExpressionParser->>InternalSpelExpressionParser: maybeEatNullReference Note over InternalSpelExpressionParser: 尝试解析null引用 InternalSpelExpressionParser->>InternalSpelExpressionParser: maybeEatConstructorReference Note over InternalSpelExpressionParser: 尝试解析构造函数引用 InternalSpelExpressionParser->>InternalSpelExpressionParser: maybeEatBeanReference Note over InternalSpelExpressionParser: 尝试解析Bean引用 InternalSpelExpressionParser->>InternalSpelExpressionParser: maybeEatProjection Note over InternalSpelExpressionParser: 尝试解析投影 InternalSpelExpressionParser->>InternalSpelExpressionParser: maybeEatSelection Note over InternalSpelExpressionParser: 尝试解析选择 InternalSpelExpressionParser->>InternalSpelExpressionParser: maybeEatIndexer Note over InternalSpelExpressionParser: 尝试解析索引器 InternalSpelExpressionParser->>InternalSpelExpressionParser: maybeEatInlineListOrMap Note over InternalSpelExpressionParser: 尝试解析内联列表或映射 InternalSpelExpressionParser->>SpelExpressionParser: 返回表达式对象 SpelExpressionParser->>TemplateAwareExpressionParser: 返回表达式对象 TemplateAwareExpressionParser->>ExpressionParserDemo: 返回表达式对象 ~~~ ### 九、源码分析 在`org.springframework.expression.common.TemplateAwareExpressionParser#parseExpression(expressionString)`方法中,接受一个字符串类型的表达式作为输入,并调用了重载的 `parseExpression` 方法来执行实际的解析操作。 ```java @Override public Expression parseExpression(String expressionString) throws ParseException { return parseExpression(expressionString, null); } ``` 在`org.springframework.expression.common.TemplateAwareExpressionParser#parseExpression(expressionString, context)`方法中,首先检查传入的 `ParserContext` 对象是否为模板模式,如果是,则调用 `parseTemplate` 方法来解析模板表达式;否则,调用 `doParseExpression` 方法来解析普通的表达式。 ```java @Override public Expression parseExpression(String expressionString, @Nullable ParserContext context) throws ParseException { if (context != null && context.isTemplate()) { return parseTemplate(expressionString, context); } else { return doParseExpression(expressionString, context); } } ``` 在`org.springframework.expression.spel.standard.SpelExpressionParser#doParseExpression`方法中,重写了 `doParseExpression` 方法。在这个方法中,它创建了一个 `InternalSpelExpressionParser` 对象,并调用了其 `doParseExpression` 方法来执行实际的 SpEL 表达式解析操作,然后返回解析得到的 `SpelExpression` 对象。 ```java @Override protected SpelExpression doParseExpression(String expressionString, @Nullable ParserContext context) throws ParseException { return new InternalSpelExpressionParser(this.configuration).doParseExpression(expressionString, context); } ``` 在`org.springframework.expression.spel.standard.InternalSpelExpressionParser#doParseExpression`方法中,首先对给定的表达式字符串进行分词处理,并通过构建抽象语法树(AST)来解析表达式。在解析过程中,它确保 AST 不为空,并且所有的令牌都已经处理。最后,基于解析得到的 AST 创建一个新的 `SpelExpression` 对象,并将其返回。 ```java @Override protected SpelExpression doParseExpression(String expressionString, @Nullable ParserContext context) throws ParseException { try { // 设置当前表达式字符串 this.expressionString = expressionString; // 对表达式字符串进行分词处理 Tokenizer tokenizer = new Tokenizer(expressionString); this.tokenStream = tokenizer.process(); this.tokenStreamLength = this.tokenStream.size(); this.tokenStreamPointer = 0; // 清空已构建节点的集合 this.constructedNodes.clear(); // 构建抽象语法树(AST) SpelNodeImpl ast = eatExpression(); // 确保 AST 不为空 Assert.state(ast != null, "No node"); // 检查是否还有未处理的令牌,如果有则抛出异常 Token t = peekToken(); if (t != null) { throw new SpelParseException(t.startPos, SpelMessage.MORE_INPUT, toString(nextToken())); } // 确保已构建节点的集合为空 Assert.isTrue(this.constructedNodes.isEmpty(), "At least one node expected"); // 创建并返回 SpEL 表达式对象 return new SpelExpression(expressionString, ast, this.configuration); } catch (InternalParseException ex) { // 抛出内部解析异常的原因 throw ex.getCause(); } } ``` 在`org.springframework.expression.spel.standard.Tokenizer#Tokenizer`方法中,接受一个字符串类型的输入数据,并将其初始化为 `Tokenizer` 类的实例。在构造函数中,输入数据被设置为类的成员变量 `expressionString`,并且将输入数据加上一个空字符以确保在处理过程中能够正确识别输入的末尾。然后,输入数据被转换为字符数组并赋值给 `charsToProcess`,同时确定了数组的长度,并将初始位置指针 `pos` 设置为0,表示在输入数据的开头位置。 ```java public Tokenizer(String inputData) { this.expressionString = inputData; this.charsToProcess = (inputData + "\0").toCharArray(); this.max = this.charsToProcess.length; this.pos = 0; } ``` 在`org.springframework.expression.spel.standard.Tokenizer#process`方法中,处理输入的字符串并生成对应的令牌列表。在处理过程中,它遍历输入字符串的每个字符,根据字符的类型执行相应的操作:对字母进行标识符解析,处理运算符和分隔符生成对应的令牌,解析数字字面量,忽略空白字符,处理引号括起的字符串字面量,以及处理到达字符串末尾的情况。最终,生成的令牌列表作为处理结果返回。 ```java public List process() { // 循环处理输入字符,直到到达输入字符串的末尾 while (this.pos < this.max) { // 获取当前位置的字符 char ch = this.charsToProcess[this.pos]; // 如果是字母,则解析标识符 if (isAlphabetic(ch)) { lexIdentifier(); } else { // 处理运算符和分隔符 switch (ch) { case '+': // 处理 "+" 号 if (isTwoCharToken(TokenKind.INC)) { pushPairToken(TokenKind.INC); } else { pushCharToken(TokenKind.PLUS); } break; // 省略其他情况的处理... case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': // 解析数值字面量 lexNumericLiteral(ch == '0'); break; // 处理空格、制表符、回车和换行符 case ' ': case '\t': case '\r': case '\n': // 忽略这些字符 this.pos++; break; case '\'': // 解析单引号括起的字符串字面量 lexQuotedStringLiteral(); break; case '"': // 解析双引号括起的字符串字面量 lexDoubleQuotedStringLiteral(); break; case 0: // 到达输入字符串的末尾 this.pos++; // 移动指针到字符串末尾 break; case '\\': // 抛出异常,不支持的转义字符 raiseParseException(this.pos, SpelMessage.UNEXPECTED_ESCAPE_CHAR); break; default: // 抛出异常,无法处理的字符 throw new IllegalStateException("Cannot handle (" + (int) ch + ") '" + ch + "'"); } } } // 返回处理后的令牌列表 return this.tokens; } ``` 在`org.springframework.expression.spel.standard.InternalSpelExpressionParser#eatExpression`方法中,首先调用 `eatLogicalOrExpression` 方法解析逻辑或表达式,然后检查下一个令牌的类型。如果下一个令牌是赋值操作符 `=`,则构造一个赋值节点 `Assign`;如果是 Elvis 操作符 `?:`,则构造一个 Elvis 节点;如果是条件运算符 `?`,则构造一个三元表达式节点 `Ternary`。 ```java @Nullable private SpelNodeImpl eatExpression() { // 解析逻辑或表达式 SpelNodeImpl expr = eatLogicalOrExpression(); // 获取下一个令牌 Token t = peekToken(); if (t != null) { // 如果下一个令牌是赋值操作符 "=" if (t.kind == TokenKind.ASSIGN) { // a=b // 如果逻辑或表达式为空,则构造一个空字面量节点 if (expr == null) { expr = new NullLiteral(t.startPos - 1, t.endPos - 1); } // 消耗赋值操作符 nextToken(); // 解析赋值操作符右侧的逻辑或表达式 SpelNodeImpl assignedValue = eatLogicalOrExpression(); // 构造赋值节点 return new Assign(t.startPos, t.endPos, expr, assignedValue); } // 如果下一个令牌是 Elvis 操作符 "?:" if (t.kind == TokenKind.ELVIS) { // a?:b (a if it isn't null, otherwise b) // 如果逻辑或表达式为空,则构造一个空字面量节点 if (expr == null) { expr = new NullLiteral(t.startPos - 1, t.endPos - 2); } // 消耗 Elvis 操作符 nextToken(); // elvis has left the building // 解析 Elvis 操作符右侧的表达式 SpelNodeImpl valueIfNull = eatExpression(); // 如果右侧表达式为空,则构造一个空字面量节点 if (valueIfNull == null) { valueIfNull = new NullLiteral(t.startPos + 1, t.endPos + 1); } // 构造 Elvis 节点 return new Elvis(t.startPos, t.endPos, expr, valueIfNull); } // 如果下一个令牌是条件运算符 "?" if (t.kind == TokenKind.QMARK) { // a?b:c // 如果逻辑或表达式为空,则构造一个空字面量节点 if (expr == null) { expr = new NullLiteral(t.startPos - 1, t.endPos - 1); } // 消耗条件运算符 nextToken(); // 解析条件运算符右侧的表达式 SpelNodeImpl ifTrueExprValue = eatExpression(); // 消耗冒号分隔符 eatToken(TokenKind.COLON); // 解析条件运算符的第三个操作数 SpelNodeImpl ifFalseExprValue = eatExpression(); // 构造三元表达式节点 return new Ternary(t.startPos, t.endPos, expr, ifTrueExprValue, ifFalseExprValue); } } // 返回解析得到的表达式节点 return expr; } ``` 在`org.springframework.expression.spel.standard.InternalSpelExpressionParser#eatLogicalOrExpression`方法中,首先调用 `eatLogicalAndExpression` 方法解析逻辑与表达式,并在循环中检查下一个令牌是否为 "or" 关键字或者符号 "||"。如果是,则消耗该令牌,然后解析逻辑与表达式的右操作数,并构造一个逻辑或操作节点。 ```java @Nullable private SpelNodeImpl eatLogicalOrExpression() { // 解析逻辑与表达式 SpelNodeImpl expr = eatLogicalAndExpression(); // 循环解析逻辑或操作 while (peekIdentifierToken("or") || peekToken(TokenKind.SYMBOLIC_OR)) { // 获取当前操作符令牌并消耗 Token t = takeToken(); //consume OR // 解析逻辑与表达式的右操作数 SpelNodeImpl rhExpr = eatLogicalAndExpression(); // 检查操作数有效性 checkOperands(t, expr, rhExpr); // 构造逻辑或操作节点 expr = new OpOr(t.startPos, t.endPos, expr, rhExpr); } // 返回解析得到的逻辑或表达式节点 return expr; } ``` 在`org.springframework.expression.spel.standard.InternalSpelExpressionParser#eatLogicalAndExpression`方法中,首先调用 `eatRelationalExpression` 方法解析关系表达式,并在循环中检查下一个令牌是否为 "and" 关键字或者符号 "&&"。如果是,则消耗该令牌,然后解析关系表达式的右操作数,并构造一个逻辑与操作节点。 ```java @Nullable private SpelNodeImpl eatLogicalAndExpression() { // 解析关系表达式 SpelNodeImpl expr = eatRelationalExpression(); // 循环解析逻辑与操作 while (peekIdentifierToken("and") || peekToken(TokenKind.SYMBOLIC_AND)) { // 获取当前操作符令牌并消耗 Token t = takeToken(); // consume 'AND' // 解析关系表达式的右操作数 SpelNodeImpl rhExpr = eatRelationalExpression(); // 检查操作数有效性 checkOperands(t, expr, rhExpr); // 构造逻辑与操作节点 expr = new OpAnd(t.startPos, t.endPos, expr, rhExpr); } // 返回解析得到的逻辑与表达式节点 return expr; } ``` 在`org.springframework.expression.spel.standard.InternalSpelExpressionParser#eatRelationalExpression`方法中,首先调用 `eatSumExpression` 方法解析加法表达式,然后尝试消耗可能存在的关系运算符。如果存在关系运算符,则解析右操作数,并根据不同的运算符类型构造相应的节点,包括大于、小于、大于等于、小于等于、等于、不等于、instanceof、matches 和 between 运算符。 ```java @Nullable private SpelNodeImpl eatRelationalExpression() { // 解析加法表达式 SpelNodeImpl expr = eatSumExpression(); // 尝试消耗可能存在的关系运算符 Token relationalOperatorToken = maybeEatRelationalOperator(); if (relationalOperatorToken != null) { // 消耗关系运算符 Token t = takeToken(); // 消耗关系运算符令牌 // 解析右操作数 SpelNodeImpl rhExpr = eatSumExpression(); // 检查操作数有效性 checkOperands(t, expr, rhExpr); TokenKind tk = relationalOperatorToken.kind; // 根据不同的运算符类型构造相应的节点 if (relationalOperatorToken.isNumericRelationalOperator()) { if (tk == TokenKind.GT) { return new OpGT(t.startPos, t.endPos, expr, rhExpr); } if (tk == TokenKind.LT) { return new OpLT(t.startPos, t.endPos, expr, rhExpr); } if (tk == TokenKind.LE) { return new OpLE(t.startPos, t.endPos, expr, rhExpr); } if (tk == TokenKind.GE) { return new OpGE(t.startPos, t.endPos, expr, rhExpr); } if (tk == TokenKind.EQ) { return new OpEQ(t.startPos, t.endPos, expr, rhExpr); } Assert.isTrue(tk == TokenKind.NE, "Not-equals token expected"); return new OpNE(t.startPos, t.endPos, expr, rhExpr); } // 处理instanceof运算符 if (tk == TokenKind.INSTANCEOF) { return new OperatorInstanceof(t.startPos, t.endPos, expr, rhExpr); } // 处理matches运算符 if (tk == TokenKind.MATCHES) { return new OperatorMatches(t.startPos, t.endPos, expr, rhExpr); } // 处理between运算符 Assert.isTrue(tk == TokenKind.BETWEEN, "Between token expected"); return new OperatorBetween(t.startPos, t.endPos, expr, rhExpr); } // 返回解析得到的关系表达式节点 return expr; } ``` 在`org.springframework.expression.spel.standard.InternalSpelExpressionParser#eatSumExpression`方法中,首先调用 `eatProductExpression` 方法解析乘法表达式,并在循环中检查下一个令牌是否为加法、减法或自增运算符。如果是,则消耗该令牌,然后解析乘法表达式的右操作数,并根据不同的运算符类型构造相应的节点(加法或减法节点)。 ```java @Nullable private SpelNodeImpl eatSumExpression() { // 解析乘法表达式 SpelNodeImpl expr = eatProductExpression(); // 循环解析加法表达式 while (peekToken(TokenKind.PLUS, TokenKind.MINUS, TokenKind.INC)) { // 获取当前操作符令牌并消耗 Token t = takeToken(); // 消耗 PLUS、MINUS 或 INC 令牌 // 解析乘法表达式的右操作数 SpelNodeImpl rhExpr = eatProductExpression(); // 检查右操作数的有效性 checkRightOperand(t, rhExpr); // 根据不同的运算符类型构造相应的节点 if (t.kind == TokenKind.PLUS) { // 构造加法节点 expr = new OpPlus(t.startPos, t.endPos, expr, rhExpr); } else if (t.kind == TokenKind.MINUS) { // 构造减法节点 expr = new OpMinus(t.startPos, t.endPos, expr, rhExpr); } } // 返回解析得到的加法表达式节点 return expr; } ``` 在`org.springframework.expression.spel.standard.InternalSpelExpressionParser#eatProductExpression`方法中,首先调用 `eatPowerIncDecExpression` 方法解析幂运算或自增自减表达式,并在循环中检查下一个令牌是否为乘法、除法或取模运算符。如果是,则消耗该令牌,然后解析幂运算或自增自减表达式的右操作数,并根据不同的运算符类型构造相应的节点(乘法、除法或取模节点)。 ```java @Nullable private SpelNodeImpl eatProductExpression() { // 解析幂运算或自增自减表达式 SpelNodeImpl expr = eatPowerIncDecExpression(); // 循环解析乘法表达式 while (peekToken(TokenKind.STAR, TokenKind.DIV, TokenKind.MOD)) { // 获取当前操作符令牌并消耗 Token t = takeToken(); // 消耗 STAR、DIV 或 MOD 令牌 // 解析幂运算或自增自减表达式的右操作数 SpelNodeImpl rhExpr = eatPowerIncDecExpression(); // 检查操作数有效性 checkOperands(t, expr, rhExpr); // 根据不同的运算符类型构造相应的节点 if (t.kind == TokenKind.STAR) { // 构造乘法节点 expr = new OpMultiply(t.startPos, t.endPos, expr, rhExpr); } else if (t.kind == TokenKind.DIV) { // 构造除法节点 expr = new OpDivide(t.startPos, t.endPos, expr, rhExpr); } else { Assert.isTrue(t.kind == TokenKind.MOD, "Mod token expected"); // 构造取模节点 expr = new OpModulus(t.startPos, t.endPos, expr, rhExpr); } } // 返回解析得到的乘法表达式节点 return expr; } ``` 在`org.springframework.expression.spel.standard.InternalSpelExpressionParser#eatPowerIncDecExpression`方法中,首先调用 `eatUnaryExpression` 方法解析一元表达式,并在条件语句中检查是否存在幂运算符或自增自减运算符。如果存在幂运算符,则解析幂运算符右侧的一元表达式,并构造幂运算节点;如果存在自增自减运算符,则根据运算符类型构造相应的自增自减节点。 ```java @Nullable private SpelNodeImpl eatPowerIncDecExpression() { // 解析一元表达式 SpelNodeImpl expr = eatUnaryExpression(); // 检查是否存在幂运算符 if (peekToken(TokenKind.POWER)) { // 获取并消耗幂运算符令牌 Token t = takeToken(); // 消耗 POWER // 解析幂运算符右侧的一元表达式 SpelNodeImpl rhExpr = eatUnaryExpression(); // 检查右操作数的有效性 checkRightOperand(t, rhExpr); // 构造幂运算节点 return new OperatorPower(t.startPos, t.endPos, expr, rhExpr); } // 如果不存在幂运算符,但存在自增自减运算符 if (expr != null && peekToken(TokenKind.INC, TokenKind.DEC)) { // 获取并消耗自增自减运算符令牌 Token t = takeToken(); // 消耗 INC/DEC // 根据运算符类型构造相应的自增自减节点 if (t.getKind() == TokenKind.INC) { return new OpInc(t.startPos, t.endPos, true, expr); } return new OpDec(t.startPos, t.endPos, true, expr); } // 如果既不存在幂运算符也不存在自增自减运算符,则直接返回解析得到的一元表达式节点 return expr; } ``` 在`org.springframework.expression.spel.standard.InternalSpelExpressionParser#eatUnaryExpression`方法中,首先检查下一个令牌是否为正号、负号或逻辑非运算符。如果是,则消耗该令牌,并递归调用 `eatUnaryExpression` 方法解析表达式的主体部分,并根据不同的运算符类型构造相应的节点(正号、负号或逻辑非节点)。如果下一个令牌是自增或自减运算符,则也消耗该令牌,并递归调用 `eatUnaryExpression` 方法解析表达式的主体部分,并根据运算符类型构造相应的自增或自减节点。如果以上条件都不满足,则调用 `eatPrimaryExpression` 方法解析主表达式。 ```java @Nullable private SpelNodeImpl eatUnaryExpression() { // 检查下一个令牌是否为正号、负号或逻辑非运算符 if (peekToken(TokenKind.PLUS, TokenKind.MINUS, TokenKind.NOT)) { // 获取并消耗运算符令牌 Token t = takeToken(); // 解析表达式的主体部分 SpelNodeImpl expr = eatUnaryExpression(); // 断言表达式不为空 Assert.state(expr != null, "No node"); // 根据运算符类型构造相应的节点 if (t.kind == TokenKind.NOT) { // 构造逻辑非节点 return new OperatorNot(t.startPos, t.endPos, expr); } if (t.kind == TokenKind.PLUS) { // 构造正号节点 return new OpPlus(t.startPos, t.endPos, expr); } // 如果不是正号,则是负号 Assert.isTrue(t.kind == TokenKind.MINUS, "Minus token expected"); // 构造负号节点 return new OpMinus(t.startPos, t.endPos, expr); } // 如果下一个令牌是自增或自减运算符 if (peekToken(TokenKind.INC, TokenKind.DEC)) { // 获取并消耗运算符令牌 Token t = takeToken(); // 解析表达式的主体部分 SpelNodeImpl expr = eatUnaryExpression(); // 根据运算符类型构造相应的自增或自减节点 if (t.getKind() == TokenKind.INC) { // 构造自增节点 return new OpInc(t.startPos, t.endPos, false, expr); } // 否则为自减节点 return new OpDec(t.startPos, t.endPos, false, expr); } // 解析主表达式 return eatPrimaryExpression(); } ``` 在`org.springframework.expression.spel.standard.InternalSpelExpressionParser#eatPrimaryExpression`方法中,首先调用 `eatStartNode` 方法解析起始节点,并初始化一个列表来存储后续的节点。然后进入循环,持续调用 `eatNode` 方法来解析后续节点,直到无法解析出新的节点为止。在循环中,如果节点列表为空,则将起始节点添加到列表中;否则,将解析得到的节点添加到列表中。最后,如果起始节点或节点列表为空,则直接返回起始节点;否则,构造一个复合表达式节点,其范围从起始节点的起始位置到最后一个节点的结束位置,包含所有解析得到的节点,并返回该复合表达式节点。 ```java @Nullable private SpelNodeImpl eatPrimaryExpression() { // 解析起始节点,通常为一个节点 SpelNodeImpl start = eatStartNode(); // 节点列表,用于存储后续的节点 List nodes = null; // 解析节点 SpelNodeImpl node = eatNode(); // 循环解析后续节点,直到无法解析出新的节点为止 while (node != null) { // 如果节点列表为空,则将起始节点添加到列表中 if (nodes == null) { nodes = new ArrayList<>(4); nodes.add(start); } // 将解析得到的节点添加到列表中 nodes.add(node); // 继续解析下一个节点 node = eatNode(); } // 如果起始节点或节点列表为空,则直接返回起始节点 if (start == null || nodes == null) { return start; } // 构造一个复合表达式节点,包含所有解析得到的节点 return new CompoundExpression(start.getStartPosition(), nodes.get(nodes.size() - 1).getEndPosition(), nodes.toArray(new SpelNodeImpl[0])); } ``` 在`org.springframework.expression.spel.standard.InternalSpelExpressionParser#eatStartNode`方法中,首先尝试解析字面量、括号表达式、类型引用、空引用、构造器引用、方法或属性、函数或变量、Bean引用、PROJECT、选择、索引器、内联列表或映射等不同类型的节点。 ```java @Nullable private SpelNodeImpl eatStartNode() { // 尝试解析字面量 if (maybeEatLiteral()) { // 返回解析的字面量节点 return pop(); } // 尝试解析括号表达式 else if (maybeEatParenExpression()) { // 返回解析的括号表达式节点 return pop(); } // 尝试解析类型引用、空引用、构造器引用、方法或属性、函数或变量等节点 else if (maybeEatTypeReference() || maybeEatNullReference() || maybeEatConstructorReference() || maybeEatMethodOrProperty(false) || maybeEatFunctionOrVar()) { // 返回解析得到的节点 return pop(); } // 尝试解析Bean引用 else if (maybeEatBeanReference()) { // 返回解析的Bean引用节点 return pop(); } // 尝试解析投影、选择、索引器节点 else if (maybeEatProjection(false) || maybeEatSelection(false) || maybeEatIndexer()) { // 返回解析得到的节点 return pop(); } // 尝试解析内联列表或映射节点 else if (maybeEatInlineListOrMap()) { // 返回解析得到的节点 return pop(); } // 如果无法解析出任何节点,则返回空 else { return null; } } ``` 在`org.springframework.expression.spel.standard.InternalSpelExpressionParser#maybeEatLiteral`方法中,首先检查下一个令牌是否存在,如果不存在,则返回 false;然后根据令牌的类型进行相应的处理:如果是整型字面量,则将其转换为整数字面量节点并入栈;如果是长整型字面量,则将其转换为长整数字面量节点并入栈;如果是十六进制整型字面量,则将其转换为对应的整数或长整数字面量节点并入栈;如果是实数或浮点数字面量,则将其转换为实数或浮点数字面量节点并入栈;如果是布尔值字面量 true,则入栈一个布尔值节点表示 true;如果是布尔值字面量 false,则入栈一个布尔值节点表示 false;如果是字符串字面量,则入栈一个字符串字面量节点;如果以上条件都不满足,则返回 false。最后,消耗当前令牌,并返回 true,表示成功解析字面量。 ```java private boolean maybeEatLiteral() { // 获取下一个令牌 Token t = peekToken(); // 如果令牌为空,则返回解析失败 if (t == null) { return false; } // 根据令牌类型进行相应处理 if (t.kind == TokenKind.LITERAL_INT) { // 将整型字面量转换为整数字面量节点并入栈 push(Literal.getIntLiteral(t.stringValue(), t.startPos, t.endPos, 10)); } else if (t.kind == TokenKind.LITERAL_LONG) { // 将长整型字面量转换为长整数字面量节点并入栈 push(Literal.getLongLiteral(t.stringValue(), t.startPos, t.endPos, 10)); } else if (t.kind == TokenKind.LITERAL_HEXINT) { // 将十六进制整型字面量转换为整数字面量节点并入栈 push(Literal.getIntLiteral(t.stringValue(), t.startPos, t.endPos, 16)); } else if (t.kind == TokenKind.LITERAL_HEXLONG) { // 将十六进制长整型字面量转换为长整数字面量节点并入栈 push(Literal.getLongLiteral(t.stringValue(), t.startPos, t.endPos, 16)); } else if (t.kind == TokenKind.LITERAL_REAL) { // 将实数字面量转换为实数数字面量节点并入栈 push(Literal.getRealLiteral(t.stringValue(), t.startPos, t.endPos, false)); } else if (t.kind == TokenKind.LITERAL_REAL_FLOAT) { // 将浮点数字面量转换为浮点数数字面量节点并入栈 push(Literal.getRealLiteral(t.stringValue(), t.startPos, t.endPos, true)); } else if (peekIdentifierToken("true")) { // 入栈一个布尔值节点表示 true push(new BooleanLiteral(t.stringValue(), t.startPos, t.endPos, true)); } else if (peekIdentifierToken("false")) { // 入栈一个布尔值节点表示 false push(new BooleanLiteral(t.stringValue(), t.startPos, t.endPos, false)); } else if (t.kind == TokenKind.LITERAL_STRING) { // 入栈一个字符串字面量节点 push(new StringLiteral(t.stringValue(), t.startPos, t.endPos, t.stringValue())); } else { // 如果令牌类型不是字面量,则返回解析失败 return false; } // 消耗当前令牌,并返回解析成功 nextToken(); return true; } ``` 在`org.springframework.expression.spel.standard.InternalSpelExpressionParser#maybeEatParenExpression`方法中,首先检查下一个令牌是否为左括号,如果是,则消耗该令牌,并解析括号内的表达式;然后检查是否成功解析出表达式节点,如果成功,则消耗右括号并将表达式节点入栈,最后返回解析成功;如果下一个令牌不是左括号,则直接返回解析失败。 ```java private boolean maybeEatParenExpression() { // 如果下一个令牌是左括号 if (peekToken(TokenKind.LPAREN)) { // 消耗左括号令牌 nextToken(); // 解析括号内的表达式 SpelNodeImpl expr = eatExpression(); // 断言确保表达式节点非空 Assert.state(expr != null, "No node"); // 消耗右括号令牌 eatToken(TokenKind.RPAREN); // 将表达式节点入栈 push(expr); // 返回解析成功 return true; } else { // 如果下一个令牌不是左括号,则返回解析失败 return false; } } ``` 在`org.springframework.expression.spel.standard.InternalSpelExpressionParser#maybeEatTypeReference`方法中,首先检查下一个令牌是否为标识符,如果是,则进一步检查该标识符是否为 "T",如果不是,则返回解析失败;如果标识符为 "T",则继续检查下一个令牌是否为右方括号,如果是,则将 "T" 视为映射的键,创建一个属性或字段引用节点,并入栈,最后返回解析成功;如果下一个令牌不是右方括号,则进一步解析括号内的内容,包括可能的限定标识符和数组维度,并构造一个类型引用节点,入栈,并返回解析成功;如果下一个令牌不是标识符,则直接返回解析失败。 ```java private boolean maybeEatTypeReference() { // 如果下一个令牌是标识符 if (peekToken(TokenKind.IDENTIFIER)) { // 获取下一个令牌 Token typeName = peekToken(); // 断言确保令牌不为空 Assert.state(typeName != null, "Expected token"); // 如果标识符不是 "T",则返回解析失败 if (!"T".equals(typeName.stringValue())) { return false; } // 获取下一个令牌,用于进一步判断是否是映射的键 Token t = takeToken(); // 如果下一个令牌是右方括号,则将 "T" 视为映射的键,创建属性或字段引用节点,并入栈,最后返回解析成功 if (peekToken(TokenKind.RSQUARE)) { // 创建属性或字段引用节点,表示 "T" 作为映射的键 push(new PropertyOrFieldReference(false, t.stringValue(), t.startPos, t.endPos)); return true; } // 否则,继续解析括号内的内容 // 消耗左括号令牌 eatToken(TokenKind.LPAREN); // 解析可能的限定标识符 SpelNodeImpl node = eatPossiblyQualifiedId(); // 检查是否存在数组维度 int dims = 0; while (peekToken(TokenKind.LSQUARE, true)) { // 消耗左方括号和右方括号令牌,统计数组维度 eatToken(TokenKind.RSQUARE); dims++; } // 消耗右括号令牌 eatToken(TokenKind.RPAREN); // 创建类型引用节点,并入栈,最后返回解析成功 this.constructedNodes.push(new TypeReference(typeName.startPos, typeName.endPos, node, dims)); return true; } // 如果下一个令牌不是标识符,则返回解析失败 return false; } ``` 在`org.springframework.expression.spel.standard.InternalSpelExpressionParser#maybeEatNullReference`方法中,首先检查下一个令牌是否为标识符,如果是,则进一步检查该标识符是否为 "null",如果不是,则返回解析失败;如果标识符为 "null",则将其解析为一个空字面量节点,并将其入栈,最后返回解析成功;如果下一个令牌不是标识符,则直接返回解析失败。 ```java private boolean maybeEatNullReference() { // 如果下一个令牌是标识符 if (peekToken(TokenKind.IDENTIFIER)) { // 获取下一个令牌 Token nullToken = peekToken(); // 断言确保令牌不为空 Assert.state(nullToken != null, "Expected token"); // 如果标识符不是 "null",则返回解析失败 if (!"null".equalsIgnoreCase(nullToken.stringValue())) { return false; } // 消耗 "null" 标识符令牌 nextToken(); // 创建空字面量节点,并入栈,最后返回解析成功 this.constructedNodes.push(new NullLiteral(nullToken.startPos, nullToken.endPos)); return true; } // 如果下一个令牌不是标识符,则返回解析失败 return false; } ``` 在`org.springframework.expression.spel.standard.InternalSpelExpressionParser#maybeEatConstructorReference`方法中,首先检查下一个令牌是否为 "new" 关键字,如果是,则继续解析构造函数引用的可能形式。如果 "new" 后面紧跟着右方括号,则将 "new" 视为映射的键,创建一个属性或字段引用节点,并入栈,最后返回解析成功;如果 "new" 后面是其他令牌,则继续解析构造函数引用的形式,包括可能的限定构造函数名和构造函数参数列表。如果构造函数参数列表中包含数组初始化器,则解析数组维度和内联列表或映射,最后创建构造函数引用节点,并入栈,最后返回解析成功;如果构造函数参数列表中不包含数组初始化器,则继续解析构造函数参数列表,最后创建构造函数引用节点,并入栈,最后返回解析成功;如果下一个令牌不是 "new" 关键字,则直接返回解析失败。 ```java private boolean maybeEatConstructorReference() { // 如果下一个令牌是 "new" 关键字 if (peekIdentifierToken("new")) { // 获取 "new" 令牌 Token newToken = takeToken(); // 如果 "new" 后面是右方括号,则将 "new" 视为映射的键 if (peekToken(TokenKind.RSQUARE)) { // 创建一个属性或字段引用节点,并入栈,最后返回解析成功 push(new PropertyOrFieldReference(false, newToken.stringValue(), newToken.startPos, newToken.endPos)); return true; } // 继续解析构造函数引用的可能形式 SpelNodeImpl possiblyQualifiedConstructorName = eatPossiblyQualifiedId(); List nodes = new ArrayList<>(); nodes.add(possiblyQualifiedConstructorName); // 如果构造函数参数列表中包含数组初始化器 if (peekToken(TokenKind.LSQUARE)) { // 解析数组维度和内联列表或映射,创建构造函数引用节点,并入栈,最后返回解析成功 eatConstructorArgs(nodes); push(new ConstructorReference(newToken.startPos, newToken.endPos, nodes.toArray(new SpelNodeImpl[0]))); } else { // 继续解析构造函数参数列表,创建构造函数引用节点,并入栈,最后返回解析成功 eatConstructorArgs(nodes); // 创建构造函数引用节点,并入栈,最后返回解析成功 push(new ConstructorReference(newToken.startPos, newToken.endPos, nodes.toArray(new SpelNodeImpl[0]))); } return true; } // 如果下一个令牌不是 "new" 关键字,则直接返回解析失败 return false; } ``` 在`org.springframework.expression.spel.standard.InternalSpelExpressionParser#maybeEatBeanReference`方法中,用于尝试解析Bean引用。如果下一个令牌是 `TokenKind.BEAN_REF` 或 `TokenKind.FACTORY_BEAN_REF`,则将其视为Bean引用。然后尝试获取Bean名称,可以是标识符或字面字符串。解析完成后,根据Bean引用类型创建相应的 `BeanReference` 实例,并将其入栈。最后返回解析结果。 ```java private boolean maybeEatBeanReference() { // 如果下一个令牌是 BEAN_REF 或 FACTORY_BEAN_REF if (peekToken(TokenKind.BEAN_REF) || peekToken(TokenKind.FACTORY_BEAN_REF)) { // 获取 BEAN_REF 或 FACTORY_BEAN_REF 令牌 Token beanRefToken = takeToken(); Token beanNameToken = null; String beanName = null; // 如果下一个令牌是标识符,则表示 Bean 名称 if (peekToken(TokenKind.IDENTIFIER)) { // 获取标识符令牌作为 Bean 名称 beanNameToken = eatToken(TokenKind.IDENTIFIER); beanName = beanNameToken.stringValue(); } // 如果下一个令牌是字符串字面量,则表示 Bean 名称 else if (peekToken(TokenKind.LITERAL_STRING)) { // 获取字符串字面量令牌作为 Bean 名称 beanNameToken = eatToken(TokenKind.LITERAL_STRING); beanName = beanNameToken.stringValue(); // 去除字符串两侧的引号,获取真实的 Bean 名称 beanName = beanName.substring(1, beanName.length() - 1); } // 如果 Bean 名称不是标识符或字符串字面量,则抛出异常 else { throw internalException(beanRefToken.startPos, SpelMessage.INVALID_BEAN_REFERENCE); } BeanReference beanReference; // 根据 Bean 引用类型创建相应的 BeanReference 实例 if (beanRefToken.getKind() == TokenKind.FACTORY_BEAN_REF) { // 如果是 FACTORY_BEAN_REF,则在 Bean 名称前添加前缀字符串 String beanNameString = String.valueOf(TokenKind.FACTORY_BEAN_REF.tokenChars) + beanName; beanReference = new BeanReference(beanRefToken.startPos, beanNameToken.endPos, beanNameString); } else { // 如果是 BEAN_REF,则直接使用 Bean 名称 beanReference = new BeanReference(beanNameToken.startPos, beanNameToken.endPos, beanName); } // 将 BeanReference 实例入栈,表示解析成功 this.constructedNodes.push(beanReference); return true; } // 如果下一个令牌不是 BEAN_REF 或 FACTORY_BEAN_REF,则直接返回解析失败 return false; } ``` 在`org.springframework.expression.spel.standard.InternalSpelExpressionParser#maybeEatProjection`方法中,用于尝试解析投影操作符(Projection),该操作符表示对集合进行投影操作,返回一个新的集合,其中每个元素都是对原集合元素的转换。若当前令牌为 PROJECT,则尝试解析表达式并创建 Projection 节点,表示成功解析。 ```java private boolean maybeEatProjection(boolean nullSafeNavigation) { Token t = peekToken(); // 获取当前令牌 // 检查当前令牌是否为投影操作符,如果不是则返回 false if (!peekToken(TokenKind.PROJECT, true)) { return false; } // 断言当前令牌非空 Assert.state(t != null, "No token"); // 解析投影操作符后的表达式 SpelNodeImpl expr = eatExpression(); // 断言表达式节点非空 Assert.state(expr != null, "No node"); // 解析右方括号 eatToken(TokenKind.RSQUARE); // 创建 Projection 节点并将其压入节点栈中 this.constructedNodes.push(new Projection(nullSafeNavigation, t.startPos, t.endPos, expr)); return true; // 成功解析投影操作符,返回 true } ``` 在`org.springframework.expression.spel.standard.InternalSpelExpressionParser#maybeEatSelection`方法中,解析选择操作符("![...]")是否存在,如果存在则解析选择操作符的表达式。然后根据选择操作符的类型(FIRST、LAST 或 ALL)创建相应的 Selection 节点,并将其压入节点栈中,最后返回 true 表示成功解析选择操作符,否则返回 false。 ```java private boolean maybeEatSelection(boolean nullSafeNavigation) { Token t = peekToken(); // 如果当前标记不是选择操作符,则返回false if (!peekSelectToken()) { return false; } Assert.state(t != null, "No token"); // 消耗选择操作符标记 nextToken(); // 解析选择表达式 SpelNodeImpl expr = eatExpression(); // 如果表达式为空,则抛出异常 if (expr == null) { throw internalException(t.startPos, SpelMessage.MISSING_SELECTION_EXPRESSION); } // 消耗右方括号标记 eatToken(TokenKind.RSQUARE); // 根据不同的选择操作符创建相应的选择节点并压入解析节点栈中 if (t.kind == TokenKind.SELECT_FIRST) { this.constructedNodes.push(new Selection(nullSafeNavigation, Selection.FIRST, t.startPos, t.endPos, expr)); } else if (t.kind == TokenKind.SELECT_LAST) { this.constructedNodes.push(new Selection(nullSafeNavigation, Selection.LAST, t.startPos, t.endPos, expr)); } else { this.constructedNodes.push(new Selection(nullSafeNavigation, Selection.ALL, t.startPos, t.endPos, expr)); } return true; } ``` 在`org.springframework.expression.spel.standard.InternalSpelExpressionParser#maybeEatIndexer`方法中,尝试解析索引器表达式。如果当前标记是左方括号,则解析相应的表达式作为索引器的索引,并将其压入解析节点栈中。 ```java private boolean maybeEatIndexer() { // 获取当前标记 Token t = peekToken(); // 如果当前标记不是 '[',则返回 false if (!peekToken(TokenKind.LSQUARE, true)) { return false; } // 断言当前标记不为空 Assert.state(t != null, "No token"); // 解析索引表达式 SpelNodeImpl expr = eatExpression(); // 断言索引表达式不为空 Assert.state(expr != null, "No node"); // 解析 ']' eatToken(TokenKind.RSQUARE); // 创建索引器节点并推入堆栈 this.constructedNodes.push(new Indexer(t.startPos, t.endPos, expr)); return true; } ``` 尝试解析内联列表或映射字面量。根据当前标记的不同情况,它可以解析空列表、空映射或包含一个或多个表达式的列表或映射。通过逐一解析表达式并检查后续标记,该方法构建相应的内联列表或映射节点,并将其推送到解析节点栈中。 ```java private boolean maybeEatInlineListOrMap() { // 获取当前标记 Token t = peekToken(); // 如果当前标记不是 '{',则返回 false if (!peekToken(TokenKind.LCURLY, true)) { return false; } // 断言当前标记不为空 Assert.state(t != null, "No token"); // 初始化节点 SpelNodeImpl expr = null; // 获取下一个标记 Token closingCurly = peekToken(); // 如果下一个标记是 '}',表示是一个空列表 '{}' if (peekToken(TokenKind.RCURLY, true)) { // 断言下一个标记不为空 Assert.state(closingCurly != null, "No token"); // 创建一个空的内联列表 expr = new InlineList(t.startPos, closingCurly.endPos); } // 如果下一个标记是 ':',表示是一个空映射 '{:}' else if (peekToken(TokenKind.COLON, true)) { // 获取 '}' closingCurly = eatToken(TokenKind.RCURLY); // 创建一个空的内联映射 expr = new InlineMap(t.startPos, closingCurly.endPos); } // 如果不是空列表或空映射,继续解析表达式 else { // 解析第一个表达式 SpelNodeImpl firstExpression = eatExpression(); // 下一个标记可能是 '}' if (peekToken(TokenKind.RCURLY)) { // 创建只有一个元素的列表 List elements = new ArrayList<>(); elements.add(firstExpression); closingCurly = eatToken(TokenKind.RCURLY); expr = new InlineList(t.startPos, closingCurly.endPos, elements.toArray(new SpelNodeImpl[0])); } // 下一个标记可能是逗号 ',',表示是多项列表 else if (peekToken(TokenKind.COMMA, true)) { // 创建多项列表 List elements = new ArrayList<>(); elements.add(firstExpression); do { elements.add(eatExpression()); } while (peekToken(TokenKind.COMMA, true)); closingCurly = eatToken(TokenKind.RCURLY); expr = new InlineList(t.startPos, closingCurly.endPos, elements.toArray(new SpelNodeImpl[0])); } // 下一个标记可能是冒号 ':',表示是映射 else if (peekToken(TokenKind.COLON, true)) { // 创建映射 List elements = new ArrayList<>(); elements.add(firstExpression); elements.add(eatExpression()); while (peekToken(TokenKind.COMMA, true)) { elements.add(eatExpression()); eatToken(TokenKind.COLON); elements.add(eatExpression()); } closingCurly = eatToken(TokenKind.RCURLY); expr = new InlineMap(t.startPos, closingCurly.endPos, elements.toArray(new SpelNodeImpl[0])); } // 若以上情况均不满足,则抛出异常 else { throw internalException(t.startPos, SpelMessage.OOD); } } // 将解析的节点推入堆栈 this.constructedNodes.push(expr); return true; } ``` ### 十、与其他组件的关系 1. **Expression** + 解析表达式字符串时,`SpelExpressionParser` 返回实现了 `Expression` 接口的对象。这些对象代表解析后的表达式,可以用于获取表达式的值、设置值或执行表达式。 2. **EvaluationContext** + 在评估表达式时,可以使用实现 `EvaluationContext` 接口的对象,如 `StandardEvaluationContext`,来提供表达式运行时的上下文信息(例如变量定义)。这有助于增强表达式的动态性和灵活性。 3. **ParseException&EvaluationException** + 这些是处理表达式解析和评估时可能出现的异常的类。如果在解析或评估表达式时出现错误,将抛出这些异常。 4. **PropertyAccessor&MethodResolver** + 这些类和接口用于在评估表达式时解析对象属性和方法调用。它们允许 `SpelExpressionParser` 与 Java 对象的属性和方法交互,实现复杂的逻辑。 5. **TypeConverter** + 用于在表达式计算过程中进行类型转换,使得可以在不同类型之间自由转换值。 ### 十一、常见问题 1. **表达式语法错误** + 编写 SpEL 表达式时,常见的错误包括拼写错误、错误的符号或操作符使用。这些错误通常会在解析表达式时抛出 `ParseException`。 2. **性能问题** + 频繁解析和评估复杂的 SpEL 表达式可能会影响应用性能。合理缓存解析后的表达式对象可以帮助缓解这一问题。 3. **上下文变量未找到** + 如果在表达式中使用了上下文(Context)中未定义的变量,将会抛出异常。确保所有在表达式中使用的变量都已在上下文中定义。 4. **类型转换问题** + 在表达式求值过程中,可能会出现类型不匹配或不能正确转换的情况,导致 `EvaluationException`。 5. **属性或方法访问问题** + 尝试访问不存在的属性或调用不存在的方法时,会抛出异常。这可能是由于拼写错误或对象类型不正确。 ================================================ FILE: spring-spel/spring-spel-expressionParser/pom.xml ================================================ spring-spel com.xcs.spring 0.0.1-SNAPSHOT 4.0.0 spring-spel-expressionParser ================================================ FILE: spring-spel/spring-spel-expressionParser/src/main/java/com/xcs/spring/ExpressionParserDemo.java ================================================ package com.xcs.spring; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.ParseException; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; /** * @author xcs * @date 2024年1月3日22:35:51 */ public class ExpressionParserDemo { public static void main(String[] args) { // 创建解析器实例 ExpressionParser parser = new SpelExpressionParser(); // 解析基本表达式 Expression expression = parser.parseExpression("100 * 2 + 10"); System.out.println("expression = " + expression); } } ================================================ FILE: spring-spel/spring-spel-methodResolver/README.md ================================================ ## MethodResolver - [MethodResolver](#MethodResolver) - [一、基本信息](#一基本信息) - [二、知识储备](#二知识储备) - [三、基本描述](#三基本描述) - [四、主要功能](#四主要功能) - [五、接口源码](#五接口源码) - [六、主要实现](#六主要实现) - [七、最佳实践](#七最佳实践) - [八、与其他组件的关系](#八与其他组件的关系) - [九、常见问题](#九常见问题) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、知识储备 1. **Spring 表达式语言(SpEL)** + `MethodResolver` 接口通常用于 SpEL 中,因此需要了解 SpEL 的语法和基本用法。这包括在 XML 或注解配置中如何使用 SpEL 表达式,以及如何在代码中动态解析 SpEL 表达式。 2. **反射(Reflection)** + `MethodResolver` 通常需要使用反射来动态地查找和调用对象的方法。因此,需要了解 Java 中的反射机制,包括 `Class`、`Method`、`Field` 等类的使用方法,以及如何通过反射调用对象的方法。 ### 三、基本描述 `MethodResolver` 接口是 Spring Framework 表达式语言(SpEL)模块的关键组成部分之一,用于在运行时解析并执行对象的方法。它定义了一个方法 `resolve`,接受方法名和参数类型列表作为参数,并返回一个 `MethodExecutor` 对象,代表解析到的方法。`MethodResolver` 的实现负责根据给定的方法名和参数类型,确定要调用的方法,并创建一个对应的 `MethodExecutor` 实例,以便在表达式求值期间执行该方法。 ### 四、主要功能 1. **方法解析** + `MethodResolver` 接口负责解析表达式中的方法调用。它根据方法名和参数类型列表,确定要调用的方法。 2. **动态方法调用** + `MethodResolver` 允许在运行时动态地调用对象的方法。这使得 SpEL 可以根据表达式的需求,灵活地调用对象的不同方法。 3. **方法执行器创建** + `MethodResolver` 返回一个 `MethodExecutor` 对象,该对象代表解析到的方法。`MethodExecutor` 负责实际执行方法,并返回执行结果。 4. **扩展性** + `MethodResolver` 接口是可扩展的,用户可以根据自己的需求实现自定义的 `MethodResolver`,以实现特定的方法解析逻辑。这使得 SpEL 在处理不同类型的对象和方法调用时更加灵活和可定制。 ### 五、接口源码 `MethodResolver` 接口定义了一个方法解析器的规范,其主要功能是在给定的上下文中,根据对象和方法名,确定可以处理指定参数的合适方法,并返回一个执行器以供调用。这个接口允许在运行时动态地解析和调用对象的方法,为表达式语言提供了灵活性和扩展性。 ```java /** * 方法解析器尝试定位一个方法,并返回一个命令执行器,该执行器可用于调用该方法。命令执行器将被缓存, * 但如果它过期了,则会再次调用解析器。 * * @author Andy Clement * @since 3.0 */ public interface MethodResolver { /** * 在提供的上下文中,确定目标对象上可以处理指定参数的合适方法。返回一个 {@link MethodExecutor}, * 该执行器可用于调用该方法,如果找不到方法,则返回 {@code null}。 * @param context 当前的评估上下文 * @param targetObject 调用方法的对象 * @param name 方法名 * @param argumentTypes 方法的参数类型列表 * @return 可以调用方法的 MethodExecutor,如果找不到方法,则返回 null * @throws AccessException 如果访问方法时发生错误 */ @Nullable MethodExecutor resolve(EvaluationContext context, Object targetObject, String name, List argumentTypes) throws AccessException; } ``` `ReflectiveMethodResolver` 是一个基于反射的方法解析器,用于在给定的上下文中尝试定位目标对象上的适当方法,并返回一个方法执行器以供调用。它通过反射机制检查目标对象的方法,根据方法名和参数类型进行匹配。在匹配过程中,它考虑了方法的参数类型以及是否需要类型转换。此外,它还支持注册方法过滤器来过滤特定类型上的方法。在解析方法时,它会对方法进行排序,处理桥接方法,并且可以选择是否使用距离计算来查找更准确的匹配。如果找到匹配的方法,它将返回一个方法执行器;否则,返回 null。 ```java /** * 基于反射的 {@link MethodResolver},在 {@link StandardEvaluationContext} 中默认使用, * 除非已经指定了显式的方法解析器。 * * 作者:Andy Clement, Juergen Hoeller, Chris Beams * 自从:3.0版本 * @see StandardEvaluationContext#addMethodResolver(MethodResolver) */ public class ReflectiveMethodResolver implements MethodResolver { // 使用距离将确保发现更准确的匹配, // 更接近遵循Java规则。 private final boolean useDistance; @Nullable private Map, MethodFilter> filters; public ReflectiveMethodResolver() { this.useDistance = true; } /** * 此构造函数允许配置 ReflectiveMethodResolver, * 使其使用距离计算来检查两个接近匹配中的更好的匹配 * (当存在多个匹配时)。使用距离计算旨在确保匹配更接近 * Java 编译器在考虑装箱/拆箱和方法候选人是否声明为 * 处理传入参数类型的类型(参数的类型)时会发生什么。 * @param useDistance 如果计算匹配时应使用距离计算,则为 {@code true};否则为 {@code false} */ public ReflectiveMethodResolver(boolean useDistance) { this.useDistance = useDistance; } /** * 注册给定类型的方法过滤器。 * @param type 要过滤的类型 * @param filter 相应的方法过滤器, * 如果要清除给定类型的任何过滤器,则为 {@code null} */ public void registerMethodFilter(Class type, @Nullable MethodFilter filter) { if (this.filters == null) { this.filters = new HashMap<>(); } if (filter != null) { this.filters.put(type, filter); } else { this.filters.remove(type); } } /** * 在给定的上下文中,尝试定位目标对象上的适当方法,并返回一个方法执行器以供调用。 * 方法执行器将被缓存,但如果其状态过期,解析器将被再次调用。 *

* 在给定的上下文中,该方法确定可以处理指定参数的适当方法,并返回一个 {@link MethodExecutor} 对象, * 该对象代表解析到的方法。如果找不到方法,则返回 {@code null}。 * @param context 当前的评估上下文 * @param targetObject 调用方法的对象 * @param name 方法名 * @param argumentTypes 方法的参数类型列表 * @return 可以调用方法的 MethodExecutor,如果找不到方法,则返回 null * @throws AccessException 如果访问方法时发生错误 */ @Override @Nullable public MethodExecutor resolve(EvaluationContext context, Object targetObject, String name, List argumentTypes) throws AccessException { try { // 获取类型转换器 TypeConverter typeConverter = context.getTypeConverter(); // 获取目标对象的类 Class type = (targetObject instanceof Class ? (Class) targetObject : targetObject.getClass()); // 获取目标对象上的所有方法 ArrayList methods = new ArrayList<>(getMethods(type, targetObject)); // 如果为该类型注册了过滤器,请调用它 MethodFilter filter = (this.filters != null ? this.filters.get(type) : null); if (filter != null) { List filtered = filter.filter(methods); methods = (filtered instanceof ArrayList ? (ArrayList) filtered : new ArrayList<>(filtered)); } // 将方法排序为合理的顺序 if (methods.size() > 1) { methods.sort((m1, m2) -> { int m1pl = m1.getParameterCount(); int m2pl = m2.getParameterCount(); // 变长参数方法放在最后 if (m1pl == m2pl) { if (!m1.isVarArgs() && m2.isVarArgs()) { return -1; } else if (m1.isVarArgs() && !m2.isVarArgs()) { return 1; } else { return 0; } } return Integer.compare(m1pl, m2pl); }); } // 解析任何桥接方法 for (int i = 0; i < methods.size(); i++) { methods.set(i, BridgeMethodResolver.findBridgedMethod(methods.get(i))); } // 删除重复的方法(由于解析的桥接方法可能导致) Set methodsToIterate = new LinkedHashSet<>(methods); Method closeMatch = null; int closeMatchDistance = Integer.MAX_VALUE; Method matchRequiringConversion = null; boolean multipleOptions = false; // 遍历方法,查找适合的方法 for (Method method : methodsToIterate) { if (method.getName().equals(name)) { int paramCount = method.getParameterCount(); List paramDescriptors = new ArrayList<>(paramCount); // 构造方法参数类型描述符列表 for (int i = 0; i < paramCount; i++) { paramDescriptors.add(new TypeDescriptor(new MethodParameter(method, i))); } ReflectionHelper.ArgumentsMatchInfo matchInfo = null; if (method.isVarArgs() && argumentTypes.size() >= (paramCount - 1)) { // 处理变长参数 matchInfo = ReflectionHelper.compareArgumentsVarargs(paramDescriptors, argumentTypes, typeConverter); } else if (paramCount == argumentTypes.size()) { // 检查参数是否匹配 matchInfo = ReflectionHelper.compareArguments(paramDescriptors, argumentTypes, typeConverter); } if (matchInfo != null) { if (matchInfo.isExactMatch()) { return new ReflectiveMethodExecutor(method); } else if (matchInfo.isCloseMatch()) { if (this.useDistance) { // 计算匹配的距离 int matchDistance = ReflectionHelper.getTypeDifferenceWeight(paramDescriptors, argumentTypes); if (closeMatch == null || matchDistance < closeMatchDistance) { // 保存更好的匹配 closeMatch = method; closeMatchDistance = matchDistance; } } else { // 如果没有更好的匹配,将其视为接近匹配 if (closeMatch == null) { closeMatch = method; } } } else if (matchInfo.isMatchRequiringConversion()) { if (matchRequiringConversion != null) { multipleOptions = true; } matchRequiringConversion = method; } } } } // 返回找到的方法执行器 if (closeMatch != null) { return new ReflectiveMethodExecutor(closeMatch); } else if (matchRequiringConversion != null) { if (multipleOptions) { // 如果有多个匹配,则抛出异常 throw new SpelEvaluationException(SpelMessage.MULTIPLE_POSSIBLE_METHODS, name); } return new ReflectiveMethodExecutor(matchRequiringConversion); } else { return null; } } catch (EvaluationException ex) { // 解析方法时出现异常 throw new AccessException("Failed to resolve method", ex); } } private Set getMethods(Class type, Object targetObject) { if (targetObject instanceof Class) { Set result = new LinkedHashSet<>(); // 添加这些方法,以便在类型上可调用静态方法:例如 Float.valueOf(..) Method[] methods = getMethods(type); for (Method method : methods) { if (Modifier.isStatic(method.getModifiers())) { result.add(method); } } // 还从 java.lang.Class 本身公开方法 Collections.addAll(result, getMethods(Class.class)); return result; } else if (Proxy.isProxyClass(type)) { Set result = new LinkedHashSet<>(); // 公开接口方法(不是代理声明的重写)以便适当的变长参数内省 for (Class ifc : type.getInterfaces()) { Method[] methods = getMethods(ifc); for (Method method : methods) { if (isCandidateForInvocation(method, type)) { result.add(method); } } } return result; } else { Set result = new LinkedHashSet<>(); Method[] methods = getMethods(type); for (Method method : methods) { if (isCandidateForInvocation(method, type)) { result.add(method); } } return result; } } /** * 返回此类型的方法集。默认实现返回给定类型的 {@link Class#getMethods()} 的结果, * 但子类可以重写以更改结果,例如指定在其他地方声明的静态方法。 * @param type 要返回方法的类 * @since 3.1.1 */ protected Method[] getMethods(Class type) { return type.getMethods(); } /** * 确定给定的 {@code Method} 是否是在给定目标类的实例上进行方法解析的候选方法。 *

默认实现将任何方法都视为候选方法,即使对于 {@link Object} 基类的静态方法 * 和非用户声明的方法也是如此。 * @param method 要评估的方法 * @param targetClass 正在内省的具体目标类 * @since 4.3.15 */ protected boolean isCandidateForInvocation(Method method, Class targetClass) { return true; } } ``` ### 六、主要实现 1. **DataBindingMethodResolver** + 是用于数据绑定环境的方法解析器,支持根据数据绑定的规则解析方法,例如 Spring 数据绑定框架中的规则,并可能使用更高级的技术如 Spring DataBinding,根据数据绑定的配置和元数据来查找适当的方法。 2. **ReflectiveMethodResolver** + 是基于反射机制的通用方法解析器,通过反射检查目标对象的方法,根据方法名和参数类型进行匹配,适用于大多数的 Java 对象和方法的解析,不需要特殊的配置或规则,只需要基于 Java 反射来解析方法即可。 ### 七、最佳实践 使用Spring Expression Language(SpEL)来调用自定义对象(`MyBean`)中的方法。首先,通过SpEL表达式解析器创建一个解析器和一个上下文对象,然后实例化一个`MyBean`对象并将其设置为上下文中的变量。接着,创建一个SpEL表达式,调用`MyBean`对象的`add`方法,并传入两个参数。最后,将表达式与上下文关联,并计算表达式的值,即调用`MyBean`对象的方法并获取结果。 ```java public class MethodResolverDemo { public static void main(String[] args) { // 创建一个SpEL表达式解析器 ExpressionParser parser = new SpelExpressionParser(); StandardEvaluationContext context = new StandardEvaluationContext(); // 在 MyBean 中定义的方法将被 SpEL 表达式调用 MyBean myBean = new MyBean(); context.setVariable("myBean", myBean); // 创建一个 SpEL 表达式,调用 MyBean 中的方法 SpelExpression expression = (SpelExpression) parser.parseExpression("#myBean.add(10, 5)"); // 为表达式设置上下文,并计算结果 int result = (int) expression.getValue(context); // 打印输出实例化的MyBean对象 System.out.println("result = " + result); } } ``` `MyBean` 类定义了一个简单的方法 `add`,它接受两个整数参数,并返回它们的和。 ```java public class MyBean { public int add(int a, int b) { return a + b; } } ``` 运行结果,在 `MethodResolverDemo` 中调用了 `MyBean` 对象的 `add` 方法,并传入了两个参数(10 和 5),所以计算结果为 10 + 5 = 15。 ```java result = 15 ``` ### 八、与其他组件的关系 1. **StandardEvaluationContext** + `MethodResolver` 接口通常与 `StandardEvaluationContext` 类一起使用。`StandardEvaluationContext` 提供了 SpEL 表达式的运行时上下文,其中可以存储变量、函数等信息,并且可以设置自定义的方法解析器,其中就包括了 `MethodResolver` 接口的实现类。 2. **ExpressionParser** + `ExpressionParser` 接口用于解析 SpEL 表达式。在 SpEL 中,可以使用 `ExpressionParser` 实现类来解析表达式,例如 `SpelExpressionParser`。在解析 SpEL 表达式时,通常需要提供一个 `EvaluationContext`,这个上下文中可能会包含一个或多个 `MethodResolver`,用于解析表达式中的方法调用。 3. **MethodExecutor** + `MethodResolver` 接口的 `resolve` 方法返回一个 `MethodExecutor` 对象,它用于执行解析到的方法。`MethodExecutor` 是一个接口,它定义了执行方法的方法 `execute`,具体的执行逻辑由其实现类提供。 4. **ReflectiveMethodResolver** & **DataBindingMethodResolver** + 这两个类是 `MethodResolver` 接口的实现类,分别用于基于反射和数据绑定环境下的方法解析。它们与 `MethodResolver` 接口的关系在于,它们实现了 `MethodResolver` 接口,并提供了具体的方法解析逻辑。在使用 SpEL 表达式时,可以选择性地使用这两个类中的一个或者自定义其他的方法解析器来解析方法调用。 ### 九、常见问题 1. **如何自定义方法解析器?** - 可以实现 `MethodResolver` 接口,并覆写其中的 `resolve` 方法来定义自定义的方法解析逻辑。然后将这个自定义的方法解析器注册到 `StandardEvaluationContext` 中。 2. **方法解析器的优先级如何确定?** - 在 `StandardEvaluationContext` 中,可以注册多个方法解析器,它们的解析顺序由注册的顺序决定。解析时会按照注册的顺序逐个调用方法解析器,直到找到匹配的方法。 3. **如何处理方法重载?** - 当方法重载时,`MethodResolver` 需要根据传入的参数类型列表来确定最适合的方法。在解析方法调用时,会尝试匹配所有可能的方法,并根据参数类型的匹配程度来选择最佳的匹配。 4. **如何处理变长参数方法?** - 变长参数方法(例如使用 `...` 定义的参数)在方法解析中需要特殊处理。方法解析器需要检查传入的参数数量和类型是否与方法定义匹配,并正确处理变长参数的情况。 5. **如何处理方法参数的类型转换?** - 当方法参数的类型与传入参数的类型不完全匹配时,可能需要进行类型转换。方法解析器可以使用注册的类型转换器来进行必要的参数类型转换。 6. **如何处理桥接方法?** - 桥接方法是 Java 泛型中的一个特殊情况,可能会导致方法重载或者匹配错误。方法解析器需要正确处理桥接方法,以确保找到正确的方法进行调用。 7. **如何处理方法的静态调用?** - 当调用静态方法时,需要在方法解析器中考虑到静态方法的情况。方法解析器需要正确处理静态方法的调用,以确保可以找到并调用正确的静态方法。 ================================================ FILE: spring-spel/spring-spel-methodResolver/pom.xml ================================================ com.xcs.spring spring-spel 0.0.1-SNAPSHOT 4.0.0 spring-spel-methodResolver ================================================ FILE: spring-spel/spring-spel-methodResolver/src/main/java/com/xcs/spring/MethodResolverDemo.java ================================================ package com.xcs.spring; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpression; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; /** * @author xcs * @date 2024年3月12日11:28:47 **/ public class MethodResolverDemo { public static void main(String[] args) { // 创建一个SpEL表达式解析器 ExpressionParser parser = new SpelExpressionParser(); StandardEvaluationContext context = new StandardEvaluationContext(); // 在 MyBean 中定义的方法将被 SpEL 表达式调用 MyBean myBean = new MyBean(); context.setVariable("myBean", myBean); // 创建一个 SpEL 表达式,调用 MyBean 中的方法 SpelExpression expression = (SpelExpression) parser.parseExpression("#myBean.add(10, 5)"); // 为表达式设置上下文,并计算结果 int result = (int) expression.getValue(context); // 打印输出实例化的MyBean对象 System.out.println("result = " + result); } } ================================================ FILE: spring-spel/spring-spel-methodResolver/src/main/java/com/xcs/spring/MyBean.java ================================================ package com.xcs.spring; public class MyBean { public int add(int a, int b) { return a + b; } } ================================================ FILE: spring-spel/spring-spel-operatorOverloader/README.md ================================================ ## OperatorOverloader - [OperatorOverloader](#OperatorOverloader) - [一、基本信息](#一基本信息) - [二、知识储备](#二知识储备) - [三、基本描述](#三基本描述) - [四、主要功能](#四主要功能) - [五、接口源码](#五接口源码) - [六、主要实现](#六主要实现) - [七、最佳实践](#七最佳实践) - [八、与其他组件的关系](#八与其他组件的关系) - [九、常见问题](#九常见问题) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、知识储备 1. **Spring Expression Language (SpEL)** + 熟悉 SpEL 的基本语法和用法,包括在 Spring 应用程序中使用 SpEL 表达式进行属性注入、条件判断等。了解 SpEL 中支持的运算符、函数、变量等。 2. **自定义运算符的概念** + 理解什么是自定义运算符以及为什么可能需要自定义运算符。了解自定义运算符在编程语言和框架中的作用和意义。 ### 三、基本描述 `OperatorOverloader` 接口是 Spring 框架中用于自定义运算符行为的扩展点,允许我们通过实现该接口来定义新的运算符操作,以扩展 Spring 表达式语言(SpEL)的功能,从而实现更灵活和定制化的表达式计算。 ### 四、主要功能 1. **自定义运算符行为** + 允许我们定义新的运算符操作,例如自定义的算术运算符、比较运算符等,以扩展 SpEL 的功能,使其能够执行特定的业务逻辑。 2. **支持非标准类型的运算** + 允许在 SpEL 表达式中对非标准类型的操作数执行运算,例如自定义的对象或数据类型,以满足特定业务需求。 3. **扩展 SpEL 的功能** + 增加了 SpEL 表达式语言的灵活性和定制性,使我们能够更方便地编写复杂的表达式,并且可以针对特定场景进行优化。 4. **提供更灵活的表达式计算** + 可以根据业务需求定制运算符的行为,从而实现更灵活、更精确的表达式计算,以满足各种复杂的计算需求。 ### 五、接口源码 `OperatorOverloader` 接口定义了用于自定义运算符行为的方法,包括检查是否支持指定的操作以及执行指定操作的方法,从而允许用户扩展 Spring 表达式语言(SpEL)的功能以支持其他类型的操作数。 ```java /** * 默认情况下,{@link Operation} 支持简单类型(如数字)的数学运算符。 * 通过提供 OperatorOverloader 的实现,表达式语言的用户可以支持对其他类型的操作进行这些运算。 * * @author Andy Clement * @since 3.0 */ public interface OperatorOverloader { /** * 如果运算符重载器支持指定操作以及应该调用它来处理该操作,则返回 true。 * @param operation 要执行的操作 * @param leftOperand 左操作数 * @param rightOperand 右操作数 * @return 如果 OperatorOverloader 支持两个操作数之间的指定操作,则返回 true * @throws EvaluationException 如果执行操作时出现问题 */ boolean overridesOperation(Operation operation, @Nullable Object leftOperand, @Nullable Object rightOperand) throws EvaluationException; /** * 在两个操作数上执行指定的操作,并返回结果。 * 请参阅 {@link Operation} 以获取支持的操作。 * @param operation 要执行的操作 * @param leftOperand 左操作数 * @param rightOperand 右操作数 * @return 在两个操作数上执行操作的结果 * @throws EvaluationException 如果执行操作时出现问题 */ Object operate(Operation operation, @Nullable Object leftOperand, @Nullable Object rightOperand) throws EvaluationException; } ``` `StandardOperatorOverloader` 实现类是 `OperatorOverloader` 接口的标准实现,其中的方法都是默认实现。在 `overridesOperation` 方法中,返回 false 表示默认情况下不覆盖任何操作;在 `operate` 方法中,抛出 `EvaluationException` 异常表示默认情况下不支持任何操作,提示用户需要自定义运算符行为。 ```java /** * {@link OperatorOverloader} 的标准实现。 * * @author Juergen Hoeller * @since 3.0 */ public class StandardOperatorOverloader implements OperatorOverloader { @Override public boolean overridesOperation(Operation operation, @Nullable Object leftOperand, @Nullable Object rightOperand) throws EvaluationException { // 默认情况下不覆盖任何操作 return false; } @Override public Object operate(Operation operation, @Nullable Object leftOperand, @Nullable Object rightOperand) throws EvaluationException { // 默认情况下不支持任何操作,抛出异常 throw new EvaluationException("No operation overloaded by default"); } } ``` ### 六、主要实现 1. **StandardOperatorOverloader** + `StandardOperatorOverloader` 是 `OperatorOverloader` 接口的标准实现类,旨在提供默认的行为。 ### 七、最佳实践 使用Spring表达式语言(SpEL)中使用自定义的运算符。通过创建 `ExpressionParser` 对象和 `StandardEvaluationContext` 上下文,并将自定义的 `OperatorOverloader` 实例注册到上下文中,我们可以定义并解析包含自定义运算符的SpEL表达式。在这个例子中,我们定义了一个包含自定义加法运算符的SpEL表达式 `#myBean1 + #myBean2`,然后通过解析并评估该表达式,得到了两个 `MyBean` 对象的相加结果,并将其打印输出。 ```java public class OperatorOverloaderDemo { public static void main(String[] args) { // 创建表达式解析器 ExpressionParser parser = new SpelExpressionParser(); // 创建表达式上下文 StandardEvaluationContext context = new StandardEvaluationContext(); // 创建自定义的OperatorOverloader实例并注册到表达式上下文中 context.setOperatorOverloader(new CustomOperatorOverloader()); context.setVariable("myBean1", new MyBean(18)); context.setVariable("myBean2", new MyBean(20)); // 定义一个SpEL表达式,使用自定义的运算符 Expression expression = parser.parseExpression("#myBean1 + #myBean2"); // 解析并评估表达式 MyBean myBean = expression.getValue(context, MyBean.class); System.out.println("myBean1+myBean2 = " + myBean); } } ``` `CustomOperatorOverloader` 类实现了 `OperatorOverloader` 接口,用于自定义运算符行为。在这个实现中,`overridesOperation` 方法用于判断是否覆盖了指定的操作,这里检查左右操作数是否都是 `MyBean` 类型;而 `operate` 方法用于执行指定的操作,这里是将两个 `MyBean` 对象的年龄相加,并创建一个新的 `MyBean` 对象返回。 ```java public class CustomOperatorOverloader implements OperatorOverloader { @Override public boolean overridesOperation(Operation operation, Object leftOperand, Object rightOperand) throws EvaluationException { return leftOperand instanceof MyBean && rightOperand instanceof MyBean; } @Override public Object operate(Operation operation, Object leftOperand, Object rightOperand) throws EvaluationException { return new MyBean(((MyBean) leftOperand).getAge() + ((MyBean) rightOperand).getAge()); } } ``` 简单的 Java Bean,包含了一个 `age` 属性以及对应的 getter 和 setter 方法。它还重写了 `toString` 方法,以便在输出对象时打印出 `age` 属性的值。 ```java public class MyBean { private int age; public MyBean(int age) { this.age = age; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "MyBean{" + "age=" + age + '}'; } } ``` 运行结果,表达式 `#myBean1 + #myBean2` 中,两个 `MyBean` 对象的年龄分别是 18 和 20,它们相加的结果为 38。因此输出结果为 `MyBean{age=38}`。 ```java myBean1+myBean2 = MyBean{age=38} ``` ### 八、与其他组件的关系 1. **Operation** + `Operation` 枚举定义了 SpEL 中支持的操作类型,例如加法、减法、乘法等。`OperatorOverloader` 接口中的方法 `overridesOperation` 和 `operate` 使用了 `Operation` 枚举来表示操作类型,从而确定要执行的操作。 2. **EvaluationException** + `EvaluationException` 异常用于表示在表达式评估过程中发生的异常。在 `OperatorOverloader` 接口的方法中可能会抛出 `EvaluationException` 异常,用于处理运算符重载过程中可能出现的异常情况。 3. **StandardEvaluationContext** + `StandardEvaluationContext` 类是 SpEL 中用于表达式求值的上下文对象。在使用 `OperatorOverloader` 接口时,可以通过 `StandardEvaluationContext` 对象设置自定义的 `OperatorOverloader` 实例,以扩展 SpEL 的功能。 ### 九、常见问题 1. **运算符重载的正确性** + 我们在实现自定义运算符时需要确保其行为正确,遵循预期的逻辑。否则可能会导致表达式计算出现错误,甚至程序异常。 2. **运算符冲突** + 在使用自定义运算符时,可能会出现与现有运算符的冲突。我们需要确保自定义的运算符与现有的运算符没有冲突,或者适当处理冲突的情况,以避免意外的行为发生。 3. **运算符的一致性** + 自定义的运算符行为应该与现有运算符的行为保持一致,符合用户的预期。否则可能会导致表达式的结果与预期不符,造成混淆或者误解。 4. **文档和说明** + 我们应该提供清晰的文档和说明,介绍自定义运算符的使用方法和行为,以便其他人能够正确地使用和理解这些运算符。 ================================================ FILE: spring-spel/spring-spel-operatorOverloader/pom.xml ================================================ com.xcs.spring spring-spel 0.0.1-SNAPSHOT 4.0.0 spring-spel-operatorOverloader ================================================ FILE: spring-spel/spring-spel-operatorOverloader/src/main/java/CustomOperatorOverloader.java ================================================ import org.springframework.expression.EvaluationException; import org.springframework.expression.Operation; import org.springframework.expression.OperatorOverloader; public class CustomOperatorOverloader implements OperatorOverloader { @Override public boolean overridesOperation(Operation operation, Object leftOperand, Object rightOperand) throws EvaluationException { return leftOperand instanceof MyBean && rightOperand instanceof MyBean; } @Override public Object operate(Operation operation, Object leftOperand, Object rightOperand) throws EvaluationException { return new MyBean(((MyBean) leftOperand).getAge() + ((MyBean) rightOperand).getAge()); } } ================================================ FILE: spring-spel/spring-spel-operatorOverloader/src/main/java/MyBean.java ================================================ public class MyBean { private int age; public MyBean(int age) { this.age = age; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "MyBean{" + "age=" + age + '}'; } } ================================================ FILE: spring-spel/spring-spel-operatorOverloader/src/main/java/OperatorOverloaderDemo.java ================================================ import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; public class OperatorOverloaderDemo { public static void main(String[] args) { // 创建表达式解析器 ExpressionParser parser = new SpelExpressionParser(); // 创建表达式上下文 StandardEvaluationContext context = new StandardEvaluationContext(); // 创建自定义的OperatorOverloader实例并注册到表达式上下文中 context.setOperatorOverloader(new CustomOperatorOverloader()); context.setVariable("myBean1", new MyBean(18)); context.setVariable("myBean2", new MyBean(20)); // 定义一个SpEL表达式,使用自定义的运算符 Expression expression = parser.parseExpression("#myBean1 + #myBean2"); // 解析并评估表达式 MyBean myBean = expression.getValue(context, MyBean.class); System.out.println("myBean1+myBean2 = " + myBean); } } ================================================ FILE: spring-spel/spring-spel-propertyAccessor/README.md ================================================ ## PropertyAccessor - [PropertyAccessor](#propertyaccessor) - [一、基本信息](#一基本信息) - [二、知识储备](#二知识储备) - [三、基本描述](#三基本描述) - [四、主要功能](#四主要功能) - [五、接口源码](#五接口源码) - [六、主要实现](#六主要实现) - [七、最佳实践](#七最佳实践) - [八、与其他组件的关系](#八与其他组件的关系) - [九、常见问题](#九常见问题) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、知识储备 1. **Spring 表达式语言(SpEL)** + 了解 SpEL 的基本语法和功能,包括表达式的解析、运算符、变量、函数等。 2. **对象属性访问与反射机制** + 了解如何使用 Java 的反射机制来访问和操作对象的属性,包括获取类的字段、调用对象的方法、设置字段的值等。 3. **设计模式** + 理解常见的设计模式,如适配器模式、策略模式等,这些设计模式在实现自定义 `PropertyAccessor` 接口时可能会用到。 ### 三、基本描述 `PropertyAccessor` 接口的作用在于定义了一套统一的属性访问规范,通过该接口可以实现对对象属性的读取和写入操作,使得 Spring 框架中的各个模块能够统一地访问对象的属性,从而实现更加灵活和可扩展的应用开发。通过实现这个接口,可以定制化对象属性的访问逻辑,以满足不同场景下的需求,例如在 Spring 表达式语言(SpEL)、数据绑定、AOP 编程等方面发挥作用。 ### 四、主要功能 1. **属性读取** + 提供了 `canRead()` 方法用于判断是否可以读取指定属性,以及 `read()` 方法用于读取属性的值。这使得可以通过统一的方式从对象中获取属性值。 2. **属性写入** + 提供了 `canWrite()` 方法用于判断是否可以写入指定属性,以及 `write()` 方法用于设置属性的值。这使得可以通过统一的方式向对象中设置属性值。 3. **自定义属性访问逻辑** + 允许我们根据实际需求实现自定义的属性访问逻辑,例如通过自定义的 `PropertyAccessor` 实现类来访问对象的属性,从而实现特定的业务逻辑或行为。 4. **适配不同的对象结构** + 可以针对不同的对象结构实现不同的 `PropertyAccessor` 实现类,以适应不同类型的对象,包括 JavaBean、Map、数组等。 ### 五、接口源码 `PropertyAccessor` 的接口,它用于读取和写入对象的属性。接口中包含了四个方法,分别用于判断是否能够读取属性、读取属性值、判断是否能够写入属性以及写入属性值。此外,接口中还包含了一个用于获取特定目标类数组的方法。该接口提供了一种统一的方式来访问对象的属性,使得可以实现自定义的属性访问逻辑,并能够适配不同的对象结构。 ```java /** * 属性访问器能够读取(可能写入)对象的属性。 * *

这个接口没有限制,因此实现者可以自由地直接访问属性作为字段,或通过getter方法或其他方式访问属性。 * *

解析器可以选择指定一个目标类的数组,用于调用它。然而,如果从{@link #getSpecificTargetClasses()}返回{@code null},它将被调用 * 以尝试解析所有属性引用,并有机会确定它是否可以读取或写入它们。 * *

属性解析器被认为是有序的,并且每个都将依次被调用。影响调用顺序的唯一规则是,在{@link #getSpecificTargetClasses()}中直接命名目标类的任何解析器 * 将在通用解析器之前首先被调用。 * * @author Andy Clement * @since 3.0 */ public interface PropertyAccessor { /** * 返回此解析器应该被调用的类数组。 *

返回{@code null}表示这是一个通用解析器,可以在任何类型上尝试解析属性。 * @return 适用于此解析器的类数组(或{@code null}如果是通用解析器) */ @Nullable Class[] getSpecificTargetClasses(); /** * 用于确定解析器实例是否能够访问指定目标对象上的指定属性。 * @param context 尝试进行访问的评估上下文 * @param target 要访问属性的目标对象 * @param name 要访问的属性的名称 * @return 如果此解析器能够读取属性,则为true * @throws AccessException 如果有任何问题确定是否可以读取属性 */ boolean canRead(EvaluationContext context, @Nullable Object target, String name) throws AccessException; /** * 用于从指定的目标对象中读取属性。 * 只有当{@link #canRead}也返回{@code true}时才能成功。 * @param context 尝试进行访问的评估上下文 * @param target 要访问属性的目标对象 * @param name 要访问的属性的名称 * @return 包装读取的属性值和其类型描述符的TypedValue对象 * @throws AccessException 如果有任何问题访问属性值 */ TypedValue read(EvaluationContext context, @Nullable Object target, String name) throws AccessException; /** * 用于确定解析器实例是否能够向指定目标对象上的指定属性写入。 * @param context 尝试进行访问的评估上下文 * @param target 要访问属性的目标对象 * @param name 要访问的属性的名称 * @return 如果此解析器能够写入属性,则为true * @throws AccessException 如果有任何问题确定是否可以写入属性 */ boolean canWrite(EvaluationContext context, @Nullable Object target, String name) throws AccessException; /** * 用于向指定目标对象的属性写入。 * 只有当{@link #canWrite}也返回{@code true}时才能成功。 * @param context 尝试进行访问的评估上下文 * @param target 要访问属性的目标对象 * @param name 要访问的属性的名称 * @param newValue 属性的新值 * @throws AccessException 如果有任何问题写入属性值 */ void write(EvaluationContext context, @Nullable Object target, String name, @Nullable Object newValue) throws AccessException; } ``` ### 六、主要实现 1. **BeanExpressionContextAccessor** + 允许在表达式中访问特定的对象和属性。通过 `BeanExpressionContextAccessor`,可以在 SpEL 表达式中直接访问上下文中的对象属性,这在某些场景下非常有用,例如在 Spring 的 `@Value` 注解中使用 SpEL 表达式读取配置属性。 2. **BeanFactoryAccessor** + 允许在 SpEL 表达式中访问 Spring `BeanFactory` 中注册的 Bean 对象的属性。`BeanFactory` 是 Spring IoC 容器的核心接口,用于管理和创建 Bean。通过 `BeanFactoryAccessor`,可以在 SpEL 表达式中访问 IoC 容器中注册的 Bean,读取和设置其属性值,这为在 SpEL 中访问 Spring Bean 提供了便利。 3. **CompilablePropertyAccessor** + 允许对属性访问进行优化以提高性能。通过编译能力,可以在运行时对属性访问逻辑进行优化,从而减少不必要的计算和提高执行效率,这在某些性能要求较高的场景下非常有用。 4. **DataBindingPropertyAccessor** + 允许在 Spring 应用程序中读取和写入属性。数据绑定是将用户输入绑定到后端数据模型的过程,在 Web 应用程序中广泛应用。`DataBindingPropertyAccessor` 提供了方便的方式来读取和设置对象的属性值,用于处理数据绑定过程中的属性访问需求。 5. **EnvironmentAccessor** + `EnvironmentAccessor` 用于从 Spring 环境中访问属性,例如读取配置文件中的属性值。Spring 的环境抽象提供了一种统一的方式来访问应用程序的环境属性,包括系统属性、环境变量、配置文件等。通过 `EnvironmentAccessor`,可以在 SpEL 表达式中直接访问 Spring 环境中的属性值,从而实现动态的配置和属性注入。 6. **JspPropertyAccessor** + 允许在 JSP 页面中使用 SpEL 表达式,并在 JSP 标签中访问对象的属性。JSP 是一种常见的 Web 视图技术,与 SpEL 结合使用可以实现更加灵活和动态的页面渲染。通过在 JSP 中使用 SpEL 表达式,并在自定义标签(如 `eval` 标签)内访问对象的属性,可以实现更加灵活和可配置的页面逻辑。 7. **MapAccessor** + `MapAccessor` 用于在 SpEL 表达式中直接访问 Map 对象中的键值对。Map 是一种常见的数据结构,用于存储键值对的集合。通过 `MapAccessor`,可以在 SpEL 表达式中直接访问 Map 对象中的键值对,读取和设置其属性值,这为在 SpEL 中操作 Map 提供了便利。 8. **OptimalPropertyAccessor** + `OptimalPropertyAccessor` 提供了属性访问的优化实现,通常在 `ReflectivePropertyAccessor` 内部使用,用于提高属性访问的性能。通过对属性访问逻辑的优化,可以减少不必要的计算和提高执行效率,从而更加高效地访问对象的属性值。 ### 七、最佳实践 创建了一个 SpEL 表达式解析器对象 `ExpressionParser`,然后创建了一个 `StandardEvaluationContext` 对象作为 SpEL 表达式的上下文。在上下文中设置了一个名为 `myBean` 的变量,其值为一个 `MyBean` 对象。接着,通过 `parser.parseExpression("#myBean.name")` 解析了一个 SpEL 表达式,该表达式表示访问 `myBean` 对象的 `name` 属性。最后,通过 `getValue(context, String.class)` 方法获取了属性值,并将其输出。 ```java public class PropertyAccessorDemo { public static void main(String[] args) { // 创建一个SpEL表达式解析器 ExpressionParser parser = new SpelExpressionParser(); StandardEvaluationContext context = new StandardEvaluationContext(); context.setVariable("myBean",new MyBean("spring-reading")); // 解析SpEL表达式,并使用构造函数实例化对象 String name = parser.parseExpression("#myBean.name").getValue(context, String.class); System.out.println("name = " + name); } } ``` `MyBean` 类定义了一个简单的 Java Bean,具有一个名为 `name` 的私有字段和相应的 getter 和 setter 方法。 ```java public class MyBean { private String name; public MyBean(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "MyBean{" + "name='" + name + '\'' + '}'; } } ``` 运行结果,通过 SpEL 表达式解析器访问了对象的属性,并得到了属性值 "spring-reading"。 ```properties name = spring-reading ``` ### 八、与其他组件的关系 1. **Spring 表达式语言(SpEL)** + 在 SpEL 中,`PropertyAccessor` 接口被用于访问对象的属性。SpEL 提供了一种在运行时动态求值表达式的机制,通过 `PropertyAccessor` 接口可以实现对对象属性的读写操作。 2. **Spring 数据绑定** + 在 Spring 中,数据绑定是一种常见的操作,用于将用户提交的数据绑定到后端数据模型上。`PropertyAccessor` 接口提供了一种统一的方式来访问对象的属性,可以在数据绑定过程中使用。 3. **Spring AOP 编程** + 在 Spring 的面向切面编程(AOP)中,`PropertyAccessor` 接口可以用于访问被增强对象的属性。通过 AOP 可以实现对被增强对象的属性进行拦截、修改等操作。 4. **Spring IOC 容器** + 在 Spring 的控制反转(IoC)容器中,`PropertyAccessor` 接口可以用于访问注册在容器中的 Bean 对象的属性。通过 IoC 容器,可以方便地管理和访问对象的属性。 5. **其他自定义组件** + 除了以上提到的场景之外,`PropertyAccessor` 接口还可以被自定义组件使用。例如,在自定义的框架或库中,可以使用 `PropertyAccessor` 接口来实现对对象属性的访问操作,以实现特定的功能或逻辑。 ### 九、常见问题 1. **属性访问权限问题** + 在使用 `PropertyAccessor` 接口时,可能会遇到对象的属性访问权限不足的问题,导致无法读取或写入属性。这可能涉及到对象的访问控制和权限设置。 2. **属性命名问题** + 在访问对象的属性时,可能会出现属性命名不一致的问题,例如大小写不匹配、拼写错误等,导致无法正确访问属性。 3. **属性类型转换问题** + 在进行属性读取或写入时,可能会遇到属性类型转换失败的问题,例如尝试将一个字符串值赋给一个整数类型的属性,导致转换异常。 4. **上下文环境配置问题** + 在使用 `PropertyAccessor` 接口时,需要提供一个合适的上下文环境(如 `EvaluationContext`),可能会出现配置错误或环境设置不当的问题,导致属性访问失败。 5. **异常处理问题** + 在属性访问过程中,可能会出现各种异常情况,例如空指针异常、访问权限异常等,需要进行适当的异常处理以保证程序的稳定性和可靠性。 ================================================ FILE: spring-spel/spring-spel-propertyAccessor/pom.xml ================================================ com.xcs.spring spring-spel 0.0.1-SNAPSHOT 4.0.0 spring-spel-propertyAccessor ================================================ FILE: spring-spel/spring-spel-propertyAccessor/src/main/java/com/xcs/spring/MyBean.java ================================================ package com.xcs.spring; public class MyBean { public MyBean(String name) { this.name = name; } private String name; // Getters and setters public String getName() { return name; } public void setName(String name) { this.name = name; } } ================================================ FILE: spring-spel/spring-spel-propertyAccessor/src/main/java/com/xcs/spring/PropertyAccessorDemo.java ================================================ package com.xcs.spring; import org.springframework.expression.AccessException; import org.springframework.expression.EvaluationContext; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.ReflectivePropertyAccessor; import org.springframework.expression.spel.support.SimpleEvaluationContext; import org.springframework.expression.spel.support.StandardEvaluationContext; import java.util.ArrayList; import java.util.List; public class PropertyAccessorDemo { public static void main(String[] args) { // 创建一个SpEL表达式解析器 ExpressionParser parser = new SpelExpressionParser(); StandardEvaluationContext context = new StandardEvaluationContext(); context.setVariable("myBean",new MyBean("spring-reading")); // 解析SpEL表达式,并使用构造函数实例化对象 String name = parser.parseExpression("#myBean.name").getValue(context, String.class); System.out.println("name = " + name); } } ================================================ FILE: spring-spel/spring-spel-typeComparator/README.md ================================================ ## TypeComparator - [TypeComparator](#TypeComparator) - [一、基本信息](#一基本信息) - [二、知识储备](#二知识储备) - [三、基本描述](#三基本描述) - [四、主要功能](#四主要功能) - [五、接口源码](#五接口源码) - [六、主要实现](#六主要实现) - [七、最佳实践](#七最佳实践) - [八、与其他组件的关系](#八与其他组件的关系) - [九、常见问题](#九常见问题) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、知识储备 1. **类型系统** + 了解编程语言中的类型系统是很重要的。这包括静态类型和动态类型语言的区别,以及强类型和弱类型语言的区别。理解类型的继承、实现和转换规则对于理解类型比较是很有帮助的。 2. **泛型** + 泛型是 Java 编程语言中的一个重要特性,它允许类、接口和方法在定义时使用参数类型。理解泛型如何在编译时和运行时被处理,以及泛型擦除的概念对于理解 `TypeComparator` 接口的实现可能是有帮助的。 3. **反射** + 反射是 Java 编程语言中的一个强大特性,它允许程序在运行时检查和操作类、对象、方法和属性。了解如何使用反射来获取类的信息、调用方法以及操作字段对于实现 `TypeComparator` 接口可能是有帮助的。 4. **设计模式** + 了解一些常见的设计模式,如策略模式和适配器模式,可能有助于理解 `TypeComparator` 接口的设计和用法。 ### 三、基本描述 `TypeComparator` 接口是 Spring Framework 中的一个重要组件,用于定义对象类型的比较器。通过实现该接口,您可以自定义对象的类型比较逻辑,以满足特定应用场景下的需求,例如在 Spring 表达式语言(SpEL)中确定两个对象的类型是否相同或是否可以进行比较。 ### 四、主要功能 1. **类型比较** + `TypeComparator` 接口定义了 `compare(T o1, T o2)` 方法,用于比较两个对象的类型。我们可以根据自己的需求实现该方法,以确定两个对象的类型是否相同或是否在继承层次结构中相互关联。 2. **可比较性判断** + `TypeComparator` 接口定义了 `canCompare(Object o1, Object o2)` 方法,用于确定两个对象是否可以进行比较。这在某些情况下是必要的,例如在进行对象类型比较之前,需要先检查对象是否为特定类型或是否具有特定的属性。 3. **自定义比较逻辑** + 通过实现 `TypeComparator` 接口,我们可以定义自己的比较逻辑,以满足特定应用场景下的需求。例如,可以根据对象的某些属性或特征来确定对象的类型,而不仅仅是基于 Java 类型系统的默认行为。 4. **扩展性** + `TypeComparator` 接口为 Spring Framework 提供了一种灵活和可扩展的机制,用于处理对象类型比较相关的任务。通过实现自定义的 `TypeComparator`,可以轻松地扩展 Spring 应用程序的功能,以满足不同的业务需求。 ### 五、接口源码 `TypeComparator` 接口定义了对象类型比较器的标准,要求实现类能够比较两个对象是否相等,并且提供了确定对象是否可比较的方法。该接口的主要目的是为了支持 Spring 表达式语言(SpEL)中的对象类型比较操作,提供了灵活的机制来定制对象类型的比较逻辑。 ```java /** * 实现类型比较器的实例应该能够比较一对对象是否相等。 * 返回值的规范与 {@link java.lang.Comparable} 相同。 * * @author Andy Clement * @since 3.0 * @see java.lang.Comparable */ public interface TypeComparator { /** * 如果比较器能够比较这两个对象,则返回 {@code true}。 * @param firstObject 第一个对象 * @param secondObject 第二个对象 * @return 如果比较器能够比较这两个对象,则返回 {@code true} */ boolean canCompare(@Nullable Object firstObject, @Nullable Object secondObject); /** * 比较给定的两个对象。 * @param firstObject 第一个对象 * @param secondObject 第二个对象 * @return 如果它们相等,则返回 0;如果第一个对象小于第二个对象,则返回 <0;如果第一个对象大于第二个对象,则返回 >0 * @throws EvaluationException 如果在比较过程中出现问题(或者它们本来就无法比较) */ int compare(@Nullable Object firstObject, @Nullable Object secondObject) throws EvaluationException; } ``` `StandardTypeComparator` 是 `TypeComparator` 接口的标准实现,支持对 `Number` 类型以及实现了 `Comparable` 接口的类型进行比较。它提供了针对不同类型的对象进行比较的逻辑,包括基本数值类型、BigDecimal、BigInteger 等,并提供了一个灵活而可靠的机制来执行对象类型的比较操作。 ```java /** * {@link TypeComparator} 接口的基本实现:支持对 {@link Number} 类型以及实现了 {@link Comparable} 接口的类型进行比较。 * * 作者:Andy Clement * 作者:Juergen Hoeller * 作者:Giovanni Dall'Oglio Risso * 自版本 3.0 起 */ public class StandardTypeComparator implements TypeComparator { @Override public boolean canCompare(@Nullable Object left, @Nullable Object right) { // 如果其中一个对象为 null,则认为可以进行比较 if (left == null || right == null) { return true; } // 如果两个对象都是 Number 类型,则可以进行比较 if (left instanceof Number && right instanceof Number) { return true; } // 如果左侧对象实现了 Comparable 接口,则可以进行比较 if (left instanceof Comparable) { return true; } return false; } @Override @SuppressWarnings("unchecked") public int compare(@Nullable Object left, @Nullable Object right) throws SpelEvaluationException { // 如果其中一个对象为 null,则根据情况返回相应值 if (left == null) { return (right == null ? 0 : -1); } else if (right == null) { return 1; // 此时左侧对象不可能为 null } // 基本的数值比较 if (left instanceof Number && right instanceof Number) { Number leftNumber = (Number) left; Number rightNumber = (Number) right; if (leftNumber instanceof BigDecimal || rightNumber instanceof BigDecimal) { // 处理 BigDecimal 类型的比较 BigDecimal leftBigDecimal = NumberUtils.convertNumberToTargetClass(leftNumber, BigDecimal.class); BigDecimal rightBigDecimal = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class); return leftBigDecimal.compareTo(rightBigDecimal); } // 其他数值类型的比较,包括 Double、Float、BigInteger、Long、Integer、Short、Byte // 如果不属于以上类型,采用 double 类型的乘法来比较 else if (leftNumber instanceof Double || rightNumber instanceof Double) { return Double.compare(leftNumber.doubleValue(), rightNumber.doubleValue()); } // 其他数值类型的比较 else if (leftNumber instanceof Float || rightNumber instanceof Float) { return Float.compare(leftNumber.floatValue(), rightNumber.floatValue()); } else if (leftNumber instanceof BigInteger || rightNumber instanceof BigInteger) { BigInteger leftBigInteger = NumberUtils.convertNumberToTargetClass(leftNumber, BigInteger.class); BigInteger rightBigInteger = NumberUtils.convertNumberToTargetClass(rightNumber, BigInteger.class); return leftBigInteger.compareTo(rightBigInteger); } else if (leftNumber instanceof Long || rightNumber instanceof Long) { return Long.compare(leftNumber.longValue(), rightNumber.longValue()); } else if (leftNumber instanceof Integer || rightNumber instanceof Integer) { return Integer.compare(leftNumber.intValue(), rightNumber.intValue()); } else if (leftNumber instanceof Short || rightNumber instanceof Short) { return Short.compare(leftNumber.shortValue(), rightNumber.shortValue()); } else if (leftNumber instanceof Byte || rightNumber instanceof Byte) { return Byte.compare(leftNumber.byteValue(), rightNumber.byteValue()); } else { // 未知的 Number 子类型 -> 最佳猜测是 double 类型的乘法 return Double.compare(leftNumber.doubleValue(), rightNumber.doubleValue()); } } try { // 如果左侧对象实现了 Comparable 接口,则使用 compareTo 方法进行比较 if (left instanceof Comparable) { return ((Comparable) left).compareTo(right); } } catch (ClassCastException ex) { // 抛出异常,表示两个对象不可比较 throw new SpelEvaluationException(ex, SpelMessage.NOT_COMPARABLE, left.getClass(), right.getClass()); } // 抛出异常,表示两个对象不可比较 throw new SpelEvaluationException(SpelMessage.NOT_COMPARABLE, left.getClass(), right.getClass()); } } ``` ### 六、主要实现 1. **StandardTypeComparator** + `StandardTypeComparator` 实现类是 `TypeComparator` 接口的标准实现,提供了对 `Number` 类型和实现了 `Comparable` 接口的类型进行比较的功能。 ### 七、最佳实践 使用 Spring 表达式语言(SpEL)中的 `TypeComparator` 接口进行对象类型的比较操作。首先,创建了一个 `StandardEvaluationContext` 对象作为评估上下文,并创建了一个 `SpelExpressionParser` 对象用于解析 SpEL 表达式。然后,使用解析器解析了一个包含比较运算符的表达式,将其表示为 `Expression` 对象。接下来,通过调用 `getValue` 方法并传入评估上下文,利用 SpEL 引擎执行了表达式,并使用 `TypeComparator` 进行比较,最后打印了比较结果。 ```java public class TypeComparatorDemo { public static void main(String[] args) { // 创建一个EvaluationContext StandardEvaluationContext context = new StandardEvaluationContext(); // 创建SpEL表达式解析器 SpelExpressionParser parser = new SpelExpressionParser(); // 解析表达式 Expression expression = parser.parseExpression("'2' < '-5.0'"); // 使用TypeComparator进行比较 boolean result = expression.getValue(context,Boolean.class); // 打印比较后的值 System.out.println("result : " + result); } } ``` 运行结果,表达式 `'2' < '-5.0'` 返回了 `false`。这意味着在 SpEL 中,字符串 `'2'` 被视为不小于字符串 `'-5.0'`,因此比较结果为 `false`。 ```java result : false ``` ### 八、与其他组件的关系 1. **java.lang.Comparable** + `TypeComparator` 接口与 `Comparable` 接口有一定的关系。`Comparable` 接口定义了对象自身的自然顺序,而 `TypeComparator` 接口提供了比较任意两个对象的类型的机制。在 `StandardTypeComparator` 实现类中,会利用 `Comparable` 接口的实现来进行对象的比较。 2. **StandardTypeComparator** + `StandardTypeComparator` 是 `TypeComparator` 接口的默认实现类,它提供了一组用于比较对象类型的标准逻辑。这个类在 Spring Framework 中被广泛使用,特别是在 SpEL 中用于处理类型比较的场景。 3. **StandardEvaluationContext** + 在 SpEL 中,用于存储表达式求值所需的上下文信息。在 `TypeComparatorDemo` 类中,就使用了 `StandardEvaluationContext` 类来创建一个上下文对象,以便进行 SpEL 表达式的求值。 4. **`SpelExpressionParser` 类** + 用于解析 SpEL 表达式的类。在 `TypeComparatorDemo` 类中,就使用了 `SpelExpressionParser` 类来创建一个表达式解析器对象,以便解析 SpEL 表达式。 ### 九、常见问题 1. **自定义比较逻辑** + 我们可能会遇到需要定义自己的比较逻辑以满足特定需求的情况。这可能涉及到对特定类型的对象进行特殊处理或考虑继承关系。 2. **类型转换问题** + 在比较对象时,可能会遇到类型转换问题,特别是当涉及到不同类型的对象时。我们需要确保在比较之前将对象转换为正确的类型。 ================================================ FILE: spring-spel/spring-spel-typeComparator/pom.xml ================================================ com.xcs.spring spring-spel 0.0.1-SNAPSHOT 4.0.0 spring-spel-typeComparator ================================================ FILE: spring-spel/spring-spel-typeComparator/src/main/java/com/xcs/spring/TypeComparatorDemo.java ================================================ package com.xcs.spring; import org.springframework.expression.Expression; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; public class TypeComparatorDemo { public static void main(String[] args) { // 创建一个EvaluationContext StandardEvaluationContext context = new StandardEvaluationContext(); // 创建SpEL表达式解析器 SpelExpressionParser parser = new SpelExpressionParser(); // 解析表达式 Expression expression = parser.parseExpression("'2' < '-5.0'"); // 使用TypeComparator进行比较 boolean result = expression.getValue(context,Boolean.class); // 打印比较后的值 System.out.println("result : " + result); } } ================================================ FILE: spring-spel/spring-spel-typeConverter/README.md ================================================ ## TypeConverter - [TypeConverter](#TypeConverter) - [一、基本信息](#一基本信息) - [二、知识储备](#二知识储备) - [三、基本描述](#三基本描述) - [四、主要功能](#四主要功能) - [五、接口源码](#五接口源码) - [六、主要实现](#六主要实现) - [七、最佳实践](#七最佳实践) - [八、与其他组件的关系](#八与其他组件的关系) - [九、常见问题](#九常见问题) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、知识储备 1. **类型转换概念** + 了解什么是类型转换以及为什么在软件开发中会经常需要进行类型转换是非常重要的。了解基本数据类型、对象之间的转换、类型兼容性等概念将有助于理解`TypeConverter`的作用和用法。 2. **Spring表达式语言(SpEL)** + `TypeConverter`通常与Spring表达式语言(SpEL)一起使用,因此了解SpEL的基本语法和用法是很重要的。SpEL是一个强大的表达式语言,它可以用于在运行时评估和处理表达式。学习如何编写和解析SpEL表达式将有助于理解如何在SpEL中使用`TypeConverter`进行类型转换。 3. **Java反射机制** + `TypeConverter`通常需要使用Java的反射机制来动态地获取对象的类型信息并进行转换。了解Java反射机制如何工作以及如何使用`Class`、`Method`、`Field`等反射API将有助于理解`TypeConverter`的实现原理。 4. **Spring类型转换器的扩展** + 了解如何自定义和扩展Spring框架中的类型转换器也是很有用的。Spring框架提供了许多内置的类型转换器,同时也支持自定义类型转换器来满足特定需求。学习如何编写自定义类型转换器将有助于更好地理解`TypeConverter`的用法和实现原理。 ### 三、基本描述 `TypeConverter`接口是Spring框架中用于在SpEL(Spring表达式语言)中进行类型转换的核心接口,它允许将不同类型的对象相互转换,例如将字符串转换为数字、将对象转换为字符串等,为Spring应用程序提供了统一且灵活的类型转换机制,以满足各种复杂场景下的类型转换需求。 ### 四、主要功能 1. **类型转换** + 提供了将一个对象转换为另一个类型的能力,例如将字符串转换为数字、将对象转换为字符串等。 2. **类型兼容性检查** + 可以检查某个对象是否可以转换为指定类型,以避免类型转换错误导致的异常。 3. **支持自定义转换规则** + 可以根据实际需求自定义类型转换规则,扩展和定制类型转换器,以满足特定场景下的类型转换需求。 4. **与SpEL集成** + 作为SpEL的一部分,`TypeConverter`接口与Spring表达式语言(SpEL)集成,可以在SpEL表达式中直接使用,实现灵活的类型转换功能。 5. **提供默认实现** + Spring框架提供了默认的`TypeConverter`实现,同时也支持用户自定义的类型转换器,以适应不同的应用场景和需求。 ### 五、接口源码 `TypeConverter`接口定义了一种类型转换器,用于在表达式评估过程中将不同类型的值相互转换。它提供了判断是否可以进行类型转换的方法以及执行类型转换的方法,并支持对类型化集合进行转换。 ```java /** * 类型转换器可以在表达式评估过程中将不同类型的值相互转换。这是表达式解析器的SPI;请参阅 * {@link org.springframework.core.convert.ConversionService} 了解Spring转换功能的主要用户API。 * * @author Andy Clement * @author Juergen Hoeller * @since 3.0 */ public interface TypeConverter { /** * 如果类型转换器可以将指定类型转换为所需的目标类型,则返回{@code true}。 * @param sourceType 描述源类型的类型描述符 * @param targetType 描述所请求结果类型的类型描述符 * @return 如果可以执行该转换,则返回{@code true} */ boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType); /** * 将一个值从一种类型转换为另一种类型,例如从{@code boolean}到{@code String}。 *

{@link TypeDescriptor} 参数支持类型化集合: * 例如,调用者可能更喜欢{@code List},而不是简单的{@code List}。 * @param value 要转换的值 * @param sourceType 提供有关源对象的额外信息的类型描述符 * @param targetType 提供有关所请求结果类型的额外信息的类型描述符 * @return 转换后的值 * @throws EvaluationException 如果转换失败或根本无法进行转换 */ @Nullable Object convertValue(@Nullable Object value, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType); } ``` `StandardTypeConverter`是`TypeConverter`接口的默认实现,它委托给核心的Spring `ConversionService`来执行类型转换。该实现提供了两个构造方法,一个使用默认的`ConversionService`,另一个接受用户指定的`ConversionService`。它实现了`canConvert`方法来判断是否可以进行类型转换,并实现了`convertValue`方法来执行实际的类型转换操作。 ```java /** * {@link TypeConverter}接口的默认实现,委托给核心的Spring {@link ConversionService}。 * * @author Juergen Hoeller * @author Andy Clement * @since 3.0 * @see org.springframework.core.convert.ConversionService */ public class StandardTypeConverter implements TypeConverter { private final ConversionService conversionService; /** * 创建一个使用默认ConversionService的StandardTypeConverter。 * @see DefaultConversionService#getSharedInstance() */ public StandardTypeConverter() { this.conversionService = DefaultConversionService.getSharedInstance(); } /** * 创建一个使用给定ConversionService的StandardTypeConverter。 * @param conversionService 要委托的ConversionService */ public StandardTypeConverter(ConversionService conversionService) { Assert.notNull(conversionService, "ConversionService must not be null"); this.conversionService = conversionService; } @Override public boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType) { return this.conversionService.canConvert(sourceType, targetType); } @Override @Nullable public Object convertValue(@Nullable Object value, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType) { try { return this.conversionService.convert(value, sourceType, targetType); } catch (ConversionException ex) { throw new SpelEvaluationException(ex, SpelMessage.TYPE_CONVERSION_ERROR, (sourceType != null ? sourceType.toString() : (value != null ? value.getClass().getName() : "null")), targetType.toString()); } } } ``` ### 六、主要实现 1. **StandardTypeConverter** + `StandardTypeConverter`是`TypeConverter`接口的默认实现,它利用核心的Spring `ConversionService`来实现类型转换。 ### 七、最佳实践 使用SpEL表达式以及`TypeConverter`进行类型转换。首先,通过SpEL表达式解析器创建解析器对象,并创建一个EvaluationContext作为表达式的运行环境。然后,定义一个需要转换的字符串值,通过解析表达式获取表达式对象,再通过`expression.getValue(context, Integer.class)`将字符串值转换为整数类型。最后,打印转换后的整数值。 ```java public class TypeConverterDemo { public static void main(String[] args) { // 创建SpEL表达式解析器 SpelExpressionParser parser = new SpelExpressionParser(); // 创建一个EvaluationContext StandardEvaluationContext context = new StandardEvaluationContext(); // 定义一个需要转换的值 String stringValue = "'123'"; // 解析表达式 Expression expression = parser.parseExpression(stringValue); // 使用TypeConverter进行转换 Integer intValue = expression.getValue(context, Integer.class); // 打印转换后的值 System.out.println("Converted Integer value: " + intValue); } } ``` 运行结果,成功将字符串 `'123'` 转换为整数类型,并输出转换后的整数值 `123`。 ```properties Converted Integer value: 123 ``` ### 八、与其他组件的关系 1. **ConversionService接口** + `TypeConverter`接口是`ConversionService`接口的一部分,它是ConversionService的SPI(Service Provider Interface)。`ConversionService`提供了一种通用的类型转换机制,它定义了一组用于在不同类型之间进行转换的方法。`TypeConverter`接口是`ConversionService`中用于执行具体类型转换的一部分,通过`ConversionService`接口,可以更方便地管理和使用`TypeConverter`。 2. **SpEL表达式语言(Spring Expression Language)** + `TypeConverter`接口通常与SpEL表达式语言一起使用,以支持在表达式中进行类型转换的功能。在SpEL表达式中,可以直接调用`TypeConverter`接口的方法来进行类型转换,从而实现更复杂的表达式计算和处理。 3. **StandardEvaluationContext类** + `TypeConverter`接口通常与`StandardEvaluationContext`类一起使用,`StandardEvaluationContext`提供了表达式的运行环境,包括变量、函数、类型转换器等。在`StandardEvaluationContext`中可以配置`TypeConverter`,以便在SpEL表达式中使用。 ### 九、常见问题 1. **类型转换失败** + 在进行类型转换时,可能会出现转换失败的情况,例如源对象的类型无法转换为目标类型,或者转换过程中发生异常。这可能会导致应用程序逻辑错误或异常。 2. **类型不兼容** + 在某些情况下,源对象的类型和目标类型之间可能存在不兼容的情况,无法进行转换。例如,将一个对象转换为不兼容的目标类型,或者将一个字符串转换为无法表示的目标类型。 3. **缺少类型转换器** + 如果没有合适的类型转换器可用,可能无法执行某些类型转换操作。在这种情况下,需要自定义类型转换器来满足特定的转换需求。 ================================================ FILE: spring-spel/spring-spel-typeConverter/pom.xml ================================================ com.xcs.spring spring-spel 0.0.1-SNAPSHOT 4.0.0 spring-spel-typeConverter ================================================ FILE: spring-spel/spring-spel-typeConverter/src/main/java/com/xcs/spring/TypeConverterDemo.java ================================================ package com.xcs.spring; import org.springframework.expression.Expression; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; public class TypeConverterDemo { public static void main(String[] args) { // 创建SpEL表达式解析器 SpelExpressionParser parser = new SpelExpressionParser(); // 创建一个EvaluationContext StandardEvaluationContext context = new StandardEvaluationContext(); // 定义一个需要转换的值 String stringValue = "'123'"; // 解析表达式 Expression expression = parser.parseExpression(stringValue); // 使用TypeConverter进行转换 Integer intValue = expression.getValue(context, Integer.class); // 打印转换后的值 System.out.println("Converted Integer value: " + intValue); } } ================================================ FILE: spring-spel/spring-spel-typeLocator/README.md ================================================ ## TypeLocator - [TypeLocator](#TypeLocator) - [一、基本信息](#一基本信息) - [二、知识储备](#二知识储备) - [三、基本描述](#三基本描述) - [四、主要功能](#四主要功能) - [五、接口源码](#五接口源码) - [六、主要实现](#六主要实现) - [七、最佳实践](#七最佳实践) - [八、与其他组件的关系](#八与其他组件的关系) - [九、常见问题](#九常见问题) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、知识储备 1. **Spring 表达式语言(SpEL)** + 了解 SpEL 的基础语法和用法是必要的,因为 `TypeLocator` 接口通常用于 SpEL 中,用于动态获取类型信息。 2. **反射(Reflection)** + 了解 Java 中的反射机制,包括 `Class` 类、`Method` 类、`Field` 类等,因为 `TypeLocator` 接口通常需要使用反射来查找和操作类型信息。 3. **设计模式** + 熟悉常见的设计模式,如工厂模式、策略模式等,这些设计模式在实现 `TypeLocator` 接口时可能会有所应用。 ### 三、基本描述 `TypeLocator` 接口是 Spring Framework 中的关键接口之一,用于动态定位类型信息,在 Spring 表达式语言(SpEL)等场景中扮演重要角色,通过提供方法如`findType(String typeName)`和`hasType(String typeName)`,允许 SpEL 在运行时动态获取和检查类型信息,增强了 Spring 应用程序的灵活性和功能性。 ### 四、主要功能 1. **查找类型信息** + 通过 `findType(String typeName)` 方法,根据给定的类型名称查找对应的类型信息,使得 SpEL 在运行时能够动态获取所需类型的信息。 2. **检查类型是否存在** + 通过 `hasType(String typeName)` 方法,可以检查是否存在给定名称的类型。这对于确定能否解析给定的类型很有用。 ### 五、接口源码 `TypeLocator` 接口定义了一种用于定位类型信息的机制,其中包含一个抽象方法 `findType(String typeName)`,用于根据给定的类型名称查找对应的类型,并返回表示该类型的 `Class` 对象。 ```java /** * 实现此接口的类应能够定位类型。它们可以使用自定义的 {@link ClassLoader}, * 和/或以任何方式处理常见的包前缀(例如 {@code java.lang})。 * *

参见 {@link org.springframework.expression.spel.support.StandardTypeLocator} * 以获取示例实现。 * * @author Andy Clement * @since 3.0 */ @FunctionalInterface public interface TypeLocator { /** * 根据名称查找类型。名称可以是完全限定的,也可以不是(例如 {@code String} 或 {@code java.lang.String})。 * @param typeName 要定位的类型 * @return 表示该类型的 {@code Class} 对象 * @throws EvaluationException 如果查找类型时出现问题 */ Class findType(String typeName) throws EvaluationException; } ``` `StandardTypeLocator` 是一个简单的实现类,实现了 `TypeLocator` 接口,用于根据给定的类型名称查找对应的类型信息。它支持使用上下文 ClassLoader 和注册的导入前缀来定位类型,当找不到类型时会尝试使用注册的导入前缀来定位。 ```java /** * 一个简单的 {@link TypeLocator} 实现,它使用上下文 ClassLoader(或设置在其上的任何 ClassLoader)。 * 它支持'well-known'包:如果找不到类型,则会尝试注册的导入来定位它。 * * @author Andy Clement * @author Juergen Hoeller * @since 3.0 */ public class StandardTypeLocator implements TypeLocator { @Nullable private final ClassLoader classLoader; private final List knownPackagePrefixes = new ArrayList<>(1); /** * 为默认的 ClassLoader(通常是线程上下文 ClassLoader)创建一个 StandardTypeLocator。 */ public StandardTypeLocator() { this(ClassUtils.getDefaultClassLoader()); } /** * 为给定的 ClassLoader 创建一个 StandardTypeLocator。 * @param classLoader 要委托的 ClassLoader */ public StandardTypeLocator(@Nullable ClassLoader classLoader) { this.classLoader = classLoader; // 类似于编写常规的 Java 代码,默认只知道 java.lang registerImport("java.lang"); } /** * 注册一个新的导入前缀,用于搜索未限定类型时使用。 * 期望的格式类似于 "java.lang"。 * @param prefix 要注册的前缀 */ public void registerImport(String prefix) { this.knownPackagePrefixes.add(prefix); } /** * 从此定位器的导入列表中删除指定的前缀。 * @param prefix 要移除的前缀 */ public void removeImport(String prefix) { this.knownPackagePrefixes.remove(prefix); } /** * 返回此 StandardTypeLocator 注册的所有导入前缀的列表。 * @return 注册的导入前缀列表 */ public List getImportPrefixes() { return Collections.unmodifiableList(this.knownPackagePrefixes); } /** * 查找(可能是未限定的)类型引用 - 首先使用原始类型名称,然后如果找不到类型名称,则尝试任何注册的前缀。 * @param typeName 要定位的类型 * @return 类型的 Class 对象 * @throws EvaluationException 如果找不到类型 */ @Override public Class findType(String typeName) throws EvaluationException { String nameToLookup = typeName; try { return ClassUtils.forName(nameToLookup, this.classLoader); } catch (ClassNotFoundException ey) { // 在放弃之前尝试任何已注册的前缀 } for (String prefix : this.knownPackagePrefixes) { try { nameToLookup = prefix + '.' + typeName; return ClassUtils.forName(nameToLookup, this.classLoader); } catch (ClassNotFoundException ex) { // 可能是另一个前缀 } } throw new SpelEvaluationException(SpelMessage.TYPE_NOT_FOUND, typeName); } } ``` ### 六、主要实现 1. **StandardTypeLocator** + `StandardTypeLocator` 类是实现了 `TypeLocator` 接口的简单实现,用于在给定类型名称时定位类型信息。 ### 七、最佳实践 使用 Spring 表达式语言(SpEL)来获取类型信息。通过解析不同的表达式,包括获取特定类型的 Class 对象和比较不同类型的枚举值,展示了 SpEL 在类型定位和类型比较方面的功能。 ```java public class TypeLocatorDemo { public static void main(String[] args) { // 创建一个SpEL表达式解析器 ExpressionParser parser = new SpelExpressionParser(); // 解析表达式获取 Date 类的 Class 对象 Class dateClass = parser.parseExpression("T(java.util.Date)").getValue(Class.class); System.out.println("dateClass = " + dateClass); // 解析表达式获取 String 类的 Class 对象 Class stringClass = parser.parseExpression("T(String)").getValue(Class.class); System.out.println("stringClass = " + stringClass); // 解析表达式比较两个 RoundingMode 枚举值的大小 boolean trueValue = parser.parseExpression("T(java.math.RoundingMode).CEILING < T(java.math.RoundingMode).FLOOR").getValue(Boolean.class); System.out.println("trueValue = " + trueValue); } } ``` 运行结果,成功获取了 `java.util.Date` 和 `java.lang.String` 的 Class 对象,并且对 `java.math.RoundingMode` 枚举类型进行了比较,结果为真。 ```properties dateClass = class java.util.Date stringClass = class java.lang.String trueValue = true ``` ### 八、与其他组件的关系 1. **Class** + `Class` 类是 Java 反射中的重要类,用于表示类的运行时信息。它提供了获取类的名称、方法、字段等信息的方法。在 `TypeLocator` 接口的实现中,可能会使用 `Class` 类来表示获取到的类信息。 2. **ClassLoader** + `ClassLoader` 类是 Java 中的一个关键类,用于动态加载 Java 类文件到 Java 虚拟机中。它负责加载类文件并生成对应的 `Class` 对象。在与 `TypeLocator` 接口相关的实现中,可能会使用 `ClassLoader` 来加载和获取类信息。 3. **StandardTypeLocator** + `TypeLocator`接口的实现类,是一个简单的类型定位器,通常用于在 SpEL 中查找类型信息。 ### 九、常见问题 1. **如何实现自定义的 TypeLocator?** - 我们可能会想要根据特定需求实现自定义的 `TypeLocator` 接口,以满足特定的类型定位需求。在这种情况下,需要实现 `findType(String typeName)` 方法,根据给定的类型名称查找对应的类型信息,并根据需求处理类型的查找逻辑。 2. **如何处理不同的类型查找策略?** - 在某些情况下,可能需要根据不同的情况使用不同的类型查找策略。例如,可能需要根据不同的包前缀使用不同的类型查找逻辑,或者需要根据不同的条件动态切换类型查找策略。在这种情况下,我们需要考虑如何设计和实现灵活的类型查找策略。 3. **如何处理类型查找失败的情况?** - 当无法找到指定类型时,需要考虑如何处理类型查找失败的情况。可能的处理方式包括抛出异常、返回默认值或者尝试其他类型查找策略。我们需要根据具体情况选择合适的处理方式,并确保用户能够得到明确的反馈。 ================================================ FILE: spring-spel/spring-spel-typeLocator/pom.xml ================================================ com.xcs.spring spring-spel 0.0.1-SNAPSHOT 4.0.0 spring-spel-typeLocator ================================================ FILE: spring-spel/spring-spel-typeLocator/src/main/java/com/xcs/spring/TypeLocatorDemo.java ================================================ package com.xcs.spring; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; public class TypeLocatorDemo { public static void main(String[] args) { // 创建一个SpEL表达式解析器 ExpressionParser parser = new SpelExpressionParser(); // 解析表达式获取 Date 类的 Class 对象 Class dateClass = parser.parseExpression("T(java.util.Date)").getValue(Class.class); System.out.println("dateClass = " + dateClass); // 解析表达式获取 String 类的 Class 对象 Class stringClass = parser.parseExpression("T(String)").getValue(Class.class); System.out.println("stringClass = " + stringClass); // 解析表达式比较两个 RoundingMode 枚举值的大小 boolean trueValue = parser.parseExpression("T(java.math.RoundingMode).CEILING < T(java.math.RoundingMode).FLOOR").getValue(Boolean.class); System.out.println("trueValue = " + trueValue); } } ================================================ FILE: spring-transaction/pom.xml ================================================ com.xcs.spring spring-reading 0.0.1-SNAPSHOT pom 4.0.0 spring-transaction spring-transaction-enableTransactionManagement spring-transaction-driverManager spring-transaction-dataSource spring-transaction-connection spring-transaction-platformTransactionManager spring-transaction-jdbcTemplate spring-transaction-transactionDefinition spring-transaction-transactionTemplate spring-transaction-springTransactionAnnotationParser spring-transaction-transactionAttributeSource spring-transaction-transactionInterceptor mysql mysql-connector-java ${mysql.version} ================================================ FILE: spring-transaction/spring-transaction-connection/README.md ================================================ ## Connection - [Connection](#connection) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、接口源码](#四接口源码) - [六、最佳实践](#六最佳实践) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址 ** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 `Connection` 接口是 Java JDBC API 中的一部分,代表着与数据库的连接。它提供了方法来管理数据库连接、执行 SQL 语句以及处理事务,是与数据库交互的重要接口之一。 ### 三、主要功能 1. **执行 SQL 语句** + 通过 `createStatement()` 方法创建 `Statement` 对象,或者通过 `prepareStatement(String sql)` 方法创建 `PreparedStatement` 对象,用于执行 SQL 查询、更新或删除等操作。 2. **事务管理** + 支持事务操作,可以通过 `commit()` 提交事务或者 `rollback()` 回滚事务。 3. **设置事务属性** + 可以设置事务的隔离级别、自动提交等属性。 ### 四、接口源码 `Connection` 接口,用于表示与特定数据库的连接。它提供了方法来执行 SQL 语句、管理事务、提交或回滚更改以及关闭连接。 ```java /** *

表示与特定数据库的连接(会话)。SQL 语句在连接的上下文中执行并返回结果。 *

* Connection 对象的数据库能够提供描述其表、支持的 SQL 语法、存储的过程、连接的功能等信息。这些信息是通过 getMetaData 方法获取的。 * *

注意:在配置 Connection 时,JDBC 应用程序应使用适当的 Connection 方法,如 setAutoCommitsetTransactionIsolation。应用程序不应直接调用 SQL 命令来更改连接的配置,而应使用 JDBC 方法。默认情况下,Connection 对象处于自动提交模式,这意味着它在执行每个语句后会自动提交更改。如果已禁用自动提交模式,则必须显式调用 commit 方法才能提交更改;否则,数据库更改将不会保存。 *

* 使用 JDBC 2.1 核心 API 创建的新的 Connection 对象具有一个最初为空的类型映射。用户可以为此类型映射中的用户定义类型(UDT)输入自定义映射。 * 当从数据源检索到 UDT 并使用 ResultSet.getObject 方法时,getObject 方法将检查连接的类型映射以查看是否存在该 UDT 的条目。如果存在,则 getObject 方法将把 UDT 映射到指定的类。如果不存在条目,则使用标准映射映射 UDT。 *

* 用户可以创建一个新的类型映射,它是一个 java.util.Map 对象,向其中添加一个条目,并将其传递给可以执行自定义映射的 java.sql 方法。在这种情况下,方法将使用给定的类型映射而不是与连接关联的类型映射。 *

* 例如,以下代码片段指定 SQL 类型 ATHLETES 将映射到 Java 编程语言中的类 Athletes。该代码片段检索 Connection 对象 con 的类型映射,将条目插入其中,然后将带有新条目的类型映射设置为连接的类型映射。 *

 *      java.util.Map map = con.getTypeMap();
 *      map.put("mySchemaName.ATHLETES", Class.forName("Athletes"));
 *      con.setTypeMap(map);
 * 
* * @see DriverManager#getConnection * @see Statement * @see ResultSet * @see DatabaseMetaData * @since 1.1 */ public interface Connection extends Wrapper, AutoCloseable { /** * 创建一个用于向数据库发送 SQL 语句的 Statement 对象。 * 通常使用 Statement 对象执行无参数的 SQL 语句。如果相同的 SQL 语句需要执行多次,使用 PreparedStatement 对象可能更有效率。 *

* 使用返回的 Statement 对象创建的结果集默认为 TYPE_FORWARD_ONLY 类型,并且并发级别为 CONCUR_READ_ONLY。可以通过调用 {@link #getHoldability} 来确定创建的结果集的可保持性。 * * @return 一个新的默认 Statement 对象 * @exception SQLException 如果发生数据库访问错误,或者在关闭的连接上调用此方法 */ Statement createStatement() throws SQLException; /** * 创建一个用于向数据库发送参数化 SQL 语句的 PreparedStatement 对象。 *

* 可以预编译带有或不带有 IN 参数的 SQL 语句,并将其存储在 PreparedStatement 对象中。然后可以使用该对象多次有效地执行此语句。 *

注意:此方法针对处理受益于预编译的参数化 SQL 语句进行了优化。如果驱动程序支持预编译,则方法 prepareStatement 将向数据库发送语句进行预编译。某些驱动程序可能不支持预编译。在这种情况下,直到执行 PreparedStatement 对象时,语句才会被发送到数据库。这对用户没有直接影响;但是,它影响了哪些方法会抛出某些 SQLException 对象。 *

* 使用返回的 PreparedStatement 对象创建的结果集默认为 TYPE_FORWARD_ONLY 类型,并且并发级别为 CONCUR_READ_ONLY。可以通过调用 {@link #getHoldability} 来确定创建的结果集的可保持性。 * * @param sql 可能包含一个或多个 '?' IN 参数占位符的 SQL 语句 * @return 包含预编译的 SQL 语句的新的默认 PreparedStatement 对象 * @exception SQLException 如果发生数据库访问错误,或者在关闭的连接上调用此方法 */ PreparedStatement prepareStatement(String sql) throws SQLException; /** * 使自上次提交/回滚以来所做的所有更改永久,并释放此 Connection 对象当前持有的任何数据库锁定。 * 只有在禁用自动提交模式时才应该使用此方法。 * * @exception SQLException 如果发生数据库访问错误,如果在参与分布式事务时调用此方法,如果在关闭的连接上调用此方法或者此 Connection 对象处于自动提交模式下 * @see #setAutoCommit */ void commit() throws SQLException; /** * 撤消当前事务中所做的所有更改,并释放此 Connection 对象当前持有的任何数据库锁定。 * 只有在禁用自动提交模式时才应该使用此方法。 * * @exception SQLException 如果发生数据库访问错误,如果在参与分布式事务时调用此方法,如果在关闭的连接上调用此方法或者此 Connection 对象处于自动提交模式下 * @see #setAutoCommit */ void rollback() throws SQLException; /** * 立即释放此 Connection 对象的数据库和 JDBC 资源,而不是等待它们自动释放。 *

* 对已关闭的 Connection 对象调用 close 方法是一个无操作。 *

* 强烈建议在调用 close 方法之前显式地提交或回滚活动事务。如果调用了 close 方法且存在活动事务,则结果是由实现定义的。 * * @exception SQLException 如果发生数据库访问错误 */ void close() throws SQLException; // .... } ``` ### 六、最佳实践 使用 Java JDBC API 连接到数据库,并执行插入操作。它首先配置了一个 `DataSource` 对象,用于连接到 MySQL 数据库。然后,它使用这个数据源获取了一个数据库连接,并设置了不自动提交,开启了一个事务。接着,它准备了一个 SQL 插入语句,并创建了一个 `PreparedStatement` 对象来执行插入操作。在执行插入操作后,它提交了事务,并打印了影响的行数。如果在执行过程中发生了异常,它会回滚事务并打印异常信息。最后,它处理了关闭连接时可能发生的异常。 ```java public class ConnectionDemo { public static void main(String[] args) { // 使用 DataSource 体现了高内聚,有利于后面更改数据库 MysqlDataSource dataSource = new MysqlDataSource(); // 数据库连接 URL,格式为 jdbc:数据库驱动名称://主机地址:端口号/数据库名称 dataSource.setUrl("jdbc:mysql://localhost:3306/spring-reading"); // 数据库用户名 dataSource.setUser("root"); // 数据库密码 dataSource.setPassword("123456"); try (Connection connection = dataSource.getConnection()) { // 设置不自动提交,开启事务 connection.setAutoCommit(false); // SQL 插入语句 String sql = "insert into scores(score) values(?)"; // 创建 PreparedStatement 对象,用于执行 SQL 插入操作 try (PreparedStatement statement = connection.prepareStatement(sql)) { // 设置参数 statement.setInt(1, new Random().nextInt(100)); // 执行插入操作 int row = statement.executeUpdate(); // 模拟异常,用于测试事务回滚 // int i = 1 / 0; // 提交事务 connection.commit(); // 打印影响行数 System.out.println("row = " + row); } catch (Exception e) { // 发生异常时回滚事务 connection.rollback(); // 打印异常信息 e.printStackTrace(); } } catch (SQLException e) { // 处理关闭连接异常 e.printStackTrace(); } } } ``` ================================================ FILE: spring-transaction/spring-transaction-connection/pom.xml ================================================ com.xcs.spring spring-transaction 0.0.1-SNAPSHOT 4.0.0 spring-transaction-connection ================================================ FILE: spring-transaction/spring-transaction-connection/src/main/java/com/xcs/spring/ConnectionDemo.java ================================================ package com.xcs.spring; import com.mysql.cj.jdbc.MysqlDataSource; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.Random; public class ConnectionDemo { public static void main(String[] args) { // 使用 DataSource 体现了高内聚,有利于后面更改数据库 MysqlDataSource dataSource = new MysqlDataSource(); // 数据库连接 URL,格式为 jdbc:数据库驱动名称://主机地址:端口号/数据库名称 dataSource.setUrl("jdbc:mysql://localhost:3306/spring-reading"); // 数据库用户名 dataSource.setUser("root"); // 数据库密码 dataSource.setPassword("123456"); try (Connection connection = dataSource.getConnection()) { // 设置不自动提交,开启事务 connection.setAutoCommit(false); // SQL 插入语句 String sql = "insert into scores(score) values(?)"; // 创建 PreparedStatement 对象,用于执行 SQL 插入操作 try (PreparedStatement statement = connection.prepareStatement(sql)) { // 设置参数 statement.setInt(1, new Random().nextInt(100)); // 执行插入操作 int row = statement.executeUpdate(); // 模拟异常,用于测试事务回滚 // int i = 1 / 0; // 提交事务 connection.commit(); // 打印影响行数 System.out.println("row = " + row); } catch (Exception e) { // 发生异常时回滚事务 connection.rollback(); // 打印异常信息 e.printStackTrace(); } } catch (SQLException e) { // 处理关闭连接异常 e.printStackTrace(); } } } ================================================ FILE: spring-transaction/spring-transaction-dataSource/README.md ================================================ ## DataSource - [DataSource](#datasource) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、接口源码](#四接口源码) - [五、最佳实践](#五最佳实践) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址 ** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 `DataSource` 接口是 Java 中用于管理数据库连接的标准接口,它提供了一种抽象的方法来获取数据库连接,使得应用程序能够与不同的数据库进行交互而无需修改代码,通过配置数据源的方式可以更灵活地处理连接池、事务管理以及连接的分配等问题,从而提高了应用程序的性能和可维护性。 ### 三、主要功能 1. **提供数据库连接** + 定义了获取数据库连接的方法,通过调用这些方法可以获取与数据库的连接,从而进行数据库操作。 2. **连接池管理** + 实现连接池,避免了频繁地创建和销毁数据库连接,提高了应用程序的性能。 3. **连接配置** + 允许配置连接的相关属性,如数据库地址、用户名、密码等信息,这些配置可以在不同的环境中灵活地进行调整。 4. **资源管理** + 负责管理数据库连接资源,包括连接的分配、回收、超时控制等,确保连接的有效性和可用性。 5. **实现数据源的透明性** + 应用程序通过 `DataSource` 获取数据库连接,无需关心具体的数据库类型和配置细节,使得代码更具可移植性和扩展性。 ### 四、接口源码 `DataSource`用于获取与物理数据源的连接。`DataSource` 提供了两个获取连接的方法:`getConnection()` 和 `getConnection(String username, String password)` ,分别用于获取默认用户和密码的连接以及指定用户和密码的连接。此外,接口还定义了一些管理连接的方法,如获取和设置日志写入器、设置登录超时时间等。接口说明了 `DataSource` 的作用及其实现的方式,以及与 `DriverManager` 的比较。最后,该接口还提供了一个默认方法 `createConnectionBuilder()` ,用于创建连接构建器的实例。 ```java /** *

此 {@code DataSource} 对象表示对物理数据源的连接工厂。作为 {@code DriverManager} 设施的替代,{@code DataSource} 对象是获取连接的首选方式。实现 {@code DataSource} 接口的对象通常会在基于 Java™ Naming and Directory (JNDI) API 的命名服务中注册。

*

{@code DataSource} 接口由驱动程序供应商实现。有三种类型的实现:

*
    *
  1. 基本实现 -- 生成标准的 {@code Connection} 对象
  2. *
  3. 连接池实现 -- 生成自动参与连接池的 {@code Connection} 对象。该实现与中间层连接池管理器配合使用。
  4. *
  5. 分布式事务实现 -- 生成可用于分布式事务的 {@code Connection} 对象,几乎总是参与连接池。该实现与中间层事务管理器以及几乎总是与连接池管理器一起使用。
  6. *
*

{@code DataSource} 对象具有在必要时可以修改的属性。例如,如果数据源移动到不同的服务器,则可以更改服务器的属性。好处在于,因为数据源的属性可以更改,因此访问该数据源的任何代码都不需要更改。

*

通过 {@code DataSource} 对象访问的驱动程序不会向 {@code DriverManager} 注册自己。相反,通过查找操作检索 {@code DataSource} 对象,然后用于创建 {@code Connection} 对象。对于基本实现,通过 {@code DataSource} 对象获取的连接与通过 {@code DriverManager} 设施获取的连接相同。

*

{@code DataSource} 的实现必须包括一个公共的无参数构造函数。

* * @since 1.4 */ public interface DataSource extends CommonDataSource, Wrapper { /** *

尝试与此 {@code DataSource} 对象表示的数据源建立连接。

* * @return 与数据源的连接 * @exception SQLException 如果发生数据库访问错误 * @throws java.sql.SQLTimeoutException 当驱动程序确定 {@code setLoginTimeout} 方法指定的超时值已超过并且至少尝试取消当前数据库连接尝试时 */ Connection getConnection() throws SQLException; /** *

尝试使用指定用户和密码与此 {@code DataSource} 对象表示的数据源建立连接。

* * @param username 连接所代表用户的数据库用户 * @param password 用户的密码 * @return 与数据源的连接 * @exception SQLException 如果发生数据库访问错误 * @throws java.sql.SQLTimeoutException 当驱动程序确定 {@code setLoginTimeout} 方法指定的超时值已超过并且至少尝试取消当前数据库连接尝试时 * @since 1.4 */ Connection getConnection(String username, String password) throws SQLException; /** * {@inheritDoc} * @since 1.4 */ @Override java.io.PrintWriter getLogWriter() throws SQLException; /** * {@inheritDoc} * @since 1.4 */ @Override void setLogWriter(java.io.PrintWriter out) throws SQLException; /** * {@inheritDoc} * @since 1.4 */ @Override void setLoginTimeout(int seconds) throws SQLException; /** * {@inheritDoc} * @since 1.4 */ @Override int getLoginTimeout() throws SQLException; // JDBC 4.3 /** * 创建一个新的 {@code ConnectionBuilder} 实例 * @implSpec 默认实现将抛出 {@code SQLFeatureNotSupportedException} * @return 创建的 ConnectionBuilder 实例 * @throws SQLException 如果创建构建器时发生错误 * @throws SQLFeatureNotSupportedException 如果驱动程序不支持分片 * @since 9 * @see ConnectionBuilder */ default ConnectionBuilder createConnectionBuilder() throws SQLException { throw new SQLFeatureNotSupportedException("createConnectionBuilder not implemented"); }; } ``` ### 五、最佳实践 使用 `DataSource` 来建立与数据库的连接,体现了高内聚的设计原则,有利于后续更改数据库。通过 `MysqlDataSource` 类设置数据库连接的 URL、用户名和密码,然后获取数据库连接。接着,执行 SQL 查询语句,将查询结果输出到控制台。最后,关闭结果集、PreparedStatement 和数据库连接,释放资源。 ```java public class DataSourceDemo { public static void main(String[] args) throws Exception { // 使用 DataSource 体现了高内聚,有利于后面更改数据库 MysqlDataSource dataSource = new MysqlDataSource(); // 数据库连接 URL,格式为 jdbc:数据库驱动名称://主机地址:端口号/数据库名称 dataSource.setUrl("jdbc:mysql://localhost:3306/spring-reading"); // 数据库用户名 dataSource.setUser("root"); // 数据库密码 dataSource.setPassword("123456"); // 建立数据库连接 Connection connection = dataSource.getConnection(); // SQL 查询语句 String sql = "SELECT * FROM scores"; // 创建 PreparedStatement 对象,用于执行 SQL 查询 PreparedStatement statement = connection.prepareStatement(sql); // 执行查询,获取结果集 ResultSet resultSet = statement.executeQuery(); // 遍历结果集 while (resultSet.next()) { // 获取 id 列的值 int id = resultSet.getInt("id"); // 获取 score 列的值 String score = resultSet.getString("score"); // 输出结果 System.out.println("id: " + id + ", score: " + score); } // 关闭结果集、PreparedStatement 和数据库连接 resultSet.close(); statement.close(); connection.close(); } } ``` 运行结果,从 "spring-reading" 的数据库的 "scores" 表中检索到的数据。每行都包括一个 "id" 和一个 "score" 列。 ```java id: 1, score: 3.50 id: 2, score: 3.65 id: 3, score: 4.00 id: 4, score: 3.85 id: 5, score: 4.00 id: 6, score: 3.65 ``` ================================================ FILE: spring-transaction/spring-transaction-dataSource/pom.xml ================================================ com.xcs.spring spring-transaction 0.0.1-SNAPSHOT 4.0.0 spring-transaction-dataSource ================================================ FILE: spring-transaction/spring-transaction-dataSource/src/main/java/com/xcs/spring/DataSourceDemo.java ================================================ package com.xcs.spring; import com.mysql.cj.jdbc.MysqlDataSource; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; public class DataSourceDemo { public static void main(String[] args) throws Exception { // 使用 DataSource 体现了高内聚,有利于后面更改数据库 MysqlDataSource dataSource = new MysqlDataSource(); // 数据库连接 URL,格式为 jdbc:数据库驱动名称://主机地址:端口号/数据库名称 dataSource.setUrl("jdbc:mysql://localhost:3306/spring-reading"); // 数据库用户名 dataSource.setUser("root"); // 数据库密码 dataSource.setPassword("123456"); // 建立数据库连接 Connection connection = dataSource.getConnection(); // SQL 查询语句 String sql = "SELECT * FROM scores"; // 创建 PreparedStatement 对象,用于执行 SQL 查询 PreparedStatement statement = connection.prepareStatement(sql); // 执行查询,获取结果集 ResultSet resultSet = statement.executeQuery(); // 遍历结果集 while (resultSet.next()) { // 获取 id 列的值 int id = resultSet.getInt("id"); // 获取 score 列的值 String score = resultSet.getString("score"); // 输出结果 System.out.println("id: " + id + ", score: " + score); } // 关闭结果集、PreparedStatement 和数据库连接 resultSet.close(); statement.close(); connection.close(); } } ================================================ FILE: spring-transaction/spring-transaction-driverManager/README.md ================================================ ## DriverManager - [DriverManager](#drivermanager) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、最佳实践](#四最佳实践) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址 ** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 `DriverManager` 是 Java 标准库中的一个类,用于管理 JDBC 驱动程序,提供了加载驱动程序和建立数据库连接的静态方法,使得 Java 应用程序能够方便地与各种数据库进行交互。 ### 三、主要功能 1. **加载数据库驱动程序** + 通过 `registerDriver()` 方法注册数据库驱动程序,使得 `DriverManager` 能够识别和加载特定数据库的驱动程序。 2. **建立数据库连接** + 通过 `getConnection()` 方法,根据指定的数据库 URL、用户名和密码获取数据库连接对象,以便后续对数据库进行操作。 3. **管理数据库连接** + `DriverManager` 负责管理数据库连接对象,确保连接的安全性和可用性,并提供了方法来获取、关闭数据库连接。 4. **卸载驱动程序** + 通过 `deregisterDriver()` 方法卸载已注册的数据库驱动程序,释放相关资源。 ### 四、最佳实践 使用 `java.sql.DriverManager` 类来连接到 MySQL 数据库,并执行一个简单的查询操作。通过指定数据库连接的 URL、用户名和密码,它建立了与名为 "spring-reading" 的数据库的连接,然后执行了一个查询语句来获取名为 "scores" 的表中的数据。最后,它遍历结果集并将每行数据的 "id" 和 "score" 列打印出来,然后关闭了结果集、预处理语句对象和数据库连接。 ```java public class DriverManagerDemo { public static void main(String[] args) throws Exception { // 数据库连接 URL,格式为 jdbc:数据库驱动名称://主机地址:端口号/数据库名称 String url = "jdbc:mysql://localhost:3306/spring-reading"; // 数据库用户名 String username = "root"; // 数据库密码 String password = "123456"; // 建立数据库连接 Connection connection = DriverManager.getConnection(url, username, password); // SQL 查询语句 String sql = "SELECT * FROM scores"; // 创建 PreparedStatement 对象,用于执行 SQL 查询 PreparedStatement statement = connection.prepareStatement(sql); // 执行查询,获取结果集 ResultSet resultSet = statement.executeQuery(); // 遍历结果集 while (resultSet.next()) { // 获取 id 列的值 int id = resultSet.getInt("id"); // 获取 score 列的值 String score = resultSet.getString("score"); // 输出结果 System.out.println("id: " + id + ", score: " + score); } // 关闭结果集、PreparedStatement 和数据库连接 resultSet.close(); statement.close(); connection.close(); } } ``` 运行结果,从 "spring-reading" 的数据库的 "scores" 表中检索到的数据。每行都包括一个 "id" 和一个 "score" 列。 ```java id:1,score:3.50 id:2,score:3.65 id:3,score:4.00 id:4,score:3.85 id:5,score:4.00 id:6,score:3.65 ``` ================================================ FILE: spring-transaction/spring-transaction-driverManager/pom.xml ================================================ com.xcs.spring spring-transaction 0.0.1-SNAPSHOT 4.0.0 spring-transaction-driverManager ================================================ FILE: spring-transaction/spring-transaction-driverManager/src/main/java/com/xcs/spring/DriverManagerDemo.java ================================================ package com.xcs.spring; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; public class DriverManagerDemo { public static void main(String[] args) throws Exception { // 数据库连接 URL,格式为 jdbc:数据库驱动名称://主机地址:端口号/数据库名称 String url = "jdbc:mysql://localhost:3306/spring-reading"; // 数据库用户名 String username = "root"; // 数据库密码 String password = "123456"; // 建立数据库连接 Connection connection = DriverManager.getConnection(url, username, password); // SQL 查询语句 String sql = "SELECT * FROM scores"; // 创建 PreparedStatement 对象,用于执行 SQL 查询 PreparedStatement statement = connection.prepareStatement(sql); // 执行查询,获取结果集 ResultSet resultSet = statement.executeQuery(); // 遍历结果集 while (resultSet.next()) { // 获取 id 列的值 int id = resultSet.getInt("id"); // 获取 score 列的值 String score = resultSet.getString("score"); // 输出结果 System.out.println("id: " + id + ", score: " + score); } // 关闭结果集、PreparedStatement 和数据库连接 resultSet.close(); statement.close(); connection.close(); } } ================================================ FILE: spring-transaction/spring-transaction-enableTransactionManagement/README.md ================================================ ## EnableTransactionManagement - [EnableTransactionManagement](#EnableTransactionManagement) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、接口源码](#四接口源码) - [五、主要实现](#五主要实现) - [六、最佳实践](#六最佳实践) - [七、源码分析](#七源码分析) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址 ** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 `@EnableTransactionManagement` 是 Spring 框架中的注解,用于启用注解驱动的事务管理。通过在配置类上使用该注解,Spring 会自动扫描和处理应用中的 `@Transactional` 注解,从而实现声明性事务管理。它通常与一个 `PlatformTransactionManager` 实例配合使用,以管理和协调数据库事务。 ### 三、主要功能 1. **启用事务管理** + 通过在配置类上添加 `@EnableTransactionManagement`,Spring 会自动扫描应用上下文中的所有事务注解(例如 `@Transactional`),并为这些注解提供事务管理的支持。 2. **事务管理器** + `@EnableTransactionManagement` 需要一个 `PlatformTransactionManager` 实例来管理事务。通常情况下,Spring 会自动检测配置中的事务管理器。如果没有配置,Spring 会尝试创建一个默认的事务管理器。 ### 四、注解源码 `@EnableTransactionManagement` 注解用于在Spring框架中启用注解驱动的事务管理功能,通过在配置类上添加该注解,Spring会自动扫描和处理应用中的 `@Transactional` 注解,实现声明式事务管理,支持传统的命令式事务管理和响应式事务管理;它允许灵活配置事务管理器,通过 `proxyTargetClass` 属性指定代理类型,通过 `mode` 属性选择代理模式(默认是 `PROXY`,可选 `ASPECTJ` ),并可通过实现 `TransactionManagementConfigurer` 接口明确指定使用的事务管理器,从而为复杂的事务管理需求提供高效的解决方案,同时其默认行为和XML配置方式具有很好的兼容性。 ```java /** * 启用Spring的注解驱动事务管理功能,类似于Spring的{@code } XML命名空间中的支持。 * 该注解用于{@link org.springframework.context.annotation.Configuration @Configuration}类上, * 以配置传统的命令式事务管理或响应式事务管理。 * *

下面的示例演示了使用{@link org.springframework.transaction.PlatformTransactionManager * PlatformTransactionManager}的命令式事务管理。对于响应式事务管理,配置 * {@link org.springframework.transaction.ReactiveTransactionManager * ReactiveTransactionManager}即可。 * *

 * @Configuration
 * @EnableTransactionManagement
 * public class AppConfig {
 *
 *     @Bean
 *     public FooRepository fooRepository() {
 *         // 配置并返回一个包含@Transactional方法的类
 *         return new JdbcFooRepository(dataSource());
 *     }
 *
 *     @Bean
 *     public DataSource dataSource() {
 *         // 配置并返回所需的JDBC DataSource
 *     }
 *
 *     @Bean
 *     public PlatformTransactionManager txManager() {
 *         return new DataSourceTransactionManager(dataSource());
 *     }
 * }
* *

参考上面的示例,可以与以下Spring XML配置进行比较: * *

 * 
 *
 *     
 *
 *     
 *         
 *     
 *
 *     
 *
 *     
 *         
 *     
 *
 * 
 * 
* * 在上述两种情况下,{@code @EnableTransactionManagement}和{@code * }负责注册驱动注解事务管理所需的Spring组件, * 如TransactionInterceptor和在调用{@code JdbcFooRepository}的{@code @Transactional}方法时 * 将拦截器织入调用堆栈的代理或基于AspectJ的建议。 * *

两个示例之间的一个小差异在于{@code TransactionManager} bean的命名: * 在{@code @Bean}示例中,名称是"txManager"(根据方法名);在XML示例中,名称是 * "transactionManager"。{@code }默认硬编码查找名为 * "transactionManager"的bean,而{@code @EnableTransactionManagement}更加灵活; * 它会回退为按类型查找容器中的任何{@code TransactionManager} bean。因此名称可以是 * "txManager"、"transactionManager"或"tm":名称无关紧要。 * *

对于希望在{@code @EnableTransactionManagement}和要使用的确切事务管理器bean之间建立更直接关系的人, * 可以实现{@link TransactionManagementConfigurer}回调接口 - 请注意下面的{@code implements}子句和{@code @Override}注解的方法: * *

 * @Configuration
 * @EnableTransactionManagement
 * public class AppConfig implements TransactionManagementConfigurer {
 *
 *     @Bean
 *     public FooRepository fooRepository() {
 *         // 配置并返回一个包含@Transactional方法的类
 *         return new JdbcFooRepository(dataSource());
 *     }
 *
 *     @Bean
 *     public DataSource dataSource() {
 *         // 配置并返回所需的JDBC DataSource
 *     }
 *
 *     @Bean
 *     public PlatformTransactionManager txManager() {
 *         return new DataSourceTransactionManager(dataSource());
 *     }
 *
 *     @Override
 *     public PlatformTransactionManager annotationDrivenTransactionManager() {
 *         return txManager();
 *     }
 * }
* *

这种方法可能只是因为更显式而是可取的,或者可能是为了区分同一容器中存在的两个{@code TransactionManager} bean。 * 顾名思义,{@code annotationDrivenTransactionManager()}将用于处理{@code @Transactional}方法。 * 有关详细信息,请参阅{@link TransactionManagementConfigurer} Javadoc。 * *

{@link #mode}属性控制如何应用建议:如果模式为{@link AdviceMode#PROXY}(默认),则其他属性控制代理行为。 * 请注意,代理模式仅允许通过代理拦截调用;同一类中的本地调用无法以这种方式拦截。 * *

请注意,如果{@linkplain #mode}设置为{@link AdviceMode#ASPECTJ},则{@link #proxyTargetClass}属性的值将被忽略。 * 此外,在这种情况下,{@code spring-aspects}模块JAR必须存在于类路径上,并且编译时织入或加载时织入将该方面应用于受影响的类。 * 这种情况下没有代理;本地调用也会被拦截。 * * @作者 Chris Beams * @作者 Juergen Hoeller * @自从 3.1 * @参见 TransactionManagementConfigurer * @参见 TransactionManagementConfigurationSelector * @参见 ProxyTransactionManagementConfiguration * @参见 org.springframework.transaction.aspectj.AspectJTransactionManagementConfiguration */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(TransactionManagementConfigurationSelector.class) public @interface EnableTransactionManagement { /** * 指示是否创建基于子类的(CGLIB)代理({@code true}),而不是标准的基于Java接口的代理({@code false})。 * 默认值为{@code false}。仅当{@link #mode()}设置为{@link AdviceMode#PROXY}时适用。 *

请注意,将此属性设置为{@code true}将影响所有需要代理的Spring管理的bean,不仅仅是那些标有 * {@code @Transactional}的bean。例如,其他标有Spring的{@code @Async}注解的bean也会同时升级为子类代理。 * 这种方法在实践中没有负面影响,除非一个明确期望一种类型的代理与另一种类型,例如在测试中。 */ boolean proxyTargetClass() default false; /** * 指示应如何应用事务性建议。 *

默认值为{@link AdviceMode#PROXY}。 * 请注意,代理模式仅允许通过代理拦截调用。同一类中的本地调用无法以这种方式拦截; * 在这种运行时情况下,具有{@link Transactional}注解的方法上的拦截器甚至不会触发。 * 对于更高级的拦截模式,请考虑将其切换为{@link AdviceMode#ASPECTJ}。 */ AdviceMode mode() default AdviceMode.PROXY; /** * 指示在特定连接点应用多个建议时事务顾问的执行顺序。 *

默认值为{@link Ordered#LOWEST_PRECEDENCE}。 */ int order() default Ordered.LOWEST_PRECEDENCE; } ``` ### 六、最佳实践 基于注解的应用上下文 `AnnotationConfigApplicationContext`,并加载 `AppConfig` 配置类,然后从该上下文中获取名为 `ScoresService` 的bean,并调用 `ScoresService` 的 `insertScore` 方法,展示了如何在Spring应用中通过注解配置和使用服务类。 ```java public class EnableTransactionManagementDemo { public static void main(String[] args) { // 创建基于注解的应用上下文 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); // 从应用上下文中获取ScoresService bean ScoresService scoresService = context.getBean(ScoresService.class); // 调用ScoresService的方法 scoresService.insertScore(); } } ``` Spring配置类 `AppConfig`,通过注解 `@Configuration`、`@ComponentScan` 和 `@EnableTransactionManagement` 来启用组件扫描和注解驱动的事务管理,并配置了几个 `@Bean`,包括一个 `DataSourceTransactionManager` 来管理事务,一个 `JdbcTemplate` 用于简化JDBC操作,以及一个 `SimpleDriverDataSource` 用于配置数据库连接,连接的数据库是MySQL,指定了连接URL、用户名和密码。 ```java @Configuration @ComponentScan @EnableTransactionManagement public class AppConfig { @Bean public TransactionManager transactionManager(DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } @Bean public JdbcTemplate jdbcTemplate(DataSource dataSource) { return new JdbcTemplate(dataSource); } @Bean public SimpleDriverDataSource dataSource() throws SQLException { // 数据库连接 URL,格式为 jdbc:数据库驱动名称://主机地址:端口号/数据库名称 String url = "jdbc:mysql://localhost:3306/spring-reading"; // 数据库用户名 String username = "root"; // 数据库密码 String password = "123456"; // 初始化数据源 return new SimpleDriverDataSource(new Driver(), url, username, password); } } ``` 实现了 `ScoresService` 接口,使用 `@Service` 注解将其标记为Spring管理的服务组件,并通过 `@Autowired` 注入 `JdbcTemplate` ;在 `insertScore` 方法中,通过 `@Transactional` 注解启用事务管理,方法生成一个随机分数并插入到数据库中,并打印受影响的行数,同时包含一个被注释掉的异常模拟代码,用于测试事务回滚机制。 ```java @Service public class ScoresServiceImpl implements ScoresService { @Autowired private JdbcTemplate jdbcTemplate; @Override @Transactional public void insertScore() { long id = System.currentTimeMillis(); int score = new Random().nextInt(100); // 向数据库中插入随机生成的分数 int row = jdbcTemplate.update("insert into scores(id,score) values(?,?)", id, score); // 模拟异常,用于测试事务回滚 // int i = 1 / 0; // 打印影响行数 System.out.println("scores row = " + row); } } ``` ### 七、源码分析 在`org.springframework.transaction.annotation.TransactionManagementConfigurationSelector` 类中,继承自 `AdviceModeImportSelector`,根据 `@EnableTransactionManagement` 注解的 `mode` 属性值选择合适的事务管理配置类。如果 `mode` 是 `PROXY`,则返回 `AutoProxyRegistrar` 和 `ProxyTransactionManagementConfiguration` 类名;如果是 `ASPECTJ` ,则根据类路径中是否存在 `javax.transaction.Transactional` 类来决定返回 JTA 事务切面配置类名或非 JTA 事务切面配置类名。这样可以灵活地根据不同的事务管理模式选择合适的实现来配置 Spring 应用的事务管理。 ```java /** * 根据导入的 {@code @Configuration} 类上 {@link EnableTransactionManagement#mode} 的值 * 选择应使用的 {@link AbstractTransactionManagementConfiguration} 实现。 * * @author Chris Beams * @author Juergen Hoeller * @since 3.1 * @see EnableTransactionManagement * @see ProxyTransactionManagementConfiguration * @see TransactionManagementConfigUtils#TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME * @see TransactionManagementConfigUtils#JTA_TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME */ public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector { /** * 根据事务管理模式返回相应的配置类。 * * @param adviceMode 事务管理模式,可以是 PROXY 或 ASPECTJ * @return 包含事务管理配置类名的数组 */ @Override protected String[] selectImports(AdviceMode adviceMode) { switch (adviceMode) { case PROXY: // 如果是 PROXY 模式,返回 AutoProxyRegistrar 和 ProxyTransactionManagementConfiguration 类名 return new String[]{AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()}; case ASPECTJ: // 如果是 ASPECTJ 模式,返回确定的事务切面配置类名 return new String[]{determineTransactionAspectClass()}; default: // 如果模式不是 PROXY 或 ASPECTJ,返回 null return null; } } /** * 确定要使用的事务切面配置类。 * * @return JTA 或非 JTA 事务切面配置类名 */ private String determineTransactionAspectClass() { // 检查类路径中是否存在 javax.transaction.Transactional return (ClassUtils.isPresent("javax.transaction.Transactional", getClass().getClassLoader()) ? // 如果存在,返回 JTA 事务切面配置类名 TransactionManagementConfigUtils.JTA_TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME : // 如果不存在,返回非 JTA 事务切面配置类名 TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME); } } ``` 在`org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration`类中,用于注册启用基于代理的注解驱动事务管理所需的 Spring 基础设施 bean。它包括注册事务通知的 `BeanFactoryTransactionAttributeSourceAdvisor` 、事务属性源的 `AnnotationTransactionAttributeSource` 和事务拦截器的 `TransactionInterceptor`。这些 bean 的注册使得 Spring 能够拦截带有 `@Transactional` 注解的方法调用,并应用事务管理功能。 ```java /** * {@code @Configuration} 类,注册了启用基于代理的注解驱动事务管理所需的Spring基础设施bean。 * * @author Chris Beams * @author Sebastien Deleuze * @since 3.1 * @see EnableTransactionManagement * @see TransactionManagementConfigurationSelector */ @Configuration(proxyBeanMethods = false) @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration { /** * 注册事务通知的Bean。 * * @param transactionAttributeSource 事务属性源 * @param transactionInterceptor 事务拦截器 * @return BeanFactoryTransactionAttributeSourceAdvisor 实例 */ @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME) @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor( TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) { BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor(); advisor.setTransactionAttributeSource(transactionAttributeSource); advisor.setAdvice(transactionInterceptor); if (this.enableTx != null) { advisor.setOrder(this.enableTx.getNumber("order")); } return advisor; } /** * 注册事务属性源的Bean。 * * @return AnnotationTransactionAttributeSource 实例 */ @Bean @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public TransactionAttributeSource transactionAttributeSource() { return new AnnotationTransactionAttributeSource(); } /** * 注册事务拦截器的Bean。 * * @param transactionAttributeSource 事务属性源 * @return TransactionInterceptor 实例 */ @Bean @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) { TransactionInterceptor interceptor = new TransactionInterceptor(); interceptor.setTransactionAttributeSource(transactionAttributeSource); if (this.txManager != null) { interceptor.setTransactionManager(this.txManager); } return interceptor; } } ``` 在`org.springframework.context.annotation.AutoProxyRegistrar`类中,实现了 `ImportBeanDefinitionRegistrar` 接口,用于根据 `@Enable*` 注解的 `mode` 和 `proxyTargetClass` 属性设置适当的自动代理创建器,并将其注册到当前的 `BeanDefinitionRegistry` 中。该类在导入的 `@Configuration` 类上查找具有正确 `mode` 和 `proxyTargetClass` 属性的最近的注解,并根据其设置注册和配置自动代理创建器。 ```java /** * 根据 {@code @Enable*} 注解的 {@code mode} 和 {@code proxyTargetClass} 属性设置适当的 * 自动代理创建器 (Auto Proxy Creator, APC),并将其注册到当前的 {@link BeanDefinitionRegistry} 中。 * * 该类通过在导入的 {@code @Configuration} 类上查找具有 {@code mode} 和 {@code proxyTargetClass} * 属性的最近的注解来工作。如果 {@code mode} 设置为 {@code PROXY},则注册 APC;如果 * {@code proxyTargetClass} 设置为 {@code true},则强制 APC 使用子类(CGLIB)代理。 * * 多个 {@code @Enable*} 注解公开了 {@code mode} 和 {@code proxyTargetClass} 属性。重要的是 * 注意,大多数这些功能最终会共享一个 {@linkplain AopConfigUtils#AUTO_PROXY_CREATOR_BEAN_NAME 单个 APC}。 * 因此,此实现不关心确切地找到哪个注解,只要它公开正确的 {@code mode} 和 {@code proxyTargetClass} * 属性,就可以注册和配置 APC。 * * @作者 Chris Beams * @自 3.1 * @见 org.springframework.cache.annotation.EnableCaching * @见 org.springframework.transaction.annotation.EnableTransactionManagement */ public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar { private final Log logger = LogFactory.getLog(getClass()); /** * 根据导入的类的元数据和Bean定义注册表,注册、升级和配置标准的自动代理创建器(APC)。 * 通过在导入的 {@code @Configuration} 类上找到最近的带有 {@code mode} 和 {@code proxyTargetClass} * 属性的注解来工作。如果 {@code mode} 设置为 {@code PROXY},则注册 APC;如果 * {@code proxyTargetClass} 设置为 {@code true},则强制 APC 使用子类(CGLIB)代理。 *

* 多个 {@code @Enable*} 注解公开了 {@code mode} 和 {@code proxyTargetClass} 属性。重要的是 * 注意,大多数这些功能最终会共享一个 {@linkplain AopConfigUtils#AUTO_PROXY_CREATOR_BEAN_NAME 单个 APC}。 * 因此,此实现不关心确切地找到哪个注解,只要它公开正确的 {@code mode} 和 {@code proxyTargetClass} * 属性,就可以注册和配置 APC。 * * @param importingClassMetadata 导入类的元数据 * @param registry Bean定义注册表 */ @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { // 是否找到候选注解 boolean candidateFound = false; // 获取导入类的所有注解类型 Set annTypes = importingClassMetadata.getAnnotationTypes(); // 遍历所有注解类型 for (String annType : annTypes) { // 获取注解的属性 AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType); // 如果属性为空,则继续下一个循环 if (candidate == null) { continue; } // 获取 mode 和 proxyTargetClass 属性 Object mode = candidate.get("mode"); Object proxyTargetClass = candidate.get("proxyTargetClass"); // 如果 mode 和 proxyTargetClass 属性不为空,并且它们的类型分别是 AdviceMode 和 Boolean if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() && Boolean.class == proxyTargetClass.getClass()) { candidateFound = true; // 如果 mode 是 AdviceMode.PROXY if (mode == AdviceMode.PROXY) { // 注册 APC AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry); // 如果 proxyTargetClass 是 true if ((Boolean) proxyTargetClass) { // 强制 APC 使用类代理 AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); return; } } } } // 如果未找到候选注解,并且 logger 是启用的 if (!candidateFound && logger.isInfoEnabled()) { String name = getClass().getSimpleName(); logger.info(String.format("%s was imported but no annotations were found " + "having both 'mode' and 'proxyTargetClass' attributes of type " + "AdviceMode and boolean respectively. This means that auto proxy " + "creator registration and configuration may not have occurred as " + "intended, and components may not be proxied as expected. Check to " + "ensure that %s has been @Import'ed on the same class where these " + "annotations are declared; otherwise remove the import of %s " + "altogether.", name, name, name)); } } } ``` ================================================ FILE: spring-transaction/spring-transaction-enableTransactionManagement/pom.xml ================================================ com.xcs.spring spring-transaction 0.0.1-SNAPSHOT 4.0.0 spring-transaction-enableTransactionManagement ================================================ FILE: spring-transaction/spring-transaction-enableTransactionManagement/src/main/java/com/xcs/spring/AppConfig.java ================================================ package com.xcs.spring; import com.mysql.jdbc.Driver; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.jdbc.datasource.SimpleDriverDataSource; import org.springframework.transaction.TransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; import javax.sql.DataSource; import java.sql.SQLException; @Configuration @ComponentScan @EnableTransactionManagement public class AppConfig { @Bean public TransactionManager transactionManager(DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } @Bean public JdbcTemplate jdbcTemplate(DataSource dataSource) { return new JdbcTemplate(dataSource); } @Bean public SimpleDriverDataSource dataSource() throws SQLException { // 数据库连接 URL,格式为 jdbc:数据库驱动名称://主机地址:端口号/数据库名称 String url = "jdbc:mysql://localhost:3306/spring-reading"; // 数据库用户名 String username = "root"; // 数据库密码 String password = "123456"; // 初始化数据源 return new SimpleDriverDataSource(new Driver(), url, username, password); } } ================================================ FILE: spring-transaction/spring-transaction-enableTransactionManagement/src/main/java/com/xcs/spring/EnableTransactionManagementDemo.java ================================================ package com.xcs.spring; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class EnableTransactionManagementDemo { public static void main(String[] args) { // 创建基于注解的应用上下文 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); // 从应用上下文中获取ScoresService bean ScoresService scoresService = context.getBean(ScoresService.class); // 调用ScoresService的方法 scoresService.insertScore(); } } ================================================ FILE: spring-transaction/spring-transaction-enableTransactionManagement/src/main/java/com/xcs/spring/ScoresService.java ================================================ package com.xcs.spring; public interface ScoresService { void insertScore(); } ================================================ FILE: spring-transaction/spring-transaction-enableTransactionManagement/src/main/java/com/xcs/spring/ScoresServiceImpl.java ================================================ package com.xcs.spring; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.Random; @Service public class ScoresServiceImpl implements ScoresService { @Autowired private JdbcTemplate jdbcTemplate; @Override @Transactional public void insertScore() { long id = System.currentTimeMillis(); int score = new Random().nextInt(100); // 向数据库中插入随机生成的分数 int row = jdbcTemplate.update("insert into scores(id,score) values(?,?)", id, score); // 模拟异常,用于测试事务回滚 // int i = 1 / 0; // 打印影响行数 System.out.println("scores row = " + row); } } ================================================ FILE: spring-transaction/spring-transaction-jdbcTemplate/README.md ================================================ ## JdbcTemplate - [JdbcTemplate](#jdbctemplate) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、最佳实践](#四最佳实践) - [四、源码分析](#四源码分析) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址 ** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 JdbcTemplate是Spring Framework中的一个关键类,用于简化JDBC编程,提供了简洁的方法执行SQL查询、更新和批处理操作,同时处理了JDBC资源的获取、释放和异常处理,使得数据库操作更加简单、高效和安全。 ### 三、主要功能 1. **简化的JDBC操作** + JdbcTemplate封装了JDBC的复杂性,提供了简洁的方法来执行SQL查询、更新等操作,无需手动管理连接、语句和结果集。 2. **异常处理** + JdbcTemplate自动捕获并转换JDBC异常为Spring的DataAccessException,使异常处理更加便捷,无需编写繁琐的异常处理代码。 3. **参数化查询** + 支持使用占位符进行参数化查询,防止SQL注入攻击,并且可以简化SQL语句的构建和维护。 4. **批处理操作** + 支持批处理操作,可以一次性执行多个SQL语句,提高数据库操作的效率。 5. **回调机制** + 提供了回调机制,可以在执行数据库操作前后插入自定义逻辑,如日志记录、性能监控等。 6. **支持ORM框架** + 可以与Spring的ORM框架(如Spring Data JPA、Spring Data JDBC)结合使用,提供更高层次的数据库访问抽象。 ### 四、最佳实践 使用JdbcTemplate执行SQL查询操作。首先,通过配置数据库连接信息创建了一个`SimpleDriverDataSource` 对象来管理数据源,并创建了一个`DataSourceTransactionManager`对象用于事务管理。然后,通过这些对象创建了一个`JdbcTemplate` 实例。接着,使用`query`方法执行了一个查询操作,将查询结果映射为`Scores`对象的列表,最后打印输出了查询结果。 ```java public class JdbcTemplateDemo { public static void main(String[] args) throws Exception { // 数据库连接 URL,格式为 jdbc:数据库驱动名称://主机地址:端口号/数据库名称 String url = "jdbc:mysql://localhost:3306/spring-reading"; // 数据库用户名 String username = "root"; // 数据库密码 String password = "123456"; // 创建 SimpleDriverDataSource 对象,用于管理数据源 SimpleDriverDataSource dataSource = new SimpleDriverDataSource(new Driver(), url, username, password); // 创建 DataSourceTransactionManager 对象,用于管理事务 DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource); // 创建 JdbcTemplate 对象,用于执行 SQL 语句 JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); List scoresList = jdbcTemplate.query("select * from scores ", new RowMapper() { @Override public Scores mapRow(ResultSet rs, int rowNum) throws SQLException { Scores scores = new Scores(); scores.setId(rs.getLong("id")); scores.setScore(rs.getLong("score")); return scores; } }); scoresList.forEach(System.out::println); } } ``` `Scores`类是一个简单的Java Bean。 ```java public class Scores { private long id; private long score; public long getId() { return id; } public void setId(long id) { this.id = id; } public long getScore() { return score; } public void setScore(long score) { this.score = score; } @Override public String toString() { return "Scores{" + "id=" + id + ", score=" + score + '}'; } } ``` 运行结果,从数据库查询出的分数记录。 ```java Scores { id = 1715586394553, score = 40 } Scores { id = 1715587289293, score = 90 } Scores { id = 1715588070751, score = 20 } Scores { id = 1715668592566, score = 25 } ``` ### 四、源码分析 在`org.springframework.jdbc.core.JdbcTemplate#query(java.lang.String, org.springframework.jdbc.core.RowMapper)` 方法中,用于执行SQL查询并将结果映射为对象列表。它接受SQL查询语句和一个RowMapper接口实现作为参数,并将查询结果通过RowMapperResultSetExtractor转换为对象列表后返回。 ```java /** * 执行给定的 SQL 查询,并将结果映射为对象列表。 * * @param sql SQL查询语句 * @param rowMapper 用于将每行结果映射为对象的 RowMapper 接口实现 * @param 返回结果的类型 * @return 查询结果的对象列表 * @throws DataAccessException 如果查询失败或转换结果时发生异常 */ @Override public List query(String sql, RowMapper rowMapper) throws DataAccessException { // 调用重载的 query 方法,将结果通过 RowMapperResultSetExtractor 转换为对象列表 return result(query(sql, new RowMapperResultSetExtractor<>(rowMapper))); } ``` 在`org.springframework.jdbc.core.JdbcTemplate#query(java.lang.String, org.springframework.jdbc.core.ResultSetExtractor)` 方法中,执行SQL查询并使用ResultSetExtractor提取结果。它接受SQL查询语句和一个ResultSetExtractor接口实现作为参数,通过Statement对象执行SQL查询并将结果集交给ResultSetExtractor进行处理,最终返回ResultSetExtractor提取的结果。 ```java /** * 执行给定的 SQL 查询,并使用指定的 ResultSetExtractor 提取结果。 * * @param sql SQL查询语句 * @param rse 用于提取结果的 ResultSetExtractor 接口实现 * @param 返回结果的类型 * @return 查询结果,如果没有结果则返回 null * @throws DataAccessException 如果查询失败或提取结果时发生异常 */ @Nullable @Override public T query(final String sql, final ResultSetExtractor rse) throws DataAccessException { // 确保 SQL 查询语句和 ResultSetExtractor 不为空 Assert.notNull(sql, "SQL must not be null"); Assert.notNull(rse, "ResultSetExtractor must not be null"); // 如果开启了调试模式,则记录SQL查询语句 if (logger.isDebugEnabled()) { logger.debug("Executing SQL query [" + sql + "]"); } /** * 内部类,用于执行查询操作的回调。 */ class QueryStatementCallback implements StatementCallback, SqlProvider { /** * 在 Statement 中执行查询并提取结果。 */ @Override @Nullable public T doInStatement(Statement stmt) throws SQLException { ResultSet rs = null; try { // 执行查询语句,并获取结果集 rs = stmt.executeQuery(sql); // 使用 ResultSetExtractor 提取结果 return rse.extractData(rs); } finally { // 关闭结果集 JdbcUtils.closeResultSet(rs); } } /** * 获取查询的 SQL 语句。 */ @Override public String getSql() { return sql; } } // 执行查询,并返回结果 return execute(new QueryStatementCallback(), true); } ``` 在`org.springframework.jdbc.core.JdbcTemplate#execute(org.springframework.jdbc.core.StatementCallback, boolean)` 方法中,执行数据库操作回调,并根据需要关闭资源。它接受一个数据库操作回调对象和一个布尔值参数,布尔值参数指示是否在执行完操作后关闭资源。在方法内部,首先获取数据库连接,然后创建Statement对象并应用设置。接着执行数据库操作回调,并处理操作过程中的警告信息。如果操作过程中发生了SQLException,会及时释放连接并抛出DataAccessException。最后,在finally块中根据需要关闭Statement和连接资源。 ```java /** * 执行给定的数据库操作回调,并根据需要关闭资源。 * * @param action 数据库操作回调对象 * @param closeResources 是否关闭资源的标志,如果为 true,则在执行完操作后关闭资源,否则不关闭 * @param 返回结果的类型 * @return 执行操作的结果 * @throws DataAccessException 如果执行操作失败 */ @Nullable private T execute(StatementCallback action, boolean closeResources) throws DataAccessException { // 确保回调对象不为空 Assert.notNull(action, "Callback object must not be null"); // 获取连接 Connection con = DataSourceUtils.getConnection(obtainDataSource()); Statement stmt = null; try { // 创建 Statement stmt = con.createStatement(); // 应用 Statement 的设置 applyStatementSettings(stmt); // 执行数据库操作 T result = action.doInStatement(stmt); // 处理警告信息 handleWarnings(stmt); return result; } catch (SQLException ex) { // 在可能发生连接池死锁的情况下,尽早释放连接以避免死锁 String sql = getSql(action); JdbcUtils.closeStatement(stmt); stmt = null; DataSourceUtils.releaseConnection(con, getDataSource()); con = null; // 转换异常并抛出 throw translateException("StatementCallback", sql, ex); } finally { // 如果需要关闭资源,则在 finally 中关闭 Statement 和连接 if (closeResources) { JdbcUtils.closeStatement(stmt); DataSourceUtils.releaseConnection(con, getDataSource()); } } } ``` ================================================ FILE: spring-transaction/spring-transaction-jdbcTemplate/pom.xml ================================================ com.xcs.spring spring-transaction 0.0.1-SNAPSHOT 4.0.0 spring-transaction-jdbcTemplate ================================================ FILE: spring-transaction/spring-transaction-jdbcTemplate/src/main/java/com/xcs/spring/JdbcTemplateDemo.java ================================================ package com.xcs.spring; import com.mysql.jdbc.Driver; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.SimpleDriverDataSource; import java.util.List; public class JdbcTemplateDemo { public static void main(String[] args) throws Exception { // 数据库连接 URL,格式为 jdbc:数据库驱动名称://主机地址:端口号/数据库名称 String url = "jdbc:mysql://localhost:3306/spring-reading"; // 数据库用户名 String username = "root"; // 数据库密码 String password = "123456"; // 创建 SimpleDriverDataSource 对象,用于管理数据源 SimpleDriverDataSource dataSource = new SimpleDriverDataSource(new Driver(), url, username, password); // 创建 JdbcTemplate 对象,用于执行 SQL 语句 JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); List scoresList = jdbcTemplate.query("select * from scores ", (rs, rowNum) -> { Scores scores = new Scores(); scores.setId(rs.getLong("id")); scores.setScore(rs.getLong("score")); return scores; }); scoresList.forEach(System.out::println); } } ================================================ FILE: spring-transaction/spring-transaction-jdbcTemplate/src/main/java/com/xcs/spring/Scores.java ================================================ package com.xcs.spring; public class Scores { private long id; private long score; public long getId() { return id; } public void setId(long id) { this.id = id; } public long getScore() { return score; } public void setScore(long score) { this.score = score; } @Override public String toString() { return "Scores{" + "id=" + id + ", score=" + score + '}'; } } ================================================ FILE: spring-transaction/spring-transaction-platformTransactionManager/README.md ================================================ ## PlatformTransactionManager - [PlatformTransactionManager](#platformtransactionmanager) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、接口源码](#四接口源码) - [五、主要实现](#五主要实现) - [六、类关系图](#六类关系图) - [七、最佳实践](#七最佳实践) - [八、源码分析](#八源码分析) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址 ** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 `PlatformTransactionManager` 接口是 Spring 框架中负责管理事务的核心接口,定义了统一的事务管理方法,包括事务的启动、提交、回滚和获取当前事务状态等,为不同的数据访问技术提供了统一的事务管理接口,便于在不同技术之间无缝切换。 ### 三、主要功能 1. **开始事务** + `PlatformTransactionManager` 允许我们通过 `getTransaction(TransactionDefinition definition)` 方法开始一个新的事务或获取一个已存在的事务。`TransactionDefinition` 对象定义了事务的属性,如传播行为、隔离级别、超时设置和只读标志等。 2. **提交事务** + 如果事务中的所有操作都成功完成,我们可以通过 `commit(TransactionStatus status)` 方法来提交事务。这将使事务中的所有更改永久化。 3. **回滚事务** + 如果在事务中发生任何异常或错误,我们可以通过 `rollback(TransactionStatus status)` 方法来回滚事务。这将撤销事务中的所有更改,使数据库回到事务开始前的状态。 4. **获取事务状态** + `TransactionStatus` 对象提供了关于当前事务状态的信息,如是否是新事务、是否已完成、是否已回滚等。这些信息可以用于在事务处理过程中进行条件逻辑判断。 ### 四、接口源码 `PlatformTransactionManager` 接口是 Spring 命令式事务基础架构的核心接口。它提供了三个主要功能`getTransaction()` 用于获取当前活动事务或创建新事务,`commit()` 用于提交给定的事务,而 `rollback()` 用于回滚给定的事务。这些功能使得应用程序能够有效地管理事务,确保数据的一致性和完整性。 ```java /** * 这是 Spring 命令式事务基础架构中的中心接口。 * 应用程序可以直接使用它,但主要用途不是作为一个 API * 通常,应用程序将使用 TransactionTemplate 或通过 AOP 实现的声明式事务界定。 * *

对于实现者来说,建议从提供的 AbstractPlatformTransactionManager 类派生, * 该类预先实现了定义的传播行为并处理了事务同步处理。子类必须实现底层事务的特定状态的模板方法, * 例如begin、suspend、resume、commit。 * *

这个策略接口的默认实现是 JtaTransactionManager 和 DataSourceTransactionManager, * 它们可以作为其他事务策略的实现指南。 * * @author Rod Johnson * @author Juergen Hoeller * @since 16.05.2003 * @see org.springframework.transaction.support.TransactionTemplate * @see org.springframework.transaction.interceptor.TransactionInterceptor * @see org.springframework.transaction.ReactiveTransactionManager */ public interface PlatformTransactionManager extends TransactionManager { /** * 根据指定的传播行为返回当前活动事务或创建一个新事务。 *

请注意,诸如隔离级别或超时之类的参数仅在创建新事务时应用, * 因此在参与活动事务时会被忽略。 *

此外,并非所有的事务定义设置都会被所有的事务管理器支持 * 当遇到不支持的设置时,适当的事务管理器实现应该抛出异常。 *

以上规则的一个例外是只读标志, * 如果不支持显式的只读模式,则应该忽略它。从本质上讲, * 只读标志只是对潜在优化的一个提示。 * @param definition TransactionDefinition 实例(对于默认值可以为 null), * 描述传播行为、隔离级别、超时等。 * @return 表示新事务或当前事务的事务状态对象 * @throws TransactionException 如果出现查找、创建或系统错误 * @throws IllegalTransactionStateException 如果给定的事务定义无法执行 * (例如,如果当前活动事务与指定的传播行为冲突) * @see TransactionDefinition#getPropagationBehavior * @see TransactionDefinition#getIsolationLevel * @see TransactionDefinition#getTimeout * @see TransactionDefinition#isReadOnly */ TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException; /** * 根据其状态提交给定的事务。如果事务已被程序标记为仅回滚,请执行回滚。 *

如果事务不是新的,则省略提交以正确参与周围的事务。 * 如果先前的事务已被暂停以创建一个新事务,则在提交新事务后恢复先前的事务。 *

请注意,当提交调用完成时,无论是正常还是抛出异常,事务都必须完全完成和清理。 * 在这种情况下,不应该期望回滚调用。 *

如果此方法引发除 TransactionException 之外的异常, * 则一些提交之前的错误导致提交尝试失败。例如, * 在提交之前可能会尝试将更改刷新到数据库中, * 由于 resulting DataAccessException 导致事务失败。在这种情况下,原始异常将传播到此 commit 方法的调用者。 * @param status 由 getTransaction 方法返回的对象 * @throws UnexpectedRollbackException 如果事务协调器启动了意外的回滚 * @throws HeuristicCompletionException 如果事务协调器在事务协调器的一侧做出了启发式决策导致的事务失败 * @throws TransactionSystemException 在提交或系统错误时(通常是由于基本资源故障引起) * @throws IllegalTransactionStateException 如果给定的事务已经完成(已提交或已回滚) * @see TransactionStatus#setRollbackOnly */ void commit(TransactionStatus status) throws TransactionException; /** * 执行给定事务的回滚。 *

如果事务不是新的,则仅将其标记为回滚,以便正确参与周围的事务。 * 如果先前的事务已被暂停以创建一个新事务,则在回滚新事务后恢复先前的事务。 *

如果提交引发异常,则不要调用事务回滚。 * 当提交返回时,事务已经完成和清理,即使在提交异常的情况下也是如此。 * 因此,在提交失败后调用回滚将导致 IllegalTransactionStateException。 * @param status 由 getTransaction 方法返回的对象 * @throws TransactionSystemException 在回滚或系统错误时(通常是由于基本资源故障引起) * @throws IllegalTransactionStateException 如果给定的事务已经完成(已提交或已回滚) */ void rollback(TransactionStatus status) throws TransactionException; } ``` ### 五、主要实现 1. **DataSourceTransactionManager** + 用于基于 JDBC 的事务管理。 ### 六、类关系图 ~~~mermaid classDiagram direction BT class AbstractPlatformTransactionManager class DataSourceTransactionManager class InitializingBean { <> } class PlatformTransactionManager { <> } class ResourceTransactionManager { <> } class TransactionManager { <> } AbstractPlatformTransactionManager ..> PlatformTransactionManager DataSourceTransactionManager --> AbstractPlatformTransactionManager DataSourceTransactionManager ..> InitializingBean DataSourceTransactionManager ..> ResourceTransactionManager PlatformTransactionManager --> TransactionManager ResourceTransactionManager --> PlatformTransactionManager ~~~ ### 七、最佳实践 使用Spring的事务管理器(`PlatformTransactionManager`)和JDBC模板(`JdbcTemplate` )在Java应用中进行数据库操作。代码首先设置数据库连接信息,并创建数据源和事务管理器。然后,通过`JdbcTemplate` 执行SQL插入操作,将随机生成的分数插入数据库。在执行插入操作时,事务被启动,并在操作成功时提交。如果出现异常,则回滚事务,确保数据一致性。最后,打印操作影响的行数。 ```java public class PlatformTransactionManagerDemo { private static PlatformTransactionManager transactionManager; private static JdbcTemplate jdbcTemplate; public static void main(String[] args) throws SQLException { // 数据库连接 URL,格式为 jdbc:数据库驱动名称://主机地址:端口号/数据库名称 String url = "jdbc:mysql://localhost:3306/spring-reading"; // 数据库用户名 String username = "root"; // 数据库密码 String password = "123456"; // 创建 SimpleDriverDataSource 对象,用于管理数据源 SimpleDriverDataSource dataSource = new SimpleDriverDataSource(new Driver(), url, username, password); // 创建 DataSourceTransactionManager 对象,用于管理事务 transactionManager = new DataSourceTransactionManager(dataSource); // 创建 JdbcTemplate 对象,用于执行 SQL 语句 jdbcTemplate = new JdbcTemplate(dataSource); insertScore(); } private static void insertScore() { // 开启一个新的事务,返回事务状态对象 TransactionStatus transactionStatus = transactionManager.getTransaction(new DefaultTransactionDefinition()); try { long id = System.currentTimeMillis(); int score = new Random().nextInt(100); // 向数据库中插入随机生成的分数 int row = jdbcTemplate.update("insert into scores(id,score) values(?,?)", id, score); // 模拟异常,用于测试事务回滚 // int i = 1 / 0; // 提交事务 transactionManager.commit(transactionStatus); // 打印影响行数 System.out.println("scores row = " + row); } catch (Exception e) { // 出现异常时回滚事务 transactionManager.rollback(transactionStatus); e.printStackTrace(); } } } ``` ### 八、源码分析 **开启事务** 在`org.springframework.transaction.support.AbstractPlatformTransactionManager#getTransaction` 方法中,实现了 `PlatformTransactionManager` 接口的 `getTransaction()` 方法,处理了事务的传播行为。根据传播行为的不同,通过调用 `doGetTransaction()`、`isExistingTransaction()` 和 `doBegin()` 等方法来获取、检查现有事务,并开始新的事务。如果存在现有事务,则根据传播行为确定如何处理;如果不存在现有事务,则根据传播行为决定如何继续。在创建新事务时,可能会挂起已存在的事务,并在出现异常时恢复挂起的资源。如果传播行为指定了 " mandatory" ,但未找到现有事务,则抛出异常。若指定的超时时间无效,则抛出超时异常。最后,根据传播行为返回相应的事务状态对象,可能是一个 " empty" 事务,没有实际的事务,但可能存在同步。 ```java /** * 此实现处理传播行为。委托给 doGetTransaction、isExistingTransaction 和 doBegin。 * @see #doGetTransaction * @see #isExistingTransaction * @see #doBegin */ @Override public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException { // 如果没有给定事务定义,则使用默认值。 TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults()); // 获取事务对象 Object transaction = doGetTransaction(); // 是否启用调试日志 boolean debugEnabled = logger.isDebugEnabled(); if (isExistingTransaction(transaction)) { // 找到现有事务 -> 检查传播行为以确定如何操作。 return handleExistingTransaction(def, transaction, debugEnabled); } // 检查新事务的定义设置。 if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) { throw new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout()); } // 未找到现有事务 -> 检查传播行为以确定如何继续。 if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) { throw new IllegalTransactionStateException( "No existing transaction found for transaction marked with propagation 'mandatory'"); } else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED || def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW || def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) { // 挂起已存在的事务(如果有),然后创建新事务。 SuspendedResourcesHolder suspendedResources = suspend(null); if (debugEnabled) { logger.debug("Creating new transaction with name [" + def.getName() + "]: " + def); } try { // 开始新事务 return startTransaction(def, transaction, debugEnabled, suspendedResources); } catch (RuntimeException | Error ex) { // 如果在开始新事务时出现异常,则恢复挂起的资源并抛出异常。 resume(null, suspendedResources); throw ex; } } else { // 创建“空”事务:没有实际的事务,但可能存在同步。 if (def.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) { logger.warn("Custom isolation level specified but no actual transaction initiated; " + "isolation level will effectively be ignored: " + def); } // 返回准备好的事务状态,没有实际事务,但可能存在同步。 boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS); return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null); } } ``` 在`org.springframework.jdbc.datasource.DataSourceTransactionManager#doGetTransaction` 方法中,首先创建了一个 `DataSourceTransactionObject` 对象,用于管理数据源事务。然后根据是否允许嵌套事务设置了保存点的允许状态。接着通过 `TransactionSynchronizationManager.getResource()` 方法获取当前线程绑定的数据源连接持有者对象,并将其设置到事务对象中。最后返回该事务对象。 ```java @Override protected Object doGetTransaction() { // 创建 DataSourceTransactionObject 对象,用于管理数据源事务 DataSourceTransactionObject txObject = new DataSourceTransactionObject(); // 设置是否允许设置保存点 txObject.setSavepointAllowed(isNestedTransactionAllowed()); // 获取当前线程绑定的数据源的连接持有者 ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource()); // 将连接持有者设置到事务对象中 txObject.setConnectionHolder(conHolder, false); return txObject; } ``` 在`org.springframework.jdbc.datasource.DataSourceTransactionManager#isExistingTransaction` 方法中,用于检查给定的事务对象是否表示一个已存在的活动事务。它首先将传入的事务对象强制转换为 `DataSourceTransactionObject` 类型,然后检查该事务对象是否具有连接持有者,并且该连接持有者中的事务是否处于活动状态。最后返回一个布尔值,表示是否存在活动事务。 ```java @Override protected boolean isExistingTransaction(Object transaction) { // 将事务对象强制转换为 DataSourceTransactionObject 类型 DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction; // 检查事务对象是否具有连接持有者,并且连接持有者中的事务是否处于活动状态 return (txObject.hasConnectionHolder() && txObject.getConnectionHolder().isTransactionActive()); } ``` 在`org.springframework.transaction.support.AbstractPlatformTransactionManager#handleExistingTransaction` 方法中,根据事务定义中的传播行为不同,它可能会挂起当前事务并创建一个新的事务,也可能会创建一个嵌套事务,或者参与已存在的事务。最终,根据处理结果创建并返回一个新的 TransactionStatus 对象,用于表示已存在的事务。 ```java /** * 处理已存在的事务,为其创建一个 TransactionStatus 对象。 * * @param definition 事务定义对象 * @param transaction 事务对象 * @param debugEnabled 是否启用调试日志 * @return 一个包含有关现有事务的 TransactionStatus 对象 * @throws TransactionException 如果在处理现有事务时发生错误 */ private TransactionStatus handleExistingTransaction( TransactionDefinition definition, Object transaction, boolean debugEnabled) throws TransactionException { // 如果传播行为是 PROPAGATION_NEVER,则不允许存在现有事务 if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) { throw new IllegalTransactionStateException( "Existing transaction found for transaction marked with propagation 'never'"); } // 如果传播行为是 PROPAGATION_NOT_SUPPORTED,则挂起当前事务 if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) { if (debugEnabled) { logger.debug("Suspending current transaction"); } // 挂起当前事务并获取挂起的资源 Object suspendedResources = suspend(transaction); // 判断是否需要新的事务同步 boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS); // 准备事务状态并返回 return prepareTransactionStatus( definition, null, false, newSynchronization, debugEnabled, suspendedResources); } // 如果传播行为是 PROPAGATION_REQUIRES_NEW,则挂起当前事务并创建一个新事务 if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) { if (debugEnabled) { logger.debug("Suspending current transaction, creating new transaction with name [" + definition.getName() + "]"); } // 挂起当前事务并获取挂起的资源 SuspendedResourcesHolder suspendedResources = suspend(transaction); try { // 启动新的事务 return startTransaction(definition, transaction, debugEnabled, suspendedResources); } catch (RuntimeException | Error beginEx) { // 开始事务时发生异常,恢复挂起的资源,并将异常抛出 resumeAfterBeginException(transaction, suspendedResources, beginEx); throw beginEx; } } // 如果传播行为是 PROPAGATION_NESTED,则创建一个嵌套事务 if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) { // 检查是否允许嵌套事务 if (!isNestedTransactionAllowed()) { throw new NestedTransactionNotSupportedException( "Transaction manager does not allow nested transactions by default - " + "specify 'nestedTransactionAllowed' property with value 'true'"); } if (debugEnabled) { logger.debug("Creating nested transaction with name [" + definition.getName() + "]"); } // 如果使用保存点进行嵌套事务,则在现有 Spring 管理的事务中创建保存点 if (useSavepointForNestedTransaction()) { // 通过 TransactionStatus 实现的 SavepointManager API 在现有的 Spring 管理的事务中创建保存点 // 通常使用 JDBC 3.0 保存点,永远不会激活 Spring 同步。 DefaultTransactionStatus status = prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null); status.createAndHoldSavepoint(); return status; } else { // 通过嵌套的 begin 和 commit/rollback 调用创建嵌套事务 // 通常仅用于 JTA:如果存在预先存在的 JTA 事务,则此处可能会激活 Spring 同步。 return startTransaction(definition, transaction, debugEnabled, null); } } // Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED. // 如果传播行为是 PROPAGATION_SUPPORTS 或 PROPAGATION_REQUIRED,则参与现有事务 if (debugEnabled) { logger.debug("Participating in existing transaction"); } // 如果启用了验证现有事务,则检查定义是否与现有事务兼容 if (isValidateExistingTransaction()) { if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) { // 检查隔离级别是否与现有事务兼容 Integer currentIsolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel(); if (currentIsolationLevel == null || currentIsolationLevel != definition.getIsolationLevel()) { Constants isoConstants = DefaultTransactionDefinition.constants; throw new IllegalTransactionStateException("Participating transaction with definition [" + definition + "] specifies isolation level which is incompatible with existing transaction: " + (currentIsolationLevel != null ? isoConstants.toCode(currentIsolationLevel, DefaultTransactionDefinition.PREFIX_ISOLATION) : "(unknown)")); } } // 检查只读属性是否与现有事务兼容 if (!definition.isReadOnly()) { if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) { throw new IllegalTransactionStateException("Participating transaction with definition [" + definition + "] is not marked as read-only but existing transaction is"); } } } // 根据情况创建新的事务同步 boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null); } ``` 在`org.springframework.transaction.support.AbstractPlatformTransactionManager#startTransaction` 方法中,首先检查是否应该启用事务同步,然后创建一个新的事务状态对象。接着调用 `doBegin` 方法执行事务的开始操作,并准备同步操作。最后返回创建的事务状态对象。 ```java /** * 开始一个新的事务。 */ private TransactionStatus startTransaction(TransactionDefinition definition, Object transaction, boolean debugEnabled, @Nullable SuspendedResourcesHolder suspendedResources) { // 判断是否要启用事务同步 boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); // 创建一个新的事务状态对象 DefaultTransactionStatus status = newTransactionStatus( definition, transaction, true, newSynchronization, debugEnabled, suspendedResources); // 执行事务开始操作 doBegin(transaction, definition); // 准备同步操作 prepareSynchronization(status, definition); return status; } ``` 在`org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin` 方法中,首先尝试从数据源获取一个数据库连接,并在获取连接后将其存储在事务对象中。然后,它根据事务定义中的属性对连接进行一系列配置,例如设置隔离级别和只读属性。如果连接是自动提交的,则将其切换为手动提交以确保事务的一致性。接着,它准备事务连接,并将连接标记为活跃的事务。最后,如果是新的连接持有者,则将连接持有者绑定到当前线程,以便在事务期间管理连接的生命周期。如果在这个过程中发生任何错误,它将释放连接资源,并抛出一个表示无法创建事务的异常。 ```java @Override protected void doBegin(Object transaction, TransactionDefinition definition) { // 将事务对象转换为 DataSourceTransactionObject DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction; Connection con = null; try { // 如果当前事务对象没有连接持有者,或者连接持有者已与事务同步,则获取新的连接 if (!txObject.hasConnectionHolder() || txObject.getConnectionHolder().isSynchronizedWithTransaction()) { // 获取数据源并从中获取连接 Connection newCon = obtainDataSource().getConnection(); // 如果日志记录级别为DEBUG,则打印获取到的连接信息 if (logger.isDebugEnabled()) { logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction"); } // 设置连接持有者为新获取的连接,并指示需要与事务同步 txObject.setConnectionHolder(new ConnectionHolder(newCon), true); } // 将连接的同步标志设置为true txObject.getConnectionHolder().setSynchronizedWithTransaction(true); // 获取当前连接 con = txObject.getConnectionHolder().getConnection(); // 准备连接的事务属性,并记录之前的隔离级别 Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition); txObject.setPreviousIsolationLevel(previousIsolationLevel); // 设置只读属性 txObject.setReadOnly(definition.isReadOnly()); // 如果连接是自动提交的,则切换为手动提交 if (con.getAutoCommit()) { // 标记需要恢复原来的自动提交状态 txObject.setMustRestoreAutoCommit(true); // 如果日志记录级别为DEBUG,则打印切换自动提交到手动提交的信息 if (logger.isDebugEnabled()) { logger.debug("Switching JDBC Connection [" + con + "] to manual commit"); } // 设置为手动提交 con.setAutoCommit(false); } // 准备事务连接,并将连接标记为活跃的事务 prepareTransactionalConnection(con, definition); txObject.getConnectionHolder().setTransactionActive(true); // 确定事务的超时时间,并设置给连接持有者 int timeout = determineTimeout(definition); if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) { txObject.getConnectionHolder().setTimeoutInSeconds(timeout); } // 如果是新的连接持有者,则将连接持有者绑定到线程 if (txObject.isNewConnectionHolder()) { TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder()); } } catch (Throwable ex) { // 如果是新的连接持有者,则释放连接,并将连接持有者设置为null if (txObject.isNewConnectionHolder()) { DataSourceUtils.releaseConnection(con, obtainDataSource()); txObject.setConnectionHolder(null, false); } // 抛出无法创建事务异常 throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex); } } ``` 在`org.springframework.jdbc.datasource.DataSourceTransactionManager#prepareTransactionalConnection` 方法中,如果设置了“enforceReadOnly”标志为true,并且事务定义指示为只读事务,则会执行一个“SET TRANSACTION READ ONLY”语句。 ```java /** * 在事务开始后准备事务性 {@code Connection}。 *

如果 {@link #setEnforceReadOnly "enforceReadOnly"} 标志设置为 {@code true}, * 并且事务定义指示为只读事务,那么默认实现将执行一个 "SET TRANSACTION READ ONLY" 语句。 *

"SET TRANSACTION READ ONLY" 语句可被 Oracle、MySQL 和 Postgres 理解,并且可能适用于其他数据库。 * 如果您希望调整此处理方式,请相应地重写此方法。 * @param con 事务性 JDBC 连接 * @param definition 当前事务定义 * @throws SQLException 如果 JDBC API 抛出异常 * @since 4.3.7 * @see #setEnforceReadOnly */ protected void prepareTransactionalConnection(Connection con, TransactionDefinition definition) throws SQLException { if (isEnforceReadOnly() && definition.isReadOnly()) { try (Statement stmt = con.createStatement()) { stmt.executeUpdate("SET TRANSACTION READ ONLY"); } } } ``` **提交事务** 在`org.springframework.transaction.support.AbstractPlatformTransactionManager#commit` 方法中,检查事务状态是否已完成,如果已完成则抛出非法事务状态异常。然后,检查事务状态是否为本地回滚,如果是,则执行回滚操作。接着,如果全局事务标记为回滚,但事务代码请求提交,则执行回滚操作。最后,如果以上情况都不满足,则执行事务的提交操作。 ```java /** * 此提交的实现处理参与现有事务和编程回滚请求。委托给{@code isRollbackOnly}、{@code doCommit}和{@code rollback}。 * 如果事务已经完成,则抛出异常。 * 如果事务状态为本地回滚,则根据情况执行回滚操作。 * 如果不应仅在全局回滚的情况下提交事务,并且事务状态为全局回滚,则根据情况执行回滚操作。 * 否则,执行提交操作。 * @param status 事务状态对象 * @throws TransactionException 如果提交过程中发生事务异常 * @see org.springframework.transaction.TransactionStatus#isRollbackOnly() * @see #doCommit * @see #rollback */ @Override public final void commit(TransactionStatus status) throws TransactionException { // 如果事务已经完成,则抛出异常 if (status.isCompleted()) { throw new IllegalTransactionStateException( "Transaction is already completed - do not call commit or rollback more than once per transaction"); } DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status; // 如果事务状态为本地回滚,则根据情况执行回滚操作 if (defStatus.isLocalRollbackOnly()) { if (defStatus.isDebug()) { logger.debug("Transactional code has requested rollback"); } processRollback(defStatus, false); return; } // 如果不应仅在全局回滚的情况下提交事务,并且事务状态为全局回滚,则根据情况执行回滚操作 if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) { if (defStatus.isDebug()) { logger.debug("Global transaction is marked as rollback-only but transactional code requested commit"); } processRollback(defStatus, true); return; } // 否则,执行提交操作 processCommit(defStatus); } ``` 在`org.springframework.transaction.support.AbstractPlatformTransactionManager#processCommit` 方法中,检查并应用了回滚标志,然后执行相应的提交逻辑。在提交的过程中,会触发各种回调方法,如提交前、提交后等,以便在事务提交的不同阶段执行特定的逻辑。如果发生了意外回滚或提交失败,它会相应地处理异常情况,并执行相应的回滚操作。最终,无论提交是否成功,都会执行清理操作以确保事务状态正确。 ```java /** * 处理实际的提交操作。 * 已经检查并应用了回滚标志。 * * @param status 表示事务的对象 * @throws TransactionException 如果提交失败 */ private void processCommit(DefaultTransactionStatus status) throws TransactionException { try { boolean beforeCompletionInvoked = false; try { boolean unexpectedRollback = false; // 为提交做准备 prepareForCommit(status); // 触发提交前的回调 triggerBeforeCommit(status); // 触发事务完成前的回调 triggerBeforeCompletion(status); beforeCompletionInvoked = true; // 如果存在保存点 if (status.hasSavepoint()) { if (status.isDebug()) { // 释放事务保存点 logger.debug("Releasing transaction savepoint"); } // 判断是否全局回滚 unexpectedRollback = status.isGlobalRollbackOnly(); // 释放持有的保存点 status.releaseHeldSavepoint(); } else if (status.isNewTransaction()) { // 如果是新事务 if (status.isDebug()) { // 开始事务提交 logger.debug("Initiating transaction commit"); } // 判断是否全局回滚 unexpectedRollback = status.isGlobalRollbackOnly(); // 执行提交 doCommit(status); } else if (isFailEarlyOnGlobalRollbackOnly()) { // 如果全局回滚 // 判断是否全局回滚 unexpectedRollback = status.isGlobalRollbackOnly(); } // 如果存在意外回滚,但仍未从提交中获得相应的异常,则抛出 UnexpectedRollbackException 异常 if (unexpectedRollback) { throw new UnexpectedRollbackException( "Transaction silently rolled back because it has been marked as rollback-only"); } } catch (UnexpectedRollbackException ex) { // 只能由 doCommit 导致 triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK); throw ex; } catch (TransactionException ex) { // 只能由 doCommit 导致 if (isRollbackOnCommitFailure()) { doRollbackOnCommitException(status, ex); } else { triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN); } throw ex; } catch (RuntimeException | Error ex) { if (!beforeCompletionInvoked) { triggerBeforeCompletion(status); } doRollbackOnCommitException(status, ex); throw ex; } // 触发 afterCommit 回调,在那里抛出的异常被传播给调用者,但事务仍被视为已提交。 try { triggerAfterCommit(status); } finally { triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED); } } finally { // 完成后的清理 cleanupAfterCompletion(status); } } ``` 在`org.springframework.jdbc.datasource.DataSourceTransactionManager#doCommit` 方法中,通过事务状态对象获取数据源事务对象,然后从事务对象中获取数据库连接,接着尝试提交数据库事务,如果提交过程中发生 SQL 异常,则将其转换为 Spring 事务异常并抛出。 ```java @Override protected void doCommit(DefaultTransactionStatus status) { // 强制转换为 DataSourceTransactionObject 对象,获取事务相关信息 DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction(); // 从事务对象中获取数据库连接 Connection con = txObject.getConnectionHolder().getConnection(); // 如果是调试模式,则记录调试信息 if (status.isDebug()) { logger.debug("Committing JDBC transaction on Connection [" + con + "]"); } try { // 提交数据库事务 con.commit(); } catch (SQLException ex) { // 发生 SQL 异常,将其转换为 Spring 事务异常并抛出 throw translateException("JDBC commit", ex); } } ``` **回滚事务** 在`org.springframework.transaction.support.AbstractPlatformTransactionManager#rollback` 方法中,,用于处理现有事务的回滚操作。它委托给 `processRollback` 方法来执行回滚,并通过检查事务状态来确保不会多次调用提交或回滚操作。 ```java /** * 该回滚操作的实现处理参与现有事务。委托给 {@code doRollback} 和 {@code doSetRollbackOnly}。 * * @see #doRollback * @see #doSetRollbackOnly */ @Override public final void rollback(TransactionStatus status) throws TransactionException { // 如果事务已经完成,则抛出异常 if (status.isCompleted()) { throw new IllegalTransactionStateException( "Transaction is already completed - do not call commit or rollback more than once per transaction"); } // 将事务状态转换为默认事务状态 DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status; // 执行回滚操作 processRollback(defStatus, false); } ``` 在`org.springframework.transaction.support.AbstractPlatformTransactionManager#processRollback` 方法中,检查事务的完成标志,然后根据事务的状态执行相应的回滚操作。如果存在保存点,则回滚到该保存点;如果是新事务,则执行初始化的事务回滚操作;如果参与了较大的事务,则根据条件进行相应的处理。在执行过程中,会根据情况触发相应的事务同步操作,并根据全局回滚标记判断是否引发意外回滚异常。最后,无论是否发生异常,都会执行完成后的清理操作。 ```java /** * 处理实际的回滚操作。 * 已经检查过事务完成标志。 * @param status 表示事务的对象 * @param unexpected 是否意外回滚 * @throws TransactionException 如果回滚失败 */ private void processRollback(DefaultTransactionStatus status, boolean unexpected) { try { boolean unexpectedRollback = unexpected; try { // 触发完成前操作 triggerBeforeCompletion(status); // 回滚到保存点 if (status.hasSavepoint()) { if (status.isDebug()) { logger.debug("Rolling back transaction to savepoint"); } status.rollbackToHeldSavepoint(); } // 初始化事务回滚 else if (status.isNewTransaction()) { if (status.isDebug()) { logger.debug("Initiating transaction rollback"); } doRollback(status); } else { // 参与较大的事务 if (status.hasTransaction()) { // 如果是本地回滚,或者全局回滚失败 if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) { if (status.isDebug()) { logger.debug("Participating transaction failed - marking existing transaction as rollback-only"); } // 设置事务为仅回滚 doSetRollbackOnly(status); } else { if (status.isDebug()) { logger.debug("Participating transaction failed - letting transaction originator decide on rollback"); } } } else { logger.debug("Should roll back transaction but cannot - no transaction available"); } // 如果不是全局仅回滚,则不会在此处考虑意外回滚 if (!isFailEarlyOnGlobalRollbackOnly()) { unexpectedRollback = false; } } } catch (RuntimeException | Error ex) { // 触发完成后操作,状态为未知 triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN); throw ex; } // 触发完成后操作,状态为已回滚 triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK); // 如果存在全局回滚标记,则引发UnexpectedRollbackException if (unexpectedRollback) { throw new UnexpectedRollbackException( "Transaction rolled back because it has been marked as rollback-only"); } } finally { // 完成后清理 cleanupAfterCompletion(status); } } ``` 在`org.springframework.jdbc.datasource.DataSourceTransactionManager#doRollback` 方法中,从事务状态中获取数据源事务对象,然后从该对象中获取连接对象。接着,它尝试执行连接对象的回滚操作。 ```java /** * 执行回滚操作。 * @param status 表示事务的对象 */ @Override protected void doRollback(DefaultTransactionStatus status) { // 获取事务数据源对象 DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction(); // 获取连接对象 Connection con = txObject.getConnectionHolder().getConnection(); // 如果启用了调试模式,则记录回滚日志 if (status.isDebug()) { logger.debug("Rolling back JDBC transaction on Connection [" + con + "]"); } try { // 执行回滚操作 con.rollback(); } catch (SQLException ex) { // 抛出数据库异常 throw translateException("JDBC rollback", ex); } } ``` ================================================ FILE: spring-transaction/spring-transaction-platformTransactionManager/pom.xml ================================================ com.xcs.spring spring-transaction 0.0.1-SNAPSHOT 4.0.0 spring-transaction-platformTransactionManager ================================================ FILE: spring-transaction/spring-transaction-platformTransactionManager/src/main/java/com/xcs/spring/PlatformTransactionManagerDemo.java ================================================ package com.xcs.spring; import com.mysql.jdbc.Driver; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.jdbc.datasource.SimpleDriverDataSource; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.DefaultTransactionDefinition; import java.sql.SQLException; import java.time.LocalDateTime; import java.util.Random; public class PlatformTransactionManagerDemo { private static PlatformTransactionManager transactionManager; private static JdbcTemplate jdbcTemplate; public static void main(String[] args) throws SQLException { // 数据库连接 URL,格式为 jdbc:数据库驱动名称://主机地址:端口号/数据库名称 String url = "jdbc:mysql://localhost:3306/spring-reading"; // 数据库用户名 String username = "root"; // 数据库密码 String password = "123456"; // 创建 SimpleDriverDataSource 对象,用于管理数据源 SimpleDriverDataSource dataSource = new SimpleDriverDataSource(new Driver(), url, username, password); // 创建 DataSourceTransactionManager 对象,用于管理事务 transactionManager = new DataSourceTransactionManager(dataSource); // 创建 JdbcTemplate 对象,用于执行 SQL 语句 jdbcTemplate = new JdbcTemplate(dataSource); insertScore(); } private static void insertScore() { // 事务定义 DefaultTransactionDefinition definition = new DefaultTransactionDefinition(DefaultTransactionDefinition.PROPAGATION_REQUIRED); // 开启一个新的事务,返回事务状态对象 TransactionStatus transactionStatus = transactionManager.getTransaction(definition); try { long id = System.currentTimeMillis(); int score = new Random().nextInt(100); // 向数据库中插入随机生成的分数 int row = jdbcTemplate.update("insert into scores(id,score) values(?,?)", id, score); insertScoreLog(id); // 模拟异常,用于测试事务回滚 // int i = 1 / 0; // 提交事务 transactionManager.commit(transactionStatus); // 打印影响行数 System.out.println("scores row = " + row); } catch (Exception e) { // 出现异常时回滚事务 transactionManager.rollback(transactionStatus); e.printStackTrace(); } } private static void insertScoreLog(long scoreId) { // 事务定义 DefaultTransactionDefinition definition = new DefaultTransactionDefinition(DefaultTransactionDefinition.PROPAGATION_REQUIRED); // 开启一个新的事务,返回事务状态对象 TransactionStatus transactionStatus = transactionManager.getTransaction(definition); try { long id = System.currentTimeMillis(); LocalDateTime createTime = LocalDateTime.now(); // 向数据库中插入随机生成的分数 int row = jdbcTemplate.update("insert into scores_log(id,score_id,create_time) values(?,?,?)", id, scoreId, createTime); // 模拟异常,用于测试事务回滚 // int i = 1 / 0; // 提交事务 transactionManager.commit(transactionStatus); // 打印影响行数 System.out.println("scores_log row = " + row); } catch (Exception e) { // 出现异常时回滚事务 transactionManager.rollback(transactionStatus); e.printStackTrace(); } } } ================================================ FILE: spring-transaction/spring-transaction-springTransactionAnnotationParser/README.md ================================================ ## TransactionAnnotationParser - [TransactionAnnotationParser](#transactionannotationparser) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、接口源码](#四接口源码) - [五、主要实现](#五主要实现) - [六、类关系图](#六类关系图) - [七、最佳实践](#七最佳实践) - [八、源码分析](#八源码分析) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址 ** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 `TransactionAnnotationParser` 接口是 Spring Framework 中的一个接口,用于定义解析事务相关注解的标准方式,我们可以通过实现该接口来自定义解析特定事务注解的逻辑,并将其转换为 Spring 内部的事务配置对象,以实现更灵活的事务管理。 ### 三、主要功能 1. **解析事务注解** + 该接口定义了解析事务相关注解的方法,我们可以通过实现该方法来提取注解中的属性信息。 2. **支持多种注解** + 允许实现类支持多种事务相关的注解,不仅限于 `@Transactional`,这使得接口更加灵活和通用。 3. **自定义扩展** + 根据自己的需求实现该接口,并自定义解析逻辑,以满足特定场景下的事务管理需求,从而扩展 Spring Framework 的事务管理功能。 ### 四、接口源码 用于解析已知的事务注解类型。它包括一个默认方法用于确定给定类是否是事务注解的候选类,以及一个方法用于解析给定方法或类上的事务注解并将其转换为 Spring 框架的事务属性对象。该接口的实现类可用于支持特定的事务注解类型,如 Spring 的 `@Transactional`、JTA 1.2 的 `javax.transaction.Transactional` 或 EJB3 的 `javax.ejb.TransactionAttribute`。 ```java /** * 已知事务注解类型解析的策略接口。 * {@link AnnotationTransactionAttributeSource} 委托给此类解析器,以支持特定的注解类型,例如 Spring 的 {@link Transactional}、JTA 1.2 的 {@link javax.transaction.Transactional} 或 EJB3 的 {@link javax.ejb.TransactionAttribute}。 * * @author Juergen Hoeller * @since 2.5 * @see AnnotationTransactionAttributeSource * @see SpringTransactionAnnotationParser * @see Ejb3TransactionAnnotationParser * @see JtaTransactionAnnotationParser */ public interface TransactionAnnotationParser { /** * 确定给定类是否是此 {@code TransactionAnnotationParser} 的注解格式中的事务属性候选类。 *

如果此方法返回 {@code false},则给定类上的方法将不会被遍历用于 {@code #parseTransactionAnnotation} 内省。 * 返回 {@code false} 是针对未受影响的类的优化,而 {@code true} 意味着类需要对给定类上的每个方法进行完整的内省。 * @param targetClass 要内省的类 * @return 如果已知该类在类或方法级别上没有事务注解,则返回 {@code false};否则返回 {@code true}。默认实现返回 {@code true},导致常规内省。 * @since 5.2 */ default boolean isCandidateClass(Class targetClass) { return true; } /** * 根据此解析器理解的注解类型,解析给定方法或类的事务属性。 *

本质上,这将已知的事务注解解析为 Spring 的元数据属性类。如果方法/类不是事务性的,则返回 {@code null}。 * @param element 被注解的方法或类 * @return 配置的事务属性,如果没有找到则返回 {@code null} * @see AnnotationTransactionAttributeSource#determineTransactionAttribute */ @Nullable TransactionAttribute parseTransactionAnnotation(AnnotatedElement element); } ``` ### 五、主要实现 1. **SpringTransactionAnnotationParser** + 用于解析 Spring Framework 中的 `@Transactional` 注解,它能够将 `@Transactional` 注解中的属性信息提取出来,并转换为 Spring 内部的事务配置对象。在 Spring 应用中,通常使用 `@Transactional` 注解声明事务,因此这个解析器是非常常用的,它使得事务管理更加便捷和灵活。 2. **Ejb3TransactionAnnotationParser** + 用于解析 EJB3 规范中的 `javax.ejb.TransactionAttribute` 注解。EJB3 是 Java EE 规范中的一部分,用于开发企业级应用程序。`Ejb3TransactionAnnotationParser` 负责解析 `javax.ejb.TransactionAttribute` 注解,并将其转换为 Spring 内部的事务配置对象,这使得 Spring 能够与 EJB3 技术集成,实现统一的事务管理。 3. **JtaTransactionAnnotationParser** + 用于解析 JTA(Java Transaction API)规范中定义的 `javax.transaction.Transactional` 注解。JTA 是 Java 平台的一部分,提供了一套 API 用于管理分布式事务。`JtaTransactionAnnotationParser` 负责解析 `javax.transaction.Transactional` 注解,并将其转换为 Spring 内部的事务配置对象,这使得 Spring 能够与分布式事务相关的技术集成,实现全面的事务管理支持。 ### 六、类关系图 ~~~mermaid classDiagram direction BT class Ejb3TransactionAnnotationParser class JtaTransactionAnnotationParser class SpringTransactionAnnotationParser class TransactionAnnotationParser { <> } Ejb3TransactionAnnotationParser ..> TransactionAnnotationParser JtaTransactionAnnotationParser ..> TransactionAnnotationParser SpringTransactionAnnotationParser ..> TransactionAnnotationParser ~~~ ### 七、最佳实践 使用 `SpringTransactionAnnotationParser` 类来解析方法上的事务注解,并将其转换为事务属性对象。在这个示例中,通过反射获取了 `ScoresServiceImpl` 类中的 `insertScore` 方法,然后通过 `SpringTransactionAnnotationParser` 解析该方法上的事务注解,最后将解析结果输出到控制台。 ```java public class SpringTransactionAnnotationParserDemo { public static void main(String[] args) throws NoSuchMethodException { // 获取 ScoresServiceImpl 类中的 insertScore 方法 Method insertScoreMethod = ScoresServiceImpl.class.getMethod("insertScore"); // 创建 SpringTransactionAnnotationParser 实例 SpringTransactionAnnotationParser parser = new SpringTransactionAnnotationParser(); // 解析 insertScore 方法上的事务注解,并转换为事务属性对象 TransactionAttribute transactionAttribute = parser.parseTransactionAnnotation(insertScoreMethod); // 输出事务属性对象 System.out.println(transactionAttribute); } } ``` `ScoresServiceImpl` 类实现了 `ScoresService` 接口,其中的 `insertScore` 方法被 `@Transactional` 注解修饰,声明了一个事务。该事务的特性包括:只读(readOnly = true),遇到任何异常都会回滚(rollbackFor = Exception.class),事务隔离级别为可重复读(isolation = Isolation.REPEATABLE_READ),超时时间为 30 秒(timeout = 30),以及标签为 "tx1" 和 "tx2"。 ```java public class ScoresServiceImpl implements ScoresService { @Override @Transactional( readOnly = true, rollbackFor = Exception.class, isolation = Isolation.REPEATABLE_READ, timeout = 30, label = {"tx1", "tx2"} ) public void insertScore() { // TODO } } ``` 运行结果,事务的传播行为为 `PROPAGATION_REQUIRED`,隔离级别为 `ISOLATION_REPEATABLE_READ`,超时时间为 30 秒,只读模式开启,标签为 "tx1" 和 "tx2",同时,事务会回滚遇到 `java.lang.Exception` 及其子类的异常。 ```java PROPAGATION_REQUIRED,ISOLATION_REPEATABLE_READ,timeout_30,readOnly; [tx1,tx2],-java.lang.Exception ``` ### 八、源码分析 在`org.springframework.transaction.annotation.SpringTransactionAnnotationParser#parseTransactionAnnotation(java.lang.reflect.AnnotatedElement)` 方法中,首先通过 `AnnotatedElementUtils` 查找并获取元素上合并的 `@Transactional` 注解的属性信息,如果找到了注解,则调用另一个方法解析事务注解并返回事务属性对象,如果未找到注解,则返回 null。 ```java @Override @Nullable public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) { // 查找并获取元素上合并的 @Transactional 注解的属性信息 AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes( element, Transactional.class, false, false); // 如果属性信息不为空,则解析事务注解并返回事务属性对象 if (attributes != null) { return parseTransactionAnnotation(attributes); } // 如果属性信息为空,则返回 null else { return null; } } ``` 在`org.springframework.transaction.annotation.SpringTransactionAnnotationParser#parseTransactionAnnotation(org.springframework.core.annotation.AnnotationAttributes)` 方法中,用于解析给定的注解属性并将其转换为事务属性对象。它根据注解属性中的信息设置事务的传播行为、隔离级别、超时时间、只读属性、限定符、标签和回滚规则等。最后,它返回一个基于规则的事务属性对象。 ```java protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) { // 创建一个基于规则的事务属性对象 RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute(); // 设置事务传播行为 Propagation propagation = attributes.getEnum("propagation"); rbta.setPropagationBehavior(propagation.value()); // 设置事务隔离级别 Isolation isolation = attributes.getEnum("isolation"); rbta.setIsolationLevel(isolation.value()); // 设置事务超时时间 rbta.setTimeout(attributes.getNumber("timeout").intValue()); // 设置事务超时时间字符串 String timeoutString = attributes.getString("timeoutString"); // 校验是否同时设置了超时时间和超时时间字符串 Assert.isTrue(!StringUtils.hasText(timeoutString) || rbta.getTimeout() < 0, "Specify 'timeout' or 'timeoutString', not both"); rbta.setTimeoutString(timeoutString); // 设置事务是否为只读模式 rbta.setReadOnly(attributes.getBoolean("readOnly")); // 设置事务限定符 rbta.setQualifier(attributes.getString("value")); // 设置事务标签 rbta.setLabels(Arrays.asList(attributes.getStringArray("label"))); // 解析回滚规则 List rollbackRules = new ArrayList<>(); // 解析需要回滚的异常类 for (Class rbRule : attributes.getClassArray("rollbackFor")) { rollbackRules.add(new RollbackRuleAttribute(rbRule)); } // 解析需要回滚的异常类名 for (String rbRule : attributes.getStringArray("rollbackForClassName")) { rollbackRules.add(new RollbackRuleAttribute(rbRule)); } // 解析不需要回滚的异常类 for (Class rbRule : attributes.getClassArray("noRollbackFor")) { rollbackRules.add(new NoRollbackRuleAttribute(rbRule)); } // 解析不需要回滚的异常类名 for (String rbRule : attributes.getStringArray("noRollbackForClassName")) { rollbackRules.add(new NoRollbackRuleAttribute(rbRule)); } // 设置事务的回滚规则 rbta.setRollbackRules(rollbackRules); // 返回解析后的事务属性对象 return rbta; } ``` ================================================ FILE: spring-transaction/spring-transaction-springTransactionAnnotationParser/pom.xml ================================================ com.xcs.spring spring-transaction 0.0.1-SNAPSHOT 4.0.0 spring-transaction-springTransactionAnnotationParser ================================================ FILE: spring-transaction/spring-transaction-springTransactionAnnotationParser/src/main/java/com/xcs/spring/ScoresService.java ================================================ package com.xcs.spring; public interface ScoresService { void insertScore(); } ================================================ FILE: spring-transaction/spring-transaction-springTransactionAnnotationParser/src/main/java/com/xcs/spring/ScoresServiceImpl.java ================================================ package com.xcs.spring; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Transactional; public class ScoresServiceImpl implements ScoresService { @Override @Transactional( readOnly = true, rollbackFor = Exception.class, isolation = Isolation.REPEATABLE_READ, timeout = 30 ) public void insertScore() { // TODO } } ================================================ FILE: spring-transaction/spring-transaction-springTransactionAnnotationParser/src/main/java/com/xcs/spring/SpringTransactionAnnotationParserDemo.java ================================================ package com.xcs.spring; import org.springframework.transaction.annotation.SpringTransactionAnnotationParser; import org.springframework.transaction.interceptor.TransactionAttribute; import java.lang.reflect.Method; public class SpringTransactionAnnotationParserDemo { public static void main(String[] args) throws NoSuchMethodException { // 获取 ScoresServiceImpl 类中的 insertScore 方法 Method insertScoreMethod = ScoresServiceImpl.class.getMethod("insertScore"); // 创建 SpringTransactionAnnotationParser 实例 SpringTransactionAnnotationParser parser = new SpringTransactionAnnotationParser(); // 解析 insertScore 方法上的事务注解,并转换为事务属性对象 TransactionAttribute transactionAttribute = parser.parseTransactionAnnotation(insertScoreMethod); // 输出事务属性对象 System.out.println(transactionAttribute); } } ================================================ FILE: spring-transaction/spring-transaction-transactionAttributeSource/README.md ================================================ ## TransactionAttributeSource - [TransactionAttributeSource](#TransactionAttributeSource) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、接口源码](#四接口源码) - [五、主要实现](#五主要实现) - [六、最佳实践](#六最佳实践) - [七、源码分析](#七源码分析) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址 ** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 `TransactionAttributeSource` 接口是 Spring Framework 中的关键接口,用于提供事务管理的配置信息,通过分析给定的方法和目标类,确定事务的属性,例如传播行为、隔离级别等,为声明式事务提供了灵活性和可定制性。 ### 三、主要功能 1. **提供事务属性** + 根据给定的方法和目标类,确定事务的属性,包括传播行为、隔离级别、超时时间、只读状态等。 2. **可扩展性** + Spring 框架提供了多种实现 `TransactionAttributeSource` 接口的类,如 `NameMatchTransactionAttributeSource`、`AnnotationTransactionAttributeSource` 等,以支持不同的解析策略,例如基于方法名的匹配、基于注解的配置等。 ### 四、接口源码 `TransactionAttributeSource` 接口,主要是由 `TransactionInterceptor` 用于元数据检索的策略接口。该接口的实现知道如何获取事务属性,可以从配置、源级别的元数据属性或其他任何地方获取。它包含了两个方法,一个用于确定给定类是否是事务属性的候选类,另一个用于返回给定方法的事务属性。 ```java /** * {@code TransactionAttributeSource} 接口是由 {@link TransactionInterceptor} 用于元数据检索的策略接口。 * 实现类知道如何获取事务属性,无论是从配置、源级别的元数据属性(例如 Java 5 注解)还是其他任何地方。 * * @author Rod Johnson * @author Juergen Hoeller * @since 15.04.2003 * @see TransactionInterceptor#setTransactionAttributeSource * @see TransactionProxyFactoryBean#setTransactionAttributeSource * @see org.springframework.transaction.annotation.AnnotationTransactionAttributeSource */ public interface TransactionAttributeSource { /** * 确定给定的类是否是此 {@code TransactionAttributeSource} 的元数据格式中事务属性的候选类。 *

如果此方法返回 {@code false},则不会遍历给定类的方法以进行 {@link #getTransactionAttribute} 内省。 * 返回 {@code false} 因此是对非受影响类的优化,而 {@code true} 则意味着必须针对给定类的每个方法进行完全内省。 * @param targetClass 要内省的类 * @return 如果类已知在类级别或方法级别没有事务属性,则返回 {@code false};否则返回 {@code true}。 * 默认实现返回 {@code true},导致常规内省。 * @since 5.2 */ default boolean isCandidateClass(Class targetClass) { return true; } /** * 返回给定方法的事务属性,如果方法不是事务性的,则返回 {@code null}。 * @param method 要内省的方法 * @param targetClass 目标类(可能为 {@code null},在这种情况下,必须使用方法的声明类) * @return 匹配的事务属性,如果未找到则返回 {@code null} */ @Nullable TransactionAttribute getTransactionAttribute(Method method, @Nullable Class targetClass); } ``` ### 五、主要实现 1. **AnnotationTransactionAttributeSource** + 用于解析基于注解的事务配置信息的实现类。它能够解析类级别和方法级别的 `@Transactional` 注解,将注解中定义的事务属性转换为 `TransactionAttribute` 对象。 2. **CompositeTransactionAttributeSource** + 将多个 `TransactionAttributeSource` 组合在一起。通过组合多个 `TransactionAttributeSource` 对象,可以实现多种事务属性解析策略的混合使用,提高了灵活性和定制性。 3. **MatchAlwaysTransactionAttributeSource** + 简单的 `TransactionAttributeSource` 实现,它始终返回相同的事务属性。通常用于简单的场景或者作为其他复杂 `TransactionAttributeSource` 实现的默认备选项。 4. **MethodMapTransactionAttributeSource** + 基于方法名匹配的 `TransactionAttributeSource` 实现。它通过配置一个方法名到事务属性的映射表,根据方法名来确定相应的事务属性。 5. **NameMatchTransactionAttributeSource** + 根据方法名进行匹配的 `TransactionAttributeSource` 实现。它能够根据配置的方法名模式,匹配目标方法并返回相应的事务属性。 ### 六、类关系图 ~~~mermaid classDiagram direction BT class AbstractFallbackTransactionAttributeSource class AnnotationTransactionAttributeSource class CompositeTransactionAttributeSource class MatchAlwaysTransactionAttributeSource class MethodMapTransactionAttributeSource class NameMatchTransactionAttributeSource class TransactionAttributeSource { <> } AbstractFallbackTransactionAttributeSource ..> TransactionAttributeSource AnnotationTransactionAttributeSource --> AbstractFallbackTransactionAttributeSource CompositeTransactionAttributeSource ..> TransactionAttributeSource MatchAlwaysTransactionAttributeSource ..> TransactionAttributeSource MethodMapTransactionAttributeSource ..> TransactionAttributeSource NameMatchTransactionAttributeSource ..> TransactionAttributeSource ~~~ ### 六、最佳实践 使用 `AnnotationTransactionAttributeSource` 类来解析基于注解的事务配置信息。通过获取 `ScoresServiceImpl` 类中的 `insertScore` 方法,然后利用 `AnnotationTransactionAttributeSource` 对象来解析该方法的事务属性,最后将解析结果输出到控制台。这样可以帮助我们了解特定方法的事务配置情况,以便进行调试和优化。 ```java public class TransactionAttributeSourceDemo { public static void main(String[] args) throws NoSuchMethodException { // 获取 ScoresServiceImpl 类中的 insertScore 方法 Method insertScoreMethod = ScoresServiceImpl.class.getMethod("insertScore"); // 创建一个基于注解的事务属性源对象 TransactionAttributeSource transactionAttributeSource = new AnnotationTransactionAttributeSource(); // 解析 insertScore 方法的事务属性 TransactionAttribute transactionAttribute = transactionAttributeSource.getTransactionAttribute(insertScoreMethod, ScoresServiceImpl.class); // 输出事务属性 System.out.println(transactionAttribute); } } ``` `ScoresServiceImpl` 类实现了 `ScoresService` 接口,其中的 `insertScore` 方法被 `@Transactional` 注解修饰,声明了一个事务。该事务的特性包括只读(readOnly = true),遇到任何异常都会回滚(rollbackFor = Exception.class),事务隔离级别为可重复读(isolation = Isolation.REPEATABLE_READ),超时时间为 30 秒(timeout = 30),以及标签为 "tx1" 和 "tx2"。 ```java public class ScoresServiceImpl implements ScoresService { @Override @Transactional( readOnly = true, rollbackFor = Exception.class, isolation = Isolation.REPEATABLE_READ, timeout = 30, label = {"tx1", "tx2"} ) public void insertScore() { // TODO } } ``` 运行结果,事务的传播行为为 `PROPAGATION_REQUIRED`,隔离级别为 `ISOLATION_REPEATABLE_READ`,超时时间为 30 秒,只读模式开启,标签为 "tx1" 和 "tx2",同时,事务会回滚遇到 `java.lang.Exception` 及其子类的异常。 ```java PROPAGATION_REQUIRED,ISOLATION_REPEATABLE_READ,timeout_30,readOnly; [tx1,tx2],-java.lang.Exception ``` ### 七、源码分析 在`org.springframework.transaction.interceptor.AbstractFallbackTransactionAttributeSource#getTransactionAttribute` 方法中,用于确定方法调用的事务属性。如果在方法级别找不到事务属性,则默认使用类级别的事务属性。首先,它检查是否有缓存的事务属性值,如果有则直接返回缓存值,否则计算方法的事务属性并将其放入缓存。在计算事务属性时,会根据方法的限定名来标识方法,如果事务属性是 `DefaultTransactionAttribute` 类型,则设置描述符和解析属性字符串。最后,根据日志级别输出添加事务方法及其属性的日志,并将计算得到的事务属性放入缓存。 ```java /** * 确定此方法调用的事务属性。 *

如果未找到方法属性,则默认为类的事务属性。 * @param method 当前调用的方法(永远不会为 {@code null}) * @param targetClass 此调用的目标类(可能为 {@code null}) * @return 此方法的 TransactionAttribute,如果方法不是事务性的,则返回 {@code null} */ @Override @Nullable public TransactionAttribute getTransactionAttribute(Method method, @Nullable Class targetClass) { // 如果方法是 Object 类的方法,直接返回 null,因为这些方法不应该是事务性的。 if (method.getDeclaringClass() == Object.class) { return null; } // 首先,查看是否有缓存值。 Object cacheKey = getCacheKey(method, targetClass); TransactionAttribute cached = this.attributeCache.get(cacheKey); if (cached != null) { // 值将是一个规范值,表示没有事务属性,或者是一个实际的事务属性。 if (cached == NULL_TRANSACTION_ATTRIBUTE) { return null; } else { return cached; } } else { // 我们需要计算它。 TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass); // 将其放入缓存。 if (txAttr == null) { this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE); } else { // 获取方法的限定名,用于在日志中标识方法。 String methodIdentification = ClassUtils.getQualifiedMethodName(method, targetClass); // 如果事务属性是 DefaultTransactionAttribute 类型,设置描述符和解析属性字符串。 if (txAttr instanceof DefaultTransactionAttribute) { DefaultTransactionAttribute dta = (DefaultTransactionAttribute) txAttr; dta.setDescriptor(methodIdentification); dta.resolveAttributeStrings(this.embeddedValueResolver); } // 如果日志级别为 TRACE,输出添加事务方法及其属性的日志。 if (logger.isTraceEnabled()) { logger.trace("Adding transactional method '" + methodIdentification + "' with attribute: " + txAttr); } this.attributeCache.put(cacheKey, txAttr); } return txAttr; } } ``` 在`org.springframework.transaction.interceptor.AbstractFallbackTransactionAttributeSource#computeTransactionAttribute` 方法中,首先检查方法是否是公共方法,并根据情况从目标类或原始方法中查找事务属性。如果找到了事务属性,则返回该属性;否则返回 null。 ```java /** * 与 {@link #getTransactionAttribute} 具有相同的签名,但不缓存结果。 * {@link #getTransactionAttribute} 实际上是此方法的缓存装饰器。 *

从 4.1.8 版本开始,此方法可以被重写。 * @since 4.1.8 * @see #getTransactionAttribute */ @Nullable protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class targetClass) { // 如果只允许公共方法,并且方法不是公共的,则不允许。 if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) { return null; } // 方法可能在接口上,但我们需要从目标类获取属性。 // 如果目标类为 null,则方法不会改变。 Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass); // 首先尝试目标类中的方法。 TransactionAttribute txAttr = findTransactionAttribute(specificMethod); if (txAttr != null) { return txAttr; } // 其次尝试目标类上的事务属性。 txAttr = findTransactionAttribute(specificMethod.getDeclaringClass()); if (txAttr != null && ClassUtils.isUserLevelMethod(method)) { return txAttr; } if (specificMethod != method) { // 回退到原始方法。 txAttr = findTransactionAttribute(method); if (txAttr != null) { return txAttr; } // 最后回退到原始方法的类。 txAttr = findTransactionAttribute(method.getDeclaringClass()); if (txAttr != null && ClassUtils.isUserLevelMethod(method)) { return txAttr; } } return null; } ``` 在`org.springframework.transaction.annotation.AnnotationTransactionAttributeSource#findTransactionAttribute(java.lang.reflect.Method)` 方法中,调用了 `determineTransactionAttribute` 方法来确定给定方法的事务属性,并将其返回。如果无法确定事务属性,则返回 null。 ```java @Override @Nullable protected TransactionAttribute findTransactionAttribute(Method method) { return determineTransactionAttribute(method); } ``` 在`org.springframework.transaction.annotation.AnnotationTransactionAttributeSource#determineTransactionAttribute` 方法中,用于确定给定方法或类的事务属性。它通过配置的 `TransactionAnnotationParsers` 来解析已知注解,并将其转换为 Spring 的元数据属性类。如果找不到事务属性,则返回 null。该方法可以被重写以支持携带事务元数据的自定义注解。 ```java /** * 确定给定方法或类的事务属性。 *

此实现委托给配置的 {@link TransactionAnnotationParser TransactionAnnotationParsers}, * 用于将已知的注解解析为 Spring 的元数据属性类。 * 如果不是事务性的,则返回 {@code null}。 *

可以重写此方法以支持携带事务元数据的自定义注解。 * @param element 带有注解的方法或类 * @return 配置的事务属性,如果找不到则返回 {@code null} */ @Nullable protected TransactionAttribute determineTransactionAttribute(AnnotatedElement element) { // 遍历所有的 TransactionAnnotationParser 实例 for (TransactionAnnotationParser parser : this.annotationParsers) { // 解析注解,获取事务属性 TransactionAttribute attr = parser.parseTransactionAnnotation(element); // 如果找到事务属性,则返回 if (attr != null) { return attr; } } // 如果未找到事务属性,则返回 null return null; } ``` ================================================ FILE: spring-transaction/spring-transaction-transactionAttributeSource/pom.xml ================================================ com.xcs.spring spring-transaction 0.0.1-SNAPSHOT 4.0.0 spring-transaction-transactionAttributeSource ================================================ FILE: spring-transaction/spring-transaction-transactionAttributeSource/src/main/java/com/xcs/spring/ScoresService.java ================================================ package com.xcs.spring; public interface ScoresService { void insertScore(); } ================================================ FILE: spring-transaction/spring-transaction-transactionAttributeSource/src/main/java/com/xcs/spring/ScoresServiceImpl.java ================================================ package com.xcs.spring; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Transactional; public class ScoresServiceImpl implements ScoresService { @Override @Transactional( readOnly = true, rollbackFor = Exception.class, isolation = Isolation.REPEATABLE_READ, timeout = 30 ) public void insertScore() { // TODO } } ================================================ FILE: spring-transaction/spring-transaction-transactionAttributeSource/src/main/java/com/xcs/spring/TransactionAttributeSourceDemo.java ================================================ package com.xcs.spring; import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource; import org.springframework.transaction.interceptor.TransactionAttribute; import org.springframework.transaction.interceptor.TransactionAttributeSource; import java.lang.reflect.Method; public class TransactionAttributeSourceDemo { public static void main(String[] args) throws NoSuchMethodException { // 获取 ScoresServiceImpl 类中的 insertScore 方法 Method insertScoreMethod = ScoresServiceImpl.class.getMethod("insertScore"); // 创建一个基于注解的事务属性源对象 TransactionAttributeSource transactionAttributeSource = new AnnotationTransactionAttributeSource(); // 解析 insertScore 方法的事务属性 TransactionAttribute transactionAttribute = transactionAttributeSource.getTransactionAttribute(insertScoreMethod, ScoresServiceImpl.class); // 输出事务属性 System.out.println(transactionAttribute); } } ================================================ FILE: spring-transaction/spring-transaction-transactionDefinition/README.md ================================================ ## TransactionDefinition - [TransactionDefinition](#transactiondefinition) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、接口源码](#四接口源码) - [五、主要实现](#五主要实现) - [六、类关系图](#六类关系图) - [七、最佳实践](#七最佳实践) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https//juejin.cn/user/4251135018533068/posts) 📚 **源码地址 ** - [github](https//github.com/xuchengsheng/spring-reading) ### 二、基本描述 `TransactionDefinition` 接口是 Spring 框架中用于定义事务属性的接口,它包含了事务的传播行为、隔离级别、超时时间和只读状态等属性,可以通过配置这些属性来灵活控制应用程序中的数据库事务行为。 ### 三、主要功能 1. **定义事务传播行为** + 定义了事务的传播方式,即当一个方法被调用时,它应该如何处理现有的事务。例如,是加入已有的事务还是创建一个新的事务。 2. **指定事务隔离级别** + 定义了事务的隔离级别,即事务操作之间的隔离程度。不同的隔离级别可以解决不同的并发问题,如脏读、不可重复读和幻读等。 3. **设置事务超时时间** + 指定了事务的超时时间,即事务在多长时间内必须完成。如果事务在指定的时间内没有完成,则会被自动回滚。 4. **配置事务只读属性** + 指定了事务是否只读。只读事务可以优化数据库性能,因为数据库可以跳过一些读取锁的操作。 ### 四、接口源码 `TransactionDefinition`接口,用于定义符合 Spring 规范的事务属性。它基于与 EJB CMT 属性类似的传播行为定义。该接口包括事务传播行为、隔离级别、超时设置和只读标志等属性。它提供了默认值和静态构建器方法,以方便创建事务定义对象。 ```java /** * 接口定义了符合 Spring 规范的事务属性。 * 基于与 EJB CMT 属性类似的传播行为定义。 * *

注意,隔离级别和超时设置仅在实际启动新事务时才会应用。 * 由于只有 {@link #PROPAGATION_REQUIRED}、{@link #PROPAGATION_REQUIRES_NEW} 和 {@link #PROPAGATION_NESTED} 可能会引起这种情况, * 因此在其他情况下指定这些设置通常是没有意义的。 * 此外,注意并非所有的事务管理器都支持这些高级功能,因此在给定非默认值时可能会抛出相应的异常。 * *

{@link #isReadOnly() 只读标志} 适用于任何事务上下文,无论是由实际资源事务支持还是在资源级别非事务性地操作。 * 在后一种情况下,该标志仅适用于应用程序中的受管资源,例如 Hibernate 的 {@code Session}。 * * @author Juergen Hoeller * @since 08.05.2003 * @see PlatformTransactionManager#getTransaction(TransactionDefinition) * @see org.springframework.transaction.support.DefaultTransactionDefinition * @see org.springframework.transaction.interceptor.TransactionAttribute */ public interface TransactionDefinition { /** * 支持当前事务;如果不存在则创建一个新事务。 * 类似于具有相同名称的 EJB 事务属性。 *

这通常是事务定义的默认设置, * 通常定义了事务同步范围。 */ int PROPAGATION_REQUIRED = 0; /** * 支持当前事务;如果不存在则以非事务方式执行。 * 类似于具有相同名称的 EJB 事务属性。 *

注意:对于具有事务同步的事务管理器, * {@code PROPAGATION_SUPPORTS} 与没有事务稍有不同, * 因为它定义了可能应用于的事务范围同步。 * 因此,同一资源(JDBC {@code Connection}、Hibernate {@code Session} 等)将用于整个指定的范围。 * 注意,确切的行为取决于事务管理器的实际同步配置! *

通常情况下,谨慎使用 {@code PROPAGATION_SUPPORTS}! * 特别是,在 {@code PROPAGATION_SUPPORTS} 范围内不要依赖 {@code PROPAGATION_REQUIRED} 或 {@code PROPAGATION_REQUIRES_NEW} * (这可能会导致运行时同步冲突)。 * 如果无法避免此类嵌套,请确保适当配置事务管理器(通常切换到“在实际事务上同步”)。 * @see org.springframework.transaction.support.AbstractPlatformTransactionManager#setTransactionSynchronization * @see org.springframework.transaction.support.AbstractPlatformTransactionManager#SYNCHRONIZATION_ON_ACTUAL_TRANSACTION */ int PROPAGATION_SUPPORTS = 1; /** * 支持当前事务;如果不存在则抛出异常。 * 类似于具有相同名称的 EJB 事务属性。 *

在 {@code PROPAGATION_MANDATORY} 范围内的事务同步始终由周围的事务驱动。 */ int PROPAGATION_MANDATORY = 2; /** * 创建一个新事务,如果存在则挂起当前事务。 * 类似于具有相同名称的 EJB 事务属性。 *

注意:实际的事务挂起不会在所有事务管理器上自动工作。 * 这尤其适用于 {@link org.springframework.transaction.jta.JtaTransactionManager}, * 它要求将 {@code javax.transaction.TransactionManager} 提供给它(在标准 Java EE 中是特定于服务器的)。 *

{@code PROPAGATION_REQUIRES_NEW} 范围总是定义自己的事务同步。 * 现有的同步将被暂停和恢复。 * @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager */ int PROPAGATION_REQUIRES_NEW = 3; /** * 不支持当前事务;而总是以非事务方式执行。 * 类似于具有相同名称的 EJB 事务属性。 *

注意:实际的事务挂起不会在所有事务管理器上自动工作。 * 这尤其适用于 {@link org.springframework.transaction.jta.JtaTransactionManager}, * 它要求将 {@code javax.transaction.TransactionManager} 提供给它(在标准 Java EE 中是特定于服务器的)。 *

请注意,在 {@code PROPAGATION_NOT_SUPPORTED} 范围内不可用事务同步。 * 现有同步将被暂停和恢复。 * @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager */ int PROPAGATION_NOT_SUPPORTED = 4; /** * 不支持当前事务;如果存在则抛出异常。 * 类似于具有相同名称的 EJB 事务属性。 *

请注意,在 {@code PROPAGATION_NEVER} 范围内不可用事务同步。 */ int PROPAGATION_NEVER = 5; /** * 在存在当前事务时执行嵌套事务,否则与 {@link #PROPAGATION_REQUIRED} 行为相同。 * 在 EJB 中没有类似的功能。 *

注意:实际创建嵌套事务只能在特定的事务管理器上工作。 * 默认情况下,这仅适用于 JDBC {@link org.springframework.jdbc.datasource.DataSourceTransactionManager} * 在使用 JDBC 3.0 驱动程序时。 * 一些 JTA 提供程序可能也支持嵌套事务。 * @see org.springframework.jdbc.datasource.DataSourceTransactionManager */ int PROPAGATION_NESTED = 6; /** * 使用底层数据存储的默认隔离级别。 * 所有其他级别都对应于 JDBC 隔离级别。 * @see java.sql.Connection */ int ISOLATION_DEFAULT = -1; /** * 表示允许发生脏读、不可重复读和幻读。 *

该级别允许一个事务修改的行在另一个事务提交之前被另一个事务读取(“脏读”)。 * 如果其中任何更改被回滚,第二个事务将检索到无效的行。 * @see java.sql.Connection#TRANSACTION_READ_UNCOMMITTED */ int ISOLATION_READ_UNCOMMITTED = 1; /** * 表示防止脏读;可以发生不可重复读和幻读。 *

该级别仅禁止事务读取一个包含未提交更改的行。 * @see java.sql.Connection#TRANSACTION_READ_COMMITTED */ int ISOLATION_READ_COMMITTED = 2; /** * 表示防止脏读和不可重复读;可以发生幻读。 *

该级别禁止事务读取一个包含未提交更改的行,同时禁止以下情况的发生: * 一个事务读取一行,第二个事务修改该行,第一个事务重新读取该行,第二次得到的值与第一次不同(“不可重复读”)。 * @see java.sql.Connection#TRANSACTION_REPEATABLE_READ */ int ISOLATION_REPEATABLE_READ = 4; /** * 表示防止脏读、不可重复读和幻读。 *

该级别包括 {@link #ISOLATION_REPEATABLE_READ} 中的禁止,同时进一步禁止以下情况的发生: * 一个事务读取满足 {@code WHERE} 条件的所有行,第二个事务插入满足该 {@code WHERE} 条件的行, * 第一个事务为相同的条件重新读取,第二次读取中检索到额外的“幻行”。 * @see java.sql.Connection#TRANSACTION_SERIALIZABLE */ int ISOLATION_SERIALIZABLE = 8; /** * 使用底层事务系统的默认超时时间,如果不支持超时,则为无。 */ int TIMEOUT_DEFAULT = -1; /** * 返回传播行为。 *

必须返回 {@link TransactionDefinition} 接口上定义的 {@code PROPAGATION_XXX} 常量之一。 *

默认值为 {@link #PROPAGATION_REQUIRED}。 * @return 传播行为 * @see #PROPAGATION_REQUIRED * @see org.springframework.transaction.support.TransactionSynchronizationManager#isActualTransactionActive() */ default int getPropagationBehavior() { return PROPAGATION_REQUIRED; } /** * 返回隔离级别。 *

必须返回 {@link TransactionDefinition} 接口上定义的 {@code ISOLATION_XXX} 常量之一。 * 这些常量设计用于与 {@link java.sql.Connection} 上的相同常量的值匹配。 *

专门用于与 {@link #PROPAGATION_REQUIRED} 或 {@link #PROPAGATION_REQUIRES_NEW} 一起使用, * 因为它仅适用于新启动的事务。 * 如果我们希望在参与具有不同隔离级别的现有事务时拒绝隔离级别声明,请考虑在事务管理器上切换 "validateExistingTransactions" 标志为 "true"。 *

默认值为 {@link #ISOLATION_DEFAULT}。 * 请注意,不支持自定义隔离级别的事务管理器将在给定除 {@link #ISOLATION_DEFAULT} 之外的任何级别时抛出异常。 * @return 隔离级别 * @see #ISOLATION_DEFAULT * @see org.springframework.transaction.support.AbstractPlatformTransactionManager#setValidateExistingTransaction */ default int getIsolationLevel() { return ISOLATION_DEFAULT; } /** * 返回事务超时时间。 *

必须返回以秒为单位的数字,或者 {@link #TIMEOUT_DEFAULT}。 *

专门用于与 {@link #PROPAGATION_REQUIRED} 或 {@link #PROPAGATION_REQUIRES_NEW} 一起使用, * 因为它仅适用于新启动的事务。 *

请注意,不支持超时的事务管理器将在给定除 {@link #TIMEOUT_DEFAULT} 之外的任何超时时抛出异常。 *

默认值为 {@link #TIMEOUT_DEFAULT}。 * @return 事务超时时间 */ default int getTimeout() { return TIMEOUT_DEFAULT; } /** * 返回是否优化为只读事务。 *

只读标志适用于任何事务上下文,无论是由实际资源事务({@link #PROPAGATION_REQUIRED}/ * {@link #PROPAGATION_REQUIRES_NEW})支持,还是在资源级别非事务性地操作({@link #PROPAGATION_SUPPORTS})。 * 在后一种情况下,该标志仅适用于应用程序中的受管资源,例如 Hibernate 的 {@code Session}。 *

这只是对实际事务子系统的提示; * 它不一定会导致写入访问尝试失败。 * 不能解释只读提示的事务管理器在要求只读事务时不会抛出异常。 * @return 如果事务被优化为只读,则为 {@code true}(默认为 {@code false}) * @see org.springframework.transaction.support.TransactionSynchronization#beforeCommit(boolean) * @see org.springframework.transaction.support.TransactionSynchronizationManager#isCurrentTransactionReadOnly() */ default boolean isReadOnly() { return false; } /** * 返回此事务的名称。可以为 {@code null}。 *

如果适用(例如,WebLogic),则将用作要显示在事务监视器中的事务名称。 *

在 Spring 的声明式事务中,暴露的名称将是 {@code fully-qualified class name + "." + method name}(默认)。 * @return 此事务的名称(默认为 {@code null}) * @see org.springframework.transaction.interceptor.TransactionAspectSupport * @see org.springframework.transaction.support.TransactionSynchronizationManager#getCurrentTransactionName() */ @Nullable default String getName() { return null; } /** * 返回具有默认值的不可修改的 {@code TransactionDefinition}。 *

为了定制目的,可以使用可修改的 {@link org.springframework.transaction.support.DefaultTransactionDefinition}。 * @since 5.2 */ static TransactionDefinition withDefaults() { return StaticTransactionDefinition.INSTANCE; } } ``` ### 五、主要实现 1. **DefaultTransactionDefinition** - Spring 框架中定义事务基本属性的默认实现类。它允许我们指定事务的传播行为、隔离级别、超时设置和只读标志等。通过提供默认值,它简化了事务属性的设置,并提供了静态方法 `withDefaults()` ,可用于创建具有默认属性的事务定义对象。 2. **DefaultTransactionAttribute** - Spring 框架中定义事务属性的默认实现类。它封装了事务的传播行为、隔离级别、超时设置和只读标志等属性,并提供了操作方法和属性获取方法。作为 `TransactionAttribute` 接口的默认实现,它可以轻松设置和获取方法或类级别的事务属性。 ### 六、类关系图 ~~~mermaid classDiagram direction BT class DefaultTransactionAttribute class DefaultTransactionDefinition class TransactionAttribute { <> } class TransactionDefinition { <> } DefaultTransactionAttribute --> DefaultTransactionDefinition DefaultTransactionAttribute ..> TransactionAttribute DefaultTransactionDefinition ..> TransactionDefinition TransactionAttribute --> TransactionDefinition ~~~ ### 七、最佳实践 使用`DefaultTransactionDefinition`类来定义事务属性,并设置传播行为、隔离级别、事务超时时间、是否只读和事务名称等属性,然后打印出它们的值。 ```java public class TransactionDefinitionDemo { public static void main(String[] args) { // 创建一个 DefaultTransactionDefinition 实例 DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition(); // 设置传播行为为PROPAGATION_REQUIRES_NEW transactionDefinition.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_REQUIRES_NEW); System.out.println("Propagation Behavior: " + transactionDefinition.getPropagationBehavior()); // 设置隔离级别为ISOLATION_REPEATABLE_READ transactionDefinition.setIsolationLevel(DefaultTransactionDefinition.ISOLATION_REPEATABLE_READ); System.out.println("Isolation Level: " + transactionDefinition.getIsolationLevel()); // 设置事务超时时间为30秒 transactionDefinition.setTimeout(30); System.out.println("Timeout: " + transactionDefinition.getTimeout()); // 设置事务为只读 transactionDefinition.setReadOnly(true); System.out.println("Is Read Only: " + transactionDefinition.isReadOnly()); // 设置事务名称为"DemoTransaction" transactionDefinition.setName("DemoTransaction"); System.out.println("Transaction Name: " + transactionDefinition.getName()); } } ``` 运行结果,使用 `DefaultTransactionDefinition` 类设置的事务属性:传播行为为 `PROPAGATION_REQUIRES_NEW` ,隔离级别为 `ISOLATION_REPEATABLE_READ`,超时时间为30秒,事务被设置为只读,并且事务名称为 "DemoTransaction"。 ```java Propagation Behavior:3 Isolation Level:4 Timeout:30 Is Read Only:true Transaction Name:DemoTransaction ``` ================================================ FILE: spring-transaction/spring-transaction-transactionDefinition/pom.xml ================================================ com.xcs.spring spring-transaction 0.0.1-SNAPSHOT 4.0.0 spring-transaction-transactionDefinition ================================================ FILE: spring-transaction/spring-transaction-transactionDefinition/src/main/java/com/xcs/spring/TransactionDefinitionDemo.java ================================================ package com.xcs.spring; import org.springframework.transaction.support.DefaultTransactionDefinition; public class TransactionDefinitionDemo { public static void main(String[] args) { // 创建一个 DefaultTransactionDefinition 实例 DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition(); // 设置传播行为为PROPAGATION_REQUIRES_NEW transactionDefinition.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_REQUIRES_NEW); System.out.println("Propagation Behavior: " + transactionDefinition.getPropagationBehavior()); // 设置隔离级别为ISOLATION_REPEATABLE_READ transactionDefinition.setIsolationLevel(DefaultTransactionDefinition.ISOLATION_REPEATABLE_READ); System.out.println("Isolation Level: " + transactionDefinition.getIsolationLevel()); // 设置事务超时时间为30秒 transactionDefinition.setTimeout(30); System.out.println("Timeout: " + transactionDefinition.getTimeout()); // 设置事务为只读 transactionDefinition.setReadOnly(true); System.out.println("Is Read Only: " + transactionDefinition.isReadOnly()); // 设置事务名称为"DemoTransaction" transactionDefinition.setName("DemoTransaction"); System.out.println("Transaction Name: " + transactionDefinition.getName()); } } ================================================ FILE: spring-transaction/spring-transaction-transactionInterceptor/README.md ================================================ ## TransactionInterceptor - [TransactionInterceptor](#transactioninterceptor) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、最佳实践](#四最佳实践) - [五、源码分析](#五源码分析) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址 ** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 `TransactionInterceptor` 类是 Spring 框架中的一个核心组件,用于实现声明式事务管理。它通过拦截方法调用,根据事务属性(如传播行为、隔离级别等)来控制事务的开始、提交和回滚,确保在方法执行过程中事务的一致性和完整性。 ### 三、主要功能 1. **获取事务属性** + 从方法或类的元数据中获取事务属性(如传播行为、隔离级别等)。 2. **事务管理器决策** + 根据事务属性和当前的事务上下文(如是否存在活动事务)来决定是创建一个新事务、加入现有事务还是不需要事务。 3. **事务控制** + 通过 `TransactionManager` 来控制事务的开始、提交和回滚。 4. **异常处理** + 在方法执行过程中,如果捕获到异常,根据事务属性配置来决定是否回滚事务。 ### 四、最佳实践 通过 `SimpleDriverDataSource` 创建了数据库连接池,然后使用 `DataSourceTransactionManager` 进行事务管理。通过 `JdbcTemplate` 执行 SQL 语句,并使用 `AnnotationTransactionAttributeSource` 和 `TransactionInterceptor` 来定义事务的属性和拦截方法调用,以确保方法在事务中执行。最后,通过 `ProxyFactory` 创建代理对象,并调用代理对象的方法,使方法的执行受到声明式事务的控制。 ```java public class TransactionInterceptorDemo { public static void main(String[] args) throws SQLException { // 数据库连接 URL,格式为 jdbc:数据库驱动名称://主机地址:端口号/数据库名称 String url = "jdbc:mysql://localhost:3306/spring-reading"; // 数据库用户名 String username = "root"; // 数据库密码 String password = "123456"; // 创建 SimpleDriverDataSource 对象,用于管理数据源 SimpleDriverDataSource dataSource = new SimpleDriverDataSource(new Driver(), url, username, password); // 创建 DataSourceTransactionManager 对象,用于管理事务 DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource); // 创建 JdbcTemplate 对象,用于执行 SQL 语句 JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); // 创建 AnnotationTransactionAttributeSource 对象,用于获取事务属性 AnnotationTransactionAttributeSource transactionAttributeSource = new AnnotationTransactionAttributeSource(); // 创建 TransactionInterceptor 对象,用于拦截方法调用并管理事务 TransactionInterceptor transactionInterceptor = new TransactionInterceptor(); transactionInterceptor.setTransactionAttributeSource(transactionAttributeSource); transactionInterceptor.setTransactionManager(transactionManager); // 创建 BeanFactoryTransactionAttributeSourceAdvisor 对象,用于配置事务拦截器 BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor(); advisor.setTransactionAttributeSource(transactionAttributeSource); advisor.setAdvice(transactionInterceptor); // 创建 ProxyFactory 对象,用于创建代理对象 ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.addAdvisor(advisor); proxyFactory.setTarget(new ScoresServiceImpl(jdbcTemplate)); // 获取代理对象,并调用其方法 ScoresService scoresService = (ScoresService) proxyFactory.getProxy(); scoresService.insertScore(); } } ``` ### 五、源码分析 在`org.springframework.transaction.interceptor.TransactionInterceptor#invoke`方法中, `TransactionInterceptor` 类中实现了`MethodInterceptor`的 `invoke` 方法,它是 Spring AOP 在事务管理方面的核心实现。通过拦截方法调用并根据事务配置信息,确保在方法执行前开启事务、方法执行后根据结果提交或回滚事务,从而保证数据操作的一致性和完整性。 ```java @Override @Nullable public Object invoke(MethodInvocation invocation) throws Throwable { // 确定目标类:可能为 {@code null}。 // TransactionAttributeSource 应该传递目标类和方法,方法可能来自接口。 Class targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null); // 适配到 TransactionAspectSupport 的 invokeWithinTransaction 方法... return invokeWithinTransaction(invocation.getMethod(), targetClass, new CoroutinesInvocationCallback() { @Override @Nullable public Object proceedWithInvocation() throws Throwable { return invocation.proceed(); } @Override public Object getTarget() { return invocation.getThis(); } @Override public Object[] getArguments() { return invocation.getArguments(); } }); } ``` 在`org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction` 方法中,根据方法的事务属性确定是否需要事务管理,然后选择合适的事务管理器执行事务操作,包括事务的开始、提交和回滚。如果方法执行过程中抛出异常,则根据事务状态进行事务回滚,并重新抛出异常;在方法正常返回时根据配置的回滚规则设置回滚标志,并提交事务。 ```java /** * 在基于环绕通知的子类中的通用委托,委托给该类上的几个其他模板方法。 * 能够处理 {@link CallbackPreferringPlatformTransactionManager}、常规 {@link PlatformTransactionManager} 实现, * 以及用于响应式返回类型的 {@link ReactiveTransactionManager} 实现。 * * @param method 被调用的方法 * @param targetClass 我们正在调用方法的目标类 * @param invocation 用于进行目标调用的回调 * @return 方法的返回值(如果有) * @throws Throwable 从目标调用中传播的异常 */ @Nullable protected Object invokeWithinTransaction(Method method, @Nullable Class targetClass, final InvocationCallback invocation) throws Throwable { // 如果事务属性为 null,则方法是非事务性的。 TransactionAttributeSource tas = getTransactionAttributeSource(); final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null); final TransactionManager tm = determineTransactionManager(txAttr); // 如果存在响应式适配器并且 tm 是 ReactiveTransactionManager 类型,则... if (this.reactiveAdapterRegistry != null && tm instanceof ReactiveTransactionManager) { // ... [代码部分省略以简化] } // 将 tm 转换为 PlatformTransactionManager PlatformTransactionManager ptm = asPlatformTransactionManager(tm); final String joinpointIdentification = methodIdentification(method, targetClass, txAttr); // 如果 txAttr 为 null 或者 ptm 不是 CallbackPreferringPlatformTransactionManager 类型 if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) { // 使用 getTransaction 和 commit/rollback 调用进行标准事务划分。 TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification); Object retVal; try { // 这是一个环绕通知:调用链中的下一个拦截器。 // 这通常会导致目标对象被调用。 retVal = invocation.proceedWithInvocation(); } catch (Throwable ex) { // 目标调用异常 completeTransactionAfterThrowing(txInfo, ex); throw ex; } finally { cleanupTransactionInfo(txInfo); } // 如果 retVal 不为 null 且存在 vavrPresent 并且 retVal 是 Vavr Try 类型 if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) { // 根据回滚规则在 Vavr 失败时设置仅回滚... TransactionStatus status = txInfo.getTransactionStatus(); if (status != null && txAttr != null) { retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status); } } // 在方法正常返回后提交事务 commitTransactionAfterReturning(txInfo); return retVal; } else { // ... [代码部分省略以简化] } } ``` 在`org.springframework.transaction.interceptor.TransactionAspectSupport#createTransactionIfNecessary` 方法中,如果事务属性存在且未指定事务名称,则使用方法标识作为事务名称。然后,如果事务属性和事务管理器都存在,则通过事务管理器获取事务状态;如果没有配置事务管理器,则记录调试日志表示跳过该事务连接点。最后,通过调用 `prepareTransactionInfo` 方法,准备并返回包含事务信息的 `TransactionInfo` 对象,无论是否创建了事务都返回该对象。 ```java /** * 根据给定的 TransactionAttribute 创建一个事务(如果有必要)。 *

允许调用者通过 TransactionAttributeSource 执行自定义的 TransactionAttribute 查找。 * * @param tm 事务管理器(可能为 {@code null}) * @param txAttr 事务属性(可能为 {@code null}) * @param joinpointIdentification 完全限定的方法名(用于监控和日志记录) * @return 一个 TransactionInfo 对象,不论是否创建了事务。 * 可以使用 TransactionInfo 的 {@code hasTransaction()} 方法来判断是否创建了事务。 * @see #getTransactionAttributeSource() */ @SuppressWarnings("serial") protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm, @Nullable TransactionAttribute txAttr, final String joinpointIdentification) { // 如果事务属性不为 null 且事务名称为 null,则使用方法标识作为事务名称。 if (txAttr != null && txAttr.getName() == null) { txAttr = new DelegatingTransactionAttribute(txAttr) { @Override public String getName() { return joinpointIdentification; } }; } TransactionStatus status = null; if (txAttr != null) { if (tm != null) { // 如果事务属性不为 null 且事务管理器不为 null,则通过事务管理器获取事务状态。 status = tm.getTransaction(txAttr); } else { // 如果事务管理器为 null,记录调试日志表示跳过事务连接点。 if (logger.isDebugEnabled()) { logger.debug("Skipping transactional joinpoint [" + joinpointIdentification + "] because no transaction manager has been configured"); } } } // 准备并返回 TransactionInfo 对象。 return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status); } ``` 在`org.springframework.transaction.interceptor.TransactionAspectSupport#prepareTransactionInfo` 方法中,通过给定的事务属性和状态对象创建并准备一个 `TransactionInfo` 对象。如果事务属性不为 null,表示该方法需要事务,并在调试日志中记录获取事务的信息,并通过 `newTransactionStatus` 方法设置事务状态;如果事务属性为 null,表示该方法不需要事务,仅创建 `TransactionInfo` 对象以维护线程局部变量的完整性。无论是否创建新事务,总是将 `TransactionInfo` 绑定到线程,以确保 `TransactionInfo` 堆栈被正确管理。 ```java /** * 为给定的事务属性和状态对象准备一个 TransactionInfo。 * @param tm 事务管理器(可能为 {@code null}) * @param txAttr 事务属性(可能为 {@code null}) * @param joinpointIdentification 完全限定的方法名(用于监控和日志记录) * @param status 当前事务的 TransactionStatus * @return 准备好的 TransactionInfo 对象 */ protected TransactionInfo prepareTransactionInfo(@Nullable PlatformTransactionManager tm, @Nullable TransactionAttribute txAttr, String joinpointIdentification, @Nullable TransactionStatus status) { // 创建一个 TransactionInfo 对象 TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification); if (txAttr != null) { // 如果事务属性不为 null,表示该方法需要事务 if (logger.isTraceEnabled()) { logger.trace("Getting transaction for [" + txInfo.getJoinpointIdentification() + "]"); } // 事务管理器会在已经存在不兼容事务时标记错误 txInfo.newTransactionStatus(status); } else { // 如果事务属性为 null,表示该方法不需要事务,仅创建 TransactionInfo 以维护线程局部变量的完整性 if (logger.isTraceEnabled()) { logger.trace("No need to create transaction for [" + joinpointIdentification + "]: This method is not transactional."); } } // 我们总是将 TransactionInfo 绑定到线程,即使我们没有在这里创建新事务。 // 这保证了即使没有通过这个切面创建事务,TransactionInfo 堆栈也会被正确管理。 txInfo.bindToThread(); return txInfo; } ``` 在`org.springframework.transaction.interceptor.TransactionAspectSupport.TransactionInfo#bindToThread` 方法中,将当前的 `TransactionInfo` 对象绑定到线程上下文中。在绑定之前,会保存当前线程上的旧的 `TransactionInfo` 对象,以便在事务完成后恢复之前保存的旧的 `TransactionInfo` 对象。 ```java /** * 将当前的 TransactionInfo 对象绑定到线程上下文中。 * 在绑定之前会保存当前线程上的旧的 TransactionInfo 对象, * 在事务完成后会恢复之前保存的旧的 TransactionInfo 对象。 */ private void bindToThread() { // 暴露当前的 TransactionStatus,并在事务完成后恢复任何现有的 TransactionStatus。 this.oldTransactionInfo = transactionInfoHolder.get(); transactionInfoHolder.set(this); } ``` 在`org.springframework.transaction.interceptor.TransactionAspectSupport#commitTransactionAfterReturning` 方法中,通过 `txInfo` 参数获取当前事务的信息,包括事务状态和事务管理器,并调用事务管理器的 `commit` 方法来提交事务。 ```java /** * 在方法成功完成调用后执行,但不会在处理异常后执行。 * 如果没有创建事务,则什么也不做。 * * @param txInfo 当前事务的信息 */ protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo) { // 如果 txInfo 不为 null 且事务状态不为 null,则执行事务提交 if (txInfo != null && txInfo.getTransactionStatus() != null) { // 如果启用了跟踪日志,则记录完成事务的信息 if (logger.isTraceEnabled()) { logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]"); } // 调用事务管理器的 commit 方法来提交事务 txInfo.getTransactionManager().commit(txInfo.getTransactionStatus()); } } ``` 在`org.springframework.transaction.interceptor.TransactionAspectSupport#completeTransactionAfterThrowing` 方法中,根据事务属性的配置完成事务的提交或回滚操作。如果事务属性要求在异常时回滚事务,则会调用事务管理器的回滚方法;否则会调用事务管理器的提交方法。 ```java /** * 处理可抛出的异常,完成事务。 * 根据配置可能会提交或回滚事务。 * * @param txInfo 当前事务的信息 * @param ex 遇到的可抛出异常 */ protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) { // 如果 txInfo 不为 null 且事务状态不为 null,则执行事务完成操作 if (txInfo != null && txInfo.getTransactionStatus() != null) { // 如果启用了跟踪日志,则记录在异常后完成事务的信息和异常信息 if (logger.isTraceEnabled()) { logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "] after exception: " + ex); } // 如果事务属性不为 null,并且根据事务属性需要在异常时回滚事务 if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) { try { // 使用事务管理器回滚事务 txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus()); } catch (TransactionSystemException ex2) { // 如果回滚过程中发生异常,则记录错误信息并抛出异常 logger.error("Application exception overridden by rollback exception", ex); ex2.initApplicationException(ex); throw ex2; } catch (RuntimeException | Error ex2) { // 如果回滚过程中发生运行时异常或错误,则记录错误信息并抛出异常 logger.error("Application exception overridden by rollback exception", ex); throw ex2; } } else { // 如果不需要在异常时回滚事务,则继续提交事务 // 如果事务状态为 RollbackOnly,则最终仍然会回滚事务 try { txInfo.getTransactionManager().commit(txInfo.getTransactionStatus()); } catch (TransactionSystemException ex2) { // 如果提交过程中发生异常,则记录错误信息并抛出异常 logger.error("Application exception overridden by commit exception", ex); ex2.initApplicationException(ex); throw ex2; } catch (RuntimeException | Error ex2) { // 如果提交过程中发生运行时异常或错误,则记录错误信息并抛出异常 logger.error("Application exception overridden by commit exception", ex); throw ex2; } } } } ``` ================================================ FILE: spring-transaction/spring-transaction-transactionInterceptor/pom.xml ================================================ com.xcs.spring spring-transaction 0.0.1-SNAPSHOT 4.0.0 spring-transaction-transactionInterceptor ================================================ FILE: spring-transaction/spring-transaction-transactionInterceptor/src/main/java/com/xcs/spring/ScoresService.java ================================================ package com.xcs.spring; public interface ScoresService { void insertScore() throws Exception; } ================================================ FILE: spring-transaction/spring-transaction-transactionInterceptor/src/main/java/com/xcs/spring/ScoresServiceImpl.java ================================================ package com.xcs.spring; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.transaction.annotation.Transactional; import java.util.Random; public class ScoresServiceImpl implements ScoresService { private JdbcTemplate jdbcTemplate; public ScoresServiceImpl(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @Override @Transactional public void insertScore() throws Exception { // 主键Id long id = System.currentTimeMillis(); // 分数 int score = new Random().nextInt(100); // 保存数据 int row = jdbcTemplate.update("insert into scores(id,score) values(?,?)", id, score); // 打印影响行数 System.out.println("scores row = " + row); // 模拟异常 // throw new Exception("测试异常"); } } ================================================ FILE: spring-transaction/spring-transaction-transactionInterceptor/src/main/java/com/xcs/spring/TransactionInterceptorDemo.java ================================================ package com.xcs.spring; import com.mysql.jdbc.Driver; import org.springframework.aop.framework.ProxyFactory; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.jdbc.datasource.SimpleDriverDataSource; import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource; import org.springframework.transaction.interceptor.BeanFactoryTransactionAttributeSourceAdvisor; import org.springframework.transaction.interceptor.TransactionInterceptor; public class TransactionInterceptorDemo { public static void main(String[] args) throws Exception { // 数据库连接 URL,格式为 jdbc:数据库驱动名称://主机地址:端口号/数据库名称 String url = "jdbc:mysql://localhost:3306/spring-reading"; // 数据库用户名 String username = "root"; // 数据库密码 String password = "123456"; // 创建 SimpleDriverDataSource 对象,用于管理数据源 SimpleDriverDataSource dataSource = new SimpleDriverDataSource(new Driver(), url, username, password); // 创建 DataSourceTransactionManager 对象,用于管理事务 DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource); // 创建 JdbcTemplate 对象,用于执行 SQL 语句 JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); // 创建 AnnotationTransactionAttributeSource 对象,用于获取事务属性 AnnotationTransactionAttributeSource transactionAttributeSource = new AnnotationTransactionAttributeSource(); // 创建 TransactionInterceptor 对象,用于拦截方法调用并管理事务 TransactionInterceptor transactionInterceptor = new TransactionInterceptor(); transactionInterceptor.setTransactionAttributeSource(transactionAttributeSource); transactionInterceptor.setTransactionManager(transactionManager); // 创建 BeanFactoryTransactionAttributeSourceAdvisor 对象,用于配置事务拦截器 BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor(); advisor.setTransactionAttributeSource(transactionAttributeSource); advisor.setAdvice(transactionInterceptor); // 创建 ProxyFactory 对象,用于创建代理对象 ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.addAdvisor(advisor); proxyFactory.setTarget(new ScoresServiceImpl(jdbcTemplate)); // 获取代理对象,并调用其方法 ScoresService scoresService = (ScoresService) proxyFactory.getProxy(); scoresService.insertScore(); } } ================================================ FILE: spring-transaction/spring-transaction-transactionTemplate/README.md ================================================ ## TransactionTemplate - [TransactionTemplate](#transactiontemplate) - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - [四、最佳实践](#四最佳实践) - [五、源码分析](#五源码分析) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址 ** - [github](https://github.com/xuchengsheng/spring-reading) ### 二、基本描述 `TransactionTemplate` 是 Spring Framework 提供的工具类,用于在代码中以编程方式管理事务。它简化了事务的启动、提交/回滚以及异常处理,同时允许灵活配置事务属性,并提供了回调机制以执行特定操作。 ### 三、主要功能 1. **事务的启动和提交/回滚** + 允许我们以编程方式启动事务,并在需要时提交或回滚事务。这种方式使得我们可以在代码的特定部分明确定义事务的边界,而不必依赖于容器管理。 2. **异常处理** + 提供了对异常的处理机制,我们可以通过配置指定在发生异常时应该执行的操作,比如回滚事务。 3. **事务属性的灵活配置** + 我们可以使用 `TransactionTemplate` 配置各种事务属性,如隔离级别、传播行为等。这使得我们可以针对不同的场景灵活地配置事务行为。 4. **回调机制** + 允许我们定义回调接口,通过这些回调接口,我们可以在事务的不同阶段执行特定的操作。这为更复杂的事务场景提供了更大的灵活性。 ### 四、最佳实践 使用 `TransactionTemplate` 来管理事务。它首先创建了一个数据库连接,并通过 `DataSourceTransactionManager` 实例化了 `TransactionTemplate`。在 `TransactionTemplate` 的 `execute` 方法中,定义了一个事务回调接口,在该接口的 `doInTransaction` 方法中执行了数据库操作。通过这种方式,可以确保操作要么全部成功提交,要么全部回滚,从而保证数据的一致性和完整性。 ```java public class TransactionTemplateDemo { public static void main(String[] args) throws SQLException { // 数据库连接 URL,格式为 jdbc:数据库驱动名称://主机地址:端口号/数据库名称 String url = "jdbc:mysql://localhost:3306/spring-reading"; // 数据库用户名 String username = "root"; // 数据库密码 String password = "123456"; // 创建 SimpleDriverDataSource 对象,用于管理数据源 SimpleDriverDataSource dataSource = new SimpleDriverDataSource(new Driver(), url, username, password); // 创建 DataSourceTransactionManager 对象,用于管理事务 DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource); // 创建 JdbcTemplate 对象,用于执行 SQL 语句 JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); // 创建事务模板 TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager); Boolean insertSuccess = transactionTemplate.execute(new TransactionCallback() { @Override public Boolean doInTransaction(TransactionStatus status) { // 主键Id long id = System.currentTimeMillis(); // 分数 int score = new Random().nextInt(100); // 保存数据 int row = jdbcTemplate.update("insert into scores(id,score) values(?,?)", id, score); // 模拟异常,用于测试事务回滚 // int i = 1 / 0; // 我们也可以使用setRollbackOnly来回滚 // status.setRollbackOnly(); // 返回是否新增成功 return row >= 1; } }); System.out.println("新增scores表数据:" + insertSuccess); } } ``` 运行结果,数据库操作成功完成并成功提交了事务。 ```java 新增scores表数据:true ``` ### 五、源码分析 在`org.springframework.transaction.support.TransactionTemplate#execute`方法中,首先确保了 `PlatformTransactionManager` 已经设置,然后根据事务管理器的类型选择合适的执行方式。如果事务管理器是 `CallbackPreferringPlatformTransactionManager` 的实例,就会调用其 `execute` 方法来执行事务。否则,它将获取事务状态,执行事务回调操作,并在操作过程中处理可能的异常。最后,无论成功还是失败,都会提交事务。 ```java @Override @Nullable public T execute(TransactionCallback action) throws TransactionException { // 断言确保已设置PlatformTransactionManager Assert.state(this.transactionManager != null, "No PlatformTransactionManager set"); // 如果事务管理器是CallbackPreferringPlatformTransactionManager的实例 if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) { // 使用CallbackPreferringPlatformTransactionManager执行事务 return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action); } else { // 获取事务状态 TransactionStatus status = this.transactionManager.getTransaction(this); T result; try { // 执行事务回调操作 result = action.doInTransaction(status); } catch (RuntimeException | Error ex) { // 事务中的代码抛出应用程序异常 -> 回滚事务 rollbackOnException(status, ex); throw ex; } catch (Throwable ex) { // 事务中的代码抛出意外异常 -> 回滚事务 rollbackOnException(status, ex); throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception"); } // 提交事务 this.transactionManager.commit(status); return result; } } ``` 在`org.springframework.transaction.support.TransactionTemplate#rollbackOnException` 方法中,首先确保已设置了 `PlatformTransactionManager` ,然后记录调试日志以表示在应用异常时启动事务回滚。接着尝试执行事务回滚操作,如果发生回滚异常,则记录错误日志,并将原始异常初始化为回滚异常的应用程序异常。最后,如果发生运行时异常或错误,则将其重新抛出。 ```java /** * 在应用异常时执行回滚,正确处理回滚异常。 * @param status 表示事务的对象 * @param ex 抛出的应用程序异常或错误 * @throws TransactionException 如果发生回滚错误 */ private void rollbackOnException(TransactionStatus status, Throwable ex) throws TransactionException { // 断言确保已设置PlatformTransactionManager Assert.state(this.transactionManager != null, "No PlatformTransactionManager set"); // 打印调试日志,表示在应用异常时启动事务回滚 logger.debug("Initiating transaction rollback on application exception", ex); try { // 执行事务回滚 this.transactionManager.rollback(status); } catch (TransactionSystemException ex2) { // 打印错误日志,表示应用异常被回滚异常覆盖 logger.error("Application exception overridden by rollback exception", ex); // 初始化应用程序异常 ex2.initApplicationException(ex); throw ex2; } catch (RuntimeException | Error ex2) { // 打印错误日志,表示应用异常被回滚异常覆盖 logger.error("Application exception overridden by rollback exception", ex); throw ex2; } } ``` ================================================ FILE: spring-transaction/spring-transaction-transactionTemplate/pom.xml ================================================ com.xcs.spring spring-transaction 0.0.1-SNAPSHOT 4.0.0 spring-transaction-transactionTemplate ================================================ FILE: spring-transaction/spring-transaction-transactionTemplate/src/main/java/com/xcs/spring/TransactionTemplateDemo.java ================================================ package com.xcs.spring; import com.mysql.jdbc.Driver; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.jdbc.datasource.SimpleDriverDataSource; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionTemplate; import java.sql.SQLException; import java.util.Random; public class TransactionTemplateDemo { public static void main(String[] args) throws SQLException { // 数据库连接 URL,格式为 jdbc:数据库驱动名称://主机地址:端口号/数据库名称 String url = "jdbc:mysql://localhost:3306/spring-reading"; // 数据库用户名 String username = "root"; // 数据库密码 String password = "123456"; // 创建 SimpleDriverDataSource 对象,用于管理数据源 SimpleDriverDataSource dataSource = new SimpleDriverDataSource(new Driver(), url, username, password); // 创建 DataSourceTransactionManager 对象,用于管理事务 DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource); // 创建 JdbcTemplate 对象,用于执行 SQL 语句 JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); // 创建事务模板 TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager); Boolean insertSuccess = transactionTemplate.execute(new TransactionCallback() { @Override public Boolean doInTransaction(TransactionStatus status) { // 主键Id long id = System.currentTimeMillis(); // 分数 int score = new Random().nextInt(100); // 保存数据 int row = jdbcTemplate.update("insert into scores(id,score) values(?,?)", id, score); // 模拟异常,用于测试事务回滚 // int i = 1 / 0; // 我们也可以使用setRollbackOnly来回滚 // status.setRollbackOnly(); // 返回是否新增成功 return row >= 1; } }); System.out.println("新增scores表数据:" + insertSuccess); } } ================================================ FILE: spring-transaction/sql/test.sql ================================================ CREATE TABLE `scores` ( `id` bigint NOT NULL, `score` decimal(5, 2) NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = DYNAMIC; CREATE TABLE `scores_log` ( `id` bigint NOT NULL, `score_id` bigint NULL DEFAULT NULL, `create_time` datetime NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = DYNAMIC;