IOC (Inversion of Control) 控制反转。熟悉Spring的应该都知道。那么具体是怎么实现的呢?下面我们通过一个例子说明。
1. Component注解定义
package com.lh.spring.study.ioc;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
}
先定义一个@Component注解。只要被@Component自定义主键注释的类都是受容器管理的Bean。
2. Inject注解定义
package com.lh.spring.study.ioc;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Inject {
}
定义一个@Inject注解,只要是被@Inject注解注释的属性都会自动注入,实现IOC功能。
3. 用户Bean实现
package com.lh.spring.study.ioc;
/**
* @ClassName User
* @Description
* @Date 2019/3/7 11:47
* @Aurhor liang.hao
*/
public class User {
private String id ;
private String name;
public User(){
}
public User(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
'}';
}
}
4. UserService实现
package com.lh.spring.study.ioc;
/**
* @ClassName UserService
* @Description
* @Date 2019/3/7 11:48
* @Aurhor liang.hao
*/
@Component
public class UserService {
public User getUser(){
return new User("1230","lianghao");
}
}
UserService实现。使用@Component注解标注该类是受容器管理的类。
5. UserController实现
package com.lh.spring.study.ioc;
/**
* @ClassName UserController
* @Description
* @Date 2019/3/7 11:50
* @Aurhor liang.hao
*/
@Component
public class UserController {
@Inject
private UserService userService;
public void printlnUser(){
System.out.println(userService.getUser());
}
}
Usercontroller实现,该类被@Component注解注释,表示受容器管理的Bean。
userService熟悉使用了@Inject自定义注解,表示该属性是容器自动注入该实例,实现IOC功能。
6. IocContext 容器实现
package com.lh.spring.study.ioc;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.URL;
import java.util.Enumeration;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @ClassName IocContext
* @Description
* @Date 2019/3/7 11:53
* @Aurhor liang.hao
*/
public class IocContext {
public static Map<Class<?> , Object> applicationContentMap = new ConcurrentHashMap<Class<?> , Object>();
public final static String PACKAGE_NAME="com.lh.spring.study";
static {
try {
initBean(PACKAGE_NAME);
} catch (IOException e) {
e.printStackTrace();
}
}
private static void initBean(String packageName) throws IOException {
Enumeration<URL> urlEnumeration = Thread.currentThread().getContextClassLoader().getResources(packageName.replaceAll("\\.", "/"));
while (urlEnumeration.hasMoreElements()) {
String path = urlEnumeration.nextElement().getPath();
System.out.println(path);
addClassByAnnotation(path, packageName);
}
IocUtil.inject();
}
private static void addClassByAnnotation(String filePath, String packageName){
try {
File[] files = getClassFile(filePath);
if(files != null){
for(File file : files){
String fileName = file.getName();
if (file.isFile()) {
Class<?> clazz = Class.forName(packageName + "." + fileName.substring(0, fileName.lastIndexOf(".")));
//判断该类是否实现了注解
if(clazz.isAnnotationPresent(Component.class)) {
applicationContentMap.put(clazz, clazz.newInstance());
}
} else {
addClassByAnnotation(file.getPath(), packageName + "." + fileName);
}
}
}
}catch (Exception e){
e.printStackTrace();
}
}
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();
}
});
}
}
package com.lh.spring.study.ioc;
import java.lang.reflect.Field;
import java.util.Map;
/**
* @ClassName IocUtil
* @Description
* @Date 2019/3/7 12:35
* @Aurhor liang.hao
*/
public class IocUtil {
public static void inject() {
Map<Class<?>, Object> map = IocContext.applicationContentMap;
try {
for (Map.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);
/**这地方可能会存在获取不到要注入的对象,即fieldObj为null,这时候就应该抛出异常,注入失败(spring常见的错误)*/
field.set(obj, fieldObj);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
- 扫描加载指定包路径下的所有的Class,并判断该Class是否是@Component注解的类,如果是,则创建实例,并保存到applicationContext缓存中。
- 调用IocUtil.inject(),进行依赖注入。
- 循环变量 applicationContext中所有的Bean,判断每个Bean中是否有被@Inject注解修饰的属性,如果有则从applicationContext中获取要注入的实例,并使用反射实现自动注入功能
7.模拟调用UserController测试类
public class Main {
public static void main(String[] args) throws Exception {
UserController userController =(UserController)IocContext.applicationContentMap.get(UserController.class);
userController.getUser();
}
}
从IocContext 容器中获取UserController实例,并调用getUser()方法。从结果中我们发现 UserController中的UserService被容器自动注入进来了。然后调用UserService.getUser() 获取用户信息。