基于spring的可扩展性

从一个日志功能开始说起

1. v1 : spring aop 实现日志

代码示例:

@Aspect
@Deprecated
public class MysqlLogAspect {
    private static final Logger log = LoggerFactory.getLogger(MysqlLogAspect.class);
    @Around("execution(* *(..))")
    public void aroundPerformance(ProceedingJoinPoint joinPoint, MysqlLog mysqlLog) throws Throwable {
        // 方法的签名
        Signature signature = joinPoint.getSignature();
        log.info("start execute [{}]", signature);
        long start = System.currentTimeMillis();
        // 方法名
        try {
            joinPoint.proceed();
        } catch (Throwable e) {
            log.error("", e);
            throw e;
        }
        long end = System.currentTimeMillis();
        log.info("end execute [{}], cost {} ms", signature, (end - start));
    }

}

可以看到通过spring的@Aspect、@Around、@Before、@After…等注解可以轻易的实现日志功能。
可是这样的就达到了要求了吗?
我希望更加精细一些的控制,类的某些方法需要日志,某些不需要,该怎么做?


2. v2 : 再精细一些的控制,添加注解支持

代码示例:

@Aspect
@Deprecated
public class MysqlLogAspect {
    private static final Logger log = LoggerFactory.getLogger(MysqlLogAspect.class);
    @Around("execution(* *(..)) && @annotation(mysqlLog)")
    public void aroundPerformance(ProceedingJoinPoint joinPoint, MysqlLog mysqlLog) throws Throwable {
        // 方法的签名
        Signature signature = joinPoint.getSignature();
        log.info("start execute [{}]", signature);
        long start = System.currentTimeMillis();
        // 方法名
        try {
            joinPoint.proceed();
        } catch (Throwable e) {
            log.error("", e);
            throw e;
        }
        long end = System.currentTimeMillis();
        log.info("end execute [{}], cost {} ms", signature, (end - start));
    }

}

这样你就可以在类的方法的注解上添加日志注解,然后就可以精细到方法了。
这样就够了吗?
如果我希望,在类上使用注解,默认这个类的所有方法便开启日志功能,要怎么做呢?
aop的切入点表达式可以实现吗?反正我是不知道?除此之外,切入点需要指定范围,而我更想把日志功能做成一个通用spring插件,就想 @Async 一样。那应该怎么做?


3. v3 : 实现spring注解日志插件

代码示例:

package com.xjy.log.core;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 日志注解,默认的日志处理器只是简单的打印了耗时
 * @author freeman.xu
 *
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
    /** key,如果不指定则是'被标注类的全包名#方法签名' */
    String key() default "";
    /** 日志描述 */
    String desc() default "";
    /** 日志处理器 */
    String logProcessor() default "";
}
package com.xjy.log.core;

import org.springframework.aop.framework.AbstractAdvisingBeanPostProcessor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;

@SuppressWarnings("serial")
public class LogAnnotationBeanPostProcessor extends AbstractAdvisingBeanPostProcessor implements BeanFactoryAware {

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        LogAnnotationAdvisor advisor = new LogAnnotationAdvisor(new DefaultLogProcessor());
        advisor.setBeanFactory(beanFactory);
        this.advisor = advisor;
    }

}
package com.xjy.log.core;

import java.lang.annotation.Annotation;
import java.util.LinkedHashSet;
import java.util.Set;

import org.aopalliance.aop.Advice;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.AbstractPointcutAdvisor;
import org.springframework.aop.support.ComposablePointcut;
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;

@SuppressWarnings("serial")
public class LogAnnotationAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {

    private Advice advice;

    private Pointcut pointcut;

    public LogAnnotationAdvisor(LogProcessor logProcessor) {
        Set<Class<? extends Annotation>> logAnnotationTypes = new LinkedHashSet<Class<? extends Annotation>>(2);
        logAnnotationTypes.add(Log.class);
        this.advice = buildAdvice(logProcessor);
        this.pointcut = buildPointcut(logAnnotationTypes);
    }

    protected Advice buildAdvice(LogProcessor logProcessor) {
        return new LogInterceptor(logProcessor);
    }

    protected Pointcut buildPointcut(Set<Class<? extends Annotation>> logAnnotationTypes) {
        ComposablePointcut result = null;
        for (Class<? extends Annotation> logAnnotationType : logAnnotationTypes) {
            Pointcut cpc = new AnnotationMatchingPointcut(logAnnotationType, true);
            Pointcut mpc = AnnotationMatchingPointcut.forMethodAnnotation(logAnnotationType);
            if (result == null) {
                result = new ComposablePointcut(cpc).union(mpc);
            }
            else {
                result.union(cpc).union(mpc);
            }
        }
        return result;
    }

    @Override
    public Pointcut getPointcut() {
        return this.pointcut;
    }

    @Override
    public Advice getAdvice() {
        return this.advice;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        if (this.advice instanceof BeanFactoryAware) {
            ((BeanFactoryAware) this.advice).setBeanFactory(beanFactory);
        }
    }

}
package com.xjy.log.core;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils;
import org.springframework.core.BridgeMethodResolver;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

@Component
public class LogInterceptor implements MethodInterceptor, Ordered, BeanFactoryAware {

    private final Map<Method, LogProcessor> logProcessors = new ConcurrentHashMap<Method, LogProcessor>(16);
    private BeanFactory beanFactory;
    private LogProcessor defaultLogProcessor;

    public LogInterceptor() {
    }

    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE;
    }

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Class<?> targetClass = getTargetClass(invocation);
        Method specificMethod = getSpecificMethod(invocation);
        String logDesc = getLogDesc(targetClass, specificMethod);
        logger.info("start execute [{}] method [{}],desc is [{}]", targetClass, specificMethod, logDesc);
        long start = System.currentTimeMillis();
        Object result = null;
        try {
            result = invocation.proceed();
        } catch (Throwable e) {
            logger.error("execute [" + targetClass + "] method [" + specificMethod + "] error", e);
            throw e;
        }
        long end = System.currentTimeMillis();
        logger.info("end execute [{}] method [{}],desc is [{}],cost[{}] ms", targetClass, specificMethod, logDesc, (end - start));
        return result;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

}

4. 为什么是这样实现的呢
1.  spring源码解析

spring源码示例:

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // Prepare this context for refreshing.
        prepareRefresh();

        // Tell the subclass to refresh the internal bean factory.
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // Prepare the bean factory for use in this context.
        prepareBeanFactory(beanFactory);

        try {
            // Allows post-processing of the bean factory in context subclasses.
            postProcessBeanFactory(beanFactory);

            // Invoke factory processors registered as beans in the context.
            invokeBeanFactoryPostProcessors(beanFactory);

            // Register bean processors that intercept bean creation.
            registerBeanPostProcessors(beanFactory);

            // Initialize message source for this context.
            initMessageSource();

            // Initialize event multicaster for this context.
            initApplicationEventMulticaster();

            // Initialize other special beans in specific context subclasses.
            onRefresh();

            // Check for listener beans and register them.
            registerListeners();

            // Instantiate all remaining (non-lazy-init) singletons.
            finishBeanFactoryInitialization(beanFactory);

            // Last step: publish corresponding event.
            finishRefresh();
        }

        catch (BeansException ex) {
            logger.warn("Exception encountered during context initialization - cancelling refresh attempt", ex);

            // Destroy already created singletons to avoid dangling resources.
            destroyBeans();

            // Reset 'active' flag.
            cancelRefresh(ex);

            // Propagate exception to caller.
            throw ex;
        }
    }
}

我知道的步骤分析:

// Tell the subclass to refresh the internal bean factory.
// 这一步会加载你在spring.xml中定义的bean
// 其中又分普通的<bean>标签以及自定义的标签,比如<task:annoatation-driven/>
// 自定义的标签会有相应的NameHandler和BeanDefinitionParser
// NameHandler的定义在spring的jar包的META-INF下面,文件名是spring.handlers、spring.schemas、spring.tooling
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

示例:

// 解析xml的类,org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
    if (delegate.isDefaultNamespace(root)) {
        NodeList nl = root.getChildNodes();
        for (int i = 0; i < nl.getLength(); i++) {
            Node node = nl.item(i);
            if (node instanceof Element) {
                Element ele = (Element) node;
                if (delegate.isDefaultNamespace(ele)) {
                    // DefaultElement,eg:http://www.springframework.org/schema/beans 
                    parseDefaultElement(ele, delegate);
                }
                else {
                    // CustomElement, eg:http://www.springframework.org/schema/task等
                    delegate.parseCustomElement(ele);
                }
            }
        }
    }
    else {
        delegate.parseCustomElement(root);
    }
}
// CustomElement的bean有NameHandler和BeanDefinitionParser, 以及NameHandler的定义在spring的jar包的META-INF下面,文件名是spring.handlers、spring.schemas
// org.springframework.scheduling.config.TaskNamespaceHandler
public class TaskNamespaceHandler extends NamespaceHandlerSupport {

    public void init() {
        this.registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
        this.registerBeanDefinitionParser("executor", new ExecutorBeanDefinitionParser());
        this.registerBeanDefinitionParser("scheduled-tasks", new ScheduledTasksBeanDefinitionParser());
        this.registerBeanDefinitionParser("scheduler", new SchedulerBeanDefinitionParser());
    }

}
// org.springframework.scheduling.config.AnnotationDrivenBeanDefinitionParser
public BeanDefinition parse(Element element, ParserContext parserContext) {
    Object source = parserContext.extractSource(element);
    // Register component for the surrounding <task:annotation-driven> element.
    CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source);
    parserContext.pushContainingComponent(compDefinition);
    // Nest the concrete post-processor bean in the surrounding component.
    BeanDefinitionRegistry registry = parserContext.getRegistry();
    String mode = element.getAttribute("mode");
    if ("aspectj".equals(mode)) {
        // mode="aspectj"
        registerAsyncExecutionAspect(element, parserContext);
    }
    else {
        // mode="proxy"
        if (registry.containsBeanDefinition(AnnotationConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME)) {
            parserContext.getReaderContext().error(
                    "Only one AsyncAnnotationBeanPostProcessor may exist within the context.", source);
        }
        else {
            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
                    "org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor");
            builder.getRawBeanDefinition().setSource(source);
            String executor = element.getAttribute("executor");
            if (StringUtils.hasText(executor)) {
                builder.addPropertyReference("executor", executor);
            }
            if (Boolean.valueOf(element.getAttribute(AopNamespaceUtils.PROXY_TARGET_CLASS_ATTRIBUTE))) {
                builder.addPropertyValue("proxyTargetClass", true);
            }
            registerPostProcessor(parserContext, builder, AnnotationConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME);
        }
    }
    if (registry.containsBeanDefinition(AnnotationConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
        parserContext.getReaderContext().error(
                "Only one ScheduledAnnotationBeanPostProcessor may exist within the context.", source);
    }
    else {
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
                "org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor");
        builder.getRawBeanDefinition().setSource(source);
        String scheduler = element.getAttribute("scheduler");
        if (StringUtils.hasText(scheduler)) {
            builder.addPropertyReference("scheduler", scheduler);
        }
        registerPostProcessor(parserContext, builder, AnnotationConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME);
    }
    // Finally register the composite component.
    parserContext.popAndRegisterContainingComponent();

    return null;
}
// Register bean processors that intercept bean creation.
// 这一步会注册BeanPostProcessors
// 所谓的BeanPostProcessors就是在创建bean的时候,拦截bean的创建,有前置拦截和后置拦截
// 也是通过BeanPostProcessors对原始的bean进行动态代理,生成代理bean
registerBeanPostProcessors(beanFactory);

示例:

// 注册BeanPostProcessors,注册的时候有优先级顺序.org.springframework.context.supportAbstractApplicationContext#registerBeanPostProcessors
// BeanPostProcessors拦截bean的创建.org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
        throws BeansException {

    Object result = existingBean;
    for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
        result = beanProcessor.postProcessAfterInitialization(result, beanName);
        if (result == null) {
            return result;
        }
    }
    return result;
}
// 动态代理拦截.org.springframework.aop.framework.AbstractAdvisingBeanPostProcessor
public Object postProcessAfterInitialization(Object bean, String beanName) {
    if (bean instanceof AopInfrastructureBean) {
        // Ignore AOP infrastructure such as scoped proxies.
        return bean;
    }

    if (bean instanceof Advised) {
        Advised advised = (Advised) bean;
        if (!advised.isFrozen() && isEligible(AopUtils.getTargetClass(bean))) {
            // Add our local Advisor to the existing proxy's Advisor chain...
            if (this.beforeExistingAdvisors) {
                advised.addAdvisor(0, this.advisor);
            }
            else {
                advised.addAdvisor(this.advisor);
            }
            return bean;
        }
    }

    if (isEligible(bean, beanName)) {
        ProxyFactory proxyFactory = new ProxyFactory(bean);
        // Copy our properties (proxyTargetClass etc) inherited from ProxyConfig.
        proxyFactory.copyFrom(this);
        proxyFactory.addAdvisor(this.advisor);
        return proxyFactory.getProxy(this.beanClassLoader);
    }

    // No async proxy needed.
    return bean;
}
// 动态代理的部分实现
// jdk
public Object getProxy(ClassLoader classLoader) {
    if (logger.isDebugEnabled()) {
        logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
    }
    Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
    findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
    return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
// cglib
public Object getProxy(ClassLoader classLoader) {
    if (logger.isDebugEnabled()) {
        logger.debug("Creating CGLIB proxy: target source is " + this.advised.getTargetSource());
    }

    try {
        Class<?> rootClass = this.advised.getTargetClass();
        Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");

        Class<?> proxySuperClass = rootClass;
        if (ClassUtils.isCglibProxyClass(rootClass)) {
            proxySuperClass = rootClass.getSuperclass();
            Class<?>[] additionalInterfaces = rootClass.getInterfaces();
            for (Class<?> additionalInterface : additionalInterfaces) {
                this.advised.addInterface(additionalInterface);
            }
        }

        // Validate the class, writing log messages as necessary.
        validateClassIfNecessary(proxySuperClass);

        // Configure CGLIB Enhancer...
        Enhancer enhancer = createEnhancer();
        if (classLoader != null) {
            enhancer.setClassLoader(classLoader);
            if (classLoader instanceof SmartClassLoader &&
                    ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
                enhancer.setUseCache(false);
            }
        }
        enhancer.setSuperclass(proxySuperClass);
        enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
        enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
        enhancer.setStrategy(new MemorySafeUndeclaredThrowableStrategy(UndeclaredThrowableException.class));
        enhancer.setInterceptDuringConstruction(false);

        Callback[] callbacks = getCallbacks(rootClass);
        Class<?>[] types = new Class<?>[callbacks.length];
        for (int x = 0; x < types.length; x++) {
            types[x] = callbacks[x].getClass();
        }
        enhancer.setCallbackFilter(new ProxyCallbackFilter(
                this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
        enhancer.setCallbackTypes(types);
        enhancer.setCallbacks(callbacks);

        // Generate the proxy class and create a proxy instance.
        Object proxy;
        if (this.constructorArgs != null) {
            proxy = enhancer.create(this.constructorArgTypes, this.constructorArgs);
        }
        else {
            proxy = enhancer.create();
        }

        return proxy;
    }
    catch (CodeGenerationException ex) {
        throw new AopConfigException("Could not generate CGLIB subclass of class [" +
                this.advised.getTargetClass() + "]: " +
                "Common causes of this problem include using a final class or a non-visible class",
                ex);
    }
    catch (IllegalArgumentException ex) {
        throw new AopConfigException("Could not generate CGLIB subclass of class [" +
                this.advised.getTargetClass() + "]: " +
                "Common causes of this problem include using a final class or a non-visible class",
                ex);
    }
    catch (Exception ex) {
        // TargetSource.getTarget() failed
        throw new AopConfigException("Unexpected AOP exception", ex);
    }
}

这是比较通用的代理bean的方法,当然有些BeanPostProcessors会重写这个方法,比如定时任务的BeanPostProcessors,它并不需要生成代理bean,而是启动调度线程。
从上面的代码可以很明显的看出代理bean的生成、代理链,以及代理链的顺序(有时候需要注意顺序)。


5. 再进一步,更加透明化,自定义标签

让人着迷的spring标签

xmlns:task="http://www.springframework.org/schema/task"
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.2.xsd
<task:scheduler id="scheduler" pool-size="10"/>
<task:executor id="executor" pool-size="10"/>
<!-- 开启自动调度和异步方法 -->
<task:annotation-driven executor="executor" scheduler="scheduler"/>

自定义标签实现(未经过测试):
需要定义在META-INF下面定义spring.handlers和spring.schemas

spring.handlers
http\://www.springframework.org/schema/log=com.xjy.core.log.LogNamespaceHandler
spring.schemas
http\://www.springframework.org/schema/log/spring-log-3.2.xsd=com/xjy/core/log/config/spring-log-3.2.xsd
<?xml version="1.0" encoding="UTF-8" standalone="no"?>

<xsd:schema xmlns="http://www.springframework.org/schema/log"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
        xmlns:beans="http://www.springframework.org/schema/beans"
        xmlns:tool="http://www.springframework.org/schema/tool"
        targetNamespace="http://www.springframework.org/schema/log"
        elementFormDefault="qualified"
        attributeFormDefault="unqualified">

    <xsd:annotation>
        <xsd:documentation><![CDATA[
    Defines the elements used in the Spring Framework's support for task execution and scheduling.
        ]]></xsd:documentation>
    </xsd:annotation>

    <xsd:import namespace="http://www.springframework.org/schema/beans" schemaLocation="http://www.springframework.org/schema/beans/spring-beans-3.2.xsd"/>
    <xsd:import namespace="http://www.springframework.org/schema/tool" schemaLocation="http://www.springframework.org/schema/tool/spring-tool-3.2.xsd"/>

    <xsd:element name="annotation-driven">
        <xsd:annotation>
            <xsd:documentation><![CDATA[
    Enables the detection of @Async and @Scheduled annotations on any Spring-managed
    object. If present, a proxy will be generated for executing the annotated methods
    asynchronously.

    See Javadoc for the org.springframework.scheduling.annotation.EnableAsync and
    org.springframework.scheduling.annotation.EnableScheduling annotations for information
    on code-based alternatives to this XML element.
            ]]></xsd:documentation>
        </xsd:annotation>
...其他
<!-- 定义log -->
<log:annotation-driven />
package com.xjy.log.core;

import org.springframework.beans.factory.xml.NamespaceHandlerSupport;

public class LogNamespaceHandler extends NamespaceHandlerSupport {

    @Override
    public void init() {
        this.registerBeanDefinitionParser("annotation-driven", new LogBeanDefinitionParser());
    }

}
package com.xjy.log.core;

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Element;

public class LogBeanDefinitionParser implements BeanDefinitionParser {
    private static final String logAnnotationProcessorBeanName = "com.xjy.core.log.internalLogAnnotationBeanPostProcessor";

    @Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        Object source = parserContext.extractSource(element);

        // Register component for the surrounding <task:annotation-driven> element.
        CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source);
        parserContext.pushContainingComponent(compDefinition);

        // Nest the concrete post-processor bean in the surrounding component.
        BeanDefinitionRegistry registry = parserContext.getRegistry();

        if (registry.containsBeanDefinition(logAnnotationProcessorBeanName)) {
            parserContext.getReaderContext().error(
                    "Only one LogAnnotationProcessorBeanName may exist within the context.", source);
        } else {
            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
                    "com.xjy.core.log.LogAnnotationBeanPostProcessor");
            builder.getRawBeanDefinition().setSource(source);
            registerPostProcessor(parserContext, builder, logAnnotationProcessorBeanName);
        }
        // Finally register the composite component.
        parserContext.popAndRegisterContainingComponent();

        return null;
    }

    private static void registerPostProcessor(
            ParserContext parserContext, BeanDefinitionBuilder builder, String beanName) {

        builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        parserContext.getRegistry().registerBeanDefinition(beanName, builder.getBeanDefinition());
        BeanDefinitionHolder holder = new BeanDefinitionHolder(builder.getBeanDefinition(), beanName);
        parserContext.registerComponent(new BeanComponentDefinition(holder));
    }

}

其实说白了,就是不用手动在去定义bean了,更方便,更透明了。
至此,一个spring插件就定义完了


6. 再进一步,抽象日志处理器,这一步和spring没啥关系

注解中可以选择使用哪个日志处理器:

package com.xjy.log.core;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 日志注解,默认的日志处理器只是简单的打印了耗时
 * @author freeman.xu
 *
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
    /** key,如果不指定则是'被标注类的全包名#方法签名' */
    String key() default "";
    /** 日志描述 */
    String desc() default "";
    /** 日志处理器 */
    String logProcessor() default "";
}

代理方法执行的时候选择对应的日志处理器即:

private LogProcessor determineLogProcessor(Method method) {

        LogProcessor logProcessor = this.logProcessors.get(method);
        if (logProcessor == null) {
            logProcessor = this.defaultLogProcessor;
            String qualifier = getExecutorQualifier(method);
            if (StringUtils.hasLength(qualifier)) {
                Assert.notNull(this.beanFactory, "BeanFactory must be set on " + getClass().getSimpleName()
                        + " to access qualified logProcessor '" + qualifier + "'");
                logProcessor = BeanFactoryAnnotationUtils.qualifiedBeanOfType(this.beanFactory, LogProcessor.class, qualifier);
            } else if (logProcessor == null) {
                return null;
            }
            this.logProcessors.put(method, logProcessor);
        }
        return logProcessor;
    }


    private String getExecutorQualifier(Method method) {
        Log log = AnnotationUtils.findAnnotation(method, Log.class);
        if (log == null) {
            log = AnnotationUtils.findAnnotation(method.getDeclaringClass(), Log.class);
        }
        return (log != null ? log.logProcessor() : null);
    }

日志处理器:

package com.xjy.log.core;

import org.aopalliance.intercept.MethodInvocation;

/**
 * 日志处理器
 * @author freeman.xu
 *
 */
public interface LogProcessor {

    Object log(MethodInvocation invocation) throws Throwable;

}
package com.xjy.log.core;

import java.lang.reflect.Method;

import org.aopalliance.intercept.MethodInvocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DefaultLogProcessor extends AbstractLogProcessor {
    private static final Logger logger = LoggerFactory.getLogger(DefaultLogProcessor.class);

    @Override
    public Object log(MethodInvocation invocation) throws Throwable {
        Class<?> targetClass = getTargetClass(invocation);
        Method specificMethod = getSpecificMethod(invocation);
        String logDesc = getLogDesc(targetClass, specificMethod);
        logger.info("start execute [{}] method [{}],desc is [{}]", targetClass, specificMethod, logDesc);
        long start = System.currentTimeMillis();
        Object result = null;
        try {
            result = invocation.proceed();
        } catch (Throwable e) {
            logger.error("execute [" + targetClass + "] method [" + specificMethod + "] error", e);
            throw e;
        }
        long end = System.currentTimeMillis();
        logger.info("end execute [{}] method [{}],desc is [{}],cost[{}] ms", targetClass, specificMethod, logDesc, (end - start));
        return result;
    }


}
package com.xjy.web.log;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import javax.servlet.http.HttpServletRequest;

import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.thrift.TException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ModelAttribute;

import com.xjy.log.core.AbstractLogProcessor;
import com.xjy.log.core.Log;
import com.xjy.log.service.ILogService;
import com.xjy.log.service.thrift.LogServiceThrift;
import com.xjy.log.service.thrift.LogThrift;

/**
 * 用户处理web请求的日志处理器。<br>
 * 一般来说,这类的日志建议把{@link Log}的key设置为访问的url,如果url是动态的。<br>
 * 那么建议方法的参数传递一个HttpServletRequest,或者使用{@link ModelAttribute}初始化HttpServletRequest,便于处理器获取url。
 * @author xujieyang
 *
 */
@Component("webLogProcessor")
public class WebLogProcessor extends AbstractLogProcessor {

    private static final Logger logger = LoggerFactory.getLogger(WebLogProcessor.class);

    @Autowired(required = false)
    @Qualifier("logServiceRmi")
    private ILogService logServiceRmi;

    @Autowired
    @Qualifier("logExecutor")
    private ThreadPoolTaskExecutor logExecutor;

    @Override
    public Object log(MethodInvocation invocation) throws Throwable {
        Class<?> targetClass = getTargetClass(invocation);
        Method specificMethod = getSpecificMethod(invocation);
        String url = getUrl(invocation, targetClass, specificMethod);
        long start = System.currentTimeMillis();
        logger.info("user start visis page [{}]", url);
        Object result = null;
        try {
            result = invocation.proceed();
            long end = System.currentTimeMillis();
            Log log = this.getLog(targetClass, specificMethod);
            addLog(url , log.desc(), (end -start), "xujieyang_blog", true);
            logger.info("use end visit page [{}], cost {} ms", url, (end -start));
        } catch (Throwable e) {
            // 异步添加失败日志
            Log log = this.getLog(targetClass, specificMethod);
            addLog(url, log.desc(), -1L, "xujieyang_blog", false);
            logger.error("execute [" + targetClass + "] method [" + specificMethod + "] error", e);
            throw e;
        }
        return result;
    }

    private String getUrl(MethodInvocation invocation, Class<?> targetClass, Method specificMethod) {
        Log log = this.getLog(targetClass, specificMethod);
        String url = log.key();
        if(StringUtils.isNotBlank(url)) {
            return url;
        }
        HttpServletRequest request = this.getHttpServletRequest(invocation);
        if(request != null) {
            url = request.getRequestURI();
        } else {
            logger.warn("Can not get HttpServletRequest, but this is a web log processor, "
                    + "so you should provide HttpServletRequest, then logProcessor can log url.");
            url = getLogDesc(targetClass, specificMethod);
        }
        return url;
    }

    private HttpServletRequest getHttpServletRequest(MethodInvocation invocation) {
        Object traget = invocation.getThis();
        HttpServletRequest request = null;
        try {
            // 从class中获取
            request = (HttpServletRequest) PropertyUtils.getProperty(traget, "request");
        } catch (IllegalAccessException | InvocationTargetException
                | NoSuchMethodException e) {
            logger.warn("can not get request property");
        }
        if(request == null) {
            // 从方法参数中获取
            Object[] args = invocation.getArguments();
            if(ArrayUtils.isEmpty(args)) {
                return request;
            }
            for(Object arg : args) {
                // Class1.isAssignableFrom(Class2)  
                // isAssignableFrom   是用来判断一个类Class1和另一个类Class2是否相同或是另一个类的超类或接口
                if(HttpServletRequest.class.isAssignableFrom(arg.getClass())) {
                    return (HttpServletRequest) arg;
                }
            }
        }
        return request;
    }

    private void addLog(String key, String desc, long costMs, String systemKey, boolean success) {
        if(logServiceRmi == null) {
            logger.warn("logServiceRmi is null");
            return;
        }
        logExecutor.execute(new Runnable() {

            @Override
            public void run() {
                com.xjy.log.model.Log log = new com.xjy.log.model.Log();
                log.setCostMs(costMs);
                log.setDesc(desc);
                log.setKey(key);
                log.setSystemKey(systemKey);
                log.setSuccess(success);
                logServiceRmi.add(log);
            }
        });
    }

}

7. 举一反三,锁实现
package com.xjy.core.lock;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 定时任务锁,也支持分布式锁,锁的默认实现为单机锁。<br>
 * @author xujieyang
 *
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Lock {
    /** 锁的key */
    String key();
    /** 锁的value */
    String value();
    /** key过期的时间,使用秒为单位 */
    long expireTime() default 0L;
    String locker() default "";
}
package com.xjy.core.lock;

import org.springframework.aop.framework.AbstractAdvisingBeanPostProcessor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;

@SuppressWarnings("serial")
public class LockAnnotationBeanPostProcessor extends AbstractAdvisingBeanPostProcessor implements BeanFactoryAware {

    public LockAnnotationBeanPostProcessor() {
        this.beforeExistingAdvisors = true;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        LockAnnotationAdvisor advisor = new LockAnnotationAdvisor(new DefaultLocker());
        advisor.setBeanFactory(beanFactory);
        this.advisor = advisor;
    }

    @Override
    public int getOrder() {
        return HIGHEST_PRECEDENCE;
    }

}
package com.xjy.core.lock;

import java.lang.annotation.Annotation;
import java.util.LinkedHashSet;
import java.util.Set;

import org.aopalliance.aop.Advice;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.AbstractPointcutAdvisor;
import org.springframework.aop.support.ComposablePointcut;
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;

@SuppressWarnings("serial")
public class LockAnnotationAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {

    private Advice advice;

    private Pointcut pointcut;

    public LockAnnotationAdvisor(Locker locker) {
        Set<Class<? extends Annotation>> lockAnnotationTypes = new LinkedHashSet<Class<? extends Annotation>>(2);
        lockAnnotationTypes.add(Lock.class);
        this.advice = this.buildAdvice(locker);
        this.pointcut = buildPointcut(lockAnnotationTypes);
    }

    protected Advice buildAdvice(Locker locker) {
        return new LockInterceptor(locker);
    }

    protected Pointcut buildPointcut(Set<Class<? extends Annotation>> redisLockAnnotationTypes) {
        ComposablePointcut result = null;
        for (Class<? extends Annotation> redisLockAnnotationType : redisLockAnnotationTypes) {
            Pointcut cpc = new AnnotationMatchingPointcut(redisLockAnnotationType, true);
            Pointcut mpc = AnnotationMatchingPointcut.forMethodAnnotation(redisLockAnnotationType);
            if (result == null) {
                result = new ComposablePointcut(cpc).union(mpc);
            }
            else {
                result.union(cpc).union(mpc);
            }
        }
        return result;
    }

    @Override
    public Pointcut getPointcut() {
        return this.pointcut;
    }

    @Override
    public Advice getAdvice() {
        return this.advice;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        if (this.advice instanceof BeanFactoryAware) {
            ((BeanFactoryAware) this.advice).setBeanFactory(beanFactory);
        }
    }

}
package com.xjy.core.lock;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils;
import org.springframework.core.BridgeMethodResolver;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

public class LockInterceptor implements MethodInterceptor, Ordered, BeanFactoryAware {

    private final Map<Method, Locker> lockers = new ConcurrentHashMap<Method, Locker>(16);
    private BeanFactory beanFactory;
    private Locker defaultLocker;

    public LockInterceptor() {
    }

    public LockInterceptor(Locker defaultLocker) {
        this.defaultLocker = defaultLocker;
    }

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE;
    }

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
        Method specificMethod = ClassUtils.getMostSpecificMethod(invocation.getMethod(), targetClass);
        specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
        Locker locker = determineLocker(specificMethod);
        return locker.lock(invocation);
    }

    private Locker determineLocker(Method method) {

        Locker locker = this.lockers.get(method);
        if (locker == null) {
            locker = this.defaultLocker;
            String qualifier = getExecutorQualifier(method);
            if (StringUtils.hasLength(qualifier)) {
                Assert.notNull(this.beanFactory, "BeanFactory must be set on " + getClass().getSimpleName()
                        + " to access qualified logProcessor '" + qualifier + "'");
                locker = BeanFactoryAnnotationUtils.qualifiedBeanOfType(this.beanFactory, Locker.class, qualifier);
            } else if (locker == null) {
                return null;
            }
            this.lockers.put(method, locker);
        }
        return locker;
    }

    private String getExecutorQualifier(Method method) {
        Lock lock = AnnotationUtils.findAnnotation(method, Lock.class);
        if (lock == null) {
            lock = AnnotationUtils.findAnnotation(method.getDeclaringClass(), Lock.class);
        }
        return (lock != null ? lock.locker() : null);
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

}

8. spring之代理的代理

java rmi和spring
只需要配置就可以发布为rmi服务,示例:

<!-- 发布为rmi服务 -->
<bean id="registry" class="org.springframework.remoting.rmi.RmiRegistryFactoryBean">
    <property name="port" value="${log.rmi.port}" />
</bean>
<bean class="org.springframework.remoting.rmi.RmiServiceExporter"
    p:service-ref="logService"
    p:serviceName="logServiceRmi"
    p:serviceInterface="com.xjy.log.service.ILogService"
    p:registry-ref="registry"
    p:registryHost="${log.rmi.host}"/>
<!-- rmi客户端 -->
<bean id="logServiceRmi" 
    class="org.springframework.remoting.rmi.RmiProxyFactoryBean"
    p:serviceUrl="${logServiceRmi}"
    p:serviceInterface="com.xjy.log.service.ILogService"
    p:cacheStub="false"
    p:refreshStubOnConnectFailure="true" />

现在你只需要在需要rmi服务的地方注入logServiceRmi就可以了。这看起来很完美,但我现在用的是thrift呀,rmi已经没落了。
10. #### spring和thrift ####
实现类似的rmi服务:

package com.xjy.core.remoting.thrift;

import java.lang.reflect.InvocationTargetException;

import org.apache.commons.lang.ClassUtils;
import org.apache.commons.lang.reflect.ConstructorUtils;
import org.apache.thrift.TProcessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;

// TODO 改造成一次可以发布多个server服务
public abstract class AbstractThriftServer implements InitializingBean {
    private static final Logger log = LoggerFactory.getLogger(AbstractThriftServer.class);
    /** thrift生成的service中Processor内部类的名称 */
    private static final String processorInnerClassName = "Processor";

    /** 发布service的端口号 */
    protected Integer port;
    /** thrift生成的service的实现类 */
    protected Object service;
    /** thrift生成的service的包名 */
    protected String triftServicePackageName;

    public Integer getPort() {
        return port;
    }
    public void setPort(Integer port) {
        this.port = port;
    }
    public Object getService() {
        return service;
    }
    public void setService(Object service) {
        this.service = service;
    }
    public String getTriftServicePackageName() {
        return triftServicePackageName;
    }
    public void setTriftServicePackageName(String triftServicePackageName) {
        this.triftServicePackageName = triftServicePackageName;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        Assert.notNull(port, "port property required");
        Assert.notNull(service, "service property required");
        Assert.notNull(triftServicePackageName, "triftServicePackageName property required");
        TProcessor tProcessor = initTprocessor();
        // 一个tProcessor对应一个线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                startThriftServer(tProcessor);
            }
        }).start();
    }

    /**
     * 初始化TProcessor
     * @return
     * @throws Exception
     */
    protected TProcessor initTprocessor() throws Exception {
        log.info("start initTprocessor, triftServicePackageName is {}", triftServicePackageName);
        TProcessor tProcessor = null;
        try {
            Class<?> processorClass = ClassUtils.getClass(triftServicePackageName + "$" + processorInnerClassName);
//                  async ? ClassUtils.getClass(triftServicePackageName + "$" + asyncProcessorInnerClassName) : 
//              ClassUtils.getClass(triftServicePackageName + "$" + processorInnerClassName);
            // TODO 验证service的正确性
//          Class<?> ifaceClass = ClassUtils.getClass(genTriftServicePackName + "$Iface");
            tProcessor = (TProcessor) ConstructorUtils.invokeConstructor(processorClass, service);
        } catch (ClassNotFoundException
                | NoSuchMethodException | IllegalAccessException
                | InvocationTargetException | InstantiationException e) {
            log.error("initTprocessor error, triftServicePackageName is " + triftServicePackageName, e);
            throw e;
        }
        log.info("end initTprocessor, triftServicePackageName is {}", triftServicePackageName);
        return tProcessor;
    }

    /**
     * 启动服务,子类选择使用不同的方式启动thrift服务
     * @param tProcessor
     */
    protected abstract void startThriftServer(TProcessor tProcessor);

}
package com.xjy.core.remoting.thrift;

import org.apache.thrift.TProcessor;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.server.TServer;
import org.apache.thrift.server.TSimpleServer;
import org.apache.thrift.transport.TServerSocket;
import org.apache.thrift.transport.TTransportException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SimpleThriftServer extends AbstractThriftServer {
    private static final Logger log = LoggerFactory.getLogger(SimpleThriftServer.class);

    @Override
    protected void startThriftServer(TProcessor tProcessor) {
        log.info("startThriftServer type is simple, port is {}, triftServicePackageName is {}", port, triftServicePackageName);
        TServerSocket serverTransport;
        try {
            serverTransport = new TServerSocket(port);
            TServer.Args tArgs = new TServer.Args(serverTransport);
            tArgs.processor(tProcessor);
            tArgs.protocolFactory(new TBinaryProtocol.Factory());
            // simple sever 用于测试
            TServer server = new TSimpleServer(tArgs);
            // 此处会阻塞
            server.serve();
        } catch (TTransportException e) {
            log.error("startThriftServer error type is simple, port is " + port + "triftServicePackageName is " + triftServicePackageName, e);
        }
    }

}
package com.xjy.core.remoting.thrift;

import org.apache.thrift.TProcessor;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.server.TServer;
import org.apache.thrift.server.TThreadPoolServer;
import org.apache.thrift.transport.TServerSocket;
import org.apache.thrift.transport.TTransportException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ThreadPoolThriftServer extends AbstractThriftServer {
    private static final Logger log = LoggerFactory.getLogger(ThreadPoolThriftServer.class);

    @Override
    protected void startThriftServer(TProcessor tProcessor) {
        log.info("startThriftServer type is ThreadPool, port is {}, triftServicePackageName is {}", port, triftServicePackageName);
        TServerSocket serverTransport;
        try {
            serverTransport = new TServerSocket(port);
            TThreadPoolServer.Args ttpsArgs = new TThreadPoolServer.Args(serverTransport);
            ttpsArgs.processor(tProcessor);
            ttpsArgs.protocolFactory(new TBinaryProtocol.Factory());
            // 线程池服务模型,使用标准的阻塞式IO,预先创建一组线程处理请求。
            TServer server = new TThreadPoolServer(ttpsArgs);
            // 此处会阻塞
            server.serve();
        } catch (TTransportException e) {
            log.error("startThriftServer error type is ThreadPool, port is " + port + "triftServicePackageName is " + triftServicePackageName, e);
        }
    }

}
package com.xjy.core.remoting.thrift;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.lang.reflect.ConstructorUtils;
import org.apache.commons.lang.reflect.MethodUtils;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TCompactProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;
import org.apache.thrift.transport.TTransportException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.ClassUtils;

public class ThriftClientProxyFactoryBean implements FactoryBean<Object>, InitializingBean, MethodInterceptor {
    private static final Logger log = LoggerFactory.getLogger(ThriftClientProxyFactoryBean.class);
    /** thrift生成的service中Iface内部类的名称 */
    private static final String iFaceInnerClassName = "Iface";
    /** thrift生成的service中Client内部类的名称 */
    private static final String clientInnerClassName = "Client";

    /** ThriftClientProxyFactoryBean产生的bean */
    private Object serviceProxy;
    /** 连接到thrift的端口号 */
    private Integer port;
    /** 连接到thrift的host */
    private String host;
    /** thrift生成的service的包名 */
    private String triftServicePackageName;
    /** 连接超时时间 */
    private Integer timeout = 3000;

    public String getTriftServicePackageName() {
        return triftServicePackageName;
    }

    public void setTriftServicePackageName(String triftServicePackageName) {
        this.triftServicePackageName = triftServicePackageName;
    }

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public Integer getPort() {
        return port;
    }

    public void setPort(Integer port) {
        this.port = port;
    }

    public Integer getTimeout() {
        return timeout;
    }

    public void setTimeout(Integer timeout) {
        this.timeout = timeout;
    }

    @Override
    public Object getObject() throws Exception {
        return this.serviceProxy;
    }

    @Override
    public Class<?> getObjectType() {
        if(triftServicePackageName == null) {
            return null;
        }
        Class<?> objectType = null;
        try {
            objectType = org.apache.commons.lang.ClassUtils.getClass(triftServicePackageName + "$" + iFaceInnerClassName);
        } catch (ClassNotFoundException e) {
            log.error("getObjectType error", e);
        }
        return objectType;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        this.serviceProxy = new ProxyFactory(org.apache.commons.lang.ClassUtils.getClass(triftServicePackageName + "$" + iFaceInnerClassName), this).getProxy(ClassUtils.getDefaultClassLoader());
    }

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        TTransport transport = null;
        if(timeout.equals(new Integer(-1))) {
            transport = new TSocket(host, port);
        } else {
            transport = new TSocket(host, port, timeout);
        }
        Object result = null;
        // 协议要和服务端一致
        TProtocol protocol = new TBinaryProtocol(transport);
        try {
            Class<?> clientClass = org.apache.commons.lang.ClassUtils.getClass(triftServicePackageName + "$" + clientInnerClassName);
            transport.open();
            Method method = invocation.getMethod();
            result = MethodUtils.invokeMethod(ConstructorUtils.invokeConstructor(clientClass, protocol), 
                    method.getName(), invocation.getArguments());
        } catch (ClassNotFoundException | TTransportException
                | NoSuchMethodException | IllegalAccessException
                | InvocationTargetException | InstantiationException e) {
            if(e instanceof TTransportException) {
                log.error("open transport error. host is " + host + " port is " + port, e);
            } else {
                log.error("MethodInvocation error", e);
            }
            throw e;
        } catch (Exception e) {
            log.error("error", e);
            throw e;
            // TODO 异常的处理
        } finally {
            if (null != transport && transport.isOpen()) {
                transport.close();
            }
        }
        return result;
    }

}

好了,你现在可以像使用本地的bean一样,直接在需要的地方注入thrift的客户端了

@Autowired
@Qualifier("blogServiceThrift")
private BlogService.Iface blogServiceThrift;

核心的原理其实还是动态代理,server其实没什么说的,主要是客户端,客户端是个MethodInterceptor、FactoryBean,而且在产生bean的时候使用了动态代理,而你代理的bean可能已经是被动态代理的bean了,就是代理的代理了。


  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值