1 /*
2  * Copyright 2018-2020 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      https://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.springframework.data.util;
17
18 import lombok.experimental.UtilityClass;
19
20 import java.util.List;
21 import java.util.Map;
22
23 import org.springframework.aop.support.AopUtils;
24 import org.springframework.core.io.support.SpringFactoriesLoader;
25 import org.springframework.util.Assert;
26 import org.springframework.util.ClassUtils;
27 import org.springframework.util.ConcurrentReferenceHashMap;
28
29 /**
30  * Proxy type detection utilities, extensible via {@link ProxyDetector} registered via Spring factories.
31  * 
32  * @author Oliver Gierke
33  * @soundtrack Victor Wooten - Cruising Altitude (Trypnotix)
34  */

35 @UtilityClass
36 public class ProxyUtils {
37
38     private static Map<Class<?>, Class<?>> USER_TYPES = new ConcurrentReferenceHashMap<>();
39
40     private static final List<ProxyDetector> DETECTORS = SpringFactoriesLoader.loadFactories(ProxyDetector.class,
41             ProxyUtils.class.getClassLoader());
42
43     static {
44         DETECTORS.add(ClassUtils::getUserClass);
45     }
46
47     /**
48      * Returns the user class for the given type.
49      * 
50      * @param type must not be {@literal null}.
51      * @return
52      */

53     public static Class<?> getUserClass(Class<?> type) {
54
55         Assert.notNull(type, "Type must not be null!");
56
57         return USER_TYPES.computeIfAbsent(type, it -> {
58
59             Class<?> result = it;
60
61             for (ProxyDetector proxyDetector : DETECTORS) {
62                 result = proxyDetector.getUserType(result);
63             }
64
65             return result;
66         });
67     }
68
69     /**
70      * Returns the user class for the given source object.
71      * 
72      * @param source must not be {@literal null}.
73      * @return
74      */

75     public static Class<?> getUserClass(Object source) {
76
77         Assert.notNull(source, "Source object must not be null!");
78
79         return getUserClass(AopUtils.getTargetClass(source));
80     }
81
82     /**
83      * SPI to extend Spring's default proxy detection capabilities.
84      *
85      * @author Oliver Gierke
86      */

87     public interface ProxyDetector {
88
89         /**
90          * Returns the user class for the given type.
91          * 
92          * @param type will never be {@literal null}.
93          * @return
94          */

95         Class<?> getUserType(Class<?> type);
96     }
97 }
98