Spring的内部实现原理模拟———丑九怪

Spring的内部实现原理模拟以及其中的AOP———丑九怪

Spring中Ioc的模拟

这篇随笔主要讨论Spring内部的模拟与实现,以下是大致内容:

  • 首先在Spring中有一个BeanFactory来装载生成存储着所有类对象的Map<String, BeanDefinition>,其中键值为类全称,值为类BeanDefinition的对象,BeanDefinition中有类的对象和类型。为了方便用户查找,这里再次给出一个Map<String, String>,其中键值为用户为某个类所设置的ID,值为类全称。那么该怎么往里面注入呢?我的实现方式是注解。 实现所需的技术有包扫描技术,对目标包进行扫描,找出我们写了注解的类,对其中的注解进行处理,向BeanFactory的Map中添加。
  1. Component注解:
    这个注解的使用条件:当我们想要将某个类装入BeanFactory的Map中的时候,就给这个类加上Component注解,我们在处理它的时候,进行一遍包扫描,就可以将几乎所有有Component注解的类装入Map中。但是注意,这里装入Map中的类。只是一个“空壳子”,如果其中的成员是类类型成员,那么这些成员都还没有初始化,所以这里,我们引入AutoWired注解。

这是对BeanMap和BeanIdMap初始化的代码

public void initBeanFactory(String packageName) {
		new MXLPackageScanner() {
			@Override
			public void dealClass(Class<?> klass) {
				try {
					if (klass.isPrimitive() || klass.isAnnotation()
							|| klass.isEnum() || klass.isInterface()
							|| !klass.isAnnotationPresent(Component.class)) {
						return;
					}
					String klassName = klass.getName();
					if (BeanMap.containsKey(klassName)) {
						return;
					}
					BeanDefinition bd = new BeanDefinition(klass);
					BeanMap.put(klassName, bd);
					String beanId = klassName;
					Component component = klass.getAnnotation(Component.class);
					if (component != null) {
						beanId = component.ID();
					}
					BeanIdMap.put(beanId, klassName);
					
					collectBeanMethods(klass, bd.getObject()); // 这个和以后的Bean注解有关系,现在请自行忽略这行
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}.packageScanner(packageName);
		try {
			bmp.scanPool(); // 这个和以后的Bean注解有关系,现在请自行忽略这行
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
  1. AutoWired注解:
    这个注解是专门为类成员准备的,给成员添加AutoWired注解,表示这个成员需要从Map中找到对应类型的对象为其实现注入,因此,可能会出现循环注入,也就是说,在一个类A中有类B的成员,而B中又有A的成员。为了处理这种情况,我们在注入的时候,先假定所找到的类的对象都已经注入完毕,这样就可以解决循环注入的问题。然鹅,现在这里又出现问题了,问:如果我的对象要是一个接口该怎么办呢?接口可是不能添加Component注解,而且还可以有多个实现类,这怎么实现?那么下来就是Qualifier注解。
  2. Qualifier注解:
    这个注解也是给成员添加,但是一定要是接口类型的成员,并且接口至少要有一个实现类有Component注解(这样我们才可以实现注入嘛)这个注解还有一个属性,表明具体要注入这个接口的哪一个实现类的对象。这还剩下一个大问题:如果我的成员是一个已经打成jar包的工具类怎么办呢?由此,引出Bean注解。

这是以上注解的代码实现

// 设置成员的值
	private void setBeanValue(BeanDefinition beanDefinition) throws Exception {
		// 如果已经注入完毕,就return
		if (beanDefinition.isDone()) { // 这个成员表示是否已经注入完毕
			return;
		}
		beanDefinition.done = true; // 假定注入完毕,防止无限递归
		Class<?> klass = beanDefinition.getKlass();
		Field[] fields = klass.getDeclaredFields(); // 得到这个类的每个成员
		for (Field field : fields) {
			if (!field.isAnnotationPresent(Autowired.class)) { // 检查是否有autowired注解
				continue;
			}
			// 取得注解,获取注解的值
			Autowired autowired = field.getAnnotation(Autowired.class);
			boolean singleton = autowired.singleton(); // 这个表示他是否是单例的
			String value = autowired.value(); // 这里的值表示,如果成员类型是八大基本类型,那么就从注解中获取具体值
			// 设置值
			setValue(beanDefinition, field, value.length() > 0 ? value : null, singleton);
		}
	}
private void setValue(BeanDefinition bd, Field field, String value, boolean singleton) throws Exception {
		// 如果注解的值不是null,则用注解的值注入并返回
		if (value != null) {
			setFieldWithValue(bd, field, value); // 用注解的八大基本类型的值对成员进行赋值操作
			return;
		}
		Class<?> type = field.getType();
		BeanDefinition bean = getBeanDefinition(type); // 从bean中注入,先取得Bean
		// 如果不存在或者这个成员是接口类型,但是接口的实现类有很多,并没有注明是哪个实现类,那么抛出异常:未设置相关的bean
		if (bean == null) { // 这里为null只能说明我们查找的类型不是类类型,而是一个接口类型
			BeanDefinition[] beans = searchBeanBySuperType(type);// 我们的得到这的接口的所有实现类
			if (beans.length == 1) { // 表明这个接口只有一个实现类,所以直接赋值
				bean = beans[0];
			} else if (beans.length <= 1 && !field.isAnnotationPresent(Qualifier.class)) {
				throw new Exception("类" + bd.getKlass() + "中的成员" + field.getName() + "注入失败");
			} else { // 当有很多实现类时,通过qualifier注解找到所要注入的类
				Qualifier qualifier = field.getAnnotation(Qualifier.class);
				String beanId = qualifier.value();
				bean = getBeanDefinition(beanId);
				if (bean == null) {
					throw new Exception("类" + bd.getKlass() + "中的成员" + field.getName() + "注入失败");
				}
			}
		}
		if (!singleton) { // 非单例情况,直接生成新的BeanDefinition完成后面的注入
			bean = new BeanDefinition(type);
		}
		// 判断找到的bean中是否存在未注入成员,如果有,就递归调用上面的方法
		if (!bean.isDone()) {
			setBeanValue(bean);
		}
		// 没有的话直接用bean的成员注入
		setFieldValue(bd, field, bean.getObject());
 	}
private void setFieldValue(BeanDefinition bd, Field field, Object val) throws Exception {
		// 因为优先使用set方法进行注入,反射机制执行set方法,所以要有类的类型,成员名
		Class<?> klass = bd.getKlass();
		// 得到成员的类型,这是set方法的参数的类型
		Class<?> type = field.getType();
		String fieldName = field.getName();
		// 得到方法名称
		String methodName = "set" + fieldName.substring(0, 1).toUpperCase()
				+ fieldName.substring(1);
		try {
			// 反射机制找到set方法,执行方法
			Method method = klass.getDeclaredMethod(methodName, new Class<?>[] { type });
			method.invoke(bd.getObject(), val);
		} catch (Exception e) {
			// 如果找不到,就默认执行直接队成员赋值
			field.setAccessible(true);
			field.set(bd.getObject(), val);
		}
	}
  1. Bean注解(这里的处理,其实是饿汉模式):
    当我们的成员类型是一个已经打成jar包的类的类型,这时我们不能简单的将他注入(jar包中无法修改内容啊),灵机一动我们可以用方法实现注入啊,返回一个对象就好了吧,说干就干。首先创建专门用来配置的类,这个类中只完成Bean注解的注入。Map中的类,其中的jar类对象的get方法都在这个类里实现,get方法返回对应的类的对象。在我们取得这些之后,首先将其放入BeanFactory的Map中,等待以后的使用。这就解决了jar包对象的问题。
/**
	 * 生成一个存储get方法的类 BeanMethodPool,其中有Map<String, BeanMethodDefinition>, BeanMethodDefinition中主要是
	 * jar包类的对象,还有get方法的返回值和参数等,
	 */
	private void collectBeanMethods(Class<?> klass, Object object) throws Exception {
		Method[] methods = klass.getDeclaredMethods();
		for (Method method : methods) {
			if (!method.isAnnotationPresent(Bean.class)) {
				continue;
			}
			Bean bean = method.getAnnotation(Bean.class);
			Class<?> returnType = method.getReturnType();
			if (returnType.equals(void.class)) {
				throw new Exception("[" + method.getName() + "]" + "不能对void加注解");
			}
			String name = bean.Id(); // 这个ID就是为了方便用户设计的
			BeanMethodDefinition bmd = new BeanMethodDefinition(name, returnType, object, method);
			String className = returnType.getName();
			if (name.length() > 0) { // 将取得的名字装入BeanMap中
				BeanIdMap.put(name, className);
			} else {
				BeanIdMap.put(className, className);
			}
			
			if (bmd.getParameterCount() <= 0) { // 第一遍先将所有的无参方法执行完,也就是说先放入BeanMap中
				Object beanObj = method.invoke(object);
				BeanDefinition bd = new BeanDefinition(beanObj);
				BeanMap.put(className, bd);
				continue;
			} 
			bmp.addBeanMethod(className, bmd); // 将带参的方法先放入BeanMethodPool中,在初始化的时候进行扫描并注入成员
		}
	}

以上为Spring中Ioc的大致实现,下面是AOP的大致实现原理以及代码

Spring中AOP的模拟

  • 代理机制是这个AOP的大前提,有了代理机制才可以进行拦截器等操作
  • 两种代理机制,cglibProxy和JDKProxy,前者原理是通过继承所给对象的类型,从而实现添加拦截,后者是通过实现接口的方式实现添加拦截

1.首先实现CGLIBProxy和JDKProxy获取代理对象

// 这里和cglib的内部有关,取得的代理,只要通过相关类型接收,调用被添加了代理的方法,就可以自动执行代理, 返回值是代理对象。
	@SuppressWarnings("unchecked")
	private <T> T getProxy(Object object, Class<?> klass) {
		try {
			Constructor<?> constructor  = klass.getDeclaredConstructor(new Class<?>[] {});
			int modifiers = constructor.getModifiers();
			if ((Modifier.PRIVATE & modifiers) != 0) {
				return (T) object; // 这里返回是因为类的构造方法为private,所以直接返回原来的对象就好
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(klass);
		enhancer.setCallback(new MethodInterceptor() {
			@Override
			public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
				return mxlProxyInvoker.invoke(object, method, args);
			}
		});
		return (T) enhancer.create();
	}
// 这个是JDKProxy中取得代理对象的方法, 返回值是代理对象。
@SuppressWarnings("unchecked")
	private <T> T getProxy(Object object, ClassLoader classLoader, Class<?>[] interfaces) {
		return (T) Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {
			@Override
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
				return mxlProxyInvoker.invoke(object, method, args);
			}
		});
	}
  1. 下面是往这些代理中添加拦截器,我们创建一个类,用来专门生成拦截器链和对拦截器以及方法的执行,上代码:
public class MxlProxyInvoker {
	Object object;
	Object proxy;
	private IntercepterChain intercepterChain;  // 已有的拦截器链
	
	MxlProxyInvoker(Object object, Object proxy) { // 相当于只生成了链表的头部 
		this.object = object;
		this.proxy = proxy;
		intercepterChain = new IntercepterChain(null);
	}
	
	// 增加拦截器,这里增加的是intercepterAdapter的实现的对象,这个适配器所实
	// 现的接口就是前置拦截,后置拦截,异常拦截等等方法
	void addIntercepter(IntercepterAdapter intercepter) {
		IntercepterChain newChain = new IntercepterChain(intercepter);
		if (intercepterChain.getNext() == null) {
			intercepterChain.next = newChain;
		} else {
			newChain.next = intercepterChain.next;
			intercepterChain.next = newChain;
		}
	}
	
	// 这个方法相当于是提供给外面使用的,相当于一个入口
	Object invoke(IntercepterChain intercepterChain, Method method, Object[] args) throws Throwable {
		Object result = null;
		result = intercepterChain.doChain(intercepterChain.next, object, method, args);
		return result;
	}
}

这是真正进行递归调用拦截器链的类

public class IntercepterChain {
	IntercepterAdapter intercepter; // 这个是拦截器的具体部分
	IntercepterChain next; // 这个指向下一个拦截器的具体部分
	boolean beforeOk;
	
	public IntercepterChain(IntercepterAdapter intercepter) {
		this.intercepter = intercepter;
		this.next = null;
	}
	
	IntercepterChain getNext() {
		return next;
	}
	
	Object doChain(IntercepterChain chain, Object object, Method method, Object[] args) throws Throwable {
		Object result = null;
		if (chain != null) { // 说明还有前置拦截器,所以继续进行拦截
			IntercepterAdapter intercepter = chain.intercepter; // 取得他的拦截器
			if (intercepter.isMethod(method)) { // 检测传进来的方法是否是要拦截的方法
				if ((beforeOk = intercepter.before(args))) { // 前置拦截的返回值决定是否要继续拦截
					try {
						result = doChain(chain.getNext(), object, method, args); // 前置拦截成功,则继续递归
						if (beforeOk) {
							result = intercepter.after(result);
						}
					} catch (Throwable e) {
						intercepter.dealException(e);
					}
				} else {
					return result;
				}
			} else { // 如果不是我们要拦截的方法,那么就换一个拦截器再检查,所以再次递归
				doChain(chain.getNext(), object, method, args);
			}
		} else {
			result = method.invoke(object, args); // 所有前置拦截完成,则执行真正的方法调用
		}
		return result;
	}
}

  1. 在前面的CGLIBProxy和JDKProxy的构造中添加参数MxlProxyInvoker,在对CGLIBProxy或者JDKProxy new的时候,添加拦截器调。然后用CGLIBProxy和JDKProxy中的getProxy方法得到代理对象,直接用代理对象执行方法就可以实现拦截器的调用

这就是我所理解的Spring的AOP和Ioc,也欢迎各位路过的看官指教!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值