From 50a95ecaac4d94e965165a1c15e6ba6e918ca535 Mon Sep 17 00:00:00 2001 From: QilongZhang Date: Thu, 29 Nov 2018 20:50:57 +0800 Subject: [PATCH] Support @SofaService to annotated on method and refactor related code. (#288) Support @SofaService to annotated on method and refactor related code. (#288) --- .travis.yml | 2 +- .../sofa/infra/utils/SOFABootEnvUtils.java | 2 - .../loader/DynamicSpringContextLoader.java | 23 +- ...renceAnnotationBeanPostProcessorTest.java} | 108 ++--- .../health/SofaModuleHealthCheckerTest.java | 14 + runtime-sofa-boot-starter/pom.xml | 11 + .../runtime/api/annotation/SofaReference.java | 2 +- .../runtime/api/annotation/SofaService.java | 2 +- ...ReferenceAnnotationBeanPostProcessor.java} | 128 +----- .../ServiceBeanFactoryPostProcessor.java | 375 ++++++++++++++++++ .../spring/bean/SofaBeanNameGenerator.java | 62 +++ .../bean/SofaParameterNameDiscoverer.java | 89 +++++ .../SofaRuntimeAutoConfiguration.java | 113 +----- .../factory/AbstractContractFactoryBean.java | 75 ++-- .../spring/factory/ServiceFactoryBean.java | 2 +- .../SofaRuntimeApplicationListener.java | 157 ++++++++ .../AbstractContractDefinitionParser.java | 43 +- .../parser/ReferenceDefinitionParser.java | 6 +- .../parser/ServiceDefinitionParser.java | 12 +- .../main/resources/META-INF/spring.factories | 4 + .../runtime/ark/SofaEventHandlerTest.java | 34 +- ...ethodBeanClassAnnotationSampleService.java | 32 ++ ...thodBeanMethodAnnotationSampleService.java | 30 ++ .../ParameterAnnotationSampleService.java | 41 ++ .../impl/XmlAnnotationSampleService.java | 32 ++ .../runtime/beans/impl/XmlSampleService.java | 2 - .../runtime/integration/IntegrationTest.java | 142 ++++++- .../TestSofaServiceAndReferenceException.java | 94 +++++ .../integration/base/AbstractTestBase.java | 77 ++-- .../runtime/integration/base/TestBase.java | 56 +++ .../integration/features/AwareTest.java | 7 + .../config/SofaRuntimePropertiesTest.java | 13 + .../SofaComponentHealthCheckerTest.java | 14 + .../META-INF/spring/test-service.xml | 1 + 34 files changed, 1398 insertions(+), 407 deletions(-) rename isle-sofa-boot-starter/src/test/java/com/alipay/sofa/isle/{ServiceAnnotationBeanPostProcessorTest.java => ReferenceAnnotationBeanPostProcessorTest.java} (53%) rename runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/{ServiceAnnotationBeanPostProcessor.java => ReferenceAnnotationBeanPostProcessor.java} (60%) create mode 100644 runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/ServiceBeanFactoryPostProcessor.java create mode 100644 runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/bean/SofaBeanNameGenerator.java create mode 100644 runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/bean/SofaParameterNameDiscoverer.java create mode 100644 runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/listener/SofaRuntimeApplicationListener.java create mode 100644 runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/beans/impl/MethodBeanClassAnnotationSampleService.java create mode 100644 runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/beans/impl/MethodBeanMethodAnnotationSampleService.java create mode 100644 runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/beans/impl/ParameterAnnotationSampleService.java create mode 100644 runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/beans/impl/XmlAnnotationSampleService.java create mode 100644 runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/TestSofaServiceAndReferenceException.java create mode 100644 runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/base/TestBase.java diff --git a/.travis.yml b/.travis.yml index 5e2c4e101..59d00909d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,10 +7,10 @@ jdk: install: - mvn clean install -DskipTests -B -V +- mvn test script: - sh ./check_format.sh after_success: -- mvn clean test - bash <(curl -s https://codecov.io/bash) \ No newline at end of file diff --git a/infra-sofa-boot-starter/src/main/java/com/alipay/sofa/infra/utils/SOFABootEnvUtils.java b/infra-sofa-boot-starter/src/main/java/com/alipay/sofa/infra/utils/SOFABootEnvUtils.java index 43e19d7a9..8fc26384e 100644 --- a/infra-sofa-boot-starter/src/main/java/com/alipay/sofa/infra/utils/SOFABootEnvUtils.java +++ b/infra-sofa-boot-starter/src/main/java/com/alipay/sofa/infra/utils/SOFABootEnvUtils.java @@ -17,8 +17,6 @@ package com.alipay.sofa.infra.utils; import com.alipay.sofa.infra.constants.SofaBootInfraConstants; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationContextInitializer; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; diff --git a/isle-sofa-boot-starter/src/main/java/com/alipay/sofa/isle/loader/DynamicSpringContextLoader.java b/isle-sofa-boot-starter/src/main/java/com/alipay/sofa/isle/loader/DynamicSpringContextLoader.java index 28243a45c..6782d5269 100644 --- a/isle-sofa-boot-starter/src/main/java/com/alipay/sofa/isle/loader/DynamicSpringContextLoader.java +++ b/isle-sofa-boot-starter/src/main/java/com/alipay/sofa/isle/loader/DynamicSpringContextLoader.java @@ -16,13 +16,9 @@ */ package com.alipay.sofa.isle.loader; -import com.alipay.sofa.isle.ApplicationRuntimeModel; -import com.alipay.sofa.isle.constants.SofaModuleFrameworkConstants; -import com.alipay.sofa.isle.deployment.DeploymentDescriptor; -import com.alipay.sofa.isle.spring.config.SofaModuleProperties; -import com.alipay.sofa.isle.spring.context.SofaModuleApplicationContext; -import com.alipay.sofa.isle.spring.factory.BeanLoadCostBeanFactory; -import com.alipay.sofa.runtime.spi.log.SofaLogger; +import java.util.Map; + +import com.alipay.sofa.runtime.spring.listener.SofaRuntimeApplicationListener; import org.springframework.beans.CachedIntrospectionResults; import org.springframework.beans.PropertyEditorRegistrar; import org.springframework.beans.PropertyEditorRegistry; @@ -35,11 +31,16 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.GenericApplicationContext; -import org.springframework.core.LocalVariableTableParameterNameDiscoverer; import org.springframework.core.io.Resource; import org.springframework.util.StringUtils; -import java.util.Map; +import com.alipay.sofa.isle.ApplicationRuntimeModel; +import com.alipay.sofa.isle.constants.SofaModuleFrameworkConstants; +import com.alipay.sofa.isle.deployment.DeploymentDescriptor; +import com.alipay.sofa.isle.spring.config.SofaModuleProperties; +import com.alipay.sofa.isle.spring.context.SofaModuleApplicationContext; +import com.alipay.sofa.isle.spring.factory.BeanLoadCostBeanFactory; +import com.alipay.sofa.runtime.spi.log.SofaLogger; /** * @@ -58,14 +59,14 @@ public void loadSpringContext(DeploymentDescriptor deployment, SofaModuleProperties sofaModuleProperties = rootApplicationContext .getBean(SofaModuleFrameworkConstants.SOFA_MODULE_PROPERTIES_BEAN_ID, SofaModuleProperties.class); + BeanLoadCostBeanFactory beanFactory = new BeanLoadCostBeanFactory( sofaModuleProperties.getBeanLoadCost()); - beanFactory.setParameterNameDiscoverer(new LocalVariableTableParameterNameDiscoverer()); beanFactory .setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver()); - GenericApplicationContext ctx = sofaModuleProperties.isPublishEventToParent() ? new GenericApplicationContext( beanFactory) : new SofaModuleApplicationContext(beanFactory); + SofaRuntimeApplicationListener.initApplicationContext(ctx); String activeProfiles = sofaModuleProperties.getActiveProfiles(); if (StringUtils.hasText(activeProfiles)) { String[] profiles = activeProfiles diff --git a/isle-sofa-boot-starter/src/test/java/com/alipay/sofa/isle/ServiceAnnotationBeanPostProcessorTest.java b/isle-sofa-boot-starter/src/test/java/com/alipay/sofa/isle/ReferenceAnnotationBeanPostProcessorTest.java similarity index 53% rename from isle-sofa-boot-starter/src/test/java/com/alipay/sofa/isle/ServiceAnnotationBeanPostProcessorTest.java rename to isle-sofa-boot-starter/src/test/java/com/alipay/sofa/isle/ReferenceAnnotationBeanPostProcessorTest.java index 708fb803f..eb362111f 100644 --- a/isle-sofa-boot-starter/src/test/java/com/alipay/sofa/isle/ServiceAnnotationBeanPostProcessorTest.java +++ b/isle-sofa-boot-starter/src/test/java/com/alipay/sofa/isle/ReferenceAnnotationBeanPostProcessorTest.java @@ -16,47 +16,44 @@ */ package com.alipay.sofa.isle; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.lang.reflect.Method; +import java.util.HashSet; + +import com.alipay.sofa.runtime.spring.ReferenceAnnotationBeanPostProcessor; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.springframework.context.ApplicationContext; + import com.alipay.sofa.runtime.api.annotation.SofaReference; import com.alipay.sofa.runtime.api.annotation.SofaReferenceBinding; -import com.alipay.sofa.runtime.api.annotation.SofaService; -import com.alipay.sofa.runtime.api.annotation.SofaServiceBinding; import com.alipay.sofa.runtime.api.binding.BindingType; import com.alipay.sofa.runtime.constants.SofaRuntimeFrameworkConstants; import com.alipay.sofa.runtime.model.InterfaceMode; -import com.alipay.sofa.runtime.service.component.Service; import com.alipay.sofa.runtime.service.component.impl.ReferenceImpl; import com.alipay.sofa.runtime.service.helper.ReferenceRegisterHelper; import com.alipay.sofa.runtime.service.impl.BindingConverterFactoryImpl; import com.alipay.sofa.runtime.spi.component.SofaRuntimeContext; import com.alipay.sofa.runtime.spi.service.BindingConverter; import com.alipay.sofa.runtime.spi.service.BindingConverterFactory; -import com.alipay.sofa.runtime.spring.ServiceAnnotationBeanPostProcessor; -import org.junit.Assert; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.powermock.api.mockito.PowerMockito; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; -import org.springframework.context.ApplicationContext; - -import java.lang.reflect.Method; -import java.util.HashSet; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; /** * @author xuanbei 18/5/15 */ @SuppressWarnings("unchecked") @RunWith(PowerMockRunner.class) -@PrepareForTest({ ReferenceRegisterHelper.class, ServiceAnnotationBeanPostProcessor.class }) -public class ServiceAnnotationBeanPostProcessorTest { +@PrepareForTest({ ReferenceRegisterHelper.class, ReferenceAnnotationBeanPostProcessor.class }) +public class ReferenceAnnotationBeanPostProcessorTest { @Test public void testCreateReferenceProxy() throws Exception { Class clazz = Class - .forName("com.alipay.sofa.runtime.spring.ServiceAnnotationBeanPostProcessor"); + .forName("com.alipay.sofa.runtime.spring.ReferenceAnnotationBeanPostProcessor"); Method createReferenceProxy = clazz.getDeclaredMethod("createReferenceProxy", SofaReference.class, Class.class); createReferenceProxy.setAccessible(true); @@ -72,7 +69,7 @@ public void testCreateReferenceProxy() throws Exception { SofaRuntimeContext sofaRuntimeContext = mock(SofaRuntimeContext.class); when(sofaRuntimeContext.getAppName()).thenReturn("testcase"); when(sofaRuntimeContext.getAppClassLoader()).thenReturn( - ServiceAnnotationBeanPostProcessorTest.class.getClassLoader()); + ReferenceAnnotationBeanPostProcessorTest.class.getClassLoader()); ApplicationContext applicationContext = mock(ApplicationContext.class); when( @@ -81,12 +78,11 @@ public void testCreateReferenceProxy() throws Exception { boolean hasException = false; BindingConverterFactory bindingConverterFactory = new BindingConverterFactoryImpl(); - ServiceAnnotationBeanPostProcessor serviceAnnotationBeanPostProcessor = new ServiceAnnotationBeanPostProcessor( - null, bindingConverterFactory); - serviceAnnotationBeanPostProcessor.setApplicationContext(applicationContext); + ReferenceAnnotationBeanPostProcessor referenceAnnotationBeanPostProcessor = new ReferenceAnnotationBeanPostProcessor( + applicationContext, sofaRuntimeContext, null, bindingConverterFactory); try { - createReferenceProxy.invoke(serviceAnnotationBeanPostProcessor, sofaReference, - ServiceAnnotationBeanPostProcessorTest.class); + createReferenceProxy.invoke(referenceAnnotationBeanPostProcessor, sofaReference, + ReferenceAnnotationBeanPostProcessorTest.class); } catch (Exception e) { Assert.assertEquals("Can not found binding converter for binding type bolt", e .getCause().getMessage()); @@ -104,65 +100,15 @@ public void testCreateReferenceProxy() throws Exception { // use power mockito mock static PowerMockito.mockStatic(ReferenceRegisterHelper.class); ReferenceImpl referenceImpl = new ReferenceImpl("uniqueId", - ServiceAnnotationBeanPostProcessorTest.class, InterfaceMode.annotation, true); + ReferenceAnnotationBeanPostProcessorTest.class, InterfaceMode.annotation, true); PowerMockito .whenNew(ReferenceImpl.class) - .withArguments("uniqueId", ServiceAnnotationBeanPostProcessorTest.class, + .withArguments("uniqueId", ReferenceAnnotationBeanPostProcessorTest.class, InterfaceMode.annotation, true).thenReturn(referenceImpl); - createReferenceProxy.invoke(serviceAnnotationBeanPostProcessor, sofaReference, - ServiceAnnotationBeanPostProcessorTest.class); + createReferenceProxy.invoke(referenceAnnotationBeanPostProcessor, sofaReference, + ReferenceAnnotationBeanPostProcessorTest.class); PowerMockito.verifyStatic(); ReferenceRegisterHelper.registerReference(referenceImpl, null, sofaRuntimeContext); } - - @Test - public void testHandleSofaServiceBinding() throws Exception { - Class clazz = Class - .forName("com.alipay.sofa.runtime.spring.ServiceAnnotationBeanPostProcessor"); - Method createReferenceProxy = clazz.getDeclaredMethod("handleSofaServiceBinding", - Service.class, SofaService.class, SofaServiceBinding.class); - createReferenceProxy.setAccessible(true); - - Service service = mock(Service.class); - SofaService sofaService = mock(SofaService.class); - SofaServiceBinding sofaServiceBinding = mock(SofaServiceBinding.class); - - when(sofaServiceBinding.bindingType()).thenReturn("bolt"); - - SofaRuntimeContext sofaRuntimeContext = mock(SofaRuntimeContext.class); - when(sofaRuntimeContext.getAppName()).thenReturn("testcase"); - when(sofaRuntimeContext.getAppClassLoader()).thenReturn( - ServiceAnnotationBeanPostProcessorTest.class.getClassLoader()); - - ApplicationContext applicationContext = mock(ApplicationContext.class); - when( - applicationContext.getBean(SofaRuntimeFrameworkConstants.SOFA_RUNTIME_CONTEXT_BEAN_ID, - SofaRuntimeContext.class)).thenReturn(sofaRuntimeContext); - - boolean hasException = false; - BindingConverterFactory bindingConverterFactory = new BindingConverterFactoryImpl(); - ServiceAnnotationBeanPostProcessor serviceAnnotationBeanPostProcessor = new ServiceAnnotationBeanPostProcessor( - null, bindingConverterFactory); - serviceAnnotationBeanPostProcessor.setApplicationContext(applicationContext); - try { - createReferenceProxy.invoke(serviceAnnotationBeanPostProcessor, service, sofaService, - sofaServiceBinding); - } catch (Exception e) { - Assert.assertEquals("Can not found binding converter for binding type bolt", e - .getCause().getMessage()); - hasException = true; - } - Assert.assertTrue(hasException); - - BindingConverter bindingConverter = mock(BindingConverter.class); - when(bindingConverter.supportBindingType()).thenReturn(new BindingType("bolt")); - when(bindingConverter.supportTagName()).thenReturn("binding:bolt"); - HashSet bindingConverters = new HashSet<>(); - bindingConverters.add(bindingConverter); - bindingConverterFactory.addBindingConverters(bindingConverters); - - createReferenceProxy.invoke(serviceAnnotationBeanPostProcessor, service, sofaService, - sofaServiceBinding); - } } diff --git a/isle-sofa-boot-starter/src/test/java/com/alipay/sofa/isle/spring/health/SofaModuleHealthCheckerTest.java b/isle-sofa-boot-starter/src/test/java/com/alipay/sofa/isle/spring/health/SofaModuleHealthCheckerTest.java index 18fb9bc4f..3e6787087 100644 --- a/isle-sofa-boot-starter/src/test/java/com/alipay/sofa/isle/spring/health/SofaModuleHealthCheckerTest.java +++ b/isle-sofa-boot-starter/src/test/java/com/alipay/sofa/isle/spring/health/SofaModuleHealthCheckerTest.java @@ -19,12 +19,18 @@ import com.alipay.sofa.healthcheck.configuration.HealthCheckConstants; import com.alipay.sofa.isle.spring.configuration.SofaModuleAutoConfiguration; import com.alipay.sofa.runtime.spring.configuration.SofaRuntimeAutoConfiguration; +import com.alipay.sofa.runtime.spring.listener.SofaRuntimeApplicationListener; import org.junit.After; import org.junit.Assert; +import org.junit.Before; import org.junit.Test; +import org.mockito.Mockito; +import org.springframework.boot.context.event.ApplicationPreparedEvent; import org.springframework.boot.test.util.EnvironmentTestUtils; import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import static org.mockito.Mockito.when; + /** * @author abby.zh * @since 2.4.10 @@ -38,6 +44,14 @@ public void closeContext() { this.applicationContext.close(); } + @Before + public void before() { + ApplicationPreparedEvent applicationPreparedEvent = Mockito + .mock(ApplicationPreparedEvent.class); + when(applicationPreparedEvent.getApplicationContext()).thenReturn(applicationContext); + new SofaRuntimeApplicationListener().onApplicationEvent(applicationPreparedEvent); + } + @Test public void testDefaultConfig() { this.applicationContext.register(SofaModuleAutoConfiguration.class); diff --git a/runtime-sofa-boot-starter/pom.xml b/runtime-sofa-boot-starter/pom.xml index e2958815d..99b910d28 100644 --- a/runtime-sofa-boot-starter/pom.xml +++ b/runtime-sofa-boot-starter/pom.xml @@ -124,6 +124,11 @@ 1.9 test + + org.mockito + mockito-core + test + junit junit @@ -135,5 +140,11 @@ aspectjweaver test + + commons-io + commons-io + 2.6 + test + diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/api/annotation/SofaReference.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/api/annotation/SofaReference.java index 1bfde0566..1b8bd4f69 100644 --- a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/api/annotation/SofaReference.java +++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/api/annotation/SofaReference.java @@ -36,7 +36,7 @@ * @author xuanbei 18/3/2 */ @Retention(RetentionPolicy.RUNTIME) -@Target({ ElementType.FIELD, ElementType.METHOD }) +@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER }) public @interface SofaReference { /** diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/api/annotation/SofaService.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/api/annotation/SofaService.java index 9025eb369..4f62504f9 100644 --- a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/api/annotation/SofaService.java +++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/api/annotation/SofaService.java @@ -39,7 +39,7 @@ * @author xuanbei 18/3/1 */ @Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) +@Target({ ElementType.TYPE, ElementType.METHOD }) public @interface SofaService { /** diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/ServiceAnnotationBeanPostProcessor.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/ReferenceAnnotationBeanPostProcessor.java similarity index 60% rename from runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/ServiceAnnotationBeanPostProcessor.java rename to runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/ReferenceAnnotationBeanPostProcessor.java index 4d814df37..5d6eae020 100644 --- a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/ServiceAnnotationBeanPostProcessor.java +++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/ReferenceAnnotationBeanPostProcessor.java @@ -20,12 +20,9 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import org.springframework.aop.framework.AopProxyUtils; import org.springframework.beans.BeansException; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; import org.springframework.core.Ordered; import org.springframework.core.env.Environment; import org.springframework.util.Assert; @@ -35,52 +32,50 @@ import com.alipay.sofa.runtime.annotation.PlaceHolderBinder; import com.alipay.sofa.runtime.api.ServiceRuntimeException; import com.alipay.sofa.runtime.api.annotation.SofaReference; -import com.alipay.sofa.runtime.api.annotation.SofaService; -import com.alipay.sofa.runtime.api.annotation.SofaServiceBinding; import com.alipay.sofa.runtime.api.binding.BindingType; -import com.alipay.sofa.runtime.constants.SofaRuntimeFrameworkConstants; import com.alipay.sofa.runtime.model.InterfaceMode; import com.alipay.sofa.runtime.service.binding.JvmBinding; import com.alipay.sofa.runtime.service.component.Reference; -import com.alipay.sofa.runtime.service.component.Service; -import com.alipay.sofa.runtime.service.component.ServiceComponent; import com.alipay.sofa.runtime.service.component.impl.ReferenceImpl; -import com.alipay.sofa.runtime.service.component.impl.ServiceImpl; import com.alipay.sofa.runtime.service.helper.ReferenceRegisterHelper; import com.alipay.sofa.runtime.spi.binding.Binding; import com.alipay.sofa.runtime.spi.binding.BindingAdapterFactory; -import com.alipay.sofa.runtime.spi.component.ComponentInfo; -import com.alipay.sofa.runtime.spi.component.DefaultImplementation; -import com.alipay.sofa.runtime.spi.component.Implementation; import com.alipay.sofa.runtime.spi.component.SofaRuntimeContext; import com.alipay.sofa.runtime.spi.service.BindingConverter; import com.alipay.sofa.runtime.spi.service.BindingConverterContext; import com.alipay.sofa.runtime.spi.service.BindingConverterFactory; /** + * Responsible to inject field annotated by @SofaReference and + * invoke setXX method annotated by @SofaReference + * * @author xuanbei 18/5/9 */ -public class ServiceAnnotationBeanPostProcessor implements BeanPostProcessor, Ordered, - ApplicationContextAware { +public class ReferenceAnnotationBeanPostProcessor implements BeanPostProcessor, Ordered { + private final PlaceHolderBinder binder = new DefaultPlaceHolderBinder(); + private ApplicationContext applicationContext; private SofaRuntimeContext sofaRuntimeContext; private BindingAdapterFactory bindingAdapterFactory; private BindingConverterFactory bindingConverterFactory; - private ApplicationContext applicationContext; - - @Autowired private Environment environment; - private final PlaceHolderBinder binder = new DefaultPlaceHolderBinder(); /** - * To construct a ServiceAnnotationBeanPostProcessor via a Spring Bean + * To construct a ReferenceAnnotationBeanPostProcessor via a Spring Bean * sofaRuntimeContext and sofaRuntimeProperties will be obtained from applicationContext + * @param applicationContext + * @param sofaRuntimeContext * @param bindingAdapterFactory * @param bindingConverterFactory */ - public ServiceAnnotationBeanPostProcessor(BindingAdapterFactory bindingAdapterFactory, - BindingConverterFactory bindingConverterFactory) { + public ReferenceAnnotationBeanPostProcessor(ApplicationContext applicationContext, + SofaRuntimeContext sofaRuntimeContext, + BindingAdapterFactory bindingAdapterFactory, + BindingConverterFactory bindingConverterFactory) { + this.applicationContext = applicationContext; + this.sofaRuntimeContext = sofaRuntimeContext; this.bindingAdapterFactory = bindingAdapterFactory; this.bindingConverterFactory = bindingConverterFactory; + this.environment = applicationContext.getEnvironment(); } @Override @@ -90,53 +85,6 @@ public Object postProcessBeforeInitialization(Object bean, String beanName) return bean; } - @Override - public Object postProcessAfterInitialization(Object bean, String beanName) - throws BeansException { - processSofaService(bean, beanName); - return bean; - } - - @SuppressWarnings("unchecked") - private void processSofaService(Object bean, String beanName) { - final Class beanClass = AopProxyUtils.ultimateTargetClass(bean); - - SofaService sofaServiceAnnotation = beanClass.getAnnotation(SofaService.class); - if (sofaServiceAnnotation == null) { - return; - } - AnnotationWrapperBuilder builder = AnnotationWrapperBuilder.wrap( - sofaServiceAnnotation).withBinder(binder); - sofaServiceAnnotation = builder.build(); - - Class interfaceType = sofaServiceAnnotation.interfaceType(); - - if (interfaceType.equals(void.class)) { - Class interfaces[] = beanClass.getInterfaces(); - - if (interfaces == null || interfaces.length == 0) { - interfaceType = beanClass; - } else if (interfaces.length == 1) { - interfaceType = interfaces[0]; - } else { - throw new ServiceRuntimeException("Bean " + beanName - + " has more than one interface."); - } - } - - Implementation implementation = new DefaultImplementation(bean); - Service service = new ServiceImpl(sofaServiceAnnotation.uniqueId(), interfaceType, - InterfaceMode.annotation, bean); - - for (SofaServiceBinding sofaServiceBinding : sofaServiceAnnotation.bindings()) { - handleSofaServiceBinding(service, sofaServiceAnnotation, sofaServiceBinding); - } - - ComponentInfo componentInfo = new ServiceComponent(implementation, service, - bindingAdapterFactory, getSofaRuntimeContext()); - getSofaRuntimeContext().getComponentManager().register(componentInfo); - } - private void processSofaReference(final Object bean) { final Class beanClass = bean.getClass(); @@ -204,30 +152,6 @@ public boolean matches(Method method) { }); } - private void handleSofaServiceBinding(Service service, SofaService sofaServiceAnnotation, - SofaServiceBinding sofaServiceBinding) { - if (JvmBinding.JVM_BINDING_TYPE.getType().equals(sofaServiceBinding.bindingType())) { - service.addBinding(new JvmBinding()); - } else { - BindingConverter bindingConverter = bindingConverterFactory - .getBindingConverter(new BindingType(sofaServiceBinding.bindingType())); - if (bindingConverter == null) { - throw new ServiceRuntimeException( - "Can not found binding converter for binding type " - + sofaServiceBinding.bindingType()); - } - - BindingConverterContext bindingConverterContext = new BindingConverterContext(); - bindingConverterContext.setInBinding(false); - bindingConverterContext.setApplicationContext(applicationContext); - bindingConverterContext.setAppName(getSofaRuntimeContext().getAppName()); - bindingConverterContext.setAppClassLoader(getSofaRuntimeContext().getAppClassLoader()); - Binding binding = bindingConverter.convert(sofaServiceAnnotation, sofaServiceBinding, - bindingConverterContext); - service.addBinding(binding); - } - } - private Object createReferenceProxy(SofaReference sofaReferenceAnnotation, Class interfaceType) { Reference reference = new ReferenceImpl(sofaReferenceAnnotation.uniqueId(), interfaceType, @@ -248,23 +172,14 @@ private Object createReferenceProxy(SofaReference sofaReferenceAnnotation, BindingConverterContext bindingConverterContext = new BindingConverterContext(); bindingConverterContext.setInBinding(true); bindingConverterContext.setApplicationContext(applicationContext); - bindingConverterContext.setAppName(getSofaRuntimeContext().getAppName()); - bindingConverterContext.setAppClassLoader(getSofaRuntimeContext().getAppClassLoader()); + bindingConverterContext.setAppName(sofaRuntimeContext.getAppName()); + bindingConverterContext.setAppClassLoader(sofaRuntimeContext.getAppClassLoader()); Binding binding = bindingConverter.convert(sofaReferenceAnnotation, sofaReferenceAnnotation.binding(), bindingConverterContext); reference.addBinding(binding); } return ReferenceRegisterHelper.registerReference(reference, bindingAdapterFactory, - getSofaRuntimeContext()); - } - - private SofaRuntimeContext getSofaRuntimeContext() { - if (sofaRuntimeContext == null) { - sofaRuntimeContext = applicationContext.getBean( - SofaRuntimeFrameworkConstants.SOFA_RUNTIME_CONTEXT_BEAN_ID, - SofaRuntimeContext.class); - } - return sofaRuntimeContext; + sofaRuntimeContext); } @Override @@ -272,11 +187,6 @@ public int getOrder() { return Ordered.LOWEST_PRECEDENCE; } - @Override - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - this.applicationContext = applicationContext; - } - class DefaultPlaceHolderBinder implements PlaceHolderBinder { @Override public String bind(String text) { diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/ServiceBeanFactoryPostProcessor.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/ServiceBeanFactoryPostProcessor.java new file mode 100644 index 000000000..0b467261e --- /dev/null +++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/ServiceBeanFactoryPostProcessor.java @@ -0,0 +1,375 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.runtime.spring; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.springframework.beans.BeansException; +import org.springframework.beans.FatalBeanException; +import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; +import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.GenericBeanDefinition; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ScannedGenericBeanDefinition; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.type.AnnotationMetadata; +import org.springframework.core.type.MethodMetadata; +import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; +import org.springframework.util.ObjectUtils; +import org.springframework.util.StringUtils; + +import com.alipay.sofa.runtime.annotation.PlaceHolderAnnotationInvocationHandler.AnnotationWrapperBuilder; +import com.alipay.sofa.runtime.annotation.PlaceHolderBinder; +import com.alipay.sofa.runtime.api.ServiceRuntimeException; +import com.alipay.sofa.runtime.api.annotation.SofaReference; +import com.alipay.sofa.runtime.api.annotation.SofaService; +import com.alipay.sofa.runtime.api.annotation.SofaServiceBinding; +import com.alipay.sofa.runtime.api.binding.BindingType; +import com.alipay.sofa.runtime.service.binding.JvmBinding; +import com.alipay.sofa.runtime.spi.binding.Binding; +import com.alipay.sofa.runtime.spi.component.SofaRuntimeContext; +import com.alipay.sofa.runtime.spi.log.SofaLogger; +import com.alipay.sofa.runtime.spi.service.BindingConverter; +import com.alipay.sofa.runtime.spi.service.BindingConverterContext; +import com.alipay.sofa.runtime.spi.service.BindingConverterFactory; +import com.alipay.sofa.runtime.spring.bean.SofaBeanNameGenerator; +import com.alipay.sofa.runtime.spring.factory.ReferenceFactoryBean; +import com.alipay.sofa.runtime.spring.factory.ServiceFactoryBean; +import com.alipay.sofa.runtime.spring.parser.AbstractContractDefinitionParser; +import com.alipay.sofa.runtime.spring.parser.ServiceDefinitionParser; + +/** + * @author qilong.zql + * @since 3.1.0 + */ +public class ServiceBeanFactoryPostProcessor implements BeanFactoryPostProcessor { + private final PlaceHolderBinder binder = new DefaultPlaceHolderBinder(); + private ConfigurableApplicationContext applicationContext; + private SofaRuntimeContext sofaRuntimeContext; + private BindingConverterFactory bindingConverterFactory; + private ConfigurableEnvironment environment; + + public ServiceBeanFactoryPostProcessor(ConfigurableApplicationContext applicationContext, + SofaRuntimeContext sofaRuntimeContext, + BindingConverterFactory bindingConverterFactory) { + this.applicationContext = applicationContext; + this.sofaRuntimeContext = sofaRuntimeContext; + this.bindingConverterFactory = bindingConverterFactory; + this.environment = applicationContext.getEnvironment(); + } + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { + Arrays.stream(beanFactory.getBeanDefinitionNames()) + .collect(Collectors.toMap(Function.identity(), beanFactory::getBeanDefinition)) + .forEach((key, value) -> transformSofaBeanDefinition(key, value, beanFactory)); + } + + /** + * {@link ScannedGenericBeanDefinition} + * {@link AnnotatedGenericBeanDefinition} + * {@link GenericBeanDefinition} + * {@link org.springframework.beans.factory.support.ChildBeanDefinition} + * {@link org.springframework.beans.factory.support.RootBeanDefinition} + * {@link org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.ConfigurationClassBeanDefinition} + */ + private void transformSofaBeanDefinition(String beanId, BeanDefinition beanDefinition, + ConfigurableListableBeanFactory beanFactory) { + if (isFromConfigurationSource(beanDefinition)) { + generateSofaServiceDefinitionOnMethod(beanId, (AnnotatedBeanDefinition) beanDefinition, + beanFactory); + } else { + Class beanClassType = resolveBeanClassType(beanDefinition); + if (beanClassType == null) { + SofaLogger.warn("Bean class type cant be resolved from bean of {}", beanId); + return; + } + generateSofaServiceDefinitionOnClass(beanId, beanClassType, beanDefinition, beanFactory); + } + } + + private void generateSofaServiceDefinitionOnMethod(String beanId, + AnnotatedBeanDefinition beanDefinition, + ConfigurableListableBeanFactory beanFactory) { + Class returnType; + Class declaringClass; + Method method = null; + + MethodMetadata methodMetadata = beanDefinition.getFactoryMethodMetadata(); + try { + returnType = ClassUtils.forName(methodMetadata.getReturnTypeName(), null); + declaringClass = ClassUtils.forName(methodMetadata.getDeclaringClassName(), null); + for (Method m : declaringClass.getDeclaredMethods()) { + + Bean bean = m.getAnnotation(Bean.class); + Set beanNames = Stream.of(m.getName()).collect(Collectors.toSet()); + if (bean != null) { + beanNames.addAll(Arrays.asList(bean.name())); + beanNames.addAll(Arrays.asList(bean.value())); + } + if (!beanNames.contains(beanId)) { + continue; + } + if (method != null) { + throw new IllegalStateException("multi @Bean-method with same name in " + + declaringClass.getCanonicalName()); + } + method = m; + } + } catch (Throwable throwable) { + SofaLogger.error(throwable, "Failed to resolve @SofaService on @Bean-method({}) in {}", + beanId, methodMetadata.getDeclaringClassName()); + throw new FatalBeanException("Failed to resolve @SofaService on method", throwable); + } + + if (method != null) { + SofaService sofaServiceAnnotation = method.getAnnotation(SofaService.class); + if (sofaServiceAnnotation == null) { + sofaServiceAnnotation = returnType.getAnnotation(SofaService.class); + } + generateSofaServiceDefinition(beanId, sofaServiceAnnotation, returnType, + beanDefinition, beanFactory); + generateSofaReferenceDefinition(beanId, method, beanFactory); + } + } + + private void generateSofaReferenceDefinition(String beanId, Method method, + ConfigurableListableBeanFactory beanFactory) { + Class[] parameterTypes = method.getParameterTypes(); + Annotation[][] parameterAnnotations = method.getParameterAnnotations(); + for (int i = 0; i < parameterAnnotations.length; ++i) { + for (Annotation annotation : parameterAnnotations[i]) { + if (annotation instanceof SofaReference) { + doGenerateSofaReferenceDefinition(beanFactory.getBeanDefinition(beanId), + (SofaReference) annotation, parameterTypes[i], beanFactory); + } + } + } + } + + @SuppressWarnings("unchecked") + private void doGenerateSofaReferenceDefinition(BeanDefinition beanDefinition, + SofaReference sofaReference, + Class parameterType, + ConfigurableListableBeanFactory beanFactory) { + Assert.isTrue( + JvmBinding.JVM_BINDING_TYPE.getType().equals(sofaReference.binding().bindingType()), + "Only jvm type of @SofaReference on parameter is supported."); + AnnotationWrapperBuilder wrapperBuilder = AnnotationWrapperBuilder.wrap( + sofaReference).withBinder(binder); + sofaReference = wrapperBuilder.build(); + Class interfaceType = sofaReference.interfaceType(); + if (interfaceType.equals(void.class)) { + interfaceType = parameterType; + } + String uniqueId = sofaReference.uniqueId(); + String referenceId = SofaBeanNameGenerator.generateSofaReferenceBeanName(interfaceType, + uniqueId); + + // build sofa reference definition + if (!beanFactory.containsBeanDefinition(referenceId)) { + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(); + builder.getRawBeanDefinition().setScope(beanDefinition.getScope()); + builder.getRawBeanDefinition().setLazyInit(beanDefinition.isLazyInit()); + builder.getRawBeanDefinition().setBeanClass(ReferenceFactoryBean.class); + builder.addPropertyValue(AbstractContractDefinitionParser.UNIQUE_ID_PROPERTY, uniqueId); + builder.addPropertyValue(AbstractContractDefinitionParser.INTERFACE_CLASS_PROPERTY, + interfaceType); + builder.addPropertyValue(AbstractContractDefinitionParser.DEFINITION_BUILDING_API_TYPE, + true); + ((BeanDefinitionRegistry) beanFactory).registerBeanDefinition(referenceId, + builder.getBeanDefinition()); + } + + // add bean dependency relationship + if (beanDefinition.getDependsOn() == null) { + beanDefinition.setDependsOn(referenceId); + } else { + String[] added = ObjectUtils.addObjectToArray(beanDefinition.getDependsOn(), + referenceId); + beanDefinition.setDependsOn(added); + } + } + + private void generateSofaServiceDefinitionOnClass(String beanId, Class beanClass, + BeanDefinition beanDefinition, + ConfigurableListableBeanFactory beanFactory) { + SofaService sofaServiceAnnotation = beanClass.getAnnotation(SofaService.class); + generateSofaServiceDefinition(beanId, sofaServiceAnnotation, beanClass, beanDefinition, + beanFactory); + } + + @SuppressWarnings("unchecked") + private void generateSofaServiceDefinition(String beanId, SofaService sofaServiceAnnotation, + Class beanClass, BeanDefinition beanDefinition, + ConfigurableListableBeanFactory beanFactory) { + if (sofaServiceAnnotation == null) { + return; + } + AnnotationWrapperBuilder wrapperBuilder = AnnotationWrapperBuilder.wrap( + sofaServiceAnnotation).withBinder(binder); + sofaServiceAnnotation = wrapperBuilder.build(); + + Class interfaceType = sofaServiceAnnotation.interfaceType(); + if (interfaceType.equals(void.class)) { + Class interfaces[] = beanClass.getInterfaces(); + + if (beanClass.isInterface() || interfaces == null || interfaces.length == 0) { + interfaceType = beanClass; + } else if (interfaces.length == 1) { + interfaceType = interfaces[0]; + } else { + throw new FatalBeanException("Bean " + beanId + " has more than one interface."); + } + } + + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(); + String serviceId = SofaBeanNameGenerator.generateSofaServiceBeanName(interfaceType, + sofaServiceAnnotation.uniqueId()); + + if (!beanFactory.containsBeanDefinition(serviceId)) { + builder.getRawBeanDefinition().setScope(beanDefinition.getScope()); + builder.setLazyInit(beanDefinition.isLazyInit()); + builder.getRawBeanDefinition().setBeanClass(ServiceFactoryBean.class); + builder.addPropertyValue(AbstractContractDefinitionParser.INTERFACE_CLASS_PROPERTY, + interfaceType); + builder.addPropertyValue(AbstractContractDefinitionParser.UNIQUE_ID_PROPERTY, + sofaServiceAnnotation.uniqueId()); + builder.addPropertyValue(AbstractContractDefinitionParser.BINDINGS, + getSofaServiceBinding(sofaServiceAnnotation, sofaServiceAnnotation.bindings())); + builder.addPropertyReference(ServiceDefinitionParser.REF, beanId); + builder.addPropertyValue(ServiceDefinitionParser.BEAN_ID, beanId); + builder.addPropertyValue(AbstractContractDefinitionParser.DEFINITION_BUILDING_API_TYPE, + true); + builder.addDependsOn(beanId); + ((BeanDefinitionRegistry) beanFactory).registerBeanDefinition(serviceId, + builder.getBeanDefinition()); + } else { + SofaLogger.error("SofaService was already registered: {0}", serviceId); + } + } + + private List getSofaServiceBinding(SofaService sofaServiceAnnotation, + SofaServiceBinding[] sofaServiceBindings) { + List bindings = new ArrayList<>(); + for (SofaServiceBinding sofaServiceBinding : sofaServiceBindings) { + if (JvmBinding.JVM_BINDING_TYPE.getType().equals(sofaServiceBinding.bindingType())) { + bindings.add(new JvmBinding()); + } else { + BindingConverter bindingConverter = bindingConverterFactory + .getBindingConverter(new BindingType(sofaServiceBinding.bindingType())); + if (bindingConverter == null) { + throw new ServiceRuntimeException( + "Can not found binding converter for binding type " + + sofaServiceBinding.bindingType()); + } + BindingConverterContext bindingConverterContext = new BindingConverterContext(); + bindingConverterContext.setInBinding(false); + bindingConverterContext.setApplicationContext(applicationContext); + bindingConverterContext.setAppName(sofaRuntimeContext.getAppName()); + bindingConverterContext.setAppClassLoader(sofaRuntimeContext.getAppClassLoader()); + Binding binding = bindingConverter.convert(sofaServiceAnnotation, + sofaServiceBinding, bindingConverterContext); + bindings.add(binding); + } + } + return bindings; + } + + /** + * {@link org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.ConfigurationClassBeanDefinition} + * + * @param beanDefinition Check whether it is a bean definition created from a configuration class + * as opposed to any other configuration source. + * @return + */ + private boolean isFromConfigurationSource(BeanDefinition beanDefinition) { + return beanDefinition + .getClass() + .getCanonicalName() + .startsWith( + "org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader"); + } + + /** + * {@link AnnotatedGenericBeanDefinition} + * {@link ScannedGenericBeanDefinition} + * {@link GenericBeanDefinition} + * {@link org.springframework.beans.factory.support.ChildBeanDefinition} + * {@link org.springframework.beans.factory.support.RootBeanDefinition} + * + * @param beanDefinition resolve bean class type from bean definition + * @return + */ + private Class resolveBeanClassType(BeanDefinition beanDefinition) { + Class clazz = null; + + if (beanDefinition instanceof AnnotatedBeanDefinition) { + AnnotationMetadata annotationMetadata = ((AnnotatedBeanDefinition) beanDefinition) + .getMetadata(); + try { + String className = annotationMetadata.getClassName(); + clazz = StringUtils.isEmpty(className) ? null : ClassUtils.forName(className, null); + } catch (Throwable throwable) { + // ignore + } + } + + if (clazz == null) { + try { + clazz = ((AbstractBeanDefinition) beanDefinition).getBeanClass(); + } catch (IllegalStateException ex) { + try { + String className = beanDefinition.getBeanClassName(); + clazz = StringUtils.isEmpty(className) ? null : ClassUtils.forName(className, + null); + } catch (Throwable throwable) { + // ignore + } + } + } + + if (ClassUtils.isCglibProxyClass(clazz)) { + return clazz.getSuperclass(); + } else { + return clazz; + } + } + + class DefaultPlaceHolderBinder implements PlaceHolderBinder { + @Override + public String bind(String text) { + return environment.resolvePlaceholders(text); + } + } +} \ No newline at end of file diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/bean/SofaBeanNameGenerator.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/bean/SofaBeanNameGenerator.java new file mode 100644 index 000000000..30ae80f9d --- /dev/null +++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/bean/SofaBeanNameGenerator.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.runtime.spring.bean; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.util.StringUtils; + +import com.alipay.sofa.runtime.spring.parser.AbstractContractDefinitionParser; + +/** + * @author qilong.zql + * @since 3.1.0 + */ +public class SofaBeanNameGenerator { + private static final String SERVICE_BEAN_NAME_PREFIX = "ServiceFactoryBean#"; + private static final String REFERENCE_BEAN_NAME_PREFIX = "ReferenceFactoryBean#"; + + public static String generateSofaServiceBeanName(BeanDefinition definition) { + String interfaceName = (String) definition.getPropertyValues().get( + AbstractContractDefinitionParser.INTERFACE_PROPERTY); + Class clazz = (Class) definition.getPropertyValues().get( + AbstractContractDefinitionParser.INTERFACE_CLASS_PROPERTY); + if (clazz != null) { + interfaceName = clazz.getCanonicalName(); + } + String uniqueId = (String) definition.getPropertyValues().get( + AbstractContractDefinitionParser.UNIQUE_ID_PROPERTY); + return generateSofaServiceBeanName(interfaceName, uniqueId); + } + + public static String generateSofaServiceBeanName(Class interfaceType, String uniqueId) { + return generateSofaServiceBeanName(interfaceType.getCanonicalName(), uniqueId); + } + + public static String generateSofaServiceBeanName(String interfaceName, String uniqueId) { + if (StringUtils.isEmpty(uniqueId)) { + return SERVICE_BEAN_NAME_PREFIX + interfaceName; + } + return SERVICE_BEAN_NAME_PREFIX + interfaceName + ":" + uniqueId; + } + + public static String generateSofaReferenceBeanName(Class interfaceType, String uniqueId) { + if (StringUtils.isEmpty(uniqueId)) { + return REFERENCE_BEAN_NAME_PREFIX + interfaceType.getCanonicalName(); + } + return REFERENCE_BEAN_NAME_PREFIX + interfaceType.getCanonicalName() + ":" + uniqueId; + } +} \ No newline at end of file diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/bean/SofaParameterNameDiscoverer.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/bean/SofaParameterNameDiscoverer.java new file mode 100644 index 000000000..de59ba422 --- /dev/null +++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/bean/SofaParameterNameDiscoverer.java @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.runtime.spring.bean; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + +import org.springframework.core.LocalVariableTableParameterNameDiscoverer; +import org.springframework.core.ParameterNameDiscoverer; +import org.springframework.core.env.Environment; + +import com.alipay.sofa.runtime.annotation.PlaceHolderAnnotationInvocationHandler.AnnotationWrapperBuilder; +import com.alipay.sofa.runtime.annotation.PlaceHolderBinder; +import com.alipay.sofa.runtime.api.annotation.SofaReference; + +/** + * @author qilong.zql + * @since 3.1.0 + */ +public class SofaParameterNameDiscoverer implements ParameterNameDiscoverer { + private final PlaceHolderBinder binder = new DefaultPlaceHolderBinder(); + private LocalVariableTableParameterNameDiscoverer localVariableTableParameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer(); + private Environment environment; + + public SofaParameterNameDiscoverer(Environment environment) { + this.environment = environment; + } + + @Override + public String[] getParameterNames(Method method) { + String[] parameterNames = localVariableTableParameterNameDiscoverer + .getParameterNames(method); + Class[] parameterTypes = method.getParameterTypes(); + Annotation[][] annotations = method.getParameterAnnotations(); + return transformParameterNames(parameterNames, parameterTypes, annotations); + } + + @Override + public String[] getParameterNames(Constructor ctor) { + String[] parameterNames = localVariableTableParameterNameDiscoverer.getParameterNames(ctor); + Class[] parameterTypes = ctor.getParameterTypes(); + Annotation[][] annotations = ctor.getParameterAnnotations(); + return transformParameterNames(parameterNames, parameterTypes, annotations); + } + + @SuppressWarnings("unchecked") + protected String[] transformParameterNames(String[] parameterNames, Class[] parameterType, + Annotation[][] annotations) { + for (int i = 0; i < annotations.length; ++i) { + for (Annotation annotation : annotations[i]) { + if (annotation instanceof SofaReference) { + AnnotationWrapperBuilder wrapperBuilder = AnnotationWrapperBuilder + .wrap(annotation).withBinder(binder); + SofaReference delegate = wrapperBuilder.build(); + Class interfaceType = delegate.interfaceType(); + if (interfaceType.equals(void.class)) { + interfaceType = parameterType[i]; + } + String uniqueId = delegate.uniqueId(); + parameterNames[i] = SofaBeanNameGenerator.generateSofaReferenceBeanName( + interfaceType, uniqueId); + } + } + } + return parameterNames; + } + + class DefaultPlaceHolderBinder implements PlaceHolderBinder { + @Override + public String bind(String text) { + return environment.resolvePlaceholders(text); + } + } +} \ No newline at end of file diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/configuration/SofaRuntimeAutoConfiguration.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/configuration/SofaRuntimeAutoConfiguration.java index 9555957c8..b92309139 100644 --- a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/configuration/SofaRuntimeAutoConfiguration.java +++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/configuration/SofaRuntimeAutoConfiguration.java @@ -16,47 +16,23 @@ */ package com.alipay.sofa.runtime.spring.configuration; +import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + import com.alipay.sofa.ark.spi.model.Biz; import com.alipay.sofa.healthcheck.core.HealthChecker; -import com.alipay.sofa.infra.constants.SofaBootInfraConstants; -import com.alipay.sofa.runtime.api.client.ReferenceClient; -import com.alipay.sofa.runtime.api.client.ServiceClient; -import com.alipay.sofa.runtime.client.impl.ClientFactoryImpl; -import com.alipay.sofa.runtime.component.impl.StandardSofaRuntimeManager; -import com.alipay.sofa.runtime.service.client.ReferenceClientImpl; -import com.alipay.sofa.runtime.service.client.ServiceClientImpl; -import com.alipay.sofa.runtime.service.impl.BindingAdapterFactoryImpl; -import com.alipay.sofa.runtime.service.impl.BindingConverterFactoryImpl; -import com.alipay.sofa.runtime.SofaFramework; -import com.alipay.sofa.runtime.spi.binding.BindingAdapter; -import com.alipay.sofa.runtime.spi.binding.BindingAdapterFactory; -import com.alipay.sofa.runtime.spi.client.ClientFactoryInternal; import com.alipay.sofa.runtime.spi.component.SofaRuntimeContext; -import com.alipay.sofa.runtime.spi.component.SofaRuntimeManager; -import com.alipay.sofa.runtime.spi.service.BindingConverter; -import com.alipay.sofa.runtime.spi.service.BindingConverterFactory; -import com.alipay.sofa.runtime.spring.ApplicationShutdownCallbackPostProcessor; -import com.alipay.sofa.runtime.spring.ClientFactoryBeanPostProcessor; -import com.alipay.sofa.runtime.spring.ServiceAnnotationBeanPostProcessor; -import com.alipay.sofa.runtime.spring.SofaRuntimeContextAwareProcessor; import com.alipay.sofa.runtime.spring.callback.CloseApplicationContextCallBack; import com.alipay.sofa.runtime.spring.config.SofaRuntimeConfigurationProperties; import com.alipay.sofa.runtime.spring.health.DefaultRuntimeHealthChecker; import com.alipay.sofa.runtime.spring.health.MultiApplicationHealthIndicator; import com.alipay.sofa.runtime.spring.health.SofaComponentHealthChecker; import com.alipay.sofa.runtime.spring.health.SofaComponentHealthIndicator; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.boot.actuate.health.HealthIndicator; -import org.springframework.boot.autoconfigure.AutoConfigureAfter; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.*; - -import java.util.HashSet; -import java.util.ServiceLoader; -import java.util.Set; /** * @author xuanbei 18/3/17 @@ -64,84 +40,11 @@ @Configuration @EnableConfigurationProperties(SofaRuntimeConfigurationProperties.class) public class SofaRuntimeAutoConfiguration { - @Bean - @Role(BeanDefinition.ROLE_INFRASTRUCTURE) - public static BindingConverterFactory bindingConverterFactory() { - BindingConverterFactory bindingConverterFactory = new BindingConverterFactoryImpl(); - bindingConverterFactory - .addBindingConverters(getClassesByServiceLoader(BindingConverter.class)); - return bindingConverterFactory; - } - - @Bean - @Role(BeanDefinition.ROLE_INFRASTRUCTURE) - public static BindingAdapterFactory bindingAdapterFactory() { - BindingAdapterFactory bindingAdapterFactory = new BindingAdapterFactoryImpl(); - bindingAdapterFactory.addBindingAdapters(getClassesByServiceLoader(BindingAdapter.class)); - return bindingAdapterFactory; - } - - @Bean - @Role(BeanDefinition.ROLE_INFRASTRUCTURE) - public static SofaRuntimeContext sofaRuntimeContext(@Value("${" - + SofaBootInfraConstants.APP_NAME_KEY - + "}") String appName, - BindingConverterFactory bindingConverterFactory, - BindingAdapterFactory bindingAdapterFactory) { - ClientFactoryInternal clientFactoryInternal = new ClientFactoryImpl(); - SofaRuntimeManager sofaRuntimeManager = new StandardSofaRuntimeManager(appName, - SofaRuntimeAutoConfiguration.class.getClassLoader(), clientFactoryInternal); - sofaRuntimeManager.getComponentManager().registerComponentClient( - ReferenceClient.class, - new ReferenceClientImpl(sofaRuntimeManager.getSofaRuntimeContext(), - bindingConverterFactory, bindingAdapterFactory)); - sofaRuntimeManager.getComponentManager().registerComponentClient( - ServiceClient.class, - new ServiceClientImpl(sofaRuntimeManager.getSofaRuntimeContext(), - bindingConverterFactory, bindingAdapterFactory)); - SofaFramework.registerSofaRuntimeManager(sofaRuntimeManager); - return sofaRuntimeManager.getSofaRuntimeContext(); - - } - - @Bean - public static ServiceAnnotationBeanPostProcessor serviceAnnotationBeanPostProcessor(BindingAdapterFactory bindingAdapterFactory, - BindingConverterFactory bindingConverterFactory) { - return new ServiceAnnotationBeanPostProcessor(bindingAdapterFactory, - bindingConverterFactory); - } - - @Bean - public static ClientFactoryBeanPostProcessor clientFactoryBeanPostProcessor(SofaRuntimeContext sofaRuntimeContext) { - return new ClientFactoryBeanPostProcessor(sofaRuntimeContext.getClientFactory()); - } - - @Bean - public static ApplicationShutdownCallbackPostProcessor applicationShutdownCallbackPostProcessor(SofaRuntimeContext sofaRuntimeContext) { - return new ApplicationShutdownCallbackPostProcessor( - sofaRuntimeContext.getSofaRuntimeManager()); - } - - @Bean - public static SofaRuntimeContextAwareProcessor sofaRuntimeContextAwareProcessor(SofaRuntimeContext sofaRuntimeContext) { - return new SofaRuntimeContextAwareProcessor(sofaRuntimeContext); - } - @Bean public CloseApplicationContextCallBack closeApplicationContextCallBack() { return new CloseApplicationContextCallBack(); } - private static Set getClassesByServiceLoader(Class clazz) { - ServiceLoader serviceLoader = ServiceLoader.load(clazz); - - Set result = new HashSet<>(); - for (T t : serviceLoader) { - result.add(t); - } - return result; - } - @Configuration @ConditionalOnClass({ HealthChecker.class }) @AutoConfigureAfter(SofaRuntimeAutoConfiguration.class) diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/factory/AbstractContractFactoryBean.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/factory/AbstractContractFactoryBean.java index c7ec13459..10429d88a 100644 --- a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/factory/AbstractContractFactoryBean.java +++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/factory/AbstractContractFactoryBean.java @@ -16,15 +16,12 @@ */ package com.alipay.sofa.runtime.spring.factory; -import com.alipay.sofa.runtime.api.ServiceRuntimeException; -import com.alipay.sofa.runtime.constants.SofaRuntimeFrameworkConstants; -import com.alipay.sofa.runtime.service.binding.JvmBinding; -import com.alipay.sofa.runtime.spi.binding.Binding; -import com.alipay.sofa.runtime.spi.binding.BindingAdapterFactory; -import com.alipay.sofa.runtime.spi.component.SofaRuntimeContext; -import com.alipay.sofa.runtime.spi.service.BindingConverter; -import com.alipay.sofa.runtime.spi.service.BindingConverterContext; -import com.alipay.sofa.runtime.spi.service.BindingConverterFactory; +import java.io.ByteArrayInputStream; +import java.util.ArrayList; +import java.util.List; + +import javax.xml.parsers.DocumentBuilderFactory; + import org.springframework.beans.BeansException; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; @@ -34,10 +31,15 @@ import org.w3c.dom.Element; import org.xml.sax.InputSource; -import javax.xml.parsers.DocumentBuilderFactory; -import java.io.ByteArrayInputStream; -import java.util.ArrayList; -import java.util.List; +import com.alipay.sofa.runtime.api.ServiceRuntimeException; +import com.alipay.sofa.runtime.constants.SofaRuntimeFrameworkConstants; +import com.alipay.sofa.runtime.service.binding.JvmBinding; +import com.alipay.sofa.runtime.spi.binding.Binding; +import com.alipay.sofa.runtime.spi.binding.BindingAdapterFactory; +import com.alipay.sofa.runtime.spi.component.SofaRuntimeContext; +import com.alipay.sofa.runtime.spi.service.BindingConverter; +import com.alipay.sofa.runtime.spi.service.BindingConverterContext; +import com.alipay.sofa.runtime.spi.service.BindingConverterFactory; /** * Abstract Contract Factory Bean @@ -70,19 +72,24 @@ public abstract class AbstractContractFactoryBean implements InitializingBean, F protected BindingConverterFactory bindingConverterFactory; /** binding adapter factory */ protected BindingAdapterFactory bindingAdapterFactory; + /** way to create factory bean. api or xml*/ + protected boolean apiType; @Override public void afterPropertiesSet() throws Exception { List tempElements = new ArrayList<>(); - for (TypedStringValue element : elements) { - DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); - documentBuilderFactory.setNamespaceAware(true); - InputSource inputSource = new InputSource(new ByteArrayInputStream(element.getValue() - .getBytes())); - inputSource.setEncoding(documentEncoding); - Element node = documentBuilderFactory.newDocumentBuilder().parse(inputSource) - .getDocumentElement(); - tempElements.add(node); + if (elements != null) { + for (TypedStringValue element : elements) { + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory + .newInstance(); + documentBuilderFactory.setNamespaceAware(true); + InputSource inputSource = new InputSource(new ByteArrayInputStream(element + .getValue().getBytes())); + inputSource.setEncoding(documentEncoding); + Element node = documentBuilderFactory.newDocumentBuilder().parse(inputSource) + .getDocumentElement(); + tempElements.add(node); + } } sofaRuntimeContext = applicationContext.getBean( SofaRuntimeFrameworkConstants.SOFA_RUNTIME_CONTEXT_BEAN_ID, SofaRuntimeContext.class); @@ -92,7 +99,9 @@ public void afterPropertiesSet() throws Exception { bindingAdapterFactory = applicationContext.getBean( SofaRuntimeFrameworkConstants.BINDING_ADAPTER_FACTORY_BEAN_ID, BindingAdapterFactory.class); - this.bindings = parseBindings(tempElements, applicationContext, isInBinding()); + if (!apiType) { + this.bindings = parseBindings(tempElements, applicationContext, isInBinding()); + } doAfterPropertiesSet(); } @@ -144,16 +153,28 @@ public Class getInterfaceClass() { interfaceClass = this.getClass().getClassLoader().loadClass(interfaceType); } catch (ClassNotFoundException e) { throw new RuntimeException(e); + } catch (NullPointerException e) { + // No type found for shortcut FactoryBean instance: + // fall back to full creation of the FactoryBean instance + return null; } } return interfaceClass; } + public void setInterfaceClass(Class interfaceType) { + this.interfaceClass = interfaceType; + } + public List getBindings() { return bindings; } + public void setBindings(List bindings) { + this.bindings = bindings; + } + public void setUniqueId(String uniqueId) { this.uniqueId = uniqueId; } @@ -187,6 +208,14 @@ public boolean isSingleton() { return true; } + public boolean isApiType() { + return apiType; + } + + public void setApiType(boolean apiType) { + this.apiType = apiType; + } + /** * is in binding or not * diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/factory/ServiceFactoryBean.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/factory/ServiceFactoryBean.java index 3dcf52ea2..090e178c0 100644 --- a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/factory/ServiceFactoryBean.java +++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/factory/ServiceFactoryBean.java @@ -45,7 +45,7 @@ public ServiceFactoryBean(String interfaceType) { @Override protected void doAfterPropertiesSet() { - if (hasSofaServiceAnnotation()) { + if (!apiType && hasSofaServiceAnnotation()) { throw new ServiceRuntimeException( "Bean " + beanId + " of type " + ref.getClass() + " has already annotated by @SofaService," diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/listener/SofaRuntimeApplicationListener.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/listener/SofaRuntimeApplicationListener.java new file mode 100644 index 000000000..8b9b8cad9 --- /dev/null +++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/listener/SofaRuntimeApplicationListener.java @@ -0,0 +1,157 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.runtime.spring.listener; + +import java.util.HashSet; +import java.util.ServiceLoader; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory; +import org.springframework.boot.context.event.ApplicationPreparedEvent; +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.ApplicationListener; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.env.ConfigurableEnvironment; + +import com.alipay.sofa.infra.constants.SofaBootInfraConstants; +import com.alipay.sofa.runtime.SofaFramework; +import com.alipay.sofa.runtime.api.client.ReferenceClient; +import com.alipay.sofa.runtime.api.client.ServiceClient; +import com.alipay.sofa.runtime.client.impl.ClientFactoryImpl; +import com.alipay.sofa.runtime.component.impl.StandardSofaRuntimeManager; +import com.alipay.sofa.runtime.constants.SofaRuntimeFrameworkConstants; +import com.alipay.sofa.runtime.service.client.ReferenceClientImpl; +import com.alipay.sofa.runtime.service.client.ServiceClientImpl; +import com.alipay.sofa.runtime.service.impl.BindingAdapterFactoryImpl; +import com.alipay.sofa.runtime.service.impl.BindingConverterFactoryImpl; +import com.alipay.sofa.runtime.spi.binding.BindingAdapter; +import com.alipay.sofa.runtime.spi.binding.BindingAdapterFactory; +import com.alipay.sofa.runtime.spi.client.ClientFactoryInternal; +import com.alipay.sofa.runtime.spi.component.SofaRuntimeContext; +import com.alipay.sofa.runtime.spi.component.SofaRuntimeManager; +import com.alipay.sofa.runtime.spi.service.BindingConverter; +import com.alipay.sofa.runtime.spi.service.BindingConverterFactory; +import com.alipay.sofa.runtime.spring.*; +import com.alipay.sofa.runtime.spring.bean.SofaParameterNameDiscoverer; +import com.alipay.sofa.runtime.spring.configuration.SofaRuntimeAutoConfiguration; + +/** + * Prepare application context for refreshing. + * + * @author qilong.zql + * @since 3.1.0 + */ +public class SofaRuntimeApplicationListener implements + ApplicationListener { + /** + * Objects shared by multi spring application context + */ + private static BindingConverterFactory bindingConverterFactory; + private static BindingAdapterFactory bindingAdapterFactory; + private static SofaRuntimeContext sofaRuntimeContext; + private AtomicBoolean isInitiated = new AtomicBoolean(false); + + public static void initApplicationContext(ConfigurableApplicationContext applicationContext) { + ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory(); + applicationContext.addBeanFactoryPostProcessor(new ServiceBeanFactoryPostProcessor( + applicationContext, sofaRuntimeContext, bindingConverterFactory)); + beanFactory.registerSingleton( + SofaRuntimeFrameworkConstants.BINDING_CONVERTER_FACTORY_BEAN_ID, + bindingConverterFactory); + beanFactory.registerSingleton( + SofaRuntimeFrameworkConstants.BINDING_ADAPTER_FACTORY_BEAN_ID, bindingAdapterFactory); + beanFactory.registerSingleton(SofaRuntimeFrameworkConstants.SOFA_RUNTIME_CONTEXT_BEAN_ID, + sofaRuntimeContext); + + beanFactory.registerSingleton( + ReferenceAnnotationBeanPostProcessor.class.getCanonicalName(), + new ReferenceAnnotationBeanPostProcessor(applicationContext, sofaRuntimeContext, + bindingAdapterFactory, bindingConverterFactory)); + beanFactory.registerSingleton(ClientFactoryBeanPostProcessor.class.getCanonicalName(), + new ClientFactoryBeanPostProcessor(sofaRuntimeContext.getClientFactory())); + beanFactory + .registerSingleton( + ApplicationShutdownCallbackPostProcessor.class.getCanonicalName(), + new ApplicationShutdownCallbackPostProcessor(sofaRuntimeContext + .getSofaRuntimeManager())); + beanFactory.registerSingleton(SofaRuntimeContextAwareProcessor.class.getCanonicalName(), + new SofaRuntimeContextAwareProcessor(sofaRuntimeContext)); + + if (beanFactory instanceof AbstractAutowireCapableBeanFactory) { + ((AbstractAutowireCapableBeanFactory) beanFactory) + .setParameterNameDiscoverer(new SofaParameterNameDiscoverer(applicationContext + .getEnvironment())); + } + } + + @Override + public void onApplicationEvent(ApplicationPreparedEvent event) { + ConfigurableApplicationContext applicationContext = event.getApplicationContext(); + ConfigurableEnvironment environment = applicationContext.getEnvironment(); + if (isInitiated.compareAndSet(false, true)) { + bindingConverterFactory = bindingConverterFactory(); + bindingAdapterFactory = bindingAdapterFactory(); + sofaRuntimeContext = sofaRuntimeContext( + environment.getProperty(SofaBootInfraConstants.APP_NAME_KEY), + bindingConverterFactory, bindingAdapterFactory); + } + initApplicationContext(applicationContext); + } + + private BindingConverterFactory bindingConverterFactory() { + BindingConverterFactory bindingConverterFactory = new BindingConverterFactoryImpl(); + bindingConverterFactory + .addBindingConverters(getClassesByServiceLoader(BindingConverter.class)); + return bindingConverterFactory; + } + + private BindingAdapterFactory bindingAdapterFactory() { + BindingAdapterFactory bindingAdapterFactory = new BindingAdapterFactoryImpl(); + bindingAdapterFactory.addBindingAdapters(getClassesByServiceLoader(BindingAdapter.class)); + return bindingAdapterFactory; + } + + private SofaRuntimeContext sofaRuntimeContext(String appName, + BindingConverterFactory bindingConverterFactory, + BindingAdapterFactory bindingAdapterFactory) { + ClientFactoryInternal clientFactoryInternal = new ClientFactoryImpl(); + SofaRuntimeManager sofaRuntimeManager = new StandardSofaRuntimeManager(appName, + SofaRuntimeAutoConfiguration.class.getClassLoader(), clientFactoryInternal); + sofaRuntimeManager.getComponentManager().registerComponentClient( + ReferenceClient.class, + new ReferenceClientImpl(sofaRuntimeManager.getSofaRuntimeContext(), + bindingConverterFactory, bindingAdapterFactory)); + sofaRuntimeManager.getComponentManager().registerComponentClient( + ServiceClient.class, + new ServiceClientImpl(sofaRuntimeManager.getSofaRuntimeContext(), + bindingConverterFactory, bindingAdapterFactory)); + SofaFramework.registerSofaRuntimeManager(sofaRuntimeManager); + return sofaRuntimeManager.getSofaRuntimeContext(); + } + + private Set getClassesByServiceLoader(Class clazz) { + ServiceLoader serviceLoader = ServiceLoader.load(clazz); + + Set result = new HashSet<>(); + for (T t : serviceLoader) { + result.add(t); + } + return result; + } +} \ No newline at end of file diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/parser/AbstractContractDefinitionParser.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/parser/AbstractContractDefinitionParser.java index 5f9b72e13..9dce9d352 100644 --- a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/parser/AbstractContractDefinitionParser.java +++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/parser/AbstractContractDefinitionParser.java @@ -16,8 +16,16 @@ */ package com.alipay.sofa.runtime.spring.parser; -import com.alipay.sofa.infra.config.spring.namespace.spi.SofaBootTagNameSupport; -import com.alipay.sofa.runtime.api.ServiceRuntimeException; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.List; + +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + import org.springframework.beans.factory.config.TypedStringValue; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; @@ -25,14 +33,8 @@ import org.springframework.util.xml.DomUtils; import org.w3c.dom.Element; -import javax.xml.transform.OutputKeys; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.List; +import com.alipay.sofa.infra.config.spring.namespace.spi.SofaBootTagNameSupport; +import com.alipay.sofa.runtime.api.ServiceRuntimeException; /** * @author xuanbei 18/3/1 @@ -40,15 +42,18 @@ public abstract class AbstractContractDefinitionParser extends AbstractSingleBeanDefinitionParser implements SofaBootTagNameSupport { - private static final String INTERFACE_ELEMENT = "interface"; - private static final String INTERFACE_PROPERTY = "interfaceType"; - private static final String BEAN_ID_ELEMENT = "id"; - private static final String BEAN_ID_PROPERTY = "beanId"; - private static final String UNIQUE_ID_ELEMENT = "unique-id"; - private static final String UNIQUE_ID_PROPERTY = "uniqueId"; - private static final String ELEMENTS = "elements"; - private static final String REPEAT_REFER_LIMIT_ELEMENT = "repeatReferLimit"; - private static final String REPEAT_REFER_LIMIT_PROPERTY = "repeatReferLimit"; + public static final String INTERFACE_ELEMENT = "interface"; + public static final String INTERFACE_PROPERTY = "interfaceType"; + public static final String INTERFACE_CLASS_PROPERTY = "interfaceClass"; + public static final String BEAN_ID_ELEMENT = "id"; + public static final String BEAN_ID_PROPERTY = "beanId"; + public static final String UNIQUE_ID_ELEMENT = "unique-id"; + public static final String UNIQUE_ID_PROPERTY = "uniqueId"; + public static final String ELEMENTS = "elements"; + public static final String BINDINGS = "bindings"; + public static final String REPEAT_REFER_LIMIT_ELEMENT = "repeatReferLimit"; + public static final String REPEAT_REFER_LIMIT_PROPERTY = "repeatReferLimit"; + public static final String DEFINITION_BUILDING_API_TYPE = "apiType"; @Override protected void doParse(Element element, ParserContext parserContext, diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/parser/ReferenceDefinitionParser.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/parser/ReferenceDefinitionParser.java index 6f198e36a..56489d7d4 100644 --- a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/parser/ReferenceDefinitionParser.java +++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/parser/ReferenceDefinitionParser.java @@ -26,9 +26,9 @@ * @author xuanbei 18/3/1 */ public class ReferenceDefinitionParser extends AbstractContractDefinitionParser { - private static final String JVM_FIRST = "jvm-first"; - private static final String PROPERTY_JVM_FIRST = "jvmFirst"; - private static final String PROPERTY_LOAD_BALANCE = "loadBalance"; + public static final String JVM_FIRST = "jvm-first"; + public static final String PROPERTY_JVM_FIRST = "jvmFirst"; + public static final String PROPERTY_LOAD_BALANCE = "loadBalance"; @Override protected void doParseInternal(Element element, ParserContext parserContext, diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/parser/ServiceDefinitionParser.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/parser/ServiceDefinitionParser.java index 0d5a79da8..c52de5e8b 100644 --- a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/parser/ServiceDefinitionParser.java +++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/parser/ServiceDefinitionParser.java @@ -16,7 +16,10 @@ */ package com.alipay.sofa.runtime.spring.parser; +import com.alipay.sofa.runtime.spring.bean.SofaBeanNameGenerator; import com.alipay.sofa.runtime.spring.factory.ServiceFactoryBean; +import org.springframework.beans.factory.BeanDefinitionStoreException; +import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.ParserContext; import org.w3c.dom.Element; @@ -25,8 +28,8 @@ * @author xuanbei 18/3/1 */ public class ServiceDefinitionParser extends AbstractContractDefinitionParser { - private static final String REF = "ref"; - private static final String BEAN_ID = "beanId"; + public static final String REF = "ref"; + public static final String BEAN_ID = "beanId"; @Override protected void doParseInternal(Element element, ParserContext parserContext, @@ -47,8 +50,9 @@ protected Class getBeanClass(Element element) { } @Override - protected boolean shouldGenerateIdAsFallback() { - return true; + protected String resolveId(Element element, AbstractBeanDefinition definition, + ParserContext parserContext) throws BeanDefinitionStoreException { + return SofaBeanNameGenerator.generateSofaServiceBeanName(definition); } @Override diff --git a/runtime-sofa-boot-starter/src/main/resources/META-INF/spring.factories b/runtime-sofa-boot-starter/src/main/resources/META-INF/spring.factories index 23be9366f..df9ab1e2f 100644 --- a/runtime-sofa-boot-starter/src/main/resources/META-INF/spring.factories +++ b/runtime-sofa-boot-starter/src/main/resources/META-INF/spring.factories @@ -3,3 +3,7 @@ com.alipay.sofa.runtime.spring.initializer.SofaRuntimeSpringContextInitializer org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.alipay.sofa.runtime.spring.configuration.SofaRuntimeAutoConfiguration + + +org.springframework.context.ApplicationListener=\ +com.alipay.sofa.runtime.spring.listener.SofaRuntimeApplicationListener \ No newline at end of file diff --git a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/ark/SofaEventHandlerTest.java b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/ark/SofaEventHandlerTest.java index cfadfd23d..dce51ec01 100644 --- a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/ark/SofaEventHandlerTest.java +++ b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/ark/SofaEventHandlerTest.java @@ -16,6 +16,23 @@ */ package com.alipay.sofa.runtime.ark; +import static org.mockito.Mockito.when; + +import java.util.Collections; +import java.util.Set; + +import org.aopalliance.intercept.MethodInvocation; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; +import org.springframework.boot.context.event.ApplicationPreparedEvent; +import org.springframework.boot.test.util.EnvironmentTestUtils; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + import com.alipay.sofa.ark.spi.constant.Constants; import com.alipay.sofa.ark.spi.event.BizEvent; import com.alipay.sofa.ark.spi.model.Biz; @@ -31,17 +48,9 @@ import com.alipay.sofa.runtime.spi.component.SofaRuntimeManager; import com.alipay.sofa.runtime.spi.service.ServiceProxy; import com.alipay.sofa.runtime.spring.configuration.SofaRuntimeAutoConfiguration; -import mockit.*; -import org.aopalliance.intercept.MethodInvocation; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.springframework.boot.test.util.EnvironmentTestUtils; -import org.springframework.context.annotation.*; +import com.alipay.sofa.runtime.spring.listener.SofaRuntimeApplicationListener; -import java.util.Collections; -import java.util.Set; +import mockit.*; /** * @author qilong.zql @@ -77,12 +86,17 @@ public Biz getBiz(SofaRuntimeManager sofaRuntimeManager) { }; applicationContext = new AnnotationConfigApplicationContext(); + ApplicationPreparedEvent applicationPreparedEvent = Mockito + .mock(ApplicationPreparedEvent.class); + when(applicationPreparedEvent.getApplicationContext()).thenReturn(applicationContext); + EnvironmentTestUtils.addEnvironment(this.applicationContext, "com.alipay.sofa.boot.disableJvmFirst=true"); EnvironmentTestUtils.addEnvironment(this.applicationContext, "com.alipay.sofa.boot.skipJvmReferenceHealthCheck=true"); this.applicationContext.register(SofaRuntimeAutoConfiguration.class, SofaBootHealthCheckInitializer.class, XmlConfiguration.class); + new SofaRuntimeApplicationListener().onApplicationEvent(applicationPreparedEvent); this.applicationContext.refresh(); } diff --git a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/beans/impl/MethodBeanClassAnnotationSampleService.java b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/beans/impl/MethodBeanClassAnnotationSampleService.java new file mode 100644 index 000000000..240152494 --- /dev/null +++ b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/beans/impl/MethodBeanClassAnnotationSampleService.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.runtime.beans.impl; + +import com.alipay.sofa.runtime.api.annotation.SofaService; +import com.alipay.sofa.runtime.beans.service.SampleService; + +/** + * @author qilong.zql + * @sicne 3.1.0 + */ +@SofaService(uniqueId = "methodBeanClassAnnotationSampleService") +public class MethodBeanClassAnnotationSampleService implements SampleService { + @Override + public String service() { + return "MethodBeanClassAnnotationSampleService"; + } +} \ No newline at end of file diff --git a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/beans/impl/MethodBeanMethodAnnotationSampleService.java b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/beans/impl/MethodBeanMethodAnnotationSampleService.java new file mode 100644 index 000000000..70c81ac6c --- /dev/null +++ b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/beans/impl/MethodBeanMethodAnnotationSampleService.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.runtime.beans.impl; + +import com.alipay.sofa.runtime.beans.service.SampleService; + +/** + * @author qilong.zql + * @since 3.1.0 + */ +public class MethodBeanMethodAnnotationSampleService implements SampleService { + @Override + public String service() { + return "methodBeanMethodAnnotationSampleService"; + } +} \ No newline at end of file diff --git a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/beans/impl/ParameterAnnotationSampleService.java b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/beans/impl/ParameterAnnotationSampleService.java new file mode 100644 index 000000000..daaa4b153 --- /dev/null +++ b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/beans/impl/ParameterAnnotationSampleService.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.runtime.beans.impl; + +import com.alipay.sofa.runtime.beans.service.SampleService; + +/** + * @author qilong.zql + * @since 3.1.0 + */ +public class ParameterAnnotationSampleService implements SampleService { + private SampleService service1; + private SampleService service2; + private SampleService service3; + + public ParameterAnnotationSampleService(SampleService service1, SampleService service2, + SampleService service3) { + this.service1 = service1; + this.service2 = service2; + this.service3 = service3; + } + + @Override + public String service() { + return service1.service() + "@" + service2.service() + "@" + service3.service(); + } +} \ No newline at end of file diff --git a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/beans/impl/XmlAnnotationSampleService.java b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/beans/impl/XmlAnnotationSampleService.java new file mode 100644 index 000000000..e89fa20f0 --- /dev/null +++ b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/beans/impl/XmlAnnotationSampleService.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.runtime.beans.impl; + +import com.alipay.sofa.runtime.api.annotation.SofaService; +import com.alipay.sofa.runtime.beans.service.SampleService; + +/** + * @author qilong.zql + * @since 3.1.0 + */ +@SofaService(uniqueId = "${mix-xml-annotation-unique-id}") +public class XmlAnnotationSampleService implements SampleService { + @Override + public String service() { + return "XmlAnnotationSampleService"; + } +} \ No newline at end of file diff --git a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/beans/impl/XmlSampleService.java b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/beans/impl/XmlSampleService.java index be289f656..0d036044e 100644 --- a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/beans/impl/XmlSampleService.java +++ b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/beans/impl/XmlSampleService.java @@ -16,9 +16,7 @@ */ package com.alipay.sofa.runtime.beans.impl; -import com.alipay.sofa.runtime.api.annotation.SofaService; import com.alipay.sofa.runtime.beans.service.SampleService; -import org.springframework.stereotype.Component; /** * @author qilong.zql diff --git a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/IntegrationTest.java b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/IntegrationTest.java index af5c53285..c2a96d424 100644 --- a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/IntegrationTest.java +++ b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/IntegrationTest.java @@ -16,16 +16,24 @@ */ package com.alipay.sofa.runtime.integration; +import org.junit.Assert; +import org.junit.Test; +import org.springframework.boot.actuate.health.Status; +import org.springframework.context.ApplicationContext; + import com.alipay.sofa.healthcheck.configuration.HealthCheckConstants; import com.alipay.sofa.healthcheck.core.HealthChecker; +import com.alipay.sofa.runtime.beans.impl.XmlAnnotationSampleService; +import com.alipay.sofa.runtime.beans.impl.XmlSampleServiceWithUniqueId; import com.alipay.sofa.runtime.beans.service.SampleService; import com.alipay.sofa.runtime.integration.aop.SampleServiceAspect; import com.alipay.sofa.runtime.integration.base.AbstractTestBase; +import com.alipay.sofa.runtime.integration.features.SampleServiceAnnotationImplWithMethod; +import com.alipay.sofa.runtime.integration.features.SampleServiceAnnotationImplWithUniqueId; import com.alipay.sofa.runtime.spi.component.SofaRuntimeContext; -import org.junit.Assert; -import org.junit.Test; -import org.springframework.boot.actuate.health.Status; -import org.springframework.context.ApplicationContext; +import com.alipay.sofa.runtime.spring.bean.SofaBeanNameGenerator; +import com.alipay.sofa.runtime.spring.factory.ReferenceFactoryBean; +import com.alipay.sofa.runtime.spring.factory.ServiceFactoryBean; /** * @author qilong.zql @@ -97,5 +105,131 @@ public void testServiceAndReference() { Assert.assertEquals(awareTest.getServiceWithoutInterface().service(), "ServiceWithoutInterface"); Assert.assertTrue(SampleServiceAspect.isAspectInvoked()); + + Assert.assertEquals(awareTest.getXmlAnnotationSampleService().service(), + "XmlAnnotationSampleService"); + Assert.assertTrue(SampleServiceAspect.isAspectInvoked()); + } + + @Test + public void testFactoryBean() { + ApplicationContext applicationContext = awareTest.getApplicationContext(); + ServiceFactoryBean serviceFactoryBean; + ReferenceFactoryBean referenceFactoryBean; + + /** + * {@link com.alipay.sofa.runtime.beans.impl.MethodBeanClassAnnotationSampleService} + **/ + serviceFactoryBean = (ServiceFactoryBean) applicationContext + .getBean("&" + + SofaBeanNameGenerator.generateSofaServiceBeanName(SampleService.class, + "methodBeanClassAnnotationSampleService")); + Assert.assertTrue(serviceFactoryBean.isApiType()); + referenceFactoryBean = (ReferenceFactoryBean) applicationContext + .getBean("&" + + SofaBeanNameGenerator.generateSofaReferenceBeanName(SampleService.class, + "methodBeanClassAnnotationSampleService")); + Assert.assertTrue(serviceFactoryBean.isApiType()); + + /** + * {@link com.alipay.sofa.runtime.integration.base.AbstractTestBase.IntegrationTestConfiguration.BeforeConfiguration} + */ + serviceFactoryBean = (ServiceFactoryBean) applicationContext + .getBean("&" + + SofaBeanNameGenerator.generateSofaServiceBeanName(SampleService.class, + "methodBeanMethodAnnotationSampleService")); + Assert.assertTrue(serviceFactoryBean.isApiType()); + referenceFactoryBean = (ReferenceFactoryBean) applicationContext + .getBean("&" + + SofaBeanNameGenerator.generateSofaReferenceBeanName(SampleService.class, + "methodBeanMethodAnnotationSampleService")); + Assert.assertTrue(referenceFactoryBean.isApiType()); + + /** + * {@link XmlAnnotationSampleService} + */ + serviceFactoryBean = (ServiceFactoryBean) applicationContext + .getBean("&" + + SofaBeanNameGenerator.generateSofaServiceBeanName(SampleService.class, + "xmlAnnotationSampleService")); + Assert.assertTrue(serviceFactoryBean.isApiType()); + referenceFactoryBean = (ReferenceFactoryBean) applicationContext + .getBean("&" + + SofaBeanNameGenerator.generateSofaReferenceBeanName(SampleService.class, + "xmlAnnotationSampleService")); + Assert.assertTrue(referenceFactoryBean.isApiType()); + + /** + * {@link com.alipay.sofa.runtime.beans.impl.XmlSampleService} + */ + serviceFactoryBean = (ServiceFactoryBean) applicationContext + .getBean("&" + + SofaBeanNameGenerator.generateSofaServiceBeanName(SampleService.class, "")); + Assert.assertFalse(serviceFactoryBean.isApiType()); + referenceFactoryBean = (ReferenceFactoryBean) applicationContext.getBean("&xmlReference"); + Assert.assertFalse(referenceFactoryBean.isApiType()); + + /** + * {@link XmlSampleServiceWithUniqueId} + */ + serviceFactoryBean = (ServiceFactoryBean) applicationContext + .getBean("&" + + SofaBeanNameGenerator + .generateSofaServiceBeanName(SampleService.class, "xml")); + Assert.assertFalse(serviceFactoryBean.isApiType()); + referenceFactoryBean = (ReferenceFactoryBean) applicationContext + .getBean("&xmlReferenceWithUniqueId"); + Assert.assertFalse(referenceFactoryBean.isApiType()); + + /** + * {@link SampleServiceAnnotationImplWithMethod} + */ + serviceFactoryBean = (ServiceFactoryBean) applicationContext + .getBean("&" + + SofaBeanNameGenerator.generateSofaServiceBeanName(SampleService.class, + "method")); + Assert.assertTrue(serviceFactoryBean.isApiType()); + + /** + * {@link SampleServiceAnnotationImplWithUniqueId} + */ + serviceFactoryBean = (ServiceFactoryBean) applicationContext + .getBean("&" + + SofaBeanNameGenerator.generateSofaServiceBeanName(SampleService.class, + "annotation")); + Assert.assertTrue(serviceFactoryBean.isApiType()); + } + + @Test + public void testParameterSofaReference() { + ApplicationContext applicationContext = awareTest.getApplicationContext(); + SampleService parameterAnnotationSampleService = (SampleService) applicationContext + .getBean("parameterAnnotationSampleService"); + + SampleService xmlAnnotationSampleService = (SampleService) applicationContext + .getBean(SofaBeanNameGenerator.generateSofaReferenceBeanName(SampleService.class, + "xmlAnnotationSampleService")); + + SampleService methodBeanClassAnnotationSampleService = (SampleService) applicationContext + .getBean(SofaBeanNameGenerator.generateSofaReferenceBeanName(SampleService.class, + "methodBeanClassAnnotationSampleService")); + + SampleService methodBeanMethodAnnotationSampleService = (SampleService) applicationContext + .getBean(SofaBeanNameGenerator.generateSofaReferenceBeanName(SampleService.class, + "methodBeanMethodAnnotationSampleService")); + + Assert.assertEquals( + parameterAnnotationSampleService.service(), + xmlAnnotationSampleService.service() + "@" + + methodBeanClassAnnotationSampleService.service() + "@" + + methodBeanMethodAnnotationSampleService.service()); + } + + @Test + public void testServiceFactoryBean() { + ApplicationContext applicationContext = awareTest.getApplicationContext(); + applicationContext.getBeansOfType(ServiceFactoryBean.class).forEach((key, value) -> { + Assert.assertTrue(key.startsWith("&ServiceFactoryBean#")); + }); } } \ No newline at end of file diff --git a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/TestSofaServiceAndReferenceException.java b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/TestSofaServiceAndReferenceException.java new file mode 100644 index 000000000..8297b02f5 --- /dev/null +++ b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/TestSofaServiceAndReferenceException.java @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.runtime.integration; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.io.FileUtils; +import org.junit.Assert; +import org.junit.Test; +import org.springframework.context.annotation.Bean; + +import com.alipay.sofa.runtime.api.annotation.SofaReference; +import com.alipay.sofa.runtime.api.annotation.SofaReferenceBinding; +import com.alipay.sofa.runtime.api.annotation.SofaService; +import com.alipay.sofa.runtime.beans.impl.SampleServiceImpl; +import com.alipay.sofa.runtime.beans.service.SampleService; +import com.alipay.sofa.runtime.integration.base.TestBase; +import com.alipay.sofa.runtime.spring.bean.SofaBeanNameGenerator; + +/** + * @author qilong.zql + * @since 3.1.0 + */ +public class TestSofaServiceAndReferenceException extends TestBase { + + @Test + public void testSofaReferenceOnMethodParameter() { + Map properties = new HashMap<>(); + properties.put("spring.application.name", "runtime-test"); + Throwable throwable = null; + try { + initApplicationContext(properties, TestSofaReferenceConfiguration.class); + } catch (Throwable t) { + throwable = t; + } + Assert.assertTrue(throwable instanceof IllegalArgumentException); + Assert.assertEquals("Only jvm type of @SofaReference on parameter is supported.", + throwable.getMessage()); + } + + @Test + public void testMultiSofaServiceWithSameInterfaceAndUniqueId() throws IOException { + File sofaLog = new File(System.getProperty("user.home") + File.separator + "logs" + + File.separator + "sofa-runtime" + File.separator + + "common-error.log"); + FileUtils.write(sofaLog, "", System.getProperty("file.encoding")); + Map properties = new HashMap<>(); + properties.put("spring.application.name", "runtime-test"); + initApplicationContext(properties, TestSofaServiceConfiguration.class); + String content = FileUtils.readFileToString(sofaLog, System.getProperty("file.encoding")); + Assert.assertTrue(content.contains("SofaService was already registered: " + + SofaBeanNameGenerator.generateSofaServiceBeanName( + SampleService.class, ""))); + } + + static class TestSofaReferenceConfiguration { + @Bean + SampleService sampleService(@SofaReference(uniqueId = "rpc", binding = @SofaReferenceBinding(bindingType = "bolt")) SampleService sampleService) { + return new SampleServiceImpl("test"); + } + } + + static class TestSofaServiceConfiguration { + @Bean + @SofaService + SampleService sampleService() { + return new SampleServiceImpl("test"); + } + + @Bean + @SofaService + SampleService duplicateSampleService() { + return new SampleServiceImpl("test"); + } + } + +} \ No newline at end of file diff --git a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/base/AbstractTestBase.java b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/base/AbstractTestBase.java index cb27451ad..6b8529623 100644 --- a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/base/AbstractTestBase.java +++ b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/base/AbstractTestBase.java @@ -16,37 +16,43 @@ */ package com.alipay.sofa.runtime.integration.base; +import java.util.HashMap; +import java.util.Map; + +import org.junit.Before; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.ImportResource; + import com.alipay.sofa.ark.spi.model.Biz; +import com.alipay.sofa.runtime.api.annotation.SofaReference; +import com.alipay.sofa.runtime.api.annotation.SofaService; +import com.alipay.sofa.runtime.beans.impl.MethodBeanClassAnnotationSampleService; +import com.alipay.sofa.runtime.beans.impl.MethodBeanMethodAnnotationSampleService; +import com.alipay.sofa.runtime.beans.impl.ParameterAnnotationSampleService; +import com.alipay.sofa.runtime.beans.service.SampleService; import com.alipay.sofa.runtime.integration.features.AwareTest; import com.alipay.sofa.runtime.integration.invoke.DynamicJvmServiceProxyFinder; import com.alipay.sofa.runtime.spi.component.SofaRuntimeManager; + import mockit.Mock; import mockit.MockUp; import mockit.Mocked; import mockit.NonStrictExpectations; -import org.junit.Before; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.test.util.EnvironmentTestUtils; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.ImportResource; - -import java.util.HashMap; -import java.util.Map; /** * @author qilong.zql * @since 2.3.1 */ -public abstract class AbstractTestBase { - - public AwareTest awareTest; +public abstract class AbstractTestBase extends TestBase { - public AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); + public AwareTest awareTest; @Mocked - public Biz biz; + public Biz biz; @Before public void before() { @@ -66,29 +72,40 @@ public Biz getBiz(SofaRuntimeManager sofaRuntimeManager) { Map properties = new HashMap<>(); properties.put("spring.application.name", "runtime-test"); + properties.put("mix-xml-annotation-unique-id", "xmlAnnotationSampleService"); initApplicationContext(properties, IntegrationTestConfiguration.class); awareTest = applicationContext.getBean(AwareTest.class); } - protected void initApplicationContext(Map properties, - Class... annotatedClasses) { - for (Map.Entry property : properties.entrySet()) { - EnvironmentTestUtils.addEnvironment(this.applicationContext, - buildProperty(property.getKey(), property.getValue())); - } - - this.applicationContext.register(annotatedClasses); - this.applicationContext.refresh(); - } - - private String buildProperty(String key, Object value) { - return key + ":" + value; - } - @Configuration @EnableAutoConfiguration @ImportResource({ "classpath*:META-INF/spring/*.xml" }) @ComponentScan({ "com.alipay.sofa.runtime.integration.features" }) static class IntegrationTestConfiguration { + @Configuration + static class BeforeConfiguration { + @Bean + MethodBeanClassAnnotationSampleService methodBeanClassAnnotationSampleService() { + return new MethodBeanClassAnnotationSampleService(); + } + + @Bean({ "name1", "name2" }) + @SofaService(uniqueId = "methodBeanMethodAnnotationSampleService") + SampleService methodBeanMethodAnnotationSampleService() { + return new MethodBeanMethodAnnotationSampleService(); + } + } + + @Configuration + @AutoConfigureAfter(BeforeConfiguration.class) + static class AfterConfiguration { + @Bean + SampleService parameterAnnotationSampleService(@SofaReference(uniqueId = "${mix-xml-annotation-unique-id}") SampleService service1, + @SofaReference(uniqueId = "methodBeanClassAnnotationSampleService") SampleService service2, + @SofaReference(uniqueId = "methodBeanMethodAnnotationSampleService") SampleService service3) { + return new ParameterAnnotationSampleService(service1, service2, service3); + } + } } + } \ No newline at end of file diff --git a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/base/TestBase.java b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/base/TestBase.java new file mode 100644 index 000000000..e33317dd1 --- /dev/null +++ b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/base/TestBase.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.runtime.integration.base; + +import static org.mockito.Mockito.when; + +import java.util.Map; + +import org.mockito.Mockito; +import org.springframework.boot.context.event.ApplicationPreparedEvent; +import org.springframework.boot.test.util.EnvironmentTestUtils; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + +import com.alipay.sofa.runtime.spring.listener.SofaRuntimeApplicationListener; + +/** + * @author qilong.zql + * @since 3.1.0 + */ +public abstract class TestBase { + public AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); + + protected void initApplicationContext(Map properties, + Class... annotatedClasses) { + for (Map.Entry property : properties.entrySet()) { + EnvironmentTestUtils.addEnvironment(this.applicationContext, + buildProperty(property.getKey(), property.getValue())); + } + + ApplicationPreparedEvent applicationPreparedEvent = Mockito + .mock(ApplicationPreparedEvent.class); + when(applicationPreparedEvent.getApplicationContext()).thenReturn(applicationContext); + new SofaRuntimeApplicationListener().onApplicationEvent(applicationPreparedEvent); + + this.applicationContext.register(annotatedClasses); + this.applicationContext.refresh(); + } + + private String buildProperty(String key, Object value) { + return key + ":" + value; + } +} \ No newline at end of file diff --git a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/features/AwareTest.java b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/features/AwareTest.java index ab1f2ad41..00ab7f0e7 100644 --- a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/features/AwareTest.java +++ b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/features/AwareTest.java @@ -59,6 +59,9 @@ public class AwareTest implements ClientFactoryAware, ApplicationContextAware, I @SofaReference(uniqueId = "annotation") private ServiceWithoutInterface serviceWithoutInterface; + @SofaReference(uniqueId = "${mix-xml-annotation-unique-id}") + private SampleService xmlAnnotationSampleService; + private SampleService sampleServicePublishedByServiceClient; private SampleService sampleServiceAnnotationImplWithMethod; @@ -120,6 +123,10 @@ public SampleService getSampleServiceAnnotationImplWithMethod() { return sampleServiceAnnotationImplWithMethod; } + public SampleService getXmlAnnotationSampleService() { + return xmlAnnotationSampleService; + } + public ServiceWithoutInterface getServiceWithoutInterface() { return serviceWithoutInterface; } diff --git a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/spring/config/SofaRuntimePropertiesTest.java b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/spring/config/SofaRuntimePropertiesTest.java index d6152c919..147e33127 100644 --- a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/spring/config/SofaRuntimePropertiesTest.java +++ b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/spring/config/SofaRuntimePropertiesTest.java @@ -18,13 +18,18 @@ import com.alipay.sofa.runtime.SofaRuntimeProperties; import com.alipay.sofa.runtime.spring.configuration.SofaRuntimeAutoConfiguration; +import com.alipay.sofa.runtime.spring.listener.SofaRuntimeApplicationListener; import org.junit.After; +import org.junit.Before; import org.junit.Test; +import org.mockito.Mockito; +import org.springframework.boot.context.event.ApplicationPreparedEvent; import org.springframework.boot.test.util.EnvironmentTestUtils; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.when; public class SofaRuntimePropertiesTest { @@ -35,6 +40,14 @@ public void closeContext() { this.applicationContext.close(); } + @Before + public void before() { + ApplicationPreparedEvent applicationPreparedEvent = Mockito + .mock(ApplicationPreparedEvent.class); + when(applicationPreparedEvent.getApplicationContext()).thenReturn(applicationContext); + new SofaRuntimeApplicationListener().onApplicationEvent(applicationPreparedEvent); + } + @Test public void testDisableJvmFirstProperty() { assertFalse(SofaRuntimeProperties.isDisableJvmFirst(applicationContext.getClassLoader())); diff --git a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/spring/health/SofaComponentHealthCheckerTest.java b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/spring/health/SofaComponentHealthCheckerTest.java index 030cfdac1..110733224 100644 --- a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/spring/health/SofaComponentHealthCheckerTest.java +++ b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/spring/health/SofaComponentHealthCheckerTest.java @@ -18,12 +18,18 @@ import com.alipay.sofa.healthcheck.configuration.HealthCheckConstants; import com.alipay.sofa.runtime.spring.configuration.SofaRuntimeAutoConfiguration; +import com.alipay.sofa.runtime.spring.listener.SofaRuntimeApplicationListener; import org.junit.After; import org.junit.Assert; +import org.junit.Before; import org.junit.Test; +import org.mockito.Mockito; +import org.springframework.boot.context.event.ApplicationPreparedEvent; import org.springframework.boot.test.util.EnvironmentTestUtils; import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import static org.mockito.Mockito.when; + /** * @author abby.zh * @since 2.4.10 @@ -37,6 +43,14 @@ public void closeContext() { this.applicationContext.close(); } + @Before + public void before() { + ApplicationPreparedEvent applicationPreparedEvent = Mockito + .mock(ApplicationPreparedEvent.class); + when(applicationPreparedEvent.getApplicationContext()).thenReturn(applicationContext); + new SofaRuntimeApplicationListener().onApplicationEvent(applicationPreparedEvent); + } + @Test public void testDefaultConfig() { this.applicationContext.register(SofaRuntimeAutoConfiguration.class); diff --git a/runtime-sofa-boot-starter/src/test/resources/META-INF/spring/test-service.xml b/runtime-sofa-boot-starter/src/test/resources/META-INF/spring/test-service.xml index d8e9de43f..c7a05f419 100644 --- a/runtime-sofa-boot-starter/src/test/resources/META-INF/spring/test-service.xml +++ b/runtime-sofa-boot-starter/src/test/resources/META-INF/spring/test-service.xml @@ -13,6 +13,7 @@ +