在业务逻辑里面往往会出现这样的步骤,比如更新信息到缓存、发送短信通知、发送邮件通知、推送信息到监控系统、到日志系统等。
这些业务逻辑中的步骤,有相同的特点:不需要返回结果,业务逻辑不关心、同步调用会占用一部分的业务逻辑响应时间。
占用响应时间这里,对于发邮件有时候尤为明显,邮箱一般有连接、验证账号是否存在、是否发送过于频繁、验证发送内容合法性。
对于这样的调用我们当然想不占用或者尽量少的占用业务处理的时间。比如快速的抛给消息系统、比如采用多线程异步化处理。
这里说道的spring factory-bean的妙用,是第二点,也就是多线程异步化处理方案,是一种比较巧妙的快速实现方案,当当网开源https://github.com/crnlmchina/ring。
方法是结合factory-bean工厂和Proxy代理。这里先看一下factory-bean的用法。
在spring里面xml定义bean的时候,一般这么定义
<bean id="xxxx" class="com.google.xxx.Xxxx" />
定义之后就可以在其他地方(或者xml或者注解)中通过id引用这个类了。
除了这种最常见的方式之外,还可以通过factory-bean的方式定义bean,在factoryBean里面可以对生成的bean进行控制。比如这样:
<bean id="myBeanFactory" class="com.google.MyBeanFactory" />
<bean id="myBean" factory-bean="myBeanFactory" factory-method="createInstance"/>
myBeanFactory的内部大概是这样子的
public class MyBeanFactory{
public MyBean createInstance() {
return new MyBean();
}
}
看到这里,是不是觉得第二种方式没有什么优势,反倒把问题复杂化了呢?
先来看一下这个类库的用法:
定义一个我们自己的接口:
interface ISave{
void save(String obj);
}
定义一个实现类:
class MySaveImpl implements ISave{
@Backgroud
void save(String obj){
//save into somewhere
}
}
配置文件
<bean id="ringFactory" class="org.ring.proxy.multithread.MultiThreadRingFactory">
<constructor-arg name="fixPoolSize" value="100" />
bean>
<bean id="mySaveBeanImpl" class="MySaveImpl" />
<bean id="mySaveBean" factory-bean="ringFactory" factory-method="delegate">
<constructor-arg name="obj" ref="mySaveBeanImpl" />
<constructor-arg name="inter" value="ISave" />
bean>
调用save的一个主类:
public static void main(String[] args){
BeanFactory context = new ClassPathXmlApplicationContext("applicationContext.xml");
ISave mySave = (ISave) context.getBean("mySaveBean");
mySave.save("异步执行!");
}
上面的save方法会另起一个线程执行保存操作,而不占用当前线程的时间。这是怎么做到的呢?
关键在于ringFactory的delegate方法,当spring初始化时创建MySaveBean实例的时候就会被调用来生成一个ISave接口的实例。以下是该类库的源码:
@SuppressWarnings("unchecked")
public T delegate(Object obj, Class inter) {
final MultiThreadInvocationHandler handler = new MultiThreadInvocationHandler(obj, executorService);
final ClassLoader classLoader = delegateClassLoader == null ? obj.getClass().getClassLoader() : delegateClassLoader;
return (T) Proxy.newProxyInstance(classLoader, new Class[] { inter }, handler);
}
看return的地方,给我们生成了一个代理对象,对象添加了一个handler,这个handler会在代理对象所有方法调用时调用。我们再来看一下handler里面的方法调用时做了什么:
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
Background background = method.getAnnotation(Background.class);
if (background != null) {
executorService.execute(new Runnable() {
public void run() {
try {
method.invoke(realObj, args);
} catch (Exception e) {
LOGGER.error(e.getMessage(), e);
}
}
});
return null;
}
return method.invoke(realObj, args);
}
注意if条件的地方,首先获取到了被调用方法的注解,如果注解是Background类型,那么就会使用线程池里的线程进行调用,也就是异步了。