• 【Spring系列】- 手写模拟Spring框架


    简单模拟Spring

    😄生命不息,写作不止
    🔥 继续踏上学习之路,学之分享笔记
    👊 总有一天我也能像各位大佬一样
    🏆 一个有梦有戏的人 @怒放吧德德
    🌝分享学习心得,欢迎指正,大家一起学习成长!

    spring.jpg

    前言

    上次已经学习了Java的设计模式,接下来就先来学习一下如何手写模拟简易的Spring,通过动手实践,才会更好的了解spring底层原理,今天就简单的模拟Spring容器是如何创建,bean又是如何注入的。
    来看一下本次案例的spring类图
    image.png

    Spring容器

    模拟spring,首先就是需要一个容器,是Spring的核心,一切Spring bean都存储在Spring容器内,并由其通过IoC技术管理。Spring容器也就是一个bean工厂(BeanFactory)。应用中bean的实例化,获取,销毁等都是由这个bean工厂管理的。就像我们刚开始学习的时候接触的ApplicationContext,就是spring的容器,他就是为了完成容器的配置,初始化,管理bean的。因此笔者自己创建了一个LydApplicationContext来模拟简单的spring容器。

    开始使用

    首先通过new LydApplicationContext(AppConfig.class)实例化对象,在通过applicationContext.getBean("userService")去获得bean对象。然而在容器的初始化可是做了许多的事情,包括扫描、实例化bean等等操作。
    初始容器创建:

    public class LydApplicationContext {
        private Class configClass; 
        public LydApplicationContext(Class configClass) { // 构造方法
            this.configClass = configClass;
        }
    }
    

    Spring扫描底层实现

    Spring容器建好之后我们就需要通过配置文件的注解获取扫描路径,我们需要获取所有的bean,并且需要实例对象。在此我们需要一个配置文件,就是使用new LydApplicationContext(AppConfig.class) 实例携带的配置类,当然这里有好多的形式,也可以是通过xml文件来处理。

    配置文件AppConfig.java

    这个就是为了提供扫描的包路径的,不做任何操作,所以不需要其他代码。

    @ComponentScan("com.lyd.service") // 扫描路径,扫描这个包下的
    public class AppConfig {
    }
    

    通过注解存放这个包路径,在后面可以通过这个注解来获取包路径,所以就需要我们创建一个ComponentScan注解。

    编写ComponentScan注解

    这个注解是用来spring容器扫描包为之提供包路径。

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ComponentScan {
        // 指定扫描路径
        String value() default "";
    }
    

    编写Component注解

    在Spring中,通过Component注解将bean注入Spring容器中,这里我们也采用高这个注解。

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Component {
        String value() default "";
    }
    

    获取包路径

    既然已经通过注解将包路径存在配置类中,接下来就可以通过这个注解来得到。但是,在这里需要注意的是,我们扫描的并非是java源文件,而是编译后的class文件。我们需要在LydApplicationContext的构造方法中去实现。
    首先,我们需要通过isAnnotationPresent方法先判断是否存在ComponentScan注解,在通过类的getAnnotation方法来得到注解。这样就可以直接得到注解上的值。这个值就是我们写入的包路径,注意,这里的路径是com.lyd.service,而我们需要用替换方法将'.'替换成'/',因为在后面获取资源路径的时候,用的是com/lyd/service这种形式,也就是相对路径。
    接下来需要获取资源路径,这个时候就需要用到类加载器LydApplicationContext.class.getClassLoader(),类加载器中有一个getResource(path)方法,这个可以根据传入的路径获取相应的资源,最后是能够拼出我们需要的绝对路径。

    if (configClass.isAnnotationPresent(ComponentScan.class)) {
        ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
        // 1.1 扫描路径:只是个包名,扫描的是java的class文件,而并非源文件,com.lyd.service
        String path = componentScanAnnotation.value();
        // 1.2 将路径文件替换成/的形式
        path = path.replace(".","/");
        // 1.3 通过类加载器获取资源路径
        ClassLoader classLoader = LydApplicationContext.class.getClassLoader();
        URL resource = classLoader.getResource(path);
        // 1.4 转成文件形式,主要是为了获取他的绝对地址
        File file = new File(resource.getFile());
        System.out.println(file);
    }
    

    通过类加载器得到的资源有个获取File的方法,然后我们通过File file = new File(resource.getFile());将资源转成File类型,因为他可以表示一个地址或者是具体文件。扫描就是扫描文件路径,也就是文件夹。我们可以打印看一下这个地址:
    image.png

    判断是否为文件夹,如果是,那就获取里面的所有文件,在通过遍历这些文件,获取绝对路径。

    if (file.isDirectory()) { // 如果是文件夹
        File[] files = file.listFiles();
        for (File f : files) {
            String absolutePath = f.getAbsolutePath(); // 获取绝对路径
            System.out.println("绝对路径:" + absolutePath);
        }
    }
    

    我们可以打印出来看一下:
    image.png

    因为我们要的是编译的.class文件,因此需要在遍历文件的时候进行判断文件是否为.class文件。为了拿到这个类,需要通过全限定名使用类加载器获取类 ,也就是利用反射机制。我们都知道,spring是通过Component注解来将bean注入spring的,因此最后就是通过判断是否有这个注解来得到一个bean。

    for (File f : files) {
    	String absolutePath = f.getAbsolutePath(); // 获取绝对路径
    	System.out.println("绝对路径:" + absolutePath);
    	// 1.5 对编译文件进行处理
    	if (absolutePath.endsWith(".class")) { // 判断是否为编译文件
    		/**
    		 * 需要拿到的是编译文件,通过类加载器去获取
    		 * 需要将com\lyd\service\UserService转成com.lyd.service.UserService
    		 */
    		String className = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));
    		className = className.replace("\\", ".");
    		System.out.println("类名:" + className);
    		try {
    			// 1.6 通过全限定名使用类加载器获取类 (利用反射机制)
    			Class clazz = classLoader.loadClass(className);
    			// 1.7 在通过这个clazz(类)来判断是否有component注解,有则是bean
    			if (clazz.isAnnotationPresent(Component.class)) {
    				// 到这里就是一个bean
    			}
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    }
    

    最后我们需要的地址就是如下:
    image.png

    Spring生成BeanDefinition

    在我们Spring容器启动或者是扫描的时候,并不建议直接实例化bean对象,因为bean是区分单例和多例的,多例bean我们是需要用到的时候再去创建。这个时候就需要生成BeanDefinition,即bean的定义,这个类存储了类和作用域(单例还是多例)。

    定义Scope注解

    通过Scope这个注解来标明是单例bean还是多例bean。

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Scope {
        String value() default "";
    }
    

    定义BeanDefinition

    在spring中,不会在获取bean的时候再去解析是否为单例,而是通过BeanDefinition类来操作。对bean的定义,记录了bean类和作用域。

    public class BeanDefinition {
        private Class type;
        private String scope; // 单例多例
        public Class getType() {
            return type;
        }
        public void setType(Class type) {
            this.type = type;
        }
        public String getScope() {
            return scope;
        }
        public void setScope(String scope) {
            this.scope = scope;
        }
    }
    

    因为我们注入springbean对象是有Component注解,因此在扫描的时候,我们会通过这个获得到bean,在这时候去创建BeanDefinition对象。通过判断注解上的值来赋值其作用域,如果没有设置,就默认是单例模式。
    创建好的bean对象,我们还需要将他进行保存起来,这个就需要定义ConcurrentHashMap beanDefinitionMap = new ConcurrentHashMap<>();存储beanDefinition。key是component的值,如果component没有传入beanName的值,那就使用spring的命名规则,采用类名首字母小写。

    // 1.7 在通过这个clazz(类)来判断是否有component注解,有则是bean
    if (clazz.isAnnotationPresent(Component.class)) {
    	Component annotation = clazz.getAnnotation(Component.class);
    	String beanName = annotation.value();
        // * 默认生成bean,如果只使用Component注解,没有写上beanName的值,那么就需要自动生成
        if (beanName.equals("")) {
            // 默认开头小字母
            beanName = Introspector.decapitalize(clazz.getSimpleName());
        }
    	/**
    	 * 這就是一个bean了
    	 * 然而在这里并不是直接就创建bean了,bean分为了单例bean和多例bean
    	 */
    	BeanDefinition beanDefinition = new BeanDefinition();
    	beanDefinition.setType(clazz);
    	if (clazz.isAnnotationPresent(Scope.class)) { // 判断是单例还是多例
    		Scope scope = clazz.getAnnotation(Scope.class);
    		beanDefinition.setScope(scope.value());
    	} else {
    		beanDefinition.setScope("singleton");
    	}
        beanDefinitionMap.put(beanName, beanDefinition);
    }
    

    getBean底层

    通过传来一个beanName,通过beanDefinitionMap中获取key为beanName的BeanDefinition对象,进行判空处理。这样我们可以通过这个BeanDefinition对象去获取作用域,判断是否为单例。

    获取bean

    在此,我们创建单例的时候,是需要将单例保存起来的,需要定义ConcurrentHashMap singletonObjects = new ConcurrentHashMap<>();单例池来保存单例bean。然后在getBean的时候,如果是单例bean,就可以先去单例池中寻找,如果没找到,再去创建对象。而多例模式就需要每次都去创建。

    // 获取bean对象
    public Object getBean(String beanName) {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (beanDefinition == null) {
            throw new NullPointerException("找不到bean名字为[" + beanName + "]的bean对象");
        } else { // 找到就做相应的操作
            String scope = beanDefinition.getScope();
            if (scope.equals("singleton")) {
                // 通过单例池获取
                Object bean = singletonObjects.get(beanName);
                if (bean == null) {
                    // 单例池中如果没有bean,就需要去创建
                    bean = createBean(beanName, beanDefinition);
                    singletonObjects.put(beanName, bean);
                }
                return bean;
            } else {
                // 多例的就不需要记录,每次都是通过创建
                return createBean(beanName, beanDefinition);
            }
        }
    }
    

    创建bean

    利用反射机制,通过无参构造方法去获取实例,这里就是bean对象实例了。就可以直接返回。

    // 创建bean
    private Object createBean(String beanName, BeanDefinition definition) {
    	// 利用反射获取实例,采用无参构造方法
    	Class clazz = definition.getType();
    	// 通过无参构造方法获取实例
    	try {
    		Object instance = clazz.getConstructor().newInstance(); // 到这里直接返回,bean的对象也就创建完成
    
    		return instance;
    
    	} catch (InstantiationException e) {
    		e.printStackTrace();
    	}
    	return null;
    }
    

    在构造方法扫描后要根据生成的beanDefinitionMap去创建单例bean对象。

    // 2 创建单例bean对象
    for (String beanName : beanDefinitionMap.keySet()) {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (beanDefinition.getScope().equals("singleton")) { // 那么,如何保证单例呢?就需要有个单例池,singletonObjects
            Object bean = createBean(beanName, beanDefinition);
            singletonObjects.put(beanName, bean); // 将单例bean存到单例池中
        }
    }
    

    运行

    public class Test {
        public static void main(String[] args) {
            LydApplicationContext applicationContext = new LydApplicationContext(AppConfig.class);
            System.out.println(applicationContext.getBean("userService"));
            System.out.println(applicationContext.getBean("userService"));
            System.out.println(applicationContext.getBean("userService"));
            System.out.println(applicationContext.getBean("userService"));
            System.out.println(applicationContext.getBean("userService"));
        }
    }
    

    我们可以先使用单例模式进行测试,我们不加scope注解,并且获取多个bean,可以看到得到的bean对象是同一个。
    image.png
    多例的时候在scope标上值,就可以看到每次获取的bean对象都是不一样的。
    image.png

    Autowired自动依赖注入

    当我们在UserService中使用RoleService,我们就需要通过Autowired注解进行依赖注入。就是需要在createBean方法中,在创建实例之后,获取类中的字段,进行依赖注入,要通过判断Autowried注解。通过字段的set方法,将bean对象注入,而这个对象如何获得呢?那就是用过getBean()方法。

    // 3 依赖注入
    for (Field field : clazz.getDeclaredFields()) {
        // 判断字段上是否存在Autowried注解
        if (field.isAnnotationPresent(Autowired.class)) {
            /**
             * 值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。
             * 值为 false 则指示反射的对象应该实施 Java 语言访问检查;
             * 实际上setAccessible是启用和禁用访问安全检查的开关,并不是为true就能访问为false就不能访问 ;
             */
            field.setAccessible(true); // 反射需要设置这个,不然无法赋值
            // 用其属性名,这就意味着private RoleService roleService;roleService不能乱取
            field.set(instance, getBean(field.getName()));
        }
    }
    

    Aware回调机制与初始化

    Aware回调

    那如果我们需要获取当前bean的名字呢?那就得通过Aware回调机制。我们需要创建一个BeanNameAware接口,里面提供一个setBeanName方法。

    public interface BeanNameAware {
        public void setBeanName(String beanName);
    }
    

    createBean方法中去编写回调机制,通过判断这个实例是否有BeanNameAware这个类,通过setName方法间接传递了beanName。

    // 4 Aware回调机制
    if (instance instanceof BeanNameAware) {
        ((BeanNameAware)instance).setBeanName(beanName);
    }
    

    UserService方法中去实现这个BeanNameAware方法,这就能够在UserService里的beanName字段中得到这个bean对象的真实的beanName了。

    @Component("userService") // 注入spring
    @Scope("property")
    public class UserService implements BeanNameAware, InitializingBean {
        @Autowired
        private RoleService roleService; // 依赖注入,加上@Autowired注解
    
        private String beanName;
    
        @Override
        public void setBeanName(String beanName) {
            this.beanName = beanName;
        }
    
        public void test() {
            System.out.println("RoleService: " + roleService);
        }
    
        @Override
        public void afterPropertiesSet() {
            // ......
            System.out.println("初始化");
        }
    }
    

    初始化

    在spring容器中,不只是完成bean对象的创建,还需要能够对bean进行初始化。需要创建InitializingBean接口类。

    public interface InitializingBean {
        public void afterPropertiesSet();
    }
    

    在需要进行初始化的bean对象去实现这个接口的方法,这里main可以进行一些操作。在createBean中,会根据判断是否有InitializingBean类,会在实例化之后调用这个方法进行初始化。

    // 5 初始化
    if (instance instanceof InitializingBean) {
        ((InitializingBean)instance).afterPropertiesSet();
    }
    

    结果
    image.png

    BeanPostProcessor

    BeanPostProcessor可以对spring中bean的创建去做一些操作。

    BeanPostProcessor接口

    在spring中定义一个BeanPostProcessor接口,里面会有初始化前后操作的方法,并且将beanNamebean对象带入进行自定义的操作。

    public interface BeanPostProcessor {
        public Object postBeforeProcessor(String beanName, Object bean); // 初始化前
        public Object postAfterProcessor(String beanName, Object bean); // 初始化后
    }
    

    自定义BeanPostProcessor

    这里可以定义自己的类去实现spring中的BeanPostProcessor,对初始化进行一些相应操作。并且能够根据某个bean对象来做不同的操作。原理就是将MyBeanPostProcessor注入到容器中,在扫描的时候将这个对象保存起来,在创建bean的时候去遍历BeanPostProcessor集合,在去调用这个实例的方法。

    @Component // 需要注入spring
    public class MyBeanPostProcessor implements BeanPostProcessor {
        @Override
        public Object postBeforeProcessor(String beanName, Object bean) {
            System.out.println("初始化前的bean:" + beanName + " -- " + bean);
            return bean;
        }
    
        @Override
        public Object postAfterProcessor(String beanName, Object bean) {
            System.out.println("初始化后的bean:" + beanName + " -- " + bean);
            return bean;
        }
    }
    

    然而,在spring扫描的时候会进行操作,因为自己实现的BeanPostProcessor是通过Component注解注入spring容器的。因此可以通过判断有Component注解时候,进行判断是否含有BeanPostProcessor类,如果有生成BeanPostProcessor对象,并且将其实例添加到beanPostProcessorList容器中。在此就需要定义ArrayList beanPostProcessorList = new ArrayList<>();来记录BeanPostProcessor。

    // 6 判断是否是并加入beanPostProcessorList,这里不能使用instanceof
    if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
        // 直接生成对象
        BeanPostProcessor instance = (BeanPostProcessor) clazz.newInstance();
        // 然后保存进去
        beanPostProcessorList.add(instance);
    }
    

    接着就可以在创建bean对象的时候,在初始化前后去遍历这个BeanPostProcessor链表,调用相应的方法,就能够调用自定义的MyBeanPostProcessor的方法。

    // 6 BeanPostProcessor 初始化前 AOP 遍历beanPostProcessorList
    for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
        instance = beanPostProcessor.postBeforeProcessor(beanName, instance);
    }
    
    // 5 初始化
    if (instance instanceof InitializingBean) {
        ((InitializingBean)instance).afterPropertiesSet();
    }
    
    // 6 BeanPostProcessor 初始化后 AOP
    for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
        instance = beanPostProcessor.postAfterProcessor(beanName, instance);
    }
    

    运行结果:.
    image.png

    AOP机制

    需要通过代理对象。这样如果不进行操作,返回的对象还是原来的,如果是通过操作了,那么返回的就是代理的对象。这里就简单的打印一句话。在扫描的时候,扫到userService这个bean的时候,返回的实例就是代理对象。

    @Override
    public Object postAfterProcessor(String beanName, Object bean) {
        System.out.println("初始化后的bean:" + beanName + " -- " + bean);
    
        if (beanName.equals("userService")) {
            // 创建一个代理对象, 代理的是UserInterface这个
            Object proxyInstance = Proxy.newProxyInstance(MyBeanPostProcessor.class.getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {
                @Override
                /**
                 * proxy:代理对象
                 * method:代理对象当前正在执行的方法
                 */
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    System.out.println("切面逻辑");
                    return method.invoke(bean, args);
                }
            });
            return proxyInstance;
        }
        return bean;
    }
    

    源码备注

    spring

    LydApplicationContext

    public class LydApplicationContext {
        private Class configClass; // 注入的配置类
        private ConcurrentHashMap beanDefinitionMap = new ConcurrentHashMap<>(); // key是component名字
        private ConcurrentHashMap singletonObjects = new ConcurrentHashMap<>(); // 单例池
        private ArrayList beanPostProcessorList = new ArrayList<>(); // 记录BeanPostProcessor,在扫描的时候判断,使用
    
        public LydApplicationContext(Class configClass) { // 构造方法
            this.configClass = configClass;
            // spring容器创建之后
            // 1 扫描
            // 通过配置文件的注解获取扫描路径
            if (configClass.isAnnotationPresent(ComponentScan.class)) {
                ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
                // 1.1 扫描路径:只是个包名,扫描的是java的class文件,而并非源文件,com.lyd.service
                String path = componentScanAnnotation.value();
                // 1.2 将路径文件替换成/的形式
                path = path.replace(".","/");
                // 1.3 通过类加载器获取资源路径
                ClassLoader classLoader = LydApplicationContext.class.getClassLoader();
                URL resource = classLoader.getResource(path);
                // 1.4 转成文件形式,主要是为了获取他的绝对地址
                File file = new File(resource.getFile());
    //            System.out.println(file);
                if (file.isDirectory()) { // 如果是文件夹
                    File[] files = file.listFiles();
                    for (File f : files) {
                        String absolutePath = f.getAbsolutePath(); // 获取绝对路径
                        System.out.println("绝对路径:" + absolutePath);
                        // 1.5 对编译文件进行处理
                        if (absolutePath.endsWith(".class")) { // 判断是否为编译文件
                            /**
                             * 需要拿到的是编译文件,通过类加载器去获取
                             * 需要将com\lyd\service\UserService转成com.lyd.service.UserService
                             */
                            String className = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));
                            className = className.replace("\\", ".");
                            System.out.println("类名:" + className);
                            try {
                                // 1.6 通过全限定名使用类加载器获取类 (利用反射机制)
                                Class clazz = classLoader.loadClass(className);
                                // 1.7 在通过这个clazz(类)来判断是否有component注解,有则是bean
                                if (clazz.isAnnotationPresent(Component.class)) {
                                    // 6 判断是否是并加入beanPostProcessorList,这里不能使用instanceof
                                    if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
                                        // 直接生成对象
                                        BeanPostProcessor instance = (BeanPostProcessor) clazz.newInstance();
                                        // 然后保存进去
                                        beanPostProcessorList.add(instance);
                                    }
                                    Component annotation = clazz.getAnnotation(Component.class);
                                    String beanName = annotation.value();
                                    // * 默认生成bean,如果只使用Component注解,没有写上beanName的值,那么就需要自动生成
                                    if (beanName.equals("")) {
                                        // 默认开头小字母
                                        beanName = Introspector.decapitalize(clazz.getSimpleName());
                                    }
                                    /**
                                     * 這就是一个bean了
                                     * 然而在这里并不是直接就创建bean了,bean分为了单例bean和多例bean
                                     */
                                    BeanDefinition beanDefinition = new BeanDefinition();
                                    beanDefinition.setType(clazz);
                                    if (clazz.isAnnotationPresent(Scope.class)) { // 判断是单例还是多例
                                        Scope scope = clazz.getAnnotation(Scope.class);
                                        beanDefinition.setScope(scope.value());
                                    } else {
                                        beanDefinition.setScope("singleton");
                                    }
                                    beanDefinitionMap.put(beanName, beanDefinition);
                                }
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
            // 2 创建单例bean对象
            for (String beanName : beanDefinitionMap.keySet()) {
                BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
                if (beanDefinition.getScope().equals("singleton")) { // 那么,如何保证单例呢?就需要有个单例池,singletonObjects
                    Object bean = createBean(beanName, beanDefinition);
                    singletonObjects.put(beanName, bean); // 将单例bean存到单例池中
                }
            }
        }
    
        // 创建bean
        private Object createBean(String beanName, BeanDefinition definition) {
            // 利用反射获取实例,采用无参构造方法
            Class clazz = definition.getType();
            // 通过无参构造方法获取实例
            try {
                Object instance = clazz.getConstructor().newInstance(); // 到这里直接返回,bean的对象也就创建完成
    
                // 3 依赖注入
                for (Field field : clazz.getDeclaredFields()) {
                    // 判断字段上是否存在Autowried注解
                    if (field.isAnnotationPresent(Autowired.class)) {
                        /**
                         * 值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。
                         * 值为 false 则指示反射的对象应该实施 Java 语言访问检查;
                         * 实际上setAccessible是启用和禁用访问安全检查的开关,并不是为true就能访问为false就不能访问 ;
                         */
                        field.setAccessible(true); // 反射需要设置这个,不然无法赋值
                        // 用其属性名,这就意味着private RoleService roleService;roleService不能乱取
                        field.set(instance, getBean(field.getName()));
                    }
                }
                // * instanceof:是针对某个对象去判断是否实现某个类
                // 4 Aware回调机制
                if (instance instanceof BeanNameAware) {
                    ((BeanNameAware)instance).setBeanName(beanName);
                }
    
                // 6 BeanPostProcessor 初始化前 AOP 遍历beanPostProcessorList
                for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                    instance = beanPostProcessor.postBeforeProcessor(beanName, instance);
                }
    
                // 5 初始化
                if (instance instanceof InitializingBean) {
                    ((InitializingBean)instance).afterPropertiesSet();
                }
    
                // 6 BeanPostProcessor 初始化后 AOP
                for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                    instance = beanPostProcessor.postAfterProcessor(beanName, instance);
                }
    
                return instance;
    
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
            return null;
        }
    
        // 获取bean对象
        public Object getBean(String beanName) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            if (beanDefinition == null) {
                throw new NullPointerException("找不到bean名字为[" + beanName + "]的bean对象");
            } else { // 找到就做相应的操作
                String scope = beanDefinition.getScope();
                if (scope.equals("singleton")) {
                    // 通过单例池获取
                    Object bean = singletonObjects.get(beanName);
                    if (bean == null) {
                        // 单例池中如果没有bean,就需要去创建
                        bean = createBean(beanName, beanDefinition);
                        singletonObjects.put(beanName, bean);
                    }
                    return bean;
                } else {
                    // 多例的就不需要记录,每次都是通过创建
                    return createBean(beanName, beanDefinition);
                }
            }
        }
    }
    
    

    service

    test

    public class Test {
        public static void main(String[] args) {
            LydApplicationContext applicationContext = new LydApplicationContext(AppConfig.class);
    //        UserService userService = (UserService) applicationContext.getBean("userService");
            UserInterface userService = (UserInterface) applicationContext.getBean("userService");
    //        System.out.println(applicationContext.getBean("userService"));
    //        System.out.println(applicationContext.getBean("roleService"));
            userService.test();
        }
    }
    

    👍创作不易,如有错误请指正,感谢观看!记得点赞哦!👍

  • 相关阅读:
    Redis性能测试
    Day23_8 Java学习之多线程安全、死锁及状态
    【腾讯云原生降本增效大讲堂】云原生混部技术标准解读
    Flutter开发- iOS 问题CocoaPods not installed or not in valid state
    【算法100天 | 18】回文链表的多种解法(JAVA实现)
    Apache反向代理&负载均衡
    湖北省制造业高质量发展专项资金政策!2022年各大类申报条件以及奖励补贴标准汇总
    开咖啡店怎样提高业绩?用这五招突破
    如何把图片转换成pdf格式?图片转PDF方法分享
    MySQL高级篇03【逻辑架构】
  • 原文地址:https://www.cnblogs.com/lyd-code/p/16886250.html