`
jinnianshilongnian
  • 浏览: 21499512 次
  • 性别: Icon_minigender_1
博客专栏
5c8dac6a-21dc-3466-8abb-057664ab39c7
跟我学spring3
浏览量:2417789
D659df3e-4ad7-3b12-8b9a-1e94abd75ac3
Spring杂谈
浏览量:3008093
43989fe4-8b6b-3109-aaec-379d27dd4090
跟开涛学SpringMVC...
浏览量:5638963
1df97887-a9e1-3328-b6da-091f51f886a1
Servlet3.1规范翻...
浏览量:259809
4f347843-a078-36c1-977f-797c7fc123fc
springmvc杂谈
浏览量:1597052
22722232-95c1-34f2-b8e1-d059493d3d98
hibernate杂谈
浏览量:250120
45b32b6f-7468-3077-be40-00a5853c9a48
跟我学Shiro
浏览量:5858161
Group-logo
跟我学Nginx+Lua开...
浏览量:701791
5041f67a-12b2-30ba-814d-b55f466529d5
亿级流量网站架构核心技术
浏览量:784902
社区版块
存档分类
最新评论

详解Spring事件驱动模型

阅读更多

 

事件驱动模型简介

事件驱动模型也就是我们常说的观察者,或者发布-订阅模型;理解它的几个关键点:

  1. 首先是一种对象间的一对多的关系;最简单的如交通信号灯,信号灯是目标(一方),行人注视着信号灯(多方);
  2. 当目标发送改变(发布),观察者(订阅者)就可以接收到改变;
  3. 观察者如何处理(如行人如何走,是快走/慢走/不走,目标不会管的),目标无需干涉;所以就松散耦合了它们之间的关系。

 

 

接下来先看一个用户注册的例子:



用户注册成功后,需要做这么多事:

1、加积分

2、发确认邮件

3、如果是游戏帐户,可能赠送游戏大礼包

4、索引用户数据

…………

问题:

  1. UserService和其他Service耦合严重,增删功能比较麻烦;
  2. 有些功能可能需要调用第三方系统,如增加积分/索引用户,速度可能比较慢,此时需要异步支持;这个如果使用Spring,可以轻松解决,后边再介绍;

 

从如上例子可以看出,应该使用一个观察者来解耦这些Service之间的依赖关系,如图:


 

增加了一个Listener来解耦UserService和其他服务,即注册成功后,只需要通知相关的监听器,不需要关系它们如何处理。增删功能非常容易。

 

这就是一个典型的事件处理模型/观察者,解耦目标对象和它的依赖对象,目标只需要通知它的依赖对象,具体怎么处理,依赖对象自己决定。比如是异步还是同步,延迟还是非延迟等。

 

上边其实也使用了DIP(依赖倒置原则),依赖于抽象,而不是具体。

 

还是就是使用了IoC思想,即以前主动去创建它依赖的Service,现在只是被动等待别人注册进来。

 

 

其他的例子还有如GUI中的按钮和动作的关系,按钮和动作本身都是一种抽象,每个不同的按钮的动作可能不一样;如“文件-->新建”打开新建窗口;点击“关闭”按钮关闭窗口等等。

 

主要目的是:松散耦合对象间的一对多的依赖关系,如按钮和动作的关系;

 

如何实现呢?面向接口编程(即面向抽象编程),而非面向实现。即按钮和动作可以定义为接口,这样它俩的依赖是最小的(如在Java中,没有比接口更抽象的了)。

 

有朋友会问,我刚开始学的时候也是这样:抽象类不也行吗?记住一个原则:接口目的是抽象,抽象类目的是复用;所以如果接触过servlet/struts2/spring等框架,大家都应该知道:

  • Servlet<-----GenericServlet<-----HttpServlet<------我们自己的
  • Action<------ActionSupport<------我们自己的
  • DaoInterface<------××DaoSupport<-----我们自己的

从上边大家应该能体会出接口、抽象类的主要目的了。现在想想其实很简单。

 

在Java中接口还一个非常重要的好处:接口是可以多实现的,类/抽象类只能单继承,所以使用接口可以非常容易扩展新功能(还可以实现所谓的mixin),类/抽象类办不到

 

Java GUI事件驱动模型/观察者 

扯远了,再来看看Java GUI世界里的事件驱动模型吧:

 

如果写过AWT/Swing程序,应该知道其所有组件都继承自java.awt.Component抽象类,其内部提供了addXXXListener(XXXListener l) 注册监听器的方法,即Component与实际动作之间依赖于XXXListener抽象。

 

比如获取焦点事件,很多组件都可以有这个事件,是我们知道组件获取到焦点后需要一个处理,虽然每个组件如何处理是特定的(具体的),但我们可以抽象一个FocusListener,让所有具体实现它然后提供具体动作,这样组件只需依赖于FocusListener抽象,而不是具体。

 

还有如java.awt.Button,提供了一个addActionListener(ActionListener l),用于注册点击后触发的ActionListener实现。

 

组件是一个抽象类,其好处主要是复用,比如复用这些监听器的触发及管理等。

 

JavaBean规范的事件驱动模型/观察者

JavaBean规范提供了JavaBean的PropertyEditorSupport及PropertyChangeListener支持。

 

PropertyEditorSupport就是目标,而PropertyChangeListener就是监听器,大家可以google搜索下,具体网上有很多例子。

 

Java提供的事件驱动模型/观察者抽象

JDK内部直接提供了观察者模式的抽象:

目标:java.util.Observable,提供了目标需要的关键抽象:addObserver/deleteObserver/notifyObservers()等,具体请参考javadoc。

观察者:java.util.Observer,提供了观察者需要的主要抽象:update(Observable o, Object arg),此处还提供了一种推模型(目标主动把数据通过arg推到观察者)/拉模型(目标需要根据o自己去拉数据,arg为null)。

 

因为网上介绍的非常多了,请google搜索了解如何使用这个抽象及推/拉模型的优缺点。

 

接下来是我们的重点:spring提供的事件驱动模型。

 

Spring提供的事件驱动模型/观察者抽象

首先看一下Spring提供的事件驱动模型体系图: 

事件

具体代表者是:ApplicationEvent:

1、其继承自JDK的EventObject,JDK要求所有事件将继承它,并通过source得到事件源,比如我们的AWT事件体系也是继承自它;

2、系统默认提供了如下ApplicationEvent事件实现:


只有一个ApplicationContextEvent,表示ApplicationContext容器事件,且其又有如下实现:

  • ContextStartedEvent:ApplicationContext启动后触发的事件;(目前版本没有任何作用)
  • ContextStoppedEvent:ApplicationContext停止后触发的事件;(目前版本没有任何作用)
  • ContextRefreshedEvent:ApplicationContext初始化或刷新完成后触发的事件;(容器初始化完成后调用)
  • ContextClosedEvent:ApplicationContext关闭后触发的事件;(如web容器关闭时自动会触发spring容器的关闭,如果是普通java应用,需要调用ctx.registerShutdownHook();注册虚拟机关闭时的钩子才行)

注:org.springframework.context.support.AbstractApplicationContext抽象类实现了LifeCycle的start和stop回调并发布ContextStartedEvent和ContextStoppedEvent事件;但是无任何实现调用它,所以目前无任何作用。

 

目标(发布事件者)

具体代表者是:ApplicationEventPublisher及ApplicationEventMulticaster,系统默认提供了如下实现:

1、ApplicationContext接口继承了ApplicationEventPublisher,并在AbstractApplicationContext实现了具体代码,实际执行是委托给ApplicationEventMulticaster(可以认为是多播):

	public void publishEvent(ApplicationEvent event) {
		//省略部分代码
		}
		getApplicationEventMulticaster().multicastEvent(event);
		if (this.parent != null) {
			this.parent.publishEvent(event);
		}
	}

我们常用的ApplicationContext都继承自AbstractApplicationContext,如ClassPathXmlApplicationContext、XmlWebApplicationContext等。所以自动拥有这个功能。

 

2、ApplicationContext自动到本地容器里找一个名字为”“的ApplicationEventMulticaster实现,如果没有自己new一个SimpleApplicationEventMulticaster。其中SimpleApplicationEventMulticaster发布事件的代码如下:

	public void multicastEvent(final ApplicationEvent event) {
		for (final ApplicationListener listener : getApplicationListeners(event)) {
			Executor executor = getTaskExecutor();
			if (executor != null) {
				executor.execute(new Runnable() {
					public void run() {
						listener.onApplicationEvent(event);
					}
				});
			}
			else {
				listener.onApplicationEvent(event);
			}
		}
	}

 大家可以看到如果给它一个executor(java.util.concurrent.Executor),它就可以异步支持发布事件了。佛则就是通过发送。

 

所以我们发送事件只需要通过ApplicationContext.publishEvent即可,没必要再创建自己的实现了。除非有必要。 

 

监听器

具体代表者是:ApplicationListener

1、其继承自JDK的EventListener,JDK要求所有监听器将继承它,比如我们的AWT事件体系也是继承自它;

2、ApplicationListener接口:

public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
	void onApplicationEvent(E event);
}

其只提供了onApplicationEvent方法,我们需要在该方法实现内部判断事件类型来处理,也没有提供按顺序触发监听器的语义,所以Spring提供了另一个接口,SmartApplicationListener:

public interface SmartApplicationListener extends ApplicationListener<ApplicationEvent>, Ordered {
        //如果实现支持该事件类型 那么返回true
	boolean supportsEventType(Class<? extends ApplicationEvent> eventType);
  
        //如果实现支持“目标”类型,那么返回true
	boolean supportsSourceType(Class<?> sourceType);
       
        //顺序,即监听器执行的顺序,值越小优先级越高
        int getOrder();
}

该接口可方便实现去判断支持的事件类型、目标类型,及执行顺序。 

 

Spring事件机制的简单例子

本例子模拟一个给多个人发送内容(类似于报纸新闻)的例子。

1、定义事件

package com.sishuok.hello;
import org.springframework.context.ApplicationEvent;
public class ContentEvent extends ApplicationEvent {
    public ContentEvent(final String content) {
        super(content);
    }
}

非常简单,如果用户发送内容,只需要通过构造器传入内容,然后通过getSource即可获取。

 

2、定义无序监听器

之所以说无序,类似于AOP机制,顺序是无法确定的。

package com.sishuok.hello;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
@Component
public class LisiListener implements ApplicationListener<ApplicationEvent> {
    @Override
    public void onApplicationEvent(final ApplicationEvent event) {
        if(event instanceof ContentEvent) {
            System.out.println("李四收到了新的内容:" + event.getSource());
        }
    }
}

1、使用@Compoent注册Bean即可;

2、在实现中需要判断event类型是ContentEvent才可以处理;

 

更简单的办法是通过泛型指定类型,如下所示

package com.sishuok.hello;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
@Component
public class ZhangsanListener implements ApplicationListener<ContentEvent> {
    @Override
    public void onApplicationEvent(final ContentEvent event) {
        System.out.println("张三收到了新的内容:" + event.getSource());
    }
}

 

3、定义有序监听器 

实现SmartApplicationListener接口即可。

package com.sishuok.hello;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.stereotype.Component;

@Component
public class WangwuListener implements SmartApplicationListener {

    @Override
    public boolean supportsEventType(final Class<? extends ApplicationEvent> eventType) {
        return eventType == ContentEvent.class;
    }
    @Override
    public boolean supportsSourceType(final Class<?> sourceType) {
        return sourceType == String.class;
    }
    @Override
    public void onApplicationEvent(final ApplicationEvent event) {
        System.out.println("王五在孙六之前收到新的内容:" + event.getSource());
    }
    @Override
    public int getOrder() {
        return 1;
    }
}
package com.sishuok.hello;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.stereotype.Component;

@Component
public class SunliuListener implements SmartApplicationListener {

    @Override
    public boolean supportsEventType(final Class<? extends ApplicationEvent> eventType) {
        return eventType == ContentEvent.class;
    }

    @Override
    public boolean supportsSourceType(final Class<?> sourceType) {
        return sourceType == String.class;
    }

    @Override
    public void onApplicationEvent(final ApplicationEvent event) {
        System.out.println("孙六在王五之后收到新的内容:" + event.getSource());
    }

    @Override
    public int getOrder() {
        return 2;
    }
}
  1. supportsEventType:用于指定支持的事件类型,只有支持的才调用onApplicationEvent;
  2. supportsSourceType:支持的目标类型,只有支持的才调用onApplicationEvent;
  3. getOrder:即顺序,越小优先级越高

 

4、测试 

4.1、配置文件

<context:component-scan base-package="com.sishuok"/>

 就一句话,自动扫描注解Bean。

 

4.2、测试类

package com.sishuok;
import com.sishuok.hello.ContentEvent;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:spring-config-hello.xml"})
public class HelloIT {

    @Autowired
    private ApplicationContext applicationContext;
    @Test
    public void testPublishEvent() {
        applicationContext.publishEvent(new ContentEvent("今年是龙年的博客更新了"));
    }

}

接着会输出:

王五在孙六之前收到新的内容:今年是龙年的博客更新了
孙六在王五之后收到新的内容:今年是龙年的博客更新了
李四收到了新的内容:今年是龙年的博客更新了
张三收到了新的内容:今年是龙年的博客更新了

   

一个简单的测试例子就演示完毕,而且我们使用spring的事件机制去写相关代码会非常简单。

 

Spring事件机制实现之前提到的注册流程

具体请下载源代码参考com.sishuok.register包里的代码。此处贴一下源码结构:


  

这里讲解一下Spring对异步事件机制的支持,实现方式有两种:

 

1、全局异步

即只要是触发事件都是以异步执行,具体配置(spring-config-register.xml)如下:

 

    <task:executor id="executor" pool-size="10" />
    <!-- 名字必须是applicationEventMulticaster和messageSource是一样的,默认找这个名字的对象 -->
    <!-- 名字必须是applicationEventMulticaster,因为AbstractApplicationContext默认找个 -->
    <!-- 如果找不到就new一个,但不是异步调用而是同步调用 -->
    <bean id="applicationEventMulticaster" class="org.springframework.context.event.SimpleApplicationEventMulticaster">
        <!-- 注入任务执行器 这样就实现了异步调用(缺点是全局的,要么全部异步,要么全部同步(删除这个属性即是同步))  -->
        <property name="taskExecutor" ref="executor"/>
    </bean>
通过注入taskExecutor来完成异步调用。具体实现可参考之前的代码介绍。这种方式的缺点很明显:要么大家都是异步,要么大家都不是。所以不推荐使用这种方式。
 

2、更灵活的异步支持

spring3提供了@Aync注解来完成异步调用。此时我们可以使用这个新特性来完成异步调用。不仅支持异步调用,还支持简单的任务调度,比如我的项目就去掉Quartz依赖,直接使用spring3这个新特性,具体可参考spring-config.xml

 

2.1、开启异步调用支持

 

    <!-- 开启@AspectJ AOP代理 -->
    <aop:aspectj-autoproxy proxy-target-class="true"/>

    <!-- 任务调度器 -->
    <task:scheduler id="scheduler" pool-size="10"/>

    <!-- 任务执行器 -->
    <task:executor id="executor" pool-size="10"/>

    <!--开启注解调度支持 @Async @Scheduled-->
    <task:annotation-driven executor="executor" scheduler="scheduler" proxy-target-class="true"/>

 

 

2.2、配置监听器让其支持异步调用

@Component
public class EmailRegisterListener implements ApplicationListener<RegisterEvent> {
    @Async
    @Override
    public void onApplicationEvent(final RegisterEvent event) {
        System.out.println("注册成功,发送确认邮件给:" + ((User)event.getSource()).getUsername());
    }
}

使用@Async注解即可,非常简单。 

 

这样不仅可以支持通过调用,也支持异步调用,非常的灵活,实际应用推荐大家使用这种方式。

 

 

通过如上,大体了解了Spring的事件机制,可以使用该机制非常简单的完成如注册流程,而且对于比较耗时的调用,可以直接使用Spring自身的异步支持来优化。

 

 

27
12
分享到:
评论
54 楼 xu5622967 2017-04-21  
为什么有时候就注册成功,不发送
53 楼 su_zui 2017-01-09  
在主线程打一个thread断点
chenliaoliu 写道
博主,问个问题:

<!--开启注解调度支持 @Async @Scheduled-->
	<task:annotation-driven executor="executor" scheduler="scheduler" proxy-target-class="true"/>


把proxy-target-class设置成true,事件监听失败(没有打印信息);但设置成false却成功了!?不明白为什么。

52 楼 793059909 2016-08-01  
public void onApplicationEvent(
被调用两次,这个怎么破?
51 楼 三人行游 2016-06-16  
ylyxf 写道
请教一下,如果用事件监听的办法,有没有可能保障发布、订阅两者在同一个数据库事务内?

同问
50 楼 lzc4244 2016-05-13  
不错不错,写的很清楚
49 楼 ylyxf 2015-08-18  
请教一下,如果用事件监听的办法,有没有可能保障发布、订阅两者在同一个数据库事务内?
48 楼 JimmyLincole 2015-06-30  
只看懂了前面一点点
47 楼 zong.jianhui06 2015-05-19  
大家可以看到如果给它一个executor(java.util.concurrent.Executor),它就可以异步支持发布事件了。佛则就是通过发送。 错别字
46 楼 飞天奔月 2015-04-24  
引用
2、ApplicationContext自动到本地容器里找一个名字为”“的ApplicationEventMulticaster实现,


这上面的 “”里面的字符串呢? 应该是 applicationEventMulticaster?
45 楼 chenliaoliu 2015-03-20  
博主,问个问题:

<!--开启注解调度支持 @Async @Scheduled-->
	<task:annotation-driven executor="executor" scheduler="scheduler" proxy-target-class="true"/>


把proxy-target-class设置成true,事件监听失败(没有打印信息);但设置成false却成功了!?不明白为什么。
44 楼 fireliuwei 2015-03-04  
您好:我在使用spring事件驱动的时候,在方法上加上事物控制的注解,结果就会锁表:
Cause: java.sql.SQLException: Lock wait timeout exceeded;
请问大概是什么原因?
43 楼 ss135ss 2014-03-25  
非常实用的帖子,感谢开涛兄
42 楼 jinnianshilongnian 2013-12-17  
lengyun3566 写道
hi,UML图用什么工具画的,蛮漂亮的

IntelliJ IDEA , 字是ps加上的
41 楼 lengyun3566 2013-12-17  
hi,UML图用什么工具画的,蛮漂亮的
40 楼 abc08010051 2013-11-07  
楼主写的确实不错,深度分析了spring的事件体系
39 楼 jinnianshilongnian 2013-07-12  
hailongshih 写道
还是看不到呀,sorry,我的相册里有 谢谢


编译时的编码问题,我程序是使用utf-8写的

加上如下配置:
 <build>
    <plugins>
        <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.0</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                    <encoding>utf-8</encoding>
                </configuration>
        </plugin>
</build>
38 楼 jinnianshilongnian 2013-07-12  
raph_java 写道
观察者模式,好熟悉啊

哈哈
37 楼 raph_java 2013-07-12  
观察者模式,好熟悉啊
36 楼 hailongshih 2013-07-12  
还是看不到呀,sorry,我的相册里有 谢谢
35 楼 hailongshih 2013-07-12  
sorry刚才没上传成功图片,请看
jinnianshilongnian 写道
hailongshih 写道
lz为啥我用maven install总提示未结束的字符串字面值,需要";"等错误提示
jinnianshilongnian 写道
hailongshih 写道
lz能否提供下jar包,

这个是maven工程,你用maven构建下即可



这个是一个普通java应用,测试时不要install啊, 你把错误截个屏看看

相关推荐

    spring boot 领域驱动设计Demo.zip

    《Spring Boot领域驱动设计实战详解》 在当前的软件开发领域,Spring Boot因其简洁的配置、快速的开发体验以及强大的生态系统,已经成为了Java后端开发的首选框架。然而,随着项目规模的扩大,如何有效地组织代码,...

    spring MVC配置详解

    Spring MVC 配置详解 Spring MVC 是一个基于 DispatcherServlet 的 MVC 框架,它是当前主流的 Web 框架之一。要想灵活运用 Spring MVC 来应对大多数的 Web 开发,就必须要掌握它的配置及原理。 一、Spring MVC ...

    Spring MVC 框架搭建及详解

    Spring MVC 是一个强大的、灵活的用于构建 Web 应用程序的模型-视图-控制器(MVC)框架。它允许开发者将业务逻辑与表现层分离,从而提高代码的可维护性和可测试性。本文将深入讲解如何搭建 Spring MVC 框架,并探讨...

    Struts1.2+Spring1.2+Hibernate2.0搭建详解

    Struts1.2作为MVC(模型-视图-控制器)框架,主要处理前端请求并控制页面展示;Spring1.2则提供依赖注入(DI)和面向切面编程(AOP),增强了组件间的解耦合,同时具备流程控制能力;Hibernate2.0是持久层框架,简化...

    《精通Spring 2.x-企业应用开发详解》chapter22.rar

    《精通Spring 2.x-企业应用开发详解》的第22章主要涵盖了Spring框架在企业级应用中的高级特性和实践技巧。Spring作为一个轻量级、全面的Java应用程序框架,为开发者提供了众多强大的功能,包括依赖注入(DI)、面向...

    spring源码中英文注释

    8. **事件驱动**:Spring框架允许应用在特定事件(如bean的初始化或销毁)发生时进行响应。`ApplicationEvent`和`ApplicationListener`接口是实现事件驱动的关键。 9. **国际化与本地化**:Spring提供了`...

    精通spring2.x企业应用开发详解

    《精通Spring 2.x企业应用开发详解》是一本深度探讨Spring框架在企业级应用开发中的实践书籍,其源代码部分包含多个章节的实例,旨在帮助读者深入理解和掌握Spring的核心特性和实际运用。以下是对各章节内容的详细...

    SpringMVC框架搭建及详解.pdf

    "SpringMVC框架搭建及详解" ...本文详细介绍了Spring MVC框架的搭建及详解,并对其工作流程进行了详细分析。通过学习Spring MVC框架,可以帮助开发者更好地理解Web应用程序的开发流程,并提高开发效率。

    Spring——jar包详解

    7. **Spring Test**:提供了一组工具,用于测试Spring配置和Spring驱动的应用程序。 了解了Spring的基本模块后,我们来看看如何在实际项目中使用jar包。通常,我们需要在项目的类路径下包含相应的Spring库,例如`...

    Spring中ApplicationContext对事件传递

    这里需要注意的是,由于Spring IoC容器中的事件模型是一种简单的、粗粒度的监听模型,即所有的监听器都会接收到同一事件,因此通常需要在监听器内部进行事件类型的检查,以决定是否需要处理该事件。 ##### 3. 事件...

    spring框架技术详解及使用指导

    Spring MVC是Spring提供的用于构建Web应用的模块,它提供了一个分层架构,使得控制器、模型、视图以及它们之间的交互得以清晰地分离。开发者可以利用它轻松实现RESTful API或者复杂的Web应用。 6. **Spring Boot**...

    spring入门教程书籍

    7. **MVC框架**:详解Spring MVC的架构,控制器、视图解析、模型-视图-控制器模式的应用。 8. **Spring测试**:教授如何使用Spring Test和JUnit进行单元测试和集成测试。 9. **Web服务**:包括RESTful服务的实现,...

    jsf2+spring sample

    JavaServer Faces 2(简称JSF2)是Java平台上的一个企业级的用户界面框架,它为构建基于Web的应用程序提供了组件化、事件驱动的模型。而Spring框架则是Java后端开发的核心框架,以其依赖注入(DI)和面向切面编程...

    spring基础教程-MVC详解

    Spring MVC 提供了一种模型-视图-控制器(Model-View-Controller)架构,帮助开发者有效地分离业务逻辑、数据处理和用户界面。这个教程将深入探讨 Spring MVC 的核心概念、配置以及实际应用。 1. **Spring MVC 架构...

    spring结合hibernate示例详解

    Spring是一个全面的后端应用程序框架,提供了依赖注入、AOP(面向切面编程)、MVC(模型-视图-控制器)等核心特性,而Hibernate则是一个对象关系映射(ORM)框架,它允许开发者将Java对象与数据库中的表进行映射,...

    spring 包详解

    - **功能描述**:提供了一个便于访问资源以及进行国际化处理的上下文环境,同时支持事件驱动的模型。 - **依赖关系**:依赖于`spring-core.jar`、`spring-beans.jar`、`spring-aop.jar`、`commons-collections.jar...

Global site tag (gtag.js) - Google Analytics