Spring的内部实现原理模拟以及其中的AOP———丑九怪
Spring中Ioc的模拟
这篇随笔主要讨论Spring内部的模拟与实现,以下是大致内容:
- 首先在Spring中有一个BeanFactory来装载生成存储着所有类对象的Map<String, BeanDefinition>,其中键值为类全称,值为类BeanDefinition的对象,BeanDefinition中有类的对象和类型。为了方便用户查找,这里再次给出一个Map<String, String>,其中键值为用户为某个类所设置的ID,值为类全称。那么该怎么往里面注入呢?我的实现方式是注解。 实现所需的技术有包扫描技术,对目标包进行扫描,找出我们写了注解的类,对其中的注解进行处理,向BeanFactory的Map中添加。
- 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();
}
}
- AutoWired注解:
这个注解是专门为类成员准备的,给成员添加AutoWired注解,表示这个成员需要从Map中找到对应类型的对象为其实现注入,因此,可能会出现循环注入,也就是说,在一个类A中有类B的成员,而B中又有A的成员。为了处理这种情况,我们在注入的时候,先假定所找到的类的对象都已经注入完毕,这样就可以解决循环注入的问题。然鹅,现在这里又出现问题了,问:如果我的对象要是一个接口该怎么办呢?接口可是不能添加Component注解,而且还可以有多个实现类,这怎么实现?那么下来就是Qualifier注解。 - 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);
}
}
- 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);
}
});
}
- 下面是往这些代理中添加拦截器,我们创建一个类,用来专门生成拦截器链和对拦截器以及方法的执行,上代码:
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;
}
}
- 在前面的CGLIBProxy和JDKProxy的构造中添加参数MxlProxyInvoker,在对CGLIBProxy或者JDKProxy new的时候,添加拦截器调。然后用CGLIBProxy和JDKProxy中的getProxy方法得到代理对象,直接用代理对象执行方法就可以实现拦截器的调用
这就是我所理解的Spring的AOP和Ioc,也欢迎各位路过的看官指教!