- 控制反转(IOC):控制反转是一种思想,即将对象的创建权力和对象与对象之间的关系交出去,给第三方容器负责
- 依赖注入(DI):实现了控制反转的思想。
- Bean管理:Bean对象的创建,以及Bean对象中属性的赋值(或者叫Bean对象之间关系的维护)。
步骤:
1.搭建Spring6模块
使用IDEA大家Spring6的模块,便于实现IOC思想并完成打包。
2.准备测试需要的bean
为实现后的产品准备验证测试工具:
- 添加依赖
- 创建UserDao接口以及创建UserDaoImpl实现
- 创建UserService接口以及创建UserServiceImpl实现类
1.添加依赖 -- 测试所需依赖
<dependencies>
<!--junit5测试-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.3.1</version>
</dependency>
</dependencies>
2.准备测试接口以及实现类
package com.czy.spring.test.service;
/**
* ClassName: UserService
* Package: com.czy.spring6.test.service
* Description:
*
* @Author Chen Ziyun
* @Version 1.0
*/
public interface UserService {
public void out();
}
package com.czy.spring.test.service.impl;
import com.czy.spring.core.annotation.Bean;
import com.czy.spring.test.dao.UserDao;
import com.czy.spring.test.service.UserService;
/**
* ClassName: UserServiceImpl
* Package: com.czy.spring6.test.service.impl
* Description:
*
* @Author Chen Ziyun
* @Version 1.0
*/
@Bean
public class UserServiceImpl implements UserService {
private UserDao userDao;
@Override
public void out() {
// userDao.print();
System.out.println("Service层执行结束");
}
}
package com.czy.spring.test.dao;
/**
* ClassName: UserDao
* Package: com.czy.spring6.test.dao
* Description:
*
* @Author Chen Ziyun
* @Version 1.0
*/
public interface UserDao {
public void print();
}
package com.czy.spring.test.dao.impl;
import com.czy.spring.core.annotation.Bean;
import com.czy.spring.test.dao.UserDao;
/**
* ClassName: UserDaoImpl
* Package: com.czy.spring6.test.dao.impl
* Description:
*
* @Author Chen Ziyun
* @Version 1.0
*/
@Bean
public class UserDaoImpl implements UserDao {
@Override
public void print() {
System.out.println("Dao层执行结束");
}
}
3.定义注解
通过注解的形式加载Bean与实现依赖注入
Bean注解:
package com.czy.spring.core.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* ClassName: Bean
* Package: com.czy.spring.core.annotation
* Description:
* 通过注解的形式加载bean与实现依赖注入
* @Author Chen Ziyun
* @Version 1.0
*/
@Target(ElementType.TYPE)// 注解作用范围
@Retention(RetentionPolicy.RUNTIME)//注解的生命周期
public @interface Bean {
}
依赖注入注解:
package com.czy.spring.core.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* ClassName: Di
* Package: com.czy.spring.core.annotation
* Description:
* 依赖注入注解
* @Author Chen Ziyun
* @Version 1.0
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Di {
}
4.定义bean容器接口
与Spring6中的ApplicationContext功能类似,即相当于BeanFactory的子接口
package com.czy.spring.core;
/**
* ClassName: ApplocationContext
* Package: com.czy.spring.core
* Description:
*
* @Author Chen Ziyun
* @Version 1.0
*/
public interface ApplicationContext {
Object getBean(Class clazz);
}
5.编写注解bean容器接口实现
AnnotationApplicationContext基于注解扫描bean
package com.czy.spring;
import com.czy.spring.core.ApplicationContext;
import java.util.HashMap;
import java.util.Map;
/**
* ClassName: AnnotationApplicationContext
* Package: com.atczy.annotation
* Description:
* 初始化该对象时,获取所有的具备Bean注释的类,将其封装到beanFactory的Map容器当中,初始化
* @Author Ziyun Chen
* @Create 2024/2/28 18:36
* @Version 1.0
*/
public class AnnotationApplicationContext implements ApplicationContext {
private static Map<Class, Object> beanFactory = new HashMap<>();// 存储bean的容器
@Override
public Object getBean(Class clazz) {
return beanFactory.get(clazz);
}
/**
* 根据包扫描加载bean
* 创建有参构造,传递包路径,设置包扫描规则
* 当前包及其子包,那个类有@Bean注解,把这个类通过反射实例化
*/
public AnnotationApplicationContext(String basePackage){
}
}
6.编写扫描bean逻辑
通过构造方法传入包的base路径,扫描被@Bean注解的java对象 ---- 加载Bean,获取所有的具备Bean注释的类,将其封装到beanFactory的Map容器当中
- 查看当前文件夹是否是空文件夹?是-返回,否-查看当前文件夹下的所有文件
- 遍历当前文件夹下的所有文件
- 当前文件是否是文件夹?是-递归,否-查看当前文件
- 查看当前class文件是否有Bean注解:是 -- 将非接口类的实例放入Map中;否 -- 直接跳过
package com.czy.spring;
import com.czy.spring.core.ApplicationContext;
import com.czy.spring.core.annotation.Bean;
import com.czy.spring.core.annotation.Di;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* ClassName: AnnotationApplicationContext
* Package: com.atczy.annotation
* Description:
* 初始化该对象时,获取所有的具备Bean注释的类,将其封装到beanFactory的Map容器当中,初始化
* @Author Ziyun Chen
* @Create 2024/2/28 18:36
* @Version 1.0
*/
public class AnnotationApplicationContext implements ApplicationContext {
private static Map<Class, Object> beanFactory = new HashMap<>();// 存储bean的容器
private static String rootPath;
@Override
public Object getBean(Class clazz) {
return beanFactory.get(clazz);
}
/**
* 根据包扫描加载bean
* 创建有参构造,传递包路径,设置包扫描规则
* 当前包及其子包,那个类有@Bean注解,把这个类通过反射实例化
*/
public AnnotationApplicationContext(String basePackage){
// 1.获取该包的绝对路径
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");
rootPath = filePath.substring(0, filePath.length()-packagePath.length());// 获取跟路径
// 加载Bean,获取所有的具备Bean注释的类,将其封装到beanFactory的Map容器当中
loadBean(new File(filePath));
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static void loadBean(File file) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
// 1.查看当前文件夹是否是空文件夹?是-返回,否-查看当前文件夹下的所有文件
if (!file.exists()){
return;
}
if (file.isDirectory()){
// 2.遍历当前文件夹下的所有文件
File[] files = file.listFiles();
if (files == null || files.length == 0){
return;
}
for (File childFile : files){
// 3.当前文件是否是文件夹?是-递归,否-查看当前文件
if (childFile.isDirectory()){
loadBean(childFile);
}else {
// 4.查看当前class文件是否有Bean注解
// 4.1 通过文件名获取全类名,第一步把绝对路径去掉
String pathWithClass = childFile.getAbsolutePath().substring(rootPath.length()-1);
// 选中class文件
if (pathWithClass.contains(".class")){
// 将路径中的/转化为.,把.class去掉 -- 获取class的全类名
String fullName = pathWithClass.replaceAll("\\\\", "\\.").
replace(".class", "");
// 4.2 是否有Bean注解,
Class<?> clazz = Class.forName(fullName);
// 将非接口类的实例放入Map中
if (!clazz.isInterface()){
Bean annotation = clazz.getAnnotation(Bean.class);
if (annotation != null){
// 实例化该对象
Object instance = clazz.getDeclaredConstructor().newInstance();
if (clazz.getInterfaces().length > 0){
// 判断是否有Implement接口 是-以该接口为clazz传入Map中
beanFactory.put(clazz.getInterfaces()[0], instance);
}else {
// 否-以该类为clazz传入Map
beanFactory.put(clazz, instance);
}
}
}
}
}
}
}
}
}
7.java类标识Bean注解
8.测试Bean加载
package com.czy.spring;
import com.czy.spring.core.ApplicationContext;
import com.czy.spring.test.service.UserService;
import org.junit.jupiter.api.Test;
/**
* ClassName: SpringIocTest
* Package: com.czy.spring
* Description:
*
* @Author Chen Ziyun
* @Version 1.0
*/
public class SpringIocTest {
@Test
public void testIoc(){
ApplicationContext applicationContext = new AnnotationApplicationContext("com.czy.spring.test");
UserService userService = (UserService) applicationContext.getBean(UserService.class);
userService.out();
System.out.println("run success");
}
}
以上,对象的创建功能已经实现了,但是对于对象之间的关系管理需要进一步实现。即依赖的注入(Di)。
9.依赖注入
在创建UserService的过程中,需要完成依赖的注入,此时,依赖注入功能还没有实现,userDao是个空对象,其中的方法都无法调用
10.依赖注入实现
需要对实现类进行扫描,查看其是否具备依赖注入相关的注解,并进行对象的创建 --- 实例化
对象在beanFactory的map集合中
- 遍历beanFactory的map集合
- 获取map的value值,每个属性获取到
- 遍历得到每个对象属性数组,得到每个属性
- 判断属性上面是否有@Di注解
- 如果有@Di注解,把对象进行设置(注入)
package com.atczy.annotation;
import com.czy.spring.core.ApplicationContext;
import com.czy.spring.core.annotation.Bean;
import com.czy.spring.core.annotation.Di;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.logging.Filter;
/**
* ClassName: AnnotationApplicationContext
* Package: com.atczy.annotation
* Description:
* 初始化该对象时,获取所有的具备Bean注释的类,将其封装到beanFactory的Map容器当中,初始化
* @Author Ziyun Chen
* @Create 2024/2/28 18:36
* @Version 1.0
*/
public class AnnotationApplicationContext implements ApplicationContext {
private static Map<Class, Object> beanFactory = new HashMap<>();// 存储bean的容器
private static String rootPath;
@Override
public Object getBean(Class clazz) {
return beanFactory.get(clazz);
}
/**
* 根据包扫描加载bean
* 创建有参构造,传递包路径,设置包扫描规则
* 当前包及其子包,那个类有@Bean注解,把这个类通过反射实例化
*/
public AnnotationApplicationContext(String basePackage){
// 1.获取该包的绝对路径
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");
rootPath = filePath.substring(0, filePath.length()-packagePath.length());// 获取跟路径
// 加载Bean,获取所有的具备Bean注释的类,将其封装到beanFactory的Map容器当中
loadBean(new File(filePath));
// 加载Di
loadDi();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static void loadBean(File file) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
// 1.查看当前文件夹是否是空文件夹?是-返回,否-查看当前文件夹下的所有文件
if (!file.exists()){
return;
}
if (file.isDirectory()){
// 2.遍历当前文件夹下的所有文件
File[] files = file.listFiles();
if (files == null || files.length == 0){
return;
}
for (File childFile : files){
// 3.当前文件是否是文件夹?是-递归,否-查看当前文件
if (childFile.isDirectory()){
loadBean(childFile);
}else {
// 4.查看当前class文件是否有Bean注解
// 4.1 通过文件名获取全类名,第一步把绝对路径去掉
String pathWithClass = childFile.getAbsolutePath().substring(rootPath.length()-1);
// 选中class文件
if (pathWithClass.contains(".class")){
// 将路径中的/转化为.,把.class去掉 -- 获取class的全类名
String fullName = pathWithClass.replaceAll("\\\\", "\\.").
replace(".class", "");
// 4.2 是否有Bean注解,
Class<?> clazz = Class.forName(fullName);
// 将非接口类的实例放入Map中
if (!clazz.isInterface()){
Bean annotation = clazz.getAnnotation(Bean.class);
if (annotation != null){
// 实例化该对象
Object instance = clazz.getDeclaredConstructor().newInstance();
if (clazz.getInterfaces().length > 0){
// 判断是否有Implement接口 是-以该接口为clazz传入Map中
beanFactory.put(clazz.getInterfaces()[0], instance);
}else {
// 否-以该类为clazz传入Map
beanFactory.put(clazz, instance);
}
}
}
}
}
}
}
}
private void loadDi() throws IllegalAccessException {
// 实例化对象在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[] declaredFields = clazz.getDeclaredFields();
// 3.遍历得到每个对象属性数组,得到每个属性
for (Field field : declaredFields){
//4.判断属性上面是否有@Di注解
Di annotation = field.getAnnotation(Di.class);
if (annotation != null){
// 5.如果有@Di注解,把对象进行设置(注入)
field.setAccessible(true);
field.set(obj, beanFactory.get(field.getType()));
}
}
}
}
}