/* * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.integration.aop; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.Arrays; import java.util.HashSet; import java.util.Set; import org.aopalliance.aop.Advice; import org.springframework.aop.ClassFilter; import org.springframework.aop.MethodMatcher; import org.springframework.aop.Pointcut; import org.springframework.aop.support.AbstractPointcutAdvisor; import org.springframework.aop.support.AopUtils; import org.springframework.aop.support.ComposablePointcut; import org.springframework.aop.support.annotation.AnnotationClassFilter; import org.springframework.aop.support.annotation.AnnotationMethodMatcher; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.integration.annotation.Publisher; import org.springframework.integration.support.channel.BeanFactoryChannelResolver; import org.springframework.util.Assert; /** * An advisor that will apply the {@link MessagePublishingInterceptor} to any * methods containing the provided annotations. If no annotations are provided, * the default will be {@link Publisher @Publisher}. * * @author Mark Fisher * @author Gary Russell * @since 2.0 */ @SuppressWarnings("serial") public class PublisherAnnotationAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware { private final Set<Class<? extends Annotation>> publisherAnnotationTypes; private final MessagePublishingInterceptor interceptor; @SuppressWarnings("unchecked") //For JDK7 public PublisherAnnotationAdvisor(Class<? extends Annotation> ... publisherAnnotationTypes) { this.publisherAnnotationTypes = new HashSet<Class<? extends Annotation>>(Arrays.asList(publisherAnnotationTypes)); PublisherMetadataSource metadataSource = new MethodAnnotationPublisherMetadataSource(this.publisherAnnotationTypes); this.interceptor = new MessagePublishingInterceptor(metadataSource); } @SuppressWarnings("unchecked") public PublisherAnnotationAdvisor() { this(Publisher.class); } /** * @param defaultChannelName the default channel name. * @since 4.0.3 */ public void setDefaultChannelName(String defaultChannelName) { this.interceptor.setDefaultChannelName(defaultChannelName); } @Override public void setBeanFactory(BeanFactory beanFactory) { this.interceptor.setChannelResolver(new BeanFactoryChannelResolver(beanFactory)); this.interceptor.setBeanFactory(beanFactory); } @Override public Advice getAdvice() { return this.interceptor; } @Override public Pointcut getPointcut() { return this.buildPointcut(); } private Pointcut buildPointcut() { ComposablePointcut result = null; for (Class<? extends Annotation> publisherAnnotationType : this.publisherAnnotationTypes) { Pointcut cpc = new MetaAnnotationMatchingPointcut(publisherAnnotationType, true); Pointcut mpc = new MetaAnnotationMatchingPointcut(null, publisherAnnotationType); if (result == null) { result = new ComposablePointcut(cpc).union(mpc); } else { result.union(cpc).union(mpc); } } return result; } private static final class MetaAnnotationMatchingPointcut implements Pointcut { private final ClassFilter classFilter; private final MethodMatcher methodMatcher; /** * Create a new MetaAnnotationMatchingPointcut for the given annotation type. * @param classAnnotationType the annotation type to look for at the class level * @param checkInherited whether to explicitly check the superclasses and * interfaces for the annotation type as well (even if the annotation type * is not marked as inherited itself) */ MetaAnnotationMatchingPointcut(Class<? extends Annotation> classAnnotationType, boolean checkInherited) { this.classFilter = new AnnotationClassFilter(classAnnotationType, checkInherited); this.methodMatcher = MethodMatcher.TRUE; } /** * Create a new MetaAnnotationMatchingPointcut for the given annotation type. * @param classAnnotationType the annotation type to look for at the class level * (can be <code>null</code>) * @param methodAnnotationType the annotation type to look for at the method level * (can be <code>null</code>) */ MetaAnnotationMatchingPointcut( Class<? extends Annotation> classAnnotationType, Class<? extends Annotation> methodAnnotationType) { Assert.isTrue((classAnnotationType != null || methodAnnotationType != null), "Either Class annotation type or Method annotation type needs to be specified (or both)"); if (classAnnotationType != null) { this.classFilter = new AnnotationClassFilter(classAnnotationType); } else { this.classFilter = ClassFilter.TRUE; } if (methodAnnotationType != null) { this.methodMatcher = new MetaAnnotationMethodMatcher(methodAnnotationType); } else { this.methodMatcher = MethodMatcher.TRUE; } } @Override public ClassFilter getClassFilter() { return this.classFilter; } @Override public MethodMatcher getMethodMatcher() { return this.methodMatcher; } } private static final class MetaAnnotationMethodMatcher extends AnnotationMethodMatcher { private final Class<? extends Annotation> annotationType; /** * Create a new AnnotationClassFilter for the given annotation type. * @param annotationType the annotation type to look for */ MetaAnnotationMethodMatcher(Class<? extends Annotation> annotationType) { super(annotationType); this.annotationType = annotationType; } @Override @SuppressWarnings("rawtypes") public boolean matches(Method method, Class targetClass) { if (AnnotationUtils.getAnnotation(method, this.annotationType) != null) { return true; } // The method may be on an interface, so let's check on the target class as well. Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass); return (specificMethod != method && (AnnotationUtils.getAnnotation(specificMethod, this.annotationType) != null)); } } }