我們都知道,Spring框架的IOC是基于Java反射機(jī)制實(shí)現(xiàn)的,下面我們先回顧一下java反射。
1、回顧Java反射
Java
反射機(jī)制是在運(yùn)行狀態(tài)中,對于任意一個(gè)類,都能夠知道這個(gè)類的所有屬性和方法;對于任意一個(gè)對象,都能夠調(diào)用它的任意方法和屬性;這種動態(tài)獲取信息以及動態(tài)調(diào)用對象方法的功能稱為Java
語言的反射機(jī)制。簡單來說,反射機(jī)制指的是程序在運(yùn)行時(shí)能夠獲取自身的信息。
要想解剖一個(gè)類,必須先要獲取到該類的Class對象。而剖析一個(gè)類或用反射解決具體的問題就是使用相關(guān)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;
//無參數(shù)構(gòu)造
public Car() {
}
//有參數(shù)構(gòu)造
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");
//實(shí)例化
Car car = (Car)clazz3.getConstructor().newInstance();
System.out.println(car);
}
//2、獲取構(gòu)造方法
@Test
public void test02() throws Exception {
Class clazz = Car.class;
//獲取所有構(gòu)造
// getConstructors()獲取所有public的構(gòu)造方法
// Constructor[] constructors = clazz.getConstructors();
// getDeclaredConstructors()獲取所有的構(gòu)造方法public private
Constructor[] constructors = clazz.getDeclaredConstructors();
for (Constructor c:constructors) {
System.out.println("方法名稱:"+c.getName()+" 參數(shù)個(gè)數(shù):"+c.getParameterCount());
}
//指定有參數(shù)構(gòu)造創(chuàng)建對象
//1 構(gòu)造public
// Constructor c1 = clazz.getConstructor(String.class, int.class, String.class);
// Car car1 = (Car)c1.newInstance("夏利", 10, "紅色");
// System.out.println(car1);
//2 構(gòu)造private
Constructor c2 = clazz.getDeclaredConstructor(String.class, int.class, String.class);
c2.setAccessible(true);
Car car2 = (Car)c2.newInstance("捷達(dá)", 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")) {
//設(shè)置允許訪問
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());
//執(zhí)行方法 toString
if(m1.getName().equals("toString")) {
String invoke = (String)m1.invoke(car);
//System.out.println("toString執(zhí)行了:"+invoke);
}
}
//2 private方法
Method[] methodsAll = clazz.getDeclaredMethods();
for (Method m:methodsAll) {
//執(zhí)行方法 run
if(m.getName().equals("run")) {
m.setAccessible(true);
m.invoke(car);
}
}
}
}
2、實(shí)現(xiàn)Spring的IoC
我們知道,IoC(控制反轉(zhuǎn))和DI(依賴注入)是Spring里面核心的東西,那么,我們?nèi)绾巫约菏謱懗鲞@樣的代碼呢?下面我們就一步一步寫出Spring框架最核心的部分。
①搭建子模塊
搭建模塊:guigu-spring,搭建方式如其他spring子模塊
②準(zhǔn)備測試需要的bean
添加依賴
<dependencies>
<!--junit5測試-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.3.1</version>
</dependency>
</dependencies>
創(chuàng)建UserDao接口
package com.atguigu.spring6.test.dao;
public interface UserDao {
public void print();
}
創(chuàng)建UserDaoImpl實(shí)現(xiàn)
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層執(zhí)行結(jié)束");
}
}
創(chuàng)建UserService接口
package com.atguigu.spring6.test.service;
public interface UserService {
public void out();
}
創(chuàng)建UserServiceImpl實(shí)現(xiàn)類
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層執(zhí)行結(jié)束");
}
}
③定義注解
我們通過注解的形式加載bean與實(shí)現(xiàn)依賴注入
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 {
}
說明:上面兩個(gè)注解可以隨意取名
④定義bean容器接口
package com.atguigu.spring.core;
public interface ApplicationContext {
Object getBean(Class clazz);
}
⑤編寫注解bean容器接口實(shí)現(xiàn)
AnnotationApplicationContext基于注解掃描bean
package com.atguigu.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);
}
/**
* 根據(jù)包掃描加載bean
* @param basePackage
*/
public AnnotationApplicationContext(String basePackage) {
}
}
⑥編寫掃描bean邏輯
我們通過構(gòu)造方法傳入包的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<Class, Object> beanFactory = new HashMap<>();
private static String rootPath;
@Override
public Object getBean(Class clazz) {
return beanFactory.get(clazz);
}
/**
* 根據(jù)包掃描加載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()) {
//如果是個(gè)文件夾就繼續(xù)調(diào)用該方法,使用了遞歸
loadBean(child);
} else {
//通過文件路徑轉(zhuǎn)變成全類名,第一步把絕對路徑部分去掉
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);
//把非接口的類實(shí)例化放在map中
if(!aClass.isInterface()){
Bean annotation = aClass.getAnnotation(Bean.class);
if(annotation != null){
Object instance = aClass.newInstance();
//判斷一下有沒有接口
if(aClass.getInterfaces().length > 0) {
//如果有接口把接口的class當(dāng)成key,實(shí)例對象當(dāng)成value
System.out.println("正在加載【"+ aClass.getInterfaces()[0] +"】,實(shí)例對象是:" + instance.getClass().getName());
beanFactory.put(aClass.getInterfaces()[0], instance);
}else{
//如果有接口把自己的class當(dāng)成key,實(shí)例對象當(dāng)成value
System.out.println("正在加載【"+ aClass.getName() +"】,實(shí)例對象是:" + instance.getClass().getName());
beanFactory.put(aClass, instance);
}
}
}
} catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
e.printStackTrace();
}
}
}
}
}
}
}
⑦java類標(biāo)識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();調(diào)用成功,說明就注入成功
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層執(zhí)行結(jié)束");
}
}
執(zhí)行第八步:報(bào)錯(cuò)了,說明當(dāng)前userDao是個(gè)空對象
⑩依賴注入實(shí)現(xiàn)文章來源:http://www.zghlxwxcb.cn/news/detail-713617.html
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<Class, Object> beanFactory = new HashMap<>();
private static String rootPath;
@Override
public Object getBean(Class clazz) {
return beanFactory.get(clazz);
}
/**
* 根據(jù)包掃描加載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);
}
//依賴注入
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()) {
//如果是個(gè)文件夾就繼續(xù)調(diào)用該方法,使用了遞歸
loadBean(child);
} else {
//通過文件路徑轉(zhuǎn)變成全類名,第一步把絕對路徑部分去掉
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);
//把非接口的類實(shí)例化放在map中
if(!aClass.isInterface()){
Bean annotation = aClass.getAnnotation(Bean.class);
if(annotation != null){
Object instance = aClass.newInstance();
//判斷一下有沒有接口
if(aClass.getInterfaces().length > 0) {
//如果有接口把接口的class當(dāng)成key,實(shí)例對象當(dāng)成value
System.out.println("正在加載【"+ aClass.getInterfaces()[0] +"】,實(shí)例對象是:" + instance.getClass().getName());
beanFactory.put(aClass.getInterfaces()[0], instance);
}else{
//如果有接口把自己的class當(dāng)成key,實(shí)例對象當(dāng)成value
System.out.println("正在加載【"+ aClass.getName() +"】,實(shí)例對象是:" + instance.getClass().getName());
beanFactory.put(aClass, instance);
}
}
}
} catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
e.printStackTrace();
}
}
}
}
}
}
private void loadDi() {
for(Map.Entry<Class,Object> 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();
}
}
}
}
}
}
執(zhí)行第八步:執(zhí)行成功,依賴注入成功文章來源地址http://www.zghlxwxcb.cn/news/detail-713617.html
到了這里,關(guān)于spring6-實(shí)現(xiàn)簡易版IOC容器的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!