第四章、原理—手写Ioc
1、回顾反射
1、内容
1、反射机制是在运行状态中,对于任意一个类,都能知道这个类的属性和方法;对于任意一个对象,都能够调用它的任意方法和属性。
2、这种动态获取信息以及动态调用方法的功能就是java反射
3、简单来说,反射机制是指程序在运行时能够获取自身的信息。
4、要解刨一个类,必须要获取到该类的Class对象,而解刨一个类或用反射解决具体问题就是使用相关API
5、java.lang.Class
6、java.lang.reflect
7、Class是反射的根源
2、代码通过反射操作
1、获取class对象的多种获取方式
2、修改和设置class对象的构造方法
3、获取和修改对象的方法
1、注意
1、getDeclaredConstructors()方法:是用来获取所有private的构造方法
2、getConstructors()方法:是用来获取所有的public的构造方法
2、代码实现
1、Car.java
package com.atqt.reflect;
public class Car {
private String name;
private int age;
private String color;
// 普通方法
private void run(){
System.out.println("私有方法run.....");
}
public Car() {
}
public Car(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
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;
}
}
2、TestCar.java
package com.atqt.reflect;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class TestCar {
// 获取class对象多种方式
@Test
public void test01() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
// 类名.class
Class<Car>clazz1 = Car.class;
// 对象.getClass()
Class clazz2 = new Car().getClass();
// Class.forName("全路径类名")
Class clazz3 = Class.forName("com.atqt.reflect.Car");
// 实例化对象
Car car = (Car)clazz3.getDeclaredConstructor().newInstance();
System.out.println(car);
}
// 获取构造方法
@Test
public void test02() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class clazz = Car.class;
//// 获取所有的构造
// Constructor[] constructors = clazz.getConstructors();
// 获取私有的构造和方法
Constructor[] constructors = clazz.getDeclaredConstructors();
for(Constructor c :constructors){
System.out.println("构造方法的名称:"+c.getName()+"参数的格式:"+c.getParameterCount());
}
// 指定有参构造创建对象
//// 1、构造公共的
// Constructor c1 = clazz.getConstructor(String.class,int.class,String.class);
// Car car1 = (Car) c1.newInstance("坦克",20,"绿色");
// System.out.println(car1);
// 2、构造私有的
Constructor c2 = clazz.getDeclaredConstructor(String.class,int.class,String.class);
c2.setAccessible(true);
Car car2 = (Car) c2.newInstance("坦克300",200,"绿色plus");
System.out.println(car2);
}
// 获取属性
@Test
public void test03() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class clazz = Car.class;
Car car = (Car) clazz.getDeclaredConstructor().newInstance();
// 获取所有的public属性
Field[] fields=clazz.getFields();
// 获取所有属性包含私有属性
Field[] fields1 = clazz.getDeclaredFields();
for (Field f :fields1){
// 在属性中设置一个值,在name设置一个值
if(f.getName().equals("name")){
// 设置运行进行访问
f.setAccessible(true);
f.set(car,"好好好");
}
System.out.println(f.getName());
System.out.println(car);
}
}
// 获取方法
@Test
public void test04() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Car car = new Car("牧马人",10,"黑色");
Class clazz = car.getClass();
// 操作public方法
Method[] methods = clazz.getMethods();
for (Method m :methods){
// System.out.println(m.getName());
// 执行方法
if (m.getName().equals("toString")){
String invoke = (String) m.invoke(car);
System.out.println(invoke);
}
}
// 操作private方法
Method[] methods1 = clazz.getDeclaredMethods();
for (Method m:methods1){
// 执行run方法
if(m.getName().equals("run")){
m.setAccessible(true);
m.invoke(car);
}
}
}
}
2、实现Spring的Ioc

1、搭建子模块
1、搭建其他模块,就是创建其他项目
2、创建测试类Service、dao
1、UserService.java
package com.atqt.service;
public interface UserService {
void add();
}
2、UserServiceImpl.java
package com.atqt.service.impl;
import com.atqt.anno.Bean;
import com.atqt.anno.Di;
import com.atqt.dao.UserDao;
import com.atqt.service.UserService;
@Bean
public class UserServiceImpl implements UserService {
@Di
private UserDao userDao;
@Override
public void add() {
System.out.println("service......");
}
}
3、UserDao.java
package com.atqt.dao;
public interface UserDao {
void add();
}
4、UserDaoImpl.java
package com.atqt.dao.imp;
import com.atqt.anno.Bean;
import com.atqt.dao.UserDao;
@Bean
public class UserDaoImpl implements UserDao {
@Override
public void add() {
System.out.println("dao ......");
}
}
3、创建两个注解bean和Di
1、bean注解
package com.atqt.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 {
}
2、Di注解(依赖注入注解)
package com.atqt.anno;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//定义限制注解的类型
@Target({ElementType.FIELD, ElementType.TYPE})
//定义限制注解的使用情况
@Retention(RetentionPolicy.RUNTIME)
public @interface Di {
}
4、创建bean容器接口ApplicationContext定义方法,返回对象
1、创建ApplicationContext接口
package com.atqt.bean;
public interface ApplicationContext {
Object getBean(Class clazz);
}
2、ApplicationContext方法的实现
package com.atqt.bean;
import com.atqt.anno.Bean;
import com.atqt.anno.Di;
import java.io.File;
import java.io.IOException;
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;
import java.util.Set;
public class AnnotationApplicationContext implements ApplicationContext{
// 创建一个Map集合,放在bean对象
private Map<Class,Object> beanFactory = new HashMap<>();
// 成员根路径rootPath
public static String rootPath;
// 返回创建的对象
@Override
public Object getBean(Class clazz) {
return beanFactory.get(clazz);
}
// 设置包的扫描规则
// 当前包及其子包,哪个类有@Bean注解,就把这个类通过反射实例化
// 创建有参数的构造,传递包的路径
public AnnotationApplicationContext(String basePackage) {
// public static void ann(String basePackage){
//com.atqt
// 把点替换为\
String packagePath = basePackage.replaceAll("\\.","\\\\");
// 获取包的绝对路径
try {
Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(packagePath);
// 遍历得到路径
while(urls.hasMoreElements()){
URL url = urls.nextElement();
// 因为转义了,所以要转回去
String filePath = URLDecoder.decode(url.getFile(),"utf-8");
// System.out.println(filePath);
// 获取包前面路径部分,字符串截取
rootPath =filePath.substring(0,filePath.length()-packagePath.length());
// 已经得到路径了,开始进行包的扫描
loadBean(new File(filePath));
}
} catch (Exception e) {
throw new RuntimeException(e);
}
// 属性注入
loadDi();
}
// 包扫描的过程,方法,bean实例化
private void loadBean(File file) throws Exception {
// 1、判断是否是个文件夹
if(file.isDirectory()){
// 2、获取文件夹中的所有内容(类、文件夹等)
File[] childFiles = file.listFiles();
// 3、判断文件夹内容为null,直接返回,(没有类)
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 pathWithClass =child.getAbsolutePath().substring(rootPath.length()-1);
// 4.4、当前文件的类型是否是.class文件
if(pathWithClass.contains(".class")){
// 4.5、如果是.class类型,把路径的\替换成. 把.class去掉
// com.atqt.service.UserServiceImpl
String allName = pathWithClass.replaceAll("\\\\",".").replace(".class","");
// 4.6、使用返回判断类上面是否有 @Bean注解,如果有就进行实例化过程
//4.6.1、获取类的class对象
Class<?> clazz = Class.forName(allName);
//4.6.2、判断不是接口
if(!clazz.isInterface()){
//4.6.3、判断类上面是否有注解@Bean
Bean annotation = clazz.getAnnotation(Bean.class);
if(annotation !=null){
//4.6.4、实例化
Object instance =clazz.getConstructor().newInstance();
// 4.7、把对象实例化后,放到map集合中
//4.7.1、判断当前类如果有接口,让接口class作为map的key
if(clazz.getInterfaces().length>0){
beanFactory.put(clazz.getInterfaces()[0],instance);
}else {
beanFactory.put(clazz,instance);
}
}
}
}
}
}
}
}
/**
* 属性的注入
*/
private void loadDi() {
// 1、实例化对象在beanFactory的map集合中
// 1、遍历beanFactory的map集合
Set<Map.Entry<Class,Object>> entries = beanFactory.entrySet();
for (Map.Entry<Class,Object> entry:entries){
// 2、获取map集合中的每个对象,把每个对象中的属性获取到
Object obj = entry.getValue();
//获取Class对象
Class clazz = obj.getClass();
//获取每个对象属性
Field[] declaredFields = clazz.getDeclaredFields();
// 3、遍历得到每个对象属性数组,得到每个属性
for(Field field:declaredFields){
// 4、判断属性上面是否有@Di注解
Di annotatio = field.getAnnotation(Di.class);
if(annotatio!=null){
//如果有私有属性,可以设置值
field.setAccessible(true);
// 5、如果有@DI注解,把对象进行设置(注入)
try {
field.set(obj,beanFactory.get(field.getType()));
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
}
}
}
5、进行测试
1、进行注释,并且实现测试类TestUser.java
package com.atqt;
import com.atqt.bean.AnnotationApplicationContext;
import com.atqt.bean.ApplicationContext;
import com.atqt.service.UserService;
import org.junit.jupiter.api.Test;
public class TestUser {
public static void main(String[] args) {
ApplicationContext context = new AnnotationApplicationContext("com.atqt");
UserService userService = (UserService) context.getBean(UserService.class);
System.out.println(userService);
userService.add();
}
}