借助Spring自定义命令执行器执行指定的方法,实现业务分离

目录

1.自定义注解

2.扫描器

3.命令执行器

4.命令执行器管理器

5. 测试使用

6.延伸 

7.源码下载


在Spring项目中,有时因为某些需求,并不想直接调用实际的实例方法(业务分离),或者调用实际的实例方法存在困难,这时候,可以自定义代码执行器来间接的调用执行指定的实例方法,来实现业务分离(有点类似于代理设计模式)。中心思想是自定义注解,通过注解将自己的方法添加到自己的命令执行器管理器中,需要使用的时候按方法去取就好了。下面记录下具体的实现过程。

1.自定义注解

首先自定义两个注解,一个用来找到目标接口(@TargetInf),一个用来找到目标接口下的目标抽象方法(@TargetMethod)。使用的时候,这两个注解分别作用在目标接口和目标接口的抽象方法上。

package com.transfar.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 自定义代理接口注解
 * 
 * @author 皮锋
 * @date 2018年4月3日 上午8:57:51
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetInf {

	/**
	 * 接口
	 */
	public Class<? extends Object> inf();
}
package com.transfar.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 自定义代理方法注解
 * 
 * @author 皮锋
 * @date 2018年4月3日 上午9:06:21
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetMethod {

	public String method();
}

 

2.扫描器

创建业务代理扫描器,实现Spring的BeanPostProcessor接口,获得代理执行业务的方法,并将方法添加到命令执行器管理器。BeanPostProcessor接口可以在Spring容器中完成bean实例化、配置以及其他初始化方法前后要添加一些自己逻辑处理。接口中两个方法不能返回null,如果返回null那么在后续初始化方法将报空指针异常或者通过getBean()方法获取不到bena实例对象,因为后置处理器从Spring IoC容器中取出bean实例对象没有再次放回IoC容器中。

package com.transfar.core;

import java.lang.reflect.Method;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

import com.transfar.annotation.TargetInf;
import com.transfar.annotation.TargetMethod;

import lombok.extern.slf4j.Slf4j;

/**
 * 业务代理扫描器,获得代理执行业务的方法,添加到命令执行器管理器
 * 
 * @author 皮锋
 * @date 2018年4月3日 上午9:09:10
 */
@Slf4j
@Component("businessAgencyScaner")
public class BusinessAgencyScaner implements BeanPostProcessor {

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		Class<? extends Object> clazz = bean.getClass();
		Class<?>[] interfaces = clazz.getInterfaces();
		if (interfaces != null && interfaces.length > 0) {
			// 扫描类的所有接口父类
			for (Class<?> interFace : interfaces) {
				// 判断是否为代理执行业务的接口类
				TargetInf vService = interFace.getAnnotation(TargetInf.class);
				if (vService == null) {
					continue;
				}
				// 找出命令方法
				Method[] methods = interFace.getMethods();
				if (methods != null && methods.length > 0) {
					for (Method method : methods) {
						TargetMethod vMethod = method.getAnnotation(TargetMethod.class);
						if (vMethod == null) {
							continue;
						}
						// 接口
						Class<? extends Object> clz = vService.inf();
						// 方法
						String mtd = vMethod.method();
						Invoker invoker = Invoker.valueOf(method, bean);
						if (InvokerHolder.getInvoker(clz, mtd) == null) {
							InvokerHolder.addInvoker(clz, mtd, invoker);
						} else {
							log.error("重复注册执行器!");
						}
					}
				}
			}
		}
		return bean;
	}

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

}

3.命令执行器

创建命令执行器,用于执行命令,此处指特定要执行的方法。

package com.transfar.core;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import lombok.extern.slf4j.Slf4j;

/**
 * 命令执行器
 * 
 * @author 皮锋
 * @date 2018年4月3日 上午8:45:53
 */
@Slf4j
public class Invoker {

	/**
	 * 方法
	 */
	private Method method;

	/**
	 * 目标对象
	 */
	private Object target;

	public static Invoker valueOf(Method method, Object target) {
		Invoker invoker = new Invoker();
		invoker.setMethod(method);
		invoker.setTarget(target);
		return invoker;
	}

	/**
	 * @description 执行
	 * @param paramValues
	 * @return Object
	 */
	public Object invoke(Object... paramValues) {
		try {
			return method.invoke(target, paramValues);
		} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
			log.error("", e);
		}
		return null;
	}

	public Method getMethod() {
		return method;
	}

	public void setMethod(Method method) {
		this.method = method;
	}

	public Object getTarget() {
		return target;
	}

	public void setTarget(Object target) {
		this.target = target;
	}
}

4.命令执行器管理器

创建命令执行器管理器,用于命令执行器的添加和获取。命令执行器管理器其实是一个map集合,里面存放着不重复的各个命令执行器,通过key进行添加和获取。

package com.transfar.core;

import java.util.HashMap;
import java.util.Map;

/**
 * 命令执行器管理器
 * 
 * @author 皮锋
 * @date 2018年4月3日 上午8:53:09
 */
public class InvokerHolder {

	/**
	 * 命令执行器
	 */
	private static Map<Class<? extends Object>, Map<String, Invoker>> invokers = new HashMap<>();

	/**
	 * @description 获取执行器
	 * @param clazz
	 * @param method
	 * @return invoker
	 */
	public static Invoker getInvoker(Class<? extends Object> clazz, String method) {
		Map<String, Invoker> map = invokers.get(clazz);
		if (map != null) {
			return map.get(method);
		}
		return null;
	}

	/**
	 * @description 添加执行器
	 * @param clazz
	 * @param method
	 * @param invoker
	 */
	public static void addInvoker(Class<? extends Object> clazz, String method, Invoker invoker) {
		Map<String, Invoker> map = invokers.get(clazz);
		if (map == null) {
			map = new HashMap<>();
			invokers.put(clazz, map);
		}
		map.put(method, invoker);
	}

}

5. 测试使用

自定义注解、扫描器、执行器和执行器管理器在第一次写完之后,后续不需要再动,对业务代码没有任何侵入,程序员可以只关注具体业务的书写。当需要扩展的时候,也很容易实现。

这里只列举说明重点的几个接口和类:

1.在要执行的方法所对应的接口以及方法上分别加上注解,扫描器就能扫描到这个方法,然后添加到命名执行器管理器。

package com.transfar.service;

import com.transfar.annotation.TargetInf;
import com.transfar.annotation.TargetMethod;

/**
 * 业务层接口
 * 
 * @author 皮锋
 * @date 2019年1月29日 下午5:05:37
 */
@TargetInf(inf = ITestService.class)
public interface ITestService {

	String test(String param);

	// 加了注解的方法将会添加到命令执行器管理器,注册到bean容器
	@TargetMethod(method = "target")
	String target(String param);

}

2. 通过命令执行器管理器,传入要执行的接口和抽象方法,获得指定的命令执行器,执行器就会执行对应这个抽象方法的具体实现方法,并返回方法执行结果。

package com.transfar.service.impl;

import org.springframework.stereotype.Service;

import com.transfar.core.Invoker;
import com.transfar.core.InvokerHolder;
import com.transfar.service.ITestService;

/**
 * 业务层
 * 
 * @author 皮锋
 * @date 2019年1月29日 下午5:05:52
 */
@Service
public class TestServiceImpl implements ITestService {

	@Override
	public String test(String param) {
		// 通过命令执行器管理器,获取指定的命令执行器
		Invoker invoker = InvokerHolder.getInvoker(ITestService.class, "target");
		// 执行命令,返回执行结果
		Object object = invoker.invoke(new Object[] { param });
		return object.toString();
	}

	@Override
	public String target(String param) {
		return "目标方法执行了,param = " + param;
	}
}
package com.transfar.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.transfar.service.ITestService;

/**
 * 测试控制器
 * 
 * @author 皮锋
 * @date 2019年1月29日 下午5:04:38
 */
@RestController
@RequestMapping("test")
public class TestController {

	@Autowired
	private ITestService iTestService;

	/**
	 * url地址:http://localhost:8080/test/test?param=pifeng
	 * 
	 * @param param
	 * @return string
	 */
	@GetMapping("test")
	public String test(@RequestParam(name = "param") String param) {
		return this.iTestService.test(param);
	}
}

  在浏览器输入地址:http://ocalhost:8080/test/test?param=pifeng,会返回如下结果。

6.延伸 

根据需求,这里自定义的注解,可以按模块和方法划分,实现思路一样,这样能实现模块之间的解耦。

7.源码下载

https://download.youkuaiyun.com/download/paulangsky/10944491

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

E%3Dmc%B2

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值