1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.ibatis.executor.loader.javassist;
17
18 import java.lang.reflect.Method;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.Set;
22
23 import javassist.util.proxy.MethodHandler;
24 import javassist.util.proxy.Proxy;
25 import javassist.util.proxy.ProxyFactory;
26
27 import org.apache.ibatis.executor.ExecutorException;
28 import org.apache.ibatis.executor.loader.AbstractEnhancedDeserializationProxy;
29 import org.apache.ibatis.executor.loader.AbstractSerialStateHolder;
30 import org.apache.ibatis.executor.loader.ResultLoaderMap;
31 import org.apache.ibatis.executor.loader.WriteReplaceInterface;
32 import org.apache.ibatis.io.Resources;
33 import org.apache.ibatis.logging.Log;
34 import org.apache.ibatis.logging.LogFactory;
35 import org.apache.ibatis.reflection.ExceptionUtil;
36 import org.apache.ibatis.reflection.factory.ObjectFactory;
37 import org.apache.ibatis.reflection.property.PropertyCopier;
38 import org.apache.ibatis.reflection.property.PropertyNamer;
39 import org.apache.ibatis.session.Configuration;
40
41
42
43
44 public class JavassistProxyFactory implements org.apache.ibatis.executor.loader.ProxyFactory {
45
46 private static final String FINALIZE_METHOD = "finalize";
47 private static final String WRITE_REPLACE_METHOD = "writeReplace";
48
49 public JavassistProxyFactory() {
50 try {
51 Resources.classForName("javassist.util.proxy.ProxyFactory");
52 } catch (Throwable e) {
53 throw new IllegalStateException(
54 "Cannot enable lazy loading because Javassist is not available. Add Javassist to your classpath.", e);
55 }
56 }
57
58 @Override
59 public Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration,
60 ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
61 return EnhancedResultObjectProxyImpl.createProxy(target, lazyLoader, configuration, objectFactory,
62 constructorArgTypes, constructorArgs);
63 }
64
65 public Object createDeserializationProxy(Object target, Map<String, ResultLoaderMap.LoadPair> unloadedProperties,
66 ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
67 return EnhancedDeserializationProxyImpl.createProxy(target, unloadedProperties, objectFactory, constructorArgTypes,
68 constructorArgs);
69 }
70
71 static Object createStaticProxy(Class<?> type, MethodHandler callback, List<Class<?>> constructorArgTypes,
72 List<Object> constructorArgs) {
73
74 ProxyFactory enhancer = new ProxyFactory();
75 enhancer.setSuperclass(type);
76
77 try {
78 type.getDeclaredMethod(WRITE_REPLACE_METHOD);
79
80 if (LogHolder.log.isDebugEnabled()) {
81 LogHolder.log.debug(WRITE_REPLACE_METHOD + " method was found on bean " + type + ", make sure it returns this");
82 }
83 } catch (NoSuchMethodException e) {
84 enhancer.setInterfaces(new Class[] { WriteReplaceInterface.class });
85 } catch (SecurityException e) {
86
87 }
88
89 Object enhanced;
90 Class<?>[] typesArray = constructorArgTypes.toArray(new Class[constructorArgTypes.size()]);
91 Object[] valuesArray = constructorArgs.toArray(new Object[constructorArgs.size()]);
92 try {
93 enhanced = enhancer.create(typesArray, valuesArray);
94 } catch (Exception e) {
95 throw new ExecutorException("Error creating lazy proxy. Cause: " + e, e);
96 }
97 ((Proxy) enhanced).setHandler(callback);
98 return enhanced;
99 }
100
101 private static class EnhancedResultObjectProxyImpl implements MethodHandler {
102
103 private final Class<?> type;
104 private final ResultLoaderMap lazyLoader;
105 private final boolean aggressive;
106 private final Set<String> lazyLoadTriggerMethods;
107 private final ObjectFactory objectFactory;
108 private final List<Class<?>> constructorArgTypes;
109 private final List<Object> constructorArgs;
110
111 private EnhancedResultObjectProxyImpl(Class<?> type, ResultLoaderMap lazyLoader, Configuration configuration,
112 ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
113 this.type = type;
114 this.lazyLoader = lazyLoader;
115 this.aggressive = configuration.isAggressiveLazyLoading();
116 this.lazyLoadTriggerMethods = configuration.getLazyLoadTriggerMethods();
117 this.objectFactory = objectFactory;
118 this.constructorArgTypes = constructorArgTypes;
119 this.constructorArgs = constructorArgs;
120 }
121
122 public static Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration,
123 ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
124 final Class<?> type = target.getClass();
125 EnhancedResultObjectProxyImpl callback = new EnhancedResultObjectProxyImpl(type, lazyLoader, configuration,
126 objectFactory, constructorArgTypes, constructorArgs);
127 Object enhanced = createStaticProxy(type, callback, constructorArgTypes, constructorArgs);
128 PropertyCopier.copyBeanProperties(type, target, enhanced);
129 return enhanced;
130 }
131
132 @Override
133 public Object invoke(Object enhanced, Method method, Method methodProxy, Object[] args) throws Throwable {
134 final String methodName = method.getName();
135 try {
136 synchronized (lazyLoader) {
137 if (WRITE_REPLACE_METHOD.equals(methodName)) {
138 Object original;
139 if (constructorArgTypes.isEmpty()) {
140 original = objectFactory.create(type);
141 } else {
142 original = objectFactory.create(type, constructorArgTypes, constructorArgs);
143 }
144 PropertyCopier.copyBeanProperties(type, enhanced, original);
145 if (lazyLoader.size() > 0) {
146 return new JavassistSerialStateHolder(original, lazyLoader.getProperties(), objectFactory,
147 constructorArgTypes, constructorArgs);
148 } else {
149 return original;
150 }
151 }
152 if (lazyLoader.size() > 0 && !FINALIZE_METHOD.equals(methodName)) {
153 if (aggressive || lazyLoadTriggerMethods.contains(methodName)) {
154 lazyLoader.loadAll();
155 } else if (PropertyNamer.isSetter(methodName)) {
156 final String property = PropertyNamer.methodToProperty(methodName);
157 lazyLoader.remove(property);
158 } else if (PropertyNamer.isGetter(methodName)) {
159 final String property = PropertyNamer.methodToProperty(methodName);
160 if (lazyLoader.hasLoader(property)) {
161 lazyLoader.load(property);
162 }
163 }
164 }
165 }
166 return methodProxy.invoke(enhanced, args);
167 } catch (Throwable t) {
168 throw ExceptionUtil.unwrapThrowable(t);
169 }
170 }
171 }
172
173 private static class EnhancedDeserializationProxyImpl extends AbstractEnhancedDeserializationProxy
174 implements MethodHandler {
175
176 private EnhancedDeserializationProxyImpl(Class<?> type, Map<String, ResultLoaderMap.LoadPair> unloadedProperties,
177 ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
178 super(type, unloadedProperties, objectFactory, constructorArgTypes, constructorArgs);
179 }
180
181 public static Object createProxy(Object target, Map<String, ResultLoaderMap.LoadPair> unloadedProperties,
182 ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
183 final Class<?> type = target.getClass();
184 EnhancedDeserializationProxyImpl callback = new EnhancedDeserializationProxyImpl(type, unloadedProperties,
185 objectFactory, constructorArgTypes, constructorArgs);
186 Object enhanced = createStaticProxy(type, callback, constructorArgTypes, constructorArgs);
187 PropertyCopier.copyBeanProperties(type, target, enhanced);
188 return enhanced;
189 }
190
191 @Override
192 public Object invoke(Object enhanced, Method method, Method methodProxy, Object[] args) throws Throwable {
193 final Object o = super.invoke(enhanced, method, args);
194 return o instanceof AbstractSerialStateHolder ? o : methodProxy.invoke(o, args);
195 }
196
197 @Override
198 protected AbstractSerialStateHolder newSerialStateHolder(Object userBean,
199 Map<String, ResultLoaderMap.LoadPair> unloadedProperties, ObjectFactory objectFactory,
200 List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
201 return new JavassistSerialStateHolder(userBean, unloadedProperties, objectFactory, constructorArgTypes,
202 constructorArgs);
203 }
204 }
205
206 private static class LogHolder {
207 private static final Log log = LogFactory.getLog(JavassistProxyFactory.class);
208 }
209
210 }