Spring框架的IOC是基于Java反射机制实现的,那具体怎么实现的,下面研究一下
反射
Java
反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为Java
语言的反射机制。简单来说,反射机制指的是程序在运行时能够获取自身的信息。
要想解剖一个类,必须先要获取到该类的Class对象。而剖析一个类或用反射解决具体的问题就是使用相关API**(1)java.lang.Class(2)java.lang.reflect**,所以,Class对象是反射的根源。
package com.atguigu.reflect;public class Car {//属性private String name;private int age;private String color;//无参数构造public Car() {}//有参数构造public Car(String name, int age, String color) {this.name = name;this.age = age;this.color = color;}//普通方法private void run() {System.out.println("私有方法-run.....");}//get和set方法public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getColor() {return color;}public void setColor(String color) {this.color = color;}@Overridepublic String toString() {return "Car{" +"name='" + name + '\'' +", age=" + age +", color='" + color + '\'' +'}';}
}
package com.atguigu.reflect;import org.junit.jupiter.api.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;public class TestCar {//1、获取Class对象多种方式@Testpublic void test01() throws Exception {//1 类名.classClass clazz1 = Car.class;//2 对象.getClass()Class clazz2 = new Car().getClass();//3 Class.forName("全路径")Class clazz3 = Class.forName("com.atguigu.reflect.Car");//实例化Car car = (Car)clazz3.getConstructor().newInstance();System.out.println(car);}
}
package com.atguigu.reflect;import org.junit.jupiter.api.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;public class TestCar {//1、获取Class对象多种方式@Testpublic void test01() throws Exception {//1 类名.classClass clazz1 = Car.class;//2 对象.getClass()Class clazz2 = new Car().getClass();//3 Class.forName("全路径")Class clazz3 = Class.forName("com.atguigu.reflect.Car");int length = clazz1.getInterfaces().length;System.out.println(length);//实例化Car car = (Car)clazz3.getConstructor().newInstance();System.out.println(car);}//2、获取构造方法@Testpublic void test02() throws Exception {Class clazz = Car.class;//获取所有构造// getConstructors()获取所有public的构造方法
// Constructor[] constructors = clazz.getConstructors();//getDeclaredConstructors()获取所有的构造方法public private
// Constructor[] constructors = clazz.getDeclaredConstructors();
// for (Constructor c: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 构造privateConstructor c2 = clazz.getDeclaredConstructor(String.class, int.class, String.class);c2.setAccessible(true);Car car2 = (Car)c2.newInstance("捷达", 15, "白色");System.out.println(car2);}//3、获取属性@Testpublic void test03() throws Exception {Class clazz = Car.class;Car car = (Car)clazz.getDeclaredConstructor().newInstance();//获取所有public属性//Field[] fields = clazz.getFields();//获取所有属性(包含私有属性)Field[] fields = clazz.getDeclaredFields();for (Field field:fields) {if(field.getName().equals("name")) {//设置允许访问field.setAccessible(true);field.set(car,"五菱宏光");System.out.println(car);}System.out.println(field.getName());}}//4、获取方法@Testpublic void test04() throws Exception {Car car = new Car("奔驰",10,"黑色");Class clazz = car.getClass();//1 public方法Method[] methods = clazz.getMethods();for (Method m1:methods) {//System.out.println(m1.getName());//执行方法 toStringif(m1.getName().equals("toString")) {String invoke = (String)m1.invoke(car);//System.out.println("toString执行了:"+invoke);}}//2 private方法Method[] methodsAll = clazz.getDeclaredMethods();for (Method m:methodsAll) {//执行方法 runif(m.getName().equals("run")) {m.setAccessible(true);m.invoke(car);}}}
}
实现Spring中的IoC
一:创建子模块
二:创建测试类
package com.atguigu.service;public interface UserService {void add();
}
package com.atguigu.service.impl;import com.atguigu.service.UserService;public class UserServiceImpl implements UserService {@Diprivate UserDao userDao;public void add() {System.out.println("service.......");//调用dao的方法userDao.add();}
}
package com.atguigu.dao;public interface UserDao {void add();
}
package com.atguigu.dao.impl;import com.atguigu.anno.Bean;
import com.atguigu.dao.UserDao;
import org.springframework.stereotype.Repository;public class UserDaoImpl implements UserDao {@Overridepublic void add() {System.out.println("dao.......");}
}
三:创建两个注解(@Bean和@Di)
package com.atguigu.anno;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 Bean {
}
package com.atguigu.anno;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Di {
}
四:创建bean容器接口ApplicationContext,定义方法返回对象
package com.atguigu.bean;public interface ApplicationContext {Object getBean(Class clazz);
}
五:实现bean容器接口,返回对象,根据包扫描规则加载bean
package com.atguigu.bean;import com.atguigu.anno.Bean;
import com.atguigu.anno.Di;import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;public class AnnotationApplicationContext implements ApplicationContext{//创建map集合,放bean对象private Map<Class,Object> beanFactory = new HashMap<>();private static String rootPath;//返回对象@Overridepublic Object getBean(Class clazz) {return beanFactory.get(clazz);}//创建有参数构造,传递包路径,设置包扫描规则//当前包及其子包,哪个类有@Bean注解,把这个类通过反射实例化public AnnotationApplicationContext(String basePackage) {// com.atguigutry {//1 把.替换成\String packagePath = basePackage.replaceAll("\\.","\\\\");//2 获取包绝对路径Enumeration<URL> urls= Thread.currentThread().getContextClassLoader().getResources(packagePath);while(urls.hasMoreElements()) {URL url = urls.nextElement();String filePath = URLDecoder.decode(url.getFile(),"utf-8");//获取包前面路径部分,字符串截取rootPath = filePath.substring(0, filePath.length() - packagePath.length());//包扫描loadBean(new File(filePath));}} catch (Exception e) {throw new RuntimeException(e);}//属性注入loadDi();}//包扫描过程,实例化private void loadBean(File file) throws Exception {//1 判断当前是否文件夹if(file.isDirectory()) {//2 获取文件夹里面所有内容File[] childrenFiles = file.listFiles();//3 判断文件夹里面为空,直接返回if(childrenFiles == null || childrenFiles.length == 0) {return;}//4 如果文件夹里面不为空,遍历文件夹所有内容for(File child : childrenFiles) {//4.1 遍历得到每个File对象,继续判断,如果还是文件夹,递归if(child.isDirectory()) {//递归loadBean(child);} else {//4.2 遍历得到File对象不是文件夹,是文件,//4.3 得到包路径+类名称部分-字符串截取String pathWithClass =child.getAbsolutePath().substring(rootPath.length() - 1);//4.4 判断当前文件类型是否.classif(pathWithClass.contains(".class")) {//4.5 如果是.class类型,把路径\替换成. 把.class去掉// com.atguigu.service.UserServiceImplString allName = pathWithClass.replaceAll("\\\\", ".").replace(".class", "");//4.6 判断类上面是否有注解 @Bean,如果有实例化过程//4.6.1 获取类的ClassClass<?> clazz = Class.forName(allName);//4.6.2 判断不是接口if(!clazz.isInterface()) {//4.6.3 判断类上面是否有注解 @BeanBean annotation = clazz.getAnnotation(Bean.class);if(annotation != null) {//4.6.4 实例化Object instance = clazz.getConstructor().newInstance();//4.7 把对象实例化之后,放到map集合beanFactory//4.7.1 判断当前类如果有接口,让接口class作为map的keyif(clazz.getInterfaces().length>0) {beanFactory.put(clazz.getInterfaces()[0],instance);} else {beanFactory.put(clazz,instance);}}}}}}}}//属性注入private void loadDi() {//实例化对象在beanFactory的map集合里面//1 遍历beanFactory的map集合Set<Map.Entry<Class, Object>> entries = beanFactory.entrySet();for (Map.Entry<Class, Object> entry:entries) {//2 获取map集合每个对象(value),每个对象属性获取到Object obj = entry.getValue();//获取对象ClassClass<?> clazz = obj.getClass();//获取每个对象属性获取到Field[] declaredFields = clazz.getDeclaredFields();//3 遍历得到每个对象属性数组,得到每个属性for(Field field:declaredFields) {//4 判断属性上面是否有@Di注解Di annotation = field.getAnnotation(Di.class);if(annotation != null) {//如果私有属性,设置可以设置值field.setAccessible(true);//5 如果有@Di注解,把对象进行设置(注入)try {field.set(obj,beanFactory.get(field.getType()));} catch (IllegalAccessException e) {throw new RuntimeException(e);}}}}}
}
最终测试
package com.atguigu;import com.atguigu.bean.AnnotationApplicationContext;
import com.atguigu.bean.ApplicationContext;
import com.atguigu.service.UserService;public class TestUser {public static void main(String[] args) {ApplicationContext context =new AnnotationApplicationContext("com.atguigu");UserService userService = (UserService)context.getBean(UserService.class);System.out.println(userService);userService.add();}
}