手写spring核心ioc

IOC
现在java开发基本就是spring的天下,某不才自己实现一个小小的ioc功能,目的并不是复制spring,而是为了更好的理解和使用spring!,本文模仿springboot只做了注解配置的。

项目结构
项目结构
pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.qdz</groupId>
    <artifactId>open_spring</artifactId>
    <version>1.0-SNAPSHOT</version>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>11</source>
                    <target>11</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <properties>
        <commons.fileupload.version>1.3.3</commons.fileupload.version>
    </properties>
    <dependencies>
        <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
        <!-- servlet的依赖,ioc可有可无,mvc必须要 -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.0</version>
            <scope>provided</scope>
        </dependency>
        <!-- 常用工具类 -->
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>${commons.fileupload.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.8.1</version>
        </dependency>
        <!-- 日志文件,其实用的是log4j -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.6</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.6</version>
        </dependency>
    </dependencies>

</project>

常用注解,三个类代码放一起显示

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface QdzAutowired {
    String value() default "";
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface QdzService {
    String value() default "";
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface QdzController {
    String value() default "";
}

QdzSpringApplicationContext,类似于spring的

package com.qdz.config;

import com.qdz.frameWork.mapping.AnnotationConfiguration;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

public class QdzSpringApplicationContext {
	//线程安全的map和list
    private static Map<String,Object> concurrentHashMap = new ConcurrentHashMap<>();
    private static List<Class<?>> basePackageMappingToClass  = new CopyOnWriteArrayList<>();

    /**
     * 根据对象实例别名获取实例
     * @param name
     */
    public static Object getBean(String name){
        return concurrentHashMap.get(name);
    }
    public static void run(Class<?> clazz,String[] args) {
        //1.获得QdzComponentScan,得到扫描的包名集合
        List<String> packageNames = AnnotationConfiguration.getPackageNames(clazz);
        //2.包名集合获得Class对象集合
        basePackageMappingToClass = AnnotationConfiguration.getClasses(packageNames);
        //3.对象实例化
        concurrentHashMap = AnnotationConfiguration.getObjects(basePackageMappingToClass);
        //4.自动注入
        AnnotationConfiguration.AutoDi(basePackageMappingToClass,concurrentHashMap);
    }

}

AnnotationConfiguration
这个才是真正干活的类

package com.qdz.frameWork.mapping;


import com.qdz.frameWork.annotation.QdzAutowired;
import com.qdz.frameWork.annotation.QdzComponentScan;
import com.qdz.frameWork.annotation.QdzController;
import com.qdz.frameWork.annotation.QdzService;
import com.qdz.frameWork.exception.AnnotationNotExistException;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;

public class AnnotationConfiguration {
    private static final Logger logger = LoggerFactory.getLogger(AnnotationConfiguration.class);
    private static final List<Class<? extends Annotation>> lists = new ArrayList<>();
    static {
        lists.add(QdzController.class);
        lists.add(QdzService.class);
    }

    /**
     * 获得扫描的包名集合
     * @param clazz 启动类的Class对象,clazz就是启动类上穿过来的类,类似于springboot启动类
     * @return 所需扫描的包名集合
     */
    public static List<String> getPackageNames(Class<?> clazz) {
        List<String> propertyList = new CopyOnWriteArrayList<>();
        //是否存在QdzComponentScan注解
        if(!clazz.isAnnotationPresent(QdzComponentScan.class))throw new AnnotationNotExistException(clazz.getName() +"类的QdzComponentScan注解不存在");
        QdzComponentScan qdzComponentScan = clazz.getAnnotation(QdzComponentScan.class);
        String[] annoScans = qdzComponentScan.value();
        if(annoScans==null||annoScans.length<=0)throw new AnnotationNotExistException(clazz.getName() +"类的QdzComponentScan注解不存在");
        for (String s:annoScans) {propertyList.add(s);}
        logger.info("扫描解析完毕");
        return propertyList;
    }

    /**
     * 根据扫描的包名集合获得Class对象集合
     * @param packageNames 包名集合
     * @return Class对象集合
     */
    public static List<Class<?>> getClasses(List<String> packageNames){
        logger.info("正在获取Class对象");
        List<Class<?>> classList = new CopyOnWriteArrayList<>();
        packageNames.stream().forEach(o->{
        	//将点换成杠,这里用了lambada只是为了熟悉,强行用
            String path  = o.replace(".","/");
            URL url = AnnotationConfiguration.class.getClassLoader().getResource(path);
            //QdzComponentScan如果是包名则会拿到url,若果是包名点类名则拿不到
            if(url!=null){
                File file =new File(url.getPath());
                setClassList(classList,file,o);
            }else{
                Class<?> clazz = null;
                try {
                    clazz = Class.forName(o);
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
                classList.add(clazz);
            }
        });
        if(classList==null||classList.size()<=0)throw new AnnotationNotExistException("扫描的包不存在");
        logger.info("扫描完毕");
        return classList.stream().distinct().collect(Collectors.toList());
    }

    /**
     * 将所有需要扫描的类的Class对象放进list
     * @param classList
     * @param file
     * @param packageName
     */
    private static void setClassList(List<Class<?>> classList, File file,String packageName) {
        if(file.isDirectory()){
            for (File f:file.listFiles()) {
                setClassList(classList,f,packageName+"."+f.getName());
            }
        }else{
            Class<?> clazz = null;
            try {//全路径
                if(packageName.endsWith(".class")) {
                    clazz = Class.forName(packageName.substring(0, packageName.lastIndexOf(".")));
                    classList.add(clazz);
                }else {
                    String path = file.getName();
                    String pathName = packageName + "." + path.substring(0, path.lastIndexOf("."));
                    clazz = Class.forName(pathName);
                    classList.add(clazz);
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * Class对象实例化
     * @param classList
     * @return map
     */
    public static Map<String,Object> getObjects(List<Class<?>> classList){
        Map<String,Object> concurrentHashMap = new ConcurrentHashMap<>();
        classList.stream().forEach(o->{
            setConcurrentHashMap(concurrentHashMap,o);
        });
        return concurrentHashMap;
    }

    /**
     *
     * @param concurrentHashMap 线程安全的map
     * @param clazz Class对象
     */
    private static void setConcurrentHashMap(Map<String, Object> concurrentHashMap, Class<?> clazz) {
        lists.stream().forEach(o->{
            Object obj = null;
            String name = "";
            //判断有没有注解
            if(clazz.isAnnotationPresent(o)){
                Object annoObj = clazz.getAnnotation(o);
                try {
                    Method method = o.getMethod("value");
                    obj = clazz.getDeclaredConstructor().newInstance();
                    name = (String)method.invoke(annoObj);
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                }catch (Exception e) {
                    e.printStackTrace();
                }
                //判断注解上面有没有value,有的话作为map的key
                if(StringUtils.isBlank(name)){
                    String key = clazz.getName().substring(clazz.getName().lastIndexOf(".")+1);
                    name = (new StringBuilder()).append(Character.toLowerCase(key.charAt(0))).append(key.substring(1)).toString();
                    logger.info("key==  "+name);
                }
                if(obj!=null)concurrentHashMap.put(name,obj);
                logger.info(clazz.getName()+"装配完毕");
            }
        });

    }

    /**
     * 自动注入,解析Autowired
     * @param basePackageMappingToClass
     * @param concurrentHashMap
     */
    public static void AutoDi(List<Class<?>> basePackageMappingToClass, Map<String, Object> concurrentHashMap) {
        if(concurrentHashMap.isEmpty())return;
        for (Map.Entry<String,Object> entry:concurrentHashMap.entrySet()) {
            Field[] fields = entry.getValue().getClass().getDeclaredFields();
            for (Field f:fields ) {
          		  //判断有没有QdzAutowired注解
                if(!f.isAnnotationPresent(QdzAutowired.class))continue;
                QdzAutowired qdzAutowired = f.getAnnotation(QdzAutowired.class);
                //获得bean的名字
                String beanName = qdzAutowired.value();
                if(StringUtils.isBlank(beanName)){
                    String key = f.getType().getName().substring(f.getType().getName().lastIndexOf(".")+1);
                    beanName = (new StringBuilder()).append(Character.toLowerCase(key.charAt(0))).append(key.substring(1)).toString();
                }
                if(!f.canAccess(entry.getValue())){
                    f.setAccessible(true);
                }
                try {
                    Object obj1 = entry.getValue();
                    Object obj2 = concurrentHashMap.get(beanName);
                    /*if(obj2==null){//根据名称找不到,直接在根据类型找
                        obj2 = getBean(f.getType());
                    }*/
                    //自动注入
                    f.set(obj1,obj2);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                    continue;
                }
            }
        }

    }

}

Controller

package com.qdz.test.controller;

import com.qdz.frameWork.annotation.QdzAutowired;
import com.qdz.frameWork.annotation.QdzController;
import com.qdz.test.service.GoodsService;

@QdzController("asd")
public class GoodsController {

    @QdzAutowired("goodsServiceImpl")
    private GoodsService goodsService;
    public void say(String args) {
        System.out.println(goodsService.say());
    }

}

Service

package com.qdz.test.service;

public interface GoodsService {
    String say();
}

ServiceImpl

package com.qdz.test.service.impl;

import com.qdz.frameWork.annotation.QdzService;
import com.qdz.test.service.GoodsService;
@QdzService
public class GoodsServiceImpl implements GoodsService {
    @Override
    public String say() {
        return "带嘎哈,我系渣渣辉";
    }
}

测试方法

package com.qdz;

import com.qdz.config.QdzSpringApplicationContext;
import com.qdz.frameWork.annotation.QdzComponentScan;
import com.qdz.test.controller.GoodsController;

@QdzComponentScan("com.qdz.test")
public class RunApp {
    public static void main(String[] args) {
        QdzSpringApplicationContext.run(RunApp.class,args);
        GoodsController goodsController = (GoodsController)QdzSpringApplicationContext.getBean("asd");
        goodsController.say(null);
    }
}

运行结果
到这里简单的IOC依赖注入就完成了,目前存在的问题还有很多,写这个目的只是为了加深对象spring的理解和使用。
还存在的问题》
1.service是一个借口,其实现类上的注解没有加名称的时候找不到
2.一下子想不起来,代码垃圾不忍直视
github地址
原创文章,转载请注明出处!

发布了42 篇原创文章 · 获赞 13 · 访问量 8324

猜你喜欢

转载自blog.csdn.net/weixin_43328357/article/details/91583041