IoC(Inverse of Controll控制反转):指的是对象的创建方式进行了反转,传统的开发方式是程序员自己 new 对象,IoC就是将这一过程进行了反转,程序员不需要自己 new 对象,而是交给 IoC 容器来创建对象,程序员只需要使用这些对象即可。 控制反转的好处是解耦合。将创建对象的控制权进行了反转,之前是直接 new 的,现在是接收工厂方法返回的对象,无法控制对象的创建,将对象创建的权力进行了反转,所以叫控制反转。
IOC创建对象的两种方式:
一、基于XML
1、创建一个 XML 文件,在文件中将程序中需要用的对象进行配置。
2、加载 IoC 容器,Spring 框架会自动读取 XML 文件,根据 XML 的配置,自动创建各种对象,放入到缓冲池中。
3、开发者只需要根据需求从缓冲池中取出相应对象使用即可。
二、基于注解
基于注解的方式和基于 XML 的方式原理是相通的,只不过换了一种形式来处理。
1、在需要注入的 IoC 的类处添加注解,标注一下,告诉Spring 框架,这个类是需要注入的 bean。默认是根据容器中的类型去加载。
方式一:
方式二:
2、加载 IoC 容器,Spring 框架会自动读取到所有添加了注解的类,通过反射机制创建 bean,注入到 IoC 容器中,指定要扫描的包。
3、开发者从 IoC 容器中取出需要的 bean。
基于注解的方式,类默认的 id 是类名的首字母小写,User --> user,如果要修改 id,只需要给@Component 注解添加 value 属性即可,属性值就是新的 id 值。
Spring IoC的核心源码原理:
一、基于 XML
1、IoC 容器加载的时候,会自动读取spring.xml。
2、通过 XML 解析,获取到目标数据。
3、通过反射机制,创建目标 bean。
4、将这些 bean 注入到 IoC 缓存中,以 key-value 的形式进行存储。
5、开发者只需要通过 key/Class 从缓存中取出 bean 使用即可。
二、基于注解
1、IoC 容器加载的时候,要遍历指定包名下的所有类。
2、判断这些类是否添加了@Component,把所有添加了@Component 注解的类进行数据收集(beanId、className),存入集合中。
3、将集合传递给下一个单元,遍历这个集合,获取目标数据。
4、通过反射机制,创建目标 bean。
5、将这些 bean 注入到 IoC 缓存中,以 key-value 的形式进行存储。
6、开发者只需要通过 key/Class 从缓存中取出 bean 使用即可。
首先在指定给目标包中添加注解,然后启动ioc容器,找到添加的注解类,获取类上面的id和class信息然后封装成一个对象,每一个对象用BeanDefinition表示,把每一个BeanDefinition放入BeanDefinitions集合,解析这个集合,遍历每一个元素,拿到每一个id根据class创建对象,最后把对象放入cache中,把id作为key存放进去。
手写一个SpringIoc容器:
步骤:
/**
* 1、自定义一个AnnotationConfigApplicationContext,构造器中传入目标包。
* 2、获取这个包下的所有类。
* 3、遍历这些类,找出添加了 @Component 注解的类,获取对应的 beanName,beanClass,封装成一个BeanDefinition 对象,存入 Set 集合,这些就是 IoC 自动装载的原材料。
* 4、遍历 Set 集合,通过反射机制创建对象,同时检测属性是否有 @Value 注解,有的话就赋值,没有就不赋值。
* 5、将上面创建的 bean 以 k-v 的形式存入 cache Map
* 6、提供 getBean 方法,通过 beanName 从缓存区取出对应的 bean
*/
使用Maven进行创建:
pom.xml文件,引入lombok和jdk版本号
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
创建所需的标注类:
Component.java
// 标注在类上定义的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
String value() default "";
}
Autowired.java
// 标注在属性上的注解,按照类型自动注入
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {}
Qualifier.java
// 标注在属性上的注解,按照名称注入
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Qualifier {
String value();
}
Value.java
// 标注在属性上的注解,用于给属性赋值
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Value {
String value();
}
创建BeanDefinition包装类
// 用于包装注解类的id和class
@Data
@AllArgsConstructor
public class BeanDefinition {
private String beanName;
private Class beanClass;
}
核心类:ioc容器入口
AnnotationConfigApplicationContext.java
public class AnnotationConfigApplicationContext {
// 用来存储创建好的bean
private Map<String,Object> cache = new HashMap<>();
public AnnotationConfigApplicationContext(String basePackage){
//遍历包,找到目标类
Set<BeanDefinition> beanDefinitions = findBeanDefinition(basePackage);
//根据原料创建bean
createObject(beanDefinitions);
//自动装载
autowiredObject(beanDefinitions);
}
/**
* 遍历包,找到目标类
* @param basePackage 要扫描的包路径
* @return 返回包装好的类
*/
private Set<BeanDefinition> findBeanDefinition(String basePackage) {
// 获取包下的所有Class
Set<Class<?>> classes = MyUtil.getClasses(basePackage);
// 遍历集合
Iterator<Class<?>> iterator = classes.iterator();
// 用来存放包装好的BeanDefinition
Set<BeanDefinition> set = new HashSet<>();
while (iterator.hasNext()){
Class<?> clazz = iterator.next();
// 判断该类是否添加了@Component注解
Component component = clazz.getAnnotation(Component.class);
if(component!=null){
// 获取注解上的值
String beanName = component.value();
if ("".equals(beanName)){
// 注解值为空,自动给这个bean取一个名字,默认类名首字母小写
String packageName = clazz.getPackage().getName();
String className = clazz.getName();
beanName = className.replace(packageName+".","");
beanName = beanName.substring(0,1).toLowerCase()+beanName.substring(1);
}
// 添加到集合
set.add(new BeanDefinition(beanName,clazz));
}
}
return set;
}
/**
* 创建bean对象
* @param beanDefinitions 包装好的beanDefinitions
*/
private void createObject(Set<BeanDefinition> beanDefinitions) {
// 遍历集合
Iterator<BeanDefinition> iterator = beanDefinitions.iterator();
while (iterator.hasNext()) {
BeanDefinition beanDefinition = iterator.next();
try {
// 获取beanClass信息
Class beanClass = beanDefinition.getBeanClass();
// 创建当前bean的实例
Object instance = beanClass.getConstructor(null).newInstance(null);
// 给属性赋值
Field[] fields = beanClass.getDeclaredFields();
for (Field field : fields) {
// 判断当前属性是否添加了@Value注解
Value fieldAnnotation = field.getAnnotation(Value.class);
if(fieldAnnotation!=null){
// 有@Value注解,给当前属性赋值
// 获取注解上的值
String value = fieldAnnotation.value();
// 获得属性名字,并找到给当前属性赋值的方法setXxx()
String fieldName = field.getName();
String methodName = "set"+fieldName.substring(0,1).toUpperCase()+fieldName.substring(1);
Method method = beanClass.getMethod(methodName, field.getType());
// 得到属性的类型
String name = field.getType().getName();
Object val = null;
switch (name){
case "java.lang.Integer":
val = Integer.parseInt(value);
break;
case "java.lang.String":
val = value;
break;
case "java.lang.Double":
val = Double.parseDouble(value);
break;
}
// 通过反射给当前属性赋值
method.invoke(instance,val);
}
}
// 并将创建好的bean放在缓存中
cache.put(beanDefinition.getBeanName(),instance);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
/**
* 自动装载
* @param beanDefinitions 包装好的beanDefinitions
*/
private void autowiredObject(Set<BeanDefinition> beanDefinitions) {
// 遍历集合
Iterator<BeanDefinition> iterator = beanDefinitions.iterator();
while (iterator.hasNext()) {
BeanDefinition beanDefinition = iterator.next();
// 获得当前的类信息
Class beanClass = beanDefinition.getBeanClass();
// 获得当前类的所有属性,并遍历它
Field[] fields = beanClass.getDeclaredFields();
for (Field field : fields) {
// 判断该属性上是否添加了@Autowired注解
Autowired autowired = field.getAnnotation(Autowired.class);
if(autowired!=null){
// 判断该属性上是否添加了@Qualifier注解
Qualifier qualifier = field.getAnnotation(Qualifier.class);
if(qualifier!=null){
// 根据名字进行装载
try {
// 获取@Qualifier注解上的value值
String value = qualifier.value();
// 得到缓存中的bean对象
Object obj = cache.get(value);
Object bean = cache.get(beanDefinition.getBeanName());
// 找到要注入的方法setXxx()
String name = field.getName();
String methodName = "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
Method method = beanClass.getMethod(methodName, field.getType());
// 通过反射注入对象
method.invoke(bean,obj);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
} else {
// 根据类型进行装载
try {
// 找到当前对象的类名字,首字母小写
Class<?> clazz = field.getType();
String packageName = clazz.getPackage().getName();
String className = clazz.getName();
className = className.replace(packageName+".","");
className = className.substring(0,1).toLowerCase()+className.substring(1);
// 去缓存中获取bean对象
Object obj = cache.get(className);
Object bean = cache.get(beanDefinition.getBeanName());
// 找到要注入的方法setXxx()
String name = field.getName();
String methodName = "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
Method method = beanClass.getMethod(methodName, field.getType());
// 通过反射注入对象
method.invoke(bean,obj);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
}
}
/**
* 通过name获取bean
* @param beanName
* @return
*/
public Object getBean(String beanName){
return cache.get(beanName);
}
}
工具类:
MyUtil.java
public class MyUtil {
public static Set<Class<?>> getClasses(String pack) {
// 第一个class类的集合
Set<Class<?>> classes = new LinkedHashSet<Class<?>>();
// 是否循环迭代
boolean recursive = true;
// 获取包的名字 并进行替换
String packageName = pack;
String packageDirName = packageName.replace('.', '/');
// 定义一个枚举的集合 并进行循环来处理这个目录下的things
Enumeration<URL> dirs;
try {
dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
// 循环迭代下去
while (dirs.hasMoreElements()) {
// 获取下一个元素
URL url = dirs.nextElement();
// 得到协议的名称
String protocol = url.getProtocol();
// 如果是以文件的形式保存在服务器上
if ("file".equals(protocol)) {
// 获取包的物理路径
String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
// 以文件的方式扫描整个包下的文件 并添加到集合中
findClassesInPackageByFile(packageName, filePath, recursive, classes);
} else if ("jar".equals(protocol)) {
// 如果是jar包文件
// 定义一个JarFile
System.out.println("jar类型的扫描");
JarFile jar;
try {
// 获取jar
jar = ((JarURLConnection) url.openConnection()).getJarFile();
// 从此jar包 得到一个枚举类
Enumeration<JarEntry> entries = jar.entries();
findClassesInPackageByJar(packageName, entries, packageDirName, recursive, classes);
} catch (IOException e) {
// log.error("在扫描用户定义视图时从jar包获取文件出错");
e.printStackTrace();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
return classes;
}
private static void findClassesInPackageByJar(String packageName, Enumeration<JarEntry> entries, String packageDirName, final boolean recursive, Set<Class<?>> classes) {
// 同样的进行循环迭代
while (entries.hasMoreElements()) {
// 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
JarEntry entry = entries.nextElement();
String name = entry.getName();
// 如果是以/开头的
if (name.charAt(0) == '/') {
// 获取后面的字符串
name = name.substring(1);
}
// 如果前半部分和定义的包名相同
if (name.startsWith(packageDirName)) {
int idx = name.lastIndexOf('/');
// 如果以"/"结尾 是一个包
if (idx != -1) {
// 获取包名 把"/"替换成"."
packageName = name.substring(0, idx).replace('/', '.');
}
// 如果可以迭代下去 并且是一个包
if ((idx != -1) || recursive) {
// 如果是一个.class文件 而且不是目录
if (name.endsWith(".class") && !entry.isDirectory()) {
// 去掉后面的".class" 获取真正的类名
String className = name.substring(packageName.length() + 1, name.length() - 6);
try {
// 添加到classes
classes.add(Class.forName(packageName + '.' + className));
} catch (ClassNotFoundException e) {
// .error("添加用户自定义视图类错误 找不到此类的.class文件");
e.printStackTrace();
}
}
}
}
}
}
private static void findClassesInPackageByFile(String packageName, String packagePath, final boolean recursive, Set<Class<?>> classes) {
// 获取此包的目录 建立一个File
File dir = new File(packagePath);
// 如果不存在或者 也不是目录就直接返回
if (!dir.exists() || !dir.isDirectory()) {
// log.warn("用户定义包名 " + packageName + " 下没有任何文件");
return;
}
// 如果存在 就获取包下的所有文件 包括目录
File[] dirfiles = dir.listFiles(new FileFilter() {
// 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
@Override
public boolean accept(File file) {
return (recursive && file.isDirectory()) || (file.getName().endsWith(".class"));
}
});
// 循环所有文件
for (File file : dirfiles) {
// 如果是目录 则继续扫描
if (file.isDirectory()) {
findClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive, classes);
} else {
// 如果是java类文件 去掉后面的.class 只留下类名
String className = file.getName().substring(0, file.getName().length() - 6);
try {
// 添加到集合中去
// classes.add(Class.forName(packageName + '.' +
// className));
classes.add(Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className));
} catch (ClassNotFoundException e) {
// log.error("添加用户自定义视图类错误 找不到此类的.class文件");
e.printStackTrace();
}
}
}
}
}
测试用的实体类:Student.java和Class.java类
@Data
@Component("stu")
public class Student {
@Value("2021100101")
private Integer sid;
@Value("张三")
private String name;
@Value("男")
private String sex;
@Autowired
@Qualifier("cls")
private Class clazz;
}
@Data
@Component("cls")
public class Class {
@Value("1001")
private Integer cid;
@Value("精英班")
private String className;
}
测试:
public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext("com.raylei.entity");
System.out.println(ioc.getBean("stu"));
}
}
结果:
GitHub:https://github.com/Mr-LeiLei/MySpringIoc
CSDN:https://download.csdn.net/download/qq_36942720/16640502