最近公司使用Jfinal开发项目,不知道什么原因Jfinal和其他的几个插件集成的时候,事物管理并不那么随心,所以就选择了Spring作为Jfinal的插件来管理事物.废话不多说,直接上代码.
public class MyConfig extends JFinalConfig{
@Override
public void configConstant(Constants me) {
IocKit.processJFinalConfig(this);
loadPropertyFile("db.properties");
me.setEncoding("UTF-8");
me.setDevMode(getPropertyToBoolean("devMode", true));
}
@Override
public void configRoute(Routes me) {
System.out.println("configRoute");
me.add("/blog", BlogController.class);
}
@Override
public void configPlugin(Plugins me) {
SpringPlugin springPlugin = new SpringPlugin("classpath*:spring/applicationContext-*.xml");
me.add(springPlugin);
SpringDataSourceProvider prov = new SpringDataSourceProvider(springPlugin);
// 配置ActiveRecord插件,将jfinal的事物交给Spring管理
ActiveRecordPlugin arp = new ActiveRecordPlugin(prov);
me.add(arp);
arp.addMapping("system_admin", SystemAdmin.class);
}
@Override
public void configInterceptor(Interceptors me) {
System.out.println("configInterceptor");
}
@Override
public void configHandler(Handlers me) {
System.out.println("configHandler");
}
@Override
public void configEngine(Engine me) {
System.out.println("configEngine");
}
}
IocKit.java
public class IocKit {
// ioc @Inject @Value @Autowired
static AutowiredAnnotationBeanPostProcessor autowiredAnnotationBeanPostProcessor;
// ioc @Resource
static CommonAnnotationBeanPostProcessor commonAnnotationBeanPostProcessor;
public static AutowiredAnnotationBeanPostProcessor getAutowiredAnnotationBeanPostProcessor() {
Assert.notNull(autowiredAnnotationBeanPostProcessor,
"The main autowiredAnnotationBeanPostProcessor is null, initialize SpringPlugin first");
return autowiredAnnotationBeanPostProcessor;
}
public static CommonAnnotationBeanPostProcessor getCommonAnnotationBeanPostProcessor() {
Assert.notNull(commonAnnotationBeanPostProcessor,
"The main commonAnnotationBeanPostProcessor is null, initialize SpringPlugin first");
return commonAnnotationBeanPostProcessor;
}
// --------------------------------------------------
// JFinalConfig
// --------------------------------------------------
/**
* @Title: 处理 JFinalConfig
*/
public static void processJFinalConfig(JFinalConfig finalConfig) {
ApplicationContext ctx = org.springframework.web.context.support.WebApplicationContextUtils
.getWebApplicationContext(JFinal.me().getServletContext());
processJFinalConfig(finalConfig, ctx);
}
/**
* @Title: 处理 JFinalConfig
*/
public static void processJFinalConfig(JFinalConfig jfinalConfig, ApplicationContext ctx) {
Assert.notNull(ctx, "ApplicationContext not be null");
Assert.notNull(jfinalConfig, "jfinalConfig not be null");
AutowiredAnnotationBeanPostProcessor autowiredAnnotationBeanPostProcessor = setBeanFactory(
new AutowiredAnnotationBeanPostProcessor(), ctx);
CommonAnnotationBeanPostProcessor commonAnnotationBeanPostProcessor = setBeanFactory(
new CommonAnnotationBeanPostProcessor(), ctx);
processInjection(jfinalConfig, commonAnnotationBeanPostProcessor, autowiredAnnotationBeanPostProcessor);
}
/**
* @Title: 注入 bean 调用
*/
public static void invokeForProcessInjection(Object invocation) {
Invocation inv = as(invocation, Invocation.class);
Controller controller = inv.getController();
processInjection(controller);
inv.invoke();
}
public static void processInjection(Object bean) {
processInjection(bean, commonAnnotationBeanPostProcessor, autowiredAnnotationBeanPostProcessor);
}
// ---------------------------------------------------------
// function
// ---------------------------------------------------------
static void init(ApplicationContext ctx) {
autowiredAnnotationBeanPostProcessor = setBeanFactory(new AutowiredAnnotationBeanPostProcessor(), ctx);
commonAnnotationBeanPostProcessor = setBeanFactory(new CommonAnnotationBeanPostProcessor(), ctx);
}
static <T extends BeanFactoryAware> T setBeanFactory(T beanPostProcessor, ApplicationContext ctx) {
beanPostProcessor.setBeanFactory(ctx.getAutowireCapableBeanFactory());
return beanPostProcessor;
}
static void processInjection(Object bean, CommonAnnotationBeanPostProcessor commonAnnotationBeanPostProcessor,
AutowiredAnnotationBeanPostProcessor autowiredAnnotationBeanPostProcessor) {
commonAnnotationBeanPostProcessor.postProcessPropertyValues(null, null, bean, null);
autowiredAnnotationBeanPostProcessor.processInjection(bean);
}
// ----------------------------------------------------------------
// as proxy
// ----------------------------------------------------------------
private final static Map<String, Method> methodCache = new HashMap<String, Method>(16);
private static Method cachedMethod(Class<?> type, Method method) throws NoSuchMethodException {
String name = method.getName();
String key = type.getName() + "." + name;
Method m = methodCache.get(key);
if (m == null) {
synchronized (methodCache) {
m = methodCache.get(key);
if (m == null) {
Class<?>[] parameterTypes = method.getParameterTypes();
try {
// Actual method name matches always come first
m = type.getMethod(name, parameterTypes);
} catch (SecurityException ignore) {
m = type.getDeclaredMethod(name, parameterTypes);
}
methodCache.put(key, m);
}
}
}
return m;
}
/**
* Create a proxy for the wrapped object allowing to typesafely invoke
* methods on it using a custom interface
*
* @param proxyType
* The interface type that is implemented by the proxy
* @return A proxy for the wrapped object
*/
@SuppressWarnings("unchecked")
private static <P> P as(final Object object, Class<P> proxyType) {
final Class<?> clazz = object.getClass();
final InvocationHandler handler = new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Method m = cachedMethod(clazz, method);
boolean accessible = m.isAccessible();
try {
if (!accessible) m.setAccessible(true);
return m.invoke(object, args);
} finally {
m.setAccessible(accessible);
}
}
};
return (P) Proxy.newProxyInstance(proxyType.getClassLoader(), new Class[] { proxyType }, handler);
}
private static interface Invocation {
void invoke();
Controller getController();
}
}
SpringDataSourceProvider.java
public class SpringDataSourceProvider implements IDataSourceProvider {
private static final String proxyDsName = "proxyDataSource";
private SpringPlugin springPlugin;
private String beanName;
public SpringDataSourceProvider(SpringPlugin springPlugin, String beanName) {
this.springPlugin = springPlugin;
this.beanName = beanName;
}
public SpringDataSourceProvider(SpringPlugin springPlugin) {
this.springPlugin = springPlugin;
this.beanName = proxyDsName;
}
public DataSource getDataSource() {
ApplicationContext ctx = springPlugin.getApplicationContext();
return (DataSource)ctx.getBean(beanName,TransactionAwareDataSourceProxy.class);
}
}
SpringPlugin.java
private static boolean isStarted = false;
private String[] configurations;
private ApplicationContext ctx;
public SpringPlugin() {}
public SpringPlugin(ApplicationContext ctx) {
this.setApplicationContext(ctx);
}
public SpringPlugin(String... configurations) {
this.configurations = configurations;
}
public void setApplicationContext(ApplicationContext ctx) {
Assert.notNull(ctx, "ApplicationContext can not be null.");
this.ctx = ctx;
}
public ApplicationContext getApplicationContext() {
return this.ctx ;
}
public boolean start() {
if (isStarted) {
return true;
}else if (!isStarted && configurations != null){
ctx = new FileSystemXmlApplicationContext(configurations);
}else {
ctx = new FileSystemXmlApplicationContext(PathKit.getWebRootPath() + "/WEB-INF/applicationContext.xml");
}
Assert.notNull(ctx, "ApplicationContext can not be null.");
IocKit.init(ctx);
return isStarted = true;
}
public boolean stop() {
ctx = null;
isStarted = false;
return true;
}
}
applicationContext-core.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
default-autowire="byName"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd">
<!-- 引入配置文件 -->
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:db.properties"/>
</bean>
<!-- dataSource配置 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="filters" value="log4j"/>
<property name="maxActive" value="5"/>
<property name="initialSize" value="1"/>
<property name="maxWait" value="6000"/>
</bean>
<bean id="proxyDataSource" class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy">
<constructor-arg>
<ref bean="dataSource" />
</constructor-arg>
</bean>
<!-- 声明式事务的配置 -->
<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置事务通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="delete*" propagation="REQUIRED" read-only="false" />
<tx:method name="insert*" propagation="REQUIRED" read-only="false" />
<tx:method name="update*" propagation="REQUIRED" read-only="false" />
<tx:method name="find*" propagation="SUPPORTS" />
<tx:method name="get*" propagation="SUPPORTS" />
<tx:method name="select*" propagation="SUPPORTS" />
</tx:attributes>
</tx:advice>
<!-- 把事务控制在Service层 -->
<aop:config>
<aop:pointcut id="pointcut" expression="execution(public * cn.minions.testSpring.Service.impl.*.*(..))" />
<aop:advisor pointcut-ref="pointcut" advice-ref="txAdvice" />
</aop:config>
<context:component-scan base-package="cn.minions.testSpring" />
</beans>
完整测试项目的地址:https://gitee.com/fhcspring/jfinal-spring
测试环境还集成了activiti mybatis