2 Star 2 Fork 2

Orange_Zh / OZAnnoIocDemo

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README
MulanPSL-2.0

OZAnnoIocDemo

介绍

实现简单的Spring的Ioc容器,包含容器初始化,Bean实例化,实现@Service、@Autowired、@Transactional、@Bean、@Value等注解

什么是IOC

IoC Inversion of Control (控制反转/反转控制),它是⼀个技术思想,不是⼀个技术实现

描述的事情:Java开发领域对象的创建,管理的问题

传统开发⽅式:⽐如类A依赖于类B,往往会在类A中new⼀个B的对象

IoC思想下开发⽅式:我们不⽤⾃⼰去new对象了,⽽是由IoC容器(Spring框架)去帮助我们实例化对象并且管理它,我们需要使⽤哪个对象,去问IoC容器要即可

我们丧失了⼀个权利(创建、管理对象的权利),得到了⼀个福利(不⽤考虑对象的创建、管理等⼀系列事情)

为什么叫做控制反转?

控制:指的是对象创建(实例化、管理)的权利

反转:控制权交给外部环境了(spring框架、IoC容器)

IoC解决对象之间的耦合问题

IOC和DI描述的是同⼀件事情,只不过⻆度不⼀样


IOC容器示例

什么是AOP

AOP: Aspect oriented Programming ⾯向切⾯编程/⾯向⽅⾯编程

AOP是OOP的延续,从OOP说起

OOP三⼤特征:封装、继承和多态

OOP是⼀种垂直继承体系,而AOP是一种纵向的体系

比如:想在多个业务方法运行前后记录日志,为了不影响业务代码那么就抽象出一个横切逻辑单读维护

手写实现IOC和AOP

案例实现了IOC容器的管理,包含容器初始化,Bean实例化,实现@Service、@Autowired、@Transactional、@Bean、@Value等注解,其中@Transactional就属于AOP的一种实现(动态代理)

  • 导入必要的依赖包
<dependencies>
        <!-- 单元测试Junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

        <!-- mysql数据库驱动包 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.21</version>
        </dependency>
        <!--druid连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.21</version>
        </dependency>

        <!-- servlet -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>

        <!-- jackson依赖 -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.6</version>
        </dependency>

        <!--dom4j依赖-->
        <dependency>
            <groupId>dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>1.6.1</version>
        </dependency>
        <!--xpath表达式依赖-->
        <dependency>
            <groupId>jaxen</groupId>
            <artifactId>jaxen</artifactId>
            <version>1.1.6</version>
        </dependency>
        <!--引入cglib依赖包-->
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.1_2</version>
        </dependency>
    </dependencies>
  • 创建数据库外部配置文件 jdbc.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ozdemo?characterEncoding=utf-8
jdbc.username=root
jdbc.password=123456
  • 创建数据库表
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for account
-- ----------------------------
DROP TABLE IF EXISTS `account`;
CREATE TABLE `account`  (
  `aid` int(11) NOT NULL AUTO_INCREMENT,
  `cardNo` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
  `money` int(11) NOT NULL DEFAULT 0,
  PRIMARY KEY (`aid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of account
-- ----------------------------
INSERT INTO `account` VALUES (1, '6029621011001', '韩梅梅', 10000);
INSERT INTO `account` VALUES (2, '6029621011000', '李大雷', 10000);

SET FOREIGN_KEY_CHECKS = 1;
  • 创建必要的注解类 @Service、@Autowired、@Transactional、@Bean、@Value、@Component等

@Transactional示例:其它类似

/**
 * @Description: @Transactional注解
 * @Author: Created by OrangeZh
 * @Date: Created in 2020/9/4 17:37
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
    String value() default "";

    String transactionManager() default "";
}
  • (AOP关建代码)创建动态代理工厂类 ProxyFactory和事务类 TransactionManager
/**
 * 代理对象工厂:生成代理对象的
 */
@Component("proxyFactory")
public class ProxyFactory {
    @Autowired
    private TransactionManager transactionManager;

    /**
     * Jdk动态代理
     *
     * @param obj 委托对象
     * @return 代理对象
     */
    public Object getJdkProxy(Object obj) {
        // 获取代理对象
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object result = null;
                try {
                    // 开启事务(关闭事务的自动提交)
                    transactionManager.beginTransaction();
                    result = method.invoke(obj, args);
                    // 提交事务
                    transactionManager.commit();
                } catch (Exception e) {
                    e.printStackTrace();
                    // 回滚事务
                    transactionManager.rollback();
                    // 抛出异常便于上层servlet捕获
                    throw e;
                }
                return result;
            }
        });
    }


    /**
     * 使用cglib动态代理生成代理对象
     *
     * @param obj 委托对象
     * @return
     */
    public Object getCglibProxy(Object obj) {
        return Enhancer.create(obj.getClass(), new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                Object result = null;
                try {
                    // 开启事务(关闭事务的自动提交)
                    transactionManager.beginTransaction();
                    result = method.invoke(obj, objects);
                    // 提交事务
                    transactionManager.commit();
                } catch (Exception e) {
                    e.printStackTrace();
                    // 回滚事务
                    transactionManager.rollback();
                    // 抛出异常便于上层servlet捕获
                    throw e;
                }
                return result;
            }
        });
    }
}

/**
 * 事务管理器类:负责手动事务的开启、提交、回滚
 */
@Component("transactionManager")
public class TransactionManager {

    @Autowired
    private ConnectionUtils connectionUtils;

    // 开启手动事务控制
    public void beginTransaction() throws SQLException {
        connectionUtils.getCurrentThreadConn().setAutoCommit(false);
    }

    // 提交事务
    public void commit() throws SQLException {
        connectionUtils.getCurrentThreadConn().commit();
    }

    // 回滚事务
    public void rollback() throws SQLException {
        connectionUtils.getCurrentThreadConn().rollback();
    }
}
  • (IOC关建代码)创建BeanFactory IOC容器 bean工厂
/**
 * @Description: IOC容器 bean工厂
 * @Author: Created by OrangeZh
 * @Date: Created in 2020/9/4 15:15
 */
public class BeanFactory {

    //外部配置文件配置信息
    private static Properties properties = new Properties();

    private static final String classpath = BeanFactory.class.getResource("/").getPath();

    //bean定义Map 处理存放bean的基本信息
    private static Map<String, Class<?>> beanDefinitionMap = new ConcurrentHashMap<>();

    //缓存bean 容器缓存(单例缓存池)
    private static ConcurrentHashMap<String, Object> beanMap = new ConcurrentHashMap<>();

    //扫描到的所有类
    private static List<Class<?>> classList = new ArrayList<>();

    static {
        initIoc();
    }

    /**
     * 初始化容器
     * 1、获取配置文件配置内容
     * 2、配置类(AppConfig)解析
     * 3、扫描包下的所有类
     * 4、遍历所有类,并放入到beanDefinitionMap中
     * 5、实例化并初始化所有bean
     */
    private static void initIoc() {
        try {
            //1、获取配置文件配置内容
            properties = getProperties("jdbc.properties");

            //2、配置类解析
            Class<?> configClass = AppConfig.class;
            String configBeanName = Introspector.decapitalize(configClass.getSimpleName());
            beanDefinitionMap.put(configBeanName, configClass);

            String[] scanPackages = configClass.getAnnotation(ComponentScan.class).value();

            //3、加载包下的所有类
            if (scanPackages.length > 0) {
                for (String scanPackage : scanPackages) {
                    getAllClasses(scanPackage, classList);
                }
            } else {
                getAllClasses("", classList);
            }

            //4、遍历所有类,并放入到beanDefinitionMap中
            if (classList.size() > 0) {
                for (Class<?> clazz : classList) {
                    String beanName = Introspector.decapitalize(clazz.getSimpleName());
                    //类注解
                    if (clazz.isAnnotationPresent(Component.class)) {
                        Component annotation = clazz.getAnnotation(Component.class);
                        if (!annotation.value().equals("")) {
                            beanName = annotation.value();
                        } else if (clazz.getInterfaces().length > 0) {
                            beanName = Introspector.decapitalize(clazz.getInterfaces()[0].getSimpleName());
                        }
                    } else if (clazz.isAnnotationPresent(Service.class)) {
                        Service annotation = clazz.getAnnotation(Service.class);
                        if (!annotation.value().equals("")) {
                            beanName = annotation.value();
                        } else if (clazz.getInterfaces().length > 0) {
                            beanName = Introspector.decapitalize(clazz.getInterfaces()[0].getSimpleName());
                        }
                    } else if (clazz.isAnnotationPresent(Repository.class)) {
                        Repository annotation = clazz.getAnnotation(Repository.class);
                        if (!annotation.value().equals("")) {
                            beanName = annotation.value();
                        }
                    } else {
                        continue;
                    }
                    beanDefinitionMap.put(beanName, clazz);
                }
            }

            //5、实例化并初始化所有bean
            //先初始化配置类
            getBean(configBeanName);
            for (String beanName : beanDefinitionMap.keySet()) {
                getBean(beanName);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 创建bean
     * 1、实例化Bean
     * 2、初始化Bean
     *
     * @param beanName
     * @throws Exception
     */
    private static Object doCreateBean(String beanName) throws Exception {
        Object bean = beanMap.get(beanName);
        if (bean != null) {
            return bean;
        }
        createBeanInstance(beanName);
        populateBean(beanName);
        return beanMap.get(beanName);
    }

    /**
     * 实例化Bean
     *
     * @param beanName
     * @throws Exception
     */
    private static void createBeanInstance(String beanName) throws Exception {
        Class<?> clazz = beanDefinitionMap.get(beanName);
        //实例化
        Object bean = clazz.newInstance();
        if (clazz.isAnnotationPresent(Transactional.class)) {
            //转为代理对象
            ProxyFactory proxyFactory = (ProxyFactory) getBean("proxyFactory");
            if (clazz.getInterfaces().length > 0) {
                bean = proxyFactory.getJdkProxy(bean);//代理对象替换原对象
            } else {
                bean = proxyFactory.getCglibProxy(bean);//代理对象替换原对象
            }
        }
        beanMap.put(beanName, bean);
    }

    /**
     * 初始化Bean
     * 解析 @Autowired、@Value、@Transactional等注解
     *
     * @param beanName
     */
    private static void populateBean(String beanName) throws Exception {
        Object bean = getBean(beanName);
        Class<? extends Object> clazz = bean.getClass();
        //自动注入
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field field : declaredFields) {
            Annotation[] annotations = field.getAnnotations();
            for (Annotation annotation : annotations) {
                if (annotation.annotationType().equals(Autowired.class)) {
                    //Autowired autowiredAnno = field.getAnnotation(Autowired.class);
                    //通过属性名称 获取bean
                    Object value = getBean(field.getName());
                    field.setAccessible(true);
                    //给属性赋值
                    field.set(bean, value);
                } else if (annotation.annotationType().equals(Value.class)) {
                    Value valueAnno = field.getAnnotation(Value.class);
                    String value = valueAnno.value();
                    if (value.startsWith("${") && value.endsWith("}")) {
                        value = properties.getProperty(value.split("\\$\\{")[1].split("}")[0]);
                    }
                    field.setAccessible(true);
                    //给属性赋值
                    field.set(bean, value);
                }
            }
        }

        //方法注解
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            Annotation[] annotations = method.getAnnotations();
            for (Annotation annotation : annotations) {
                if (annotation.annotationType().equals(Transactional.class)) {
                    //转为代理对象
                    ProxyFactory proxyFactory = (ProxyFactory) getBean("proxyFactory");
                    Class<?>[] cl = clazz.getInterfaces();
                    if (clazz.getInterfaces().length > 0) {
                        bean = proxyFactory.getJdkProxy(bean);//代理对象替换原对象
                    } else {
                        bean = proxyFactory.getCglibProxy(bean);//代理对象替换原对象
                    }
                    beanMap.put(beanName, bean);
                } else if (annotation.annotationType().equals(Bean.class)) {
                    Bean beanAnno = method.getAnnotation(Bean.class);
                    Class<?> returnClass = method.getReturnType();
                    String beanName1 = Introspector.decapitalize(returnClass.getSimpleName());
                    if (!beanAnno.value().equals("")) {
                        beanName1 = beanAnno.value();
                    }
                    //获取方法执行返回的对象,返回的就是bean
                    Object beanClass = method.invoke(bean);
                    beanMap.put(beanName1, beanClass);
                }
            }
        }
    }

    /**
     * 获取bean
     *
     * @param beanName
     * @return
     */
    public static Object getBean(String beanName) throws Exception {
        Object bean = beanMap.get(beanName);
        if (bean == null) {
            bean = doCreateBean(beanName);
        }
        return bean;
    }

    /**
     * 获取某个包下的所有类
     *
     * @param classes     存放类的list
     * @param packageName 包名
     * @return
     */
    private static void getAllClasses(String packageName, List<Class<?>> classes) {
        try {
            //将包名转换为路径
            String path = classpath + packageName.replaceAll("\\.", Matcher.quoteReplacement(File.separator));
            File file = new File(path);
            File[] fileList = file.listFiles();
            if (null != fileList && fileList.length > 0) {
                for (File curFile : fileList) {
                    if (curFile.isFile()) {
                        System.out.println("file is:" + curFile);
                        System.out.println(packageName + "." + curFile.getName().split("\\.")[0]);
                        Class<?> aClass = Class.forName(packageName + "." + curFile.getName().split("\\.")[0]);
                        classes.add(aClass);
                    }
                    if (curFile.isDirectory()) {
                        System.out.println("dir is :" + curFile);
                        getAllClasses(packageName + "." + curFile.getName(), classes);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 读取配置文件
     *
     * @return
     */
    private static Properties getProperties(String... files) {
        Properties properties = new Properties();
        try {
            if (files.length > 0) {
                for (String file : files) {
                    InputStream inputStream = BeanFactory.class.getClassLoader().getResourceAsStream(file);
                    InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "UTF-8");
                    properties.load(inputStreamReader);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("Get properties is fail!");
        }
        return properties;
    }

Spring源码构建

  • 下载源码(github)
  • 安装gradle 5.6.3(类似于maven) Idea Jdk 8+
  • 导⼊(耗费⼀定时间)
  • 编译⼯程(顺序:core-oxm-context-beans-aspects-aop) ⼯程—>tasks—>compileTestJava
  • 运行可能出现的错误:https://zhuanlan.zhihu.com/p/86606269?from_voters_page=true

Spring IOC源码分析

1、进入源码前准备TestBean和测试用例 此次源码分析主要以Bean初始化为主线

public class ItBean {

	private TestBean testBean;

	public void setLagouBean(TestBean testBean) {
		this.testBean = testBean;
	}

	/**
	 * 构造函数
	 */
	public ItBean(){
	    System.out.println("ItBean 构造器...");
	}
}

public class TestBean implements InitializingBean, ApplicationContextAware {

	private ItBean itBean;

	public void setItBean(ItBean itBean) {
		this.itBean = itBean;
	}

	/**
	 * 构造函数
	 */
	public TestBean(){
	    System.out.println("TestBean 构造器...");
	}
}

2、ClasspathXmlApplicationContext 为例,进入源码流程

/**
 * ApplicationContext是容器的高级接口,BeanFactory(顶级容器/根容器,规范了/定义了容器的基础行为)
 * Spring应用上下文,官方称之为 IoC容器(错误的认识:容器就是map而已;准确来说,map是ioc容器的一个成员,叫做单例池, singletonObjects,容器是一组组件和过程的集合,包括BeanFactory、单例池、BeanPostProcessor等以及之间的协作流程)
 * 
 * Ioc容器创建管理Bean对象的,Spring Bean是有生命周期的
 * 构造器执行、初始化方法执行、Bean后置处理器的before/after方法、:AbstractApplicationContext#refresh#finishBeanFactoryInitialization
 * Bean工厂后置处理器初始化、方法执行:AbstractApplicationContext#refresh#invokeBeanFactoryPostProcessors
 * Bean后置处理器初始化:AbstractApplicationContext#refresh#registerBeanPostProcessors
 */
 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
 TestBean testBean = applicationContext.getBean(TestBean.class);

3、在TestBean 构造器方法处打断点,看在哪个方法对Bean进行了初始化

Bean初始化调用方法路径

由上图可看出调用的方法路径

4、org.springframework.context.support.ClassPathXmlApplicationContext#ClassPathXmlApplicationContext(java.lang.String[], boolean, org.springframework.context.ApplicationContext)

public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)throws BeansException {
        super(parent);
	setConfigLocations(configLocations);
        //是否刷新容器
	if (refresh) {
            //初始化容器
	    refresh();
	}
}

5、org.springframework.context.support.AbstractApplicationContext#refresh

	@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// 第⼀步:刷新前的预处理
			prepareRefresh();

			// 第⼆步:获取BeanFactory;默认实现是DefaultListableBeanFactory,加载BeanDefinition 并注册到 BeanDefinitionRegistry
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// 第三步:BeanFactory的预准备⼯作(BeanFactory进⾏⼀些设置,⽐如context的类加载器等)
			prepareBeanFactory(beanFactory);

			try {
				// 第四步:BeanFactory准备⼯作完成后进⾏的后置处理⼯作
				postProcessBeanFactory(beanFactory);

				// 第五步:实例化并调⽤实现了BeanFactoryPostProcessor接⼝的Bean
				invokeBeanFactoryPostProcessors(beanFactory);

				// 第六步:注册BeanPostProcessor(Bean的后置处理器),在创建bean的前后等执⾏
				registerBeanPostProcessors(beanFactory);

				// 第七步:初始化MessageSource组件(做国际化功能;消息绑定,消息解析);
				initMessageSource();

				// 第⼋步:初始化事件派发器
				initApplicationEventMulticaster();

				// 第九步:⼦类重写这个⽅法,在容器刷新的时候可以⾃定义逻辑
				onRefresh();

				// 第⼗步:注册应⽤的监听器。就是注册实现了ApplicationListener接⼝的监听器bean
				registerListeners();

				// 第⼗⼀步:初始化所有的⾮懒加载的单例bean实例(未设置属性)
				//填充属性
				//初始化⽅法调⽤(⽐如调⽤afterPropertiesSet⽅法、init-method⽅法)
				//调⽤BeanPostProcessor(后置处理器)对实例bean进⾏后置处
				finishBeanFactoryInitialization(beanFactory);

				// 第⼗⼆步:完成context的刷新。主要是调⽤LifecycleProcessor的onRefresh()⽅法,并且发布事件(ContextRefreshedEvent)
				finishRefresh();
			} catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}
				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();

				// Reset 'active' flag.
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}
			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
			}
		}
	}

6、org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization 初始化Bean

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
		// Initialize conversion service for this context.
		if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
				beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
			beanFactory.setConversionService(
					beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
		}

		// Register a default embedded value resolver if no bean post-processor
		// (such as a PropertyPlaceholderConfigurer bean) registered any before:
		// at this point, primarily for resolution in annotation attribute values.
		if (!beanFactory.hasEmbeddedValueResolver()) {
			beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
		}

		// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
		String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
		for (String weaverAwareName : weaverAwareNames) {
			getBean(weaverAwareName);
		}

		// Stop using the temporary ClassLoader for type matching.
		beanFactory.setTempClassLoader(null);

		// Allow for caching all bean definition metadata, not expecting further changes.
		beanFactory.freezeConfiguration();

		// 实例化所有剩余(非惰性init)单例
		beanFactory.preInstantiateSingletons();
	}

7、org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons 实例化所有剩余(非惰性init)单例

@Override
	public void preInstantiateSingletons() throws BeansException {
		if (logger.isTraceEnabled()) {
			logger.trace("Pre-instantiating singletons in " + this);
		}

		// Iterate over a copy to allow for init methods which in turn register new bean definitions.
		// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
		List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

		// 实例化所有剩余的(非延迟初始化)单例Bean,beanDefinitionNames在创建BeanFactory时就缓存了
		for (String beanName : beanNames) {
			RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
			if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { //判断是否是非抽象类,是否是单例,是否是非懒加载
				if (isFactoryBean(beanName)) { //判断是否是FactoryBean,是的话要加 & 前缀获取
					Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
					if (bean instanceof FactoryBean) {
						final FactoryBean<?> factory = (FactoryBean<?>) bean;
						boolean isEagerInit;
						if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
							isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
											((SmartFactoryBean<?>) factory)::isEagerInit,
									getAccessControlContext());
						}
						else {
							isEagerInit = (factory instanceof SmartFactoryBean &&
									((SmartFactoryBean<?>) factory).isEagerInit());
						}
						if (isEagerInit) {
							getBean(beanName);
						}
					}
				}
				else { //获取Bean
					getBean(beanName);
				}
			}
		}

		// Trigger post-initialization callback for all applicable beans...
		for (String beanName : beanNames) {
			Object singletonInstance = getSingleton(beanName);
			if (singletonInstance instanceof SmartInitializingSingleton) {
				final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
				if (System.getSecurityManager() != null) {
					AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
						smartSingleton.afterSingletonsInstantiated();
						return null;
					}, getAccessControlContext());
				}
				else {
					smartSingleton.afterSingletonsInstantiated();
				}
			}
		}
	}

8、org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String) 从容器中获取Bean 9、org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

getSingleton()方法从单例池获取Bean doGetBean()中先从单例池中获取,如果没有再创建,并放入单例池中,再获取

10、org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition, java.lang.Object[]) 创建Bean实例

11、org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean 创建Bean实例,填充Bean属性

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
			throws BeanCreationException {

		// 实例化Bean
		BeanWrapper instanceWrapper = null;
		if (mbd.isSingleton()) {
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		if (instanceWrapper == null) {
			instanceWrapper = createBeanInstance(beanName, mbd, args); //创建Bean实例
		}
		final Object bean = instanceWrapper.getWrappedInstance();
		Class<?> beanType = instanceWrapper.getWrappedClass();
		if (beanType != NullBean.class) {
			mbd.resolvedTargetType = beanType;
		}

		// Allow post-processors to modify the merged bean definition.
		synchronized (mbd.postProcessingLock) {
			if (!mbd.postProcessed) {
				try {
					applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
				}
				catch (Throwable ex) {
					throw new BeanCreationException(mbd.getResourceDescription(), beanName,
							"Post-processing of merged bean definition failed", ex);
				}
				mbd.postProcessed = true;
			}
		}

		// Eagerly cache singletons to be able to resolve circular references
		// even when triggered by lifecycle interfaces like BeanFactoryAware.
		boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
		if (earlySingletonExposure) {
			if (logger.isTraceEnabled()) {
				logger.trace("Eagerly caching bean '" + beanName +
						"' to allow for resolving potential circular references");
			}
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

		// 初始化Bean实例
		Object exposedObject = bean;
		try {
			populateBean(beanName, mbd, instanceWrapper); //填充Bean属性,循环依赖(三级缓存逻辑也就从此处开始)
			exposedObject = initializeBean(beanName, exposedObject, mbd); //初始化Bean
		}
		catch (Throwable ex) {
			if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
				throw (BeanCreationException) ex;
			}
			else {
				throw new BeanCreationException(
						mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
			}
		}

		if (earlySingletonExposure) {
			Object earlySingletonReference = getSingleton(beanName, false);
			if (earlySingletonReference != null) {
				if (exposedObject == bean) {
					exposedObject = earlySingletonReference;
				}
				else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
					String[] dependentBeans = getDependentBeans(beanName);
					Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
					for (String dependentBean : dependentBeans) {
						if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
							actualDependentBeans.add(dependentBean);
						}
					}
					if (!actualDependentBeans.isEmpty()) {
						throw new BeanCurrentlyInCreationException(beanName,
								"Bean with name '" + beanName + "' has been injected into other beans [" +
								StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
								"] in its raw version as part of a circular reference, but has eventually been " +
								"wrapped. This means that said other beans do not use the final version of the " +
								"bean. This is often the result of over-eager type matching - consider using " +
								"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
					}
				}
			}
		}

		// Register bean as disposable.
		try {
			registerDisposableBeanIfNecessary(beanName, bean, mbd);
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
		}

		return exposedObject;
	}

Spring循环依赖

1、发生在populateBean方法:填充属性,这一步主要是对bean的依赖属性进行注入(@Autowired), 此时会出现A依赖于B,B又依赖于A这种情况;

2、三级缓存:

  • singletonObjects:用于存放完全初始化好的 bean,从该缓存中取出的 bean 可以直接使用
  • earlySingletonObjects:提前曝光的单例对象的cache,存放原始的 bean 对象(尚未填充属性),用于解决循环依赖
  • singletonFactories:单例对象工厂的cache,存放 bean 工厂对象,用于解决循环依赖

3、getSingleton方法:

  • 先从一级缓存singletonObjects中去获取。(如果获取到就直接return)
  • 如果获取不到或者对象正在创建中(isSingletonCurrentlyInCreation()),那就再从二级缓存earlySingletonObjects中获取。(如果获取到就直接return)
  • 如果还是获取不到,且允许singletonFactories(allowEarlyReference=true)通过getObject()获取。就从三级缓存singletonFactory.getObject()获取。(如果获取到了就从singletonFactories中移除,并且放进earlySingletonObjects, 其实也就是从三级缓存移动到了二级缓存)

循环依赖方法执行

Spring Aop源码分析

1、编写配置类SpringConfig、AopTestBean和AopTestAspect

/**
 * @Description: 配置类
 * @Author: Created by OrangeZh
 * @Date: Created in 2020/11/25 15:00
 */
@Configuration
@ComponentScan("com.ozdemo")
@EnableAspectJAutoProxy //开启spring对注解AOP的⽀持
public class SpringConfig {
}

/**
 * @Description: Aop测试对象
 * @Author: Created by OrangeZh
 * @Date: Created in 2020/11/25 14:38
 */
@Component
public class AopTestBean {

	public void test(){
		System.out.println("AopTestBean.test方法执行了");
	}
}

/**
 * @Description: 切面类
 * @Author: Created by OrangeZh
 * @Date: Created in 2020/11/25 14:40
 */
@Component
@Aspect
public class AopTestAspect {
	@Pointcut("execution(* com.ozdemo.test.AopTestBean.*(..))")
	public void pointcut(){
	}
	@Before("pointcut()")
	public void before() {
		System.out.println("before method ......");
	}

	@After("pointcut()")
	public void after() {
		System.out.println("after method ......");
	}
}

2、编写测试用例进入源码

@Test
public void testAOP() {
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
    AopTestBean aopTestBean = applicationContext.getBean(AopTestBean.class);
    aopTestBean.test();
}

Aop动态代理Bean

由上图我们可以看出getBean 之前,AopTestBean 对象已经产⽣(即在第⼀⾏初始化代码中完成),⽽且该对象是⼀个代理对象(Cglib代理对象),我们断定,容器初始化过程中⽬标Ban已经完成了代理,返回了代理对象。

3、org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object, org.springframework.beans.factory.support.RootBeanDefinition) 初始化Bean 这一步的源码就接着IOC的11步继续了

	/**
	 *
	 * 初始化Bean
	 * 包括Bean后置处理器初始化
	 * Bean的⼀些初始化方法的执行init-method
	 * AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization
	 * Bean的实现的声明周期相关接口的属性注入
	 */
	protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
		if (System.getSecurityManager() != null) {
			AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
				invokeAwareMethods(beanName, bean);
				return null;
			}, getAccessControlContext());
		}
		else {
			// 执⾏所有的AwareMethods
			invokeAwareMethods(beanName, bean);
		}

		Object wrappedBean = bean;
		if (mbd == null || !mbd.isSynthetic()) {
			// 执⾏所有的BeanPostProcessor#postProcessBeforeInitialization 初始化之前的处理器方法
			wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
		}

		try {
			// 这⾥就开始执行afterPropertiesSet(实现了InitializingBean接口)方法和initMethod
			invokeInitMethods(beanName, wrappedBean, mbd);
		}
		catch (Throwable ex) {
			throw new BeanCreationException(
					(mbd != null ? mbd.getResourceDescription() : null),
					beanName, "Invocation of init method failed", ex);
		}
		if (mbd == null || !mbd.isSynthetic()) {
			// 整个Bean初始化完成,执⾏后置处理器⽅法
			wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
		}

		return wrappedBean;
	}

4、org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization 遍历执行后置处理器 5、org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization 创建代理对象的后置处理器

@Override
	public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
		if (bean != null) {
			// 检查下该类是否已经暴露过了(可能已经创建了,⽐如A依赖B时,创建A时候,就会先去创建B,当真正需要创建B时,就没必要再代理⼀次已经代理过的对象),避免重复创建
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			if (this.earlyProxyReferences.remove(cacheKey) != bean) {
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		return bean;
	}

6、org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNecessary

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
		if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
			return bean;
		}
		if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
			return bean;
		}
		if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
			this.advisedBeans.put(cacheKey, Boolean.FALSE);
			return bean;
		}

		//得到所有候选Advisor,对Advisors和bean的⽅法双层遍历匹配,最终得到⼀个List<Advisor>,即specificInterceptors
		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
		if (specificInterceptors != DO_NOT_PROXY) {
			this.advisedBeans.put(cacheKey, Boolean.TRUE);
			//创建代理
			Object proxy = createProxy(
					bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
			this.proxyTypes.put(cacheKey, proxy.getClass());
			return proxy;
		}

		this.advisedBeans.put(cacheKey, Boolean.FALSE);
		return bean;
	}

7、org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#createProxy 为指定 bean 创建代理对象

protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
			@Nullable Object[] specificInterceptors, TargetSource targetSource) {

		if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
			AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
		}

		//创建代理工厂
		ProxyFactory proxyFactory = new ProxyFactory();
		proxyFactory.copyFrom(this);

		//根据⼀些情况判断是否要设置proxyTargetClass=true
		if (!proxyFactory.isProxyTargetClass()) {
			if (shouldProxyTargetClass(beanClass, beanName)) {
				proxyFactory.setProxyTargetClass(true);
			}
			else {
				evaluateProxyInterfaces(beanClass, proxyFactory);
			}
		}

		// 把指定和通⽤拦截对象合并, 并都适配成Advisor
		Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
		proxyFactory.addAdvisors(advisors);
		proxyFactory.setTargetSource(targetSource);
		customizeProxyFactory(proxyFactory);

		proxyFactory.setFrozen(this.freezeProxy);
		if (advisorsPreFiltered()) {
			proxyFactory.setPreFiltered(true);
		}

		//创建代理对象
		return proxyFactory.getProxy(getProxyClassLoader());
	}

8、org.springframework.aop.framework.ProxyFactory#getProxy(java.lang.ClassLoader)

public Object getProxy(@Nullable ClassLoader classLoader) {
        //创建aop代理对象  提供了两种:jdk proxy, cglib   
	return createAopProxy().getProxy(classLoader);
}

org.springframework.aop.framework.ProxyCreatorSupport#createAopProxy 创建代理对象,jdk proxy, cglib org.springframework.aop.framework.DefaultAopProxyFactory#createAopProxy

/**
	 * 这里决定创建代理对象是用JDK Proxy,还是⽤ Cglib 了,最简单的从使用方面使⽤来说:设置proxyTargetClass=true强制使⽤Cglib 代理,什么参数都不设并且对象类实现了接口则默认用JDK 代 理,如果没有实现接⼝则也必须⽤Cglib
	 * @param config the AOP configuration in the form of an
	 * AdvisedSupport object
	 * @return
	 * @throws AopConfigException
	 */
	@Override
	public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
		if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
			Class<?> targetClass = config.getTargetClass();
			if (targetClass == null) {
				throw new AopConfigException("TargetSource cannot determine target class: " +
						"Either an interface or a target is required for proxy creation.");
			}
			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
				return new JdkDynamicAopProxy(config);
			}
			return new ObjenesisCglibAopProxy(config);
		}
		else {
			return new JdkDynamicAopProxy(config);
		}
	}

9、org.springframework.aop.framework.CglibAopProxy#getProxy(java.lang.ClassLoader) 创建CglibAopProxy,⽣成代理类,并且创建⼀个代理类的实例

木兰宽松许可证, 第2版 木兰宽松许可证, 第2版 2020年1月 http://license.coscl.org.cn/MulanPSL2 您对“软件”的复制、使用、修改及分发受木兰宽松许可证,第2版(“本许可证”)的如下条款的约束: 0. 定义 “软件”是指由“贡献”构成的许可在“本许可证”下的程序和相关文档的集合。 “贡献”是指由任一“贡献者”许可在“本许可证”下的受版权法保护的作品。 “贡献者”是指将受版权法保护的作品许可在“本许可证”下的自然人或“法人实体”。 “法人实体”是指提交贡献的机构及其“关联实体”。 “关联实体”是指,对“本许可证”下的行为方而言,控制、受控制或与其共同受控制的机构,此处的控制是指有受控方或共同受控方至少50%直接或间接的投票权、资金或其他有价证券。 1. 授予版权许可 每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的版权许可,您可以复制、使用、修改、分发其“贡献”,不论修改与否。 2. 授予专利许可 每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的(根据本条规定撤销除外)专利许可,供您制造、委托制造、使用、许诺销售、销售、进口其“贡献”或以其他方式转移其“贡献”。前述专利许可仅限于“贡献者”现在或将来拥有或控制的其“贡献”本身或其“贡献”与许可“贡献”时的“软件”结合而将必然会侵犯的专利权利要求,不包括对“贡献”的修改或包含“贡献”的其他结合。如果您或您的“关联实体”直接或间接地,就“软件”或其中的“贡献”对任何人发起专利侵权诉讼(包括反诉或交叉诉讼)或其他专利维权行动,指控其侵犯专利权,则“本许可证”授予您对“软件”的专利许可自您提起诉讼或发起维权行动之日终止。 3. 无商标许可 “本许可证”不提供对“贡献者”的商品名称、商标、服务标志或产品名称的商标许可,但您为满足第4条规定的声明义务而必须使用除外。 4. 分发限制 您可以在任何媒介中将“软件”以源程序形式或可执行形式重新分发,不论修改与否,但您必须向接收者提供“本许可证”的副本,并保留“软件”中的版权、商标、专利及免责声明。 5. 免责声明与责任限制 “软件”及其中的“贡献”在提供时不带任何明示或默示的担保。在任何情况下,“贡献者”或版权所有者不对任何人因使用“软件”或其中的“贡献”而引发的任何直接或间接损失承担责任,不论因何种原因导致或者基于何种法律理论,即使其曾被建议有此种损失的可能性。 6. 语言 “本许可证”以中英文双语表述,中英文版本具有同等法律效力。如果中英文版本存在任何冲突不一致,以中文版为准。 条款结束 如何将木兰宽松许可证,第2版,应用到您的软件 如果您希望将木兰宽松许可证,第2版,应用到您的新软件,为了方便接收者查阅,建议您完成如下三步: 1, 请您补充如下声明中的空白,包括软件名、软件的首次发表年份以及您作为版权人的名字; 2, 请您在软件包的一级目录下创建以“LICENSE”为名的文件,将整个许可证文本放入该文件中; 3, 请将如下声明文本放入每个源文件的头部注释中。 Copyright (c) [Year] [name of copyright holder] [Software Name] is licensed under Mulan PSL v2. You can use this software according to the terms and conditions of the Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at: http://license.coscl.org.cn/MulanPSL2 THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. See the Mulan PSL v2 for more details. Mulan Permissive Software License,Version 2 Mulan Permissive Software License,Version 2 (Mulan PSL v2) January 2020 http://license.coscl.org.cn/MulanPSL2 Your reproduction, use, modification and distribution of the Software shall be subject to Mulan PSL v2 (this License) with the following terms and conditions: 0. Definition Software means the program and related documents which are licensed under this License and comprise all Contribution(s). Contribution means the copyrightable work licensed by a particular Contributor under this License. Contributor means the Individual or Legal Entity who licenses its copyrightable work under this License. Legal Entity means the entity making a Contribution and all its Affiliates. Affiliates means entities that control, are controlled by, or are under common control with the acting entity under this License, ‘control’ means direct or indirect ownership of at least fifty percent (50%) of the voting power, capital or other securities of controlled or commonly controlled entity. 1. Grant of Copyright License Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable copyright license to reproduce, use, modify, or distribute its Contribution, with modification or not. 2. Grant of Patent License Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable (except for revocation under this Section) patent license to make, have made, use, offer for sale, sell, import or otherwise transfer its Contribution, where such patent license is only limited to the patent claims owned or controlled by such Contributor now or in future which will be necessarily infringed by its Contribution alone, or by combination of the Contribution with the Software to which the Contribution was contributed. The patent license shall not apply to any modification of the Contribution, and any other combination which includes the Contribution. If you or your Affiliates directly or indirectly institute patent litigation (including a cross claim or counterclaim in a litigation) or other patent enforcement activities against any individual or entity by alleging that the Software or any Contribution in it infringes patents, then any patent license granted to you under this License for the Software shall terminate as of the date such litigation or activity is filed or taken. 3. No Trademark License No trademark license is granted to use the trade names, trademarks, service marks, or product names of Contributor, except as required to fulfill notice requirements in Section 4. 4. Distribution Restriction You may distribute the Software in any medium with or without modification, whether in source or executable forms, provided that you provide recipients with a copy of this License and retain copyright, patent, trademark and disclaimer statements in the Software. 5. Disclaimer of Warranty and Limitation of Liability THE SOFTWARE AND CONTRIBUTION IN IT ARE PROVIDED WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED. IN NO EVENT SHALL ANY CONTRIBUTOR OR COPYRIGHT HOLDER BE LIABLE TO YOU FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO ANY DIRECT, OR INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING FROM YOUR USE OR INABILITY TO USE THE SOFTWARE OR THE CONTRIBUTION IN IT, NO MATTER HOW IT’S CAUSED OR BASED ON WHICH LEGAL THEORY, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 6. Language THIS LICENSE IS WRITTEN IN BOTH CHINESE AND ENGLISH, AND THE CHINESE VERSION AND ENGLISH VERSION SHALL HAVE THE SAME LEGAL EFFECT. IN THE CASE OF DIVERGENCE BETWEEN THE CHINESE AND ENGLISH VERSIONS, THE CHINESE VERSION SHALL PREVAIL. END OF THE TERMS AND CONDITIONS How to Apply the Mulan Permissive Software License,Version 2 (Mulan PSL v2) to Your Software To apply the Mulan PSL v2 to your work, for easy identification by recipients, you are suggested to complete following three steps: i Fill in the blanks in following statement, including insert your software name, the year of the first publication of your software, and your name identified as the copyright owner; ii Create a file named “LICENSE” which contains the whole context of this License in the first directory of your software package; iii Attach the statement to the appropriate annotated syntax at the beginning of each source file. Copyright (c) [Year] [name of copyright holder] [Software Name] is licensed under Mulan PSL v2. You can use this software according to the terms and conditions of the Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at: http://license.coscl.org.cn/MulanPSL2 THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. See the Mulan PSL v2 for more details.

简介

实现简单的Spring的Ioc容器,包含容器初始化,Bean实例化,实现@Service、@Autowired、@Transactional、@Bean、@Value等注解 展开 收起
Java
MulanPSL-2.0
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Java
1
https://gitee.com/orangezh/OZAnnoIocDemo.git
git@gitee.com:orangezh/OZAnnoIocDemo.git
orangezh
OZAnnoIocDemo
OZAnnoIocDemo
master

搜索帮助

344bd9b3 5694891 D2dac590 5694891