继上次对象的成员注入后,当我们对某个类对象进行注入时,如果该类对象中有一个待注入的成员是jar包里的类对象,由于我们无法对jar包进行@Component的注解,所以就得采用在一个方法中实例化该对象再注入的手段。
@Bean
public Point getjar(Complex c) {
Point p = new Point();
return p;
}
上图该方法就是为了处理当jar包里的Point类对象作为待注入成员,我们可以通过反射机制调用将该方法,将其返回值打包成BeanDefinition然后存到BeanFactory的beanpool里面等待以后注入,这便是解决思路。
但在实际处理时我们要注意以下问题:
1.@Bean注解的方法有可能是无参的,我们把他保存下来以供后面直接反射机制调用处理就行。但如果@Bean注解的方法如果有参数且参数类是@Component注解的类(上图的方法便是如此),那我们就得考虑先后顺序问题,即应该先处理@Component注解的普通类并将其存入beanpool中,然后在处理方法的注入。
解决办法:包扫描时扫描到方法带有@Bean注解时我们先将该方法打包分类保存下来,等扫描完所有的@Component类后在进行处理
new PackageScanner() {//
@Override
public void dealClass(Class<?> klass) {
if(klass.isPrimitive() || klass.isAnnotation()
||klass.isEnum()||klass.isArray()
||! klass.isAnnotationPresent(Component.class)) {
return;
}
Object obj = null;
try {
obj = klass.newInstance();
BeanDefinition bd = new BeanDefinition();
bd.setKlass(klass);
bd.setObj(obj);
beanpool.put(klass.getName(), bd);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
collentmethod(obj,klass,on);
//没扫描一个类就直接调用处理保存方法
}
}.packageScanner(pagename);
private static void collentmethod(Object obj,Class<?>klass,Onreday on) {
//对扫面到的类的方法进行处理
ParamterDependce pa = new ParamterDependce();
Method[] method = klass.getMethods();
for(int i = 0; i < method.length;i++ ) {
if(!method[i].isAnnotationPresent(Bean.class)) {
//看该方法有么有Bean注解
continue;
}
//BeanMessageDefinnation便是保存方法的类
BeanMessageDefinnation bmd = new BeanMessageDefinnation();//有的话就直接newBeanMessageDefinnation对象保存
bmd.setKlass(klass);
bmd.setMethod(method[i]);//处理方法
bmd.setObj(obj);
if(!pa.addDependce(bmd)) {//判断有参无参
on.in(bmd);//无参直接保存
}
}
2.该如何处理带参数的方法,这是整套体系的核心所在。因为有些特殊情况我们不得不重视:
<a>多个方法参数相同或包含同一个参数;也就是说我们每处理一个方法是否应该都要先遍历一遍beanpool。
否,因为他的时间复杂度实在太高了,这里我们的解决办法就是用另外一个容器储存map,将参数的类类型作为键,将所有需求参数的方法组成一个list作为值,这样只需遍历一遍beanpool得到可供使用的参数。
//用于储存带参方法的容器
private static final Map<Class<?>,List<BeanMessageDefinnation>> parameterDependce;
//用参数类型为键,需要该参数值的所有方法形成list作为值;
static {
parameterDependce = new HashMap<Class<?>,List<BeanMessageDefinnation>>();
}
//该方法用于给parameterDependce容器添加内容,每次传递过来一个@bean注解方法
boolean addDependce(BeanMessageDefinnation bmd){
Method method = bmd.getMethod();//得到该方法名
int methodcount = bmd.getParacount();//取得参数类型个数
if(methodcount <= 0) {//无参方法处理
return false;
}
Parameter[] paras = method.getParameters();//取得所有参数类型成员
for(int i = 0; i < paras.length; i++) {//遍历参数成员
Class<?> paratype = paras[i].getType();//得到该参数类型
if(!parameterDependce.containsKey(paratype)) {
//判断容器里是否包含该键
parameterDependce.put(paratype,
new ArrayList<BeanMessageDefinnation>());//不包含则生成一条新记录
}
List<BeanMessageDefinnation> list = parameterDependce.get(paratype);//包含则将自己加入需求参数的数组中
list.add(bmd);
}
return true;
}
<b> 如何得知某个方法参数已经全部准备妥当了可以进行处理并存入map以供其他情况使用(例如下面的<c>情况);这里我们采用标记计数法,即当扫描到该方法在保存时,将他的参数类型个数保存下来,在每满足该方法的一个参数后我们便给标记减一。当标记减到零时将该方法从parameterDependce 容器中的list捞出来放到ready容器中以供处理,无参方法则直接放入ready容器中以供处理,parameterDependce 容器中每处理完一个参数键对应的list就该销毁整条记录。
void matchDependance(Class<?> klass, Onreday onReady) {
//该方法用于对以注入的参数的方法list进行操作
List<BeanMessageDefinnation> list = parameterDependce.get(klass);
//先得到这个已经满足的list
if(list == null) {
return;
}
for(int i = 0 ; i < list.size(); i++) {//遍历
int count = list.get(i).increase();//给每个方法的参数个数进行--
if(count == 0) {//如果为0则证明该方法已完毕
onReady.in(list.get(i));
}
list.remove(i);//无论是否完毕都要删除掉该方法
if(list.isEmpty()) {
parameterDependce.remove(klass);
//若该参数对应的数组已经空了,则删除整个记录
}
}
}
public void setMethod(Method method) {//计数的方法
this.method = method;
Parameter[] par = method.getParameters();
if(par.length == 0) {
this.paracount = 0;
return;
}
Map<Class<?>,Object> Methmap = new HashMap<Class<?>,Object>();
for(Parameter parameter : par) {
Methmap.put(parameter.getType(), null);
}
this.paracount = Methmap.size();
this.fcount = Methmap.size();
}
<c> 一个方法的返回值是另一个方法的参数;例如:A方法的返回值是B方法的参数,那就应该在每处理完一个方法后检查beanpool看是否又满足新的需求。
void check(Onreday orbmd) {//检查已经可用的依赖
BeanFactory bf = new BeanFactory();
for(Class<?> klass : parameterDependce.keySet()) {
//遍历依赖关系的map,得到键集合在beanfactor里的map找是否满足
if( bf.getBeanDefinition(klass) != null) {
matchDependance(klass, orbmd);//满足了就调用下面方法
}
}
}
private static void processBeanMethod(Onreday orbm,ParamterDependce pa) {
//处理ready的列表里准备好的方法
while(orbm.hasNext()) {//如果准备好的list里有下一成员
BeanMessageDefinnation bm = orbm.next();
deallist( bm);//调用该方法给beanpool里添加新的bean
pa.check(orbm);
//每次执行完后就在检查是否有有新的关系满足了,实现处理和生成同时操作
}
}
private static void deallist(BeanMessageDefinnation bm) {
Parameter[] pa =bm.getMethod().getParameters();
Object[] op = null;
if(pa.length == 0) {//判断有参无参
op = new Object[] {};
}else {
op = new Object[pa.length];
}
for(int i = 0; i<pa.length;i++) {
op[i] = beanpool.get(pa[i].getType().getName()).getObj(); //从map里得到参数具体值
}
try {
Object ob = bm.getMethod().invoke(bm.getObj(), op);
BeanDefinition bd = new BeanDefinition();
bd.setKlass(ob.getClass());
bd.setObj(ob);
bd.setInject(false);
beanpool.put(ob.getClass().getName(),bd);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
在处理完问题后,我们的模型就成了,在这里我在次简述下大体思路:先扫描,扫描到普通类时处理完存入map;扫描到方法时先分情况保存下来,无参的直接加入到ready容器中,带参的先保存到parameterDependce容器中等加工完成后在加入到ready容器中,然后对reday容器中准备好的方法进行处理在保存到map中以供使用。
以下是源代码和测试结果,感兴趣的小伙伴可以了解下(解决了上次遗留的循环依赖,并还有些其他处理过程)。
BeanFactory类:
public class BeanFactory {
private static boolean firstGetBean = true;
private static final Map<String,BeanDefinition> beanpool;
//注入时应该在map里找,即注入的是map中存在的值
static {
beanpool = new HashMap<String, BeanDefinition>();
}
boolean isFirstGetBean() {
return firstGetBean;
}
void setFirstGetBean(boolean firstGetBean) {
BeanFactory.firstGetBean = firstGetBean;
}
public static void scanpackage(String pagename) {
ParamterDependce pa = new ParamterDependce();
Onreday on = new Onreday();
new PackageScanner() {
@Override
public void dealClass(Class<?> klass) {
if(klass.isPrimitive() || klass.isAnnotation()
||klass.isEnum()||klass.isArray()
||!klass.isAnnotationPresent(Component.class)) {
return;
}
Object obj = null;
try {
obj = klass.newInstance();
BeanDefinition bd = new BeanDefinition();
bd.setKlass(klass);
bd.setObj(obj);
beanpool.put(klass.getName(), bd);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
collentmethod(obj,klass,on);//没扫描一个类就直接调用处理保存方法
}
}.packageScanner(pagename);
pa.check(on);//在扫面并装入完所有类对象后先检查以满足的方法
processBeanMethod (on,pa);//处理方法
}
private static void processBeanMethod(Onreday orbm,ParamterDependce pa) {//处理ready的列表里准备好的方法
while(orbm.hasNext()) {//如果准备好的list里有下一成员
BeanMessageDefinnation bm = orbm.next();
deallist( bm);//调用该方法给beanpool里添加新的bean
pa.check(orbm)//每次执行完后就在检查是否有有新的关系满足了,实现处理和生成同时操作
}
}
private static void deallist(BeanMessageDefinnation bm) {
Parameter[] pa =bm.getMethod().getParameters();
Object[] op = null;
if(pa.length == 0) {//判断有参无参
op = new Object[] {};
}else {
op = new Object[pa.length];
}
for(int i = 0; i<pa.length;i++) {
op[i] = beanpool.get(pa[i].getType().getName()).getObj(); //从map里得到参数具体值
}
try {
Object ob = bm.getMethod().invoke(bm.getObj(), op);
BeanDefinition bd = new BeanDefinition();
bd.setKlass(ob.getClass());
bd.setObj(ob);
bd.setInject(false);
beanpool.put(ob.getClass().getName(),bd);
} catch (Exception e) {
e.printStackTrace();
}
}
private static void collentmethod(Object obj,Class<?>klass,Onreday on) {//对扫面到的类的方法进行处理
ParamterDependce pa = new ParamterDependce();
Method[] method = klass.getMethods();
for(int i = 0; i < method.length;i++ ) {
if(!method[i].isAnnotationPresent(Bean.class)) {
//看该方法有么有Bean注解
continue;
}
//BeanMessageDefinnation
BeanMessageDefinnation bmd = new BeanMessageDefinnation();//有的话就直接new对象
bmd.setKlass(klass);
bmd.setMethod(method[i]);//处理方法
bmd.setObj(obj);
if(!pa.addDependce(bmd)) {
on.in(bmd);
}
}
}
//通过键取出definnation
BeanDefinition getBeanDefinition(String klassname) {
return beanpool.get(klassname);
}
BeanDefinition getBeanDefinition(Class<?> klassname) {
return getBeanDefinition(klassname.getName());
}
private void injectPro(BeanDefinition bd) throws RuntimeException {//给该对象待注入的成员注入
Class<?> klass = bd.getKlass();
// System.out.println(klass.getName());
Object obj = bd.getObj();
Field[] field = klass.getDeclaredFields();
for(int i = 0; i < field.length;i++) {
if(!field[i].isAnnotationPresent(Autowired.class)) {
//如果没有注入标记就跳过
continue;
}
field[i].setAccessible(true);
//有标记的话就先修改权限,然后在map中通过class找对应的值
Object value = getBean(field[i].getType());
if(value == null) {
//如果value == null,就说明该成员待注入却没在map中没找到,则说明有错那就抛异常
throw new StopException("类{"+klass.getName()+"}中成员{"+field[i].getName()+"}在map中没有对应的值");
}
try {
field[i].set(obj,value);
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}//给该对象的成员赋值
System.out.println("成员["+klass.getName()+"."+field[i].getName()+"]以完成注入");
}
bd.setInject(true);
//对所有成员注入完成之后一定要对对象的注入标记做更改,这样才能保证只注入一次
}
//通过键直接取得对象
@SuppressWarnings("unchecked")
public <T> T getBean(String klassname) throws RuntimeException {
//也可以将Objet作为返回类型,不过在使用时得要强转下,所以泛型好用些
if (firstGetBean) { // 这里并没有考虑线程安全性的问题
ParamterDependce parameterDependance = new ParamterDependce();
if (!parameterDependance.isEmpty()) {
throw new RuntimeException(
parameterDependance.getDependanceMessage());
}
}
BeanDefinition bd = getBeanDefinition(klassname);
if(bd == null) {
return null;
}
Object res = bd.getObj();
// System.out.println(!bd.isInject());
if(!bd.isInject()) {//判断该对象的成员是否已经被注入过了
bd.setInject(true);
injectPro(bd);
}
return (T) res;
}
public <T> T getBean(Class<?> klassname) throws RuntimeException {
return getBean(klassname.getName());
}
}
BeanMessageDefinnation类:
public class BeanMessageDefinnation {
private Class<?> klass;
private Object obj;
private Method method;
private int paracount;
private int fcount;
public BeanMessageDefinnation(){
}
public int getFcount() {
return fcount;
}
public int getParacount() {
return paracount;
}
int increase() {
return --this.paracount;
}
public Class<?> getKlass() {
return klass;
}
public void setKlass(Class<?> klass) {
this.klass = klass;
}
public Object getObj() {
return obj;
}
public void setObj(Object obj) {
this.obj = obj;
}
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
Parameter[] par = method.getParameters();
if(par.length == 0) {
this.paracount = 0;
return;
}
Map<Class<?>,Object> Methmap = new HashMap<Class<?>,Object>();
for(Parameter parameter : par) {
Methmap.put(parameter.getType(), null);
}
this.paracount = Methmap.size();
this.fcount = Methmap.size();
}
}
ParamterDependce类:
public class ParamterDependce {
public static boolean sign = true;
private static final Map<Class<?>,List<BeanMessageDefinnation>> parameterDependce;//用于从处理待注入的方法
//用参数类型为键,需要该参数作为注入值的所有方法形成list作为值;
static {
parameterDependce = new HashMap<Class<?>,List<BeanMessageDefinnation>>();
}
public ParamterDependce() {
}
boolean isEmpty() {
return parameterDependce.isEmpty();
}
String getDependanceMessage() {
StringBuffer res = new StringBuffer();
for (Class<?> klass : parameterDependce.keySet()) {
List<BeanMessageDefinnation> bmdList = parameterDependce.get(klass);
for (BeanMessageDefinnation bmd : bmdList) {
res.append('\n').append("类:"+bmd.getKlass().getName()+"中的:")
.append(bmd.getMethod().getName()).append("方法")
.append(" 缺少对应参数: ")
.append(klass.getName());
}
}
res.append('\n');
return res.toString();
}
boolean addDependce(BeanMessageDefinnation bmd){
//该方法用于给parameterDependce容器添加内容,每次传递过来一个bean注解方法
Method method = bmd.getMethod();//得到该方法名
int methodcount = bmd.getParacount();//取得参数类型个数
if(methodcount <= 0) {//无参方法
return false;
}
Parameter[] paras = method.getParameters();//取得所有参数类型成员
for(int i = 0; i < paras.length; i++) {//遍历参数成员
Class<?> paratype = paras[i].getType();//得到该参数类型
if(!parameterDependce.containsKey(paratype)) {
//判断容器里是否包含该键
parameterDependce.put(paratype, new ArrayList<BeanMessageDefinnation>());//不包含则生成一条记录
}
List<BeanMessageDefinnation> list = parameterDependce.get(paratype);//将自己加入需求参数的数组中
list.add(bmd);
}
return true;
}
void check(Onreday orbmd) {//检查已经可用的依赖
BeanFactory bf = new BeanFactory();
for(Class<?> klass : parameterDependce.keySet()) {
//遍历依赖关系的map,得到键集合在beanfactor里的map找是否满足
if( bf.getBeanDefinition(klass) != null) {
matchDependance(klass, orbmd);//满足了就调用下面方法
}
}
}
void matchDependance(Class<?> klass, Onreday onReady) {
//该方法用于对以注入的参数的方法list进行操作
List<BeanMessageDefinnation> list = parameterDependce.get(klass);
//先得到这个已经满足的list
if(list == null) {
return;
}
for(int i = 0 ; i < list.size(); i++) {//遍历
int count = list.get(i).increase();//给每个方法的参数个数进行--
if(count == 0) {//如果为0则证明该方法已完毕
onReady.in(list.get(i));
}
list.remove(i);//无论是否完毕都要删除掉该方法
if(list.isEmpty()) {
parameterDependce.remove(klass);
//若该参数对应的数组已经空了,则删除整个记录
}
}
}
}
Oneready类:
public class Onreday {
private List<BeanMessageDefinnation> onreadlist;
public Onreday() {
onreadlist = new LinkedList<BeanMessageDefinnation>();
}
void in(BeanMessageDefinnation bmd) {
onreadlist.add(bmd);
}
BeanMessageDefinnation next() {
return onreadlist.remove(0);
}
boolean hasNext() {
return !onreadlist.isEmpty();
}
}
Point类:
public class Point {
private int a;
private int b;
@Autowired
private Complex c;
public Point() {
}
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
public int getB() {
return b;
}
public void setB(int b) {
this.b = b;
}
@Override
public String toString() {
return "Point [a=" + a + ", b=" + b + "]"+c
;
}
}
测试结果:
public static void main(String[] args) {
BeanFactory bf = new BeanFactory();
BeanFactory.scanpackage("com.mec.test");
Point a = bf.getBean(Point.class);
System.out.println(a);
/*output:
成员[com.mec.test.Point.c]以完成注入
Point [a=0, b=0](0.0,0.0)
*/
}