From f3e1450a7966062212b435864cf6797e725af74c Mon Sep 17 00:00:00 2001 From: skywalker Date: Sun, 19 Mar 2017 11:20:34 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E5=BE=AA=E7=8E=AF=E5=BC=95?= =?UTF-8?q?=E7=94=A8=E5=AF=BC=E8=87=B4=E7=9A=84StackOverflow=EF=BC=8Cjson?= =?UTF-8?q?=E9=85=8D=E7=BD=AEString=E5=BC=BA=E8=BD=AC=E7=AD=89=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 17 +++++ src/main/java/bean/BeanContainer.java | 75 ++++++++++++++++--- src/main/java/bean/BeanWrapper.java | 10 ++- .../exception/CircularReferenceException.java | 14 ++++ src/main/java/conf/JsonSource.java | 2 +- src/test/java/ioc/IOCTest.java | 26 ++----- src/test/java/ioc/Student.java | 6 +- src/test/java/ioc/Teacher.java | 9 --- src/test/java/json/JsonTest.java | 2 +- 9 files changed, 117 insertions(+), 44 deletions(-) create mode 100644 src/main/java/bean/exception/CircularReferenceException.java diff --git a/pom.xml b/pom.xml index 4f5e1fd..8a5f09f 100644 --- a/pom.xml +++ b/pom.xml @@ -15,6 +15,19 @@ 1.8 + + maven-assembly-plugin + + + + com.allen.capturewebdata.Main + + + + jar-with-dependencies + + + jar @@ -33,16 +46,20 @@ 4.12 test + org.slf4j slf4j-api 1.7.22 + provided ch.qos.logback logback-classic 1.1.8 + provided + org.reflections reflections diff --git a/src/main/java/bean/BeanContainer.java b/src/main/java/bean/BeanContainer.java index 3e3bdd4..287be5d 100644 --- a/src/main/java/bean/BeanContainer.java +++ b/src/main/java/bean/BeanContainer.java @@ -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; @@ -152,18 +153,44 @@ public T get(Class 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; } + /** + * 在候选者中寻找最合适的一个,逻辑: + *
    + *
  • 如果candidates为空,那么直接返回null.
  • + *
  • 如果candidates大小为1,那么返回第一个.
  • + *
  • 如果candidates大小大于1,那么寻找targetClass与requiredType相等(==)的候选者.
  • + *
+ * + * @throws IllegalStateException 如果发现多个候选者 + */ + private BeanWrapper findEligibleCandidate(List candidates, Class 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. * @@ -190,14 +217,28 @@ public List getBeansWithType(Class clazz) { * * @param beanWrapper {@linkplain BeanWrapper} 非空 * @return 初始化的bean实例 + * @throws CircularReferenceException 如果满足以下条件: + *
    + *
  • {@link #allowCircularReference}设为false.
  • + *
  • bean尚未完成初始化(构造器).
  • + *
  • bean的{@link Scope}不为{@linkplain Scope#SINGLETOM}.
  • + *
*/ 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; } @@ -214,9 +255,14 @@ private String getCandidatesInfo(List 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); } @@ -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; } diff --git a/src/main/java/bean/BeanWrapper.java b/src/main/java/bean/BeanWrapper.java index b3dd7f5..c9a0d15 100644 --- a/src/main/java/bean/BeanWrapper.java +++ b/src/main/java/bean/BeanWrapper.java @@ -17,7 +17,6 @@ public class BeanWrapper { * 是否正在创建对应的bean实例. */ private boolean currentlyInCreation = false; - //TODO 被构造器依赖? public Scope getScope() { return scope; @@ -51,6 +50,14 @@ 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{" + @@ -58,6 +65,7 @@ public String toString() { ", beanName='" + beanName + '\'' + ", targetClass=" + targetClass + ", target=" + target + + ", currentlyInCreation=" + currentlyInCreation + '}'; } diff --git a/src/main/java/bean/exception/CircularReferenceException.java b/src/main/java/bean/exception/CircularReferenceException.java new file mode 100644 index 0000000..38a4756 --- /dev/null +++ b/src/main/java/bean/exception/CircularReferenceException.java @@ -0,0 +1,14 @@ +package bean.exception; + +/** + * 循环引用. + * + * @author skywalker + */ +public class CircularReferenceException extends RuntimeException { + + public CircularReferenceException(String message) { + super(message); + } + +} diff --git a/src/main/java/conf/JsonSource.java b/src/main/java/conf/JsonSource.java index 64cadd0..bde741f 100644 --- a/src/main/java/conf/JsonSource.java +++ b/src/main/java/conf/JsonSource.java @@ -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) { diff --git a/src/test/java/ioc/IOCTest.java b/src/test/java/ioc/IOCTest.java index e742eac..45147f9 100644 --- a/src/test/java/ioc/IOCTest.java +++ b/src/test/java/ioc/IOCTest.java @@ -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); } /** @@ -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()); } /** @@ -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 task = () -> container.get(Student.class); - Future s1 = service.submit(task); - Future s2 = service.submit(task); + Callable task = () -> container.get(Teacher.class); + Future s1 = service.submit(task); + Future s2 = service.submit(task); service.shutdown(); Assert.assertTrue(s1.get() == s2.get()); } @@ -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 students = container.getBeansWithType(Student.class); - System.out.println(students); + System.out.println(container.get(Student.class)); } } diff --git a/src/test/java/ioc/Student.java b/src/test/java/ioc/Student.java index 7c6f241..29d8eb1 100644 --- a/src/test/java/ioc/Student.java +++ b/src/test/java/ioc/Student.java @@ -1,5 +1,8 @@ package ioc; +import bean.Scope; +import bean.annotation.Component; + import javax.annotation.Resource; /** @@ -7,7 +10,8 @@ * * @author skywalker */ -public abstract class Student { +@Component(scope = Scope.PROTOTYPE) +public class Student { @Resource private Teacher teacher; diff --git a/src/test/java/ioc/Teacher.java b/src/test/java/ioc/Teacher.java index cc5901a..a724b35 100644 --- a/src/test/java/ioc/Teacher.java +++ b/src/test/java/ioc/Teacher.java @@ -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) { diff --git a/src/test/java/json/JsonTest.java b/src/test/java/json/JsonTest.java index 6fa22a6..68f9e2a 100644 --- a/src/test/java/json/JsonTest.java +++ b/src/test/java/json/JsonTest.java @@ -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); }