Java-Bean管理之异步代理方法

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配置文件读取工具类

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值