学习材料
尚硅谷Spring零基础入门到进阶,一套搞定spring6全套视频教程(源码级讲解)
有关反射的知识回顾
IoC是基于反射机制实现的。
Java
反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为Java
语言的反射机制。简单来说,反射机制指的是程序在运行时能够获取自身的信息。
要想解剖一个类,必须先要获取到该类的Class对象。而剖析一个类或用反射解决具体的问题就是使用相关API(1)java.lang.Class(2)java.lang.reflect,所以,Class对象是反射的根源。
具体要掌握的操作就是如何获取class(三种方法),如何获取构造器及创建对象,如何获取属性,如何获取方法并执行。(这里注意public和private的不同操作)。
package com.zy.reflect;
import org.testng.annotations.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class TestCar {
@Test
public void test01() throws Exception {
//获取class的方法
// Car car = new Car();
// Class clazz1 = car.getClass();
Class clazz2 = Car.class;
Class clazz3 = Class.forName("com.zy.reflect.Car");
Constructor[] constructors = clazz3.getDeclaredConstructors();
for(Constructor con:constructors){
if(con.getParameterCount()==3){
con.setAccessible(true);
Car car = (Car) con.newInstance("奔驰", 1, "黑色");
// System.out.println(car);
}
}
//或者直接获取
Constructor c2 = clazz3.getDeclaredConstructor(String.class, Integer.class, String.class);
c2.setAccessible(true);
Car car2 = (Car)c2.newInstance("捷达", 15, "白色");
System.out.println(car2);
}
@Test
public void test02() throws Exception {
Class clazz3 = Class.forName("com.zy.reflect.Car");
Car car = (Car)clazz3.getConstructor().newInstance();
Field[] fields = clazz3.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());
}
}
@Test
public void test03() throws Exception {
Class clazz3 = Class.forName("com.zy.reflect.Car");
Car car = (Car)clazz3.getConstructor().newInstance();
Method[] methods = clazz3.getDeclaredMethods();
for (Method method:methods) {
//执行方法 toString
if(method.getName().equals("toString")) {
String invoke = (String)method.invoke(car);
System.out.println("toString执行了:"+invoke);
}
if(method.getName().equals("run")) {
//设置允许访问
method.setAccessible(true);
method.invoke(car);//这里需要有对应的对象。
System.out.println(car);
}
}
}
}
手写IoC
实现的思路设计
环境框架
第一二步手打过很多次了,没什么难度。
第三步:
创建Annotation文件,与接口之类的类似。不同在于有元注解需要考虑(注解面向的范围)Bean是创建类,Di注入
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {
}
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Di {
}
第四步:要了解这一步要干嘛,需要从需求出发。
需求如下:即输入一个com.zy。我要组织得到一个Map集合,创建好所有标注注解Bean的类,与Di注入。getBean用于利用class信息返回Map内的元素。
public class TestUser {
public static void main(String[] args) {
ApplicationContext context = new AnnotationApplicationContext("com.zy");
UserService bean = (UserService)context.getBean(UserService.class);
bean.add();
}
}
如何实现创建Map(主要学习的是如何遍历文件,如何扫描注解)
private Map<Class, Object> beanFactory = new HashMap<>();
private static String rootPath;
@Override
public Object getBean(Class clazz) {
return beanFactory.get(clazz);
}
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");
// System.out.println(filePath);
//包扫描
rootPath = filePath.substring(0, filePath.length()-packageDirName.length());
loadBean(new File(filePath));
}
} catch (Exception e) {
throw new RuntimeException(e);
}
//属性注入
loadDi();
}
private void loadBean(File file) throws Exception {
//1.当前FIle是否是文件夹
if (file.isDirectory()){
//2.是文件夹的话,获取所有内容
File[] childrenFiles = file.listFiles();
//3.判断内容是否为空
if (childrenFiles == null || childrenFiles.length==0){
return;
}
//4.遍历所有内容
for (File child:childrenFiles){
//1.还是文件夹就递归
if (child.isDirectory()){
loadBean(child);
}else {
//是文件,获取完整的类路径
String pathWithClass = child.getAbsolutePath().substring(rootPath.length()-1);
//是不是class文件
if (pathWithClass.contains(".class")){
String allName = pathWithClass.replaceAll("\\\\", "\\.").replace(".class", "");
//判断类中有没有@Bean注解
Class<?> clazz = Class.forName(allName);
if (!clazz.isInterface()){
Bean annotation = (Bean)clazz.getAnnotation(Bean.class);
if (annotation!=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(){
//实例化对象在beanFactory的map
//1 遍历beanFactory的map
Set<Map.Entry<Class, Object>> entries = beanFactory.entrySet();
for(Map.Entry<Class, Object> entry:entries){
//2 获取map的每个对象value,每个对象属性获取到
Object obj = entry.getValue();
Class clazz = obj.getClass();
Field[] fields = clazz.getDeclaredFields();
//3 遍历得到每个对象属性数组,得到每个属性
for(Field field:fields){
//4 判断属性上面是都有@Di注解
Di annotation = field.getAnnotation(Di.class);
//5 如果有@Di注解,把对象进行设置(注入)
if (annotation!=null){
field.setAccessible(true);
//这一步什么??filed这个属性的类型field.getType() beanFactory.get()得到对应的对象。
try {
field.set(obj, beanFactory.get(field.getType()));
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
}