IOC原理(手写IOC)
Spring框架的IOC是基于反射机制实现的。
反射回顾
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制,简单来说,反射机制就是程序在运行时能够获取自身的信息。
示例
实体类Car
package com.louis.reflect;/*** @author XRY* @date 2023年06月26日8:59*/
public class Car {private String bind;private int lifeTime;private String color;public Car() {}public Car(String bind, int lifeTime, String color) {this.bind = bind;this.lifeTime = lifeTime;this.color = color;}//普通方法private void use(){System.out.println("私有方法..........");}public String getBind() {return bind;}public void setBind(String bind) {this.bind = bind;}public int getLifeTime() {return lifeTime;}public void setLifeTime(int lifeTime) {this.lifeTime = lifeTime;}public String getColor() {return color;}public void setColor(String color) {this.color = color;}@Overridepublic String toString() {return "Car{" +"bind='" + bind + '\'' +", lifeTime=" + lifeTime +", color='" + color + '\'' +'}';}
}
1、获取class对象
@Test
public void testGetClass() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {//1、类名.classClass<Car> clazz01 = Car.class;//2、对象.getClass()Class<? extends Car> clazz02 = new Car().getClass();//3、Class.forName("全路径")Class<?> clazz03 = Class.forName("com.louis.reflect.Car");//实例化Car car = (Car)clazz03.getDeclaredConstructor().newInstance();logger.info("car" + car);
}/*[2023-06-26 09:16:13:036] [INFO] - com.louis.reflect.TestCar.testGetClass(TestCar.java:28) - carcom.louis.reflect.Car@2445445a*/
2、获取构造方法
@Test
public void testCacheConstructor() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {Class<Car> clazz = Car.class;//获取所有构造//getConstructors针对public方法,如果是private则不能够使用这种方法获取,如果构造方法中包含私有的方法,则需要使用getDeclaredConstructorsConstructor<?>[] constructors = clazz.getConstructors();for (Constructor<?> constructor : constructors) {logger.info("constructor" + constructor.getName() + "参数个数" + constructor.getParameterCount());/** [2023-06-26 09:57:16:855] [INFO] - com.louis.reflect.TestCar.testCacheConstructor(TestCar.java:38) - constructorcom.louis.reflect.Car参数个数0[2023-06-26 09:57:16:858] [INFO] - com.louis.reflect.TestCar.testCacheConstructor(TestCar.java:38) - constructorcom.louis.reflect.Car参数个数3* */}//指定有参数的构造去创建对象//1、构造是public,如果目标对象是private则会报错/*Constructor<Car> haveParameterPub = clazz.getConstructor(String.class, int.class, String.class);Car car = haveParameterPub.newInstance("野马", 1, "blue");/*[2023-06-26 10:07:47:947] [INFO] - com.louis.reflect.TestCar.testCacheConstructor(TestCar.java:39) - constructorcom.louis.reflect.Car参数个数0[2023-06-26 10:07:47:950] [INFO] - com.louis.reflect.TestCar.testCacheConstructor(TestCar.java:39) - constructorcom.louis.reflect.Car参数个数3汽车Car{bind='野马', lifeTime=1, color='blue'}*///2、构造是privateConstructor<Car> haveParameterPri = clazz.getDeclaredConstructor(String.class, int.class, String.class);haveParameterPri.setAccessible(true);//设置访问权限,如果为false不能够访问Car car1 = haveParameterPri.newInstance("悍马", 2, "yellow");System.out.println("car1 = " + car1);/*[2023-06-26 10:13:58:492] [INFO] - com.louis.reflect.TestCar.testCacheConstructor(TestCar.java:39) - constructorcom.louis.reflect.Car参数个数0[2023-06-26 10:13:58:496] [INFO] - com.louis.reflect.TestCar.testCacheConstructor(TestCar.java:39) - constructorcom.louis.reflect.Car参数个数3car1 = Car{bind='悍马', lifeTime=2, color='yellow'}* */
}
3、获取属性
@Test
public void getAttribute() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {//获得类的字节码文件,类、对象和class.forNameClass<?> carClass = Class.forName("com.louis.reflect.Car");//实例化Car car = (Car)carClass.getDeclaredConstructor().newInstance();//获取其中所有的public方法Field[] fields = carClass.getFields();//获取所有的属性,包括私有的属性Field[] declaredFields = carClass.getDeclaredFields();for (Field declaredField : declaredFields) {System.out.println("declaredField.getName() = " + declaredField.getName());//给属性赋值if(declaredField.getName().equals("bind")){//设置允许访问declaredField.setAccessible(true);//传入对象和属性值declaredField.set(car,"野马");}System.out.println("car" + car);}/** declaredField.getName() = bindcarCar{bind='野马', lifeTime=0, color='null'}declaredField.getName() = lifeTimecarCar{bind='野马', lifeTime=0, color='null'}declaredField.getName() = colorcarCar{bind='野马', lifeTime=0, color='null'}* */
}
4、获取方法
@Test
public void getMethod() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {Car car = new Car("Benz", 10, "black");Class<? extends Car> clazz = car.getClass();//1、public方法,不会取到私有Method[] methods = clazz.getMethods();for (Method method : methods) {
// System.out.println(method.getName());//执行方法toStringif(method.getName().equals("toString")){String invoke = (String)method.invoke(car);System.out.println("toString执行了" + invoke);/*toString执行了Car{bind='Benz', lifeTime=10, color='black'}*/}}//2、private方法Method[] methodsAll = clazz.getDeclaredMethods();for (Method methodA : methodsAll) {//执行私有方法if(methodA.getName().equals("use")){methodA.setAccessible(true);methodA.invoke(car);}}/*私有方法..........*/
}
实现Spring的IoC
实现过程
1、创建模块spring-ioc
2、创建测试类service、dao
接口实现
@Bean
public class UserServiceImpl implements UserService {@Diprivate UserDao userDao;public void add(){System.out.println("service......");//调用dao的方法userDao.show();}
}
3、创建两个注解@Bean(创建对象)、 @Di(属性注入)
@Bean
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 {//用于创建对象
}
@Di
package com.louis.annotation;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 {//用于注入属性
}
4、创建bean容器接口ApplicationContext,定义方法、返回对象
public interface ApplicationContext {//在IOC中BeanFactory返回的是一个工厂Object getBean(Class clazz);
}
5、实现bean容器接口,创建实现类
(1)返回对象
(2)根据包规则加载bean(扫描路径下包含@Bean注解的类,并将这些类通过反射实例化)
package com.louis.bean.impl;import com.louis.annotation.Bean;
import com.louis.annotation.Di;
import com.louis.bean.ApplicationContext;import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;/*** @author XRY* @date 2023年06月26日16:39*/
public class AnnotationApplicationContext implements ApplicationContext {//模拟IOC,创建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){//扫描路径//1、将.替换成\String packagePath = basePackage.replaceAll("\\.", "\\\\");//2、获取包的绝对路径,编译之后的路径try {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());
// System.out.println("filePath = " + filePath);//根据获得的路径进行包扫描loadBean(new File(filePath));}} catch (Exception e) {throw new RuntimeException(e);}//属性注入loadDi();}/*** 包扫描过程,进行实例化* @param file*/private void loadBean(File file) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {//1、判断当前路径下的东西是否是文件夹,如果不是就不需要继续往下查找if(file.isDirectory()){//2、获取文件夹里面所有内容File[] childFiles = file.listFiles();//3、判断文件夹里面为空,直接返回if(childFiles == null || childFiles.length == 0){return;}//4、如果文件夹里面不为空,遍历文件夹中所有内容for(File child:childFiles){//4.1遍历得到每个File对象,继续判断,如果是文件夹,递归if(child.isDirectory()){//递归loadBean(child);}else{//4.2遍历得到的File对象不是文件夹//4.3得到包路径+类名称部分String packagePath = child.getAbsolutePath().substring(rootPath.length() - 1);//4.4判断当前文件类型是否为.class,如果是,将路径中的\替换为.并将.class文件去掉if(packagePath.contains(".class")){String allName = packagePath.replaceAll("\\\\", "\\.").replaceAll(".class", "");//4.5判断类上面是否有注解@Bean,如果有进行实例化过程//4.5.1获取类的classClass<?> clazz = Class.forName(allName);//4.5.2判断不是interfaceif(!clazz.isInterface()){Bean annotation = clazz.getAnnotation(Bean.class);if(annotation != null){//4.5.3实例化Object instance = clazz.getConstructor().newInstance();//4.7把对象实例化之后,放到map集合beanFactory//4.7.1判断当前类如果有接口,让接口class作为map的key,如果没有接口将自己的class作为keyif(clazz.getInterfaces().length > 0){beanFactory.put(clazz.getInterfaces()[0], instance);}else{beanFactory.put(clazz,instance);}}}}}}}}// public static void main(String[] args) {
// new AnnotationApplicationContext("com.louis");
// }//属性注入private void loadDi(){//实例化的对象都在beanFactory的map集合里//1、遍历beanFactory的map集合,entrySet()用来获取到对象的集合Set<Map.Entry<Class, Object>> entries = beanFactory.entrySet();for (Map.Entry<Class, Object> entry : entries) {//2、获取map集合中每个对象(value),每个对象获取到属性Object value = entry.getValue();//获取对象ClassClass<?> clazz = value.getClass();//获取每个对象中的属性Field[] declaredFields = clazz.getDeclaredFields();//3、遍历得到的每个对象属性的数组,得到每个属性for (Field field : declaredFields) {//4、判断属性上是否有@Di注解,Di annotation = field.getAnnotation(Di.class);if(annotation != null){//如果是私有属性可以设置一个值field.setAccessible(true);//有Di注解就把对象进行设置(注入)try {field.set(value, beanFactory.get(field.getType()));} catch (IllegalAccessException e) {throw new RuntimeException(e);}}}}}
}
6、测试
public class TestBean {public static void main(String[] args){ApplicationContext context = new AnnotationApplicationContext("com.louis");UserService userService = (UserService) context.getBean(UserService.class);
// UserService userService1 = new UserServiceImpl();userService.add();}
}/*
* service......
UserDao................
* */