Spring 依赖注入——模拟实现(二)(方法注入)
这里紧接着上一篇,Spring 依赖注入——模拟实现(一)(对象注入)着重讲述如解决类似jar包里面的我们无法增加注解的类。
难点一
怎样获取无法加注解的类?
解决思路
1.给方法加Bean注解。
@Bean
public Point getPoint(Complex complex) {
Point point = new Point(complex);
return point;
}
2.通过包扫描,扫描带有Component注解的类,再进一步扫描该类里面带有Bean注解的方法,并且执行该方法,把方法返回值作为BeanDefinition的object,键为返回值类型的名称,增加到beanPool里面。
在包扫描时,先处理带有Component注解的类,并收集相应带有Bean注解的方法(这里不能马上执行,因为若该方法里面需要有带有Component注解的类的实例为参数的话,如果在方法执行时,参数要求未能达到,那么该方法不能执行,或者报错),再去处理带有Bean注解的方法。
类设计
为了更好的描述这个方法,我们设计了BeanmethodDefinition类,里面封装了被Bean注解的方法的的元数据类,实例,方法,以及参数个数。具体内容如下。
package com.mec.spring_imitate.core;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.HashMap;
import java.util.Map;
public class BeanMethodDefinition {
private Class<?> klass;
private Object object;
private Method method;
private int parameterCount;
BeanMethodDefinition() {
}
Class<?> getKlass() {
return klass;
}
void setKlass(Class<?> klass) {
this.klass = klass;
}
Object getObject() {
return object;
}
void setObject(Object object) {
this.object = object;
}
Method getMethod() {
return method;
}
void setMethod(Method method) {
this.method = method;
Parameter[] parameters = method.getParameters();
if (parameters.length <= 0) {
parameterCount = 0;
return;
}
Map<Class<?>, Object> paraTypeMap = new HashMap<Class<?>, Object>();
for (Parameter parameter : parameters) {
paraTypeMap.put(parameter.getType(), null);
}
parameterCount = paraTypeMap.size();
}
int getParameterCount() {
return parameterCount;
}
int decrease() {
return --this.parameterCount;
}
}
难点二
如何处理处理次序及循环依赖问题?
@Bean
public Point getPoint(Complex complex) {
Point point = new Point(complex);
return point;
}
比如,图示方法中(假如该方法是在Configuration类(该类带有Conponent注解)里面的),需要Complex的一个实例作为参数,如果该参数是带有Component注解的,并且已经通过包扫描形成BeanDefinition加入到BeanFactory里面的Map,即beanPool里面,那么它是符合我们的期望的。
但如果是先扫描的是Configuration类呢(比如,当Configuration类所在包比Complex类所在包较浅时)?那么悲剧发生了,此时该方法里面的参数都不存在,所以此刻必须先押后处理。
一个简单的方案是,用一个List来收集 BeanMethodDefinition,收集了之后,再一个个判断,看能否执行,如果能执行,则执行,否则继续等待,但是,用这种方式,到底应该执行多少遍,才能执行完所有的方法呢?如果只要没执行完,就重新扫描,那么,重新扫描的依据又是什么?更可怕的是,如果某一BeanMethodDefinition不存在,而在另一扫描包内扫描才能形成。那么,就执行不下去了。
其二,当被注解的方法所需参数需要的有多个(极有可能来自于BeanFactory里面的beanPool),那么当参数不全时又该当如何处理?
解决思路
2.在方法和参数之间形成一种依赖或者是映射关系,以便更好的描述及处理这种关系。当通过包扫描处理完Component注解的类时,对于收集到的带有Bean注解的方法,我们在类ParameterDependance
中形成方法与参数的映射关系表Map<Class<?>, List<BeanMethodDefinition>> parameterDependance
,对于已经满足了相关参数要求(及所需要的参数已经加入到BeanFactory里面的beanPool里面)的方法,提取出去,放到类OnReadyBeanMethodDefinition
中的可执行列表List<BeanMethodDefinition> onReadymethodDefinitionList
里面,执行,并且每次执行都会产生一个新的BeanDefinition加入到BeanFactory里面的beanPool里面,那么,对于那些需要以此BeanDefinition为参数的BeanMethodDefinition,当参数要求得到满足时,又可以提取出去到onReadymethodDefinitionList
,执行,又形成新的BeanDefinition加入到beanPool里面,如此往复,来解决处理次序及循环依赖问题。
对于那些参数要求始终不能满足的,这里我们报出运行时异常,告知使用者,哪些参数不存在。
这里需要重新设计一下BeanMethodDefinition类,我给其加了parameterCount成员及相关方法,更改以后的BeanMethodDefinition类具体内容如下图所示。
package com.mec.spring_imitate.core;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.HashMap;
import java.util.Map;
public class BeanMethodDefinition {
private Class<?> klass;
private Object object;
private Method method;
private int parameterCount;
BeanMethodDefinition() {
}
Class<?> getKlass() {
return klass;
}
void setKlass(Class<?> klass) {
this.klass = klass;
}
Object getObject() {
return object;
}
void setObject(Object object) {
this.object = object;
}
Method getMethod() {
return method;
}
void setMethod(Method method) {
this.method = method;
Parameter[] parameters = method.getParameters();
if (parameters.length <= 0) {
parameterCount = 0;
return;
}
Map<Class<?>, Object> paraTypeMap = new HashMap<Class<?>, Object>();
for (Parameter parameter : parameters) {
paraTypeMap.put(parameter.getType(), null);
}
parameterCount = paraTypeMap.size();
}
int getParameterCount() {
return parameterCount;
}
int decrease() {
return --this.parameterCount;
}
}
增加parameterCount成员是用来表示被Bean注解标识的方法的参数个数的,使得BeanMethodDefinition类更加完整,以便后续操作。已下给出简单说明。
ClassTwo fun1(Point point);
ClassOne fun2(Comlex complex,Point point);
ClassThree fun3();
如上图所示,以参数类类型为键,及需要该参数的函数对应的BeanMethodDefinition列表为值,对于函数fun1来说,则需要一个参数Point.class,parameterCount=1;对于函数fun2来说需要两个参数Complex和Point.class,parameterCount=2;对于函数fun3来说,它是一个无参函数,parameterCount=0,所以无需参数即能执行。
这里我们是这样来进行设计的,在映射关系表parameterDependance
中,当某一参数已经满足时,将它对应的BeanMethodDefinition列表里面的所有BeanMethodDefinition的parameterCount-1(如果某个BeanMethodDefinition的parameterCount等于0,则说明所需参数已全部就位,则将其加入到onReadymethodDefinitionList
),并删除parameterDependance
里面该参数类类型所对应的BeanMethodDefinition列表。
比如,当Point.class参数已经满足时,先将对应列表里面的所有BeanMethodDefinition1,BeanMethodDefinition2的parameterCount减一,并且删除BeanMethodDefinition1,BeanMethodDefinition2,此时,BeanMethodDefinition1的parameterCount值为0,BeanMethodDefinition2的parameterCount值为1,则将BeanMethodDefinition1往onReadymethodDefinitionList
里面加,执行,执行过后再次检查parameterDependance
中是否有新的映射关系得到满足,有的话则重复上述操作,直到parameterDependance
里面只剩下那些相关参数无法被满足的函数的映射关系,此时,报出异常,指出函数因哪些参数未得到满足而不能执行。
更新后的类BeanFactory 具体内容如下图所示。
package com.mec.spring_imitate.core;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.HashMap;
import java.util.Map;
import com.mec.orm.core.PackageScanner;
import com.mec.spring_imitate.annotation.Autowired;
import com.mec.spring_imitate.annotation.Bean;
import com.mec.spring_imitate.annotation.Component;
public class BeanFactory {
private static final Map<String, BeanDefinition> beanPool;
static {
beanPool = new HashMap<String, BeanDefinition>();
}
private static boolean firstGetBean = true;
public BeanFactory() {
}
boolean isFirstGetBean() {
return firstGetBean;
}
void setFirstGetBean(boolean firstGetBean) {
BeanFactory.firstGetBean = firstGetBean;
}
public void scanBeanByPackage(String packageName) {
OnReadyBeanMethodDefinition orbmd = new OnReadyBeanMethodDefinition();
ParameterDependance parameterDependance = new ParameterDependance();
new PackageScanner() {
@Override
public void dealClass(Class<?> klass) {
if (klass.isPrimitive()
|| klass.isInterface()
|| klass.isAnnotation()
|| klass.isEnum()
|| klass.isArray()
|| !klass.isAnnotationPresent(Component.class)) {
return;
}
Object object = null;
try {
object = klass.newInstance();
BeanDefinition bd = new BeanDefinition();
bd.setKlass(klass);
bd.setObject(object);
beanPool.put(klass.getName(), bd);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
// 查找并处理Bean注解的方法
// 现在只能将所有Bean注解的方法收集起来
collectBeanMethod(klass, object, orbmd);
}
}.packageScanner(packageName);
// 现在开始检查参数方法映射表里面的方法是否满足参数要求
parameterDependance.checkOnReady(orbmd);
// 现在开始处理Bean注解的方法
processBeanMethod(parameterDependance, orbmd);
}
//根据方法取得方法参数对应的具体类型的值
private Object[] getParameterValue(Method method) {
Parameter[] parameters = method.getParameters();
int paraCount = parameters.length;
if (paraCount <= 0) {
return new Object[] {};
}
Object[] parameterValues = new Object[paraCount];
int index = 0;
for (Parameter parameter : parameters) {
Class<?> type = parameter.getType();
parameterValues[index++] = getBeanDefinition(type).getObject();
}
return parameterValues;
}
//收集带有Bean注解的方法,形成对应的BeanMethodDefinition,并判断是否可以加入到往参数方法映射表里面
private static void collectBeanMethod(Class<?> klass, Object object,
OnReadyBeanMethodDefinition orbmd) {
ParameterDependance pd = new ParameterDependance();
Method[] methods = klass.getDeclaredMethods();
for (Method method : methods) {
if (!method.isAnnotationPresent(Bean.class)) {
continue;
}
BeanMethodDefinition bmd = new BeanMethodDefinition();
bmd.setKlass(klass);
bmd.setObject(object);
bmd.setMethod(method);
if (pd.addDependance(bmd) == false) {
orbmd.in(bmd);
}
}
}
//开始执行准备列表或者说可执行列表里面的BeanMethodDefinition
private void processBeanMethod(
ParameterDependance parameterDependance,
OnReadyBeanMethodDefinition orbmd) {
//可执行列表里面的BeanMethodDefinition没执行完
while (orbmd.hasNext()) {
BeanMethodDefinition bmd = orbmd.next();
Object object = bmd.getObject();
Method method = bmd.getMethod();
Object[] parameterValues = getParameterValue(method);
try {//反射机制调用执行
Object result = method.invoke(object, parameterValues);
//方法返回值形成新的BeanDefinition加入到beanPool里面
Class<?> resultClass = result.getClass();
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setInject(true);
beanDefinition.setKlass(resultClass);
beanDefinition.setObject(result);
beanPool.put(resultClass.getName(), beanDefinition);
//重新匹配参数方法依赖表
parameterDependance.matchDependance(resultClass, orbmd);
} catch (Exception e) {
e.printStackTrace();
}
}
}
boolean hasClass(String className) {
return beanPool.containsKey(className);
}
BeanDefinition getBeanDefinition(String klassName) {
return beanPool.get(klassName);
}
BeanDefinition getBeanDefinition(Class<?> klass) {
return getBeanDefinition(klass.getName());
}
private void injectProperties(BeanDefinition beanDefinition) throws RuntimeException {
Class<?> klass = beanDefinition.getKlass();
Object object = beanDefinition.getObject();
Field[] fields = klass.getDeclaredFields();
for (Field field : fields) {
if (!field.isAnnotationPresent(Autowired.class)) {
continue;
}
// 应该先对inject进行判断,若为true,表示该对象已经完成注入;
// 这种方法可以避免循环依赖。
field.setAccessible(true);
Object value = getBean(field.getType());
if (value == null) {
throw new HasNoBeanException("类[" + klass.getName()
+ "]的[" + field.getName()
+ "]成员没有对应的Bean!");
}
try {
field.set(object, value);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
beanDefinition.setInject(true);
}
@SuppressWarnings("unchecked")
public <T> T getBean(String klassName) throws RuntimeException {
if (firstGetBean) { // 这里并没有考虑线程安全性的问题
ParameterDependance parameterDependance = new ParameterDependance();
//参数方法映射表里面还存在不满足参数要求的映射关系
if (!parameterDependance.isEmpty()) {
throw new RuntimeException(
parameterDependance.getDependanceMessage());
}
}
BeanDefinition bd = getBeanDefinition(klassName);
if (bd == null) {
return null;
}
Object result = bd.getObject();
if (!bd.isInject()) {
bd.setInject(true);
injectProperties(bd);
}
return (T) result;
}
public <T> T getBean(Class<?> klass) throws RuntimeException {
return getBean(klass.getName());
}
}
封装参数方法映射(依赖)关系的类ParameterDependance具体内容如下。
package com.mec.spring_imitate.core;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ParameterDependance {
private static final Map<Class<?>, List<BeanMethodDefinition>> parameterDependance;参数方法映射关系表
static {
parameterDependance = new ConcurrentHashMap<Class<?>, List<BeanMethodDefinition>>();
}
ParameterDependance() {
}
boolean isEmpty() {
return parameterDependance.isEmpty();
}
String getDependanceMessage() {
StringBuffer res = new StringBuffer();
for (Class<?> klass : parameterDependance.keySet()) {
List<BeanMethodDefinition> bmdList = parameterDependance.get(klass);
for (BeanMethodDefinition bmd : bmdList) {
res.append('\n').append(bmd.getMethod())
.append(" 缺少对应参数: ")
.append(klass.getName());
}
}
res.append('\n');
return res.toString();
}
boolean addDependance(BeanMethodDefinition methodDefinition) {
Method method = methodDefinition.getMethod();
int parameterCount = method.getParameterCount();
//如果是无参函数,则参数与方法之间无对应关系,已满足参数要求就不往参数方法关系映射表里面加
//如果参数要求已满足,也不往参数方法关系映射表里面加
if (parameterCount <= 0) {
return false;
}
Parameter[] parameters = method.getParameters();
for (Parameter parameter : parameters) {
Class<?> type = parameter.getType();
//判断参数方法关系映射表里面是否有该参数类型对应的BeanMethodDefinition列表,如果没有,则新建一个
if (!parameterDependance.containsKey(type)) {
parameterDependance.put(type, new LinkedList<BeanMethodDefinition>());
}
//先取得参数类型对应的BeanMethodDefinition列表,再往列表里面加
List<BeanMethodDefinition> bmdList = parameterDependance.get(type);
bmdList.add(methodDefinition);
}
return true;
}
void checkOnReady(OnReadyBeanMethodDefinition onReady) {
BeanFactory beanFactory = new BeanFactory();
for (Class<?> klass : parameterDependance.keySet()) {
//若需要的参数类型已准备好,则开始匹配
if (beanFactory.getBeanDefinition(klass) != null) {
matchDependance(klass, onReady);
}
}
}
void matchDependance(Class<?> klass, OnReadyBeanMethodDefinition onReady) {
//根据参数类型取得对应的方法列表,如果为null,则说明该参数类型没有对应的方法
List<BeanMethodDefinition> bmdList = parameterDependance.get(klass);
if (bmdList == null) {
return;
}
//循环遍历方法列表
Iterator<BeanMethodDefinition> bmditor=bmdList.iterator();
while (bmditor.hasNext()) {
BeanMethodDefinition bmd = (BeanMethodDefinition) bmditor.next();
int paraCount = bmd.decrease();paraCount成员的值减一
if (paraCount == 0) { //当BeanMethodDefinition的paraCount成员的值为0时,说明参数要求已得到满足,则将其加入到准备队列里面
onReady.in(bmd);
}
bmditor.remove(); /*无论,BeanMethodDefinition的paraCount成员的值是否为0,
都要从参数类型对应的方法列表里面删除*/
if (bmdList.isEmpty()) {//当参数类型对应的方法列表为空时,则将参数与方法映射表里面的映射关系移除
parameterDependance.remove(klass);
}
}
}
}
封装可执行列表(准备执行列表)的类OnReadyBeanMethodDefinition具体内容如下。
package com.mec.spring_imitate.core;
import java.util.LinkedList;
import java.util.List;
public class OnReadyBeanMethodDefinition {
private List<BeanMethodDefinition> onReadymethodDefinitionList;//可执行列表
OnReadyBeanMethodDefinition() {
this.onReadymethodDefinitionList = new LinkedList<BeanMethodDefinition>();
}
void in(BeanMethodDefinition bmd) {
onReadymethodDefinitionList.add(bmd);
}
BeanMethodDefinition next() {
return onReadymethodDefinitionList.remove(0);
}
boolean hasNext() {
return !onReadymethodDefinitionList.isEmpty();
}
}
运行时异常HasNoBeanException 具体内容如下。
`package com.mec.spring_imitate.core;
public class HasNoBeanException extends RuntimeException {
private static final long serialVersionUID = -900159240707170537L;
public HasNoBeanException() {
}
public HasNoBeanException(String message) {
super(message);
}
public HasNoBeanException(Throwable cause) {
super(cause);
}
public HasNoBeanException(String message, Throwable cause) {
super(message, cause);
}
public HasNoBeanException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
`
正常测试代码如下图。
package com.mec.spring_imitate.test;
import com.mec.spring_imitate.core.BeanFactory;
import com.mec.spring_imitate.some_class.ClassFive;
import com.mec.spring_imitate.some_class.ClassOne;
import com.mec.spring_imitate.some_class.ClassSex;
import com.mec.spring_imitate.some_class.ClassThree;
import com.mec.spring_imitate.some_class.Complex;
import com.mec.spring_imitate.some_class.Point;
import com.mec.spring_imitate.some_class.StudentDao;
public class Demo {
public static void main(String[] args) {
BeanFactory beanFactory = new BeanFactory();
beanFactory.scanBeanByPackage("com.mec.spring_imitate.some_class");
Complex c1 = beanFactory.getBean(Complex.class.getName());
System.out.println(c1);
Point point = beanFactory.getBean(Point.class);
System.out.println(point);
ClassOne classOne = beanFactory.getBean(ClassOne.class);
System.out.println(classOne);
StudentDao dao = beanFactory.getBean(StudentDao.class);
dao.getStudentById("32432");
// ClassThree three = beanFactory.getBean(ClassThree.class);
// System.out.println("three:" + three);
ClassFive five=beanFactory.getBean(ClassFive.class);
System.out.println(five);
ClassSex sex=beanFactory.getBean(ClassSex.class);
System.out.println(sex);
}
}
正常测试结果如下图。
(0.0, 0.0), Point:[0, 0]
[0, 0]
ClassOne [complex=(0.0, 0.0), Point:[0, 0], str=null, point=[0, 0]]
database:com.mec.orm.core.Database@d716361
com.mec.spring_imitate.some_class.ClassFive@6ff3c5b5
com.mec.spring_imitate.some_class.ClassSex@3764951d
异常测试代码如下图。
package com.mec.spring_imitate.test;
import com.mec.spring_imitate.core.BeanFactory;
import com.mec.spring_imitate.some_class.ClassFive;
import com.mec.spring_imitate.some_class.ClassOne;
import com.mec.spring_imitate.some_class.ClassSex;
import com.mec.spring_imitate.some_class.ClassThree;
import com.mec.spring_imitate.some_class.Complex;
import com.mec.spring_imitate.some_class.Point;
import com.mec.spring_imitate.some_class.StudentDao;
public class Demo {
public static void main(String[] args) {
BeanFactory beanFactory = new BeanFactory();
beanFactory.scanBeanByPackage("com.mec.spring_imitate.some_class");
Complex c1 = beanFactory.getBean(Complex.class.getName());
System.out.println(c1);
Point point = beanFactory.getBean(Point.class);
System.out.println(point);
ClassOne classOne = beanFactory.getBean(ClassOne.class);
System.out.println(classOne);
StudentDao dao = beanFactory.getBean(StudentDao.class);
dao.getStudentById("32432");
ClassThree three = beanFactory.getBean(ClassThree.class);
System.out.println("three:" + three);
ClassFive five=beanFactory.getBean(ClassFive.class);
System.out.println(five);
ClassSex sex=beanFactory.getBean(ClassSex.class);
System.out.println(sex);
}
}
异常测试结果如图。
Exception in thread "main" java.lang.RuntimeException:
public com.mec.spring_imitate.some_class.ClassThree com.mec.spring_imitate.some_class.Configuration.getClassThree(com.mec.spring_imitate.some_class.ClassFour) 缺少对应参数: com.mec.spring_imitate.some_class.ClassFour
at com.mec.spring_imitate.core.BeanFactory.getBean(BeanFactory.java:181)
at com.mec.spring_imitate.test.Demo.main(Demo.java:18)
由于Configuration里面的public ClassThree getClassThree(ClassFour four)方法需要的参数ClassFour four既没有加Component注解,也没有获得它的带有Bean注解的方法,因此在BeanFactory里面的beanPool里面没有ClassFour对应的BeanDefinition,所以在其它参数方法映射关系被处理完后,参数方法映射关系表里还剩下ClassFour对应的映射关系尚未满足,所以报出以上错误,但这恰证明我们的方法是正确的。
测试用例类
Configuration 类
package com.mec.spring_imitate.some_class;
import com.mec.orm.core.Database;
import com.mec.spring_imitate.annotation.Bean;
import com.mec.spring_imitate.annotation.Component;
@Component
public class Configuration {
public Configuration() {
}
@Bean
public Database getDatabase() {
Database database = new Database();
return database;
}
@Bean
public ClassFive getClassFive(Complex complex) {
return new ClassFive(complex);
}
@Bean
public ClassThree getClassThree(ClassFour four) {
return new ClassThree(four);
}
@Bean
public ClassSex getClassSex(ClassFive five,ClassSeven seven) {
return new ClassSex(five, seven);
}
}
ClassThree类
package com.mec.spring_imitate.some_class;
public class ClassThree {
private ClassFour four;
public ClassThree(ClassFour four) {
this.four = four;
}
public ClassFour getFour() {
return four;
}
public void setFour(ClassFour four) {
this.four = four;
}
@Override
public String toString() {
return "ClassThree [four=" + four + "]";
}
}
ClassFour类
package com.mec.spring_imitate.some_class;
public class ClassFour {
private ClassThree three;
public ClassFour(ClassThree three) {
this.three = three;
}
public ClassThree getThree() {
return three;
}
public void setThree(ClassThree three) {
this.three = three;
}
@Override
public String toString() {
return "ClassFour [three=" + three + "]";
}
}
ClassFive类
package com.mec.spring_imitate.some_class;
public class ClassFive {
private Complex complex;
public ClassFive() {
}
public ClassFive(Complex complex) {
this.complex=complex;
}
}
ClassSex类
package com.mec.spring_imitate.some_class;
public class ClassSex {
private ClassFive five;
private ClassSeven seven;
public ClassSex() {
}
public ClassSex(ClassFive five,ClassSeven seven) {
this.five=five;
this.seven=seven;
}
}
ClassSeven类
package com.mec.spring_imitate.some_class;
import com.mec.spring_imitate.annotation.Component;
@Component
public class ClassSeven {
public ClassSeven() {
}
}
总结
无法增加注解的类,通过给方法增加注解Bean的方式也能变为BeanFactory里面的BeanDefinition,然后再通过注入的方式来处理循环依赖,如果采用XML文件配置的方式,无需增加注解,而通过Bean标签,指明哪个类以及成员需要注入即可。
下载地址:https://github.com/dx99606707/depository/blob/master/Spring模拟实现依赖注入之方法注入.rar
上一篇:Spring 依赖注入——模拟实现(一)(对象注入)
下一篇:Spring 应用示例