手撸一个IOC容器
手撸一个IOC容器,完成依赖的注入以及类的查找。
分两步
1、进行类的加载,将带有特殊注解的类注册到容器当中
2、进行依赖的注入,完成带有特定标签的属性的值的自动注入。
一、创建一堆注解,用于表示标识类需要被加载到容器中 @Component 、@Controller、@Service、@Repository
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Component { } @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Controller { } @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Repository { } @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Service { }
二、创建一个单例BeanContainer,作为IOC容器
/** * 下面是实现一个单例的容器 */ private BeanContainer(){} public static BeanContainer getInstance(){ return ContainerHolder.HOLDER.instance; } //利用枚举的特性,创建单例,可以防止反射和序列化造成多个实例的问题。 private enum ContainerHolder{ HOLDER; private BeanContainer instance; ContainerHolder(){ instance = new BeanContainer(); } }
/** * 存放所有的被标记的目标对象 * 想当与从一个包中查找出的带有特定标签(待注入的类)的类都放在这个MAP当中 * object相当于newInstance创建的实例 */ private final static ConcurrentHashMap<Class<?>,Object> beanMap = new ConcurrentHashMap<Class<?>, Object>(); /** * 将需要被处理的注解放到一个列表当中 */ private final static List<Class<? extends Annotation>> beanAnno = new ArrayList<Class<? extends Annotation>>(){{ add( Component.class); add( Controller.class); add( Repository.class); add( Service.class); }
核心代码loadBeans,把包内的类加载到容器当中
/**
* 加载所有的Bean
* @param packageName
*/
public void loadBeans(String packageName){
Set<Class<?>> classSet = new HashSet<Class<?>>();
classSet = ClassUtil.extractPackageClass(packageName);
if(classSet == null || classSet.isEmpty()){
System.out.println("包下没有任何类");
return;
}
//如果存在class
for (Class<?> clazz : classSet){
//查看某个类型是不是有列表中的注解
for (Class<? extends Annotation> annoClass:beanAnno){
if(clazz.isAnnotationPresent(annoClass)){
beanMap.put(clazz,getNewInstance(clazz,true));
}
}
}
}
/**
* 获取新实例
* @param clazz
* @param accessible
* @param <T>
* @return
*/
private <T> T getNewInstance(Class<?> clazz,boolean accessible ) {
try{
Constructor<?> constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(accessible);
return (T)constructor.newInstance();
}
catch (Exception ex){
ex.printStackTrace();
throw new RuntimeException();
}
}
三、创建一些容器的操作
/**
* 添加bean
* @param clazz
* @param obj
* @return
*/
public Object addBean(Class<?> clazz,Object obj){
return beanMap.put(clazz,obj);
}
/**
* 删除Bean
* @param clazz
* @return
*/
public Object removeBean(Class<?> clazz){
return beanMap.remove(clazz);
}
/**
* 获取bean
* @param clazz
* @return
*/
public Object getBean(Class<?> clazz){
return beanMap.get(clazz);
}
/**
* 获取所有的bean
* @return
*/
public Set<Object> getBeans(){
return new HashSet<Object>(beanMap.values());
}
/**
* 获取所有的Classes
* @return
*/
public Set<Class<?>> getClasses(){
return beanMap.keySet();
}
/**
* 获取指定注解的类
* @param annoClass
* @return
*/
public Set<Class<?>> getClassesByAnno(Class<? extends Annotation> annoClass){
Set<Class<?>> classes = getClasses();
if(classes ==null || classes.isEmpty()){
System.out.println("没找到指定注解的类型");
}
Set<Class<?>> classSet = new HashSet<Class<?>>();
for(Class<?> clazz : classes){
if(clazz.isAnnotationPresent(annoClass)){
classSet.add(clazz);
}
}
return classSet;
}
/**
* 获取指定接口的类
* @param interfaceOrClass
* @return
*/
public Set<Class<?>> getClassesByInterfaceOrClass(Class<?> interfaceOrClass){
Set<Class<?>> classes = getClasses();
if(classes ==null || classes.isEmpty()){
System.out.println("没找到指定接口的类型");
}
Set<Class<?>> classSet = new HashSet<Class<?>>();
for(Class<?> clazz : classes){
if(clazz.isAssignableFrom(interfaceOrClass)){
classSet.add(clazz);
}
}
return classSet;
}
创建一个工具类
public class ClassUtil {
public static final String FILE_PROTOCOL = "file";
/**
* 获取包下的所有的类的集合
* @param packageName
*/
public static Set<Class<?>> extractPackageClass(String packageName){
Set<Class<?>> classSet = null;
//获取类的加载器,主要为了解决实际路径的问题,包名没办法定位
ClassLoader classLoader = getClassLoader();
//通过类加载器加载资源
URL url = classLoader.getResource(packageName.replace('.', '/'));
if(url==null){
System.out.println("资源不存在");
return classSet;
}
//根据不同的资源类型,通过不同的方式获取资源
//如果是文件协议
if(url.getProtocol().equalsIgnoreCase(FILE_PROTOCOL)){
classSet = new HashSet<Class<?>>();
File packageDir = new File(url.getPath());
extractClassFile(classSet,packageDir,packageName);
}
return classSet;
}
/**
* 提取类文件(需要进行递归,判断是不是文件夹还是文件)
* @param classSet
* @param fileSoucre
* @param packageName
*/
private static void extractClassFile(final Set<Class<?>> classSet, File fileSoucre, final String packageName) {
//如果是文件的情况
if(!fileSoucre.isDirectory()){
return;
}
else {
//文件夹的情况,列出文件夹下的所有文件
File[] files = fileSoucre.listFiles(new FileFilter() {
public boolean accept(File file) {
if(file.isDirectory()){
return true;
}
else {
//获取文件的绝对路径
String absolutePath = file.getAbsolutePath();
if(absolutePath.endsWith(".class")){
//class文件直接加载
add2ClassSet(absolutePath);
return true;
}
}
return false;
}
/**
* 添加class到classSet当中
* @param absolutePath
*/
private void add2ClassSet(String absolutePath) {
System.out.println(absolutePath);
//从绝对路径中获取到包名+类名
absolutePath = absolutePath.replace(File.separator,".");
//根据传进来的packagename,去掉路径中的包名,只得到
String className = absolutePath.substring(absolutePath.indexOf(packageName));
className = className.substring(0,className.lastIndexOf('.'));
Class<?> aClass = loadClass(className);
classSet.add(aClass);
}
private Class<?> loadClass(String className) {
try{
return Class.forName(className);
}
catch (Exception ex){
System.out.println("load class error");
throw new RuntimeException();
}
}
});
if(files!=null){
//遍历每一个文件
for (File f : files){
extractClassFile(classSet,f,packageName);
}
}
}
}
/**
* 获取ClassLoader
* @return
*/
public static ClassLoader getClassLoader(){
//通过这个方式获取ClassLoader实例
return Thread.currentThread().getContextClassLoader();
}
}
四、实现依赖注入,创建一个注解@Autowire,限定为Field使用
@Retention(value = RUNTIME)
@Target(ElementType.FIELD)
public @interface Autowire {
/**
* 定义一个属性,用于存储默认的实现
* @return
*/
public String value() default "";
}
创建一个DependcyInjector类,可以实现类中的依赖注入。
public class DependcyInjector {
private BeanContainer beanContainer;
public DependcyInjector() {
//获取容器的单例
beanContainer = BeanContainer.getInstance();
}
/**
* 执行依赖注入
*/
public void doIoc(){
//1、遍历Bean容器当中所有的Class对象
Set<Class<?>> iocClasses = beanContainer.getClasses();
for (Class<?> clazz : iocClasses){
//2、遍历类中的所有的属性
Field[] fields = clazz.getDeclaredFields();
for(Field field : fields){
//3、判断属性是否带有AutoWire注解,然后通过注入该属性
if(field.isAnnotationPresent(Autowire.class)){
//4、获取这些被Autowire注解修饰的类
Class<?> autowireType = field.getType();
//5、获取这个类型的实例,要考虑多个实现的情况如何获取,获取哪一个
String autowireValue = field.getDeclaredAnnotation(Autowire.class).value();
Object fieldInstance = getFieldInstance(autowireType, autowireValue);
if(fieldInstance ==null){
System.out.println(MessageFormat.format("注入失败,属性{0}实现类不存在",autowireType.getSimpleName()));
}
//6、将属性的实现类实例,注入到属性当中
Object classBean = beanContainer.getBean(clazz);
//设置值到Field当中
SetField(field,fieldInstance,classBean,true);
}
}
}
}
/**
* 设置属性
*/
private void SetField(Field field,Object fieldInstance,Object target,boolean access) {
field.setAccessible(access);
try{
field.set(target,fieldInstance);
}catch (Exception ex){
ex.printStackTrace();
}
}
/**
* 获取属性的实例
* @param fieldClass
* @param autowireValue
* @return
*/
private Object getFieldInstance(Class<?> fieldClass,String autowireValue) {
//从容器中获取实例
Object filedClassInstance = beanContainer.getBean(fieldClass);
if(filedClassInstance !=null){
return filedClassInstance;
}
else {
//可能传入的不是类,而是接口,需要获取接口对应的实现类
Class<?> implementClass = getImplementClass(fieldClass, autowireValue);
if(implementClass!=null){
return beanContainer.getBean(implementClass);
}
return null;
}
}
/**
* 获取接口对应的实现类
* @param interfaceClass
* @return
*/
private Class<?> getImplementClass(Class<?> interfaceClass,String autowireValue) {
Set<Class<?>> implClasses = beanContainer.getClassesByInterfaceOrClass(interfaceClass);
//如果实现类不存在
if(implClasses == null || implClasses.isEmpty()){
System.out.println(MessageFormat.format("接口{0}的实现类型不存在",interfaceClass.getName().toString()));
return null;
}
//如果autowirevalue是默认值,并且实现类只有一个,此时返回对应的默认的实现类
if(autowireValue.equals("") && implClasses.size() ==1){
return implClasses.iterator().next();
}
//如果指定了实现类名称
if(!autowireValue.equals("")){
//存在多个
for (Class<?> clazz : implClasses){
if(clazz.getSimpleName().equals(autowireValue)){
return clazz;
}
}
System.out.println(MessageFormat.format("接口{0}存在的实现类中不存在名为{1}的实现",interfaceClass.getName().toString(),autowireValue));
}
return null;
}
}
五、使用测试
创建几个测试用的类

@Component
public class User {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
@Service
public class Student extends User{
public String getLevel() {
return level;
}
public void setLevel(String level) {
this.level = level;
}
private String level;
public String Hello(){
return "HelloWorld";
}
}
//学生服务
@Service
public class StudentService {
@Autowire
private Student stu ;
public Student getStudent(){
stu.setLevel("2");
stu.setAge(11);
stu.setName("123123");
return stu;
}
}
//Hello服务
@Service
public class HelloService {
@Autowire
private StudentService studentService;
public void sayHello(){
System.out.println(studentService.getStudent().getName() + " Hello");
}
}
Main方法运行
public static void main(String[]args){
//初始化容器
BeanContainer ioc = BeanContainer.getInstance();
ioc.loadBeans("org.simpleframework.entity");
ioc.loadBeans("org.simpleframework.service");
//实现依赖注入
DependcyInjector injector = new DependcyInjector();
injector.doIoc();
//执行代码
HelloService helloService =(HelloService)ioc.getBean(HelloService.class);
helloService.sayHello();
}
执行结果可以看到类都加载到BeanContainer,并且服务内方法也成功调用了:
E:\Projects\myioc\target\classes\org\simpleframework\entity\Student.class E:\Projects\myioc\target\classes\org\simpleframework\entity\User.class E:\Projects\myioc\target\classes\org\simpleframework\service\HelloService.class E:\Projects\myioc\target\classes\org\simpleframework\service\StudentService.class 123123 Hello
总结,可以简单归纳如图


浙公网安备 33010602011771号