在这里我要实现的是 Spring 的 IoC 和 AOP 的核心, 而且有关 IoC 的实现, 注解 + xml 能混合使用!
参考资料:
IoC: 控制反转 (Inversion of Control, 缩写为 IoC), 是面向对象编程中的一种设计原则, 可以用来减低计算机代码之间的耦合度. 其中最常见的方式叫做依赖注入 (Dependency Injection, 简称 DI), 还有一种方式叫 "依赖查找"(Dependency Lookup). 通过控制反转, 对象在被创建的时候, 由一个调控系统内所有对象的外界实体, 将其所依赖的对象的引用传递给它. 也可以说, 依赖被注入到对象中.
AOP:Aspect Oriented Programming 的缩写, 意为: 面向切面编程, 通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.
(以上是度娘给出的说法, 可以看到这样的说法很不容易让人理解, 过于官方化, 下面是我的理解)
IoC: 我们平时在写 Java 程序时, 总要通过 new 的方式来产生一个对象, 对象的生死存亡是与我们直接挂钩的, 我们需要它时, 就 new 一个, 不需要他时就通过 GC 帮我们回收; 控制反转的意思就是将对象的生死权交给第三方, 由第三方来生成和销毁对象, 而我们只在需要时从第三方手中取获取, 其余不管, 这样, 对象的控制权就在第三方手里, 我们只有使用权! 这就是所谓的控制反转!
在 Spring 中, 提供了 xml 文件配置和注解的方式来向第三方表明我们需要第三方帮我们创建什么对象, Spring 就是这个第三方! 它负责通过 xml 文件的解析或者包扫描的方式, 找到我们给出的映射关系, 利用反射机制, 在其内部帮我们 "new" 出对象, 再存储起来, 供我们使用!
AOP : 就是动态代理的体现, 在 Spring 中就是利用 JDKProxy 或者 CGLibProxy 技术, 对方法进行拦截!
比如说有一个叫做 fun() 的方法, 我们在其执行前后对其拦截:
就像这样, fun 看成是纵向的线, 那么就相当于用平面把这条线截断了!
有了上面的铺垫, 我们可以大概知道, Sping 的 IoC 和 AOP 可以帮我们创建并管理对象, 可以对对象的方法进行拦截, 那么这两个技术合在一起, 就可以达到自动帮我们创建, 管理, 并对对象进行拦截!
下面先给出我简化的 SpringIOC 容器框图:
模拟的 IoC 框图
这是我简化后的 IoC 框图, 实际上的 SpringIOC 是非常庞大的, 里面包含了许多接口, 以及继承关系, 它把要处理的事务区分的非常细腻, 将问题由大化小, 层层递减, 把面向接口, 高内聚低耦合体现的淋漓尽致.
Spring 提供了注解和 xml 方式的注入, 所以后面会有两个分支, 分别处理注解和 xml 文件的配置!
BeanFactory:
在别的地方说法是一个最底层容器, 其实不要被其 "误导", 在我这它仅仅只是一个接口, 只提供了最基础的一些方法, 而方法的具体实现就需要真正的高级容器了! 代码如下:
- public interface BeanFactory {
- String FACTORY_BEAN_PREFIX = "&";
- Object getBean(String var1) throws BeansException;
- <T> T getBean(String var1, Class<T> var2) throws BeansException;
- <T> T getBean(Class<T> var1) throws BeansException;
- Object getBean(String var1, Object... var2) throws BeansException;
- <T> T getBean(Class<T> var1, Object... var2) throws BeansException;
- boolean containsBean(String var1);
- boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;
- boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;
- boolean isTypeMatch(String var1, Class<?> var2) throws NoSuchBeanDefinitionException;
- Class<?> getType(String var1) throws NoSuchBeanDefinitionException;
- String[] getAliases(String var1);
- }
- (这里我直接挪用了 Spring 的源码, 由于是模拟实现, 所以后面只实现了其 getBean 的方法)
- ApplicationContext:
在别的地方的说法是一个高级容器, 其实, 它还是一个接口, 只不过在源码中其继承了许多接口 (核心还是 BeanFactory), 是一个集大成者, 提供了远比 BeanFactory 更多的功能. 但目前所要实现的核心暂时用不上它, 所以暂时留一个空接口吧...
- public interface ApplicationContext extends BeanFactory {
- // TODO
- }
说到这里, 就不能往下继续了, 因为在上面我们看到的, 所谓的 "容器", 仅仅是定义了接口, 完全不能装东西啊, 还有, 所谓的容器里又要装什么? 这里就要引入 Bean!
Bean:
其实就是 IoC 容器里存放的东西! 前面我说过, Spring 会根据我们给出的映射关系, 帮我们创建对象并存储起来, 那么是否这个对象就是 Bean? 是! 但也不是! 如果说 Spring 仅仅只是帮我们管理对象, 那么它的功能也太单一了, 那么, 现在不得不再次提到前面说过的 AOP!
前面说到 Spring 中的 AOP 使用了 JDKProxy 和 CGLibProxy 这两种代理技术, 这两种技术的目的都是产生代理对象, 对方法进行拦截. 那么, 是否这个代理对象就是我们的 Bean? 不完全是! Bean 其实是原对象 + 代理对象! 先不急, 看到后面就会明白!
下面介绍两种动态代理技术:
JDKProxy
使用 JDK 代理, 其所代理的类, 必须要实现某一个接口:
- @SuppressWarnings("unchecked")
- public <E> E getJDKProxy(Class<?> klass, Object tagObject) {
- return (E) Proxy.newProxyInstance(klass.getClassLoader(),
- klass.getInterfaces(),
- new InvocationHandler() {
- @Override
- public Object invoke(Object proxy, Method method, Object[] args)
- throws Throwable {
- // TODO 置前拦截, 可对参数 args 进行判断
- Object result = null;
- try {
- result = method.invoke(tagObject, args);
- } catch (Exception e) {
- // TODO 对异常进行拦截
- throw e;
- }
- // TODO 置后拦截, 可对方法返回值进行修改
- return result;
- }
- });
- }
使用 JDK 代理的话就不得不传入一个原始对象, 所以如果不考虑 CGLib 代理, 那么 Bean 就是原始对象 + 代理对象!
CGLibProxy:
使用 CGLib 代理, 是让被代理的类作为代理对象的父类, 故原类不能被 final 修饰, 也不能对 final 修饰的方法拦截!
以下是网上绝大多数人给出的用法:
- @SuppressWarnings("unchecked")
- public <E> E getCGLibProxy(Class<?> klass) {
- Enhancer enhancer = new Enhancer();
- enhancer.setSuperclass(klass); // 从这里可以明显看到, 让被代理的类作为了代理对象的父类
- enhancer.setCallback(new MethodInterceptor() {
- @Override
- public Object intercept(Object proxyObject, Method method, Object[] args, MethodProxy methodProxy)
- throws Throwable {
- // TODO 置前拦截, 可对参数 args 进行判断
- Object result = null;
- try {
- result = methodProxy.invokeSuper(proxyObject, args);
- } catch (Exception e) {
- // TODO 对异常进行拦截
- throw e;
- }
- // TODO 置后拦截, 可对方法返回值进行修改
- return result;
- }
- });
- return (E) enhancer.create();
- }
这种方式是没错, 但是不适用于后面要做的, 至于原因, 后面分析到了就会明白!
所以使用如下方式:
- @SuppressWarnings("unchecked")
- public <E> E getCGLibProxy(Class<?> klass, Object tagObject) {
- Enhancer enhancer = new Enhancer();
- enhancer.setSuperclass(klass);
- enhancer.setCallback(new MethodInterceptor() {
- @Override
- public Object intercept(Object proxyObject, Method method, Object[] args, MethodProxy methodProxy)
- throws Throwable {
- // TODO 置前拦截, 可对参数 args 进行判断
- Object result = null;
- try {
- result = method.invoke(tagObject, args);
- } catch (Exception e) {
- // TODO 对异常进行拦截
- throw e;
- }
- // TODO 置后拦截, 可对方法返回值进行修改
- return result;
- }
- });
- return (E) enhancer.create();
- }
由于是模拟实现, 后面就全部采用 CGLib 代理!
可以看到以上面这种方式进行 CGLib 代理就需要原始对象, 那么前面说到的 Bean 就必须是原对象 + 代理对象!
当然我知道以 invokeSuper 那种方式是不需要原始对象, 但是原因不是因为 Bean, 还在后面!
综上, Bean 的定义如下:
- public interface BeanElement {
- <E> E getProxy();
- Object getObject();
- boolean isDI(); // 用于以后判断是否完成注入
- }
在这里我把 BeanElement 定义为了一个接口, 后面会产生两个分支, 会产生两种不同处理方式的 Bean, 用接口更灵活, 耦合也低!
现在知道了 Bean 到底是什么, 我们就可以往下继续进行:
AbstractApplicationContext:
ApplicationContext 的具体实现类, 但它是一个抽象类, 只能实现部分功能, 往后在它的基础上还要分化成两支, 那么, 把所有的 Bean 存在这里面再合适不过了!
- public abstract class AbstractApplicationContext implements ApplicationContext {
- protected static final Map<String, String> beanNameMap; // key : id value : className
- protected static final Map<String, BeanElement> beanMap; // key : className value : Bean
- protected AopFactory aopFactory; // Aop 工厂, 生产代理对象
- static {
- beanNameMap = new HashMap<>();
- beanMap = new HashMap<>();
- }
- protected AbstractApplicationContext() {
- aopFactory = new AopFactory();
- // 设置切面
- aopFactory.setAdvice(
- (new AdviceHander())
- .setIntercepterFactory(new IntercepterLoader()));
- }
- protected void add(String id, String className, BeanElement bean) throws BeansException {
- if (beanMap.containsKey(className)) {
- // TODO 可以抛异常!
- return;
- }
- beanMap.put(className, bean);
- if (id.length() <= 0) return;
- if (beanNameMap.containsKey(id)) {
- throw new BeansException("bean:" + id + "已定义!");
- }
- beanNameMap.put(id, className);
- }
- }
其中的 aopFactory 是代理工厂, 负责生产代理, 会在后面给出, 先不急.
可以看到, AbstractApplicationContext 这个类持有了两张静态的 map, 第一组是用来存取 Bean 的别名 (id), 第二组用来存放真正的 Bean, 这就是我们真正意义上的容器, 由于其 map 都是 static 修饰的, 在类加载的时候就存在了, 所以往后有别的类继承它时, 这两个 map 是共享的! 只增加了一个 add 方法, 只要是继承自它的子类, 都会通过这种方式添加 Bean! 并且这个类是 protected 的, 不对外提供使用!
我们先进行左边的分支, 用注解的方式完成 IoC
这里说的注解都是自定义注解, 属于 RUNTIME, 就必须通过反射机制来获取, 反射机制就要得到类或者类名称, 那么就先得到符合要求的类, 这里就要用到包扫描, 我在前面的博客中有介绍过:[Java] 包, jar 包的扫描
首先是对类的注解:
- @Component
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.TYPE)
- public @interface Component {
- String id() default "";
- }
其中的 id 相当于类名称的别名, 具有唯一性, 如果不设置则不处理! 通过这个注解我们就可以判断哪个类是需要进行操作的, 就应该自动地为这个类生成一个对象和代理对象, 将其添加到 beanMap 中, 就是 bean 的标志!
- <bean id="XXX" class="XXX">
- ......
- </bean>
- @Autowired
- @Retention(RetentionPolicy.RUNTIME)
- @Target({ElementType.FIELD, ElementType.METHOD, ElementType.CONSTRUCTOR})
- public @interface Autowired {
- boolean requisite() default true;
- }
- <bean id="XXX" class="XXX">
- <property name="XXX" ref="XXX">
- </bean>
- <bean id="XXX" class="XXX">
- <constructor-arg index="0" ref="XXX">
- <constructor-arg index="1" ref="XXX">
- </bean>
- <bean id="XXX" class="XXX">
- <property name="XXX" ref="XXX">
- </bean>
- @Retention(RetentionPolicy.RUNTIME)
- @Target({ElementType.FIELD, ElementType.PARAMETER})
- public @interface Value {
- String value();
- }
- @Component
- public class TestA {
- @Autowired
- private TestB testB;
- @Value(value="直接赋值")
- private String member;
- public TestA() {
- }
- }
- @Component(id = "testB")
- public class TestB {
- private int a;
- private TestA testA;
- @Autowired
- public TestB(@Value(value="10")int a, TestA testA) {
- this.a = a;
- this.testA = testA;
- }
- }
- <bean class="xxx.xxx.TestA">
- <property name="testB" ref="xxx.xxx.TestB">
- <property name="member" value="直接赋值">
- </bean>
- <bean id="testB" class="xxx.xxx.TestB">
- <constructor-arg index="0" value="10"></constructor-arg>
- <constructor-arg index="1" ref="xxx.xxx.TestA"></constructor-arg>
- </bean>
- @Bean
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.METHOD)
- public @interface Bean {
- String id() default "";
- }
- @Component
- public class Action {
- @Bean(id="getDocumentBuilderFactory")
- public DocumentBuilderFactory getDocumnet() throws Exception {
- return DocumentBuilderFactory.newInstance();
- }
- @Bean
- public DocumentBuilder getDocumentBuilder(DocumentBuilderFactory factory) throws Exception {
- return factory.newDocumentBuilder();
- }
- }
- <bean class="xxx.xxx.Action"></bean>
- <bean class="javax.xml.parsers.DocumentBuilderFactory" id="getDocumentBuilderFactory"
- factory-method="newInstance"></bean>
- <bean class="javax.xml.parsers.DocumentBuilderFactory"
- factory-bean="getDocumentBuilderFactory" factory-method="newDocumentBuilder">
- </bean>
- /**
- * 执行包扫描
- * 将符合要求的结果添加到父类 AbstractApplicationContext 的 beanMap 中
- * 只处理 @Component 和 @Bean 注解
- * @Autowired 注解留给下一级处理
- */
- public class AnnotationContext extends AbstractApplicationContext {
- // method 缓冲区, 保存暂时不能执行的方法, 其中的 MethodBuffer 用来封装 method, 以及 invoke 时所需要的内容
- private List<MethodBuffer> methodList;
- protected AnnotationContext() {
- }
- protected AnnotationContext(String packageName) {
- scanPackage(packageName);
- }
- /**
- * 通过 aopFactory 产生代理对象, 将代理对象和原始对象封装成 bean 添加到父类的 map 中
- */
- private void addBean(Class<?> klass, Object object, String id, String className) {
- // aopFactory 是其父类 AbstractApplicationContext 的成员, 原来产生代理对象
- Object proxy = aopFactory.creatCGLibProxy(klass, object);
- // AnnoationBean 是 BeanElement 接口的一个实现类
- AnnotationBean bean = new AnnotationBean(object, proxy);
- // 父类 AbstractApplicationContext 的 add 方法
- add(id, className, bean);
- }
- protected void scanPackage(String packageName) {
- new PackageScanner() {
- @Override
- public void dealClass(Class<?> klass) {
- if (!klass.isAnnotationPresent(Component.class)) return;
- Component component = klass.getAnnotation(Component.class);
- String className = klass.getName();
- String name = component.id();
- try {
- // 这里简单起见, 不考虑构造方法的重载, 默认执行无参构造
- Object object = klass.newInstance();
- // 产生 BeanElement, 加入到 beanMap 中
- addBean(klass, object, name, className);
- // 处理带有 @Bean 注解的方法
- dealMethod(klass, object);
- } catch (Exception e) {
- // TODO
- e.printStackTrace();
- }
- }
- }.scanPackage(packageName);
- if (methodList == null) return;
- // 执行缓存的所有方法
- for (MethodBuffer methodBuffer : methodList) {
- // 获得方法执行所需要的东西
- String id = methodBuffer.getId();
- Class<?> returnClass = methodBuffer.getReturnClass();
- Method method = methodBuffer.getMethod();
- Object object = methodBuffer.getObject();
- Parameter[] parameters = methodBuffer.getParameters();
- try {
- dealMultiParaMethod(returnClass, method, object, parameters, id);
- } catch (Exception e) {
- // TODO
- e.printStackTrace();
- }
- }
- methodList.clear();
- }
- private void dealMultiParaMethod(Class<?> returnClass, Method method,
- Object object, Parameter[] parameters, String id)
- throws BeansException, IllegalAccessException, IllegalArgumentException,
- InvocationTargetException, ValueOnlyPrimitiveType {
- int count = parameters.length;
- Object[] result = new Object[count];
- for (int index = 0; index <count; index++) {
- Parameter para = parameters[index];
- // 判断参数是否带有 @Value 注解
- if (para.isAnnotationPresent(Value.class)) {
- Class<?> type = para.getType();
- // 判断 @Value 注解标识的参数是否是基本类型 (八大类型和 String)
- if (!type.isPrimitive() && !type.equals(String.class)) {
- throw new ValueOnlyPrimitiveType("Value 只能用基本类型!");
- }
- // TypeConversion 是我自己的一个工具类, 用于将字符串转换成基本类型!
- result[index] = TypeConversion.getValue(para.getAnnotation(Value.class).value(),
- para.getType().getSimpleName());
- } else {
- // 如果不是基本类型, 那么就需要从 beanMap 中获取符合要求的 bean
- result[index] = getBean(para.getType());
- }
- }
- // 执行方法, 获得方法返回值
- Object returnObject = method.invoke(object, result);
- // 为方法执行结果创建 bean, 添加到 beanMap 中
- addBean(returnClass, returnObject , id, returnClass.getName());
- }
- /**
- * 遍历所有方法, 处理带有 @Bean 注解的方法
- */
- private void dealMethod(Class<?> klass, Object object) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
- Method[] methods = klass.getDeclaredMethods();
- for (Method method : methods) {
- if (!method.isAnnotationPresent(Bean.class)) continue;
- Class<?> returnType = method.getReturnType();
- if (returnType.equals(void.class)) {
- // TODO 如果没有返回值, 那么根本没有必要做
- return;
- }
- String id= method.getAnnotation(Bean.class).id();
- Parameter[] parameters = method.getParameters();
- // 判断方法是否有参数, 没有参数, 直接执行, 添加 Bean
- // 有参数就先缓存起来, 等所有都扫描完成后再执行
- if (parameters.length <= 0) {
- Object returnObject = method.invoke(object);
- addBean(returnType, returnObject, id, returnType.getName());
- } else {
- (methodList = methodList == null ? new ArrayList<>() : methodList)
- .add(new MethodBuffer(returnType, object, method, parameters, id));
- }
- }
- }
- }
- /**
- * 注解分支中 BeanElement 的具体实现类
- */
- public class AnnotationBean implements BeanElement {
- private boolean DI; // 判断是否完成注入
- private Object object; // 原始对象
- private Object proxy; // 代理对象
- AnnotationBean() {
- this(false, null, null);
- }
- AnnotationBean(Object object, Object proxy) {
- this(false, object, proxy);
- }
- AnnotationBean(boolean dI, Object object, Object proxy) {
- DI = dI;
- setObject(object);
- setProxy(proxy);
- }
- @Override
- public Object getObject() {
- return object;
- }
- AnnotationBean setObject(Object object) {
- this.object = object;
- return this;
- }
- AnnotationBean setProxy(Object proxy) {
- this.proxy = proxy;
- return this;
- }
- void setDI(boolean DI) {
- this.DI = DI;
- }
- @Override
- public boolean isDI() {
- return DI;
- }
- @Override
- @SuppressWarnings("unchecked")
- public <E> E getProxy() {
- return (E) proxy;
- }
- }
- /**
- * 带有 @Bean 注解的方法封装类
- */
- public class MethodBuffer {
- private String id; // bean 的 id
- private Class<?> returnType; // 方法返回值类型
- private Object object; // 方法执行所需对象
- private Method method; // 方法本身
- private Parameter[] parameters; // 方法所需参数
- MethodBuffer() {
- }
- MethodBuffer(Class<?> returnType, Object object, Method method, Parameter[] parameters, String id) {
- this.returnType = returnType;
- this.object = object;
- this.method = method;
- this.parameters = parameters;
- this.id = id;
- }
- String getId() {
- return id;
- }
- Object getObject() {
- return object;
- }
- Class<?> getReturnType() {
- return returnType;
- }
- Method getMethod() {
- return method;
- }
- Parameter[] getParameters() {
- return parameters;
- }
- }
- public class AnnotationConfigApplicationContext extends AnnotationContext {
- public AnnotationConfigApplicationContext() {
- }
- // 调用父类的构造
- public AnnotationConfigApplicationContext(String packageName) {
- super(packageName);
- }
- // Advice 是代理的拦截处理, 内部使用默认的一种方式, 用户也可以注入一种方式
- public AnnotationConfigApplicationContext setAdvice(Advice advice) {
- aopFactory.setAdvice(advice);
- return this;
- }
- public AnnotationConfigApplicationContext parsePackage(String packageName) {
- scanPackage(packageName);
- return this;
- }
- @Override
- public Object getBean(String name) throws BeansException {
- String className = beanNameMap.get(name);
- return className == null ? get(name) : get(className);
- }
- private <T> T get(String className, Class<?> klass) throws BeansException {
- BeanElement bean = beanMap.get(className);
- if (bean == null) {
- throw new BeansException("Bean :" + klass + "不存在!");
- }
- if (!bean.isDI() && bean instanceof AnnotationBean) {
- autowired(klass, (AnnotationBean)bean);
- }
- return bean.getProxy();
- }
- @Override
- public <T> T getBean(Class<T> klass) throws BeansException {
- return get(klass.getName());
- }
- private void autowired(AnnotationBean bean) throws BeansException {
- // 一开始令自身完成注入
- bean.setDI(true);
- Object object = bean.getObject();
- Class<?> klass = object.getClass();
- Field[] fields = klass.getDeclaredFields();
- Object arg = null;
- for (Field field : fields) {
- if (field.isAnnotationPresent(Value.class)) {
- try {
- // 注入基本类型的值
- arg = injectValue(field);
- } catch (ValueOnlyPrimitiveType e) {
- e.printStackTrace();
- }
- } else if (field.isAnnotationPresent(Autowired.class)) {
- // 注入 bean 中的 Bean
- arg = injectBean(field);
- } else {
- continue;
- }
- try {
- // 成员注入
- field.setAccessible(true);
- field.set(object, arg);
- } catch (Exception e) {
- throw new BeansException(klass + "依赖关系不正确!");
- }
- }
- }
- private Object injectValue(Field field) throws ValueOnlyPrimitiveType {
- Class<?> type = field.getType();
- if (!type.isPrimitive() && !type.equals(String.class)) {
- throw new ValueOnlyPrimitiveType("Value 只能用于八大基本类型!");
- }
- Value value = field.getAnnotation(Value.class);
- return TypeConversion.getValue(value.value(), type.getSimpleName());
- }
- private Object injectBean(Field field) throws BeansException {
- Class<?> fieldType = field.getType();
- BeanElement fieldBean = beanMap.get(fieldType.getName());
- if (fieldBean == null) { return null;}
- if (!fieldBean.isDI() && fieldBean instanceof AnnotationBean) {
- autowired((AnnotationBean)fieldBean);
- }
- return fieldBean.getProxy();
- }
- }
- @Component(id="studentA")
- public class StudentA {
- @Value(value="我是 A")
- String name;
- @Autowired
- private StudentB B;
- public StudentA() {
- }
- @Override
- public String toString() {
- return "A:" + name + "->" + B;
- }
- }
- @Component
- public class StudentB {
- @Value(value="我是 B")
- private String name;
- @Autowired
- private StudentC C;
- public StudentB() {
- }
- @Override
- public String toString() {
- return "B:" + name + "->" + C;
- }
- }
- @Component
- public class StudentC {
- @Value(value="我是 C")
- private String name;
- @Autowired
- private StudentA A;
- public StudentC() {
- }
- @Override
- public String toString() {
- return "C:" + name;
- }
- }
- public class StudentD {
- private String name;
- @Autowired
- private StudentA A;
- public StudentD(String name) {
- this.name = name;
- }
- @Override
- public String toString() {
- return "D:" + name + "->" + A;
- }
- }
- @Component
- public class MethodAction {
- public MethodAction() {
- }
- @Bean(id="studentD")
- public StudentD getStudentD(@Value(value="我是 D")String name) {
- return new StudentD(name);
- }
- }
- public static void main(String[] args) throws BeansException {
- ApplicationContext applicationContext =
- new AnnotationConfigApplicationContext("com.zc.ioc.demo");
- StudentD studentD = applicationContext.getBean(StudentD.class);
- System.out.println(studentD);
- }
- public static void main(String[] args) throws BeansException {
- BeanFactory beanFactory = new AnnotationConfigApplicationContext("com.zc.ioc.demo");
- StudentD studentD = (StudentD) beanFactory.getBean("studentD");
- System.out.println(studentD);
- }
来源: https://www.cnblogs.com/a526583280/p/9866488.html