Spring框架中的IOC的模拟实现;循环依赖问题的递归解决;

  • 本博文内容是基于本人的学习理解与实践论证所作
  • 如有错误请积极指出,不胜感激
  • 转载请注明出处

推荐阅读:

Spring Framework中IOC&AOP手动模拟实现;基于责任链模式的AOP拦截器链;IOC循环依赖的递归处理法;

正文

1、所用到的工具

包扫描工具(如有疑问请参见往期博文)

2、所用注解及其介绍:

@Component

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

‘@Component’注解的作用对象是类,‘@Bean’注解的作用对象是类中的方法,‘@Autowired’注解的作用对象是类中的需要进行自动注入的成员。
给类加 ‘@Component’注解是为了实现此类的自动注入,目的是在想使用这些类的对象的时不再去亲自new,既直接从beanFactory里直接取出使用。

@Bean

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Bean {
}

——我们的目的是帮框架使用者生成他们想要的类的实例,并按照其意愿对这些实例进行一些注入操作。从框架使用者的角度来看:如果涉及到某些类的实例,而这些类并没有提供源代码,所以就无法通过添加@component注解或者@Autowired注解去实现(以下将称这种类为“引入类”)。于是用户可以在类中用@Bean注解一些方法,在这些方法中直接new出并返回“引入类”的实例。在扫描过程中这些方法将被自动执行,并将返回的对象装入我们的Bean工厂。 为了避免某些带参方法在执行时需要“引入类”的对象作为参数,所以在处理@bean注解时先执行无参的方法,丰富Bean工厂中的内容,同时将需要参数的方法存储起来。之后再执行那些带参的方法。

@Autowired

给方法加‘@Bean’注解的作用是为了在扫描到一个自动注入类之后,用该类的对象作为前缀执行其加中加了‘@Bean’注解的方法,并将返回值加入到bean工厂中进行处理。

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

给类中的字段加‘@Autowired’注解是为了声明该类中需要进行自动注入的字段。其中,被添加了@Autowired注解的字段中,在自动注入时,注入的将是代理类型的成员。

以上两个注解给出name属性表示支持可以用别名在Bean工厂中取到。

3、扫描过程分析:

在包扫描时筛选出注解@Component的类,在每个类中再次筛选出注解@bean注解的方法,将无参的方法先执行,需要参数的方法存储起来(存储方式是封装在类中,详见代码),待无参方法执行完后再将带参方法执行完。此时的bean工厂已经被丰富填充了,可以进行真正的注入操作了。

存储带参方法的类:BeanMethodDefination

public class BeanMethodDefination {
	private Method method;
	private Class<?> returnType;
	private Class<?>[] parametersClass;
	private Object object;
	private String beanName;
	
	//带参构造及其getters、setters
}

IOC工厂:BeanFactory

包扫描时所进行的操作

public static void scanPackage(String packageName) {
		ProxyBeanFactory proxyBeanFactory = new ProxyBeanFactory();
		//对指定包路径下的类进行扫描
		/**
		 * 层级关系依次为:@Component -> @Bean
		 */
		new PackageScanner() {
			@Override
			public void dealClass(Class<?> klass) {
				if(!klass.isAnnotationPresent(Component.class)) {
					return;
				}
				String beanName = klass.getAnnotation(Component.class).name();
				String className = klass.getName();
				try {
					proxyBeanFactory.getCGLProxy(klass);
					Object object = proxyBeanFactory.getMecProxy(klass).getObject();
					
					Method[] methods = klass.getDeclaredMethods();
					for(Method method : methods) {
						if(!method.isAnnotationPresent(Bean.class)) {
							continue;
						}
						invokeBeanMethod(method, klass, object, proxyBeanFactory, beanName);
					}
					//如果有别名,则进行别名添加
					if (beanName.length() > 0) {
						proxyBeanFactory.addBean(beanName, className);
					}
				} catch (Exception e) {
					e.printStackTrace();
				}

			}
		}.packageScan(packageName);
		//执行带参的Bean注解的方法
		for(BeanMethodDefination beanMethodDefination : methodList) {
			Object object = beanMethodDefination.getObject();
			Class<?> klass = beanMethodDefination.getClass();
			Method method = beanMethodDefination.getMethod();
			Class<?>[] parametersClass = beanMethodDefination.getParameters();
			String beanName = beanMethodDefination.getBeanName();
			
			try {
				invokeMulBeanMethod(method, klass, object, parametersClass, proxyBeanFactory, beanName);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		methodList.clear();
	}
	/**
	 *
	 * 	先执行无参的方法,添加其返回值到Bean工厂中。避免出现还未扫描到的参数参数
	 * 	然后再将带参的方法存储起来,到最后一并执行
	 * @param method @Bean注解的方法
	 * @param klass @Component注解的类
	 * @param object 注解类的实例对象
	 * @param proxyBeanFactory 代理工厂
	 * @param beanName 别名
	 * @throws Exception
	 */
	private static void invokeBeanMethod(Method method, Class<?> klass, Object object,
			ProxyBeanFactory proxyBeanFactory, String beanName) throws Exception {
		//此处接手所有的method
		
		Class<?>[] parameters = method.getParameterTypes();
		Class<?> returnType = method.getReturnType();
		if(returnType.equals(void.class)) {
			return;
		}

		if(parameters.length <= 0) {
			Object result = method.invoke(object, (Object[]) null);
			createBean(proxyBeanFactory, returnType, result, beanName);
		} else {
			BeanMethodDefination beanMethodDefination = 
					new BeanMethodDefination(method, returnType, parameters, object, beanName);
			methodList.add(beanMethodDefination);
		}
	}
	
	private static void invokeMulBeanMethod(Method method, Class<?> klass, Object object,
			Class<?>[] parametersClass, ProxyBeanFactory proxyBeanFactory, String beanName)
					throws Exception {
		
		Object[] paraObj = new Object[parametersClass.length];
		for(int index = 0; index < parametersClass.length; index++) {
			Object result = proxyBeanFactory.getBeanProxy(parametersClass[index]);
			paraObj[index] = result;
		}
		Object beanObject = method.invoke(object, paraObj);
		createBean(proxyBeanFactory, klass, beanObject, beanName);
		
	}
	
	private static void createBean(ProxyBeanFactory proxyBeanFactory, Class<?> klass, Object object, String beanName)
			throws Exception {
		if(object != null) {
			proxyBeanFactory.getCGLProxy(object);
		} else {
			proxyBeanFactory.getCGLProxy(klass);
		}
		
		if(beanName.length() > 0) {
			proxyBeanFactory.addBean(beanName, klass.getName());
		}
	}

代理工厂:ProxyBeanFactory

代理工厂在IOC的包扫描阶段使用,在扫描到Component注解类后,产生他的代理对象,并将其代理对象与原对象封装存储。关于封装类在代理工厂的代码后附上。


public class ProxyBeanFactory {
	private static final Map<String, MecProxy> mecProxymap;//类名称对应的MecProxy对象
	private static final Map<String, String> beanNamemap;//别名信息
	
	static {
		mecProxymap = new HashMap<>();
		beanNamemap = new HashMap<>();
	};
	
	public <T> T getCGLProxy(Object object) {
		return CGLProxy(object.getClass(), object);
	}
	
	@SuppressWarnings("deprecation")
	public <T> T getCGLProxy(Class<?> klass) throws InstantiationException, IllegalAccessException {
		return CGLProxy(klass, klass.newInstance());
	}
	
	public void addBean(String beanName, String className) throws Exception {
		String orgClassName = beanNamemap.get(beanName);
		if(orgClassName != null) {
			throw new BeanNameAlreadyExistException();
		}
		
		beanNamemap.put(beanName, className);
		
	}
	
	@SuppressWarnings("unchecked")
	private <T> T CGLProxy(Class<?> klass, Object object) {
		String className = klass.getName();
		MecProxy mecProxy = mecProxymap.get(className);
		
		if(mecProxy != null) {
			return (T) mecProxy.getProxy();
		} else {//产生一系列的代理
			mecProxy = new MecProxy();
			T proxy = mecProxy.getCGLProxy(object);
			mecProxy.setProxy(proxy);
			mecProxymap.put(className, mecProxy);
			
			return proxy;
		}
	}
	
	public MecProxy getMecProxy(Class<?> klass) {
		MecProxy mecProxy = mecProxymap.get(klass.getName());
		
		if(mecProxy == null) {
			return null;
		}
		
		return mecProxy;
	}
	
	public <T> T getBeanProxy(String beanName) {
		String result = beanNamemap.get(beanName);
		
		if(result != null) {
			return mecProxymap.get(result).getProxy();
		}
		
		return null;
	}
	
	public <T> T getBeanProxy(Class<?> klass) {
		String className = klass.getName();
		MecProxy mecProxy = mecProxymap.get(className);
		
		if(mecProxy != null) {
			return mecProxy.getProxy();
		}
		
		return null;
	}
	
	public String getBeanClassName(String beanName) {
		return beanNamemap.get(beanName);
	}
}

代理包装类:MecProxy

其中包含代理对象与原对象,并且提供了cglib代理的获取,在所覆盖的intercep()方法中嵌入拦截器链的拦截过程。

public class MecProxy {
	private Object proxy;//代理对象
	private Object object;//原对象,用于方法的执行
	private boolean injection;//是否注入过的判断标志
	
	protected void setInjection(boolean injection) {
		this.injection = injection;
	}

	public MecProxy() {
		injection = false;
	}

	protected boolean isInjection() {
		return injection;
	}
	
	@SuppressWarnings("unchecked")
	protected <T> T getProxy() {
		return (T) proxy;
	}
	
	protected void setProxy(Object proxy) {
		this.proxy = proxy;
	}
	
	@SuppressWarnings("deprecation")
	protected <T> T getCGLProxy(Class<?> klass) throws InstantiationException, IllegalAccessException {
		return cglProxy(klass, klass.newInstance());
	}
	
	protected <T> T getCGLProxy(Object object) {
		return cglProxy(object.getClass(), object);
	}

	@SuppressWarnings({ "unchecked" })
	private <T> T cglProxy(Class<?> klass, Object object) {
		this.object = object;
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(klass);
		enhancer.setCallback(new MethodInterceptor() {
			
			@Override
			public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
				return doInvoke(klass, method, object, args);
			}
		});
		
		return (T) enhancer.create();
	}

	private Object doInvoke(Class<?> klass, Method method, Object object, Object[] args) throws Exception {
		// 前置拦截
		Object result = null;
		IntercepterTargetDefination itd 
			= new IntercepterTargetDefination(klass, method);
		IntercepterNodes firstNode = null;
		firstNode = IntercepterFactory.getBeforeNodes(itd);
		if(firstNode != null) {
			if (!IntercepterFactory.getBeforeNodes(itd).doBefore(klass, method, args)) {
				return result;
			}
		}
		try {
			result = method.invoke(object, args);
			// 滞后拦截
			firstNode = null;
			System.out.println("itd:" + itd);
			firstNode = IntercepterFactory.getAfterNodes(itd);
			if(firstNode != null) {
				result = firstNode.doAfter(klass, method, result);
			}
		} catch (Exception e) {
			//  异常拦截
			firstNode = IntercepterFactory.getExceptionNodes(itd);
			firstNode.doException(klass, method, e);;
			throw e;
		}
		return result;
	}
	
	protected Object getObject() {
		return object;
	}
}

4、递归注入法及其问题

我们需要被将注入的对象的Autowired成员丰富起来,并返回注入对象的代理对象。也就是AOP了。事实上一开始代理对象和原始对象就一起生成了。

此时bean工厂中所有需要注入的对象就已经存在了,现在需要做的就是注入了。

扫描需要注入的类,筛选出注解了Autowired的字段,再在bean工厂中进行查找,如果此字段中又有需要注入的成员,那就可以通过递归的方式进行注入了。
此处存在一个问题:若A类有一个Autowired成员是B类类型的,而这个B类类型又有一个Autowired成员是C类类型的,
这个C类类型的成员又有一个Autowired成员是A类类型的,这种情况如果不加以控制,这个**“循环依赖”**的问题就会产生无限递归,就会导致StackOverFlow的JVM异常。

解决方法

给这个类的对象在准备注入前就加上一个已经注入的标记,这个标记默认在对象产生的时候为False,之后再进行递归注入,将此对象的标记值作为递归是否进行下去的控制条件就可以完美解决。以下贴出递归注入部分的代码。

	private static void injectBean (ProxyBeanFactory proxyBeanFactory, Object object, Class<?> klass) {
		MecProxy orgMecProxy = proxyBeanFactory.getMecProxy(klass);
		orgMecProxy.setInjection(true);//立即标记本次注入
		
		Field[] fields = klass.getDeclaredFields();
		for(Field field : fields) {
			if(!field.isAnnotationPresent(Autowired.class)) {
				continue;
			}
			Class<?> fieldClass = field.getType();
			MecProxy mecProxy = proxyBeanFactory.getMecProxy(fieldClass);
			if(!mecProxy.isInjection()) {//如果本次注入对象中的Autowired字段没有被注入过,先递归此字段,再注入
				injectBean(proxyBeanFactory, mecProxy.getObject(), fieldClass);
			}
			field.setAccessible(true);
			try {
				//需要注意:此处注入的是成员的代理对象!
				field.set(object, mecProxy.getProxy());
				System.out.println("注入了:"+ mecProxy.getObject() + "到" + object + "的" + field + "中");
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

5、实践论证

循环依赖的实践验证:【(@Component)StudentAction类中有(@Autowired)studentDao字段,以此依赖关系,最后的StudentModel中有(@Autowired)studentAction字段】,以下贴出输出结果:

(输出结果中的类名字由于存在包名所以很长,在此删掉一部分)

注入了:.student.action.StudentAction@137e0d2到.student.model.StudentModel@59eb14中
注入了:.student.model.StudentModel@59eb14到.student.dao.TeacherDao@2e4553中
注入了:.student.dao.TeacherDao@2e4553到.student.dao.StudentDao@8c97a5中
注入了:.student.dao.StudentDao@8c97a5到.student.action.StudentAction@137e0d2中

studentAction中的studentdao:.student.dao.StudentDao:@8c97a5 studentDao
中的 teacherDao:
.student.dao.TeacherDao:@2e4553 teacherDao 中的
studentModel:.student.model.StudentModel:@59eb14 sudentModel 中的
studentAction:
.student.action.StudentAction:@137e0d2

6、源码链接

https://gitee.com/MEC_student_Conph/ResChain_IOC_AOP_version_2

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值