Spring框架的IoC是基于Java反射机制实现的
Java
反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性,这种动态获取信息以及动态调用对象方法的功能称为Java
语言的反射机制。简单来说,反射机制指的是程序在运行时能够获取自身的信息
要想解剖一个类,必须先要获取到该类的Class对象,而剖析一个类或用反射解决具体的问题就是使用相关API(1)java.lang.Class(2)java.lang.reflect,所以,Class对象是反射的根源
package com.qcby;import org.junit.Test;import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;public class TestCar {//1.获取Class对象多种方式@Testpublic void test01() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {//1)类名.classClass<Car> clazz1 = Car.class;//2)对象.getClass()Class clazz2 = new Car().getClass();//3.Class.forName("全路径")Class clazz3 = Class.forName("com.qcby.Car");//实例化Car car = (Car) clazz3.getDeclaredConstructor().newInstance();System.out.println(car);}//2.获取构造方法@Testpublic void test02() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {Class<Car> clazz = Car.class;//获取所有public的构造方法
// Constructor[] constructors = clazz.getConstructors();//获取所有构造方法(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("宝马", 10, "白色");System.out.println(car2);}//3.获取属性@Testpublic void test03() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {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(field.getName());System.out.println(car);}}//4.获取方法@Testpublic void test04() throws InvocationTargetException, IllegalAccessException {Car car = new Car("奔驰",10,"黑色");Class clazz = car.getClass();//1)public方法Method[] methods = clazz.getMethods();for (Method method : methods) {
// System.out.println(method.getName());//执行方法if(method.getName().equals("toString")){String invoke = (String) method.invoke(car);System.out.println("toString执行了:"+invoke);}}//2)private方法Method[] methods1 = clazz.getDeclaredMethods();for (Method method : methods1) {//执行方法if(method.getName().equals("run")){method.setAccessible(true);method.invoke(car);}}}}
手写IoC
1.创建子模块spring-ioc
2.创建测试类 service dao
3.创建两个注解
- @Bean 创建对象
- @Di 属性注入
4.创建bean容器接口 ApplicationContext
- 定义方法
- 返回对象
5.实现bean容器接口
- 返回对象
- 根据包规则加载bean
扫描com.qcby这个包和它的子包里面的所有类,看类上面是否有@Bean注解,如果有则把这个类通过反射实例化
package com.qcby.dao;public interface UserDao {void add();
}
package com.qcby.dao.impl;import com.qcby.anno.Bean;
import com.qcby.dao.UserDao;
import org.springframework.stereotype.Repository;@Bean
public class UserDaoImpl implements UserDao {@Overridepublic void add() {System.out.println("dao...");}
}
package com.qcby.service;public interface UserService {void add();
}
package com.qcby.service.impl;import com.qcby.anno.Bean;
import com.qcby.anno.Di;
import com.qcby.dao.UserDao;
import com.qcby.service.UserService;
import org.springframework.stereotype.Service;@Bean
public class UserServiceImpl implements UserService {@Diprivate UserDao userDao;@Overridepublic void add() {System.out.println("service...");//调用dao的方法userDao.add();}
}
package com.qcby.bean;public interface ApplicationContext {Object getBean(Class clazz);
}
package com.qcby.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.qcby.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 {
}
package com.qcby.bean;import com.qcby.anno.Bean;
import com.qcby.anno.Di;import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
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;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) throws IOException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {//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));}//属性注入loadDi();}//包扫描过程,实例化private void loadBean(File file) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {//1.判断当前是否是文件夹if(file.isDirectory()){//2.获取文件夹里的所有内容File[] childrenFiles = file.listFiles();//3.判断文件夹里面为空,直接返回if(childrenFiles == null || childrenFiles.length == 0){return;}//4.如果文件夹不为空,遍历文件夹所有内容for (File child : childrenFiles) {//1)遍历得到每个File对象,继续判断,如果还是文件夹,递归if(child.isDirectory()){loadBean(child);}else {//2)遍历得到File对象不是文件夹,是文件//3)得到包路径+类名称部分 字符串截取String pathWithClass = child.getAbsolutePath().substring(rootPath.length() - 1);//4)判断当前文件类型是否是.classif(pathWithClass.contains(".class")){//5)如果是.class类型,把路径\替换成.,把.class去掉String allName = pathWithClass.replaceAll("\\\\", ".").replace(".class", "");//6)判断类上是否有注解@Bean,如果有,实例化过程//6.1获取类的ClassClass<?> clazz = Class.forName(allName);//6.2判断不是接口,实例化if(!clazz.isInterface()){//6.3判断类上面是否有注解@BeanBean annotation = clazz.getAnnotation(Bean.class);if(annotation != null){//6.4实例化Object instance = clazz.getConstructor().newInstance();//7)把对象实例化之后,放到map集合beanFactory//7.1判断当前类如果有接口,让接口class作为map的keyif (clazz.getInterfaces().length>0){beanFactory.put(clazz.getInterfaces()[0],instance);}else {beanFactory.put(clazz,instance);}}}}}}}}//属性注入private void loadDi() throws IllegalAccessException {//实例化对象在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注解,把对象进行设置(注入)field.set(obj,beanFactory.get(field.getType()));}}}}}
测试:
package com.qcby;import com.qcby.bean.AnnotationApplicationContext;
import com.qcby.bean.ApplicationContext;
import com.qcby.service.UserService;import java.io.IOException;
import java.lang.reflect.InvocationTargetException;public class TestUser {public static void main(String[] args) throws IOException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {ApplicationContext context =new AnnotationApplicationContext("com.qcby");UserService userService = (UserService) context.getBean(UserService.class);System.out.println(userService);userService.add();}
}