`
pi88dian88
  • 浏览: 40076 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

简单记录spring 实现IOC的流程

阅读更多

IOC: Inversion of Control, 指由spring容器来生成对象并且完成对象的装配。

 

下面来看下这两个问题:(下面是以 ClassPathXmlApplicationContext为起点)

1, spring如何生成对象?

2, spring如何完成对象组装?

 

完成这个的核心是通过BeanFactory,下面是BeanFactory的代码:

public interface BeanFactory {	
	String FACTORY_BEAN_PREFIX = "&";

	Object getBean(String name) throws BeansException;

	<T> T getBean(String name, Class<T> requiredType) throws BeansException;

	<T> T getBean(Class<T> requiredType) throws BeansException;

	Object getBean(String name, Object... args) throws BeansException;

	boolean containsBean(String name);

	boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

	boolean isTypeMatch(String name, Class<?> targetType) throws NoSuchBeanDefinitionException;

	Class<?> getType(String name) throws NoSuchBeanDefinitionException;

	String[] getAliases(String name);
}

 从上面可知,如果有了BeanFactory,就可以通过getBean的方法去获取想要的对象,后面组装的事情也就顺理成章了。

 

下面是简单的spring的demo,代码可以通过附件下载,部分测试代码如下:

public class SpringDemo {
	public static void main(String[] args) {
		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath*:/beans.xml");
		AppleService appleService = context.getBean("appleService", AppleService.class);
		Apple apple = appleService.createApple();
		System.out.println(apple);
	}
}

 

代码的入口从ClassPathXmlApplicationContext开始,下面是ClassPathXmlApplicationContext的代码:

public ClassPathXmlApplicationContext(String[] paths, Class clazz, ApplicationContext parent)
			throws BeansException {
		super(parent);
		Assert.notNull(paths, "Path array must not be null");
		Assert.notNull(clazz, "Class argument must not be null");
		this.configResources = new Resource[paths.length];
		for (int i = 0; i < paths.length; i++) {
			this.configResources[i] = new ClassPathResource(paths[i], clazz);
		}
		refresh();
	}

调用父类AbstractApplicationContext的refresh方法,

public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.
				initMessageSource();

				// Initialize event multicaster for this context.
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				onRefresh();

				// Check for listener beans and register them.
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				finishRefresh();
			}

			catch (BeansException ex) {
				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();

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

				// Propagate exception to caller.
				throw ex;
			}
		}
	}

 在obtainFreshBeanFactory方法中,创建DefaultListableBeanFactory,然后通过DocumentBuilderFactory将spring配置文件转成Document对象,然后根据不同的节点名进行处理,解析元素代码如下:

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
                //IMPORT_ELEMENT = "import"
		if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
			importBeanDefinitionResource(ele);
		}
                //ALIAS_ELEMENT = "alias"
		else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
			processAliasRegistration(ele);
		}
                //BEAN_ELEMENT="bean"
		else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
			processBeanDefinition(ele, delegate);
		}
                //NESTED_BEANS_ELEMENT = "beans"
		else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
			// recurse
			doRegisterBeanDefinitions(ele);
		}
	}

 以bean节点解析为例,看下parseBeanDefinitionElement的方法

 

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
                //解析bean节点
		BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
		if (bdHolder != null) {
			bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
			try {
				// Register the final decorated instance.
                                //将BeanDefinition注册到BeanFactory中(后面有解释)
				BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
			}
			catch (BeanDefinitionStoreException ex) {
				getReaderContext().error("Failed to register bean definition with name '" +
						bdHolder.getBeanName() + "'", ele, ex);
			}
			// Send registration event.
			getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
		}
	}

 下面是调用BeanDefinitionParserDelegate的parseBeanDefinitionAttributes方法,将xml配置的bean的属性(lazy-init, scope, destroy-method等属性)设置到BeanDefinition中。

 

 

public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
			BeanDefinition containingBean, AbstractBeanDefinition bd) {

		if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
			// Spring 2.x "scope" attribute
			bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
			if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
				error("Specify either 'scope' or 'singleton', not both", ele);
			}
		}
		else if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
			// Spring 1.x "singleton" attribute
			bd.setScope(TRUE_VALUE.equals(ele.getAttribute(SINGLETON_ATTRIBUTE)) ?
					BeanDefinition.SCOPE_SINGLETON : BeanDefinition.SCOPE_PROTOTYPE);
		}
		else if (containingBean != null) {
			// Take default from containing bean in case of an inner bean definition.
			bd.setScope(containingBean.getScope());
		}

		if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
			bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
		}

		String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
		if (DEFAULT_VALUE.equals(lazyInit)) {
			lazyInit = this.defaults.getLazyInit();
		}
		bd.setLazyInit(TRUE_VALUE.equals(lazyInit));

		String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
		bd.setAutowireMode(getAutowireMode(autowire));

		String dependencyCheck = ele.getAttribute(DEPENDENCY_CHECK_ATTRIBUTE);
		bd.setDependencyCheck(getDependencyCheck(dependencyCheck));

		if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
			String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
			bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
		}

		String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);
		if ("".equals(autowireCandidate) || DEFAULT_VALUE.equals(autowireCandidate)) {
			String candidatePattern = this.defaults.getAutowireCandidates();
			if (candidatePattern != null) {
				String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);
				bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
			}
		}
		else {
			bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate));
		}

		if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
			bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
		}

		if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {
			String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);
			if (!"".equals(initMethodName)) {
				bd.setInitMethodName(initMethodName);
			}
		}
		else {
			if (this.defaults.getInitMethod() != null) {
				bd.setInitMethodName(this.defaults.getInitMethod());
				bd.setEnforceInitMethod(false);
			}
		}

		if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
			String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);
			if (!"".equals(destroyMethodName)) {
				bd.setDestroyMethodName(destroyMethodName);
			}
		}
		else {
			if (this.defaults.getDestroyMethod() != null) {
				bd.setDestroyMethodName(this.defaults.getDestroyMethod());
				bd.setEnforceDestroyMethod(false);
			}
		}

		if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
			bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
		}
		if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
			bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
		}

		return bd;
	}

 在上面的processBeanDefinition方法中,解析完某个节点,生成BeanDefinition时,会调用BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry())方法,然后向BeanFactory注册该BeanDefinition对象,代码如下:

 

 

//DefaultListableBeanFactory.java
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {

		...
		synchronized (this.beanDefinitionMap) {
			Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
			if (oldBeanDefinition != null) {
				if (!this.allowBeanDefinitionOverriding) {
					throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
							"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
							"': There is already [" + oldBeanDefinition + "] bound.");
				}
			}
			else {
				this.beanDefinitionNames.add(beanName);
				this.frozenBeanDefinitionNames = null;
			}
			this.beanDefinitionMap.put(beanName, beanDefinition);
		}

		resetBeanDefinition(beanName);
	}

 然后重新回到AbstractApplicationContext的refresh方法,调用invokeBeanFactoryPostProcessors(spring提供了BeanFactoryPostProcessor扩展点,可以通过实现BeanFactoryPostProcess来初始化某些参数),再通过调用refresh方法中的finishBeanFactoryInitialization来完成对象的创建。代码如下:

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));
		}

		// 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();

		// Instantiate all remaining (non-lazy-init) singletons.
                //创建并且组装对象
		beanFactory.preInstantiateSingletons();
	}

然后进入DefaultListableBeanFactory的preInstantiateSingletons方法,代码如下,beanDefinitionMap存放的是前面解析配置文件得到的<beanName, BeanDefinition>的map数据。

public void preInstantiateSingletons() throws BeansException {
		...
		List<String> beanNames;
		synchronized (this.beanDefinitionMap) {
			// 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.
			beanNames = new ArrayList<String>(this.beanDefinitionNames);
		}
		for (String beanName : beanNames) {
			RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
			if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
				if (isFactoryBean(beanName)) {
					final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
					boolean isEagerInit;
					if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
						isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
							public Boolean run() {
								return ((SmartFactoryBean<?>) factory).isEagerInit();
							}
						}, getAccessControlContext());
					}
					else {
						isEagerInit = (factory instanceof SmartFactoryBean &&
								((SmartFactoryBean<?>) factory).isEagerInit());
					}
					if (isEagerInit) {
						getBean(beanName);
					}
				}
				else {
					getBean(beanName);
				}
			}
		}
	}

 在getBean方法中,会调用AbstractBeanFactory的doGetBean来创建对象,方法如下:

protected <T> T doGetBean(
			final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
			throws BeansException {
                ...
		if (mbd.isSingleton()) {
			sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
				public Object getObject() throws BeansException {
					try {
						return createBean(beanName, mbd, args);
					}
					catch (BeansException ex) {
						// Explicitly remove instance from singleton cache: It might have been put there
						// eagerly by the creation process, to allow for circular reference resolution.
						// Also remove any beans that received a temporary reference to the bean.
						destroySingleton(beanName);
						throw ex;
					}
				}
			});
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
		}

		else if (mbd.isPrototype()) {
			// It's a prototype -> create a new instance.
			Object prototypeInstance = null;
			try {
				beforePrototypeCreation(beanName);
				prototypeInstance = createBean(beanName, mbd, args);
			}
			finally {
				afterPrototypeCreation(beanName);
			}
			bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
		}
                ...
		return (T) bean;
	}

 在createBean中,会调用AbstractAutowireCapableBeanFactory的doCreateBean方法,代码如下:(这个方法显示了对象的创建以及后续初始化的流程

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
		// Instantiate the bean.
		BeanWrapper instanceWrapper = null;
		if (mbd.isSingleton()) {
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
                //实例化对象
		if (instanceWrapper == null) {
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
		Class beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);

		// Allow post-processors to modify the merged bean definition.
		synchronized (mbd.postProcessingLock) {
			if (!mbd.postProcessed) {
                               //调用BeanPostProcessor处理
				applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
				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.isDebugEnabled()) {
				logger.debug("Eagerly caching bean '" + beanName +
						"' to allow for resolving potential circular references");
			}
			addSingletonFactory(beanName, new ObjectFactory() {
				public Object getObject() throws BeansException {
					return getEarlyBeanReference(beanName, mbd, bean);
				}
			});
		}

		// Initialize the bean instance.
		Object exposedObject = bean;
		try {
                        //设置bean的参数(通过property标签配置)
			populateBean(beanName, mbd, instanceWrapper);
			if (exposedObject != null) {
                                //调用init-method
				exposedObject = initializeBean(beanName, exposedObject, mbd);
			}
		}
		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);
			}
		}

		...
		return exposedObject;
	}

 继续往下看createBeanInstance方法,通过调用instantiateBean实例化对象,代码如下:

protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
		try {
			Object beanInstance;
			final BeanFactory parent = this;
			if (System.getSecurityManager() != null) {
				beanInstance = AccessController.doPrivileged(new PrivilegedAction<Object>() {
					public Object run() {
						return getInstantiationStrategy().instantiate(mbd, beanName, parent);
					}
				}, getAccessControlContext());
			}
			else {
				beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
			}
			BeanWrapper bw = new BeanWrapperImpl(beanInstance);
			initBeanWrapper(bw);
			return bw;
		}
		catch (Throwable ex) {
			throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
		}
	}

 InstantiationStrategy有两种,SimpleInstantiationStrategy和CglibSubclassingInstantiationStrategy。关系如下:

 

spring对CglibSubclassingInstantiationStrategy的使用场景注释:Using Method Injection features requires CGLIB on the classpath. However, the core IoC container will still run without CGLIB being available.

下面再来看SimpleInstantiationStrategy的instantiate方法,代码如下:

public Object instantiate(RootBeanDefinition beanDefinition, String beanName, BeanFactory owner) {
		// Don't override the class with CGLIB if no overrides.
		if (beanDefinition.getMethodOverrides().isEmpty()) {
			Constructor<?> constructorToUse;
			synchronized (beanDefinition.constructorArgumentLock) {
				constructorToUse = (Constructor<?>) beanDefinition.resolvedConstructorOrFactoryMethod;
				if (constructorToUse == null) {
					final Class clazz = beanDefinition.getBeanClass();
					if (clazz.isInterface()) {
						throw new BeanInstantiationException(clazz, "Specified class is an interface");
					}
					try {
						if (System.getSecurityManager() != null) {
							constructorToUse = AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor>() {
								public Constructor run() throws Exception {
									return clazz.getDeclaredConstructor((Class[]) null);
								}
							});
						}
						else {
							constructorToUse =	clazz.getDeclaredConstructor((Class[]) null);
						}
						beanDefinition.resolvedConstructorOrFactoryMethod = constructorToUse;
					}
					catch (Exception ex) {
						throw new BeanInstantiationException(clazz, "No default constructor found", ex);
					}
				}
			}
			return BeanUtils.instantiateClass(constructorToUse);
		}
		else {
			// Must generate CGLIB subclass.
			return instantiateWithMethodInjection(beanDefinition, beanName, owner);
		}
	}

 通过上面的流程,这样对象的创建就完成了。

 

受篇幅所限,只是介绍了对象的生成,对象的装配具体过程,下篇来写了~


 

 

 

 

  • 大小: 13 KB
0
0
分享到:
评论

相关推荐

    基于Spring Boot构建的HTTP请求日志记录和重试补偿工具(95分以上课程大作业).zip

    Java SSM项目是一种使用Java语言和SSM框架(Spring + Spring MVC + MyBatis)开发的Web应用程序。SSM是一种常用的Java开发框架组合,它结合了Spring框架、Spring MVC框架和MyBatis框架的优点,能够快速构建可靠、...

    Spring面试题

    因为 org.springframework.beans.factory.BeanFactory 是一个简单接口,所以可以针对各种底层存储方法实现。最常用的 BeanFactory 定义是 XmlBeanFactory,它根据 XML 文件中的定义装入 bean,如清单 1 所示。 清单...

    java开源项目:基于spring+springmvc+hibernate的在线招标项目(免费提供源码)

    Spring框架: 负责依赖注入、AOP编程等,提供了控制反转(IoC)和面向切面编程(AOP)的支持。 Spring MVC框架: 负责请求响应和路由管理,使得项目具有良好的MVC架构。 Hibernate框架: 负责对象关系映射(ORM),...

    车辆管理系统(struts+hibernate+spring+oracle)130225.rar

    Spring作为IoC容器,负责管理对象的生命周期和依赖关系,使得系统的各个模块能够灵活地组合和替换。该系统的主要功能包括:车辆信息的增删改查,包括车辆的类型、车牌号、颜色、购买日期等;用户信息的增删改查,...

    毕设绝技车辆车位租赁系统ssm开发方案

    一、项目背景与目标 随着城市化的快速发展,车辆数量急剧增加,车位租赁成为了一个迫切的需求。...Spring提供IoC和AOP功能,简化企业级应用开发流程;SpringMVC基于MVC设计模式,实现Web应用的快速开发;

    基于微信小程序的在线订餐系统的设计与实现+springboot框架.rar

    利用Spring框架实现了控制反转(IoC)和依赖注入,提高了代码的可维护性和可测试性。 使用Spring MVC实现了前端请求的分发和处理,将业务逻辑与视图层进行了有效分离,提高了系统的可扩展性。 结合Spring Data JPA...

    基于微信小程序的小说阅读器的实现+ssm框架.rar

    基于微信小程序的小说阅读器结合SSM(Spring + Spring MVC + MyBatis)框架的实现,为用户提供了便捷的在线小说阅读服务,具有以下特点: 微信小程序端: 用户友好的界面设计,符合微信小程序的交互规范,提供流畅...

    基于微信小程序的房屋租赁管理系统的设计与实现+ssm框架.rar

    使用Spring框架实现控制反转(IoC)和面向切面编程(AOP),提高了代码的可维护性和可测试性。 使用Spring MVC实现了前端请求的分发和处理,将业务逻辑与视图层进行了有效分离。 结合MyBatis框架实现了持久层与...

    weixin135房屋租赁管理系统的设计与实现+ssm--论文pf.rar

    “weixin135房屋租赁管理系统”的设计与实现基于SSM(Spring, Spring MVC, MyBatis)框架,这是一个典型的Java EE应用架构。SSM集成了Spring的依赖注入和面向切面编程、Spring MVC的MVC框架以及MyBatis的持久层框架...

    weixin183基于小程序宿舍报修系统的设计与实现ssm--论文pf.rar

    该系统采用SSM(Spring, SpringMVC, MyBatis)框架进行设计与实现,结合微信平台强大的用户基础和操作便利性,为学生提供了一个快速、简单的报修通道。 使用的技术主要包括: 微信小程序前端开发:利用微信官方...

    Java毕业设计-基于ssm框架开发的游戏美术外包管理信息系统--论文-附毕设源代码+说明文档.rar

    说明文档详细记录了系统的功能设计、数据库结构、关键代码实现以及可能遇到的问题和解决方案,为二次开发或定制提供了有力的指导。 总的来说,这份资源不仅是一份优秀的毕业设计作品,更是一份实用的学习资料和开发...

    基于SSM+Mysql的汽车租赁管理系统(vue).zip

    Spring框架提供了强大的IoC(控制反转)和AOP(面向切面编程)功能,简化了系统的配置和开发过程。SpringMVC作为一个基于MVC(模型-视图-控制器)设计模式的框架,提供了良好的请求分发和处理机制。MyBatis作为一个...

    基于SSM+Mysql的高速公路收费系统.zip

    Spring框架提供了IoC(控制反转)和AOP(面向切面编程)等功能,简化了系统的开发和配置。SpringMVC作为一个基于MVC(模型-视图-控制器)的框架,提供了良好的请求处理和页面跳转机制。MyBatis作为一个优秀的持久层...

    基于SSM+Mysql的高校学生请假管理系统.zip

    Spring框架提供了IoC(控制反转)和AOP(面向切面编程)等功能,简化了系统的开发和配置。SpringMVC作为一个基于MVC(模型-视图-控制器)的框架,提供了良好的请求处理和页面跳转机制。MyBatis作为一个优秀的持久层...

    基于SSM+Mysql的公司员工考勤管理系统.zip

    Spring框架提供了IoC(控制反转)和AOP(面向切面编程)等功能,简化了系统的开发和配置。SpringMVC作为一个基于MVC(模型-视图-控制器)的框架,提供了良好的请求处理和页面跳转机制。MyBatis作为一个优秀的持久层...

    基于SSM+Mysql的高校在线请假与审批系统.zip

    Spring框架提供了IoC(控制反转)和AOP(面向切面编程)等功能,简化了系统的开发和配置。SpringMVC作为一个基于MVC(模型-视图-控制器)的框架,提供了良好的请求处理和页面跳转机制。MyBatis作为一个优秀的持久层...

    基于SSM+Mysql的高校自习室预约系统.zip

    Spring框架提供了IoC(控制反转)和AOP(面向切面编程)等功能,简化了系统的开发和配置。SpringMVC作为一个基于MVC(模型-视图-控制器)的框架,提供了良好的请求处理和页面跳转机制。MyBatis作为一个优秀的持久层...

    基于SSM+Mysql的酒店管理系统.zip

    系统的后端采用SSM框架,Spring框架提供了IoC(控制反转)和AOP(面向切面编程)等功能,简化了系统的开发和配置。SpringMVC作为一个基于MVC(模型-视图-控制器)的框架,提供了良好的请求处理和页面跳转机制。...

    基于SSM+Mysql的高校就业管理系统.zip

    Spring框架提供了IoC(控制反转)和AOP(面向切面编程)等功能,简化了系统的开发和配置。SpringMVC作为一个基于MVC(模型-视图-控制器)的框架,提供了良好的请求处理和页面跳转机制。MyBatis作为一个优秀的持久层...

Global site tag (gtag.js) - Google Analytics