用反射模拟SpringIOC

1.代码结构

image-20210120161511178

2. 基础类代码

MyComponent 自定义的组件注解

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyComponent {
}

MyComponentScan 自定义的扫描注解

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyComponentScan {
    String[] value();
}

MyResource 自定义的Resource注解

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyResource {
}

User 类

public class User {
    private String userName;
    private String password;
    public User(String userName, String password) {
        this.userName = userName;
        this.password = password;
    }
	...省略get、set
    @Override
    public String toString() {
        return "User{" +
                "userName='" + userName + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

UserDao

public interface UserDao {
    void login(User user);
}

UserDaoImpl 类

@MyComponent
public class UserDaoImpl implements UserDao {
    @Override
    public void login(User user) {
        System.out.println("用户登录校验!");
    }
}

UserService 类

public interface UserService {
    void login(User user);
}

UserServiceImpl 类

@MyComponent
public class UserServiceImpl implements UserService {
    @MyResource
    private UserDao userDao;
    @Override
    public void login(User user) {
        System.out.println("调用UserDaoImpl的login方法");
        userDao.login(user);
    }
}

beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <bean id="user" class="com.xuboming.reflection.entity.User">
        <property name="username" value="张三" />
        <property name="password" value="123" />
    </bean>
</beans>

3.核心代码

AnnotationConfigApplicationContext 对扫描路径下的类以及类中带MyResource注解的属性进行注入

/**
 * 解析扫面路径下的类并解析类中的注解
 */
public class AnnotationConfigApplicationContext<T> {
	
    //Class的存储路径
    private String path;

    //1.定义一个集合用来存在解析出来的对象,模拟spring容器
    private Map<Class,Object> beanFactory = new HashMap<Class, Object>();

    //2.从beanFactory中获取bean
    public T getBean(Class clazz){
        return (T) beanFactory.get(clazz);
    }

    //3.解析扫面路径下的类并解析类中的注解
    public void initBeanByAnnotation(){
        //编译后的项目根目录:E:/workspace/spring-study/spring-poxy/target/classes/
        path = AnnotationConfigApplicationContext.class.getClassLoader().getResource("").getFile();
        //3.1获取驱动类的class对象
        Class clazz = MyTest.class;
        //3.2获取扫描的注解
        MyComponentScan myComponentScan = (MyComponentScan) clazz.getAnnotation(MyComponentScan.class);
        if(myComponentScan != null){
            //获取自定义的扫描路径
            String[] scanPaths = myComponentScan.value();
            //有定义扫描路径,就去加载路径下的实例
            if(scanPaths != null && scanPaths.length>0){
                loadClassInDefinedDir(path,scanPaths);
            }
        }
        //对MyResource注解的属性进行注入
        assembleObject();
    }
	//将扫描路径(相对路径)转为绝对路径,得到的是文件夹
    private void loadClassInDefinedDir(String path,String[] scanPaths){
        for(String scanPath : scanPaths){
            scanPath = scanPath.replaceAll("\\.","/");
            String fileDir = path + scanPath;
            System.out.println(fileDir);
            findClassFile(new File(fileDir));
        }
    }
	//遍历文件夹,获取文件夹中的class文件
    private void findClassFile(File fileDir) {
        //判断路径是否为文件夹
        if(fileDir.isDirectory()){
            File[] files = fileDir.listFiles();
            if(files == null || files.length == 0){
                return ;
            }
            //遍历文件夹中的文件或文件夹
            for(File file : files){
                if(file.isDirectory()){//如果是文件夹,递归
                    findClassFile(file);
                }else{//如果是文件,加载该类到容器中
                    loadClassWithAnnotation(file);
                }
            }
        }
    }
	//将class文件转为对象,并存到beanFactory中
    private void loadClassWithAnnotation(File file) {
        //1.去掉前面的项目绝对路径
        String classPath = file.getAbsolutePath().substring(path.length()-1);
        //2.将'\'转为'.' 并去掉后面的'.class'
        if(classPath.contains(".class")){
            String fullName = classPath.replaceAll("\\\\","\\.").replaceAll("\\.class","");
            try {
                //获取Class对象
                Class clazz = Class.forName(fullName);
                //3.判断是不是接口,不是接口才创建实例
                if(!clazz.isInterface()){
                    //4.判断是否有MyComponent注解,有MyComponent注解才创建对象
                    MyComponent myComponent = (MyComponent) clazz.getAnnotation(MyComponent.class);
                    if(myComponent != null ){
                        //5.创建实例
                        Object instance = clazz.newInstance();
                        //6.判断该类是否有实现的接口
                        Class[] interfaces = clazz.getInterfaces();
                        if(interfaces != null && interfaces.length>0){
                            //如果存在实现接口,将接口作为key,实例对象作为value存到map容器中 一个类只能实现一个接口,不用遍历接口类
                            System.out.println("正在加载类【"+interfaces[0].getName()+"】实例对象"+instance.getClass().getName());
                            //生成的对象加到beanFactory中
                            beanFactory.put(interfaces[0],instance);
                        }else{
                            //如果没有实现接口,就将class作为key,instance作为value
                            System.out.println("正在加载类【"+clazz.getName()+"】实例对象"+instance.getClass().getName());
                            //生成的对象加到beanFactory中
                            beanFactory.put(clazz,instance);
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
     //给MyResource注解的属性赋值
    private void assembleObject() {
        //生成Map遍历器
        Set<Map.Entry<Class, Object>> beanSet = beanFactory.entrySet();
        //遍历所有遍历器中的Bean
        for(Map.Entry<Class,Object> entry : beanSet){
            //获取当前对象
            Object object = entry.getValue();
            //取当前对象的所有的属性值
            Field[] fields = object.getClass().getDeclaredFields();
            //遍历属性值判段是否存在MyResource注解
            for(Field field : fields){
                MyResource myResource = field.getAnnotation(MyResource.class);
                //存在MyResource注解,给该属性赋值
                if(myResource != null){
                    //打开安全检测机制
                    field.setAccessible(true);
                    try {
                        //给object(当前)对象中的属性赋值
                        field.set(object,beanFactory.get(field.getType()));
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

}

ClassPathXMLApplicationContext 解析xml文件,将xml中的bean实例化

/**
 * 解析xml文件
 */
public class ClassPathXMLApplicationContext {

    //xml文件
    private File file;

    //模拟容器
    private Map<String,Object> beanFactory = new HashMap();
	
    //获取对象
    public Object getBean(String id){
        return beanFactory.get(id);
    }
	
   	//构造方法,传入需要解析的xml文件名
    public ClassPathXMLApplicationContext(String configXml) throws URISyntaxException {
        //获取传入的xml文件的绝对路径
        URL url = this.getClass().getClassLoader().getResource(configXml);
        System.out.println(url.toURI());
        //生成File
        file  = new File(url.toURI());

        try {
            //解析XMl
            XMLParsing();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
	//解析Xml
    private void XMLParsing() throws JDOMException, IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException {
        SAXBuilder builder = new SAXBuilder();
        Document document = builder.build(file);
        //获取根节点
        Element root = document.getRootElement();
        //获取所有的bean节点
        List beanElementList = root.getChildren("bean");
        //生成bean节点集合的遍历器
        Iterator it = beanElementList.iterator();
        //遍历bean节点
        while (it.hasNext()){
            Element bean = (Element) it.next();
            //获取id属性值
            String id = bean.getAttributeValue("id");
            //获取class属性值
            String cls = bean.getAttributeValue("class");
            //根据class创建class对象
            Class clazz  = Class.forName(cls);
            //实例化
            Object object = clazz.newInstance();
            //获取类中所有的方法
            Method[] methods = clazz.getDeclaredMethods();
			//获取bean下的property节点集合
            List<Element> propertyElementList = bean.getChildren("property");
            //判断property是否为空,methods是否为空。一般都不会为空
            if(propertyElementList != null && propertyElementList.size()>0 && methods != null && methods.length>0){
                //给property中的属性赋值
                for(Element property : propertyElementList){
                    //获取属性名
                    String name = property.getAttributeValue("name");
                    //获取属性值
                    String value = property.getAttributeValue("value");
                    //遍历该对象下的所有方法
                    for(Method method : methods){
                        //获得方法名
                        String methodName = method.getName();
                        //判断该方法是否是set方法
                        if(methodName.startsWith("set")){
                            //如果是set方法,将其处理后与property中进行比较判断是否相等
                            methodName = methodName.substring(3).toLowerCase();
                            if(methodName.equals(name)){
                                //如果相等,调用该方法对对象Object进行赋值
                                method.invoke(object,value);
                            }
                        }
                    }
                }
            }
            beanFactory.put(id,object);
        }
    }
}

4. 运行结果

MyTest 客户端

@MyComponentScan({"com.xuboming.reflection.dao","com.xuboming.reflection.service"})
public class MyTest {
    public static void main(String[] args) throws URISyntaxException {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        applicationContext.initBeanByAnnotation();
        UserServiceImpl userService = (UserServiceImpl) applicationContext.getBean(UserService.class);

        ClassPathXMLApplicationContext classPathXMLApplicationContext =  new ClassPathXMLApplicationContext("beans.xml");
        User user = (User) classPathXMLApplicationContext.getBean("user");
        System.out.println(user);
        userService.login(user);
    }

}

运行结果

image-20210120164907795

posted @ 2021-01-20 16:51  随风mc  阅读(159)  评论(0)    收藏  举报