• 注解学习(模仿springMvc的注解注入方式)


    最近在看springMvc的源码,看到了该框架的注入注解的部分觉的有点吃力,可能还是对注解的方面的知识还认识的不够深刻,所以特意去学习注解方面的知识。由于本人也是抱着学习的态度来阅读源码,若文章在表述和代码方面如有不妥之处,欢迎批评指正。留下你的脚印,欢迎评论!希望能互相学习。

    1,首先定义三个常用的注解Service,Autowired,Contrller;(主要的解释都在代码中有,在这里就不多陈述)

    Service:

    package com.lishun.Annotation;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /*Description:
     * @Target:指定注解的使用范围(指的是,在哪些类型可以使用该注解:Service注解只能在类,接口(包括注解类型)或enum等使用)
     * 可选值:
     * 可选的值在枚举类 ElemenetType 中,包括: 
              ElemenetType.CONSTRUCTOR 构造器声明 
              ElemenetType.FIELD 域声明(包括 enum 实例) 
              ElemenetType.LOCAL_VARIABLE 局部变量声明
              ElemenetType.ANNOTATION_TYPE 作用于注解量声明
              ElemenetType.METHOD 方法声明
              ElemenetType.PACKAGE 包声明 
              ElemenetType.PARAMETER 参数声明 
              ElemenetType.TYPE 类,接口(包括注解类型)或enum声明 
    
     * */
    
    @Target(ElementType.TYPE)
    /*Description:
     * @Retention :表示在什么级别保存该注解信息
     * 可选的参数值在枚举类型 RetentionPolicy 中,包括: 
              RetentionPolicy.SOURCE 注解将被编译器丢弃 
              RetentionPolicy.CLASS 注解在class文件中可用,但会被VM丢弃 
              RetentionPolicy.RUNTIME VM将在运行期也保留注释,因此可以通过反射机制读取注解的信息。 
     * */
    @Retention(RetentionPolicy.RUNTIME)
    
    /*@Documented 将此注解包含在 javadoc 中 ,它代表着此注解会被javadoc工具提取成文档。
     * 在doc文档中的内容会因为此注解的信息内容不同而不同。相当与@see,@param 等。
     * */
    @Documented
    
    
    public @interface Service {
        /* @interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。
         * 方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。
         * 可以通过default来声明参数的默认值。
        */
        String value() default "this is service annotation";
    }

    Autowired:

    package com.lishun.Annotation;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Inherited;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target({ElementType.METHOD,ElementType.FIELD})  
    @Retention(RetentionPolicy.RUNTIME)  
    public @interface Autowired {
          public String value() default "no description";  
    }

    Contrller:

    package com.lishun.Annotation;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Contrller {
        String value() default "this is contrller annotation";
    }

    ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    2:javaBean数据池-BeanFactory:主要存放含有注解的类;

    package com.lishun.factory;
    
    import java.util.HashMap;
    import java.util.Map;
    /**
     * Description:存放所有bean的数据池 
     * @author lishun
     * @since 2015-09-10
     */
    public class BeanFactory {
        private static Map<String, Object> map = new HashMap<String, Object>();
    
        public static void addBean(String beanName, Object bean) {
            map.put(beanName, bean);
        }
    
        public static Object getBean(String beanName) throws Exception {
            Object o = map.get(beanName);
            if (o != null) {
                return o;
            } else {
                throw new Exception("未注入的类型:" + beanName);
            }
        }
        public static Boolean containsBean(String beanName){
            return map.containsKey(beanName);
        }
    }

    ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    3:编写处理注解的核心代码(这里涉及的主要知识是反射,如果反射知识不够熟练的话建议先学习反射方面的知识),主要涉及的两个类是注解驱动(AnnotationDriven)和注解扫描类(PackUtils-这个类主要的是扫描包名下所有的类(如com.lishun,就是扫描该包下所有的类),代码主要是来自网络)

    AnnotationDriven:

    package com.lishun.utils;
    
    import java.lang.annotation.Annotation;
    import java.lang.reflect.Field;
    import java.util.List;
    
    import com.lishun.Annotation.Autowired;
    import com.lishun.Annotation.Contrller;
    import com.lishun.Annotation.Service;
    import com.lishun.factory.BeanFactory;
    
    /**
     * Description:注入驱动类,所有的注解注入都在这里实现(这里只实现了通过类型来注入值,其他方式没实现,其实代码都是差不多了,有兴趣的可以自行脑补)
     * @author lishun
     *
     */
    public class AnnotationDriven {
        public static void annotationDriven(String packName) throws Exception {
            //注入Service和Contrller
            List<Class<?>> classSaveServicePaths = PackUtils
                    .getClassListByAnnotation(packName, Service.class);
            List<Class<?>> classSaveContrllerPaths = PackUtils
                    .getClassListByAnnotation(packName, Contrller.class);
            saveBean(classSaveServicePaths);
            saveBean(classSaveContrllerPaths);
            //注入Autowired
            List<Class<?>> classInjectPaths = PackUtils.getClassListByAnnotation(
                    packName, Autowired.class);
            inject(classInjectPaths);
        }
    
        private static void saveBean(List<Class<?>> classSavePaths)
                throws InstantiationException, IllegalAccessException {
            for (Class<?> classPath : classSavePaths) {
                try {
                    Class c = Class.forName(classPath.getName());
                    Object o = c.newInstance();
                    //扫描的到的含有注解的类实例化后保存在池中
                    BeanFactory.addBean(classPath.getName(), o);
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }
    
        private static void inject(List<Class<?>> classInjectPaths) throws Exception {
            Object o = null;
            for (Class<?> classInjectPath : classInjectPaths) {
    
                Class c = Class.forName(classInjectPath.getName());
                //判断存放bean的池中是否存在该bean
                if (BeanFactory.containsBean(classInjectPath.getName())) {
                    o = BeanFactory.getBean(classInjectPath.getName());
                } else {
                    o = c.newInstance();
                }
                Field[] fields = c.getDeclaredFields();
                for (Field field : fields) {
                    Annotation[] annotations = field.getAnnotations();
                    for (Annotation annotation : annotations) {
                        // 判断是否是通过类型注解注入
                        if (annotation instanceof Autowired) {
                            Class classField = field.getType();
                            Object clazz = BeanFactory
                                    .getBean(classField.getName());
                            field.set(o, clazz);
                            BeanFactory.addBean(classInjectPath.getName(), o);
    
                        }
                    }
                }
    
            }
        }
    }

    PackUtils:

    package com.lishun.utils;
    import java.io.File;
    import java.io.FileFilter;
    import java.lang.annotation.Annotation;
    import java.lang.reflect.Field;
    import java.net.JarURLConnection;
    import java.net.URL;
    import java.util.ArrayList;
    import java.util.Enumeration;
    import java.util.List;
    import java.util.jar.JarEntry;
    import java.util.jar.JarFile;
    /**
     * Description:扫描指定包工具类的注解
     * @author lishun
     * @since 2015-09-10
     */
    public class PackUtils {
        public static List<Class<?>> getClassList(String packageName, boolean isRecursive) {
            List<Class<?>> classList = new ArrayList<Class<?>>();
            try {
                Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(packageName.replaceAll("\.", "/"));
                while (urls.hasMoreElements()) {
                    URL url = urls.nextElement();
                    if (url != null) {
                        String protocol = url.getProtocol();
                        if (protocol.equals("file")) {
                            String packagePath = url.getPath();
                            addClass(classList, packagePath, packageName, isRecursive);
                        } else if (protocol.equals("jar")) {
                            JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
                            JarFile jarFile = jarURLConnection.getJarFile();
                            Enumeration<JarEntry> jarEntries = jarFile.entries();
                            while (jarEntries.hasMoreElements()) {
                                JarEntry jarEntry = jarEntries.nextElement();
                                String jarEntryName = jarEntry.getName();
                                if (jarEntryName.endsWith(".class")) {
                                    String className = jarEntryName.substring(0, jarEntryName.lastIndexOf(".")).replaceAll("/", ".");
                                    if (isRecursive || className.substring(0, className.lastIndexOf(".")).equals(packageName)) {
                                        classList.add(Class.forName(className));
                                    }
                                }
                            }
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return classList;
        }
    
        // 获取指定包名下的所有类(可根据注解进行过滤)
        public static List<Class<?>> getClassListByAnnotation(String packageName, Class<? extends Annotation> annotationClass) {
            List<Class<?>> classList = new ArrayList<Class<?>>();
            try {
                Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(packageName.replaceAll("\.", "/"));
                while (urls.hasMoreElements()) {
                    URL url = urls.nextElement();
                    if (url != null) {
                        String protocol = url.getProtocol();
                        if (protocol.equals("file")) {
                            String packagePath = url.getPath();
                            addClassByAnnotation(classList, packagePath, packageName, annotationClass);
                        } else if (protocol.equals("jar")) {
                            JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
                            JarFile jarFile = jarURLConnection.getJarFile();
                            Enumeration<JarEntry> jarEntries = jarFile.entries();
                            while (jarEntries.hasMoreElements()) {
                                JarEntry jarEntry = jarEntries.nextElement();
                                String jarEntryName = jarEntry.getName();
                                if (jarEntryName.endsWith(".class")) {
                                    String className = jarEntryName.substring(0, jarEntryName.lastIndexOf(".")).replaceAll("/", ".");
                                    Class<?> cls = Class.forName(className);
                                    if (cls.isAnnotationPresent(annotationClass)) {
                                        classList.add(cls);
                                    }
                                }
                            }
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return classList;
        }
    
        private static void addClass(List<Class<?>> classList, String packagePath, String packageName, boolean isRecursive) {
            try {
                File[] files = getClassFiles(packagePath);
                if (files != null) {
                    for (File file : files) {
                        String fileName = file.getName();
                        if (file.isFile()) {
                            String className = getClassName(packageName, fileName);
                            classList.add(Class.forName(className));
                        } else {
                            if (isRecursive) {
                                String subPackagePath = getSubPackagePath(packagePath, fileName);
                                String subPackageName = getSubPackageName(packageName, fileName);
                                addClass(classList, subPackagePath, subPackageName, isRecursive);
                            }
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        private static File[] getClassFiles(String packagePath) {
            return new File(packagePath).listFiles(new FileFilter() {
                @Override
                public boolean accept(File file) {
                    return (file.isFile() && file.getName().endsWith(".class")) || file.isDirectory();
                }
            });
        }
    
        private static String getClassName(String packageName, String fileName) {
            String className = fileName.substring(0, fileName.lastIndexOf("."));
            if (!packageName.equals("")) {
                className = packageName + "." + className;
            }
            return className;
        }
    
        private static String getSubPackagePath(String packagePath, String filePath) {
            String subPackagePath = filePath;
            if (!packagePath.equals("")) {
                subPackagePath = packagePath + "/" + subPackagePath;
            }
            return subPackagePath;
        }
    
        private static String getSubPackageName(String packageName, String filePath) {
            String subPackageName = filePath;
            if (!packageName.equals("")) {
                subPackageName = packageName + "." + subPackageName;
            }
            return subPackageName;
        }
    
        private static void addClassByAnnotation(List<Class<?>> classList, String packagePath, String packageName, Class<? extends Annotation> annotationClass) {
            try {
                File[] files = getClassFiles(packagePath);
                if (files != null) {
                    for (File file : files) {
                        String fileName = file.getName();
                        if (file.isFile()) {
                            String className = getClassName(packageName, fileName);
                            Class<?> cls = Class.forName(className);
                            if (cls.isAnnotationPresent(annotationClass)) {
                                classList.add(cls);
                            }
                            Field[] fields=cls.getFields();
                            for (Field field : fields) {
                                if(field.isAnnotationPresent(annotationClass)){
                                     classList.add(cls);
                                }
                            }
                        } else {
                            String subPackagePath = getSubPackagePath(packagePath, fileName);
                            String subPackageName = getSubPackageName(packageName, fileName);
                            addClassByAnnotation(classList, subPackagePath, subPackageName, annotationClass);
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    4 最后编写平时使用的设计模式来测试注解(Dao,Service,Contrller)【这里主要是为测试注解的注入,所以没有使用实际的使用数据库数据,侧重点不在这里】

    Dao

    package com.lishun.Dao;
    
    import com.lishun.Annotation.Service;
    
    @Service
    public class UserDao {
        public void run(){
            System.out.println("测试成功");
        }
    }

    Service:

    package com.lishun.Service;
    
    import com.lishun.Annotation.Autowired;
    import com.lishun.Annotation.Service;
    import com.lishun.Dao.UserDao;
    @Service
    public class UserService {
        @Autowired
        public UserDao userDao;
    
        public void run(){
             userDao.run();
        }
    
    }

    Controller:

    package com.lishun.controller;
    
    import com.lishun.Annotation.Autowired;
    import com.lishun.Annotation.Contrller;
    import com.lishun.Service.UserService;
    @Contrller
    public class UserContrller {
        @Autowired
        public  UserService userService;
        public void login(){
            userService.run();
        }
    }

    测试入口

    package com.lishun.t;
    
    import java.lang.annotation.Annotation;
    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.util.Arrays;
    import java.util.List;
    
    import org.junit.Test;
    
    import com.lishun.Annotation.Autowired;
    import com.lishun.Annotation.Contrller;
    import com.lishun.Annotation.Service;
    import com.lishun.Dao.UserDao;
    import com.lishun.Service.UserService;
    import com.lishun.controller.UserContrller;
    import com.lishun.factory.BeanFactory;
    import com.lishun.utils.AnnotationDriven;
    import com.lishun.utils.PackUtils;
    
    public class test {
    
        @Test
        public void main() throws Exception {
            //启动时根据需要扫描的包名,来注入含有注解的类的字段值
            AnnotationDriven.annotationDriven("com.lishun");
            //这里相当于web的访问一次controller的一次请求
            UserContrller user = (UserContrller) BeanFactory
                    .getBean("com.lishun.controller.UserContrller");
            user.login();
        }
    
    }

    最后运行,

    控制台输出:测试成功

    由于本人水平有限,若文章在表述和代码方面如有不妥之处,欢迎批评指正。留下你的脚印,欢迎评论!希望能互相学习。需要源码的留下邮箱

  • 相关阅读:
    Spring中的@Transactional(rollbackFor = Exception.class)属性详解
    查询数据库中表数量和各表中数据量
    69道Spring面试题和答案
    Spring常见面试题总结(超详细回答)
    nginx 解决session一致性
    redis 主从同步
    如何实现一个线程安全的单例,前提是不能加锁
    InnoDB中一棵B+树能存多少行数据
    ConcurrentHashMap 源码分析
    java HashMap 源码解析
  • 原文地址:https://www.cnblogs.com/lishun1005/p/4800936.html
Copyright © 2020-2023  润新知