Skip to content

Commit

Permalink
解决循环引用导致的StackOverflow,json配置String强转等问题
Browse files Browse the repository at this point in the history
  • Loading branch information
seaswalker committed Mar 19, 2017
1 parent 7080529 commit f3e1450
Show file tree
Hide file tree
Showing 9 changed files with 117 additions and 44 deletions.
17 changes: 17 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,19 @@
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>com.allen.capturewebdata.Main</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
</plugins>
</build>
<packaging>jar</packaging>
Expand All @@ -33,16 +46,20 @@
<version>4.12</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.22</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.8</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
Expand Down
75 changes: 63 additions & 12 deletions src/main/java/bean/BeanContainer.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import bean.annotation.Init;
import bean.annotation.Value;
import bean.converter.*;
import bean.exception.CircularReferenceException;
import conf.Source;
import org.apache.commons.lang3.StringUtils;
import org.reflections.ReflectionUtils;
Expand Down Expand Up @@ -152,18 +153,44 @@ public <T> T get(Class<T> beanClass) {
candidates.add(entry.getValue());
}
}
int size = candidates.size();
if (size > 1) {
throw new IllegalStateException("Given bean class has one more candidates: " + getCandidatesInfo(candidates) + ".");
}
if (size == 1) {
BeanWrapper beanWrapper = candidates.get(0);
BeanWrapper beanWrapper = findEligibleCandidate(candidates, beanClass);
if (beanWrapper != null) {
result = (T) loadBean(beanWrapper);
}
}
return result;
}

/**
* 在候选者中寻找最合适的一个,逻辑:
* <ul>
* <li>如果candidates为空,那么直接返回null.</li>
* <li>如果candidates大小为1,那么返回第一个.</li>
* <li>如果candidates大小大于1,那么寻找targetClass与requiredType相等(==)的候选者.</li>
* </ul>
*
* @throws IllegalStateException 如果发现多个候选者
*/
private <T> BeanWrapper findEligibleCandidate(List<BeanWrapper> candidates, Class<T> requiredType) {
int size = candidates.size();
BeanWrapper result = null;
if (size == 1) {
result = candidates.get(0);
} else if (size > 1) {
for (int i = 0; i < size; i++) {
if (candidates.get(i).getTargetClass() == requiredType) {
result = candidates.get(i);
break;
}
}
if (result == null) {
throw new IllegalStateException("Given bean class has one more candidates: " +
getCandidatesInfo(candidates) + ".");
}
}
return result;
}

/**
* 获得所有clazz类型的bean.
*
Expand All @@ -190,14 +217,28 @@ public <T> List<T> getBeansWithType(Class<T> clazz) {
*
* @param beanWrapper {@linkplain BeanWrapper} 非空
* @return 初始化的bean实例
* @throws CircularReferenceException 如果满足以下条件:
* <ul>
* <li>{@link #allowCircularReference}设为false.</li>
* <li>bean尚未完成初始化(构造器).</li>
* <li>bean的{@link Scope}不为{@linkplain Scope#SINGLETOM}.</li>
* </ul>
*/
private Object loadBean(BeanWrapper beanWrapper) {
Object result = beanWrapper.getTarget();
if (beanWrapper.isCurrentlyInCreation()) {
if (!allowCircularReference || (result == null) || beanWrapper.getScope() != Scope.SINGLETOM) {
throw new CircularReferenceException("Circular reference bean '" + beanWrapper.getBeanName() + "'.");
}
return result;
}
if (result == null || beanWrapper.getScope() == Scope.PROTOTYPE) {
result = createBean(beanWrapper.getTargetClass());
beanWrapper.setCurrentlyInCreation(true);
result = createBean(beanWrapper);
if (result != null && beanWrapper.getScope() == Scope.SINGLETOM) {
beanWrapper.setTarget(result);
}
beanWrapper.setCurrentlyInCreation(false);
}
return result;
}
Expand All @@ -214,9 +255,14 @@ private String getCandidatesInfo(List<BeanWrapper> candidates) {
/**
* 创建bean实例.
*/
private Object createBean(Class beanClass) {
private Object createBean(BeanWrapper beanWrapper) {
Class beanClass = beanWrapper.getTargetClass();
Object instance = newInstance(beanClass);
if (instance != null) {
if (allowCircularReference && beanWrapper.getScope() == Scope.SINGLETOM) {
//earlyReference
beanWrapper.setTarget(instance);
}
if (source != null) {
injectConfs(instance, beanClass);
}
Expand Down Expand Up @@ -285,17 +331,22 @@ private Object[] resolveArgs(Parameter[] parameters) {
*/
private Object resolveArg(Parameter parameter) {
Value value = parameter.getAnnotation(Value.class);
Object result;
Object result = null;
//jdk1.8 javac -parameters参数
String name = (parameter.isNamePresent() ? parameter.getName() : null);
if (value != null) {
//jdk1.8 javac -parameters参数
String name = (parameter.isNamePresent() ? parameter.getName() : null);
result = resolveConfByValue(parameter, name, parameter.getType(), parameter.getParameterizedType());
} else {
Class type = parameter.getType();
if (type.isPrimitive()) {
throw new IllegalStateException("Can't inject to primitive type: " + parameter + ".");
}
result = get(type);
if (name != null) {
result = get(name);
}
if (result == null) {
result = get(type);
}
}
return result;
}
Expand Down
10 changes: 9 additions & 1 deletion src/main/java/bean/BeanWrapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ public class BeanWrapper {
* 是否正在创建对应的bean实例.
*/
private boolean currentlyInCreation = false;
//TODO 被构造器依赖?

public Scope getScope() {
return scope;
Expand Down Expand Up @@ -51,13 +50,22 @@ public void setTarget(Object target) {
this.target = target;
}

public boolean isCurrentlyInCreation() {
return currentlyInCreation;
}

public void setCurrentlyInCreation(boolean currentlyInCreation) {
this.currentlyInCreation = currentlyInCreation;
}

@Override
public String toString() {
return "BeanWrapper{" +
"scope=" + scope +
", beanName='" + beanName + '\'' +
", targetClass=" + targetClass +
", target=" + target +
", currentlyInCreation=" + currentlyInCreation +
'}';
}

Expand Down
14 changes: 14 additions & 0 deletions src/main/java/bean/exception/CircularReferenceException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package bean.exception;

/**
* 循环引用.
*
* @author skywalker
*/
public class CircularReferenceException extends RuntimeException {

public CircularReferenceException(String message) {
super(message);
}

}
2 changes: 1 addition & 1 deletion src/main/java/conf/JsonSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ private String readAsString() throws IOException {

@Override
public String get(String key) {
return (String) doGet(key);
return doGet(key).toString();
}

private Object doGet(String key) {
Expand Down
26 changes: 7 additions & 19 deletions src/test/java/ioc/IOCTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public void getByName() throws LoadException {
Injecter injecter = new Injecter().source(source);
BeanContainer container = injecter.basePackage("ioc").inject();
Teacher teacher = (Teacher) container.get("teacher");
teacher.printStudent();
System.out.println(teacher);
}

/**
Expand All @@ -43,18 +43,7 @@ public void getByNameNull() {
Injecter injecter = new Injecter();
BeanContainer container = injecter.basePackage("ioc").inject();
Teacher teacher = (Teacher) container.get("tea");
teacher.printStudent();
}

/**
* 测试bean name冲突的情况.需要将{@link Student}的名改为teacher.
*/
@Test(expected = IllegalStateException.class)
public void nameComplict() {
Injecter injecter = new Injecter();
BeanContainer container = injecter.basePackage("ioc").inject();
Teacher teacher = (Teacher) container.get("teacher");
teacher.printStudent();
System.out.println(teacher.toString());
}

/**
Expand All @@ -81,9 +70,9 @@ public void threadSafe() throws ExecutionException, InterruptedException {
Injecter injecter = new Injecter();
BeanContainer container = injecter.basePackage("ioc").inject();
ExecutorService service = Executors.newFixedThreadPool(2);
Callable<Student> task = () -> container.get(Student.class);
Future<Student> s1 = service.submit(task);
Future<Student> s2 = service.submit(task);
Callable<Teacher> task = () -> container.get(Teacher.class);
Future<Teacher> s1 = service.submit(task);
Future<Teacher> s2 = service.submit(task);
service.shutdown();
Assert.assertTrue(s1.get() == s2.get());
}
Expand All @@ -96,10 +85,9 @@ public void getWithType() throws LoadException {
CompositeSource source = new CompositeSource();
source.registerSource(new JsonSource("etc/conf.json"), new PropertiesSource("etc/db.properties"),
new XmlSource("etc/test.xml"));
Injecter injecter = new Injecter().source(source);
Injecter injecter = new Injecter().source(source).allowCircularReference(true);
BeanContainer container = injecter.basePackage("ioc").inject();
List<Student> students = container.getBeansWithType(Student.class);
System.out.println(students);
System.out.println(container.get(Student.class));
}

}
6 changes: 5 additions & 1 deletion src/test/java/ioc/Student.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package ioc;

import bean.Scope;
import bean.annotation.Component;

import javax.annotation.Resource;

/**
* 学生.
*
* @author skywalker
*/
public abstract class Student {
@Component(scope = Scope.PROTOTYPE)
public class Student {

@Resource
private Teacher teacher;
Expand Down
9 changes: 0 additions & 9 deletions src/test/java/ioc/Teacher.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,6 @@
@Component
public class Teacher implements BeanContainerAware {

private Student student;

public Teacher(Student student) {
this.student = student;
}

public void printStudent() {
System.out.println(student);
}

@Override
public void setBeanContainer(BeanContainer beanContainer) {
Expand Down
2 changes: 1 addition & 1 deletion src/test/java/json/JsonTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class JsonTest {
public void test() throws LoadException {
Source source = new JsonSource("etc/conf.json");
Injecter injecter = new Injecter();
BeanContainer beanContainer = injecter.basePackage("xml").source(source).inject();
BeanContainer beanContainer = injecter.basePackage("json").source(source).inject();
JsonHolder holder = beanContainer.get(JsonHolder.class);
System.out.println(holder);
}
Expand Down

0 comments on commit f3e1450

Please sign in to comment.