class RepositoryItemReader<T> extends AbstractItemCountingItemStreamItemReader<T> implements InitializingBean {

	protected Log logger = LogFactory.getLog(getClass());

	private PagingAndSortingRepository<?, ?> repository;

	private Sort sort;

	private volatile int page = 0;

	private int pageSize = 10;

	private volatile int current = 0;

	private List<?> arguments;

	private volatile List<T> results;

	private final Lock lock = new ReentrantLock();

	private String methodName;

	public RepositoryItemReader() {
		setName(ClassUtils.getShortName(RepositoryItemReader.class));
	}

	/**
	 * Arguments to be passed to the data providing method.
	 * @param arguments list of method arguments to be passed to the repository
	 */
	public void setArguments(List<?> arguments) {
		this.arguments = arguments;
	}

	/**
	 * Provides ordering of the results so that order is maintained between paged queries
	 * @param sorts the fields to sort by and the directions
	 */
	public void setSort(Map<String, Sort.Direction> sorts) {
		this.sort = convertToSort(sorts);
	}

	/**
	 * @param pageSize The number of items to retrieve per page. Must be greater than 0.
	 */
	public void setPageSize(int pageSize) {
		this.pageSize = pageSize;
	}

	/**
	 * The {@link org.springframework.data.repository.PagingAndSortingRepository}
	 * implementation used to read input from.
	 * @param repository underlying repository for input to be read from.
	 */
	public void setRepository(PagingAndSortingRepository<?, ?> repository) {
		this.repository = repository;
	}

	/**
	 * Specifies what method on the repository to call. This method must take
	 * {@link org.springframework.data.domain.Pageable} as the <em>last</em> argument.
	 * @param methodName name of the method to invoke
	 */
	public void setMethodName(String methodName) {
		this.methodName = methodName;
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		Assert.state(repository != null, "A PagingAndSortingRepository is required");
		Assert.state(pageSize > 0, "Page size must be greater than 0");
		Assert.state(sort != null, "A sort is required");
		Assert.state(this.methodName != null && !this.methodName.isEmpty(), "methodName is required.");
		if (isSaveState()) {
			Assert.state(StringUtils.hasText(getName()), "A name is required when saveState is set to true.");
		}
	}

	@Nullable
	@Override
	protected T doRead() throws Exception {

		this.lock.lock();
		try {
			boolean nextPageNeeded = (results != null && current >= results.size());

			if (results == null || nextPageNeeded) {

				if (logger.isDebugEnabled()) {
					logger.debug("Reading page " + page);
				}

				results = doPageRead();
				page++;

				if (results.isEmpty()) {
					return null;
				}

				if (nextPageNeeded) {
					current = 0;
				}
			}

			if (current < results.size()) {
				T curLine = results.get(current);
				current++;
				return curLine;
			}
			else {
				return null;
			}
		}
		finally {
			this.lock.unlock();
		}
	}

	@Override
	protected void jumpToItem(int itemLastIndex) throws Exception {
		this.lock.lock();
		try {
			page = itemLastIndex / pageSize;
			current = itemLastIndex % pageSize;
		}
		finally {
			this.lock.unlock();
		}
	}

	/**
	 * Performs the actual reading of a page via the repository. Available for overriding
	 * as needed.
	 * @return the list of items that make up the page
	 * @throws Exception Based on what the underlying method throws or related to the
	 * calling of the method
	 */
	@SuppressWarnings("unchecked")
	protected List<T> doPageRead() throws Exception {
		Pageable pageRequest = PageRequest.of(page, pageSize, sort);

		MethodInvoker invoker = createMethodInvoker(repository, methodName);

		List<Object> parameters = new ArrayList<>();

		if (arguments != null && arguments.size() > 0) {
			parameters.addAll(arguments);
		}

		parameters.add(pageRequest);

		invoker.setArguments(parameters.toArray());

		Slice<T> curPage = (Slice<T>) doInvoke(invoker);

		return curPage.getContent();
	}

	@Override
	protected void doOpen() throws Exception {
	}

	@Override
	protected void doClose() throws Exception {
		this.lock.lock();
		try {
			current = 0;
			page = 0;
			results = null;
		}
		finally {
			this.lock.unlock();
		}
	}

	private Sort convertToSort(Map<String, Sort.Direction> sorts) {
		List<Sort.Order> sortValues = new ArrayList<>();

		for (Map.Entry<String, Sort.Direction> curSort : sorts.entrySet()) {
			sortValues.add(new Sort.Order(curSort.getValue(), curSort.getKey()));
		}

		return Sort.by(sortValues);
	}

	private Object doInvoke(MethodInvoker invoker) throws Exception {
		try {
			invoker.prepare();
		}
		catch (ClassNotFoundException | NoSuchMethodException e) {
			throw new DynamicMethodInvocationException(e);
		}

		try {
			return invoker.invoke();
		}
		catch (InvocationTargetException e) {
			if (e.getCause() instanceof Exception) {
				throw (Exception) e.getCause();
			}
			else {
				throw new InvocationTargetThrowableWrapper(e.getCause());
			}
		}
		catch (IllegalAccessException e) {
			throw new DynamicMethodInvocationException(e);
		}
	}

	private MethodInvoker createMethodInvoker(Object targetObject, String targetMethod) {<FILL_FUNCTION_BODY>}

}

		MethodInvoker invoker = new MethodInvoker();
		invoker.setTargetObject(targetObject);
		invoker.setTargetMethod(targetMethod);
		return invoker;
	
<methods>public non-sealed void <init>() ,public void close() throws org.springframework.batch.item.ItemStreamException,public int getCurrentItemCount() ,public boolean isSaveState() ,public void open(org.springframework.batch.item.ExecutionContext) throws org.springframework.batch.item.ItemStreamException,public T read() throws java.lang.Exception,public void setCurrentItemCount(int) ,public void setMaxItemCount(int) ,public void setSaveState(boolean) ,public void update(org.springframework.batch.item.ExecutionContext) throws org.springframework.batch.item.ItemStreamException<variables>private static final java.lang.String READ_COUNT,private static final java.lang.String READ_COUNT_MAX,private int currentItemCount,private int maxItemCount,private boolean saveState