【Java框架】自主模拟实现Spring核心——实现一个简易的IoC容器

(1)什么是IoC

  IoC(Inversion of Control)控制反转,是一种设计思想,DI(依赖注入)只是实现ioc的一种方式。是Spring的核心内容。可以xml配置,注解。Spring容器在初始化先读取配置文件,根据配置文件或者元数据创建与组织对象存入容器,程序使用时再从ioc容器中取出需要的对象。

  没有IoC,传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,对象的创建和对象间的依赖关系完全在代码中,代码是程序员写的,控制权在程序员手里,对象的创建由代码控制。

  有了IoC容器后,控制权发生了反转,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是松耦合,控制反转后将对象的创建交给用户,用户需要什么对象就用什么对象,我们程序员再也不用管对象的创建了,更多时间去开发新的业务实现,耦合性大大降低。

  总的来说,不使用IoC,类的创建形成多对多依赖关系,耦合度高。IoC相当于一个中间层,IoC与类之间是一对多依赖,将多对多关系变为一对多关系,减少系统整体耦合度。网上一个图片可以很好的解释。
在这里插入图片描述
  耦合的所有对象之间有关系的,每个对象之间有关系,一层掉一层,我们想要解耦,生成个中间件去链接它们,我们调用中间件就行了,IoC容器将它们解耦了,它们四个不再具有强耦合性了,用户想要去调用谁就去调用谁,独立化。

为了解释IoC(控制反转)和DI(依赖注入)给下面例子

//OneClass类//
public class OneClass {

	private TwoClass two;
	
	public OneClass() {
	}
	
	public void doSomething() {
		System.out.println(two);
	}
}
//TwoClass类//
public class TwoClass {

	public TwoClass() {
	}

	@Override
	public String toString() {
		return "这是TwoClass的对象";
	}
	
}
//测试
public class Demo1 {

	public static void main(String[] args) {

		OneClass oneClass = new OneClass();
		oneClass.doSomething();
	}

}
//结果
null

上面运行结果是我们很容易想到的,因为OneClass没有对成员two进行赋值操作,输出当然为null了。如果有一个工具,可以悄悄地,自动的对OneClass的成员进行初始化(注入),那么运行就可以有结果了。

依赖注入:相关类的成员不是通过代码(new)完成的,而是靠一套工具来完成的,所以是依赖于工具注入。

控制反转:本来应该是OneClass类对成员two进行控制,而现在是通过外面工具进行控制的,控制权从OneClass变为了工具。

这个工具就是IoC容器。Spring模拟实现可以用XML配置和注解。

(2)XML配置和注解区别

  首先说明通过XML文件配置完成的注解也可以完成,注解完成的,XML也可以完成。两者各有利弊,要编程者自己斟酌用哪个。

XML配置:

  优点:相关配置文件是独立于代码的,不侵害和更改代码,不用改代码就不需要测试。保证了开闭原则(对修改关闭,对扩展开放)。

  缺点:如果相关类的关系很复杂,其就需要大量配置文件的抒写。(早些年的配置文件很长,一度被人们称为“配置地狱”)

注解方式:

  优点:简单,明了,方便,程序可读性强,无需多写相关配置文件,使得开发效率高。

  缺点:对注解进行更改,就需要进行重新的全面的严格的测试,因为修改了内部代码,一经改动就需进行大量测试以得到更改的正确性。

  总而言之,条条大路通罗马,不拘一格降人才,XML和注解都可以解决问题,我们先学一样,再去找他们的相通之处就能学习到更多知识。

(3)IoC容器的模拟实现

注解:@Component注解

import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Retention(RUNTIME)
@Target(TYPE)
public @interface Component {

}

@Component注解:在类型上面使用的,告知包扫描工具,说明该类型的对象要加到IoC容器里面去,是该容器的一个组件。

注解:@Autowired注解

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Retention(RUNTIME)
@Target({ FIELD, METHOD })
public @interface Autowired {

}

@Autowired注解:该注解用于成员或成员设置方法(setter)的上面,其目的是对成员进行注入,这个成员的对象是从IoC容器中取出来。因此就要求被@Autowired注解标识的成员其类型必须有@Component注解。

BeanDefinition类

public class BeanDefinition {

	private Class<?> klass; 	//那个类
	private Object object;		//那个类实例化对象,只放一份在ioC容器中
	private volatile boolean inject;	//是否注入了

	BeanDefinition() {
		this(null, null);
	}

	BeanDefinition(Class<?> klass, Object object) {
		this.klass = klass;
		this.object = object;
		this.inject = false;
	}

	Class<?> getKlass() {
		return klass;
	}

	void setKlass(Class<?> klass) {
		this.klass = klass;
	}

	Object getObject() {
		return object;
	}

	void setObject(Object object) {
		this.object = object;
	}

	boolean isInject() {
		return inject;
	}

	void setInject(boolean inject) {
		this.inject = inject;
	}
	
}

该类主要描述一个有@Component注解的组件,将其类型和实例化对象本身封装起来形成一个豆子。

BeanFactory类

import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

import com.mec.util.PackageScanner;

public class BeanFactory {

	private static final Map<String, BeanDefinition> beanPool = new HashMap<>();//IoC容器
	
	public BeanFactory() { 
	}
	
	public static void scanPackage(String packageName) {
		new PackageScanner() {
			
			@Override
			public void dealClass(Class<?> klass) {
				if (klass.isPrimitive() || klass.isArray() || klass.isInterface()
						|| klass == String.class || klass.isEnum() || klass.isAnnotation()
						|| !klass.isAnnotationPresent(Component.class) ) {
				return;
				}
				try {
					Object object = klass.getConstructor(new Class[] {}).newInstance(new Object[] {});
					BeanDefinition beanDefinition = new BeanDefinition(klass, object);
					beanPool.put(klass.getName(), beanDefinition);
					/*看似任务把相关组件放到IoC容器中完成了,但实际上,该beanDefinition的成员可能还没有注入*/
				} catch (InstantiationException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				} catch (IllegalArgumentException e) {
					e.printStackTrace();
				} catch (InvocationTargetException e) {
					e.printStackTrace();
				} catch (NoSuchMethodException e) {
					e.printStackTrace();
				} catch (SecurityException e) {
					e.printStackTrace();
				}
			}
		}.scanPackage(packageName);
	}
	
	@SuppressWarnings("unchecked")
	public <T> T getBean(String beanId) {
		BeanDefinition bd = beanPool.get(beanId);
		if (bd == null) {
			System.out.println("bean["	+ beanId + "]没有定义"); //这里可以抛运行时异常
			return null;
		}
		return (T) bd.getObject();
	}
	
	public <T> T getBean(Class<?> klass) {
		return getBean(klass.getName());
	}
		
	public void addBean(String nickName, BeanDefinition beanDefinition) { //不想用类名称当作键,可以用别名
		beanPool.put(nickName, beanDefinition);
	}
	
}

前面说了那么久的IoC容器就是这里面的beanPool,其键为类名称,值为一个BeanDefinition的对象。

另外,我们不对八大基本类型和String类型进行注入,其实可以有解决方法就是在@Autowired里加value。

还有,我们这个类需要用到以前写过的包扫描工具。

因为只需要一个IoC容器,我们就不需要FactoryBuilder,直接在Factory里进行扫描。

关于注入的时机

  在没有完全扫描所有需要的包以前,是没有办法确定依赖关系的完整性的,所以不能进行注入工作。所以注入工作应该在得到或者引用相关Bean的时候。这可以称为懒汉模式

  懒:扫描完包,相关类的成员还没注入,等你要得到这个类的时候再去注入。

  饿:一扫描完,就立刻去进行注入。 饿汉其实根本行不通,因为如果工程需要,多个类需要多次包扫描才能最终完成,这样注入方式是行不通的。

  同时不可以重复注入,第二次getBean()如果不加判断就会重复注入,重复注入根本没有必要,解决方法是在BeanDefinition中加是否注入(inject)了标识。

BeanFactory加注入代码


import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;

import com.mec.util.PackageScanner;

public class BeanFactory {

	private static final Map<String, BeanDefinition> beanPool = new HashMap<>();//IoC容器
	
	public BeanFactory() { 
	}
	
	public static void scanPackage(String packageName) {
		new PackageScanner() {
			
			@Override
			public void dealClass(Class<?> klass) {
				if (klass.isPrimitive() || klass.isArray() || klass.isInterface()
						|| klass == String.class || klass.isEnum() || klass.isAnnotation()
						|| !klass.isAnnotationPresent(Component.class) ) {
				return;
				}
				try {
					Object object = klass.getConstructor(new Class[] {}).newInstance(new Object[] {});
					BeanDefinition beanDefinition = new BeanDefinition(klass, object);
					beanPool.put(klass.getName(), beanDefinition);
				} catch (InstantiationException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				} catch (IllegalArgumentException e) {
					e.printStackTrace();
				} catch (InvocationTargetException e) {
					e.printStackTrace();
				} catch (NoSuchMethodException e) {
					e.printStackTrace();
				} catch (SecurityException e) {
					e.printStackTrace();
				}
			}
		}.scanPackage(packageName);
	}
	
	@SuppressWarnings("unchecked")
	public <T> T getBean(String beanId) {
		BeanDefinition beanDefinition = beanPool.get(beanId);
		if (beanDefinition == null) {
			System.out.println("bean["	+ beanId + "]没有定义"); //这里可以抛运行时异常
			return null;
		}
		
		//在第一次获得相关beanDefinition时候,进行注入,多线程在这有问题
		if (!beanDefinition.isInject()) {		//经典DLC问题,多线程都用这个inject,去加volatile
			synchronized (beanPool) {		//用beanPool最好,单例的,谁都认识它
				if (!beanDefinition.isInject()) {
					doInject(beanDefinition);
					beanDefinition.setInject(true);
				}
			}
		}
		return (T) beanDefinition.getObject();
	}
	
	public <T> T getBean(Class<?> klass) {
		return getBean(klass.getName());
	}
		
	public void addBean(String nickName, BeanDefinition beanDefinition) { //不想用类名称当作键,可以用别名
		beanPool.put(nickName, beanDefinition);
	}
	
	private void doInject(BeanDefinition beanDefinition) {
	 	Class<?> klass = beanDefinition.getKlass();
		Object object = beanDefinition.getObject();
		injectByField(klass, object);
		injectBySetMethod(klass, object);
	}
	
	private void injectByField(Class<?> klass, Object object) {
		Field[] fields = klass.getDeclaredFields();
		for (Field field : fields) {
			if (!field.isAnnotationPresent(Autowired.class)) {
				continue;
			}
			Class<?> fieldClass = field.getType();
			Object value = getBean(fieldClass);
			field.setAccessible(true);			//很糟糕这里,priavte的成员被打开权限了
			try {
				field.set(object, value);
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		}
	}
	
	private void injectBySetMethod(Class<?> klass, Object object) {
		Method[] methods = klass.getDeclaredMethods();
		for (Method method : methods) {
			if (!method.isAnnotationPresent(Autowired.class)
					|| !method.getName().startsWith("set")
					|| method.getParameterCount() != 1
					|| method.getModifiers() != Modifier.PUBLIC) {
				continue;
			}
			Class<?> paraClass = method.getParameterTypes()[0];
			Object value = getBean(paraClass);
			try {
				method.invoke(object, new Object[] {value});
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			}
		}
	}
}


Autowired注解建议应该在方法上而不是成员上

  前面实现的代码我们可以看到,对于私有成员的注入,用到了field.setAccessible(true),本来硬性规定的私有成员,现在就这样被别人权限大开了,是否违反我们private关键字的初衷呢?所以说反射机制是个双刃剑,给我们带了了遍历,也带来了危害。对于setAccessible()方法是否会破坏类的访问规则,产生安全隐患,我在知乎上面看到的一篇回答貌似很有道理:

在这里插入图片描述
  所以我们尽量将Autowired注解设置在公开的setter方法上。话虽这样说,但Spring大部分还是用在成员上,因为是在工具内部完成的,但还是要说清楚危害。

讲Autowired注解用在公开的setter方法上面,考虑setter方法的共性:1. 参数只有一个,且该参数类型是相关成员类型 2.无返回值

  在做实验的期间,发现个很有意思的知识。是关于权限修饰符的类Modifier。在输出各个修饰符的值,发现public:1private:2protected:4,发现它们都是属于2的次方,这不由得想到了二进制,在类Modifier发现果然是二进制。继续横向学习Linxu的chmod命令也是这样的。在做实验,发现final:16,经过 public final修饰的值为17,从中得知原来多个权限符共同修饰的计算是 |(或运算),那当然了,在计算机位运算一定比加法快,由此得知开发Java的前辈为了使用位运算在底层做了功夫。从中也得知了学习要横向学习。Java开发者很聪明,和Linux结合了起来。

注解:@Configuration注解和@Bean注解配合

看下面这个例子

import java.util.Calendar;
import com.google.gson.Gson;
import com.mec.mSpring.core.Autowired;
import com.mec.mSpring.core.Component;

@Component
public class One {

	@Autowired
	private Complex complex;
	@Autowired
	private Gson gson;			//需要导包
	@Autowired
	private Calendar calendar;	//内部工具类
	
	public One() {
	}

	public void doSomething() {
		System.out.println(complex);
		System.out.println(gson);
		System.out.println(calendar);
	}
	
}
---

@Component
public class Complex {

	private double real;
	private double vir;
	
	public Complex() {
	}

	public Complex(double real, double vir) {
		this.real = real;
		this.vir = vir;
	}
	
	public double getReal() {
		return real;
	}

	public void setReal(double real) {
		this.real = real;
	}

	public double getVir() {
		return vir;
	}

	public void setVir(double vir) {
		this.vir = vir;
	}

	@Override
	public String toString() {
		return "Complex [real=" + real + ", vir=" + vir + "]";
	}
	
}

---

import com.mec.mSpring.core.BeanFactory;

public class Demo3 {

	public static void main(String[] args) {

		BeanFactory.scanPackage("com.mec.mSpring.test");
		BeanFactory beanFactory = new BeanFactory();
		One one = beanFactory.getBean(One.class);
		one.doSomething();
	}

}

/*运行结果
bean[com.google.gson.Gson]没有定义
bean[java.util.Calendar]没有定义
Complex [real=0.0, vir=0.0]
null
null
*/

上面例子的三个成员就是@Autowired注解所存在的缺陷。

缺陷一:形如Complex这种类,我们工具只能调用它的无参构造,产生的对象的值都是0,不灵活,不能自定义。 对于类对象的构造用户想个性化,或者类的对象不是简简单单通过无参构造出来后就直接能使用的,还要对这个对象做一些事(例如数据库的Connection设置)。

缺陷二:形如Gson这种jar包的类,我们知道@Autowired只能从IoC容器中获取对象,而所获取的对象所对应的类都需要有@Component注解,对于不可更改的jar包中的类我们没办法加相关注解,也就不能实现注入操作。

缺陷三:形如Calendar这种没有提供可用的构造方法;所谓的没有提供可用的构造方法包括相关构造方法是private的,或其构造方法不能直接调用的,或者相关类的对象根本不是通过无参构造产生的(例如Calendar类,其通过Calendar.getInstance()得到相关对象)。在这种情况下,因为我们工具是调用相关类的无参构造产生的,那么,对于上述情况就不能产生这个类的对象。

@Configuration注解@Bean注解就是为了解决这种问题产生的。

import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Retention(RUNTIME)
@Target(TYPE)
public @interface Configuration {

}

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Retention(RUNTIME)
@Target(METHOD)
public @interface Bean {

}

@Configuration注解用来标识盛放带有@Bean注解的那个类。

@Bean注解所注释的方法中,你可以更灵活的自定义对象值,将这个方法的返回值类型作为键,返回值作为值放到IoC容器中,还可以合适的调用获取该类对象的方法,这很好的解决了上面三个缺陷。

解决上面例子三个成员就可以这样写一个类。

import java.util.Calendar;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.mec.mSpring.core.Bean;
import com.mec.mSpring.core.Configuration;

@Configuration
public class Config {

	public Config() {
	}

	@Bean
	public Complex getComplex() {
		return new Complex(9.8, 2.0);   //这个值可以根据需求改动
	}
	
	@Bean
	public Gson getGson() {
		return new GsonBuilder().create();
	}
	
	@Bean
	public Calendar getCalendar() {
		return Calendar.getInstance();
	}
	
}

修改BeanFactory里的代码

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;

import com.mec.util.PackageScanner;

public class BeanFactory {

	private static final Map<String, BeanDefinition> beanPool = new HashMap<>();//IoC容器
	
	public BeanFactory() { 
	}
	
	public static void scanPackage(String packageName) {
		new PackageScanner() {
			
			@Override
			public void dealClass(Class<?> klass) {
				if (klass.isPrimitive() || klass.isArray() || klass.isInterface()
						|| klass == String.class || klass.isEnum() || klass.isAnnotation()
						|| (!klass.isAnnotationPresent(Component.class) && !klass.isAnnotationPresent(Configuration.class))) {
				return;
				}
				try {
					if (klass.isAnnotationPresent(Configuration.class)) {
						dealBean(klass);
						return;
					}
					Object object = klass.getConstructor(new Class[] {}).newInstance(new Object[] {});
					BeanDefinition beanDefinition = new BeanDefinition(klass, object);
					beanPool.put(klass.getName(), beanDefinition);
				} catch (InstantiationException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				} catch (IllegalArgumentException e) {
					e.printStackTrace();
				} catch (InvocationTargetException e) {
					e.printStackTrace();
				} catch (NoSuchMethodException e) {
					e.printStackTrace();
				} catch (SecurityException e) {
					e.printStackTrace();
				}
			}
		}.scanPackage(packageName);
	}
	
	@SuppressWarnings("unchecked")
	public <T> T getBean(String beanId) {
		BeanDefinition beanDefinition = beanPool.get(beanId);
		if (beanDefinition == null) {
			System.out.println("bean["	+ beanId + "]没有定义"); //这里可以抛运行时异常
			return null;
		}
		
		//在第一次获得相关beanDefinition时候,进行注入,多线程在这有问题
		if (!beanDefinition.isInject()) {		//经典DLC问题,多线程都用这个inject,去加volatile
			synchronized (beanPool) {		//用beanPool最好,单例的,谁都认识它
				if (!beanDefinition.isInject()) {
					doInject(beanDefinition);
					beanDefinition.setInject(true);
				}
			}
		}
		return (T) beanDefinition.getObject();
	}
	
	public <T> T getBean(Class<?> klass) {
		return getBean(klass.getName());
	}
		
	public void addBean(String nickName, BeanDefinition beanDefinition) { //不想用类名称当作键,可以用别名
		beanPool.put(nickName, beanDefinition);
	}
	
	private void doInject(BeanDefinition beanDefinition) {
	 	Class<?> klass = beanDefinition.getKlass();
		Object object = beanDefinition.getObject();
		injectByField(klass, object);
		injectBySetMethod(klass, object);
	}
	
	private void injectByField(Class<?> klass, Object object) {
		Field[] fields = klass.getDeclaredFields();
		for (Field field : fields) {
			if (!field.isAnnotationPresent(Autowired.class)) {
				continue;
			}
			Class<?> fieldClass = field.getType();
			Object value = getBean(fieldClass);
			field.setAccessible(true);			//很糟糕这里,priavte的成员被打开权限了
			try {
				field.set(object, value);
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		}
	}
	
	private void injectBySetMethod(Class<?> klass, Object object) {
		Method[] methods = klass.getDeclaredMethods();
		for (Method method : methods) {
			if (!method.isAnnotationPresent(Autowired.class)
					|| !method.getName().startsWith("set")
					|| method.getParameterCount() != 1
					|| method.getModifiers() != Modifier.PUBLIC) {
				continue;
			}
			Class<?> paraClass = method.getParameterTypes()[0];
			Object value = getBean(paraClass);
			try {
				method.invoke(object, new Object[] {value});
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			}
		}
	}
	
	private static void dealBean(Class<?> klass) {
		try {
			Object configObject = klass.getConstructor(new Class[] {}).newInstance(new Object[] {});
			Method[] methods = klass.getMethods();
			for (Method method :methods) {
				if (!method.isAnnotationPresent(Bean.class)) {
					continue;
				}
				int paraCount = method.getParameterCount();
				Class<?> returnType = method.getReturnType();
				if (paraCount != 0) {
					//带参数的Bean注解下的方法,这问题有点深,稍后讨论
				} else {
					Object result = method.invoke(configObject, new Object[] {});
					BeanDefinition beanDefinition = new BeanDefinition(returnType, result);
					beanPool.put(returnType.getName(), beanDefinition);
				}
			}
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		}
	}
	
}

测试
public class Demo3 {

	public static void main(String[] args) {

		BeanFactory.scanPackage("com.mec.mSpring.test");
		BeanFactory beanFactory = new BeanFactory();
		One one = beanFactory.getBean(One.class);
		one.doSomething();
		
	}

}
/*
Complex [real=9.8, vir=2.0]
{serializeNulls:falsefactories:[Factory[typeHierarchy=com.google.gson.JsonElement gson太长了,简化后的
java.util.GregorianCalendar[time=1602233859225,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=19,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2020,MONTH=9,WEEK_OF_YEAR=41,WEEK_OF_MONTH=2,DAY_OF_MONTH=9,DAY_OF_YEAR=283,DAY_OF_WEEK=6,DAY_OF_WEEK_IN_MONTH=2,AM_PM=1,HOUR=4,HOUR_OF_DAY=16,MINUTE=57,SECOND=39,MILLISECOND=225,ZONE_OFFSET=28800000,DST_OFFSET=0]
*/

可以这样认为,@Configuration注解@Bean注解为用户创造自己规定的实例化对象放入到IoC容器中提供了可能。

要注意的是,用@Bean注解获得的对象所对应的类不需要@Component注解,你都已经用方法的方式得到对象实例了,就不需要通过扫描并且反射机制去。

细心的读者可能发现了,我们对于带参数的有@Bean注解的方法并没有进行相关代码的编写。是的,因为IoC比较困难的问题之一就在这里,带参数的方法和循环依赖问题都在这里混着,我阐述相关观点又要许多文字,不希望把博文写的太长,这样会引起你们的阅读疲劳,所以有关问题放在下一篇博文。我一定保证下篇会非常精彩,希望对模拟Spring有兴趣的同学一定要看!

循环依赖问题的解决

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值