RMI是java中和远程方法调用 ,基于接口,和本地调用有一样的使用体验,这个不再赘述了
AOP面向切面编程,spring中常用来做事务管理,本篇我们就了解一下使用aop管理rmi事务
我们项目中的做法是:
spring rmi向外暴露方法, 使用aop配置事务在rmi方法上
我们先写一个业务逻辑
接口
public interface TxApi {
void txMethod1();
}
及实现
import javax.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@Service("txApi")
public class TxApiImpl implements TxApi {
private static final Logger LOGGER = LoggerFactory.getLogger(TxApiImpl.class);
@Override
public void txMethod1() {
LOGGER.info("方法内部--------------------");
}
}
这个业务逻辑有一个txMethod1方法, 在方法实现中我们只输出 "方法内部--------------------"
将这个接口暴露为rmi服务并给rmi调用加上拦截
<bean id="txApiRmiExporter"
class="org.springframework.remoting.rmi.RmiServiceExporter"
destroy-method="destroy">
<property name="service">
<ref bean="txApi" />
</property>
<property name="serviceName" value="txApiRmi" />
<property name="serviceInterface"
value="com.yj.tr.test.spring.tx.biz.TxApi" />
<property name="registryPort" value="${rmi.registryPort}" />
<property name="servicePort" value="${rmi.servicePort}" />
<property name="interceptors">
<list>
<ref bean="securityInterceptor" />
</list>
</property>
</bean>
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Component
public class SecurityInterceptor implements MethodInterceptor {
private static final Logger LOGGER = LoggerFactory.getLogger(SecurityInterceptor.class);
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
Object methodReturnObj = null;
System.out.println("Rmi-SecurityInterceptor 方法执行前------<<<<<<<<<<<<<<<<");
LOGGER.info("Rmi-SecurityInterceptor 方法执行前------<<<<<<<<<<<<<<<<");
methodReturnObj = methodInvocation.proceed();
System.out.println("Rmi-SecurityInterceptor 方法执行后------<<<<<<<<<<<<<<<<");
LOGGER.info("Rmi-SecurityInterceptor 方法执行后------<<<<<<<<<<<<<<<<");
return methodReturnObj;
}
}
然后在这个业务上通过aop配置事务及方法调用记录,test-spring-tx.xmx配置如下
<context:component-scan
base-package="com.yj.tr.test.spring.tx.biz" />
<context:component-scan
base-package="com.yj.tr.gds.model.dao.impl" />
<tx:annotation-driven />
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory">
</property>
</bean>
<tx:advice id="transaction"
transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*_tx_r" propagation="REQUIRED"
read-only="true" />
<tx:method name="*_tx_s" propagation="SUPPORTS" />
<tx:method name="*" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>
<aop:config>
<!-- pointcuts -->
<aop:pointcut id="tx"
expression="execution(public * com.yj.tr.test.spring.tx.biz.*.*(..))" />
<!-- transaction advisors -->
<aop:advisor advice-ref="transaction" pointcut-ref="tx"/>
<!-- logger advisors -->
<aop:advisor advice-ref="methodLogger" pointcut-ref="tx" />
</aop:config>
方法记录类
@Component
public class MethodLogger implements MethodInterceptor {
private static final Logger LOGGER = LoggerFactory.getLogger(MethodLogger.class);
private static final Logger LOGGER2 = LoggerFactory.getLogger("method-invoke");
@Resource
ComboPooledDataSource dataSource;
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("aop日志-方法前--->>>>>");
LOGGER.info("aop日志-方法前--->>>>>");
Object methodReturnObj = null;
methodReturnObj = methodInvocation.proceed();
System.out.println("aop日志-方法后--->>>>>");
LOGGER.info("aop日志-方法后--->>>>>");
return methodReturnObj;
}
}
再写一个rmi客户端调用,引用rmi接口 , test-spring-rmi-import.xml配置如下
<bean id="txApiRmiClient"
class="com.yj.org.springframework.remoting.rmi.RmiProxyFactoryBean">
<property name="serviceUrl"
value="rmi://${gds.rmi.server.hostname}:${gds.rmi.registryPort}/txApiRmi" />
<property name="serviceInterface"
value="com.yj.tr.test.spring.tx.biz.TxApi" />
<property name="lookupStubOnStartup" value="false"></property>
<property name="refreshStubOnConnectFailure" value="true"></property>
</bean>
运行rmi服务端
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ServerApplication {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(new String[] { //
"classpath:test-spring-hibernate.xml", //
"classpath:test-spring-tx.xml", //
"classpath:test-spring-rmi-export.xml", //
"classpath:test-spring-rmi-import.xml",//
});
System.out.println("server started");
}
}
客户端来调用它 (客户端我使用junit测试来跑)
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.yj.tr.test.spring.tx.biz.TxApi;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { //
"classpath:test-spring-rmi-import.xml",//
})
public class TxRmiApiTest {
private static final Logger LOGGER = LoggerFactory.getLogger(TxRmiApiTest.class);
@Resource
TxApi txApiRmiClient;
@Test
public void testTxMethod1_RMI() {
txApiRmiClient.txMethod1();
}
}
从服务端的输出看,aop方法、事务、rmi拦截器的输出
Building new Hibernate SessionFactory --[INFO] 2018-08-25 14:37:04
Rmi-SecurityInterceptor 方法执行前------<<<<<<<<<<<<<<<< --[INFO] 2018-08-25 14:37:30
Creating new transaction with name [com.yj.tr.test.spring.tx.biz.TxApiImpl.txMethod1]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT --[DEBUG] 2018-08-25 14:37:30
Opened new Session [org.hibernate.impl.SessionImpl@3632ea8b] for Hibernate transaction --[DEBUG] 2018-08-25 14:37:30
Preparing JDBC Connection of Hibernate Session [org.hibernate.impl.SessionImpl@3632ea8b] --[DEBUG] 2018-08-25 14:37:30
Exposing Hibernate transaction as JDBC transaction [com.mchange.v2.c3p0.impl.NewProxyConnection@14cf4af9] --[DEBUG] 2018-08-25 14:37:30
aop日志-方法前--->>>>> --[INFO] 2018-08-25 14:37:30
方法内部-------------------- --[INFO] 2018-08-25 14:37:30
aop日志-方法后--->>>>> --[INFO] 2018-08-25 14:37:30
Triggering beforeCommit synchronization --[DEBUG] 2018-08-25 14:37:30
Triggering beforeCompletion synchronization --[DEBUG] 2018-08-25 14:37:30
Initiating transaction commit --[DEBUG] 2018-08-25 14:37:30
Committing Hibernate transaction on Session [org.hibernate.impl.SessionImpl@3632ea8b] --[DEBUG] 2018-08-25 14:37:30
Triggering afterCommit synchronization --[DEBUG] 2018-08-25 14:37:30
Triggering afterCompletion synchronization --[DEBUG] 2018-08-25 14:37:30
Closing Hibernate Session [org.hibernate.impl.SessionImpl@3632ea8b] after transaction --[DEBUG] 2018-08-25 14:37:30
Closing Hibernate Session --[DEBUG] 2018-08-25 14:37:30
Rmi-SecurityInterceptor 方法执行后------<<<<<<<<<<<<<<<< --[INFO] 2018-08-25 14:37:30
可以看到rmi的拦截器最先执行, 然后是spring事务管理器,然后再是方法记录, 整个执行过程是
SecurityInterceptor-> transactionManager -> MethodLogger -> MethodLogger -> transactionManager -> SecurityInterceptor
我们通过修改aop的order来指定aop执行顺序(注意新加了order属性配置)
<aop:advisor advice-ref="transaction" pointcut-ref="tx" order="2"/>
<!-- logger advisors -->
<aop:advisor advice-ref="methodLogger" pointcut-ref="tx" order="1"/>
再运行看日志输出
Building new Hibernate SessionFactory --[INFO] 2018-08-25 14:43:27
Rmi-SecurityInterceptor 方法执行前------<<<<<<<<<<<<<<<< --[INFO] 2018-08-25 14:43:34
aop日志-方法前--->>>>> --[INFO] 2018-08-25 14:43:34
Creating new transaction with name [com.yj.tr.test.spring.tx.biz.TxApiImpl.txMethod1]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT --[DEBUG] 2018-08-25 14:43:34
Opened new Session [org.hibernate.impl.SessionImpl@7966f7ed] for Hibernate transaction --[DEBUG] 2018-08-25 14:43:34
Preparing JDBC Connection of Hibernate Session [org.hibernate.impl.SessionImpl@7966f7ed] --[DEBUG] 2018-08-25 14:43:34
Exposing Hibernate transaction as JDBC transaction [com.mchange.v2.c3p0.impl.NewProxyConnection@54e3813f] --[DEBUG] 2018-08-25 14:43:34
方法内部-------------------- --[INFO] 2018-08-25 14:43:34
Triggering beforeCommit synchronization --[DEBUG] 2018-08-25 14:43:35
Triggering beforeCompletion synchronization --[DEBUG] 2018-08-25 14:43:35
Initiating transaction commit --[DEBUG] 2018-08-25 14:43:35
Committing Hibernate transaction on Session [org.hibernate.impl.SessionImpl@7966f7ed] --[DEBUG] 2018-08-25 14:43:35
Triggering afterCommit synchronization --[DEBUG] 2018-08-25 14:43:35
Triggering afterCompletion synchronization --[DEBUG] 2018-08-25 14:43:35
Closing Hibernate Session [org.hibernate.impl.SessionImpl@7966f7ed] after transaction --[DEBUG] 2018-08-25 14:43:35
Closing Hibernate Session --[DEBUG] 2018-08-25 14:43:35
aop日志-方法后--->>>>> --[INFO] 2018-08-25 14:43:35
Rmi-SecurityInterceptor 方法执行后------<<<<<<<<<<<<<<<< --[INFO] 2018-08-25 14:43:35
现在整个执行顺序变为了
SecurityInterceptor-> MethodLogger ->transactionManager -> transactionManager-> MethodLogger -> SecurityInterceptor