【JavaEE】自主实现Spring核心IOC和AOP之实现类的注入

2 篇文章 0 订阅
1 篇文章 0 订阅

我们先来给出一个复杂的类;这个类的成员是类类型的成员。

public class A {
	private B b;
	private C c;
	
	public A() {
	}

	public void doSomething() {
		System.out.println("A类的b成员:" + b);
		System.out.println("A类的c成员:" + c);
	}
}
public class B {
	private C c;
	
	public B() {
	}

	public void doSomething() {
		System.out.println("输出B类的c成员:" + c);
	}
}
public class C {
	public C() {
	}
	
	public void doSomething() {
		System.out.println("C类没有成员");
	}
}

对于上述类的注入,不能仅仅是简简单单的newInstance()就可以实现的,因为A类的成员也是类类型的成员。
现在考虑这么一种思路:将某应用所涉及的类及其对象,都集中存储到一个集合(池子)中;凡是在这个集合中的类,尤其是这些类的成员类型也在这个池子中,则,这些成员的初始化都从池子中的对象还给予!那么,A类的B类类型成员,C类类型成员,还有B类中的C类类型成员,都由一段代码自动完成“初始化”。以后使用这些类的对象时,一律从这个集合(池子)中取。
构建一个容器(上下文),在这个容器中存储类及其对象;在使用这些类的对象时,基本上都是从这个容器中获取的;这些类的成员,若其类型也在容器中,则,它们将被自动初始化,且用容器中的对象完成初始化。需要说明的是,对于类中的成员的初始化选择,应该由用户决定。
选择权的实现有两种具体的方法:
1、通过XML配置映射关系;
2、通过注解配置映射关系。
这里,我们采用的是注解方式实现。我们增加一个Component注解,

@Retention(RUNTIME)
@Target(TYPE)
public @interface Component {
	String name() default "";
}

Component注解:这是对类的注解。只要一个类有Component注解,那么表示这个类将被放入Bean容器中。
我们通过包扫描技术包扫描实现上述的想法:

public void scanBeanByPackage(String packageName) {
		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();
				}

			}
		}.packageScanner(packageName);
	}
	

上述的beanPool是一个map集合,键是类的名字,值是自己定义的BeanDefinition类,

public class BeanDefinition {
	private Class<?> klass;
	private Object object;
	//是已经完成注入
	private boolean inject;
	//是否是单例的
	private boolean singleton;
	
	public BeanDefinition() {
		this.singleton = true;
		this.inject = false;
	}

	Class<?> getKlass() {
		return klass;
	}

	void setKlass(Class<?> klass) {
		this.klass = klass;
	}

	Object getObject() {
		return object;
	}

	void setObject(Object object) {
		this.object = object;
	}

	boolean isInject() {
		return inject;
	}

	void setInject(boolean inject) {
		this.inject = inject;
	}

	boolean isSingleton() {
		return singleton;
	}

	void setSingleton(boolean singleton) {
		this.singleton = singleton;
	}
}

上述操作完成了对有@Componnet注解的类的实例化,并将其放到池子中的操作,但是,没有实现对这个实例中成员的注入工作。对于类类型的成员的初始化,我们应该再增加一个Autowired注解。有这个注解的成员,应该被自动找到依赖关系并进行实例化。

@Retention(RUNTIME)
@Target(FIELD)
public @interface Autowired {
	String name() default "";
}

除了用注解方式实现依赖关系的描述外,还可以用XML方式配置依赖
关系;这种情况下,对于Bean的发现,可能需要多次“包扫描”或者“XML配置解析”;那么,在没有完全扫描完之前,是没有办法确定依赖关系的完整性的!因此,在包扫描或解析XML配置时,不能急于处理依赖关系,即,不能急于完成“注入”工作!完成注入的最佳实际,应该延迟到GetBean()时!
这可以称之为:懒汉模式【懒汉模式、饿汉模式】
所以说,我们应该完善getBean()方法;

	public static <T> T getBean(Class<?> klass) {
		return getBean(klass.getName());
	}
	
	@SuppressWarnings("unchecked")
	public static <T> T getBean(String klassName) {
		BeanDefinition beanDefinition = beanPool.get(klassName);
		if (beanDefinition == null) {
			System.out.println(klassName + "没有找到");
			return null;
		}
		
		Object object = beanDefinition.getObject();
		if (!beanDefinition.isSingleton()) {
			Class<?> klass = beanDefinition.getKlass();
			try {
				Object obj =klass.newInstance();
				beanDefinition.setObject(obj);
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		}
		if (!beanDefinition.isInject() || !beanDefinition.isSingleton()) {
			beanDefinition.setInject(true);
			// TODO 这里应该开始实现对类的注入;
			
		}
			
		return (T) object;
	}

上述,TODO可以看到,我们这时完成的因该是对成员的注入,这时,已经把所有提供的类全部进行了包扫描,并且已经放到了beanPool中,我们只需要将他们取出来,并进行赋值。
Autowired注解:对要注入的成员的注解(也可以通过set方法得到该类)。一个成员有该注解,那么说明这个成员需要被注入。有可能该成员是一个类类型的成员,其的内部还有一个成员也有Autowired注解,也需要被注入但是目前还没有被注入,那么我们就需要先将那个内部成员注入,之后才能对该成员进行注入。这时,这个Autowired注解就对应了一个Component注解。这就会涉及到一个很复杂的问题:循环注入。各个类之间彼此循环依赖,这个问题我们在后面解决。

现在我们开始完成 TODO的操作,

	private static void inject(BeanDefinition beanDefinition) {
		// 这里是对成员的注入;
		injectField(beanDefinition);
		// 这里是对方法的注入,
		injectMethod(beanDefinition);
	}
	private static void injectField(BeanDefinition beanDefinition) {
		Class<?> klass = beanDefinition.getKlass();
		Field[] fields = klass.getDeclaredFields();
		for (Field field : fields) {
			if (!field.isAnnotationPresent(Autowired.class)) {
				continue ;
			}
			 Object value = getBean(field.getType());
			 field.setAccessible(true);
			 try {
				field.set(beanDefinition.getObject(), value);
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		}
	}
	private static void injectMethod(BeanDefinition beanDefinition) {
		Class<?> klass = beanDefinition.getKlass();
		Method[] methods = klass.getDeclaredMethods();
		
		for (Method method : methods) {
			if (!method.isAnnotationPresent(Autowired.class) || method.getParameterCount() != 1
					|| !method.getName().startsWith("set")) {
				continue ;
			}
			Parameter[] parameters = method.getParameters();
			Class<?> paraKlass = parameters[0].getType();
			Object obj = getBean(paraKlass);
			try {
			//	通过反射机制执行该方法
				method.invoke(beanDefinition.getObject(),obj);
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			}
		}
	} 

上述的注入方式包含了成员的注入和方法的注入。

对于成员的注入,有一步关键性的操作是:field.setAccessible(true);,但是这个操作破坏了private的权限,使得private成员可以被取得到。对于方法的注入,我们只能注入set方法,且参数是单参的,并且这个方法的权限是public的。
由此,我们已经实现了将一个类包括类里面的类类型的成员,注入到beanPool里面。其中,类和作为成员的类之间最大的区别在于前者实在scanPackage的时候注入的,后者是在getBean()的时候注入的。但是,我更倾向于Autowired注解用到set方法身上,这样可以不破坏private本身的权限。但是不论怎样,我们都可以在getBean()的时候获取到该类的对象。

其实,在getBean()方法中,有一个细节,我们已经实现了单例和多例。

Object object = beanDefinition.getObject();
		// 检测是否是单例的
		if (!beanDefinition.isSingleton()) {
		//	非单例的,重新进行new操作,并且覆盖之前的object,
			Class<?> klass = beanDefinition.getKlass();
			try {
				Object obj =klass.newInstance();
				beanDefinition.setObject(obj);
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		}

上述的Autowired注解存在缺陷:Autowired只能获取池子中的对象,而池中对象都是需要给对应的类以@Component注解;对于不可更改的Jar包中的类,就没有办法增加@Component注解,也就不能实现“注入”操作。

Bean注解:
这时,Bean注解的存在就是为了解决这样的问题。
给一个方法增加Bean注解,而将这个方法的返回值对象和返回值对象类型作为键值对,存储到池子中!在反射机制执行该方法之后,我们就可以实现将一个类注入到池子中。

Bean注解的第二个应用场合:
若相关类没有提供可用的构造方法;所谓的没有提供可用的构造方法包括相关构造方法是private的,或者,构造方法不能直接调用,或者,构造方法不能直接生成对象!在这种情况下,由于对于Component注解的处理是通过调用相关类的无参构造产生的,那么,对于上述情况,就不能产生
这个类的对象!此时,可以通过Bean注解,调用合适的获取该类对象的方法,取得这个类的对象,并加入BeanPool中!

Bean注解的第三个应用场合:
相关类的对象,不是简单无参构造就能直接使用的;意思是:这个类虽然存在无参构造,但是,无参构造出来的对象不能直接使用。那么,在这种情况下,通过Bean注解的方法,完成其对象所必须的基础数据,从而使得该对象可用!

有Bean注解且含有参数的方法的处理:这里的关键是:方法所依赖的参数是否满足?如果满足,则,该方法是可以执行并得到一个Bean的。如果多个方法的参数形成了循环依赖关系,则,应将这种循环依赖环,告知用户。其核心是:检测依赖是否满足。这里可以先考虑构造MethodDefinition类。
MethodDefinition中应该存储方法反射执行所需要的内容:
1、对象;
2、方法本身;
3、参数对象集合。
这里最难解决的问题是:参数所需要的对象可能是随机满足的。
将所有不满足依赖关系的方法中的参数形成一个Map,此Map的键是参数类型,而值是MethodDefinition所形成的List。再准备一个满足要求的MethodDefinition的列表。

综上所述:
准备3种东西:
1、不能满足依赖关系的MethodDefinition列表:列表1;
2、参数类型为键,MethodDefinition为值的Map;
3、满足了依赖关系的MethodDefinition列表:列表2。
如下图所示:
在这里插入图片描述
大致思路:对于有Bean注解的方法,先获取其参数类型,检测是否在beanPool里面存在,如果存在,则继续获取其参数,如果不存在,将该方法放到unOkMethods里面,并将该参数的类型作为键,该方法放到dependenceMethodPool中以该参数为键所对应的值里面,知道全部遍历完成。如果方法全部可以获取到,那么,直接将该方法放到okMethods中,等待后续的执行。我们需要在BeanFactory类里面,只要put一次,我们就需要检测,是否有参数满足可以获取到。一旦可以获取到,就把该参数从dependenceMethodPool中删去。如果某个方法的参数全部可以获取到,直接将unOkMethods里面的该方法删除,将该方法加入到okMethods列表里面。在包扫描所有的类后,我们需要执行这些方法。
代码如下:

public class MethodDependence {
	private final static Map<Class<?>, List<MethodDefinition>> dependenceMethodPool = new HashMap<Class<?>, List<MethodDefinition>>();
	private static final List<MethodDefinition> okMethods = new LinkedList<MethodDefinition>();
	private static final List<MethodDefinition> unOkMethods = new LinkedList<MethodDefinition>();
	
	MethodDependence() {
	}

	static void addUnInvokeMethod(Map<Class<?>, Integer> mapPara,MethodDefinition methodDefinition) {
		unOkMethods.add(methodDefinition);
		
		for (Class<?> para : mapPara.keySet() ) {
			if (!dependenceMethodPool.containsKey(para)) {
				List<MethodDefinition> methodDefinitionList = new LinkedList<MethodDefinition>();
				dependenceMethodPool.put(para, methodDefinitionList);
			}
			List<MethodDefinition> methodDefinitionList = dependenceMethodPool.get(para);
			methodDefinitionList.add(methodDefinition);
		}
	}
	
	static void checkMethod(Class<?> beanClass) {
		 List<MethodDefinition> methodList = dependenceMethodPool.get(beanClass);
		 if (methodList.isEmpty() || methodList == null) {
			 dependenceMethodPool.remove(beanClass);
			 return ;
		 }
		 for (MethodDefinition method : methodList ) {
			 if (method.sub() <= 0) {
				 unOkMethods.remove(method);
				 okMethods.add(method);
			 }
		 }
		 dependenceMethodPool.remove(beanClass);
	}
	
	static void invokeMethod() {
		while(!okMethods.isEmpty()) {
			MethodDefinition methodDefinition = okMethods.remove(0);
			Method method = methodDefinition.getMethod();
			Object object = methodDefinition.getObject();
			Class<?> klass = object.getClass();
			
			BeanFactory.invokeMethodWithPara(klass, method, object);
		}
	}
}

	// BeanFactory类
	private static void dealMethodWithPara(Class<?> klass, Method method,Object object) {
		Map<Class<?>, Integer> paraMap = getMethodPara(method);
		if (paraMap.isEmpty()) {
			invokeMethodWithPara(klass,method,object);
			return ;
		}
		
		MethodDefinition methodDefinition = new MethodDefinition();
		methodDefinition.setMethod(method);
		methodDefinition.setObject(object);
		methodDefinition.setParaCount(paraMap.size());
		
		MethodDependence.addUnInvokeMethod(paraMap, methodDefinition);
	}
	private static void dealBean(BeanDefinition beanDefinition) {
		Class<?> klass = beanDefinition.getKlass();
		Method[] methods = klass.getMethods();
		for (Method method : methods) {
			if (!method.isAnnotationPresent(Bean.class) ){
				continue ;
			}
			if ( method.getParameterCount() > 0) {
				// 处理带参的方法;
				dealMethodWithPara(klass,method,beanDefinition.getObject());
				continue ;
			}
			Object object = beanDefinition.getObject();
			Class<?> returnKlass = method.getReturnType();
			try {
				Object obj = method.invoke(object,new Object[] {});
				BeanDefinition bean = new BeanDefinition();
				bean.setKlass(returnKlass);
				bean.setObject(obj);

				beanPool.put(returnKlass.getName(), bean);
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			}
		}
	}
	// 这里的Integer类型可以是随便的,因为这里只关心键Class<?>类型。
	private static Map<Class<?>, Integer> getMethodPara(Method method) {
		Class<?>[] parametertypes = method.getParameterTypes();
		Map<Class<?>, Integer> paraMap = new HashMap<Class<?>, Integer>();
		List<Class<?>> okParas = new LinkedList<Class<?>>();
		for (Class<?> parameterType : parametertypes) {
			paraMap.put(parameterType, 0);
			if (beanPool.get(parameterType.getName()) != null) {
				okParas.add(parameterType);
			}
		}
		
		for (Class<?> one : okParas) {
			paraMap.remove(one);
		}
		return paraMap;
	}
	public static void invokeMethodWithPara(Class<?> klass,Method method,Object object) {
		Class<?>[] paraTypes = method.getParameterTypes();
		Object[] objects = new Object[paraTypes.length];
		for (int index = 0;index < paraTypes.length;index++) {
			objects[index] = getBean(paraTypes[index]);
		}
		
		Class<?> returnPara = method.getReturnType();
		BeanDefinition bean = new BeanDefinition();
		try {
			Object obj = method.invoke(object, objects);
			bean.setObject(obj);
			bean.setKlass(returnPara);
			beanPool.put(returnPara.getName(), bean);
			
			
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
	}
	
public class MethodDefinition {
	private Object object;
	private Method method;
	private int paraCount;
	
	MethodDefinition() {
		this.paraCount = 0;
	}

	Object getObject() {
		return object;
	}

	void setObject(Object object) {
		this.object = object;
	}

	Method getMethod() {
		return method;
	}

	void setMethod(Method method) {
		this.method = method;
	}
	
	void setParaCount(int paraCount) {
		this.paraCount = paraCount;
	}

	int sub() {
		return --this.paraCount;
	}

	int getParaCount() {
		return paraCount;
	}
	
}

1、遇到一个带参方法,先检测其所有参数;
     1.1、若参数满足要求,则,暂不处理(可以将count–),这里的count是“参数个数”
     1.2、若参数不能得到满足,则,将这个参数类型作为键, 这个MethodDefinition作为值,存储到Map中;
      1.3、当对所有参数都进行了检测,若存在未满足的参数, 则,将MethodDefinition存储到列表1;
      1.4、当所有参数都满足要求,则,将其存储到列表2;
2、每处理完一个Bean,都扫描Map,将依赖这个Bean的MethodDefinition 的count–; 若count为0了,则,将其存储到列表2中。

事实上,类的名字有时会非常的长,为了方便我们记忆,Spring给出了Qualifier注解。
Qualifier注解:用在方法或参数身上,表示它的别名。
别名的引出,我们从beanPool中获取或存储时就不应该简简单单的直接get或者put了。因为有的类型会写别名,有的类型用户可能不会去写别名。但是,**代码编到了这个程度,不应该去修改大量的代码。**为此,我们应该改动最少的代码来完成这一目的。
我们可以增加一个类,名字叫做BeanPool;

public class BeanPool {
	private static final Map<String, BeanDefinition> classBeanPool
			= new HashMap<>();
	private static final Map<String, BeanDefinition> aliasBeanPool
	= new HashMap<>();
	
	static void put(String key, BeanDefinition bean) {
		Class<?> beanClass = bean.getKlass();
		String beanClassName = beanClass.getName();
		if (beanClassName.equals(key)) {
			classBeanPool.put(key, bean);
		} else {
			aliasBeanPool.put(key, bean);
		}
	}
	
	static BeanDefinition get(String key) {
		BeanDefinition bean = classBeanPool.get(key);
		if (bean == null) {
			bean = aliasBeanPool.get(key);
		}
		
		return bean;
	}
}

这个类里面有两个map,一个是类的别名对应的BeanDefinition,一个是类名字对应的BeanDefinition。重做get和put方法,这样,我们可以修改最少的代码。
对于getMethodPara(Method method)方法,我们不应该只是返回一个键是Class<?>,值是随便的Map了,而应该改为键是String,值是Class<?>,String表示别名或者类的名称,Class<?>表示对应的类的类型。这样一来,可以方便我们获取和存储。
现在我们只需要将 Map<Class<?>, Integer>类型改为Map

private static Map<String ,Class<?>> getMethodPara(Method method) {
		Map<String ,Class<?>> paraPool = new HashMap<>();
		
		Class<?>[] parameterTypes = method.getParameterTypes();
		// 去重;去掉类型重复的参数类型
		for (int index = 0; index < parameterTypes.length; index++) {
			if (parameterTypes[index].isAnnotationPresent(Qualifier.class)) {
				String name = parameterTypes[index].getAnnotation(Qualifier.class).name();
				paraPool.put(name, parameterTypes[index]);
			} else {
				paraPool.put(parameterTypes[index].getName(), parameterTypes[index]);
			}
		}
		List<Class<?>> klassList = new ArrayList<Class<?>>();
		for (Class<?> type : paraPool.values()) {
			BeanDefinition beanDefinition = dealPara(type);
			if (beanDefinition != null) {
				klassList.add(type);
			}
		}
		for (int index = 0; index < klassList.size(); index++) {
			if (klassList.get(index).isAnnotationPresent(Qualifier.class)) {
				paraPool.remove(klassList.get(index).getAnnotation(Qualifier.class).name());
			} else {
				paraPool.remove(klassList.get(index).getName());	
			}
		}
		
		return paraPool;
	}

到此为止,我们类的注入已经大致完成。至于其中有些类存在的循环依赖,循环注入等,在后续博文会写到。
我们来检测一下我们的代码吧:
看例子:

@Component
public class TwoClass {
	@Autowired
	//	这里的Complex类是存放在jar包里的,无法更改这个Complex类。 所以用bean注解来完成注入。
	private Complex c;
	
	public TwoClass() {
	}
	
	@Override
	public String toString() {
		return "这是一个TwoClass的对象";
	}
	
}

@Component
public class ThreeClass {
	@Autowired
	private TwoClass two;
	
	public ThreeClass() {
	}

	public TwoClass getTwo() {
		return two;
	}

	public void setTwo(TwoClass two) {
		this.two = two;
	}
	
}

@Component
public class OneClass {
	@Autowired
	private TwoClass two;
	@Autowired
	private Complex complex;
	@Autowired
	private Calendar date;
	
	public OneClass() {
	}

	public Complex getComplex() {
		return complex;
	}

	public void setComplex(Complex complex) {
		this.complex = complex;
	}

	@Autowired
	public void setTwo(TwoClass two) {
		this.two = two;
	}

	public TwoClass getTwo() {
		return two;
	}

	public void doOneThing() {
		System.out.println(two);
	}
	
}
@Component
public class Config {

	public Config() {
	}
	
	@Bean
	public Calendar getComplex(@Qualifier(name="one") Complex one) {
		System.out.println("将Complex这个不能更改的类,装入池子!");
		Complex c = new Complex(one.getReal(),one.getVir());
		c.setReal(c.getReal() + 2);
		System.out.println("c:" + c);
		
		Calendar date = Calendar.getInstance();
		return date;
	}
	
	@Bean(name="one")
	public Complex getComplex() {
		Complex complex = new Complex(3,4);
		
		return complex;
	}
}

public class Test { 

	public static void main(String[] args) {
		BeanFactory.scanPackage("com.mec.mpring.demo");
		
		BeanFactory beanFactory = new BeanFactory();
		
		Calendar one = beanFactory.getBean(Calendar.class);
		System.out.println(one);
		System.out.println("-----------------------");
		OneClass oneClass = beanFactory.getBean(OneClass.class);
		System.out.println(oneClass);
	}

}

执行结果如下:
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值