1 概述
BeanPostProcessors可以在spring IoC容器初始化受管Bean前、屬性設定後對該Bean先做一些預先處理,或者在容器銷毀受管Bean之前自己釋放資源。《Spring中BeanPostProcessors【1】簡單預先處理》一文有執行個體說明。本文介紹一個具體案例,優雅實現業務分離。
2 基礎類
設想一個系統中分很多模組,對於模組又有不同的操作命令,對於不同模組的不同命令情境,會有不同的處理。當然通過if else判斷可以實現,但可讀性和優雅性太差。下面採取註解+BeanPostProcessors的方式優雅實現。
【1】註解類
package com.xy.annotation;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)public @interface Module {/** * 模組號 */short moduleId();}import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface BizCommand {/** * 命令號 */short cmdId();}
【2】常量類
package com.xy.constants;public interface ConstantCmd {// 添加public short ADD = 1;// 刪除public short DELETE = 2;}public interface ConstantModule {// 學生模組public short STUDENT = 1;// 教師模組public short TEACHER = 2;}
【3】業務類
package com.xy.service;import com.xy.annotation.BizCommand;import com.xy.annotation.Module;import com.xy.constants.ConstantCmd;import com.xy.constants.ConstantModule;@Module(moduleId = ConstantModule.STUDENT)public interface StuService {@BizCommand(cmdId = ConstantCmd.ADD)public void add(String name);@BizCommand(cmdId = ConstantCmd.DELETE)public void delete(String name);}import org.springframework.stereotype.Component;@Component // 該類需要被Spring託管public class StuServiceImpl implements StuService {public void add(String name) {System.out.println("I am student add method.add name:" + name);}public void delete(String name) {System.out.println("I am student delete method.delete name:" + name);}}
3 核心類
總體思路是利用模組號+命令號對應不同的方法和實作類別,方法和實作類別被存在對應列表中。
傳入模組號和命令號,即可擷取對應的方法名和實作類別,執行即可。
package com.xy.scanner;import java.lang.reflect.Method;import org.springframework.beans.BeansException;import org.springframework.beans.factory.config.BeanPostProcessor;import org.springframework.stereotype.Component;import com.xy.annotation.BizCommand;import com.xy.annotation.Module;@Component // 該類也需要被Spring託管public class BizScanner implements BeanPostProcessor {public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {return bean;}public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {// 容器管理的類資訊(stuServiceImpl)Class<? extends Object> clazz = bean.getClass();// 該類實現的介面(stuService)Class<?>[] interfaces = clazz.getInterfaces();if (null != interfaces && interfaces.length > 0) {for (Class<?> interFace : interfaces) {// 擷取介面註解// 模組資訊Module module = interFace.getAnnotation(Module.class);if (null == module)continue;// 該介面所有方法Method[] methods = interFace.getMethods();if (null != methods && methods.length > 0) {for (Method method : methods) {// 命令資訊BizCommand bizCommand = method.getAnnotation(BizCommand.class);if (null == bizCommand)continue;// 模組號short moduleId = module.moduleId();// 命令號short cmdId = bizCommand.cmdId();if (BizInvokerManager.getInvoker(moduleId, cmdId) == null) {BizInvokerManager.addInvoker(moduleId, cmdId, BizInvoker.valueOf(method, bean));} else {System.out.println("重複命令:" + "module:" + moduleId + " " + "cmdId:" + cmdId);}}}}}return bean;}}import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;/** * 命令執行器 */public class BizInvoker {/** * 方法 */private Method method;/** * 目標對象(實作類別) */private Object target;/** * 封裝執行器 * * @param method 方法 com.xy.service.StuService.add * @param target 目標 com.xy.service.StuServiceImpl@273e07bd * @return 執行器 */public static BizInvoker valueOf(Method method, Object target) {BizInvoker invoker = new BizInvoker();invoker.setMethod(method);invoker.setTarget(target);return invoker;}/** * 調用方法 * * @param paramValues 參數 * @return 執行結果 */public Object invoke(Object... paramValues) {try {// method 方法 com.xy.service.StuService.add// target 目標 com.xy.service.StuServiceImpl@273e07bdreturn method.invoke(target, paramValues);} catch (IllegalAccessException e) {e.printStackTrace();} catch (IllegalArgumentException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}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;}}import java.util.HashMap;import java.util.Map;/** * 命令執行器管理者 */public class BizInvokerManager {/** * 命令調用器(第1個short是模組號,第2個short是命令號) */private static Map<Short, Map<Short, BizInvoker>> invokers = new HashMap<Short, Map<Short, BizInvoker>>();/** * 添加命令調用 * * @param module 模組號 * @param cmd 命令號 * @param invoker 調用器 */public static void addInvoker(short moduleId, short cmdId, BizInvoker invoker) {Map<Short, BizInvoker> map = invokers.get(moduleId);if (null == map) {map = new HashMap<Short, BizInvoker>();invokers.put(moduleId, map);}map.put(cmdId, invoker); // 改變引用,將命令號存入}/** * 擷取命令調用 * * @param module 模組號 * @param cmd 命令號 */public static BizInvoker getInvoker(short moduleId, short cmdId) {Map<Short, BizInvoker> map = invokers.get(moduleId);if (map != null) {return map.get(cmdId);}return null;}}
4 Spring檔案
<context:component-scan base-package="com.xy.scanner" /><context:component-scan base-package="com.xy.service" />
5 測試
package com.xy.test;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;import com.xy.constants.ConstantCmd;import com.xy.constants.ConstantModule;import com.xy.scanner.BizInvoker;import com.xy.scanner.BizInvokerManager;public class TestBean {public static void main(String[] args) {ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");System.out.println(ac);BizInvoker invoker1 = BizInvokerManager.getInvoker(ConstantModule.STUDENT, ConstantCmd.ADD);invoker1.invoke("xy1");System.out.println("========================");BizInvoker invoker2 = BizInvokerManager.getInvoker(ConstantModule.STUDENT, ConstantCmd.DELETE);invoker2.invoke("xy2");}}