我们都知道,Spring框架的IOC是基于Java反射机制实现的,下面我们先回顾一下java反射。
回顾Java反射
Java
反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为Java
语言的反射机制。简单来说,反射机制指的是程序在运行时能够获取自身的信息。
要想解剖一个类,必须先要获取到该类的Class对象。而剖析一个类或用反射解决具体的问题就是使用相关API (1)java.lang.Class(2)java.lang.reflect,所以,Class对象是反射的根源。
自定义类
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 + '\'' +
'}';
}
}
测试类
public class TestCar {
/**
* //1、获取Class对象多种方式
*
* @throws ClassNotFoundException
* @throws NoSuchMethodException
* @throws InvocationTargetException
* @throws InstantiationException
* @throws IllegalAccessException
*/
@Test
public void test01() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//1 类名.class
Class clazz1 = Car.class;
//2 对象.getClass()
Class clazz2 = new Car().getClass();
//3 Class.forName("全路径")
Class clazz3 = Class.forName("com.zhubayi.reflect.Car");
//实例化
Car car = (Car) clazz3.getConstructor().newInstance();
System.out.println(car);
}
/**
* 获取构造方法
*/
@Test
public void test02() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class<Car> 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);
}
}
}
}
实现Spring的IoC
我们知道,IoC(控制反转)和DI(依赖注入)是Spring里面核心的东西,那么,我们如何自己手写出这样的代码呢?下面我们就一步一步写出Spring框架最核心的部分。
添加依赖
<dependencies>
<!--junit5测试-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.3.1</version>
</dependency>
</dependencies>
定义注解,我们通过注解的形式加载bean与实现依赖注入
bean注解
package com.zhubayi.spring.core.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author zhubayi
* @Description
*/
@Target(ElementType.TYPE)//class interface
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {
}
依赖注入注解
package com.zhubayi.spring.core.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author zhubayi
* @Description
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Di {
}
说明:上面两个注解可以随意取名
准备测试需要的bean
创建UserDao接口
package com.zhubayi.spring6.test.dao;
/**
* @author zhubayi
* @Description
*/
public interface UserDao {
void print();
}
创建UserDaoImpl实现
package com.zhubayi.spring6.test.dao.impl;
import com.zhubayi.spring.core.annotation.Bean;
import com.zhubayi.spring6.test.dao.UserDao;
/**
* @author zhubayi
* @Description
*/
@Bean
public class UserDaoImpl implements UserDao {
@Override
public void print() {
System.out.println("Dao方法执行成功!");
}
}
创建UserService接口
package com.zhubayi.spring6.test.service;
/**
* @author zhubayi
* @Description
*/
public interface UserService {
void out();
}
创建UserServiceImpl实现类
package com.zhubayi.spring6.test.service.impl;
import com.zhubayi.spring.core.annotation.Bean;
import com.zhubayi.spring.core.annotation.Di;
import com.zhubayi.spring6.test.dao.UserDao;
import com.zhubayi.spring6.test.service.UserService;
/**
* @author zhubayi
* @Description
*/
@Bean
public class UserServiceImpl implements UserService {
@Override
public void out() {
System.out.println("Service层执行结束");
}
}
定义bean容器接口
package com.zhubayi.spring.core;
/**
* @author zhubayi
* @Description
*/
public interface ApplicationContext {
/**
* 得到bean
*
* @param clazz clazz
* @return {@link Object}
*/
Object getBean(Class clazz);
}
编写注解bean容器接口实现
AnnotationApplicationContext基于注解扫描bean
package com.zhubayi.spring.core;
import java.util.HashMap;
public class AnnotationApplicationContext implements ApplicationContext {
//存储bean的容器
private HashMap<Class, Object> 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.zhuabyi.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<Class, Object> 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<URL> 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
测试
package com.zhubayi.spring;
import com.zhubayi.spring.core.AnnotationApplicationContext;
import com.zhubayi.spring.core.ApplicationContext;
import com.zhubayi.spring6.test.service.UserService;
import org.junit.jupiter.api.Test;
/**
* @author zhubayi
* @Description
*/
public class SpringIcoTest {
@Test
public void testIoc() {
ApplicationContext applicationContext = new AnnotationApplicationContext("com.zhubayi.spring6.test");
UserService userService = (UserService)applicationContext.getBean(UserService.class);
userService.out();
System.out.println("run success");
}
}
结果
正在加载【interface com.zhubayi.spring6.test.dao.UserDao】,实例对象是:com.zhubayi.spring6.test.dao.impl.UserDaoImpl
正在加载【interface com.zhubayi.spring6.test.service.UserService】,实例对象是:com.zhubayi.spring6.test.service.impl.UserServiceImpl
Service层执行结束
run success
依赖注入
只要userDao.print();调用成功,说明就注入成功
package com.zhubayi.spring6.test.service.impl;
import com.zhubayi.spring.core.annotation.Bean;
import com.zhubayi.spring.core.annotation.Di;
import com.zhubayi.spring6.test.dao.UserDao;
import com.zhubayi.spring6.test.service.UserService;
/**
* @author zhubayi
* @Description
*/
@Bean
public class UserServiceImpl implements UserService {
@Di
private UserDao userDao;
@Override
public void out() {
userDao.print();
System.out.println("Service层执行结束");
}
}
执行以下代码
@Test
public void testDi() {
ApplicationContext applicationContext = new AnnotationApplicationContext("com.zhubayi.spring6.test");
UserService userService = (UserService)applicationContext.getBean(UserService.class);
userService.out();
System.out.println("run success");
}
执行报错
正在加载【interface com.zhubayi.spring6.test.dao.UserDao】,实例对象是:com.zhubayi.spring6.test.dao.impl.UserDaoImpl
正在加载【interface com.zhubayi.spring6.test.service.UserService】,实例对象是:com.zhubayi.spring6.test.service.impl.UserServiceImpl
java.lang.NullPointerException: Cannot invoke "com.zhubayi.spring6.test.dao.UserDao.print()" because "this.userDao" is null
at com.zhubayi.spring6.test.service.impl.UserServiceImpl.out(UserServiceImpl.java:18)
at com.zhubayi.spring.SpringIcoTest.testDi(SpringIcoTest.java:24)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:515)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:115)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:171)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:167)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:114)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:59)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:105)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:95)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:71)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:110)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:95)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:71)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:110)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:95)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:71)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:220)
at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$6(DefaultLauncher.java:188)
at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:202)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:181)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:128)
at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:57)
at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
说明当前userDao是个空对象
依赖注入实现
package com.zhubayi.spring.core;
import com.zhubayi.spring.core.annotation.Bean;
import com.zhubayi.spring.core.annotation.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;
/**
* @author zhubayi
* @Description
*/
public class AnnotationApplicationContext implements ApplicationContext {
//存储bean的容器
private Map<Class, Object> beanFactory = new HashMap<>();
private static String rootPath;
/**
* 得到bean
*
* @param clazz clazz
* @return {@link Object}
*/
@Override
public Object getBean(Class clazz) {
return beanFactory.get(clazz);
}
/**
* 根据包扫描加载bean
*
* @param basePackage
*/
public AnnotationApplicationContext(String basePackage) {
try {
//就是把.替换成\\
String packageDirName = basePackage.replaceAll("\\.", "\\\\");
Enumeration<URL> 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) {
e.printStackTrace();
}
//依赖注入
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);
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 | InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
}
private void loadDi() {
for (Map.Entry<Class, Object> entry : beanFactory.entrySet()) {
Object obj = entry.getValue();
Class<?> aClass = obj.getClass();
Field[] fields = aClass.getDeclaredFields();
for (Field field : fields) {
Di di = field.getAnnotation(Di.class);
if (di != 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) {
throw new RuntimeException(e);
}
}
}
}
}
}
再次执行testDi 方法
正在加载【interface com.zhubayi.spring6.test.dao.UserDao】,实例对象是:com.zhubayi.spring6.test.dao.impl.UserDaoImpl
正在加载【interface com.zhubayi.spring6.test.service.UserService】,实例对象是:com.zhubayi.spring6.test.service.impl.UserServiceImpl
正在给【com.zhubayi.spring6.test.service.impl.UserServiceImpl】属性【userDao】注入值【com.zhubayi.spring6.test.dao.impl.UserDaoImpl】
Dao方法执行成功!
Service层执行结束
run success
执行成功,依赖注入成功