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;
}
@Override
public String toString() {
return "Car{" +
"bind='" + bind + '\'' +
", lifeTime=" + lifeTime +
", color='" + color + '\'' +
'}';
}
}
1、获取class对象
@Test
public void testGetClass() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//1、类名.class
Class<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则不能够使用这种方法获取,如果构造方法中包含私有的方法,则需要使用getDeclaredConstructors
Constructor<?>[] 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、构造是private
Constructor<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参数个数3
car1 = Car{bind='悍马', lifeTime=2, color='yellow'}
* */
}
3、获取属性
@Test
public void getAttribute() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//获得类的字节码文件,类、对象和class.forName
Class<?> 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() = bind
carCar{bind='野马', lifeTime=0, color='null'}
declaredField.getName() = lifeTime
carCar{bind='野马', lifeTime=0, color='null'}
declaredField.getName() = color
carCar{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());
//执行方法toString
if(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 {
@Di
private 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;
//返回对象
@Override
public 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获取类的class
Class<?> clazz = Class.forName(allName);
//4.5.2判断不是interface
if(!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作为key
if(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();
//获取对象Class
Class<?> 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................
* */