com.google.inject.internal.ProxyFactory Maven / Gradle / Ivy
/*
* Copyright (C) 2009 Google Inc.
*
* 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 com.google.inject.internal;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.inject.spi.InjectionPoint;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.BitSet;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.aopalliance.intercept.MethodInterceptor;
/**
* Builds a construction proxy that can participate in AOP. This class manages applying type and
* method matchers to come up with the set of intercepted methods.
*
* @author [email protected] (Jesse Wilson)
*/
final class ProxyFactory implements ConstructionProxyFactory {
private static final Logger logger = Logger.getLogger(ProxyFactory.class.getName());
private final InjectionPoint injectionPoint;
private final Function> enhancer;
private final ImmutableMap> interceptors;
private final InvocationHandler[] callbacks;
ProxyFactory(InjectionPoint injectionPoint, Iterable methodAspects)
throws ErrorsException {
this.injectionPoint = injectionPoint;
Class> hostClass = injectionPoint.getMember().getDeclaringClass();
// Find applicable aspects. Bow out if none are applicable to this class.
List applicableAspects = Lists.newArrayList();
for (MethodAspect methodAspect : methodAspects) {
if (methodAspect.matches(hostClass)) {
applicableAspects.add(methodAspect);
}
}
if (applicableAspects.isEmpty()) {
enhancer = null;
interceptors = ImmutableMap.of();
callbacks = null;
return;
}
BytecodeGen.EnhancerBuilder enhancerBuilder = BytecodeGen.enhancerBuilder(hostClass);
Method[] methods = enhancerBuilder.getEnhanceableMethods();
int numMethods = methods.length;
Multimap matchedInterceptors = ArrayListMultimap.create();
BitSet matchedMethodIndices = new BitSet();
// Iterate over aspects and add interceptors for the methods they apply to
for (MethodAspect methodAspect : applicableAspects) {
for (int methodIndex = 0; methodIndex < numMethods; methodIndex++) {
Method method = methods[methodIndex];
if (methodAspect.matches(method)) {
if (method.isSynthetic()) {
logger.log(
Level.WARNING,
"Method [{0}] is synthetic and is being intercepted by {1}."
+ " This could indicate a bug. The method may be intercepted twice,"
+ " or may not be intercepted at all.",
new Object[] {method, methodAspect.interceptors()});
}
matchedInterceptors.putAll(method, methodAspect.interceptors());
matchedMethodIndices.set(methodIndex);
}
}
}
if (matchedMethodIndices.isEmpty()) {
enhancer = null;
interceptors = ImmutableMap.of();
callbacks = null;
return;
}
try {
enhancer = enhancerBuilder.buildEnhancer(matchedMethodIndices);
} catch (Throwable e) {
throw new Errors().errorEnhancingClass(hostClass, e).toException();
}
callbacks = new InvocationHandler[matchedMethodIndices.cardinality()];
ImmutableMap.Builder> interceptorsMapBuilder =
ImmutableMap.builder();
int callbackIndex = 0;
for (int methodIndex = matchedMethodIndices.nextSetBit(0);
methodIndex >= 0;
methodIndex = matchedMethodIndices.nextSetBit(methodIndex + 1)) {
Method method = methods[methodIndex];
List deDuplicated =
ImmutableSet.copyOf(matchedInterceptors.get(method)).asList();
interceptorsMapBuilder.put(method, deDuplicated);
BiFunction