前提:用于存储对象的容器我们可以把它想象成一个Map集合,Map<Class, Object>,class为对象类型,object为实例化后的对象。
1.手写IOC需要自定义创建单例对象的注解和依赖注入的注解
项目结构如下:
2. 创建注解
光标移至相应的包下:右键->new->java class
2.1. @Bean注解如下,用于创建单例对象
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {
}
2.2. 用于依赖注入的注解
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DI { }
2.3. 用于获取IOC容器中的Bean接口及其实现类
接口:
public interface ApplicationContext {
Object getBean(Class clazz);
}
实现类:
public class AnnotationApplicationContext implements ApplicationContext {
private static final Map<Class, Object> beanFactory = new HashMap<>();
private static String rootPath;
@Override
public Object getBean(Class clazz) {
return beanFactory.get(clazz); }
/**
* 创建有参构造,传递包路径,设置包扫描规则
* 当前包及其子包,哪个类有@Bean注解,把这个类通过反射机制创建出来,放到beanFactory中
* @param basePackage 包路径
*/
public AnnotationApplicationContext(String basePackage) throws IOException {
String packagePath = basePackage.replace(".", "\\");
Enumeration<URL> resources =
Thread.currentThread().getContextClassLoader().getResources(packagePath);
while (resources.hasMoreElements()) {
URL url = resources.nextElement();
String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
rootPath = filePath.substring(0, filePath.length() - packagePath.length());
// 包扫描
loadPackage(new File(filePath)); }
loadDi();
}
private static void loadPackage(File file) {
// 1.判断是否是文件夹
if (file.isDirectory()) {
// 2.获取文件夹中的所有内容
File[] childrenFiles = file.listFiles();
// 3.判断,内容为空直接返回
if (childrenFiles == null || childrenFiles.length == 0) {
return; }
// 4.不为空,遍历文件夹中的所有内容
Arrays.stream(childrenFiles).forEach(
item -> createBean(item)
); }
}
private static void createBean(File item) {
boolean directory = item.isDirectory();
if (directory) {
// 4.1 遍历得到每个file对象,继续判断,还是文件夹就遍历
loadPackage(item);
} else {
// 4.2 遍历得到的file对象不是文件夹,是文件。 // 4.3 包路径+类名称,
String pathWithClass = item.getAbsolutePath().substring(rootPath.length() - 1);
// 4.4 是否为.class文件的类型
if (pathWithClass.contains(".class")) {
try {
// 4.5 .calss类型,把路径\替换成. 把.class去掉
String allName = pathWithClass.replaceAll("\\\\", ".")
.replace(".class", "");
// 4.6 判断类上是否有注解,有注解进行实例化的过程
Class<?> clazz = Class.forName(allName);
if (clazz.isAnnotationPresent(Bean.class)) {
Object instance = clazz.getConstructor().newInstance();
// 4.7 将实例化的对象放到map集合中,beanFactory
// 4.8 判断当前类如果有接口,让接口class作为map的key
if (clazz.getInterfaces().length > 0) {
beanFactory.put(clazz.getInterfaces()[0], instance);
} else {
beanFactory.put(clazz, instance);
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace(); } catch (Exception e) {
e.printStackTrace(); }
}
}
}
//属性注入
public void loadDi(){
//实例化对象在beanFactory集合里面
//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 declaredField : declaredFields) {
//4、判断属性上是否有注解@Di
DI annotation = declaredField.getAnnotation(DI.class);
if (annotation != null) {
// 如果是私有属性 设置可以设置值
declaredField.setAccessible(true);
//5、如果有@Di注解 把对象进行设置(注入)
try {
declaredField.set(obj, beanFactory.get(declaredField.getType()));
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
}
}
}
2.4. 创建数据库访问层和业务层
数据访问层dao:
public interface UserDao {
void addUser();}
@Bean
public class UserDaoImpl implements UserDao{
@Override
public void addUser() {
System.out.println("add...."); }
}
业务层service:
public interface UserService {
void add();}
public interface AccountService {
void addAccount();}
@Bean
public class UserServiceImpl implements UserService, AccountService {
@DI
private UserDao userDao;
@Override
public void add(){
System.out.println("adUser..."); userDao.addUser(); }
@Override
public void addAccount() {
System.out.println("addAccount"); userDao.addUser(); }
}
2.5. 用main方法测试
public class TestAnnotation {
public static void main(String[] args) throws IOException {
AnnotationApplicationContext context
= new AnnotationApplicationContext("com.munal");
UserService userService = (UserService) context.getBean(UserService.class);
userService.add();
//AccountService accountService = (AccountService) context.getBean(AccountService.class);
//accountService.addAccount(); }
}
*注意:
运行main测试方法中被注释的代码会报空指针异常;
原因:该手写IOC的流程中,如果一个类中有多个实现类,map中指挥默认存一份,key值为实现的第一个接口的class,值为实例化对象。account接口并没有对应的实例化对象,所以accountService为null。