基于spring的可扩展性
2016-03-06 22:08
579 查看
从一个日志功能开始说起
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>> 20930 ; 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了,就是代理的代理了。
相关文章推荐
- 一个jar包里的网站
- 一个jar包里的网站之文件上传
- 一个jar包里的网站之返回对媒体类型
- Spring整合Quartz(JobDetailBean方式)
- Spring整合Quartz(JobDetailBean方式)
- 模拟Spring的简单实现
- spring+html5实现安全传输随机数字密码键盘
- Spring中属性注入详解
- SpringMVC框架下JQuery传递并解析Json格式的数据是如何实现的
- struts2 spring整合fieldError问题
- spring的jdbctemplate的crud的基类dao
- 读取spring配置文件的方法(spring读取资源文件)
- Spring Bean基本管理实例详解
- java实现简单美女拼图游戏
- 详解Java的Spring框架中的事务管理方式
- 解析Java的Spring框架的BeanPostProcessor发布处理器
- Java开发框架spring实现自定义缓存标签
- java基本教程之线程休眠 java多线程教程
- JSP开发中在spring mvc项目中实现登录账号单浏览器登录
- spring boot实战之内嵌容器tomcat配置