文章目录
前言
我们都知道,Spring框架的IOC是基于Java反射机制实现的,下面我们先回顾一下java反射。
一、回顾Java反射
Java
反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为Java
语言的反射机制。简单来说,反射机制指的是程序在运行时能够获取自身的信息。
要想解剖一个类,必须先要获取到该类的Class对象。而剖析一个类或用反射解决具体的问题就是使用相关API**(1)java.lang.Class(2)java.lang.reflect**,所以,Class对象是反射的根源。
自定义类
packagecom.atguigu.reflect; publicclassCar { //属性privateStringname; privateintage; privateStringcolor; //无参数构造publicCar() { } //有参数构造publicCar(Stringname, intage, Stringcolor) { this.name=name; this.age=age; this.color=color; } //普通方法privatevoidrun() { System.out.println("私有方法-run....."); } //get和set方法publicStringgetName() { returnname; } publicvoidsetName(Stringname) { this.name=name; } publicintgetAge() { returnage; } publicvoidsetAge(intage) { this.age=age; } publicStringgetColor() { returncolor; } publicvoidsetColor(Stringcolor) { this.color=color; } publicStringtoString() { return"Car{"+"name='"+name+'\''+", age="+age+", color='"+color+'\''+'}'; } }
编写测试类
packagecom.atguigu.reflect; importorg.junit.jupiter.api.Test; importjava.lang.reflect.Constructor; importjava.lang.reflect.Field; importjava.lang.reflect.Method; publicclassTestCar { //1、获取Class对象多种方式publicvoidtest01() throwsException { //1 类名.classClassclazz1=Car.class; //2 对象.getClass()Classclazz2=newCar().getClass(); //3 Class.forName("全路径")Classclazz3=Class.forName("com.atguigu.reflect.Car"); //实例化Carcar= (Car)clazz3.getConstructor().newInstance(); System.out.println(car); } //2、获取构造方法publicvoidtest02() throwsException { Classclazz=Car.class; //获取所有构造// getConstructors()获取所有public的构造方法// Constructor[] constructors = clazz.getConstructors();// getDeclaredConstructors()获取所有的构造方法public privateConstructor[] constructors=clazz.getDeclaredConstructors(); for (Constructorc:constructors) { System.out.println("方法名称:"+c.getName()+" 参数个数:"+c.getParameterCount()); } //指定有参数构造创建对象//1 构造public// Constructor c1 = clazz.getConstructor(String.class, int.class, String.class);// Car car1 = (Car)c1.newInstance("夏利", 10, "红色");// System.out.println(car1);//2 构造privateConstructorc2=clazz.getDeclaredConstructor(String.class, int.class, String.class); c2.setAccessible(true); Carcar2= (Car)c2.newInstance("捷达", 15, "白色"); System.out.println(car2); } //3、获取属性publicvoidtest03() throwsException { Classclazz=Car.class; Carcar= (Car)clazz.getDeclaredConstructor().newInstance(); //获取所有public属性//Field[] fields = clazz.getFields();//获取所有属性(包含私有属性)Field[] fields=clazz.getDeclaredFields(); for (Fieldfield:fields) { if(field.getName().equals("name")) { //设置允许访问field.setAccessible(true); field.set(car,"五菱宏光"); System.out.println(car); } System.out.println(field.getName()); } } //4、获取方法publicvoidtest04() throwsException { Carcar=newCar("奔驰",10,"黑色"); Classclazz=car.getClass(); //1 public方法Method[] methods=clazz.getMethods(); for (Methodm1:methods) { //System.out.println(m1.getName());//执行方法 toStringif(m1.getName().equals("toString")) { Stringinvoke= (String)m1.invoke(car); //System.out.println("toString执行了:"+invoke); } } //2 private方法Method[] methodsAll=clazz.getDeclaredMethods(); for (Methodm:methodsAll) { //执行方法 runif(m.getName().equals("run")) { m.setAccessible(true); m.invoke(car); } } } }
二、实现Spring的IoC
我们知道,IoC(控制反转)和DI(依赖注入)是Spring里面核心的东西,那么,我们如何自己手写出这样的代码呢?下面我们就一步一步写出Spring框架最核心的部分。
①搭建子模块
搭建模块:guigu-spring,搭建方式如其他spring子模块
②准备测试需要的bean
添加依赖
<dependencies><!--junit5测试--><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-api</artifactId><version>5.3.1</version></dependency></dependencies>
创建UserDao接口
packagecom.atguigu.spring6.test.dao; publicinterfaceUserDao { publicvoidprint(); }
创建UserDaoImpl实现
packagecom.atguigu.spring6.test.dao.impl; importcom.atguigu.spring.dao.UserDao; publicclassUserDaoImplimplementsUserDao { publicvoidprint() { System.out.println("Dao层执行结束"); } }
创建UserService接口
packagecom.atguigu.spring6.test.service; publicinterfaceUserService { publicvoidout(); }
创建UserServiceImpl实现类
packagecom.atguigu.spring.test.service.impl; importcom.atguigu.spring.core.annotation.Bean; importcom.atguigu.spring.service.UserService; publicclassUserServiceImplimplementsUserService { // private UserDao userDao;publicvoidout() { //userDao.print();System.out.println("Service层执行结束"); } }
③定义注解
我们通过注解的形式加载bean与实现依赖注入
bean注解
packagecom.atguigu.spring.core.annotation; importjava.lang.annotation.ElementType; importjava.lang.annotation.Retention; importjava.lang.annotation.RetentionPolicy; importjava.lang.annotation.Target; ElementType.TYPE) (RetentionPolicy.RUNTIME) (public@interfaceBean { }
依赖注入注解
packagecom.atguigu.spring.core.annotation; importjava.lang.annotation.ElementType; importjava.lang.annotation.Retention; importjava.lang.annotation.RetentionPolicy; importjava.lang.annotation.Target; ElementType.FIELD}) ({RetentionPolicy.RUNTIME) (public@interfaceDi { }
说明:上面两个注解可以随意取名
④定义bean容器接口
packagecom.atguigu.spring.core; publicinterfaceApplicationContext { ObjectgetBean(Classclazz); }
⑤编写注解bean容器接口实现
AnnotationApplicationContext基于注解扫描bean
packagecom.atguigu.spring.core; importjava.util.HashMap; publicclassAnnotationApplicationContextimplementsApplicationContext { //存储bean的容器privateHashMap<Class, Object>beanFactory=newHashMap<>(); publicObjectgetBean(Classclazz) { returnbeanFactory.get(clazz); } /*** 根据包扫描加载bean* @param basePackage*/publicAnnotationApplicationContext(StringbasePackage) { } }
⑥编写扫描bean逻辑
我们通过构造方法传入包的base路径,扫描被@Bean注解的java对象,完整代码如下:
packagecom.atguigu.spring.core; importcom.atguigu.spring.core.annotation.Bean; importjava.io.File; importjava.util.HashMap; publicclassAnnotationApplicationContextimplementsApplicationContext { //存储bean的容器privateHashMap<Class, Object>beanFactory=newHashMap<>(); privatestaticStringrootPath; publicObjectgetBean(Classclazz) { returnbeanFactory.get(clazz); } /*** 根据包扫描加载bean* @param basePackage*/publicAnnotationApplicationContext(StringbasePackage) { try { StringpackageDirName=basePackage.replaceAll("\\.", "\\\\"); Enumeration<URL>dirs=Thread.currentThread().getContextClassLoader().getResources(packageDirName); while (dirs.hasMoreElements()) { URLurl=dirs.nextElement(); StringfilePath=URLDecoder.decode(url.getFile(),"utf-8"); rootPath=filePath.substring(0, filePath.length()-packageDirName.length()); loadBean(newFile(filePath)); } } catch (Exceptione) { thrownewRuntimeException(e); } } privatevoidloadBean(FilefileParent) { if (fileParent.isDirectory()) { File[] childrenFiles=fileParent.listFiles(); if(childrenFiles==null||childrenFiles.length==0){ return; } for (Filechild : childrenFiles) { if (child.isDirectory()) { //如果是个文件夹就继续调用该方法,使用了递归loadBean(child); } else { //通过文件路径转变成全类名,第一步把绝对路径部分去掉StringpathWithClass=child.getAbsolutePath().substring(rootPath.length() -1); //选中class文件if (pathWithClass.contains(".class")) { // com.xinzhi.dao.UserDao//去掉.class后缀,并且把 \ 替换成 .StringfullName=pathWithClass.replaceAll("\\\\", ".").replace(".class", ""); try { Class<?>aClass=Class.forName(fullName); //把非接口的类实例化放在map中if(!aClass.isInterface()){ Beanannotation=aClass.getAnnotation(Bean.class); if(annotation!=null){ Objectinstance=aClass.newInstance(); //判断一下有没有接口if(aClass.getInterfaces().length>0) { //如果有接口把接口的class当成key,实例对象当成valueSystem.out.println("正在加载【"+aClass.getInterfaces()[0] +"】,实例对象是:"+instance.getClass().getName()); beanFactory.put(aClass.getInterfaces()[0], instance); }else{ //如果有接口把自己的class当成key,实例对象当成valueSystem.out.println("正在加载【"+aClass.getName() +"】,实例对象是:"+instance.getClass().getName()); beanFactory.put(aClass, instance); } } } } catch (ClassNotFoundException|IllegalAccessException|InstantiationExceptione) { e.printStackTrace(); } } } } } } }
⑦java类标识Bean注解
publicclassUserServiceImplimplementsUserService
publicclassUserDaoImplimplementsUserDao
⑧测试Bean加载
packagecom.atguigu.spring; importcom.atguigu.spring.core.AnnotationApplicationContext; importcom.atguigu.spring.core.ApplicationContext; importcom.atguigu.spring.test.service.UserService; importorg.junit.jupiter.api.Test; publicclassSpringIocTest { publicvoidtestIoc() { ApplicationContextapplicationContext=newAnnotationApplicationContext("com.atguigu.spring.test"); UserServiceuserService= (UserService)applicationContext.getBean(UserService.class); userService.out(); System.out.println("run success"); } }
控制台打印测试
⑨依赖注入
只要userDao.print();调用成功,说明就注入成功
packagecom.atguigu.spring.test.service.impl; importcom.atguigu.spring.core.annotation.Bean; importcom.atguigu.spring.core.annotation.Di; importcom.atguigu.spring.dao.UserDao; importcom.atguigu.spring.service.UserService; publicclassUserServiceImplimplementsUserService { privateUserDaouserDao; publicvoidout() { userDao.print(); System.out.println("Service层执行结束"); } }
执行第八步:报错了,说明当前userDao是个空对象
⑩依赖注入实现
packagecom.atguigu.spring.core; importcom.atguigu.spring.core.annotation.Bean; importcom.atguigu.spring.core.annotation.Di; importjava.io.File; importjava.lang.reflect.Field; importjava.util.HashMap; importjava.util.Map; publicclassAnnotationApplicationContextimplementsApplicationContext { //存储bean的容器privateHashMap<Class, Object>beanFactory=newHashMap<>(); privatestaticStringrootPath; publicObjectgetBean(Classclazz) { returnbeanFactory.get(clazz); } /*** 根据包扫描加载bean* @param basePackage*/publicAnnotationApplicationContext(StringbasePackage) { try { StringpackageDirName=basePackage.replaceAll("\\.", "\\\\"); Enumeration<URL>dirs=Thread.currentThread().getContextClassLoader().getResources(packageDirName); while (dirs.hasMoreElements()) { URLurl=dirs.nextElement(); StringfilePath=URLDecoder.decode(url.getFile(),"utf-8"); rootPath=filePath.substring(0, filePath.length()-packageDirName.length()); loadBean(newFile(filePath)); } } catch (Exceptione) { thrownewRuntimeException(e); } //依赖注入loadDi(); } privatevoidloadBean(FilefileParent) { if (fileParent.isDirectory()) { File[] childrenFiles=fileParent.listFiles(); if(childrenFiles==null||childrenFiles.length==0){ return; } for (Filechild : childrenFiles) { if (child.isDirectory()) { //如果是个文件夹就继续调用该方法,使用了递归loadBean(child); } else { //通过文件路径转变成全类名,第一步把绝对路径部分去掉StringpathWithClass=child.getAbsolutePath().substring(rootPath.length() -1); //选中class文件if (pathWithClass.contains(".class")) { // com.xinzhi.dao.UserDao//去掉.class后缀,并且把 \ 替换成 .StringfullName=pathWithClass.replaceAll("\\\\", ".").replace(".class", ""); try { Class<?>aClass=Class.forName(fullName); //把非接口的类实例化放在map中if(!aClass.isInterface()){ Beanannotation=aClass.getAnnotation(Bean.class); if(annotation!=null){ Objectinstance=aClass.newInstance(); //判断一下有没有接口if(aClass.getInterfaces().length>0) { //如果有接口把接口的class当成key,实例对象当成valueSystem.out.println("正在加载【"+aClass.getInterfaces()[0] +"】,实例对象是:"+instance.getClass().getName()); beanFactory.put(aClass.getInterfaces()[0], instance); }else{ //如果有接口把自己的class当成key,实例对象当成valueSystem.out.println("正在加载【"+aClass.getName() +"】,实例对象是:"+instance.getClass().getName()); beanFactory.put(aClass, instance); } } } } catch (ClassNotFoundException|IllegalAccessException|InstantiationExceptione) { e.printStackTrace(); } } } } } } privatevoidloadDi() { for(Map.Entry<Class,Object>entry : beanFactory.entrySet()){ //就是咱们放在容器的对象Objectobj=entry.getValue(); Class<?>aClass=obj.getClass(); Field[] declaredFields=aClass.getDeclaredFields(); for (Fieldfield : declaredFields){ Diannotation=field.getAnnotation(Di.class); if( annotation!=null ){ field.setAccessible(true); try { System.out.println("正在给【"+obj.getClass().getName()+"】属性【"+field.getName() +"】注入值【"+beanFactory.get(field.getType()).getClass().getName() +"】"); field.set(obj,beanFactory.get(field.getType())); } catch (IllegalAccessExceptione) { e.printStackTrace(); } } } } } }
执行第八步:执行成功,依赖注入成功
总结
以上就是Spring之容器:IOC(3)的相关知识点,希望对你有所帮助。
积跬步以至千里,积怠惰以至深渊。时代在这跟着你一起努力哦!