本文完整资源包,懒得一步步操作的同学可以移步下载:
CSDN资源-手写IOC和DI完整代码下载
我们都知道,Spring框架的IOC是基于Java的反射机制实现的,下面我们先回顾一下Java反射:
Java反射机制是在运行状态中,对于任意类,都能够知道这个类的属性和方法;对于任何一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象的方式称为Java语言的反射机制。简单来说,反射机制指的是程序在运行时能够获取自身的信息。
想要解剖一个类,必须先要获取到该类的Class对象。而剖析一个类或用反射解决具体的问题就是使用相关应用程序接口(1)java.lang.Class
(2)java.lang.reflect
,所以,Class对象是反射的根源。
User类中包括三个私有属性、set get方法、一个无参构造方法、一个有参构造方法和一个私有普通方法
run
package user;
/**
* @author Sean Zhang
* @date 2023/9/20 23:37
*/
public class User {
private int id;
private String name;
private int age;
public User() {
}
public User(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
private void run() {
System.out.println("私有方法run...");
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
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;
}
}
UserTest主要测试一下问题:
- 获取Class的多种方式
- 获取构造方法
- 获取属性
- 获取方法
package user;
import org.junit.jupiter.api.Test;
/**
* @author Sean Zhang
* @date 2023/9/20 23:45
*/
public class UserTest {
// 1. 获取Class的多种方式
@Test
public void testClass() {
}
// 2. 获取构造方法
@Test
public void testConstructor() {
}
// 3. 获取属性
@Test
public void testProperty() {
}
// 4. 获取方法
@Test
public void testMethod() {
}
}
测试1:获取Class的多种方式testClass
:
获取Class的三种方式
类名.class
对象.getClass()
Class.forName("全路径")
public void testClass() throws ClassNotFoundException {
// 1. `类名.class`
Class<User> userClass1 = User.class;
// 2. `对象.getClass()`
Class<? extends User> userClass2 = new User().getClass();
// 3. `Class.forName("全路径")`
Class<?> userClass3 = Class.forName("user.User");
}
注:获取Class对象之后,对对象实例化:
User user = userClass1.getDeclaredConstructor().newInstance();
测试2:获取构造方法testConstructor
:
获取构造方法testConstructor步骤
- 获取Class
- 获取所有构造方法
public void testConstructor() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class<User> userClass = User.class;
Constructor<?>[] constructors = userClass.getDeclaredConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println("方法名称:" + constructor.getName() + "参数个数:" + constructor.getParameterCount());
}
Constructor<?>[] publicConstructors = userClass.getConstructors();
//构造创建对象
Constructor<User> constructor = userClass.getDeclaredConstructor(int.class, String.class, int.class);
//私有的要设置允许
constructor.setAccessible(true);
User user = constructor.newInstance(1, "张三", 18);
}
注:仅仅获得public
构造方法:
Constructor<?>[] publicConstructors = userClass.getConstructors();
测试3:获取属性testProperty
:
public void testProperty() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class<User> userClass = User.class;
User user = userClass.getDeclaredConstructor().newInstance();
Field[] declaredFields = userClass.getDeclaredFields();
for (Field field : declaredFields) {
if (field.getName().equals("name")) {
//私有的属性要设置允许
field.setAccessible(true);
field.set(user, "李四");
}
System.out.println(field.getName());
}
}
注:私有的属性要设置允许访问:
field.setAccessible(true);
测试4:获取方法testMethod
:
public void testMethod() throws InvocationTargetException, IllegalAccessException {
User user = new User(2, "王五", 20);
Class<? extends User> clazz = user.getClass();
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
if (method.getName().equals("run")) {
method.setAccessible(true);
Object invoke = method.invoke(user);
System.out.println("run执行了" + invoke);
}
}
}
注:私有的方法要设置允许访问:
method.setAccessible(true);
我们知道,IOC和DI是Spring里面最核心的东西,,下面我们一步步写出这两个模块。
步骤:
- 创建测试类service和dao
- 手写两个注解:
@Bean
创建对象@DI
属性注入- 创建Bean容器接口ApplicationContext定义方法,返回对象
- 实现bean容器接口
a. 返回对象
b. 根据包规则加载bean
步骤1:手写两个注解:@Bean
创建对象 @DI
属性注入:
package com.hand.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.hand.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 {
}
步骤2:创建测试类service和dao:
service和dao类上面标注注解
@Bean
,具体@Bean实现控制反转后面在ApplicationContext处理
service对dao的依赖要标注注解@DI
,具体@DI实现依赖注入后面在ApplicationContext处理
dao:
package com.hand.dao;
public interface UserDao {
void add();
}
package com.hand.dao.impl;
import com.hand.MyLog;
import com.hand.anno.Bean;
import com.hand.dao.UserDao;
@Bean
public class UserDaoImpl implements UserDao {
@Override
public void add() {
MyLog.logger.info("dao add ...");
}
}
service:
package com.hand.service;
public interface UserService {
void add();
}
package com.hand.service.impl;
import com.hand.MyLog;
import com.hand.anno.Bean;
import com.hand.anno.Di;
import com.hand.dao.UserDao;
import com.hand.service.UserService;
@Bean
public class UserServiceImpl implements UserService {
@Di
private UserDao userDao;
@Override
public void add() {
MyLog.logger.info("service add ...");
userDao.add();
}
}
步骤3:创建接口类ApplicationContext及其实现类AnnoApplicationContext:
主要是使用反射进行对象创建和依赖注入
package com.hand.bean;
public interface ApplicationContext {
Object getBean(Class clazz);
}
package com.hand.bean;
import com.hand.MyLog;
import com.hand.anno.Bean;
import com.hand.anno.Di;
import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
public class AnnoApplicationContext implements ApplicationContext {
private String rootPath = null;
private Map<Class, Object> beanFactory = new HashMap<>();
@Override
public Object getBean(Class clazz) {
return beanFactory.get(clazz);
}
public AnnoApplicationContext(String packageStr) {
try {
String packagePath = packageStr.replaceAll("\\.", "\\\\");
Enumeration<URL> resources = Thread.currentThread().getContextClassLoader().getResources(packagePath);
while (resources.hasMoreElements()) {
URL url = resources.nextElement();
String filePath = URLDecoder.decode(url.getFile(), "utf-8");
// MyLog.logger.info(filePath);
rootPath = filePath.substring(0, filePath.length() - packagePath.length());
loadBean(new File(filePath));
}
} catch (Exception e) {
e.printStackTrace();
}
loadDi();
}
/**
* 对象加入容器
*/
private void loadBean(File file) throws Exception {
if (file.isDirectory()) {
File[] childFiles = file.listFiles();
if (childFiles == null || childFiles.length == 0) {
return;
}
for (File childFile : childFiles) {
if (childFile.isDirectory()) {
loadBean(childFile);
continue;
}
String pathWhitClass = childFile.getAbsolutePath().substring(rootPath.length() - 1);
if (!pathWhitClass.contains(".class")) {
continue;
}
String allName = pathWhitClass.replaceAll("\\\\", ".").replace(".class", "");
MyLog.logger.info("类:" + allName);
Class<?> clazz = Class.forName(allName);
if (clazz.isInterface()) {
continue;
}
Bean bean = clazz.getAnnotation(Bean.class);
if (bean != null) {
Object instance = clazz.getConstructor().newInstance();
if (clazz.getInterfaces().length > 0) {
beanFactory.put(clazz.getInterfaces()[0], instance);
} else {
beanFactory.put(clazz, instance);
}
}
}
}
}
/**
* 依赖注入
*/
private void loadDi() {
for (Object obj : beanFactory.values()) {
Class<?> clazz = obj.getClass();
Field[] declaredFields = clazz.getDeclaredFields();
for (Field field : declaredFields) {
Di di = field.getAnnotation(Di.class);
if (di == null) {
continue;
}
field.setAccessible(true);
try {
field.set(obj, beanFactory.get(field.getType()));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
步骤4:测试:
package com.hand;
import com.hand.bean.AnnoApplicationContext;
import com.hand.service.UserService;
public class Ioc {
public static void main(String[] args) {
AnnoApplicationContext context = new AnnoApplicationContext("com.hand");
UserService userService = (UserService) context.getBean(UserService.class);
userService.add();
}
}