001/* 002 * Copyright 2002-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.context.event; 018 019import java.lang.reflect.Method; 020import java.util.ArrayList; 021import java.util.Collections; 022import java.util.List; 023import java.util.Map; 024import java.util.Set; 025import java.util.concurrent.ConcurrentHashMap; 026 027import org.apache.commons.logging.Log; 028import org.apache.commons.logging.LogFactory; 029 030import org.springframework.aop.framework.autoproxy.AutoProxyUtils; 031import org.springframework.aop.scope.ScopedObject; 032import org.springframework.aop.scope.ScopedProxyUtils; 033import org.springframework.aop.support.AopUtils; 034import org.springframework.beans.factory.BeanInitializationException; 035import org.springframework.beans.factory.SmartInitializingSingleton; 036import org.springframework.context.ApplicationContext; 037import org.springframework.context.ApplicationContextAware; 038import org.springframework.context.ApplicationListener; 039import org.springframework.context.ConfigurableApplicationContext; 040import org.springframework.core.MethodIntrospector; 041import org.springframework.core.annotation.AnnotatedElementUtils; 042import org.springframework.core.annotation.AnnotationAwareOrderComparator; 043import org.springframework.util.Assert; 044import org.springframework.util.CollectionUtils; 045 046/** 047 * Registers {@link EventListener} methods as individual {@link ApplicationListener} instances. 048 * 049 * @author Stephane Nicoll 050 * @author Juergen Hoeller 051 * @since 4.2 052 */ 053public class EventListenerMethodProcessor implements SmartInitializingSingleton, ApplicationContextAware { 054 055 protected final Log logger = LogFactory.getLog(getClass()); 056 057 private ConfigurableApplicationContext applicationContext; 058 059 private final EventExpressionEvaluator evaluator = new EventExpressionEvaluator(); 060 061 private final Set<Class<?>> nonAnnotatedClasses = 062 Collections.newSetFromMap(new ConcurrentHashMap<Class<?>, Boolean>(64)); 063 064 065 @Override 066 public void setApplicationContext(ApplicationContext applicationContext) { 067 Assert.isTrue(applicationContext instanceof ConfigurableApplicationContext, 068 "ApplicationContext does not implement ConfigurableApplicationContext"); 069 this.applicationContext = (ConfigurableApplicationContext) applicationContext; 070 } 071 072 @Override 073 public void afterSingletonsInstantiated() { 074 List<EventListenerFactory> factories = getEventListenerFactories(); 075 String[] beanNames = this.applicationContext.getBeanNamesForType(Object.class); 076 for (String beanName : beanNames) { 077 if (!ScopedProxyUtils.isScopedTarget(beanName)) { 078 Class<?> type = null; 079 try { 080 type = AutoProxyUtils.determineTargetClass(this.applicationContext.getBeanFactory(), beanName); 081 } 082 catch (Throwable ex) { 083 // An unresolvable bean type, probably from a lazy bean - let's ignore it. 084 if (logger.isDebugEnabled()) { 085 logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex); 086 } 087 } 088 if (type != null) { 089 if (ScopedObject.class.isAssignableFrom(type)) { 090 try { 091 type = AutoProxyUtils.determineTargetClass(this.applicationContext.getBeanFactory(), 092 ScopedProxyUtils.getTargetBeanName(beanName)); 093 } 094 catch (Throwable ex) { 095 // An invalid scoped proxy arrangement - let's ignore it. 096 if (logger.isDebugEnabled()) { 097 logger.debug("Could not resolve target bean for scoped proxy '" + beanName + "'", ex); 098 } 099 } 100 } 101 try { 102 processBean(factories, beanName, type); 103 } 104 catch (Throwable ex) { 105 throw new BeanInitializationException("Failed to process @EventListener " + 106 "annotation on bean with name '" + beanName + "'", ex); 107 } 108 } 109 } 110 } 111 } 112 113 114 /** 115 * Return the {@link EventListenerFactory} instances to use to handle 116 * {@link EventListener} annotated methods. 117 */ 118 protected List<EventListenerFactory> getEventListenerFactories() { 119 Map<String, EventListenerFactory> beans = this.applicationContext.getBeansOfType(EventListenerFactory.class); 120 List<EventListenerFactory> factories = new ArrayList<EventListenerFactory>(beans.values()); 121 AnnotationAwareOrderComparator.sort(factories); 122 return factories; 123 } 124 125 protected void processBean(final List<EventListenerFactory> factories, final String beanName, final Class<?> targetType) { 126 if (!this.nonAnnotatedClasses.contains(targetType)) { 127 Map<Method, EventListener> annotatedMethods = null; 128 try { 129 annotatedMethods = MethodIntrospector.selectMethods(targetType, 130 new MethodIntrospector.MetadataLookup<EventListener>() { 131 @Override 132 public EventListener inspect(Method method) { 133 return AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class); 134 } 135 }); 136 } 137 catch (Throwable ex) { 138 // An unresolvable type in a method signature, probably from a lazy bean - let's ignore it. 139 if (logger.isDebugEnabled()) { 140 logger.debug("Could not resolve methods for bean with name '" + beanName + "'", ex); 141 } 142 } 143 if (CollectionUtils.isEmpty(annotatedMethods)) { 144 this.nonAnnotatedClasses.add(targetType); 145 if (logger.isTraceEnabled()) { 146 logger.trace("No @EventListener annotations found on bean class: " + targetType.getName()); 147 } 148 } 149 else { 150 // Non-empty set of methods 151 for (Method method : annotatedMethods.keySet()) { 152 for (EventListenerFactory factory : factories) { 153 if (factory.supportsMethod(method)) { 154 Method methodToUse = AopUtils.selectInvocableMethod( 155 method, this.applicationContext.getType(beanName)); 156 ApplicationListener<?> applicationListener = 157 factory.createApplicationListener(beanName, targetType, methodToUse); 158 if (applicationListener instanceof ApplicationListenerMethodAdapter) { 159 ((ApplicationListenerMethodAdapter) applicationListener) 160 .init(this.applicationContext, this.evaluator); 161 } 162 this.applicationContext.addApplicationListener(applicationListener); 163 break; 164 } 165 } 166 } 167 if (logger.isDebugEnabled()) { 168 logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" + 169 beanName + "': " + annotatedMethods); 170 } 171 } 172 } 173 } 174 175}