Commit c121a9e6 authored by Jason Song's avatar Jason Song Committed by GitHub

Merge pull request #1676 from nobodyiam/fix-1670

use weak reference to hold bean objects so that they could be garbage collected
parents d93a6ba4 790ee89d
package com.ctrip.framework.apollo.spring.property; package com.ctrip.framework.apollo.spring.property;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
...@@ -16,7 +17,7 @@ public class SpringValue { ...@@ -16,7 +17,7 @@ public class SpringValue {
private MethodParameter methodParameter; private MethodParameter methodParameter;
private Field field; private Field field;
private Object bean; private WeakReference<Object> beanRef;
private String beanName; private String beanName;
private String key; private String key;
private String placeholder; private String placeholder;
...@@ -25,7 +26,7 @@ public class SpringValue { ...@@ -25,7 +26,7 @@ public class SpringValue {
private boolean isJson; private boolean isJson;
public SpringValue(String key, String placeholder, Object bean, String beanName, Field field, boolean isJson) { public SpringValue(String key, String placeholder, Object bean, String beanName, Field field, boolean isJson) {
this.bean = bean; this.beanRef = new WeakReference<>(bean);
this.beanName = beanName; this.beanName = beanName;
this.field = field; this.field = field;
this.key = key; this.key = key;
...@@ -38,7 +39,7 @@ public class SpringValue { ...@@ -38,7 +39,7 @@ public class SpringValue {
} }
public SpringValue(String key, String placeholder, Object bean, String beanName, Method method, boolean isJson) { public SpringValue(String key, String placeholder, Object bean, String beanName, Method method, boolean isJson) {
this.bean = bean; this.beanRef = new WeakReference<>(bean);
this.beanName = beanName; this.beanName = beanName;
this.methodParameter = new MethodParameter(method, 0); this.methodParameter = new MethodParameter(method, 0);
this.key = key; this.key = key;
...@@ -60,6 +61,10 @@ public class SpringValue { ...@@ -60,6 +61,10 @@ public class SpringValue {
} }
private void injectField(Object newVal) throws IllegalAccessException { private void injectField(Object newVal) throws IllegalAccessException {
Object bean = beanRef.get();
if (bean == null) {
return;
}
boolean accessible = field.isAccessible(); boolean accessible = field.isAccessible();
field.setAccessible(true); field.setAccessible(true);
field.set(bean, newVal); field.set(bean, newVal);
...@@ -68,6 +73,10 @@ public class SpringValue { ...@@ -68,6 +73,10 @@ public class SpringValue {
private void injectMethod(Object newVal) private void injectMethod(Object newVal)
throws InvocationTargetException, IllegalAccessException { throws InvocationTargetException, IllegalAccessException {
Object bean = beanRef.get();
if (bean == null) {
return;
}
methodParameter.getMethod().invoke(bean, newVal); methodParameter.getMethod().invoke(bean, newVal);
} }
...@@ -103,8 +112,16 @@ public class SpringValue { ...@@ -103,8 +112,16 @@ public class SpringValue {
return isJson; return isJson;
} }
boolean isTargetBeanValid() {
return beanRef.get() != null;
}
@Override @Override
public String toString() { public String toString() {
Object bean = beanRef.get();
if (bean == null) {
return "";
}
if (isField()) { if (isField()) {
return String return String
.format("key: %s, beanName: %s, field: %s.%s", key, beanName, bean.getClass().getName(), field.getName()); .format("key: %s, beanName: %s, field: %s.%s", key, beanName, bean.getClass().getName(), field.getName());
......
package com.ctrip.framework.apollo.spring.property; package com.ctrip.framework.apollo.spring.property;
import com.ctrip.framework.apollo.core.utils.ApolloThreadFactory;
import com.google.common.collect.LinkedListMultimap; import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import java.util.Collection; import java.util.Collection;
import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactory;
public class SpringValueRegistry { public class SpringValueRegistry {
private static final long CLEAN_INTERVAL_IN_SECONDS = 5;
private final Map<BeanFactory, Multimap<String, SpringValue>> registry = Maps.newConcurrentMap(); private final Map<BeanFactory, Multimap<String, SpringValue>> registry = Maps.newConcurrentMap();
private final AtomicBoolean initialized = new AtomicBoolean(false);
private final Object LOCK = new Object(); private final Object LOCK = new Object();
public void register(BeanFactory beanFactory, String key, SpringValue springValue) { public void register(BeanFactory beanFactory, String key, SpringValue springValue) {
...@@ -22,6 +29,11 @@ public class SpringValueRegistry { ...@@ -22,6 +29,11 @@ public class SpringValueRegistry {
} }
registry.get(beanFactory).put(key, springValue); registry.get(beanFactory).put(key, springValue);
// lazy initialize
if (initialized.compareAndSet(false, true)) {
initialize();
}
} }
public Collection<SpringValue> get(BeanFactory beanFactory, String key) { public Collection<SpringValue> get(BeanFactory beanFactory, String key) {
...@@ -31,4 +43,33 @@ public class SpringValueRegistry { ...@@ -31,4 +43,33 @@ public class SpringValueRegistry {
} }
return beanFactorySpringValues.get(key); return beanFactorySpringValues.get(key);
} }
private void initialize() {
Executors.newSingleThreadScheduledExecutor(ApolloThreadFactory.create("SpringValueRegistry", true)).scheduleAtFixedRate(
new Runnable() {
@Override
public void run() {
try {
scanAndClean();
} catch (Throwable ex) {
ex.printStackTrace();
}
}
}, CLEAN_INTERVAL_IN_SECONDS, CLEAN_INTERVAL_IN_SECONDS, TimeUnit.SECONDS);
}
private void scanAndClean() {
Iterator<Multimap<String, SpringValue>> iterator = registry.values().iterator();
while (!Thread.currentThread().isInterrupted() && iterator.hasNext()) {
Multimap<String, SpringValue> springValues = iterator.next();
Iterator<Entry<String, SpringValue>> springValueIterator = springValues.entries().iterator();
while (springValueIterator.hasNext()) {
Entry<String, SpringValue> springValue = springValueIterator.next();
if (!springValue.getValue().isTargetBeanValid()) {
// clear unused spring values
springValueIterator.remove();
}
}
}
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment