手写spring注解依赖注入基本实现


  4.0.0
  com.learn
  demo
	0.0.1-SNAPSHOT
	
		
		
			org.springframework
			spring-core
			3.0.6.RELEASE
		
		
			org.springframework
			spring-context
			3.0.6.RELEASE
		
		
			org.springframework
			spring-aop
			3.0.6.RELEASE
		
		
			org.springframework
			spring-orm
			3.0.6.RELEASE
		
		
			org.aspectj
			aspectjrt
			1.6.1
		
		
			aspectj
			aspectjweaver
			1.5.3
		
		
			cglib
			cglib
			2.1_2
		

		
		
			com.mchange
			c3p0
			0.9.5.2
		
		
		
			mysql
			mysql-connector-java
			5.1.37
		

		
		
			dom4j
			dom4j
			1.6.1
		
		
		
			commons-lang
			commons-lang
			2.6
		

	

package com.learn.service;

public interface OrderService {

	public void addOrder();

}
package com.learn.service.impl;

import com.learn.annotation.ExtService;
import com.learn.service.OrderService;

/**
 * 在这里写一个实现
 * 实现OrderService接口
 * 在这里我同样加上@ExtService注解
 * 我来问一下你们
 * 
 * 
 * 
 * @author Leon.Sun
 *
 */
@ExtService
public class OrderServiceImpl implements OrderService {

	/**
	 * 重写add方法
	 * 
	 */
	@Override
	public void addOrder() {
		/**
		 * 在这里打印一段话叫addOrder
		 * 
		 */
		System.out.println("addOrder");
	}

}
package com.learn.service;

//user 服务层
public interface UserService {

	public void add();

	public void del();
}
package com.learn.service.impl;

import org.springframework.transaction.annotation.Transactional;

import com.learn.annotation.ExtResource;
import com.learn.annotation.ExtService;
import com.learn.service.UserService;

/**
 * 我在这里也加上@ExtService注解
 * 
 * 
 * @author Leon.Sun
 *
 */
@ExtService
public class UserServiceImpl implements UserService {
	
	/**
	 * 你们把JDK换一下
	 * 因为JDK1.5可能没有这个注解
	 * 这个注解好像是在1.6出来的
	 * 我们用原生的JDK注解能不能找到bean呢
	 * 能不能找的到
	 * 你们说能不能找的到
	 * 能不能找的到其实蛮容易
	 * 能找到
	 * 我来运行一下
	 * 
	 * 你自己定义的容器他怎么能够找的到呢
	 * 我们讲一下依赖注入的原理
	 * 谁能告诉我依赖注入的原理是什么
	 * 这是我们自定义容器
	 * 肯定找不到的
	 * @Resource这个注解底层是怎么实现的
	 * 它是默认通过你的属性名称
	 * 去你bean容器里面去找
	 * 是不是这样的
	 * 其实这个注入非常容易
	 * 我来问你依赖注入是什么意思
	 * 什么是依赖注入
	 * 就是引用他的对象
	 * 这个注解是怎么实现出来的呢
	 * 我大体说一下他的思路
	 * 
	 * 依赖注入注解的原理
	 * 依赖注入它是什么样的一个原理
	 * 注解原理
	 * 使用反射
	 * 获取当前类的所有属性
	 * 判断当前类属性是否存在注解
	 * 默认使用属性名称查找bean容器对象
	 * 其实我们讲的思想完全一样
	 * 
	 * 
	 */
	@ExtResource
	private OrderServiceImpl orderServiceImpl;
	
	@Transactional
	public void add() {
		/**
		 * 这里报空了
		 * 这不是一个容器
		 * 
		 * 
		 */
		orderServiceImpl.addOrder();
		System.out.println("使用JAVA反射机制初始化对象....");
	}
	// 方法执行完毕之后,才会提交事务

	public void del() {

	}


}
package com.learn.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

//自定义注解 从Spring容器获取bean
/**
 * 定义一个ExtResource
 * 
 * @author Leon.Sun
 *
 */
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface ExtResource {

}
package com.learn.utils;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * 这段代码千万不要去写
 * 写了没意义
 * 这个我大体说一下原理
 * 原理是什么样的呢
 * 
 * 
 * @author Leon.Sun
 *
 */
public class ClassUtil {

	/**
	 * 取得某个接口下所有实现这个接口的类
	 */
	public static List getAllClassByInterface(Class c) {
		List returnClassList = null;

		if (c.isInterface()) {
			// 获取当前的包名
			String packageName = c.getPackage().getName();
			// 获取当前包下以及子包下所以的类
			List> allClass = getClasses(packageName);
			if (allClass != null) {
				returnClassList = new ArrayList();
				for (Class classes : allClass) {
					// 判断是否是同一个接口
					if (c.isAssignableFrom(classes)) {
						// 本身不加入进去
						if (!c.equals(classes)) {
							returnClassList.add(classes);
						}
					}
				}
			}
		}

		return returnClassList;
	}

	/*
	 * 取得某一类所在包的所有类名 不含迭代
	 */
	public static String[] getPackageAllClassName(String classLocation, String packageName) {
		// 将packageName分解
		String[] packagePathSplit = packageName.split("[.]");
		String realClassLocation = classLocation;
		int packageLength = packagePathSplit.length;
		for (int i = 0; i < packageLength; i++) {
			realClassLocation = realClassLocation + File.separator + packagePathSplit[i];
		}
		File packeageDir = new File(realClassLocation);
		if (packeageDir.isDirectory()) {
			String[] allClassName = packeageDir.list();
			return allClassName;
		}
		return null;
	}

	/**
	 * 从包package中获取所有的Class
	 * 
	 * @param pack
	 * @return
	 */
	public static List> getClasses(String packageName) {

		// 第一个class类的集合
		List> classes = new ArrayList>();
		// 是否循环迭代
		boolean recursive = true;
		// 获取包的名字 并进行替换
		String packageDirName = packageName.replace('.', '/');
		// 定义一个枚举的集合 并进行循环来处理这个目录下的things
		Enumeration dirs;
		try {
			dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
			// 循环迭代下去
			while (dirs.hasMoreElements()) {
				// 获取下一个元素
				URL url = dirs.nextElement();
				// 得到协议的名称
				String protocol = url.getProtocol();
				// 如果是以文件的形式保存在服务器上
				if ("file".equals(protocol)) {
					// 获取包的物理路径
					String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
					// 以文件的方式扫描整个包下的文件 并添加到集合中
					findAndAddClassesInPackageByFile(packageName, filePath, recursive, classes);
				} else if ("jar".equals(protocol)) {
					// 如果是jar包文件
					// 定义一个JarFile
					JarFile jar;
					try {
						// 获取jar
						jar = ((JarURLConnection) url.openConnection()).getJarFile();
						// 从此jar包 得到一个枚举类
						Enumeration entries = jar.entries();
						// 同样的进行循环迭代
						while (entries.hasMoreElements()) {
							// 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
							JarEntry entry = entries.nextElement();
							String name = entry.getName();
							// 如果是以/开头的
							if (name.charAt(0) == '/') {
								// 获取后面的字符串
								name = name.substring(1);
							}
							// 如果前半部分和定义的包名相同
							if (name.startsWith(packageDirName)) {
								int idx = name.lastIndexOf('/');
								// 如果以"/"结尾 是一个包
								if (idx != -1) {
									// 获取包名 把"/"替换成"."
									packageName = name.substring(0, idx).replace('/', '.');
								}
								// 如果可以迭代下去 并且是一个包
								if ((idx != -1) || recursive) {
									// 如果是一个.class文件 而且不是目录
									if (name.endsWith(".class") && !entry.isDirectory()) {
										// 去掉后面的".class" 获取真正的类名
										String className = name.substring(packageName.length() + 1, name.length() - 6);
										try {
											// 添加到classes
											classes.add(Class.forName(packageName + '.' + className));
										} catch (ClassNotFoundException e) {
											e.printStackTrace();
										}
									}
								}
							}
						}
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}

		return classes;
	}

	/**
	 * 以文件的形式来获取包下的所有Class
	 * 
	 * @param packageName
	 * @param packagePath
	 * @param recursive
	 * @param classes
	 */
	public static void findAndAddClassesInPackageByFile(String packageName, String packagePath, final boolean recursive,
			List> classes) {
		// 获取此包的目录 建立一个File
		File dir = new File(packagePath);
		// 如果不存在或者 也不是目录就直接返回
		if (!dir.exists() || !dir.isDirectory()) {
			return;
		}
		// 如果存在 就获取包下的所有文件 包括目录
		File[] dirfiles = dir.listFiles(new FileFilter() {
			// 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
			public boolean accept(File file) {
				return (recursive && file.isDirectory()) || (file.getName().endsWith(".class"));
			}
		});
		// 循环所有文件
		for (File file : dirfiles) {
			// 如果是目录 则继续扫描
			if (file.isDirectory()) {
				findAndAddClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive,
						classes);
			} else {
				// 如果是java类文件 去掉后面的.class 只留下类名
				String className = file.getName().substring(0, file.getName().length() - 6);
				try {
					// 添加到集合中去
					classes.add(Class.forName(packageName + '.' + className));
				} catch (ClassNotFoundException e) {
					e.printStackTrace();
				}
			}
		}
	}
}
package com.learn.spring;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.lang.StringUtils;

import com.learn.annotation.ExtResource;
import com.learn.annotation.ExtService;
import com.learn.utils.ClassUtil;

public class ExtAnnotationApplicationContext  {
    private String packageName;
    /**保存有Service注解的类*/
    private ConcurrentHashMap beans = null;

    public ExtAnnotationApplicationContext (String packageName) throws InstantiationException, IllegalAccessException {
        this.packageName = packageName;
        initBeans();
        //在所有Bean容器里所有bean自动注入所有的Bean
        for (Map.Entry entry : beans.entrySet()) {
            System.out.println("beanId:"+entry.getKey());
            Object bean = entry.getValue();
            attrAssign(bean);
        }
    }

    /**初始化Bean容器*/
    private void initBeans() throws IllegalAccessException, InstantiationException {
        beans = new ConcurrentHashMap();
        //使用扫包工具获得包下所有的类
        List> classes = ClassUtil.getClasses(packageName);
        //判断所有的类上面是否有注解,有的话就会加入到Bean容器里面去
        findClassExistAnnotation(classes);
        if (beans == null || beans.isEmpty()) {
            throw new RuntimeException("没有类加上了注解");
        }
    }

    /**扫包,把有注解的类加入到bean容器里*/
    private void findClassExistAnnotation(List> classes) throws InstantiationException, IllegalAccessException {
        for (Class classInfo : classes) {
            //判断是否有注解
            Annotation annotation = classInfo.getAnnotation(ExtService.class);
            if (annotation != null) {
                //到这里表示有这个注解
                String className = classInfo.getName();
                //默认Id是首字母小写
                beans.put(toLowerCaseFirestOne(classInfo.getSimpleName()), newInstance(classInfo));
            }
        }
    }

    /**类名的首字母小写*/
    private String toLowerCaseFirestOne(String className) {
        return new StringBuilder().append(Character.toLowerCase(className.charAt(0))).append(className.substring(1)).toString();
    }

    /**获取Bean的方法*/
    public Object getBean(String beanId) throws IllegalAccessException, InstantiationException {
        if (StringUtils.isEmpty(beanId)) {
            throw new RuntimeException("BeanID为空");
        }
        return beans.get(beanId);
    }

    /**利用反射机制创建Bean*/
    private Object newInstance(Class classInfo) throws IllegalAccessException, InstantiationException {
        if (classInfo == null) {
            throw new RuntimeException("没有这个ID的bean");
        }
        return classInfo.newInstance();
    }

    /**自动注入注入这个对象的属性*/
    private void attrAssign(Object object) throws IllegalAccessException {
        //获取这个类所有的属性
        Field[] fields = object.getClass().getDeclaredFields();
        //判断当前属性是否有注解
        for (Field field : fields) {
            ExtResource extResource = field.getAnnotation(ExtResource.class);
            if (extResource != null) {
                //允许访问私有属性
                field.setAccessible(true);
                //到这里说明这个属性里有这个注解,在从容器里获取对象然后给这个属性赋值
                String fieldName = field.getName();
                Object target = beans.get(fieldName);
                if (target == null) {
                    throw new RuntimeException("注入\"" + fieldName + "\"属性失败,bean容器里没有这个对象");
                }
                //第一个参数是这个属性所在的对象
                field.set(object,target);
            }
        }

    }
}
package com.learn;

import com.learn.service.UserService;
import com.learn.spring.ExtAnnotationApplicationContext;

/**
 * 核心是三步
 * 但是代码是比较麻烦的
 * 我可能会用到一些工具类
 * 那么我们这边同样道理
 * 我们首先要定义注解
 * 
 * 
 * @author Leon.Sun
 *
 */
public class Test001 {

	public static void main(String[] args) throws Exception {
		ExtAnnotationApplicationContext  app = new ExtAnnotationApplicationContext ("com.learn.service.impl");
		
		UserService userService = (UserService) app.getBean("userServiceImpl");
		
		System.out.println(userService);
		
		userService.add();
		
	}
}

 

你可能感兴趣的:(手写spring注解依赖注入基本实现)