手写简易版IOC容器

1、回顾Java反射2、实现Spring的IoC

我们都知道,Spring框架的IOC是基于Java反射机制实现的,下面我们先回顾一下java反射。

1、回顾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;

}

@Override

public 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对象多种方式

@Test

public void test01() throws Exception {

//1 类名.class

Class 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);

}

//2、获取构造方法

@Test

public 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 构造private

Constructor c2 = clazz.getDeclaredConstructor(String.class, int.class, String.class);

c2.setAccessible(true);

Car car2 = (Car)c2.newInstance("捷达", 15, "白色");

System.out.println(car2);

}

//3、获取属性

@Test

public 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、获取方法

@Test

public 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());

//执行方法 toString

if(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) {

//执行方法 run

if(m.getName().equals("run")) {

m.setAccessible(true);

m.invoke(car);

}

}

}

}

2、实现Spring的IoC

我们知道,IoC(控制反转)和DI(依赖注入)是Spring里面核心的东西,那么,我们如何自己手写出这样的代码呢?下面我们就一步一步写出Spring框架最核心的部分。

①搭建子模块

搭建模块:guigu-spring,搭建方式如其他spring子模块

②准备测试需要的bean

添加依赖

org.junit.jupiter

junit-jupiter-api

5.3.1

创建UserDao接口

package com.atguigu.spring6.test.dao;

public interface UserDao {

public void print();

}

创建UserDaoImpl实现

package com.atguigu.spring6.test.dao.impl;

import com.atguigu.spring.dao.UserDao;

public class UserDaoImpl implements UserDao {

@Override

public void print() {

System.out.println("Dao层执行结束");

}

}

创建UserService接口

package com.atguigu.spring6.test.service;

public interface UserService {

public void out();

}

创建UserServiceImpl实现类

package com.atguigu.spring.test.service.impl;

import com.atguigu.spring.core.annotation.Bean;

import com.atguigu.spring.service.UserService;

@Bean

public class UserServiceImpl implements UserService {

// private UserDao userDao;

@Override

public void out() {

//userDao.print();

System.out.println("Service层执行结束");

}

}

③定义注解

我们通过注解的形式加载bean与实现依赖注入

bean注解

package com.atguigu.spring.core.annotation;

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.spring.core.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 {

}

说明:上面两个注解可以随意取名

④定义bean容器接口

package com.atguigu.spring.core;

public interface ApplicationContext {

Object getBean(Class clazz);

}

⑤编写注解bean容器接口实现

AnnotationApplicationContext基于注解扫描bean

package com.atguigu.spring.core;

import java.util.HashMap;

public class AnnotationApplicationContext implements ApplicationContext {

//存储bean的容器

private HashMap beanFactory = new HashMap<>();

@Override

public Object getBean(Class clazz) {

return beanFactory.get(clazz);

}

/**

* 根据包扫描加载bean

* @param basePackage

*/

public AnnotationApplicationContext(String basePackage) {

}

}

⑥编写扫描bean逻辑

我们通过构造方法传入包的base路径,扫描被@Bean注解的java对象,完整代码如下:

package com.atguigu.spring.core;

import com.atguigu.spring.core.annotation.Bean;

import java.io.File;

import java.util.HashMap;

public class AnnotationApplicationContext implements ApplicationContext {

//存储bean的容器

private HashMap beanFactory = new HashMap<>();

private static String rootPath;

@Override

public Object getBean(Class clazz) {

return beanFactory.get(clazz);

}

/**

* 根据包扫描加载bean

* @param basePackage

*/

public AnnotationApplicationContext(String basePackage) {

try {

String packageDirName = basePackage.replaceAll("\\.", "\\\\");

Enumeration dirs =Thread.currentThread().getContextClassLoader().getResources(packageDirName);

while (dirs.hasMoreElements()) {

URL url = dirs.nextElement();

String filePath = URLDecoder.decode(url.getFile(),"utf-8");

rootPath = filePath.substring(0, filePath.length()-packageDirName.length());

loadBean(new File(filePath));

}

} catch (Exception e) {

throw new RuntimeException(e);

}

}

private void loadBean(File fileParent) {

if (fileParent.isDirectory()) {

File[] childrenFiles = fileParent.listFiles();

if(childrenFiles == null || childrenFiles.length == 0){

return;

}

for (File child : childrenFiles) {

if (child.isDirectory()) {

//如果是个文件夹就继续调用该方法,使用了递归

loadBean(child);

} else {

//通过文件路径转变成全类名,第一步把绝对路径部分去掉

String pathWithClass = child.getAbsolutePath().substring(rootPath.length() - 1);

//选中class文件

if (pathWithClass.contains(".class")) {

// com.xinzhi.dao.UserDao

//去掉.class后缀,并且把 \ 替换成 .

String fullName = pathWithClass.replaceAll("\\\\", ".").replace(".class", "");

try {

Class aClass = Class.forName(fullName);

//把非接口的类实例化放在map中

if(!aClass.isInterface()){

Bean annotation = aClass.getAnnotation(Bean.class);

if(annotation != null){

Object instance = aClass.newInstance();

//判断一下有没有接口

if(aClass.getInterfaces().length > 0) {

//如果有接口把接口的class当成key,实例对象当成value

System.out.println("正在加载【"+ aClass.getInterfaces()[0] +"】,实例对象是:" + instance.getClass().getName());

beanFactory.put(aClass.getInterfaces()[0], instance);

}else{

//如果有接口把自己的class当成key,实例对象当成value

System.out.println("正在加载【"+ aClass.getName() +"】,实例对象是:" + instance.getClass().getName());

beanFactory.put(aClass, instance);

}

}

}

} catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {

e.printStackTrace();

}

}

}

}

}

}

}

⑦java类标识Bean注解

@Bean

public class UserServiceImpl implements UserService

@Bean

public class UserDaoImpl implements UserDao

⑧测试Bean加载

package com.atguigu.spring;

import com.atguigu.spring.core.AnnotationApplicationContext;

import com.atguigu.spring.core.ApplicationContext;

import com.atguigu.spring.test.service.UserService;

import org.junit.jupiter.api.Test;

public class SpringIocTest {

@Test

public void testIoc() {

ApplicationContext applicationContext = new AnnotationApplicationContext("com.atguigu.spring.test");

UserService userService = (UserService)applicationContext.getBean(UserService.class);

userService.out();

System.out.println("run success");

}

}

控制台打印测试

⑨依赖注入

只要userDao.print();调用成功,说明就注入成功

package com.atguigu.spring.test.service.impl;

import com.atguigu.spring.core.annotation.Bean;

import com.atguigu.spring.core.annotation.Di;

import com.atguigu.spring.dao.UserDao;

import com.atguigu.spring.service.UserService;

@Bean

public class UserServiceImpl implements UserService {

@Di

private UserDao userDao;

@Override

public void out() {

userDao.print();

System.out.println("Service层执行结束");

}

}

执行第八步:报错了,说明当前userDao是个空对象

⑩依赖注入实现

package com.atguigu.spring.core;

import com.atguigu.spring.core.annotation.Bean;

import com.atguigu.spring.core.annotation.Di;

import java.io.File;

import java.lang.reflect.Field;

import java.util.HashMap;

import java.util.Map;

public class AnnotationApplicationContext implements ApplicationContext {

//存储bean的容器

private HashMap beanFactory = new HashMap<>();

private static String rootPath;

@Override

public Object getBean(Class clazz) {

return beanFactory.get(clazz);

}

/**

* 根据包扫描加载bean

* @param basePackage

*/

public AnnotationApplicationContext(String basePackage) {

try {

String packageDirName = basePackage.replaceAll("\\.", "\\\\");

Enumeration dirs =Thread.currentThread().getContextClassLoader().getResources(packageDirName);

while (dirs.hasMoreElements()) {

URL url = dirs.nextElement();

String filePath = URLDecoder.decode(url.getFile(),"utf-8");

rootPath = filePath.substring(0, filePath.length()-packageDirName.length());

loadBean(new File(filePath));

}

} catch (Exception e) {

throw new RuntimeException(e);

}

//依赖注入

loadDi();

}

private void loadBean(File fileParent) {

if (fileParent.isDirectory()) {

File[] childrenFiles = fileParent.listFiles();

if(childrenFiles == null || childrenFiles.length == 0){

return;

}

for (File child : childrenFiles) {

if (child.isDirectory()) {

//如果是个文件夹就继续调用该方法,使用了递归

loadBean(child);

} else {

//通过文件路径转变成全类名,第一步把绝对路径部分去掉

String pathWithClass = child.getAbsolutePath().substring(rootPath.length() - 1);

//选中class文件

if (pathWithClass.contains(".class")) {

// com.xinzhi.dao.UserDao

//去掉.class后缀,并且把 \ 替换成 .

String fullName = pathWithClass.replaceAll("\\\\", ".").replace(".class", "");

try {

Class aClass = Class.forName(fullName);

//把非接口的类实例化放在map中

if(!aClass.isInterface()){

Bean annotation = aClass.getAnnotation(Bean.class);

if(annotation != null){

Object instance = aClass.newInstance();

//判断一下有没有接口

if(aClass.getInterfaces().length > 0) {

//如果有接口把接口的class当成key,实例对象当成value

System.out.println("正在加载【"+ aClass.getInterfaces()[0] +"】,实例对象是:" + instance.getClass().getName());

beanFactory.put(aClass.getInterfaces()[0], instance);

}else{

//如果有接口把自己的class当成key,实例对象当成value

System.out.println("正在加载【"+ aClass.getName() +"】,实例对象是:" + instance.getClass().getName());

beanFactory.put(aClass, instance);

}

}

}

} catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {

e.printStackTrace();

}

}

}

}

}

}

private void loadDi() {

for(Map.Entry entry : beanFactory.entrySet()){

//就是咱们放在容器的对象

Object obj = entry.getValue();

Class aClass = obj.getClass();

Field[] declaredFields = aClass.getDeclaredFields();

for (Field field : declaredFields){

Di annotation = 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 (IllegalAccessException e) {

e.printStackTrace();

}

}

}

}

}

}

执行第八步:执行成功,依赖注入成功

参考阅读

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: