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配置文件讀取工具類