Spring之IOC小结(二)基于注解手写IOC

Spring之IOC小结(二)基于注解手写IOC

目标:基于注解方式怎样实现动态地创建对象

Demo目录结构:

在这里插入图片描述

第一步,创建注解

@Target 属性用于注明此注解用在什么位置 , ElementType.TYPE表示可用在类、接口、枚举上等
@Retention 属性表示所定义的注解何时有效, RetentionPolicy.RUNTIME表示在运行时有效
@interface 表示注解类型

MyComponent:
package com.jbp.annotation;
import	java.lang.annotation.RetentionPolicy;

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

/**
 * @Author JiangBeiPing
 * @Date 2020/1/7 11:49
 * @Version 1.0
 **/
//@Target 属性用于注明此注解用在什么位置 , ElementType.TYPE表示可用在类、接口、枚举上等
//@Retention 属性表示所定义的注解何时有效, RetentionPolicy.RUNTIME表示在运行时有效
//@interface 表示注解类型
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyComponent {

    public String scope();//为此注解定义scope属性

}

MyValue:
package com.jbp.annotation;

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

/**
 * @Author JiangBeiPing
 * @Date 2020/1/7 11:55
 * @Version 1.0
 **/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyValue {

    public String value();//定义value属性

}

第二步,创建entity对象类型,用于在运行时创建实例对象

PrototypeUser:
package com.jbp.entity;

import com.jbp.annotation.MyComponent;
import com.jbp.annotation.MyValue;

/**
 * @Author JiangBeiPing
 * @Date 2020/1/7 12:02
 * @Version 1.0
 **/
@MyComponent(scope="prototype")
public class PrototypeUser {

    @MyValue("008")
    private Integer id;

    @MyValue("xgc")
    private String name;

    @MyValue("xgc")
    private String password;

    public PrototypeUser() {
        System.out.println("无参构造方法执行");
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "PrototypeUser{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

SingletonUser:
package com.jbp.entity;

import com.jbp.annotation.MyComponent;
import com.jbp.annotation.MyValue;

/**
 * @Author JiangBeiPing
 * @Date 2020/1/7 11:57
 * @Version 1.0
 **/
@MyComponent(scope = "singleton")
public class SingletonUser {

    @MyValue("007")
    private Integer id;

    @MyValue("jbp")
    private String name;

    @MyValue("jbp")
    private String password;

    public SingletonUser() {
        System.out.println("无参构造方法执行");
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "SingletonUser{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

第三步,创建AnnotationConfigApplicationContext工厂类

package com.jbp.applicationContext;

import com.jbp.annotation.MyComponent;
import com.jbp.annotation.MyValue;

import java.io.File;
import java.io.FileFilter;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author JiangBeiPing
 * @Date 2020/1/7 12:07
 * @Version 1.0
 **/
public class AnnotationConfigApplicationContext {

    //beanDefinitionFacotry: 容器用于存储类定义对象
    private Map<String, Class<?>> beanDefinationFacotry=new ConcurrentHashMap<>();

    //singletonbeanFactory: 容器用于存储单例对象
    private Map<String,Object> singletonbeanFactory=new ConcurrentHashMap<>();

    //有参构造方法,参数类型为指定要扫描加载的包名
    public AnnotationConfigApplicationContext(String packageName) {
        scanPkg(packageName);//扫描指定的包路径
    }

    /**
     * 扫描指定包,找到包中的类文件
     * 对于标准(类上有定义注解的)类文件反射加载创建类定义对象并放入容器中
     */
    private void scanPkg(final String pkg){
        //替换包名中的".",将包结构转换为目录结构
        String pkgDir=pkg.replaceAll("\\.", "/");
        //获取目录结构在类路径中的位置(其中url中封装了具体资源的路径)
        URL url=getClass().getClassLoader().getResource(pkgDir);
        //基于这个路径资源(url),构建一个文件对象
        File file=new File(url.getFile());
        //获取此目录中指定标准(以".class"结尾)的文件
        File[] fs=file.listFiles(new FileFilter() {
            @Override
            public boolean accept(File file) {
                //获取文件名
                String fName=file.getName();
                //判断该文件是否为目录,如为目录,递归进一步扫描其内部所有文件
                if(file.isDirectory()){
                    scanPkg(pkg+"."+fName);
                }else{
                    //判定文件的后缀是否为.class
                    if(fName.endsWith(".class")){
                        return true;
                    }
                }
                return false;
            }
        });
        //遍历所有符合标准的File文件
        for(File f:fs){
            //获取文件名
            String fName=f.getName();
            //获取去除.class之后的文件名
            fName=fName.substring(0,fName.lastIndexOf("."));
            //将名字(类名,通常为大写开头)的第一个字母转换小写(用它作为key存储工厂中)
            String key=String.valueOf(fName.charAt(0)).toLowerCase()+fName.substring(1);
            //构建一个类全名(包名.类名)
            String pkgCls=pkg+"."+fName;
            try{
                //通过反射构建类对象
                Class<?> c=Class.forName(pkgCls);
                //判定这个类上是否有MyComponent注解
                if(c.isAnnotationPresent(MyComponent.class)){
                    //将类对象存储到map容器中
                    beanDefinationFacotry.put(key, c);
                }
            }catch(Exception e){
                throw new RuntimeException(e);
            }
        }
    }


    /**
     *根据传入的bean的id值获取容器中的对象,类型为Object
     */
    public Object getBean(String beanId){
        //根据传入beanId获取类对象
        Class<?> cls = beanDefinationFacotry.get(beanId);
        //根据类对象获取其定义的注解
        MyComponent annotation = cls.getAnnotation(MyComponent.class);
        //获取注解的scope属性值
        String scope = annotation.scope();
        try {
            //如果scope等于singleton,创建单例对象
            if ("singleton".equals(scope)){
                //判断容器中是否已有该对象的实例,如果没有,创建一个实例对象放到容器中
                if (singletonbeanFactory.get(beanId)  == null){
                    Object instance = cls.newInstance();
                    setFieldValues(cls,instance);
                    singletonbeanFactory.put(beanId,instance);
                }
                //根据beanId获取对象并返回
                return singletonbeanFactory.get(beanId);
            }
            //如果scope等于prototype,则创建并返回多例对象
            if ("prototype".equals(scope)){
                Object instance = cls.newInstance();
                setFieldValues(cls,instance);
                return instance;
            }
            //目前仅支持单例和多例两种创建对象的方式
        }catch (InstantiationException e){
           // e.printStackTrace();
        }catch (IllegalAccessException e){
           // e.printStackTrace();
        }
        //如果遭遇异常,返回null
        return null;
    }


    /**
     * 此为重载方法,根据传入的class对象在内部进行强转
     * @param beanId
     * @param c
     * @param <T>
     * @return 返回传入的class对象的类型
     */
    public <T>T getBean(String beanId,Class<T> c){
        return (T)getBean(beanId);
    }

    /**
     * 此方法用于为对象的属性赋值
     * 内部是通过获取成员属性上注解的值,在转换为类型之后,通过反射为对象赋值
     * @param cls 类定义对象
     * @param obj 要为其赋值的实例对象
     */
    public void setFieldValues(Class<?> cls,Object obj){
        //获取类中所有的成员属性
        Field[] fields = cls.getDeclaredFields();
        //遍历所有属性
        for (Field field : fields) {
            //如果此属性有MyValue注解修饰,对其进行操作
            if (field.isAnnotationPresent(MyValue.class)){
                //获取属性名
                String fieldName = field.getName();
                //获取注解中的值
                String value = field.getAnnotation(MyValue.class).value();
                //获取属性所定义的类型
                String type = field.getType().getName();
                //将属性名改为以大写字母开头,如:id改为ID,name改为Name
                 fieldName = String.valueOf(fieldName.charAt(0)).toUpperCase() + fieldName.substring(1);
                //set方法名称,如:setId,setName...
                String setterName = "set" + fieldName;
                try {
                    //根据方法名称和参数类型获取对应的set方法对象
                    Method method = cls.getDeclaredMethod(setterName, field.getType());
                    //判断属性类型,如类型不一致,则转换类型后调用set方法为属性赋值
                    if ("java.lang.Integer".equals(type) || "int".equals(type)){
                        int intValue = Integer.valueOf(value);
                        method.invoke(obj,value);
                    }else if ("java.lang.String".equals(type)){
                        method.invoke(obj, value);
                    }
                    //作为测试,仅判断Integer和String类型,其它类型同理
                }catch (NoSuchMethodException | SecurityException e){
                 //   e.printStackTrace();
                }catch (IllegalAccessException e){
                  //  e.printStackTrace();
                }catch (IllegalArgumentException e){
                  //  e.printStackTrace();
                }catch (InvocationTargetException e){
                 //   e.printStackTrace();
                }
            }

        }
    }


    /**
     * 销毁方法,用于释放资源
     */
    public void close(){
        beanDefinationFacotry.clear();
        beanDefinationFacotry=null;
        singletonbeanFactory.clear();
        singletonbeanFactory=null;
    }


}


第三步,测试

package com.jbp.test;

import com.jbp.applicationContext.AnnotationConfigApplicationContext;
import com.jbp.entity.PrototypeUser;
import com.jbp.entity.SingletonUser;

/**
 * @Author JiangBeiPing
 * @Date 2020/1/7 15:56
 * @Version 1.0
 **/
public class springIocTest {
    public static void main(String[] args) {
        //创建AnnotationConfigApplicationContext对象
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext("com.jbp.entity");
        //仅使用key作为参数获取对象,需要强转
        SingletonUser singletonUser1=(SingletonUser) ctx.getBean("singletonUser");
        System.out.println("单例User对象:"+singletonUser1);
        //使用key和类对象作为参数获取对象,无需强转
        SingletonUser singletonUser2=ctx.getBean("singletonUser",SingletonUser.class);
        System.out.println("单例User对象:"+singletonUser2);
        //仅使用key作为参数获取对象,需要强转
        PrototypeUser prototypeUser1=(PrototypeUser) ctx.getBean("prototypeUser");
        System.out.println("多例User对象:"+prototypeUser1);
        //使用key和类对象作为参数获取对象,无需强转
        PrototypeUser prototypeUser2=ctx.getBean("prototypeUser",PrototypeUser.class);
        System.out.println("多例User对象:"+prototypeUser2);
        //关闭资源
        ctx.close();
    }



}

测试结果:

在这里插入图片描述

发布了29 篇原创文章 · 获赞 21 · 访问量 654

猜你喜欢

转载自blog.csdn.net/xueguchen/article/details/103877424