‘壹’ 如何使用Spring配置异步和同步事件发布
在Spring中使用异步事件实现同步事务:
结合Scala+Spring,我们将采取一个很简单的场景:下订单,然后发送一封电子邮件。
编制一个服务:
@Service
class OrderService @Autowired() (orderDao: OrderDao, mailNotifier: OrderMailNotifier) {
@Transactional
def placeOrder(order: Order) {
orderDao save order //保存订单
mailNotifier sendMail order //发送邮件
}
}
上面代码是在保存订单和发送邮件两个同步执行,发送邮件需要连接邮件服务器,比较耗时,拖延了整个性能,我们采取异步发送电子邮件,利用Spring内置的自定义事件,与JMS或其他生产者 - 消费者类似。
case class OrderPlacedEvent(order: Order) extends ApplicationEvent
@Service
class OrderService @Autowired() (orderDao: OrderDao, eventPublisher: ApplicationEventPublisher) {
@Transactional
def placeOrder(order: Order) {
orderDao save order
eventPublisher publishEvent OrderPlacedEvent(order)
}
}
区别是继承了ApplicationEvent 之前是直接用 OrderMailNotifier 直接发送,而现在我们使用ApplicationEventPublisher 发送发邮件事件了。
事件监听者代码如下:
@Service
class OrderMailNotifier extends ApplicationListener[OrderPlacedEvent] {
def onApplicationEvent(event: OrderPlacedEvent) {
//sending e-mail...
}
}
在监听者方法中真正实现邮件发送。
但是Spring的ApplicationEvents是同步事件,意味着我们并没有真正实现异步,程序还会在这里堵塞,如果希望异步,我们需要重新定义一个ApplicationEventMulticaster,实现类型和TaskExecutor:
@Bean
def applicationEventMulticaster() = {
val multicaster = new ()
multicaster.setTaskExecutor(taskExecutor())
multicaster
}
@Bean
def taskExecutor() = {
val pool = new ThreadPoolTaskExecutor()
pool.setMaxPoolSize(10)
pool.setCorePoolSize(10)
pool.setThreadNamePrefix("Spring-Async-")
pool
}
Spring通过使用TaskExecutor已经支持广播事件了,对onApplicationEvent() 标注 @Async
@Async
def onApplicationEvent(event: OrderPlacedEvent) { //...
如果你希望使用@Async,可以编制自己的异步执行器:
@Configuration
@EnableAsync
class ThreadingConfig extends AsyncConfigurer {
def getAsyncExecutor = taskExecutor()
@Bean
def taskExecutor() = {
val pool = new ThreadPoolTaskExecutor()
pool.setMaxPoolSize(10)
pool.setCorePoolSize(10)
pool.setThreadNamePrefix("Spring-Async-")
pool
}
}
@ EnableAsync是足够了。,默认情况下,Spring使用SimpleAsyncTaskExecutor类创建新的线程。
以上所有设置暴露一个真正的问题。现在,我们虽然使用其他线程发送一个异步消息处理。不幸的是,我们引入竞争条件。
开始事务
存储order到数据库
发送一个包装order的消息
确认
异步线程获得OrderPlacedEvent并开始处理。现在的问题是,它发生(3)之后,还是(4)之前或者(4)之后?这有一个很大的区别!在前者的情况下,交易也尚未提交订单所以不存在于数据库中。另一方面,延迟加载可能已经在工作,致使订单对象仍然然绑定在 PersistenceContext(缺省我们使用JPA)。
解决办法是使用 .,可以注册很多监听者 TransactionSynchronization,它对于事务的提交或回滚都有事件发送。
@Transactional
def placeOrder(order: Order) {
orderDao save order
afterCommit {
eventPublisher publishEvent OrderPlacedEvent(order)
}
}
private def afterCommit[T](fun: => T) {
.registerSynchronization(new {
override def afterCommit() {
fun
}
})
}
当前事务提交后 afterCommit()接受调用,可以安全地调用registerSynchronization()多次 - 监听器存储在Set并且本地保存到当前事务中,事务提交后消失。
我们将afterCommit方法单独抽象成一个类,分离关注。
class (delegate: ApplicationEventPublisher)
extends ApplicationEventPublisher {
override def publishEvent(event: ApplicationEvent) {
if (.isActualTransactionActive) {
.registerSynchronization(
new {
override def afterCommit() {
delegate publishEvent event
}
})
}
else
delegate publishEvent event
}
}
是实现Spring的ApplicationEventPublisher。
我们要将这个新的实现告诉Spring替换掉旧的,用@Primary:
@Resource
val applicationContext: ApplicationContext = null
@Bean
@Primary
def () =
new (applicationContext)
再看看原来的订单服务:
@Service
class OrderService @Autowired() (orderDao: OrderDao, eventPublisher:ApplicationEventPublisher) {
@Transactional
def placeOrder(order: Order) {
orderDao save order
eventPublisher publishEvent OrderPlacedEvent(order)
}
注意这里ApplicationEventPublisher已经是我们自己实现的,将被自动注入这个服务。
最后,要在真正订单保存的业务代码上放置事务:
def placeOrder(order: Order) {
storeOrder(order)
eventPublisher publishEvent OrderPlacedEvent(order)
}
@Transactional
def storeOrder(order: Order) = orderDao save order
‘贰’ springboot之几种同步,线程安全处理的方法
在一些公共资源的处理上,经常会出现对公共资源的争夺使用权限的问题,以及对数据库处理时,容易出现线程安全的问题,比如对数据操作时的一致性,可见性等等。
这时候,为了避免这样的问题,一般的处理方式是当某一个公共资源在被某一个线程调用时,把这个公共资源(即代码块)锁住。
下面先大概介绍两种简单的同步方法:
注:同步是一种高开销的操作,因此应该尽量减少同步的内容。
没有必要同步整个方法,只使用synchronized代码块同步关键代码即可。
1.同步方法
即有synchronized关键字修饰的方法。
由于java的每个对象都有一个内置锁,当用此关键字修饰方法时,
内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。
代码如:
public synchronized void demo(){}
注: synchronized关键字也可以修饰静态方法,此时如果调用该静态方法,将会锁住整个类
2.同步代码块
即有synchronized关键字修饰的语句块。
被该关键字修饰的语句块会自动被加上内置锁,从而实现同步
代码如:
synchronized(object){
}
或
lock.lock(); try {
System.out.println("已锁住"); for (int i=0;i<10;i++) { //放大代码块执行完成的时间,便于观察
System.out.println(i); try {
System.out.println(Thread.currentThread().getName()+ "休眠5秒!");
Thread.sleep(5000);//放大代码块执行完成的时间,便于观察
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}finally { lock.unlock();
System.out.println("解锁");
}12345678910111213141516
当代码块被锁住后,有其它的线程来调用被锁住的线程时,必须先等待上一个线程使用完之后,释放资源,才能继续执行。
以上两种方法很简单,就不过多的写了,但是这两种方法在使用时,有以下几个问题:
(1). 当公共资源被占用时,其它线程必须等待资源被释放后才能执行,这种场景适用于对数据库的操作,保证其数据的原子性。当被抢占的资源是即时的公共资源,只有在某一时刻抢到才有意义,比如说在公共场所对空调的控制,大家都要争夺对空调的控制权,但如果是用上述的方法,对空调的控制会按照发出请求的时间,依次执行,那就没有意义了。所以这种场景适合只执行最先抢到资源的线程,在资源被占用时,杀死其它的请求线程。这种情况用以下的方法实现
3. Semaphore并发包
Semaphore是基于计数的信号量,它可以设定一个资源的总数量,基于这个总数量,多线程竞争获取许可信号,做自己的申请后归还,超过总数量后,线程申请许可,信号将会被阻塞。等到有资源时,继续执行。
下面在springboot中实现:
@Contorller public class Controller(){
Semaphore semaphore=new Semaphore(1); //定义资源的总数量
@GetMapping("/userInfo/request")
@ResponseBody public String Resquest(){ int availablePermits=semaphore.availablePermits();//可用资源数
if(availablePermits>0){
System.out.println("抢到资源");
}else{
System.out.println("资源已被占用,稍后再试"); return "Resource is busy!";
} try {
semaphore.acquire(1); //请求占用一个资源
System.out.println("资源正在被使用");
Thread.sleep(30000);//放大资源占用时间,便于观察
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
semaphore.release(1);//释放一个资源
} return "Success";
}
}
当只有一个资源请求时,效果如下:
大致的思路和框架就是这样,可根据自己的需要进行修改。
‘叁’ 关于Spring配置数据库
在src下创建.properties。
jdbc.driverClass=oracle.jdbc.driver.OracleDriver
jdbc.url=jdbc:oracle:thin:@*.*.*.*:1521:oel1245
jdbc.username=userName
jdbc.password=password
jdbc.maxActive=30
jdbc.maxIdle=30
jdbc.maxWait=3000
然后在applicationContext.xml文件中加载这个文件,
然后再配置DataSource就OK了
如果是其他数据库的话请更改driverClass和url
也可以直接在applicationContext中配置行相应的值,而不需要.properties
‘肆’ 如何利用Spring实现数据库读写分离
读写分离是为了减少数据库的负荷,当用户高并发访问时,绝大部分都是用户查询,少部分用户是写入到数据库的。这些我们把数据库拆分成主从两个数据库,主数据库用高性能
服务器承载高并发的用户访问并加redis缓存。在这里我不讲mysql的主从同步配置,大家可以去查下资料,我接下来重点讲怎么动态的给每个sql注入数据源。
‘伍’ 如何用spring 调用数据库连接使用jdbc
一 ,使用spring的jdbc
1.在myeclipse添加spring jar包,
添加Spring2.5 Core/AOP/ JDBC Library
2.在applicationContext.xml中,配置jdbc bean:
‘陆’ Spring连接数据库的几种常用方法
数据库是按照数据结构来组织、存储和管理数据的仓库,它产生于距今六十多年前,随着信息技术和市场的发展,特别是二十世纪九十年代以后,数据管理不再仅仅是存储和管理数据,而转变成用户所需要的各种数据管理的方式。数据库有很多种类型,从最简单的存储有各种数据的表格到能够进行海量数据存储的大型数据库系统都在各个方面得到了广泛的应用。在信息化社会,充分有效地管理和利用各类信息资源,是进行科学研究和决策管理的前提条件。数据库技术是管理信息系统、办公自动化系统、决策支持系统等各类信息系统的核心部分,是进行科学研究和决策管理的重要技术手段。数据库是一个单位或是一个应用领域的通用数据处理系统,它存储的是属于企业和事业部门、团体和个人的有关数据的集合。数据库中的数据是从全局观点出发建立的,按一定的数据模型进行组织、描述和存储。其结构基于数据间的自然联系,从而可提供一切必要的存取路径,且数据不再针对某一应用,而是面向全组织,具有整体的结构化特征。数据库中的数据是为众多用户所共享其信息而建立的,已经摆脱了具体程序的限制和制约。不同的用户可以按各自的用法使用数据库中的数据;多个用户可以同时共享数据库中的数据资源,即不同的用户可以同时存取数据库中的同一个数据。数据共享性不仅满足了各用户对信息内容的要求,同时也满足了各用户之间信息通信的要求。
‘柒’ spring整合数据库连接的几种方式
1.解决方案:
property name="<.commons; value=" value="close".properties文件
2.配置多个数据库:
jdbc;minEvictableIdleTimeMillis"driverClassName".maxActive}" /
password}".url}"${jdbc;>.driver;3600000"maxActive";>${jdbc.driver}"
/username"${dbcp;
</ value=".dbcp;url".BasicDataSource"
< value="bean id=".username}"
< value="
<>password"${dbcp;org.;dataSource"false" /
<property name=" destroy-method="3600000"
application;property name=" class=",property name="
< value=">.apache; value=" /property name=">
<${jdbc;property name="property name=">.;
‘捌’ spring连接数据库的技术理论是什么
Spring工作原理
Spring 已经用过一段时间了,感觉Spring是个很不错的框架。内部最核心的就是IOC了,
动态注入,让一个对象的创建不用new了,可以自动的生产,这其实就是利用java里的反射
反射其实就是在运行时动态的去创建、调用对象,Spring就是在运行时,跟xml Spring的配置
文件来动态的创建对象,和调用对象里的方法的 。
Spring还有一个核心就是AOP这个就是面向切面编程,可以为某一类对象 进行监督和控制(也就是
在调用这类对象的具体方法的前后去调用你指定的 模块)从而达到对一个模块扩充的功能。这些都是通过
配置类达到的。
Spring目的:就是让对象与对象(模块与模块)之间的关系没有通过代码来关联,都是通过配置类说明
管理的(Spring根据这些配置 内部通过反射去动态的组装对象)
要记住:Spring是一个容器,凡是在容器里的对象才会有Spring所提供的这些服务和功能。
Spring里用的最经典的一个设计模式就是:模板方法模式。(这里我都不介绍了,是一个很常用的设计模式)
Spring里的配置是很多的,很难都记住,但是Spring里的精华也无非就是以上的两点,把以上两点跟理解了
也就基本上掌握了Spring.
‘玖’ spring+mybatis 多数据库事务管理:一个方法里面能同时对两个数据库的数据进行操作
定义两个DAO分别使用不同的数据源,ADAO连接A数据库,BDAO连接B数据库
定义一个Service类,加上Spring注解@Transactional,表示进行事务管理。
将ADAO和BDAO注入到Service类里面。
在service类里面创建一个方法,方法里调用ADAO的方法插入数据到A数据库的user表,然后调用BDAO的方法插入数据到B数据库的user表