从零手写IOC
2017-12-03 本文已影响686人
jijs
概述
IOC (Inversion of Control) 控制反转。熟悉Spring的应该都知道。那么具体是怎么实现的呢?下面我们通过一个例子说明。
1. Component注解定义
package cn.com.infcn.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 指定需要容器管理的类
*
* @author jijs
* @date 2017-12-3
*/
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
}
先定义一个@Component注解。只要被@Component自定义主键注释的类都是受容器管理的Bean。
2. Inject注解定义
package cn.com.infcn.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 指定需要注入的属性
* @author jijs
* @date 2017-12-3
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Inject {
}
定义一个@Inject注解,只要是被@Inject注解注释的属性都会自动注入,实现IOC功能。
3. 用户Bean实现
package cn.com.infcn.bean;
/**
* 用户Bean
*
* @author jijs
* @date 2017-12-3
*/
public class User {
private String userName;
private Integer age;
public User(String userName, Integer age) {
this.userName = userName;
this.age = age;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User [userName=" + userName + ", age=" + age + "]";
}
}
只是一个普通的模型bean。
4. UserService实现
package cn.com.infcn.service;
import cn.com.infcn.annotation.Component;
import cn.com.infcn.bean.User;
/**
* 用户Service实现
*
* @author jijs
* @date 2017-12-3
*/
@Component
public class UserService {
public User getUser() {
User user = new User("张三", 20);
return user;
}
}
UserService实现。使用@Component注解标注该类是受容器管理的类。
3. UserController实现
package cn.com.infcn.controller;
import cn.com.infcn.annotation.Component;
import cn.com.infcn.annotation.Inject;
import cn.com.infcn.bean.User;
import cn.com.infcn.service.UserService;
/**
* 用户Controller实现
*
* @author jijs
* @date 2017-12-3
*/
@Component
public class UserController {
@Inject
private UserService userService;
public void getUser() {
User user = userService.getUser();
System.out.println(user);
}
}
Usercontroller实现,该类被@Component注解注释,表示受容器管理的Bean。
userService熟悉使用了@Inject自定义注解,表示该属性是容器自动注入该实例,实现IOC功能。
6. IocContext 容器实现
package cn.com.infcn.ioc;
import java.io.File;
import java.io.FileFilter;
import java.net.URL;
import java.util.Enumeration;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import cn.com.infcn.annotation.Component;
/**
* Ioc 容器实现类
*
* @author jijs
* @date 2017-12-3
*/
public class IocContext {
public static final Map<Class<?>, Object> applicationContext = new ConcurrentHashMap<Class<?>, Object>();
static{
String packageName = "cn.com.infcn";
try {
initBean(packageName);
} catch (Exception e) {
e.printStackTrace();
}
}
private static void initBean(String packageName) throws Exception {
Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(packageName.replaceAll("\\.", "/"));
while (urls.hasMoreElements()) {
addClassByAnnotation(urls.nextElement().getPath(), packageName);
}
//IOC实现, 自定注入
IocUtil.inject();
}
//获取指定包路径下实现 Component主键Bean的实例
private static void addClassByAnnotation(String filePath, String packageName) {
try {
File[] files = getClassFile(filePath);
if (files != null) {
for (File f : files) {
String fileName = f.getName();
if (f.isFile()) {
Class<?> clazz = Class.forName(packageName + "." + fileName.substring(0, fileName.lastIndexOf(".")));
//判断该类是否实现了注解
if(clazz.isAnnotationPresent(Component.class)) {
applicationContext.put(clazz, clazz.newInstance());
}
} else {
addClassByAnnotation(f.getPath(), packageName + "." + fileName);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
//获取该路径下所遇的class文件和目录
private static File[] getClassFile(String filePath) {
return new File(filePath).listFiles(new FileFilter() {
@Override
public boolean accept(File file) {
return file.isFile() && file.getName().endsWith(".class") || file.isDirectory();
}
});
}
}
- 扫描加载指定包路径下的所有的Class,并判断该Class是否是@Component注解的类,如果是,则创建实例,并保存到applicationContext缓存中。
- 调用IocUtil.inject(),进行依赖注入。
7. Ioc 依赖注入实现
package cn.com.infcn.ioc;
import java.lang.reflect.Field;
import java.util.Map;
import java.util.Map.Entry;
import cn.com.infcn.annotation.Inject;
/**
* Ioc 注入实现
*
* @author jijs
* @date 2017-12-3
*/
public class IocUtil {
public static void inject() {
Map<Class<?>, Object> map = IocContext.applicationContext;
try {
for (Entry<Class<?>, Object> entry : map.entrySet()) {
Class<?> clazz = entry.getKey();
Object obj = entry.getValue();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(Inject.class)) {
Class<?> fieldClazz = field.getType();
field.setAccessible(true);
Object fieldObj = map.get(fieldClazz);
field.set(obj, fieldObj);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
循环变量 applicationContext中所有的Bean,判断每个Bean中是否有被@Inject注解修饰的属性,如果有则从applicationContext中获取要注入的实例,并使用反射实现自动注入功能。
8. 模拟调用UserController测试类
package cn.com.infcn;
import cn.com.infcn.controller.UserController;
import cn.com.infcn.ioc.IocContext;
/**
* 模拟调用UserController
*
* @author jijs
* @date 2017-12-3
*/
public class Main {
public static void main(String[] args) throws Exception {
UserController userController = (UserController)IocContext.applicationContext.get(UserController.class);
userController.getUser();
}
}
从IocContext 容器中获取UserController实例,并调用getUser()方法。运行结果结果如下图。从结果中我们发现 UserController中的UserService被容器自动注入进来了。然后调用UserService.getUser() 获取用户信息。
运行结果
想了解更多精彩内容请关注我的公众号