Java-Bean管理,实现同步/异步管理
本文介绍利用Spring的Bean管理,结合自己封装的ThreadPoolExecutor线程池,利用注解的方式,实现方法的异步管理;
常用异步方法:不影响系统正常执行、可后台操作、对数据安全性要求差、等等诸如此类的业务逻辑。如日志的操作,短信发送、用户信息的更新等。异步在一定程度上能够提升系统的响应度,但不合理的设计或是代码的不合理运用会导致一场灾难~
以下代码仅供学习参考,实用性、合理性及安全性仍需检测,也欢迎茫茫人海之中的你能够给一板砖~
1 环境搭建
1.1 引入Spring依赖
<properties>
<spring.version>4.3.2.RELEASE</spring.version>
</properties>
<dependencies>
<!-- spring support -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
2.核心实现
2.1 异步处理器:InvocationHandler
package com.nieli.proxy;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.nieli.base.config.Configuration;
/**
*@描述:
*@版权: Copyright (c) 2018
*@作者: nieli
*@版本: 1.0
*@创建日期: 2018年01月29日
*@创建时间: 10:17
*/
public class InvocationHandler<T> implements java.lang.reflect.InvocationHandler
{
private static final Logger logger = LoggerFactory
.getLogger(InvocationHandler.class);
//线程池线程名称前缀
public static final String POOL_THREAD_NAME_PREFIX = Configuration.getString(
"proxy.poolThreadNamePrefix",
"Async-Invoke-Thread-");
//异步执行线程池大小 默认:CPU个数 * 2
private static final int ASYNC_THREAD_POOL_CORE = Configuration
.getInt("proxy.asyncThreadPoolCore",
Runtime.getRuntime()
.availableProcessors() * 2);
//异步执行线程池队列大小 默认:10000
private static final int ASYNC_THREAD_POOL_QUEUE_CORE = Configuration
.getInt("proxy.asyncThreadPoolQueueCore",
10000);
//异步执行线程池拒绝模式 默认:callerRuns 可选:abort(抛出异常) discardOldest(丢弃最老任务) callerRuns(同步执行任务) discard(丢弃任务)
private static final String ASYNC_THREAD_POOL_REJECT_POLICY = Configuration.getString(
"proxy.asyncThreadPoolPolicy",
"callerRuns");
//线程池对象
private static volatile ThreadPoolExecutor threadPoolExecutor;
//代理目标对象
private T target;
public InvocationHandler(T target)
{
super();
this.target = target;
}
/**
*@描述:获取拒绝策略
*@作者:nieli
*@日期:2018/2/1
*@时间:18:16
*/
private static RejectedExecutionHandler getRejectedPolicy(String policy)
{
if ( "abort".equalsIgnoreCase(policy) )
{
return new ThreadPoolExecutor.AbortPolicy();
}
else if ( "discardOldest".equalsIgnoreCase(policy) )
{
return new ThreadPoolExecutor.DiscardOldestPolicy();
}
else if ( "discard".equalsIgnoreCase(policy) )
{
return new ThreadPoolExecutor.DiscardPolicy();
}
else
{
return new ThreadPoolExecutor.CallerRunsPolicy();
}
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
if ( method.getAnnotation(Async.class) != null )
{
if ( logger.isDebugEnabled() )
{
logger.debug("执行代理对象[{}]-方法[{}],执行类型:【异步】", target.getClass().getName(), method.getName());
}
//异步执行
execute(target, method, args);
return null;
}
else
{
if ( logger.isDebugEnabled() )
{
logger.debug("执行代理对象[{}]-方法[{}],执行类型:【同步】", target.getClass().getName(), method.getName());
}
//同步执行
try
{
return method.invoke(target, args);
}
catch (InvocationTargetException e)
{
throw e.getTargetException();
}
}
}
public T getTarget()
{
return target;
}
public void setTarget(T target)
{
this.target = target;
}
private void execute(T target, Method method, Object[] args)
{
if ( threadPoolExecutor == null )
{
synchronized (InvocationHandler.class)
{
if ( threadPoolExecutor == null )
{
threadPoolExecutor = new ThreadPoolExecutor(ASYNC_THREAD_POOL_CORE, ASYNC_THREAD_POOL_CORE, 60L,
TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(ASYNC_THREAD_POOL_QUEUE_CORE),
new AysncInvokeRunnableFactory(), getRejectedPolicy(ASYNC_THREAD_POOL_REJECT_POLICY));
logger.info("异步代理执行线程池初始化成功,线程池大小:[{}],队列大小:[{}].", ASYNC_THREAD_POOL_CORE,
ASYNC_THREAD_POOL_QUEUE_CORE);
}
}
}
//提交到线程池
AsyncInvokeRunnable<T> invokeRunnable = new AsyncInvokeRunnable<T>(target, method, args);
threadPoolExecutor.execute(invokeRunnable);
}
/**
*@描述:线程工厂类
*@作者:nieli
*@日期:2018/1/29
*@时间:16:17
*/
private static class AysncInvokeRunnableFactory implements ThreadFactory
{
private static AtomicLong count = new AtomicLong(0);
@Override
public Thread newThread(Runnable runnable)
{
Thread thread = new Thread(runnable);
thread.setName(POOL_THREAD_NAME_PREFIX + count.addAndGet(1));
return thread;
}
}
/**
*@描述:具体执行逻辑
*@作者:nieli
*@日期:2018/1/29
*@时间:16:17
*/
private static class AsyncInvokeRunnable<T> implements Runnable
{
//代理对象
private final T target;
//执行方法
private final Method method;
//执行参数
private final Object[] args;
public AsyncInvokeRunnable(T target, Method method, Object[] args)
{
this.target = target;
this.method = method;
this.args = args;
}
@Override
public void run()
{
try
{
method.invoke(target, args);
}
catch (InvocationTargetException e)
{
//获取详细错误信息,可catch自定义错误等
Throwable throwble = e.getTargetException();
logger.error("执行代理对象[{}]-方法[{}]失败.", target.getClass().getName(), method.getName(), throwble);
}
catch (Exception e)
{
logger.error("执行代理对象[{}]-方法[{}]失败.", target.getClass().getName(), method.getName(), e);
}
}
}
}
2.2 异步注解:Async
package com.nieli.proxy;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @描述: 异步代理注解类
* @版权: Copyright (c) 2018
* @作者: nieli
* @版本: 1.0
* @创建日期: 2018年3月12日
* @创建时间: 下午6:40:54
*/
@Target({ java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Async
{
}
2.3 Bean管理:BeanHelper
package com.nieli.proxy;
import java.lang.reflect.Proxy;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import com.nieli.base.config.Configuration;
/**
* @描述: bean托管类
* @版权: Copyright (c) 2018
* @公司:
* @作者: nieli
* @版本: 1.0
* @创建日期: 2018年3月12日
* @创建时间: 下午6:47:22
*/
public final class BeanHelper
{
private static final Logger logger = LoggerFactory.getLogger(BeanHelper.class);
//是否开启代理
public static final boolean IS_OPEN_PROXY = Configuration.getInt("proxy.openProxy", 0) == 1;
//是否bean缓存
public static final boolean IS_OPEN_CACHE = Configuration.getInt("proxy.openCache", 0) == 1;
//代理对象实例cache
private static final HashMap<String, Object> TARGET_INSTANCE_CACHE = new HashMap<String, Object>();
private static ApplicationContext ctx;
static
{
List<String> locations = null;
try
{
locations = new ArrayList<String>();
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = (Resource[]) resolver.getResources("classpath*:/conf/spring/**/*.xml");
URL url = null;
for (Resource resource : resources)
{
try
{
url = resource.getURL();
if ( url != null )
{
//apache的FilenameUtils,按运行环境格式化至标准路径
String path = org.apache.commons.io.FilenameUtils.normalize(url.getPath());
int index = path.indexOf(org.apache.commons.io.FilenameUtils.normalize("/conf/spring/"));
path = index > 0 ? path.substring(index) : path;
locations.add(path);
}
}
catch (Exception e)
{
logger.error(e.getMessage(), e);
}
}
ctx = new ClassPathXmlApplicationContext((String[]) locations.toArray(new String[0]));
}
catch (Exception e)
{
logger.error("加载spring配置文件失败", e);
}
finally
{
if ( locations != null )
{
locations.clear();
locations = null;
}
}
}
public static <T> T getBean(String beanId, Class<T> clazz)
{
T target = null;
//如果开启代理
if ( IS_OPEN_PROXY )
{
//如果开启代理对象缓存,那么先从缓存对象中找
if ( IS_OPEN_CACHE && TARGET_INSTANCE_CACHE.containsKey(beanId) )
{
target = (T) TARGET_INSTANCE_CACHE.get(beanId);
}
//缓存对象中未找到,则获取对象详细信息,判断是否满足代理配置
else
{
target = getContext().getBean(beanId, clazz);
/**
* 1.接口注解为异步
* 2.对象确认为接口
* 3.spring返回的子对象,其超类确认为指定的接口
*/
if ( clazz.getAnnotation(Async.class) != null && clazz.isInterface()
&& clazz.isAssignableFrom(target.getClass()) )
{
InvocationHandler<T> invocationHandler = new InvocationHandler<T>(target);
target = (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[] { clazz },
invocationHandler);
//将代理实例添加至缓存
if ( IS_OPEN_CACHE )
{
TARGET_INSTANCE_CACHE.put(beanId, target);
}
}
}
}
//未开启代理,返回spring bean结果
else
{
target = getContext().getBean(beanId, clazz);
}
if ( logger.isDebugEnabled() )
{
logger.debug("bean:[{}]返回的对象为:[{}]", beanId, target.getClass());
}
return target;
}
/**
*@描述:获取Spring上下文对象
*@作者:nieli
*@日期:2018/2/2
*@时间:12:30
*/
public static ApplicationContext getContext()
{
return ctx;
}
}
3.测试
3.1 测试接口类
package com.nieli.proxy.service;
import com.nieli.proxy.Async;
/**
* @描述:
* @版权: Copyright (c) 2018
* @公司:
* @作者: nieli
* @版本: 1.0
* @创建日期: 2018年3月12日
* @创建时间: 下午6:53:36
*/
//标记为异步class
@Async
public interface TestService
{
//异步执行
@Async
void login(long v);
//同步执行
void logout(long v);
}
3.2 测试实现类
package com.nieli.proxy.service.impl;
import com.nieli.proxy.service.TestService;
/**
* @描述:
* @版权: Copyright (c) 2018
* @公司:
* @作者: nieli
* @版本: 1.0
* @创建日期: 2018年3月13日
* @创建时间: 上午9:53:32
*/
public class TestServiceImpl implements TestService
{
@Override
public void login(long v)
{
System.out.println("this is login :" + v);
}
@Override
public void logout(long v)
{
System.out.println("this is logout :" + v);
}
}
3.3 Spring配置(配置文件路径:classpath:/conf/spring/**.xml)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="test" class="com.nieli.proxy.service.impl.TestServiceImpl" />
</beans>
3.4 单元测试类
package com.nieli.proxy;
import java.util.concurrent.atomic.AtomicLong;
import com.nieli.proxy.service.TestService;
/**
* @描述:
* @版权: Copyright (c) 2018
* @公司:
* @作者: nieli
* @版本: 1.0
* @创建日期: 2018年3月12日
* @创建时间: 下午6:53:15
*/
public class Test
{
public static void main(String[] args) throws InterruptedException
{
TestService service = BeanHelper.getBean("test", TestService.class);
service.login(1);
service.logout(2);
Thread.sleep(2000L);
System.exit(0);
}
}
3.5 Console输出
使用说明:
定义Service,并通过spring配置文件注入后,将含有异步执行的Bean注解为异步Bean(@Async)
再将需要异步执行的方法标识为异步方法,如图:
Spring配置文件自动扫描路径:resource/conf/spring/*.xml
其他(非文章展示)代码说明:
com.nieli.base.config.Configuration =》 xml配置文件读取工具类