Spring IOC 在面试中经常出现的问题,通过刷面试题只能做到知其然不知其所以然,深入底层追问,然后就一脸懵逼,在此,手写一篇Spring IOC,帮你彻底理解什么叫控制反转
Spring IOC 操作有:配置文件方式,注解方式
本demo 采用注解方式:
- 自定义注解
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Service {
}
- 定义BeanFactory
public interface BeanFactory {
/**
* 初始化 bean
*/
void initBeans() throws InstantiationException, IllegalAccessException;
/**
* 按 bean ID 获取 bean
* @param var1
* @return
*/
Object getBean(String var1) throws Exception;
/**
* 按 classes 获取 bean
* @param var1
* @param <T>
* @return
*/
<T> T getBean(Class<T> var1);
}
- 创建ApplicationContext
public class ApplicationContext implements BeanFactory {
volatile int num;
/**
* 1. 定义一个bean容器,用于存放bean
* 2. 初始化 bean 并注入容器
* 3. 初始化 properties
*/
private String packageName;
/**
* 定义一个 bean 容器
*/
private ConcurrentHashMap<Object, Object> beans = null;
public ApplicationContext(String packageName) throws Exception {
beans = new ConcurrentHashMap<>();
this.packageName = packageName;
initBeans();
initProperties();
}
private void initProperties() throws Exception {
System.out.println( num++ + ", 开始初始化实例成员遍历");
Set<Object> set = new HashSet<>();
for (Map.Entry<Object, Object> entry : beans.entrySet()) {
Object bean = entry.getValue();
if (!set.contains(bean)) { // 避免beanID和classes注册的 bean 实例化重复
System.out.println( num++ + ", 检查属性上是否有注解");
attrAssign(bean);
}
}
System.out.println( num++ + ", 结束初始化实例成员遍历");
}
private void attrAssign(Object obj) throws Exception {
// 通过反射 获取 Class
Class<? extends Object> classes = obj.getClass();
Field[] fields = classes.getDeclaredFields();
if (fields != null || fields.length > 0) {
for (Field field : fields) {
Autowired autowired = field.getAnnotation(Autowired.class);
if (autowired != null) {
// 获取属性名称
String beanId = field.getName();
Object bean = getBean(beanId);
if (bean != null) {
field.setAccessible(true); // 允许访问私有变量
field.set(obj, bean);
}
}
}
}
}
@Override
public void initBeans() throws InstantiationException, IllegalAccessException {
System.out.println( num++ + ", 开始初始化 bean");
// 1. 加载 包路径下 的 类
List<Class<?>> classes = ClassUtil.getClass(packageName);
// 遍历需要注册为bean的类并注入到 bean 容器中
findClassExistAnnotation(classes);
if (beans == null || beans.isEmpty()) {
System.out.println( num++ + ", 该路径下没有需要注册为bean的类");
}
System.out.println( num++ + ", 结束初始化 bean");
}
/**
* 遍历需要注册为bean的类并注入到 bean 容器中
* @param classes
* @return
*/
private void findClassExistAnnotation(List<Class<?>> classes) throws IllegalAccessException, InstantiationException {
for (Class<?> classInfo : classes) {
Service service = classInfo.getAnnotation(Service.class);
if (service != null) {
System.out.println( num++ + ", 获取类名");
String className = classInfo.getSimpleName();
System.out.println( num++ + ", 转换类名第一个字母为小写");
String beanId = toLowerCaseFirstOne(className);
System.out.println( num++ + ", 通过class名称获取类的实例化对象");
Object newInstance = classInfo.newInstance();
System.out.println( num++ + ", 将实例化对象注入bean 容器");
beans.put(beanId, newInstance);
beans.put(classInfo, newInstance);
}
}
}
/**
* bean 名首字母转小写
* @param className
* @return
*/
private String toLowerCaseFirstOne(String className) {
return Character.isLowerCase(className.charAt(0)) ? className : new StringBuffer().append(Character.toLowerCase(className.charAt(0))).append(className.substring(1)).toString();
}
@Override
public Object getBean(String beanId) throws Exception {
if (StringUtils.isEmpty(beanId)) {
System.out.println( "beanId 参数不能为空");
throw new Exception("beanId 参数不能为空");
}
Object object = beans.get(beanId);
return object;
}
@Override
public <T> T getBean(Class<T> var1) {
T o = (T) beans.get(var1);
return o;
}
}
- 创建需要注册为bean的对象
public interface OrderService {
void getOrder();
}
@Service
public class OrderServiceImpl implements OrderService {
@Override
public void getOrder() {
System.out.println(">>>>>>>>>>>>>>>>>>> 调用了 OrderServiceImpl.getOrder >>>>>>>>>>>>>>>>>>>>>>>>");
}
}
public interface UserService {
void findUserOrder();
}
@Service
public class UserServiceImpl implements UserService {
@Autowired
private OrderService orderServiceImpl;
@Override
public void findUserOrder() {
orderServiceImpl.getOrder();
}
}
- ClassUtil, 通过包路径获取对象
public class ClassUtil {
public static List<Class<?>> getClass(String packageName) {
List<Class<?>> classes = new ArrayList<>();
boolean flag = true; //是否循环迭代
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
String packagePath = packageName.replace(".", "/");
try {
Enumeration<URL> urls = classLoader.getResources(packagePath);
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
if (url == null) {
continue;
}
String protocol = url.getProtocol();
if ("file".equals(protocol)) {
String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
findAndAddClassesInPackageByFile(packageName, filePath, flag, classes);
}
}
} catch (IOException e) {
e.printStackTrace();
}
return classes;
}
private static void findAndAddClassesInPackageByFile(String packageName, String filePath, boolean flag, List<Class<?>> classes) {
File dir = new File(filePath);
if (!dir.exists() || !dir.isDirectory()) {
System.out.println("此路径下没有文件");
return;
}
File[] dirFiles = dir.listFiles(new FileFilter(){
@Override
public boolean accept(File pathname) {
return flag && pathname.isDirectory() || pathname.getName().endsWith(".class");
}
});
for (File file : dirFiles) {
if (file.isDirectory()) {
findAndAddClassesInPackageByFile(packageName + "/" + file.getName(), file.getAbsolutePath(), flag, classes);
} else {
String className = file.getName().substring(0, file.getName().length() -6);
System.out.println("类名:" +className);
try {
classes.add(Thread.currentThread().getContextClassLoader().loadClass(packageName + "." + className));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
}
- 测试
public class DemoTest {
public static void main(String[] args) throws Exception {
ApplicationContext app = new ApplicationContext("com.pro.service.impl");
UserServiceImpl userServiceImpl = (UserServiceImpl) app.getBean("userServiceImpl");
UserServiceImpl userService = app.getBean(UserServiceImpl.class);
userServiceImpl.findUserOrder();
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>");
userService.findUserOrder();
}
}