dubbo自定义filter,报No such extension xxxFilter for filter/com.alibaba.dubbo.rpc.Filter错误

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Andyzhu_2005/article/details/83997806

前言

最近在学习dubbo的filter时候,根据dubbo的开发手册,自定义了一个filter,然后配置,结果控制台报 No such extension xxxFilter for filter/com.alibaba.dubbo.rpc.Filter错误。特此记录。

故障复现

首先定义filter,需要实现com.alibaba.dubbo.rpc.Filter接口

/**  
 * All rights Reserved, Designed By www.tydic.com
 * @Title:  ExporterListener.java   
 * @Package com.andy.exporterListener   
 * @Description:    TODO(用一句话描述该文件做什么)   
 * @author: andyzhu    
 * @date:   2018年11月7日 下午9:50:50   
 * @version V1.0 
 * @Copyright: 2018 www.acc.com Inc. All rights reserved. 
 * 注意:禁止外泄以及用于其他的商业目
 */  
package com.andy.exporterfilter;

import com.alibaba.dubbo.common.logger.Logger;
import com.alibaba.dubbo.common.logger.LoggerFactory;
import com.alibaba.dubbo.rpc.Exporter;
import com.alibaba.dubbo.rpc.Filter;
import com.alibaba.dubbo.rpc.Invocation;
import com.alibaba.dubbo.rpc.Invoker;
import com.alibaba.dubbo.rpc.Result;
import com.alibaba.dubbo.rpc.RpcException;

/**
 * @author sks,这个andyfilter就是简单的验证filter功能
 *
 */



public class AndyFilter implements Filter{
	
	private static Logger log = LoggerFactory
			.getLogger(AndyFilter.class);

	/* (non-Javadoc)
	 * @see com.alibaba.dubbo.rpc.Filter#invoke(com.alibaba.dubbo.rpc.Invoker, com.alibaba.dubbo.rpc.Invocation)
	 */
	@Override
	public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
		// TODO Auto-generated method stub
		log.info(("自定义过滤器--》"+invoker.getInterface()));
		return  invoker.invoke(invocation);
	}



}

对上述filter进行配置(项目使用maven进行构建)

在resources目录下新建META-INF文件夹,然后建立子文件夹dubbo,最后新建文件com.alibaba.dubbo.rpc.Filter,文件内容为

andyFilter=com.andy.exporterfilter.AndyFilter

在配置文件中使用这个filter

<dubbo:service retries="0" interface="cn.andy.dubbo.DataService" ref="dataServiceImpl"filter="andyFilter" />

大功告成,但是在启动工程时候失败,报如下错误:

2018-11-12 19:38:52,038  WARN [AbstractApplicationContext.java:546] : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cn.andy.dubbo.DataService': Error setting property values; nested exception is org.springframework.beans.PropertyBatchUpdateException; nested PropertyAccessExceptions (1) are:
PropertyAccessException 1: org.springframework.beans.MethodInvocationException: Property 'filter' threw exception; nested exception is java.lang.IllegalStateException: No such extension andyFilter for filter/com.alibaba.dubbo.rpc.Filter
2018-11-12 19:38:52,040 ERROR [DubboProviderMain.java:38] : == DubboProvider context start error:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cn.andy.dubbo.DataService': Error setting property values; nested exception is org.springframework.beans.PropertyBatchUpdateException; nested PropertyAccessExceptions (1) are:
PropertyAccessException 1: org.springframework.beans.MethodInvocationException: Property 'filter' threw exception; nested exception is java.lang.IllegalStateException: No such extension andyFilter for filter/com.alibaba.dubbo.rpc.Filter

大意就是找不到这个andyFilter。

原因分析

根据错误提示,大概率原因是找不到andyFilter。根据dubbo的spi扩展机制原理。这个andyFilter是在进行filter接口的扩展加载实现时候导入进来的,那么我们就从那里分析。
所有的接口的spi扩展都从下面这个方法得到相应的接口实现。我们在这个方法里打上断点,当发现ExtensionLoader的type是com.alibaba.dubbo.rpc.Filter时候(即开始加载filter的接口的相关的所有扩展实现),在路径是META-INF/dubbo/com.alibaba.dubbo.rpc.Filter时候时(我们自定义的AndyFilter就在这个目录下的文件中),下面的方法中 urls = classLoader.getResources(fileName);得到的结果为空,即在这个目录下找不到com.alibaba.dubbo.rpc.Filter这个文件。

    private void loadFile(Map<String, Class<?>> extensionClasses, String dir) {
        String fileName = dir + type.getName();
        try {
            Enumeration<java.net.URL> urls;
            ClassLoader classLoader = findClassLoader();
            if (classLoader != null) {
                urls = classLoader.getResources(fileName);
            } else {
                urls = ClassLoader.getSystemResources(fileName);
            }
            if (urls != null) {
                while (urls.hasMoreElements()) {
                    java.net.URL url = urls.nextElement();
                    try {
                        BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8"));
                        try {
                            String line = null;
                            while ((line = reader.readLine()) != null) {
                                final int ci = line.indexOf('#');
                                if (ci >= 0) line = line.substring(0, ci);
                                line = line.trim();
                                if (line.length() > 0) {
                                    try {
                                        String name = null;
                                        int i = line.indexOf('=');
                                        if (i > 0) {
                                            name = line.substring(0, i).trim();
                                            line = line.substring(i + 1).trim();
                                        }
                                        if (line.length() > 0) {
                                            Class<?> clazz = Class.forName(line, true, classLoader);
                                            if (! type.isAssignableFrom(clazz)) {
                                                throw new IllegalStateException("Error when load extension class(interface: " +
                                                        type + ", class line: " + clazz.getName() + "), class " 
                                                        + clazz.getName() + "is not subtype of interface.");
                                            }
                                            if (clazz.isAnnotationPresent(Adaptive.class)) {
                                                if(cachedAdaptiveClass == null) {
                                                    cachedAdaptiveClass = clazz;
                                                } else if (! cachedAdaptiveClass.equals(clazz)) {
                                                    throw new IllegalStateException("More than 1 adaptive class found: "
                                                            + cachedAdaptiveClass.getClass().getName()
                                                            + ", " + clazz.getClass().getName());
                                                }
                                            } else {
                                                try {
                                                    clazz.getConstructor(type);
                                                    Set<Class<?>> wrappers = cachedWrapperClasses;
                                                    if (wrappers == null) {
                                                        cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
                                                        wrappers = cachedWrapperClasses;
                                                    }
                                                    wrappers.add(clazz);
                                                } catch (NoSuchMethodException e) {
                                                    clazz.getConstructor();
                                                    if (name == null || name.length() == 0) {
                                                        name = findAnnotationName(clazz);
                                                        if (name == null || name.length() == 0) {
                                                            if (clazz.getSimpleName().length() > type.getSimpleName().length()
                                                                    && clazz.getSimpleName().endsWith(type.getSimpleName())) {
                                                                name = clazz.getSimpleName().substring(0, clazz.getSimpleName().length() - type.getSimpleName().length()).toLowerCase();
                                                            } else {
                                                                throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + url);
                                                            }
                                                        }
                                                    }
                                                    String[] names = NAME_SEPARATOR.split(name);
                                                    if (names != null && names.length > 0) {
                                                        Activate activate = clazz.getAnnotation(Activate.class);
                                                        if (activate != null) {
                                                            cachedActivates.put(names[0], activate);
                                                        }
                                                        for (String n : names) {
                                                            if (! cachedNames.containsKey(clazz)) {
                                                                cachedNames.put(clazz, n);
                                                            }
                                                            Class<?> c = extensionClasses.get(n);
                                                            if (c == null) {
                                                                extensionClasses.put(n, clazz);
                                                            } else if (c != clazz) {
                                                                throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    } catch (Throwable t) {
                                        IllegalStateException e = new IllegalStateException("Failed to load extension class(interface: " + type + ", class line: " + line + ") in " + url + ", cause: " + t.getMessage(), t);
                                        exceptions.put(line, e);
                                    }
                                }
                            } // end of while read lines
                        } finally {
                            reader.close();
                        }
                    } catch (Throwable t) {
                        logger.error("Exception when load extension class(interface: " +
                                            type + ", class file: " + url + ") in " + url, t);
                    }
                } // end of while urls
            }
        } catch (Throwable t) {
            logger.error("Exception when load extension class(interface: " +
                    type + ", description file: " + fileName + ").", t);
        }
    }

既然找不到这个文件,那么我们就直接进入编译后的目录下,取找找是否真的没有这个文件。

在这里插入图片描述
果然,连dubbo这个文件夹都没有,更别说com.alibaba.dubbo.rpc.Filter这个文件了。
那么问题出在哪呢?
考虑一会,大概率是在使用maven构建工程时候,出了问题。
翻看maven关于resources的配置

<resources>
			<resource>
				<targetPath>${project.build.directory}/classes</targetPath>
				<directory>src/main/resources</directory>
				<filtering>true</filtering>
				<includes>
					<include>**/*.xml</include>
					<include>**/*.properties</include>
				</includes>
			</resource>

果然,maven在进行相关的resoures的资源文件配置时,只包含了后缀名是xml和properities的文件,其他的文件都给过滤了!而我们的文件名没有后缀名(或者可以理解为后缀名就是Filter).
找到了问题,那就把所有的后缀名的文件都给放到编译后的resources文件夹。

<resources>
			<resource>
				<targetPath>${project.build.directory}/classes</targetPath>
				<directory>src/main/resources</directory>
				<filtering>true</filtering>
				<includes>
					<include>**/*.xml</include>
					<include>**/*.*</include>
				</includes>
			</resource>

,重新进行maven clean,和maven install。然后运行:没问题啦!

filter的源码分析

filter的执行在ProtocolFilterWrapper类中。

public class ProtocolFilterWrapper implements Protocol {

    private final Protocol protocol;

    public ProtocolFilterWrapper(Protocol protocol){
        if (protocol == null) {
            throw new IllegalArgumentException("protocol == null");
        }
        this.protocol = protocol;
    }
    }

ProtocolFilterWrapper 实现了Protocol 接口,而且其构造方法中,有Protocol 这个参数。说明其是一个wrapper包装类。我们以dubboProtocol为例。在实例化dubboProtocol时候,,会对Protocol先进行依赖注入,然后进行Wrapper包装,最后返回被修改过的Protocol。比如,dubboprotocol包装经过了ProtocolFilterWrapper,ProtocolListenerWrapper。这里先不考虑ProtocolListenerWrapper。
那么,这个类在执行export的方法会执行什么逻辑呢?

    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    //当protocol是registerProtocol时候,直接执行,不经过filter
        if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
            return protocol.export(invoker);
        }
        //重点是buildInvokerChain方法。
        return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));
    }

重点是buildInvokerChain方法。这个方法相当于在发布(或者获取)任务时候,会先经过这些过滤器,完成过滤器操作后,再执行真正的方法。

    private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
        Invoker<T> last = invoker;
        List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
        if (filters.size() > 0) {
            for (int i = filters.size() - 1; i >= 0; i --) {
                final Filter filter = filters.get(i);
                final Invoker<T> next = last;
                last = new Invoker<T>() {

                    public Class<T> getInterface() {
                        return invoker.getInterface();
                    }

                    public URL getUrl() {
                        return invoker.getUrl();
                    }

                    public boolean isAvailable() {
                        return invoker.isAvailable();
                    }

                    public Result invoke(Invocation invocation) throws RpcException {
                        return filter.invoke(next, invocation);
                    }

                    public void destroy() {
                        invoker.destroy();
                    }

                    @Override
                    public String toString() {
                        return invoker.toString();
                    }
                };
            }
        }
        return last;
    }

假设有一个待发布的invokerA,和两个过滤器
在这里插入图片描述
根据上面的buildInvokerChain方法,执行for循环操作。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

因此 return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER))方法中的invoker就是发布的invokerX。

猜你喜欢

转载自blog.csdn.net/Andyzhu_2005/article/details/83997806