`
hanmiao
  • 浏览: 55157 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论
收藏列表
标题 标签 来源
基于 activity 流程引擎封装的统一的 TaskListener/ExecutionListener https://zhuanlan.zhihu.com/p/380974129
//全部统一设置成ActivitiListener监听器
public class ActivitiListener implements TaskListener, ExecutionListener {
​
    private static final long serialVersionUID = 6071133014325140738L;
    // 用户任务监听器被触发的话,就会调用下面的notify方法
    @Override
    public void notify(DelegateTask delegateTask) {
        // 获取当前任务的taskDefinitionKey,其实就是节点id
        String taskDefinitionKey = delegateTask.getTaskDefinitionKey();
        // 通过taskDefinitionKey和目标类型去IOC容器中查找指定的Bean
        UserTaskListener userTaskHandler =
                ApplicationContextUtil.getBean(taskDefinitionKey, UserTaskListener.class);
        if (Objects.nonNull(userTaskHandler)) {
            // 执行目标Bean中的方法
            userTaskHandler.notify(delegateTask);
        }
    }
    // ExecutionListener回调方法
    @Override
    public void notify(DelegateExecution execution) {
        // 获取IOC容器
        ApplicationContext webApplicationContext = 
ApplicationContextUtil.getApplicationContext();
        // 从容器中查找目标类型Bean列表
        Map<String, AbstractElement> beans =
                webApplicationContext.getBeansOfType(AbstractElement.class);
        if (CollectionUtils.isEmpty(beans)) {
            return;
        }
        // 通过activityId查找符合要求的Bean实例。在bpmn中,其实就是元素的id。
        String activityId = execution.getCurrentActivityId();
        AbstractElement abstractElement = beans.get(activityId);
        if (Objects.nonNull(abstractElement)) {
            // 执行目标Bean的notify方法
            abstractElement.notify(execution);
            return;
        }
        // 如果通过activityId找不到,就通过processDefinitionKey查找目标Bean
        String processDefinitionKey =
                CastUtil.castString(execution.getVariable(Const.PROCESS_DEFINITION_KEY));
        abstractElement = beans.get(processDefinitionKey);
        if (Objects.nonNull(abstractElement)) {
            // 执行目标Bean的notify方法
            abstractElement.notify(execution);
        }
    }
}

// 这个接口是需要所有UserTask来实现的
public interface UserTaskListener {
    void notify(DelegateTask delegateTask);
}
//把所有的节点都抽象成AbstractElement,主要在于需要让每一个节点都是ExecutionListener的子类
//这个设计的灵感来自于bpmn流程图里面任何组件元素都可以指定ExecutionListener,那意味着我如
//果想要统一规范所有的组件元素的Listener为ActivitiListener的话,那么我就需要把所有的组件都
//抽象成一个目标,这样我在ActivitiListener的实现里面就可以通过一定的规则找到目标组件的实现了。
//最终,通过总结,要求所有元素的id都作为Bean的名字,也就是说,通过这个元素的id可以在IOC容器
//中找到对应的Bean实例
public abstract class AbstractElement extends AbstractActivitiServiceImpl
        implements ExecutionListener {
​
    private static final long serialVersionUID = 4836235578847862052L;
​
    @Autowired private ActivitiBusinessDataRepository activitiBusinessDataRepository;
​
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void notify(DelegateExecution execution) {
        // 如果当前对象不支持,就直接结束
        if (!support(execution)) {
            return;
        }
        Map<String, Object> variables = Maps.newHashMap();
        Map<String, Object> data = Maps.newHashMap();
        // 执行前
        beforeExecute(execution, variables, data);
        // 真正的执行目标代码
        execute(execution, variables, data);
        // 执行后
        afterExecute(execution, data);
    }
​
    //执行业务
    protected abstract void execute(
            DelegateExecution execution, 
			Map<String, Object> variables, 
			Map<String, Object> data);
​
    //后置处理
    protected abstract void afterExecute(
		DelegateExecution execution, 
		Map<String, Object> data);
​
    protected boolean support(DelegateExecution execution) {
        return false;
    }
​
    //读取前置系统参数
    protected void beforeExecute(
            DelegateExecution execution, 
			Map<String, Object> variables, 
			Map<String, Object> data) {
        String taskDefinitionKey = execution.getCurrentActivityId();
        String processDefinitionKey =
                CastUtil.castString(execution.getVariable(Const.PROCESS_DEFINITION_KEY));
        // 从数据库里面把自定义配置读取到variables里面。如果没有这种需要的话,可以去掉。
        activitiBusinessDataRepository.readProcessInstanceVariable(
                processDefinitionKey, taskDefinitionKey, variables);
        if (!CollectionUtils.isEmpty(variables)) {
            for (Map.Entry<String, Object> e : variables.entrySet()) {
                String name = e.getKey();
                Object value = e.getValue();
                execution.setVariable(e.getKey(), e.getValue());
                data.put(name, value);
            }
        }
    }
​
    protected void saveData(DelegateExecution execution, Map<String, Object> data) {
        Timestamp now = new Timestamp(System.currentTimeMillis());
        String id = execution.getId();
        String taskDefinitionKey = execution.getCurrentActivityId();
        String name = execution.getCurrentFlowElement().getName();
        String processInstanceId = execution.getProcessInstanceId();
        String businessKey = execution.getProcessInstanceBusinessKey();
        TaskNodeInfo taskNodeInfo = TaskNodeInfo.newInstance(id, name, taskDefinitionKey);
​
        // 自动保存的流程节点数据;把一些我认为一定要保存的数据自动保存下来,也就是说,
		//不依赖于开发人员,我自己认为一定要保存的数据,比如businessKey一定要保存
        List<FieldInfo> autoSaveTaskNodeVariables =
                activitiBusinessDataRepository.getAutoSaveTaskNodeVariables(
                        Executor.defaultExecutor(), businessKey, data);
        // processInstanceData是抽象方法,需要每一个子类实现;返回的数据是当前流程实例
		//需要保存的信息,以便后续节点可以使用
        List<FieldInfo> processInstanceDataList = processInstanceData(execution, data);
        // taskNodeData是抽象方法,子类需要实现;返回的数据是当前节点的快照信息
        List<FieldInfo> taskNodeDataList = taskNodeData(execution, data);
​
        // 记录流程实例数据;保存当前流程实例数据
        activitiBusinessDataRepository.saveProcessInstanceData(
                processInstanceDataList, null, processInstanceId, businessKey, now);
        // 记录节点数据;把当前节点的相关信息保存起来
        activitiBusinessDataRepository.saveTaskNodeData(
                autoSaveTaskNodeVariables, taskNodeDataList, 
				processInstanceId, taskNodeInfo, now);
        // 自动记录节点历史,不需要开发者操心
        // 一般历史数据大概可以归纳为时间、节点、任务、流程实例ID,附加信息等等,所以花
		//点心思总结一下,应该是可以固定下来的,所以就不需要子类去实现
        activitiBusinessDataRepository.saveHistory(
                businessKey,
                processInstanceId,
                Executor.defaultExecutor(),
                taskNodeInfo,
                data,
                now);
    }
    // 返回的数据是当前流程实例需要保存的信息
    protected abstract List<FieldInfo> processInstanceData(
            DelegateExecution execution, Map<String, Object> data);
  
    // 返回的数据是当前节点的快照信息
    protected abstract List<FieldInfo> taskNodeData(
            DelegateExecution execution, Map<String, Object> data);
​
    //读取变量参数
    protected void readTransactorAndSendNoticeVariable(
            String processDefinitionKey, 
			String taskDefinitionKey, 
			Map<String, Object> variables) {
        activitiBusinessDataRepository.readTransactorAndSendNoticeVariable(
                processDefinitionKey, taskDefinitionKey, variables);
    }
}
DateTimeFormatter 是线程安全的类,可用于替换 SimpleDateFormat
//CustomRunnable.java
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.Random;

public class CustomRunnable implements Runnable {
	
	static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

	@Override
	public void run() {
		Instant instant = new Date(Math.abs(new Random().nextLong())).toInstant();
		ZoneId zone = ZoneId.systemDefault();
		LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
		System.out.println("currentDateTime is : " + formatter.format(localDateTime));
	}
}

//Example.java
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Example {
	
	public static void main(String[] args) {
		ExecutorService service = Executors.newFixedThreadPool(10);
		for(int i=0; i<10; i++){
			CustomRunnable task = new CustomRunnable();
			service.submit(task);
		}
		service.shutdown();
	}
}
获取代码在源文件中的文件名、方法名、行号等信息
Thread.currentThread().getStackTrace()[2].getLineNumber();
Thread.currentThread().getStackTrace()[2].getMethodName();
Thread.currentThread().getStackTrace()[2].getFileName();
Thread.currentThread().getStackTrace()[2].getClassName();
新版本的Quartz-2.3.0定时任务 CronTrigger
//ExampleJob.java
import java.text.SimpleDateFormat;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

public class ExampleJob implements Job {

	@Override
	public void execute(JobExecutionContext context) throws JobExecutionException {
		SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		String currentTime = format.format(new java.util.Date());
		System.out.println("hello, world. current time is : " + currentTime);
	}
}

//CronTriggerExample.java
import static org.quartz.CronScheduleBuilder.cronSchedule;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.TriggerBuilder.newTrigger;

import org.quartz.CronTrigger;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.impl.StdSchedulerFactory;

public class CronTriggerExample {

  public static void main(String[] args) throws Exception {

	  SchedulerFactory schedulerFactory = new StdSchedulerFactory();
	    Scheduler scheduler = schedulerFactory.getScheduler();
	    JobDetail job = newJob(ExampleJob.class)
	    		.withIdentity("job1", "group1")
	    		.build();

	    CronTrigger trigger = newTrigger()
	    		.withIdentity("trigger1", "group1")
	    		.withSchedule(cronSchedule("0/2 * * * * ?"))
	    		.build();

	    scheduler.scheduleJob(job, trigger);
	    scheduler.start();
  }
}
//输出结果
//hello, world. current time is : 2022-01-28 15:41:24
//hello, world. current time is : 2022-01-28 15:41:26
//hello, world. current time is : 2022-01-28 15:41:28
//hello, world. current time is : 2022-01-28 15:41:30
简单的Quartz定时任务CronTrigger
//ExampleJob.java
import java.text.SimpleDateFormat;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

public class ExampleJob implements Job {

	@Override
	public void execute(JobExecutionContext context) throws JobExecutionException {
		SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		String currentTime = format.format(new java.util.Date());
		System.out.println("hello, world. current time is : " + currentTime);
	}
}

//CronTriggerExample.java
import org.quartz.CronExpression;
import org.quartz.CronTrigger;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.impl.StdSchedulerFactory;

public class CronTriggerExample {

	public static void main(String[] args) throws Exception {
		JobDetail jobDetail = new JobDetail("exampleJob", Scheduler.DEFAULT_GROUP, ExampleJob.class);
		//每两秒钟触发壹次
		CronTrigger cronTrigger = new CronTrigger("cronTrigger", Scheduler.DEFAULT_GROUP);
		cronTrigger.setCronExpression(new CronExpression("*/2 * * * * ?"));
		
		Scheduler scheduler = new StdSchedulerFactory().getScheduler();
		scheduler.scheduleJob(jobDetail, cronTrigger);
		scheduler.start();
	}
}
//输出内容
//hello, world. current time is : 2022-01-28 15:07:22
//hello, world. current time is : 2022-01-28 15:07:24
//hello, world. current time is : 2022-01-28 15:07:26
//hello, world. current time is : 2022-01-28 15:07:28
简单的Quartz定时任务SimpleTrigger
//ExampleJob.java
import java.text.SimpleDateFormat;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

public class ExampleJob implements Job {

	@Override
	public void execute(JobExecutionContext context) throws JobExecutionException {
		SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		String currentTime = format.format(new java.util.Date());
		System.out.println("hello, world. current time is : " + currentTime);
	}
}

//SimpleTriggerExample.java
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SimpleTrigger;
import org.quartz.Trigger;
import org.quartz.impl.StdSchedulerFactory;

public class SimpleTriggerExample {
	public static void main(String[] args) throws Exception{
		JobDetail jobDetail = new JobDetail("exampleJob", Scheduler.DEFAULT_GROUP, ExampleJob.class);
		Trigger trigger = new SimpleTrigger("simpleTrigger", Scheduler.DEFAULT_GROUP, new java.util.Date(), null, SimpleTrigger.REPEAT_INDEFINITELY, 2000);;
		
		Scheduler scheduler = new StdSchedulerFactory().getScheduler();
		scheduler.scheduleJob(jobDetail, trigger);
		scheduler.start();
	}
}
//输出内容
//hello, world. current time is : 2022-01-28 15:05:27
//hello, world. current time is : 2022-01-28 15:05:29
//hello, world. current time is : 2022-01-28 15:05:31
//hello, world. current time is : 2022-01-28 15:05:33
巧用 Base58进制生成指定格式字符串
public class Base58Example {
	//之所以使用58进制,是为了避开容易混淆的字符0与O,1与l
	private final static String characters = "23456789ABCDEFGHIJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
	private final static int scale = characters.length();
    private final static char[] array = characters.toCharArray();
    
    public static void main(String[] args){
    	String result = generateUnionDeviceId(12345687L);
    	System.out.println("result=" + result);
    	//输出内容 result=00037Hwg
    }
    
    //十进制数字转58进制并返回
    static String fromBase10ToBase58(long base10) {
        StringBuilder sb = new StringBuilder();
        long remainder = 0;
        do {
            remainder = base10 % scale;
            sb.append(array[(int) remainder]);
            base10 = base10 / scale;
        } while (base10 > scale - 1);
        sb.append(array[(int) base10]);
        // 倒序
        return sb.reverse().toString();
    }

    //产生8位长度的银联终端号,如果位数不足向左补零
    public static String generateUnionDeviceId(long base10) {
        String base58 = fromBase10ToBase58(base10);
       return org.apache.commons.lang3.StringUtils.leftPad(base58, 8, "0");
    }
}
log4j.properties 通用配置样例
log4j.rootLogger=WARN,CONSOLE,logfile
# 输出到控制台
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=[frame] %d{yyyy-MM-dd HH:mm:ss,SSS} - %-4r %-5p [%t] %C:%L %x - %m%n
# 输出到日志文件中
log4j.appender.logfile=org.apache.log4j.RollingFileAppender
log4j.appender.logfile.Encoding=UTF-8
log4j.appender.logfile.File=logs/root.log
log4j.appender.logfile.MaxFileSize=10MB
log4j.appender.logfile.MaxBackupIndex=3
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %F %p %m%n
# 对不同的类输出不同的日志文件
# club.bagedate包下的日志单独输出
log4j.logger.club.bagedate=DEBUG,bagedate
# 设置为false该日志信息就不会加入到rootLogger中了
log4j.additivity.club.bagedate=false
# 下面就和上面配置一样了
log4j.appender.bagedate=org.apache.log4j.RollingFileAppender
log4j.appender.bagedate.Encoding=UTF-8
log4j.appender.bagedate.File=logs/bagedate.log
log4j.appender.bagedate.MaxFileSize=10MB
log4j.appender.bagedate.MaxBackupIndex=3
log4j.appender.bagedate.layout=org.apache.log4j.PatternLayout
log4j.appender.bagedate.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %F %p %m%n
log4j 的动态初始化之二
Properties properties = new Properties();
properties.put("log4j.rootLogger", "info,A");
properties.put("log4j.appender.A", "org.apache.log4j.DailyRollingFileAppender");
properties.put("log4j.appender.A.File", "src/system.log");
properties.put("log4j.appender.A.DatePattern", "'.'yyyy-MM-dd-HH");
properties.put("log4j.appender.A.layout", "org.apache.log4j.PatternLayout");
properties.put("log4j.appender.A.layout.ConversionPattern", "%-d{yyyy-MM-dd HH:mm:ss} [%p][%c]%l %m%n");

PropertyConfigurator.configure(properties);
Logger logger = LogManager.getLogger(Example.class);
LogManager.getRootLogger().setLevel(Level.DEBUG);

logger.trace("This is a TRACE message");
logger.debug("This is a DEBUG message");
logger.info("This is a INFO message");
logger.warn("This is a WARN message");
logger.error("This is a ERROR message");
logger.fatal("This is a FATAL message");

//输出日志如下
//2022-01-20 14:01:20 [INFO][publish.Example]publish.Example.initLog4jDynamic(Example.java:37) This is a INFO message
//2022-01-20 14:01:20 [WARN][publish.Example]publish.Example.initLog4jDynamic(Example.java:38) This is a WARN message
//2022-01-20 14:01:20 [ERROR][publish.Example]publish.Example.initLog4jDynamic(Example.java:39) This is a ERROR message
//2022-01-20 14:01:20 [FATAL][publish.Example]publish.Example.initLog4jDynamic(Example.java:40) This is a FATAL message
log4j 的动态初始化之壹
import java.io.IOException;

import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.DailyRollingFileAppender;
import org.apache.log4j.Level;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.spi.LoggerRepository;

public class Example {

	public static void main(String[] args) throws IOException {
		LoggerRepository repository = LogManager.getLoggerRepository();  
        Logger logger = repository.getLogger("com.hanmiao.client");  
        logger.setAdditivity(false);  
        logger.setLevel(Level.INFO);
        PatternLayout layout = new PatternLayout("%d{yyyy-MM-dd HH:mm:ss} [%p] %c %l - %m%n");
        DailyRollingFileAppender fileAppender = new DailyRollingFileAppender(layout,"C:\\logs\\example.log","yyyy-MM-dd");
        ConsoleAppender consoleAppender = new ConsoleAppender(layout, ConsoleAppender.SYSTEM_OUT);
        logger.addAppender(fileAppender);
        logger.addAppender(consoleAppender);
        
        logger.debug("debug message....................");
        logger.info("info message....................");
        logger.warn("warn message....................");
        logger.error("error message....................");
        logger.fatal("fatal message....................");
	}
}

//打印出的日志如下
//2022-01-19 18:16:15 [INFO] com.hanmiao.client null - info message....................
//2022-01-19 18:16:15 [WARN] com.hanmiao.client null - warn message....................
//2022-01-19 18:16:15 [ERROR] com.hanmiao.client null - error message....................
//2022-01-19 18:16:15 [FATAL] com.hanmiao.client null - fatal message....................
//2022-01-20 10:09:22 [INFO] com.hanmiao.client null - info message....................
//2022-01-20 10:09:22 [WARN] com.hanmiao.client null - warn message....................
//2022-01-20 10:09:22 [ERROR] com.hanmiao.client null - error message....................
//2022-01-20 10:09:22 [FATAL] com.hanmiao.client null - fatal message....................
Java 调用帆软报表生成 PDF 文档并加密后发出邮件 https://help.fanruan.com/finereport10.0/doc-view-735.html
package com.fr.io;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Map;
import java.util.Properties;

import com.fr.base.operator.common.CommonOperator;
import com.fr.chart.activator.ChartBaseActivator;
import com.fr.cluster.engine.activator.standalone.StandaloneModeActivator;
import com.fr.config.activator.BaseDBActivator;
import com.fr.config.activator.ConfigurationActivator;
import com.fr.env.operator.CommonOperatorImpl;
import com.fr.general.I18nResource;
import com.fr.general.log.Log4jConfig;
import com.fr.general.log.parser.ExtraPatternParserManager;
import com.fr.health.activator.ModuleHealActivator;
import com.fr.io.exporter.PDFExporter;
import com.fr.main.impl.WorkBook;
import com.fr.module.Module;
import com.fr.module.tool.ActivatorToolBox;
import com.fr.report.ReportActivator;
import com.fr.report.RestrictionActivator;
import com.fr.report.module.ReportBaseActivator;
import com.fr.report.write.WriteActivator;
import com.fr.scheduler.SchedulerActivator;
import com.fr.stable.WriteActor;
import com.fr.stable.core.UUID;
import com.fr.stable.resource.ResourceLoader;
import com.fr.store.StateServiceActivator;
import com.fr.third.apache.log4j.Level;
import com.fr.third.apache.log4j.PropertyConfigurator;
import com.fr.third.eclipse.jgit.util.FileUtils;
import com.fr.third.org.apache.http.conn.ConnectTimeoutException;
import com.fr.workspace.simple.SimpleWork;
import com.lowagie.text.DocumentException;
import com.lowagie.text.pdf.PdfReader;
import com.lowagie.text.pdf.PdfStamper;
import com.lowagie.text.pdf.PdfWriter;

import aia.cn.dsb.fore.sendemail.bean.MailInfo;
import aia.cn.dsb.utils.SendMail;
//classpath 必须设置成如下形式才能编译通过
//classpath=.;C:\Program Files (x86)\Java\jdk1.8.0_144\lib\dt.jar;C:\Program Files (x86)\Java\jdk1.8.0_144\lib\tools.jar;
public class Example {
	//帆软报表的本地工程路径
	private static final String ENVPATH = "C:\\FineReport_10.0\\webapps\\webroot\\WEB-INF\\";
	//PDF文件的输出路径
	private static final String TEMP_FOLDER_PATH = "C:\\io\\WorkBook\\";
	//用于加密PDF的密码
	private static final String PASSWORD = "123456";
	private static final String[] receivers = new String[]{"Miao-M.Han@aia.com"};
	private static final String[] cc = new String[]{"Hai-H.li@aia.com", "Kenny-YM.Wang@aia.com"};
	private static final String[] bcc = new String[]{"Airlaue-E.Peng@aia.com"};
	private static final String subject = "邮件主题 By Hanmiao";
	private static final String content = "邮件正文 By Hanmiao 2021-01-14 16:25\n附件密码:123456";

	public static void main(String[] args) throws IOException, DocumentException {
		// 初始化报表环境
		System.out.println("================>initial environment...");
		Module module = initReportEnv();
		// 生成PDF文件并返回输出文件的全路径
		String fullFilePath = generatePDFile(module);
		System.out.println("================>generate pdf file: " + fullFilePath);
		//加密PDF文件
		encryptFile(fullFilePath);
		System.out.println("================>encrypt pdf file...");
		//将加密后的PDF作为邮件附件并发送
		sendmail(fullFilePath);
		System.out.println("================>mail send established...");
		//删除临时目录里的文件(如果希望发送邮件之后清空目录,可以取消下面两行的注释)
		FileUtils.delete(new java.io.File(fullFilePath));
		System.out.println("================>delete temp file...");
	}

	private static void sendmail(String fullFilePath){
		MailInfo mailInfo = new MailInfo();
		//发件服务器
		mailInfo.setMailServerHost("smtphk-int.aia.biz");
		mailInfo.setMailServerPort("25");
		//发件人信息
		mailInfo.setFromAddress("ManagementDashboardTeam@aia.com");
		mailInfo.setValidate(false);
		mailInfo.setUserName("");//当validate设置为true时,需要指定发件人帐号
		mailInfo.setPassword("");//当validate设置为true时,需要指定发件人密码
		//收件人信息
		mailInfo.setToAddress(receivers);
		if(enableDebug()){
			mailInfo.setCcAddress(cc);
			mailInfo.setBccAddress(bcc);
		}
		//邮件主题,正文及附件信息
		mailInfo.setSubject(subject);
		mailInfo.setContent(content);
		mailInfo.setAttachFile(new String[]{fullFilePath});
		//发送邮件 
		SendMail sender = new SendMail();
		try{
			sender.send(mailInfo);
		}catch(Throwable e){
			throw new RuntimeException("mail send failed, error message is: " + e.getMessage());
		}
	}

	//如果同时发送CC与BCC则返回true,否则返回 false
	private static boolean enableDebug() {
		return false;
	}

	private static void encryptFile(String filePath) throws IOException, DocumentException {
		FileInputStream fis = new FileInputStream(filePath);
		PdfReader reader = new PdfReader(fis);
		FileOutputStream fos = new FileOutputStream(filePath);
		PdfStamper stamper = new PdfStamper(reader, fos);
		stamper.setEncryption(PASSWORD.getBytes(), "".getBytes(),
				PdfWriter.ALLOW_PRINTING, 
				PdfWriter.ENCRYPTION_AES_128 | PdfWriter.DO_NOT_ENCRYPT_METADATA);
		stamper.close();
		reader.close();
		fis.close();
	}

	private static String generatePDFile(Module module) {
		try {
			//定义输出的模板路径,以reportlets为根目录
			WorkBook workbook = (WorkBook) TemplateWorkBookIO.readTemplateWorkBook("GettingStarted.cpt");
			//设置传入的参数
			Map<String, Object> paramMap = MapBuilder.buildHashMap(String.class, Object.class).put("地区", "华东").build();
			FileUtils.mkdirs(new java.io.File(TEMP_FOLDER_PATH), true);
			String uuid = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
			String fullFilePath = TEMP_FOLDER_PATH.concat("PdfExport-" + uuid + ".pdf");
			// 将结果工作薄导出为PDF文件
			FileOutputStream fos = new FileOutputStream(new java.io.File(fullFilePath));
			PDFExporter tool = new PDFExporter();
			tool.export(fos, workbook.execute(paramMap, new WriteActor()));
			fos.close();
			module.stop();
			return fullFilePath;
		} catch (ConnectTimeoutException e) {
			//ignore this error message
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			SimpleWork.checkOut();
		}
		return "";
	}

	private static Module initReportEnv() {
		//initLog4j();
		// 定义报表运行环境,用于执行报表
		Module module = ActivatorToolBox.simpleLink(new BaseDBActivator(), new ConfigurationActivator(),
				new ResourceRepositoryActivator(), new StandaloneModeActivator(), new ModuleHealActivator(),
				new StateServiceActivator(), new SchedulerActivator(), new ReportBaseActivator(),
				new RestrictionActivator(), new ReportActivator(), new WriteActivator(), new ChartBaseActivator());
		SimpleWork.supply(CommonOperator.class, new CommonOperatorImpl());
		SimpleWork.checkIn(ENVPATH);
		I18nResource.getInstance();
		module.start();
		return module;
	}

	private static void initLog4j() {
		PropertyConfigurator.configure(loadLog4jPropertiesFromJar(Level.toLevel("INFO")));
	}

	private static Properties loadLog4jPropertiesFromJar(Level level) {
		Properties properties = new Properties();
		System.setProperty("LOG_HOME", System.getProperty("user.dir"));
		System.setProperty("LOG_ROOT_LEVEL", level.toString());
		ExtraPatternParserManager.setSystemProperty();
		try {
			properties.load(ResourceLoader.getResourceAsStream("/log4j.properties", Log4jConfig.class));
		} catch (IOException ignore) {
			//do nothing
		}
		return properties;
	}

}
spring 初始化 bean 对象
ApplicationContext context = new ClassPathXmlApplicationContext("aop/demo/spring.xml");
Greeting greeting = (Greeting) context.getBean("greetingProxy");
greeting.sayHello("Jack");
CGlib 动态代理类
import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class CGlibProxy implements MethodInterceptor {

	@Override
	public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
		before();
        Object result = proxy.invokeSuper(obj, args);
        after();
        return result;
	}

	public <T> T getProxy(Class<T> cls) {
        return (T) Enhancer.create(cls, this);
    }
	
    public static CGlibProxy getInstance() {
        return instance;
    }

	private void after() {
		System.out.println("invoke after() method...");
	}

	private void before() {
		System.out.println("invoke before() method...");
	}
	
	private CGlibProxy() {}
	
	private static CGlibProxy instance = new CGlibProxy();
}
//调用方法举例
//ServiceImpl service = CGlibProxy.getInstance().getProxy(ServiceImpl.class);
//service.doSth();
mybatis 初始化代码
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
SqlSession session = factory.openSession(true);
UserDao userDao = session.getMapper(UserDao.class);
List<User> userList = userDao.findByUsername("小");
Java List链式操作,Map链式操作,Set链式操作 Java List链式操作,Map链式操作,Set链式操作
//ListBuilder.java
import java.util.ArrayList;
import java.util.List;

public class ListBuilder<T> {

    private List<T> innerList;

    public static <T> ListBuilder<T> build(Class<T> clazz) {
        return new ListBuilder<T>();
    }

    public ListBuilder<T> add(T t) {
        if (null == innerList) {
            innerList = new ArrayList<>();
        }
        innerList.add(t);
        return this;
    }

    public List<T> get() {
        return innerList;
    }
}

//MapBuilder.java
import java.util.HashMap;
import java.util.Map;

public class MapBuilder<K, V> {

	private Map<K, V> innerMap;

	public static <K, V> MapBuilder<K, V> buildHashMap(Class<K> kCLass, Class<V> vClass) {
		return new MapBuilder<K, V>();
	}
	
	public MapBuilder<K, V> put(K k,V v) {
		if (null == innerMap) {
			innerMap = new HashMap<K,V>();
		}
		
		innerMap.put(k, v);
		return this;
	}
	
	public Map<K, V> get() {
		return innerMap;
	}
}


//SetBuilder.java
import java.util.HashSet;
import java.util.Set;

public class SetBuilder<T> {

    private Set<T> innerSet;

    public static <T> SetBuilder<T> build(Class<T> clazz) {
        return new SetBuilder<T>();
    }

    public SetBuilder<T> add(T t) {
        if (null == innerSet) {
        	innerSet = new HashSet<>();
        }
        innerSet.add(t);
        return this;
    }

    public Set<T> get() {
        return innerSet;
    }
}

//ListBuilder 使用方法
List<String> strList = ListBuilder.build(String.class)
				.add("str1").add("str2")
				.get();

List<String> strList = new ListBuilder<String>()
			.add("str1").add("str2")
			.get();

//MapBuilder 使用方法
Map<String,Object> map = new MapBuilder<String,Object>()
					.put("key", "value")
					.get();

Map<String,Object> map = MapBuilder.buildHashMap(String.class, Object.class)
				.put("key", "value")
				.get();

//SetBuilder 使用方法
Set<Person> personSet = SetBuilder.build(Person.class)
				.add(new Person("zs", 23)).add(new Person("ls", 25)).get();

Set<Person> personSet = new SetBuilder<Person>()
				.add(new Person("zs", 23)).add(new Person("ls", 25)).get();

线程池调用实例
//SomeCallable.java
import java.util.Random;
import java.util.concurrent.Callable;

public class SomeCallable implements Callable<Integer> {

	@Override
	public Integer call() throws Exception {		
		Random random = new Random();
		Integer randomValue = random.nextInt(100);
		System.out.println("callable invoked, the random value is " + randomValue);
		return randomValue;
	}
}
//Example.java
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;

public class Example {

	public static void main(String[] args) throws InterruptedException, ExecutionException {
		test01();
		test02();
		test03();
	}
	
	private static void test03() throws InterruptedException, ExecutionException{
		Callable<Integer> task = new SomeCallable();
		
		ExecutorService executor = Executors.newFixedThreadPool(10);
		CompletionService<Integer> pool = new ExecutorCompletionService<Integer>(executor);
		for(int i=0; i<10; i++){
			pool.submit(task);
		}
		
		for(int i=0; i<10; i++){
			Integer result = pool.take().get();
			System.out.println("result=" + result);
		}		
		executor.shutdown();
	}
	
	private static void test02() throws InterruptedException, ExecutionException{
		Callable<Integer> callable = new SomeCallable();
		FutureTask<Integer> task = new FutureTask<Integer>(callable);
		List<Future<Integer>> futures = new ArrayList<Future<Integer>>();
		
		ExecutorService executor = Executors.newFixedThreadPool(10);
		for(int i=0; i<10; i++){
			futures.add((Future<Integer>) executor.submit(task));
		}
		
		for(Future future : futures){
			if(future.isDone()){
				Integer result = (Integer)future.get();
				System.out.println("result=" + result);
			}
		}		
		executor.shutdown();
	}
	
	private static void test01() throws InterruptedException, ExecutionException {
		Callable<Integer> callable = new SomeCallable();
		FutureTask<Integer> task = new FutureTask<Integer>(callable);
		new Thread(task).start();
		System.out.println("task.get()=" + task.get());
	}
}
Java 通用的方法返回值
//接口定义IErrorCode
public interface IErrorCode {
	public long getCode();
	public String getMessage();
}
//枚举类型ResultCode
public enum ResultCode implements IErrorCode {
	
	SUCCESS(10000L, "SUCCESS"), 
	FAILED(10001L, "FAILED"), 
	VALIDATE_FAILED(10002L, "VALIDATE_FAILED"), 
	UNAUTHORIZED(10003L, "UNAUTHORIZED"), 
	FORBIDDEN(10004L, "FORBIDDEN");
	
	public long getCode() {
		return code;
	}
	
	public String getMessage() {
		return message;
	}
	
	private ResultCode(long code, String message){
		this.code = code;
		this.message = message;
	}

	private long code;
	private String message;
}
//通用返回对象CommonResult
public class CommonResult<T> {
    private long code;
    private String message;
    private T data;

    protected CommonResult() {
    }

    protected CommonResult(long code, String message, T data) {
        this.code = code;
        this.message = message;
        this.data = data;
    }

    /**
     * 成功返回结果
     *
     * @param data 获取的数据
     */
    public static <T> CommonResult<T> success(T data) {
        return new CommonResult<T>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data);
    }

    /**
     * 成功返回结果
     *
     * @param data 获取的数据
     * @param  message 提示信息
     */
    public static <T> CommonResult<T> success(T data, String message) {
        return new CommonResult<T>(ResultCode.SUCCESS.getCode(), message, data);
    }

    /**
     * 失败返回结果
     * @param errorCode 错误码
     */
    public static <T> CommonResult<T> failed(IErrorCode errorCode) {
        return new CommonResult<T>(errorCode.getCode(), errorCode.getMessage(), null);
    }

    /**
     * 失败返回结果
     * @param message 提示信息
     */
    public static <T> CommonResult<T> failed(String message) {
        return new CommonResult<T>(ResultCode.FAILED.getCode(), message, null);
    }

    /**
     * 失败返回结果
     */
    public static <T> CommonResult<T> failed() {
        return failed(ResultCode.FAILED);
    }

    /**
     * 参数验证失败返回结果
     */
    public static <T> CommonResult<T> validateFailed() {
        return failed(ResultCode.VALIDATE_FAILED);
    }

    /**
     * 参数验证失败返回结果
     * @param message 提示信息
     */
    public static <T> CommonResult<T> validateFailed(String message) {
        return new CommonResult<T>(ResultCode.VALIDATE_FAILED.getCode(), message, null);
    }

    /**
     * 未登录返回结果
     */
    public static <T> CommonResult<T> unauthorized(T data) {
        return new CommonResult<T>(ResultCode.UNAUTHORIZED.getCode(), ResultCode.UNAUTHORIZED.getMessage(), data);
    }

    /**
     * 未授权返回结果
     */
    public static <T> CommonResult<T> forbidden(T data) {
        return new CommonResult<T>(ResultCode.FORBIDDEN.getCode(), ResultCode.FORBIDDEN.getMessage(), data);
    }

    public long getCode() {
        return code;
    }

    public void setCode(long code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}
Java 8 实现 Builder 模式 疯狂的类构造器Builder模式,链式调用
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Supplier;

public class GenericBuilder<T> {  
	  
    private final Supplier<T> instantiator;  
  
    private List<Consumer<T>> instanceModifiers = new ArrayList<Consumer<T>>();  
  
    public GenericBuilder(Supplier<T> instantiator) {  
        this.instantiator = instantiator;  
    }  
  
    public static <T> GenericBuilder<T> of(Supplier<T> instantiator) {  
        return new GenericBuilder<T>(instantiator);  
    }  
  
    public <U> GenericBuilder<T> with(BiConsumer<T, U> consumer, U value) {  
        Consumer<T> c = instance -> consumer.accept(instance, value);  
        instanceModifiers.add(c);  
        return this;  
    }  
  
    public T build() {  
        T value = instantiator.get();  
        instanceModifiers.forEach(modifier -> modifier.accept(value));  
        instanceModifiers.clear();  
        return value;  
    }  
} 

//调用方式如下
Java8Task java8Task = GenericBuilder.of(Java8Task::new)
	.with(Java8Task::setId, 99L)
	.with(Java8Task::setName, "紧急任务")
	.with(Java8Task::setType, 1)
	.with(Java8Task::setContent, "处理一下这个任务")
	.with(Java8Task::setStatus, 0)
	.with(Java8Task::setFinishDate, new Date())
	.build();
Java 实现 Builder 模式
//1、在Computer 中创建一个公开的静态内部类 Builder,然后将Computer 中的参数都复制到Builder类中;
//2、在Builder中创建一个public的构造函数,参数为Computer中必填的那些参数,cpu 和ram;
//3、在Builder中创建setXXX函数,对Computer中那些可选参数进行赋值,方法名为参数名或者setXXX,返回值为Builder类型的实例;
//4、在Builder中创建一个public 的 build()方法,在其中构建Computer的实例并返回;
//5、在Computer中创建一个private的构造函数,参数为Builder类型;
//6、在Computer中添加各属性的getXXX方法;
public class Computer {
    private final String cpu;//required
    private final String ram;//required
    private final int usbCount;//optional
    private final String keyboard;//optional
    private final String display;//optional

    private Computer(Builder builder){
        this.cpu = builder.cpu;
        this.ram = builder.ram;
        this.usbCount = builder.usbCount;
        this.keyboard = builder.keyboard;
        this.display = builder.display;
    }
    public static class Builder{
        private String cpu;//required
        private String ram;//required
        private int usbCount;//optional
        private String keyboard;//optional
        private String display;//optional

        public Builder(String cpu, String ram){
            this.cpu = cpu;
            this.ram = ram;
        }

        public Builder setUsbCount(int usbCount) {
            this.usbCount = usbCount;
            return this;
        }
        public Builder setKeyboard(String keyboard) {
            this.keyboard = keyboard;
            return this;
        }
        public Builder setDisplay(String display) {
            this.display = display;
            return this;
        }        
        public Computer build(){
            return new Computer(this);
        }
    }
  //省略getter方法
}

//调用方法如下
Computer computer = new Computer.Builder("因特尔","三星")
        .setDisplay("三星24寸")
        .setKeyboard("罗技")
        .setUsbCount(2)
        .build();
POI 同时读取 XLS/XLSX 两种格式的 EXCEL 文档
FileInputStream fis = new FileInputStream(new File(filename));			
String suffix = filename.split("\\.")[1];
if (suffix.equalsIgnoreCase("xlsx")) {
	XSSFWorkbook workbook = new XSSFWorkbook(fis);
	//TODO: 读取 workbook 内容
} else if(suffix.equalsIgnoreCase("xls")){
	HSSFWorkbook workbook = new HSSFWorkbook(fis);
	//TODO: 读取 workbook 内容
}
定义一个名称为NULL的全局的Object 以避免集合元素出现空值
//定义一个名称为NULL的全局的Object。当需要用null值的时候,用这个NULL来代替。
static final Object NULL = new Object();
MessageFormat.format() 方法格式化字符串
String msg = "{0}{1}{2}{3}{4}{5}{6}{7}{8}";  
Object [] array = new Object[]{"A","B","C","D","E","F","G","H","I",};         
String value = MessageFormat.format(msg, array);
在使用了 Spring 注入的 Web 应用中获取注入的 Bean 对象
XXXService service = ContextLoader.getCurrentWebApplicationContext().getBean(XXXService.class);

XXXService service = ContextLoader.getCurrentWebApplicationContext().getBean("xXXService");
获取 JAR 包中某个类在服务器上部署的具体路径
String path = this.getClass().getProtectionDomain().getCodeSource().getLocation().getPath();
activiti 流程模板中添加 TaskListener 和 ExecutionListener 的 XML 语法
<!-- 注意:每次在eclipse中使用此方法修改了流程模板之后,需要在 eclipse 中刷新该文件的上级目录之后才能生效,建议多操作几次,因为有壹定机率新的修改刷新不出来,壹个巨大的坑 -->
<!-- 在Execution连线上添加监听器 -->
<sequenceFlow id="flow2" name="不同意" sourceRef="usertask2" targetRef="usertask1">
  <extensionElements>
    <activiti:executionListener event="take" class="com.listener.CustomExecutionListener"></activiti:executionListener>
  </extensionElements>
  <conditionExpression xsi:type="tFormalExpression"><![CDATA[${outcome=='不同意'}]]></conditionExpression>
</sequenceFlow>

<!-- 在Task节点上添加监听器 -->
 <userTask id="usertask2" name="部门审批" activiti:assignee="${orguser}">
  <extensionElements>
    <activiti:taskListener event="all" class="com.listener.CustomTaskListener"></activiti:taskListener>
  </extensionElements>
</userTask>
CGLib 实现的动态代理
//==========CGLibProxy.java=========================
package com.hanmiao.proxy;

import java.lang.reflect.Method;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
//需引入 cglib-2.1.3.jar 和 asm.jar 以完成编译运行
public class CGLibProxy implements MethodInterceptor {

    public static CGLibProxy getInstance(){
        return Holder.instance;
    }

    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        before(obj, method, args);
        Object result = proxy.invokeSuper(obj, args);
        after(obj, method, args);
        return result;
    }

    private void before(Object obj, Method method, Object[] args){
        logger.info("Before method {} invoking...", method.getName());
    }

    private void after(Object obj, Method method, Object[] args){
        logger.info("After method {} invoking...", method.getName());
    }

    //生成不带参数的代理类
    @SuppressWarnings("unchecked")
    public <T> T getProxy(Class<T> clazz) {
        return (T) Enhancer.create(clazz, this);
    }

//生成有参数的代理类,target是代理目标,argsType 是各个参数的类型,argsValue 是各个参数的值
@SuppressWarnings("unchecked")
public <T>T createProxy(T target, Class<T>[] argsType, Object[] argsValue){
	Enhancer enhancer = new Enhancer();
	enhancer.setSuperclass(target.getClass());
	enhancer.setCallback(this);
	return (T)enhancer.create(argsType, argsValue);
}

    private static class Holder{
        private static final CGLibProxy instance = new CGLibProxy();
    }

    private Logger logger = LoggerFactory.getLogger(this.getClass());
}

//=================Example.java=======================
package com.hanmiao.proxy;

public class Example {
    public static void main(String[] args){
        Hello proxy = CGLibProxy.getInstance().getProxy(Hello.class);
        proxy.sayHello("hanmiao");
    }
}

//==================Hello.java==========================
package com.hanmiao.proxy;

public class Hello{
    public void sayHello(String username){
        System.out.println("Hello, " + username);
    }
}
ibatis/mybatis 中 jdbcType 与 javaType 对应关系表
JDBC Type           Java Type  
CHAR                String  
VARCHAR             String  
LONGVARCHAR         String  
NUMERIC             java.math.BigDecimal  
DECIMAL             java.math.BigDecimal  
BIT                 boolean  
BOOLEAN             boolean  
TINYINT             byte  
SMALLINT            short  
INTEGER             int  
BIGINT              long  
REAL                float  
FLOAT               double  
DOUBLE              double  
BINARY              byte[]  
VARBINARY           byte[]  
LONGVARBINARY       byte[]  
DATE                java.sql.Date  
TIME                java.sql.Time  
TIMESTAMP           java.sql.Timestamp  
CLOB                Clob  
BLOB                Blob  
ARRAY               Array  
DISTINCT            mapping of underlying type  
STRUCT              Struct  
REF                 Ref  
DATALINK            java.net.URL
高效的将数组对象转换为列表List java, 数组, list, 高性能
import java.util.Collections;

Integer[] array = new Integer[]{4, 7, 9};
List<Integer> list = new ArrayList<Integer>(array.length);
Collections.addAll(list, array);
Log4j 日志中的类路径显示首字母缩写 log4j, 日志格式, 类路径, 首字母缩写 Java Log4j 日志中的类路径显示首字母缩写
log4j.rootLogger=stdout, dailyFile

#stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.EnhancedPatternLayout
#log4j.appender.stdout.layout.ConversionPattern=%d %-5p [%c{5}] - %m%n
log4j.appender.stdout.layout.ConversionPattern=%d{MM-dd HH:mm} [%-30.30c{1.}] %m%n

#DailyFile
log4j.appender.dailyFile=org.apache.log4j.DailyRollingFileAppender
log4j.appender.dailyFile.File=C:/logs/hour/log.log
log4j.appender.dailyFile.ImmediateFlush=true
log4j.appender.dailyFile.DatePattern='.'yyyy-MM-dd-HH'.log'
log4j.appender.dailyFile.layout=org.apache.log4j.EnhancedPatternLayout
log4j.appender.dailyFile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p [%-30.30c{1.}] %m%n
静态内部类实现的线程安全的单例模式 单例模式, 懒加载, 静态内部类, java
public class Example {
    private Example() {}
  
    public static Example getInstance() {
        return Holder.INSTANCE;
    }
  
    private static class Holder {
        private static final Example INSTANCE = new Example();
    }  
}
Global site tag (gtag.js) - Google Analytics