查看: 175|回复: 6

[Java Web开发] 手写Spring

[复制链接]
  • TA的每日心情

    2018-10-18 16:49
  • 签到天数: 13 天

    [LV.3]偶尔看看II

    发表于 2019-5-17 14:37:06 | 显示全部楼层 |阅读模式
    手写Spring

          本文主要介绍一下,注解方式的Spring ,Resources和Service 两种常用注解方式.

          首先我们来看一下项目结构

            

         1.先创建一个工具类 叫做ClassUtil当然可以随便取

            话不多说,贴代码

            


    package com.spring.util;

    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 博博
    *
    */
    @SuppressWarnings(value = { "rawtypes", "unchecked" })
    public class ClassUtil {

            /**
             * 取得某个接口下所有实现这个接口的类
             */

            public static List<Class> getAllClassByInterface(Class c) {
                    List<Class> returnClassList = null;

                    if (c.isInterface()) {
                            // 获取当前的包名
                            String packageName = c.getPackage().getName();
                            // 获取当前包下以及子包下所以的类
                            List<Class<?>> allClass = getClasses(packageName);
                            if (allClass != null) {
                                    returnClassList = new ArrayList<Class>();
                                    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;
                    }
                    File packeageDir = new File(realClassLocation);
                    if (packeageDir.isDirectory()) {
                            String[] allClassName = packeageDir.list();
                            return allClassName;
                    }
                    return null;
            }

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

                    // 第一个class类的集合
                    List<Class<?>> classes = new ArrayList<Class<?>>();
                    // 是否循环迭代
                    boolean recursive = true;
                    // 获取包的名字 并进行替换
                    String packageDirName = packageName.replace('.', '/');
                    // 定义一个枚举的集合 并进行循环来处理这个目录下的things
                    Enumeration<URL> 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<JarEntry> 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<Class<?>> 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();
                                    }
                            }
                    }
            }

            /**
             * 首字母小写
             * @param s
             * @return
             */
            public static String toLowerCaseFirstOne(String s) {
                    if (Character.isLowerCase(s.charAt(0)))
                            return s;
                    else
                            return (new StringBuilder()).append(Character.toLowerCase(s.charAt(0))).append(s.substring(1)).toString();
            }
    }

    本工具类中包含了很多反射需要用到的方法,封装成工具类方便之后使用。

    第二步、创建两个注解 MhbResource、MhbServicepackage com.spring.annotation;

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

    /**
    * 自定义service注入
    *
    * @author 博博
    *
    */
    @Target({ ElementType.TYPE })
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MhbService {

    }
    package com.spring.annotation;

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

    /**
    * 自定义注解,bean自动注入
    *
    * @author 博博
    *
    */
    @Target({ ElementType.FIELD })
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MhbResource {

    }
    创建两个注解之后,创建关键类ClassPathAnnotationApplicationContextpackage com.spring.springannotation;

    import java.lang.reflect.Field;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.ConcurrentHashMap;

    import org.apache.commons.lang.StringUtils;

    import com.spring.annotation.MhbService;
    import com.spring.util.ClassUtil;

    /**
    * 获取bean实例类
    *
    * @author 博博
    *
    */
    public class ClassPathAnnotationApplicationContext {

            // 定义扫包范围
            private String packageNum;

            // 定义一个ConcurrentHashMap的用来存储bean实例
            private ConcurrentHashMap<String, Object> initBean = null;

            public ClassPathAnnotationApplicationContext(String packageNum) {
                    this.packageNum = packageNum;
            }

            /**
             * 获取bean实例
             *
             * @param baenId
             * @return
             * @throws Exception
             */
            public Object getBean(String beanId) throws Exception {
                    // 先判断是否为空
                    if (initBean != null) {
                            // 不为空的时候,查找是否有这个bean
                            Object obj = initBean.get(beanId);
                            // 有这个bean就直接返回,保证了单例
                            if (obj != null) {
                                    return obj;
                            } else {
                                    return initBean(beanId);
                            }
                    } else {
                            return initBean(beanId);
                    }

            }

            /**
             * 初始化
             *
             * @return
             */
            public Object initBean(String beanId) throws Exception {
                    // 把所有被MhbService注解 使用的类全部获取
                    List<Class<?>> findClassMhbService = findClassMhbService();
                    if (findClassMhbService == null || findClassMhbService.isEmpty()) {
                            throw new RuntimeException("该包下没有类使用了注解!");
                    }

                    // 把初始化的bean放入map中
                    initBean = initBean(findClassMhbService);
                    if (initBean == null || initBean.isEmpty()) {
                            throw new RuntimeException("is not find");
                    }

                    // 把对象实例从map中取出来
                    Object object = initBean.get(beanId);

                    attriAssignMhbResource(object);

                    return object;
            }

            /**
             * 利用反射检查哪些类使用了注解
             *
             * @return
             * @throws Exception
             */
            public List<Class<?>> findClassMhbService() throws Exception {
                    // 判断传入的扫包范围是否为空
                    if (StringUtils.isEmpty(packageNum)) {
                            throw new Exception("扫包的范围不能为空!");
                    }

                    // 扫描出所有包下的所有类
                    List<Class<?>> classes = ClassUtil.getClasses(packageNum);

                    // 把扫包范围的使用了注解的类存入list中
                    List<Class<?>> list = new ArrayList<Class<?>>();

                    for (Class<?> classInfo : classes) {
                            // 判断类上是否用来注解
                            MhbService mhbService = classInfo.getDeclaredAnnotation(MhbService.class);
                            // 不为空为使用了
                            if (mhbService != null) {
                                    // 存入定义的list
                                    list.add(classInfo);
                                    // 跳出循环
                                    continue;
                            }
                    }

                    return list;
            }

            /**
             * 初始化bean
             *
             * @return
             * @throws IllegalAccessException
             * @throws InstantiationException
             */
            public ConcurrentHashMap<String, Object> initBean(List<Class<?>> findClassMhbService)
                            throws InstantiationException, IllegalAccessException {

                    if (findClassMhbService == null || findClassMhbService.isEmpty()) {
                            return null;
                    }
                    ConcurrentHashMap<String, Object> concurrentHashMap = new ConcurrentHashMap<String, Object>();
                    // 循环传入的所有类,并实例化
                    for (Class<?> classInfo : findClassMhbService) {
                            // 初始化bean
                            Object object = classInfo.newInstance();
                            // 把找到的类第一个首字母变成小写
                            String className = ClassUtil.toLowerCaseFirstOne(classInfo.getSimpleName());
                            // 把实例传入
                            concurrentHashMap.put(className, object);
                    }

                    return concurrentHashMap;
            }

            /**
             * 通过反射获取类的属性信息,赋值信息
             *
             * @param object 传入的实例对象
             * @throws IllegalAccessException
             * @throws IllegalArgumentException
             */
            public void attriAssignMhbResource(Object object) throws IllegalArgumentException, IllegalAccessException {
                    // 1.获取类的属性是否存在 获取bean注解
                    Class<? extends Object> classInfo = object.getClass();

                    Field[] fields = classInfo.getDeclaredFields();
                    for (Field field : fields) {
                            // 获取属性名称
                            String name = field.getName();

                            Object bean = initBean.get(name);
                            if (bean != null) {
                                    // 允许访问私有属性
                                    field.setAccessible(true);
                                    field.set(object, bean);
                                    continue;
                            }

                    }

            }

    }
    接下来,我们来测试一下这个效果怎么样
    创建UsersMapper.java

    创建UsersMapperImpl.java

    创建UsersService

    创建UsersServiceImpl


    由于代码比较容易,就直接贴一起了

    package com.spring.mapper;

    public interface UsersMapper {

            String doRun();
           
    }

    package com.spring.mapper.impl;

    import com.spring.annotation.MhbService;
    import com.spring.mapper.UsersMapper;

    // 使用自定义注解
    @MhbService
    public class UsersMapperImpl implements UsersMapper{

            @Override
            public String doRun() {
                    return "测试成功!";
            }

    }
    package com.spring.service;

    public interface UsersService {

            void doRun();
           
    }
    package com.spring.service.impl;

    import com.spring.annotation.MhbResource;
    import com.spring.annotation.MhbService;
    import com.spring.mapper.UsersMapper;
    import com.spring.service.UsersService;

    // 使用自定义注解
    @MhbService
    public class UsersServiceImpl implements UsersService {

            // 注入
            @MhbResource
            private UsersMapper usersMapperImpl;

            @Override
            public void doRun() {

                    System.out.println(usersMapperImpl.doRun());
            }

    }
    创建测试类
    package com.spring.test;

    import org.junit.Test;

    import com.spring.service.impl.UsersServiceImpl;
    import com.spring.springannotation.ClassPathAnnotationApplicationContext;

    public class TextSpring {

           
            @Test
            public void Text1() throws Exception {
                    ClassPathAnnotationApplicationContext app = new ClassPathAnnotationApplicationContext("com.spring");
                    UsersServiceImpl usersServiceImpl = (UsersServiceImpl) app.getBean("usersServiceImpl");
                    usersServiceImpl.doRun();
            }
           
    }
    运行结果


    好了,本期手写Spring就结束了,有的人可能就想问,为什么要手写这些东西呢?现成的不是有吗?对此呢,可能等你手写一次就很明白了,我这里也不能直说,

    就这样,拜拜.


    个人博客 :

    http://mhba.work


    该用户从未签到

    发表于 2019-5-29 22:55:37 | 显示全部楼层
    注解方式的Spring

    该用户从未签到

    发表于 2019-6-26 22:29:13 | 显示全部楼层
          首先我们来看一下项目结构

    您需要登录后才可以回帖 登录 | 注册青鸟豆号

    本版积分规则

    Copyright 1999-2019 Beijing Aptech Beida Jade Bird Information Technology Co.,Ltd

    北大青鸟IT教育 北京阿博泰克北大青鸟信息技术有限公司 版权所有

    京ICP备11045574号-3 京公网安备11010802013845号

    快速回复 返回顶部 返回列表