Skip to content

Try to get the Spring application context in the Dubbo Filter. #13508

@LJiangTao

Description

@LJiangTao

Scenario

We need to call a remote service in Filter to get the address of the service that load balance can call later.

Solution

@Activate(group = { CommonConstants.CONSUMER })
public class DeviceSelectorFilter implements Filter {



  public DeviceSelectorFilter(ApplicationModel model) {

    // Get Extension that contain Spring Application Context.
    SpringExtensionInjector springExtensionInjector = SpringExtensionInjector.get(
      model
    );

    // Now you get Spring Application Context.
    var applicationContext = springExtensionInjector.getContext();
  }

  @Override
  public Result invoke(Invoker<?> invoker, Invocation invocation)
    throws RpcException {
    // YOUR BUSNIESS LOGIC

  }
}

I use JDK-17, If you are using JDK-1.8.0, replace [var] to target class.

Constructor Instantiates

The Dubbo framework instantiates the filter itself, therefore interfaces related to Spring (such as BeanFactoryAware) are not supported. However, Dubbo provides injectable objects that must implement ScopeModel. such as :

  • org.apache.dubbo.rpc.model.ApplicationModel
  • org.apache.dubbo.rpc.model.FrameworkModel
  • org.apache.dubbo.rpc.model.ModuleModel

Other types do not support constructor injection. more details in org.apache.dubbo.common.beans.support.InstantiationStrategy Class.

@SuppressWarnings("unchecked")
public <T> T instantiate(Class<T> type) throws ReflectiveOperationException {

   // HIDE SOME CODE.

    // 2. use matched constructor if found
    List<Constructor<?>> matchedConstructors = new ArrayList<>();
    Constructor<?>[] declaredConstructors = type.getConstructors();
    for (Constructor<?> constructor : declaredConstructors) {

        // HERE! CHECK Constrcutor Arguments Object must implement ScopeModel!!!
        if (isMatched(constructor)) {
            matchedConstructors.add(constructor);
        }
    }
 
   // HIDE SOME CODE

    // create instance with arguments
    Class<?>[] parameterTypes = targetConstructor.getParameterTypes();
    Object[] args = new Object[parameterTypes.length];
    for (int i = 0; i < parameterTypes.length; i++) {
        // get target scopeModel object.
        args[i] = getArgumentValueForType(parameterTypes[i]);
    }
    return (T) targetConstructor.newInstance(args);
}

private boolean isMatched(Constructor<?> constructor) {
    for (Class<?> parameterType : constructor.getParameterTypes()) {
        if (!isSupportedConstructorParameterType(parameterType)) {
            return false;
        }
    }
    return true;
}


private boolean isSupportedConstructorParameterType(Class<?> parameterType) {
    return ScopeModel.class.isAssignableFrom(parameterType);
}

}

Try Get BeanFactory by ApplicationModel

ApplicationModel has getBeanFactory() method. but it return ScopeBeanFactory , that is internal BeanFactory of Dubbo, not Spring BeanFactory. ScopeBeanFactory contains only objects instantiated by dubbo itself, not objects from the spring BeanFactory.

THIS WAY IS DEAD END

  • org.apache.dubbo.rpc.model.ScopeModel
public abstract class ScopeModel implements ExtensionAccessor {

    private volatile ScopeBeanFactory beanFactory;


    public ScopeBeanFactory getBeanFactory() {
        return beanFactory;
    }

}

Thanks

Very thankful of Dubbo Community's Liu Jun, Thanks !

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions